NEXOWARE ConnectSchema-Explorer · Kafka-Backbone

Verträge / EsOrderState

Shop-Bestellung State

EsOrderState · com.nexoware.connect.events

Aggregat
EsOrder
Topic
esorder.state je Mandant mit Präfix, z. B. <mandant>.esorder.state
Key
EsOrderUuid
Nachrichtenart
State — der aktuelle Zustand, ein Writer
Schreiber
Shop (shop)
Leser
c-entron Webservice (centron) , NEXOWARE.ERP (nexoware-erp)
Fassung
Version 1 (neueste)
Schema-ID
63

Vertragsentwurf: vollstaendiger veroeffentlichter Zustand fuer Shop-Bestellung. Feldumfang aus dem archivierten EsOrderChanged; kein fachlicher Vergleich beider Produkte. Direkte Veroeffentlichung durch shop; kein Patch-Pfad. Grenzen, Migration und offene Fachregeln: docs/contracts/esorder.md.

Fassungen

Felder

Referenziert von

Rohfassung

Schema dieser Fassung (Avro)
{
  "type": "record",
  "name": "EsOrderState",
  "namespace": "com.nexoware.connect.events",
  "doc": "Vertragsentwurf: vollstaendiger veroeffentlichter Zustand fuer Shop-Bestellung. Feldumfang aus dem archivierten EsOrderChanged; kein fachlicher Vergleich beider Produkte. Direkte Veroeffentlichung durch shop; kein Patch-Pfad. Grenzen, Migration und offene Fachregeln: docs/contracts/esorder.md.",
  "fields": [
    {
      "name": "EsOrderUuid",
      "type": {
        "type": "string",
        "logicalType": "uuid"
      },
      "doc": "Stabile, global eindeutige Identitaet der Bestellung (Matching-Schluessel; zugleich Message-Key). Der SHOP mintet sie - er ist der Ursprung des Aggregats. Sie ist zugleich der fachliche Idempotenzschluessel, an dem der c-entron-Consumer seine Verknuepfung Bestellung -> Beleg fuehrt: Eine erneute Zustellung trifft auf die vorhandene Verknuepfung, statt einen zweiten Beleg zu erzeugen. Genau das fehlte der Alt-Implementierung, deren Duplikatschutz auf einem nachtraeglich geschriebenen Mapping und einem ES-Statuswechsel beruhte (Inventur Schritt 6, Retry-Risiko)."
    },
    {
      "name": "ShopOrderNumber",
      "type": [
        "null",
        "string"
      ],
      "doc": "Bestellnummer, unter der die Bestellung im Shop gefuehrt wird (Quelle ORDER_ID_SHOP) - fachlicher Business-Key, nicht die Identitaet (event-envelope.md). Der Kunde kennt seine Bestellung unter dieser Nummer, und die Alt-Implementierung meldete zu ihr die ERP-Belegnummer zurueck. Vorsorglich optional geschnitten: ob die ES-Quelle sie garantiert, ist nicht verifiziert.",
      "default": null
    },
    {
      "name": "Status",
      "type": "string",
      "doc": "Zustand der Bestellung, wie der Shop ihn fuehrt - roh durchgereicht als Zeichenkette der Quelle. Belegt sind aus der Inventur new (neu), openpayment (offene Zahlung) und open (in Bearbeitung); die Alt-Implementierung importierte die ersten beiden und schrieb danach open zurueck. Das Feld heisst bewusst NICHT State: Das State des Korpus ist ein int-Statusfeld mit vertraglich festgelegten Zahlenwerten, die ES-Quelle liefert dagegen Namen. Ein int erzwaenge eine Abbildung, die kein Dokument festlegt; ein Avro-Enum verlangte die vollstaendige Wertemenge, und die ist nicht bekannt (die Inventur nennt drei Werte, nicht den Katalog). Was aus dem Status folgt - ob der Consumer einen Beleg anlegt oder wartet -, entscheidet der Consumer. Ist die Wertemenge quellseitig abgeschlossen, gehoert sie in ein Enum nachgezogen, wie die Rechenart der Sonderpreis-Regel (ADR-0018, Punkt 3).",
      "default": ""
    },
    {
      "name": "AccountUuid",
      "type": {
        "type": "string",
        "logicalType": "uuid"
      },
      "doc": "Referenz auf den Geschaeftspartner, der bestellt hat, ueber dessen UUID (Quelle BuyerPartyId ueber den ES-Kunden). Parent-Referenz und damit Pflicht: kann der Producer sie nicht aufloesen, scheitert er laut, statt still null zu senden (event-envelope.md). Genau dieses Verhalten hatte die Alt-Implementierung fachlich schon - sie uebersprang einen Auftrag, dessen Kunde sich nicht aufloesen liess -, nur still und mit Wiederholung beim naechsten Lauf. Das Read-back-Mapping ueber erp_customerid entfaellt: ueber den Vertrag reist die UUID.",
      "default": "00000000-0000-0000-0000-000000000000"
    },
    {
      "name": "OrderDate",
      "type": {
        "type": "long",
        "logicalType": "timestamp-millis"
      },
      "doc": "Zeitpunkt, zu dem die Bestellung im Shop aufgegeben wurde, als UTC-Instant. Als Pflicht geschnitten: eine Bestellung ohne Aufgabezeitpunkt ist keine, und der Consumer braucht ihn als Belegdatum. Nicht zu verwechseln mit ChangedAt - das ist der Zeitpunkt DIESES Snapshots und aendert sich bei jeder spaeteren Aenderung der Bestellung. Die quellseitige Nullability ist nicht verifiziert.",
      "default": 0
    },
    {
      "name": "DesiredDeliveryDate",
      "type": [
        "null",
        {
          "type": "long",
          "logicalType": "timestamp-millis"
        }
      ],
      "doc": "Vom Besteller gewuenschter Liefertermin als UTC-Instant; die Alt-Implementierung schrieb ihn beim Import in den Belegkopf. Optional - ein Wunschtermin ist eine Angabe, keine Pflicht.",
      "default": null
    },
    {
      "name": "CurrencyIso",
      "type": [
        "null",
        "string"
      ],
      "doc": "ISO-Waehrungskennzeichen der Bestellung. Feldname und Form wie am Beleg (OrderState.CurrencyIso). Einen Umrechnungsfaktor zur Hauswaehrung traegt die Bestellung nicht: er ist ERP-Wahrheit und gehoert zum Beleg, den der Consumer daraus macht.",
      "default": null
    },
    {
      "name": "PurchaseOrderNumber",
      "type": [
        "null",
        "string"
      ],
      "doc": "Bestellnummer des Kunden (dessen eigene Referenz, nicht die des Shops); die Alt-Implementierung schrieb sie beim Import in den Belegkopf. Feldname wie am Beleg (OrderState.PurchaseOrderNumber).",
      "default": null
    },
    {
      "name": "Comment",
      "type": [
        "null",
        "string"
      ],
      "doc": "Bemerkung des Bestellers zum Kopf der Bestellung; die Alt-Implementierung schrieb sie beim Import in den Belegkopf. ADR-0015 nennt sie ausdruecklich als eine der Angaben, die beim Schnitt nicht verlorengehen duerfen - sie war der Grund, warum der Beleg-Vertrag im selben Schritt eine Kopf-Bemerkung bekommt (OrderState.Comment).",
      "default": null
    },
    {
      "name": "AssetConditionUuid",
      "type": [
        "null",
        {
          "type": "string",
          "logicalType": "uuid"
        }
      ],
      "doc": "Referenz auf die Zahlungskondition der Bestellung ueber deren UUID (Quelle PAYMENT_ID). Dasselbe Konditions-Stammdatum wie am Beleg; die Alt-Implementierung loeste die ES-Kennung ueber ein Konditions-Mapping auf und fiel auf die Default-Kondition zurueck, wenn das misslang. Der Fallback ist Consumer-Entscheidung und nicht Vertragsinhalt: null heisst, dass der Shop keine Kondition mitgibt.",
      "default": null
    },
    {
      "name": "DeliveryConditionUuid",
      "type": [
        "null",
        {
          "type": "string",
          "logicalType": "uuid"
        }
      ],
      "doc": "Referenz auf die Lieferbedingung der Bestellung ueber deren UUID (Quelle SHIPMENT_ID) - dasselbe Konditions-Stammdatum wie AssetConditionUuid (references am Feld), wie am Beleg. Null, wenn der Shop keine mitgibt.",
      "default": null,
      "references": "AssetCondition"
    },
    {
      "name": "GrossTotal",
      "type": [
        "null",
        {
          "type": "bytes",
          "logicalType": "decimal",
          "precision": 18,
          "scale": 2
        }
      ],
      "doc": "Brutto-Gesamtsumme der Bestellung, beim Publish kaufmaennisch auf 2 Nachkommastellen gerundet. Nur brutto: Der Shop rechnet in Bruttopreisen, und die Brutto-Netto-Umrechnung ist Consumer-Arbeit (ADR-0015, Punkt 3). Ein Netto-Feld hier hiesse, den Producer eine Rechnung anstellen zu lassen, deren Ergebnis der Konsument verantwortet.",
      "default": null
    },
    {
      "name": "CustomerDiscountPercent",
      "type": [
        "null",
        {
          "type": "bytes",
          "logicalType": "decimal",
          "precision": 6,
          "scale": 2
        }
      ],
      "doc": "Kundenrabatt in Prozent auf die gesamte Bestellung. Die Alt-Implementierung machte daraus eine negative Belegposition auf den RABATT-Artikel und rechnete den Brutto-Wert vorher auf netto um - beides ist Uebersetzung in Beleg-Wahrheit und gehoert zum Consumer. Ueber den Vertrag reist der Rabatt als das, was er im Shop ist: ein Satz am Kopf.",
      "default": null
    },
    {
      "name": "OrderDiscountPercent",
      "type": [
        "null",
        {
          "type": "bytes",
          "logicalType": "decimal",
          "precision": 6,
          "scale": 2
        }
      ],
      "doc": "Auftragsrabatt in Prozent, zusaetzlich zum Kundenrabatt. Wie dieser eine Kopf-Angabe des Shops, keine Belegposition.",
      "default": null
    },
    {
      "name": "VoucherAmountGross",
      "type": [
        "null",
        {
          "type": "bytes",
          "logicalType": "decimal",
          "precision": 18,
          "scale": 2
        }
      ],
      "doc": "Eingeloester Gutschein als Brutto-Betrag (nicht als Satz). Positiv geschnitten: Es reist der Wert des Gutscheins, nicht seine Wirkung auf den Beleg - dass daraus eine negative Position wird, entscheidet der Consumer.",
      "default": null
    },
    {
      "name": "FreightAmountNet",
      "type": [
        "null",
        {
          "type": "bytes",
          "logicalType": "decimal",
          "precision": 18,
          "scale": 2
        }
      ],
      "doc": "Frachtkosten als Netto-Betrag. Anders als die Rabatte liefert der Shop die Zuschlaege netto - der Feldname sagt es deshalb ausdruecklich, damit niemand die beiden Seiten gleich behandelt. Welcher Artikel im Beleg daraus wird (Fracht-Artikel aus den Beleg-Einstellungen), ist Consumer-Wissen.",
      "default": null
    },
    {
      "name": "SmallQuantitySurchargeNet",
      "type": [
        "null",
        {
          "type": "bytes",
          "logicalType": "decimal",
          "precision": 18,
          "scale": 2
        }
      ],
      "doc": "Mindermengenzuschlag als Netto-Betrag. Die Alt-Implementierung suchte dafuer einen Eigenartikel ueber seinen Namen und fiel auf den Platzhalter ARTIKELNEU zurueck, wenn sie ihn nicht fand - eine Suchkette, die zum Consumer wandert.",
      "default": null
    },
    {
      "name": "Surcharge1Net",
      "type": [
        "null",
        {
          "type": "bytes",
          "logicalType": "decimal",
          "precision": 18,
          "scale": 2
        }
      ],
      "doc": "Erster allgemeiner Aufschlag als Netto-Betrag (Quelle Aufschlag 1). Wofuer er steht, legt der Shop-Betreiber fest; der Vertrag deutet ihn nicht.",
      "default": null
    },
    {
      "name": "Surcharge2Net",
      "type": [
        "null",
        {
          "type": "bytes",
          "logicalType": "decimal",
          "precision": 18,
          "scale": 2
        }
      ],
      "doc": "Zweiter allgemeiner Aufschlag als Netto-Betrag (Quelle Aufschlag 2).",
      "default": null
    },
    {
      "name": "DeliveryAddress",
      "type": [
        "null",
        {
          "type": "record",
          "name": "EsOrderDeliveryAddress",
          "fields": [
            {
              "name": "AddressUuid",
              "type": [
                "null",
                {
                  "type": "string",
                  "logicalType": "uuid"
                }
              ],
              "doc": "Referenz auf die Anschrift des Geschaeftspartners ueber deren UUID (AddressState), wenn der Besteller eine im ERP bekannte Anschrift gewaehlt hat. Null bei einer im Shop frei eingegebenen Lieferadresse - dann gelten die Werte darunter.",
              "default": null
            },
            {
              "name": "CompanyName",
              "type": [
                "null",
                "string"
              ],
              "doc": "Firmenname des Lieferziels.",
              "default": null
            },
            {
              "name": "ContactName",
              "type": [
                "null",
                "string"
              ],
              "doc": "Name der Person, an die geliefert werden soll.",
              "default": null
            },
            {
              "name": "Department",
              "type": [
                "null",
                "string"
              ],
              "doc": "Abteilung.",
              "default": null
            },
            {
              "name": "Street",
              "type": [
                "null",
                "string"
              ],
              "doc": "Strasse.",
              "default": null
            },
            {
              "name": "HouseNumber",
              "type": [
                "null",
                "string"
              ],
              "doc": "Hausnummer. Ob die ES-Quelle Strasse und Hausnummer getrennt fuehrt, ist nicht verifiziert - liefert sie eine gemeinsame Zeile, traegt Street sie und HouseNumber bleibt null.",
              "default": null
            },
            {
              "name": "Zip",
              "type": [
                "null",
                "string"
              ],
              "doc": "Postleitzahl.",
              "default": null
            },
            {
              "name": "City",
              "type": [
                "null",
                "string"
              ],
              "doc": "Ort.",
              "default": null
            },
            {
              "name": "CountryIso",
              "type": [
                "null",
                "string"
              ],
              "doc": "ISO-Laenderkennzeichen des Lieferziels - als Code, nicht als Referenz auf das Laender-Stammdatum. Der Shop kennt Laendercodes, keine c-entron-UUIDs; die Aufloesung auf CountryState ist Teil der Uebersetzung und gehoert zum Consumer. Feldform wie CurrencyIso am Kopf.",
              "default": null
            }
          ]
        }
      ],
      "doc": "Lieferziel der Bestellung, so wie der Shop es kennt. Eingebettet und nicht bloss referenziert: Der Besteller kann im Shop eine Anschrift eingeben, die es im ERP noch gar nicht gibt - eine reine Referenz liesse die Bestellung dann ohne Lieferziel durchgehen. Die optionale AddressUuid darin deckt den anderen Fall ab: Hat der Besteller eine bekannte Anschrift seines Kunden gewaehlt, reist sie als Referenz mit, und der Consumer nimmt die Referenz statt der Werte. Das Adress-Mapping der Alt-Implementierung (DELIVERY_PARTY_ID ueber ObjectExternalReferences) entfaellt damit. Null, wenn die Bestellung an die Standardanschrift des Kunden geht. PERSONENBEZUG: siehe doc des Records.",
      "default": null
    },
    {
      "name": "Positions",
      "type": {
        "type": "array",
        "items": {
          "type": "record",
          "name": "EsOrderPosition",
          "fields": [
            {
              "name": "PositionNumber",
              "type": "int",
              "doc": "Positionsnummer innerhalb der Bestellung - SORTIERUNG, nicht Identitaet. Positionen sind damit nicht einzeln adressierbar; Commands auf eingebettete Positionen sind repo-weit unzulaessig (aggregate-declaration.md, Sperrvermerk; ADR-0013, Punkt 7).",
              "default": 0
            },
            {
              "name": "Quantity",
              "type": [
                "null",
                "double"
              ],
              "doc": "Bestellte Menge.",
              "default": null
            },
            {
              "name": "UnitPriceGross",
              "type": [
                "null",
                {
                  "type": "bytes",
                  "logicalType": "decimal",
                  "precision": 18,
                  "scale": 4
                }
              ],
              "doc": "BRUTTO-Einzelpreis der Position, beim Publish kaufmaennisch auf 4 Nachkommastellen gerundet - so, wie der Shop ihn dem Kunden angezeigt und berechnet hat. Der Beleg fuehrt an derselben Stelle einen Netto-Preis (OrderState.Positions.BasePrice); die Umrechnung liegt dazwischen und gehoert zum Consumer. Der Feldname sagt die Seite ausdruecklich, damit die beiden nicht verwechselt werden.",
              "default": null
            },
            {
              "name": "VatRate",
              "type": [
                "null",
                {
                  "type": "bytes",
                  "logicalType": "decimal",
                  "precision": 6,
                  "scale": 2
                }
              ],
              "doc": "Mehrwertsteuersatz der Position in Prozent, wie der Shop ihn gerechnet hat. Es reist der SATZ, nicht die Referenz auf das Steuersatz-Stammdatum: Der Shop kennt Prozentwerte, die Zuordnung zum ValueAddedTax-Aggregat traf in der Alt-Implementierung der Sync ueber die USt-Liste des ERP - auch das ist Uebersetzung und wandert zum Consumer.",
              "default": null
            },
            {
              "name": "Ean",
              "type": [
                "null",
                "string"
              ],
              "doc": "EAN/GTIN des bestellten Artikels - vierte Stufe der Artikel-Suchkette der Alt-Implementierung. Einer von vier Identifikatoren, die eine Position tragen kann; welcher davon gesetzt ist, haengt am Artikel und am Shop-Katalog.",
              "default": null
            },
            {
              "name": "SupplierArticleNumber",
              "type": [
                "null",
                "string"
              ],
              "doc": "Lieferanten-Artikelnummer (Quelle SUPPLIER_AID) - zweite und dritte Stufe der Suchkette. Traegt sie das Praefix CEN-, meinte die Alt-Implementierung damit einen c-entron-Artikelcode; ohne Praefix einen fremden. Diese Deutung ist Consumer-Wissen, der Vertrag reicht die Nummer roh durch.",
              "default": null
            },
            {
              "name": "ShopArticleNumber",
              "type": [
                "null",
                "string"
              ],
              "doc": "Artikelnummer des Shops (Quelle SHOP_AID) - fuenfte Stufe der Suchkette, ebenfalls mit der CEN-Praefix-Deutung beim Consumer.",
              "default": null
            },
            {
              "name": "ArticleUuid",
              "type": [
                "null",
                {
                  "type": "string",
                  "logicalType": "uuid"
                }
              ],
              "doc": "Referenz auf das Artikel-Stammdatum ueber dessen UUID, wenn der Shop den ERP-Artikel kennt - die erste und sicherste Stufe der Suchkette (in der Alt-Implementierung die I3D in ERP_AID). Ist sie gesetzt, entfaellt die Suche; ist sie null, arbeitet der Consumer die uebrigen Identifikatoren ab.",
              "default": null
            },
            {
              "name": "Text",
              "type": [
                "null",
                "string"
              ],
              "doc": "Positionstext, wie er im Shop steht (Artikelbezeichnung oder Freitext).",
              "default": null
            },
            {
              "name": "CostCenterUuid",
              "type": [
                "null",
                {
                  "type": "string",
                  "logicalType": "uuid"
                }
              ],
              "doc": "Referenz auf die Kostenstelle der Position ueber deren UUID. Die Alt-Implementierung fuellte sie beim Anlegen der Belegposition; ADR-0015 nennt die Kostenstellen-Zuordnung ausdruecklich als Angabe, die beim Schnitt nicht verlorengehen darf - sie war der Grund, warum der Beleg-Vertrag im selben Schritt eine Positions-Kostenstelle bekommt (OrderState.Positions.CostCenterUuid). Aus welcher Angabe der Bestellung sie stammt, ist nicht verifiziert; die Inventur fuehrt die Positionen mit einem UDX-Block, dessen Inhalt sie nicht aufschluesselt.",
              "default": null
            }
          ]
        }
      },
      "doc": "Eingebettete Positionsliste der Bestellung, aufsteigend nach PositionNumber. Voll-Snapshot: der Consumer ersetzt die Collection je Event vollstaendig (kein positionsweises Mergen) - dieselbe Regel wie bei den Belegarten. Default [] nur fuer Schema-Evolution. Die Positionen tragen ausschliesslich Shop-Wahrheit: Was daraus im Beleg wird - Artikel-Identifikation ueber die sechsstufige Suchkette, Platzhalter FREMD/ARTIKELNEU bei unaufloesbaren Zeilen, Netto-Preis, Lager, Steuersatz-Stammdatum -, entscheidet der c-entron-Consumer (ADR-0015, Punkt 3). Bleibt eine Zeile dort unaufloesbar, wird die Bestellung zum sichtbaren geparkten Zustand statt zum stillen Wiederholungs-Loop (consumer-guidelines Regel 6).",
      "default": []
    },
    {
      "name": "Revision",
      "type": "long",
      "doc": "Streng monotone Revision je Objekt, vom publizierenden Produkt bei einer Zustandsaenderung erhoeht und zusammen mit der Outbox persistiert. Wiederholungen behalten die Revision."
    },
    {
      "name": "LastEventId",
      "type": {
        "type": "string",
        "logicalType": "uuid"
      },
      "doc": "Stabile EventId des zuletzt publizierten Zustands; bei Wiederholung derselben Outbox-Nachricht identisch."
    },
    {
      "name": "ChangedByUuid",
      "type": [
        "null",
        {
          "type": "string",
          "logicalType": "uuid"
        }
      ],
      "doc": "UUID des aendernden Mitarbeiters (Employee); null, wenn unbekannt. Hier praktisch immer null: Eine Bestellung gibt ein Kunde im Shop auf, kein c-entron-Mitarbeiter. Das Feld bleibt trotzdem Teil des Umschlags, weil ADR-0016, Punkt 3 fuer Aggregate mit Ursprung ausserhalb von c-entron dieselbe Mechanik verlangt wie beim groessten Beleg.",
      "default": null,
      "references": "Employee"
    },
    {
      "name": "ChangedAt",
      "type": {
        "type": "long",
        "logicalType": "timestamp-millis"
      },
      "doc": "Zeitpunkt DIESER Aenderung als UTC-Instant - nicht der Zeitpunkt, zu dem die Bestellung aufgegeben wurde (dafuer OrderDate). Eine spaetere Shop-seitige Aenderung reist als neuer Voll-Snapshot desselben Aggregats mit neuem ChangedAt (event-envelope.md, Granularitaet)."
    }
  ],
  "aggregate": "EsOrder",
  "label": "Shop-Bestellung",
  "topic": "esorder.state",
  "key": "EsOrderUuid",
  "cleanupPolicy": "compact",
  "partitions": 1,
  "writers": [
    "shop"
  ],
  "readers": [
    "centron",
    "nexoware-erp"
  ]
}