Files
cbsnexora/src/app/cashier/day-book/day-book.component.html
T

271 lines
11 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="pg">
<header class="pg-head">
<div>
<h1 class="pg-title">{{ title }}</h1>
<p class="pg-sub">
Every voucher raised on the selected date — cash deposits, withdrawals, disbursements, collections, interest and
journals, with each person's own credit, debit and net.
</p>
</div>
<div class="pg-toolbar">
<button type="button" class="pg-btn" (click)="shiftDate(-1)" title="Previous day">
<mat-icon>chevron_left</mat-icon>
</button>
<label class="pg-field">
<span>Date</span>
<input type="date" [value]="date" (change)="date = $any($event.target).value; load()" />
</label>
<button type="button" class="pg-btn" (click)="shiftDate(1)" title="Next day">
<mat-icon>chevron_right</mat-icon>
</button>
<button type="button" class="pg-btn" (click)="goToday()">Today</button>
<button type="button" class="pg-btn pg-btn--primary" (click)="load()" [disabled]="loading">
<mat-icon>refresh</mat-icon> Reload
</button>
</div>
</header>
<!-- Filters -->
<div class="db-filters">
<label class="pg-field">
<span>Side</span>
<select [(ngModel)]="side" (change)="load()">
<option value="">All</option>
<option value="CR">CR · Credit (money in)</option>
<option value="DR">DR · Debit (money out)</option>
</select>
</label>
<label class="pg-field">
<span>Status</span>
<select [(ngModel)]="status" (change)="load()">
<option value="">All</option>
<option value="APPROVED">Approved</option>
<option value="PENDING">Pending</option>
<option value="REJECTED">Rejected</option>
</select>
</label>
<label class="pg-field db-filters__search">
<span>Search</span>
<input type="text" [(ngModel)]="search" placeholder="Voucher no, account, party, payment type, narration…" />
</label>
<button type="button" class="pg-btn" (click)="exportCsv()" [disabled]="filteredRows.length === 0">
<mat-icon>download</mat-icon> CSV
</button>
</div>
@if (errorMsg) {
<div class="pg-alert pg-alert--error">{{ errorMsg }}</div>
}
@if (loading) {
<div class="pg-empty">Loading…</div>
}
@if (!loading && loaded && rows.length === 0) {
<div class="pg-empty">
<mat-icon>receipt_long</mat-icon>
<p>No vouchers were raised on {{ date }}.</p>
</div>
}
@if (!loading && filteredRows.length > 0) {
<!-- Totals bar. Reflects what is on screen, so a search or side filter still reconciles. -->
<div class="closing-summary">
<div class="closing-summary__item closing-summary__item--in">
<span class="closing-summary__label">Credit · CR (money in)</span>
<span class="closing-summary__value">₹{{ shownCredit | number: '1.2-2' }}</span>
</div>
<div class="closing-summary__sep">−</div>
<div class="closing-summary__item closing-summary__item--out">
<span class="closing-summary__label">Debit · DR (money out)</span>
<span class="closing-summary__value">₹{{ shownDebit | number: '1.2-2' }}</span>
</div>
<div class="closing-summary__sep">=</div>
<div
class="closing-summary__item"
[class.closing-summary__item--net-pos]="shownCredit - shownDebit >= 0"
[class.closing-summary__item--net-neg]="shownCredit - shownDebit < 0"
>
<span class="closing-summary__label">Net</span>
<span class="closing-summary__value">₹{{ shownCredit - shownDebit | number: '1.2-2' }}</span>
</div>
</div>
<p class="db-note">
Totals count approved vouchers only — pending and rejected rows are listed but never move the money.
</p>
<!-- The day per person, for the officer who signs the book off. The sections below trace one
voucher at a time; this is the other reading a branch needs — what each member of staff
handled, and whether their column adds up. Built from whoever appears in the day's rows, so
one person working alone gets one line and five people get five. -->
@if (byUser.length > 0) {
<div class="pg-card db-section db-byuser">
<div class="db-section__head">
<strong>Officer Verification Sheet</strong>
<span class="db-muted">Each person's own credit, debit and net for the day</span>
<span class="db-section__count"> {{ byUser.length }} {{ byUser.length === 1 ? 'person' : 'people' }} </span>
</div>
<div class="pg-table-wrap">
<table class="pg-table db-table">
<thead>
<tr>
<th>Handled by</th>
<th class="num">Items</th>
<th class="num">Credit (CR)</th>
<th class="num">Debit (DR)</th>
<th class="num">Net</th>
<th class="num">Pending</th>
</tr>
</thead>
<tbody>
@for (u of byUser; track u.name) {
<tr>
<td class="db-byuser__name">{{ u.name }}</td>
<td class="num">{{ u.items }}</td>
<td class="num db-byuser__cr">₹{{ u.credit | number: '1.2-2' }}</td>
<td class="num db-byuser__dr">₹{{ u.debit | number: '1.2-2' }}</td>
<td
class="num db-byuser__net"
[class.db-byuser__net--pos]="u.net >= 0"
[class.db-byuser__net--neg]="u.net < 0"
>
₹{{ u.net | number: '1.2-2' }}
</td>
<td class="num">
@if (u.pending > 0) {
<span class="db-badge db-badge--pending">{{ u.pending }}</span>
} @else {
—
}
</td>
</tr>
}
</tbody>
<!-- The sheet's own total, which must equal the day's totals above. That equality is
what makes the sheet verifiable rather than just informative. -->
<tfoot>
<tr class="db-byuser__total">
<td>All staff</td>
<td class="num">{{ byUserTotals.items }}</td>
<td class="num db-byuser__cr">₹{{ byUserTotals.credit | number: '1.2-2' }}</td>
<td class="num db-byuser__dr">₹{{ byUserTotals.debit | number: '1.2-2' }}</td>
<td
class="num db-byuser__net"
[class.db-byuser__net--pos]="byUserTotals.net >= 0"
[class.db-byuser__net--neg]="byUserTotals.net < 0"
>
₹{{ byUserTotals.net | number: '1.2-2' }}
</td>
<td class="num">
@if (byUserTotals.pending > 0) {
{{ byUserTotals.pending }}
} @else {
—
}
</td>
</tr>
</tfoot>
</table>
</div>
</div>
}
<!-- One table per source register rather than one long list. The two read differently: the
cashier's rows are cash that actually crossed the counter, the clerk's are requests nobody
has actioned yet. Mixed together they invited the wrong sum. -->
@for (s of sections; track s.key) {
<!-- The section's own colour on the left edge: cash that actually crossed the counter, work
still waiting to be actioned, and postings the system made are three different things to
a person closing the day. -->
<div [class]="'pg-card db-section db-section--' + s.key">
<div class="db-section__head">
<strong>{{ s.label }}</strong>
<span class="db-muted">{{ s.hint }}</span>
<span class="db-section__count">{{ s.rows.length }} row{{ s.rows.length === 1 ? '' : 's' }}</span>
</div>
<table class="pg-table">
<thead>
<tr>
<th>Voucher No</th>
<th>Dr/Cr</th>
<th>Type</th>
<th>Account</th>
<th>Party</th>
<th class="num">Amount</th>
<th>Payment</th>
<th>Status</th>
<th>By</th>
<th>Approved By</th>
<th>Time</th>
</tr>
</thead>
<tbody>
@for (r of s.rows; track r.voucherNo ?? r.section + r.txnId) {
<tr [class.db-row--cr]="r.side === 'CR'" [class.db-row--dr]="r.side === 'DR'">
<td>
<span class="pg-chip">{{ r.voucherNo ?? '—' }}</span>
</td>
<td>
<span class="db-side" [class.db-side--cr]="r.side === 'CR'" [class.db-side--dr]="r.side === 'DR'">
{{ r.side }}
</span>
</td>
<td>{{ r.kind }}</td>
<td>
{{ r.accountNo ?? '—' }}
@if (r.accountType) {
<span class="db-muted">· {{ r.accountType }}</span>
}
</td>
<td>{{ r.party ?? '—' }}</td>
<td class="num">₹{{ r.amount | number: '1.2-2' }}</td>
<td>{{ r.paymentTypeName ?? '—' }}</td>
<td>
<span [class]="statusClass(r.status)">{{ r.status }}</span>
</td>
<td>{{ r.handledBy ?? '—' }}</td>
<td>
{{ r.approvedBy ?? '—' }}
@if (r.approvedBy && r.actionedAt) {
<span class="db-muted">· {{ timeOf(r.actionedAt) }}</span>
}
</td>
<td>{{ timeOf(r.submittedAt) }}</td>
</tr>
}
</tbody>
<tfoot>
<tr>
<td colspan="5">{{ s.label }} subtotal</td>
<td class="num">
@if (s.credit > 0 && s.debit > 0) {
₹{{ s.credit | number: '1.2-2' }} CR · ₹{{ s.debit | number: '1.2-2' }} DR
} @else if (s.credit > 0) {
₹{{ s.credit | number: '1.2-2' }} CR
} @else if (s.debit > 0) {
₹{{ s.debit | number: '1.2-2' }} DR
} @else {
—
}
</td>
<td colspan="5"></td>
</tr>
</tfoot>
</table>
</div>
}
}
@if (!loading && loaded && rows.length > 0 && filteredRows.length === 0) {
<div class="pg-empty">No voucher matches “{{ search }}”.</div>
}
</div>