feat(shares): move voucher search behind a popup, not an always-visible toolbar
The GL Account / Voucher No lookup is for the exception -- a receipt the unattributed list and the member's own list do not already carry -- so it now sits behind a "Search a Voucher Not Listed Below" button in both Apply Collection and Apply Additional Shares, opening the same lookup in a popup (matching the existing receipt-detail popup) instead of occupying screen space whether or not anyone needs it. A successful find closes the popup and the result appears ticked in the list behind it, same as before.
This commit is contained in:
@@ -178,61 +178,25 @@
|
||||
<div class="funding-intro-banner">
|
||||
<mat-icon class="banner-icon">info</mat-icon>
|
||||
<span>
|
||||
Select the one receipt that covers this payment, or enter a voucher number to search. Click
|
||||
Select the one receipt that covers this payment. Click
|
||||
<mat-icon class="inline-icon">visibility</mat-icon> on any row to view full details popup.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Toolbar: GL Account Select & Voucher Lookup -->
|
||||
<div class="lookup-toolbar">
|
||||
<div class="gl-select-box">
|
||||
<label class="toolbar-label"> <mat-icon class="mini-icon">account_tree</mat-icon> GL Account </label>
|
||||
<select
|
||||
class="custom-select"
|
||||
[(ngModel)]="glAccountNo"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
[disabled]="lookingUpVoucher || 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-box">
|
||||
<label class="toolbar-label">
|
||||
<mat-icon class="mini-icon">search</mat-icon> Find Specific Voucher
|
||||
</label>
|
||||
<div class="lookup-input-group">
|
||||
<input
|
||||
type="text"
|
||||
class="lookup-input font-mono"
|
||||
placeholder="Voucher no. (e.g. 000123)"
|
||||
[(ngModel)]="voucherNo"
|
||||
[disabled]="lookingUpVoucher || submitting"
|
||||
(keyup.enter)="lookupVoucher()"
|
||||
/>
|
||||
<button
|
||||
mat-flat-button
|
||||
color="primary"
|
||||
type="button"
|
||||
class="find-btn"
|
||||
[disabled]="!voucherNo.trim() || !glAccountNo.trim() || lookingUpVoucher || submitting"
|
||||
(click)="lookupVoucher()"
|
||||
>
|
||||
@if (lookingUpVoucher) {
|
||||
<mat-spinner diameter="14"></mat-spinner>
|
||||
} @else {
|
||||
<mat-icon>search</mat-icon>
|
||||
<span>Find</span>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- A voucher not already listed below (an older receipt, or one only the member's slip
|
||||
names) is reached through the popup, not a toolbar sitting here whether or not
|
||||
anyone needs it -- the unattributed list and this member's own receipts already
|
||||
cover the ordinary case. -->
|
||||
<button
|
||||
mat-stroked-button
|
||||
type="button"
|
||||
class="voucher-search-toggle-btn"
|
||||
[disabled]="submitting"
|
||||
(click)="openVoucherSearch()"
|
||||
>
|
||||
<mat-icon>search</mat-icon>
|
||||
<span>Search a Voucher Not Listed Below</span>
|
||||
</button>
|
||||
|
||||
<!-- Receipts tagged "Share Collection" that belong to nobody yet. A deposit straight into
|
||||
a GL account names no member, so it is never attributed on its own -- and never
|
||||
@@ -649,3 +613,85 @@
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- SEARCH VOUCHER POPUP -->
|
||||
@if (voucherSearchOpen) {
|
||||
<div class="modal-backdrop animate-fade-in" (click)="closeVoucherSearch()">
|
||||
<div class="voucher-modal-dialog voucher-search-dialog" (click)="$event.stopPropagation()">
|
||||
<div class="modal-header">
|
||||
<div class="header-title font-accent">
|
||||
<mat-icon class="m-icon">search</mat-icon>
|
||||
<span>Search a Voucher</span>
|
||||
</div>
|
||||
<button type="button" mat-icon-button class="close-btn" (click)="closeVoucherSearch()">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<p class="voucher-search-hint">
|
||||
For a receipt not already listed above — one taken on an earlier day, or a slip the member is holding.
|
||||
</p>
|
||||
|
||||
<div class="lookup-toolbar">
|
||||
<div class="gl-select-box">
|
||||
<label class="toolbar-label"><mat-icon class="mini-icon">account_tree</mat-icon> GL Account</label>
|
||||
<select
|
||||
class="custom-select"
|
||||
[(ngModel)]="glAccountNo"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
[disabled]="lookingUpVoucher || 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-box">
|
||||
<label class="toolbar-label"> <mat-icon class="mini-icon">receipt</mat-icon> Voucher / Receipt No </label>
|
||||
<div class="lookup-input-group">
|
||||
<input
|
||||
type="text"
|
||||
class="lookup-input font-mono"
|
||||
placeholder="Voucher no. (e.g. 000123)"
|
||||
[(ngModel)]="voucherNo"
|
||||
[disabled]="lookingUpVoucher || submitting"
|
||||
(keyup.enter)="lookupVoucher()"
|
||||
/>
|
||||
<button
|
||||
mat-flat-button
|
||||
color="primary"
|
||||
type="button"
|
||||
class="find-btn"
|
||||
[disabled]="!voucherNo.trim() || !glAccountNo.trim() || lookingUpVoucher || submitting"
|
||||
(click)="lookupVoucher()"
|
||||
>
|
||||
@if (lookingUpVoucher) {
|
||||
<mat-spinner diameter="14"></mat-spinner>
|
||||
} @else {
|
||||
<mat-icon>search</mat-icon>
|
||||
<span>Find</span>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (voucherError) {
|
||||
<div class="alert-box alert-error">
|
||||
<mat-icon>error_outline</mat-icon>
|
||||
<span>{{ voucherError }}</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" mat-stroked-button (click)="closeVoucherSearch()">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -335,6 +335,36 @@ mat-form-field {
|
||||
}
|
||||
}
|
||||
|
||||
.voucher-search-toggle-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 600;
|
||||
color: $accent-blue;
|
||||
border-color: $primary-border;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
mat-icon {
|
||||
font-size: 17px;
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
}
|
||||
}
|
||||
|
||||
.voucher-search-hint {
|
||||
font-size: 0.8rem;
|
||||
color: $text-muted;
|
||||
margin: 0 0 0.85rem;
|
||||
}
|
||||
|
||||
// The shared .lookup-toolbar normally goes two-column above its own breakpoint of BROWSER width,
|
||||
// which says nothing about how wide this popup itself is -- at the popup's width, two columns crowd
|
||||
// the GL Account select against the voucher input. Single column reads better at this popup's width.
|
||||
.voucher-search-dialog .lookup-toolbar {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
/* Lookup Toolbar */
|
||||
.lookup-toolbar {
|
||||
display: grid;
|
||||
|
||||
@@ -100,6 +100,25 @@ export class ApplySharesComponent implements OnInit {
|
||||
lookingUpVoucher = false;
|
||||
voucherError = '';
|
||||
|
||||
/**
|
||||
* Whether the voucher-search popup is open.
|
||||
*
|
||||
* The GL Account / Voucher No lookup is for the exception — a receipt the unattributed list and
|
||||
* this member's own list do not carry — so it sits behind a button rather than on screen whether
|
||||
* or not anyone needs it, exactly like the receipt-detail popup already does for the same reason.
|
||||
*/
|
||||
voucherSearchOpen = false;
|
||||
|
||||
openVoucherSearch(): void {
|
||||
this.voucherError = '';
|
||||
this.voucherSearchOpen = true;
|
||||
}
|
||||
|
||||
closeVoucherSearch(): void {
|
||||
this.voucherSearchOpen = false;
|
||||
this.voucherError = '';
|
||||
}
|
||||
|
||||
/** Voucher / Receipt No shown in the "Find Specific Voucher" box. Mirrors whichever receipt is
|
||||
* ticked below (see {@link toggleVoucher}) so a tick is visibly confirmed up there too, and
|
||||
* carries whatever the counter has typed in when they search by hand instead. */
|
||||
@@ -445,6 +464,9 @@ export class ApplySharesComponent implements OnInit {
|
||||
// Once attached it belongs to this member, so it leaves the unattributed list rather than
|
||||
// sitting there inviting a second, conflicting attachment.
|
||||
this.unattributed = this.unattributed.filter((u) => u.voucherNo !== voucherNo);
|
||||
// Found it -- the popup has done its job, so it closes rather than sitting open over a
|
||||
// receipt that now already shows, ticked, in the table behind it.
|
||||
this.voucherSearchOpen = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+93
-50
@@ -80,57 +80,21 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- SHARE RECEIPT LOOKUP SECTION -->
|
||||
<!-- SHARE RECEIPT LOOKUP — a voucher not already sitting in the lists below (an older receipt,
|
||||
or a member's own slip) is reached through the popup, not a toolbar that sat here whether or
|
||||
not anyone needed it. The unattributed list and the member's own receipts already cover the
|
||||
ordinary case; this is only for the exception. -->
|
||||
<div class="search-section">
|
||||
<div class="section-label">
|
||||
<mat-icon class="sec-icon">search</mat-icon>
|
||||
<span>Share Receipt Lookup</span>
|
||||
</div>
|
||||
|
||||
<div class="lookup-toolbar">
|
||||
<div class="gl-select-box">
|
||||
<label class="toolbar-label"><mat-icon class="mini-icon">account_tree</mat-icon> GL Account</label>
|
||||
<select
|
||||
class="custom-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-box">
|
||||
<label class="toolbar-label"><mat-icon class="mini-icon">receipt</mat-icon> Voucher / Receipt No</label>
|
||||
<div class="lookup-input-group">
|
||||
<input
|
||||
type="text"
|
||||
class="lookup-input font-mono"
|
||||
placeholder="Voucher no. (e.g. CR/001/2608/0001)"
|
||||
[(ngModel)]="voucherNo"
|
||||
[disabled]="lookingUp || submitting"
|
||||
(keyup.enter)="lookupVoucher()"
|
||||
/>
|
||||
<button
|
||||
mat-flat-button
|
||||
color="primary"
|
||||
type="button"
|
||||
class="find-btn"
|
||||
[disabled]="!voucherNo.trim() || !glAccountNo.trim() || lookingUp || submitting"
|
||||
(click)="lookupVoucher()"
|
||||
>
|
||||
@if (lookingUp) {
|
||||
<mat-spinner diameter="14"></mat-spinner>
|
||||
} @else {
|
||||
<mat-icon>search</mat-icon>
|
||||
<span>Find</span>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
mat-stroked-button
|
||||
type="button"
|
||||
class="voucher-search-toggle-btn"
|
||||
[disabled]="submitting"
|
||||
(click)="openVoucherSearch()"
|
||||
>
|
||||
<mat-icon>search</mat-icon>
|
||||
<span>Search a Voucher Not Listed Below</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- ENTRY FEE RECEIPT SECTION (If charge exists) -->
|
||||
@@ -575,3 +539,82 @@
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- SEARCH VOUCHER POPUP -->
|
||||
@if (voucherSearchOpen) {
|
||||
<div class="modal-backdrop animate-fade-in" (click)="closeVoucherSearch()">
|
||||
<div class="voucher-modal-dialog voucher-search-dialog" (click)="$event.stopPropagation()">
|
||||
<div class="modal-header">
|
||||
<div class="header-title font-accent">
|
||||
<mat-icon class="m-icon">search</mat-icon>
|
||||
<span>Search a Voucher</span>
|
||||
</div>
|
||||
<button type="button" mat-icon-button class="close-btn" (click)="closeVoucherSearch()">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<p class="voucher-search-hint">
|
||||
For a receipt not already listed above — one taken on an earlier day, or a slip the member is holding.
|
||||
</p>
|
||||
|
||||
<div class="lookup-toolbar">
|
||||
<div class="gl-select-box">
|
||||
<label class="toolbar-label"><mat-icon class="mini-icon">account_tree</mat-icon> GL Account</label>
|
||||
<select
|
||||
class="custom-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-box">
|
||||
<label class="toolbar-label"><mat-icon class="mini-icon">receipt</mat-icon> Voucher / Receipt No</label>
|
||||
<div class="lookup-input-group">
|
||||
<input
|
||||
type="text"
|
||||
class="lookup-input font-mono"
|
||||
placeholder="Voucher no. (e.g. CR/001/2608/0001)"
|
||||
[(ngModel)]="voucherNo"
|
||||
[disabled]="lookingUp || submitting"
|
||||
(keyup.enter)="lookupVoucher()"
|
||||
/>
|
||||
<button
|
||||
mat-flat-button
|
||||
color="primary"
|
||||
type="button"
|
||||
class="find-btn"
|
||||
[disabled]="!voucherNo.trim() || !glAccountNo.trim() || lookingUp || submitting"
|
||||
(click)="lookupVoucher()"
|
||||
>
|
||||
@if (lookingUp) {
|
||||
<mat-spinner diameter="14"></mat-spinner>
|
||||
} @else {
|
||||
<mat-icon>search</mat-icon>
|
||||
<span>Find</span>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (errorMsg) {
|
||||
<div class="alert-box alert-error">
|
||||
<mat-icon>error_outline</mat-icon>
|
||||
<span>{{ errorMsg }}</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" mat-stroked-button (click)="closeVoucherSearch()">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
+30
@@ -253,6 +253,36 @@ $danger-border: #fecaca;
|
||||
}
|
||||
}
|
||||
|
||||
.voucher-search-toggle-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 600;
|
||||
color: $accent-blue;
|
||||
border-color: $primary-border;
|
||||
|
||||
mat-icon {
|
||||
font-size: 17px;
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
}
|
||||
}
|
||||
|
||||
.voucher-search-hint {
|
||||
font-size: 0.8rem;
|
||||
color: $text-muted;
|
||||
margin: 0 0 0.85rem;
|
||||
}
|
||||
|
||||
// The shared .lookup-toolbar normally goes two-column above 680px of BROWSER width, which says
|
||||
// nothing about how wide this popup itself is -- at the popup's own ~36rem, two columns crowd the
|
||||
// GL Account select against the voucher input. Single column reads better at this popup's width,
|
||||
// full stop, rather than reintroducing a container query for one dialog.
|
||||
.voucher-search-dialog .lookup-toolbar {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
/* Lookup Toolbar - Wide Layout */
|
||||
.lookup-toolbar {
|
||||
display: grid;
|
||||
|
||||
+22
@@ -72,6 +72,25 @@ export class ApplyCollectionDialogComponent implements OnInit {
|
||||
voucherNo = '';
|
||||
lookingUp = false;
|
||||
|
||||
/**
|
||||
* Whether the voucher-search popup is open.
|
||||
*
|
||||
* The GL Account / Voucher No lookup is for the exception — a receipt the unattributed list and
|
||||
* the member's own list do not carry — so it sits behind a button rather than on screen whether or
|
||||
* not anyone needs it, exactly like the receipt-detail popup already does for the same reason.
|
||||
*/
|
||||
voucherSearchOpen = false;
|
||||
|
||||
openVoucherSearch(): void {
|
||||
this.errorMsg = '';
|
||||
this.voucherSearchOpen = true;
|
||||
}
|
||||
|
||||
closeVoucherSearch(): void {
|
||||
this.voucherSearchOpen = false;
|
||||
this.errorMsg = '';
|
||||
}
|
||||
|
||||
/** GL account the deposit was posted to — required, and always picked by hand. Never resolved
|
||||
* from a share product's accounting setup, so a broken or missing product mapping can never
|
||||
* make a genuine voucher unreachable. */
|
||||
@@ -427,6 +446,9 @@ export class ApplyCollectionDialogComponent implements OnInit {
|
||||
if (res.gl_code) {
|
||||
this.glAccountNo = res.gl_code;
|
||||
}
|
||||
// Found it -- the popup has done its job, so it closes rather than sitting open over a
|
||||
// receipt that now already shows, ticked, in the table behind it.
|
||||
this.voucherSearchOpen = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user