Add consolidated disclosure statement type
Create unified disclosure statement to organize footnote disclosures separate from primary financial statements. Disclosures are now grouped by type (tax, debt, securities, derivatives, leases, intangibles, ma, revenue, cash_flow) in a dedicated statement type for cleaner UI presentation.
This commit is contained in:
@@ -1,13 +1,22 @@
|
||||
import { and, desc, eq, gte, lt, sql } from 'drizzle-orm';
|
||||
import { db } from '@/lib/server/db';
|
||||
import { filingStatementSnapshot } from '@/lib/server/db/schema';
|
||||
import { and, desc, eq, gte, lt, sql } from "drizzle-orm";
|
||||
import { db } from "@/lib/server/db";
|
||||
import { filingStatementSnapshot } from "@/lib/server/db/schema";
|
||||
|
||||
type FilingStatementSnapshotRow = typeof filingStatementSnapshot.$inferSelect;
|
||||
|
||||
type ParseStatus = 'ready' | 'partial' | 'failed';
|
||||
type SnapshotSource = 'sec_filing_summary' | 'xbrl_instance' | 'companyfacts_fallback';
|
||||
type ParseStatus = "ready" | "partial" | "failed";
|
||||
type SnapshotSource =
|
||||
| "sec_filing_summary"
|
||||
| "xbrl_instance"
|
||||
| "companyfacts_fallback";
|
||||
|
||||
type FinancialStatementKind = 'income' | 'balance' | 'cash_flow' | 'equity' | 'comprehensive_income';
|
||||
type FinancialStatementKind =
|
||||
| "income"
|
||||
| "balance"
|
||||
| "cash_flow"
|
||||
| "disclosure"
|
||||
| "equity"
|
||||
| "comprehensive_income";
|
||||
|
||||
type StatementValuesByPeriod = Record<string, number | null>;
|
||||
|
||||
@@ -18,7 +27,7 @@ export type FilingStatementSnapshotPeriod = {
|
||||
filingDate: string;
|
||||
periodStart: string | null;
|
||||
periodEnd: string | null;
|
||||
filingType: '10-K' | '10-Q';
|
||||
filingType: "10-K" | "10-Q";
|
||||
periodLabel: string;
|
||||
};
|
||||
|
||||
@@ -53,12 +62,18 @@ export type DimensionStatementSnapshotRow = {
|
||||
|
||||
export type FilingStatementBundle = {
|
||||
periods: FilingStatementSnapshotPeriod[];
|
||||
statements: Record<FinancialStatementKind, FilingFaithfulStatementSnapshotRow[]>;
|
||||
statements: Record<
|
||||
FinancialStatementKind,
|
||||
FilingFaithfulStatementSnapshotRow[]
|
||||
>;
|
||||
};
|
||||
|
||||
export type StandardizedStatementBundle = {
|
||||
periods: FilingStatementSnapshotPeriod[];
|
||||
statements: Record<FinancialStatementKind, StandardizedStatementSnapshotRow[]>;
|
||||
statements: Record<
|
||||
FinancialStatementKind,
|
||||
StandardizedStatementSnapshotRow[]
|
||||
>;
|
||||
};
|
||||
|
||||
export type DimensionStatementBundle = {
|
||||
@@ -70,7 +85,7 @@ export type FilingStatementSnapshotRecord = {
|
||||
filing_id: number;
|
||||
ticker: string;
|
||||
filing_date: string;
|
||||
filing_type: '10-K' | '10-Q';
|
||||
filing_type: "10-K" | "10-Q";
|
||||
period_end: string | null;
|
||||
statement_bundle: FilingStatementBundle | null;
|
||||
standardized_bundle: StandardizedStatementBundle | null;
|
||||
@@ -86,7 +101,7 @@ export type UpsertFilingStatementSnapshotInput = {
|
||||
filing_id: number;
|
||||
ticker: string;
|
||||
filing_date: string;
|
||||
filing_type: '10-K' | '10-Q';
|
||||
filing_type: "10-K" | "10-Q";
|
||||
period_end: string | null;
|
||||
statement_bundle: FilingStatementBundle | null;
|
||||
standardized_bundle: StandardizedStatementBundle | null;
|
||||
@@ -96,7 +111,9 @@ export type UpsertFilingStatementSnapshotInput = {
|
||||
source: SnapshotSource;
|
||||
};
|
||||
|
||||
function toSnapshotRecord(row: FilingStatementSnapshotRow): FilingStatementSnapshotRecord {
|
||||
function toSnapshotRecord(
|
||||
row: FilingStatementSnapshotRow,
|
||||
): FilingStatementSnapshotRecord {
|
||||
return {
|
||||
id: row.id,
|
||||
filing_id: row.filing_id,
|
||||
@@ -111,7 +128,7 @@ function toSnapshotRecord(row: FilingStatementSnapshotRow): FilingStatementSnaps
|
||||
parse_error: row.parse_error,
|
||||
source: row.source,
|
||||
created_at: row.created_at,
|
||||
updated_at: row.updated_at
|
||||
updated_at: row.updated_at,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -131,7 +148,9 @@ export async function getFilingStatementSnapshotByFilingId(filingId: number) {
|
||||
return row ? toSnapshotRecord(row) : null;
|
||||
}
|
||||
|
||||
export async function upsertFilingStatementSnapshot(input: UpsertFilingStatementSnapshotInput) {
|
||||
export async function upsertFilingStatementSnapshot(
|
||||
input: UpsertFilingStatementSnapshotInput,
|
||||
) {
|
||||
const now = new Date().toISOString();
|
||||
|
||||
const [saved] = await db
|
||||
@@ -149,7 +168,7 @@ export async function upsertFilingStatementSnapshot(input: UpsertFilingStatement
|
||||
parse_error: input.parse_error,
|
||||
source: input.source,
|
||||
created_at: now,
|
||||
updated_at: now
|
||||
updated_at: now,
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: filingStatementSnapshot.filing_id,
|
||||
@@ -164,8 +183,8 @@ export async function upsertFilingStatementSnapshot(input: UpsertFilingStatement
|
||||
parse_status: input.parse_status,
|
||||
parse_error: input.parse_error,
|
||||
source: input.source,
|
||||
updated_at: now
|
||||
}
|
||||
updated_at: now,
|
||||
},
|
||||
})
|
||||
.returning();
|
||||
|
||||
@@ -174,16 +193,20 @@ export async function upsertFilingStatementSnapshot(input: UpsertFilingStatement
|
||||
|
||||
export async function listFilingStatementSnapshotsByTicker(input: {
|
||||
ticker: string;
|
||||
window: '10y' | 'all';
|
||||
window: "10y" | "all";
|
||||
limit?: number;
|
||||
cursor?: string | null;
|
||||
}) {
|
||||
const safeLimit = Math.min(Math.max(Math.trunc(input.limit ?? 40), 1), 120);
|
||||
const cursorId = input.cursor ? Number.parseInt(input.cursor, 10) : null;
|
||||
const constraints = [eq(filingStatementSnapshot.ticker, input.ticker.trim().toUpperCase())];
|
||||
const constraints = [
|
||||
eq(filingStatementSnapshot.ticker, input.ticker.trim().toUpperCase()),
|
||||
];
|
||||
|
||||
if (input.window === '10y') {
|
||||
constraints.push(gte(filingStatementSnapshot.filing_date, tenYearsAgoIso()));
|
||||
if (input.window === "10y") {
|
||||
constraints.push(
|
||||
gte(filingStatementSnapshot.filing_date, tenYearsAgoIso()),
|
||||
);
|
||||
}
|
||||
|
||||
if (cursorId && Number.isFinite(cursorId) && cursorId > 0) {
|
||||
@@ -194,18 +217,21 @@ export async function listFilingStatementSnapshotsByTicker(input: {
|
||||
.select()
|
||||
.from(filingStatementSnapshot)
|
||||
.where(and(...constraints))
|
||||
.orderBy(desc(filingStatementSnapshot.filing_date), desc(filingStatementSnapshot.id))
|
||||
.orderBy(
|
||||
desc(filingStatementSnapshot.filing_date),
|
||||
desc(filingStatementSnapshot.id),
|
||||
)
|
||||
.limit(safeLimit + 1);
|
||||
|
||||
const hasMore = rows.length > safeLimit;
|
||||
const usedRows = hasMore ? rows.slice(0, safeLimit) : rows;
|
||||
const nextCursor = hasMore
|
||||
? String(usedRows[usedRows.length - 1]?.id ?? '')
|
||||
? String(usedRows[usedRows.length - 1]?.id ?? "")
|
||||
: null;
|
||||
|
||||
return {
|
||||
snapshots: usedRows.map(toSnapshotRecord),
|
||||
nextCursor
|
||||
nextCursor,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -213,18 +239,21 @@ export async function countFilingStatementSnapshotStatuses(ticker: string) {
|
||||
const rows = await db
|
||||
.select({
|
||||
status: filingStatementSnapshot.parse_status,
|
||||
count: sql<string>`count(*)`
|
||||
count: sql<string>`count(*)`,
|
||||
})
|
||||
.from(filingStatementSnapshot)
|
||||
.where(eq(filingStatementSnapshot.ticker, ticker.trim().toUpperCase()))
|
||||
.groupBy(filingStatementSnapshot.parse_status);
|
||||
|
||||
return rows.reduce<Record<ParseStatus, number>>((acc, row) => {
|
||||
acc[row.status] = Number(row.count);
|
||||
return acc;
|
||||
}, {
|
||||
ready: 0,
|
||||
partial: 0,
|
||||
failed: 0
|
||||
});
|
||||
return rows.reduce<Record<ParseStatus, number>>(
|
||||
(acc, row) => {
|
||||
acc[row.status] = Number(row.count);
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
ready: 0,
|
||||
partial: 0,
|
||||
failed: 0,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -897,6 +897,7 @@ function emptyStatementRows(): StatementRowMap {
|
||||
income: [],
|
||||
balance: [],
|
||||
cash_flow: [],
|
||||
disclosure: [],
|
||||
equity: [],
|
||||
comprehensive_income: [],
|
||||
};
|
||||
@@ -907,6 +908,7 @@ function emptySurfaceRows(): SurfaceRowMap {
|
||||
income: [],
|
||||
balance: [],
|
||||
cash_flow: [],
|
||||
disclosure: [],
|
||||
equity: [],
|
||||
comprehensive_income: [],
|
||||
};
|
||||
@@ -917,6 +919,7 @@ function emptyDetailRows(): DetailRowMap {
|
||||
income: {},
|
||||
balance: {},
|
||||
cash_flow: {},
|
||||
disclosure: {},
|
||||
equity: {},
|
||||
comprehensive_income: {},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user