Zum Hauptinhalt springen

Produkte

Zweck

Der Artikel- und Leistungsstamm haelt physische Produkte, Dienstleistungen und Calculation-Products (Vorlagen aus mehreren SalesDocumentItems). Produkte werden in Verkaufs- und Einkaufsbelegen referenziert.

Drei Auspraegungen über Flags:

  • shopProduct — für den Web-Shop sichtbar.
  • priceOnRequest — Preis auf Anfrage; sellPrice wird im Output ausgeblendet.
  • isCalculationProduct — Vorlagen-Container; statt Lieferanten-Tab erscheint der Tab Calculation Items.

Voraussetzungen

- Eingerichtete Stammdaten: Mengeneinheit, Hersteller, Warengruppe, Konten (siehe Setup-Wizard). - Berechtigung `create:Product`.

Berechtigungen (CASL)

Frontend-Page-Guard:

ActionSubjectKeycloak-Rolle
viewFE_Product
viewProductAPP_SPEAMCORE_VIEW_PRODUCT

Tab-Subjects (conditional):

TabSub-PfadSubjectSichtbar wenn
Attribute/products/:id/attributesAttributeParent:viewimmer
Lieferanten/products/:id/suppliersProductSupplier:viewisCalculationProduct = false
Calculation Items/products/:id/calculation-itemsSalesDocumentItem:viewisCalculationProduct = true
Komponenten/products/:id/componentsProductComponent:viewimmer
Dokumente/products/:id/documentsDocument:viewimmer

API-Datenzugriff:

ActionSubjectEndpointKeycloak-Rolle
viewProductGET /api/products, GET /api/products/:idAPP_SPEAMCORE_VIEW_PRODUCT
createProductPOST /api/productsAPP_SPEAMCORE_CREATE_PRODUCT
updateProductPATCH /api/products/:idAPP_SPEAMCORE_UPDATE_PRODUCT
deleteProductDELETE /api/products/:idAPP_SPEAMCORE_DELETE_PRODUCT
view + viewProduct + SalesDocumentItemGET /api/products/:productId/calculation-itemsAPP_SPEAMCORE_VIEW + VIEW_PRODUCT + SALES_DOCUMENT_ITEM
update + createProduct + SalesDocumentItemPOST /api/products/:productId/calculation-itemsAPP_SPEAMCORE_UPDATE + CREATE_PRODUCT + SALES_DOCUMENT_ITEM
update + updateProduct + SalesDocumentItemPATCH /api/products/:productId/calculation-items/:itemIdAPP_SPEAMCORE_UPDATE + UPDATE_PRODUCT + SALES_DOCUMENT_ITEM
update + deleteProduct + SalesDocumentItemDELETE /api/products/:productId/calculation-items/:itemIdAPP_SPEAMCORE_UPDATE + DELETE_PRODUCT + SALES_DOCUMENT_ITEM

Schritt-für-Schritt-Anleitung

Produkt anlegen

  1. Produkte (/products) → + Neu.
  2. Pflegen Sie Pflichtfelder: Titel, Mengeneinheit, ggf. Warengruppe und Hersteller.
  3. Setzen Sie priceOnRequest oder sellPrice.
  4. Ggf. shopProduct = true für den Web-Shop.
  5. Speichern.

Calculation-Product anlegen

  1. Beim Anlegen: isCalculationProduct = true setzen.
  2. Auf der Detailseite den Tab Calculation Items öffnen.
  3. Beliebige SalesDocumentItems als Vorlage hinterlegen — diese werden bei Verwendung des Produkts in einen Verkaufsbeleg übernommen.

Listenansicht — products

Toolbar (Detail-Seite)

Schlanke Toolbar oben rechts:

IconAktion (aria-label)CASLWirkung
ZurückgehenZurück zur Liste.
🏠Zur Startseite gehenSpringt auf das Dashboard / /.
⏮/◀/▶/⏭PaginationNavigation durch die gefilterte Liste — Massen-Bearbeitung ohne Liste-Sprung.

Wie auf jeder Detail-Seite verfuegbar — siehe Floating-Quickbar:

  • KAL. (Mini-Kalender)
  • ZEIT (Persoenliche Wochen-Arbeitszeit)
  • ARBEIT (Eigene bevorstehende Aufträge)

UI-Elemente

Button: „+ Neu"

Listenseite. Erfordert create:Product.

Felder und Eingaben (Auswahl)

FeldnamePflichtDatentypWirkung beim AusfuellenVoraussetzung
productNoneinStringIdentifikation in Belegen, Lager und Shop.Eindeutigkeit pro Mandant empfohlen.
titlejaStringAnzeigetext in allen Selects, Belegen und im Shop.
descriptionneinTEXT (HTML-Editor)Lange Beschreibung in Belegen, PDFs und im Shop sichtbar.HTML-Editor-Komponente.
unitIdjaUUIDMengeneinheit für Beleg-Positionen, Lager-Bestand und Reports.view:Unit; Master-Sync mind. einmal gelaufen.
productTypeIdneinUUIDKlassifizierung für Reports und ggf. spezifische Felder. Invisible bei isCalculationProduct=true.view:ProductType.
productGroupIdneinUUIDWarengruppe — beeinflusst Mandanten-Sichtbarkeit über ProductGroupProfile.view:ProductGroup; passendes Profil aktiv.
manufacturerIdneinUUIDHersteller-Verknuepfung.view:Manufacturer.
manufacturerProductNoneinStringHersteller-Artikelnummer für Bestellungen.
hsCodeIdneinUUIDZolltarifnummer (HS-Code). Pflicht für Export.Master-Sync mit HS-Codes.
countryOfOriginIdneinUUIDUrsprungsland für Zoll und Lieferdokumente.view:Country.
customsTariffNumberneinStringFreie Zoll-Tarifnummer.
length / width / heightneinDecimalAbmessungen — für Versand und Lager.
netWeight / grossWeightneinDecimalGewichte für Versand und Pflichtangaben.
eanneinStringEAN-Barcode für Shop und Scanner.
sellPricejaDecimalVerkaufspreis. Verborgen bei priceOnRequest=true. Validierung gegen ProductSpecialCondition blockt Werte unter dem Fixed-Discount.Keine widersprechende Sonderkondition.
priceOnRequestjaBooleanWenn true: Preis wird im Shop und in Belegen ausgeblendet, manueller Eingriff bei Angebot noetig.
shopProductjaBooleanWenn true: Produkt erscheint im Shop. Triggert afterUpdate-Hook, der Document.isPublic der Produkt-Bilder synct.Mandanten-Profil hat passende Warengruppe freigeschaltet.
isCalculationProductjaBooleanWenn true: Tab-Struktur wechselt (Calculation Items statt Lieferanten). Vorlagen-Container für mehrere SalesDocumentItems.Migration: nicht trivial nach Anlage zurueckwechseln.
accountIdneinUUIDErloeskonto für Buchungen aus Verkauf.view:Account.
expenseAccountIdneinUUIDAufwandskonto für Buchungen aus Einkauf.view:Account.
marketplaceEnabledneinBoolean (Default false)Bietet das Produkt im Produkt-Marktplatz anderen Mandanten an.Mindestens ein Lieferant mit EK > 0 muss hinterlegt sein (sonst Fehler).
marketplacePriceneinDecimalMarktplatz-/Katalogpreis. Die Marge ergibt sich aus marketplacePrice − günstigster EK.nur relevant bei marketplaceEnabled = true.
masterIdUUID, nullable (read-only)Roundtrip-ID vom Admin-Master — kennzeichnet ein bezogenes (gemirrortes) Produkt.wird beim Push/Pull gesetzt.
syncSellPriceToProviderneinBooleanVerkaufspreis-Änderungen an den Anbieter zurücksynchronisieren (bidirektional).
shortDescriptionneinTEXT (HTML)Kurzbeschreibung — für kompakte Listen und Shop-Teaser.
packagingUnitneinDecimal(12,3)Verpackungseinheit auf Produkt-Ebene (je Lieferant separat überschreibbar).
hasTieredPricesneinBooleanAktiviert mengengestaffelte Preise und öffnet den Staffelpreis-Editor.
tieredPricesneinJSONMengengestaffelte Verkaufspreise [{fromQuantity, sellPrice}]. Einkaufsseitige Staffeln liegen je Lieferant, siehe Produkt-Lieferanten-Preise.hasTieredPrices = true.
baseProductIdneinUUID (Self-Ref)Verweis auf ein Basis-Produkt — kennzeichnet eine Variante.

Lieferanten-Preise & Marge: EK/VK/Rabatt und der Deckungsbeitrag je Bezugsquelle werden im Tab Lieferanten gepflegt — siehe Produkt — Lieferanten und das Konzept Produkt-Lieferanten-Preise.

Produkt-Marktplatz (seit Juni 2026)

Analog zum Kurs-Marktplatz können Produkte mandantenübergreifend angeboten und bezogen werden — direkt im Produkt-Formular, ohne eigene Seite.

  • Anbieten: marketplaceEnabled = true setzen und einen marketplacePrice hinterlegen. Voraussetzung ist mindestens ein Lieferant mit Einkaufspreis — das Formular zeigt dazu den günstigsten effektiven EK (über alle Lieferanten-Rabatte gerechnet) als Kalkulationshilfe an.
  • Beziehen / Re-Provider: Ein bezogenes Produkt (masterId ≠ null) wird vom Anbieter gepflegt; es kann — wie beim Kurs — selbst wieder im Marktplatz angeboten werden.
  • Synchronisation: Push/Pull laufen über den Admin-Master; der lokale Marktplatz-Preis wird vom Anbieter-Sync nicht überschrieben. Eine Umsatz-/Abrechnungs-Übersicht wie beim Kurs-Marktplatz gibt es (noch) nicht.
Der Schalter `marketplaceEnabled` lässt sich nur aktivieren, wenn das Produkt mindestens **einen Lieferanten mit Einkaufspreis > 0** hat. Ohne Lieferant wird das Speichern mit einer Fehlermeldung abgewiesen.

Workflows und Zustaende

Produkte haben kein Status-Feld — Aktivität wird über shopProduct (sichtbar im Shop) oder über Sonderkonditionen geregelt.

Wiederverwendbare Konzepte

Verknuepfungen zu anderen Modulen

  • Mengeneinheit (Master-Sync) — unitId.
  • WarengruppeproductGroupId (mit Client-Scope-Filterung über ClientProductGroupProfile).
  • HerstellermanufacturerId.
  • SonderkonditionenProductSpecialCondition.productId. Validierung in beforeCreate/beforeUpdate: sellPrice darf nicht unter den fixed-discount-Wert fallen.
  • Verkaufspositionen — Calculation-Items sind SalesDocumentItems mit Vorlagen-Verknuepfung.
  • LieferantenProductSupplier (1:N) — nur bei isCalculationProduct=false.
  • Kurs-Marktplatz — Produkte können analog mandantenübergreifend angeboten/bezogen werden (Produkt-Marktplatz).
  • Komponenten / StuecklisteProductComponent.
  • DokumenteDocumentParent (parentType = 'Product'); Document.isPublic synct mit shopProduct.

Häufige Fehler und Lösungen

FehlerLösung
sellPrice unter Sonderkondition abgelehntProductSpecialCondition setzt fixed discount. Preis erhoehen oder Kondition anpassen.
Tab „Lieferanten" fehltisCalculationProduct = true — Lieferanten und Calculation Items sind exklusiv.
Produkt erscheint nicht im ShopshopProduct = false oder Warengruppe nicht im Client-Profil freigegeben.
Bilder erscheinen nicht im ShopDocument.isPublic = false. Wird automatisch synchronisiert, wenn shopProduct gesetzt wird (afterUpdate-Hook).
Preise eines Mandanten weichen abProductGroupProfileProductGroupManufacturer.discount wirkt mandantenspezifisch (afterFind-Hook).

API/Schnittstellen

MethodeEndpointZweckCASL
GET/api/productsListe mit Client-Scope und Discountview Product
GET/api/products/:idDetailview Product
POST/api/productsAnlegencreate Product
PATCH/api/products/:idÄndernupdate Product
DELETE/api/products/:idSoft-Deletedelete Product
GET/api/products/:productId/calculation-itemsVorlagen lesen (nur isCalculationProduct=true)view Product + view SalesDocumentItem
POST/api/products/:productId/calculation-itemsVorlage anlegenupdate Product + create SalesDocumentItem
PATCH/api/products/:productId/calculation-items/:itemIdVorlage ändernupdate Product + update SalesDocumentItem
DELETE/api/products/:productId/calculation-items/:itemIdVorlage löschenupdate Product + delete SalesDocumentItem

Versionshinweise

  • 2026-06-22: Neue Produkt-Felder shortDescription, packagingUnit, hasTieredPrices, tieredPrices (Verkaufsstaffeln) und baseProductId (Variante) ergänzt; Verweis auf die Lieferanten-Preis-Engine (Konzept). Verifiziert an product.model.ts.
  • 2026-06-12: Produkt-Marktplatz dokumentiert — neue Felder marketplaceEnabled, marketplacePrice, masterId, syncSellPriceToProvider; mandantenübergreifendes Anbieten/Beziehen analog Kurs-Marktplatz, Voraussetzung Lieferant-mit-EK, günstigster-EK-Anzeige, Re-Provider. Verifiziert an product.model.ts (Marketplace-Invariante) und productMarketplaceSync.service.ts.
  • 2026-04-29: Initiale Veroeffentlichung.