feat(shares): one-voucher-per-slot receipt dialogs, Apply Additional Shares as a popup
Apply Collection and Apply Additional Shares settle the same Share Collection receipts for the same member, so they are now deliberately the same screen. Apply Collection takes exactly two receipts and no more: one for the share value, one for the entry fee, in two separate slots. They are different things -- the share receipt is money moving into share capital, while the fee receipt only proves the fee was already collected and is never posted a second time -- and picking both out of one ticked list made that distinction invisible. Apply Additional Shares moves from its own route to a dialog. It was a full page a clerk had to leave the account for and come back from, to fill in a short form with the member standing at the counter. Its old route, resolver case and action- host branch are removed rather than left behind as dead paths. Both screens now draw their receipts from a single pool, whether a receipt already names the member or is a voucher paid straight into a GL account; an unattributed one is attached at the moment it is picked, so what used to be an Attach in one table and a tick in another is one deliberate click. The by-voucher search and the GL-entry detail both sit behind buttons, as exceptions rather than permanent furniture. Dialog chrome and the receipt shape are shared (share-dialog.shared.scss, share-receipt-option.ts) so the two cannot drift apart again. Also fixes content being clipped at the dialog edges: the wrapper cancelled a 24px surface padding that the panel class had already zeroed, pushing every edge outside the surface where overflow:hidden cut it off, and the height cap sat on the scrolling body rather than the wrapper, letting the footer stack past the bottom of the viewport. Adds the product-wise share summary report (authorised, issued, available and unpaid shares, distinctive range and account count per product). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
/**
|
||||
* One Share Collection receipt the counter can choose, in a single shape whatever it came from.
|
||||
*
|
||||
* {@code GET /shareadvancecollection/unattributed} answers with two differently-shaped lists — the
|
||||
* member's own receipts under {@code own}, and every other receipt still going spare under
|
||||
* {@code rows} — and normalising both to this on arrival is what lets a screen show one plain list
|
||||
* of vouchers instead of two tables whose difference a counter has to understand before they can
|
||||
* pick anything.
|
||||
*
|
||||
* Shared by the two screens that settle a share purchase (the first application's Apply Collection
|
||||
* dialog and the Apply Additional Shares dialog) so a receipt reads identically on both.
|
||||
*/
|
||||
export interface ReceiptOption {
|
||||
/** Stable identity for tracking and comparison; unique across both source lists. */
|
||||
key: string;
|
||||
/**
|
||||
* The {@code m_share_advance_collection} row behind this receipt, or null while it is still only
|
||||
* a voucher number. A null one is attached to the member on first pick.
|
||||
*/
|
||||
collectionId: number | null;
|
||||
voucherNo: string;
|
||||
glAccountNo: string;
|
||||
glAccountName: string;
|
||||
date: string;
|
||||
amount: number;
|
||||
description: string;
|
||||
cashierName: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A receipt that already has an {@code m_share_advance_collection} row — ready to settle as-is.
|
||||
*
|
||||
* Also the shape {@code GET /shareadvancecollection/by-voucher} answers with, so a receipt found by
|
||||
* number arrives here too, already attached.
|
||||
*/
|
||||
export function receiptFromOwn(row: any): ReceiptOption {
|
||||
return {
|
||||
key: `c${row.id}`,
|
||||
collectionId: Number(row.id),
|
||||
voucherNo: row.voucher_no || row.receipt_no || '',
|
||||
glAccountNo: row.gl_code || '',
|
||||
glAccountName: '',
|
||||
date: String(row.collection_date || '').slice(0, 10),
|
||||
amount: Number(row.amount || 0),
|
||||
description: row.note || '',
|
||||
cashierName: row.collected_by_name || ''
|
||||
};
|
||||
}
|
||||
|
||||
/** A receipt that is still only a voucher, attached to the member when it is picked. */
|
||||
export function receiptFromUnattached(row: any): ReceiptOption {
|
||||
return {
|
||||
key: `v${row.id}`,
|
||||
collectionId: null,
|
||||
voucherNo: row.voucherNo || '',
|
||||
glAccountNo: row.glAccountNo || '',
|
||||
glAccountName: row.glAccountName || '',
|
||||
date: String(row.txnDate || '').slice(0, 10),
|
||||
amount: Number(row.amount || 0),
|
||||
description: row.description || '',
|
||||
cashierName: row.cashierName || ''
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The GL account a receipt sits in, named where the name is known and coded where it is not.
|
||||
*
|
||||
* An attached receipt carries only its {@code gl_code}, so the name is looked up in whatever GL
|
||||
* account list the screen already loaded for its voucher search rather than fetched again.
|
||||
*/
|
||||
export function receiptGlLabel(option: ReceiptOption, glAccounts: any[]): string {
|
||||
const name = option.glAccountName || glAccounts.find((gl) => gl.gl_code === option.glAccountNo)?.name || '';
|
||||
if (!option.glAccountNo) {
|
||||
return name || '—';
|
||||
}
|
||||
return name ? `${option.glAccountNo} — ${name}` : option.glAccountNo;
|
||||
}
|
||||
Reference in New Issue
Block a user