- 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!
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 inkluderarmeta.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.mjsfrå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):
- 2026-07-01 10:30 — Skapad av admin@landvex.com
- 2026-07-01 11:00 — Skickad via e-post till anna@kommunx.se
- 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: [--- ▼]
- Rad-formulär:
- 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 |
| 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