Advanced payment allocation configuration on Loan products (#1852)

Co-authored-by: Jose Alberto Hernandez <alberto@blue-daemon.local>
This commit is contained in:
José Alberto Hernández
2023-08-27 21:48:20 -06:00
committed by GitHub
co-authored by Jose Alberto Hernandez
parent 10e84d1acb
commit 2dab3b70ca
9 changed files with 250 additions and 70 deletions
@@ -53,17 +53,19 @@
<mifosx-loan-product-settings-step <mifosx-loan-product-settings-step
[loanProductsTemplate]="loanProductsTemplate" [loanProductsTemplate]="loanProductsTemplate"
[isLinkedToFloatingInterestRates]="loanProductTermsForm.get('isLinkedToFloatingInterestRates')" [isLinkedToFloatingInterestRates]="loanProductTermsForm.get('isLinkedToFloatingInterestRates')"
(advancePaymentStrategy)="advancePaymentStrategy($event)"
> >
</mifosx-loan-product-settings-step> </mifosx-loan-product-settings-step>
</mat-step> </mat-step>
<mat-step *ngIf="isAdvancePaymentStrategy"> <mat-step *ngIf="isAdvancedPaymentStrategy">
<ng-template matStepLabel>PAYMENT STRATEGY</ng-template> <ng-template matStepLabel>PAYMENT ALLOCATION</ng-template>
<mifosx-loan-product-payment-strategy-step <mifosx-loan-product-payment-strategy-step
[loanProductsTemplate]="loanProductsTemplate" [loanProductsTemplate]="loanProductsTemplate"
(paymentAllocation)="setPaymentAllocation($event)"
> >
</mifosx-loan-product-payment-strategy-step> </mifosx-loan-product-payment-strategy-step>
@@ -8,12 +8,12 @@ import { LoanProductCurrencyStepComponent } from '../loan-product-stepper/loan-p
import { LoanProductTermsStepComponent } from '../loan-product-stepper/loan-product-terms-step/loan-product-terms-step.component'; import { LoanProductTermsStepComponent } from '../loan-product-stepper/loan-product-terms-step/loan-product-terms-step.component';
import { LoanProductSettingsStepComponent } from '../loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component'; import { LoanProductSettingsStepComponent } from '../loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component';
import { LoanProductChargesStepComponent } from '../loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component'; import { LoanProductChargesStepComponent } from '../loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component';
import { LoanProductPaymentStrategyStepComponent } from '../loan-product-stepper/loan-product-payment-strategy-step/loan-product-payment-strategy-step.component';
import { LoanProductAccountingStepComponent } from '../loan-product-stepper/loan-product-accounting-step/loan-product-accounting-step.component'; import { LoanProductAccountingStepComponent } from '../loan-product-stepper/loan-product-accounting-step/loan-product-accounting-step.component';
/** Custom Services */ /** Custom Services */
import { ProductsService } from 'app/products/products.service'; import { ProductsService } from 'app/products/products.service';
import { LoanProducts } from '../loan-products'; import { LoanProducts } from '../loan-products';
import { PaymentAllocation } from '../loan-product-stepper/loan-product-payment-strategy-step/loan-product-payment-strategy-step.component';
@Component({ @Component({
selector: 'mifosx-create-loan-product', selector: 'mifosx-create-loan-product',
@@ -26,7 +26,6 @@ export class CreateLoanProductComponent implements OnInit {
@ViewChild(LoanProductCurrencyStepComponent, { static: true }) loanProductCurrencyStep: LoanProductCurrencyStepComponent; @ViewChild(LoanProductCurrencyStepComponent, { static: true }) loanProductCurrencyStep: LoanProductCurrencyStepComponent;
@ViewChild(LoanProductTermsStepComponent, { static: true }) loanProductTermsStep: LoanProductTermsStepComponent; @ViewChild(LoanProductTermsStepComponent, { static: true }) loanProductTermsStep: LoanProductTermsStepComponent;
@ViewChild(LoanProductSettingsStepComponent, { static: true }) loanProductSettingsStep: LoanProductSettingsStepComponent; @ViewChild(LoanProductSettingsStepComponent, { static: true }) loanProductSettingsStep: LoanProductSettingsStepComponent;
@ViewChild(LoanProductPaymentStrategyStepComponent, { static: true }) loanProductPaymentStrategyStep: LoanProductPaymentStrategyStepComponent;
@ViewChild(LoanProductChargesStepComponent, { static: true }) loanProductChargesStep: LoanProductChargesStepComponent; @ViewChild(LoanProductChargesStepComponent, { static: true }) loanProductChargesStep: LoanProductChargesStepComponent;
@ViewChild(LoanProductAccountingStepComponent, { static: true }) loanProductAccountingStep: LoanProductAccountingStepComponent; @ViewChild(LoanProductAccountingStepComponent, { static: true }) loanProductAccountingStep: LoanProductAccountingStepComponent;
@@ -34,7 +33,8 @@ export class CreateLoanProductComponent implements OnInit {
accountingRuleData = ['None', 'Cash', 'Accrual (periodic)', 'Accrual (upfront)']; accountingRuleData = ['None', 'Cash', 'Accrual (periodic)', 'Accrual (upfront)'];
itemsByDefault: any[] = []; itemsByDefault: any[] = [];
isAdvancePaymentStrategy = false; isAdvancedPaymentStrategy = false;
paymentAllocation: PaymentAllocation[] = [];
/** /**
* @param {ActivatedRoute} route Activated Route. * @param {ActivatedRoute} route Activated Route.
@@ -75,6 +75,14 @@ export class CreateLoanProductComponent implements OnInit {
return this.loanProductTermsStep.loanProductTermsForm; return this.loanProductTermsStep.loanProductTermsForm;
} }
advancePaymentStrategy(value: string) {
this.isAdvancedPaymentStrategy = (value === 'advanced-payment-allocation-strategy');
}
setPaymentAllocation(paymentAllocation: PaymentAllocation[]) {
this.paymentAllocation = paymentAllocation;
}
get loanProductSettingsForm() { get loanProductSettingsForm() {
return this.loanProductSettingsStep.loanProductSettingsForm; return this.loanProductSettingsStep.loanProductSettingsForm;
} }
@@ -83,12 +91,7 @@ export class CreateLoanProductComponent implements OnInit {
return this.loanProductAccountingStep.loanProductAccountingForm; return this.loanProductAccountingStep.loanProductAccountingForm;
} }
get loanProductPaymentStrategyForm() {
return this.loanProductPaymentStrategyStep.loanProductPaymentStrategyForm;
}
get loanProductFormValid() { get loanProductFormValid() {
this.isAdvancePaymentStrategy = (this.loanProductSettingsStep.loanProductSettings.transactionProcessingStrategyCode === 'advanced-payment-allocation-strategy');
return ( return (
this.loanProductDetailsForm.valid && this.loanProductDetailsForm.valid &&
this.loanProductCurrencyForm.valid && this.loanProductCurrencyForm.valid &&
@@ -99,7 +102,7 @@ export class CreateLoanProductComponent implements OnInit {
} }
get loanProduct() { get loanProduct() {
return { const loanProduct = {
...this.loanProductDetailsStep.loanProductDetails, ...this.loanProductDetailsStep.loanProductDetails,
...this.loanProductCurrencyStep.loanProductCurrency, ...this.loanProductCurrencyStep.loanProductCurrency,
...this.loanProductTermsStep.loanProductTerms, ...this.loanProductTermsStep.loanProductTerms,
@@ -107,6 +110,10 @@ export class CreateLoanProductComponent implements OnInit {
...this.loanProductChargesStep.loanProductCharges, ...this.loanProductChargesStep.loanProductCharges,
...this.loanProductAccountingStep.loanProductAccounting ...this.loanProductAccountingStep.loanProductAccounting
}; };
if (this.isAdvancedPaymentStrategy) {
loanProduct['paymentAllocation'] = this.paymentAllocation;
}
return loanProduct;
} }
submit() { submit() {
@@ -46,18 +46,32 @@
</mat-step> </mat-step>
<mat-step [stepControl]="loanProductSettingsForm" completed> <mat-step [stepControl]="loanProductSettingsForm">
<ng-template matStepLabel>SETTINGS</ng-template> <ng-template matStepLabel>SETTINGS</ng-template>
<mifosx-loan-product-settings-step <mifosx-loan-product-settings-step
[loanProductsTemplate]="loanProductAndTemplate" [loanProductsTemplate]="loanProductAndTemplate"
[isLinkedToFloatingInterestRates]="loanProductTermsForm.get('isLinkedToFloatingInterestRates')" [isLinkedToFloatingInterestRates]="loanProductTermsForm.get('isLinkedToFloatingInterestRates')"
(advancePaymentStrategy)="advancePaymentStrategy($event)"
> >
</mifosx-loan-product-settings-step> </mifosx-loan-product-settings-step>
</mat-step> </mat-step>
<mat-step *ngIf="isAdvancedPaymentStrategy">
<ng-template matStepLabel>PAYMENT ALLOCATION</ng-template>
<mifosx-loan-product-payment-strategy-step
[loanProductsTemplate]="loanProductsTemplate"
(paymentAllocation)="setPaymentAllocation($event)"
(paymentAllocationChange)="paymentAllocationChanged($event)"
>
</mifosx-loan-product-payment-strategy-step>
</mat-step>
<mat-step completed> <mat-step completed>
<ng-template matStepLabel>CHARGES</ng-template> <ng-template matStepLabel>CHARGES</ng-template>
@@ -14,6 +14,7 @@ import { LoanProductAccountingStepComponent } from '../loan-product-stepper/loan
import { ProductsService } from 'app/products/products.service'; import { ProductsService } from 'app/products/products.service';
import { GlobalConfiguration } from 'app/system/configurations/global-configurations-tab/configuration.model'; import { GlobalConfiguration } from 'app/system/configurations/global-configurations-tab/configuration.model';
import { LoanProducts } from '../loan-products'; import { LoanProducts } from '../loan-products';
import { PaymentAllocation } from '../loan-product-stepper/loan-product-payment-strategy-step/loan-product-payment-strategy-step.component';
@Component({ @Component({
selector: 'mifosx-edit-loan-product', selector: 'mifosx-edit-loan-product',
@@ -33,6 +34,10 @@ export class EditLoanProductComponent implements OnInit {
accountingRuleData = ['None', 'Cash', 'Accrual (periodic)', 'Accrual (upfront)']; accountingRuleData = ['None', 'Cash', 'Accrual (periodic)', 'Accrual (upfront)'];
itemsByDefault: GlobalConfiguration[] = []; itemsByDefault: GlobalConfiguration[] = [];
isAdvancedPaymentStrategy = false;
wasPaymentAllocationChanged = false;
paymentAllocation: PaymentAllocation[] = [];
/** /**
* @param {ActivatedRoute} route Activated Route. * @param {ActivatedRoute} route Activated Route.
* @param {ProductsService} productsService Product Service. * @param {ProductsService} productsService Product Service.
@@ -75,6 +80,18 @@ export class EditLoanProductComponent implements OnInit {
return this.loanProductSettingsStep.loanProductSettingsForm; return this.loanProductSettingsStep.loanProductSettingsForm;
} }
advancePaymentStrategy(value: string): void {
this.isAdvancedPaymentStrategy = (value === 'advanced-payment-allocation-strategy');
}
setPaymentAllocation(paymentAllocation: PaymentAllocation[]): void {
this.paymentAllocation = paymentAllocation;
}
paymentAllocationChanged(value: boolean): void {
this.wasPaymentAllocationChanged = value;
}
get loanProductAccountingForm() { get loanProductAccountingForm() {
return this.loanProductAccountingStep.loanProductAccountingForm; return this.loanProductAccountingStep.loanProductAccountingForm;
} }
@@ -92,13 +109,14 @@ export class EditLoanProductComponent implements OnInit {
!this.loanProductTermsForm.pristine || !this.loanProductTermsForm.pristine ||
!this.loanProductSettingsForm.pristine || !this.loanProductSettingsForm.pristine ||
!this.loanProductChargesStep.pristine || !this.loanProductChargesStep.pristine ||
!this.loanProductAccountingForm.pristine !this.loanProductAccountingForm.pristine ||
this.wasPaymentAllocationChanged
) )
); );
} }
get loanProduct() { get loanProduct() {
return { const loanProduct = {
...this.loanProductDetailsStep.loanProductDetails, ...this.loanProductDetailsStep.loanProductDetails,
...this.loanProductCurrencyStep.loanProductCurrency, ...this.loanProductCurrencyStep.loanProductCurrency,
...this.loanProductTermsStep.loanProductTerms, ...this.loanProductTermsStep.loanProductTerms,
@@ -106,6 +124,10 @@ export class EditLoanProductComponent implements OnInit {
...this.loanProductChargesStep.loanProductCharges, ...this.loanProductChargesStep.loanProductCharges,
...this.loanProductAccountingStep.loanProductAccounting ...this.loanProductAccountingStep.loanProductAccounting
}; };
if (this.isAdvancedPaymentStrategy) {
loanProduct['paymentAllocation'] = this.paymentAllocation;
}
return loanProduct;
} }
submit() { submit() {
@@ -1,43 +1,27 @@
<div class="container space-top" fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column"> <div class="container space-top" fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
<mat-form-field fxFlex="31%"> <mat-form-field fxFlex="33%">
<mat-label>Transaction Type</mat-label> <mat-label>Future Installment Allocation Rule</mat-label>
<mat-select [formControl]="transactionType"> <mat-select [formControl]="futureInstallmentAllocationRule">
<mat-option *ngFor="let job of transactionTypeOptions" [value]="transactionType"> <mat-option *ngFor="let installmentAllocation of installmentAllocationOptions" [value]="installmentAllocation.code">
{{ transactionType }} {{ installmentAllocation.name | translate }}
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<div class="action-button" fxLayoutGap="25px">
<button mat-raised-button color="primary" (click)="addTransactionType()">
<fa-icon icon="plus" class="m-r-10"></fa-icon>Add Transaction Type
</button>
</div>
<div class="mat-elevation-z8 container"> <div class="mat-elevation-z8 container">
<table mat-table [dataSource]="transactionTypesData" #table cdkDropList [cdkDropListData]="transactionTypesData" <table mat-table [dataSource]="transactionTypesData" #table cdkDropList [cdkDropListData]="transactionTypesData"
(cdkDropListDropped)="dropTable($event)"> (cdkDropListDropped)="dropTable($event)">
<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="transactionName"> <ng-container matColumnDef="transactionName">
<th mat-header-cell *matHeaderCellDef> Transaction Type </th> <th mat-header-cell *matHeaderCellDef> Transaction Type </th>
<td mat-cell *matCellDef="let jobStep"> {{ jobStep.stepName }} </td> <td mat-cell *matCellDef="let transactionType"> {{ transactionType.name }} </td>
</ng-container>
<ng-container matColumnDef="transactionOrder">
<th mat-header-cell *matHeaderCellDef> Order </th>
<td mat-cell *matCellDef="let jobStep"> {{ jobStep.order }} </td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let row; let rowIndex = index">
<button type="button" mat-icon-button color="warn" (click)="removeTransactionType(rowIndex)" matTooltip="Delete"
matTooltipPosition="left">
<fa-icon icon="trash"></fa-icon>
</button>
</td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
@@ -1,10 +1,26 @@
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
import { Component, Input, OnInit, ViewChild } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { FormControl, Validators } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { MatTable } from '@angular/material/table'; import { MatTable } from '@angular/material/table';
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component'; 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;
}
@Component({ @Component({
selector: 'mifosx-loan-product-payment-strategy-step', selector: 'mifosx-loan-product-payment-strategy-step',
templateUrl: './loan-product-payment-strategy-step.component.html', templateUrl: './loan-product-payment-strategy-step.component.html',
@@ -13,61 +29,116 @@ import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.co
export class LoanProductPaymentStrategyStepComponent implements OnInit { export class LoanProductPaymentStrategyStepComponent implements OnInit {
@Input() loanProductsTemplate: any; @Input() loanProductsTemplate: any;
loanProductPaymentStrategyForm: FormGroup; @Output() paymentAllocation = new EventEmitter<PaymentAllocation[]>();
@Output() paymentAllocationChange = new EventEmitter<boolean>();
transactionTypesData: any = []; transactionTypesData: PaymentCode[] = [];
transactionTypeOptions: any = []; transactionTypeOptions: PaymentCode[] = [];
installmentAllocationOptions: PaymentCode[] = [];
transactionType = new FormControl('', Validators.required); transactionType = new FormControl('');
futureInstallmentAllocationRule = new FormControl('', Validators.required);
stepOrderHasChanged = false;
@ViewChild('table') table: MatTable<any>; @ViewChild('table') table: MatTable<any>;
/** Columns to be displayed in the table. */ /** Columns to be displayed in the table. */
displayedColumns: string[] = ['transactionName', 'transactionOrder', 'actions']; displayedColumns: string[] = ['order', 'transactionName'];
constructor(private formBuilder: FormBuilder, constructor(private dialog: MatDialog) { }
private dialog: MatDialog) {
this.createForm();
}
ngOnInit(): void { ngOnInit(): void {
this.transactionTypesData = []; this.transactionTypesData = [];
this.setOptions();
this.sendPaymentAllocation();
} }
createForm(): void { setOptions(): void {
this.loanProductPaymentStrategyForm = this.formBuilder.group({ this.transactionTypeOptions = [
'transactionType': ['', Validators.required], { 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 { addTransactionType(): void {
console.log(this.transactionType.value); this.transactionTypeOptions.forEach((txType: PaymentCode) => {
this.transactionTypesData.append(this.transactionType.value); 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) { removeTransactionType(index: number) {
const transactionTypeName = this.transactionTypesData[index].name;
const dialogRef = this.dialog.open(DeleteDialogComponent, { const dialogRef = this.dialog.open(DeleteDialogComponent, {
data: { deleteContext: `this` } data: { deleteContext: transactionTypeName }
}); });
dialogRef.afterClosed().subscribe((response: any) => { dialogRef.afterClosed().subscribe((response: any) => {
if (response.delete) { if (response.delete) {
this.transactionTypesData.splice(index, 1); this.transactionTypesData.splice(index, 1);
this.transactionTypesData = this.transactionTypesData.concat([]); this.transactionTypesData = this.transactionTypesData.concat([]);
this.transactionTypesData = [...this.transactionTypesData]; this.transactionTypesData = [...this.transactionTypesData];
this.stepOrderHasChanged = true; this.sendPaymentAllocation();
} }
}); });
} }
dropTable(event: CdkDragDrop<any[]>) { dropTable(event: CdkDragDrop<any[]>) {
const prevIndex = this.transactionTypesData.findIndex((d: any) => d === event.item.data); const prevIndex = this.transactionTypesData.findIndex((d: any) => d === event.item.data);
moveItemInArray(this.transactionTypesData, prevIndex, event.currentIndex); moveItemInArray(this.transactionTypesData, prevIndex, event.currentIndex);
this.transactionTypesData = [...this.transactionTypesData]; this.transactionTypesData = [...this.transactionTypesData];
this.table.renderRows(); this.table.renderRows();
this.stepOrderHasChanged = true; this.sendPaymentAllocation();
this.paymentAllocationChange.emit(true);
} }
sendPaymentAllocation(): void {
const paymentAllocationOrder: PaymentAllocationOrder[] = [];
this.transactionTypesData.forEach((txType: PaymentCode, index: number) => {
paymentAllocationOrder.push({ 'paymentAllocationRule': txType.code, order: (index + 1) });
});
const paymentAllocation: PaymentAllocation[] = [];
paymentAllocation.push({
'futureInstallmentAllocationRule': this.futureInstallmentAllocationRule.value,
'transactionType': 'DEFAULT',
'paymentAllocationOrder': paymentAllocationOrder
});
this.paymentAllocation.emit(paymentAllocation);
}
} }
@@ -328,6 +328,40 @@
find:loanProductsTemplate.transactionProcessingStrategyOptions:'code':'name' }}</span> find:loanProductsTemplate.transactionProcessingStrategyOptions:'code':'name' }}</span>
</div> </div>
<div fxFlexFill *ngIf="loanProduct.paymentAllocation?.length">
<div fxFlexFill *ngFor="let paymentAllocation of loanProduct.paymentAllocation">
<div fxFlexFill>
<span fxFlex="40%">Transaction Type:</span>
<span fxFlex="60%">{{ paymentAllocation.transactionType }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Future Installment Allocation:</span>
<span fxFlex="60%">{{ paymentAllocation.futureInstallmentAllocationRule }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Payment Allocation Order</span>
<span fxFlex="60%">
<table>
<thead>
<tr>
<th>Order</th>
<th>Payment Allocation Rule</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let paymentAllocation of paymentAllocation.paymentAllocationOrder; let idx = index;">
<td>{{ (idx + 1) }}</td>
<td>{{ paymentAllocation.paymentAllocationRule }}</td>
</tr>
</tbody>
</table>
</span>
</div>
</div>
</div>
<div fxFlexFill *ngIf="loanProduct.graceOnPrincipalPayment"> <div fxFlexFill *ngIf="loanProduct.graceOnPrincipalPayment">
<span fxFlex="40%">Grace on principal payment:</span> <span fxFlex="40%">Grace on principal payment:</span>
<span fxFlex="60%">{{ loanProduct.graceOnPrincipalPayment }}</span> <span fxFlex="60%">{{ loanProduct.graceOnPrincipalPayment }}</span>
@@ -1,4 +1,4 @@
import { Component, OnInit, Input } from '@angular/core'; import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms'; import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms';
import { LoanProducts } from '../../loan-products'; import { LoanProducts } from '../../loan-products';
import { rangeValidator } from 'app/shared/validators/percentage.validator'; import { rangeValidator } from 'app/shared/validators/percentage.validator';
@@ -12,6 +12,7 @@ export class LoanProductSettingsStepComponent implements OnInit {
@Input() loanProductsTemplate: any; @Input() loanProductsTemplate: any;
@Input() isLinkedToFloatingInterestRates: FormControl; @Input() isLinkedToFloatingInterestRates: FormControl;
@Output() advancePaymentStrategy = new EventEmitter<string>();
loanProductSettingsForm: FormGroup; loanProductSettingsForm: FormGroup;
@@ -353,6 +354,11 @@ export class LoanProductSettingsStepComponent implements OnInit {
} }
}); });
this.loanProductSettingsForm.get('transactionProcessingStrategyCode').valueChanges
.subscribe((transactionProcessingStrategyCode: string) => {
this.advancePaymentStrategy.emit(transactionProcessingStrategyCode);
});
this.loanProductSettingsForm.get('allowAttributeConfiguration').valueChanges this.loanProductSettingsForm.get('allowAttributeConfiguration').valueChanges
.subscribe((allowAttributeConfiguration: any) => { .subscribe((allowAttributeConfiguration: any) => {
if (allowAttributeConfiguration) { if (allowAttributeConfiguration) {
@@ -88,9 +88,11 @@
<div fxFlexFill *ngIf="loanProduct.allowApprovedDisbursedAmountsOverApplied"> <div fxFlexFill *ngIf="loanProduct.allowApprovedDisbursedAmountsOverApplied">
<span fxFlex="40%">Over Applied Amount:</span> <span fxFlex="40%">Over Applied Amount:</span>
<span fxFlex="60%" *ngIf="loanProduct.overAppliedCalculationType === 'percentage'">{{ loanProduct.overAppliedNumber | formatNumber <span fxFlex="60%" *ngIf="loanProduct.overAppliedCalculationType === 'percentage'">{{ loanProduct.overAppliedNumber
| formatNumber
}} %</span> }} %</span>
<span fxFlex="60%" *ngIf="loanProduct.overAppliedCalculationType === 'flat'">{{ loanProduct.overAppliedNumber | formatNumber }} {{ <span fxFlex="60%" *ngIf="loanProduct.overAppliedCalculationType === 'flat'">{{ loanProduct.overAppliedNumber |
formatNumber }} {{
loanProduct.currencyCode }}</span> loanProduct.currencyCode }}</span>
</div> </div>
@@ -329,6 +331,40 @@
<span fxFlex="60%">{{ loanProduct.transactionProcessingStrategyName }}</span> <span fxFlex="60%">{{ loanProduct.transactionProcessingStrategyName }}</span>
</div> </div>
<div fxFlexFill *ngIf="loanProduct.paymentAllocation?.length">
<div fxFlexFill *ngFor="let paymentAllocation of loanProduct.paymentAllocation">
<div fxFlexFill>
<span fxFlex="40%">Transaction Type:</span>
<span fxFlex="60%">{{ paymentAllocation.transactionType }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Future Installment Allocation:</span>
<span fxFlex="60%">{{ paymentAllocation.futureInstallmentAllocationRule }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Payment Allocation Order</span>
<span fxFlex="60%">
<table>
<thead>
<tr>
<th>Order</th>
<th>Payment Allocation Rule</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let paymentAllocation of paymentAllocation.paymentAllocationOrder; let idx = index;">
<td>{{ (idx + 1) }}</td>
<td>{{ paymentAllocation.paymentAllocationRule }}</td>
</tr>
</tbody>
</table>
</span>
</div>
</div>
</div>
<div fxFlexFill *ngIf="loanProduct.graceOnPrincipalPayment"> <div fxFlexFill *ngIf="loanProduct.graceOnPrincipalPayment">
<span fxFlex="40%">Grace on principal payment:</span> <span fxFlex="40%">Grace on principal payment:</span>
<span fxFlex="60%">{{ loanProduct.graceOnPrincipalPayment }}</span> <span fxFlex="60%">{{ loanProduct.graceOnPrincipalPayment }}</span>
@@ -709,15 +745,18 @@
<span fxFlex="40%">Income from Recovery Repayments:</span> <span fxFlex="40%">Income from Recovery Repayments:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.incomeFromRecoveryAccount.glCode}}) {{ <span fxFlex="60%">({{loanProduct.accountingMappings.incomeFromRecoveryAccount.glCode}}) {{
loanProduct.accountingMappings.incomeFromRecoveryAccount.name }}</span> loanProduct.accountingMappings.incomeFromRecoveryAccount.name }}</span>
<span *ngIf="loanProduct.accountingMappings.incomeFromChargeOffInterestAccount" fxFlex="40%">Income from ChargeOff Interest:</span> <span *ngIf="loanProduct.accountingMappings.incomeFromChargeOffInterestAccount" fxFlex="40%">Income from ChargeOff
Interest:</span>
<span *ngIf="loanProduct.accountingMappings.incomeFromChargeOffInterestAccount" fxFlex="60%"> <span *ngIf="loanProduct.accountingMappings.incomeFromChargeOffInterestAccount" fxFlex="60%">
({{loanProduct.accountingMappings.incomeFromChargeOffInterestAccount.glCode}}) {{ ({{loanProduct.accountingMappings.incomeFromChargeOffInterestAccount.glCode}}) {{
loanProduct.accountingMappings.incomeFromChargeOffInterestAccount.name }}</span> loanProduct.accountingMappings.incomeFromChargeOffInterestAccount.name }}</span>
<span fxFlex="40%" *ngIf="loanProduct.accountingMappings.incomeFromChargeOffFeesAccount">Income from ChargeOff Fees:</span> <span fxFlex="40%" *ngIf="loanProduct.accountingMappings.incomeFromChargeOffFeesAccount">Income from ChargeOff
Fees:</span>
<span fxFlex="60%" *ngIf="loanProduct.accountingMappings.incomeFromChargeOffFeesAccount"> <span fxFlex="60%" *ngIf="loanProduct.accountingMappings.incomeFromChargeOffFeesAccount">
({{loanProduct.accountingMappings.incomeFromChargeOffFeesAccount.glCode}}) {{ ({{loanProduct.accountingMappings.incomeFromChargeOffFeesAccount.glCode}}) {{
loanProduct.accountingMappings.incomeFromChargeOffFeesAccount.name }}</span> loanProduct.accountingMappings.incomeFromChargeOffFeesAccount.name }}</span>
<span fxFlex="40%" *ngIf="loanProduct.accountingMappings.incomeFromChargeOffPenaltyAccount">Income from ChargeOff Penalty:</span> <span fxFlex="40%" *ngIf="loanProduct.accountingMappings.incomeFromChargeOffPenaltyAccount">Income from ChargeOff
Penalty:</span>
<span fxFlex="60%" *ngIf="loanProduct.accountingMappings.incomeFromChargeOffPenaltyAccount"> <span fxFlex="60%" *ngIf="loanProduct.accountingMappings.incomeFromChargeOffPenaltyAccount">
({{loanProduct.accountingMappings.incomeFromChargeOffPenaltyAccount.glCode}}) {{ ({{loanProduct.accountingMappings.incomeFromChargeOffPenaltyAccount.glCode}}) {{
loanProduct.accountingMappings.incomeFromChargeOffPenaltyAccount.name }}</span> loanProduct.accountingMappings.incomeFromChargeOffPenaltyAccount.name }}</span>
@@ -736,7 +775,8 @@
<span *ngIf="loanProduct.accountingMappings.chargeOffExpenseAccount" fxFlex="60%"> <span *ngIf="loanProduct.accountingMappings.chargeOffExpenseAccount" fxFlex="60%">
({{loanProduct.accountingMappings.chargeOffExpenseAccount.glCode}}) {{ ({{loanProduct.accountingMappings.chargeOffExpenseAccount.glCode}}) {{
loanProduct.accountingMappings.chargeOffExpenseAccount.name }}</span> loanProduct.accountingMappings.chargeOffExpenseAccount.name }}</span>
<span *ngIf="loanProduct.accountingMappings.chargeOffFraudExpenseAccount" fxFlex="40%">ChargeOff Fraud Expense:</span> <span *ngIf="loanProduct.accountingMappings.chargeOffFraudExpenseAccount" fxFlex="40%">ChargeOff Fraud
Expense:</span>
<span *ngIf="loanProduct.accountingMappings.chargeOffFraudExpenseAccount" fxFlex="60%"> <span *ngIf="loanProduct.accountingMappings.chargeOffFraudExpenseAccount" fxFlex="60%">
({{loanProduct.accountingMappings.chargeOffFraudExpenseAccount.glCode}}) {{ ({{loanProduct.accountingMappings.chargeOffFraudExpenseAccount.glCode}}) {{
loanProduct.accountingMappings.chargeOffFraudExpenseAccount.name }}</span> loanProduct.accountingMappings.chargeOffFraudExpenseAccount.name }}</span>