Skip to content

Commit

Permalink
FINERACT-2107: Interest refund configuration for Progressive loan
Browse files Browse the repository at this point in the history
  • Loading branch information
adamsaghy authored and alberto-art3ch committed Jul 23, 2024
1 parent 95c9161 commit 7800d67
Show file tree
Hide file tree
Showing 27 changed files with 241 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,13 @@ <h3 class="mat-h3" fxFlexFill>{{ 'labels.inputs.Loan Schedule' | translate}}</h3
</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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -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'];
Expand Down Expand Up @@ -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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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;
Expand All @@ -37,6 +40,7 @@ export class CreateLoanProductComponent implements OnInit {
isAdvancedPaymentStrategy = false;
paymentAllocation: PaymentAllocation[] = [];
creditAllocation: PaymentAllocation[] = [];
supportedInterestRefundTypes: StringEnumOptionData[] = [];
advancedPaymentAllocations: AdvancedPaymentAllocation[] = [];
advancedCreditAllocations: AdvancedPaymentAllocation[] = [];

Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand All @@ -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)
Expand All @@ -147,4 +163,7 @@ export class CreateLoanProductComponent implements OnInit {
});
}

mapStringEnumOptionToIdList(incomingValues: StringEnumOptionData[]): string[] {
return incomingValues.map(v => v.id);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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;
Expand All @@ -41,6 +44,7 @@ export class EditLoanProductComponent implements OnInit {
creditAllocation: CreditAllocation[] = [];
advancedPaymentAllocations: AdvancedPaymentAllocation[] = [];
advancedCreditAllocations: AdvancedCreditAllocation[] = [];
supportedInterestRefundTypes: StringEnumOptionData[] = [];

/**
* @param {ActivatedRoute} route Activated Route.
Expand Down Expand Up @@ -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;
}
}

Expand All @@ -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);
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand All @@ -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)
Expand All @@ -175,4 +193,8 @@ export class EditLoanProductComponent implements OnInit {
});
}

mapStringEnumOptionToIdList(incomingValues: StringEnumOptionData[]): string[] {
return incomingValues.map(v => v.id);
}

}
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.margin-t {
margin-top: 1em;
}
Original file line number Diff line number Diff line change
@@ -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();
});
});
Original file line number Diff line number Diff line change
@@ -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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
[useDueForRepaymentsConfigurations]="loanProduct.useDueForRepaymentsConfigurations"
[paymentAllocations]="loanProduct.paymentAllocation"
[creditAllocations]="loanProduct.creditAllocation"
[supportedInterestRefundTypes]="loanProduct.supportedInterestRefundTypes"
>
</mifosx-loan-product-summary>

Expand Down
3 changes: 2 additions & 1 deletion src/app/products/loan-products/models/loan-product.model.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -125,6 +125,7 @@ export interface LoanProduct {
feeToIncomeAccountMappings?: ChargeToIncomeAccountMapping[];
penaltyToIncomeAccountMappings?: ChargeToIncomeAccountMapping[];
enableAccrualActivityPosting?: boolean;
supportedInterestRefundTypes?: StringEnumOptionData[];
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@
[useDueForRepaymentsConfigurations]="useDueForRepaymentsConfigurations"
[paymentAllocations]="loanProduct.paymentAllocation"
[creditAllocations]="loanProduct.creditAllocation"
[supportedInterestRefundTypes]="loanProduct.supportedInterestRefundTypes"
>
</mifosx-loan-product-summary>
2 changes: 2 additions & 0 deletions src/app/products/products.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -138,6 +139,7 @@ import { LoanProductSummaryComponent } from './loan-products/common/loan-product
LoanProductsComponent,
LoanProductDetailsStepComponent,
LoanProductCurrencyStepComponent,
LoanProductInterestRefundStepComponent,
LoanProductTermsStepComponent,
LoanProductSettingsStepComponent,
LoanProductPaymentStrategyStepComponent,
Expand Down
6 changes: 6 additions & 0 deletions src/app/shared/models/option-data.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,9 @@ export interface CodeName {
code: string;
name: string;
}

export interface StringEnumOptionData {
id: string;
value: string;
code: string;
}
Loading

0 comments on commit 7800d67

Please sign in to comment.