A
This commit is contained in:
+395
-201
@@ -6,222 +6,416 @@
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<h2 mat-dialog-title>Apply Share Advance Collection</h2>
|
||||
|
||||
<mat-dialog-content>
|
||||
<p class="intro">
|
||||
Tick the receipts that make up this payment to settle share account <b>{{ data.accountNo }}</b
|
||||
>, then apply. Today's unapplied receipts for this member are listed below — tick more than one where they paid in
|
||||
instalments. The amount moves out of Share Sundry into share capital, and any entry fee is posted to fee income.
|
||||
</p>
|
||||
|
||||
<!-- What this account needs, up front — so the counter knows the full figure, and that it may take
|
||||
a second receipt for the entry fee, before a search ever comes up short. -->
|
||||
@if (requirement) {
|
||||
<div class="requirement-summary">
|
||||
<span class="requirement-line">
|
||||
Required: <b>{{ requirement.shareAmount | number: '1.2-2' }}</b> (Shares)
|
||||
@if (requirement.chargeAmount > 0) {
|
||||
+ <b>{{ requirement.chargeAmount | number: '1.2-2' }}</b> (Entry Fee) =
|
||||
<b>{{ requirement.required | number: '1.2-2' }}</b> Total
|
||||
}
|
||||
</span>
|
||||
@if (selectedIds.length > 0) {
|
||||
<span class="requirement-progress" [class.covered]="shortfall === 0">
|
||||
@if (shortfall === 0) {
|
||||
Share receipts covered
|
||||
} @else {
|
||||
{{ selectedTotal | number: '1.2-2' }} of {{ shareRequired | number: '1.2-2' }} collected —
|
||||
{{ shortfall | number: '1.2-2' }} short
|
||||
}
|
||||
</span>
|
||||
}
|
||||
<div class="dialog-wrapper">
|
||||
<div class="dialog-header">
|
||||
<div class="header-icon">
|
||||
<mat-icon>account_balance_wallet</mat-icon>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- For a receipt the list does not carry — one taken on an earlier day, or a slip the member is
|
||||
holding that is quicker to type than to find. Both the GL account the deposit was posted to
|
||||
and the voucher number are required — never resolved from a share product's own accounting
|
||||
setup, so a broken or missing product mapping can never make a genuine voucher unreachable. -->
|
||||
<div class="voucher-section">
|
||||
<div class="voucher-section-label">Share Receipt</div>
|
||||
<div class="gl-select-field">
|
||||
<select class="gl-select" [(ngModel)]="glAccountNo" [disabled]="lookingUp || submitting || loadingGlAccounts">
|
||||
<option value="" disabled>{{ loadingGlAccounts ? 'Loading GL accounts…' : 'Select GL account…' }}</option>
|
||||
@for (gl of glAccounts; track gl.id) {
|
||||
<option [value]="gl.gl_code">{{ gl.gl_code }} — {{ gl.name }}</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="voucher-lookup">
|
||||
<input
|
||||
type="text"
|
||||
class="voucher-input"
|
||||
placeholder="Voucher no. (e.g. CR/001/2608/0001)"
|
||||
[(ngModel)]="voucherNo"
|
||||
[disabled]="lookingUp || submitting"
|
||||
(keyup.enter)="lookupVoucher()"
|
||||
/>
|
||||
<button
|
||||
mat-stroked-button
|
||||
type="button"
|
||||
[disabled]="!voucherNo.trim() || !glAccountNo.trim() || lookingUp || submitting"
|
||||
(click)="lookupVoucher()"
|
||||
>
|
||||
{{ lookingUp ? 'Finding…' : 'Find' }}
|
||||
</button>
|
||||
<div class="header-text">
|
||||
<h2>Apply Share Collection</h2>
|
||||
<p>
|
||||
Share account <strong class="mono">{{ data.accountNo }}</strong>
|
||||
</p>
|
||||
</div>
|
||||
<button type="button" mat-icon-button class="header-close" [mat-dialog-close]="false" aria-label="Close">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Shown only when this account actually has an outstanding fee -- a member sometimes pays it as
|
||||
a second receipt into a different GL account rather than folding it into the share-value
|
||||
deposit. This search only ever verifies that receipt exists; it never moves its money -- that
|
||||
stays exactly where the cashier already deposited it, unlike the share receipts above which
|
||||
genuinely move out of Sundry. -->
|
||||
@if (requirement && requirement.chargeAmount > 0) {
|
||||
<div class="voucher-section">
|
||||
<div class="voucher-section-label">Entry Fee Receipt — verification only, no GL entry is posted for it</div>
|
||||
|
||||
@if (chargeReceipt) {
|
||||
<div class="charge-receipt-found" [class.short]="chargeShortfall > 0">
|
||||
<mat-icon>{{ chargeShortfall > 0 ? 'error_outline' : 'check_circle_outline' }}</mat-icon>
|
||||
<span class="charge-receipt-detail">
|
||||
<b class="mono">{{ chargeReceipt.voucher_no || chargeReceipt.receipt_no }}</b>
|
||||
— {{ chargeReceipt.amount | number: '1.2-2' }}
|
||||
@if (chargeShortfall > 0) {
|
||||
(short by {{ chargeShortfall | number: '1.2-2' }})
|
||||
} @else {
|
||||
— entry fee verified
|
||||
}
|
||||
</span>
|
||||
<button mat-button type="button" [disabled]="submitting" (click)="clearChargeReceipt()">Change</button>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="gl-select-field">
|
||||
<select
|
||||
class="gl-select"
|
||||
[(ngModel)]="chargeGlAccountNo"
|
||||
[disabled]="lookingUpCharge || submitting || loadingGlAccounts"
|
||||
<mat-dialog-content class="dialog-body">
|
||||
@if (loading) {
|
||||
<div class="state-block">
|
||||
<mat-spinner diameter="30"></mat-spinner>
|
||||
<span>Loading receipts…</span>
|
||||
</div>
|
||||
} @else {
|
||||
@if (requirement) {
|
||||
<div class="sc-card">
|
||||
<!-- Share row -->
|
||||
<div
|
||||
class="sc-row"
|
||||
[class.sc-ok]="shareReceipt && shareShortfall === 0"
|
||||
[class.sc-bad]="shareReceipt && shareShortfall > 0"
|
||||
>
|
||||
<option value="" disabled>{{ loadingGlAccounts ? 'Loading GL accounts…' : 'Select GL account…' }}</option>
|
||||
<div class="sc-left">
|
||||
<span class="sc-label">Share Collection</span>
|
||||
<span class="sc-sub"
|
||||
>Required: <b>{{ requirement.shareAmount | number: '1.2-2' }}</b></span
|
||||
>
|
||||
</div>
|
||||
<div class="sc-right">
|
||||
<span
|
||||
class="sc-val"
|
||||
[class.sc-val-ok]="shareReceipt && shareShortfall === 0"
|
||||
[class.sc-val-bad]="shareReceipt && shareShortfall > 0"
|
||||
>
|
||||
{{ shareReceipt ? (shareReceipt.amount | number: '1.2-2') : '—' }}
|
||||
</span>
|
||||
@if (shareShortfall > 0) {
|
||||
<span class="sc-tag sc-tag-bad">-{{ shareShortfall | number: '1.2-2' }}</span>
|
||||
} @else if (shareReceipt && shareShortfall === 0) {
|
||||
<span class="sc-tag sc-tag-ok">✓ Covered</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Entry Fee row -->
|
||||
@if (hasFee) {
|
||||
<div
|
||||
class="sc-row sc-sep"
|
||||
[class.sc-ok]="chargeReceipt && feeShortfall === 0"
|
||||
[class.sc-bad]="chargeReceipt && feeShortfall > 0"
|
||||
>
|
||||
<div class="sc-left">
|
||||
<span class="sc-label">Entry Fee</span>
|
||||
<span class="sc-sub"
|
||||
>Required: <b>{{ requirement.chargeAmount | number: '1.2-2' }}</b></span
|
||||
>
|
||||
</div>
|
||||
<div class="sc-right">
|
||||
<span
|
||||
class="sc-val"
|
||||
[class.sc-val-ok]="chargeReceipt && feeShortfall === 0"
|
||||
[class.sc-val-bad]="chargeReceipt && feeShortfall > 0"
|
||||
>
|
||||
{{ chargeReceipt ? (chargeReceipt.amount | number: '1.2-2') : '—' }}
|
||||
</span>
|
||||
@if (feeShortfall > 0) {
|
||||
<span class="sc-tag sc-tag-bad">-{{ feeShortfall | number: '1.2-2' }}</span>
|
||||
} @else if (chargeReceipt && feeShortfall === 0) {
|
||||
<span class="sc-tag sc-tag-ok">✓ Covered</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Total row -->
|
||||
<div class="sc-total-row" [class.sc-total-ok]="canApply">
|
||||
<span class="sc-total-lbl">Total Payable</span>
|
||||
<div class="sc-total-nums">
|
||||
<span class="sc-total-req mono">{{ requirement.required | number: '1.2-2' }}</span>
|
||||
<span class="sc-arrow">→</span>
|
||||
<span class="sc-total-got mono" [class.sc-total-got-ok]="canApply">
|
||||
{{
|
||||
(shareReceipt?.amount || 0) + (hasFee && chargeReceipt ? chargeReceipt.amount || 0 : 0)
|
||||
| number: '1.2-2'
|
||||
}}
|
||||
</span>
|
||||
<span class="sc-status-pill" [class.sc-pill-ok]="canApply" [class.sc-pill-pending]="!canApply">
|
||||
{{ canApply ? '✓ Ready' : 'Pending' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ── Slot 1: the share receipt ─────────────────────────────────────────── -->
|
||||
<section class="slot">
|
||||
<header class="slot-head">
|
||||
<span class="slot-step">1</span>
|
||||
<div class="slot-title">
|
||||
<h3>Share Collection Receipt</h3>
|
||||
<p>Posts Dr Sundry / Cr Share Capital.</p>
|
||||
</div>
|
||||
<button type="button" class="slot-search" (click)="openSearch('share')">
|
||||
<mat-icon>search</mat-icon>
|
||||
<span>Find by voucher</span>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
@if (shareReceipt) {
|
||||
<div class="chosen" [class.chosen-short]="shareShortfall > 0">
|
||||
<div class="chosen-badge">
|
||||
<mat-icon>{{ shareShortfall > 0 ? 'warning' : 'check_circle' }}</mat-icon>
|
||||
</div>
|
||||
<div class="chosen-main">
|
||||
<span class="chosen-voucher mono">{{ shareReceipt.voucherNo }}</span>
|
||||
<span class="chosen-gl">{{ glLabel(shareReceipt) }}</span>
|
||||
</div>
|
||||
<span class="chosen-amount mono">{{ shareReceipt.amount | number: '1.2-2' }}</span>
|
||||
<button type="button" class="chosen-clear" (click)="clear('share')" aria-label="Change receipt">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
@if (shareShortfall > 0) {
|
||||
<p class="slot-note note-bad">
|
||||
Short by {{ shareShortfall | number: '1.2-2' }} — this receipt does not cover
|
||||
{{ shareRequired | number: '1.2-2' }}.
|
||||
</p>
|
||||
} @else if (requirement) {
|
||||
<p class="slot-note note-good">Covers the {{ shareRequired | number: '1.2-2' }} payable.</p>
|
||||
}
|
||||
} @else {
|
||||
@if (optionsFor('share').length) {
|
||||
<ul class="options">
|
||||
@for (option of optionsFor('share'); track option.key) {
|
||||
<li class="option" [class.option-busy]="attaching === option.key" (click)="pick('share', option)">
|
||||
<span class="option-radio"></span>
|
||||
<div class="option-main">
|
||||
<span class="option-voucher mono">{{ option.voucherNo }}</span>
|
||||
<span class="option-meta">{{ option.date }} · {{ glLabel(option) }}</span>
|
||||
@if (option.description) {
|
||||
<span class="option-desc">{{ option.description }}</span>
|
||||
}
|
||||
</div>
|
||||
<span class="option-amount mono">{{ option.amount | number: '1.2-2' }}</span>
|
||||
@if (attaching === option.key) {
|
||||
<mat-spinner diameter="16"></mat-spinner>
|
||||
} @else if (option.collectionId !== null) {
|
||||
<button
|
||||
type="button"
|
||||
class="option-info"
|
||||
(click)="openDetail(option, $event)"
|
||||
aria-label="GL entries"
|
||||
>
|
||||
<mat-icon>receipt_long</mat-icon>
|
||||
</button>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
} @else {
|
||||
<p class="slot-empty">
|
||||
No Share Collection receipt is waiting. Collect the cash at <em>Cashier › Transaction Entry</em> with
|
||||
entry type <strong>Share Collection</strong>, or find an earlier voucher by number.
|
||||
</p>
|
||||
}
|
||||
}
|
||||
</section>
|
||||
|
||||
<!-- ── Slot 2: the entry fee receipt ─────────────────────────────────────── -->
|
||||
@if (hasFee) {
|
||||
<section class="slot">
|
||||
<header class="slot-head">
|
||||
<span class="slot-step">2</span>
|
||||
<div class="slot-title">
|
||||
<h3>Entry Fee Receipt</h3>
|
||||
<p>Verification only — no GL entry is posted for it.</p>
|
||||
</div>
|
||||
<button type="button" class="slot-search" (click)="openSearch('fee')">
|
||||
<mat-icon>search</mat-icon>
|
||||
<span>Find by voucher</span>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
@if (chargeReceipt) {
|
||||
<div class="chosen" [class.chosen-short]="feeShortfall > 0">
|
||||
<div class="chosen-badge">
|
||||
<mat-icon>{{ feeShortfall > 0 ? 'warning' : 'check_circle' }}</mat-icon>
|
||||
</div>
|
||||
<div class="chosen-main">
|
||||
<span class="chosen-voucher mono">{{ chargeReceipt.voucherNo }}</span>
|
||||
<span class="chosen-gl">{{ glLabel(chargeReceipt) }}</span>
|
||||
</div>
|
||||
<span class="chosen-amount mono">{{ chargeReceipt.amount | number: '1.2-2' }}</span>
|
||||
<button type="button" class="chosen-clear" (click)="clear('fee')" aria-label="Change receipt">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
@if (feeShortfall > 0) {
|
||||
<p class="slot-note note-bad">
|
||||
Short by {{ feeShortfall | number: '1.2-2' }} — the fee is
|
||||
{{ requirement!.chargeAmount | number: '1.2-2' }}.
|
||||
</p>
|
||||
} @else {
|
||||
<p class="slot-note note-good">Entry fee verified.</p>
|
||||
}
|
||||
} @else {
|
||||
@if (optionsFor('fee').length) {
|
||||
<ul class="options">
|
||||
@for (option of optionsFor('fee'); track option.key) {
|
||||
<li class="option" [class.option-busy]="attaching === option.key" (click)="pick('fee', option)">
|
||||
<span class="option-radio"></span>
|
||||
<div class="option-main">
|
||||
<span class="option-voucher mono">{{ option.voucherNo }}</span>
|
||||
<span class="option-meta">{{ option.date }} · {{ glLabel(option) }}</span>
|
||||
@if (option.description) {
|
||||
<span class="option-desc">{{ option.description }}</span>
|
||||
}
|
||||
</div>
|
||||
<span class="option-amount mono">{{ option.amount | number: '1.2-2' }}</span>
|
||||
@if (attaching === option.key) {
|
||||
<mat-spinner diameter="16"></mat-spinner>
|
||||
} @else if (option.collectionId !== null) {
|
||||
<button
|
||||
type="button"
|
||||
class="option-info"
|
||||
(click)="openDetail(option, $event)"
|
||||
aria-label="GL entries"
|
||||
>
|
||||
<mat-icon>receipt_long</mat-icon>
|
||||
</button>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
} @else {
|
||||
<p class="slot-empty">No separate fee receipt is waiting — find one by voucher number.</p>
|
||||
}
|
||||
<p class="slot-note note-muted">
|
||||
Leave this empty if the fee was already collected inside the share receipt above.
|
||||
</p>
|
||||
}
|
||||
</section>
|
||||
}
|
||||
|
||||
@if (errorMsg) {
|
||||
<div class="alert">
|
||||
<mat-icon>error_outline</mat-icon>
|
||||
<span>{{ errorMsg }}</span>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions class="dialog-actions">
|
||||
<button type="button" mat-stroked-button [mat-dialog-close]="false" [disabled]="submitting">Cancel</button>
|
||||
<button type="button" mat-flat-button color="primary" [disabled]="!canApply" (click)="apply()">
|
||||
@if (submitting) {
|
||||
<mat-spinner diameter="16"></mat-spinner>
|
||||
<span>Applying…</span>
|
||||
} @else {
|
||||
<mat-icon>check</mat-icon>
|
||||
<span>Apply Collection</span>
|
||||
}
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
</div>
|
||||
|
||||
<!-- ── Find a voucher the list does not carry ───────────────────────────────── -->
|
||||
@if (searchFor) {
|
||||
<div class="overlay" (click)="closeSearch()">
|
||||
<div class="popup" (click)="$event.stopPropagation()">
|
||||
<header class="popup-head">
|
||||
<h3>{{ searchFor === 'share' ? 'Find a share receipt' : 'Find an entry fee receipt' }}</h3>
|
||||
<button type="button" mat-icon-button (click)="closeSearch()" aria-label="Close">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div class="popup-body">
|
||||
<label class="field">
|
||||
<span>GL Account</span>
|
||||
<select [(ngModel)]="searchGlAccountNo" [disabled]="loadingGlAccounts">
|
||||
<option value="">{{ loadingGlAccounts ? 'Loading…' : 'Select the account it was paid into' }}</option>
|
||||
@for (gl of glAccounts; track gl.id) {
|
||||
<option [value]="gl.gl_code">{{ gl.gl_code }} — {{ gl.name }}</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<div class="voucher-lookup">
|
||||
<label class="field">
|
||||
<span>Voucher No.</span>
|
||||
<input
|
||||
type="text"
|
||||
class="voucher-input"
|
||||
placeholder="Voucher no. (e.g. CR/001/2608/0002)"
|
||||
[(ngModel)]="chargeVoucherNo"
|
||||
[disabled]="lookingUpCharge || submitting"
|
||||
(keyup.enter)="lookupChargeVoucher()"
|
||||
[(ngModel)]="searchVoucherNo"
|
||||
placeholder="CR/18092026/0004"
|
||||
(keyup.enter)="runSearch()"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<button
|
||||
mat-stroked-button
|
||||
type="button"
|
||||
[disabled]="!chargeVoucherNo.trim() || !chargeGlAccountNo.trim() || lookingUpCharge || submitting"
|
||||
(click)="lookupChargeVoucher()"
|
||||
>
|
||||
{{ lookingUpCharge ? 'Finding…' : 'Find' }}
|
||||
</button>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<div class="charge-receipt-hint">
|
||||
Leave this blank if the fee was already paid as part of the share receipt above.
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (errorMsg) {
|
||||
<div class="alert-error">
|
||||
<mat-icon>error_outline</mat-icon>
|
||||
<span>{{ errorMsg }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (loading) {
|
||||
<div class="loading">
|
||||
<mat-spinner diameter="28"></mat-spinner>
|
||||
<span>Loading receipts…</span>
|
||||
</div>
|
||||
} @else if (receipts.length === 0) {
|
||||
<div class="empty">
|
||||
<mat-icon>info_outline</mat-icon>
|
||||
<div>
|
||||
<b>No unapplied receipt for this member today.</b>
|
||||
<div class="empty-hint">
|
||||
If the cash was taken on an earlier day, enter that voucher number above. Otherwise collect it first from
|
||||
Cashier › Transaction Entry as a deposit to the Share Sundry GL — once a manager approves it, the
|
||||
voucher appears here.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<table class="receipt-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-tick"></th>
|
||||
<th class="ta-left">Voucher / Receipt</th>
|
||||
<th class="ta-left">Date</th>
|
||||
<th class="ta-right">Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (r of receipts; track r.id) {
|
||||
<tr [class.selected]="isSelected(r.id)" (click)="toggle(r.id)">
|
||||
<td class="ta-center col-tick">
|
||||
<!-- Checkboxes, not radios: a member paying in instalments has several receipts
|
||||
against one application, and all of them settle it together. -->
|
||||
<input
|
||||
type="checkbox"
|
||||
[checked]="isSelected(r.id)"
|
||||
(change)="toggle(r.id)"
|
||||
(click)="$event.stopPropagation()"
|
||||
/>
|
||||
</td>
|
||||
<td class="ta-left mono">{{ r.voucher_no || r.receipt_no }}</td>
|
||||
<td class="ta-left">{{ r.collection_date }}</td>
|
||||
<td class="ta-right amount-cell">{{ r.amount | number: '1.2-2' }}</td>
|
||||
</tr>
|
||||
@if (errorMsg) {
|
||||
<div class="alert">
|
||||
<mat-icon>error_outline</mat-icon>
|
||||
<span>{{ errorMsg }}</span>
|
||||
</div>
|
||||
}
|
||||
</tbody>
|
||||
<!-- Shown from the first tick rather than the second: the total against what the account needs
|
||||
is what decides whether the settlement is accepted, so it should be on screen as soon as
|
||||
there is anything to total. -->
|
||||
@if (selectedIds.length > 0) {
|
||||
<tfoot>
|
||||
<tr class="total-row">
|
||||
<td class="col-tick"></td>
|
||||
<td class="ta-left" colspan="2">
|
||||
{{ selectedIds.length }} {{ selectedIds.length === 1 ? 'receipt' : 'receipts' }} selected
|
||||
</td>
|
||||
<td class="ta-right amount-cell">{{ selectedTotal | number: '1.2-2' }}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="note">
|
||||
Their total must cover the account's share value — plus any entry fee, unless it was verified as its own receipt
|
||||
above. Anything paid over that is credited to the member's savings account.
|
||||
<footer class="popup-foot">
|
||||
<button type="button" mat-stroked-button (click)="closeSearch()">Cancel</button>
|
||||
<button
|
||||
type="button"
|
||||
mat-flat-button
|
||||
color="primary"
|
||||
[disabled]="!searchVoucherNo.trim() || !searchGlAccountNo.trim() || searching"
|
||||
(click)="runSearch()"
|
||||
>
|
||||
@if (searching) {
|
||||
<mat-spinner diameter="16"></mat-spinner>
|
||||
} @else {
|
||||
<span>Find & Use</span>
|
||||
}
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
}
|
||||
</mat-dialog-content>
|
||||
</div>
|
||||
}
|
||||
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button [mat-dialog-close]="false" [disabled]="submitting">Cancel</button>
|
||||
<button
|
||||
mat-raised-button
|
||||
color="primary"
|
||||
type="button"
|
||||
[disabled]="selectedIds.length === 0 || chargeShortfall > 0 || submitting"
|
||||
(click)="apply()"
|
||||
>
|
||||
{{ submitting ? 'Applying…' : 'Apply Collection' }}
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
<!-- ── A receipt's own GL entries ───────────────────────────────────────────── -->
|
||||
@if (viewing) {
|
||||
<div class="overlay" (click)="closeDetail()">
|
||||
<div class="popup" (click)="$event.stopPropagation()">
|
||||
<header class="popup-head">
|
||||
<h3>
|
||||
Voucher <span class="mono">{{ viewing.voucherNo }}</span>
|
||||
</h3>
|
||||
<button type="button" mat-icon-button (click)="closeDetail()" aria-label="Close">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div class="popup-body">
|
||||
<dl class="detail-list">
|
||||
<div>
|
||||
<dt>Date</dt>
|
||||
<dd class="mono">{{ viewing.date }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Amount</dt>
|
||||
<dd class="mono">{{ viewing.amount | number: '1.2-2' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>GL Account</dt>
|
||||
<dd>{{ glLabel(viewing) }}</dd>
|
||||
</div>
|
||||
@if (viewing.cashierName) {
|
||||
<div>
|
||||
<dt>Collected By</dt>
|
||||
<dd>{{ viewing.cashierName }}</dd>
|
||||
</div>
|
||||
}
|
||||
@if (viewing.description) {
|
||||
<div>
|
||||
<dt>Description</dt>
|
||||
<dd>{{ viewing.description }}</dd>
|
||||
</div>
|
||||
}
|
||||
</dl>
|
||||
|
||||
<h4 class="detail-heading">Journal Entries</h4>
|
||||
@if (loadingGlEntries) {
|
||||
<div class="state-block"><mat-spinner diameter="22"></mat-spinner></div>
|
||||
} @else if (viewingGlEntries?.length) {
|
||||
<table class="gl-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Account</th>
|
||||
<th class="num">Debit</th>
|
||||
<th class="num">Credit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (entry of viewingGlEntries; track $index) {
|
||||
<tr>
|
||||
<td>
|
||||
<span class="mono">{{ entry.glCode }}</span> {{ entry.accountName }}
|
||||
</td>
|
||||
<td class="num mono">{{ entry.drCr === 'Dr' ? (entry.amount | number: '1.2-2') : '' }}</td>
|
||||
<td class="num mono">{{ entry.drCr === 'Cr' ? (entry.amount | number: '1.2-2') : '' }}</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
} @else {
|
||||
<p class="slot-empty">No journal entries are recorded against this receipt.</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
<footer class="popup-foot">
|
||||
<button type="button" mat-stroked-button (click)="closeDetail()">Close</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user