-
Notifications
You must be signed in to change notification settings - Fork 961
WEB-657: Working Capital Loan Delinquency - Reset & Undo Reset #3729
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| <!-- | ||
| Copyright since 2025 Mifos Initiative | ||
|
|
||
| This Source Code Form is subject to the terms of the Mozilla Public | ||
| License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
| --> | ||
|
|
||
| <h2 mat-dialog-title>{{ 'labels.heading.Loan Delinquency Actions' | translate }}</h2> | ||
|
|
||
| <div mat-dialog-content [formGroup]="delinquencyActionForm"> | ||
| <div class="layout-column"> | ||
| <mat-checkbox class="flex-98" formControlName="startNewPeriod"> | ||
| <p>{{ 'labels.inputs.Start new period' | translate }}</p> | ||
| </mat-checkbox> | ||
| </div> | ||
| </div> | ||
|
|
||
| <mat-dialog-actions class="layout-row layout-xs-column layout-align-center gap-2percent"> | ||
| <button mat-raised-button mat-dialog-close>{{ 'labels.buttons.Cancel' | translate }}</button> | ||
| <button | ||
| mat-raised-button | ||
| color="primary" | ||
| [mat-dialog-close]="{ data: delinquencyActionForm }" | ||
| > | ||
| {{ 'labels.buttons.Reset' | translate }} | ||
| </button> | ||
| </mat-dialog-actions> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| /** | ||
| * Copyright since 2025 Mifos Initiative | ||
| * | ||
| * This Source Code Form is subject to the terms of the Mozilla Public | ||
| * License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
| */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| /** | ||
| * Copyright since 2025 Mifos Initiative | ||
| * | ||
| * This Source Code Form is subject to the terms of the Mozilla Public | ||
| * License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
| */ | ||
|
|
||
| import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; | ||
| import { UntypedFormBuilder, UntypedFormGroup, Validators, ReactiveFormsModule } from '@angular/forms'; | ||
| import { | ||
| MAT_DIALOG_DATA, | ||
| MatDialogRef, | ||
| MatDialogTitle, | ||
| MatDialogContent, | ||
| MatDialogActions, | ||
| MatDialogClose | ||
| } from '@angular/material/dialog'; | ||
| import { CdkScrollable } from '@angular/cdk/scrolling'; | ||
| import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; | ||
| import { MatTooltip } from '@angular/material/tooltip'; | ||
| import { StringEnumOptionData } from 'app/shared/models/option-data.model'; | ||
|
|
||
| @Component({ | ||
| selector: 'mifosx-loan-delinquency-action-reset-dialog', | ||
| templateUrl: './loan-delinquency-action-reset-dialog.component.html', | ||
| styleUrl: './loan-delinquency-action-reset-dialog.component.scss', | ||
| imports: [ | ||
| ...STANDALONE_SHARED_IMPORTS, | ||
| MatDialogTitle, | ||
| MatDialogContent, | ||
| MatDialogActions, | ||
| MatDialogClose | ||
| ], | ||
| changeDetection: ChangeDetectionStrategy.OnPush | ||
| }) | ||
| export class LoanDelinquencyActionResetDialogComponent { | ||
| dialogRef = inject<MatDialogRef<LoanDelinquencyActionResetDialogComponent>>(MatDialogRef); | ||
| data = inject(MAT_DIALOG_DATA); | ||
| private formBuilder = inject(UntypedFormBuilder); | ||
|
|
||
| delinquencyActionForm: UntypedFormGroup; | ||
|
|
||
| constructor() { | ||
| this.createDelinquencyActionForm(); | ||
| } | ||
|
|
||
| createDelinquencyActionForm() { | ||
| this.delinquencyActionForm = this.formBuilder.group({ | ||
| startNewPeriod: [ | ||
| false | ||
| ] | ||
| }); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -163,6 +163,22 @@ <h3>{{ 'labels.heading.Loan Delinquency Installment Tags' | translate }}</h3> | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <fa-icon icon="calendar" class="m-r-10"></fa-icon>{{ 'labels.buttons.Reschedule' | translate }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mat-raised-button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| color="primary" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (click)="createDelinquencyActionReset()" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| *mifosxHasPermission="'CREATE_WC_DELINQUENCY_RESET'" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <fa-icon icon="calendar" class="m-r-10"></fa-icon>{{ 'labels.buttons.Reset' | translate }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mat-raised-button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| color="primary" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (click)="createDelinquencyActionUndoReset()" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| *mifosxHasPermission="'CREATE_WC_DELINQUENCY_UNDO_RESET'" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <fa-icon icon="calendar" class="m-r-10"></fa-icon>{{ 'labels.buttons.Undo Reset' | translate }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+166
to
+181
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Align margin utility classes with the 8px grid system. As per coding guidelines, visual design and spacing must stick to the 8px grid system. The ♻️ Proposed fix <button
mat-raised-button
color="primary"
(click)="createDelinquencyActionReset()"
*mifosxHasPermission="'CREATE_WC_DELINQUENCY_RESET'"
>
- <fa-icon icon="calendar" class="m-r-10"></fa-icon>{{ 'labels.buttons.Reset' | translate }}
+ <fa-icon icon="calendar" class="m-r-8"></fa-icon>{{ 'labels.buttons.Reset' | translate }}
</button>
<button
mat-raised-button
color="primary"
(click)="createDelinquencyActionUndoReset()"
*mifosxHasPermission="'CREATE_WC_DELINQUENCY_UNDO_RESET'"
>
- <fa-icon icon="calendar" class="m-r-10"></fa-icon>{{ 'labels.buttons.Undo Reset' | translate }}
+ <fa-icon icon="calendar" class="m-r-8"></fa-icon>{{ 'labels.buttons.Undo Reset' | translate }}
</button>📝 Committable suggestion
Suggested change
🤖 Prompt for AI AgentsSource: Coding guidelines |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -56,6 +56,9 @@ import { LoanProductBaseComponent } from 'app/products/loan-products/common/loan | |
| import { LoanDelinquencyActionRescheduleDialogComponent } from 'app/loans/custom-dialog/loan-delinquency-action-reschedule-dialog/loan-delinquency-action-reschedule-dialog.component'; | ||
| import { StringEnumOptionData } from 'app/shared/models/option-data.model'; | ||
| import { ProductsService } from 'app/products/products.service'; | ||
| import { | ||
| LoanDelinquencyActionResetDialogComponent | ||
| } from 'app/loans/custom-dialog/loan-delinquency-action-reset-dialog/loan-delinquency-action-reset-dialog.component'; | ||
|
|
||
| type DelinquencyActionStatus = 'active' | 'scheduled' | 'expired'; | ||
|
|
||
|
|
@@ -293,7 +296,7 @@ export class LoanDelinquencyTagsTabComponent extends LoanProductBaseComponent im | |
| const startDate: Date = response.data.value.startDate; | ||
| const endDate: Date = response.data.value.endDate; | ||
|
|
||
| this.sendDelinquencyAction(action, startDate, endDate, null, null, null, null); | ||
| this.sendDelinquencyAction(action, startDate, endDate, null, null, null, null, null); | ||
| }); | ||
| } | ||
|
|
||
|
|
@@ -312,7 +315,36 @@ export class LoanDelinquencyTagsTabComponent extends LoanProductBaseComponent im | |
| const frequency: number = response.data.value.frequency; | ||
| const frequencyType: string = response.data.value.frequencyType; | ||
|
|
||
| this.sendDelinquencyAction(action, null, null, minimumPayment, minimumPaymentType, frequency, frequencyType); | ||
| this.sendDelinquencyAction(action, null, null, minimumPayment, minimumPaymentType, frequency, frequencyType, null); | ||
| }); | ||
| } | ||
|
|
||
| createDelinquencyActionReset(): void { | ||
| const action = 'reset'; | ||
| const loanDelinquencyActionDialogRef = this.dialog.open(LoanDelinquencyActionResetDialogComponent, { | ||
| data: { | ||
| action: action, | ||
| startNewPeriod: false | ||
| } | ||
| }); | ||
| loanDelinquencyActionDialogRef.afterClosed().subscribe((response: { data: any }) => { | ||
| const startNewPeriod: boolean = response.data.value.startNewPeriod; | ||
|
|
||
| this.sendDelinquencyAction(action, null, null, null, null, null, null, startNewPeriod); | ||
| }); | ||
|
Comment on lines
+330
to
+334
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win Null pointer exception on dialog cancellation. Both
📍 Affects 1 file
🤖 Prompt for AI Agents |
||
| } | ||
| createDelinquencyActionUndoReset(): void { | ||
| const action = 'undo_reset'; | ||
| const loanDelinquencyActionDialogRef = this.dialog.open(ConfirmationDialogComponent, { | ||
| data: { | ||
| heading: this.translateService.instant('labels.heading.Undo Reset'), | ||
| dialogContext: this.translateService.instant('labels.dialogContext.Are you sure you want to undo last reset action') | ||
| } | ||
| }); | ||
| loanDelinquencyActionDialogRef.afterClosed().subscribe((response: { confirm: any }) => { | ||
| if (response.confirm) { | ||
| this.sendDelinquencyAction(action, null, null, null, null, null, null, null); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
|
|
@@ -330,7 +362,7 @@ export class LoanDelinquencyTagsTabComponent extends LoanProductBaseComponent im | |
| removePauseDialogRef.afterClosed().subscribe((response: any) => { | ||
| if (response.confirm) { | ||
| if (this.loanProductService.isLoanProduct) { | ||
| this.sendDelinquencyAction('resume', null, null, null, null, null, null); | ||
| this.sendDelinquencyAction('resume', null, null, null, null, null, null, null); | ||
| } else { | ||
| this.sendDelinquencyAction( | ||
| 'resume', | ||
|
|
@@ -339,6 +371,7 @@ export class LoanDelinquencyTagsTabComponent extends LoanProductBaseComponent im | |
| null, | ||
| null, | ||
| null, | ||
| null, | ||
| null | ||
| ); | ||
| } | ||
|
|
@@ -353,7 +386,8 @@ export class LoanDelinquencyTagsTabComponent extends LoanProductBaseComponent im | |
| minimumPayment: number | null, | ||
| minimumPaymentType: string | null, | ||
| frequency: number | null, | ||
| frequencyType: string | null | ||
| frequencyType: string | null, | ||
| startNewPeriod: boolean | null | ||
| ): void { | ||
| let payload: any = { | ||
| action, | ||
|
|
@@ -378,6 +412,12 @@ export class LoanDelinquencyTagsTabComponent extends LoanProductBaseComponent im | |
| frequency, | ||
| frequencyType | ||
| }; | ||
| } else if (action === 'reset') { | ||
| payload = { | ||
| action, | ||
| locale: this.locale, | ||
| startNewPeriod, | ||
| } | ||
| } | ||
|
|
||
| this.loansServices | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -746,6 +746,7 @@ | |
| "Unassign Staff": "Unassign Staff", | ||
| "Undo": "Undo", | ||
| "Undo Rejection": "Undo Rejection", | ||
| "Undo Reset": "Undo Reset", | ||
| "Undo Transfer": "Undo Transfer", | ||
| "Unpin": "Unpin", | ||
| "Update Default Savings": "Update Default Savings", | ||
|
|
@@ -1570,6 +1571,7 @@ | |
| "Unassign Staff": "Unassign Staff", | ||
| "Undo": "Undo", | ||
| "Undo Transaction": "Undo Transaction", | ||
| "Undo Reset": "Undo Reset", | ||
| "Upcoming Charges": "Upcoming Charges", | ||
| "Upload": "Upload", | ||
| "Upload Client Image": "Upload Client Image", | ||
|
|
@@ -2932,6 +2934,7 @@ | |
| "Standing Instruction Type": "Standing Instruction Type", | ||
| "Standing Instructions": "Standing Instructions", | ||
| "Start Date": "Start Date", | ||
| "Start new period": "Start New Period", | ||
| "Start Time": "Start Time", | ||
| "Started On": "Started On", | ||
| "Starting Date": "Starting Date", | ||
|
|
@@ -3388,6 +3391,7 @@ | |
| "Are you sure you want to delete checker": "Are you sure you want to delete checker", | ||
| "Are you sure you want to Approve Loan": "Are you sure you want to Approve Loan", | ||
| "Are you sure you want to Disburse Loan": "Are you sure you want to Disburse Loan", | ||
| "Are you sure you want to undo last reset action": "Are you sure you want to undo last reset action", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Polish the undo-reset confirmation text. Use “Are you sure you want to undo the last reset action?” and keep the translation key unchanged. 🤖 Prompt for AI Agents |
||
| "undo_account_transfer": "Are you sure you want to undo this account transfer?", | ||
| "Are you sure you want to": "Are you sure you want to ", | ||
| "the productmix component with id": "the productmix component with id", | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Use strongly typed forms instead of untyped forms.
As per coding guidelines, use strict typing conventions.
UntypedFormBuilderandUntypedFormGroupbypass compile-time type safety. Prefer strongly typedFormBuilderandFormGroupfor the reactive form.♻️ Proposed refactor
Update your imports to replace
UntypedFormBuilderandUntypedFormGroupwithFormBuilder,FormGroup, andFormControl, then apply this strongly-typed pattern:🤖 Prompt for AI Agents
Source: Coding guidelines