feat: add create recurring deposit product component (#934)

Fixes: #193
This commit is contained in:
Muskan Khedia
2020-07-30 13:06:46 +05:30
committed by GitHub
parent 3b686863f8
commit a6f937d37c
41 changed files with 2755 additions and 7 deletions
+10 -1
View File
@@ -42,6 +42,7 @@ import { CreateTaxComponentComponent } from './manage-tax-components/create-tax-
import { EditTaxComponentComponent } from './manage-tax-components/edit-tax-component/edit-tax-component.component';
import { ViewTaxGroupComponent } from './manage-tax-groups/view-tax-group/view-tax-group.component';
import { ShareProductsDividendsComponent } from './share-products/dividends-share-product/dividends.components';
import { CreateRecurringDepositProductComponent } from './recurring-deposit-products/create-recurring-deposit-product/create-recurring-deposit-product.component';
/** Custom Resolvers */
import { LoanProductsResolver } from './loan-products/loan-products.resolver';
@@ -301,7 +302,7 @@ const routes: Routes = [
},
{
path: 'recurring-deposit-products',
data: { title: extract('Recurring Deposit Products'), breadcrumb: 'Recurring Deposit Products' },
data: { title: extract('Recurring Deposit Products'), breadcrumb: 'Recurring Deposit Products' },
children: [
{
path: '',
@@ -324,6 +325,14 @@ const routes: Routes = [
}
]
},
{
path: 'create',
component: CreateRecurringDepositProductComponent,
data: { title: extract('Create Recurring Deposit Product'), breadcrumb: 'Create' },
resolve: {
recurringDepositProductsTemplate: RecurringDepositProductsTemplateResolver
}
}
]
},
{
+24 -4
View File
@@ -1,11 +1,14 @@
/** Angular Imports */
import { NgModule } from '@angular/core';
import { DatePipe, CommonModule } from '@angular/common';
import { MatFormFieldModule, MatSelectModule, MatOptionModule } from '@angular/material';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
/** Custom Modules */
import { SharedModule } from '../shared/shared.module';
import { ProductsRoutingModule } from './products-routing.module';
import { PipesModule } from 'app/pipes/pipes.module';
import { DirectivesModule } from '../directives/directives.module';
/** Custom Components */
import { ProductsComponent } from './products.component';
@@ -71,11 +74,18 @@ import { FloatingRatePeriodDialogComponent } from './floating-rates/floating-rat
import { CreateTaxComponentComponent } from './manage-tax-components/create-tax-component/create-tax-component.component';
import { EditTaxComponentComponent } from './manage-tax-components/edit-tax-component/edit-tax-component.component';
import { EditChargeComponent } from './charges/edit-charge/edit-charge.component';
import { MatFormFieldModule, MatSelectModule, MatOptionModule } from '@angular/material';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ViewTaxGroupComponent } from './manage-tax-groups/view-tax-group/view-tax-group.component';
import { ShareProductsDividendsComponent } from './share-products/dividends-share-product/dividends.components';
import { ViewRecurringDepositProductComponent } from './recurring-deposit-products/view-recurring-deposit-product/view-recurring-deposit-product.component';
import { CreateRecurringDepositProductComponent } from './recurring-deposit-products/create-recurring-deposit-product/create-recurring-deposit-product.component';
import { RecurringDepositProductAccountingStepComponent } from './recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-accounting-step/recurring-deposit-product-accounting-step.component';
import { RecurringDepositProductChargesStepComponent } from './recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-charges-step/recurring-deposit-product-charges-step.component';
import { RecurringDepositProductCurrencyStepComponent } from './recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-currency-step/recurring-deposit-product-currency-step.component';
import { RecurringDepositProductDetailsStepComponent } from './recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-details-step/recurring-deposit-product-details-step.component';
import { RecurringDepositProductInterestRateChartStepComponent } from './recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-interest-rate-chart-step/recurring-deposit-product-interest-rate-chart-step.component';
import { RecurringDepositProductPreviewStepComponent } from './recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-preview-step/recurring-deposit-product-preview-step.component';
import { RecurringDepositProductSettingsStepComponent } from './recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-settings-step/recurring-deposit-product-settings-step.component';
import { RecurringDepositProductTermsStepComponent } from './recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-terms-step/recurring-deposit-product-terms-step.component';
/**
* Products Module
@@ -86,7 +96,8 @@ import { ViewRecurringDepositProductComponent } from './recurring-deposit-produc
imports: [
SharedModule,
ProductsRoutingModule,
PipesModule
PipesModule,
DirectivesModule
],
declarations: [
ProductsComponent,
@@ -155,7 +166,16 @@ import { ViewRecurringDepositProductComponent } from './recurring-deposit-produc
ViewTaxGroupComponent,
ShareProductsDividendsComponent,
DepositProductIncentiveFormDialogComponent,
ViewRecurringDepositProductComponent
ViewRecurringDepositProductComponent,
CreateRecurringDepositProductComponent,
RecurringDepositProductAccountingStepComponent,
RecurringDepositProductChargesStepComponent,
RecurringDepositProductCurrencyStepComponent,
RecurringDepositProductDetailsStepComponent,
RecurringDepositProductInterestRateChartStepComponent,
RecurringDepositProductPreviewStepComponent,
RecurringDepositProductSettingsStepComponent,
RecurringDepositProductTermsStepComponent
],
entryComponents: [
DepositProductIncentiveFormDialogComponent,
+5
View File
@@ -276,4 +276,9 @@ export class ProductsService {
const httpParams = new HttpParams().set('productId', productId);
return this.http.get(`/interestratecharts`, {params: httpParams});
}
createRecurringDepositProduct(recurringDepositProduct: string): Observable<any> {
return this.http.post('/recurringdepositproducts', recurringDepositProduct);
}
}
@@ -0,0 +1,110 @@
<div class="container">
<mat-horizontal-stepper class="mat-elevation-z8" labelPosition="bottom" #recurringDepositProductStepper>
<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]="recurringDepositProductDetailsForm">
<ng-template matStepLabel>DETAILS</ng-template>
<mifosx-recurring-deposit-product-details-step></mifosx-recurring-deposit-product-details-step>
</mat-step>
<mat-step [stepControl]="recurringDepositProductCurrencyForm">
<ng-template matStepLabel>CURRENCY</ng-template>
<mifosx-recurring-deposit-product-currency-step
[recurringDepositProductsTemplate]="recurringDepositProductsTemplate">
</mifosx-recurring-deposit-product-currency-step>
</mat-step>
<mat-step [stepControl]="recurringDepositProductTermsForm">
<ng-template matStepLabel>TERMS</ng-template>
<mifosx-recurring-deposit-product-terms-step
[recurringDepositProductsTemplate]="recurringDepositProductsTemplate">
</mifosx-recurring-deposit-product-terms-step>
</mat-step>
<mat-step [stepControl]="recurringDepositProductSettingsForm">
<ng-template matStepLabel>SETTINGS</ng-template>
<mifosx-recurring-deposit-product-settings-step
[recurringDepositProductsTemplate]="recurringDepositProductsTemplate">
</mifosx-recurring-deposit-product-settings-step>
</mat-step>
<mat-step [stepControl]="recurringDepositProductInterestRateChartForm">
<ng-template matStepLabel>INTEREST RATE CHART</ng-template>
<mifosx-recurring-deposit-product-interest-rate-chart-step
[recurringDepositProductsTemplate]="recurringDepositProductsTemplate">
</mifosx-recurring-deposit-product-interest-rate-chart-step>
</mat-step>
<mat-step>
<ng-template matStepLabel>CHARGES</ng-template>
<mifosx-recurring-deposit-product-charges-step
[recurringDepositProductsTemplate]="recurringDepositProductsTemplate"
[currencyCode]="recurringDepositProductCurrencyForm.get('currencyCode')">
</mifosx-recurring-deposit-product-charges-step>
</mat-step>
<mat-step [stepControl]="recurringDepositProductAccountingForm">
<ng-template matStepLabel>ACCOUNTING</ng-template>
<mifosx-recurring-deposit-product-accounting-step
[recurringDepositProductsTemplate]="recurringDepositProductsTemplate" [accountingRuleData]="accountingRuleData"
[recurringDepositProductFormValid]="recurringDepositProductFormValid">
</mifosx-recurring-deposit-product-accounting-step>
</mat-step>
<mat-step state="preview" *ngIf="recurringDepositProductFormValid" completed>
<ng-template matStepLabel>PREVIEW</ng-template>
<mifosx-recurring-deposit-product-preview-step
[recurringDepositProductsTemplate]="recurringDepositProductsTemplate"
[chartSlabsDisplayedColumns]="recurringDepositProductInterestRateChartStep.chartSlabsDisplayedColumns"
[accountingRuleData]="accountingRuleData" [recurringDepositProduct]="recurringDepositProduct"
(submit)="submit()">
</mifosx-recurring-deposit-product-preview-step>
</mat-step>
</mat-horizontal-stepper>
</div>
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CreateRecurringDepositProductComponent } from './create-recurring-deposit-product.component';
describe('CreateRecurringDepositProductComponent', () => {
let component: CreateRecurringDepositProductComponent;
let fixture: ComponentFixture<CreateRecurringDepositProductComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CreateRecurringDepositProductComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CreateRecurringDepositProductComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,107 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { RecurringDepositProductDetailsStepComponent } from '../recurring-deposit-product-stepper/recurring-deposit-product-details-step/recurring-deposit-product-details-step.component';
import { RecurringDepositProductCurrencyStepComponent } from '../recurring-deposit-product-stepper/recurring-deposit-product-currency-step/recurring-deposit-product-currency-step.component';
import { RecurringDepositProductTermsStepComponent } from '../recurring-deposit-product-stepper/recurring-deposit-product-terms-step/recurring-deposit-product-terms-step.component';
import { RecurringDepositProductSettingsStepComponent } from '../recurring-deposit-product-stepper/recurring-deposit-product-settings-step/recurring-deposit-product-settings-step.component';
import { RecurringDepositProductInterestRateChartStepComponent } from '../recurring-deposit-product-stepper/recurring-deposit-product-interest-rate-chart-step/recurring-deposit-product-interest-rate-chart-step.component';
import { RecurringDepositProductChargesStepComponent } from '../recurring-deposit-product-stepper/recurring-deposit-product-charges-step/recurring-deposit-product-charges-step.component';
import { RecurringDepositProductAccountingStepComponent } from '../recurring-deposit-product-stepper/recurring-deposit-product-accounting-step/recurring-deposit-product-accounting-step.component';
import { ProductsService } from 'app/products/products.service';
@Component({
selector: 'mifosx-create-recurring-deposit-product',
templateUrl: './create-recurring-deposit-product.component.html',
styleUrls: ['./create-recurring-deposit-product.component.scss']
})
export class CreateRecurringDepositProductComponent implements OnInit {
@ViewChild(RecurringDepositProductDetailsStepComponent) recurringDepositProductDetailsStep: RecurringDepositProductDetailsStepComponent;
@ViewChild(RecurringDepositProductCurrencyStepComponent) recurringDepositProductCurrencyStep: RecurringDepositProductCurrencyStepComponent;
@ViewChild(RecurringDepositProductTermsStepComponent) recurringDepositProductTermsStep: RecurringDepositProductTermsStepComponent;
@ViewChild(RecurringDepositProductSettingsStepComponent) recurringDepositProductSettingsStep: RecurringDepositProductSettingsStepComponent;
@ViewChild(RecurringDepositProductInterestRateChartStepComponent) recurringDepositProductInterestRateChartStep: RecurringDepositProductInterestRateChartStepComponent;
@ViewChild(RecurringDepositProductChargesStepComponent) recurringDepositProductChargesStep: RecurringDepositProductChargesStepComponent;
@ViewChild(RecurringDepositProductAccountingStepComponent) recurringDepositProductAccountingStep: RecurringDepositProductAccountingStepComponent;
recurringDepositProductsTemplate: any;
accountingRuleData = ['None', 'Cash'];
constructor(private route: ActivatedRoute,
private productsService: ProductsService,
private router: Router) {
this.route.data.subscribe((data: { recurringDepositProductsTemplate: any }) => {
this.recurringDepositProductsTemplate = data.recurringDepositProductsTemplate;
});
}
ngOnInit() {
}
get recurringDepositProductDetailsForm() {
return this.recurringDepositProductDetailsStep.recurringDepositProductDetailsForm;
}
get recurringDepositProductCurrencyForm() {
return this.recurringDepositProductCurrencyStep.recurringDepositProductCurrencyForm;
}
get recurringDepositProductTermsForm() {
return this.recurringDepositProductTermsStep.recurringDepositProductTermsForm;
}
get recurringDepositProductSettingsForm() {
return this.recurringDepositProductSettingsStep.recurringDepositProductSettingsForm;
}
get recurringDepositProductInterestRateChartForm() {
return this.recurringDepositProductInterestRateChartStep.recurringDepositProductInterestRateChartForm;
}
get recurringDepositProductAccountingForm() {
return this.recurringDepositProductAccountingStep.recurringDepositProductAccountingForm;
}
get recurringDepositProductFormValid() {
return (
this.recurringDepositProductDetailsForm.valid &&
this.recurringDepositProductCurrencyForm.valid &&
this.recurringDepositProductTermsForm.valid &&
this.recurringDepositProductSettingsForm.valid &&
this.recurringDepositProductInterestRateChartForm.valid &&
this.recurringDepositProductAccountingForm.valid
);
}
get recurringDepositProduct() {
return {
...this.recurringDepositProductDetailsStep.recurringDepositProductDetails,
...this.recurringDepositProductCurrencyStep.recurringDepositProductCurrency,
...this.recurringDepositProductTermsStep.recurringDepositProductTerms,
...this.recurringDepositProductSettingsStep.recurringDepositProductSettings,
...this.recurringDepositProductInterestRateChartStep.recurringDepositProductInterestRateChart,
...this.recurringDepositProductChargesStep.recurringDepositProductCharges,
...this.recurringDepositProductAccountingStep.recurringDepositProductAccounting
};
}
submit() {
// TODO: Update once language and date settings are setup
let recurringDepositProduct = {
...this.recurringDepositProduct,
charges: this.recurringDepositProduct.charges.map((charge: any) => ({ id: charge.id })),
locale: 'en' // locale required for depositAmount
};
if (!recurringDepositProduct.description) {
recurringDepositProduct.description = '';
}
delete recurringDepositProduct.advancedAccountingRules;
this.productsService.createRecurringDepositProduct(recurringDepositProduct)
.subscribe((response: any) => {
this.router.navigate(['../', response.resourceId], { relativeTo: this.route });
});
}
}
@@ -0,0 +1,264 @@
<form [formGroup]="recurringDepositProductAccountingForm">
<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="recurringDepositProductAccountingForm.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="recurringDepositProductAccountingForm.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]="!recurringDepositProductFormValid">
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 { RecurringDepositProductAccountingStepComponent } from './recurring-deposit-product-accounting-step.component';
describe('RecurringDepositProductAccountingStepComponent', () => {
let component: RecurringDepositProductAccountingStepComponent;
let fixture: ComponentFixture<RecurringDepositProductAccountingStepComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ RecurringDepositProductAccountingStepComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RecurringDepositProductAccountingStepComponent);
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-recurring-deposit-product-accounting-step',
templateUrl: './recurring-deposit-product-accounting-step.component.html',
styleUrls: ['./recurring-deposit-product-accounting-step.component.scss']
})
export class RecurringDepositProductAccountingStepComponent implements OnInit {
@Input() recurringDepositProductsTemplate: any;
@Input() accountingRuleData: any;
@Input() recurringDepositProductFormValid: boolean;
recurringDepositProductAccountingForm: 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.createrecurringDepositProductAccountingForm();
this.setConditionalControls();
}
ngOnInit() {
this.chargeData = this.recurringDepositProductsTemplate.chargeOptions || [];
this.penaltyData = this.recurringDepositProductsTemplate.penaltyOptions || [];
this.paymentTypeData = this.recurringDepositProductsTemplate.paymentTypeOptions || [];
this.assetAccountData = this.recurringDepositProductsTemplate.accountingMappingOptions.assetAccountOptions || [];
this.incomeAccountData = this.recurringDepositProductsTemplate.accountingMappingOptions.incomeAccountOptions || [];
this.expenseAccountData = this.recurringDepositProductsTemplate.accountingMappingOptions.expenseAccountOptions || [];
this.liabilityAccountData = this.recurringDepositProductsTemplate.accountingMappingOptions.liabilityAccountOptions || [];
}
createrecurringDepositProductAccountingForm() {
this.recurringDepositProductAccountingForm = this.formBuilder.group({
'accountingRule': [1]
});
}
setConditionalControls() {
this.recurringDepositProductAccountingForm.get('accountingRule').valueChanges
.subscribe((accountingRule: any) => {
if (accountingRule === 2) {
this.recurringDepositProductAccountingForm.addControl('savingsReferenceAccountId', new FormControl('', Validators.required));
this.recurringDepositProductAccountingForm.addControl('savingsControlAccountId', new FormControl('', Validators.required));
this.recurringDepositProductAccountingForm.addControl('transfersInSuspenseAccountId', new FormControl('', Validators.required));
this.recurringDepositProductAccountingForm.addControl('interestOnSavingsAccountId', new FormControl('', Validators.required));
this.recurringDepositProductAccountingForm.addControl('incomeFromFeeAccountId', new FormControl('', Validators.required));
this.recurringDepositProductAccountingForm.addControl('incomeFromPenaltyAccountId', new FormControl('', Validators.required));
this.recurringDepositProductAccountingForm.addControl('advancedAccountingRules', new FormControl(false));
this.recurringDepositProductAccountingForm.get('advancedAccountingRules').valueChanges
.subscribe((advancedAccountingRules: boolean) => {
if (advancedAccountingRules) {
this.recurringDepositProductAccountingForm.addControl('paymentChannelToFundSourceMappings', this.formBuilder.array([]));
this.recurringDepositProductAccountingForm.addControl('feeToIncomeAccountMappings', this.formBuilder.array([]));
this.recurringDepositProductAccountingForm.addControl('penaltyToIncomeAccountMappings', this.formBuilder.array([]));
} else {
this.recurringDepositProductAccountingForm.removeControl('paymentChannelToFundSourceMappings');
this.recurringDepositProductAccountingForm.removeControl('feeToIncomeAccountMappings');
this.recurringDepositProductAccountingForm.removeControl('penaltyToIncomeAccountMappings');
}
});
} else {
this.recurringDepositProductAccountingForm.removeControl('savingsReferenceAccountId');
this.recurringDepositProductAccountingForm.removeControl('overdraftPortfolioControlId');
this.recurringDepositProductAccountingForm.removeControl('savingsControlAccountId');
this.recurringDepositProductAccountingForm.removeControl('transfersInSuspenseAccountId');
this.recurringDepositProductAccountingForm.removeControl('interestOnSavingsAccountId');
this.recurringDepositProductAccountingForm.removeControl('writeOffAccountId');
this.recurringDepositProductAccountingForm.removeControl('incomeFromFeeAccountId');
this.recurringDepositProductAccountingForm.removeControl('incomeFromPenaltyAccountId');
this.recurringDepositProductAccountingForm.removeControl('incomeFromInterestId');
this.recurringDepositProductAccountingForm.removeControl('advancedAccountingRules');
this.recurringDepositProductAccountingForm.removeControl('escheatLiabilityId');
}
});
}
get paymentChannelToFundSourceMappings(): FormArray {
return this.recurringDepositProductAccountingForm.get('paymentChannelToFundSourceMappings') as FormArray;
}
get feeToIncomeAccountMappings(): FormArray {
return this.recurringDepositProductAccountingForm.get('feeToIncomeAccountMappings') as FormArray;
}
get penaltyToIncomeAccountMappings(): FormArray {
return this.recurringDepositProductAccountingForm.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 recurringDepositProductAccounting() {
return this.recurringDepositProductAccountingForm.value;
}
}
@@ -0,0 +1,76 @@
<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 { RecurringDepositProductChargesStepComponent } from './recurring-deposit-product-charges-step.component';
describe('RecurringDepositProductChargesStepComponent', () => {
let component: RecurringDepositProductChargesStepComponent;
let fixture: ComponentFixture<RecurringDepositProductChargesStepComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ RecurringDepositProductChargesStepComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RecurringDepositProductChargesStepComponent);
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-recurring-deposit-product-charges-step',
templateUrl: './recurring-deposit-product-charges-step.component.html',
styleUrls: ['./recurring-deposit-product-charges-step.component.scss']
})
export class RecurringDepositProductChargesStepComponent implements OnInit {
@Input() recurringDepositProductsTemplate: any;
@Input() currencyCode: FormControl;
chargeData: any;
chargesDataSource: {}[];
displayedColumns: string[] = ['name', 'chargeCalculationType', 'amount', 'chargeTimeType', 'action'];
constructor(public dialog: MatDialog) {
}
ngOnInit() {
this.chargeData = this.recurringDepositProductsTemplate.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 recurringDepositProductCharges() {
return {
charges: this.chargesDataSource
};
}
}
@@ -0,0 +1,46 @@
<form [formGroup]="recurringDepositProductCurrencyForm">
<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 { RecurringDepositProductCurrencyStepComponent } from './recurring-deposit-product-currency-step.component';
describe('RecurringDepositProductCurrencyStepComponent', () => {
let component: RecurringDepositProductCurrencyStepComponent;
let fixture: ComponentFixture<RecurringDepositProductCurrencyStepComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ RecurringDepositProductCurrencyStepComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RecurringDepositProductCurrencyStepComponent);
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-recurring-deposit-product-currency-step',
templateUrl: './recurring-deposit-product-currency-step.component.html',
styleUrls: ['./recurring-deposit-product-currency-step.component.scss']
})
export class RecurringDepositProductCurrencyStepComponent implements OnInit {
@Input() recurringDepositProductsTemplate: any;
recurringDepositProductCurrencyForm: FormGroup;
currencyData: any;
constructor(private formBuilder: FormBuilder) {
this.createrecurringDepositProductCurrencyForm();
}
ngOnInit() {
this.currencyData = this.recurringDepositProductsTemplate.currencyOptions;
this.recurringDepositProductCurrencyForm.patchValue({
'currencyCode': this.currencyData[0].code,
'digitsAfterDecimal': 2
});
}
createrecurringDepositProductCurrencyForm() {
this.recurringDepositProductCurrencyForm = this.formBuilder.group({
'currencyCode': ['', Validators.required],
'digitsAfterDecimal': ['', Validators.required],
'inMultiplesOf': ['', Validators.required]
});
}
get recurringDepositProductCurrency() {
return this.recurringDepositProductCurrencyForm.value;
}
}
@@ -0,0 +1,39 @@
<form [formGroup]="recurringDepositProductDetailsForm">
<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 { RecurringDepositProductDetailsStepComponent } from './recurring-deposit-product-details-step.component';
describe('RecurringDepositProductDetailsStepComponent', () => {
let component: RecurringDepositProductDetailsStepComponent;
let fixture: ComponentFixture<RecurringDepositProductDetailsStepComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ RecurringDepositProductDetailsStepComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RecurringDepositProductDetailsStepComponent);
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-recurring-deposit-product-details-step',
templateUrl: './recurring-deposit-product-details-step.component.html',
styleUrls: ['./recurring-deposit-product-details-step.component.scss']
})
export class RecurringDepositProductDetailsStepComponent implements OnInit {
recurringDepositProductDetailsForm: FormGroup;
constructor(private formBuilder: FormBuilder) {
this.createrecurringDepositProductDetailsForm();
}
ngOnInit() {
}
createrecurringDepositProductDetailsForm() {
this.recurringDepositProductDetailsForm = this.formBuilder.group({
'name': ['', Validators.required],
'shortName': ['', Validators.required],
'description': ['']
});
}
get recurringDepositProductDetails() {
return this.recurringDepositProductDetailsForm.value;
}
}
@@ -0,0 +1,225 @@
<form [formGroup]="recurringDepositProductInterestRateChartForm">
<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 { RecurringDepositProductInterestRateChartStepComponent } from './recurring-deposit-product-interest-rate-chart-step.component';
describe('RecurringDepositProductInterestRateChartStepComponent', () => {
let component: RecurringDepositProductInterestRateChartStepComponent;
let fixture: ComponentFixture<RecurringDepositProductInterestRateChartStepComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ RecurringDepositProductInterestRateChartStepComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RecurringDepositProductInterestRateChartStepComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,244 @@
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-recurring-deposit-product-interest-rate-chart-step',
templateUrl: './recurring-deposit-product-interest-rate-chart-step.component.html',
styleUrls: ['./recurring-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 RecurringDepositProductInterestRateChartStepComponent implements OnInit {
@Input() recurringDepositProductsTemplate: any;
recurringDepositProductInterestRateChartForm: 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.createrecurringDepositProductInterestRateChartForm();
}
ngOnInit() {
this.periodTypeData = this.recurringDepositProductsTemplate.chartTemplate.periodTypes;
this.entityTypeData = this.recurringDepositProductsTemplate.chartTemplate.entityTypeOptions;
this.attributeNameData = this.recurringDepositProductsTemplate.chartTemplate.attributeNameOptions;
this.conditionTypeData = this.recurringDepositProductsTemplate.chartTemplate.conditionTypeOptions;
this.genderData = this.recurringDepositProductsTemplate.chartTemplate.genderOptions;
this.clientTypeData = this.recurringDepositProductsTemplate.chartTemplate.clientTypeOptions;
this.clientClassificationData = this.recurringDepositProductsTemplate.chartTemplate.clientClassificationOptions;
this.incentiveTypeData = this.recurringDepositProductsTemplate.chartTemplate.incentiveTypeOptions;
}
createrecurringDepositProductInterestRateChartForm() {
this.recurringDepositProductInterestRateChartForm = this.formBuilder.group({
'charts': this.formBuilder.array([])
});
}
get charts(): FormArray {
return this.recurringDepositProductInterestRateChartForm.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.recurringDepositProductsTemplate.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 recurringDepositProductInterestRateChart() {
// TODO: Update once language and date settings are setup
const dateFormat = 'yyyy-MM-dd';
const locale = 'en';
const recurringDepositProductInterestRateChart = this.recurringDepositProductInterestRateChartForm.value;
for (const chart of recurringDepositProductInterestRateChart.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;
}
if (chart.description === '') {
delete chart.description;
}
}
return recurringDepositProductInterestRateChart;
}
}
@@ -0,0 +1,466 @@
<div fxLayout="row wrap" fxLayout.lt-md="column">
<h2 class="mat-h2" fxFlexFill>{{ recurringDepositProduct.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%">{{ recurringDepositProduct.shortName }}</span>
</div>
<div fxFlexFill *ngIf="recurringDepositProduct.description">
<span fxFlex="40%">Description:</span>
<span fxFlex="60%">{{ recurringDepositProduct.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%">{{ recurringDepositProduct.currencyCode | find:recurringDepositProductsTemplate.currencyOptions:'code':'name' }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Decimal Places:</span>
<span fxFlex="60%">{{ recurringDepositProduct.digitsAfterDecimal }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Currency in multiples of:</span>
<span fxFlex="60%">{{ recurringDepositProduct.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%">{{ recurringDepositProduct.depositAmount + ' (Min: ' + (recurringDepositProduct.minDepositAmount ? recurringDepositProduct.minDepositAmount : recurringDepositProduct.depositAmount) + ', Max: ' + (recurringDepositProduct.maxDepositAmount ? recurringDepositProduct.maxDepositAmount : recurringDepositProduct.depositAmount) + ')' }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Interest Compounding Period:</span>
<span fxFlex="60%">{{ recurringDepositProduct.interestCompoundingPeriodType | find:recurringDepositProductsTemplate.interestCompoundingPeriodTypeOptions:'id':'value' }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Interest Posting Period:</span>
<span fxFlex="60%">{{ recurringDepositProduct.interestPostingPeriodType | find:recurringDepositProductsTemplate.interestPostingPeriodTypeOptions:'id':'value' }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Interest Calculated using:</span>
<span fxFlex="60%">{{ recurringDepositProduct.interestCalculationType | find:recurringDepositProductsTemplate.interestCalculationTypeOptions:'id':'value' }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Days in Year:</span>
<span fxFlex="60%">{{ recurringDepositProduct.interestCalculationDaysInYearType | find:recurringDepositProductsTemplate.interestCalculationDaysInYearTypeOptions:'id':'value' }}</span>
</div>
<h3 class="mat-h3" fxFlexFill>Settings</h3>
<mat-divider fxFlexFill></mat-divider>
<div fxFlexFill *ngIf="recurringDepositProduct.lockinPeriodFrequency">
<span fxFlex="40%">Lock-in Period:</span>
<span fxFlex="60%">{{ recurringDepositProduct.lockinPeriodFrequency + ' ' + (recurringDepositProduct.lockinPeriodFrequencyType | find:recurringDepositProductsTemplate.lockinPeriodFrequencyTypeOptions:'id':'value') }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Minimum Deposit Term:</span>
<span fxFlex="60%">{{ recurringDepositProduct.minDepositTerm + ' ' + (recurringDepositProduct.minDepositTermTypeId | find:recurringDepositProductsTemplate.periodFrequencyTypeOptions:'id':'value') }}</span>
</div>
<div fxFlexFill *ngIf="recurringDepositProduct.inMultiplesOfDepositTerm">
<span fxFlex="40%">And thereafter, in Multiples of:</span>
<span fxFlex="60%">{{ recurringDepositProduct.inMultiplesOfDepositTerm + ' ' + (recurringDepositProduct.inMultiplesOfDepositTermTypeId | find:recurringDepositProductsTemplate.periodFrequencyTypeOptions:'id':'value') }}</span>
</div>
<div fxFlexFill *ngIf="recurringDepositProduct.maxDepositTerm">
<span fxFlex="40%">Minimum Deposit Term:</span>
<span fxFlex="60%">{{ recurringDepositProduct.maxDepositTerm + ' ' + (recurringDepositProduct.maxDepositTermTypeId | find:recurringDepositProductsTemplate.periodFrequencyTypeOptions:'id':'value') }}</span>
</div>
<!-- For Pre-mature closure -->
<div fxFlexFill>
<span fxFlex="40%">Apply Penal Interest (less):</span>
<span fxFlex="60%">{{ recurringDepositProduct.preClosurePenalApplicable ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="recurringDepositProduct.preClosurePenalApplicable">
<span fxFlex="40%">Penal Interest (%):</span>
<span fxFlex="60%">{{ recurringDepositProduct.preClosurePenalInterest + '% (' + (recurringDepositProduct.preClosurePenalInterestOnTypeId | find:recurringDepositProductsTemplate.preClosurePenalInterestOnTypeOptions:'id':'value') + ')' }}</span>
</div>
<div fxFlexFill *ngIf="recurringDepositProduct.isMandatoryDeposit !== undefined">
<span fxFlex="40%">Recurring Deposit Type:</span>
<span fxFlex="60%" *ngIf="recurringDepositProduct.isMandatoryDeposit"> Mandatory </span>
<span fxFlex="60%" *ngIf="!recurringDepositProduct.isMandatoryDeposit"> Voluntary </span>
</div>
<!-- -->
<div fxFlexFill>
<span fxFlex="40%">Withhold Tax is Applicable:</span>
<span fxFlex="60%">{{ recurringDepositProduct.withHoldTax ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="recurringDepositProduct.withHoldTax">
<span fxFlex="40%">Tax Group:</span>
<span fxFlex="60%">{{ recurringDepositProduct.taxGroupId | find:recurringDepositProductsTemplate.taxGroupOptions:'id':'name' }}</span>
</div>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column" *ngIf="recurringDepositProduct.charts.length !== 0">
<h3 class="mat-h3" fxFlexFill>Interest Rate Charts</h3>
<mat-divider fxFlexFill></mat-divider>
<div fxFlexFill *ngFor="let chart of recurringDepositProduct.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%">{{ recurringDepositProduct.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:recurringDepositProductsTemplate.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:recurringDepositProductsTemplate.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:recurringDepositProductsTemplate.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:recurringDepositProductsTemplate.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:recurringDepositProductsTemplate.chartTemplate.genderOptions:'id':'name' }}</span>
<span *ngSwitchCase="3">{{ incentive.attributeValue }}</span>
<span *ngSwitchCase="4">{{ incentive.attributeValue | find:recurringDepositProductsTemplate.chartTemplate.clientTypeOptions:'id':'name' }}</span>
<span *ngSwitchCase="5">{{ incentive.attributeValue | find:recurringDepositProductsTemplate.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:recurringDepositProductsTemplate.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="recurringDepositProduct.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]="recurringDepositProduct.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[recurringDepositProduct.accountingRule-1] }}</span>
</div>
<div fxFlexFill *ngIf="recurringDepositProduct.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%">{{ recurringDepositProduct.savingsReferenceAccountId | find:recurringDepositProductsTemplate.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%">{{ recurringDepositProduct.savingsControlAccountId | find:recurringDepositProductsTemplate.accountingMappingOptions.liabilityAccountOptions:'id':'name' }}</span>
<span fxFlex="40%">Savings transfers in suspense:</span>
<span fxFlex="60%">{{ recurringDepositProduct.transfersInSuspenseAccountId | find:recurringDepositProductsTemplate.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%">{{ recurringDepositProduct.interestOnSavingsAccountId | find:recurringDepositProductsTemplate.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%">{{ recurringDepositProduct.incomeFromFeeAccountId | find:recurringDepositProductsTemplate.accountingMappingOptions.incomeAccountOptions:'id':'name' }}</span>
<span fxFlex="40%">Income from penalties:</span>
<span fxFlex="60%">{{ recurringDepositProduct.incomeFromPenaltyAccountId | find:recurringDepositProductsTemplate.accountingMappingOptions.incomeAccountOptions:'id':'name' }}</span>
</div>
<div *ngIf="recurringDepositProduct.advancedAccountingRules && (recurringDepositProduct.paymentChannelToFundSourceMappings?.length || recurringDepositProduct.feeToIncomeAccountMappings?.length || recurringDepositProduct.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="recurringDepositProduct.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]="recurringDepositProduct.paymentChannelToFundSourceMappings">
<ng-container matColumnDef="paymentTypeId">
<th mat-header-cell *matHeaderCellDef> Payment Type </th>
<td mat-cell *matCellDef="let paymentFundSource">
{{ paymentFundSource.paymentTypeId | find:recurringDepositProductsTemplate.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:recurringDepositProductsTemplate.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="recurringDepositProduct.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]="recurringDepositProduct.feeToIncomeAccountMappings">
<ng-container matColumnDef="chargeId">
<th mat-header-cell *matHeaderCellDef> Fees </th>
<td mat-cell *matCellDef="let feesIncome">
{{ feesIncome.chargeId | find:recurringDepositProductsTemplate.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:recurringDepositProductsTemplate.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="recurringDepositProduct.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]="recurringDepositProduct.penaltyToIncomeAccountMappings">
<ng-container matColumnDef="chargeId">
<th mat-header-cell *matHeaderCellDef> Penalty </th>
<td mat-cell *matCellDef="let penaltyIncome">
{{ penaltyIncome.chargeId | find:recurringDepositProductsTemplate.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:recurringDepositProductsTemplate.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 { RecurringDepositProductPreviewStepComponent } from './recurring-deposit-product-preview-step.component';
describe('RecurringDepositProductPreviewStepComponent', () => {
let component: RecurringDepositProductPreviewStepComponent;
let fixture: ComponentFixture<RecurringDepositProductPreviewStepComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ RecurringDepositProductPreviewStepComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RecurringDepositProductPreviewStepComponent);
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-recurring-deposit-product-preview-step',
templateUrl: './recurring-deposit-product-preview-step.component.html',
styleUrls: ['./recurring-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 RecurringDepositProductPreviewStepComponent implements OnInit {
@Input() recurringDepositProductsTemplate: any;
@Input() chartSlabsDisplayedColumns: any[];
@Input() accountingRuleData: any;
@Input() recurringDepositProduct: 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,142 @@
<form [formGroup]="recurringDepositProductSettingsForm">
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
<mat-checkbox fxFlex="48%" labelPosition="before" formControlName="isMandatoryDeposit" class="margin-v">
Is Mandatory Deposit?
</mat-checkbox>
<mat-checkbox fxFlex="48%" labelPosition="before" formControlName="adjustAdvanceTowardsFuturePayments" class="margin-v">
Adjust advance payments toward future installments?
</mat-checkbox>
<mat-checkbox fxFlex="48%" labelPosition="before" formControlName="allowWithdrawal" class="margin-v">
Allow Withdrawals?
</mat-checkbox>
<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="recurringDepositProductSettingsForm.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 { RecurringDepositProductSettingsStepComponent } from './recurring-deposit-product-settings-step.component';
describe('RecurringDepositProductSettingsStepComponent', () => {
let component: RecurringDepositProductSettingsStepComponent;
let fixture: ComponentFixture<RecurringDepositProductSettingsStepComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ RecurringDepositProductSettingsStepComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RecurringDepositProductSettingsStepComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,73 @@
import { Component, OnInit, Input } from '@angular/core';
import { FormGroup, FormBuilder, FormControl, Validators } from '@angular/forms';
@Component({
selector: 'mifosx-recurring-deposit-product-settings-step',
templateUrl: './recurring-deposit-product-settings-step.component.html',
styleUrls: ['./recurring-deposit-product-settings-step.component.scss']
})
export class RecurringDepositProductSettingsStepComponent implements OnInit {
@Input() recurringDepositProductsTemplate: any;
recurringDepositProductSettingsForm: FormGroup;
lockinPeriodFrequencyTypeData: any;
periodFrequencyTypeData: any;
preClosurePenalInterestOnTypeData: any;
taxGroupData: any;
constructor(private formBuilder: FormBuilder) {
this.createrecurringDepositProductSettingsForm();
this.setConditionalControls();
}
ngOnInit() {
this.lockinPeriodFrequencyTypeData = this.recurringDepositProductsTemplate.lockinPeriodFrequencyTypeOptions;
this.periodFrequencyTypeData = this.recurringDepositProductsTemplate.periodFrequencyTypeOptions.slice(0, -1);
this.preClosurePenalInterestOnTypeData = this.recurringDepositProductsTemplate.preClosurePenalInterestOnTypeOptions;
this.taxGroupData = this.recurringDepositProductsTemplate.taxGroupOptions;
}
createrecurringDepositProductSettingsForm() {
this.recurringDepositProductSettingsForm = this.formBuilder.group({
'isMandatoryDeposit': [false],
'adjustAdvanceTowardsFuturePayments': [false],
'allowWithdrawal': [false],
'lockinPeriodFrequency': [''],
'lockinPeriodFrequencyType': [''],
'minDepositTerm': ['', Validators.required],
'minDepositTermTypeId': ['', Validators.required],
'inMultiplesOfDepositTerm': [''],
'inMultiplesOfDepositTermTypeId': [''],
'maxDepositTerm': [''],
'maxDepositTermTypeId': [''],
'preClosurePenalApplicable': [false],
'preClosurePenalInterest': [''],
'preClosurePenalInterestOnTypeId': [''],
'withHoldTax': [false]
});
}
setConditionalControls() {
this.recurringDepositProductSettingsForm.get('withHoldTax').valueChanges
.subscribe((withHoldTax: any) => {
if (withHoldTax) {
this.recurringDepositProductSettingsForm.addControl('taxGroupId', new FormControl('', Validators.required));
} else {
this.recurringDepositProductSettingsForm.removeControl('taxGroupId');
}
});
}
get recurringDepositProductSettings() {
const recurringDepositProductSettings = this.recurringDepositProductSettingsForm.value;
for (const key in recurringDepositProductSettings) {
if (recurringDepositProductSettings[key] === '') {
delete recurringDepositProductSettings[key];
}
}
return recurringDepositProductSettings;
}
}
@@ -0,0 +1,88 @@
<form [formGroup]="recurringDepositProductTermsForm">
<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 { RecurringDepositProductTermsStepComponent } from './recurring-deposit-product-terms-step.component';
describe('RecurringDepositProductTermsStepComponent', () => {
let component: RecurringDepositProductTermsStepComponent;
let fixture: ComponentFixture<RecurringDepositProductTermsStepComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ RecurringDepositProductTermsStepComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RecurringDepositProductTermsStepComponent);
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-recurring-deposit-product-terms-step',
templateUrl: './recurring-deposit-product-terms-step.component.html',
styleUrls: ['./recurring-deposit-product-terms-step.component.scss']
})
export class RecurringDepositProductTermsStepComponent implements OnInit {
@Input() recurringDepositProductsTemplate: any;
recurringDepositProductTermsForm: FormGroup;
interestCompoundingPeriodTypeData: any;
interestPostingPeriodTypeData: any;
interestCalculationTypeData: any;
interestCalculationDaysInYearTypeData: any;
constructor(private formBuilder: FormBuilder) {
this.createrecurringDepositProductTermsForm();
}
ngOnInit() {
this.interestCompoundingPeriodTypeData = this.recurringDepositProductsTemplate.interestCompoundingPeriodTypeOptions;
this.interestPostingPeriodTypeData = this.recurringDepositProductsTemplate.interestPostingPeriodTypeOptions;
this.interestCalculationTypeData = this.recurringDepositProductsTemplate.interestCalculationTypeOptions;
this.interestCalculationDaysInYearTypeData = this.recurringDepositProductsTemplate.interestCalculationDaysInYearTypeOptions;
this.recurringDepositProductTermsForm.patchValue({
'interestCompoundingPeriodType': this.recurringDepositProductsTemplate.interestCompoundingPeriodType.id,
'interestPostingPeriodType': this.recurringDepositProductsTemplate.interestPostingPeriodType.id,
'interestCalculationType': this.recurringDepositProductsTemplate.interestCalculationType.id,
'interestCalculationDaysInYearType': this.recurringDepositProductsTemplate.interestCalculationDaysInYearType.id
});
}
createrecurringDepositProductTermsForm() {
this.recurringDepositProductTermsForm = this.formBuilder.group({
'minDepositAmount': [''],
'depositAmount': ['', Validators.required],
'maxDepositAmount': [''],
'interestCompoundingPeriodType': ['', Validators.required],
'interestPostingPeriodType': ['', Validators.required],
'interestCalculationType': ['', Validators.required],
'interestCalculationDaysInYearType': ['', Validators.required]
});
}
get recurringDepositProductTerms() {
const recurringDepositProductTerms = this.recurringDepositProductTermsForm.value;
for (const key in recurringDepositProductTerms) {
if (recurringDepositProductTerms[key] === '') {
delete recurringDepositProductTerms[key];
}
}
return recurringDepositProductTerms;
}
}
@@ -11,7 +11,7 @@ import { ProductsService } from '../products.service';
@Injectable()
export class RecurringDepositProductsTemplateResolver implements Resolve<Object> {
constructor(private productsService: ProductsService) {}
constructor(private productsService: ProductsService) { }
/**
* Returns the recurring deposit products template data.
@@ -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']" mifosxHasPermission='"CREATE_RECURRINGDEPOSITPRODUCT"'>
<fa-icon icon="plus"></fa-icon>&nbsp;&nbsp;
Create Recurring Deposit Product
</button>