feat(cashier): add CBS Blue Warm Stone theme, validations, non-individual support, and custom alert modal
- Replace table grid with ws-wrap card-form layout (Warm Stone / CBS Blue design) - Add day-status blocking banners (previousDayUnsettled, morningTransferReceived) - Add custom styled alert modal replacing browser alert() — error/warning/info/success types - Add Non-Individual Account Details section with Partners table (click-to-view signature/image) - Fix denomination overdraft inline alert in Closing Position panel - Fix bannedEntityMatch/willfulDefaulter false positive (DB stores YES/NO strings) - Fix denomination mismatch 403 — only send denomination when cash payment matches amount - Fix Today's Activity for CA accounts (m_current_account_transaction, remarks column, type=7 label) - Fix A/C TYPE [object Object] — map from acTyp label - Fix charge deduction sign — use transactionType.deposit instead of withdrawal - Remove Add Row buttons everywhere - CBS Blue theme variables: focus #2563eb, accent #e87722, Hanken Grotesk + Newsreader fonts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
f64047080a
commit
f7d9259eb7
@@ -64,13 +64,6 @@
|
||||
Eff. Date
|
||||
<input class="ws-header__date" type="date" [value]="todayIso" readonly />
|
||||
</span>
|
||||
<button class="ws-btn ws-btn--add" type="button" (click)="addRow()">
|
||||
<svg width="13" height="13" 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 Row
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -251,64 +244,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete button -->
|
||||
<button
|
||||
class="ws-card__del"
|
||||
type="button"
|
||||
title="Remove row"
|
||||
[disabled]="rows.length <= 1"
|
||||
(click)="$event.stopPropagation(); deleteRow(i)"
|
||||
>
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18" />
|
||||
<line x1="6" y1="6" x2="18" y2="18" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- ── Footer totals bar ── -->
|
||||
<div class="ws-footer">
|
||||
<div class="ws-footer__stats">
|
||||
<div class="ws-stat">
|
||||
<span class="ws-stat__label">Rows</span>
|
||||
<span class="ws-stat__val">{{ rows.length }}</span>
|
||||
</div>
|
||||
<div class="ws-stat">
|
||||
<span class="ws-stat__label">Total Debit</span>
|
||||
<span class="ws-stat__val ws-stat__val--dr">₹ {{ formatInr(totalDebit) }}</span>
|
||||
</div>
|
||||
<div class="ws-stat">
|
||||
<span class="ws-stat__label">Total Credit</span>
|
||||
<span class="ws-stat__val ws-stat__val--cr">₹ {{ formatInr(totalCredit) }}</span>
|
||||
</div>
|
||||
<div class="ws-stat">
|
||||
<span class="ws-stat__label">Difference</span>
|
||||
<span class="ws-stat__val" [class.ws-stat__val--ok]="difference === 0">₹ {{ formatInr(difference) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ws-footer__actions">
|
||||
<span
|
||||
class="ws-balance-pill"
|
||||
[class.ws-balance-pill--ok]="isBalanced"
|
||||
[class.ws-balance-pill--bad]="!isBalanced"
|
||||
>
|
||||
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3">
|
||||
@if (isBalanced) {
|
||||
<polyline points="20 6 9 17 4 12" />
|
||||
} @else {
|
||||
<line x1="18" y1="6" x2="6" y2="18" />
|
||||
<line x1="6" y1="6" x2="18" y2="18" />
|
||||
}
|
||||
</svg>
|
||||
{{ isBalanced ? 'Balanced' : 'Not balanced' }}
|
||||
</span>
|
||||
<button class="ws-btn ws-btn--draft" type="button">Save Draft</button>
|
||||
<button class="ws-btn ws-btn--post" type="button" [disabled]="!isBalanced">Post Entry</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1230,108 +1168,202 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── ENTRY GRID (bottom) ─────────────────────────────────────── -->
|
||||
<div class="te-grid-wrap">
|
||||
<table class="te-grid">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Typ</th>
|
||||
<th>Br.</th>
|
||||
<th>Tr.Tp.</th>
|
||||
<th>Actyp</th>
|
||||
<th>A/c No.</th>
|
||||
<th>Ecs A/c No.</th>
|
||||
<th>Db./Cr.</th>
|
||||
<th>Pay Type *</th>
|
||||
<th>Cheque No.</th>
|
||||
<th>Amount</th>
|
||||
<th>Description</th>
|
||||
<th>Tok.No.</th>
|
||||
<th>Cheq.Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (row of rows; track $index; let i = $index) {
|
||||
<tr [class.te-row--selected]="selectedRow === row" (click)="onRowSelect(row, i)">
|
||||
<td><input class="te-cell te-cell--xs" [(ngModel)]="row.typ" /></td>
|
||||
<td><input class="te-cell te-cell--xs" type="number" [(ngModel)]="row.br" /></td>
|
||||
<td>
|
||||
<select class="te-cell te-cell--sm" [(ngModel)]="row.trTp">
|
||||
<option value=""></option>
|
||||
@for (t of trTpOptions; track t) {
|
||||
<option [value]="t">{{ t }}</option>
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<select class="te-cell te-cell--sm" [(ngModel)]="row.acTyp">
|
||||
@for (a of acTypOptions; track a) {
|
||||
<option [value]="a">{{ a }}</option>
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
<td><input class="te-cell te-cell--md" [(ngModel)]="row.acNo" (blur)="onAcNoBlur(row, i)" /></td>
|
||||
<td><input class="te-cell te-cell--lg" [(ngModel)]="row.ecsAcNo" /></td>
|
||||
<td>
|
||||
<select class="te-cell te-cell--sm" [(ngModel)]="row.dbCr">
|
||||
<option value=""></option>
|
||||
@for (d of dbCrOptions; track d) {
|
||||
<option [value]="d">{{ d }}</option>
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<select
|
||||
class="te-cell te-cell--md"
|
||||
[(ngModel)]="row.paymentTypeId"
|
||||
[class.te-cell--required]="!row.paymentTypeId"
|
||||
>
|
||||
<option [ngValue]="null">— Pay Type —</option>
|
||||
@for (pt of paymentTypes; track pt.id) {
|
||||
<option [ngValue]="pt.id">{{ pt.name }}</option>
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
<td><input class="te-cell te-cell--md" [(ngModel)]="row.chequeNo" /></td>
|
||||
<td>
|
||||
<input
|
||||
class="te-cell te-cell--md"
|
||||
type="number"
|
||||
[value]="isCashPayment && selectedRow === row ? denominationTotal || '' : (row.amount ?? '')"
|
||||
[disabled]="isCashPayment && selectedRow === row"
|
||||
[title]="isCashPayment && selectedRow === row ? 'Cash: Amount from denomination' : ''"
|
||||
(input)="row.amount = +$any($event.target).value"
|
||||
[class.te-cell--cash-auto]="isCashPayment && selectedRow === row"
|
||||
/>
|
||||
</td>
|
||||
<td><input class="te-cell te-cell--lg" [(ngModel)]="row.description" /></td>
|
||||
<td><input class="te-cell te-cell--sm" [(ngModel)]="row.tokNo" /></td>
|
||||
<td><input class="te-cell te-cell--sm" [(ngModel)]="row.cheqDate" placeholder="dd/mm/yy" /></td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="te-grid-footer">
|
||||
<span class="te-eff-date"
|
||||
>Eff. Dt : <strong>{{ today }}</strong></span
|
||||
>
|
||||
<button class="te-add-btn" type="button" (click)="addRow()" title="Add row">+</button>
|
||||
<!-- ── 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>
|
||||
|
||||
<!-- ACTION BUTTONS -->
|
||||
<div class="te-actions">
|
||||
<button class="te-btn te-btn--exit" type="button" (click)="onExit()">Exit</button>
|
||||
<button class="te-btn" type="button" (click)="onCancel()">Cancel</button>
|
||||
<button class="te-btn" type="button">Drop</button>
|
||||
<button class="te-btn" type="button" (click)="onSearch()">Search</button>
|
||||
<button class="te-btn te-btn--mode" type="button">Change Tr.Mode</button>
|
||||
<button class="te-btn te-btn--primary" type="button" (click)="onSave()" [disabled]="saving">
|
||||
<svg width="14" height="14" 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 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>
|
||||
<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="Cash">Cash</option>
|
||||
<option value="Transfer">Transfer</option>
|
||||
<option value="Clearing">Clearing</option>
|
||||
<option value="Journal">Journal</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--trtp">
|
||||
<label class="ws-f__lbl">TXN TP.</label>
|
||||
<select class="ws-f__ctl" [(ngModel)]="row.trTp">
|
||||
<option value=""></option>
|
||||
@for (t of trTpOptions; track t) {
|
||||
<option [value]="t">{{ t }}</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">
|
||||
@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"
|
||||
[(ngModel)]="row.acNo"
|
||||
(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'"
|
||||
(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--paytype">
|
||||
<label class="ws-f__lbl ws-f__lbl--accent">PAY TYPE</label>
|
||||
<select
|
||||
class="ws-f__ctl"
|
||||
[(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--amt">
|
||||
<label class="ws-f__lbl">AMOUNT</label>
|
||||
<div class="ws-amt-wrap" [class.ws-amt-wrap--cash]="isCashPayment && selectedRow === row">
|
||||
<span class="ws-amt-prefix">₹</span>
|
||||
<input
|
||||
class="ws-f__ctl ws-amt-input"
|
||||
type="number"
|
||||
[value]="
|
||||
isCashPayment && selectedRow === row ? denominationTotal || '' : (row.amount ?? '')
|
||||
"
|
||||
[disabled]="isCashPayment && selectedRow === row"
|
||||
(input)="row.amount = +$any($event.target).value"
|
||||
placeholder="0.00"
|
||||
/>
|
||||
</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" />
|
||||
</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>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Action buttons footer -->
|
||||
<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>
|
||||
</div>
|
||||
<div class="ws-footer__actions">
|
||||
<button class="ws-btn ws-btn--post" type="button" (click)="onSave()" [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 ? 'Posting…' : 'Save' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user