Files
boc/aamos-ledger/schema.sql
T

159 lines
8.0 KiB
SQL
Raw Normal View History

-- ═══════════════════════════════════════════════════════════════════════════
-- AAMOS Ledger Engine — PostgreSQL Schema
-- Etapp 1 — Vertikal stängning
-- Principer: Audit First · Tenant Isolation · Deterministic · Reproducible
-- ═══════════════════════════════════════════════════════════════════════════
-- ── Chart of Accounts (Kontoplan) ───────────────────────────────────────────
-- Separerad från Rule Engine per arkitekturprincipen.
-- Stöder: BAS (SE), IFRS, US GAAP, lokala kontoplaner
CREATE TABLE IF NOT EXISTS ledger_accounts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id TEXT NOT NULL,
account_number TEXT NOT NULL, -- t.ex. '1910', '3000'
name TEXT NOT NULL,
account_type TEXT NOT NULL, -- 'asset'|'liability'|'equity'|'revenue'|'expense'
normal_balance TEXT NOT NULL, -- 'debit'|'credit'
coa_standard TEXT NOT NULL DEFAULT 'BAS', -- 'BAS'|'IFRS'|'USGAAP'|'custom'
parent_account TEXT, -- för kontostruktur/gruppering
vat_code TEXT, -- koppling till momsregel
is_active BOOLEAN NOT NULL DEFAULT TRUE,
metadata JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (tenant_id, account_number, coa_standard)
);
-- ── Journal Entries (Verifikationer) ────────────────────────────────────────
-- Ledgerns sanning. IMMUTABLE efter posted.
-- Alla fält för full spårbarhet per BYGGPLAN 0.001.
CREATE TABLE IF NOT EXISTS ledger_journal_entries (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id TEXT NOT NULL,
entry_number BIGINT, -- löpnummer per tenant+räkenskapsår
fiscal_year INTEGER NOT NULL, -- t.ex. 2026
period TEXT NOT NULL, -- 'YYYY-MM'
entry_date DATE NOT NULL,
description TEXT NOT NULL,
reference TEXT, -- externt ref (fakturanr, kvittonr)
source_type TEXT NOT NULL, -- 'manual'|'import_sie'|'import_csv'|'bank'|'system'
source_id TEXT, -- ID i källsystemet
status TEXT NOT NULL DEFAULT 'draft', -- 'draft'|'posted'|'voided'
void_reason TEXT,
voided_at TIMESTAMPTZ,
voided_by TEXT,
-- Observability / Trace
trace_id TEXT NOT NULL,
correlation_id TEXT NOT NULL,
user_id TEXT NOT NULL,
decision_source TEXT NOT NULL DEFAULT 'user', -- 'user'|'system'|'agent'
-- Metadata
metadata JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
posted_at TIMESTAMPTZ,
CONSTRAINT chk_status CHECK (status IN ('draft','posted','voided')),
CONSTRAINT chk_source CHECK (source_type IN ('manual','import_sie','import_csv','bank','system','agent'))
);
CREATE SEQUENCE IF NOT EXISTS ledger_entry_number_seq START 1;
-- ── Journal Lines (Verifikationsrader) ──────────────────────────────────────
-- Dubbelbokföring: sum(debit) == sum(credit) enforced i applikationskoden.
CREATE TABLE IF NOT EXISTS ledger_journal_lines (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
entry_id UUID NOT NULL REFERENCES ledger_journal_entries(id) ON DELETE CASCADE,
tenant_id TEXT NOT NULL,
line_number INTEGER NOT NULL, -- rad 1, 2, 3…
account_number TEXT NOT NULL,
account_name TEXT, -- denormaliserat för rapporter
debit NUMERIC(18,2),
credit NUMERIC(18,2),
currency TEXT NOT NULL DEFAULT 'SEK',
amount_base NUMERIC(18,2), -- belopp i basvaluta
vat_code TEXT, -- momsregel vid tidpunkten
vat_amount NUMERIC(18,2),
cost_center TEXT,
project_code TEXT,
description TEXT,
metadata JSONB NOT NULL DEFAULT '{}',
CONSTRAINT chk_debit_credit CHECK (
(debit IS NOT NULL AND credit IS NULL) OR
(debit IS NULL AND credit IS NOT NULL)
),
CONSTRAINT chk_positive_debit CHECK (debit IS NULL OR debit > 0),
CONSTRAINT chk_positive_credit CHECK (credit IS NULL OR credit > 0)
);
-- ── Audit Log ────────────────────────────────────────────────────────────────
-- Varje förändring i ledgern genererar en auditpost.
-- APPEND ONLY — aldrig DELETE eller UPDATE.
CREATE TABLE IF NOT EXISTS ledger_audit_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id TEXT NOT NULL,
trace_id TEXT NOT NULL,
correlation_id TEXT NOT NULL,
user_id TEXT NOT NULL,
entity_type TEXT NOT NULL, -- 'journal_entry'|'account'|'period'
entity_id TEXT NOT NULL,
action TEXT NOT NULL, -- 'created'|'posted'|'voided'|'imported'
decision_source TEXT NOT NULL,
before_state JSONB,
after_state JSONB,
ip_address TEXT,
session_id TEXT,
ts TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- ── Periods ──────────────────────────────────────────────────────────────────
-- Workflow-styrd periodstängning.
CREATE TABLE IF NOT EXISTS ledger_periods (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id TEXT NOT NULL,
fiscal_year INTEGER NOT NULL,
period TEXT NOT NULL, -- 'YYYY-MM'
status TEXT NOT NULL DEFAULT 'open', -- 'open'|'review'|'approved'|'closed'
opened_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
closed_at TIMESTAMPTZ,
closed_by TEXT,
workflow_id UUID, -- referens till workflow engine
trial_balance JSONB, -- snapshot vid stängning
trace_id TEXT NOT NULL,
metadata JSONB NOT NULL DEFAULT '{}',
UNIQUE (tenant_id, fiscal_year, period),
CONSTRAINT chk_period_status CHECK (status IN ('open','review','approved','closed'))
);
-- ── Indexes ───────────────────────────────────────────────────────────────────
CREATE INDEX IF NOT EXISTS idx_journal_tenant_period
ON ledger_journal_entries (tenant_id, fiscal_year, period);
CREATE INDEX IF NOT EXISTS idx_journal_status
ON ledger_journal_entries (tenant_id, status);
CREATE INDEX IF NOT EXISTS idx_journal_date
ON ledger_journal_entries (tenant_id, entry_date);
CREATE INDEX IF NOT EXISTS idx_journal_reference
ON ledger_journal_entries (tenant_id, reference);
CREATE INDEX IF NOT EXISTS idx_lines_entry
ON ledger_journal_lines (entry_id);
CREATE INDEX IF NOT EXISTS idx_lines_account
ON ledger_journal_lines (tenant_id, account_number);
CREATE INDEX IF NOT EXISTS idx_audit_entity
ON ledger_audit_log (tenant_id, entity_type, entity_id);
CREATE INDEX IF NOT EXISTS idx_audit_trace
ON ledger_audit_log (trace_id);
CREATE INDEX IF NOT EXISTS idx_accounts_tenant
ON ledger_accounts (tenant_id, account_number);
CREATE INDEX IF NOT EXISTS idx_periods_tenant
ON ledger_periods (tenant_id, fiscal_year, period);
-- ── BAS 2024 — Bas-konton för Wavult Group (seed) ───────────────────────────
-- Läggs in via seed-script, inte här. Schema är rent.