feat(cashier): add full-screen cashier teller workstation dashboard

- Add Transaction Entry screen with full account details panel
  (personal info, joint holders, nominees, KYC, balances)
- Role-based layout: CASHIER role hides nav/sidebar/ticker,
  redirects to /cashier/transaction-entry on login
- Denomination entry with opening/closing position preview
  (notes first then coins, stepper +/- round button inputs)
- Auto-fetch savings/current/FD/RD accounts with associations,
  client KYC, nominees, addresses, family members (S/D/W/H/O)
- Submit deposit/withdrawal via POST /transactions with denominationBreakdown
- Cashier position auto-loads for CASHIER role (no filter dropdowns)
- Pagination added to cash-history tabs and currency-exchange-detail
- Cashier menu simplified for cashier role permissions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jaanu423
2026-06-05 10:59:34 +05:30
co-authored by Claude Sonnet 4.6
parent c8e9ab8f84
commit 9094b9a0eb
18 changed files with 2828 additions and 190 deletions
@@ -18,34 +18,44 @@
</header>
<div class="pg-toolbar pg-toolbar--mat">
<mat-form-field appearance="outline" subscriptSizing="dynamic">
<mat-label>Teller (optional)</mat-label>
<mat-select [value]="selectedTellerId" (selectionChange)="onTellerSelect($event.value)">
<mat-option [value]="null">All Tellers</mat-option>
@for (t of tellers; track t.id) {
<mat-option [value]="t.id">{{ t.name }}</mat-option>
}
</mat-select>
</mat-form-field>
<!-- Teller + Cashier filters — hidden for cashier role (auto-loads own position) -->
@if (!isCashierMode) {
<mat-form-field appearance="outline" subscriptSizing="dynamic">
<mat-label>Teller (optional)</mat-label>
<mat-select [value]="selectedTellerId" (selectionChange)="onTellerSelect($event.value)">
<mat-option [value]="null">All Tellers</mat-option>
@for (t of tellers; track t.id) {
<mat-option [value]="t.id">{{ t.name }}</mat-option>
}
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline" subscriptSizing="dynamic">
<mat-label>Cashier (optional)</mat-label>
<mat-select [value]="selectedCashierId" (selectionChange)="selectedCashierId = $event.value">
<mat-option [value]="null">All Cashiers</mat-option>
@for (c of cashiers; track c.id) {
<mat-option [value]="c.id">{{ c.label }}</mat-option>
}
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline" subscriptSizing="dynamic">
<mat-label>Cashier (optional)</mat-label>
<mat-select [value]="selectedCashierId" (selectionChange)="selectedCashierId = $event.value">
<mat-option [value]="null">All Cashiers</mat-option>
@for (c of cashiers; track c.id) {
<mat-option [value]="c.id">{{ c.label }}</mat-option>
}
</mat-select>
</mat-form-field>
}
<mat-form-field appearance="outline" subscriptSizing="dynamic">
<mat-label>As of Date</mat-label>
<input matInput type="date" [value]="asOfDate" (change)="asOfDate = $any($event.target).value" />
</mat-form-field>
<button type="button" class="pg-btn pg-btn--primary" (click)="load()" [disabled]="loading">
<mat-icon>bar_chart</mat-icon> Load Position
</button>
@if (isCashierMode) {
<!-- Cashier mode: just a refresh button -->
<button type="button" class="pg-btn pg-btn--primary" (click)="loadMe()" [disabled]="loading">
<mat-icon>refresh</mat-icon> Refresh
</button>
} @else {
<button type="button" class="pg-btn pg-btn--primary" (click)="load()" [disabled]="loading">
<mat-icon>bar_chart</mat-icon> Load Position
</button>
}
</div>
@if (errorMsg) {