-- ═══════════════════════════════════════════════════════════════════════════ -- AAMOS Admin v2 — Fullständigt databasschema -- CRM, Fakturering, Bokföring, Automation, Övervakning -- ═══════════════════════════════════════════════════════════════════════════ -- ── Customers (Kunder) ───────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS billing_customers ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), customer_type TEXT NOT NULL DEFAULT 'company', -- 'company'|'individual' org_nr TEXT, name TEXT NOT NULL, email TEXT, phone TEXT, address TEXT, postal_code TEXT, city TEXT, country TEXT DEFAULT 'SE', reference TEXT, notes TEXT, status TEXT NOT NULL DEFAULT 'active', -- 'active'|'inactive'|'prospect' payment_terms_days INTEGER DEFAULT 30, vat_number TEXT, -- Landvex Platform integration platform_customer_id TEXT, platform_synced_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_customers_status ON billing_customers (status); CREATE INDEX IF NOT EXISTS idx_customers_org_nr ON billing_customers (org_nr); -- ── Leads ────────────────────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS crm_leads ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, email TEXT, phone TEXT, company TEXT, source TEXT NOT NULL DEFAULT 'web', -- 'web'|'referral'|'cold_call'|'event'|'other' status TEXT NOT NULL DEFAULT 'new', -- 'new'|'contacted'|'qualified'|'converted'|'lost' notes TEXT, converted_at TIMESTAMPTZ, converted_to_customer_id UUID REFERENCES billing_customers(id), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_leads_status ON crm_leads (status); CREATE INDEX IF NOT EXISTS idx_leads_source ON crm_leads (source); CREATE UNIQUE INDEX IF NOT EXISTS idx_leads_email ON crm_leads (email); -- ── Agreements (Avtal) ───────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS billing_agreements ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), agreement_number TEXT NOT NULL UNIQUE, customer_id UUID NOT NULL REFERENCES billing_customers(id), agreement_type TEXT NOT NULL DEFAULT 'kommersiellt', -- 'kommunalt'|'kommersiellt'|'internt' status TEXT NOT NULL DEFAULT 'draft', -- 'draft'|'active'|'suspended'|'terminated'|'expired' title TEXT NOT NULL, description TEXT, start_date DATE NOT NULL, end_date DATE, auto_renew BOOLEAN NOT NULL DEFAULT FALSE, renewal_period_months INTEGER, billing_frequency TEXT NOT NULL DEFAULT 'monthly', -- 'monthly'|'quarterly'|'semi_annual'|'annual'|'one_time' billing_day INTEGER NOT NULL DEFAULT 1, payment_terms_days INTEGER NOT NULL DEFAULT 30, amount NUMERIC(18,2) NOT NULL DEFAULT 0, currency TEXT NOT NULL DEFAULT 'SEK', vat_rate NUMERIC(5,2) NOT NULL DEFAULT 25.0, next_invoice_date DATE, last_invoice_date DATE, -- Landvex Platform platform_agreement_id TEXT, platform_synced_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_agreements_customer ON billing_agreements (customer_id); CREATE INDEX IF NOT EXISTS idx_agreements_status ON billing_agreements (status); -- ── Invoices (Fakturor) ──────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS billing_invoices ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), invoice_number TEXT NOT NULL UNIQUE, customer_id UUID NOT NULL REFERENCES billing_customers(id), agreement_id UUID REFERENCES billing_agreements(id), invoice_type TEXT NOT NULL DEFAULT 'standard', -- 'standard'|'credit'|'proforma'|'reminder' status TEXT NOT NULL DEFAULT 'draft', -- 'draft'|'sent'|'paid'|'partially_paid'|'overdue'|'cancelled'|'credited' customer_name TEXT NOT NULL, customer_address TEXT, customer_postal TEXT, customer_city TEXT, customer_email TEXT, customer_reference TEXT, invoice_date DATE NOT NULL, due_date DATE NOT NULL, delivery_date DATE, sent_at TIMESTAMPTZ, paid_at TIMESTAMPTZ, credited_at TIMESTAMPTZ, currency TEXT NOT NULL DEFAULT 'SEK', subtotal NUMERIC(18,2) NOT NULL DEFAULT 0, vat_total NUMERIC(18,2) NOT NULL DEFAULT 0, total NUMERIC(18,2) NOT NULL DEFAULT 0, amount_paid NUMERIC(18,2) NOT NULL DEFAULT 0, amount_due NUMERIC(18,2) NOT NULL DEFAULT 0, vat_specification JSONB NOT NULL DEFAULT '{}', -- Ledger integration journal_entry_id UUID, ledger_posted_at TIMESTAMPTZ, reminder_count INTEGER NOT NULL DEFAULT 0, last_reminder_at TIMESTAMPTZ, reminder_fee NUMERIC(18,2) DEFAULT 0, interest_fee NUMERIC(18,2) DEFAULT 0, pdf_generated_at TIMESTAMPTZ, pdf_url TEXT, -- Landvex Platform platform_invoice_id TEXT, platform_synced_at TIMESTAMPTZ, notes TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_invoices_customer ON billing_invoices (customer_id); CREATE INDEX IF NOT EXISTS idx_invoices_status ON billing_invoices (status); CREATE INDEX IF NOT EXISTS idx_invoices_dates ON billing_invoices (invoice_date, due_date); CREATE INDEX IF NOT EXISTS idx_invoices_overdue ON billing_invoices (status, due_date) WHERE status IN ('sent', 'partially_paid'); -- ── Invoice Lines ────────────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS billing_invoice_lines ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), invoice_id UUID NOT NULL REFERENCES billing_invoices(id) ON DELETE CASCADE, line_number INTEGER NOT NULL, description TEXT NOT NULL, quantity NUMERIC(12,2) NOT NULL DEFAULT 1, unit TEXT, unit_price NUMERIC(18,2) NOT NULL, line_total NUMERIC(18,2) NOT NULL, vat_rate NUMERIC(5,2) NOT NULL, vat_amount NUMERIC(18,2) NOT NULL, line_total_incl_vat NUMERIC(18,2) NOT NULL, revenue_account TEXT NOT NULL DEFAULT '3000', cost_center TEXT, project_code TEXT, UNIQUE (invoice_id, line_number) ); -- ── Payments (Inbetalningar) ─────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS billing_payments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), invoice_id UUID NOT NULL REFERENCES billing_invoices(id), payment_date DATE NOT NULL, amount NUMERIC(18,2) NOT NULL, currency TEXT NOT NULL DEFAULT 'SEK', payment_method TEXT NOT NULL, -- 'bank_transfer'|'swish'|'card'|'cash'|'autogiro'|'other' payment_reference TEXT, bank_transaction_id TEXT, reconciliation_status TEXT NOT NULL DEFAULT 'unreconciled', -- 'unreconciled'|'matched'|'reconciled' reconciled_at TIMESTAMPTZ, journal_entry_id UUID, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_payments_invoice ON billing_payments (invoice_id); CREATE INDEX IF NOT EXISTS idx_payments_reconciliation ON billing_payments (reconciliation_status); -- ── Automations ──────────────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS automations ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, description TEXT, trigger_type TEXT NOT NULL, -- 'scheduled'|'event'|'manual' frequency TEXT, -- 'daily'|'weekly'|'monthly'|'quarterly' action TEXT NOT NULL, -- 'create_invoice'|'send_reminder'|'export_report'|'backup' action_config JSONB NOT NULL DEFAULT '{}', is_active BOOLEAN NOT NULL DEFAULT TRUE, last_run_at TIMESTAMPTZ, last_run_status TEXT, last_run_result JSONB, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- ── Automation Logs ──────────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS automation_logs ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), automation_id UUID NOT NULL REFERENCES automations(id), status TEXT NOT NULL, -- 'success'|'failed'|'running' result JSONB, executed_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- ── Settings ─────────────────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS settings ( id INTEGER PRIMARY KEY DEFAULT 1, company_name TEXT NOT NULL DEFAULT 'LandveX AB', org_nr TEXT DEFAULT '559141-7042', vat_nr TEXT DEFAULT 'SE559141704201', address TEXT, postal_code TEXT, city TEXT, payment_terms_days INTEGER DEFAULT 30, default_vat_rate NUMERIC(5,2) DEFAULT 25.0, bank_account TEXT, bankgiro TEXT, postgiro TEXT, swish_number TEXT, logo_url TEXT, email_from TEXT, email_reply_to TEXT, updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); INSERT INTO settings (id) VALUES (1) ON CONFLICT (id) DO NOTHING; -- ── Bank Transactions (for reconciliation) ───────────────────────────────── CREATE TABLE IF NOT EXISTS bank_transactions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), transaction_date DATE NOT NULL, description TEXT, amount NUMERIC(18,2) NOT NULL, currency TEXT NOT NULL DEFAULT 'SEK', reference TEXT, bank_account TEXT, matched_at TIMESTAMPTZ, matched_by TEXT, reconciled_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- ── Mail Messages ────────────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS mail_messages ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), from_email TEXT NOT NULL, from_name TEXT, to_email TEXT, subject TEXT, body TEXT, folder TEXT NOT NULL DEFAULT 'inbox', -- 'inbox'|'sent'|'archive'|'trash' status TEXT NOT NULL DEFAULT 'unread', -- 'unread'|'read'|'replied'|'archived' source TEXT, -- 'quixzoom_waitlist'|'landvex_contact'|'aamos_contact'|'manual'|'manual_reply' source_url TEXT, lead_id UUID REFERENCES crm_leads(id), in_reply_to UUID REFERENCES mail_messages(id), read_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_mail_folder ON mail_messages (folder); CREATE INDEX IF NOT EXISTS idx_mail_status ON mail_messages (status); CREATE INDEX IF NOT EXISTS idx_mail_source ON mail_messages (source); CREATE INDEX IF NOT EXISTS idx_mail_lead ON mail_messages (lead_id); CREATE INDEX IF NOT EXISTS idx_mail_created ON mail_messages (created_at DESC); -- ── Audit Log ────────────────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS audit_log ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id TEXT, entity_type TEXT NOT NULL, -- 'customer'|'invoice'|'payment'|'agreement'|'automation' entity_id TEXT NOT NULL, action TEXT NOT NULL, -- 'created'|'updated'|'deleted'|'sent'|'paid' before_state JSONB, after_state JSONB, ip_address TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- ── Landvex Platform Sync Queue ──────────────────────────────────────────── CREATE TABLE IF NOT EXISTS platform_sync_queue ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), entity_type TEXT NOT NULL, -- 'customer'|'agreement'|'invoice' entity_id UUID NOT NULL, action TEXT NOT NULL, -- 'create'|'update'|'delete' status TEXT NOT NULL DEFAULT 'pending', -- 'pending'|'processing'|'completed'|'failed' error_message TEXT, retry_count INTEGER DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), processed_at TIMESTAMPTZ );