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:
co-authored by
Claude Sonnet 4.6
parent
e661161eac
commit
cbb2bd7c58
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user