Zum Hauptinhalt springen

Bestellbelege

Zweck

Bestellbelege sind die zentralen Einkaufsbelege in SpeamCore. Eine Belegart kennt vier Auspraegungen:

documentTypeBezeichnungLifecycleTypischer Anwendungsfall
orderBestellungErstellt → Gesendet → StorniertStandard-Beschaffung beim Lieferanten
orderRouteBestellung (Strecke)Erstellt → Gesendet → StorniertStreckengeschaeft (Lieferant liefert direkt an Kunde)
priceRequestPreisanfrageErstellt → GesendetAnfrage vor Bestellung, ohne Storno
returnRetoureErstellt → GesendetRuecksendung an Lieferant, ohne Storno

Pro Beleg fuehren Sie Positionen, Textbloecke und Anhänge.

Voraussetzungen

- Mindestens ein **Lieferant** (siehe `/suppliers`). - Mindestens eine **Niederlassung** und/oder ein **Lager** als Empfaenger (`parentId`). - Eingerichtete **Zahlungsbedingungen** (siehe Setup-Wizard, Schritt 8) — für alle `documentType` ausser `priceRequest`. - Konfigurierter **Nummernkreis** für die jeweilige Belegart (siehe Setup-Wizard, Schritt 11).

Berechtigungen (CASL)

Frontend-Page-Guard (aus requiredAbility in routes.tsx):

ActionSubjectWirkungKeycloak-Rolle
viewFE_PurchaseDocumentSeite aufrufbar
viewPurchaseDocumentListendaten lesbarAPP_SPEAMCORE_VIEW_PURCHASE_DOCUMENT
viewPurchaseDocumentItemTab „Positionen" aufrufbarAPP_SPEAMCORE_VIEW_PURCHASE_DOCUMENT_ITEM
viewTextBlockParentTab „Textbloecke" aufrufbarAPP_SPEAMCORE_VIEW_TEXT_BLOCK_PARENT
viewDocument, DocumentParentTab „Dokumente" aufrufbarAPP_SPEAMCORE_VIEW_DOCUMENT, DOCUMENT_PARENT

API-Datenzugriff (caslMiddleware im BE-Router):

ActionSubjectEndpoint(s)Keycloak-Rolle
viewPurchaseDocumentGET /api/purchase-documents, GET /api/purchase-documents/:idAPP_SPEAMCORE_VIEW_PURCHASE_DOCUMENT
createPurchaseDocumentPOST /api/purchase-documents, POST /api/purchase-documents/parseAPP_SPEAMCORE_CREATE_PURCHASE_DOCUMENT
updatePurchaseDocumentPATCH /api/purchase-documents/:id, POST /:id/apply-text-block-defaultsAPP_SPEAMCORE_UPDATE_PURCHASE_DOCUMENT
deletePurchaseDocumentDELETE /api/purchase-documents/:idAPP_SPEAMCORE_DELETE_PURCHASE_DOCUMENT
doLockPurchaseDocumentPOST /api/purchase-documents/:id/lockAPP_SPEAMCORE_DO_LOCK_PURCHASE_DOCUMENT
doUnlockPurchaseDocumentPOST /api/purchase-documents/:id/unlockAPP_SPEAMCORE_DO_UNLOCK_PURCHASE_DOCUMENT
doCopyPurchaseDocumentPOST /api/purchase-documents/:id/copyAPP_SPEAMCORE_DO_COPY_PURCHASE_DOCUMENT
doTakeOverPurchaseDocumentPOST /api/purchase-documents/:id/takeoverAPP_SPEAMCORE_DO_TAKE_OVER_PURCHASE_DOCUMENT

Schritt-für-Schritt-Anleitung

Bestellbeleg anlegen

  1. Öffnen Sie Einkaufsbelege in der Sidebar (Pfad /purchase-documents).
  2. Klicken Sie + Neu rechts oben.
  3. Wählen Sie den documentType (Bestellung / Bestellung Strecke / Preisanfrage / Retoure).
  4. Bestaetigen Sie — der Beleg wird mit Status created und automatisch vergebener Nummer angelegt.
  5. Pflegen Sie auf der Detailseite die Pflichtfelder (siehe Felder und Eingaben).
  6. Wechseln Sie auf den Tab Positionen (oder Pfad /purchase-documents/:id/items), um Artikel hinzuzufuegen.
  7. Optional: Tab Textbloecke für Belegtexte und Dokumente für Anhänge.

Bestellbeleg aus PDF/XML einlesen (Parsen)

  1. Klicken Sie auf der Listenseite Parse/Upload.
  2. Wählen Sie eine PDF- oder XML-Datei (UBL-Format: Invoice, Order, RequestForQuotation, InstructionForReturns).
  3. Prüfen Sie das automatisch erkannte Mapping im DocumentParserModal.
  4. Bestaetigen Sie — der Beleg wird angelegt, Positionen und Adresse aus dem Dokument übernommen.

Bestellbeleg sperren / entsperren

  1. Öffnen Sie den Beleg.
  2. Klicken Sie im Header Sperren (oder Entsperren).
  3. Gesperrte Belege koennen nicht mehr geändert oder gelöscht werden — sie sind buchhalterisch fixiert.

Bestellbeleg umwandeln (Takeover)

  1. Öffnen Sie den Beleg.
  2. Klicken Sie im Header Übernehmen.
  3. Wählen Sie das Ziel-documentType (z. B. von priceRequest zu order).
  4. Der neue Beleg wird mit kopierten Positionen und Textbloecken angelegt; ein Process verbindet beide.
Nicht jede Umwandlung ist erlaubt. Die zulässigen Konvertierungen sind in `allowedConversions` (FE und BE, Datei `purchaseDocument.validate.ts`) definiert. Beispielsweise ist `priceRequest → order` zulässig, `order → priceRequest` nicht.

Bestellbeleg kopieren

  1. Öffnen Sie den Beleg.
  2. Klicken Sie im Header Kopieren.
  3. Wählen Sie den documentType der Kopie (allowedCopys regelt die zulässigen Ziele).
  4. Sie werden auf den neuen Beleg umgeleitet (Status created).

Listenansicht — purchase-documents

Einkaufsbeleg-Detail (Bestellung PO-2026-00001) — Tabs Allgemeine Daten / Positionen / Textblöcke / Dokumente, Header-Buttons (Vorschau, Drucken, PDF, Kopieren, Übernehmen, Sperren) und Formularfelder Dokumenttyp, Status, Kunde/Standort, Lieferant, Kostenstelle, Zahlungsziel, Aufwandskonto (Test-Daten).

Toolbar (Detail-Seite)

Die Einkaufsbeleg-Detailseite hat oben rechts eine Toolbar mit allen Folgeaktionen. Pro Icon eine eigene Aktion:

IconAktion (aria-label)CASLWirkung
ZurückgehenZurück zur Einkaufsbeleg-Liste.
🏠Zur Startseite gehenSpringt auf das Dashboard / /.
Prozess anzeigenview:PurchaseDocumentVisualisiert den aktuellen Status im Lifecycle (Bestellung → Wareneingang → Eingangsrechnung → bezahlt).
📄Einkaufsbeleg Vorschauview:PurchaseDocumentPDF-Vorschau des Belegs ohne Druck/Download.
🖨Einkaufsbeleg ausdruckenview:PurchaseDocumentGeneriert finales PDF und triggert Download/Druck. Setzt printedAt.
📋Einkaufsbeleg kopieren increate:PurchaseDocumentBelegtyp-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 increate:PurchaseDocumentFolgebeleg-Kette: Erzeugt den nächsten Beleg im Einkaufs-Lifecycle (Preisanfrage → Bestellung → Wareneingang → Eingangsrechnung). Setzt parentPurchaseDocumentId; Quell-Beleg wird ggf. gesperrt.
🔒Einkaufsbeleg sperrenupdate:PurchaseDocumentSetzt locked = true. Beleg ist read-only. Drucken und „übernehmen in" setzen locked = true automatisch.
⏮/◀/▶/⏭PaginationNavigation durch die gefilterte Belegliste.
Der Wareneingang (`GoodsReceipt`) bucht beim Speichern automatisch den Lagerbestand auf — `WarehouseProduct.amount += quantity` pro Position. Diese Buchung ist **nicht reversibel** durch reines Löschen des Belegs; eine Korrektur muss über einen Storno-Wareneingang erfolgen.

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.

Für Belege vom Typ **Eingangsrechnung** (`documentType = 'incomingInvoice'`) ist dieser Schritt zugleich das **Verbuchen** („Freigeben"). Beim Freigeben
  • 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

FeldnamePflichtSichtbarkeitWirkung beim AusfuellenVoraussetzung
documentTypeja (disabled nach Anlage)immerorder / orderRoute / priceRequest / return. Bestimmt Statusoptionen, sichtbare Felder und Nummernkreis.Nach Anlage nicht änderbar — für Konvertierung den Takeover-Workflow nutzen.
purchaseDocumentNojaimmerBelegnummer aus Nummernkreis. Erscheint auf PDF, in Reports und in OP-Listen.Nummernkreis für den jeweiligen documentType konfiguriert.
statusneinimmercreated, sent, cancelled (cancelled nur für order/orderRoute). Wechsel auf sent setzt Buchungs-Trigger.Erlaubte Status pro documentType (siehe Workflow).
documentDateneinimmerDefault: heute. Anker für dueDate-Berechnung.
parentId + parentTypeneinimmerEmpfaenger: Niederlassung, Kunde oder Lager (polymorph). Triggert im beforeCreate-Hook die Adress-Snapshot-Felder.view auf den jeweiligen Parent-Typ.
contactParentIdneinwenn parent ≠ LagerAnsprechpartner; erscheint im Belegfooter und in E-Mails.view:Contact.
parentName/Street/Zip/City/CountryIdwenn parent gesetztAdress-Snapshot vom Parent. Änderungen am Parent wirken nicht rueckwirkend.
supplierIdneinimmerLieferant. Wird auf dem Beleg als Empfaenger gedruckt.view:Supplier.
branchIdneinimmerNiederlassung/Betrieb. Default aus Mitarbeiter-Vertrag. Steuert Reporting nach Niederlassung.view:Branch.
employeeIdneinimmerVerantwortlicher Mitarbeiter. Verantwortung für Wiedervorlagen und Eskalationen.view:Employee.
costCenterIdneinimmerKostenstelle. Default aus Mitarbeiter-Vertrag.view:CostCenter.
expenseAccountIdneinimmerAufwandskonto für Buchungen.view:Account.
paymentTargetIdneinnicht bei priceRequestSteuert dueDate über documentDate + paymentTarget.days.view:PaymentTarget.
resubmissionDateneinnur priceRequestWiedervorlage-Datum — Beleg erscheint danach in der Wiedervorlage-Liste.
deliveryDateneinnur order/orderRouteLiefer-Wunschtermin.
confirmedDeliveryDateneinnur order/orderRouteVom Lieferanten bestaetigter Termin — Anker für Verzugs-Indikatoren.
customerReferenceneinimmerKundenreferenz (EN-16931 BT-10). Freitext-Feld im UI; in älteren Mandanten-Versionen noch als „Betreff" beschriftet.
supplierDocumentNumberneinimmer„Lieferanten Dokument Nr." — die Belegnummer des Lieferanten zu diesem Beleg (z. B. dessen Auftragsbestätigungs-Nr.).
dueDateberechnetimmerdocumentDate + paymentTarget.days.
lockedimmertrue 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:

FeldDatentypBedeutung
approvalStatusEnum (pending, approved, rejected), Default pendingFreigabe-Status
approvalNoteText, nullableNotiz/Begründung (bei Ablehnung Pflicht)
approvedByEmployeeIdUUID, nullablewer freigegeben/abgelehnt hat
approvedAtDateTime, nullableZeitpunkt der Freigabe/Ablehnung
taxRateIdUUID, nullableSteuersatz-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ür supplierId.
  • Niederlassungen / Kunden / LagerparentId (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:

MethodeEndpointZweckCASL
GET/api/purchase-document-workordersVerknüpfungen lesenview PurchaseDocument
POST/api/purchase-document-workordersVerknüpfung anlegenupdate PurchaseDocument
DELETE/api/purchase-document-workorders/:idVerknüpfung entfernenupdate PurchaseDocument

Häufige Fehler und Lösungen

FehlerLösung
„Sie koennen diesen Beleg nicht ändern"locked = true. Erst entsperren (Recht do:UnlockPurchaseDocument).
Konvertierung in documentType X nicht möglichallowedConversions blockiert. Prüfen, ob die Quell-/Zielkombination zulässig ist.
Status cancelled nicht wählbardocumentType ist priceRequest oder return — diese kennen kein cancelled.
Parser erkennt das PDF nichtUBL-Format prüfen. Bei Freiform-PDFs manuell anlegen.
Belege werden nicht gedrucktPrint-Job prüfen — POST /print-pdf ist asynchron, das fertige Dokument wird über GET /documents/:id abgeholt.
dueDate weicht vom erwarteten Datum abDer Wert wird aus documentDate + paymentTarget.days berechnet. Prüfen Sie den zugewiesenen paymentTargetId.

API/Schnittstellen

MethodeEndpointZweckCASL
GET/api/purchase-documentsListe mit Pagination/Filterview PurchaseDocument
GET/api/purchase-documents/:idEinzelbelegview PurchaseDocument
POST/api/purchase-documentsAnlegencreate PurchaseDocument
PATCH/api/purchase-documents/:idÄndern (geblockt bei locked)update PurchaseDocument
DELETE/api/purchase-documents/:idLöschen (Paranoid Soft-Delete, geblockt bei locked)delete PurchaseDocument
POST/api/purchase-documents/parsePDF/XML-Parser, gibt documentData zurückcreate PurchaseDocument
POST/api/purchase-documents/:id/locklocked = truedo LockPurchaseDocument
POST/api/purchase-documents/:id/unlocklocked = falsedo UnlockPurchaseDocument
POST/api/purchase-documents/:id/copyVollständige Kopie als neuer Belegdo CopyPurchaseDocument
POST/api/purchase-documents/:id/takeoverKonvertierung (z. B. priceRequest → order)do TakeOverPurchaseDocument
POST/api/purchase-documents/:id/apply-text-block-defaultsStandard-Textbloecke übernehmenupdate PurchaseDocument
GET/api/purchase-documents/:id/approval-statusStatus-Aggregat für die Belegfreigabeview PurchaseDocument
POST/api/purchase-documents/:id/approveBeleg freigebenupdate PurchaseDocument
POST/api/purchase-documents/:id/rejectBeleg ablehnen (Begründung Pflicht)update PurchaseDocument
GET/api/belegfreigabe/flagsBulk-Vollständigkeits-Flags je Belegview PurchaseDocument

Sub-Resources werden über separate Routes verwaltet:

  • GET /api/purchase-documents/:id/items (Positionen) — view PurchaseDocumentItem
  • GET /api/text-block-parents?parentId=:id&parentType=PurchaseDocument (Textbloecke) — view TextBlockParent
  • GET /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-Override taxRateId; Endpoints …/approve, …/reject, …/approval-status, /belegfreigabe/flags. Freigabe und Verbuchung sind getrennt; Cockpit: Belegfreigabe. Verifiziert an purchaseDocument.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 an purchaseDocument.controller.ts + purchaseDocumentWorkorder.router.ts (master).
  • 2026-04-29: Initiale Veroeffentlichung. Grundlage: Code-Analyse aus speamcore.com und api.speamcore.com (master, Stand f53c55dc / b51c43ac). IST-Screenshots stehen aus.