feat(cashier): denomination overdraft validation, exchange detail fix, cash exchange in reports
- Savings/Current/FD/RD withdrawal forms: card-style overdraft warning when requested denomination count exceeds cashier stock; submit blocked on overdraft - Cash History detail view: show both RECEIVED and GIVEN denomination breakdowns for Cash Exchange (previously only RECEIVED side was displayed) - Closing Cash Report: add Cash Exchanges section (denomination-wise received/given/net) - Excess Transaction Report: include cash exchanges >= threshold with Type badge (Exchange/Txn) and ID Proof column for RBI/AML compliance - cashier-shared.scss: etr-badge and closing-exchange-head styles - Cash Exchange entry: overdraft warning redesigned to cx-overdraft-card BEM layout Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
51495f8eb8
commit
f291e4da5b
+30
-1
@@ -326,6 +326,31 @@
|
||||
Use ₹{{ denominationTotal | number: '1.2-2' }} as Transaction Amount
|
||||
</button>
|
||||
</div>
|
||||
<!-- Denomination overdraft warning (withdrawal only) -->
|
||||
@if (transactionType.withdrawal && hasWithdrawalDenomOverdraft) {
|
||||
<div class="sat__denom-warn">
|
||||
<div class="sat__denom-warn-title">
|
||||
<fa-icon icon="exclamation-triangle" class="sat__denom-warn-ico"></fa-icon>
|
||||
<span>Insufficient denomination stock — cannot proceed</span>
|
||||
</div>
|
||||
<div class="sat__denom-warn-rows">
|
||||
@for (item of withdrawalDenomOverdraft; track item.label) {
|
||||
<div class="sat__denom-warn-row">
|
||||
<span class="sat__denom-warn-chip">{{ item.label }}</span>
|
||||
<span class="sat__denom-warn-stat"
|
||||
><span class="sat__denom-warn-lbl">Need</span><strong>{{ item.requested }}</strong></span
|
||||
>
|
||||
<span class="sat__denom-warn-dot">·</span>
|
||||
<span class="sat__denom-warn-stat"
|
||||
><span class="sat__denom-warn-lbl">Available</span><strong>{{ item.available }}</strong></span
|
||||
>
|
||||
<span class="sat__denom-warn-short">Short: {{ item.requested - item.available }}</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Closing cash position -->
|
||||
@if (openingLoaded && openingDenoms.length > 0) {
|
||||
<div
|
||||
@@ -486,7 +511,11 @@
|
||||
mat-flat-button
|
||||
type="submit"
|
||||
class="sat__submit"
|
||||
[disabled]="!currentAccountTransactionForm.valid || (denominationTotal > 0 && !denominationMatchesForm)"
|
||||
[disabled]="
|
||||
!currentAccountTransactionForm.valid ||
|
||||
(denominationTotal > 0 && !denominationMatchesForm) ||
|
||||
hasWithdrawalDenomOverdraft
|
||||
"
|
||||
>
|
||||
<fa-icon icon="check" class="m-r-6"></fa-icon>
|
||||
{{ 'labels.buttons.Submit' | translate }}
|
||||
|
||||
+95
@@ -1094,3 +1094,98 @@ $red: #b91c1c;
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: #0d1b2a;
|
||||
}
|
||||
|
||||
.sat__denom-warn {
|
||||
margin: 8px 14px;
|
||||
background: #fff5f5;
|
||||
border: 1.5px solid #fca5a5;
|
||||
border-left: 4px solid #ef4444;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
|
||||
&-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 9px 14px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: #7f1d1d;
|
||||
background: #fef2f2;
|
||||
border-bottom: 1px solid #fecaca;
|
||||
}
|
||||
|
||||
&-ico {
|
||||
color: #ef4444;
|
||||
font-size: 14px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&-rows {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
&-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 7px 14px;
|
||||
font-size: 12.5px;
|
||||
color: #7f1d1d;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1px dashed #fecaca;
|
||||
}
|
||||
}
|
||||
|
||||
&-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 3px 10px;
|
||||
background: linear-gradient(135deg, #fee2e2, #fecaca);
|
||||
border: 1px solid #fca5a5;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
color: #991b1b;
|
||||
min-width: 52px;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
&-stat {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
strong {
|
||||
font-weight: 800;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
}
|
||||
|
||||
&-lbl {
|
||||
font-size: 11px;
|
||||
color: #b91c1c;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&-dot {
|
||||
color: #fca5a5;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&-short {
|
||||
margin-left: auto;
|
||||
padding: 2px 9px;
|
||||
background: #fff;
|
||||
border: 1.5px solid #f87171;
|
||||
border-radius: 999px;
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
color: #b91c1c;
|
||||
}
|
||||
}
|
||||
|
||||
+18
@@ -389,6 +389,24 @@ export class CurrentAccountTransactionsComponent implements OnInit {
|
||||
return this.denominationTotal > 0 && formAmt === this.denominationTotal;
|
||||
}
|
||||
|
||||
get withdrawalDenomOverdraft(): Array<{ label: string; available: number; requested: number }> {
|
||||
if (!this.transactionType.withdrawal || !this.openingLoaded || this.openingDenoms.length === 0) return [];
|
||||
const openMap = new Map<string, number>();
|
||||
for (const d of this.openingDenoms) {
|
||||
openMap.set(`${d.kind}:${d.denominationValue}`, Number(d.count ?? 0));
|
||||
}
|
||||
return [
|
||||
...this.notes,
|
||||
...this.coins
|
||||
]
|
||||
.map((d) => ({ label: d.label, available: openMap.get(this.denomKey(d)) ?? 0, requested: this.countFor(d) }))
|
||||
.filter((item) => item.requested > 0 && item.requested > item.available);
|
||||
}
|
||||
|
||||
get hasWithdrawalDenomOverdraft(): boolean {
|
||||
return this.withdrawalDenomOverdraft.length > 0;
|
||||
}
|
||||
|
||||
applyDenominationTotal(): void {
|
||||
this.currentAccountTransactionForm.controls['transactionAmount'].setValue(this.denominationTotal);
|
||||
this.currentAccountTransactionForm.controls['transactionAmount'].markAsDirty();
|
||||
|
||||
Reference in New Issue
Block a user