Files
boc/aamos-billing/DESIGN.md
T
Bernt 05ed037fe8 pilot.landvex.com: HTTPS + Full Stack Verified
- DNS: pilot.landvex.com -> 16.170.83.169
- TLS: Let's Encrypt certificate (expires 2026-09-30)
- Nginx: reverse proxy with SSL termination
- API: https://pilot.landvex.com/api/v1/missions
- UI: https://pilot.landvex.com/
- Upload: POST /api/v1/missions/import (multipart/form-data)

Verified:
 https://pilot.landvex.com/health
 https://pilot.landvex.com/version
 https://pilot.landvex.com/api/v1/missions (list)
 https://pilot.landvex.com/api/v1/missions/:id (get)
 POST /api/v1/missions/import (video upload)
 UI loads with title 'LandveX Intelligence Lab'

Next: Pilot 001 — Break the system!
2026-07-02 17:34:19 +00:00

57 KiB

aamos-billing — Designdokument

LandveX Enterprise Platform — Faktureringsmodul

Version: 1.0
Datum: 2026-07-02
Status: Design / för granskning
Ansvarig: LandveX Architecture Team
Beroenden: aamos-ledger (bokföringsmotor), Hermes Event Fabric


1. Översikt

Faktureringsmodulen (aamos-billing) hanterar kommunala fakturor, avtal, återkommande debitering och integration med externa ekonomisystem. Den är byggd som en självständig mikrotjänst som kommunicerar med ledgern via events (Hermes) och ett väldefinierat API.

1.1 Designprinciper (samma som ledger)

Princip Beskrivning
Audit First Varje operation genererar auditpost
Tenant Isolation tenant_id filtrerar all data, kunddata korsas aldrig
Hermes Events Ekonomiska händelser publiceras via event fabric
Immutable Journal Bokförda fakturor ändras aldrig; kreditering = ny post
Determinism Alla beslut reproducerbara och spårbara
No Direct Dependencies Inga hårda beroenden till andra moduler utöver Hermes

1.2 Modulens placering i plattformen

┌─────────────────────────────────────────────────────────────┐
│                    LandveX Enterprise                        │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │
│  │   Identity  │  │   Hermes    │  │   Ouroboros UI      │  │
│  │   Service   │  │   Events    │  │   (React/Vue)       │  │
│  └──────┬──────┘  └──────┬──────┘  └──────────┬──────────┘  │
│         │                │                    │              │
│  ┌──────┴────────────────┴────────────────────┴──────────┐  │
│  │              API Gateway / Reverse Proxy               │  │
│  └──────┬────────────────┬────────────────────┬──────────┘  │
│         │                │                    │              │
│  ┌──────▼──────┐  ┌──────▼──────┐  ┌─────────▼──────────┐  │
│  │ aamos-      │  │ aamos-      │  │ aamos-billing      │  │
│  │ ledger      │◄─┤ billing     │  │ (DENNA MODUL)      │  │
│  │ (3250)      │  │ (3260)      │  │ Port: 3260         │  │
│  │ Bokföring   │  │ Fakturering │  │                    │  │
│  └─────────────┘  └──────┬──────┘  └────────────────────┘  │
│                          │                                  │
│              ┌───────────┼───────────┐                      │
│              ▼           ▼           ▼                      │
│         ┌────────┐  ┌────────┐  ┌────────┐                 │
│         │ Visma  │  │Agresso │  │ SIE4   │                 │
│         │        │  │        │  │ Export │                 │
│         └────────┘  └────────┘  └────────┘                 │
└─────────────────────────────────────────────────────────────┘

1.3 Kommunikationsmönster

  • aamos-billing → aamos-ledger: Via Hermes events (billing.invoice.created → ledger skapar journalpost)
  • aamos-ledger → aamos-billing: Via Hermes events (finance.journal.posted → billing uppdaterar fakturastatus)
  • UI → aamos-billing: REST API (JSON)
  • aamos-billing → Externa system: Adapter-mönster (pluggbara exporters)

2. Datamodell

2.1 Entiteter — Översikt

┌─────────────────────┐     ┌─────────────────────┐     ┌─────────────────────┐
│   billing_agreements│     │  billing_invoices   │     │billing_invoice_lines│
│   (Avtal)           │◄────┤  (Fakturor)         │◄────┤  (Fakturarader)     │
└─────────────────────┘     └─────────────────────┘     └─────────────────────┘
         │                           │
         │                    ┌──────┴──────┐
         │                    ▼             ▼
         │           ┌─────────────┐  ┌─────────────┐
         │           │billing_payments│  │billing_reminders│
         │           │(Betalningar)  │  │(Påminnelser)    │
         │           └─────────────┘  └─────────────┘
         │
         ▼
┌─────────────────────┐
│billing_schedules    │
│(Schemaläggning)     │
└─────────────────────┘

2.2 Tabell: billing_agreements — Avtal

CREATE TABLE IF NOT EXISTS billing_agreements (
  id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id           TEXT NOT NULL,

  -- Avtalsidentitet
  agreement_number    TEXT NOT NULL,           -- t.ex. "KOM-2026-001"
  agreement_type      TEXT NOT NULL,           -- 'kommunalt'|'kommersiellt'|'internt'
  status              TEXT NOT NULL DEFAULT 'draft', -- 'draft'|'active'|'suspended'|'terminated'|'expired'

  -- Parter
  customer_type       TEXT NOT NULL,           -- 'municipality'|'company'|'individual'
  customer_org_nr     TEXT,                    -- org.nr för företag/kommun
  customer_name       TEXT NOT NULL,
  customer_address    TEXT,
  customer_postal     TEXT,
  customer_city       TEXT,
  customer_contact    TEXT,                    -- kontaktperson
  customer_email      TEXT,
  customer_phone      TEXT,
  customer_reference  TEXT,                    -- er referens (t.ex. "Projektledare Svensson")

  -- Avtalsvillkor
  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,               -- t.ex. 12 för årlig förlängning
  termination_notice_days INTEGER DEFAULT 90,  -- uppsägningstid i dagar

  -- Ekonomi
  currency            TEXT NOT NULL DEFAULT 'SEK',
  billing_frequency   TEXT NOT NULL,           -- 'monthly'|'quarterly'|'semi_annual'|'annual'|'one_time'
  billing_day         INTEGER NOT NULL DEFAULT 1, -- dag i månaden för fakturering
  payment_terms_days  INTEGER NOT NULL DEFAULT 30, -- nettodagar
  late_fee_percent    NUMERIC(5,2) DEFAULT 8.0, -- dröjsmålsränta (%)
  vat_rate            NUMERIC(5,2) NOT NULL DEFAULT 25.0,

  -- Bokföring
  revenue_account     TEXT NOT NULL DEFAULT '3000', -- intäktskonto
  receivable_account  TEXT NOT NULL DEFAULT '1510', -- kundfordringskonto
  vat_account         TEXT NOT NULL DEFAULT '2610', -- momskonto

  -- Integration
  external_system_id  TEXT,                    -- ID i externt system (Visma, Agresso)
  external_system     TEXT,                    -- 'visma'|'agresso'|'fortnox'|'custom'

  -- Metadata & spårbarhet
  metadata            JSONB NOT NULL DEFAULT '{}',
  created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  created_by          TEXT NOT NULL,
  updated_by          TEXT NOT NULL,

  -- Audit
  trace_id            TEXT NOT NULL,

  UNIQUE (tenant_id, agreement_number)
);

CREATE INDEX IF NOT EXISTS idx_agreements_tenant_status
  ON billing_agreements (tenant_id, status);
CREATE INDEX IF NOT EXISTS idx_agreements_customer
  ON billing_agreements (tenant_id, customer_org_nr);
CREATE INDEX IF NOT EXISTS idx_agreements_dates
  ON billing_agreements (tenant_id, start_date, end_date);

2.3 Tabell: billing_agreement_lines — Avtalsrader (återkommande poster)

CREATE TABLE IF NOT EXISTS billing_agreement_lines (
  id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  agreement_id        UUID NOT NULL REFERENCES billing_agreements(id) ON DELETE CASCADE,
  tenant_id           TEXT NOT NULL,

  line_number         INTEGER NOT NULL,        -- rad 1, 2, 3...
  description         TEXT NOT NULL,           -- t.ex. "Drift av VA-nät"
  quantity            NUMERIC(12,2) NOT NULL DEFAULT 1,
  unit                TEXT,                    -- 'st'|'tim'|'mån'|'år'|'m²'
  unit_price          NUMERIC(18,2) NOT NULL,  -- pris per enhet exkl. moms
  vat_rate            NUMERIC(5,2),            -- överstyrning per rad, NULL = arv från avtal
  discount_percent    NUMERIC(5,2) DEFAULT 0,  -- rabatt %
  discount_amount     NUMERIC(18,2),           -- fast rabattbelopp

  -- Bokföring per rad
  revenue_account     TEXT,                    -- NULL = arv från avtal
  cost_center         TEXT,
  project_code        TEXT,

  -- Aktivering
  active_from         DATE NOT NULL DEFAULT CURRENT_DATE,
  active_until        DATE,
  is_active           BOOLEAN NOT NULL DEFAULT TRUE,

  metadata            JSONB NOT NULL DEFAULT '{}',
  created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),

  UNIQUE (agreement_id, line_number)
);

CREATE INDEX IF NOT EXISTS idx_agreement_lines_agreement
  ON billing_agreement_lines (agreement_id);
CREATE INDEX IF NOT EXISTS idx_agreement_lines_active
  ON billing_agreement_lines (agreement_id, is_active, active_from, active_until);

2.4 Tabell: billing_invoices — Fakturor

CREATE TABLE IF NOT EXISTS billing_invoices (
  id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id           TEXT NOT NULL,

  -- Fakturaidentitet
  invoice_number      TEXT NOT NULL,           -- t.ex. "F-2026-00042"
  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'

  -- Kundinfo (denormaliserat — snapshot vid fakturering)
  customer_org_nr     TEXT,
  customer_name       TEXT NOT NULL,
  customer_address    TEXT,
  customer_postal     TEXT,
  customer_city       TEXT,
  customer_contact    TEXT,
  customer_email      TEXT,
  customer_reference  TEXT,

  -- Datum
  invoice_date        DATE NOT NULL,
  due_date            DATE NOT NULL,
  delivery_date       DATE,                    -- leveransdatum (momsregel)
  sent_at             TIMESTAMPTZ,
  paid_at             TIMESTAMPTZ,
  credited_at         TIMESTAMPTZ,

  -- Belopp
  currency            TEXT NOT NULL DEFAULT 'SEK',
  subtotal            NUMERIC(18,2) NOT NULL DEFAULT 0, -- netto
  vat_total           NUMERIC(18,2) NOT NULL DEFAULT 0,
  total               NUMERIC(18,2) NOT NULL DEFAULT 0, -- inkl. moms
  amount_paid         NUMERIC(18,2) NOT NULL DEFAULT 0,
  amount_due          NUMERIC(18,2) NOT NULL DEFAULT 0,

  -- Moms-specifikation (JSONB för flexibilitet)
  vat_specification   JSONB NOT NULL DEFAULT '[]',
  -- Exempel: [{"rate": 25.0, "base": 10000.00, "vat": 2500.00}, {"rate": 12.0, ...}]

  -- Bokföring
  journal_entry_id    UUID,                    -- referens till ledger_journal_entries
  ledger_posted_at    TIMESTAMPTZ,             -- när bokförd

  -- Kreditering
  credited_by_invoice_id UUID,                 -- referens till kreditfaktura
  original_invoice_id    UUID,                 -- om detta är en kreditfaktura

  -- Påminnelse
  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 / dokument
  pdf_generated_at    TIMESTAMPTZ,
  pdf_url             TEXT,                    -- S3/MinIO-länk

  -- Extern integration
  external_system_id  TEXT,                    -- ID i Visma/Agresso
  external_system     TEXT,
  exported_at         TIMESTAMPTZ,

  -- Metadata & spårbarhet
  metadata            JSONB NOT NULL DEFAULT '{}',
  notes               TEXT,                    -- interna anteckningar
  created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  created_by          TEXT NOT NULL,
  updated_by          TEXT NOT NULL,

  trace_id            TEXT NOT NULL,
  correlation_id      TEXT NOT NULL,

  UNIQUE (tenant_id, invoice_number)
);

CREATE INDEX IF NOT EXISTS idx_invoices_tenant_status
  ON billing_invoices (tenant_id, status);
CREATE INDEX IF NOT EXISTS idx_invoices_agreement
  ON billing_invoices (agreement_id);
CREATE INDEX IF NOT EXISTS idx_invoices_dates
  ON billing_invoices (tenant_id, invoice_date, due_date);
CREATE INDEX IF NOT EXISTS idx_invoices_overdue
  ON billing_invoices (tenant_id, status, due_date)
  WHERE status IN ('sent', 'partially_paid');
CREATE INDEX IF NOT EXISTS idx_invoices_journal
  ON billing_invoices (journal_entry_id);
CREATE INDEX IF NOT EXISTS idx_invoices_external
  ON billing_invoices (tenant_id, external_system, external_system_id);

2.5 Tabell: billing_invoice_lines — Fakturarader

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,
  tenant_id           TEXT NOT NULL,

  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,  -- quantity * unit_price (netto)

  vat_rate            NUMERIC(5,2) NOT NULL,
  vat_amount          NUMERIC(18,2) NOT NULL,
  line_total_incl_vat NUMERIC(18,2) NOT NULL,  -- line_total + vat_amount

  -- Koppling till avtalsrad
  agreement_line_id   UUID REFERENCES billing_agreement_lines(id),

  -- Bokföring
  revenue_account     TEXT NOT NULL,
  cost_center         TEXT,
  project_code        TEXT,

  metadata            JSONB NOT NULL DEFAULT '{}',

  UNIQUE (invoice_id, line_number)
);

CREATE INDEX IF NOT EXISTS idx_invoice_lines_invoice
  ON billing_invoice_lines (invoice_id);
CREATE INDEX IF NOT EXISTS idx_invoice_lines_account
  ON billing_invoice_lines (tenant_id, revenue_account);

2.6 Tabell: billing_payments — Inbetalningar

CREATE TABLE IF NOT EXISTS billing_payments (
  id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id           TEXT NOT NULL,

  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',

  -- Betalningskälla
  payment_method      TEXT NOT NULL,           -- 'bank_transfer'|'swish'|'card'|'cash'|'autogiro'|'other'
  payment_reference   TEXT,                    -- OCR-nummer, betalningsreferens
  bank_transaction_id TEXT,                    -- referens från banken

  -- Avstämningsstatus
  reconciliation_status TEXT NOT NULL DEFAULT 'unreconciled', -- 'unreconciled'|'matched'|'reconciled'
  reconciled_at       TIMESTAMPTZ,
  reconciled_by       TEXT,

  -- Bokföring
  journal_entry_id    UUID,                    -- referens till ledger

  metadata            JSONB NOT NULL DEFAULT '{}',
  created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  created_by          TEXT NOT NULL,
  trace_id            TEXT NOT NULL,

  UNIQUE (tenant_id, bank_transaction_id)
);

CREATE INDEX IF NOT EXISTS idx_payments_invoice
  ON billing_payments (invoice_id);
CREATE INDEX IF NOT EXISTS idx_payments_reconciliation
  ON billing_payments (tenant_id, reconciliation_status);
CREATE INDEX IF NOT EXISTS idx_payments_date
  ON billing_payments (tenant_id, payment_date);

2.7 Tabell: billing_reminders — Påminnelser / Inkasso

CREATE TABLE IF NOT EXISTS billing_reminders (
  id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id           TEXT NOT NULL,
  invoice_id          UUID NOT NULL REFERENCES billing_invoices(id),

  reminder_number     INTEGER NOT NULL,        -- 1, 2, 3...
  reminder_type       TEXT NOT NULL,           -- 'reminder'|'debt_collection'|'legal'
  sent_at             TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  due_date            DATE NOT NULL,

  -- Avgifter
  reminder_fee        NUMERIC(18,2) NOT NULL DEFAULT 0,
  interest_fee        NUMERIC(18,2) NOT NULL DEFAULT 0,
  total_demand        NUMERIC(18,2) NOT NULL,  -- fakturabelopp + avgifter

  -- Status
  status              TEXT NOT NULL DEFAULT 'sent', -- 'sent'|'paid'|'escalated'|'cancelled'

  -- Kommunikation
  sent_via            TEXT,                    -- 'email'|'post'|'sms'
  email_address       TEXT,
  pdf_url             TEXT,

  metadata            JSONB NOT NULL DEFAULT '{}',
  created_by          TEXT NOT NULL,
  trace_id            TEXT NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_reminders_invoice
  ON billing_reminders (invoice_id);
CREATE INDEX IF NOT EXISTS idx_reminders_status
  ON billing_reminders (tenant_id, status);

2.8 Tabell: billing_schedules — Schemaläggning (cron-jobb)

CREATE TABLE IF NOT EXISTS billing_schedules (
  id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id           TEXT NOT NULL,
  agreement_id        UUID NOT NULL REFERENCES billing_agreements(id) ON DELETE CASCADE,

  -- Schemaläggning
  schedule_type       TEXT NOT NULL,           -- 'recurring'|'one_time'
  frequency           TEXT NOT NULL,           -- 'monthly'|'quarterly'|'semi_annual'|'annual'
  day_of_month        INTEGER NOT NULL DEFAULT 1,
  next_run_date       DATE NOT NULL,
  last_run_date       DATE,
  last_invoice_id     UUID REFERENCES billing_invoices(id),

  -- Status
  is_active           BOOLEAN NOT NULL DEFAULT TRUE,
  paused_until        DATE,

  -- Resultat
  runs_count          INTEGER NOT NULL DEFAULT 0,
  failures_count      INTEGER NOT NULL DEFAULT 0,
  last_error          TEXT,

  metadata            JSONB NOT NULL DEFAULT '{}',
  created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),

  UNIQUE (tenant_id, agreement_id, schedule_type)
);

CREATE INDEX IF NOT EXISTS idx_schedules_next_run
  ON billing_schedules (next_run_date, is_active)
  WHERE is_active = TRUE;

2.9 Tabell: billing_audit_log — Audit trail

CREATE TABLE IF NOT EXISTS billing_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,           -- 'agreement'|'invoice'|'payment'|'reminder'
  entity_id           TEXT NOT NULL,
  action              TEXT NOT NULL,           -- 'created'|'updated'|'sent'|'paid'|'cancelled'|'credited'|'reminded'
  decision_source     TEXT NOT NULL DEFAULT 'user', -- 'user'|'system'|'agent'
  before_state        JSONB,
  after_state         JSONB,
  ip_address          TEXT,
  session_id          TEXT,
  ts                  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_billing_audit_entity
  ON billing_audit_log (tenant_id, entity_type, entity_id);
CREATE INDEX IF NOT EXISTS idx_billing_audit_trace
  ON billing_audit_log (trace_id);
CREATE INDEX IF NOT EXISTS idx_billing_audit_ts
  ON billing_audit_log (tenant_id, ts);

2.10 Tabell: billing_export_jobs — Export-jobb (SIE4, PDF, etc.)

CREATE TABLE IF NOT EXISTS billing_export_jobs (
  id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id           TEXT NOT NULL,

  job_type            TEXT NOT NULL,           -- 'sie4'|'pdf'|'csv'|'visma'|'agresso'
  job_status          TEXT NOT NULL DEFAULT 'pending', -- 'pending'|'running'|'completed'|'failed'

  -- Filter
  period_from         TEXT,                    -- 'YYYY-MM'
  period_to           TEXT,
  fiscal_year         INTEGER,
  invoice_ids         UUID[],                  -- specifika fakturor
  agreement_ids       UUID[],                  -- specifika avtal

  -- Resultat
  file_url            TEXT,                    -- S3/MinIO-länk
  file_size_bytes     INTEGER,
  record_count        INTEGER,
  error_message       TEXT,

  -- Metadata
  requested_by        TEXT NOT NULL,
  started_at          TIMESTAMPTZ,
  completed_at        TIMESTAMPTZ,
  created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  trace_id            TEXT NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_export_jobs_tenant
  ON billing_export_jobs (tenant_id, job_status);

3. API — REST Endpoints

3.1 Konventioner

  • Bas-URL: https://api.landvex.com/billing (port 3260 internt)
  • Auth: JWT via Authorization: Bearer <token>
  • Tenant: X-Tenant-ID: <tenant>
  • Trace: X-Trace-ID, X-Correlation-ID
  • Svar: { ok: true/false, data: ..., error: ..., meta: {...} }
  • Paginering: ?page=1&limit=50, svar inkluderar meta.total, meta.page, meta.pages

3.2 Avtal (Agreements)

GET    /api/billing/agreements
       Query: ?status=active&customer_org_nr=559141-7042&page=1&limit=50
       Response: { ok: true, agreements: [...], meta: { total: 120, page: 1, pages: 3 } }

GET    /api/billing/agreements/:id
       Response: { ok: true, agreement: {...}, lines: [...], schedules: [...] }

POST   /api/billing/agreements
       Body: { agreement_number, agreement_type, customer_type, customer_org_nr,
               customer_name, customer_address, ..., title, start_date, end_date,
               billing_frequency, billing_day, payment_terms_days, ... }
       Response: 201 { ok: true, agreement: {...} }

PUT    /api/billing/agreements/:id
       Body: { ...uppdaterbara fält... }
       Response: { ok: true, agreement: {...} }

POST   /api/billing/agreements/:id/activate
       Body: { activation_date }
       Response: { ok: true, agreement: { status: 'active' } }

POST   /api/billing/agreements/:id/terminate
       Body: { termination_date, reason }
       Response: { ok: true, agreement: { status: 'terminated' } }

POST   /api/billing/agreements/:id/suspend
       Body: { reason, resume_date }
       Response: { ok: true, agreement: { status: 'suspended' } }

POST   /api/billing/agreements/:id/resume
       Response: { ok: true, agreement: { status: 'active' } }

DELETE /api/billing/agreements/:id
       Response: { ok: true } (endast om status=draft och inga fakturor)

3.3 Avtalsrader (Agreement Lines)

GET    /api/billing/agreements/:id/lines
       Response: { ok: true, lines: [...] }

POST   /api/billing/agreements/:id/lines
       Body: { description, quantity, unit, unit_price, vat_rate,
               revenue_account, cost_center, project_code, ... }
       Response: 201 { ok: true, line: {...} }

PUT    /api/billing/agreements/:id/lines/:lineId
       Body: { ... }
       Response: { ok: true, line: {...} }

DELETE /api/billing/agreements/:id/lines/:lineId
       Response: { ok: true }

3.4 Fakturor (Invoices)

GET    /api/billing/invoices
       Query: ?status=sent&period=2026-07&customer_org_nr=...&overdue=true&page=1
       Response: { ok: true, invoices: [...], meta: {...} }

GET    /api/billing/invoices/:id
       Response: { ok: true, invoice: {...}, lines: [...], payments: [...], reminders: [...] }

POST   /api/billing/invoices
       Body: {
         invoice_type: 'standard',
         customer_org_nr, customer_name, customer_address, ...,
         invoice_date, due_date, delivery_date,
         lines: [
           { description, quantity, unit, unit_price, vat_rate,
             revenue_account, cost_center, project_code }
         ],
         notes
       }
       Response: 201 { ok: true, invoice: {...} }

PUT    /api/billing/invoices/:id
       Body: { ...uppdaterbara fält (endast draft)... }
       Response: { ok: true, invoice: {...} }

POST   /api/billing/invoices/:id/send
       Body: { send_via: 'email'|'post', email_address, cc_addresses: [] }
       Response: { ok: true, invoice: { status: 'sent', sent_at: ... } }

POST   /api/billing/invoices/:id/credit
       Body: { reason, credit_date, credit_lines: [...] }
       Response: { ok: true, credit_invoice: {...}, original_invoice: {...} }

POST   /api/billing/invoices/:id/cancel
       Body: { reason }
       Response: { ok: true, invoice: { status: 'cancelled' } }

POST   /api/billing/invoices/:id/remind
       Body: { reminder_type: 'reminder'|'debt_collection', reminder_fee, interest_fee }
       Response: { ok: true, reminder: {...}, invoice: {...} }

DELETE /api/billing/invoices/:id
       Response: { ok: true } (endast draft)

3.5 Betalningar (Payments)

GET    /api/billing/payments
       Query: ?invoice_id=...&reconciliation_status=unreconciled&page=1
       Response: { ok: true, payments: [...] }

POST   /api/billing/payments
       Body: { invoice_id, payment_date, amount, payment_method,
               payment_reference, bank_transaction_id }
       Response: 201 { ok: true, payment: {...} }

POST   /api/billing/payments/:id/reconcile
       Body: { bank_transaction_id }
       Response: { ok: true, payment: { reconciliation_status: 'reconciled' } }

POST   /api/billing/payments/import
       Body: { format: 'bankgiro'|'swish'|'csv', file_data: 'base64...' }
       Response: { ok: true, imported: 42, matched: 38, unmatched: 4 }

3.6 Schemaläggning (Schedules)

GET    /api/billing/schedules
       Query: ?agreement_id=...&is_active=true
       Response: { ok: true, schedules: [...] }

POST   /api/billing/schedules
       Body: { agreement_id, frequency, day_of_month, next_run_date }
       Response: 201 { ok: true, schedule: {...} }

POST   /api/billing/schedules/:id/run-now
       Response: { ok: true, invoice: {...} }

POST   /api/billing/schedules/:id/pause
       Body: { until_date }
       Response: { ok: true, schedule: { is_active: false, paused_until: ... } }

POST   /api/billing/schedules/:id/resume
       Response: { ok: true, schedule: { is_active: true } }

3.7 Rapportering & Export

GET    /api/billing/reports/aging
       Query: ?as_of_date=2026-07-31&customer_org_nr=...
       Response: { ok: true, buckets: [
         { range: '0-30', amount: 450000, invoices: 12 },
         { range: '31-60', amount: 120000, invoices: 5 },
         { range: '61-90', amount: 85000, invoices: 3 },
         { range: '90+', amount: 32000, invoices: 2 }
       ]}

GET    /api/billing/reports/revenue
       Query: ?period_from=2026-01&period_to=2026-06&group_by=month|customer|agreement
       Response: { ok: true, periods: [...] }

GET    /api/billing/reports/outstanding
       Query: ?status=sent,partially_paid&page=1
       Response: { ok: true, invoices: [...], total_outstanding: 687000 }

POST   /api/billing/exports/sie4
       Body: { period_from, period_to, fiscal_year, invoice_ids: [] }
       Response: 202 { ok: true, job_id: '...', status: 'pending' }

POST   /api/billing/exports/pdf
       Body: { invoice_ids: [...] }
       Response: 202 { ok: true, job_id: '...', status: 'pending' }

POST   /api/billing/exports/csv
       Body: { format: 'invoices'|'payments'|'agreements', filters: {...} }
       Response: 202 { ok: true, job_id: '...', status: 'pending' }

POST   /api/billing/exports/visma
       Body: { period_from, period_to, options: {...} }
       Response: 202 { ok: true, job_id: '...', status: 'pending' }

POST   /api/billing/exports/agresso
       Body: { period_from, period_to, options: {...} }
       Response: 202 { ok: true, job_id: '...', status: 'pending' }

GET    /api/billing/exports/:jobId
       Response: { ok: true, job: { status: 'completed', file_url: '...', record_count: 42 } }

GET    /api/billing/exports/:jobId/download
       Response: File download (stream)

3.8 Dashboard & Översikt

GET    /api/billing/dashboard
       Response: {
         ok: true,
         summary: {
           total_agreements: 45,
           active_agreements: 38,
           invoices_this_month: 12,
           total_outstanding: 687000,
           overdue_amount: 237000,
           overdue_count: 8,
           payments_this_month: 890000
         },
         alerts: [
           { type: 'overdue', severity: 'high', message: '3 fakturor > 90 dagar', count: 3 },
           { type: 'draft', severity: 'medium', message: '5 utkast väntar', count: 5 }
         ]
       }

GET    /api/billing/dashboard/activities
       Query: ?limit=20
       Response: { ok: true, activities: [
         { ts, type: 'invoice_sent', description, user_id, entity_id }
       ]}

3.9 Webhooks (för externa system)

GET    /api/billing/webhooks
POST   /api/billing/webhooks
       Body: { url, events: ['invoice.sent', 'invoice.paid', 'payment.received'], secret }
PUT    /api/billing/webhooks/:id
DELETE /api/billing/webhooks/:id
POST   /api/billing/webhooks/:id/test

4. Event-schema (Hermes)

4.1 Events som billing EMITTAR

Event Beskrivning Konsumenter
billing.agreement.created Nytt avtal skapat ledger (förbered konto), audit
billing.agreement.activated Avtal aktiverat ledger, notification
billing.agreement.terminated Avtal uppsagt ledger, notification
billing.invoice.created Faktura skapad ledger (bokför), notification
billing.invoice.sent Faktura skickad notification, CRM
billing.invoice.paid Faktura betald ledger (bokför betalning), notification
billing.invoice.overdue Faktura förfallen notification, reminder-engine
billing.invoice.credited Kreditfaktura skapad ledger
billing.invoice.cancelled Faktura annullerad ledger
billing.payment.received Betalning mottagen ledger, invoice (uppdatera status)
billing.payment.reconciled Betalning avstämd ledger
billing.reminder.sent Påminnelse skickad notification
billing.export.completed Export klar notification
billing.schedule.executed Schemalagd körning audit

4.2 Events som billing KONSUMERAR

Event Källa Åtgärd
finance.journal.posted ledger Uppdatera invoice.ledger_posted_at, payment.journal_entry_id
finance.period.closed ledger Pausa schemaläggning för stängd period

4.3 Exempel: Event-envelope

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "trace_id": "abc123",
  "correlation_id": "inv-2026-00042",
  "event_type": "billing.invoice.created",
  "source": "aamos-billing",
  "tenant_id": "landvex",
  "user_id": "user@landvex.com",
  "entity_type": "invoice",
  "entity_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "decision_source": "user",
  "payload": {
    "invoice_number": "F-2026-00042",
    "agreement_id": "...",
    "customer_org_nr": "559141-7042",
    "total": 125000.00,
    "vat_total": 25000.00,
    "currency": "SEK",
    "due_date": "2026-08-01"
  },
  "ts": "2026-07-02T10:30:00Z",
  "schema_version": "1.0"
}

5. Integration med Ekonomisystem

5.1 Adapter-arkitektur

┌─────────────────────────────────────────────────────────────┐
│                    aamos-billing                             │
│  ┌─────────────────────────────────────────────────────┐    │
│  │           Export Engine (abstrakt)                   │    │
│  │  ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐  │    │
│  │  │ SIE4    │ │ Visma   │ │Agresso  │ │ Fortnox │  │    │
│  │  │ Exporter│ │ Adapter │ │ Adapter │ │ Adapter │  │    │
│  │  └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘  │    │
│  └───────┼───────────┼───────────┼───────────┼───────┘    │
│          │           │           │           │              │
│          ▼           ▼           ▼           ▼              │
│       SIE4-fil   Visma API   Agresso API  Fortnox API      │
│       (.si)      (REST/SOAP) (REST/SOAP) (REST/OAuth)     │
└─────────────────────────────────────────────────────────────┘

5.2 SIE4 Export

  • Format: SIE4 (svensk standard för ekonomidata)
  • Innehåll: Verifikationer för fakturor och betalningar
  • Trigger: Manuell export eller schemalagd (månadsvis)
  • Filnamn: SIE4_<tenant>_<period>_<timestamp>.si
  • Befintlig kod: Återanvänd sie4-export.mjs från aamos-ledger

5.3 Visma Integration

  • API: Visma.net ERP API (REST/OAuth2)
  • Funktioner:
    • Skicka fakturor till Visma (tvåvägs)
    • Hämta kundregister
    • Synkronisera betalningar
  • Konfiguration: visma_client_id, visma_client_secret, visma_tenant_id

5.4 Agresso Integration

  • API: Agresso Business World API (SOAP/REST)
  • Funktioner:
    • Exportera fakturor som Agresso-importfiler
    • Hämta huvudboksdata
  • Konfiguration: agresso_wsdl_url, agresso_username, agresso_password

5.5 Fortnox Integration

  • API: Fortnox API v3 (REST/OAuth2)
  • Funktioner:
    • Skapa fakturor i Fortnox
    • Hämta betalningsstatus
    • Synkronisera kunder
  • Konfiguration: fortnox_client_id, fortnox_client_secret, fortnox_access_token

5.6 Konfigurationstabell

CREATE TABLE IF NOT EXISTS billing_integration_configs (
  id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id       TEXT NOT NULL,
  system_name     TEXT NOT NULL,           -- 'visma'|'agresso'|'fortnox'|'custom'
  is_active       BOOLEAN NOT NULL DEFAULT FALSE,
  config          JSONB NOT NULL,          -- system-specifik konfig
  last_sync_at    TIMESTAMPTZ,
  last_sync_status TEXT,                   -- 'success'|'failed'
  last_error      TEXT,
  created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at      TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  UNIQUE (tenant_id, system_name)
);

6. UI-design — Skärmar och Flöden

6.1 Designprinciper för UI

  • Mobil-first: Fungerar på alla skärmstorlekar
  • Tydliga åtgärder: Primära knappar (skapa, skicka) framhävs
  • Statusfärger: Grön (betald), gul (skickad), röd (förfallen), grå (utkast)
  • Kontextuell hjälp: Tooltips och hjälptexter vid varje fält
  • Bulk-åtgärder: Markera flera fakturor och skicka/påminn/export
  • Sök & Filter: Snabb åtkomst till data

6.2 Skärmöversikt

┌─────────────────────────────────────────────────────────────────┐
│  LandveX Billing                    [🔍 Sök] [👤 Admin] [⚙️]   │
├─────────────────────────────────────────────────────────────────┤
│  [📊 Dashboard] [📋 Fakturor] [📄 Avtal] [💰 Betalningar]      │
│  [📈 Rapporter] [⚙️ Inställningar]                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  INNEHÅLL (varierar per vy)                                    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

6.3 Skärm 1: Dashboard (Start)

Syfte: Översikt och snabbåtkomst till viktig information.

Innehåll:

  • KPI-kort (4 st):
    • "Aktiva avtal" — 38 st (↗ +2 denna månad)
    • "Obetalt belopp" — 687 000 kr (8 fakturor)
    • "Förfallet belopp" — 237 000 kr (3 > 90 dagar) 🔴
    • "Inbetalningar denna månad" — 890 000 kr
  • Varningar/Alerts:
    • Röd: "3 fakturor förfallna > 90 dagar"
    • Gul: "5 fakturor i utkast väntar på att skickas"
    • Blå: "2 schemalagda körningar idag"
  • Snabbåtgärder:
    • [+ Skapa faktura]
    • [+ Nytt avtal]
    • [📤 Skicka utkast]
    • [📥 Importera betalningar]
  • Aktivitetsflöde (senaste 10):
    • "Faktura F-2026-0042 skickad till Kund AB"
    • "Betalning 45 000 kr mottagen för F-2026-0038"
    • "Avtal KOM-2026-005 aktiverat"
  • Graf: Inbetalningar per månad (stapeldiagram, senaste 12 mån)

6.4 Skärm 2: Fakturor (Lista)

Syfte: Hantera alla fakturor — sök, filtrera, skicka, påminn.

Innehåll:

  • Filter-rad:
    • Status: [Alla ▼] [Utkast] [Skickad] [Betald] [Förfallen] [Krediterad]
    • Period: [2026-07 ▼]
    • Kund: [🔍 Sök kund...]
    • Belopp: [Min] — [Max]
    • [🔍 Sök] [ Rensa]
  • Tabell:
    ☑️ Faktura Kund Datum Förfallo Belopp Status Åtgärder
    F-2026-0045 Kommun X 2026-07-01 2026-08-01 125 000 kr 🟡 Skickad [📧] [💰] [⚙️]
    F-2026-0044 Företag Y 2026-06-15 2026-07-15 45 000 kr 🔴 Förfallen [📧] [💰] [⚙️]
    F-2026-0043 Kommun Z 2026-06-01 2026-07-01 89 000 kr 🟢 Betald [👁️]
  • Bulk-åtgärder (när rader markerade):
    • [📤 Skicka valda] [📧 Påminn valda] [📥 Exportera] [🗑️ Ta bort]
  • Paginering: « 1 2 3 ... 12 »

6.5 Skärm 3: Faktura — Detaljvy

Syfte: Visa och hantera en enskild faktura.

Innehåll:

  • Header:
    • Fakturanummer: F-2026-0045
    • Status-badge: 🟡 Skickad
    • Åtgärds-knappar: [📧 Skicka påminnelse] [💰 Registrera betalning] [🔄 Kreditera] [🖨️ PDF]
  • Kundinfo (kort):
    • Kommun X (org.nr 212000-1234)
    • Storgatan 1, 123 45 Staden
    • Kontakt: Anna Svensson, anna@kommunx.se
  • Fakturainfo:
    • Fakturadatum: 2026-07-01
    • Förfallodatum: 2026-08-01 (15 dagar kvar)
    • Leveransdatum: 2026-07-01
    • Vår referens: Projektledare Nilsson
    • Er referens: Anna Svensson
  • Fakturarader (tabell):
    # Beskrivning Antal Enhet á-pris Moms% Moms Totalt
    1 Drift av VA-nät 1 mån 100 000 kr 25% 25 000 kr 125 000 kr
  • Summering (höger):
    • Netto: 100 000 kr
    • Moms: 25 000 kr
    • Att betala: 125 000 kr
    • Betalt: 0 kr
    • Kvar att betala: 125 000 kr
  • Betalningshistorik:
    • (tom om obetald)
  • Påminnelser:
    • (tom om inga skickade)
  • Bokföring:
    • Bokförd: Ja (verifikat #1234, 2026-07-01)
    • [Visa i ledger]
  • Audit-logg (expandable):
  • Flikar: [Faktura] [Betalningar] [Påminnelser] [Bokföring] [Historik]

6.6 Skärm 4: Skapa Faktura (Wizard)

Syfte: Guidad process för att skapa ny faktura.

Steg 1: Välj kund

  • Sök kund (autocomplete från avtal/kundregister)
    • Eller: [+ Ny kund]
  • Visa kundinfo-kort när vald
  • [Nästa ▶]

Steg 2: Fakturainformation

  • Fakturadatum: [2026-07-02 ▼]
  • Förfallodatum: [2026-08-02 ▼] (auto = +30 dagar)
  • Leveransdatum: [2026-07-02 ▼]
  • Vår referens: [Projektledare Nilsson]
  • Er referens: [Anna Svensson]
  • Betalningsvillkor: [30 dagar netto ▼]
  • [⬅️ Föregående] [Nästa ▶]

Steg 3: Fakturarader

  • [+ Lägg till rad]
    • Rad-formulär:
      • Beskrivning: [Drift av VA-nät]
      • Antal: [1]
      • Enhet: [mån ▼]
      • á-pris (exkl. moms): [100 000]
      • Moms%: [25 ▼]
      • Intäktskonto: [3000 ▼]
      • Kostnadsställe: [--- ▼]
      • Projekt: [--- ▼]
  • Summering uppdateras live
  • [⬅️ Föregående] [Nästa ▶]

Steg 4: Granska & Skicka

  • Förhandsvisning av faktura (PDF-liknande layout)
  • [🖨️ Förhandsgranska PDF]
  • [💾 Spara som utkast]
  • [📧 Skicka nu]
  • [📧 Skicka senare — välj datum]

6.7 Skärm 5: Avtal (Lista)

Syfte: Hantera avtal — skapa, ändra, förläng, säg upp.

Innehåll:

  • Filter:
    • Status: [Alla ▼] [Utkast] [Aktivt] [Uppsagt] [Avslutat]
    • Typ: [Alla ▼] [Kommunalt] [Kommersiellt] [Internt]
    • Kund: [🔍 Sök...]
    • [🔍 Sök] [ Rensa]
  • Tabell:
    Avtalsnr Kund Typ Period Frekvens Nästa faktura Status
    KOM-2026-001 Kommun X Kommunalt 2026-01 — 2026-12 Månadsvis 2026-08-01 🟢 Aktiv
    KOM-2026-005 Företag Y Kommersiellt 2026-03 — 2027-03 Kvartalsvis 2026-10-01 🟢 Aktiv
  • Åtgärder per rad:
    • [👁️ Visa] [✏️ Redigera] [📄 Skapa faktura] [⏸️ Pausa] [🛑 Säg upp]
  • [+ Nytt avtal]

6.8 Skärm 6: Avtal — Detaljvy

Syfte: Visa och hantera ett enskilt avtal.

Innehåll:

  • Header:
    • Avtalsnummer: KOM-2026-001
    • Status: 🟢 Aktiv
    • Åtgärder: [✏️ Redigera] [📄 Skapa faktura] [⏸️ Pausa] [🛑 Säg upp]
  • Avtalsinfo:
    • Kund: Kommun X (org.nr 212000-1234)
    • Typ: Kommunalt
    • Period: 2026-01-01 — 2026-12-31
    • Auto-förlängning: Ja (12 mån)
    • Uppsägningstid: 90 dagar
    • Betalningsvillkor: 30 dagar
  • Avtalsrader:
    # Beskrivning Antal Enhet á-pris Moms% Status
    1 Drift av VA-nät 1 mån 100 000 kr 25% Aktiv
    2 Underhåll 1 mån 25 000 kr 25% Aktiv
  • [+ Lägg till rad]
  • Fakturahistorik:
    Faktura Datum Belopp Status
    F-2026-0045 2026-07-01 125 000 kr 🟡 Skickad
    F-2026-0038 2026-06-01 125 000 kr 🟢 Betald
  • Schemaläggning:
    • Nästa körning: 2026-08-01
    • Frekvens: Månadsvis (dag 1)
    • [▶️ Kör nu] [⏸️ Pausa] [⚙️ Ändra schema]
  • Flikar: [Översikt] [Rader] [Fakturor] [Schema] [Historik]

6.9 Skärm 7: Betalningar

Syfte: Registrera och avstämma betalningar.

Innehåll:

  • Import: [📥 Importera från bank] [📥 Importera CSV]
  • Filter:
    • Status: [Alla ▼] [Oavstämd] [Avstämd]
    • Period: [2026-07 ▼]
    • [🔍 Sök]
  • Tabell:
    Datum Faktura Kund Belopp Metod Referens Status
    2026-07-02 F-2026-0038 Kommun X 125 000 kr Bankgiro OCR 12345 🟢 Avstämd
    2026-07-01 Okänd 45 000 kr Bankgiro OCR 99999 🔴 Oavstämd
  • Åtgärder:
    • Avstämd: [👁️ Visa]
    • Oavstämd: [🔗 Matcha faktura] [✏️ Redigera] [🗑️ Ta bort]

6.10 Skärm 8: Rapporter

Syfte: Generera och exportera rapporter.

Innehåll:

  • Rapport-typer (kort):
    • [📊 Förfallna fordringar] — Aging-report
    • [📈 Intäktsrapport] — Per period/kund/avtal
    • [📋 Obetalda fakturor] — Lista med filter
    • [📤 Export] — SIE4, PDF, CSV, Visma, Agresso
  • Förfallna fordringar (exempel):
    • Filter: Per [månad ▼] | Kund: [Alla ▼]
    • Graf: Stapeldiagram per bucket (0-30, 31-60, 61-90, 90+)
    • Tabell: Detaljer per kund
    • [📥 Exportera till Excel] [📥 Exportera till PDF]
  • Export-dialog:
    • Format: [SIE4 ▼] [PDF ▼] [CSV ▼] [Visma ▼] [Agresso ▼]
    • Period: [Från] — [Till]
    • [▶️ Generera]
    • Status: "Genererar..." → "Klar! [Ladda ner]"

6.11 Skärm 9: Inställningar

Syfte: Konfigurera modulen och integrationer.

Innehåll:

  • Flikar: [Allmänt] [Integrationer] [Moms] [Användare] [Avancerat]
  • Allmänt:
    • Standard betalningsvillkor: [30 dagar]
    • Standard dröjsmålsränta: [8%]
    • Fakturanummer-serie: [F-YYYY-NNNNN]
    • Avtalsnummer-serie: [KOM-YYYY-NNN]
  • Integrationer:
    • Visma: [Anslut] / [ Ansluten] [⚙️ Konfigurera] [🔄 Synka nu]
    • Agresso: [Anslut] / [ Ansluten]
    • Fortnox: [Anslut] / [ Ej konfigurerad]
  • Moms:
    • Standard moms: [25% ▼]
    • Momssatser: [25%], [12%], [6%], [0%]
  • Användare & Roller:
    • Lista användare med roller (admin, accountant, viewer)
    • [+ Lägg till användare]

7. Arbetsflöden (Workflows)

7.1 Flöde: Skapa och skicka faktura

[Användare] ──► [Skapa faktura] ──► [Fyll i kund, rader] ──► [Granska]
                                                      │
                                                      ▼
                                              [Spara utkast]
                                                      │
                                                      ▼
                                              [Skicka faktura]
                                                      │
                              ┌───────────────────────┼───────────────────────┐
                              ▼                       ▼                       ▼
                        [E-post]                [Post]                  [PDF nedladdning]
                              │                       │                       │
                              ▼                       ▼                       ▼
                        [Status: sent]          [Status: sent]          [Status: sent]
                              │
                              ▼
                        [Hermes: billing.invoice.sent]
                              │
                              ▼
                        [Ledger: bokför kundfordran]
                              │
                              ▼
                        [Status: posted i ledger]

7.2 Flöde: Återkommande fakturering (schemalagd)

[Cron-jobb varje dag 06:00]
         │
         ▼
[ billing_schedules: next_run_date <= idag AND is_active ]
         │
         ▼
[ För varje schema: ]
         │
         ▼
[ Skapa faktura från avtalsrader ]
         │
         ▼
[ Sätt status = 'draft' ]
         │
         ▼
[ Uppdatera next_run_date (nästa månad/kvartal/år) ]
         │
         ▼
[ Hermes: billing.schedule.executed ]
         │
         ▼
[ Notification: "Ny faktura genererad: F-2026-0045" ]
         │
         ▼
[ Användare granskar och skickar ]

7.3 Flöde: Betalning och avstämning

[ Bankgiro-fil / Swish-rapport / Manuell inmatning ]
         │
         ▼
[ Import till billing_payments ]
         │
         ▼
[ Auto-match: payment_reference (OCR) ↔ invoice_number ]
         │
         ├─────────────────┬─────────────────┐
         ▼                 ▼                 ▼
    [Matchad]        [Delvis matchad]   [Ej matchad]
         │                 │                 │
         ▼                 ▼                 ▼
[ Uppdatera invoice ] [ Uppdatera ]    [ Markera som ]
[ amount_paid ]       [ partially_paid ]  [ unreconciled ]
         │                 │                 │
         ▼                 ▼                 ▼
[ Om amount_paid >= total ]    [ Manuell matchning ]
         │
         ▼
[ Status: paid ]
         │
         ▼
[ Hermes: billing.payment.received ]
         │
         ▼
[ Ledger: bokför betalning (1910 → 1510) ]

7.4 Flöde: Påminnelse och inkasso

[Cron-jobb: dagligen, 07:00]
         │
         ▼
[ invoices: due_date < idag AND status IN ('sent', 'partially_paid') ]
         │
         ▼
[ För varje förfallen faktura: ]
         │
         ▼
[ Räkna dagar sedan due_date ]
         │
         ├─────────────┬─────────────┬─────────────┐
         ▼             ▼             ▼             ▼
      [1-30 dagar] [31-60 dagar] [61-90 dagar] [90+ dagar]
         │             │             │             │
         ▼             ▼             ▼             ▼
      [Inget]    [Påminnelse 1] [Påminnelse 2] [Inkasso]
                              │             │             │
                              ▼             ▼             ▼
                        [Skicka mail] [Skicka mail] [Skicka till]
                        [+ påminnelse-] [+ påminnelse-] [inkassobyrå]
                        [avgift 60 kr] [avgift 60 kr]
                              │             │
                              ▼             ▼
                        [billing_reminders] [billing_reminders]
                        [reminder_number=1] [reminder_number=2]
                              │
                              ▼
                        [Hermes: billing.reminder.sent]

7.5 Flöde: Kreditfaktura

[Användare väljer faktura] ──► [Kreditera]
                                      │
                                      ▼
                              [Ange anledning]
                                      │
                                      ▼
                              [Välj rader att kreditera]
                              [eller hela fakturan]
                                      │
                                      ▼
                              [Skapa kreditfaktura (KF-2026-0001)]
                              [invoice_type = 'credit']
                              [original_invoice_id = F-2026-0045]
                                      │
                                      ▼
                              [Negativa belopp på rader]
                                      │
                                      ▼
                              [Status: sent]
                                      │
                                      ▼
                              [Uppdatera original: credited_by_invoice_id]
                              [Original status: credited]
                                      │
                                      ▼
                              [Hermes: billing.invoice.credited]
                                      │
                                      ▼
                              [Ledger: bokför kreditering]

8. Teknisk Arkitektur

8.1 Teknisk Stack

Komponent Teknik
Runtime Node.js 20+ (ESM)
Framework Express.js
Databas PostgreSQL 15+
Cache Redis (Hermes + sessions)
Auth JWT (RS256/HS256), samma som ledger
Validering Joi
PDF Puppeteer / Playwright (HTML → PDF)
E-post Nodemailer / AWS SES
Fil-lagring S3 / MinIO
Cron node-cron / bullmq

8.2 Projektstruktur

aamos-billing/
├── index.mjs              # Express-app, routes
├── package.json
├── schema.sql             # PostgreSQL-schema
├── auth.mjs               # JWT + RBAC (återanvänd från ledger)
├── validation.mjs         # Joi-scheman
├── hermes.mjs             # Event-publishing (återanvänd från ledger)
├── config.mjs             # Konfiguration
│
├── routes/
│   ├── agreements.mjs     # Avtal-endpoints
│   ├── invoices.mjs       # Faktura-endpoints
│   ├── payments.mjs       # Betalnings-endpoints
│   ├── schedules.mjs      # Schemaläggning
│   ├── reports.mjs        # Rapporter
│   ├── exports.mjs        # Export-jobb
│   └── dashboard.mjs      # Dashboard-data
│
├── services/
│   ├── invoiceService.mjs      # Affärslogik fakturor
│   ├── agreementService.mjs    # Affärslogik avtal
│   ├── paymentService.mjs      # Affärslogik betalningar
│   ├── scheduleService.mjs     # Cron-jobb & schemaläggning
│   ├── reminderService.mjs     # Påminnelsemotor
│   ├── pdfService.mjs          # PDF-generering
│   ├── emailService.mjs        # E-postutskick
│   └── exportService.mjs       # Export-motorer
│
├── exporters/
│   ├── sie4Exporter.mjs        # SIE4-export
│   ├── vismaAdapter.mjs        # Visma-integration
│   ├── agressoAdapter.mjs      # Agresso-integration
│   ├── fortnoxAdapter.mjs      # Fortnox-integration
│   └── csvExporter.mjs         # CSV-export
│
├── templates/
│   ├── invoice-template.html   # Faktura-PDF mall
│   ├── reminder-template.html  # Påminnelse-PDF mall
│   └── email/
│       ├── invoice-sent.html
│       ├── payment-received.html
│       └── reminder.html
│
├── ui/                    # Frontend (återanvänd Ouroboros-stil)
│   ├── index.html
│   ├── invoices.html
│   ├── agreements.html
│   ├── payments.html
│   ├── reports.html
│   └── settings.html
│
├── tests/
│   ├── agreements.test.mjs
│   ├── invoices.test.mjs
│   ├── payments.test.mjs
│   └── exports.test.mjs
│
└── Dockerfile

8.3 Miljövariabler

# Server
BILLING_PORT=3260
NODE_ENV=production

# Database
DATABASE_URL=postgresql://user:pass@localhost:5432/billing

# Redis (Hermes)
REDIS_URL=redis://127.0.0.1:6379
HERMES_CHANNEL=aamos:hermes

# Auth
JWT_PUBLIC_KEY_PATH=/secrets/jwt.pub.pem
JWT_SECRET_KEY_PATH=/secrets/jwt.key.pem

# File storage (S3/MinIO)
S3_ENDPOINT=https://s3.landvex.com
S3_BUCKET=billing-documents
S3_ACCESS_KEY=...
S3_SECRET_KEY=...

# Email
SMTP_HOST=smtp.landvex.com
SMTP_PORT=587
SMTP_USER=...
SMTP_PASS=...
EMAIL_FROM=billing@landvex.com

# Integrations
VISMA_CLIENT_ID=...
VISMA_CLIENT_SECRET=...
AGRESSO_WSDL_URL=...
FORTNOX_CLIENT_ID=...

9. Säkerhet

9.1 Åtkomstkontroll (RBAC)

Roll Rättigheter
admin Full åtkomst — alla operationer
accountant Skapa/redigera fakturor, avtal, betalningar. Stänga perioder.
viewer Läsa all data, generera rapporter. Inga ändringar.
billing_operator Skapa och skicka fakturor. Registrera betalningar. Ej bokföring.

9.2 Säkerhetsåtgärder

  • Input-validering: Alla endpoints valideras med Joi
  • SQL-injektion: Parameteriserade queries (pg)
  • XSS: Output encoding, CSP-headers
  • CSRF: Token-baserat skydd för state-changing requests
  • Rate limiting: 100 req/15min generellt, 10 req/15min för auth
  • Audit logging: Alla operationer loggas i billing_audit_log
  • Kryptering: Kunddata i vila (DB), TLS i transport
  • Filuppladdning: Validering av filtyp och storlek, virusscan

10. Implementeringsplan

Etapp 1: Kärna (vecka 1-2)

  • Databasschema (migrations)
  • Grundläggande CRUD för avtal och fakturor
  • PDF-generering
  • E-postutskick
  • Enkel dashboard

Etapp 2: Automatisering (vecka 3-4)

  • Schemaläggning (cron)
  • Påminnelsemotor
  • Betalningsimport och avstämning
  • Hermes-integration

Etapp 3: Integrationer (vecka 5-6)

  • SIE4-export
  • Visma-adapter
  • Agresso-adapter
  • CSV-export

Etapp 4: UI & Polish (vecka 7-8)

  • Komplett UI (HTML/CSS/JS)
  • Rapporter och grafer
  • Bulk-åtgärder
  • Tester och dokumentation

11. Bilagor

11.1 Fakturanummer-serie

Format: {prefix}-{YYYY}-{NNNNN}
Exempel: F-2026-00042, KF-2026-00001 (kredit)

Prefix:
  F   = Standardfaktura
  KF  = Kreditfaktura
  PF  = Proformafaktura
  R   = Påminnelse

11.2 Avtalsnummer-serie

Format: {prefix}-{YYYY}-{NNN}
Exempel: KOM-2026-001, KOM-2026-005

Prefix:
  KOM = Kommunalt avtal
  KOM = Kommersiellt avtal
  INT = Internt avtal

11.3 Moms-konton (BAS 2024)

Moms Konto Beskrivning
25% 2610 Utgående moms, 25%
12% 2620 Utgående moms, 12%
6% 2630 Utgående moms, 6%
0% Momsexempt

Dokumentversion 1.0 — 2026-07-02 Nästa granskning: Efter Etapp 1-komplettering