Skip to content

Commit

Permalink
#3768 - Report Creation and E2E tests (#4039)
Browse files Browse the repository at this point in the history
## Report Creation

- [x] Created a new dynamic report `CAS Supplier Maintenance Updates`
which captures the updates from student since their CAS Supplier is set
to be valid.

![image](https://github.com/user-attachments/assets/47481f4f-f29b-4c1a-ab99-d157f3574835)


![image](https://github.com/user-attachments/assets/5ec554bf-0cac-4f3d-95b9-26fcc34cd464)

- [x] To identify the students who had one or more updates to their
profile from CAS perspective, student profile data is compared with the
`student_profile_snapshot` column of CAS supplier.
**Note**: All the data comparisons are string comparison ignoring the
case.

- [x] Data is sorted by `sims.students.updated_at`
- [x] Byte Order Mark Update: Update the code to NOT add the BYTE ORDER
MARK character when report is empty as it is not required.

## Rollback Evidence


![image](https://github.com/user-attachments/assets/3ab8edc3-f44f-4eb4-9d88-81f4c639af5e)


## E2E Tests

- [x] Added E2E tests to validate the report data when student updates
their
  - Last name
  - SIN
  - Address line 1
  - Postal code
- [x] Added E2E test to ensure the comparison of data is not case
sensitive. (Used last name as scenario).


![image](https://github.com/user-attachments/assets/1133f411-400e-44fd-81b9-ac3e12e77071)
dheepak-aot authored Dec 11, 2024
1 parent 7a58eb3 commit 9547f75
Showing 12 changed files with 575 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -239,6 +239,7 @@ export const APPLICATION_RESTRICTION_BYPASS_NOT_FOUND =
*/
export const APPLICATION_RESTRICTION_BYPASS_IS_NOT_ACTIVE =
"APPLICATION_RESTRICTION_BYPASS_IS_NOT_ACTIVE";

/**
* Student not found error code.
*/
Original file line number Diff line number Diff line change
@@ -38,19 +38,25 @@ import {
InstitutionLocation,
OfferingIntensity,
ProgramIntensity,
Student,
SupplierStatus,
WorkflowData,
} from "@sims/sims-db";
import { addDays, getISODateOnlyString } from "@sims/utilities";
import { DataSource } from "typeorm";
import { createFakeEducationProgram } from "@sims/test-utils/factories/education-program";
import { createFakeSINValidation } from "@sims/test-utils/factories/sin-validation";
import { getPSTPDTDateFormatted } from "@sims/test-utils/utils";
import { MinistryReportsFilterAPIInDTO } from "apps/api/src/route-controllers/report/models/report.dto";

describe("ReportAestController(e2e)-exportReport", () => {
let app: INestApplication;
let appModule: TestingModule;
let db: E2EDataSources;
let appDataSource: DataSource;
let formService: FormService;
let sharedCASSupplierUpdatedStudent: Student;
let casSupplierMaintenanceUpdatesPayload: MinistryReportsFilterAPIInDTO;

beforeAll(async () => {
const { nestApplication, module, dataSource } =
@@ -65,6 +71,13 @@ describe("ReportAestController(e2e)-exportReport", () => {
AppAESTModule,
FormService,
);
// Shared student used for CAS Supplier maintenance updates report.
sharedCASSupplierUpdatedStudent = await saveFakeStudent(db.dataSource);
// Build payload for CAS Supplier maintenance updates report to use across tests.
casSupplierMaintenanceUpdatesPayload = {
reportName: "CAS_Supplier_Maintenance_Updates_Report",
params: {},
};
});

it("Should generate the eCert Feedback Errors report when a report generation request is made with the appropriate offering intensity and date range.", async () => {
@@ -1426,6 +1439,285 @@ describe("ReportAestController(e2e)-exportReport", () => {
});
});

it(
"Should generate CAS Supplier maintenance updates report with the student details of the given student" +
" when last name of the student is updated after the CAS supplier is set to be valid.",
async () => {
// Arrange
// Save a CASSupplier for the student.
await saveFakeCASSupplier(
db,
{ student: sharedCASSupplierUpdatedStudent },
{
initialValues: {
supplierStatus: SupplierStatus.Verified,
isValid: true,
},
},
);
// Update the student's last name.
sharedCASSupplierUpdatedStudent.user.lastName = "Updated Last Name";
await db.student.save(sharedCASSupplierUpdatedStudent);

const endpoint = "/aest/report";
const ministryUserToken = await getAESTToken(
AESTGroups.BusinessAdministrators,
);

const dryRunSubmissionMock = jest.fn().mockResolvedValue({
valid: true,
formName: FormNames.ExportFinancialReports,
data: { data: casSupplierMaintenanceUpdatesPayload },
});
formService.dryRunSubmission = dryRunSubmissionMock;

// Act/Assert
await request(app.getHttpServer())
.post(endpoint)
.send(casSupplierMaintenanceUpdatesPayload)
.auth(ministryUserToken, BEARER_AUTH_TYPE)
.expect(HttpStatus.CREATED)
.then((response) => {
const fileContent = response.request.res["text"];
const parsedResult = parse(fileContent, {
header: true,
});
const expectedReportData = buildCASSupplierMaintenanceUpdatesReport(
sharedCASSupplierUpdatedStudent,
{ lastNameUpdated: true },
);
const [actualReportData] = parsedResult.data as Record<
string,
string
>[];
expect(parsedResult.data.length).toBe(1);
expect(actualReportData).toEqual(expectedReportData);
});
},
);

it(
"Should generate CAS Supplier maintenance updates report with the student details of the given student" +
" when SIN number of the student is updated after the CAS supplier is set to be valid.",
async () => {
// Arrange
// Save a CASSupplier for the student.
await saveFakeCASSupplier(
db,
{ student: sharedCASSupplierUpdatedStudent },
{
initialValues: {
supplierStatus: SupplierStatus.Verified,
isValid: true,
},
},
);
// Update the student's SIN.
const newSINValidation = createFakeSINValidation({
student: sharedCASSupplierUpdatedStudent,
});
sharedCASSupplierUpdatedStudent.sinValidation = newSINValidation;
await db.student.save(sharedCASSupplierUpdatedStudent);

const endpoint = "/aest/report";
const ministryUserToken = await getAESTToken(
AESTGroups.BusinessAdministrators,
);

const dryRunSubmissionMock = jest.fn().mockResolvedValue({
valid: true,
formName: FormNames.ExportFinancialReports,
data: { data: casSupplierMaintenanceUpdatesPayload },
});
formService.dryRunSubmission = dryRunSubmissionMock;

// Act/Assert
await request(app.getHttpServer())
.post(endpoint)
.send(casSupplierMaintenanceUpdatesPayload)
.auth(ministryUserToken, BEARER_AUTH_TYPE)
.expect(HttpStatus.CREATED)
.then((response) => {
const fileContent = response.request.res["text"];
const parsedResult = parse(fileContent, {
header: true,
});
const expectedReportData = buildCASSupplierMaintenanceUpdatesReport(
sharedCASSupplierUpdatedStudent,
{ sinUpdated: true },
);
const [actualReportData] = parsedResult.data as Record<
string,
string
>[];
expect(parsedResult.data.length).toBe(1);
expect(actualReportData).toEqual(expectedReportData);
});
},
);

it(
"Should generate CAS Supplier maintenance updates report with the student details of the given student" +
" when 'address line 1' of the student is updated after the CAS supplier is set to be valid.",
async () => {
// Arrange
// Save a CASSupplier for the student.
await saveFakeCASSupplier(
db,
{ student: sharedCASSupplierUpdatedStudent },
{
initialValues: {
supplierStatus: SupplierStatus.Verified,
isValid: true,
},
},
);
// Update the student's address line 1.
sharedCASSupplierUpdatedStudent.contactInfo.address.addressLine1 =
"Updated Address Line 1";
await db.student.save(sharedCASSupplierUpdatedStudent);

const endpoint = "/aest/report";
const ministryUserToken = await getAESTToken(
AESTGroups.BusinessAdministrators,
);

const dryRunSubmissionMock = jest.fn().mockResolvedValue({
valid: true,
formName: FormNames.ExportFinancialReports,
data: { data: casSupplierMaintenanceUpdatesPayload },
});
formService.dryRunSubmission = dryRunSubmissionMock;

// Act/Assert
await request(app.getHttpServer())
.post(endpoint)
.send(casSupplierMaintenanceUpdatesPayload)
.auth(ministryUserToken, BEARER_AUTH_TYPE)
.expect(HttpStatus.CREATED)
.then((response) => {
const fileContent = response.request.res["text"];
const parsedResult = parse(fileContent, {
header: true,
});
const expectedReportData = buildCASSupplierMaintenanceUpdatesReport(
sharedCASSupplierUpdatedStudent,
{ addressLine1Updated: true },
);
const [actualReportData] = parsedResult.data as Record<
string,
string
>[];
expect(parsedResult.data.length).toBe(1);
expect(actualReportData).toEqual(expectedReportData);
});
},
);

it(
"Should generate CAS Supplier maintenance updates report with the student details of the given student" +
" when postal code of the student is updated after the CAS supplier is set to be valid.",
async () => {
// Arrange
// Save a CASSupplier for the student.
await saveFakeCASSupplier(
db,
{ student: sharedCASSupplierUpdatedStudent },
{
initialValues: {
supplierStatus: SupplierStatus.Verified,
isValid: true,
},
},
);
// Update the student's postal code.
sharedCASSupplierUpdatedStudent.contactInfo.address.postalCode =
"Updated postal code";
await db.student.save(sharedCASSupplierUpdatedStudent);

const endpoint = "/aest/report";
const ministryUserToken = await getAESTToken(
AESTGroups.BusinessAdministrators,
);

const dryRunSubmissionMock = jest.fn().mockResolvedValue({
valid: true,
formName: FormNames.ExportFinancialReports,
data: { data: casSupplierMaintenanceUpdatesPayload },
});
formService.dryRunSubmission = dryRunSubmissionMock;

// Act/Assert
await request(app.getHttpServer())
.post(endpoint)
.send(casSupplierMaintenanceUpdatesPayload)
.auth(ministryUserToken, BEARER_AUTH_TYPE)
.expect(HttpStatus.CREATED)
.then((response) => {
const fileContent = response.request.res["text"];
const parsedResult = parse(fileContent, {
header: true,
});
const expectedReportData = buildCASSupplierMaintenanceUpdatesReport(
sharedCASSupplierUpdatedStudent,
{ postalCodeUpdated: true },
);
const [actualReportData] = parsedResult.data as Record<
string,
string
>[];
expect(parsedResult.data.length).toBe(1);
expect(actualReportData).toEqual(expectedReportData);
});
},
);

it(
"Should generate CAS Supplier maintenance updates report without the student details of the given student" +
" when last name of the student is updated just to change the case to upper case characters.",
async () => {
// Arrange
// Save a CASSupplier for the student.
await saveFakeCASSupplier(
db,
{ student: sharedCASSupplierUpdatedStudent },
{
initialValues: {
supplierStatus: SupplierStatus.Verified,
isValid: true,
},
},
);
// Update the student's last name to upper case.
sharedCASSupplierUpdatedStudent.user.lastName =
sharedCASSupplierUpdatedStudent.user.lastName.toUpperCase();
await db.student.save(sharedCASSupplierUpdatedStudent);

const endpoint = "/aest/report";
const ministryUserToken = await getAESTToken(
AESTGroups.BusinessAdministrators,
);

const dryRunSubmissionMock = jest.fn().mockResolvedValue({
valid: true,
formName: FormNames.ExportFinancialReports,
data: { data: casSupplierMaintenanceUpdatesPayload },
});
formService.dryRunSubmission = dryRunSubmissionMock;

// Act/Assert
await request(app.getHttpServer())
.post(endpoint)
.send(casSupplierMaintenanceUpdatesPayload)
.auth(ministryUserToken, BEARER_AUTH_TYPE)
.expect(HttpStatus.CREATED)
.then((response) => {
const fileContent = response.request.res["text"] as string;
expect(fileContent).toBe("");
});
},
);

/**
* Converts education program offering object into a key-value pair object matching the result data.
* @param fakeOffering an education program offering record.
@@ -1492,4 +1784,61 @@ describe("ReportAestController(e2e)-exportReport", () => {
"Offering Intensity": "",
};
}

/**
* Build CAS Supplier maintenance updates report data.
* @param student student.
* @param options expected report data options.
* - `firstNameUpdated` indicates if the first name of the student is updated.
* - `lastNameUpdated` indicates if the last name of the student is updated.
* - `sinUpdated` indicates if the SIN number of the student is updated.
* - `addressLine1Updated` indicates if the address line 1 of the student is updated.
* - `cityUpdated` indicates if the city of the student is updated.
* - `provinceUpdated` indicates if the province of the student is updated.
* - `postalCodeUpdated` indicates if the postal code of the student is updated.
* - `countryUpdated` indicates if the country of the student is updated.
* @returns report data.
*/
function buildCASSupplierMaintenanceUpdatesReport(
student: Student,
options?: {
firstNameUpdated?: boolean;
lastNameUpdated?: boolean;
sinUpdated?: boolean;
addressLine1Updated?: boolean;
cityUpdated?: boolean;
provinceUpdated?: boolean;
postalCodeUpdated?: boolean;
countryUpdated?: boolean;
},
): Record<string, string> {
return {
"Given Names": student.user.firstName,
"Last Name": student.user.lastName,
SIN: student.sinValidation.sin,
"Address Line 1": student.contactInfo.address.addressLine1,
City: student.contactInfo.address.city,
Province: student.contactInfo.address.provinceState,
"Postal Code": student.contactInfo.address.postalCode,
Country: student.contactInfo.address.country,
"Disability Status": student.disabilityStatus,
"Profile Type": student.user.identityProviderType ?? "",
"Student Updated Date": getPSTPDTDateFormatted(student.updatedAt),
Supplier: student.casSupplier.supplierNumber,
Site: student.casSupplier.supplierAddress.supplierSiteCode,
"Protected Supplier": student.casSupplier.supplierProtected?.toString(),
"Protected Site": student.casSupplier.supplierAddress.siteProtected,
"Supplier Verified Date": getPSTPDTDateFormatted(
student.casSupplier.supplierStatusUpdatedOn,
),
"First Name Updated": options?.firstNameUpdated ? "true" : "false",
"Last Name Updated": options?.lastNameUpdated ? "true" : "false",
"SIN Updated": options?.sinUpdated ? "true" : "false",
"Address Line 1 Updated": options?.addressLine1Updated ? "true" : "false",
"City Updated": options?.cityUpdated ? "true" : "false",
"Province Updated": options?.provinceUpdated ? "true" : "false",
"Postal Code Updated": options?.postalCodeUpdated ? "true" : "false",
"Country Updated": options?.countryUpdated ? "true" : "false",
};
}
});
Original file line number Diff line number Diff line change
@@ -24,6 +24,7 @@ enum MinistryReportNames {
InstitutionDesignation = "Institution_Designation_Report",
StudentUnmetNeed = "Ministry_Student_Unmet_Need_Report",
ProgramAndOfferingStatus = "Program_And_Offering_Status_Report",
CASSupplierMaintenanceUpdates = "CAS_Supplier_Maintenance_Updates_Report",
}

/**
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ import {
import {
getFileNameAsCurrentTimestamp,
CustomNamedError,
UTF8_BYTE_ORDER_MARK,
appendByteOrderMark,
} from "@sims/utilities";
import { Response } from "express";
import { Readable } from "stream";
@@ -99,12 +99,10 @@ export class ReportControllerService {
const filename = `${reportName}_${timestamp}.csv`;
// Adding byte order mark characters to the original file content as applications
// like excel would look for BOM characters to view the file as UTF8 encoded.
const byteOrderMarkBuffer = Buffer.from(UTF8_BYTE_ORDER_MARK);
const fileContentBuffer = Buffer.from(fileContent);
const responseBuffer = Buffer.concat([
byteOrderMarkBuffer,
fileContentBuffer,
]);
// Append byte order mark characters only if the file content is not empty.
const responseBuffer = fileContent
? appendByteOrderMark(fileContent)
: Buffer.from("");
response.setHeader(
"Content-Disposition",
`attachment; filename=${filename}`,
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { MigrationInterface, QueryRunner } from "typeorm";
import { getSQLFileData } from "../utilities/sqlLoader";

export class AddCASSupplierMaintenanceReport1733340941443
implements MigrationInterface
{
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
getSQLFileData(
"Create-cas-supplier-maintenance-updates-report.sql",
"Reports",
),
);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
getSQLFileData(
"Rollback-create-cas-supplier-maintenance-updates-report.sql",
"Reports",
),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
INSERT INTO
sims.report_configs (report_name, report_sql)
VALUES
(
'CAS_Supplier_Maintenance_Updates_Report',
'WITH cas_supplier_report AS (
SELECT
COALESCE(student_user.first_name, '''') AS student_first_name,
student_user.last_name AS student_last_name,
student_user.identity_provider_type AS student_profile_type,
student.updated_at AS student_updated_date,
student.disability_status AS student_disability_status,
sin_validation.sin AS student_sin,
student.contact_info -> ''address'' ->> ''addressLine1'' AS student_address_line_1,
student.contact_info -> ''address'' ->> ''city'' AS student_city,
COALESCE(
student.contact_info -> ''address'' ->> ''provinceState'',
''''
) AS student_province,
student.contact_info -> ''address'' ->> ''postalCode'' AS student_postal_code,
student.contact_info -> ''address'' ->> ''country'' AS student_country,
cas_supplier.supplier_number AS cas_supplier,
cas_supplier.supplier_protected AS cas_supplier_protected,
cas_supplier.supplier_address ->> ''supplierSiteCode'' AS cas_site,
cas_supplier.supplier_address ->> ''siteProtected'' AS cas_site_protected,
cas_supplier.supplier_status_updated_on AS cas_supplier_verified_date,
COALESCE(
cas_supplier.student_profile_snapshot ->> ''firstName'',
''''
) AS cas_snapshot_first_name,
COALESCE(
cas_supplier.student_profile_snapshot ->> ''lastName'',
''''
) AS cas_snapshot_last_name,
COALESCE(
cas_supplier.student_profile_snapshot ->> ''sin'',
''''
) AS cas_snapshot_sin,
COALESCE(
cas_supplier.student_profile_snapshot ->> ''addressLine1'',
''''
) AS cas_snapshot_address_line_1,
COALESCE(
cas_supplier.student_profile_snapshot ->> ''city'',
''''
) AS cas_snapshot_city,
COALESCE(
cas_supplier.student_profile_snapshot ->> ''province'',
''''
) AS cas_snapshot_province,
COALESCE(
cas_supplier.student_profile_snapshot ->> ''postalCode'',
''''
) AS cas_snapshot_postal_code,
COALESCE(
cas_supplier.student_profile_snapshot ->> ''country'',
''''
) AS cas_snapshot_country
FROM
sims.students student
INNER JOIN sims.cas_suppliers cas_supplier on student.cas_supplier_id = cas_supplier.id
INNER JOIN sims.users student_user on student.user_id = student_user.id
INNER JOIN sims.sin_validations sin_validation on student.sin_validation_id = sin_validation.id
WHERE
cas_supplier.is_valid = true
AND (
jsonb_build_object(
''firstName'',
student_user.first_name,
''lastName'',
student_user.last_name,
''sin'',
sin_validation.sin,
''addressLine1'',
student.contact_info -> ''address'' ->> ''addressLine1'',
''city'',
student.contact_info -> ''address'' ->> ''city'',
''province'',
student.contact_info -> ''address'' ->> ''provinceState'',
''postalCode'',
student.contact_info -> ''address'' ->> ''postalCode'',
''country'',
student.contact_info -> ''address'' ->> ''country''
) :: text NOT ILIKE jsonb_build_object(
''firstName'',
cas_supplier.student_profile_snapshot ->> ''firstName'',
''lastName'',
cas_supplier.student_profile_snapshot ->> ''lastName'',
''sin'',
cas_supplier.student_profile_snapshot ->> ''sin'',
''addressLine1'',
cas_supplier.student_profile_snapshot ->> ''addressLine1'',
''city'',
cas_supplier.student_profile_snapshot ->> ''city'',
''province'',
cas_supplier.student_profile_snapshot ->> ''province'',
''postalCode'',
cas_supplier.student_profile_snapshot ->> ''postalCode'',
''country'',
cas_supplier.student_profile_snapshot ->> ''country''
) :: text
)
)
SELECT
cas_supplier_report.student_first_name AS "Given Names",
cas_supplier_report.student_last_name AS "Last Name",
cas_supplier_report.student_sin AS "SIN",
cas_supplier_report.student_address_line_1 AS "Address Line 1",
cas_supplier_report.student_city AS "City",
cas_supplier_report.student_province AS "Province",
cas_supplier_report.student_postal_code AS "Postal Code",
cas_supplier_report.student_country AS "Country",
cas_supplier_report.student_disability_status AS "Disability Status",
cas_supplier_report.student_profile_type AS "Profile Type",
TO_CHAR(
(cas_supplier_report.student_updated_date AT TIME ZONE ''America/Vancouver''),
''YYYY-MM-DD''
) AS "Student Updated Date",
cas_supplier_report.cas_supplier AS "Supplier",
cas_supplier_report.cas_site AS "Site",
cas_supplier_report.cas_supplier_protected AS "Protected Supplier",
cas_supplier_report.cas_site_protected AS "Protected Site",
TO_CHAR(
(cas_supplier_report.cas_supplier_verified_date AT TIME ZONE ''America/Vancouver''),
''YYYY-MM-DD''
) AS "Supplier Verified Date",
cas_supplier_report.student_first_name NOT ILIKE cas_supplier_report.cas_snapshot_first_name AS "First Name Updated",
cas_supplier_report.student_last_name NOT ILIKE cas_supplier_report.cas_snapshot_last_name AS "Last Name Updated",
cas_supplier_report.student_sin NOT ILIKE cas_supplier_report.cas_snapshot_sin AS "SIN Updated",
cas_supplier_report.student_address_line_1 NOT ILIKE cas_supplier_report.cas_snapshot_address_line_1 AS "Address Line 1 Updated",
cas_supplier_report.student_city NOT ILIKE cas_supplier_report.cas_snapshot_city AS "City Updated",
cas_supplier_report.student_province NOT ILIKE cas_supplier_report.cas_snapshot_province AS "Province Updated",
cas_supplier_report.student_postal_code NOT ILIKE cas_supplier_report.cas_snapshot_postal_code AS "Postal Code Updated",
cas_supplier_report.student_country NOT ILIKE cas_supplier_report.cas_snapshot_country AS "Country Updated"
FROM
cas_supplier_report
ORDER BY
student_updated_date;'
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
DELETE from
sims.report_configs
WHERE
report_name = 'CAS_Supplier_Maintenance_Updates_Report';
Original file line number Diff line number Diff line change
@@ -164,12 +164,12 @@ export interface SupplierAddress {
* Student profile snapshot information.
*/
export interface StudentProfileSnapshot {
firstName: string;
firstName?: string;
lastName: string;
sin: string;
addressLine1: string;
city: string;
province: string;
province?: string;
postalCode: string;
country: string;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
CASSupplier,
Student,
StudentProfileSnapshot,
SupplierAddress,
SupplierStatus,
User,
@@ -38,7 +39,9 @@ export async function saveFakeCASSupplier(
student = await saveFakeStudent(db.dataSource);
}
const casSupplier = createFakeCASSupplier({ student, auditUser }, options);
return db.casSupplier.save(casSupplier);
student.casSupplier = casSupplier;
await db.student.save(student);
return student.casSupplier;
}

/**
@@ -86,7 +89,7 @@ export function createFakeCASSupplier(
lastUpdated: new Date(),
};
casSupplier.supplierProtected = true;
casSupplier.isValid = false;
casSupplier.isValid = options?.initialValues.isValid ?? false;
}
casSupplier.supplierName = `${faker.name.lastName()}, ${faker.name.firstName()}`;
casSupplier.supplierNumber = faker.datatype
@@ -98,6 +101,21 @@ export function createFakeCASSupplier(
casSupplier.supplierStatusUpdatedOn = new Date();
casSupplier.creator = relations.auditUser;
casSupplier.student = relations.student;
// Build the student profile snapshot based on valid status.
const studentProfileSnapshot: StudentProfileSnapshot = casSupplier.isValid
? {
firstName: relations.student.user.firstName,
lastName: relations.student.user.lastName,
sin: relations.student.sinValidation.sin,
addressLine1: relations.student.contactInfo.address.addressLine1,
city: relations.student.contactInfo.address.city,
province: relations.student.contactInfo.address.provinceState,
postalCode: relations.student.contactInfo.address.postalCode,
country: relations.student.contactInfo.address.country,
}
: null;
casSupplier.studentProfileSnapshot =
options?.initialValues?.studentProfileSnapshot ?? studentProfileSnapshot;

return casSupplier;
}
14 changes: 14 additions & 0 deletions sources/packages/backend/libs/test-utils/src/utils/date-utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DATE_ONLY_ISO_FORMAT, PST_TIMEZONE } from "@sims/utilities";
import * as dayjs from "dayjs";

/**
@@ -35,3 +36,16 @@ export const addMilliSeconds = (
.add(milliSecondsToAdd, "millisecond")
.toDate();
};

/**
* Convert the date to PST/PDT(PST: UTC−08:00, PDT: UTC−07:00) and format.
* @param date date.
* @param format date format.
* @returns converted date in the given format.
*/
export function getPSTPDTDateFormatted(
date: Date | string,
format = DATE_ONLY_ISO_FORMAT,
): string {
return dayjs(new Date(date)).tz(PST_TIMEZONE, false).format(format);
}
13 changes: 12 additions & 1 deletion sources/packages/backend/libs/utilities/src/string-utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FILE_DEFAULT_ENCODING } from "@sims/utilities";
import { FILE_DEFAULT_ENCODING, UTF8_BYTE_ORDER_MARK } from "@sims/utilities";

const REPLACE_LINE_BREAK_REGEX = /\r?\n|\r/g;

@@ -154,3 +154,14 @@ export function convertToASCII(rawContent?: string): Buffer | null {
export function convertToASCIIString(rawContent?: string): string | null {
return convertToASCII(rawContent)?.toString() ?? null;
}

/**
* Append UTF-8 BOM to the content.
* @param content content to append the BOM.
* @returns content with the BOM appended.
*/
export function appendByteOrderMark(content: string): Buffer {
const byteOrderMarkBuffer = Buffer.from(UTF8_BYTE_ORDER_MARK);
const fileContentBuffer = Buffer.from(content);
return Buffer.concat([byteOrderMarkBuffer, fileContentBuffer]);
}
4 changes: 4 additions & 0 deletions sources/packages/web/src/constants/report-constants.ts
Original file line number Diff line number Diff line change
@@ -10,6 +10,10 @@ export const INSTITUTION_REPORTS: OptionItemAPIOutDTO[] = [
];

export const MINISTRY_REPORTS: OptionItemAPIOutDTO[] = [
{
description: "CAS Supplier Maintenance Updates",
id: "CAS_Supplier_Maintenance_Updates_Report",
},
{ description: "Data Inventory", id: "Data_Inventory_Report" },
{ description: "Disbursements", id: "Disbursement_Report" },
{

0 comments on commit 9547f75

Please sign in to comment.