Skip to content

Commit de1706b

Browse files
authored
#2303 - Total eligible amount in NOA screen (#2365)
Total eligible amount is not showing for NOA (part-time and full-time)
1 parent 3efd4c2 commit de1706b

File tree

4 files changed

+197
-15
lines changed

4 files changed

+197
-15
lines changed

sources/packages/backend/apps/api/src/route-controllers/assessment/_tests_/e2e/assessment.institutions.controller.getAssessmentNOA.e2e-spec.ts

+173-15
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ import {
2121
} from "@sims/test-utils";
2222
import {
2323
AssessmentStatus,
24+
DisbursementSchedule,
2425
Institution,
2526
InstitutionLocation,
27+
OfferingIntensity,
2628
} from "@sims/sims-db";
2729
import { getDateOnlyFormat } from "@sims/utilities";
2830
import { MASKED_MSFAA_NUMBER } from "../../../../../src/services";
@@ -52,7 +54,7 @@ describe("AssessmentInstitutionsController(e2e)-getAssessmentNOA", () => {
5254
);
5355
});
5456

55-
it("Should get the student noa details for an eligible application when an eligible public institution user tries to access it.", async () => {
57+
it("Should get the student noa details for an eligible full time application when an eligible public institution user tries to access it.", async () => {
5658
// Arrange
5759

5860
// Student has an application to the institution eligible for NOA.
@@ -65,21 +67,30 @@ describe("AssessmentInstitutionsController(e2e)-getAssessmentNOA", () => {
6567
},
6668
);
6769
await db.msfaaNumber.save(currentMSFAA);
68-
const application = await saveFakeApplicationDisbursements(db.dataSource, {
69-
institution: collegeF,
70-
institutionLocation: collegeFLocation,
71-
student,
72-
msfaaNumber: currentMSFAA,
73-
});
70+
const application = await saveFakeApplicationDisbursements(
71+
db.dataSource,
72+
{
73+
institution: collegeF,
74+
institutionLocation: collegeFLocation,
75+
student,
76+
msfaaNumber: currentMSFAA,
77+
},
78+
{
79+
offeringIntensity: OfferingIntensity.fullTime,
80+
createSecondDisbursement: true,
81+
},
82+
);
7483
const assessment = application.currentAssessment;
75-
const [firstDisbursementSchedule] =
76-
application.currentAssessment.disbursementSchedules;
84+
const [firstDisbursementSchedule, secondDisbursementSchedule] =
85+
assessment.disbursementSchedules;
7786

78-
const awards = {};
79-
firstDisbursementSchedule.disbursementValues.forEach((disbursement) => {
80-
awards[`disbursement1${disbursement.valueCode.toLowerCase()}`] =
81-
disbursement.valueAmount;
82-
});
87+
const firstDisbursementScheduleAwards = createNOADisbursementScheduleAwards(
88+
firstDisbursementSchedule,
89+
1,
90+
);
91+
92+
const secondDisbursementScheduleAwards =
93+
createNOADisbursementScheduleAwards(secondDisbursementSchedule, 2);
8394

8495
const endpoint = `/institutions/assessment/student/${student.id}/application/${application.id}/assessment/${assessment.id}/noa`;
8596
const institutionUserToken = await getInstitutionToken(
@@ -112,8 +123,127 @@ describe("AssessmentInstitutionsController(e2e)-getAssessmentNOA", () => {
112123
firstDisbursementSchedule.disbursementScheduleStatus,
113124
disbursement1TuitionRemittance:
114125
firstDisbursementSchedule.tuitionRemittanceRequestedAmount,
115-
...awards,
126+
...firstDisbursementScheduleAwards,
127+
disbursement2COEStatus: secondDisbursementSchedule.coeStatus,
128+
disbursement2Date: getDateOnlyFormat(
129+
secondDisbursementSchedule.disbursementDate,
130+
),
131+
disbursement2Id: secondDisbursementSchedule.id,
132+
disbursement2MSFAACancelledDate:
133+
secondDisbursementSchedule.msfaaNumber?.cancelledDate,
134+
disbursement2MSFAADateSigned:
135+
secondDisbursementSchedule.msfaaNumber?.dateSigned,
136+
disbursement2MSFAAId: secondDisbursementSchedule.msfaaNumber?.id,
137+
disbursement2MSFAANumber: MASKED_MSFAA_NUMBER,
138+
disbursement2Status:
139+
secondDisbursementSchedule.disbursementScheduleStatus,
140+
disbursement2TuitionRemittance:
141+
secondDisbursementSchedule.tuitionRemittanceRequestedAmount,
142+
...secondDisbursementScheduleAwards,
116143
},
144+
eligibleAmount: 2,
145+
fullName: getUserFullName(application.student.user),
146+
locationName: assessment.offering.institutionLocation.name,
147+
noaApprovalStatus: assessment.noaApprovalStatus,
148+
offeringIntensity: assessment.offering.offeringIntensity,
149+
offeringStudyEndDate: getDateOnlyFormat(
150+
assessment.offering.studyEndDate,
151+
),
152+
offeringStudyStartDate: getDateOnlyFormat(
153+
assessment.offering.studyStartDate,
154+
),
155+
programName: assessment.offering.educationProgram.name,
156+
});
157+
});
158+
159+
it("Should get the student noa details for an eligible part time application when an eligible public institution user tries to access it.", async () => {
160+
// Arrange
161+
162+
// Student has an application to the institution eligible for NOA.
163+
const student = await saveFakeStudent(db.dataSource);
164+
165+
const currentMSFAA = createFakeMSFAANumber(
166+
{ student },
167+
{
168+
msfaaState: MSFAAStates.Signed,
169+
},
170+
);
171+
await db.msfaaNumber.save(currentMSFAA);
172+
const application = await saveFakeApplicationDisbursements(
173+
db.dataSource,
174+
{
175+
institution: collegeF,
176+
institutionLocation: collegeFLocation,
177+
student,
178+
msfaaNumber: currentMSFAA,
179+
},
180+
{
181+
offeringIntensity: OfferingIntensity.partTime,
182+
createSecondDisbursement: true,
183+
},
184+
);
185+
const assessment = application.currentAssessment;
186+
const [firstDisbursementSchedule, secondDisbursementSchedule] =
187+
assessment.disbursementSchedules;
188+
189+
const firstDisbursementScheduleAwards = createNOADisbursementScheduleAwards(
190+
firstDisbursementSchedule,
191+
1,
192+
);
193+
194+
const secondDisbursementScheduleAwards =
195+
createNOADisbursementScheduleAwards(secondDisbursementSchedule, 2);
196+
197+
const endpoint = `/institutions/assessment/student/${student.id}/application/${application.id}/assessment/${assessment.id}/noa`;
198+
const institutionUserToken = await getInstitutionToken(
199+
InstitutionTokenTypes.CollegeFUser,
200+
);
201+
202+
// Act/Assert
203+
await request(app.getHttpServer())
204+
.get(endpoint)
205+
.auth(institutionUserToken, BEARER_AUTH_TYPE)
206+
.expect(HttpStatus.OK)
207+
.expect({
208+
applicationId: application.id,
209+
applicationNumber: application.applicationNumber,
210+
applicationStatus: application.applicationStatus,
211+
assessment: assessment.assessmentData,
212+
disbursement: {
213+
disbursement1COEStatus: firstDisbursementSchedule.coeStatus,
214+
disbursement1Date: getDateOnlyFormat(
215+
firstDisbursementSchedule.disbursementDate,
216+
),
217+
disbursement1Id: firstDisbursementSchedule.id,
218+
disbursement1MSFAACancelledDate:
219+
firstDisbursementSchedule.msfaaNumber?.cancelledDate,
220+
disbursement1MSFAADateSigned:
221+
firstDisbursementSchedule.msfaaNumber?.dateSigned,
222+
disbursement1MSFAAId: firstDisbursementSchedule.msfaaNumber?.id,
223+
disbursement1MSFAANumber: MASKED_MSFAA_NUMBER,
224+
disbursement1Status:
225+
firstDisbursementSchedule.disbursementScheduleStatus,
226+
disbursement1TuitionRemittance:
227+
firstDisbursementSchedule.tuitionRemittanceRequestedAmount,
228+
...firstDisbursementScheduleAwards,
229+
disbursement2COEStatus: secondDisbursementSchedule.coeStatus,
230+
disbursement2Date: getDateOnlyFormat(
231+
secondDisbursementSchedule.disbursementDate,
232+
),
233+
disbursement2Id: secondDisbursementSchedule.id,
234+
disbursement2MSFAACancelledDate:
235+
secondDisbursementSchedule.msfaaNumber?.cancelledDate,
236+
disbursement2MSFAADateSigned:
237+
secondDisbursementSchedule.msfaaNumber?.dateSigned,
238+
disbursement2MSFAAId: secondDisbursementSchedule.msfaaNumber?.id,
239+
disbursement2MSFAANumber: MASKED_MSFAA_NUMBER,
240+
disbursement2Status:
241+
secondDisbursementSchedule.disbursementScheduleStatus,
242+
disbursement2TuitionRemittance:
243+
secondDisbursementSchedule.tuitionRemittanceRequestedAmount,
244+
...secondDisbursementScheduleAwards,
245+
},
246+
eligibleAmount: 2,
117247
fullName: getUserFullName(application.student.user),
118248
locationName: assessment.offering.institutionLocation.name,
119249
noaApprovalStatus: assessment.noaApprovalStatus,
@@ -254,6 +384,34 @@ describe("AssessmentInstitutionsController(e2e)-getAssessmentNOA", () => {
254384
});
255385
});
256386

387+
/**
388+
* Disbursement awards.
389+
*/
390+
interface DisbursementAwards {
391+
[disbursementValueCode: string]: number;
392+
}
393+
394+
/**
395+
* Creates disbursement schedule awards based on the given schedule and disbursement number.
396+
* @param disbursementSchedule Disbursement schedule containing disbursement values.
397+
* @param disbursementNumber Number associated with the disbursement schedule (e.g., 1 for the first, 2 for the second).
398+
* @returns Disbursement awards with keys like 'disbursement1code' and values as amounts.
399+
*/
400+
function createNOADisbursementScheduleAwards(
401+
disbursementSchedule: DisbursementSchedule,
402+
disbursementNumber: 1 | 2,
403+
): DisbursementAwards {
404+
const disbursementScheduleAwards: DisbursementAwards = {};
405+
406+
disbursementSchedule.disbursementValues.forEach((disbursementValue) => {
407+
disbursementScheduleAwards[
408+
`disbursement${disbursementNumber}${disbursementValue.valueCode.toLowerCase()}`
409+
] = disbursementValue.valueAmount;
410+
});
411+
412+
return disbursementScheduleAwards;
413+
}
414+
257415
afterAll(async () => {
258416
await app?.close();
259417
});

sources/packages/backend/apps/api/src/route-controllers/assessment/assessment.controller.service.ts

+22
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ export class AssessmentControllerService {
9090
assessment.offering.studyStartDate,
9191
),
9292
offeringStudyEndDate: getDateOnlyFormat(assessment.offering.studyEndDate),
93+
eligibleAmount: this.sumDisbursementValueAmounts(
94+
assessment.disbursementSchedules,
95+
),
9396
disbursement: this.populateDisbursementAwardValues(
9497
assessment.disbursementSchedules,
9598
{ maskMSFAA: options?.maskMSFAA },
@@ -152,6 +155,25 @@ export class AssessmentControllerService {
152155
return disbursementDetails;
153156
}
154157

158+
/**
159+
* Calculate the sum of value amount for each disbursement value within the disbursement schedules.
160+
* @param disbursementSchedules disbursement schedule details.
161+
* @returns The total sum of value amount across all disbursement schedules.
162+
*/
163+
private sumDisbursementValueAmounts(
164+
disbursementSchedules: DisbursementSchedule[],
165+
): number {
166+
return disbursementSchedules
167+
.flatMap(
168+
(disbursementSchedule) => disbursementSchedule.disbursementValues,
169+
)
170+
.reduce(
171+
(accumulator, disbursementValue) =>
172+
accumulator + disbursementValue.valueAmount,
173+
0,
174+
);
175+
}
176+
155177
/**
156178
* Get estimated and actual(if present) award details of an assessment.
157179
* @param assessmentId assessment to which awards details belong to.

sources/packages/backend/apps/api/src/route-controllers/assessment/models/assessment.dto.ts

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export class AssessmentNOAAPIOutDTO {
6565
offeringIntensity: OfferingIntensity;
6666
offeringStudyStartDate: string;
6767
offeringStudyEndDate: string;
68+
eligibleAmount: number;
6869
disbursement: Record<string, string | number>;
6970
noaApprovalStatus: AssessmentStatus;
7071
applicationStatus: ApplicationStatus;

sources/packages/web/src/services/http/dto/Assessment.dto.ts

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export interface AssessmentNOAAPIOutDTO {
5858
offeringStudyEndDate: string;
5959
msfaaNumber: string;
6060
disbursement: unknown;
61+
eligibleAmount: number;
6162
noaApprovalStatus: AssessmentStatus;
6263
applicationStatus: ApplicationStatus;
6364
}

0 commit comments

Comments
 (0)