/** * 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, Input, OnChanges, SimpleChanges, inject } from '@angular/core'; import { UntypedFormGroup, UntypedFormBuilder, UntypedFormControl, Validators } from '@angular/forms'; import { MatCheckbox } from '@angular/material/checkbox'; import { MatIcon } from '@angular/material/icon'; import { MatIconButton } from '@angular/material/button'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; /** * Fixed Deposit Account Maturity Terms Step -- what happens to this account at maturity * (Withdraw / Transfer to Savings / Reinvest) and the pre-mature-closure penalty, decided * per account rather than silently inherited from the product. */ @Component({ selector: 'mifosx-fixed-deposit-account-maturity-terms-step', templateUrl: './fixed-deposit-account-maturity-terms-step.component.html', styleUrls: ['./fixed-deposit-account-maturity-terms-step.component.scss'], imports: [ ...STANDALONE_SHARED_IMPORTS, MatCheckbox, MatIcon, MatIconButton ] }) export class FixedDepositAccountMaturityTermsStepComponent implements OnChanges { private formBuilder = inject(UntypedFormBuilder); @Input() fixedDepositsAccountTemplate: any; @Input() fixedDepositsAccountProductTemplate: any; /** Account already linked elsewhere in the wizard (the Details step's Deposit Account Link) -- * pre-fills Transfer to Savings so the officer doesn't have to pick the same account twice. */ @Input() linkedSavingsAccountId: number | null = null; closureSettingsForm: UntypedFormGroup; maturityInstructionOptions: any[] = []; savingsAccountsData: any[] = []; preClosurePenalInterestOnTypeData: any[] = []; constructor() { this.closureSettingsForm = this.formBuilder.group({ maturityInstructionId: [''], preClosurePenalApplicable: [false], preClosurePenalInterest: [ '', Validators.min(0) ], preClosurePenalInterestOnTypeId: [''] }); this.closureSettingsForm.get('maturityInstructionId').valueChanges.subscribe((value: number) => { // 200 = Transfer to Savings -- the whole matured balance goes here. 400 = Reinvest Principal // Only -- the principal rolls into a new FD, but the leftover interest still needs the same // kind of target so it isn't left stranded on the closed account (a real gap the account used // to silently hit before this field also asked for it here). Reinvest Principal + Interest // (300) rolls the whole balance into the same account, so it never needs a target at all. if (value === 200 || value === 400) { this.closureSettingsForm.addControl( 'transferToSavingsId', new UntypedFormControl(this.linkedSavingsAccountId ?? '', Validators.required) ); } else { this.closureSettingsForm.removeControl('transferToSavingsId'); } }); } ngOnChanges(changes: SimpleChanges) { if (changes.linkedSavingsAccountId) { const control = this.closureSettingsForm.get('transferToSavingsId'); if (control && !control.value && this.linkedSavingsAccountId) { control.setValue(this.linkedSavingsAccountId); } } if (this.fixedDepositsAccountProductTemplate) { // Withdraw Deposit (id 100) books the maturity proceeds out of the bank's cash/vault the moment // the account matures, before the customer has actually collected it -- disabled everywhere. this.maturityInstructionOptions = ( this.fixedDepositsAccountProductTemplate.maturityInstructionOptions ?? [] ).filter((option: any) => option.id !== 100); this.savingsAccountsData = this.fixedDepositsAccountProductTemplate.savingsAccounts ?? []; this.preClosurePenalInterestOnTypeData = this.fixedDepositsAccountProductTemplate.preClosurePenalInterestOnTypeOptions ?? []; if (!this.closureSettingsForm.get('preClosurePenalApplicable').dirty) { this.closureSettingsForm.patchValue({ preClosurePenalApplicable: this.fixedDepositsAccountProductTemplate.preClosurePenalApplicable ?? false, preClosurePenalInterest: this.fixedDepositsAccountProductTemplate.preClosurePenalInterest ?? '', preClosurePenalInterestOnTypeId: this.fixedDepositsAccountProductTemplate.preClosurePenalInterestOnType?.id ?? '' }); } } // Editing an existing account: its own already-saved Maturity Instructions choice lives on // `fixedDepositsAccountTemplate` (the real account+template GET, FixedDepositAccountData's // `onAccountClosure`) -- NOT on `fixedDepositsAccountProductTemplate`, which is only the blank // per-product default template refetched from the Product dropdown's own productId (via // getFixedDepositsAccountTemplate(clientId, productId)) and carries no account-specific data at // all. This was never read from the right place before, so Edit always opened onto a blank // picker even for an account that really does have an instruction saved. `.dirty` guards it the // same way the penal-interest fields above do, so a maker's own in-progress edit here is never // silently overwritten if this input fires again mid-edit. if (this.fixedDepositsAccountTemplate && !this.closureSettingsForm.get('maturityInstructionId').dirty) { this.closureSettingsForm .get('maturityInstructionId') ?.setValue(this.fixedDepositsAccountTemplate.onAccountClosure?.id ?? ''); const transferControl = this.closureSettingsForm.get('transferToSavingsId'); if (transferControl) { transferControl.setValue( this.fixedDepositsAccountTemplate.transferToSavingsId ?? this.linkedSavingsAccountId ?? '' ); } } } /** The linked-savings account for the transfer-to-savings / reinvest-principal-only-interest * maturity instruction, once picked. */ get selectedTransferAccount(): any { const id = this.closureSettingsForm.value.transferToSavingsId; if (!id) return null; return this.savingsAccountsData.find((account: any) => account.id === id) ?? null; } get transferAccountCardTitle(): string { return this.closureSettingsForm.value.maturityInstructionId === 400 ? 'Interest payout account' : 'Transfer to savings account'; } get transferAccountCardSubtitle(): string { return this.closureSettingsForm.value.maturityInstructionId === 400 ? 'The principal renews into a new FD; interest earned is paid out to this account.' : 'Maturity proceeds will be credited to this account.'; } clearTransferAccount(): void { this.closureSettingsForm.get('transferToSavingsId')?.setValue(''); } /** Chosen maturity/closure settings, ready to merge into the submit payload. */ get closureSettings() { const settings = this.closureSettingsForm.getRawValue(); for (const key in settings) { if (settings[key] === '') { delete settings[key]; } } return settings; } }