feat: recurring deposit enhancements, dashboard, chart, and account view updates

This commit is contained in:
2026-10-12 10:23:10 +05:30
parent 6b0f218b70
commit 2d2eac8d03
69 changed files with 3619 additions and 595 deletions
@@ -70,8 +70,9 @@ export class ActivateRecurringDepositsAccountComponent implements OnInit {
*/
createActivateRecurringDepositsAccountForm() {
this.activateRecurringDepositsAccountForm = this.formBuilder.group({
// Defaults to the business date (the branch's working day), which is what the account is normally activated on.
activatedOnDate: [
'',
this.settingsService.businessDate,
Validators.required
]
});
@@ -72,8 +72,9 @@ export class ApproveRecurringDepositsAccountComponent implements OnInit {
*/
createApproveRecurringDepositsAccountForm() {
this.approveRecurringDepositsAccountForm = this.formBuilder.group({
// Defaults to the business date (the branch's working day), which is what the account is normally approved on.
approvedOnDate: [
'',
this.settingsService.businessDate,
Validators.required
],
note: ['']
@@ -13,6 +13,16 @@
<div class="layout-column">
<h3 class="mat-h3 flex-fill">{{ title }}</h3>
@if (totalLienAmount > 0) {
<p class="lien-warn">
This deposit is pledged as loan security (lien {{ totalLienAmount | number: '1.2-2' }} -
@for (lien of liens; track lien.loanId; let last = $last) {
loan {{ lien.loanAccountNo }}{{ last ? '' : ', ' }}
}
). It cannot be closed until the loan is repaid or the guarantee is released.
</p>
}
<mat-form-field (click)="closedOnDatePicker.open()">
<mat-label>{{ 'labels.inputs.Closed On' | translate }}</mat-label>
<input
@@ -154,7 +164,7 @@
<button type="button" mat-raised-button [routerLink]="['../general']">
{{ 'labels.buttons.Cancel' | translate }}
</button>
<button mat-raised-button color="primary" [disabled]="!closeRecurringDepositForm.valid">
<button mat-raised-button color="primary" [disabled]="!closeRecurringDepositForm.valid || totalLienAmount > 0">
{{ 'labels.buttons.Submit' | translate }}
</button>
</mat-card-actions>
@@ -10,6 +10,12 @@
max-width: 37rem;
}
.lien-warn {
margin: 0 0 16px;
font-size: 13px;
color: #c62828;
}
.expandcollapsebutton {
margin-top: -7px;
margin-left: 2%;
@@ -64,6 +64,9 @@ export class CloseRecurringDepositsAccountComponent implements OnInit {
maxDate = new Date();
/** Close Recurring Deposit Account form. */
closeRecurringDepositForm: UntypedFormGroup;
/** Loans this deposit secures. While any remain, the server refuses to close it. */
liens: any[] = [];
totalLienAmount = 0;
/**
* Retrieves action details template data from `resolve`
@@ -79,6 +82,11 @@ export class CloseRecurringDepositsAccountComponent implements OnInit {
this.maturityAmount = data.recurringDepositsAccountActionData.maturityAmount;
this.onAccountClosureOptions = data.recurringDepositsAccountActionData.onAccountClosureOptions;
this.paymentTypes = data.recurringDepositsAccountActionData.paymentTypeOptions;
// A matured deposit closes on or after its maturity date - the server refuses anything earlier.
const maturityDate = data.recurringDepositsAccountActionData.maturityDate;
if (Array.isArray(maturityDate) && maturityDate.length >= 3) {
this.minDate = new Date(maturityDate[0], maturityDate[1] - 1, maturityDate[2]);
}
if (data.recurringDepositsAccountActionData.maturityAmount) {
this.title = 'Recurring Deposit Closure on Maturity';
} else {
@@ -92,6 +100,10 @@ export class CloseRecurringDepositsAccountComponent implements OnInit {
this.maxDate = this.settingsService.businessDate;
this.createcloseRecurringDepositForm();
this.addTransferDetails();
this.recurringDepositsService.getRecurringDepositLiens(this.accountId).subscribe((response: any) => {
this.liens = response?.liens ?? [];
this.totalLienAmount = Number(response?.totalLienAmount) || 0;
});
}
/**
@@ -99,8 +111,10 @@ export class CloseRecurringDepositsAccountComponent implements OnInit {
*/
createcloseRecurringDepositForm() {
this.closeRecurringDepositForm = this.formBuilder.group({
// The business date, not the computer's clock: the branch's working day can differ from the calendar day,
// and closing on the clock's date (before maturity, or before the deposit's last installment) fails.
closedOnDate: [
new Date(),
this.settingsService.businessDate,
Validators.required
],
maturityAmount: [{ value: this.maturityAmount, disabled: true }],
@@ -11,6 +11,51 @@
<form [formGroup]="prematureCloseRecurringDepositsAccountForm" (ngSubmit)="submit()">
<mat-card-content>
<div class="layout-column">
@if (account) {
<div class="pc-context">
<div class="pc-row">
<span>Account</span><b>{{ account.accountNo }}</b>
</div>
<div class="pc-row">
<span>Current balance</span><b>{{ currencySymbol }} {{ balance | number: '1.2-2' }}</b>
</div>
@if (account.maturityDate) {
<div class="pc-row">
<span>Maturity date</span><b>{{ account.maturityDate | dateFormat }}</b>
</div>
}
@if (account.maturityAmount != null) {
<div class="pc-row">
<span>Amount at maturity</span><b>{{ currencySymbol }} {{ account.maturityAmount | number: '1.2-2' }}</b>
</div>
}
<div class="pc-row">
<span>Pre-closure penalty</span>
<b>
@if (account.preClosurePenalApplicable) {
{{ account.preClosurePenalInterest }}% less interest
@if (account.preClosurePenalInterestOnType?.value) {
({{ account.preClosurePenalInterestOnType.value }})
}
} @else {
None
}
</b>
</div>
</div>
}
@if (totalLienAmount > 0) {
<p class="pc-warn">
This deposit is pledged as loan security (lien {{ currencySymbol }}
{{ totalLienAmount | number: '1.2-2' }} -
@for (lien of liens; track lien.loanId; let last = $last) {
loan {{ lien.loanAccountNo }}{{ last ? '' : ', ' }}
}
). It cannot be closed until the loan is repaid or the guarantee is released.
</p>
}
<mat-form-field (click)="closedOnDatePicker.open()">
<mat-label>{{ 'labels.inputs.Closed On' | translate }}</mat-label>
<input
@@ -23,6 +68,7 @@
/>
<mat-datepicker-toggle matSuffix [for]="closedOnDatePicker"></mat-datepicker-toggle>
<mat-datepicker #closedOnDatePicker></mat-datepicker>
<mat-hint>Must be before the maturity date - on or after it, use Close instead.</mat-hint>
@if (prematureCloseRecurringDepositsAccountForm.controls.closedOnDate.hasError('required')) {
<mat-error>
{{ 'labels.inputs.Closed On Date' | translate }} {{ 'labels.commons.is' | translate }}
@@ -30,6 +76,89 @@
</mat-error>
}
</mat-form-field>
@if (reasonOptions.length > 1) {
<mat-form-field subscriptSizing="dynamic">
<mat-label>Closure reason</mat-label>
<mat-select formControlName="prematureClosureReasonId">
@for (reason of reasonOptions; track reason.id) {
<mat-option [value]="reason.id">{{ reason.label }}</mat-option>
}
</mat-select>
<mat-hint>{{ selectedReasonHint }}</mat-hint>
</mat-form-field>
}
<div class="pc-calc">
<button
type="button"
mat-stroked-button
color="primary"
(click)="calculate()"
[disabled]="
calculating || totalLienAmount > 0 || !prematureCloseRecurringDepositsAccountForm.value.closedOnDate
"
>
{{ calculating ? 'Calculating…' : 'Calculate Payout' }}
</button>
</div>
@if (preview) {
<div class="pc-result">
<div class="pc-row pc-row--total">
<span>Payable on premature closure</span>
<b>{{ currencySymbol }} {{ preview.payoutAmount | number: '1.2-2' }}</b>
</div>
@if (shortfallVsMaturity != null && shortfallVsMaturity > 0) {
<div class="pc-row pc-row--muted">
<span>Less than at maturity by</span>
<b>{{ currencySymbol }} {{ shortfallVsMaturity | number: '1.2-2' }}</b>
</div>
}
<p class="pc-note">
Paid by transfer to the client's savings account - a recurring deposit cannot be broken for cash.
</p>
</div>
@if (destinationAccounts.length === 0) {
<p class="pc-warn">
This client has no active savings account to receive the proceeds. Open one first, then close this
deposit.
</p>
} @else {
<mat-form-field>
<mat-label>{{ 'labels.inputs.Transfer to Savings' | translate }}</mat-label>
<mat-select required formControlName="toSavingsAccountId">
@for (savings of destinationAccounts; track savings.id) {
<mat-option [value]="savings.id">
{{ savings.accountNo }} - {{ savings.productName }}
@if (savings.owner) {
(nominee: {{ savings.owner }})
} @else if (savings.id === account?.linkedAccountId) {
(linked)
}
</mat-option>
}
</mat-select>
@if (prematureCloseRecurringDepositsAccountForm.controls.toSavingsAccountId.hasError('required')) {
<mat-error>
{{ 'labels.inputs.Transfer to savings account' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong>
</mat-error>
}
</mat-form-field>
<mat-form-field>
<mat-label>{{ 'labels.inputs.Transfer Description' | translate }}</mat-label>
<input matInput formControlName="transferDescription" />
</mat-form-field>
<mat-form-field>
<mat-label>{{ 'labels.inputs.Note' | translate }}</mat-label>
<textarea matInput cdkTextareaAutosize formControlName="note"></textarea>
</mat-form-field>
}
}
</div>
</mat-card-content>
@@ -37,8 +166,8 @@
<button type="button" mat-raised-button [routerLink]="['../../']">
{{ 'labels.buttons.Cancel' | translate }}
</button>
<button mat-raised-button color="primary" [disabled]="!prematureCloseRecurringDepositsAccountForm">
{{ 'labels.buttons.Confirm' | translate }}
<button mat-raised-button color="primary" [disabled]="!canConfirm">
{{ submitting ? 'Closing…' : ('labels.buttons.Confirm' | translate) }}
</button>
</mat-card-actions>
</form>
@@ -9,3 +9,45 @@
.container {
max-width: 37rem;
}
.pc-context,
.pc-result {
display: flex;
flex-direction: column;
gap: 6px;
padding: 12px 14px;
margin-bottom: 16px;
border: 1px solid rgb(128 128 128 / 30%);
border-radius: 6px;
}
.pc-row {
display: flex;
justify-content: space-between;
gap: 12px;
font-size: 13px;
&--total {
font-size: 15px;
}
&--muted {
opacity: 0.75;
}
}
.pc-calc {
margin: 4px 0 16px;
}
.pc-note {
margin: 4px 0 0;
font-size: 12px;
opacity: 0.75;
}
.pc-warn {
margin: 0 0 16px;
font-size: 13px;
color: #c62828;
}
@@ -8,25 +8,41 @@
/** Angular Imports */
import { Component, OnInit, inject } from '@angular/core';
import { UntypedFormGroup, UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { UntypedFormGroup, UntypedFormBuilder, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { CdkTextareaAutosize } from '@angular/cdk/text-field';
/** Custom Services */
import { RecurringDepositsService } from '../../recurring-deposits.service';
import { ClientsService } from 'app/clients/clients.service';
import { SettingsService } from 'app/settings/settings.service';
import { Dates } from 'app/core/utils/dates';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
/** DepositAccountOnClosureType.TRANSFER_TO_SAVINGS - the only way a broken RD may be paid out. */
const TRANSFER_TO_SAVINGS = 200;
/** PrematureClosureReasonType values the RD closure understands. */
const REASON_NORMAL = 100;
const REASON_DECEASED = 400;
const REASON_NOMINEE_HEIR = 500;
/**
* Premature Close Recurring Deposits Account Component
*
* The server will only break a recurring deposit early by transferring the proceeds to one of the client's savings
* accounts (no cash payout, no reinvest), and it needs to be told so: onAccountClosureId and toSavingsAccountId are
* mandatory. This screen used to send only the closure date, so every premature closure was rejected. It now asks the
* server what the deposit would pay on the chosen date (calculatePrematureAmount, which applies the product's
* pre-closure penalty) and only lets the closure be confirmed against that exact figure.
*/
@Component({
selector: 'mifosx-premature-close-recurring-deposit-account',
templateUrl: './premature-close-recurring-deposit-account.component.html',
styleUrls: ['./premature-close-recurring-deposit-account.component.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS
...STANDALONE_SHARED_IMPORTS,
CdkTextareaAutosize
]
})
export class PrematureCloseRecurringDepositAccountComponent implements OnInit {
@@ -36,72 +52,263 @@ export class PrematureCloseRecurringDepositAccountComponent implements OnInit {
private route = inject(ActivatedRoute);
private router = inject(Router);
private settingsService = inject(SettingsService);
private clientsService = inject(ClientsService);
/** Minimum date allowed. */
/** Savings accounts of this deposit's nominees - offered as destinations only for a deceased holder. */
nomineeSavingsAccounts: any[] = [];
/** Minimum date allowed: the deposit's activation date once loaded. */
minDate = new Date(2000, 0, 1);
/** Maximum date allowed. */
/** Maximum date allowed: the business date, and never the maturity date or later (that is a normal Close). */
maxDate = new Date();
/** premature close Recurring Deposits Account form. */
prematureCloseRecurringDepositsAccountForm: UntypedFormGroup;
/** Recurring Deposits Account Id */
accountId: any;
/** The deposit being closed, for the context panel and the default destination account. */
account: any;
/** Result of the last calculatePrematureAmount call, and the date it was calculated for. */
preview: { payoutAmount: number; savingsAccounts: any[]; closureOptionId: number } | null = null;
previewedFor: string | null = null;
calculating = false;
submitting = false;
/** Loans this deposit secures. While any remain, the server refuses to close it. */
liens: any[] = [];
totalLienAmount = 0;
/**
* @param {FormBuilder} formBuilder Form Builder
* @param {RecurringDepositsService} recurringDepositsService Recurring Deposits Service
* @param {Dates} dateUtils Date Utils
* @param {ActivatedRoute} route Activated Route
* @param {Router} router Router
* @param {SettingsService} settingsService Settings Service
*/
constructor() {
this.accountId = this.route.parent.snapshot.params['recurringDepositAccountId'];
}
/**
* Creates the premature close recurring deposits form.
*/
ngOnInit() {
this.maxDate = this.settingsService.businessDate;
this.createprematureCloseRecurringDepositsAccountForm();
}
/**
* Creates the premature close recurring deposits account form.
*/
createprematureCloseRecurringDepositsAccountForm() {
this.prematureCloseRecurringDepositsAccountForm = this.formBuilder.group({
closedOnDate: [
'',
Validators.required
]
this.recurringDepositsService.getRecurringDepositsAccountData(this.accountId).subscribe((account: any) => {
this.account = account;
const activatedOn = this.toDate(account?.timeline?.activatedOnDate);
if (activatedOn) {
this.minDate = activatedOn;
}
const maturity = this.toDate(account?.maturityDate);
if (maturity) {
const dayBeforeMaturity = new Date(maturity);
dayBeforeMaturity.setDate(dayBeforeMaturity.getDate() - 1);
if (dayBeforeMaturity < this.maxDate) {
this.maxDate = dayBeforeMaturity;
}
}
});
this.recurringDepositsService.getRecurringDepositLiens(this.accountId).subscribe((response: any) => {
this.liens = response?.liens ?? [];
this.totalLienAmount = Number(response?.totalLienAmount) || 0;
});
// A different date or reason is a different payout - the old preview no longer describes what would happen.
this.prematureCloseRecurringDepositsAccountForm.get('closedOnDate').valueChanges.subscribe(() => {
this.preview = null;
this.previewedFor = null;
});
this.prematureCloseRecurringDepositsAccountForm.get('prematureClosureReasonId').valueChanges.subscribe((reason: number) => {
this.preview = null;
this.previewedFor = null;
if (reason === REASON_DECEASED) {
this.loadNomineeSavingsAccounts();
}
});
}
/**
* Submits the form and premature closes the recurring deposit account,
* if successful redirects to the recurring deposit account.
*/
submit() {
const prematureCloseRecurringDepositsAccountFormData = this.prematureCloseRecurringDepositsAccountForm.value;
const locale = this.settingsService.language.code;
const dateFormat = this.settingsService.dateFormat;
const prevClosedOnDate: Date = this.prematureCloseRecurringDepositsAccountForm.value.closedOnDate;
if (prematureCloseRecurringDepositsAccountFormData.closedOnDate instanceof Date) {
prematureCloseRecurringDepositsAccountFormData.closedOnDate = this.dateUtils.formatDate(
prevClosedOnDate,
dateFormat
);
/** Reasons this deposit's product allows; Normal is always there. */
get reasonOptions(): Array<{ id: number; label: string; hint: string }> {
const options = [{ id: REASON_NORMAL, label: 'Normal', hint: "Pre-closure penalty as per the deposit's terms." }];
if (this.account?.noPenaltyNomineeHeirClosure) {
options.push({ id: REASON_NOMINEE_HEIR, label: 'Nominee / legal heir claim', hint: 'No pre-closure penalty.' });
}
const data = {
...prematureCloseRecurringDepositsAccountFormData,
dateFormat,
locale
};
this.recurringDepositsService
.executeRecurringDepositsAccountCommand(this.accountId, 'prematureClose', data)
.subscribe(() => {
this.router.navigate(['../../'], { relativeTo: this.route });
if (this.account?.deceasedSettlement) {
options.push({
id: REASON_DECEASED,
label: 'Account holder deceased',
hint: "No penalty, allowed inside lock-in; may be paid to a nominee's savings account."
});
}
return options;
}
get selectedReasonHint(): string {
const reason = this.prematureCloseRecurringDepositsAccountForm.value.prematureClosureReasonId;
return this.reasonOptions.find((option) => option.id === reason)?.hint ?? '';
}
/** The client's own savings accounts, plus - for a deceased holder - the nominees' savings accounts. */
get destinationAccounts(): any[] {
const own = (this.preview?.savingsAccounts ?? []).map((acc: any) => ({ ...acc, owner: null }));
if (this.prematureCloseRecurringDepositsAccountForm.value.prematureClosureReasonId !== REASON_DECEASED) {
return own;
}
const ownIds = new Set(own.map((acc: any) => acc.id));
return [
...own,
...this.nomineeSavingsAccounts.filter((acc: any) => !ownIds.has(acc.id))
];
}
private loadNomineeSavingsAccounts() {
this.nomineeSavingsAccounts = [];
const clientId = this.account?.clientId;
if (!clientId || !this.account?.accountNo) {
return;
}
this.clientsService.getClientNominees(clientId, this.account.accountNo, 'RD').subscribe((nominees: any) => {
const list: any[] = Array.isArray(nominees) ? nominees : (nominees?.pageItems ?? []);
list
.filter((nominee: any) => nominee?.nomineeClientId && nominee.isActive !== false)
.forEach((nominee: any) => {
this.clientsService.getClientAccountData(nominee.nomineeClientId).subscribe((accounts: any) => {
const savings = (accounts?.savingsAccounts ?? []).filter(
(acc: any) => acc?.depositType?.id === 100 && acc?.status?.active
);
this.nomineeSavingsAccounts = [
...this.nomineeSavingsAccounts,
...savings.map((acc: any) => ({
id: acc.id,
accountNo: acc.accountNo,
productName: acc.productName,
owner: nominee.nomineeDisplayName
}))
];
});
});
});
}
createprematureCloseRecurringDepositsAccountForm() {
this.prematureCloseRecurringDepositsAccountForm = this.formBuilder.group({
closedOnDate: [
this.settingsService.businessDate,
Validators.required
],
prematureClosureReasonId: [REASON_NORMAL],
toSavingsAccountId: [
null,
Validators.required
],
transferDescription: ['Premature closure of recurring deposit'],
note: ['']
});
}
get balance(): number | null {
return this.account?.summary?.accountBalance ?? null;
}
get currencySymbol(): string {
return this.account?.currency?.displaySymbol || this.account?.currency?.code || '';
}
/** What breaking early costs compared with letting the deposit run to maturity, when both are known. */
get shortfallVsMaturity(): number | null {
const maturityAmount = this.account?.maturityAmount;
if (!this.preview || maturityAmount == null) {
return null;
}
return Number(maturityAmount) - Number(this.preview.payoutAmount);
}
get canConfirm(): boolean {
return (
this.totalLienAmount <= 0 &&
!!this.preview &&
this.previewedFor === this.formattedClosedOnDate &&
this.prematureCloseRecurringDepositsAccountForm.valid &&
!this.submitting
);
}
private get formattedClosedOnDate(): string | null {
const value = this.prematureCloseRecurringDepositsAccountForm.value.closedOnDate;
if (!value) {
return null;
}
return value instanceof Date ? this.dateUtils.formatDate(value, this.settingsService.dateFormat) : value;
}
/** Asks the server what the deposit pays if broken on the chosen date. */
calculate() {
const closedOnDate = this.formattedClosedOnDate;
if (!closedOnDate) {
return;
}
this.calculating = true;
const data = {
closedOnDate,
prematureClosureReasonId: this.prematureCloseRecurringDepositsAccountForm.value.prematureClosureReasonId,
dateFormat: this.settingsService.dateFormat,
locale: this.settingsService.language.code
};
this.recurringDepositsService.executeRecurringDepositsAccountCommand(this.accountId, 'calculatePrematureAmount', data).subscribe({
next: (response: any) => {
const savingsAccounts = response?.savingsAccounts ?? [];
const closureOptionId =
(response?.onAccountClosureOptions ?? []).find((option: any) => option.id === TRANSFER_TO_SAVINGS)?.id ??
TRANSFER_TO_SAVINGS;
this.preview = { payoutAmount: response?.maturityAmount, savingsAccounts, closureOptionId };
this.previewedFor = closedOnDate;
this.calculating = false;
this.selectDefaultDestination(savingsAccounts);
},
error: () => {
this.calculating = false;
}
});
}
/** The deposit's own linked savings account, when it has one the client can still receive into. */
private selectDefaultDestination(savingsAccounts: any[]) {
const control = this.prematureCloseRecurringDepositsAccountForm.get('toSavingsAccountId');
if (control.value && savingsAccounts.some((acc: any) => acc.id === control.value)) {
return;
}
const linkedId = this.account?.linkedAccountId;
const linked = savingsAccounts.find((acc: any) => acc.id === linkedId);
control.setValue(linked?.id ?? (savingsAccounts.length === 1 ? savingsAccounts[0].id : null));
}
submit() {
if (!this.canConfirm) {
return;
}
const formValue = this.prematureCloseRecurringDepositsAccountForm.value;
const data: any = {
closedOnDate: this.formattedClosedOnDate,
onAccountClosureId: this.preview.closureOptionId,
prematureClosureReasonId: formValue.prematureClosureReasonId,
toSavingsAccountId: formValue.toSavingsAccountId,
transferDescription: formValue.transferDescription,
dateFormat: this.settingsService.dateFormat,
locale: this.settingsService.language.code
};
if (formValue.note) {
data.note = formValue.note;
}
this.submitting = true;
this.recurringDepositsService.executeRecurringDepositsAccountCommand(this.accountId, 'prematureClose', data).subscribe({
next: () => {
this.router.navigate(['../../'], { relativeTo: this.route });
},
error: () => {
this.submitting = false;
}
});
}
/** API dates arrive as [yyyy, m, d] arrays or ISO strings. */
private toDate(value: any): Date | null {
if (!value) {
return null;
}
if (Array.isArray(value)) {
return new Date(value[0], value[1] - 1, value[2]);
}
const parsed = new Date(value);
return isNaN(parsed.getTime()) ? null : parsed;
}
}
@@ -30,6 +30,14 @@
@if (actions['Close']) {
<mifosx-close-recurring-deposits-account></mifosx-close-recurring-deposits-account>
}
@if (actions['Deposit'] || actions['Withdrawal']) {
<!-- A direct deposit from the account screen skips the manager's approval; RD installments come in through the
cashier's Receipt or Auto Debit instead, so the Deposit action is not offered here even by URL. -->
@if (actions['Deposit']) {
<p>
RD installments cannot be deposited from the account screen. Use a cashier Receipt, or Auto Debit from the linked
savings account.
</p>
}
@if (actions['Withdrawal']) {
<mifosx-deposit-recurring-deposits-account [currency]="currency"></mifosx-deposit-recurring-deposits-account>
}