feat(accounts): add Modify Application for rejected Current/Savings/FD/RD accounts
- Add 'Modify Application' button in kebab menu for rejected accounts (all 4 types)
- Rewrite edit components with tab-based UI matching create form
- Pre-populate all create-time values in edit form: product, account status,
customer segment, account type, joint holders, nominees, introducer,
deposit account link, senior citizen rate, tenure, flags (msme/balanceSwap/autoDebit)
- Fix edit mode detection using accountNo presence
- Add preselectedIntroducer @Input to IntroducerSearchComponent for pre-fill
- Support both camelCase and lowercase keys for joint holders (MySQL queryForList)
- Fix account number display: actual accountNo before previewAccountNo
- Fix cbsAccountType restore: cbsAccountType only, avoid Fineract enum object
- Add nominees fetch via /clients/{id}/nominees API in all 4 edit components
- Fix RD/FD Interest Rate Chart: SimpleChanges to prevent reset race condition
- Pre-check Senior Citizen checkbox in edit form from isSeniorCitizen DB field
- Auto-calculate rate when tenure pre-filled in edit mode
- FD calculator: use backend calculate-fd-interest API for all compounding types
- Support interestCompoundingPeriodInMonths=0 (daily) in FD calculator API
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
3be2f96210
commit
260fabaa48
+138
-89
@@ -6,96 +6,145 @@
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<div class="container">
|
||||
<mat-stepper
|
||||
orientation="vertical"
|
||||
[linear]="false"
|
||||
class="mat-elevation-z8"
|
||||
labelPosition="bottom"
|
||||
#fixedDepositAccountStepper
|
||||
<div class="cfa-container">
|
||||
<!-- Hero Header -->
|
||||
<div class="cfa-header">
|
||||
<fa-icon icon="university" class="cfa-header__icon"></fa-icon>
|
||||
<div>
|
||||
<h2 class="cfa-header__title">Modify Fixed Deposit Account Application</h2>
|
||||
<div class="cfa-header__sub">
|
||||
Review and update the account details, currency and interest terms before resubmitting.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Wizard tabs -->
|
||||
<mat-tab-group
|
||||
class="cfa-tabs"
|
||||
animationDuration="200ms"
|
||||
[preserveContent]="true"
|
||||
[(selectedIndex)]="selectedTabIndex"
|
||||
>
|
||||
<ng-template matStepperIcon="number">
|
||||
<fa-icon icon="pencil-alt" size="sm"></fa-icon>
|
||||
</ng-template>
|
||||
|
||||
<ng-template matStepperIcon="edit">
|
||||
<fa-icon icon="pencil-alt" size="sm"></fa-icon>
|
||||
</ng-template>
|
||||
|
||||
<ng-template matStepperIcon="done">
|
||||
<fa-icon icon="check" size="sm"></fa-icon>
|
||||
</ng-template>
|
||||
|
||||
<ng-template matStepperIcon="error">
|
||||
<fa-icon icon="exclamation-triangle" size="lg"></fa-icon>
|
||||
</ng-template>
|
||||
|
||||
<ng-template matStepperIcon="preview">
|
||||
<fa-icon icon="eye" size="sm"></fa-icon>
|
||||
</ng-template>
|
||||
|
||||
<mat-step [stepControl]="fixedDepositAccountDetailsForm">
|
||||
<ng-template matStepLabel>{{ 'labels.inputs.DETAILS' | translate }}</ng-template>
|
||||
|
||||
<mifosx-fixed-deposit-account-details-step
|
||||
[fixedDepositsAccountTemplate]="fixedDepositsAccountAndTemplate"
|
||||
(fixedDepositsAccountProductTemplate)="setTemplate($event)"
|
||||
>
|
||||
</mifosx-fixed-deposit-account-details-step>
|
||||
</mat-step>
|
||||
|
||||
<mat-step [stepControl]="fixedDepositAccountTermsForm">
|
||||
<ng-template matStepLabel>{{ 'labels.inputs.TERMS' | translate }}</ng-template>
|
||||
|
||||
<mifosx-fixed-deposit-account-terms-step
|
||||
[fixedDepositsAccountTemplate]="fixedDepositsAccountAndTemplate"
|
||||
[fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate"
|
||||
>
|
||||
</mifosx-fixed-deposit-account-terms-step>
|
||||
</mat-step>
|
||||
|
||||
<mat-step [stepControl]="fixedDepositAccountSettingsForm">
|
||||
<ng-template matStepLabel>{{ 'labels.inputs.SETTINGS' | translate }}</ng-template>
|
||||
|
||||
<mifosx-fixed-deposit-account-settings-step
|
||||
[fixedDepositsAccountTemplate]="fixedDepositsAccountAndTemplate"
|
||||
[fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate"
|
||||
>
|
||||
</mifosx-fixed-deposit-account-settings-step>
|
||||
</mat-step>
|
||||
|
||||
<mat-step>
|
||||
<ng-template matStepLabel>{{ 'labels.inputs.INTEREST RATE CHART' | translate }}</ng-template>
|
||||
|
||||
<mifosx-fixed-deposit-account-interest-rate-chart-step
|
||||
[fixedDepositsAccountTemplate]="fixedDepositsAccountAndTemplate"
|
||||
[fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate"
|
||||
>
|
||||
</mifosx-fixed-deposit-account-interest-rate-chart-step>
|
||||
</mat-step>
|
||||
|
||||
<mat-step>
|
||||
<ng-template matStepLabel>{{ 'labels.inputs.CHARGES' | translate }}</ng-template>
|
||||
|
||||
<mifosx-fixed-deposit-account-charges-step
|
||||
[fixedDepositsAccountTemplate]="fixedDepositsAccountAndTemplate"
|
||||
[fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate"
|
||||
[fixedDepositAccountFormValid]="fixedDepositAccountFormValidAndNotPristine"
|
||||
>
|
||||
</mifosx-fixed-deposit-account-charges-step>
|
||||
</mat-step>
|
||||
|
||||
@if (fixedDepositAccountFormValidAndNotPristine) {
|
||||
<mat-step state="preview" completed>
|
||||
<ng-template matStepLabel>{{ 'labels.inputs.PREVIEW' | translate }}</ng-template>
|
||||
<mifosx-fixed-deposit-account-preview-step
|
||||
<!-- Tab 1: Product Classification + Details -->
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<fa-icon icon="check-square" class="cfa-tab__icon"></fa-icon>
|
||||
Product
|
||||
</ng-template>
|
||||
<div class="cfa-tab-body">
|
||||
<mifosx-fixed-deposit-account-details-step
|
||||
[fixedDepositsAccountTemplate]="fixedDepositsAccountAndTemplate"
|
||||
[fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate"
|
||||
[fixedDepositAccountData]="fixedDepositAccount"
|
||||
(submitEvent)="submit()"
|
||||
(fixedDepositsAccountProductTemplate)="setTemplate($event)"
|
||||
[hideButtons]="true"
|
||||
>
|
||||
</mifosx-fixed-deposit-account-preview-step>
|
||||
</mat-step>
|
||||
}
|
||||
</mat-stepper>
|
||||
</mifosx-fixed-deposit-account-details-step>
|
||||
|
||||
<div class="cfa-actions">
|
||||
<span class="cfa-actions__spacer"></span>
|
||||
<button mat-raised-button color="primary" [disabled]="!fixedDepositsAccountProductTemplate" (click)="next()">
|
||||
{{ 'labels.buttons.Next' | translate }}
|
||||
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
<!-- Tab 2: Currency -->
|
||||
<mat-tab [disabled]="!fixedDepositsAccountProductTemplate">
|
||||
<ng-template mat-tab-label>
|
||||
<fa-icon icon="dollar-sign" class="cfa-tab__icon"></fa-icon>
|
||||
Currency
|
||||
</ng-template>
|
||||
<div class="cfa-tab-body">
|
||||
@if (fixedDepositsAccountProductTemplate) {
|
||||
<mifosx-fixed-deposit-account-currency-step
|
||||
[fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate"
|
||||
[primaryClientId]="fixedDepositsAccountAndTemplate?.clientId ?? null"
|
||||
[existingAccount]="fixedDepositsAccountAndTemplate"
|
||||
></mifosx-fixed-deposit-account-currency-step>
|
||||
}
|
||||
|
||||
<div class="cfa-actions">
|
||||
<button mat-stroked-button (click)="prev()">
|
||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.Previous' | translate }}
|
||||
</button>
|
||||
<span class="cfa-actions__spacer"></span>
|
||||
<button mat-raised-button color="primary" (click)="next()">
|
||||
{{ 'labels.buttons.Next' | translate }}
|
||||
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
<!-- Tab 3: Interest Rate Chart -->
|
||||
<mat-tab [disabled]="!fixedDepositsAccountProductTemplate">
|
||||
<ng-template mat-tab-label>
|
||||
<fa-icon icon="university" class="cfa-tab__icon"></fa-icon>
|
||||
Interest Rate Chart
|
||||
</ng-template>
|
||||
<div class="cfa-tab-body">
|
||||
@if (fixedDepositsAccountProductTemplate) {
|
||||
<mifosx-fixed-deposit-account-interest-rate-chart-step
|
||||
[fixedDepositsAccountTemplate]="fixedDepositsAccountAndTemplate"
|
||||
[fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate"
|
||||
[cbsBaseRates]="cbsBaseRates"
|
||||
[initialTenure]="selectedTenure"
|
||||
[submittedOnDate]="submittedOnDate"
|
||||
(tenureSelected)="onTenureSelected($event)"
|
||||
>
|
||||
</mifosx-fixed-deposit-account-interest-rate-chart-step>
|
||||
}
|
||||
|
||||
<div class="cfa-actions">
|
||||
<button mat-stroked-button (click)="prev()">
|
||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.Previous' | translate }}
|
||||
</button>
|
||||
<span class="cfa-actions__spacer"></span>
|
||||
<button mat-raised-button color="primary" (click)="next()">
|
||||
{{ 'labels.buttons.Next' | translate }}
|
||||
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
<!-- Tab 4: Preview -->
|
||||
<mat-tab [disabled]="!fixedDepositsAccountProductTemplate">
|
||||
<ng-template mat-tab-label>
|
||||
<fa-icon icon="eye" class="cfa-tab__icon"></fa-icon>
|
||||
{{ 'labels.inputs.PREVIEW' | translate }}
|
||||
</ng-template>
|
||||
<div class="cfa-tab-body">
|
||||
@if (fixedDepositsAccountProductTemplate) {
|
||||
<mifosx-fixed-deposit-account-preview-step
|
||||
[fixedDepositsAccountTemplate]="fixedDepositsAccountAndTemplate"
|
||||
[fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate"
|
||||
[fixedDepositAccountData]="fixedDepositAccount"
|
||||
[selectedTenure]="selectedTenure"
|
||||
[fdNominees]="fixedDepositAccountCurrencyStep?.nominees ?? []"
|
||||
[hideButtons]="true"
|
||||
(submitEvent)="submit()"
|
||||
></mifosx-fixed-deposit-account-preview-step>
|
||||
}
|
||||
|
||||
<div class="cfa-actions">
|
||||
<button mat-stroked-button (click)="prev()">
|
||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.Previous' | translate }}
|
||||
</button>
|
||||
<span class="cfa-actions__spacer"></span>
|
||||
<button mat-stroked-button [routerLink]="['../']">
|
||||
{{ 'labels.buttons.Cancel' | translate }}
|
||||
</button>
|
||||
<button mat-raised-button color="primary" class="preview-submit-row__primary" (click)="submit()">
|
||||
<fa-icon icon="check" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.Submit' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</div>
|
||||
|
||||
+161
@@ -5,3 +5,164 @@
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
$navy: #0d1b2a;
|
||||
$blue: #1e88e5;
|
||||
$blue-d: #1e40af;
|
||||
$indigo: #4f46e5;
|
||||
$border: #e2e8f0;
|
||||
|
||||
.cfa-container {
|
||||
max-width: 1180px;
|
||||
margin: 0 auto;
|
||||
padding: 20px 24px;
|
||||
}
|
||||
|
||||
/* ─── Hero header (matches Nexora pages) ────────────────────────────── */
|
||||
.cfa-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
&__icon {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 12px;
|
||||
background: linear-gradient(135deg, $blue 0%, $indigo 100%);
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
box-shadow: 0 2px 8px rgb(29 78 216 / 25%);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__title {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 800;
|
||||
color: $navy;
|
||||
line-height: 1.1;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
&__sub {
|
||||
margin-top: 2px;
|
||||
font-size: 12.5px;
|
||||
color: #64748b;
|
||||
}
|
||||
}
|
||||
|
||||
/* ─── Wizard tab strip ──────────────────────────────────────────────── */
|
||||
.cfa-tabs {
|
||||
background: #fff;
|
||||
border: 1px solid $border;
|
||||
border-radius: 14px;
|
||||
box-shadow: 0 1px 3px rgb(15 23 42 / 4%);
|
||||
overflow: hidden;
|
||||
|
||||
::ng-deep {
|
||||
.mat-mdc-tab-header {
|
||||
background: #f8fafc;
|
||||
border-bottom: 1px solid $border;
|
||||
}
|
||||
|
||||
.mat-mdc-tab {
|
||||
min-width: 140px;
|
||||
height: 50px;
|
||||
padding: 0 22px;
|
||||
font-size: 13px !important;
|
||||
font-weight: 600 !important;
|
||||
letter-spacing: 0.01em;
|
||||
color: #475569 !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
.mat-mdc-tab:hover:not(.mdc-tab--active, .mat-mdc-tab-disabled) {
|
||||
background: rgb(30 64 175 / 5%);
|
||||
color: $navy !important;
|
||||
}
|
||||
|
||||
.mat-mdc-tab.mdc-tab--active {
|
||||
.mdc-tab__text-label {
|
||||
color: $blue-d !important;
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.mdc-tab-indicator__content--underline {
|
||||
border-color: $blue-d !important;
|
||||
border-top-width: 3px !important;
|
||||
}
|
||||
|
||||
.mat-mdc-tab.mat-mdc-tab-disabled {
|
||||
opacity: 0.45 !important;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.mat-mdc-tab-body-wrapper {
|
||||
min-height: 320px;
|
||||
}
|
||||
|
||||
.mat-mdc-tab-body,
|
||||
.mat-mdc-tab-body-content {
|
||||
overflow: visible !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cfa-tab__icon {
|
||||
margin-right: 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* ─── Tab body + action bar ─────────────────────────────────────────── */
|
||||
.cfa-tab-body {
|
||||
padding: 24px;
|
||||
|
||||
@media (width <= 768px) {
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.cfa-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-top: 24px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid #f1f5f9;
|
||||
|
||||
&__spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
button {
|
||||
height: 40px !important;
|
||||
border-radius: 10px !important;
|
||||
font-weight: 600 !important;
|
||||
padding: 0 18px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.m-l-10 {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.m-r-10 {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
button.preview-submit-row__primary,
|
||||
.mat-mdc-raised-button.mat-primary.preview-submit-row__primary,
|
||||
.preview-submit-row__primary {
|
||||
background: linear-gradient(135deg, #10b981 0%, #047857 100%) !important;
|
||||
|
||||
--mdc-protected-button-container-color: transparent !important;
|
||||
--mdc-filled-button-container-color: transparent !important;
|
||||
|
||||
color: #fff !important;
|
||||
box-shadow: 0 4px 12px -2px rgb(4 120 87 / 35%) !important;
|
||||
}
|
||||
|
||||
+198
-88
@@ -12,23 +12,23 @@ import { ActivatedRoute, Router } from '@angular/router';
|
||||
|
||||
/** Custom Services */
|
||||
import { FixedDepositsService } from '../fixed-deposits.service';
|
||||
import { ClientsService } from 'app/clients/clients.service';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { AlertService } from 'app/core/alert/alert.service';
|
||||
import { ProductsService } from 'app/products/products.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 { Currency } from 'app/shared/models/general.model';
|
||||
import { MatStepper, MatStepperIcon, MatStep, MatStepLabel } from '@angular/material/stepper';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { FixedDepositAccountInterestRateChartStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-interest-rate-chart-step/fixed-deposit-account-interest-rate-chart-step.component';
|
||||
import { FixedDepositAccountPreviewStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
|
||||
/**
|
||||
* Edit Fixed Deposit Account Component
|
||||
* Edit Fixed Deposit Account Component — same tab UI as Create, pre-populated with existing data.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-edit-fixed-deposit-account',
|
||||
@@ -36,16 +36,11 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
styleUrls: ['./edit-fixed-deposit-account.component.scss'],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
MatStepper,
|
||||
MatStepperIcon,
|
||||
MatTabsModule,
|
||||
FaIconComponent,
|
||||
MatStep,
|
||||
MatStepLabel,
|
||||
FixedDepositAccountDetailsStepComponent,
|
||||
FixedDepositAccountTermsStepComponent,
|
||||
FixedDepositAccountSettingsStepComponent,
|
||||
FixedDepositAccountInterestRateChartStepComponent,
|
||||
FixedDepositAccountChargesStepComponent,
|
||||
FixedDepositAccountCurrencyStepComponent,
|
||||
FixedDepositAccountPreviewStepComponent
|
||||
]
|
||||
})
|
||||
@@ -55,110 +50,201 @@ export class EditFixedDepositAccountComponent {
|
||||
private dateUtils = inject(Dates);
|
||||
private fixedDepositsService = inject(FixedDepositsService);
|
||||
private settingsService = inject(SettingsService);
|
||||
private alertService = inject(AlertService);
|
||||
private productsService = inject(ProductsService);
|
||||
private clientsService = inject(ClientsService);
|
||||
|
||||
/** Fixed Deposits Account Details Step */
|
||||
@ViewChild(FixedDepositAccountDetailsStepComponent, { static: true })
|
||||
fixedDepositsAccountDetailsStep: FixedDepositAccountDetailsStepComponent;
|
||||
/** Fixed Deposits Account Terms Step */
|
||||
@ViewChild(FixedDepositAccountTermsStepComponent, { static: true })
|
||||
fixedDepositAccountTermsStep: FixedDepositAccountTermsStepComponent;
|
||||
/** Fixed Deposits Account Settings Step */
|
||||
@ViewChild(FixedDepositAccountSettingsStepComponent, { static: true })
|
||||
fixedDepositAccountSettingsStep: FixedDepositAccountSettingsStepComponent;
|
||||
/** Fixed Deposits Account Charges Step */
|
||||
@ViewChild(FixedDepositAccountChargesStepComponent, { static: true })
|
||||
fixedDepositAccountChargesStep: FixedDepositAccountChargesStepComponent;
|
||||
@ViewChild(FixedDepositAccountCurrencyStepComponent)
|
||||
fixedDepositAccountCurrencyStep: FixedDepositAccountCurrencyStepComponent;
|
||||
|
||||
/** Fixed Deposits Account Template */
|
||||
/** Full account data + template from resolver */
|
||||
fixedDepositsAccountAndTemplate: any;
|
||||
/** Fixed Deposit Account Product Template */
|
||||
/** Product template emitted by details step */
|
||||
fixedDepositsAccountProductTemplate: any;
|
||||
/** Currently visible wizard tab (0-indexed). */
|
||||
selectedTabIndex = 0;
|
||||
private readonly totalTabs = 4;
|
||||
/** Active CBS base interest rate versions for the selected product's FD type */
|
||||
cbsBaseRates: any[] = [];
|
||||
/** Tenure selected from the interest rate chart step */
|
||||
selectedTenure: {
|
||||
depositPeriod: number;
|
||||
depositPeriodFrequencyId: number;
|
||||
depositAmount?: number;
|
||||
maturityAmount?: number;
|
||||
interestEarned?: number;
|
||||
appliedRate?: number;
|
||||
isSeniorCitizen?: boolean;
|
||||
} | null = null;
|
||||
|
||||
currency: Currency;
|
||||
|
||||
/**
|
||||
* Fetches FD account template from `resolve`
|
||||
* @param {ActivatedRoute} route Activated Route
|
||||
* @param {Router} router Router
|
||||
* @param {Dates} dateUtils Date Utils
|
||||
* @param {FixedDepositsService} fixedDepositsService Fixed Deposits Service
|
||||
* @param {SettingsService} settingsService Settings Service
|
||||
*/
|
||||
constructor() {
|
||||
this.route.data.subscribe((data: { fixedDepositsAccountAndTemplate: any }) => {
|
||||
this.fixedDepositsAccountAndTemplate = data.fixedDepositsAccountAndTemplate;
|
||||
const account = data.fixedDepositsAccountAndTemplate;
|
||||
this.fixedDepositsAccountAndTemplate = account;
|
||||
// Pre-populate tenure from existing account
|
||||
if (account?.depositPeriod) {
|
||||
this.selectedTenure = {
|
||||
depositPeriod: account.depositPeriod,
|
||||
depositPeriodFrequencyId: account.depositPeriodFrequencyType?.id ?? 2,
|
||||
depositAmount: account.depositAmount,
|
||||
isSeniorCitizen: !!account.isSeniorCitizen,
|
||||
appliedRate: account.seniorCitizenInterestRate ?? undefined
|
||||
};
|
||||
}
|
||||
if (account?.clientId && account?.accountNo) {
|
||||
this.clientsService
|
||||
.getClientNominees(String(account.clientId), account.accountNo, 'FD')
|
||||
.subscribe((nominees: any[]) => {
|
||||
if (Array.isArray(nominees) && nominees.length > 0) {
|
||||
this.fixedDepositsAccountAndTemplate = { ...this.fixedDepositsAccountAndTemplate, nominees };
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets fixed deposits account product template.
|
||||
* @param {any} $event API response
|
||||
*/
|
||||
next(): void {
|
||||
if (this.selectedTabIndex < this.totalTabs - 1) {
|
||||
this.selectedTabIndex += 1;
|
||||
}
|
||||
}
|
||||
|
||||
prev(): void {
|
||||
if (this.selectedTabIndex > 0) {
|
||||
this.selectedTabIndex -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
setTemplate($event: any) {
|
||||
this.fixedDepositsAccountProductTemplate = $event;
|
||||
this.currency = this.fixedDepositsAccountAndTemplate.currency;
|
||||
const fdType = $event?.fdType;
|
||||
this.productsService.getFdBaseInterestRates(fdType).subscribe({
|
||||
next: (rates: any[]) => (this.cbsBaseRates = rates || []),
|
||||
error: () => (this.cbsBaseRates = [])
|
||||
});
|
||||
}
|
||||
|
||||
onTenureSelected($event: {
|
||||
depositPeriod: number;
|
||||
depositPeriodFrequencyId: number;
|
||||
depositAmount?: number;
|
||||
maturityAmount?: number;
|
||||
interestEarned?: number;
|
||||
appliedRate?: number;
|
||||
isSeniorCitizen?: boolean;
|
||||
}) {
|
||||
this.selectedTenure = $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves Fixed Deposit Account Details Form Data
|
||||
*/
|
||||
get fixedDepositAccountDetailsForm() {
|
||||
return this.fixedDepositsAccountDetailsStep.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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if stepper is valid and not pristine.
|
||||
*/
|
||||
get fixedDepositAccountFormValidAndNotPristine() {
|
||||
return (
|
||||
this.fixedDepositAccountDetailsForm.valid &&
|
||||
this.fixedDepositAccountTermsForm.valid &&
|
||||
this.fixedDepositAccountSettingsForm.valid &&
|
||||
(!this.fixedDepositAccountDetailsForm.pristine ||
|
||||
!this.fixedDepositAccountTermsForm.pristine ||
|
||||
!this.fixedDepositAccountSettingsForm.pristine ||
|
||||
!this.fixedDepositAccountChargesStep.pristine)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the fixed deposit account object.
|
||||
*/
|
||||
get fixedDepositAccount() {
|
||||
return {
|
||||
...this.fixedDepositsAccountDetailsStep.fixedDepositAccountDetails,
|
||||
...this.fixedDepositAccountTermsStep.fixedDepositAccountTerms,
|
||||
...this.fixedDepositAccountSettingsStep.fixedDepositAccountSettings,
|
||||
...this.fixedDepositAccountChargesStep?.fixedDepositAccountCharges
|
||||
...this.fixedDepositsAccountDetailsStep?.fixedDepositAccountDetails,
|
||||
...this.fixedDepositAccountCurrencyStep?.currencyStepData
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits the fixed deposit form and creates a new fixed deposit account
|
||||
*/
|
||||
get submittedOnDate(): Date | null {
|
||||
const val =
|
||||
this.fixedDepositsAccountDetailsStep?.fixedDepositAccountDetails?.submittedOnDate ??
|
||||
this.fixedDepositsAccountAndTemplate?.timeline?.submittedOnDate;
|
||||
if (!val) return null;
|
||||
const d = Array.isArray(val) ? new Date(val[0], val[1] - 1, val[2]) : new Date(val);
|
||||
return isNaN(d.getTime()) ? null : d;
|
||||
}
|
||||
|
||||
submit() {
|
||||
const detailsStep = this.fixedDepositsAccountDetailsStep;
|
||||
if (detailsStep?.selectedAccountStatus === 'JOINT' && detailsStep.totalJointSharePercentage !== 100) {
|
||||
this.alertService.alert({
|
||||
type: 'Error',
|
||||
message: `Total ownership is ${detailsStep.totalJointSharePercentage}%. It must equal 100% before submitting.`
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (
|
||||
this.fixedDepositsAccountProductTemplate?.nominationFacility &&
|
||||
(this.fixedDepositAccountCurrencyStep?.nominees ?? []).length === 0
|
||||
) {
|
||||
this.alertService.alert({
|
||||
type: 'Error',
|
||||
message: 'Nomination Facility is enabled for this product. Please add at least one nominee before submitting.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!this.selectedTenure || !this.selectedTenure.depositPeriod) {
|
||||
this.alertService.alert({
|
||||
type: 'Error',
|
||||
message: 'Please enter the Deposit Tenure and click "Find Rate" before submitting.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!detailsStep?.selectedCustomerSegment) {
|
||||
this.alertService.alert({ type: 'Error', message: 'Please select a Customer Segment before submitting.' });
|
||||
return;
|
||||
}
|
||||
const locale = this.settingsService.language.code;
|
||||
const dateFormat = this.settingsService.dateFormat;
|
||||
const monthDayFormat = 'dd MMMM';
|
||||
const fixedDepositAccount = {
|
||||
const pt = this.fixedDepositsAccountProductTemplate;
|
||||
|
||||
const terms: any = {};
|
||||
if (pt?.nominalAnnualInterestRate != null) terms.nominalAnnualInterestRate = pt.nominalAnnualInterestRate;
|
||||
if (pt?.interestCompoundingPeriodType?.id != null)
|
||||
terms.interestCompoundingPeriodType = pt.interestCompoundingPeriodType.id;
|
||||
if (pt?.interestPostingPeriodType?.id != null) terms.interestPostingPeriodType = pt.interestPostingPeriodType.id;
|
||||
if (pt?.interestCalculationType?.id != null) terms.interestCalculationType = pt.interestCalculationType.id;
|
||||
if (pt?.interestCalculationDaysInYearType?.id != null)
|
||||
terms.interestCalculationDaysInYearType = pt.interestCalculationDaysInYearType.id;
|
||||
|
||||
if (this.selectedTenure?.depositAmount != null) {
|
||||
terms.depositAmount = this.selectedTenure.depositAmount;
|
||||
} else if (pt?.depositAmount != null) {
|
||||
terms.depositAmount = pt.depositAmount;
|
||||
}
|
||||
|
||||
if (pt?.minDepositAmount != null) terms.minDepositAmount = pt.minDepositAmount;
|
||||
if (pt?.maxDepositAmount != null) terms.maxDepositAmount = pt.maxDepositAmount;
|
||||
|
||||
const settings: any = {};
|
||||
if (pt?.lockinPeriodFrequency != null) settings.lockinPeriodFrequency = pt.lockinPeriodFrequency;
|
||||
if (pt?.lockinPeriodFrequencyType?.id != null) settings.lockinPeriodFrequencyType = pt.lockinPeriodFrequencyType.id;
|
||||
if (pt?.minDepositTerm != null) settings.minDepositTerm = pt.minDepositTerm;
|
||||
if (pt?.minDepositTermType?.id != null) settings.minDepositTermTypeId = pt.minDepositTermType.id;
|
||||
if (pt?.inMultiplesOfDepositTerm != null) settings.inMultiplesOfDepositTerm = pt.inMultiplesOfDepositTerm;
|
||||
if (pt?.inMultiplesOfDepositTermType?.id != null)
|
||||
settings.inMultiplesOfDepositTermTypeId = pt.inMultiplesOfDepositTermType.id;
|
||||
if (pt?.maxDepositTerm != null) settings.maxDepositTerm = pt.maxDepositTerm;
|
||||
if (pt?.maxDepositTermType?.id != null) settings.maxDepositTermTypeId = pt.maxDepositTermType.id;
|
||||
if (pt?.preClosurePenalApplicable != null) settings.preClosurePenalApplicable = pt.preClosurePenalApplicable;
|
||||
if (pt?.preClosurePenalInterest != null) settings.preClosurePenalInterest = pt.preClosurePenalInterest;
|
||||
if (pt?.preClosurePenalInterestOnType?.id != null)
|
||||
settings.preClosurePenalInterestOnTypeId = pt.preClosurePenalInterestOnType.id;
|
||||
if (pt?.maturityInstructionId != null) settings.maturityInstructionId = pt.maturityInstructionId;
|
||||
if (pt?.transferInterestToSavings != null) settings.transferInterestToSavings = pt.transferInterestToSavings;
|
||||
if (pt?.withHoldTax != null) settings.withHoldTax = pt.withHoldTax;
|
||||
|
||||
const rawCharges: any[] =
|
||||
(this.fixedDepositAccount.charges?.length ? this.fixedDepositAccount.charges : pt?.charges) ?? [];
|
||||
|
||||
const fixedDepositAccount: any = {
|
||||
...this.fixedDepositAccount,
|
||||
...terms,
|
||||
...settings,
|
||||
...(this.selectedTenure
|
||||
? {
|
||||
depositPeriod: this.selectedTenure.depositPeriod,
|
||||
depositPeriodFrequencyId: this.selectedTenure.depositPeriodFrequencyId
|
||||
}
|
||||
: {}),
|
||||
clientId: this.fixedDepositsAccountAndTemplate.clientId,
|
||||
charges: this.fixedDepositAccount.charges.map((charge: any) => ({
|
||||
chargeId: charge.id,
|
||||
charges: rawCharges.map((charge: any) => ({
|
||||
chargeId: charge.chargeId ?? charge.id,
|
||||
amount: charge.amount,
|
||||
dueDate: charge.dueDate && this.dateUtils.formatDate(charge.dueDate, dateFormat),
|
||||
feeOnMonthDay:
|
||||
@@ -166,11 +252,35 @@ export class EditFixedDepositAccountComponent {
|
||||
feeInterval: charge.feeInterval
|
||||
})),
|
||||
submittedOnDate: this.dateUtils.formatDate(this.fixedDepositAccount.submittedOnDate, dateFormat),
|
||||
charts: [{ chartSlabs: this.fixedDepositsAccountProductTemplate.accountChart.chartSlabs }],
|
||||
charts: [{ chartSlabs: pt?.accountChart?.chartSlabs ?? [] }],
|
||||
dateFormat,
|
||||
monthDayFormat,
|
||||
locale
|
||||
};
|
||||
|
||||
const nominees = this.fixedDepositAccountCurrencyStep?.nominees ?? [];
|
||||
if (nominees.length > 0) {
|
||||
fixedDepositAccount.nominees = nominees.map((n: any) => ({
|
||||
nomineeClientId: n.nomineeClientId,
|
||||
nominationType: n.nominationType,
|
||||
relationshipId: n.relationshipId,
|
||||
percentageShare: n.percentageShare,
|
||||
nomineeIsMinor: n.nomineeIsMinor,
|
||||
...(n.guardianClientId ? { guardianClientId: n.guardianClientId } : {}),
|
||||
remarks: n.remarks
|
||||
}));
|
||||
}
|
||||
|
||||
if (
|
||||
this.selectedTenure?.isSeniorCitizen &&
|
||||
this.selectedTenure.appliedRate != null &&
|
||||
this.selectedTenure.appliedRate > 0
|
||||
) {
|
||||
fixedDepositAccount.isSeniorCitizen = true;
|
||||
fixedDepositAccount.seniorCitizenInterestRate = this.selectedTenure.appliedRate;
|
||||
fixedDepositAccount.nominalAnnualInterestRate = this.selectedTenure.appliedRate;
|
||||
}
|
||||
|
||||
this.fixedDepositsService
|
||||
.updateFixedDepositAccount(this.fixedDepositsAccountAndTemplate.id, fixedDepositAccount)
|
||||
.subscribe((response: any) => {
|
||||
|
||||
Reference in New Issue
Block a user