- Add the full Share Reports surface (final balance, account statement, issued/status/nominal/share-wise/guarantor/member-interest reports, enquiry, payment certificate, share detail, dividend statement, folio statement, distinctive numbers, area-wise, mailing labels/letters, share transfer) as standalone routed components, wired into the Share menu - Wire the unattributed Share Collection receipt list into Apply Collection and Apply Additional Shares, with an Attach action independent of account/client - Sync the GL Account and Voucher No fields to whichever receipt is ticked or found, and surface the receipt's real Dr/Cr GL entries in its detail popup - Block Apply Additional Shares until the account's original application is settled Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
153 lines
5.5 KiB
HTML
153 lines
5.5 KiB
HTML
<!--
|
|
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/.
|
|
-->
|
|
|
|
<div class="cert-root">
|
|
<div class="cert-head">
|
|
<div class="cert-head-l">
|
|
<fa-icon icon="tag" class="cert-head-ic"></fa-icon>
|
|
<div>
|
|
<h2 class="cert-head-t">Share Distinctive Number Detail</h2>
|
|
<div class="cert-head-s">
|
|
Which numbered shares each member holds — the range every allotment covers, per share class.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="cert-head-r">
|
|
<button class="cert-cta-btn" (click)="exportCsv()" [disabled]="loading || !filteredRows.length">
|
|
<fa-icon icon="file-alt"></fa-icon> Export CSV
|
|
</button>
|
|
<button class="cert-cta-btn" (click)="print()" [disabled]="loading || !filteredRows.length">
|
|
<fa-icon icon="print"></fa-icon> Print
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
@if (totals) {
|
|
<div class="cert-kpis">
|
|
<div class="cert-kpi cert-kpi--blue">
|
|
<fa-icon icon="file-alt" class="cert-kpi-ic"></fa-icon>
|
|
<div>
|
|
<div class="cert-kpi-n">{{ totals.allotmentCount | number }}</div>
|
|
<div class="cert-kpi-l">Numbered Allotments</div>
|
|
</div>
|
|
</div>
|
|
<div class="cert-kpi cert-kpi--green">
|
|
<fa-icon icon="coins" class="cert-kpi-ic"></fa-icon>
|
|
<div>
|
|
<div class="cert-kpi-n">{{ totals.sharesNumbered | number }}</div>
|
|
<div class="cert-kpi-l">Shares Numbered</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<!-- The numbers are derived, not recorded. Said plainly so nobody reads them as an independent
|
|
register the society has been maintaining. -->
|
|
<div class="srep-warn">
|
|
<fa-icon icon="info-circle"></fa-icon>
|
|
<span>
|
|
Distinctive numbers are <b>derived from issue order</b> within each share class — no distinctive-number column
|
|
exists in the system, so each paid-up allotment takes the next block in sequence. Redeemed shares leave a gap
|
|
rather than renumbering everybody else, exactly as a physical register would. Only paid-up allotments are
|
|
numbered.
|
|
</span>
|
|
</div>
|
|
|
|
<div class="cert-filters">
|
|
<div class="cert-filter">
|
|
<label>Find share number</label>
|
|
<div class="cert-input-ic">
|
|
<fa-icon icon="search"></fa-icon>
|
|
<input type="number" min="1" [(ngModel)]="shareNumberLookup" placeholder="e.g. 175 — who holds it?" />
|
|
</div>
|
|
</div>
|
|
<div class="cert-filter">
|
|
<label>Member / A/c No / Resolution</label>
|
|
<div class="cert-input-ic">
|
|
<fa-icon icon="search"></fa-icon>
|
|
<input type="text" [(ngModel)]="search" placeholder="e.g. member name" />
|
|
</div>
|
|
</div>
|
|
<div class="cert-filter">
|
|
<label>Share class</label>
|
|
<select [(ngModel)]="productFilter">
|
|
<option value="">All classes</option>
|
|
@for (p of productOptions; track p) {
|
|
<option [value]="p">{{ p }}</option>
|
|
}
|
|
</select>
|
|
</div>
|
|
<div class="cert-filter">
|
|
<label> </label>
|
|
<button class="cert-cta-btn" (click)="clearFilters()">Clear filters</button>
|
|
</div>
|
|
</div>
|
|
|
|
@if (loading) {
|
|
<div class="srep-state">Loading the distinctive number register…</div>
|
|
} @else if (loadError) {
|
|
<div class="srep-state srep-state--error"><fa-icon icon="exclamation-circle"></fa-icon> {{ loadError }}</div>
|
|
} @else if (!rows.length) {
|
|
<div class="srep-state">No paid-up allotment has been made yet, so no share carries a number.</div>
|
|
} @else if (!filteredRows.length) {
|
|
<div class="srep-state">
|
|
@if (shareNumberLookup) {
|
|
Share number {{ shareNumberLookup | number }} has not been allotted in this share class.
|
|
} @else {
|
|
No allotment matches the filters applied.
|
|
}
|
|
</div>
|
|
} @else {
|
|
<div class="srep-table-wrap">
|
|
<table class="srep-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Share Class</th>
|
|
<th>Distinctive Numbers</th>
|
|
<th class="srep-num">Shares</th>
|
|
<th>Member</th>
|
|
<th>Membership No</th>
|
|
<th>Share A/c No</th>
|
|
<th>Branch</th>
|
|
<th>Allotment Date</th>
|
|
<th>Funded By</th>
|
|
<th>Board Resolution</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@for (row of filteredRows; track row.transactionId) {
|
|
<tr class="srep-row--click" (click)="openAccount(row)">
|
|
<td>{{ row.productName }}</td>
|
|
<td class="srep-mono">
|
|
<b>{{ range(row) }}</b>
|
|
</td>
|
|
<td class="srep-num">{{ row.shares | number }}</td>
|
|
<td>{{ row.clientName }}</td>
|
|
<td class="srep-mono">{{ row.cifNumber || '—' }}</td>
|
|
<td class="srep-mono">{{ row.accountNo }}</td>
|
|
<td>{{ row.officeName || '—' }}</td>
|
|
<td>{{ row.allotmentDate }}</td>
|
|
<td>{{ funding(row.fundingSource) }}</td>
|
|
<td class="srep-mono">{{ row.resolutionNumber || '—' }}</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
<tfoot>
|
|
<tr>
|
|
<td colspan="2">
|
|
{{ isFiltered ? 'Total (filtered)' : 'Total' }} — {{ filteredRows.length | number }} allotments
|
|
</td>
|
|
<td class="srep-num">{{ shownShares | number }}</td>
|
|
<td colspan="7"></td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
}
|
|
</div>
|