feat(investment,reconciliation): add Export to Excel across Investment, fix export popup

Adds a shared Export to Excel utility (investment-export.ts) and wires it
into every listing/report screen in the Investment module — Dashboard
(break-up + allocation), Reports (all ~20 report shapes), Interest (per
active tab), the FD/Bond/Share Holdings registers, and Masters (per active
master tab). Entry/transaction forms intentionally have no export button.
No sign-off is included here, matching the request to keep that feature
reconciliation-only.

Also fixes the Reconciliation export flow: the Prepared/Checked/Approved
sign-off is now an optional one-off popup shown when "Export to Excel" is
clicked (fields are blank by default and downloading works whether or not
they're filled), replacing the earlier always-visible sign-off card. Also
fixes three reconciliation reports (Sundry Dr/Cr, NEFT/RTGS, UDIR) whose
labels contain "/" — an Excel-forbidden worksheet-name character — which
was silently aborting their export.
This commit is contained in:
2026-08-01 19:20:53 +05:30
parent 8ac4010967
commit 0832017508
18 changed files with 1610 additions and 0 deletions
@@ -43,6 +43,7 @@ import {
yearsBetween,
todayDDMMYYYY
} from '../investment-data';
import { exportWorkbook } from '../investment-export';
type BondType = 'GOVT_SECURITY' | 'NON_SLR' | 'BOND' | 'MUTUAL_FUND';
type BondTab =
@@ -210,6 +211,7 @@ export class BondInvestmentComponent {
readonly intTypeOptions = INT_TYPE_OPTIONS;
readonly authorityOptions = AUTHORITY_OPTIONS;
generating = false;
toastMsg = '';
toastVisible = false;
private toastTimer: ReturnType<typeof setTimeout> | undefined;
@@ -1092,6 +1094,68 @@ export class BondInvestmentComponent {
return `inv-badge inv-badge--${h.status.toLowerCase()}`;
}
// -------------------- Export to Excel (Holdings register) --------------------
async exportExcel(): Promise<void> {
this.generating = true;
try {
await exportWorkbook(
[
{
sheetName: this.instrumentLabel(this.bondType),
title: `${this.instrumentLabel(this.bondType)} Holdings Register`,
subtitle: `As on ${this.asOnDate}`,
headers: [
'Cert No',
'Script',
'Party',
'Units',
'Face Value',
'Rate',
'Purchase Date',
'Maturity Date',
'Accrued Interest',
'Maturity Value',
'Status'
],
widths: [
14,
26,
22,
12,
16,
10,
14,
14,
16,
16,
12
],
rows: this.holdingsForType.map((h) => [
h.certNo,
h.scriptName,
h.party,
h.units,
h.faceValue,
h.interestRate,
h.purchaseDate,
h.maturityDate,
this.accrued(h),
this.maturity(h),
h.status
])
}
],
`${this.instrumentLabel(this.bondType)}-Holdings-${this.asOnDate}`
);
this.toast(`${this.instrumentLabel(this.bondType)} Holdings exported.`);
} catch (err) {
console.error('Bond Holdings export failed', err);
this.toast('Could not generate the workbook — please retry.');
} finally {
this.generating = false;
}
}
private toast(msg: string): void {
this.toastMsg = msg;
this.toastVisible = true;