feat: add create fixed deposit product component (#471)

- Add fixed deposit product stepper components
- Add fixed deposit product component

Fixes: #192
This commit is contained in:
Abhay Chawla
2019-12-01 23:20:51 +03:00
parent 90be946675
commit be7d006e04
47 changed files with 2923 additions and 8 deletions
+4 -1
View File
@@ -6,7 +6,10 @@ import { Pipe, PipeTransform } from '@angular/core';
export class FindPipe implements PipeTransform {
transform(value: any, options: any, key: string, property: string): any {
const optionFound = options.find((option: any) => option[key] === value);
let optionFound;
if (options) {
optionFound = options.find((option: any) => option[key] === value);
}
return optionFound ? optionFound[property] : '';
}
@@ -0,0 +1,67 @@
<h1 mat-dialog-title>{{ title }}</h1>
<div mat-dialog-content [formGroup]="depositProductIncentiveForm" fxLayout="column">
<mat-form-field>
<mat-label>Attribute</mat-label>
<mat-select formControlName="attributeName" required>
<mat-option *ngFor="let attributeName of attributeNameData" [value]="attributeName.id">
{{ attributeName.value }}
</mat-option>
</mat-select>
<mat-error>
Attribute is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>Condition</mat-label>
<mat-select formControlName="conditionType" required>
<mat-option *ngFor="let conditionType of conditionTypeData" [value]="conditionType.id">
{{ conditionType.value }}
</mat-option>
</mat-select>
<mat-error>
Condition is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field *ngIf="depositProductIncentiveForm.value.attributeName">
<mat-label>Value</mat-label>
<mat-select formControlName="attributeValue" *ngIf="depositProductIncentiveForm.value.attributeName !== 3" required>
<mat-option *ngFor="let attributeValue of attributeValueData" [value]="attributeValue.id">
{{ attributeValue.name }}
</mat-option>
</mat-select>
<input type="number" matInput formControlName="attributeValue" *ngIf="depositProductIncentiveForm.value.attributeName === 3" required>
<mat-error>
Value is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>Type</mat-label>
<mat-select formControlName="incentiveType" required>
<mat-option *ngFor="let incentiveType of incentiveTypeData" [value]="incentiveType.id">
{{ incentiveType.value }}
</mat-option>
</mat-select>
<mat-error>
Type is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>Interest</mat-label>
<input type="number" matInput formControlName="amount" required>
<mat-error>
Interest is <strong>required</strong>
</mat-error>
</mat-form-field>
</div>
<mat-dialog-actions fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
<button mat-raised-button mat-dialog-close> Cancel </button>
<button mat-raised-button color="primary" [mat-dialog-close]="{ data: depositProductIncentiveForm }" [disabled]="!depositProductIncentiveForm.valid || depositProductIncentiveForm.pristine">{{ layout.addButtonText }}</button>
</mat-dialog-actions>
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { DepositProductIncentiveFormDialogComponent } from './deposit-product-incentive-form-dialog.component';
describe('DepositProductIncentiveFormDialogComponent', () => {
let component: DepositProductIncentiveFormDialogComponent;
let fixture: ComponentFixture<DepositProductIncentiveFormDialogComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ DepositProductIncentiveFormDialogComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(DepositProductIncentiveFormDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,82 @@
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'mifosx-deposit-product-incentive-form-dialog',
templateUrl: './deposit-product-incentive-form-dialog.component.html',
styleUrls: ['./deposit-product-incentive-form-dialog.component.scss']
})
export class DepositProductIncentiveFormDialogComponent implements OnInit {
layout: {
addButtonText?: string
} = {
addButtonText: 'Add'
};
depositProductIncentiveForm: FormGroup;
title: string;
entityTypeData: any;
attributeNameData: any;
conditionTypeData: any;
attributeValueData: any;
incentiveTypeData: any;
constructor(public dialogRef: MatDialogRef<DepositProductIncentiveFormDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
private formBuilder: FormBuilder) {
this.createDepositProductIncentiveForm();
this.setConditionalControls();
this.layout = { ...this.layout, ...data.layout };
this.dialogRef.disableClose = true;
}
ngOnInit() {
this.dialogRef.updateSize('400px');
this.entityTypeData = this.data.chartTemplate.entityTypeOptions;
this.attributeNameData = this.data.chartTemplate.attributeNameOptions;
this.conditionTypeData = this.data.chartTemplate.conditionTypeOptions;
this.incentiveTypeData = this.data.chartTemplate.incentiveTypeOptions;
if (this.data.values) {
this.depositProductIncentiveForm.patchValue({
'entityType': this.data.values.entityType,
'attributeName': this.data.values.attributeName,
'conditionType': this.data.values.conditionType,
'attributeValue': this.data.values.attributeValue,
'incentiveType': this.data.values.incentiveType,
'amount': this.data.values.amount
});
} else {
this.depositProductIncentiveForm.patchValue({
'entityType': this.data.entityType
});
}
this.title = `Incentives: ${this.entityTypeData.find((entityType: any) => this.depositProductIncentiveForm.get('entityType').value === entityType.id).value} Attributes`;
}
setConditionalControls() {
this.depositProductIncentiveForm.get('attributeName').valueChanges
.subscribe((attributeName: any) => {
this.depositProductIncentiveForm.patchValue({ 'attributeValue': '' });
this.attributeValueData = this.data.chartTemplate[`${this.attributeNameData.find((option: any) => option.id === attributeName).code.split('.')[1]}Options`];
});
}
createDepositProductIncentiveForm() {
this.depositProductIncentiveForm = this.formBuilder.group({
'entityType': [''],
'attributeName': ['', Validators.required],
'conditionType': ['', Validators.required],
'attributeValue': ['', Validators.required],
'incentiveType': ['', Validators.required],
'amount': ['', Validators.required]
});
}
}
@@ -0,0 +1,107 @@
<div class="container">
<mat-horizontal-stepper class="mat-elevation-z8" labelPosition="bottom" #fixedDepositProductStepper>
<ng-template matStepperIcon="number">
<fa-icon icon="pencil-alt" size="sm"></fa-icon>
</ng-template>
<ng-template matStepperIcon="edit">
<fa-icon icon="pencil-alt" size="sm"></fa-icon>
</ng-template>
<ng-template matStepperIcon="done">
<fa-icon icon="check" size="sm"></fa-icon>
</ng-template>
<ng-template matStepperIcon="error">
<fa-icon icon="exclamation-triangle" size="lg"></fa-icon>
</ng-template>
<ng-template matStepperIcon="preview">
<fa-icon icon="eye" size="sm"></fa-icon>
</ng-template>
<mat-step [stepControl]="fixedDepositProductDetailsForm">
<ng-template matStepLabel>DETAILS</ng-template>
<mifosx-fixed-deposit-product-details-step></mifosx-fixed-deposit-product-details-step>
</mat-step>
<mat-step [stepControl]="fixedDepositProductCurrencyForm">
<ng-template matStepLabel>CURRENCY</ng-template>
<mifosx-fixed-deposit-product-currency-step [fixedDepositProductsTemplate]="fixedDepositProductsTemplate"></mifosx-fixed-deposit-product-currency-step>
</mat-step>
<mat-step [stepControl]="fixedDepositProductTermsForm">
<ng-template matStepLabel>TERMS</ng-template>
<mifosx-fixed-deposit-product-terms-step [fixedDepositProductsTemplate]="fixedDepositProductsTemplate"></mifosx-fixed-deposit-product-terms-step>
</mat-step>
<mat-step [stepControl]="fixedDepositProductSettingsForm">
<ng-template matStepLabel>SETTINGS</ng-template>
<mifosx-fixed-deposit-product-settings-step [fixedDepositProductsTemplate]="fixedDepositProductsTemplate"></mifosx-fixed-deposit-product-settings-step>
</mat-step>
<mat-step [stepControl]="fixedDepositProductInterestRateChartForm">
<ng-template matStepLabel>INTEREST RATE CHART</ng-template>
<mifosx-fixed-deposit-product-interest-rate-chart-step [fixedDepositProductsTemplate]="fixedDepositProductsTemplate"></mifosx-fixed-deposit-product-interest-rate-chart-step>
</mat-step>
<mat-step>
<ng-template matStepLabel>CHARGES</ng-template>
<mifosx-fixed-deposit-product-charges-step
[fixedDepositProductsTemplate]="fixedDepositProductsTemplate"
[currencyCode]="fixedDepositProductCurrencyForm.get('currencyCode')"
>
</mifosx-fixed-deposit-product-charges-step>
</mat-step>
<mat-step [stepControl]="fixedDepositProductAccountingForm">
<ng-template matStepLabel>ACCOUNTING</ng-template>
<mifosx-fixed-deposit-product-accounting-step
[fixedDepositProductsTemplate]="fixedDepositProductsTemplate"
[accountingRuleData]="accountingRuleData"
[fixedDepositProductFormValid]="fixedDepositProductFormValid"
>
</mifosx-fixed-deposit-product-accounting-step>
</mat-step>
<mat-step state="preview" *ngIf="fixedDepositProductFormValid" completed>
<ng-template matStepLabel>PREVIEW</ng-template>
<mifosx-fixed-deposit-product-preview-step
[fixedDepositProductsTemplate]="fixedDepositProductsTemplate"
[chartSlabsDisplayedColumns]="fixedDepositProductInterestRateChartStep.chartSlabsDisplayedColumns"
[accountingRuleData]="accountingRuleData"
[fixedDepositProduct]="fixedDepositProduct"
(submit)="submit()"
>
</mifosx-fixed-deposit-product-preview-step>
</mat-step>
</mat-horizontal-stepper>
</div>
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CreateFixedDepositProductComponent } from './create-fixed-deposit-product.component';
describe('CreateFixedDepositProductComponent', () => {
let component: CreateFixedDepositProductComponent;
let fixture: ComponentFixture<CreateFixedDepositProductComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CreateFixedDepositProductComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CreateFixedDepositProductComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,104 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { FixedDepositProductDetailsStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-details-step/fixed-deposit-product-details-step.component';
import { FixedDepositProductCurrencyStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-currency-step/fixed-deposit-product-currency-step.component';
import { FixedDepositProductTermsStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-terms-step/fixed-deposit-product-terms-step.component';
import { FixedDepositProductSettingsStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-settings-step/fixed-deposit-product-settings-step.component';
import { FixedDepositProductInterestRateChartStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-interest-rate-chart-step/fixed-deposit-product-interest-rate-chart-step.component';
import { FixedDepositProductChargesStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-charges-step/fixed-deposit-product-charges-step.component';
import { FixedDepositProductAccountingStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-accounting-step/fixed-deposit-product-accounting-step.component';
import { ProductsService } from 'app/products/products.service';
@Component({
selector: 'mifosx-create-fixed-deposit-product',
templateUrl: './create-fixed-deposit-product.component.html',
styleUrls: ['./create-fixed-deposit-product.component.scss']
})
export class CreateFixedDepositProductComponent implements OnInit {
@ViewChild(FixedDepositProductDetailsStepComponent) fixedDepositProductDetailsStep: FixedDepositProductDetailsStepComponent;
@ViewChild(FixedDepositProductCurrencyStepComponent) fixedDepositProductCurrencyStep: FixedDepositProductCurrencyStepComponent;
@ViewChild(FixedDepositProductTermsStepComponent) fixedDepositProductTermsStep: FixedDepositProductTermsStepComponent;
@ViewChild(FixedDepositProductSettingsStepComponent) fixedDepositProductSettingsStep: FixedDepositProductSettingsStepComponent;
@ViewChild(FixedDepositProductInterestRateChartStepComponent) fixedDepositProductInterestRateChartStep: FixedDepositProductInterestRateChartStepComponent;
@ViewChild(FixedDepositProductChargesStepComponent) fixedDepositProductChargesStep: FixedDepositProductChargesStepComponent;
@ViewChild(FixedDepositProductAccountingStepComponent) fixedDepositProductAccountingStep: FixedDepositProductAccountingStepComponent;
fixedDepositProductsTemplate: any;
accountingRuleData = ['None', 'Cash'];
constructor(private route: ActivatedRoute,
private productsService: ProductsService,
private router: Router) {
this.route.data.subscribe((data: { fixedDepositProductsTemplate: any }) => {
this.fixedDepositProductsTemplate = data.fixedDepositProductsTemplate;
});
}
ngOnInit() {
}
get fixedDepositProductDetailsForm() {
return this.fixedDepositProductDetailsStep.fixedDepositProductDetailsForm;
}
get fixedDepositProductCurrencyForm() {
return this.fixedDepositProductCurrencyStep.fixedDepositProductCurrencyForm;
}
get fixedDepositProductTermsForm() {
return this.fixedDepositProductTermsStep.fixedDepositProductTermsForm;
}
get fixedDepositProductSettingsForm() {
return this.fixedDepositProductSettingsStep.fixedDepositProductSettingsForm;
}
get fixedDepositProductInterestRateChartForm() {
return this.fixedDepositProductInterestRateChartStep.fixedDepositProductInterestRateChartForm;
}
get fixedDepositProductAccountingForm() {
return this.fixedDepositProductAccountingStep.fixedDepositProductAccountingForm;
}
get fixedDepositProductFormValid() {
return (
this.fixedDepositProductDetailsForm.valid &&
this.fixedDepositProductCurrencyForm.valid &&
this.fixedDepositProductTermsForm.valid &&
this.fixedDepositProductSettingsForm.valid &&
this.fixedDepositProductInterestRateChartForm.valid &&
this.fixedDepositProductAccountingForm.valid
);
}
get fixedDepositProduct() {
return {
...this.fixedDepositProductDetailsStep.fixedDepositProductDetails,
...this.fixedDepositProductCurrencyStep.fixedDepositProductCurrency,
...this.fixedDepositProductTermsStep.fixedDepositProductTerms,
...this.fixedDepositProductSettingsStep.fixedDepositProductSettings,
...this.fixedDepositProductInterestRateChartStep.fixedDepositProductInterestRateChart,
...this.fixedDepositProductChargesStep.fixedDepositProductCharges,
...this.fixedDepositProductAccountingStep.fixedDepositProductAccounting
};
}
submit() {
// TODO: Update once language and date settings are setup
const fixedDepositProduct = {
...this.fixedDepositProduct,
charges: this.fixedDepositProduct.charges.map((charge: any) => ({ id: charge.id })),
locale: 'en' // locale required for depositAmount
};
delete fixedDepositProduct.advancedAccountingRules;
this.productsService.createFixedDepositProduct(fixedDepositProduct)
.subscribe((response: any) => {
this.router.navigate(['../', response.resourceId], { relativeTo: this.route });
});
}
}
@@ -0,0 +1,253 @@
<form [formGroup]="fixedDepositProductAccountingForm">
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
<mat-radio-group fxFlex="98%" fxLayout="row" fxLayoutGap="5%" fxLayout.lt-md="column" formControlName="accountingRule">
<mat-radio-button *ngFor="let accountingRule of accountingRuleData; let i = index" [value]="i+1">
{{ accountingRule }}
</mat-radio-button>
</mat-radio-group>
<mat-divider fxFlex="98%"></mat-divider>
<div *ngIf="fixedDepositProductAccountingForm.value.accountingRule === 2" fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
<h4 fxFlex="98%" class="mat-h4">Assets</h4>
<mat-form-field fxFlex="48%">
<mat-label>Saving reference</mat-label>
<mat-select formControlName="savingsReferenceAccountId" required>
<mat-option *ngFor="let assetAccount of assetAccountData" [value]="assetAccount.id">
{{ assetAccount.name }}
</mat-option>
</mat-select>
<mat-error>
Saving reference is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-divider fxFlex="98%"></mat-divider>
<h4 fxFlex="98%" class="mat-h4">Liabilities</h4>
<mat-form-field fxFlex="48%">
<mat-label>Saving control</mat-label>
<mat-select formControlName="savingsControlAccountId" required>
<mat-option *ngFor="let liabilityAccount of liabilityAccountData" [value]="liabilityAccount.id">
{{ liabilityAccount.name }}
</mat-option>
</mat-select>
<mat-error>
Saving control is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Savings transfers in suspense</mat-label>
<mat-select formControlName="transfersInSuspenseAccountId" required>
<mat-option *ngFor="let liabilityAccount of liabilityAccountData" [value]="liabilityAccount.id">
{{ liabilityAccount.name }}
</mat-option>
</mat-select>
<mat-error>
Savings transfers in suspense is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-divider fxFlex="98%"></mat-divider>
<h4 fxFlex="98%" class="mat-h4">Expenses</h4>
<mat-form-field fxFlex="48%">
<mat-label>Interest on savings</mat-label>
<mat-select formControlName="interestOnSavingsAccountId" required>
<mat-option *ngFor="let expenseAccount of expenseAccountData" [value]="expenseAccount.id">
{{ expenseAccount.name }}
</mat-option>
</mat-select>
<mat-error>
Interest on savings is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-divider fxFlex="98%"></mat-divider>
<h4 fxFlex="98%" class="mat-h4">Income</h4>
<mat-form-field fxFlex="48%">
<mat-label>Income from fees</mat-label>
<mat-select formControlName="incomeFromFeeAccountId" required>
<mat-option *ngFor="let incomeAccount of incomeAccountData" [value]="incomeAccount.id">
{{ incomeAccount.name }}
</mat-option>
</mat-select>
<mat-error>
Income from fees Repayments is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Income from penalties</mat-label>
<mat-select formControlName="incomeFromPenaltyAccountId" required>
<mat-option *ngFor="let incomeAccount of incomeAccountData" [value]="incomeAccount.id">
{{ incomeAccount.name }}
</mat-option>
</mat-select>
<mat-error>
Income from penalties is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-divider fxFlex="98%"></mat-divider>
<h3 fxFlex="23%" class="mat-h3">Advanced Accounting Rules</h3>
<mat-checkbox fxFlex="73%" formControlName="advancedAccountingRules"></mat-checkbox>
<div *ngIf="fixedDepositProductAccountingForm.value.advancedAccountingRules" fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
<h4 fxFlex="33%" class="mat-h4">Configure Fund Sources for Payment Channels</h4>
<div fxFlex="63%">
<button type="button" mat-raised-button color="primary" (click)="add('PaymentFundSource', paymentChannelToFundSourceMappings)">
<fa-icon icon="plus"></fa-icon>&nbsp;&nbsp;
Add
</button>
</div>
<table fxFlex="98%" class="mat-elevation-z1" mat-table [dataSource]="paymentChannelToFundSourceMappings.value" *ngIf="paymentChannelToFundSourceMappings.value.length !== 0">
<ng-container matColumnDef="paymentTypeId">
<th mat-header-cell *matHeaderCellDef> Payment Type </th>
<td mat-cell *matCellDef="let paymentFundSource">
{{ paymentFundSource.paymentTypeId | find:paymentTypeData:'id':'name' }}
</td>
</ng-container>
<ng-container matColumnDef="fundSourceAccountId">
<th mat-header-cell *matHeaderCellDef> Fund Source </th>
<td mat-cell *matCellDef="let paymentFundSource">
{{ paymentFundSource.fundSourceAccountId | find:assetAccountData:'id':'name' }}
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let paymentFundSource; let i = index">
<button mat-icon-button color="primary" (click)="edit('PaymentFundSource', paymentChannelToFundSourceMappings, i)">
<fa-icon icon="edit"></fa-icon>
</button>
<button mat-icon-button color="warn" (click)="delete(paymentChannelToFundSourceMappings, i)">
<fa-icon icon="trash"></fa-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="paymentFundSourceDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: paymentFundSourceDisplayedColumns;"></tr>
</table>
<h4 fxFlex="33%" class="mat-h4">Map Fees to Specific Income Accounts</h4>
<div fxFlex="63%">
<button type="button" mat-raised-button color="primary" (click)="add('FeesIncome', feeToIncomeAccountMappings)">
<fa-icon icon="plus"></fa-icon>&nbsp;&nbsp;
Add
</button>
</div>
<table fxFlex="98%" class="mat-elevation-z1" mat-table [dataSource]="feeToIncomeAccountMappings.value" *ngIf="feeToIncomeAccountMappings.value.length !== 0">
<ng-container matColumnDef="chargeId">
<th mat-header-cell *matHeaderCellDef> Fees </th>
<td mat-cell *matCellDef="let feesIncome">
{{ feesIncome.chargeId | find:chargeData:'id':'name' }}
</td>
</ng-container>
<ng-container matColumnDef="incomeAccountId">
<th mat-header-cell *matHeaderCellDef> Income Account </th>
<td mat-cell *matCellDef="let feesIncome">
{{ feesIncome.incomeAccountId | find:incomeAccountData:'id':'name' }}
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let feesIncome; let i = index">
<button mat-icon-button color="primary" (click)="edit('FeesIncome', feeToIncomeAccountMappings, i)">
<fa-icon icon="edit"></fa-icon>
</button>
<button mat-icon-button color="warn" (click)="delete(feeToIncomeAccountMappings, i)">
<fa-icon icon="trash"></fa-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="feesPenaltyIncomeDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: feesPenaltyIncomeDisplayedColumns;"></tr>
</table>
<h4 fxFlex="33%" class="mat-h4">Map Penalties to Specific Income Accounts</h4>
<div fxFlex="63%">
<button type="button" mat-raised-button color="primary" (click)="add('PenaltyIncome', penaltyToIncomeAccountMappings)">
<fa-icon icon="plus"></fa-icon>&nbsp;&nbsp;
Add
</button>
</div>
<table fxFlex="98%" class="mat-elevation-z1" mat-table [dataSource]="penaltyToIncomeAccountMappings.value" *ngIf="penaltyToIncomeAccountMappings.value.length !== 0">
<ng-container matColumnDef="chargeId">
<th mat-header-cell *matHeaderCellDef> Penalty </th>
<td mat-cell *matCellDef="let penaltyIncome">
{{ penaltyIncome.chargeId | find:penaltyData:'id':'name' }}
</td>
</ng-container>
<ng-container matColumnDef="incomeAccountId">
<th mat-header-cell *matHeaderCellDef> Income Account </th>
<td mat-cell *matCellDef="let penaltyIncome">
{{ penaltyIncome.incomeAccountId | find:incomeAccountData:'id':'name' }}
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let penaltyIncome; let i = index">
<button mat-icon-button color="primary" (click)="edit('PenaltyIncome', penaltyToIncomeAccountMappings, i)">
<fa-icon icon="edit"></fa-icon>
</button>
<button mat-icon-button color="warn" (click)="delete(penaltyToIncomeAccountMappings, i)">
<fa-icon icon="trash"></fa-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="feesPenaltyIncomeDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: feesPenaltyIncomeDisplayedColumns;"></tr>
</table>
</div>
</div>
</div>
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
<button mat-raised-button matStepperPrevious>
<fa-icon icon="arrow-left"></fa-icon>&nbsp;&nbsp;
Previous
</button>
<button mat-raised-button matStepperNext [disabled]="!fixedDepositProductFormValid">
Next&nbsp;&nbsp;
<fa-icon icon="arrow-right"></fa-icon>
</button>
</div>
</form>
@@ -0,0 +1,23 @@
table {
width: 100%;
}
.mat-elevation-z1 {
margin: 1em 0 1.5em 0;
}
h3, h4 {
font-weight: 500;
}
mat-radio-group, h4 {
margin: 1em 0;
}
mat-divider {
margin: 1em 0 2em 0;
}
.margin-t {
margin-top: 1em;
}
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FixedDepositProductAccountingStepComponent } from './fixed-deposit-product-accounting-step.component';
describe('FixedDepositProductAccountingStepComponent', () => {
let component: FixedDepositProductAccountingStepComponent;
let fixture: ComponentFixture<FixedDepositProductAccountingStepComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FixedDepositProductAccountingStepComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FixedDepositProductAccountingStepComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,218 @@
import { Component, OnInit, Input } from '@angular/core';
import { FormGroup, FormBuilder, FormArray, Validators, FormControl } from '@angular/forms';
import { MatDialog } from '@angular/material';
import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component';
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-base';
import { SelectBase } from 'app/shared/form-dialog/formfield/model/select-base';
@Component({
selector: 'mifosx-fixed-deposit-product-accounting-step',
templateUrl: './fixed-deposit-product-accounting-step.component.html',
styleUrls: ['./fixed-deposit-product-accounting-step.component.scss']
})
export class FixedDepositProductAccountingStepComponent implements OnInit {
@Input() fixedDepositProductsTemplate: any;
@Input() accountingRuleData: any;
@Input() fixedDepositProductFormValid: boolean;
fixedDepositProductAccountingForm: FormGroup;
chargeData: any;
penaltyData: any;
paymentTypeData: any;
assetAccountData: any;
incomeAccountData: any;
expenseAccountData: any;
liabilityAccountData: any;
paymentFundSourceDisplayedColumns: string[] = ['paymentTypeId', 'fundSourceAccountId', 'actions'];
feesPenaltyIncomeDisplayedColumns: string[] = ['chargeId', 'incomeAccountId', 'actions'];
constructor(private formBuilder: FormBuilder,
public dialog: MatDialog) {
this.createfixedDepositProductAccountingForm();
this.setConditionalControls();
}
ngOnInit() {
this.chargeData = this.fixedDepositProductsTemplate.chargeOptions || [];
this.penaltyData = this.fixedDepositProductsTemplate.penaltyOptions || [];
this.paymentTypeData = this.fixedDepositProductsTemplate.paymentTypeOptions || [];
this.assetAccountData = this.fixedDepositProductsTemplate.accountingMappingOptions.assetAccountOptions || [];
this.incomeAccountData = this.fixedDepositProductsTemplate.accountingMappingOptions.incomeAccountOptions || [];
this.expenseAccountData = this.fixedDepositProductsTemplate.accountingMappingOptions.expenseAccountOptions || [];
this.liabilityAccountData = this.fixedDepositProductsTemplate.accountingMappingOptions.liabilityAccountOptions || [];
}
createfixedDepositProductAccountingForm() {
this.fixedDepositProductAccountingForm = this.formBuilder.group({
'accountingRule': [1]
});
}
setConditionalControls() {
this.fixedDepositProductAccountingForm.get('accountingRule').valueChanges
.subscribe((accountingRule: any) => {
if (accountingRule === 2) {
this.fixedDepositProductAccountingForm.addControl('savingsReferenceAccountId', new FormControl('', Validators.required));
this.fixedDepositProductAccountingForm.addControl('savingsControlAccountId', new FormControl('', Validators.required));
this.fixedDepositProductAccountingForm.addControl('transfersInSuspenseAccountId', new FormControl('', Validators.required));
this.fixedDepositProductAccountingForm.addControl('interestOnSavingsAccountId', new FormControl('', Validators.required));
this.fixedDepositProductAccountingForm.addControl('incomeFromFeeAccountId', new FormControl('', Validators.required));
this.fixedDepositProductAccountingForm.addControl('incomeFromPenaltyAccountId', new FormControl('', Validators.required));
this.fixedDepositProductAccountingForm.addControl('advancedAccountingRules', new FormControl(false));
this.fixedDepositProductAccountingForm.get('advancedAccountingRules').valueChanges
.subscribe((advancedAccountingRules: boolean) => {
if (advancedAccountingRules) {
this.fixedDepositProductAccountingForm.addControl('paymentChannelToFundSourceMappings', this.formBuilder.array([]));
this.fixedDepositProductAccountingForm.addControl('feeToIncomeAccountMappings', this.formBuilder.array([]));
this.fixedDepositProductAccountingForm.addControl('penaltyToIncomeAccountMappings', this.formBuilder.array([]));
} else {
this.fixedDepositProductAccountingForm.removeControl('paymentChannelToFundSourceMappings');
this.fixedDepositProductAccountingForm.removeControl('feeToIncomeAccountMappings');
this.fixedDepositProductAccountingForm.removeControl('penaltyToIncomeAccountMappings');
}
});
} else {
this.fixedDepositProductAccountingForm.removeControl('savingsReferenceAccountId');
this.fixedDepositProductAccountingForm.removeControl('overdraftPortfolioControlId');
this.fixedDepositProductAccountingForm.removeControl('savingsControlAccountId');
this.fixedDepositProductAccountingForm.removeControl('transfersInSuspenseAccountId');
this.fixedDepositProductAccountingForm.removeControl('interestOnSavingsAccountId');
this.fixedDepositProductAccountingForm.removeControl('writeOffAccountId');
this.fixedDepositProductAccountingForm.removeControl('incomeFromFeeAccountId');
this.fixedDepositProductAccountingForm.removeControl('incomeFromPenaltyAccountId');
this.fixedDepositProductAccountingForm.removeControl('incomeFromInterestId');
this.fixedDepositProductAccountingForm.removeControl('advancedAccountingRules');
this.fixedDepositProductAccountingForm.removeControl('escheatLiabilityId');
}
});
}
get paymentChannelToFundSourceMappings(): FormArray {
return this.fixedDepositProductAccountingForm.get('paymentChannelToFundSourceMappings') as FormArray;
}
get feeToIncomeAccountMappings(): FormArray {
return this.fixedDepositProductAccountingForm.get('feeToIncomeAccountMappings') as FormArray;
}
get penaltyToIncomeAccountMappings(): FormArray {
return this.fixedDepositProductAccountingForm.get('penaltyToIncomeAccountMappings') as FormArray;
}
add(formType: string, formArray: FormArray) {
const data = { ...this.getData(formType), pristine: false };
const dialogRef = this.dialog.open(FormDialogComponent, { data });
dialogRef.afterClosed().subscribe((response: any) => {
if (response.data) {
formArray.push(response.data);
}
});
}
edit(formType: string, formArray: FormArray, index: number) {
const data = { ...this.getData(formType, formArray.at(index).value), layout: { addButtonText: 'Edit' } };
const dialogRef = this.dialog.open(FormDialogComponent, { data });
dialogRef.afterClosed().subscribe((response: any) => {
if (response.data) {
formArray.at(index).patchValue(response.data.value);
}
});
}
delete(formArray: FormArray, index: number) {
const dialogRef = this.dialog.open(DeleteDialogComponent, {
data: { deleteContext: `this` }
});
dialogRef.afterClosed().subscribe((response: any) => {
if (response.delete) {
formArray.removeAt(index);
}
});
}
getData(formType: string, values?: any) {
switch (formType) {
case 'PaymentFundSource': return { title: 'Configure Fund Sources for Payment Channels', formfields: this.getPaymentFundSourceFormfields(values) };
case 'FeesIncome': return { title: 'Map Fees to Income Accounts', formfields: this.getFeesIncomeFormfields(values) };
case 'PenaltyIncome': return { title: 'Map Penalties to Specific Income Accounts', formfields: this.getPenaltyIncomeFormfields(values) };
}
}
getPaymentFundSourceFormfields(values?: any) {
const formfields: FormfieldBase[] = [
new SelectBase({
controlName: 'paymentTypeId',
label: 'Payment Type',
value: values ? values.paymentTypeId : this.paymentTypeData[0].id,
options: { label: 'name', value: 'id', data: this.paymentTypeData },
required: true,
order: 1
}),
new SelectBase({
controlName: 'fundSourceAccountId',
label: 'Fund Source',
value: values ? values.fundSourceAccountId : this.assetAccountData[0].id,
options: { label: 'name', value: 'id', data: this.assetAccountData },
required: true,
order: 2
})
];
return formfields;
}
getFeesIncomeFormfields(values?: any) {
const formfields: FormfieldBase[] = [
new SelectBase({
controlName: 'chargeId',
label: 'Fees',
value: values ? values.chargeId : this.chargeData[0].id,
options: { label: 'name', value: 'id', data: this.chargeData },
required: true,
order: 1
}),
new SelectBase({
controlName: 'incomeAccountId',
label: 'Income Account',
value: values ? values.incomeAccountId : this.incomeAccountData[0].id,
options: { label: 'name', value: 'id', data: this.incomeAccountData },
required: true,
order: 2
})
];
return formfields;
}
getPenaltyIncomeFormfields(values?: any) {
const formfields: FormfieldBase[] = [
new SelectBase({
controlName: 'chargeId',
label: 'Penalty',
value: values ? values.chargeId : this.penaltyData[0].id,
options: { label: 'name', value: 'id', data: this.penaltyData },
required: true,
order: 1
}),
new SelectBase({
controlName: 'incomeAccountId',
label: 'Income Account',
value: values ? values.incomeAccountId : this.incomeAccountData[0].id,
options: { label: 'name', value: 'id', data: this.incomeAccountData },
required: true,
order: 2
})
];
return formfields;
}
get fixedDepositProductAccounting() {
return this.fixedDepositProductAccountingForm.value;
}
}
@@ -0,0 +1,74 @@
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
<mat-form-field fxFlex="48%">
<mat-label>Charge</mat-label>
<mat-select #charge>
<mat-option *ngFor="let charge of chargeData | chargesFilter:chargesDataSource:currencyCode.value" [value]="charge">
{{ charge.name }}
</mat-option>
</mat-select>
</mat-form-field>
<div fxFlex="48%" fxFlexAlign="center">
<button type="button" mat-raised-button color="primary" (click)="addCharge(charge)" [disabled]="!charge.value">
<fa-icon icon="plus"></fa-icon>&nbsp;&nbsp;
Add
</button>
</div>
<table fxFlex="98%" class="mat-elevation-z1" mat-table [dataSource]="chargesDataSource" [hidden]="chargesDataSource.length === 0">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef> Name </th>
<td mat-cell *matCellDef="let charge">
{{ charge.name + ', ' + charge.currency.displaySymbol }}
</td>
</ng-container>
<ng-container matColumnDef="chargeCalculationType">
<th mat-header-cell *matHeaderCellDef> Type </th>
<td mat-cell *matCellDef="let charge">
{{ charge.chargeCalculationType.value }}
</td>
</ng-container>
<ng-container matColumnDef="amount">
<th mat-header-cell *matHeaderCellDef> Amount </th>
<td mat-cell *matCellDef="let charge">
{{ charge.amount }}
</td>
</ng-container>
<ng-container matColumnDef="chargeTimeType">
<th mat-header-cell *matHeaderCellDef> Collected On </th>
<td mat-cell *matCellDef="let charge">
{{ charge.chargeTimeType.value }}
</td>
</ng-container>
<ng-container matColumnDef="action">
<th mat-header-cell *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let charge">
<button mat-icon-button color="warn" (click)="deleteCharge(charge)">
<fa-icon icon="trash"></fa-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
<button mat-raised-button matStepperPrevious>
<fa-icon icon="arrow-left"></fa-icon>&nbsp;&nbsp;
Previous
</button>
<button mat-raised-button matStepperNext>
Next&nbsp;&nbsp;
<fa-icon icon="arrow-right"></fa-icon>
</button>
</div>
@@ -0,0 +1,11 @@
table {
width: 100%;
}
.mat-elevation-z1 {
margin: 1em 0 1.5em 0;
}
.margin-t {
margin-top: 1em;
}
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FixedDepositProductChargesStepComponent } from './fixed-deposit-product-charges-step.component';
describe('FixedDepositProductChargesStepComponent', () => {
let component: FixedDepositProductChargesStepComponent;
let fixture: ComponentFixture<FixedDepositProductChargesStepComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FixedDepositProductChargesStepComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FixedDepositProductChargesStepComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,56 @@
import { Component, OnInit, Input } from '@angular/core';
import { MatDialog } from '@angular/material';
import { FormControl } from '@angular/forms';
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
@Component({
selector: 'mifosx-fixed-deposit-product-charges-step',
templateUrl: './fixed-deposit-product-charges-step.component.html',
styleUrls: ['./fixed-deposit-product-charges-step.component.scss']
})
export class FixedDepositProductChargesStepComponent implements OnInit {
@Input() fixedDepositProductsTemplate: any;
@Input() currencyCode: FormControl;
chargeData: any;
chargesDataSource: {}[];
displayedColumns: string[] = ['name', 'chargeCalculationType', 'amount', 'chargeTimeType', 'action'];
constructor(public dialog: MatDialog) {
}
ngOnInit() {
this.chargeData = this.fixedDepositProductsTemplate.chargeOptions;
this.chargesDataSource = [];
this.currencyCode.valueChanges.subscribe(() => this.chargesDataSource = []);
}
addCharge(charge: any) {
this.chargesDataSource = this.chargesDataSource.concat([charge.value]);
charge.value = '';
}
deleteCharge(charge: any) {
const deleteChargeDialogRef = this.dialog.open(DeleteDialogComponent, {
data: { deleteContext: `charge ${charge.name}` }
});
deleteChargeDialogRef.afterClosed().subscribe((response: any) => {
if (response.delete) {
this.chargesDataSource.splice(this.chargesDataSource.indexOf(charge), 1);
this.chargesDataSource = this.chargesDataSource.concat([]);
}
});
}
get fixedDepositProductCharges() {
return {
charges: this.chargesDataSource
};
}
}
@@ -0,0 +1,46 @@
<form [formGroup]="fixedDepositProductCurrencyForm">
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
<mat-form-field fxFlex="48%">
<mat-label>Currency</mat-label>
<mat-select formControlName="currencyCode" required>
<mat-option *ngFor="let currency of currencyData" [value]="currency.code">
{{ currency.name }}
</mat-option>
</mat-select>
<mat-error>
Currency is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Decimal Places</mat-label>
<input type="number" matInput formControlName="digitsAfterDecimal" required>
<mat-error>
Decimal Places is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Currency in multiples of</mat-label>
<input type="number" matInput formControlName="inMultiplesOf" required>
<mat-error>
Currency in multiples of is <strong>required</strong>
</mat-error>
</mat-form-field>
</div>
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
<button mat-raised-button matStepperPrevious>
<fa-icon icon="arrow-left"></fa-icon>&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,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FixedDepositProductCurrencyStepComponent } from './fixed-deposit-product-currency-step.component';
describe('FixedDepositProductCurrencyStepComponent', () => {
let component: FixedDepositProductCurrencyStepComponent;
let fixture: ComponentFixture<FixedDepositProductCurrencyStepComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FixedDepositProductCurrencyStepComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FixedDepositProductCurrencyStepComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,42 @@
import { Component, OnInit, Input } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
@Component({
selector: 'mifosx-fixed-deposit-product-currency-step',
templateUrl: './fixed-deposit-product-currency-step.component.html',
styleUrls: ['./fixed-deposit-product-currency-step.component.scss']
})
export class FixedDepositProductCurrencyStepComponent implements OnInit {
@Input() fixedDepositProductsTemplate: any;
fixedDepositProductCurrencyForm: FormGroup;
currencyData: any;
constructor(private formBuilder: FormBuilder) {
this.createFixedDepositProductCurrencyForm();
}
ngOnInit() {
this.currencyData = this.fixedDepositProductsTemplate.currencyOptions;
this.fixedDepositProductCurrencyForm.patchValue({
'currencyCode': this.currencyData[0].code,
'digitsAfterDecimal': 2
});
}
createFixedDepositProductCurrencyForm() {
this.fixedDepositProductCurrencyForm = this.formBuilder.group({
'currencyCode': ['', Validators.required],
'digitsAfterDecimal': ['', Validators.required],
'inMultiplesOf': ['', Validators.required]
});
}
get fixedDepositProductCurrency() {
return this.fixedDepositProductCurrencyForm.value;
}
}
@@ -0,0 +1,39 @@
<form [formGroup]="fixedDepositProductDetailsForm">
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
<mat-form-field fxFlex="48%">
<mat-label>Product Name</mat-label>
<input matInput formControlName="name" required>
<mat-error>
Product Name is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Short Name</mat-label>
<input matInput formControlName="shortName" maxlength="4" required>
<mat-error>
Short Name is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field fxFlex="98%">
<mat-label>Description</mat-label>
<textarea matInput formControlName="description"></textarea>
</mat-form-field>
</div>
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
<button mat-raised-button matStepperPrevious disabled>
<fa-icon icon="arrow-left"></fa-icon>&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,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FixedDepositProductDetailsStepComponent } from './fixed-deposit-product-details-step.component';
describe('FixedDepositProductDetailsStepComponent', () => {
let component: FixedDepositProductDetailsStepComponent;
let fixture: ComponentFixture<FixedDepositProductDetailsStepComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FixedDepositProductDetailsStepComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FixedDepositProductDetailsStepComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,32 @@
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
@Component({
selector: 'mifosx-fixed-deposit-product-details-step',
templateUrl: './fixed-deposit-product-details-step.component.html',
styleUrls: ['./fixed-deposit-product-details-step.component.scss']
})
export class FixedDepositProductDetailsStepComponent implements OnInit {
fixedDepositProductDetailsForm: FormGroup;
constructor(private formBuilder: FormBuilder) {
this.createFixedDepositProductDetailsForm();
}
ngOnInit() {
}
createFixedDepositProductDetailsForm() {
this.fixedDepositProductDetailsForm = this.formBuilder.group({
'name': ['', Validators.required],
'shortName': ['', Validators.required],
'description': ['']
});
}
get fixedDepositProductDetails() {
return this.fixedDepositProductDetailsForm.value;
}
}
@@ -0,0 +1,225 @@
<form [formGroup]="fixedDepositProductInterestRateChartForm">
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
<h3 fxFlex="13%" class="mat-h3">Interest Rate Charts</h3>
<div fxFlex="83%" fxLayout="row" fxLayoutAlign="start center">
<button type="button" mat-raised-button color="primary" (click)="addChart()">
<fa-icon icon="plus"></fa-icon>&nbsp;&nbsp;
Add
</button>
</div>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column" formArrayName="charts" *ngFor="let chart of charts.controls; let chartIndex = index">
<mat-divider fxFlex="98%"></mat-divider>
<div fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column" [formGroupName]="chartIndex">
<div fxFlex="98%" fxLayout="row" fxLayoutAlign="end center">
<button type="button" mat-icon-button color="warn" (click)="delete(charts, chartIndex)" matTooltip="Delete Interest Rate Chart" matTooltipPosition="left">
<fa-icon icon="trash"></fa-icon>
</button>
</div>
<mat-form-field fxFlex="32%">
<mat-label>Name</mat-label>
<input matInput formControlName="name">
</mat-form-field>
<mat-form-field fxFlex="31%">
<mat-label>Valid from Date</mat-label>
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="validFromDatePicker" formControlName="fromDate" required>
<mat-datepicker-toggle matSuffix [for]="validFromDatePicker"></mat-datepicker-toggle>
<mat-datepicker #validFromDatePicker></mat-datepicker>
<mat-error>
Valid from Date is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field fxFlex="31%">
<mat-label>End Date</mat-label>
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="endDatePicker" formControlName="endDate">
<mat-datepicker-toggle matSuffix [for]="endDatePicker"></mat-datepicker-toggle>
<mat-datepicker #endDatePicker></mat-datepicker>
</mat-form-field>
<mat-form-field fxFlex="65%">
<mat-label>Description</mat-label>
<textarea matInput formControlName="description"></textarea>
</mat-form-field>
<div fxFlex="31%" fxLayout="row" fxLayoutAlign="space-between center">
<mat-checkbox labelPosition="before" formControlName="isPrimaryGroupingByAmount">
Is primary grouping by amount?
</mat-checkbox>
<button type="button" mat-raised-button color="primary" (click)="addChartSlab(chart.controls.chartSlabs)">
<fa-icon icon="plus"></fa-icon>&nbsp;&nbsp;
Add Slab
</button>
</div>
<table fxFlex="98%" class="mat-elevation-z1" mat-table [dataSource]="chart.value.chartSlabs" *ngIf="chart.value.chartSlabs.length !== 0" multiTemplateDataRows>
<ng-container matColumnDef="period">
<th mat-header-cell *matHeaderCellDef> Period </th>
<td mat-cell *matCellDef="let chartSlab">
{{ chartSlab.fromPeriod + ' - ' + chartSlab.toPeriod + ' ' + (chartSlab.periodType | find:periodTypeData:'id':'value') }}
</td>
</ng-container>
<ng-container matColumnDef="amountRange">
<th mat-header-cell *matHeaderCellDef> Amount Range </th>
<td mat-cell *matCellDef="let chartSlab">
{{ chartSlab.amountRangeFrom + ' - ' + chartSlab.amountRangeTo }}
</td>
</ng-container>
<ng-container matColumnDef="annualInterestRate">
<th mat-header-cell *matHeaderCellDef> Interest </th>
<td mat-cell *matCellDef="let chartSlab">
{{ chartSlab.annualInterestRate }}
</td>
</ng-container>
<ng-container matColumnDef="description">
<th mat-header-cell *matHeaderCellDef> Description </th>
<td mat-cell *matCellDef="let chartSlab">
{{ chartSlab.description }}
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let chartSlab; let chartSlabIndex = dataIndex">
<button mat-icon-button color="primary" (click)="editChartSlab(chart.controls.chartSlabs, chartSlabIndex)">
<fa-icon icon="edit"></fa-icon>
</button>
<button mat-icon-button color="warn" (click)="delete(chart.controls.chartSlabs, chartSlabIndex)">
<fa-icon icon="trash"></fa-icon>
</button>
<button mat-button color="primary" (click)="expandChartSlabIndex[chartIndex] = expandChartSlabIndex[chartIndex] === chartSlabIndex ? null : chartSlabIndex">
<span *ngIf="expandChartSlabIndex[chartIndex] !== chartSlabIndex">
<fa-icon icon="eye"></fa-icon>&nbsp;&nbsp;
View Incentives
</span>
<span *ngIf="expandChartSlabIndex[chartIndex] === chartSlabIndex">
<fa-icon icon="eye-slash"></fa-icon>&nbsp;&nbsp;
Hide Incentives
</span>
</button>
</td>
</ng-container>
<ng-container matColumnDef="incentives">
<td mat-cell *matCellDef="let chartSlab; let chartSlabIndex = dataIndex" [attr.colspan]="chartSlabsDisplayedColumns[chartIndex].length">
<div fxLayout="row wrap" fxFlexFill class="incentives" [@expandChartSlab]="chartSlabIndex === expandChartSlabIndex[chartIndex] ? 'expanded' : 'collapsed'">
<mat-card fxLayout="row wrap" fxFlexFill>
<h4 class="mat-h4" fxFlex="13%">
Incentives
</h4>
<div fxFlex="83%">
<button mat-raised-button color="primary" (click)="addIncentive(getIncentives(chart.controls.chartSlabs, chartSlabIndex))">
<fa-icon icon="plus"></fa-icon>&nbsp;&nbsp;
Add
</button>
</div>
<table fxFlexFill class="mat-elevation-z1" mat-table [dataSource]="chartSlab.incentives" *ngIf="chartSlab.incentives.length">
<ng-container matColumnDef="entityType">
<th mat-header-cell *matHeaderCellDef> Entity Type </th>
<td mat-cell *matCellDef="let incentive">
{{ incentive.entityType | find:entityTypeData:'id':'value' }}
</td>
</ng-container>
<ng-container matColumnDef="attributeName">
<th mat-header-cell *matHeaderCellDef> Attribute Name </th>
<td mat-cell *matCellDef="let incentive">
{{ incentive.attributeName | find:attributeNameData:'id':'value' }}
</td>
</ng-container>
<ng-container matColumnDef="conditionType">
<th mat-header-cell *matHeaderCellDef> Condition Type </th>
<td mat-cell *matCellDef="let incentive">
{{ incentive.conditionType | find:conditionTypeData:'id':'value' }}
</td>
</ng-container>
<ng-container matColumnDef="attributeValue">
<th mat-header-cell *matHeaderCellDef> Attribute Value </th>
<td mat-cell *matCellDef="let incentive" [ngSwitch]="incentive.attributeName">
<span *ngSwitchCase="2">{{ incentive.attributeValue | find:genderData:'id':'name' }}</span>
<span *ngSwitchCase="3">{{ incentive.attributeValue }}</span>
<span *ngSwitchCase="4">{{ incentive.attributeValue | find:clientTypeData:'id':'name' }}</span>
<span *ngSwitchCase="5">{{ incentive.attributeValue | find:clientClassificationData:'id':'name' }}</span>
</td>
</ng-container>
<ng-container matColumnDef="incentiveType">
<th mat-header-cell *matHeaderCellDef> Incentive Type </th>
<td mat-cell *matCellDef="let incentive">
{{ incentive.incentiveType | find:incentiveTypeData:'id':'value'}}
</td>
</ng-container>
<ng-container matColumnDef="amount">
<th mat-header-cell *matHeaderCellDef> Interest </th>
<td mat-cell *matCellDef="let incentive">
{{ incentive.amount }}
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let incentive; let incentiveIndex = index">
<button mat-icon-button color="primary" (click)="editIncentive(getIncentives(chart.controls.chartSlabs, chartSlabIndex), incentiveIndex)">
<fa-icon icon="edit"></fa-icon>
</button>
<button mat-icon-button color="warn" (click)="delete(getIncentives(chart.controls.chartSlabs, chartSlabIndex), incentiveIndex)">
<fa-icon icon="trash"></fa-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="incentivesDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: incentivesDisplayedColumns;"></tr>
</table>
</mat-card>
</div>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="chartSlabsDisplayedColumns[chartIndex]"></tr>
<tr mat-row *matRowDef="let row; columns: chartSlabsDisplayedColumns[chartIndex];"></tr>
<tr mat-row *matRowDef="let row; columns: chartSlabsIncentivesDisplayedColumns;" class="incentives-row"></tr>
</table>
</div>
</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,47 @@
table {
width: 100%;
tr.incentives-row {
height: 0;
> td {
padding: 0;
}
.incentives {
overflow: hidden;
}
.mat-card {
border-radius: 0;
> h4 {
margin: 0.5em 0 0;
}
}
}
}
mat-divider {
margin: 1em 0 1em 0;
}
.mat-elevation-z1 {
margin: 1em 0 1.5em 0;
}
h3, h4 {
font-weight: 500;
}
h3, h4 {
margin: 1em 0;
}
.margin-t {
margin-top: 1em;
}
.margin-v {
margin: 1em 0;
}
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FixedDepositProductInterestRateChartStepComponent } from './fixed-deposit-product-interest-rate-chart-step.component';
describe('FixedDepositProductInterestRateChartStepComponent', () => {
let component: FixedDepositProductInterestRateChartStepComponent;
let fixture: ComponentFixture<FixedDepositProductInterestRateChartStepComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FixedDepositProductInterestRateChartStepComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FixedDepositProductInterestRateChartStepComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,241 @@
import { Component, OnInit, Input } from '@angular/core';
import { FormGroup, FormBuilder, Validators, FormArray } from '@angular/forms';
import { DatePipe } from '@angular/common';
import { MatDialog } from '@angular/material';
import { animate, state, style, transition, trigger } from '@angular/animations';
import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component';
import { DepositProductIncentiveFormDialogComponent } from 'app/products/deposit-product-incentive-form-dialog/deposit-product-incentive-form-dialog.component';
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-base';
import { SelectBase } from 'app/shared/form-dialog/formfield/model/select-base';
import { InputBase } from 'app/shared/form-dialog/formfield/model/input-base';
@Component({
selector: 'mifosx-fixed-deposit-product-interest-rate-chart-step',
templateUrl: './fixed-deposit-product-interest-rate-chart-step.component.html',
styleUrls: ['./fixed-deposit-product-interest-rate-chart-step.component.scss'],
animations: [
trigger('expandChartSlab', [
state('collapsed', style({ height: '0px', minHeight: '0' })),
state('expanded', style({ height: '*' })),
transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)'))
])
]
})
export class FixedDepositProductInterestRateChartStepComponent implements OnInit {
@Input() fixedDepositProductsTemplate: any;
fixedDepositProductInterestRateChartForm: FormGroup;
periodTypeData: any;
entityTypeData: any;
attributeNameData: any;
conditionTypeData: any;
genderData: any;
clientTypeData: any;
clientClassificationData: any;
incentiveTypeData: any;
chartSlabsDisplayedColumns: any[] = [];
chartSlabsIncentivesDisplayedColumns: string[] = ['incentives'];
incentivesDisplayedColumns: string[] = ['entityType', 'attributeName', 'conditionType', 'attributeValue', 'incentiveType', 'amount', 'actions'];
minDate = new Date(2000, 0, 1);
maxDate = new Date(new Date().setFullYear(new Date().getFullYear() + 10));
expandChartSlabIndex: number[] = [];
constructor(private formBuilder: FormBuilder,
public dialog: MatDialog,
private datePipe: DatePipe) {
this.createFixedDepositProductInterestRateChartForm();
}
ngOnInit() {
this.periodTypeData = this.fixedDepositProductsTemplate.chartTemplate.periodTypes;
this.entityTypeData = this.fixedDepositProductsTemplate.chartTemplate.entityTypeOptions;
this.attributeNameData = this.fixedDepositProductsTemplate.chartTemplate.attributeNameOptions;
this.conditionTypeData = this.fixedDepositProductsTemplate.chartTemplate.conditionTypeOptions;
this.genderData = this.fixedDepositProductsTemplate.chartTemplate.genderOptions;
this.clientTypeData = this.fixedDepositProductsTemplate.chartTemplate.clientTypeOptions;
this.clientClassificationData = this.fixedDepositProductsTemplate.chartTemplate.clientClassificationOptions;
this.incentiveTypeData = this.fixedDepositProductsTemplate.chartTemplate.incentiveTypeOptions;
}
createFixedDepositProductInterestRateChartForm() {
this.fixedDepositProductInterestRateChartForm = this.formBuilder.group({
'charts': this.formBuilder.array([])
});
}
get charts(): FormArray {
return this.fixedDepositProductInterestRateChartForm.get('charts') as FormArray;
}
createChartForm(): FormGroup {
return this.formBuilder.group({
'name': [''],
'description': [''],
'fromDate': ['', Validators.required],
'endDate': [''],
'isPrimaryGroupingByAmount': [false],
'chartSlabs': this.formBuilder.array([])
});
}
addChart() {
this.charts.push(this.createChartForm());
this.setConditionalControls(this.charts.length - 1);
}
setConditionalControls(chartIndex: number) {
this.chartSlabsDisplayedColumns[chartIndex] = ['period', 'amountRange', 'annualInterestRate', 'description', 'actions'];
this.charts.at(chartIndex).get('isPrimaryGroupingByAmount').valueChanges
.subscribe((isPrimaryGroupingByAmount: boolean) => {
this.chartSlabsDisplayedColumns[chartIndex] = isPrimaryGroupingByAmount ? ['amountRange', 'period'] : ['period', 'amountRange'];
this.chartSlabsDisplayedColumns[chartIndex].push('annualInterestRate', 'description', 'actions');
});
}
getIncentives(chartSlabs: FormArray, chartSlabIndex: number): FormArray {
return chartSlabs.at(chartSlabIndex).get('incentives') as FormArray;
}
addChartSlab(chartSlabs: FormArray) {
const data = { ...this.getData('Slab') };
const dialogRef = this.dialog.open(FormDialogComponent, { data });
dialogRef.afterClosed().subscribe((response: any) => {
if (response.data) {
response.data.addControl('incentives', this.formBuilder.array([]));
chartSlabs.push(response.data);
}
});
}
addIncentive(incentives: FormArray) {
const data = { ...this.getData('Incentive'), entityType: this.entityTypeData[0].id };
const dialogRef = this.dialog.open(DepositProductIncentiveFormDialogComponent, { data });
dialogRef.afterClosed().subscribe((response: any) => {
if (response.data) {
incentives.push(response.data);
}
});
}
editChartSlab(chartSlabs: FormArray, chartSlabIndex: number) {
const data = { ...this.getData('Slab', chartSlabs.at(chartSlabIndex).value), layout: { addButtonText: 'Edit' } };
const dialogRef = this.dialog.open(FormDialogComponent, { data });
dialogRef.afterClosed().subscribe((response: any) => {
if (response.data) {
chartSlabs.at(chartSlabIndex).patchValue(response.data.value);
}
});
}
editIncentive(incentives: FormArray, incentiveIndex: number) {
const data = { ...this.getData('Incentive', incentives.at(incentiveIndex).value), layout: { addButtonText: 'Edit' } };
const dialogRef = this.dialog.open(DepositProductIncentiveFormDialogComponent, { data });
dialogRef.afterClosed().subscribe((response: any) => {
if (response.data) {
incentives.at(incentiveIndex).patchValue(response.data.value);
}
});
}
delete(formArray: FormArray, index: number) {
const dialogRef = this.dialog.open(DeleteDialogComponent, {
data: { deleteContext: `this` }
});
dialogRef.afterClosed().subscribe((response: any) => {
if (response.delete) {
formArray.removeAt(index);
}
});
}
getData(formType: string, values?: any) {
switch (formType) {
case 'Slab': return { title: 'Slab', formfields: this.getSlabFormfields(values) };
case 'Incentive': return { values, chartTemplate: this.fixedDepositProductsTemplate.chartTemplate };
}
}
getSlabFormfields(values?: any) {
const formfields: FormfieldBase[] = [
new SelectBase({
controlName: 'periodType',
label: 'Period Type',
value: values ? values.periodType : this.periodTypeData[0].id,
options: { label: 'value', value: 'id', data: this.periodTypeData },
required: true,
order: 1
}),
new InputBase({
controlName: 'fromPeriod',
label: 'Period From',
value: values ? values.fromPeriod : undefined,
type: 'number',
required: true,
order: 2
}),
new InputBase({
controlName: 'toPeriod',
label: 'Period To',
value: values ? values.toPeriod : undefined,
type: 'number',
order: 3
}),
new InputBase({
controlName: 'amountRangeFrom',
label: 'Amount Range From',
value: values ? values.amountRangeFrom : undefined,
type: 'number',
order: 4
}),
new InputBase({
controlName: 'amountRangeTo',
label: 'Amount Range To',
value: values ? values.amountRangeTo : undefined,
type: 'number',
order: 5
}),
new InputBase({
controlName: 'annualInterestRate',
label: 'Interest',
value: values ? values.annualInterestRate : undefined,
type: 'number',
required: true,
order: 6
}),
new InputBase({
controlName: 'description',
label: 'Description',
value: values ? values.description : undefined,
required: true,
order: 7
})
];
return formfields;
}
get fixedDepositProductInterestRateChart() {
// TODO: Update once language and date settings are setup
const dateFormat = 'yyyy-MM-dd';
const locale = 'en';
const fixedDepositProductInterestRateChart = this.fixedDepositProductInterestRateChartForm.value;
for (const chart of fixedDepositProductInterestRateChart.charts) {
chart.dateFormat = dateFormat;
chart.locale = locale;
chart.fromDate = this.datePipe.transform(chart.fromDate, dateFormat) || '';
chart.endDate = this.datePipe.transform(chart.endDate, dateFormat) || '';
if (chart.endDate === '') {
delete chart.endDate;
}
}
return fixedDepositProductInterestRateChart;
}
}
@@ -0,0 +1,460 @@
<div fxLayout="row wrap" fxLayout.lt-md="column">
<h2 class="mat-h2" fxFlexFill>{{ fixedDepositProduct.name }}</h2>
<mat-divider fxFlexFill></mat-divider>
<h3 class="mat-h3" fxFlexFill>Details</h3>
<mat-divider fxFlexFill></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Short Name:</span>
<span fxFlex="60%">{{ fixedDepositProduct.shortName }}</span>
</div>
<div fxFlexFill *ngIf="fixedDepositProduct.description">
<span fxFlex="40%">Description:</span>
<span fxFlex="60%">{{ fixedDepositProduct.description }}</span>
</div>
<h3 class="mat-h3" fxFlexFill>Currency</h3>
<mat-divider fxFlexFill></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Currency:</span>
<span fxFlex="60%">{{ fixedDepositProduct.currencyCode | find:fixedDepositProductsTemplate.currencyOptions:'code':'name' }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Decimal Places:</span>
<span fxFlex="60%">{{ fixedDepositProduct.digitsAfterDecimal }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Currency in multiples of:</span>
<span fxFlex="60%">{{ fixedDepositProduct.inMultiplesOf }}</span>
</div>
<h3 class="mat-h3" fxFlexFill>Terms</h3>
<mat-divider fxFlexFill></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Deposit Amount:</span>
<span fxFlex="60%">{{ fixedDepositProduct.depositAmount + ' (Min: ' + (fixedDepositProduct.minDepositAmount ? fixedDepositProduct.minDepositAmount : fixedDepositProduct.depositAmount) + ', Max: ' + (fixedDepositProduct.maxDepositAmount ? fixedDepositProduct.maxDepositAmount : fixedDepositProduct.depositAmount) + ')' }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Interest Compounding Period:</span>
<span fxFlex="60%">{{ fixedDepositProduct.interestCompoundingPeriodType | find:fixedDepositProductsTemplate.interestCompoundingPeriodTypeOptions:'id':'value' }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Interest Posting Period:</span>
<span fxFlex="60%">{{ fixedDepositProduct.interestPostingPeriodType | find:fixedDepositProductsTemplate.interestPostingPeriodTypeOptions:'id':'value' }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Interest Calculated using:</span>
<span fxFlex="60%">{{ fixedDepositProduct.interestCalculationType | find:fixedDepositProductsTemplate.interestCalculationTypeOptions:'id':'value' }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Days in Year:</span>
<span fxFlex="60%">{{ fixedDepositProduct.interestCalculationDaysInYearType | find:fixedDepositProductsTemplate.interestCalculationDaysInYearTypeOptions:'id':'value' }}</span>
</div>
<h3 class="mat-h3" fxFlexFill>Settings</h3>
<mat-divider fxFlexFill></mat-divider>
<div fxFlexFill *ngIf="fixedDepositProduct.lockinPeriodFrequency">
<span fxFlex="40%">Lock-in Period:</span>
<span fxFlex="60%">{{ fixedDepositProduct.lockinPeriodFrequency + ' ' + (fixedDepositProduct.lockinPeriodFrequencyType | find:fixedDepositProductsTemplate.lockinPeriodFrequencyTypeOptions:'id':'value') }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Minimum Deposit Term:</span>
<span fxFlex="60%">{{ fixedDepositProduct.minDepositTerm + ' ' + (fixedDepositProduct.minDepositTermTypeId | find:fixedDepositProductsTemplate.periodFrequencyTypeOptions:'id':'value') }}</span>
</div>
<div fxFlexFill *ngIf="fixedDepositProduct.inMultiplesOfDepositTerm">
<span fxFlex="40%">And thereafter, in Multiples of:</span>
<span fxFlex="60%">{{ fixedDepositProduct.inMultiplesOfDepositTerm + ' ' + (fixedDepositProduct.inMultiplesOfDepositTermTypeId | find:fixedDepositProductsTemplate.periodFrequencyTypeOptions:'id':'value') }}</span>
</div>
<div fxFlexFill *ngIf="fixedDepositProduct.maxDepositTerm">
<span fxFlex="40%">Minimum Deposit Term:</span>
<span fxFlex="60%">{{ fixedDepositProduct.maxDepositTerm + ' ' + (fixedDepositProduct.maxDepositTermTypeId | find:fixedDepositProductsTemplate.periodFrequencyTypeOptions:'id':'value') }}</span>
</div>
<!-- For Pre-mature closure -->
<div fxFlexFill>
<span fxFlex="40%">Apply Penal Interest (less):</span>
<span fxFlex="60%">{{ fixedDepositProduct.preClosurePenalApplicable ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="fixedDepositProduct.preClosurePenalApplicable">
<span fxFlex="40%">Penal Interest (%):</span>
<span fxFlex="60%">{{ fixedDepositProduct.preClosurePenalInterest + '% (' + (fixedDepositProduct.preClosurePenalInterestOnTypeId | find:fixedDepositProductsTemplate.preClosurePenalInterestOnTypeOptions:'id':'value') + ')' }}</span>
</div>
<!-- -->
<div fxFlexFill>
<span fxFlex="40%">Withhold Tax is Applicable:</span>
<span fxFlex="60%">{{ fixedDepositProduct.withHoldTax ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="fixedDepositProduct.withHoldTax">
<span fxFlex="40%">Tax Group:</span>
<span fxFlex="60%">{{ fixedDepositProduct.taxGroupId | find:fixedDepositProductsTemplate.taxGroupOptions:'id':'name' }}</span>
</div>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column" *ngIf="fixedDepositProduct.charts.length !== 0">
<h3 class="mat-h3" fxFlexFill>Interest Rate Charts</h3>
<mat-divider fxFlexFill></mat-divider>
<div fxFlexFill *ngFor="let chart of fixedDepositProduct.charts; let chartIndex = index" fxLayout="row wrap" fxLayout.lt-md="column">
<div fxFlexFill *ngIf="chart.name">
<span fxFlex="40%">Name:</span>
<span fxFlex="60%">{{ chart.name }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Valid from Date:</span>
<span fxFlex="60%">{{ chart.fromDate | date }}</span>
</div>
<div fxFlexFill *ngIf="chart.endDate">
<span fxFlex="40%">End Date:</span>
<span fxFlex="60%">{{ chart.endDate | date }}</span>
</div>
<div fxFlexFill *ngIf="chart.description">
<span fxFlex="40%">Description:</span>
<span fxFlex="60%">{{ chart.description }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Primary Grouping by Amount:</span>
<span fxFlex="60%">{{ fixedDepositProduct.isPrimaryGroupingByAmount ? 'Yes' : 'No' }}</span>
</div>
<table fxFlexFill class="mat-elevation-z1" mat-table [dataSource]="chart.chartSlabs" *ngIf="chart.chartSlabs.length !== 0" multiTemplateDataRows>
<ng-container matColumnDef="period">
<th mat-header-cell *matHeaderCellDef> Period </th>
<td mat-cell *matCellDef="let chartSlab">
{{ chartSlab.fromPeriod + ' - ' + chartSlab.toPeriod + ' ' + (chartSlab.periodType | find:fixedDepositProductsTemplate.chartTemplate.periodTypes:'id':'value') }}
</td>
</ng-container>
<ng-container matColumnDef="amountRange">
<th mat-header-cell *matHeaderCellDef> Amount Range </th>
<td mat-cell *matCellDef="let chartSlab">
{{ chartSlab.amountRangeFrom + ' - ' + chartSlab.amountRangeTo }}
</td>
</ng-container>
<ng-container matColumnDef="annualInterestRate">
<th mat-header-cell *matHeaderCellDef> Interest </th>
<td mat-cell *matCellDef="let chartSlab">
{{ chartSlab.annualInterestRate }}
</td>
</ng-container>
<ng-container matColumnDef="description">
<th mat-header-cell *matHeaderCellDef> Description </th>
<td mat-cell *matCellDef="let chartSlab">
{{ chartSlab.description }}
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let chartSlab; let chartSlabIndex = dataIndex">
<button mat-button color="primary" (click)="expandChartSlabIndex[chartIndex] = expandChartSlabIndex[chartIndex] === chartSlabIndex ? null : chartSlabIndex" [disabled]="chartSlab.incentives.length === 0">
<span *ngIf="expandChartSlabIndex[chartIndex] !== chartSlabIndex">
<fa-icon icon="eye"></fa-icon>&nbsp;&nbsp;
View Incentives
</span>
<span *ngIf="expandChartSlabIndex[chartIndex] === chartSlabIndex">
<fa-icon icon="eye-slash"></fa-icon>&nbsp;&nbsp;
Hide Incentives
</span>
</button>
</td>
</ng-container>
<ng-container matColumnDef="incentives">
<td mat-cell *matCellDef="let chartSlab; let chartSlabIndex = dataIndex" [attr.colspan]="chartSlabsDisplayedColumns[chartIndex].length">
<div fxLayout="row wrap" fxFlexFill class="incentives" [@expandChartSlab]="chartSlabIndex === expandChartSlabIndex[chartIndex] ? 'expanded' : 'collapsed'">
<mat-card fxLayout="row wrap" fxFlexFill>
<h4 class="mat-h4" fxFlex="23%">
Incentives
</h4>
<table fxFlexFill class="mat-elevation-z1" mat-table [dataSource]="chartSlab.incentives">
<ng-container matColumnDef="entityType">
<th mat-header-cell *matHeaderCellDef> Entity Type </th>
<td mat-cell *matCellDef="let incentive">
{{ incentive.entityType | find:fixedDepositProductsTemplate.chartTemplate.entityTypeOptions:'id':'value' }}
</td>
</ng-container>
<ng-container matColumnDef="attributeName">
<th mat-header-cell *matHeaderCellDef> Attribute Name </th>
<td mat-cell *matCellDef="let incentive">
{{ incentive.attributeName | find:fixedDepositProductsTemplate.chartTemplate.attributeNameOptions:'id':'value' }}
</td>
</ng-container>
<ng-container matColumnDef="conditionType">
<th mat-header-cell *matHeaderCellDef> Condition Type </th>
<td mat-cell *matCellDef="let incentive">
{{ incentive.conditionType | find:fixedDepositProductsTemplate.chartTemplate.conditionTypeOptions:'id':'value' }}
</td>
</ng-container>
<ng-container matColumnDef="attributeValue">
<th mat-header-cell *matHeaderCellDef> Attribute Value </th>
<td mat-cell *matCellDef="let incentive" [ngSwitch]="incentive.attributeName">
<span *ngSwitchCase="2">{{ incentive.attributeValue | find:fixedDepositProductsTemplate.chartTemplate.genderOptions:'id':'name' }}</span>
<span *ngSwitchCase="3">{{ incentive.attributeValue }}</span>
<span *ngSwitchCase="4">{{ incentive.attributeValue | find:fixedDepositProductsTemplate.chartTemplate.clientTypeOptions:'id':'name' }}</span>
<span *ngSwitchCase="5">{{ incentive.attributeValue | find:fixedDepositProductsTemplate.chartTemplate.clientClassificationOptions:'id':'name' }}</span>
</td>
</ng-container>
<ng-container matColumnDef="incentiveType">
<th mat-header-cell *matHeaderCellDef> Incentive Type </th>
<td mat-cell *matCellDef="let incentive">
{{ incentive.incentiveType | find:fixedDepositProductsTemplate.chartTemplate.incentiveTypeOptions:'id':'value'}}
</td>
</ng-container>
<ng-container matColumnDef="amount">
<th mat-header-cell *matHeaderCellDef> Amount </th>
<td mat-cell *matCellDef="let incentive">
{{ incentive.amount }}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="incentivesDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: incentivesDisplayedColumns;"></tr>
</table>
</mat-card>
</div>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="chartSlabsDisplayedColumns[chartIndex]"></tr>
<tr mat-row *matRowDef="let row; columns: chartSlabsDisplayedColumns[chartIndex];"></tr>
<tr mat-row *matRowDef="let row; columns: chartSlabsIncentivesDisplayedColumns;" class="incentives-row"></tr>
</table>
<mat-divider fxFlexFill></mat-divider>
</div>
</div>
<div fxFlexFill *ngIf="fixedDepositProduct.charges.length" fxLayout="row wrap" fxLayout.lt-md="column">
<h3 class="mat-h3" fxFlexFill>Charges</h3>
<mat-divider fxFlexFill></mat-divider>
<table fxFlexFill class="mat-elevation-z1" mat-table [dataSource]="fixedDepositProduct.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 fxFlexFill></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Type:</span>
<span fxFlex="60%">{{ accountingRuleData[fixedDepositProduct.accountingRule-1] }}</span>
</div>
<div fxFlexFill *ngIf="fixedDepositProduct.accountingRule === 2" fxLayout="row wrap" fxLayout.lt-md="column">
<h4 fxFlexFill class="mat-h4">Assets</h4>
<div fxFlexFill>
<span fxFlex="40%">Saving reference:</span>
<span fxFlex="60%">{{ fixedDepositProduct.savingsReferenceAccountId | find:fixedDepositProductsTemplate.accountingMappingOptions.assetAccountOptions:'id':'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%">{{ fixedDepositProduct.savingsControlAccountId | find:fixedDepositProductsTemplate.accountingMappingOptions.liabilityAccountOptions:'id':'name' }}</span>
<span fxFlex="40%">Savings transfers in suspense:</span>
<span fxFlex="60%">{{ fixedDepositProduct.transfersInSuspenseAccountId | find:fixedDepositProductsTemplate.accountingMappingOptions.liabilityAccountOptions:'id':'name' }}</span>
</div>
<h4 fxFlexFill class="mat-h4">Expenses</h4>
<div fxFlexFill>
<span fxFlex="40%">Interest on savings:</span>
<span fxFlex="60%">{{ fixedDepositProduct.interestOnSavingsAccountId | find:fixedDepositProductsTemplate.accountingMappingOptions.expenseAccountOptions:'id':'name' }}</span>
</div>
<h4 fxFlexFill class="mat-h4">Income</h4>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Income from fees:</span>
<span fxFlex="60%">{{ fixedDepositProduct.incomeFromFeeAccountId | find:fixedDepositProductsTemplate.accountingMappingOptions.incomeAccountOptions:'id':'name' }}</span>
<span fxFlex="40%">Income from penalties:</span>
<span fxFlex="60%">{{ fixedDepositProduct.incomeFromPenaltyAccountId | find:fixedDepositProductsTemplate.accountingMappingOptions.incomeAccountOptions:'id':'name' }}</span>
</div>
<div *ngIf="fixedDepositProduct.advancedAccountingRules && (fixedDepositProduct.paymentChannelToFundSourceMappings?.length || fixedDepositProduct.feeToIncomeAccountMappings?.length || fixedDepositProduct.penaltyToIncomeAccountMappings?.length)" fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<h3 class="mat-h3" fxFlexFill>Advanced Accounting Rules</h3>
<mat-divider fxFlexFill></mat-divider>
<div *ngIf="fixedDepositProduct.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]="fixedDepositProduct.paymentChannelToFundSourceMappings">
<ng-container matColumnDef="paymentTypeId">
<th mat-header-cell *matHeaderCellDef> Payment Type </th>
<td mat-cell *matCellDef="let paymentFundSource">
{{ paymentFundSource.paymentTypeId | find:fixedDepositProductsTemplate.paymentTypeOptions:'id':'name' }}
</td>
</ng-container>
<ng-container matColumnDef="fundSourceAccountId">
<th mat-header-cell *matHeaderCellDef> Fund Source </th>
<td mat-cell *matCellDef="let paymentFundSource">
{{ paymentFundSource.fundSourceAccountId | find:fixedDepositProductsTemplate.accountingMappingOptions.assetAccountOptions:'id':'name' }}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="paymentFundSourceDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: paymentFundSourceDisplayedColumns;"></tr>
</table>
</div>
<div *ngIf="fixedDepositProduct.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]="fixedDepositProduct.feeToIncomeAccountMappings">
<ng-container matColumnDef="chargeId">
<th mat-header-cell *matHeaderCellDef> Fees </th>
<td mat-cell *matCellDef="let feesIncome">
{{ feesIncome.chargeId | find:fixedDepositProductsTemplate.chargeOptions:'id':'name' }}
</td>
</ng-container>
<ng-container matColumnDef="incomeAccountId">
<th mat-header-cell *matHeaderCellDef> Income Account </th>
<td mat-cell *matCellDef="let feesIncome">
{{ feesIncome.incomeAccountId | find:fixedDepositProductsTemplate.accountingMappingOptions.incomeAccountOptions:'id':'name' }}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="feesPenaltyIncomeDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: feesPenaltyIncomeDisplayedColumns;"></tr>
</table>
</div>
<div *ngIf="fixedDepositProduct.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]="fixedDepositProduct.penaltyToIncomeAccountMappings">
<ng-container matColumnDef="chargeId">
<th mat-header-cell *matHeaderCellDef> Penalty </th>
<td mat-cell *matCellDef="let penaltyIncome">
{{ penaltyIncome.chargeId | find:fixedDepositProductsTemplate.penaltyOptions:'id':'name' }}
</td>
</ng-container>
<ng-container matColumnDef="incomeAccountId">
<th mat-header-cell *matHeaderCellDef> Income Account </th>
<td mat-cell *matCellDef="let penaltyIncome">
{{ penaltyIncome.incomeAccountId | find:fixedDepositProductsTemplate.accountingMappingOptions.incomeAccountOptions:'id':'name' }}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="feesPenaltyIncomeDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: feesPenaltyIncomeDisplayedColumns;"></tr>
</table>
</div>
</div>
</div>
</div>
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
<button mat-raised-button matStepperPrevious>
<fa-icon icon="arrow-left"></fa-icon>&nbsp;&nbsp;
Previous
</button>
<button mat-raised-button [routerLink]="['../']">
Cancel
</button>
<button mat-raised-button color="primary" (click)="submit.emit()">
Submit
</button>
</div>
@@ -0,0 +1,45 @@
table {
width: 100%;
tr.incentives-row {
height: 0;
> td {
padding: 0;
}
.incentives {
overflow: hidden;
}
.mat-card {
border-radius: 0;
> h4 {
font-weight: 500;
margin: 0.5em 0 0;
}
}
}
}
.mat-elevation-z1 {
margin: 1em 0 1.5em 0;
}
h2, h3, h4 {
margin: 0;
font-weight: 500;
}
span {
margin: 0.5em 0;
}
mat-divider {
margin: 0 0 0.5em 0;
}
.margin-t {
margin-top: 1em;
}
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FixedDepositProductPreviewStepComponent } from './fixed-deposit-product-preview-step.component';
describe('FixedDepositProductPreviewStepComponent', () => {
let component: FixedDepositProductPreviewStepComponent;
let fixture: ComponentFixture<FixedDepositProductPreviewStepComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FixedDepositProductPreviewStepComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FixedDepositProductPreviewStepComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,37 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { trigger, state, transition, animate, style } from '@angular/animations';
@Component({
selector: 'mifosx-fixed-deposit-product-preview-step',
templateUrl: './fixed-deposit-product-preview-step.component.html',
styleUrls: ['./fixed-deposit-product-preview-step.component.scss'],
animations: [
trigger('expandChartSlab', [
state('collapsed', style({height: '0px', minHeight: '0'})),
state('expanded', style({height: '*'})),
transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)'))
])
]
})
export class FixedDepositProductPreviewStepComponent implements OnInit {
@Input() fixedDepositProductsTemplate: any;
@Input() chartSlabsDisplayedColumns: any[];
@Input() accountingRuleData: any;
@Input() fixedDepositProduct: any;
@Output() submit = new EventEmitter();
chartSlabsIncentivesDisplayedColumns: string[] = ['incentives'];
incentivesDisplayedColumns: string[] = ['entityType', 'attributeName', 'conditionType', 'attributeValue', 'incentiveType', 'amount'];
chargesDisplayedColumns: string[] = ['name', 'chargeCalculationType', 'amount', 'chargeTimeType'];
paymentFundSourceDisplayedColumns: string[] = ['paymentTypeId', 'fundSourceAccountId'];
feesPenaltyIncomeDisplayedColumns: string[] = ['chargeId', 'incomeAccountId'];
expandChartSlabIndex: number[] = [];
constructor() { }
ngOnInit() {
}
}
@@ -0,0 +1,128 @@
<form [formGroup]="fixedDepositProductSettingsForm">
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
<h4 fxFlex="98%" class="mat-h4">Lock-in Period</h4>
<mat-form-field fxFlex="48%">
<mat-label>Frequency</mat-label>
<input type="number" matInput formControlName="lockinPeriodFrequency">
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Type</mat-label>
<mat-select formControlName="lockinPeriodFrequencyType">
<mat-option *ngFor="let lockinPeriodFrequencyType of lockinPeriodFrequencyTypeData" [value]="lockinPeriodFrequencyType.id">
{{ lockinPeriodFrequencyType.value }}
</mat-option>
</mat-select>
</mat-form-field>
<h4 fxFlex="98%" class="mat-h4">Minimum Deposit Term</h4>
<mat-form-field fxFlex="48%">
<mat-label>Frequency</mat-label>
<input type="number" matInput formControlName="minDepositTerm" required>
<mat-error>
Minimum Deposit Term Frequency is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Frequency Type</mat-label>
<mat-select formControlName="minDepositTermTypeId" required>
<mat-option *ngFor="let periodFrequencyType of periodFrequencyTypeData" [value]="periodFrequencyType.id">
{{ periodFrequencyType.value }}
</mat-option>
</mat-select>
<mat-error>
Minimum Deposit Term Frequency Type is <strong>required</strong>
</mat-error>
</mat-form-field>
<h4 fxFlex="98%" class="mat-h4">And thereafter, in Multiples of</h4>
<mat-form-field fxFlex="48%">
<mat-label>Frequency</mat-label>
<input type="number" matInput formControlName="inMultiplesOfDepositTerm">
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Type</mat-label>
<mat-select formControlName="inMultiplesOfDepositTermTypeId">
<mat-option *ngFor="let periodFrequencyType of periodFrequencyTypeData" [value]="periodFrequencyType.id">
{{ periodFrequencyType.value }}
</mat-option>
</mat-select>
</mat-form-field>
<h4 fxFlex="98%" class="mat-h4">Maximum Deposit Term</h4>
<mat-form-field fxFlex="48%">
<mat-label>Frequency</mat-label>
<input type="number" matInput formControlName="maxDepositTerm">
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Type</mat-label>
<mat-select formControlName="maxDepositTermTypeId">
<mat-option *ngFor="let periodFrequencyType of periodFrequencyTypeData" [value]="periodFrequencyType.id">
{{ periodFrequencyType.value }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-divider fxFlex="98%"></mat-divider>
<h4 fxFlex="23%" class="mat-h4">For Pre-mature closure</h4>
<mat-checkbox fxFlex="73%" labelPosition="before" formControlName="preClosurePenalApplicable" class="margin-v">
Apply Penal Interest (less)
</mat-checkbox>
<mat-form-field fxFlex="48%">
<mat-label>Penal Interest (%)</mat-label>
<input type="number" matInput formControlName="preClosurePenalInterest">
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Period</mat-label>
<mat-select formControlName="preClosurePenalInterestOnTypeId">
<mat-option *ngFor="let preClosurePenalInterestOnType of preClosurePenalInterestOnTypeData" [value]="preClosurePenalInterestOnType.id">
{{ preClosurePenalInterestOnType.value }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-divider fxFlex="98%"></mat-divider>
<mat-checkbox fxFlex="48%" labelPosition="before" formControlName="withHoldTax" class="margin-v">
Is Withhold Tax Applicable?
</mat-checkbox>
<mat-form-field fxFlex="48%" *ngIf="fixedDepositProductSettingsForm.value.withHoldTax">
<mat-label>Tax Group</mat-label>
<mat-select formControlName="taxGroupId" required>
<mat-option *ngFor="let taxGroup of taxGroupData" [value]="taxGroup.id">
{{ taxGroup.name }}
</mat-option>
</mat-select>
<mat-error>
Tax Group is <strong>required</strong>
</mat-error>
</mat-form-field>
</div>
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
<button mat-raised-button matStepperPrevious>
<fa-icon icon="arrow-left"></fa-icon>&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,20 @@
h4 {
font-weight: 500;
margin: 1em 0;
}
mat-divider {
margin: 1em 0 2em 0;
}
.margin-v {
margin: 1em 0;
}
.margin-b {
margin: 0 0 1em 0;
}
.margin-t {
margin-top: 1em;
}
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FixedDepositProductSettingsStepComponent } from './fixed-deposit-product-settings-step.component';
describe('FixedDepositProductSettingsStepComponent', () => {
let component: FixedDepositProductSettingsStepComponent;
let fixture: ComponentFixture<FixedDepositProductSettingsStepComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FixedDepositProductSettingsStepComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FixedDepositProductSettingsStepComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,70 @@
import { Component, OnInit, Input } from '@angular/core';
import { FormGroup, FormBuilder, FormControl, Validators } from '@angular/forms';
@Component({
selector: 'mifosx-fixed-deposit-product-settings-step',
templateUrl: './fixed-deposit-product-settings-step.component.html',
styleUrls: ['./fixed-deposit-product-settings-step.component.scss']
})
export class FixedDepositProductSettingsStepComponent implements OnInit {
@Input() fixedDepositProductsTemplate: any;
fixedDepositProductSettingsForm: FormGroup;
lockinPeriodFrequencyTypeData: any;
periodFrequencyTypeData: any;
preClosurePenalInterestOnTypeData: any;
taxGroupData: any;
constructor(private formBuilder: FormBuilder) {
this.createFixedDepositProductSettingsForm();
this.setConditionalControls();
}
ngOnInit() {
this.lockinPeriodFrequencyTypeData = this.fixedDepositProductsTemplate.lockinPeriodFrequencyTypeOptions;
this.periodFrequencyTypeData = this.fixedDepositProductsTemplate.periodFrequencyTypeOptions.slice(0, -1);
this.preClosurePenalInterestOnTypeData = this.fixedDepositProductsTemplate.preClosurePenalInterestOnTypeOptions;
this.taxGroupData = this.fixedDepositProductsTemplate.taxGroupOptions;
}
createFixedDepositProductSettingsForm() {
this.fixedDepositProductSettingsForm = this.formBuilder.group({
'lockinPeriodFrequency': [''],
'lockinPeriodFrequencyType': [''],
'minDepositTerm': ['', Validators.required],
'minDepositTermTypeId': ['', Validators.required],
'inMultiplesOfDepositTerm': [''],
'inMultiplesOfDepositTermTypeId': [''],
'maxDepositTerm': [''],
'maxDepositTermTypeId': [''],
'preClosurePenalApplicable': [false],
'preClosurePenalInterest': [''],
'preClosurePenalInterestOnTypeId': [''],
'withHoldTax': [false]
});
}
setConditionalControls() {
this.fixedDepositProductSettingsForm.get('withHoldTax').valueChanges
.subscribe((withHoldTax: any) => {
if (withHoldTax) {
this.fixedDepositProductSettingsForm.addControl('taxGroupId', new FormControl('', Validators.required));
} else {
this.fixedDepositProductSettingsForm.removeControl('taxGroupId');
}
});
}
get fixedDepositProductSettings() {
const fixedDepositProductSettings = this.fixedDepositProductSettingsForm.value;
for (const key in fixedDepositProductSettings) {
if (fixedDepositProductSettings[key] === '') {
delete fixedDepositProductSettings[key];
}
}
return fixedDepositProductSettings;
}
}
@@ -0,0 +1,88 @@
<form [formGroup]="fixedDepositProductTermsForm">
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
<h4 fxFlex="98%" class="mat-h4">Deposit Amount</h4>
<mat-form-field fxFlex="31%">
<mat-label>Minimum</mat-label>
<input type="number" matInput formControlName="minDepositAmount">
</mat-form-field>
<mat-form-field fxFlex="31%">
<mat-label>Default</mat-label>
<input type="number" matInput formControlName="depositAmount" required>
<mat-error>
Default Deposit Amount is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field fxFlex="31%">
<mat-label>Maximum</mat-label>
<input type="number" matInput formControlName="maxDepositAmount">
</mat-form-field>
<mat-divider fxFlex="98%"></mat-divider>
<mat-form-field fxFlex="48%">
<mat-label>Interest Compounding Period</mat-label>
<mat-select formControlName="interestCompoundingPeriodType" required>
<mat-option *ngFor="let interestCompoundingPeriodType of interestCompoundingPeriodTypeData" [value]="interestCompoundingPeriodType.id">
{{ interestCompoundingPeriodType.value }}
</mat-option>
</mat-select>
<mat-error>
Interest Compounding Period is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Interest Posting Period</mat-label>
<mat-select formControlName="interestPostingPeriodType" required>
<mat-option *ngFor="let interestPostingPeriodType of interestPostingPeriodTypeData" [value]="interestPostingPeriodType.id">
{{ interestPostingPeriodType.value }}
</mat-option>
</mat-select>
<mat-error>
Interest Posting Period is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Interest Calculated using</mat-label>
<mat-select formControlName="interestCalculationType" required>
<mat-option *ngFor="let interestCalculationType of interestCalculationTypeData" [value]="interestCalculationType.id">
{{ interestCalculationType.value }}
</mat-option>
</mat-select>
<mat-error>
Interest Calculated using is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Days in Year</mat-label>
<mat-select formControlName="interestCalculationDaysInYearType" required>
<mat-option *ngFor="let interestCalculationDaysInYearType of interestCalculationDaysInYearTypeData" [value]="interestCalculationDaysInYearType.id">
{{ interestCalculationDaysInYearType.value }}
</mat-option>
</mat-select>
<mat-error>
Days in Year is <strong>required</strong>
</mat-error>
</mat-form-field>
</div>
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
<button mat-raised-button matStepperPrevious>
<fa-icon icon="arrow-left"></fa-icon>&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,12 @@
h4 {
font-weight: 500;
margin: 1em 0;
}
mat-divider {
margin: 1em 0 2em 0;
}
.margin-t {
margin-top: 1em;
}
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FixedDepositProductTermsStepComponent } from './fixed-deposit-product-terms-step.component';
describe('FixedDepositProductTermsStepComponent', () => {
let component: FixedDepositProductTermsStepComponent;
let fixture: ComponentFixture<FixedDepositProductTermsStepComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FixedDepositProductTermsStepComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FixedDepositProductTermsStepComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,60 @@
import { Component, OnInit, Input } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
@Component({
selector: 'mifosx-fixed-deposit-product-terms-step',
templateUrl: './fixed-deposit-product-terms-step.component.html',
styleUrls: ['./fixed-deposit-product-terms-step.component.scss']
})
export class FixedDepositProductTermsStepComponent implements OnInit {
@Input() fixedDepositProductsTemplate: any;
fixedDepositProductTermsForm: FormGroup;
interestCompoundingPeriodTypeData: any;
interestPostingPeriodTypeData: any;
interestCalculationTypeData: any;
interestCalculationDaysInYearTypeData: any;
constructor(private formBuilder: FormBuilder) {
this.createFixedDepositProductTermsForm();
}
ngOnInit() {
this.interestCompoundingPeriodTypeData = this.fixedDepositProductsTemplate.interestCompoundingPeriodTypeOptions;
this.interestPostingPeriodTypeData = this.fixedDepositProductsTemplate.interestPostingPeriodTypeOptions;
this.interestCalculationTypeData = this.fixedDepositProductsTemplate.interestCalculationTypeOptions;
this.interestCalculationDaysInYearTypeData = this.fixedDepositProductsTemplate.interestCalculationDaysInYearTypeOptions;
this.fixedDepositProductTermsForm.patchValue({
'interestCompoundingPeriodType': this.fixedDepositProductsTemplate.interestCompoundingPeriodType.id,
'interestPostingPeriodType': this.fixedDepositProductsTemplate.interestPostingPeriodType.id,
'interestCalculationType': this.fixedDepositProductsTemplate.interestCalculationType.id,
'interestCalculationDaysInYearType': this.fixedDepositProductsTemplate.interestCalculationDaysInYearType.id
});
}
createFixedDepositProductTermsForm() {
this.fixedDepositProductTermsForm = this.formBuilder.group({
'minDepositAmount': [''],
'depositAmount': ['', Validators.required],
'maxDepositAmount': [''],
'interestCompoundingPeriodType': ['', Validators.required],
'interestPostingPeriodType': ['', Validators.required],
'interestCalculationType': ['', Validators.required],
'interestCalculationDaysInYearType': ['', Validators.required]
});
}
get fixedDepositProductTerms() {
const fixedDepositProductTerms = this.fixedDepositProductTermsForm.value;
for (const key in fixedDepositProductTerms) {
if (fixedDepositProductTerms[key] === '') {
delete fixedDepositProductTerms[key];
}
}
return fixedDepositProductTerms;
}
}
@@ -0,0 +1,24 @@
/** Angular Imports */
import { Injectable } from '@angular/core';
import { Resolve } from '@angular/router';
/** rxjs Imports */
import { Observable } from 'rxjs';
/** Custom Services */
import { ProductsService } from '../products.service';
@Injectable()
export class FixedDepositProductsTemplateResolver implements Resolve<Object> {
constructor(private productsService: ProductsService) {}
/**
* Returns the fixed deposit products template data.
* @returns {Observable<any>}
*/
resolve(): Observable<any> {
return this.productsService.getFixedDepositProductsTemplate();
}
}
@@ -1,5 +1,5 @@
<div class="container m-b-20" fxLayout="row" fxLayoutAlign="end" fxLayoutGap="20px">
<button mat-raised-button color="primary">
<button mat-raised-button color="primary" [routerLink]="['create']">
<fa-icon icon="plus"></fa-icon>&nbsp;&nbsp;
Create Fixed Deposit Product
</button>
+20 -4
View File
@@ -26,6 +26,7 @@ import { ManageTaxConfigurationsComponent } from './manage-tax-configurations/ma
import { RecurringDepositProductsComponent } from './recurring-deposit-products/recurring-deposit-products.component';
import { ChargesComponent } from './charges/charges.component';
import { FixedDepositProductsComponent } from './fixed-deposit-products/fixed-deposit-products.component';
import { CreateFixedDepositProductComponent } from './fixed-deposit-products/create-fixed-deposit-product/create-fixed-deposit-product.component';
import { ProductsMixComponent } from './products-mix/products-mix.component';
import { ManageTaxComponentsComponent } from './manage-tax-components/manage-tax-components.component';
@@ -45,6 +46,7 @@ import { ShareProductAndTemplateResolver } from './share-products/edit-share-pro
import { RecurringDepositProductsResolver } from './recurring-deposit-products/recurring-deposit-products.resolver';
import { ChargesResolver } from './charges/charges.resolver';
import { FixedDepositProductsResolver } from './fixed-deposit-products/fixed-deposit-products.resolver';
import { FixedDepositProductsTemplateResolver } from './fixed-deposit-products/fixed-deposit-products-template.resolver';
import { ProductsMixResolver } from './products-mix/products-mix.resolver';
import { ManageTaxComponentsResolver } from './manage-tax-components/manage-tax-components.resolver';
@@ -221,11 +223,24 @@ const routes: Routes = [
},
{
path: 'fixed-deposit-products',
component: FixedDepositProductsComponent,
resolve: {
fixedDepositProducts: FixedDepositProductsResolver
},
data: { title: extract('Fixed Deposit Products'), breadcrumb: 'Fixed Deposit Products' },
children: [
{
path: '',
component: FixedDepositProductsComponent,
resolve: {
fixedDepositProducts: FixedDepositProductsResolver
},
},
{
path: 'create',
component: CreateFixedDepositProductComponent,
data: { title: extract('Create Fixed Deposit Product'), breadcrumb: 'Create' },
resolve: {
fixedDepositProductsTemplate: FixedDepositProductsTemplateResolver
}
}
]
},
{
path: 'products-mix',
@@ -264,6 +279,7 @@ const routes: Routes = [
RecurringDepositProductsResolver,
ChargesResolver,
FixedDepositProductsResolver,
FixedDepositProductsTemplateResolver,
ProductsMixResolver,
ManageTaxComponentsResolver
]
+22 -1
View File
@@ -49,6 +49,16 @@ import { ShareProductPreviewStepComponent } from './share-products/share-product
import { CreateShareProductComponent } from './share-products/create-share-product/create-share-product.component';
import { ViewShareProductComponent } from './share-products/view-share-product/view-share-product.component';
import { EditShareProductComponent } from './share-products/edit-share-product/edit-share-product.component';
import { CreateFixedDepositProductComponent } from './fixed-deposit-products/create-fixed-deposit-product/create-fixed-deposit-product.component';
import { FixedDepositProductDetailsStepComponent } from './fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-details-step/fixed-deposit-product-details-step.component';
import { FixedDepositProductCurrencyStepComponent } from './fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-currency-step/fixed-deposit-product-currency-step.component';
import { FixedDepositProductTermsStepComponent } from './fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-terms-step/fixed-deposit-product-terms-step.component';
import { FixedDepositProductSettingsStepComponent } from './fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-settings-step/fixed-deposit-product-settings-step.component';
import { FixedDepositProductInterestRateChartStepComponent } from './fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-interest-rate-chart-step/fixed-deposit-product-interest-rate-chart-step.component';
import { FixedDepositProductChargesStepComponent } from './fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-charges-step/fixed-deposit-product-charges-step.component';
import { FixedDepositProductAccountingStepComponent } from './fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-accounting-step/fixed-deposit-product-accounting-step.component';
import { FixedDepositProductPreviewStepComponent } from './fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-preview-step/fixed-deposit-product-preview-step.component';
import { DepositProductIncentiveFormDialogComponent } from './deposit-product-incentive-form-dialog/deposit-product-incentive-form-dialog.component';
/**
* Products Module
@@ -102,9 +112,20 @@ import { EditShareProductComponent } from './share-products/edit-share-product/e
ShareProductPreviewStepComponent,
CreateShareProductComponent,
ViewShareProductComponent,
EditShareProductComponent
EditShareProductComponent,
CreateFixedDepositProductComponent,
FixedDepositProductDetailsStepComponent,
FixedDepositProductCurrencyStepComponent,
FixedDepositProductTermsStepComponent,
FixedDepositProductSettingsStepComponent,
FixedDepositProductInterestRateChartStepComponent,
FixedDepositProductChargesStepComponent,
FixedDepositProductAccountingStepComponent,
FixedDepositProductPreviewStepComponent,
DepositProductIncentiveFormDialogComponent
],
entryComponents: [
DepositProductIncentiveFormDialogComponent
],
providers: [DatePipe]
})
+8
View File
@@ -111,6 +111,14 @@ export class ProductsService {
return this.http.get('/fixeddepositproducts');
}
createFixedDepositProduct(fixedDepositProduct: string): Observable<any> {
return this.http.post('/fixeddepositproducts', fixedDepositProduct);
}
getFixedDepositProductsTemplate(): Observable<any> {
return this.http.get('/fixeddepositproducts/template');
}
/**
* @returns {Observable<any>} Tax Components data
*/
@@ -18,7 +18,7 @@ export class FormfieldBase {
this.controlType = options.controlType || '';
this.controlName = options.controlName || '';
this.label = options.label || '';
this.value = options.value || '';
this.value = options.value === undefined ? '' : options.value;
this.required = !!options.required;
this.order = options.order === undefined ? 1 : options.order;
}