feat: add Pending Account and Reject Account tabs to Approval Center
- Added Pending Account tab showing savings/FD/RD/current accounts with status=pending (100) - Added Reject Account tab showing accounts with status=rejected (500) - Each tab displays: account no, client name, product, type badge, office, field officer, submitted/rejected date, status - Bulk approve with correct endpoint per deposit type (savings/FD/RD/current) - Resolvers use forkJoin to fetch savings + current accounts in parallel - Office name resolved via /offices map; field officer name taken directly from API response Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
3370a95235
commit
df96be9a0e
+120
@@ -0,0 +1,120 @@
|
|||||||
|
<!--
|
||||||
|
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
|
||||||
|
#filterInput
|
||||||
|
matInput
|
||||||
|
placeholder="Search by client or account"
|
||||||
|
(keyup)="applyFilter(filterInput.value)"
|
||||||
|
/>
|
||||||
|
</mat-form-field>
|
||||||
|
<div class="cv-action-buttons">
|
||||||
|
<button mat-stroked-button class="cv-btn cv-btn-approve" (click)="approveAccounts()">
|
||||||
|
<fa-icon icon="check" class="m-r-10"></fa-icon>Approve
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-container">
|
||||||
|
<table mat-table [dataSource]="dataSource" class="modern-table">
|
||||||
|
<ng-container matColumnDef="select">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>
|
||||||
|
<mat-checkbox
|
||||||
|
(change)="$event ? masterToggle() : null"
|
||||||
|
[checked]="selection.hasValue() && isAllSelected()"
|
||||||
|
[indeterminate]="selection.hasValue() && !isAllSelected()"
|
||||||
|
[aria-label]="checkboxLabel()"
|
||||||
|
></mat-checkbox>
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let row">
|
||||||
|
<mat-checkbox
|
||||||
|
(click)="$event.stopPropagation()"
|
||||||
|
(change)="$event ? selection.toggle(row) : null"
|
||||||
|
[checked]="selection.isSelected(row)"
|
||||||
|
[aria-label]="checkboxLabel(row)"
|
||||||
|
></mat-checkbox>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="accountNo">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Account No</th>
|
||||||
|
<td mat-cell *matCellDef="let account" class="cell-mono">{{ account.accountNo }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="clientName">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Client Name</th>
|
||||||
|
<td mat-cell *matCellDef="let account">{{ account.clientName }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="productName">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Product</th>
|
||||||
|
<td mat-cell *matCellDef="let account">{{ account.savingsProductName || account.productName }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="accountType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Type</th>
|
||||||
|
<td mat-cell *matCellDef="let account">
|
||||||
|
<span
|
||||||
|
class="type-badge"
|
||||||
|
[class.type-badge--current]="account.accountType === 'Current'"
|
||||||
|
[class.type-badge--fd]="account.accountType === 'Fixed Deposit'"
|
||||||
|
[class.type-badge--rd]="account.accountType === 'Recurring Deposit'"
|
||||||
|
>
|
||||||
|
{{ account.accountType }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="officeName">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Office</th>
|
||||||
|
<td mat-cell *matCellDef="let account">{{ account.officeName }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="fieldOfficerName">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Field Officer</th>
|
||||||
|
<td mat-cell *matCellDef="let account">{{ account.fieldOfficerName || '—' }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="submittedOnDate">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Submitted On</th>
|
||||||
|
<td mat-cell *matCellDef="let account">
|
||||||
|
{{ formatDate(account.timeline?.submittedOnDate) }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="status">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Status</th>
|
||||||
|
<td mat-cell *matCellDef="let account">
|
||||||
|
<span class="status-badge status-badge--pending">{{ account.status?.value }}</span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr
|
||||||
|
mat-row
|
||||||
|
*matRowDef="let row; columns: displayedColumns"
|
||||||
|
(click)="selection.toggle(row)"
|
||||||
|
class="table-row"
|
||||||
|
></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (!showData) {
|
||||||
|
<div class="empty-state">
|
||||||
|
<i class="fas fa-clock empty-icon"></i>
|
||||||
|
<p class="empty-title">No pending accounts</p>
|
||||||
|
<p class="empty-text">There are no savings accounts awaiting approval at this time.</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
+171
@@ -0,0 +1,171 @@
|
|||||||
|
/**
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$brand: #1353a0;
|
||||||
|
$border: #e2e8f0;
|
||||||
|
$text-muted: #64748b;
|
||||||
|
|
||||||
|
.cv-tab-content {
|
||||||
|
padding: 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cv-action-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cv-search-field {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 200px;
|
||||||
|
max-width: 320px;
|
||||||
|
|
||||||
|
::ng-deep {
|
||||||
|
.mat-mdc-form-field-flex {
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-form-field-infix {
|
||||||
|
padding: 6px 0;
|
||||||
|
min-height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-form-field-subscript-wrapper {
|
||||||
|
height: 0;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdc-notched-outline {
|
||||||
|
.mdc-notched-outline__leading {
|
||||||
|
border-radius: 8px 0 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdc-notched-outline__trailing {
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-form-field-icon-prefix {
|
||||||
|
padding: 0 6px 0 8px;
|
||||||
|
color: $text-muted;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cv-action-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-left: auto;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cv-btn {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
height: 36px;
|
||||||
|
padding: 0 16px;
|
||||||
|
font-family: 'DM Sans', Roboto, sans-serif;
|
||||||
|
|
||||||
|
fa-icon {
|
||||||
|
margin-right: 4px;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cv-btn-approve {
|
||||||
|
border-color: #16a34a;
|
||||||
|
color: #16a34a;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f0fdf4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 2px 10px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
background: #dbeafe;
|
||||||
|
color: #1e40af;
|
||||||
|
|
||||||
|
&--current {
|
||||||
|
background: #ede9fe;
|
||||||
|
color: #5b21b6;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--fd {
|
||||||
|
background: #fef3c7;
|
||||||
|
color: #92400e;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--rd {
|
||||||
|
background: #d1fae5;
|
||||||
|
color: #065f46;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 2px 10px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
background: #fef9c3;
|
||||||
|
color: #854d0e;
|
||||||
|
|
||||||
|
&--pending {
|
||||||
|
background: #fef9c3;
|
||||||
|
color: #854d0e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 60px 24px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px dashed $border;
|
||||||
|
border-radius: 14px;
|
||||||
|
text-align: center;
|
||||||
|
margin: 16px 0;
|
||||||
|
|
||||||
|
.empty-icon {
|
||||||
|
font-size: 42px;
|
||||||
|
color: #cbd5e1;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-title {
|
||||||
|
margin: 0 0 4px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #0d1b2a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 12.5px;
|
||||||
|
color: $text-muted;
|
||||||
|
max-width: 380px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
+178
@@ -0,0 +1,178 @@
|
|||||||
|
/**
|
||||||
|
* 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 { SelectionModel } from '@angular/cdk/collections';
|
||||||
|
import {
|
||||||
|
MatTableDataSource,
|
||||||
|
MatTable,
|
||||||
|
MatColumnDef,
|
||||||
|
MatHeaderCellDef,
|
||||||
|
MatHeaderCell,
|
||||||
|
MatCellDef,
|
||||||
|
MatCell,
|
||||||
|
MatHeaderRowDef,
|
||||||
|
MatHeaderRow,
|
||||||
|
MatRowDef,
|
||||||
|
MatRow
|
||||||
|
} from '@angular/material/table';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { MatIcon } from '@angular/material/icon';
|
||||||
|
import { MatCheckbox } from '@angular/material/checkbox';
|
||||||
|
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||||
|
|
||||||
|
import { TasksService } from '../../tasks.service';
|
||||||
|
import { SettingsService } from 'app/settings/settings.service';
|
||||||
|
import { Dates } from 'app/core/utils/dates';
|
||||||
|
import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component';
|
||||||
|
import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-base';
|
||||||
|
import { DatepickerBase } from 'app/shared/form-dialog/formfield/model/datepicker-base';
|
||||||
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'mifosx-pending-account',
|
||||||
|
templateUrl: './pending-account.component.html',
|
||||||
|
styleUrls: ['./pending-account.component.scss'],
|
||||||
|
imports: [
|
||||||
|
...STANDALONE_SHARED_IMPORTS,
|
||||||
|
FaIconComponent,
|
||||||
|
MatTable,
|
||||||
|
MatColumnDef,
|
||||||
|
MatHeaderCellDef,
|
||||||
|
MatHeaderCell,
|
||||||
|
MatCheckbox,
|
||||||
|
MatCellDef,
|
||||||
|
MatCell,
|
||||||
|
MatHeaderRowDef,
|
||||||
|
MatHeaderRow,
|
||||||
|
MatRowDef,
|
||||||
|
MatRow,
|
||||||
|
MatIcon
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class PendingAccountComponent {
|
||||||
|
private route = inject(ActivatedRoute);
|
||||||
|
private router = inject(Router);
|
||||||
|
private dialog = inject(MatDialog);
|
||||||
|
private dateUtils = inject(Dates);
|
||||||
|
private settingsService = inject(SettingsService);
|
||||||
|
private tasksService = inject(TasksService);
|
||||||
|
|
||||||
|
dataSource: MatTableDataSource<any>;
|
||||||
|
selection: SelectionModel<any>;
|
||||||
|
showData = false;
|
||||||
|
|
||||||
|
displayedColumns: string[] = [
|
||||||
|
'select',
|
||||||
|
'accountNo',
|
||||||
|
'clientName',
|
||||||
|
'productName',
|
||||||
|
'accountType',
|
||||||
|
'officeName',
|
||||||
|
'fieldOfficerName',
|
||||||
|
'submittedOnDate',
|
||||||
|
'status'
|
||||||
|
];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.route.data.subscribe((data: { pendingAccountsData: any }) => {
|
||||||
|
// status 100 = submitted and pending approval only; exclude active/approved/etc.
|
||||||
|
const items = (data.pendingAccountsData?.pageItems || []).filter((a: any) => a.status?.id === 100);
|
||||||
|
this.showData = items.length > 0;
|
||||||
|
this.dataSource = new MatTableDataSource(items);
|
||||||
|
this.selection = new SelectionModel(true, []);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
isAllSelected() {
|
||||||
|
return this.selection.selected.length === this.dataSource.data.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
masterToggle() {
|
||||||
|
this.isAllSelected() ? this.selection.clear() : this.dataSource.data.forEach((row) => this.selection.select(row));
|
||||||
|
}
|
||||||
|
|
||||||
|
checkboxLabel(row?: any): string {
|
||||||
|
if (!row) return `${this.isAllSelected() ? 'deselect' : 'select'} all`;
|
||||||
|
return `${this.selection.isSelected(row) ? 'deselect' : 'select'} row`;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyFilter(filterValue: string = '') {
|
||||||
|
this.dataSource.filter = filterValue.trim().toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
approveAccounts() {
|
||||||
|
const formfields: FormfieldBase[] = [
|
||||||
|
new DatepickerBase({
|
||||||
|
controlName: 'approvedOnDate',
|
||||||
|
label: 'Approved On Date',
|
||||||
|
value: new Date(),
|
||||||
|
type: 'datetime-local',
|
||||||
|
required: true
|
||||||
|
})
|
||||||
|
];
|
||||||
|
const dialogRef = this.dialog.open(FormDialogComponent, {
|
||||||
|
data: {
|
||||||
|
title: 'Enter Approval Date',
|
||||||
|
layout: { addButtonText: 'Confirm' },
|
||||||
|
formfields
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dialogRef.afterClosed().subscribe((response: any) => {
|
||||||
|
if (response?.data) {
|
||||||
|
this.bulkApprove(response.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private bulkApprove(submittedData: any) {
|
||||||
|
const dateFormat = this.settingsService.dateFormat;
|
||||||
|
const locale = this.settingsService.language.code;
|
||||||
|
const approvedOnDate = this.dateUtils.formatDate(submittedData.value.approvedOnDate, dateFormat);
|
||||||
|
const payload = { approvedOnDate, dateFormat, locale };
|
||||||
|
|
||||||
|
const selected = this.selection.selected;
|
||||||
|
let done = 0;
|
||||||
|
selected.forEach((account: any) => {
|
||||||
|
const depositTypeId = account.depositType?.id;
|
||||||
|
let approve$;
|
||||||
|
if (account.accountType === 'Current') {
|
||||||
|
approve$ = this.tasksService.approveCurrentAccount(account.id, payload);
|
||||||
|
} else if (depositTypeId === 200) {
|
||||||
|
approve$ = this.tasksService.approveFDAccount(account.id, payload);
|
||||||
|
} else if (depositTypeId === 300) {
|
||||||
|
approve$ = this.tasksService.approveRDAccount(account.id, payload);
|
||||||
|
} else {
|
||||||
|
approve$ = this.tasksService.approveSavingsAccount(account.id, payload);
|
||||||
|
}
|
||||||
|
approve$.subscribe(() => {
|
||||||
|
done++;
|
||||||
|
if (done === selected.length) {
|
||||||
|
this.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
formatDate(date: any): string {
|
||||||
|
if (!date) return '';
|
||||||
|
if (Array.isArray(date)) {
|
||||||
|
const d = new Date(date[0], date[1] - 1, date[2]);
|
||||||
|
return d.toLocaleDateString('en-IN', { day: '2-digit', month: 'short', year: 'numeric' });
|
||||||
|
}
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
reload() {
|
||||||
|
const url = this.router.url;
|
||||||
|
this.router
|
||||||
|
.navigateByUrl('/checker-inbox-and-tasks', { skipLocationChange: true })
|
||||||
|
.then(() => this.router.navigate([url]));
|
||||||
|
}
|
||||||
|
}
|
||||||
+98
@@ -0,0 +1,98 @@
|
|||||||
|
<!--
|
||||||
|
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
|
||||||
|
#filterInput
|
||||||
|
matInput
|
||||||
|
placeholder="Search by client or account"
|
||||||
|
(keyup)="applyFilter(filterInput.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 account" class="cell-mono">{{ account.accountNo }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="clientName">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Client Name</th>
|
||||||
|
<td mat-cell *matCellDef="let account">{{ account.clientName }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="productName">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Product</th>
|
||||||
|
<td mat-cell *matCellDef="let account">{{ account.savingsProductName || account.productName }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="accountType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Type</th>
|
||||||
|
<td mat-cell *matCellDef="let account">
|
||||||
|
<span
|
||||||
|
class="type-badge"
|
||||||
|
[class.type-badge--current]="account.accountType === 'Current'"
|
||||||
|
[class.type-badge--fd]="account.accountType === 'Fixed Deposit'"
|
||||||
|
[class.type-badge--rd]="account.accountType === 'Recurring Deposit'"
|
||||||
|
>
|
||||||
|
{{ account.accountType }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="officeName">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Office</th>
|
||||||
|
<td mat-cell *matCellDef="let account">{{ account.officeName }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="fieldOfficerName">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Field Officer</th>
|
||||||
|
<td mat-cell *matCellDef="let account">{{ account.fieldOfficerName || '—' }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="submittedOnDate">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Submitted On</th>
|
||||||
|
<td mat-cell *matCellDef="let account">
|
||||||
|
{{ formatDate(account.timeline?.submittedOnDate) }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="rejectedOnDate">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Rejected On</th>
|
||||||
|
<td mat-cell *matCellDef="let account">
|
||||||
|
{{ formatDate(account.timeline?.rejectedOnDate) }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="status">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Status</th>
|
||||||
|
<td mat-cell *matCellDef="let account">
|
||||||
|
<span class="status-badge status-badge--rejected">{{ account.status?.value }}</span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns" class="table-row"></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (!showData) {
|
||||||
|
<div class="empty-state">
|
||||||
|
<i class="fas fa-ban empty-icon"></i>
|
||||||
|
<p class="empty-title">No rejected accounts</p>
|
||||||
|
<p class="empty-text">There are no rejected savings accounts to display.</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
+138
@@ -0,0 +1,138 @@
|
|||||||
|
/**
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$border: #e2e8f0;
|
||||||
|
$text-muted: #64748b;
|
||||||
|
|
||||||
|
.cv-tab-content {
|
||||||
|
padding: 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cv-action-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cv-search-field {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 200px;
|
||||||
|
max-width: 320px;
|
||||||
|
|
||||||
|
::ng-deep {
|
||||||
|
.mat-mdc-form-field-flex {
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-form-field-infix {
|
||||||
|
padding: 6px 0;
|
||||||
|
min-height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-form-field-subscript-wrapper {
|
||||||
|
height: 0;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdc-notched-outline {
|
||||||
|
.mdc-notched-outline__leading {
|
||||||
|
border-radius: 8px 0 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdc-notched-outline__trailing {
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-form-field-icon-prefix {
|
||||||
|
padding: 0 6px 0 8px;
|
||||||
|
color: $text-muted;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 2px 10px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
background: #dbeafe;
|
||||||
|
color: #1e40af;
|
||||||
|
|
||||||
|
&--current {
|
||||||
|
background: #ede9fe;
|
||||||
|
color: #5b21b6;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--fd {
|
||||||
|
background: #fef3c7;
|
||||||
|
color: #92400e;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--rd {
|
||||||
|
background: #d1fae5;
|
||||||
|
color: #065f46;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 2px 10px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
&--rejected {
|
||||||
|
background: #fee2e2;
|
||||||
|
color: #991b1b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 60px 24px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px dashed $border;
|
||||||
|
border-radius: 14px;
|
||||||
|
text-align: center;
|
||||||
|
margin: 16px 0;
|
||||||
|
|
||||||
|
.empty-icon {
|
||||||
|
font-size: 42px;
|
||||||
|
color: #cbd5e1;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-title {
|
||||||
|
margin: 0 0 4px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #0d1b2a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 12.5px;
|
||||||
|
color: $text-muted;
|
||||||
|
max-width: 380px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
+87
@@ -0,0 +1,87 @@
|
|||||||
|
/**
|
||||||
|
* 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 } 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-rejected-account',
|
||||||
|
templateUrl: './rejected-account.component.html',
|
||||||
|
styleUrls: ['./rejected-account.component.scss'],
|
||||||
|
imports: [
|
||||||
|
...STANDALONE_SHARED_IMPORTS,
|
||||||
|
FaIconComponent,
|
||||||
|
MatTable,
|
||||||
|
MatColumnDef,
|
||||||
|
MatHeaderCellDef,
|
||||||
|
MatHeaderCell,
|
||||||
|
MatCellDef,
|
||||||
|
MatCell,
|
||||||
|
MatHeaderRowDef,
|
||||||
|
MatHeaderRow,
|
||||||
|
MatRowDef,
|
||||||
|
MatRow,
|
||||||
|
MatIcon
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class RejectedAccountComponent {
|
||||||
|
private route = inject(ActivatedRoute);
|
||||||
|
|
||||||
|
dataSource: MatTableDataSource<any>;
|
||||||
|
showData = false;
|
||||||
|
|
||||||
|
displayedColumns: string[] = [
|
||||||
|
'accountNo',
|
||||||
|
'clientName',
|
||||||
|
'productName',
|
||||||
|
'accountType',
|
||||||
|
'officeName',
|
||||||
|
'fieldOfficerName',
|
||||||
|
'submittedOnDate',
|
||||||
|
'rejectedOnDate',
|
||||||
|
'status'
|
||||||
|
];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.route.data.subscribe((data: { rejectedAccountsData: any }) => {
|
||||||
|
// status 500 = rejected only; strip out any non-rejected records
|
||||||
|
const items = (data.rejectedAccountsData?.pageItems || []).filter((a: any) => a.status?.id === 500);
|
||||||
|
this.showData = items.length > 0;
|
||||||
|
this.dataSource = new MatTableDataSource(items);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
applyFilter(filterValue: string = '') {
|
||||||
|
this.dataSource.filter = filterValue.trim().toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
formatDate(date: any): string {
|
||||||
|
if (!date) return '';
|
||||||
|
if (Array.isArray(date)) {
|
||||||
|
const d = new Date(date[0], date[1] - 1, date[2]);
|
||||||
|
return d.toLocaleDateString('en-IN', { day: '2-digit', month: 'short', year: 'numeric' });
|
||||||
|
}
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -82,6 +82,30 @@
|
|||||||
<fa-icon icon="calendar-alt" class="ac-tab__ic"></fa-icon>
|
<fa-icon icon="calendar-alt" class="ac-tab__ic"></fa-icon>
|
||||||
<span>Reschedule Loan</span>
|
<span>Reschedule Loan</span>
|
||||||
</a>
|
</a>
|
||||||
|
<a
|
||||||
|
mat-tab-link
|
||||||
|
class="ac-tab"
|
||||||
|
[routerLink]="['./pending-account']"
|
||||||
|
routerLinkActive
|
||||||
|
#pendingAccount="routerLinkActive"
|
||||||
|
[active]="pendingAccount.isActive"
|
||||||
|
*mifosxHasPermission="'ALL_FUNCTIONS_READ'"
|
||||||
|
>
|
||||||
|
<fa-icon icon="hourglass-half" class="ac-tab__ic"></fa-icon>
|
||||||
|
<span>Pending Account</span>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
mat-tab-link
|
||||||
|
class="ac-tab"
|
||||||
|
[routerLink]="['./rejected-account']"
|
||||||
|
routerLinkActive
|
||||||
|
#rejectedAccount="routerLinkActive"
|
||||||
|
[active]="rejectedAccount.isActive"
|
||||||
|
*mifosxHasPermission="'ALL_FUNCTIONS_READ'"
|
||||||
|
>
|
||||||
|
<fa-icon icon="ban" class="ac-tab__ic"></fa-icon>
|
||||||
|
<span>Reject Account</span>
|
||||||
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<mat-tab-nav-panel #tabPanel>
|
<mat-tab-nav-panel #tabPanel>
|
||||||
|
|||||||
@@ -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 GetPendingAccounts implements Resolve<any> {
|
||||||
|
private tasksService = inject(TasksService);
|
||||||
|
|
||||||
|
resolve(): Observable<any> {
|
||||||
|
return forkJoin({
|
||||||
|
savings: this.tasksService.getPendingSavingsAccounts().pipe(catchError(() => of({ pageItems: [] }))),
|
||||||
|
current: this.tasksService.getPendingCurrentAccounts().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
|
||||||
|
] };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* 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 GetRejectedAccounts implements Resolve<any> {
|
||||||
|
private tasksService = inject(TasksService);
|
||||||
|
|
||||||
|
resolve(): Observable<any> {
|
||||||
|
return forkJoin({
|
||||||
|
savings: this.tasksService.getRejectedSavingsAccounts().pipe(catchError(() => of({ pageItems: [] }))),
|
||||||
|
current: this.tasksService.getRejectedCurrentAccounts().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 || [])
|
||||||
|
.filter((a: any) => a.status?.id === 500)
|
||||||
|
.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 || [])
|
||||||
|
.filter((a: any) => a.status?.id === 500)
|
||||||
|
.map((a: any) => ({ ...a, accountType: 'Current' }));
|
||||||
|
|
||||||
|
return { pageItems: [
|
||||||
|
...savingsItems,
|
||||||
|
...currentItems
|
||||||
|
] };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,8 @@ import { ClientApprovalComponent } from './checker-inbox-and-tasks-tabs/client-a
|
|||||||
import { LoanApprovalComponent } from './checker-inbox-and-tasks-tabs/loan-approval/loan-approval.component';
|
import { LoanApprovalComponent } from './checker-inbox-and-tasks-tabs/loan-approval/loan-approval.component';
|
||||||
import { LoanDisbursalComponent } from './checker-inbox-and-tasks-tabs/loan-disbursal/loan-disbursal.component';
|
import { LoanDisbursalComponent } from './checker-inbox-and-tasks-tabs/loan-disbursal/loan-disbursal.component';
|
||||||
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 { RejectedAccountComponent } from './checker-inbox-and-tasks-tabs/rejected-account/rejected-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 */
|
||||||
@@ -31,6 +33,8 @@ import { GetLoansToBeDisbursed } from './common-resolvers/getLoansToBeDisbursed.
|
|||||||
import { GetRescheduleLoans } from './common-resolvers/getRescheduleLoans.resolver';
|
import { GetRescheduleLoans } from './common-resolvers/getRescheduleLoans.resolver';
|
||||||
import { MakerCheckerTemplate } from './common-resolvers/makerCheckerTemplate.resolver';
|
import { MakerCheckerTemplate } from './common-resolvers/makerCheckerTemplate.resolver';
|
||||||
import { GetCheckerInboxDetailResolver } from './common-resolvers/getCheckerInboxDetail.resolver';
|
import { GetCheckerInboxDetailResolver } from './common-resolvers/getCheckerInboxDetail.resolver';
|
||||||
|
import { GetPendingAccounts } from './common-resolvers/getPendingAccounts.resolver';
|
||||||
|
import { GetRejectedAccounts } from './common-resolvers/getRejectedAccounts.resolver';
|
||||||
|
|
||||||
/** Tasks Routes */
|
/** Tasks Routes */
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
@@ -81,6 +85,22 @@ const routes: Routes = [
|
|||||||
resolve: {
|
resolve: {
|
||||||
rescheduleLoansData: GetRescheduleLoans
|
rescheduleLoansData: GetRescheduleLoans
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'pending-account',
|
||||||
|
component: PendingAccountComponent,
|
||||||
|
data: { title: 'Pending Account' },
|
||||||
|
resolve: {
|
||||||
|
pendingAccountsData: GetPendingAccounts
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'rejected-account',
|
||||||
|
component: RejectedAccountComponent,
|
||||||
|
data: { title: 'Rejected Account' },
|
||||||
|
resolve: {
|
||||||
|
rejectedAccountsData: GetRejectedAccounts
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -111,7 +131,9 @@ const routes: Routes = [
|
|||||||
GetLoansToBeDisbursed,
|
GetLoansToBeDisbursed,
|
||||||
GetRescheduleLoans,
|
GetRescheduleLoans,
|
||||||
MakerCheckerTemplate,
|
MakerCheckerTemplate,
|
||||||
GetCheckerInboxDetailResolver
|
GetCheckerInboxDetailResolver,
|
||||||
|
GetPendingAccounts,
|
||||||
|
GetRejectedAccounts
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class TasksRoutingModule {}
|
export class TasksRoutingModule {}
|
||||||
|
|||||||
@@ -102,6 +102,76 @@ export class TasksService {
|
|||||||
return this.http.get('/rescheduleloans', { params: httpParams });
|
return this.http.get('/rescheduleloans', { params: httpParams });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get savings accounts submitted and pending approval (status 100)
|
||||||
|
*/
|
||||||
|
getPendingSavingsAccounts(): Observable<any> {
|
||||||
|
const httpParams = new HttpParams()
|
||||||
|
.set('limit', '1000')
|
||||||
|
.set('status', 'submittedAndPendingApproval')
|
||||||
|
.set('sqlSearch', 's.status_enum = 100');
|
||||||
|
return this.http.get('/savingsaccounts', { params: httpParams });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current accounts submitted and pending approval (status 100)
|
||||||
|
*/
|
||||||
|
getPendingCurrentAccounts(): Observable<any> {
|
||||||
|
const httpParams = new HttpParams().set('limit', '1000').set('status', 'submittedAndPendingApproval');
|
||||||
|
return this.http.get('/currentaccounts', { params: httpParams });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get savings accounts that have been rejected (status 500)
|
||||||
|
*/
|
||||||
|
getRejectedSavingsAccounts(): Observable<any> {
|
||||||
|
const httpParams = new HttpParams()
|
||||||
|
.set('limit', '1000')
|
||||||
|
.set('status', 'rejected')
|
||||||
|
.set('sqlSearch', 's.status_enum = 500');
|
||||||
|
return this.http.get('/savingsaccounts', { params: httpParams });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current accounts that have been rejected (status 500)
|
||||||
|
*/
|
||||||
|
getRejectedCurrentAccounts(): Observable<any> {
|
||||||
|
const httpParams = new HttpParams().set('limit', '1000').set('status', 'rejected');
|
||||||
|
return this.http.get('/currentaccounts', { params: httpParams });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Approve a savings account
|
||||||
|
*/
|
||||||
|
approveSavingsAccount(accountId: any, data: any): Observable<any> {
|
||||||
|
const httpParams = new HttpParams().set('command', 'approve');
|
||||||
|
return this.http.post(`/savingsaccounts/${accountId}`, data, { params: httpParams });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Approve a fixed deposit account
|
||||||
|
*/
|
||||||
|
approveFDAccount(accountId: any, data: any): Observable<any> {
|
||||||
|
const httpParams = new HttpParams().set('command', 'approve');
|
||||||
|
return this.http.post(`/fixeddepositaccounts/${accountId}`, data, { params: httpParams });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Approve a recurring deposit account
|
||||||
|
*/
|
||||||
|
approveRDAccount(accountId: any, data: any): Observable<any> {
|
||||||
|
const httpParams = new HttpParams().set('command', 'approve');
|
||||||
|
return this.http.post(`/recurringdepositaccounts/${accountId}`, data, { params: httpParams });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Approve a current account
|
||||||
|
*/
|
||||||
|
approveCurrentAccount(accountId: any, data: any): Observable<any> {
|
||||||
|
const httpParams = new HttpParams().set('command', 'approve');
|
||||||
|
return this.http.post(`/currentaccounts/${accountId}`, data, { params: httpParams });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Submit data in batches.
|
* Submit data in batches.
|
||||||
* @param {data} Data to be submitted
|
* @param {data} Data to be submitted
|
||||||
|
|||||||
Reference in New Issue
Block a user