Skip to content

#5569 - Funding Summary Reorganization#5719

Open
weskubo-cgi wants to merge 8 commits intomainfrom
feature/#5569-funding-summary-reorganization
Open

#5569 - Funding Summary Reorganization#5719
weskubo-cgi wants to merge 8 commits intomainfrom
feature/#5569-funding-summary-reorganization

Conversation

@weskubo-cgi
Copy link
Collaborator

@weskubo-cgi weskubo-cgi commented Feb 3, 2026

Frontend

  • Reorganized Award Table layout to have both Estimated and Final awards in a single component
  • Refactored DisbursementStatusChip
  • Updates to ChipLabel to support disbursement status and made it global

Backend

  • Added retrieval of Adjustments for Estimated awards
  • Updated DTO structure
  • Exposed ECertGenerationService in e-cert-integration.module.ts

Screenshots

Pending (Ministry)

image

Completed (Ministry)

image

Legend

image

E2E Tests

  • Updated existing tests to new DTO structure
  • Added new test cases for Estimated Award Adjustments
image image image

@weskubo-cgi weskubo-cgi marked this pull request as draft February 3, 2026 22:12
@weskubo-cgi weskubo-cgi self-assigned this Feb 4, 2026
@weskubo-cgi weskubo-cgi added Student Student Features Institution Institution Features Ministry Ministry Features SIMS-Api SIMS-Api E2E/Unit tests Web portal Complex More advanced issue and needs special attention. Backend Used by the dependabot pull requests to identify PRs related to the backend. labels Feb 4, 2026
@weskubo-cgi weskubo-cgi marked this pull request as ready for review February 5, 2026 01:50
@weskubo-cgi weskubo-cgi requested a review from Copilot February 5, 2026 01:51
@weskubo-cgi weskubo-cgi removed the Complex More advanced issue and needs special attention. label Feb 5, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR reorganizes the Award Table layout to combine Estimated and Final awards into a single unified view, updates the DTO structure to use structured objects instead of dynamic properties, adds the ability to retrieve adjustments for Estimated awards based on overawards and restrictions, and makes the ChipLabel component globally available.

Changes:

  • Restructured award data from dynamic key-value pairs to structured DTOs with explicit properties for disbursement schedules and values
  • Added new logic to calculate and display adjustments (restrictions, overawards, disbursed amounts) for both estimated and final awards
  • Made ChipLabel component global and created new StatusChipDisbursement component
  • Updated all e2e tests to match the new DTO structure and added test coverage for estimated award adjustments

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
sources/packages/web/src/types/contracts/institution/ConfirmationOfEnrollment.ts Added ReadyToSend disbursement status enum value
sources/packages/web/src/types/contracts/Assessment.ts Replaced dynamic award structure with structured types for adjustments and award data
sources/packages/web/src/services/http/dto/Assessment.dto.ts Updated frontend DTOs to match new structured response format
sources/packages/web/src/main.ts Registered ChipLabel component globally
sources/packages/web/src/composables/useDisbursement.ts Added composable to map disbursement status to badge display details
sources/packages/web/src/components/generic/* Updated chip components to support new statuses and made ChipLabel global
sources/packages/web/src/components/common/students/applicationDetails/* Updated award display components to use new structured data
sources/packages/web/src/components/common/AwardTable.vue Completely reorganized to combine estimated and final awards in a single table
sources/packages/web/src/components/common/AssessmentAwardDetails.vue Simplified to delegate to AwardTable component
sources/packages/web/src/assets/css/base.scss Added error chip background styling
sources/packages/backend/libs/integrations/src/esdc-integration/e-cert-integration/e-cert-integration.module.ts Added ECertGenerationService to module providers
sources/packages/backend/apps/api/src/route-controllers/institution-user/institution-user.institutions.controller.ts Commented out BCeID service code
sources/packages/backend/apps/api/src/route-controllers/assessment/models/assessment.dto.ts Updated backend DTOs to use structured classes instead of dynamic types
sources/packages/backend/apps/api/src/route-controllers/assessment/assessment.controller.service.ts Refactored to populate structured award data and calculate estimated adjustments
sources/packages/backend/apps/api/src/route-controllers/assessment/tests/e2e/* Updated all e2e tests for new DTO structure and added tests for estimated adjustments

@github-actions
Copy link

github-actions bot commented Feb 5, 2026

E2E Workflow Workers Coverage Report

Totals Coverage
Statements: 75.41% ( 1055 / 1399 )
Methods: 79.31% ( 115 / 145 )
Lines: 78.79% ( 769 / 976 )
Branches: 61.51% ( 171 / 278 )

@github-actions
Copy link

github-actions bot commented Feb 5, 2026

E2E Queue Consumers Coverage Report

Totals Coverage
Statements: 85.68% ( 1616 / 1886 )
Methods: 85% ( 187 / 220 )
Lines: 88.64% ( 1287 / 1452 )
Branches: 66.36% ( 142 / 214 )

@github-actions
Copy link

github-actions bot commented Feb 5, 2026

E2E SIMS API Coverage Report

Totals Coverage
Statements: 77.57% ( 8950 / 11538 )
Methods: 76.96% ( 1049 / 1363 )
Lines: 81.66% ( 6496 / 7955 )
Branches: 63.29% ( 1405 / 2220 )

@weskubo-cgi weskubo-cgi changed the title #5569 - Finding Summary Reorganization #5569 - Funding Summary Reorganization Feb 5, 2026
return "success-shade";
case StatusChipTypes.Warning:
return "warning-shade";
case StatusChipTypes.Error:
Copy link
Collaborator Author

@weskubo-cgi weskubo-cgi Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updates to support StatusChipTypes.Error should be safe for existing code as Error isn't used.

.component("ErrorSummary", ErrorSummary)
.component("ChipStatus", ChipStatus)
.component("ChipTag", ChipTag)
.component("ChipLabel", ChipLabel)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It made sense to make this a global component in line with ChipStatus and ChipTag.

* Indicates if the money amount information was already
* sent to be paid to the student.
*/
export enum DisbursementScheduleStatus {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the location of this enum but didn't want to take on any additional changes with this PR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to add a "TODO" to move to a better location.

index++;
const disbursement = new AwardDisbursementScheduleAPIOutDTO();

disbursement.disbursementDate = getDateOnlyFullMonthFormat(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The preference for these values would be to have them returned from the API as Date (unless date-only), and the format be applied in the UI.

Comment on lines +282 to +283
let firstEligibleDisbursement: EligibleECertDisbursement;
let secondEligibleDisbursement: EligibleECertDisbursement;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Can be a single statement

let firstEligibleDisbursement: EligibleECertDisbursement,
      secondEligibleDisbursement: EligibleECertDisbursement;

if (disbursement.status === DisbursementScheduleStatus.Pending) {
// Estimated Award - calculate estimated adjustments.

// Restriction: If the student has a restriction that impacts funding, flag the adjustment.)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parenthesis seems to be lost at the end of the sentence.

ECertGenerationService,
EligibleECertDisbursement,
} from "@sims/integrations/services";
import { getStopFundingTypesAndRestrictionsMap } from "@sims/integrations/services/disbursement-schedule/e-cert-processing-steps/e-cert-steps-utils";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add this file to the index inside the e-cert-processing-steps or maybe into the services folder.

);
const finalAward =
await this.populateDisbursementFinalAwardsValues(assessment);
let secondDisbursement;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a minor warning stating this variable has an implicit type of any.
As a general rule, it would be better to have it typed during its declaration.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed that one, thanks.

institutionName:
assessment.offering.educationProgram.institution.operatingName,
offeringIntensity: assessment.offering.offeringIntensity,
offeringStudyStartDate: getDateOnlyFormat(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Outside PR changes, the getDateOnlyFormat is no longer required for studyStartDate and studyEndDate.
They were required way in the past, but not anymore.

});
}

const firstDisbursement = await this.populateAwardDisbursement(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The calls can be executed in parallel as below.

const [firstDisbursement, secondDisbursement] = await Promise.all([
  this.populateAwardDisbursement(
    firstDisbursementSchedule,
    firstEligibleDisbursement,
    assessment.application.student.id,
    { includeDocumentNumber, includeDateSent, maskMSFAA },
  ),
  secondDisbursementSchedule
    ? this.populateAwardDisbursement(
        secondDisbursementSchedule,
        secondEligibleDisbursement,
        assessment.application.student.id,
        { includeDocumentNumber, includeDateSent, maskMSFAA },
      )
    : null,
]);

const finalAward: DynamicAwardValue = {};
disbursementValues.forEach((award) => {
if (award.valueType === DisbursementValueType.BCTotalGrant) {
if (eligibleDisbursement) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a scenario where the eligibleDisbursement would not be present? If yes, should the parameter be declared as

eligibleDisbursement: EligibleECertDisbursement | undefined,

If it is mandatory and present, it also has the studentId available.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this comment helps answer the question: it may not be present if the disbursement is no longer pending.

DisbursementScheduleStatus.Pending
) {
[firstEligibleDisbursement, secondEligibleDisbursement] =
await this.eCertGenerationService.getEligibleDisbursements({
Copy link
Collaborator

@andrewsignori-aot andrewsignori-aot Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getEligibleDisbursements has the following condition, which enforces that only eligible disbursements to create an e-Cert will be considered. When an application has two disbursements and the first one is sent, this method will start to return only one record that will represent the second disbursement, which will be wrongly associated with the firstEligibleDisbursement, also making the secondEligibleDisbursement null.

.where(
  "disbursementSchedule.disbursementScheduleStatus = :disbursementScheduleStatus",
  { disbursementScheduleStatus: DisbursementScheduleStatus.Pending },
)

If the above assumption is right (please validate it), considering that we no longer need the firstEligibleDisbursement once it is sent, we can stop using the array destructuring and execute the association using the disbursement ID. Does it make sense?

finalAward?: DynamicAwardValue;
firstDisbursement: AwardDisbursementScheduleAPIOutDTO;
secondDisbursement: AwardDisbursementScheduleAPIOutDTO;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a blank line, following the file pattern.

})
finalAward?: DynamicAwardValue;
firstDisbursement: AwardDisbursementScheduleAPIOutDTO;
secondDisbursement: AwardDisbursementScheduleAPIOutDTO;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

secondDisbursement should be nullable, unless I am missing something.

Comment on lines +209 to +210
msfaaCancelledDate: string;
msfaaDateSigned: string;
Copy link
Collaborator

@andrewsignori-aot andrewsignori-aot Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should be nullable, same for enrolmentDate.

>
First disbursement
</h3>
<div>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a need to use div + v-row? Would the below not work in the same way?

<template>
  <content-group>
    <award-table
     ...
    />
  </content-group>
  <content-group v-if="isSecondDisbursementAvailable">
    <award-table
      ...
    />
  </content-group>
</template>

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not required. This was older code I should have removed.

<tbody>
<tr v-for="award in awards" :key="award.awardType">
<td>
<span v-if="$vuetify.display.smAndDown">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use the below instead if that achieves the same?

const { mobile: isMobile } = useDisplay();

<span class="label-bold">Earliest date of disbursement: </span>
<span>{{ dateOnlyLongString(disbursement.disbursementDate) }}</span>
</div>
<div v-if="isDisbursementCompleted && disbursement.documentNumber">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, even if it was named as such before, it may be misleading. I would recommend naming it something more specific to the COE completion, for instance, isCOECompleted.
Either way, the disbursement.documentNumber would also be enough as a condition to display it.

/>
</div>

<div></div>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this div a leftover?

const { currencyFormatter, dateOnlyLongString } = useFormatters();

// Associate disbursement values with their award types.
const awards: AssessmentAwardData[] = awardTypes.value.map((award) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The awards variable is based on a computed property awardTypes but it is not defined to be reactive/computed. Is there any reason not have the awards as a computed variable?

Comment on lines +235 to +242
hasDisbursedAdjustment:
disbursementValue?.hasDisbursedAdjustment ?? false,
hasRestrictionAdjustment:
disbursementValue?.hasRestrictionAdjustment ?? false,
hasNegativeOverawardAdjustment:
disbursementValue?.hasNegativeOverawardAdjustment ?? false,
hasPositiveOverawardAdjustment:
disbursementValue?.hasPositiveOverawardAdjustment ?? false,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the intention of having the AssessmentAwardData is to serve as a model for the UI, the transformation expected to provide the data to the component assessment-award-adjustments can be executed at this moment.

Image

disbursementReceipt2bgpd: 800,
disbursementReceipt2sbsd: 900,
},
.then((response) => {
Copy link
Collaborator

@andrewsignori-aot andrewsignori-aot Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a problem keeping the previous syntax as below (and removing the expect.arrayContaining)?

.expect(HttpStatus.OK)
.expect({
  applicationNumber: application.applicationNumber,

Copy link
Collaborator

@andrewsignori-aot andrewsignori-aot Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed many tests changed in this way. Was there a benefit in changing it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests were behaving unpredictably due to the changing order of nested objects (disbursement values) in the array. This was the only way to test the values content irrespective of order. I could add a sort to the API if you prefer but it isn't needed by web.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not a matter of preference; from a project perspective, the idea is to test as much as possible, as accurately as possible. If I am not wrong, the arrayContaining would allow more items to be returned in the array, and the test would still pass.
We should not change PROD code in favor of E2E tests, but adding the sort for the awards and ensuring the query will always be returned in the same way would not be so bad in this scenario.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have one scenario for when an application has two disbursements, where the first disbursement is sent and the second one is pending?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure!

Copy link
Collaborator

@andrewsignori-aot andrewsignori-aot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, and it is amazing to see how much the summary page has changed. Please take a look at the comments.

title="Funding summary"
sub-title="Below is the summary from your assessment. To view your Notice of Assessment, click on view assessment."
<toggle-content
:toggled="!assessmentAwardData.firstDisbursement"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When will the firstDisbursement be evaluated to false here? New instance of a class intiatialized with the new keyword will evaluate to true always even if the object is empty. Since, it is populated using teh below in the backend, I believe it will never evaluate to false.

const disbursement = new AwardDisbursementScheduleAPIOutDTO();

Copy link
Collaborator

@sh16011993 sh16011993 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work @weskubo-cgi 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backend Used by the dependabot pull requests to identify PRs related to the backend. E2E/Unit tests Institution Institution Features Ministry Ministry Features SIMS-Api SIMS-Api Student Student Features Web portal

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants