feat(bank-transfer): convert bank transfer to maker-checker approval flow

- submitBankTransfer() now calls submit() with requestType='BANK_TRANSFER'
  instead of the immediate-execution /banktransfer endpoint
- Confirm dialog updated: 'Submit for Approval' instead of 'Execute Transfer'
- disbursement-review: isBankTransferMode getter, separate DR/CR entry tables,
  'Approve Bank Transfer' button label, correct isCollectionMode guard
- Disbursement Requests checker inbox: Type badge (Bank Transfer/Collection/
  Disbursement) column, GL column shows 'Intra-bank transfer' for BT requests
- Checker inbox tab renamed to 'Disbursement & Transfers'

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jaanu423
2026-06-17 14:46:26 +05:30
co-authored by Claude Sonnet 4.6
parent e661161eac
commit cbb2bd7c58
7 changed files with 401 additions and 85 deletions
@@ -45,4 +45,8 @@ export class ClerkDisbursementService {
reject(id: number, reason: string): Observable<any> {
return this.http.post(`/clerk/disbursement-requests/${id}/reject`, { reason });
}
bankTransfer(data: any): Observable<any> {
return this.http.post('/clerk/disbursement-requests/banktransfer', data);
}
}
@@ -150,7 +150,12 @@
class="ws-f__ctl"
[(ngModel)]="row.acTyp"
(click)="$event.stopPropagation()"
(change)="row.acTyp === 'GL' && row.typ !== 'Disbursement' && (row.dbCr = 'Cr')"
(change)="
row.acTyp === 'GL' &&
row.typ !== 'Disbursement' &&
row.typ !== 'Enter Bank Transfer' &&
(row.dbCr = 'Cr')
"
>
@for (a of acTypOptions; track a) {
<option [value]="a">{{ a }}</option>
@@ -192,7 +197,9 @@
type="button"
class="ws-seg__btn ws-seg__btn--dr"
[class.ws-seg__btn--active-dr]="row.dbCr === 'Dr'"
[disabled]="row.acTyp === 'GL' && row.typ !== 'Disbursement'"
[disabled]="
row.acTyp === 'GL' && row.typ !== 'Disbursement' && row.typ !== 'Enter Bank Transfer'
"
(click)="row.dbCr = 'Dr'"
>
DR
@@ -1194,7 +1201,12 @@
<select
class="ws-f__ctl"
[(ngModel)]="row.acTyp"
(change)="row.acTyp === 'GL' && row.typ !== 'Disbursement' && (row.dbCr = 'Cr')"
(change)="
row.acTyp === 'GL' &&
row.typ !== 'Disbursement' &&
row.typ !== 'Enter Bank Transfer' &&
(row.dbCr = 'Cr')
"
>
@for (a of acTypOptions; track a) {
<option [value]="a">{{ a }}</option>
@@ -1232,7 +1244,9 @@
type="button"
class="ws-seg__btn ws-seg__btn--dr"
[class.ws-seg__btn--active-dr]="row.dbCr === 'Dr'"
[disabled]="row.acTyp === 'GL'"
[disabled]="
row.acTyp === 'GL' && row.typ !== 'Disbursement' && row.typ !== 'Enter Bank Transfer'
"
(click)="$event.stopPropagation(); row.dbCr = 'Dr'"
>
DR
@@ -1065,10 +1065,10 @@ export class TransactionEntryComponent implements OnInit, OnDestroy {
addRow(): void {
const active = this.selectedRow;
// Disbursement: auto-add mirror CR row (same type/txntp, opposite DR/CR)
if (active?.typ === 'Disbursement') {
// Disbursement/Bank Transfer: auto-add mirror CR row when active row is DR
if ((active?.typ === 'Disbursement' || active?.typ === 'Enter Bank Transfer') && active.dbCr === 'Dr') {
const mirror: TxnRow = {
typ: 'Disbursement',
typ: active.typ,
br: active.br ?? 1,
trTp: '',
acTyp: 'SD',
@@ -1125,6 +1125,10 @@ export class TransactionEntryComponent implements OnInit, OnDestroy {
if (!row.dbCr) row.dbCr = 'Dr';
row.paymentTypeId = this.findTransferPaymentTypeId();
break;
case 'Enter Bank Transfer':
if (!row.dbCr) row.dbCr = 'Dr';
row.paymentTypeId = this.findTransferPaymentTypeId();
break;
}
}
@@ -1278,6 +1282,69 @@ export class TransactionEntryComponent implements OnInit, OnDestroy {
});
}
/** Submits an intra-bank book transfer for maker-checker approval. */
private submitBankTransfer(): void {
const drRows = this.rows.filter((r) => r.dbCr === 'Dr');
const crRows = this.rows.filter((r) => r.dbCr === 'Cr');
const firstRow = drRows[0] ?? crRows[0];
const toEntry = (r: TxnRow, dbCr: 'Dr' | 'Cr') => ({
dbCr,
accountNo: r.acNo,
accountName: this.rowAccountNames[r.acNo] ?? r.acNo,
accountType: r.acTyp === 'CA' || r.acTyp === 'CB' ? 'CURRENT' : 'SAVINGS',
savingsAccountId: this.rowAccountIds[r.acNo],
amount: String(r.amount),
description: r.description ?? null
});
const allEntries = [
...drRows.map((r) => toEntry(r, 'Dr')),
...crRows.map((r) => toEntry(r, 'Cr'))
];
const payload = {
requestType: 'BANK_TRANSFER',
officeId: firstRow?.br ?? 1,
currencyCode: 'INR',
description: firstRow?.description || undefined,
creditEntries: JSON.stringify(allEntries)
};
this.saving = true;
this.clerkDisbursementService
.submit(payload)
.pipe(
catchError((err: any) => {
const msg =
err?.error?.errors?.[0]?.defaultUserMessage ??
err?.error?.message ??
'Bank transfer submission failed. Please try again.';
this.showAlert('error', 'Submission Failed', [msg]);
this.saving = false;
return of(null);
})
)
.subscribe((res: any) => {
this.saving = false;
if (res) {
const totalDr = drRows.reduce((s, r) => s + (r.amount ?? 0), 0);
this.showAlert('success', 'Bank Transfer Submitted', [
`Voucher No: ${res.voucherNo}`,
`Total: ₹${totalDr.toLocaleString('en-IN', { minimumFractionDigits: 2 })} · DR: ${drRows.length} account(s) → CR: ${crRows.length} account(s)`,
'Pending Manager approval. Track status in "My Disbursement Requests".'
]);
this.rows = [];
this.rowAccountIds = {};
this.rowAccountNames = {};
this.addRow();
this.selectedRow = this.rows[0];
this.accountDetails = null;
this.clientDetails = null;
}
});
}
/** Cashier enters a token number → look up the APPROVED withdrawal request for auto-fill. */
onTokNoBlur(row: TxnRow): void {
if (this.isClerk || !row.tokNo?.trim()) return;
@@ -2217,6 +2284,80 @@ export class TransactionEntryComponent implements OnInit, OnDestroy {
}
onSave(): void {
// Clerk bank transfer — N DR savings/CA + N CR savings/CA, immediate execution
if (this.isClerk && this.rows.length >= 2 && this.rows.every((r) => r.typ === 'Enter Bank Transfer')) {
const drRows = this.rows.filter((r) => r.dbCr === 'Dr');
const crRows = this.rows.filter((r) => r.dbCr === 'Cr');
if (drRows.length === 0) {
this.showAlert('error', 'DR Row Missing', ['At least one DR row (debit account) is required.']);
return;
}
if (crRows.length === 0) {
this.showAlert('error', 'CR Row Missing', ['At least one CR row (credit account) is required.']);
return;
}
// Bank transfer: only savings (SD) or current (CA/CB) accounts allowed
const allowedTypes = new Set([
'SD',
'CA',
'CB'
]);
const invalidAccounts = [
...drRows,
...crRows
].filter((r) => !allowedTypes.has(r.acTyp));
if (invalidAccounts.length > 0) {
this.showAlert('error', 'Invalid Account Type', [
`Account type(s) not allowed in Bank Transfer: ${invalidAccounts.map((r) => r.acTyp + ' (' + r.acNo + ')').join(', ')}`,
'Only Savings (SD) and Current (CA) accounts are permitted.',
'GL, FD, RD, SHR accounts are not allowed — use Disbursement/Collection instead.'
]);
return;
}
const totalDr = drRows.reduce((s, r) => s + (r.amount ?? 0), 0);
const totalCr = crRows.reduce((s, r) => s + (r.amount ?? 0), 0);
if (Math.abs(totalDr - totalCr) > 0.005) {
this.showAlert('error', 'Unbalanced Entry', [
`DR ₹${totalDr.toLocaleString('en-IN', { minimumFractionDigits: 2 })} ≠ CR ₹${totalCr.toLocaleString('en-IN', { minimumFractionDigits: 2 })}`,
'Total DR must equal total CR for a bank transfer.'
]);
return;
}
// All accounts must be loaded
const unloaded = [
...drRows,
...crRows
].filter((r) => !this.rowAccountIds[r.acNo]);
if (unloaded.length > 0) {
this.showAlert('error', 'Account Not Loaded', [
`Please load account details for: ${unloaded.map((r) => r.acNo).join(', ')}`
]);
return;
}
const confirmLines = [
`DR (${drRows.length} account${drRows.length !== 1 ? 's' : ''}):`,
...drRows.map(
(r) => ` [${r.acTyp}] ${r.acNo} ₹${(r.amount ?? 0).toLocaleString('en-IN', { minimumFractionDigits: 2 })}`
),
`CR (${crRows.length} account${crRows.length !== 1 ? 's' : ''}):`,
...crRows.map(
(r) => ` [${r.acTyp}] ${r.acNo} ₹${(r.amount ?? 0).toLocaleString('en-IN', { minimumFractionDigits: 2 })}`
),
`Total: ₹${totalDr.toLocaleString('en-IN', { minimumFractionDigits: 2 })}`,
'Transfer requires Manager approval before execution.'
];
this.showConfirmAlert('info', 'Confirm Bank Transfer', confirmLines, 'Submit for Approval', () =>
this.submitBankTransfer()
);
return;
}
// Clerk multi-row disbursement — handle before single-row guard (selected row may be incomplete)
if (this.isClerk && this.rows.length >= 2 && this.rows.every((r) => r.typ === 'Disbursement')) {
const drRows = this.rows.filter((r) => r.dbCr === 'Dr');
@@ -13,7 +13,7 @@
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2v20M2 12h20" />
</svg>
{{ isCollectionMode ? 'Collection Review' : 'Disbursement Review' }}
{{ isBankTransferMode ? 'Bank Transfer Review' : isCollectionMode ? 'Collection Review' : 'Disbursement Review' }}
</button>
<span class="cashier-nav__esc-hint" (click)="backToInbox()">← Back to Inbox</span>
</nav>
@@ -63,77 +63,189 @@
</div>
</div>
<!-- GL card — Source (disbursement) or Target (collection) -->
<div class="dri-card">
<div class="dri-section-title">
<mat-icon>account_balance</mat-icon>
{{ isCollectionMode ? 'GL Credit (Target)' : 'GL Debit (Source)' }}
</div>
<div class="dri-detail-grid">
<div class="dri-detail-item">
<span class="dri-detail-label">GL Account No</span>
<span class="dri-detail-value cell-mono">{{ request.gl_account_no }}</span>
<!-- GL card — Source (disbursement) or Target (collection). Hidden for bank transfer. -->
@if (!isBankTransferMode) {
<div class="dri-card">
<div class="dri-section-title">
<mat-icon>account_balance</mat-icon>
{{ isCollectionMode ? 'GL Credit (Target)' : 'GL Debit (Source)' }}
</div>
<div class="dri-detail-item">
<span class="dri-detail-label">GL Account Name</span>
<span class="dri-detail-value">{{ request.gl_account_name }}</span>
</div>
<div class="dri-detail-item">
<span class="dri-detail-label">{{ isCollectionMode ? 'CR Amount' : 'DR Amount' }}</span>
<span class="dri-detail-value dri-amount">₹{{ request.dr_amount | number: '1.2-2' }}</span>
</div>
<div class="dri-detail-item">
<span class="dri-detail-label">Payment Mode</span>
<span class="dri-detail-value">{{ request.payment_type_name || 'Transfer' }}</span>
</div>
@if (request.description) {
<div class="dri-detail-item dri-detail-item--full">
<span class="dri-detail-label">Description</span>
<span class="dri-detail-value">{{ request.description }}</span>
<div class="dri-detail-grid">
<div class="dri-detail-item">
<span class="dri-detail-label">GL Account No</span>
<span class="dri-detail-value cell-mono">{{ request.gl_account_no }}</span>
</div>
}
<div class="dri-detail-item">
<span class="dri-detail-label">GL Account Name</span>
<span class="dri-detail-value">{{ request.gl_account_name }}</span>
</div>
<div class="dri-detail-item">
<span class="dri-detail-label">{{ isCollectionMode ? 'CR Amount' : 'DR Amount' }}</span>
<span class="dri-detail-value dri-amount">₹{{ request.dr_amount | number: '1.2-2' }}</span>
</div>
<div class="dri-detail-item">
<span class="dri-detail-label">Payment Mode</span>
<span class="dri-detail-value">{{ request.payment_type_name || 'Transfer' }}</span>
</div>
@if (request.description) {
<div class="dri-detail-item dri-detail-item--full">
<span class="dri-detail-label">Description</span>
<span class="dri-detail-value">{{ request.description }}</span>
</div>
}
</div>
</div>
</div>
}
<!-- Bank Transfer summary card (replaces GL card) -->
@if (isBankTransferMode) {
<div class="dri-card">
<div class="dri-section-title">
<mat-icon>swap_horiz</mat-icon>
Bank Transfer Summary
</div>
<div class="dri-detail-grid">
<div class="dri-detail-item">
<span class="dri-detail-label">Total Amount</span>
<span class="dri-detail-value dri-amount">₹{{ request.dr_amount | number: '1.2-2' }}</span>
</div>
<div class="dri-detail-item">
<span class="dri-detail-label">DR Accounts</span>
<span class="dri-detail-value"
>{{ drEntries.length }} account{{ drEntries.length !== 1 ? 's' : '' }}</span
>
</div>
<div class="dri-detail-item">
<span class="dri-detail-label">CR Accounts</span>
<span class="dri-detail-value"
>{{ crEntries.length }} account{{ crEntries.length !== 1 ? 's' : '' }}</span
>
</div>
@if (request.description) {
<div class="dri-detail-item dri-detail-item--full">
<span class="dri-detail-label">Description</span>
<span class="dri-detail-value">{{ request.description }}</span>
</div>
}
</div>
</div>
}
<!-- Entries table — DR savings (collection) or CR accounts (disbursement) -->
<div class="dri-card">
<div class="dri-section-title">
<mat-icon>account_balance_wallet</mat-icon>
@if (isCollectionMode) {
Debit Entries ({{ drEntries.length }} account{{ drEntries.length !== 1 ? 's' : '' }} — collect from)
} @else {
Credit Entries ({{ crEntries.length }} account{{ crEntries.length !== 1 ? 's' : '' }})
}
</div>
<table class="dri-cr-table">
<thead>
<tr>
<th>#</th>
<th>Account No</th>
<th>Account Name</th>
<th>Type</th>
<th class="num">{{ isCollectionMode ? 'Amount (DR)' : 'Amount (CR)' }}</th>
</tr>
</thead>
<tbody>
@for (entry of isCollectionMode ? drEntries : crEntries; track entry.accountNo; let i = $index) {
<tr>
<td class="dri-cr-idx">{{ i + 1 }}</td>
<td class="cell-mono">{{ entry.accountNo }}</td>
<td>{{ entry.accountName }}</td>
<td>
<span class="dri-type-badge">{{ entry.accountType }}</span>
</td>
<td class="num dri-cr-amount">₹{{ entry.amount | number: '1.2-2' }}</td>
</tr>
@if (!isBankTransferMode) {
<div class="dri-card">
<div class="dri-section-title">
<mat-icon>account_balance_wallet</mat-icon>
@if (isCollectionMode) {
Debit Entries ({{ drEntries.length }} account{{ drEntries.length !== 1 ? 's' : '' }} — collect from)
} @else {
Credit Entries ({{ crEntries.length }} account{{ crEntries.length !== 1 ? 's' : '' }})
}
<tr class="dri-cr-total-row">
<td colspan="4" class="dri-cr-total-label">{{ isCollectionMode ? 'Total Debit' : 'Total Credit' }}</td>
<td class="num dri-cr-total-amount">₹{{ totalCreditAmount() | number: '1.2-2' }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<table class="dri-cr-table">
<thead>
<tr>
<th>#</th>
<th>Account No</th>
<th>Account Name</th>
<th>Type</th>
<th class="num">{{ isCollectionMode ? 'Amount (DR)' : 'Amount (CR)' }}</th>
</tr>
</thead>
<tbody>
@for (entry of isCollectionMode ? drEntries : crEntries; track entry.accountNo; let i = $index) {
<tr>
<td class="dri-cr-idx">{{ i + 1 }}</td>
<td class="cell-mono">{{ entry.accountNo }}</td>
<td>{{ entry.accountName }}</td>
<td>
<span class="dri-type-badge">{{ entry.accountType }}</span>
</td>
<td class="num dri-cr-amount">₹{{ entry.amount | number: '1.2-2' }}</td>
</tr>
}
<tr class="dri-cr-total-row">
<td colspan="4" class="dri-cr-total-label">{{ isCollectionMode ? 'Total Debit' : 'Total Credit' }}</td>
<td class="num dri-cr-total-amount">₹{{ totalCreditAmount() | number: '1.2-2' }}</td>
</tr>
</tbody>
</table>
</div>
}
<!-- Bank Transfer: DR entries table -->
@if (isBankTransferMode) {
<div class="dri-card">
<div class="dri-section-title">
<mat-icon>arrow_upward</mat-icon>
Debit Entries ({{ drEntries.length }} account{{ drEntries.length !== 1 ? 's' : '' }} — funds out)
</div>
<table class="dri-cr-table">
<thead>
<tr>
<th>#</th>
<th>Account No</th>
<th>Account Name</th>
<th>Type</th>
<th class="num">Amount (DR)</th>
</tr>
</thead>
<tbody>
@for (entry of drEntries; track entry.accountNo; let i = $index) {
<tr>
<td class="dri-cr-idx">{{ i + 1 }}</td>
<td class="cell-mono">{{ entry.accountNo }}</td>
<td>{{ entry.accountName }}</td>
<td>
<span class="dri-type-badge">{{ entry.accountType }}</span>
</td>
<td class="num dri-cr-amount">₹{{ entry.amount | number: '1.2-2' }}</td>
</tr>
}
<tr class="dri-cr-total-row">
<td colspan="4" class="dri-cr-total-label">Total Debit</td>
<td class="num dri-cr-total-amount">₹{{ totalDebitAmount() | number: '1.2-2' }}</td>
</tr>
</tbody>
</table>
</div>
<!-- Bank Transfer: CR entries table -->
<div class="dri-card">
<div class="dri-section-title">
<mat-icon>arrow_downward</mat-icon>
Credit Entries ({{ crEntries.length }} account{{ crEntries.length !== 1 ? 's' : '' }} — funds in)
</div>
<table class="dri-cr-table">
<thead>
<tr>
<th>#</th>
<th>Account No</th>
<th>Account Name</th>
<th>Type</th>
<th class="num">Amount (CR)</th>
</tr>
</thead>
<tbody>
@for (entry of crEntries; track entry.accountNo; let i = $index) {
<tr>
<td class="dri-cr-idx">{{ i + 1 }}</td>
<td class="cell-mono">{{ entry.accountNo }}</td>
<td>{{ entry.accountName }}</td>
<td>
<span class="dri-type-badge">{{ entry.accountType }}</span>
</td>
<td class="num dri-cr-amount">₹{{ entry.amount | number: '1.2-2' }}</td>
</tr>
}
<tr class="dri-cr-total-row">
<td colspan="4" class="dri-cr-total-label">Total Credit</td>
<td class="num dri-cr-total-amount">₹{{ totalCreditAmount() | number: '1.2-2' }}</td>
</tr>
</tbody>
</table>
</div>
}
<!-- Processing indicator -->
@if (request.status === 'PROCESSING') {
@@ -204,7 +316,15 @@
@if (!showRejectForm) {
<button type="button" class="dri-btn dri-btn--approve" [disabled]="acting" (click)="approve()">
<mat-icon>check_circle</mat-icon>
{{ acting ? 'Processing…' : isCollectionMode ? 'Approve Collection' : 'Approve Disbursement' }}
{{
acting
? 'Processing…'
: isBankTransferMode
? 'Approve Bank Transfer'
: isCollectionMode
? 'Approve Collection'
: 'Approve Disbursement'
}}
</button>
<button type="button" class="dri-btn dri-btn--reject" [disabled]="acting" (click)="showRejectForm = true">
<mat-icon>cancel</mat-icon>
@@ -91,7 +91,10 @@ export class DisbursementReviewComponent implements OnInit {
if (res?.error) {
this.snackBar.open(res.error, 'Close', { duration: 5000 });
} else {
this.snackBar.open('Disbursement approved successfully.', 'Close', { duration: 3000 });
const msg = this.isBankTransferMode
? 'Bank transfer approved successfully.'
: 'Disbursement approved successfully.';
this.snackBar.open(msg, 'Close', { duration: 3000 });
this.router.navigate(['/checker-inbox-and-tasks/disbursement-requests']);
}
});
@@ -114,7 +117,12 @@ export class DisbursementReviewComponent implements OnInit {
});
}
get isBankTransferMode(): boolean {
return this.request?.request_type === 'BANK_TRANSFER';
}
get isCollectionMode(): boolean {
if (this.isBankTransferMode) return false;
return this.creditEntries.some((e) => e.dbCr === 'Dr');
}
@@ -131,6 +139,10 @@ export class DisbursementReviewComponent implements OnInit {
return entries.reduce((sum, e) => sum + (e.amount ?? 0), 0);
}
totalDebitAmount(): number {
return this.drEntries.reduce((sum, e) => sum + (e.amount ?? 0), 0);
}
statusClass(status: string): string {
const map: Record<string, string> = {
PENDING: 'dri-status--pending',
@@ -12,8 +12,10 @@
<div class="wri-action-bar__left">
<mat-icon class="wri-action-bar__icon">swap_horiz</mat-icon>
<div>
<div class="wri-action-bar__title">Disbursement Requests</div>
<div class="wri-action-bar__sub">Review, approve or reject clerk GL disbursement requests.</div>
<div class="wri-action-bar__title">Disbursement &amp; Transfer Requests</div>
<div class="wri-action-bar__sub">
Review, approve or reject clerk disbursement, collection, and bank transfer requests.
</div>
</div>
</div>
</div>
@@ -106,10 +108,10 @@
<thead>
<tr>
<th class="wri-th-id">Voucher No</th>
<th>GL Account</th>
<th class="num">DR Amount</th>
<th>Credit Entries</th>
<th>Payment Mode</th>
<th>Type</th>
<th>GL Account / Details</th>
<th class="num">Amount</th>
<th>Entries</th>
<th>Clerk</th>
<th>Reviewed By</th>
<th>Submitted At</th>
@@ -122,14 +124,37 @@
<tr class="wri-row" (click)="review(r)">
<td class="cell-mono wri-voucher">{{ r.voucher_no || '—' }}</td>
<td>
<span class="cell-mono">{{ r.gl_account_no }}</span>
<span class="wri-gl-name">{{ r.gl_account_name }}</span>
<span
class="wri-credit-badge"
[style.background]="
r.request_type === 'BANK_TRANSFER'
? '#e3f2fd'
: r.request_type === 'COLLECTION'
? '#fce4ec'
: '#e8f5e9'
"
>
{{
r.request_type === 'BANK_TRANSFER'
? 'Bank Transfer'
: r.request_type === 'COLLECTION'
? 'Collection'
: 'Disbursement'
}}
</span>
</td>
<td>
@if (r.request_type === 'BANK_TRANSFER') {
<span class="wri-gl-name">Intra-bank transfer</span>
} @else {
<span class="cell-mono">{{ r.gl_account_no }}</span>
<span class="wri-gl-name">{{ r.gl_account_name }}</span>
}
</td>
<td class="num wri-amount">₹{{ r.dr_amount | number: '1.2-2' }}</td>
<td class="wri-credit-count">
<span class="wri-credit-badge">{{ creditCount(r) }} account{{ creditCount(r) !== 1 ? 's' : '' }}</span>
</td>
<td>{{ r.payment_type_name || 'Transfer' }}</td>
<td>{{ r.clerk_name }}</td>
<td>{{ r.manager_name || r.manager_username || '—' }}</td>
<td class="wri-dt">{{ formatDateTime(r.submitted_at) }}</td>
@@ -140,7 +140,7 @@
*mifosxHasPermission="'ALL_FUNCTIONS_READ'"
>
<fa-icon icon="money-bill-wave" class="ac-tab__ic"></fa-icon>
<span>Disbursement Requests</span>
<span>Disbursement & Transfers</span>
</a>
</nav>