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 { StatusLookupPipe } from './status-lookup.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({
|
||||
imports: [
|
||||
CommonModule
|
||||
],
|
||||
declarations: [StatusLookupPipe, AccountsFilterPipe],
|
||||
providers: [StatusLookupPipe, AccountsFilterPipe],
|
||||
exports: [StatusLookupPipe, AccountsFilterPipe]
|
||||
declarations: [StatusLookupPipe, AccountsFilterPipe, ChargesFilterPipe, ChargesPenaltyFilterPipe, FindPipe],
|
||||
providers: [StatusLookupPipe, AccountsFilterPipe, ChargesFilterPipe, ChargesPenaltyFilterPipe, FindPipe],
|
||||
exports: [StatusLookupPipe, AccountsFilterPipe, ChargesFilterPipe, ChargesPenaltyFilterPipe, FindPipe]
|
||||
})
|
||||
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 */
|
||||
import { ProductsComponent } from './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 { RecurringDepositProductsComponent } from './recurring-deposit-products/recurring-deposit-products.component';
|
||||
import { ChargesComponent } from './charges/charges.component';
|
||||
@@ -20,6 +21,7 @@ import { ManageTaxComponentsComponent } from './manage-tax-components/manage-tax
|
||||
|
||||
/** Custom Resolvers */
|
||||
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 { ChargesResolver } from './charges/charges.resolver';
|
||||
import { FixedDepositProductsResolver } from './fixed-deposit-products/fixed-deposit-products.resolver';
|
||||
@@ -47,6 +49,14 @@ const routes: Routes = [
|
||||
resolve: {
|
||||
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],
|
||||
providers: [
|
||||
LoanProductsResolver,
|
||||
LoanProductsTemplateResolver,
|
||||
RecurringDepositProductsResolver,
|
||||
ChargesResolver,
|
||||
FixedDepositProductsResolver,
|
||||
|
||||
@@ -10,6 +10,14 @@ import { PipesModule } from 'app/pipes/pipes.module';
|
||||
/** Custom Components */
|
||||
import { ProductsComponent } from './products.component';
|
||||
import { LoanProductsComponent } from './loan-products/loan-products.component';
|
||||
import { LoanProductDetailsStepComponent } from './loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component';
|
||||
import { LoanProductCurrencyStepComponent } from './loan-products/loan-product-stepper/loan-product-currency-step/loan-product-currency-step.component';
|
||||
import { 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 { RecurringDepositProductsComponent } from './recurring-deposit-products/recurring-deposit-products.component';
|
||||
import { ChargesComponent } from './charges/charges.component';
|
||||
@@ -31,6 +39,14 @@ import { ManageTaxComponentsComponent } from './manage-tax-components/manage-tax
|
||||
declarations: [
|
||||
ProductsComponent,
|
||||
LoanProductsComponent,
|
||||
LoanProductDetailsStepComponent,
|
||||
LoanProductCurrencyStepComponent,
|
||||
LoanProductTermsStepComponent,
|
||||
LoanProductSettingsStepComponent,
|
||||
LoanProductChargesStepComponent,
|
||||
LoanProductAccountingStepComponent,
|
||||
LoanProductPreviewStepComponent,
|
||||
CreateLoanProductComponent,
|
||||
ManageTaxConfigurationsComponent,
|
||||
RecurringDepositProductsComponent,
|
||||
ChargesComponent,
|
||||
|
||||
@@ -25,6 +25,14 @@ export class ProductsService {
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -7,12 +7,15 @@ import { library } from '@fortawesome/fontawesome-svg-core';
|
||||
import {
|
||||
faAnchor,
|
||||
faArchive,
|
||||
faArrowLeft,
|
||||
faArrowRight,
|
||||
faBars,
|
||||
faBell,
|
||||
faBriefcase,
|
||||
faBuilding,
|
||||
faCalendar,
|
||||
faChartBar,
|
||||
faCheck,
|
||||
faCheckCircle,
|
||||
faChevronDown,
|
||||
faChevronLeft,
|
||||
@@ -26,6 +29,7 @@ import {
|
||||
faEdit,
|
||||
faEnvelope,
|
||||
faExclamationCircle,
|
||||
faExclamationTriangle,
|
||||
faEye,
|
||||
faEyeSlash,
|
||||
faFileAlt,
|
||||
@@ -44,6 +48,7 @@ import {
|
||||
faMinusCircle,
|
||||
faMoneyBillAlt,
|
||||
faMoneyCheck,
|
||||
faPencilAlt,
|
||||
faPlay,
|
||||
faPlus,
|
||||
faPlusCircle,
|
||||
@@ -77,12 +82,15 @@ import {
|
||||
library.add(
|
||||
faAnchor,
|
||||
faArchive,
|
||||
faArrowLeft,
|
||||
faArrowRight,
|
||||
faBars,
|
||||
faBell,
|
||||
faBriefcase,
|
||||
faBuilding,
|
||||
faCalendar,
|
||||
faChartBar,
|
||||
faCheck,
|
||||
faCheckCircle,
|
||||
faChevronDown,
|
||||
faChevronLeft,
|
||||
@@ -96,6 +104,7 @@ library.add(
|
||||
faEdit,
|
||||
faEnvelope,
|
||||
faExclamationCircle,
|
||||
faExclamationTriangle,
|
||||
faEye,
|
||||
faEyeSlash,
|
||||
faFileAlt,
|
||||
@@ -114,6 +123,7 @@ library.add(
|
||||
faMinusCircle,
|
||||
faMoneyBillAlt,
|
||||
faMoneyCheck,
|
||||
faPencilAlt,
|
||||
faPlay,
|
||||
faPlus,
|
||||
faPlusCircle,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/** Angular Imports */
|
||||
import { NgModule } from '@angular/core';
|
||||
import { LayoutModule } from '@angular/cdk/layout';
|
||||
import { STEPPER_GLOBAL_OPTIONS } from '@angular/cdk/stepper';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import {
|
||||
MAT_FORM_FIELD_DEFAULT_OPTIONS,
|
||||
@@ -86,6 +87,13 @@ import {
|
||||
{
|
||||
provide: MAT_FORM_FIELD_DEFAULT_OPTIONS,
|
||||
useValue: { appearance: 'standard' }
|
||||
},
|
||||
{
|
||||
provide: STEPPER_GLOBAL_OPTIONS,
|
||||
useValue: {
|
||||
showError: true,
|
||||
displayDefaultIndicatorType: false
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
@@ -71,3 +71,5 @@ $status-approved: #0f36e4;
|
||||
$status-overpaid: #800080;
|
||||
$status-active-overdue: #ff0000;
|
||||
$status-matured: #ff4000;
|
||||
|
||||
$state-done: #00ce00;
|
||||
|
||||
@@ -1704,3 +1704,7 @@ textarea.noresize {
|
||||
.status-matured {
|
||||
color: $status-matured;
|
||||
}
|
||||
|
||||
.mat-step-header .mat-step-icon-state-done {
|
||||
background-color: $state-done;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user