From 622a738caa14b21946e14581843468b4d565e37a Mon Sep 17 00:00:00 2001 From: Attila Budai Date: Wed, 10 Sep 2025 13:38:56 +0200 Subject: [PATCH] WEB-296: add undo write-off in the upper right menu --- .../loan-action-button.resolver.ts | 2 + .../loan-account-actions.component.html | 1 + .../loan-account-actions.component.ts | 8 +- .../undo-write-off.component.html | 32 ++++++ .../undo-write-off.component.scss | 32 ++++++ .../undo-write-off.component.ts | 102 ++++++++++++++++++ .../loans-view/loan-accounts-button-config.ts | 5 + src/assets/translations/en-US.json | 4 +- 8 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 src/app/loans/loans-view/loan-account-actions/undo-write-off/undo-write-off.component.html create mode 100644 src/app/loans/loans-view/loan-account-actions/undo-write-off/undo-write-off.component.scss create mode 100644 src/app/loans/loans-view/loan-account-actions/undo-write-off/undo-write-off.component.ts diff --git a/src/app/loans/common-resolvers/loan-action-button.resolver.ts b/src/app/loans/common-resolvers/loan-action-button.resolver.ts index 5549e3cb5..ed17c6810 100644 --- a/src/app/loans/common-resolvers/loan-action-button.resolver.ts +++ b/src/app/loans/common-resolvers/loan-action-button.resolver.ts @@ -79,6 +79,8 @@ export class LoanActionButtonResolver { return this.loansService.getLoanActionTemplate(loanId, 'contractTermination'); } else if (loanActionButton === 'Buy Down Fee') { return this.loansService.getLoanActionTemplate(loanId, 'buyDownFee'); + } else if (loanActionButton === 'Undo Write-off') { + return undefined; } else { return undefined; } diff --git a/src/app/loans/loans-view/loan-account-actions/loan-account-actions.component.html b/src/app/loans/loans-view/loan-account-actions/loan-account-actions.component.html index be7954dd6..c13b67ae2 100644 --- a/src/app/loans/loans-view/loan-account-actions/loan-account-actions.component.html +++ b/src/app/loans/loans-view/loan-account-actions/loan-account-actions.component.html @@ -66,3 +66,4 @@ [dataObject]="navigationData" *ngIf="actions['Add Interest Pause']" > + diff --git a/src/app/loans/loans-view/loan-account-actions/loan-account-actions.component.ts b/src/app/loans/loans-view/loan-account-actions/loan-account-actions.component.ts index 3edb352eb..65fb34cf6 100644 --- a/src/app/loans/loans-view/loan-account-actions/loan-account-actions.component.ts +++ b/src/app/loans/loans-view/loan-account-actions/loan-account-actions.component.ts @@ -30,6 +30,7 @@ import { AssetTransferLoanComponent } from './asset-transfer-loan/asset-transfer import { LoanReagingComponent } from './loan-reaging/loan-reaging.component'; import { LoanReamortizeComponent } from './loan-reamortize/loan-reamortize.component'; import { AddInterestPauseComponent } from './add-interest-pause/add-interest-pause.component'; +import { UndoWriteOffComponent } from './undo-write-off/undo-write-off.component'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; /** @@ -69,7 +70,8 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; AssetTransferLoanComponent, LoanReagingComponent, LoanReamortizeComponent, - AddInterestPauseComponent + AddInterestPauseComponent, + UndoWriteOffComponent ] }) export class LoanAccountActionsComponent { @@ -116,6 +118,7 @@ export class LoanAccountActionsComponent { 'Capitalized Income': boolean; 'Contract Termination': boolean; 'Buy Down Fee': boolean; + 'Undo Write-off': boolean; } = { Close: false, 'Undo Approval': false, @@ -154,7 +157,8 @@ export class LoanAccountActionsComponent { 'Add Interest Pause': false, 'Capitalized Income': false, 'Contract Termination': false, - 'Buy Down Fee': false + 'Buy Down Fee': false, + 'Undo Write-off': false }; actionButtonData: any; diff --git a/src/app/loans/loans-view/loan-account-actions/undo-write-off/undo-write-off.component.html b/src/app/loans/loans-view/loan-account-actions/undo-write-off/undo-write-off.component.html new file mode 100644 index 000000000..83655be95 --- /dev/null +++ b/src/app/loans/loans-view/loan-account-actions/undo-write-off/undo-write-off.component.html @@ -0,0 +1,32 @@ +
+ +
+ +
+

+ {{ 'labels.text.Undo Write-off Description' | translate }} +

+ + + {{ 'labels.inputs.Note' | translate }} + + +
+ + + + + +
+
+
+
diff --git a/src/app/loans/loans-view/loan-account-actions/undo-write-off/undo-write-off.component.scss b/src/app/loans/loans-view/loan-account-actions/undo-write-off/undo-write-off.component.scss new file mode 100644 index 000000000..4ea838079 --- /dev/null +++ b/src/app/loans/loans-view/loan-account-actions/undo-write-off/undo-write-off.component.scss @@ -0,0 +1,32 @@ +.container { + max-width: 37rem; +} + +.mat-card { + padding: 20px; +} + +.layout-column { + display: flex; + flex-direction: column; + gap: 20px; +} + +.mat-card-actions { + padding-top: 20px; +} + +.gap-5px { + gap: 5px; +} + +.responsive-column { + @media (width <= 768px) { + flex-direction: column; + align-items: stretch; + } +} + +.m-b-20 { + margin-bottom: 20px; +} diff --git a/src/app/loans/loans-view/loan-account-actions/undo-write-off/undo-write-off.component.ts b/src/app/loans/loans-view/loan-account-actions/undo-write-off/undo-write-off.component.ts new file mode 100644 index 000000000..ba1722077 --- /dev/null +++ b/src/app/loans/loans-view/loan-account-actions/undo-write-off/undo-write-off.component.ts @@ -0,0 +1,102 @@ +/** Angular Imports */ +import { Component, OnInit, Input } from '@angular/core'; +import { UntypedFormGroup, UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; + +/** Custom Services */ +import { LoansService } from 'app/loans/loans.service'; +import { SettingsService } from 'app/settings/settings.service'; +import { AlertService } from 'app/core/alert/alert.service'; +import { Dates } from 'app/core/utils/dates'; +import { CdkTextareaAutosize } from '@angular/cdk/text-field'; +import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; + +/** + * Loan Undo Write-off Action + */ +@Component({ + selector: 'mifosx-undo-write-off', + templateUrl: './undo-write-off.component.html', + styleUrls: ['./undo-write-off.component.scss'], + imports: [ + ...STANDALONE_SHARED_IMPORTS, + CdkTextareaAutosize + ] +}) +export class UndoWriteOffComponent implements OnInit { + @Input() dataObject: any; + /** Loan Id */ + loanId: string; + /** Minimum Date allowed. */ + minDate = new Date(2000, 0, 1); + /** Maximum Date allowed. */ + maxDate = new Date(); + /** Undo Write-off Loan Form */ + undoWriteOffLoanForm: UntypedFormGroup; + + /** + * @param {FormBuilder} formBuilder Form Builder. + * @param {LoansService} loanService Loan Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + * @param {SettingsService} settingsService Settings Service + */ + constructor( + private formBuilder: UntypedFormBuilder, + private loanService: LoansService, + private route: ActivatedRoute, + private router: Router, + private dateUtils: Dates, + private settingsService: SettingsService, + private alertService: AlertService + ) { + this.loanId = this.route.snapshot.params['loanId']; + } + + /** + * Creates the undo write-off loan form + * and initialize with the required values + */ + ngOnInit() { + this.maxDate = this.settingsService.businessDate; + this.createUndoWriteOffLoanForm(); + } + + /** + * Creates the undo write-off loan form + */ + createUndoWriteOffLoanForm() { + this.undoWriteOffLoanForm = this.formBuilder.group({ + note: [''] + }); + } + + /** Submits the undo write-off form */ + submit() { + const undoWriteOffLoanFormData = this.undoWriteOffLoanForm.value; + const locale = this.settingsService.language.code; + const dateFormat = this.settingsService.dateFormat; + const operationDate = this.settingsService.businessDate; + const data = { + ...undoWriteOffLoanFormData, + transactionDate: this.dateUtils.formatDate(operationDate && new Date(operationDate), dateFormat), + transactionAmount: 0, + dateFormat, + locale + }; + + this.loanService.submitLoanActionButton(this.loanId, data, 'undowriteoff').subscribe({ + next: (response: any) => { + this.router.navigate(['../../general'], { relativeTo: this.route }); + }, + error: (error) => { + console.error('Undo write-off failed:', error); + this.alertService.alert({ + type: 'Undo Write-off Failed', + message: + 'An error occurred while processing the undo write-off transaction. Please try again or contact support if the problem persists.' + }); + } + }); + } +} diff --git a/src/app/loans/loans-view/loan-accounts-button-config.ts b/src/app/loans/loans-view/loan-accounts-button-config.ts index 728388465..c78e5f262 100644 --- a/src/app/loans/loans-view/loan-accounts-button-config.ts +++ b/src/app/loans/loans-view/loan-accounts-button-config.ts @@ -124,6 +124,11 @@ export class LoansAccountButtonConfiguration { name: 'Recovery Payment', icon: 'briefcase', taskPermissionName: 'RECOVERYPAYMENT_LOAN' + }, + { + name: 'Undo Write-off', + icon: 'undo', + taskPermissionName: 'UNDOWRITEOFF_LOAN' } ]; break; diff --git a/src/assets/translations/en-US.json b/src/assets/translations/en-US.json index 7fdd159e0..2d0a67cbf 100644 --- a/src/assets/translations/en-US.json +++ b/src/assets/translations/en-US.json @@ -2616,6 +2616,7 @@ "Recover From Guarantor": "Recover From Guarantor", "Recovery Payment": "Recovery Payment", "Reject": "Reject", + "Undo Write-off": "Undo Write-off", "Reports": "Reports", "Reschedule": "Reschedule", "Savings": "Savings", @@ -3428,7 +3429,8 @@ "Voluntary": "Voluntary", "Write a note": "Write a note", "“Maker-Checker” principle requires every tasks": "The “Maker-Checker” principle requires every tasks to be completed by two people to reduce the chance of errors and misuse. One person initiates the process and the second completes it.", - "Buy Down Fees": "Buy Down Fees" + "Buy Down Fees": "Buy Down Fees", + "Undo Write-off Description": "This action will reverse the write-off transaction and restore the loan to its previous active status. Please provide a note explaining the reason for this action." }, "titles": { "Dashboard": "Dashboard",