Files
cbsnexora/src/app/cashier/my-work/my-work.component.html
T
niteshandClaude Opus 5 b71619c402 feat(cashier, sponsor-dd): GL settlement and DD funding entry points, DD registry redesign
Transaction Entry (clerk):
- Route a two-row Fund Transfer of Dr SHR / Cr GL to the share-withdrawal
  request with settlementMode GL, so a clerk can settle a share withdrawal
  into any GL account instead of the member's linked savings account.
- Offer GL as an account type for Demand Draft funding, and submit it as a
  GL debit (no client or savings/current account is involved).

My Work:
- Add a voucher detail dialog so a clerk can open a raised voucher from the
  request list and see its GL lines.

Closing Cash Report:
- Fix the total row so it reflects the corrected opening balance.

DD Leaflet Registry and DD Inventory:
- Redesign both pages onto the shared usr-* visual language: gradient header
  badge, status filter chips with counts, card-wrapped sticky-header table,
  and one primary action per row with the rest behind an overflow menu.
  Filtering, pagination and every action condition are unchanged.
- Register faHandPaper, needed by the new Stop Payment menu item.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 17:17:22 +05:30

217 lines
8.5 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">
<div>
<div class="pg-eyebrow">MY WORK</div>
<h1 class="pg-title">My Vouchers</h1>
<p class="pg-sub">
Everything you entered and everything you approved — receipts, payments and journals in one list.
</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>
<button type="button" class="pg-btn pg-btn--primary" (click)="load()" [disabled]="loading">
<mat-icon>refresh</mat-icon> Run
</button>
</div>
</header>
@if (error) {
<div class="pg-alert pg-alert--error">{{ error }}</div>
}
@if (loading) {
<div class="pg-empty">Loading…</div>
}
@if (report && !loading) {
<div class="mw-who">
<strong>{{ report.displayName || report.username }}</strong>
<span class="mw-who__sep">·</span>
{{ report.from | date: 'dd MMM yyyy' }}
@if (report.from !== report.to) {
<span> to {{ report.to | date: 'dd MMM yyyy' }}</span>
}
</div>
<!-- The day at a glance, read off the same rows the table below shows: narrow "My part" to
Entered or Approved and these five follow, instead of staying pinned to the combined
day total regardless of the filter. -->
<div class="mw-tiles">
<div class="mw-tile">
<div class="mw-tile__label">Received</div>
<div class="mw-tile__value mw-tile__value--cr">₹{{ receivedTotal | number: '1.2-2' }}</div>
</div>
<div class="mw-tile">
<div class="mw-tile__label">Payments</div>
<div class="mw-tile__value mw-tile__value--dr">₹{{ paymentTotal | number: '1.2-2' }}</div>
</div>
<div class="mw-tile">
<div class="mw-tile__label">Journal</div>
<div class="mw-tile__value">₹{{ journalTotal | number: '1.2-2' }}</div>
</div>
<div class="mw-tile">
<div class="mw-tile__label">Entered</div>
<div class="mw-tile__value">{{ raisedCount }}</div>
</div>
<div class="mw-tile">
<div class="mw-tile__label">Approved</div>
<div class="mw-tile__value">{{ verifiedCount }}</div>
</div>
</div>
@if (report.count > 0) {
<div class="mw-filters">
<label class="pg-field">
<span>Side</span>
<select [(ngModel)]="sideFilter">
<option value="">All</option>
<option value="CR">Received</option>
<option value="DR">Payment</option>
<option value="JV">Journal</option>
</select>
</label>
<label class="pg-field">
<span>My part</span>
<select [(ngModel)]="roleFilter">
<option value="">All</option>
<option value="RAISED">Entered</option>
<option value="VERIFIED">Approved</option>
<option value="RAISED_AND_VERIFIED">Entered &amp; approved</option>
</select>
</label>
@if (isFiltered) {
<button type="button" class="pg-btn" (click)="clearFilters()"><mat-icon>clear</mat-icon> Clear</button>
}
</div>
}
@if (rows.length === 0) {
<div class="pg-empty">
@if (report.count === 0) {
Nothing recorded against you for this period.
} @else {
No vouchers match these filters — {{ report.count }} hidden.
}
</div>
} @else {
<div class="mw-count">
Showing {{ rows.length }}
@if (isFiltered) {
<span>of {{ report.count }}</span>
}
{{ rows.length === 1 ? 'voucher' : 'vouchers' }}
<span class="mw-count__total">Total ₹{{ filteredTotal | number: '1.2-2' }}</span>
</div>
<!-- One table per part played, not one long list. Entering a request and passing somebody
else's are different jobs answerable to different people, and each carries its own total
so a maker's tally and a checker's can both be read off the page. -->
@for (g of groups; track g.key) {
<div class="pg-card mw-group" [class]="'mw-group--' + g.key">
<div class="mw-group__head">
<strong>{{ g.label }}</strong>
<span class="mw-group__hint">{{ g.hint }}</span>
<span class="mw-group__count">
{{ g.rows.length }} {{ g.rows.length === 1 ? 'voucher' : 'vouchers' }}
<span class="mw-group__total">₹{{ g.total | number: '1.2-2' }}</span>
</span>
</div>
<div class="pg-table-wrap">
<table class="pg-table mw-table">
<thead>
<tr>
<th>Voucher</th>
<th>Side</th>
<th>Type</th>
<th>Debit</th>
<th>Credit</th>
<th class="num">Amount</th>
<th>My part</th>
<th>Status</th>
<th>Time</th>
</tr>
</thead>
<tbody>
@for (r of g.rows; track r.voucherNo) {
<!-- The left edge repeats the section's own colour, so a row still says which part it
is when the heading has scrolled away. Clicking anywhere on the row opens the full
slip -- every line, not just the first debit and first credit this table has room
to show. -->
<tr
[class]="'mw-row mw-row--' + r.myRole"
class="mw-row--clickable"
(click)="openDetail(r.voucherNo)"
[attr.aria-busy]="loadingDetailFor === r.voucherNo"
>
<td class="mw-vno">
{{ r.voucherNo }}
@if (loadingDetailFor === r.voucherNo) {
<mat-icon class="mw-vno__spinner" inline>hourglass_empty</mat-icon>
}
</td>
<td>
<span class="mw-badge" [class]="'mw-badge--' + (r.side || 'JV')">{{ sideLabel(r.side) }}</span>
</td>
<td class="mw-type">{{ r.voucherType }}</td>
<td>
{{ r.debitParty || '—' }}
@if (r.debitAccount) {
<span class="mw-acct">{{ r.debitAccount }}</span>
}
</td>
<td>
{{ r.creditParty || '—' }}
@if (r.creditAccount) {
<span class="mw-acct">{{ r.creditAccount }}</span>
}
@if (r.extraLines > 0) {
<span class="mw-more" title="Click the row to see every line">+{{ r.extraLines }} more</span>
}
</td>
<td class="num">₹{{ r.amount | number: '1.2-2' }}</td>
<td>
<span class="mw-part" [class]="'mw-part--' + r.myRole">
<mat-icon class="mw-part__icon">{{ roleIcon(r.myRole) }}</mat-icon>
{{ roleLabel(r.myRole) }}
</span>
<!-- Whose entry it was, when it was not this user's — that is the whole point of
an approval row appearing here at all. -->
@if (r.myRole === 'VERIFIED' && r.raisedBy) {
<span class="mw-acct">by {{ r.raisedBy }}</span>
}
</td>
<td>
<span class="mw-status" [class]="'mw-status--' + statusClass(r.verificationStatus)">
{{ statusLabel(r.verificationStatus) }}
</span>
</td>
<td class="mw-time">{{ r.createdAt | date: 'HH:mm' }}</td>
</tr>
}
</tbody>
</table>
</div>
</div>
}
}
}
</div>