# Phase 1: DEV-Ist-Stand Tabellen- und Prozesskonzept Stand: 2026-06-02 Status: Verbindlicher DEV-Ist-Stand fuer Phase 1 ## 1. Uebersicht Dieses Dokument beschreibt die technische Wahrheit des aktuellen DEV-Standes auf Basis von: - `docs/CONCEPT.md` - `docs/SCHEMA_PHASE1.sql` - `docs/PROCESS_PHASE1.md` - `db/migrations/0001_phase1_core.sql` bis `db/migrations/0006_phase1_otc_products.sql` - `order-import.php` - `n8n/exports/current/*.json` Wichtige Einordnung: - `SCHEMA_PHASE1.sql` bleibt ein Draft und ist nicht identisch mit der live DB. - Die live DB hat 18 Base Tables und 1 View. - Die Forecast-Erweiterung aus `0003_phase1_inventory_forecast.sql` ist im live Schema nicht aktiv. - Die Doku unten trennt deshalb zwischen implementiertem Ist-Stand und nicht gefundenen Zielbild-Teilen. Tabellen: - `party` - `address` - `contact` - `product` - `sellable_item` - `external_item_alias` - `sellable_item_component` - `warehouse` - `location` - `stock_lot` - `payment_method` - `shipping_method` - `sales_order` - `sales_order_line` - `stock_move` - `sales_order_line_lot_allocation` - `audit_log` - `outbound_webhook_event` View: - `v_stock_lot_balance` Prozesse: - `n8n.bestell-eingang-online-shop` - `order-import.php` - `db.trigger.sales_order` - `db.trigger.sales_order_line` - `db.trigger.stock_move` - `db.trigger.product` - `n8n.adressetikette-erstellen` - Outbox-Ablage ueber `outbound_webhook_event` ## 2. Zweck Dieses Dokument beschreibt die aktuelle DB- und Prozesswahrheit fuer Phase 1 auf dem DEV-Stand. Es ist eine technische Uebersicht fuer Analyse, Umsetzung und Review. Nicht Bestandteil dieses Dokuments sind: - API-Details - UI-Details - Migrations-Rollout-Planung - offene Zielbilddiskussionen ausserhalb des aktuellen DEV-Standes ## 3. Normatives Datenmodell ### 3.1 `party` Zweck: - gemeinsamer Kontaktstamm fuer Kunden und Lieferanten Ist-Stand: - `type` ist `customer`, `supplier` oder `both` - `status` ist `active` oder `inactive` - `email` wird fuer Match/Upsert genutzt ### 3.2 `address` Zweck: - Rechnungs- und Lieferadressen pro `party` Ist-Stand: - `type` ist `billing` oder `shipping` - `raw_payload` speichert die Rohdaten des Eingangsdokuments ### 3.3 `contact` Zweck: - zusaetzliche Ansprechpartnerdaten pro `party` Ist-Stand: - keine weitere Prozesslogik im Code gefunden ### 3.4 `product` Zweck: - lagergefuehrtes Produkt mit Bestand und Charge Ist-Stand: - `sku` ist eindeutig - beim Insert erzeugt der Trigger `trg_product_bootstrap_lots` sofort eine `current`- und eine `open`-Charge ### 3.5 `sellable_item` Zweck: - verkaufbarer Shop-Artikel Ist-Stand: - Bestellungen referenzieren zuerst `sellable_item` - der Import kann neue `sellable_item`-Datensaetze automatisch erzeugen ### 3.6 `external_item_alias` Zweck: - Mapping externer Shop-Daten auf `sellable_item` Ist-Stand: - `source_system = 'wix'` - Aufloesung ueber Artikelnummer, normalisierten Titel oder Originaltitel ### 3.7 `sellable_item_component` Zweck: - Stueckliste eines `sellable_item` auf lagergefuehrte Produkte Ist-Stand: - `qty_per_item > 0` - wird vom Import und von Seed-Skripten gepflegt ### 3.8 `warehouse` Zweck: - oberer Lagerstandort Ist-Stand: - im Seed ist ein Hauptlager vorgesehen ### 3.9 `location` Zweck: - konkreter Lagerort innerhalb eines `warehouse` Ist-Stand: - `type` ist `storage`, `receiving`, `dispatch` oder `adjustment` ### 3.10 `stock_lot` Zweck: - Charge eines Produkts Ist-Stand: - `status` ist `open`, `current` oder `closed` - pro Produkt sind `current` und `open` per Unique Index abgesichert - die live DB hat keine `sellout_date`- oder `warning_state`-Spalten - die Sicht `v_stock_lot_balance` ist die Bestandswahrheit ### 3.11 `payment_method` Zweck: - normalisierte Zahlungsart Ist-Stand: - Seed-Werte im DEV: `card`, `twint`, `bank_transfer`, `cash`, `paypal` ### 3.12 `shipping_method` Zweck: - normalisierte Versandart Ist-Stand: - Seed-Werte im DEV: `post_standard`, `pickup` ### 3.13 `sales_order` Zweck: - Bestellkopf fuer Online-Import und Direktverkauf Ist-Stand: - `external_ref` ist eindeutig - `order_source` ist `wix` oder `direct` - `party_id` ist nullable - `payment_status` ist faktisch auf `paid` beschraenkt - `shipping_date` existiert im live Schema und wird per Trigger berechnet, falls leer ### 3.14 `sales_order_line` Zweck: - Bestellposition Ist-Stand: - `qty_cancelled` und `line_status` werden per Trigger synchronisiert - `sellable_item_id` kann `NULL` sein ### 3.15 `stock_move` Zweck: - Bewegungsjournal fuer Zu- und Abgaenge Ist-Stand: - `move_type` ist `in`, `out`, `transfer` oder `adjustment` - Bewegungen werden vor Insert/Update gegen Negativbestand validiert ### 3.16 `sales_order_line_lot_allocation` Zweck: - explizite Rueckverfolgung zwischen Bestellposition und Charge Ist-Stand: - `allocation_status` ist `reserved`, `allocated`, `released` oder `cancelled` - im Import wird `allocated` verwendet ### 3.17 `audit_log` Zweck: - technische Historisierung Ist-Stand: - im Schema vorhanden - im geprüften PHP- und n8n-Flow nicht als zentraler Pflichtpfad sichtbar ### 3.18 `outbound_webhook_event` Zweck: - Outbox fuer ERP-Events Ist-Stand: - Spalten: `event_type`, `event_key`, `aggregate_type`, `aggregate_id`, `payload`, `status`, `attempt_count`, `next_attempt_at`, `last_attempt_at`, `last_error`, `created_at`, `sent_at` - `fn_enqueue_event(...)` schreibt idempotent in diese Tabelle - ein Dispatcher/Worker fuer die Auslieferung wurde im geprüften DEV-Baum nicht gefunden ### 3.19 `v_stock_lot_balance` Zweck: - berechneter Chargensaldo Ist-Stand: - die Sicht ermittelt `qty_in`, `qty_out` und `qty_net` - sie ist die Grundlage fuer Bestandspruefung und Auto-Switch ## 4. Normatives Prozessmodell Alle Prozesse gehoeren fachlich zur Phase-1-Bestell-, Lager- und Integrationslogik. ### 4.1 `n8n.bestell-eingang-online-shop` Fachliche Aufgabe: - E-Mail auf `Neue Bestellung` erkennen - Payload in ERP-JSON umformen - JSON per HTTP POST an `order-import.php` senden Liest: - IMAP-Eingang - n8n-Extraktionslogik Schreibt: - keine DB direkt Fachliche Wirkung: - n8n ist hier nur die Integrationshuelle fuer den ERP-Import ### 4.2 `order-import.php` Fachliche Aufgabe: - eingehende Bestelldaten idempotent in die ERP-DB schreiben - Kontakte, Adressen, Bestellkopf, Positionen und Chargenrueckverfolgung aufbauen - bei Reimport vorhandene Allokationen zurueckbuchen - nach Commit die Label- und Excel-Flows direkt anstossen Liest: - `.env` - eingehendes Webhook-JSON - `party`, `address`, `sales_order`, `sales_order_line` - `external_item_alias` - `sellable_item_component` - `stock_lot` - `v_stock_lot_balance` Schreibt: - `party` - `address` - `sales_order` - `sales_order_line` - `sellable_item` - `external_item_alias` - `sellable_item_component` - `warehouse` - `location` - `stock_lot` - `stock_move` - `sales_order_line_lot_allocation` Fachliche Wirkung: - die Bestellung wird im ERP gespeichert und mit Lagerbewegung verknuepft - vorhandene Allokationen derselben `external_ref` werden vor dem Neuimport rueckwaerts gebucht - nach erfolgreichem Commit werden Label- und Excel-N8N-Flows direkt per HTTP ausgelöst ### 4.3 `db.trigger.sales_order` Fachliche Aufgabe: - `shipping_date` aus `order_date` ableiten - `order.imported` und `order.cancelled.full` in die Outbox schreiben - `direct`-Bestellungen automatisch mit `DIR-...` Nummer versehen Liest: - `sales_order` - `fn_next_business_day(timestamp)` Schreibt: - `sales_order.shipping_date` - `outbound_webhook_event` Fachliche Wirkung: - fehlendes `shipping_date` wird auf den naechsten Werktag gesetzt - neue Auftraege und Vollstornos werden als Event in die Outbox gelegt - direkte Auftraege erhalten bei leerer Nummer automatisch einen `DIR-...`-Ref ### 4.4 `db.trigger.sales_order_line` Fachliche Aufgabe: - `line_status` aus `qty_cancelled` ableiten - Partial-Cancel-Event enqueuen Liest: - `sales_order_line` Schreibt: - `sales_order_line.line_status` - `outbound_webhook_event` Fachliche Wirkung: - `allocated`, `partially_cancelled` und `cancelled` werden deterministisch gesetzt - ein Wechsel auf `partially_cancelled` erzeugt ein `order.cancelled.partial`-Event ### 4.5 `db.trigger.stock_move` Fachliche Aufgabe: - Negativbestand verhindern - Chargenwechsel bei leerer `current`-Charge ausloesen Liest: - `stock_move` - `stock_lot` - `v_stock_lot_balance` Schreibt: - `stock_lot` - `outbound_webhook_event` Fachliche Wirkung: - `out`-Bewegungen sind nur auf `current`-Chargen erlaubt - bei `qty_net <= 0` wird die naechste `open`-Charge `current` - danach wird wieder eine neue `open`-Charge angelegt ### 4.6 `db.trigger.product` Fachliche Aufgabe: - neue Produkte sofort chargenfaehig machen Liest: - `product` Schreibt: - `stock_lot` Fachliche Wirkung: - jedes neue Produkt bekommt direkt eine `current`- und eine `open`-Charge ### 4.7 `n8n.adressetikette-erstellen` Fachliche Aufgabe: - Lieferadresse entgegennehmen - Felder normalisieren - HTML/CSS in PDF/PNG umsetzen - Ergebnis per SFTP hochladen Liest: - Webhook-Input - Gotenberg / pdf2png / SFTP-Ziele Schreibt: - keine ERP-DB Fachliche Wirkung: - eigenstaendiger Ausgabekanal fuer Versandetiketten ### 4.8 Outbox-Ablage ueber `outbound_webhook_event` Fachliche Aufgabe: - Eventdaten fuer spaetere Auslieferung sammeln Liest: - `fn_enqueue_event` Schreibt: - `outbound_webhook_event` Fachliche Wirkung: - Queue wird im geprüften DEV-Stand befuellt - ein dazugehoeriger Dispatcher/Worker wurde im Code nicht gefunden ## 5. Technische Einbettung Die reale Phase-1-Logik bildet einen kleinen operativen Kern mit vier Schwerpunkten: 1. Kontakt- und Adressstamm 2. Bestellimport via n8n -> PHP -> DB 3. Chargen- und Lagerbewegung mit Trigger-Logik 4. Label-Generierung als separater n8n-Ausgang Wichtige Abweichungen zum alten Zielbild: - Sellout-Forecast ist im live Schema nicht aktiv. - Ein Dispatcher fuer `outbound_webhook_event` wurde im geprüften DEV-Baum nicht gefunden. - Eine separate Direct-Sale-Eingabestrecke wurde im geprüften DEV-Baum nicht gefunden; vorhanden ist nur die DB-/Trigger-Unterstuetzung fuer `order_source = direct`. ## 6. Kurzfazit Die aktuelle technische Wahrheit von Phase 1 ist auf Nachvollziehbarkeit und direkte Integrationspfade ausgerichtet: - Bestellungen werden idempotent gespeichert - Lagerbestand wird ueber Chargen und Bewegungen abgebildet - Rueckverfolgung erfolgt ueber explizite Allokationen - n8n importiert und erzeugt Labels direkt - Outbox-Events werden geschrieben, aber ein Dispatcher ist im geprüften DEV-Stand nicht vorhanden