From a12ba20de4c9d7fe302ed2ce522b264d7cf766a3 Mon Sep 17 00:00:00 2001 From: Nitesh Yadav Date: Thu, 7 May 2026 11:17:49 +0530 Subject: [PATCH] update fd account --- ...reate-fixed-deposit-account.component.html | 56 +- .../create-fixed-deposit-account.component.ts | 53 +- ...posit-account-currency-step.component.html | 66 + ...posit-account-currency-step.component.scss | 124 ++ ...deposit-account-currency-step.component.ts | 55 + ...eposit-account-details-step.component.html | 922 +++++++++- ...eposit-account-details-step.component.scss | 1549 ++++++++++++++++- ...-deposit-account-details-step.component.ts | 503 +++++- ...nt-interest-rate-chart-step.component.html | 566 ++++-- ...nt-interest-rate-chart-step.component.scss | 868 ++++++++- ...ount-interest-rate-chart-step.component.ts | 229 ++- ...eposit-account-preview-step.component.html | 959 +++++----- ...eposit-account-preview-step.component.scss | 493 +++++- ...-deposit-account-preview-step.component.ts | 53 +- src/environments/.env.ts | 5 +- 15 files changed, 5727 insertions(+), 774 deletions(-) create mode 100644 src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-currency-step/fixed-deposit-account-currency-step.component.html create mode 100644 src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-currency-step/fixed-deposit-account-currency-step.component.scss create mode 100644 src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-currency-step/fixed-deposit-account-currency-step.component.ts diff --git a/src/app/deposits/fixed-deposits/create-fixed-deposit-account/create-fixed-deposit-account.component.html b/src/app/deposits/fixed-deposits/create-fixed-deposit-account/create-fixed-deposit-account.component.html index 66a5f262e..69239f86e 100644 --- a/src/app/deposits/fixed-deposits/create-fixed-deposit-account/create-fixed-deposit-account.component.html +++ b/src/app/deposits/fixed-deposits/create-fixed-deposit-account/create-fixed-deposit-account.component.html @@ -45,38 +45,15 @@ + @if (fixedDepositsAccountProductTemplate) { - - {{ 'labels.inputs.TERMS' | translate }} + Currency - - - -
- - -
-
- - - - {{ 'labels.inputs.SETTINGS' | translate }} - - - +
- - - {{ 'labels.inputs.CHARGES' | translate }} - - - - -
- - -
-
- {{ 'labels.inputs.PREVIEW' | translate }} @@ -142,6 +97,7 @@ [fixedDepositsAccountTemplate]="fixedDepositsAccountTemplate" [fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate" [fixedDepositAccountData]="fixedDepositAccount" + [selectedTenure]="selectedTenure" (submitEvent)="submit()" > diff --git a/src/app/deposits/fixed-deposits/create-fixed-deposit-account/create-fixed-deposit-account.component.ts b/src/app/deposits/fixed-deposits/create-fixed-deposit-account/create-fixed-deposit-account.component.ts index 0251f0b87..f788ed178 100644 --- a/src/app/deposits/fixed-deposits/create-fixed-deposit-account/create-fixed-deposit-account.component.ts +++ b/src/app/deposits/fixed-deposits/create-fixed-deposit-account/create-fixed-deposit-account.component.ts @@ -16,9 +16,7 @@ import { SettingsService } from 'app/settings/settings.service'; /** Custom Components */ import { FixedDepositAccountDetailsStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-details-step/fixed-deposit-account-details-step.component'; -import { FixedDepositAccountTermsStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-terms-step/fixed-deposit-account-terms-step.component'; -import { FixedDepositAccountSettingsStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-settings-step/fixed-deposit-account-settings-step.component'; -import { FixedDepositAccountChargesStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-charges-step/fixed-deposit-account-charges-step.component'; +import { FixedDepositAccountCurrencyStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-currency-step/fixed-deposit-account-currency-step.component'; import { Dates } from 'app/core/utils/dates'; import { MatStepper, @@ -50,10 +48,8 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; MatStep, MatStepLabel, FixedDepositAccountDetailsStepComponent, - FixedDepositAccountTermsStepComponent, - FixedDepositAccountSettingsStepComponent, FixedDepositAccountInterestRateChartStepComponent, - FixedDepositAccountChargesStepComponent, + FixedDepositAccountCurrencyStepComponent, FixedDepositAccountPreviewStepComponent ] }) @@ -66,17 +62,15 @@ export class CreateFixedDepositAccountComponent { @ViewChild(FixedDepositAccountDetailsStepComponent, { static: true }) fixedDepositsAccountDetailsStep: FixedDepositAccountDetailsStepComponent; - @ViewChild(FixedDepositAccountTermsStepComponent) - fixedDepositAccountTermsStep: FixedDepositAccountTermsStepComponent; - @ViewChild(FixedDepositAccountSettingsStepComponent) - fixedDepositAccountSettingsStep: FixedDepositAccountSettingsStepComponent; - @ViewChild(FixedDepositAccountChargesStepComponent) - fixedDepositAccountChargesStep: FixedDepositAccountChargesStepComponent; + @ViewChild(FixedDepositAccountCurrencyStepComponent) + fixedDepositAccountCurrencyStep: FixedDepositAccountCurrencyStepComponent; /** Fixed Deposits Account Template */ fixedDepositsAccountTemplate: any; /** Fixed Deposit Account Product Template */ fixedDepositsAccountProductTemplate: any; + /** Tenure selected from the interest rate chart step */ + selectedTenure: { depositPeriod: number; depositPeriodFrequencyId: number } | null = null; /** * Fetches FD account template from `resolve` @@ -100,36 +94,22 @@ export class CreateFixedDepositAccountComponent { this.fixedDepositsAccountProductTemplate = $event; } + onTenureSelected($event: { depositPeriod: number; depositPeriodFrequencyId: number }) { + this.selectedTenure = $event; + } + /** * Retrieves Fixed Deposit Account Details Form Data */ get fixedDepositAccountDetailsForm() { - return this.fixedDepositsAccountDetailsStep.fixedDepositAccountDetailsForm; - } - - /** - * Retrieves Fixed Deposit Account Terms Form Data - */ - get fixedDepositAccountTermsForm() { - return this.fixedDepositAccountTermsStep.fixedDepositAccountTermsForm; - } - - /** - * Retrieves Fixed Deposit Account Settings Form Data - */ - get fixedDepositAccountSettingsForm() { - return this.fixedDepositAccountSettingsStep.fixedDepositAccountSettingsForm; + return this.fixedDepositsAccountDetailsStep?.fixedDepositAccountDetailsForm; } /** * Checks stepper validity. */ get fixedDepositAccountFormValid() { - return ( - this.fixedDepositAccountDetailsForm.valid && - this.fixedDepositAccountTermsForm.valid && - this.fixedDepositAccountSettingsForm.valid - ); + return !!this.fixedDepositAccountDetailsForm?.valid; } /** @@ -137,10 +117,8 @@ export class CreateFixedDepositAccountComponent { */ get fixedDepositAccount() { return { - ...this.fixedDepositsAccountDetailsStep.fixedDepositAccountDetails, - ...this.fixedDepositAccountTermsStep.fixedDepositAccountTerms, - ...this.fixedDepositAccountSettingsStep.fixedDepositAccountSettings, - ...this.fixedDepositAccountChargesStep.fixedDepositAccountCharges + ...this.fixedDepositsAccountDetailsStep?.fixedDepositAccountDetails, + ...this.fixedDepositAccountCurrencyStep?.currencyStepData }; } @@ -153,6 +131,7 @@ export class CreateFixedDepositAccountComponent { const monthDayFormat = 'dd MMMM'; const fixedDepositAccount = { ...this.fixedDepositAccount, + ...(this.selectedTenure ?? {}), clientId: this.fixedDepositsAccountTemplate.clientId, charges: this.fixedDepositAccount.charges.map((charge: any) => ({ chargeId: charge.id, @@ -163,7 +142,7 @@ export class CreateFixedDepositAccountComponent { feeInterval: charge.feeInterval })), submittedOnDate: this.dateUtils.formatDate(this.fixedDepositAccount.submittedOnDate, dateFormat), - charts: [{ chartSlabs: this.fixedDepositsAccountProductTemplate.accountChart.chartSlabs }], + charts: [{ chartSlabs: this.fixedDepositsAccountProductTemplate.accountChart?.chartSlabs ?? [] }], dateFormat, monthDayFormat, locale diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-currency-step/fixed-deposit-account-currency-step.component.html b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-currency-step/fixed-deposit-account-currency-step.component.html new file mode 100644 index 000000000..5f920e61b --- /dev/null +++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-currency-step/fixed-deposit-account-currency-step.component.html @@ -0,0 +1,66 @@ + + +
+
+ + Currency & Account Status Configuration + From Product +
+
+
+ + Currency + + Inherited from selected product + + + + Decimal Places + + Inherited from selected product + +
+ +
+
+

Auto Debit Facility

+

Enable automatic deduction of funds for recurring payments, utility bills, or EMI obligations.

+
+ +
+ +
+
+

Dormancy Alert

+

Enable automated notifications when an account becomes inactive based on product rules.

+
+ + {{ fixedDepositsAccountProductTemplate?.dormancyAlert ? 'Yes' : 'No' }} + +
+ @if (fixedDepositsAccountProductTemplate?.dormancyAlert) { + + } + +
+
+

Nomination Facility

+

Allow account holders to designate individuals to receive funds in the event of account holder's death.

+
+ + {{ fixedDepositsAccountProductTemplate?.nominationFacility ? 'Yes' : 'No' }} + +
+ @if (fixedDepositsAccountProductTemplate?.nominationFacility) { + + } +
+
diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-currency-step/fixed-deposit-account-currency-step.component.scss b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-currency-step/fixed-deposit-account-currency-step.component.scss new file mode 100644 index 000000000..ccfce3c23 --- /dev/null +++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-currency-step/fixed-deposit-account-currency-step.component.scss @@ -0,0 +1,124 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +.facc-section { + border: 1px solid #e0e0e0; + border-radius: 8px; + background-color: #fff; + overflow: hidden; + margin-bottom: 1.5rem; + + &__header { + background-color: #f8fafc; + padding: 0.75rem 1.25rem; + border-bottom: 1px solid #e0e0e0; + display: flex; + align-items: center; + gap: 0.75rem; + + i { + color: #1976d2; + font-size: 1rem; + } + + span { + font-weight: 600; + color: #333; + font-size: 0.9rem; + } + } +} + +.facc-body { + padding: 1.25rem; +} + +.facc-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1.25rem; + margin-bottom: 1rem; + + @media (width <= 600px) { + grid-template-columns: 1fr; + } +} + +.facc-field { + width: 100%; +} + +.facc-alert { + display: flex; + align-items: center; + justify-content: space-between; + padding: 1rem 1.25rem; + border: 1px solid #e2e8f0; + border-radius: 8px; + margin-bottom: 1rem; + background-color: #fff; + gap: 1.5rem; + + &--editable { + border-color: #bfdbfe; + background-color: #eff6ff; + } + + &__content { + flex: 1; + + h4 { + margin: 0 0 0.25rem; + font-size: 0.95rem; + font-weight: 600; + color: #1e293b; + } + + p { + margin: 0; + font-size: 0.8rem; + color: #64748b; + line-height: 1.4; + } + } +} + +.facc-badge { + padding: 0.2rem 0.6rem; + border-radius: 20px; + font-size: 0.7rem; + font-weight: 700; + text-transform: uppercase; + + &--from { + background-color: #dbeafe; + color: #1e40af; + } +} + +.facc-yn { + padding: 0.25rem 1rem; + border-radius: 6px; + font-size: 0.85rem; + font-weight: 600; + background-color: #fee2e2; + color: #991b1b; + min-width: 60px; + text-align: center; + + &--yes { + background-color: #dcfce7; + color: #166534; + } +} + +:host ::ng-deep { + .mat-mdc-form-field-subscript-wrapper { + font-size: 0.75rem; + } +} diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-currency-step/fixed-deposit-account-currency-step.component.ts b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-currency-step/fixed-deposit-account-currency-step.component.ts new file mode 100644 index 000000000..dd2cac625 --- /dev/null +++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-currency-step/fixed-deposit-account-currency-step.component.ts @@ -0,0 +1,55 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; +import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; +import { NomineeFormComponent } from 'app/shared/nominee-form/nominee-form.component'; +import { DormancyAlertPanelComponent } from 'app/shared/dormancy-alert-panel/dormancy-alert-panel.component'; +import { MatSlideToggle } from '@angular/material/slide-toggle'; +import { FormsModule } from '@angular/forms'; + +@Component({ + selector: 'mifosx-fixed-deposit-account-currency-step', + templateUrl: './fixed-deposit-account-currency-step.component.html', + styleUrls: ['./fixed-deposit-account-currency-step.component.scss'], + imports: [ + ...STANDALONE_SHARED_IMPORTS, + NomineeFormComponent, + DormancyAlertPanelComponent, + MatSlideToggle, + FormsModule + ] +}) +export class FixedDepositAccountCurrencyStepComponent implements OnChanges { + @Input() fixedDepositsAccountProductTemplate: any; + + nomineeData: any = null; + + autoDebitFacility = false; + + // Editable feature toggles (pre-filled from product, overridable by user) + + ngOnChanges(changes: SimpleChanges): void { + if (changes['fixedDepositsAccountProductTemplate']?.currentValue) { + const pt = changes['fixedDepositsAccountProductTemplate'].currentValue; + this.autoDebitFacility = !!pt.autoDebitFacility; + } + } + + onNomineeChange(event: any): void { + this.nomineeData = event; + } + + /** Called by parent to collect currency step values */ + get currencyStepData() { + return { + autoDebitFacility: this.autoDebitFacility, + ...(this.nomineeData ? { nominee: this.nomineeData } : {}) + }; + } +} diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-details-step/fixed-deposit-account-details-step.component.html b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-details-step/fixed-deposit-account-details-step.component.html index 6fc8de446..cb87d103a 100644 --- a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-details-step/fixed-deposit-account-details-step.component.html +++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-details-step/fixed-deposit-account-details-step.component.html @@ -7,67 +7,883 @@ -->
-
- - {{ 'labels.inputs.Product Name' | translate }} - - @for (product of productData; track product) { - - {{ product.name }} - - } - - - {{ 'labels.inputs.Product Name' | translate }} {{ 'labels.commons.is' | translate }} - {{ 'labels.commons.required' | translate }} - - - - @if (isProductSelected) { - - {{ 'labels.inputs.Submitted On' | translate }} - - - + +
+
+ +

Account Details

+
+
+ + {{ 'labels.inputs.Product Name' | translate }} + + @for (product of productData; track product) { + {{ product.name }} + } + - {{ 'labels.inputs.Submission Date' | translate }} {{ 'labels.commons.is' | translate }} + {{ 'labels.inputs.Product Name' | translate }} {{ 'labels.commons.is' | translate }} {{ 'labels.commons.required' | translate }} - } - @if (isProductSelected) { - - {{ 'labels.inputs.Field Officer' | translate }} - - @for (officer of fieldOfficerData; track officer) { - - {{ officer.displayName }} - - } - - - } + @if (isProductSelected) { + + {{ 'labels.inputs.Submitted On' | translate }} + + + + + {{ 'labels.inputs.Submitted On' | translate }} {{ 'labels.commons.is' | translate }} + {{ 'labels.commons.required' | translate }} + + - @if (isProductSelected) { - - {{ 'labels.inputs.External id' | translate }} - - - } + + {{ 'labels.inputs.Field Officer' | translate }} + + @for (officer of fieldOfficerData; track officer) { + {{ officer.displayName }} + } + + + + + {{ 'labels.inputs.External id' | translate }} + + + } +
+ @if (isProductSelected && productTemplate) { + +
+
+ +

Product Classification

+ From Product +
+
+ + Account Type + + @for (opt of accountTypeOptions; track opt) { + {{ opt }} + } + + + + @if (productTemplate?.fdType) { + + FD Type + + + } + + + Customer Segment + + @for (opt of customerSegmentOptions; track opt) { + {{ opt }} + } + + + + + Account No + + + + @if (productTemplate.effectiveDate) { + + Effective Date + + + } + + @if (productTemplate.endDate) { + + End Date + + + } + +
+ Deposit Account Link + + {{ productTemplate.shareLinkingEnabled ? 'Yes' : 'No' }} + +
+
+
+ + +
+
+ +

Currency

+ From Product +
+
+ + {{ 'labels.inputs.Currency' | translate }} + + + + + {{ 'labels.inputs.Decimal Places' | translate }} + + + + + {{ 'labels.inputs.Currency In Multiples Of' | translate }} + + +
+
+ + + @if (productTemplate?.shareLinkingEnabled) { +
+
+
+
+ link +
+
+

Deposit Account Link

+ Link an existing Savings or Current account to this FD +
+
+
+ +
+ @if (dalLoading) { +
+ hourglass_top + Loading accounts… +
+ } @else if (selectedLinkedAccount) { + +
+
+ + {{ + selectedLinkedAccount.accountType === 'SAVINGS' ? 'savings' : 'account_balance' + }} + {{ selectedLinkedAccount.accountType === 'SAVINGS' ? 'Savings' : 'Current' }} + +
+ {{ selectedLinkedAccount.accountNo }} + {{ selectedLinkedAccount.productName }} +
+
+ Balance + {{ selectedLinkedAccount.currencyCode }} + {{ selectedLinkedAccount.balance | number: '1.2-2' }} +
+
+ +
+ } @else { + + @if (!dalSelectedType) { +
+

Select account type to link:

+
+ + +
+
+ } @else { + +
+
+ + + {{ dalSelectedType === 'SAVINGS' ? 'savings' : 'account_balance' }} + {{ dalSelectedType === 'SAVINGS' ? 'Savings Accounts' : 'Current Accounts' }} + +
+ + + Search by Account No. or Product + search + + @if (dalSearch) { + + } + + + @if (dalFilteredAccounts.length === 0) { +
+ search_off + No {{ dalSelectedType === 'SAVINGS' ? 'Savings' : 'Current' }} accounts found. +
+ } @else { +
+ @for (account of dalFilteredAccounts; track account.id) { +
+
+ {{ account.accountNo }} + {{ account.productName }} +
+
+ {{ account.status }} + {{ account.currencyCode }} {{ account.balance | number: '1.2-2' }} +
+ chevron_right +
+ } +
+ } +
+ } + } +
+
+ } + } + + + @if (isProductSelected) { +
+
+
+
+ +
+
+

Status of Account

+ As per RBI guidelines — select account holder type +
+
+ RBI Compliant +
+ + + @if (clientDob) { +
+
+ cake + Date of Birth: {{ formatTemplateDate(clientDob) }} +
+
+ person_pin + Age: {{ clientAge }} years +
+ @if (clientAge !== null && clientAge < 18) { + + child_care Auto-detected: Minor + + } @else if (clientAge !== null && clientAge >= 60) { + + elderly Auto-detected: Senior Citizen + + } @else { + check_circle Age Verified + } +
+ } + + +
+ @for (opt of accountStatusOptions; track opt.value) { + + } +
+ + + @if (selectedAccountStatus === 'JOINT') { +
+
+
+ A +
+ Joint Account Holders + Add all co-applicants linked to this joint FD account +
+
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + @for (h of jointHolders; track h; let i = $index) { + + + + + + + + + + } + + + + + + + + + + +
Sr.Holder NameCIF No.Ownership %Signing AuthorityRoleAction
1 +
+
+ {{ primaryClientName ? primaryClientName[0].toUpperCase() : '?' }} +
+
+ {{ primaryClientName || 'Primary Holder' }} + Primary Account Holder +
+
+
+ {{ primaryClientCif || '—' }} + + + % + + + lock Mandatory + + Primary Account Holder—
{{ i + 2 }} +
+
+ {{ h.clientName[0].toUpperCase() }} +
+
+ {{ h.clientName }} + @if (h.isMandatory) { + Mandatory Signatory + } +
+
+
+ {{ h.clientCif || '—' }} + + + {{ h.sharePercentage }}% + + + + Co-Holder + +
Total Ownership + + {{ totalJointSharePercentage }}% + + + + lock {{ mandatoryJointCount + 1 }} Mandatory + +
+
+ + @if (jointHolders.length === 0) { +
+ group_add + Click Add Holder to add joint account holders. +
+ } + + @if (totalJointSharePercentage !== 100) { +
+ warning_amber + Total ownership is {{ totalJointSharePercentage }}%. It must equal 100%. +
+ } @else { +
+ check_circle + Ownership allocation is complete (100%). +
+ } +
+ +
+
+
+ B +
+ Mode of Operation + Auto-set from mandatory holders — override manually if required +
+
+ @if (modeOfOperation) { + auto_awesome Auto + } +
+ + @if (jointHolders.length === 0) { +
+ how_to_vote + Add joint holders in Section A first. +
+ } @else { +
+ @for (opt of modeOfOperationOptions; track opt.value) { + + } +
+ +
+ @if (modeOfOperation) { + how_to_vote + Mode of Operation: {{ selectedModeLabel }} + + ({{ mandatoryJointCount }} Mandatory + {{ jointHolders.length - mandatoryJointCount }} Optional) + + } @else { + info + Set Mandatory/Optional for holders above to auto-select mode. + } +
+ } +
+ } + + + @if (selectedAccountStatus === 'MINOR') { +
+
+
+ + child_care + +
+ Minor Account Details + As per RBI — guardian must be an existing registered client +
+
+ Guardian Required +
+ +
+ + Minor's Date of Birth + + + + @if (clientDob) { + Auto-filled from client profile + } + + + + Age + + Must be below 18 years + + + + Date of Majority (Auto) + + Auto-calculated: DOB + 18 years + + + + Guardian Relationship + + @for (rel of guardianRelationshipOptions; track rel) { + {{ rel }} + } + + +
+ + + +
+ info +
    +
  • Minor below 10 years — Guardian must operate the account.
  • +
  • Minor above 10 years — Can operate independently (bank discretion).
  • +
  • Account must be converted to regular savings on attaining majority.
  • +
  • Fresh KYC of the minor must be done on majority.
  • +
+
+
+ } + + + @if (selectedAccountStatus === 'SENIOR_CITIZEN') { +
+
+
+ + elderly + +
+ Senior Citizen Account + Age ≥ 60 years — eligible for preferential banking services +
+
+ Age ≥ 60 +
+ +
+ + Date of Birth + + From client profile + + + + Age + + Age ≥ 60 required for Senior Citizen + +
+ +
+
+ priority_high +
+ Priority Service + Dedicated counters / assisted banking at branches +
+
+
+ percent +
+ Preferential Interest Rate + Additional 0.25%–0.50% on deposits (as per product) +
+
+
+ health_and_safety +
+ Doorstep Banking + Eligible for home banking services (RBI circular) +
+
+
+ gavel +
+ Nomination Mandatory + RBI mandate — ensure nomination is registered +
+
+
+ +
+ info +
    +
  • Age proof (DOB document) mandatory at time of account opening.
  • +
  • Nomination registration is compulsory (Form DA-1).
  • +
  • Bank must offer dedicated helpline and priority grievance redressal.
  • +
  • Door-step banking services must be offered (RBI Master Direction 2023).
  • +
+
+
+ } + + + @if (selectedAccountStatus === 'INDIVIDUAL_MALE') { +
+
+
+ + person + +
+ Individual Account (Male) + Select special category if applicable — governs applicable schemes & benefits +
+
+ Special Category +
+ +
+ @for (opt of specialCategoryOptions; track opt.value) { + + } +
+ +
+ info +
    +
  • Valid KYC documents required (Aadhaar / PAN / Passport).
  • +
  • Nomination registration is mandatory (Form DA-1).
  • +
  • Defence / Ex-Serviceman — eligible for special savings schemes (DSSP).
  • +
  • NRI — FCNR / NRE / NRO account rules apply as per FEMA guidelines.
  • +
+
+
+ } + + + @if (selectedAccountStatus === 'INDIVIDUAL_FEMALE') { +
+
+
+ + person_outline + +
+ Individual Account (Female) + Select weaker section category if applicable — governs PMJDY & priority schemes +
+
+ Weaker Section +
+ +
+ @for (opt of weakerSectionOptions; track opt.value) { + + } +
+ +
+ info +
    +
  • Valid KYC documents required (Aadhaar / PAN / Passport).
  • +
  • Nomination registration is mandatory (Form DA-1).
  • +
  • Eligible for PMJDY overdraft facilities (subject to rules).
  • +
  • Financial literacy & priority processing for women applicants.
  • +
+
+
+ } + + + @if (selectedAccountStatus === 'SINGLE') { +
+
+
+ + account_circle + +
+ Single Account + Standard individual account operated by a single person +
+
+ Single Holder +
+ +
+ info +
    +
  • Only one primary holder allowed.
  • +
  • Nomination is highly recommended.
  • +
  • Full independent operation of account.
  • +
  • Convertible to joint account later if required.
  • +
+
+
+ } +
+ } +
- + +
+
+
+
+ +
+
+ Rate Calculator + Enter your deposit tenure to find the applicable interest rate +
+
+
+ +
+
+ +
+ +
+ + + @if (allowedPeriodUnits.length === 1) { + {{ allowedPeriodUnits[0] }} + } @else { +
+ @for (unit of allowedPeriodUnits; track unit) { + + } +
+ } +
+ + @if (fdType === 'SINGLE') { + Max 364 days allowed + } + @if (fdType === 'DOUBLE') { + Minimum 12 months required + } +
+ + + @if (isSpecial) { +
+ +
+ ₹ + +
+
+ } + +
+ + @if (tenureValue || matchResult) { + + } +
+
+ + + @if (validationWarning) { +
+ + {{ validationWarning }} +
+ } + + + @if (matchResult) { +
+
+ +
+
+ +
+ +
+ Your Selected FD Tenure + {{ tenureValue }} {{ tenurePeriodUnit }} +
+ Will be saved +
+ +
+ {{ matchResult.appliedRate | number: '1.2-2' }}% + Annual Interest Rate +
+
+
+ + Applicable Slab: + + {{ matchResult.slab.fromPeriod }}–{{ matchResult.slab.toPeriod ?? '∞' }} + {{ matchResult.slab.periodType?.value }} + + +
+ @if (matchResult.slab.description) { +
+ + {{ matchResult.slab.description }} +
+ } + @if (matchResult.slab.amountRangeFrom || matchResult.slab.amountRangeTo) { +
+ + Amount Range: + {{ matchResult.slab.amountRangeFrom | number }}–{{ + matchResult.slab.amountRangeTo | number + }} + +
+ } +
+ + + @if (isSpecial && matchResult.estimatedMonthlyPayout != null) { +
+ +
+ Estimated Monthly Payout + ₹ {{ matchResult.estimatedMonthlyPayout | number: '1.2-2' }} + = ₹{{ principalAmount | number }} × {{ matchResult.appliedRate }}% ÷ 12 +
+
+ } + @if (isSpecial && !matchResult.estimatedMonthlyPayout) { +
+ + Enter a principal amount above to see estimated monthly interest payout. +
+ } +
+
+ } +
+
+ + +
+
+
+ + Interest Rate Slabs + @if (interestRateChartData.length) { + {{ interestRateChartData.length }} slab{{ interestRateChartData.length !== 1 ? 's' : '' }} + } +
+ @if (matchResult) { + Matched slab is highlighted + } +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Period + + {{ slab.fromPeriod }} + @if (slab.toPeriod != null) { + – {{ slab.toPeriod }} + } @else { + + + } + + {{ slab.periodType?.value }} + Amount Range + @if (slab.amountRangeFrom || slab.amountRangeTo) { + {{ slab.amountRangeFrom | number }} – {{ slab.amountRangeTo | number }} + } @else { + — + } + Interest Rate + {{ slab.annualInterestRate | number: '1.2-2' }}% + Description + {{ slab.description || '—' }} + Incentives + @if (slab.incentives?.length) { + + } @else { + None + } + +
+ @if (slab.incentives?.length) { +
+
Incentives for this slab
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Entity Type{{ inc.entityType?.value }}Attribute{{ inc.attributeName?.value }}Condition{{ inc.conditionType?.value | titlecase }}Value{{ inc.attributeValueDesc }}Type{{ inc.incentiveType?.value }}Amount{{ inc.amount }}
+
+ } +
+
+ + @if (!interestRateChartData.length) { +
+ + No interest rate slabs defined for this product. +
+ } +
+
diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-interest-rate-chart-step/fixed-deposit-account-interest-rate-chart-step.component.scss b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-interest-rate-chart-step/fixed-deposit-account-interest-rate-chart-step.component.scss index 03ea8d084..d7220553c 100644 --- a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-interest-rate-chart-step/fixed-deposit-account-interest-rate-chart-step.component.scss +++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-interest-rate-chart-step/fixed-deposit-account-interest-rate-chart-step.component.scss @@ -6,26 +6,866 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -.margin-t { - margin-top: 2em; +$border: #e2e8f0; +$gray-50: #f8f9fa; +$gray-100: #f1f5f9; +$gray-200: #e8eaed; +$gray-600: #64748b; +$gray-700: #334155; +$gray-900: #0f172a; +$blue: #2563eb; +$blue-light: #dbeafe; +$green: #16a34a; +$green-light: #dcfce7; +$amber: #d97706; +$amber-light: #fef3c7; +$purple: #7c3aed; +$purple-light: #ede9fe; +$red: #dc2626; +$red-light: #fee2e2; + +.fdrc-wrapper { + display: flex; + flex-direction: column; + gap: 16px; } -table { - width: 100%; +// ── Chart Header Card ────────────────────────────────────────────────────── - tr.incentives-row { - height: 0; +.fdrc-header-card { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16px; + background: #fff; + border: 1px solid $border; + border-radius: 12px; + padding: 16px 20px; + flex-wrap: wrap; - > td { - padding: 0; + &__left { + display: flex; + align-items: center; + gap: 12px; + min-width: 0; + } + + &__icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 10px; + background: $blue-light; + color: $blue; + font-size: 18px; + flex-shrink: 0; + } + + &__title { + font-size: 15px; + font-weight: 700; + color: $gray-900; + } + + &__desc { + font-size: 12px; + color: $gray-600; + margin-top: 2px; + } + + &__meta { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 8px; + } + + &__dates { + display: flex; + gap: 12px; + flex-wrap: wrap; + justify-content: flex-end; + } +} + +.fdrc-meta-item { + display: flex; + align-items: center; + gap: 5px; + font-size: 12px; + color: $gray-700; + + i { + color: $gray-600; + } + + strong { + color: $gray-900; + } +} + +// ── Pills ────────────────────────────────────────────────────────────────── + +.fdrc-pill { + display: inline-flex; + align-items: center; + gap: 5px; + padding: 3px 10px; + border-radius: 999px; + font-size: 11px; + font-weight: 700; + letter-spacing: 0.03em; + white-space: nowrap; + + &--type { + background: #f1f5f9; + color: $gray-700; + } + + &--single { + background: $amber-light; + color: $amber; + } + + &--double { + background: $purple-light; + color: $purple; + } + + &--special { + background: $green-light; + color: $green; + } + + &--amount { + background: $blue-light; + color: $blue; + } +} + +// ── FD Type Rule Banner ──────────────────────────────────────────────────── + +.fdrc-rule-banner { + display: flex; + align-items: flex-start; + gap: 14px; + padding: 14px 18px; + border-radius: 10px; + border-left: 4px solid; + + &--single { + background: $amber-light; + border-color: $amber; + color: #b45309; + } + + &--double { + background: $purple-light; + border-color: $purple; + color: #6d28d9; + } + + &--special { + background: $green-light; + border-color: $green; + color: #15803d; + } + + &__icon { + font-size: 18px; + margin-top: 2px; + flex-shrink: 0; + } + + &__content { + display: flex; + flex-direction: column; + gap: 3px; + + strong { + font-size: 13px; + font-weight: 700; } - .incentives { - overflow: hidden; - } - - .mat-card { - border-radius: 0; + span { + font-size: 12px; } } } + +// ── Calculator Card ──────────────────────────────────────────────────────── + +.fdrc-calc-card { + background: #fff; + border: 1px solid $border; + border-radius: 12px; + overflow: hidden; + + &__header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 14px 20px; + background: $gray-50; + border-bottom: 1px solid $gray-200; + } + + &__header-left { + display: flex; + align-items: center; + gap: 12px; + } + + &__title { + font-size: 14px; + font-weight: 700; + color: $gray-900; + display: block; + } + + &__sub { + font-size: 12px; + color: $gray-600; + display: block; + margin-top: 1px; + } + + &__body { + padding: 20px; + } +} + +.fdrc-calc-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + border-radius: 8px; + background: $blue-light; + color: $blue; + font-size: 16px; + flex-shrink: 0; +} + +.fdrc-calc-inputs { + display: flex; + flex-wrap: wrap; + gap: 16px; + align-items: flex-end; +} + +.fdrc-calc-field { + display: flex; + flex-direction: column; + gap: 6px; + flex: 1 1 220px; + min-width: 180px; +} + +.fdrc-calc-label { + font-size: 12px; + font-weight: 600; + color: $gray-700; + letter-spacing: 0.04em; + text-transform: uppercase; +} + +.fdrc-calc-optional { + font-weight: 400; + text-transform: none; + letter-spacing: 0; + color: $gray-600; +} + +.fdrc-tenure-row { + display: flex; + align-items: center; + gap: 8px; +} + +.fdrc-tenure-input { + height: 42px; + border: 1.5px solid $border; + border-radius: 8px; + padding: 0 12px; + font-size: 15px; + font-weight: 600; + color: $gray-900; + background: #fff; + width: 130px; + transition: border-color 0.15s; + + &:focus { + outline: none; + border-color: $blue; + } + + &--amount { + width: 180px; + } +} + +.fdrc-period-badge { + display: inline-flex; + align-items: center; + padding: 0 14px; + height: 42px; + background: $gray-100; + border: 1.5px solid $border; + border-radius: 8px; + font-size: 13px; + font-weight: 700; + color: $gray-700; +} + +.fdrc-period-tabs { + display: flex; + border: 1.5px solid $border; + border-radius: 8px; + overflow: hidden; +} + +.fdrc-period-tab { + height: 42px; + padding: 0 14px; + font-size: 13px; + font-weight: 600; + border: none; + background: #fff; + color: $gray-600; + cursor: pointer; + transition: + background 0.15s, + color 0.15s; + + & + & { + border-left: 1px solid $border; + } + + &--active { + background: $blue; + color: #fff; + } +} + +.fdrc-amount-row { + display: flex; + align-items: center; +} + +.fdrc-amount-prefix { + display: inline-flex; + align-items: center; + justify-content: center; + width: 40px; + height: 42px; + background: $gray-100; + border: 1.5px solid $border; + border-right: none; + border-radius: 8px 0 0 8px; + font-size: 15px; + font-weight: 700; + color: $gray-700; +} + +.fdrc-amount-row .fdrc-tenure-input { + border-radius: 0 8px 8px 0; +} + +.fdrc-calc-hint { + font-size: 11px; + color: $amber; + display: flex; + align-items: center; + gap: 4px; +} + +.fdrc-calc-actions { + display: flex; + align-items: center; + gap: 10px; + flex-shrink: 0; + margin-top: 22px; +} + +.fdrc-calc-btn { + display: inline-flex; + align-items: center; + gap: 7px; + height: 42px; + padding: 0 22px; + background: $blue; + color: #fff; + border: none; + border-radius: 8px; + font-size: 14px; + font-weight: 700; + cursor: pointer; + transition: background 0.15s; + + &:hover { + background: #1d4ed8; + } +} + +.fdrc-calc-reset { + display: inline-flex; + align-items: center; + gap: 6px; + height: 42px; + padding: 0 16px; + background: transparent; + color: $gray-600; + border: 1.5px solid $border; + border-radius: 8px; + font-size: 13px; + font-weight: 600; + cursor: pointer; + transition: + border-color 0.15s, + color 0.15s; + + &:hover { + border-color: $red; + color: $red; + } +} + +// ── Alert ───────────────────────────────────────────────────────────────── + +.fdrc-alert { + display: flex; + align-items: flex-start; + gap: 10px; + padding: 12px 16px; + border-radius: 8px; + margin-top: 16px; + font-size: 13px; + + i { + margin-top: 1px; + flex-shrink: 0; + } + + &--warn { + background: $amber-light; + color: #b45309; + border: 1px solid rgba($amber, 0.4); + } +} + +// ── Tenure Banner (inside result card) ─────────────────────────────────── + +.fdrc-tenure-banner { + display: flex; + align-items: center; + gap: 12px; + padding: 10px 14px; + background: #fff; + border: 1.5px solid rgba($green, 0.4); + border-radius: 8px; + margin-bottom: 14px; + + > i { + font-size: 18px; + color: $green; + flex-shrink: 0; + } + + &__content { + display: flex; + flex-direction: column; + gap: 1px; + flex: 1; + } + + &__label { + font-size: 10px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.06em; + color: $gray-600; + } + + &__value { + font-size: 20px; + font-weight: 800; + color: $gray-900; + line-height: 1.2; + } + + &__saved { + display: inline-flex; + align-items: center; + gap: 5px; + padding: 4px 10px; + background: $green-light; + color: $green; + border-radius: 999px; + font-size: 11px; + font-weight: 700; + white-space: nowrap; + flex-shrink: 0; + } +} + +// ── Result Card ─────────────────────────────────────────────────────────────── + +.fdrc-result-card { + display: flex; + align-items: flex-start; + gap: 16px; + margin-top: 16px; + padding: 18px 20px; + background: $green-light; + border: 1.5px solid rgba($green, 0.35); + border-radius: 12px; + + &__check { + font-size: 26px; + color: $green; + flex-shrink: 0; + margin-top: 2px; + } + + &__body { + flex: 1; + min-width: 0; + } +} + +.fdrc-result-rate { + display: flex; + align-items: baseline; + gap: 8px; + margin-bottom: 10px; + + &__value { + font-size: 28px; + font-weight: 800; + color: $green; + line-height: 1; + } + + &__label { + font-size: 13px; + color: #166534; + font-weight: 600; + } +} + +.fdrc-result-meta { + display: flex; + flex-direction: column; + gap: 5px; + + &__item { + display: flex; + align-items: center; + gap: 7px; + font-size: 12px; + color: $gray-700; + + i { + color: $green; + font-size: 11px; + } + + strong { + color: $gray-900; + } + } +} + +// ── Monthly Payout (SPECIAL) ─────────────────────────────────────────────── + +.fdrc-payout-card { + display: flex; + align-items: center; + gap: 14px; + margin-top: 14px; + padding: 14px 16px; + background: #fff; + border: 1.5px solid rgba($green, 0.3); + border-radius: 10px; + + i { + font-size: 22px; + color: $green; + flex-shrink: 0; + } + + &__content { + display: flex; + flex-direction: column; + gap: 2px; + } + + &__label { + font-size: 11px; + font-weight: 600; + color: $gray-600; + text-transform: uppercase; + letter-spacing: 0.05em; + } + + &__amount { + font-size: 22px; + font-weight: 800; + color: $green; + line-height: 1.2; + } + + &__formula { + font-size: 11px; + color: $gray-600; + font-style: italic; + } +} + +.fdrc-payout-hint { + display: flex; + align-items: center; + gap: 8px; + margin-top: 12px; + font-size: 12px; + color: $gray-600; + font-style: italic; + + i { + color: $amber; + } +} + +// ── Rate Chart Table Card ───────────────────────────────────────────────── + +.fdrc-table-card { + background: #fff; + border: 1px solid $border; + border-radius: 12px; + overflow: hidden; + + &__header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 14px 20px; + background: $gray-50; + border-bottom: 1px solid $gray-200; + flex-wrap: wrap; + gap: 8px; + } + + &__title-row { + display: flex; + align-items: center; + gap: 10px; + + i { + color: $blue; + } + } + + &__title { + font-size: 14px; + font-weight: 700; + color: $gray-900; + } +} + +.fdrc-slab-count { + display: inline-flex; + align-items: center; + padding: 2px 8px; + background: $blue-light; + color: $blue; + border-radius: 999px; + font-size: 11px; + font-weight: 700; +} + +.fdrc-highlight-note { + font-size: 12px; + color: $green; + font-weight: 600; + display: flex; + align-items: center; + gap: 5px; +} + +.fdrc-table-wrap { + overflow-x: auto; +} + +.fdrc-table { + width: 100%; + min-width: 600px; +} + +.fdrc-th { + font-size: 11px !important; + font-weight: 700 !important; + text-transform: uppercase; + letter-spacing: 0.06em; + color: $gray-700 !important; + background: $gray-50; + padding: 10px 16px !important; +} + +.fdrc-td { + padding: 12px 16px !important; + font-size: 13px; + color: $gray-700; + vertical-align: middle; + + &--period { + display: flex; + flex-direction: column; + gap: 2px; + } + + &--na { + color: #94a3b8; + } +} + +.fdrc-period-range { + font-weight: 700; + color: $gray-900; + font-size: 13px; +} + +.fdrc-period-type { + font-size: 11px; + color: $gray-600; +} + +.fdrc-rate-badge { + display: inline-flex; + align-items: center; + padding: 3px 10px; + background: $blue-light; + color: $blue; + border-radius: 999px; + font-size: 13px; + font-weight: 700; +} + +.fdrc-incentive-btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 4px 12px; + height: 32px; + background: transparent; + border: 1.5px solid $border; + border-radius: 6px; + font-size: 12px; + font-weight: 600; + color: $gray-700; + cursor: pointer; + transition: + border-color 0.15s, + color 0.15s; + + &:hover { + border-color: $blue; + color: $blue; + } +} + +.fdrc-header-row { + background: $gray-50; +} + +.fdrc-data-row { + transition: background 0.15s; + + &:hover td { + background: $gray-50; + } + + &--matched td { + background: $green-light !important; + border-left: 3px solid $green; + } +} + +.fdrc-incentives-row { + height: 0; + + > td { + padding: 0 !important; + } +} + +.fdrc-incentives-cell { + padding: 0 !important; + overflow: hidden; +} + +.fdrc-incentives-wrap { + overflow: hidden; +} + +.fdrc-incentives-inner { + padding: 12px 20px 16px; + background: $gray-50; + border-top: 1px dashed $border; +} + +.fdrc-incentives-title { + display: flex; + align-items: center; + gap: 8px; + font-size: 12px; + font-weight: 700; + color: $gray-700; + margin-bottom: 10px; + text-transform: uppercase; + letter-spacing: 0.05em; + + i { + color: $purple; + } +} + +.fdrc-incentives-table { + width: 100%; + background: #fff; + border: 1px solid $border; + border-radius: 8px; + overflow: hidden; +} + +.fdrc-empty { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 12px; + padding: 48px 20px; + color: #94a3b8; + + i { + font-size: 32px; + } + + span { + font-size: 14px; + } +} + +// ── Stepper Actions ─────────────────────────────────────────────────────── + +.fdrc-stepper-actions { + display: flex; + align-items: center; + justify-content: center; + gap: 16px; + margin-top: 8px; + flex-wrap: wrap; +} diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-interest-rate-chart-step/fixed-deposit-account-interest-rate-chart-step.component.ts b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-interest-rate-chart-step/fixed-deposit-account-interest-rate-chart-step.component.ts index 26c8dcf60..a99b29971 100644 --- a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-interest-rate-chart-step/fixed-deposit-account-interest-rate-chart-step.component.ts +++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-interest-rate-chart-step/fixed-deposit-account-interest-rate-chart-step.component.ts @@ -6,8 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -/** Angular Imports */ -import { Component, Input, OnChanges, ViewChild } from '@angular/core'; +import { Component, Input, OnChanges, Output, EventEmitter, ViewChild } from '@angular/core'; import { MatTableDataSource, MatTable, @@ -22,15 +21,20 @@ import { MatRow } from '@angular/material/table'; import { animate, state, style, transition, trigger } from '@angular/animations'; -import { NgIf, NgSwitch, TitleCasePipe } from '@angular/common'; -import { FaIconComponent } from '@fortawesome/angular-fontawesome'; -import { MatStepperPrevious, MatStepperNext } from '@angular/material/stepper'; +import { NgIf, NgSwitch, TitleCasePipe, DecimalPipe } from '@angular/common'; +import { FormsModule } from '@angular/forms'; import { DateFormatPipe } from '../../../../pipes/date-format.pipe'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; -/** - * Fixed Deposits Account Interest Rate Chart Step - */ +export type FdPeriodUnit = 'Days' | 'Months' | 'Years'; + +export interface SlabMatchResult { + slab: any; + slabIndex: number; + appliedRate: number; + estimatedMonthlyPayout?: number; +} + @Component({ selector: 'mifosx-fixed-deposit-account-interest-rate-chart-step', templateUrl: './fixed-deposit-account-interest-rate-chart-step.component.html', @@ -44,41 +48,38 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; ], imports: [ ...STANDALONE_SHARED_IMPORTS, + FormsModule, MatTable, MatColumnDef, MatHeaderCellDef, MatHeaderCell, MatCellDef, MatCell, - FaIconComponent, NgSwitch, MatHeaderRowDef, MatHeaderRow, MatRowDef, MatRow, - MatStepperPrevious, - MatStepperNext, TitleCasePipe, - DateFormatPipe + DateFormatPipe, + DecimalPipe ] }) export class FixedDepositAccountInterestRateChartStepComponent implements OnChanges { - /** Fixed deposits account template */ @Input() fixedDepositsAccountTemplate: any; - /** Fixed deposits account and product template */ @Input() fixedDepositsAccountProductTemplate: any; - /** Interest Rate Chart Data */ - interestRateChartData: any = []; - /** Columns to be displayed in interest rate chart table. */ - chartSlabsDisplayedColumns: any[] = [ + @Output() tenureSelected = new EventEmitter<{ depositPeriod: number; depositPeriodFrequencyId: number }>(); + + interestRateChartData: any[] = []; + + chartSlabsDisplayedColumns: string[] = [ 'period', 'amountRange', 'interest', 'description', 'actions' ]; - /** Columns to be displayed in incentives sub-table. */ incentivesDisplayedColumns: string[] = [ 'entityType', 'attributeName', @@ -87,20 +88,196 @@ export class FixedDepositAccountInterestRateChartStepComponent implements OnChan 'incentiveType', 'amount' ]; - /** Additional Column to disblac incentives table */ chartSlabsIncentivesDisplayedColumns: string[] = ['incentives']; - /** Expand Chart Slab Index used in the view */ - expandChartSlabIndex: number; - /** Interest Rate charts table reference */ + expandChartSlabIndex: number | null = null; + @ViewChild('chartsTable', { static: true }) chartsTableRef: MatTable; - constructor() {} + // ── Tenure Calculator ─────────────────────────────────────────────────── + tenureValue: number | null = null; + tenurePeriodUnit: FdPeriodUnit = 'Days'; + principalAmount: number | null = null; + + matchResult: SlabMatchResult | null = null; + validationWarning: string | null = null; + + // ── FD Type labels ────────────────────────────────────────────────────── + readonly fdTypeLabels: Record = { + SINGLE: 'FD Single (Days < 1 Year)', + DOUBLE: 'FD Double (≥ 1 Year)', + SPECIAL: 'FD Special (Monthly Interest Payout)' + }; + + get fdType(): string { + return this.fixedDepositsAccountProductTemplate?.fdType || ''; + } + + get fdTypeLabel(): string { + return this.fdTypeLabels[this.fdType] || this.fdType; + } + + /** Period unit options driven by fdType */ + get allowedPeriodUnits(): FdPeriodUnit[] { + if (this.fdType === 'SINGLE') return ['Days']; + if (this.fdType === 'DOUBLE') return [ + 'Months', + 'Years' + ]; + return [ + 'Days', + 'Months', + 'Years' + ]; + } + + get isSpecial(): boolean { + return this.fdType === 'SPECIAL'; + } ngOnChanges() { if (this.fixedDepositsAccountProductTemplate) { - this.interestRateChartData = this.fixedDepositsAccountProductTemplate.accountChart.chartSlabs; - this.chartsTableRef.renderRows(); + this.interestRateChartData = this.fixedDepositsAccountProductTemplate.accountChart?.chartSlabs ?? []; + if (this.chartsTableRef) { + this.chartsTableRef.renderRows(); + } + // Reset calculator when product changes + this.resetCalculator(); + // Set default period unit based on FD type + this.tenurePeriodUnit = this.allowedPeriodUnits[0]; } } + + // Convert entered tenure to Days for slab matching + private toDays(value: number, unit: FdPeriodUnit): number { + if (unit === 'Days') return value; + if (unit === 'Months') return value * 30; + return value * 365; + } + + // Convert slab period (which is stored in its own periodType) to days + private slabPeriodToDays(period: number, periodTypeValue: string): number { + const p = (periodTypeValue || '').toLowerCase(); + if (p.includes('month')) return period * 30; + if (p.includes('year')) return period * 365; + return period; // days + } + + calculateRate() { + this.validationWarning = null; + this.matchResult = null; + + if (!this.tenureValue || this.tenureValue <= 0) { + this.validationWarning = 'Please enter a valid tenure value greater than 0.'; + return; + } + + // FD Type specific validation + const enteredDays = this.toDays(this.tenureValue, this.tenurePeriodUnit); + + if (this.fdType === 'SINGLE' && enteredDays >= 365) { + this.validationWarning = + 'FD Single allows tenure less than 1 year (max 364 days). Please enter a shorter tenure.'; + return; + } + + if (this.fdType === 'DOUBLE' && enteredDays < 365) { + this.validationWarning = + 'FD Double requires tenure of at least 1 year (minimum 12 months). Please enter a longer tenure.'; + return; + } + + const slabs: any[] = this.interestRateChartData || []; + if (!slabs.length) { + this.validationWarning = 'No interest rate slabs are defined for this product.'; + return; + } + + let matchedSlab: any = null; + let matchedIndex = -1; + + for (let i = 0; i < slabs.length; i++) { + const slab = slabs[i]; + const periodTypeValue = slab.periodType?.value || 'Days'; + const fromDays = this.slabPeriodToDays(slab.fromPeriod ?? 0, periodTypeValue); + const toDays = slab.toPeriod != null ? this.slabPeriodToDays(slab.toPeriod, periodTypeValue) : Infinity; + + if (enteredDays >= fromDays && enteredDays <= toDays) { + matchedSlab = slab; + matchedIndex = i; + break; + } + } + + if (!matchedSlab) { + this.validationWarning = + 'No applicable interest rate slab found for the entered tenure. Please check the rate chart below.'; + return; + } + + const rate = matchedSlab.annualInterestRate ?? 0; + const result: SlabMatchResult = { + slab: matchedSlab, + slabIndex: matchedIndex, + appliedRate: rate + }; + + if (this.isSpecial && this.principalAmount && this.principalAmount > 0) { + result.estimatedMonthlyPayout = (this.principalAmount * rate) / 1200; + } + + this.matchResult = result; + + // Emit selected tenure to parent for backend submission + const frequencyId = this.resolveFrequencyId(this.tenurePeriodUnit); + if (frequencyId !== null) { + this.tenureSelected.emit({ depositPeriod: this.tenureValue, depositPeriodFrequencyId: frequencyId }); + } + } + + private resolveFrequencyId(unit: FdPeriodUnit): number | null { + const options: any[] = this.fixedDepositsAccountProductTemplate?.periodFrequencyTypeOptions ?? []; + const unitLower = unit.toLowerCase(); + const match = options.find((o: any) => (o.value || '').toLowerCase().includes(unitLower)); + if (match) return match.id; + // Fineract standard fallback: Days=0, Months=2, Years=3 + const fallback: Record = { Days: 0, Months: 2, Years: 3 }; + return fallback[unit] ?? null; + } + + resetCalculator() { + this.tenureValue = null; + this.principalAmount = null; + this.matchResult = null; + this.validationWarning = null; + } + + onPeriodUnitChange() { + this.matchResult = null; + this.validationWarning = null; + } + + isMatchedSlab(index: number): boolean { + return this.matchResult?.slabIndex === index; + } + + get chartName(): string { + return this.fixedDepositsAccountProductTemplate?.accountChart?.name || ''; + } + + get chartFromDate(): any { + return this.fixedDepositsAccountProductTemplate?.accountChart?.fromDate; + } + + get chartEndDate(): any { + return this.fixedDepositsAccountProductTemplate?.accountChart?.endDate; + } + + get chartDescription(): string { + return this.fixedDepositsAccountProductTemplate?.accountChart?.description || ''; + } + + get isPrimaryGroupingByAmount(): boolean { + return !!this.fixedDepositsAccountProductTemplate?.accountChart?.isPrimaryGroupingByAmount; + } } diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.html b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.html index 3ee5a89e4..c2c88fc73 100644 --- a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.html +++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.html @@ -6,416 +6,587 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. --> -
-

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

- - -
- {{ 'labels.inputs.Product' | translate }} - {{ - fixedDepositAccountData.productId | find: fixedDepositsAccountTemplate.productOptions : 'id' : 'name' - }} -
- -
- {{ 'labels.inputs.Submitted On' | translate }} - {{ fixedDepositAccountData.submittedOnDate | dateFormat }} -
- -
- {{ 'labels.inputs.External Id' | translate }} - {{ fixedDepositAccountData.externalId }} -
- -
- {{ 'labels.inputs.Field Officer' | translate }} - {{ - (fixedDepositAccountData.fieldOfficerId - | find: fixedDepositsAccountProductTemplate.fieldOfficerOptions : 'id' : 'displayName') || 'Unassigned' - | translateKey: 'inputs' - }} -
- -

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

- - -
- {{ 'labels.inputs.Currency' | translate }} - {{ fixedDepositsAccountProductTemplate.currency.code }} -
- -
- {{ 'labels.inputs.Currency Multiple' | translate }} - {{ fixedDepositsAccountProductTemplate.currency.inMultiplesOf }} -
- -
- {{ 'labels.inputs.Decimal Places' | translate }} - {{ fixedDepositsAccountProductTemplate.currency.decimalPlaces }} -
- -

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

- - -
- {{ 'labels.inputs.Fixed Deposit Amount' | translate }}: - {{ - fixedDepositAccountData.depositAmount - | currency: fixedDepositsAccountProductTemplate.currency.code : 'symbol-narrow' : '1.2-2' - }} -
- -
- {{ 'labels.inputs.Deposit Period' | translate }}: - {{ fixedDepositAccountData.depositPeriod }} - {{ - fixedDepositAccountData.depositPeriodFrequencyId - | find: fixedDepositsAccountProductTemplate.periodFrequencyTypeOptions : 'id' : 'value' - | translateKey: 'catalogs' - }} -
- -
- {{ 'labels.inputs.Interest Compounding Period' | translate }}: - {{ - fixedDepositAccountData.interestCompoundingPeriodType - | find: fixedDepositsAccountProductTemplate.interestCompoundingPeriodTypeOptions : 'id' : 'value' - | translateKey: 'catalogs' - }} -
- -
- {{ 'labels.inputs.Interest Posting Period' | translate }}: - {{ - fixedDepositAccountData.interestPostingPeriodType - | find: fixedDepositsAccountProductTemplate.interestPostingPeriodTypeOptions : 'id' : 'value' - | translateKey: 'catalogs' - }} -
- -
- {{ 'labels.inputs.Interest Calculated using' | translate }}: - {{ - fixedDepositAccountData.interestCalculationType - | find: fixedDepositsAccountProductTemplate.interestCalculationTypeOptions : 'id' : 'value' - | translateKey: 'catalogs' - }} -
- -
- {{ 'labels.inputs.Days in Year' | translate }}: - {{ - fixedDepositAccountData.interestCalculationDaysInYearType - | find: fixedDepositsAccountProductTemplate.interestCalculationDaysInYearTypeOptions : 'id' : 'value' - | translateKey: 'catalogs' - }} -
- -

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

- - -
- {{ 'labels.inputs.Lock-in Period' | translate }}: - {{ fixedDepositAccountData.lockinPeriodFrequency }} - {{ - fixedDepositAccountData.lockinPeriodFrequencyType - | find: fixedDepositsAccountProductTemplate.lockinPeriodFrequencyTypeOptions : 'id' : 'value' - | translateKey: 'catalogs' - }} -
- -
- {{ 'labels.inputs.Minimum Deposit Term' | translate }}: - {{ fixedDepositsAccountProductTemplate.minDepositTerm }} - {{ fixedDepositsAccountProductTemplate.minDepositTermType?.value | translateKey: 'catalogs' }} -
- -
- {{ 'labels.inputs.In Multiples Of' | translate }}: - {{ fixedDepositsAccountProductTemplate.inMultiplesOfDepositTerm }} - {{ fixedDepositsAccountProductTemplate.inMultiplesOfDepositTermType?.value | translateKey: 'catalogs' }} -
- -
- {{ 'labels.inputs.Maximum Deposit Term' | translate }} - {{ fixedDepositsAccountProductTemplate.maxDepositTerm }} - {{ fixedDepositsAccountProductTemplate.maxDepositTermType?.value | translateKey: 'catalogs' }} -
- -
- {{ 'labels.inputs.Transfer Interest to Savings Account' | translate }}? - {{ fixedDepositAccountData.transferInterestToSavings | yesNo }} -
- -
- {{ 'labels.inputs.Linked Savings Account' | translate }} - {{ - fixedDepositAccountData.linkAccountId - | find: fixedDepositsAccountProductTemplate.savingsAccounts : 'id' : 'accountNo' - }} -
- -
- {{ 'labels.inputs.Maturity Instructions' | translate }} - {{ - fixedDepositAccountData.maturityInstructionId - | find: fixedDepositsAccountProductTemplate.maturityInstructionOptions : 'id' : 'value' - | translateKey: 'catalogs' - }} -
- -
- {{ 'labels.inputs.Apply Penal Interest (less)' | translate }}: - {{ fixedDepositsAccountProductTemplate.preClosurePenalApplicable | yesNo }} -
- -
- {{ 'labels.inputs.Penal Interest' | translate }} (%): - {{ - fixedDepositsAccountProductTemplate.preClosurePenalInterest + - '% (' + - fixedDepositsAccountProductTemplate.preClosurePenalInterestOnType?.id + - ')' - }} -
- -
- {{ 'labels.inputs.Withhold Tax is Applicable' | translate }}: - {{ fixedDepositAccountData.withHoldTax | yesNo }} -
- -

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

- - -
- {{ 'labels.inputs.name' | translate }}: - {{ fixedDepositsAccountProductTemplate?.accountChart.name }} -
- -
- {{ 'labels.inputs.Valid from Date' | translate }}: - {{ fixedDepositsAccountProductTemplate?.accountChart.fromDate | dateFormat }} -
- -
- {{ 'labels.inputs.End Date' | translate }}: - {{ fixedDepositsAccountProductTemplate?.accountChart.endDate | dateFormat }} -
- -
- {{ 'labels.inputs.Description' | translate }}: - {{ fixedDepositsAccountProductTemplate?.accountChart.description }} -
- -
- {{ 'labels.inputs.Primary Grouping by Amount' | translate }}: - {{ - fixedDepositsAccountProductTemplate?.accountChart.isPrimaryGroupingByAmount | yesNo - }} -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+ Submitted On + {{ fixedDepositAccountData?.submittedOnDate | dateFormat }} +
+
+ Field Officer + {{ + (fixedDepositAccountData?.fieldOfficerId + | find: fixedDepositsAccountProductTemplate?.fieldOfficerOptions : 'id' : 'displayName') || 'Unassigned' + }} +
+
+ External ID + + {{ fixedDepositAccountData?.externalId || 'Unassigned' }} + +
+ + + - - - -
{{ 'labels.inputs.Period' | translate }} - {{ chartSlab.fromPeriod }} - {{ chartSlab.toPeriod }} {{ - chartSlab.periodType.value | translateKey: 'catalogs' - }} - {{ 'labels.inputs.Amount Range' | translate }} - {{ chartSlab.amountRangeFrom | formatNumber }} - {{ chartSlab.amountRangeTo | formatNumber }} - {{ 'labels.inputs.Interest' | translate }}{{ chartSlab.annualInterestRate | formatNumber }} %{{ 'labels.inputs.Description' | translate }}{{ chartSlab.description }}{{ 'labels.inputs.Actions' | translate }} - - -
- -

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

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{ 'labels.inputs.Entity Type' | translate }} - {{ incentive.entityType.value }} - {{ 'labels.inputs.Attribute Name' | translate }} - {{ incentive.attributeName.value }} - {{ 'labels.inputs.Condition Type' | translate }} - {{ incentive.conditionType.value | titlecase }} - {{ 'labels.inputs.Attribute Value' | translate }} - {{ incentive.attributeValueDesc }} - {{ 'labels.inputs.Incentive Type' | translate }} - {{ incentive.incentiveType.value }} - {{ 'labels.inputs.Interest' | translate }} - {{ incentive.amount | formatNumber }} -
-
+
+ +
+
+ + Account Details +
+
+
+
+ Product + {{ + fixedDepositAccountData?.productId | find: fixedDepositsAccountTemplate?.productOptions : 'id' : 'name' + }}
-
+ + @if (fixedDepositsAccountProductTemplate) { +
+
+ + Product Classification + From Product +
+
+
+ @if (fixedDepositAccountData?.accountType) { +
+ Account Type + {{ fixedDepositAccountData.accountType }} +
+ } + @if (fixedDepositAccountData?.customerSegment) { +
+ Customer Segment + {{ fixedDepositAccountData.customerSegment }} +
+ } + @if (fixedDepositsAccountProductTemplate?.fdType) { +
+ FD Type + {{ getFdTypeLabel(fixedDepositsAccountProductTemplate.fdType) }} +
+ } +
+ Account No + + {{ + fixedDepositsAccountProductTemplate?.previewAccountNo || + fixedDepositsAccountTemplate?.accountNo || + 'Auto-generated' + }} + +
+ @if (fixedDepositsAccountProductTemplate?.effectiveDate) { +
+ Effective To + {{ fixedDepositsAccountProductTemplate.effectiveDate | dateFormat }} +
+ } + @if (fixedDepositsAccountProductTemplate?.endDate) { +
+ End Date + {{ fixedDepositsAccountProductTemplate.endDate | dateFormat }} +
+ } +
+ Share Linking Enabled + + {{ fixedDepositsAccountProductTemplate?.shareLinkingEnabled ? 'Yes' : 'No' }} + +
+ @if (fixedDepositsAccountProductTemplate?.introducerRequired !== undefined) { +
+ Introducer Required + + {{ fixedDepositsAccountProductTemplate?.introducerRequired ? 'Yes' : 'No' }} + +
+ } +
+
+
+ } -
-

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

+ + @if (fixedDepositAccountData?.accountStatus) { +
+
+ + Status of Account + RBI Guidelines +
+
+
+ + @switch (fixedDepositAccountData.accountStatus) { + @case ('INDIVIDUAL_MALE') { + Individual (Male) + } + @case ('INDIVIDUAL_FEMALE') { + Individual (Female) + } + @case ('SINGLE') { + Single Account + } + @case ('JOINT') { + Joint Account + } + @case ('MINOR') { + Minor Account + } + @case ('SENIOR_CITIZEN') { + Senior Citizen + } + @default { + {{ fixedDepositAccountData.accountStatus }} + } + } + +
- - - - - - + @if (fixedDepositAccountData.accountStatus === 'JOINT') { +
+
+ Mode of Operation + + @switch (fixedDepositAccountData.modeOfOperation) { + @case ('EITHER_OR_SURVIVOR') { + Either or Survivor + } + @case ('FORMER_OR_SURVIVOR') { + Former or Survivor + } + @case ('JOINTLY') { + Jointly + } + @case ('ANYONE_OR_SURVIVOR') { + Anyone or Survivor + } + @default { + {{ fixedDepositAccountData.modeOfOperation || '—' }} + } + } + +
+
+ @if (fixedDepositAccountData.jointHolders?.length) { +
+
{{ 'labels.inputs.name' | translate }} - {{ charge.name + ', ' + charge.currency.displaySymbol }} -
+ + + + + + + + + + + + @for (holder of fixedDepositAccountData.jointHolders; track holder.clientId; let i = $index) { + + + + + + + + + } + +
Sr.Holder NameCIF No.Ownership %SigningRole
{{ i + 1 }} + {{ holder.displayName || holder.clientName || '—' }} + @if (holder.isPrimary) { + Primary + } + {{ holder.cifNumber || holder.clientCif || '—' }}{{ holder.sharePercentage ?? '—' }}%{{ holder.isMandatory ? 'Mandatory' : 'Optional' }}{{ holder.isPrimary ? 'Primary Account Holder' : 'Co-Holder' }}
+
+ } + } - - {{ 'labels.inputs.Type' | translate }} - - {{ charge.chargeCalculationType.value }} - - + @if (fixedDepositAccountData.accountStatus === 'INDIVIDUAL_MALE' && fixedDepositAccountData.specialCategory) { +
+
+ Special Category + {{ fixedDepositAccountData.specialCategory }} +
+
+ } - - {{ 'labels.inputs.Amount' | translate }} - - {{ charge.amount }} - - + @if (fixedDepositAccountData.accountStatus === 'INDIVIDUAL_FEMALE' && fixedDepositAccountData.weakerSection) { +
+
+ Weaker Section + {{ fixedDepositAccountData.weakerSection }} +
+
+ } - - {{ 'labels.inputs.Date' | translate }} - + @if (fixedDepositAccountData.accountStatus === 'MINOR') { +
+
+ Guardian Name + {{ fixedDepositAccountData.guardianName || '—' }} +
+
+ Guardian CIF + {{ fixedDepositAccountData.guardianCifNumber || '—' }} +
+
+ Relationship + {{ fixedDepositAccountData.guardianRelationship || '—' }} +
+
+ } +
+
+ } + + + @if (fixedDepositAccountData?.linkedAccountId) { +
+
+ + Linked Deposit Account + {{ fixedDepositAccountData.linkedAccountType }} +
+
+
+
+ Account No + {{ fixedDepositAccountData.linkedAccountNo }} +
+
+ Account Type + {{ fixedDepositAccountData.linkedAccountType }} +
+
+ Account ID + {{ fixedDepositAccountData.linkedAccountId }} +
+
+
+
+ } + + +
+
+ + {{ 'labels.heading.Currency' | translate }} +
+
+
+
+ {{ 'labels.inputs.Currency' | translate }} + {{ currencyName }} +
+
+ {{ 'labels.inputs.Decimal Places' | translate }} + {{ decimalPlaces }} +
+
+ {{ 'labels.inputs.Currency In Multiples Of' | translate }} + {{ inMultiplesOf }} +
+ +
+ Auto Debit Facility - {{ (charge.dueDate | dateFormat) || 'Unassigned' }} - - - {{ charge.feeOnMonthDay ? ([2000].concat(charge.feeOnMonthDay) | dateFormat: 'dd MMMM') : 'Unassigned' }} + {{ fixedDepositAccountData?.autoDebitFacility | yesNo }} +
+
+
+
+ + + @if (fixedDepositsAccountProductTemplate?.dormancyAlert) { +
+
+ + Dormancy Monitoring + Monitoring: Active +
+
+
+
+
+
+ Inactive + + After {{ fixedDepositsAccountProductTemplate?.daysToInactiveFromProduct || 30 }} days of no transaction + +
+
+
+
+
+
+ Dormant + + {{ fixedDepositsAccountProductTemplate?.daysToDormancyFromProduct || 60 }} days after inactive + +
+
+
+
+
+
+ Escheat + + {{ fixedDepositsAccountProductTemplate?.daysToEscheatFromProduct || 90 }} days after dormant + +
+
+
+
+
+ } + + + @if (fixedDepositAccountData?.nominee) { +
+
+ + Nominee Details + {{ + fixedDepositAccountData.nominee.mode === 'linked' ? 'Linked Client' : 'Manual Entry' + }} +
+
+
+
+ Full Name + {{ + fixedDepositAccountData.nominee.fullName || fixedDepositAccountData.nominee.displayName + }} +
+
+ Relationship + {{ fixedDepositAccountData.nominee.relationship }} +
+
+ Nominee Share (%) + {{ fixedDepositAccountData.nominee.nomineeShare }}% +
+ @if (fixedDepositAccountData.nominee.dateOfBirth) { +
+ Date of Birth + {{ fixedDepositAccountData.nominee.dateOfBirth | dateFormat }} +
+ } + @if (fixedDepositAccountData.nominee.nationality) { +
+ Nationality + {{ fixedDepositAccountData.nominee.nationality }} +
+ } + @if (fixedDepositAccountData.nominee.mobileNo) { +
+ Mobile Number + {{ fixedDepositAccountData.nominee.mobileNo }} +
+ } +
+
+
+ } + + +
+
+ + {{ 'labels.heading.Interest Rate Chart' | translate }} + @if (selectedTenure) { + + Selected Tenure: {{ selectedTenureLabel }} + + } +
+
+ + @if (selectedTenure && matchedSlabIndex >= 0) { +
+
+ +
+ Your FD Tenure + {{ selectedTenureLabel }} +
+
+
+
+ Applicable Interest Rate + {{ interestRateChartData[matchedSlabIndex]?.annualInterestRate | formatNumber }}% +
+
+ + Slab: {{ interestRateChartData[matchedSlabIndex]?.fromPeriod }}–{{ + interestRateChartData[matchedSlabIndex]?.toPeriod ?? '∞' + }} + {{ interestRateChartData[matchedSlabIndex]?.periodType?.value }} +
+
+ } + +
+ @if (fixedDepositsAccountProductTemplate?.accountChart?.name) { +
+ Name + {{ fixedDepositsAccountProductTemplate.accountChart.name }} +
+ } +
+ Valid From + {{ + fixedDepositsAccountProductTemplate?.accountChart?.fromDate | dateFormat + }} +
+
+ Primary Grouping by Amount - {{ 'labels.inputs.N/A' | translate }} + {{ fixedDepositsAccountProductTemplate?.accountChart?.isPrimaryGroupingByAmount | yesNo }} - - +
+
- - {{ 'labels.inputs.Repayments Every' | translate }} - - {{ charge.feeInterval || 'Not Provided' }} - - + + + + + - - - - + + + + - - -
{{ 'labels.inputs.Period' | translate }} + {{ chartSlab.fromPeriod }} - {{ chartSlab.toPeriod }} {{ + chartSlab.periodType?.value | translateKey: 'catalogs' + }} + {{ 'labels.inputs.Collected On' | translate }} - {{ charge.chargeTimeType.value }} - {{ 'labels.inputs.Amount Range' | translate }} + {{ chartSlab.amountRangeFrom | formatNumber }} - {{ chartSlab.amountRangeTo | formatNumber }} +
+ + {{ 'labels.inputs.Interest' | translate }} + {{ chartSlab.annualInterestRate | formatNumber }} % + + + + {{ 'labels.inputs.Description' | translate }} + {{ chartSlab.description }} + + + + {{ 'labels.inputs.Actions' | translate }} + + + + + + + +
+ +

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

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ 'labels.inputs.Entity Type' | translate }}{{ incentive.entityType?.value }}{{ 'labels.inputs.Attribute Name' | translate }}{{ incentive.attributeName?.value }}{{ 'labels.inputs.Condition Type' | translate }}{{ incentive.conditionType?.value | titlecase }}{{ 'labels.inputs.Attribute Value' | translate }}{{ incentive.attributeValueDesc }}{{ 'labels.inputs.Incentive Type' | translate }}{{ incentive.incentiveType?.value }}{{ 'labels.inputs.Interest' | translate }}{{ incentive.amount | formatNumber }}
+
+
+ +
+ + + + + + @if (selectedTenure && matchedSlabIndex >= 0) { +
+ + Highlighted row is the slab applicable for your selected tenure of {{ selectedTenureLabel }} +
+ } +
diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.scss b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.scss index 4c9f978a6..c7c88cafb 100644 --- a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.scss +++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.scss @@ -6,35 +6,277 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -table { - width: 100%; +// ── Wrapper ────────────────────────────────────────────────────────────────── +.preview-wrapper { + display: flex; + flex-direction: column; + gap: 1.25rem; + padding: 0.5rem 0; } -.mat-elevation-z1 { - margin: 1em 0 1.5em; +// ── Section card ───────────────────────────────────────────────────────────── +.preview-section { + border: 1px solid #e2e8f0; + border-radius: 10px; + overflow: hidden; + background: #fff; + box-shadow: 0 1px 4px rgb(0 0 0 / 5%); + + &__header { + display: flex; + align-items: center; + gap: 8px; + background: #f8fafc; + border-bottom: 1px solid #e2e8f0; + padding: 0.6rem 1rem; + } + + &__icon { + font-size: 13px; + color: #0ea5e9; + width: 16px; + text-align: center; + } + + &__title { + flex: 1; + font-size: 0.82rem; + font-weight: 700; + color: #1e293b; + text-transform: uppercase; + letter-spacing: 0.06em; + } + + &__badge { + display: inline-flex; + align-items: center; + padding: 2px 10px; + border-radius: 999px; + font-size: 10px; + font-weight: 700; + background: #dbeafe; + color: #1d4ed8; + border: 1px solid #bfdbfe; + text-transform: uppercase; + + &--active { + background: #dcfce7 !important; + color: #15803d !important; + border-color: #bbf7d0 !important; + } + } } -h2, -h3, -h4 { - margin: 0; +// ── Body ───────────────────────────────────────────────────────────────────── +.preview-body { + padding: 1rem 1.25rem; +} + +// ── Subsection (inside Settings) ───────────────────────────────────────────── +.preview-subsection { + margin-bottom: 1rem; + + &__title { + font-size: 0.82rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: #666; + margin-bottom: 0.6rem; + } +} + +.preview-divider { + margin: 0.75rem 0 1rem; +} + +// ── Grid ───────────────────────────────────────────────────────────────────── +.preview-grid { + display: grid; + gap: 0.85rem 1.5rem; + + &--three { + grid-template-columns: repeat(3, 1fr); + + @media (width <= 768px) { + grid-template-columns: repeat(2, 1fr); + } + + @media (width <= 480px) { + grid-template-columns: 1fr; + } + } +} + +// ── Field ───────────────────────────────────────────────────────────────────── +.preview-field { + display: flex; + flex-direction: column; + gap: 0.15rem; +} + +.preview-label { + font-size: 0.68rem; + color: #94a3b8; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.preview-value { + font-size: 0.9rem; + color: #1e293b; font-weight: 500; } -span { - margin: 0.5em 0; +// ── Yes/No badge ───────────────────────────────────────────────────────────── +.preview-badge { + display: inline-block; + padding: 0.15rem 0.6rem; + border-radius: 12px; + font-size: 0.75rem; + font-weight: 600; + background: #fef2f2; + color: #dc2626; + width: fit-content; + + &--yes { + background: #f0fdf4; + color: #15803d; + } + + &--warn { + background: #fef3c7; + color: #92400e; + } } -mat-divider { - margin: 0 0 0.5em; +// ── Section badge variant for tenure ───────────────────────────────────────── +.preview-section__badge--tenure { + display: inline-flex; + align-items: center; + gap: 5px; + padding: 3px 10px; + border-radius: 999px; + font-size: 11px; + font-weight: 700; + background: #dcfce7; + color: #16a34a; + margin-left: auto; } -.margin-t { - margin-top: 1em; +// ── Tenure + Rate banner ────────────────────────────────────────────────────── +.preview-tenure-banner { + display: flex; + align-items: center; + gap: 20px; + padding: 14px 18px; + background: #f0fdf4; + border: 1.5px solid #86efac; + border-radius: 10px; + margin-bottom: 16px; + flex-wrap: wrap; + + &__left { + display: flex; + align-items: center; + gap: 10px; + + > i { + font-size: 20px; + color: #16a34a; + } + } + + &__label { + display: block; + font-size: 10px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.06em; + color: #4b5563; + } + + &__value { + display: block; + font-size: 22px; + font-weight: 800; + color: #0f172a; + line-height: 1.2; + } + + &__divider { + width: 1px; + height: 40px; + background: #86efac; + } + + &__right { + display: flex; + flex-direction: column; + gap: 2px; + } + + &__rate-label { + font-size: 10px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.06em; + color: #4b5563; + } + + &__rate { + font-size: 26px; + font-weight: 800; + color: #16a34a; + line-height: 1.2; + } + + &__slab { + display: flex; + align-items: center; + gap: 6px; + font-size: 12px; + color: #374151; + margin-left: auto; + + i { + color: #16a34a; + font-size: 11px; + } + } } -.margin-b { - margin-bottom: 0.25em; +// ── Matched slab row highlight ──────────────────────────────────────────────── +.preview-slab-row--matched td { + background: #dcfce7 !important; + border-left: 3px solid #16a34a; +} + +// ── Legend below table ──────────────────────────────────────────────────────── +.preview-slab-legend { + display: flex; + align-items: center; + gap: 8px; + margin-top: 8px; + font-size: 12px; + color: #4b5563; + + &__dot { + display: inline-block; + width: 12px; + height: 12px; + border-radius: 2px; + background: #dcfce7; + border: 2px solid #16a34a; + flex-shrink: 0; + } +} + +// ── Tables ─────────────────────────────────────────────────────────────────── +.preview-table { + width: 100%; + margin-top: 1rem; } .irc-table { @@ -56,3 +298,222 @@ mat-divider { } } } + +.margin-t { + margin-top: 1em; +} + +// ── Preview Tenure Result Card ───────────────────────────────────────────────── +.preview-tenure-result { + border: 1px solid #e0f2fe; + border-radius: 10px; + overflow: hidden; + + &__row { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.65rem 1rem; + border-bottom: 1px solid #f0f9ff; + + &:last-child { + border-bottom: none; + } + + &--highlight { + background: #f0f9ff; + } + } + + &__rate { + font-size: 1.5rem; + font-weight: 800; + color: #0369a1; + } +} + +// ── Misc ────────────────────────────────────────────────────────────────────── +.preview-muted { + color: #94a3b8; + font-style: italic; +} + +.preview-mono { + font-family: monospace; + font-size: 0.88rem; + color: #334155; +} + +// ── Status of Account (SOA) ─────────────────────────────────────────────────── +.preview-soa-status-row { + display: flex; + align-items: center; + gap: 10px; +} + +.preview-soa-tile { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 8px 20px; + border-radius: 999px; + font-size: 14px; + font-weight: 700; + background: #eff6ff; + color: #1d4ed8; + border: 2px solid #bfdbfe; + + i { + font-size: 16px; + } +} + +.preview-soa-table-wrap { + margin-top: 14px; + overflow-x: auto; + border-radius: 8px; + border: 1px solid #e2e8f0; +} + +.preview-soa-tbl { + width: 100%; + border-collapse: collapse; + font-size: 13px; +} + +.preview-soa-th { + background: #f8fafc; + padding: 8px 12px; + text-align: left; + font-weight: 600; + color: #475569; + border-bottom: 1px solid #e2e8f0; + white-space: nowrap; +} + +.preview-soa-td { + padding: 10px 12px; + border-bottom: 1px solid #f1f5f9; + color: #0f172a; + vertical-align: middle; +} + +.preview-soa-tr--primary { + background: #f0f9ff; + + .preview-soa-td { + font-weight: 600; + } +} + +.preview-soa-primary-tag { + display: inline-block; + margin-left: 6px; + padding: 1px 8px; + border-radius: 999px; + font-size: 10px; + font-weight: 700; + background: #dbeafe; + color: #1d4ed8; + border: 1px solid #bfdbfe; + vertical-align: middle; +} + +// ── Dormancy Monitoring Timeline ────────────────────────────────────────────── +.preview-dormancy-timeline { + display: flex; + align-items: flex-start; + gap: 12px; + margin: 1.25rem 0; + overflow-x: auto; + padding-bottom: 8px; +} + +.preview-dorm-step { + display: flex; + flex-direction: column; + align-items: center; + flex: 0 0 140px; + gap: 10px; + + &__dot { + width: 32px; + height: 32px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + border: 3px solid #e2e8f0; + background: #f8fafc; + position: relative; + box-shadow: 0 0 0 4px #fff; + z-index: 2; + + &--inactive { + border-color: #f59e0b; + background: #fffbeb; + + &::after { + content: ''; + width: 10px; + height: 10px; + border-radius: 50%; + background: #f59e0b; + } + } + + &--dormant { + border-color: #ef4444; + background: #fef2f2; + + &::after { + content: ''; + width: 10px; + height: 10px; + border-radius: 50%; + background: #ef4444; + } + } + + &--escheat { + border-color: #7c3aed; + background: #f5f3ff; + + &::after { + content: ''; + width: 10px; + height: 10px; + border-radius: 50%; + background: #7c3aed; + } + } + } + + &__body { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + gap: 2px; + } + + &__label { + font-size: 12px; + font-weight: 700; + color: #1e293b; + } + + &__days { + font-size: 10.5px; + color: #64748b; + line-height: 1.3; + } +} + +.preview-dorm-connector { + flex: 0 0 40px; + height: 3px; + background: linear-gradient(90deg, #e2e8f0, #cbd5e1); + margin-top: 16px; + border-radius: 2px; +} diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.ts b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.ts index e4b3d2c86..b9f463659 100644 --- a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.ts +++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.ts @@ -76,6 +76,8 @@ export class FixedDepositAccountPreviewStepComponent implements OnChanges { @Input() fixedDepositsAccountProductTemplate: any; /** Fixed Deposits Account Data */ @Input() fixedDepositAccountData: any; + /** Tenure selected from the interest rate chart step */ + @Input() selectedTenure: { depositPeriod: number; depositPeriodFrequencyId: number } | null = null; /** Charges Displayed Columns */ chargesDisplayedColumns: string[] = [ @@ -110,6 +112,28 @@ export class FixedDepositAccountPreviewStepComponent implements OnChanges { /** Expand Chart Slab Index used in the view */ expandChartSlabIndex: number; + readonly fdTypeLabels: Record = { + SINGLE: 'FD Single (Days < 1 Year)', + DOUBLE: 'FD Double (≥ 1 Year)', + SPECIAL: 'FD Special (Monthly Interest Payout)' + }; + + getFdTypeLabel(fdType: string): string { + return this.fdTypeLabels[fdType] || fdType; + } + + get currencyName(): string { + return this.fixedDepositsAccountProductTemplate?.currency?.name || '—'; + } + + get decimalPlaces(): string | number { + return this.fixedDepositsAccountProductTemplate?.currency?.decimalPlaces ?? '—'; + } + + get inMultiplesOf(): string | number { + return this.fixedDepositsAccountProductTemplate?.currency?.inMultiplesOf ?? '—'; + } + /** Form submission event */ @Output() submitEvent = new EventEmitter(); @@ -117,7 +141,34 @@ export class FixedDepositAccountPreviewStepComponent implements OnChanges { ngOnChanges() { if (this.fixedDepositsAccountProductTemplate) { - this.interestRateChartData = this.fixedDepositsAccountProductTemplate.accountChart.chartSlabs; + this.interestRateChartData = this.fixedDepositsAccountProductTemplate.accountChart?.chartSlabs ?? []; } } + + get matchedSlabIndex(): number { + if (!this.selectedTenure || !this.interestRateChartData?.length) return -1; + const { depositPeriod, depositPeriodFrequencyId } = this.selectedTenure; + // Convert selected tenure to days for comparison + const toDays = (v: number, id: number) => { + if (id === 2) return v * 30; // Months + if (id === 3) return v * 365; // Years + return v; // Days (id=0) + }; + const enteredDays = toDays(depositPeriod, depositPeriodFrequencyId); + + return this.interestRateChartData.findIndex((slab: any) => { + const periodTypeValue = (slab.periodType?.value || '').toLowerCase(); + const slabId = periodTypeValue.includes('month') ? 2 : periodTypeValue.includes('year') ? 3 : 0; + const fromDays = toDays(slab.fromPeriod ?? 0, slabId); + const toDaysVal = slab.toPeriod != null ? toDays(slab.toPeriod, slabId) : Infinity; + return enteredDays >= fromDays && enteredDays <= toDaysVal; + }); + } + + get selectedTenureLabel(): string { + if (!this.selectedTenure) return ''; + const { depositPeriod, depositPeriodFrequencyId } = this.selectedTenure; + const unitMap: Record = { 0: 'Days', 2: 'Months', 3: 'Years' }; + return `${depositPeriod} ${unitMap[depositPeriodFrequencyId] ?? ''}`; + } } diff --git a/src/environments/.env.ts b/src/environments/.env.ts index ab249d204..c863a04fb 100644 --- a/src/environments/.env.ts +++ b/src/environments/.env.ts @@ -5,13 +5,12 @@ * 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': '260504', - 'hash': 'e9d2f4b5' + 'version': '260507', + 'hash': 'b0cffd38' }, 'allow_switching_backend_instance': true };