pending account terminate feature develop
This commit is contained in:
@@ -89,4 +89,8 @@ export class AccountsService {
|
|||||||
if (params.asOnDate) httpParams = httpParams.set('asOnDate', params.asOnDate);
|
if (params.asOnDate) httpParams = httpParams.set('asOnDate', params.asOnDate);
|
||||||
return this.http.get('/recurringdepositaccounts', { params: httpParams });
|
return this.http.get('/recurringdepositaccounts', { params: httpParams });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
terminateAccount(accountId: string, data: any): Observable<any> {
|
||||||
|
return this.http.post(`/v1/accounts/${accountId}/terminate`, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ import { ClientsService } from 'app/clients/clients.service';
|
|||||||
import { ConfirmationDialogComponent } from 'app/shared/confirmation-dialog/confirmation-dialog.component';
|
import { ConfirmationDialogComponent } from 'app/shared/confirmation-dialog/confirmation-dialog.component';
|
||||||
import { Currency } from 'app/shared/models/general.model';
|
import { Currency } from 'app/shared/models/general.model';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { AccountsService } from 'app/accounts/accounts.service';
|
||||||
|
import { SettingsService } from 'app/settings/settings.service';
|
||||||
|
import { Dates } from 'app/core/utils/dates';
|
||||||
|
|
||||||
/** Environment Configuration */
|
/** Environment Configuration */
|
||||||
import { environment } from '../../../environments/environment';
|
import { environment } from '../../../environments/environment';
|
||||||
@@ -83,6 +86,9 @@ export class CurrentAccountViewComponent implements OnInit {
|
|||||||
private currentAccountsService = inject(CurrentAccountsService);
|
private currentAccountsService = inject(CurrentAccountsService);
|
||||||
private clientsService = inject(ClientsService);
|
private clientsService = inject(ClientsService);
|
||||||
private translateService = inject(TranslateService);
|
private translateService = inject(TranslateService);
|
||||||
|
private accountsService = inject(AccountsService);
|
||||||
|
private settingsService = inject(SettingsService);
|
||||||
|
private dateUtils = inject(Dates);
|
||||||
dialog = inject(MatDialog);
|
dialog = inject(MatDialog);
|
||||||
|
|
||||||
/** Current Account Data */
|
/** Current Account Data */
|
||||||
@@ -126,7 +132,8 @@ export class CurrentAccountViewComponent implements OnInit {
|
|||||||
Close: 'danger',
|
Close: 'danger',
|
||||||
Reject: 'danger',
|
Reject: 'danger',
|
||||||
Delete: 'danger',
|
Delete: 'danger',
|
||||||
'Withdrawn by Client': 'danger'
|
'Withdrawn by Client': 'danger',
|
||||||
|
Terminate: 'danger'
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly ACTION_ICON: Record<string, string> = {
|
private readonly ACTION_ICON: Record<string, string> = {
|
||||||
@@ -157,7 +164,8 @@ export class CurrentAccountViewComponent implements OnInit {
|
|||||||
Close: 'times-circle',
|
Close: 'times-circle',
|
||||||
Reject: 'ban',
|
Reject: 'ban',
|
||||||
Delete: 'trash',
|
Delete: 'trash',
|
||||||
'Withdrawn by Client': 'user-slash'
|
'Withdrawn by Client': 'user-slash',
|
||||||
|
Terminate: 'times-circle'
|
||||||
};
|
};
|
||||||
|
|
||||||
kindOf(name: string): 'transact' | 'block' | 'interest' | 'admin' | 'danger' {
|
kindOf(name: string): 'transact' | 'block' | 'interest' | 'admin' | 'danger' {
|
||||||
@@ -309,6 +317,9 @@ export class CurrentAccountViewComponent implements OnInit {
|
|||||||
case 'Delete':
|
case 'Delete':
|
||||||
this.deleteCurrentAccount();
|
this.deleteCurrentAccount();
|
||||||
break;
|
break;
|
||||||
|
case 'Terminate':
|
||||||
|
this.terminateCurrentAccount();
|
||||||
|
break;
|
||||||
case 'Calculate Interest':
|
case 'Calculate Interest':
|
||||||
this.calculateInterest();
|
this.calculateInterest();
|
||||||
break;
|
break;
|
||||||
@@ -370,6 +381,32 @@ export class CurrentAccountViewComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private terminateCurrentAccount() {
|
||||||
|
const terminateCurrentAccountDialogRef = this.dialog.open(ConfirmationDialogComponent, {
|
||||||
|
data: {
|
||||||
|
heading: 'Terminate Account',
|
||||||
|
dialogContext: 'Are you sure you want to terminate this account?',
|
||||||
|
type: 'Strong'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
terminateCurrentAccountDialogRef.afterClosed().subscribe((response: any) => {
|
||||||
|
if (response && response.confirm) {
|
||||||
|
const locale = this.settingsService.language.code;
|
||||||
|
const dateFormat = this.settingsService.dateFormat;
|
||||||
|
const terminatedOnDate = this.dateUtils.formatDate(new Date(), dateFormat);
|
||||||
|
const data = {
|
||||||
|
accountType: 'CURRENT',
|
||||||
|
terminatedOnDate,
|
||||||
|
dateFormat,
|
||||||
|
locale
|
||||||
|
};
|
||||||
|
this.accountsService.terminateAccount(this.currentAccountData.id, data).subscribe(() => {
|
||||||
|
this.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates current account's interest
|
* Calculates current account's interest
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -194,6 +194,10 @@ export class CurrentAccountButtonsConfiguration {
|
|||||||
{
|
{
|
||||||
name: 'Delete',
|
name: 'Delete',
|
||||||
taskPermissionName: 'DELETE_CURRENTACCOUNT'
|
taskPermissionName: 'DELETE_CURRENTACCOUNT'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Terminate',
|
||||||
|
taskPermissionName: 'DELETE_CURRENTACCOUNT'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
|
|||||||
+40
-3
@@ -11,8 +11,8 @@ import { Component, OnInit, inject } from '@angular/core';
|
|||||||
import { ActivatedRoute, Router, RouterLinkActive, RouterLink, RouterOutlet } from '@angular/router';
|
import { ActivatedRoute, Router, RouterLinkActive, RouterLink, RouterOutlet } from '@angular/router';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
|
||||||
/** Custom Dialogs */
|
|
||||||
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
|
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
|
||||||
|
import { ConfirmationDialogComponent } from 'app/shared/confirmation-dialog/confirmation-dialog.component';
|
||||||
import { CalculateInterestDialogComponent } from './custom-dialogs/calculate-interest-dialog/calculate-interest-dialog.component';
|
import { CalculateInterestDialogComponent } from './custom-dialogs/calculate-interest-dialog/calculate-interest-dialog.component';
|
||||||
import { PostInterestDialogComponent } from './custom-dialogs/post-interest-dialog/post-interest-dialog.component';
|
import { PostInterestDialogComponent } from './custom-dialogs/post-interest-dialog/post-interest-dialog.component';
|
||||||
import { ToggleWithholdTaxDialogComponent } from './custom-dialogs/toggle-withhold-tax-dialog/toggle-withhold-tax-dialog.component';
|
import { ToggleWithholdTaxDialogComponent } from './custom-dialogs/toggle-withhold-tax-dialog/toggle-withhold-tax-dialog.component';
|
||||||
@@ -23,7 +23,10 @@ import { FixedDepositsButtonsConfiguration } from './fixed-deposits-buttons.conf
|
|||||||
/** Custom Services */
|
/** Custom Services */
|
||||||
import { FixedDepositsService } from '../fixed-deposits.service';
|
import { FixedDepositsService } from '../fixed-deposits.service';
|
||||||
import { SavingsService } from 'app/savings/savings.service';
|
import { SavingsService } from 'app/savings/savings.service';
|
||||||
|
import { AccountsService } from 'app/accounts/accounts.service';
|
||||||
import { Currency } from 'app/shared/models/general.model';
|
import { Currency } from 'app/shared/models/general.model';
|
||||||
|
import { SettingsService } from 'app/settings/settings.service';
|
||||||
|
import { Dates } from 'app/core/utils/dates';
|
||||||
import { MatTooltip } from '@angular/material/tooltip';
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
import { NgClass, CurrencyPipe } from '@angular/common';
|
import { NgClass, CurrencyPipe } from '@angular/common';
|
||||||
import { AccountNumberComponent } from '../../../shared/account-number/account-number.component';
|
import { AccountNumberComponent } from '../../../shared/account-number/account-number.component';
|
||||||
@@ -67,6 +70,9 @@ export class FixedDepositAccountViewComponent implements OnInit {
|
|||||||
private router = inject(Router);
|
private router = inject(Router);
|
||||||
private fixedDepositsService = inject(FixedDepositsService);
|
private fixedDepositsService = inject(FixedDepositsService);
|
||||||
private savingsService = inject(SavingsService);
|
private savingsService = inject(SavingsService);
|
||||||
|
private accountsService = inject(AccountsService);
|
||||||
|
private settingsService = inject(SettingsService);
|
||||||
|
private dateUtils = inject(Dates);
|
||||||
dialog = inject(MatDialog);
|
dialog = inject(MatDialog);
|
||||||
|
|
||||||
/** Fixed Deposits Account Data */
|
/** Fixed Deposits Account Data */
|
||||||
@@ -99,7 +105,8 @@ export class FixedDepositAccountViewComponent implements OnInit {
|
|||||||
'Apply Annual Fees': 'admin',
|
'Apply Annual Fees': 'admin',
|
||||||
Reject: 'danger',
|
Reject: 'danger',
|
||||||
'Withdrawn by Client': 'danger',
|
'Withdrawn by Client': 'danger',
|
||||||
Delete: 'danger'
|
Delete: 'danger',
|
||||||
|
Terminate: 'danger'
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly ACTION_ICON: Record<string, string> = {
|
private readonly ACTION_ICON: Record<string, string> = {
|
||||||
@@ -121,7 +128,8 @@ export class FixedDepositAccountViewComponent implements OnInit {
|
|||||||
'Apply Annual Fees': 'receipt',
|
'Apply Annual Fees': 'receipt',
|
||||||
Reject: 'ban',
|
Reject: 'ban',
|
||||||
'Withdrawn by Client': 'user-slash',
|
'Withdrawn by Client': 'user-slash',
|
||||||
Delete: 'trash'
|
Delete: 'trash',
|
||||||
|
Terminate: 'times-circle'
|
||||||
};
|
};
|
||||||
|
|
||||||
kindOf(name: string): 'transact' | 'block' | 'interest' | 'admin' | 'danger' {
|
kindOf(name: string): 'transact' | 'block' | 'interest' | 'admin' | 'danger' {
|
||||||
@@ -237,6 +245,9 @@ export class FixedDepositAccountViewComponent implements OnInit {
|
|||||||
case 'Disable Withhold Tax':
|
case 'Disable Withhold Tax':
|
||||||
this.disableWithHoldTax();
|
this.disableWithHoldTax();
|
||||||
break;
|
break;
|
||||||
|
case 'Terminate':
|
||||||
|
this.terminateFixedDepositsAccount();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,6 +267,32 @@ export class FixedDepositAccountViewComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private terminateFixedDepositsAccount() {
|
||||||
|
const terminateFixedDepositAccountDialogRef = this.dialog.open(ConfirmationDialogComponent, {
|
||||||
|
data: {
|
||||||
|
heading: 'Terminate Account',
|
||||||
|
dialogContext: 'Are you sure you want to terminate this account?',
|
||||||
|
type: 'Strong'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
terminateFixedDepositAccountDialogRef.afterClosed().subscribe((response: any) => {
|
||||||
|
if (response && response.confirm) {
|
||||||
|
const locale = this.settingsService.language.code;
|
||||||
|
const dateFormat = this.settingsService.dateFormat;
|
||||||
|
const terminatedOnDate = this.dateUtils.formatDate(new Date(), dateFormat);
|
||||||
|
const data = {
|
||||||
|
accountType: 'FD',
|
||||||
|
terminatedOnDate,
|
||||||
|
dateFormat,
|
||||||
|
locale
|
||||||
|
};
|
||||||
|
this.accountsService.terminateAccount(this.fixedDepositsAccountData.id, data).subscribe(() => {
|
||||||
|
this.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates fixed deposit account's interest
|
* Calculates fixed deposit account's interest
|
||||||
*/
|
*/
|
||||||
|
|||||||
+3
@@ -132,6 +132,9 @@ export class FixedDepositsButtonsConfiguration {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Delete'
|
name: 'Delete'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Terminate'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
|
|||||||
+40
-2
@@ -20,9 +20,13 @@ import { RecurringDepositsButtonsConfiguration } from './recurring-deposits-butt
|
|||||||
|
|
||||||
/** Custom Dialogs */
|
/** Custom Dialogs */
|
||||||
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
|
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
|
||||||
|
import { ConfirmationDialogComponent } from 'app/shared/confirmation-dialog/confirmation-dialog.component';
|
||||||
import { RecurringDepositConfirmationDialogComponent } from './custom-dialogs/recurring-deposit-confirmation-dialog/recurring-deposit-confirmation-dialog.component';
|
import { RecurringDepositConfirmationDialogComponent } from './custom-dialogs/recurring-deposit-confirmation-dialog/recurring-deposit-confirmation-dialog.component';
|
||||||
import { Currency } from 'app/shared/models/general.model';
|
import { Currency } from 'app/shared/models/general.model';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { AccountsService } from 'app/accounts/accounts.service';
|
||||||
|
import { SettingsService } from 'app/settings/settings.service';
|
||||||
|
import { Dates } from 'app/core/utils/dates';
|
||||||
import { MatTooltip } from '@angular/material/tooltip';
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
import { NgClass, DecimalPipe, CurrencyPipe } from '@angular/common';
|
import { NgClass, DecimalPipe, CurrencyPipe } from '@angular/common';
|
||||||
import { AccountNumberComponent } from '../../../shared/account-number/account-number.component';
|
import { AccountNumberComponent } from '../../../shared/account-number/account-number.component';
|
||||||
@@ -67,6 +71,9 @@ export class RecurringDepositsAccountViewComponent implements OnInit {
|
|||||||
private router = inject(Router);
|
private router = inject(Router);
|
||||||
private recurringDepositsService = inject(RecurringDepositsService);
|
private recurringDepositsService = inject(RecurringDepositsService);
|
||||||
private savingsService = inject(SavingsService);
|
private savingsService = inject(SavingsService);
|
||||||
|
private accountsService = inject(AccountsService);
|
||||||
|
private settingsService = inject(SettingsService);
|
||||||
|
private dateUtils = inject(Dates);
|
||||||
dialog = inject(MatDialog);
|
dialog = inject(MatDialog);
|
||||||
private translateService = inject(TranslateService);
|
private translateService = inject(TranslateService);
|
||||||
|
|
||||||
@@ -103,7 +110,8 @@ export class RecurringDepositsAccountViewComponent implements OnInit {
|
|||||||
'Apply Annual Fees': 'admin',
|
'Apply Annual Fees': 'admin',
|
||||||
Reject: 'danger',
|
Reject: 'danger',
|
||||||
'Withdrawn by Client': 'danger',
|
'Withdrawn by Client': 'danger',
|
||||||
Delete: 'danger'
|
Delete: 'danger',
|
||||||
|
Terminate: 'danger'
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly ACTION_ICON: Record<string, string> = {
|
private readonly ACTION_ICON: Record<string, string> = {
|
||||||
@@ -125,7 +133,8 @@ export class RecurringDepositsAccountViewComponent implements OnInit {
|
|||||||
'Apply Annual Fees': 'receipt',
|
'Apply Annual Fees': 'receipt',
|
||||||
Reject: 'ban',
|
Reject: 'ban',
|
||||||
'Withdrawn by Client': 'user-slash',
|
'Withdrawn by Client': 'user-slash',
|
||||||
Delete: 'trash'
|
Delete: 'trash',
|
||||||
|
Terminate: 'times-circle'
|
||||||
};
|
};
|
||||||
|
|
||||||
kindOf(name: string): 'transact' | 'block' | 'interest' | 'admin' | 'danger' {
|
kindOf(name: string): 'transact' | 'block' | 'interest' | 'admin' | 'danger' {
|
||||||
@@ -287,6 +296,9 @@ export class RecurringDepositsAccountViewComponent implements OnInit {
|
|||||||
case 'Disable Withhold Tax':
|
case 'Disable Withhold Tax':
|
||||||
this.disableWithHoldTax();
|
this.disableWithHoldTax();
|
||||||
break;
|
break;
|
||||||
|
case 'Terminate':
|
||||||
|
this.terminateRecurringDepositsAccount();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,6 +320,32 @@ export class RecurringDepositsAccountViewComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private terminateRecurringDepositsAccount() {
|
||||||
|
const terminateRecurringDepositAccountDialogRef = this.dialog.open(ConfirmationDialogComponent, {
|
||||||
|
data: {
|
||||||
|
heading: 'Terminate Account',
|
||||||
|
dialogContext: 'Are you sure you want to terminate this account?',
|
||||||
|
type: 'Strong'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
terminateRecurringDepositAccountDialogRef.afterClosed().subscribe((response: any) => {
|
||||||
|
if (response && response.confirm) {
|
||||||
|
const locale = this.settingsService.language.code;
|
||||||
|
const dateFormat = this.settingsService.dateFormat;
|
||||||
|
const terminatedOnDate = this.dateUtils.formatDate(new Date(), dateFormat);
|
||||||
|
const data = {
|
||||||
|
accountType: 'RD',
|
||||||
|
terminatedOnDate,
|
||||||
|
dateFormat,
|
||||||
|
locale
|
||||||
|
};
|
||||||
|
this.accountsService.terminateAccount(this.recurringDepositsAccountData.id, data).subscribe(() => {
|
||||||
|
this.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates savings account's interest
|
* Calculates savings account's interest
|
||||||
*/
|
*/
|
||||||
|
|||||||
+5
@@ -163,6 +163,11 @@ export class RecurringDepositsButtonsConfiguration {
|
|||||||
name: 'Delete',
|
name: 'Delete',
|
||||||
icon: 'trash',
|
icon: 'trash',
|
||||||
taskPermissionName: 'DELETE_RECURRINGDEPOSITACCOUNT'
|
taskPermissionName: 'DELETE_RECURRINGDEPOSITACCOUNT'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Terminate',
|
||||||
|
icon: 'times-circle',
|
||||||
|
taskPermissionName: 'DELETE_RECURRINGDEPOSITACCOUNT'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export class StatusLookupPipe implements PipeTransform {
|
|||||||
'savingsAccountStatusType.activeInactive': 'status-active-overdue',
|
'savingsAccountStatusType.activeInactive': 'status-active-overdue',
|
||||||
'savingsAccountStatusType.activeDormant': 'status-active-overdue',
|
'savingsAccountStatusType.activeDormant': 'status-active-overdue',
|
||||||
'savingsAccountStatusType.matured': 'status-matured',
|
'savingsAccountStatusType.matured': 'status-matured',
|
||||||
|
'savingsAccountStatusType.terminated': 'status-deleted',
|
||||||
'shareAccountStatusType.submitted.and.pending.approval': 'status-pending',
|
'shareAccountStatusType.submitted.and.pending.approval': 'status-pending',
|
||||||
'shareAccountStatusType.approved': 'status-approved',
|
'shareAccountStatusType.approved': 'status-approved',
|
||||||
'shareAccountStatusType.active': 'status-active',
|
'shareAccountStatusType.active': 'status-active',
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ import { SavingsService } from '../savings.service';
|
|||||||
import { ConfirmationDialogComponent } from 'app/shared/confirmation-dialog/confirmation-dialog.component';
|
import { ConfirmationDialogComponent } from 'app/shared/confirmation-dialog/confirmation-dialog.component';
|
||||||
import { Currency } from 'app/shared/models/general.model';
|
import { Currency } from 'app/shared/models/general.model';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { AccountsService } from 'app/accounts/accounts.service';
|
||||||
|
import { SettingsService } from 'app/settings/settings.service';
|
||||||
|
import { Dates } from 'app/core/utils/dates';
|
||||||
|
|
||||||
/** Environment Configuration */
|
/** Environment Configuration */
|
||||||
import { environment } from '../../../environments/environment';
|
import { environment } from '../../../environments/environment';
|
||||||
@@ -75,6 +78,9 @@ export class SavingsAccountViewComponent implements OnInit {
|
|||||||
private router = inject(Router);
|
private router = inject(Router);
|
||||||
private savingsService = inject(SavingsService);
|
private savingsService = inject(SavingsService);
|
||||||
private translateService = inject(TranslateService);
|
private translateService = inject(TranslateService);
|
||||||
|
private accountsService = inject(AccountsService);
|
||||||
|
private settingsService = inject(SettingsService);
|
||||||
|
private dateUtils = inject(Dates);
|
||||||
dialog = inject(MatDialog);
|
dialog = inject(MatDialog);
|
||||||
|
|
||||||
/** Savings Account Data */
|
/** Savings Account Data */
|
||||||
@@ -124,7 +130,8 @@ export class SavingsAccountViewComponent implements OnInit {
|
|||||||
Close: 'danger',
|
Close: 'danger',
|
||||||
Reject: 'danger',
|
Reject: 'danger',
|
||||||
Delete: 'danger',
|
Delete: 'danger',
|
||||||
'Withdrawn by Client': 'danger'
|
'Withdrawn by Client': 'danger',
|
||||||
|
Terminate: 'danger'
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Lightweight icon fallback for actions whose buttonConfig doesn't
|
/** Lightweight icon fallback for actions whose buttonConfig doesn't
|
||||||
@@ -157,7 +164,8 @@ export class SavingsAccountViewComponent implements OnInit {
|
|||||||
Close: 'times-circle',
|
Close: 'times-circle',
|
||||||
Reject: 'ban',
|
Reject: 'ban',
|
||||||
Delete: 'trash',
|
Delete: 'trash',
|
||||||
'Withdrawn by Client': 'user-slash'
|
'Withdrawn by Client': 'user-slash',
|
||||||
|
Terminate: 'times-circle'
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Returns the colour kind for a given action name (used to tint the
|
/** Returns the colour kind for a given action name (used to tint the
|
||||||
@@ -306,6 +314,9 @@ export class SavingsAccountViewComponent implements OnInit {
|
|||||||
case 'Delete':
|
case 'Delete':
|
||||||
this.deleteSavingsAccount();
|
this.deleteSavingsAccount();
|
||||||
break;
|
break;
|
||||||
|
case 'Terminate':
|
||||||
|
this.terminateSavingsAccount();
|
||||||
|
break;
|
||||||
case 'Calculate Interest':
|
case 'Calculate Interest':
|
||||||
this.calculateInterest();
|
this.calculateInterest();
|
||||||
break;
|
break;
|
||||||
@@ -363,6 +374,35 @@ export class SavingsAccountViewComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Terminates Savings Account.
|
||||||
|
*/
|
||||||
|
private terminateSavingsAccount() {
|
||||||
|
const terminateSavingsAccountDialogRef = this.dialog.open(ConfirmationDialogComponent, {
|
||||||
|
data: {
|
||||||
|
heading: 'Terminate Account',
|
||||||
|
dialogContext: 'Are you sure you want to terminate this account?',
|
||||||
|
type: 'Strong'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
terminateSavingsAccountDialogRef.afterClosed().subscribe((response: any) => {
|
||||||
|
if (response && response.confirm) {
|
||||||
|
const locale = this.settingsService.language.code;
|
||||||
|
const dateFormat = this.settingsService.dateFormat;
|
||||||
|
const terminatedOnDate = this.dateUtils.formatDate(new Date(), dateFormat);
|
||||||
|
const data = {
|
||||||
|
accountType: 'SAVINGS',
|
||||||
|
terminatedOnDate,
|
||||||
|
dateFormat,
|
||||||
|
locale
|
||||||
|
};
|
||||||
|
this.accountsService.terminateAccount(this.savingsAccountData.id, data).subscribe(() => {
|
||||||
|
this.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates savings account's interest
|
* Calculates savings account's interest
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -192,6 +192,10 @@ export class SavingsButtonsConfiguration {
|
|||||||
{
|
{
|
||||||
name: 'Delete',
|
name: 'Delete',
|
||||||
taskPermissionName: 'DELETE_SAVINGSACCOUNT'
|
taskPermissionName: 'DELETE_SAVINGSACCOUNT'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Terminate',
|
||||||
|
taskPermissionName: 'DELETE_SAVINGSACCOUNT'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -115,11 +115,10 @@ export class CheckerInboxComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
this.route.data.subscribe((data: { makerCheckerResource: any; makerCheckerTemplate: any }) => {
|
this.route.data.subscribe((data: { makerCheckerResource: any; makerCheckerTemplate: any }) => {
|
||||||
this.searchData = data.makerCheckerResource;
|
const raw = data.makerCheckerResource;
|
||||||
if (this.searchData.length > 0) {
|
this.searchData = Array.isArray(raw) ? raw : (raw?.pageItems ?? []);
|
||||||
this.checkerData = true;
|
this.checkerData = this.searchData.length > 0;
|
||||||
}
|
this.makerCheckerTemplate = data.makerCheckerTemplate ?? {};
|
||||||
this.makerCheckerTemplate = data.makerCheckerTemplate;
|
|
||||||
this.dataSource = new MatTableDataSource(this.searchData);
|
this.dataSource = new MatTableDataSource(this.searchData);
|
||||||
this.selection = new SelectionModel(true, []);
|
this.selection = new SelectionModel(true, []);
|
||||||
});
|
});
|
||||||
|
|||||||
+4
-2
@@ -82,8 +82,10 @@ export class PendingAccountComponent {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.route.data.subscribe((data: { pendingAccountsData: any }) => {
|
this.route.data.subscribe((data: { pendingAccountsData: any }) => {
|
||||||
// status 100 = submitted and pending approval only; exclude active/approved/etc.
|
// status 100 = submitted and pending approval only; exclude terminated accounts
|
||||||
const items = (data.pendingAccountsData?.pageItems || []).filter((a: any) => a.status?.id === 100);
|
const items = (data.pendingAccountsData?.pageItems || []).filter(
|
||||||
|
(a: any) => a.status?.id === 100 && !a.terminateTrue
|
||||||
|
);
|
||||||
this.showData = items.length > 0;
|
this.showData = items.length > 0;
|
||||||
this.dataSource = new MatTableDataSource(items);
|
this.dataSource = new MatTableDataSource(items);
|
||||||
this.selection = new SelectionModel(true, []);
|
this.selection = new SelectionModel(true, []);
|
||||||
|
|||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
<!--
|
||||||
|
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/.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<div class="cv-tab-content">
|
||||||
|
@if (showData) {
|
||||||
|
<div class="cv-action-bar">
|
||||||
|
<mat-form-field appearance="outline" class="cv-search-field">
|
||||||
|
<mat-icon matPrefix>search</mat-icon>
|
||||||
|
<input matInput placeholder="Search accounts..." (keyup)="applyFilter($event.target['value'])" />
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-container">
|
||||||
|
<table mat-table [dataSource]="dataSource" class="modern-table">
|
||||||
|
<ng-container matColumnDef="accountNo">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Account No</th>
|
||||||
|
<td mat-cell *matCellDef="let row" class="cell-mono">{{ row.accountNo }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="clientName">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Client Name</th>
|
||||||
|
<td mat-cell *matCellDef="let row">{{ row.clientName }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="productName">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Product</th>
|
||||||
|
<td mat-cell *matCellDef="let row">{{ row.savingsProductName || row.productName }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="accountType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Type</th>
|
||||||
|
<td mat-cell *matCellDef="let row">{{ row.accountType }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="officeName">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Office</th>
|
||||||
|
<td mat-cell *matCellDef="let row">{{ row.officeName }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="submittedOnDate">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Submitted On</th>
|
||||||
|
<td mat-cell *matCellDef="let row">{{ formatDate(row.timeline?.submittedOnDate || row.submittedOnDate) }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="status">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Status</th>
|
||||||
|
<td mat-cell *matCellDef="let row">
|
||||||
|
<span class="status-badge status-badge--terminated">Terminated</span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr
|
||||||
|
mat-row
|
||||||
|
*matRowDef="let row; columns: displayedColumns"
|
||||||
|
(click)="navigateToAccount(row)"
|
||||||
|
class="table-row"
|
||||||
|
></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (!showData) {
|
||||||
|
<div class="empty-state">
|
||||||
|
<i class="fas fa-times-circle empty-icon"></i>
|
||||||
|
<p class="empty-title">No terminated accounts</p>
|
||||||
|
<p class="empty-text">Accounts marked for termination will appear here.</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.status-badge--terminated {
|
||||||
|
background: #fef2f2;
|
||||||
|
color: #dc2626;
|
||||||
|
border: 1px solid #fecaca;
|
||||||
|
padding: 2px 10px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
+106
@@ -0,0 +1,106 @@
|
|||||||
|
/**
|
||||||
|
* 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, inject } from '@angular/core';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import {
|
||||||
|
MatTableDataSource,
|
||||||
|
MatTable,
|
||||||
|
MatColumnDef,
|
||||||
|
MatHeaderCellDef,
|
||||||
|
MatHeaderCell,
|
||||||
|
MatCellDef,
|
||||||
|
MatCell,
|
||||||
|
MatHeaderRowDef,
|
||||||
|
MatHeaderRow,
|
||||||
|
MatRowDef,
|
||||||
|
MatRow
|
||||||
|
} from '@angular/material/table';
|
||||||
|
import { MatIcon } from '@angular/material/icon';
|
||||||
|
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||||
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'mifosx-terminated-account',
|
||||||
|
templateUrl: './terminated-account.component.html',
|
||||||
|
styleUrls: ['./terminated-account.component.scss'],
|
||||||
|
imports: [
|
||||||
|
...STANDALONE_SHARED_IMPORTS,
|
||||||
|
FaIconComponent,
|
||||||
|
MatTable,
|
||||||
|
MatColumnDef,
|
||||||
|
MatHeaderCellDef,
|
||||||
|
MatHeaderCell,
|
||||||
|
MatCellDef,
|
||||||
|
MatCell,
|
||||||
|
MatHeaderRowDef,
|
||||||
|
MatHeaderRow,
|
||||||
|
MatRowDef,
|
||||||
|
MatRow,
|
||||||
|
MatIcon
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class TerminatedAccountComponent {
|
||||||
|
private route = inject(ActivatedRoute);
|
||||||
|
private router = inject(Router);
|
||||||
|
|
||||||
|
dataSource: MatTableDataSource<any>;
|
||||||
|
showData = false;
|
||||||
|
|
||||||
|
displayedColumns: string[] = [
|
||||||
|
'accountNo',
|
||||||
|
'clientName',
|
||||||
|
'productName',
|
||||||
|
'accountType',
|
||||||
|
'officeName',
|
||||||
|
'submittedOnDate',
|
||||||
|
'status'
|
||||||
|
];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.route.data.subscribe((data: { terminatedAccountsData: any }) => {
|
||||||
|
const items = data.terminatedAccountsData?.pageItems || [];
|
||||||
|
this.showData = items.length > 0;
|
||||||
|
this.dataSource = new MatTableDataSource(items);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateToAccount(account: any): void {
|
||||||
|
const clientId = account.clientId;
|
||||||
|
const accountId = account.id;
|
||||||
|
const depositTypeId = account.depositType?.id;
|
||||||
|
if (account.accountType === 'Current') {
|
||||||
|
this.router.navigate([`/clients/${clientId}/current-accounts/${accountId}`]);
|
||||||
|
} else if (depositTypeId === 200) {
|
||||||
|
this.router.navigate([`/clients/${clientId}/fixed-deposits-accounts/${accountId}`]);
|
||||||
|
} else if (depositTypeId === 300) {
|
||||||
|
this.router.navigate([`/clients/${clientId}/recurring-deposits-accounts/${accountId}`]);
|
||||||
|
} else {
|
||||||
|
this.router.navigate([`/clients/${clientId}/savings-accounts/${accountId}`]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applyFilter(filterValue: string = '') {
|
||||||
|
this.dataSource.filter = filterValue.trim().toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
formatDate(date: any): string {
|
||||||
|
if (!date) return '';
|
||||||
|
if (Array.isArray(date)) {
|
||||||
|
return new Date(date[0], date[1] - 1, date[2]).toLocaleDateString('en-IN', {
|
||||||
|
day: '2-digit',
|
||||||
|
month: 'short',
|
||||||
|
year: 'numeric'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const d = new Date(date);
|
||||||
|
return isNaN(d.getTime())
|
||||||
|
? date
|
||||||
|
: d.toLocaleDateString('en-IN', { day: '2-digit', month: 'short', year: 'numeric' });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -106,6 +106,18 @@
|
|||||||
<fa-icon icon="ban" class="ac-tab__ic"></fa-icon>
|
<fa-icon icon="ban" class="ac-tab__ic"></fa-icon>
|
||||||
<span>Reject Account</span>
|
<span>Reject Account</span>
|
||||||
</a>
|
</a>
|
||||||
|
<a
|
||||||
|
mat-tab-link
|
||||||
|
class="ac-tab"
|
||||||
|
[routerLink]="['./terminated-account']"
|
||||||
|
routerLinkActive
|
||||||
|
#terminatedAccount="routerLinkActive"
|
||||||
|
[active]="terminatedAccount.isActive"
|
||||||
|
*mifosxHasPermission="'ALL_FUNCTIONS_READ'"
|
||||||
|
>
|
||||||
|
<fa-icon icon="times-circle" class="ac-tab__ic"></fa-icon>
|
||||||
|
<span>Terminated Account</span>
|
||||||
|
</a>
|
||||||
<a
|
<a
|
||||||
mat-tab-link
|
mat-tab-link
|
||||||
class="ac-tab"
|
class="ac-tab"
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* 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 { Injectable, inject } from '@angular/core';
|
||||||
|
import { Resolve } from '@angular/router';
|
||||||
|
import { Observable, forkJoin, of } from 'rxjs';
|
||||||
|
import { map, catchError } from 'rxjs/operators';
|
||||||
|
import { TasksService } from '../tasks.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GetTerminatedAccounts implements Resolve<any> {
|
||||||
|
private tasksService = inject(TasksService);
|
||||||
|
|
||||||
|
resolve(): Observable<any> {
|
||||||
|
return forkJoin({
|
||||||
|
savings: this.tasksService.getTerminatedSavingsAccounts().pipe(catchError(() => of({ pageItems: [] }))),
|
||||||
|
current: this.tasksService.getTerminatedCurrentAccounts().pipe(catchError(() => of([]))),
|
||||||
|
offices: this.tasksService.getAllOffices().pipe(catchError(() => of([])))
|
||||||
|
}).pipe(
|
||||||
|
map((results) => {
|
||||||
|
const officeMap: Record<number, string> = {};
|
||||||
|
(results.offices || []).forEach((o: any) => {
|
||||||
|
officeMap[o.id] = o.name;
|
||||||
|
});
|
||||||
|
|
||||||
|
const savingsItems = (results.savings?.pageItems || []).map((a: any) => ({
|
||||||
|
...a,
|
||||||
|
officeName: officeMap[a.officeId] || a.officeName || '',
|
||||||
|
fieldOfficerName: a.fieldOfficerName || '',
|
||||||
|
accountType: a.depositType?.value || 'Savings'
|
||||||
|
}));
|
||||||
|
|
||||||
|
const currentItems = (Array.isArray(results.current) ? results.current : results.current?.pageItems || []).map(
|
||||||
|
(a: any) => ({ ...a, accountType: 'Current' })
|
||||||
|
);
|
||||||
|
|
||||||
|
return { pageItems: [
|
||||||
|
...savingsItems,
|
||||||
|
...currentItems
|
||||||
|
] };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ import { LoanDisbursalComponent } from './checker-inbox-and-tasks-tabs/loan-disb
|
|||||||
import { RescheduleLoanComponent } from './checker-inbox-and-tasks-tabs/reschedule-loan/reschedule-loan.component';
|
import { RescheduleLoanComponent } from './checker-inbox-and-tasks-tabs/reschedule-loan/reschedule-loan.component';
|
||||||
import { PendingAccountComponent } from './checker-inbox-and-tasks-tabs/pending-account/pending-account.component';
|
import { PendingAccountComponent } from './checker-inbox-and-tasks-tabs/pending-account/pending-account.component';
|
||||||
import { RejectedAccountComponent } from './checker-inbox-and-tasks-tabs/rejected-account/rejected-account.component';
|
import { RejectedAccountComponent } from './checker-inbox-and-tasks-tabs/rejected-account/rejected-account.component';
|
||||||
|
import { TerminatedAccountComponent } from './checker-inbox-and-tasks-tabs/terminated-account/terminated-account.component';
|
||||||
import { ViewCheckerInboxComponent } from './view-checker-inbox/view-checker-inbox.component';
|
import { ViewCheckerInboxComponent } from './view-checker-inbox/view-checker-inbox.component';
|
||||||
|
|
||||||
/** Custom Resolvers */
|
/** Custom Resolvers */
|
||||||
@@ -38,6 +39,7 @@ import { MakerCheckerTemplate } from './common-resolvers/makerCheckerTemplate.re
|
|||||||
import { GetCheckerInboxDetailResolver } from './common-resolvers/getCheckerInboxDetail.resolver';
|
import { GetCheckerInboxDetailResolver } from './common-resolvers/getCheckerInboxDetail.resolver';
|
||||||
import { GetPendingAccounts } from './common-resolvers/getPendingAccounts.resolver';
|
import { GetPendingAccounts } from './common-resolvers/getPendingAccounts.resolver';
|
||||||
import { GetRejectedAccounts } from './common-resolvers/getRejectedAccounts.resolver';
|
import { GetRejectedAccounts } from './common-resolvers/getRejectedAccounts.resolver';
|
||||||
|
import { GetTerminatedAccounts } from './common-resolvers/getTerminatedAccounts.resolver';
|
||||||
|
|
||||||
/** Tasks Routes */
|
/** Tasks Routes */
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
@@ -105,6 +107,14 @@ const routes: Routes = [
|
|||||||
rejectedAccountsData: GetRejectedAccounts
|
rejectedAccountsData: GetRejectedAccounts
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'terminated-account',
|
||||||
|
component: TerminatedAccountComponent,
|
||||||
|
data: { title: 'Terminated Account' },
|
||||||
|
resolve: {
|
||||||
|
terminatedAccountsData: GetTerminatedAccounts
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'withdrawal-requests',
|
path: 'withdrawal-requests',
|
||||||
component: WithdrawalRequestsComponent,
|
component: WithdrawalRequestsComponent,
|
||||||
@@ -175,7 +185,8 @@ const routes: Routes = [
|
|||||||
MakerCheckerTemplate,
|
MakerCheckerTemplate,
|
||||||
GetCheckerInboxDetailResolver,
|
GetCheckerInboxDetailResolver,
|
||||||
GetPendingAccounts,
|
GetPendingAccounts,
|
||||||
GetRejectedAccounts
|
GetRejectedAccounts,
|
||||||
|
GetTerminatedAccounts
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class TasksRoutingModule {}
|
export class TasksRoutingModule {}
|
||||||
|
|||||||
@@ -140,6 +140,16 @@ export class TasksService {
|
|||||||
return this.http.get('/currentaccounts', { params: httpParams });
|
return this.http.get('/currentaccounts', { params: httpParams });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTerminatedSavingsAccounts(): Observable<any> {
|
||||||
|
const httpParams = new HttpParams().set('limit', '1000').set('status', 'terminated');
|
||||||
|
return this.http.get('/savingsaccounts', { params: httpParams });
|
||||||
|
}
|
||||||
|
|
||||||
|
getTerminatedCurrentAccounts(): Observable<any> {
|
||||||
|
const httpParams = new HttpParams().set('limit', '1000').set('status', 'terminated');
|
||||||
|
return this.http.get('/currentaccounts', { params: httpParams });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Approve a savings account
|
* Approve a savings account
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user