feat: add TallyNote local reimbursement ledger
TallyNote release / linux-x64 (push) Failing after 2m41s
TallyNote release / linux-x64 (push) Failing after 2m41s
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
CREATE TABLE IF NOT EXISTS admins (
|
||||
id TEXT PRIMARY KEY,
|
||||
username TEXT NOT NULL,
|
||||
username_norm TEXT NOT NULL UNIQUE,
|
||||
display_name TEXT NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active','disabled')),
|
||||
must_change_password INTEGER NOT NULL DEFAULT 1 CHECK(must_change_password IN (0,1)),
|
||||
auth_version INTEGER NOT NULL DEFAULT 1 CHECK(auth_version >= 1),
|
||||
version INTEGER NOT NULL DEFAULT 1 CHECK(version >= 1),
|
||||
created_at INTEGER NOT NULL,
|
||||
created_by TEXT REFERENCES admins(id) ON DELETE RESTRICT,
|
||||
password_changed_at INTEGER,
|
||||
last_login_at INTEGER,
|
||||
disabled_at INTEGER,
|
||||
disabled_by TEXT REFERENCES admins(id) ON DELETE RESTRICT
|
||||
) STRICT;
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS admins_username_norm_uq ON admins(username_norm);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
token_hash TEXT PRIMARY KEY,
|
||||
admin_id TEXT NOT NULL REFERENCES admins(id) ON DELETE CASCADE,
|
||||
csrf_hash TEXT NOT NULL,
|
||||
auth_version INTEGER NOT NULL,
|
||||
created_at INTEGER NOT NULL,
|
||||
last_seen_at INTEGER NOT NULL,
|
||||
idle_expires_at INTEGER NOT NULL,
|
||||
absolute_expires_at INTEGER NOT NULL
|
||||
) STRICT;
|
||||
CREATE INDEX IF NOT EXISTS sessions_admin_idx ON sessions(admin_id);
|
||||
CREATE INDEX IF NOT EXISTS sessions_expiry_idx ON sessions(idle_expires_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS expenses (
|
||||
id TEXT PRIMARY KEY,
|
||||
paid_at INTEGER NOT NULL,
|
||||
amount_cents INTEGER NOT NULL CHECK(amount_cents > 0 AND amount_cents <= 999999999999),
|
||||
note TEXT NOT NULL DEFAULT '',
|
||||
status TEXT NOT NULL DEFAULT 'unreimbursed' CHECK(status IN ('unreimbursed','reimbursed')),
|
||||
version INTEGER NOT NULL DEFAULT 1 CHECK(version >= 1),
|
||||
created_at INTEGER NOT NULL,
|
||||
created_by TEXT NOT NULL REFERENCES admins(id) ON DELETE RESTRICT,
|
||||
updated_at INTEGER NOT NULL,
|
||||
updated_by TEXT NOT NULL REFERENCES admins(id) ON DELETE RESTRICT,
|
||||
reimbursed_at INTEGER,
|
||||
reimbursed_by TEXT REFERENCES admins(id) ON DELETE RESTRICT,
|
||||
deleted_at INTEGER,
|
||||
deleted_by TEXT REFERENCES admins(id) ON DELETE RESTRICT
|
||||
) STRICT;
|
||||
CREATE INDEX IF NOT EXISTS expenses_list_idx ON expenses(deleted_at, status, paid_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS attachments (
|
||||
id TEXT PRIMARY KEY,
|
||||
expense_id TEXT NOT NULL REFERENCES expenses(id) ON DELETE CASCADE,
|
||||
kind TEXT NOT NULL CHECK(kind IN ('payment_proof','invoice')),
|
||||
storage_path TEXT NOT NULL UNIQUE,
|
||||
original_name TEXT NOT NULL,
|
||||
mime_type TEXT NOT NULL,
|
||||
size_bytes INTEGER NOT NULL CHECK(size_bytes > 0),
|
||||
sha256 TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL,
|
||||
created_by TEXT NOT NULL REFERENCES admins(id) ON DELETE RESTRICT
|
||||
) STRICT;
|
||||
CREATE INDEX IF NOT EXISTS attachments_expense_idx ON attachments(expense_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS audit_events (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
occurred_at INTEGER NOT NULL,
|
||||
request_id TEXT NOT NULL,
|
||||
actor_admin_id TEXT,
|
||||
actor_username TEXT,
|
||||
action TEXT NOT NULL,
|
||||
target_type TEXT NOT NULL,
|
||||
target_id TEXT,
|
||||
outcome TEXT NOT NULL CHECK(outcome IN ('success','denied','failure')),
|
||||
before_json TEXT CHECK(before_json IS NULL OR json_valid(before_json)),
|
||||
after_json TEXT CHECK(after_json IS NULL OR json_valid(after_json)),
|
||||
metadata_json TEXT CHECK(metadata_json IS NULL OR json_valid(metadata_json))
|
||||
) STRICT;
|
||||
CREATE INDEX IF NOT EXISTS audit_time_idx ON audit_events(occurred_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS audit_target_idx ON audit_events(target_type, target_id, occurred_at DESC);
|
||||
CREATE TRIGGER IF NOT EXISTS audit_events_no_update BEFORE UPDATE ON audit_events
|
||||
BEGIN SELECT RAISE(ABORT, 'audit_events are append-only'); END;
|
||||
CREATE TRIGGER IF NOT EXISTS audit_events_no_delete BEFORE DELETE ON audit_events
|
||||
BEGIN SELECT RAISE(ABORT, 'audit_events are append-only'); END;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS system_settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
) STRICT;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS export_jobs (
|
||||
id TEXT PRIMARY KEY,
|
||||
admin_id TEXT NOT NULL REFERENCES admins(id) ON DELETE CASCADE,
|
||||
session_hash TEXT NOT NULL,
|
||||
status TEXT NOT NULL CHECK(status IN ('queued','building','ready','failed','expired')),
|
||||
selection_json TEXT NOT NULL CHECK(json_valid(selection_json)),
|
||||
snapshot_json TEXT NOT NULL CHECK(json_valid(snapshot_json)),
|
||||
file_path TEXT,
|
||||
file_name TEXT NOT NULL,
|
||||
size_bytes INTEGER,
|
||||
sha256 TEXT,
|
||||
error_message TEXT,
|
||||
created_at INTEGER NOT NULL,
|
||||
ready_at INTEGER,
|
||||
expires_at INTEGER NOT NULL
|
||||
) STRICT;
|
||||
CREATE INDEX IF NOT EXISTS exports_expiry_idx ON export_jobs(expires_at);
|
||||
CREATE INDEX IF NOT EXISTS exports_session_idx ON export_jobs(session_hash);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS login_attempts (
|
||||
key_hash TEXT PRIMARY KEY,
|
||||
window_start INTEGER NOT NULL,
|
||||
failures INTEGER NOT NULL,
|
||||
blocked_until INTEGER
|
||||
) STRICT;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS file_deletions (
|
||||
id TEXT PRIMARY KEY,
|
||||
storage_path TEXT NOT NULL,
|
||||
reason TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','complete','failed')),
|
||||
attempts INTEGER NOT NULL DEFAULT 0,
|
||||
last_error TEXT,
|
||||
created_at INTEGER NOT NULL,
|
||||
completed_at INTEGER
|
||||
) STRICT;
|
||||
CREATE INDEX IF NOT EXISTS file_deletions_status_idx ON file_deletions(status);
|
||||
Reference in New Issue
Block a user