Files
cbsnexora/src/app/current-accounts/create-current-account/create-current-account.component.ts
T
nitesh ff781e50e3 feat(savings,deposits,current-accounts): streamline account activation flow
Matches the backend change that stopped auto-depositing money at
activation: removes the opening-balance field from every Activate
screen (Savings, Current, FD, RD) since funding now happens as a
separate deposit afterward, and removes the Digital-Facility step from
every Create wizard since every facility starts disabled by default
and gets turned on later through Edit Nominee & Features.

Also merges Approve+Activate into a single action for Savings,
Current, FD and RD accounts -- one click takes the account straight to
Active instead of two separate screens.
2026-08-19 10:39:52 +05:30

248 lines
8.6 KiB
TypeScript

/**
* 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/.
*/
/** Angular Imports */
import { Component, ViewChild, inject } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
/** Custom Components */
import { CurrentAccountDetailsStepComponent } from '../current-account-stepper/current-account-details-step/current-account-details-step.component';
import { CurrentAccountCurrencyStepComponent } from '../current-account-stepper/current-account-currency-step/current-account-currency-step.component';
import { CurrentAccountDigitalStepComponent } from '../current-account-stepper/current-account-digital-step/current-account-digital-step.component';
/** Custom Services */
import { CurrentAccountsService } from '../current-accounts.service';
import { SettingsService } from 'app/settings/settings.service';
import { Dates } from 'app/core/utils/dates';
import { AlertService } from 'app/core/alert/alert.service';
import { MatTabsModule } from '@angular/material/tabs';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { CurrentAccountPreviewStepComponent } from '../current-account-stepper/current-account-preview-step/current-account-preview-step.component';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { FormGroup } from '@angular/forms';
/**
* Create Current Account Component
*/
@Component({
selector: 'mifosx-create-current-account-account',
templateUrl: './create-current-account.component.html',
styleUrls: ['./create-current-account.component.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS,
MatTabsModule,
FaIconComponent,
CurrentAccountDetailsStepComponent,
CurrentAccountCurrencyStepComponent,
CurrentAccountDigitalStepComponent,
CurrentAccountPreviewStepComponent
]
})
export class CreateCurrentAccountComponent {
private route = inject(ActivatedRoute);
private router = inject(Router);
private dateUtils = inject(Dates);
private currentAccountsService = inject(CurrentAccountsService);
private settingsService = inject(SettingsService);
private alertService = inject(AlertService);
/** Current Account Template */
currentAccountTemplate: any;
/** Current Account Product Template */
currentAccountProductTemplate: any;
/** Currently visible wizard tab (0-indexed). */
selectedTabIndex = 0;
/** Total wizard tabs — Product, Currency, Preview. */
private readonly totalTabs = 3;
@ViewChild(CurrentAccountDetailsStepComponent, { static: true })
currentAccountDetailsStep: CurrentAccountDetailsStepComponent;
@ViewChild(CurrentAccountCurrencyStepComponent)
currentAccountCurrencyStep: CurrentAccountCurrencyStepComponent;
@ViewChild(CurrentAccountDigitalStepComponent)
currentAccountDigitalStep: CurrentAccountDigitalStepComponent;
constructor() {
this.route.data.subscribe((data: { currentAccountTemplate: any }) => {
this.currentAccountTemplate = data.currentAccountTemplate;
});
}
/**
* Sets current account product template.
*/
setTemplate($event: any) {
this.currentAccountProductTemplate = $event;
}
next(): void {
if (this.selectedTabIndex < this.totalTabs - 1) {
this.selectedTabIndex += 1;
}
}
prev(): void {
if (this.selectedTabIndex > 0) {
this.selectedTabIndex -= 1;
}
}
/**
* Retrieves current account details form.
*/
get currentAccountDetailsForm() {
return this.currentAccountDetailsStep.currentAccountDetailsForm;
}
/** Stub forms needed by preview step inputs */
get currentAccountTermForm(): FormGroup {
return new FormGroup({});
}
get currentAccountSettingsForm(): FormGroup {
return new FormGroup({});
}
get currentAccountTermsForm(): FormGroup {
return new FormGroup({});
}
get currentAccount() {
return { ...this.currentAccountDetailsStep.currentAccountDetails };
}
get currentAccountDigital() {
return this.currentAccountDigitalStep?.currentAccountDigital ?? null;
}
get currentAccountCurrencyFlags() {
return {
balanceSwapOption: this.currentAccountCurrencyStep?.balanceSwapOption ?? false,
msme: this.currentAccountCurrencyStep?.msme ?? false,
autoDebitFacility: this.currentAccountCurrencyStep?.autoDebitFacility ?? false
};
}
/**
* Creates a new current account.
*/
submit() {
const detailsStep = this.currentAccountDetailsStep;
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.currentAccountDetailsStep.isNonIndividualClient &&
this.currentAccountProductTemplate?.nominationFacility &&
(this.currentAccountCurrencyStep?.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.currentAccountProductTemplate?.introducerRequired && !this.currentAccountDetailsStep.introducerId) {
this.alertService.alert({
type: 'Error',
message: 'Introducer is required for this product. Please select an introducer before submitting.'
});
return;
}
const locale = this.settingsService.language.code;
const dateFormat = this.settingsService.dateFormat;
const monthDayFormat = 'dd MMMM';
const details = this.currentAccount;
const currentAccount: any = {
...details,
...this.currentAccountCurrencyStep.currencyStepData,
...(this.currentAccountDigitalStep?.currentAccountDigital ?? {}),
charges: [],
submittedOnDate: this.dateUtils.formatDate(details.submittedOnDate, dateFormat),
dateFormat,
monthDayFormat,
locale
};
if (details.establishmentDate) {
currentAccount.establishmentDate = this.dateUtils.formatDate(details.establishmentDate, dateFormat);
}
if (details.startOperationDate) {
currentAccount.startOperationDate = this.dateUtils.formatDate(details.startOperationDate, dateFormat);
}
// Explicitly re-set partners from the details step to ensure all are included
if (this.currentAccountDetailsStep.isNonIndividualClient) {
currentAccount.isNonIndividualClient = true;
currentAccount.partners = this.currentAccountDetailsStep.relatedClients.map((c: any) => ({
clientId: c.clientId,
clientName: c.clientName,
clientCif: c.clientCif,
sharePercentage: c.sharePercentage,
isMandatory: c.isMandatory
}));
} else {
delete currentAccount.isNonIndividualClient;
}
if (this.currentAccountTemplate.clientId) {
currentAccount.clientId = this.currentAccountTemplate.clientId;
} else {
currentAccount.groupId = this.currentAccountTemplate.groupId;
}
const nominees = this.currentAccountCurrencyStep?.nominees ?? [];
if (nominees.length > 0) {
currentAccount.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
}));
}
this.currentAccountsService.createCurrentAccount(currentAccount).subscribe({
next: (response: any) => {
this.router.navigate(
[
'../',
response.resourceId
],
{ relativeTo: this.route }
);
},
error: (error: any) => {
const errorCode =
error?.error?.errors?.[0]?.userMessageGlobalisationCode ?? error?.error?.userMessageGlobalisationCode;
if (errorCode === 'error.msg.savings.account.duplicate.product.for.client') {
this.alertService.alert({
type: 'Error',
message:
'This client already has an active Current account with the selected product. Please choose a different product.'
});
} else if (errorCode === 'error.msg.savings.account.duplicate.externalid') {
this.alertService.alert({
type: 'Error',
message: 'An account with this External ID already exists. Please use a unique External ID.'
});
}
}
});
}
}