feat: dynamic denomination breakdown in deposit forms + savings menu scroller
Deposit forms (Current/Savings/FD/RD) now load cash denominations from the Denomination Master instead of a hardcoded list: - active + discontinued denominations fetched via /denominationmaster - discontinued rows hidden by default behind a "Show discontinued" toggle, rendered faded and disabled (cannot be counted) when revealed - rows sorted by value descending; redesigned as a Denom/Qty/Amount table with circular coin chips and per-row amount (faded at zero) Also: savings account-actions menu now scrolls (max-height + grouped sections mirror the current-account menu) and renders all action items. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
918a90368c
commit
89e17f561f
+95
-51
@@ -142,68 +142,47 @@
|
||||
<fa-icon icon="money-bill-wave" class="sat__panel-ico"></fa-icon>
|
||||
<h3 class="sat__panel-title">Cash Denomination Breakdown</h3>
|
||||
<span class="sat__panel-spacer"></span>
|
||||
@if (hasInactive) {
|
||||
<button type="button" mat-stroked-button class="sat__panel-action" (click)="showInactive = !showInactive">
|
||||
{{ showInactive ? 'Hide discontinued' : 'Show discontinued' }}
|
||||
</button>
|
||||
}
|
||||
<button type="button" mat-stroked-button class="sat__panel-action" (click)="clearDenominations()">
|
||||
<fa-icon icon="undo" class="m-r-6"></fa-icon> Clear
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<!-- Banknotes -->
|
||||
<div class="sat__denom-group">
|
||||
<div class="sat__denom-group-head">
|
||||
<span class="sat__denom-group-title">Banknotes</span>
|
||||
<span class="sat__denom-group-total">₹{{ notesTotal | number: '1.2-2' }}</span>
|
||||
<!-- Denomination table (Denom · Qty · Amount) -->
|
||||
<div class="sat__dt">
|
||||
<div class="sat__dt-head">
|
||||
<span class="sat__dt-h-denom">Denom</span>
|
||||
<span class="sat__dt-h-qty">Qty</span>
|
||||
<span class="sat__dt-h-amount">Amount</span>
|
||||
</div>
|
||||
<div class="sat__denom-grid">
|
||||
@for (d of notes; track d.value) {
|
||||
<div class="sat__denom" [class.sat__denom--active]="countFor(d) > 0">
|
||||
<div class="sat__denom-top">
|
||||
<span class="sat__denom-chip sat__denom-chip--note">{{ d.label }}</span>
|
||||
<span class="sat__denom-amount">₹{{ subtotalFor(d) | number: '1.2-2' }}</span>
|
||||
</div>
|
||||
<div class="sat__denom-stepper">
|
||||
<button
|
||||
type="button"
|
||||
class="sat__denom-step"
|
||||
(click)="bumpDenom(d, -1)"
|
||||
[disabled]="countFor(d) === 0"
|
||||
aria-label="Decrease"
|
||||
>
|
||||
−
|
||||
</button>
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
[value]="countFor(d)"
|
||||
(input)="onDenomChange(d, $any($event.target).value)"
|
||||
inputmode="numeric"
|
||||
class="sat__denom-count"
|
||||
/>
|
||||
<button type="button" class="sat__denom-step" (click)="bumpDenom(d, 1)" aria-label="Increase">+</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Coins -->
|
||||
<div class="sat__denom-group">
|
||||
<div class="sat__denom-group-head">
|
||||
<span class="sat__denom-group-title">Coins</span>
|
||||
<span class="sat__denom-group-total">₹{{ coinsTotal | number: '1.2-2' }}</span>
|
||||
<!-- Bank notes -->
|
||||
<div class="sat__dt-section">
|
||||
<span class="sat__dt-section-title">Bank Notes</span>
|
||||
<span class="sat__dt-section-total">₹{{ notesTotal | number: '1.2-2' }}</span>
|
||||
</div>
|
||||
<div class="sat__denom-grid">
|
||||
@for (d of coins; track d.value) {
|
||||
<div class="sat__denom" [class.sat__denom--active]="countFor(d) > 0">
|
||||
<div class="sat__denom-top">
|
||||
<span class="sat__denom-chip sat__denom-chip--coin">{{ d.label }}</span>
|
||||
<span class="sat__denom-amount">₹{{ subtotalFor(d) | number: '1.2-2' }}</span>
|
||||
</div>
|
||||
@for (d of notes; track d.value) {
|
||||
<div
|
||||
class="sat__dt-row"
|
||||
[class.sat__dt-row--active]="countFor(d) > 0"
|
||||
[style.display]="!d.active && !showInactive ? 'none' : null"
|
||||
[style.opacity]="d.active ? null : 0.55"
|
||||
[attr.title]="d.active ? null : 'Discontinued — not active for new deposits'"
|
||||
>
|
||||
<div class="sat__dt-denom">
|
||||
<span class="sat__denom-chip sat__denom-chip--note">{{ d.label }}</span>
|
||||
</div>
|
||||
<div class="sat__dt-qty">
|
||||
<div class="sat__denom-stepper">
|
||||
<button
|
||||
type="button"
|
||||
class="sat__denom-step"
|
||||
(click)="bumpDenom(d, -1)"
|
||||
[disabled]="countFor(d) === 0"
|
||||
[disabled]="countFor(d) === 0 || !d.active"
|
||||
aria-label="Decrease"
|
||||
>
|
||||
−
|
||||
@@ -213,14 +192,79 @@
|
||||
min="0"
|
||||
[value]="countFor(d)"
|
||||
(input)="onDenomChange(d, $any($event.target).value)"
|
||||
[disabled]="!d.active"
|
||||
inputmode="numeric"
|
||||
class="sat__denom-count"
|
||||
/>
|
||||
<button type="button" class="sat__denom-step" (click)="bumpDenom(d, 1)" aria-label="Increase">+</button>
|
||||
<button
|
||||
type="button"
|
||||
class="sat__denom-step"
|
||||
(click)="bumpDenom(d, 1)"
|
||||
[disabled]="!d.active"
|
||||
aria-label="Increase"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="sat__dt-amount" [class.sat__dt-amount--zero]="countFor(d) === 0">
|
||||
₹{{ subtotalFor(d) | number: '1.2-2' }}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Coins -->
|
||||
<div class="sat__dt-section">
|
||||
<span class="sat__dt-section-title">Coins</span>
|
||||
<span class="sat__dt-section-total">₹{{ coinsTotal | number: '1.2-2' }}</span>
|
||||
</div>
|
||||
@for (d of coins; track d.value) {
|
||||
<div
|
||||
class="sat__dt-row"
|
||||
[class.sat__dt-row--active]="countFor(d) > 0"
|
||||
[style.display]="!d.active && !showInactive ? 'none' : null"
|
||||
[style.opacity]="d.active ? null : 0.55"
|
||||
[attr.title]="d.active ? null : 'Discontinued — not active for new deposits'"
|
||||
>
|
||||
<div class="sat__dt-denom">
|
||||
<span class="sat__denom-chip sat__denom-chip--coin sat__denom-chip--circle">{{ d.label }}</span>
|
||||
</div>
|
||||
<div class="sat__dt-qty">
|
||||
<div class="sat__denom-stepper">
|
||||
<button
|
||||
type="button"
|
||||
class="sat__denom-step"
|
||||
(click)="bumpDenom(d, -1)"
|
||||
[disabled]="countFor(d) === 0 || !d.active"
|
||||
aria-label="Decrease"
|
||||
>
|
||||
−
|
||||
</button>
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
[value]="countFor(d)"
|
||||
(input)="onDenomChange(d, $any($event.target).value)"
|
||||
[disabled]="!d.active"
|
||||
inputmode="numeric"
|
||||
class="sat__denom-count"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="sat__denom-step"
|
||||
(click)="bumpDenom(d, 1)"
|
||||
[disabled]="!d.active"
|
||||
aria-label="Increase"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sat__dt-amount" [class.sat__dt-amount--zero]="countFor(d) === 0">
|
||||
₹{{ subtotalFor(d) | number: '1.2-2' }}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Tally + apply -->
|
||||
|
||||
+67
-33
@@ -431,22 +431,42 @@ $red: #b91c1c;
|
||||
}
|
||||
|
||||
/* ─── Denomination groups ─────────────────────────────────────────── */
|
||||
.sat__denom-group {
|
||||
padding: 8px 14px 4px;
|
||||
|
||||
& + & {
|
||||
border-top: 1px solid #f1f5f9;
|
||||
}
|
||||
/* ── Denomination table: Denom · Qty · Amount ── */
|
||||
.sat__dt {
|
||||
padding: 4px 0 6px;
|
||||
}
|
||||
|
||||
.sat__denom-group-head {
|
||||
.sat__dt-head {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 140px 120px;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 2px 16px 8px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: #94a3b8;
|
||||
border-bottom: 1px solid $border;
|
||||
}
|
||||
|
||||
.sat__dt-h-qty {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sat__dt-h-amount {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.sat__dt-section {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
margin-bottom: 5px;
|
||||
padding: 12px 16px 4px;
|
||||
}
|
||||
|
||||
.sat__denom-group-title {
|
||||
.sat__dt-section-title {
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
@@ -454,46 +474,52 @@ $red: #b91c1c;
|
||||
color: $slate;
|
||||
}
|
||||
|
||||
.sat__denom-group-total {
|
||||
.sat__dt-section-total {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: $navy;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.sat__denom-grid {
|
||||
.sat__dt-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.sat__denom {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
padding: 6px 9px;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
border: 1px solid $border;
|
||||
transition:
|
||||
border-color 0.15s ease,
|
||||
background 0.15s ease,
|
||||
box-shadow 0.15s ease;
|
||||
min-width: 0;
|
||||
grid-template-columns: 1fr 140px 120px;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 7px 16px;
|
||||
border-bottom: 1px dotted #e2e8f0;
|
||||
transition: background 0.12s ease;
|
||||
|
||||
&--active {
|
||||
border-color: #86efac;
|
||||
background: #f0fdf4;
|
||||
box-shadow: 0 1px 3px -1px rgb(4 120 87 / 18%);
|
||||
}
|
||||
}
|
||||
|
||||
.sat__denom-top {
|
||||
.sat__dt-denom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.sat__dt-qty {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.sat__denom-stepper {
|
||||
width: 130px;
|
||||
}
|
||||
}
|
||||
|
||||
.sat__dt-amount {
|
||||
text-align: right;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: $navy;
|
||||
font-variant-numeric: tabular-nums;
|
||||
|
||||
&--zero {
|
||||
color: #cbd5e1;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.sat__denom-chip {
|
||||
@@ -521,6 +547,14 @@ $red: #b91c1c;
|
||||
color: #854d0e;
|
||||
border: 1px solid #fcd34d;
|
||||
}
|
||||
|
||||
&--circle {
|
||||
min-width: 36px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
padding: 0;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.sat__denom-amount {
|
||||
|
||||
+41
-17
@@ -19,6 +19,7 @@ import { Router, ActivatedRoute, RouterLink } from '@angular/router';
|
||||
|
||||
/** Custom Services */
|
||||
import { CurrentAccountsService } from '../../current-accounts.service';
|
||||
import { DenominationMasterService } from 'app/system/denomination-master/denomination-master.service';
|
||||
import { ClientsService } from 'app/clients/clients.service';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { Dates } from 'app/core/utils/dates';
|
||||
@@ -37,6 +38,7 @@ interface Denomination {
|
||||
value: number;
|
||||
kind: 'note' | 'coin';
|
||||
label: string;
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,6 +65,7 @@ export class CurrentAccountTransactionsComponent implements OnInit {
|
||||
private clientsService = inject(ClientsService);
|
||||
private settingsService = inject(SettingsService);
|
||||
private dialog = inject(MatDialog);
|
||||
private denominationMasterService = inject(DenominationMasterService);
|
||||
|
||||
/** Minimum Due Date allowed. */
|
||||
minDate = new Date(2000, 0, 1);
|
||||
@@ -88,24 +91,14 @@ export class CurrentAccountTransactionsComponent implements OnInit {
|
||||
currentAccountId: string;
|
||||
currency: Currency | null = null;
|
||||
|
||||
/** Indian banknote denominations (descending). */
|
||||
readonly notes: Denomination[] = [
|
||||
{ value: 500, kind: 'note', label: '₹500' },
|
||||
{ value: 200, kind: 'note', label: '₹200' },
|
||||
{ value: 100, kind: 'note', label: '₹100' },
|
||||
{ value: 50, kind: 'note', label: '₹50' },
|
||||
{ value: 20, kind: 'note', label: '₹20' },
|
||||
{ value: 10, kind: 'note', label: '₹10' }
|
||||
];
|
||||
/** Banknote denominations — loaded dynamically from the Denomination Master (active only). */
|
||||
notes: Denomination[] = [];
|
||||
|
||||
/** Indian coin denominations (descending). */
|
||||
readonly coins: Denomination[] = [
|
||||
{ value: 20, kind: 'coin', label: '₹20' },
|
||||
{ value: 10, kind: 'coin', label: '₹10' },
|
||||
{ value: 5, kind: 'coin', label: '₹5' },
|
||||
{ value: 2, kind: 'coin', label: '₹2' },
|
||||
{ value: 1, kind: 'coin', label: 'Re 1' }
|
||||
];
|
||||
/** Coin denominations — loaded dynamically from the Denomination Master. */
|
||||
coins: Denomination[] = [];
|
||||
|
||||
/** When false, discontinued (inactive) denominations stay hidden in the grid. */
|
||||
showInactive = false;
|
||||
|
||||
/** Quantity per denomination — keyed by `${kind}:${value}`. */
|
||||
denominationCounts: Record<string, number> = {};
|
||||
@@ -135,9 +128,40 @@ export class CurrentAccountTransactionsComponent implements OnInit {
|
||||
ngOnInit() {
|
||||
this.maxDate = this.settingsService.businessDate;
|
||||
this.createCurrentAccountTransactionForm();
|
||||
this.loadDenominations();
|
||||
this.loadContextStrip();
|
||||
}
|
||||
|
||||
/** Load active denominations from the Denomination Master so the cash
|
||||
* breakdown grid reflects the bank's current configuration. */
|
||||
private loadDenominations(): void {
|
||||
this.denominationMasterService.getAll().subscribe({
|
||||
next: (list: any[]) => {
|
||||
const rows: Denomination[] = (list || []).map((d) => ({
|
||||
value: d.denomination,
|
||||
kind: d.kind as 'note' | 'coin',
|
||||
label: '₹' + d.denomination,
|
||||
active: !!d.isActive
|
||||
}));
|
||||
const order = (a: Denomination, b: Denomination) => b.value - a.value;
|
||||
this.notes = rows.filter((r) => r.kind === 'note').sort(order);
|
||||
this.coins = rows.filter((r) => r.kind === 'coin').sort(order);
|
||||
},
|
||||
error: () => {
|
||||
this.notes = [];
|
||||
this.coins = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** True when at least one discontinued denomination exists. */
|
||||
get hasInactive(): boolean {
|
||||
return [
|
||||
...this.notes,
|
||||
...this.coins
|
||||
].some((d) => !d.active);
|
||||
}
|
||||
|
||||
private loadContextStrip(): void {
|
||||
if (!this.currentAccountId) return;
|
||||
this.currentAccountsService.getCurrentAccountData(this.currentAccountId).subscribe({
|
||||
|
||||
+95
-49
@@ -116,66 +116,47 @@
|
||||
<fa-icon icon="money-bill-wave" class="sat__panel-ico"></fa-icon>
|
||||
<h3 class="sat__panel-title">Cash Denomination Breakdown</h3>
|
||||
<span class="sat__panel-spacer"></span>
|
||||
@if (hasInactive) {
|
||||
<button type="button" mat-stroked-button class="sat__panel-action" (click)="showInactive = !showInactive">
|
||||
{{ showInactive ? 'Hide discontinued' : 'Show discontinued' }}
|
||||
</button>
|
||||
}
|
||||
<button type="button" mat-stroked-button class="sat__panel-action" (click)="clearDenominations()">
|
||||
<fa-icon icon="undo" class="m-r-6"></fa-icon> Clear
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div class="sat__denom-group">
|
||||
<div class="sat__denom-group-head">
|
||||
<span class="sat__denom-group-title">Banknotes</span>
|
||||
<span class="sat__denom-group-total">₹{{ notesTotal | number: '1.2-2' }}</span>
|
||||
<!-- Denomination table (Denom · Qty · Amount) -->
|
||||
<div class="sat__dt">
|
||||
<div class="sat__dt-head">
|
||||
<span class="sat__dt-h-denom">Denom</span>
|
||||
<span class="sat__dt-h-qty">Qty</span>
|
||||
<span class="sat__dt-h-amount">Amount</span>
|
||||
</div>
|
||||
<div class="sat__denom-grid">
|
||||
@for (d of notes; track d.value) {
|
||||
<div class="sat__denom" [class.sat__denom--active]="countFor(d) > 0">
|
||||
<div class="sat__denom-top">
|
||||
<span class="sat__denom-chip sat__denom-chip--note">{{ d.label }}</span>
|
||||
<span class="sat__denom-amount">₹{{ subtotalFor(d) | number: '1.2-2' }}</span>
|
||||
</div>
|
||||
<div class="sat__denom-stepper">
|
||||
<button
|
||||
type="button"
|
||||
class="sat__denom-step"
|
||||
(click)="bumpDenom(d, -1)"
|
||||
[disabled]="countFor(d) === 0"
|
||||
aria-label="Decrease"
|
||||
>
|
||||
−
|
||||
</button>
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
[value]="countFor(d)"
|
||||
(input)="onDenomChange(d, $any($event.target).value)"
|
||||
inputmode="numeric"
|
||||
class="sat__denom-count"
|
||||
/>
|
||||
<button type="button" class="sat__denom-step" (click)="bumpDenom(d, 1)" aria-label="Increase">+</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sat__denom-group">
|
||||
<div class="sat__denom-group-head">
|
||||
<span class="sat__denom-group-title">Coins</span>
|
||||
<span class="sat__denom-group-total">₹{{ coinsTotal | number: '1.2-2' }}</span>
|
||||
<!-- Bank notes -->
|
||||
<div class="sat__dt-section">
|
||||
<span class="sat__dt-section-title">Bank Notes</span>
|
||||
<span class="sat__dt-section-total">₹{{ notesTotal | number: '1.2-2' }}</span>
|
||||
</div>
|
||||
<div class="sat__denom-grid">
|
||||
@for (d of coins; track d.value) {
|
||||
<div class="sat__denom" [class.sat__denom--active]="countFor(d) > 0">
|
||||
<div class="sat__denom-top">
|
||||
<span class="sat__denom-chip sat__denom-chip--coin">{{ d.label }}</span>
|
||||
<span class="sat__denom-amount">₹{{ subtotalFor(d) | number: '1.2-2' }}</span>
|
||||
</div>
|
||||
@for (d of notes; track d.value) {
|
||||
<div
|
||||
class="sat__dt-row"
|
||||
[class.sat__dt-row--active]="countFor(d) > 0"
|
||||
[style.display]="!d.active && !showInactive ? 'none' : null"
|
||||
[style.opacity]="d.active ? null : 0.55"
|
||||
[attr.title]="d.active ? null : 'Discontinued — not active for new deposits'"
|
||||
>
|
||||
<div class="sat__dt-denom">
|
||||
<span class="sat__denom-chip sat__denom-chip--note">{{ d.label }}</span>
|
||||
</div>
|
||||
<div class="sat__dt-qty">
|
||||
<div class="sat__denom-stepper">
|
||||
<button
|
||||
type="button"
|
||||
class="sat__denom-step"
|
||||
(click)="bumpDenom(d, -1)"
|
||||
[disabled]="countFor(d) === 0"
|
||||
[disabled]="countFor(d) === 0 || !d.active"
|
||||
aria-label="Decrease"
|
||||
>
|
||||
−
|
||||
@@ -185,14 +166,79 @@
|
||||
min="0"
|
||||
[value]="countFor(d)"
|
||||
(input)="onDenomChange(d, $any($event.target).value)"
|
||||
[disabled]="!d.active"
|
||||
inputmode="numeric"
|
||||
class="sat__denom-count"
|
||||
/>
|
||||
<button type="button" class="sat__denom-step" (click)="bumpDenom(d, 1)" aria-label="Increase">+</button>
|
||||
<button
|
||||
type="button"
|
||||
class="sat__denom-step"
|
||||
(click)="bumpDenom(d, 1)"
|
||||
[disabled]="!d.active"
|
||||
aria-label="Increase"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="sat__dt-amount" [class.sat__dt-amount--zero]="countFor(d) === 0">
|
||||
₹{{ subtotalFor(d) | number: '1.2-2' }}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Coins -->
|
||||
<div class="sat__dt-section">
|
||||
<span class="sat__dt-section-title">Coins</span>
|
||||
<span class="sat__dt-section-total">₹{{ coinsTotal | number: '1.2-2' }}</span>
|
||||
</div>
|
||||
@for (d of coins; track d.value) {
|
||||
<div
|
||||
class="sat__dt-row"
|
||||
[class.sat__dt-row--active]="countFor(d) > 0"
|
||||
[style.display]="!d.active && !showInactive ? 'none' : null"
|
||||
[style.opacity]="d.active ? null : 0.55"
|
||||
[attr.title]="d.active ? null : 'Discontinued — not active for new deposits'"
|
||||
>
|
||||
<div class="sat__dt-denom">
|
||||
<span class="sat__denom-chip sat__denom-chip--coin sat__denom-chip--circle">{{ d.label }}</span>
|
||||
</div>
|
||||
<div class="sat__dt-qty">
|
||||
<div class="sat__denom-stepper">
|
||||
<button
|
||||
type="button"
|
||||
class="sat__denom-step"
|
||||
(click)="bumpDenom(d, -1)"
|
||||
[disabled]="countFor(d) === 0 || !d.active"
|
||||
aria-label="Decrease"
|
||||
>
|
||||
−
|
||||
</button>
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
[value]="countFor(d)"
|
||||
(input)="onDenomChange(d, $any($event.target).value)"
|
||||
[disabled]="!d.active"
|
||||
inputmode="numeric"
|
||||
class="sat__denom-count"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="sat__denom-step"
|
||||
(click)="bumpDenom(d, 1)"
|
||||
[disabled]="!d.active"
|
||||
aria-label="Increase"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sat__dt-amount" [class.sat__dt-amount--zero]="countFor(d) === 0">
|
||||
₹{{ subtotalFor(d) | number: '1.2-2' }}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="sat__tally">
|
||||
|
||||
+67
-33
@@ -383,22 +383,41 @@ $amber-d: #b45309;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.sat__denom-group {
|
||||
padding: 8px 14px 4px;
|
||||
|
||||
& + & {
|
||||
border-top: 1px solid #f1f5f9;
|
||||
}
|
||||
/* ── Denomination table: Denom · Qty · Amount ── */
|
||||
.sat__dt {
|
||||
padding: 4px 0 6px;
|
||||
}
|
||||
|
||||
.sat__denom-group-head {
|
||||
.sat__dt-head {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 140px 120px;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 2px 16px 8px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: #94a3b8;
|
||||
border-bottom: 1px solid $border;
|
||||
}
|
||||
|
||||
.sat__dt-h-qty {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sat__dt-h-amount {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.sat__dt-section {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
margin-bottom: 5px;
|
||||
padding: 12px 16px 4px;
|
||||
}
|
||||
|
||||
.sat__denom-group-title {
|
||||
.sat__dt-section-title {
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
@@ -406,45 +425,52 @@ $amber-d: #b45309;
|
||||
color: $slate;
|
||||
}
|
||||
|
||||
.sat__denom-group-total {
|
||||
.sat__dt-section-total {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: $navy;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.sat__denom-grid {
|
||||
.sat__dt-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.sat__denom {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
padding: 6px 9px;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
border: 1px solid $border;
|
||||
transition:
|
||||
border-color 0.15s ease,
|
||||
background 0.15s ease;
|
||||
min-width: 0;
|
||||
grid-template-columns: 1fr 140px 120px;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 7px 16px;
|
||||
border-bottom: 1px dotted #e2e8f0;
|
||||
transition: background 0.12s ease;
|
||||
|
||||
&--active {
|
||||
border-color: #86efac;
|
||||
background: #f0fdf4;
|
||||
box-shadow: 0 1px 3px -1px rgb(4 120 87 / 18%);
|
||||
}
|
||||
}
|
||||
|
||||
.sat__denom-top {
|
||||
.sat__dt-denom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.sat__dt-qty {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.sat__denom-stepper {
|
||||
width: 130px;
|
||||
}
|
||||
}
|
||||
|
||||
.sat__dt-amount {
|
||||
text-align: right;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: $navy;
|
||||
font-variant-numeric: tabular-nums;
|
||||
|
||||
&--zero {
|
||||
color: #cbd5e1;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.sat__denom-chip {
|
||||
@@ -470,6 +496,14 @@ $amber-d: #b45309;
|
||||
color: #854d0e;
|
||||
border: 1px solid #fcd34d;
|
||||
}
|
||||
|
||||
&--circle {
|
||||
min-width: 36px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
padding: 0;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.sat__denom-amount {
|
||||
|
||||
+39
-15
@@ -15,6 +15,7 @@ import {
|
||||
ReactiveFormsModule
|
||||
} from '@angular/forms';
|
||||
import { FixedDepositsService } from '../../fixed-deposits.service';
|
||||
import { DenominationMasterService } from 'app/system/denomination-master/denomination-master.service';
|
||||
import { ClientsService } from 'app/clients/clients.service';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { Dates } from 'app/core/utils/dates';
|
||||
@@ -34,6 +35,7 @@ interface Denomination {
|
||||
value: number;
|
||||
kind: 'note' | 'coin';
|
||||
label: string;
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@@ -57,6 +59,7 @@ export class FixedDepositsCashTransactionComponent implements OnInit {
|
||||
private clientsService = inject(ClientsService);
|
||||
private settingsService = inject(SettingsService);
|
||||
private dialog = inject(MatDialog);
|
||||
private denominationMasterService = inject(DenominationMasterService);
|
||||
|
||||
minDate = new Date(2000, 0, 1);
|
||||
maxDate = new Date();
|
||||
@@ -69,22 +72,12 @@ export class FixedDepositsCashTransactionComponent implements OnInit {
|
||||
accountId: string;
|
||||
currency: Currency;
|
||||
|
||||
readonly notes: Denomination[] = [
|
||||
{ value: 500, kind: 'note', label: '₹500' },
|
||||
{ value: 200, kind: 'note', label: '₹200' },
|
||||
{ value: 100, kind: 'note', label: '₹100' },
|
||||
{ value: 50, kind: 'note', label: '₹50' },
|
||||
{ value: 20, kind: 'note', label: '₹20' },
|
||||
{ value: 10, kind: 'note', label: '₹10' }
|
||||
];
|
||||
notes: Denomination[] = [];
|
||||
|
||||
readonly coins: Denomination[] = [
|
||||
{ value: 20, kind: 'coin', label: '₹20' },
|
||||
{ value: 10, kind: 'coin', label: '₹10' },
|
||||
{ value: 5, kind: 'coin', label: '₹5' },
|
||||
{ value: 2, kind: 'coin', label: '₹2' },
|
||||
{ value: 1, kind: 'coin', label: 'Re 1' }
|
||||
];
|
||||
coins: Denomination[] = [];
|
||||
|
||||
/** When false, discontinued (inactive) denominations stay hidden in the grid. */
|
||||
showInactive = false;
|
||||
|
||||
denominationCounts: Record<string, number> = {};
|
||||
|
||||
@@ -114,9 +107,40 @@ export class FixedDepositsCashTransactionComponent implements OnInit {
|
||||
ngOnInit() {
|
||||
this.maxDate = this.settingsService.businessDate;
|
||||
this.createSavingAccountTransactionForm();
|
||||
this.loadDenominations();
|
||||
this.loadContextStrip();
|
||||
}
|
||||
|
||||
/** Load active denominations from the Denomination Master so the cash
|
||||
* breakdown grid reflects the bank's current configuration. */
|
||||
private loadDenominations(): void {
|
||||
this.denominationMasterService.getAll().subscribe({
|
||||
next: (list: any[]) => {
|
||||
const rows: Denomination[] = (list || []).map((d) => ({
|
||||
value: d.denomination,
|
||||
kind: d.kind as 'note' | 'coin',
|
||||
label: '₹' + d.denomination,
|
||||
active: !!d.isActive
|
||||
}));
|
||||
const order = (a: Denomination, b: Denomination) => b.value - a.value;
|
||||
this.notes = rows.filter((r) => r.kind === 'note').sort(order);
|
||||
this.coins = rows.filter((r) => r.kind === 'coin').sort(order);
|
||||
},
|
||||
error: () => {
|
||||
this.notes = [];
|
||||
this.coins = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** True when at least one discontinued denomination exists. */
|
||||
get hasInactive(): boolean {
|
||||
return [
|
||||
...this.notes,
|
||||
...this.coins
|
||||
].some((d) => !d.active);
|
||||
}
|
||||
|
||||
private loadContextStrip(): void {
|
||||
if (!this.accountId) return;
|
||||
this.fixedDepositsService.getFixedDepositsAccountData(this.accountId).subscribe({
|
||||
|
||||
+95
-49
@@ -116,66 +116,47 @@
|
||||
<fa-icon icon="money-bill-wave" class="sat__panel-ico"></fa-icon>
|
||||
<h3 class="sat__panel-title">Cash Denomination Breakdown</h3>
|
||||
<span class="sat__panel-spacer"></span>
|
||||
@if (hasInactive) {
|
||||
<button type="button" mat-stroked-button class="sat__panel-action" (click)="showInactive = !showInactive">
|
||||
{{ showInactive ? 'Hide discontinued' : 'Show discontinued' }}
|
||||
</button>
|
||||
}
|
||||
<button type="button" mat-stroked-button class="sat__panel-action" (click)="clearDenominations()">
|
||||
<fa-icon icon="undo" class="m-r-6"></fa-icon> Clear
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div class="sat__denom-group">
|
||||
<div class="sat__denom-group-head">
|
||||
<span class="sat__denom-group-title">Banknotes</span>
|
||||
<span class="sat__denom-group-total">₹{{ notesTotal | number: '1.2-2' }}</span>
|
||||
<!-- Denomination table (Denom · Qty · Amount) -->
|
||||
<div class="sat__dt">
|
||||
<div class="sat__dt-head">
|
||||
<span class="sat__dt-h-denom">Denom</span>
|
||||
<span class="sat__dt-h-qty">Qty</span>
|
||||
<span class="sat__dt-h-amount">Amount</span>
|
||||
</div>
|
||||
<div class="sat__denom-grid">
|
||||
@for (d of notes; track d.value) {
|
||||
<div class="sat__denom" [class.sat__denom--active]="countFor(d) > 0">
|
||||
<div class="sat__denom-top">
|
||||
<span class="sat__denom-chip sat__denom-chip--note">{{ d.label }}</span>
|
||||
<span class="sat__denom-amount">₹{{ subtotalFor(d) | number: '1.2-2' }}</span>
|
||||
</div>
|
||||
<div class="sat__denom-stepper">
|
||||
<button
|
||||
type="button"
|
||||
class="sat__denom-step"
|
||||
(click)="bumpDenom(d, -1)"
|
||||
[disabled]="countFor(d) === 0"
|
||||
aria-label="Decrease"
|
||||
>
|
||||
−
|
||||
</button>
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
[value]="countFor(d)"
|
||||
(input)="onDenomChange(d, $any($event.target).value)"
|
||||
inputmode="numeric"
|
||||
class="sat__denom-count"
|
||||
/>
|
||||
<button type="button" class="sat__denom-step" (click)="bumpDenom(d, 1)" aria-label="Increase">+</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sat__denom-group">
|
||||
<div class="sat__denom-group-head">
|
||||
<span class="sat__denom-group-title">Coins</span>
|
||||
<span class="sat__denom-group-total">₹{{ coinsTotal | number: '1.2-2' }}</span>
|
||||
<!-- Bank notes -->
|
||||
<div class="sat__dt-section">
|
||||
<span class="sat__dt-section-title">Bank Notes</span>
|
||||
<span class="sat__dt-section-total">₹{{ notesTotal | number: '1.2-2' }}</span>
|
||||
</div>
|
||||
<div class="sat__denom-grid">
|
||||
@for (d of coins; track d.value) {
|
||||
<div class="sat__denom" [class.sat__denom--active]="countFor(d) > 0">
|
||||
<div class="sat__denom-top">
|
||||
<span class="sat__denom-chip sat__denom-chip--coin">{{ d.label }}</span>
|
||||
<span class="sat__denom-amount">₹{{ subtotalFor(d) | number: '1.2-2' }}</span>
|
||||
</div>
|
||||
@for (d of notes; track d.value) {
|
||||
<div
|
||||
class="sat__dt-row"
|
||||
[class.sat__dt-row--active]="countFor(d) > 0"
|
||||
[style.display]="!d.active && !showInactive ? 'none' : null"
|
||||
[style.opacity]="d.active ? null : 0.55"
|
||||
[attr.title]="d.active ? null : 'Discontinued — not active for new deposits'"
|
||||
>
|
||||
<div class="sat__dt-denom">
|
||||
<span class="sat__denom-chip sat__denom-chip--note">{{ d.label }}</span>
|
||||
</div>
|
||||
<div class="sat__dt-qty">
|
||||
<div class="sat__denom-stepper">
|
||||
<button
|
||||
type="button"
|
||||
class="sat__denom-step"
|
||||
(click)="bumpDenom(d, -1)"
|
||||
[disabled]="countFor(d) === 0"
|
||||
[disabled]="countFor(d) === 0 || !d.active"
|
||||
aria-label="Decrease"
|
||||
>
|
||||
−
|
||||
@@ -185,14 +166,79 @@
|
||||
min="0"
|
||||
[value]="countFor(d)"
|
||||
(input)="onDenomChange(d, $any($event.target).value)"
|
||||
[disabled]="!d.active"
|
||||
inputmode="numeric"
|
||||
class="sat__denom-count"
|
||||
/>
|
||||
<button type="button" class="sat__denom-step" (click)="bumpDenom(d, 1)" aria-label="Increase">+</button>
|
||||
<button
|
||||
type="button"
|
||||
class="sat__denom-step"
|
||||
(click)="bumpDenom(d, 1)"
|
||||
[disabled]="!d.active"
|
||||
aria-label="Increase"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="sat__dt-amount" [class.sat__dt-amount--zero]="countFor(d) === 0">
|
||||
₹{{ subtotalFor(d) | number: '1.2-2' }}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Coins -->
|
||||
<div class="sat__dt-section">
|
||||
<span class="sat__dt-section-title">Coins</span>
|
||||
<span class="sat__dt-section-total">₹{{ coinsTotal | number: '1.2-2' }}</span>
|
||||
</div>
|
||||
@for (d of coins; track d.value) {
|
||||
<div
|
||||
class="sat__dt-row"
|
||||
[class.sat__dt-row--active]="countFor(d) > 0"
|
||||
[style.display]="!d.active && !showInactive ? 'none' : null"
|
||||
[style.opacity]="d.active ? null : 0.55"
|
||||
[attr.title]="d.active ? null : 'Discontinued — not active for new deposits'"
|
||||
>
|
||||
<div class="sat__dt-denom">
|
||||
<span class="sat__denom-chip sat__denom-chip--coin sat__denom-chip--circle">{{ d.label }}</span>
|
||||
</div>
|
||||
<div class="sat__dt-qty">
|
||||
<div class="sat__denom-stepper">
|
||||
<button
|
||||
type="button"
|
||||
class="sat__denom-step"
|
||||
(click)="bumpDenom(d, -1)"
|
||||
[disabled]="countFor(d) === 0 || !d.active"
|
||||
aria-label="Decrease"
|
||||
>
|
||||
−
|
||||
</button>
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
[value]="countFor(d)"
|
||||
(input)="onDenomChange(d, $any($event.target).value)"
|
||||
[disabled]="!d.active"
|
||||
inputmode="numeric"
|
||||
class="sat__denom-count"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="sat__denom-step"
|
||||
(click)="bumpDenom(d, 1)"
|
||||
[disabled]="!d.active"
|
||||
aria-label="Increase"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sat__dt-amount" [class.sat__dt-amount--zero]="countFor(d) === 0">
|
||||
₹{{ subtotalFor(d) | number: '1.2-2' }}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="sat__tally">
|
||||
|
||||
+67
-33
@@ -383,22 +383,41 @@ $amber-d: #b45309;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.sat__denom-group {
|
||||
padding: 8px 14px 4px;
|
||||
|
||||
& + & {
|
||||
border-top: 1px solid #f1f5f9;
|
||||
}
|
||||
/* ── Denomination table: Denom · Qty · Amount ── */
|
||||
.sat__dt {
|
||||
padding: 4px 0 6px;
|
||||
}
|
||||
|
||||
.sat__denom-group-head {
|
||||
.sat__dt-head {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 140px 120px;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 2px 16px 8px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: #94a3b8;
|
||||
border-bottom: 1px solid $border;
|
||||
}
|
||||
|
||||
.sat__dt-h-qty {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sat__dt-h-amount {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.sat__dt-section {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
margin-bottom: 5px;
|
||||
padding: 12px 16px 4px;
|
||||
}
|
||||
|
||||
.sat__denom-group-title {
|
||||
.sat__dt-section-title {
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
@@ -406,45 +425,52 @@ $amber-d: #b45309;
|
||||
color: $slate;
|
||||
}
|
||||
|
||||
.sat__denom-group-total {
|
||||
.sat__dt-section-total {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: $navy;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.sat__denom-grid {
|
||||
.sat__dt-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.sat__denom {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
padding: 6px 9px;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
border: 1px solid $border;
|
||||
transition:
|
||||
border-color 0.15s ease,
|
||||
background 0.15s ease;
|
||||
min-width: 0;
|
||||
grid-template-columns: 1fr 140px 120px;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 7px 16px;
|
||||
border-bottom: 1px dotted #e2e8f0;
|
||||
transition: background 0.12s ease;
|
||||
|
||||
&--active {
|
||||
border-color: #86efac;
|
||||
background: #f0fdf4;
|
||||
box-shadow: 0 1px 3px -1px rgb(4 120 87 / 18%);
|
||||
}
|
||||
}
|
||||
|
||||
.sat__denom-top {
|
||||
.sat__dt-denom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.sat__dt-qty {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.sat__denom-stepper {
|
||||
width: 130px;
|
||||
}
|
||||
}
|
||||
|
||||
.sat__dt-amount {
|
||||
text-align: right;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: $navy;
|
||||
font-variant-numeric: tabular-nums;
|
||||
|
||||
&--zero {
|
||||
color: #cbd5e1;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.sat__denom-chip {
|
||||
@@ -470,6 +496,14 @@ $amber-d: #b45309;
|
||||
color: #854d0e;
|
||||
border: 1px solid #fcd34d;
|
||||
}
|
||||
|
||||
&--circle {
|
||||
min-width: 36px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
padding: 0;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.sat__denom-amount {
|
||||
|
||||
+39
-15
@@ -19,6 +19,7 @@ import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
||||
|
||||
/** Custom Services */
|
||||
import { RecurringDepositsService } from '../../recurring-deposits.service';
|
||||
import { DenominationMasterService } from 'app/system/denomination-master/denomination-master.service';
|
||||
import { ClientsService } from 'app/clients/clients.service';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { Dates } from 'app/core/utils/dates';
|
||||
@@ -37,6 +38,7 @@ interface Denomination {
|
||||
value: number;
|
||||
kind: 'note' | 'coin';
|
||||
label: string;
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,6 +65,7 @@ export class DepositRecurringDepositsAccountComponent implements OnInit {
|
||||
private clientsService = inject(ClientsService);
|
||||
private settingsService = inject(SettingsService);
|
||||
private dialog = inject(MatDialog);
|
||||
private denominationMasterService = inject(DenominationMasterService);
|
||||
|
||||
@Input() currency: Currency;
|
||||
|
||||
@@ -80,22 +83,12 @@ export class DepositRecurringDepositsAccountComponent implements OnInit {
|
||||
transactionCommand: TransactionCommand;
|
||||
transactionType: TransactionTypeFlags = { deposit: false, withdrawal: false };
|
||||
|
||||
readonly notes: Denomination[] = [
|
||||
{ value: 500, kind: 'note', label: '₹500' },
|
||||
{ value: 200, kind: 'note', label: '₹200' },
|
||||
{ value: 100, kind: 'note', label: '₹100' },
|
||||
{ value: 50, kind: 'note', label: '₹50' },
|
||||
{ value: 20, kind: 'note', label: '₹20' },
|
||||
{ value: 10, kind: 'note', label: '₹10' }
|
||||
];
|
||||
notes: Denomination[] = [];
|
||||
|
||||
readonly coins: Denomination[] = [
|
||||
{ value: 20, kind: 'coin', label: '₹20' },
|
||||
{ value: 10, kind: 'coin', label: '₹10' },
|
||||
{ value: 5, kind: 'coin', label: '₹5' },
|
||||
{ value: 2, kind: 'coin', label: '₹2' },
|
||||
{ value: 1, kind: 'coin', label: 'Re 1' }
|
||||
];
|
||||
coins: Denomination[] = [];
|
||||
|
||||
/** When false, discontinued (inactive) denominations stay hidden in the grid. */
|
||||
showInactive = false;
|
||||
|
||||
denominationCounts: Record<string, number> = {};
|
||||
|
||||
@@ -132,9 +125,40 @@ export class DepositRecurringDepositsAccountComponent implements OnInit {
|
||||
ngOnInit() {
|
||||
this.maxDate = this.settingsService.businessDate;
|
||||
this.createdepositRecurringDepositForm();
|
||||
this.loadDenominations();
|
||||
this.loadContextStrip();
|
||||
}
|
||||
|
||||
/** Load active denominations from the Denomination Master so the cash
|
||||
* breakdown grid reflects the bank's current configuration. */
|
||||
private loadDenominations(): void {
|
||||
this.denominationMasterService.getAll().subscribe({
|
||||
next: (list: any[]) => {
|
||||
const rows: Denomination[] = (list || []).map((d) => ({
|
||||
value: d.denomination,
|
||||
kind: d.kind as 'note' | 'coin',
|
||||
label: '₹' + d.denomination,
|
||||
active: !!d.isActive
|
||||
}));
|
||||
const order = (a: Denomination, b: Denomination) => b.value - a.value;
|
||||
this.notes = rows.filter((r) => r.kind === 'note').sort(order);
|
||||
this.coins = rows.filter((r) => r.kind === 'coin').sort(order);
|
||||
},
|
||||
error: () => {
|
||||
this.notes = [];
|
||||
this.coins = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** True when at least one discontinued denomination exists. */
|
||||
get hasInactive(): boolean {
|
||||
return [
|
||||
...this.notes,
|
||||
...this.coins
|
||||
].some((d) => !d.active);
|
||||
}
|
||||
|
||||
private loadContextStrip(): void {
|
||||
if (!this.accountId) return;
|
||||
this.recurringDepositsService.getRecurringDepositsAccountData(this.accountId).subscribe({
|
||||
|
||||
+95
-51
@@ -142,68 +142,47 @@
|
||||
<fa-icon icon="money-bill-wave" class="sat__panel-ico"></fa-icon>
|
||||
<h3 class="sat__panel-title">Cash Denomination Breakdown</h3>
|
||||
<span class="sat__panel-spacer"></span>
|
||||
@if (hasInactive) {
|
||||
<button type="button" mat-stroked-button class="sat__panel-action" (click)="showInactive = !showInactive">
|
||||
{{ showInactive ? 'Hide discontinued' : 'Show discontinued' }}
|
||||
</button>
|
||||
}
|
||||
<button type="button" mat-stroked-button class="sat__panel-action" (click)="clearDenominations()">
|
||||
<fa-icon icon="undo" class="m-r-6"></fa-icon> Clear
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<!-- Banknotes -->
|
||||
<div class="sat__denom-group">
|
||||
<div class="sat__denom-group-head">
|
||||
<span class="sat__denom-group-title">Banknotes</span>
|
||||
<span class="sat__denom-group-total">₹{{ notesTotal | number: '1.2-2' }}</span>
|
||||
<!-- Denomination table (Denom · Qty · Amount) -->
|
||||
<div class="sat__dt">
|
||||
<div class="sat__dt-head">
|
||||
<span class="sat__dt-h-denom">Denom</span>
|
||||
<span class="sat__dt-h-qty">Qty</span>
|
||||
<span class="sat__dt-h-amount">Amount</span>
|
||||
</div>
|
||||
<div class="sat__denom-grid">
|
||||
@for (d of notes; track d.value) {
|
||||
<div class="sat__denom" [class.sat__denom--active]="countFor(d) > 0">
|
||||
<div class="sat__denom-top">
|
||||
<span class="sat__denom-chip sat__denom-chip--note">{{ d.label }}</span>
|
||||
<span class="sat__denom-amount">₹{{ subtotalFor(d) | number: '1.2-2' }}</span>
|
||||
</div>
|
||||
<div class="sat__denom-stepper">
|
||||
<button
|
||||
type="button"
|
||||
class="sat__denom-step"
|
||||
(click)="bumpDenom(d, -1)"
|
||||
[disabled]="countFor(d) === 0"
|
||||
aria-label="Decrease"
|
||||
>
|
||||
−
|
||||
</button>
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
[value]="countFor(d)"
|
||||
(input)="onDenomChange(d, $any($event.target).value)"
|
||||
inputmode="numeric"
|
||||
class="sat__denom-count"
|
||||
/>
|
||||
<button type="button" class="sat__denom-step" (click)="bumpDenom(d, 1)" aria-label="Increase">+</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Coins -->
|
||||
<div class="sat__denom-group">
|
||||
<div class="sat__denom-group-head">
|
||||
<span class="sat__denom-group-title">Coins</span>
|
||||
<span class="sat__denom-group-total">₹{{ coinsTotal | number: '1.2-2' }}</span>
|
||||
<!-- Bank notes -->
|
||||
<div class="sat__dt-section">
|
||||
<span class="sat__dt-section-title">Bank Notes</span>
|
||||
<span class="sat__dt-section-total">₹{{ notesTotal | number: '1.2-2' }}</span>
|
||||
</div>
|
||||
<div class="sat__denom-grid">
|
||||
@for (d of coins; track d.value) {
|
||||
<div class="sat__denom" [class.sat__denom--active]="countFor(d) > 0">
|
||||
<div class="sat__denom-top">
|
||||
<span class="sat__denom-chip sat__denom-chip--coin">{{ d.label }}</span>
|
||||
<span class="sat__denom-amount">₹{{ subtotalFor(d) | number: '1.2-2' }}</span>
|
||||
</div>
|
||||
@for (d of notes; track d.value) {
|
||||
<div
|
||||
class="sat__dt-row"
|
||||
[class.sat__dt-row--active]="countFor(d) > 0"
|
||||
[style.display]="!d.active && !showInactive ? 'none' : null"
|
||||
[style.opacity]="d.active ? null : 0.55"
|
||||
[attr.title]="d.active ? null : 'Discontinued — not active for new deposits'"
|
||||
>
|
||||
<div class="sat__dt-denom">
|
||||
<span class="sat__denom-chip sat__denom-chip--note">{{ d.label }}</span>
|
||||
</div>
|
||||
<div class="sat__dt-qty">
|
||||
<div class="sat__denom-stepper">
|
||||
<button
|
||||
type="button"
|
||||
class="sat__denom-step"
|
||||
(click)="bumpDenom(d, -1)"
|
||||
[disabled]="countFor(d) === 0"
|
||||
[disabled]="countFor(d) === 0 || !d.active"
|
||||
aria-label="Decrease"
|
||||
>
|
||||
−
|
||||
@@ -213,14 +192,79 @@
|
||||
min="0"
|
||||
[value]="countFor(d)"
|
||||
(input)="onDenomChange(d, $any($event.target).value)"
|
||||
[disabled]="!d.active"
|
||||
inputmode="numeric"
|
||||
class="sat__denom-count"
|
||||
/>
|
||||
<button type="button" class="sat__denom-step" (click)="bumpDenom(d, 1)" aria-label="Increase">+</button>
|
||||
<button
|
||||
type="button"
|
||||
class="sat__denom-step"
|
||||
(click)="bumpDenom(d, 1)"
|
||||
[disabled]="!d.active"
|
||||
aria-label="Increase"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="sat__dt-amount" [class.sat__dt-amount--zero]="countFor(d) === 0">
|
||||
₹{{ subtotalFor(d) | number: '1.2-2' }}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Coins -->
|
||||
<div class="sat__dt-section">
|
||||
<span class="sat__dt-section-title">Coins</span>
|
||||
<span class="sat__dt-section-total">₹{{ coinsTotal | number: '1.2-2' }}</span>
|
||||
</div>
|
||||
@for (d of coins; track d.value) {
|
||||
<div
|
||||
class="sat__dt-row"
|
||||
[class.sat__dt-row--active]="countFor(d) > 0"
|
||||
[style.display]="!d.active && !showInactive ? 'none' : null"
|
||||
[style.opacity]="d.active ? null : 0.55"
|
||||
[attr.title]="d.active ? null : 'Discontinued — not active for new deposits'"
|
||||
>
|
||||
<div class="sat__dt-denom">
|
||||
<span class="sat__denom-chip sat__denom-chip--coin sat__denom-chip--circle">{{ d.label }}</span>
|
||||
</div>
|
||||
<div class="sat__dt-qty">
|
||||
<div class="sat__denom-stepper">
|
||||
<button
|
||||
type="button"
|
||||
class="sat__denom-step"
|
||||
(click)="bumpDenom(d, -1)"
|
||||
[disabled]="countFor(d) === 0 || !d.active"
|
||||
aria-label="Decrease"
|
||||
>
|
||||
−
|
||||
</button>
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
[value]="countFor(d)"
|
||||
(input)="onDenomChange(d, $any($event.target).value)"
|
||||
[disabled]="!d.active"
|
||||
inputmode="numeric"
|
||||
class="sat__denom-count"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="sat__denom-step"
|
||||
(click)="bumpDenom(d, 1)"
|
||||
[disabled]="!d.active"
|
||||
aria-label="Increase"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sat__dt-amount" [class.sat__dt-amount--zero]="countFor(d) === 0">
|
||||
₹{{ subtotalFor(d) | number: '1.2-2' }}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Tally + apply -->
|
||||
|
||||
+67
-38
@@ -453,22 +453,42 @@ $red: #b91c1c;
|
||||
}
|
||||
|
||||
/* ─── Denomination groups ─────────────────────────────────────────── */
|
||||
.sat__denom-group {
|
||||
padding: 8px 14px 4px;
|
||||
|
||||
& + & {
|
||||
border-top: 1px solid #f1f5f9;
|
||||
}
|
||||
/* ── Denomination table: Denom · Qty · Amount ── */
|
||||
.sat__dt {
|
||||
padding: 4px 0 6px;
|
||||
}
|
||||
|
||||
.sat__denom-group-head {
|
||||
.sat__dt-head {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 140px 120px;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 2px 16px 8px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: #94a3b8;
|
||||
border-bottom: 1px solid $border;
|
||||
}
|
||||
|
||||
.sat__dt-h-qty {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sat__dt-h-amount {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.sat__dt-section {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
margin-bottom: 5px;
|
||||
padding: 12px 16px 4px;
|
||||
}
|
||||
|
||||
.sat__denom-group-title {
|
||||
.sat__dt-section-title {
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
@@ -476,51 +496,52 @@ $red: #b91c1c;
|
||||
color: $slate;
|
||||
}
|
||||
|
||||
.sat__denom-group-total {
|
||||
.sat__dt-section-total {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: $navy;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.sat__denom-grid {
|
||||
.sat__dt-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
/* ── Single denomination tile ──
|
||||
Two-row layout: top has the denomination chip + running subtotal,
|
||||
bottom has a full-width stepper (− / count / +). Easier to read,
|
||||
gives the cashier obvious tap targets, and never clips on narrow
|
||||
columns. */
|
||||
.sat__denom {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
padding: 6px 9px;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
border: 1px solid $border;
|
||||
transition:
|
||||
border-color 0.15s ease,
|
||||
background 0.15s ease,
|
||||
box-shadow 0.15s ease;
|
||||
min-width: 0;
|
||||
grid-template-columns: 1fr 140px 120px;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 7px 16px;
|
||||
border-bottom: 1px dotted #e2e8f0;
|
||||
transition: background 0.12s ease;
|
||||
|
||||
&--active {
|
||||
border-color: #86efac;
|
||||
background: #f0fdf4;
|
||||
box-shadow: 0 1px 3px -1px rgb(4 120 87 / 18%);
|
||||
}
|
||||
}
|
||||
|
||||
.sat__denom-top {
|
||||
.sat__dt-denom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.sat__dt-qty {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.sat__denom-stepper {
|
||||
width: 130px;
|
||||
}
|
||||
}
|
||||
|
||||
.sat__dt-amount {
|
||||
text-align: right;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: $navy;
|
||||
font-variant-numeric: tabular-nums;
|
||||
|
||||
&--zero {
|
||||
color: #cbd5e1;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.sat__denom-chip {
|
||||
@@ -548,6 +569,14 @@ $red: #b91c1c;
|
||||
color: #854d0e;
|
||||
border: 1px solid #fcd34d;
|
||||
}
|
||||
|
||||
&--circle {
|
||||
min-width: 36px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
padding: 0;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.sat__denom-amount {
|
||||
|
||||
+41
-17
@@ -19,6 +19,7 @@ import { Router, ActivatedRoute, RouterLink } from '@angular/router';
|
||||
|
||||
/** Custom Services */
|
||||
import { SavingsService } from '../../savings.service';
|
||||
import { DenominationMasterService } from 'app/system/denomination-master/denomination-master.service';
|
||||
import { ClientsService } from 'app/clients/clients.service';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { Dates } from 'app/core/utils/dates';
|
||||
@@ -37,6 +38,7 @@ interface Denomination {
|
||||
value: number;
|
||||
kind: 'note' | 'coin';
|
||||
label: string;
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,6 +70,7 @@ export class SavingsAccountTransactionsComponent implements OnInit {
|
||||
private clientsService = inject(ClientsService);
|
||||
private settingsService = inject(SettingsService);
|
||||
private dialog = inject(MatDialog);
|
||||
private denominationMasterService = inject(DenominationMasterService);
|
||||
|
||||
/** Minimum Due Date allowed. */
|
||||
minDate = new Date(2000, 0, 1);
|
||||
@@ -93,24 +96,14 @@ export class SavingsAccountTransactionsComponent implements OnInit {
|
||||
savingAccountId: string;
|
||||
currency: Currency | null = null;
|
||||
|
||||
/** Indian banknote denominations (descending). */
|
||||
readonly notes: Denomination[] = [
|
||||
{ value: 500, kind: 'note', label: '₹500' },
|
||||
{ value: 200, kind: 'note', label: '₹200' },
|
||||
{ value: 100, kind: 'note', label: '₹100' },
|
||||
{ value: 50, kind: 'note', label: '₹50' },
|
||||
{ value: 20, kind: 'note', label: '₹20' },
|
||||
{ value: 10, kind: 'note', label: '₹10' }
|
||||
];
|
||||
/** Banknote denominations — loaded dynamically from the Denomination Master (active only). */
|
||||
notes: Denomination[] = [];
|
||||
|
||||
/** Indian coin denominations (descending). */
|
||||
readonly coins: Denomination[] = [
|
||||
{ value: 20, kind: 'coin', label: '₹20' },
|
||||
{ value: 10, kind: 'coin', label: '₹10' },
|
||||
{ value: 5, kind: 'coin', label: '₹5' },
|
||||
{ value: 2, kind: 'coin', label: '₹2' },
|
||||
{ value: 1, kind: 'coin', label: 'Re 1' }
|
||||
];
|
||||
/** Coin denominations — loaded dynamically from the Denomination Master. */
|
||||
coins: Denomination[] = [];
|
||||
|
||||
/** When false, discontinued (inactive) denominations stay hidden in the grid. */
|
||||
showInactive = false;
|
||||
|
||||
/** Quantity per denomination — keyed by `${kind}:${value}`. */
|
||||
denominationCounts: Record<string, number> = {};
|
||||
@@ -152,9 +145,40 @@ export class SavingsAccountTransactionsComponent implements OnInit {
|
||||
ngOnInit() {
|
||||
this.maxDate = this.settingsService.businessDate;
|
||||
this.createSavingAccountTransactionForm();
|
||||
this.loadDenominations();
|
||||
this.loadContextStrip();
|
||||
}
|
||||
|
||||
/** Load active denominations from the Denomination Master so the cash
|
||||
* breakdown grid reflects the bank's current configuration. */
|
||||
private loadDenominations(): void {
|
||||
this.denominationMasterService.getAll().subscribe({
|
||||
next: (list: any[]) => {
|
||||
const rows: Denomination[] = (list || []).map((d) => ({
|
||||
value: d.denomination,
|
||||
kind: d.kind as 'note' | 'coin',
|
||||
label: '₹' + d.denomination,
|
||||
active: !!d.isActive
|
||||
}));
|
||||
const order = (a: Denomination, b: Denomination) => b.value - a.value;
|
||||
this.notes = rows.filter((r) => r.kind === 'note').sort(order);
|
||||
this.coins = rows.filter((r) => r.kind === 'coin').sort(order);
|
||||
},
|
||||
error: () => {
|
||||
this.notes = [];
|
||||
this.coins = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** True when at least one discontinued denomination exists. */
|
||||
get hasInactive(): boolean {
|
||||
return [
|
||||
...this.notes,
|
||||
...this.coins
|
||||
].some((d) => !d.active);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull the savings account + client to populate the cashier's context
|
||||
* strip (account number, client name, KYC status, risk level, same-day
|
||||
|
||||
@@ -104,155 +104,172 @@
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Cash movement (Deposit / Withdrawal / Hold Amount) -->
|
||||
<div class="sav-menu__group">
|
||||
<div class="sav-menu__group-title">Transact</div>
|
||||
@for (item of buttonConfig.singleButtons; track item.name) {
|
||||
@if (kindOf(item.name) === 'transact') {
|
||||
<button
|
||||
mat-menu-item
|
||||
class="sav-menu__item sav-menu__item--transact"
|
||||
*mifosxHasPermission="item.taskPermissionName"
|
||||
(click)="doAction(item.name)"
|
||||
>
|
||||
<span class="sav-menu__ico">
|
||||
<fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon>
|
||||
</span>
|
||||
<span class="sav-menu__label">{{ 'labels.menus.' + item.name | translate }}</span>
|
||||
</button>
|
||||
}
|
||||
<div class="sav-menu__body">
|
||||
<!-- Transact: singleButtons + options of kind 'transact' -->
|
||||
@if (hasKind('transact')) {
|
||||
<div class="sav-menu__group">
|
||||
<div class="sav-menu__group-title">Transact</div>
|
||||
@for (item of buttonConfig.singleButtons; track item.name) {
|
||||
@if (kindOf(item.name) === 'transact') {
|
||||
<button
|
||||
mat-menu-item
|
||||
class="sav-menu__item sav-menu__item--transact"
|
||||
*mifosxHasPermission="item.taskPermissionName"
|
||||
(click)="doAction(item.name)"
|
||||
>
|
||||
<span class="sav-menu__ico"><fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon></span>
|
||||
<span class="sav-menu__label">{{ 'labels.menus.' + item.name | translate }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
@for (option of buttonConfig.options; track option.name) {
|
||||
@if (kindOf(option.name) === 'transact') {
|
||||
<button
|
||||
mat-menu-item
|
||||
class="sav-menu__item sav-menu__item--transact"
|
||||
*mifosxHasPermission="option.taskPermissionName"
|
||||
(click)="doAction(option.name)"
|
||||
>
|
||||
<span class="sav-menu__ico"><fa-icon [icon]="iconOf(option.name)"></fa-icon></span>
|
||||
<span class="sav-menu__label">{{ 'labels.menus.' + option.name | translate }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Restrict: singleButtons + options of kind 'block' -->
|
||||
@if (hasKind('block')) {
|
||||
<div class="sav-menu__group">
|
||||
<div class="sav-menu__group-title">Restrict</div>
|
||||
@for (item of buttonConfig.singleButtons; track item.name) {
|
||||
@if (kindOf(item.name) === 'block') {
|
||||
<button
|
||||
mat-menu-item
|
||||
class="sav-menu__item sav-menu__item--block"
|
||||
*mifosxHasPermission="item.taskPermissionName"
|
||||
(click)="doAction(item.name)"
|
||||
>
|
||||
<span class="sav-menu__ico"><fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon></span>
|
||||
<span class="sav-menu__label">{{ 'labels.menus.' + item.name | translate }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
@for (option of buttonConfig.options; track option.name) {
|
||||
@if (kindOf(option.name) === 'block') {
|
||||
<button
|
||||
mat-menu-item
|
||||
class="sav-menu__item sav-menu__item--block"
|
||||
*mifosxHasPermission="option.taskPermissionName"
|
||||
(click)="doAction(option.name)"
|
||||
>
|
||||
<span class="sav-menu__ico"><fa-icon [icon]="iconOf(option.name)"></fa-icon></span>
|
||||
<span class="sav-menu__label">{{ 'labels.menus.' + option.name | translate }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Interest: singleButtons + options of kind 'interest' -->
|
||||
@if (hasKind('interest')) {
|
||||
<div class="sav-menu__group">
|
||||
<div class="sav-menu__group-title">Interest</div>
|
||||
@for (item of buttonConfig.singleButtons; track item.name) {
|
||||
@if (kindOf(item.name) === 'interest') {
|
||||
<button
|
||||
mat-menu-item
|
||||
class="sav-menu__item sav-menu__item--interest"
|
||||
*mifosxHasPermission="item.taskPermissionName"
|
||||
(click)="doAction(item.name)"
|
||||
>
|
||||
<span class="sav-menu__ico"><fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon></span>
|
||||
<span class="sav-menu__label">{{ 'labels.menus.' + item.name | translate }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
@for (option of buttonConfig.options; track option.name) {
|
||||
@if (kindOf(option.name) === 'interest') {
|
||||
<button
|
||||
mat-menu-item
|
||||
class="sav-menu__item sav-menu__item--interest"
|
||||
*mifosxHasPermission="option.taskPermissionName"
|
||||
(click)="doAction(option.name)"
|
||||
>
|
||||
<span class="sav-menu__ico"><fa-icon [icon]="iconOf(option.name)"></fa-icon></span>
|
||||
<span class="sav-menu__label">{{ 'labels.menus.' + option.name | translate }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Admin: singleButtons + options of kind 'admin', hidden when blocked -->
|
||||
@if (hasKind('admin') && !savingsAccountData.subStatus.block) {
|
||||
<div class="sav-menu__group">
|
||||
<div class="sav-menu__group-title">Admin</div>
|
||||
@for (item of buttonConfig.singleButtons; track item.name) {
|
||||
@if (kindOf(item.name) === 'admin') {
|
||||
<button
|
||||
mat-menu-item
|
||||
class="sav-menu__item sav-menu__item--admin"
|
||||
*mifosxHasPermission="item.taskPermissionName"
|
||||
(click)="doAction(item.name)"
|
||||
>
|
||||
<span class="sav-menu__ico"><fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon></span>
|
||||
<span class="sav-menu__label">{{ 'labels.menus.' + item.name | translate }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
@for (option of buttonConfig.options; track option.name) {
|
||||
@if (kindOf(option.name) === 'admin') {
|
||||
<button
|
||||
mat-menu-item
|
||||
class="sav-menu__item sav-menu__item--admin"
|
||||
*mifosxHasPermission="option.taskPermissionName"
|
||||
(click)="doAction(option.name)"
|
||||
>
|
||||
<span class="sav-menu__ico"><fa-icon [icon]="iconOf(option.name)"></fa-icon></span>
|
||||
<span class="sav-menu__label">{{ 'labels.menus.' + option.name | translate }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Danger zone: singleButtons + options of kind 'danger' -->
|
||||
@if (hasKind('danger')) {
|
||||
<div class="sav-menu__group sav-menu__group--danger">
|
||||
<div class="sav-menu__group-title">Danger zone</div>
|
||||
@for (item of buttonConfig.singleButtons; track item.name) {
|
||||
@if (kindOf(item.name) === 'danger') {
|
||||
<button
|
||||
mat-menu-item
|
||||
class="sav-menu__item sav-menu__item--danger"
|
||||
*mifosxHasPermission="item.taskPermissionName"
|
||||
(click)="doAction(item.name)"
|
||||
>
|
||||
<span class="sav-menu__ico"><fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon></span>
|
||||
<span class="sav-menu__label">{{ 'labels.menus.' + item.name | translate }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
@for (option of buttonConfig.options; track option.name) {
|
||||
@if (kindOf(option.name) === 'danger') {
|
||||
<button
|
||||
mat-menu-item
|
||||
class="sav-menu__item sav-menu__item--danger"
|
||||
*mifosxHasPermission="option.taskPermissionName"
|
||||
(click)="doAction(option.name)"
|
||||
>
|
||||
<span class="sav-menu__ico"><fa-icon [icon]="iconOf(option.name)"></fa-icon></span>
|
||||
<span class="sav-menu__label">{{ 'labels.menus.' + option.name | translate }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Restrictions (Block / Unblock variants) -->
|
||||
<div class="sav-menu__group">
|
||||
<div class="sav-menu__group-title">Restrict</div>
|
||||
@for (item of buttonConfig.singleButtons; track item.name) {
|
||||
@if (kindOf(item.name) === 'block') {
|
||||
<button
|
||||
mat-menu-item
|
||||
class="sav-menu__item sav-menu__item--block"
|
||||
*mifosxHasPermission="item.taskPermissionName"
|
||||
(click)="doAction(item.name)"
|
||||
>
|
||||
<span class="sav-menu__ico">
|
||||
<fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon>
|
||||
</span>
|
||||
<span class="sav-menu__label">{{ 'labels.menus.' + item.name | translate }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Interest -->
|
||||
<div class="sav-menu__group">
|
||||
<div class="sav-menu__group-title">Interest</div>
|
||||
@for (item of buttonConfig.singleButtons; track item.name) {
|
||||
@if (kindOf(item.name) === 'interest') {
|
||||
<button
|
||||
mat-menu-item
|
||||
class="sav-menu__item sav-menu__item--interest"
|
||||
*mifosxHasPermission="item.taskPermissionName"
|
||||
(click)="doAction(item.name)"
|
||||
>
|
||||
<span class="sav-menu__ico">
|
||||
<fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon>
|
||||
</span>
|
||||
<span class="sav-menu__label">{{ 'labels.menus.' + item.name | translate }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Admin (approval/staff/charges) — drawn from BOTH primary
|
||||
buttons and the historical "More" options. -->
|
||||
@if (buttonConfig.options && !savingsAccountData.subStatus.block) {
|
||||
<div class="sav-menu__group">
|
||||
<div class="sav-menu__group-title">Admin</div>
|
||||
@for (item of buttonConfig.singleButtons; track item.name) {
|
||||
@if (kindOf(item.name) === 'admin') {
|
||||
<button
|
||||
mat-menu-item
|
||||
class="sav-menu__item sav-menu__item--admin"
|
||||
*mifosxHasPermission="item.taskPermissionName"
|
||||
(click)="doAction(item.name)"
|
||||
>
|
||||
<span class="sav-menu__ico">
|
||||
<fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon>
|
||||
</span>
|
||||
<span class="sav-menu__label">{{ 'labels.menus.' + item.name | translate }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
@for (option of buttonConfig.options; track option.name) {
|
||||
@if (kindOf(option.name) === 'admin') {
|
||||
<button
|
||||
mat-menu-item
|
||||
class="sav-menu__item sav-menu__item--admin"
|
||||
*mifosxHasPermission="option.taskPermissionName"
|
||||
(click)="doAction(option.name)"
|
||||
>
|
||||
<span class="sav-menu__ico">
|
||||
<fa-icon [icon]="iconOf(option.name)"></fa-icon>
|
||||
</span>
|
||||
<span class="sav-menu__label">{{ 'labels.menus.' + option.name | translate }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
@for (option of buttonConfig.options; track option.name) {
|
||||
@if (kindOf(option.name) === 'interest') {
|
||||
<button
|
||||
mat-menu-item
|
||||
class="sav-menu__item sav-menu__item--interest"
|
||||
*mifosxHasPermission="option.taskPermissionName"
|
||||
(click)="doAction(option.name)"
|
||||
>
|
||||
<span class="sav-menu__ico">
|
||||
<fa-icon [icon]="iconOf(option.name)"></fa-icon>
|
||||
</span>
|
||||
<span class="sav-menu__label">{{ 'labels.menus.' + option.name | translate }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
@for (option of buttonConfig.options; track option.name) {
|
||||
@if (kindOf(option.name) === 'transact') {
|
||||
<button
|
||||
mat-menu-item
|
||||
class="sav-menu__item sav-menu__item--transact"
|
||||
*mifosxHasPermission="option.taskPermissionName"
|
||||
(click)="doAction(option.name)"
|
||||
>
|
||||
<span class="sav-menu__ico">
|
||||
<fa-icon [icon]="iconOf(option.name)"></fa-icon>
|
||||
</span>
|
||||
<span class="sav-menu__label">{{ 'labels.menus.' + option.name | translate }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Danger (Close / Reject / Delete) -->
|
||||
@if (buttonConfig.options && !savingsAccountData.subStatus.block) {
|
||||
<div class="sav-menu__group sav-menu__group--danger">
|
||||
<div class="sav-menu__group-title">Danger zone</div>
|
||||
@for (option of buttonConfig.options; track option.name) {
|
||||
@if (kindOf(option.name) === 'danger') {
|
||||
<button
|
||||
mat-menu-item
|
||||
class="sav-menu__item sav-menu__item--danger"
|
||||
*mifosxHasPermission="option.taskPermissionName"
|
||||
(click)="doAction(option.name)"
|
||||
>
|
||||
<span class="sav-menu__ico">
|
||||
<fa-icon [icon]="iconOf(option.name)"></fa-icon>
|
||||
</span>
|
||||
<span class="sav-menu__label">{{ 'labels.menus.' + option.name | translate }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</mat-menu>
|
||||
</header>
|
||||
|
||||
@@ -170,6 +170,14 @@ export class SavingsAccountViewComponent implements OnInit {
|
||||
return this.ACTION_ICON[name] ?? fallback ?? 'circle';
|
||||
}
|
||||
|
||||
hasKind(kind: 'transact' | 'block' | 'interest' | 'admin' | 'danger'): boolean {
|
||||
const all = [
|
||||
...(this.buttonConfig?.singleButtons ?? []),
|
||||
...(this.buttonConfig?.options ?? [])
|
||||
];
|
||||
return all.some((item) => this.kindOf(item.name) === kind);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches savings account data from `resolve`
|
||||
* @param {ActivatedRoute} route Activated Route
|
||||
|
||||
@@ -16,6 +16,10 @@ export class DenominationMasterService {
|
||||
return this.http.get<any[]>('/denominationmaster');
|
||||
}
|
||||
|
||||
getActive() {
|
||||
return this.http.get<any[]>('/denominationmaster/active');
|
||||
}
|
||||
|
||||
create(data: any) {
|
||||
return this.http.post('/denominationmaster', data);
|
||||
}
|
||||
|
||||
@@ -1061,12 +1061,34 @@ mat-card {
|
||||
|
||||
.sav-action-menu .mat-mdc-menu-content {
|
||||
padding: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.sav-menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: Inter, 'DM Sans', Roboto, sans-serif;
|
||||
max-height: 440px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sav-menu__body {
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #cbd5e1 transparent;
|
||||
}
|
||||
|
||||
.sav-menu__body::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.sav-menu__body::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.sav-menu__body::-webkit-scrollbar-thumb {
|
||||
background: #cbd5e1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.sav-menu__head {
|
||||
|
||||
Reference in New Issue
Block a user