diff --git a/src/app/fd-interest-posting-report/fd-interest-posting-report.component.html b/src/app/fd-interest-posting-report/fd-interest-posting-report.component.html
index 6bbb1aa36..16942b226 100644
--- a/src/app/fd-interest-posting-report/fd-interest-posting-report.component.html
+++ b/src/app/fd-interest-posting-report/fd-interest-posting-report.component.html
@@ -25,6 +25,9 @@
Preview interest (today)
+
+ Posted outside approval
+
@@ -227,7 +230,7 @@
{{ fdTypeShort(r.fdType) }}
- {{ r.interestPostingDate }}
+ {{ r.interestPeriodFrom || '—' }} → {{ r.interestPostingDate }}
₹{{ r.depositAmount | number: '1.2-2' }}
{{ r.interestRate | number: '1.2-2' }}
{{ r.maturityDate || '—' }}
@@ -250,6 +253,129 @@
}
+ @if (showOutsideApproval) {
+
+
+
+
Fixed deposit interest posted outside this report
+
+ 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.
+
+
+
+ Total
+ ₹{{ outsideApprovalTotal | number: '1.2-2' }}
+ {{ outsideApprovalRows.length }} posting(s)
+
+
+
+
+ From
+
+
+
+ To
+
+
+
+ Refresh
+
+
+ Close
+
+
+ @if (outsideApprovalLoading) {
+
Looking for deposit interest posted outside this report…
+ } @else {
+
+
+
+
+ Product
+ Scheme
+ Deposits
+ Interest
+
+
+
+ @for (g of outsideApprovalGroups; track g.productName) {
+
+
+ {{ g.productName }}
+
+
+ {{ fdTypeShort(g.fdType) }}
+
+ {{ g.accounts }}
+ ₹{{ g.interest | number: '1.2-2' }}
+
+ } @empty {
+
+
+ No fixed deposit interest was posted outside this report in this period.
+
+
+ }
+
+
+
+
+ @if (outsideApprovalRows.length > 0) {
+
+
+
+
+ Account no
+ Customer
+ Product
+ Scheme
+ Interest for
+ Interest
+ Posted at
+
+
+
+ @for (r of outsideApprovalRows; track r.transactionId) {
+
+ {{ r.accountNo }}
+ {{ r.clientName }}
+ {{ r.productName }}
+
+ {{ fdTypeShort(r.fdType) }}
+
+ {{ r.interestFor }}
+ ₹{{ r.interestAmount | number: '1.2-2' }}
+ {{ r.postedAt | date: 'dd MMM yyyy, HH:mm' }}
+
+ }
+
+
+
+ Total
+
+ ₹{{ outsideApprovalTotal | number: '1.2-2' }}
+
+
+
+
+
+
+ }
+ }
+
+ }
+
Reports{{ postingDate ? ' for ' + postingDate : '' }}
@@ -347,6 +473,7 @@
{{ selected.officeName }} · {{ selected.productName }} — {{ selected.postingDate }}
{{ fdTypeShort(selected.fdType) }}
+ {{ selected.postingPeriodLabel }}
{{ selected.status }}
{{ fdTypeLabel(selected.fdType) }}
@@ -384,7 +511,7 @@
{{ l.accountNo }}
{{ l.clientName }}
- {{ l.interestPostingDate }}
+ {{ l.interestPeriodFrom || '—' }} → {{ l.interestPostingDate }}
₹{{ l.depositAmount | number: '1.2-2' }}
{{ l.interestRate | number: '1.2-2' }}
{{ l.maturityDate || '—' }}
diff --git a/src/app/fd-interest-posting-report/fd-interest-posting-report.component.ts b/src/app/fd-interest-posting-report/fd-interest-posting-report.component.ts
index 28d12d189..d77e345fb 100644
--- a/src/app/fd-interest-posting-report/fd-interest-posting-report.component.ts
+++ b/src/app/fd-interest-posting-report/fd-interest-posting-report.component.ts
@@ -18,6 +18,7 @@ import {
FdInterestReport,
FdInterestReportDetail,
FdInterestPostingReportService,
+ FdOutsideApprovalRow,
FdReportScope,
FdType
} from './fd-interest-posting-report.service';
@@ -478,6 +479,61 @@ export class FdInterestPostingReportComponent implements OnInit {
);
}
+ // ── Outside approval: deposit interest posted without this report, chiefly at maturity ──
+
+ showOutsideApproval = false;
+ outsideApprovalLoading = false;
+ outsideApprovalRows: FdOutsideApprovalRow[] = [];
+ outsideApprovalFrom = '';
+ outsideApprovalTo = '';
+
+ openOutsideApproval(): void {
+ this.showOutsideApproval = true;
+ if (!this.outsideApprovalTo) {
+ this.outsideApprovalTo = this.businessDateIso();
+ this.outsideApprovalFrom = `${this.outsideApprovalTo.slice(0, 7)}-01`;
+ }
+ this.loadOutsideApproval();
+ }
+
+ loadOutsideApproval(): void {
+ this.outsideApprovalLoading = true;
+ this.errorMessage = '';
+ this.reportService
+ .outsideApproval(this.outsideApprovalFrom || undefined, this.outsideApprovalTo || undefined, undefined, this.tab)
+ .subscribe({
+ next: (rows) => {
+ this.outsideApprovalRows = rows ?? [];
+ this.outsideApprovalLoading = false;
+ },
+ error: (err) => {
+ this.outsideApprovalLoading = false;
+ this.outsideApprovalRows = [];
+ this.errorMessage = this.messageOf(err, 'Could not load deposit interest posted outside this report.');
+ }
+ });
+ }
+
+ closeOutsideApproval(): void {
+ this.showOutsideApproval = false;
+ }
+
+ get outsideApprovalTotal(): number {
+ return this.outsideApprovalRows.reduce((sum, r) => sum + Number(r.interestAmount || 0), 0);
+ }
+
+ /** Totals by product and scheme, so a branch can see at a glance where its unapproved interest sits. */
+ get outsideApprovalGroups(): { productName: string; fdType: string; accounts: number; interest: number }[] {
+ const map = new Map();
+ this.outsideApprovalRows.forEach((r) => {
+ const g = map.get(r.productName) ?? { productName: r.productName, fdType: r.fdType, accounts: 0, interest: 0 };
+ g.accounts += 1;
+ g.interest += Number(r.interestAmount || 0);
+ map.set(r.productName, g);
+ });
+ return [...map.values()];
+ }
+
private businessDateIso(): string {
const businessDate = this.settingsService.businessDate as any;
const parsed = businessDate ? new Date(businessDate) : new Date();
diff --git a/src/app/fd-interest-posting-report/fd-interest-posting-report.service.ts b/src/app/fd-interest-posting-report/fd-interest-posting-report.service.ts
index 85d7a4b91..72d12f397 100644
--- a/src/app/fd-interest-posting-report/fd-interest-posting-report.service.ts
+++ b/src/app/fd-interest-posting-report/fd-interest-posting-report.service.ts
@@ -37,6 +37,8 @@ export interface FdInterestReport {
approvedOnBehalf: boolean;
postedAt: string | null;
failureReason: string | null;
+ /** The product's posting cycle in words — Monthly, Quarterly, Half-yearly, Yearly. */
+ postingPeriodLabel: string;
/** Whether this user may regenerate it: their own branch, or the head office for a branch that did not open. */
canAct: boolean;
blockedReason: string | null;
@@ -49,6 +51,12 @@ export interface FdInterestReportLine {
fdAccountId: number;
accountNo: string;
clientName: string;
+ /**
+ * The day after the deposit's last paid period ended, or its activation date for a first-ever
+ * posting — null for a line posted before this was tracked. Paired with interestPostingDate, it is
+ * the span the interest was earned over.
+ */
+ interestPeriodFrom: string | null;
/**
* The period this interest is for, as the interest engine dated it — not the day the branch closes.
* A deposit owed two unpaid periods appears on two lines, one per date.
@@ -103,6 +111,28 @@ export interface FdInterestSummaryRow {
approvedOnBehalf: boolean;
approvedAt: string | null;
postedAt: string | null;
+ postingPeriodLabel: string;
+}
+
+/**
+ * One fixed deposit interest transaction that reached the customer's account without this report's
+ * approval — chiefly a deposit that matured before its own posting cycle completed, whose last period
+ * is settled directly by the maturity job. View only, for visibility.
+ */
+export interface FdOutsideApprovalRow {
+ transactionId: number;
+ fdAccountId: number;
+ accountNo: string;
+ clientName: string;
+ officeId: number;
+ officeName: string;
+ productId: number;
+ productName: string;
+ fdType: FdType;
+ interestFor: string;
+ interestAmount: number;
+ /** When the posting actually reached the account — not the period it is for. */
+ postedAt: string;
}
export interface FdInterestPreviewRow {
@@ -114,6 +144,8 @@ export interface FdInterestPreviewRow {
productId: number;
productName: string;
fdType: FdType;
+ /** The day after the deposit's last paid period ended, or its activation date for the first one. */
+ interestPeriodFrom: string | null;
/** The period this interest belongs to, as the engine dated it. */
interestPostingDate: string;
depositAmount: number | null;
@@ -230,4 +262,19 @@ export class FdInterestPostingReportService {
if (productId) params = params.set('productId', String(productId));
return this.http.get(`${this.base}/summary`, { params });
}
+
+ /** Deposit interest posted without this report's approval — a deposit maturing mid-cycle, chiefly. */
+ outsideApproval(
+ fromDate?: string,
+ toDate?: string,
+ officeId?: number,
+ scope?: FdReportScope
+ ): Observable {
+ let params = new HttpParams();
+ if (scope) params = params.set('scope', scope);
+ if (fromDate) params = params.set('fromDate', fromDate);
+ if (toDate) params = params.set('toDate', toDate);
+ if (officeId) params = params.set('officeId', String(officeId));
+ return this.http.get(`${this.base}/outside-approval`, { params });
+ }
}