update code account saving,current,RD

This commit is contained in:
Nitesh Yadav
2026-04-24 16:24:28 +05:30
parent fc1c5126fc
commit fbaec18552
49 changed files with 4221 additions and 2260 deletions
@@ -34,7 +34,8 @@
<fa-icon icon="eye" size="sm"></fa-icon>
</ng-template>
<mat-step [stepControl]="recurringDepositAccountDetailsForm">
<!-- Step 1: Details -->
<mat-step>
<ng-template matStepLabel>{{ 'labels.inputs.DETAILS' | translate }}</ng-template>
<mifosx-recurring-deposits-account-details-step
@@ -44,49 +45,52 @@
</mifosx-recurring-deposits-account-details-step>
</mat-step>
<mat-step [stepControl]="recurringDepositAccountTermsForm">
<ng-template matStepLabel>{{ 'labels.inputs.TERMS' | translate }}</ng-template>
<!-- Step 2: Currency -->
@if (recurringDepositsAccountProductTemplate) {
<mat-step>
<ng-template matStepLabel>Currency</ng-template>
<mifosx-recurring-deposits-account-terms-step
[recurringDepositsAccountTemplate]="recurringDepositsAccountTemplate"
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
>
</mifosx-recurring-deposits-account-terms-step>
</mat-step>
<mifosx-recurring-deposits-account-currency-step
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
>
</mifosx-recurring-deposits-account-currency-step>
<mat-step [stepControl]="recurringDepositAccountSettingsForm">
<ng-template matStepLabel>{{ 'labels.inputs.SETTINGS' | translate }}</ng-template>
<div class="layout-row align-center gap-2percent margin-t responsive-column">
<button mat-raised-button matStepperPrevious>
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Previous' | translate }}
</button>
<button mat-raised-button matStepperNext>
{{ 'labels.buttons.Next' | translate }}
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
</button>
</div>
</mat-step>
<mifosx-recurring-deposits-account-settings-step
[recurringDepositsAccountTemplate]="recurringDepositsAccountTemplate"
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
>
</mifosx-recurring-deposits-account-settings-step>
</mat-step>
<!-- Step 3: Interest Rate Chart -->
<mat-step>
<ng-template matStepLabel>{{ 'labels.inputs.INTEREST RATE CHART' | translate }}</ng-template>
<mat-step>
<ng-template matStepLabel>{{ 'labels.inputs.INTEREST RATE CHART' | translate }}</ng-template>
<mifosx-recurring-deposits-account-interest-rate-chart-step
[recurringDepositsAccountTemplate]="recurringDepositsAccountTemplate"
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
[rdInterestRates]="rdInterestRates"
>
</mifosx-recurring-deposits-account-interest-rate-chart-step>
<mifosx-recurring-deposits-account-interest-rate-chart-step
[recurringDepositsAccountTemplate]="recurringDepositsAccountTemplate"
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
[rdInterestRates]="rdInterestRates"
>
</mifosx-recurring-deposits-account-interest-rate-chart-step>
</mat-step>
<div class="layout-row align-center gap-2percent margin-t responsive-column">
<button mat-raised-button matStepperPrevious>
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Previous' | translate }}
</button>
<button mat-raised-button matStepperNext>
{{ 'labels.buttons.Next' | translate }}
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
</button>
</div>
</mat-step>
<mat-step>
<ng-template matStepLabel>{{ 'labels.inputs.CHARGES' | translate }}</ng-template>
<mifosx-recurring-deposits-account-charges-step
[recurringDepositsAccountTemplate]="recurringDepositsAccountTemplate"
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
[recurringDepositAccountFormValid]="recurringDepositAccountFormValid"
>
</mifosx-recurring-deposits-account-charges-step>
</mat-step>
@if (recurringDepositAccountFormValid) {
<!-- Step 4: Preview -->
<mat-step state="preview" completed>
<ng-template matStepLabel>{{ 'labels.inputs.PREVIEW' | translate }}</ng-template>
<mifosx-recurring-deposits-account-preview-step
@@ -5,3 +5,15 @@
* 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/.
*/
:host ::ng-deep mat-stepper {
background: transparent;
.mat-step-content {
overflow: visible !important;
}
}
:host ::ng-deep mat-step-content {
overflow: visible !important;
}
@@ -17,12 +17,17 @@ import { ProductsService } from 'app/products/products.service';
/** Step Components */
import { RecurringDepositsAccountDetailsStepComponent } from '../recurring-deposits-account-stepper/recurring-deposits-account-details-step/recurring-deposits-account-details-step.component';
import { RecurringDepositsAccountTermsStepComponent } from '../recurring-deposits-account-stepper/recurring-deposits-account-terms-step/recurring-deposits-account-terms-step.component';
import { RecurringDepositsAccountSettingsStepComponent } from '../recurring-deposits-account-stepper/recurring-deposits-account-settings-step/recurring-deposits-account-settings-step.component';
import { RecurringDepositsAccountChargesStepComponent } from '../recurring-deposits-account-stepper/recurring-deposits-account-charges-step/recurring-deposits-account-charges-step.component';
import { Dates } from 'app/core/utils/dates';
import { MatStepper, MatStepperIcon, MatStep, MatStepLabel } from '@angular/material/stepper';
import {
MatStepper,
MatStepperIcon,
MatStep,
MatStepLabel,
MatStepperNext,
MatStepperPrevious
} from '@angular/material/stepper';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { RecurringDepositsAccountCurrencyStepComponent } from '../recurring-deposits-account-stepper/recurring-deposits-account-currency-step/recurring-deposits-account-currency-step.component';
import { RecurringDepositsAccountInterestRateChartStepComponent } from '../recurring-deposits-account-stepper/recurring-deposits-account-interest-rate-chart-step/recurring-deposits-account-interest-rate-chart-step.component';
import { RecurringDepositsAccountPreviewStepComponent } from '../recurring-deposits-account-stepper/recurring-deposits-account-preview-step/recurring-deposits-account-preview-step.component';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
@@ -38,14 +43,14 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
...STANDALONE_SHARED_IMPORTS,
MatStepper,
MatStepperIcon,
MatStepperNext,
MatStepperPrevious,
FaIconComponent,
MatStep,
MatStepLabel,
RecurringDepositsAccountDetailsStepComponent,
RecurringDepositsAccountTermsStepComponent,
RecurringDepositsAccountSettingsStepComponent,
RecurringDepositsAccountCurrencyStepComponent,
RecurringDepositsAccountInterestRateChartStepComponent,
RecurringDepositsAccountChargesStepComponent,
RecurringDepositsAccountPreviewStepComponent
]
})
@@ -57,15 +62,11 @@ export class CreateRecurringDepositsAccountComponent {
private settingsService = inject(SettingsService);
private productsService = inject(ProductsService);
/** Imports all the step component */
@ViewChild(RecurringDepositsAccountDetailsStepComponent, { static: true })
recurringDepositsAccountDetailsStep: RecurringDepositsAccountDetailsStepComponent;
@ViewChild(RecurringDepositsAccountTermsStepComponent, { static: true })
recurringDepositAccountTermsStep: RecurringDepositsAccountTermsStepComponent;
@ViewChild(RecurringDepositsAccountSettingsStepComponent, { static: true })
recurringDepositAccountSettingsStep: RecurringDepositsAccountSettingsStepComponent;
@ViewChild(RecurringDepositsAccountChargesStepComponent, { static: true })
recurringDepositAccountChargesStep: RecurringDepositsAccountChargesStepComponent;
@ViewChild(RecurringDepositsAccountCurrencyStepComponent, { static: true })
recurringDepositsAccountCurrencyStep: RecurringDepositsAccountCurrencyStepComponent;
/** Recurring Deposits Account Template */
recurringDepositsAccountTemplate: any;
@@ -83,7 +84,6 @@ export class CreateRecurringDepositsAccountComponent {
/**
* Sets recurring deposits account product template.
* Also fetches the active CBS base interest rate chart.
* @param {any} $event API response (includes productId)
*/
setTemplate($event: any) {
this.recurringDepositsAccountProductTemplate = $event;
@@ -102,42 +102,16 @@ export class CreateRecurringDepositsAccountComponent {
return this.recurringDepositsAccountDetailsStep.recurringDepositAccountDetailsForm;
}
/** Get Recurring Deposit Account Terms Form Data */
get recurringDepositAccountTermsForm() {
return this.recurringDepositAccountTermsStep.recurringDepositAccountTermsForm;
}
/** Get Recurring Deposit Account Settings Form Data */
get recurringDepositAccountSettingsForm() {
return this.recurringDepositAccountSettingsStep.recurringDepositAccountSettingsForm;
}
/** Checks wheter all the forms in different steps are valid or not */
get recurringDepositAccountFormValid() {
return (
this.recurringDepositAccountDetailsForm.valid &&
this.recurringDepositAccountTermsForm.valid &&
this.recurringDepositAccountSettingsForm.valid
);
}
/** Retrieves Data of all the forms */
/** Retrieves Data of all forms */
get recurringDepositAccountData() {
return {
...this.recurringDepositsAccountDetailsStep.recurringDepositAccountDetails,
...this.recurringDepositAccountTermsStep.recurringDepositAccountTerms,
...this.recurringDepositAccountSettingsStep.recurringDepositAccountSettings,
...this.recurringDepositAccountChargesStep.recurringDepositAccountCharges
...this.recurringDepositsAccountDetailsStep.recurringDepositAccountDetails
};
}
/** Retrieves Data of all forms except Currency to submit the data */
get recurringDepositAccount() {
return {
...this.recurringDepositsAccountDetailsStep.recurringDepositAccountDetails,
...this.recurringDepositAccountTermsStep.recurringDepositAccountTerms,
...this.recurringDepositAccountSettingsStep.recurringDepositAccountSettings,
...this.recurringDepositAccountChargesStep.recurringDepositAccountCharges
...this.recurringDepositsAccountDetailsStep.recurringDepositAccountDetails
};
}
@@ -150,17 +124,10 @@ export class CreateRecurringDepositsAccountComponent {
const monthDayFormat = 'dd MMMM';
const recurringDepositAccount = {
...this.recurringDepositAccount,
...this.recurringDepositsAccountCurrencyStep.currencyStepData,
clientId: this.recurringDepositsAccountTemplate.clientId,
charges: this.recurringDepositAccount.charges.map((charge: any) => ({
chargeId: charge.id,
amount: charge.amount,
dueDate: charge.dueDate && this.dateUtils.formatDate(charge.dueDate, dateFormat),
feeOnMonthDay: charge.feeOnMonthDay,
feeInterval: charge.feeInterval
})),
isCalendarInherited: this.recurringDepositAccount.recurringDepositAccount
? this.recurringDepositAccount.recurringDepositAccount
: false,
charges: [],
isCalendarInherited: false,
submittedOnDate: this.dateUtils.formatDate(this.recurringDepositAccount.submittedOnDate, dateFormat),
expectedFirstDepositOnDate: this.dateUtils.formatDate(
this.recurringDepositAccount.expectedFirstDepositOnDate,
@@ -0,0 +1,82 @@
<!--
Copyright since 2025 Mifos Initiative
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<div class="racc-section">
<div class="racc-section__header">
<i class="fas fa-coins"></i>
<span>Currency &amp; Account Status Configuration</span>
<span class="racc-badge racc-badge--from">From Product</span>
</div>
<div class="racc-body">
<div class="racc-grid">
<mat-form-field appearance="outline" class="racc-field">
<mat-label>Currency</mat-label>
<input matInput [value]="recurringDepositsAccountProductTemplate?.currency?.name || ''" readonly />
<mat-hint>Inherited from selected product</mat-hint>
</mat-form-field>
<mat-form-field appearance="outline" class="racc-field">
<mat-label>Decimal Places</mat-label>
<input matInput [value]="recurringDepositsAccountProductTemplate?.currency?.decimalPlaces ?? ''" readonly />
<mat-hint>Inherited from selected product</mat-hint>
</mat-form-field>
</div>
<div class="racc-alert racc-alert--editable">
<div class="racc-alert__content">
<h4>Balance Swap Option</h4>
<p>Enable automated transfer of excess funds to linked deposits or vice-versa to maintain liquidity.</p>
</div>
<mat-slide-toggle [(ngModel)]="balanceSwapOption" color="primary"></mat-slide-toggle>
</div>
<div class="racc-alert racc-alert--editable">
<div class="racc-alert__content">
<h4>MSME</h4>
<p>Micro, Small and Medium Enterprises — mark this account under MSME category.</p>
</div>
<mat-slide-toggle [(ngModel)]="msme" color="primary"></mat-slide-toggle>
</div>
<div class="racc-alert racc-alert--editable">
<div class="racc-alert__content">
<h4>Auto Debit Facility</h4>
<p>Enable automatic deduction of funds for recurring payments, utility bills, or EMI obligations.</p>
</div>
<mat-slide-toggle [(ngModel)]="autoDebitFacility" color="primary"></mat-slide-toggle>
</div>
<div class="racc-alert">
<div class="racc-alert__content">
<h4>Dormancy Alert</h4>
<p>Enable automated notifications when an account becomes inactive based on product rules.</p>
</div>
<span class="racc-yn" [class.racc-yn--yes]="recurringDepositsAccountProductTemplate?.dormancyAlert">
{{ recurringDepositsAccountProductTemplate?.dormancyAlert ? 'Yes' : 'No' }}
</span>
</div>
<mifosx-dormancy-alert-panel
[productTemplate]="recurringDepositsAccountProductTemplate"
></mifosx-dormancy-alert-panel>
<div class="racc-alert">
<div class="racc-alert__content">
<h4>Nomination Facility</h4>
<p>Allow account holders to designate individuals to receive funds in the event of account holder's death.</p>
</div>
<span class="racc-yn" [class.racc-yn--yes]="recurringDepositsAccountProductTemplate?.nominationFacility">
{{ recurringDepositsAccountProductTemplate?.nominationFacility ? 'Yes' : 'No' }}
</span>
</div>
@if (recurringDepositsAccountProductTemplate?.nominationFacility) {
<mifosx-nominee-form (nomineeChange)="onNomineeChange($event)"></mifosx-nominee-form>
}
</div>
</div>
@@ -0,0 +1,141 @@
/**
* 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/.
*/
$accent: #0ea5e9;
$border: #e2e8f0;
$gray-50: #f8f9fa;
$gray-200: #e8eaed;
$gray-900: #202124;
.racc-section {
border: 1px solid $border;
border-radius: 10px;
overflow: hidden;
background: #fff;
&__header {
display: flex;
align-items: center;
gap: 10px;
padding: 12px 24px;
background: $gray-50;
border-bottom: 1px solid $gray-200;
i {
font-size: 14px;
color: $accent;
}
span {
margin: 0;
font-size: 14px;
font-weight: 600;
color: $gray-900;
}
}
}
.racc-body {
background: #fff;
}
.racc-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 4px 16px;
padding: 16px 24px;
@media (width <= 600px) {
grid-template-columns: 1fr;
}
}
.racc-field {
width: 100%;
}
.racc-alert {
display: flex;
align-items: center;
justify-content: space-between;
gap: 24px;
padding: 18px 24px;
border-top: 1px solid $border;
background: #f8fafc;
@media (width <= 760px) {
flex-direction: column;
align-items: flex-start;
}
&__content {
h4 {
margin: 0 0 6px;
font-size: 14px;
font-weight: 600;
color: $gray-900;
}
p {
margin: 0;
font-size: 12px;
line-height: 1.5;
color: #64748b;
max-width: 540px;
}
}
}
.racc-yn {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 48px;
padding: 4px 14px;
border-radius: 999px;
font-size: 12px;
font-weight: 700;
background: #fee2e2;
color: #b91c1c;
border: 1px solid #fecaca;
&--yes {
background: #dcfce7;
color: #15803d;
border-color: #bbf7d0;
}
}
.racc-badge {
display: inline-flex;
align-items: center;
padding: 2px 10px;
border-radius: 999px;
font-size: 11px;
font-weight: 600;
&--from {
background: #eff6ff;
color: #1d4ed8;
border: 1px solid #bfdbfe;
}
}
.racc-alert--editable {
border-color: #bae6fd;
background: #f0f9ff;
&:hover {
border-color: #7dd3fc;
box-shadow: 0 2px 8px rgb(2 132 199 / 10%);
}
h4 {
color: #0369a1;
}
}
@@ -0,0 +1,60 @@
/**
* 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-recurring-deposits-account-currency-step',
templateUrl: './recurring-deposits-account-currency-step.component.html',
styleUrls: ['./recurring-deposits-account-currency-step.component.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS,
NomineeFormComponent,
DormancyAlertPanelComponent,
MatSlideToggle,
FormsModule
]
})
export class RecurringDepositsAccountCurrencyStepComponent implements OnChanges {
@Input() recurringDepositsAccountProductTemplate: any;
nomineeData: any = null;
// Editable feature toggles (pre-filled from product, overridable by user)
balanceSwapOption = false;
msme = false;
autoDebitFacility = false;
ngOnChanges(changes: SimpleChanges): void {
if (changes['recurringDepositsAccountProductTemplate']?.currentValue) {
const pt = changes['recurringDepositsAccountProductTemplate'].currentValue;
this.balanceSwapOption = !!pt.balanceSwapOption;
this.msme = !!pt.msme;
this.autoDebitFacility = !!pt.autoDebitFacility;
}
}
onNomineeChange(event: any): void {
this.nomineeData = event;
}
/** Called by parent to collect currency step values */
get currencyStepData() {
return {
balanceSwapOption: this.balanceSwapOption,
msme: this.msme,
autoDebitFacility: this.autoDebitFacility,
...(this.nomineeData ? { nominee: this.nomineeData } : {})
};
}
}
@@ -7,7 +7,7 @@
-->
<form [formGroup]="recurringDepositAccountDetailsForm">
<!-- ===================== Account Details ===================== -->
<!-- ══ Account Details ══════════════════════════════════════════ -->
<div class="rad-section">
<div class="rad-section__header">
<i class="fas fa-info-circle"></i>
@@ -64,7 +64,7 @@
</div>
@if (isProductSelected && productTemplate) {
<!-- ===================== Product Classification ===================== -->
<!-- ══ Product Classification (read-only) ══════════════════════ -->
<div class="rad-section rad-section--mt">
<div class="rad-section__header">
<i class="fas fa-tags"></i>
@@ -115,101 +115,8 @@
</div>
</div>
<!-- ===================== Currency & Account Status Configuration ===================== -->
<div class="rad-section rad-section--mt">
<div class="rad-section__header">
<i class="fas fa-coins"></i>
<h3>Currency &amp; Account Status Configuration</h3>
<span class="rad-badge">From Product</span>
</div>
<div class="rad-body">
@if (productTemplate.currency) {
<div class="rad-grid">
<mat-form-field appearance="outline" class="rad-field">
<mat-label>Currency</mat-label>
<input matInput [value]="productTemplate.currency.name" readonly />
<mat-hint>Default: Indian Rupee - auto-set for domestic products</mat-hint>
</mat-form-field>
@if (
productTemplate.currency.decimalPlaces !== undefined && productTemplate.currency.decimalPlaces !== null
) {
<mat-form-field appearance="outline" class="rad-field">
<mat-label>Decimal Places</mat-label>
<input matInput [value]="productTemplate.currency.decimalPlaces" readonly />
</mat-form-field>
}
</div>
}
<div class="rad-alert">
<div class="rad-alert__content">
<h4>Dormancy Alert</h4>
<p>Enable automated notifications when an account becomes inactive based on product rules.</p>
</div>
<div class="rad-alert__choices">
<span class="rad-yn" [class.rad-yn--active]="productTemplate.dormancyAlert === true">yes</span>
<span class="rad-yn" [class.rad-yn--active]="productTemplate.dormancyAlert === false">no</span>
</div>
</div>
<div class="rad-alert">
<div class="rad-alert__content">
<h4>Balance Swap Option</h4>
<p>Enable automated transfer of excess funds to linked deposits or vice-versa to maintain liquidity.</p>
</div>
<div class="rad-alert__choices">
<span class="rad-yn" [class.rad-yn--active]="productTemplate.balanceSwapOption === true">yes</span>
<span class="rad-yn" [class.rad-yn--active]="productTemplate.balanceSwapOption === false">no</span>
</div>
</div>
<div class="rad-alert">
<div class="rad-alert__content">
<h4>MSME</h4>
<p>Micro, Small and Medium Enterprises</p>
</div>
<div class="rad-alert__choices">
<span class="rad-yn" [class.rad-yn--active]="productTemplate.msme === true">yes</span>
<span class="rad-yn" [class.rad-yn--active]="productTemplate.msme === false">no</span>
</div>
</div>
<div class="rad-alert">
<div class="rad-alert__content">
<h4>Nomination Facility</h4>
<p>
Allow account holders to designate individuals to receive funds in the event of account holder's death.
</p>
</div>
<div class="rad-alert__choices">
<span class="rad-yn" [class.rad-yn--active]="productTemplate.nominationFacility === true">yes</span>
<span class="rad-yn" [class.rad-yn--active]="productTemplate.nominationFacility === false">no</span>
</div>
</div>
<div class="rad-alert">
<div class="rad-alert__content">
<h4>Auto Debit Facility</h4>
<p>Enable automatic deduction of funds for recurring payments, utility bills, or EMI obligations.</p>
</div>
<div class="rad-alert__choices">
<span class="rad-yn" [class.rad-yn--active]="productTemplate.autoDebitFacility === true">yes</span>
<span class="rad-yn" [class.rad-yn--active]="productTemplate.autoDebitFacility === false">no</span>
</div>
</div>
</div>
</div>
@if (productTemplate?.introducerRequired) {
<mifosx-introducer-search (introducerChange)="onIntroducerChange($event)"></mifosx-introducer-search>
}
}
<div class="layout-row align-center gap-2percent margin-t responsive-column">
<button mat-raised-button matStepperPrevious disabled>
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Previous' | translate }}
</button>
<button mat-raised-button matStepperNext [disabled]="recurringDepositAccountDetailsForm.invalid">
{{ 'labels.buttons.Next' | translate }}
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
</button>
</div>
</form>
@@ -16,7 +16,7 @@ $gray-900: #202124;
.rad-section {
border: 1px solid $border;
border-radius: 10px;
overflow: hidden;
overflow: visible;
background: #fff;
&--mt {
@@ -183,3 +183,61 @@ $gray-900: #202124;
}
}
}
// ── Account Feature Toggles ────────────────────────────────────────────────
.rad-toggles {
display: flex;
flex-direction: column;
gap: 10px;
padding: 4px 0;
}
.rad-toggle-card {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 18px;
border: 1.5px solid #e2e8f0;
border-radius: 10px;
background: #fff;
transition:
border-color 0.2s,
box-shadow 0.2s;
&:hover {
border-color: #93c5fd;
box-shadow: 0 1px 6px rgb(59 130 246 / 10%);
}
&__left {
display: flex;
align-items: center;
gap: 12px;
> i {
width: 36px;
height: 36px;
border-radius: 8px;
background: #f0f9ff;
border: 1px solid #bae6fd;
color: #0284c7;
display: flex;
align-items: center;
justify-content: center;
font-size: 15px;
flex: 0 0 auto;
}
}
&__label {
font-size: 13.5px;
font-weight: 600;
color: #1e293b;
}
&__hint {
font-size: 11.5px;
color: #64748b;
margin-top: 2px;
}
}
@@ -18,6 +18,7 @@ import { MatTooltip } from '@angular/material/tooltip';
import { MatStepperPrevious, MatStepperNext } from '@angular/material/stepper';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { IntroducerSearchComponent } from 'app/shared/introducer-search/introducer-search.component';
/**
* Recurring Deposits Account Details Step
@@ -31,7 +32,8 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
MatTooltip,
MatStepperPrevious,
FaIconComponent,
MatStepperNext
MatStepperNext,
IntroducerSearchComponent
]
})
export class RecurringDepositsAccountDetailsStepComponent implements OnInit {
@@ -58,6 +60,7 @@ export class RecurringDepositsAccountDetailsStepComponent implements OnInit {
recurringDepositAccountDetailsForm!: UntypedFormGroup;
isProductSelected = false;
introducerId: number | null = null;
/** Recurring Deposits Account Template with product data */
@Output() recurringDepositsAccountProductTemplate = new EventEmitter();
@@ -106,7 +109,10 @@ export class RecurringDepositsAccountDetailsStepComponent implements OnInit {
Validators.required
],
fieldOfficerId: [''],
externalId: ['']
externalId: [''],
balanceSwapOption: [false],
msme: [false],
autoDebitFacility: [false]
});
}
@@ -135,6 +141,12 @@ export class RecurringDepositsAccountDetailsStepComponent implements OnInit {
this.recurringDepositsAccountProductTemplate.emit(response);
this.fieldOfficerData = response.fieldOfficerOptions;
this.isProductSelected = true;
// Pre-populate toggles from product defaults (user can override)
this.recurringDepositAccountDetailsForm.patchValue({
balanceSwapOption: !!response.balanceSwapOption,
msme: !!response.msme,
autoDebitFacility: !!response.autoDebitFacility
});
this.cdr.detectChanges();
if (!this.isFieldOfficerPatched && this.recurringDepositsAccountTemplate.fieldOfficerId) {
this.recurringDepositAccountDetailsForm
@@ -184,7 +196,15 @@ export class RecurringDepositsAccountDetailsStepComponent implements OnInit {
/**
* Returns recurring deposits account details form value.
*/
onIntroducerChange(event: { clientId: number; displayName: string } | null): void {
this.introducerId = event?.clientId ?? null;
}
get recurringDepositAccountDetails() {
return this.recurringDepositAccountDetailsForm.value;
const value = this.recurringDepositAccountDetailsForm.value;
if (this.introducerId) {
value.introducerId = this.introducerId;
}
return value;
}
}
@@ -207,14 +207,3 @@
<tr mat-row *matRowDef="let row; columns: chartSlabsIncentivesDisplayedColumns" class="incentives-row"></tr>
</table>
</div>
<div class="layout-row responsive-column align-center gap-2px margin-t">
<button mat-raised-button matStepperPrevious>
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Previous' | translate }}
</button>
<button mat-raised-button matStepperNext>
{{ 'labels.buttons.Next' | translate }}
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
</button>
</div>
@@ -249,316 +249,6 @@
</div>
</div>
<!-- ===================== Terms ===================== -->
<div class="preview-section">
<div class="preview-section__header">
<span class="preview-section__title">{{ 'labels.heading.Terms' | translate }}</span>
</div>
<div class="preview-body">
<div class="preview-grid preview-grid--three">
<div class="preview-field">
<span class="preview-label">{{ 'labels.inputs.Nominal Annual Interest' | translate }}</span>
<span class="preview-value">
{{ recurringDepositsAccountProductTemplate?.nominalAnnualInterestRate ?? '—' }}
@if (recurringDepositsAccountProductTemplate?.nominalAnnualInterestRate != null) {
%
}
</span>
</div>
@if (recurringDepositsAccountProductTemplate?.effectiveDate) {
<div class="preview-field">
<span class="preview-label">Effective Date</span>
<span class="preview-value">{{ recurringDepositsAccountProductTemplate?.effectiveDate | dateFormat }}</span>
</div>
}
<div class="preview-field">
<span class="preview-label">{{ 'labels.inputs.Interest Compounding Period' | translate }}</span>
<span class="preview-value">{{
recurringDepositAccountData?.interestCompoundingPeriodType
| find: recurringDepositsAccountProductTemplate?.interestCompoundingPeriodTypeOptions : 'id' : 'value'
| translateKey: 'catalogs'
}}</span>
</div>
<div class="preview-field">
<span class="preview-label">{{ 'labels.inputs.Interest Posting Period' | translate }}</span>
<span class="preview-value">{{
recurringDepositAccountData?.interestPostingPeriodType
| find: recurringDepositsAccountProductTemplate?.interestPostingPeriodTypeOptions : 'id' : 'value'
| translateKey: 'catalogs'
}}</span>
</div>
@if (recurringDepositsAccountProductTemplate?.averagePeriod) {
<div class="preview-field">
<span class="preview-label">Average Period</span>
<span class="preview-value">{{ recurringDepositsAccountProductTemplate?.averagePeriod }}</span>
</div>
}
<div class="preview-field">
<span class="preview-label">{{ 'labels.inputs.Interest Calculated using' | translate }}</span>
<span class="preview-value">{{
recurringDepositAccountData?.interestCalculationType
| find: recurringDepositsAccountProductTemplate?.interestCalculationTypeOptions : 'id' : 'value'
| translateKey: 'catalogs'
}}</span>
</div>
<div class="preview-field">
<span class="preview-label">{{ 'labels.inputs.Days in Year' | translate }}</span>
<span class="preview-value">{{
recurringDepositAccountData?.interestCalculationDaysInYearType
| find: recurringDepositsAccountProductTemplate?.interestCalculationDaysInYearTypeOptions : 'id' : 'value'
| translateKey: 'catalogs'
}}</span>
</div>
@if (
recurringDepositsAccountProductTemplate?.autoPost !== undefined &&
recurringDepositsAccountProductTemplate?.autoPost !== null
) {
<div class="preview-field">
<span class="preview-label">Auto Interest Post</span>
<span
class="preview-value preview-badge"
[class.preview-badge--yes]="recurringDepositsAccountProductTemplate?.autoPost"
>
{{ recurringDepositsAccountProductTemplate?.autoPost | yesNo }}
</span>
</div>
}
@if (
recurringDepositsAccountProductTemplate?.frequencyFixDay !== undefined &&
recurringDepositsAccountProductTemplate?.frequencyFixDay !== null
) {
<div class="preview-field">
<span class="preview-label">Frequency (Fix Day)</span>
<span class="preview-value">{{ recurringDepositsAccountProductTemplate?.frequencyFixDay }}</span>
</div>
}
@if (recurringDepositsAccountProductTemplate?.lastInterestDate) {
<div class="preview-field">
<span class="preview-label">Last Interest Date</span>
<span class="preview-value">{{
recurringDepositsAccountProductTemplate?.lastInterestDate | dateFormat
}}</span>
</div>
}
@if (recurringDepositsAccountProductTemplate?.interestCalculationTypeName) {
<div class="preview-field">
<span class="preview-label">Interest Calculation Type</span>
<span class="preview-value">{{
recurringDepositsAccountProductTemplate?.interestCalculationTypeName
}}</span>
</div>
}
</div>
</div>
</div>
<!-- ===================== Settings ===================== -->
<div class="preview-section">
<div class="preview-section__header">
<span class="preview-section__title">{{ 'labels.heading.Settings' | translate }}</span>
</div>
<div class="preview-body">
<!-- Premature Option -->
<div class="preview-subsection">
<div class="preview-subsection__title">Premature Option</div>
<div class="preview-grid preview-grid--three">
<div class="preview-field">
<span class="preview-label">Loan Facility</span>
<span
class="preview-value preview-badge"
[class.preview-badge--yes]="recurringDepositAccountData?.loanFacility"
>
{{ recurringDepositAccountData?.loanFacility | yesNo }}
</span>
</div>
<div class="preview-field">
<span class="preview-label">Premature Withdrawal Option</span>
<span
class="preview-value preview-badge"
[class.preview-badge--yes]="recurringDepositAccountData?.prematureWithdrawalOption"
>
{{ recurringDepositAccountData?.prematureWithdrawalOption | yesNo }}
</span>
</div>
<div class="preview-field">
<span class="preview-label">Tenure Flexibility</span>
<span
class="preview-value preview-badge"
[class.preview-badge--yes]="recurringDepositAccountData?.tenureFlexibility"
>
{{ recurringDepositAccountData?.tenureFlexibility | yesNo }}
</span>
</div>
<div class="preview-field">
<span class="preview-label">Lien Marked</span>
<span
class="preview-value preview-badge"
[class.preview-badge--yes]="recurringDepositAccountData?.lienMarked"
>
{{ recurringDepositAccountData?.lienMarked | yesNo }}
</span>
</div>
<div class="preview-field">
<span class="preview-label">Withdrawal Fee for Transfers</span>
<span
class="preview-value preview-badge"
[class.preview-badge--yes]="recurringDepositAccountData?.withdrawalFeeForTransfers"
>
{{ recurringDepositAccountData?.withdrawalFeeForTransfers | yesNo }}
</span>
</div>
</div>
</div>
<mat-divider class="preview-divider"></mat-divider>
<!-- Lock-in Period -->
<div class="preview-subsection">
<div class="preview-subsection__title">{{ 'labels.inputs.Lock-in Period' | translate }}</div>
<div class="preview-grid preview-grid--three">
<div class="preview-field">
<span class="preview-label">Enable Lock-in Period</span>
<span
class="preview-value preview-badge"
[class.preview-badge--yes]="recurringDepositAccountData?.enableLockinPeriod"
>
{{ recurringDepositAccountData?.enableLockinPeriod | yesNo }}
</span>
</div>
@if (recurringDepositAccountData?.enableLockinPeriod) {
<div class="preview-field">
<span class="preview-label">Lock-in Frequency</span>
<span class="preview-value">
{{ recurringDepositAccountData?.lockinPeriodFrequency }}
{{
recurringDepositAccountData?.lockinPeriodFrequencyType
| find: recurringDepositsAccountProductTemplate?.lockinPeriodFrequencyTypeOptions : 'id' : 'value'
| translateKey: 'catalogs'
}}
</span>
</div>
}
</div>
</div>
<mat-divider class="preview-divider"></mat-divider>
<!-- Maturity Instructions -->
<div class="preview-subsection">
<div class="preview-subsection__title">Maturity Instructions</div>
<div class="preview-grid preview-grid--three">
<div class="preview-field">
<span class="preview-label">Maturity Instructions</span>
<span
class="preview-value preview-badge"
[class.preview-badge--yes]="recurringDepositAccountData?.maturityInstructions"
>
{{ recurringDepositAccountData?.maturityInstructions | yesNo }}
</span>
</div>
<div class="preview-field">
<span class="preview-label">Credit Maturity Proceeds</span>
<span
class="preview-value preview-badge"
[class.preview-badge--yes]="recurringDepositAccountData?.creditMaturityProceeds"
>
{{ recurringDepositAccountData?.creditMaturityProceeds | yesNo }}
</span>
</div>
<div class="preview-field">
<span class="preview-label">Penalty / Grace Period for Missed Installments</span>
<span
class="preview-value preview-badge"
[class.preview-badge--yes]="recurringDepositAccountData?.penaltyGracePeriodMissedInstallments"
>
{{ recurringDepositAccountData?.penaltyGracePeriodMissedInstallments | yesNo }}
</span>
</div>
<div class="preview-field">
<span class="preview-label">Panel Request Applicable</span>
<span
class="preview-value preview-badge"
[class.preview-badge--yes]="recurringDepositAccountData?.panelRequestApplicable"
>
{{ recurringDepositAccountData?.panelRequestApplicable | yesNo }}
</span>
</div>
<div class="preview-field">
<span class="preview-label">No Penalty for Nominee / Legal Heir Closure</span>
<span
class="preview-value preview-badge"
[class.preview-badge--yes]="recurringDepositAccountData?.noPenaltyNomineeHeirClosure"
>
{{ recurringDepositAccountData?.noPenaltyNomineeHeirClosure | yesNo }}
</span>
</div>
<div class="preview-field">
<span class="preview-label">Deceased Settlement</span>
<span
class="preview-value preview-badge"
[class.preview-badge--yes]="recurringDepositAccountData?.deceasedSettlement"
>
{{ recurringDepositAccountData?.deceasedSettlement | yesNo }}
</span>
</div>
<div class="preview-field">
<span class="preview-label">Allow Debit Txn after Due Date</span>
<span
class="preview-value preview-badge"
[class.preview-badge--yes]="recurringDepositAccountData?.allowDebitTxnAfterDueDate"
>
{{ recurringDepositAccountData?.allowDebitTxnAfterDueDate | yesNo }}
</span>
</div>
</div>
</div>
<mat-divider class="preview-divider"></mat-divider>
<!-- Additional Rules -->
<div class="preview-subsection">
<div class="preview-subsection__title">Additional Rules</div>
<div class="preview-grid preview-grid--three">
<div class="preview-field">
<span class="preview-label">Account Closed with Zero Balance in Day Closing</span>
<span
class="preview-value preview-badge"
[class.preview-badge--yes]="recurringDepositAccountData?.accountClosedZeroBalanceInDayClosing"
>
{{ recurringDepositAccountData?.accountClosedZeroBalanceInDayClosing | yesNo }}
</span>
</div>
@if (recurringDepositAccountData?.minBalanceForInterestCalculation) {
<div class="preview-field">
<span class="preview-label">Min. Balance for Interest Calculation</span>
<span class="preview-value">{{
recurringDepositAccountData?.minBalanceForInterestCalculation | formatNumber
}}</span>
</div>
}
@if (recurringDepositAccountData?.mandatoryRecommendedDepositAmount) {
<div class="preview-field">
<span class="preview-label">{{ 'labels.inputs.Recurring Deposit Amount' | translate }}</span>
<span class="preview-value">{{
recurringDepositAccountData?.mandatoryRecommendedDepositAmount | formatNumber
}}</span>
</div>
}
</div>
</div>
</div>
</div>
<!-- ===================== Interest Rate Chart ===================== -->
@if (_activeRdVersion || recurringDepositsAccountProductTemplate?.accountChart) {
<div class="preview-section">
@@ -770,82 +460,6 @@
</div>
</div>
}
<!-- ===================== Charges ===================== -->
@if (recurringDepositAccountData?.charges?.length) {
<div class="preview-section">
<div class="preview-section__header">
<span class="preview-section__title">{{ 'labels.heading.Charges' | translate }}</span>
</div>
<div class="preview-body">
<table
class="flex-100 mat-elevation-z1 preview-table"
mat-table
[dataSource]="recurringDepositAccountData.charges"
>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.name' | translate }}</th>
<td mat-cell *matCellDef="let charge">{{ charge.name + ', ' + charge.currency?.displaySymbol }}</td>
</ng-container>
<ng-container matColumnDef="chargeCalculationType">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Type' | translate }}</th>
<td mat-cell *matCellDef="let charge">
{{ charge.chargeCalculationType?.value | translateKey: 'catalogs' }}
</td>
</ng-container>
<ng-container matColumnDef="amount">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Amount' | translate }}</th>
<td mat-cell *matCellDef="let charge">{{ charge.amount | formatNumber }}</td>
</ng-container>
<ng-container matColumnDef="chargeTimeType">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Collected On' | translate }}</th>
<td mat-cell *matCellDef="let charge">{{ charge.chargeTimeType?.value }}</td>
</ng-container>
<ng-container matColumnDef="date">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Date' | translate }}</th>
<td mat-cell *matCellDef="let charge">
<span
*ngIf="
charge.chargeTimeType?.value === 'Specified due date' || charge.chargeTimeType?.value === 'Weekly Fee'
"
>
{{ (charge.dueDate | dateFormat) || 'Unassigned' }}
</span>
<span
*ngIf="charge.chargeTimeType?.value === 'Monthly Fee' || charge.chargeTimeType?.value === 'Annual Fee'"
>
{{ (charge.feeOnMonthDay | dateFormat) || 'Unassigned' }}
</span>
<span
*ngIf="
!(
charge.chargeTimeType?.value === 'Monthly Fee' ||
charge.chargeTimeType?.value === 'Annual Fee' ||
charge.chargeTimeType?.value === 'Specified due date' ||
charge.chargeTimeType?.value === 'Weekly Fee'
)
"
>
{{ 'labels.inputs.N/A' | translate }}
</span>
</td>
</ng-container>
<ng-container matColumnDef="repaymentsEvery">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Repayments Every' | translate }}</th>
<td mat-cell *matCellDef="let charge">{{ charge.feeInterval || 'Not Provided' }}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="chargesDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: chargesDisplayedColumns"></tr>
</table>
</div>
</div>
}
</div>
<div class="layout-row responsive-column align-center gap-2px margin-t">