Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
createFakeDisbursementValue,
saveFakeApplication,
RestrictionCode,
saveFakeInstitutionRestriction,
} from "@sims/test-utils";
import {
ApplicationStatus,
Expand All @@ -29,6 +30,7 @@ import {
DisbursementValueType,
OfferingIntensity,
RestrictionActionType,
RestrictionType,
} from "@sims/sims-db";
import { getISODateOnlyString } from "@sims/utilities";
import { ECertFailedValidation } from "@sims/integrations/services/disbursement-schedule/disbursement-schedule.models";
Expand Down Expand Up @@ -111,8 +113,7 @@ describe("ApplicationStudentsController(e2e)-getApplicationWarnings", () => {
},
);

application.currentAssessment.workflowData.calculatedData.pdppdStatus =
true;
application.currentAssessment.workflowData.calculatedData.pdppdStatus = true;
await db.studentAssessment.save(application.currentAssessment);

const endpoint = `/students/application/${application.id}/warnings`;
Expand Down Expand Up @@ -264,6 +265,86 @@ describe("ApplicationStudentsController(e2e)-getApplicationWarnings", () => {
},
);

it(
"Should return a failed ecert validations array with stop disbursement institution restriction when" +
" there is an effective restriction on institution account for the application location and program" +
" and the offering intensity is part-time.",
async () => {
// Arrange
const student = await saveFakeStudent(db.dataSource);
const msfaaNumber = createFakeMSFAANumber(
{
student,
},
{
msfaaState: MSFAAStates.Signed,
msfaaInitialValues: {
offeringIntensity: OfferingIntensity.partTime,
},
},
);
await db.msfaaNumber.save(msfaaNumber);

// Mock user services to return the saved student.
await mockUserLoginInfo(appModule, student);

const application = await saveFakeApplicationDisbursements(
appDataSource,
{ student, msfaaNumber },
{
applicationStatus: ApplicationStatus.Completed,
offeringIntensity: OfferingIntensity.partTime,
firstDisbursementInitialValues: {
coeStatus: COEStatus.completed,
disbursementScheduleStatus: DisbursementScheduleStatus.Pending,
},
},
);

// Institution restriction.
const restriction = await db.restriction.findOne({
select: { id: true },
where: {
restrictionType: RestrictionType.Institution,
actionType: ArrayContains([
RestrictionActionType.StopPartTimeDisbursement,
]),
},
});
const location =
application.currentAssessment.offering.institutionLocation;
const program = application.currentAssessment.offering.educationProgram;
const institution =
application.currentAssessment.offering.institutionLocation.institution;
await saveFakeInstitutionRestriction(db, {
restriction,
institution,
location,
program,
});

const endpoint = `/students/application/${application.id}/warnings`;
const token = await getStudentToken(
FakeStudentUsersTypes.FakeStudentUserType1,
);

// Act/Assert
await request(app.getHttpServer())
.get(endpoint)
.auth(token, BEARER_AUTH_TYPE)
.expect(HttpStatus.OK)
.expect({
eCertFailedValidations: [
ECertFailedValidation.HasStopDisbursementInstitutionRestriction,
],
canAcceptAssessment: false,
eCertFailedValidationsInfo: {
hasEffectiveAviationRestriction: false,
},
});
},
);

it("Should return a failed ecert validations array with no estimated award amounts when no disbursements values are present.", async () => {
// Arrange
const student = await saveFakeStudent(db.dataSource);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
createFakeCRAIncomeVerification,
createFakeSupportingUser,
RestrictionCode,
saveFakeInstitutionRestriction,
} from "@sims/test-utils";
import {
Application,
Expand All @@ -43,6 +44,7 @@ import {
MSFAANumber,
OfferingIntensity,
RestrictionActionType,
RestrictionType,
Student,
StudentAppeal,
StudentAppealStatus,
Expand Down Expand Up @@ -806,6 +808,93 @@ describe("ApplicationStudentsController(e2e)-getCompletedApplicationDetails", ()
},
);

it(
"Should get application details with ecert failed validations array having stop disbursement institution restriction when" +
" there is an effective restriction on institution account for the application location and program" +
" and the offering intensity is part-time.",
async () => {
// Arrange
const student = await saveFakeStudent(db.dataSource);
const msfaaNumber = createFakeMSFAANumber(
{
student,
},
{
msfaaState: MSFAAStates.Signed,
msfaaInitialValues: { offeringIntensity: OfferingIntensity.partTime },
},
);
await db.msfaaNumber.save(msfaaNumber);

// Mock user services to return the saved student.
await mockUserLoginInfo(appModule, student);

const application = await saveFakeApplicationDisbursements(
appDataSource,
{ student, msfaaNumber },
{
applicationStatus: ApplicationStatus.Completed,
offeringIntensity: OfferingIntensity.partTime,
firstDisbursementInitialValues: {
coeStatus: COEStatus.completed,
disbursementScheduleStatus: DisbursementScheduleStatus.Pending,
},
},
);
const [firstDisbursement] =
application.currentAssessment.disbursementSchedules;

// Institution restriction.
const restriction = await db.restriction.findOne({
select: { id: true },
where: {
restrictionType: RestrictionType.Institution,
actionType: ArrayContains([
RestrictionActionType.StopPartTimeDisbursement,
]),
},
});
const location =
application.currentAssessment.offering.institutionLocation;
const program = application.currentAssessment.offering.educationProgram;
const institution =
application.currentAssessment.offering.institutionLocation.institution;
await saveFakeInstitutionRestriction(db, {
restriction,
institution,
location,
program,
});

const endpoint = `/students/application/${application.id}/completed`;
const token = await getStudentToken(
FakeStudentUsersTypes.FakeStudentUserType1,
);

// Act/Assert
await request(app.getHttpServer())
.get(endpoint)
.auth(token, BEARER_AUTH_TYPE)
.expect(HttpStatus.OK)
.expect({
firstDisbursement: {
coeStatus: firstDisbursement.coeStatus,
disbursementScheduleStatus:
firstDisbursement.disbursementScheduleStatus,
},
assessmentTriggerType: application.currentAssessment.triggerType,
hasActiveUnsuccessfulCompletionWeeks: false,
hasBlockFundingFeedbackError: false,
eCertFailedValidations: [
ECertFailedValidation.HasStopDisbursementInstitutionRestriction,
],
eCertFailedValidationsInfo: {
hasEffectiveAviationRestriction: false,
},
});
},
);

it(
"Should get application details with e-cert failed validation results having stop disbursement restriction and indicating effective aviation restriction" +
" in the validations info when the application is for aviation credential type 'commercialPilotTraining'" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
RelationshipStatus,
RestrictionActionType,
RestrictionBypassBehaviors,
RestrictionType,
User,
WorkflowData,
} from "@sims/sims-db";
Expand All @@ -30,6 +31,7 @@ import {
createFakeUser,
saveFakeApplicationDisbursements,
saveFakeApplicationRestrictionBypass,
saveFakeInstitutionRestriction,
saveFakeSFASIndividual,
saveFakeStudent,
saveFakeStudentAssessment,
Expand Down Expand Up @@ -63,6 +65,7 @@ import {
} from "./e-cert-utils";
import { RestrictionCode, SystemUsersService } from "@sims/services";
import { createFakeSFASApplication } from "@sims/test-utils/factories/sfas-application";
import { ConfigService } from "@sims/utilities/config";

describe(
describeQueueProcessorRootTest(QueueNames.FullTimeECertIntegration),
Expand Down Expand Up @@ -1915,6 +1918,109 @@ describe(
]);
});

it(
"Should block the disbursement and log the information when the institution" +
" has an effective institution restriction for the application location and program" +
` with action type ${RestrictionActionType.StopFullTimeDisbursement}.`,
async () => {
// Arrange
// Eligible COE basic properties.
const eligibleDisbursement: Partial<DisbursementSchedule> = {
coeStatus: COEStatus.completed,
coeUpdatedAt: new Date(),
};
// Student with valid SIN.
const student = await saveFakeStudent(db.dataSource);
// Valid MSFAA Number.
const msfaaNumber = await db.msfaaNumber.save(
createFakeMSFAANumber(
{ student },
{
msfaaState: MSFAAStates.Signed,
msfaaInitialValues: {
offeringIntensity: OfferingIntensity.fullTime,
},
},
),
);
const application = await saveFakeApplicationDisbursements(
db.dataSource,
{
student,
msfaaNumber,
},
{
offeringIntensity: OfferingIntensity.fullTime,
applicationStatus: ApplicationStatus.Completed,
currentAssessmentInitialValues: {
assessmentData: { weeks: 5 } as Assessment,
assessmentDate: new Date(),
},
firstDisbursementInitialValues: {
...eligibleDisbursement,
disbursementDate: getISODateOnlyString(addDays(1)),
},
},
);
// Institution restriction that blocks disbursement.
const restriction = await db.restriction.findOne({
select: { id: true },
where: {
restrictionType: RestrictionType.Institution,
actionType: ArrayContains([
RestrictionActionType.StopFullTimeDisbursement,
]),
},
});
const offering = application.currentAssessment.offering;
const location = offering.institutionLocation;
const program = offering.educationProgram;
const institution = location.institution;
// Add institution restriction for the application location and program.
await saveFakeInstitutionRestriction(db, {
restriction,
institution,
program,
location,
});
// Queued job.
const mockedJob = mockBullJob<void>();

// Act
const result = await processor.processQueue(mockedJob.job);

// Assert
// Assert uploaded file.
const uploadedFile = getUploadedFile(sftpClientMock);
const uploadedFileName = getUploadedFileName();
expect(uploadedFile.remoteFilePath).toBe(uploadedFileName);
// No records should be sent.
expect(result).toStrictEqual([
`Generated file: ${uploadedFileName}`,
"Uploaded records: 0",
]);
// Assert log messages for the blocked disbursement.
expect(
mockedJob.containLogMessages([
`Institution has an effective '${RestrictionActionType.StopFullTimeDisbursement}' restriction` +
` for program ${program.id} and location ${location.id} and the disbursement calculation will not proceed.`,
"The step determined that the calculation should be interrupted. This disbursement will not be part of the next e-Cert generation.",
]),
).toBe(true);
const [disbursement] =
application.currentAssessment.disbursementSchedules;
// Assert that the disbursement is still in status 'Pending' with date sent null.
const scheduleIsPending = await db.disbursementSchedule.exists({
where: {
id: disbursement.id,
dateSent: IsNull(),
disbursementScheduleStatus: DisbursementScheduleStatus.Pending,
},
});
expect(scheduleIsPending).toBe(true);
},
);

describe("Aviation credential full-time applications e-Cert generation", () => {
for (const {
aviationCredentialType,
Expand All @@ -1936,7 +2042,12 @@ describe(
const msfaaNumber = await db.msfaaNumber.save(
createFakeMSFAANumber(
{ student },
{ msfaaState: MSFAAStates.Signed },
{
msfaaState: MSFAAStates.Signed,
msfaaInitialValues: {
offeringIntensity: OfferingIntensity.fullTime,
},
},
),
);
const aviationCredentialApplication =
Expand Down Expand Up @@ -2457,7 +2568,8 @@ describe(
* @returns The uploaded file name
*/
function getUploadedFileName(): string {
const ftpRequestFolder = new ConfigService().esdcIntegration.ftpRequestFolder;
const fileDate = dayjs().format("YYYYMMDD");
const uploadedFileName = `MSFT-Request\\DPBC.EDU.FTECERTS.${fileDate}.001`;
const uploadedFileName = `${ftpRequestFolder}\\DPBC.EDU.FTECERTS.${fileDate}.001`;
return uploadedFileName;
}
Loading