Bestellbelege
Zweck
Bestellbelege sind die zentralen Einkaufsbelege in SpeamCore. Eine Belegart kennt vier Auspraegungen:
| documentType | Bezeichnung | Lifecycle | Typischer Anwendungsfall |
|---|---|---|---|
order | Bestellung | Erstellt → Gesendet → Storniert | Standard-Beschaffung beim Lieferanten |
orderRoute | Bestellung (Strecke) | Erstellt → Gesendet → Storniert | Streckengeschaeft (Lieferant liefert direkt an Kunde) |
priceRequest | Preisanfrage | Erstellt → Gesendet | Anfrage vor Bestellung, ohne Storno |
return | Retoure | Erstellt → Gesendet | Ruecksendung an Lieferant, ohne Storno |
Pro Beleg fuehren Sie Positionen, Textbloecke und Anhänge.
Voraussetzungen
Berechtigungen (CASL)
Frontend-Page-Guard (aus requiredAbility in routes.tsx):
| Action | Subject | Wirkung | Keycloak-Rolle |
|---|---|---|---|
view | FE_PurchaseDocument | Seite aufrufbar | — |
view | PurchaseDocument | Listendaten lesbar | APP_SPEAMCORE_VIEW_PURCHASE_DOCUMENT |
view | PurchaseDocumentItem | Tab „Positionen" aufrufbar | APP_SPEAMCORE_VIEW_PURCHASE_DOCUMENT_ITEM |
view | TextBlockParent | Tab „Textbloecke" aufrufbar | APP_SPEAMCORE_VIEW_TEXT_BLOCK_PARENT |
view | Document, DocumentParent | Tab „Dokumente" aufrufbar | APP_SPEAMCORE_VIEW_DOCUMENT, DOCUMENT_PARENT |
API-Datenzugriff (caslMiddleware im BE-Router):
| Action | Subject | Endpoint(s) | Keycloak-Rolle |
|---|---|---|---|
view | PurchaseDocument | GET /api/purchase-documents, GET /api/purchase-documents/:id | APP_SPEAMCORE_VIEW_PURCHASE_DOCUMENT |
create | PurchaseDocument | POST /api/purchase-documents, POST /api/purchase-documents/parse | APP_SPEAMCORE_CREATE_PURCHASE_DOCUMENT |
update | PurchaseDocument | PATCH /api/purchase-documents/:id, POST /:id/apply-text-block-defaults | APP_SPEAMCORE_UPDATE_PURCHASE_DOCUMENT |
delete | PurchaseDocument | DELETE /api/purchase-documents/:id | APP_SPEAMCORE_DELETE_PURCHASE_DOCUMENT |
do | LockPurchaseDocument | POST /api/purchase-documents/:id/lock | APP_SPEAMCORE_DO_LOCK_PURCHASE_DOCUMENT |
do | UnlockPurchaseDocument | POST /api/purchase-documents/:id/unlock | APP_SPEAMCORE_DO_UNLOCK_PURCHASE_DOCUMENT |
do | CopyPurchaseDocument | POST /api/purchase-documents/:id/copy | APP_SPEAMCORE_DO_COPY_PURCHASE_DOCUMENT |
do | TakeOverPurchaseDocument | POST /api/purchase-documents/:id/takeover | APP_SPEAMCORE_DO_TAKE_OVER_PURCHASE_DOCUMENT |
Schritt-für-Schritt-Anleitung
Bestellbeleg anlegen
- Öffnen Sie Einkaufsbelege in der Sidebar (Pfad
/purchase-documents). - Klicken Sie + Neu rechts oben.
- Wählen Sie den documentType (Bestellung / Bestellung Strecke / Preisanfrage / Retoure).
- Bestaetigen Sie — der Beleg wird mit Status
createdund automatisch vergebener Nummer angelegt. - Pflegen Sie auf der Detailseite die Pflichtfelder (siehe Felder und Eingaben).
- Wechseln Sie auf den Tab Positionen (oder Pfad
/purchase-documents/:id/items), um Artikel hinzuzufuegen. - Optional: Tab Textbloecke für Belegtexte und Dokumente für Anhänge.
Bestellbeleg aus PDF/XML einlesen (Parsen)
- Klicken Sie auf der Listenseite Parse/Upload.
- Wählen Sie eine PDF- oder XML-Datei (UBL-Format: Invoice, Order, RequestForQuotation, InstructionForReturns).
- Prüfen Sie das automatisch erkannte Mapping im DocumentParserModal.
- Bestaetigen Sie — der Beleg wird angelegt, Positionen und Adresse aus dem Dokument übernommen.
Bestellbeleg sperren / entsperren
- Öffnen Sie den Beleg.
- Klicken Sie im Header Sperren (oder Entsperren).
- Gesperrte Belege koennen nicht mehr geändert oder gelöscht werden — sie sind buchhalterisch fixiert.
Bestellbeleg umwandeln (Takeover)
- Öffnen Sie den Beleg.
- Klicken Sie im Header Übernehmen.
- Wählen Sie das Ziel-
documentType(z. B. vonpriceRequestzuorder). - Der neue Beleg wird mit kopierten Positionen und Textbloecken angelegt; ein Process verbindet beide.
Bestellbeleg kopieren
- Öffnen Sie den Beleg.
- Klicken Sie im Header Kopieren.
- Wählen Sie den
documentTypeder Kopie (allowedCopysregelt die zulässigen Ziele). - Sie werden auf den neuen Beleg umgeleitet (Status
created).


Toolbar (Detail-Seite)
Die Einkaufsbeleg-Detailseite hat oben rechts eine Toolbar mit allen Folgeaktionen. Pro Icon eine eigene Aktion:
| Icon | Aktion (aria-label) | CASL | Wirkung |
|---|---|---|---|
| ← | Zurückgehen | — | Zurück zur Einkaufsbeleg-Liste. |
| 🏠 | Zur Startseite gehen | — | Springt auf das Dashboard / /. |
| ↻ | Prozess anzeigen | view:PurchaseDocument | Visualisiert den aktuellen Status im Lifecycle (Bestellung → Wareneingang → Eingangsrechnung → bezahlt). |
| 📄 | Einkaufsbeleg Vorschau | view:PurchaseDocument | PDF-Vorschau des Belegs ohne Druck/Download. |
| 🖨 | Einkaufsbeleg ausdrucken | view:PurchaseDocument | Generiert finales PDF und triggert Download/Druck. Setzt printedAt. |
| 📋 | Einkaufsbeleg kopieren in | create:PurchaseDocument | Belegtyp-Kopie: Erzeugt einen neuen Einkaufsbeleg desselben oder anderen Typs (z. B. „Preisanfrage kopieren in Bestellung") mit gleichen Lieferanten-/Positions-Daten. Quell-Beleg bleibt unangetastet. |
| ↗ | Einkaufsbeleg übernehmen in | create:PurchaseDocument | Folgebeleg-Kette: Erzeugt den nächsten Beleg im Einkaufs-Lifecycle (Preisanfrage → Bestellung → Wareneingang → Eingangsrechnung). Setzt parentPurchaseDocumentId; Quell-Beleg wird ggf. gesperrt. |
| 🔒 | Einkaufsbeleg sperren | update:PurchaseDocument | Setzt locked = true. Beleg ist read-only. Drucken und „übernehmen in" setzen locked = true automatisch. |
| ⏮/◀/▶/⏭ | Pagination | — | Navigation durch die gefilterte Belegliste. |
Globale Floating-Drawer (links)
Wie auf jeder Detail-Seite verfuegbar — siehe Floating-Quickbar:
- KAL. (Mini-Kalender)
- ZEIT (Persoenliche Wochen-Arbeitszeit)
- ARBEIT (Eigene bevorstehende Aufträge)
UI-Elemente (Listenseite)
Button: „+ Neu"
Listenseite, oben rechts. Erfordert create:PurchaseDocument. Öffnet ein Dropdown mit Belegtypen (Bestellung, Bestellung (Strecke), Preisanfrage, Retoure). Klick auf Belegtyp legt direkt den Beleg an, ohne Modal-Dialog.
Button: „Parse/Upload"
Listenseite. Öffnet das DocumentParserModal, in dem PDF/XML-Dokumente eingelesen werden. API: POST /api/purchase-documents/parse. Erfordert create:PurchaseDocument.
Button: „Vorschau"
Detailseite-Header. Öffnet ein Modal mit PDF-Vorschau (PDF.js). API: POST /print-pdf (asynchroner Job).
Button: „Drucken"
Detailseite-Header. Erzeugt PDF und laedt es herunter. API: POST /print-pdf + GET /documents/:id.
Button: „Kopieren"
Detailseite-Header. Erfordert (implizit) do:CopyPurchaseDocument. API: POST /api/purchase-documents/:id/copy. MenuButton mit Auswahl des Ziel-documentType.
Button: „Übernehmen" (Takeover)
Detailseite-Header. Erfordert (implizit) do:TakeOverPurchaseDocument. API: POST /api/purchase-documents/:id/takeover. Konvertiert den Beleg in einen anderen documentType und verknuepft über Process.
Button: „Sperren" / „Freigeben"
Detailseite-Header. Erfordert do:LockPurchaseDocument. API: POST /api/purchase-documents/:id/lock. Setzt locked = true. Anschliessend sind Änderungen und Loeschung blockiert.
- prüft SpeamCore, dass ein Aufwandskonto (
expenseAccountId) gesetzt ist (sonst Fehler), - bucht mit Soll-Versteuerung (Soll Aufwand + Soll Vorsteuer an Haben Kreditor) —
createAccountEntriesForPurchaseDocument, - leitet die Fälligkeit aus dem Zahlungsziel ab und
- legt einen offenen Posten an (
OpenItem,parentType = 'PurchaseDocument', payable) → erscheint in den Verbindlichkeiten.
Der Vorgang ist idempotent (maßgeblich ist, ob bereits ein OpenItem existiert — kein doppeltes Buchen). Reine Bestellungen (order) werden weiterhin nur gesperrt und nicht verbucht.
Button: „Entsperren"
Detailseite-Header. Erfordert do:UnlockPurchaseDocument. API: POST /api/purchase-documents/:id/unlock. Setzt locked = false. Bei einer Eingangsrechnung wird damit die Verbuchung rückgängig gemacht: offener Posten und Buchungssätze entfallen (bereits erfasste Zahlungen bleiben bestehen und müssen separat storniert werden).
Button: „Process"
Detailseite-Header. Sichtbar mit view:Statistics, view:Process, view:ProcessItem. Zeigt das Process-Element, das diesen Beleg verbindet (Lifecycle-Sicht über mehrere Belege hinweg).
Felder und Eingaben
| Feldname | Pflicht | Sichtbarkeit | Wirkung beim Ausfuellen | Voraussetzung |
|---|---|---|---|---|
documentType | ja (disabled nach Anlage) | immer | order / orderRoute / priceRequest / return. Bestimmt Statusoptionen, sichtbare Felder und Nummernkreis. | Nach Anlage nicht änderbar — für Konvertierung den Takeover-Workflow nutzen. |
purchaseDocumentNo | ja | immer | Belegnummer aus Nummernkreis. Erscheint auf PDF, in Reports und in OP-Listen. | Nummernkreis für den jeweiligen documentType konfiguriert. |
status | nein | immer | created, sent, cancelled (cancelled nur für order/orderRoute). Wechsel auf sent setzt Buchungs-Trigger. | Erlaubte Status pro documentType (siehe Workflow). |
documentDate | nein | immer | Default: heute. Anker für dueDate-Berechnung. | — |
parentId + parentType | nein | immer | Empfaenger: Niederlassung, Kunde oder Lager (polymorph). Triggert im beforeCreate-Hook die Adress-Snapshot-Felder. | view auf den jeweiligen Parent-Typ. |
contactParentId | nein | wenn parent ≠ Lager | Ansprechpartner; erscheint im Belegfooter und in E-Mails. | view:Contact. |
parentName/Street/Zip/City/CountryId | — | wenn parent gesetzt | Adress-Snapshot vom Parent. Änderungen am Parent wirken nicht rueckwirkend. | — |
supplierId | nein | immer | Lieferant. Wird auf dem Beleg als Empfaenger gedruckt. | view:Supplier. |
branchId | nein | immer | Niederlassung/Betrieb. Default aus Mitarbeiter-Vertrag. Steuert Reporting nach Niederlassung. | view:Branch. |
employeeId | nein | immer | Verantwortlicher Mitarbeiter. Verantwortung für Wiedervorlagen und Eskalationen. | view:Employee. |
costCenterId | nein | immer | Kostenstelle. Default aus Mitarbeiter-Vertrag. | view:CostCenter. |
expenseAccountId | nein | immer | Aufwandskonto für Buchungen. | view:Account. |
paymentTargetId | nein | nicht bei priceRequest | Steuert dueDate über documentDate + paymentTarget.days. | view:PaymentTarget. |
resubmissionDate | nein | nur priceRequest | Wiedervorlage-Datum — Beleg erscheint danach in der Wiedervorlage-Liste. | — |
deliveryDate | nein | nur order/orderRoute | Liefer-Wunschtermin. | — |
confirmedDeliveryDate | nein | nur order/orderRoute | Vom Lieferanten bestaetigter Termin — Anker für Verzugs-Indikatoren. | — |
customerReference | nein | immer | Kundenreferenz (EN-16931 BT-10). Freitext-Feld im UI; in älteren Mandanten-Versionen noch als „Betreff" beschriftet. | — |
supplierDocumentNumber | nein | immer | „Lieferanten Dokument Nr." — die Belegnummer des Lieferanten zu diesem Beleg (z. B. dessen Auftragsbestätigungs-Nr.). | — |
dueDate | berechnet | immer | documentDate + paymentTarget.days. | — |
locked | — | immer | true schliesst alle Änderungen und Loeschung aus. Wird typischerweise im Buchungs-Service gesetzt. | Beim update/delete wird der Wert vom beforeUpdate/beforeDestroy-Hook geprüft. |
Bei locked = true sind alle Felder ausser documentType und purchaseDocumentNo disabled.
Seitenleiste: Projektzuordnungen + Kommentare
Rechts neben dem Formular zeigt die Detailseite zwei Karten:
- Projektzuordnungen — verknüpft den Beleg mit einem oder mehreren Projekten (über
Klicken zum Hinzufügen). Solange keine Zuordnung besteht, steht „Keine Projektzuordnungen". Dient der Projekt-Kostenauswertung. - Kommentare — interne Notizen zum Beleg. Über den Schalter „Interne ausblenden" lassen sich interne Kommentare ein-/ausblenden.
Workflows und Zustaende
Lock-Lifecycle (orthogonal zum Status):
Im Status cancelled bleibt der Beleg lesbar, ist aber nicht mehr editierbar.
Belegfreigabe (seit Juni 2026)
Eingehende Rechnungen (documentType = incomingInvoice) durchlaufen vor der Verbuchung einen Freigabe-Workflow. Der Beleg trägt dafür eigene Felder:
| Feld | Datentyp | Bedeutung |
|---|---|---|
approvalStatus | Enum (pending, approved, rejected), Default pending | Freigabe-Status |
approvalNote | Text, nullable | Notiz/Begründung (bei Ablehnung Pflicht) |
approvedByEmployeeId | UUID, nullable | wer freigegeben/abgelehnt hat |
approvedAt | DateTime, nullable | Zeitpunkt der Freigabe/Ablehnung |
taxRateId | UUID, nullable | Steuersatz-Override direkt am Beleg — geht dem Steuersatz des Aufwandskontos vor |
Freigegeben/abgelehnt wird im Belegfreigabe-Cockpit über POST /purchase-documents/:id/approve bzw. …/reject (CASL update:PurchaseDocument). Welche Voraussetzungen (Belegdokument, Zuordnung) Pflicht sind, steuern die Belegfreigabe-Einstellungen. Freigabe und Verbuchung sind getrennt — die Buchung läuft weiterhin über das Transaktions-Cockpit.
Wiederverwendbare Konzepte
- Berechtigungen verstehen (CASL) — Subject-Pattern und Keycloak-Mapping.
- Filter, Tabellen, Spaltenkonfiguration (
/grundlagen/...) — folgen mit nächster Live-Session.
Verknuepfungen zu anderen Modulen
- Lieferanten (
/suppliers) — Pflicht-Referenz fürsupplierId. - Niederlassungen / Kunden / Lager —
parentId(polymorph). - Artikel & Leistungen — Positionen verweisen auf Produkte.
- Setup-Wizard Schritt 8 (Zahlungsbedingungen) — fuellt
paymentTargetId-Auswahl. - Setup-Wizard Schritt 4 (Buchhaltung/SKR03) — fuellt
expenseAccountId-Auswahl. - Setup-Wizard Schritt 11 (Nummernkreise) — definiert
purchaseDocumentNo-Format. - Process — verbindet zusammenhaengende Belege über Takeover.
- Aufträge — eine Eingangsrechnung kann mehreren Arbeitsaufträgen zugeordnet werden (n:m,
PurchaseDocumentWorkorder) → siehe unten.
Arbeitsauftrags-Zuordnung (n:m, Juni 2026)
Über eine n:m-Verknüpfung (PurchaseDocumentWorkorder) lässt sich ein Einkaufsbeleg — typischerweise eine Eingangsrechnung — als Kostenreferenz mit einem oder mehreren Aufträgen verbinden. Die Verknüpfung dient der Kostenzuordnung; sie verändert den Beleg selbst nicht. Pflege im Cockpit (Freigabe-Panel) oder über die API:
| Methode | Endpoint | Zweck | CASL |
|---|---|---|---|
GET | /api/purchase-document-workorders | Verknüpfungen lesen | view PurchaseDocument |
POST | /api/purchase-document-workorders | Verknüpfung anlegen | update PurchaseDocument |
DELETE | /api/purchase-document-workorders/:id | Verknüpfung entfernen | update PurchaseDocument |
Häufige Fehler und Lösungen
| Fehler | Lösung |
|---|---|
| „Sie koennen diesen Beleg nicht ändern" | locked = true. Erst entsperren (Recht do:UnlockPurchaseDocument). |
Konvertierung in documentType X nicht möglich | allowedConversions blockiert. Prüfen, ob die Quell-/Zielkombination zulässig ist. |
Status cancelled nicht wählbar | documentType ist priceRequest oder return — diese kennen kein cancelled. |
| Parser erkennt das PDF nicht | UBL-Format prüfen. Bei Freiform-PDFs manuell anlegen. |
| Belege werden nicht gedruckt | Print-Job prüfen — POST /print-pdf ist asynchron, das fertige Dokument wird über GET /documents/:id abgeholt. |
dueDate weicht vom erwarteten Datum ab | Der Wert wird aus documentDate + paymentTarget.days berechnet. Prüfen Sie den zugewiesenen paymentTargetId. |
API/Schnittstellen
| Methode | Endpoint | Zweck | CASL |
|---|---|---|---|
GET | /api/purchase-documents | Liste mit Pagination/Filter | view PurchaseDocument |
GET | /api/purchase-documents/:id | Einzelbeleg | view PurchaseDocument |
POST | /api/purchase-documents | Anlegen | create PurchaseDocument |
PATCH | /api/purchase-documents/:id | Ändern (geblockt bei locked) | update PurchaseDocument |
DELETE | /api/purchase-documents/:id | Löschen (Paranoid Soft-Delete, geblockt bei locked) | delete PurchaseDocument |
POST | /api/purchase-documents/parse | PDF/XML-Parser, gibt documentData zurück | create PurchaseDocument |
POST | /api/purchase-documents/:id/lock | locked = true | do LockPurchaseDocument |
POST | /api/purchase-documents/:id/unlock | locked = false | do UnlockPurchaseDocument |
POST | /api/purchase-documents/:id/copy | Vollständige Kopie als neuer Beleg | do CopyPurchaseDocument |
POST | /api/purchase-documents/:id/takeover | Konvertierung (z. B. priceRequest → order) | do TakeOverPurchaseDocument |
POST | /api/purchase-documents/:id/apply-text-block-defaults | Standard-Textbloecke übernehmen | update PurchaseDocument |
GET | /api/purchase-documents/:id/approval-status | Status-Aggregat für die Belegfreigabe | view PurchaseDocument |
POST | /api/purchase-documents/:id/approve | Beleg freigeben | update PurchaseDocument |
POST | /api/purchase-documents/:id/reject | Beleg ablehnen (Begründung Pflicht) | update PurchaseDocument |
GET | /api/belegfreigabe/flags | Bulk-Vollständigkeits-Flags je Beleg | view PurchaseDocument |
Sub-Resources werden über separate Routes verwaltet:
GET /api/purchase-documents/:id/items(Positionen) —view PurchaseDocumentItemGET /api/text-block-parents?parentId=:id&parentType=PurchaseDocument(Textbloecke) —view TextBlockParentGET /api/document-parents?parentId=:id&parentType=PurchaseDocument(Anhänge) —view DocumentParent
Versionshinweise
- 2026-06-12: Belegfreigabe-Workflow ergänzt — Felder
approvalStatus/approvalNote/approvedByEmployeeId/approvedAt+ Steuersatz-OverridetaxRateId; Endpoints…/approve,…/reject,…/approval-status,/belegfreigabe/flags. Freigabe und Verbuchung sind getrennt; Cockpit: Belegfreigabe. Verifiziert anpurchaseDocument.model.ts,purchaseDocument.router.ts,purchaseDocumentApproval.service.ts. - 2026-06-03: „Sperren" bei Eingangsrechnungen ist jetzt zugleich Verbuchen/Freigeben — erzeugt Buchungssätze (Soll-Versteuerung), Fälligkeit und einen offenen Posten (Verbindlichkeiten); Aufwandskonto Pflicht, idempotent, beim Entsperren rückgängig. Zusätzlich: n:m-Zuordnung zu Aufträgen (
PurchaseDocumentWorkorder). Verifiziert anpurchaseDocument.controller.ts+purchaseDocumentWorkorder.router.ts(master). - 2026-04-29: Initiale Veroeffentlichung. Grundlage: Code-Analyse aus
speamcore.comundapi.speamcore.com(master, Standf53c55dc/b51c43ac). IST-Screenshots stehen aus.