feat(fixed-deposits): show the posting period and GL-reconciled maturity interest

Each report line now shows the span its interest covers — "01 Oct to
31 Dec" rather than just the end date — and the report header shows
the product's posting cycle (Monthly, Quarterly, Half-yearly, Yearly).

Adds a "Posted outside approval" view: deposit interest posted without
this report's approval, chiefly a deposit that matured before its own
posting cycle completed. Product-wise totals plus a detail list with
when each posting actually reached the account.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 00:07:41 +05:30
co-authored by Claude Sonnet 5
parent cec5223378
commit 4acee3e63b
3 changed files with 232 additions and 2 deletions
@@ -25,6 +25,9 @@
<button class="fd-btn fd-btn--primary" (click)="openPreview()" [disabled]="previewLoading">
<fa-icon icon="eye"></fa-icon>Preview interest (today)
</button>
<button class="fd-btn" (click)="openOutsideApproval()" [disabled]="outsideApprovalLoading">
<fa-icon icon="bolt"></fa-icon>Posted outside approval
</button>
</div>
</div>
@@ -227,7 +230,7 @@
<td>
<span class="fd-chip" [ngClass]="fdTypeClass(r.fdType)">{{ fdTypeShort(r.fdType) }}</span>
</td>
<td class="fd-mono">{{ r.interestPostingDate }}</td>
<td class="fd-mono">{{ r.interestPeriodFrom || '—' }} &rarr; {{ r.interestPostingDate }}</td>
<td class="num">&#8377;{{ r.depositAmount | number: '1.2-2' }}</td>
<td class="num">{{ r.interestRate | number: '1.2-2' }}</td>
<td class="fd-mono">{{ r.maturityDate || '—' }}</td>
@@ -250,6 +253,129 @@
</div>
}
@if (showOutsideApproval) {
<div class="fd-card">
<div class="fd-detail-head">
<div>
<h3>Fixed deposit interest posted outside this report</h3>
<div class="muted">
A deposit that matured before its own posting cycle completed has its last period settled directly, the
moment it matures &mdash; before any report here could be generated or approved for it. Listed here so
nothing paid to a customer is invisible, approved or not.
</div>
</div>
<div class="fd-total">
<span>Total</span>
<strong>&#8377;{{ outsideApprovalTotal | number: '1.2-2' }}</strong>
<span class="muted">{{ outsideApprovalRows.length }} posting(s)</span>
</div>
</div>
<div
style="
display: flex;
flex-wrap: wrap;
align-items: flex-end;
gap: 10px;
padding: 14px 20px;
border-bottom: 1px solid var(--fd-border-soft);
"
>
<label class="fd-date">
From
<input type="date" [(ngModel)]="outsideApprovalFrom" (change)="loadOutsideApproval()" />
</label>
<label class="fd-date">
To
<input type="date" [(ngModel)]="outsideApprovalTo" (change)="loadOutsideApproval()" />
</label>
<button class="fd-btn fd-btn--slim" (click)="loadOutsideApproval()" [disabled]="outsideApprovalLoading">
<fa-icon icon="undo"></fa-icon>Refresh
</button>
<button class="fd-btn fd-btn--slim" (click)="closeOutsideApproval()">
<fa-icon icon="times"></fa-icon>Close
</button>
</div>
@if (outsideApprovalLoading) {
<div class="fd-empty">Looking for deposit interest posted outside this report&hellip;</div>
} @else {
<div class="fd-table-wrap">
<table class="fd-table">
<thead>
<tr>
<th>Product</th>
<th>Scheme</th>
<th class="num">Deposits</th>
<th class="num">Interest</th>
</tr>
</thead>
<tbody>
@for (g of outsideApprovalGroups; track g.productName) {
<tr>
<td>
<strong>{{ g.productName }}</strong>
</td>
<td>
<span class="fd-chip" [ngClass]="fdTypeClass(g.fdType)">{{ fdTypeShort(g.fdType) }}</span>
</td>
<td class="num">{{ g.accounts }}</td>
<td class="num">&#8377;{{ g.interest | number: '1.2-2' }}</td>
</tr>
} @empty {
<tr>
<td colspan="4" class="fd-empty">
No fixed deposit interest was posted outside this report in this period.
</td>
</tr>
}
</tbody>
</table>
</div>
@if (outsideApprovalRows.length > 0) {
<div class="fd-table-wrap">
<table class="fd-table">
<thead>
<tr>
<th>Account no</th>
<th>Customer</th>
<th>Product</th>
<th>Scheme</th>
<th>Interest for</th>
<th class="num">Interest</th>
<th>Posted at</th>
</tr>
</thead>
<tbody>
@for (r of outsideApprovalRows; track r.transactionId) {
<tr>
<td class="fd-mono">{{ r.accountNo }}</td>
<td>{{ r.clientName }}</td>
<td>{{ r.productName }}</td>
<td>
<span class="fd-chip" [ngClass]="fdTypeClass(r.fdType)">{{ fdTypeShort(r.fdType) }}</span>
</td>
<td class="fd-mono">{{ r.interestFor }}</td>
<td class="num">&#8377;{{ r.interestAmount | number: '1.2-2' }}</td>
<td class="fd-sub">{{ r.postedAt | date: 'dd MMM yyyy, HH:mm' }}</td>
</tr>
}
</tbody>
<tfoot>
<tr>
<td colspan="5"><strong>Total</strong></td>
<td class="num">
<strong>&#8377;{{ outsideApprovalTotal | number: '1.2-2' }}</strong>
</td>
<td></td>
</tr>
</tfoot>
</table>
</div>
}
}
</div>
}
<div class="fd-card">
<div class="fd-card-h">
<h2>Reports{{ postingDate ? ' for ' + postingDate : '' }}</h2>
@@ -347,6 +473,7 @@
<h3>
{{ selected.officeName }} &middot; {{ selected.productName }} &mdash; {{ selected.postingDate }}
<span class="fd-chip" [ngClass]="fdTypeClass(selected.fdType)">{{ fdTypeShort(selected.fdType) }}</span>
<span class="fd-badge fd-badge--muted">{{ selected.postingPeriodLabel }}</span>
<span class="fd-badge" [ngClass]="statusClass(selected.status)">{{ selected.status }}</span>
</h3>
<div class="muted">{{ fdTypeLabel(selected.fdType) }}</div>
@@ -384,7 +511,7 @@
<tr>
<td class="fd-mono">{{ l.accountNo }}</td>
<td>{{ l.clientName }}</td>
<td class="fd-mono">{{ l.interestPostingDate }}</td>
<td class="fd-mono">{{ l.interestPeriodFrom || '—' }} &rarr; {{ l.interestPostingDate }}</td>
<td class="num">&#8377;{{ l.depositAmount | number: '1.2-2' }}</td>
<td class="num">{{ l.interestRate | number: '1.2-2' }}</td>
<td class="fd-mono">{{ l.maturityDate || '—' }}</td>