Skip to content

Commit e6f568a

Browse files
authored
#1501 - Process ECE Response file part 2 (#1947)
Process ECE Response file part 2
1 parent 2d01f6f commit e6f568a

File tree

20 files changed

+471
-47
lines changed

20 files changed

+471
-47
lines changed

sources/packages/backend/apps/api/src/route-controllers/confirmation-of-enrollment/models/confirmation-of-enrollment.dto.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import { Min, Max, IsNotEmpty, ValidateIf } from "class-validator";
22
import { COEStatus, ProgramInfoStatus } from "@sims/sims-db";
33
import { COEApprovalPeriodStatus } from "../../../services/disbursement-schedule/disbursement-schedule.models";
4-
import {
5-
COE_DENIED_REASON_OTHER_ID,
6-
MONEY_VALUE_FOR_UNKNOWN_MAX_VALUE,
7-
} from "../../../utilities";
4+
import { MONEY_VALUE_FOR_UNKNOWN_MAX_VALUE } from "../../../utilities";
5+
import { COE_DENIED_REASON_OTHER_ID } from "@sims/utilities";
86

97
export class ApplicationDetailsForCOEAPIOutDTO {
108
applicationProgramName: string;

sources/packages/backend/apps/api/src/utilities/application-utils.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Application, DisbursementSchedule } from "@sims/sims-db";
2-
import { COE_DENIED_REASON_OTHER_ID, PIR_DENIED_REASON_OTHER_ID } from ".";
2+
import { PIR_DENIED_REASON_OTHER_ID } from ".";
3+
import { COE_DENIED_REASON_OTHER_ID } from "@sims/utilities";
34
export const PIR_OR_DATE_OVERLAP_ERROR = "PIR_OR_DATE_OVERLAP_ERROR";
45
export const PIR_OR_DATE_OVERLAP_ERROR_MESSAGE =
56
"There is an existing application already with overlapping study period or a pending PIR.";

sources/packages/backend/apps/api/src/utilities/system-configurations-constants.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
export const PIR_DENIED_REASON_OTHER_ID = 1;
2-
export const COE_DENIED_REASON_OTHER_ID = 1;
32
// Timeout to handle the worst-case scenario where the commit/rollback
43
// was not executed due to a possible catastrophic failure.
54
export const SUPPORTING_USERS_TRANSACTION_IDLE_TIMEOUT_SECONDS = 10;

sources/packages/backend/apps/queue-consumers/src/processors/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ export * from "./schedulers/esdc-integration/msfaa-integration/msfaa-part-time-p
2424
export * from "./schedulers/sfas-integration/sfas-integration.scheduler";
2525
export * from "./schedulers/atbc-respone-integration/atbc-response-integration.scheduler";
2626
export * from "./schedulers/application/process-archive-application.scheduler";
27+
export * from "./schedulers/institution-integration/ece-response/ece-response-integration.scheduler";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { InjectQueue, Process, Processor } from "@nestjs/bull";
2+
import { QueueService } from "@sims/services/queue";
3+
import { QueueNames } from "@sims/utilities";
4+
import { InjectLogger, LoggerService } from "@sims/utilities/logger";
5+
import { Job, Queue } from "bull";
6+
import { QueueProcessSummaryResult } from "../../../models/processors.models";
7+
import { BaseScheduler } from "../../base-scheduler";
8+
import { ECEResponseProcessingService } from "@sims/integrations/institution-integration/ece-integration";
9+
10+
@Processor(QueueNames.ECEProcessResponseIntegration)
11+
export class ECEResponseIntegrationScheduler extends BaseScheduler<void> {
12+
constructor(
13+
@InjectQueue(QueueNames.ECEProcessResponseIntegration)
14+
schedulerQueue: Queue<void>,
15+
private readonly eceResponseProcessingService: ECEResponseProcessingService,
16+
queueService: QueueService,
17+
) {
18+
super(schedulerQueue, queueService);
19+
}
20+
21+
/**
22+
* Process all the institution ECE responses and update the enrolment accordingly.
23+
* @params job details.
24+
* @returns Processing result log.
25+
*/
26+
@Process()
27+
async processECERequest(
28+
job: Job<void>,
29+
): Promise<QueueProcessSummaryResult[]> {
30+
this.logger.log(
31+
`Processing ECE response integration job ${job.id} of type ${job.name}.`,
32+
);
33+
this.logger.log("Processing ECE response files ...");
34+
const processingResult = await this.eceResponseProcessingService.process();
35+
this.logger.log("Processing ECE response files completed.");
36+
await this.cleanSchedulerQueueHistory();
37+
return processingResult;
38+
}
39+
40+
@InjectLogger()
41+
logger: LoggerService;
42+
}

sources/packages/backend/apps/queue-consumers/src/queue-consumers.module.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import {
2323
SFASIntegrationScheduler,
2424
ATBCResponseIntegrationScheduler,
2525
ProcessArchiveApplicationsScheduler,
26+
ECEProcessIntegrationScheduler,
27+
ECEResponseIntegrationScheduler,
2628
} from "./processors";
2729
import {
2830
DisbursementScheduleSharedService,
@@ -53,7 +55,6 @@ import { StudentAssessmentService, ApplicationService } from "./services";
5355
import { SFASIntegrationModule } from "@sims/integrations/sfas-integration";
5456
import { ATBCIntegrationModule } from "@sims/integrations/atbc-integration";
5557
import { ECEIntegrationModule } from "@sims/integrations/institution-integration/ece-integration";
56-
import { ECEProcessIntegrationScheduler } from "./processors/schedulers/institution-integration/ece-request/ece-process-integration.scheduler";
5758

5859
@Module({
5960
imports: [
@@ -107,6 +108,7 @@ import { ECEProcessIntegrationScheduler } from "./processors/schedulers/institut
107108
FullTimeMSFAAProcessResponseIntegrationScheduler,
108109
PartTimeMSFAAProcessResponseIntegrationScheduler,
109110
ProcessArchiveApplicationsScheduler,
111+
ECEResponseIntegrationScheduler,
110112
SystemUsersService,
111113
ApplicationService,
112114
ConfirmationOfEnrollmentService,

sources/packages/backend/libs/integrations/src/constants/system-configurations-constants.ts

+6
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,9 @@ export const DISBURSEMENT_FILE_GENERATION_ANTICIPATION_DAYS = 5;
99
* File name of the ECE response file sent by institutions.
1010
*/
1111
export const ECE_RESPONSE_FILE_NAME = "CONR_008.TXT";
12+
13+
/**
14+
* Reason to be saved while declining an enrolment through ECE file integration.
15+
*/
16+
export const ECE_RESPONSE_COE_DECLINED_REASON =
17+
"A reason was not provided by your institution. Please speak to your Financial Aid Officer at your institution for details.";

sources/packages/backend/libs/integrations/src/institution-integration/ece-integration/ece-files/ece-response-file-detail.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@ export class ECEResponseFileDetail extends ECEResponseFileRecord {
6464
getInvalidDataMessage(): string | undefined {
6565
const errors: string[] = [];
6666
if (isNaN(this.disbursementIdentifier)) {
67-
errors.push("invalid unique index number for the disbursement record.");
67+
errors.push("Invalid unique index number for the disbursement record.");
6868
}
69-
if (!this.applicationNumber) {
70-
errors.push("application number not found or invalid.");
69+
if (!this.applicationNumber?.trim()) {
70+
errors.push("Invalid application number.");
7171
}
7272
return errors.length ? errors.join(", ") : undefined;
7373
}

sources/packages/backend/libs/integrations/src/institution-integration/ece-integration/ece-integration.module.ts

+6
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ import {
33
RestrictionSharedService,
44
StudentRestrictionSharedService,
55
WorkflowClientService,
6+
ConfirmationOfEnrollmentService,
7+
SequenceControlService,
68
} from "@sims/services";
79
import { ConfigModule } from "@sims/utilities/config";
810
import {
911
DisbursementScheduleService,
12+
InstitutionLocationService,
1013
SshService,
1114
} from "@sims/integrations/services";
1215
import { ECEProcessingService } from "./ece.processing.service";
@@ -27,6 +30,9 @@ import { ECEResponseProcessingService } from "./ece-response.processing.service"
2730
RestrictionSharedService,
2831
ECEResponseIntegrationService,
2932
ECEResponseProcessingService,
33+
InstitutionLocationService,
34+
ConfirmationOfEnrollmentService,
35+
SequenceControlService,
3036
],
3137
exports: [ECEProcessingService, ECEResponseProcessingService],
3238
})

sources/packages/backend/libs/integrations/src/institution-integration/ece-integration/ece-response.integration.service.ts

+18-4
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,23 @@ export class ECEResponseIntegrationService extends SFTPIntegrationBase<
2323
async downloadResponseFile(
2424
remoteFilePath: string,
2525
): Promise<ECEResponseFileDetail[]> {
26-
const fileLines = await this.downloadResponseFileLines(remoteFilePath);
26+
const fileLines = await this.downloadResponseFileLines(remoteFilePath, {
27+
checkIfFileExist: true,
28+
});
29+
// Check if the file exist in the remote server.
30+
if (!fileLines) {
31+
return [];
32+
}
33+
// The file is not expected to be empty without content.
34+
if (!fileLines.length) {
35+
const error = "The ECE response file is empty and cannot be processed.";
36+
this.logger.error(error);
37+
throw new Error(error);
38+
}
2739
// Read the first line to check if the header record type is the expected one.
2840
const header = new ECEResponseFileHeader(fileLines.shift()); // Read and remove header.
2941
if (header.recordType !== RecordTypeCodes.ECEHeader) {
30-
const error = `The ECE response file ${remoteFilePath} has an invalid record type on header: ${header.recordType}`;
42+
const error = `The ECE response file has an invalid record type on header: ${header.recordType}`;
3143
this.logger.error(error);
3244
// If the header is not the expected one, throw an error.
3345
throw new Error(error);
@@ -42,14 +54,16 @@ export class ECEResponseIntegrationService extends SFTPIntegrationBase<
4254
}
4355
// The file is expected to have at least one detail record.
4456
if (!fileLines.length) {
45-
const error = `The ECE response file ${remoteFilePath} does not have any detail records to process.`;
57+
const error =
58+
"The ECE response file does not have any detail records to process.";
4659
this.logger.error(error);
4760
throw new Error(error);
4861
}
4962
// The total count of detail records is mentioned in the footer record and it is expected to
5063
// match the actual count of total records.
5164
if (fileLines.length !== footer.totalDetailRecords) {
52-
const error = `In ${remoteFilePath} the total count of detail records mentioned in the footer record does not match with the actual total details records count.`;
65+
const error =
66+
"In the total count of detail records mentioned in the footer record does not match with the actual total details records count.";
5367
this.logger.error(error);
5468
throw new Error(error);
5569
}

0 commit comments

Comments
 (0)