Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PSP-7075 : Show ACQ team (organization) in project exports #3568

Merged
merged 4 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,14 @@ public AgreementReportModel(PimsAgreement agreement, ClaimsPrincipal user)

private static string GetTeamMemberName(PimsAcquisitionFile file, string teamProfileTypeCode)
{
PimsPerson matchingPerson = file?.PimsAcquisitionFileTeams?.FirstOrDefault(x => x.AcqFlTeamProfileTypeCode == teamProfileTypeCode)?.Person;
return matchingPerson?.GetFullName() ?? string.Empty;
var matchingTeamMember = file?.PimsAcquisitionFileTeams?.FirstOrDefault(x => x.AcqFlTeamProfileTypeCode == teamProfileTypeCode);

if(matchingTeamMember is not null)
{
return matchingTeamMember.PersonId.HasValue ? matchingTeamMember.Person?.GetFullName() : matchingTeamMember.Organization.Name;
}

return string.Empty;
}

private static string GetNullableDate(DateTime? dateTime)
Expand Down
2 changes: 1 addition & 1 deletion source/backend/api/Services/AcquisitionFileService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public List<AcquisitionFileExportModel> GetAcquisitionFileExport(AcquisitionFilt
FileAcquisitionCompleted = fileProperty.file.CompletionDate.HasValue ? fileProperty.file.CompletionDate.Value.ToString("dd-MMM-yyyy") : string.Empty,
FilePhysicalStatus = fileProperty.file.AcqPhysFileStatusTypeCodeNavigation is not null ? fileProperty.file.AcqPhysFileStatusTypeCodeNavigation.Description : string.Empty,
FileAcquisitionType = fileProperty.file.AcquisitionTypeCodeNavigation is not null ? fileProperty.file.AcquisitionTypeCodeNavigation.Description : string.Empty,
FileAcquisitionTeam = string.Join(", ", fileProperty.file.PimsAcquisitionFileTeams.Select(x => x.Person.GetFullName(true))),
FileAcquisitionTeam = string.Join(", ", fileProperty.file.PimsAcquisitionFileTeams.Select(x => x.PersonId.HasValue ? x.Person.GetFullName(true) : x.Organization.Name)),
FileAcquisitionOwners = string.Join(", ", fileProperty.file.PimsAcquisitionOwners.Select(x => x.FormatOwnerName())),
}).ToList();
}
Expand Down
2 changes: 2 additions & 0 deletions source/backend/dal/Models/AcquisitionReportFilterModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ public class AcquisitionReportFilterModel
public IEnumerable<long> Projects { get; set; }

public IEnumerable<long> AcquisitionTeamPersons { get; set; }

public IEnumerable<long> AcquisitionTeamOrganizations { get; set; }
}
}
9 changes: 9 additions & 0 deletions source/backend/dal/Repositories/AgreementRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,25 @@
{
predicate.And(a => a.AcquisitionFile.ProjectId.HasValue && filter.Projects.Contains(a.AcquisitionFile.ProjectId.Value));
}

if (filter.AcquisitionTeamPersons != null && filter.AcquisitionTeamPersons.Any())
{
predicate.And(a => a.AcquisitionFile.PimsAcquisitionFileTeams.Any(afp => afp.PersonId.HasValue && filter.AcquisitionTeamPersons.Contains((long)afp.PersonId)));
}

if (filter.AcquisitionTeamOrganizations != null && filter.AcquisitionTeamOrganizations.Any())
{
predicate.And(a => a.AcquisitionFile.PimsAcquisitionFileTeams.Any(o => o.OrganizationId.HasValue && filter.AcquisitionTeamOrganizations.Contains((long)o.OrganizationId)));
}

Check warning on line 65 in source/backend/dal/Repositories/AgreementRepository.cs

View check run for this annotation

Codecov / codecov/patch

source/backend/dal/Repositories/AgreementRepository.cs#L63-L65

Added lines #L63 - L65 were not covered by tests

var query = Context.PimsAgreements
.Include(a => a.AgreementTypeCodeNavigation)
.Include(a => a.AcquisitionFile)
.ThenInclude(a => a.PimsAcquisitionFileTeams)
.ThenInclude(afp => afp.Person)
.Include(a => a.AcquisitionFile)
.ThenInclude(a => a.PimsAcquisitionFileTeams)
.ThenInclude(o => o.Organization)
.Include(a => a.AcquisitionFile)
.ThenInclude(a => a.Project)
.Include(a => a.AcquisitionFile)
Expand Down
10 changes: 10 additions & 0 deletions source/backend/dal/Repositories/CompReqFinancialRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@
.ThenInclude(cr => cr.AcquisitionFile)
.ThenInclude(a => a.PimsAcquisitionFileTeams)
.ThenInclude(afp => afp.Person)
.Include(f => f.CompensationRequisition)
.ThenInclude(cr => cr.AcquisitionFile)
.ThenInclude(a => a.PimsAcquisitionFileTeams)
.ThenInclude(o => o.Organization)
.Include(f => f.CompensationRequisition)
.ThenInclude(cr => cr.AcquisitionFile)
.ThenInclude(a => a.Project)
Expand All @@ -71,11 +75,17 @@
(f.CompensationRequisition.AlternateProjectId.HasValue && filter.Projects.Contains(f.CompensationRequisition.AlternateProjectId.Value)) ||
(!f.CompensationRequisition.AlternateProjectId.HasValue && f.CompensationRequisition.AcquisitionFile.ProjectId.HasValue && filter.Projects.Contains(f.CompensationRequisition.AcquisitionFile.ProjectId.Value)));
}

if (filter.AcquisitionTeamPersons != null && filter.AcquisitionTeamPersons.Any())
{
query = query.Where(f => f.CompensationRequisition.AcquisitionFile.PimsAcquisitionFileTeams.Any(afp => afp.PersonId.HasValue && filter.AcquisitionTeamPersons.Contains((long)afp.PersonId)));
}

if (filter.AcquisitionTeamOrganizations != null && filter.AcquisitionTeamOrganizations.Any())
{
query = query.Where(f => f.CompensationRequisition.AcquisitionFile.PimsAcquisitionFileTeams.Any(o => o.OrganizationId.HasValue && filter.AcquisitionTeamOrganizations.Contains((long)o.OrganizationId)));
}

Check warning on line 87 in source/backend/dal/Repositories/CompReqFinancialRepository.cs

View check run for this annotation

Codecov / codecov/patch

source/backend/dal/Repositories/CompReqFinancialRepository.cs#L85-L87

Added lines #L85 - L87 were not covered by tests

return query.ToList();
}
}
Expand Down
19 changes: 17 additions & 2 deletions source/backend/tests/unit/api/Models/AgreementReportModelTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ namespace Pims.Api.Test
public class AgreementReportModelTest
{
[Fact]
public void AgreementReportModel_TeamMember()
public void AgreementReportModel_TeamMember_Person()
{
// Arrange
var testAgreement = new Dal.Entities.PimsAgreement();
var propCoord = new PimsAcquisitionFileTeam() { AcqFlTeamProfileTypeCode = "PROPCOORD", Person = new PimsPerson() { Surname = "test" } };
var propCoord = new PimsAcquisitionFileTeam() { AcqFlTeamProfileTypeCode = "PROPCOORD", PersonId = 1, Person = new PimsPerson() { PersonId = 1, Surname = "test" } };
testAgreement.AcquisitionFile = new Dal.Entities.PimsAcquisitionFile() { PimsAcquisitionFileTeams = new List<PimsAcquisitionFileTeam>() { propCoord } };

// Act
Expand All @@ -24,6 +24,21 @@ public void AgreementReportModel_TeamMember()
model.PropertyCoordinator.Should().Be("test");
}

[Fact]
public void AgreementReportModel_TeamMember_Organization()
{
// Arrange
var testAgreement = new Dal.Entities.PimsAgreement();
var propCoord = new PimsAcquisitionFileTeam() { AcqFlTeamProfileTypeCode = "PROPCOORD", OrganizationId = 100, Organization = new PimsOrganization() { OrganizationId = 100, Name = "FORTIS BC" } };
testAgreement.AcquisitionFile = new Dal.Entities.PimsAcquisitionFile() { PimsAcquisitionFileTeams = new List<PimsAcquisitionFileTeam>() { propCoord } };

// Act
var model = new AgreementReportModel(testAgreement, new System.Security.Claims.ClaimsPrincipal());

// Assert
model.PropertyCoordinator.Should().Be("FORTIS BC");
}

[Fact]
public void AgreementReportModel_TeamMember_Null()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createMemoryHistory } from 'history';

import { getMockPerson } from '@/mocks/contacts.mock';
import { mockLookups } from '@/mocks/lookups.mock';
import { getMockOrganization } from '@/mocks/organization.mock';
import { mockProjects } from '@/mocks/projects.mock';
import { lookupCodesSlice } from '@/store/slices/lookupCodes';
import { act, render, RenderOptions, screen, userEvent } from '@/utils/test-utils';
Expand Down Expand Up @@ -97,9 +98,14 @@ describe('ProjectExportForm component', () => {
expect(screen.getByText(/776/g)).not.toBeNull();
});

it('displays team members when passed', async () => {
it('displays team members when passed person', async () => {
const { getByDisplayValue } = setup({
teamMembers: [getMockPerson({ id: 1, surname: 'last', firstName: 'first' })],
teamMembers: [
{
personId: 1,
person: getMockPerson({ id: 1, surname: 'last', firstName: 'first' }),
},
],
} as any);

const select = getByDisplayValue(/Select Export Type.../i);
Expand All @@ -111,6 +117,25 @@ describe('ProjectExportForm component', () => {
expect(screen.getByText(/first last/g)).not.toBeNull();
});

it('displays team members when passed organization', async () => {
const { getByDisplayValue } = setup({
teamMembers: [
{
organizationId: 100,
organization: getMockOrganization({ id: 100, name: 'FORTIS BC' }),
},
],
} as any);

const select = getByDisplayValue(/Select Export Type.../i);

await act(async () => {
userEvent.selectOptions(select, ProjectExportTypes.AGREEMENT);
});

expect(screen.getByText(/FORTIS BC/g)).not.toBeNull();
});

it('hides submit, project, team members by default', async () => {
setup({} as any);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as React from 'react';

import { Select, SelectOption } from '@/components/common/form';
import { SectionField } from '@/components/common/Section/SectionField';
import { Api_Person } from '@/models/api/Person';
import { Api_AcquisitionFileTeam } from '@/models/api/AcquisitionFile';
import { Api_Project } from '@/models/api/Project';
import { Api_ExportProjectFilter } from '@/models/api/ProjectFilter';

Expand All @@ -14,7 +14,7 @@ export interface IProjectExportFormProps {
onExportTypeSelected: () => void;
onExport: (filter: Api_ExportProjectFilter) => Promise<void>;
projects: Api_Project[];
teamMembers: Api_Person[];
teamMembers: Api_AcquisitionFileTeam[];
loading: boolean;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ export const ProjectExportFormContent: React.FunctionComponent<IProjectExportFor
});
const teamMembersOptions = teamMembers.map<CodeTypeSelectOption>(x => {
return {
codeType: x?.id?.toString() ?? '',
codeTypeDescription: formatApiPersonNames(x),
codeType: x.personId ? `P-${x.personId}` : `O-${x.organizationId}`,
codeTypeDescription: x.personId ? formatApiPersonNames(x.person) : x.organization?.name ?? '',
};
});

Expand Down
24 changes: 23 additions & 1 deletion source/frontend/src/features/projects/reports/models.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { CodeTypeSelectOption } from '@/components/maps/leaflet/Control/AdvancedFilter/models';
import { Api_ExportProjectFilter } from '@/models/api/ProjectFilter';

type IdSelector = 'O' | 'P';

export class ExportProjectModel {
public exportType: keyof typeof ProjectExportTypes | '' = '';
public projects: CodeTypeSelectOption[] = [];
Expand All @@ -10,7 +12,8 @@
return {
type: this.exportType === '' ? undefined : this.exportType,
projects: this.projects.map(p => +p.codeType),
acquisitionTeamPersons: this.acquisitionTeam.map(t => +t.codeType),
acquisitionTeamPersons: getParameterIdFromOptions(this.acquisitionTeam),
acquisitionTeamOrganizations: getParameterIdFromOptions(this.acquisitionTeam, 'O'),
};
}
}
Expand All @@ -19,3 +22,22 @@
COMPENSATION = 'Compensation Requisition Export',
AGREEMENT = 'Agreement Export',
}

const getParameterIdFromOptions = (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would you mind moving this out to a shared method (since I think your last pr uses this as well). maybe in contactutils?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

different logic, this one returns an array, the other one returns a string. I thought about it.

options: CodeTypeSelectOption[],
selector: IdSelector = 'P',
): number[] => {
if (!options.length) {
return [];
}

var filteredItems = options.filter(option => String(option.codeType).startsWith(selector));
if (!filteredItems.length) {
return [];

Check warning on line 36 in source/frontend/src/features/projects/reports/models.ts

View check run for this annotation

Codecov / codecov/patch

source/frontend/src/features/projects/reports/models.ts#L34-L36

Added lines #L34 - L36 were not covered by tests
}

return filteredItems.map(x => {
var number = x.codeType.split('-').pop() ?? '';
return parseInt(number) ?? 0;

Check warning on line 41 in source/frontend/src/features/projects/reports/models.ts

View check run for this annotation

Codecov / codecov/patch

source/frontend/src/features/projects/reports/models.ts#L39-L41

Added lines #L39 - L41 were not covered by tests
});
};
1 change: 1 addition & 0 deletions source/frontend/src/models/api/ProjectFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ import { ProjectExportTypes } from '@/features/projects/reports/models';
export interface Api_ExportProjectFilter {
projects: number[];
acquisitionTeamPersons: number[];
acquisitionTeamOrganizations: number[];
type?: keyof typeof ProjectExportTypes;
}
Loading