WEB-657: Working Capital loan creation with Originators#3689
WEB-657: Working Capital loan creation with Originators#3689alberto-art3ch wants to merge 1 commit into
Conversation
|
Caution Review failedAn error occurred during the review process. Please try again later. Note
|
| Layer / File(s) | Summary |
|---|---|
Route resolver wiring src/app/organization/loan-originators/loan-originators.resolver.ts, src/app/loans/loans-routing.module.ts |
loanOriginatorsData is resolved on the create and edit loan account routes, and the resolver is provided in root. |
Details form dates src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.html |
The details form moves the submitted and disbursement date pickers earlier, switches search labels to translation keys, and removes the later duplicate date block. |
Originator selector src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.ts, src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.html |
The details step loads originator options from route data, reads the creation flag from SystemService, filters the catalog, and maps originatorExternalId into the submitted originators array. |
Originator display safety src/app/loans/models/loan-account.model.ts, src/app/loans/loans-view/loan-originators-tab/loan-originators-tab.component.ts, src/app/loans/loans-view/loan-originators-tab/loan-originators-tab.component.html, src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.html |
The originator model makes nested type fields optional, the originators tab guards missing names and adjusts its data source logic, and the preview shows the first originator external id. |
Sequence Diagram(s)
sequenceDiagram
participant LoansRoutingModule
participant LoanOriginatorsCatalogResolver
participant LoansAccountDetailsStepComponent
participant SystemService
LoansRoutingModule->>LoanOriginatorsCatalogResolver: resolve loanOriginatorsData
LoanOriginatorsCatalogResolver-->>LoansRoutingModule: loanOriginatorsData
LoansRoutingModule-->>LoansAccountDetailsStepComponent: route.snapshot.data.loanOriginatorsData
LoansAccountDetailsStepComponent->>SystemService: getConfigurationByName(enable-originator-creation-during-loan-application)
SystemService-->>LoansAccountDetailsStepComponent: configuration value
LoansAccountDetailsStepComponent->>LoansAccountDetailsStepComponent: filter originatorOptions and map originatorExternalId
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~25 minutes
Suggested reviewers
- adamsaghy
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | The title is concise and matches the main change: adding originator support to working capital loan creation. |
| Docstring Coverage | ✅ Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. |
| Linked Issues check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
| Out of Scope Changes check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
✨ Finishing Touches
🧪 Generate unit tests (beta)
- Create PR with unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.ts (1)
249-258: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAvoid adding
anyto the new originator payload path.The payload shape is local here; type the added
originatorscontract so regressions in{ externalId }are checked.As per coding guidelines, “Use TypeScript for all application code with strict typing conventions.”
♻️ Proposed refactor
- const { originatorExternalId, ...rest } = this.loansAccountDetailsForm.getRawValue(); - const loanAccountDetails: any = { + const { originatorExternalId, ...rest } = this.loansAccountDetailsForm.getRawValue() as { + originatorExternalId?: string; + } & Record<string, unknown>; + const loanAccountDetails: Record<string, unknown> & { + productId: LoanProductBasicDetails['id']; + originators?: { externalId: string }[]; + } = { ...rest, productId: this.productSelected.id };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.ts` around lines 249 - 258, The new originator payload path in loansAccountDetails should not use any; replace the loose loanAccountDetails typing with a proper TypeScript shape that includes the originators contract. Update the logic in loans-account-details-step.component.ts where getRawValue() is transformed so originators is explicitly typed as an array of objects with externalId, and keep the rest of the payload strongly typed to match productId and other fields.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.html`:
- Around line 87-92: The disbursement required error is currently bound to the
wrong form control, so the message will not show for an empty disbursement date.
Update the `loans-account-details-form` template logic that checks
`loansAccountDetailsForm.controls.submittedOnDate?.hasError('required')` to use
`expectedDisbursementDate` instead, and keep the existing `mat-error` copy
aligned with that control.
- Line 132: The clear icon button in loans-account-details-step.component.html
is currently a form submit by default and its aria-label is hardcoded. Update
the button used with clearOriginator($event) to explicitly be non-submit, and
replace the “Clear” label with a translated i18n value using the existing
`@ngx-translate/core` patterns used in this template. Make sure the localized
label is still readable by screen readers and keep the change scoped to the
clear button element.
In
`@src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.ts`:
- Around line 181-182: The submitted payload in the loans account details step
is only setting originatorExternalId inside the loan-product-only path, which
causes it to be omitted for product-based templates. Update the payload-building
logic in loansAccountDetailsStepComponent so originatorExternalId is assigned
independently of the loanProductId/product branch, using the existing
loansAccountTemplate.originators data regardless of which branch is taken. Keep
the fix localized to the payload construction in the relevant method so an
existing originator is always preserved on submit.
In
`@src/app/loans/loans-view/loan-originators-tab/loan-originators-tab.component.ts`:
- Around line 91-97: The subscription in loanOriginatorsTabComponent currently
reads loanOriginatorsData.originators without any guard, which can throw if the
resolver payload is missing that key. Update the subscribe callback in the
loan-originators data flow to use a defensive access pattern like the one used
for loanDetailsData, and ensure loanOriginatorsData is assigned safely with
optional chaining and a fallback when originators is absent.
---
Nitpick comments:
In
`@src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.ts`:
- Around line 249-258: The new originator payload path in loansAccountDetails
should not use any; replace the loose loanAccountDetails typing with a proper
TypeScript shape that includes the originators contract. Update the logic in
loans-account-details-step.component.ts where getRawValue() is transformed so
originators is explicitly typed as an array of objects with externalId, and keep
the rest of the payload strongly typed to match productId and other fields.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 5973e9f9-f756-414e-83ab-cb75a54a6333
📒 Files selected for processing (8)
src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.htmlsrc/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.tssrc/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.htmlsrc/app/loans/loans-routing.module.tssrc/app/loans/loans-view/loan-originators-tab/loan-originators-tab.component.htmlsrc/app/loans/loans-view/loan-originators-tab/loan-originators-tab.component.tssrc/app/loans/models/loan-account.model.tssrc/app/organization/loan-originators/loan-originators.resolver.ts
| @if (loansAccountDetailsForm.controls.submittedOnDate?.hasError('required')) { | ||
| <mat-error> | ||
| {{ 'labels.inputs.Disbursement on' | translate }} {{ 'labels.commons.is' | translate }} | ||
| <strong>{{ 'labels.commons.required' | translate }}</strong> | ||
| </mat-error> | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Bind the disbursement error to expectedDisbursementDate.
Line 87 checks submittedOnDate, so the required error for an empty disbursement date will not render reliably.
🐛 Proposed fix
- `@if` (loansAccountDetailsForm.controls.submittedOnDate?.hasError('required')) {
+ `@if` (loansAccountDetailsForm.controls.expectedDisbursementDate?.hasError('required')) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @if (loansAccountDetailsForm.controls.submittedOnDate?.hasError('required')) { | |
| <mat-error> | |
| {{ 'labels.inputs.Disbursement on' | translate }} {{ 'labels.commons.is' | translate }} | |
| <strong>{{ 'labels.commons.required' | translate }}</strong> | |
| </mat-error> | |
| } | |
| `@if` (loansAccountDetailsForm.controls.expectedDisbursementDate?.hasError('required')) { | |
| <mat-error> | |
| {{ 'labels.inputs.Disbursement on' | translate }} {{ 'labels.commons.is' | translate }} | |
| <strong>{{ 'labels.commons.required' | translate }}</strong> | |
| </mat-error> | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.html`
around lines 87 - 92, The disbursement required error is currently bound to the
wrong form control, so the message will not show for an empty disbursement date.
Update the `loans-account-details-form` template logic that checks
`loansAccountDetailsForm.controls.submittedOnDate?.hasError('required')` to use
`expectedDisbursementDate` instead, and keep the existing `mat-error` copy
aligned with that control.
| } | ||
| </mat-select> | ||
| @if (loansAccountDetailsForm.get('originatorExternalId')?.value) { | ||
| <button matSuffix mat-icon-button aria-label="Clear" (click)="clearOriginator($event)"> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Make the clear icon non-submit and localize its label.
Line 132 defaults to a submit button inside forms; clearing the originator can trigger form submission. Also translate the screen-reader label.
As per coding guidelines, “Use proper i18n variables from @ngx-translate/core for all user-facing strings instead of hardcoded text.”
🐛 Proposed fix
- <button matSuffix mat-icon-button aria-label="Clear" (click)="clearOriginator($event)">
+ <button
+ type="button"
+ matSuffix
+ mat-icon-button
+ [attr.aria-label]="'labels.buttons.Clear' | translate"
+ (click)="clearOriginator($event)"
+ >📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <button matSuffix mat-icon-button aria-label="Clear" (click)="clearOriginator($event)"> | |
| <button | |
| type="button" | |
| matSuffix | |
| mat-icon-button | |
| [attr.aria-label]="'labels.buttons.Clear' | translate" | |
| (click)="clearOriginator($event)" | |
| > |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.html`
at line 132, The clear icon button in loans-account-details-step.component.html
is currently a form submit by default and its aria-label is hardcoded. Update
the button used with clearOriginator($event) to explicitly be non-submit, and
replace the “Clear” label with a translated i18n value using the existing
`@ngx-translate/core` patterns used in this template. Make sure the localized
label is still readable by screen readers and keep the change scoped to the
clear button element.
Source: Coding guidelines
| if (this.loanProductService.isLoanProduct) { | ||
| this.route.parent.data | ||
| .pipe(takeUntilDestroyed(this.destroyRef)) | ||
| .subscribe((data: { loanOriginatorsData: any }) => { | ||
| this.loanOriginatorsData = data.loanOriginatorsData.originators; | ||
| }); | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -nP -C3 'loanOriginatorsData' src/app/organization/loan-originators/loan-originators.resolver.ts
ast-grep run --pattern 'resolve($$$) { $$$ }' --lang typescript src/app/organization/loan-originators/loan-originators.resolver.tsRepository: openMF/web-app
Length of output: 464
🏁 Script executed:
cat -n src/app/organization/loan-originators/loan-originators.resolver.tsRepository: openMF/web-app
Length of output: 1521
🏁 Script executed:
find src/app/loans -name "*loan-originators*route*" -o -name "*loan-originators*routing*" 2>/dev/nullRepository: openMF/web-app
Length of output: 152
🏁 Script executed:
rg -n 'loanOriginatorsData' src/app/loans --type tsRepository: openMF/web-app
Length of output: 1355
🏁 Script executed:
sed -n '340,360p' src/app/loans/loans-routing.module.tsRepository: openMF/web-app
Length of output: 832
🏁 Script executed:
rg -n "getLoanOriginator\(|getLoanOriginators\(" src/app/organization/organization.service.ts | head -20Repository: openMF/web-app
Length of output: 263
🏁 Script executed:
sed -n '842,870p' src/app/organization/organization.service.tsRepository: openMF/web-app
Length of output: 910
🏁 Script executed:
rg -n "LoanOriginatorsCatalogResolver" src/app --type ts -A5 -B2Repository: openMF/web-app
Length of output: 2758
🏁 Script executed:
cat -n src/app/loans/common-resolvers/loan-originators.resolver.tsRepository: openMF/web-app
Length of output: 1584
🏁 Script executed:
sed -n '75,85p' src/app/loans/loans-routing.module.tsRepository: openMF/web-app
Length of output: 1424
🏁 Script executed:
rg -n "getLoanOriginators" src/app/loans/loans.service.ts -A5Repository: openMF/web-app
Length of output: 314
🏁 Script executed:
sed -n '85,98p' src/app/loans/loans-view/loan-originators-tab/loan-originators-tab.component.tsRepository: openMF/web-app
Length of output: 693
Guard against missing originators property in resolved loanOriginatorsData.
The second subscription directly accesses data.loanOriginatorsData.originators without verifying the property exists, unlike the first subscription which uses 'originators' in data.loanDetailsData as a guard. If the API response shape changes or returns data without the originators key, this will throw a TypeError.
Apply optional chaining with a fallback to match the defensive pattern used elsewhere:
if (this.loanProductService.isLoanProduct) {
this.route.parent.data
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((data: { loanOriginatorsData: any }) => {
- this.loanOriginatorsData = data.loanOriginatorsData.originators;
+ this.loanOriginatorsData = data.loanOriginatorsData?.originators ?? [];
});
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (this.loanProductService.isLoanProduct) { | |
| this.route.parent.data | |
| .pipe(takeUntilDestroyed(this.destroyRef)) | |
| .subscribe((data: { loanOriginatorsData: any }) => { | |
| this.loanOriginatorsData = data.loanOriginatorsData.originators; | |
| }); | |
| } | |
| if (this.loanProductService.isLoanProduct) { | |
| this.route.parent.data | |
| .pipe(takeUntilDestroyed(this.destroyRef)) | |
| .subscribe((data: { loanOriginatorsData: any }) => { | |
| this.loanOriginatorsData = data.loanOriginatorsData?.originators ?? []; | |
| }); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/app/loans/loans-view/loan-originators-tab/loan-originators-tab.component.ts`
around lines 91 - 97, The subscription in loanOriginatorsTabComponent currently
reads loanOriginatorsData.originators without any guard, which can throw if the
resolver payload is missing that key. Update the subscribe callback in the
loan-originators data flow to use a defensive access pattern like the one used
for loanDetailsData, and ensure loanOriginatorsData is assigned safely with
optional chaining and a fallback when originators is absent.
b97cd2c to
7679228
Compare
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (9)
Note
|
| Layer / File(s) | Summary |
|---|---|
Route resolver wiring src/app/organization/loan-originators/loan-originators.resolver.ts, src/app/loans/loans-routing.module.ts |
loanOriginatorsData is resolved on the create and edit loan account routes, and the resolver is provided in root. |
Details form dates src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.html |
The details form moves the submitted and disbursement date pickers earlier, switches search labels to translation keys, and removes the later duplicate date block. |
Originator selector src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.ts, src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.html |
The details step loads originator options from route data, reads the creation flag from SystemService, filters the catalog, and maps originatorExternalId into the submitted originators array. |
Originator display safety src/app/loans/models/loan-account.model.ts, src/app/loans/loans-view/loan-originators-tab/loan-originators-tab.component.ts, src/app/loans/loans-view/loan-originators-tab/loan-originators-tab.component.html, src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.html |
The originator model makes nested type fields optional, the originators tab guards missing names and adjusts its data source logic, and the preview shows the first originator external id. |
Sequence Diagram(s)
sequenceDiagram
participant LoansRoutingModule
participant LoanOriginatorsCatalogResolver
participant LoansAccountDetailsStepComponent
participant SystemService
LoansRoutingModule->>LoanOriginatorsCatalogResolver: resolve loanOriginatorsData
LoanOriginatorsCatalogResolver-->>LoansRoutingModule: loanOriginatorsData
LoansRoutingModule-->>LoansAccountDetailsStepComponent: route.snapshot.data.loanOriginatorsData
LoansAccountDetailsStepComponent->>SystemService: getConfigurationByName(enable-originator-creation-during-loan-application)
SystemService-->>LoansAccountDetailsStepComponent: configuration value
LoansAccountDetailsStepComponent->>LoansAccountDetailsStepComponent: filter originatorOptions and map originatorExternalId
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~25 minutes
Suggested reviewers
- adamsaghy
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | The title is concise and matches the main change: adding originator support to working capital loan creation. |
| Docstring Coverage | ✅ Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. |
| Linked Issues check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
| Out of Scope Changes check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
✨ Finishing Touches
🧪 Generate unit tests (beta)
- Create PR with unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands.
Description
Loan Origination functionality is currently build and available for term loans and working capital loans, so It is possible to create a loan account with Originator data
Related issues and discussion
WEB-657
Screenshots
Screen.Recording.2026-06-26.at.1.34.48.PM.mov
Checklist
Please make sure these boxes are checked before submitting your pull request - thanks!
If you have multiple commits please combine them into one commit by squashing them.
Read and understood the contribution guidelines at
web-app/.github/CONTRIBUTING.md.Summary by CodeRabbit
New Features
Improvements