fix(shares): mandatory GL+voucher entry for advance collection, split entry-fee verification from settlement
- Apply Collection dialog: replace product-config auto-resolved GL account with a required GL + voucher search (mirrored for Share and Entry Fee receipts). The entry-fee receipt is tracked separately from the share receipts and sent as its own field so its amount is never swept into the Sundry-side total or double-posted — it only verifies the fee was paid, since that money already sits wherever the cashier deposited it. - Shows the account's required total (share value + entry fee) and live coverage up front, instead of only after a failed apply. - Apply Additional Shares: same mandatory GL + voucher pattern for its by-voucher lookup, plus a GL account picker fed by the new /shareadvancecollection/glaccounts endpoint. - shares-account-charges-step: fixed a race where the currency-control subscription (meant only for the edit-an-existing-application flow) wiped out product charges that ngOnChanges had just seeded from the same product-selection event, leaving new accounts with no charges. - shares-account-view: hide "Share Certificate" until collection_applied is true, so a certificate can't be printed for shares nobody has actually paid for yet. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -388,19 +388,31 @@ export class CashierService {
|
||||
}
|
||||
|
||||
/** One unapplied Share Sundry receipt, found by the voucher number the member quotes. */
|
||||
getShareAdvanceByVoucher(voucherNo: string, clientId?: number): Observable<any> {
|
||||
getShareAdvanceByVoucher(voucherNo: string, glAccountNo: string, clientId?: number): Observable<any> {
|
||||
// The voucher travels as a query parameter, not a path segment: numbers are formatted
|
||||
// CR/001/2108/0001, and Spring Security's strict firewall rejects the encoded slashes
|
||||
// (%2F) outright with a 400 before the request ever reaches the resource.
|
||||
// clientId lets the backend adopt a cashier deposit made straight to the Share Sundry GL,
|
||||
// which carries no member of its own.
|
||||
let params = new HttpParams().set('voucherNo', voucherNo);
|
||||
// clientId lets the backend adopt a cashier deposit made straight to a GL account, which
|
||||
// carries no member of its own. glAccountNo is required — which GL account to search in is
|
||||
// always chosen 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.
|
||||
let params = new HttpParams().set('voucherNo', voucherNo).set('glAccountNo', glAccountNo);
|
||||
if (clientId) {
|
||||
params = params.set('clientId', String(clientId));
|
||||
}
|
||||
return this.http.get('/shareadvancecollection/by-voucher', { params });
|
||||
}
|
||||
|
||||
/** GL accounts selectable when searching for a voucher, for the picker on the Apply Collection screen. */
|
||||
getShareAdvanceGlAccounts(): Observable<any> {
|
||||
return this.http.get('/shareadvancecollection/glaccounts');
|
||||
}
|
||||
|
||||
/** Share value + any outstanding entry fee for one share account, shown before a voucher search. */
|
||||
getShareAdvanceRequirement(shareAccountId: number): Observable<any> {
|
||||
return this.http.get('/shareadvancecollection/requirement', { params: { shareAccountId: String(shareAccountId) } });
|
||||
}
|
||||
|
||||
/** Receipts still sitting in Share Sundry, optionally for a single member. */
|
||||
getUnappliedShareAdvances(clientId?: number): Observable<any> {
|
||||
const suffix = clientId ? `?clientId=${clientId}` : '';
|
||||
|
||||
@@ -227,9 +227,8 @@
|
||||
<td class="gl-voucher">
|
||||
@if (r.txnSource === 'GL') {
|
||||
<span class="gl-badge">GL</span>
|
||||
} @else {
|
||||
{{ r.voucherNo }}
|
||||
}
|
||||
{{ r.voucherNo }}
|
||||
</td>
|
||||
<td class="gl-date">
|
||||
<div>{{ r.txnDate }}</div>
|
||||
|
||||
Reference in New Issue
Block a user