Implement fiscal-style research MVP flows
Some checks failed
PR Checks / typecheck-and-build (push) Has been cancelled
Some checks failed
PR Checks / typecheck-and-build (push) Has been cancelled
This commit is contained in:
@@ -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
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user