Technische Dokumentation

AIQ BILLING MOCK

Demo-Abrechnungssystem eines deutschen Energieversorgers (Strom & Gas) — zentrale Backend-Datenquelle für Kundenportal, Bestellstrecke und Vertriebspartnerportal, inklusive Admin-Frontend zur Simulation realer Abrechnungs- und Vertriebsvorgänge.

Überblick

Das aiq-billing-mock bildet ein echtes Billing-System für Produktdemos nach. Es zeigt, wie Daten in einem Abrechnungssystem ankommen bzw. abgerufen werden, und stellt dafür bereit:

Abgrenzung: Keine Marktkommunikation (kein EDIFACT/UTILMD/APERAK, keine Bilanzierung, kein MaStR). MaLo-/MeLo-IDs sind reine Stammdaten mit Prüfziffer-Validierung. Die „Ablehnung durch Netzbetreiber“ ist eine reine Admin-Status-Simulation am Auftrag.
3+1
Module (+ Admin)
~28
Prisma-Modelle
49
API-Endpunkte/-Checks
7
Demo-Personas

Architektur

Ein Next.js-16-Monolith stellt sowohl die Modul-APIs als auch das Admin-Frontend bereit und spricht eine PostgreSQL-Datenbank über Prisma an. Die drei Konsumenten-Frontends sind externe Module; das Output-Management (Dokumente/E-Mails) ist das externe System output.rocks.

Kundenportal externes Frontend Bestellstrecke externes Frontend Vertriebsportal Check24 · Verifox REST / Bearer Next.js 16 · aiq-billing-mock REST-API (Zod-validiert, OpenAPI) /api/order · /api/sales · /api/customer · /api/admin · /api/docs Services & State Machines tariff · order · contract commission · invoice · audit order/contract/vorgang FSM Validierung (MaLo, IBAN, OBIS) Admin-Frontend (CONUTI) Simulations-Rail Kunden · Verträge · Aufträge Tarife · Vorgänge · API-Keys EreignisLog-Timeline Auth Auth.js v5 (Admin-Session) · API-Key-Bearer (Module) · Customer-JWT Prisma 7 (PrismaPg Driver-Adapter) Zod-first Schemas · EreignisLog (append-only) PostgreSQL 17 Docker-Volume · ~28 Tabellen output.rocks Output-Mgmt (extern, Referenz)

Tech-Stack

BereichTechnologieHinweis
FrameworkNext.js 16 (App Router)APIs + Admin-UI in einem Projekt; dynamische params sind ein Promise
DatenbankPostgreSQL 17postgres:17-alpine
ORMPrisma 7Driver-Adapter PrismaPg (Pflicht), Client in lib/generated/prisma
Validierung + OpenAPIZod 4 + zod-openapiSingle Source: ein Schema validiert und erzeugt das Spec
Swagger-UIswagger-ui-reactunter /api/docs
AuthAuth.js v5 + API-Key-BearerAdmin-Session (Rolle ADMIN) + Modul-Keys + Customer-JWT
StylingTailwind v4 + CONUTI-TokensRaleway, teal/lime, Pills/Kreise, Lucide-Icons
BetriebDocker Composemigrate + seed + start automatisch beim Container-Start

Datenmodell

Kern ist die SAP-IS-U-nahe Hierarchie Geschäftspartner → Vertragskonto → Vertrag → Lieferstelle → Zähler → Zählerstand. Strom + Gas eines Kunden bündeln sich auf einem Vertragskonto (eine Bankverbindung / ein SEPA-Mandat). Geld als Decimal, IDs als cuid(), Zeitstempel als Timestamptz.

Stammdaten & Markt
  • Geschaeftspartner (Kunde)
  • Vertragskonto (bündelt Rechnung/Zahlung)
  • Bankverbindung, SepaMandat
  • MarketPartner (Lieferant/VNB/MSB, MP-ID)
  • Netzgebiet, PlzNetzgebiet, GasParameter
Vertrag & Versorgung
  • Vertrag (Sparte, Status, Laufzeit, Prognose)
  • Lieferstelle (MaLo/MeLo, Adresse)
  • Zaehler (Typ, HT/NT, Einspeisung)
  • Zaehlerstand (OBIS, Plausibilität)
  • Tarif, TarifPreis (je Netzgebiet, Provision)
Abrechnung & Zahlung
  • Abschlagsplan
  • Rechnung (Turnus/Schluss, Saldo)
  • Dokument (Metadaten + output.rocks-Ref)
Vertrieb & Order
  • Partner, PartnerUser
  • Order (Vertrieb + Bestellstrecke)
  • OrderStatusHistorie
  • Commission, Clawback, ImportBatch
Service (Kundenportal)
  • Vorgang (Querschnitts-Servicefall)
  • VorgangStatusHistorie, Nachricht
System
  • EreignisLog (append-only Audit über alles)
  • ApiClient (Modul-Keys), AdminUser

State Machines & Simulation

Drei verknüpfte Zustandsautomaten als Transition-Tabellen. Zustandswechsel erfolgen nur über Service-Funktionen; jeder Wechsel schreibt einen EreignisLog-Eintrag. Ein ungültiger Übergang liefert 409 INVALID_TRANSITION. Das Admin-Frontend zeigt pro Status nur die gültigen Aktions-Buttons.

Order (Auftrag)

ANGELEGTBONITAETSPRUEFUNG AN_NETZBETREIBER_UEBERMITTELTIN_PRUEFUNG BESTAETIGTIN_VERTRAG_UEBERFUEHRT
Terminal: BONITAET_ABGELEHNTABGELEHNTWIDERRUFENSTORNIERT

Vertrag

ANGELEGTIN_ANMELDUNG AKTIVIN_KUENDIGUNG GEKUENDIGTBEENDET · Seitenpfade: MAHNUNG, GESPERRT

Admin-Simulationen & Seiteneffekte

Aktion (Endpunkt)Wirkung
bonitaet_setzensetzt Bonität; OK → Übermittlung, ABGELEHNT → BONITAET_ABGELEHNT
bestaetigenOrder → BESTAETIGT; bucht Commission (RESERVIERT) bei Partner-Order
lieferbeginn_bestaetigenOrder → IN_VERTRAG_UEBERFUEHRT; Vertrag → AKTIV; Commission → VERDIENT
netzbetreiber_ablehnung (+ Grund)Order → ABGELEHNT; löst Clawback aus (Grund-Enum)
storno_simulieren / widerrufOrder → STORNIERT/WIDERRUFEN; Clawback
kuendigung_bestaetigenVertrag → GEKUENDIGT; Lieferende + Kündigungsbestätigung (Dokument)
schlussrechnung_erzeugenerzeugt SCHLUSSRECHNUNG + Dokument
sperrung / entsperrung / mahnungVertragsstatus + Sperr-Flag
time/advancespult fristabhängige Felder eines Auftrags vor (Demo)

Validierung

Zentrale Utilities (lib/validation/) werden von der API und vom Seed genutzt — dadurch sind alle Demo-IDs garantiert gültig.

PrüfungRegel
MaLo-ID11-stellig, Prüfziffer nach Lok-/Waggon-Verfahren (Mod-10)
MP-ID / Codenummer13-stellig, gleiches Prüfziffer-Verfahren
MeLo-ID33 Zeichen (DE + 6 Codenummer + 5 PLZ + 20 Zählpunkt)
IBANISO-7064 Mod-97
ZählerstandRückläufer = Hard-Fail (außer Zählerwechsel/Einspeisung); Verbrauch außerhalb ~50–200 % der Prognose = Warnung mit Bestätigungspflicht; Datum nicht in Zukunft/vor letztem Stand
Abschlag50–200 % vom Soll
GaskWh = m³ × Zustandszahl × Brennwert

Auth-Konzept

EndpunktgruppeAuthentifizierung
/api/order/**Modul-Bearer (aiq_order_demo_key)
/api/sales/**Modul-Bearer + Header X-Partner-Id (Mandant) + Scopes
/api/customer/auth/loginModul-Bearer (aiq_customer_demo_key)
übrige /api/customer/**Modul-Bearer + X-Customer-Token (JWT, kundengescoped)
/api/admin/** + /adminAuth.js-Session, Rolle ADMIN (proxy.ts)
/api/docs/**öffentlich (lokale Demo)

API · Bestellstrecke Bearer: aiq_order_demo_key

MethodePfadZweck
GET/api/order/verfuegbarkeitBelieferbarkeit + Netzbetreiber zur Adresse
GET/api/order/tarifeTarife nach PLZ/Sparte/Verbrauch (inkl. Jahreskosten, Monatsabschlag)
POST/api/order/tarife/berechnungExakte Preisberechnung inkl. USt
POST/api/order/ordersAuftrag abschließen (legt Order + Vorgang + Widerrufsfrist an)
GET/api/order/orders/{nr}/statusAuftragsstatus
POST/api/order/orders/{nr}/widerrufWiderruf (14-Tage-Frist)
POST/api/order/orders/{nr}/bonitaetBonitätsprüfung (Mock)

API · Vertrieb Bearer: aiq_sales_demo_key · X-Partner-Id

MethodePfadZweck
POST/api/sales/auth/loginPartner-Login → liefert partnerId/Kennung
GET/api/sales/tariffsTarif- + Provisions-Feed
POST/api/sales/ordersAuftrag/Lead übermitteln (qualifiziert/unqualifiziert)
GET/api/sales/orderseigene Aufträge (mandantengetrennt)
GET/api/sales/orders/{nr} · /historyStatus + Statushistorie
POST/api/sales/orders/importListen-Import (Leads) → ImportBatch + Fehlerprotokoll
GET/api/sales/commissionsProvisionsübersicht inkl. Stornoreserve
GET/api/sales/reporting/performance · /forecastAbschluss-/Stornoquote, Provisions-Forecast

API · Kundenportal Bearer: aiq_customer_demo_key · X-Customer-Token

MethodePfadZweck
POST/api/customer/auth/loginLogin (Vertragsnr + Nachname/Geburtsdatum) → JWT
GET/api/customer/contracts · /{nr} · /{nr}/metersVerträge, Vertragsdetails, Zähler
POST/GET/api/customer/contracts/{nr}/meter-readingsZählerstand melden (Plausibilität) / Historie
PUT/api/customer/contracts/{nr}/installmentAbschlag ändern (Min/Max-Korridor)
PUT/api/customer/bank-accountBankdaten/SEPA ändern (IBAN-Prüfung, neues Mandat)
POST/api/customer/contracts/{nr}/move · /terminationUmzug melden, Kündigung
GET/api/customer/invoices · /documents · /casesRechnungen, Dokumentenarchiv, Vorgänge
POST/api/customer/cases/{nr}/messagesRückfrage/Nachricht zu einem Vorgang
PUT/api/customer/profileKontaktdaten ändern

Jede schreibende Aktion legt einen Vorgang mit Statushistorie an.

API · Admin Auth.js-Session

MethodePfadZweck
POST/api/admin/orders/{id}/actionsAuftrags-Simulationen (s. State Machines)
POST/api/admin/contracts/{id}/actionsVertrags-Aktionen (Kündigung, Sperrung, Schlussrechnung …)
POST/api/admin/meter-readings/{id}/releaseunplausiblen Zählerstand freigeben
POST/api/admin/time/advanceDemo-Fristen vorspulen
GET/POST/api/admin/api-clientsModul-API-Keys verwalten (Klartext einmalig)

Admin-Frontend

Server Components unter /admin lesen Prisma direkt; Mutationen laufen als Server Actions über die Services. Seiten: Dashboard (KPIs + EreignisLog-Timeline), Kunden, Verträge (mit Aktions-Rail), Zählerstände (Freigabe), Tarife, Aufträge (Status-Flow + Simulations-Rail mit Grund-Dropdowns), Vorgänge (Nachrichten-Thread), Dokumente, API-Clients, Swagger. CONUTI-Stil: teal Sidebar, lime Akzent, Pills, Karten mit weicher Schattierung, Raleway, Lucide-Icons.

Seed & Personas

Der Seed ist idempotent (clear-then-insert, Zeitbezüge relativ zu now()). Stammdaten: 4 Regionen/Netzgebiete (Stuttgart, München, Berlin, Köln), je Sparte 3–4 Tarife mit Preisen je Netzgebiet, GasParameter, Marktrollen.

#PersonaDemonstriert
1Anna BergerStrom Eintarif AKTIV (Standardfall: Stände, Jahresabrechnung, Vorgang mit Rückfrage)
2Max SonnbergStrom Zweitarif + PV (HT/NT + Einspeisung, OBIS)
3Petra GasmannGas mit laufendem Lieferantenwechsel (Admin: Lieferbeginn bestätigen)
4Klaus SteinGas-Auftrag vom Netzbetreiber abgelehnt
5Familie KombiStrom + Gas auf einem Vertragskonto (gebündelt)
6Sandra WechslerCheck24-Auftrag (bestätigt, Provision reserviert)
7Tom NeukundeStorno/Widerruf-Kandidat (Widerrufsfrist offen)

Setup & Betrieb

# Aus dem Repo-Root:
docker compose up --build        # Postgres + App: migrate + seed + start
#   Admin:   http://localhost:3000/admin   (admin / admin)
#   Swagger: http://localhost:3000/api/docs
docker compose down -v           # inkl. DB-Volume zurücksetzen

# Lokale Entwicklung (aiq-billing-mock/):
docker compose up -d postgres
cp .env.example .env && npm install
npm run db:migrate && npm run db:seed && npm run dev

Tests

Ein End-to-End-Smoke-Test (scripts/smoke-test.sh) prüft alle Module gegen die laufende App — 49 Checks, 0 Fehler: Status-Codes (200/201/401/403/422/409), Auth-Gates, Tarifberechnung, Auftrags- & Simulationsketten (Order → Vertrag → Commission), Zählerstand-Plausibilität, IBAN-Prüfung, OpenAPI-Specs, Swagger-UI.

bash aiq-billing-mock/scripts/smoke-test.sh
# ==== RESULT: 49 passed, 0 failed ====

Scope & Grenzen