WEB-670: Loan modification saves charge value instead of percent (#3096)
Co-authored-by: Jose Alberto Hernandez <alberto@black-box.local>
This commit is contained in:
co-authored by
Jose Alberto Hernandez
parent
0cb4d7b0cc
commit
e198f92ba4
+1
-1
@@ -142,7 +142,7 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="layout-row responsive-column align-center gap-2px margin-t">
|
<div class="layout-row responsive-column align-center gap-2px margin-t stepper-buttons">
|
||||||
<button mat-raised-button matStepperPrevious>
|
<button mat-raised-button matStepperPrevious>
|
||||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||||
{{ 'labels.buttons.Previous' | translate }}
|
{{ 'labels.buttons.Previous' | translate }}
|
||||||
|
|||||||
+31
-14
@@ -27,7 +27,6 @@ import {
|
|||||||
/** Dialog Components */
|
/** Dialog Components */
|
||||||
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
|
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
|
||||||
import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component';
|
import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component';
|
||||||
// import { LoansAccountAddCollateralDialogComponent } from 'app/loans/custom-dialog/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component';
|
|
||||||
|
|
||||||
/** Custom Services */
|
/** Custom Services */
|
||||||
import { DatepickerBase } from 'app/shared/form-dialog/formfield/model/datepicker-base';
|
import { DatepickerBase } from 'app/shared/form-dialog/formfield/model/datepicker-base';
|
||||||
@@ -35,14 +34,16 @@ import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-
|
|||||||
import { InputBase } from 'app/shared/form-dialog/formfield/model/input-base';
|
import { InputBase } from 'app/shared/form-dialog/formfield/model/input-base';
|
||||||
import { SettingsService } from 'app/settings/settings.service';
|
import { SettingsService } from 'app/settings/settings.service';
|
||||||
import { Dates } from 'app/core/utils/dates';
|
import { Dates } from 'app/core/utils/dates';
|
||||||
import { ActivatedRoute, RouterLink } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { MatButton, MatIconButton } from '@angular/material/button';
|
import { MatIconButton } from '@angular/material/button';
|
||||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||||
import { MatDivider } from '@angular/material/divider';
|
import { MatDivider } from '@angular/material/divider';
|
||||||
import { MatStepperPrevious, MatStepperNext } from '@angular/material/stepper';
|
import { MatStepperPrevious, MatStepperNext } from '@angular/material/stepper';
|
||||||
import { DateFormatPipe } from '../../../pipes/date-format.pipe';
|
import { DateFormatPipe } from '../../../pipes/date-format.pipe';
|
||||||
import { FormatNumberPipe } from '../../../pipes/format-number.pipe';
|
import { FormatNumberPipe } from '../../../pipes/format-number.pipe';
|
||||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { LoanCharge } from 'app/loans/models/loan-charge.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recurring Deposit Account Charges Step
|
* Recurring Deposit Account Charges Step
|
||||||
@@ -77,6 +78,7 @@ export class LoansAccountChargesStepComponent implements OnInit, OnChanges {
|
|||||||
private dateUtils = inject(Dates);
|
private dateUtils = inject(Dates);
|
||||||
private route = inject(ActivatedRoute);
|
private route = inject(ActivatedRoute);
|
||||||
private settingsService = inject(SettingsService);
|
private settingsService = inject(SettingsService);
|
||||||
|
private translateService = inject(TranslateService);
|
||||||
|
|
||||||
// @Input loansAccountProductTemplate: LoansAccountProductTemplate
|
// @Input loansAccountProductTemplate: LoansAccountProductTemplate
|
||||||
@Input() loansAccountProductTemplate: any;
|
@Input() loansAccountProductTemplate: any;
|
||||||
@@ -148,11 +150,13 @@ export class LoansAccountChargesStepComponent implements OnInit, OnChanges {
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
if (this.loansAccountTemplate && this.loansAccountTemplate.charges) {
|
if (this.loansAccountTemplate && this.loansAccountTemplate.charges) {
|
||||||
this.chargesDataSource =
|
this.chargesDataSource =
|
||||||
this.loansAccountTemplate.charges.map((charge: any) => {
|
this.loansAccountTemplate.charges.map((loanCharge: LoanCharge) => {
|
||||||
|
const amount = this.isPercentageCharge(loanCharge) ? loanCharge.percentage : loanCharge.amount;
|
||||||
return {
|
return {
|
||||||
...charge,
|
...loanCharge,
|
||||||
id: charge.id,
|
amount,
|
||||||
chargeId: charge.chargeId
|
id: loanCharge.id,
|
||||||
|
chargeId: loanCharge.chargeId
|
||||||
};
|
};
|
||||||
}) || [];
|
}) || [];
|
||||||
}
|
}
|
||||||
@@ -189,11 +193,13 @@ export class LoansAccountChargesStepComponent implements OnInit, OnChanges {
|
|||||||
})) || [];
|
})) || [];
|
||||||
} else if (isModification && this.loansAccountTemplate && this.loansAccountTemplate.charges) {
|
} else if (isModification && this.loansAccountTemplate && this.loansAccountTemplate.charges) {
|
||||||
this.chargesDataSource =
|
this.chargesDataSource =
|
||||||
this.loansAccountTemplate.charges.map((charge: any) => {
|
this.loansAccountTemplate.charges.map((loanCharge: LoanCharge) => {
|
||||||
|
const amount = this.isPercentageCharge(loanCharge) ? loanCharge.percentage : loanCharge.amount;
|
||||||
return {
|
return {
|
||||||
...charge,
|
...loanCharge,
|
||||||
id: charge.id,
|
amount,
|
||||||
chargeId: charge.chargeId
|
id: loanCharge.id,
|
||||||
|
chargeId: loanCharge.chargeId
|
||||||
};
|
};
|
||||||
}) || [];
|
}) || [];
|
||||||
}
|
}
|
||||||
@@ -222,13 +228,16 @@ export class LoansAccountChargesStepComponent implements OnInit, OnChanges {
|
|||||||
new InputBase({
|
new InputBase({
|
||||||
controlName: 'amount',
|
controlName: 'amount',
|
||||||
label: 'Amount',
|
label: 'Amount',
|
||||||
value: charge.amount,
|
value: this.isPercentageCharge(charge) ? charge.amountOrPercentage : charge.amount,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
required: false
|
required: false
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
const data = {
|
const data = {
|
||||||
title: 'Edit Charge Amount',
|
title:
|
||||||
|
this.translateService.instant('labels.buttons.Edit') +
|
||||||
|
' ' +
|
||||||
|
this.translateService.instant('labels.inputs.Charge Amount'),
|
||||||
layout: { addButtonText: 'Confirm' },
|
layout: { addButtonText: 'Confirm' },
|
||||||
formfields: formfields
|
formfields: formfields
|
||||||
};
|
};
|
||||||
@@ -254,11 +263,15 @@ export class LoansAccountChargesStepComponent implements OnInit, OnChanges {
|
|||||||
label: 'Date',
|
label: 'Date',
|
||||||
value: charge.dueDate || charge.feeOnMonthDay || '',
|
value: charge.dueDate || charge.feeOnMonthDay || '',
|
||||||
type: 'datetime-local',
|
type: 'datetime-local',
|
||||||
|
maxDate: this.settingsService.maxFutureDate,
|
||||||
required: false
|
required: false
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
const data = {
|
const data = {
|
||||||
title: 'Edit Charge Date',
|
title:
|
||||||
|
this.translateService.instant('labels.buttons.Edit') +
|
||||||
|
' ' +
|
||||||
|
this.translateService.instant('labels.inputs.Charge Date'),
|
||||||
layout: { addButtonText: 'Confirm' },
|
layout: { addButtonText: 'Confirm' },
|
||||||
formfields: formfields
|
formfields: formfields
|
||||||
};
|
};
|
||||||
@@ -380,4 +393,8 @@ export class LoansAccountChargesStepComponent implements OnInit, OnChanges {
|
|||||||
this.selectAllItems =
|
this.selectAllItems =
|
||||||
len === 0 ? false : this.activeClientMembers.filter((item: any) => item.selected).length === len;
|
len === 0 ? false : this.activeClientMembers.filter((item: any) => item.selected).length === len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private isPercentageCharge(loanCharge: LoanCharge): boolean {
|
||||||
|
return loanCharge.chargeCalculationType.code.includes('.percent.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -54,7 +54,7 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
<div class="layout-row align-center gap-2percent margin-t responsive-column stepper-buttons">
|
||||||
<button mat-raised-button matStepperPrevious disabled>
|
<button mat-raised-button matStepperPrevious disabled>
|
||||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||||
{{ 'labels.buttons.Previous' | translate }}
|
{{ 'labels.buttons.Previous' | translate }}
|
||||||
|
|||||||
+1
-1
@@ -138,7 +138,7 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
<div class="layout-row align-center gap-2percent margin-t responsive-column stepper-buttons">
|
||||||
<button mat-raised-button matStepperPrevious disabled>
|
<button mat-raised-button matStepperPrevious disabled>
|
||||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||||
{{ 'labels.buttons.Previous' | translate }}
|
{{ 'labels.buttons.Previous' | translate }}
|
||||||
|
|||||||
+1
-1
@@ -390,7 +390,7 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="layout-row responsive-column align-center gap-2px margin-t">
|
<div class="layout-row responsive-column align-center gap-2px margin-t stepper-buttons">
|
||||||
<button mat-raised-button matStepperPrevious>
|
<button mat-raised-button matStepperPrevious>
|
||||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||||
{{ 'labels.buttons.Previous' | translate }}
|
{{ 'labels.buttons.Previous' | translate }}
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@
|
|||||||
</mifosx-repayment-schedule-tab>
|
</mifosx-repayment-schedule-tab>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="layout-row responsive-column align-center gap-2px margin-t">
|
<div class="layout-row responsive-column align-center gap-2px margin-t stepper-buttons">
|
||||||
<button mat-raised-button matStepperPrevious>
|
<button mat-raised-button matStepperPrevious>
|
||||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||||
{{ 'labels.buttons.Previous' | translate }}
|
{{ 'labels.buttons.Previous' | translate }}
|
||||||
|
|||||||
+1
-1
@@ -726,7 +726,7 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
<div class="layout-row align-center gap-2percent margin-t responsive-column stepper-buttons">
|
||||||
<button mat-raised-button matStepperPrevious>
|
<button mat-raised-button matStepperPrevious>
|
||||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||||
{{ 'labels.buttons.Previous' | translate }}
|
{{ 'labels.buttons.Previous' | translate }}
|
||||||
|
|||||||
+1
-1
@@ -59,7 +59,7 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="layout-row responsive-column align-center gap-2px margin-t">
|
<div class="layout-row responsive-column align-center gap-2px margin-t stepper-buttons">
|
||||||
<button mat-raised-button matStepperPrevious>
|
<button mat-raised-button matStepperPrevious>
|
||||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||||
{{ 'labels.buttons.Previous' | translate }}
|
{{ 'labels.buttons.Previous' | translate }}
|
||||||
|
|||||||
@@ -56,7 +56,12 @@
|
|||||||
|
|
||||||
<ng-container matColumnDef="calculationtype">
|
<ng-container matColumnDef="calculationtype">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Calculation Type' | translate }}</th>
|
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Calculation Type' | translate }}</th>
|
||||||
<td mat-cell *matCellDef="let charge">{{ charge.chargeCalculationType.value }}</td>
|
<td mat-cell *matCellDef="let charge">
|
||||||
|
{{ charge.chargeCalculationType.value }}
|
||||||
|
@if (isPercentageCharge(charge)) {
|
||||||
|
({{ charge.percentage | formatNumber: 2 }}%)
|
||||||
|
}
|
||||||
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="due">
|
<ng-container matColumnDef="due">
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
/** Angular Imports */
|
/** Angular Imports */
|
||||||
import { Component, OnInit, ViewChild, inject } from '@angular/core';
|
import { Component, OnInit, ViewChild, inject } from '@angular/core';
|
||||||
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { MatPaginator } from '@angular/material/paginator';
|
import { MatPaginator } from '@angular/material/paginator';
|
||||||
import { MatSort, MatSortHeader } from '@angular/material/sort';
|
import { MatSort, MatSortHeader } from '@angular/material/sort';
|
||||||
@@ -47,6 +47,8 @@ import { CurrencyPipe } from '@angular/common';
|
|||||||
import { MatTooltip } from '@angular/material/tooltip';
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
import { DateFormatPipe } from '../../../pipes/date-format.pipe';
|
import { DateFormatPipe } from '../../../pipes/date-format.pipe';
|
||||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
|
import { LoanCharge } from 'app/loans/models/loan-charge.model';
|
||||||
|
import { FormatNumberPipe } from '@pipes/format-number.pipe';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'mifosx-charges-tab',
|
selector: 'mifosx-charges-tab',
|
||||||
@@ -69,7 +71,8 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
MatRow,
|
MatRow,
|
||||||
MatPaginator,
|
MatPaginator,
|
||||||
CurrencyPipe,
|
CurrencyPipe,
|
||||||
DateFormatPipe
|
DateFormatPipe,
|
||||||
|
FormatNumberPipe
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class ChargesTabComponent implements OnInit {
|
export class ChargesTabComponent implements OnInit {
|
||||||
@@ -78,14 +81,14 @@ export class ChargesTabComponent implements OnInit {
|
|||||||
private dateUtils = inject(Dates);
|
private dateUtils = inject(Dates);
|
||||||
private router = inject(Router);
|
private router = inject(Router);
|
||||||
private translateService = inject(TranslateService);
|
private translateService = inject(TranslateService);
|
||||||
dialog = inject(MatDialog);
|
private dialog = inject(MatDialog);
|
||||||
private settingsService = inject(SettingsService);
|
private settingsService = inject(SettingsService);
|
||||||
private systemService = inject(SystemService);
|
private systemService = inject(SystemService);
|
||||||
|
|
||||||
/** Loan Details Data */
|
/** Loan Details Data */
|
||||||
loanDetails: any;
|
loanDetails: any;
|
||||||
/** Charges Data */
|
/** Charges Data */
|
||||||
chargesData: any;
|
chargesData: LoanCharge[] = [];
|
||||||
/** Status */
|
/** Status */
|
||||||
status: any;
|
status: any;
|
||||||
/** Columns to be displayed in charges table. */
|
/** Columns to be displayed in charges table. */
|
||||||
@@ -232,7 +235,7 @@ export class ChargesTabComponent implements OnInit {
|
|||||||
new InputBase({
|
new InputBase({
|
||||||
controlName: 'amount',
|
controlName: 'amount',
|
||||||
label: 'Amount',
|
label: 'Amount',
|
||||||
value: charge.amount || charge.amountOrPercentage,
|
value: this.isPercentageCharge(charge) ? charge.amountOrPercentage : charge.amount,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
required: true
|
required: true
|
||||||
})
|
})
|
||||||
@@ -295,4 +298,8 @@ export class ChargesTabComponent implements OnInit {
|
|||||||
.navigateByUrl(`/clients/${clientId}/loans-accounts`, { skipLocationChange: true })
|
.navigateByUrl(`/clients/${clientId}/loans-accounts`, { skipLocationChange: true })
|
||||||
.then(() => this.router.navigate([url]));
|
.then(() => this.router.navigate([url]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isPercentageCharge(loanCharge: LoanCharge): boolean {
|
||||||
|
return loanCharge.chargeCalculationType.code.includes('.percent.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
matTooltip="{{ 'labels.status.' + loanDetailsData.status.value | translate }}"
|
matTooltip="{{ 'labels.status.' + loanDetailsData.status.value | translate }}"
|
||||||
[ngClass]="iconLoanStatusColor() | statusLookup"
|
[ngClass]="iconLoanStatusColor() | statusLookup"
|
||||||
></i>
|
></i>
|
||||||
<span class="m-r-5">{{ 'labels.heading.Loan Product' | translate }} :</span>
|
<span class="m-l-5 m-r-5">{{ 'labels.heading.Loan Product' | translate }} :</span>
|
||||||
<span class="m-r-5"
|
<span class="m-r-5"
|
||||||
><mifosx-long-text textValue="{{ loanDetailsData.loanProductName }}"></mifosx-long-text
|
><mifosx-long-text textValue="{{ loanDetailsData.loanProductName }}"></mifosx-long-text
|
||||||
></span>
|
></span>
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* 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 { Charge, Currency } from 'app/shared/models/general.model';
|
||||||
|
import { OptionData } from 'app/shared/models/option-data.model';
|
||||||
|
|
||||||
|
export interface LoanCharge {
|
||||||
|
id: number;
|
||||||
|
chargeId: number;
|
||||||
|
name: string;
|
||||||
|
chargeTimeType: Charge;
|
||||||
|
submittedOnDate: number[];
|
||||||
|
dueDate: number[];
|
||||||
|
chargeCalculationType: OptionData;
|
||||||
|
percentage: number;
|
||||||
|
amountPercentageAppliedTo: number;
|
||||||
|
currency: Currency;
|
||||||
|
amount: number;
|
||||||
|
amountPaid: number;
|
||||||
|
amountWaived: number;
|
||||||
|
amountWrittenOff: number;
|
||||||
|
amountOutstanding: number;
|
||||||
|
amountOrPercentage: number;
|
||||||
|
penalty: boolean;
|
||||||
|
chargePaymentMode: OptionData;
|
||||||
|
chargeTime: OptionData;
|
||||||
|
paid: boolean;
|
||||||
|
waived: boolean;
|
||||||
|
chargePayable: boolean;
|
||||||
|
loanId: number;
|
||||||
|
externalId: string;
|
||||||
|
externalLoanId: string;
|
||||||
|
}
|
||||||
@@ -44,3 +44,7 @@ textarea.noresize {
|
|||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stepper-buttons {
|
||||||
|
height: 38px;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user