Files
cbsnexora/src/app/cashier/voucher-report/voucher-report.component.html
T

238 lines
9.4 KiB
HTML

<!--
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 vr-no-print">
<div>
<div class="pg-eyebrow">CASHIER · REPORT</div>
<h1 class="pg-title">{{ title }}</h1>
<p class="pg-sub">{{ subtitle }}</p>
</div>
<div class="pg-toolbar">
<label class="pg-field">
<span>Period</span>
<select [(ngModel)]="period" (change)="load()">
<option value="DAY">Day</option>
<option value="MONTH">Month</option>
</select>
</label>
<label class="pg-field">
<span>{{ period === 'DAY' ? 'Date' : 'Month of' }}</span>
<input type="date" [(ngModel)]="anchor" (change)="load()" />
</label>
@if (report?.canSeeAllUsers) {
<label class="pg-field">
<span>User</span>
<input
type="text"
[(ngModel)]="usernameFilter"
(keyup.enter)="load()"
placeholder="All users"
autocomplete="off"
/>
</label>
}
<button type="button" class="pg-btn pg-btn--primary" (click)="load()" [disabled]="loading">
<mat-icon>refresh</mat-icon> Run
</button>
@if (report && count > 0) {
<button type="button" class="pg-btn" (click)="print()"><mat-icon>print</mat-icon> Print</button>
}
</div>
</header>
@if (error) {
<div class="pg-alert pg-alert--error vr-no-print">{{ error }}</div>
}
@if (loading) {
<div class="pg-empty vr-no-print">Loading…</div>
}
@if (report && !loading) {
<div class="vr-period">
{{ report.from | date: 'dd MMM yyyy' }}
@if (report.from !== report.to) {
— {{ report.to | date: 'dd MMM yyyy' }}
}
· {{ count }} {{ noun }}{{ count === 1 ? '' : 's' }} · ₹{{ totalAmount | number: '1.2-2' }}
@if (report.username) {
· {{ report.username }}
}
<!-- Said plainly rather than left to be inferred from a missing filter: a cashier should know
this is their own work and not the branch's. -->
@if (!report.canSeeAllUsers) {
<span class="vr-scope-note">Your own vouchers</span>
}
<!-- The total on a cash register means something narrower than the whole voucher book, so the
screen says which one it is instead of leaving the figure ambiguous. -->
@if (cashOnly) {
<span class="vr-scope-note">Cash only</span>
}
</div>
<!-- A voucher with no CR/DR prefix belongs to neither report. Say so, so the two reports not
adding up to the day's total is explained rather than left to be puzzled over. -->
@if (unclassifiedCount > 0) {
<div class="pg-alert vr-no-print">
{{ unclassifiedCount }} voucher{{ unclassifiedCount === 1 ? '' : 's' }} in this period
{{ unclassifiedCount === 1 ? 'carries' : 'carry' }} no CR or DR prefix, so
{{ unclassifiedCount === 1 ? 'it appears' : 'they appear' }} on neither the Received nor the Payment report.
</div>
}
<!-- Which part of the total has notes and coins counted behind it. Shown on the cash register
as well as the full one: a voucher with no breakdown is still real money, so it stays in
the total, and what the drawer can be checked against is stated instead of assumed. -->
@if (uncountedCount > 0) {
<div class="pg-alert vr-no-print">
{{ uncountedCount }} of these {{ noun }}s {{ uncountedCount === 1 ? 'has' : 'have' }} no denomination breakdown
recorded, totalling ₹{{ uncountedTotal | number: '1.2-2' }}. Some registers pay out without counting notes
against the voucher, so these are still cash — ₹{{ countedTotal | number: '1.2-2' }} of the total has a
breakdown behind it.
</div>
}
<!-- Explains the blank Debit/Credit cells instead of leaving them looking like lost data. -->
@if (missingSidesCount > 0) {
<div class="pg-alert vr-no-print">
{{ missingSidesCount }} of these {{ noun }}s {{ missingSidesCount === 1 ? 'has' : 'have' }} no debit or credit
recorded. The sides are attached when a request is approved, so vouchers raised before that step was added keep
their number and amount but show no accounts. Newly raised {{ noun }}s carry both sides.
</div>
}
@if (count === 0) {
<div class="pg-empty">No {{ noun }}s were raised in this period.</div>
} @else {
<div class="vr-summaries">
<div class="pg-card">
<div class="pos-section-label">By type</div>
<table class="pg-table">
<thead>
<tr>
<th>Type</th>
<th class="num">Count</th>
<th class="num">Amount</th>
</tr>
</thead>
<tbody>
@for (row of byType; track row.type) {
<tr>
<td>{{ row.type }}</td>
<td class="num">{{ row.count }}</td>
<td class="num">{{ row.total | number: '1.2-2' }}</td>
</tr>
}
</tbody>
</table>
</div>
<!-- Only worth showing to someone who can see more than one person's work. -->
@if (report.canSeeAllUsers) {
<div class="pg-card">
<div class="pos-section-label">By user</div>
<table class="pg-table">
<thead>
<tr>
<th>User</th>
<th class="num">Count</th>
<th class="num">Amount</th>
</tr>
</thead>
<tbody>
@for (row of byUser; track row.username) {
<tr>
<td>{{ row.username }}</td>
<td class="num">{{ row.count }}</td>
<td class="num">{{ row.total | number: '1.2-2' }}</td>
</tr>
}
</tbody>
</table>
</div>
}
</div>
<div class="pg-card">
<div class="pos-section-label">{{ isReceived ? 'Receipts' : 'Payments' }}</div>
<table class="pg-table vr-rows">
<thead>
<tr>
<th>Voucher No</th>
<th>Type</th>
<th>Debit</th>
<th>Credit</th>
<th class="num">Amount</th>
<th>User</th>
<!-- Two dates, not one, on the system register.
A voucher's timestamp is the minute it was written; the posting behind it carries
the business date. A nightly run finishing after midnight writes today's voucher
for yesterday's work, so a reader opening this screen today is looking at last
night's postings — and without the ledger date they would take the timestamp for
the day the money moved. -->
@if (systemOnly) {
<th>Ledger date</th>
}
<th>Time</th>
</tr>
</thead>
<tbody>
@for (row of rows; track row.voucherNo) {
<tr>
<td>
{{ row.voucherNo }}
@if (row.verificationStatus === 'EXCEPTION') {
<mat-icon class="vr-flag" title="An exception was raised against this voucher"
>error_outline</mat-icon
>
}
</td>
<td>{{ row.voucherType || '—' }}</td>
<!-- A voucher raised before its sides were recorded has neither. Say that once,
across both cells, rather than showing two dashes that read as "nowhere". -->
@if (!hasSides(row)) {
<td colspan="2" class="vr-nosides">Sides not recorded</td>
} @else {
<td>
<div class="vr-party">{{ row.debitParty || '—' }}</div>
@if (row.debitAccount) {
<div class="vr-acct">{{ row.debitAccount }}</div>
}
</td>
<td>
<div class="vr-party">{{ row.creditParty || '—' }}</div>
@if (row.creditAccount) {
<div class="vr-acct">{{ row.creditAccount }}</div>
}
<!-- A DD with charges, or a compound journal, has sides this row does not show. -->
@if (row.extraLines > 0) {
<div class="vr-more">+{{ row.extraLines }} more on voucher</div>
}
</td>
}
<td class="num">{{ row.amount | number: '1.2-2' }}</td>
<td>{{ row.username || '—' }}</td>
@if (systemOnly) {
<td class="vr-ledgerdate" [class.vr-ledgerdate--prior]="row.postedAfterClose">
{{ row.ledgerDate ? (row.ledgerDate | date: 'dd MMM yyyy') : '—' }}
@if (row.postedAfterClose) {
<span class="vr-priorflag" title="Posted after that day had closed">after close</span>
}
</td>
}
<td>{{ row.createdAt | date: 'dd MMM, HH:mm' }}</td>
</tr>
}
</tbody>
</table>
</div>
}
}
</div>