Files
cbsnexora/src/app/cashier/transaction-entry/transaction-entry.component.html
T
Jaanu423andClaude Sonnet 5 ed2a40a8bf
build-run / Run Lint, Build and Deploy (push) Canceled after 0s
Publish Image in Docker Hub / build (push) Canceled after 0s
Playwright E2E Tests / Run Playwright Tests (push) Canceled after 0s
Tests / testRigor (push) Canceled after 0s
fix(chequebook,cashier): surface real hold/lien amount instead of fake placeholders
- Savings account general tab now shows the real lien/user hold
  (savingsAmountOnHold) alongside the existing guarantor hold (onHoldFunds).
- Cashier transaction-entry "Transfer Hold" / "Tr. Hold" now bind to the real
  freezeAmount instead of a hardcoded 0.00 placeholder; "Freeze Amount" itself
  reverted to 0.00 per request. withdrawableAmount no longer double-subtracts
  the hold for savings accounts (whose availableBalance is already
  hold-adjusted server-side), while current accounts (whose availableBalance
  is not) still get the client-side subtraction they need.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 15:06:58 +05:30

1715 lines
78 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!--
Copyright since 2025 Mifos Initiative
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<div class="te-root">
<!-- ── Clerk edit: rejection reason banner ───────────────────────────── -->
@if (editMode && editRequest?.rejection_reason) {
<div class="te-reject-banner">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10" />
<line x1="12" y1="8" x2="12" y2="12" />
<line x1="12" y1="16" x2="12.01" y2="16" />
</svg>
<div>
<strong>This request was rejected by the manager.</strong>
<span>Reason: {{ editRequest.rejection_reason }}</span>
<small>Update the details below and click “Resubmit for Approval”.</small>
</div>
</div>
}
<!-- ── Cashier Day-Status Blocking Banners ───────────────────────────── -->
@if (previousDayUnsettled) {
<div class="te-day-block te-day-block--danger">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<circle cx="12" cy="12" r="10" />
<line x1="12" y1="8" x2="12" y2="12" />
<line x1="12" y1="16" x2="12.01" y2="16" />
</svg>
<span>
<strong>TRANSACTIONS BLOCKED</strong> — Previous day cash settlement is pending
@if (unsettledSince) {
<em>(since {{ unsettledSince }})</em>
}
. Complete Cash Settlement before processing any transactions.
</span>
</div>
}
@if (!morningTransferReceived && !previousDayUnsettled) {
<div class="te-day-block te-day-block--warning">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
<line x1="12" y1="9" x2="12" y2="13" />
<line x1="12" y1="17" x2="12.01" y2="17" />
</svg>
<span>
<strong>NO CASH ALLOCATED</strong> — Morning transfer not received. Request a morning transfer from the vault
before processing cash transactions.
</span>
</div>
}
<!-- ── ENTRY GRID shown when no account loaded yet ────────────────── -->
@if (!accountDetails && !loadingAccount) {
<div class="ws-wrap">
<!-- ── Header ── -->
<div class="ws-header">
<div class="ws-header__left">
<div class="ws-header__icon">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="2" y="3" width="20" height="14" rx="2" />
<line x1="8" y1="21" x2="16" y2="21" />
<line x1="12" y1="17" x2="12" y2="21" />
</svg>
</div>
<span class="ws-header__title">Transaction Entry</span>
</div>
<div class="ws-header__right">
<span class="ws-header__effdt">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="4" width="18" height="18" rx="2" />
<line x1="16" y1="2" x2="16" y2="6" />
<line x1="8" y1="2" x2="8" y2="6" />
<line x1="3" y1="10" x2="21" y2="10" />
</svg>
Eff. Date
<input class="ws-header__date" type="date" [value]="todayIso" readonly />
</span>
</div>
</div>
<!-- ── Row cards ── -->
<div class="ws-rows">
@for (row of rows; track $index; let i = $index) {
<div class="ws-card" [class.ws-card--active]="selectedRow === row" (click)="onRowSelect(row, i)">
<div class="ws-card__badge">{{ i + 1 }}</div>
@if (isClerk && rows.length > 1) {
<button
class="ws-card__del"
type="button"
(click)="$event.stopPropagation(); deleteRow(i)"
title="Remove this row"
>
×
</button>
}
<div class="ws-card__body">
<!-- ── Row 1: Classification | Account ── -->
<div class="ws-group-row">
<div class="ws-group">
<span class="ws-caption">Classification</span>
<div class="ws-group__fields">
<div class="ws-f ws-f--type">
<label class="ws-f__lbl" title="Transaction Type">TYPE</label>
<select
class="ws-f__ctl"
[(ngModel)]="row.typ"
(click)="$event.stopPropagation()"
(change)="onTypChange(row)"
>
<option value="~">~</option>
<option value="Withdrawal Slip">Withdrawal Slip</option>
<option value="Cheque">Cheque</option>
<option value="Disbursement">Disbursement</option>
<option value="Enter Bank Transfer">Enter Bank Transfer</option>
<option value="DD">DD (Demand Draft)</option>
</select>
</div>
<div class="ws-f ws-f--branch">
<label class="ws-f__lbl" title="Branch">BRANCH</label>
<select class="ws-f__ctl" [(ngModel)]="row.br" (click)="$event.stopPropagation()">
<option [ngValue]="1">001 · Main</option>
<option [ngValue]="2">002 · M.G. Road</option>
<option [ngValue]="14">014 · Station Rd</option>
<option [ngValue]="27">027 · Industrial</option>
</select>
</div>
<div class="ws-f ws-f--paytype">
<label class="ws-f__lbl ws-f__lbl--accent" title="Transaction Type">TXN TP.</label>
<select
class="ws-f__ctl ws-f__ctl--accent"
[(ngModel)]="row.paymentTypeId"
(click)="$event.stopPropagation()"
[class.ws-f__ctl--req]="!row.paymentTypeId"
>
<option [ngValue]="null">— Select —</option>
@for (pt of paymentTypes; track pt.id) {
<option [ngValue]="pt.id">{{ pt.name }}</option>
}
</select>
</div>
<div class="ws-f ws-f--actyp">
<label class="ws-f__lbl" title="Account Type">AC. TP.</label>
<select
class="ws-f__ctl"
[(ngModel)]="row.acTyp"
(click)="$event.stopPropagation()"
(change)="
row.acTyp === 'GL' &&
row.typ !== 'Disbursement' &&
row.typ !== 'Enter Bank Transfer' &&
(row.dbCr = 'Cr')
"
>
@for (a of acTypOptions; track a) {
<option [value]="a">{{ a }}</option>
}
</select>
</div>
</div>
</div>
<div class="ws-vdivider"></div>
<div class="ws-group ws-group--flex">
<span class="ws-caption">Account</span>
<div class="ws-group__fields">
<div class="ws-f ws-f--acno">
<label class="ws-f__lbl" title="Account Number">A/C NO.</label>
<input
class="ws-f__ctl ws-f__ctl--mono"
[value]="row.acNo"
(input)="onAcNoInput(row, i, $any($event.target).value)"
(click)="$event.stopPropagation()"
(blur)="onAcNoBlur(row, i)"
placeholder="Account number"
/>
</div>
<div class="ws-f ws-f--ecsno">
<label class="ws-f__lbl" title="ECS Account Number">ECS A/C NO.</label>
<input
class="ws-f__ctl ws-f__ctl--mono"
[(ngModel)]="row.ecsAcNo"
(click)="$event.stopPropagation()"
placeholder="ECS account"
/>
</div>
<div class="ws-f ws-f--dbcr">
<label class="ws-f__lbl" title="Debit / Credit">DR./CR.</label>
<div class="ws-seg" (click)="$event.stopPropagation()">
<button
type="button"
class="ws-seg__btn ws-seg__btn--dr"
[class.ws-seg__btn--active-dr]="row.dbCr === 'Dr'"
[disabled]="
row.acTyp === 'GL' && row.typ !== 'Disbursement' && row.typ !== 'Enter Bank Transfer'
"
(click)="row.dbCr = 'Dr'"
>
DR
</button>
<button
type="button"
class="ws-seg__btn ws-seg__btn--cr"
[class.ws-seg__btn--active-cr]="row.dbCr === 'Cr'"
(click)="row.dbCr = 'Cr'"
>
CR
</button>
</div>
</div>
</div>
</div>
</div>
<!-- ── Row 2: Payment instrument | Narration ── -->
<div class="ws-group-row ws-group-row--mt">
<div class="ws-group">
<span class="ws-caption">Payment instrument</span>
<div class="ws-group__fields">
<div class="ws-f ws-f--amt">
<label class="ws-f__lbl" title="Transaction Amount">
AMOUNT
@if (!isClerk && activeWithdrawalRequest) {
<span class="ws-amt-locked-badge" title="Amount locked — approved withdrawal">🔒</span>
}
</label>
<div
class="ws-amt-wrap"
[class.ws-amt-wrap--cash]="isCashPayment && selectedRow === row"
[class.ws-amt-wrap--locked]="!isClerk && !!activeWithdrawalRequest"
>
<span class="ws-amt-prefix">₹</span>
<input
class="ws-f__ctl ws-amt-input"
type="number"
[value]="row.amount ?? ''"
(input)="row.amount = +$any($event.target).value"
(click)="$event.stopPropagation()"
placeholder="0.00"
[readonly]="!isClerk && !!activeWithdrawalRequest"
[class.ws-amt-input--locked]="!isClerk && !!activeWithdrawalRequest"
/>
</div>
</div>
@if (row.typ !== 'Withdrawal Slip') {
<div class="ws-f ws-f--cheqno">
<label class="ws-f__lbl" title="Cheque Number">CHEQUE NO.</label>
<input
class="ws-f__ctl ws-f__ctl--mono"
[(ngModel)]="row.chequeNo"
(click)="$event.stopPropagation()"
placeholder="—"
/>
</div>
<div class="ws-f ws-f--cheqdt">
<label class="ws-f__lbl" title="Cheque Date">CHEQ. DATE</label>
<input
class="ws-f__ctl"
[(ngModel)]="row.cheqDate"
placeholder="dd-mm-yyyy"
(click)="$event.stopPropagation()"
/>
</div>
}
<div class="ws-f ws-f--tok">
<label class="ws-f__lbl" title="Token Number">TOK. NO.</label>
<input
class="ws-f__ctl"
[(ngModel)]="row.tokNo"
[readonly]="!isClerk && !!activeWithdrawalRequest"
[class.ws-f__ctl--readonly]="!isClerk && !!activeWithdrawalRequest"
placeholder="Enter token"
(blur)="onTokNoBlur(row)"
(click)="$event.stopPropagation()"
/>
</div>
</div>
</div>
<div class="ws-vdivider"></div>
<div class="ws-group ws-group--flex">
<span class="ws-caption">Narration</span>
<div class="ws-group__fields">
<div class="ws-f ws-f--desc">
<label class="ws-f__lbl" title="Description / Narration">DESCRIPTION</label>
<input
class="ws-f__ctl"
[(ngModel)]="row.description"
(click)="$event.stopPropagation()"
placeholder="Narration / remarks"
/>
</div>
</div>
</div>
</div>
</div>
</div>
}
</div>
</div>
}
<!-- ── APPROVED WITHDRAWAL BANNER (cashier disburse flow) ─────────── -->
@if (!isClerk && loadingWithdrawal) {
<div class="ws-withdrawal-banner ws-withdrawal-banner--loading">
<div class="ws-withdrawal-banner__spinner"></div>
<span>Looking up approved withdrawal request…</span>
</div>
}
@if (!isClerk && activeWithdrawalRequest) {
<div class="ws-withdrawal-banner">
<mat-icon class="ws-withdrawal-banner__icon">check_circle</mat-icon>
<div class="ws-withdrawal-banner__content">
<div class="ws-withdrawal-banner__title">Approved Withdrawal Request Linked</div>
<div class="ws-withdrawal-banner__details">
<span
>Token: <strong>{{ activeWithdrawalRequest.tok_no }}</strong></span
>
<span class="ws-wb-sep">·</span>
<span
>Amount: <strong>₹{{ activeWithdrawalRequest.amount | number: '1.2-2' }}</strong></span
>
<span class="ws-wb-sep">·</span>
<span
>Client: <strong>{{ activeWithdrawalRequest.client_name }}</strong></span
>
<span class="ws-wb-sep">·</span>
<span>By Clerk: {{ activeWithdrawalRequest.clerk_name }}</span>
<span class="ws-wb-sep">·</span>
<span>Approved by: {{ activeWithdrawalRequest.manager_name || '—' }}</span>
</div>
<div class="ws-withdrawal-banner__note">
After transaction succeeds, this request will be automatically marked <strong>WITHDRAWN</strong>.
</div>
</div>
<button type="button" class="ws-withdrawal-banner__clear" (click)="activeWithdrawalRequest = null" title="Unlink">
<mat-icon>close</mat-icon>
</button>
</div>
}
<!-- ── STATUS ────────────────────────────────────────────────────────── -->
@if (loadingAccount) {
<div class="te-loading">
<div class="te-loading__spinner">
<svg width="56" height="56" viewBox="0 0 56 56" fill="none">
<circle cx="28" cy="28" r="24" stroke="#e2e8f0" stroke-width="4" />
<circle
cx="28"
cy="28"
r="24"
stroke="url(#te-grad)"
stroke-width="4"
stroke-linecap="round"
stroke-dasharray="40 112"
class="te-loading__arc"
/>
<defs>
<linearGradient id="te-grad" x1="0" y1="0" x2="56" y2="56" gradientUnits="userSpaceOnUse">
<stop offset="0%" stop-color="#2563eb" />
<stop offset="100%" stop-color="#7c3aed" />
</linearGradient>
</defs>
</svg>
<div class="te-loading__pulse"></div>
</div>
<span class="te-loading__title">Loading Account</span>
<span class="te-loading__sub">Fetching account details, please wait…</span>
</div>
}
@if (accountError && !loadingAccount) {
<div class="te-error">{{ accountError }}</div>
}
<!-- ── ACCOUNT DETAIL ─────────────────────────────────────────────────── -->
@if (accountDetails && !loadingAccount) {
<div class="te-detail">
<!-- Customer info panels — Clerk role only -->
@if (isClerk) {
<!-- ROW 1: Personal | Joint + Nominees | Other + Balances -->
<div class="te-detail-top">
<!-- Personal Information -->
<div class="te-panel">
<div class="te-panel__header">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="8" r="4" />
<path d="M4 20c0-4 3.6-7 8-7s8 3 8 7" />
</svg>
Personal Information
</div>
<div class="te-panel__body">
<div class="te-field">
<span class="te-label">Branch</span><span class="te-value">{{ officeName }}</span>
</div>
<div class="te-field">
<span class="te-label">A/c No.</span
><span class="te-value te-mono">{{ accountDetails.accountNo }}</span>
</div>
<div class="te-field">
<span class="te-label">Name</span><span class="te-value te-value-name">{{ clientName }}</span>
</div>
<div class="te-field">
<span class="te-label">S/D/W/H/O</span><span class="te-value">{{ guardianName }}</span>
</div>
<div class="te-field">
<span class="te-label">Address</span><span class="te-value te-value-wrap">{{ clientAddress }}</span>
</div>
<div class="te-field">
<span class="te-label">Email</span><span class="te-value">{{ clientEmail }}</span>
</div>
<div class="te-field-row">
<div class="te-field">
<span class="te-label">Last Txn</span><span class="te-value">{{ lastTransactionDate }}</span>
</div>
<div class="te-field">
<span class="te-label">Type</span
><span class="te-value">{{ accountDetails.depositType?.value ?? selectedRow?.acTyp ?? '—' }}</span>
</div>
</div>
<div class="te-field-row">
<div class="te-field">
<span class="te-label">Opening Dt</span><span class="te-value">{{ openingDate }}</span>
</div>
<div class="te-field">
<span class="te-label">Status</span
><span class="te-value">{{ accountDetails.status?.value ?? '—' }}</span>
</div>
</div>
<div class="te-field">
<span class="te-label">CIF No.</span><span class="te-value te-mono">{{ cifNumber }}</span>
</div>
@if (isNonIndividual) {
<div class="te-field">
<span class="te-label">Constitution</span>
<span class="te-value">{{
nonPersonDetails?.constitution?.name ?? nonPersonDetails?.constitutionType?.name ?? '—'
}}</span>
</div>
<div class="te-field">
<span class="te-label">Incorp. No.</span>
<span class="te-value te-mono">{{
nonPersonDetails?.incorpNo ?? nonPersonDetails?.incorporationNo ?? '—'
}}</span>
</div>
<div class="te-field">
<span class="te-label">Main Business</span>
<span class="te-value">{{
nonPersonDetails?.mainBusinessLine?.name ?? nonPersonDetails?.mainBusinessLineType?.name ?? '—'
}}</span>
</div>
}
</div>
</div>
<!-- Joint Holders + Nominees -->
<div class="te-panel">
<div class="te-panel__header">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
<circle cx="9" cy="7" r="4" />
<path d="M23 21v-2a4 4 0 0 0-3-3.87" />
<path d="M16 3.13a4 4 0 0 1 0 7.75" />
</svg>
Joint A/c Holders
@if (accountDetails?.modeOfOperation) {
<span class="te-mandate-badge">{{ modeOfOperationLabel }}</span>
}
</div>
<div class="te-panel__body">
@if (jointHolders.length === 0) {
<span class="te-dim">No joint holders</span>
}
@for (jh of jointHolders; track jh.id ?? $index; let i = $index) {
<div
class="te-joint-row te-joint-row--clickable"
(click)="toggleHolder(i, jh)"
title="Click to view details"
>
<span class="te-joint-name">{{ jh.displayName }}</span>
<span class="te-badge" [class.te-badge--primary]="jh.isPrimary || jh.primary">
{{ jh.role ?? (jh.isPrimary || jh.primary ? 'Primary' : 'Co-Holder') }}
</span>
@if (jh.sharePercentage) {
<span class="te-dim">{{ jh.sharePercentage }}%</span>
}
@if (jh.signingAuthority) {
<span
class="te-sign-badge"
[class.te-sign-badge--mandatory]="jh.signingAuthority?.toLowerCase() === 'mandatory'"
[class.te-sign-badge--optional]="jh.signingAuthority?.toLowerCase() === 'optional'"
>
{{ jh.signingAuthority }}
</span>
}
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="2">
<circle cx="11" cy="11" r="8" />
<line x1="21" y1="21" x2="16.65" y2="16.65" />
</svg>
</div>
}
<!-- Withdrawal rule alert -->
@if (withdrawalRule) {
<div
class="te-withdrawal-rule"
[class.te-withdrawal-rule--danger]="withdrawalRule.type === 'danger'"
[class.te-withdrawal-rule--warning]="withdrawalRule.type === 'warning'"
>
<span class="te-wr-icon">{{ withdrawalRule.icon }}</span>
<span class="te-wr-msg">{{ withdrawalRule.msg }}</span>
</div>
}
</div>
<!-- Nominees (Individual) / Non-Individual Account Details -->
@if (!isNonIndividual) {
<div class="te-panel__header te-panel__header--mt">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
</svg>
Nominees
</div>
<div class="te-panel__body">
@if (nominees.length === 0) {
<span class="te-dim">No nominees</span>
}
@for (n of nominees; track n.id ?? $index) {
<div class="te-joint-row">
<span class="te-joint-name">{{ n.nomineeDisplayName ?? '—' }}</span>
<span class="te-dim">{{ n.relationshipName ?? '—' }}</span>
@if (n.percentageShare) {
<span class="te-badge">{{ n.percentageShare }}%</span>
}
@if (n.nomineeIsMinor) {
<span class="te-badge" style="background: #fff7ed; color: #c2410c; border-color: #fed7aa"
>Minor</span
>
}
</div>
}
</div>
}
@if (isNonIndividual) {
<div class="te-panel__header te-panel__header--mt">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="2" y="7" width="20" height="14" rx="2" />
<path d="M16 7V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2" />
</svg>
Non-Individual Account Details
@if (accountDetails?.modeOfOperation) {
<span class="te-mandate-badge">{{ modeOfOperationLabel }}</span>
}
</div>
<div class="te-panel__body">
@if (!accountDetails?.partners?.length) {
<span class="te-dim">No partners / members linked</span>
}
@for (p of accountDetails?.partners ?? []; track p.id; let i = $index) {
<div
class="te-joint-row te-joint-row--clickable"
(click)="
toggleHolder(i + 1000, {
clientId: p.clientId,
displayName: p.clientName,
cifNumber: p.clientCif,
sharePercentage: p.sharePercentage,
signingAuthority: p.isMandatory ? 'Mandatory' : 'Optional',
role: 'Partner',
isPrimary: false
})
"
title="Click to view details"
>
<span class="te-joint-name">{{ p.clientName ?? '—' }}</span>
<span class="te-dim te-mono" style="font-size: 10px">{{ p.clientCif ?? '' }}</span>
@if (p.sharePercentage) {
<span class="te-dim">{{ p.sharePercentage }}%</span>
}
@if (p.isMandatory) {
<span class="te-sign-badge te-sign-badge--mandatory">Mandatory</span>
} @else {
<span class="te-sign-badge te-sign-badge--optional">Optional</span>
}
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="2">
<circle cx="11" cy="11" r="8" />
<line x1="21" y1="21" x2="16.65" y2="16.65" />
</svg>
</div>
}
@if (withdrawalRule && isNonIndividual) {
<div
class="te-withdrawal-rule"
[class.te-withdrawal-rule--danger]="withdrawalRule.type === 'danger'"
[class.te-withdrawal-rule--warning]="withdrawalRule.type === 'warning'"
>
<span class="te-wr-icon">{{ withdrawalRule.icon }}</span>
<span class="te-wr-msg">{{ withdrawalRule.msg }}</span>
</div>
}
</div>
}
</div>
<!-- Other Info + KYC + Account Balances -->
<div class="te-panel">
<div class="te-panel__header">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="4" width="18" height="18" rx="2" />
<line x1="16" y1="2" x2="16" y2="6" />
<line x1="8" y1="2" x2="8" y2="6" />
<line x1="3" y1="10" x2="21" y2="10" />
</svg>
Other Information
</div>
<div class="te-panel__body">
<div class="te-field">
<span class="te-label">Phone No.</span><span class="te-value">{{ clientPhone }}</span>
</div>
<div class="te-field">
<span class="te-label">Client ID</span
><span class="te-value te-mono">{{ accountDetails.clientId }}</span>
</div>
<div class="te-field">
<span class="te-label">PAN</span><span class="te-value te-mono">{{ panNumber }}</span>
</div>
<div class="te-field">
<span class="te-label">KYC Status</span>
<span class="te-value" [style.color]="kycDetails?.kycStatus === 300 ? '#059669' : '#f59e0b'">{{
kycStatus
}}</span>
</div>
<div class="te-field">
<span class="te-label">CKYC No.</span><span class="te-value te-mono">{{ ckycNumber }}</span>
</div>
<div class="te-field">
<span class="te-label">Aadhaar</span><span class="te-value te-mono">{{ aadhaarNumber }}</span>
</div>
<div class="te-field">
<span class="te-label">Product</span>
<span class="te-value">{{
accountDetails.savingsProductName ||
accountDetails.depositProductName ||
accountDetails.productName ||
'—'
}}</span>
</div>
</div>
<div class="te-panel__header te-panel__header--mt">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="12" y1="1" x2="12" y2="23" />
<path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6" />
</svg>
Account Information
</div>
<div class="te-panel__body">
<div class="te-balance-row">
<span class="te-bal-num">1.</span>
<span class="te-bal-label">{{ accountDetails?.glCode ? 'GL Balance' : 'Current Balance' }}</span>
<span class="te-bal-value te-bal-value--cr"
>{{ currencySymbol }}{{ currentBalance | number: '1.2-2' }}</span
>
</div>
<div class="te-balance-row">
<span class="te-bal-num">2.</span><span class="te-bal-label">Today's Clg. Amount</span>
<span class="te-bal-value">{{ currencySymbol }}{{ todayClgAmount | number: '1.2-2' }}</span>
</div>
<div class="te-balance-row">
<span class="te-bal-num">3.</span><span class="te-bal-label">Clear Balance (2-3)</span>
<span class="te-bal-value te-bal-value--cr"
>{{ currencySymbol }}{{ clearBalance | number: '1.2-2' }}</span
>
</div>
<div class="te-balance-row">
<span class="te-bal-num">4.</span><span class="te-bal-label">Freeze Amount</span>
<span class="te-bal-value">{{ currencySymbol }}0.00</span>
</div>
<div class="te-balance-row">
<span class="te-bal-num">5.</span><span class="te-bal-label">Withdrawable Amt.</span>
<span class="te-bal-value te-bal-value--cr"
>{{ currencySymbol }}{{ withdrawableAmount | number: '1.2-2' }}</span
>
</div>
</div>
</div>
</div>
<!-- ── ALERTS PANEL ─────────────────────────────────────────────── -->
@if (accountAlerts.length > 0) {
<div class="te-alerts-wrap">
@for (alert of accountAlerts; track $index) {
<div
class="te-alert"
[class.te-alert--danger]="alert.type === 'danger'"
[class.te-alert--warning]="alert.type === 'warning'"
[class.te-alert--info]="alert.type === 'info'"
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
@if (alert.type === 'danger') {
<path
d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"
/>
<line x1="12" y1="9" x2="12" y2="13" />
<line x1="12" y1="17" x2="12.01" y2="17" />
}
@if (alert.type === 'warning') {
<circle cx="12" cy="12" r="10" />
<line x1="12" y1="8" x2="12" y2="12" />
<line x1="12" y1="16" x2="12.01" y2="16" />
}
@if (alert.type === 'info') {
<circle cx="12" cy="12" r="10" />
<line x1="12" y1="16" x2="12" y2="12" />
<line x1="12" y1="8" x2="12.01" y2="8" />
}
</svg>
<span>{{ alert.message }}</span>
</div>
}
</div>
}
<!-- Balance info — Clerk mode (below full panels) -->
<div class="te-balance-bar">
<div class="te-bal-item">
<span class="te-bal-item__label">Net Balance</span>
<span class="te-bal-item__val te-bal-item__val--cr te-bal-item__val--big">
{{ currencySymbol }}{{ clearBalance | number: '1.2-2' }} CR
</span>
</div>
<div class="te-bal-item">
<span class="te-bal-item__label">Opening</span>
<span class="te-bal-item__val">{{ currencySymbol }}{{ currentBalance | number: '1.2-2' }}</span>
</div>
<div class="te-bal-item">
<span class="te-bal-item__label">Cash Hold</span>
<span class="te-bal-item__val">0.00</span>
</div>
<div class="te-bal-item">
<span class="te-bal-item__label">Transfer Hold</span>
<span class="te-bal-item__val">{{ freezeAmount | number: '1.2-2' }}</span>
</div>
<div class="te-bal-item">
<span class="te-bal-item__label">Clearing Hold</span>
<span class="te-bal-item__val">0.00</span>
</div>
<div class="te-bal-item">
<span class="te-bal-item__label">After Trans.</span>
<span class="te-bal-item__val te-bal-item__val--cr"
>{{ currencySymbol }}{{ clearBalance | number: '1.2-2' }}</span
>
</div>
<div class="te-bal-item">
<span class="te-bal-item__label">Rate</span>
<span class="te-bal-item__val">{{ accountDetails.nominalAnnualInterestRate ?? 0 | number: '1.2-2' }}%</span>
</div>
<div class="te-bal-item">
<span class="te-bal-item__label">Currency</span>
<span class="te-bal-item__val">{{ accountDetails.currency?.code ?? 'INR' }}</span>
</div>
</div>
} @else {
<!-- Cashier: all account info in one compact strip -->
<div class="te-acs">
<!-- Identity -->
<div class="te-acs-id">
@if (clientPhotoUrl) {
<img
[src]="clientPhotoUrl"
class="te-acs-photo te-acs-photo--clickable"
alt="Photo"
title="Click to enlarge"
(click)="previewImage(clientPhotoUrl, 'Client Photo')"
/>
} @else {
<div class="te-acs-photo te-acs-photo--ph">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="1.5">
<circle cx="12" cy="8" r="4" />
<path d="M4 20c0-4 3.6-7 8-7s8 3 8 7" />
</svg>
</div>
}
<div class="te-acs-nameblock">
<span class="te-acs-name">{{
accountDetails?.glCode
? (accountDetails?.name ?? '—')
: (clientDetails?.displayName ?? accountDetails?.clientName ?? '—')
}}</span>
<span class="te-acs-acno">{{ accountDetails?.glCode ?? accountDetails?.accountNo }}</span>
</div>
</div>
<div class="te-acs-sep"></div>
<!-- Account meta -->
<div class="te-acs-meta">
<span
class="te-acs-tag"
[class.te-acs-tag--active]="accountDetails?.status?.value === 'Active'"
[class.te-acs-tag--blocked]="accountDetails?.status?.value === 'Blocked'"
[class.te-acs-tag--dormant]="accountDetails?.subStatus?.value === 'Dormant'"
>{{
accountDetails?.subStatus?.value === 'Dormant' ? 'Dormant' : (accountDetails?.status?.value ?? '—')
}}</span
>
<span class="te-acs-kv"
><b>A/c</b>
{{
accountDetails?.depositType?.value ||
(selectedRow?.acTyp === 'CA'
? 'Current'
: selectedRow?.acTyp === 'SD'
? 'Savings'
: selectedRow?.acTyp === 'FD'
? 'Fixed Dep.'
: selectedRow?.acTyp === 'RD'
? 'Recurring'
: selectedRow?.acTyp === 'GL'
? (accountDetails?.type?.value ?? 'GL Account')
: selectedRow?.acTyp === 'SHR'
? 'Share'
: selectedRow?.acTyp === 'LA'
? 'Loan'
: '—')
}}</span
>
<span class="te-acs-kv"><b>Branch</b> {{ officeName ?? '—' }}</span>
<span class="te-acs-kv"
><b>Product</b>
{{
accountDetails?.savingsProductName ||
accountDetails?.depositProductName ||
accountDetails?.productName ||
'—'
}}</span
>
<span class="te-acs-kv"
><b>Rate</b> {{ accountDetails?.nominalAnnualInterestRate ?? 0 | number: '1.2-2' }}%</span
>
<span class="te-acs-kv"><b>CCY</b> {{ accountDetails?.currency?.code ?? 'INR' }}</span>
</div>
<div class="te-acs-sep"></div>
<!-- Balance figures -->
<div class="te-acs-bals">
<div class="te-acs-b te-acs-b--main">
<span class="te-acs-b-l">Net Bal</span>
<span class="te-acs-b-v te-acs-b-v--cr">{{ currencySymbol }}{{ clearBalance | number: '1.2-2' }} CR</span>
</div>
<div class="te-acs-b">
<span class="te-acs-b-l">Opening</span>
<span class="te-acs-b-v">{{ currencySymbol }}{{ currentBalance | number: '1.2-2' }}</span>
</div>
<div class="te-acs-b">
<span class="te-acs-b-l">Cash Hold</span>
<span class="te-acs-b-v">0.00</span>
</div>
<div class="te-acs-b">
<span class="te-acs-b-l">Tr. Hold</span>
<span class="te-acs-b-v">{{ freezeAmount | number: '1.2-2' }}</span>
</div>
<div class="te-acs-b">
<span class="te-acs-b-l">Clr. Hold</span>
<span class="te-acs-b-v">0.00</span>
</div>
<div class="te-acs-b te-acs-b--after">
<span class="te-acs-b-l">After Trans.</span>
<span class="te-acs-b-v te-acs-b-v--cr">{{ currencySymbol }}{{ clearBalance | number: '1.2-2' }}</span>
</div>
</div>
<!-- Alerts -->
@if (accountAlerts.length > 0) {
<div class="te-acs-sep"></div>
<div class="te-acs-alerts">
@for (alert of accountAlerts; track alert.message) {
<span class="te-acs-alert te-acs-alert--{{ alert.type }}">
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<circle cx="12" cy="12" r="10" />
<line x1="12" y1="8" x2="12" y2="12" />
<line x1="12" y1="16" x2="12.01" y2="16" />
</svg>
{{ alert.message }}
</span>
}
</div>
}
</div>
}
<!-- ── DENOMINATION + OPENING/CLOSING — Cashier only ───────────── -->
@if (!isClerk) {
<div class="te-denom-wrap">
<!-- Opening Balance -->
<div class="te-panel te-panel--denom">
<div class="te-panel__header">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10" />
<polyline points="12 6 12 12 16 14" />
</svg>
Opening Position — {{ today }}
</div>
<div class="te-panel__body">
@if (!openingLoaded) {
<span class="te-dim">Loading…</span>
}
@if (openingLoaded && openingDenoms.length === 0) {
<span class="te-dim">No opening position found</span>
}
@if (openingLoaded && openingDenoms.length > 0) {
<table class="te-denom-table">
<thead>
<tr>
<th>Denomination</th>
<th class="num">Opening Count</th>
<th class="num">Amount</th>
</tr>
</thead>
<tbody>
@for (d of sortedOpeningDenoms; track d.denominationId) {
@if (d.count > 0) {
<tr>
<td>
<span class="te-denom-chip" [class.te-denom-chip--coin]="d.kind === 'coin'"
>₹{{ d.denominationValue }}</span
>
</td>
<td class="num">{{ d.count }}</td>
<td class="num te-cr">₹{{ d.count * d.denominationValue | number: '1.2-2' }}</td>
</tr>
}
}
</tbody>
<tfoot>
<tr>
<td><strong>Total</strong></td>
<td></td>
<td class="num te-cr">
<strong>₹{{ openingTotal | number: '1.2-2' }}</strong>
</td>
</tr>
</tfoot>
</table>
}
</div>
</div>
<!-- Denomination Entry -->
<div class="te-panel te-panel--denom">
<div class="te-panel__header">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="2" y="7" width="20" height="14" rx="2" />
<path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16" />
</svg>
Denomination Entry
@if (denominationTotal > 0) {
<span class="te-mandate-badge">₹{{ denominationTotal | number: '1.2-2' }}</span>
}
@if (isCashPayment && denominationTotal <= 0) {
<span class="te-denom-required-badge">⚠ Required for Cash</span>
}
</div>
<div class="te-panel__body">
<table class="te-denom-table">
<thead>
<tr>
<th>Denomination</th>
<th class="num">Count</th>
<th class="num">Amount</th>
</tr>
</thead>
<tbody>
@for (d of allDenominations; track d.id) {
<tr>
<td>
<span class="te-denom-chip" [class.te-denom-chip--coin]="d.kind === 'coin'">
{{ d.label }}
</span>
</td>
<td class="num">
<div class="te-denom-stepper">
<button type="button" (click)="bumpDenom(d, -1)" [disabled]="countFor(d) === 0">−</button>
<input
type="number"
min="0"
[value]="countFor(d) || ''"
placeholder="0"
(input)="onDenomChange(d, $any($event.target).value)"
/>
<button type="button" (click)="bumpDenom(d, 1)">+</button>
</div>
</td>
<td class="num te-cr">
{{ subtotalFor(d) > 0 ? (subtotalFor(d) | number: '1.2-2') : '—' }}
</td>
</tr>
}
</tbody>
<tfoot>
<tr>
<td><strong>Total</strong></td>
<td></td>
<td class="num te-cr">
<strong>₹{{ denominationTotal | number: '1.2-2' }}</strong>
</td>
</tr>
</tfoot>
</table>
@if (denominationTotal > 0) {
<div class="te-denom-actions">
<button class="te-btn te-btn--primary" type="button" (click)="applyDenominationTotal()">
Apply ₹{{ denominationTotal | number: '1.2-2' }} to Amount
</button>
<button class="te-btn" type="button" (click)="clearDenominations()">Clear</button>
</div>
}
</div>
</div>
<!-- Closing Position -->
<div class="te-panel te-panel--denom">
<div class="te-panel__header">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="20 6 9 17 4 12" />
</svg>
Closing Position (Preview)
@if (selectedRow?.dbCr === 'Dr' && hasWithdrawalDenomOverdraft) {
<span class="te-denom-overdraft-badge">⚠ Stock Overdraft</span>
}
</div>
<div class="te-panel__body">
@if (selectedRow?.dbCr === 'Dr' && hasWithdrawalDenomOverdraft) {
<div class="te-denom-overdraft-alert">
<div class="te-denom-overdraft-alert__title">
<svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2.5"
>
<circle cx="12" cy="12" r="10" />
<line x1="12" y1="8" x2="12" y2="12" />
<line x1="12" y1="16" x2="12.01" y2="16" />
</svg>
Insufficient Denomination Stock — Cannot Proceed
</div>
<table class="te-denom-overdraft-alert__table">
<thead>
<tr>
<th>Denomination</th>
<th>Requested</th>
<th>Available</th>
<th>Short</th>
</tr>
</thead>
<tbody>
@for (o of denominationOverdrafts; track o.label) {
<tr>
<td>{{ o.label }}</td>
<td class="te-dr">{{ o.requested }}</td>
<td>{{ o.available }}</td>
<td class="te-dr">
<strong>−{{ o.shortfall }}</strong>
</td>
</tr>
}
</tbody>
</table>
<div class="te-denom-overdraft-alert__hint">Adjust denomination or request vault replenishment.</div>
</div>
}
@if (closingDenoms.length === 0) {
<span class="te-dim">Enter denomination to see closing position</span>
}
@if (closingDenoms.length > 0) {
<table class="te-denom-table">
<thead>
<tr>
<th>Denomination</th>
<th class="num">Opening</th>
<th class="num">{{ selectedRow?.dbCr === 'Cr' ? '+Deposit' : '-Withdrawal' }}</th>
<th class="num">Closing</th>
</tr>
</thead>
<tbody>
@for (d of sortedClosingDenoms; track d.denominationValue + d.kind) {
<tr [class.te-closing-row--neg]="d.closingCount < 0">
<td>
<span class="te-denom-chip" [class.te-denom-chip--coin]="d.kind === 'coin'"
>₹{{ d.denominationValue }}</span
>
</td>
<td class="num">{{ d.openCount }}</td>
<td class="num" [style.color]="selectedRow?.dbCr === 'Cr' ? '#059669' : '#dc2626'">
{{ selectedRow?.dbCr === 'Cr' ? '+' : '-' }}{{ d.entered }}
</td>
<td class="num" [class.te-cr]="d.closingCount >= 0" [class.te-dr]="d.closingCount < 0">
{{ d.closingCount }}
</td>
</tr>
}
</tbody>
<tfoot>
<tr>
<td><strong>Total</strong></td>
<td class="num">₹{{ openingTotal | number: '1.2-2' }}</td>
<td class="num" [style.color]="selectedRow?.dbCr === 'Cr' ? '#059669' : '#dc2626'">
{{ selectedRow?.dbCr === 'Cr' ? '+' : '-' }}₹{{ denominationTotal | number: '1.2-2' }}
</td>
<td class="num" [class.te-cr]="closingTotal >= 0" [class.te-dr]="closingTotal < 0">
<strong>₹{{ closingTotal | number: '1.2-2' }}</strong>
</td>
</tr>
</tfoot>
</table>
}
</div>
</div>
</div>
}
<!-- ── ENTRY CARD (bottom — same design as top) ──────────────── -->
<div class="ws-wrap">
<div class="ws-header">
<div class="ws-header__left">
<div class="ws-header__icon">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="2" y="3" width="20" height="14" rx="2" />
<line x1="8" y1="21" x2="16" y2="21" />
<line x1="12" y1="17" x2="12" y2="21" />
</svg>
</div>
<span class="ws-header__title">Transaction Entry</span>
</div>
<div class="ws-header__right">
<span class="ws-header__effdt">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="4" width="18" height="18" rx="2" />
<line x1="16" y1="2" x2="16" y2="6" />
<line x1="8" y1="2" x2="8" y2="6" />
<line x1="3" y1="10" x2="21" y2="10" />
</svg>
Eff. Date
<input class="ws-header__date" type="date" [value]="todayIso" readonly />
</span>
</div>
</div>
<div class="ws-rows">
<fieldset class="ws-rows-fieldset" [disabled]="formReadOnly">
@for (row of rows; track $index; let i = $index) {
<div class="ws-card" [class.ws-card--active]="selectedRow === row" (click)="onRowSelect(row, i)">
<div class="ws-card__badge">{{ i + 1 }}</div>
<div class="ws-card__body">
<div class="ws-group-row">
<div class="ws-group">
<span class="ws-caption">Classification</span>
<div class="ws-group__fields">
<div class="ws-f ws-f--type">
<label class="ws-f__lbl">TYPE</label>
<select class="ws-f__ctl" [(ngModel)]="row.typ">
<option value="~">~</option>
<option value="Withdrawal Slip">Withdrawal Slip</option>
<option value="Cheque">Cheque</option>
<option value="Disbursement">Disbursement</option>
<option value="Enter Bank Transfer">Enter Bank Transfer</option>
<option value="DD">DD (Demand Draft)</option>
</select>
</div>
<div class="ws-f ws-f--branch">
<label class="ws-f__lbl">BRANCH</label>
<select class="ws-f__ctl" [(ngModel)]="row.br">
<option [ngValue]="1">001 · Main</option>
<option [ngValue]="2">002 · M.G. Road</option>
<option [ngValue]="14">014 · Station Rd</option>
<option [ngValue]="27">027 · Industrial</option>
</select>
</div>
<div class="ws-f ws-f--paytype">
<label class="ws-f__lbl ws-f__lbl--accent">TXN TP.</label>
<select
class="ws-f__ctl ws-f__ctl--accent"
[(ngModel)]="row.paymentTypeId"
[class.ws-f__ctl--req]="!row.paymentTypeId"
>
<option [ngValue]="null">— Select —</option>
@for (pt of paymentTypes; track pt.id) {
<option [ngValue]="pt.id">{{ pt.name }}</option>
}
</select>
</div>
<div class="ws-f ws-f--actyp">
<label class="ws-f__lbl">AC. TP.</label>
<select
class="ws-f__ctl"
[(ngModel)]="row.acTyp"
(change)="
row.acTyp === 'GL' &&
row.typ !== 'Disbursement' &&
row.typ !== 'Enter Bank Transfer' &&
(row.dbCr = 'Cr')
"
>
@for (a of acTypOptions; track a) {
<option [value]="a">{{ a }}</option>
}
</select>
</div>
</div>
</div>
<div class="ws-vdivider"></div>
<div class="ws-group ws-group--flex">
<span class="ws-caption">Account</span>
<div class="ws-group__fields">
<div class="ws-f ws-f--acno">
<label class="ws-f__lbl">A/C NO.</label>
<input
class="ws-f__ctl ws-f__ctl--mono"
[value]="row.acNo"
(input)="onAcNoInput(row, i, $any($event.target).value)"
(blur)="onAcNoBlur(row, i)"
placeholder="Account number"
/>
</div>
<div class="ws-f ws-f--ecsno">
<label class="ws-f__lbl">ECS A/C NO.</label>
<input
class="ws-f__ctl ws-f__ctl--mono"
[(ngModel)]="row.ecsAcNo"
placeholder="ECS account"
/>
</div>
<div class="ws-f ws-f--dbcr">
<label class="ws-f__lbl">DR./CR.</label>
<div class="ws-seg">
<button
type="button"
class="ws-seg__btn ws-seg__btn--dr"
[class.ws-seg__btn--active-dr]="row.dbCr === 'Dr'"
[disabled]="
row.acTyp === 'GL' && row.typ !== 'Disbursement' && row.typ !== 'Enter Bank Transfer'
"
(click)="$event.stopPropagation(); row.dbCr = 'Dr'"
>
DR
</button>
<button
type="button"
class="ws-seg__btn ws-seg__btn--cr"
[class.ws-seg__btn--active-cr]="row.dbCr === 'Cr'"
(click)="$event.stopPropagation(); row.dbCr = 'Cr'"
>
CR
</button>
</div>
</div>
</div>
</div>
</div>
<div class="ws-group-row ws-group-row--mt">
<div class="ws-group">
<span class="ws-caption">Payment instrument</span>
<div class="ws-group__fields">
<div class="ws-f ws-f--amt">
<label class="ws-f__lbl">
AMOUNT
@if (!isClerk && activeWithdrawalRequest) {
<span class="ws-amt-locked-badge" title="Amount locked — approved withdrawal">🔒</span>
}
</label>
<div
class="ws-amt-wrap"
[class.ws-amt-wrap--cash]="isCashPayment && selectedRow === row"
[class.ws-amt-wrap--locked]="!isClerk && !!activeWithdrawalRequest"
>
<span class="ws-amt-prefix">₹</span>
<input
class="ws-f__ctl ws-amt-input"
type="number"
[value]="row.amount ?? ''"
(input)="row.amount = +$any($event.target).value"
(click)="$event.stopPropagation()"
placeholder="0.00"
[readonly]="!isClerk && !!activeWithdrawalRequest"
[class.ws-amt-input--locked]="!isClerk && !!activeWithdrawalRequest"
/>
</div>
</div>
<div class="ws-f ws-f--cheqno">
<label class="ws-f__lbl">CHEQUE NO.</label>
<input class="ws-f__ctl ws-f__ctl--mono" [(ngModel)]="row.chequeNo" placeholder="—" />
</div>
<div class="ws-f ws-f--cheqdt">
<label class="ws-f__lbl">CHEQ. DATE</label>
<input class="ws-f__ctl" [(ngModel)]="row.cheqDate" placeholder="dd-mm-yyyy" />
</div>
<div class="ws-f ws-f--tok">
<label class="ws-f__lbl">TOK. NO.</label>
<input
class="ws-f__ctl"
[(ngModel)]="row.tokNo"
[readonly]="!isClerk && !!activeWithdrawalRequest"
[class.ws-f__ctl--readonly]="!isClerk && !!activeWithdrawalRequest"
placeholder="Enter token"
(blur)="onTokNoBlur(row)"
/>
</div>
</div>
</div>
<div class="ws-vdivider"></div>
<div class="ws-group ws-group--flex">
<span class="ws-caption">Narration</span>
<div class="ws-group__fields">
<div class="ws-f ws-f--desc">
<label class="ws-f__lbl">DESCRIPTION</label>
<input class="ws-f__ctl" [(ngModel)]="row.description" placeholder="Narration / remarks" />
</div>
</div>
</div>
</div>
</div>
</div>
}
</fieldset>
</div>
<!-- Action buttons footer -->
@if (reviewMode) {
<div class="ws-footer">
<div class="ws-footer__stats">
<button class="ws-btn ws-btn--ghost ws-btn--exit" type="button" (click)="backToInbox()">
← Back to Inbox
</button>
@if (reviewRequest) {
<span class="ws-review-status" [attr.data-status]="reviewRequest.status">
{{ reviewRequest.status }}
</span>
}
</div>
<div class="ws-footer__actions">
@if (reviewRequest?.status === 'PENDING') {
<button class="ws-btn ws-btn--reject" type="button" (click)="openRejectReview()" [disabled]="saving">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
Reject
</button>
<button class="ws-btn ws-btn--post" type="button" (click)="approveReview()" [disabled]="saving">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<polyline points="20 6 9 17 4 12" />
</svg>
{{ saving ? 'Processing…' : 'Approve & Post' }}
</button>
}
</div>
</div>
} @else if (editMode) {
<div class="ws-footer">
<div class="ws-footer__stats">
<button class="ws-btn ws-btn--ghost ws-btn--exit" type="button" (click)="cancelEdit()">
← My Requests
</button>
@if (editRequest) {
<span class="ws-review-status" [attr.data-status]="editRequest.status">
{{ editResubmittable ? 'EDITING REJECTED' : editRequest.status }}
</span>
}
</div>
<div class="ws-footer__actions">
@if (editResubmittable) {
<button class="ws-btn ws-btn--post" type="button" (click)="resubmitEdit()" [disabled]="saving">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<polyline points="20 6 9 17 4 12" />
</svg>
{{ saving ? 'Resubmitting…' : 'Resubmit for Approval' }}
</button>
}
</div>
</div>
} @else {
@if (isClerkCashDeposit) {
<div class="te-day-block te-day-block--warning">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
<line x1="12" y1="9" x2="12" y2="13" />
<line x1="12" y1="17" x2="12.01" y2="17" />
</svg>
<span>
<strong>CASH DEPOSIT NOT PERMITTED</strong> — Cash deposits can only be processed by the cashier.
Notes/coins denomination is managed at the cashier vault.
</span>
</div>
}
<div class="ws-footer">
<div class="ws-footer__stats">
<button class="ws-btn ws-btn--ghost ws-btn--exit" type="button" (click)="onExit()">Exit</button>
<button class="ws-btn ws-btn--ghost" type="button" (click)="onCancel()">Cancel</button>
<button class="ws-btn ws-btn--ghost" type="button">Drop</button>
<button class="ws-btn ws-btn--ghost" type="button" (click)="onSearch()">Search</button>
<button class="ws-btn ws-btn--mode" type="button">Change Tr.Mode</button>
@if (isClerk) {
<button class="ws-btn ws-btn--add" type="button" (click)="addRow()" title="Add another transaction row">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<line x1="12" y1="5" x2="12" y2="19" />
<line x1="5" y1="12" x2="19" y2="12" />
</svg>
Add
</button>
<button
class="ws-btn ws-btn--ghost"
type="button"
(click)="router.navigate(['/clerk/my-disbursement-requests'])"
title="View my disbursement requests"
>
My Disbursements
</button>
}
</div>
<div class="ws-footer__actions">
<button
class="ws-btn ws-btn--post"
type="button"
(click)="onSave()"
[disabled]="saving || isClerkCashDeposit"
>
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<polyline points="20 6 9 17 4 12" />
</svg>
{{ saving ? 'Posting…' : 'Save' }}
</button>
</div>
</div>
}
</div>
</div>
}
</div>
<!-- ── REJECT DIALOG (manager review) ───────────────────────────────────── -->
@if (showRejectDialog) {
<div class="te-confirm-overlay" (click)="cancelRejectReview()">
<div class="te-confirm-box" (click)="$event.stopPropagation()">
<div class="te-confirm-header te-confirm-header--withdrawal">
<span class="te-confirm-icon">✕</span>
<span>Reject Withdrawal Request</span>
</div>
<div class="te-confirm-body">
<p class="ws-reject-hint">
Please provide a reason. The request will be sent back to the clerk for correction and resubmission.
</p>
<textarea
class="ws-reject-textarea"
[(ngModel)]="rejectionReason"
rows="4"
placeholder="e.g. Amount does not match the withdrawal slip; please verify and resubmit."
></textarea>
</div>
<div class="te-confirm-actions">
<button class="te-btn" type="button" (click)="cancelRejectReview()">Cancel</button>
<button class="te-btn te-btn--danger" type="button" [disabled]="saving" (click)="confirmRejectReview()">
{{ saving ? 'Rejecting…' : 'Confirm Reject' }}
</button>
</div>
</div>
</div>
}
<!-- ── CONFIRMATION DIALOG ──────────────────────────────────────────────── -->
@if (showConfirmDialog && confirmData) {
<div class="te-confirm-overlay" (click)="showConfirmDialog = false">
<div class="te-confirm-box" (click)="$event.stopPropagation()">
<div
class="te-confirm-header"
[class.te-confirm-header--deposit]="confirmData.type === 'DEPOSIT'"
[class.te-confirm-header--withdrawal]="confirmData.type === 'WITHDRAWAL'"
[class.te-confirm-header--disbursement]="confirmData.isDisbursement || confirmData.isDepositRequest"
>
<span class="te-confirm-icon">{{ confirmData.type === 'WITHDRAWAL' ? '⬆' : '⬇' }}</span>
<span>Confirm {{ confirmData.isDisbursement ? 'CASH DISBURSEMENT' : confirmData.type }}</span>
</div>
<div class="te-confirm-body">
<div class="te-confirm-row">
<span class="te-confirm-label">Account</span
><span class="te-confirm-val te-mono">{{ confirmData.accountNo }}</span>
</div>
<div class="te-confirm-row">
<span class="te-confirm-label">Customer</span><span class="te-confirm-val">{{ confirmData.clientName }}</span>
</div>
<div class="te-confirm-row">
<span class="te-confirm-label">Payment Type</span
><span class="te-confirm-val">{{ confirmData.paymentType }}</span>
</div>
@if (confirmData.chequeNo) {
<div class="te-confirm-row">
<span class="te-confirm-label">Cheque No.</span
><span class="te-confirm-val te-mono">{{ confirmData.chequeNo }}</span>
</div>
}
@if (confirmData.tokNo) {
<div class="te-confirm-row">
<span class="te-confirm-label">Token No.</span
><span class="te-confirm-val te-mono"
><strong>{{ confirmData.tokNo }}</strong></span
>
</div>
}
<div class="te-confirm-divider"></div>
@if (confirmData.isDisbursement) {
<!-- Token-based disbursement: balance was already deducted at manager approval -->
<div class="te-confirm-row">
<span class="te-confirm-label">Current Balance</span>
<span class="te-confirm-val"
>{{ confirmData.symbol }}{{ confirmData.currentBalance | number: '1.2-2' }}</span
>
</div>
<div class="te-confirm-row te-confirm-row--amount">
<span class="te-confirm-label">Disburse Amount</span>
<span class="te-confirm-val te-cr">{{ confirmData.symbol }}{{ confirmData.amount | number: '1.2-2' }}</span>
</div>
<div class="te-confirm-row te-confirm-disbursed-note">
<span class="te-confirm-disbursed-badge"
>Balance already deducted at manager approval — no further deduction</span
>
</div>
} @else if (confirmData.isDepositRequest) {
<!-- Maker-checker deposit: balance credits only after manager approval -->
<div class="te-confirm-row">
<span class="te-confirm-label">Current Balance</span>
<span class="te-confirm-val"
>{{ confirmData.symbol }}{{ confirmData.currentBalance | number: '1.2-2' }}</span
>
</div>
<div class="te-confirm-row te-confirm-row--amount">
<span class="te-confirm-label">Deposit Amount</span>
<span class="te-confirm-val te-cr"
>+{{ confirmData.symbol }}{{ confirmData.amount | number: '1.2-2' }}</span
>
</div>
<div class="te-confirm-row te-confirm-disbursed-note">
<span class="te-confirm-disbursed-badge"
>Sent for manager approval — balance will be credited only after approval</span
>
</div>
} @else {
<div class="te-confirm-row">
<span class="te-confirm-label">Current Balance</span
><span class="te-confirm-val"
>{{ confirmData.symbol }}{{ confirmData.currentBalance | number: '1.2-2' }}</span
>
</div>
<div class="te-confirm-row te-confirm-row--amount">
<span class="te-confirm-label">{{ confirmData.type === 'DEPOSIT' ? 'Deposit' : 'Withdrawal' }} Amount</span>
<span
class="te-confirm-val"
[class.te-cr]="confirmData.type === 'DEPOSIT'"
[class.te-dr]="confirmData.type === 'WITHDRAWAL'"
>
{{ confirmData.type === 'DEPOSIT' ? '+' : '−' }}{{ confirmData.symbol
}}{{ confirmData.amount | number: '1.2-2' }}
</span>
</div>
<div class="te-confirm-row te-confirm-row--after">
<span class="te-confirm-label">Balance After</span>
<span
class="te-confirm-val"
[class.te-cr]="confirmData.balanceAfter >= 0"
[class.te-dr]="confirmData.balanceAfter < 0"
>
<strong>{{ confirmData.symbol }}{{ confirmData.balanceAfter | number: '1.2-2' }}</strong>
</span>
</div>
}
</div>
<div class="te-confirm-actions">
<button class="te-btn" type="button" (click)="showConfirmDialog = false">Cancel</button>
<button class="te-btn te-btn--primary" type="button" [disabled]="saving" (click)="confirmSubmit()">
{{
saving
? 'Posting…'
: confirmData.isDisbursement
? 'Confirm DISBURSEMENT'
: confirmData.isDepositRequest
? 'Submit for Approval'
: 'Confirm ' + confirmData.type
}}
</button>
</div>
</div>
</div>
}
<!-- ── Custom Alert / Confirm Modal ──────────────────────────────────────── -->
@if (alertModal) {
<div class="te-alert-overlay" (click)="closeAlert()">
<div class="te-alert-box te-alert-box--{{ alertModal.type }}" (click)="$event.stopPropagation()">
<!-- Left sidebar -->
<div class="te-alert-box__sidebar">
<div class="te-alert-box__sidebar-icon">
@if (alertModal.type === 'error') {
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<circle cx="12" cy="12" r="10" />
<line x1="15" y1="9" x2="9" y2="15" />
<line x1="9" y1="9" x2="15" y2="15" />
</svg>
}
@if (alertModal.type === 'warning') {
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
<line x1="12" y1="9" x2="12" y2="13" />
<line x1="12" y1="17" x2="12.01" y2="17" />
</svg>
}
@if (alertModal.type === 'info') {
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<circle cx="12" cy="12" r="10" />
<line x1="12" y1="8" x2="12" y2="12" />
<line x1="12" y1="16" x2="12.01" y2="16" />
</svg>
}
@if (alertModal.type === 'success') {
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2.5"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="12" cy="12" r="10" />
<polyline points="9 12 11 14 15 10" />
</svg>
}
</div>
<span class="te-alert-box__sidebar-type">
{{
alertModal.type === 'error'
? 'ERROR'
: alertModal.type === 'warning'
? 'WARN'
: alertModal.type === 'success'
? 'OK'
: 'INFO'
}}
</span>
</div>
<!-- Right content -->
<div class="te-alert-box__content">
<div class="te-alert-box__content-head">
<h3>{{ alertModal.title }}</h3>
<button class="te-alert-box__close" (click)="closeAlert()">
<svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2.5"
stroke-linecap="round"
>
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
</button>
</div>
<div class="te-alert-box__lines">
@for (line of alertModal.lines; track $index) {
@if ($index === 0) {
<p class="te-alert-box__desc">{{ line }}</p>
} @else if (line.includes(':') && $index < alertModal.lines.length - 1) {
<div class="te-alert-box__kv">
<span class="te-alert-box__kv-k">{{ line.split(':')[0].trim() }}</span>
<span class="te-alert-box__kv-v">{{ line.split(':').slice(1).join(':').trim() }}</span>
</div>
} @else {
<p class="te-alert-box__foot">{{ line }}</p>
}
}
</div>
<div class="te-alert-box__actions">
@if (alertModal.onConfirm) {
<button class="te-alert-btn te-alert-btn--ghost" type="button" (click)="closeAlert()">Cancel</button>
<button
class="te-alert-btn te-alert-btn--solid te-alert-btn--{{ alertModal.type }}"
type="button"
(click)="confirmAlert()"
>
{{ alertModal.confirmLabel ?? 'Confirm' }}
</button>
} @else {
<button
class="te-alert-btn te-alert-btn--solid te-alert-btn--{{ alertModal.type }}"
type="button"
(click)="closeAlert()"
>
{{ alertModal.type === 'success' ? 'Done' : 'OK, Got It' }}
</button>
}
</div>
</div>
</div>
</div>
}