/** * 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.' }); } } }); } }