diff --git a/src/app/deposits/recurring-deposits/create-recurring-deposits-account/create-recurring-deposits-account.component.html b/src/app/deposits/recurring-deposits/create-recurring-deposits-account/create-recurring-deposits-account.component.html index 8b257b561..95803a7d0 100644 --- a/src/app/deposits/recurring-deposits/create-recurring-deposits-account/create-recurring-deposits-account.component.html +++ b/src/app/deposits/recurring-deposits/create-recurring-deposits-account/create-recurring-deposits-account.component.html @@ -70,6 +70,7 @@ diff --git a/src/app/deposits/recurring-deposits/create-recurring-deposits-account/create-recurring-deposits-account.component.ts b/src/app/deposits/recurring-deposits/create-recurring-deposits-account/create-recurring-deposits-account.component.ts index 4d459251f..0b5649391 100644 --- a/src/app/deposits/recurring-deposits/create-recurring-deposits-account/create-recurring-deposits-account.component.ts +++ b/src/app/deposits/recurring-deposits/create-recurring-deposits-account/create-recurring-deposits-account.component.ts @@ -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 */ diff --git a/src/app/deposits/recurring-deposits/edit-recurring-deposit-account/edit-recurring-deposit-account.component.html b/src/app/deposits/recurring-deposits/edit-recurring-deposit-account/edit-recurring-deposit-account.component.html index 0ac77d4cb..e44e07f33 100644 --- a/src/app/deposits/recurring-deposits/edit-recurring-deposit-account/edit-recurring-deposit-account.component.html +++ b/src/app/deposits/recurring-deposits/edit-recurring-deposit-account/edit-recurring-deposit-account.component.html @@ -71,6 +71,7 @@ diff --git a/src/app/deposits/recurring-deposits/edit-recurring-deposit-account/edit-recurring-deposit-account.component.ts b/src/app/deposits/recurring-deposits/edit-recurring-deposit-account/edit-recurring-deposit-account.component.ts index ffb6c9821..278e8e501 100644 --- a/src/app/deposits/recurring-deposits/edit-recurring-deposit-account/edit-recurring-deposit-account.component.ts +++ b/src/app/deposits/recurring-deposits/edit-recurring-deposit-account/edit-recurring-deposit-account.component.ts @@ -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 */ diff --git a/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-interest-rate-chart-step/recurring-deposits-account-interest-rate-chart-step.component.html b/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-interest-rate-chart-step/recurring-deposits-account-interest-rate-chart-step.component.html index 8c5d75118..b4d0cf3a0 100644 --- a/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-interest-rate-chart-step/recurring-deposits-account-interest-rate-chart-step.component.html +++ b/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-interest-rate-chart-step/recurring-deposits-account-interest-rate-chart-step.component.html @@ -7,34 +7,84 @@ -->
-
- {{ 'labels.inputs.name' | translate }}: - {{ recurringDepositsAccountProductTemplate.accountChart.name }} -
+ + @if (_activeRdVersion) { +
+ {{ _activeRdVersion.versionLabel }} + Active + Effective from: {{ _activeRdVersion.effectiveFromDate }} +
-
- {{ 'labels.inputs.Valid from Date' | translate }}: - {{ recurringDepositsAccountProductTemplate?.accountChart.fromDate | dateFormat }} -
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sr.{{ slab.srNo }}Particular{{ slab.description }}From{{ slab.fromPeriod }}To{{ slab.toPeriod ?? '—' }}Period Type{{ slab.periodTypeLabel }}Annual Rate (%) + {{ slab.annualInterestRate | number: '1.2-2' }} +
+ } -
- {{ 'labels.inputs.End Date' | translate }}: - {{ recurringDepositsAccountProductTemplate?.accountChart.endDate | dateFormat }} -
+ + @if (!_activeRdVersion) { +
+ {{ 'labels.inputs.name' | translate }}: + {{ recurringDepositsAccountProductTemplate.accountChart.name }} +
-
- {{ 'labels.inputs.Description' | translate }}: - {{ recurringDepositsAccountProductTemplate?.accountChart.description }} -
+
+ {{ 'labels.inputs.Valid from Date' | translate }}: + {{ recurringDepositsAccountProductTemplate?.accountChart.fromDate | dateFormat }} +
-
- {{ 'labels.inputs.Primary Grouping by Amount' | translate }}: - {{ - recurringDepositsAccountProductTemplate?.accountChart.isPrimaryGroupingByAmount | yesNo - }} -
+
+ {{ 'labels.inputs.End Date' | translate }}: + {{ recurringDepositsAccountProductTemplate?.accountChart.endDate | dateFormat }} +
- +
+ {{ 'labels.inputs.Description' | translate }}: + {{ recurringDepositsAccountProductTemplate?.accountChart.description }} +
+ +
+ {{ 'labels.inputs.Primary Grouping by Amount' | translate }}: + {{ + recurringDepositsAccountProductTemplate?.accountChart.isPrimaryGroupingByAmount | yesNo + }} +
+ } + +
{{ 'labels.inputs.Period' | translate }} diff --git a/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-interest-rate-chart-step/recurring-deposits-account-interest-rate-chart-step.component.ts b/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-interest-rate-chart-step/recurring-deposits-account-interest-rate-chart-step.component.ts index fdf29b5f3..d194eeb9a 100644 --- a/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-interest-rate-chart-step/recurring-deposits-account-interest-rate-chart-step.component.ts +++ b/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-interest-rate-chart-step/recurring-deposits-account-interest-rate-chart-step.component.ts @@ -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; diff --git a/src/app/products/products-routing.module.ts b/src/app/products/products-routing.module.ts index 0c49649fd..c6cb43e70 100644 --- a/src/app/products/products-routing.module.ts +++ b/src/app/products/products-routing.module.ts @@ -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 }, diff --git a/src/app/products/products.module.ts b/src/app/products/products.module.ts index 27675d51e..d98b4d3a9 100644 --- a/src/app/products/products.module.ts +++ b/src/app/products/products.module.ts @@ -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, diff --git a/src/app/products/products.service.ts b/src/app/products/products.service.ts index 97daf2e91..82e0f2035 100644 --- a/src/app/products/products.service.ts +++ b/src/app/products/products.service.ts @@ -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 { + return this.getRdBaseInterestRates(); + } + + /** Returns the active CBS base interest-rate version(s). */ + getRdBaseInterestRates(): Observable { + return this.http.get('/recurringdepositproducts/baseinterestrates'); + } + + /** Returns the full CBS base chart history. */ + getRdBaseInterestRateHistory(): Observable { + return this.http.get('/recurringdepositproducts/baseinterestrates/history'); + } + + /** Saves a new CBS base interest-rate version. */ + saveRdBaseInterestRates(payload: any): Observable { + return this.http.post('/recurringdepositproducts/baseinterestrates', payload); + } + /** * @returns {Observable} Collateral data. */ diff --git a/src/app/products/recurring-deposit-products/base-recurring-deposit-interest-rate-chart/base-recurring-deposit-interest-rate-chart.component.html b/src/app/products/recurring-deposit-products/base-recurring-deposit-interest-rate-chart/base-recurring-deposit-interest-rate-chart.component.html new file mode 100644 index 000000000..f8cdf923e --- /dev/null +++ b/src/app/products/recurring-deposit-products/base-recurring-deposit-interest-rate-chart/base-recurring-deposit-interest-rate-chart.component.html @@ -0,0 +1,119 @@ + + +
+ + + @if (recurringDepositProductsTemplate?.chartTemplate) { + + + +
+ + @if (saveMessage) { + {{ saveMessage }} + } +
+ } @else { +

Loading CBS base chart...

+ } + +
+
+
+

Chart History

+

Previous CBS base chart versions are listed here for comparison and audit.

+
+
+ + @if (isHistoryLoading) { +

Loading history...

+ } @else if (rdInterestRateHistory.length === 0) { +

No history found for the CBS base chart.

+ } @else { + + @for (version of rdInterestRateHistory; track version.id) { + + + + + {{ version.versionLabel }} + + {{ version.isActive ? 'Active' : 'Inactive' }} + + + + + From {{ version.effectiveFromDate }} + To {{ version.effectiveToDate || 'Current' }} + + + + @if (version.remarks) { +

{{ version.remarks }}

+ } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sr.{{ slab.srNo }}Description{{ slab.description }}From{{ slab.fromPeriod }}To + {{ slab.toPeriod !== null && slab.toPeriod !== undefined ? slab.toPeriod : 'N/A' }} + Period Type{{ slab.periodTypeLabel }}Annual Rate (%){{ slab.annualInterestRate | number: '1.2-2' }}
+
+ } +
+ } +
+
diff --git a/src/app/products/recurring-deposit-products/base-recurring-deposit-interest-rate-chart/base-recurring-deposit-interest-rate-chart.component.scss b/src/app/products/recurring-deposit-products/base-recurring-deposit-interest-rate-chart/base-recurring-deposit-interest-rate-chart.component.scss new file mode 100644 index 000000000..00de402dc --- /dev/null +++ b/src/app/products/recurring-deposit-products/base-recurring-deposit-interest-rate-chart/base-recurring-deposit-interest-rate-chart.component.scss @@ -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%; +} diff --git a/src/app/products/recurring-deposit-products/base-recurring-deposit-interest-rate-chart/base-recurring-deposit-interest-rate-chart.component.ts b/src/app/products/recurring-deposit-products/base-recurring-deposit-interest-rate-chart/base-recurring-deposit-interest-rate-chart.component.ts new file mode 100644 index 000000000..811810cfc --- /dev/null +++ b/src/app/products/recurring-deposit-products/base-recurring-deposit-interest-rate-chart/base-recurring-deposit-interest-rate-chart.component.ts @@ -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; + } + }); + } +} diff --git a/src/app/products/recurring-deposit-products/create-recurring-deposit-product/create-recurring-deposit-product.component.html b/src/app/products/recurring-deposit-products/create-recurring-deposit-product/create-recurring-deposit-product.component.html index b0882ab54..3b01d9081 100644 --- a/src/app/products/recurring-deposit-products/create-recurring-deposit-product/create-recurring-deposit-product.component.html +++ b/src/app/products/recurring-deposit-products/create-recurring-deposit-product/create-recurring-deposit-product.component.html @@ -69,12 +69,60 @@ - {{ 'labels.inputs.INTEREST RATE CHART' | translate }} + CBS Base Chart
- + @if (activeBaseVersion) { +
+
+
+

{{ activeBaseVersion.versionLabel }}

+

+ Active CBS base chart + Effective from {{ activeBaseVersion.effectiveFromDate }} +

+
+ Active +
+ + @if (activeBaseVersion.remarks) { +

{{ activeBaseVersion.remarks }}

+ } + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sr.{{ slab.srNo }}Particular{{ slab.description }}From{{ slab.fromPeriod }}To{{ slab.toPeriod ?? 'N/A' }}Period Type{{ slab.periodTypeLabel }}Annual Rate (%) + {{ slab.annualInterestRate | number: '1.2-2' }} +
+
+ } @else { +

Loading CBS base chart...

+ }
@@ -114,7 +162,7 @@
{ + 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 }; } diff --git a/src/app/products/recurring-deposit-products/edit-recurring-deposit-product/edit-recurring-deposit-product.component.html b/src/app/products/recurring-deposit-products/edit-recurring-deposit-product/edit-recurring-deposit-product.component.html index 606a08fb4..975cc9065 100644 --- a/src/app/products/recurring-deposit-products/edit-recurring-deposit-product/edit-recurring-deposit-product.component.html +++ b/src/app/products/recurring-deposit-products/edit-recurring-deposit-product/edit-recurring-deposit-product.component.html @@ -6,112 +6,195 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. --> -
- +
+ +

{{ 'labels.heading.Edit Recurring Deposit Product' | translate }}

+
+ + - - - + + + + {{ 'labels.inputs.DETAILS' | translate }} + +
+ +
+
- - - + + + + {{ 'labels.inputs.CURRENCY' | translate }} + +
+ +
+
- - - + + + + {{ 'labels.inputs.TERMS' | translate }} + +
+ +
+
- - - + + + + {{ 'labels.inputs.SETTINGS' | translate }} + +
+ +
+
- - - + + + + CBS Base Chart + +
+ @if (activeBaseVersion) { +
+
+
+

{{ activeBaseVersion.versionLabel }}

+

+ Active CBS base chart + Effective from {{ activeBaseVersion.effectiveFromDate }} +

+
+ Active +
- - {{ 'labels.inputs.DETAILS' | translate }} + @if (activeBaseVersion.remarks) { +

{{ activeBaseVersion.remarks }}

+ } - - -
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sr.{{ slab.srNo }}Particular{{ slab.description }}From{{ slab.fromPeriod }}To{{ slab.toPeriod ?? 'N/A' }}Period Type{{ slab.periodTypeLabel }}Annual Rate (%) + {{ slab.annualInterestRate | number: '1.2-2' }} +
+
+ } @else { +

Loading CBS base chart...

+ } +
+
- - {{ 'labels.inputs.CURRENCY' | translate }} + + + + {{ 'labels.inputs.CHARGES' | translate }} + +
+ +
+
- - -
- - - {{ 'labels.inputs.TERMS' | translate }} - - - - - - - {{ 'labels.inputs.SETTINGS' | translate }} - - - - - - - {{ 'labels.inputs.INTEREST RATE CHART' | translate }} - - - - - - - {{ 'labels.inputs.CHARGES' | translate }} - - - - - - - {{ 'labels.inputs.ACCOUNTING' | translate }} - - - - + + + + {{ 'labels.inputs.ACCOUNTING' | translate }} + +
+ +
+
@if (recurringDepositProductFormValidAndNotPrinstine) { - - {{ 'labels.inputs.PREVIEW' | translate }} - - - + + + + {{ 'labels.inputs.PREVIEW' | translate }} + +
+ +
+
} -
+ + +
+ +
+ @if (selectedTabIndex > 0) { + + } + @if (selectedTabIndex < totalTabs - 1) { + + } + @if (recurringDepositProductFormValidAndNotPrinstine && selectedTabIndex === totalTabs - 1) { + + } +
+
diff --git a/src/app/products/recurring-deposit-products/edit-recurring-deposit-product/edit-recurring-deposit-product.component.scss b/src/app/products/recurring-deposit-products/edit-recurring-deposit-product/edit-recurring-deposit-product.component.scss index af9f4c075..c04c383f8 100644 --- a/src/app/products/recurring-deposit-products/edit-recurring-deposit-product/edit-recurring-deposit-product.component.scss +++ b/src/app/products/recurring-deposit-products/edit-recurring-deposit-product/edit-recurring-deposit-product.component.scss @@ -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; + } +} diff --git a/src/app/products/recurring-deposit-products/edit-recurring-deposit-product/edit-recurring-deposit-product.component.ts b/src/app/products/recurring-deposit-products/edit-recurring-deposit-product/edit-recurring-deposit-product.component.ts index 2c39bef1d..aa5fb1ff0 100644 --- a/src/app/products/recurring-deposit-products/edit-recurring-deposit-product/edit-recurring-deposit-product.component.ts +++ b/src/app/products/recurring-deposit-products/edit-recurring-deposit-product/edit-recurring-deposit-product.component.ts @@ -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 }); }); diff --git a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-interest-rate-chart-step/recurring-deposit-product-interest-rate-chart-step.component.html b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-interest-rate-chart-step/recurring-deposit-product-interest-rate-chart-step.component.html index df813724b..7b799e488 100644 --- a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-interest-rate-chart-step/recurring-deposit-product-interest-rate-chart-step.component.html +++ b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-interest-rate-chart-step/recurring-deposit-product-interest-rate-chart-step.component.html @@ -6,325 +6,104 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. --> -
-
-
- -

{{ 'labels.heading.Interest Rate Charts' | translate }}

- - -
+ +
+
+ +

Bank Interest Rate Chart  Reference / Editable

+ + +
-
- @for (chart of charts.controls; track chart; let chartIndex = $index) { -
-
-
- -
- -
- - {{ 'labels.inputs.Name' | translate }} - - @if (chart.get('name')?.touched && chart.get('name')?.hasError('required')) { - - {{ 'labels.inputs.Name' | translate }} {{ 'labels.commons.is' | translate }} - {{ 'labels.commons.required' | translate }} - - } - - - - {{ 'labels.inputs.Description' | translate }} - - @if (chart.get('description')?.touched && chart.get('description')?.hasError('required')) { - - {{ 'labels.inputs.Description' | translate }} {{ 'labels.commons.is' | translate }} - {{ 'labels.commons.required' | translate }} - - } - - - - {{ 'labels.inputs.From Date' | translate }} - - - - - {{ 'labels.inputs.From Date' | translate }} {{ 'labels.commons.is' | translate }} - {{ 'labels.commons.required' | translate }} - - - - - {{ 'labels.inputs.End Date' | translate }} - - - - - -
- - {{ 'labels.inputs.Is primary grouping by amount?' | translate }} - -
-
- -
- -
- - @if (chart.value.chartSlabs.length === 0) { -
-

{{ 'labels.heading.It is required to add at least one Slab' | translate }}

-
- } - - @if (chart.value.chartSlabs.length !== 0) { -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{ 'labels.inputs.Period' | translate }} - {{ - chartSlab.fromPeriod + - ' - ' + - chartSlab.toPeriod + - ' ' + - (chartSlab.periodType | find: periodTypeData : 'id' : 'value') - }} - {{ 'labels.inputs.Amount Range' | translate }} - {{ chartSlab.amountRangeFrom + ' - ' + chartSlab.amountRangeTo }} - {{ 'labels.inputs.Interest' | translate }} - {{ chartSlab.annualInterestRate }} - {{ 'labels.inputs.Description' | translate }} - {{ chartSlab.description }} - {{ 'labels.inputs.Actions' | translate }} - - - - -
- -

- {{ 'labels.heading.Incentives' | translate }} -

-
- -
- @if (chartSlab.incentives.length) { -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- {{ 'labels.inputs.Entity Type' | translate }} - - {{ incentive.entityType | find: entityTypeData : 'id' : 'value' }} - - {{ 'labels.inputs.Attribute Name' | translate }} - - {{ incentive.attributeName | find: attributeNameData : 'id' : 'value' }} - - {{ 'labels.inputs.Condition Type' | translate }} - - {{ incentive.conditionType | find: conditionTypeData : 'id' : 'value' }} - - {{ 'labels.inputs.Attribute Value' | translate }} - - @switch (incentive.attributeName) { - @case (2) { - {{ incentive.attributeValue | find: genderData : 'id' : 'name' }} - } - @case (3) { - {{ incentive.attributeValue }} - } - @case (4) { - {{ - incentive.attributeValue | find: clientTypeData : 'id' : 'name' - }} - } - @case (5) { - {{ - incentive.attributeValue | find: clientClassificationData : 'id' : 'name' - }} - } - } - - {{ 'labels.inputs.Incentive Type' | translate }} - - {{ incentive.incentiveType | find: incentiveTypeData : 'id' : 'value' }} - {{ 'labels.inputs.Interest' | translate }} - {{ incentive.amount }} - {{ 'labels.inputs.Actions' | translate }} - - -
-
- } -
-
-
-
- } -
-
+ @if (_referenceVersionLabel) { +
+ Version: {{ _referenceVersionLabel }} + @if (_referenceRemarks) { +  | {{ _referenceRemarks }} }
-
- + } + + @if (_editableSlabs.length === 0) { +
+

No interest rate slabs defined. Click Add Slab to add one.

+
+ } + + @if (_editableSlabs.length > 0) { +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sr.{{ slab.srNo }}Particular + + From + + To + + Period{{ slab.periodTypeLabel }}Rate (%) + +
+
+ + + } +
diff --git a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-interest-rate-chart-step/recurring-deposit-product-interest-rate-chart-step.component.scss b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-interest-rate-chart-step/recurring-deposit-product-interest-rate-chart-step.component.scss index e72e64e10..21f95779c 100644 --- a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-interest-rate-chart-step/recurring-deposit-product-interest-rate-chart-step.component.scss +++ b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-interest-rate-chart-step/recurring-deposit-product-interest-rate-chart-step.component.scss @@ -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; +} diff --git a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-interest-rate-chart-step/recurring-deposit-product-interest-rate-chart-step.component.ts b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-interest-rate-chart-step/recurring-deposit-product-interest-rate-chart-step.component.ts index 8f6778ce0..ddb15f739 100644 --- a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-interest-rate-chart-step/recurring-deposit-product-interest-rate-chart-step.component.ts +++ b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-interest-rate-chart-step/recurring-deposit-product-interest-rate-chart-step.component.ts @@ -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 + }; } } diff --git a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-preview-step/recurring-deposit-product-preview-step.component.html b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-preview-step/recurring-deposit-product-preview-step.component.html index 3c9a72fa5..acad7041a 100644 --- a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-preview-step/recurring-deposit-product-preview-step.component.html +++ b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-preview-step/recurring-deposit-product-preview-step.component.html @@ -6,516 +6,638 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -
-
- -

{{ 'labels.inputs.Details' | translate }}

-
-
-

{{ recurringDepositProduct.name }}

-
- {{ 'labels.inputs.Short Name' | translate }}: - {{ recurringDepositProduct.shortName }} -
- @if (recurringDepositProduct.description) { -
- {{ 'labels.inputs.Description' | translate }}: - {{ recurringDepositProduct.description }} -
- } -
-
+
+ +

{{ recurringDepositProduct.name }}

- -
-
- -

{{ 'labels.inputs.Currency' | translate }}

-
-
-
- {{ 'labels.inputs.Currency' | translate }}: - {{ - recurringDepositProduct.currencyCode | find: recurringDepositProductsTemplate.currencyOptions : 'code' : 'name' - }} -
-
- {{ 'labels.inputs.Decimal Places' | translate }}: - {{ - recurringDepositProduct.digitsAfterDecimal ?? recurringDepositProduct.currency?.decimalPlaces - }} -
- @if (recurringDepositProduct.inMultiplesOf) { -
- {{ 'labels.inputs.Currency in multiples of' | translate }}: - {{ recurringDepositProduct.inMultiplesOf }} -
- } -
-
+ + +

{{ 'labels.inputs.Details' | translate }}

+ - -
-
- -

{{ 'labels.heading.Terms' | translate }}

+
+ {{ 'labels.inputs.Short Name' | translate }}: + {{ recurringDepositProduct.shortName }}
-
-
- {{ 'labels.inputs.Deposit Amount' | translate }}: - {{ - recurringDepositProduct.depositAmount + - ' (Min: ' + - (recurringDepositProduct.minDepositAmount - ? recurringDepositProduct.minDepositAmount - : recurringDepositProduct.depositAmount) + - ', Max: ' + - (recurringDepositProduct.maxDepositAmount - ? recurringDepositProduct.maxDepositAmount - : recurringDepositProduct.depositAmount) + - ')' - }} -
-
- {{ 'labels.inputs.Interest Compounding Period' | translate }}: - {{ - recurringDepositProduct.interestCompoundingPeriodType - | find: recurringDepositProductsTemplate.interestCompoundingPeriodTypeOptions : 'id' : 'value' - }} -
-
- {{ 'labels.inputs.Interest Posting Period' | translate }}: - {{ - recurringDepositProduct.interestPostingPeriodType - | find: recurringDepositProductsTemplate.interestPostingPeriodTypeOptions : 'id' : 'value' - }} -
-
- {{ 'labels.inputs.Interest Calculated using' | translate }}: - {{ - recurringDepositProduct.interestCalculationType - | find: recurringDepositProductsTemplate.interestCalculationTypeOptions : 'id' : 'value' - }} -
-
- {{ 'labels.inputs.Days in Year' | translate }}: - {{ - recurringDepositProduct.interestCalculationDaysInYearType - | find: recurringDepositProductsTemplate.interestCalculationDaysInYearTypeOptions : 'id' : 'value' - }} -
-
-
- -
-
- -

{{ 'labels.heading.Settings' | translate }}

-
-
- @if (recurringDepositProduct.lockinPeriodFrequency) { -
- {{ 'labels.inputs.Lock-in Period' | translate }}: - {{ - recurringDepositProduct.lockinPeriodFrequency + - ' ' + - (recurringDepositProduct.lockinPeriodFrequencyType - | find: recurringDepositProductsTemplate.lockinPeriodFrequencyTypeOptions : 'id' : 'value') - }} -
- } -
- {{ 'labels.inputs.Minimum Deposit Term' | translate }}: - {{ - recurringDepositProduct.minDepositTerm + - ' ' + - (recurringDepositProduct.minDepositTermTypeId - | find: recurringDepositProductsTemplate.periodFrequencyTypeOptions : 'id' : 'value') - }} + @if (recurringDepositProduct.description) { +
+ {{ 'labels.inputs.Description' | translate }}: + {{ recurringDepositProduct.description }}
- @if (recurringDepositProduct.inMultiplesOfDepositTerm) { -
- {{ 'labels.inputs.And thereafter, in Multiples of' | translate }}: - {{ - recurringDepositProduct.inMultiplesOfDepositTerm + - ' ' + - (recurringDepositProduct.inMultiplesOfDepositTermTypeId - | find: recurringDepositProductsTemplate.periodFrequencyTypeOptions : 'id' : 'value') - }} -
- } - @if (recurringDepositProduct.maxDepositTerm) { -
- {{ 'labels.inputs.Maximum Deposit Term' | translate }}: - {{ - recurringDepositProduct.maxDepositTerm + - ' ' + - (recurringDepositProduct.maxDepositTermTypeId - | find: recurringDepositProductsTemplate.periodFrequencyTypeOptions : 'id' : 'value') - }} -
- } -
- {{ 'labels.inputs.Apply Penal Interest (less)' | translate }}: - {{ recurringDepositProduct.preClosurePenalApplicable | yesNo }} -
- @if (recurringDepositProduct.preClosurePenalApplicable) { -
- {{ 'labels.inputs.Penal Interest' | translate }} (%): - {{ - recurringDepositProduct.preClosurePenalInterest + - '% (' + - (recurringDepositProduct.preClosurePenalInterestOnTypeId - | find: recurringDepositProductsTemplate.preClosurePenalInterestOnTypeOptions : 'id' : 'value') + - ')' - }} -
- } - @if (recurringDepositProduct.isMandatoryDeposit !== undefined) { -
- {{ 'labels.inputs.Recurring Deposit Type' | translate }}: - @if (recurringDepositProduct.isMandatoryDeposit) { - {{ 'labels.text.Mandatory' | translate }} - } - @if (!recurringDepositProduct.isMandatoryDeposit) { - {{ 'labels.text.Voluntary' | translate }} - } -
- } -
- {{ 'labels.inputs.Withhold Tax is Applicable' | translate }}: - {{ recurringDepositProduct.withHoldTax | yesNo }} -
- @if (recurringDepositProduct.withHoldTax) { -
- {{ 'labels.inputs.Tax Group' | translate }}: - {{ - recurringDepositProduct.taxGroupId | find: recurringDepositProductsTemplate.taxGroupOptions : 'id' : 'name' - }} -
- } -
-
+ } - -@if (recurringDepositProduct.charts.length !== 0) { -
-
- -

{{ 'labels.heading.Interest Rate Charts' | translate }}

+ + @if ( + recurringDepositProduct.accountType || + recurringDepositProduct.customerSegment || + recurringDepositProduct.productCode || + recurringDepositProduct.effectiveTo || + recurringDepositProduct.introducerRequired !== undefined || + recurringDepositProduct.shareLinkingEnabled !== undefined + ) { +

Product Classification

+ + + @if (recurringDepositProduct.accountType) { +
+ Account Type: + {{ recurringDepositProduct.accountType }} +
+ } + + @if (recurringDepositProduct.customerSegment) { +
+ Customer Segment: + {{ recurringDepositProduct.customerSegment }} +
+ } + + @if (recurringDepositProduct.productCode) { +
+ Product Code: + {{ recurringDepositProduct.productCode }} +
+ } + + @if (recurringDepositProduct.effectiveTo) { +
+ Effective To: + {{ recurringDepositProduct.effectiveTo }} +
+ } + + @if (recurringDepositProduct.endDate) { +
+ End Date: + {{ recurringDepositProduct.endDate }} +
+ } + + @if (recurringDepositProduct.introducerRequired !== undefined) { +
+ Introducer Required: + {{ + recurringDepositProduct.introducerRequired === true || recurringDepositProduct.introducerRequired === 'Yes' + ? 'Yes' + : 'No' + }} +
+ } + + @if (recurringDepositProduct.shareLinkingEnabled !== undefined) { +
+ Share Linking Enabled: + {{ recurringDepositProduct.shareLinkingEnabled | yesNo }} +
+ } + } + + +

{{ 'labels.inputs.Currency' | translate }}

+ + +
+ {{ 'labels.inputs.Currency' | translate }}: + {{ + recurringDepositProduct.currencyCode | find: recurringDepositProductsTemplate.currencyOptions : 'code' : 'name' + }} +
+ +
+ {{ 'labels.inputs.Decimal Places' | translate }}: + {{ recurringDepositProduct.digitsAfterDecimal }} +
+ + @if (recurringDepositProduct.dormancyAlert !== undefined) { +
+ Dormancy Alert: + {{ recurringDepositProduct.dormancyAlert | yesNo }}
-
- @for (chart of recurringDepositProduct.charts; track chart; let chartIndex = $index) { -
- @if (chart.name) { -
- {{ 'labels.inputs.Name' | translate }}: - {{ chart.name }} -
- } -
- {{ 'labels.inputs.From Date' | translate }}: - {{ chart.fromDate | dateFormat }} -
- @if (chart.endDate) { -
- {{ 'labels.inputs.End Date' | translate }}: - {{ chart.endDate | dateFormat }} -
- } - @if (chart.description) { -
- {{ 'labels.inputs.Description' | translate }}: - {{ chart.description }} -
- } -
- {{ 'labels.inputs.Primary Grouping by Amount' | translate }}: - {{ recurringDepositProduct.isPrimaryGroupingByAmount | yesNo }} -
- @if (chart.chartSlabs.length !== 0) { -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{ 'labels.inputs.Period' | translate }} - {{ - chartSlab.fromPeriod + - ' - ' + - chartSlab.toPeriod + - ' ' + - (chartSlab.periodType - | find: recurringDepositProductsTemplate.chartTemplate.periodTypes : 'id' : 'value') - }} - {{ 'labels.inputs.Amount Range' | translate }} - {{ chartSlab.amountRangeFrom + ' - ' + chartSlab.amountRangeTo }} - {{ 'labels.inputs.Interest' | translate }}{{ chartSlab.annualInterestRate | formatNumber }} %{{ 'labels.inputs.Description' | translate }}{{ chartSlab.description }}{{ 'labels.inputs.Actions' | translate }} - - -
- -

{{ 'labels.heading.Incentives' | translate }}

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{ 'labels.inputs.Entity Type' | translate }} - {{ - incentive.entityType - | find - : recurringDepositProductsTemplate.chartTemplate.entityTypeOptions - : 'id' - : 'value' - }} - - {{ 'labels.inputs.Attribute Name' | translate }} - - {{ - incentive.attributeName - | find - : recurringDepositProductsTemplate.chartTemplate.attributeNameOptions - : 'id' - : 'value' - }} - - {{ 'labels.inputs.Condition Type' | translate }} - - {{ - incentive.conditionType - | find - : recurringDepositProductsTemplate.chartTemplate.conditionTypeOptions - : 'id' - : 'value' - }} - - {{ 'labels.inputs.Attribute Value' | translate }} - - @switch (incentive.attributeName) { - @case (2) { - {{ - incentive.attributeValue - | find - : recurringDepositProductsTemplate.chartTemplate.genderOptions - : 'id' - : 'name' - }} - } - @case (3) { - {{ incentive.attributeValue }} - } - @case (4) { - {{ - incentive.attributeValue - | find - : recurringDepositProductsTemplate.chartTemplate.clientTypeOptions - : 'id' - : 'name' - }} - } - @case (5) { - {{ - incentive.attributeValue - | find - : recurringDepositProductsTemplate.chartTemplate.clientClassificationOptions - : 'id' - : 'name' - }} - } - } - - {{ 'labels.inputs.Incentive Type' | translate }} - - {{ - incentive.incentiveType - | find - : recurringDepositProductsTemplate.chartTemplate.incentiveTypeOptions - : 'id' - : 'value' - }} - {{ 'labels.inputs.Amount' | translate }} - {{ incentive.amount | formatNumber }} -
-
-
-
-
-
- } + } + + @if (recurringDepositProduct.balanceSwapOption !== undefined) { +
+ Balance Swap Option: + {{ recurringDepositProduct.balanceSwapOption | yesNo }} +
+ } + + @if (recurringDepositProduct.msme !== undefined) { +
+ MSME: + {{ recurringDepositProduct.msme | yesNo }} +
+ } + + @if (recurringDepositProduct.nominationFacility !== undefined) { +
+ Nomination Facility: + {{ recurringDepositProduct.nominationFacility | yesNo }} +
+ } + + @if (recurringDepositProduct.autoDebitFacility !== undefined) { +
+ Auto Debit Facility: + {{ recurringDepositProduct.autoDebitFacility | yesNo }} +
+ } + + +

{{ 'labels.inputs.Terms' | translate }}

+ + +
+ {{ 'labels.inputs.Nominal Annual Interest Rate' | translate }} (%): + {{ recurringDepositProduct.nominalAnnualInterestRate | formatNumber }} % +
+ +
+ {{ 'labels.inputs.Interest Compounding Period' | translate }}: + {{ + recurringDepositProduct.interestCompoundingPeriodType + | find: recurringDepositProductsTemplate.interestCompoundingPeriodTypeOptions : 'id' : 'value' + }} +
+ +
+ {{ 'labels.inputs.Interest Posting Period' | translate }}: + {{ + recurringDepositProduct.interestPostingPeriodType + | find: recurringDepositProductsTemplate.interestPostingPeriodTypeOptions : 'id' : 'value' + }} +
+ +
+ {{ 'labels.inputs.Interest Calculated using' | translate }}: + {{ + recurringDepositProduct.interestCalculationType + | find: recurringDepositProductsTemplate.interestCalculationTypeOptions : 'id' : 'value' + }} +
+ +
+ {{ 'labels.inputs.Days in Year' | translate }}: + {{ + recurringDepositProduct.interestCalculationDaysInYearType + | find: recurringDepositProductsTemplate.interestCalculationDaysInYearTypeOptions : 'id' : 'value' + }} +
+ + @if (recurringDepositProduct.effectiveDate) { +
+ Effective Date: + {{ recurringDepositProduct.effectiveDate }} +
+ } + + @if (recurringDepositProduct.averagePeriod) { +
+ Average Period: + {{ recurringDepositProduct.averagePeriod }} +
+ } + +
+ Auto Interest Post: + {{ recurringDepositProduct.autoPost | yesNo }} +
+ + @if ( + recurringDepositProduct.frequencyFixDay !== undefined && + recurringDepositProduct.frequencyFixDay !== null && + recurringDepositProduct.frequencyFixDay !== '' + ) { +
+ Frequency (Fix Day): + {{ recurringDepositProduct.frequencyFixDay }} +
+ } + + @if (recurringDepositProduct.lastInterestDate) { +
+ Last Interest Date: + {{ recurringDepositProduct.lastInterestDate }} +
+ } + + +

{{ 'labels.heading.Settings' | translate }}

+ + +
+ Loan Facility: + {{ recurringDepositProduct.loanFacility | yesNo }} +
+ +
+ Premature Withdrawal: + {{ recurringDepositProduct.prematureWithdrawalOption | yesNo }} +
+ +
+ Tenure Flexibility: + {{ recurringDepositProduct.tenureFlexibility | yesNo }} +
+ +
+ Lien Marked: + {{ recurringDepositProduct.lienMarked | yesNo }} +
+ +
+ {{ 'labels.inputs.Apply Withdrawal Fee for Transfers' | translate }}: + {{ recurringDepositProduct.withdrawalFeeForTransfers | yesNo }} +
+ +
+ Enable Lock-in Period: + {{ recurringDepositProduct.enableLockinPeriod | yesNo }} +
+ + @if (recurringDepositProduct.enableLockinPeriod && recurringDepositProduct.lockinPeriodFrequency) { +
+ Lock-in Period: + + {{ recurringDepositProduct.lockinPeriodFrequency }} + {{ getLockinPeriodFrequencyTypeLabel(recurringDepositProduct.lockinPeriodFrequencyType) }} + +
+ } + +
+ Maturity Instructions: + {{ recurringDepositProduct.maturityInstructions | yesNo }} +
+ +
+ Credit Maturity Proceeds: + {{ recurringDepositProduct.creditMaturityProceeds | yesNo }} +
+ +
+ Penalty Grace Period (Missed Installments): + {{ recurringDepositProduct.penaltyGracePeriodMissedInstallments | yesNo }} +
+ +
+ Panel Request Applicable: + {{ recurringDepositProduct.panelRequestApplicable | yesNo }} +
+ +
+ No Penalty (Nominee / Heir Closure): + {{ recurringDepositProduct.noPenaltyNomineeHeirClosure | yesNo }} +
+ +
+ Deceased Settlement: + {{ recurringDepositProduct.deceasedSettlement | yesNo }} +
+ +
+ Allow Debit Txn After Due Date: + {{ recurringDepositProduct.allowDebitTxnAfterDueDate | yesNo }} +
+ +
+ Account Closed with Zero Balance in Day Closing: + {{ recurringDepositProduct.accountClosedZeroBalanceInDayClosing | yesNo }} +
+ + @if (recurringDepositProduct.minBalanceForInterestCalculation) { +
+ Min Balance for Interest Calculation: + {{ recurringDepositProduct.minBalanceForInterestCalculation | formatNumber }} +
+ } + + + @if (recurringDepositProduct.rdSlabs?.length) { +

{{ 'labels.heading.Interest Rate Charts' | translate }}

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sr.{{ slab.srNo }}Particular{{ slab.description }}From{{ slab.fromPeriod }}To{{ slab.toPeriod ?? '—' }}Period{{ slab.periodTypeLabel }}Rate (%) + {{ slab.annualInterestRate }} +
+
+ } + + + @if (recurringDepositProduct.charges?.length) { +
+

{{ 'labels.inputs.Charges' | translate }}

+ + + + + + + + + + + + + + + + + + + + +
{{ 'labels.inputs.Name' | translate }}{{ charge.name + ', ' + charge.currency.displaySymbol }}{{ 'labels.inputs.Type' | translate }}{{ charge.chargeCalculationType.value }}{{ 'labels.inputs.Amount' | translate }}{{ charge.amount | formatNumber }}{{ 'labels.inputs.Collected On' | translate }}{{ charge.chargeTimeType.value }}
+
+ } + + +

{{ 'labels.heading.Accounting' | translate }}

+ + + @if (isCashOrAccrualAccounting()) { +
+
+

GL Mapping

+
+ +
+
Assets
+
+ Savings Reference Account + {{ + formatGlAccountById( + recurringDepositProduct.savingsReferenceAccountId, + recurringDepositProductsTemplate?.accountingMappingOptions.assetAccountOptions + ) + }}
+ @if (isAccrualAccounting()) { +
+ Fees Receivable Account + {{ + formatGlAccountById( + recurringDepositProduct.feesReceivableAccountId, + recurringDepositProductsTemplate?.accountingMappingOptions.assetAccountOptions + ) + }} +
+
+ Penalties Receivable Account + {{ + formatGlAccountById( + recurringDepositProduct.penaltiesReceivableAccountId, + recurringDepositProductsTemplate?.accountingMappingOptions.assetAccountOptions + ) + }} +
+ } +
+ +
+
Liabilities
+
+ Savings Control Account + {{ + formatGlAccountById( + recurringDepositProduct.savingsControlAccountId, + recurringDepositProductsTemplate?.accountingMappingOptions.liabilityAccountOptions + ) + }} +
+
+ Transfers In Suspense Account + {{ + formatGlAccountById( + recurringDepositProduct.transfersInSuspenseAccountId, + recurringDepositProductsTemplate?.accountingMappingOptions.liabilityAccountOptions + ) + }} +
+ @if (isAccrualAccounting()) { +
+ Interest Payable Account + {{ + formatGlAccountById( + recurringDepositProduct.interestPayableAccountId, + recurringDepositProductsTemplate?.accountingMappingOptions.liabilityAccountOptions + ) + }} +
+ } +
+ +
+
Expenses
+
+ Interest on Savings Account + {{ + formatGlAccountById( + recurringDepositProduct.interestOnSavingsAccountId, + recurringDepositProductsTemplate?.accountingMappingOptions.expenseAccountOptions + ) + }} +
+
+ +
+
Income
+
+ Income from Fees + {{ + formatGlAccountById( + recurringDepositProduct.incomeFromFeeAccountId, + recurringDepositProductsTemplate?.accountingMappingOptions.incomeAccountOptions + ) + }} +
+
+ Income from Penalties + {{ + formatGlAccountById( + recurringDepositProduct.incomeFromPenaltyAccountId, + recurringDepositProductsTemplate?.accountingMappingOptions.incomeAccountOptions + ) + }} +
+
+
+ } + + + @if (recurringDepositProduct.otherGlMappings) { +
+
+

Other GL Mappings

+
+ + @if (recurringDepositProduct.otherGlMappings.assets?.length) { + + + + + + + + + + + +
Asset Account{{ row.accountName }}GL Account + {{ + formatGlAccountByCode( + row.glCode, + recurringDepositProductsTemplate?.accountingMappingOptions.assetAccountOptions + ) + }} +
+ } + + @if (recurringDepositProduct.otherGlMappings.liabilities?.length) { + + + + + + + + + + + +
Liability Account{{ row.accountName }}GL Account + {{ + formatGlAccountByCode( + row.glCode, + recurringDepositProductsTemplate?.accountingMappingOptions.liabilityAccountOptions + ) + }} +
+ } + + @if (recurringDepositProduct.otherGlMappings.expenses?.length) { + + + + + + + + + + + +
Expense Account{{ row.accountName }}GL Account + {{ + formatGlAccountByCode( + row.glCode, + recurringDepositProductsTemplate?.accountingMappingOptions.expenseAccountOptions + ) + }} +
+ } + + @if (recurringDepositProduct.otherGlMappings.income?.length) { + + + + + + + + + + + +
Income Account{{ row.accountName }}GL Account + {{ + formatGlAccountByCode( + row.glCode, + recurringDepositProductsTemplate?.accountingMappingOptions.incomeAccountOptions + ) + }} +
}
-
-} + } - -@if (recurringDepositProduct.charges.length) { -
-
- -

{{ 'labels.heading.Charges' | translate }}

-
-
-
- - - - - - - - - - - - - - - - - - - -
{{ 'labels.inputs.Name' | translate }} - {{ charge.name + ', ' + charge.currency.displaySymbol }} - {{ 'labels.inputs.Type' | translate }} - {{ charge.chargeCalculationType.value }} - {{ 'labels.inputs.Amount' | translate }} - {{ charge.amount }} - {{ 'labels.inputs.Collected On' | translate }} - {{ charge.chargeTimeType.value }} -
-
-
-
-} - - -
-
- -

{{ 'labels.heading.Accounting' | translate }}

-
-
- @if (!isNoneAccounting()) { - - - } - - @if ( - recurringDepositProduct.advancedAccountingRules && - (recurringDepositProduct.paymentChannelToFundSourceMappings?.length || - recurringDepositProduct.feeToIncomeAccountMappings?.length || - recurringDepositProduct.penaltyToIncomeAccountMappings?.length) - ) { -

{{ 'labels.heading.Advanced Accounting Rules' | translate }}

+ + @if ( + recurringDepositProduct.advancedAccountingRules && + (recurringDepositProduct.paymentChannelToFundSourceMappings?.length || + recurringDepositProduct.feeToIncomeAccountMappings?.length || + recurringDepositProduct.penaltyToIncomeAccountMappings?.length) + ) { +
+

{{ 'labels.heading.Advanced Accounting Rules' | translate }}

+ @if (recurringDepositProduct.paymentChannelToFundSourceMappings?.length) { -
{{ 'labels.heading.Fund Sources for Payment Channels' | translate }}
-
- +
+

{{ 'labels.heading.Fund Sources for Payment Channels' | translate }}

+
- -
{{ 'labels.inputs.Payment Type' | translate }} - {{ - paymentFundSource.paymentTypeId - | find: recurringDepositProductsTemplate.paymentTypeOptions : 'id' : 'name' - }} + + {{ item.paymentTypeId | find: recurringDepositProductsTemplate.paymentTypeOptions : 'id' : 'name' }} {{ 'labels.inputs.Fund Source' | translate }} + {{ - paymentFundSource.fundSourceAccountId + item.fundSourceAccountId | find - : recurringDepositProductsTemplate.accountingMappingOptions.assetAccountOptions + : recurringDepositProductsTemplate?.accountingMappingOptions.assetAccountOptions : 'id' : 'name' }} @@ -528,22 +650,26 @@ } @if (recurringDepositProduct.feeToIncomeAccountMappings?.length) { -
{{ 'labels.heading.Fees to Specific Income Accounts' | translate }}
-
- +
+

{{ 'labels.heading.Fees to Specific Income Accounts' | translate }}

+
- - + + + + + + + + + + + + + + + + + + + + + + + + +
{{ 'labels.inputs.Fees' | translate }} - {{ feesIncome.chargeId | find: recurringDepositProductsTemplate.chargeOptions : 'id' : 'name' }} + + {{ item.chargeId | find: recurringDepositProductsTemplate.chargeOptions : 'id' : 'name' }} {{ 'labels.inputs.Income Account' | translate }} + {{ - feesIncome.incomeAccountId + item.incomeAccountId | find - : recurringDepositProductsTemplate.accountingMappingOptions.incomeAccountOptions + : recurringDepositProductsTemplate?.accountingMappingOptions.incomeAccountOptions : 'id' : 'name' }} @@ -556,22 +682,26 @@ } @if (recurringDepositProduct.penaltyToIncomeAccountMappings?.length) { -
{{ 'labels.heading.Penalties to Specific Income Accounts' | translate }}
-
- +
+

{{ 'labels.heading.Penalties to Specific Income Accounts' | translate }}

+
- -
{{ 'labels.inputs.Penalty' | translate }} - {{ penaltyIncome.chargeId | find: recurringDepositProductsTemplate.penaltyOptions : 'id' : 'name' }} + + {{ item.chargeId | find: recurringDepositProductsTemplate.penaltyOptions : 'id' : 'name' }} {{ 'labels.inputs.Income Account' | translate }} + {{ - penaltyIncome.incomeAccountId + item.incomeAccountId | find - : recurringDepositProductsTemplate.accountingMappingOptions.incomeAccountOptions + : recurringDepositProductsTemplate?.accountingMappingOptions.incomeAccountOptions : 'id' : 'name' }} @@ -582,6 +712,6 @@
} - } - + + } diff --git a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-preview-step/recurring-deposit-product-preview-step.component.scss b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-preview-step/recurring-deposit-product-preview-step.component.scss index b00fc9c5c..33a0e1852 100644 --- a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-preview-step/recurring-deposit-product-preview-step.component.scss +++ b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-preview-step/recurring-deposit-product-preview-step.component.scss @@ -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; - } - - &__header { - display: flex; - align-items: center; - gap: 10px; - padding: 12px 24px; - background: #f8f9fa; - border-bottom: 1px solid #e8eaed; - - i { - font-size: 14px; - color: #0ea5e9; - } - - h3 { - margin: 0; - font-size: 14px; - font-weight: 600; - color: #202124; - } - } - - &__body { - padding: 16px 24px; - } +.mat-h3 { + margin-top: 10px !important; + font-weight: 300; + color: $status-approved; } -.rdpp-product-name { - margin: 0 0 12px; - font-size: 18px; - font-weight: 600; - color: #202124; +.mat-elevation-z1 { + margin: 1em 0 1.5em; } -.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; +h2, +h3, +h4 { + margin: 0; 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; - } -} - -.rdpp-sub-heading { - margin: 16px 0 8px; - font-size: 14px; - font-weight: 600; - color: #0ea5e9; -} - -.rdpp-sub-heading-sm { - margin: 12px 0 4px; - font-size: 13px; - font-weight: 600; - color: #202124; -} - -.rdpp-chart-block { - & + & { - margin-top: 16px; - padding-top: 16px; - border-top: 1px solid #e2e8f0; - } -} - -.rdpp-incentives { - 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; + width: 100%; +} - > td { - padding: 0; - } +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; + } +} + +.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; + word-break: break-word; +} + +.spa-other-gl-table { + margin-top: 0.5rem; + margin-bottom: 0.75rem; + table-layout: fixed; + width: 100%; + + &:first-of-type { + margin-top: 0.25rem; + } + + 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; + } +} + +.spa-other-gl-summary .mat-elevation-z1 { + border-radius: 10px; + overflow: hidden; +} + +@media (width <= 768px) { + .spa-gl-row { + grid-template-columns: 1fr; + gap: 0.35rem; } } diff --git a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-preview-step/recurring-deposit-product-preview-step.component.ts b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-preview-step/recurring-deposit-product-preview-step.component.ts index d259e2e75..dadf5d011 100644 --- a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-preview-step/recurring-deposit-product-preview-step.component.ts +++ b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-preview-step/recurring-deposit-product-preview-step.component.ts @@ -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; + } } diff --git a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-terms-step/recurring-deposit-product-terms-step.component.ts b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-terms-step/recurring-deposit-product-terms-step.component.ts index ccef15fff..f19d25d85 100644 --- a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-terms-step/recurring-deposit-product-terms-step.component.ts +++ b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-terms-step/recurring-deposit-product-terms-step.component.ts @@ -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 diff --git a/src/app/products/recurring-deposit-products/recurring-deposit-products.component.html b/src/app/products/recurring-deposit-products/recurring-deposit-products.component.html index 5523e15cc..a2e463e62 100644 --- a/src/app/products/recurring-deposit-products/recurring-deposit-products.component.html +++ b/src/app/products/recurring-deposit-products/recurring-deposit-products.component.html @@ -20,6 +20,18 @@ {{ 'labels.buttons.Create Recurring Deposit Product' | translate }} +
+ +
diff --git a/src/app/products/recurring-deposit-products/view-recurring-deposit-product/rd-interest-rates-tab/rd-interest-rates-tab.component.html b/src/app/products/recurring-deposit-products/view-recurring-deposit-product/rd-interest-rates-tab/rd-interest-rates-tab.component.html new file mode 100644 index 000000000..2295a36d2 --- /dev/null +++ b/src/app/products/recurring-deposit-products/view-recurring-deposit-product/rd-interest-rates-tab/rd-interest-rates-tab.component.html @@ -0,0 +1,68 @@ + + +
+ @if (!rdInterestRates || rdInterestRates.length === 0) { +

No CBS base interest rate versions defined.

+ } + + + @for (version of rdInterestRates; track version.id) { + + + + + {{ version.versionLabel }} + + {{ version.isActive ? 'Active' : 'Inactive' }} + + Effective from: {{ version.effectiveFromDate }} + + + + + @if (version.remarks) { +

+ {{ version.remarks }} +

+ } + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sr.{{ slab.srNo }}Particular{{ slab.description }}From{{ slab.fromPeriod }}To{{ slab.toPeriod ?? '—' }}Period Type{{ slab.periodTypeLabel }}Annual Rate (%) + {{ slab.annualInterestRate | number: '1.2-2' }} +
+
+ } +
+
diff --git a/src/app/products/recurring-deposit-products/view-recurring-deposit-product/rd-interest-rates-tab/rd-interest-rates-tab.component.scss b/src/app/products/recurring-deposit-products/view-recurring-deposit-product/rd-interest-rates-tab/rd-interest-rates-tab.component.scss new file mode 100644 index 000000000..1728940c9 --- /dev/null +++ b/src/app/products/recurring-deposit-products/view-recurring-deposit-product/rd-interest-rates-tab/rd-interest-rates-tab.component.scss @@ -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; +} diff --git a/src/app/products/recurring-deposit-products/view-recurring-deposit-product/rd-interest-rates-tab/rd-interest-rates-tab.component.ts b/src/app/products/recurring-deposit-products/view-recurring-deposit-product/rd-interest-rates-tab/rd-interest-rates-tab.component.ts new file mode 100644 index 000000000..79c7ac552 --- /dev/null +++ b/src/app/products/recurring-deposit-products/view-recurring-deposit-product/rd-interest-rates-tab/rd-interest-rates-tab.component.ts @@ -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 || []; + }); + } +} diff --git a/src/app/products/recurring-deposit-products/view-recurring-deposit-product/rd-interest-rates-tab/rd-interest-rates.resolver.ts b/src/app/products/recurring-deposit-products/view-recurring-deposit-product/rd-interest-rates-tab/rd-interest-rates.resolver.ts new file mode 100644 index 000000000..68e82a666 --- /dev/null +++ b/src/app/products/recurring-deposit-products/view-recurring-deposit-product/rd-interest-rates-tab/rd-interest-rates.resolver.ts @@ -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 = (route) => { + const productsService = inject(ProductsService); + return productsService.getRdBaseInterestRates(); +}; diff --git a/src/app/products/recurring-deposit-products/view-recurring-deposit-product/recurring-deposit-general-tab/recurring-deposit-general-tab.component.html b/src/app/products/recurring-deposit-products/view-recurring-deposit-product/recurring-deposit-general-tab/recurring-deposit-general-tab.component.html index 004fb3daa..241b30d61 100644 --- a/src/app/products/recurring-deposit-products/view-recurring-deposit-product/recurring-deposit-general-tab/recurring-deposit-general-tab.component.html +++ b/src/app/products/recurring-deposit-products/view-recurring-deposit-product/recurring-deposit-general-tab/recurring-deposit-general-tab.component.html @@ -6,546 +6,629 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. --> -
-
-
- - -
-

{{ recurringDepositProduct.name }}

+
+

{{ recurringDepositProduct.name }}

- + + +

{{ 'labels.inputs.Details' | translate }}

+ -

{{ 'labels.inputs.Details' | translate }}

+
+ {{ 'labels.inputs.Short Name' | translate }}: + {{ recurringDepositProduct.shortName }} +
- + @if (recurringDepositProduct.description) { +
+ {{ 'labels.inputs.Description' | translate }}: + {{ recurringDepositProduct.description }} +
+ } + +

Product Classification

+ + + @if (recurringDepositProduct.accountType) { +
+ Account Type: + {{ recurringDepositProduct.accountType }} +
+ } + @if (recurringDepositProduct.customerSegment) { +
+ Customer Segment: + {{ recurringDepositProduct.customerSegment }} +
+ } + @if (recurringDepositProduct.productCode) { +
+ Product Code: + {{ recurringDepositProduct.productCode }} +
+ } + @if (recurringDepositProduct.effectiveTo) { +
+ Effective To: + {{ recurringDepositProduct.effectiveTo | dateFormat }} +
+ } + @if (recurringDepositProduct.endDate) { +
+ End Date: + {{ recurringDepositProduct.endDate | dateFormat }} +
+ } +
+ Introducer Required: + {{ recurringDepositProduct.introducerRequired | yesNo }} +
+
+ Share Linking Enabled: + {{ recurringDepositProduct.shareLinkingEnabled | yesNo }} +
+ + +

{{ 'labels.inputs.Currency' | translate }}

+ + +
+ {{ 'labels.inputs.Currency' | translate }}: + ({{ recurringDepositProduct.currency.code }}) {{ recurringDepositProduct.currency.name }} +
+
+ {{ 'labels.inputs.Decimal Places' | translate }}: + {{ recurringDepositProduct.currency.decimalPlaces }} +
+
+ Dormancy Alert: + {{ recurringDepositProduct.dormancyAlert | yesNo }} +
+
+ Balance Swap Option: + {{ recurringDepositProduct.balanceSwapOption | yesNo }} +
+
+ MSME: + {{ recurringDepositProduct.msme | yesNo }} +
+
+ Nomination Facility: + {{ recurringDepositProduct.nominationFacility | yesNo }} +
+
+ Auto Debit Facility: + {{ recurringDepositProduct.autoDebitFacility | yesNo }} +
+ + +

{{ 'labels.inputs.Terms' | translate }}

+ + +
+ {{ 'labels.inputs.Deposit Amount' | translate }}: + {{ + recurringDepositProduct.depositAmount + + ' (Min: ' + + (recurringDepositProduct.minDepositAmount || recurringDepositProduct.depositAmount) + + ', Max: ' + + (recurringDepositProduct.maxDepositAmount || recurringDepositProduct.depositAmount) + + ')' + }} +
+
+ {{ 'labels.inputs.Nominal Annual Interest' | translate }}: + {{ recurringDepositProduct.nominalAnnualInterestRate | formatNumber }} % +
+ @if (recurringDepositProduct.effectiveDate) { +
+ Effective Date: + {{ recurringDepositProduct.effectiveDate | dateFormat }} +
+ } +
+ {{ 'labels.inputs.Interest Calculation Type' | translate }}: + {{ recurringDepositProduct.interestCalculationType.value }} +
+ @if (recurringDepositProduct.interestCalculateSelector !== undefined) { +
+ Interest Calculate: + {{ recurringDepositProduct.interestCalculateSelector }} +
+ } + @if (recurringDepositProduct.averagePeriod) { +
+ Average Period: + {{ recurringDepositProduct.averagePeriod }} +
+ } +
+ {{ 'labels.inputs.Interest Posting Period' | translate }}: + {{ recurringDepositProduct.interestPostingPeriodType.value }} +
+
+ {{ 'labels.inputs.Interest Compounding Period' | translate }}: + {{ recurringDepositProduct.interestCompoundingPeriodType.value }} +
+
+ {{ 'labels.inputs.Days in Year' | translate }}: + {{ recurringDepositProduct.interestCalculationDaysInYearType.value }} +
+
+ Auto Post: + {{ recurringDepositProduct.autoPost | yesNo }} +
+ @if (recurringDepositProduct.frequencyFixDay) { +
+ Frequency (Fix Day): + {{ recurringDepositProduct.frequencyFixDay }} +
+ } + @if (recurringDepositProduct.lastInterestDate) { +
+ Last Interest Date: + {{ recurringDepositProduct.lastInterestDate | dateFormat }} +
+ } + + +

{{ 'labels.inputs.Settings' | translate }}

+ + + +

Premature Option

+
+ Loan Facility: + {{ recurringDepositProduct.loanFacility | yesNo }} +
+
+ Premature Withdrawal Option: + {{ recurringDepositProduct.prematureWithdrawalOption | yesNo }} +
+
+ Tenure Flexibility: + {{ recurringDepositProduct.tenureFlexibility | yesNo }} +
+
+ Lien Marked: + {{ recurringDepositProduct.lienMarked | yesNo }} +
+
+ {{ 'labels.inputs.Apply Withdrawal Fee for Transfers' | translate }}: + {{ recurringDepositProduct.withdrawalFeeForTransfers | yesNo }} +
+ + +

{{ 'labels.inputs.Lock-in Period' | translate }}

+
+ Enable Lock-in Period: + {{ recurringDepositProduct.enableLockinPeriod | yesNo }} +
+ @if (recurringDepositProduct.lockinPeriodFrequency) { +
+ {{ 'labels.inputs.Lock-in Period' | translate }}: + {{ recurringDepositProduct.lockinPeriodFrequency }} + {{ recurringDepositProduct.lockinPeriodFrequencyType?.value }} +
+ } + + +

Maturity Instructions

+
+ Maturity Instructions: + {{ recurringDepositProduct.maturityInstructions | yesNo }} +
+
+ Credit Maturity Proceeds: + {{ recurringDepositProduct.creditMaturityProceeds | yesNo }} +
+
+ Penalty Grace Period (Missed Installments): + {{ recurringDepositProduct.penaltyGracePeriodMissedInstallments | yesNo }} +
+
+ Panel Request Applicable: + {{ recurringDepositProduct.panelRequestApplicable | yesNo }} +
+
+ No Penalty (Nominee / Heir Closure): + {{ recurringDepositProduct.noPenaltyNomineeHeirClosure | yesNo }} +
+
+ Deceased Settlement: + {{ recurringDepositProduct.deceasedSettlement | yesNo }} +
+
+ Allow Debit Txn After Due Date: + {{ recurringDepositProduct.allowDebitTxnAfterDueDate | yesNo }} +
+ + +

Additional Rules

+
+ Account Closed Zero Balance in Day Closing: + {{ recurringDepositProduct.accountClosedZeroBalanceInDayClosing | yesNo }} +
+ @if (recurringDepositProduct.minBalanceForInterestCalculation) { +
+ {{ 'labels.inputs.Balance Required for Interest Calculation' | translate }}: + {{ recurringDepositProduct.minBalanceForInterestCalculation | formatNumber }} +
+ } + + @if (recurringDepositProduct.minDepositTerm) { +
+ {{ 'labels.inputs.Minimum Deposit Term' | translate }}: + {{ recurringDepositProduct.minDepositTerm }} {{ recurringDepositProduct.minDepositTermType?.value }} +
+ } + @if (recurringDepositProduct.maxDepositTerm) { +
+ {{ 'labels.inputs.Maximum Deposit Term' | translate }}: + {{ recurringDepositProduct.maxDepositTerm }} {{ recurringDepositProduct.maxDepositTermType?.value }} +
+ } + @if (recurringDepositProduct.inMultipleOfDepositTerm) { +
+ {{ 'labels.inputs.Deposit Term In multiple of' | translate }}: + {{ recurringDepositProduct.inMultipleOfDepositTerm }} - + {{ recurringDepositProduct.inMultipleOfDepositTermType?.value }} +
+ } + @if (recurringDepositProduct.isMandatoryDeposit !== undefined) { +
+ {{ 'labels.inputs.Recurring Deposit Type' | translate }}: + {{ + recurringDepositProduct.isMandatoryDeposit + ? ('labels.text.Mandatory' | translate) + : ('labels.text.Voluntary' | translate) + }} +
+ } + @if (recurringDepositProduct.preClosurePenalInterest) { +
+ {{ 'labels.inputs.Apply penal interest (less)' | translate }}: + {{ recurringDepositProduct.preClosurePenalInterest }}% On + {{ recurringDepositProduct.preClosurePenalInterestOnType?.value }} +
+ } +
+ {{ 'labels.inputs.Withhold Tax is Applicable' | translate }}: + {{ recurringDepositProduct.withHoldTax | yesNo }} +
+ @if (recurringDepositProduct.withHoldTax) { +
+ {{ 'labels.inputs.Tax Group' | translate }}: + {{ recurringDepositProduct.taxGroup?.name }} +
+ } + + + @if (recurringDepositProduct.activeChart) { +
+

{{ 'labels.heading.Interest Rate Charts' | translate }}

+ +
+ @if (recurringDepositProduct.activeChart.name) { +
+ {{ 'labels.inputs.Name' | translate }}: + {{ recurringDepositProduct.activeChart.name }} +
+ }
- {{ 'labels.inputs.Short Name' | translate }}: - {{ recurringDepositProduct.shortName }} + {{ 'labels.inputs.Valid from Date' | translate }}: + {{ recurringDepositProduct.activeChart.fromDate | dateFormat }}
- - @if (recurringDepositProduct.description) { + @if (recurringDepositProduct.activeChart.endDate) { +
+ {{ 'labels.inputs.End Date' | translate }}: + {{ recurringDepositProduct.activeChart.endDate | dateFormat }} +
+ } + @if (recurringDepositProduct.activeChart.description) {
{{ 'labels.inputs.Description' | translate }}: - {{ recurringDepositProduct.description }} + {{ recurringDepositProduct.activeChart.description }}
} - -

{{ 'labels.inputs.Currency' | translate }}

- - -
- {{ 'labels.heading.Currency' | translate }}: - ({{ recurringDepositProduct.currency.code }}) {{ recurringDepositProduct.currency.name }} + {{ 'labels.inputs.Primary Grouping by Amount' | translate }}: + {{ recurringDepositProduct.activeChart.isPrimaryGroupingByAmount | yesNo }}
- -
- {{ 'labels.inputs.Decimal Places' | translate }}: - {{ recurringDepositProduct.currency.decimalPlaces }} -
- -
- {{ 'labels.inputs.Currency in multiples of' | translate }}: - {{ recurringDepositProduct.currency.inMultiplesOf | formatNumber }} -
- -

{{ 'labels.heading.Terms' | translate }}

- - - -
- {{ 'labels.inputs.Deposit Amount' | translate }}: - {{ - recurringDepositProduct.depositAmount + - ' (Min: ' + - (recurringDepositProduct.minDepositAmount - ? recurringDepositProduct.minDepositAmount - : recurringDepositProduct.depositAmount) + - ', Max: ' + - (recurringDepositProduct.maxDepositAmount - ? recurringDepositProduct.maxDepositAmount - : recurringDepositProduct.depositAmount) + - ')' - }} -
- -
- {{ 'labels.inputs.Interest Compounding Period' | translate }}: - {{ recurringDepositProduct.interestCompoundingPeriodType.value }} -
- -
- {{ 'labels.inputs.Interest Posting Period' | translate }}: - {{ recurringDepositProduct.interestPostingPeriodType.value }} -
- -
- {{ 'labels.inputs.Interest Calculated using' | translate }}: - {{ recurringDepositProduct.interestCalculationType.value }} -
- -
- {{ 'labels.inputs.Days in Year' | translate }}: - {{ recurringDepositProduct.interestCalculationDaysInYearType.value }} -
- -

{{ 'labels.heading.Settings' | translate }}

- - - - @if (recurringDepositProduct.lockinPeriodFrequency) { -
- {{ 'labels.inputs.Lock-in Period' | translate }}: - @if (recurringDepositProduct.lockinPeriodFrequency !== undefined) { - - {{ recurringDepositProduct.lockinPeriodFrequency }} - {{ recurringDepositProduct.lockinPeriodFrequencyType.value }} - } -
- } - - @if (recurringDepositProduct.minDepositTerm) { -
- {{ 'labels.inputs.Minimum Deposit Term' | translate }}: - @if (recurringDepositProduct.minDepositTerm !== undefined) { - {{ recurringDepositProduct.minDepositTerm }} -   - {{ recurringDepositProduct.minDepositTermType.value }} - } -
- } - - @if (recurringDepositProduct.maxDepositTerm) { -
- {{ 'labels.inputs.Maximum Deposit Term' | translate }}: - @if (recurringDepositProduct.maxDepositTerm !== undefined) { - {{ recurringDepositProduct.maxDepositTerm }} -   - {{ recurringDepositProduct.maxDepositTermType.value }} - } -
- } - - @if (recurringDepositProduct.inMultipleOfDepositTerm) { -
- {{ 'labels.inputs.Deposit Term In multiple of' | translate }}: - @if (recurringDepositProduct.inMultipleOfDepositTerm !== undefined) { - {{ recurringDepositProduct.inMultipleOfDepositTerm }} - - - {{ recurringDepositProduct.inMultipleOfDepositTermType.value }} - } -
- } - - @if (recurringDepositProduct.isMandatoryDeposit !== undefined) { -
- {{ 'labels.inputs.Recurring Deposit Type' | translate }}: - @if (recurringDepositProduct.isMandatoryDeposit) { - - {{ 'labels.text.Mandatory' | translate }} - - } - @if (!recurringDepositProduct.isMandatoryDeposit) { - - {{ 'labels.text.Voluntary' | translate }} - - } -
- } - - @if (recurringDepositProduct.preClosurePenalInterest) { -
- {{ 'labels.inputs.Apply penal interest (less)' | translate }}: - {{ recurringDepositProduct.preClosurePenalInterest }}%On - {{ recurringDepositProduct.preClosurePenalInterestOnType.value }} - -
- } - -
- {{ 'labels.inputs.Withhold Tax is Applicable' | translate }}: - {{ recurringDepositProduct.withHoldTax | yesNo }} -
- - @if (recurringDepositProduct.withHoldTax) { -
- {{ 'labels.inputs.Withhold Tax group' | translate }}: - {{ recurringDepositProduct.taxGroup.name }} -
- } - - @if (recurringDepositProduct.activeChart) { -
-

{{ 'labels.heading.Interest Rate Charts' | translate }}

- -
- @if (recurringDepositProduct.activeChart.name) { -
- {{ 'labels.inputs.Name' | translate }}: - {{ recurringDepositProduct.activeChart.name }} -
- } -
- {{ 'labels.inputs.Valid from Date' | translate }}: - {{ recurringDepositProduct.activeChart.fromDate | dateFormat }} -
- @if (recurringDepositProduct.activeChart.endDate) { -
- {{ 'labels.inputs.End Date' | translate }}: - {{ recurringDepositProduct.activeChart.endDate | dateFormat }} -
- } - @if (recurringDepositProduct.activeChart.description) { -
- {{ 'labels.inputs.Description' | translate }}: - {{ recurringDepositProduct.activeChart.description }} -
- } -
- {{ 'labels.inputs.Primary Grouping by Amount' | translate }}: - {{ recurringDepositProduct.activeChart.isPrimaryGroupingByAmount | yesNo }} -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{ 'labels.inputs.Period' | translate }} - @if (chartSlab.fromPeriod !== undefined) { -
{{ chartSlab.fromPeriod }} - {{ chartSlab.toPeriod }} {{ chartSlab.periodType.value }}
- } - @if (chartSlab.fromPeriod === undefined) { -
 
- } -
{{ 'labels.inputs.Amount Range' | translate }} - @if (chartSlab.amountRangeFrom !== undefined) { -
{{ chartSlab.amountRangeFrom }} - {{ chartSlab.amountRangeTo }}
- } - @if (chartSlab.amountRangeFrom === undefined) { -
 
- } -
{{ 'labels.inputs.Interest' | translate }} - {{ chartSlab.annualInterestRate }} - {{ 'labels.inputs.Description' | translate }} - {{ chartSlab.description }} - {{ 'labels.inputs.Actions' | translate }} - - -
- -

- {{ 'labels.heading.Incentives' | translate }} -

- - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{ 'labels.inputs.Entity Type' | translate }} - {{ - incentive.entityType - | find - : recurringDepositProductTemplate.chartTemplate.entityTypeOptions - : 'id' - : 'value' - }} - {{ 'labels.inputs.Attribute Name' | translate }} - {{ - incentive.attributeName - | find - : recurringDepositProductTemplate.chartTemplate.attributeNameOptions - : 'id' - : 'value' - }} - {{ 'labels.inputs.Condition Type' | translate }} - {{ - incentive.conditionType - | find - : recurringDepositProductTemplate.chartTemplate.conditionTypeOptions - : 'id' - : 'value' - }} - {{ 'labels.inputs.Attribute Value' | translate }} - @switch (incentive.attributeName) { - @case (2) { - {{ - incentive.attributeValue - | find - : recurringDepositProductTemplate.chartTemplate.genderOptions - : 'id' - : 'name' - }} - } - @case (3) { - {{ incentive.attributeValue }} - } - @case (4) { - {{ - incentive.attributeValue - | find - : recurringDepositProductTemplate.chartTemplate.clientTypeOptions - : 'id' - : 'name' - }} - } - @case (5) { - {{ - incentive.attributeValue - | find - : recurringDepositProductTemplate.chartTemplate.clientClassificationOptions - : 'id' - : 'name' - }} - } - } - {{ 'labels.inputs.Incentive Type' | translate }} - {{ - incentive.incentiveType - | find - : recurringDepositProductTemplate.chartTemplate.incentiveTypeOptions - : 'id' - : 'value' - }} - {{ 'labels.inputs.Amount' | translate }} - {{ incentive.amount }} -
-
-
-
- -
-
- } - - @if (recurringDepositProduct.charges.length) { -
-

{{ 'labels.heading.Charges' | translate }}

- - - - - - - - - - - - - - - - - - - - -
{{ 'labels.inputs.Name' | translate }} - {{ charge.name }} - {{ 'labels.inputs.Type' | translate }} - {{ charge.chargeCalculationType.value }} - {{ 'labels.inputs.Amount' | translate }} - {{ charge.amount | formatNumber }} - {{ 'labels.inputs.Collected On' | translate }} - {{ charge.chargeTimeType.value }} -
-
- } - -

{{ 'labels.heading.Accounting' | translate }}

- - - - - - - @if ( - recurringDepositProduct.paymentChannelToFundSourceMappings?.length || - recurringDepositProduct.feeToIncomeAccountMappings?.length || - recurringDepositProduct.penaltyToIncomeAccountMappings?.length - ) { -
-

{{ 'labels.inputs.Advanced Accounting Rules' | translate }}

- - @if (recurringDepositProduct.paymentChannelToFundSourceMappings?.length) { -
-

- {{ 'labels.inputs.Configure Fund Sources for Payment Channels' | translate }} -

- - - - - - - - - - - -
{{ 'labels.inputs.Payment Type' | translate }} - {{ paymentFundSource.paymentType.name }} - {{ 'labels.inputs.Fund Source' | translate }} - {{ paymentFundSource.fundSourceAccount.name }} -
+ +
{{ 'labels.inputs.Period' | translate }} + @if (chartSlab.fromPeriod !== undefined) { +
{{ chartSlab.fromPeriod }} - {{ chartSlab.toPeriod }} {{ chartSlab.periodType.value }}
+ } + @if (chartSlab.fromPeriod === undefined) { +
 
+ } +
{{ 'labels.inputs.Amount Range' | translate }} + @if (chartSlab.amountRangeFrom !== undefined) { +
{{ chartSlab.amountRangeFrom }} - {{ chartSlab.amountRangeTo }}
+ } + @if (chartSlab.amountRangeFrom === undefined) { +
 
+ } +
{{ 'labels.inputs.Interest' | translate }}{{ chartSlab.annualInterestRate }}{{ 'labels.inputs.Description' | translate }}{{ chartSlab.description }}{{ 'labels.inputs.Actions' | translate }} + + +
+ +

{{ 'labels.heading.Incentives' | translate }}

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ 'labels.inputs.Entity Type' | translate }} + {{ + incentive.entityType + | find: recurringDepositProductTemplate.chartTemplate.entityTypeOptions : 'id' : 'value' + }} + {{ 'labels.inputs.Attribute Name' | translate }} + {{ + incentive.attributeName + | find: recurringDepositProductTemplate.chartTemplate.attributeNameOptions : 'id' : 'value' + }} + {{ 'labels.inputs.Condition Type' | translate }} + {{ + incentive.conditionType + | find: recurringDepositProductTemplate.chartTemplate.conditionTypeOptions : 'id' : 'value' + }} + {{ 'labels.inputs.Attribute Value' | translate }} + @switch (incentive.attributeName) { + @case (2) { + {{ + incentive.attributeValue + | find: recurringDepositProductTemplate.chartTemplate.genderOptions : 'id' : 'name' + }} + } + @case (3) { + {{ incentive.attributeValue }} + } + @case (4) { + {{ + incentive.attributeValue + | find: recurringDepositProductTemplate.chartTemplate.clientTypeOptions : 'id' : 'name' + }} + } + @case (5) { + {{ + incentive.attributeValue + | find + : recurringDepositProductTemplate.chartTemplate.clientClassificationOptions + : 'id' + : 'name' + }} + } + } + {{ 'labels.inputs.Incentive Type' | translate }} + {{ + incentive.incentiveType + | find: recurringDepositProductTemplate.chartTemplate.incentiveTypeOptions : 'id' : 'value' + }} + {{ 'labels.inputs.Amount' | translate }}{{ incentive.amount }}
+
- } - @if (recurringDepositProduct.feeToIncomeAccountMappings?.length) { -
-

{{ 'labels.inputs.Fees to Specific Income Accounts' | translate }}

- - - - - - - - - - - -
{{ 'labels.inputs.Fees' | translate }} - {{ feesIncome.charge.name }} - {{ 'labels.inputs.Income Account' | translate }} - {{ feesIncome.incomeAccount.name }} -
-
- } - @if (recurringDepositProduct.penaltyToIncomeAccountMappings?.length) { -
-

{{ 'labels.inputs.Penalties to Specific Income Accounts' | translate }}

- - - - - - - - - - - -
{{ 'labels.inputs.Penalty' | translate }} - {{ penaltyIncome.charge.name }} - {{ 'labels.inputs.Income Account' | translate }} - {{ penaltyIncome.incomeAccount.name }} -
-
- } - - } +
+
- - + + } + + + @if (recurringDepositProduct.charges.length) { +
+

{{ 'labels.heading.Charges' | translate }}

+ + + + + + + + + + + + + + + + + + + + +
{{ 'labels.inputs.Name' | translate }}{{ charge.name }}{{ 'labels.inputs.Type' | translate }}{{ charge.chargeCalculationType.value }}{{ 'labels.inputs.Amount' | translate }}{{ charge.amount | formatNumber }}{{ 'labels.inputs.Collected On' | translate }}{{ charge.chargeTimeType.value }}
+
+ } + + +

{{ 'labels.heading.Accounting' | translate }}

+ + + + + + @if ( + recurringDepositProduct.paymentChannelToFundSourceMappings?.length || + recurringDepositProduct.feeToIncomeAccountMappings?.length || + recurringDepositProduct.penaltyToIncomeAccountMappings?.length + ) { +
+

{{ 'labels.inputs.Advanced Accounting Rules' | translate }}

+ + @if (recurringDepositProduct.paymentChannelToFundSourceMappings?.length) { +
+

{{ 'labels.inputs.Fund Sources for Payment Channels' | translate }}

+ + + + + + + + + + + +
{{ 'labels.inputs.Payment Type' | translate }}{{ paymentFundSource.paymentType.name }}{{ 'labels.inputs.Fund Source' | translate }}{{ paymentFundSource.fundSourceAccount.name }}
+
+ } + @if (recurringDepositProduct.feeToIncomeAccountMappings?.length) { +
+

{{ 'labels.inputs.Fees to Specific Income Accounts' | translate }}

+ + + + + + + + + + + +
{{ 'labels.inputs.Fee/Charge' | translate }}{{ feesIncome.charge.name }}{{ 'labels.inputs.Income Account' | translate }}{{ feesIncome.incomeAccount.name }}
+
+ } + @if (recurringDepositProduct.penaltyToIncomeAccountMappings?.length) { +
+

{{ 'labels.inputs.Penalties to Specific Income Accounts' | translate }}

+ + + + + + + + + + + +
{{ 'labels.inputs.Penalty' | translate }}{{ penaltyIncome.charge.name }}{{ 'labels.inputs.Income Account' | translate }}{{ penaltyIncome.incomeAccount.name }}
+
+ } +
+ } diff --git a/src/app/products/recurring-deposit-products/view-recurring-deposit-product/recurring-deposit-general-tab/recurring-deposit-general-tab.component.ts b/src/app/products/recurring-deposit-products/view-recurring-deposit-product/recurring-deposit-general-tab/recurring-deposit-general-tab.component.ts index 88331fcaf..4ba8ac022 100644 --- a/src/app/products/recurring-deposit-products/view-recurring-deposit-product/recurring-deposit-general-tab/recurring-deposit-general-tab.component.ts +++ b/src/app/products/recurring-deposit-products/view-recurring-deposit-product/recurring-deposit-general-tab/recurring-deposit-general-tab.component.ts @@ -68,6 +68,7 @@ export class RecurringDepositGeneralTabComponent { recurringDepositProduct: any; recurringDepositProductTemplate: any; + expandChartSlabIndex: number | null = null; chartSlabsIncentivesDisplayedColumns: string[] = ['incentives']; chartSlabsDisplayedColumns: string[] = [ diff --git a/src/app/products/recurring-deposit-products/view-recurring-deposit-product/view-recurring-deposit-product.component.html b/src/app/products/recurring-deposit-products/view-recurring-deposit-product/view-recurring-deposit-product.component.html index 041cff962..4bde6da1a 100644 --- a/src/app/products/recurring-deposit-products/view-recurring-deposit-product/view-recurring-deposit-product.component.html +++ b/src/app/products/recurring-deposit-products/view-recurring-deposit-product/view-recurring-deposit-product.component.html @@ -18,6 +18,15 @@ > {{ 'labels.heading.General' | translate }} + + CBS Base Chart + @for (recurringDepositDatatable of recurringDepositDatatables; track recurringDepositDatatable) {