Down payment configuration for Loan Products (#1836)

Co-authored-by: Jose Alberto Hernandez <alberto@blue-daemon.local>
This commit is contained in:
José Alberto Hernández
2023-08-07 16:41:50 -06:00
committed by GitHub
co-authored by Jose Alberto Hernandez
parent a9b8543120
commit 861cf7a000
11 changed files with 90 additions and 43 deletions
@@ -9,9 +9,7 @@ import { extract } from '../core/i18n/i18n.service';
/** Custom Components */
import { IndividualCollectionSheetComponent } from './individual-collection-sheet/individual-collection-sheet.component';
/** Custom Resolvers */
import { GetOfficesResolver } from './individual-collection-sheet/get-offices.resolver';
import { OfficesResolver } from 'app/organization/offices/common-resolvers/offices.resolver';
const routes: Routes = [
Route.withShell([
@@ -23,7 +21,7 @@ const routes: Routes = [
data: { title: extract('Individual Collection Sheet'), breadcrumb: 'Individual Collection Sheet', routeParamBreadcrumb: false },
component: IndividualCollectionSheetComponent,
resolve: {
officesData: GetOfficesResolver
officesData: OfficesResolver
}
},
]
@@ -33,7 +31,7 @@ const routes: Routes = [
@NgModule({
imports: [RouterModule.forChild(routes)],
providers: [GetOfficesResolver],
providers: [],
exports: [RouterModule]
})
export class CollectionsRoutingModule { }
@@ -1,30 +0,0 @@
/** Angular Imports */
import { Injectable } from '@angular/core';
import { Resolve } from '@angular/router';
/** rxjs Imports */
import { Observable } from 'rxjs';
/** Custom Services */
import { CollectionsService } from '../collections.service';
/**
* Returns all the offices data.
*/
@Injectable()
export class GetOfficesResolver implements Resolve<Object> {
/**
* @param {CollectionsService} CollectionsService Collections service.
*/
constructor(private collectionsService: CollectionsService) { }
/**
* Returns the Collection Office Data.
* @returns {Observable<any>}
*/
resolve(): Observable<any> {
return this.collectionsService.getOffices();
}
}
@@ -50,7 +50,7 @@
<mat-card-actions fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px">
<button type="button" mat-raised-button [routerLink]="['../']">Cancel</button>
<button mat-raised-button color="primary" [disabled]="!collectionSheetForm.valid">
<fa-icon icon="search"></fa-icon>&nbsp; Collection Sheet
<fa-icon icon="search" class="m-r-10"></fa-icon> Collection Sheet
</button>
</mat-card-actions>
@@ -27,4 +27,4 @@
<mifosx-edit-repayment-schedule *ngIf="actions['Edit Repayment Schedule']"></mifosx-edit-repayment-schedule>
<mifosx-charge-off [dataObject]="actionButtonData" *ngIf="actions['Charge-Off']"></mifosx-charge-off>
<mifosx-undo-command [actionName]="actionName" *ngIf="actions['Undo Charge-Off']"></mifosx-undo-command>
<mifosx-asset-transfer-loan *ngIf="actions['Sale Loan'] || actions['Buy Back Loan']"></mifosx-asset-transfer-loan>
<mifosx-asset-transfer-loan *ngIf="actions['Sell Loan'] || actions['Buy Back Loan']"></mifosx-asset-transfer-loan>
@@ -43,7 +43,7 @@ export class LoanAccountActionsComponent {
'Edit Repayment Schedule': boolean,
'Charge-Off': boolean,
'Undo Charge-Off': boolean,
'Sale Loan': boolean,
'Sell Loan': boolean,
'Buy Back Loan': boolean
} = {
'Close': false,
@@ -76,7 +76,7 @@ export class LoanAccountActionsComponent {
'Edit Repayment Schedule': false,
'Charge-Off': false,
'Undo Charge-Off': false,
'Sale Loan': false,
'Sell Loan': false,
'Buy Back Loan': false
};
@@ -191,7 +191,7 @@ export class LoansAccountButtonConfiguration {
taskPermissionName: 'RECOVERGUARANTEES_LOAN',
},
{
name: 'Sale Loan',
name: 'Sell Loan',
taskPermissionName: 'SALE_LOAN',
}
];
@@ -473,6 +473,20 @@
<span fxFlex="60%">{{ loanProduct.disallowExpectedDisbursements ? 'Yes' : 'No' }}</span>
</div>
<h3 class="mat-h3" fxFlexFill>Down Payments</h3>
<mat-divider [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Enable Down Payments:</span>
<span fxFlex="60%">{{ loanProduct.enableDownPayment ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="loanProduct.enableDownPayment" fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Disbursed Amount Percentage Down Payment (%):</span>
<span fxFlex="60%">{{ loanProduct.disbursedAmountPercentageForDownPayment }} %</span>
</div>
<h3 class="mat-h3" fxFlexFill>Event Settings</h3>
<div fxFlexFill>
@@ -399,7 +399,7 @@
<mat-form-field fxFlex="48%">
<mat-label>Maximum Tranche count</mat-label>
<input type="number" matInput matTooltip="The maximum number of disbursements allowed for a loan account." formControlName="maxTrancheCount" required>
<mat-error>
<mat-error *ngIf="loanProductSettingsForm.controls.maxTrancheCount.hasError('required')">
Maximum Tranche count is <strong>required</strong>
</mat-error>
</mat-form-field>
@@ -416,6 +416,28 @@
<mat-divider fxFlex="98%"></mat-divider>
<h3 fxFlex="23%" class="mat-h3">Down Payment</h3>
<mat-checkbox fxFlex="73%" labelPosition="before" formControlName="enableDownPayment" matTooltip="Leave this checkbox checked if the loan has Down Payment, A Down Payment is
a sum a buyer pays upfront when purchasing a good. It represents a percentage of the total purchase price, and the balance is usually financed" class="margin-b">
Enable Down Payment
</mat-checkbox>
<div *ngIf="loanProductSettingsForm.value.enableDownPayment" fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
<mat-form-field fxFlex="48%">
<mat-label>Disbursed Amount Percentage Down Payment (%)</mat-label>
<input type="number" matInput matTooltip="The percentage of disbursements amount for Down Payment" formControlName="disbursedAmountPercentageForDownPayment" required [min]="0" [max]="100">
<mat-error *ngIf="loanProductSettingsForm.controls.disbursedAmountPercentageForDownPayment.hasError('required')">
Disbursed Amount Percentage Down Payment is <strong>required</strong>
</mat-error>
<mat-error *ngIf="loanProductSettingsForm.controls.disbursedAmountPercentageForDownPayment.errors?.range">
Disbursed Amount Percentage Down Payment is out <strong>percentage range</strong>
</mat-error>
</mat-form-field>
</div>
<mat-divider fxFlex="98%"></mat-divider>
<h3 fxFlex="23%" class="mat-h3">Event Settings <i class="fas fa-question"
matTooltip="Setting for event notifications to be sent before or after repayments. Blue color means default value from global configurations."></i></h3>
<div fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
@@ -1,6 +1,7 @@
import { Component, OnInit, Input } from '@angular/core';
import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms';
import { LoanProducts } from '../../loan-products';
import { rangeValidator } from 'app/shared/validators/percentage.validator';
@Component({
selector: 'mifosx-loan-product-settings-step',
@@ -89,10 +90,17 @@ export class LoanProductSettingsStepComponent implements OnInit {
'maxTrancheCount': this.loanProductsTemplate.maxTrancheCount,
'outstandingLoanBalance': this.loanProductsTemplate.outstandingLoanBalance,
'dueDaysForRepaymentEvent': this.loanProductsTemplate.dueDaysForRepaymentEvent,
'overDueDaysForRepaymentEvent': this.loanProductsTemplate.overDueDaysForRepaymentEvent
'overDueDaysForRepaymentEvent': this.loanProductsTemplate.overDueDaysForRepaymentEvent,
'enableDownPayment': this.loanProductsTemplate.enableDownPayment
});
if (this.loanProductsTemplate.delinquencyBucket) {
this.loanProductSettingsForm.patchValue({
'disbursedAmountPercentageForDownPayment': this.loanProductsTemplate.disbursedAmountPercentageForDownPayment || 0
});
}
if (this.loanProductsTemplate.enableDownPayment) {
this.loanProductSettingsForm.patchValue({
'delinquencyBucketId': this.loanProductsTemplate.delinquencyBucket.id
});
@@ -187,7 +195,8 @@ export class LoanProductSettingsStepComponent implements OnInit {
}),
'delinquencyBucketId': ['', Validators.required],
'dueDaysForRepaymentEvent': [''],
'overDueDaysForRepaymentEvent': ['']
'overDueDaysForRepaymentEvent': [''],
'enableDownPayment': [false]
});
}
@@ -332,6 +341,15 @@ export class LoanProductSettingsStepComponent implements OnInit {
}
});
this.loanProductSettingsForm.get('enableDownPayment').valueChanges
.subscribe(enableDownPayment => {
if (enableDownPayment) {
this.loanProductSettingsForm.addControl('disbursedAmountPercentageForDownPayment', new FormControl(0, [Validators.required, rangeValidator(0, 100) ]));
} else {
this.loanProductSettingsForm.removeControl('disbursedAmountPercentageForDownPayment');
}
});
this.loanProductSettingsForm.get('allowAttributeConfiguration').valueChanges
.subscribe((allowAttributeConfiguration: any) => {
if (allowAttributeConfiguration) {
@@ -506,6 +506,20 @@
</div>
</div>
<h3 class="mat-h3" fxFlexFill>Down Payments</h3>
<mat-divider [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Enable Down Payments:</span>
<span fxFlex="60%">{{ loanProduct.enableDownPayment ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="loanProduct.enableDownPayment" fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Disbursed Amount Percentage Down Payment (%):</span>
<span fxFlex="60%">{{ loanProduct.disbursedAmountPercentageForDownPayment }} %</span>
</div>
<h3 class="mat-h3" fxFlexFill>Event Settings</h3>
<div fxFlexFill>
@@ -0,0 +1,11 @@
import { AbstractControl, ValidatorFn } from '@angular/forms';
export function rangeValidator(min: number, max: number): ValidatorFn {
return (control: AbstractControl): { [key: string]: boolean } | null => {
if (control.value !== undefined && (isNaN(control.value) || control.value < min || control.value > max)) {
return { 'range': true };
}
return null;
};
}