502 lines
10 KiB
Markdown
502 lines
10 KiB
Markdown
# 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`kzz
|
|
- `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
|