Expand financials surfaces with ratios, KPIs, and cadence support

- Add bundled financial modeling pipeline (ratios, KPI dimensions/notes, trend series, standardization)
- Introduce company financial bundles storage (Drizzle migration + repo wiring)
- Refactor financials page/API/query flow to use surfaceKind + cadence and new response shapes
This commit is contained in:
2026-03-07 15:16:35 -05:00
parent a42622ba6e
commit db01f207a5
33 changed files with 3589 additions and 1643 deletions

View File

@@ -171,7 +171,7 @@ describe('financial taxonomy internals', () => {
]
});
const selection = __financialTaxonomyInternals.selectPrimaryPeriods([snapshot], 'income');
const selection = __financialTaxonomyInternals.selectPrimaryPeriodsByCadence([snapshot], 'income', 'quarterly');
expect(selection.periods).toHaveLength(1);
expect(selection.periods[0]?.id).toBe('quarter');
@@ -189,7 +189,7 @@ describe('financial taxonomy internals', () => {
]
});
const selection = __financialTaxonomyInternals.selectPrimaryPeriods([snapshot], 'balance');
const selection = __financialTaxonomyInternals.selectPrimaryPeriodsByCadence([snapshot], 'balance', 'annual');
expect(selection.periods).toHaveLength(1);
expect(selection.periods[0]?.id).toBe('current');
@@ -218,9 +218,11 @@ describe('financial taxonomy internals', () => {
]
});
const periods = __financialTaxonomyInternals.buildPeriods([annual, quarterly], 'income');
const annualPeriods = __financialTaxonomyInternals.selectPrimaryPeriodsByCadence([annual, quarterly], 'income', 'annual').periods;
const quarterlyPeriods = __financialTaxonomyInternals.selectPrimaryPeriodsByCadence([annual, quarterly], 'income', 'quarterly').periods;
expect(periods.map((period) => period.id)).toEqual(['annual', 'quarter']);
expect(annualPeriods.map((period) => period.id)).toEqual(['annual']);
expect(quarterlyPeriods.map((period) => period.id)).toEqual(['quarter']);
});
it('maps overlapping GAAP aliases into one standardized COGS row while preserving faithful rows', () => {
@@ -281,14 +283,17 @@ describe('financial taxonomy internals', () => {
], 'income', new Set(['2024-q4', '2025-q4']));
const standardizedRows = __financialTaxonomyInternals.buildStandardizedRows(
faithfulRows,
'income',
[period2024, period2025]
{
rows: faithfulRows,
statement: 'income',
periods: [period2024, period2025],
facts: []
}
);
expect(faithfulRows).toHaveLength(2);
const cogs = standardizedRows.find((row) => row.key === 'cost-of-revenue');
const cogs = standardizedRows.find((row) => row.key === 'cost_of_revenue');
expect(cogs).toBeDefined();
expect(cogs?.values['2024-q4']).toBe(45_000);
expect(cogs?.values['2025-q4']).toBe(48_000);
@@ -327,9 +332,12 @@ describe('financial taxonomy internals', () => {
})
];
const standardizedRows = __financialTaxonomyInternals.buildStandardizedRows(
faithfulRows,
'income',
[period2024, period2025]
{
rows: faithfulRows,
statement: 'income',
periods: [period2024, period2025],
facts: []
}
);
const breakdown = __financialTaxonomyInternals.buildDimensionBreakdown([
@@ -355,7 +363,7 @@ describe('financial taxonomy internals', () => {
})
], [period2024, period2025], faithfulRows, standardizedRows);
const cogs = breakdown?.['cost-of-revenue'] ?? [];
const cogs = breakdown?.['cost_of_revenue'] ?? [];
expect(cogs).toHaveLength(2);
expect(cogs.map((row) => row.sourceLabel)).toEqual([
'Cost of Revenue',