Transaktions-Zuordnungen (TransactionAllocation)
Zweck
TransactionAllocation modelliert die buchhalterische Zuordnung einer Bank- oder Pleo-Transaktion zu einem Beleg (Sales- oder Purchase-Document), einem Auftrag oder einer Auslage — und legt fest, auf welches SKR03-Konto plus Steuersatz gebucht wird.
Die Zuordnung läuft entweder
- automatisch durch die KI-Allocation-Proposal-Engine (bei hoher Confidence ≥ 95 Punkte),
- vorgeschlagen vom Sync — die User bestätigt im Transaktions-Cockpit, oder
- manuell im Allocations-Tab der Transaktions-Detail-Seite.
Sobald eine Allocation gebucht wird, erzeugt der Booking-Service symmetrische Konten-Buchungen — typischerweise Bank ↔ Debitor/Kreditor + GL-Buchung auf das SKR-Konto inklusive Vorsteuer-Split.
Status — wo lebt der Workflow?
Wichtig: TransactionAllocation selbst hat kein Status-Feld. Der Workflow-Status lebt auf der Transaktion (Transaction.matchingStatus) und wird durch die Kombination aus „Allocation existiert" + „AccountEntries existieren" gespiegelt.
matchingStatus (auf Transaction) | Bedeutung | Allocation existiert? | Gebucht? |
|---|---|---|---|
unmatched | keine Zuordnung, kein Vorschlag | nein | nein |
proposal_created | Allocation als Vorschlag angelegt, User soll bestätigen | ja | nein |
auto_matched | KI hat autonom zugeordnet UND gebucht (Score ≥ 95) | ja | ja |
manually_matched | User hat im Cockpit/Tab „Bestätigen & Buchen" geklickt | ja | ja |
skipped | User hat bewusst übersprungen | optional | nein |
Voraussetzungen
Berechtigungen (CASL)
| Action | Subject | Wirkung |
|---|---|---|
view | FE_TransactionAllocation | Allocation-Listen und -Detail aufrufbar |
view | TransactionAllocation | Datensätze lesen |
create | TransactionAllocation | Manuelle Zuordnung anlegen, „Bestätigen & Buchen" |
update | TransactionAllocation | Konto/Tax/Document nachträglich ändern |
delete | TransactionAllocation | Zuordnung löschen (cascade: AccountEntries werden mit weggeräumt; matchingStatus fällt zurück auf unmatched) |
update | Transaction | Skip / matchingStatus-Flip im Cockpit |
do | AiSuggestion | POST /transactions/:id/ai-classify für KI-Neu-Klassifizierung |
Datenmodell
| Feld | Typ | Bedeutung |
|---|---|---|
id | UUID | Primary Key |
transactionId | UUID | FK → Transaction |
documentType | ENUM nullable | SalesDocument | PurchaseDocument | Workorder — Zielobjekt der Zuordnung |
documentId | UUID nullable | FK auf das gewählte Dokument |
accountId | UUID nullable | FK → Account — SKR-Konto (Pflicht außer bei Workorder-Only) |
taxRateId | UUID nullable | FK → TaxRate — Vorsteuer-Split |
allocatedAmount | Decimal | Betrag (Teil-Allocation möglich, in Summe gleich Transaction.amount) |
currency | String | EUR / CHF / … (i. d. R. identisch mit der Transaction) |
note | TEXT nullable | freie Notiz (z. B. „Anteilig Auftrag #00045") |
isSkonto | Boolean | Wenn true: Booking auf Skonto-Konto 8736 + USt-Korrektur |
createdAt, updatedAt, deletedAt | DateTime | Standard + Soft-Delete |
Die KI-Begründung lebt nicht auf der Allocation, sondern auf TransactionAiAssessment (classificationReasoning, classificationConfidence) und wird über den Proposal-Endpoint mitgeliefert.
Allocation-Proposal — Score-Engine
Pro Transaktion liefert GET /api/transactions/:id/allocation-proposal einen Vorschlag, der aus drei Signalen kombiniert wird:
| Signal | Punkte | Beschreibung |
|---|---|---|
| Expense-Match (deterministisch) | 0 / 60 / 95 | Hard (95): Name-Token-Overlap + Betrag ±2 %; Soft (60): nur Betrag (Pleo-Pfad). Zieht defaultAccountId und taxRateId aus dem Expense-Datensatz |
| AI-Confidence | 0 – 30 | classificationConfidence × 0,3 aus TransactionAiAssessment (Anthropic-Klassifizierung). Füllt Lücken bei suggestedAccountId, -TaxRateId, -WorkorderId |
| Account-Default | 0 / 10 | transactionAccountFrom.defaultOffsetAccountId als Fallback-Konto (z. B. „Pleo Geldtransit contra → 1361") |
Gesamtscore = clamp(Summe, 0, 100).
Auto-Apply läuft nur bei Score ≥ 95 UND suggestedAccountId !== null — praktisch nur bei einem Expense-Hard-Match. Reine KI-Vorschläge bleiben bewusst im Modus „Vorschlag" und brauchen Bestätigung im Cockpit.
Details: KI-Allocation-Proposal-Engine.
Drei Apply-Pfade
| Pfad | Trigger | Action | resultierender matchingStatus |
|---|---|---|---|
| Auto-Apply | Score ≥ 95 (Sync) | Allocation anlegen + buchen + Tx-Status setzen | auto_matched |
| Cockpit-Bestätigung | User klickt „Bestätigen & Buchen" | bestehende Proposal-Allocation übernehmen + buchen | manually_matched |
| Proposal-Only | Score 1 – 94 (Sync) | Allocation anlegen, nicht buchen | proposal_created |
Idempotent: Findet der Service eine bestehende Allocation zur Transaktion, wird sie wiederverwendet — keine Duplikate.
Schritt-für-Schritt-Anleitung
Im Cockpit bestätigen
- Transaktions-Cockpit öffnen, Transaktion mit Status „Vorschlag" auswählen.
- Im AiClassificationCard rechts den vorgeschlagenen SKR-Konto, Steuersatz, Auftrag/Beleg prüfen — inklusive Score-Balken, Signal-Breakdown und KI-Begründung.
- Bestätigen & Buchen klicken →
POST /transactions/:id/allocation-proposal/applymitforceBookAndMatch=true. - Allocation wird gebucht,
AccountEntrieswerden erzeugt, Tx-Status wechselt aufmanually_matched.
Manuell zuordnen (Allocations-Tab)
- Transaktion-Detail-Seite öffnen → Tab Zuordnungen.
- + Zuordnung klicken — Modal
ManualAllocationFormModal. - Ziel wählen:
- Workorder (nur bei ausgehenden Transaktionen / Aufwand) — SKR-Konto optional (Cost-Tracking ohne GL-Eintrag möglich).
- PurchaseDocument (ausgehend) — SKR-Konto Pflicht.
- SalesDocument (eingehend) — SKR-Konto Pflicht.
- SKR-Konto + Steuersatz wählen, Betrag bestätigen, Notiz optional.
- Speichern →
POST /api/transaction-allocations. Die Allocation wird angelegt, aber nicht gebucht. - Buchung anstoßen über Cockpit (
Bestätigen & Buchen) oder über die Detail-Sicht.
Allocation stornieren (Reverse)
POST /api/transaction-allocations/:id/reverse erzeugt eine Spiegel-Allocation mit umgekehrtem Debit/Credit. Die ursprüngliche Allocation bleibt zur Nachvollziehbarkeit erhalten.
RefLinks im UI
Die TransactionAllocationSummary-Komponente zeigt für jeden Vorschlag/jede Allocation klickbare Icons zu den verknüpften Modulen — Sprung in neuem Tab:
| Icon | Ziel |
|---|---|
| 📒 | Konto — SKR-Detail |
| 💶 | Steuersatz |
| 🛠 | Auftrag |
| 🧾 | Auslage |
So lässt sich aus der Buchungs-Bestätigung schnell ins Quell-Modul springen, ohne den Cockpit-Workflow zu verlassen.
Booking-Verhalten
Beim Buchen erzeugt booking.service symmetrische AccountEntry-Paare:
| Fall | Debet | Kredit |
|---|---|---|
| Eingang (Sales-Document) | 1200 Bank | 1400 Debitor |
| Ausgang (Purchase-Document) | 1600 Kreditor | 1200 Bank |
| GL-Eintrag (SKR-Konto) | SKR-Konto (z. B. 4830 Werbung) | Steuer-Splitting via taxRateId |
Skonto (isSkonto = true) | 8736 Skonto-Aufwand | USt-Korrektur |
Die description der erzeugten Konten-Buchungen folgt seit Welle 129 dem Muster <fromAccount.displayName> — <tx.reference> (z. B. „Pleo – Andreas Hey — OBI SAGT DANKE"); freie note der Allocation überschreibt sie.
Workflows und Zustände
API/Schnittstellen
| Methode | Endpoint | Zweck | CASL |
|---|---|---|---|
GET | /api/transaction-allocations | Liste (filterbar nach transactionId, documentType, accountId) | view TransactionAllocation |
POST | /api/transaction-allocations | Manuelle Zuordnung anlegen (kein Auto-Book) | create TransactionAllocation |
GET | /api/transaction-allocations/:id | Detail | view TransactionAllocation |
PATCH | /api/transaction-allocations/:id | Felder ändern | update TransactionAllocation |
DELETE | /api/transaction-allocations/:id | Löschen (cascade auf AccountEntries) | delete TransactionAllocation |
POST | /api/transaction-allocations/:id/reverse | Spiegel-Buchung erzeugen | update TransactionAllocation |
GET | /api/transactions/:id/allocation-proposal | Score-Engine-Vorschlag (mit isPersisted, matchingStatus) | view Transaction, view TransactionAiAssessment |
POST | /api/transactions/:id/allocation-proposal/apply | Übernehmen + buchen (forceBookAndMatch=true) | create TransactionAllocation, update Transaction |
POST | /api/transactions/:id/ai-classify | KI-Klassifizierung neu anstoßen | do AiSuggestion |
Verknüpfungen zu anderen Modulen
- Transaktionen —
Transaction.matchingStatusspiegelt den Allocation-Status. - Transaktions-Cockpit — Arbeitsoberfläche, in der Vorschläge bestätigt werden.
- Konten-Buchungen — werden vom Booking-Service erzeugt, wenn eine Allocation gebucht wird.
- Match-Vorschläge — separater Workflow für das Matching gegen offene Posten, koexistiert mit Allocations.
- Konten, Steuersätze — Zielfelder der Allocation.
- Aufträge, Auslagen, Verkaufsbelege, Einkaufsbelege — Zieldokumente.
Häufige Fehler und Lösungen
| Fehler | Lösung |
|---|---|
| „Bestätigen & Buchen" nicht klickbar | Variante ist compact (linke Spalte) — Button erscheint nur in der rechten Panel-Variante. Allocation ist bereits gebucht (isBooked = true). |
| Karten zeigen widersprüchliche Status | matchingStatus und isPersisted aktualisieren über Query-Cache — Cockpit-Reload bringt Konsistenz. |
| KI gibt keinen Vorschlag | TransactionAiAssessment fehlt — POST /transactions/:id/ai-classify neu anstoßen. |
| Allocation löschen funktioniert nicht | AccountEntries hängen dran. Erst reverse, dann delete — oder direkt delete (cascade). |
| Manuelle Allocation ohne Konto erlaubt? | Nur bei documentType = Workorder — Cost-Tracking ohne GL-Buchung. Bei Sales/Purchase ist accountId Pflicht. |
| Skonto-Buchung fehlt | isSkonto = true setzen — Service bucht automatisch auf 8736 + USt-Korrektur. |
Versionshinweise
- 2026-05-16 (Welle 129): Initiale Veröffentlichung. Status-Modell auf
Transaction.matchingStatusumgestellt, Score-Engine mit drei Signalen (Expense-Match / AI-Confidence / Account-Default), Auto-Apply nur bei Expense-Hard-Match, KI-Reasoning + Confidence durchgereicht,forceBookAndMatch-Pfad für Cockpit-Bestätigung, RefLink-Icons zu Konto/Tax/Workorder/Expense, „Bestätigen & Buchen"-Button nur in Panel-Variante. Quelle: BE-Commitsbbd6e713,1b962d16,c3c1aa0c,a0f4a675,3c7e6d7b; FE-Commitsc17ce160,871c0107,5e6efe9c,a54fb476,38df9cea,5922e752,c760900c, weitere.