159 lines
8.0 KiB
SQL
159 lines
8.0 KiB
SQL
|
|
-- ═══════════════════════════════════════════════════════════════════════════
|
||
|
|
-- 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.
|