Implement fiscal-style research MVP flows
Some checks failed
PR Checks / typecheck-and-build (push) Has been cancelled

This commit is contained in:
2026-03-07 09:51:18 -05:00
parent f69e5b671b
commit 52136271d3
26 changed files with 2719 additions and 243 deletions

View File

@@ -707,6 +707,85 @@ function latestMetrics(snapshots: FilingTaxonomySnapshotRecord[]) {
};
}
function rowValue(row: { values: Record<string, number | null> }, periodId: string) {
return periodId in row.values ? row.values[periodId] : null;
}
function findStandardizedRow(rows: StandardizedStatementRow[], key: string) {
return rows.find((row) => row.key === key) ?? null;
}
function findFaithfulRowByLocalNames(rows: TaxonomyStatementRow[], localNames: string[]) {
const normalizedNames = localNames.map((name) => name.toLowerCase());
const exact = rows.find((row) => normalizedNames.includes(row.localName.toLowerCase()));
if (exact) {
return exact;
}
return rows.find((row) => {
const haystack = `${row.key} ${row.label} ${row.localName} ${row.qname}`.toLowerCase();
return normalizedNames.some((name) => haystack.includes(name));
}) ?? null;
}
function asOverviewLabel(period: FinancialStatementPeriod) {
const source = period.periodEnd ?? period.filingDate;
const parsed = Date.parse(source);
if (!Number.isFinite(parsed)) {
return source;
}
return new Intl.DateTimeFormat('en-US', {
month: 'short',
year: 'numeric'
}).format(new Date(parsed));
}
function buildOverviewMetrics(input: {
periods: FinancialStatementPeriod[];
faithfulRows: TaxonomyStatementRow[];
standardizedRows: StandardizedStatementRow[];
}): CompanyFinancialStatementsResponse['overviewMetrics'] {
const periods = [...input.periods].sort(periodSorter);
const revenueRow = findStandardizedRow(input.standardizedRows, 'revenue')
?? findFaithfulRowByLocalNames(input.faithfulRows, ['RevenueFromContractWithCustomerExcludingAssessedTax', 'Revenues', 'SalesRevenueNet', 'Revenue']);
const netIncomeRow = findStandardizedRow(input.standardizedRows, 'net-income')
?? findFaithfulRowByLocalNames(input.faithfulRows, ['NetIncomeLoss', 'ProfitLoss']);
const assetsRow = findStandardizedRow(input.standardizedRows, 'total-assets')
?? findFaithfulRowByLocalNames(input.faithfulRows, ['Assets']);
const cashRow = findStandardizedRow(input.standardizedRows, 'cash-and-equivalents')
?? findFaithfulRowByLocalNames(input.faithfulRows, ['CashAndCashEquivalentsAtCarryingValue', 'CashAndShortTermInvestments', 'Cash']);
const debtRow = findStandardizedRow(input.standardizedRows, 'total-debt')
?? findFaithfulRowByLocalNames(input.faithfulRows, ['LongTermDebt', 'Debt', 'LongTermDebtNoncurrent']);
const series = periods.map((period) => ({
periodId: period.id,
filingDate: period.filingDate,
periodEnd: period.periodEnd,
label: asOverviewLabel(period),
revenue: revenueRow ? rowValue(revenueRow, period.id) : null,
netIncome: netIncomeRow ? rowValue(netIncomeRow, period.id) : null,
totalAssets: assetsRow ? rowValue(assetsRow, period.id) : null,
cash: cashRow ? rowValue(cashRow, period.id) : null,
debt: debtRow ? rowValue(debtRow, period.id) : null
}));
const latest = series[series.length - 1] ?? null;
return {
referencePeriodId: latest?.periodId ?? null,
referenceDate: latest?.filingDate ?? null,
latest: {
revenue: latest?.revenue ?? null,
netIncome: latest?.netIncome ?? null,
totalAssets: latest?.totalAssets ?? null,
cash: latest?.cash ?? null,
debt: latest?.debt ?? null
},
series
};
}
export function defaultFinancialSyncLimit(window: FinancialHistoryWindow) {
return window === 'all' ? 120 : 60;
}
@@ -806,6 +885,11 @@ export async function getCompanyFinancialTaxonomy(input: GetCompanyFinancialTaxo
pendingFilings: Math.max(0, financialFilings.length - statuses.ready - statuses.partial - statuses.failed),
queuedSync: input.queuedSync
},
overviewMetrics: buildOverviewMetrics({
periods,
faithfulRows,
standardizedRows
}),
metrics,
dimensionBreakdown
};