update RD prodcut code
This commit is contained in:
+1
@@ -70,6 +70,7 @@
|
||||
<mifosx-recurring-deposits-account-interest-rate-chart-step
|
||||
[recurringDepositsAccountTemplate]="recurringDepositsAccountTemplate"
|
||||
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
|
||||
[rdInterestRates]="rdInterestRates"
|
||||
>
|
||||
</mifosx-recurring-deposits-account-interest-rate-chart-step>
|
||||
</mat-step>
|
||||
|
||||
+14
-1
@@ -13,6 +13,7 @@ import { ActivatedRoute, Router } from '@angular/router';
|
||||
/** Custom Services */
|
||||
import { RecurringDepositsService } from '../recurring-deposits.service';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { ProductsService } from 'app/products/products.service';
|
||||
|
||||
/** Step Components */
|
||||
import { RecurringDepositsAccountDetailsStepComponent } from '../recurring-deposits-account-stepper/recurring-deposits-account-details-step/recurring-deposits-account-details-step.component';
|
||||
@@ -54,6 +55,7 @@ export class CreateRecurringDepositsAccountComponent {
|
||||
private dateUtils = inject(Dates);
|
||||
private recurringDepositsService = inject(RecurringDepositsService);
|
||||
private settingsService = inject(SettingsService);
|
||||
private productsService = inject(ProductsService);
|
||||
|
||||
/** Imports all the step component */
|
||||
@ViewChild(RecurringDepositsAccountDetailsStepComponent, { static: true })
|
||||
@@ -69,6 +71,8 @@ export class CreateRecurringDepositsAccountComponent {
|
||||
recurringDepositsAccountTemplate: any;
|
||||
/** Recurring Deposit Account Product Template */
|
||||
recurringDepositsAccountProductTemplate: any;
|
||||
/** Active CBS base interest rate chart */
|
||||
rdInterestRates: any[] = [];
|
||||
|
||||
constructor() {
|
||||
this.route.data.subscribe((data: { recurringDepositsAccountTemplate: any }) => {
|
||||
@@ -78,10 +82,19 @@ export class CreateRecurringDepositsAccountComponent {
|
||||
|
||||
/**
|
||||
* Sets recurring deposits account product template.
|
||||
* @param {any} $event API response
|
||||
* Also fetches the active CBS base interest rate chart.
|
||||
* @param {any} $event API response (includes productId)
|
||||
*/
|
||||
setTemplate($event: any) {
|
||||
this.recurringDepositsAccountProductTemplate = $event;
|
||||
this.productsService.getRdBaseInterestRates().subscribe({
|
||||
next: (rates: any[]) => {
|
||||
this.rdInterestRates = rates || [];
|
||||
},
|
||||
error: () => {
|
||||
this.rdInterestRates = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Get Recurring Deposit Account Details Form Data */
|
||||
|
||||
+1
@@ -71,6 +71,7 @@
|
||||
<mifosx-recurring-deposits-account-interest-rate-chart-step
|
||||
[recurringDepositsAccountTemplate]="recurringDepositsAccountAndTemplate"
|
||||
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
|
||||
[rdInterestRates]="rdInterestRates"
|
||||
>
|
||||
</mifosx-recurring-deposits-account-interest-rate-chart-step>
|
||||
</mat-step>
|
||||
|
||||
+12
@@ -13,6 +13,7 @@ import { ActivatedRoute, Router } from '@angular/router';
|
||||
/** Custom Services */
|
||||
import { RecurringDepositsService } from '../recurring-deposits.service';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { ProductsService } from 'app/products/products.service';
|
||||
|
||||
/** Step Components */
|
||||
import { RecurringDepositsAccountDetailsStepComponent } from '../recurring-deposits-account-stepper/recurring-deposits-account-details-step/recurring-deposits-account-details-step.component';
|
||||
@@ -54,6 +55,7 @@ export class EditRecurringDepositAccountComponent {
|
||||
private dateUtils = inject(Dates);
|
||||
private recurringDepositsService = inject(RecurringDepositsService);
|
||||
private settingsService = inject(SettingsService);
|
||||
private productsService = inject(ProductsService);
|
||||
|
||||
/** Imports all the step component */
|
||||
@ViewChild(RecurringDepositsAccountDetailsStepComponent, { static: true })
|
||||
@@ -69,6 +71,8 @@ export class EditRecurringDepositAccountComponent {
|
||||
recurringDepositsAccountAndTemplate: any;
|
||||
/** Recurring Deposit Account Product Template */
|
||||
recurringDepositsAccountProductTemplate: any;
|
||||
/** Active CBS base interest rate chart */
|
||||
rdInterestRates: any[] = [];
|
||||
|
||||
constructor() {
|
||||
this.route.data.subscribe((data: { recurringDepositsAccountAndTemplate: any }) => {
|
||||
@@ -82,6 +86,14 @@ export class EditRecurringDepositAccountComponent {
|
||||
*/
|
||||
setTemplate($event: any) {
|
||||
this.recurringDepositsAccountProductTemplate = $event;
|
||||
this.productsService.getRdBaseInterestRates().subscribe({
|
||||
next: (rates: any[]) => {
|
||||
this.rdInterestRates = rates || [];
|
||||
},
|
||||
error: () => {
|
||||
this.rdInterestRates = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Get Recurring Deposit Account Details Form Data */
|
||||
|
||||
+51
-1
@@ -7,6 +7,48 @@
|
||||
-->
|
||||
|
||||
<div class="layout-column gap-2px">
|
||||
<!-- ── Custom RD Interest Rate Chart (from m_rd_interest_rate_slab_history) ── -->
|
||||
@if (_activeRdVersion) {
|
||||
<div class="rd-rate-header">
|
||||
<strong>{{ _activeRdVersion.versionLabel }}</strong>
|
||||
<span class="rd-rate-active-badge">Active</span>
|
||||
<span class="rd-rate-from">Effective from: {{ _activeRdVersion.effectiveFromDate }}</span>
|
||||
</div>
|
||||
|
||||
<table class="mat-elevation-z1 rd-rate-table" mat-table [dataSource]="_activeRdVersion.slabs">
|
||||
<ng-container matColumnDef="srNo">
|
||||
<th mat-header-cell *matHeaderCellDef>Sr.</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.srNo }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="description">
|
||||
<th mat-header-cell *matHeaderCellDef>Particular</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.description }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="fromPeriod">
|
||||
<th mat-header-cell *matHeaderCellDef>From</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.fromPeriod }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="toPeriod">
|
||||
<th mat-header-cell *matHeaderCellDef>To</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.toPeriod ?? '—' }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="periodTypeLabel">
|
||||
<th mat-header-cell *matHeaderCellDef>Period Type</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.periodTypeLabel }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="annualInterestRate">
|
||||
<th mat-header-cell *matHeaderCellDef>Annual Rate (%)</th>
|
||||
<td mat-cell *matCellDef="let slab">
|
||||
<strong>{{ slab.annualInterestRate | number: '1.2-2' }}</strong>
|
||||
</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="rdSlabColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: rdSlabColumns"></tr>
|
||||
</table>
|
||||
}
|
||||
|
||||
<!-- ── Legacy chart (shown only when no custom rate chart available) ── -->
|
||||
@if (!_activeRdVersion) {
|
||||
<div class="flex-fill" *ngIf="recurringDepositsAccountProductTemplate?.accountChart.name">
|
||||
<span class="flex-40">{{ 'labels.inputs.name' | translate }}:</span>
|
||||
<span class="flex-60">{{ recurringDepositsAccountProductTemplate.accountChart.name }}</span>
|
||||
@@ -33,8 +75,16 @@
|
||||
recurringDepositsAccountProductTemplate?.accountChart.isPrimaryGroupingByAmount | yesNo
|
||||
}}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<table class="mat-elevation-z1" mat-table [dataSource]="interestRateChartData" #chartsTable multiTemplateDataRows>
|
||||
<table
|
||||
class="mat-elevation-z1"
|
||||
[class.hidden]="_activeRdVersion"
|
||||
mat-table
|
||||
[dataSource]="interestRateChartData"
|
||||
#chartsTable
|
||||
multiTemplateDataRows
|
||||
>
|
||||
<ng-container matColumnDef="period">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Period' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let chartSlab">
|
||||
|
||||
+24
-3
@@ -21,7 +21,7 @@ import {
|
||||
MatRow
|
||||
} from '@angular/material/table';
|
||||
import { trigger, state, style, transition, animate } from '@angular/animations';
|
||||
import { NgIf, NgSwitch, TitleCasePipe } from '@angular/common';
|
||||
import { NgIf, NgSwitch, TitleCasePipe, DecimalPipe } from '@angular/common';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { MatStepperPrevious, MatStepperNext } from '@angular/material/stepper';
|
||||
import { DateFormatPipe } from '../../../../pipes/date-format.pipe';
|
||||
@@ -69,7 +69,28 @@ export class RecurringDepositsAccountInterestRateChartStepComponent implements O
|
||||
@Input() recurringDepositsAccountTemplate: any;
|
||||
@Input() recurringDepositsAccountProductTemplate: any;
|
||||
|
||||
/** Interest Rate Chart Data */
|
||||
/**
|
||||
* CBS base rate chart loaded from the active base-interest-rate service.
|
||||
* When set, this table is shown instead of the legacy accountChart.chartSlabs.
|
||||
*/
|
||||
@Input() set rdInterestRates(rates: any[]) {
|
||||
this._rdInterestRates = rates || [];
|
||||
this._activeRdVersion = this._rdInterestRates.find((v: any) => v.isActive) ?? this._rdInterestRates[0] ?? null;
|
||||
}
|
||||
|
||||
_rdInterestRates: any[] = [];
|
||||
_activeRdVersion: any = null;
|
||||
|
||||
readonly rdSlabColumns = [
|
||||
'srNo',
|
||||
'description',
|
||||
'fromPeriod',
|
||||
'toPeriod',
|
||||
'periodTypeLabel',
|
||||
'annualInterestRate'
|
||||
];
|
||||
|
||||
/** Interest Rate Chart Data (legacy – used when no custom rate chart is available) */
|
||||
interestRateChartData: any = [];
|
||||
/** Columns to be displayed in interest rate chart table. */
|
||||
chartSlabsDisplayedColumns: any[] = [
|
||||
@@ -88,7 +109,7 @@ export class RecurringDepositsAccountInterestRateChartStepComponent implements O
|
||||
'incentiveType',
|
||||
'amount'
|
||||
];
|
||||
/** Additional Column to disblac incentives table */
|
||||
/** Additional Column to display incentives table */
|
||||
chartSlabsIncentivesDisplayedColumns: string[] = ['incentives'];
|
||||
/** Expand Chart Slab Index used in the view */
|
||||
expandChartSlabIndex: number;
|
||||
|
||||
@@ -49,6 +49,7 @@ import { EditTaxComponentComponent } from './manage-tax-components/edit-tax-comp
|
||||
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';
|
||||
import { BaseRecurringDepositInterestRateChartComponent } from './recurring-deposit-products/base-recurring-deposit-interest-rate-chart/base-recurring-deposit-interest-rate-chart.component';
|
||||
import { CreateDividendComponent } from './share-products/create-dividend/create-dividend.component';
|
||||
import { ViewFixedDepositProductComponent } from './fixed-deposit-products/view-fixed-deposit-product/view-fixed-deposit-product.component';
|
||||
import { ViewDividendComponent } from './share-products/view-dividend/view-dividend.component';
|
||||
@@ -129,6 +130,8 @@ import { FixedDepositGeneralTabComponent } from './fixed-deposit-products/view-f
|
||||
import { FixedDepositDatatableTabComponent } from './fixed-deposit-products/view-fixed-deposit-product/fixed-deposit-datatable-tab/fixed-deposit-datatable-tab.component';
|
||||
import { RecurringDepositGeneralTabComponent } from './recurring-deposit-products/view-recurring-deposit-product/recurring-deposit-general-tab/recurring-deposit-general-tab.component';
|
||||
import { RecurringDepositDatatableTabComponent } from './recurring-deposit-products/view-recurring-deposit-product/recurring-deposit-datatable-tab/recurring-deposit-datatable-tab.component';
|
||||
import { RdInterestRatesTabComponent } from './recurring-deposit-products/view-recurring-deposit-product/rd-interest-rates-tab/rd-interest-rates-tab.component';
|
||||
import { rdInterestRatesResolver } from './recurring-deposit-products/view-recurring-deposit-product/rd-interest-rates-tab/rd-interest-rates.resolver';
|
||||
import { ShareProductGeneralTabComponent } from './share-products/view-share-product/share-product-general-tab/share-product-general-tab.component';
|
||||
import { ShareProductDatatableTabComponent } from './share-products/view-share-product/share-product-datatable-tab/share-product-datatable-tab.component';
|
||||
import { ShareProductDatatablesResolver } from './share-products/share-product-datatables.resolver';
|
||||
@@ -652,6 +655,14 @@ const routes: Routes = [
|
||||
path: 'recurring-deposit-products',
|
||||
data: { title: 'Recurring Deposit Products', breadcrumb: 'Recurring Deposit Products' },
|
||||
children: [
|
||||
{
|
||||
path: 'base-interest-rates',
|
||||
component: BaseRecurringDepositInterestRateChartComponent,
|
||||
data: { title: 'CBS Base Interest Rates', breadcrumb: 'Base Interest Rates' },
|
||||
resolve: {
|
||||
recurringDepositProductsTemplate: RecurringDepositProductsTemplateResolver
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
component: CreateRecurringDepositProductComponent,
|
||||
@@ -704,6 +715,14 @@ const routes: Routes = [
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'interest-rates',
|
||||
component: RdInterestRatesTabComponent,
|
||||
data: { title: 'CBS Base Chart', breadcrumb: 'CBS Base Chart', routeParamBreadcrumb: false },
|
||||
resolve: {
|
||||
rdInterestRates: rdInterestRatesResolver
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'edit',
|
||||
data: { title: 'Edit Recurring Deposit Product', breadcrumb: 'edit', routeParamBreadcrumb: false },
|
||||
|
||||
@@ -84,6 +84,7 @@ import { ViewTaxGroupComponent } from './manage-tax-groups/view-tax-group/view-t
|
||||
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 { BaseRecurringDepositInterestRateChartComponent } from './recurring-deposit-products/base-recurring-deposit-interest-rate-chart/base-recurring-deposit-interest-rate-chart.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';
|
||||
@@ -213,6 +214,7 @@ import { AdvancedAccountingMappingRuleComponent } from './loan-products/loan-pro
|
||||
DepositProductIncentiveFormDialogComponent,
|
||||
ViewRecurringDepositProductComponent,
|
||||
CreateRecurringDepositProductComponent,
|
||||
BaseRecurringDepositInterestRateChartComponent,
|
||||
RecurringDepositProductAccountingStepComponent,
|
||||
RecurringDepositProductChargesStepComponent,
|
||||
RecurringDepositProductCurrencyStepComponent,
|
||||
|
||||
@@ -526,6 +526,26 @@ export class ProductsService {
|
||||
return this.http.put(`/recurringdepositproducts/${recurringDepositProductId}`, recurringDepositProduct);
|
||||
}
|
||||
|
||||
/** Returns the active CBS base interest-rate version(s). */
|
||||
getAllRdInterestRates(): Observable<any> {
|
||||
return this.getRdBaseInterestRates();
|
||||
}
|
||||
|
||||
/** Returns the active CBS base interest-rate version(s). */
|
||||
getRdBaseInterestRates(): Observable<any> {
|
||||
return this.http.get('/recurringdepositproducts/baseinterestrates');
|
||||
}
|
||||
|
||||
/** Returns the full CBS base chart history. */
|
||||
getRdBaseInterestRateHistory(): Observable<any> {
|
||||
return this.http.get('/recurringdepositproducts/baseinterestrates/history');
|
||||
}
|
||||
|
||||
/** Saves a new CBS base interest-rate version. */
|
||||
saveRdBaseInterestRates(payload: any): Observable<any> {
|
||||
return this.http.post('/recurringdepositproducts/baseinterestrates', payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Observable<any>} Collateral data.
|
||||
*/
|
||||
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
<!--
|
||||
Copyright since 2025 Mifos Initiative
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<div class="page-layout">
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h2>CBS Base Recurring Deposit Interest Chart</h2>
|
||||
<p class="page-subtitle">This chart is the master template used when creating a recurring deposit product.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (recurringDepositProductsTemplate?.chartTemplate) {
|
||||
<mifosx-recurring-deposit-product-interest-rate-chart-step
|
||||
#stepComponent
|
||||
[recurringDepositProductsTemplate]="recurringDepositProductsTemplate"
|
||||
[rdInterestRates]="rdInterestRates"
|
||||
>
|
||||
</mifosx-recurring-deposit-product-interest-rate-chart-step>
|
||||
|
||||
<div class="actions-row">
|
||||
<button
|
||||
type="button"
|
||||
mat-raised-button
|
||||
color="primary"
|
||||
[disabled]="isSaving || !stepComponent"
|
||||
(click)="save(stepComponent)"
|
||||
>
|
||||
{{ isSaving ? 'Saving...' : 'Save CBS Base Chart' }}
|
||||
</button>
|
||||
@if (saveMessage) {
|
||||
<span class="save-message">{{ saveMessage }}</span>
|
||||
}
|
||||
</div>
|
||||
} @else {
|
||||
<p>Loading CBS base chart...</p>
|
||||
}
|
||||
|
||||
<div class="history-section">
|
||||
<div class="section-header">
|
||||
<div>
|
||||
<h3>Chart History</h3>
|
||||
<p class="section-subtitle">Previous CBS base chart versions are listed here for comparison and audit.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (isHistoryLoading) {
|
||||
<p>Loading history...</p>
|
||||
} @else if (rdInterestRateHistory.length === 0) {
|
||||
<p>No history found for the CBS base chart.</p>
|
||||
} @else {
|
||||
<mat-accordion multi>
|
||||
@for (version of rdInterestRateHistory; track version.id) {
|
||||
<mat-expansion-panel [expanded]="version.isActive">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
<span class="version-header">
|
||||
<strong>{{ version.versionLabel }}</strong>
|
||||
<span [class]="version.isActive ? 'active-badge' : 'inactive-badge'">
|
||||
{{ version.isActive ? 'Active' : 'Inactive' }}
|
||||
</span>
|
||||
</span>
|
||||
</mat-panel-title>
|
||||
<mat-panel-description>
|
||||
<span class="meta-text">From {{ version.effectiveFromDate }}</span>
|
||||
<span class="meta-text">To {{ version.effectiveToDate || 'Current' }}</span>
|
||||
</mat-panel-description>
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
@if (version.remarks) {
|
||||
<p class="remarks-text">{{ version.remarks }}</p>
|
||||
}
|
||||
|
||||
<table class="history-table" mat-table [dataSource]="version.slabs">
|
||||
<ng-container matColumnDef="srNo">
|
||||
<th mat-header-cell *matHeaderCellDef>Sr.</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.srNo }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="description">
|
||||
<th mat-header-cell *matHeaderCellDef>Description</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.description }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="fromPeriod">
|
||||
<th mat-header-cell *matHeaderCellDef>From</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.fromPeriod }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="toPeriod">
|
||||
<th mat-header-cell *matHeaderCellDef>To</th>
|
||||
<td mat-cell *matCellDef="let slab">
|
||||
{{ slab.toPeriod !== null && slab.toPeriod !== undefined ? slab.toPeriod : 'N/A' }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="periodTypeLabel">
|
||||
<th mat-header-cell *matHeaderCellDef>Period Type</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.periodTypeLabel }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="annualInterestRate">
|
||||
<th mat-header-cell *matHeaderCellDef>Annual Rate (%)</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.annualInterestRate | number: '1.2-2' }}</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="historySlabColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: historySlabColumns"></tr>
|
||||
</table>
|
||||
</mat-expansion-panel>
|
||||
}
|
||||
</mat-accordion>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
.page-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.25rem;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
margin: 0.25rem 0 0;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.actions-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.save-message {
|
||||
color: #2563eb;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.history-section {
|
||||
margin-top: 1rem;
|
||||
padding-top: 0.5rem;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.section-subtitle {
|
||||
margin: 0.25rem 0 0;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.version-header {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.meta-text {
|
||||
margin-left: 0.75rem;
|
||||
color: #6b7280;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.active-badge,
|
||||
.inactive-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.2rem 0.5rem;
|
||||
border-radius: 999px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.active-badge {
|
||||
background: #dcfce7;
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
.inactive-badge {
|
||||
background: #f3f4f6;
|
||||
color: #4b5563;
|
||||
}
|
||||
|
||||
.remarks-text {
|
||||
margin: 0 0 0.75rem;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.history-table {
|
||||
width: 100%;
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Component, OnInit, inject } from '@angular/core';
|
||||
import { ProductsService } from 'app/products/products.service';
|
||||
import { RecurringDepositProductInterestRateChartStepComponent } from '../recurring-deposit-product-stepper/recurring-deposit-product-interest-rate-chart-step/recurring-deposit-product-interest-rate-chart-step.component';
|
||||
import { MatButton } from '@angular/material/button';
|
||||
import { DecimalPipe } from '@angular/common';
|
||||
import {
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
MatHeaderCellDef,
|
||||
MatHeaderCell,
|
||||
MatCellDef,
|
||||
MatCell,
|
||||
MatHeaderRowDef,
|
||||
MatHeaderRow,
|
||||
MatRowDef,
|
||||
MatRow
|
||||
} from '@angular/material/table';
|
||||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
|
||||
@Component({
|
||||
selector: 'mifosx-base-recurring-deposit-interest-rate-chart',
|
||||
templateUrl: './base-recurring-deposit-interest-rate-chart.component.html',
|
||||
styleUrls: ['./base-recurring-deposit-interest-rate-chart.component.scss'],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
MatButton,
|
||||
MatExpansionModule,
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
MatHeaderCellDef,
|
||||
MatHeaderCell,
|
||||
MatCellDef,
|
||||
MatCell,
|
||||
MatHeaderRowDef,
|
||||
MatHeaderRow,
|
||||
MatRowDef,
|
||||
MatRow,
|
||||
DecimalPipe,
|
||||
RecurringDepositProductInterestRateChartStepComponent
|
||||
]
|
||||
})
|
||||
export class BaseRecurringDepositInterestRateChartComponent implements OnInit {
|
||||
private productsService = inject(ProductsService);
|
||||
|
||||
recurringDepositProductsTemplate: any;
|
||||
rdInterestRates: any[] = [];
|
||||
rdInterestRateHistory: any[] = [];
|
||||
readonly historySlabColumns = [
|
||||
'srNo',
|
||||
'description',
|
||||
'fromPeriod',
|
||||
'toPeriod',
|
||||
'periodTypeLabel',
|
||||
'annualInterestRate'
|
||||
];
|
||||
isSaving = false;
|
||||
isHistoryLoading = false;
|
||||
saveMessage = '';
|
||||
|
||||
ngOnInit(): void {
|
||||
this.productsService.getRecurringDepositProductsTemplate().subscribe({
|
||||
next: (template: any) => (this.recurringDepositProductsTemplate = template),
|
||||
error: () => (this.recurringDepositProductsTemplate = null)
|
||||
});
|
||||
|
||||
this.loadBaseRates();
|
||||
this.loadBaseRateHistory();
|
||||
}
|
||||
|
||||
get chartStep(): RecurringDepositProductInterestRateChartStepComponent | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
save(stepComponent: RecurringDepositProductInterestRateChartStepComponent): void {
|
||||
this.isSaving = true;
|
||||
this.saveMessage = '';
|
||||
const payload = stepComponent.modifiedRdInterestRatePayload;
|
||||
this.productsService.saveRdBaseInterestRates(payload).subscribe({
|
||||
next: () => {
|
||||
this.saveMessage = 'CBS base chart saved successfully.';
|
||||
this.loadBaseRates();
|
||||
this.loadBaseRateHistory();
|
||||
this.isSaving = false;
|
||||
},
|
||||
error: () => {
|
||||
this.saveMessage = 'Unable to save CBS base chart.';
|
||||
this.isSaving = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private loadBaseRates(): void {
|
||||
this.productsService.getRdBaseInterestRates().subscribe({
|
||||
next: (rates: any[]) => (this.rdInterestRates = rates || []),
|
||||
error: () => (this.rdInterestRates = [])
|
||||
});
|
||||
}
|
||||
|
||||
private loadBaseRateHistory(): void {
|
||||
this.isHistoryLoading = true;
|
||||
this.productsService.getRdBaseInterestRateHistory().subscribe({
|
||||
next: (history: any[]) => {
|
||||
this.rdInterestRateHistory = history || [];
|
||||
this.isHistoryLoading = false;
|
||||
},
|
||||
error: () => {
|
||||
this.rdInterestRateHistory = [];
|
||||
this.isHistoryLoading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+53
-5
@@ -69,12 +69,60 @@
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<i class="fas fa-chart-area rdp-tab-icon"></i>
|
||||
{{ 'labels.inputs.INTEREST RATE CHART' | translate }}
|
||||
CBS Base Chart
|
||||
</ng-template>
|
||||
<div class="rdp-tab-content">
|
||||
<mifosx-recurring-deposit-product-interest-rate-chart-step
|
||||
[recurringDepositProductsTemplate]="recurringDepositProductsTemplate"
|
||||
></mifosx-recurring-deposit-product-interest-rate-chart-step>
|
||||
@if (activeBaseVersion) {
|
||||
<div class="base-rate-card">
|
||||
<div class="base-rate-header">
|
||||
<div>
|
||||
<h3 class="base-rate-title">{{ activeBaseVersion.versionLabel }}</h3>
|
||||
<p class="base-rate-meta">
|
||||
Active CBS base chart
|
||||
<span>Effective from {{ activeBaseVersion.effectiveFromDate }}</span>
|
||||
</p>
|
||||
</div>
|
||||
<span class="base-rate-badge">Active</span>
|
||||
</div>
|
||||
|
||||
@if (activeBaseVersion.remarks) {
|
||||
<p class="base-rate-remarks">{{ activeBaseVersion.remarks }}</p>
|
||||
}
|
||||
|
||||
<table class="base-rate-table" mat-table [dataSource]="activeBaseVersion.slabs">
|
||||
<ng-container matColumnDef="srNo">
|
||||
<th mat-header-cell *matHeaderCellDef>Sr.</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.srNo }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="description">
|
||||
<th mat-header-cell *matHeaderCellDef>Particular</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.description }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="fromPeriod">
|
||||
<th mat-header-cell *matHeaderCellDef>From</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.fromPeriod }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="toPeriod">
|
||||
<th mat-header-cell *matHeaderCellDef>To</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.toPeriod ?? 'N/A' }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="periodTypeLabel">
|
||||
<th mat-header-cell *matHeaderCellDef>Period Type</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.periodTypeLabel }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="annualInterestRate">
|
||||
<th mat-header-cell *matHeaderCellDef>Annual Rate (%)</th>
|
||||
<td mat-cell *matCellDef="let slab">
|
||||
<strong>{{ slab.annualInterestRate | number: '1.2-2' }}</strong>
|
||||
</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="rdSlabColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: rdSlabColumns"></tr>
|
||||
</table>
|
||||
</div>
|
||||
} @else {
|
||||
<p>Loading CBS base chart...</p>
|
||||
}
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
@@ -114,7 +162,7 @@
|
||||
<div class="rdp-tab-content">
|
||||
<mifosx-recurring-deposit-product-preview-step
|
||||
[recurringDepositProductsTemplate]="recurringDepositProductsTemplate"
|
||||
[chartSlabsDisplayedColumns]="recurringDepositProductInterestRateChartStep.chartSlabsDisplayedColumns"
|
||||
[chartSlabsDisplayedColumns]="rdSlabColumns"
|
||||
[accountingRuleData]="accountingRuleData"
|
||||
[recurringDepositProduct]="recurringDepositProduct"
|
||||
(submitEvent)="submit()"
|
||||
|
||||
+48
@@ -71,6 +71,54 @@ $primary: #1353a0;
|
||||
padding: 24px 16px;
|
||||
}
|
||||
|
||||
.base-rate-card {
|
||||
padding: 16px;
|
||||
border: 1px solid #dbe4f0;
|
||||
border-radius: 14px;
|
||||
background: linear-gradient(180deg, #f8fbff 0%, #fff 100%);
|
||||
}
|
||||
|
||||
.base-rate-header {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.base-rate-title {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.base-rate-meta {
|
||||
margin: 6px 0 0;
|
||||
color: #5b6b7f;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.base-rate-badge {
|
||||
padding: 4px 10px;
|
||||
border-radius: 999px;
|
||||
background: #dcfce7;
|
||||
color: #166534;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.base-rate-remarks {
|
||||
margin: 0 0 12px;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.base-rate-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.rdp-action-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
+51
-12
@@ -15,9 +15,20 @@ import { RecurringDepositProductDetailsStepComponent } from '../recurring-deposi
|
||||
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 {
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
MatHeaderCellDef,
|
||||
MatHeaderCell,
|
||||
MatCellDef,
|
||||
MatCell,
|
||||
MatHeaderRowDef,
|
||||
MatHeaderRow,
|
||||
MatRowDef,
|
||||
MatRow
|
||||
} from '@angular/material/table';
|
||||
|
||||
/** Custom Services */
|
||||
import { ProductsService } from 'app/products/products.service';
|
||||
@@ -40,10 +51,19 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
RecurringDepositProductCurrencyStepComponent,
|
||||
RecurringDepositProductTermsStepComponent,
|
||||
RecurringDepositProductSettingsStepComponent,
|
||||
RecurringDepositProductInterestRateChartStepComponent,
|
||||
RecurringDepositProductChargesStepComponent,
|
||||
RecurringDepositProductAccountingStepComponent,
|
||||
RecurringDepositProductPreviewStepComponent
|
||||
RecurringDepositProductPreviewStepComponent,
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
MatHeaderCellDef,
|
||||
MatHeaderCell,
|
||||
MatCellDef,
|
||||
MatCell,
|
||||
MatHeaderRowDef,
|
||||
MatHeaderRow,
|
||||
MatRowDef,
|
||||
MatRow
|
||||
]
|
||||
})
|
||||
export class CreateRecurringDepositProductComponent {
|
||||
@@ -61,8 +81,6 @@ export class CreateRecurringDepositProductComponent {
|
||||
recurringDepositProductTermsStep: RecurringDepositProductTermsStepComponent;
|
||||
@ViewChild(RecurringDepositProductSettingsStepComponent, { static: true })
|
||||
recurringDepositProductSettingsStep: RecurringDepositProductSettingsStepComponent;
|
||||
@ViewChild(RecurringDepositProductInterestRateChartStepComponent, { static: true })
|
||||
recurringDepositProductInterestRateChartStep: RecurringDepositProductInterestRateChartStepComponent;
|
||||
@ViewChild(RecurringDepositProductChargesStepComponent, { static: true })
|
||||
recurringDepositProductChargesStep: RecurringDepositProductChargesStepComponent;
|
||||
@ViewChild(RecurringDepositProductAccountingStepComponent, { static: true })
|
||||
@@ -71,6 +89,15 @@ export class CreateRecurringDepositProductComponent {
|
||||
recurringDepositProductsTemplate: any;
|
||||
accountingRuleData: string[] = [];
|
||||
selectedTabIndex = 0;
|
||||
rdInterestRates: any[] = [];
|
||||
readonly rdSlabColumns = [
|
||||
'srNo',
|
||||
'description',
|
||||
'fromPeriod',
|
||||
'toPeriod',
|
||||
'periodTypeLabel',
|
||||
'annualInterestRate'
|
||||
];
|
||||
|
||||
/**
|
||||
* @param {ActivatedRoute} route Activated Route.
|
||||
@@ -84,6 +111,15 @@ export class CreateRecurringDepositProductComponent {
|
||||
this.recurringDepositProductsTemplate = data.recurringDepositProductsTemplate;
|
||||
});
|
||||
this.accountingRuleData = this.accounting.getAccountingRulesForSavings();
|
||||
// Load the CBS base chart as the reference for the editable table
|
||||
this.productsService.getRdBaseInterestRates().subscribe({
|
||||
next: (rates: any[]) => {
|
||||
this.rdInterestRates = rates || [];
|
||||
},
|
||||
error: () => {
|
||||
this.rdInterestRates = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get recurringDepositProductDetailsForm() {
|
||||
@@ -102,10 +138,6 @@ export class CreateRecurringDepositProductComponent {
|
||||
return this.recurringDepositProductSettingsStep.recurringDepositProductSettingsForm;
|
||||
}
|
||||
|
||||
get recurringDepositProductInterestRateChartForm() {
|
||||
return this.recurringDepositProductInterestRateChartStep.recurringDepositProductInterestRateChartForm;
|
||||
}
|
||||
|
||||
get recurringDepositProductAccountingForm() {
|
||||
return this.recurringDepositProductAccountingStep.recurringDepositProductAccountingForm;
|
||||
}
|
||||
@@ -120,20 +152,27 @@ export class CreateRecurringDepositProductComponent {
|
||||
this.recurringDepositProductCurrencyForm.valid &&
|
||||
this.recurringDepositProductTermsForm.valid &&
|
||||
this.recurringDepositProductSettingsForm.valid &&
|
||||
this.recurringDepositProductInterestRateChartForm.valid &&
|
||||
this.recurringDepositProductAccountingForm.valid
|
||||
);
|
||||
}
|
||||
|
||||
get activeBaseVersion() {
|
||||
return this.rdInterestRates.find((version: any) => version.isActive) ?? this.rdInterestRates[0] ?? null;
|
||||
}
|
||||
|
||||
get activeBaseSlabs() {
|
||||
return this.activeBaseVersion?.slabs || [];
|
||||
}
|
||||
|
||||
get recurringDepositProduct() {
|
||||
return {
|
||||
...this.recurringDepositProductDetailsStep.recurringDepositProductDetails,
|
||||
...this.recurringDepositProductCurrencyStep.recurringDepositProductCurrency,
|
||||
...this.recurringDepositProductTermsStep.recurringDepositProductTerms,
|
||||
...this.recurringDepositProductSettingsStep.recurringDepositProductSettings,
|
||||
...this.recurringDepositProductInterestRateChartStep.recurringDepositProductInterestRateChart,
|
||||
...this.recurringDepositProductChargesStep.recurringDepositProductCharges,
|
||||
...this.recurringDepositProductAccountingStep.recurringDepositProductAccounting
|
||||
...this.recurringDepositProductAccountingStep.recurringDepositProductAccounting,
|
||||
rdSlabs: this.activeBaseSlabs
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+159
-76
@@ -6,112 +6,195 @@
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<div class="container">
|
||||
<mat-stepper
|
||||
orientation="vertical"
|
||||
[linear]="false"
|
||||
class="mat-elevation-z8"
|
||||
labelPosition="bottom"
|
||||
#recurringDepositProductStepper
|
||||
<div class="rdp-container">
|
||||
<div class="rdp-header">
|
||||
<i class="fas fa-sync-alt rdp-header__icon"></i>
|
||||
<h2 class="rdp-header__title">{{ 'labels.heading.Edit Recurring Deposit Product' | translate }}</h2>
|
||||
</div>
|
||||
|
||||
<mat-tab-group
|
||||
class="rdp-tabs"
|
||||
animationDuration="200ms"
|
||||
[preserveContent]="true"
|
||||
[(selectedIndex)]="selectedTabIndex"
|
||||
>
|
||||
<ng-template matStepperIcon="number">
|
||||
<fa-icon icon="pencil-alt" size="sm"></fa-icon>
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<i class="fas fa-info-circle rdp-tab-icon"></i>
|
||||
{{ 'labels.inputs.DETAILS' | translate }}
|
||||
</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>{{ 'labels.inputs.DETAILS' | translate }}</ng-template>
|
||||
|
||||
<div class="rdp-tab-content">
|
||||
<mifosx-recurring-deposit-product-details-step
|
||||
[recurringDepositProductsTemplate]="recurringDepositProductsTemplate"
|
||||
>
|
||||
</mifosx-recurring-deposit-product-details-step>
|
||||
</mat-step>
|
||||
|
||||
<mat-step [stepControl]="recurringDepositProductCurrencyForm">
|
||||
<ng-template matStepLabel>{{ 'labels.inputs.CURRENCY' | translate }}</ng-template>
|
||||
></mifosx-recurring-deposit-product-details-step>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<i class="fas fa-coins rdp-tab-icon"></i>
|
||||
{{ 'labels.inputs.CURRENCY' | translate }}
|
||||
</ng-template>
|
||||
<div class="rdp-tab-content">
|
||||
<mifosx-recurring-deposit-product-currency-step
|
||||
[recurringDepositProductsTemplate]="recurringDepositProductsTemplate"
|
||||
>
|
||||
</mifosx-recurring-deposit-product-currency-step>
|
||||
</mat-step>
|
||||
|
||||
<mat-step [stepControl]="recurringDepositProductTermsForm">
|
||||
<ng-template matStepLabel>{{ 'labels.inputs.TERMS' | translate }}</ng-template>
|
||||
></mifosx-recurring-deposit-product-currency-step>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<i class="fas fa-sliders-h rdp-tab-icon"></i>
|
||||
{{ 'labels.inputs.TERMS' | translate }}
|
||||
</ng-template>
|
||||
<div class="rdp-tab-content">
|
||||
<mifosx-recurring-deposit-product-terms-step
|
||||
[recurringDepositProductsTemplate]="recurringDepositProductsTemplate"
|
||||
>
|
||||
</mifosx-recurring-deposit-product-terms-step>
|
||||
</mat-step>
|
||||
|
||||
<mat-step [stepControl]="recurringDepositProductSettingsForm">
|
||||
<ng-template matStepLabel>{{ 'labels.inputs.SETTINGS' | translate }}</ng-template>
|
||||
></mifosx-recurring-deposit-product-terms-step>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<i class="fas fa-cog rdp-tab-icon"></i>
|
||||
{{ 'labels.inputs.SETTINGS' | translate }}
|
||||
</ng-template>
|
||||
<div class="rdp-tab-content">
|
||||
<mifosx-recurring-deposit-product-settings-step
|
||||
[recurringDepositProductsTemplate]="recurringDepositProductsTemplate"
|
||||
>
|
||||
</mifosx-recurring-deposit-product-settings-step>
|
||||
</mat-step>
|
||||
></mifosx-recurring-deposit-product-settings-step>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
<mat-step [stepControl]="recurringDepositProductInterestRateChartForm">
|
||||
<ng-template matStepLabel>{{ 'labels.inputs.INTEREST RATE CHART' | translate }}</ng-template>
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<i class="fas fa-chart-area rdp-tab-icon"></i>
|
||||
CBS Base Chart
|
||||
</ng-template>
|
||||
<div class="rdp-tab-content">
|
||||
@if (activeBaseVersion) {
|
||||
<div class="base-rate-card">
|
||||
<div class="base-rate-header">
|
||||
<div>
|
||||
<h3 class="base-rate-title">{{ activeBaseVersion.versionLabel }}</h3>
|
||||
<p class="base-rate-meta">
|
||||
Active CBS base chart
|
||||
<span>Effective from {{ activeBaseVersion.effectiveFromDate }}</span>
|
||||
</p>
|
||||
</div>
|
||||
<span class="base-rate-badge">Active</span>
|
||||
</div>
|
||||
|
||||
<mifosx-recurring-deposit-product-interest-rate-chart-step
|
||||
[recurringDepositProductsTemplate]="recurringDepositProductsTemplate"
|
||||
>
|
||||
</mifosx-recurring-deposit-product-interest-rate-chart-step>
|
||||
</mat-step>
|
||||
@if (activeBaseVersion.remarks) {
|
||||
<p class="base-rate-remarks">{{ activeBaseVersion.remarks }}</p>
|
||||
}
|
||||
|
||||
<mat-step>
|
||||
<ng-template matStepLabel>{{ 'labels.inputs.CHARGES' | translate }}</ng-template>
|
||||
<table class="base-rate-table" mat-table [dataSource]="activeBaseVersion.slabs">
|
||||
<ng-container matColumnDef="srNo">
|
||||
<th mat-header-cell *matHeaderCellDef>Sr.</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.srNo }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="description">
|
||||
<th mat-header-cell *matHeaderCellDef>Particular</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.description }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="fromPeriod">
|
||||
<th mat-header-cell *matHeaderCellDef>From</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.fromPeriod }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="toPeriod">
|
||||
<th mat-header-cell *matHeaderCellDef>To</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.toPeriod ?? 'N/A' }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="periodTypeLabel">
|
||||
<th mat-header-cell *matHeaderCellDef>Period Type</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.periodTypeLabel }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="annualInterestRate">
|
||||
<th mat-header-cell *matHeaderCellDef>Annual Rate (%)</th>
|
||||
<td mat-cell *matCellDef="let slab">
|
||||
<strong>{{ slab.annualInterestRate | number: '1.2-2' }}</strong>
|
||||
</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="rdSlabColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: rdSlabColumns"></tr>
|
||||
</table>
|
||||
</div>
|
||||
} @else {
|
||||
<p>Loading CBS base chart...</p>
|
||||
}
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<i class="fas fa-receipt rdp-tab-icon"></i>
|
||||
{{ 'labels.inputs.CHARGES' | translate }}
|
||||
</ng-template>
|
||||
<div class="rdp-tab-content">
|
||||
<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>{{ 'labels.inputs.ACCOUNTING' | translate }}</ng-template>
|
||||
></mifosx-recurring-deposit-product-charges-step>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<i class="fas fa-book rdp-tab-icon"></i>
|
||||
{{ 'labels.inputs.ACCOUNTING' | translate }}
|
||||
</ng-template>
|
||||
<div class="rdp-tab-content">
|
||||
<mifosx-recurring-deposit-product-accounting-step
|
||||
[recurringDepositProductsTemplate]="recurringDepositProductsTemplate"
|
||||
[accountingRuleData]="accountingRuleData"
|
||||
[recurringDepositProductFormValid]="recurringDepositProductFormValidAndNotPrinstine"
|
||||
>
|
||||
</mifosx-recurring-deposit-product-accounting-step>
|
||||
</mat-step>
|
||||
></mifosx-recurring-deposit-product-accounting-step>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
@if (recurringDepositProductFormValidAndNotPrinstine) {
|
||||
<mat-step state="preview" completed>
|
||||
<ng-template matStepLabel>{{ 'labels.inputs.PREVIEW' | translate }}</ng-template>
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<i class="fas fa-eye rdp-tab-icon"></i>
|
||||
{{ 'labels.inputs.PREVIEW' | translate }}
|
||||
</ng-template>
|
||||
<div class="rdp-tab-content">
|
||||
<mifosx-recurring-deposit-product-preview-step
|
||||
[recurringDepositProductsTemplate]="recurringDepositProductsTemplate"
|
||||
[chartSlabsDisplayedColumns]="recurringDepositProductInterestRateChartStep.chartSlabsDisplayedColumns"
|
||||
[chartSlabsDisplayedColumns]="rdSlabColumns"
|
||||
[accountingRuleData]="accountingRuleData"
|
||||
[recurringDepositProduct]="recurringDepositProduct"
|
||||
[recurringDepositProduct]="recurringDepositProductPreview"
|
||||
(submitEvent)="submit()"
|
||||
>
|
||||
</mifosx-recurring-deposit-product-preview-step>
|
||||
</mat-step>
|
||||
></mifosx-recurring-deposit-product-preview-step>
|
||||
</div>
|
||||
</mat-tab>
|
||||
}
|
||||
</mat-stepper>
|
||||
</mat-tab-group>
|
||||
|
||||
<div class="rdp-action-bar">
|
||||
<button mat-raised-button [routerLink]="['../../']" class="rdp-action-bar__cancel">
|
||||
{{ 'labels.buttons.Cancel' | translate }}
|
||||
</button>
|
||||
<div class="rdp-action-bar__right">
|
||||
@if (selectedTabIndex > 0) {
|
||||
<button mat-raised-button (click)="selectedTabIndex = selectedTabIndex - 1">
|
||||
<i class="fas fa-arrow-left" style="margin-right: 8px"></i>
|
||||
{{ 'labels.buttons.Previous' | translate }}
|
||||
</button>
|
||||
}
|
||||
@if (selectedTabIndex < totalTabs - 1) {
|
||||
<button mat-raised-button color="primary" (click)="selectedTabIndex = selectedTabIndex + 1">
|
||||
{{ 'labels.buttons.Next' | translate }}
|
||||
<i class="fas fa-arrow-right" style="margin-left: 8px"></i>
|
||||
</button>
|
||||
}
|
||||
@if (recurringDepositProductFormValidAndNotPrinstine && selectedTabIndex === totalTabs - 1) {
|
||||
<button mat-raised-button color="primary" (click)="submit()">
|
||||
<i class="fas fa-check" style="margin-right: 8px"></i>
|
||||
{{ 'labels.buttons.Submit' | translate }}
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+131
@@ -5,3 +5,134 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
$primary: #1353a0;
|
||||
|
||||
.rdp-container {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 24px 16px;
|
||||
}
|
||||
|
||||
.rdp-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
&__icon {
|
||||
font-size: 20px;
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
&__title {
|
||||
margin: 0;
|
||||
font-size: 22px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.rdp-tabs {
|
||||
background: var(--md-sys-color-surface, #fff);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgb(0 0 0 / 8%);
|
||||
|
||||
::ng-deep {
|
||||
.mat-mdc-tab-header {
|
||||
border-bottom: 1px solid rgb(0 0 0 / 8%);
|
||||
}
|
||||
|
||||
.mat-mdc-tab {
|
||||
min-width: 100px;
|
||||
padding: 0 12px;
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
.mat-mdc-tab-body-wrapper {
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.mat-mdc-tab-body {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.mat-mdc-tab-body-content {
|
||||
overflow: visible !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rdp-tab-icon {
|
||||
margin-right: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.rdp-tab-content {
|
||||
padding: 24px 16px;
|
||||
}
|
||||
|
||||
.base-rate-card {
|
||||
padding: 16px;
|
||||
border: 1px solid #dbe4f0;
|
||||
border-radius: 14px;
|
||||
background: linear-gradient(180deg, #f8fbff 0%, #fff 100%);
|
||||
}
|
||||
|
||||
.base-rate-header {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.base-rate-title {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.base-rate-meta {
|
||||
margin: 6px 0 0;
|
||||
color: #5b6b7f;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.base-rate-badge {
|
||||
padding: 4px 10px;
|
||||
border-radius: 999px;
|
||||
background: #dcfce7;
|
||||
color: #166534;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.base-rate-remarks {
|
||||
margin: 0 0 12px;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.base-rate-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.rdp-action-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 20px;
|
||||
padding: 16px 0;
|
||||
border-top: 1px solid rgb(0 0 0 / 8%);
|
||||
|
||||
&__cancel {
|
||||
color: rgb(0 0 0 / 60%);
|
||||
}
|
||||
|
||||
&__right {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
+64
-20
@@ -15,16 +15,26 @@ import { RecurringDepositProductDetailsStepComponent } from '../recurring-deposi
|
||||
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 {
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
MatHeaderCellDef,
|
||||
MatHeaderCell,
|
||||
MatCellDef,
|
||||
MatCell,
|
||||
MatHeaderRowDef,
|
||||
MatHeaderRow,
|
||||
MatRowDef,
|
||||
MatRow
|
||||
} from '@angular/material/table';
|
||||
|
||||
/** Custom Services */
|
||||
import { ProductsService } from 'app/products/products.service';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { Accounting } from 'app/core/utils/accounting';
|
||||
import { MatStepper, MatStepperIcon, MatStep, MatStepLabel } from '@angular/material/stepper';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { RecurringDepositProductPreviewStepComponent } from '../recurring-deposit-product-stepper/recurring-deposit-product-preview-step/recurring-deposit-product-preview-step.component';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
|
||||
@@ -34,19 +44,24 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
styleUrls: ['./edit-recurring-deposit-product.component.scss'],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
MatStepper,
|
||||
MatStepperIcon,
|
||||
FaIconComponent,
|
||||
MatStep,
|
||||
MatStepLabel,
|
||||
MatTabsModule,
|
||||
RecurringDepositProductDetailsStepComponent,
|
||||
RecurringDepositProductCurrencyStepComponent,
|
||||
RecurringDepositProductTermsStepComponent,
|
||||
RecurringDepositProductSettingsStepComponent,
|
||||
RecurringDepositProductInterestRateChartStepComponent,
|
||||
RecurringDepositProductChargesStepComponent,
|
||||
RecurringDepositProductAccountingStepComponent,
|
||||
RecurringDepositProductPreviewStepComponent
|
||||
RecurringDepositProductPreviewStepComponent,
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
MatHeaderCellDef,
|
||||
MatHeaderCell,
|
||||
MatCellDef,
|
||||
MatCell,
|
||||
MatHeaderRowDef,
|
||||
MatHeaderRow,
|
||||
MatRowDef,
|
||||
MatRow
|
||||
]
|
||||
})
|
||||
export class EditRecurringDepositProductComponent {
|
||||
@@ -64,8 +79,6 @@ export class EditRecurringDepositProductComponent {
|
||||
recurringDepositProductTermsStep: RecurringDepositProductTermsStepComponent;
|
||||
@ViewChild(RecurringDepositProductSettingsStepComponent, { static: true })
|
||||
recurringDepositProductSettingsStep: RecurringDepositProductSettingsStepComponent;
|
||||
@ViewChild(RecurringDepositProductInterestRateChartStepComponent, { static: true })
|
||||
recurringDepositProductInterestRateChartStep: RecurringDepositProductInterestRateChartStepComponent;
|
||||
@ViewChild(RecurringDepositProductChargesStepComponent, { static: true })
|
||||
recurringDepositProductChargesStep: RecurringDepositProductChargesStepComponent;
|
||||
@ViewChild(RecurringDepositProductAccountingStepComponent, { static: true })
|
||||
@@ -73,6 +86,16 @@ export class EditRecurringDepositProductComponent {
|
||||
|
||||
recurringDepositProductsTemplate: any;
|
||||
accountingRuleData: string[] = [];
|
||||
rdInterestRates: any[] = [];
|
||||
readonly rdSlabColumns = [
|
||||
'srNo',
|
||||
'description',
|
||||
'fromPeriod',
|
||||
'toPeriod',
|
||||
'periodTypeLabel',
|
||||
'annualInterestRate'
|
||||
];
|
||||
selectedTabIndex = 0;
|
||||
|
||||
/**
|
||||
* @param {ActivatedRoute} route Activated Route.
|
||||
@@ -84,6 +107,14 @@ export class EditRecurringDepositProductComponent {
|
||||
constructor() {
|
||||
this.route.data.subscribe((data: { recurringDepositProductAndTemplate: any }) => {
|
||||
this.recurringDepositProductsTemplate = data.recurringDepositProductAndTemplate;
|
||||
this.productsService.getRdBaseInterestRates().subscribe({
|
||||
next: (rates: any[]) => {
|
||||
this.rdInterestRates = rates || [];
|
||||
},
|
||||
error: () => {
|
||||
this.rdInterestRates = [];
|
||||
}
|
||||
});
|
||||
});
|
||||
this.accountingRuleData = this.accounting.getAccountingRulesForSavings();
|
||||
}
|
||||
@@ -104,43 +135,55 @@ export class EditRecurringDepositProductComponent {
|
||||
return this.recurringDepositProductSettingsStep.recurringDepositProductSettingsForm;
|
||||
}
|
||||
|
||||
get recurringDepositProductInterestRateChartForm() {
|
||||
return this.recurringDepositProductInterestRateChartStep.recurringDepositProductInterestRateChartForm;
|
||||
}
|
||||
|
||||
get recurringDepositProductAccountingForm() {
|
||||
return this.recurringDepositProductAccountingStep.recurringDepositProductAccountingForm;
|
||||
}
|
||||
|
||||
get totalTabs(): number {
|
||||
return this.recurringDepositProductFormValidAndNotPrinstine ? 7 : 6;
|
||||
}
|
||||
|
||||
get recurringDepositProductFormValidAndNotPrinstine() {
|
||||
return (
|
||||
this.recurringDepositProductDetailsForm.valid &&
|
||||
this.recurringDepositProductCurrencyForm.valid &&
|
||||
this.recurringDepositProductTermsForm.valid &&
|
||||
this.recurringDepositProductSettingsForm.valid &&
|
||||
this.recurringDepositProductInterestRateChartForm.valid &&
|
||||
this.recurringDepositProductAccountingForm.valid &&
|
||||
(this.recurringDepositProductDetailsForm.pristine ||
|
||||
this.recurringDepositProductCurrencyForm.pristine ||
|
||||
this.recurringDepositProductTermsForm.pristine ||
|
||||
this.recurringDepositProductSettingsForm.pristine ||
|
||||
this.recurringDepositProductInterestRateChartForm.pristine ||
|
||||
this.recurringDepositProductAccountingForm.pristine)
|
||||
);
|
||||
}
|
||||
|
||||
get activeBaseVersion() {
|
||||
return this.rdInterestRates.find((version: any) => version.isActive) ?? this.rdInterestRates[0] ?? null;
|
||||
}
|
||||
|
||||
get activeBaseSlabs() {
|
||||
return this.activeBaseVersion?.slabs || [];
|
||||
}
|
||||
|
||||
get recurringDepositProduct() {
|
||||
return {
|
||||
...this.recurringDepositProductDetailsStep.recurringDepositProductDetails,
|
||||
...this.recurringDepositProductCurrencyStep.recurringDepositProductCurrency,
|
||||
...this.recurringDepositProductTermsStep.recurringDepositProductTerms,
|
||||
...this.recurringDepositProductSettingsStep.recurringDepositProductSettings,
|
||||
...this.recurringDepositProductInterestRateChartStep.recurringDepositProductInterestRateChart,
|
||||
...this.recurringDepositProductChargesStep.recurringDepositProductCharges,
|
||||
...this.recurringDepositProductAccountingStep.recurringDepositProductAccounting
|
||||
};
|
||||
}
|
||||
|
||||
get recurringDepositProductPreview() {
|
||||
return {
|
||||
...this.recurringDepositProduct,
|
||||
rdSlabs: this.activeBaseSlabs
|
||||
};
|
||||
}
|
||||
|
||||
submit() {
|
||||
// TODO: Update once language and date settings are setup
|
||||
const recurringDepositProduct = {
|
||||
@@ -152,8 +195,9 @@ export class EditRecurringDepositProductComponent {
|
||||
recurringDepositProduct.description = '';
|
||||
}
|
||||
delete recurringDepositProduct.advancedAccountingRules;
|
||||
const productId = this.recurringDepositProductsTemplate.id;
|
||||
this.productsService
|
||||
.updateRecurringDepositProduct(this.recurringDepositProductsTemplate.id, recurringDepositProduct)
|
||||
.updateRecurringDepositProduct(productId, recurringDepositProduct)
|
||||
.subscribe((response: any) => {
|
||||
this.router.navigate(['../'], { relativeTo: this.route });
|
||||
});
|
||||
|
||||
+72
-293
@@ -6,325 +6,104 @@
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<form [formGroup]="recurringDepositProductInterestRateChartForm">
|
||||
<div class="rdpi-section">
|
||||
<!-- ── Bank Interest Rate Chart (editable reference table from DB) ───────── -->
|
||||
<div class="rdpi-section rdpi-ref-section">
|
||||
<div class="rdpi-section__header">
|
||||
<i class="fa fa-chart-line"></i>
|
||||
<h3>{{ 'labels.heading.Interest Rate Charts' | translate }}</h3>
|
||||
<i class="fa fa-table"></i>
|
||||
<h3>Bank Interest Rate Chart <span class="rdpi-ref-badge">Reference / Editable</span></h3>
|
||||
<span class="rdpi-spacer"></span>
|
||||
<button type="button" mat-raised-button color="primary" class="rdpi-add-btn" (click)="addChart()">
|
||||
<button type="button" mat-raised-button color="primary" class="rdpi-add-btn" (click)="addSlab()">
|
||||
<fa-icon icon="plus" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.Add' | translate }}
|
||||
Add Slab
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="rdpi-content">
|
||||
@for (chart of charts.controls; track chart; let chartIndex = $index) {
|
||||
<div class="rdpi-chart-block" formArrayName="charts">
|
||||
<div [formGroupName]="chartIndex">
|
||||
<div class="rdpi-chart-actions">
|
||||
<button
|
||||
type="button"
|
||||
mat-icon-button
|
||||
color="warn"
|
||||
(click)="delete(charts, chartIndex)"
|
||||
matTooltip="{{ 'tooltips.Delete Interest Rate Chart' | translate }}"
|
||||
matTooltipPosition="left"
|
||||
>
|
||||
<fa-icon icon="trash"></fa-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="rdpi-grid">
|
||||
<mat-form-field appearance="outline" class="rdpi-field">
|
||||
<mat-label>{{ 'labels.inputs.Name' | translate }}</mat-label>
|
||||
<input matInput formControlName="name" required />
|
||||
@if (chart.get('name')?.touched && chart.get('name')?.hasError('required')) {
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Name' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
@if (_referenceVersionLabel) {
|
||||
<div class="rdpi-ref-meta">
|
||||
<span><strong>Version:</strong> {{ _referenceVersionLabel }}</span>
|
||||
@if (_referenceRemarks) {
|
||||
<span> | {{ _referenceRemarks }}</span>
|
||||
}
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="rdpi-field rdpi-field--full">
|
||||
<mat-label>{{ 'labels.inputs.Description' | translate }}</mat-label>
|
||||
<textarea matInput formControlName="description" required></textarea>
|
||||
@if (chart.get('description')?.touched && chart.get('description')?.hasError('required')) {
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Description' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</div>
|
||||
}
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="rdpi-field" (click)="validFromDatePicker.open()">
|
||||
<mat-label>{{ 'labels.inputs.From Date' | translate }}</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>
|
||||
{{ 'labels.inputs.From Date' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="rdpi-field" (click)="endDatePicker.open()">
|
||||
<mat-label>{{ 'labels.inputs.End Date' | translate }}</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>
|
||||
|
||||
<div class="rdpi-field rdpi-checkbox-row">
|
||||
<mat-checkbox labelPosition="before" formControlName="isPrimaryGroupingByAmount">
|
||||
{{ 'labels.inputs.Is primary grouping by amount?' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rdpi-slab-actions">
|
||||
<button
|
||||
type="button"
|
||||
mat-raised-button
|
||||
color="primary"
|
||||
class="rdpi-add-btn"
|
||||
(click)="addChartSlab(chart.controls.chartSlabs)"
|
||||
>
|
||||
<fa-icon icon="plus" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.Add Slab' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@if (chart.value.chartSlabs.length === 0) {
|
||||
@if (_editableSlabs.length === 0) {
|
||||
<div class="rdpi-empty-msg">
|
||||
<h3>{{ 'labels.heading.It is required to add at least one Slab' | translate }}</h3>
|
||||
<h3>No interest rate slabs defined. Click <strong>Add Slab</strong> to add one.</h3>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (chart.value.chartSlabs.length !== 0) {
|
||||
@if (_editableSlabs.length > 0) {
|
||||
<div class="rdpi-table-wrapper">
|
||||
<table mat-table [dataSource]="chart.value.chartSlabs" multiTemplateDataRows>
|
||||
<ng-container matColumnDef="period">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Period' | translate }}</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>{{ 'labels.inputs.Amount Range' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let chartSlab">
|
||||
{{ chartSlab.amountRangeFrom + ' - ' + chartSlab.amountRangeTo }}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="annualInterestRate">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Interest' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let chartSlab">
|
||||
{{ chartSlab.annualInterestRate }}
|
||||
</td>
|
||||
<table mat-table [dataSource]="_editableSlabs" class="rdpi-ref-table">
|
||||
<ng-container matColumnDef="srNo">
|
||||
<th mat-header-cell *matHeaderCellDef>Sr.</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.srNo }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="description">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Description' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let chartSlab">
|
||||
{{ chartSlab.description }}
|
||||
<th mat-header-cell *matHeaderCellDef>Particular</th>
|
||||
<td mat-cell *matCellDef="let slab; let i = index">
|
||||
<input
|
||||
class="rdpi-inline-input"
|
||||
[value]="slab.description"
|
||||
(change)="editSlab(i, 'description', $any($event.target).value)"
|
||||
/>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Actions' | translate }}</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
|
||||
"
|
||||
>
|
||||
@if (expandChartSlabIndex[chartIndex] !== chartSlabIndex) {
|
||||
<span>
|
||||
<fa-icon icon="eye" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.View Incentives' | translate }}
|
||||
</span>
|
||||
}
|
||||
@if (expandChartSlabIndex[chartIndex] === chartSlabIndex) {
|
||||
<span>
|
||||
<fa-icon icon="eye-slash" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.Hide Incentives' | translate }}
|
||||
</span>
|
||||
}
|
||||
</button>
|
||||
|
||||
<ng-container matColumnDef="fromPeriod">
|
||||
<th mat-header-cell *matHeaderCellDef>From</th>
|
||||
<td mat-cell *matCellDef="let slab; let i = index">
|
||||
<input
|
||||
class="rdpi-inline-input rdpi-inline-input--num"
|
||||
type="number"
|
||||
[value]="slab.fromPeriod"
|
||||
(change)="editSlab(i, 'fromPeriod', +$any($event.target).value)"
|
||||
/>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="incentives">
|
||||
<td
|
||||
mat-cell
|
||||
*matCellDef="let chartSlab; let chartSlabIndex = dataIndex"
|
||||
[attr.colspan]="chartSlabsDisplayedColumns[chartIndex].length"
|
||||
>
|
||||
<div
|
||||
class="rdpi-incentives"
|
||||
[@expandChartSlab]="
|
||||
chartSlabIndex === expandChartSlabIndex[chartIndex] ? 'expanded' : 'collapsed'
|
||||
"
|
||||
>
|
||||
<mat-card class="rdpi-incentive-card">
|
||||
<h4 class="rdpi-incentive-title">
|
||||
{{ 'labels.heading.Incentives' | translate }}
|
||||
</h4>
|
||||
<div class="rdpi-incentive-add">
|
||||
<button
|
||||
mat-raised-button
|
||||
color="primary"
|
||||
class="rdpi-add-btn"
|
||||
(click)="addIncentive(getIncentives(chart.controls.chartSlabs, chartSlabIndex))"
|
||||
>
|
||||
<fa-icon icon="plus" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.Add' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
@if (chartSlab.incentives.length) {
|
||||
<div class="rdpi-table-wrapper">
|
||||
<table mat-table [dataSource]="chartSlab.incentives">
|
||||
<ng-container matColumnDef="entityType">
|
||||
<th mat-header-cell *matHeaderCellDef>
|
||||
{{ 'labels.inputs.Entity Type' | translate }}
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let incentive">
|
||||
{{ incentive.entityType | find: entityTypeData : 'id' : 'value' }}
|
||||
|
||||
<ng-container matColumnDef="toPeriod">
|
||||
<th mat-header-cell *matHeaderCellDef>To</th>
|
||||
<td mat-cell *matCellDef="let slab; let i = index">
|
||||
<input
|
||||
class="rdpi-inline-input rdpi-inline-input--num"
|
||||
type="number"
|
||||
[value]="slab.toPeriod ?? ''"
|
||||
(change)="editSlab(i, 'toPeriod', $any($event.target).value ? +$any($event.target).value : null)"
|
||||
/>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="attributeName">
|
||||
<th mat-header-cell *matHeaderCellDef>
|
||||
{{ 'labels.inputs.Attribute Name' | translate }}
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let incentive">
|
||||
{{ incentive.attributeName | find: attributeNameData : 'id' : 'value' }}
|
||||
|
||||
<ng-container matColumnDef="periodTypeLabel">
|
||||
<th mat-header-cell *matHeaderCellDef>Period</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.periodTypeLabel }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="annualInterestRate">
|
||||
<th mat-header-cell *matHeaderCellDef>Rate (%)</th>
|
||||
<td mat-cell *matCellDef="let slab; let i = index">
|
||||
<input
|
||||
class="rdpi-inline-input rdpi-inline-input--rate"
|
||||
type="number"
|
||||
step="0.01"
|
||||
[value]="slab.annualInterestRate"
|
||||
(change)="editSlab(i, 'annualInterestRate', +$any($event.target).value)"
|
||||
/>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="conditionType">
|
||||
<th mat-header-cell *matHeaderCellDef>
|
||||
{{ 'labels.inputs.Condition Type' | translate }}
|
||||
</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>
|
||||
{{ 'labels.inputs.Attribute Value' | translate }}
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let incentive">
|
||||
@switch (incentive.attributeName) {
|
||||
@case (2) {
|
||||
<span>{{ incentive.attributeValue | find: genderData : 'id' : 'name' }}</span>
|
||||
}
|
||||
@case (3) {
|
||||
<span>{{ incentive.attributeValue }}</span>
|
||||
}
|
||||
@case (4) {
|
||||
<span>{{
|
||||
incentive.attributeValue | find: clientTypeData : 'id' : 'name'
|
||||
}}</span>
|
||||
}
|
||||
@case (5) {
|
||||
<span>{{
|
||||
incentive.attributeValue | find: clientClassificationData : 'id' : 'name'
|
||||
}}</span>
|
||||
}
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="incentiveType">
|
||||
<th mat-header-cell *matHeaderCellDef>
|
||||
{{ 'labels.inputs.Incentive Type' | translate }}
|
||||
</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>{{ 'labels.inputs.Interest' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let incentive">
|
||||
{{ incentive.amount }}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Actions' | translate }}</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>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="refSlabColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: refSlabColumns"></tr>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
</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 class="rdpi-ref-footer">
|
||||
<p class="rdpi-ref-hint">
|
||||
Edit the rates above. When you <strong>Save</strong> the product, a new version will be inserted in the database
|
||||
— previous rates are preserved as history.
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
+65
@@ -148,3 +148,68 @@
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Editable reference-rate table ───────────────────────────── */
|
||||
.rdpi-ref-section {
|
||||
margin-top: 24px;
|
||||
border-color: #bfdbfe;
|
||||
background: #f0f7ff;
|
||||
|
||||
.rdpi-section__header {
|
||||
background: #dbeafe;
|
||||
color: #1e3a5f;
|
||||
}
|
||||
}
|
||||
|
||||
.rdpi-ref-badge {
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
background: #3b82f6;
|
||||
color: #fff;
|
||||
border-radius: 10px;
|
||||
padding: 2px 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.rdpi-ref-meta {
|
||||
padding: 8px 16px;
|
||||
font-size: 13px;
|
||||
color: #555;
|
||||
border-bottom: 1px solid #bfdbfe;
|
||||
}
|
||||
|
||||
.rdpi-ref-table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.rdpi-inline-input {
|
||||
border: 1px solid #cbd5e1;
|
||||
border-radius: 4px;
|
||||
padding: 3px 6px;
|
||||
font-size: 13px;
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: #3b82f6;
|
||||
}
|
||||
|
||||
&--num {
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
&--rate {
|
||||
width: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
.rdpi-ref-footer {
|
||||
padding: 10px 16px;
|
||||
}
|
||||
|
||||
.rdpi-ref-hint {
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
+194
-23
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
/** Angular Imports */
|
||||
import { Component, OnInit, Input, inject } from '@angular/core';
|
||||
import { Component, OnInit, OnChanges, Input, inject } from '@angular/core';
|
||||
import {
|
||||
UntypedFormGroup,
|
||||
UntypedFormBuilder,
|
||||
@@ -21,7 +21,6 @@ import { animate, state, style, transition, trigger } from '@angular/animations'
|
||||
/** Custom Components */
|
||||
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';
|
||||
|
||||
/** Dialog Components */
|
||||
import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-base';
|
||||
@@ -31,11 +30,10 @@ import { InputBase } from 'app/shared/form-dialog/formfield/model/input-base';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { Dates } from 'app/core/utils/dates';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { MatButton, MatIconButton } from '@angular/material/button';
|
||||
import { MatButton } from '@angular/material/button';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
|
||||
import { MatDivider } from '@angular/material/divider';
|
||||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
import { MatCheckbox } from '@angular/material/checkbox';
|
||||
import {
|
||||
MatTable,
|
||||
@@ -68,8 +66,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
FaIconComponent,
|
||||
MatDivider,
|
||||
MatIconButton,
|
||||
MatTooltip,
|
||||
MatButton,
|
||||
MatCheckbox,
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
@@ -81,8 +78,6 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
MatHeaderRow,
|
||||
MatRowDef,
|
||||
MatRow,
|
||||
MatStepperPrevious,
|
||||
MatStepperNext,
|
||||
FindPipe
|
||||
]
|
||||
})
|
||||
@@ -94,6 +89,190 @@ export class RecurringDepositProductInterestRateChartStepComponent implements On
|
||||
private translateService = inject(TranslateService);
|
||||
|
||||
@Input() recurringDepositProductsTemplate: any;
|
||||
/** Reference rate chart fetched from m_rd_interest_rate_effective / m_rd_interest_rate_slab_history */
|
||||
@Input() set rdInterestRates(rates: any[]) {
|
||||
if (rates && rates.length > 0) {
|
||||
// Use the first active version's slabs as the editable working copy
|
||||
const activeVersion = rates.find((v: any) => v.isActive) ?? rates[0];
|
||||
this._referenceVersionLabel = activeVersion.versionLabel;
|
||||
this._referenceRemarks = activeVersion.remarks;
|
||||
// Deep-clone slabs so the original is not mutated
|
||||
this._editableSlabs = (activeVersion.slabs ?? []).map((s: any) => ({ ...s }));
|
||||
this.ensureSlabRangeRules(this._editableSlabs);
|
||||
}
|
||||
}
|
||||
|
||||
/** Slabs the user is editing in the reference-rate table */
|
||||
_editableSlabs: any[] = [];
|
||||
_referenceVersionLabel = '';
|
||||
_referenceRemarks = '';
|
||||
|
||||
/** Columns for the editable reference-rate table */
|
||||
readonly refSlabColumns = [
|
||||
'srNo',
|
||||
'description',
|
||||
'fromPeriod',
|
||||
'toPeriod',
|
||||
'periodTypeLabel',
|
||||
'annualInterestRate'
|
||||
];
|
||||
|
||||
/** Returns the payload to save as a new rate version (called by parent after product save) */
|
||||
get modifiedRdInterestRatePayload(): any {
|
||||
return {
|
||||
versionLabel: this._referenceVersionLabel || 'Rate Version ' + new Date().toISOString().slice(0, 10),
|
||||
remarks: this._referenceRemarks,
|
||||
slabs: this._editableSlabs.map((s: any, idx: number) => ({
|
||||
srNo: s.srNo ?? idx + 1,
|
||||
description: s.description,
|
||||
periodTypeEnum: s.periodTypeEnum ?? 1,
|
||||
fromPeriod: s.fromPeriod,
|
||||
toPeriod: s.toPeriod ?? null,
|
||||
annualInterestRate: s.annualInterestRate
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
editSlab(index: number, field: string, value: any) {
|
||||
const slabs = this._editableSlabs.map((slab: any) => ({ ...slab }));
|
||||
const current = slabs[index];
|
||||
if (!current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextValue = this.normalizeSlabValue(value);
|
||||
const oldFrom = this.normalizeSlabValue(current.fromPeriod);
|
||||
const oldTo = this.normalizeSlabValue(current.toPeriod);
|
||||
|
||||
current[field] = nextValue;
|
||||
|
||||
// Keep the first slab anchored at 0 so the chart always starts from zero.
|
||||
if (index === 0 && field === 'fromPeriod') {
|
||||
current.fromPeriod = 0;
|
||||
}
|
||||
|
||||
if (field === 'fromPeriod' && nextValue === null) {
|
||||
current.fromPeriod = oldFrom;
|
||||
}
|
||||
|
||||
if (field === 'fromPeriod') {
|
||||
const effectiveFrom = this.normalizeSlabValue(current.fromPeriod);
|
||||
const delta = effectiveFrom - oldFrom;
|
||||
|
||||
if (current.toPeriod !== null && current.toPeriod !== undefined) {
|
||||
current.toPeriod = this.normalizeSlabValue(current.toPeriod) + delta;
|
||||
}
|
||||
|
||||
this.shiftFollowingSlabs(slabs, index, delta);
|
||||
}
|
||||
|
||||
if (field === 'toPeriod') {
|
||||
if (index < slabs.length - 1 && nextValue === null) {
|
||||
// Non-final slabs should always have a closing boundary so later slabs can stay contiguous.
|
||||
current.toPeriod = oldTo;
|
||||
} else {
|
||||
const delta = (this.normalizeSlabValue(current.toPeriod) ?? 0) - (oldTo ?? 0);
|
||||
this.shiftFollowingSlabs(slabs, index, delta);
|
||||
}
|
||||
}
|
||||
|
||||
this.ensureSlabRangeRules(slabs);
|
||||
this._editableSlabs = slabs;
|
||||
}
|
||||
|
||||
addSlab() {
|
||||
const slabs = this._editableSlabs.map((slab: any) => ({ ...slab }));
|
||||
const nextSr = slabs.length + 1;
|
||||
const lastSlab = slabs[slabs.length - 1];
|
||||
const lastFrom = this.normalizeSlabValue(lastSlab?.fromPeriod) ?? 0;
|
||||
|
||||
if (lastSlab && this.normalizeSlabValue(lastSlab.toPeriod) === null) {
|
||||
// Split the open-ended tail so the new slab can become the new last row.
|
||||
// This keeps the sequence contiguous while preserving the existing tail start.
|
||||
lastSlab.toPeriod = lastFrom;
|
||||
}
|
||||
|
||||
const previousSlab = slabs[slabs.length - 1];
|
||||
this._editableSlabs = [
|
||||
...slabs,
|
||||
{
|
||||
srNo: nextSr,
|
||||
description: '',
|
||||
periodTypeEnum: 1,
|
||||
periodTypeLabel: 'Days',
|
||||
fromPeriod: slabs.length === 0 ? 0 : this.nextFromPeriod(previousSlab),
|
||||
toPeriod: null,
|
||||
annualInterestRate: null
|
||||
}
|
||||
];
|
||||
this.ensureSlabRangeRules(this._editableSlabs);
|
||||
}
|
||||
|
||||
private normalizeSlabValue(value: any): number | null {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const parsed = Number(value);
|
||||
return Number.isNaN(parsed) ? null : parsed;
|
||||
}
|
||||
|
||||
private nextFromPeriod(previousSlab: any): number {
|
||||
const previousTo = this.normalizeSlabValue(previousSlab?.toPeriod);
|
||||
const previousFrom = this.normalizeSlabValue(previousSlab?.fromPeriod) ?? 0;
|
||||
return previousTo === null ? previousFrom + 1 : previousTo + 1;
|
||||
}
|
||||
|
||||
private shiftFollowingSlabs(slabs: any[], index: number, delta: number): void {
|
||||
if (!delta) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = index + 1; i < slabs.length; i++) {
|
||||
const slab = slabs[i];
|
||||
const currentFrom = this.normalizeSlabValue(slab.fromPeriod);
|
||||
const currentTo = this.normalizeSlabValue(slab.toPeriod);
|
||||
|
||||
if (currentFrom !== null) {
|
||||
slab.fromPeriod = currentFrom + delta;
|
||||
}
|
||||
|
||||
if (currentTo !== null) {
|
||||
slab.toPeriod = currentTo + delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ensureSlabRangeRules(slabs: any[]): void {
|
||||
if (slabs.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
slabs[0].fromPeriod = 0;
|
||||
|
||||
for (let i = 1; i < slabs.length; i++) {
|
||||
const previous = slabs[i - 1];
|
||||
const previousTo = this.normalizeSlabValue(previous.toPeriod);
|
||||
if (previousTo === null) {
|
||||
slabs[i].fromPeriod = this.normalizeSlabValue(slabs[i].fromPeriod);
|
||||
continue;
|
||||
}
|
||||
|
||||
slabs[i].fromPeriod = previousTo + 1;
|
||||
}
|
||||
|
||||
// Keep the last slab open-ended so the chart ends with null.
|
||||
slabs[slabs.length - 1].toPeriod = null;
|
||||
|
||||
// Prevent inverted ranges on intermediate rows.
|
||||
for (let i = 0; i < slabs.length - 1; i++) {
|
||||
const fromPeriod = this.normalizeSlabValue(slabs[i].fromPeriod);
|
||||
const toPeriod = this.normalizeSlabValue(slabs[i].toPeriod);
|
||||
if (fromPeriod !== null && toPeriod !== null && toPeriod < fromPeriod) {
|
||||
slabs[i].toPeriod = fromPeriod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
recurringDepositProductInterestRateChartForm: UntypedFormGroup;
|
||||
|
||||
@@ -146,9 +325,9 @@ export class RecurringDepositProductInterestRateChartStepComponent implements On
|
||||
this.clientClassificationData = this.recurringDepositProductsTemplate.chartTemplate.clientClassificationOptions;
|
||||
this.incentiveTypeData = this.recurringDepositProductsTemplate.chartTemplate.incentiveTypeOptions;
|
||||
|
||||
if (!(this.recurringDepositProductsTemplate === undefined)) {
|
||||
this.assignFormData();
|
||||
}
|
||||
// assignFormData() intentionally not called — old chart form is removed from UI.
|
||||
// The recurringDepositProductInterestRateChartForm is kept only for stepControl
|
||||
// validity; it remains empty (valid) so the preview tab becomes available.
|
||||
}
|
||||
|
||||
// Assign form the values for edit recurring Depoosit
|
||||
@@ -429,17 +608,6 @@ export class RecurringDepositProductInterestRateChartStepComponent implements On
|
||||
});
|
||||
}
|
||||
|
||||
delete(formArray: UntypedFormArray, 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':
|
||||
@@ -541,6 +709,9 @@ export class RecurringDepositProductInterestRateChartStepComponent implements On
|
||||
delete chart.id;
|
||||
}
|
||||
}
|
||||
return recurringDepositProductInterestRateChart;
|
||||
return {
|
||||
...recurringDepositProductInterestRateChart,
|
||||
rdSlabs: this._editableSlabs
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+567
-437
File diff suppressed because it is too large
Load Diff
+94
-98
@@ -6,130 +6,126 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
.rdpp-section {
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
@use 'assets/styles/colours' as *;
|
||||
|
||||
& + & {
|
||||
margin-top: 16px;
|
||||
.mat-h3 {
|
||||
margin-top: 10px !important;
|
||||
font-weight: 300;
|
||||
color: $status-approved;
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px 24px;
|
||||
background: #f8f9fa;
|
||||
border-bottom: 1px solid #e8eaed;
|
||||
|
||||
i {
|
||||
font-size: 14px;
|
||||
color: #0ea5e9;
|
||||
.mat-elevation-z1 {
|
||||
margin: 1em 0 1.5em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #202124;
|
||||
}
|
||||
}
|
||||
|
||||
&__body {
|
||||
padding: 16px 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.rdpp-product-name {
|
||||
margin: 0 0 12px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #202124;
|
||||
}
|
||||
|
||||
.rdpp-row {
|
||||
display: flex;
|
||||
padding: 6px 0;
|
||||
border-bottom: 1px solid #f1f3f4;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.rdpp-label {
|
||||
flex: 0 0 40%;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #5f6368;
|
||||
}
|
||||
|
||||
.rdpp-value {
|
||||
flex: 1;
|
||||
font-size: 13px;
|
||||
color: #202124;
|
||||
}
|
||||
|
||||
.rdpp-table-wrapper {
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
margin-top: 8px;
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
& + & {
|
||||
margin-top: 12px;
|
||||
span {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
mat-divider {
|
||||
margin: 0 0 0.5em;
|
||||
}
|
||||
|
||||
/* ── GL mapping summary (same as savings product preview) ── */
|
||||
.spa-gl-summary,
|
||||
.spa-other-gl-summary {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.9rem;
|
||||
margin-top: 1.25rem;
|
||||
}
|
||||
|
||||
.spa-gl-summary__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.25rem 0 0.5rem;
|
||||
}
|
||||
|
||||
.spa-gl-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem 1.15rem;
|
||||
border: 1px solid #e3e7f2;
|
||||
border-radius: 12px;
|
||||
background: #fff;
|
||||
|
||||
h5 {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: #91a0bc;
|
||||
}
|
||||
}
|
||||
|
||||
.rdpp-sub-heading {
|
||||
margin: 16px 0 8px;
|
||||
font-size: 14px;
|
||||
.spa-gl-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(220px, 40%) 1fr;
|
||||
gap: 1rem;
|
||||
align-items: baseline;
|
||||
padding: 0.2rem 0;
|
||||
}
|
||||
|
||||
.spa-gl-row__label {
|
||||
color: #1f2f4a;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.spa-gl-row__value {
|
||||
color: #101828;
|
||||
font-weight: 600;
|
||||
color: #0ea5e9;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.rdpp-sub-heading-sm {
|
||||
margin: 12px 0 4px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #202124;
|
||||
.spa-other-gl-table {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
|
||||
&:first-of-type {
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.rdpp-chart-block {
|
||||
& + & {
|
||||
margin-top: 16px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
th.mat-mdc-header-cell,
|
||||
td.mat-mdc-cell {
|
||||
padding-left: 1.25rem;
|
||||
padding-right: 1.25rem;
|
||||
}
|
||||
|
||||
th.mat-mdc-header-cell:first-child,
|
||||
td.mat-mdc-cell:first-child {
|
||||
width: 38%;
|
||||
}
|
||||
|
||||
th.mat-mdc-header-cell:last-child,
|
||||
td.mat-mdc-cell:last-child {
|
||||
width: 62%;
|
||||
word-break: break-word;
|
||||
}
|
||||
}
|
||||
|
||||
.rdpp-incentives {
|
||||
.spa-other-gl-summary .mat-elevation-z1 {
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
|
||||
&__card {
|
||||
border-radius: 0;
|
||||
padding: 12px 16px;
|
||||
|
||||
h4 {
|
||||
margin: 0 0 8px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
tr.incentives-row {
|
||||
height: 0;
|
||||
|
||||
> td {
|
||||
padding: 0;
|
||||
}
|
||||
@media (width <= 768px) {
|
||||
.spa-gl-row {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
}
|
||||
|
||||
+34
-76
@@ -7,9 +7,9 @@
|
||||
*/
|
||||
|
||||
import { Component, OnInit, Input, Output, EventEmitter, OnChanges, SimpleChanges, inject } from '@angular/core';
|
||||
import { trigger, state, transition, animate, style } from '@angular/animations';
|
||||
import { OptionData } from 'app/shared/models/option-data.model';
|
||||
import { Accounting } from 'app/core/utils/accounting';
|
||||
import { OptionData } from 'app/shared/models/option-data.model';
|
||||
import { MatDivider } from '@angular/material/divider';
|
||||
import {
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
MatRow
|
||||
} from '@angular/material/table';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { ViewSavingsAccountingDetailsComponent } from '../../../../shared/accounting/view-savings-accounting-details/view-savings-accounting-details.component';
|
||||
import { FindPipe } from '../../../../pipes/find.pipe';
|
||||
import { DateFormatPipe } from '../../../../pipes/date-format.pipe';
|
||||
import { FormatNumberPipe } from '../../../../pipes/format-number.pipe';
|
||||
@@ -34,15 +33,9 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
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)'))
|
||||
])
|
||||
],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
MatDivider,
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
MatHeaderCellDef,
|
||||
@@ -54,7 +47,6 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
MatHeaderRow,
|
||||
MatRowDef,
|
||||
MatRow,
|
||||
ViewSavingsAccountingDetailsComponent,
|
||||
FindPipe,
|
||||
DateFormatPipe,
|
||||
FormatNumberPipe,
|
||||
@@ -65,20 +57,11 @@ export class RecurringDepositProductPreviewStepComponent implements OnInit, OnCh
|
||||
private accounting = inject(Accounting);
|
||||
|
||||
@Input() recurringDepositProductsTemplate: any;
|
||||
@Input() chartSlabsDisplayedColumns: any[];
|
||||
@Input() chartSlabsDisplayedColumns: any[] = [];
|
||||
@Input() accountingRuleData: any;
|
||||
@Input() recurringDepositProduct: any;
|
||||
@Output() submitEvent = new EventEmitter();
|
||||
|
||||
chartSlabsIncentivesDisplayedColumns: string[] = ['incentives'];
|
||||
incentivesDisplayedColumns: string[] = [
|
||||
'entityType',
|
||||
'attributeName',
|
||||
'conditionType',
|
||||
'attributeValue',
|
||||
'incentiveType',
|
||||
'amount'
|
||||
];
|
||||
chargesDisplayedColumns: string[] = [
|
||||
'name',
|
||||
'chargeCalculationType',
|
||||
@@ -94,72 +77,28 @@ export class RecurringDepositProductPreviewStepComponent implements OnInit, OnCh
|
||||
'incomeAccountId'
|
||||
];
|
||||
|
||||
expandChartSlabIndex: number[] = [];
|
||||
readonly rdSlabColumns = [
|
||||
'srNo',
|
||||
'description',
|
||||
'fromPeriod',
|
||||
'toPeriod',
|
||||
'periodTypeLabel',
|
||||
'annualInterestRate'
|
||||
];
|
||||
|
||||
accountingMappings: any = {};
|
||||
accountingRule: OptionData;
|
||||
accountingRule: OptionData | undefined;
|
||||
|
||||
ngOnInit() {
|
||||
this.setCurrentValues();
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
ngOnChanges(_changes: SimpleChanges): void {
|
||||
this.setCurrentValues();
|
||||
}
|
||||
|
||||
setCurrentValues(): void {
|
||||
if (this.isCashOrAccrualAccounting()) {
|
||||
if (this.recurringDepositProduct) {
|
||||
this.accountingRule = this.accounting.getAccountingRuleFrom(this.recurringDepositProduct.accountingRule);
|
||||
|
||||
const assetAccountData = this.recurringDepositProductsTemplate.accountingMappingOptions.assetAccountOptions || [];
|
||||
const incomeAccountData =
|
||||
this.recurringDepositProductsTemplate.accountingMappingOptions.incomeAccountOptions || [];
|
||||
const expenseAccountData =
|
||||
this.recurringDepositProductsTemplate.accountingMappingOptions.expenseAccountOptions || [];
|
||||
const liabilityAccountData =
|
||||
this.recurringDepositProductsTemplate.accountingMappingOptions.liabilityAccountOptions || [];
|
||||
|
||||
this.accountingMappings = {
|
||||
savingsReferenceAccount: this.accounting.glAccountLookUp(
|
||||
this.recurringDepositProduct.savingsReferenceAccountId,
|
||||
assetAccountData
|
||||
),
|
||||
savingsControlAccount: this.accounting.glAccountLookUp(
|
||||
this.recurringDepositProduct.savingsControlAccountId,
|
||||
liabilityAccountData
|
||||
),
|
||||
transfersInSuspenseAccount: this.accounting.glAccountLookUp(
|
||||
this.recurringDepositProduct.transfersInSuspenseAccountId,
|
||||
liabilityAccountData
|
||||
),
|
||||
interestOnSavingsAccount: this.accounting.glAccountLookUp(
|
||||
this.recurringDepositProduct.interestOnSavingsAccountId,
|
||||
expenseAccountData
|
||||
),
|
||||
incomeFromFeeAccount: this.accounting.glAccountLookUp(
|
||||
this.recurringDepositProduct.incomeFromFeeAccountId,
|
||||
incomeAccountData
|
||||
),
|
||||
incomeFromPenaltyAccount: this.accounting.glAccountLookUp(
|
||||
this.recurringDepositProduct.incomeFromPenaltyAccountId,
|
||||
incomeAccountData
|
||||
)
|
||||
};
|
||||
|
||||
if (this.isAccrualAccounting()) {
|
||||
this.accountingMappings['feeReceivableAccount'] = this.accounting.glAccountLookUp(
|
||||
this.recurringDepositProduct.feesReceivableAccountId,
|
||||
assetAccountData
|
||||
);
|
||||
this.accountingMappings['penaltyReceivableAccount'] = this.accounting.glAccountLookUp(
|
||||
this.recurringDepositProduct.penaltiesReceivableAccountId,
|
||||
assetAccountData
|
||||
);
|
||||
this.accountingMappings['interestPayableAccount'] = this.accounting.glAccountLookUp(
|
||||
this.recurringDepositProduct.interestPayableAccountId,
|
||||
liabilityAccountData
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,4 +113,23 @@ export class RecurringDepositProductPreviewStepComponent implements OnInit, OnCh
|
||||
isAccrualAccounting(): boolean {
|
||||
return this.accounting.isAccrualAccountingRuleId(this.recurringDepositProduct.accountingRule);
|
||||
}
|
||||
|
||||
getLockinPeriodFrequencyTypeLabel(value: any): string {
|
||||
if (!value && value !== 0) return '';
|
||||
const options = this.recurringDepositProductsTemplate?.lockinPeriodFrequencyTypeOptions || [];
|
||||
const found = options.find((o: any) => o.id === value);
|
||||
return found?.value || String(value);
|
||||
}
|
||||
|
||||
formatGlAccountById(glAccountId: number, options: any[]): string {
|
||||
if (!glAccountId || !Array.isArray(options)) return '-';
|
||||
const account = this.accounting.glAccountLookUp(glAccountId, options as any);
|
||||
return account ? `(${account.glCode}) ${account.name}` : '-';
|
||||
}
|
||||
|
||||
formatGlAccountByCode(glCode: string, options: any[]): string {
|
||||
if (!glCode || !Array.isArray(options)) return glCode || '-';
|
||||
const account = options.find((o: any) => o?.glCode === glCode);
|
||||
return account ? `(${account.glCode}) ${account.name}` : glCode;
|
||||
}
|
||||
}
|
||||
|
||||
+11
-2
@@ -72,7 +72,7 @@ export class RecurringDepositProductTermsStepComponent implements OnInit {
|
||||
: this.interestCalculationTypeOptionData?.[0]?.id || 1,
|
||||
interestCalculationDaysInYearType: this.recurringDepositProductsTemplate.interestCalculationDaysInYearType?.id,
|
||||
effectiveDate: this.recurringDepositProductsTemplate.effectiveDate
|
||||
? new Date(this.recurringDepositProductsTemplate.effectiveDate)
|
||||
? this.parseDate(this.recurringDepositProductsTemplate.effectiveDate)
|
||||
: '',
|
||||
interestCalculateSelector: isEditing
|
||||
? this.recurringDepositProductsTemplate.interestCalculateSelector || 1
|
||||
@@ -83,7 +83,7 @@ export class RecurringDepositProductTermsStepComponent implements OnInit {
|
||||
autoPost: isEditing ? (this.recurringDepositProductsTemplate.autoPost ?? true) : true,
|
||||
frequencyFixDay: isEditing ? this.recurringDepositProductsTemplate.frequencyFixDay || 30 : 30,
|
||||
lastInterestDate: this.recurringDepositProductsTemplate.lastInterestDate
|
||||
? new Date(this.recurringDepositProductsTemplate.lastInterestDate)
|
||||
? this.parseDate(this.recurringDepositProductsTemplate.lastInterestDate)
|
||||
: ''
|
||||
});
|
||||
|
||||
@@ -93,6 +93,15 @@ export class RecurringDepositProductTermsStepComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
private parseDate(dateValue: any): Date | null {
|
||||
if (!dateValue) return null;
|
||||
if (Array.isArray(dateValue)) {
|
||||
return new Date(dateValue[0], dateValue[1] - 1, dateValue[2]);
|
||||
}
|
||||
if (dateValue instanceof Date) return dateValue;
|
||||
return new Date(dateValue);
|
||||
}
|
||||
|
||||
private setYesNoOptions(options: any[]) {
|
||||
if (!Array.isArray(options)) return;
|
||||
const mapped = options
|
||||
|
||||
@@ -20,6 +20,18 @@
|
||||
{{ 'labels.buttons.Create Recurring Deposit Product' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="in-block">
|
||||
<button
|
||||
mat-stroked-button
|
||||
color="primary"
|
||||
[routerLink]="['/products/recurring-deposit-products/base-interest-rates']"
|
||||
matTooltip="Open the CBS base interest chart"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
<fa-icon icon="chart-line" class="m-r-10"></fa-icon>
|
||||
CBS Base Chart
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
<!--
|
||||
Copyright since 2025 Mifos Initiative
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<div class="tab-container mat-typography">
|
||||
@if (!rdInterestRates || rdInterestRates.length === 0) {
|
||||
<p class="no-rates">No CBS base interest rate versions defined.</p>
|
||||
}
|
||||
|
||||
<mat-accordion multi>
|
||||
@for (version of rdInterestRates; track version.id) {
|
||||
<mat-expansion-panel [expanded]="version.isActive">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
<span class="rd-version-header">
|
||||
<strong>{{ version.versionLabel }}</strong>
|
||||
<span [class]="version.isActive ? 'rd-active-badge' : 'rd-inactive-badge'">
|
||||
{{ version.isActive ? 'Active' : 'Inactive' }}
|
||||
</span>
|
||||
<span style="color: #757575; font-size: 13px">Effective from: {{ version.effectiveFromDate }}</span>
|
||||
</span>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
@if (version.remarks) {
|
||||
<p>
|
||||
<em>{{ version.remarks }}</em>
|
||||
</p>
|
||||
}
|
||||
|
||||
<table class="rd-slab-table mat-elevation-z1" mat-table [dataSource]="version.slabs">
|
||||
<ng-container matColumnDef="srNo">
|
||||
<th mat-header-cell *matHeaderCellDef>Sr.</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.srNo }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="description">
|
||||
<th mat-header-cell *matHeaderCellDef>Particular</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.description }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="fromPeriod">
|
||||
<th mat-header-cell *matHeaderCellDef>From</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.fromPeriod }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="toPeriod">
|
||||
<th mat-header-cell *matHeaderCellDef>To</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.toPeriod ?? '—' }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="periodTypeLabel">
|
||||
<th mat-header-cell *matHeaderCellDef>Period Type</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.periodTypeLabel }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="annualInterestRate">
|
||||
<th mat-header-cell *matHeaderCellDef>Annual Rate (%)</th>
|
||||
<td mat-cell *matCellDef="let slab">
|
||||
<strong>{{ slab.annualInterestRate | number: '1.2-2' }}</strong>
|
||||
</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="slabColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: slabColumns"></tr>
|
||||
</table>
|
||||
</mat-expansion-panel>
|
||||
}
|
||||
</mat-accordion>
|
||||
</div>
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
.rd-version-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.rd-active-badge {
|
||||
background: #4caf50;
|
||||
color: #fff;
|
||||
border-radius: 4px;
|
||||
padding: 2px 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.rd-inactive-badge {
|
||||
background: #9e9e9e;
|
||||
color: #fff;
|
||||
border-radius: 4px;
|
||||
padding: 2px 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.rd-slab-table {
|
||||
width: 100%;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.no-rates {
|
||||
padding: 24px;
|
||||
color: #757575;
|
||||
text-align: center;
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { DecimalPipe } from '@angular/common';
|
||||
import {
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
MatHeaderCellDef,
|
||||
MatHeaderCell,
|
||||
MatCellDef,
|
||||
MatCell,
|
||||
MatHeaderRowDef,
|
||||
MatHeaderRow,
|
||||
MatRowDef,
|
||||
MatRow
|
||||
} from '@angular/material/table';
|
||||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
|
||||
@Component({
|
||||
selector: 'mifosx-rd-interest-rates-tab',
|
||||
templateUrl: './rd-interest-rates-tab.component.html',
|
||||
styleUrls: ['./rd-interest-rates-tab.component.scss'],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
MatHeaderCellDef,
|
||||
MatHeaderCell,
|
||||
MatCellDef,
|
||||
MatCell,
|
||||
MatHeaderRowDef,
|
||||
MatHeaderRow,
|
||||
MatRowDef,
|
||||
MatRow,
|
||||
MatExpansionModule,
|
||||
DecimalPipe
|
||||
]
|
||||
})
|
||||
export class RdInterestRatesTabComponent {
|
||||
private route = inject(ActivatedRoute);
|
||||
|
||||
rdInterestRates: any[] = [];
|
||||
|
||||
readonly slabColumns = [
|
||||
'srNo',
|
||||
'description',
|
||||
'fromPeriod',
|
||||
'toPeriod',
|
||||
'periodTypeLabel',
|
||||
'annualInterestRate'
|
||||
];
|
||||
|
||||
constructor() {
|
||||
this.route.data.subscribe((data: { rdInterestRates: any[] }) => {
|
||||
this.rdInterestRates = data.rdInterestRates || [];
|
||||
});
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { inject } from '@angular/core';
|
||||
import { ResolveFn } from '@angular/router';
|
||||
import { ProductsService } from '../../../products.service';
|
||||
|
||||
export const rdInterestRatesResolver: ResolveFn<any[]> = (route) => {
|
||||
const productsService = inject(ProductsService);
|
||||
return productsService.getRdBaseInterestRates();
|
||||
};
|
||||
+260
-177
@@ -6,24 +6,25 @@
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<div class="layout-row align-end gap-2px responsive-column container m-t-20 m-b-20">
|
||||
<button mat-raised-button color="primary" [routerLink]="['../edit']">
|
||||
<fa-icon icon="edit" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.Edit' | translate }}
|
||||
<div class="layout-row align-end gap-2px responsive-column container m-b-20">
|
||||
<button
|
||||
mat-raised-button
|
||||
color="primary"
|
||||
class="m-t-20"
|
||||
[routerLink]="['../edit']"
|
||||
*mifosxHasPermission="'UPDATE_RECURRINGDEPOSITPRODUCT'"
|
||||
>
|
||||
<fa-icon icon="edit" class="m-r-10"></fa-icon>{{ 'labels.buttons.Edit' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<div class="responsive-column">
|
||||
<h2 class="mat-h2 flex-100">{{ recurringDepositProduct.name }}</h2>
|
||||
|
||||
<mat-divider [inset]="true"></mat-divider>
|
||||
|
||||
<!-- ===== DETAILS ===== -->
|
||||
<mat-divider class="flex-100"></mat-divider>
|
||||
<h3 class="mat-h3 flex-100">{{ 'labels.inputs.Details' | translate }}</h3>
|
||||
|
||||
<mat-divider [inset]="true"></mat-divider>
|
||||
<mat-divider class="flex-100"></mat-divider>
|
||||
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Short Name' | translate }}:</span>
|
||||
@@ -37,165 +38,301 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
<h3 class="mat-h3 flex-100">{{ 'labels.inputs.Currency' | translate }}</h3>
|
||||
<!-- Product Classification (inside Details tab) -->
|
||||
<h3 class="mat-h3 flex-100">Product Classification</h3>
|
||||
<mat-divider class="flex-100"></mat-divider>
|
||||
|
||||
<mat-divider [inset]="true"></mat-divider>
|
||||
@if (recurringDepositProduct.accountType) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Account Type:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.accountType }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (recurringDepositProduct.customerSegment) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Customer Segment:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.customerSegment }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (recurringDepositProduct.productCode) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Product Code:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.productCode }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (recurringDepositProduct.effectiveTo) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Effective To:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.effectiveTo | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (recurringDepositProduct.endDate) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">End Date:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.endDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Introducer Required:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.introducerRequired | yesNo }}</span>
|
||||
</div>
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Share Linking Enabled:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.shareLinkingEnabled | yesNo }}</span>
|
||||
</div>
|
||||
|
||||
<!-- ===== CURRENCY ===== -->
|
||||
<h3 class="mat-h3 flex-100">{{ 'labels.inputs.Currency' | translate }}</h3>
|
||||
<mat-divider class="flex-100"></mat-divider>
|
||||
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.heading.Currency' | translate }}:</span>
|
||||
<span class="flex-40">{{ 'labels.inputs.Currency' | translate }}:</span>
|
||||
<span class="flex-60"
|
||||
>({{ recurringDepositProduct.currency.code }}) {{ recurringDepositProduct.currency.name }}</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Decimal Places' | translate }}:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.currency.decimalPlaces }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Currency in multiples of' | translate }}:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.currency.inMultiplesOf | formatNumber }}</span>
|
||||
<span class="flex-40">Dormancy Alert:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.dormancyAlert | yesNo }}</span>
|
||||
</div>
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Balance Swap Option:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.balanceSwapOption | yesNo }}</span>
|
||||
</div>
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">MSME:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.msme | yesNo }}</span>
|
||||
</div>
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Nomination Facility:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.nominationFacility | yesNo }}</span>
|
||||
</div>
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Auto Debit Facility:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.autoDebitFacility | yesNo }}</span>
|
||||
</div>
|
||||
|
||||
<h3 class="mat-h3 flex-100">{{ 'labels.heading.Terms' | translate }}</h3>
|
||||
|
||||
<mat-divider [inset]="true"></mat-divider>
|
||||
<!-- ===== TERMS ===== -->
|
||||
<h3 class="mat-h3 flex-100">{{ 'labels.inputs.Terms' | translate }}</h3>
|
||||
<mat-divider class="flex-100"></mat-divider>
|
||||
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Deposit Amount' | translate }}:</span>
|
||||
<span class="flex-60">{{
|
||||
recurringDepositProduct.depositAmount +
|
||||
' (Min: ' +
|
||||
(recurringDepositProduct.minDepositAmount
|
||||
? recurringDepositProduct.minDepositAmount
|
||||
: recurringDepositProduct.depositAmount) +
|
||||
(recurringDepositProduct.minDepositAmount || recurringDepositProduct.depositAmount) +
|
||||
', Max: ' +
|
||||
(recurringDepositProduct.maxDepositAmount
|
||||
? recurringDepositProduct.maxDepositAmount
|
||||
: recurringDepositProduct.depositAmount) +
|
||||
(recurringDepositProduct.maxDepositAmount || recurringDepositProduct.depositAmount) +
|
||||
')'
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Interest Compounding Period' | translate }}:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.interestCompoundingPeriodType.value }}</span>
|
||||
<span class="flex-40">{{ 'labels.inputs.Nominal Annual Interest' | translate }}:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.nominalAnnualInterestRate | formatNumber }} %</span>
|
||||
</div>
|
||||
|
||||
@if (recurringDepositProduct.effectiveDate) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Effective Date:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.effectiveDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Interest Calculation Type' | translate }}:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.interestCalculationType.value }}</span>
|
||||
</div>
|
||||
@if (recurringDepositProduct.interestCalculateSelector !== undefined) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Interest Calculate:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.interestCalculateSelector }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (recurringDepositProduct.averagePeriod) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Average Period:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.averagePeriod }}</span>
|
||||
</div>
|
||||
}
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Interest Posting Period' | translate }}:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.interestPostingPeriodType.value }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Interest Calculated using' | translate }}:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.interestCalculationType.value }}</span>
|
||||
<span class="flex-40">{{ 'labels.inputs.Interest Compounding Period' | translate }}:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.interestCompoundingPeriodType.value }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Days in Year' | translate }}:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.interestCalculationDaysInYearType.value }}</span>
|
||||
</div>
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Auto Post:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.autoPost | yesNo }}</span>
|
||||
</div>
|
||||
@if (recurringDepositProduct.frequencyFixDay) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Frequency (Fix Day):</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.frequencyFixDay }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (recurringDepositProduct.lastInterestDate) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Last Interest Date:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.lastInterestDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<h3 class="mat-h3 flex-100">{{ 'labels.heading.Settings' | translate }}</h3>
|
||||
<!-- ===== SETTINGS ===== -->
|
||||
<h3 class="mat-h3 flex-100">{{ 'labels.inputs.Settings' | translate }}</h3>
|
||||
<mat-divider class="flex-100"></mat-divider>
|
||||
|
||||
<mat-divider [inset]="true"></mat-divider>
|
||||
<!-- Premature option -->
|
||||
<h4 class="mat-h4 flex-100">Premature Option</h4>
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Loan Facility:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.loanFacility | yesNo }}</span>
|
||||
</div>
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Premature Withdrawal Option:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.prematureWithdrawalOption | yesNo }}</span>
|
||||
</div>
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Tenure Flexibility:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.tenureFlexibility | yesNo }}</span>
|
||||
</div>
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Lien Marked:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.lienMarked | yesNo }}</span>
|
||||
</div>
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Apply Withdrawal Fee for Transfers' | translate }}:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.withdrawalFeeForTransfers | yesNo }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Lock-in Period -->
|
||||
<h4 class="mat-h4 flex-100">{{ 'labels.inputs.Lock-in Period' | translate }}</h4>
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Enable Lock-in Period:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.enableLockinPeriod | yesNo }}</span>
|
||||
</div>
|
||||
@if (recurringDepositProduct.lockinPeriodFrequency) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Lock-in Period' | translate }}:</span>
|
||||
@if (recurringDepositProduct.lockinPeriodFrequency !== undefined) {
|
||||
<span class="flex-60">
|
||||
{{ recurringDepositProduct.lockinPeriodFrequency }}
|
||||
{{ recurringDepositProduct.lockinPeriodFrequencyType.value }}</span
|
||||
<span class="flex-60"
|
||||
>{{ recurringDepositProduct.lockinPeriodFrequency }}
|
||||
{{ recurringDepositProduct.lockinPeriodFrequencyType?.value }}</span
|
||||
>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Maturity Instructions -->
|
||||
<h4 class="mat-h4 flex-100">Maturity Instructions</h4>
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Maturity Instructions:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.maturityInstructions | yesNo }}</span>
|
||||
</div>
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Credit Maturity Proceeds:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.creditMaturityProceeds | yesNo }}</span>
|
||||
</div>
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Penalty Grace Period (Missed Installments):</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.penaltyGracePeriodMissedInstallments | yesNo }}</span>
|
||||
</div>
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Panel Request Applicable:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.panelRequestApplicable | yesNo }}</span>
|
||||
</div>
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">No Penalty (Nominee / Heir Closure):</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.noPenaltyNomineeHeirClosure | yesNo }}</span>
|
||||
</div>
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Deceased Settlement:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.deceasedSettlement | yesNo }}</span>
|
||||
</div>
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Allow Debit Txn After Due Date:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.allowDebitTxnAfterDueDate | yesNo }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Additional Rules -->
|
||||
<h4 class="mat-h4 flex-100">Additional Rules</h4>
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Account Closed Zero Balance in Day Closing:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.accountClosedZeroBalanceInDayClosing | yesNo }}</span>
|
||||
</div>
|
||||
@if (recurringDepositProduct.minBalanceForInterestCalculation) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Balance Required for Interest Calculation' | translate }}:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.minBalanceForInterestCalculation | formatNumber }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (recurringDepositProduct.minDepositTerm) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Minimum Deposit Term' | translate }}:</span>
|
||||
@if (recurringDepositProduct.minDepositTerm !== undefined) {
|
||||
<span class="flex-60"
|
||||
>{{ recurringDepositProduct.minDepositTerm }}
|
||||
|
||||
{{ recurringDepositProduct.minDepositTermType.value }}</span
|
||||
>{{ recurringDepositProduct.minDepositTerm }} {{ recurringDepositProduct.minDepositTermType?.value }}</span
|
||||
>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (recurringDepositProduct.maxDepositTerm) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Maximum Deposit Term' | translate }}:</span>
|
||||
@if (recurringDepositProduct.maxDepositTerm !== undefined) {
|
||||
<span class="flex-60"
|
||||
>{{ recurringDepositProduct.maxDepositTerm }}
|
||||
|
||||
{{ recurringDepositProduct.maxDepositTermType.value }}</span
|
||||
>{{ recurringDepositProduct.maxDepositTerm }} {{ recurringDepositProduct.maxDepositTermType?.value }}</span
|
||||
>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (recurringDepositProduct.inMultipleOfDepositTerm) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Deposit Term In multiple of' | translate }}:</span>
|
||||
@if (recurringDepositProduct.inMultipleOfDepositTerm !== undefined) {
|
||||
<span class="flex-60"
|
||||
>{{ recurringDepositProduct.inMultipleOfDepositTerm }}
|
||||
-
|
||||
{{ recurringDepositProduct.inMultipleOfDepositTermType.value }}</span
|
||||
>{{ recurringDepositProduct.inMultipleOfDepositTerm }} -
|
||||
{{ recurringDepositProduct.inMultipleOfDepositTermType?.value }}</span
|
||||
>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (recurringDepositProduct.isMandatoryDeposit !== undefined) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Recurring Deposit Type' | translate }}:</span>
|
||||
@if (recurringDepositProduct.isMandatoryDeposit) {
|
||||
<span class="flex-60">
|
||||
{{ 'labels.text.Mandatory' | translate }}
|
||||
</span>
|
||||
}
|
||||
@if (!recurringDepositProduct.isMandatoryDeposit) {
|
||||
<span class="flex-60">
|
||||
{{ 'labels.text.Voluntary' | translate }}
|
||||
</span>
|
||||
}
|
||||
<span class="flex-60">{{
|
||||
recurringDepositProduct.isMandatoryDeposit
|
||||
? ('labels.text.Mandatory' | translate)
|
||||
: ('labels.text.Voluntary' | translate)
|
||||
}}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (recurringDepositProduct.preClosurePenalInterest) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Apply penal interest (less)' | translate }}:</span>
|
||||
<span class="flex-60"
|
||||
><span class="m-r-5">{{ recurringDepositProduct.preClosurePenalInterest }}</span
|
||||
>%<span class="m-r-5">On</span>
|
||||
{{ recurringDepositProduct.preClosurePenalInterestOnType.value }}
|
||||
</span>
|
||||
>{{ recurringDepositProduct.preClosurePenalInterest }}% On
|
||||
{{ recurringDepositProduct.preClosurePenalInterestOnType?.value }}</span
|
||||
>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Withhold Tax is Applicable' | translate }}:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.withHoldTax | yesNo }}</span>
|
||||
</div>
|
||||
|
||||
@if (recurringDepositProduct.withHoldTax) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Withhold Tax group' | translate }}:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.taxGroup.name }}</span>
|
||||
<span class="flex-40">{{ 'labels.inputs.Tax Group' | translate }}:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.taxGroup?.name }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ===== INTEREST RATE CHART ===== -->
|
||||
@if (recurringDepositProduct.activeChart) {
|
||||
<div class="layout-row-wrap responsive-column flex-100">
|
||||
<h3 class="mat-h3 flex-100">{{ 'labels.heading.Interest Rate Charts' | translate }}</h3>
|
||||
<mat-divider [inset]="true"></mat-divider>
|
||||
<mat-divider class="flex-100"></mat-divider>
|
||||
<div class="layout-row-wrap responsive-column flex-100">
|
||||
@if (recurringDepositProduct.activeChart.name) {
|
||||
<div class="flex-100 layout-row">
|
||||
@@ -253,15 +390,11 @@
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="annualInterestRate">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Interest' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let chartSlab">
|
||||
{{ chartSlab.annualInterestRate }}
|
||||
</td>
|
||||
<td mat-cell *matCellDef="let chartSlab">{{ chartSlab.annualInterestRate }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="description">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Description' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let chartSlab">
|
||||
{{ chartSlab.description }}
|
||||
</td>
|
||||
<td mat-cell *matCellDef="let chartSlab">{{ chartSlab.description }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Actions' | translate }}</th>
|
||||
@@ -272,16 +405,16 @@
|
||||
(click)="expandChartSlabIndex = expandChartSlabIndex === chartSlabIndex ? null : chartSlabIndex"
|
||||
>
|
||||
@if (expandChartSlabIndex !== chartSlabIndex) {
|
||||
<span>
|
||||
<fa-icon icon="eye" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.View Incentives' | translate }}
|
||||
</span>
|
||||
<span
|
||||
><fa-icon icon="eye" class="m-r-10"></fa-icon
|
||||
>{{ 'labels.buttons.View Incentives' | translate }}</span
|
||||
>
|
||||
}
|
||||
@if (expandChartSlabIndex === chartSlabIndex) {
|
||||
<span>
|
||||
<fa-icon icon="eye-slash" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.Hide Incentives' | translate }}
|
||||
</span>
|
||||
<span
|
||||
><fa-icon icon="eye-slash" class="m-r-10"></fa-icon
|
||||
>{{ 'labels.buttons.Hide Incentives' | translate }}</span
|
||||
>
|
||||
}
|
||||
</button>
|
||||
</td>
|
||||
@@ -297,19 +430,14 @@
|
||||
[@expandChartSlab]="chartSlabIndex === expandChartSlabIndex ? 'expanded' : 'collapsed'"
|
||||
>
|
||||
<mat-card class="layout-row-wrap flex-100">
|
||||
<h4 class="mat-h4 flex-23">
|
||||
{{ 'labels.heading.Incentives' | translate }}
|
||||
</h4>
|
||||
<h4 class="mat-h4 flex-23">{{ 'labels.heading.Incentives' | translate }}</h4>
|
||||
<table class="flex-100 mat-elevation-z1" mat-table [dataSource]="chartSlab.incentives">
|
||||
<ng-container matColumnDef="entityType">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Entity Type' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let incentive">
|
||||
{{
|
||||
incentive.entityType
|
||||
| find
|
||||
: recurringDepositProductTemplate.chartTemplate.entityTypeOptions
|
||||
: 'id'
|
||||
: 'value'
|
||||
| find: recurringDepositProductTemplate.chartTemplate.entityTypeOptions : 'id' : 'value'
|
||||
}}
|
||||
</td>
|
||||
</ng-container>
|
||||
@@ -318,10 +446,7 @@
|
||||
<td mat-cell *matCellDef="let incentive">
|
||||
{{
|
||||
incentive.attributeName
|
||||
| find
|
||||
: recurringDepositProductTemplate.chartTemplate.attributeNameOptions
|
||||
: 'id'
|
||||
: 'value'
|
||||
| find: recurringDepositProductTemplate.chartTemplate.attributeNameOptions : 'id' : 'value'
|
||||
}}
|
||||
</td>
|
||||
</ng-container>
|
||||
@@ -330,10 +455,7 @@
|
||||
<td mat-cell *matCellDef="let incentive">
|
||||
{{
|
||||
incentive.conditionType
|
||||
| find
|
||||
: recurringDepositProductTemplate.chartTemplate.conditionTypeOptions
|
||||
: 'id'
|
||||
: 'value'
|
||||
| find: recurringDepositProductTemplate.chartTemplate.conditionTypeOptions : 'id' : 'value'
|
||||
}}
|
||||
</td>
|
||||
</ng-container>
|
||||
@@ -344,10 +466,7 @@
|
||||
@case (2) {
|
||||
<span>{{
|
||||
incentive.attributeValue
|
||||
| find
|
||||
: recurringDepositProductTemplate.chartTemplate.genderOptions
|
||||
: 'id'
|
||||
: 'name'
|
||||
| find: recurringDepositProductTemplate.chartTemplate.genderOptions : 'id' : 'name'
|
||||
}}</span>
|
||||
}
|
||||
@case (3) {
|
||||
@@ -356,10 +475,7 @@
|
||||
@case (4) {
|
||||
<span>{{
|
||||
incentive.attributeValue
|
||||
| find
|
||||
: recurringDepositProductTemplate.chartTemplate.clientTypeOptions
|
||||
: 'id'
|
||||
: 'name'
|
||||
| find: recurringDepositProductTemplate.chartTemplate.clientTypeOptions : 'id' : 'name'
|
||||
}}</span>
|
||||
}
|
||||
@case (5) {
|
||||
@@ -379,18 +495,13 @@
|
||||
<td mat-cell *matCellDef="let incentive">
|
||||
{{
|
||||
incentive.incentiveType
|
||||
| find
|
||||
: recurringDepositProductTemplate.chartTemplate.incentiveTypeOptions
|
||||
: 'id'
|
||||
: 'value'
|
||||
| find: recurringDepositProductTemplate.chartTemplate.incentiveTypeOptions : 'id' : 'value'
|
||||
}}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="amount">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Amount' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let incentive">
|
||||
{{ incentive.amount }}
|
||||
</td>
|
||||
<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>
|
||||
@@ -401,45 +512,34 @@
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="chartSlabsDisplayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: chartSlabsDisplayedColumns"></tr>
|
||||
<tr
|
||||
mat-row
|
||||
*matRowDef="let row; columns: chartSlabsIncentivesDisplayedColumns"
|
||||
class="incentives-row"
|
||||
></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: chartSlabsIncentivesDisplayedColumns" class="incentives-row"></tr>
|
||||
</table>
|
||||
<mat-divider [inset]="true"></mat-divider>
|
||||
<mat-divider class="flex-100"></mat-divider>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ===== CHARGES ===== -->
|
||||
@if (recurringDepositProduct.charges.length) {
|
||||
<div class="layout-row-wrap responsive-column flex-100">
|
||||
<h3 class="mat-h3 flex-100">{{ 'labels.heading.Charges' | translate }}</h3>
|
||||
<mat-divider [inset]="true"></mat-divider>
|
||||
<mat-divider class="flex-100"></mat-divider>
|
||||
<table class="flex-100 mat-elevation-z1" mat-table [dataSource]="recurringDepositProduct.charges">
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Name' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
{{ charge.name }}
|
||||
</td>
|
||||
<td mat-cell *matCellDef="let charge">{{ charge.name }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="type">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Type' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
{{ charge.chargeCalculationType.value }}
|
||||
</td>
|
||||
<td mat-cell *matCellDef="let charge">{{ charge.chargeCalculationType.value }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="amount">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Amount' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
{{ charge.amount | formatNumber }}
|
||||
</td>
|
||||
<td mat-cell *matCellDef="let charge">{{ charge.amount | formatNumber }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="collectedon">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Collected On' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
{{ charge.chargeTimeType.value }}
|
||||
</td>
|
||||
<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>
|
||||
@@ -447,12 +547,12 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ===== ACCOUNTING ===== -->
|
||||
<h3 class="mat-h3 flex-100">{{ 'labels.heading.Accounting' | translate }}</h3>
|
||||
|
||||
<mat-divider [inset]="true"></mat-divider>
|
||||
<mat-divider class="flex-100"></mat-divider>
|
||||
|
||||
<mifosx-view-savings-accounting-details
|
||||
class="container"
|
||||
class="flex-100"
|
||||
[accountingRule]="recurringDepositProduct.accountingRule"
|
||||
[accountingMappings]="recurringDepositProduct.accountingMappings"
|
||||
>
|
||||
@@ -463,14 +563,12 @@
|
||||
recurringDepositProduct.feeToIncomeAccountMappings?.length ||
|
||||
recurringDepositProduct.penaltyToIncomeAccountMappings?.length
|
||||
) {
|
||||
<div class="flex-100 layout-row-wrap layout-column.lt-md">
|
||||
<div class="flex-100 layout-row-wrap responsive-column">
|
||||
<h3 class="mat-h3 flex-100">{{ 'labels.inputs.Advanced Accounting Rules' | translate }}</h3>
|
||||
<mat-divider [inset]="true"></mat-divider>
|
||||
<mat-divider class="flex-100"></mat-divider>
|
||||
@if (recurringDepositProduct.paymentChannelToFundSourceMappings?.length) {
|
||||
<div class="flex-100 layout-row-wrap layout-xs-column">
|
||||
<h4 class="mat-h4 flex-100">
|
||||
{{ 'labels.inputs.Configure Fund Sources for Payment Channels' | translate }}
|
||||
</h4>
|
||||
<div class="flex-100 layout-row-wrap responsive-column">
|
||||
<h4 class="mat-h4 flex-100">{{ 'labels.inputs.Fund Sources for Payment Channels' | translate }}</h4>
|
||||
<table
|
||||
class="mat-elevation-z1 flex-100"
|
||||
mat-table
|
||||
@@ -478,15 +576,11 @@
|
||||
>
|
||||
<ng-container matColumnDef="paymentTypeId">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Payment Type' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let paymentFundSource">
|
||||
{{ paymentFundSource.paymentType.name }}
|
||||
</td>
|
||||
<td mat-cell *matCellDef="let paymentFundSource">{{ paymentFundSource.paymentType.name }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="fundSourceAccountId">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Fund Source' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let paymentFundSource">
|
||||
{{ paymentFundSource.fundSourceAccount.name }}
|
||||
</td>
|
||||
<td mat-cell *matCellDef="let paymentFundSource">{{ paymentFundSource.fundSourceAccount.name }}</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="paymentFundSourceDisplayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: paymentFundSourceDisplayedColumns"></tr>
|
||||
@@ -494,24 +588,20 @@
|
||||
</div>
|
||||
}
|
||||
@if (recurringDepositProduct.feeToIncomeAccountMappings?.length) {
|
||||
<div class="flex-100 layout-row-wrap layout-xs-column">
|
||||
<div class="flex-100 layout-row-wrap responsive-column">
|
||||
<h4 class="mat-h4 flex-100">{{ 'labels.inputs.Fees to Specific Income Accounts' | translate }}</h4>
|
||||
<table
|
||||
class="mat-elevation-z1 flex-100"
|
||||
class="flex-100 mat-elevation-z1"
|
||||
mat-table
|
||||
[dataSource]="recurringDepositProduct.feeToIncomeAccountMappings"
|
||||
>
|
||||
<ng-container matColumnDef="chargeId">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Fees' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let feesIncome">
|
||||
{{ feesIncome.charge.name }}
|
||||
</td>
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Fee/Charge' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let feesIncome">{{ feesIncome.charge.name }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="incomeAccountId">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Income Account' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let feesIncome">
|
||||
{{ feesIncome.incomeAccount.name }}
|
||||
</td>
|
||||
<td mat-cell *matCellDef="let feesIncome">{{ feesIncome.incomeAccount.name }}</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="feesPenaltyIncomeDisplayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: feesPenaltyIncomeDisplayedColumns"></tr>
|
||||
@@ -519,24 +609,20 @@
|
||||
</div>
|
||||
}
|
||||
@if (recurringDepositProduct.penaltyToIncomeAccountMappings?.length) {
|
||||
<div class="flex-100 layout-row-wrap layout-xs-column">
|
||||
<div class="flex-100 layout-row-wrap responsive-column">
|
||||
<h4 class="mat-h4 flex-100">{{ 'labels.inputs.Penalties to Specific Income Accounts' | translate }}</h4>
|
||||
<table
|
||||
class="mat-elevation-z1 flex-100"
|
||||
class="flex-100 mat-elevation-z1"
|
||||
mat-table
|
||||
[dataSource]="recurringDepositProduct.penaltyToIncomeAccountMappings"
|
||||
>
|
||||
<ng-container matColumnDef="chargeId">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Penalty' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let penaltyIncome">
|
||||
{{ penaltyIncome.charge.name }}
|
||||
</td>
|
||||
<td mat-cell *matCellDef="let penaltyIncome">{{ penaltyIncome.charge.name }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="incomeAccountId">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Income Account' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let penaltyIncome">
|
||||
{{ penaltyIncome.incomeAccount.name }}
|
||||
</td>
|
||||
<td mat-cell *matCellDef="let penaltyIncome">{{ penaltyIncome.incomeAccount.name }}</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="feesPenaltyIncomeDisplayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: feesPenaltyIncomeDisplayedColumns"></tr>
|
||||
@@ -546,6 +632,3 @@
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
||||
+1
@@ -68,6 +68,7 @@ export class RecurringDepositGeneralTabComponent {
|
||||
|
||||
recurringDepositProduct: any;
|
||||
recurringDepositProductTemplate: any;
|
||||
expandChartSlabIndex: number | null = null;
|
||||
|
||||
chartSlabsIncentivesDisplayedColumns: string[] = ['incentives'];
|
||||
chartSlabsDisplayedColumns: string[] = [
|
||||
|
||||
+9
@@ -18,6 +18,15 @@
|
||||
>
|
||||
{{ 'labels.heading.General' | translate }}
|
||||
</a>
|
||||
<a
|
||||
mat-tab-link
|
||||
[routerLink]="['./interest-rates']"
|
||||
routerLinkActive
|
||||
#interestRates="routerLinkActive"
|
||||
[active]="interestRates.isActive"
|
||||
>
|
||||
CBS Base Chart
|
||||
</a>
|
||||
@for (recurringDepositDatatable of recurringDepositDatatables; track recurringDepositDatatable) {
|
||||
<a
|
||||
mat-tab-link
|
||||
|
||||
+1
@@ -20,6 +20,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
MatTabNav,
|
||||
MatTabLink,
|
||||
RouterLinkActive,
|
||||
RouterLink,
|
||||
MatTabNavPanel,
|
||||
RouterOutlet
|
||||
]
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
// IMPORTANT: THIS FILE IS AUTO GENERATED! DO NOT MANUALLY EDIT OR CHECKIN!
|
||||
/* tslint:disable */
|
||||
export default {
|
||||
'mifos_x': {
|
||||
'version': '260415',
|
||||
'hash': '4679c6f7'
|
||||
'version': '260420',
|
||||
'hash': 'fe61c925'
|
||||
},
|
||||
'allow_switching_backend_instance': true
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user