Skip to content

Commit

Permalink
#257 - Merge Release v1.15.0 into main (#3940)
Browse files Browse the repository at this point in the history
Merge Release v1.15 to Main

#260 [- Merge Hotfix v1.14.1 into
Release v1.15.0
(](https://github.com/bcgov/SIMS/pull/3940/commits/a94ebcebe3869618244a23d6da5edee887ceeac9)https://github.com/bcgov/SIMS/pull/3885[)]

#3889 [- Increase Logging for SFTP
and file handling
(](https://github.com/bcgov/SIMS/pull/3940/commits/ba81ad5fc894521463837bdcc704c2b8ef64afd3)https://github.com/bcgov/SIMS/pull/3897[)]

#3889 [- Increase Logging for SFTP
and file handling - (Catch
fix)](c791b9e)

#3889 - Increase Logging for SFTP
and file handling - SSH Lib Upgrade

#3912 [- Allow Disbursement Receipts
File Integration Scheduler For Pa…]

#3924 [- Ecert Creation - "Prefer
not to answer" Gender (]

#3922 [- PT Monthly Loan Balance -
Update Balance (]

#3896 [- Mask Institution User
Income View
(](https://github.com/bcgov/SIMS/pull/3940/commits/ec1c63516c55b557d3a76018c0390f0ff9c3ab9a)https://github.com/bcgov/SIMS/pull/3932[)]

---------

Co-authored-by: Shashank Shekhar <[email protected]>
Co-authored-by: Andre Pestana <[email protected]>
Co-authored-by: Lewis Chen <[email protected]>
Co-authored-by: Andrew Boni Signori <[email protected]>
Co-authored-by: Dheepak Ramanathan <[email protected]>
  • Loading branch information
6 people authored Nov 14, 2024
1 parent 019ac73 commit 6a27866
Show file tree
Hide file tree
Showing 35 changed files with 305 additions and 113 deletions.
7 changes: 4 additions & 3 deletions sources/packages/backend/apps/api/src/app.exception.filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ export class AppAllExceptionsFilter extends BaseExceptionFilter {
const request: Request = ctx.getRequest();

// Logging Additional info
this.logger.error("Unhandled exception");
this.logger.error(`Request path [${request.path}]`);
this.logger.error(`${JSON.stringify(exception)}`);
this.logger.error(
`Unhandled exception, request path: ${request.path}`,
exception,
);

// Calling super
super.catch(exception, host);
Expand Down
10 changes: 3 additions & 7 deletions sources/packages/backend/apps/api/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,13 @@ async function bootstrap() {
await app.listen(port);
// Logging node http server error
app.getHttpServer().on("error", (error: unknown) => {
logger.error(`Application server receive ${error}`, undefined, "Bootstrap");
logger.error("Application server receive.", error, "Bootstrap");
exit(1);
});
logger.log(`Application is listing on port ${port}`, "Bootstrap");
}
bootstrap().catch((error: unknown) => {
const logger = new LoggerService();
logger.error(
`Application bootstrap exception: ${error}`,
undefined,
"Bootstrap-Main",
);
const logger = new LoggerService("Bootstrap-Main");
logger.error("Application bootstrap exception.", error);
exit(1);
});
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,10 @@ describe("AssessmentInstitutionsController(e2e)-getAssessmentNOA", () => {
applicationId: application.id,
applicationNumber: application.applicationNumber,
applicationStatus: application.applicationStatus,
assessment: assessment.assessmentData,
assessment: {
...assessment.assessmentData,
totalFamilyIncome: "XXXXX",
},
disbursement: {
disbursement1COEStatus: firstDisbursementSchedule.coeStatus,
disbursement1Date: getDateOnlyFullMonthFormat(
Expand Down Expand Up @@ -210,7 +213,10 @@ describe("AssessmentInstitutionsController(e2e)-getAssessmentNOA", () => {
applicationId: application.id,
applicationNumber: application.applicationNumber,
applicationStatus: application.applicationStatus,
assessment: assessment.assessmentData,
assessment: {
...assessment.assessmentData,
totalFamilyIncome: "XXXXX",
},
disbursement: {
disbursement1COEStatus: firstDisbursementSchedule.coeStatus,
disbursement1Date: getDateOnlyFullMonthFormat(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ export class AssessmentAESTController extends BaseController {
): Promise<AssessmentNOAAPIOutDTO> {
return this.assessmentControllerService.getAssessmentNOA(assessmentId, {
maskMSFAA: false,
maskTotalFamilyIncome: false,
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
ApplicationExceptionService,
MASKED_MSFAA_NUMBER,
ApplicationOfferingChangeRequestService,
MASKED_MONEY_AMOUNT,
} from "../../services";
import {
AssessmentNOAAPIOutDTO,
Expand All @@ -34,6 +35,7 @@ import {
RequestAssessmentTypeAPIOutDTO,
AssessmentHistorySummaryAPIOutDTO,
DynamicAwardValue,
AssessmentAPIOutDTO,
} from "./models/assessment.dto";
import { getUserFullName } from "../../utilities";
import { getDateOnlyFormat, getDateOnlyFullMonthFormat } from "@sims/utilities";
Expand Down Expand Up @@ -63,6 +65,8 @@ export class AssessmentControllerService {
* - `studentId` optional student for authorization when needed.
* - `applicationId` application id,
* - `maskMSFAA` mask MSFAA or not.
* - `maskTotalFamilyIncome` mask total family income resulted
* from the assessment calculations. Defaults to true if not provided.
* @returns notice of assessment data.
*/
async getAssessmentNOA(
Expand All @@ -71,8 +75,10 @@ export class AssessmentControllerService {
studentId?: number;
applicationId?: number;
maskMSFAA?: boolean;
maskTotalFamilyIncome?: boolean;
},
): Promise<AssessmentNOAAPIOutDTO> {
const maskTotalFamilyIncome = options?.maskTotalFamilyIncome ?? true;
const assessment = await this.assessmentService.getAssessmentForNOA(
assessmentId,
{ studentId: options?.studentId, applicationId: options?.applicationId },
Expand All @@ -88,8 +94,13 @@ export class AssessmentControllerService {
);
}

const assessmentDTO: AssessmentAPIOutDTO = assessment.assessmentData;
if (maskTotalFamilyIncome) {
assessmentDTO.totalFamilyIncome = MASKED_MONEY_AMOUNT;
}

return {
assessment: assessment.assessmentData,
assessment: assessmentDTO,
applicationId: assessment.application.id,
noaApprovalStatus: assessment.noaApprovalStatus,
applicationStatus: assessment.application.applicationStatus,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export class AssessmentStudentsController extends BaseController {
return this.assessmentControllerService.getAssessmentNOA(assessmentId, {
studentId: userToken.studentId,
maskMSFAA: false,
maskTotalFamilyIncome: false,
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,28 @@ export class AssessmentHistorySummaryAPIOutDTO {
hasUnsuccessfulWeeks?: boolean;
}

/**
* Assessment calculations output with possible
* adjustments for API output DTO.
*/
export type AssessmentAPIOutDTO = Omit<Assessment, "totalFamilyIncome"> & {
/**
* Total family income to be considered.
* Users without proper access should see only a masked value.
* This property overrides the original type to allow to keep
* the property as number and also as a string, when a mask is required.
*/
totalFamilyIncome: number | string;
};

export class AssessmentNOAAPIOutDTO {
@ApiProperty({
description:
"Dynamic output of the workflow calculation. " +
"Contains data that could represent a part-time or a full-time assessment. " +
"Part-time and full-time will have some common and some specific properties for each payload.",
})
assessment: Assessment;
assessment: AssessmentAPIOutDTO;
applicationId: number;
applicationNumber: string;
applicationCurrentAssessmentId: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export const ASSESSMENT_NOT_FOUND = "ASSESSMENT_NOT_FOUND";
export const ASSESSMENT_INVALID_OPERATION_IN_THE_CURRENT_STATE =
"ASSESSMENT_INVALID_OPERATION_IN_THE_CURRENT_STATE";
export const MASKED_MSFAA_NUMBER = "XXXXXXXXXX";
export const MASKED_MONEY_AMOUNT = "XXXXX";
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ export abstract class BaseScheduler<T> implements OnApplicationBootstrap {
QueueNames.FullTimeMSFAAIntegration,
QueueNames.FullTimeECertIntegration,
QueueNames.FullTimeFeedbackIntegration,
QueueNames.DisbursementReceiptsFileIntegration,
QueueNames.FullTimeMSFAAProcessResponseIntegration,
QueueNames.IER12Integration,
QueueNames.ECEProcessIntegration,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ describe(
formatDate(coeUpdatedAtDate, "YYYYMMDD"),
);
expect(record1Parsed.postalCode).toBe("V1V 1V1");
expect(record1Parsed.gender).toBe("X");
// TODO Add other fields as needed.
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ describe(
const recordParsed = new PartTimeCertRecordParser(record);
expect(recordParsed.recordType).toBe("02");
expect(recordParsed.hasUser(student.user)).toBe(true);
expect(recordParsed.gender).toBe("X");
expect(recordParsed.disbursementAmount).toBe("000123500");
// TODO include other fields as needed.

Expand All @@ -410,6 +411,70 @@ describe(
expect(scheduleIsSent).toBe(true);
});

it("Should create an e-Cert with valid student profile data when the student has necessary profile data and gender defined as 'Prefer not to answer'.", async () => {
// Arrange
// Student with valid SIN.
const student = await saveFakeStudent(db.dataSource, null, {
initialValue: {
gender: "preferNotToAnswer",
},
});
// Valid MSFAA Number.
const msfaaNumber = await db.msfaaNumber.save(
createFakeMSFAANumber(
{ student },
{
msfaaState: MSFAAStates.Signed,
msfaaInitialValues: {
offeringIntensity: OfferingIntensity.partTime,
},
},
),
);
// Student application eligible for e-Cert.
await saveFakeApplicationDisbursements(
db.dataSource,
{
student,
msfaaNumber,
firstDisbursementValues: [
createFakeDisbursementValue(
DisbursementValueType.CanadaLoan,
"CSLP",
1234.57,
),
],
},
{
offeringIntensity: OfferingIntensity.partTime,
applicationStatus: ApplicationStatus.Completed,
currentAssessmentInitialValues: {
assessmentData: { weeks: 5 } as Assessment,
assessmentDate: new Date(),
},
firstDisbursementInitialValues: {
coeStatus: COEStatus.completed,
},
},
);
// Queued job.
const { job } = mockBullJob<void>();

// Act
await processor.processECert(job);

// Assert
// Assert student profile data.
const uploadedFile = getUploadedFile(sftpClientMock);
expect(uploadedFile.fileLines).toHaveLength(3);
const [, record] = uploadedFile.fileLines;
// TODO: include other student profile fields as needed.
const recordParsed = new PartTimeCertRecordParser(record);
expect(recordParsed.recordType).toBe("02");
expect(recordParsed.hasUser(student.user)).toBe(true);
expect(recordParsed.gender).toBe(" ");
});

it("Should adjust tuition remittance when requested tuition remittance is greater than the max tuition remittance.", async () => {
// Arrange

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ export abstract class ECertRecordParser {
*/
abstract get lastName(): string;

/**
* Student's gender.
*/
abstract get gender(): string;

/**
* Validate if the first name and last names belongs to the
* provided student user.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ export class FullTimeCertRecordParser extends ECertRecordParser {
return this.record.substring(151, 176).trim();
}

/**
* Student's gender.
*/
get gender(): string {
return this.record.substring(594, 595);
}

/**
* Federal CSLF amount (loan).
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ export class PartTimeCertRecordParser extends ECertRecordParser {
return this.record.substring(2, 27).trim();
}

/**
* Student's gender.
*/
get gender(): string {
return this.record.substring(54, 55);
}

/**
* Disbursement amount.
* This field includes 2 decimals in the file.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ describe(
`2000000001000900000000PABCD19950630${processDateFormatted}Doe AAAAAAC John EEEEIIII MMNOOOOO Address Line 1 UUUU Address Line 2 Calgary AB H1H 1H1 Canada [email protected] PT `,
);
expect(msfaaPartTimeOtherCountry).toBe(
`2000000001001900000001PEFDG20000101${processDateFormatted}Other Doe aaaaaac Jane eeeeiiii Snooooo Address Line 1 Some city in the United S United States [email protected] PT `,
`2000000001001900000001PEFDG20000101${processDateFormatted}Other Doe aaaaaac Jane eeeeiiii Snooooo Address Line 1 Some city in the United S United States [email protected] PT `,
);
expect(msfaaPartTimeRelationshipOther).toBe(
`2000000001002900000002PIHKL20011231${processDateFormatted}Other Doe uuuuyy Other John ???? FOAddress Line 1 Address Line 2 Victoria BC H1H 1H1 Canada [email protected] PT `,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ describe(
expect(studentLoanBalance).toEqual([
{
balanceDate: "2023-12-31",
cslBalance: 148154,
cslBalance: 1481.54,
},
]);
});
Expand Down Expand Up @@ -330,7 +330,7 @@ describe(
},
{
balanceDate: currentBalanceDate,
cslBalance: 148154,
cslBalance: 1481.54,
},
]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@ import {
CRASFTPResponseFile,
ProcessSftpResponseResult,
} from "./cra-integration.models";
import { getUTCNow } from "@sims/utilities";
import { getUTCNow, parseJSONError } from "@sims/utilities";
import * as path from "path";
import { ConfigService } from "@sims/utilities/config";
import { CRAIntegrationService } from "./cra.integration.service";
import { CRAIncomeVerificationsService } from "../services";
import { SFTP_ARCHIVE_DIRECTORY } from "@sims/integrations/constants";

const INCOME_VERIFICATION_TAG = "VERIFICATION_ID";

Expand Down Expand Up @@ -275,14 +274,15 @@ export class CRAIncomeVerificationProcessingService {

try {
// Archive file.
await this.craService.archiveFile(remoteFilePath, SFTP_ARCHIVE_DIRECTORY);
await this.craService.archiveFile(remoteFilePath);
} catch (error) {
// Log the error but allow the process to continue.
// If there was an issue only during the file archiving, it will be
// processed again and could be archived in the second attempt.
const logMessage = `Error while archiving CRA response file: ${remoteFilePath}`;
this.logger.error(logMessage);
result.errorsSummary.push(logMessage);
result.errorsSummary.push(parseJSONError(error));
this.logger.error(logMessage, error);
}

return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ import {
ReportsFilterModel,
} from "@sims/services";
import { DAILY_DISBURSEMENT_REPORT_NAME } from "@sims/services/constants";
import { getISODateOnlyString } from "@sims/utilities";
import { SFTP_ARCHIVE_DIRECTORY } from "@sims/integrations/constants";
import { getISODateOnlyString, parseJSONError } from "@sims/utilities";

/**
* Disbursement schedule map which consists of disbursement schedule id for a document number.
Expand Down Expand Up @@ -160,15 +159,12 @@ export class DisbursementReceiptProcessingService {

try {
// Archiving the file once it has been processed.
await this.integrationService.archiveFile(
remoteFilePath,
SFTP_ARCHIVE_DIRECTORY,
);
await this.integrationService.archiveFile(remoteFilePath);
} catch (error) {
result.errorsSummary.push(
`Error while archiving disbursement receipt file: ${remoteFilePath}`,
);
result.errorsSummary.push(error);
const logMessage = `Unexpected error while archiving disbursement receipt file: ${remoteFilePath}.`;
result.errorsSummary.push(logMessage);
result.errorsSummary.push(parseJSONError(error));
this.logger.error(logMessage, error);
}
return result;
}
Expand Down
Loading

0 comments on commit 6a27866

Please sign in to comment.