- 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>
119 lines
4.0 KiB
TypeScript
119 lines
4.0 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/.
|
|
*/
|
|
|
|
import { Component, Input, OnChanges, SimpleChanges, ViewChild } from '@angular/core';
|
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|
import { NomineeFormComponent } from 'app/shared/nominee-form/nominee-form.component';
|
|
import { DormancyAlertPanelComponent } from 'app/shared/dormancy-alert-panel/dormancy-alert-panel.component';
|
|
import { MatSlideToggle } from '@angular/material/slide-toggle';
|
|
import { FormsModule } from '@angular/forms';
|
|
|
|
@Component({
|
|
selector: 'mifosx-recurring-deposits-account-currency-step',
|
|
templateUrl: './recurring-deposits-account-currency-step.component.html',
|
|
styleUrls: ['./recurring-deposits-account-currency-step.component.scss'],
|
|
imports: [
|
|
...STANDALONE_SHARED_IMPORTS,
|
|
NomineeFormComponent,
|
|
DormancyAlertPanelComponent,
|
|
MatSlideToggle,
|
|
FormsModule
|
|
]
|
|
})
|
|
export class RecurringDepositsAccountCurrencyStepComponent implements OnChanges {
|
|
@Input() recurringDepositsAccountProductTemplate: any;
|
|
@Input() primaryClientId: number | null = null;
|
|
@Input() existingAccount: any = null;
|
|
@ViewChild(NomineeFormComponent) nomineeFormComp: NomineeFormComponent;
|
|
|
|
nominees: any[] = [];
|
|
pendingNominee: any = null;
|
|
showNomineeForm = false;
|
|
nomineeError = '';
|
|
|
|
autoDebitFacility = false;
|
|
|
|
private existingAccountApplied = false;
|
|
|
|
ngOnChanges(changes: SimpleChanges): void {
|
|
if (changes['existingAccount']?.currentValue) {
|
|
const ea = changes['existingAccount'].currentValue;
|
|
this.existingAccountApplied = true;
|
|
if (ea.autoDebitFacility !== undefined) {
|
|
this.autoDebitFacility = !!ea.autoDebitFacility;
|
|
}
|
|
if (Array.isArray(ea.nominees) && ea.nominees.length > 0) {
|
|
this.nominees = [...ea.nominees];
|
|
}
|
|
}
|
|
if (changes['recurringDepositsAccountProductTemplate']?.currentValue && !this.existingAccountApplied) {
|
|
const pt = changes['recurringDepositsAccountProductTemplate'].currentValue;
|
|
this.autoDebitFacility = !!pt.autoDebitFacility;
|
|
}
|
|
}
|
|
|
|
get totalShare(): number {
|
|
return this.nominees.reduce((sum, n) => sum + (+n.percentageShare || 0), 0);
|
|
}
|
|
|
|
get remainingShare(): number {
|
|
return 100 - this.totalShare;
|
|
}
|
|
|
|
get canAddMore(): boolean {
|
|
return this.totalShare < 100;
|
|
}
|
|
|
|
onNomineeChange(event: any): void {
|
|
this.pendingNominee = event;
|
|
this.nomineeError = '';
|
|
}
|
|
|
|
addNominee(): void {
|
|
const data = this.nomineeFormComp?.currentNomineeData ?? this.pendingNominee;
|
|
if (!data?.nomineeClientId) {
|
|
this.nomineeError = 'Please select a nominee before adding.';
|
|
return;
|
|
}
|
|
if (this.nominees.some((n) => n.nomineeClientId === data.nomineeClientId)) {
|
|
this.nomineeError = 'This person is already added as a nominee.';
|
|
return;
|
|
}
|
|
const relationshipLower = data.relationshipName?.toLowerCase();
|
|
if (relationshipLower === 'father' || relationshipLower === 'mother' || relationshipLower === 'wife') {
|
|
if (this.nominees.some((n) => n.relationshipName?.toLowerCase() === relationshipLower)) {
|
|
this.nomineeError = `Only one ${data.relationshipName} can be added as a nominee.`;
|
|
return;
|
|
}
|
|
}
|
|
if (!data.percentageShare || data.percentageShare < 1) {
|
|
this.nomineeError = 'Share must be at least 1%.';
|
|
return;
|
|
}
|
|
const newTotal = this.totalShare + (+data.percentageShare || 0);
|
|
if (newTotal > 100) {
|
|
this.nomineeError = `Total share cannot exceed 100%. Remaining: ${this.remainingShare}%`;
|
|
return;
|
|
}
|
|
this.nominees.push({ ...data });
|
|
this.pendingNominee = null;
|
|
this.showNomineeForm = false;
|
|
this.nomineeError = '';
|
|
}
|
|
|
|
removeNominee(index: number): void {
|
|
this.nominees.splice(index, 1);
|
|
}
|
|
|
|
get currencyStepData() {
|
|
return {
|
|
autoDebitFacility: this.autoDebitFacility
|
|
};
|
|
}
|
|
}
|