DEV-Ist-Stand fuer Phase 1 dokumentiert

This commit is contained in:
2026-06-02 21:49:57 +02:00
parent 0d8353fb9c
commit 70272e81d6
+501
View File
@@ -0,0 +1,501 @@
# 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