feat: add create loan product component (#434)
- Add loan product stepper components - Add create loan product component - Add charges penalty filter pipe - Add charges filter pipe - Add find pipe Fixes: #188
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
import { ChargesFilterPipe } from './charges-filter.pipe';
|
||||||
|
|
||||||
|
describe('ChargesFilterPipe', () => {
|
||||||
|
it('create an instance', () => {
|
||||||
|
const pipe = new ChargesFilterPipe();
|
||||||
|
expect(pipe).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'chargesFilter'
|
||||||
|
})
|
||||||
|
export class ChargesFilterPipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(charges: any, chargesDataSource: any, currencyCode: string, multiDisburseLoan?: boolean): any {
|
||||||
|
if (charges) {
|
||||||
|
charges = charges.filter((charge: any) => {
|
||||||
|
if ((charge.currency.code !== currencyCode) || (!!multiDisburseLoan && charge.chargeTimeType.id === 12) || chargesDataSource.includes(charge)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return charges;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { ChargesPenaltyFilterPipe } from './charges-penalty-filter.pipe';
|
||||||
|
|
||||||
|
describe('ChargesPenaltyFilterPipe', () => {
|
||||||
|
it('create an instance', () => {
|
||||||
|
const pipe = new ChargesPenaltyFilterPipe();
|
||||||
|
expect(pipe).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'chargesPenaltyFilter'
|
||||||
|
})
|
||||||
|
export class ChargesPenaltyFilterPipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(charges: any, penalty: boolean): any {
|
||||||
|
if (charges) {
|
||||||
|
charges = charges.filter((charge: any) => {
|
||||||
|
if (charge.penalty === penalty) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return charges;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { FindPipe } from './find.pipe';
|
||||||
|
|
||||||
|
describe('FindPipe', () => {
|
||||||
|
it('create an instance', () => {
|
||||||
|
const pipe = new FindPipe();
|
||||||
|
expect(pipe).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'find'
|
||||||
|
})
|
||||||
|
export class FindPipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(value: any, options: any, key: string, property: string): any {
|
||||||
|
const optionFound = options.find((option: any) => option[key] === value);
|
||||||
|
return optionFound ? optionFound[property] : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,15 +2,16 @@ import { NgModule } from '@angular/core';
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { StatusLookupPipe } from './status-lookup.pipe';
|
import { StatusLookupPipe } from './status-lookup.pipe';
|
||||||
import { AccountsFilterPipe } from './accounts-filter.pipe';
|
import { AccountsFilterPipe } from './accounts-filter.pipe';
|
||||||
|
import { ChargesFilterPipe } from './charges-filter.pipe';
|
||||||
|
import { ChargesPenaltyFilterPipe } from './charges-penalty-filter.pipe';
|
||||||
|
import { FindPipe } from './find.pipe';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule
|
CommonModule
|
||||||
],
|
],
|
||||||
declarations: [StatusLookupPipe, AccountsFilterPipe],
|
declarations: [StatusLookupPipe, AccountsFilterPipe, ChargesFilterPipe, ChargesPenaltyFilterPipe, FindPipe],
|
||||||
providers: [StatusLookupPipe, AccountsFilterPipe],
|
providers: [StatusLookupPipe, AccountsFilterPipe, ChargesFilterPipe, ChargesPenaltyFilterPipe, FindPipe],
|
||||||
exports: [StatusLookupPipe, AccountsFilterPipe]
|
exports: [StatusLookupPipe, AccountsFilterPipe, ChargesFilterPipe, ChargesPenaltyFilterPipe, FindPipe]
|
||||||
})
|
})
|
||||||
export class PipesModule { }
|
export class PipesModule { }
|
||||||
|
|||||||
+102
@@ -0,0 +1,102 @@
|
|||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<mat-horizontal-stepper class="mat-elevation-z8" labelPosition="bottom" #loanProductStepper>
|
||||||
|
|
||||||
|
<ng-template matStepperIcon="number">
|
||||||
|
<fa-icon icon="pencil-alt" size="sm"></fa-icon>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template matStepperIcon="edit">
|
||||||
|
<fa-icon icon="pencil-alt" size="sm"></fa-icon>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template matStepperIcon="done">
|
||||||
|
<fa-icon icon="check" size="sm"></fa-icon>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template matStepperIcon="error">
|
||||||
|
<fa-icon icon="exclamation-triangle" size="lg"></fa-icon>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template matStepperIcon="preview">
|
||||||
|
<fa-icon icon="eye" size="sm"></fa-icon>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<mat-step [stepControl]="loanProductDetailsForm">
|
||||||
|
|
||||||
|
<ng-template matStepLabel>DETAILS</ng-template>
|
||||||
|
|
||||||
|
<mifosx-loan-product-details-step [loanProductsTemplate]="loanProductsTemplate"></mifosx-loan-product-details-step>
|
||||||
|
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
<mat-step [stepControl]="loanProductCurrencyForm">
|
||||||
|
|
||||||
|
<ng-template matStepLabel>CURRENCY</ng-template>
|
||||||
|
|
||||||
|
<mifosx-loan-product-currency-step [loanProductsTemplate]="loanProductsTemplate"></mifosx-loan-product-currency-step>
|
||||||
|
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
<mat-step [stepControl]="loanProductTermsForm">
|
||||||
|
|
||||||
|
<ng-template matStepLabel>TERMS</ng-template>
|
||||||
|
|
||||||
|
<mifosx-loan-product-terms-step [loanProductsTemplate]="loanProductsTemplate"></mifosx-loan-product-terms-step>
|
||||||
|
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
<mat-step [stepControl]="loanProductSettingsForm">
|
||||||
|
|
||||||
|
<ng-template matStepLabel>SETTINGS</ng-template>
|
||||||
|
|
||||||
|
<mifosx-loan-product-settings-step
|
||||||
|
[loanProductsTemplate]="loanProductsTemplate"
|
||||||
|
[isLinkedToFloatingInterestRates]="loanProductTermsForm.get('isLinkedToFloatingInterestRates')"
|
||||||
|
>
|
||||||
|
</mifosx-loan-product-settings-step>
|
||||||
|
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
<mat-step>
|
||||||
|
|
||||||
|
<ng-template matStepLabel>CHARGES</ng-template>
|
||||||
|
|
||||||
|
<mifosx-loan-product-charges-step
|
||||||
|
[loanProductsTemplate]="loanProductsTemplate"
|
||||||
|
[currencyCode]="loanProductCurrencyForm.value.currencyCode"
|
||||||
|
[multiDisburseLoan]="loanProductSettingsForm.value.multiDisburseLoan"
|
||||||
|
>
|
||||||
|
</mifosx-loan-product-charges-step>
|
||||||
|
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
<mat-step [stepControl]="loanProductAccountingForm">
|
||||||
|
|
||||||
|
<ng-template matStepLabel>ACCOUNTING</ng-template>
|
||||||
|
|
||||||
|
<mifosx-loan-product-accounting-step
|
||||||
|
[loanProductsTemplate]="loanProductsTemplate"
|
||||||
|
[accountingRuleData]="accountingRuleData"
|
||||||
|
>
|
||||||
|
</mifosx-loan-product-accounting-step>
|
||||||
|
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
<mat-step state="preview" *ngIf="loanProductFormValid" completed>
|
||||||
|
|
||||||
|
<ng-template matStepLabel>PREVIEW</ng-template>
|
||||||
|
|
||||||
|
<mifosx-loan-product-preview-step
|
||||||
|
[loanProductsTemplate]="loanProductsTemplate"
|
||||||
|
[accountingRuleData]="accountingRuleData"
|
||||||
|
[loanProduct]="loanProduct"
|
||||||
|
(submit)="submit()"
|
||||||
|
>
|
||||||
|
</mifosx-loan-product-preview-step>
|
||||||
|
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
</mat-horizontal-stepper>
|
||||||
|
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
fa-icon {
|
||||||
|
position: relative;
|
||||||
|
left: 5%;
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { CreateLoanProductComponent } from './create-loan-product.component';
|
||||||
|
|
||||||
|
describe('CreateLoanProductComponent', () => {
|
||||||
|
let component: CreateLoanProductComponent;
|
||||||
|
let fixture: ComponentFixture<CreateLoanProductComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ CreateLoanProductComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CreateLoanProductComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
|
||||||
|
import { LoanProductDetailsStepComponent } from '../loan-product-stepper/loan-product-details-step/loan-product-details-step.component';
|
||||||
|
import { LoanProductCurrencyStepComponent } from '../loan-product-stepper/loan-product-currency-step/loan-product-currency-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 { 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 { ProductsService } from 'app/products/products.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'mifosx-create-loan-product',
|
||||||
|
templateUrl: './create-loan-product.component.html',
|
||||||
|
styleUrls: ['./create-loan-product.component.scss']
|
||||||
|
})
|
||||||
|
export class CreateLoanProductComponent implements OnInit {
|
||||||
|
|
||||||
|
@ViewChild(LoanProductDetailsStepComponent) loanProductDetailsStep: LoanProductDetailsStepComponent;
|
||||||
|
@ViewChild(LoanProductCurrencyStepComponent) loanProductCurrencyStep: LoanProductCurrencyStepComponent;
|
||||||
|
@ViewChild(LoanProductTermsStepComponent) loanProductTermsStep: LoanProductTermsStepComponent;
|
||||||
|
@ViewChild(LoanProductSettingsStepComponent) loanProductSettingsStep: LoanProductSettingsStepComponent;
|
||||||
|
@ViewChild(LoanProductChargesStepComponent) loanProductChargesStep: LoanProductChargesStepComponent;
|
||||||
|
@ViewChild(LoanProductAccountingStepComponent) loanProductAccountingStep: LoanProductAccountingStepComponent;
|
||||||
|
|
||||||
|
loanProductsTemplate: any;
|
||||||
|
accountingRuleData = ['None', 'Cash', 'Accrual (periodic)', 'Accrual (upfront)'];
|
||||||
|
|
||||||
|
constructor(private route: ActivatedRoute,
|
||||||
|
private productsService: ProductsService,
|
||||||
|
private router: Router) {
|
||||||
|
this.route.data.subscribe((data: { loanProductsTemplate: any }) => {
|
||||||
|
this.loanProductsTemplate = data.loanProductsTemplate;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
get loanProductDetailsForm() {
|
||||||
|
return this.loanProductDetailsStep.loanProductDetailsForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
get loanProductCurrencyForm() {
|
||||||
|
return this.loanProductCurrencyStep.loanProductCurrencyForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
get loanProductTermsForm() {
|
||||||
|
return this.loanProductTermsStep.loanProductTermsForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
get loanProductSettingsForm() {
|
||||||
|
return this.loanProductSettingsStep.loanProductSettingsForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
get loanProductAccountingForm() {
|
||||||
|
return this.loanProductAccountingStep.loanProductAccountingForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
get loanProductFormValid() {
|
||||||
|
return (
|
||||||
|
this.loanProductDetailsForm.valid &&
|
||||||
|
this.loanProductCurrencyForm.valid &&
|
||||||
|
this.loanProductTermsForm.valid &&
|
||||||
|
this.loanProductSettingsForm.valid &&
|
||||||
|
this.loanProductAccountingForm.valid
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get loanProduct() {
|
||||||
|
return {
|
||||||
|
...this.loanProductDetailsStep.loanProductDetails,
|
||||||
|
...this.loanProductCurrencyStep.loanProductCurrency,
|
||||||
|
...this.loanProductTermsStep.loanProductTerms,
|
||||||
|
...this.loanProductSettingsStep.loanProductSettings,
|
||||||
|
...this.loanProductChargesStep.charges,
|
||||||
|
...this.loanProductAccountingStep.loanProductAccounting
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
submit() {
|
||||||
|
// TODO: Update once language and date settings are setup
|
||||||
|
const dateFormat = 'yyyy-MM-dd';
|
||||||
|
const loanProduct = {
|
||||||
|
...this.loanProduct,
|
||||||
|
charges: this.loanProduct.charges.map((charge: any) => ({ id: charge.id })),
|
||||||
|
dateFormat,
|
||||||
|
locale: 'en'
|
||||||
|
};
|
||||||
|
delete loanProduct.allowAttributeConfiguration;
|
||||||
|
delete loanProduct.advancedAccountingRules;
|
||||||
|
this.productsService.createLoanProduct(loanProduct)
|
||||||
|
.subscribe((response: any) => {
|
||||||
|
this.router.navigate(['../', response.resourceId], { relativeTo: this.route });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+329
@@ -0,0 +1,329 @@
|
|||||||
|
<form [formGroup]="loanProductAccountingForm">
|
||||||
|
|
||||||
|
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<mat-radio-group fxFlex="98%" fxLayout="row" fxLayoutGap="5%" fxLayout.lt-md="column" formControlName="accountingRule">
|
||||||
|
<mat-radio-button *ngFor="let accountingRule of accountingRuleData; let i = index" [value]="i+1">
|
||||||
|
{{ accountingRule }}
|
||||||
|
</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
<mat-divider fxFlex="98%"></mat-divider>
|
||||||
|
|
||||||
|
<div *ngIf="loanProductAccountingForm.value.accountingRule >= 2 && loanProductAccountingForm.value.accountingRule <= 4" fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<h4 fxFlex="98%" class="mat-h4">Assets</h4>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Fund source</mat-label>
|
||||||
|
<mat-select formControlName="fundSourceAccountId" required>
|
||||||
|
<mat-option *ngFor="let assetAccount of assetAccountData" [value]="assetAccount.id">
|
||||||
|
{{ assetAccount.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Fund source is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Loan portfolio</mat-label>
|
||||||
|
<mat-select formControlName="loanPortfolioAccountId" required>
|
||||||
|
<mat-option *ngFor="let assetAccount of assetAccountData" [value]="assetAccount.id">
|
||||||
|
{{ assetAccount.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Loan portfolio is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div *ngIf="loanProductAccountingForm.value.accountingRule === 3 || loanProductAccountingForm.value.accountingRule === 4" fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Interest Receivable</mat-label>
|
||||||
|
<mat-select formControlName="receivableInterestAccountId" required>
|
||||||
|
<mat-option *ngFor="let assetAccount of assetAccountData" [value]="assetAccount.id">
|
||||||
|
{{ assetAccount.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Interest Receivable is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Fees Receivable</mat-label>
|
||||||
|
<mat-select formControlName="receivableFeeAccountId" required>
|
||||||
|
<mat-option *ngFor="let assetAccount of assetAccountData" [value]="assetAccount.id">
|
||||||
|
{{ assetAccount.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Fees Receivable is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Penalties Receivable</mat-label>
|
||||||
|
<mat-select formControlName="receivablePenaltyAccountId" required>
|
||||||
|
<mat-option *ngFor="let assetAccount of assetAccountData" [value]="assetAccount.id">
|
||||||
|
{{ assetAccount.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Penalties Receivable is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Transfer in suspense</mat-label>
|
||||||
|
<mat-select formControlName="transfersInSuspenseAccountId" required>
|
||||||
|
<mat-option *ngFor="let assetAccount of assetAccountData" [value]="assetAccount.id">
|
||||||
|
{{ assetAccount.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Transfer in suspense is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-divider fxFlex="98%"></mat-divider>
|
||||||
|
|
||||||
|
<h4 fxFlex="98%" class="mat-h4">Income</h4>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Income from Interest</mat-label>
|
||||||
|
<mat-select formControlName="interestOnLoanAccountId" required>
|
||||||
|
<mat-option *ngFor="let incomeAccount of incomeAccountData" [value]="incomeAccount.id">
|
||||||
|
{{ incomeAccount.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Income from Interest is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Income from fees</mat-label>
|
||||||
|
<mat-select formControlName="incomeFromFeeAccountId" required>
|
||||||
|
<mat-option *ngFor="let incomeAccount of incomeAccountData" [value]="incomeAccount.id">
|
||||||
|
{{ incomeAccount.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Income from fees is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Income from penalties</mat-label>
|
||||||
|
<mat-select formControlName="incomeFromPenaltyAccountId" required>
|
||||||
|
<mat-option *ngFor="let incomeAccount of incomeAccountData" [value]="incomeAccount.id">
|
||||||
|
{{ incomeAccount.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Income from penalties is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Income from Recovery Repayments</mat-label>
|
||||||
|
<mat-select formControlName="incomeFromRecoveryAccountId" required>
|
||||||
|
<mat-option *ngFor="let incomeAccount of incomeAccountData" [value]="incomeAccount.id">
|
||||||
|
{{ incomeAccount.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Income from Recovery Repayments is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-divider fxFlex="98%"></mat-divider>
|
||||||
|
|
||||||
|
<h4 fxFlex="98%" class="mat-h4">Expenses</h4>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Losses written off</mat-label>
|
||||||
|
<mat-select formControlName="writeOffAccountId" required>
|
||||||
|
<mat-option *ngFor="let expenseAccount of expenseAccountData" [value]="expenseAccount.id">
|
||||||
|
{{ expenseAccount.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Losses written off is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-divider fxFlex="98%"></mat-divider>
|
||||||
|
|
||||||
|
<h4 fxFlex="98%" class="mat-h4">Liabilities</h4>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Over payment liability</mat-label>
|
||||||
|
<mat-select formControlName="overpaymentLiabilityAccountId" required>
|
||||||
|
<mat-option *ngFor="let liabilityAccount of liabilityAccountData" [value]="liabilityAccount.id">
|
||||||
|
{{ liabilityAccount.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Over payment liability is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-divider fxFlex="98%"></mat-divider>
|
||||||
|
|
||||||
|
<h3 fxFlex="23%" class="mat-h3">Advanced Accounting Rules</h3>
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="73%" formControlName="advancedAccountingRules"></mat-checkbox>
|
||||||
|
|
||||||
|
<div *ngIf="loanProductAccountingForm.value.advancedAccountingRules" fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<h4 fxFlex="33%" class="mat-h4">Configure Fund Sources for Payment Channels</h4>
|
||||||
|
|
||||||
|
<div fxFlex="63%">
|
||||||
|
<button type="button" mat-raised-button color="primary" (click)="add('PaymentFundSource', paymentChannelToFundSourceMappings)">
|
||||||
|
<fa-icon icon="plus"></fa-icon>
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table fxFlex="98%" class="mat-elevation-z1" mat-table [dataSource]="paymentChannelToFundSourceMappings.value" *ngIf="paymentChannelToFundSourceMappings.value.length !== 0">
|
||||||
|
|
||||||
|
<ng-container matColumnDef="paymentTypeId">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Payment Type </th>
|
||||||
|
<td mat-cell *matCellDef="let paymentFundSource">
|
||||||
|
{{ paymentFundSource.paymentTypeId | find:paymentTypeData:'id':'name' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="fundSourceAccountId">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Fund Source </th>
|
||||||
|
<td mat-cell *matCellDef="let paymentFundSource">
|
||||||
|
{{ paymentFundSource.fundSourceAccountId | find:assetAccountData:'id':'name' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="actions">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||||
|
<td mat-cell *matCellDef="let paymentFundSource; let i = index">
|
||||||
|
<button mat-icon-button color="primary" (click)="edit('PaymentFundSource', paymentChannelToFundSourceMappings, i)">
|
||||||
|
<fa-icon icon="edit"></fa-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button color="warn" (click)="delete(paymentChannelToFundSourceMappings, i)">
|
||||||
|
<fa-icon icon="trash"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="paymentFundSourceDisplayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: paymentFundSourceDisplayedColumns;"></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h4 fxFlex="33%" class="mat-h4">Map Fees to Specific Income Accounts</h4>
|
||||||
|
|
||||||
|
<div fxFlex="63%">
|
||||||
|
<button type="button" mat-raised-button color="primary" (click)="add('FeesIncome', feeToIncomeAccountMappings)">
|
||||||
|
<fa-icon icon="plus"></fa-icon>
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table fxFlex="98%" class="mat-elevation-z1" mat-table [dataSource]="feeToIncomeAccountMappings.value" *ngIf="feeToIncomeAccountMappings.value.length !== 0">
|
||||||
|
|
||||||
|
<ng-container matColumnDef="chargeId">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Fees </th>
|
||||||
|
<td mat-cell *matCellDef="let feesIncome">
|
||||||
|
{{ feesIncome.chargeId | find:chargeData:'id':'name' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="incomeAccountId">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Income Account </th>
|
||||||
|
<td mat-cell *matCellDef="let feesIncome">
|
||||||
|
{{ feesIncome.incomeAccountId | find:incomeAndLiabilityAccountData:'id':'name' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="actions">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||||
|
<td mat-cell *matCellDef="let feesIncome; let i = index">
|
||||||
|
<button mat-icon-button color="primary" (click)="edit('FeesIncome', feeToIncomeAccountMappings, i)">
|
||||||
|
<fa-icon icon="edit"></fa-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button color="warn" (click)="delete(feeToIncomeAccountMappings, i)">
|
||||||
|
<fa-icon icon="trash"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="feesPenaltyIncomeDisplayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: feesPenaltyIncomeDisplayedColumns;"></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h4 fxFlex="33%" class="mat-h4">Map Penalties to Specific Income Accounts</h4>
|
||||||
|
|
||||||
|
<div fxFlex="63%">
|
||||||
|
<button type="button" mat-raised-button color="primary" (click)="add('PenaltyIncome', penaltyToIncomeAccountMappings)">
|
||||||
|
<fa-icon icon="plus"></fa-icon>
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<table fxFlex="98%" class="mat-elevation-z1" mat-table [dataSource]="penaltyToIncomeAccountMappings.value" *ngIf="penaltyToIncomeAccountMappings.value.length !== 0">
|
||||||
|
|
||||||
|
<ng-container matColumnDef="chargeId">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Penalty </th>
|
||||||
|
<td mat-cell *matCellDef="let penaltyIncome">
|
||||||
|
{{ penaltyIncome.chargeId | find:penaltyData:'id':'name' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="incomeAccountId">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Income Account </th>
|
||||||
|
<td mat-cell *matCellDef="let penaltyIncome">
|
||||||
|
{{ penaltyIncome.incomeAccountId | find:incomeAccountData:'id':'name' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="actions">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||||
|
<td mat-cell *matCellDef="let penaltyIncome; let i = index">
|
||||||
|
<button mat-icon-button color="primary" (click)="edit('PenaltyIncome', penaltyToIncomeAccountMappings, i)">
|
||||||
|
<fa-icon icon="edit"></fa-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button color="warn" (click)="delete(penaltyToIncomeAccountMappings, i)">
|
||||||
|
<fa-icon icon="trash"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="feesPenaltyIncomeDisplayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: feesPenaltyIncomeDisplayedColumns;"></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
|
||||||
|
<button mat-raised-button matStepperPrevious>
|
||||||
|
<fa-icon icon="arrow-left"></fa-icon>
|
||||||
|
Previous
|
||||||
|
</button>
|
||||||
|
<button mat-raised-button matStepperNext>
|
||||||
|
Next
|
||||||
|
<fa-icon icon="arrow-right"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-elevation-z1 {
|
||||||
|
margin: 1em 0 1.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3, h4 {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-radio-group, h4 {
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-divider {
|
||||||
|
margin: 1em 0 2em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-t {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { LoanProductAccountingStepComponent } from './loan-product-accounting-step.component';
|
||||||
|
|
||||||
|
describe('LoanProductAccountingStepComponent', () => {
|
||||||
|
let component: LoanProductAccountingStepComponent;
|
||||||
|
let fixture: ComponentFixture<LoanProductAccountingStepComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ LoanProductAccountingStepComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(LoanProductAccountingStepComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
+231
@@ -0,0 +1,231 @@
|
|||||||
|
import { Component, OnInit, Input } from '@angular/core';
|
||||||
|
import { FormGroup, FormBuilder, FormArray, Validators, FormControl } from '@angular/forms';
|
||||||
|
import { MatDialog } from '@angular/material';
|
||||||
|
|
||||||
|
import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component';
|
||||||
|
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
|
||||||
|
|
||||||
|
import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-base';
|
||||||
|
import { SelectBase } from 'app/shared/form-dialog/formfield/model/select-base';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'mifosx-loan-product-accounting-step',
|
||||||
|
templateUrl: './loan-product-accounting-step.component.html',
|
||||||
|
styleUrls: ['./loan-product-accounting-step.component.scss']
|
||||||
|
})
|
||||||
|
export class LoanProductAccountingStepComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() loanProductsTemplate: any;
|
||||||
|
@Input() accountingRuleData: any;
|
||||||
|
|
||||||
|
loanProductAccountingForm: FormGroup;
|
||||||
|
|
||||||
|
chargeData: any;
|
||||||
|
penaltyData: any;
|
||||||
|
paymentTypeData: any;
|
||||||
|
assetAccountData: any;
|
||||||
|
incomeAccountData: any;
|
||||||
|
expenseAccountData: any;
|
||||||
|
liabilityAccountData: any;
|
||||||
|
incomeAndLiabilityAccountData: any;
|
||||||
|
|
||||||
|
paymentFundSourceDisplayedColumns: string[] = ['paymentTypeId', 'fundSourceAccountId', 'actions'];
|
||||||
|
feesPenaltyIncomeDisplayedColumns: string[] = ['chargeId', 'incomeAccountId', 'actions'];
|
||||||
|
|
||||||
|
constructor(private formBuilder: FormBuilder,
|
||||||
|
public dialog: MatDialog) {
|
||||||
|
this.createLoanProductAccountingForm();
|
||||||
|
this.setConditionalControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.chargeData = this.loanProductsTemplate.chargeOptions || [];
|
||||||
|
this.penaltyData = this.loanProductsTemplate.penaltyOptions || [];
|
||||||
|
this.paymentTypeData = this.loanProductsTemplate.paymentTypeOptions || [];
|
||||||
|
this.assetAccountData = this.loanProductsTemplate.accountingMappingOptions.assetAccountOptions || [];
|
||||||
|
this.incomeAccountData = this.loanProductsTemplate.accountingMappingOptions.incomeAccountOptions || [];
|
||||||
|
this.expenseAccountData = this.loanProductsTemplate.accountingMappingOptions.expenseAccountOptions || [];
|
||||||
|
this.liabilityAccountData = this.loanProductsTemplate.accountingMappingOptions.liabilityAccountOptions || [];
|
||||||
|
this.incomeAndLiabilityAccountData = this.incomeAccountData.concat(this.liabilityAccountData);
|
||||||
|
}
|
||||||
|
|
||||||
|
createLoanProductAccountingForm() {
|
||||||
|
this.loanProductAccountingForm = this.formBuilder.group({
|
||||||
|
'accountingRule': [1]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setConditionalControls() {
|
||||||
|
this.loanProductAccountingForm.get('accountingRule').valueChanges
|
||||||
|
.subscribe((accountingRule: any) => {
|
||||||
|
if (accountingRule >= 2 && accountingRule <= 4) {
|
||||||
|
this.loanProductAccountingForm.addControl('fundSourceAccountId', new FormControl('', Validators.required));
|
||||||
|
this.loanProductAccountingForm.addControl('loanPortfolioAccountId', new FormControl('', Validators.required));
|
||||||
|
this.loanProductAccountingForm.addControl('transfersInSuspenseAccountId', new FormControl('', Validators.required));
|
||||||
|
this.loanProductAccountingForm.addControl('interestOnLoanAccountId', new FormControl('', Validators.required));
|
||||||
|
this.loanProductAccountingForm.addControl('incomeFromFeeAccountId', new FormControl('', Validators.required));
|
||||||
|
this.loanProductAccountingForm.addControl('incomeFromPenaltyAccountId', new FormControl('', Validators.required));
|
||||||
|
this.loanProductAccountingForm.addControl('incomeFromRecoveryAccountId', new FormControl('', Validators.required));
|
||||||
|
this.loanProductAccountingForm.addControl('writeOffAccountId', new FormControl('', Validators.required));
|
||||||
|
this.loanProductAccountingForm.addControl('overpaymentLiabilityAccountId', new FormControl('', Validators.required));
|
||||||
|
this.loanProductAccountingForm.addControl('advancedAccountingRules', new FormControl(false));
|
||||||
|
|
||||||
|
this.loanProductAccountingForm.get('advancedAccountingRules').valueChanges
|
||||||
|
.subscribe((advancedAccountingRules: boolean) => {
|
||||||
|
if (advancedAccountingRules) {
|
||||||
|
this.loanProductAccountingForm.addControl('paymentChannelToFundSourceMappings', this.formBuilder.array([]));
|
||||||
|
this.loanProductAccountingForm.addControl('feeToIncomeAccountMappings', this.formBuilder.array([] ));
|
||||||
|
this.loanProductAccountingForm.addControl('penaltyToIncomeAccountMappings', this.formBuilder.array([]));
|
||||||
|
} else {
|
||||||
|
this.loanProductAccountingForm.removeControl('paymentChannelToFundSourceMappings');
|
||||||
|
this.loanProductAccountingForm.removeControl('feeToIncomeAccountMappings');
|
||||||
|
this.loanProductAccountingForm.removeControl('penaltyToIncomeAccountMappings');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.loanProductAccountingForm.removeControl('fundSourceAccountId');
|
||||||
|
this.loanProductAccountingForm.removeControl('loanPortfolioAccountId');
|
||||||
|
this.loanProductAccountingForm.removeControl('transfersInSuspenseAccountId');
|
||||||
|
this.loanProductAccountingForm.removeControl('interestOnLoanAccountId');
|
||||||
|
this.loanProductAccountingForm.removeControl('incomeFromFeeAccountId');
|
||||||
|
this.loanProductAccountingForm.removeControl('incomeFromPenaltyAccountId');
|
||||||
|
this.loanProductAccountingForm.removeControl('incomeFromRecoveryAccountId');
|
||||||
|
this.loanProductAccountingForm.removeControl('writeOffAccountId');
|
||||||
|
this.loanProductAccountingForm.removeControl('overpaymentLiabilityAccountId');
|
||||||
|
this.loanProductAccountingForm.removeControl('advancedAccountingRules');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accountingRule === 3 || accountingRule === 4) {
|
||||||
|
this.loanProductAccountingForm.addControl('receivableInterestAccountId', new FormControl('', Validators.required));
|
||||||
|
this.loanProductAccountingForm.addControl('receivableFeeAccountId', new FormControl('', Validators.required));
|
||||||
|
this.loanProductAccountingForm.addControl('receivablePenaltyAccountId', new FormControl('', Validators.required));
|
||||||
|
} else {
|
||||||
|
this.loanProductAccountingForm.removeControl('receivableInterestAccountId');
|
||||||
|
this.loanProductAccountingForm.removeControl('receivableFeeAccountId');
|
||||||
|
this.loanProductAccountingForm.removeControl('receivablePenaltyAccountId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get paymentChannelToFundSourceMappings(): FormArray {
|
||||||
|
return this.loanProductAccountingForm.get('paymentChannelToFundSourceMappings') as FormArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
get feeToIncomeAccountMappings(): FormArray {
|
||||||
|
return this.loanProductAccountingForm.get('feeToIncomeAccountMappings') as FormArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
get penaltyToIncomeAccountMappings(): FormArray {
|
||||||
|
return this.loanProductAccountingForm.get('penaltyToIncomeAccountMappings') as FormArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
add(formType: string, formArray: FormArray) {
|
||||||
|
const data = this.getData(formType);
|
||||||
|
const dialogRef = this.dialog.open(FormDialogComponent, { data });
|
||||||
|
dialogRef.afterClosed().subscribe((response: any) => {
|
||||||
|
if (response.data) {
|
||||||
|
formArray.push(response.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
edit(formType: string, formArray: FormArray, index: number) {
|
||||||
|
const data = { ...this.getData(formType, formArray.at(index).value), layout: { addButtonText: 'Edit' } };
|
||||||
|
const dialogRef = this.dialog.open(FormDialogComponent, { data });
|
||||||
|
dialogRef.afterClosed().subscribe((response: any) => {
|
||||||
|
if (response.data) {
|
||||||
|
formArray.at(index).patchValue(response.data.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(formArray: FormArray, index: number) {
|
||||||
|
const dialogRef = this.dialog.open(DeleteDialogComponent, {
|
||||||
|
data: { deleteContext: `this` }
|
||||||
|
});
|
||||||
|
dialogRef.afterClosed().subscribe((response: any) => {
|
||||||
|
if (response.delete) {
|
||||||
|
formArray.removeAt(index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getData(formType: string, values?: any) {
|
||||||
|
switch (formType) {
|
||||||
|
case 'PaymentFundSource': return { title: 'Configure Fund Sources for Payment Channels', formfields: this.getPaymentFundSourceFormfields(values) };
|
||||||
|
case 'FeesIncome': return { title: 'Map Fees to Income Accounts', formfields: this.getFeesIncomeFormfields(values) };
|
||||||
|
case 'PenaltyIncome': return { title: 'Map Penalties to Specific Income Accounts', formfields: this.getPenaltyIncomeFormfields(values) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getPaymentFundSourceFormfields(values?: any) {
|
||||||
|
const formfields: FormfieldBase[] = [
|
||||||
|
new SelectBase({
|
||||||
|
controlName: 'paymentTypeId',
|
||||||
|
label: 'Payment Type',
|
||||||
|
value: values ? values.paymentTypeId : this.paymentTypeData[0].id,
|
||||||
|
options: { label: 'name', value: 'id', data: this.paymentTypeData },
|
||||||
|
required: true,
|
||||||
|
order: 1
|
||||||
|
}),
|
||||||
|
new SelectBase({
|
||||||
|
controlName: 'fundSourceAccountId',
|
||||||
|
label: 'Fund Source',
|
||||||
|
value: values ? values.fundSourceAccountId : this.assetAccountData[0].id,
|
||||||
|
options: { label: 'name', value: 'id', data: this.assetAccountData },
|
||||||
|
required: true,
|
||||||
|
order: 2
|
||||||
|
})
|
||||||
|
];
|
||||||
|
return formfields;
|
||||||
|
}
|
||||||
|
|
||||||
|
getFeesIncomeFormfields(values?: any) {
|
||||||
|
const formfields: FormfieldBase[] = [
|
||||||
|
new SelectBase({
|
||||||
|
controlName: 'chargeId',
|
||||||
|
label: 'Fees',
|
||||||
|
value: values ? values.chargeId : this.chargeData[0].id,
|
||||||
|
options: { label: 'name', value: 'id', data: this.chargeData },
|
||||||
|
required: true,
|
||||||
|
order: 1
|
||||||
|
}),
|
||||||
|
new SelectBase({
|
||||||
|
controlName: 'incomeAccountId',
|
||||||
|
label: 'Income Account',
|
||||||
|
value: values ? values.incomeAccountId : this.incomeAndLiabilityAccountData[0].id,
|
||||||
|
options: { label: 'name', value: 'id', data: this.incomeAndLiabilityAccountData },
|
||||||
|
required: true,
|
||||||
|
order: 2
|
||||||
|
})
|
||||||
|
];
|
||||||
|
return formfields;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPenaltyIncomeFormfields(values?: any) {
|
||||||
|
const formfields: FormfieldBase[] = [
|
||||||
|
new SelectBase({
|
||||||
|
controlName: 'chargeId',
|
||||||
|
label: 'Penalty',
|
||||||
|
value: values ? values.chargeId : this.penaltyData[0].id,
|
||||||
|
options: { label: 'name', value: 'id', data: this.penaltyData },
|
||||||
|
required: true,
|
||||||
|
order: 1
|
||||||
|
}),
|
||||||
|
new SelectBase({
|
||||||
|
controlName: 'incomeAccountId',
|
||||||
|
label: 'Income Account',
|
||||||
|
value: values ? values.incomeAccountId : this.incomeAccountData[0].id,
|
||||||
|
options: { label: 'name', value: 'id', data: this.incomeAccountData },
|
||||||
|
required: true,
|
||||||
|
order: 2
|
||||||
|
})
|
||||||
|
];
|
||||||
|
return formfields;
|
||||||
|
}
|
||||||
|
|
||||||
|
get loanProductAccounting() {
|
||||||
|
return this.loanProductAccountingForm.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+140
@@ -0,0 +1,140 @@
|
|||||||
|
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Charge</mat-label>
|
||||||
|
<mat-select #charge>
|
||||||
|
<mat-option *ngFor="let charge of chargeData | chargesFilter:chargesDataSource:currencyCode:multiDisburseLoan" [value]="charge">
|
||||||
|
{{ charge.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div fxFlex="48%" fxFlexAlign="center">
|
||||||
|
<button type="button" mat-raised-button color="primary" (click)="addCharge(charge)" [disabled]="!charge.value">
|
||||||
|
<fa-icon icon="plus"></fa-icon>
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table fxFlex="98%" class="mat-elevation-z1" mat-table [dataSource]="chargesDataSource | chargesPenaltyFilter:false" #chargesTable [hidden]="chargesTable.dataSource.length === 0">
|
||||||
|
|
||||||
|
<ng-container matColumnDef="name">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Name </th>
|
||||||
|
<td mat-cell *matCellDef="let charge">
|
||||||
|
{{ charge.name + ', ' + charge.currency.displaySymbol }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="chargeCalculationType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Type </th>
|
||||||
|
<td mat-cell *matCellDef="let charge">
|
||||||
|
{{ charge.chargeCalculationType.value }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="amount">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Amount </th>
|
||||||
|
<td mat-cell *matCellDef="let charge">
|
||||||
|
{{ charge.amount }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="chargeTimeType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Collected On </th>
|
||||||
|
<td mat-cell *matCellDef="let charge">
|
||||||
|
{{ charge.chargeTimeType.value }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="action">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||||
|
<td mat-cell *matCellDef="let charge">
|
||||||
|
<button mat-icon-button color="warn" (click)="deleteCharge(charge)">
|
||||||
|
<fa-icon icon="trash"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<mat-divider fxFlex="98%"></mat-divider>
|
||||||
|
|
||||||
|
<div fxFlex="98%">
|
||||||
|
<h3 class="mat-h3">Overdue Charges</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Overdue Charge</mat-label>
|
||||||
|
<mat-select #overdueCharge>
|
||||||
|
<mat-option *ngFor="let overdueCharge of overdueChargeData | chargesFilter:chargesDataSource:currencyCode" [value]="overdueCharge">
|
||||||
|
{{ overdueCharge.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div fxFlex="48%" fxFlexAlign="center">
|
||||||
|
<button type="button" mat-raised-button color="primary" (click)="addCharge(overdueCharge)" [disabled]="!overdueCharge.value">
|
||||||
|
<fa-icon icon="plus"></fa-icon>
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table fxFlex="98%" class="mat-elevation-z1" mat-table [dataSource]="chargesDataSource | chargesPenaltyFilter:true" #overdueChargesTable [hidden]="overdueChargesTable.dataSource.length === 0">
|
||||||
|
|
||||||
|
<ng-container matColumnDef="name">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Name </th>
|
||||||
|
<td mat-cell *matCellDef="let overdueCharge">
|
||||||
|
{{ overdueCharge.name + ', ' + overdueCharge.currency.displaySymbol }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="chargeCalculationType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Type </th>
|
||||||
|
<td mat-cell *matCellDef="let overdueCharge">
|
||||||
|
{{ overdueCharge.chargeCalculationType.value }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="amount">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Amount </th>
|
||||||
|
<td mat-cell *matCellDef="let overdueCharge">
|
||||||
|
{{ overdueCharge.amount }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="chargeTimeType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Collected On </th>
|
||||||
|
<td mat-cell *matCellDef="let overdueCharge">
|
||||||
|
{{ overdueCharge.chargeTimeType.value }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="action">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||||
|
<td mat-cell *matCellDef="let overdueCharge">
|
||||||
|
<button mat-icon-button color="warn" (click)="deleteCharge(overdueCharge)">
|
||||||
|
<fa-icon icon="trash"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxLayout="row" class="margin-t+" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
|
||||||
|
<button mat-raised-button matStepperPrevious>
|
||||||
|
<fa-icon icon="arrow-left"></fa-icon>
|
||||||
|
Previous
|
||||||
|
</button>
|
||||||
|
<button mat-raised-button matStepperNext>
|
||||||
|
Next
|
||||||
|
<fa-icon icon="arrow-right"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-elevation-z1 {
|
||||||
|
margin: 1em 0 1.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-divider {
|
||||||
|
margin: 1em 0 2em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-t {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { LoanProductChargesStepComponent } from './loan-product-charges-step.component';
|
||||||
|
|
||||||
|
describe('LoanProductChargesStepComponent', () => {
|
||||||
|
let component: LoanProductChargesStepComponent;
|
||||||
|
let fixture: ComponentFixture<LoanProductChargesStepComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ LoanProductChargesStepComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(LoanProductChargesStepComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
import { Component, OnInit, Input } from '@angular/core';
|
||||||
|
import { MatDialog } from '@angular/material';
|
||||||
|
|
||||||
|
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'mifosx-loan-product-charges-step',
|
||||||
|
templateUrl: './loan-product-charges-step.component.html',
|
||||||
|
styleUrls: ['./loan-product-charges-step.component.scss']
|
||||||
|
})
|
||||||
|
export class LoanProductChargesStepComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() loanProductsTemplate: any;
|
||||||
|
@Input() currencyCode: any;
|
||||||
|
@Input() multiDisburseLoan: any;
|
||||||
|
|
||||||
|
chargeData: any;
|
||||||
|
overdueChargeData: any;
|
||||||
|
|
||||||
|
chargesDataSource: {}[] = [];
|
||||||
|
displayedColumns: string[] = ['name', 'chargeCalculationType', 'amount', 'chargeTimeType', 'action'];
|
||||||
|
|
||||||
|
constructor(public dialog: MatDialog) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.chargeData = this.loanProductsTemplate.chargeOptions;
|
||||||
|
this.overdueChargeData = this.loanProductsTemplate.penaltyOptions.filter((penalty: any) => penalty.chargeTimeType.code === 'chargeTimeType.overdueInstallment');
|
||||||
|
}
|
||||||
|
|
||||||
|
addCharge(charge: any) {
|
||||||
|
this.chargesDataSource = this.chargesDataSource.concat([charge.value]);
|
||||||
|
charge.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteCharge(charge: any) {
|
||||||
|
const deleteChargeDialogRef = this.dialog.open(DeleteDialogComponent, {
|
||||||
|
data: { deleteContext: `charge ${charge.name}` }
|
||||||
|
});
|
||||||
|
deleteChargeDialogRef.afterClosed().subscribe((response: any) => {
|
||||||
|
if (response.delete) {
|
||||||
|
this.chargesDataSource.splice(this.chargesDataSource.indexOf(charge), 1);
|
||||||
|
this.chargesDataSource = this.chargesDataSource.concat([]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get charges() {
|
||||||
|
return {
|
||||||
|
charges: this.chargesDataSource
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
<form [formGroup]="loanProductCurrencyForm">
|
||||||
|
|
||||||
|
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Currency</mat-label>
|
||||||
|
<mat-select formControlName="currencyCode" required>
|
||||||
|
<mat-option *ngFor="let currency of currencyData" [value]="currency.code">
|
||||||
|
{{ currency.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Currency is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Decimal Places</mat-label>
|
||||||
|
<input type="number" matInput formControlName="digitsAfterDecimal" required>
|
||||||
|
<mat-error>
|
||||||
|
Decimal Places is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Currency in multiples of</mat-label>
|
||||||
|
<input type="number" matInput formControlName="inMultiplesOf" required>
|
||||||
|
<mat-error>
|
||||||
|
Currency in multiples of is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Installment in multiples of</mat-label>
|
||||||
|
<input type="number" matInput formControlName="installmentAmountInMultiplesOf" required>
|
||||||
|
<mat-error>
|
||||||
|
Installment in multiples of is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
|
||||||
|
<button mat-raised-button matStepperPrevious>
|
||||||
|
<fa-icon icon="arrow-left"></fa-icon>
|
||||||
|
Previous
|
||||||
|
</button>
|
||||||
|
<button mat-raised-button matStepperNext>
|
||||||
|
Next
|
||||||
|
<fa-icon icon="arrow-right"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
.margin-t {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { LoanProductCurrencyStepComponent } from './loan-product-currency-step.component';
|
||||||
|
|
||||||
|
describe('LoanProductCurrencyStepComponent', () => {
|
||||||
|
let component: LoanProductCurrencyStepComponent;
|
||||||
|
let fixture: ComponentFixture<LoanProductCurrencyStepComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ LoanProductCurrencyStepComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(LoanProductCurrencyStepComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
import { Component, OnInit, Input } from '@angular/core';
|
||||||
|
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'mifosx-loan-product-currency-step',
|
||||||
|
templateUrl: './loan-product-currency-step.component.html',
|
||||||
|
styleUrls: ['./loan-product-currency-step.component.scss']
|
||||||
|
})
|
||||||
|
export class LoanProductCurrencyStepComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() loanProductsTemplate: any;
|
||||||
|
|
||||||
|
loanProductCurrencyForm: FormGroup;
|
||||||
|
|
||||||
|
currencyData: any;
|
||||||
|
|
||||||
|
constructor(private formBuilder: FormBuilder) {
|
||||||
|
this.createLoanProductCurrencyForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.currencyData = this.loanProductsTemplate.currencyOptions;
|
||||||
|
this.loanProductCurrencyForm.patchValue({ 'currencyCode': this.currencyData[0].code });
|
||||||
|
}
|
||||||
|
|
||||||
|
createLoanProductCurrencyForm() {
|
||||||
|
this.loanProductCurrencyForm = this.formBuilder.group({
|
||||||
|
'currencyCode': ['', Validators.required],
|
||||||
|
'digitsAfterDecimal': [2, Validators.required],
|
||||||
|
'inMultiplesOf': ['', Validators.required],
|
||||||
|
'installmentAmountInMultiplesOf': ['', Validators.required]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get loanProductCurrency() {
|
||||||
|
return this.loanProductCurrencyForm.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+66
@@ -0,0 +1,66 @@
|
|||||||
|
<form [formGroup]="loanProductDetailsForm">
|
||||||
|
|
||||||
|
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column" fxLayoutAlign.gt-sm="start center">
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Product Name</mat-label>
|
||||||
|
<input matInput formControlName="name" required>
|
||||||
|
<mat-error>
|
||||||
|
Product Name is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Short Name</mat-label>
|
||||||
|
<input matInput formControlName="shortName" maxlength="4" required>
|
||||||
|
<mat-error>
|
||||||
|
Short Name is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Fund</mat-label>
|
||||||
|
<mat-select formControlName="fundId">
|
||||||
|
<mat-option *ngFor="let fund of fundData" [value]="fund.id">
|
||||||
|
{{ fund.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-checkbox labelPosition="before" formControlName="includeInBorrowerCycle" fxFlex="48%">
|
||||||
|
Include in Customer Loan Counter
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Start Date</mat-label>
|
||||||
|
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="startDatePicker" formControlName="startDate">
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="startDatePicker"></mat-datepicker-toggle>
|
||||||
|
<mat-datepicker #startDatePicker></mat-datepicker>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Close Date</mat-label>
|
||||||
|
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="closeDatePicker" formControlName="closeDate">
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="closeDatePicker"></mat-datepicker-toggle>
|
||||||
|
<mat-datepicker #closeDatePicker></mat-datepicker>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="98%">
|
||||||
|
<mat-label>Description</mat-label>
|
||||||
|
<textarea matInput formControlName="description"></textarea>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
|
||||||
|
<button mat-raised-button matStepperPrevious disabled>
|
||||||
|
<fa-icon icon="arrow-left"></fa-icon>
|
||||||
|
Previous
|
||||||
|
</button>
|
||||||
|
<button mat-raised-button matStepperNext>
|
||||||
|
Next
|
||||||
|
<fa-icon icon="arrow-right"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
.margin-t {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { LoanProductDetailsStepComponent } from './loan-product-details-step.component';
|
||||||
|
|
||||||
|
describe('LoanProductDetailsStepComponent', () => {
|
||||||
|
let component: LoanProductDetailsStepComponent;
|
||||||
|
let fixture: ComponentFixture<LoanProductDetailsStepComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ LoanProductDetailsStepComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(LoanProductDetailsStepComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
import { Component, OnInit, Input } from '@angular/core';
|
||||||
|
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
||||||
|
import { DatePipe } from '@angular/common';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'mifosx-loan-product-details-step',
|
||||||
|
templateUrl: './loan-product-details-step.component.html',
|
||||||
|
styleUrls: ['./loan-product-details-step.component.scss']
|
||||||
|
})
|
||||||
|
export class LoanProductDetailsStepComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() loanProductsTemplate: any;
|
||||||
|
|
||||||
|
loanProductDetailsForm: FormGroup;
|
||||||
|
|
||||||
|
fundData: any;
|
||||||
|
|
||||||
|
minDate = new Date(2000, 0, 1);
|
||||||
|
maxDate = new Date(new Date().setFullYear(new Date().getFullYear() + 10));
|
||||||
|
|
||||||
|
constructor(private formBuilder: FormBuilder,
|
||||||
|
private datePipe: DatePipe) {
|
||||||
|
this.createLoanProductDetailsForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.fundData = this.loanProductsTemplate.fundOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
createLoanProductDetailsForm() {
|
||||||
|
this.loanProductDetailsForm = this.formBuilder.group({
|
||||||
|
'name': ['', Validators.required],
|
||||||
|
'shortName': ['', Validators.required],
|
||||||
|
'description': [''],
|
||||||
|
'fundId': [''],
|
||||||
|
'startDate': [''],
|
||||||
|
'closeDate': [''],
|
||||||
|
'includeInBorrowerCycle': [false]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get loanProductDetails() {
|
||||||
|
const prevStartDate: Date = this.loanProductDetailsForm.value.startDate;
|
||||||
|
const prevCloseDate: Date = this.loanProductDetailsForm.value.closeDate;
|
||||||
|
// TODO: Update once language and date settings are setup
|
||||||
|
const dateFormat = 'yyyy-MM-dd';
|
||||||
|
this.loanProductDetailsForm.patchValue({
|
||||||
|
startDate: this.datePipe.transform(prevStartDate, dateFormat) || '',
|
||||||
|
closeDate: this.datePipe.transform(prevCloseDate, dateFormat) || ''
|
||||||
|
});
|
||||||
|
return this.loanProductDetailsForm.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+730
@@ -0,0 +1,730 @@
|
|||||||
|
<div fxLayout="row wrap" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<h2 class="mat-h2" fxFlexFill>{{ loanProduct.name }}</h2>
|
||||||
|
|
||||||
|
<mat-divider fxFlexFill></mat-divider>
|
||||||
|
|
||||||
|
<h3 class="mat-h3" fxFlexFill>Details</h3>
|
||||||
|
|
||||||
|
<mat-divider fxFlexFill></mat-divider>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Short Name:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.shortName }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.fundId">
|
||||||
|
<span fxFlex="40%">Fund Name:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.fundId | find:loanProductsTemplate.fundOptions:'id':'name' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Include in Customer Loan Counter:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.includeInBorrowerCycle ? 'Yes' : 'No' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.startDate">
|
||||||
|
<span fxFlex="40%">Start Date:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.startDate }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.closeDate">
|
||||||
|
<span fxFlex="40%">Close Date:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.closeDate }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.description">
|
||||||
|
<span fxFlex="40%">Description:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.description }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="mat-h3" fxFlexFill>Currency</h3>
|
||||||
|
|
||||||
|
<mat-divider fxFlexFill></mat-divider>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Currency:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.currencyCode | find:loanProductsTemplate.currencyOptions:'code':'name' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Decimal Places:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.digitsAfterDecimal }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Currency in multiples of:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.inMultiplesOf }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Installment in multiples of:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.installmentAmountInMultiplesOf }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="mat-h3" fxFlexFill>Terms</h3>
|
||||||
|
|
||||||
|
<mat-divider fxFlexFill></mat-divider>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Principal:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.principal + ' (Min: ' + (loanProduct.minPrincipal ? loanProduct.minPrincipal : loanProduct.principal) + ', Max: ' + (loanProduct.maxPrincipal ? loanProduct.maxPrincipal : loanProduct.principal) + ')' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Number of Repayments:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.numberOfRepayments + ' (Min: ' + (loanProduct.minNumberOfRepayments ? loanProduct.minNumberOfRepayments : loanProduct.numberOfRepayments) + ', Max: ' + (loanProduct.maxNumberOfRepayments ? loanProduct.maxNumberOfRepayments : loanProduct.numberOfRepayments) + ')' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Linked to floating interest rates:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.isLinkedToFloatingInterestRates ? 'Yes' : 'No' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="!loanProduct.isLinkedToFloatingInterestRates">
|
||||||
|
<span fxFlex="40%">Nominal interest rate:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.interestRatePerPeriod + ' (Min: ' + (loanProduct.minInterestRatePerPeriod ? loanProduct.minInterestRatePerPeriod : loanProduct.interestRatePerPeriod) + ', Max: ' + (loanProduct.maxInterestRatePerPeriod ? loanProduct.maxInterestRatePerPeriod : loanProduct.interestRatePerPeriod) + ')' + ' ' + (loanProduct.interestRateFrequencyType | find:loanProductsTemplate.interestRateFrequencyTypeOptions:'id':'value') }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.isLinkedToFloatingInterestRates" fxLayout="row wrap" fxLayout.lt-md="column">
|
||||||
|
<span fxFlex="40%">Floating Lending Interest Rate:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.floatingRatesId | find:loanProductsTemplate.floatingRateOptions:'id':'name' }}</span>
|
||||||
|
<span fxFlex="40%">Interest Rate Differential:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.interestRateDifferential }}</span>
|
||||||
|
<span fxFlex="40%">Floating Interest Rate Calculation Allowed:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.isFloatingInterestRateCalculationAllowed ? 'Yes' : 'No' }}</span>
|
||||||
|
<span fxFlex="40%">Floating Interest Rate:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.defaultDifferentialLendingRate + ' (Min: ' + (loanProduct.minDifferentialLendingRate ? loanProduct.minDifferentialLendingRate : loanProduct.defaultDifferentialLendingRate) + ', Max: ' + (loanProduct.maxDifferentialLendingRate ? loanProduct.maxDifferentialLendingRate : loanProduct.defaultDifferentialLendingRate) + ')' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Terms vary based on loan cycle:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.useBorrowerCycle ? 'Yes' : 'No' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.principalVariationsForBorrowerCycle?.length">
|
||||||
|
|
||||||
|
<h4 class="mat-h4" fxFlexFill>Principal by loan cycle</h4>
|
||||||
|
|
||||||
|
<table fxFlexFill class="mat-elevation-z1" mat-table [dataSource]="loanProduct.principalVariationsForBorrowerCycle">
|
||||||
|
|
||||||
|
<ng-container matColumnDef="valueConditionType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Condition </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.valueConditionType | find:loanProductsTemplate.valueConditionTypeOptions:'id':'value' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="borrowerCycleNumber">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Loan Cycle </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.borrowerCycleNumber }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="minValue">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Minimum </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.minValue }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="defaultValue">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Default </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.defaultValue }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="maxValue">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Maximum </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.maxValue }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="variationsDisplayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: variationsDisplayedColumns;"></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.numberOfRepaymentVariationsForBorrowerCycle?.length">
|
||||||
|
|
||||||
|
<h4 class="mat-h4" fxFlexFill>Number of Repayments by loan cycle</h4>
|
||||||
|
|
||||||
|
<table fxFlexFill class="mat-elevation-z1" mat-table [dataSource]="loanProduct.numberOfRepaymentVariationsForBorrowerCycle">
|
||||||
|
|
||||||
|
<ng-container matColumnDef="valueConditionType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Condition </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.valueConditionType | find:loanProductsTemplate.valueConditionTypeOptions:'id':'value' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="borrowerCycleNumber">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Loan Cycle </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.borrowerCycleNumber }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="minValue">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Minimum </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.minValue }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="defaultValue">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Default </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.defaultValue }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="maxValue">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Maximum </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.maxValue }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="variationsDisplayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: variationsDisplayedColumns;"></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.interestRateVariationsForBorrowerCycle?.length">
|
||||||
|
|
||||||
|
<h4 class="mat-h4" fxFlexFill>Nominal Interest Rate by loan cycle</h4>
|
||||||
|
|
||||||
|
<table fxFlexFill class="mat-elevation-z1" mat-table [dataSource]="loanProduct.interestRateVariationsForBorrowerCycle">
|
||||||
|
|
||||||
|
<ng-container matColumnDef="valueConditionType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Condition </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.valueConditionType | find:loanProductsTemplate.valueConditionTypeOptions:'id':'value' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="borrowerCycleNumber">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Loan Cycle </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.borrowerCycleNumber }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="minValue">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Minimum </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.minValue }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="defaultValue">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Default </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.defaultValue }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="maxValue">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Maximum </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.maxValue }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="variationsDisplayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: variationsDisplayedColumns;"></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Repay Every:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.repaymentEvery + ' ' + (loanProduct.repaymentFrequencyType | find:loanProductsTemplate.repaymentFrequencyTypeOptions:'id':'value') }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.minimumDaysBetweenDisbursalAndFirstRepayment">
|
||||||
|
<span fxFlex="40%">Minimum days between disbursal and first repayment date:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.minimumDaysBetweenDisbursalAndFirstRepayment }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="mat-h3" fxFlexFill>Settings</h3>
|
||||||
|
|
||||||
|
<mat-divider fxFlexFill></mat-divider>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Amortization:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.amortizationType | find:loanProductsTemplate.amortizationTypeOptions:'id':'value' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Is Equal Amortization:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.isEqualAmortization ? 'Yes' : 'No' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Interest Method:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.interestType | find:loanProductsTemplate.interestTypeOptions:'id':'value' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Interest Calculation Period:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.interestCalculationPeriodType | find:loanProductsTemplate.interestCalculationPeriodTypeOptions:'id':'value' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.interestCalculationPeriodType === 1">
|
||||||
|
<span fxFlex="40%">Allow Partial Interest Calculation with same as repayment:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.allowPartialPeriodInterestCalcualtion ? 'Yes' : 'No' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Repayment Strategy:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.transactionProcessingStrategyId | find:loanProductsTemplate.transactionProcessingStrategyOptions:'id':'name' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.graceOnPrincipalPayment">
|
||||||
|
<span fxFlex="40%">Grace on principal payment:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.graceOnPrincipalPayment }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.graceOnInterestPayment">
|
||||||
|
<span fxFlex="40%">Grace on interest payment:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.graceOnInterestPayment }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.graceOnInterestCharged">
|
||||||
|
<span fxFlex="40%">Interest free period:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.graceOnInterestCharged }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.inArrearsTolerance">
|
||||||
|
<span fxFlex="40%">Arrears tolerance:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.inArrearsTolerance }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Days in year:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.daysInYearType | find:loanProductsTemplate.daysInYearTypeOptions:'id':'value' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Days in month:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.daysInMonthType | find:loanProductsTemplate.daysInMonthTypeOptions:'id':'value' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Allow fixing of the installment amount:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.canDefineInstallmentAmount ? 'Yes' : 'No' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.graceOnArrearsAgeing">
|
||||||
|
<span fxFlex="40%">Number of days a loan may be overdue before moving into arrears:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.graceOnArrearsAgeing }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.overdueDaysForNPA">
|
||||||
|
<span fxFlex="40%">Maximum number of days a loan may be overdue before becoming a NPA (non performing asset):</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.overdueDaysForNPA }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Account moves out of NPA only after all arrears have been cleared:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.accountMovesOutOfNPAOnlyOnArrearsCompletion ? 'Yes' : 'No' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.principalThresholdForLastInstallment">
|
||||||
|
<span fxFlex="40%">Principal Threshold (%) for Last Instalment:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.principalThresholdForLastInstallment }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Variable Installments allowed:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.allowVariableInstallments ? 'Yes' : 'No' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.allowVariableInstallments">
|
||||||
|
<span fxFlex="40%">Variable Installments:</span>
|
||||||
|
<span fxFlex="60%">{{ '(Min: ' + loanProduct.minimumGap + ', Max: ' + (loanProduct.maximumGap ? loanProduct.maximumGap : loanProduct.minimumGap) + ')' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Allowed to be used for providing Topup Loans:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.canUseForTopup ? 'Yes' : 'No' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="mat-h3" fxFlexFill>Interest Recalculation</h3>
|
||||||
|
|
||||||
|
<mat-divider fxFlexFill></mat-divider>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Recalculate Interest:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.isInterestRecalculationEnabled ? 'Yes' : 'No' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.isInterestRecalculationEnabled" fxLayout="row wrap" fxLayout.lt-md="column">
|
||||||
|
<span fxFlex="40%">Pre-closure interest calculation rule:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.preClosureInterestCalculationStrategy | find:loanProductsTemplate.preClosureInterestCalculationStrategyOptions:'id':'value' }}</span>
|
||||||
|
<span fxFlex="40%">Advance payments adjustment type:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.rescheduleStrategyMethod | find:loanProductsTemplate.rescheduleStrategyTypeOptions:'id':'value' }}</span>
|
||||||
|
<span fxFlex="40%">Interest recalculation compounding on:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.interestRecalculationCompoundingMethod | find:loanProductsTemplate.interestRecalculationCompoundingTypeOptions:'id':'value' }}</span>
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.interestRecalculationCompoundingMethod !== 0" fxLayout="row wrap" fxLayout.lt-md="column">
|
||||||
|
<span fxFlex="40%">Frequency for compounding:</span>
|
||||||
|
<span fxFlex="60%">
|
||||||
|
{{ loanProduct.recalculationCompoundingFrequencyType | find:loanProductsTemplate.interestRecalculationFrequencyTypeOptions:'id':'value' }}
|
||||||
|
<span *ngIf="loanProduct.recalculationCompoundingFrequencyType === 4"> {{ loanProduct.recalculationCompoundingFrequencyNthDayType | find:loanProductsTemplate.interestRecalculationNthDayTypeOptions:'id':'value' }}</span>
|
||||||
|
<span *ngIf="(loanProduct.recalculationCompoundingFrequencyType === 4 && loanProduct.recalculationCompoundingFrequencyNthDayType !== -2) || loanProduct.recalculationCompoundingFrequencyType === 3"> {{ loanProduct.recalculationCompoundingFrequencyDayOfWeekType | find:loanProductsTemplate.interestRecalculationDayOfWeekTypeOptions:'id':'value' }}</span>
|
||||||
|
<span *ngIf="loanProduct.recalculationCompoundingFrequencyType === 4 && loanProduct.recalculationCompoundingFrequencyNthDayType === -2"> {{ loanProduct.recalculationCompoundingFrequencyOnDayType }}</span>
|
||||||
|
</span>
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.recalculationCompoundingFrequencyType !== 1" fxLayout="row wrap" fxLayout.lt-md="column">
|
||||||
|
<span fxFlex="40%">Frequency Interval for compounding:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.recalculationCompoundingFrequencyInterval }}</span>
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.recalculationCompoundingFrequencyDate">
|
||||||
|
<span fxFlex="40%">Compounding Frequency Date:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.recalculationCompoundingFrequencyDate }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
|
||||||
|
<span fxFlex="40%">Frequency for recalculate Outstanding Principal:</span>
|
||||||
|
<span fxFlex="60%">
|
||||||
|
{{ loanProduct.recalculationRestFrequencyType | find:loanProductsTemplate.interestRecalculationFrequencyTypeOptions:'id':'value' }}
|
||||||
|
<span *ngIf="loanProduct.recalculationRestFrequencyType === 4"> {{ loanProduct.recalculationRestFrequencyNthDayType | find:loanProductsTemplate.interestRecalculationNthDayTypeOptions:'id':'value' }}</span>
|
||||||
|
<span *ngIf="(loanProduct.recalculationRestFrequencyType === 4 && loanProduct.recalculationRestFrequencyNthDayType !== -2) || loanProduct.recalculationRestFrequencyType === 3"> {{ loanProduct.recalculationRestFrequencyDayOfWeekType | find:loanProductsTemplate.interestRecalculationDayOfWeekTypeOptions:'id':'value' }}</span>
|
||||||
|
<span *ngIf="loanProduct.recalculationRestFrequencyType === 4 && loanProduct.recalculationRestFrequencyNthDayType === -2"> {{ loanProduct.recalculationRestFrequencyOnDayType }}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.recalculationRestFrequencyType !== 1" fxLayout="row wrap" fxLayout.lt-md="column">
|
||||||
|
<span fxFlex="40%">Frequency Interval for recalculation:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.recalculationRestFrequencyInterval }}</span>
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.recalculationRestFrequencyDate">
|
||||||
|
<span fxFlex="40%">Rest Frequency Date:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.recalculationRestFrequencyDate }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Is Arrears recognization based on original schedule:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.isArrearsBasedOnOriginalSchedule ? 'Yes' : 'No' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="mat-h3" fxFlexFill>Guarantee Requirements</h3>
|
||||||
|
|
||||||
|
<mat-divider fxFlexFill></mat-divider>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Place Guarantee Funds On-Hold:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.holdGuaranteeFunds ? 'Yes' : 'No' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.holdGuaranteeFunds" fxLayout="row wrap" fxLayout.lt-md="column">
|
||||||
|
<span fxFlex="40%">Mandatory Guarantee(%):</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.mandatoryGuarantee }}</span>
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.minimumGuaranteeFromOwnFunds">
|
||||||
|
<span fxFlex="40%">Minimum Guarantee from Own Funds(%):</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.minimumGuaranteeFromOwnFunds }}</span>
|
||||||
|
</div>
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.minimumGuaranteeFromGuarantor">
|
||||||
|
<span fxFlex="40%">Minimum Guarantee from Guarantor Funds(%):</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.minimumGuaranteeFromGuarantor }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="mat-h3" fxFlexFill>Loan Tranche Details</h3>
|
||||||
|
|
||||||
|
<mat-divider fxFlexFill></mat-divider>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Enable Multiple Disbursals:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.multiDisburseLoan ? 'Yes' : 'No' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.multiDisburseLoan" fxLayout="row wrap" fxLayout.lt-md="column">
|
||||||
|
<span fxFlex="40%">Maximum Tranche count:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.maxTrancheCount }}</span>
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.outstandingLoanBalance">
|
||||||
|
<span fxFlex="40%">Maximum allowed outstanding balance:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.outstandingLoanBalance }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="mat-h3" fxFlexFill>Configurable Terms and Settings</h3>
|
||||||
|
|
||||||
|
<mat-divider fxFlexFill></mat-divider>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Allow overriding select terms and settings in loan accounts:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.allowAttributeConfiguration ? 'Yes' : 'No' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.allowAttributeConfiguration" fxLayout="row wrap" fxLayout.lt-md="column">
|
||||||
|
<span fxFlex="40%">Amortization:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.allowAttributeOverrides.amortizationType ? 'Yes' : 'No' }}</span>
|
||||||
|
<span fxFlex="40%">Interest method:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.allowAttributeOverrides.interestType ? 'Yes' : 'No' }}</span>
|
||||||
|
<span fxFlex="40%">Repayment strategy:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.allowAttributeOverrides.transactionProcessingStrategyId ? 'Yes' : 'No' }}</span>
|
||||||
|
<span fxFlex="40%">Interest calculation period:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.allowAttributeOverrides.interestCalculationPeriodType ? 'Yes' : 'No' }}</span>
|
||||||
|
<span fxFlex="40%">Arrears tolerance:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.allowAttributeOverrides.inArrearsTolerance ? 'Yes' : 'No' }}</span>
|
||||||
|
<span fxFlex="40%">Repaid every:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.allowAttributeOverrides.repaymentEvery ? 'Yes' : 'No' }}</span>
|
||||||
|
<span fxFlex="40%">Moratorium:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.allowAttributeOverrides.graceOnPrincipalAndInterestPayment ? 'Yes' : 'No' }}</span>
|
||||||
|
<span fxFlex="40%">Number of days a loan may be overdue before moving into arrears:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.allowAttributeOverrides.graceOnArrearsAgeing ? 'Yes' : 'No' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.charges.length" fxLayout="row wrap" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<h3 class="mat-h3" fxFlexFill>Charges</h3>
|
||||||
|
|
||||||
|
<mat-divider fxFlexFill></mat-divider>
|
||||||
|
|
||||||
|
<table fxFlexFill class="mat-elevation-z1" mat-table [dataSource]="loanProduct.charges | chargesPenaltyFilter:false">
|
||||||
|
|
||||||
|
<ng-container matColumnDef="name">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Name </th>
|
||||||
|
<td mat-cell *matCellDef="let charge">
|
||||||
|
{{ charge.name + ', ' + charge.currency.displaySymbol }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="chargeCalculationType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Type </th>
|
||||||
|
<td mat-cell *matCellDef="let charge">
|
||||||
|
{{ charge.chargeCalculationType.value }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="amount">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Amount </th>
|
||||||
|
<td mat-cell *matCellDef="let charge">
|
||||||
|
{{ charge.amount }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="chargeTimeType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Collected On </th>
|
||||||
|
<td mat-cell *matCellDef="let charge">
|
||||||
|
{{ charge.chargeTimeType.value }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="chargesDisplayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: chargesDisplayedColumns;"></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3 class="mat-h3" fxFlexFill>Overdue Charges</h3>
|
||||||
|
|
||||||
|
<mat-divider fxFlexFill></mat-divider>
|
||||||
|
|
||||||
|
<table fxFlexFill class="mat-elevation-z1" mat-table [dataSource]="loanProduct.charges | chargesPenaltyFilter:true">
|
||||||
|
|
||||||
|
<ng-container matColumnDef="name">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Name </th>
|
||||||
|
<td mat-cell *matCellDef="let overdueCharge">
|
||||||
|
{{ overdueCharge.name + ', ' + overdueCharge.currency.displaySymbol }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="chargeCalculationType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Type </th>
|
||||||
|
<td mat-cell *matCellDef="let overdueCharge">
|
||||||
|
{{ overdueCharge.chargeCalculationType.value }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="amount">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Amount </th>
|
||||||
|
<td mat-cell *matCellDef="let overdueCharge">
|
||||||
|
{{ overdueCharge.amount }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="chargeTimeType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Collected On </th>
|
||||||
|
<td mat-cell *matCellDef="let overdueCharge">
|
||||||
|
{{ overdueCharge.chargeTimeType.value }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="chargesDisplayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: chargesDisplayedColumns;"></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="mat-h3" fxFlexFill>Accounting</h3>
|
||||||
|
|
||||||
|
<mat-divider fxFlexFill></mat-divider>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Type:</span>
|
||||||
|
<span fxFlex="60%">{{ accountingRuleData[loanProduct.accountingRule-1] }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.accountingRule >= 2 && loanProduct.accountingRule <= 4" fxLayout="row wrap" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<h4 fxFlexFill class="mat-h4">Assets</h4>
|
||||||
|
|
||||||
|
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
|
||||||
|
<span fxFlex="40%">Fund source:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.fundSourceAccountId | find:loanProductsTemplate.accountingMappingOptions.assetAccountOptions:'id':'name' }}</span>
|
||||||
|
<span fxFlex="40%">Loan portfolio:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.loanPortfolioAccountId | find:loanProductsTemplate.accountingMappingOptions.assetAccountOptions:'id':'name' }}</span>
|
||||||
|
<div fxFlexFill *ngIf="loanProduct.accountingRule === 3 || loanProduct.accountingRule === 4" fxLayout="row wrap" fxLayout.lt-md="column">
|
||||||
|
<span fxFlex="40%">Interest Receivable:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.receivableInterestAccountId | find:loanProductsTemplate.accountingMappingOptions.assetAccountOptions:'id':'name' }}</span>
|
||||||
|
<span fxFlex="40%">Fees Receivable:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.receivableFeeAccountId | find:loanProductsTemplate.accountingMappingOptions.assetAccountOptions:'id':'name' }}</span>
|
||||||
|
<span fxFlex="40%">Penalties Receivable:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.receivablePenaltyAccountId | find:loanProductsTemplate.accountingMappingOptions.assetAccountOptions:'id':'name' }}</span>
|
||||||
|
</div>
|
||||||
|
<span fxFlex="40%">Transfer in suspense:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.transfersInSuspenseAccountId | find:loanProductsTemplate.accountingMappingOptions.assetAccountOptions:'id':'name' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4 fxFlexFill class="mat-h4">Income</h4>
|
||||||
|
|
||||||
|
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
|
||||||
|
<span fxFlex="40%">Income from Interest:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.interestOnLoanAccountId | find:loanProductsTemplate.accountingMappingOptions.incomeAccountOptions:'id':'name' }}</span>
|
||||||
|
<span fxFlex="40%">Income from fees:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.incomeFromFeeAccountId | find:loanProductsTemplate.accountingMappingOptions.incomeAccountOptions:'id':'name' }}</span>
|
||||||
|
<span fxFlex="40%">Income from penalties:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.incomeFromPenaltyAccountId | find:loanProductsTemplate.accountingMappingOptions.incomeAccountOptions:'id':'name' }}</span>
|
||||||
|
<span fxFlex="40%">Income from Recovery Repayments:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.incomeFromRecoveryAccountId | find:loanProductsTemplate.accountingMappingOptions.incomeAccountOptions:'id':'name' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4 fxFlexFill class="mat-h4">Expenses</h4>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Losses written off:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.writeOffAccountId | find:loanProductsTemplate.accountingMappingOptions.expenseAccountOptions:'id':'name' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4 fxFlexFill class="mat-h4">Liabilities</h4>
|
||||||
|
|
||||||
|
<div fxFlexFill>
|
||||||
|
<span fxFlex="40%">Over payment liability:</span>
|
||||||
|
<span fxFlex="60%">{{ loanProduct.overpaymentLiabilityAccountId | find:loanProductsTemplate.accountingMappingOptions.liabilityAccountOptions:'id':'name' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="loanProduct.advancedAccountingRules && (loanProduct.paymentChannelToFundSourceMappings?.length || loanProduct.feeToIncomeAccountMappings?.length || loanProduct.penaltyToIncomeAccountMappings?.length)" fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<h3 class="mat-h3" fxFlexFill>Advanced Accounting Rules</h3>
|
||||||
|
|
||||||
|
<mat-divider fxFlexFill></mat-divider>
|
||||||
|
|
||||||
|
<div *ngIf="loanProduct.paymentChannelToFundSourceMappings?.length" fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<h4 class="mat-h4" fxFlexFill>Fund Sources for Payment Channels</h4>
|
||||||
|
|
||||||
|
<table fxFlexFill class="mat-elevation-z1" mat-table [dataSource]="loanProduct.paymentChannelToFundSourceMappings">
|
||||||
|
|
||||||
|
<ng-container matColumnDef="paymentTypeId">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Payment Type </th>
|
||||||
|
<td mat-cell *matCellDef="let paymentFundSource">
|
||||||
|
{{ paymentFundSource.paymentTypeId | find:loanProductsTemplate.paymentTypeOptions:'id':'name' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="fundSourceAccountId">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Fund Source </th>
|
||||||
|
<td mat-cell *matCellDef="let paymentFundSource">
|
||||||
|
{{ paymentFundSource.fundSourceAccountId | find:loanProductsTemplate.accountingMappingOptions.assetAccountOptions:'id':'name' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="paymentFundSourceDisplayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: paymentFundSourceDisplayedColumns;"></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="loanProduct.feeToIncomeAccountMappings?.length" fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<h4 class="mat-h4" fxFlexFill>Fees to Specific Income Accounts</h4>
|
||||||
|
|
||||||
|
<table fxFlexFill class="mat-elevation-z1" mat-table [dataSource]="loanProduct.feeToIncomeAccountMappings">
|
||||||
|
|
||||||
|
<ng-container matColumnDef="chargeId">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Fees </th>
|
||||||
|
<td mat-cell *matCellDef="let feesIncome">
|
||||||
|
{{ feesIncome.chargeId | find:loanProductsTemplate.chargeOptions:'id':'name' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="incomeAccountId">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Income Account </th>
|
||||||
|
<td mat-cell *matCellDef="let feesIncome">
|
||||||
|
{{ feesIncome.incomeAccountId | find:loanProductsTemplate.accountingMappingOptions.incomeAccountOptions.concat(loanProductsTemplate.accountingMappingOptions.liabilityAccountOptions):'id':'name' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="feesPenaltyIncomeDisplayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: feesPenaltyIncomeDisplayedColumns;"></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="loanProduct.penaltyToIncomeAccountMappings?.length" fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<h4 class="mat-h4" fxFlexFill>Penalties to Specific Income Accounts</h4>
|
||||||
|
|
||||||
|
<table fxFlexFill class="mat-elevation-z1" mat-table [dataSource]="loanProduct.penaltyToIncomeAccountMappings">
|
||||||
|
|
||||||
|
<ng-container matColumnDef="chargeId">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Penalty </th>
|
||||||
|
<td mat-cell *matCellDef="let penaltyIncome">
|
||||||
|
{{ penaltyIncome.chargeId | find:loanProductsTemplate.penaltyOptions:'id':'name' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="incomeAccountId">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Income Account </th>
|
||||||
|
<td mat-cell *matCellDef="let penaltyIncome">
|
||||||
|
{{ penaltyIncome.incomeAccountId | find:loanProductsTemplate.accountingMappingOptions.incomeAccountOptions:'id':'name' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="feesPenaltyIncomeDisplayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: feesPenaltyIncomeDisplayedColumns;"></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
|
||||||
|
<button mat-raised-button matStepperPrevious>
|
||||||
|
<fa-icon icon="arrow-left"></fa-icon>
|
||||||
|
Previous
|
||||||
|
</button>
|
||||||
|
<button mat-raised-button [routerLink]="['../']">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button mat-raised-button color="primary" (click)="submit.emit()">
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-elevation-z1 {
|
||||||
|
margin: 1em 0 1.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2, h3, h4 {
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-divider {
|
||||||
|
margin: 0 0 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-t {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { LoanProductPreviewStepComponent } from './loan-product-preview-step.component';
|
||||||
|
|
||||||
|
describe('LoanProductPreviewStepComponent', () => {
|
||||||
|
let component: LoanProductPreviewStepComponent;
|
||||||
|
let fixture: ComponentFixture<LoanProductPreviewStepComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ LoanProductPreviewStepComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(LoanProductPreviewStepComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'mifosx-loan-product-preview-step',
|
||||||
|
templateUrl: './loan-product-preview-step.component.html',
|
||||||
|
styleUrls: ['./loan-product-preview-step.component.scss']
|
||||||
|
})
|
||||||
|
export class LoanProductPreviewStepComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() loanProductsTemplate: any;
|
||||||
|
@Input() accountingRuleData: any;
|
||||||
|
@Input() loanProduct: any;
|
||||||
|
@Output() submit = new EventEmitter();
|
||||||
|
|
||||||
|
variationsDisplayedColumns: string[] = ['valueConditionType', 'borrowerCycleNumber', 'minValue', 'defaultValue', 'maxValue'];
|
||||||
|
chargesDisplayedColumns: string[] = ['name', 'chargeCalculationType', 'amount', 'chargeTimeType'];
|
||||||
|
paymentFundSourceDisplayedColumns: string[] = ['paymentTypeId', 'fundSourceAccountId'];
|
||||||
|
feesPenaltyIncomeDisplayedColumns: string[] = ['chargeId', 'incomeAccountId'];
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+466
@@ -0,0 +1,466 @@
|
|||||||
|
<form [formGroup]="loanProductSettingsForm">
|
||||||
|
|
||||||
|
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column" fxLayoutAlign.gt-sm="start center">
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="31%">
|
||||||
|
<mat-label>Amortization</mat-label>
|
||||||
|
<mat-select formControlName="amortizationType" required>
|
||||||
|
<mat-option *ngFor="let amortizationType of amortizationTypeData" [value]="amortizationType.id">
|
||||||
|
{{ amortizationType.value }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Amortization is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="15%" labelPosition="before" formControlName="isEqualAmortization">
|
||||||
|
Is Equal Amortization?
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Interest Method</mat-label>
|
||||||
|
<mat-select formControlName="interestType" required>
|
||||||
|
<mat-option *ngFor="let interestType of interestTypeData" [value]="interestType.id">
|
||||||
|
{{ interestType.value }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Interest Method is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Interest calculation period</mat-label>
|
||||||
|
<mat-select formControlName="interestCalculationPeriodType" required>
|
||||||
|
<mat-option *ngFor="let interestCalculationPeriodType of interestCalculationPeriodTypeData" [value]="interestCalculationPeriodType.id">
|
||||||
|
{{ interestCalculationPeriodType.value }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Interest calculation period is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-checkbox *ngIf="loanProductSettingsForm.value.interestCalculationPeriodType === 1" fxFlex="48%" labelPosition="before" formControlName="allowPartialPeriodInterestCalcualtion">
|
||||||
|
Calculate interest for exact days in partial period
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Repayment strategy</mat-label>
|
||||||
|
<mat-select formControlName="transactionProcessingStrategyId" required>
|
||||||
|
<mat-option *ngFor="let transactionProcessingStrategy of transactionProcessingStrategyData" [value]="transactionProcessingStrategy.id">
|
||||||
|
{{ transactionProcessingStrategy.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Repayment strategy is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<span fxFlex="48%" fxHide.lt-md></span>
|
||||||
|
|
||||||
|
<h4 fxFlex="98%" class="mat-h4">Moratorium</h4>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Grace on principal payment</mat-label>
|
||||||
|
<input type="number" matInput formControlName="graceOnPrincipalPayment">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Grace on interest payment</mat-label>
|
||||||
|
<input type="number" matInput formControlName="graceOnInterestPayment">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-divider fxFlex="98%"></mat-divider>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Interest free period</mat-label>
|
||||||
|
<input type="number" matInput formControlName="graceOnInterestCharged">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Arrears tolerance</mat-label>
|
||||||
|
<input type="number" matInput formControlName="inArrearsTolerance">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Days in year</mat-label>
|
||||||
|
<mat-select formControlName="daysInYearType" required>
|
||||||
|
<mat-option *ngFor="let daysInYearType of daysInYearTypeData" [value]="daysInYearType.id">
|
||||||
|
{{ daysInYearType.value }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Days in year is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Days in month</mat-label>
|
||||||
|
<mat-select formControlName="daysInMonthType" required>
|
||||||
|
<mat-option *ngFor="let daysInMonthType of daysInMonthTypeData" [value]="daysInMonthType.id">
|
||||||
|
{{ daysInMonthType.value }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Days in month is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="98%" labelPosition="before" formControlName="canDefineInstallmentAmount" class="margin-v">
|
||||||
|
Allow fixing of the installment amount
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<h4 fxFlex="48%" class="mat-h4">Number of days a loan may be overdue before moving into arrears</h4>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>On arrears aging</mat-label>
|
||||||
|
<input type="number" matInput formControlName="graceOnArrearsAgeing">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<h4 fxFlex="48%" class="mat-h4">Maximum number of days a loan may be overdue before becoming a NPA (non-performing asset)</h4>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Overdue days for NPA</mat-label>
|
||||||
|
<input type="number" matInput formControlName="overdueDaysForNPA">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="48%" labelPosition="before" formControlName="accountMovesOutOfNPAOnlyOnArrearsCompletion" class="margin-v">
|
||||||
|
Account moves out of NPA only after all arrears have been cleared?
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Principal Threshold (%) for Last Instalment</mat-label>
|
||||||
|
<input type="number" matInput formControlName="principalThresholdForLastInstallment">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="48%" labelPosition="before" formControlName="allowVariableInstallments" class="margin-v">
|
||||||
|
Are Variable Installments allowed?
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<div *ngIf="loanProductSettingsForm.value.allowVariableInstallments" fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<h4 fxFlex="98%" class="mat-h4">Variable Installments</h4>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Minimum gap between Installments</mat-label>
|
||||||
|
<input type="number" matInput formControlName="minimumGap" required>
|
||||||
|
<mat-error>
|
||||||
|
Minimum gap between Installments is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Maximum gap between Installments</mat-label>
|
||||||
|
<input type="number" matInput formControlName="maximumGap">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="48%" labelPosition="before" formControlName="canUseForTopup" class="margin-v">
|
||||||
|
Allowed to be used for providing Topup Loans
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<mat-divider fxFlex="98%"></mat-divider>
|
||||||
|
|
||||||
|
<h3 fxFlex="23%" class="mat-h3">Interest Recalculation</h3>
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="73%" labelPosition="before" formControlName="isInterestRecalculationEnabled" class="margin-b">
|
||||||
|
Recalculate Interest
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<div *ngIf="loanProductSettingsForm.value.isInterestRecalculationEnabled" fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Pre-closure interest calculation rule</mat-label>
|
||||||
|
<mat-select formControlName="preClosureInterestCalculationStrategy" required>
|
||||||
|
<mat-option *ngFor="let preClosureInterestCalculationStrategy of preClosureInterestCalculationStrategyData" [value]="preClosureInterestCalculationStrategy.id">
|
||||||
|
{{ preClosureInterestCalculationStrategy.value }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Pre-closure interest calculation rule is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Advance payments adjustment type</mat-label>
|
||||||
|
<mat-select formControlName="rescheduleStrategyMethod" required>
|
||||||
|
<mat-option *ngFor="let rescheduleStrategyType of rescheduleStrategyTypeData" [value]="rescheduleStrategyType.id">
|
||||||
|
{{ rescheduleStrategyType.value }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Advance payments adjustment type is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Interest recalculation compounding on</mat-label>
|
||||||
|
<mat-select formControlName="interestRecalculationCompoundingMethod" required>
|
||||||
|
<mat-option *ngFor="let interestRecalculationCompoundingType of interestRecalculationCompoundingTypeData" [value]="interestRecalculationCompoundingType.id">
|
||||||
|
{{ interestRecalculationCompoundingType.value }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Interest recalculation compounding on is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<span fxFlex="48%" fxHide.lt-md></span>
|
||||||
|
|
||||||
|
<div *ngIf="loanProductSettingsForm.value.interestRecalculationCompoundingMethod !== 0 && loanProductSettingsForm.value.recalculationCompoundingFrequencyType !== 1" fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Frequency Interval for compounding</mat-label>
|
||||||
|
<input type="number" matInput formControlName="recalculationCompoundingFrequencyInterval" required>
|
||||||
|
<mat-error>
|
||||||
|
Frequency Interval for compounding is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Compounding Frequency Date</mat-label>
|
||||||
|
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="recalculationCompoundingFrequencyDatePicker" formControlName="recalculationCompoundingFrequencyDate">
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="recalculationCompoundingFrequencyDatePicker"></mat-datepicker-toggle>
|
||||||
|
<mat-datepicker #recalculationCompoundingFrequencyDatePicker></mat-datepicker>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="loanProductSettingsForm.value.recalculationRestFrequencyType !== 1" fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Frequency Interval for recalculation</mat-label>
|
||||||
|
<input type="number" matInput formControlName="recalculationRestFrequencyInterval" required>
|
||||||
|
<mat-error>
|
||||||
|
Frequency Interval for recalculation is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Rest Frequency Date</mat-label>
|
||||||
|
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="recalculationRestFrequencyDatePicker" formControlName="recalculationRestFrequencyDate">
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="recalculationRestFrequencyDatePicker"></mat-datepicker-toggle>
|
||||||
|
<mat-datepicker #recalculationRestFrequencyDatePicker></mat-datepicker>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="loanProductSettingsForm.value.interestRecalculationCompoundingMethod !== 0" fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="31%">
|
||||||
|
<mat-label>Frequency for compounding</mat-label>
|
||||||
|
<mat-select formControlName="recalculationCompoundingFrequencyType" required>
|
||||||
|
<mat-option *ngFor="let interestRecalculationFrequencyType of interestRecalculationFrequencyTypeData" [value]="interestRecalculationFrequencyType.id">
|
||||||
|
{{ interestRecalculationFrequencyType.value }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div *ngIf="loanProductSettingsForm.value.recalculationCompoundingFrequencyType === 4" fxFlex="31%" fxLayout="row" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<mat-form-field fxFlexFill>
|
||||||
|
<mat-select formControlName="recalculationCompoundingFrequencyNthDayType">
|
||||||
|
<mat-option *ngFor="let interestRecalculationNthDayType of interestRecalculationNthDayTypeData" [value]="interestRecalculationNthDayType.id">
|
||||||
|
{{ interestRecalculationNthDayType.value }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="(loanProductSettingsForm.value.recalculationCompoundingFrequencyType === 4 && loanProductSettingsForm.value.recalculationCompoundingFrequencyNthDayType !== -2) || loanProductSettingsForm.value.recalculationCompoundingFrequencyType === 3" fxFlex="31%" fxLayout="row" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<mat-form-field fxFlexFill>
|
||||||
|
<mat-select formControlName="recalculationCompoundingFrequencyDayOfWeekType">
|
||||||
|
<mat-option *ngFor="let interestRecalculationDayOfWeekType of interestRecalculationDayOfWeekTypeData" [value]="interestRecalculationDayOfWeekType.id">
|
||||||
|
{{ interestRecalculationDayOfWeekType.value }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="loanProductSettingsForm.value.recalculationCompoundingFrequencyType === 4 && loanProductSettingsForm.value.recalculationCompoundingFrequencyNthDayType === -2" fxFlex="31%" fxLayout="row" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<mat-form-field fxFlexFill>
|
||||||
|
<mat-select formControlName="recalculationCompoundingFrequencyOnDayType">
|
||||||
|
<mat-option *ngFor="let interestRecalculationOnDayType of interestRecalculationOnDayTypeData" [value]="interestRecalculationOnDayType">
|
||||||
|
{{ interestRecalculationOnDayType }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="31%">
|
||||||
|
<mat-label>Frequency for recalculate Outstanding Principal</mat-label>
|
||||||
|
<mat-select formControlName="recalculationRestFrequencyType" required>
|
||||||
|
<mat-option *ngFor="let interestRecalculationFrequencyType of interestRecalculationFrequencyTypeData" [value]="interestRecalculationFrequencyType.id">
|
||||||
|
{{ interestRecalculationFrequencyType.value }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Frequency for recalculate Outstanding Principal is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div *ngIf="loanProductSettingsForm.value.recalculationRestFrequencyType === 4" fxFlex="31%" fxLayout="row" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<mat-form-field fxFlexFill>
|
||||||
|
<mat-select formControlName="recalculationRestFrequencyNthDayType">
|
||||||
|
<mat-option *ngFor="let interestRecalculationNthDayType of interestRecalculationNthDayTypeData" [value]="interestRecalculationNthDayType.id">
|
||||||
|
{{ interestRecalculationNthDayType.value }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="(loanProductSettingsForm.value.recalculationRestFrequencyType === 4 && loanProductSettingsForm.value.recalculationRestFrequencyNthDayType !== -2) || loanProductSettingsForm.value.recalculationRestFrequencyType === 3" fxFlex="31%" fxLayout="row" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<mat-form-field fxFlexFill>
|
||||||
|
<mat-select formControlName="recalculationRestFrequencyDayOfWeekType">
|
||||||
|
<mat-option *ngFor="let interestRecalculationDayOfWeekType of interestRecalculationDayOfWeekTypeData" [value]="interestRecalculationDayOfWeekType.id">
|
||||||
|
{{ interestRecalculationDayOfWeekType.value }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="loanProductSettingsForm.value.recalculationRestFrequencyType === 4 && loanProductSettingsForm.value.recalculationRestFrequencyNthDayType === -2" fxFlex="31%" fxLayout="row" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<mat-form-field fxFlexFill>
|
||||||
|
<mat-select formControlName="recalculationRestFrequencyOnDayType">
|
||||||
|
<mat-option *ngFor="let interestRecalculationOnDayType of interestRecalculationOnDayTypeData" [value]="interestRecalculationOnDayType">
|
||||||
|
{{ interestRecalculationOnDayType }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="98%" labelPosition="before" formControlName="isArrearsBasedOnOriginalSchedule" class="margin-v">
|
||||||
|
Is Arrears recognization based on original schedule?
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-divider fxFlex="98%"></mat-divider>
|
||||||
|
|
||||||
|
<h3 fxFlex="23%" class="mat-h3">Guarantee Requirements</h3>
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="73%" labelPosition="before" formControlName="holdGuaranteeFunds" class="margin-b">
|
||||||
|
Place Guarantee Funds On-Hold?
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<div *ngIf="loanProductSettingsForm.value.holdGuaranteeFunds" fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="31%">
|
||||||
|
<mat-label>Mandatory Guarantee(%)</mat-label>
|
||||||
|
<input type="number" matInput formControlName="mandatoryGuarantee" required>
|
||||||
|
<mat-error>
|
||||||
|
Mandatory Guarantee is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="31%">
|
||||||
|
<mat-label>Minimum Guarantee from Own Funds(%)</mat-label>
|
||||||
|
<input type="number" matInput formControlName="minimumGuaranteeFromOwnFunds">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="31%">
|
||||||
|
<mat-label>Minimum Guarantee from Guarantor Funds(%)</mat-label>
|
||||||
|
<input type="number" matInput formControlName="minimumGuaranteeFromGuarantor">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-divider fxFlex="98%"></mat-divider>
|
||||||
|
|
||||||
|
<h3 fxFlex="23%" class="mat-h3">Loan Tranche Details</h3>
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="73%" labelPosition="before" formControlName="multiDisburseLoan" class="margin-b">
|
||||||
|
Enable Multiple Disbursals
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<div *ngIf="loanProductSettingsForm.value.multiDisburseLoan" fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Maximum Tranche count</mat-label>
|
||||||
|
<input type="number" matInput formControlName="maxTrancheCount" required>
|
||||||
|
<mat-error>
|
||||||
|
Maximum Tranche count is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Maximum allowed outstanding balance</mat-label>
|
||||||
|
<input type="number" matInput formControlName="outstandingLoanBalance">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-divider fxFlex="98%"></mat-divider>
|
||||||
|
|
||||||
|
<h3 fxFlex="23%" class="mat-h3">Configurable Terms and Settings</h3>
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="73%" labelPosition="before" formControlName="allowAttributeConfiguration" class="margin-b">
|
||||||
|
Allow overriding select terms and settings in loan accounts
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<div *ngIf="loanProductSettingsForm.value.allowAttributeConfiguration" formGroupName="allowAttributeOverrides" fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="48%" labelPosition="before" formControlName="amortizationType" class="margin-v">
|
||||||
|
Amortization
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="48%" labelPosition="before" formControlName="interestType" class="margin-v">
|
||||||
|
Interest method
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="48%" labelPosition="before" formControlName="transactionProcessingStrategyId" class="margin-v">
|
||||||
|
Repayment strategy
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="48%" labelPosition="before" formControlName="interestCalculationPeriodType" class="margin-v">
|
||||||
|
Interest calculation period
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="48%" labelPosition="before" formControlName="inArrearsTolerance" class="margin-v">
|
||||||
|
Arrears tolerance
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="48%" labelPosition="before" formControlName="repaymentEvery" class="margin-v">
|
||||||
|
Repaid every
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="48%" labelPosition="before" formControlName="graceOnPrincipalAndInterestPayment" class="margin-v">
|
||||||
|
Moratorium
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="48%" labelPosition="before" formControlName="graceOnArrearsAgeing" class="margin-v">
|
||||||
|
Number of days a loan may be overdue before moving into arrears
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
|
||||||
|
<button mat-raised-button matStepperPrevious>
|
||||||
|
<fa-icon icon="arrow-left"></fa-icon>
|
||||||
|
Previous
|
||||||
|
</button>
|
||||||
|
<button mat-raised-button matStepperNext>
|
||||||
|
Next
|
||||||
|
<fa-icon icon="arrow-right"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
h3, h4 {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-divider {
|
||||||
|
margin: 1em 0 2em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-v {
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-b {
|
||||||
|
margin: 0 0 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-t {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { LoanProductSettingsStepComponent } from './loan-product-settings-step.component';
|
||||||
|
|
||||||
|
describe('LoanProductSettingsStepComponent', () => {
|
||||||
|
let component: LoanProductSettingsStepComponent;
|
||||||
|
let fixture: ComponentFixture<LoanProductSettingsStepComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ LoanProductSettingsStepComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(LoanProductSettingsStepComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
+308
@@ -0,0 +1,308 @@
|
|||||||
|
import { Component, OnInit, Input } from '@angular/core';
|
||||||
|
import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms';
|
||||||
|
import { DatePipe } from '@angular/common';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'mifosx-loan-product-settings-step',
|
||||||
|
templateUrl: './loan-product-settings-step.component.html',
|
||||||
|
styleUrls: ['./loan-product-settings-step.component.scss']
|
||||||
|
})
|
||||||
|
export class LoanProductSettingsStepComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() loanProductsTemplate: any;
|
||||||
|
@Input() isLinkedToFloatingInterestRates: FormControl;
|
||||||
|
|
||||||
|
loanProductSettingsForm: FormGroup;
|
||||||
|
|
||||||
|
amortizationTypeData: any;
|
||||||
|
interestTypeData: any;
|
||||||
|
interestCalculationPeriodTypeData: any;
|
||||||
|
transactionProcessingStrategyData: any;
|
||||||
|
daysInYearTypeData: any;
|
||||||
|
daysInMonthTypeData: any;
|
||||||
|
preClosureInterestCalculationStrategyData: any;
|
||||||
|
rescheduleStrategyTypeData: any;
|
||||||
|
interestRecalculationCompoundingTypeData: any;
|
||||||
|
interestRecalculationFrequencyTypeData: any;
|
||||||
|
interestRecalculationNthDayTypeData: any;
|
||||||
|
interestRecalculationDayOfWeekTypeData: any;
|
||||||
|
interestRecalculationOnDayTypeData: any;
|
||||||
|
|
||||||
|
minDate = new Date(2000, 0, 1);
|
||||||
|
maxDate = new Date(new Date().setFullYear(new Date().getFullYear() + 10));
|
||||||
|
|
||||||
|
constructor(private formBuilder: FormBuilder,
|
||||||
|
private datePipe: DatePipe) {
|
||||||
|
this.createLoanProductSettingsForm();
|
||||||
|
this.setConditionalControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.isLinkedToFloatingInterestRates.valueChanges
|
||||||
|
.subscribe((isLinkedToFloatingInterestRates: any) => {
|
||||||
|
if (isLinkedToFloatingInterestRates) {
|
||||||
|
this.loanProductSettingsForm.get('isInterestRecalculationEnabled').setValue(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.amortizationTypeData = this.loanProductsTemplate.amortizationTypeOptions;
|
||||||
|
this.interestTypeData = this.loanProductsTemplate.interestTypeOptions;
|
||||||
|
this.interestCalculationPeriodTypeData = this.loanProductsTemplate.interestCalculationPeriodTypeOptions;
|
||||||
|
this.transactionProcessingStrategyData = this.loanProductsTemplate.transactionProcessingStrategyOptions;
|
||||||
|
this.daysInYearTypeData = this.loanProductsTemplate.daysInYearTypeOptions;
|
||||||
|
this.daysInMonthTypeData = this.loanProductsTemplate.daysInMonthTypeOptions;
|
||||||
|
this.preClosureInterestCalculationStrategyData = this.loanProductsTemplate.preClosureInterestCalculationStrategyOptions;
|
||||||
|
this.rescheduleStrategyTypeData = this.loanProductsTemplate.rescheduleStrategyTypeOptions;
|
||||||
|
this.interestRecalculationCompoundingTypeData = this.loanProductsTemplate.interestRecalculationCompoundingTypeOptions;
|
||||||
|
this.interestRecalculationFrequencyTypeData = this.loanProductsTemplate.interestRecalculationFrequencyTypeOptions;
|
||||||
|
this.interestRecalculationNthDayTypeData = this.loanProductsTemplate.interestRecalculationNthDayTypeOptions;
|
||||||
|
this.interestRecalculationNthDayTypeData.push({ 'id': -2, 'code': 'onDay', 'value': 'on day' });
|
||||||
|
this.interestRecalculationDayOfWeekTypeData = this.loanProductsTemplate.interestRecalculationDayOfWeekTypeOptions;
|
||||||
|
this.interestRecalculationOnDayTypeData = Array.from({ length: 28 }, (_, index) => index + 1);
|
||||||
|
|
||||||
|
this.loanProductSettingsForm.patchValue({
|
||||||
|
'amortizationType': this.amortizationTypeData[0].id,
|
||||||
|
'interestType': this.interestTypeData[0].id,
|
||||||
|
'interestCalculationPeriodType': this.interestCalculationPeriodTypeData[0].id,
|
||||||
|
'transactionProcessingStrategyId': this.transactionProcessingStrategyData[0].id,
|
||||||
|
'daysInYearType': this.daysInYearTypeData[0].id,
|
||||||
|
'daysInMonthType': this.daysInMonthTypeData[0].id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createLoanProductSettingsForm() {
|
||||||
|
this.loanProductSettingsForm = this.formBuilder.group({
|
||||||
|
'amortizationType': ['', Validators.required],
|
||||||
|
'interestType': ['', Validators.required],
|
||||||
|
'isEqualAmortization': [false],
|
||||||
|
'interestCalculationPeriodType': ['', Validators.required],
|
||||||
|
'transactionProcessingStrategyId': ['', Validators.required],
|
||||||
|
'graceOnPrincipalPayment': [''],
|
||||||
|
'graceOnInterestPayment': [''],
|
||||||
|
'graceOnInterestCharged': [''],
|
||||||
|
'inArrearsTolerance': [''],
|
||||||
|
'daysInYearType': ['', Validators.required],
|
||||||
|
'daysInMonthType': ['', Validators.required],
|
||||||
|
'canDefineInstallmentAmount': [false],
|
||||||
|
'graceOnArrearsAgeing': [''],
|
||||||
|
'overdueDaysForNPA': [''],
|
||||||
|
'accountMovesOutOfNPAOnlyOnArrearsCompletion': [false],
|
||||||
|
'principalThresholdForLastInstallment': [''],
|
||||||
|
'allowVariableInstallments': [false],
|
||||||
|
'canUseForTopup': [false],
|
||||||
|
'isInterestRecalculationEnabled': [false],
|
||||||
|
'holdGuaranteeFunds': [false],
|
||||||
|
'multiDisburseLoan': [false],
|
||||||
|
'allowAttributeConfiguration': [true],
|
||||||
|
'allowAttributeOverrides': this.formBuilder.group({
|
||||||
|
'amortizationType': [true],
|
||||||
|
'interestType': [true],
|
||||||
|
'transactionProcessingStrategyId': [true],
|
||||||
|
'interestCalculationPeriodType': [true],
|
||||||
|
'inArrearsTolerance': [true],
|
||||||
|
'repaymentEvery': [true],
|
||||||
|
'graceOnPrincipalAndInterestPayment': [true],
|
||||||
|
'graceOnArrearsAgeing': [true]
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setConditionalControls() {
|
||||||
|
const allowAttributeOverrides = this.loanProductSettingsForm.get('allowAttributeOverrides');
|
||||||
|
|
||||||
|
this.loanProductSettingsForm.get('interestCalculationPeriodType').valueChanges
|
||||||
|
.subscribe((interestCalculationPeriodType: any) => {
|
||||||
|
if (interestCalculationPeriodType === 1) {
|
||||||
|
this.loanProductSettingsForm.addControl('allowPartialPeriodInterestCalcualtion', new FormControl(false));
|
||||||
|
} else {
|
||||||
|
this.loanProductSettingsForm.removeControl('allowPartialPeriodInterestCalcualtion');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.loanProductSettingsForm.get('allowVariableInstallments').valueChanges
|
||||||
|
.subscribe((allowVariableInstallments: any) => {
|
||||||
|
if (allowVariableInstallments) {
|
||||||
|
this.loanProductSettingsForm.addControl('minimumGap', new FormControl('', Validators.required));
|
||||||
|
this.loanProductSettingsForm.addControl('maximumGap', new FormControl(''));
|
||||||
|
} else {
|
||||||
|
this.loanProductSettingsForm.removeControl('minimumGap');
|
||||||
|
this.loanProductSettingsForm.removeControl('maximumGap');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.loanProductSettingsForm.get('isInterestRecalculationEnabled').valueChanges
|
||||||
|
.subscribe((isInterestRecalculationEnabled: any) => {
|
||||||
|
if (isInterestRecalculationEnabled) {
|
||||||
|
this.loanProductSettingsForm.addControl('preClosureInterestCalculationStrategy', new FormControl(this.preClosureInterestCalculationStrategyData[0].id, Validators.required));
|
||||||
|
this.loanProductSettingsForm.addControl('rescheduleStrategyMethod', new FormControl(this.rescheduleStrategyTypeData[0].id, Validators.required));
|
||||||
|
this.loanProductSettingsForm.addControl('interestRecalculationCompoundingMethod', new FormControl(this.interestRecalculationCompoundingTypeData[0].id, Validators.required));
|
||||||
|
this.loanProductSettingsForm.addControl('recalculationRestFrequencyType', new FormControl(this.interestRecalculationFrequencyTypeData[0].id, Validators.required));
|
||||||
|
this.loanProductSettingsForm.addControl('isArrearsBasedOnOriginalSchedule', new FormControl(''));
|
||||||
|
|
||||||
|
// interestRecalculationCompoundingMethod (initially 0)
|
||||||
|
this.loanProductSettingsForm.get('interestRecalculationCompoundingMethod').valueChanges
|
||||||
|
.subscribe((interestRecalculationCompoundingMethod: any) => {
|
||||||
|
if (interestRecalculationCompoundingMethod !== 0) {
|
||||||
|
this.loanProductSettingsForm.addControl('recalculationCompoundingFrequencyType', new FormControl(this.interestRecalculationFrequencyTypeData[0].id, Validators.required));
|
||||||
|
|
||||||
|
// recalculationCompoundingFrequencyType (initially 1)
|
||||||
|
this.loanProductSettingsForm.get('recalculationCompoundingFrequencyType').valueChanges
|
||||||
|
.subscribe((recalculationCompoundingFrequencyType: any) => {
|
||||||
|
if (recalculationCompoundingFrequencyType !== 1) {
|
||||||
|
this.loanProductSettingsForm.addControl('recalculationCompoundingFrequencyInterval', new FormControl('', Validators.required));
|
||||||
|
this.loanProductSettingsForm.addControl('recalculationCompoundingFrequencyDate', new FormControl(''));
|
||||||
|
} else {
|
||||||
|
this.loanProductSettingsForm.removeControl('recalculationCompoundingFrequencyInterval');
|
||||||
|
this.loanProductSettingsForm.removeControl('recalculationCompoundingFrequencyDate');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recalculationCompoundingFrequencyType === 3) {
|
||||||
|
this.loanProductSettingsForm.addControl('recalculationCompoundingFrequencyDayOfWeekType', new FormControl(''));
|
||||||
|
} else {
|
||||||
|
this.loanProductSettingsForm.removeControl('recalculationCompoundingFrequencyDayOfWeekType');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recalculationCompoundingFrequencyType === 4) {
|
||||||
|
this.loanProductSettingsForm.addControl('recalculationCompoundingFrequencyNthDayType', new FormControl(''));
|
||||||
|
this.loanProductSettingsForm.addControl('recalculationCompoundingFrequencyDayOfWeekType', new FormControl(''));
|
||||||
|
|
||||||
|
// recalculationCompoundingFrequencyNthDayType
|
||||||
|
this.loanProductSettingsForm.get('recalculationCompoundingFrequencyNthDayType').valueChanges
|
||||||
|
.subscribe((recalculationCompoundingFrequencyNthDayType: any) => {
|
||||||
|
if (recalculationCompoundingFrequencyNthDayType === -2) {
|
||||||
|
this.loanProductSettingsForm.addControl('recalculationCompoundingFrequencyOnDayType', new FormControl(''));
|
||||||
|
this.loanProductSettingsForm.removeControl('recalculationCompoundingFrequencyDayOfWeekType');
|
||||||
|
} else {
|
||||||
|
this.loanProductSettingsForm.addControl('recalculationCompoundingFrequencyDayOfWeekType', new FormControl(''));
|
||||||
|
this.loanProductSettingsForm.removeControl('recalculationCompoundingFrequencyOnDayType');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.loanProductSettingsForm.removeControl('recalculationCompoundingFrequencyNthDayType');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.loanProductSettingsForm.removeControl('recalculationCompoundingFrequencyType');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// recalculationRestFrequencyType
|
||||||
|
this.loanProductSettingsForm.get('recalculationRestFrequencyType').valueChanges
|
||||||
|
.subscribe((recalculationRestFrequencyType: any) => {
|
||||||
|
if (recalculationRestFrequencyType !== 1) {
|
||||||
|
this.loanProductSettingsForm.addControl('recalculationRestFrequencyInterval', new FormControl('', Validators.required));
|
||||||
|
this.loanProductSettingsForm.addControl('recalculationRestFrequencyDate', new FormControl(''));
|
||||||
|
} else {
|
||||||
|
this.loanProductSettingsForm.removeControl('recalculationRestFrequencyInterval');
|
||||||
|
this.loanProductSettingsForm.removeControl('recalculationRestFrequencyDate');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recalculationRestFrequencyType === 3) {
|
||||||
|
this.loanProductSettingsForm.addControl('recalculationRestFrequencyDayOfWeekType', new FormControl(''));
|
||||||
|
} else {
|
||||||
|
this.loanProductSettingsForm.removeControl('recalculationRestFrequencyDayOfWeekType');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recalculationRestFrequencyType === 4) {
|
||||||
|
this.loanProductSettingsForm.addControl('recalculationRestFrequencyNthDayType', new FormControl(''));
|
||||||
|
this.loanProductSettingsForm.addControl('recalculationRestFrequencyDayOfWeekType', new FormControl(''));
|
||||||
|
|
||||||
|
// recalculationRestFrequencyNthDayType
|
||||||
|
this.loanProductSettingsForm.get('recalculationRestFrequencyNthDayType').valueChanges
|
||||||
|
.subscribe((recalculationRestFrequencyNthDayType: any) => {
|
||||||
|
if (recalculationRestFrequencyNthDayType === -2) {
|
||||||
|
this.loanProductSettingsForm.addControl('recalculationRestFrequencyOnDayType', new FormControl(''));
|
||||||
|
this.loanProductSettingsForm.removeControl('recalculationRestFrequencyDayOfWeekType');
|
||||||
|
} else {
|
||||||
|
this.loanProductSettingsForm.addControl('recalculationRestFrequencyDayOfWeekType', new FormControl(''));
|
||||||
|
this.loanProductSettingsForm.removeControl('recalculationRestFrequencyOnDayType');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.loanProductSettingsForm.removeControl('recalculationRestFrequencyNthDayType');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.loanProductSettingsForm.removeControl('preClosureInterestCalculationStrategy');
|
||||||
|
this.loanProductSettingsForm.removeControl('rescheduleStrategyMethod');
|
||||||
|
this.loanProductSettingsForm.removeControl('interestRecalculationCompoundingMethod');
|
||||||
|
this.loanProductSettingsForm.removeControl('recalculationRestFrequencyType');
|
||||||
|
this.loanProductSettingsForm.removeControl('isArrearsBasedOnOriginalSchedule');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.loanProductSettingsForm.get('holdGuaranteeFunds').valueChanges
|
||||||
|
.subscribe(holdGuaranteeFunds => {
|
||||||
|
if (holdGuaranteeFunds) {
|
||||||
|
this.loanProductSettingsForm.addControl('mandatoryGuarantee', new FormControl('', Validators.required));
|
||||||
|
this.loanProductSettingsForm.addControl('minimumGuaranteeFromOwnFunds', new FormControl(''));
|
||||||
|
this.loanProductSettingsForm.addControl('minimumGuaranteeFromGuarantor', new FormControl(''));
|
||||||
|
} else {
|
||||||
|
this.loanProductSettingsForm.removeControl('mandatoryGuarantee');
|
||||||
|
this.loanProductSettingsForm.removeControl('minimumGuaranteeFromOwnFunds');
|
||||||
|
this.loanProductSettingsForm.removeControl('minimumGuaranteeFromGuarantor');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.loanProductSettingsForm.get('multiDisburseLoan').valueChanges
|
||||||
|
.subscribe(multiDisburseLoan => {
|
||||||
|
if (multiDisburseLoan) {
|
||||||
|
this.loanProductSettingsForm.addControl('maxTrancheCount', new FormControl('', Validators.required));
|
||||||
|
this.loanProductSettingsForm.addControl('outstandingLoanBalance', new FormControl(''));
|
||||||
|
} else {
|
||||||
|
this.loanProductSettingsForm.removeControl('maxTrancheCount');
|
||||||
|
this.loanProductSettingsForm.removeControl('outstandingLoanBalance');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.loanProductSettingsForm.get('allowAttributeConfiguration').valueChanges
|
||||||
|
.subscribe((allowAttributeConfiguration: any) => {
|
||||||
|
if (allowAttributeConfiguration) {
|
||||||
|
allowAttributeOverrides.patchValue({
|
||||||
|
'amortizationType': true,
|
||||||
|
'interestType': true,
|
||||||
|
'transactionProcessingStrategyId': true,
|
||||||
|
'interestCalculationPeriodType': true,
|
||||||
|
'inArrearsTolerance': true,
|
||||||
|
'repaymentEvery': true,
|
||||||
|
'graceOnPrincipalAndInterestPayment': true,
|
||||||
|
'graceOnArrearsAgeing': true
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
allowAttributeOverrides.patchValue({
|
||||||
|
'amortizationType': false,
|
||||||
|
'interestType': false,
|
||||||
|
'transactionProcessingStrategyId': false,
|
||||||
|
'interestCalculationPeriodType': false,
|
||||||
|
'inArrearsTolerance': false,
|
||||||
|
'repaymentEvery': false,
|
||||||
|
'graceOnPrincipalAndInterestPayment': false,
|
||||||
|
'graceOnArrearsAgeing': false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get loanProductSettings() {
|
||||||
|
const prevCompoundingFrequencyDate: Date = this.loanProductSettingsForm.value.recalculationCompoundingFrequencyDate;
|
||||||
|
const prevRestFrequencyDate: Date = this.loanProductSettingsForm.value.recalculationRestFrequencyDate;
|
||||||
|
// TODO: Update once language and date settings are setup
|
||||||
|
const dateFormat = 'yyyy-MM-dd';
|
||||||
|
if (prevCompoundingFrequencyDate) {
|
||||||
|
this.loanProductSettingsForm.patchValue({
|
||||||
|
recalculationCompoundingFrequencyDate: this.datePipe.transform(prevCompoundingFrequencyDate, dateFormat)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (prevRestFrequencyDate) {
|
||||||
|
this.loanProductSettingsForm.patchValue({
|
||||||
|
recalculationRestFrequencyDate: this.datePipe.transform(prevRestFrequencyDate, dateFormat)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this.loanProductSettingsForm.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+386
@@ -0,0 +1,386 @@
|
|||||||
|
<form [formGroup]="loanProductTermsForm">
|
||||||
|
|
||||||
|
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<h4 fxFlex="98%" class="mat-h4">Principal</h4>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="31%">
|
||||||
|
<mat-label>Minimum</mat-label>
|
||||||
|
<input type="number" matInput formControlName="minPrincipal">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="31%">
|
||||||
|
<mat-label>Default</mat-label>
|
||||||
|
<input type="number" matInput formControlName="principal" required>
|
||||||
|
<mat-error>
|
||||||
|
Default Principal is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="31%">
|
||||||
|
<mat-label>Maximum</mat-label>
|
||||||
|
<input type="number" matInput formControlName="maxPrincipal">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<h4 fxFlex="98%" class="mat-h4">Number of repayments</h4>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="31%">
|
||||||
|
<mat-label>Minimum</mat-label>
|
||||||
|
<input type="number" matInput formControlName="minNumberOfRepayments">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="31%">
|
||||||
|
<mat-label>Default</mat-label>
|
||||||
|
<input type="number" matInput formControlName="numberOfRepayments" required>
|
||||||
|
<mat-error>
|
||||||
|
Default number of repayments is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="31%">
|
||||||
|
<mat-label>Maximum</mat-label>
|
||||||
|
<input type="number" matInput formControlName="maxNumberOfRepayments">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-divider fxFlex="98%"></mat-divider>
|
||||||
|
|
||||||
|
<h3 fxFlex="23%" class="mat-h3">Interest Rates</h3>
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="73%" labelPosition="before" formControlName="isLinkedToFloatingInterestRates">
|
||||||
|
Is Linked to floating interest rates?
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<div *ngIf="!loanProductTermsForm.value.isLinkedToFloatingInterestRates" fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<h4 fxFlex="98%" class="mat-h4">Nominal interest rate</h4>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="23%">
|
||||||
|
<mat-label>Minimum</mat-label>
|
||||||
|
<input type="number" matInput formControlName="minInterestRatePerPeriod">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="23%">
|
||||||
|
<mat-label>Default</mat-label>
|
||||||
|
<input type="number" matInput formControlName="interestRatePerPeriod" required>
|
||||||
|
<mat-error>
|
||||||
|
Default nominal interest rate is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="23%">
|
||||||
|
<mat-label>Maximum</mat-label>
|
||||||
|
<input type="number" matInput formControlName="maxInterestRatePerPeriod">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="23%">
|
||||||
|
<mat-label>Frequency</mat-label>
|
||||||
|
<mat-select formControlName="interestRateFrequencyType" required>
|
||||||
|
<mat-option *ngFor="let interestRateFrequencyType of interestRateFrequencyTypeData" [value]="interestRateFrequencyType.id">
|
||||||
|
{{ interestRateFrequencyType.value }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Nominal interest rate frequency is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="loanProductTermsForm.value.isLinkedToFloatingInterestRates" fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column" fxLayoutAlign.gt-sm="start center">
|
||||||
|
|
||||||
|
<h4 fxFlex="98%" class="mat-h4">Floating interest rate</h4>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="31%">
|
||||||
|
<mat-label>Floating Rate</mat-label>
|
||||||
|
<mat-select formControlName="floatingRatesId" required>
|
||||||
|
<mat-option *ngFor="let floatingRate of floatingRateData" [value]="floatingRate.id">
|
||||||
|
{{ floatingRate.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Floating rate is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="31%">
|
||||||
|
<mat-label>Differential Rate</mat-label>
|
||||||
|
<input type="number" matInput formControlName="interestRateDifferential" required>
|
||||||
|
<mat-error>
|
||||||
|
Differential rate is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="31%" labelPosition="before" formControlName="isFloatingInterestRateCalculationAllowed">
|
||||||
|
Is Floating calculation allowed?
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="31%">
|
||||||
|
<mat-label>Minimum</mat-label>
|
||||||
|
<input type="number" matInput formControlName="minDifferentialLendingRate" required>
|
||||||
|
<mat-error>
|
||||||
|
Minimum interest rate is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="31%">
|
||||||
|
<mat-label>Default</mat-label>
|
||||||
|
<input type="number" matInput formControlName="defaultDifferentialLendingRate" required>
|
||||||
|
<mat-error>
|
||||||
|
Default interest rate is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="31%">
|
||||||
|
<mat-label>Maximum</mat-label>
|
||||||
|
<input type="number" matInput formControlName="maxDifferentialLendingRate" required>
|
||||||
|
<mat-error>
|
||||||
|
Maximum interest rate is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-divider fxFlex="98%"></mat-divider>
|
||||||
|
|
||||||
|
<h3 fxFlex="23%" class="mat-h3">Variations</h3>
|
||||||
|
|
||||||
|
<mat-checkbox fxFlex="73%" labelPosition="before" formControlName="useBorrowerCycle">
|
||||||
|
Terms vary based on loan cycle
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<div *ngIf="loanProductTermsForm.value.useBorrowerCycle" fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||||
|
|
||||||
|
<h4 fxFlex="23%" class="mat-h4">Principal by loan cycle</h4>
|
||||||
|
|
||||||
|
<div fxFlex="73%">
|
||||||
|
<button type="button" mat-raised-button color="primary" (click)="addVariationsForBorrowerCycle('Principal', principalVariationsForBorrowerCycle)">
|
||||||
|
<fa-icon icon="plus"></fa-icon>
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table fxFlex="98%" class="mat-elevation-z1" mat-table [dataSource]="principalVariationsForBorrowerCycle.value" *ngIf="principalVariationsForBorrowerCycle.value.length">
|
||||||
|
|
||||||
|
<ng-container matColumnDef="valueConditionType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Condition </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.valueConditionType | find:valueConditionTypeData:'id':'value' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="borrowerCycleNumber">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Loan Cycle </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.borrowerCycleNumber }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="minValue">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Minimum </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.minValue }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="defaultValue">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Default </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.defaultValue }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="maxValue">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Maximum </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.maxValue }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="actions">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||||
|
<td mat-cell *matCellDef="let variation; let i = index">
|
||||||
|
<button mat-icon-button color="primary" (click)="editVariationsForBorrowerCycle('Principal', principalVariationsForBorrowerCycle, i)">
|
||||||
|
<fa-icon icon="edit"></fa-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button color="warn" (click)="deleteVariationsForBorrowerCycle(principalVariationsForBorrowerCycle, i)">
|
||||||
|
<fa-icon icon="trash"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h4 fxFlex="23%" class="mat-h4">Number of repayments by loan cycle</h4>
|
||||||
|
|
||||||
|
<div fxFlex="73%">
|
||||||
|
<button type="button" mat-raised-button color="primary" (click)="addVariationsForBorrowerCycle('NumberOfRepayments', numberOfRepaymentVariationsForBorrowerCycle)">
|
||||||
|
<fa-icon icon="plus"></fa-icon>
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table fxFlex="98%" class="mat-elevation-z1" mat-table [dataSource]="numberOfRepaymentVariationsForBorrowerCycle.value" *ngIf="numberOfRepaymentVariationsForBorrowerCycle.value.length">
|
||||||
|
|
||||||
|
<ng-container matColumnDef="valueConditionType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Condition </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.valueConditionType | find:valueConditionTypeData:'id':'value' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="borrowerCycleNumber">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Loan Cycle </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.borrowerCycleNumber }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="minValue">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Minimum </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.minValue }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="defaultValue">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Default </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.defaultValue }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="maxValue">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Maximum </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.maxValue }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="actions">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||||
|
<td mat-cell *matCellDef="let variation; let i = index">
|
||||||
|
<button mat-icon-button color="primary" (click)="editVariationsForBorrowerCycle('NumberOfRepayments', numberOfRepaymentVariationsForBorrowerCycle, i)">
|
||||||
|
<fa-icon icon="edit"></fa-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button color="warn" (click)="deleteVariationsForBorrowerCycle(numberOfRepaymentVariationsForBorrowerCycle, i)">
|
||||||
|
<fa-icon icon="trash"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h4 fxFlex="23%" class="mat-h4">Nominal interest rate by loan cycle</h4>
|
||||||
|
|
||||||
|
<div fxFlex="73%">
|
||||||
|
<button type="button" mat-raised-button color="primary" (click)="addVariationsForBorrowerCycle('NominalInterestRate', interestRateVariationsForBorrowerCycle)">
|
||||||
|
<fa-icon icon="plus"></fa-icon>
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table fxFlex="98%" class="mat-elevation-z1" mat-table [dataSource]="interestRateVariationsForBorrowerCycle.value" *ngIf="interestRateVariationsForBorrowerCycle.value.length">
|
||||||
|
|
||||||
|
<ng-container matColumnDef="valueConditionType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Condition </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.valueConditionType | find:valueConditionTypeData:'id':'value' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="borrowerCycleNumber">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Loan Cycle </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.borrowerCycleNumber }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="minValue">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Minimum </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.minValue }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="defaultValue">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Default </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.defaultValue }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="maxValue">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Maximum </th>
|
||||||
|
<td mat-cell *matCellDef="let variation">
|
||||||
|
{{ variation.maxValue }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="actions">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||||
|
<td mat-cell *matCellDef="let element; let i = index">
|
||||||
|
<button mat-icon-button color="primary" (click)="editVariationsForBorrowerCycle('NominalInterestRate', interestRateVariationsForBorrowerCycle, i)">
|
||||||
|
<fa-icon icon="edit"></fa-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button color="warn" (click)="deleteVariationsForBorrowerCycle(interestRateVariationsForBorrowerCycle, i)">
|
||||||
|
<fa-icon icon="trash"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-divider fxFlex="98%"></mat-divider>
|
||||||
|
|
||||||
|
<h4 fxFlex="98%" class="mat-h4">Repaid every</h4>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Frequency</mat-label>
|
||||||
|
<input type="number" matInput formControlName="repaymentEvery" required>
|
||||||
|
<mat-error>
|
||||||
|
Repaid every frequency is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Type</mat-label>
|
||||||
|
<mat-select formControlName="repaymentFrequencyType" required>
|
||||||
|
<mat-option *ngFor="let repaymentFrequencyType of repaymentFrequencyTypeData" [value]="repaymentFrequencyType.id">
|
||||||
|
{{ repaymentFrequencyType.value }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
Repaid every type is <strong>required</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field fxFlex="48%">
|
||||||
|
<mat-label>Minimum days between disbursal and first repayment date</mat-label>
|
||||||
|
<input type="number" matInput formControlName="minimumDaysBetweenDisbursalAndFirstRepayment">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
|
||||||
|
<button mat-raised-button matStepperPrevious>
|
||||||
|
<fa-icon icon="arrow-left"></fa-icon>
|
||||||
|
Previous
|
||||||
|
</button>
|
||||||
|
<button mat-raised-button matStepperNext>
|
||||||
|
Next
|
||||||
|
<fa-icon icon="arrow-right"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-elevation-z1 {
|
||||||
|
margin: 1em 0 1.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3, h4 {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-divider {
|
||||||
|
margin: 1em 0 2em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-t {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { LoanProductTermsStepComponent } from './loan-product-terms-step.component';
|
||||||
|
|
||||||
|
describe('LoanProductTermsStepComponent', () => {
|
||||||
|
let component: LoanProductTermsStepComponent;
|
||||||
|
let fixture: ComponentFixture<LoanProductTermsStepComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ LoanProductTermsStepComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(LoanProductTermsStepComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
+208
@@ -0,0 +1,208 @@
|
|||||||
|
import { Component, OnInit, Input } from '@angular/core';
|
||||||
|
import { FormGroup, FormBuilder, Validators, FormArray, FormControl } from '@angular/forms';
|
||||||
|
import { MatDialog } from '@angular/material';
|
||||||
|
|
||||||
|
import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component';
|
||||||
|
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
|
||||||
|
|
||||||
|
import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-base';
|
||||||
|
import { InputBase } from 'app/shared/form-dialog/formfield/model/input-base';
|
||||||
|
import { SelectBase } from 'app/shared/form-dialog/formfield/model/select-base';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'mifosx-loan-product-terms-step',
|
||||||
|
templateUrl: './loan-product-terms-step.component.html',
|
||||||
|
styleUrls: ['./loan-product-terms-step.component.scss']
|
||||||
|
})
|
||||||
|
export class LoanProductTermsStepComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() loanProductsTemplate: any;
|
||||||
|
|
||||||
|
loanProductTermsForm: FormGroup;
|
||||||
|
|
||||||
|
valueConditionTypeData: any;
|
||||||
|
floatingRateData: any;
|
||||||
|
interestRateFrequencyTypeData: any;
|
||||||
|
repaymentFrequencyTypeData: any;
|
||||||
|
|
||||||
|
displayedColumns: string[] = ['valueConditionType', 'borrowerCycleNumber', 'minValue', 'defaultValue', 'maxValue', 'actions'];
|
||||||
|
|
||||||
|
constructor(private formBuilder: FormBuilder,
|
||||||
|
public dialog: MatDialog) {
|
||||||
|
this.createLoanProductTermsForm();
|
||||||
|
this.setConditionalControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.valueConditionTypeData = this.loanProductsTemplate.valueConditionTypeOptions;
|
||||||
|
this.floatingRateData = this.loanProductsTemplate.floatingRateOptions;
|
||||||
|
this.interestRateFrequencyTypeData = this.loanProductsTemplate.interestRateFrequencyTypeOptions;
|
||||||
|
this.repaymentFrequencyTypeData = this.loanProductsTemplate.repaymentFrequencyTypeOptions;
|
||||||
|
this.loanProductTermsForm.patchValue({
|
||||||
|
'interestRateFrequencyType': this.interestRateFrequencyTypeData[0].id,
|
||||||
|
'repaymentFrequencyType': this.repaymentFrequencyTypeData[0].id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createLoanProductTermsForm() {
|
||||||
|
this.loanProductTermsForm = this.formBuilder.group({
|
||||||
|
'useBorrowerCycle': [false],
|
||||||
|
'minPrincipal': [''],
|
||||||
|
'principal': ['', Validators.required],
|
||||||
|
'maxPrincipal': [''],
|
||||||
|
'minNumberOfRepayments': [''],
|
||||||
|
'numberOfRepayments': ['', Validators.required],
|
||||||
|
'maxNumberOfRepayments': [''],
|
||||||
|
'isLinkedToFloatingInterestRates': [false],
|
||||||
|
'minInterestRatePerPeriod': [''],
|
||||||
|
'interestRatePerPeriod': ['', Validators.required],
|
||||||
|
'maxInterestRatePerPeriod': [''],
|
||||||
|
'interestRateFrequencyType': ['', Validators.required],
|
||||||
|
'repaymentEvery': ['', Validators.required],
|
||||||
|
'repaymentFrequencyType': ['', Validators.required],
|
||||||
|
'minimumDaysBetweenDisbursalAndFirstRepayment': ['']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setConditionalControls() {
|
||||||
|
this.loanProductTermsForm.get('isLinkedToFloatingInterestRates').valueChanges
|
||||||
|
.subscribe(isLinkedToFloatingInterestRates => {
|
||||||
|
if (isLinkedToFloatingInterestRates) {
|
||||||
|
this.loanProductTermsForm.removeControl('minInterestRatePerPeriod');
|
||||||
|
this.loanProductTermsForm.removeControl('interestRatePerPeriod');
|
||||||
|
this.loanProductTermsForm.removeControl('maxInterestRatePerPeriod');
|
||||||
|
this.loanProductTermsForm.removeControl('interestRateFrequencyType');
|
||||||
|
this.loanProductTermsForm.addControl('floatingRatesId', new FormControl('', Validators.required));
|
||||||
|
this.loanProductTermsForm.addControl('interestRateDifferential', new FormControl('', Validators.required));
|
||||||
|
this.loanProductTermsForm.addControl('isFloatingInterestRateCalculationAllowed', new FormControl(false));
|
||||||
|
this.loanProductTermsForm.addControl('minDifferentialLendingRate', new FormControl('', Validators.required));
|
||||||
|
this.loanProductTermsForm.addControl('defaultDifferentialLendingRate', new FormControl('', Validators.required));
|
||||||
|
this.loanProductTermsForm.addControl('maxDifferentialLendingRate', new FormControl('', Validators.required));
|
||||||
|
} else {
|
||||||
|
this.loanProductTermsForm.addControl('minInterestRatePerPeriod', new FormControl(''));
|
||||||
|
this.loanProductTermsForm.addControl('interestRatePerPeriod', new FormControl('', Validators.required));
|
||||||
|
this.loanProductTermsForm.addControl('maxInterestRatePerPeriod', new FormControl(''));
|
||||||
|
this.loanProductTermsForm.addControl('interestRateFrequencyType', new FormControl(this.interestRateFrequencyTypeData[0].id, Validators.required));
|
||||||
|
this.loanProductTermsForm.removeControl('floatingRatesId');
|
||||||
|
this.loanProductTermsForm.removeControl('interestRateDifferential');
|
||||||
|
this.loanProductTermsForm.removeControl('isFloatingInterestRateCalculationAllowed');
|
||||||
|
this.loanProductTermsForm.removeControl('minDifferentialLendingRate');
|
||||||
|
this.loanProductTermsForm.removeControl('defaultDifferentialLendingRate');
|
||||||
|
this.loanProductTermsForm.removeControl('maxDifferentialLendingRate');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.loanProductTermsForm.get('useBorrowerCycle').valueChanges
|
||||||
|
.subscribe(useBorrowerCycle => {
|
||||||
|
if (useBorrowerCycle) {
|
||||||
|
this.loanProductTermsForm.addControl('principalVariationsForBorrowerCycle', this.formBuilder.array([]));
|
||||||
|
this.loanProductTermsForm.addControl('numberOfRepaymentVariationsForBorrowerCycle', this.formBuilder.array([]));
|
||||||
|
this.loanProductTermsForm.addControl('interestRateVariationsForBorrowerCycle', this.formBuilder.array([]));
|
||||||
|
} else {
|
||||||
|
this.loanProductTermsForm.removeControl('principalVariationsForBorrowerCycle');
|
||||||
|
this.loanProductTermsForm.removeControl('numberOfRepaymentVariationsForBorrowerCycle');
|
||||||
|
this.loanProductTermsForm.removeControl('interestRateVariationsForBorrowerCycle');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get principalVariationsForBorrowerCycle(): FormArray {
|
||||||
|
return this.loanProductTermsForm.get('principalVariationsForBorrowerCycle') as FormArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
get numberOfRepaymentVariationsForBorrowerCycle(): FormArray {
|
||||||
|
return this.loanProductTermsForm.get('numberOfRepaymentVariationsForBorrowerCycle') as FormArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
get interestRateVariationsForBorrowerCycle(): FormArray {
|
||||||
|
return this.loanProductTermsForm.get('interestRateVariationsForBorrowerCycle') as FormArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
addVariationsForBorrowerCycle(formType: string, variationsForBorrowerCycleFormArray: FormArray) {
|
||||||
|
const data = this.getData(formType);
|
||||||
|
const addVariationsForBorrowerCycleDialogRef = this.dialog.open(FormDialogComponent, { data });
|
||||||
|
addVariationsForBorrowerCycleDialogRef.afterClosed().subscribe((response: any) => {
|
||||||
|
if (response.data) {
|
||||||
|
variationsForBorrowerCycleFormArray.push(response.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
editVariationsForBorrowerCycle(formType: string, variationsForBorrowerCycleFormArray: FormArray, index: number) {
|
||||||
|
const data = { ...this.getData(formType, variationsForBorrowerCycleFormArray.at(index).value), layout: { addButtonText: 'Edit' } };
|
||||||
|
const addVariationsForBorrowerCycleDialogRef = this.dialog.open(FormDialogComponent, { data });
|
||||||
|
addVariationsForBorrowerCycleDialogRef.afterClosed().subscribe((response: any) => {
|
||||||
|
if (response.data) {
|
||||||
|
variationsForBorrowerCycleFormArray.at(index).patchValue(response.data.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteVariationsForBorrowerCycle(variationsForBorrowerCycleFormArray: FormArray, index: number) {
|
||||||
|
const deleteVariationsForBorrowerCycleDialogRef = this.dialog.open(DeleteDialogComponent, {
|
||||||
|
data: { deleteContext: `this` }
|
||||||
|
});
|
||||||
|
deleteVariationsForBorrowerCycleDialogRef.afterClosed().subscribe((response: any) => {
|
||||||
|
if (response.delete) {
|
||||||
|
variationsForBorrowerCycleFormArray.removeAt(index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getData(formType: string, values?: any) {
|
||||||
|
switch (formType) {
|
||||||
|
case 'Principal': return { title: 'Principal by loan cycle', formfields: this.getFormfields(values) };
|
||||||
|
case 'NumberOfRepayments': return { title: 'Number of repayments by loan cycle', formfields: this.getFormfields(values) };
|
||||||
|
case 'NominalInterestRate': return { title: 'Nominal interest rate by loan cycle', formfields: this.getFormfields(values) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormfields(values?: any) {
|
||||||
|
const formfields: FormfieldBase[] = [
|
||||||
|
new SelectBase({
|
||||||
|
controlName: 'valueConditionType',
|
||||||
|
label: 'Condition',
|
||||||
|
value: values ? values.valueConditionType : this.valueConditionTypeData[0].id,
|
||||||
|
options: { label: 'value', value: 'id', data: this.valueConditionTypeData },
|
||||||
|
required: true,
|
||||||
|
order: 1
|
||||||
|
}),
|
||||||
|
new InputBase({
|
||||||
|
controlName: 'borrowerCycleNumber',
|
||||||
|
label: 'Loan Cycle',
|
||||||
|
value: values ? values.borrowerCycleNumber : undefined,
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
order: 2
|
||||||
|
}),
|
||||||
|
new InputBase({
|
||||||
|
controlName: 'minValue',
|
||||||
|
label: 'Minimum',
|
||||||
|
value: values ? values.minValue : undefined,
|
||||||
|
type: 'number',
|
||||||
|
order: 3
|
||||||
|
}),
|
||||||
|
new InputBase({
|
||||||
|
controlName: 'defaultValue',
|
||||||
|
label: 'Default',
|
||||||
|
value: values ? values.defaultValue : undefined,
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
order: 4
|
||||||
|
}),
|
||||||
|
new InputBase({
|
||||||
|
controlName: 'maxValue',
|
||||||
|
label: 'Maximum',
|
||||||
|
value: values ? values.maxValue : undefined,
|
||||||
|
type: 'number',
|
||||||
|
order: 5
|
||||||
|
})
|
||||||
|
];
|
||||||
|
return formfields;
|
||||||
|
}
|
||||||
|
|
||||||
|
get loanProductTerms() {
|
||||||
|
return this.loanProductTermsForm.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/** Angular Imports */
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Resolve } from '@angular/router';
|
||||||
|
|
||||||
|
/** rxjs Imports */
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
/** Custom Services */
|
||||||
|
import { ProductsService } from '../products.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class LoanProductsTemplateResolver implements Resolve<Object> {
|
||||||
|
|
||||||
|
constructor(private productsService: ProductsService) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the loan products template data.
|
||||||
|
* @returns {Observable<any>}
|
||||||
|
*/
|
||||||
|
resolve(): Observable<any> {
|
||||||
|
return this.productsService.getLoanProductsTemplate();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import { extract } from '../core/i18n/i18n.service';
|
|||||||
/** Custom Components */
|
/** Custom Components */
|
||||||
import { ProductsComponent } from './products.component';
|
import { ProductsComponent } from './products.component';
|
||||||
import { LoanProductsComponent } from './loan-products/loan-products.component';
|
import { LoanProductsComponent } from './loan-products/loan-products.component';
|
||||||
|
import { CreateLoanProductComponent } from './loan-products/create-loan-product/create-loan-product.component';
|
||||||
import { ManageTaxConfigurationsComponent } from './manage-tax-configurations/manage-tax-configurations.component';
|
import { ManageTaxConfigurationsComponent } from './manage-tax-configurations/manage-tax-configurations.component';
|
||||||
import { RecurringDepositProductsComponent } from './recurring-deposit-products/recurring-deposit-products.component';
|
import { RecurringDepositProductsComponent } from './recurring-deposit-products/recurring-deposit-products.component';
|
||||||
import { ChargesComponent } from './charges/charges.component';
|
import { ChargesComponent } from './charges/charges.component';
|
||||||
@@ -20,6 +21,7 @@ import { ManageTaxComponentsComponent } from './manage-tax-components/manage-tax
|
|||||||
|
|
||||||
/** Custom Resolvers */
|
/** Custom Resolvers */
|
||||||
import { LoanProductsResolver } from './loan-products/loan-products.resolver';
|
import { LoanProductsResolver } from './loan-products/loan-products.resolver';
|
||||||
|
import { LoanProductsTemplateResolver } from './loan-products/loan-products-template.resolver';
|
||||||
import { RecurringDepositProductsResolver } from './recurring-deposit-products/recurring-deposit-products.resolver';
|
import { RecurringDepositProductsResolver } from './recurring-deposit-products/recurring-deposit-products.resolver';
|
||||||
import { ChargesResolver } from './charges/charges.resolver';
|
import { ChargesResolver } from './charges/charges.resolver';
|
||||||
import { FixedDepositProductsResolver } from './fixed-deposit-products/fixed-deposit-products.resolver';
|
import { FixedDepositProductsResolver } from './fixed-deposit-products/fixed-deposit-products.resolver';
|
||||||
@@ -47,6 +49,14 @@ const routes: Routes = [
|
|||||||
resolve: {
|
resolve: {
|
||||||
loanProducts: LoanProductsResolver
|
loanProducts: LoanProductsResolver
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'create',
|
||||||
|
component: CreateLoanProductComponent,
|
||||||
|
data: { title: extract('Create Loan Product'), breadcrumb: 'Create' },
|
||||||
|
resolve: {
|
||||||
|
loanProductsTemplate: LoanProductsTemplateResolver
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -115,6 +125,7 @@ const routes: Routes = [
|
|||||||
exports: [RouterModule],
|
exports: [RouterModule],
|
||||||
providers: [
|
providers: [
|
||||||
LoanProductsResolver,
|
LoanProductsResolver,
|
||||||
|
LoanProductsTemplateResolver,
|
||||||
RecurringDepositProductsResolver,
|
RecurringDepositProductsResolver,
|
||||||
ChargesResolver,
|
ChargesResolver,
|
||||||
FixedDepositProductsResolver,
|
FixedDepositProductsResolver,
|
||||||
|
|||||||
@@ -10,6 +10,14 @@ import { PipesModule } from 'app/pipes/pipes.module';
|
|||||||
/** Custom Components */
|
/** Custom Components */
|
||||||
import { ProductsComponent } from './products.component';
|
import { ProductsComponent } from './products.component';
|
||||||
import { LoanProductsComponent } from './loan-products/loan-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 { 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';
|
||||||
|
import { LoanProductAccountingStepComponent } from './loan-products/loan-product-stepper/loan-product-accounting-step/loan-product-accounting-step.component';
|
||||||
|
import { LoanProductPreviewStepComponent } from './loan-products/loan-product-stepper/loan-product-preview-step/loan-product-preview-step.component';
|
||||||
|
import { CreateLoanProductComponent } from './loan-products/create-loan-product/create-loan-product.component';
|
||||||
import { ManageTaxConfigurationsComponent } from './manage-tax-configurations/manage-tax-configurations.component';
|
import { ManageTaxConfigurationsComponent } from './manage-tax-configurations/manage-tax-configurations.component';
|
||||||
import { RecurringDepositProductsComponent } from './recurring-deposit-products/recurring-deposit-products.component';
|
import { RecurringDepositProductsComponent } from './recurring-deposit-products/recurring-deposit-products.component';
|
||||||
import { ChargesComponent } from './charges/charges.component';
|
import { ChargesComponent } from './charges/charges.component';
|
||||||
@@ -31,6 +39,14 @@ import { ManageTaxComponentsComponent } from './manage-tax-components/manage-tax
|
|||||||
declarations: [
|
declarations: [
|
||||||
ProductsComponent,
|
ProductsComponent,
|
||||||
LoanProductsComponent,
|
LoanProductsComponent,
|
||||||
|
LoanProductDetailsStepComponent,
|
||||||
|
LoanProductCurrencyStepComponent,
|
||||||
|
LoanProductTermsStepComponent,
|
||||||
|
LoanProductSettingsStepComponent,
|
||||||
|
LoanProductChargesStepComponent,
|
||||||
|
LoanProductAccountingStepComponent,
|
||||||
|
LoanProductPreviewStepComponent,
|
||||||
|
CreateLoanProductComponent,
|
||||||
ManageTaxConfigurationsComponent,
|
ManageTaxConfigurationsComponent,
|
||||||
RecurringDepositProductsComponent,
|
RecurringDepositProductsComponent,
|
||||||
ChargesComponent,
|
ChargesComponent,
|
||||||
|
|||||||
@@ -25,6 +25,14 @@ export class ProductsService {
|
|||||||
return this.http.get('/loanproducts');
|
return this.http.get('/loanproducts');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createLoanProduct(loanProduct: string): Observable<any> {
|
||||||
|
return this.http.post('/loanproducts', loanProduct);
|
||||||
|
}
|
||||||
|
|
||||||
|
getLoanProductsTemplate(): Observable<any> {
|
||||||
|
return this.http.get('/loanproducts/template');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {Observable<any>} Recurring deposit products data
|
* @returns {Observable<any>} Recurring deposit products data
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -7,12 +7,15 @@ import { library } from '@fortawesome/fontawesome-svg-core';
|
|||||||
import {
|
import {
|
||||||
faAnchor,
|
faAnchor,
|
||||||
faArchive,
|
faArchive,
|
||||||
|
faArrowLeft,
|
||||||
|
faArrowRight,
|
||||||
faBars,
|
faBars,
|
||||||
faBell,
|
faBell,
|
||||||
faBriefcase,
|
faBriefcase,
|
||||||
faBuilding,
|
faBuilding,
|
||||||
faCalendar,
|
faCalendar,
|
||||||
faChartBar,
|
faChartBar,
|
||||||
|
faCheck,
|
||||||
faCheckCircle,
|
faCheckCircle,
|
||||||
faChevronDown,
|
faChevronDown,
|
||||||
faChevronLeft,
|
faChevronLeft,
|
||||||
@@ -26,6 +29,7 @@ import {
|
|||||||
faEdit,
|
faEdit,
|
||||||
faEnvelope,
|
faEnvelope,
|
||||||
faExclamationCircle,
|
faExclamationCircle,
|
||||||
|
faExclamationTriangle,
|
||||||
faEye,
|
faEye,
|
||||||
faEyeSlash,
|
faEyeSlash,
|
||||||
faFileAlt,
|
faFileAlt,
|
||||||
@@ -44,6 +48,7 @@ import {
|
|||||||
faMinusCircle,
|
faMinusCircle,
|
||||||
faMoneyBillAlt,
|
faMoneyBillAlt,
|
||||||
faMoneyCheck,
|
faMoneyCheck,
|
||||||
|
faPencilAlt,
|
||||||
faPlay,
|
faPlay,
|
||||||
faPlus,
|
faPlus,
|
||||||
faPlusCircle,
|
faPlusCircle,
|
||||||
@@ -77,12 +82,15 @@ import {
|
|||||||
library.add(
|
library.add(
|
||||||
faAnchor,
|
faAnchor,
|
||||||
faArchive,
|
faArchive,
|
||||||
|
faArrowLeft,
|
||||||
|
faArrowRight,
|
||||||
faBars,
|
faBars,
|
||||||
faBell,
|
faBell,
|
||||||
faBriefcase,
|
faBriefcase,
|
||||||
faBuilding,
|
faBuilding,
|
||||||
faCalendar,
|
faCalendar,
|
||||||
faChartBar,
|
faChartBar,
|
||||||
|
faCheck,
|
||||||
faCheckCircle,
|
faCheckCircle,
|
||||||
faChevronDown,
|
faChevronDown,
|
||||||
faChevronLeft,
|
faChevronLeft,
|
||||||
@@ -96,6 +104,7 @@ library.add(
|
|||||||
faEdit,
|
faEdit,
|
||||||
faEnvelope,
|
faEnvelope,
|
||||||
faExclamationCircle,
|
faExclamationCircle,
|
||||||
|
faExclamationTriangle,
|
||||||
faEye,
|
faEye,
|
||||||
faEyeSlash,
|
faEyeSlash,
|
||||||
faFileAlt,
|
faFileAlt,
|
||||||
@@ -114,6 +123,7 @@ library.add(
|
|||||||
faMinusCircle,
|
faMinusCircle,
|
||||||
faMoneyBillAlt,
|
faMoneyBillAlt,
|
||||||
faMoneyCheck,
|
faMoneyCheck,
|
||||||
|
faPencilAlt,
|
||||||
faPlay,
|
faPlay,
|
||||||
faPlus,
|
faPlus,
|
||||||
faPlusCircle,
|
faPlusCircle,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/** Angular Imports */
|
/** Angular Imports */
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { LayoutModule } from '@angular/cdk/layout';
|
import { LayoutModule } from '@angular/cdk/layout';
|
||||||
|
import { STEPPER_GLOBAL_OPTIONS } from '@angular/cdk/stepper';
|
||||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
import {
|
import {
|
||||||
MAT_FORM_FIELD_DEFAULT_OPTIONS,
|
MAT_FORM_FIELD_DEFAULT_OPTIONS,
|
||||||
@@ -86,6 +87,13 @@ import {
|
|||||||
{
|
{
|
||||||
provide: MAT_FORM_FIELD_DEFAULT_OPTIONS,
|
provide: MAT_FORM_FIELD_DEFAULT_OPTIONS,
|
||||||
useValue: { appearance: 'standard' }
|
useValue: { appearance: 'standard' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: STEPPER_GLOBAL_OPTIONS,
|
||||||
|
useValue: {
|
||||||
|
showError: true,
|
||||||
|
displayDefaultIndicatorType: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -71,3 +71,5 @@ $status-approved: #0f36e4;
|
|||||||
$status-overpaid: #800080;
|
$status-overpaid: #800080;
|
||||||
$status-active-overdue: #ff0000;
|
$status-active-overdue: #ff0000;
|
||||||
$status-matured: #ff4000;
|
$status-matured: #ff4000;
|
||||||
|
|
||||||
|
$state-done: #00ce00;
|
||||||
|
|||||||
@@ -1704,3 +1704,7 @@ textarea.noresize {
|
|||||||
.status-matured {
|
.status-matured {
|
||||||
color: $status-matured;
|
color: $status-matured;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mat-step-header .mat-step-icon-state-done {
|
||||||
|
background-color: $state-done;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user