Skip to content

Commit

Permalink
#4133 - SFAS to SIMS Bridge - PT App Cancellation Date & MSFAA (FT/PT…
Browse files Browse the repository at this point in the history
…) Change - Part 3 (#4182)

- Changed logic to: 
- Not consider part-time SFAS cancelled applications for program year
totals;
  - Not consider part-time application date overlap and; 
- Ignore full-time and part-time applications in the MSFAA number
process.
- Added E2E tests;
  • Loading branch information
andrepestana-aot authored Dec 30, 2024
1 parent 6b22b2c commit b35b5f1
Show file tree
Hide file tree
Showing 7 changed files with 340 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,104 @@ describe("ApplicationStudentsController(e2e)-submitApplication", () => {
},
);

it(
"Should submit an application for a student when there is a cancelled part-time application in SFAS with overlapping study dates" +
" ignoring the cancelled SFAS application.",
async () => {
// Arrange
const student = await saveFakeStudent(db.dataSource);
const sfasIndividual = await saveFakeSFASIndividual(db.dataSource, {
initialValues: {
lastName: student.user.lastName,
birthDate: student.birthDate,
sin: student.sinValidation.sin,
},
});
// Cancelled SFAS full time application with overlapping study dates.
const sfasPartTimeApplication = createFakeSFASPartTimeApplication(
{ individual: sfasIndividual },
{
initialValues: {
startDate: getISODateOnlyString(new Date()),
endDate: getISODateOnlyString(addDays(50)),
// The SFAS application is cancelled.
applicationCancelDate: getISODateOnlyString(new Date()),
},
},
);
await db.sfasPartTimeApplications.save(sfasPartTimeApplication);

// SIMS Offering having overlapping study period with SFAS.
const simsApplicationOfferingInitialValues = {
studyStartDate: getISODateOnlyString(addDays(30)),
studyEndDate: getISODateOnlyString(addDays(90)),
offeringIntensity: OfferingIntensity.partTime,
};
const simsApplication = createFakeApplication(
{
student,
},
{
initialValue: {
data: {},
applicationStatus: ApplicationStatus.Draft,
applicationStatusUpdatedOn: new Date(),
creator: systemUsersService.systemUser,
createdAt: new Date(),
} as Application,
},
);
const simsDraftApplication = await db.application.save(simsApplication);
const auditUser = await db.user.save(createFakeUser());
const simsApplicationOffering = await db.educationProgramOffering.save(
createFakeEducationProgramOffering(
{
auditUser,
institutionLocation: simsApplication.location,
},
{
initialValues: simsApplicationOfferingInitialValues,
},
),
);
const secondApplicationProgram = simsApplicationOffering.educationProgram;
const applicationData = {
selectedOfferingDate:
simsApplicationOfferingInitialValues.studyStartDate,
selectedOfferingEndDate:
simsApplicationOfferingInitialValues.studyEndDate,
howWillYouBeAttendingTheProgram:
simsApplicationOfferingInitialValues.offeringIntensity,
selectedProgram: secondApplicationProgram.id,
selectedOffering: simsApplicationOffering.id,
selectedLocation: simsApplication.location.id,
};
const payload = {
associatedFiles: [],
data: applicationData,
programYearId: simsApplication.programYear.id,
} as SaveApplicationAPIInDTO;
const endpoint = `/students/application/${simsDraftApplication.id}/submit`;
const token = await getStudentToken(
FakeStudentUsersTypes.FakeStudentUserType1,
);
const dryRunSubmissionMock = jest.fn().mockResolvedValue({
valid: true,
formName: FormNames.Application,
data: { data: applicationData },
});
formService.dryRunSubmission = dryRunSubmissionMock;
await mockUserLoginInfo(appModule, student);
// Act/Assert
await request(app.getHttpServer())
.patch(endpoint)
.send(payload)
.auth(token, BEARER_AUTH_TYPE)
.expect(HttpStatus.OK)
.expect({});
},
);

afterAll(async () => {
await app?.close();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,112 @@ describe("AssessmentController(e2e)-verifyAssessmentCalculationOrder", () => {
});
});

it("Should not sum the awards from SFAS and SFAS part-time applications data when the SFAS applications for the same student and program year are cancelled.", async () => {
// Arrange

// Create the student to be shared across the applications.
const student = await saveFakeStudent(db.dataSource);
// Get the program year for the start date.
const programYear = await db.programYear.findOne({ where: { id: 2 } });

// Current application having the first assessment already processed.
const currentApplication = await saveFakeApplicationDisbursements(
db.dataSource,
{ student },
{
offeringIntensity: OfferingIntensity.partTime,
applicationStatus: ApplicationStatus.InProgress,
currentAssessmentInitialValues: {
assessmentWorkflowId: "some fake id",
studentAssessmentStatus: StudentAssessmentStatus.Completed,
assessmentDate: addDays(30, programYear.startDate),
},
},
);
const firstAssessmentDate =
currentApplication.currentAssessment.assessmentDate;
// The start date for the first SFAS and SFAS part-time application record is set to the date before the first assessment date of the current application.
const legacyApplicationStartDate = faker.date.between(
programYear.startDate,
addDays(-1, firstAssessmentDate),
);
const legacyApplicationEndDate = addDays(30, firstAssessmentDate);

// Create the second assessment for the current application with a different assessment date.
const secondAssessment = createFakeStudentAssessment(
{
auditUser: currentApplication.student.user,
application: currentApplication,
offering: currentApplication.currentAssessment.offering,
},
{
initialValue: {
assessmentWorkflowId: "some fake id",
studentAssessmentStatus: StudentAssessmentStatus.InProgress,
},
},
);
currentApplication.currentAssessment = secondAssessment;
await db.application.save(currentApplication);

// SFAS Individual.
const sfasIndividual = await saveFakeSFASIndividual(db.dataSource, {
initialValues: {
lastName: student.user.lastName,
birthDate: student.birthDate,
sin: student.sinValidation.sin,
},
});

const fakeSFASApplication = createFakeSFASApplication(
{ individual: sfasIndividual },
{
initialValues: {
startDate: getISODateOnlyString(legacyApplicationStartDate),
endDate: getISODateOnlyString(legacyApplicationEndDate),
csgdAward: 9,
csgpAward: 10,
sbsdAward: 12,
bcagAward: 13,
applicationCancelDate: getISODateOnlyString(new Date()),
},
},
);
await db.sfasApplication.save(fakeSFASApplication);

const fakeSFASPartTimeApplication = createFakeSFASPartTimeApplication(
{ individual: sfasIndividual },
{
initialValues: {
startDate: getISODateOnlyString(legacyApplicationStartDate),
endDate: getISODateOnlyString(legacyApplicationEndDate),
csptAward: 2,
csgdAward: 3,
csgpAward: 4,
sbsdAward: 6,
bcagAward: 7,
applicationCancelDate: getISODateOnlyString(new Date()),
},
},
);
await db.sfasPartTimeApplications.save(fakeSFASPartTimeApplication);
// Act
const result = await assessmentController.verifyAssessmentCalculationOrder(
createFakeVerifyAssessmentCalculationOrderPayload(
currentApplication.currentAssessment.id,
),
);
// Assert
expect(FakeWorkerJobResult.getResultType(result)).toBe(
MockedZeebeJobResult.Complete,
);
// The calculation will not take cancelled SFAS and SFAS part-time application data.
expect(FakeWorkerJobResult.getOutputVariables(result)).toStrictEqual({
isReadyForCalculation: true,
latestCSLPBalance: 0,
});
});

it("Should not return any program year total awards or grants from awards from SFAS and SFAS part-time applications when there are no SIMS applications in the past and SFAS and SFAS part-time applications for the same student and program year.", async () => {
// Arrange

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,69 @@ describe("DisbursementController(e2e)-associateMSFAA", () => {
expect(cancelledMSFAANumber.cancelledDate).not.toBe(null);
});

it("Should not reuse the MSFAA number from SFAS full-time cancelled applications in SIMS when there is a MSFAA for a cancelled SFAS application.", async () => {
// Arrange
const legacyApplicationStartDate = addDays(-100);
const legacyApplicationEndDate = addDays(-10);

// Create student and save fake application with disbursements.
const student = await saveFakeStudent(db.dataSource);
const application = await saveFakeApplicationDisbursements(
db.dataSource,
{ student },
{
offeringIntensity: OfferingIntensity.fullTime,
applicationStatus: ApplicationStatus.InProgress,
},
);

const savedSFASIndividual = await saveFakeSFASIndividual(db.dataSource, {
initialValues: { student: student },
});
await db.sfasApplication.save(
createFakeSFASApplication(
{ individual: savedSFASIndividual },
{
initialValues: {
startDate: getISODateOnlyString(legacyApplicationStartDate),
endDate: getISODateOnlyString(legacyApplicationEndDate),
applicationCancelDate: getISODateOnlyString(new Date()),
},
},
),
);

// Act
const associateMSFAAPayload = createFakeAssociateMSFAAPayload({
assessmentId: application.currentAssessment.id,
});
const saveResult = await disbursementController.associateMSFAA(
associateMSFAAPayload,
);

// Asserts
expect(FakeWorkerJobResult.getResultType(saveResult)).toBe(
MockedZeebeJobResult.Complete,
);

// Fetch MSFAA Number for the student in SIMS.
const createdMSFAANumber = await db.msfaaNumber.findOne({
select: {
id: true,
msfaaNumber: true,
},
where: {
student: { id: student.id },
},
order: {
id: "DESC",
},
});
expect(createdMSFAANumber.msfaaNumber).not.toEqual(
savedSFASIndividual.msfaaNumber,
);
});

it("Should create new MSFAA Number for full-time applications by creating and activating in SIMS when MSFAA is not found or invalid SFAS application offering end date.", async () => {
// Arrange
const firstLegacyApplicationStartDate = addDays(-MAX_MSFAA_VALID_DAYS + 10);
Expand Down Expand Up @@ -617,6 +680,69 @@ describe("DisbursementController(e2e)-associateMSFAA", () => {
expect(secondDisbursementSchedule.msfaaNumber.id).toBe(savedMSFAANumber.id);
});

it("Should not reuse the MSFAA number from SFAS part-time cancelled applications in SIMS when there is a MSFAA for a cancelled SFAS application.", async () => {
// Arrange
const legacyApplicationStartDate = addDays(-100);
const legacyApplicationEndDate = addDays(-10);

// Create student and save fake application with disbursements.
const student = await saveFakeStudent(db.dataSource);
const application = await saveFakeApplicationDisbursements(
db.dataSource,
{ student },
{
offeringIntensity: OfferingIntensity.partTime,
applicationStatus: ApplicationStatus.InProgress,
},
);

const savedSFASIndividual = await saveFakeSFASIndividual(db.dataSource, {
initialValues: { student: student },
});
await db.sfasPartTimeApplications.save(
createFakeSFASPartTimeApplication(
{ individual: savedSFASIndividual },
{
initialValues: {
startDate: getISODateOnlyString(legacyApplicationStartDate),
endDate: getISODateOnlyString(legacyApplicationEndDate),
applicationCancelDate: getISODateOnlyString(new Date()),
},
},
),
);

// Act
const associateMSFAAPayload = createFakeAssociateMSFAAPayload({
assessmentId: application.currentAssessment.id,
});
const saveResult = await disbursementController.associateMSFAA(
associateMSFAAPayload,
);

// Asserts
expect(FakeWorkerJobResult.getResultType(saveResult)).toBe(
MockedZeebeJobResult.Complete,
);

// Fetch MSFAA Number for the student in SIMS.
const createdMSFAANumber = await db.msfaaNumber.findOne({
select: {
id: true,
msfaaNumber: true,
},
where: {
student: { id: student.id },
},
order: {
id: "DESC",
},
});
expect(createdMSFAANumber.msfaaNumber).not.toEqual(
savedSFASIndividual.msfaaNumber,
);
});

it("Should create new MSFAA number for part-time application when MSFAA is found for previously signed disbursement and its offering end date is not between the valid date.", async () => {
// Arrange
const student = await saveFakeStudent(db.dataSource);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export class SFASApplicationService extends DataModelService<SFASApplication> {
individual: true,
},
where: {
applicationCancelDate: IsNull(),
individual: { student: { id: studentId }, msfaaNumber: Not(IsNull()) },
endDate: MoreThanOrEqual(getISODateOnlyString(minMSFAAValidDate)),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ export class SFASPartTimeApplicationsService extends DataModelService<SFASPartTi
.createQueryBuilder("sfasPTApplication")
.select(["sfasPTApplication.id"])
.innerJoin("sfasPTApplication.individual", "sfasPTstudent")
.where("lower(sfasPTstudent.lastName) = lower(:lastName)", { lastName })
.where("sfasPTApplication.applicationCancelDate IS NULL")
.andWhere("lower(sfasPTstudent.lastName) = lower(:lastName)", {
lastName,
})
.andWhere("sfasPTstudent.sin = :sin", { sin })
.andWhere("sfasPTstudent.birthDate = :birthDate", { birthDate })
.andWhere(
Expand Down Expand Up @@ -89,6 +92,7 @@ export class SFASPartTimeApplicationsService extends DataModelService<SFASPartTi
individual: true,
},
where: {
applicationCancelDate: IsNull(),
individual: { student: { id: studentId }, msfaaNumber: Not(IsNull()) },
endDate: MoreThanOrEqual(getISODateOnlyString(minMSFAAValidDate)),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,8 @@ export class AssessmentSequentialProcessingService {
.addSelect("SUM(sfasPTApplication.bcagAward)", "BCAG")
.addSelect("SUM(sfasPTApplication.csptAward)", "CSPT")
.innerJoin("sfasPTApplication.individual", "sfasStudent")
.where("lower(sfasStudent.lastName) = lower(:lastName)", { lastName })
.where("sfasPTApplication.applicationCancelDate IS NULL")
.andWhere("lower(sfasStudent.lastName) = lower(:lastName)", { lastName })
.andWhere("sfasStudent.sin = :sin", { sin })
.andWhere("sfasStudent.birthDate = :birthDate", { birthDate })
.andWhere("sfasPTApplication.startDate >= :startDate", {
Expand Down
Loading

0 comments on commit b35b5f1

Please sign in to comment.