Fixed and Recurring Deposit wizards now bidirectionally sync the Product tab's linked-account selection with the new Terms tab's Transfer-to-Savings account, disable the Withdraw Deposit maturity instruction end-to-end, and card-style the Transfer to Savings picker to match the existing linked-account UI. Adds a printable FD/RD deposit certificate from the account view page, following the existing letter-mgmt certificate pattern. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
118 lines
4.9 KiB
TypeScript
118 lines
4.9 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, 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 { 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
|
|
]
|
|
})
|
|
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 -- Withdraw Deposit (100) needs no target account, and both
|
|
// Reinvest options (300/400) open a brand-new FD instead, so they ignore this field too.
|
|
if (value === 200) {
|
|
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 ?? ''
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/** The linked-savings account for the transfer-to-savings 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|