feat(shares, cashier): Entry Type tagging on Transaction Entry, real GL details on receipts
Adds an Entry Type field to Transaction Entry's GL-crediting rows (Receipt / Collection), fetched from the backend's GLEntryType code, so the counter can flag what a GL credit is actually for (General / Share Collection / DD Issue) instead of leaving every GL account picker throughout Share Advance Collection guessing from the whole chart of accounts. Apply Share / Additional Share screens: - GL Account picker narrowed to accounts that actually have an eligible receipt, and only auto-selected when exactly one candidate exists -- no more defaulting to whichever account sorts first (Cash at Vault). - Voucher tables show Description and Collected By, and a Detail view exposes the receipt's real Dr/Cr GL legs instead of the GL Account dropdown's current unrelated selection. - Additional Share funds from a single voucher (radio-style selection); ticking a receipt syncs the GL Account picker to its own actual account. - Checker's Additional Share Request review dialog gets the same Description + real GL entry detail, expandable per voucher, so approval is never a guess about what will post. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+53
-1
@@ -68,18 +68,70 @@
|
||||
<tr>
|
||||
<th class="ta-left">Voucher / Receipt</th>
|
||||
<th class="ta-left">Date</th>
|
||||
<th class="ta-left">Description</th>
|
||||
<th class="ta-left">Collected by</th>
|
||||
<th class="ta-right">Amount</th>
|
||||
<th class="ta-center">Detail</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (v of detail.vouchers; track v.id) {
|
||||
<tr>
|
||||
<tr class="sard-voucher-row" (click)="toggleVoucherGlEntries(v.id)">
|
||||
<td class="ta-left mono">{{ v.voucherNo || v.receiptNo }}</td>
|
||||
<td class="ta-left">{{ v.collectionDate }}</td>
|
||||
<td class="ta-left sard-desc-cell" [title]="v.note">{{ v.note || '—' }}</td>
|
||||
<td class="ta-left">{{ v.collectedByName || '—' }}</td>
|
||||
<td class="ta-right amount-cell">{{ v.amount | number: '1.2-2' }}</td>
|
||||
<td class="ta-center">
|
||||
<button
|
||||
type="button"
|
||||
mat-icon-button
|
||||
class="sard-detail-btn"
|
||||
(click)="$event.stopPropagation(); toggleVoucherGlEntries(v.id)"
|
||||
title="View this receipt's actual GL posting"
|
||||
>
|
||||
<mat-icon>visibility</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@if (expandedVoucherId === v.id) {
|
||||
<tr class="sard-gl-row">
|
||||
<td colspan="6">
|
||||
@if (loadingGlEntriesId === v.id) {
|
||||
<div class="sard-gl-loading">Loading GL entry…</div>
|
||||
} @else if (!glEntriesByVoucherId[v.id]?.length) {
|
||||
<div class="sard-gl-empty">No GL posting recorded for this receipt.</div>
|
||||
} @else {
|
||||
<table class="sard-gl-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="ta-left">GL Account</th>
|
||||
<th class="ta-center">Dr / Cr</th>
|
||||
<th class="ta-right">Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (leg of glEntriesByVoucherId[v.id]; track leg.glCode + leg.drCr) {
|
||||
<tr>
|
||||
<td class="ta-left mono">{{ leg.glCode }} — {{ leg.accountName }}</td>
|
||||
<td class="ta-center">
|
||||
<span
|
||||
class="sard-drcr-pill"
|
||||
[class.sard-drcr-pill--dr]="leg.drCr === 'Dr'"
|
||||
[class.sard-drcr-pill--cr]="leg.drCr === 'Cr'"
|
||||
>
|
||||
{{ leg.drCr }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="ta-right amount-cell">{{ leg.amount | number: '1.2-2' }}</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
+90
@@ -121,3 +121,93 @@ $danger: #b3261e;
|
||||
font-size: 13px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.ta-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sard-voucher-row {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #f4f7fb;
|
||||
}
|
||||
}
|
||||
|
||||
.sard-desc-cell {
|
||||
max-width: 160px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.sard-detail-btn {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
|
||||
mat-icon {
|
||||
font-size: 18px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: $primary;
|
||||
}
|
||||
}
|
||||
|
||||
.sard-gl-row td {
|
||||
background: #fbfcfe;
|
||||
border-top: none;
|
||||
padding: 4px 8px 10px;
|
||||
}
|
||||
|
||||
.sard-gl-loading,
|
||||
.sard-gl-empty {
|
||||
font-size: 12px;
|
||||
color: $muted;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.sard-gl-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 12px;
|
||||
background: #fff;
|
||||
border: 1px solid $border;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
|
||||
th {
|
||||
color: $muted;
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.4px;
|
||||
padding: 5px 8px;
|
||||
background: #f4f7fb;
|
||||
border-bottom: 1px solid $border;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 5px 8px;
|
||||
border-top: 1px solid #eceff4;
|
||||
}
|
||||
}
|
||||
|
||||
.sard-drcr-pill {
|
||||
display: inline-block;
|
||||
padding: 1px 8px;
|
||||
border-radius: 999px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
|
||||
&.sard-drcr-pill--dr {
|
||||
background: #fef2f2;
|
||||
color: #dc2626;
|
||||
border: 1px solid #fecaca;
|
||||
}
|
||||
|
||||
&.sard-drcr-pill--cr {
|
||||
background: #ecfdf5;
|
||||
color: #059669;
|
||||
border: 1px solid #a7f3d0;
|
||||
}
|
||||
}
|
||||
|
||||
+26
@@ -50,6 +50,12 @@ export class ShareAdditionRequestDetailDialogComponent implements OnInit {
|
||||
actioning = false;
|
||||
detail: any = null;
|
||||
|
||||
/** Which voucher's row is currently expanded to show its real Dr/Cr GL legs, or null if none. */
|
||||
expandedVoucherId: number | null = null;
|
||||
loadingGlEntriesId: number | null = null;
|
||||
/** Fetched once per voucher and kept — a checker toggling the same row twice shouldn't re-fetch. */
|
||||
glEntriesByVoucherId: { [id: number]: { glCode: string; accountName: string; drCr: string; amount: number }[] } = {};
|
||||
|
||||
public dialogRef = inject(MatDialogRef<ShareAdditionRequestDetailDialogComponent>);
|
||||
public data = inject<{ transactionId: number }>(MAT_DIALOG_DATA);
|
||||
|
||||
@@ -76,6 +82,26 @@ export class ShareAdditionRequestDetailDialogComponent implements OnInit {
|
||||
return this.detail?.vouchers?.length || 0;
|
||||
}
|
||||
|
||||
/** Shows or hides a voucher's real GL posting, fetching it the first time it is opened. */
|
||||
toggleVoucherGlEntries(voucherId: number): void {
|
||||
if (this.expandedVoucherId === voucherId) {
|
||||
this.expandedVoucherId = null;
|
||||
return;
|
||||
}
|
||||
this.expandedVoucherId = voucherId;
|
||||
if (this.glEntriesByVoucherId[voucherId]) {
|
||||
return;
|
||||
}
|
||||
this.loadingGlEntriesId = voucherId;
|
||||
this.cashierService
|
||||
.getShareAdvanceGlEntries(voucherId)
|
||||
.pipe(catchError(() => of(null)))
|
||||
.subscribe((res: any) => {
|
||||
this.glEntriesByVoucherId[voucherId] = res?.entries || [];
|
||||
this.loadingGlEntriesId = null;
|
||||
});
|
||||
}
|
||||
|
||||
approve(): void {
|
||||
this.act('approveadditionalshares');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user