Implement dual-surface financials and db bootstrap
This commit is contained in:
@@ -1,26 +1,47 @@
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
import { __financialTaxonomyInternals } from './financial-taxonomy';
|
||||
import type { FilingTaxonomySnapshotRecord } from './repos/filing-taxonomy';
|
||||
import type { FinancialStatementKind, TaxonomyStatementRow } from '@/lib/types';
|
||||
import type {
|
||||
FinancialStatementKind,
|
||||
FinancialStatementPeriod,
|
||||
TaxonomyFactRow,
|
||||
TaxonomyStatementRow
|
||||
} from '@/lib/types';
|
||||
|
||||
function createRow(input: {
|
||||
key?: string;
|
||||
label?: string;
|
||||
conceptKey?: string;
|
||||
qname?: string;
|
||||
localName?: string;
|
||||
statement?: FinancialStatementKind;
|
||||
order?: number;
|
||||
depth?: number;
|
||||
hasDimensions?: boolean;
|
||||
values: Record<string, number | null>;
|
||||
sourceFactIds?: number[];
|
||||
}): TaxonomyStatementRow {
|
||||
const localName = input.localName ?? 'RevenueFromContractWithCustomerExcludingAssessedTax';
|
||||
const conceptKey = input.conceptKey ?? `http://fasb.org/us-gaap/2024#${localName}`;
|
||||
const qname = input.qname ?? `us-gaap:${localName}`;
|
||||
|
||||
function createRow(periodIds: string[]): TaxonomyStatementRow {
|
||||
return {
|
||||
key: 'us-gaap:RevenueFromContractWithCustomerExcludingAssessedTax',
|
||||
label: 'Revenue From Contract With Customer Excluding Assessed Tax',
|
||||
conceptKey: 'us-gaap:RevenueFromContractWithCustomerExcludingAssessedTax',
|
||||
qname: 'us-gaap:RevenueFromContractWithCustomerExcludingAssessedTax',
|
||||
namespaceUri: 'http://fasb.org/us-gaap/2021-01-31',
|
||||
localName: 'RevenueFromContractWithCustomerExcludingAssessedTax',
|
||||
key: input.key ?? conceptKey,
|
||||
label: input.label ?? localName,
|
||||
conceptKey,
|
||||
qname,
|
||||
namespaceUri: 'http://fasb.org/us-gaap/2024',
|
||||
localName,
|
||||
isExtension: false,
|
||||
statement: 'income',
|
||||
roleUri: 'income',
|
||||
order: 1,
|
||||
depth: 0,
|
||||
statement: input.statement ?? 'income',
|
||||
roleUri: input.statement ?? 'income',
|
||||
order: input.order ?? 1,
|
||||
depth: input.depth ?? 0,
|
||||
parentKey: null,
|
||||
values: Object.fromEntries(periodIds.map((periodId, index) => [periodId, 100 + index])),
|
||||
units: Object.fromEntries(periodIds.map((periodId) => [periodId, 'iso4217:USD'])),
|
||||
hasDimensions: false,
|
||||
sourceFactIds: periodIds.map((_, index) => index + 1)
|
||||
values: input.values,
|
||||
units: Object.fromEntries(Object.keys(input.values).map((periodId) => [periodId, 'iso4217:USD'])),
|
||||
hasDimensions: input.hasDimensions ?? false,
|
||||
sourceFactIds: input.sourceFactIds ?? [1]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -35,8 +56,12 @@ function createSnapshot(input: {
|
||||
periodLabel: string;
|
||||
}>;
|
||||
statement: FinancialStatementKind;
|
||||
rows?: TaxonomyStatementRow[];
|
||||
}) {
|
||||
const row = createRow(input.periods.map((period) => period.id));
|
||||
const defaultRow = createRow({
|
||||
statement: input.statement,
|
||||
values: Object.fromEntries(input.periods.map((period, index) => [period.id, 100 + index]))
|
||||
});
|
||||
|
||||
return {
|
||||
id: input.filingId,
|
||||
@@ -58,9 +83,9 @@ function createSnapshot(input: {
|
||||
periodLabel: period.periodLabel
|
||||
})),
|
||||
statement_rows: {
|
||||
income: input.statement === 'income' ? [row] : [],
|
||||
balance: input.statement === 'balance' ? [{ ...row, statement: 'balance' }] : [],
|
||||
cash_flow: [],
|
||||
income: input.statement === 'income' ? (input.rows ?? [defaultRow]) : [],
|
||||
balance: input.statement === 'balance' ? (input.rows ?? [{ ...defaultRow, statement: 'balance' }]) : [],
|
||||
cash_flow: input.statement === 'cash_flow' ? (input.rows ?? [{ ...defaultRow, statement: 'cash_flow' }]) : [],
|
||||
equity: [],
|
||||
comprehensive_income: []
|
||||
},
|
||||
@@ -74,6 +99,64 @@ function createSnapshot(input: {
|
||||
} satisfies FilingTaxonomySnapshotRecord;
|
||||
}
|
||||
|
||||
function createPeriod(input: {
|
||||
id: string;
|
||||
filingId: number;
|
||||
filingDate: string;
|
||||
periodEnd: string;
|
||||
periodStart?: string | null;
|
||||
filingType?: '10-K' | '10-Q';
|
||||
}): FinancialStatementPeriod {
|
||||
return {
|
||||
id: input.id,
|
||||
filingId: input.filingId,
|
||||
accessionNumber: `0000-${input.filingId}`,
|
||||
filingDate: input.filingDate,
|
||||
periodStart: input.periodStart ?? null,
|
||||
periodEnd: input.periodEnd,
|
||||
filingType: input.filingType ?? '10-Q',
|
||||
periodLabel: 'Test period'
|
||||
};
|
||||
}
|
||||
|
||||
function createDimensionFact(input: {
|
||||
filingId: number;
|
||||
filingDate: string;
|
||||
conceptKey: string;
|
||||
qname: string;
|
||||
localName: string;
|
||||
periodEnd: string;
|
||||
value: number;
|
||||
axis?: string;
|
||||
member?: string;
|
||||
}): TaxonomyFactRow {
|
||||
return {
|
||||
id: input.filingId,
|
||||
snapshotId: input.filingId,
|
||||
filingId: input.filingId,
|
||||
filingDate: input.filingDate,
|
||||
statement: 'income',
|
||||
roleUri: 'income',
|
||||
conceptKey: input.conceptKey,
|
||||
qname: input.qname,
|
||||
namespaceUri: 'http://fasb.org/us-gaap/2024',
|
||||
localName: input.localName,
|
||||
value: input.value,
|
||||
contextId: `ctx-${input.filingId}`,
|
||||
unit: 'iso4217:USD',
|
||||
decimals: null,
|
||||
periodStart: '2025-01-01',
|
||||
periodEnd: input.periodEnd,
|
||||
periodInstant: null,
|
||||
dimensions: [{
|
||||
axis: input.axis ?? 'srt:ProductOrServiceAxis',
|
||||
member: input.member ?? 'msft:CloudMember'
|
||||
}],
|
||||
isDimensionless: false,
|
||||
sourceFile: null
|
||||
};
|
||||
}
|
||||
|
||||
describe('financial taxonomy internals', () => {
|
||||
it('selects the primary quarter duration for 10-Q income statements', () => {
|
||||
const snapshot = createSnapshot({
|
||||
@@ -139,4 +222,144 @@ describe('financial taxonomy internals', () => {
|
||||
|
||||
expect(periods.map((period) => period.id)).toEqual(['annual', 'quarter']);
|
||||
});
|
||||
|
||||
it('maps overlapping GAAP aliases into one standardized COGS row while preserving faithful rows', () => {
|
||||
const period2024 = createPeriod({
|
||||
id: '2024-q4',
|
||||
filingId: 30,
|
||||
filingDate: '2025-01-29',
|
||||
periodEnd: '2024-12-31'
|
||||
});
|
||||
const period2025 = createPeriod({
|
||||
id: '2025-q4',
|
||||
filingId: 31,
|
||||
filingDate: '2026-01-28',
|
||||
periodEnd: '2025-12-31'
|
||||
});
|
||||
|
||||
const faithfulRows = __financialTaxonomyInternals.buildRows([
|
||||
createSnapshot({
|
||||
filingId: 30,
|
||||
filingType: '10-Q',
|
||||
filingDate: '2025-01-29',
|
||||
statement: 'income',
|
||||
periods: [{
|
||||
id: '2024-q4',
|
||||
periodStart: '2024-10-01',
|
||||
periodEnd: '2024-12-31',
|
||||
periodLabel: '2024-10-01 to 2024-12-31'
|
||||
}],
|
||||
rows: [
|
||||
createRow({
|
||||
localName: 'CostOfRevenue',
|
||||
label: 'Cost of Revenue',
|
||||
values: { '2024-q4': 45_000 },
|
||||
sourceFactIds: [101]
|
||||
})
|
||||
]
|
||||
}),
|
||||
createSnapshot({
|
||||
filingId: 31,
|
||||
filingType: '10-Q',
|
||||
filingDate: '2026-01-28',
|
||||
statement: 'income',
|
||||
periods: [{
|
||||
id: '2025-q4',
|
||||
periodStart: '2025-10-01',
|
||||
periodEnd: '2025-12-31',
|
||||
periodLabel: '2025-10-01 to 2025-12-31'
|
||||
}],
|
||||
rows: [
|
||||
createRow({
|
||||
localName: 'CostOfGoodsSold',
|
||||
label: 'Cost of Goods Sold',
|
||||
values: { '2025-q4': 48_000 },
|
||||
sourceFactIds: [202]
|
||||
})
|
||||
]
|
||||
})
|
||||
], 'income', new Set(['2024-q4', '2025-q4']));
|
||||
|
||||
const standardizedRows = __financialTaxonomyInternals.buildStandardizedRows(
|
||||
faithfulRows,
|
||||
'income',
|
||||
[period2024, period2025]
|
||||
);
|
||||
|
||||
expect(faithfulRows).toHaveLength(2);
|
||||
|
||||
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);
|
||||
expect(cogs?.sourceConcepts).toEqual([
|
||||
'us-gaap:CostOfGoodsSold',
|
||||
'us-gaap:CostOfRevenue'
|
||||
]);
|
||||
expect(cogs?.sourceRowKeys).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('aggregates standardized dimension drill-down across mapped source concepts', () => {
|
||||
const period2024 = createPeriod({
|
||||
id: '2024-q4',
|
||||
filingId: 40,
|
||||
filingDate: '2025-01-29',
|
||||
periodEnd: '2024-12-31'
|
||||
});
|
||||
const period2025 = createPeriod({
|
||||
id: '2025-q4',
|
||||
filingId: 41,
|
||||
filingDate: '2026-01-28',
|
||||
periodEnd: '2025-12-31'
|
||||
});
|
||||
const faithfulRows = [
|
||||
createRow({
|
||||
localName: 'CostOfRevenue',
|
||||
label: 'Cost of Revenue',
|
||||
values: { '2024-q4': 45_000 },
|
||||
hasDimensions: true
|
||||
}),
|
||||
createRow({
|
||||
localName: 'CostOfGoodsSold',
|
||||
label: 'Cost of Goods Sold',
|
||||
values: { '2025-q4': 48_000 },
|
||||
hasDimensions: true
|
||||
})
|
||||
];
|
||||
const standardizedRows = __financialTaxonomyInternals.buildStandardizedRows(
|
||||
faithfulRows,
|
||||
'income',
|
||||
[period2024, period2025]
|
||||
);
|
||||
|
||||
const breakdown = __financialTaxonomyInternals.buildDimensionBreakdown([
|
||||
createDimensionFact({
|
||||
filingId: 40,
|
||||
filingDate: '2025-01-29',
|
||||
conceptKey: faithfulRows[0].key,
|
||||
qname: faithfulRows[0].qname,
|
||||
localName: faithfulRows[0].localName,
|
||||
periodEnd: '2024-12-31',
|
||||
value: 20_000,
|
||||
member: 'msft:ProductivityMember'
|
||||
}),
|
||||
createDimensionFact({
|
||||
filingId: 41,
|
||||
filingDate: '2026-01-28',
|
||||
conceptKey: faithfulRows[1].key,
|
||||
qname: faithfulRows[1].qname,
|
||||
localName: faithfulRows[1].localName,
|
||||
periodEnd: '2025-12-31',
|
||||
value: 28_000,
|
||||
member: 'msft:IntelligentCloudMember'
|
||||
})
|
||||
], [period2024, period2025], faithfulRows, standardizedRows);
|
||||
|
||||
const cogs = breakdown?.['cost-of-revenue'] ?? [];
|
||||
expect(cogs).toHaveLength(2);
|
||||
expect(cogs.map((row) => row.sourceLabel)).toEqual([
|
||||
'Cost of Revenue',
|
||||
'Cost of Goods Sold'
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user