Fix Loan product Payment Allocation (#1855)

Co-authored-by: Jose Alberto Hernandez <alberto@blue-daemon.local>
This commit is contained in:
José Alberto Hernández
2023-08-29 20:29:54 -06:00
committed by GitHub
co-authored by Jose Alberto Hernandez
parent 80936a3ea9
commit 32a9db1f6d
15 changed files with 164 additions and 125 deletions
@@ -63,9 +63,10 @@
<ng-template matStepLabel>PAYMENT ALLOCATION</ng-template>
<mifosx-loan-product-payment-strategy-step
<mifosx-loan-product-payment-strategy-step *ngFor="let transactionType of transactionTypes"
[loanProductsTemplate]="loanProductsTemplate"
(paymentAllocation)="setPaymentAllocation($event)"
(transactionType)="transactionType"
>
</mifosx-loan-product-payment-strategy-step>
@@ -13,7 +13,7 @@ import { LoanProductAccountingStepComponent } from '../loan-product-stepper/loan
/** Custom Services */
import { ProductsService } from 'app/products/products.service';
import { LoanProducts } from '../loan-products';
import { PaymentAllocation } from '../loan-product-stepper/loan-product-payment-strategy-step/loan-product-payment-strategy-step.component';
import { PaymentAllocation, PaymentAllocationTransactionType, PaymentAllocationTransactionTypes, PaymentAllocationTypes } from '../loan-product-stepper/loan-product-payment-strategy-step/payment-allocation-model';
@Component({
selector: 'mifosx-create-loan-product',
@@ -35,6 +35,9 @@ export class CreateLoanProductComponent implements OnInit {
isAdvancedPaymentStrategy = false;
paymentAllocation: PaymentAllocation[] = [];
transactionTypes: PaymentAllocationTransactionType[] = [
PaymentAllocationTransactionTypes.DEFAULT_TRANSACTION
];
/**
* @param {ActivatedRoute} route Activated Route.
@@ -63,10 +63,11 @@
<ng-template matStepLabel>PAYMENT ALLOCATION</ng-template>
<mifosx-loan-product-payment-strategy-step
<mifosx-loan-product-payment-strategy-step *ngFor="let transactionType of transactionTypes"
[loanProductsTemplate]="loanProductsTemplate"
(paymentAllocation)="setPaymentAllocation($event)"
(paymentAllocationChange)="paymentAllocationChanged($event)"
(transactionType)="transactionType"
>
</mifosx-loan-product-payment-strategy-step>
@@ -14,7 +14,7 @@ import { LoanProductAccountingStepComponent } from '../loan-product-stepper/loan
import { ProductsService } from 'app/products/products.service';
import { GlobalConfiguration } from 'app/system/configurations/global-configurations-tab/configuration.model';
import { LoanProducts } from '../loan-products';
import { PaymentAllocation } from '../loan-product-stepper/loan-product-payment-strategy-step/loan-product-payment-strategy-step.component';
import { PaymentAllocation, PaymentAllocationTransactionType, PaymentAllocationTransactionTypes } from '../loan-product-stepper/loan-product-payment-strategy-step/payment-allocation-model';
@Component({
selector: 'mifosx-edit-loan-product',
@@ -37,6 +37,9 @@ export class EditLoanProductComponent implements OnInit {
isAdvancedPaymentStrategy = false;
wasPaymentAllocationChanged = false;
paymentAllocation: PaymentAllocation[] = [];
transactionTypes: PaymentAllocationTransactionType[] = [
PaymentAllocationTransactionTypes.DEFAULT_TRANSACTION
];
/**
* @param {ActivatedRoute} route Activated Route.
@@ -1,32 +1,55 @@
<div class="container space-top" fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
<div fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
<mat-form-field fxFlex="33%">
<mat-label>Future Installment Allocation Rule</mat-label>
<mat-select [formControl]="futureInstallmentAllocationRule">
<mat-option *ngFor="let installmentAllocation of installmentAllocationOptions" [value]="installmentAllocation.code">
{{ installmentAllocation.name | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex="33%">
<mat-label>Transaction Type</mat-label>
<mat-select [formControl]="transactionTypeCtrl" disabled>
<mat-option *ngFor="let transactionTypeOption of transactionTypeOptions" [value]="transactionTypeOption.code">
{{ transactionTypeOption.name | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<div class="mat-elevation-z8 container">
<div class="mat-elevation-z8 container m-t-20">
<table mat-table [dataSource]="transactionTypesData" #table cdkDropList [cdkDropListData]="transactionTypesData"
(cdkDropListDropped)="dropTable($event)">
<table mat-table [dataSource]="paymentAllocationsData" #table cdkDropList
[cdkDropListData]="paymentAllocationsData" (cdkDropListDropped)="dropTable($event)"
matTooltip="You can drag and drop the rows to set a Payment Allocations order">
<ng-container matColumnDef="order">
<th mat-header-cell *matHeaderCellDef> Order </th>
<td mat-cell *matCellDef="let transactionType; let rowIndex = index"> {{ (rowIndex + 1) }} </td>
</ng-container>
<ng-container matColumnDef="order">
<th mat-header-cell *matHeaderCellDef> Order </th>
<td mat-cell *matCellDef="let paymentAllocation; let rowIndex = index"> {{ (rowIndex + 1) }} </td>
</ng-container>
<ng-container matColumnDef="transactionName">
<th mat-header-cell *matHeaderCellDef> Transaction Type </th>
<td mat-cell *matCellDef="let transactionType"> {{ transactionType.name }} </td>
</ng-container>
<ng-container matColumnDef="paymentAllocation">
<th mat-header-cell *matHeaderCellDef> Payment Allocation </th>
<td mat-cell *matCellDef="let paymentAllocation"> {{ paymentAllocation.name }} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row cdkDrag [cdkDragData]="row" *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row cdkDrag [cdkDragData]="row" *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>
<mat-form-field fxFlex="33%" class="m-t-20">
<mat-label>Future Installment Allocation Rule</mat-label>
<mat-select [formControl]="futureInstallmentAllocationRule">
<mat-option *ngFor="let installmentAllocation of installmentAllocationOptions"
[value]="installmentAllocation.code">
{{ installmentAllocation.name | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div fxFlexFill fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
<button mat-raised-button matStepperPrevious>
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
Previous
</button>
<button mat-raised-button matStepperNext>
Next
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
</button>
</div>
</div>
@@ -3,23 +3,7 @@ import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angu
import { UntypedFormControl, Validators } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { MatTable } from '@angular/material/table';
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
interface PaymentCode {
name: string;
code: string;
}
interface PaymentAllocationOrder {
paymentAllocationRule: string;
order: number;
}
export interface PaymentAllocation {
transactionType: string;
paymentAllocationOrder: PaymentAllocationOrder[];
futureInstallmentAllocationRule: string;
}
import { FutureInstallmentAllocationRules, PaymentAllocation, PaymentAllocationOrder, PaymentAllocationTransactionType, PaymentAllocationTransactionTypes, PaymentAllocationTypes, PaymentCode } from './payment-allocation-model';
@Component({
selector: 'mifosx-loan-product-payment-strategy-step',
@@ -29,112 +13,64 @@ export interface PaymentAllocation {
export class LoanProductPaymentStrategyStepComponent implements OnInit {
@Input() loanProductsTemplate: any;
@Input() transactionType: PaymentAllocationTransactionType;
@Output() paymentAllocation = new EventEmitter<PaymentAllocation[]>();
@Output() paymentAllocationChange = new EventEmitter<boolean>();
transactionTypesData: PaymentCode[] = [];
transactionTypeOptions: PaymentCode[] = [];
paymentAllocationsData: PaymentCode[] = [];
installmentAllocationOptions: PaymentCode[] = [];
transactionTypeOptions: PaymentCode[] = [];
transactionType = new UntypedFormControl('');
transactionTypeCtrl = new UntypedFormControl('');
futureInstallmentAllocationRule = new UntypedFormControl('', Validators.required);
@ViewChild('table') table: MatTable<any>;
/** Columns to be displayed in the table. */
displayedColumns: string[] = ['order', 'transactionName'];
displayedColumns: string[] = ['order', 'paymentAllocation'];
constructor(private dialog: MatDialog) { }
constructor() { }
ngOnInit(): void {
this.transactionTypesData = [];
this.paymentAllocationsData = [];
this.setOptions();
this.sendPaymentAllocation();
}
setOptions(): void {
this.transactionTypeOptions = [
{ name: 'Past due penalty', code: 'PAST_DUE_PENALTY' },
{ name: 'Past due fee', code: 'PAST_DUE_FEE' },
{ name: 'Past due principal', code: 'PAST_DUE_PRINCIPAL' },
{ name: 'Past due interest', code: 'PAST_DUE_INTEREST' },
{ name: 'Due penalty', code: 'DUE_PENALTY' },
{ name: 'Due fee', code: 'DUE_FEE' },
{ name: 'Due principal', code: 'DUE_PRINCIPAL' },
{ name: 'Due interest', code: 'DUE_INTEREST' },
{ name: 'In advance penalty', code: 'IN_ADVANCE_PENALTY' },
{ name: 'In advance fee', code: 'IN_ADVANCE_FEE' },
{ name: 'In advance principal', code: 'IN_ADVANCE_PRINCIPAL' },
{ name: 'In advance interest', code: 'IN_ADVANCE_INTEREST' }
];
this.transactionTypesData = this.transactionTypeOptions;
this.installmentAllocationOptions = [
{ name: 'Next Installment', code: 'NEXT_INSTALLMENT' },
{ name: 'Last Installment', code: 'LAST_INSTALLMENT' },
{ name: 'Reamortization', code: 'REAMORTIZATION' }
];
this.futureInstallmentAllocationRule.patchValue('NEXT_INSTALLMENT');
}
addTransactionType(): void {
this.transactionTypeOptions.forEach((txType: PaymentCode) => {
if (txType.code === this.transactionType.value) {
this.transactionTypesData = [
...this.transactionTypesData,
txType
];
this.sendPaymentAllocation();
this.removePaymentCode(txType);
this.resetTransactionType();
}
});
}
removePaymentCode(txType: PaymentCode): void {
this.transactionTypeOptions = this.transactionTypeOptions.filter(({ code }) => code !== txType.code);
}
resetTransactionType(): void {
this.transactionType.patchValue('');
this.transactionType.markAsUntouched();
}
removeTransactionType(index: number) {
const transactionTypeName = this.transactionTypesData[index].name;
const dialogRef = this.dialog.open(DeleteDialogComponent, {
data: { deleteContext: transactionTypeName }
});
dialogRef.afterClosed().subscribe((response: any) => {
if (response.delete) {
this.transactionTypesData.splice(index, 1);
this.transactionTypesData = this.transactionTypesData.concat([]);
this.transactionTypesData = [...this.transactionTypesData];
this.sendPaymentAllocation();
}
});
this.paymentAllocationsData = PaymentAllocationTypes.options;
this.transactionTypeOptions = PaymentAllocationTransactionTypes.options;
this.transactionTypeCtrl.patchValue(this.transactionTypeOptions[0].code);
if (this.transactionType) {
this.transactionTypeCtrl.patchValue(this.transactionType.code);
}
this.installmentAllocationOptions = FutureInstallmentAllocationRules.options;
this.futureInstallmentAllocationRule.patchValue(this.installmentAllocationOptions[0].code);
}
dropTable(event: CdkDragDrop<any[]>) {
const prevIndex = this.transactionTypesData.findIndex((d: any) => d === event.item.data);
moveItemInArray(this.transactionTypesData, prevIndex, event.currentIndex);
this.transactionTypesData = [...this.transactionTypesData];
const prevIndex = this.paymentAllocationsData.findIndex((d: any) => d === event.item.data);
moveItemInArray(this.paymentAllocationsData, prevIndex, event.currentIndex);
this.paymentAllocationsData = [...this.paymentAllocationsData];
this.table.renderRows();
this.sendPaymentAllocation();
this.paymentAllocationChange.emit(true);
}
sendPaymentAllocation(): void {
let transactionTypeVal = PaymentAllocationTransactionTypes.DEFAULT_TRANSACTION.code;
if (this.transactionType) {
transactionTypeVal = this.transactionType.code;
}
const paymentAllocationOrder: PaymentAllocationOrder[] = [];
this.transactionTypesData.forEach((txType: PaymentCode, index: number) => {
this.paymentAllocationsData.forEach((txType: PaymentCode, index: number) => {
paymentAllocationOrder.push({ 'paymentAllocationRule': txType.code, order: (index + 1) });
});
const paymentAllocation: PaymentAllocation[] = [];
paymentAllocation.push({
'futureInstallmentAllocationRule': this.futureInstallmentAllocationRule.value,
'transactionType': 'DEFAULT',
'transactionType': transactionTypeVal,
'paymentAllocationOrder': paymentAllocationOrder
});
@@ -0,0 +1,53 @@
export interface PaymentCode {
name: string;
code: string;
}
export interface PaymentAllocationOrder {
paymentAllocationRule: string;
order: number;
}
export interface PaymentAllocation {
transactionType: string;
paymentAllocationOrder: PaymentAllocationOrder[];
futureInstallmentAllocationRule: string;
}
export interface PaymentAllocationTransactionType {
name: string;
code: string;
}
export class PaymentAllocationTransactionTypes {
public static DEFAULT_TRANSACTION: PaymentAllocationTransactionType = { name: 'Default', code: 'DEFAULT' };
public static options: PaymentCode[] = [
this.DEFAULT_TRANSACTION
];
}
export class PaymentAllocationTypes {
public static options: PaymentCode[] = [
{ name: 'Past due penalty', code: 'PAST_DUE_PENALTY' },
{ name: 'Past due fee', code: 'PAST_DUE_FEE' },
{ name: 'Past due principal', code: 'PAST_DUE_PRINCIPAL' },
{ name: 'Past due interest', code: 'PAST_DUE_INTEREST' },
{ name: 'Due penalty', code: 'DUE_PENALTY' },
{ name: 'Due fee', code: 'DUE_FEE' },
{ name: 'Due principal', code: 'DUE_PRINCIPAL' },
{ name: 'Due interest', code: 'DUE_INTEREST' },
{ name: 'In advance penalty', code: 'IN_ADVANCE_PENALTY' },
{ name: 'In advance fee', code: 'IN_ADVANCE_FEE' },
{ name: 'In advance principal', code: 'IN_ADVANCE_PRINCIPAL' },
{ name: 'In advance interest', code: 'IN_ADVANCE_INTEREST' }
];
}
export class FutureInstallmentAllocationRules {
public static options: PaymentCode[] = [
{ name: 'Next Installment', code: 'NEXT_INSTALLMENT' },
{ name: 'Last Installment', code: 'LAST_INSTALLMENT' },
{ name: 'Reamortization', code: 'REAMORTIZATION' }
];
}