FINERACT-2107: Interest refund configuration for Progressive loan

This commit is contained in:
Adam Saghy
2024-07-23 10:40:00 -06:00
committed by Jose Alberto Hernandez
parent 95c9161770
commit 7800d6797d
27 changed files with 241 additions and 27 deletions
@@ -372,6 +372,13 @@
</span>
</div>
<div fxFlexFill *ngIf="isAdvancedPaymentAllocation && supportedInterestRefundTypes.length > 0">
<span fxFlex="47%">{{ 'labels.inputs.SUPPORTED INTEREST REFUND TYPES' | translate}}:</span>
<span fxFlex="53%">
{{mapHumanReadableValueStringEnumOptionDataList(supportedInterestRefundTypes)}}
</span>
</div>
<h3 class="mat-h3" fxFlexFill>{{ 'labels.inputs.Down Payments' | translate}}</h3>
<mat-divider [inset]="true"></mat-divider>
@@ -3,7 +3,7 @@ import { DelinquencyBucket, LoanProduct } from '../../models/loan-product.model'
import { AccountingMapping, Charge, ChargeToIncomeAccountMapping, GLAccount, PaymentChannelToFundSourceMapping, PaymentType, PaymentTypeOption } from '../../../../shared/models/general.model';
import { AdvancePaymentAllocationData, CreditAllocation, PaymentAllocation } from '../../loan-product-stepper/loan-product-payment-strategy-step/payment-allocation-model';
import { LoanProducts } from '../../loan-products';
import { CodeName, OptionData } from '../../../../shared/models/option-data.model';
import { CodeName, OptionData, StringEnumOptionData } from '../../../../shared/models/option-data.model';
import { Accounting } from 'app/core/utils/accounting';
@Component({
@@ -19,6 +19,7 @@ export class LoanProductSummaryComponent implements OnInit, OnChanges {
@Input() useDueForRepaymentsConfigurations: boolean;
@Input() paymentAllocations: PaymentAllocation | null;
@Input() creditAllocations: CreditAllocation | null;
@Input() supportedInterestRefundTypes: StringEnumOptionData[] | null;
variationsDisplayedColumns: string[] = ['valueConditionType', 'borrowerCycleNumber', 'minValue', 'defaultValue', 'maxValue'];
chargesDisplayedColumns: string[] = ['name', 'chargeCalculationType', 'amount', 'chargeTimeType'];
@@ -331,4 +332,8 @@ export class LoanProductSummaryComponent implements OnInit, OnChanges {
return this.accounting.getAccountRuleName(value.toUpperCase());
}
mapHumanReadableValueStringEnumOptionDataList(incomingParameter: StringEnumOptionData[]): string[] {
return incomingParameter.map(v => v.value);
}
}
@@ -51,6 +51,15 @@
</mat-step>
<mat-step *ngIf="isAdvancedPaymentStrategy" [stepControl]="loanProductInterestRefundForm">
<ng-template matStepLabel>{{'labels.inputs.INTEREST REFUND' | translate}}</ng-template>
<mifosx-loan-product-interest-refund-step
[loanProductsTemplate]="loanProductsTemplate"
(supportedInterestRefundTypes)="setSupportedInterestRefundTypes($event)"
>
</mifosx-loan-product-interest-refund-step>
</mat-step>
<mat-step *ngIf="isAdvancedPaymentStrategy">
<ng-template matStepLabel>{{'labels.inputs.PAYMENT ALLOCATION' | translate}}</ng-template>
@@ -9,12 +9,14 @@ import { LoanProductTermsStepComponent } from '../loan-product-stepper/loan-prod
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 { LoanProductAccountingStepComponent } from '../loan-product-stepper/loan-product-accounting-step/loan-product-accounting-step.component';
import { LoanProductInterestRefundStepComponent } from '../loan-product-stepper/loan-product-interest-refund-step/loan-product-interest-refund-step.component';
/** Custom Services */
import { ProductsService } from 'app/products/products.service';
import { LoanProducts } from '../loan-products';
import { AdvancedPaymentAllocation, AdvancedPaymentStrategy, PaymentAllocation } from '../loan-product-stepper/loan-product-payment-strategy-step/payment-allocation-model';
import { Accounting } from 'app/core/utils/accounting';
import { StringEnumOptionData } from '../../../shared/models/option-data.model';
@Component({
selector: 'mifosx-create-loan-product',
@@ -25,6 +27,7 @@ export class CreateLoanProductComponent implements OnInit {
@ViewChild(LoanProductDetailsStepComponent, { static: true }) loanProductDetailsStep: LoanProductDetailsStepComponent;
@ViewChild(LoanProductCurrencyStepComponent, { static: true }) loanProductCurrencyStep: LoanProductCurrencyStepComponent;
@ViewChild(LoanProductInterestRefundStepComponent, {static: true}) loanProductInterestRefundStep: LoanProductInterestRefundStepComponent;
@ViewChild(LoanProductTermsStepComponent, { static: true }) loanProductTermsStep: LoanProductTermsStepComponent;
@ViewChild(LoanProductSettingsStepComponent, { static: true }) loanProductSettingsStep: LoanProductSettingsStepComponent;
@ViewChild(LoanProductChargesStepComponent, { static: true }) loanProductChargesStep: LoanProductChargesStepComponent;
@@ -37,6 +40,7 @@ export class CreateLoanProductComponent implements OnInit {
isAdvancedPaymentStrategy = false;
paymentAllocation: PaymentAllocation[] = [];
creditAllocation: PaymentAllocation[] = [];
supportedInterestRefundTypes: StringEnumOptionData[] = [];
advancedPaymentAllocations: AdvancedPaymentAllocation[] = [];
advancedCreditAllocations: AdvancedPaymentAllocation[] = [];
@@ -79,6 +83,12 @@ export class CreateLoanProductComponent implements OnInit {
}
}
get loanProductInterestRefundForm() {
if (this.loanProductInterestRefundStep != null) {
return this.loanProductInterestRefundStep.loanProductInterestRefundForm;
}
}
get loanProductTermsForm() {
return this.loanProductTermsStep.loanProductTermsForm;
}
@@ -99,6 +109,10 @@ export class CreateLoanProductComponent implements OnInit {
this.creditAllocation = paymentAllocation;
}
setSupportedInterestRefundTypes(supportedInterestRefundTypes: StringEnumOptionData[]): void {
this.supportedInterestRefundTypes = supportedInterestRefundTypes;
}
get loanProductSettingsForm() {
return this.loanProductSettingsStep.loanProductSettingsForm;
}
@@ -129,6 +143,7 @@ export class CreateLoanProductComponent implements OnInit {
if (this.isAdvancedPaymentStrategy) {
loanProduct['paymentAllocation'] = this.paymentAllocation;
loanProduct['creditAllocation'] = this.creditAllocation;
loanProduct['supportedInterestRefundTypes'] = this.supportedInterestRefundTypes;
}
return loanProduct;
}
@@ -139,6 +154,7 @@ export class CreateLoanProductComponent implements OnInit {
loanProduct['dueDaysForRepaymentEvent'] = null;
loanProduct['overDueDaysForRepaymentEvent'] = null;
}
loanProduct['supportedInterestRefundTypes'] = this.mapStringEnumOptionToIdList(loanProduct['supportedInterestRefundTypes']);
delete loanProduct['useDueForRepaymentsConfigurations'];
this.productsService.createLoanProduct(loanProduct)
@@ -147,4 +163,7 @@ export class CreateLoanProductComponent implements OnInit {
});
}
mapStringEnumOptionToIdList(incomingValues: StringEnumOptionData[]): string[] {
return incomingValues.map(v => v.id);
}
}
@@ -51,6 +51,15 @@
</mat-step>
<mat-step *ngIf="isAdvancedPaymentStrategy" [stepControl]="loanProductInterestRefundForm" completed>
<ng-template matStepLabel>{{'labels.inputs.INTEREST REFUND' | translate}}</ng-template>
<mifosx-loan-product-interest-refund-step
[loanProductsTemplate]="loanProductAndTemplate"
(supportedInterestRefundTypes)="setSupportedInterestRefundTypes($event)"
>
</mifosx-loan-product-interest-refund-step>
</mat-step>
<mat-step *ngIf="isAdvancedPaymentStrategy" completed>
<ng-template matStepLabel>{{'labels.inputs.PAYMENT ALLOCATION' | translate}}</ng-template>
@@ -14,8 +14,10 @@ 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 { AdvancedCreditAllocation, AdvancedPaymentAllocation, AdvancedPaymentStrategy, CreditAllocation, PaymentAllocation, PaymentAllocationOrder, PaymentAllocationTransactionTypes } from '../loan-product-stepper/loan-product-payment-strategy-step/payment-allocation-model';
import { AdvancedCreditAllocation, AdvancedPaymentAllocation, AdvancedPaymentStrategy, CreditAllocation, PaymentAllocation } from '../loan-product-stepper/loan-product-payment-strategy-step/payment-allocation-model';
import { Accounting } from 'app/core/utils/accounting';
import { LoanProductInterestRefundStepComponent } from '../loan-product-stepper/loan-product-interest-refund-step/loan-product-interest-refund-step.component';
import { StringEnumOptionData } from '../../../shared/models/option-data.model';
@Component({
selector: 'mifosx-edit-loan-product',
@@ -26,6 +28,7 @@ export class EditLoanProductComponent implements OnInit {
@ViewChild(LoanProductDetailsStepComponent, { static: true }) loanProductDetailsStep: LoanProductDetailsStepComponent;
@ViewChild(LoanProductCurrencyStepComponent, { static: true }) loanProductCurrencyStep: LoanProductCurrencyStepComponent;
@ViewChild(LoanProductInterestRefundStepComponent, { static: true }) loanProductInterestRefundStep: LoanProductInterestRefundStepComponent;
@ViewChild(LoanProductTermsStepComponent, { static: true }) loanProductTermsStep: LoanProductTermsStepComponent;
@ViewChild(LoanProductSettingsStepComponent, { static: true }) loanProductSettingsStep: LoanProductSettingsStepComponent;
@ViewChild(LoanProductChargesStepComponent, { static: true }) loanProductChargesStep: LoanProductChargesStepComponent;
@@ -41,6 +44,7 @@ export class EditLoanProductComponent implements OnInit {
creditAllocation: CreditAllocation[] = [];
advancedPaymentAllocations: AdvancedPaymentAllocation[] = [];
advancedCreditAllocations: AdvancedCreditAllocation[] = [];
supportedInterestRefundTypes: StringEnumOptionData[] = [];
/**
* @param {ActivatedRoute} route Activated Route.
@@ -77,6 +81,7 @@ export class EditLoanProductComponent implements OnInit {
if (this.isAdvancedPaymentStrategy) {
this.paymentAllocation = this.loanProductAndTemplate.paymentAllocation;
this.creditAllocation = this.loanProductAndTemplate.creditAllocation;
this.supportedInterestRefundTypes = this.loanProductAndTemplate.supportedInterestRefundTypes;
}
}
@@ -96,6 +101,12 @@ export class EditLoanProductComponent implements OnInit {
return this.loanProductSettingsStep.loanProductSettingsForm;
}
get loanProductInterestRefundForm() {
if (this.loanProductInterestRefundStep != null) {
return this.loanProductInterestRefundStep.loanProductInterestRefundForm;
}
}
advancePaymentStrategy(value: string): void {
this.isAdvancedPaymentStrategy = LoanProducts.isAdvancedPaymentAllocationStrategy(value);
}
@@ -115,6 +126,10 @@ export class EditLoanProductComponent implements OnInit {
this.wasPaymentAllocationChanged = true;
}
setSupportedInterestRefundTypes(supportedInterestRefundTypes: StringEnumOptionData[]): void {
this.supportedInterestRefundTypes = supportedInterestRefundTypes;
}
paymentAllocationChanged(value: boolean): void {
this.wasPaymentAllocationChanged = value;
}
@@ -154,9 +169,11 @@ export class EditLoanProductComponent implements OnInit {
// Default empty array
loanProduct['paymentAllocation'] = [];
loanProduct['creditAllocation'] = [];
loanProduct['supportedInterestRefundTypes'] = [];
if (this.isAdvancedPaymentStrategy) {
loanProduct['paymentAllocation'] = this.paymentAllocation;
loanProduct['creditAllocation'] = this.creditAllocation;
loanProduct['supportedInterestRefundTypes'] = this.supportedInterestRefundTypes;
}
return loanProduct;
}
@@ -167,6 +184,7 @@ export class EditLoanProductComponent implements OnInit {
loanProduct['dueDaysForRepaymentEvent'] = null;
loanProduct['overDueDaysForRepaymentEvent'] = null;
}
loanProduct['supportedInterestRefundTypes'] = this.mapStringEnumOptionToIdList(loanProduct['supportedInterestRefundTypes']);
delete loanProduct['useDueForRepaymentsConfigurations'];
this.productsService.updateLoanProduct(this.loanProductAndTemplate.id, loanProduct)
@@ -175,4 +193,8 @@ export class EditLoanProductComponent implements OnInit {
});
}
mapStringEnumOptionToIdList(incomingValues: StringEnumOptionData[]): string[] {
return incomingValues.map(v => v.id);
}
}
@@ -0,0 +1,14 @@
<form [formGroup]="loanProductInterestRefundForm">
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
<mat-form-field fxFlex="48%">
<mat-label>{{'labels.inputs.SUPPORTED INTEREST REFUND TYPES' | translate}}</mat-label>
<mat-select multiple formControlName="supportedInterestRefundTypes" matTooltip="{{ 'tooltips.Refund transactions where interest refund will automatically be calculated' | translate}}">
<mat-option *ngFor="let supportedTransaction of supportedInterestRefundTypesOptions" [value]="supportedTransaction.id">
{{ supportedTransaction.value }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
</form>
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LoanProductInterestRefundStepComponent } from './loan-product-interest-refund-step.component';
describe('LoanProductInterestRefundStepComponent', () => {
let component: LoanProductInterestRefundStepComponent;
let fixture: ComponentFixture<LoanProductInterestRefundStepComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LoanProductInterestRefundStepComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoanProductInterestRefundStepComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,54 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { UntypedFormBuilder, UntypedFormGroup} from '@angular/forms';
import { StringEnumOptionData } from '../../../../shared/models/option-data.model';
@Component({
selector: 'mifosx-loan-product-interest-refund-step',
templateUrl: './loan-product-interest-refund-step.component.html',
styleUrls: ['./loan-product-interest-refund-step.component.scss']
})
export class LoanProductInterestRefundStepComponent implements OnInit {
@Input() loanProductsTemplate: any;
@Output() supportedInterestRefundTypes = new EventEmitter<StringEnumOptionData[]>();
loanProductInterestRefundForm: UntypedFormGroup;
supportedInterestRefundTypesOptions: StringEnumOptionData[];
constructor(private formBuilder: UntypedFormBuilder) {
this.createLoanProductInterestRefundForm();
this.setConditionalControls();
}
ngOnInit() {
this.supportedInterestRefundTypesOptions = this.loanProductsTemplate.supportedInterestRefundTypesOptions;
const values: StringEnumOptionData[] = this.loanProductsTemplate.supportedInterestRefundTypes;
const supportedInterestRefundTypes: string[] = this.mapStringEnumOptionToIdList(values);
this.loanProductInterestRefundForm.patchValue({
'supportedInterestRefundTypes': supportedInterestRefundTypes
});
this.supportedInterestRefundTypes.emit(values);
}
createLoanProductInterestRefundForm() {
this.loanProductInterestRefundForm = this.formBuilder.group({
'supportedInterestRefundTypes': ''
});
}
setConditionalControls() {
this.loanProductInterestRefundForm.get('supportedInterestRefundTypes').valueChanges
.subscribe(value => {
this.supportedInterestRefundTypes.emit(this.mapIdToStringEnumOptionList(value, this.loanProductsTemplate.supportedInterestRefundTypesOptions));
});
}
mapStringEnumOptionToIdList(incomingValues: StringEnumOptionData[]): string[] {
return incomingValues.map(v => v.id);
}
mapIdToStringEnumOptionList(incomingValues: string[], options: StringEnumOptionData[]): StringEnumOptionData[] {
return options.filter(v => incomingValues.includes(v.id));
}
}
@@ -6,6 +6,7 @@
[useDueForRepaymentsConfigurations]="loanProduct.useDueForRepaymentsConfigurations"
[paymentAllocations]="loanProduct.paymentAllocation"
[creditAllocations]="loanProduct.creditAllocation"
[supportedInterestRefundTypes]="loanProduct.supportedInterestRefundTypes"
>
</mifosx-loan-product-summary>
@@ -1,5 +1,5 @@
import { AccountingMapping, ChargeToIncomeAccountMapping, Currency, PaymentChannelToFundSourceMapping } from 'app/shared/models/general.model';
import { OptionData } from 'app/shared/models/option-data.model';
import { OptionData, StringEnumOptionData } from 'app/shared/models/option-data.model';
import { CreditAllocation, PaymentAllocation } from '../loan-product-stepper/loan-product-payment-strategy-step/payment-allocation-model';
export interface LoanProduct {
@@ -125,6 +125,7 @@ export interface LoanProduct {
feeToIncomeAccountMappings?: ChargeToIncomeAccountMapping[];
penaltyToIncomeAccountMappings?: ChargeToIncomeAccountMapping[];
enableAccrualActivityPosting?: boolean;
supportedInterestRefundTypes?: StringEnumOptionData[];
}
@@ -18,5 +18,6 @@
[useDueForRepaymentsConfigurations]="useDueForRepaymentsConfigurations"
[paymentAllocations]="loanProduct.paymentAllocation"
[creditAllocations]="loanProduct.creditAllocation"
[supportedInterestRefundTypes]="loanProduct.supportedInterestRefundTypes"
>
</mifosx-loan-product-summary>
+2
View File
@@ -12,6 +12,7 @@ import { ProductsComponent } from './products.component';
import { LoanProductsComponent } from './loan-products/loan-products.component';
import { LoanProductDetailsStepComponent } from './loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component';
import { LoanProductCurrencyStepComponent } from './loan-products/loan-product-stepper/loan-product-currency-step/loan-product-currency-step.component';
import { LoanProductInterestRefundStepComponent } from './loan-products/loan-product-stepper/loan-product-interest-refund-step/loan-product-interest-refund-step.component';
import { LoanProductTermsStepComponent } from './loan-products/loan-product-stepper/loan-product-terms-step/loan-product-terms-step.component';
import { LoanProductSettingsStepComponent } from './loan-products//loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component';
import { LoanProductChargesStepComponent } from './loan-products/loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component';
@@ -138,6 +139,7 @@ import { LoanProductSummaryComponent } from './loan-products/common/loan-product
LoanProductsComponent,
LoanProductDetailsStepComponent,
LoanProductCurrencyStepComponent,
LoanProductInterestRefundStepComponent,
LoanProductTermsStepComponent,
LoanProductSettingsStepComponent,
LoanProductPaymentStrategyStepComponent,
@@ -8,3 +8,9 @@ export interface CodeName {
code: string;
name: string;
}
export interface StringEnumOptionData {
id: string;
value: string;
code: string;
}