Zum Hauptinhalt springen

Verkaufsbeleg — Positionen (Sub-Route)

Zweck

Sub-Route /sales-documents/:id/items ist die Pflege-Sicht der Positionen eines Verkaufsbelegs. Anders als die globale Cross-Beleg-Sicht /sales-document-items (read-only Auswertung) erlaubt diese Sub-Route Hinzufuegen, Ändern und Löschen von Positionen am konkreten Beleg.

Voraussetzungen

- Berechtigung `view:SalesDocument` und `view:SalesDocumentItem`. - Für Pflege: `create`/`update`/`delete:SalesDocumentItem`. - Belegstatus erlaubt Änderungen — gesetzte `locked = true`-Belege koennen typischerweise nur eingeschraenkt geändert werden.

Berechtigungen (CASL)

ActionSubjectWirkungKeycloak-Rolle
viewFE_SalesDocument, SalesDocumentDetail aufrufbar
view/create/update/deleteSalesDocumentItemPositionen pflegenAPP_SPEAMCORE_VIEW/CREATE/UPDATE/DELETE_SALES_DOCUMENT_ITEM
viewProductProdukt-AuswahlAPP_SPEAMCORE_VIEW_PRODUCT

Schritt-für-Schritt-Anleitung

  1. Verkaufsbeleg (/sales-documents/:id) → Tab Positionen.
  2. + Position → Produkt wählen oder freie Position eintragen (Titel, Beschreibung, Menge, Preis, Einheit).
  3. Optional optional/alternative markieren — wirkt sich auf Beleg-Summen-Berechnung aus.
  4. Position in der Liste neu sortieren per Drag-and-Drop.

Listenansicht — sales-documents-items

Positionen-Toolbar (6 Icons)

Direkt über dem Positionen-DataGrid sitzt eine kleine Icon-Reihe mit sechs Aktionen — sie sind das Herzstück der Positionen-Bearbeitung und in der Praxis täglich verwendet:

Icon: „➕ Position hinzufügen"

Klick öffnet ein Dropdown mit vier Position-Typen:

Position-TypBedeutungDaten-Quelle
ProduktStandard-Position aus dem Produkt-Stamm.productId referenziert ein bestehendes Produkt; Snapshot-Felder (Titel, Beschreibung, Verkaufspreis, Einheit) werden bei Anlage befüllt.
Produkt (Kalkulation)Wie „Produkt", zusätzlich mit ausführlicher Kalkulations-Aufschlüsselung im PDF.productId + Kalkulations-Detail-Felder.
Produkt (benutzerdefiniert)Produkt aus Stamm gewählt, aber Snapshot-Felder dürfen abweichend überschrieben werden — Stamm bleibt unverändert.productId + customTitle/customDescription/customPrice.
Leere Text PositionReine Text-Position ohne Produktbezug — z. B. Kapitel-Überschriften, Zwischentexte, Hinweise.Nur title/description, kein productId.
Der Unterschied **Produkt** vs **Produkt (benutzerdefiniert)** ist wichtig: bei „benutzerdefiniert" werden Anpassungen an Titel/Beschreibung/Preis **nur in dieser Position** wirksam, ohne den Produkt-Stamm zu touchieren. Dadurch ist der Beleg-Druck individuell, der Stamm aber zentral gepflegt. Bei „Produkt (Kalkulation)" sieht der Endkunde im PDF die einzelnen Kostenkomponenten — typisch für Beratungs- oder Bauleistungs-Belege.

Icon: „📤 GAEB-Datei hochladen"

Klick öffnet einen Datei-Upload-Dialog. GAEB (Gemeinsamer Ausschuss Elektronik im Bauwesen) ist der deutsche Standard für elektronische Leistungsverzeichnisse im Bauwesen — Format *.x83, *.x84, *.x86, *.x89 (XML-basiert).

Hochgeladene GAEB-Dateien werden geparst, die Leistungspositionen werden 1:1 als SalesDocumentItem angelegt (mit Position, Titel, Beschreibung, Menge, Einheit, Preis). Erforderlich für Auftraggeber im Bauwesen die LV-Templates senden.

Format-Mapping:

  • <HierarchicalGroup> → Kapitel-Position (Text-Position).
  • <Item> → Standard-Produkt-Position.
  • <Pricing>priceNet und ggf. Kalkulations-Komponenten.
GAEB-Import ist **idempotent** — wiederholtes Hochladen derselben Datei legt keine Duplikate an, sondern ersetzt die Positionen. Bei Teil-Updates der GAEB-Datei beim Auftraggeber kann es zu Conflict-Situationen kommen. Sicherer Workflow: alle Positionen löschen → frisch hochladen.

Icon: „1. 2. 3. Pos.-Nr. neu nummerieren"

Klick durchnummeriert alle bestehenden Positionen neu — position-Feld wird auf 1, 2, 3, ... gesetzt, basierend auf der aktuellen Sortier-Reihenfolge der Liste (Drag-and-Drop).

Anwendungsfall: Nachdem Positionen umsortiert oder gelöscht wurden, sind die Positionsnummern unregelmäßig (z. B. 1, 3, 5, 8). Dieser Klick macht daraus wieder 1, 2, 3, 4.

Wirkung: PATCH /api/sales-document-items/:id für jede Position mit dem neuen position-Wert. Das ändert nichts am inhaltlichen Beleg, aber Anwender und Endkunde sehen saubere Nummerierung im PDF.

Icon: „↻ Produktdaten aktualisieren"

Klick synchronisiert die Snapshot-Felder aller Positionen mit dem aktuellen Produkt-Stamm.

Anwendungsfall: Ein Produkt wurde im Stamm geändert (z. B. neuer Verkaufspreis nach Listenpreis-Update), und der bestehende Beleg-Entwurf soll diese Änderung übernehmen.

Wirkung: Pro Position mit productId !== null: liest aktuelle Stamm-Werte → setzt priceNet, taxRate, unitId, ggf. title/description (je nach Typ — bei „benutzerdefiniert" bleiben Custom-Werte erhalten).

Funktioniert **nur bei nicht-gesperrten Belegen** (`locked = false`). Bei gesperrten Belegen ist diese Aktion ausgegraut. Idempotent — kann mehrfach geklickt werden, ändert nur was sich geändert hat.

Icon: „📦 Vorlage speichern"

Klick öffnet ein Dialog zur Vorlagen-Erstellung. Die aktuellen Positionen des Belegs werden als wiederverwendbare Vorlage gespeichert — z. B. „Standard-Wartung Sprinkler 4x jährlich".

Felder im Dialog:

  • templateName (Pflicht) — Eindeutiger Name der Vorlage.
  • templateDescription (optional) — Beschreibung was die Vorlage enthält.
  • isPublic (Toggle) — Vorlage für alle Mitarbeiter sichtbar (default false = privat).

Wirkung: Erzeugt einen SalesDocumentItemTemplate-Datensatz. Spätere Wiederverwendung über das Icon „Vorlage laden".

Icon: „📋 Vorlage laden"

Klick öffnet einen Vorlagen-Auswahl-Dialog. Der Anwender wählt eine bestehende Vorlage; alle Positionen der Vorlage werden zusätzlich zum aktuellen Beleg hinzugefügt (nicht ersetzt — bestehende Positionen bleiben).

Anwendungsfall: Wartungsangebot mit immer-gleichen Standard-Leistungen schnell anlegen, dann individuell ergänzen.

Hinweis: Der productId der geladenen Positionen wird übernommen — aktuelle Stamm-Werte werden nicht zwingend nachgezogen. Empfehlung: nach „Vorlage laden" den Button „↻ Produktdaten aktualisieren" klicken.

Bulk-Auswahl in der Liste

Die erste Spalte des Positionen-DataGrids ist „Ausgewählt" mit einer Checkbox. Bei Mehrfach-Auswahl erscheint im Header eine Bulk-Aktions-Toolbar:

  • Massen-Löschen — alle markierten Positionen entfernen.
  • Massen-Verschieben — Positionen in einen anderen Beleg übernehmen.
  • Massen-Aktualisieren — Produktdaten aller markierten Positionen synchronisieren.

Felder und Eingaben

FeldnamePflichtDatentypWirkung beim AusfuellenVoraussetzung
productIdneinUUIDVerweist auf Produkt — falls gesetzt, werden Snapshot-Felder vorbefuellt.view:Product.
positionneinStringPositionsnummer (z. B. 1, 1.1). Auto-vergebene Reihenfolge.
quantityjaDecimalMenge.
pricejaDecimalVerkaufspreis (netto).
productTitle, productDescriptionneinString/TEXTAnzeige-Texte (Snapshot vom Produkt oder freier Text).
optionalneinBooleanPosition als optional markieren.
alternativeneinBooleanPosition als Alternative markieren.
accountIdneinUUIDBuchhaltungskonto für Erloesbuchung.

Wiederverwendbare Konzepte

Verknuepfungen zu anderen Modulen

API/Schnittstellen

MethodeEndpointZweckCASL
GET/api/sales-document-items?filter[salesDocumentId]Listeview SalesDocumentItem
POST/api/sales-document-itemsAnlegencreate SalesDocumentItem
PATCH/api/sales-document-items/:idÄndernupdate SalesDocumentItem
DELETE/api/sales-document-items/:idSoft-Deletedelete SalesDocumentItem

Versionshinweise

  • 2026-04-30: Initiale Veroeffentlichung.