324 lines
9.7 KiB
SQL
324 lines
9.7 KiB
SQL
-- M0: Local household ledger skeleton.
|
|
-- SQLite-compatible baseline schema. The SQLAlchemy models are the runtime source for dev
|
|
-- create_all(); this file documents the first relational shape for migration tooling.
|
|
|
|
CREATE TABLE IF NOT EXISTS households (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
default_currency TEXT NOT NULL DEFAULT 'EUR',
|
|
created_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id TEXT PRIMARY KEY,
|
|
household_id TEXT NOT NULL REFERENCES households(id),
|
|
display_name TEXT NOT NULL,
|
|
email TEXT,
|
|
created_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS login_identities (
|
|
id TEXT PRIMARY KEY,
|
|
user_id TEXT NOT NULL REFERENCES users(id),
|
|
provider TEXT NOT NULL,
|
|
provider_subject TEXT NOT NULL,
|
|
email TEXT,
|
|
display_name TEXT,
|
|
created_at TEXT NOT NULL,
|
|
UNIQUE(provider, provider_subject)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS local_sessions (
|
|
id TEXT PRIMARY KEY,
|
|
user_id TEXT NOT NULL REFERENCES users(id),
|
|
household_id TEXT NOT NULL REFERENCES households(id),
|
|
token_hash TEXT NOT NULL UNIQUE,
|
|
created_at TEXT NOT NULL,
|
|
last_seen_at TEXT NOT NULL,
|
|
expires_at TEXT NOT NULL,
|
|
revoked_at TEXT
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS ix_local_session_token
|
|
ON local_sessions(token_hash);
|
|
CREATE INDEX IF NOT EXISTS ix_local_session_user
|
|
ON local_sessions(user_id, expires_at);
|
|
|
|
CREATE TABLE IF NOT EXISTS accounts (
|
|
id TEXT PRIMARY KEY,
|
|
household_id TEXT NOT NULL REFERENCES households(id),
|
|
owner_user_id TEXT REFERENCES users(id),
|
|
name TEXT NOT NULL,
|
|
account_type TEXT NOT NULL,
|
|
institution_name TEXT,
|
|
currency TEXT NOT NULL DEFAULT 'EUR',
|
|
visibility_mode TEXT NOT NULL,
|
|
is_active INTEGER NOT NULL DEFAULT 1,
|
|
created_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS raw_observations (
|
|
id TEXT PRIMARY KEY,
|
|
household_id TEXT NOT NULL REFERENCES households(id),
|
|
source_type TEXT NOT NULL,
|
|
source_account_id TEXT REFERENCES accounts(id),
|
|
owner_user_id TEXT REFERENCES users(id),
|
|
observed_at TEXT,
|
|
received_at TEXT NOT NULL,
|
|
raw_hash TEXT NOT NULL,
|
|
source_reference TEXT,
|
|
provider_transaction_id TEXT,
|
|
message_id TEXT,
|
|
raw_text TEXT,
|
|
raw_file_path TEXT,
|
|
raw_payload_json TEXT,
|
|
parsed_json TEXT,
|
|
parser_name TEXT,
|
|
parser_version TEXT,
|
|
confidence NUMERIC(14, 2) NOT NULL DEFAULT 1.00,
|
|
status TEXT NOT NULL,
|
|
created_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS ix_observation_dedupe
|
|
ON raw_observations(household_id, source_type, raw_hash);
|
|
CREATE INDEX IF NOT EXISTS ix_observation_external_refs
|
|
ON raw_observations(provider_transaction_id, message_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS categories (
|
|
id TEXT PRIMARY KEY,
|
|
household_id TEXT NOT NULL REFERENCES households(id),
|
|
parent_id TEXT REFERENCES categories(id),
|
|
name TEXT NOT NULL,
|
|
path TEXT NOT NULL,
|
|
is_active INTEGER NOT NULL DEFAULT 1
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS projects (
|
|
id TEXT PRIMARY KEY,
|
|
household_id TEXT NOT NULL REFERENCES households(id),
|
|
name TEXT NOT NULL,
|
|
description TEXT,
|
|
start_date TEXT,
|
|
end_date TEXT,
|
|
default_split_rule_id TEXT,
|
|
status TEXT NOT NULL,
|
|
currency TEXT NOT NULL DEFAULT 'EUR',
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS financial_events (
|
|
id TEXT PRIMARY KEY,
|
|
household_id TEXT NOT NULL REFERENCES households(id),
|
|
source_account_id TEXT REFERENCES accounts(id),
|
|
payment_account_id TEXT REFERENCES accounts(id),
|
|
payer_user_id TEXT REFERENCES users(id),
|
|
event_type TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
event_date TEXT NOT NULL,
|
|
booking_date TEXT,
|
|
due_date TEXT,
|
|
amount NUMERIC(14, 2) NOT NULL,
|
|
currency TEXT NOT NULL DEFAULT 'EUR',
|
|
merchant TEXT,
|
|
counterparty TEXT,
|
|
description TEXT,
|
|
category_id TEXT REFERENCES categories(id),
|
|
project_id TEXT REFERENCES projects(id),
|
|
created_by_user_id TEXT REFERENCES users(id),
|
|
fingerprint TEXT,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS ix_events_household_status_date
|
|
ON financial_events(household_id, status, event_date);
|
|
|
|
CREATE TABLE IF NOT EXISTS event_observation_links (
|
|
id TEXT PRIMARY KEY,
|
|
event_id TEXT NOT NULL REFERENCES financial_events(id),
|
|
observation_id TEXT NOT NULL REFERENCES raw_observations(id),
|
|
relation TEXT NOT NULL,
|
|
confidence NUMERIC(14, 2) NOT NULL DEFAULT 1.00,
|
|
created_at TEXT NOT NULL,
|
|
UNIQUE(event_id, observation_id, relation)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS event_relations (
|
|
id TEXT PRIMARY KEY,
|
|
from_event_id TEXT NOT NULL REFERENCES financial_events(id),
|
|
to_event_id TEXT NOT NULL REFERENCES financial_events(id),
|
|
relation_type TEXT NOT NULL,
|
|
confidence NUMERIC(14, 2) NOT NULL DEFAULT 1.00,
|
|
created_by TEXT NOT NULL DEFAULT 'system',
|
|
created_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS split_lines (
|
|
id TEXT PRIMARY KEY,
|
|
event_id TEXT NOT NULL REFERENCES financial_events(id),
|
|
user_id TEXT NOT NULL REFERENCES users(id),
|
|
share_amount NUMERIC(14, 2) NOT NULL,
|
|
share_percent NUMERIC(14, 2),
|
|
reason TEXT NOT NULL,
|
|
created_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS tags (
|
|
id TEXT PRIMARY KEY,
|
|
household_id TEXT NOT NULL REFERENCES households(id),
|
|
name TEXT NOT NULL,
|
|
UNIQUE(household_id, name)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS event_tags (
|
|
event_id TEXT NOT NULL REFERENCES financial_events(id),
|
|
tag_id TEXT NOT NULL REFERENCES tags(id),
|
|
PRIMARY KEY(event_id, tag_id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS project_participants (
|
|
id TEXT PRIMARY KEY,
|
|
project_id TEXT NOT NULL REFERENCES projects(id),
|
|
user_id TEXT NOT NULL REFERENCES users(id),
|
|
role TEXT NOT NULL,
|
|
default_share_percent NUMERIC(14, 2),
|
|
UNIQUE(project_id, user_id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS project_budget_lines (
|
|
id TEXT PRIMARY KEY,
|
|
project_id TEXT NOT NULL REFERENCES projects(id),
|
|
category_id TEXT REFERENCES categories(id),
|
|
name TEXT NOT NULL,
|
|
budgeted_amount NUMERIC(14, 2) NOT NULL DEFAULT 0.00,
|
|
committed_amount_cached NUMERIC(14, 2),
|
|
paid_amount_cached NUMERIC(14, 2),
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
notes TEXT
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS project_planned_items (
|
|
id TEXT PRIMARY KEY,
|
|
project_id TEXT NOT NULL REFERENCES projects(id),
|
|
budget_line_id TEXT REFERENCES project_budget_lines(id),
|
|
description TEXT NOT NULL,
|
|
estimated_amount NUMERIC(14, 2) NOT NULL DEFAULT 0.00,
|
|
committed_amount NUMERIC(14, 2),
|
|
expected_date TEXT,
|
|
due_date TEXT,
|
|
expected_payer_account_id TEXT REFERENCES accounts(id),
|
|
split_rule_id TEXT,
|
|
planning_status TEXT NOT NULL,
|
|
payment_status TEXT NOT NULL,
|
|
reconciliation_status TEXT NOT NULL,
|
|
settlement_status TEXT NOT NULL,
|
|
notes TEXT,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS payment_lines (
|
|
id TEXT PRIMARY KEY,
|
|
planned_item_id TEXT REFERENCES project_planned_items(id),
|
|
event_id TEXT REFERENCES financial_events(id),
|
|
amount NUMERIC(14, 2) NOT NULL,
|
|
currency TEXT NOT NULL DEFAULT 'EUR',
|
|
date TEXT NOT NULL,
|
|
paid_by_user_id TEXT REFERENCES users(id),
|
|
payment_account_id TEXT REFERENCES accounts(id),
|
|
source_observation_id TEXT REFERENCES raw_observations(id),
|
|
status TEXT NOT NULL,
|
|
created_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS recurring_commitments (
|
|
id TEXT PRIMARY KEY,
|
|
household_id TEXT NOT NULL REFERENCES households(id),
|
|
provider TEXT NOT NULL,
|
|
description TEXT,
|
|
amount_expected NUMERIC(14, 2) NOT NULL,
|
|
amount_min NUMERIC(14, 2),
|
|
amount_max NUMERIC(14, 2),
|
|
currency TEXT NOT NULL DEFAULT 'EUR',
|
|
interval TEXT NOT NULL,
|
|
next_due_date TEXT NOT NULL,
|
|
payer_account_id TEXT REFERENCES accounts(id),
|
|
split_rule_id TEXT,
|
|
category_id TEXT REFERENCES categories(id),
|
|
project_id TEXT REFERENCES projects(id),
|
|
notice_period TEXT,
|
|
renewal_date TEXT,
|
|
status TEXT NOT NULL,
|
|
matching_rule_json TEXT,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS promised_payments (
|
|
id TEXT PRIMARY KEY,
|
|
household_id TEXT NOT NULL REFERENCES households(id),
|
|
related_event_id TEXT REFERENCES financial_events(id),
|
|
provider TEXT NOT NULL,
|
|
amount NUMERIC(14, 2) NOT NULL,
|
|
currency TEXT NOT NULL DEFAULT 'EUR',
|
|
created_date TEXT NOT NULL,
|
|
due_date TEXT NOT NULL,
|
|
expected_funding_account_id TEXT REFERENCES accounts(id),
|
|
responsible_user_id TEXT REFERENCES users(id),
|
|
status TEXT NOT NULL,
|
|
source_observation_id TEXT REFERENCES raw_observations(id),
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS import_connections (
|
|
id TEXT PRIMARY KEY,
|
|
household_id TEXT NOT NULL REFERENCES households(id),
|
|
owner_user_id TEXT NOT NULL REFERENCES users(id),
|
|
connection_type TEXT NOT NULL,
|
|
display_name TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
last_sync_at TEXT,
|
|
last_error TEXT,
|
|
secret_storage_ref TEXT,
|
|
runs_locally INTEGER NOT NULL DEFAULT 1,
|
|
connector_config_json TEXT,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS import_runs (
|
|
id TEXT PRIMARY KEY,
|
|
household_id TEXT NOT NULL REFERENCES households(id),
|
|
connection_id TEXT REFERENCES import_connections(id),
|
|
actor_user_id TEXT REFERENCES users(id),
|
|
source_type TEXT,
|
|
status TEXT NOT NULL DEFAULT 'running',
|
|
imported_count INTEGER NOT NULL DEFAULT 0,
|
|
duplicate_count INTEGER NOT NULL DEFAULT 0,
|
|
created_event_count INTEGER NOT NULL DEFAULT 0,
|
|
skipped_count INTEGER NOT NULL DEFAULT 0,
|
|
error TEXT,
|
|
summary_json TEXT,
|
|
started_at TEXT NOT NULL,
|
|
finished_at TEXT
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS ix_import_runs_household_started
|
|
ON import_runs(household_id, started_at);
|
|
CREATE INDEX IF NOT EXISTS ix_import_runs_connection_started
|
|
ON import_runs(connection_id, started_at);
|
|
|
|
CREATE TABLE IF NOT EXISTS audit_log (
|
|
id TEXT PRIMARY KEY,
|
|
household_id TEXT NOT NULL REFERENCES households(id),
|
|
actor_user_id TEXT REFERENCES users(id),
|
|
action TEXT NOT NULL,
|
|
entity_type TEXT NOT NULL,
|
|
entity_id TEXT,
|
|
details_json TEXT,
|
|
created_at TEXT NOT NULL
|
|
);
|