feat: deliver schema 15 monthly settlement lifecycle
This commit is contained in:
Vendored
+289
@@ -0,0 +1,289 @@
|
||||
PRAGMA journal_mode=WAL;
|
||||
PRAGMA foreign_keys=ON;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS schema_meta (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS accounts (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
type TEXT NOT NULL DEFAULT 'bank',
|
||||
institution TEXT NOT NULL DEFAULT '',
|
||||
currency TEXT NOT NULL DEFAULT 'KRW',
|
||||
active INTEGER NOT NULL DEFAULT 1,
|
||||
memo TEXT NOT NULL DEFAULT '',
|
||||
account_number_masked TEXT NOT NULL DEFAULT '',
|
||||
holder_name TEXT NOT NULL DEFAULT '',
|
||||
last_balance INTEGER,
|
||||
last_balance_basis_date TEXT NOT NULL DEFAULT '',
|
||||
last_synced_at TEXT NOT NULL DEFAULT '',
|
||||
statement_status TEXT NOT NULL DEFAULT 'unknown',
|
||||
liquidity_bucket TEXT NOT NULL DEFAULT 'other',
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS transactions (
|
||||
id TEXT PRIMARY KEY,
|
||||
transaction_date TEXT NOT NULL,
|
||||
posted_date TEXT NOT NULL DEFAULT '',
|
||||
type TEXT NOT NULL,
|
||||
amount INTEGER NOT NULL,
|
||||
currency TEXT NOT NULL DEFAULT 'KRW',
|
||||
category TEXT NOT NULL DEFAULT '',
|
||||
subcategory TEXT NOT NULL DEFAULT '',
|
||||
merchant TEXT NOT NULL DEFAULT '',
|
||||
description TEXT NOT NULL DEFAULT '',
|
||||
account_id TEXT NOT NULL DEFAULT '',
|
||||
payment_method TEXT NOT NULL DEFAULT '',
|
||||
person TEXT NOT NULL DEFAULT '',
|
||||
project TEXT NOT NULL DEFAULT '',
|
||||
tags TEXT NOT NULL DEFAULT '[]',
|
||||
is_fixed INTEGER NOT NULL DEFAULT 0,
|
||||
source TEXT NOT NULL DEFAULT 'manual',
|
||||
import_batch_id TEXT NOT NULL DEFAULT '',
|
||||
external_id TEXT NOT NULL DEFAULT '',
|
||||
dedupe_key TEXT NOT NULL DEFAULT '',
|
||||
memo TEXT NOT NULL DEFAULT '',
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
transaction_datetime TEXT NOT NULL DEFAULT '',
|
||||
counterparty TEXT NOT NULL DEFAULT '',
|
||||
balance_after INTEGER,
|
||||
branch TEXT NOT NULL DEFAULT '',
|
||||
raw_summary TEXT NOT NULL DEFAULT '',
|
||||
raw_payload TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_transactions_date ON transactions(transaction_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_transactions_category ON transactions(category, subcategory);
|
||||
CREATE INDEX IF NOT EXISTS idx_transactions_person ON transactions(person);
|
||||
CREATE INDEX IF NOT EXISTS idx_transactions_account ON transactions(account_id);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_transactions_dedupe ON transactions(dedupe_key) WHERE dedupe_key <> '';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS budgets (
|
||||
id TEXT PRIMARY KEY,
|
||||
month TEXT NOT NULL,
|
||||
category TEXT NOT NULL,
|
||||
subcategory TEXT NOT NULL DEFAULT '',
|
||||
person TEXT NOT NULL DEFAULT '',
|
||||
amount INTEGER NOT NULL,
|
||||
currency TEXT NOT NULL DEFAULT 'KRW',
|
||||
memo TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_budgets_month ON budgets(month);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_budgets_unique ON budgets(month, category, subcategory, person);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS import_batches (
|
||||
id TEXT PRIMARY KEY,
|
||||
source TEXT NOT NULL,
|
||||
filename TEXT NOT NULL DEFAULT '',
|
||||
imported_at TEXT NOT NULL,
|
||||
row_count INTEGER NOT NULL DEFAULT 0,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
memo TEXT NOT NULL DEFAULT ''
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS category_rules (
|
||||
id TEXT PRIMARY KEY,
|
||||
priority INTEGER NOT NULL DEFAULT 100,
|
||||
match_field TEXT NOT NULL,
|
||||
match_type TEXT NOT NULL DEFAULT 'contains',
|
||||
pattern TEXT NOT NULL,
|
||||
amount_min INTEGER,
|
||||
amount_max INTEGER,
|
||||
day_of_month INTEGER,
|
||||
transaction_type TEXT NOT NULL DEFAULT '',
|
||||
category TEXT NOT NULL,
|
||||
subcategory TEXT NOT NULL DEFAULT '',
|
||||
person TEXT NOT NULL DEFAULT '',
|
||||
is_fixed INTEGER,
|
||||
confidence REAL NOT NULL DEFAULT 1.0,
|
||||
active INTEGER NOT NULL DEFAULT 1,
|
||||
memo TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_category_rules_active_priority ON category_rules(active, priority);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_category_rules_unique ON category_rules(match_field, match_type, pattern, transaction_type, category, subcategory, person);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ledger_categories (
|
||||
id TEXT PRIMARY KEY,
|
||||
category TEXT NOT NULL,
|
||||
subcategory TEXT NOT NULL DEFAULT '',
|
||||
cashflow_kind TEXT NOT NULL DEFAULT 'expense',
|
||||
active INTEGER NOT NULL DEFAULT 1,
|
||||
sort_order INTEGER NOT NULL DEFAULT 100,
|
||||
memo TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_ledger_categories_active_sort ON ledger_categories(active, sort_order, category, subcategory);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_ledger_categories_unique ON ledger_categories(category, subcategory);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS manual_assets (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
liquidity_bucket TEXT NOT NULL DEFAULT 'other',
|
||||
valuation_amount INTEGER,
|
||||
valuation_date TEXT NOT NULL DEFAULT '',
|
||||
currency TEXT NOT NULL DEFAULT 'KRW',
|
||||
source_note TEXT NOT NULL DEFAULT '',
|
||||
memo TEXT NOT NULL DEFAULT '',
|
||||
active INTEGER NOT NULL DEFAULT 1,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_manual_assets_active_bucket ON manual_assets(active, liquidity_bucket, name);
|
||||
CREATE INDEX IF NOT EXISTS idx_manual_assets_valuation_date ON manual_assets(valuation_date);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS asset_snapshots (
|
||||
id TEXT PRIMARY KEY,
|
||||
basis_date TEXT NOT NULL,
|
||||
source_type TEXT NOT NULL,
|
||||
source_id TEXT NOT NULL DEFAULT '',
|
||||
name TEXT NOT NULL,
|
||||
amount INTEGER,
|
||||
currency TEXT NOT NULL DEFAULT 'KRW',
|
||||
liquidity_bucket TEXT NOT NULL DEFAULT 'other',
|
||||
valuation_source TEXT NOT NULL DEFAULT '',
|
||||
source_note TEXT NOT NULL DEFAULT '',
|
||||
memo TEXT NOT NULL DEFAULT '',
|
||||
snapshot_run_id TEXT NOT NULL DEFAULT '',
|
||||
correction_of_run_id TEXT NOT NULL DEFAULT '',
|
||||
correction_reason TEXT NOT NULL DEFAULT '',
|
||||
is_correction INTEGER NOT NULL DEFAULT 0,
|
||||
closed_at TEXT NOT NULL DEFAULT '',
|
||||
valuation_id TEXT NOT NULL DEFAULT '',
|
||||
selection_policy TEXT NOT NULL DEFAULT 'exact',
|
||||
verification_status TEXT NOT NULL DEFAULT 'unverified',
|
||||
evidence_kind TEXT NOT NULL DEFAULT 'applied',
|
||||
exclusion_reason TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_asset_snapshots_basis_date ON asset_snapshots(basis_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_asset_snapshots_source ON asset_snapshots(source_type, source_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS asset_snapshot_runs (
|
||||
id TEXT PRIMARY KEY,
|
||||
basis_date TEXT NOT NULL,
|
||||
close_status TEXT NOT NULL,
|
||||
selection_policy TEXT NOT NULL DEFAULT 'exact',
|
||||
idempotency_key TEXT NOT NULL DEFAULT '',
|
||||
approved_by TEXT NOT NULL DEFAULT '',
|
||||
approved_at TEXT NOT NULL DEFAULT '',
|
||||
approval_reason TEXT NOT NULL DEFAULT '',
|
||||
approval_reference TEXT NOT NULL DEFAULT '',
|
||||
correction_of_run_id TEXT NOT NULL DEFAULT '',
|
||||
correction_reason TEXT NOT NULL DEFAULT '',
|
||||
applied_evidence TEXT NOT NULL DEFAULT '[]',
|
||||
excluded_evidence TEXT NOT NULL DEFAULT '[]',
|
||||
carried_evidence TEXT NOT NULL DEFAULT '[]',
|
||||
request_fingerprint TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL,
|
||||
settlement_month TEXT,
|
||||
currency TEXT,
|
||||
income_total INTEGER,
|
||||
expense_total INTEGER,
|
||||
net_cashflow INTEGER,
|
||||
total_assets INTEGER,
|
||||
total_liabilities INTEGER,
|
||||
net_worth INTEGER,
|
||||
cashflow_policy_json TEXT,
|
||||
aggregation_version TEXT,
|
||||
snapshot_schema_version INTEGER,
|
||||
source_snapshot_json TEXT
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_asset_snapshot_runs_idempotency ON asset_snapshot_runs(idempotency_key) WHERE idempotency_key <> '';
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_asset_snapshot_runs_initial_basis ON asset_snapshot_runs(basis_date) WHERE correction_of_run_id = '';
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_asset_snapshot_runs_correction_parent ON asset_snapshot_runs(correction_of_run_id) WHERE correction_of_run_id <> '';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS settlement_cashflow_snapshots (
|
||||
id TEXT PRIMARY KEY,
|
||||
settlement_run_id TEXT NOT NULL REFERENCES asset_snapshot_runs(id) ON DELETE RESTRICT,
|
||||
cashflow_kind TEXT NOT NULL CHECK (cashflow_kind IN ('income', 'expense', 'excluded')),
|
||||
category TEXT NOT NULL DEFAULT '',
|
||||
subcategory TEXT NOT NULL DEFAULT '',
|
||||
amount INTEGER NOT NULL CHECK (typeof(amount) = 'integer'),
|
||||
transaction_count INTEGER NOT NULL CHECK (typeof(transaction_count) = 'integer' AND transaction_count > 0),
|
||||
currency TEXT NOT NULL,
|
||||
UNIQUE (settlement_run_id, cashflow_kind, category, subcategory, currency)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_settlement_cashflow_snapshots_run ON settlement_cashflow_snapshots(settlement_run_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS review_decisions (
|
||||
id TEXT PRIMARY KEY,
|
||||
target_type TEXT NOT NULL,
|
||||
target_id TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
decision_type TEXT NOT NULL DEFAULT 'unknown',
|
||||
reason_code TEXT NOT NULL DEFAULT '',
|
||||
memo TEXT NOT NULL DEFAULT '',
|
||||
operator TEXT NOT NULL DEFAULT 'operator',
|
||||
decided_at TEXT NOT NULL DEFAULT '',
|
||||
review_after TEXT NOT NULL DEFAULT '',
|
||||
target_payload TEXT NOT NULL DEFAULT '{}',
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_review_decisions_target ON review_decisions(target_type, target_id, updated_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_review_decisions_status ON review_decisions(status, review_after, updated_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS reconciliation_sessions (
|
||||
id TEXT PRIMARY KEY,
|
||||
account_id TEXT NOT NULL,
|
||||
basis_date TEXT NOT NULL,
|
||||
expected_balance INTEGER,
|
||||
actual_balance INTEGER,
|
||||
difference INTEGER,
|
||||
status TEXT NOT NULL DEFAULT 'open',
|
||||
closed_at TEXT NOT NULL DEFAULT '',
|
||||
source_note TEXT NOT NULL DEFAULT '',
|
||||
memo TEXT NOT NULL DEFAULT '',
|
||||
operator TEXT NOT NULL DEFAULT 'operator',
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_reconciliation_sessions_account_basis ON reconciliation_sessions(account_id, basis_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_reconciliation_sessions_status ON reconciliation_sessions(status, closed_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS account_valuations (
|
||||
id TEXT PRIMARY KEY,
|
||||
account_id TEXT NOT NULL REFERENCES accounts(id),
|
||||
basis_date TEXT NOT NULL,
|
||||
amount INTEGER NOT NULL,
|
||||
currency TEXT NOT NULL DEFAULT 'KRW',
|
||||
valuation_kind TEXT NOT NULL DEFAULT 'balance',
|
||||
source_type TEXT NOT NULL DEFAULT 'manual',
|
||||
source_ref TEXT NOT NULL DEFAULT '',
|
||||
verification_status TEXT NOT NULL DEFAULT 'unverified',
|
||||
recorded_at TEXT NOT NULL,
|
||||
memo TEXT NOT NULL DEFAULT '',
|
||||
idempotency_key TEXT NOT NULL DEFAULT '',
|
||||
supersedes_id TEXT REFERENCES account_valuations(id),
|
||||
correction_reason TEXT NOT NULL DEFAULT '',
|
||||
as_of_at TEXT,
|
||||
timezone TEXT,
|
||||
cost_basis INTEGER,
|
||||
net_contributed INTEGER,
|
||||
reported_principal_kind TEXT,
|
||||
reported_principal_label TEXT,
|
||||
profit INTEGER,
|
||||
return_rate REAL,
|
||||
return_basis TEXT,
|
||||
principal INTEGER,
|
||||
interest INTEGER,
|
||||
fee INTEGER,
|
||||
representative_liability_definition TEXT
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_account_valuations_account_date ON account_valuations(account_id, basis_date, recorded_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_account_valuations_status_date ON account_valuations(verification_status, basis_date);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_account_valuations_idempotency ON account_valuations(account_id, idempotency_key) WHERE idempotency_key <> '';
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_account_valuations_single_correction ON account_valuations(supersedes_id) WHERE supersedes_id IS NOT NULL;
|
||||
|
||||
INSERT OR REPLACE INTO schema_meta(key, value) VALUES ('schema_version', '14');
|
||||
Reference in New Issue
Block a user