diff --git a/src/app/current-accounts/current-account-actions/current-account-transactions/current-account-transactions.component.html b/src/app/current-accounts/current-account-actions/current-account-transactions/current-account-transactions.component.html index eb87f5fa6..9d3973bb7 100644 --- a/src/app/current-accounts/current-account-actions/current-account-transactions/current-account-transactions.component.html +++ b/src/app/current-accounts/current-account-actions/current-account-transactions/current-account-transactions.component.html @@ -142,68 +142,47 @@

Cash Denomination Breakdown

+ @if (hasInactive) { + + } - -
-
- Banknotes - ₹{{ notesTotal | number: '1.2-2' }} + +
+
+ Denom + Qty + Amount
-
- @for (d of notes; track d.value) { -
-
- {{ d.label }} - ₹{{ subtotalFor(d) | number: '1.2-2' }} -
-
- - - -
-
- } -
-
- -
-
- Coins - ₹{{ coinsTotal | number: '1.2-2' }} + +
+ Bank Notes + ₹{{ notesTotal | number: '1.2-2' }}
-
- @for (d of coins; track d.value) { -
-
- {{ d.label }} - ₹{{ subtotalFor(d) | number: '1.2-2' }} -
+ @for (d of notes; track d.value) { +
+
+ {{ d.label }} +
+
+
- } +
+ ₹{{ subtotalFor(d) | number: '1.2-2' }} +
+
+ } + + +
+ Coins + ₹{{ coinsTotal | number: '1.2-2' }}
+ @for (d of coins; track d.value) { +
+
+ {{ d.label }} +
+
+
+ + + +
+
+
+ ₹{{ subtotalFor(d) | number: '1.2-2' }} +
+
+ }
diff --git a/src/app/current-accounts/current-account-actions/current-account-transactions/current-account-transactions.component.scss b/src/app/current-accounts/current-account-actions/current-account-transactions/current-account-transactions.component.scss index 8d1c6a0cf..1c7f11f6d 100644 --- a/src/app/current-accounts/current-account-actions/current-account-transactions/current-account-transactions.component.scss +++ b/src/app/current-accounts/current-account-actions/current-account-transactions/current-account-transactions.component.scss @@ -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 { diff --git a/src/app/current-accounts/current-account-actions/current-account-transactions/current-account-transactions.component.ts b/src/app/current-accounts/current-account-actions/current-account-transactions/current-account-transactions.component.ts index 09cad551f..fe99882ae 100644 --- a/src/app/current-accounts/current-account-actions/current-account-transactions/current-account-transactions.component.ts +++ b/src/app/current-accounts/current-account-actions/current-account-transactions/current-account-transactions.component.ts @@ -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 = {}; @@ -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({ diff --git a/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/fixed-deposits-cash-transaction/fixed-deposits-cash-transaction.component.html b/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/fixed-deposits-cash-transaction/fixed-deposits-cash-transaction.component.html index c9074136e..fa6b7df27 100644 --- a/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/fixed-deposits-cash-transaction/fixed-deposits-cash-transaction.component.html +++ b/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/fixed-deposits-cash-transaction/fixed-deposits-cash-transaction.component.html @@ -116,66 +116,47 @@

Cash Denomination Breakdown

+ @if (hasInactive) { + + } -
-
- Banknotes - ₹{{ notesTotal | number: '1.2-2' }} + +
+
+ Denom + Qty + Amount
-
- @for (d of notes; track d.value) { -
-
- {{ d.label }} - ₹{{ subtotalFor(d) | number: '1.2-2' }} -
-
- - - -
-
- } -
-
-
-
- Coins - ₹{{ coinsTotal | number: '1.2-2' }} + +
+ Bank Notes + ₹{{ notesTotal | number: '1.2-2' }}
-
- @for (d of coins; track d.value) { -
-
- {{ d.label }} - ₹{{ subtotalFor(d) | number: '1.2-2' }} -
+ @for (d of notes; track d.value) { +
+
+ {{ d.label }} +
+
+
- } +
+ ₹{{ subtotalFor(d) | number: '1.2-2' }} +
+
+ } + + +
+ Coins + ₹{{ coinsTotal | number: '1.2-2' }}
+ @for (d of coins; track d.value) { +
+
+ {{ d.label }} +
+
+
+ + + +
+
+
+ ₹{{ subtotalFor(d) | number: '1.2-2' }} +
+
+ }
diff --git a/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/fixed-deposits-cash-transaction/fixed-deposits-cash-transaction.component.scss b/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/fixed-deposits-cash-transaction/fixed-deposits-cash-transaction.component.scss index d7810f153..521810253 100644 --- a/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/fixed-deposits-cash-transaction/fixed-deposits-cash-transaction.component.scss +++ b/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/fixed-deposits-cash-transaction/fixed-deposits-cash-transaction.component.scss @@ -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 { diff --git a/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/fixed-deposits-cash-transaction/fixed-deposits-cash-transaction.component.ts b/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/fixed-deposits-cash-transaction/fixed-deposits-cash-transaction.component.ts index 77a72c092..a6698c0e2 100644 --- a/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/fixed-deposits-cash-transaction/fixed-deposits-cash-transaction.component.ts +++ b/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/fixed-deposits-cash-transaction/fixed-deposits-cash-transaction.component.ts @@ -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 = {}; @@ -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({ diff --git a/src/app/deposits/recurring-deposits/recurring-deposits-account-actions/deposit-recurring-deposits-account/deposit-recurring-deposits-account.component.html b/src/app/deposits/recurring-deposits/recurring-deposits-account-actions/deposit-recurring-deposits-account/deposit-recurring-deposits-account.component.html index 7f167a47d..f4d2869da 100644 --- a/src/app/deposits/recurring-deposits/recurring-deposits-account-actions/deposit-recurring-deposits-account/deposit-recurring-deposits-account.component.html +++ b/src/app/deposits/recurring-deposits/recurring-deposits-account-actions/deposit-recurring-deposits-account/deposit-recurring-deposits-account.component.html @@ -116,66 +116,47 @@

Cash Denomination Breakdown

+ @if (hasInactive) { + + } -
-
- Banknotes - ₹{{ notesTotal | number: '1.2-2' }} + +
+
+ Denom + Qty + Amount
-
- @for (d of notes; track d.value) { -
-
- {{ d.label }} - ₹{{ subtotalFor(d) | number: '1.2-2' }} -
-
- - - -
-
- } -
-
-
-
- Coins - ₹{{ coinsTotal | number: '1.2-2' }} + +
+ Bank Notes + ₹{{ notesTotal | number: '1.2-2' }}
-
- @for (d of coins; track d.value) { -
-
- {{ d.label }} - ₹{{ subtotalFor(d) | number: '1.2-2' }} -
+ @for (d of notes; track d.value) { +
+
+ {{ d.label }} +
+
+
- } +
+ ₹{{ subtotalFor(d) | number: '1.2-2' }} +
+
+ } + + +
+ Coins + ₹{{ coinsTotal | number: '1.2-2' }}
+ @for (d of coins; track d.value) { +
+
+ {{ d.label }} +
+
+
+ + + +
+
+
+ ₹{{ subtotalFor(d) | number: '1.2-2' }} +
+
+ }
diff --git a/src/app/deposits/recurring-deposits/recurring-deposits-account-actions/deposit-recurring-deposits-account/deposit-recurring-deposits-account.component.scss b/src/app/deposits/recurring-deposits/recurring-deposits-account-actions/deposit-recurring-deposits-account/deposit-recurring-deposits-account.component.scss index d7810f153..521810253 100644 --- a/src/app/deposits/recurring-deposits/recurring-deposits-account-actions/deposit-recurring-deposits-account/deposit-recurring-deposits-account.component.scss +++ b/src/app/deposits/recurring-deposits/recurring-deposits-account-actions/deposit-recurring-deposits-account/deposit-recurring-deposits-account.component.scss @@ -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 { diff --git a/src/app/deposits/recurring-deposits/recurring-deposits-account-actions/deposit-recurring-deposits-account/deposit-recurring-deposits-account.component.ts b/src/app/deposits/recurring-deposits/recurring-deposits-account-actions/deposit-recurring-deposits-account/deposit-recurring-deposits-account.component.ts index 3afdc3cd1..a22dea89b 100644 --- a/src/app/deposits/recurring-deposits/recurring-deposits-account-actions/deposit-recurring-deposits-account/deposit-recurring-deposits-account.component.ts +++ b/src/app/deposits/recurring-deposits/recurring-deposits-account-actions/deposit-recurring-deposits-account/deposit-recurring-deposits-account.component.ts @@ -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 = {}; @@ -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({ diff --git a/src/app/savings/saving-account-actions/savings-account-transactions/savings-account-transactions.component.html b/src/app/savings/saving-account-actions/savings-account-transactions/savings-account-transactions.component.html index 1a47c22ca..a2b686307 100644 --- a/src/app/savings/saving-account-actions/savings-account-transactions/savings-account-transactions.component.html +++ b/src/app/savings/saving-account-actions/savings-account-transactions/savings-account-transactions.component.html @@ -142,68 +142,47 @@

Cash Denomination Breakdown

+ @if (hasInactive) { + + } - -
-
- Banknotes - ₹{{ notesTotal | number: '1.2-2' }} + +
+
+ Denom + Qty + Amount
-
- @for (d of notes; track d.value) { -
-
- {{ d.label }} - ₹{{ subtotalFor(d) | number: '1.2-2' }} -
-
- - - -
-
- } -
-
- -
-
- Coins - ₹{{ coinsTotal | number: '1.2-2' }} + +
+ Bank Notes + ₹{{ notesTotal | number: '1.2-2' }}
-
- @for (d of coins; track d.value) { -
-
- {{ d.label }} - ₹{{ subtotalFor(d) | number: '1.2-2' }} -
+ @for (d of notes; track d.value) { +
+
+ {{ d.label }} +
+
+
- } +
+ ₹{{ subtotalFor(d) | number: '1.2-2' }} +
+
+ } + + +
+ Coins + ₹{{ coinsTotal | number: '1.2-2' }}
+ @for (d of coins; track d.value) { +
+
+ {{ d.label }} +
+
+
+ + + +
+
+
+ ₹{{ subtotalFor(d) | number: '1.2-2' }} +
+
+ }
diff --git a/src/app/savings/saving-account-actions/savings-account-transactions/savings-account-transactions.component.scss b/src/app/savings/saving-account-actions/savings-account-transactions/savings-account-transactions.component.scss index dfe99b06d..2bce2c4d2 100644 --- a/src/app/savings/saving-account-actions/savings-account-transactions/savings-account-transactions.component.scss +++ b/src/app/savings/saving-account-actions/savings-account-transactions/savings-account-transactions.component.scss @@ -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 { diff --git a/src/app/savings/saving-account-actions/savings-account-transactions/savings-account-transactions.component.ts b/src/app/savings/saving-account-actions/savings-account-transactions/savings-account-transactions.component.ts index a15a24803..e9dbeed9c 100644 --- a/src/app/savings/saving-account-actions/savings-account-transactions/savings-account-transactions.component.ts +++ b/src/app/savings/saving-account-actions/savings-account-transactions/savings-account-transactions.component.ts @@ -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 = {}; @@ -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 diff --git a/src/app/savings/savings-account-view/savings-account-view.component.html b/src/app/savings/savings-account-view/savings-account-view.component.html index a9db2e800..c29a4f77e 100644 --- a/src/app/savings/savings-account-view/savings-account-view.component.html +++ b/src/app/savings/savings-account-view/savings-account-view.component.html @@ -104,155 +104,172 @@
- -
-
Transact
- @for (item of buttonConfig.singleButtons; track item.name) { - @if (kindOf(item.name) === 'transact') { - - } +
+ + @if (hasKind('transact')) { +
+
Transact
+ @for (item of buttonConfig.singleButtons; track item.name) { + @if (kindOf(item.name) === 'transact') { + + } + } + @for (option of buttonConfig.options; track option.name) { + @if (kindOf(option.name) === 'transact') { + + } + } +
+ } + + + @if (hasKind('block')) { +
+
Restrict
+ @for (item of buttonConfig.singleButtons; track item.name) { + @if (kindOf(item.name) === 'block') { + + } + } + @for (option of buttonConfig.options; track option.name) { + @if (kindOf(option.name) === 'block') { + + } + } +
+ } + + + @if (hasKind('interest')) { +
+
Interest
+ @for (item of buttonConfig.singleButtons; track item.name) { + @if (kindOf(item.name) === 'interest') { + + } + } + @for (option of buttonConfig.options; track option.name) { + @if (kindOf(option.name) === 'interest') { + + } + } +
+ } + + + @if (hasKind('admin') && !savingsAccountData.subStatus.block) { +
+
Admin
+ @for (item of buttonConfig.singleButtons; track item.name) { + @if (kindOf(item.name) === 'admin') { + + } + } + @for (option of buttonConfig.options; track option.name) { + @if (kindOf(option.name) === 'admin') { + + } + } +
+ } + + + @if (hasKind('danger')) { +
+
Danger zone
+ @for (item of buttonConfig.singleButtons; track item.name) { + @if (kindOf(item.name) === 'danger') { + + } + } + @for (option of buttonConfig.options; track option.name) { + @if (kindOf(option.name) === 'danger') { + + } + } +
}
- - -
-
Restrict
- @for (item of buttonConfig.singleButtons; track item.name) { - @if (kindOf(item.name) === 'block') { - - } - } -
- - -
-
Interest
- @for (item of buttonConfig.singleButtons; track item.name) { - @if (kindOf(item.name) === 'interest') { - - } - } -
- - - @if (buttonConfig.options && !savingsAccountData.subStatus.block) { -
-
Admin
- @for (item of buttonConfig.singleButtons; track item.name) { - @if (kindOf(item.name) === 'admin') { - - } - } - @for (option of buttonConfig.options; track option.name) { - @if (kindOf(option.name) === 'admin') { - - } - } - @for (option of buttonConfig.options; track option.name) { - @if (kindOf(option.name) === 'interest') { - - } - } - @for (option of buttonConfig.options; track option.name) { - @if (kindOf(option.name) === 'transact') { - - } - } -
- } - - - @if (buttonConfig.options && !savingsAccountData.subStatus.block) { -
-
Danger zone
- @for (option of buttonConfig.options; track option.name) { - @if (kindOf(option.name) === 'danger') { - - } - } -
- }
diff --git a/src/app/savings/savings-account-view/savings-account-view.component.ts b/src/app/savings/savings-account-view/savings-account-view.component.ts index 5739366fe..8d1603aa0 100644 --- a/src/app/savings/savings-account-view/savings-account-view.component.ts +++ b/src/app/savings/savings-account-view/savings-account-view.component.ts @@ -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 diff --git a/src/app/system/denomination-master/denomination-master.service.ts b/src/app/system/denomination-master/denomination-master.service.ts index 6b037b0ad..cd9797f5f 100644 --- a/src/app/system/denomination-master/denomination-master.service.ts +++ b/src/app/system/denomination-master/denomination-master.service.ts @@ -16,6 +16,10 @@ export class DenominationMasterService { return this.http.get('/denominationmaster'); } + getActive() { + return this.http.get('/denominationmaster/active'); + } + create(data: any) { return this.http.post('/denominationmaster', data); } diff --git a/src/main.scss b/src/main.scss index d09f99259..caea15a1a 100644 --- a/src/main.scss +++ b/src/main.scss @@ -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 {