oss file updated, profitl loss as per the day month and also with date select
This commit is contained in:
@@ -0,0 +1,283 @@
|
||||
/**
|
||||
* 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 { Component } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import * as ExcelJS from 'exceljs';
|
||||
import {
|
||||
addBanner,
|
||||
addLineRows,
|
||||
addSectionHeader,
|
||||
boxAllCells,
|
||||
buildGeneralInfoSheet,
|
||||
buildSignatorySheet,
|
||||
downloadWorkbook,
|
||||
newWorkbook,
|
||||
solidFill,
|
||||
XL
|
||||
} from '../oss-workbook';
|
||||
import { RAL_LIABILITIES } from '../oss1-ral/oss1-ral-data';
|
||||
import {
|
||||
ROE_EARNINGS,
|
||||
ROE_EARNINGS_NOTES,
|
||||
ROE_GENERAL_INFO,
|
||||
ROE_INTEREST_NOT_RECOGNIZED,
|
||||
ROE_OTHER_OPERATING_EXPENSES,
|
||||
ROE_SIGNATORIES
|
||||
} from './oss2-roe-data';
|
||||
|
||||
type Oss2Tab = 'general' | 'earnings' | 'interest' | 'other-expenses' | 'signatory';
|
||||
|
||||
/** One of the two OSS-1 tie-ups printed at the foot of the Earnings sheet. */
|
||||
interface TieUp {
|
||||
label: string;
|
||||
oss2Value: number | null;
|
||||
oss1Label: string;
|
||||
oss1Value: number | null;
|
||||
tallies: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'mifosx-oss2-roe',
|
||||
templateUrl: './oss2-roe.component.html',
|
||||
styleUrls: ['../oss-return.scss'],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
FaIconComponent,
|
||||
FormsModule
|
||||
]
|
||||
})
|
||||
export class Oss2RoeComponent {
|
||||
activeTab: Oss2Tab = 'general';
|
||||
|
||||
generalInfo = ROE_GENERAL_INFO;
|
||||
earnings = ROE_EARNINGS;
|
||||
earningsNotes = ROE_EARNINGS_NOTES;
|
||||
interestNotRecognized = ROE_INTEREST_NOT_RECOGNIZED;
|
||||
otherExpenses = ROE_OTHER_OPERATING_EXPENSES;
|
||||
signatories = ROE_SIGNATORIES;
|
||||
|
||||
generating = false;
|
||||
toastMsg = '';
|
||||
toastVisible = false;
|
||||
private toastTimer: any;
|
||||
|
||||
showTab(tab: Oss2Tab): void {
|
||||
this.activeTab = tab;
|
||||
}
|
||||
|
||||
private info(label: string): string {
|
||||
return this.generalInfo.find((g) => g.label === label)?.value ?? '';
|
||||
}
|
||||
|
||||
get returnName(): string {
|
||||
return this.info('Return Name');
|
||||
}
|
||||
get returnCode(): string {
|
||||
return this.info('Return Code');
|
||||
}
|
||||
get bankName(): string {
|
||||
return this.info('Name Of Bank');
|
||||
}
|
||||
get bankCode(): string {
|
||||
return this.info('Bank Code');
|
||||
}
|
||||
get quarterEndDate(): string {
|
||||
return this.info('Quarter End Date');
|
||||
}
|
||||
get auditFlag(): string {
|
||||
return this.info('Audit Flag');
|
||||
}
|
||||
|
||||
private earningsValue(prefix: string): number | null {
|
||||
return this.earnings.find((l) => l.label.startsWith(prefix))?.value ?? null;
|
||||
}
|
||||
|
||||
private liabilityValue(prefix: string): number | null {
|
||||
return RAL_LIABILITIES.find((l) => l.label.startsWith(prefix))?.value ?? null;
|
||||
}
|
||||
|
||||
get netProfitAfterTax(): number {
|
||||
return this.earningsValue('23.') ?? 0;
|
||||
}
|
||||
get operatingProfit(): number {
|
||||
return this.earningsValue('14.') ?? 0;
|
||||
}
|
||||
|
||||
/** Sum of the detail sheet — must equal Earnings line 12 ix) Others. */
|
||||
get otherExpensesTotal(): number {
|
||||
return this.otherExpenses.reduce((sum, e) => sum + (e.amount ?? 0), 0);
|
||||
}
|
||||
get otherExpensesLineValue(): number | null {
|
||||
return this.earningsValue('ix) Others');
|
||||
}
|
||||
get otherExpensesTally(): boolean {
|
||||
return this.otherExpensesTotal === (this.otherExpensesLineValue ?? 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Earnings sheet carries two footnotes requiring 17(i) and 17(ii) to tally
|
||||
* with OSS-1 liabilities 12(ii) and 12(i). Checked live rather than left to a
|
||||
* reader spotting it in the fine print.
|
||||
*/
|
||||
get oss1TieUps(): TieUp[] {
|
||||
const rows: TieUp[] = [
|
||||
{
|
||||
label: '17 (i) Provision for loan losses (BDDR, Spl. BDDR, etc.)',
|
||||
oss2Value: this.earningsValue('i) Provision for loan losses'),
|
||||
oss1Label: 'OSS-1 liabilities 12 (ii) BDDR, Spl BDDR',
|
||||
oss1Value: this.liabilityValue('ii. BDDR, Spl BDDR'),
|
||||
tallies: false
|
||||
},
|
||||
{
|
||||
label: '17 (ii) Provision for depreciation in investments including AFS',
|
||||
oss2Value: this.earningsValue('ii) Provision for depreciation in investments'),
|
||||
oss1Label: 'OSS-1 liabilities 12 (i) Investment Depreciation Reserve',
|
||||
oss1Value: this.liabilityValue('i. For investments (Investment Depreciation Reserve)'),
|
||||
tallies: false
|
||||
}
|
||||
];
|
||||
return rows.map((r) => ({ ...r, tallies: (r.oss2Value ?? 0) === (r.oss1Value ?? 0) }));
|
||||
}
|
||||
|
||||
get allTieUpsMatch(): boolean {
|
||||
return this.oss1TieUps.every((t) => t.tallies);
|
||||
}
|
||||
|
||||
// ══════════════ Excel generation ══════════════
|
||||
|
||||
/** Rebuilds the RBI workbook: one sheet per section, original layout and styling. */
|
||||
async generateExcel(): Promise<void> {
|
||||
this.generating = true;
|
||||
try {
|
||||
const wb = newWorkbook();
|
||||
|
||||
buildGeneralInfoSheet(wb, this.generalInfo);
|
||||
this.buildEarningsSheet(wb);
|
||||
this.buildInterestSheet(wb);
|
||||
this.buildOtherExpensesSheet(wb);
|
||||
buildSignatorySheet(wb, this.signatories, 'Signatory Information');
|
||||
|
||||
await downloadWorkbook(wb, `Form OSS-2_CIMS ${this.returnCode} - ${this.quarterEndDate}.xlsx`);
|
||||
this.toast(`${this.returnCode} workbook generated for quarter ending ${this.quarterEndDate}`);
|
||||
} catch {
|
||||
this.toast('Could not generate the workbook — please retry');
|
||||
} finally {
|
||||
this.generating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private buildEarningsSheet(wb: ExcelJS.Workbook): void {
|
||||
const sheet = wb.addWorksheet('Earnings');
|
||||
sheet.columns = [
|
||||
{ width: 90 },
|
||||
{ width: 20 }
|
||||
];
|
||||
addBanner(sheet, 'Earnings', 'B');
|
||||
|
||||
const first = sheet.rowCount + 1;
|
||||
addSectionHeader(sheet, '', 'Rs in Thousand');
|
||||
addLineRows(sheet, this.earnings);
|
||||
boxAllCells(sheet, first, sheet.rowCount, 2);
|
||||
|
||||
sheet.addRow([]);
|
||||
const notesStart = sheet.rowCount + 1;
|
||||
for (const note of this.earningsNotes) {
|
||||
const row = sheet.addRow([note]);
|
||||
sheet.mergeCells(`A${row.number}:B${row.number}`);
|
||||
row.font = { bold: true, italic: true, size: 10 };
|
||||
row.alignment = { wrapText: true };
|
||||
row.getCell(1).fill = solidFill(XL.headerGrey);
|
||||
}
|
||||
boxAllCells(sheet, notesStart, sheet.rowCount, 2);
|
||||
|
||||
sheet.views = [{ state: 'frozen', ySplit: 3 }];
|
||||
}
|
||||
|
||||
private buildInterestSheet(wb: ExcelJS.Workbook): void {
|
||||
const sheet = wb.addWorksheet('Interest Receivable Not Recog');
|
||||
sheet.columns = [
|
||||
{ width: 72 },
|
||||
{ width: 22 }
|
||||
];
|
||||
addBanner(sheet, 'Interest Receivable Not Recognized', 'B');
|
||||
|
||||
const start = sheet.rowCount + 1;
|
||||
const unit = sheet.addRow(['', 'Rs in Thousand']);
|
||||
unit.font = { bold: true };
|
||||
unit.alignment = { horizontal: 'center' };
|
||||
unit.eachCell((c) => (c.fill = solidFill(XL.headerGrey)));
|
||||
|
||||
const head = sheet.addRow(['Interest receivable on NPAs not recognized as income', this.quarterEndDate]);
|
||||
head.font = { bold: true };
|
||||
head.getCell(2).alignment = { horizontal: 'center' };
|
||||
head.eachCell((c) => (c.fill = solidFill(XL.headerGrey)));
|
||||
|
||||
for (const r of this.interestNotRecognized) {
|
||||
const row = sheet.addRow([r.label, r.value]);
|
||||
row.getCell(1).alignment = { wrapText: true, vertical: 'middle' };
|
||||
row.getCell(1).fill = solidFill(XL.headerGrey);
|
||||
row.getCell(2).numFmt = '#,##0.00';
|
||||
if (r.total) {
|
||||
row.getCell(2).fill = solidFill(XL.derivedBlue);
|
||||
row.getCell(2).font = { bold: true };
|
||||
}
|
||||
}
|
||||
boxAllCells(sheet, start, sheet.rowCount, 2);
|
||||
}
|
||||
|
||||
private buildOtherExpensesSheet(wb: ExcelJS.Workbook): void {
|
||||
const sheet = wb.addWorksheet('Other Operating Expenses');
|
||||
sheet.columns = [
|
||||
{ width: 10 },
|
||||
{ width: 56 },
|
||||
{ width: 20 }
|
||||
];
|
||||
addBanner(sheet, 'Other Operating Expenses', 'C');
|
||||
|
||||
const start = sheet.rowCount + 1;
|
||||
const title = sheet.addRow(['Details of Other Operating Expenses', '', 'Rs in Thousand']);
|
||||
sheet.mergeCells(`A${title.number}:B${title.number}`);
|
||||
title.font = { bold: true };
|
||||
title.eachCell((c) => (c.fill = solidFill(XL.headerGrey)));
|
||||
title.getCell(3).alignment = { horizontal: 'center' };
|
||||
|
||||
const head = sheet.addRow(['Sr No', 'Particulars', 'Amount']);
|
||||
head.font = { bold: true };
|
||||
head.alignment = { horizontal: 'center' };
|
||||
head.eachCell((c) => (c.fill = solidFill(XL.headerGrey)));
|
||||
|
||||
for (const e of this.otherExpenses) {
|
||||
const row = sheet.addRow([e.srNo, e.particulars, e.amount]);
|
||||
row.getCell(3).numFmt = '#,##0.00';
|
||||
}
|
||||
|
||||
const total = sheet.addRow(['Total', '', this.otherExpensesTotal]);
|
||||
sheet.mergeCells(`A${total.number}:B${total.number}`);
|
||||
total.font = { bold: true };
|
||||
total.getCell(1).fill = solidFill(XL.headerGrey);
|
||||
total.getCell(3).numFmt = '#,##0.00';
|
||||
total.getCell(3).fill = solidFill(XL.derivedBlue);
|
||||
|
||||
boxAllCells(sheet, start, sheet.rowCount, 3);
|
||||
|
||||
const ref = sheet.addRow(['', '', 'Refer Earnings Sheet — line 12 ix) Others']);
|
||||
ref.getCell(3).font = { italic: true, size: 10 };
|
||||
}
|
||||
|
||||
private toast(msg: string): void {
|
||||
this.toastMsg = msg;
|
||||
this.toastVisible = true;
|
||||
if (this.toastTimer) {
|
||||
clearTimeout(this.toastTimer);
|
||||
}
|
||||
this.toastTimer = setTimeout(() => (this.toastVisible = false), 3200);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user