From 4490c27f863a8135b0361def2fab77211271b7bc Mon Sep 17 00:00:00 2001 From: Tiago Graf Date: Mon, 5 Jan 2026 15:03:06 -0800 Subject: [PATCH 1/8] Add migration to update report --- ...66-UpdateDisbursementReportForecastDate.ts | 33 ++++++++ ...ate-disbursements-report-forecast-date.sql | 79 ++++++++++++++++++ ...ate-disbursements-report-forecast-date.sql | 80 +++++++++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 sources/packages/backend/apps/db-migrations/src/migrations/1767653404966-UpdateDisbursementReportForecastDate.ts create mode 100644 sources/packages/backend/apps/db-migrations/src/sql/Reports/Rollback-update-disbursements-report-forecast-date.sql create mode 100644 sources/packages/backend/apps/db-migrations/src/sql/Reports/Update-disbursements-report-forecast-date.sql diff --git a/sources/packages/backend/apps/db-migrations/src/migrations/1767653404966-UpdateDisbursementReportForecastDate.ts b/sources/packages/backend/apps/db-migrations/src/migrations/1767653404966-UpdateDisbursementReportForecastDate.ts new file mode 100644 index 0000000000..0b1a496ac9 --- /dev/null +++ b/sources/packages/backend/apps/db-migrations/src/migrations/1767653404966-UpdateDisbursementReportForecastDate.ts @@ -0,0 +1,33 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; +import { getSQLFileData } from "../utilities/sqlLoader"; + +/** + * Update the disbursement reports to include the "Forecast Date" column. + */ +export class UpdateDisbursementReportForecastDate1767653404966 implements MigrationInterface { + /** + * Update the disbursement reports to include the "Forecast Date" column. + * @param queryRunner + */ + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + getSQLFileData( + "Update-disbursements-report-forecast-date.sql", + "Reports", + ), + ); + } + + /** + * Rollback the disbursement reports update that included the "Forecast Date" column. + * @param queryRunner + */ + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + getSQLFileData( + "Rollback-update-disbursements-report-forecast-date.sql", + "Reports", + ), + ); + } +} diff --git a/sources/packages/backend/apps/db-migrations/src/sql/Reports/Rollback-update-disbursements-report-forecast-date.sql b/sources/packages/backend/apps/db-migrations/src/sql/Reports/Rollback-update-disbursements-report-forecast-date.sql new file mode 100644 index 0000000000..05341d86c8 --- /dev/null +++ b/sources/packages/backend/apps/db-migrations/src/sql/Reports/Rollback-update-disbursements-report-forecast-date.sql @@ -0,0 +1,79 @@ +/** Rollback the disbursement report SQL to the previous version before the + * forecast date update. + */ +UPDATE + sims.report_configs +SET + report_sql = ( + '( + select + to_char(dr.disburse_date, ''YYYY-MM-DD'') as "Date of Disbursement", + dr.student_sin as "SIN", + app.application_number as "Application Number", + ds.document_number as "Certificate Number", + drv.grant_type as "Funding Code", + drv.grant_amount as "Disbursement Amount" + from + sims.disbursement_receipts dr + inner join sims.disbursement_receipt_values drv on drv.disbursement_receipt_id = dr.id + inner join sims.disbursement_schedules ds on ds.id = dr.disbursement_schedule_id + inner join sims.student_assessments sa on sa.id = ds.student_assessment_id + inner join sims.applications app on app.id = sa.application_id + inner join sims.education_programs_offerings epo on epo.id = sa.offering_id + where + epo.offering_intensity = any(:offeringIntensity) + and dr.disburse_date between :startDate + and :endDate + union + all + select + to_char(dr.disburse_date, ''YYYY-MM-DD'') as "Date of Disbursement", + dr.student_sin as "SIN", + app.application_number as "Application Number", + ds.document_number as "Certificate Number", + dv.value_code as "Funding Code", + dv.effective_amount as "Disbursement Amount" + from + sims.disbursement_receipts dr + inner join sims.disbursement_receipt_values drv on drv.disbursement_receipt_id = dr.id + inner join sims.disbursement_schedules ds on ds.id = dr.disbursement_schedule_id + inner join sims.disbursement_values dv on dv.disbursement_schedule_id = ds.id + inner join sims.student_assessments sa on sa.id = ds.student_assessment_id + inner join sims.applications app on app.id = sa.application_id + inner join sims.education_programs_offerings epo on epo.id = sa.offering_id + where + epo.offering_intensity = any(:offeringIntensity) + and dr.funding_type <> ''FE'' + and drv.grant_type = ''BCSG'' + and dv.value_type = ''BC Grant'' + and dr.disburse_date between :startDate + and :endDate + union + all + select + to_char(dr.disburse_date, ''YYYY-MM-DD'') as "Date of Disbursement", + dr.student_sin as "SIN", + app.application_number as "Application Number", + ds.document_number as "Certificate Number", + case + when dr.funding_type = ''BC'' then ''BCSL'' + when dr.funding_type = ''FE'' then ''CSL'' + end as "Funding Code", + dr.total_disbursed_amount as "Disbursement Amount" + from + sims.disbursement_receipts dr + inner join sims.disbursement_schedules ds on ds.id = dr.disbursement_schedule_id + inner join sims.student_assessments sa on sa.id = ds.student_assessment_id + inner join sims.applications app on app.id = sa.application_id + inner join sims.education_programs_offerings epo on epo.id = sa.offering_id + where + epo.offering_intensity = any(:offeringIntensity) + and dr.disburse_date between :startDate + and :endDate + ) + order by + "Date of Disbursement", + "Certificate Number"' + ) +WHERE + report_name = 'Disbursement_Report'; \ No newline at end of file diff --git a/sources/packages/backend/apps/db-migrations/src/sql/Reports/Update-disbursements-report-forecast-date.sql b/sources/packages/backend/apps/db-migrations/src/sql/Reports/Update-disbursements-report-forecast-date.sql new file mode 100644 index 0000000000..38b3e864e0 --- /dev/null +++ b/sources/packages/backend/apps/db-migrations/src/sql/Reports/Update-disbursements-report-forecast-date.sql @@ -0,0 +1,80 @@ +/** Update the Disbursements Report to include the "Forecast Date" column. **/ +UPDATE + sims.report_configs +SET + report_sql = ( + '( + select + to_char(ds.disbursement_date, ''YYYY-MM-DD'') as "Forecast Date", + to_char(dr.disburse_date, ''YYYY-MM-DD'') as "Date of Disbursement", + dr.student_sin as "SIN", + app.application_number as "Application Number", + ds.document_number as "Certificate Number", + drv.grant_type as "Funding Code", + drv.grant_amount as "Disbursement Amount" + from + sims.disbursement_receipts dr + inner join sims.disbursement_receipt_values drv on drv.disbursement_receipt_id = dr.id + inner join sims.disbursement_schedules ds on ds.id = dr.disbursement_schedule_id + inner join sims.student_assessments sa on sa.id = ds.student_assessment_id + inner join sims.applications app on app.id = sa.application_id + inner join sims.education_programs_offerings epo on epo.id = sa.offering_id + where + epo.offering_intensity = any(:offeringIntensity) + and dr.disburse_date between :startDate + and :endDate + union + all + select + to_char(ds.disbursement_date, ''YYYY-MM-DD'') as "Forecast Date", + to_char(dr.disburse_date, ''YYYY-MM-DD'') as "Date of Disbursement", + dr.student_sin as "SIN", + app.application_number as "Application Number", + ds.document_number as "Certificate Number", + dv.value_code as "Funding Code", + dv.effective_amount as "Disbursement Amount" + from + sims.disbursement_receipts dr + inner join sims.disbursement_receipt_values drv on drv.disbursement_receipt_id = dr.id + inner join sims.disbursement_schedules ds on ds.id = dr.disbursement_schedule_id + inner join sims.disbursement_values dv on dv.disbursement_schedule_id = ds.id + inner join sims.student_assessments sa on sa.id = ds.student_assessment_id + inner join sims.applications app on app.id = sa.application_id + inner join sims.education_programs_offerings epo on epo.id = sa.offering_id + where + epo.offering_intensity = any(:offeringIntensity) + and dr.funding_type <> ''FE'' + and drv.grant_type = ''BCSG'' + and dv.value_type = ''BC Grant'' + and dr.disburse_date between :startDate + and :endDate + union + all + select + to_char(ds.disbursement_date, ''YYYY-MM-DD'') as "Forecast Date", + to_char(dr.disburse_date, ''YYYY-MM-DD'') as "Date of Disbursement", + dr.student_sin as "SIN", + app.application_number as "Application Number", + ds.document_number as "Certificate Number", + case + when dr.funding_type = ''BC'' then ''BCSL'' + when dr.funding_type = ''FE'' then ''CSL'' + end as "Funding Code", + dr.total_disbursed_amount as "Disbursement Amount" + from + sims.disbursement_receipts dr + inner join sims.disbursement_schedules ds on ds.id = dr.disbursement_schedule_id + inner join sims.student_assessments sa on sa.id = ds.student_assessment_id + inner join sims.applications app on app.id = sa.application_id + inner join sims.education_programs_offerings epo on epo.id = sa.offering_id + where + epo.offering_intensity = any(:offeringIntensity) + and dr.disburse_date between :startDate + and :endDate + ) + order by + "Date of Disbursement", + "Certificate Number"' + ) +WHERE + report_name = 'Disbursement_Report'; \ No newline at end of file From 5473752eba5e2ed2231c48e987a335d4cab6f55a Mon Sep 17 00:00:00 2001 From: Tiago Graf Date: Mon, 5 Jan 2026 15:46:34 -0800 Subject: [PATCH 2/8] Update SQL to format inner SQL (dollar sign instead of quotes) --- ...66-UpdateDisbursementReportForecastDate.ts | 4 +- ...ate-disbursements-report-forecast-date.sql | 142 +++++++++-------- ...ate-disbursements-report-forecast-date.sql | 146 +++++++++--------- sources/packages/backend/package-lock.json | 2 +- 4 files changed, 144 insertions(+), 150 deletions(-) diff --git a/sources/packages/backend/apps/db-migrations/src/migrations/1767653404966-UpdateDisbursementReportForecastDate.ts b/sources/packages/backend/apps/db-migrations/src/migrations/1767653404966-UpdateDisbursementReportForecastDate.ts index 0b1a496ac9..6137af8f9b 100644 --- a/sources/packages/backend/apps/db-migrations/src/migrations/1767653404966-UpdateDisbursementReportForecastDate.ts +++ b/sources/packages/backend/apps/db-migrations/src/migrations/1767653404966-UpdateDisbursementReportForecastDate.ts @@ -7,7 +7,7 @@ import { getSQLFileData } from "../utilities/sqlLoader"; export class UpdateDisbursementReportForecastDate1767653404966 implements MigrationInterface { /** * Update the disbursement reports to include the "Forecast Date" column. - * @param queryRunner + * @param queryRunner the query runner. */ public async up(queryRunner: QueryRunner): Promise { await queryRunner.query( @@ -20,7 +20,7 @@ export class UpdateDisbursementReportForecastDate1767653404966 implements Migrat /** * Rollback the disbursement reports update that included the "Forecast Date" column. - * @param queryRunner + * @param queryRunner the query runner. */ public async down(queryRunner: QueryRunner): Promise { await queryRunner.query( diff --git a/sources/packages/backend/apps/db-migrations/src/sql/Reports/Rollback-update-disbursements-report-forecast-date.sql b/sources/packages/backend/apps/db-migrations/src/sql/Reports/Rollback-update-disbursements-report-forecast-date.sql index 05341d86c8..13dec7fe09 100644 --- a/sources/packages/backend/apps/db-migrations/src/sql/Reports/Rollback-update-disbursements-report-forecast-date.sql +++ b/sources/packages/backend/apps/db-migrations/src/sql/Reports/Rollback-update-disbursements-report-forecast-date.sql @@ -1,79 +1,75 @@ -/** Rollback the disbursement report SQL to the previous version before the - * forecast date update. - */ +-- Rollback the disbursement report SQL to the previous version before the forecast date update. UPDATE sims.report_configs SET - report_sql = ( - '( - select - to_char(dr.disburse_date, ''YYYY-MM-DD'') as "Date of Disbursement", - dr.student_sin as "SIN", - app.application_number as "Application Number", - ds.document_number as "Certificate Number", - drv.grant_type as "Funding Code", - drv.grant_amount as "Disbursement Amount" - from - sims.disbursement_receipts dr - inner join sims.disbursement_receipt_values drv on drv.disbursement_receipt_id = dr.id - inner join sims.disbursement_schedules ds on ds.id = dr.disbursement_schedule_id - inner join sims.student_assessments sa on sa.id = ds.student_assessment_id - inner join sims.applications app on app.id = sa.application_id - inner join sims.education_programs_offerings epo on epo.id = sa.offering_id - where - epo.offering_intensity = any(:offeringIntensity) - and dr.disburse_date between :startDate - and :endDate - union - all - select - to_char(dr.disburse_date, ''YYYY-MM-DD'') as "Date of Disbursement", - dr.student_sin as "SIN", - app.application_number as "Application Number", - ds.document_number as "Certificate Number", - dv.value_code as "Funding Code", - dv.effective_amount as "Disbursement Amount" - from - sims.disbursement_receipts dr - inner join sims.disbursement_receipt_values drv on drv.disbursement_receipt_id = dr.id - inner join sims.disbursement_schedules ds on ds.id = dr.disbursement_schedule_id - inner join sims.disbursement_values dv on dv.disbursement_schedule_id = ds.id - inner join sims.student_assessments sa on sa.id = ds.student_assessment_id - inner join sims.applications app on app.id = sa.application_id - inner join sims.education_programs_offerings epo on epo.id = sa.offering_id - where - epo.offering_intensity = any(:offeringIntensity) - and dr.funding_type <> ''FE'' - and drv.grant_type = ''BCSG'' - and dv.value_type = ''BC Grant'' - and dr.disburse_date between :startDate - and :endDate - union - all - select - to_char(dr.disburse_date, ''YYYY-MM-DD'') as "Date of Disbursement", - dr.student_sin as "SIN", - app.application_number as "Application Number", - ds.document_number as "Certificate Number", - case - when dr.funding_type = ''BC'' then ''BCSL'' - when dr.funding_type = ''FE'' then ''CSL'' - end as "Funding Code", - dr.total_disbursed_amount as "Disbursement Amount" - from - sims.disbursement_receipts dr - inner join sims.disbursement_schedules ds on ds.id = dr.disbursement_schedule_id - inner join sims.student_assessments sa on sa.id = ds.student_assessment_id - inner join sims.applications app on app.id = sa.application_id - inner join sims.education_programs_offerings epo on epo.id = sa.offering_id - where - epo.offering_intensity = any(:offeringIntensity) - and dr.disburse_date between :startDate - and :endDate - ) - order by - "Date of Disbursement", - "Certificate Number"' + report_sql = $$ ( + SELECT + to_char(dr.disburse_date, 'YYYY-MM-DD') AS "Date of Disbursement", + dr.student_sin AS "SIN", + app.application_number AS "Application Number", + ds.document_number AS "Certificate Number", + drv.grant_type AS "Funding Code", + drv.grant_amount AS "Disbursement Amount" + FROM + sims.disbursement_receipts dr + INNER JOIN sims.disbursement_receipt_values drv ON drv.disbursement_receipt_id = dr.id + INNER JOIN sims.disbursement_schedules ds ON ds.id = dr.disbursement_schedule_id + INNER JOIN sims.student_assessments sa ON sa.id = ds.student_assessment_id + INNER JOIN sims.applications app ON app.id = sa.application_id + INNER JOIN sims.education_programs_offerings epo ON epo.id = sa.offering_id + WHERE + epo.offering_intensity = ANY(:offeringIntensity) + AND dr.disburse_date BETWEEN :startDate + AND :endDate + UNION + ALL + SELECT + to_char(dr.disburse_date, 'YYYY-MM-DD') AS "Date of Disbursement", + dr.student_sin AS "SIN", + app.application_number AS "Application Number", + ds.document_number AS "Certificate Number", + dv.value_code AS "Funding Code", + dv.effective_amount AS "Disbursement Amount" + FROM + sims.disbursement_receipts dr + INNER JOIN sims.disbursement_receipt_values drv ON drv.disbursement_receipt_id = dr.id + INNER JOIN sims.disbursement_schedules ds ON ds.id = dr.disbursement_schedule_id + INNER JOIN sims.disbursement_values dv ON dv.disbursement_schedule_id = ds.id + INNER JOIN sims.student_assessments sa ON sa.id = ds.student_assessment_id + INNER JOIN sims.applications app ON app.id = sa.application_id + INNER JOIN sims.education_programs_offerings epo ON epo.id = sa.offering_id + WHERE + epo.offering_intensity = ANY(:offeringIntensity) + AND dr.funding_type <> 'FE' + AND drv.grant_type = 'BCSG' + AND dv.value_type = 'BC Grant' + AND dr.disburse_date BETWEEN :startDate + AND :endDate + UNION + ALL + SELECT + to_char(dr.disburse_date, 'YYYY-MM-DD') AS "Date of Disbursement", + dr.student_sin AS "SIN", + app.application_number AS "Application Number", + ds.document_number AS "Certificate Number", + CASE + WHEN dr.funding_type = 'BC' THEN 'BCSL' + WHEN dr.funding_type = 'FE' THEN 'CSL' + END AS "Funding Code", + dr.total_disbursed_amount AS "Disbursement Amount" + FROM + sims.disbursement_receipts dr + INNER JOIN sims.disbursement_schedules ds ON ds.id = dr.disbursement_schedule_id + INNER JOIN sims.student_assessments sa ON sa.id = ds.student_assessment_id + INNER JOIN sims.applications app ON app.id = sa.application_id + INNER JOIN sims.education_programs_offerings epo ON epo.id = sa.offering_id + WHERE + epo.offering_intensity = ANY(:offeringIntensity) + AND dr.disburse_date BETWEEN :startDate + AND :endDate ) +ORDER BY + "Date of Disbursement", + "Certificate Number" $$ WHERE report_name = 'Disbursement_Report'; \ No newline at end of file diff --git a/sources/packages/backend/apps/db-migrations/src/sql/Reports/Update-disbursements-report-forecast-date.sql b/sources/packages/backend/apps/db-migrations/src/sql/Reports/Update-disbursements-report-forecast-date.sql index 38b3e864e0..de8781cd8b 100644 --- a/sources/packages/backend/apps/db-migrations/src/sql/Reports/Update-disbursements-report-forecast-date.sql +++ b/sources/packages/backend/apps/db-migrations/src/sql/Reports/Update-disbursements-report-forecast-date.sql @@ -1,80 +1,78 @@ -/** Update the Disbursements Report to include the "Forecast Date" column. **/ +-- Update the Disbursements Report to include the "Forecast Date" column. UPDATE sims.report_configs SET - report_sql = ( - '( - select - to_char(ds.disbursement_date, ''YYYY-MM-DD'') as "Forecast Date", - to_char(dr.disburse_date, ''YYYY-MM-DD'') as "Date of Disbursement", - dr.student_sin as "SIN", - app.application_number as "Application Number", - ds.document_number as "Certificate Number", - drv.grant_type as "Funding Code", - drv.grant_amount as "Disbursement Amount" - from - sims.disbursement_receipts dr - inner join sims.disbursement_receipt_values drv on drv.disbursement_receipt_id = dr.id - inner join sims.disbursement_schedules ds on ds.id = dr.disbursement_schedule_id - inner join sims.student_assessments sa on sa.id = ds.student_assessment_id - inner join sims.applications app on app.id = sa.application_id - inner join sims.education_programs_offerings epo on epo.id = sa.offering_id - where - epo.offering_intensity = any(:offeringIntensity) - and dr.disburse_date between :startDate - and :endDate - union - all - select - to_char(ds.disbursement_date, ''YYYY-MM-DD'') as "Forecast Date", - to_char(dr.disburse_date, ''YYYY-MM-DD'') as "Date of Disbursement", - dr.student_sin as "SIN", - app.application_number as "Application Number", - ds.document_number as "Certificate Number", - dv.value_code as "Funding Code", - dv.effective_amount as "Disbursement Amount" - from - sims.disbursement_receipts dr - inner join sims.disbursement_receipt_values drv on drv.disbursement_receipt_id = dr.id - inner join sims.disbursement_schedules ds on ds.id = dr.disbursement_schedule_id - inner join sims.disbursement_values dv on dv.disbursement_schedule_id = ds.id - inner join sims.student_assessments sa on sa.id = ds.student_assessment_id - inner join sims.applications app on app.id = sa.application_id - inner join sims.education_programs_offerings epo on epo.id = sa.offering_id - where - epo.offering_intensity = any(:offeringIntensity) - and dr.funding_type <> ''FE'' - and drv.grant_type = ''BCSG'' - and dv.value_type = ''BC Grant'' - and dr.disburse_date between :startDate - and :endDate - union - all - select - to_char(ds.disbursement_date, ''YYYY-MM-DD'') as "Forecast Date", - to_char(dr.disburse_date, ''YYYY-MM-DD'') as "Date of Disbursement", - dr.student_sin as "SIN", - app.application_number as "Application Number", - ds.document_number as "Certificate Number", - case - when dr.funding_type = ''BC'' then ''BCSL'' - when dr.funding_type = ''FE'' then ''CSL'' - end as "Funding Code", - dr.total_disbursed_amount as "Disbursement Amount" - from - sims.disbursement_receipts dr - inner join sims.disbursement_schedules ds on ds.id = dr.disbursement_schedule_id - inner join sims.student_assessments sa on sa.id = ds.student_assessment_id - inner join sims.applications app on app.id = sa.application_id - inner join sims.education_programs_offerings epo on epo.id = sa.offering_id - where - epo.offering_intensity = any(:offeringIntensity) - and dr.disburse_date between :startDate - and :endDate - ) - order by - "Date of Disbursement", - "Certificate Number"' + report_sql = $$ ( + SELECT + to_char(ds.disbursement_date, 'YYYY-MM-DD') AS "Forecast Date", + to_char(dr.disburse_date, 'YYYY-MM-DD') AS "Date of Disbursement", + dr.student_sin AS "SIN", + app.application_number AS "Application Number", + ds.document_number AS "Certificate Number", + drv.grant_type AS "Funding Code", + drv.grant_amount AS "Disbursement Amount" + FROM + sims.disbursement_receipts dr + INNER JOIN sims.disbursement_receipt_values drv ON drv.disbursement_receipt_id = dr.id + INNER JOIN sims.disbursement_schedules ds ON ds.id = dr.disbursement_schedule_id + INNER JOIN sims.student_assessments sa ON sa.id = ds.student_assessment_id + INNER JOIN sims.applications app ON app.id = sa.application_id + INNER JOIN sims.education_programs_offerings epo ON epo.id = sa.offering_id + WHERE + epo.offering_intensity = ANY(:offeringIntensity) + AND dr.disburse_date BETWEEN :startDate + AND :endDate + UNION + ALL + SELECT + to_char(ds.disbursement_date, 'YYYY-MM-DD') AS "Forecast Date", + to_char(dr.disburse_date, 'YYYY-MM-DD') AS "Date of Disbursement", + dr.student_sin AS "SIN", + app.application_number AS "Application Number", + ds.document_number AS "Certificate Number", + dv.value_code AS "Funding Code", + dv.effective_amount AS "Disbursement Amount" + FROM + sims.disbursement_receipts dr + INNER JOIN sims.disbursement_receipt_values drv ON drv.disbursement_receipt_id = dr.id + INNER JOIN sims.disbursement_schedules ds ON ds.id = dr.disbursement_schedule_id + INNER JOIN sims.disbursement_values dv ON dv.disbursement_schedule_id = ds.id + INNER JOIN sims.student_assessments sa ON sa.id = ds.student_assessment_id + INNER JOIN sims.applications app ON app.id = sa.application_id + INNER JOIN sims.education_programs_offerings epo ON epo.id = sa.offering_id + WHERE + epo.offering_intensity = ANY(:offeringIntensity) + AND dr.funding_type <> 'FE' + AND drv.grant_type = 'BCSG' + AND dv.value_type = 'BC Grant' + AND dr.disburse_date BETWEEN :startDate + AND :endDate + UNION + ALL + SELECT + to_char(ds.disbursement_date, 'YYYY-MM-DD') AS "Forecast Date", + to_char(dr.disburse_date, 'YYYY-MM-DD') AS "Date of Disbursement", + dr.student_sin AS "SIN", + app.application_number AS "Application Number", + ds.document_number AS "Certificate Number", + CASE + WHEN dr.funding_type = 'BC' THEN 'BCSL' + WHEN dr.funding_type = 'FE' THEN 'CSL' + END AS "Funding Code", + dr.total_disbursed_amount AS "Disbursement Amount" + FROM + sims.disbursement_receipts dr + INNER JOIN sims.disbursement_schedules ds ON ds.id = dr.disbursement_schedule_id + INNER JOIN sims.student_assessments sa ON sa.id = ds.student_assessment_id + INNER JOIN sims.applications app ON app.id = sa.application_id + INNER JOIN sims.education_programs_offerings epo ON epo.id = sa.offering_id + WHERE + epo.offering_intensity = ANY(:offeringIntensity) + AND dr.disburse_date BETWEEN :startDate + AND :endDate ) +ORDER BY + "Date of Disbursement", + "Certificate Number" $$ WHERE report_name = 'Disbursement_Report'; \ No newline at end of file diff --git a/sources/packages/backend/package-lock.json b/sources/packages/backend/package-lock.json index 24aa41f309..dc454e7ce0 100644 --- a/sources/packages/backend/package-lock.json +++ b/sources/packages/backend/package-lock.json @@ -17202,4 +17202,4 @@ } } } -} +} \ No newline at end of file From 9d9bf1ca74eb1d53cf214dcd3405e7ffc724b7e6 Mon Sep 17 00:00:00 2001 From: Tiago Graf Date: Tue, 6 Jan 2026 10:05:47 -0800 Subject: [PATCH 3/8] Update package-lock.json --- sources/packages/backend/package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/packages/backend/package-lock.json b/sources/packages/backend/package-lock.json index dc454e7ce0..24aa41f309 100644 --- a/sources/packages/backend/package-lock.json +++ b/sources/packages/backend/package-lock.json @@ -17202,4 +17202,4 @@ } } } -} \ No newline at end of file +} From adf1df35d3ec59a0f2e5b0c15754d7f07c83c111 Mon Sep 17 00:00:00 2001 From: Tiago Graf Date: Tue, 6 Jan 2026 10:06:22 -0800 Subject: [PATCH 4/8] Update 1767653404966-UpdateDisbursementReportForecastDate.ts --- .../1767653404966-UpdateDisbursementReportForecastDate.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sources/packages/backend/apps/db-migrations/src/migrations/1767653404966-UpdateDisbursementReportForecastDate.ts b/sources/packages/backend/apps/db-migrations/src/migrations/1767653404966-UpdateDisbursementReportForecastDate.ts index 6137af8f9b..b28fca9d4d 100644 --- a/sources/packages/backend/apps/db-migrations/src/migrations/1767653404966-UpdateDisbursementReportForecastDate.ts +++ b/sources/packages/backend/apps/db-migrations/src/migrations/1767653404966-UpdateDisbursementReportForecastDate.ts @@ -7,7 +7,7 @@ import { getSQLFileData } from "../utilities/sqlLoader"; export class UpdateDisbursementReportForecastDate1767653404966 implements MigrationInterface { /** * Update the disbursement reports to include the "Forecast Date" column. - * @param queryRunner the query runner. + * @param queryRunner the query runner. */ public async up(queryRunner: QueryRunner): Promise { await queryRunner.query( @@ -20,7 +20,7 @@ export class UpdateDisbursementReportForecastDate1767653404966 implements Migrat /** * Rollback the disbursement reports update that included the "Forecast Date" column. - * @param queryRunner the query runner. + * @param queryRunner the query runner. */ public async down(queryRunner: QueryRunner): Promise { await queryRunner.query( From 34fd44a696f3892bab701ed4f2be8550c5392080 Mon Sep 17 00:00:00 2001 From: Tiago Graf Date: Tue, 6 Jan 2026 11:39:23 -0800 Subject: [PATCH 5/8] Add unit test --- ...t.aest.controller.exportReport.e2e-spec.ts | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) diff --git a/sources/packages/backend/apps/api/src/route-controllers/report/_tests_/e2e/report.aest.controller.exportReport.e2e-spec.ts b/sources/packages/backend/apps/api/src/route-controllers/report/_tests_/e2e/report.aest.controller.exportReport.e2e-spec.ts index 0a329cfac4..934463a629 100644 --- a/sources/packages/backend/apps/api/src/route-controllers/report/_tests_/e2e/report.aest.controller.exportReport.e2e-spec.ts +++ b/sources/packages/backend/apps/api/src/route-controllers/report/_tests_/e2e/report.aest.controller.exportReport.e2e-spec.ts @@ -11,6 +11,7 @@ import { saveFakeApplicationDisbursements, saveFakeCASSupplier, saveFakeDesignationAgreementLocation, + saveFakeDisbursementReceiptsFromDisbursementSchedule, saveFakeStudent, } from "@sims/test-utils"; import { @@ -1340,6 +1341,154 @@ describe("ReportAestController(e2e)-exportReport", () => { }); }); + it("Should generate the Disbursement report when a report generation request is made with the appropriate offering intensity and date range.", async () => { + // Arrange + // Use a unique historical date to avoid conflicts with other tests. + const disburseDate = "2024-12-15"; + + const bcGrantBCAG = createFakeDisbursementValue( + DisbursementValueType.BCGrant, + "BCAG", + 300, + ); + + const canadaLoanCSL = createFakeDisbursementValue( + DisbursementValueType.CanadaLoan, + "CSL", + 1000, + ); + + const bcLoanBCSL = createFakeDisbursementValue( + DisbursementValueType.BCLoan, + "BCSL", + 500, + ); + + // Full time application with disbursement values. + const application = await saveFakeApplicationDisbursements( + appDataSource, + { + firstDisbursementValues: [bcLoanBCSL, canadaLoanCSL, bcGrantBCAG], + }, + { + applicationStatus: ApplicationStatus.Completed, + offeringIntensity: OfferingIntensity.fullTime, + firstDisbursementInitialValues: { + coeStatus: COEStatus.completed, + disbursementScheduleStatus: DisbursementScheduleStatus.Sent, + disbursementDate: disburseDate, + }, + }, + ); + + // Part time application with disbursement values. Not used but to ensure filtering works. + const partTimeApplication = await saveFakeApplicationDisbursements( + appDataSource, + { + firstDisbursementValues: [bcLoanBCSL, canadaLoanCSL, bcGrantBCAG], + }, + { + applicationStatus: ApplicationStatus.Completed, + offeringIntensity: OfferingIntensity.partTime, + firstDisbursementInitialValues: { + coeStatus: COEStatus.completed, + disbursementScheduleStatus: DisbursementScheduleStatus.Sent, + disbursementDate: disburseDate, + }, + }, + ); + // Create disbursement receipts for the part-time application using the utility method. + await saveFakeDisbursementReceiptsFromDisbursementSchedule( + db, + partTimeApplication.currentAssessment.disbursementSchedules[0], + ); + + // Set valid SIN for the student. + const student = application.student; + const sinValidation = createFakeSINValidation({ + student, + }); + student.sinValidation = sinValidation; + await db.student.save(student); + await db.sinValidation.save(sinValidation); + + // Create disbursement receipts for the report using the utility method. + const [firstDisbursement] = + application.currentAssessment.disbursementSchedules; + const { federal, provincial } = + await saveFakeDisbursementReceiptsFromDisbursementSchedule( + db, + firstDisbursement, + ); + + // Update receipt dates to match the unique test date to ensure proper filtering. + federal.disburseDate = disburseDate; + federal.batchRunDate = disburseDate; + federal.fileDate = disburseDate; + provincial.disburseDate = disburseDate; + provincial.batchRunDate = disburseDate; + provincial.fileDate = disburseDate; + await db.disbursementReceipt.save([federal, provincial]); + + const payload = { + reportName: "Disbursement_Report", + params: { + startDate: disburseDate, + endDate: getISODateOnlyString(addDays(1, disburseDate)), + offeringIntensity: { + "Full Time": true, // Filter to include only full time disbursements. + "Part Time": false, + }, + }, + }; + + // Mock the formio service dry run submission to return the payload. + const dryRunSubmissionMock = jest.fn().mockResolvedValue({ + valid: true, + formName: FormNames.ExportFinancialReports, + data: { data: payload }, + }); + formService.dryRunSubmission = dryRunSubmissionMock; + + const endpoint = "/aest/report"; + const ministryUserToken = await getAESTToken( + AESTGroups.BusinessAdministrators, + ); + + // Act/Assert + await request(app.getHttpServer()) + .post(endpoint) + .send(payload) + .auth(ministryUserToken, BEARER_AUTH_TYPE) + .expect(HttpStatus.CREATED) + .then((response) => { + const fileContent = response.request.res["text"]; + const parsedResult = parse(fileContent, { + header: true, + }); + expect(parsedResult.data).toStrictEqual([ + { + "Forecast Date": disburseDate, + "Date of Disbursement": disburseDate, + SIN: student.sinValidation.sin, + "Application Number": application.applicationNumber, + "Certificate Number": firstDisbursement.documentNumber.toString(), + "Funding Code": bcLoanBCSL.valueCode, + "Disbursement Amount": bcLoanBCSL.valueAmount.toFixed(2), + }, + { + "Forecast Date": disburseDate, + "Date of Disbursement": disburseDate, + SIN: student.sinValidation.sin, + "Application Number": application.applicationNumber, + "Certificate Number": firstDisbursement.documentNumber.toString(), + "Funding Code": canadaLoanCSL.valueCode, + "Disbursement Amount": canadaLoanCSL.valueAmount.toFixed(2), + }, + ]); + }); + }); + 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.", From 5005dbab651b5098a7452341a1098e25f14cc8b9 Mon Sep 17 00:00:00 2001 From: Tiago Graf Date: Tue, 6 Jan 2026 12:08:50 -0800 Subject: [PATCH 6/8] Update Update-disbursements-report-forecast-date.sql --- .../sql/Reports/Update-disbursements-report-forecast-date.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sources/packages/backend/apps/db-migrations/src/sql/Reports/Update-disbursements-report-forecast-date.sql b/sources/packages/backend/apps/db-migrations/src/sql/Reports/Update-disbursements-report-forecast-date.sql index de8781cd8b..4c022f07de 100644 --- a/sources/packages/backend/apps/db-migrations/src/sql/Reports/Update-disbursements-report-forecast-date.sql +++ b/sources/packages/backend/apps/db-migrations/src/sql/Reports/Update-disbursements-report-forecast-date.sql @@ -73,6 +73,7 @@ SET ) ORDER BY "Date of Disbursement", - "Certificate Number" $$ + "Certificate Number", + "Funding Code" $$ WHERE report_name = 'Disbursement_Report'; \ No newline at end of file From c9b9687b36cd2ff7e8455d4af602bb547c0a0f43 Mon Sep 17 00:00:00 2001 From: Tiago Graf Date: Tue, 6 Jan 2026 13:46:39 -0800 Subject: [PATCH 7/8] Update report.aest.controller.exportReport.e2e-spec.ts --- .../report.aest.controller.exportReport.e2e-spec.ts | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/sources/packages/backend/apps/api/src/route-controllers/report/_tests_/e2e/report.aest.controller.exportReport.e2e-spec.ts b/sources/packages/backend/apps/api/src/route-controllers/report/_tests_/e2e/report.aest.controller.exportReport.e2e-spec.ts index 934463a629..8bc1cb27e7 100644 --- a/sources/packages/backend/apps/api/src/route-controllers/report/_tests_/e2e/report.aest.controller.exportReport.e2e-spec.ts +++ b/sources/packages/backend/apps/api/src/route-controllers/report/_tests_/e2e/report.aest.controller.exportReport.e2e-spec.ts @@ -1344,7 +1344,7 @@ describe("ReportAestController(e2e)-exportReport", () => { it("Should generate the Disbursement report when a report generation request is made with the appropriate offering intensity and date range.", async () => { // Arrange // Use a unique historical date to avoid conflicts with other tests. - const disburseDate = "2024-12-15"; + const disburseDate = "2025-01-15"; const bcGrantBCAG = createFakeDisbursementValue( DisbursementValueType.BCGrant, @@ -1364,11 +1364,13 @@ describe("ReportAestController(e2e)-exportReport", () => { 500, ); + const student = await saveFakeStudent(appDataSource); // Full time application with disbursement values. const application = await saveFakeApplicationDisbursements( appDataSource, { firstDisbursementValues: [bcLoanBCSL, canadaLoanCSL, bcGrantBCAG], + student: student, }, { applicationStatus: ApplicationStatus.Completed, @@ -1403,15 +1405,6 @@ describe("ReportAestController(e2e)-exportReport", () => { partTimeApplication.currentAssessment.disbursementSchedules[0], ); - // Set valid SIN for the student. - const student = application.student; - const sinValidation = createFakeSINValidation({ - student, - }); - student.sinValidation = sinValidation; - await db.student.save(student); - await db.sinValidation.save(sinValidation); - // Create disbursement receipts for the report using the utility method. const [firstDisbursement] = application.currentAssessment.disbursementSchedules; From a25eeeae0042ff0139c93103b0b5e32968dd8425 Mon Sep 17 00:00:00 2001 From: Tiago Graf Date: Tue, 6 Jan 2026 15:49:21 -0800 Subject: [PATCH 8/8] Updated PR comments --- .../report.aest.controller.exportReport.e2e-spec.ts | 12 ++++++++---- .../route-controllers/report/models/report.dto.ts | 2 +- ...653404966-UpdateDisbursementReportForecastDate.ts | 7 ++----- ...ack-update-disbursement-report-forecast-date.sql} | 0 ... => Update-disbursement-report-forecast-date.sql} | 0 5 files changed, 11 insertions(+), 10 deletions(-) rename sources/packages/backend/apps/db-migrations/src/sql/Reports/{Rollback-update-disbursements-report-forecast-date.sql => Rollback-update-disbursement-report-forecast-date.sql} (100%) rename sources/packages/backend/apps/db-migrations/src/sql/Reports/{Update-disbursements-report-forecast-date.sql => Update-disbursement-report-forecast-date.sql} (100%) diff --git a/sources/packages/backend/apps/api/src/route-controllers/report/_tests_/e2e/report.aest.controller.exportReport.e2e-spec.ts b/sources/packages/backend/apps/api/src/route-controllers/report/_tests_/e2e/report.aest.controller.exportReport.e2e-spec.ts index 8bc1cb27e7..41c4e938b1 100644 --- a/sources/packages/backend/apps/api/src/route-controllers/report/_tests_/e2e/report.aest.controller.exportReport.e2e-spec.ts +++ b/sources/packages/backend/apps/api/src/route-controllers/report/_tests_/e2e/report.aest.controller.exportReport.e2e-spec.ts @@ -47,7 +47,10 @@ import { import { DataSource } from "typeorm"; import { createFakeEducationProgram } from "@sims/test-utils/factories/education-program"; import { createFakeSINValidation } from "@sims/test-utils/factories/sin-validation"; -import { MinistryReportsFilterAPIInDTO } from "../../models/report.dto"; +import { + MinistryReportNames, + MinistryReportsFilterAPIInDTO, +} from "../../models/report.dto"; import { buildUnmetNeedReportData, createApplicationsDataSetup, @@ -1341,7 +1344,7 @@ describe("ReportAestController(e2e)-exportReport", () => { }); }); - it("Should generate the Disbursement report when a report generation request is made with the appropriate offering intensity and date range.", async () => { + it("Should generate the Disbursement report when a report generation request is made for full time offering intensity and date range.", async () => { // Arrange // Use a unique historical date to avoid conflicts with other tests. const disburseDate = "2025-01-15"; @@ -1383,7 +1386,8 @@ describe("ReportAestController(e2e)-exportReport", () => { }, ); - // Part time application with disbursement values. Not used but to ensure filtering works. + // Part time application with disbursement values. + // This data is not used, its only created to ensure filtering works. const partTimeApplication = await saveFakeApplicationDisbursements( appDataSource, { @@ -1424,7 +1428,7 @@ describe("ReportAestController(e2e)-exportReport", () => { await db.disbursementReceipt.save([federal, provincial]); const payload = { - reportName: "Disbursement_Report", + reportName: MinistryReportNames.Disbursements, params: { startDate: disburseDate, endDate: getISODateOnlyString(addDays(1, disburseDate)), diff --git a/sources/packages/backend/apps/api/src/route-controllers/report/models/report.dto.ts b/sources/packages/backend/apps/api/src/route-controllers/report/models/report.dto.ts index 8a672ddd92..628565e07f 100644 --- a/sources/packages/backend/apps/api/src/route-controllers/report/models/report.dto.ts +++ b/sources/packages/backend/apps/api/src/route-controllers/report/models/report.dto.ts @@ -15,7 +15,7 @@ enum InstitutionReportNames { COERequests = "COE_Requests", } -enum MinistryReportNames { +export enum MinistryReportNames { ForecastDisbursements = "Disbursement_Forecast_Report", Disbursements = "Disbursement_Report", DisbursementsWithoutValidSupplier = "Disbursements_Without_Valid_Supplier_Report", diff --git a/sources/packages/backend/apps/db-migrations/src/migrations/1767653404966-UpdateDisbursementReportForecastDate.ts b/sources/packages/backend/apps/db-migrations/src/migrations/1767653404966-UpdateDisbursementReportForecastDate.ts index b28fca9d4d..ac707ec22d 100644 --- a/sources/packages/backend/apps/db-migrations/src/migrations/1767653404966-UpdateDisbursementReportForecastDate.ts +++ b/sources/packages/backend/apps/db-migrations/src/migrations/1767653404966-UpdateDisbursementReportForecastDate.ts @@ -11,10 +11,7 @@ export class UpdateDisbursementReportForecastDate1767653404966 implements Migrat */ public async up(queryRunner: QueryRunner): Promise { await queryRunner.query( - getSQLFileData( - "Update-disbursements-report-forecast-date.sql", - "Reports", - ), + getSQLFileData("Update-disbursement-report-forecast-date.sql", "Reports"), ); } @@ -25,7 +22,7 @@ export class UpdateDisbursementReportForecastDate1767653404966 implements Migrat public async down(queryRunner: QueryRunner): Promise { await queryRunner.query( getSQLFileData( - "Rollback-update-disbursements-report-forecast-date.sql", + "Rollback-update-disbursement-report-forecast-date.sql", "Reports", ), ); diff --git a/sources/packages/backend/apps/db-migrations/src/sql/Reports/Rollback-update-disbursements-report-forecast-date.sql b/sources/packages/backend/apps/db-migrations/src/sql/Reports/Rollback-update-disbursement-report-forecast-date.sql similarity index 100% rename from sources/packages/backend/apps/db-migrations/src/sql/Reports/Rollback-update-disbursements-report-forecast-date.sql rename to sources/packages/backend/apps/db-migrations/src/sql/Reports/Rollback-update-disbursement-report-forecast-date.sql diff --git a/sources/packages/backend/apps/db-migrations/src/sql/Reports/Update-disbursements-report-forecast-date.sql b/sources/packages/backend/apps/db-migrations/src/sql/Reports/Update-disbursement-report-forecast-date.sql similarity index 100% rename from sources/packages/backend/apps/db-migrations/src/sql/Reports/Update-disbursements-report-forecast-date.sql rename to sources/packages/backend/apps/db-migrations/src/sql/Reports/Update-disbursement-report-forecast-date.sql