BOC v1.0: RS256 auth, Ledger integration, Prometheus metrics, CI/CD, backup
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
-- BOC Ledger Schema
|
||||
-- Fullständigt bokföringsschema för LandveX AB
|
||||
-- BAS-kontoplan, verifikationer, moms, arbetsgivaravgift
|
||||
|
||||
-- Kontoplan (BAS-standard)
|
||||
CREATE TABLE IF NOT EXISTS boc_accounts (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
account_number TEXT NOT NULL, -- BAS-kontonummer, t.ex. 1930
|
||||
name TEXT NOT NULL,
|
||||
account_type TEXT NOT NULL, -- asset, liability, equity, income, expense
|
||||
vat_code TEXT, -- moms-kod, t.ex. 25, 12, 6
|
||||
parent_account TEXT, -- överordnat konto
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(tenant_id, account_number)
|
||||
);
|
||||
|
||||
-- Verifikationer (bokföringsposter)
|
||||
CREATE TABLE IF NOT EXISTS boc_vouchers (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
voucher_series TEXT NOT NULL DEFAULT 'A', -- verifikationsserie
|
||||
voucher_number INTEGER NOT NULL,
|
||||
date DATE NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
reference TEXT, -- fakturanummer, referens etc
|
||||
attachments JSONB DEFAULT '[]', -- bilagor
|
||||
created_by UUID REFERENCES boc_users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(tenant_id, voucher_series, voucher_number)
|
||||
);
|
||||
|
||||
-- Verifikationstransaktioner (dubbel bokföring)
|
||||
CREATE TABLE IF NOT EXISTS boc_voucher_lines (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
voucher_id UUID REFERENCES boc_vouchers(id) ON DELETE CASCADE,
|
||||
account_id UUID REFERENCES boc_accounts(id) ON DELETE RESTRICT,
|
||||
debit DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||
credit DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||
description TEXT,
|
||||
project TEXT, -- projektkod
|
||||
cost_center TEXT, -- kostnadsställe
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Saldon per konto och period
|
||||
CREATE TABLE IF NOT EXISTS boc_account_balances (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
account_id UUID REFERENCES boc_accounts(id) ON DELETE CASCADE,
|
||||
fiscal_year INTEGER NOT NULL,
|
||||
period INTEGER NOT NULL, -- 1-12 för månad, 0 för årssaldo
|
||||
opening_balance DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||
closing_balance DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||
total_debit DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||
total_credit DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(tenant_id, account_id, fiscal_year, period)
|
||||
);
|
||||
|
||||
-- Momsredovisning
|
||||
CREATE TABLE IF NOT EXISTS boc_vat_reports (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
period_start DATE NOT NULL,
|
||||
period_end DATE NOT NULL,
|
||||
vat_in DECIMAL(15,2) NOT NULL DEFAULT 0, -- ingående moms
|
||||
vat_out DECIMAL(15,2) NOT NULL DEFAULT 0, -- utgående moms
|
||||
vat_payable DECIMAL(15,2) NOT NULL DEFAULT 0, -- moms att betala
|
||||
status TEXT NOT NULL DEFAULT 'draft', -- draft, filed, paid
|
||||
filed_at TIMESTAMPTZ,
|
||||
paid_at TIMESTAMPTZ,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_by UUID REFERENCES boc_users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Moms-transaktioner per verifikation
|
||||
CREATE TABLE IF NOT EXISTS boc_vat_transactions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
voucher_line_id UUID REFERENCES boc_voucher_lines(id) ON DELETE CASCADE,
|
||||
vat_rate DECIMAL(5,2) NOT NULL, -- 25.00, 12.00, 6.00
|
||||
vat_amount DECIMAL(15,2) NOT NULL,
|
||||
base_amount DECIMAL(15,2) NOT NULL,
|
||||
vat_type TEXT NOT NULL, -- input, output
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Löner och arbetsgivaravgifter
|
||||
CREATE TABLE IF NOT EXISTS boc_payroll (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
employee_id UUID REFERENCES boc_employees(id),
|
||||
period DATE NOT NULL, -- löne månad
|
||||
gross_salary DECIMAL(15,2) NOT NULL,
|
||||
net_salary DECIMAL(15,2) NOT NULL,
|
||||
tax_deduction DECIMAL(15,2) NOT NULL DEFAULT 0, -- skatteavdrag
|
||||
pension_contribution DECIMAL(15,2) DEFAULT 0, -- pensionsinbetalning
|
||||
employer_contribution DECIMAL(15,2) DEFAULT 0, -- arbetsgivaravgift
|
||||
benefits JSONB DEFAULT '{}', -- förmåner
|
||||
deductions JSONB DEFAULT '{}', -- avdrag
|
||||
status TEXT NOT NULL DEFAULT 'draft',
|
||||
paid_at TIMESTAMPTZ,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_by UUID REFERENCES boc_users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Arbetsgivaravgiftsberäkning
|
||||
CREATE TABLE IF NOT EXISTS boc_employer_contributions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
period DATE NOT NULL,
|
||||
total_salary DECIMAL(15,2) NOT NULL,
|
||||
health_insurance DECIMAL(15,2) NOT NULL DEFAULT 0, -- sjukförsäkringsavgift
|
||||
pension_fee DECIMAL(15,2) NOT NULL DEFAULT 0, -- ålderspensionsavgift
|
||||
parental_fee DECIMAL(15,2) NOT NULL DEFAULT 0, -- föräldraförsäkringsavgift
|
||||
occupational_fee DECIMAL(15,2) NOT NULL DEFAULT 0, -- arbetsmarknadsförsäkringsavgift
|
||||
general_payroll_tax DECIMAL(15,2) NOT NULL DEFAULT 0, -- allmän löneavgift
|
||||
total_contribution DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||
status TEXT NOT NULL DEFAULT 'draft',
|
||||
paid_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Fakturor (kund och leverantör)
|
||||
CREATE TABLE IF NOT EXISTS boc_invoices_ledger (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
invoice_type TEXT NOT NULL, -- customer, supplier
|
||||
invoice_number TEXT NOT NULL,
|
||||
counterparty TEXT NOT NULL, -- kund/leverantör namn
|
||||
org_number TEXT,
|
||||
amount DECIMAL(15,2) NOT NULL,
|
||||
vat_amount DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||
total_amount DECIMAL(15,2) NOT NULL,
|
||||
currency TEXT NOT NULL DEFAULT 'SEK',
|
||||
issue_date DATE NOT NULL,
|
||||
due_date DATE NOT NULL,
|
||||
paid_date DATE,
|
||||
paid_amount DECIMAL(15,2) DEFAULT 0,
|
||||
status TEXT NOT NULL DEFAULT 'draft', -- draft, sent, paid, overdue, cancelled
|
||||
voucher_id UUID REFERENCES boc_vouchers(id),
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_by UUID REFERENCES boc_users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Räkenskapsår
|
||||
CREATE TABLE IF NOT EXISTS boc_fiscal_years (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
year INTEGER NOT NULL,
|
||||
start_date DATE NOT NULL,
|
||||
end_date DATE NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'open', -- open, closed, locked
|
||||
closed_at TIMESTAMPTZ,
|
||||
closed_by UUID REFERENCES boc_users(id),
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(tenant_id, year)
|
||||
);
|
||||
|
||||
-- Index
|
||||
CREATE INDEX IF NOT EXISTS idx_accounts_tenant ON boc_accounts(tenant_id, account_number);
|
||||
CREATE INDEX IF NOT EXISTS idx_vouchers_date ON boc_vouchers(tenant_id, date DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_voucher_lines_account ON boc_voucher_lines(account_id, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_balances_period ON boc_account_balances(tenant_id, fiscal_year, period);
|
||||
CREATE INDEX IF NOT EXISTS idx_vat_period ON boc_vat_reports(tenant_id, period_start, period_end);
|
||||
CREATE INDEX IF NOT EXISTS idx_payroll_period ON boc_payroll(tenant_id, period);
|
||||
CREATE INDEX IF NOT EXISTS idx_invoices_ledger ON boc_invoices_ledger(tenant_id, invoice_type, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_fiscal_years ON boc_fiscal_years(tenant_id, year);
|
||||
@@ -0,0 +1,280 @@
|
||||
-- Multi-Tenant Ledger Schema
|
||||
-- Supports SE (BAS/SIE4), US-DE (GAAP), US-TX (GAAP)
|
||||
|
||||
-- Company registry
|
||||
CREATE TABLE IF NOT EXISTS boc_companies (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
legal_name TEXT NOT NULL,
|
||||
org_number TEXT, -- Swedish org nr or US EIN
|
||||
tax_id TEXT, -- EIN for US companies
|
||||
jurisdiction TEXT NOT NULL, -- SE, US-DE, US-TX
|
||||
company_type TEXT NOT NULL, -- AB, Inc, LLC, etc
|
||||
address JSONB,
|
||||
currency TEXT NOT NULL DEFAULT 'SEK',
|
||||
fiscal_year_end DATE NOT NULL DEFAULT '12-31-2026',
|
||||
accounting_std TEXT NOT NULL DEFAULT 'BAS', -- BAS, GAAP, IFRS
|
||||
vat_registered BOOLEAN DEFAULT FALSE,
|
||||
vat_number TEXT,
|
||||
settings JSONB DEFAULT '{}',
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Chart of accounts per company (BAS for SE, GAAP for US)
|
||||
CREATE TABLE IF NOT EXISTS boc_chart_of_accounts (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
company_id UUID REFERENCES boc_companies(id) ON DELETE CASCADE,
|
||||
account_code TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
name_en TEXT, -- English name for US entities
|
||||
account_type TEXT NOT NULL, -- asset, liability, equity, revenue, expense
|
||||
account_subtype TEXT, -- current_asset, fixed_asset, current_liability, etc
|
||||
parent_code TEXT,
|
||||
vat_code TEXT, -- SE: 25, 12, 6, 0 | US: exempt
|
||||
is_bank_account BOOLEAN DEFAULT FALSE,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
sort_order INTEGER,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(company_id, account_code)
|
||||
);
|
||||
|
||||
-- Standard BAS kontoplan (Sweden)
|
||||
INSERT INTO boc_chart_of_accounts (company_id, account_code, name, name_en, account_type, account_subtype, vat_code, sort_order)
|
||||
SELECT
|
||||
c.id,
|
||||
a.account_code,
|
||||
a.name_sv,
|
||||
a.name_en,
|
||||
a.account_type,
|
||||
a.account_subtype,
|
||||
a.vat_code,
|
||||
a.sort_order
|
||||
FROM boc_companies c
|
||||
CROSS JOIN (VALUES
|
||||
('1000', 'Tillgångar', 'Assets', 'asset', 'header', NULL, 100),
|
||||
('1221', 'Datorer och kringutrustning', 'Computer equipment', 'asset', 'fixed_asset', NULL, 1221),
|
||||
('1227', 'Personbilar', 'Vehicles', 'asset', 'fixed_asset', NULL, 1227),
|
||||
('1930', 'Företagskonto / affärskonto', 'Business account', 'asset', 'current_asset', NULL, 1930),
|
||||
('2000', 'Skulder', 'Liabilities', 'liability', 'header', NULL, 2000),
|
||||
('2510', 'Skatteskulder', 'Tax liabilities', 'liability', 'current_liability', NULL, 2510),
|
||||
('2611', 'Utgående moms 25%', 'Output VAT 25%', 'liability', 'current_liability', '25', 2611),
|
||||
('2640', 'Ingående moms', 'Input VAT', 'asset', 'current_asset', NULL, 2640),
|
||||
('2990', 'Övriga skulder till närstående / ägaruttag', 'Related party debt / owner drawings', 'liability', 'current_liability', NULL, 2990),
|
||||
('3000', 'Intäkter', 'Revenue', 'revenue', 'header', NULL, 3000),
|
||||
('3001', 'Försäljning av tjänster', 'Service revenue', 'revenue', 'operating_revenue', '25', 3001),
|
||||
('3010', 'Konsultarvode', 'Consulting fees', 'revenue', 'operating_revenue', '25', 3010),
|
||||
('3020', 'Hyresintäkter', 'Rental income', 'revenue', 'operating_revenue', '25', 3020),
|
||||
('3900', 'Övriga rörelseintäkter', 'Other operating income', 'revenue', 'other_revenue', '25', 3900),
|
||||
('5000', 'Kostnader', 'Expenses', 'expense', 'header', NULL, 5000),
|
||||
('5420', 'Personalrepresentation', 'Staff entertainment', 'expense', 'operating_expense', NULL, 5420),
|
||||
('5460', 'Arbetskläder och skyddsmaterial', 'Work clothes and safety', 'expense', 'operating_expense', NULL, 5460),
|
||||
('5612', 'Fordonsskatt', 'Vehicle tax', 'expense', 'operating_expense', NULL, 5612),
|
||||
('5614', 'Bilförsäkring', 'Vehicle insurance', 'expense', 'operating_expense', NULL, 5614),
|
||||
('5810', 'Resekostnader', 'Travel expenses', 'expense', 'operating_expense', NULL, 5810),
|
||||
('5820', 'Biljettkostnader', 'Ticket expenses', 'expense', 'operating_expense', NULL, 5820),
|
||||
('5900', 'Reklam och marknadsföring', 'Advertising and marketing', 'expense', 'operating_expense', '25', 5900),
|
||||
('6071', 'Representation avdragsgill', 'Deductible entertainment', 'expense', 'operating_expense', NULL, 6071),
|
||||
('6540', 'IT-tjänster, köpta', 'IT services purchased', 'expense', 'operating_expense', '25', 6540),
|
||||
('6550', 'Programvarulicenser', 'Software licenses', 'expense', 'operating_expense', '25', 6550),
|
||||
('7630', 'Friskvård', 'Wellness', 'expense', 'personnel_expense', NULL, 7630),
|
||||
('8000', 'Finansiella poster', 'Financial items', 'expense', 'header', NULL, 8000),
|
||||
('8910', 'Skatt på årets resultat', 'Income tax', 'expense', 'tax_expense', NULL, 8910)
|
||||
) AS a(account_code, name_sv, name_en, account_type, account_subtype, vat_code, sort_order)
|
||||
WHERE c.jurisdiction = 'SE';
|
||||
|
||||
-- Standard GAAP chart (US)
|
||||
INSERT INTO boc_chart_of_accounts (company_id, account_code, name, name_en, account_type, account_subtype, sort_order)
|
||||
SELECT
|
||||
c.id,
|
||||
a.account_code,
|
||||
a.name_en,
|
||||
a.name_en,
|
||||
a.account_type,
|
||||
a.account_subtype,
|
||||
a.sort_order
|
||||
FROM boc_companies c
|
||||
CROSS JOIN (VALUES
|
||||
('1000', 'Assets', 'asset', 'header', 100),
|
||||
('1100', 'Cash and equivalents', 'asset', 'current_asset', 1100),
|
||||
('1200', 'Accounts receivable', 'asset', 'current_asset', 1200),
|
||||
('1500', 'Computer equipment', 'asset', 'fixed_asset', 1500),
|
||||
('1600', 'Vehicles', 'asset', 'fixed_asset', 1600),
|
||||
('2000', 'Liabilities', 'liability', 'header', 2000),
|
||||
('2100', 'Accounts payable', 'liability', 'current_liability', 2100),
|
||||
('2200', 'Accrued expenses', 'liability', 'current_liability', 2200),
|
||||
('2300', 'Taxes payable', 'liability', 'current_liability', 2300),
|
||||
('2500', 'Related party debt', 'liability', 'current_liability', 2500),
|
||||
('3000', 'Equity', 'equity', 'header', 3000),
|
||||
('3100', 'Common stock', 'equity', 'equity', 3100),
|
||||
('3200', 'Retained earnings', 'equity', 'equity', 3200),
|
||||
('3500', 'Owner drawings', 'equity', 'equity', 3500),
|
||||
('4000', 'Revenue', 'revenue', 'header', 4000),
|
||||
('4100', 'Service revenue', 'revenue', 'operating_revenue', 4100),
|
||||
('4200', 'Consulting revenue', 'revenue', 'operating_revenue', 4200),
|
||||
('4300', 'Rental income', 'revenue', 'operating_revenue', 4300),
|
||||
('4900', 'Other income', 'revenue', 'other_revenue', 4900),
|
||||
('5000', 'Expenses', 'expense', 'header', 5000),
|
||||
('5100', 'Advertising', 'expense', 'operating_expense', 5100),
|
||||
('5200', 'Travel and meals', 'expense', 'operating_expense', 5200),
|
||||
('5300', 'IT services', 'expense', 'operating_expense', 5300),
|
||||
('5400', 'Software licenses', 'expense', 'operating_expense', 5400),
|
||||
('5500', 'Vehicle expenses', 'expense', 'operating_expense', 5500),
|
||||
('5600', 'Insurance', 'expense', 'operating_expense', 5600),
|
||||
('5700', 'Professional fees', 'expense', 'operating_expense', 5700),
|
||||
('6000', 'Personnel', 'expense', 'header', 6000),
|
||||
('6100', 'Salaries and wages', 'expense', 'personnel_expense', 6100),
|
||||
('6200', 'Payroll taxes', 'expense', 'personnel_expense', 6200),
|
||||
('6300', 'Benefits', 'expense', 'personnel_expense', 6300),
|
||||
('7000', 'Taxes', 'expense', 'header', 7000),
|
||||
('7100', 'Federal income tax', 'expense', 'tax_expense', 7100),
|
||||
('7200', 'State income tax', 'expense', 'tax_expense', 7200)
|
||||
) AS a(account_code, name_en, account_type, account_subtype, sort_order)
|
||||
WHERE c.jurisdiction LIKE 'US-%';
|
||||
|
||||
-- Journal entries (universal, works for both BAS and GAAP)
|
||||
CREATE TABLE IF NOT EXISTS boc_journal_entries (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
company_id UUID REFERENCES boc_companies(id) ON DELETE CASCADE,
|
||||
entry_number TEXT NOT NULL,
|
||||
entry_date DATE NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
reference TEXT,
|
||||
source TEXT DEFAULT 'manual', -- manual, import, bank, payroll
|
||||
source_id UUID,
|
||||
attachments JSONB DEFAULT '[]',
|
||||
is_reversed BOOLEAN DEFAULT FALSE,
|
||||
reversed_by UUID REFERENCES boc_journal_entries(id),
|
||||
status TEXT NOT NULL DEFAULT 'posted', -- draft, posted, reversed
|
||||
posted_at TIMESTAMPTZ,
|
||||
posted_by UUID REFERENCES boc_users(id),
|
||||
created_by UUID REFERENCES boc_users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(company_id, entry_number)
|
||||
);
|
||||
|
||||
-- Journal entry lines
|
||||
CREATE TABLE IF NOT EXISTS boc_journal_lines (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
company_id UUID REFERENCES boc_companies(id) ON DELETE CASCADE,
|
||||
entry_id UUID REFERENCES boc_journal_entries(id) ON DELETE CASCADE,
|
||||
account_id UUID REFERENCES boc_chart_of_accounts(id) ON DELETE RESTRICT,
|
||||
debit DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||
credit DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||
description TEXT,
|
||||
project TEXT,
|
||||
department TEXT,
|
||||
vat_amount DECIMAL(15,2) DEFAULT 0,
|
||||
vat_rate DECIMAL(5,2) DEFAULT 0,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Account balances per period
|
||||
CREATE TABLE IF NOT EXISTS boc_period_balances (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
company_id UUID REFERENCES boc_companies(id) ON DELETE CASCADE,
|
||||
account_id UUID REFERENCES boc_chart_of_accounts(id) ON DELETE CASCADE,
|
||||
fiscal_year INTEGER NOT NULL,
|
||||
period INTEGER NOT NULL, -- 1-12 for month, 0 for year
|
||||
opening_balance DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||
closing_balance DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||
total_debit DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||
total_credit DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||
currency TEXT NOT NULL DEFAULT 'SEK',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(company_id, account_id, fiscal_year, period)
|
||||
);
|
||||
|
||||
-- VAT/GST tracking (SE: moms, US: sales tax if applicable)
|
||||
CREATE TABLE IF NOT EXISTS boc_tax_reports (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
company_id UUID REFERENCES boc_companies(id) ON DELETE CASCADE,
|
||||
tax_type TEXT NOT NULL, -- VAT, GST, SalesTax
|
||||
period_start DATE NOT NULL,
|
||||
period_end DATE NOT NULL,
|
||||
tax_in DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||
tax_out DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||
tax_payable DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||
status TEXT NOT NULL DEFAULT 'draft',
|
||||
filed_at TIMESTAMPTZ,
|
||||
filed_with TEXT,
|
||||
paid_at TIMESTAMPTZ,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_by UUID REFERENCES boc_users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Payroll (works for both SE and US)
|
||||
CREATE TABLE IF NOT EXISTS boc_payroll_entries (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
company_id UUID REFERENCES boc_companies(id) ON DELETE CASCADE,
|
||||
employee_id UUID REFERENCES boc_employees(id),
|
||||
period DATE NOT NULL,
|
||||
gross_pay DECIMAL(15,2) NOT NULL,
|
||||
net_pay DECIMAL(15,2) NOT NULL,
|
||||
-- SE specific
|
||||
tax_deduction DECIMAL(15,2) DEFAULT 0,
|
||||
pension_contribution DECIMAL(15,2) DEFAULT 0,
|
||||
-- US specific
|
||||
federal_tax DECIMAL(15,2) DEFAULT 0,
|
||||
state_tax DECIMAL(15,2) DEFAULT 0,
|
||||
social_security DECIMAL(15,2) DEFAULT 0,
|
||||
medicare DECIMAL(15,2) DEFAULT 0,
|
||||
-- Employer contributions
|
||||
employer_contribution DECIMAL(15,2) DEFAULT 0, -- SE: arbetsgivaravgift, US: FUTA + SUTA
|
||||
benefits JSONB DEFAULT '{}',
|
||||
status TEXT NOT NULL DEFAULT 'draft',
|
||||
paid_at TIMESTAMPTZ,
|
||||
journal_entry_id UUID REFERENCES boc_journal_entries(id),
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_by UUID REFERENCES boc_users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Intercompany transactions
|
||||
CREATE TABLE IF NOT EXISTS boc_intercompany (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
from_company_id UUID REFERENCES boc_companies(id) ON DELETE CASCADE,
|
||||
to_company_id UUID REFERENCES boc_companies(id) ON DELETE CASCADE,
|
||||
entry_id UUID REFERENCES boc_journal_entries(id),
|
||||
amount DECIMAL(15,2) NOT NULL,
|
||||
currency TEXT NOT NULL,
|
||||
description TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'open',
|
||||
reconciled_at TIMESTAMPTZ,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Currency exchange rates
|
||||
CREATE TABLE IF NOT EXISTS boc_exchange_rates (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
from_currency TEXT NOT NULL,
|
||||
to_currency TEXT NOT NULL,
|
||||
rate DECIMAL(15,6) NOT NULL,
|
||||
date DATE NOT NULL,
|
||||
source TEXT DEFAULT 'manual',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(from_currency, to_currency, date)
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_companies_tenant ON boc_companies(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_coa_company ON boc_chart_of_accounts(company_id, account_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_journal_company_date ON boc_journal_entries(company_id, entry_date DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_journal_lines_entry ON boc_journal_lines(entry_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_journal_lines_account ON boc_journal_lines(account_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_balances_company_period ON boc_period_balances(company_id, fiscal_year, period);
|
||||
CREATE INDEX IF NOT EXISTS idx_tax_reports_company ON boc_tax_reports(company_id, period_start, period_end);
|
||||
CREATE INDEX IF NOT EXISTS idx_payroll_company_period ON boc_payroll_entries(company_id, period);
|
||||
CREATE INDEX IF NOT EXISTS idx_intercompany_from ON boc_intercompany(from_company_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_intercompany_to ON boc_intercompany(to_company_id, status);
|
||||
@@ -0,0 +1,50 @@
|
||||
-- Seed the three companies
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
v_tenant_id UUID;
|
||||
BEGIN
|
||||
-- Get or create main tenant
|
||||
INSERT INTO boc_tenants (name, slug, domain, settings)
|
||||
VALUES ('LandveX Group', 'landvex-group', 'landvex.com', '{"group": true}')
|
||||
ON CONFLICT (slug) DO UPDATE SET name = 'LandveX Group'
|
||||
RETURNING id INTO v_tenant_id;
|
||||
|
||||
-- LandveX AB (Sweden)
|
||||
INSERT INTO boc_companies (
|
||||
tenant_id, name, legal_name, org_number, jurisdiction,
|
||||
company_type, currency, fiscal_year_end, accounting_std,
|
||||
vat_registered, vat_number, settings
|
||||
) VALUES (
|
||||
v_tenant_id, 'LandveX AB', 'LandveX Aktiebolag', '559141-7042', 'SE',
|
||||
'Aktiebolag', 'SEK', '2026-12-31', 'BAS',
|
||||
true, 'SE559141704201', '{"moms_period": "monthly", "arbetsgivaravgift": true}'
|
||||
)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- quiXzoom Inc (Delaware)
|
||||
INSERT INTO boc_companies (
|
||||
tenant_id, name, legal_name, tax_id, jurisdiction,
|
||||
company_type, currency, fiscal_year_end, accounting_std,
|
||||
vat_registered, settings
|
||||
) VALUES (
|
||||
v_tenant_id, 'quiXzoom Inc', 'quiXzoom Incorporated', null, 'US-DE',
|
||||
'C-Corp', 'USD', '2026-12-31', 'GAAP',
|
||||
false, '{"delaware_franchise_tax": true, "state": "DE"}'
|
||||
)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Landvex Inc (Texas)
|
||||
INSERT INTO boc_companies (
|
||||
tenant_id, name, legal_name, tax_id, jurisdiction,
|
||||
company_type, currency, fiscal_year_end, accounting_std,
|
||||
vat_registered, settings
|
||||
) VALUES (
|
||||
v_tenant_id, 'Landvex Inc', 'Landvex Incorporated', null, 'US-TX',
|
||||
'C-Corp', 'USD', '2026-12-31', 'GAAP',
|
||||
false, '{"texas_franchise_tax": true, "state": "TX"}'
|
||||
)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
RAISE NOTICE 'Three companies seeded successfully';
|
||||
END $$;
|
||||
@@ -0,0 +1,85 @@
|
||||
# BOC Bokföringsmigrering — LandveX AB FY2026
|
||||
|
||||
## Status
|
||||
|
||||
SIE4-fil hittad: `/opt/amos/data/landvex-fy2026-20260608.sie`
|
||||
- 22 konton (BAS-kontoplan)
|
||||
- 22 verifikationer (feb-apr 2026)
|
||||
- 58 transaktioner
|
||||
- IB/UB saldon för alla konton
|
||||
|
||||
## Datastruktur (från SIE4)
|
||||
|
||||
### Konton med saldon
|
||||
| Konto | Namn | IB | UB |
|
||||
|-------|------|-----|-----|
|
||||
| 1221 | Datorer och kringutrustning | 23,200 | 90,713 |
|
||||
| 1227 | Personbilar | 0 | 18,000 |
|
||||
| 1930 | Företagskonto | -6,450 | 686,560.66 |
|
||||
| 2510 | Skatteskulder | 0 | -123,441 |
|
||||
| 2611 | Utgående moms 25% | -18,000 | -247,370.05 |
|
||||
| 2990 | Ägaruttag | 20,000 | 58,991.26 |
|
||||
| 3000 | Försäljning tjänster | 0 | -753,147.20 |
|
||||
| 3010 | Konsultarvode | 0 | -125,000 |
|
||||
| 3020 | Hyresintäkter | -72,000 | -53,333 |
|
||||
| 3900 | Övriga rörelseintäkter | 0 | -58,000 |
|
||||
| 5420 | Personalrepresentation | 0 | ? |
|
||||
| 5460 | Arbetskläder | 0 | ? |
|
||||
| 5612 | Fordonsskatt | 0 | ? |
|
||||
| 5614 | Bilförsäkring | 0 | ? |
|
||||
| 5810 | Resekostnader | 0 | ? |
|
||||
| 5820 | Biljettkostnader | 0 | ? |
|
||||
| 5900 | Reklam/marknadsföring | 0 | ? |
|
||||
| 6071 | Representation avdragsgill | 0 | ? |
|
||||
| 6540 | IT-tjänster | 0 | ? |
|
||||
| 6550 | Programvarulicenser | 0 | ? |
|
||||
| 7630 | Friskvård | 0 | ? |
|
||||
| 8910 | Skatt på resultat | 0 | ? |
|
||||
|
||||
### Verifikationer att importera
|
||||
1. **A1** 2026-02-04 — Elles Fastighets AB (renovering)
|
||||
2. **A2** 2026-02-05 — Sony A7 + Sennheiser (marknadsutrustning)
|
||||
3. **A3** 2026-02-16 — Inköp personbil VW TPT119
|
||||
4. **A4** 2026-02-26 — Avyttring utrustning (Trygg Bil)
|
||||
5. **A5** 2026-02-28 — IT-kostnader dec-feb (Synthesia, Adobe, etc)
|
||||
6. **A6** 2026-03-10 — Marrakech MENA-resa
|
||||
7. **A7** 2026-03-13 — Svedea bilförsäkring
|
||||
8. **A8** 2026-03-16 — Thai Airways Stockholm-Bangkok
|
||||
9. **A9** 2026-03-27 — Airbnb Phuket/Alicante
|
||||
10. **A10** 2026-03-31 — Anthropic Claude API
|
||||
11. **A11** 2026-03-31 — Mjukvarulicenser (ElevenLabs, Loopia, etc)
|
||||
12. **A12** 2026-03-31 — Representation och friskvård
|
||||
13. **A13** 2026-04-07 — Trygg Bil Stockholm (hyra + konsult)
|
||||
14. **A14** 2026-04-09 — Svedea bilförsäkring
|
||||
15. **A15** 2026-04-13 — Funktionskläder Bangkok
|
||||
16. **A16** 2026-04-14 — Hotell affärsresor
|
||||
17. **A17** 2026-04-14 — Apple Store Bangkok (företagstelefon)
|
||||
18. **A18** 2026-04-14 — IT-kostnader (Lovable + Anthropic + OpenAI)
|
||||
19. **A19** 2026-04-15 — Fordonsskatt TPT119
|
||||
20. **A20** 2026-04-20 — Ägaruttag Diora Clinic
|
||||
21. **A21** 2026-04-22 — Ägaruttag oidentifierade poster
|
||||
22. **A22** 2026-04-30 — Beräknad bolagsskatt FY2025/26
|
||||
|
||||
## Momsberäkning
|
||||
- Utgående moms (2611): -247,370.05 SEK (att betala)
|
||||
- Ingående moms finns i verifikationerna
|
||||
- Momsrapport ska genereras per månad
|
||||
|
||||
## Arbetsgivaravgift
|
||||
- Inte synlig i SIE4-filen (ingen löneutbetalning än)
|
||||
- Ska beräknas när löner bokförs
|
||||
|
||||
## Nästa steg
|
||||
1. [ ] Kör migration 003_ledger_schema.sql
|
||||
2. [ ] Kör import_sie4.sql (konton)
|
||||
3. [ ] Skriv SIE4-parser i Go
|
||||
4. [ ] Importera alla verifikationer
|
||||
5. [ ] Beräkna och bokföra moms per månad
|
||||
6. [ ] Stäm av saldon mot UB i SIE4
|
||||
7. [ ] Generera rapporter (balans, resultat, moms)
|
||||
|
||||
## Viktigt
|
||||
- Alla belopp i SEK
|
||||
- Dubbel bokföring måste balansera
|
||||
- Moms ska beräknas korrekt per transaktion
|
||||
- Saldon ska stämma vid periodens slut
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"quixzoom_events": {
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"event_id": { "type": "keyword" },
|
||||
"event_type": { "type": "keyword" },
|
||||
"timestamp": { "type": "date" },
|
||||
"user_id": { "type": "keyword" },
|
||||
"user_email": { "type": "keyword" },
|
||||
"company_id": { "type": "keyword" },
|
||||
"job_id": { "type": "keyword" },
|
||||
"amount": { "type": "scaled_float", "scaling_factor": 100 },
|
||||
"currency": { "type": "keyword" },
|
||||
"status": { "type": "keyword" },
|
||||
"country": { "type": "keyword" },
|
||||
"ip_address": { "type": "ip" },
|
||||
"user_agent": { "type": "text" },
|
||||
"metadata": { "type": "object" },
|
||||
"risk_score": { "type": "float" },
|
||||
"anomaly_detected": { "type": "boolean" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"landvex_invoices": {
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"invoice_id": { "type": "keyword" },
|
||||
"invoice_number": { "type": "keyword" },
|
||||
"company_id": { "type": "keyword" },
|
||||
"customer_id": { "type": "keyword" },
|
||||
"contract_id": { "type": "keyword" },
|
||||
"amount": { "type": "scaled_float", "scaling_factor": 100 },
|
||||
"vat_amount": { "type": "scaled_float", "scaling_factor": 100 },
|
||||
"total_amount": { "type": "scaled_float", "scaling_factor": 100 },
|
||||
"currency": { "type": "keyword" },
|
||||
"status": { "type": "keyword" },
|
||||
"issue_date": { "type": "date" },
|
||||
"due_date": { "type": "date" },
|
||||
"paid_date": { "type": "date" },
|
||||
"plan_type": { "type": "keyword" },
|
||||
"period_start": { "type": "date" },
|
||||
"period_end": { "type": "date" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
-- Import SIE4 data for LandveX AB FY2026
|
||||
-- This script imports the existing bookkeeping data
|
||||
|
||||
-- Create tenant for LandveX AB
|
||||
INSERT INTO boc_tenants (name, slug, domain, settings)
|
||||
VALUES ('LandveX AB', 'landvex', 'landvex.com', '{"org_number": "559141-7042", "currency": "SEK"}')
|
||||
ON CONFLICT (slug) DO UPDATE SET name = 'LandveX AB';
|
||||
|
||||
-- Get tenant ID
|
||||
DO $$
|
||||
DECLARE
|
||||
v_tenant_id UUID;
|
||||
v_fiscal_year_id UUID;
|
||||
BEGIN
|
||||
SELECT id INTO v_tenant_id FROM boc_tenants WHERE slug = 'landvex';
|
||||
|
||||
-- Create fiscal year 2026
|
||||
INSERT INTO boc_fiscal_years (tenant_id, year, start_date, end_date)
|
||||
VALUES (v_tenant_id, 2026, '2026-01-01', '2026-12-31')
|
||||
ON CONFLICT (tenant_id, year) DO NOTHING;
|
||||
|
||||
SELECT id INTO v_fiscal_year_id FROM boc_fiscal_years WHERE tenant_id = v_tenant_id AND year = 2026;
|
||||
|
||||
-- Insert BAS accounts from SIE4
|
||||
INSERT INTO boc_accounts (tenant_id, account_number, name, account_type, vat_code) VALUES
|
||||
(v_tenant_id, '1221', 'Datorer och kringutrustning', 'asset', NULL),
|
||||
(v_tenant_id, '1227', 'Personbilar', 'asset', NULL),
|
||||
(v_tenant_id, '1930', 'Företagskonto / affärskonto', 'asset', NULL),
|
||||
(v_tenant_id, '2510', 'Skatteskulder', 'liability', NULL),
|
||||
(v_tenant_id, '2611', 'Utgående moms 25%', 'liability', '25'),
|
||||
(v_tenant_id, '2990', 'Övriga skulder till närstående / ägaruttag', 'liability', NULL),
|
||||
(v_tenant_id, '3000', 'Försäljning av tjänster', 'income', '25'),
|
||||
(v_tenant_id, '3010', 'Konsultarvode', 'income', '25'),
|
||||
(v_tenant_id, '3020', 'Hyresintäkter', 'income', '25'),
|
||||
(v_tenant_id, '3900', 'Övriga rörelseintäkter', 'income', '25'),
|
||||
(v_tenant_id, '5420', 'Personalrepresentation', 'expense', NULL),
|
||||
(v_tenant_id, '5460', 'Arbetskläder och skyddsmaterial', 'expense', NULL),
|
||||
(v_tenant_id, '5612', 'Fordonsskatt', 'expense', NULL),
|
||||
(v_tenant_id, '5614', 'Bilförsäkring', 'expense', NULL),
|
||||
(v_tenant_id, '5810', 'Resekostnader', 'expense', NULL),
|
||||
(v_tenant_id, '5820', 'Biljettkostnader', 'expense', NULL),
|
||||
(v_tenant_id, '5900', 'Reklam och marknadsföring', 'expense', NULL),
|
||||
(v_tenant_id, '6071', 'Representation avdragsgill', 'expense', NULL),
|
||||
(v_tenant_id, '6540', 'IT-tjänster, köpta', 'expense', '25'),
|
||||
(v_tenant_id, '6550', 'Programvarulicenser', 'expense', '25'),
|
||||
(v_tenant_id, '7630', 'Friskvård', 'expense', NULL),
|
||||
(v_tenant_id, '8910', 'Skatt på årets resultat', 'expense', NULL)
|
||||
ON CONFLICT (tenant_id, account_number) DO NOTHING;
|
||||
|
||||
RAISE NOTICE 'LandveX AB tenant and accounts created';
|
||||
END $$;
|
||||
Reference in New Issue
Block a user