Adds the FD Maturity Reports screen (due-without-report, generate/ approve, branch and head-office scope, detail with closure instruction and estimated interest per deposit), a nav entry, the route, and Branch Day Closing's maturing-today section and approval-pending warning banner. Adds "Matured automatically" to the maturity screen: deposits that settled via their own Transfer-to-Savings or Reinvest instruction, needing no approval, listed for visibility with product totals and per-deposit detail. Adds "Product summary" to the FD Interest Posting Reports screen (head office): interest by product and scheme — posted through the report, still pending, and posted directly at maturity instead — added up to what each product has paid out in total over a period. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
647 lines
25 KiB
HTML
647 lines
25 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="fd">
|
|
<div class="fd-head">
|
|
<div>
|
|
<h2 class="fd-head-t"><fa-icon icon="percentage"></fa-icon> FD Interest Posting Reports</h2>
|
|
<div class="fd-head-s">
|
|
Each branch gets its own report for each fixed deposit scheme, generated automatically at 12:00 noon on the
|
|
posting date. One user puts the figures up and a second approves them — whoever generated a report cannot
|
|
approve it. Approved interest posts when the branch closes its day, and it cannot close until then.
|
|
</div>
|
|
</div>
|
|
<div class="fd-head-a">
|
|
<label class="fd-date">
|
|
Posting date
|
|
<input type="date" [(ngModel)]="postingDate" (change)="load()" />
|
|
</label>
|
|
<button class="fd-btn" (click)="load()" [disabled]="loading"><fa-icon icon="undo"></fa-icon>Reload</button>
|
|
<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>
|
|
@if (isHeadOffice) {
|
|
<button class="fd-btn" (click)="openProductSummary()" [disabled]="productSummaryLoading">
|
|
<fa-icon icon="table"></fa-icon>Product summary
|
|
</button>
|
|
}
|
|
</div>
|
|
</div>
|
|
|
|
@if (!approvalEnabled) {
|
|
<div class="fd-alert fd-alert--info">
|
|
<fa-icon icon="info-circle"></fa-icon>
|
|
<div>
|
|
Fixed deposit interest is currently posted directly by the nightly job, so there is nothing to approve here.
|
|
Turn on <strong>fd-interest-posting-approval</strong> under System → Configurations to put deposits behind
|
|
this report.
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
@if (isHeadOffice) {
|
|
<div class="fd-tabs">
|
|
<button class="fd-tab" [class.is-active]="tab === 'own'" (click)="switchTab('own')">
|
|
<fa-icon icon="building"></fa-icon>My branch{{ myBranch ? ' — ' + myBranch : '' }}
|
|
</button>
|
|
<button class="fd-tab" [class.is-active]="tab === 'others'" (click)="switchTab('others')">
|
|
<fa-icon icon="sitemap"></fa-icon>Other branches
|
|
</button>
|
|
</div>
|
|
@if (tab === 'others') {
|
|
<div class="fd-alert fd-alert--info">
|
|
<fa-icon icon="info-circle"></fa-icon>
|
|
<div>
|
|
Each branch generates and approves its own reports. The head office can act here only for a branch that has
|
|
not opened the day, and only once every other open branch has closed. Anything it approves is marked (HO).
|
|
</div>
|
|
</div>
|
|
}
|
|
}
|
|
|
|
@if (errorMessage) {
|
|
<div class="fd-alert fd-alert--error">
|
|
<fa-icon icon="exclamation-triangle"></fa-icon>
|
|
<div>{{ errorMessage }}</div>
|
|
</div>
|
|
}
|
|
@if (notice) {
|
|
<div class="fd-alert fd-alert--ok">
|
|
<fa-icon icon="check"></fa-icon>
|
|
<div>{{ notice }}</div>
|
|
</div>
|
|
}
|
|
@if (pendingCount > 0) {
|
|
<div class="fd-alert fd-alert--warn">
|
|
<fa-icon icon="clock"></fa-icon>
|
|
<div>
|
|
<strong>{{ pendingCount }} report(s) waiting for approval.</strong> The day cannot be closed until they are
|
|
approved, and the user who generated a report cannot be the one to approve it.
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
@if (due.length > 0) {
|
|
<div class="fd-alert fd-alert--warn">
|
|
<fa-icon icon="exclamation-triangle"></fa-icon>
|
|
<div class="fd-due-body">
|
|
<div>Fixed deposit interest is due but no report has been generated yet for:</div>
|
|
@for (d of due; track d.productId + '-' + d.officeId) {
|
|
<div class="fd-due-row">
|
|
<strong>{{ d.productName }}</strong>
|
|
<span class="fd-chip" [ngClass]="fdTypeClass(d.fdType)">{{ fdTypeShort(d.fdType) }}</span>
|
|
<span>
|
|
· {{ d.officeName }} · {{ d.postingDate }} · {{ d.accountCount }} deposit(s) ·
|
|
₹{{ d.totalInterest | number: '1.2-2' }}
|
|
</span>
|
|
@if (d.canAct) {
|
|
<button class="fd-btn fd-btn--primary fd-btn--slim" (click)="generate(d)" [disabled]="busy">
|
|
<fa-icon icon="sync"></fa-icon>{{ tab === 'others' ? 'Generate on behalf' : 'Generate report' }}
|
|
</button>
|
|
} @else if (d.blockedReason) {
|
|
<span class="fd-blocked">{{ d.blockedReason }}</span>
|
|
}
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<!-- Summary strip -->
|
|
<div class="fd-summary">
|
|
<div class="fd-stat">
|
|
<div class="fd-stat__label">Interest due, no report</div>
|
|
<div class="fd-stat__value">₹{{ dueTotal | number: '1.2-2' }}</div>
|
|
<div class="fd-stat__sub">{{ dueAccountTotal }} deposit(s) · {{ due.length }} product(s)</div>
|
|
</div>
|
|
<div class="fd-stat fd-stat--warn">
|
|
<div class="fd-stat__label">Awaiting approval</div>
|
|
<div class="fd-stat__value">₹{{ awaitingTotal | number: '1.2-2' }}</div>
|
|
<div class="fd-stat__sub">{{ awaitingReports.length }} report(s) · blocking day close</div>
|
|
</div>
|
|
<div class="fd-stat fd-stat--ok">
|
|
<div class="fd-stat__label">Approved — posts at close</div>
|
|
<div class="fd-stat__value">₹{{ approvedTotal | number: '1.2-2' }}</div>
|
|
<div class="fd-stat__sub">{{ approvedReports.length }} report(s)</div>
|
|
</div>
|
|
<div class="fd-stat fd-stat--posted">
|
|
<div class="fd-stat__label">Posted today</div>
|
|
<div class="fd-stat__value">₹{{ postedTotal | number: '1.2-2' }}</div>
|
|
<div class="fd-stat__sub">{{ postedReports.length }} report(s)</div>
|
|
</div>
|
|
</div>
|
|
|
|
@if (showPreview) {
|
|
<div class="fd-card">
|
|
<div class="fd-detail-head">
|
|
<div>
|
|
<h3>FD interest preview — earned up to {{ previewAsOf }}</h3>
|
|
<div class="muted">
|
|
What each deposit would be paid if interest were posted today. View only — nothing is saved, approved
|
|
or posted.
|
|
</div>
|
|
</div>
|
|
<div class="fd-total">
|
|
<span>Total so far</span>
|
|
<strong>₹{{ previewTotal | number: '1.2-2' }}</strong>
|
|
<span class="muted">{{ previewRows.length }} deposit(s)</span>
|
|
</div>
|
|
</div>
|
|
<div
|
|
style="
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
padding: 14px 20px;
|
|
border-bottom: 1px solid var(--fd-border-soft);
|
|
"
|
|
>
|
|
<button class="fd-btn fd-btn--slim" (click)="openPreview()" [disabled]="previewLoading">
|
|
<fa-icon icon="undo"></fa-icon>Refresh
|
|
</button>
|
|
<button
|
|
class="fd-btn fd-btn--slim"
|
|
(click)="exportPreview()"
|
|
[disabled]="previewLoading || previewRows.length === 0"
|
|
>
|
|
<fa-icon icon="file-excel"></fa-icon>Export Excel
|
|
</button>
|
|
<button class="fd-btn fd-btn--slim" (click)="closePreview()"><fa-icon icon="times"></fa-icon>Close</button>
|
|
</div>
|
|
@if (previewLoading) {
|
|
<div class="fd-empty">Working out what each deposit has earned up to today…</div>
|
|
} @else {
|
|
<div class="fd-table-wrap">
|
|
<table class="fd-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Branch</th>
|
|
<th>Product</th>
|
|
<th>Scheme</th>
|
|
<th class="num">Deposits</th>
|
|
<th class="num">Interest so far</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@for (g of previewGroups; track g.officeName + g.productName) {
|
|
<tr>
|
|
<td>{{ g.officeName }}</td>
|
|
<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">₹{{ g.interest | number: '1.2-2' }}</td>
|
|
</tr>
|
|
} @empty {
|
|
<tr>
|
|
<td colspan="5" class="fd-empty">No fixed deposit has interest waiting to be posted.</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="fd-table-wrap">
|
|
<table class="fd-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Account no</th>
|
|
<th>Customer</th>
|
|
<th>Scheme</th>
|
|
<th>Interest for</th>
|
|
<th class="num">Deposit</th>
|
|
<th class="num">Rate %</th>
|
|
<th>Maturity</th>
|
|
<th>Pays into</th>
|
|
<th class="num">Interest so far</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@for (r of previewRows; track r.fdAccountId + '-' + r.interestPostingDate) {
|
|
<tr>
|
|
<td class="fd-mono">{{ r.accountNo }}</td>
|
|
<td>{{ r.clientName }}</td>
|
|
<td>
|
|
<span class="fd-chip" [ngClass]="fdTypeClass(r.fdType)">{{ fdTypeShort(r.fdType) }}</span>
|
|
</td>
|
|
<td class="fd-mono">{{ r.interestPeriodFrom || '—' }} → {{ r.interestPostingDate }}</td>
|
|
<td class="num">₹{{ r.depositAmount | number: '1.2-2' }}</td>
|
|
<td class="num">{{ r.interestRate | number: '1.2-2' }}</td>
|
|
<td class="fd-mono">{{ r.maturityDate || '—' }}</td>
|
|
<td class="fd-sub">{{ r.payoutAccountNo || 'Stays in the deposit' }}</td>
|
|
<td class="num">₹{{ r.interest | number: '1.2-2' }}</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
<tfoot>
|
|
<tr>
|
|
<td colspan="8"><strong>Total</strong></td>
|
|
<td class="num">
|
|
<strong>₹{{ previewTotal | number: '1.2-2' }}</strong>
|
|
</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
}
|
|
</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 — 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>₹{{ 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…</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">₹{{ 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">₹{{ 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>₹{{ outsideApprovalTotal | number: '1.2-2' }}</strong>
|
|
</td>
|
|
<td></td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
}
|
|
}
|
|
</div>
|
|
}
|
|
|
|
@if (showProductSummary) {
|
|
<div class="fd-card">
|
|
<div class="fd-detail-head">
|
|
<div>
|
|
<h3>Interest by product</h3>
|
|
<div class="muted">
|
|
Posted through this report, still awaiting approval or posting, and posted directly at maturity instead —
|
|
added up to what the product has paid out in total over the period.
|
|
</div>
|
|
</div>
|
|
<div class="fd-total">
|
|
<span>Total, all products</span>
|
|
<strong>₹{{ productSummaryGrandTotal.total | number: '1.2-2' }}</strong>
|
|
</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)]="productSummaryFrom" (change)="loadProductSummary()" />
|
|
</label>
|
|
<label class="fd-date">
|
|
To
|
|
<input type="date" [(ngModel)]="productSummaryTo" (change)="loadProductSummary()" />
|
|
</label>
|
|
<button class="fd-btn fd-btn--slim" (click)="loadProductSummary()" [disabled]="productSummaryLoading">
|
|
<fa-icon icon="undo"></fa-icon>Refresh
|
|
</button>
|
|
<button class="fd-btn fd-btn--slim" (click)="closeProductSummary()">
|
|
<fa-icon icon="times"></fa-icon>Close
|
|
</button>
|
|
</div>
|
|
@if (productSummaryLoading) {
|
|
<div class="fd-empty">Adding up interest by product…</div>
|
|
} @else {
|
|
<div class="fd-table-wrap">
|
|
<table class="fd-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Product</th>
|
|
<th>Scheme</th>
|
|
<th>Posting period</th>
|
|
<th class="num">Posted (report)</th>
|
|
<th class="num">Pending approval</th>
|
|
<th class="num">Posted at maturity</th>
|
|
<th class="num">Total</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@for (row of productSummaryRows; track row.productId) {
|
|
<tr>
|
|
<td>
|
|
<strong>{{ row.productName }}</strong>
|
|
</td>
|
|
<td>
|
|
<span class="fd-chip" [ngClass]="fdTypeClass(row.fdType)">{{ fdTypeShort(row.fdType) }}</span>
|
|
</td>
|
|
<td class="fd-sub">{{ row.postingPeriodLabel || '—' }}</td>
|
|
<td class="num">₹{{ row.posted | number: '1.2-2' }}</td>
|
|
<td class="num">₹{{ row.pending | number: '1.2-2' }}</td>
|
|
<td class="num">₹{{ row.maturityPosted | number: '1.2-2' }}</td>
|
|
<td class="num">
|
|
<strong>₹{{ row.total | number: '1.2-2' }}</strong>
|
|
</td>
|
|
</tr>
|
|
} @empty {
|
|
<tr>
|
|
<td colspan="7" class="fd-empty">No fixed deposit interest in this period.</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
<tfoot>
|
|
<tr>
|
|
<td colspan="3"><strong>Total</strong></td>
|
|
<td class="num">
|
|
<strong>₹{{ productSummaryGrandTotal.posted | number: '1.2-2' }}</strong>
|
|
</td>
|
|
<td class="num">
|
|
<strong>₹{{ productSummaryGrandTotal.pending | number: '1.2-2' }}</strong>
|
|
</td>
|
|
<td class="num">
|
|
<strong>₹{{ productSummaryGrandTotal.maturityPosted | number: '1.2-2' }}</strong>
|
|
</td>
|
|
<td class="num">
|
|
<strong>₹{{ productSummaryGrandTotal.total | number: '1.2-2' }}</strong>
|
|
</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
}
|
|
</div>
|
|
}
|
|
|
|
<div class="fd-card">
|
|
<div class="fd-card-h">
|
|
<h2>Reports{{ postingDate ? ' for ' + postingDate : '' }}</h2>
|
|
<span class="fd-card-sub">One report per scheme, per branch</span>
|
|
</div>
|
|
<div class="fd-table-wrap">
|
|
<table class="fd-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Branch</th>
|
|
<th>Scheme</th>
|
|
<th>Type</th>
|
|
<th>Posting date</th>
|
|
<th class="num">Deposits</th>
|
|
<th class="num">Total interest</th>
|
|
<th>Status</th>
|
|
<th>Maker</th>
|
|
<th>Checker</th>
|
|
<th></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@for (r of reports; track r.id) {
|
|
<tr class="is-clickable" [class.is-selected]="selected?.id === r.id" (click)="open(r)">
|
|
<td>{{ r.officeName }}</td>
|
|
<td>
|
|
<strong>{{ r.productName }}</strong>
|
|
</td>
|
|
<td [title]="fdTypeLabel(r.fdType)">
|
|
<span class="fd-chip" [ngClass]="fdTypeClass(r.fdType)">{{ fdTypeShort(r.fdType) }}</span>
|
|
</td>
|
|
<td class="fd-mono">{{ r.postingDate }}</td>
|
|
<td class="num">{{ r.accountCount }}</td>
|
|
<td class="num">₹{{ r.totalInterest | number: '1.2-2' }}</td>
|
|
<td>
|
|
<span class="fd-badge" [ngClass]="statusClass(r.status)">{{ r.status }}</span>
|
|
@if (r.failureReason) {
|
|
<div class="fd-fail" [title]="r.failureReason">Posting failed: {{ r.failureReason }}</div>
|
|
}
|
|
</td>
|
|
<td class="fd-sub">
|
|
{{ r.generatedBy || 'System' }} · {{ r.generationMode }}<br />{{
|
|
r.generatedAt | date: 'dd MMM, HH:mm'
|
|
}}
|
|
</td>
|
|
<td class="fd-sub">
|
|
@if (r.approvedAt) {
|
|
{{ r.approvedBy }}
|
|
@if (r.approvedOnBehalf) {
|
|
<span class="fd-behalf">(HO)</span>
|
|
}
|
|
<br />{{ r.approvedAt | date: 'dd MMM, HH:mm' }}
|
|
} @else {
|
|
—
|
|
}
|
|
</td>
|
|
<td (click)="$event.stopPropagation()">
|
|
<div style="display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end">
|
|
@if (canRegenerate(r)) {
|
|
<button class="fd-btn fd-btn--slim" (click)="regenerate(r)" [disabled]="busy">
|
|
<fa-icon icon="sync"></fa-icon>Regenerate
|
|
</button>
|
|
}
|
|
@if (canApprove(r)) {
|
|
<button class="fd-btn fd-btn--primary fd-btn--slim" (click)="approve(r)" [disabled]="busy">
|
|
<fa-icon icon="check"></fa-icon>{{ tab === 'others' ? 'Approve on behalf' : 'Approve' }}
|
|
</button>
|
|
} @else if (r.status === 'GENERATED' && r.approveBlockedReason) {
|
|
<span class="fd-blocked">{{ r.approveBlockedReason }}</span>
|
|
}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
} @empty {
|
|
<tr>
|
|
<td colspan="10" class="fd-empty">
|
|
@if (loading) {
|
|
Loading…
|
|
} @else {
|
|
No fixed deposit interest posting report for {{ postingDate || 'this date' }}. Reports are generated
|
|
at 12:00 noon on a product's posting date.
|
|
}
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
@if (selected) {
|
|
<div class="fd-card">
|
|
<div class="fd-detail-head">
|
|
<div>
|
|
<h3>
|
|
{{ selected.officeName }} · {{ selected.productName }} — {{ 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>
|
|
<div class="muted">
|
|
Dr Interest Expense → Cr {{ selected.payableGlCode }} {{ selected.payableGlName }}; Dr
|
|
{{ selected.payableGlCode }} → Cr {{ selected.paidGlCode }} {{ selected.paidGlName }}; then Dr
|
|
{{ selected.paidGlCode }} → Cr each deposit below
|
|
</div>
|
|
</div>
|
|
<div class="fd-total">
|
|
<span>Total interest</span>
|
|
<strong>₹{{ selected.totalInterest | number: '1.2-2' }}</strong>
|
|
<span class="muted">{{ selected.accountCount }} deposit(s)</span>
|
|
</div>
|
|
</div>
|
|
<div class="fd-table-wrap">
|
|
<table class="fd-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Account no</th>
|
|
<th>Account holder</th>
|
|
<th>Interest for</th>
|
|
<th class="num">Deposit</th>
|
|
<th class="num">Rate %</th>
|
|
<th>Maturity</th>
|
|
<th class="num">Interest</th>
|
|
<th class="num">Balance before</th>
|
|
<th class="num">Balance after</th>
|
|
<th>Pays into</th>
|
|
<th>Posted txn</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@for (l of selected.lines; track l.fdAccountId + '-' + l.interestPostingDate) {
|
|
<tr>
|
|
<td class="fd-mono">{{ l.accountNo }}</td>
|
|
<td>{{ l.clientName }}</td>
|
|
<td class="fd-mono">{{ l.interestPeriodFrom || '—' }} → {{ l.interestPostingDate }}</td>
|
|
<td class="num">₹{{ l.depositAmount | number: '1.2-2' }}</td>
|
|
<td class="num">{{ l.interestRate | number: '1.2-2' }}</td>
|
|
<td class="fd-mono">{{ l.maturityDate || '—' }}</td>
|
|
<td class="num">₹{{ l.interestAmount | number: '1.2-2' }}</td>
|
|
<td class="num">₹{{ l.balanceBefore | number: '1.2-2' }}</td>
|
|
<td class="num">₹{{ l.balanceAfter | number: '1.2-2' }}</td>
|
|
<td class="fd-sub">{{ l.payoutAccountNo || 'Stays in the deposit' }}</td>
|
|
<td class="fd-sub">{{ l.fdTransactionId ? '#' + l.fdTransactionId : '—' }}</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
<tfoot>
|
|
<tr>
|
|
<td colspan="6"><strong>Total</strong></td>
|
|
<td class="num">
|
|
<strong>₹{{ selected.totalInterest | number: '1.2-2' }}</strong>
|
|
</td>
|
|
<td colspan="4"></td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|