Files
cbsnexora/src/app/rbi-circulars/rbi-circulars.service.ts
T
keshavandClaude Opus 5.5 b6994c0b76 feat(rbi-reserve,savings): Form 1/Form 9 returns UI, branch-wise interest posting reports and summary
- Reports > Interest Posting Reports: per branch and product, generate/regenerate
  at any time, approve; branch column, HO-on-behalf marker, posted txn per account
- Reports > Interest Posting Summary (branch-wise) for the head office, with Excel export
- Day Close warns and disables Close Day while the branch's interest report is unapproved
- Savings product accounting: Interest On Savings (expense), Savings Interest Payable
  and Paid (liability rows, required)
- RBI Form 1 statutory and Form 9 return screens, reserve config updates
- Interest posting report GETs exempted from the response cache

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 13:20:01 +05:30

253 lines
9.5 KiB
TypeScript

/**
* 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/.
*/
import { Injectable, inject } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
export interface RbiFeedItem {
id: number;
title: string;
link: string;
description: string;
pubDate: any; // Fineract returns [year,month,day,hour,min,sec] array
category: string;
fetchedOn?: any;
openProjectWpId?: number; // OpenProject work package ID
openProjectWpStatus?: string;
openProjectWpProgress?: number;
}
@Injectable({
providedIn: 'root'
})
export class RbiCircularsService {
private http = inject(HttpClient);
getAll(category?: string): Observable<RbiFeedItem[]> {
let params = new HttpParams();
if (category) {
params = params.set('category', category);
}
return this.http.get<RbiFeedItem[]>('/rbi-feeds', { params });
}
getById(id: number): Observable<RbiFeedItem> {
return this.http.get<RbiFeedItem>(`/rbi-feeds/${id}`);
}
triggerFetch(): Observable<any> {
return this.http.post('/rbi-feeds/fetch', {});
}
/**
* Members and share capital per share type for one financial year, with the year before alongside.
*
* @param financialYear the year the FY opens in — 2025 means FY 2025-26.
*/
getShareCapitalReturn(financialYear: number): Observable<any> {
return this.http.get('/shareproducts/capital-return', {
params: new HttpParams().set('financialYear', String(financialYear))
});
}
/** Financial years that actually have share activity, newest first, for the year picker. */
getShareCapitalReturnYears(): Observable<any> {
return this.http.get('/shareproducts/capital-return/financial-years');
}
/**
* Form 1 — CRR, computed live from the ledger for the fortnight the reporting date falls in.
*
* The DTL base and the balances maintained are read as at different dates: the base lags by 45
* days (snapped back to a reporting Friday), the holdings are as at the reporting date itself.
* The response's `basis` block states both, so the lag is auditable rather than implied.
*
* @param reportingDate yyyy-MM-dd. Defaults to the current business date on the backend.
* @param officeId Optional — scopes every figure to one office.
*/
getForm1(reportingDate?: string, baseDate?: string, officeId?: number): Observable<any> {
let params = new HttpParams();
if (reportingDate) {
params = params.set('reportingDate', reportingDate);
}
// The base date can be given outright. The 45-day lag lands where it lands, and a bank reading
// an earlier fortnight — or correcting a filing — needs to name the date the base is struck at
// rather than work backwards from it. Left blank it is derived from the lag as before.
if (baseDate) {
params = params.set('baseDate', baseDate);
}
if (officeId != null) {
params = params.set('officeId', String(officeId));
}
return this.http.get('/rbireservereturn/form1', { params });
}
/**
* Form 9 — SLR, computed live on the month-end position. Any date within a month resolves to that
* month's end, since the return reports the position at close.
*/
getForm9(reportingDate?: string, officeId?: number): Observable<any> {
let params = new HttpParams();
if (reportingDate) {
params = params.set('reportingDate', reportingDate);
}
if (officeId != null) {
params = params.set('officeId', String(officeId));
}
return this.http.get('/rbireservereturn/form9', { params });
}
/**
* Form 9 in the shape it is filed — the statutory statement of compliance with section 24.
*
* A different statement from `getForm9`, not a reformatting: three dated columns rather than one,
* and the ratio charged on the last Friday of the second preceding fortnight rather than on the
* month end itself.
*/
getForm9Statutory(reportingDate?: string, officeId?: number): Observable<any> {
let params = new HttpParams();
if (reportingDate) {
params = params.set('reportingDate', reportingDate);
}
if (officeId != null) {
params = params.set('officeId', String(officeId));
}
return this.http.get('/rbireservereturn/form9-statutory', { params });
}
/**
* Form IX — the Return on Assets and Liabilities a co-operative bank files monthly, code R408.
*
* A different return from `getForm9` despite the name: that one reports the SLR ratio, this one
* reports the whole balance sheet. Every figure comes back in rupees thousands, with the rupee
* figure alongside it so a line can still be reconciled against the ledger.
*
* @param reportingDate yyyy-MM-dd — any date in the month; the return reports that month's end.
*/
getForm9Return(reportingDate?: string, officeId?: number): Observable<any> {
let params = new HttpParams();
if (reportingDate) {
params = params.set('reportingDate', reportingDate);
}
if (officeId != null) {
params = params.set('officeId', String(officeId));
}
return this.http.get('/rbireservereturn/form9-return', { params });
}
/**
* OSS 1 — Return on Assets and Liabilities, computed live from the ledger.
*
* Every figure is a closing balance as at the quarter end; the bank's own particulars come from
* the institution profile. Any date inside a quarter resolves to that quarter, so a caller never
* has to know which day a quarter ends on.
*
* @param quarterEndDate yyyy-MM-dd — any date in the quarter. Defaults to the business date.
* @param officeId Optional — scopes every figure to one office.
*/
getOss1(quarterEndDate?: string, officeId?: number): Observable<any> {
return this.http.get('/ossreturn/oss1', { params: this.ossParams(quarterEndDate, officeId) });
}
/**
* OSS 2 — Return on Earnings, computed live from the ledger.
*
* Earnings lines report the movement over the quarter rather than a closing balance, so the same
* accounts read as balances would carry forward every earlier quarter's income as well.
*/
getOss2(quarterEndDate?: string, officeId?: number): Observable<any> {
return this.http.get('/ossreturn/oss2', { params: this.ossParams(quarterEndDate, officeId) });
}
/**
* Form 1 as a co-operative bank files it — Banking Regulation (Co-operative Societies) Rules
* 1966, Rule 5, sections 18(1) and 24(3).
*
* A different statement from `getForm1`, not a reformatting: three columns rather than one, both
* ratios settled on the same liabilities, and those liabilities taken from the last Friday of the
* second preceding fortnight rather than a 45-day lag.
*/
getForm1Statutory(
reportingDate?: string,
firstColumnDate?: string,
secondColumnDate?: string,
officeId?: number
): Observable<any> {
let params = new HttpParams();
if (reportingDate) {
params = params.set('reportingDate', reportingDate);
}
// Both fortnight closes can be given outright. A bank filing August wants 15/08 and 31/08 on
// the face of the form whatever today is, and one date cannot produce a pair that spans a month
// boundary or skips a fortnight — which a late or corrected filing needs.
if (firstColumnDate) {
params = params.set('firstColumnDate', firstColumnDate);
}
if (secondColumnDate) {
params = params.set('secondColumnDate', secondColumnDate);
}
if (officeId != null) {
params = params.set('officeId', String(officeId));
}
return this.http.get('/rbireservereturn/form1-statutory', { params });
}
// ── Saved returns ──
/**
* Returns saved earlier, newest first. A saved return is served from the snapshot taken when it
* was saved, never recomputed — a backdated entry or a changed mapping would otherwise produce a
* different statement from the one that was reviewed.
*/
getSavedReturns(formCode?: string): Observable<any> {
let params = new HttpParams();
if (formCode) {
params = params.set('formCode', formCode);
}
return this.http.get('/rbiformafiling', { params });
}
/** One saved return, with the stored snapshot, so the screen renders what was saved. */
getSavedReturn(filingId: number): Observable<any> {
return this.http.get(`/rbiformafiling/${filingId}`);
}
/** Keeps the return currently on screen. Re-saving the same form and date replaces it. */
saveReturn(formCode: string, reportingDate: string, report: any, remarks?: string): Observable<any> {
return this.http.post('/rbiformafiling', {
formCode,
reportingDate,
report,
remarks,
locale: 'en',
dateFormat: 'yyyy-MM-dd'
});
}
/** Moves a saved return along its filing lifecycle; the snapshot itself is untouched. */
updateSavedReturnStatus(filingId: number, status: string, remarks?: string): Observable<any> {
return this.http.post(`/rbiformafiling/${filingId}/status`, { status, remarks });
}
deleteSavedReturn(filingId: number): Observable<any> {
return this.http.delete(`/rbiformafiling/${filingId}`);
}
private ossParams(quarterEndDate?: string, officeId?: number): HttpParams {
let params = new HttpParams();
if (quarterEndDate) {
params = params.set('quarterEndDate', quarterEndDate);
}
if (officeId != null) {
params = params.set('officeId', String(officeId));
}
return params;
}
}