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:
@@ -16,6 +16,11 @@
|
||||
{{ asOnDate }}.
|
||||
</p>
|
||||
</div>
|
||||
<div class="pg-toolbar">
|
||||
<button type="button" class="pg-btn pg-btn--ghost" [disabled]="generating" (click)="exportExcel()">
|
||||
{{ generating ? 'Exporting…' : 'Export to Excel' }}
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="inv-kpis">
|
||||
@@ -196,3 +201,5 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="inv-toast" [class.show]="toastVisible">{{ toastMsg }}</div>
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
daysToMaturity,
|
||||
maturityAmount
|
||||
} from '../investment-data';
|
||||
import { exportWorkbook } from '../investment-export';
|
||||
|
||||
interface InstrumentSummary {
|
||||
type: InstrumentType;
|
||||
@@ -57,6 +58,10 @@ interface AllocationRow {
|
||||
})
|
||||
export class InvestmentDashboardComponent {
|
||||
asOnDate = '';
|
||||
generating = false;
|
||||
toastMsg = '';
|
||||
toastVisible = false;
|
||||
private toastTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
private readonly types: Array<{ type: InstrumentType; route: string }> = [
|
||||
{ type: 'FD', route: '/investment/fd' },
|
||||
{ type: 'GOVT_SECURITY', route: '/investment/bonds' },
|
||||
@@ -152,4 +157,85 @@ export class InvestmentDashboardComponent {
|
||||
instrumentLabel(type: InstrumentType): string {
|
||||
return INSTRUMENT_LABELS[type];
|
||||
}
|
||||
|
||||
async exportExcel(): Promise<void> {
|
||||
this.generating = true;
|
||||
try {
|
||||
await exportWorkbook(
|
||||
[
|
||||
{
|
||||
sheetName: 'Instrument Breakup',
|
||||
title: 'Investment Portfolio — Instrument-wise Break-up',
|
||||
subtitle: `As on ${this.asOnDate}`,
|
||||
headers: [
|
||||
'Instrument Type',
|
||||
'Holdings',
|
||||
'Face Value',
|
||||
'Accrued Interest',
|
||||
'Maturity Value'
|
||||
],
|
||||
widths: [
|
||||
26,
|
||||
12,
|
||||
18,
|
||||
18,
|
||||
18
|
||||
],
|
||||
rows: this.summaries.map((s) => [
|
||||
s.label,
|
||||
s.count,
|
||||
s.faceValue,
|
||||
s.accruedInterest,
|
||||
s.maturityValue
|
||||
])
|
||||
},
|
||||
{
|
||||
sheetName: 'Portfolio Allocation',
|
||||
title: 'Portfolio Allocation — Target vs Actual',
|
||||
subtitle: `As on ${this.asOnDate}`,
|
||||
headers: [
|
||||
'Instrument Type',
|
||||
'Target %',
|
||||
'Actual %',
|
||||
'Actual Value',
|
||||
'Variance %',
|
||||
'Status'
|
||||
],
|
||||
widths: [
|
||||
26,
|
||||
12,
|
||||
12,
|
||||
18,
|
||||
12,
|
||||
18
|
||||
],
|
||||
rows: this.allocationVsTarget.map((a) => [
|
||||
a.label,
|
||||
a.targetPct,
|
||||
a.actualPct,
|
||||
a.actualValue,
|
||||
a.variancePct,
|
||||
a.statusLabel
|
||||
])
|
||||
}
|
||||
],
|
||||
`Investment-Dashboard-${this.asOnDate}`
|
||||
);
|
||||
this.toast('Investment Dashboard exported.');
|
||||
} catch (err) {
|
||||
console.error('Investment Dashboard 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;
|
||||
if (this.toastTimer) {
|
||||
clearTimeout(this.toastTimer);
|
||||
}
|
||||
this.toastTimer = setTimeout(() => (this.toastVisible = false), 3200);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user