diff --git a/src/app/products/fixed-deposit-products/create-fixed-deposit-product/create-fixed-deposit-product.component.html b/src/app/products/fixed-deposit-products/create-fixed-deposit-product/create-fixed-deposit-product.component.html index 93986e809..ba2cf8fdd 100644 --- a/src/app/products/fixed-deposit-products/create-fixed-deposit-product/create-fixed-deposit-product.component.html +++ b/src/app/products/fixed-deposit-products/create-fixed-deposit-product/create-fixed-deposit-product.component.html @@ -24,7 +24,9 @@ {{ 'labels.inputs.DETAILS' | translate }}
- +
@@ -48,6 +50,7 @@
@@ -147,27 +150,40 @@ [fixedDepositProductsTemplate]="fixedDepositProductsTemplate" [accountingRuleData]="accountingRuleData" [fixedDepositProductFormValid]="fixedDepositProductFormValid" + [isDormancyTrackingActive]="fixedDepositProductCurrencyForm.get('dormancyAlert')" > - @if (fixedDepositProductFormValid) { - - - - {{ 'labels.inputs.PREVIEW' | translate }} - -
+ + + + {{ 'labels.inputs.PREVIEW' | translate }} + +
+ @if (fixedDepositProductFormValid) { -
-
- } + } @else { +
+ +

Please complete all required fields

+

+ Look for the icon on each tab to find + which steps still need attention. +

+
+ } +
+
diff --git a/src/app/products/fixed-deposit-products/create-fixed-deposit-product/create-fixed-deposit-product.component.ts b/src/app/products/fixed-deposit-products/create-fixed-deposit-product/create-fixed-deposit-product.component.ts index 449082120..e9517e10e 100644 --- a/src/app/products/fixed-deposit-products/create-fixed-deposit-product/create-fixed-deposit-product.component.ts +++ b/src/app/products/fixed-deposit-products/create-fixed-deposit-product/create-fixed-deposit-product.component.ts @@ -33,6 +33,7 @@ import { /** Custom Services */ import { ProductsService } from 'app/products/products.service'; import { SettingsService } from 'app/settings/settings.service'; +import { Dates } from 'app/core/utils/dates'; import { Accounting } from 'app/core/utils/accounting'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { FixedDepositProductPreviewStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-preview-step/fixed-deposit-product-preview-step.component'; @@ -71,6 +72,7 @@ export class CreateFixedDepositProductComponent { private productsService = inject(ProductsService); private router = inject(Router); private settingsService = inject(SettingsService); + private dateUtils = inject(Dates); private accounting = inject(Accounting); @ViewChild(FixedDepositProductDetailsStepComponent, { static: true }) @@ -98,13 +100,20 @@ export class CreateFixedDepositProductComponent { 'periodTypeLabel', 'annualInterestRate' ]; + readonly previewChartSlabColumns = [ + 'period', + 'amountRange', + 'annualInterestRate', + 'description', + 'actions' + ]; constructor() { this.route.data.subscribe((data: { fixedDepositProductsTemplate: any }) => { this.fixedDepositProductsTemplate = data.fixedDepositProductsTemplate; }); this.accountingRuleData = this.accounting.getAccountingRulesForSavings(); - this.productsService.getRdBaseInterestRates().subscribe({ + this.productsService.getFdBaseInterestRates().subscribe({ next: (rates: any[]) => { this.fdInterestRates = rates || []; }, @@ -115,41 +124,41 @@ export class CreateFixedDepositProductComponent { } get fixedDepositProductDetailsForm() { - return this.fixedDepositProductDetailsStep.fixedDepositProductDetailsForm; + return this.fixedDepositProductDetailsStep?.fixedDepositProductDetailsForm; } get fixedDepositProductCurrencyForm() { - return this.fixedDepositProductCurrencyStep.fixedDepositProductCurrencyForm; + return this.fixedDepositProductCurrencyStep?.fixedDepositProductCurrencyForm; } get fixedDepositProductTermsForm() { - return this.fixedDepositProductTermsStep.fixedDepositProductTermsForm; + return this.fixedDepositProductTermsStep?.fixedDepositProductTermsForm; } get fixedDepositProductSettingsForm() { - return this.fixedDepositProductSettingsStep.fixedDepositProductSettingsForm; + return this.fixedDepositProductSettingsStep?.fixedDepositProductSettingsForm; } get fixedDepositProductAccountingForm() { - return this.fixedDepositProductAccountingStep.fixedDepositProductAccountingForm; + return this.fixedDepositProductAccountingStep?.fixedDepositProductAccountingForm; } get totalTabs(): number { - return this.fixedDepositProductFormValid ? 8 : 7; + return 8; } get fixedDepositProductFormValid() { - return ( - this.fixedDepositProductDetailsForm.valid && - this.fixedDepositProductCurrencyForm.valid && - this.fixedDepositProductTermsForm.valid && - this.fixedDepositProductSettingsForm.valid && - this.fixedDepositProductAccountingForm.valid - ); + return !!this.fixedDepositProductDetailsForm?.valid; + } + + get selectedFdType(): string | null { + return this.fixedDepositProductDetailsStep?.fixedDepositProductDetailsForm?.get('fdType')?.value ?? null; } get activeBaseVersion() { - return this.fdInterestRates.find((v: any) => v.isActive) ?? this.fdInterestRates[0] ?? null; + const fdType = this.selectedFdType; + const filtered = fdType ? this.fdInterestRates.filter((v: any) => v.fdType === fdType) : this.fdInterestRates; + return filtered.find((v: any) => v.isActive) ?? filtered[0] ?? null; } get activeBaseSlabs() { @@ -164,16 +173,97 @@ export class CreateFixedDepositProductComponent { ...this.fixedDepositProductSettingsStep.fixedDepositProductSettings, ...this.fixedDepositProductChargesStep.fixedDepositProductCharges, ...this.fixedDepositProductAccountingStep.fixedDepositProductAccounting, - fdSlabs: this.activeBaseSlabs + charts: this.fixedDepositProductCharts, + fdSlabs: this.activeBaseVersion?.slabs || [] }; } + get fixedDepositProductCharts() { + const activeBaseVersion = this.activeBaseVersion; + if (!activeBaseVersion?.slabs?.length) { + return []; + } + const dateFormat = this.settingsService.dateFormat; + + return [ + { + name: activeBaseVersion.versionLabel || 'CBS Base Chart', + description: activeBaseVersion.remarks || activeBaseVersion.versionLabel || 'CBS base chart', + fromDate: + this.dateUtils.formatDate(activeBaseVersion.effectiveFromDate, dateFormat) || + activeBaseVersion.effectiveFromDate, + ...(activeBaseVersion.effectiveToDate + ? { + endDate: + this.dateUtils.formatDate(activeBaseVersion.effectiveToDate, dateFormat) || + activeBaseVersion.effectiveToDate + } + : {}), + isPrimaryGroupingByAmount: false, + locale: this.settingsService.language.code, + dateFormat, + chartSlabs: activeBaseVersion.slabs.map((slab: any) => { + const chartSlab: any = { + periodType: slab.periodTypeEnum, + fromPeriod: slab.fromPeriod, + toPeriod: slab.toPeriod, + annualInterestRate: slab.annualInterestRate, + description: slab.description || '', + incentives: [] as any[] + }; + + if (slab.amountRangeFrom !== null && slab.amountRangeFrom !== undefined && slab.amountRangeFrom !== '') { + chartSlab.amountRangeFrom = slab.amountRangeFrom; + } + + if (slab.amountRangeTo !== null && slab.amountRangeTo !== undefined && slab.amountRangeTo !== '') { + chartSlab.amountRangeTo = slab.amountRangeTo; + } + + return chartSlab; + }) + } + ] as any[]; + } + + private pruneEmptyValues(value: T): T { + if (Array.isArray(value)) { + return value.map((item) => this.pruneEmptyValues(item)).filter((item) => item !== undefined) as T; + } + + if (value && typeof value === 'object') { + return Object.entries(value as Record).reduce( + (acc, [ + key, + currentValue + ]) => { + const cleanedValue = this.pruneEmptyValues(currentValue); + if (cleanedValue === undefined) { + return acc; + } + return { + ...acc, + [key]: cleanedValue + }; + }, + {} as Record + ) as T; + } + + if (value === '' || value === null || value === undefined) { + return undefined as T; + } + + return value; + } + submit() { - const fixedDepositProduct = { + const fixedDepositProduct: any = this.pruneEmptyValues({ ...this.fixedDepositProduct, - charges: this.fixedDepositProduct.charges.map((charge: any) => ({ id: charge.id })), + charts: this.fixedDepositProductCharts, + charges: (this.fixedDepositProduct.charges ?? []).map((charge: any) => ({ id: charge.id })), locale: this.settingsService.language.code - }; + }); delete fixedDepositProduct.advancedAccountingRules; this.productsService.createFixedDepositProduct(fixedDepositProduct).subscribe((response: any) => { this.router.navigate( diff --git a/src/app/products/fixed-deposit-products/edit-fixed-deposit-product/edit-fixed-deposit-product.component.html b/src/app/products/fixed-deposit-products/edit-fixed-deposit-product/edit-fixed-deposit-product.component.html index 1d41c96f0..ee9f6f9f0 100644 --- a/src/app/products/fixed-deposit-products/edit-fixed-deposit-product/edit-fixed-deposit-product.component.html +++ b/src/app/products/fixed-deposit-products/edit-fixed-deposit-product/edit-fixed-deposit-product.component.html @@ -6,104 +6,197 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. --> -
- +
+ +

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

+
+ + - - - - - - - - - - - - - - - - - - - - - - {{ 'labels.inputs.DETAILS' | translate }} - - - - - - - {{ 'labels.inputs.CURRENCY' | translate }} - - - - - - - {{ 'labels.inputs.TERMS' | translate }} - - - - - - - {{ 'labels.inputs.SETTINGS' | translate }} - - - - - - - {{ 'labels.inputs.INTEREST RATE CHART' | translate }} - - - - - - - {{ 'labels.inputs.CHARGES' | translate }} - - - - - - - {{ 'labels.inputs.ACCOUNTING' | translate }} - - - - - - @if (fixedDepositProductFormValidAndNotPrinstine) { - - {{ 'labels.inputs.PREVIEW' | translate }} - + + + {{ 'labels.inputs.DETAILS' | translate }} + +
+ +
+ + + + + + {{ 'labels.inputs.CURRENCY' | translate }} + +
+ +
+
+ + + + + {{ 'labels.inputs.TERMS' | translate }} + +
+ +
+
+ + + + + {{ 'labels.inputs.SETTINGS' | translate }} + +
+ +
+
+ + + + + {{ 'labels.inputs.CHARGES' | 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...

+ } +
+
+ + + + + {{ 'labels.inputs.ACCOUNTING' | translate }} + +
+ - - + [fixedDepositProductFormValid]="fixedDepositProductFormValid" + [isDormancyTrackingActive]="fixedDepositProductCurrencyForm?.get('dormancyAlert')" + > +
+
+ + @if (fixedDepositProductFormValid) { + + + + {{ 'labels.inputs.PREVIEW' | translate }} + +
+ +
+
} -
+ + +
+ +
+ @if (selectedTabIndex > 0) { + + } + @if (selectedTabIndex < totalTabs - 1) { + + } + @if (fixedDepositProductFormValid && selectedTabIndex === totalTabs - 1) { + + } +
+
diff --git a/src/app/products/fixed-deposit-products/edit-fixed-deposit-product/edit-fixed-deposit-product.component.scss b/src/app/products/fixed-deposit-products/edit-fixed-deposit-product/edit-fixed-deposit-product.component.scss index af9f4c075..cb2ce12ba 100644 --- a/src/app/products/fixed-deposit-products/edit-fixed-deposit-product/edit-fixed-deposit-product.component.scss +++ b/src/app/products/fixed-deposit-products/edit-fixed-deposit-product/edit-fixed-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; + +.fdp-container { + max-width: 960px; + margin: 0 auto; + padding: 24px 16px; +} + +.fdp-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; + } +} + +.fdp-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: 120px; + padding: 0 16px; + 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; + } + } +} + +.fdp-tab-icon { + margin-right: 8px; + font-size: 14px; +} + +.fdp-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; +} + +.fdp-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/fixed-deposit-products/edit-fixed-deposit-product/edit-fixed-deposit-product.component.ts b/src/app/products/fixed-deposit-products/edit-fixed-deposit-product/edit-fixed-deposit-product.component.ts index 6129b383c..a02ad4d3d 100644 --- a/src/app/products/fixed-deposit-products/edit-fixed-deposit-product/edit-fixed-deposit-product.component.ts +++ b/src/app/products/fixed-deposit-products/edit-fixed-deposit-product/edit-fixed-deposit-product.component.ts @@ -15,15 +15,27 @@ import { FixedDepositProductDetailsStepComponent } from '../fixed-deposit-produc import { FixedDepositProductCurrencyStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-currency-step/fixed-deposit-product-currency-step.component'; import { FixedDepositProductTermsStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-terms-step/fixed-deposit-product-terms-step.component'; import { FixedDepositProductSettingsStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-settings-step/fixed-deposit-product-settings-step.component'; -import { FixedDepositProductInterestRateChartStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-interest-rate-chart-step/fixed-deposit-product-interest-rate-chart-step.component'; import { FixedDepositProductChargesStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-charges-step/fixed-deposit-product-charges-step.component'; import { FixedDepositProductAccountingStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-accounting-step/fixed-deposit-product-accounting-step.component'; /** Custom Services */ import { ProductsService } from 'app/products/products.service'; import { SettingsService } from 'app/settings/settings.service'; +import { MatTabsModule } from '@angular/material/tabs'; +import { + MatTable, + MatColumnDef, + MatHeaderCellDef, + MatHeaderCell, + MatCellDef, + MatCell, + MatHeaderRowDef, + MatHeaderRow, + MatRowDef, + MatRow +} from '@angular/material/table'; +import { Dates } from 'app/core/utils/dates'; import { Accounting } from 'app/core/utils/accounting'; -import { MatStepper, MatStepperIcon, MatStep, MatStepLabel } from '@angular/material/stepper'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { FixedDepositProductPreviewStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-preview-step/fixed-deposit-product-preview-step.component'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; @@ -34,16 +46,22 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; styleUrls: ['./edit-fixed-deposit-product.component.scss'], imports: [ ...STANDALONE_SHARED_IMPORTS, - MatStepper, - MatStepperIcon, + MatTabsModule, FaIconComponent, - MatStep, - MatStepLabel, + MatTable, + MatColumnDef, + MatHeaderCellDef, + MatHeaderCell, + MatCellDef, + MatCell, + MatHeaderRowDef, + MatHeaderRow, + MatRowDef, + MatRow, FixedDepositProductDetailsStepComponent, FixedDepositProductCurrencyStepComponent, FixedDepositProductTermsStepComponent, FixedDepositProductSettingsStepComponent, - FixedDepositProductInterestRateChartStepComponent, FixedDepositProductChargesStepComponent, FixedDepositProductAccountingStepComponent, FixedDepositProductPreviewStepComponent @@ -55,6 +73,7 @@ export class EditFixedDepositProductComponent { private router = inject(Router); private settingsService = inject(SettingsService); private accounting = inject(Accounting); + private dateUtils = inject(Dates); @ViewChild(FixedDepositProductDetailsStepComponent, { static: true }) fixedDepositProductDetailsStep: FixedDepositProductDetailsStepComponent; @@ -64,8 +83,6 @@ export class EditFixedDepositProductComponent { fixedDepositProductTermsStep: FixedDepositProductTermsStepComponent; @ViewChild(FixedDepositProductSettingsStepComponent, { static: true }) fixedDepositProductSettingsStep: FixedDepositProductSettingsStepComponent; - @ViewChild(FixedDepositProductInterestRateChartStepComponent, { static: true }) - fixedDepositProductInterestRateChartStep: FixedDepositProductInterestRateChartStepComponent; @ViewChild(FixedDepositProductChargesStepComponent, { static: true }) fixedDepositProductChargesStep: FixedDepositProductChargesStepComponent; @ViewChild(FixedDepositProductAccountingStepComponent, { static: true }) @@ -73,6 +90,23 @@ export class EditFixedDepositProductComponent { fixedDepositProductsTemplate: any; accountingRuleData: string[] = []; + selectedTabIndex = 0; + fdInterestRates: any[] = []; + readonly fdSlabColumns = [ + 'srNo', + 'description', + 'fromPeriod', + 'toPeriod', + 'periodTypeLabel', + 'annualInterestRate' + ]; + readonly previewChartSlabColumns = [ + 'period', + 'amountRange', + 'annualInterestRate', + 'description', + 'actions' + ]; /** * @param {ActivatedRoute} route Activated Route. @@ -86,47 +120,42 @@ export class EditFixedDepositProductComponent { this.fixedDepositProductsTemplate = data.fixedDepositProductAndTemplate; }); this.accountingRuleData = this.accounting.getAccountingRulesForSavings(); + this.productsService.getFdBaseInterestRates().subscribe({ + next: (rates: any[]) => { + this.fdInterestRates = rates || []; + }, + error: () => { + this.fdInterestRates = []; + } + }); } get fixedDepositProductDetailsForm() { - return this.fixedDepositProductDetailsStep.fixedDepositProductDetailsForm; + return this.fixedDepositProductDetailsStep?.fixedDepositProductDetailsForm; } get fixedDepositProductCurrencyForm() { - return this.fixedDepositProductCurrencyStep.fixedDepositProductCurrencyForm; + return this.fixedDepositProductCurrencyStep?.fixedDepositProductCurrencyForm; } get fixedDepositProductTermsForm() { - return this.fixedDepositProductTermsStep.fixedDepositProductTermsForm; + return this.fixedDepositProductTermsStep?.fixedDepositProductTermsForm; } get fixedDepositProductSettingsForm() { - return this.fixedDepositProductSettingsStep.fixedDepositProductSettingsForm; - } - - get fixedDepositProductInterestRateChartForm() { - return this.fixedDepositProductInterestRateChartStep.fixedDepositProductInterestRateChartForm; + return this.fixedDepositProductSettingsStep?.fixedDepositProductSettingsForm; } get fixedDepositProductAccountingForm() { - return this.fixedDepositProductAccountingStep.fixedDepositProductAccountingForm; + return this.fixedDepositProductAccountingStep?.fixedDepositProductAccountingForm; } - get fixedDepositProductFormValidAndNotPrinstine() { - return ( - this.fixedDepositProductDetailsForm.valid && - this.fixedDepositProductCurrencyForm.valid && - this.fixedDepositProductTermsForm.valid && - this.fixedDepositProductSettingsForm.valid && - this.fixedDepositProductInterestRateChartForm.valid && - this.fixedDepositProductAccountingForm.valid && - (this.fixedDepositProductDetailsForm.pristine || - this.fixedDepositProductCurrencyForm.pristine || - this.fixedDepositProductTermsForm.pristine || - this.fixedDepositProductSettingsForm.pristine || - this.fixedDepositProductInterestRateChartForm.pristine || - this.fixedDepositProductAccountingForm.pristine) - ); + get totalTabs(): number { + return this.fixedDepositProductFormValid ? 8 : 7; + } + + get fixedDepositProductFormValid() { + return !!this.fixedDepositProductDetailsForm?.valid; } get fixedDepositProduct() { @@ -135,33 +164,118 @@ export class EditFixedDepositProductComponent { ...this.fixedDepositProductCurrencyStep.fixedDepositProductCurrency, ...this.fixedDepositProductTermsStep.fixedDepositProductTerms, ...this.fixedDepositProductSettingsStep.fixedDepositProductSettings, - ...this.fixedDepositProductInterestRateChartStep.fixedDepositProductInterestRateChart, ...this.fixedDepositProductChargesStep.fixedDepositProductCharges, - ...this.fixedDepositProductAccountingStep.fixedDepositProductAccounting + ...this.fixedDepositProductAccountingStep.fixedDepositProductAccounting, + charts: this.fixedDepositProductCharts, + fdSlabs: this.activeBaseVersion?.slabs || [] }; } - submit() { - const fixedDepositProduct = { - ...this.fixedDepositProduct, - charges: this.fixedDepositProduct.charges.map((charge: any) => ({ id: charge.id })), - locale: this.settingsService.language.code // locale required for depositAmount - }; - if (!fixedDepositProduct.description) { - fixedDepositProduct.description = ''; - } - const charts: any[] = []; - fixedDepositProduct.charts.forEach((chart: any) => { - if (chart['amountRangeFrom'] === '') { - delete chart['amountRangeFrom']; - } - if (chart['amountRangeTo'] === '') { - delete chart['amountRangeTo']; - } - charts.push(chart); - }); - fixedDepositProduct.charts = charts; + get selectedFdType(): string | null { + return this.fixedDepositProductDetailsStep?.fixedDepositProductDetailsForm?.get('fdType')?.value ?? null; + } + get activeBaseVersion() { + const fdType = this.selectedFdType; + const filtered = fdType ? this.fdInterestRates.filter((v: any) => v.fdType === fdType) : this.fdInterestRates; + return filtered.find((v: any) => v.isActive) ?? filtered[0] ?? null; + } + + get activeBaseSlabs() { + return this.activeBaseVersion?.slabs || []; + } + + get fixedDepositProductCharts() { + const activeBaseVersion = this.activeBaseVersion; + if (!activeBaseVersion?.slabs?.length) { + return []; + } + const dateFormat = this.settingsService.dateFormat; + const formattedFromDate = + this.dateUtils.formatDate(activeBaseVersion.effectiveFromDate, dateFormat) || activeBaseVersion.effectiveFromDate; + + // Find the existing chart from the template to UPDATE it instead of creating a duplicate + let existingChartId: number | undefined; + let existingChartSlabs: any[] = []; + const templateCharts: any[] = this.fixedDepositProductsTemplate?.charts ?? []; + if (templateCharts.length) { + const match = templateCharts.find((c: any) => { + const d = c.fromDate; + if (Array.isArray(d)) { + const formatted = this.dateUtils.formatDate(new Date(d[0], d[1] - 1, d[2]), dateFormat); + return formatted === formattedFromDate; + } + return d === formattedFromDate; + }); + const matchedChart = match ?? templateCharts[0]; + existingChartId = matchedChart?.id; + existingChartSlabs = matchedChart?.chartSlabs ?? []; + } + if (!existingChartId && this.fixedDepositProductsTemplate?.activeChart?.id) { + existingChartId = this.fixedDepositProductsTemplate.activeChart.id; + existingChartSlabs = this.fixedDepositProductsTemplate.activeChart.chartSlabs ?? []; + } + + // Build a lookup: fromPeriod+toPeriod → existing slab id (to update existing slabs, not add new ones) + const existingSlabIdMap = new Map(); + for (const slab of existingChartSlabs) { + const key = `${slab.fromPeriod ?? ''}_${slab.toPeriod ?? ''}`; + if (slab.id) { + existingSlabIdMap.set(key, slab.id); + } + } + + return [ + { + ...(existingChartId ? { id: existingChartId } : {}), + name: activeBaseVersion.versionLabel || 'CBS Base Chart', + description: activeBaseVersion.remarks || activeBaseVersion.versionLabel || 'CBS base chart', + fromDate: formattedFromDate, + ...(activeBaseVersion.effectiveToDate + ? { + endDate: + this.dateUtils.formatDate(activeBaseVersion.effectiveToDate, dateFormat) || + activeBaseVersion.effectiveToDate + } + : {}), + isPrimaryGroupingByAmount: false, + locale: this.settingsService.language.code, + dateFormat, + chartSlabs: activeBaseVersion.slabs.map((slab: any) => { + const key = `${slab.fromPeriod ?? ''}_${slab.toPeriod ?? ''}`; + const existingSlabId = existingSlabIdMap.get(key); + + const chartSlab: any = { + ...(existingSlabId ? { id: existingSlabId } : {}), + periodType: slab.periodTypeEnum, + fromPeriod: slab.fromPeriod, + toPeriod: slab.toPeriod, + annualInterestRate: slab.annualInterestRate, + description: slab.description || '', + incentives: [] as any[] + }; + + if (slab.amountRangeFrom !== null && slab.amountRangeFrom !== undefined && slab.amountRangeFrom !== '') { + chartSlab.amountRangeFrom = slab.amountRangeFrom; + } + + if (slab.amountRangeTo !== null && slab.amountRangeTo !== undefined && slab.amountRangeTo !== '') { + chartSlab.amountRangeTo = slab.amountRangeTo; + } + + return chartSlab; + }) + } + ] as any[]; + } + + submit() { + const fixedDepositProduct: any = this.pruneEmptyValues({ + ...this.fixedDepositProduct, + charts: this.fixedDepositProductCharts, + charges: (this.fixedDepositProduct.charges ?? []).map((charge: any) => ({ id: charge.id })), + locale: this.settingsService.language.code + }); delete fixedDepositProduct.advancedAccountingRules; this.productsService .updateFixedDepositProduct(this.fixedDepositProductsTemplate.id, fixedDepositProduct) @@ -169,4 +283,35 @@ export class EditFixedDepositProductComponent { this.router.navigate(['../'], { relativeTo: this.route }); }); } + + private pruneEmptyValues(value: T): T { + if (Array.isArray(value)) { + return value.map((item) => this.pruneEmptyValues(item)).filter((item) => item !== undefined) as T; + } + + if (value && typeof value === 'object') { + return Object.entries(value as Record).reduce( + (acc, [ + key, + currentValue + ]) => { + const cleanedValue = this.pruneEmptyValues(currentValue); + if (cleanedValue === undefined) { + return acc; + } + return { + ...acc, + [key]: cleanedValue + }; + }, + {} as Record + ) as T; + } + + if (value === '' || value === null || value === undefined) { + return undefined as T; + } + + return value; + } } diff --git a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-accounting-step/fixed-deposit-product-accounting-step.component.html b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-accounting-step/fixed-deposit-product-accounting-step.component.html index 2df0b809d..8cf56ba2b 100644 --- a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-accounting-step/fixed-deposit-product-accounting-step.component.html +++ b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-accounting-step/fixed-deposit-product-accounting-step.component.html @@ -36,7 +36,9 @@
@for (row of section.rows; track row.key) { - @if (!row.accrualOnly || isAccrualAccounting()) { + @if ( + (!row.accrualOnly || isAccrualAccounting()) && (!row.dormancyOnly || isDormancyTrackingEnabled()) + ) {
v.length) ? otherGlMappings : null; } + private setDormancyControl() { + this.isDormancyTrackingActive?.valueChanges?.subscribe(() => { + this.syncAccountingControls(this.fixedDepositProductAccountingForm.value.accountingRule); + }); + } + + isDormancyTrackingEnabled(): boolean { + return this.isDormancyTrackingActive?.value ?? this.isDormancyTrackingActive ?? false; + } + private syncAccountingControls(accountingRule: number) { - const addControl = (name: string, required = false) => { - if (!this.fixedDepositProductAccountingForm.contains(name)) { - this.fixedDepositProductAccountingForm.addControl( - name, - new UntypedFormControl('', required ? Validators.required : undefined) - ); - } else if (required) { - this.fixedDepositProductAccountingForm.get(name)?.setValidators(Validators.required); - this.fixedDepositProductAccountingForm.get(name)?.updateValueAndValidity({ emitEvent: false }); - } - }; - const removeControl = (name: string) => { - if (this.fixedDepositProductAccountingForm.contains(name)) { - this.fixedDepositProductAccountingForm.removeControl(name); - } - }; - - if (this.accounting.isCashOrAccrualAccountingRuleId(accountingRule)) { - addControl('savingsReferenceAccountId', true); - addControl('savingsControlAccountId', true); - addControl('transfersInSuspenseAccountId', true); - addControl('interestOnSavingsAccountId', true); - addControl('incomeFromFeeAccountId', true); - addControl('incomeFromPenaltyAccountId', true); - - if (this.accounting.isAccrualAccountingRuleId(accountingRule)) { - addControl('feesReceivableAccountId', true); - addControl('penaltiesReceivableAccountId', true); - addControl('interestPayableAccountId', true); + const setRequired = (name: string, required: boolean) => { + const ctrl = this.fixedDepositProductAccountingForm.get(name); + if (!ctrl) return; + if (required) { + ctrl.setValidators(Validators.required); } else { - removeControl('feesReceivableAccountId'); - removeControl('penaltiesReceivableAccountId'); - removeControl('interestPayableAccountId'); + ctrl.clearValidators(); } - } else { - [ - 'savingsReferenceAccountId', - 'savingsControlAccountId', - 'transfersInSuspenseAccountId', - 'interestOnSavingsAccountId', - 'incomeFromFeeAccountId', - 'incomeFromPenaltyAccountId', - 'feesReceivableAccountId', - 'penaltiesReceivableAccountId', - 'interestPayableAccountId' - ].forEach(removeControl); + ctrl.updateValueAndValidity({ emitEvent: false }); + }; + + const cashOrAccrual = this.accounting.isCashOrAccrualAccountingRuleId(accountingRule); + const accrualOnly = this.accounting.isAccrualAccountingRuleId(accountingRule); + + setRequired('savingsReferenceAccountId', cashOrAccrual); + setRequired('savingsControlAccountId', cashOrAccrual); + setRequired('transfersInSuspenseAccountId', cashOrAccrual); + setRequired('interestOnSavingsAccountId', cashOrAccrual); + setRequired('incomeFromFeeAccountId', cashOrAccrual); + setRequired('incomeFromPenaltyAccountId', cashOrAccrual); + setRequired('feesReceivableAccountId', accrualOnly); + setRequired('penaltiesReceivableAccountId', accrualOnly); + setRequired('interestPayableAccountId', accrualOnly); + setRequired('escheatLiabilityId', cashOrAccrual && this.isDormancyTrackingEnabled()); + + if (!cashOrAccrual) { this.clearOtherGlMappings(); } } @@ -331,6 +381,14 @@ export class FixedDepositProductAccountingStepComponent implements OnInit { controlName: 'interestPayableAccountId', options: this.liabilityAccountOptions, accrualOnly: true + }, + { + key: 'escheatLiabilityId', + label: 'Escheat Liability Account', + inputLabel: 'Escheat liability account', + controlName: 'escheatLiabilityId', + options: this.liabilityAccountOptions, + dormancyOnly: true } ], otherMappingsControlName: 'liabilitiesOtherGlMappings', @@ -392,7 +450,23 @@ export class FixedDepositProductAccountingStepComponent implements OnInit { ...accounting } = this.fixedDepositProductAccountingForm.value; const otherGlMappings = this.getOtherGlMappings(); - return { ...accounting, ...(otherGlMappings ? { otherGlMappings } : {}) }; + const cleanedAccounting = Object.entries(accounting).reduce( + (acc, [ + key, + value + ]) => { + if (value === '' || value === null || value === undefined) { + return acc; + } + return { + ...acc, + [key]: value + }; + }, + {} as Record + ); + + return { ...cleanedAccounting, ...(otherGlMappings ? { otherGlMappings } : {}) }; } isCashOrAccrualAccounting(): boolean { diff --git a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-currency-step/fixed-deposit-product-currency-step.component.html b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-currency-step/fixed-deposit-product-currency-step.component.html index df8a5251d..fc75cf7cc 100644 --- a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-currency-step/fixed-deposit-product-currency-step.component.html +++ b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-currency-step/fixed-deposit-product-currency-step.component.html @@ -63,27 +63,30 @@
-
-
-

Balance Swap Option

-

Enable automated transfer of excess funds to linked deposits or vice-versa to maintain liquidity.

-
- - yes - no - -
+ @if (fixedDepositProductCurrencyForm.get('dormancyAlert')?.value === true) { +
+ + {{ 'labels.inputs.Days to Inactive' | translate }} + + {{ 'labels.inputs.Days to Inactive' | translate }} is required + -
-
-

MSME

-

Micro, Small and Medium Enterprises

+ + {{ 'labels.inputs.Days to Dormancy' | translate }} + + {{ 'labels.inputs.Days to Dormancy' | translate }} is required + + + + {{ 'labels.inputs.Days to Escheat' | translate }} + + {{ 'labels.inputs.Days to Escheat' | translate }} is required +
- - yes - no - -
+ }
diff --git a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-currency-step/fixed-deposit-product-currency-step.component.scss b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-currency-step/fixed-deposit-product-currency-step.component.scss index f32a534a6..0c037753d 100644 --- a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-currency-step/fixed-deposit-product-currency-step.component.scss +++ b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-currency-step/fixed-deposit-product-currency-step.component.scss @@ -15,7 +15,6 @@ $gray-900: #202124; .fdpc-section { border: 1px solid $border; border-radius: 10px; - overflow: hidden; background: #fff; & + & { diff --git a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-currency-step/fixed-deposit-product-currency-step.component.ts b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-currency-step/fixed-deposit-product-currency-step.component.ts index 6e0466dfa..c8ff0a435 100644 --- a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-currency-step/fixed-deposit-product-currency-step.component.ts +++ b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-currency-step/fixed-deposit-product-currency-step.component.ts @@ -51,8 +51,10 @@ export class FixedDepositProductCurrencyStepComponent implements OnInit { this.fixedDepositProductsTemplate.currency?.decimalPlaces ?? 2, dormancyAlert: this.fixedDepositProductsTemplate.dormancyAlert ?? false, - balanceSwapOption: this.fixedDepositProductsTemplate.balanceSwapOption ?? false, - msme: this.fixedDepositProductsTemplate.msme ?? false, + isDormancyTrackingActive: this.fixedDepositProductsTemplate.isDormancyTrackingActive ?? false, + daysToInactive: this.fixedDepositProductsTemplate.daysToInactive ?? 30, + daysToDormancy: this.fixedDepositProductsTemplate.daysToDormancy ?? 60, + daysToEscheat: this.fixedDepositProductsTemplate.daysToEscheat ?? 90, nominationFacility: this.fixedDepositProductsTemplate.nominationFacility ?? false, autoDebitFacility: this.fixedDepositProductsTemplate.autoDebitFacility ?? false }); @@ -72,8 +74,10 @@ export class FixedDepositProductCurrencyStepComponent implements OnInit { ] ], dormancyAlert: [false], - balanceSwapOption: [false], - msme: [false], + isDormancyTrackingActive: [false], + daysToInactive: [30], + daysToDormancy: [60], + daysToEscheat: [90], nominationFacility: [false], autoDebitFacility: [false] }); @@ -81,14 +85,21 @@ export class FixedDepositProductCurrencyStepComponent implements OnInit { get fixedDepositProductCurrency() { const formValue = this.fixedDepositProductCurrencyForm.value; - return { + const result: any = { currencyCode: formValue.currencyCode, digitsAfterDecimal: formValue.digitsAfterDecimal, dormancyAlert: formValue.dormancyAlert, - balanceSwapOption: formValue.balanceSwapOption, - msme: formValue.msme, nominationFacility: formValue.nominationFacility, autoDebitFacility: formValue.autoDebitFacility }; + if (formValue.dormancyAlert) { + result.isDormancyTrackingActive = true; + result.daysToInactive = formValue.daysToInactive; + result.daysToDormancy = formValue.daysToDormancy; + result.daysToEscheat = formValue.daysToEscheat; + } else { + result.isDormancyTrackingActive = false; + } + return result; } } diff --git a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-details-step/fixed-deposit-product-details-step.component.html b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-details-step/fixed-deposit-product-details-step.component.html index 98add2dc8..a00fbdef9 100644 --- a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-details-step/fixed-deposit-product-details-step.component.html +++ b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-details-step/fixed-deposit-product-details-step.component.html @@ -79,12 +79,13 @@ - Customer Segment - - @for (customerSegment of customerSegmentData; track customerSegment) { - {{ customerSegment }} - } + FD Type + + FD Single (Days < 1 Year) + FD Double (≥ 1 Year) + FD Special (Monthly Interest Payout) + FD Type is required @@ -110,15 +111,6 @@ - - Introducer Required - - @for (option of yesNoOptions; track option.value) { - {{ option.label }} - } - - -
Deposit Account Link
diff --git a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-details-step/fixed-deposit-product-details-step.component.scss b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-details-step/fixed-deposit-product-details-step.component.scss index 59c20ba02..2e487792e 100644 --- a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-details-step/fixed-deposit-product-details-step.component.scss +++ b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-details-step/fixed-deposit-product-details-step.component.scss @@ -15,7 +15,6 @@ $gray-900: #202124; .fdpd-section { border: 1px solid $border; border-radius: 10px; - overflow: hidden; background: #fff; & + & { diff --git a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-details-step/fixed-deposit-product-details-step.component.ts b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-details-step/fixed-deposit-product-details-step.component.ts index 30b91dffb..76efeae01 100644 --- a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-details-step/fixed-deposit-product-details-step.component.ts +++ b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-details-step/fixed-deposit-product-details-step.component.ts @@ -34,12 +34,12 @@ export class FixedDepositProductDetailsStepComponent implements OnInit, OnChange fixedDepositProductDetailsForm: UntypedFormGroup; accountTypeData: string[] = []; - customerSegmentData: string[] = []; - liabilityAccountOptions: any[] = []; - yesNoOptions: Array<{ value: boolean; label: string }> = [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' } + fdTypeOptions: string[] = [ + 'SINGLE', + 'DOUBLE', + 'SPECIAL' ]; + liabilityAccountOptions: any[] = []; constructor() { this.createFixedDepositProductDetailsForm(); @@ -72,11 +72,13 @@ export class FixedDepositProductDetailsStepComponent implements OnInit, OnChange null, Validators.required ], - customerSegment: [null], + fdType: [ + null, + Validators.required + ], glAccountId: [null], effectiveTo: [''], endDate: [''], - introducerRequired: [false], shareLinkingEnabled: [false] }); } @@ -99,7 +101,6 @@ export class FixedDepositProductDetailsStepComponent implements OnInit, OnChange private applyTemplate(template: any) { this.accountTypeData = this.getOptionNames(template.accountTypeOptions); - this.customerSegmentData = this.getOptionNames(template.customerSegmentOptions); this.liabilityAccountOptions = template.accountingMappingOptions?.liabilityAccountOptions ?? []; this.cdr.detectChanges(); @@ -109,11 +110,10 @@ export class FixedDepositProductDetailsStepComponent implements OnInit, OnChange shortName: isEditing ? template.shortName : template.shortName || '', description: template.description || '', accountType: template.accountType ?? this.accountTypeData[0] ?? null, - customerSegment: template.customerSegment ?? this.customerSegmentData[0] ?? null, + fdType: template.fdType ?? null, glAccountId: template.glAccountId ?? null, effectiveTo: this.parseDate(template.effectiveTo), endDate: this.parseDate(template.endDate), - introducerRequired: template.introducerRequired === true || template.introducerRequired === 'Yes', shareLinkingEnabled: !!template.shareLinkingEnabled }); } diff --git a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-preview-step/fixed-deposit-product-preview-step.component.html b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-preview-step/fixed-deposit-product-preview-step.component.html index 716482fcf..e92b052fd 100644 --- a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-preview-step/fixed-deposit-product-preview-step.component.html +++ b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-preview-step/fixed-deposit-product-preview-step.component.html @@ -6,589 +6,665 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. --> -
- -

{{ fixedDepositProduct.name }}

+
+

{{ fixedDepositProduct.name }}

- -
-
- -

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

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

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

+ + +
+ {{ 'labels.inputs.Short Name' | translate }}: + {{ fixedDepositProduct.shortName }}
- -
-
- -

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

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

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

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

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

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

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

-
-
- @for (chart of fixedDepositProduct.charts; track chart; let chartIndex = $index) { -
-
- @if (chart.name) { -
- {{ 'labels.inputs.Name' | translate }}: - {{ chart.name }} -
- } - @if (chart.description) { -
- {{ 'labels.inputs.Description' | translate }}: - {{ chart.description }} -
- } -
- {{ 'labels.inputs.From Date' | translate }}: - {{ chart.fromDate | dateFormat }} -
- @if (chart.endDate) { -
- {{ 'labels.inputs.End Date' | translate }}: - {{ chart.endDate | dateFormat }} -
- } -
- {{ 'labels.inputs.Primary Grouping by Amount' | translate }}: - {{ fixedDepositProduct.isPrimaryGroupingByAmount | yesNo }} -
-
- - @if (chart.chartSlabs.length !== 0) { -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{ 'labels.inputs.Period' | translate }} - {{ - chartSlab.fromPeriod + - ' - ' + - chartSlab.toPeriod + - ' ' + - (chartSlab.periodType - | find: fixedDepositProductsTemplate.chartTemplate.periodTypes : '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 }}

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

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

+ @if ( + fixedDepositProduct.accountType || + fixedDepositProduct.fdType || + fixedDepositProduct.glAccountId || + fixedDepositProduct.effectiveTo || + fixedDepositProduct.endDate || + fixedDepositProduct.shareLinkingEnabled !== undefined + ) { +

Product Classification

+ + + @if (fixedDepositProduct.accountType) { +
+ Account Type: + {{ fixedDepositProduct.accountType }}
-
-
- - - - - - - - - - - - - - - - - - - -
{{ '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 }} -
-
+ } + + @if (fixedDepositProduct.fdType) { +
+ FD Type: + {{ fixedDepositProduct.fdType }}
+ } + + @if (fixedDepositProduct.glAccountId) { +
+ GL Account (Liability): + {{ + fixedDepositProduct.glAccountId + | find: fixedDepositProductsTemplate.accountingMappingOptions.liabilityAccountOptions : 'id' : 'name' + }} +
+ } + + @if (fixedDepositProduct.effectiveTo) { +
+ Effective To: + {{ fixedDepositProduct.effectiveTo | dateFormat }} +
+ } + + @if (fixedDepositProduct.endDate) { +
+ End Date: + {{ fixedDepositProduct.endDate | dateFormat }} +
+ } + + @if (fixedDepositProduct.shareLinkingEnabled !== undefined) { +
+ Deposit Account Link: + {{ fixedDepositProduct.shareLinkingEnabled | yesNo }} +
+ } + } + + @if ( + fixedDepositProduct.currencyCode || + fixedDepositProduct.digitsAfterDecimal !== undefined || + fixedDepositProduct.dormancyAlert !== undefined || + fixedDepositProduct.nominationFacility !== undefined || + fixedDepositProduct.autoDebitFacility !== undefined + ) { + +

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

+ + +
+ {{ 'labels.inputs.Currency' | translate }}: + {{ + fixedDepositProduct.currencyCode | find: fixedDepositProductsTemplate.currencyOptions : 'code' : 'name' + }} +
+ +
+ {{ 'labels.inputs.Decimal Places' | translate }}: + {{ fixedDepositProduct.digitsAfterDecimal }} +
+ + @if (fixedDepositProduct.dormancyAlert !== undefined) { +
+ Dormancy Alert: + {{ fixedDepositProduct.dormancyAlert | yesNo }} +
+ } + + @if (fixedDepositProduct.dormancyAlert) { +
+ Days to Inactive: + {{ fixedDepositProduct.daysToInactive }} +
+
+ Days to Dormancy: + {{ fixedDepositProduct.daysToDormancy }} +
+
+ Days to Escheat: + {{ fixedDepositProduct.daysToEscheat }} +
+ } + + @if (fixedDepositProduct.nominationFacility !== undefined) { +
+ Nomination Facility: + {{ fixedDepositProduct.nominationFacility | yesNo }} +
+ } + + @if (fixedDepositProduct.autoDebitFacility !== undefined) { +
+ Auto Debit Facility: + {{ fixedDepositProduct.autoDebitFacility | yesNo }} +
+ } + } + + @if (fixedDepositProduct.minDepositAmount !== undefined || fixedDepositProduct.maxDepositAmount !== undefined) { + +

Deposit Amount

+ + + @if (fixedDepositProduct.minDepositAmount !== undefined) { +
+ Minimum: + {{ fixedDepositProduct.minDepositAmount }} +
+ } + + @if (fixedDepositProduct.maxDepositAmount !== undefined) { +
+ Maximum: + {{ fixedDepositProduct.maxDepositAmount }} +
+ } + } + + +

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

+ + +
+ {{ 'labels.inputs.Nominal Annual Interest Rate' | translate }} (%): + {{ fixedDepositProduct.nominalAnnualInterestRate | formatNumber }} % +
+ +
+ {{ 'labels.inputs.Interest Compounding Period' | translate }}: + {{ + fixedDepositProduct.interestCompoundingPeriodType + | find: fixedDepositProductsTemplate.interestCompoundingPeriodTypeOptions : 'id' : 'value' + }} +
+ +
+ {{ 'labels.inputs.Interest Posting Period' | translate }}: + {{ + fixedDepositProduct.interestPostingPeriodType + | find: fixedDepositProductsTemplate.interestPostingPeriodTypeOptions : 'id' : 'value' + }} +
+ +
+ {{ 'labels.inputs.Interest Calculated using' | translate }}: + {{ + fixedDepositProduct.interestCalculationType + | find: fixedDepositProductsTemplate.interestCalculationTypeOptions : 'id' : 'value' + }} +
+ +
+ {{ 'labels.inputs.Days in Year' | translate }}: + {{ + fixedDepositProduct.interestCalculationDaysInYearType + | find: fixedDepositProductsTemplate.interestCalculationDaysInYearTypeOptions : 'id' : 'value' + }} +
+ + @if (fixedDepositProduct.effectiveDate) { +
+ Effective Date: + {{ fixedDepositProduct.effectiveDate | dateFormat }}
} - -
-
- -

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

+ @if (fixedDepositProduct.interestCalculateSelector) { +
+ Interest Calculation Selector: + {{ + fixedDepositProduct.interestCalculateSelector + | find: fixedDepositProductsTemplate.interestCalculationTypeOption : 'id' : 'value' + }}
-
- @if (!isNoneAccounting()) { - - - } + } + + @if (fixedDepositProduct.averagePeriod) { +
+ Average Period: + {{ fixedDepositProduct.averagePeriod }}
+ } + +
+ Auto Interest Post: + {{ fixedDepositProduct.autoPost | yesNo }}
- + @if (fixedDepositProduct.frequencyFixDay !== undefined && fixedDepositProduct.frequencyFixDay !== null) { +
+ Frequency (Fix Day): + {{ fixedDepositProduct.frequencyFixDay }} +
+ } + + @if (fixedDepositProduct.lastInterestDate) { +
+ Last Interest Date: + {{ fixedDepositProduct.lastInterestDate | dateFormat }} +
+ } + + +

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

+ + +
+ Loan Facility: + {{ fixedDepositProduct.loanFacility | yesNo }} +
+ +
+ Premature Withdrawal Option: + {{ fixedDepositProduct.prematureWithdrawalOption | yesNo }} +
+ +
+ Tenure Flexibility: + {{ fixedDepositProduct.tenureFlexibility | yesNo }} +
+ +
+ Lien Marked: + {{ fixedDepositProduct.lienMarked | yesNo }} +
+ +
+ {{ 'labels.inputs.Apply Withdrawal Fee for Transfers' | translate }}: + {{ fixedDepositProduct.withdrawalFeeForTransfers | yesNo }} +
+ +
+ Enable Lock-in Period: + {{ fixedDepositProduct.enableLockinPeriod | yesNo }} +
+ + @if (fixedDepositProduct.lockinPeriodFrequency) { +
+ Lock-in Period: + + {{ fixedDepositProduct.lockinPeriodFrequency }} + {{ + fixedDepositProduct.lockinPeriodFrequencyType + | find: fixedDepositProductsTemplate.lockinPeriodFrequencyTypeOptions : 'id' : 'value' + }} + +
+ } + +
+ Maturity Instructions: + {{ fixedDepositProduct.maturityInstructions | yesNo }} +
+ +
+ Credit Maturity Proceeds: + {{ fixedDepositProduct.creditMaturityProceeds | yesNo }} +
+ +
+ Penalty Grace Period (Missed Installments): + {{ fixedDepositProduct.penaltyGracePeriodMissedInstallments | yesNo }} +
+ +
+ Panel Request Applicable: + {{ fixedDepositProduct.panelRequestApplicable | yesNo }} +
+ +
+ No Penalty (Nominee / Heir Closure): + {{ fixedDepositProduct.noPenaltyNomineeHeirClosure | yesNo }} +
+ +
+ Deceased Settlement: + {{ fixedDepositProduct.deceasedSettlement | yesNo }} +
+ +
+ Allow Debit Txn After Due Date: + {{ fixedDepositProduct.allowDebitTxnAfterDueDate | yesNo }} +
+ +
+ Account Closed with Zero Balance in Day Closing: + {{ fixedDepositProduct.accountClosedZeroBalanceInDayClosing | yesNo }} +
+ + @if (fixedDepositProduct.minBalanceForInterestCalculation) { +
+ Min Balance for Interest Calculation: + {{ fixedDepositProduct.minBalanceForInterestCalculation | formatNumber }} +
+ } + +
+ {{ 'labels.inputs.Minimum Deposit Term' | translate }}: + {{ + fixedDepositProduct.minDepositTerm + + ' ' + + (fixedDepositProduct.minDepositTermTypeId + | find: fixedDepositProductsTemplate.periodFrequencyTypeOptions : 'id' : 'value') + }} +
+ + @if (fixedDepositProduct.inMultiplesOfDepositTerm) { +
+ {{ 'labels.inputs.And thereafter, in Multiples of' | translate }}: + {{ + fixedDepositProduct.inMultiplesOfDepositTerm + + ' ' + + (fixedDepositProduct.inMultiplesOfDepositTermTypeId + | find: fixedDepositProductsTemplate.periodFrequencyTypeOptions : 'id' : 'value') + }} +
+ } + + @if (fixedDepositProduct.maxDepositTerm) { +
+ {{ 'labels.inputs.Maximum Deposit Term' | translate }}: + {{ + fixedDepositProduct.maxDepositTerm + + ' ' + + (fixedDepositProduct.maxDepositTermTypeId + | find: fixedDepositProductsTemplate.periodFrequencyTypeOptions : 'id' : 'value') + }} +
+ } + +
+ {{ 'labels.inputs.Apply Penal Interest (less)' | translate }}: + {{ fixedDepositProduct.preClosurePenalApplicable | yesNo }} +
+ + @if (fixedDepositProduct.preClosurePenalApplicable) { +
+ {{ 'labels.inputs.Penal Interest' | translate }} (%): + {{ + fixedDepositProduct.preClosurePenalInterest + + '% (' + + (fixedDepositProduct.preClosurePenalInterestOnTypeId + | find: fixedDepositProductsTemplate.preClosurePenalInterestOnTypeOptions : 'id' : 'value') + + ')' + }} +
+ } + +
+ {{ 'labels.inputs.Withhold Tax is Applicable' | translate }}: + {{ fixedDepositProduct.withHoldTax | yesNo }} +
+ + @if (fixedDepositProduct.withHoldTax) { +
+ {{ 'labels.inputs.Tax Group' | translate }}: + {{ + fixedDepositProduct.taxGroupId | find: fixedDepositProductsTemplate.taxGroupOptions : 'id' : 'name' + }} +
+ } + + + @if (fdSlabs.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 (fixedDepositProduct.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 (!isNoneAccounting()) { + + } + + @if (otherGlMappings) { +
+

Other GL Mappings

+ + + @if (otherGlMappings.assets?.length) { + + + + + + + + + + + +
Asset Account{{ row.accountName }}GL Account + {{ + formatGlAccountByCode( + row.glCode, + fixedDepositProductsTemplate?.accountingMappingOptions?.assetAccountOptions + ) + }} +
+ } + + @if (otherGlMappings.liabilities?.length) { + + + + + + + + + + + +
Liability Account{{ row.accountName }}GL Account + {{ + formatGlAccountByCode( + row.glCode, + fixedDepositProductsTemplate?.accountingMappingOptions?.liabilityAccountOptions + ) + }} +
+ } + + @if (otherGlMappings.expenses?.length) { + + + + + + + + + + + +
Expense Account{{ row.accountName }}GL Account + {{ + formatGlAccountByCode( + row.glCode, + fixedDepositProductsTemplate?.accountingMappingOptions?.expenseAccountOptions + ) + }} +
+ } + + @if (otherGlMappings.income?.length) { + + + + + + + + + + + +
Income Account{{ row.accountName }}GL Account + {{ + formatGlAccountByCode( + row.glCode, + fixedDepositProductsTemplate?.accountingMappingOptions?.incomeAccountOptions + ) + }} +
+ } +
+ } + @if ( fixedDepositProduct.advancedAccountingRules && (fixedDepositProduct.paymentChannelToFundSourceMappings?.length || fixedDepositProduct.feeToIncomeAccountMappings?.length || fixedDepositProduct.penaltyToIncomeAccountMappings?.length) ) { -
-
- -

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

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

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

-
- - - - - - - - - - - -
{{ 'labels.inputs.Payment Type' | translate }} - {{ - paymentFundSource.paymentTypeId - | find: fixedDepositProductsTemplate.paymentTypeOptions : 'id' : 'name' - }} - {{ 'labels.inputs.Fund Source' | translate }} - {{ - paymentFundSource.fundSourceAccountId - | find: fixedDepositProductsTemplate.accountingMappingOptions.assetAccountOptions : 'id' : 'name' - }} -
-
- } - @if (fixedDepositProduct.feeToIncomeAccountMappings?.length) { -

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

-
- - - - - - - - - - - -
{{ 'labels.inputs.Fees' | translate }} - {{ feesIncome.chargeId | find: fixedDepositProductsTemplate.chargeOptions : 'id' : 'name' }} - {{ 'labels.inputs.Income Account' | translate }} - {{ - feesIncome.incomeAccountId - | find: fixedDepositProductsTemplate.accountingMappingOptions.incomeAccountOptions : 'id' : 'name' - }} -
-
- } - @if (fixedDepositProduct.penaltyToIncomeAccountMappings?.length) { -

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

-
- - - - - - - - - - - -
{{ 'labels.inputs.Penalty' | translate }} - {{ penaltyIncome.chargeId | find: fixedDepositProductsTemplate.penaltyOptions : 'id' : 'name' }} - {{ 'labels.inputs.Income Account' | translate }} - {{ - penaltyIncome.incomeAccountId - | find: fixedDepositProductsTemplate.accountingMappingOptions.incomeAccountOptions : 'id' : 'name' - }} -
-
- } -
-
+

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

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

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

+ + + + + + + + + + + +
{{ 'labels.inputs.Payment Type' | translate }} + {{ + paymentFundSource.paymentTypeId | find: fixedDepositProductsTemplate.paymentTypeOptions : 'id' : 'name' + }} + {{ 'labels.inputs.Fund Source' | translate }} + {{ + paymentFundSource.fundSourceAccountId + | find: fixedDepositProductsTemplate.accountingMappingOptions.assetAccountOptions : 'id' : 'name' + }} +
+ } + + @if (fixedDepositProduct.feeToIncomeAccountMappings?.length) { +

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

+ + + + + + + + + + + +
{{ 'labels.inputs.Fees' | translate }} + {{ feesIncome.chargeId | find: fixedDepositProductsTemplate.chargeOptions : 'id' : 'name' }} + {{ 'labels.inputs.Income Account' | translate }} + {{ + feesIncome.incomeAccountId + | find: fixedDepositProductsTemplate.accountingMappingOptions.incomeAccountOptions : 'id' : 'name' + }} +
+ } + + @if (fixedDepositProduct.penaltyToIncomeAccountMappings?.length) { +

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

+ + + + + + + + + + + +
{{ 'labels.inputs.Penalty' | translate }} + {{ penaltyIncome.chargeId | find: fixedDepositProductsTemplate.penaltyOptions : 'id' : 'name' }} + {{ 'labels.inputs.Income Account' | translate }} + {{ + penaltyIncome.incomeAccountId + | find: fixedDepositProductsTemplate.accountingMappingOptions.incomeAccountOptions : 'id' : 'name' + }} +
+ } }
- -
- - -
diff --git a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-preview-step/fixed-deposit-product-preview-step.component.scss b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-preview-step/fixed-deposit-product-preview-step.component.scss index 4ae6c504a..d2e754479 100644 --- a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-preview-step/fixed-deposit-product-preview-step.component.scss +++ b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-preview-step/fixed-deposit-product-preview-step.component.scss @@ -6,103 +6,39 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -.fdpp-container { - display: flex; - flex-direction: column; - gap: 16px; +@use 'assets/styles/colours' as *; + +.mat-h3 { + margin-top: 10px !important; + font-weight: 300; + color: $status-approved; } -.fdpp-title { +.mat-elevation-z1 { + margin: 1em 0 1.5em; +} + +h2, +h3, +h4 { margin: 0; - font-size: 20px; - font-weight: 600; - color: #202124; -} - -.fdpp-section { - border: 1px solid #e2e8f0; - border-radius: 10px; - overflow: hidden; - background: #fff; - - &__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; - } - } -} - -.fdpp-section-body { - padding: 16px 24px; -} - -.fdpp-preview-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 4px 16px; - padding: 16px 24px; - - @media (width <= 600px) { - grid-template-columns: 1fr; - } -} - -.fdpp-row { - display: flex; - flex-direction: column; - padding: 6px 0; - - &--full { - grid-column: 1 / -1; - } -} - -.fdpp-label { - font-size: 12px; font-weight: 500; - color: #5f6368; - margin-bottom: 2px; } -.fdpp-value { - font-size: 14px; - font-weight: 500; - color: #202124; +table { + width: 100%; } -.fdpp-chart-block { - padding-bottom: 16px; - border-bottom: 1px solid #e2e8f0; - margin-bottom: 16px; - - &:last-child { - border-bottom: none; - margin-bottom: 0; - padding-bottom: 0; - } +span { + margin: 0.5em 0; } +mat-divider { + margin: 0 0 0.5em; +} + +/* ── Incentives expand row ── */ .fdpp-table-wrapper { - border: 1px solid #e2e8f0; - border-radius: 8px; - overflow: hidden; - margin-top: 8px; - table { width: 100%; } @@ -132,20 +68,95 @@ } } -.fdpp-subsection-title { - margin: 16px 0 8px; - font-size: 14px; - font-weight: 600; - color: #202124; +/* ── GL mapping summary (same as RD product preview) ── */ +.spa-gl-summary, +.spa-other-gl-summary { + display: flex; + flex-direction: column; + gap: 0.9rem; + margin-top: 1.25rem; +} - &:first-child { - margin-top: 0; +.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; } } -.fdpp-actions { - display: flex; - align-items: center; - gap: 12px; - margin-top: 16px; +.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/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-preview-step/fixed-deposit-product-preview-step.component.ts b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-preview-step/fixed-deposit-product-preview-step.component.ts index b34220322..c493fd858 100644 --- a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-preview-step/fixed-deposit-product-preview-step.component.ts +++ b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-preview-step/fixed-deposit-product-preview-step.component.ts @@ -10,6 +10,7 @@ import { Component, OnInit, Input, Output, EventEmitter, SimpleChanges, OnChange import { trigger, state, transition, animate, style } from '@angular/animations'; import { Accounting } from 'app/core/utils/accounting'; import { OptionData } from 'app/shared/models/option-data.model'; +import { MatDivider } from '@angular/material/divider'; import { MatTable, @@ -44,6 +45,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; ], imports: [ ...STANDALONE_SHARED_IMPORTS, + MatDivider, MatTable, MatColumnDef, MatHeaderCellDef, @@ -100,6 +102,23 @@ export class FixedDepositProductPreviewStepComponent implements OnInit, OnChange accountingMappings: any = {}; accountingRule: OptionData; + readonly fdSlabColumns = [ + 'srNo', + 'description', + 'fromPeriod', + 'toPeriod', + 'periodTypeLabel', + 'annualInterestRate' + ]; + + get otherGlMappings() { + return this.fixedDepositProduct?.otherGlMappings || this.fixedDepositProduct?.otherGlMappingsMap || null; + } + + get fdSlabs(): any[] { + return this.fixedDepositProduct?.fdSlabs ?? []; + } + ngOnInit() { this.setCurrentValues(); } @@ -142,7 +161,9 @@ export class FixedDepositProductPreviewStepComponent implements OnInit, OnChange incomeFromPenaltyAccount: this.accounting.glAccountLookUp( this.fixedDepositProduct.incomeFromPenaltyAccountId, incomeAccountData - ) + ), + escheatLiabilityAccount: + this.accounting.glAccountLookUp(this.fixedDepositProduct.escheatLiabilityId, liabilityAccountData) || null }; if (this.isAccrualAccounting()) { @@ -173,4 +194,30 @@ export class FixedDepositProductPreviewStepComponent implements OnInit, OnChange isAccrualAccounting(): boolean { return this.accounting.isAccrualAccountingRuleId(this.fixedDepositProduct.accountingRule); } + + getLockinPeriodFrequencyTypeLabel(value: any): string { + if (!value && value !== 0) return ''; + const options = this.fixedDepositProductsTemplate?.lockinPeriodFrequencyTypeOptions || []; + const found = options.find((o: any) => o.id === value); + return found?.value || String(value); + } + + getGlAccountName(id: any): string { + if (!id) return ''; + const accounts = this.fixedDepositProductsTemplate?.accountingMappingOptions?.liabilityAccountOptions ?? []; + const found = accounts.find((a: any) => a.id === id || a.id === +id); + return found ? `${found.name} (${found.glCode})` : String(id); + } + + 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/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-settings-step/fixed-deposit-product-settings-step.component.scss b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-settings-step/fixed-deposit-product-settings-step.component.scss index 6017718c9..8310cf654 100644 --- a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-settings-step/fixed-deposit-product-settings-step.component.scss +++ b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-settings-step/fixed-deposit-product-settings-step.component.scss @@ -15,7 +15,6 @@ $gray-900: #202124; .fdps-section { border: 1px solid $border; border-radius: 10px; - overflow: hidden; background: #fff; & + & { diff --git a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-terms-step/fixed-deposit-product-terms-step.component.html b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-terms-step/fixed-deposit-product-terms-step.component.html index ab0346c21..239c749bd 100644 --- a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-terms-step/fixed-deposit-product-terms-step.component.html +++ b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-terms-step/fixed-deposit-product-terms-step.component.html @@ -7,6 +7,33 @@ -->
+ + + + + + + +
+
+ + Deposit Amount +
+
+
+ + Minimum + + + + + Maximum + + +
+
+
+
@@ -65,6 +92,28 @@
+
+ + Interest Compounding Period + + @for (opt of interestCompoundingPeriodTypeData; track opt.id) { + {{ opt.value | translateKey: 'catalogs' }} + } + + Interest Compounding Period is required + + + + Days in Year + + @for (opt of interestCalculationDaysInYearTypeData; track opt.id) { + {{ opt.value | translateKey: 'catalogs' }} + } + + Days in Year is required + +
+
Interest Calculate @@ -83,7 +132,7 @@ Average Period - @for (period of periodFrequencyData; track period.id) { + @for (period of averagePeriodData; track period.id) { {{ period.value }} } diff --git a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-terms-step/fixed-deposit-product-terms-step.component.scss b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-terms-step/fixed-deposit-product-terms-step.component.scss index 8b4b08ace..513244097 100644 --- a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-terms-step/fixed-deposit-product-terms-step.component.scss +++ b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-terms-step/fixed-deposit-product-terms-step.component.scss @@ -15,7 +15,6 @@ $gray-900: #202124; .fdpt-section { border: 1px solid $border; border-radius: 10px; - overflow: hidden; background: #fff; & + & { diff --git a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-terms-step/fixed-deposit-product-terms-step.component.ts b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-terms-step/fixed-deposit-product-terms-step.component.ts index 29dc4defa..efe589523 100644 --- a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-terms-step/fixed-deposit-product-terms-step.component.ts +++ b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-terms-step/fixed-deposit-product-terms-step.component.ts @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { Component, OnInit, Input, inject } from '@angular/core'; +import { Component, OnInit, OnChanges, Input, SimpleChanges, inject } from '@angular/core'; import { UntypedFormGroup, UntypedFormBuilder, Validators } from '@angular/forms'; import { MatTooltip } from '@angular/material/tooltip'; import { Dates } from 'app/core/utils/dates'; @@ -22,12 +22,13 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; MatTooltip ] }) -export class FixedDepositProductTermsStepComponent implements OnInit { +export class FixedDepositProductTermsStepComponent implements OnInit, OnChanges { private formBuilder = inject(UntypedFormBuilder); private dateUtils = inject(Dates); private settingsService = inject(SettingsService); @Input() fixedDepositProductsTemplate: any; + @Input() fdType: string | null = null; fixedDepositProductTermsForm: UntypedFormGroup; @@ -36,6 +37,7 @@ export class FixedDepositProductTermsStepComponent implements OnInit { interestCalculationTypeData: any; interestCalculationTypeOptionData: any; interestCalculationDaysInYearTypeData: any; + averagePeriodData: any[] = []; periodFrequencyData: any[] = []; yesNoOptions: Array<{ value: boolean; label: string }> = [ { value: true, label: 'Yes' }, @@ -47,16 +49,33 @@ export class FixedDepositProductTermsStepComponent implements OnInit { } ngOnInit() { + this.initializeTerms(); + } + + ngOnChanges(changes: SimpleChanges) { + if (changes.fdType) { + this.applyFdTypeConstraints(); + } + if (changes.fixedDepositProductsTemplate?.currentValue) { + this.initializeTerms(); + } + } + + private initializeTerms() { + if (!this.fixedDepositProductsTemplate) return; + this.interestCompoundingPeriodTypeData = this.fixedDepositProductsTemplate.interestCompoundingPeriodTypeOptions; this.interestPostingPeriodTypeData = this.fixedDepositProductsTemplate.interestPostingPeriodTypeOptions; this.interestCalculationTypeData = this.fixedDepositProductsTemplate.interestCalculationTypeOptions; this.interestCalculationTypeOptionData = this.fixedDepositProductsTemplate.interestCalculationTypeOption || []; this.interestCalculationDaysInYearTypeData = this.fixedDepositProductsTemplate.interestCalculationDaysInYearTypeOptions; - this.periodFrequencyData = this.fixedDepositProductsTemplate.periodFrequencyOptions || []; + this.averagePeriodData = this.fixedDepositProductsTemplate.periodFrequencyOptions || []; + this.periodFrequencyData = this.fixedDepositProductsTemplate.periodFrequencyTypeOptions || []; const isEditing = !!this.fixedDepositProductsTemplate.id; - const defaultPeriod = this.periodFrequencyData[0]?.value || 'Monthly'; + const defaultPeriodTypeId = this.periodFrequencyData[0]?.id ?? null; + const defaultAveragePeriod = this.averagePeriodData[0]?.value || 'Monthly'; const savedEnumTypeId = this.fixedDepositProductsTemplate.interestCalculationType?.id || this.interestCalculationTypeData?.[0]?.id || 1; const savedSelectorId = isEditing @@ -65,15 +84,15 @@ export class FixedDepositProductTermsStepComponent implements OnInit { null) : (this.interestCalculationTypeOptionData?.[0]?.id ?? null); - if (isEditing) { - this.fixedDepositProductTermsForm.patchValue({ - minDepositAmount: this.fixedDepositProductsTemplate.minDepositAmount, - depositAmount: this.fixedDepositProductsTemplate.depositAmount, - maxDepositAmount: this.fixedDepositProductsTemplate.maxDepositAmount - }); - } - this.fixedDepositProductTermsForm.patchValue({ + minDepositTerm: this.fixedDepositProductsTemplate.minDepositTerm ?? 1, + minDepositTermTypeId: this.fixedDepositProductsTemplate.minDepositTermType?.id ?? defaultPeriodTypeId, + maxDepositTerm: this.fixedDepositProductsTemplate.maxDepositTerm ?? '', + maxDepositTermTypeId: this.fixedDepositProductsTemplate.maxDepositTermType?.id ?? '', + inMultiplesOfDepositTerm: this.fixedDepositProductsTemplate.inMultiplesOfDepositTerm ?? '', + inMultiplesOfDepositTermTypeId: this.fixedDepositProductsTemplate.inMultiplesOfDepositTermType?.id ?? '', + minDepositAmount: this.fixedDepositProductsTemplate.minDepositAmount, + maxDepositAmount: this.fixedDepositProductsTemplate.maxDepositAmount, nominalAnnualInterestRate: this.fixedDepositProductsTemplate.nominalAnnualInterestRate ?? 0, interestCompoundingPeriodType: this.fixedDepositProductsTemplate.interestCompoundingPeriodType?.id, interestPostingPeriodType: this.fixedDepositProductsTemplate.interestPostingPeriodType?.id, @@ -83,13 +102,34 @@ export class FixedDepositProductTermsStepComponent implements OnInit { ? this.parseDate(this.fixedDepositProductsTemplate.effectiveDate) : '', interestCalculateSelector: savedSelectorId, - averagePeriod: isEditing ? this.fixedDepositProductsTemplate.averagePeriod || defaultPeriod : defaultPeriod, + averagePeriod: isEditing + ? this.fixedDepositProductsTemplate.averagePeriod || defaultAveragePeriod + : defaultAveragePeriod, autoPost: isEditing ? (this.fixedDepositProductsTemplate.autoPost ?? true) : true, frequencyFixDay: isEditing ? (this.fixedDepositProductsTemplate.frequencyFixDay ?? 30) : 30, lastInterestDate: this.fixedDepositProductsTemplate.lastInterestDate ? this.parseDate(this.fixedDepositProductsTemplate.lastInterestDate) : '' }); + this.applyFdTypeConstraints(); + } + + private applyFdTypeConstraints() { + if (!this.fixedDepositProductTermsForm || !this.interestPostingPeriodTypeData) return; + const postingCtrl = this.fixedDepositProductTermsForm.get('interestPostingPeriodType'); + if (!postingCtrl) return; + if (this.fdType === 'SPECIAL') { + // Monthly posting = id 4 (MONTHLY in Fineract enum) + const monthlyOption = this.interestPostingPeriodTypeData?.find( + (o: any) => o.value?.toLowerCase() === 'monthly' || o.id === 4 + ); + if (monthlyOption) { + postingCtrl.setValue(monthlyOption.id); + postingCtrl.disable(); + } + } else { + postingCtrl.enable(); + } } private parseDate(dateValue: any): Date | null { @@ -101,18 +141,21 @@ export class FixedDepositProductTermsStepComponent implements OnInit { createFixedDepositProductTermsForm() { this.fixedDepositProductTermsForm = this.formBuilder.group({ - minDepositAmount: [ - '', - Validators.min(0) - ], - depositAmount: [ - '', + minDepositTerm: [ + 1, [ - Validators.required, Validators.min(0) ] ], - maxDepositAmount: [ + minDepositTermTypeId: [''], + maxDepositTerm: [ + '', + Validators.min(0) + ], + maxDepositTermTypeId: [''], + inMultiplesOfDepositTerm: [''], + inMultiplesOfDepositTermTypeId: [''], + minDepositAmount: [ '', Validators.min(0) ], @@ -139,6 +182,10 @@ export class FixedDepositProductTermsStepComponent implements OnInit { '', Validators.required ], + maxDepositAmount: [ + '', + Validators.min(0) + ], effectiveDate: [''], interestCalculateSelector: [1], averagePeriod: ['Monthly'], @@ -152,7 +199,7 @@ export class FixedDepositProductTermsStepComponent implements OnInit { } get fixedDepositProductTerms() { - const formValue = { ...this.fixedDepositProductTermsForm.value }; + const formValue = { ...this.fixedDepositProductTermsForm.getRawValue() }; const dateFormat = this.settingsService.dateFormat; if (formValue.effectiveDate instanceof Date) { diff --git a/src/app/products/fixed-deposit-products/view-fixed-deposit-product/fixed-deposit-general-tab/fixed-deposit-general-tab.component.html b/src/app/products/fixed-deposit-products/view-fixed-deposit-product/fixed-deposit-general-tab/fixed-deposit-general-tab.component.html index 7afb66932..7678c4813 100644 --- a/src/app/products/fixed-deposit-products/view-fixed-deposit-product/fixed-deposit-general-tab/fixed-deposit-general-tab.component.html +++ b/src/app/products/fixed-deposit-products/view-fixed-deposit-product/fixed-deposit-general-tab/fixed-deposit-general-tab.component.html @@ -6,23 +6,29 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +
+

{{ fixedDepositProductData.name }}

- - + +

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

- - +
{{ 'labels.inputs.Short Name' | translate }}: @@ -36,9 +42,59 @@
} -

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

+ + +

Product Classification

+ - + @if (fixedDepositProductData.accountType) { +
+ {{ 'labels.inputs.Account Type' | translate }}: + {{ fixedDepositProductData.accountType }} +
+ } + + @if (fixedDepositProductData.fdType) { +
+ {{ 'labels.inputs.FD Type' | translate }}: + {{ fixedDepositProductData.fdType }} +
+ } + + @if (fixedDepositProductData.glAccountName || fixedDepositProductData.glAccountId) { +
+ GL Account (Liability): + + {{ fixedDepositProductData.glAccountName || fixedDepositProductData.glAccountId }} + +
+ } + + @if (fixedDepositProductData.effectiveTo) { +
+ Effective To: + {{ fixedDepositProductData.effectiveTo | dateFormat }} +
+ } + + @if (fixedDepositProductData.endDate) { +
+ {{ 'labels.inputs.End Date' | translate }}: + {{ fixedDepositProductData.endDate | dateFormat }} +
+ } + + @if (fixedDepositProductData.shareLinkingEnabled !== undefined) { +
+ {{ 'labels.inputs.Share Linking Enabled' | translate }}: + {{ fixedDepositProductData.shareLinkingEnabled | yesNo }} +
+ } + + + +

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

+
{{ 'labels.heading.Currency' | translate }}: @@ -57,83 +113,229 @@ {{ fixedDepositProductData.currency.inMultiplesOf | formatNumber }}
-

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

+ @if (fixedDepositProductData.dormancyAlert !== undefined) { +
+ Dormancy Alert: + {{ fixedDepositProductData.dormancyAlert | yesNo }} +
+ } - + @if (fixedDepositProductData.dormancyAlert) { +
+ Days to Inactive: + {{ fixedDepositProductData.daysToInactive }} +
+
+ Days to Dormancy: + {{ fixedDepositProductData.daysToDormancy }} +
+
+ Days to Escheat: + {{ fixedDepositProductData.daysToEscheat }} +
+ } + + @if (fixedDepositProductData.nominationFacility !== undefined) { +
+ {{ 'labels.inputs.Nomination Facility' | translate }}: + {{ fixedDepositProductData.nominationFacility | yesNo }} +
+ } + + @if (fixedDepositProductData.autoDebitFacility !== undefined) { +
+ {{ 'labels.inputs.Auto Debit Facility' | translate }}: + {{ fixedDepositProductData.autoDebitFacility | yesNo }} +
+ } + + + @if ( + fixedDepositProductData.minDepositAmount !== undefined || + fixedDepositProductData.maxDepositAmount !== undefined + ) { + +

Deposit Amount

+ + + @if (fixedDepositProductData.minDepositAmount !== undefined) { +
+ Minimum: + {{ fixedDepositProductData.minDepositAmount }} +
+ } + @if (fixedDepositProductData.maxDepositAmount !== undefined) { +
+ Maximum: + {{ fixedDepositProductData.maxDepositAmount }} +
+ } + } + + + +

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

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

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

+ - +
+ {{ 'labels.inputs.Loan Facility' | translate }}: + {{ fixedDepositProductData.loanFacility | yesNo }} +
- @if (fixedDepositProductData.lockinPeriodFrequency) { +
+ {{ 'labels.inputs.Premature Withdrawal Option' | translate }}: + {{ fixedDepositProductData.prematureWithdrawalOption | yesNo }} +
+ +
+ {{ 'labels.inputs.Tenure Flexibility' | translate }}: + {{ fixedDepositProductData.tenureFlexibility | yesNo }} +
+ +
+ {{ 'labels.inputs.Lien Marked' | translate }}: + {{ fixedDepositProductData.lienMarked | yesNo }} +
+ +
+ {{ 'labels.inputs.Apply Withdrawal Fee for Transfers' | translate }}: + {{ fixedDepositProductData.withdrawalFeeForTransfers | yesNo }} +
+ +
+ Enable Lock-in Period: + {{ fixedDepositProductData.enableLockinPeriod | yesNo }} +
+ + @if (fixedDepositProductData.lockinPeriodFrequency !== undefined) {
{{ 'labels.inputs.Lock-in Period' | translate }}: {{ fixedDepositProductData.lockinPeriodFrequency }} - {{ fixedDepositProductData.lockinPeriodFrequencyType.value }} + {{ fixedDepositProductData.lockinPeriodFrequencyType?.value }} +
} - @if (fixedDepositProductData.minDepositTerm) { + @if (fixedDepositProductData.maturityInstructions !== undefined) { +
+ Maturity Instructions: + {{ fixedDepositProductData.maturityInstructions | yesNo }} +
+ } + +
+ Credit Maturity Proceeds: + {{ fixedDepositProductData.creditMaturityProceeds | yesNo }} +
+ +
+ Penalty Grace Period (Missed Installments): + {{ fixedDepositProductData.penaltyGracePeriodMissedInstallments | yesNo }} +
+ +
+ Panel Request Applicable: + {{ fixedDepositProductData.panelRequestApplicable | yesNo }} +
+ +
+ No Penalty (Nominee / Heir Closure): + {{ fixedDepositProductData.noPenaltyNomineeHeirClosure | yesNo }} +
+ +
+ Deceased Settlement: + {{ fixedDepositProductData.deceasedSettlement | yesNo }} +
+ +
+ Allow Debit Txn After Due Date: + {{ fixedDepositProductData.allowDebitTxnAfterDueDate | yesNo }} +
+ +
+ Account Closed with Zero Balance in Day Closing: + {{ fixedDepositProductData.accountClosedZeroBalanceInDayClosing | yesNo }} +
+ + @if (fixedDepositProductData.minBalanceForInterestCalculation !== undefined) { +
+ Min Balance for Interest Calculation: + {{ fixedDepositProductData.minBalanceForInterestCalculation | formatNumber }} +
+ } + + @if (fixedDepositProductData.minDepositTerm !== undefined) {
{{ 'labels.inputs.Minimum Deposit Term' | translate }}: - @if (fixedDepositProductData.minDepositTerm !== undefined) { - {{ fixedDepositProductData.minDepositTerm }} -   - {{ fixedDepositProductData.minDepositTermType.value }} - } -
- } - - @if (fixedDepositProductData.maxDepositTerm) { -
- {{ 'labels.inputs.Maximum Deposit Term' | translate }}: - @if (fixedDepositProductData.maxDepositTerm !== undefined) { - {{ fixedDepositProductData.maxDepositTerm }} -   - {{ fixedDepositProductData.maxDepositTermType.value }} - } + {{ fixedDepositProductData.minDepositTerm }} + {{ fixedDepositProductData.minDepositTermType?.value }}
} @@ -142,34 +344,35 @@ {{ 'labels.inputs.Deposit Term In multiple of' | translate }}: {{ fixedDepositProductData.inMultiplesOfDepositTerm }} - - - {{ fixedDepositProductData.inMultiplesOfDepositTermType.value }}
} -
- {{ 'labels.inputs.Apply Penal Interest (less)' | translate }}: - {{ fixedDepositProductData.preClosurePenalApplicable | yesNo }} -
+ @if (fixedDepositProductData.maxDepositTerm) { +
+ {{ 'labels.inputs.Maximum Deposit Term' | translate }}: + {{ fixedDepositProductData.maxDepositTerm }} + {{ fixedDepositProductData.maxDepositTermType?.value }} +
+ } @if (fixedDepositProductData.preClosurePenalApplicable) { +
+ {{ 'labels.inputs.Apply Penal Interest (less)' | translate }}: + {{ fixedDepositProductData.preClosurePenalApplicable | yesNo }} +
{{ 'labels.inputs.Penal Interest' | translate }} (%): {{ fixedDepositProductData.preClosurePenalInterest }}     %     On    - {{ fixedDepositProductData.preClosurePenalInterestOnType.value }}{{ fixedDepositProductData.preClosurePenalInterest }} % On + {{ fixedDepositProductData.preClosurePenalInterestOnType?.value }}
} - @if (fixedDepositProductData.minBalanceForInterestCalculation) { -
- {{ 'labels.inputs.Balance Required for Interest Calculation' | translate }}: - {{ fixedDepositProductData.minBalanceForInterestCalculation | formatNumber }} -
- } -
{{ 'labels.inputs.Withhold Tax is Applicable' | translate }}: {{ fixedDepositProductData.withHoldTax | yesNo }} @@ -178,249 +381,72 @@ @if (fixedDepositProductData.withHoldTax) {
{{ 'labels.inputs.Withhold Tax group' | translate }}: - {{ fixedDepositProductData.taxGroup.name }} + {{ fixedDepositProductData.taxGroup?.name }}
} - @if (fixedDepositProductData.activeChart) { -
-

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

- -
- @if (fixedDepositProductData.activeChart.name) { -
- {{ 'labels.inputs.Name' | translate }}: - {{ fixedDepositProductData.activeChart.name }} -
- } - @if (fixedDepositProductData.activeChart.description) { -
- {{ 'labels.inputs.Description' | translate }}: - {{ fixedDepositProductData.activeChart.description }} -
- } -
- {{ 'labels.inputs.From Date' | translate }}: - {{ fixedDepositProductData.activeChart.fromDate | dateFormat }} -
- @if (fixedDepositProductData.activeChart.endDate) { -
- {{ 'labels.inputs.End Date' | translate }}: - {{ fixedDepositProductData.activeChart.endDate | dateFormat }} -
- } -
- {{ 'labels.inputs.Primary Grouping by Amount' | translate }}: - {{ fixedDepositProductData.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: fixedDepositProductsTemplate.chartTemplate.entityTypeOptions : 'id' : 'value' - }} - {{ 'labels.inputs.Attribute Name' | translate }} - {{ - incentive.attributeName - | find - : fixedDepositProductsTemplate.chartTemplate.attributeNameOptions - : 'id' - : 'value' - }} - {{ 'labels.inputs.Condition Type' | translate }} - {{ - incentive.conditionType - | find - : fixedDepositProductsTemplate.chartTemplate.conditionTypeOptions - : 'id' - : 'value' - }} - {{ 'labels.inputs.Attribute Value' | translate }} - @switch (incentive.attributeName) { - @case (2) { - {{ - incentive.attributeValue - | find: fixedDepositProductsTemplate.chartTemplate.genderOptions : 'id' : 'name' - }} - } - @case (3) { - {{ incentive.attributeValue }} - } - @case (4) { - {{ - incentive.attributeValue - | find - : fixedDepositProductsTemplate.chartTemplate.clientTypeOptions - : 'id' - : 'name' - }} - } - @case (5) { - {{ - incentive.attributeValue - | find - : fixedDepositProductsTemplate.chartTemplate.clientClassificationOptions - : 'id' - : 'name' - }} - } - } - {{ 'labels.inputs.Incentive Type' | translate }} - {{ - incentive.incentiveType - | find - : fixedDepositProductsTemplate.chartTemplate.incentiveTypeOptions - : 'id' - : 'value' - }} - {{ 'labels.inputs.Amount' | translate }} - {{ incentive.amount | formatNumber }} -
-
-
-
-
-
- } + + @if (fdSlabs.length) { + +

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

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

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

- + - + - + - + @@ -428,107 +454,16 @@ } + +

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

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

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

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

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

-
{{ 'labels.inputs.Name' | translate }} - {{ charge.name }} - {{ charge.name }} {{ 'labels.inputs.Type' | translate }} - {{ charge.chargeCalculationType.value }} - {{ charge.chargeCalculationType?.value }} {{ 'labels.inputs.Amount' | translate }} - {{ charge.amount | formatNumber }} - {{ charge.amount | formatNumber }} {{ 'labels.inputs.Collected On' | translate }} - {{ charge.chargeTimeType.value }} - {{ charge.chargeTimeType?.value }}
- - - - - - - - - - -
{{ 'labels.inputs.Payment Type' | translate }} - {{ paymentFundSource.paymentType.name }} - {{ 'labels.inputs.Fund Source' | translate }} - {{ paymentFundSource.fundSourceAccount.name }} -
-
- } - @if (fixedDepositProductData.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 (fixedDepositProductData.penaltyToIncomeAccountMappings?.length) { -
-

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

- - - - - - - - - - - -
{{ 'labels.inputs.Penalty' | translate }} - {{ penaltyIncome.charge.name }} - {{ 'labels.inputs.Income Account' | translate }} - {{ penaltyIncome.incomeAccount.name }} -
-
- } -
+ @if (!isNoneAccounting()) { + }
diff --git a/src/app/products/fixed-deposit-products/view-fixed-deposit-product/fixed-deposit-general-tab/fixed-deposit-general-tab.component.scss b/src/app/products/fixed-deposit-products/view-fixed-deposit-product/fixed-deposit-general-tab/fixed-deposit-general-tab.component.scss index 83a6fded5..66781c6a4 100644 --- a/src/app/products/fixed-deposit-products/view-fixed-deposit-product/fixed-deposit-general-tab/fixed-deposit-general-tab.component.scss +++ b/src/app/products/fixed-deposit-products/view-fixed-deposit-product/fixed-deposit-general-tab/fixed-deposit-general-tab.component.scss @@ -39,7 +39,7 @@ span { margin: 0.5em 0; } -.mat-card .mat-divider-horizontal.mat-divider-inset { +mat-divider { margin: 0 0 0.5em; } diff --git a/src/app/products/fixed-deposit-products/view-fixed-deposit-product/fixed-deposit-general-tab/fixed-deposit-general-tab.component.ts b/src/app/products/fixed-deposit-products/view-fixed-deposit-product/fixed-deposit-general-tab/fixed-deposit-general-tab.component.ts index 1f694e83e..6952ed868 100644 --- a/src/app/products/fixed-deposit-products/view-fixed-deposit-product/fixed-deposit-general-tab/fixed-deposit-general-tab.component.ts +++ b/src/app/products/fixed-deposit-products/view-fixed-deposit-product/fixed-deposit-general-tab/fixed-deposit-general-tab.component.ts @@ -6,12 +6,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { Component, inject } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { ActivatedRoute, RouterLink } from '@angular/router'; -import { trigger, state, transition, animate, style } from '@angular/animations'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { MatDivider } from '@angular/material/divider'; - import { MatTable, MatColumnDef, @@ -24,6 +22,9 @@ import { MatRowDef, MatRow } from '@angular/material/table'; +import { Accounting } from 'app/core/utils/accounting'; +import { OptionData } from 'app/shared/models/option-data.model'; +import { ProductsService } from 'app/products/products.service'; 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'; @@ -35,15 +36,9 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; selector: 'mifosx-fixed-deposit-general-tab', templateUrl: './fixed-deposit-general-tab.component.html', styleUrls: ['./fixed-deposit-general-tab.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, + RouterLink, FaIconComponent, MatDivider, MatTable, @@ -63,52 +58,90 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; YesnoPipe ] }) -export class FixedDepositGeneralTabComponent { +export class FixedDepositGeneralTabComponent implements OnInit { private route = inject(ActivatedRoute); + private accounting = inject(Accounting); + private productsService = inject(ProductsService); /** Fixed Deposit Product data. */ fixedDepositProductData: any; fixedDepositProductsTemplate: any; - chartSlabsIncentivesDisplayedColumns: string[] = ['incentives']; - chartSlabsDisplayedColumns: string[] = [ - 'period', - 'amountRange', - 'annualInterestRate', + fdInterestRates: any[] = []; + + accountingMappings: any = {}; + accountingRule: OptionData; + + readonly fdSlabColumns = [ + 'srNo', 'description', - 'actions' + 'fromPeriod', + 'toPeriod', + 'periodTypeLabel', + 'annualInterestRate' ]; - incentivesDisplayedColumns: string[] = [ - 'entityType', - 'attributeName', - 'conditionType', - 'attributeValue', - 'incentiveType', - 'amount' - ]; - chargesDisplayedColumns: string[] = [ + readonly chargesDisplayedColumns = [ 'name', 'type', 'amount', 'collectedon' ]; - paymentFundSourceDisplayedColumns: string[] = [ + readonly paymentFundSourceDisplayedColumns = [ 'paymentTypeId', 'fundSourceAccountId' ]; - feesPenaltyIncomeDisplayedColumns: string[] = [ + readonly feesPenaltyIncomeDisplayedColumns = [ 'chargeId', 'incomeAccountId' ]; - /** - * Retrieves the fixed deposit product data from `resolve`. - * @param {ActivatedRoute} route Activated Route. - */ constructor() { this.route.data.subscribe((data: { fixedDepositProduct: any; fixedDepositProductsTemplate: any }) => { this.fixedDepositProductData = data.fixedDepositProduct; this.fixedDepositProductsTemplate = data.fixedDepositProductsTemplate; }); } + + ngOnInit(): void { + this.productsService.getFdBaseInterestRates().subscribe({ + next: (rates: any[]) => { + this.fdInterestRates = rates || []; + this.buildAccountingMappings(); + }, + error: () => { + this.fdInterestRates = []; + this.buildAccountingMappings(); + } + }); + } + + get activeBaseVersion(): any { + return this.fdInterestRates.find((v: any) => v.isActive) ?? this.fdInterestRates[0] ?? null; + } + + get fdSlabs(): any[] { + return this.activeBaseVersion?.slabs || []; + } + + get otherGlMappings(): any { + return this.fixedDepositProductData?.otherGlMappings || this.fixedDepositProductData?.otherGlMappingsMap || null; + } + + private buildAccountingMappings(): void { + const data = this.fixedDepositProductData; + if (!data) return; + this.accountingRule = data.accountingRule; + // accountingMappings come directly from the API response with full account objects + this.accountingMappings = data.accountingMappings || {}; + } + + isNoneAccounting(): boolean { + return this.accounting.isNoneAccounting(this.fixedDepositProductData?.accountingRule); + } + + 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/fixed-deposit-products/view-fixed-deposit-product/view-fixed-deposit-product.component.ts b/src/app/products/fixed-deposit-products/view-fixed-deposit-product/view-fixed-deposit-product.component.ts index 047a291f5..34c2318fe 100644 --- a/src/app/products/fixed-deposit-products/view-fixed-deposit-product/view-fixed-deposit-product.component.ts +++ b/src/app/products/fixed-deposit-products/view-fixed-deposit-product/view-fixed-deposit-product.component.ts @@ -8,7 +8,7 @@ /** Angular Imports */ import { Component, inject } from '@angular/core'; -import { ActivatedRoute, RouterLinkActive, RouterLink, RouterOutlet } from '@angular/router'; +import { ActivatedRoute, RouterLinkActive, RouterLink, RouterOutlet, Router } from '@angular/router'; import { MatTabNav, MatTabLink, MatTabNavPanel } from '@angular/material/tabs'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; @@ -30,6 +30,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; }) export class ViewFixedDepositProductComponent { private route = inject(ActivatedRoute); + private router = inject(Router); fixedDepositDatatables: any = []; diff --git a/src/app/products/products-routing.module.ts b/src/app/products/products-routing.module.ts index c6cb43e70..6838e83bd 100644 --- a/src/app/products/products-routing.module.ts +++ b/src/app/products/products-routing.module.ts @@ -685,44 +685,49 @@ const routes: Routes = [ breadcrumb: 'productId', routeParamBreadcrumb: 'productId' }, - component: ViewRecurringDepositProductComponent, resolve: { recurringDepositDatatables: SavingProductDatatablesResolver }, children: [ { path: '', - redirectTo: 'general', - pathMatch: 'full' - }, - { - path: 'general', - component: RecurringDepositGeneralTabComponent, - resolve: { - recurringDepositProduct: RecurringDepositProductResolver - } - }, - { - path: 'datatables', + component: ViewRecurringDepositProductComponent, children: [ { - path: ':datatableName', - component: RecurringDepositDatatableTabComponent, - data: { title: 'Data Table View', routeParamBreadcrumb: 'datatableName' }, + path: '', + redirectTo: 'general', + pathMatch: 'full' + }, + { + path: 'general', + component: RecurringDepositGeneralTabComponent, resolve: { - recurringDepositDatatable: SavingProductDatatableResolver + recurringDepositProduct: RecurringDepositProductResolver + } + }, + { + path: 'datatables', + children: [ + { + path: ':datatableName', + component: RecurringDepositDatatableTabComponent, + data: { title: 'Data Table View', routeParamBreadcrumb: 'datatableName' }, + resolve: { + recurringDepositDatatable: SavingProductDatatableResolver + } + } + ] + }, + { + path: 'interest-rates', + component: RdInterestRatesTabComponent, + data: { title: 'CBS Base Chart', breadcrumb: 'CBS Base Chart', routeParamBreadcrumb: false }, + resolve: { + rdInterestRates: rdInterestRatesResolver } } ] }, - { - 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 }, @@ -757,33 +762,38 @@ const routes: Routes = [ { path: ':productId', data: { title: 'View Fixed Deposit Product', breadcrumb: 'productId', routeParamBreadcrumb: 'productId' }, - component: ViewFixedDepositProductComponent, resolve: { fixedDepositDatatables: SavingProductDatatablesResolver }, children: [ { path: '', - redirectTo: 'general', - pathMatch: 'full' - }, - { - path: 'general', - component: FixedDepositGeneralTabComponent, - resolve: { - fixedDepositProduct: FixedDepositProductResolver - } - }, - { - path: 'datatables', + component: ViewFixedDepositProductComponent, children: [ { - path: ':datatableName', - component: FixedDepositDatatableTabComponent, - data: { title: 'Data Table View', routeParamBreadcrumb: 'datatableName' }, + path: '', + redirectTo: 'general', + pathMatch: 'full' + }, + { + path: 'general', + component: FixedDepositGeneralTabComponent, resolve: { - fixedDepositDatatable: SavingProductDatatableResolver + fixedDepositProduct: FixedDepositProductResolver } + }, + { + path: 'datatables', + children: [ + { + path: ':datatableName', + component: FixedDepositDatatableTabComponent, + data: { title: 'Data Table View', routeParamBreadcrumb: 'datatableName' }, + resolve: { + fixedDepositDatatable: SavingProductDatatableResolver + } + } + ] } ] }, diff --git a/src/app/products/products.service.ts b/src/app/products/products.service.ts index 82e0f2035..d2eada87c 100644 --- a/src/app/products/products.service.ts +++ b/src/app/products/products.service.ts @@ -546,6 +546,24 @@ export class ProductsService { return this.http.post('/recurringdepositproducts/baseinterestrates', payload); } + /** Returns the active FD CBS base interest-rate version(s), optionally filtered by fdType. */ + getFdBaseInterestRates(fdType?: string): Observable { + const url = fdType + ? `/fixeddepositproducts/baseinterestrates?fdType=${encodeURIComponent(fdType)}` + : '/fixeddepositproducts/baseinterestrates'; + return this.http.get(url); + } + + /** Returns the full FD CBS base chart history. */ + getFdBaseInterestRateHistory(): Observable { + return this.http.get('/fixeddepositproducts/baseinterestrates/history'); + } + + /** Saves a new FD CBS base interest-rate version. */ + saveFdBaseInterestRates(payload: any): Observable { + return this.http.post('/fixeddepositproducts/baseinterestrates', payload); + } + /** * @returns {Observable} Collateral data. */ 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 3b01d9081..bf3559643 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 @@ -149,6 +149,7 @@ [recurringDepositProductsTemplate]="recurringDepositProductsTemplate" [accountingRuleData]="accountingRuleData" [recurringDepositProductFormValid]="recurringDepositProductFormValid" + [isDormancyTrackingActive]="recurringDepositProductCurrencyForm.get('dormancyAlert')" >
diff --git a/src/app/products/recurring-deposit-products/create-recurring-deposit-product/create-recurring-deposit-product.component.ts b/src/app/products/recurring-deposit-products/create-recurring-deposit-product/create-recurring-deposit-product.component.ts index 3afd4956e..f30868277 100644 --- a/src/app/products/recurring-deposit-products/create-recurring-deposit-product/create-recurring-deposit-product.component.ts +++ b/src/app/products/recurring-deposit-products/create-recurring-deposit-product/create-recurring-deposit-product.component.ts @@ -123,23 +123,23 @@ export class CreateRecurringDepositProductComponent { } get recurringDepositProductDetailsForm() { - return this.recurringDepositProductDetailsStep.recurringDepositProductDetailsForm; + return this.recurringDepositProductDetailsStep?.recurringDepositProductDetailsForm; } get recurringDepositProductCurrencyForm() { - return this.recurringDepositProductCurrencyStep.recurringDepositProductCurrencyForm; + return this.recurringDepositProductCurrencyStep?.recurringDepositProductCurrencyForm; } get recurringDepositProductTermsForm() { - return this.recurringDepositProductTermsStep.recurringDepositProductTermsForm; + return this.recurringDepositProductTermsStep?.recurringDepositProductTermsForm; } get recurringDepositProductSettingsForm() { - return this.recurringDepositProductSettingsStep.recurringDepositProductSettingsForm; + return this.recurringDepositProductSettingsStep?.recurringDepositProductSettingsForm; } get recurringDepositProductAccountingForm() { - return this.recurringDepositProductAccountingStep.recurringDepositProductAccountingForm; + return this.recurringDepositProductAccountingStep?.recurringDepositProductAccountingForm; } get totalTabs(): number { @@ -147,13 +147,7 @@ export class CreateRecurringDepositProductComponent { } get recurringDepositProductFormValid() { - return ( - this.recurringDepositProductDetailsForm.valid && - this.recurringDepositProductCurrencyForm.valid && - this.recurringDepositProductTermsForm.valid && - this.recurringDepositProductSettingsForm.valid && - this.recurringDepositProductAccountingForm.valid - ); + return !!this.recurringDepositProductDetailsForm?.valid; } get activeBaseVersion() { 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 975cc9065..1a456d361 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 @@ -148,12 +148,14 @@ + [recurringDepositProductFormValid]="recurringDepositProductFormValid" + [isDormancyTrackingActive]="recurringDepositProductCurrencyForm.get('dormancyAlert')" + > +
- @if (recurringDepositProductFormValidAndNotPrinstine) { + @if (recurringDepositProductFormValid) { @@ -189,7 +191,7 @@ } - @if (recurringDepositProductFormValidAndNotPrinstine && selectedTabIndex === totalTabs - 1) { + @if (recurringDepositProductFormValid && selectedTabIndex === totalTabs - 1) {
@for (row of section.rows; track row.key) { - @if (!row.accrualOnly || isAccrualAccounting()) { + @if ( + (!row.accrualOnly || isAccrualAccounting()) && (!row.dormancyOnly || isDormancyTrackingEnabled()) + ) {
v.length) ? otherGlMappings : null; } + private setDormancyControl() { + this.isDormancyTrackingActive?.valueChanges?.subscribe(() => { + this.syncAccountingControls(this.recurringDepositProductAccountingForm.value.accountingRule); + }); + } + + isDormancyTrackingEnabled(): boolean { + return this.isDormancyTrackingActive?.value ?? this.isDormancyTrackingActive ?? false; + } + private syncAccountingControls(accountingRule: number) { - const addControl = (name: string, required = false) => { - if (!this.recurringDepositProductAccountingForm.contains(name)) { - this.recurringDepositProductAccountingForm.addControl( - name, - new UntypedFormControl('', required ? Validators.required : undefined) - ); - } else if (required) { - this.recurringDepositProductAccountingForm.get(name)?.setValidators(Validators.required); - this.recurringDepositProductAccountingForm.get(name)?.updateValueAndValidity({ emitEvent: false }); - } - }; - const removeControl = (name: string) => { - if (this.recurringDepositProductAccountingForm.contains(name)) { - this.recurringDepositProductAccountingForm.removeControl(name); - } - }; - - if (this.accounting.isCashOrAccrualAccountingRuleId(accountingRule)) { - addControl('savingsReferenceAccountId', true); - addControl('savingsControlAccountId', true); - addControl('transfersInSuspenseAccountId', true); - addControl('interestOnSavingsAccountId', true); - addControl('incomeFromFeeAccountId', true); - addControl('incomeFromPenaltyAccountId', true); - - if (this.accounting.isAccrualAccountingRuleId(accountingRule)) { - addControl('feesReceivableAccountId', true); - addControl('penaltiesReceivableAccountId', true); - addControl('interestPayableAccountId', true); + const setRequired = (name: string, required: boolean) => { + const ctrl = this.recurringDepositProductAccountingForm.get(name); + if (!ctrl) return; + if (required) { + ctrl.setValidators(Validators.required); } else { - removeControl('feesReceivableAccountId'); - removeControl('penaltiesReceivableAccountId'); - removeControl('interestPayableAccountId'); + ctrl.clearValidators(); } - } else { - [ - 'savingsReferenceAccountId', - 'savingsControlAccountId', - 'transfersInSuspenseAccountId', - 'interestOnSavingsAccountId', - 'incomeFromFeeAccountId', - 'incomeFromPenaltyAccountId', - 'feesReceivableAccountId', - 'penaltiesReceivableAccountId', - 'interestPayableAccountId' - ].forEach(removeControl); + ctrl.updateValueAndValidity({ emitEvent: false }); + }; + + const cashOrAccrual = this.accounting.isCashOrAccrualAccountingRuleId(accountingRule); + const accrualOnly = this.accounting.isAccrualAccountingRuleId(accountingRule); + + setRequired('savingsReferenceAccountId', cashOrAccrual); + setRequired('savingsControlAccountId', cashOrAccrual); + setRequired('transfersInSuspenseAccountId', cashOrAccrual); + setRequired('interestOnSavingsAccountId', cashOrAccrual); + setRequired('incomeFromFeeAccountId', cashOrAccrual); + setRequired('incomeFromPenaltyAccountId', cashOrAccrual); + setRequired('feesReceivableAccountId', accrualOnly); + setRequired('penaltiesReceivableAccountId', accrualOnly); + setRequired('interestPayableAccountId', accrualOnly); + setRequired('escheatLiabilityId', cashOrAccrual && this.isDormancyTrackingEnabled()); + + if (!cashOrAccrual) { this.clearOtherGlMappings(); } } @@ -331,6 +335,14 @@ export class RecurringDepositProductAccountingStepComponent implements OnInit { controlName: 'interestPayableAccountId', options: this.liabilityAccountOptions, accrualOnly: true + }, + { + key: 'escheatLiabilityId', + label: 'Escheat Liability Account', + inputLabel: 'Escheat liability account', + controlName: 'escheatLiabilityId', + options: this.liabilityAccountOptions, + dormancyOnly: true } ], otherMappingsControlName: 'liabilitiesOtherGlMappings', diff --git a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-currency-step/recurring-deposit-product-currency-step.component.scss b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-currency-step/recurring-deposit-product-currency-step.component.scss index c2bcdef6b..d7a2abd40 100644 --- a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-currency-step/recurring-deposit-product-currency-step.component.scss +++ b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-currency-step/recurring-deposit-product-currency-step.component.scss @@ -15,7 +15,6 @@ $gray-900: #202124; .rdpc-section { border: 1px solid $border; border-radius: 10px; - overflow: hidden; background: #fff; & + & { diff --git a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-details-step/recurring-deposit-product-details-step.component.scss b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-details-step/recurring-deposit-product-details-step.component.scss index fd6cd1cb3..ca87b04f4 100644 --- a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-details-step/recurring-deposit-product-details-step.component.scss +++ b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-details-step/recurring-deposit-product-details-step.component.scss @@ -15,7 +15,6 @@ $gray-900: #202124; .rdpd-section { border: 1px solid $border; border-radius: 10px; - overflow: hidden; background: #fff; & + & { diff --git a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-settings-step/recurring-deposit-product-settings-step.component.scss b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-settings-step/recurring-deposit-product-settings-step.component.scss index 53e82e31e..3aefca054 100644 --- a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-settings-step/recurring-deposit-product-settings-step.component.scss +++ b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-settings-step/recurring-deposit-product-settings-step.component.scss @@ -15,7 +15,6 @@ $gray-900: #202124; .rdps-section { border: 1px solid $border; border-radius: 10px; - overflow: hidden; background: #fff; & + & { diff --git a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-terms-step/recurring-deposit-product-terms-step.component.scss b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-terms-step/recurring-deposit-product-terms-step.component.scss index e5e2608bc..b2e03a37d 100644 --- a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-terms-step/recurring-deposit-product-terms-step.component.scss +++ b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-terms-step/recurring-deposit-product-terms-step.component.scss @@ -15,7 +15,6 @@ $gray-900: #202124; .rdpt-section { border: 1px solid $border; border-radius: 10px; - overflow: hidden; background: #fff; & + & { diff --git a/src/app/products/recurring-deposit-products/view-recurring-deposit-product/view-recurring-deposit-product.component.ts b/src/app/products/recurring-deposit-products/view-recurring-deposit-product/view-recurring-deposit-product.component.ts index 8c90de2e3..7b9dad9ff 100644 --- a/src/app/products/recurring-deposit-products/view-recurring-deposit-product/view-recurring-deposit-product.component.ts +++ b/src/app/products/recurring-deposit-products/view-recurring-deposit-product/view-recurring-deposit-product.component.ts @@ -7,7 +7,7 @@ */ import { Component, inject } from '@angular/core'; -import { ActivatedRoute, RouterLinkActive, RouterLink, RouterOutlet } from '@angular/router'; +import { ActivatedRoute, RouterLinkActive, RouterLink, RouterOutlet, Router } from '@angular/router'; import { MatTabNav, MatTabLink, MatTabNavPanel } from '@angular/material/tabs'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; @@ -27,6 +27,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; }) export class ViewRecurringDepositProductComponent { private route = inject(ActivatedRoute); + private router = inject(Router); recurringDepositDatatables: any = []; diff --git a/src/environments/.env.ts b/src/environments/.env.ts index 8ce4b3249..ab249d204 100644 --- a/src/environments/.env.ts +++ b/src/environments/.env.ts @@ -5,12 +5,13 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + // IMPORTANT: THIS FILE IS AUTO GENERATED! DO NOT MANUALLY EDIT OR CHECKIN! /* tslint:disable */ export default { 'mifos_x': { - 'version': '260430', - 'hash': '2d4fb423' + 'version': '260504', + 'hash': 'e9d2f4b5' }, 'allow_switching_backend_instance': true };