Datatables enhacements to support products (#1641)

Co-authored-by: Jose Alberto Hernandez <alberto@MacBook-Pro.local>
This commit is contained in:
José Alberto Hernández
2023-01-13 02:07:51 -06:00
committed by GitHub
co-authored by Jose Alberto Hernandez
parent 08a327fdbf
commit f330e91aa4
161 changed files with 2985 additions and 4971 deletions
@@ -84,6 +84,14 @@
</mat-step>
<mat-step *ngFor="let datatable of datatables">
<ng-template matStepLabel>{{datatable.registeredTableName}}</ng-template>
<mifosx-loan-product-datatable-step [datatableData]="datatable" #dtloanproduct></mifosx-loan-product-datatable-step>
</mat-step>
<mat-step state="preview" *ngIf="loanProductFormValid" completed>
<ng-template matStepLabel>PREVIEW</ng-template>
@@ -1,5 +1,5 @@
/** Angular Imports */
import { Component, OnInit, ViewChild } from '@angular/core';
import { Component, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
/** Custom Components */
@@ -13,6 +13,7 @@ import { LoanProductAccountingStepComponent } from '../loan-product-stepper/loan
/** Custom Services */
import { ProductsService } from 'app/products/products.service';
import { SettingsService } from 'app/settings/settings.service';
import { LoanProductDatatableStepComponent } from '../loan-product-stepper/loan-product-datatable-step/loan-product-datatable-step.component';
@Component({
selector: 'mifosx-create-loan-product',
@@ -27,6 +28,10 @@ export class CreateLoanProductComponent implements OnInit {
@ViewChild(LoanProductSettingsStepComponent, { static: true }) loanProductSettingsStep: LoanProductSettingsStepComponent;
@ViewChild(LoanProductChargesStepComponent, { static: true }) loanProductChargesStep: LoanProductChargesStepComponent;
@ViewChild(LoanProductAccountingStepComponent, { static: true }) loanProductAccountingStep: LoanProductAccountingStepComponent;
/** Get handle on dtloanproduct tags in the template */
@ViewChildren('dtloanproduct') loanProductDatatables: QueryList<LoanProductDatatableStepComponent>;
datatables: any = [];
loanProductsTemplate: any;
accountingRuleData = ['None', 'Cash', 'Accrual (periodic)', 'Accrual (upfront)'];
@@ -42,8 +47,10 @@ export class CreateLoanProductComponent implements OnInit {
private productsService: ProductsService,
private settingsService: SettingsService,
private router: Router) {
this.route.data.subscribe((data: { loanProductsTemplate: any }) => {
this.route.data.subscribe((data: { loanProductsTemplate: any, loanProductDatatables: any }) => {
this.loanProductsTemplate = data.loanProductsTemplate;
// this.datatables = data.loanProductDatatables;
this.datatables = [];
const assetAccountData = this.loanProductsTemplate.accountingMappingOptions.assetAccountOptions || [];
const liabilityAccountData = this.loanProductsTemplate.accountingMappingOptions.liabilityAccountOptions || [];
this.loanProductsTemplate.accountingMappingOptions.assetAndLiabilityAccountOptions = assetAccountData.concat(liabilityAccountData);
@@ -74,13 +81,21 @@ export class CreateLoanProductComponent implements OnInit {
}
get loanProductFormValid() {
return (
let areValids = (
this.loanProductDetailsForm.valid &&
this.loanProductCurrencyForm.valid &&
this.loanProductTermsForm.valid &&
this.loanProductSettingsForm.valid &&
this.loanProductAccountingForm.valid
);
if (this.datatables.length > 0 && this.loanProductDatatables) {
this.loanProductDatatables.forEach((loanProductDatatable: LoanProductDatatableStepComponent) => {
areValids = areValids && loanProductDatatable.datatableForm.valid;
});
}
return areValids;
}
get loanProduct() {
@@ -95,7 +110,6 @@ export class CreateLoanProductComponent implements OnInit {
}
submit() {
// TODO: Update once language and date settings are setup
const dateFormat = this.settingsService.dateFormat;
const loanProduct = {
...this.loanProduct,
@@ -105,6 +119,16 @@ export class CreateLoanProductComponent implements OnInit {
};
delete loanProduct.allowAttributeConfiguration;
delete loanProduct.advancedAccountingRules;
if (this.datatables.length > 0) {
const datatables: any[] = [];
this.loanProductDatatables.forEach((loanProductDatatable: LoanProductDatatableStepComponent) => {
datatables.push(loanProductDatatable.payload);
});
loanProduct['datatables'] = datatables;
}
this.productsService.createLoanProduct(loanProduct)
.subscribe((response: any) => {
this.router.navigate(['../', response.resourceId], { relativeTo: this.route });
@@ -24,7 +24,7 @@ export class LoanProductAndTemplateResolver implements Resolve<Object> {
* @returns {Observable<any>}
*/
resolve(route: ActivatedRouteSnapshot): Observable<any> {
const loanProductId = route.paramMap.get('id');
const loanProductId = route.paramMap.get('productId');
return this.productsService.getLoanProduct(loanProductId, true);
}
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { LoanProductDatatableResolver } from './loan-product-datatable.resolver';
describe('LoanProductDatatableResolver', () => {
let resolver: LoanProductDatatableResolver;
beforeEach(() => {
TestBed.configureTestingModule({});
resolver = TestBed.inject(LoanProductDatatableResolver);
});
it('should be created', () => {
expect(resolver).toBeTruthy();
});
});
@@ -0,0 +1,26 @@
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
import { SystemService } from 'app/system/system.service';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class LoanProductDatatableResolver implements Resolve<boolean> {
/**
* @param {SystemService} systemService Products service.
*/
constructor(private systemService: SystemService) { }
/**
* Returns the loan product data.
* @returns {Observable<any>}
*/
resolve(route: ActivatedRouteSnapshot): Observable<any> {
const productId = route.parent.parent.paramMap.get('productId');
const datatableName = route.paramMap.get('datatableName');
return this.systemService.getEntityDatatable(productId, datatableName);
}
}
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { LoanProductDatatablesResolver } from './loan-product-datatables.resolver';
describe('LoanProductDatatablesResolver', () => {
let resolver: LoanProductDatatablesResolver;
beforeEach(() => {
TestBed.configureTestingModule({});
resolver = TestBed.inject(LoanProductDatatablesResolver);
});
it('should be created', () => {
expect(resolver).toBeTruthy();
});
});
@@ -0,0 +1,23 @@
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
import { SystemService } from 'app/system/system.service';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class LoanProductDatatablesResolver implements Resolve<boolean> {
/**
* @param {SystemService} systemService Products service.
*/
constructor(private systemService: SystemService) { }
/**
* Returns the loan product data.
* @returns {Observable<any>}
*/
resolve(route: ActivatedRouteSnapshot): Observable<any> {
return this.systemService.getEntityDatatables('m_product_loan');
}
}
@@ -0,0 +1,51 @@
<form [formGroup]="datatableForm">
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
<div *ngFor="let datatableInput of datatableInputs" fxFlex="48%">
<mat-form-field fxFlex="100%" *ngIf="!isBoolean(datatableInput.columnDisplayType)">
<mat-label>{{getInputName(datatableInput)}}</mat-label>
<mat-select *ngIf="isDropdown(datatableInput.columnDisplayType)" formControlName="{{datatableInput.controlName}}">
<mat-option *ngFor="let code of datatableInput.columnValues" [value]="code.id">
{{ code.value }}
</mat-option>
</mat-select>
<input matInput type="number" *ngIf="isNumeric(datatableInput.columnDisplayType)"
formControlName="{{datatableInput.controlName}}">
<input matInput *ngIf="isString(datatableInput.columnDisplayType)"
formControlName="{{datatableInput.controlName}}">
<textarea matInput *ngIf="isText(datatableInput.columnDisplayType)"
formControlName="{{datatableInput.controlName}}"></textarea>
<span *ngIf="isDate(datatableInput.columnDisplayType)" (click)="datePicker.open()">
<input matInput [matDatepicker]="datePicker" formControlName="{{datatableInput.controlName}}" class="date-picker">
<mat-datepicker-toggle matSuffix [for]="datePicker"></mat-datepicker-toggle>
<mat-datepicker #datePicker></mat-datepicker>
</span>
</mat-form-field>
<mat-checkbox *ngIf="isBoolean(datatableInput.columnDisplayType)" formControlName="{{datatableInput.controlName}}"
labelPosition="before" class="margin-v">
{{getInputName(datatableInput)}}
</mat-checkbox>
</div>
</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>&nbsp;&nbsp;
Previous
</button>
<button mat-raised-button matStepperNext>
Next&nbsp;&nbsp;
<fa-icon icon="arrow-right"></fa-icon>
</button>
</div>
</form>
@@ -0,0 +1,7 @@
.date-picker {
width: 92%;
}
mat-checkbox {
margin-top: 30px;
}
@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LoanProductDatatableStepComponent } from './loan-product-datatable-step.component';
describe('LoanProductDatatableStepComponent', () => {
let component: LoanProductDatatableStepComponent;
let fixture: ComponentFixture<LoanProductDatatableStepComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ LoanProductDatatableStepComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(LoanProductDatatableStepComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,82 @@
import { Component, Input, OnInit } from '@angular/core';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { Datatables } from 'app/core/utils/datatables';
import { SettingsService } from 'app/settings/settings.service';
@Component({
selector: 'mifosx-loan-product-datatable-step',
templateUrl: './loan-product-datatable-step.component.html',
styleUrls: ['./loan-product-datatable-step.component.scss']
})
export class LoanProductDatatableStepComponent implements OnInit {
/** Input Fields Data */
@Input() datatableData: any;
/** Create Input Form */
datatableForm: FormGroup;
datatableInputs: any = [];
constructor(private formBuilder: FormBuilder,
private settingsService: SettingsService,
private datatableService: Datatables) { }
ngOnInit(): void {
this.datatableInputs = this.datatableService.filterSystemColumns(this.datatableData.columnHeaderData);
const inputItems: any = {};
this.datatableInputs.forEach((input: any) => {
input.controlName = this.getInputName(input);
if (!input.isColumnNullable) {
if (this.isNumeric(input.columnDisplayType)) {
inputItems[input.controlName] = new FormControl(0, [Validators.required]);
} else {
inputItems[input.controlName] = new FormControl('', [Validators.required]);
}
} else {
inputItems[input.controlName] = new FormControl('');
}
});
this.datatableForm = this.formBuilder.group(inputItems);
}
getInputName(datatableInput: any): string {
return this.datatableService.getInputName(datatableInput);
}
isNumeric(columnType: string) {
return this.datatableService.isNumeric(columnType);
}
isDate(columnType: string) {
return this.datatableService.isDate(columnType);
}
isBoolean(columnType: string) {
return this.datatableService.isBoolean(columnType);
}
isDropdown(columnType: string) {
return this.datatableService.isDropdown(columnType);
}
isString(columnType: string) {
return this.datatableService.isString(columnType);
}
isText(columnType: string) {
return this.datatableService.isText(columnType);
}
get payload(): any {
const dateFormat = this.settingsService.dateFormat;
const datatableDataValues = this.datatableForm.value;
const data = this.datatableService.buildPayload(this.datatableInputs, datatableDataValues, dateFormat,
{ locale: this.settingsService.language.code });
return {
registeredTableName: this.datatableData.registeredTableName,
data: data
};
}
}
@@ -84,8 +84,7 @@ export class LoanProductSettingsStepComponent implements OnInit {
'holdGuaranteeFunds': this.loanProductsTemplate.holdGuaranteeFunds,
'multiDisburseLoan': this.loanProductsTemplate.multiDisburseLoan,
'maxTrancheCount': this.loanProductsTemplate.maxTrancheCount,
'outstandingLoanBalance': this.loanProductsTemplate.outstandingLoanBalance,
'delinquencyBucketId': this.loanProductsTemplate.delinquencyBucket.id
'outstandingLoanBalance': this.loanProductsTemplate.outstandingLoanBalance
});
if (this.loanProductsTemplate.isInterestRecalculationEnabled) {
@@ -24,8 +24,8 @@ export class LoanProductResolver implements Resolve<Object> {
* @returns {Observable<any>}
*/
resolve(route: ActivatedRouteSnapshot): Observable<any> {
const id = route.paramMap.get('id');
return this.productsService.getLoanProduct(id);
const productId = route.parent.paramMap.get('productId');
return this.productsService.getLoanProduct(productId);
}
}
@@ -0,0 +1,9 @@
<div class="tab-container mat-typography">
<mifosx-entity-datatable-tab
entityType="Loan Product"
[entityId]="entityId"
[multiRowDatatableFlag]="multiRowDatatableFlag"
[entityDatatable]="entityDatatable"
>
</mifosx-entity-datatable-tab>
</div>
@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DatatableTabComponent } from './datatable-tab.component';
describe('DatatableTabComponent', () => {
let component: DatatableTabComponent;
let fixture: ComponentFixture<DatatableTabComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DatatableTabComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(DatatableTabComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,26 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'mifosx-datatable-tab',
templateUrl: './datatable-tab.component.html',
styleUrls: ['./datatable-tab.component.scss']
})
export class DatatableTabComponent implements OnInit {
entityId: string;
entityDatatable: any;
multiRowDatatableFlag: boolean;
constructor(private route: ActivatedRoute) {
this.entityId = this.route.parent.parent.snapshot.paramMap.get('productId');
this.route.data.subscribe((data: { loanProductDatatable: any }) => {
this.entityDatatable = data.loanProductDatatable;
this.multiRowDatatableFlag = this.entityDatatable.columnHeaders[0].columnName === 'id' ? true : false;
});
}
ngOnInit(): void {
}
}
@@ -0,0 +1,799 @@
<div fxLayout="row" fxLayoutAlign="end" fxLayoutGap="2%" fxLayout.lt-md="column" class="container m-b-20">
<button mat-raised-button color="primary" [routerLink]="['edit']" *mifosxHasPermission="'UPDATE_LOANPRODUCT'">
<fa-icon icon="edit" class="m-r-10"></fa-icon>Edit
</button>
</div>
<div class="tab-container mat-typography">
<h3 class="mat-h3" fxFlexFill>Details</h3>
<mat-divider [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Name:</span>
<span fxFlex="60%"><b>{{ loanProduct.name }}</b></span>
</div>
<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.fundName }}</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 | dateFormat }}</span>
</div>
<div fxFlexFill *ngIf="loanProduct.closeDate">
<span fxFlex="40%">Close Date:</span>
<span fxFlex="60%">{{ loanProduct.closeDate | dateFormat }}</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 [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Currency:</span>
<span fxFlex="60%">{{ loanProduct.currency.name }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Decimal Places:</span>
<span fxFlex="60%">{{ loanProduct.currency.decimalPlaces }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Currency in multiples of:</span>
<span fxFlex="60%">{{ loanProduct.currency.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 [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Principal:</span>
<span fxFlex="60%">{{ loanProduct.principal | number }}
(Min {{ (loanProduct.minPrincipal ? loanProduct.minPrincipal : loanProduct.principal) | number }}
: Max {{ (loanProduct.maxPrincipal ? loanProduct.maxPrincipal : loanProduct.principal) | number }})
</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Allow Approved / Disbursed Amounts Over Applied:</span>
<span fxFlex="60%">{{ loanProduct.allowApprovedDisbursedAmountsOverApplied ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="loanProduct.allowApprovedDisbursedAmountsOverApplied">
<span fxFlex="40%">Over Applied Amount:</span>
<span fxFlex="60%" *ngIf="loanProduct.overAppliedCalculationType === 'percentage'">{{ loanProduct.overAppliedNumber | number
}} %</span>
<span fxFlex="60%" *ngIf="loanProduct.overAppliedCalculationType === 'flat'">{{ loanProduct.overAppliedNumber | number }} {{
loanProduct.currencyCode }}</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.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.floatingRateName }}</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.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.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.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.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 [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Amortization:</span>
<span fxFlex="60%">{{ loanProduct.amortizationType.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.value }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Interest Calculation Period:</span>
<span fxFlex="60%">{{ loanProduct.interestCalculationPeriodType.value }}</span>
</div>
<div fxFlexFill *ngIf="loanProduct.interestCalculationPeriodType.id === 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.transactionProcessingStrategyName }}</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.value }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Days in month:</span>
<span fxFlex="60%">{{ loanProduct.daysInMonthType.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 [inset]="true"></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.interestRecalculationData.preClosureInterestCalculationStrategy.value }}</span>
<span fxFlex="40%">Advance payments adjustment type:</span>
<span fxFlex="60%">{{ loanProduct.interestRecalculationData.rescheduleStrategyType.value }}</span>
<span fxFlex="40%">Interest recalculation compounding on:</span>
<span fxFlex="60%">{{ loanProduct.interestRecalculationData.interestRecalculationCompoundingType.value }}</span>
<div fxFlexFill *ngIf="loanProduct.interestRecalculationData.interestRecalculationCompoundingType.id !== 0"
fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Frequency for compounding:</span>
<span fxFlex="60%">
{{ loanProduct.interestRecalculationData.recalculationCompoundingFrequencyType.value }}
<span
*ngIf="loanProduct.interestRecalculationData.recalculationCompoundingFrequencyType.id === 4 && loanProduct.interestRecalculationData.recalculationCompoundingFrequencyNthDay">&nbsp;&nbsp;{{
'on ' + loanProduct.interestRecalculationData.recalculationCompoundingFrequencyNthDay?.value }}</span>
<span
*ngIf="(loanProduct.interestRecalculationData.recalculationCompoundingFrequencyType.id === 4 && !loanProduct.interestRecalculationData.recalculationCompoundingFrequencyOnDay) || loanProduct.interestRecalculationData.recalculationCompoundingFrequencyType.id === 3">&nbsp;&nbsp;{{
loanProduct.interestRecalculationData.recalculationCompoundingFrequencyWeekday?.value }}</span>
<span
*ngIf="loanProduct.interestRecalculationData.recalculationCompoundingFrequencyType.id === 4 && loanProduct.interestRecalculationData.recalculationCompoundingFrequencyOnDay">&nbsp;&nbsp;{{
'on day ' + loanProduct.interestRecalculationData.recalculationCompoundingFrequencyOnDay }}</span>
</span>
<div fxFlexFill *ngIf="loanProduct.interestRecalculationData.recalculationCompoundingFrequencyType.id !== 1"
fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Frequency Interval for compounding:</span>
<span fxFlex="60%">{{ loanProduct.interestRecalculationData.recalculationCompoundingFrequencyInterval }}</span>
</div>
</div>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Frequency for recalculate Outstanding Principal:</span>
<span fxFlex="60%">
{{ loanProduct.interestRecalculationData.recalculationRestFrequencyType.value }}
<span
*ngIf="loanProduct.interestRecalculationData.recalculationRestFrequencyType.id === 4 && loanProduct.interestRecalculationData.recalculationRestFrequencyNthDay">&nbsp;&nbsp;{{
'on ' + loanProduct.interestRecalculationData.recalculationRestFrequencyNthDay?.value }}</span>
<span
*ngIf="(loanProduct.interestRecalculationData.recalculationRestFrequencyType.id === 4 && !loanProduct.interestRecalculationData.recalculationRestFrequencyOnDay) || loanProduct.interestRecalculationData.recalculationRestFrequencyType.id === 3">&nbsp;&nbsp;{{
loanProduct.interestRecalculationData.recalculationRestFrequencyWeekday?.value }}</span>
<span
*ngIf="loanProduct.interestRecalculationData.recalculationRestFrequencyType.id === 4 && loanProduct.interestRecalculationData.recalculationRestFrequencyOnDay">&nbsp;&nbsp;{{
'on day ' + loanProduct.interestRecalculationData.recalculationRestFrequencyOnDay }}</span>
</span>
</div>
<div fxFlexFill *ngIf="loanProduct.interestRecalculationData.recalculationRestFrequencyType.id !== 1"
fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Frequency Interval for recalculation:</span>
<span fxFlex="60%">{{ loanProduct.interestRecalculationData.recalculationRestFrequencyInterval }}</span>
<div fxFlexFill *ngIf="loanProduct.recalculationRestFrequencyDate">
<span fxFlex="40%">Rest Frequency Date:</span>
<span fxFlex="60%">{{ loanProduct.interestRecalculationData.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 [inset]="true"></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.productGuaranteeData.mandatoryGuarantee }}</span>
<div fxFlexFill *ngIf="loanProduct.productGuaranteeData.minimumGuaranteeFromOwnFunds">
<span fxFlex="40%">Minimum Guarantee from Own Funds(%):</span>
<span fxFlex="60%">{{ loanProduct.productGuaranteeData.minimumGuaranteeFromOwnFunds }}</span>
</div>
<div fxFlexFill *ngIf="loanProduct.productGuaranteeData.minimumGuaranteeFromGuarantor">
<span fxFlex="40%">Minimum Guarantee from Guarantor Funds(%):</span>
<span fxFlex="60%">{{ loanProduct.productGuaranteeData.minimumGuaranteeFromGuarantor }}</span>
</div>
</div>
<h3 class="mat-h3" fxFlexFill>Loan Tranche Details</h3>
<mat-divider [inset]="true"></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 fxFlexFill>
<span fxFlex="40%">Disallow Expected Disbursements:</span>
<span fxFlex="60%">{{ loanProduct.disallowExpectedDisbursements ? 'Yes' : 'No' }}</span>
</div>
</div>
<h3 class="mat-h3" fxFlexFill>Configurable Terms and Settings</h3>
<mat-divider [inset]="true"></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.transactionProcessingStrategyCode ? '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>
<span fxFlex="40%">Delinquency Bucket:</span>
<span fxFlex="60%">{{ loanProduct.delinquencyBucket.id ? '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 [inset]="true"></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 [inset]="true"></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 [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Type:</span>
<span fxFlex="60%">{{ loanProduct.accountingRule.value }}</span>
</div>
<div fxFlexFill *ngIf="loanProduct.accountingRule.id >= 2 && loanProduct.accountingRule.id <= 4" fxLayout="row wrap"
fxLayout.lt-md="column">
<h4 fxFlexFill class="mat-h4">Assets / Liabilities</h4>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Fund source:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.fundSourceAccount.glCode}}) {{
loanProduct.accountingMappings.fundSourceAccount.name }}</span>
</div>
<h4 fxFlexFill class="mat-h4">Assets</h4>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Loan portfolio:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.loanPortfolioAccount.glCode}}) {{
loanProduct.accountingMappings.loanPortfolioAccount.name }}</span>
<div fxFlexFill *ngIf="loanProduct.accountingRule.id === 3 || loanProduct.accountingRule.id === 4"
fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Interest Receivable:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.receivableInterestAccount.glCode}}) {{
loanProduct.accountingMappings.receivableInterestAccount.name }}</span>
<span fxFlex="40%">Fees Receivable:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.receivableInterestAccount.glCode}}) {{
loanProduct.accountingMappings.receivableInterestAccount.name }}</span>
<span fxFlex="40%">Penalties Receivable:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.receivablePenaltyAccount.glCode}}) {{
loanProduct.accountingMappings.receivablePenaltyAccount.name }}</span>
</div>
<span fxFlex="40%">Transfer in suspense:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.transfersInSuspenseAccount.glCode}}) {{
loanProduct.accountingMappings.transfersInSuspenseAccount.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.accountingMappings.interestOnLoanAccount.glCode}}) {{
loanProduct.accountingMappings.interestOnLoanAccount.name }}</span>
<span fxFlex="40%">Income from fees:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.incomeFromFeeAccount.glCode}}) {{
loanProduct.accountingMappings.incomeFromFeeAccount.name }}</span>
<span fxFlex="40%">Income from penalties:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.incomeFromPenaltyAccount.glCode}}) {{
loanProduct.accountingMappings.incomeFromPenaltyAccount.name }}</span>
<span fxFlex="40%">Income from Recovery Repayments:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.incomeFromRecoveryAccount.glCode}}) {{
loanProduct.accountingMappings.incomeFromRecoveryAccount.name }}</span>
</div>
<h4 fxFlexFill class="mat-h4">Expenses</h4>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Losses written off:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.writeOffAccount.glCode}}) {{
loanProduct.accountingMappings.writeOffAccount.name }}</span>
<span fxFlex="40%">Goodwill credit:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.goodwillCreditAccount.glCode}}) {{
loanProduct.accountingMappings.goodwillCreditAccount.name }}</span>
</div>
<h4 fxFlexFill class="mat-h4">Liabilities</h4>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Over payment liability:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.overpaymentLiabilityAccount.glCode}}) {{
loanProduct.accountingMappings.overpaymentLiabilityAccount.name }}</span>
</div>
<div
*ngIf="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 [inset]="true"></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.paymentType.name }}
</td>
</ng-container>
<ng-container matColumnDef="fundSourceAccountId">
<th mat-header-cell *matHeaderCellDef> Fund Source </th>
<td mat-cell *matCellDef="let paymentFundSource">
{{ paymentFundSource.fundSourceAccount.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.charge.name }}
</td>
</ng-container>
<ng-container matColumnDef="incomeAccountId">
<th mat-header-cell *matHeaderCellDef> Income Account </th>
<td mat-cell *matCellDef="let feesIncome">
{{ feesIncome.incomeAccount.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.charge.name }}
</td>
</ng-container>
<ng-container matColumnDef="incomeAccountId">
<th mat-header-cell *matHeaderCellDef> Income Account </th>
<td mat-cell *matCellDef="let penaltyIncome">
{{ penaltyIncome.incomeAccount.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>
@@ -0,0 +1,12 @@
@import "assets/styles/helper";
.tab-container {
padding: 1%;
margin: 1%;
}
.mat-h3 {
margin-top: 10px !important;
font-weight: 300;
color: $status-approved;
}
@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GeneralTabComponent } from './general-tab.component';
describe('GeneralTabComponent', () => {
let component: GeneralTabComponent;
let fixture: ComponentFixture<GeneralTabComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ GeneralTabComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(GeneralTabComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,28 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'mifosx-general-tab',
templateUrl: './general-tab.component.html',
styleUrls: ['./general-tab.component.scss']
})
export class GeneralTabComponent implements OnInit {
loanProduct: any;
variationsDisplayedColumns: string[] = ['valueConditionType', 'borrowerCycleNumber', 'minValue', 'defaultValue', 'maxValue'];
chargesDisplayedColumns: string[] = ['name', 'chargeCalculationType', 'amount', 'chargeTimeType'];
paymentFundSourceDisplayedColumns: string[] = ['paymentTypeId', 'fundSourceAccountId'];
feesPenaltyIncomeDisplayedColumns: string[] = ['chargeId', 'incomeAccountId'];
constructor(private route: ActivatedRoute) {
this.route.data.subscribe((data: { loanProduct: any }) => {
this.loanProduct = data.loanProduct;
});
}
ngOnInit() {
this.loanProduct.allowAttributeConfiguration = Object.values(this.loanProduct.allowAttributeOverrides).some((attribute: boolean) => attribute);
}
}
@@ -1,756 +1,23 @@
<div fxLayout="row" fxLayoutAlign="end" fxLayoutGap="2%" fxLayout.lt-md="column" class="container m-b-20">
<button mat-raised-button color="primary" [routerLink]="['edit']" *mifosxHasPermission="'UPDATE_LOANPRODUCT'">
<fa-icon icon="edit"></fa-icon>&nbsp;&nbsp;
Edit
</button>
</div>
<mat-card class="product-card">
<div class="container">
<mat-card-content>
<mat-card>
<nav mat-tab-nav-bar class="navigation-tabs">
<a mat-tab-link [routerLink]="['./general']" routerLinkActive #general="routerLinkActive"
[active]="general.isActive">
General
</a>
<ng-container *ngFor="let loanProductDatatable of loanProductDatatables">
<a mat-tab-link [routerLink]="['./datatables',loanProductDatatable.registeredTableName]" routerLinkActive
#datatable="routerLinkActive" [active]="datatable.isActive"
*mifosxHasPermission="'READ_' + loanProductDatatable.registeredTableName">
{{loanProductDatatable.registeredTableName}}
</a>
</ng-container>
</nav>
<mat-card-content>
<router-outlet></router-outlet>
<div fxLayout="row wrap" fxLayout.lt-md="column">
</mat-card-content>
<h2 class="mat-h2" fxFlexFill>{{ loanProduct.name }}</h2>
<mat-divider [inset]="true"></mat-divider>
<h3 class="mat-h3" fxFlexFill>Details</h3>
<mat-divider [inset]="true"></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.fundName }}</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 | dateFormat }}</span>
</div>
<div fxFlexFill *ngIf="loanProduct.closeDate">
<span fxFlex="40%">Close Date:</span>
<span fxFlex="60%">{{ loanProduct.closeDate | dateFormat }}</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 [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Currency:</span>
<span fxFlex="60%">{{ loanProduct.currency.name }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Decimal Places:</span>
<span fxFlex="60%">{{ loanProduct.currency.decimalPlaces }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Currency in multiples of:</span>
<span fxFlex="60%">{{ loanProduct.currency.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 [inset]="true"></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%">Allow Approved / Disbursed Amounts Over Applied:</span>
<span fxFlex="60%">{{ loanProduct.allowApprovedDisbursedAmountsOverApplied ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="loanProduct.allowApprovedDisbursedAmountsOverApplied">
<span fxFlex="40%">Over Applied Amount:</span>
<span fxFlex="60%" *ngIf="loanProduct.overAppliedCalculationType === 'percentage'">{{ loanProduct.overAppliedNumber }} %</span>
<span fxFlex="60%" *ngIf="loanProduct.overAppliedCalculationType === 'flat'">{{ loanProduct.overAppliedNumber }} {{ loanProduct.currencyCode }}</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.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.floatingRateName }}</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.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.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.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.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 [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Amortization:</span>
<span fxFlex="60%">{{ loanProduct.amortizationType.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.value }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Interest Calculation Period:</span>
<span fxFlex="60%">{{ loanProduct.interestCalculationPeriodType.value }}</span>
</div>
<div fxFlexFill *ngIf="loanProduct.interestCalculationPeriodType.id === 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.transactionProcessingStrategyName }}</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.value }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Days in month:</span>
<span fxFlex="60%">{{ loanProduct.daysInMonthType.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 [inset]="true"></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.interestRecalculationData.preClosureInterestCalculationStrategy.value }}</span>
<span fxFlex="40%">Advance payments adjustment type:</span>
<span fxFlex="60%">{{ loanProduct.interestRecalculationData.rescheduleStrategyType.value }}</span>
<span fxFlex="40%">Interest recalculation compounding on:</span>
<span fxFlex="60%">{{ loanProduct.interestRecalculationData.interestRecalculationCompoundingType.value }}</span>
<div fxFlexFill *ngIf="loanProduct.interestRecalculationData.interestRecalculationCompoundingType.id !== 0" fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Frequency for compounding:</span>
<span fxFlex="60%">
{{ loanProduct.interestRecalculationData.recalculationCompoundingFrequencyType.value }}
<span *ngIf="loanProduct.interestRecalculationData.recalculationCompoundingFrequencyType.id === 4 && loanProduct.interestRecalculationData.recalculationCompoundingFrequencyNthDay">&nbsp;&nbsp;{{ 'on ' + loanProduct.interestRecalculationData.recalculationCompoundingFrequencyNthDay?.value }}</span>
<span *ngIf="(loanProduct.interestRecalculationData.recalculationCompoundingFrequencyType.id === 4 && !loanProduct.interestRecalculationData.recalculationCompoundingFrequencyOnDay) || loanProduct.interestRecalculationData.recalculationCompoundingFrequencyType.id === 3">&nbsp;&nbsp;{{ loanProduct.interestRecalculationData.recalculationCompoundingFrequencyWeekday?.value }}</span>
<span *ngIf="loanProduct.interestRecalculationData.recalculationCompoundingFrequencyType.id === 4 && loanProduct.interestRecalculationData.recalculationCompoundingFrequencyOnDay">&nbsp;&nbsp;{{ 'on day ' + loanProduct.interestRecalculationData.recalculationCompoundingFrequencyOnDay }}</span>
</span>
<div fxFlexFill *ngIf="loanProduct.interestRecalculationData.recalculationCompoundingFrequencyType.id !== 1" fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Frequency Interval for compounding:</span>
<span fxFlex="60%">{{ loanProduct.interestRecalculationData.recalculationCompoundingFrequencyInterval }}</span>
</div>
</div>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Frequency for recalculate Outstanding Principal:</span>
<span fxFlex="60%">
{{ loanProduct.interestRecalculationData.recalculationRestFrequencyType.value }}
<span *ngIf="loanProduct.interestRecalculationData.recalculationRestFrequencyType.id === 4 && loanProduct.interestRecalculationData.recalculationRestFrequencyNthDay">&nbsp;&nbsp;{{ 'on ' + loanProduct.interestRecalculationData.recalculationRestFrequencyNthDay?.value }}</span>
<span *ngIf="(loanProduct.interestRecalculationData.recalculationRestFrequencyType.id === 4 && !loanProduct.interestRecalculationData.recalculationRestFrequencyOnDay) || loanProduct.interestRecalculationData.recalculationRestFrequencyType.id === 3">&nbsp;&nbsp;{{ loanProduct.interestRecalculationData.recalculationRestFrequencyWeekday?.value }}</span>
<span *ngIf="loanProduct.interestRecalculationData.recalculationRestFrequencyType.id === 4 && loanProduct.interestRecalculationData.recalculationRestFrequencyOnDay">&nbsp;&nbsp;{{ 'on day ' + loanProduct.interestRecalculationData.recalculationRestFrequencyOnDay }}</span>
</span>
</div>
<div fxFlexFill *ngIf="loanProduct.interestRecalculationData.recalculationRestFrequencyType.id !== 1" fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Frequency Interval for recalculation:</span>
<span fxFlex="60%">{{ loanProduct.interestRecalculationData.recalculationRestFrequencyInterval }}</span>
<div fxFlexFill *ngIf="loanProduct.recalculationRestFrequencyDate">
<span fxFlex="40%">Rest Frequency Date:</span>
<span fxFlex="60%">{{ loanProduct.interestRecalculationData.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 [inset]="true"></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.productGuaranteeData.mandatoryGuarantee }}</span>
<div fxFlexFill *ngIf="loanProduct.productGuaranteeData.minimumGuaranteeFromOwnFunds">
<span fxFlex="40%">Minimum Guarantee from Own Funds(%):</span>
<span fxFlex="60%">{{ loanProduct.productGuaranteeData.minimumGuaranteeFromOwnFunds }}</span>
</div>
<div fxFlexFill *ngIf="loanProduct.productGuaranteeData.minimumGuaranteeFromGuarantor">
<span fxFlex="40%">Minimum Guarantee from Guarantor Funds(%):</span>
<span fxFlex="60%">{{ loanProduct.productGuaranteeData.minimumGuaranteeFromGuarantor }}</span>
</div>
</div>
<h3 class="mat-h3" fxFlexFill>Loan Tranche Details</h3>
<mat-divider [inset]="true"></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 fxFlexFill>
<span fxFlex="40%">Disallow Expected Disbursements:</span>
<span fxFlex="60%">{{ loanProduct.disallowExpectedDisbursements ? 'Yes' : 'No' }}</span>
</div>
</div>
<h3 class="mat-h3" fxFlexFill>Configurable Terms and Settings</h3>
<mat-divider [inset]="true"></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.transactionProcessingStrategyCode ? '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>
<span fxFlex="40%">Delinquency Bucket:</span>
<span fxFlex="60%">{{ loanProduct.delinquencyBucket.id ? '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 [inset]="true"></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 [inset]="true"></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 [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Type:</span>
<span fxFlex="60%">{{ loanProduct.accountingRule.value }}</span>
</div>
<div fxFlexFill *ngIf="loanProduct.accountingRule.id >= 2 && loanProduct.accountingRule.id <= 4" fxLayout="row wrap" fxLayout.lt-md="column">
<h4 fxFlexFill class="mat-h4">Assets / Liabilities</h4>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Fund source:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.fundSourceAccount.glCode}}) {{ loanProduct.accountingMappings.fundSourceAccount.name }}</span>
</div>
<h4 fxFlexFill class="mat-h4">Assets</h4>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Loan portfolio:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.loanPortfolioAccount.glCode}}) {{ loanProduct.accountingMappings.loanPortfolioAccount.name }}</span>
<div fxFlexFill *ngIf="loanProduct.accountingRule.id === 3 || loanProduct.accountingRule.id === 4" fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Interest Receivable:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.receivableInterestAccount.glCode}}) {{ loanProduct.accountingMappings.receivableInterestAccount.name }}</span>
<span fxFlex="40%">Fees Receivable:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.receivableInterestAccount.glCode}}) {{ loanProduct.accountingMappings.receivableInterestAccount.name }}</span>
<span fxFlex="40%">Penalties Receivable:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.receivablePenaltyAccount.glCode}}) {{ loanProduct.accountingMappings.receivablePenaltyAccount.name }}</span>
</div>
<span fxFlex="40%">Transfer in suspense:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.transfersInSuspenseAccount.glCode}}) {{ loanProduct.accountingMappings.transfersInSuspenseAccount.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.accountingMappings.interestOnLoanAccount.glCode}}) {{ loanProduct.accountingMappings.interestOnLoanAccount.name }}</span>
<span fxFlex="40%">Income from fees:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.incomeFromFeeAccount.glCode}}) {{ loanProduct.accountingMappings.incomeFromFeeAccount.name }}</span>
<span fxFlex="40%">Income from penalties:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.incomeFromPenaltyAccount.glCode}}) {{ loanProduct.accountingMappings.incomeFromPenaltyAccount.name }}</span>
<span fxFlex="40%">Income from Recovery Repayments:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.incomeFromRecoveryAccount.glCode}}) {{ loanProduct.accountingMappings.incomeFromRecoveryAccount.name }}</span>
</div>
<h4 fxFlexFill class="mat-h4">Expenses</h4>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Losses written off:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.writeOffAccount.glCode}}) {{ loanProduct.accountingMappings.writeOffAccount.name }}</span>
<span fxFlex="40%">Goodwill credit:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.goodwillCreditAccount.glCode}}) {{ loanProduct.accountingMappings.goodwillCreditAccount.name }}</span>
</div>
<h4 fxFlexFill class="mat-h4">Liabilities</h4>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Over payment liability:</span>
<span fxFlex="60%">({{loanProduct.accountingMappings.overpaymentLiabilityAccount.glCode}}) {{ loanProduct.accountingMappings.overpaymentLiabilityAccount.name }}</span>
</div>
<div *ngIf="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 [inset]="true"></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.paymentType.name }}
</td>
</ng-container>
<ng-container matColumnDef="fundSourceAccountId">
<th mat-header-cell *matHeaderCellDef> Fund Source </th>
<td mat-cell *matCellDef="let paymentFundSource">
{{ paymentFundSource.fundSourceAccount.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.charge.name }}
</td>
</ng-container>
<ng-container matColumnDef="incomeAccountId">
<th mat-header-cell *matHeaderCellDef> Income Account </th>
<td mat-cell *matCellDef="let feesIncome">
{{ feesIncome.incomeAccount.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.charge.name }}
</td>
</ng-container>
<ng-container matColumnDef="incomeAccountId">
<th mat-header-cell *matHeaderCellDef> Income Account </th>
<td mat-cell *matCellDef="let penaltyIncome">
{{ penaltyIncome.incomeAccount.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>
</mat-card-content>
</mat-card>
</div>
</mat-card>
@@ -1,24 +1,13 @@
table {
width: 100%;
}
.product-card {
margin: 0 auto;
max-width: 80rem;
width: 90%;
padding: 0;
.mat-elevation-z1 {
margin: 1em 0 1.5em 0;
}
h2, h3, h4 {
margin: 0;
font-weight: 500;
}
span {
margin: 0.5em 0;
}
.mat-card .mat-divider-horizontal.mat-divider-inset {
margin: 0 0 0.5em 0;
}
.margin-t {
margin-top: 1em;
.navigation-tabs {
overflow: auto;
}
i:hover {
cursor: pointer;
}
}
@@ -8,21 +8,15 @@ import { ActivatedRoute } from '@angular/router';
})
export class ViewLoanProductComponent implements OnInit {
loanProduct: any;
variationsDisplayedColumns: string[] = ['valueConditionType', 'borrowerCycleNumber', 'minValue', 'defaultValue', 'maxValue'];
chargesDisplayedColumns: string[] = ['name', 'chargeCalculationType', 'amount', 'chargeTimeType'];
paymentFundSourceDisplayedColumns: string[] = ['paymentTypeId', 'fundSourceAccountId'];
feesPenaltyIncomeDisplayedColumns: string[] = ['chargeId', 'incomeAccountId'];
loanProductDatatables: any = [];
constructor(private route: ActivatedRoute) {
this.route.data.subscribe((data: { loanProduct: any }) => {
this.loanProduct = data.loanProduct;
this.route.data.subscribe((data: { loanProductDatatables: any }) => {
this.loanProductDatatables = data.loanProductDatatables;
});
}
ngOnInit() {
this.loanProduct.allowAttributeConfiguration = Object.values(this.loanProduct.allowAttributeOverrides).some((attribute: boolean) => attribute);
}
}
+57 -12
View File
@@ -112,6 +112,14 @@ import { CreateRangeComponent } from './manage-delinquency-buckets/delinquency-r
import { CreateBucketComponent } from './manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component';
import { EditBucketComponent } from './manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component';
import { ViewBucketComponent } from './manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component';
import { LoanProductDatatablesResolver } from './loan-products/loan-product-datatables.resolver';
import { GeneralTabComponent } from './loan-products/view-loan-product/general-tab/general-tab.component';
import { DatatableTabComponent } from './loan-products/view-loan-product/datatable-tab/datatable-tab.component';
import { LoanProductDatatableResolver } from './loan-products/loan-product-datatable.resolver';
import { SavingProductDatatableResolver } from './saving-products/saving-product-datatable.resolver';
import { SavingProductDatatablesResolver } from './saving-products/saving-product-datatables.resolver';
import { SavingProductGeneralTabComponent } from './saving-products/view-saving-product/saving-product-general-tab/saving-product-general-tab.component';
import { SavingProductDatatableTabComponent } from './saving-products/view-saving-product/saving-product-datatable-tab/saving-product-datatable-tab.component';
/** Products Routes */
const routes: Routes = [
@@ -140,22 +148,41 @@ const routes: Routes = [
component: CreateLoanProductComponent,
data: { title: extract('Create Loan Product'), breadcrumb: 'Create' },
resolve: {
loanProductsTemplate: LoanProductsTemplateResolver
loanProductsTemplate: LoanProductsTemplateResolver,
loanProductDatatables: LoanProductDatatablesResolver
}
},
{
path: ':id',
data: { title: extract('View Loan Product'), routeParamBreadcrumb: 'id' },
path: ':productId',
component: ViewLoanProductComponent,
resolve: {
loanProduct: LoanProductResolver
loanProductDatatables: LoanProductDatatablesResolver
},
data: { title: extract('View Loan Product'), breadcrumb: 'productId', routeParamBreadcrumb: 'productId' },
children: [
{
path: '',
component: ViewLoanProductComponent,
redirectTo: 'general',
pathMatch: 'full'
},
{
path: 'general',
data: { title: extract('General'), breadcrumb: 'General', routeParamBreadcrumb: false },
component: GeneralTabComponent,
resolve: {
loanProduct: LoanProductResolver
}
},
},
{
path: 'datatables',
children: [{
path: ':datatableName',
component: DatatableTabComponent,
data: { title: extract('Data Table View'), routeParamBreadcrumb: 'datatableName' },
resolve: {
loanProductDatatable: LoanProductDatatableResolver
}
}]
},
{
path: 'edit',
@@ -185,22 +212,40 @@ const routes: Routes = [
component: CreateSavingProductComponent,
data: { title: extract('Create Saving Product'), breadcrumb: 'Create' },
resolve: {
savingProductsTemplate: SavingProductsTemplateResolver
savingProductDatatables: SavingProductDatatablesResolver
}
},
{
path: ':id',
data: { title: extract('View Saving Product'), routeParamBreadcrumb: 'id' },
path: ':productId',
component: ViewSavingProductComponent,
data: { title: extract('View Saving Product'), breadcrumb: 'productId', routeParamBreadcrumb: 'productId' },
resolve: {
savingProduct: SavingProductResolver
savingProductDatatables: SavingProductDatatablesResolver
},
children: [
{
path: '',
component: ViewSavingProductComponent,
redirectTo: 'general',
pathMatch: 'full'
},
{
path: 'general',
data: { title: extract('General'), breadcrumb: 'General', routeParamBreadcrumb: false },
component: SavingProductGeneralTabComponent,
resolve: {
savingProduct: SavingProductResolver
}
},
},
{
path: 'datatables',
children: [{
path: ':datatableName',
component: SavingProductDatatableTabComponent,
data: { title: extract('Data Table View'), routeParamBreadcrumb: 'datatableName' },
resolve: {
savingProductDatatable: SavingProductDatatableResolver
}
}]
},
{
path: 'edit',
+11 -1
View File
@@ -106,6 +106,11 @@ import { EditRangeComponent } from './manage-delinquency-buckets/delinquency-ran
import { ViewBucketComponent } from './manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component';
import { CreateBucketComponent } from './manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component';
import { EditBucketComponent } from './manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component';
import { LoanProductDatatableStepComponent } from './loan-products/loan-product-stepper/loan-product-datatable-step/loan-product-datatable-step.component';
import { DatatableTabComponent } from './loan-products/view-loan-product/datatable-tab/datatable-tab.component';
import { GeneralTabComponent } from './loan-products/view-loan-product/general-tab/general-tab.component';
import { SavingProductGeneralTabComponent } from './saving-products/view-saving-product/saving-product-general-tab/saving-product-general-tab.component';
import { SavingProductDatatableTabComponent } from './saving-products/view-saving-product/saving-product-datatable-tab/saving-product-datatable-tab.component';
/**
* Products Module
@@ -219,7 +224,12 @@ import { EditBucketComponent } from './manage-delinquency-buckets/delinquency-bu
EditRangeComponent,
ViewBucketComponent,
CreateBucketComponent,
EditBucketComponent
EditBucketComponent,
LoanProductDatatableStepComponent,
DatatableTabComponent,
GeneralTabComponent,
SavingProductGeneralTabComponent,
SavingProductDatatableTabComponent
],
entryComponents: [
DepositProductIncentiveFormDialogComponent,
+50
View File
@@ -542,4 +542,54 @@ export class ProductsService {
return this.http.post('/collateral-management', collateral);
}
/**
* @param {string} product Product type.
* @returns {Observable<any>}
*/
getProductDatatables(product: string): Observable<any> {
const httpParams = new HttpParams().set('apptable', product);
return this.http.get(`/datatables`, { params: httpParams });
}
/**
* @param {string} productId Product Id.
* @returns {Observable<any>}
*/
getProductDatatable(productId: string, datatableName: string) {
const httpParams = new HttpParams().set('genericResultSet', 'true');
return this.http.get(`/datatables/${datatableName}/${productId}`, { params: httpParams });
}
/**
* @param {string} productId Product Id.
* @param {string} datatableName Datatable name
* @param {any} data Payload.
* @returns {Observable<any>}
*/
addProductDatatableEntry(productId: string, datatableName: string, data: any) {
const httpParams = new HttpParams().set('genericResultSet', 'true');
return this.http.post(`/datatables/${datatableName}/${productId}`, data, { params: httpParams });
}
/**
* @param {string} productId Product Id.
* @param {string} datatableName Datatable name
* @param {any} data Payload.
* @returns {Observable<any>}
*/
editProductDatatableEntry(productId: string, datatableName: string, data: any) {
const httpParams = new HttpParams().set('genericResultSet', 'true');
return this.http.put(`/datatables/${datatableName}/${productId}`, data, { params: httpParams });
}
/**
* @param {string} productId Product Id.
* @param {string} datatableName Datatable name
* @returns {Observable<any>}
*/
deleteDatatableContent(productId: string, datatableName: string) {
const httpParams = new HttpParams().set('genericResultSet', 'true');
return this.http.delete(`/datatables/${datatableName}/${productId}`, { params: httpParams });
}
}
@@ -24,8 +24,8 @@ export class SavingProductAndTemplateResolver implements Resolve<Object> {
* @returns {Observable<any>}
*/
resolve(route: ActivatedRouteSnapshot): Observable<any> {
const savingProductId = route.paramMap.get('id');
return this.productsService.getSavingProduct(savingProductId, true);
const productId = route.paramMap.get('productId');
return this.productsService.getSavingProduct(productId, true);
}
}
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { SavingProductDatatableResolver } from './saving-product-datatable.resolver';
describe('SavingProductDatatableResolver', () => {
let resolver: SavingProductDatatableResolver;
beforeEach(() => {
TestBed.configureTestingModule({});
resolver = TestBed.inject(SavingProductDatatableResolver);
});
it('should be created', () => {
expect(resolver).toBeTruthy();
});
});
@@ -0,0 +1,26 @@
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
import { SystemService } from 'app/system/system.service';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class SavingProductDatatableResolver implements Resolve<boolean> {
/**
* @param {SystemService} systemService Products service.
*/
constructor(private systemService: SystemService) { }
/**
* Returns the loan product data.
* @returns {Observable<any>}
*/
resolve(route: ActivatedRouteSnapshot): Observable<any> {
const productId = route.parent.parent.paramMap.get('productId');
const datatableName = route.paramMap.get('datatableName');
return this.systemService.getEntityDatatable(productId, datatableName);
}
}
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { SavingProductDatatablesResolver } from './saving-product-datatables.resolver';
describe('SavingProductDatatablesResolver', () => {
let resolver: SavingProductDatatablesResolver;
beforeEach(() => {
TestBed.configureTestingModule({});
resolver = TestBed.inject(SavingProductDatatablesResolver);
});
it('should be created', () => {
expect(resolver).toBeTruthy();
});
});
@@ -0,0 +1,27 @@
import { Injectable } from '@angular/core';
import {
Router, Resolve,
RouterStateSnapshot,
ActivatedRouteSnapshot
} from '@angular/router';
import { SystemService } from 'app/system/system.service';
import { Observable, of } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class SavingProductDatatablesResolver implements Resolve<boolean> {
/**
* @param {SystemService} systemService Products service.
*/
constructor(private systemService: SystemService) { }
/**
* Returns the loan product data.
* @returns {Observable<any>}
*/
resolve(route: ActivatedRouteSnapshot): Observable<any> {
return this.systemService.getEntityDatatables('m_savings_product');
}
}
@@ -24,8 +24,8 @@ export class SavingProductResolver implements Resolve<Object> {
* @returns {Observable<any>}
*/
resolve(route: ActivatedRouteSnapshot): Observable<any> {
const id = route.paramMap.get('id');
return this.productsService.getSavingProduct(id);
const productId = route.parent.paramMap.get('productId');
return this.productsService.getSavingProduct(productId);
}
}
@@ -0,0 +1,9 @@
<div class="tab-container mat-typography">
<mifosx-entity-datatable-tab
entityType="Savings Product"
[entityId]="entityId"
[multiRowDatatableFlag]="multiRowDatatableFlag"
[entityDatatable]="entityDatatable"
>
</mifosx-entity-datatable-tab>
</div>
@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SavingProductDatatableTabComponent } from './saving-product-datatable-tab.component';
describe('SavingProductDatatableTabComponent', () => {
let component: SavingProductDatatableTabComponent;
let fixture: ComponentFixture<SavingProductDatatableTabComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SavingProductDatatableTabComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(SavingProductDatatableTabComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,26 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'mifosx-saving-product-datatable-tab',
templateUrl: './saving-product-datatable-tab.component.html',
styleUrls: ['./saving-product-datatable-tab.component.scss']
})
export class SavingProductDatatableTabComponent implements OnInit {
entityId: string;
entityDatatable: any;
multiRowDatatableFlag: boolean;
constructor(private route: ActivatedRoute) {
this.entityId = this.route.parent.parent.snapshot.paramMap.get('productId');
this.route.data.subscribe((data: { savingProductDatatable: any }) => {
this.entityDatatable = data.savingProductDatatable;
this.multiRowDatatableFlag = this.entityDatatable.columnHeaders[0].columnName === 'id' ? true : false;
});
}
ngOnInit(): void {
}
}
@@ -0,0 +1,353 @@
<div fxLayout="row" fxLayoutAlign="end" fxLayoutGap="2%" fxLayout.lt-md="column" class="container m-b-20">
<button mat-raised-button color="primary" [routerLink]="['edit']" *mifosxHasPermission="'UPDATE_SAVINGSPRODUCT'">
<fa-icon icon="edit" class="m-r-10"></fa-icon>Edit
</button>
</div>
<div class="container">
<mat-card>
<mat-card-content>
<div fxLayout="row wrap" fxLayout.lt-md="column">
<h2 class="mat-h2" fxFlexFill>{{ savingProduct.name }}</h2>
<mat-divider [inset]="true"></mat-divider>
<h3 class="mat-h3" fxFlexFill>Details</h3>
<mat-divider [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Short Name:</span>
<span fxFlex="60%">{{ savingProduct.shortName }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.description">
<span fxFlex="40%">Description:</span>
<span fxFlex="60%">{{ savingProduct.description }}</span>
</div>
<h3 class="mat-h3" fxFlexFill>Currency</h3>
<mat-divider [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Currency:</span>
<span fxFlex="60%">{{ savingProduct.currency.name }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Decimal Places:</span>
<span fxFlex="60%">{{ savingProduct.currency.decimalPlaces }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Currency in multiples of:</span>
<span fxFlex="60%">{{ savingProduct.currency.inMultiplesOf }}</span>
</div>
<h3 class="mat-h3" fxFlexFill>Terms</h3>
<mat-divider [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Nominal Annual Interest Rate:</span>
<span fxFlex="60%">{{ savingProduct.nominalAnnualInterestRate }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Interest Compounding Period:</span>
<span fxFlex="60%">{{ savingProduct.interestCompoundingPeriodType.value }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Interest Posting Period:</span>
<span fxFlex="60%">{{ savingProduct.interestPostingPeriodType.value }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Interest Calculated using:</span>
<span fxFlex="60%">{{ savingProduct.interestCalculationType.value }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Days in Year:</span>
<span fxFlex="60%">{{ savingProduct.interestCalculationDaysInYearType.value }}</span>
</div>
<h3 class="mat-h3" fxFlexFill>Settings</h3>
<mat-divider [inset]="true"></mat-divider>
<div fxFlexFill *ngIf="savingProduct.minRequiredOpeningBalance">
<span fxFlex="40%">Minimum Opening Balance:</span>
<span fxFlex="60%">{{ savingProduct.minRequiredOpeningBalance }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.lockinPeriodFrequency">
<span fxFlex="40%">Lock-in Period:</span>
<span fxFlex="60%">{{ savingProduct.lockinPeriodFrequency + ' ' + savingProduct.lockinPeriodFrequencyType.value }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Apply Withdrawal Fee for Transfers:</span>
<span fxFlex="60%">{{ savingProduct.withdrawalFeeForTransfers ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.minBalanceForInterestCalculation">
<span fxFlex="40%">Balance Required for Interest Calculation:</span>
<span fxFlex="60%">{{ savingProduct.minBalanceForInterestCalculation }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Enforce Minimum Balance:</span>
<span fxFlex="60%">{{ savingProduct.enforceMinRequiredBalance ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.minRequiredBalance">
<span fxFlex="40%">Minimum Balance:</span>
<span fxFlex="60%">{{ savingProduct.minRequiredBalance }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Withhold Tax is Applicable:</span>
<span fxFlex="60%">{{ savingProduct.withHoldTax ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.withHoldTax">
<span fxFlex="40%">Tax Group:</span>
<span fxFlex="60%">{{ savingProduct.taxGroup.name }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Is Overdraft Allowed:</span>
<span fxFlex="60%">{{ savingProduct.allowOverdraft ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.minOverdraftForInterestCalculation">
<span fxFlex="40%">Minimum Overdraft Required for Interest Calculation:</span>
<span fxFlex="60%">{{ savingProduct.minOverdraftForInterestCalculation }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.nominalAnnualInterestRateOverdraft">
<span fxFlex="40%">Nominal Annual Interest for Overdraft:</span>
<span fxFlex="60%">{{ savingProduct.nominalAnnualInterestRateOverdraft }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.overdraftLimit">
<span fxFlex="40%">Maximum Overdraft Amount Limit:</span>
<span fxFlex="60%">{{ savingProduct.overdraftLimit }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Enable Dormancy Tracking:</span>
<span fxFlex="60%">{{ savingProduct.isDormancyTrackingActive ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.isDormancyTrackingActive" fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Number of Days to Inactive sub-status:</span>
<span fxFlex="60%">{{ savingProduct.daysToInactive }}</span>
<span fxFlex="40%">Number of Days to Dormant sub-status:</span>
<span fxFlex="60%">{{ savingProduct.daysToDormancy }}</span>
<span fxFlex="40%">Number of Days to Escheat:</span>
<span fxFlex="60%">{{ savingProduct.daysToEscheat }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.charges.length" fxLayout="row wrap" fxLayout.lt-md="column">
<h3 class="mat-h3" fxFlexFill>Charges</h3>
<mat-divider [inset]="true"></mat-divider>
<table fxFlexFill class="mat-elevation-z1" mat-table [dataSource]="savingProduct.charges">
<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>
</div>
<h3 class="mat-h3" fxFlexFill>Accounting</h3>
<mat-divider [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Type:</span>
<span fxFlex="60%">{{ savingProduct.accountingRule.value }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.accountingRule.id === 2" 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%">Saving reference:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.savingsReferenceAccount.name }}</span>
<span fxFlex="40%">Overdraft portfolio:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.overdraftPortfolioControl.name }}</span>
</div>
<h4 fxFlexFill class="mat-h4">Liabilities</h4>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Saving control:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.savingsControlAccount.name }}</span>
<span fxFlex="40%">Savings transfers in suspense:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.transfersInSuspenseAccount.name }}</span>
<div fxFlexFill *ngIf="savingProduct.isDormancyTrackingActive">
<span fxFlex="40%">Escheat liability:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.escheatLiabilityAccount.name }}</span>
</div>
</div>
<h4 fxFlexFill class="mat-h4">Expenses</h4>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Interest on savings:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.interestOnSavingsAccount.name }}</span>
<span fxFlex="40%">Write-off:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.writeOffAccount.name }}</span>
</div>
<h4 fxFlexFill class="mat-h4">Income</h4>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Income from fees:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.incomeFromFeeAccount.name }}</span>
<span fxFlex="40%">Income from penalties:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.incomeFromPenaltyAccount.name }}</span>
<span fxFlex="40%">Overdraft Interest Income:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.incomeFromInterest.name }}</span>
</div>
<div *ngIf="savingProduct.paymentChannelToFundSourceMappings?.length || savingProduct.feeToIncomeAccountMappings?.length || savingProduct.penaltyToIncomeAccountMappings?.length" fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<h3 class="mat-h3" fxFlexFill>Advanced Accounting Rules</h3>
<mat-divider [inset]="true"></mat-divider>
<div *ngIf="savingProduct.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]="savingProduct.paymentChannelToFundSourceMappings">
<ng-container matColumnDef="paymentTypeId">
<th mat-header-cell *matHeaderCellDef> Payment Type </th>
<td mat-cell *matCellDef="let paymentFundSource">
{{ paymentFundSource.paymentType.name }}
</td>
</ng-container>
<ng-container matColumnDef="fundSourceAccountId">
<th mat-header-cell *matHeaderCellDef> Fund Source </th>
<td mat-cell *matCellDef="let paymentFundSource">
{{ paymentFundSource.fundSourceAccount.name }}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="paymentFundSourceDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: paymentFundSourceDisplayedColumns;"></tr>
</table>
</div>
<div *ngIf="savingProduct.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]="savingProduct.feeToIncomeAccountMappings">
<ng-container matColumnDef="chargeId">
<th mat-header-cell *matHeaderCellDef> Fees </th>
<td mat-cell *matCellDef="let feesIncome">
{{ feesIncome.charge.name }}
</td>
</ng-container>
<ng-container matColumnDef="incomeAccountId">
<th mat-header-cell *matHeaderCellDef> Income Account </th>
<td mat-cell *matCellDef="let feesIncome">
{{ feesIncome.incomeAccount.name }}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="feesPenaltyIncomeDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: feesPenaltyIncomeDisplayedColumns;"></tr>
</table>
</div>
<div *ngIf="savingProduct.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]="savingProduct.penaltyToIncomeAccountMappings">
<ng-container matColumnDef="chargeId">
<th mat-header-cell *matHeaderCellDef> Penalty </th>
<td mat-cell *matCellDef="let penaltyIncome">
{{ penaltyIncome.charge.name }}
</td>
</ng-container>
<ng-container matColumnDef="incomeAccountId">
<th mat-header-cell *matHeaderCellDef> Income Account </th>
<td mat-cell *matCellDef="let penaltyIncome">
{{ penaltyIncome.incomeAccount.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>
</mat-card-content>
</mat-card>
</div>
@@ -0,0 +1,12 @@
@import "assets/styles/helper";
.tab-container {
padding: 1%;
margin: 1%;
}
.mat-h3 {
margin-top: 10px !important;
font-weight: 300;
color: $status-approved;
}
@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SavingProductGeneralTabComponent } from './saving-product-general-tab.component';
describe('SavingProductGeneralTabComponent', () => {
let component: SavingProductGeneralTabComponent;
let fixture: ComponentFixture<SavingProductGeneralTabComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SavingProductGeneralTabComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(SavingProductGeneralTabComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,24 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'mifosx-saving-product-general-tab',
templateUrl: './saving-product-general-tab.component.html',
styleUrls: ['./saving-product-general-tab.component.scss']
})
export class SavingProductGeneralTabComponent implements OnInit {
savingProduct: any;
chargesDisplayedColumns: string[] = ['name', 'chargeCalculationType', 'amount', 'chargeTimeType'];
paymentFundSourceDisplayedColumns: string[] = ['paymentTypeId', 'fundSourceAccountId'];
feesPenaltyIncomeDisplayedColumns: string[] = ['chargeId', 'incomeAccountId'];
constructor(private route: ActivatedRoute) {
this.route.data.subscribe((data: { savingProduct: any }) => {
this.savingProduct = data.savingProduct;
});
}
ngOnInit() {
}
}
@@ -1,353 +1,23 @@
<div fxLayout="row" fxLayoutAlign="end" fxLayoutGap="2%" fxLayout.lt-md="column" class="container m-b-20">
<button mat-raised-button color="primary" [routerLink]="['edit']" *mifosxHasPermission="'UPDATE_SAVINGSPRODUCT'">
<fa-icon icon="edit"></fa-icon>&nbsp;&nbsp;
Edit
</button>
</div>
<mat-card class="product-card">
<div class="container">
<mat-card-content>
<mat-card>
<nav mat-tab-nav-bar class="navigation-tabs">
<a mat-tab-link [routerLink]="['./general']" routerLinkActive #general="routerLinkActive"
[active]="general.isActive">
General
</a>
<ng-container *ngFor="let savingProductDatatable of savingProductDatatables">
<a mat-tab-link [routerLink]="['./datatables',savingProductDatatable.registeredTableName]" routerLinkActive
#datatable="routerLinkActive" [active]="datatable.isActive"
*mifosxHasPermission="'READ_' + savingProductDatatable.registeredTableName">
{{savingProductDatatable.registeredTableName}}
</a>
</ng-container>
</nav>
<mat-card-content>
<router-outlet></router-outlet>
<div fxLayout="row wrap" fxLayout.lt-md="column">
</mat-card-content>
<h2 class="mat-h2" fxFlexFill>{{ savingProduct.name }}</h2>
<mat-divider [inset]="true"></mat-divider>
<h3 class="mat-h3" fxFlexFill>Details</h3>
<mat-divider [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Short Name:</span>
<span fxFlex="60%">{{ savingProduct.shortName }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.description">
<span fxFlex="40%">Description:</span>
<span fxFlex="60%">{{ savingProduct.description }}</span>
</div>
<h3 class="mat-h3" fxFlexFill>Currency</h3>
<mat-divider [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Currency:</span>
<span fxFlex="60%">{{ savingProduct.currency.name }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Decimal Places:</span>
<span fxFlex="60%">{{ savingProduct.currency.decimalPlaces }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Currency in multiples of:</span>
<span fxFlex="60%">{{ savingProduct.currency.inMultiplesOf }}</span>
</div>
<h3 class="mat-h3" fxFlexFill>Terms</h3>
<mat-divider [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Nominal Annual Interest Rate:</span>
<span fxFlex="60%">{{ savingProduct.nominalAnnualInterestRate }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Interest Compounding Period:</span>
<span fxFlex="60%">{{ savingProduct.interestCompoundingPeriodType.value }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Interest Posting Period:</span>
<span fxFlex="60%">{{ savingProduct.interestPostingPeriodType.value }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Interest Calculated using:</span>
<span fxFlex="60%">{{ savingProduct.interestCalculationType.value }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Days in Year:</span>
<span fxFlex="60%">{{ savingProduct.interestCalculationDaysInYearType.value }}</span>
</div>
<h3 class="mat-h3" fxFlexFill>Settings</h3>
<mat-divider [inset]="true"></mat-divider>
<div fxFlexFill *ngIf="savingProduct.minRequiredOpeningBalance">
<span fxFlex="40%">Minimum Opening Balance:</span>
<span fxFlex="60%">{{ savingProduct.minRequiredOpeningBalance }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.lockinPeriodFrequency">
<span fxFlex="40%">Lock-in Period:</span>
<span fxFlex="60%">{{ savingProduct.lockinPeriodFrequency + ' ' + savingProduct.lockinPeriodFrequencyType.value }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Apply Withdrawal Fee for Transfers:</span>
<span fxFlex="60%">{{ savingProduct.withdrawalFeeForTransfers ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.minBalanceForInterestCalculation">
<span fxFlex="40%">Balance Required for Interest Calculation:</span>
<span fxFlex="60%">{{ savingProduct.minBalanceForInterestCalculation }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Enforce Minimum Balance:</span>
<span fxFlex="60%">{{ savingProduct.enforceMinRequiredBalance ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.minRequiredBalance">
<span fxFlex="40%">Minimum Balance:</span>
<span fxFlex="60%">{{ savingProduct.minRequiredBalance }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Withhold Tax is Applicable:</span>
<span fxFlex="60%">{{ savingProduct.withHoldTax ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.withHoldTax">
<span fxFlex="40%">Tax Group:</span>
<span fxFlex="60%">{{ savingProduct.taxGroup.name }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Is Overdraft Allowed:</span>
<span fxFlex="60%">{{ savingProduct.allowOverdraft ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.minOverdraftForInterestCalculation">
<span fxFlex="40%">Minimum Overdraft Required for Interest Calculation:</span>
<span fxFlex="60%">{{ savingProduct.minOverdraftForInterestCalculation }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.nominalAnnualInterestRateOverdraft">
<span fxFlex="40%">Nominal Annual Interest for Overdraft:</span>
<span fxFlex="60%">{{ savingProduct.nominalAnnualInterestRateOverdraft }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.overdraftLimit">
<span fxFlex="40%">Maximum Overdraft Amount Limit:</span>
<span fxFlex="60%">{{ savingProduct.overdraftLimit }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Enable Dormancy Tracking:</span>
<span fxFlex="60%">{{ savingProduct.isDormancyTrackingActive ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.isDormancyTrackingActive" fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Number of Days to Inactive sub-status:</span>
<span fxFlex="60%">{{ savingProduct.daysToInactive }}</span>
<span fxFlex="40%">Number of Days to Dormant sub-status:</span>
<span fxFlex="60%">{{ savingProduct.daysToDormancy }}</span>
<span fxFlex="40%">Number of Days to Escheat:</span>
<span fxFlex="60%">{{ savingProduct.daysToEscheat }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.charges.length" fxLayout="row wrap" fxLayout.lt-md="column">
<h3 class="mat-h3" fxFlexFill>Charges</h3>
<mat-divider [inset]="true"></mat-divider>
<table fxFlexFill class="mat-elevation-z1" mat-table [dataSource]="savingProduct.charges">
<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>
</div>
<h3 class="mat-h3" fxFlexFill>Accounting</h3>
<mat-divider [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Type:</span>
<span fxFlex="60%">{{ savingProduct.accountingRule.value }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.accountingRule.id === 2" 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%">Saving reference:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.savingsReferenceAccount.name }}</span>
<span fxFlex="40%">Overdraft portfolio:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.overdraftPortfolioControl.name }}</span>
</div>
<h4 fxFlexFill class="mat-h4">Liabilities</h4>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Saving control:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.savingsControlAccount.name }}</span>
<span fxFlex="40%">Savings transfers in suspense:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.transfersInSuspenseAccount.name }}</span>
<div fxFlexFill *ngIf="savingProduct.isDormancyTrackingActive">
<span fxFlex="40%">Escheat liability:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.escheatLiabilityAccount.name }}</span>
</div>
</div>
<h4 fxFlexFill class="mat-h4">Expenses</h4>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Interest on savings:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.interestOnSavingsAccount.name }}</span>
<span fxFlex="40%">Write-off:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.writeOffAccount.name }}</span>
</div>
<h4 fxFlexFill class="mat-h4">Income</h4>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Income from fees:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.incomeFromFeeAccount.name }}</span>
<span fxFlex="40%">Income from penalties:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.incomeFromPenaltyAccount.name }}</span>
<span fxFlex="40%">Overdraft Interest Income:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.incomeFromInterest.name }}</span>
</div>
<div *ngIf="savingProduct.paymentChannelToFundSourceMappings?.length || savingProduct.feeToIncomeAccountMappings?.length || savingProduct.penaltyToIncomeAccountMappings?.length" fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<h3 class="mat-h3" fxFlexFill>Advanced Accounting Rules</h3>
<mat-divider [inset]="true"></mat-divider>
<div *ngIf="savingProduct.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]="savingProduct.paymentChannelToFundSourceMappings">
<ng-container matColumnDef="paymentTypeId">
<th mat-header-cell *matHeaderCellDef> Payment Type </th>
<td mat-cell *matCellDef="let paymentFundSource">
{{ paymentFundSource.paymentType.name }}
</td>
</ng-container>
<ng-container matColumnDef="fundSourceAccountId">
<th mat-header-cell *matHeaderCellDef> Fund Source </th>
<td mat-cell *matCellDef="let paymentFundSource">
{{ paymentFundSource.fundSourceAccount.name }}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="paymentFundSourceDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: paymentFundSourceDisplayedColumns;"></tr>
</table>
</div>
<div *ngIf="savingProduct.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]="savingProduct.feeToIncomeAccountMappings">
<ng-container matColumnDef="chargeId">
<th mat-header-cell *matHeaderCellDef> Fees </th>
<td mat-cell *matCellDef="let feesIncome">
{{ feesIncome.charge.name }}
</td>
</ng-container>
<ng-container matColumnDef="incomeAccountId">
<th mat-header-cell *matHeaderCellDef> Income Account </th>
<td mat-cell *matCellDef="let feesIncome">
{{ feesIncome.incomeAccount.name }}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="feesPenaltyIncomeDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: feesPenaltyIncomeDisplayedColumns;"></tr>
</table>
</div>
<div *ngIf="savingProduct.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]="savingProduct.penaltyToIncomeAccountMappings">
<ng-container matColumnDef="chargeId">
<th mat-header-cell *matHeaderCellDef> Penalty </th>
<td mat-cell *matCellDef="let penaltyIncome">
{{ penaltyIncome.charge.name }}
</td>
</ng-container>
<ng-container matColumnDef="incomeAccountId">
<th mat-header-cell *matHeaderCellDef> Income Account </th>
<td mat-cell *matCellDef="let penaltyIncome">
{{ penaltyIncome.incomeAccount.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>
</mat-card-content>
</mat-card>
</div>
</mat-card>
@@ -1,24 +1,13 @@
table {
width: 100%;
}
.product-card {
margin: 0 auto;
max-width: 80rem;
width: 90%;
padding: 0;
.mat-elevation-z1 {
margin: 1em 0 1.5em 0;
}
h2, h3, h4 {
margin: 0;
font-weight: 500;
}
span {
margin: 0.5em 0;
}
.mat-card .mat-divider-horizontal.mat-divider-inset {
margin: 0 0 0.5em 0;
}
.margin-t {
margin-top: 1em;
.navigation-tabs {
overflow: auto;
}
i:hover {
cursor: pointer;
}
}
@@ -8,15 +8,11 @@ import { ActivatedRoute } from '@angular/router';
})
export class ViewSavingProductComponent implements OnInit {
savingProduct: any;
chargesDisplayedColumns: string[] = ['name', 'chargeCalculationType', 'amount', 'chargeTimeType'];
paymentFundSourceDisplayedColumns: string[] = ['paymentTypeId', 'fundSourceAccountId'];
feesPenaltyIncomeDisplayedColumns: string[] = ['chargeId', 'incomeAccountId'];
savingProductDatatables: any = [];
constructor(private route: ActivatedRoute) {
this.route.data.subscribe((data: { savingProduct: any }) => {
this.savingProduct = data.savingProduct;
this.route.data.subscribe((data: { savingProductDatatables: any }) => {
this.savingProductDatatables = data.savingProductDatatables;
});
}