A claim that someone checked the books is evidence the CPA handoff rests on, so it is a journal fact and replays: audit.run (a programme run through a day at the books' figure head, with the hashes that make it reproducible and the two filed workpapers), receipt.attested, entry.reviewed and period.attested. Behind the journal feature audits, schema 20; no figure moves. The figure head is the last line that bears on a figure: filing a workpaper, a run or an attestation never makes an audit stale, an entry, document, close, settings or role change does, so a period is attested only on an audit that still describes the books. A receipt reading that differs from the entry is recorded as disagreeing and never counts; whether a claim counts is derived against the bar when asked, never stored. The store also reads what a ledger cannot: the chain, the database against a replay, stored and extracted files, the access log, duplicate files, and the backups' standing handed in by the server. Fixture old-books/p8_before was written by the version before this one and opens to its figures. period.attested was the tests' placeholder for an unknown event type; they use ledger.not_yet_written now. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
526 lines
18 KiB
Rust
526 lines
18 KiB
Rust
//! SQLite schema and migrations.
|
|
//!
|
|
//! The projection schema. Amounts are TEXT (decimal strings) — never REAL.
|
|
//! Migrations are numbered SQL scripts applied in order; `SCHEMA_VERSION`
|
|
//! is the current version.
|
|
|
|
/// Current schema version.
|
|
pub const SCHEMA_VERSION: u32 = 20;
|
|
|
|
/// Migrations, in order. `MIGRATIONS[i]` migrates from version `i` to `i+1`.
|
|
pub const MIGRATIONS: &[&str] = &[
|
|
r#"
|
|
CREATE TABLE IF NOT EXISTS meta(
|
|
key TEXT PRIMARY KEY,
|
|
value TEXT NOT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS accounts(
|
|
id INTEGER PRIMARY KEY,
|
|
code TEXT UNIQUE NOT NULL,
|
|
name TEXT NOT NULL,
|
|
type TEXT NOT NULL,
|
|
parent INTEGER NULL,
|
|
placeholder INTEGER NOT NULL,
|
|
currency TEXT NOT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS entries(
|
|
id INTEGER PRIMARY KEY,
|
|
date TEXT NOT NULL,
|
|
description TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
reversal_of INTEGER NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS lines(
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
entry_id INTEGER NOT NULL,
|
|
account_id INTEGER NOT NULL,
|
|
side TEXT NOT NULL,
|
|
amount TEXT NOT NULL,
|
|
currency TEXT NOT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS blobs(
|
|
sha256 TEXT PRIMARY KEY,
|
|
size_bytes INTEGER NOT NULL,
|
|
mime_type TEXT NOT NULL,
|
|
original_name TEXT NOT NULL,
|
|
created_at TEXT NOT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS attachments(
|
|
id INTEGER PRIMARY KEY,
|
|
blob_sha TEXT NOT NULL,
|
|
entry_id INTEGER NULL,
|
|
category TEXT NOT NULL,
|
|
description TEXT NULL,
|
|
created_at TEXT NOT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS events(
|
|
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
type TEXT NOT NULL,
|
|
payload TEXT NOT NULL,
|
|
created_at TEXT NOT NULL
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_lines_entry ON lines(entry_id);
|
|
CREATE INDEX IF NOT EXISTS idx_entries_date ON entries(date);
|
|
CREATE INDEX IF NOT EXISTS idx_attachments_entry ON attachments(entry_id);
|
|
"#,
|
|
r#"
|
|
-- v2: fixed-asset register and depreciation runs.
|
|
CREATE TABLE IF NOT EXISTS assets(
|
|
id INTEGER PRIMARY KEY,
|
|
description TEXT NOT NULL,
|
|
purchase_date TEXT NOT NULL,
|
|
cost_amount TEXT NOT NULL,
|
|
cost_currency TEXT NOT NULL,
|
|
salvage_amount TEXT NOT NULL,
|
|
salvage_currency TEXT NOT NULL,
|
|
useful_life_months INTEGER NOT NULL,
|
|
method TEXT NOT NULL,
|
|
cost_account INTEGER NOT NULL,
|
|
accum_depr_account INTEGER NOT NULL,
|
|
expense_account INTEGER NOT NULL,
|
|
status TEXT NOT NULL,
|
|
disposed_date TEXT NULL,
|
|
proceeds_amount TEXT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS asset_depreciation(
|
|
asset_id INTEGER NOT NULL,
|
|
period TEXT NOT NULL,
|
|
entry_id INTEGER NOT NULL,
|
|
amount TEXT NOT NULL,
|
|
PRIMARY KEY (asset_id, period)
|
|
);
|
|
"#,
|
|
r#"
|
|
-- v3: accounting periods, A/R + A/P, cleared-line flags, FX rates, and
|
|
-- foreign-currency columns on lines. Amounts are TEXT, never REAL.
|
|
CREATE TABLE IF NOT EXISTS periods(
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
start TEXT NOT NULL,
|
|
end TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
closing_entry INTEGER NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS customers(
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT NOT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS vendors(
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT NOT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS invoices(
|
|
id INTEGER PRIMARY KEY,
|
|
customer_id INTEGER NOT NULL,
|
|
date TEXT NOT NULL,
|
|
due_date TEXT NOT NULL,
|
|
lines_json TEXT NOT NULL,
|
|
amount_paid TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
entry_id INTEGER NOT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS bills(
|
|
id INTEGER PRIMARY KEY,
|
|
vendor_id INTEGER NOT NULL,
|
|
date TEXT NOT NULL,
|
|
due_date TEXT NOT NULL,
|
|
lines_json TEXT NOT NULL,
|
|
amount_paid TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
entry_id INTEGER NOT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS cleared_lines(
|
|
entry_id INTEGER NOT NULL,
|
|
line_index INTEGER NOT NULL,
|
|
PRIMARY KEY (entry_id, line_index)
|
|
);
|
|
CREATE TABLE IF NOT EXISTS fx_rates(
|
|
currency TEXT NOT NULL,
|
|
date TEXT NOT NULL,
|
|
rate TEXT NOT NULL,
|
|
PRIMARY KEY (currency, date)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_invoices_customer ON invoices(customer_id);
|
|
CREATE INDEX IF NOT EXISTS idx_bills_vendor ON bills(vendor_id);
|
|
ALTER TABLE lines ADD COLUMN foreign_currency TEXT;
|
|
ALTER TABLE lines ADD COLUMN foreign_amount TEXT;
|
|
"#,
|
|
r#"
|
|
-- v4: classes, payroll runs, and class_id on lines. Amounts are TEXT,
|
|
-- never REAL.
|
|
CREATE TABLE IF NOT EXISTS classes(
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT NOT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS payroll_runs(
|
|
id INTEGER PRIMARY KEY,
|
|
date TEXT NOT NULL,
|
|
entry_id INTEGER NOT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS payroll_lines(
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
run_id INTEGER NOT NULL,
|
|
employee_name TEXT NOT NULL,
|
|
gross_pay TEXT NOT NULL,
|
|
federal_withholding TEXT NOT NULL,
|
|
state_withholding TEXT NOT NULL,
|
|
ss_employee TEXT NOT NULL,
|
|
medicare_employee TEXT NOT NULL,
|
|
ss_employer TEXT NOT NULL,
|
|
medicare_employer TEXT NOT NULL,
|
|
other_deductions TEXT NOT NULL
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_payroll_lines_run ON payroll_lines(run_id);
|
|
ALTER TABLE lines ADD COLUMN class_id INTEGER;
|
|
"#,
|
|
r#"
|
|
-- v5: withdrawn attachments. Withdrawal retires a receipt or document from
|
|
-- listings and linking; the bytes stay in the blob store and the journal
|
|
-- keeps the filing and the withdrawal.
|
|
ALTER TABLE attachments ADD COLUMN withdrawn_at TEXT;
|
|
ALTER TABLE attachments ADD COLUMN withdrawn_reason TEXT;
|
|
"#,
|
|
r#"
|
|
-- v6: tax categories on accounts (where each account lands on the return,
|
|
-- and how much of it is deductible). Percent is TEXT, never REAL.
|
|
ALTER TABLE accounts ADD COLUMN tax_category TEXT;
|
|
ALTER TABLE accounts ADD COLUMN deductible_percent TEXT;
|
|
"#,
|
|
r#"
|
|
-- v7: 1099 tracking: which return each vendor gets, whether its W-9 is on
|
|
-- file (never the TIN itself), and the vendor each entry paid.
|
|
ALTER TABLE vendors ADD COLUMN form_1099 TEXT;
|
|
ALTER TABLE vendors ADD COLUMN tin_on_file INTEGER NOT NULL DEFAULT 0;
|
|
CREATE TABLE IF NOT EXISTS entry_vendors(
|
|
entry_id INTEGER PRIMARY KEY,
|
|
vendor_id INTEGER NOT NULL
|
|
);
|
|
"#,
|
|
r#"
|
|
-- v8: each filing keeps its own file name and type. Before, they came from
|
|
-- the blob, so filing the same bytes again under another name listed the
|
|
-- first name (found by the spec-blind store suite, 2026-10-13). Existing
|
|
-- rows take the blob's; a database whose filings differed then fails
|
|
-- verify's table check and is rebuilt from the journal.
|
|
ALTER TABLE attachments ADD COLUMN file_name TEXT;
|
|
ALTER TABLE attachments ADD COLUMN mime_type TEXT;
|
|
UPDATE attachments SET
|
|
file_name = (SELECT original_name FROM blobs WHERE sha256 = attachments.blob_sha),
|
|
mime_type = (SELECT mime_type FROM blobs WHERE sha256 = attachments.blob_sha);
|
|
"#,
|
|
r#"
|
|
-- v9: asset tags, serial numbers, and each asset's acquisition entry. assets.tag is the current tag;
|
|
-- asset_tags_log keeps every tagging in journal order, because a tag is
|
|
-- never reused by another asset even after a retag.
|
|
ALTER TABLE assets ADD COLUMN tag TEXT;
|
|
ALTER TABLE assets ADD COLUMN serial TEXT;
|
|
ALTER TABLE assets ADD COLUMN acquisition_entry INTEGER;
|
|
CREATE TABLE IF NOT EXISTS asset_tags_log(
|
|
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
asset_id INTEGER NOT NULL,
|
|
tag TEXT NOT NULL
|
|
);
|
|
"#,
|
|
r#"
|
|
-- v10: each asset's federal tax basis: the rule (cost, carryover,
|
|
-- personal_use_conversion) and the contributing member's adjusted basis
|
|
-- (TEXT, exact decimal, never REAL).
|
|
ALTER TABLE assets ADD COLUMN tax_rule TEXT;
|
|
ALTER TABLE assets ADD COLUMN contributor_basis TEXT;
|
|
"#,
|
|
r#"
|
|
-- v11: attachments link to any subject (docs/parties-and-attachments.md):
|
|
-- entries beyond the first, accounts, assets, parties, invoices, bills,
|
|
-- periods, payroll runs, schedules, reconciliations, the company. Bank and
|
|
-- card statements carry their account, period, and balances; a
|
|
-- reconciliation records the statement, its balance, and the lines it
|
|
-- cleared. Amounts are TEXT, exact decimal.
|
|
CREATE TABLE IF NOT EXISTS attachment_links(
|
|
attachment_id INTEGER NOT NULL,
|
|
subject_kind TEXT NOT NULL,
|
|
subject_id INTEGER NOT NULL,
|
|
PRIMARY KEY(attachment_id, subject_kind, subject_id)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_attachment_links_subject ON attachment_links(subject_kind, subject_id);
|
|
-- Payroll lines name their employee party (Ledger PayrollLine.employee).
|
|
ALTER TABLE payroll_lines ADD COLUMN employee_id INTEGER;
|
|
CREATE TABLE IF NOT EXISTS statements(
|
|
attachment_id INTEGER PRIMARY KEY,
|
|
account_id INTEGER NOT NULL,
|
|
period_start TEXT NOT NULL,
|
|
period_end TEXT NOT NULL,
|
|
opening_balance TEXT NOT NULL,
|
|
closing_balance TEXT NOT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS reconciliations(
|
|
id INTEGER PRIMARY KEY,
|
|
account_id INTEGER NOT NULL,
|
|
statement_date TEXT NOT NULL,
|
|
statement_balance TEXT NOT NULL,
|
|
attachment_id INTEGER NOT NULL,
|
|
voided_reason TEXT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS reconciliation_lines(
|
|
reconciliation_id INTEGER NOT NULL,
|
|
entry_id INTEGER NOT NULL,
|
|
line_index INTEGER NOT NULL,
|
|
PRIMARY KEY(reconciliation_id, entry_id, line_index)
|
|
);
|
|
"#,
|
|
r#"
|
|
-- v12: the books' own record of how they are kept (journal format 2,
|
|
-- plan/served.md 5.2): settings in force (each value as canonical JSON,
|
|
-- with the seq and source that set it), conventions (the current
|
|
-- revision of each, and its retirement), decisions, open items, and
|
|
-- approvals of draft entries.
|
|
CREATE TABLE IF NOT EXISTS settings(
|
|
key TEXT PRIMARY KEY,
|
|
value_json TEXT NOT NULL,
|
|
seq INTEGER NOT NULL,
|
|
source_json TEXT NOT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS conventions(
|
|
id TEXT PRIMARY KEY,
|
|
kind TEXT NOT NULL,
|
|
title TEXT NOT NULL,
|
|
body_md TEXT NOT NULL,
|
|
structured_json TEXT NULL,
|
|
source_json TEXT NOT NULL,
|
|
seq INTEGER NOT NULL,
|
|
revision INTEGER NOT NULL,
|
|
retired_reason TEXT NULL,
|
|
retired_source_json TEXT NULL,
|
|
retired_seq INTEGER NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS decisions(
|
|
id INTEGER PRIMARY KEY,
|
|
subject TEXT NOT NULL,
|
|
decision_md TEXT NOT NULL,
|
|
decided_by_json TEXT NOT NULL,
|
|
decided_on TEXT NOT NULL,
|
|
evidence_attachment INTEGER NULL,
|
|
supersedes INTEGER NULL,
|
|
citation TEXT NULL,
|
|
seq INTEGER NOT NULL,
|
|
superseded_by INTEGER NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS open_items(
|
|
id INTEGER PRIMARY KEY,
|
|
title TEXT NOT NULL,
|
|
detail_md TEXT NULL,
|
|
due TEXT NULL,
|
|
source_json TEXT NOT NULL,
|
|
seq INTEGER NOT NULL,
|
|
resolution TEXT NULL,
|
|
resolved_source_json TEXT NULL,
|
|
resolved_seq INTEGER NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS entry_approvals(
|
|
seq INTEGER PRIMARY KEY,
|
|
entry_id INTEGER NOT NULL,
|
|
approver TEXT NOT NULL,
|
|
fingerprint TEXT NOT NULL,
|
|
note TEXT NULL,
|
|
source_json TEXT NOT NULL
|
|
);
|
|
"#,
|
|
r#"
|
|
-- v13: 1099 decisions and payment methods (plan P3, defects 12, 18 and
|
|
-- 23; D-40). Each 1099 decision recorded with who decided (journal
|
|
-- feature vendor_1099_decisions), line by line: the date it takes effect
|
|
-- (NULL: from the beginning), nec, misc, not_reportable or NULL
|
|
-- (undetermined), and for not_reportable its kind and reason. A decision
|
|
-- from the beginning also sets vendors.form_1099, as every decision
|
|
-- written before these did. Payment methods (feature payment_instruments)
|
|
-- and the method each entry was paid with. Rows only come from lines
|
|
-- written under the features, so books written before them project the
|
|
-- same as before.
|
|
CREATE TABLE IF NOT EXISTS vendor_1099_decisions(
|
|
seq INTEGER PRIMARY KEY,
|
|
vendor_id INTEGER NOT NULL,
|
|
effective TEXT NULL,
|
|
form_1099 TEXT NULL,
|
|
reason_kind TEXT NULL,
|
|
reason TEXT NULL,
|
|
source_json TEXT NOT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS instruments(
|
|
id INTEGER PRIMARY KEY,
|
|
kind TEXT NOT NULL,
|
|
label TEXT NOT NULL,
|
|
last4 TEXT NULL,
|
|
owner_party INTEGER NULL,
|
|
account_id INTEGER NOT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS entry_instruments(
|
|
entry_id INTEGER PRIMARY KEY,
|
|
instrument_id INTEGER NOT NULL
|
|
);
|
|
"#,
|
|
r#"
|
|
-- v14: assets' basis facts and their depreciation runs keyed by entry
|
|
-- (plan P3, defect 13; D-41). Its own migration: v13 shipped on main
|
|
-- before these columns, so a v13 file already built never gains them by
|
|
-- an edit to v13 (each schema's file is rebuilt from the journal, and a
|
|
-- migration's text never changes once shipped).
|
|
--
|
|
-- An asset's basis facts beyond the rule and contributor basis
|
|
-- (asset.basis_set): the fair market value at contribution (TEXT, exact
|
|
-- decimal), the §704(c) method, and who gave the facts (JSON). tax_rule
|
|
-- keeps the rule as journaled, by its pre-P3 name or its wire name.
|
|
-- Depreciation runs keyed by their entry too: the journal can hold a
|
|
-- second run for a month whose first was reversed, and the projection
|
|
-- keeps every run, as replay does.
|
|
ALTER TABLE assets ADD COLUMN fmv_at_contribution TEXT;
|
|
ALTER TABLE assets ADD COLUMN sec704c_method TEXT;
|
|
ALTER TABLE assets ADD COLUMN basis_source_json TEXT;
|
|
CREATE TABLE asset_depreciation_v13(
|
|
asset_id INTEGER NOT NULL,
|
|
period TEXT NOT NULL,
|
|
entry_id INTEGER NOT NULL,
|
|
amount TEXT NOT NULL,
|
|
PRIMARY KEY (asset_id, period, entry_id)
|
|
);
|
|
INSERT INTO asset_depreciation_v13(asset_id, period, entry_id, amount)
|
|
SELECT asset_id, period, entry_id, amount FROM asset_depreciation;
|
|
DROP TABLE asset_depreciation;
|
|
ALTER TABLE asset_depreciation_v13 RENAME TO asset_depreciation;
|
|
"#,
|
|
r#"
|
|
-- v15: a document keeps no figure of its own (plan P4b S1, D-45; rules.md
|
|
-- B-38): what an invoice or bill has been paid, and its status, are read
|
|
-- from the ledger lines tagged with it, as of any date, and a reversed
|
|
-- payment no longer counts. The columns that summed its payment events
|
|
-- (and so went on counting a reversed one) go.
|
|
ALTER TABLE invoices DROP COLUMN amount_paid;
|
|
ALTER TABLE invoices DROP COLUMN status;
|
|
ALTER TABLE bills DROP COLUMN amount_paid;
|
|
ALTER TABLE bills DROP COLUMN status;
|
|
"#,
|
|
r#"
|
|
-- v16: a document's new fields (plan P4b S4.3, feature documents_v2): the
|
|
-- number it was issued under, its terms, the bill-to and seller snapshots,
|
|
-- the customer's reference and its memo; the bill's vendor reference, its
|
|
-- receipt date, terms and memo, and why a duplicate was entered anyway.
|
|
-- Terms and snapshots are canonical JSON TEXT; a document written before
|
|
-- this schema names none and its columns stay NULL. A line's account,
|
|
-- quantity and unit price ride its entry in lines_json, as its project
|
|
-- does.
|
|
ALTER TABLE invoices ADD COLUMN number TEXT;
|
|
ALTER TABLE invoices ADD COLUMN terms TEXT;
|
|
ALTER TABLE invoices ADD COLUMN bill_to TEXT;
|
|
ALTER TABLE invoices ADD COLUMN seller TEXT;
|
|
ALTER TABLE invoices ADD COLUMN customer_ref TEXT;
|
|
ALTER TABLE invoices ADD COLUMN memo TEXT;
|
|
ALTER TABLE bills ADD COLUMN vendor_ref TEXT;
|
|
ALTER TABLE bills ADD COLUMN received_on TEXT;
|
|
ALTER TABLE bills ADD COLUMN terms TEXT;
|
|
ALTER TABLE bills ADD COLUMN memo TEXT;
|
|
ALTER TABLE bills ADD COLUMN duplicate_reason TEXT;
|
|
"#,
|
|
r#"
|
|
-- v17: sent tracking (plan P4b S5, `invoice.sent`): one row per recorded
|
|
-- send of an invoice, in the order journaled. A send moves no figure and
|
|
-- no ledger line, so this table is only the projection's copy of the
|
|
-- journal's record (parity with replay). `on` and `by` are quoted: `on`
|
|
-- is an SQL keyword.
|
|
CREATE TABLE IF NOT EXISTS invoice_sends(
|
|
invoice_id INTEGER NOT NULL,
|
|
"on" TEXT NOT NULL,
|
|
"by" TEXT NOT NULL,
|
|
via TEXT NOT NULL,
|
|
to_json TEXT NOT NULL,
|
|
kind TEXT NOT NULL,
|
|
reference TEXT NULL,
|
|
note TEXT NULL
|
|
);
|
|
"#,
|
|
r#"
|
|
-- v18: evidence (plan P6, feature attachment_text): what was read out of
|
|
-- a file (`attachment.text_extracted`), the facts a human annotated it
|
|
-- with (`attachment.annotated`), and the duplicate an attachment was
|
|
-- withdrawn as. Extraction is its own record per (attachment, extractor,
|
|
-- version): an extractor upgrade adds one, never replaces one. A text
|
|
-- hash is the extraction's blob; the text itself is content-addressed in
|
|
-- the blob store. Amounts stay TEXT (exact decimal), never REAL.
|
|
CREATE TABLE IF NOT EXISTS extractions(
|
|
attachment_id INTEGER NOT NULL,
|
|
extractor TEXT NOT NULL,
|
|
version TEXT NOT NULL,
|
|
text_sha256 TEXT NULL,
|
|
pages INTEGER NOT NULL,
|
|
status TEXT NOT NULL,
|
|
seq INTEGER NOT NULL,
|
|
PRIMARY KEY(attachment_id, extractor, version)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_extractions_text ON extractions(text_sha256);
|
|
CREATE TABLE IF NOT EXISTS annotations(
|
|
attachment_id INTEGER PRIMARY KEY,
|
|
receipt_number INTEGER NULL,
|
|
payee TEXT NULL,
|
|
amount TEXT NULL,
|
|
date TEXT NULL,
|
|
instrument TEXT NULL,
|
|
category TEXT NULL,
|
|
source TEXT NULL,
|
|
reimbursable_to TEXT NULL,
|
|
note TEXT NULL,
|
|
seq INTEGER NOT NULL
|
|
);
|
|
ALTER TABLE attachments ADD COLUMN duplicate_of INTEGER NULL;
|
|
"#,
|
|
r#"
|
|
-- v19: draft states and posting order (plan P4, `drafts_v2`). Drafts are
|
|
-- not rows of `entries` (they never touch the projection until posted),
|
|
-- so a draft's state has a table of its own: one row per draft awaiting
|
|
-- its evidence (a draft being built is the default and has no row), gone
|
|
-- when the draft posts or is discarded. `posted_seq` is the journal line
|
|
-- (`entry.posted` or `entry.reversed`) that posted the entry: what the
|
|
-- digest's `since_seq` reads. Old rows are never migrated in place: the
|
|
-- database file is named for its schema, so a v19 file is built from the
|
|
-- journal.
|
|
ALTER TABLE entries ADD COLUMN posted_seq INTEGER NULL;
|
|
CREATE TABLE IF NOT EXISTS draft_states(
|
|
entry_id INTEGER PRIMARY KEY,
|
|
state TEXT NOT NULL,
|
|
note TEXT NULL
|
|
);
|
|
"#,
|
|
r#"
|
|
-- v20: audits and attestations (plan P8, feature `audits`). An audit run
|
|
-- is a row per `audit.run`: the date it covers, the figure head it was
|
|
-- made at, the hashes that make it reproducible and the filed workpaper.
|
|
-- An attestation is a row per `receipt.attested`, `entry.reviewed` or
|
|
-- `period.attested`; whether one counts is derived against
|
|
-- `policy.attestation_bar` when asked and never stored. The method is the
|
|
-- client's own claim, kept as canonical JSON. Amounts stay TEXT.
|
|
CREATE TABLE IF NOT EXISTS audits(
|
|
id INTEGER PRIMARY KEY,
|
|
through TEXT NOT NULL,
|
|
through_seq INTEGER NOT NULL,
|
|
scope TEXT NOT NULL,
|
|
program_version TEXT NOT NULL,
|
|
tb_hash TEXT NOT NULL,
|
|
findings_sha256 TEXT NOT NULL,
|
|
workpaper_attachment INTEGER NULL,
|
|
workpaper_pdf INTEGER NULL,
|
|
seq INTEGER NOT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS attestations(
|
|
seq INTEGER PRIMARY KEY,
|
|
kind TEXT NOT NULL,
|
|
attachment_id INTEGER NULL,
|
|
entry_id INTEGER NULL,
|
|
period_id INTEGER NULL,
|
|
audit_run_id INTEGER NULL,
|
|
by_who TEXT NOT NULL,
|
|
at_ts TEXT NOT NULL,
|
|
method TEXT NOT NULL,
|
|
amount_checked TEXT NULL,
|
|
date_checked TEXT NULL,
|
|
disagrees INTEGER NULL,
|
|
note TEXT NULL
|
|
);
|
|
"#,
|
|
];
|