Files
erp_naurua/docs/CONCEPT.md

263 lines
14 KiB
Markdown

# Concept
## Vision
ERP Naurua bildet den operativen Kern fuer Einkauf, Lager und Auftragsabwicklung mit voller Nachvollziehbarkeit von Kontakten, Bestellungen und Chargen.
## Fachliches Zielbild
ERP Naurua dient als zentraler operativer Kern fuer einen Heilpilzhandel mit zwei Verkaufskanaelen: Online-Shop (Wix) und Direktverkauf (z. B. Marktstand). Online-Bestelldaten kommen ueber eine Integrationsschicht (n8n Webhook), Direktverkaeufe werden im ERP manuell als Tages-/Sammelerfassung gebucht. Das System ist intern fuer Lager, Chargen, MHD sowie Rueckverfolgung verantwortlich. Die Kundinnen und Kunden werden im gemeinsamen Kontaktmodell verwaltet (Kunde/Lieferant als Rollen); Direktverkaeufe duerfen ohne individuellen Kundenkontakt erfasst werden. Bestellungen werden mit Chargenbewegungen verknuepft, sodass jede Entnahme auf Charge und Lagerort zurueckfuehrbar ist.
Das Zielbild ist modular erweiterbar: Rechnungswesen, Versandautomatisierung und Beratung werden als Module angefuegt, ohne das Kernmodell zu zerbrechen. Die Prioritaet in Schritt 1 liegt auf Datenqualitaet, Rueckverfolgbarkeit und klaren, auditierbaren Bewegungen.
## Scope
Der erste Umsetzungsschritt umfasst ausschliesslich die Module, die mit `(1)` markiert wurden:
1. Lagerverwaltung mit Chargen, MHD, Zu- und Abgaengen
2. Bestellerfassung mit Chargenrueckverfolgung
3. Kontakt-Angaben inkl. Liefer- und Kundenangaben
## Non-Goals
Nicht Bestandteil von Schritt 1:
1. Kreditoren- und Debitoren-Stammdaten
2. Rechnungseingang (inkl. OCR)
3. Rechnungspruefung und Freigabe-Workflows
4. Automatische Zuordnung von Rechnungen zu Bestellungen/Lieferungen/Leistungen
5. Operative Nachverfolgung von Rechnungsvorgaengen
## Principles
1. Traceability first: Jede Warenbewegung und jede Bestellung muss auf Charge rueckfuehrbar sein; bei Direktverkauf ist ein Laufkundenfall ohne Kontakt zulaessig.
2. Daten vor UI-Komfort: Zuerst robuste Datenmodelle und Prozesse, danach Optimierungen.
3. Erweiterbarkeit: Schritt-1-Modelle muessen spaetere Finanz- und Rechnungsprozesse aufnehmen koennen.
4. Einfache Integrationsfaehigkeit: Importfaehigkeit fuer externe Bestelldaten ist von Beginn an mitzudenken.
5. Kanaltrennung: Online- und Direktverkaeufe muessen technisch eindeutig unterscheidbar sein.
## Users And Roles
1. Einkauf/Disposition: Erfasst und verwaltet Bestellungen.
2. Lager/Logistik: Pflegt Chargen, MHD sowie Warenzu- und -abgaenge.
3. Administration/Backoffice: Verwalten von Kunden- und Lieferkontakten.
## Domain Overview
Schritt 1 basiert auf drei Kern-Domaenen:
1. Kontakte: Kunden und Lieferanten mit abrechnungs- und lieferrelevanten Angaben.
2. Bestellungen: Bestellungskopf und Positionen aus Online-Import und Direktverkauf, gekoppelt an betroffene Chargen; Kontaktzuordnung ist fuer Direktverkauf optional.
3. Lager/Chargen: Bestandsfuehrung je Produkt/Charge inkl. MHD und Bewegungen.
Kernaussage: Eine Bestellung kann mehrere Positionen enthalten; jede Position kann einer oder mehreren Chargen zugeordnet werden; jede Charge hat Bewegungen und MHD-Status.
## System Overview
Schritt-1-Systemkomponenten:
1. Kontaktmodul (CRUD, Basisspeicherung, optionale Felder fuer spaetere Erweiterung)
2. Bestellmodul (Bestellungskopf, Positionen, Upsert ueber externe Bestellnummer)
3. Artikel-Mapping (externe Shop-Artikel -> interne verkaufbare Artikel -> lagergefuehrte Produkte)
4. Lagermodul (Chargenstamm, MHD, Bewegungsjournal, aktueller Bestand)
5. Import-Schnittstelle fuer initiale Bestelldaten (n8n Webhook JSON)
6. Direktverkaufs-Erfassung (Tages-/Sammelverkauf ohne Kundenregistrierung)
## Grobmodell (Schema) Schritt 1
Die folgenden Entitaeten bilden das Zielschema fuer Schritt 1. Felder sind als grobe Vorschlaege zu verstehen und werden in der Detailphase konkretisiert.
Der Entwurf des SQL-Schemas liegt unter:
`/Users/mathias/Documents/Dokumente Chouchou/Codebases/erp_naurua/docs/SCHEMA_PHASE1.sql`
Die ausfuehrbaren Migrationen liegen unter:
`/Users/mathias/Documents/Dokumente Chouchou/Codebases/erp_naurua/db/migrations/`
1. `party`
- Zweck: Kunde oder Lieferant (Rollenmodell).
- Kernfelder: `id`, `type` (customer/supplier/both), `name`, `email`, `phone`, `status`.
2. `address`
- Zweck: Liefer- und Rechnungsadressen pro Kontakt.
- Kernfelder: `id`, `party_id`, `type` (billing/shipping), `street`, `house_number`, `zip`, `city`, `state_code`, `country_name`, `country_iso2`.
3. `contact`
- Zweck: Ansprechpartner oder zusaetzliche Kontaktpunkte.
- Kernfelder: `id`, `party_id`, `first_name`, `last_name`, `email`, `phone`.
4. `product`
- Zweck: Lagergefuehrtes Produkt (Bestandsfuehrung, Charge, MHD).
- Kernfelder: `id`, `sku`, `name`, `status`, `uom`.
5. `sellable_item`
- Zweck: Verkaufbarer Shop-Artikel (kann Bundle sein).
- Kernfelder: `id`, `item_code`, `display_name`, `status`.
6. `external_item_alias`
- Zweck: Mapping von externen Webshop-Artikeldaten auf interne Artikel.
- Kernfelder: `id`, `source_system`, `external_article_number`, `external_title`, `title_normalized`, `sellable_item_id`.
7. `sellable_item_component`
- Zweck: Stueckliste je Shop-Artikel auf lagergefuehrte Produkte.
- Kernfelder: `id`, `sellable_item_id`, `product_id`, `qty_per_item`.
8. `warehouse`
- Zweck: Lagerstandort auf hoher Ebene.
- Kernfelder: `id`, `name`, `code`.
9. `location`
- Zweck: Lagerorte innerhalb eines Lagers.
- Kernfelder: `id`, `warehouse_id`, `code`, `name`, `type` (storage/receiving/dispatch).
10. `stock_lot`
- Zweck: Charge/Batch mit MHD.
- Kernfelder: `id`, `product_id`, `lot_number`, `mfg_date`, `expiry_date`, `status`, `sellout_date`, `warning_state`.
11. `payment_method` und `shipping_method`
- Zweck: Normalisierte interne Werte fuer Zahlungs- und Lieferart.
- Kernfelder: `id`, `code`, `label`, `is_active`.
12. `sales_order`
- Zweck: Bestellungskopf (Online-Shop und Direktverkauf).
- Kernfelder: `id`, `external_ref` (unique), `order_source`, `party_id` (optional bei Direktverkauf), `order_date`, `order_status`, `payment_status`, Summenfelder, `webhook_payload`.
13. `sales_order_line`
- Zweck: Bestellpositionen.
- Kernfelder: `id`, `sales_order_id`, `line_no`, `sellable_item_id`, `raw_external_article_number`, `raw_external_title`, `qty`, `unit_price`.
14. `stock_move`
- Zweck: Zu- und Abgaenge sowie Umlagerungen.
- Kernfelder: `id`, `product_id`, `lot_id`, `from_location_id`, `to_location_id`, `qty`, `move_type`, `move_date`.
15. `sales_order_line_lot_allocation`
- Zweck: Explizite Zuordnung Bestellposition <-> Charge (Rueckverfolgung).
- Kernfelder: `id`, `sales_order_line_id`, `product_id`, `lot_id`, `qty`, `stock_move_id`.
16. `audit_log`
- Zweck: Standardisierte Historisierung von Importen und Aenderungen.
- Kernfelder: `id`, `entity_name`, `entity_id`, `action`, `changed_at`, `before_data`, `after_data`.
17. `outbound_webhook_event`
- Zweck: Outbox-Queue fuer robuste ERP -> n8n Zustellung.
- Kernfelder: `id`, `event_type`, `event_key`, `aggregate_type`, `aggregate_id`, `payload`, `status`, `attempt_count`.
## Skizze (ERD)
```mermaid
erDiagram
PARTY ||--o{ ADDRESS : "has"
PARTY ||--o{ CONTACT : "has"
PARTY o|--o{ SALES_ORDER : "places_or_walkin"
SALES_ORDER ||--o{ SALES_ORDER_LINE : "contains"
SELLABLE_ITEM ||--o{ SALES_ORDER_LINE : "ordered_item"
SELLABLE_ITEM ||--o{ EXTERNAL_ITEM_ALIAS : "mapped_from"
SELLABLE_ITEM ||--o{ SELLABLE_ITEM_COMPONENT : "contains"
PRODUCT ||--o{ SELLABLE_ITEM_COMPONENT : "component"
WAREHOUSE ||--o{ LOCATION : "contains"
PRODUCT ||--o{ STOCK_LOT : "has"
PRODUCT ||--o{ STOCK_MOVE : "moves"
STOCK_LOT ||--o{ STOCK_MOVE : "tracks"
LOCATION ||--o{ STOCK_MOVE : "from_to"
SALES_ORDER_LINE ||--o{ SALES_ORDER_LINE_LOT_ALLOCATION : "allocates"
STOCK_LOT ||--o{ SALES_ORDER_LINE_LOT_ALLOCATION : "traces_to"
PRODUCT ||--o{ SALES_ORDER_LINE_LOT_ALLOCATION : "allocated_product"
```
## Phase-1 Beziehungen (fachlich)
1. Ein Kontakt (`party`) kann mehrere Adressen und Kontakte besitzen.
2. Eine Bestellung kann einem Kontakt zugeordnet sein (`sales_order.party_id`), bei Direktverkauf ist `NULL` zulaessig.
3. Eine Bestellung besteht aus mehreren Positionen (`sales_order_line`).
4. Jede Position referenziert genau einen verkaufbaren Artikel (`sellable_item`), nicht direkt ein Lagerprodukt.
5. Ein verkaufbarer Artikel kann aus mehreren lagergefuehrten Produkten bestehen (`sellable_item_component`).
6. Chargen (`stock_lot`) gehoeren zu genau einem lagergefuehrten Produkt (`product`).
7. Lagerbewegungen (`stock_move`) referenzieren Produkt und Charge und bewegen Menge von einem Lagerort zum anderen.
8. Rueckverfolgung erfolgt ueber `sales_order_line_lot_allocation` und kann je Position ueber mehrere Chargen gesplittet werden.
9. `sales_order.external_ref` ist eindeutig; fuer Online-Import ist es der Upsert-Schluessel, fuer Direktverkauf wird es im ERP mit `DIR-`-Praefix erzeugt.
10. Outbound-Ereignisse werden ueber `outbound_webhook_event` idempotent publiziert.
## Data And Integrations
Minimale Kernobjekte fuer Schritt 1:
1. Kontakt
2. Adresse (Rechnungsadresse, Lieferadresse)
3. Bestellung
4. Bestellposition
5. Verkaufbarer Artikel (`sellable_item`)
6. Lagerprodukt (`product`)
7. Artikel-Mapping (`external_item_alias`)
8. Artikel-Stueckliste (`sellable_item_component`)
9. Charge
10. Lagerbewegung (Zugang/Abgang)
11. Bestellpositions-zu-Chargen-Zuordnung (`sales_order_line_lot_allocation`)
12. Verkaufsquelle im Auftrag (`sales_order.order_source`: `wix` | `direct`)
Zuordnung zum Beispiel-Datensatz:
1. `BestellungNr`, `Zahlungsstatus`, Summenfelder -> Bestellung
2. `Vorname_*`, `Nachname_*`, `EmailKunde` -> Kontakt
3. `*_RgAdr`, `*_LfAdr` -> Adresse
4. `lineItems[*]` -> Bestellposition mit Rohdaten (`raw_external_article_number`, `raw_external_title`)
5. `lineItems[*]` + Alias-Mapping -> interner Artikel (`sellable_item`)
6. Artikel-Stueckliste -> benoetigte Lagerprodukte pro Position
7. Kommissionierung/Abgang -> Chargenzuordnung in `sales_order_line_lot_allocation`
Webhook-Regeln fuer Schritt 1 (Online-Shop):
1. Upsert erfolgt ueber `sales_order.external_ref = BestellungNr`.
2. Zahlungsstatus aus Online-Bestellung wird intern standardmaessig als `paid` gespeichert.
3. Rechnungsadresse darf leer sein; Lieferadresse wird normal erfasst.
4. Adressen speichern Klarname (`country_name`) und ISO-Code (`country_iso2`) parallel.
5. Adressdaten werden nur gespeichert, nicht serverseitig validiert (Validierung erfolgt im Shop).
Direktverkauf-Regeln fuer Schritt 1:
1. Direktverkaeufe werden im ERP erfasst (`sales_order.order_source = direct`) und kommen nicht ueber n8n-Webhook.
2. Die Bestellnummer wird im ERP erzeugt und hat den Praefix `DIR-` (z. B. `DIR-20260329-00017`).
3. `sales_order.party_id` ist optional; bei Laufkundschaft wird kein individueller Kontakt angelegt.
4. Positionen werden als Sammelverkauf mit Mengen je Produkt erfasst.
5. Der brutto Gesamtpreis kann auf Flaschenebene verteilt werden (Durchschnittspreis = Gesamtpreis / Gesamtmenge).
6. Zahlungsart wird explizit gespeichert (z. B. `twint`, `cash`, `paypal`, `bank_transfer`).
Lagerregeln fuer Schritt 1:
1. Pro Produkt existiert operativ genau eine `current`-Charge und genau eine `open`-Charge.
2. `open` darf nicht fehlen; sie ist der direkte Ueberlauf fuer den naechsten operativen Entnahmefall.
3. Statusfluss fuer Chargen: `open -> current -> closed`.
4. Bei Erreichen von `qty_net <= 0` auf der `current`-Charge erfolgt automatischer Wechsel auf die vorhandene `open`-Charge.
5. Nach dem Wechsel wird automatisch wieder eine neue `open`-Charge erzeugt (lot_number bleibt initial leer bis manuell gesetzt).
6. Negative Chargenbestaende sind nicht zulaessig.
7. Chargensalden (`in/out/net`) werden aus Bewegungen berechnet.
8. Jede Verkaufsbewegung muss einer Charge zugeordnet sein (kein chargenloser Abgang).
9. Verkaufsbuchung erfolgt standardmaessig gegen die `current`-Charge.
10. Korrekturen bleiben durch editierbare Datensaetze und/oder Korrekturbewegungen moeglich.
11. Abverkaufdatum wird systemintern berechnet; Warnstatus wird fuer UI bereitgestellt (ohne E-Mail-Prozess in Phase 1).
## Milestones
Schritt 1 wird in drei Modulpakete umgesetzt:
1. M1 Kontaktmodul: Datenmodell, API/Service, Basisspeicherung
2. M2 Bestellmodul: Bestellung + Positionen + optionale Kontaktverknuepfung + Upsert
3. M3 Lagermodul: Charge, MHD, Bewegungen, Bestandssicht, Rueckverfolgung zur Bestellung
Operative Ablaufdetails liegen unter:
`/Users/mathias/Documents/Dokumente Chouchou/Codebases/erp_naurua/docs/PROCESS_PHASE1.md`
Komponenten-Spezifikation liegt unter:
`/Users/mathias/Documents/Dokumente Chouchou/Codebases/erp_naurua/docs/SPEC_PHASE1_COMPONENTS.md`
DB-Migrationsdetails liegen unter:
`/Users/mathias/Documents/Dokumente Chouchou/Codebases/erp_naurua/db/README.md`
Erfolgskriterien Schritt 1:
1. Eine Bestellung kann mit Kunden-/Lieferkontakt oder als Direktverkauf ohne Kontakt erfasst werden.
2. Positionen koennen einer oder mehreren Chargen zugeordnet werden.
3. Lagerzugaenge/-abgaenge aktualisieren Bestand pro Charge konsistent.
4. Rueckverfolgung Bestellung <-> Charge <-> Lagerbewegung ist technisch vorhanden.
## Entscheidungen Fuer Umsetzung
1. Eindeutige Bestellidentifikation ueber `sales_order.external_ref` (Shop: `BestellungNr`, Direktverkauf: `DIR-...`); Dateneingang via Upsert.
2. Rechnungsadresse ist optional (`NULL` zulaessig), Lieferadresse wird separat gespeichert.
3. Interne Normalisierung von Zahlungs- und Liefermethoden ueber Mapping-Tabellen.
4. Land wird als Klarname und ISO-Code gespeichert.
5. Initial keine serverseitige Adressvalidierung.
6. Keine Buchhaltungs-/Rechnungsfunktion in Schritt 1.
7. Chargenrueckverfolgung wird ueber explizite Bestellpositions-Allokation aufgebaut.
8. Lagerbetrieb mit genau einer aktiven und einer vorbereiteten Charge pro Produkt (`current` + `open`).
9. Storno-Fall wird im Modell vorbereitet (Status `cancelled` + Freigabe von Reservierungen).
10. Chargennummer fuer neu vorbereitete Charge wird initial manuell durch Mitarbeitende gesetzt.
11. Abverkaufprognose und Warnlogik werden in die ERP-Datenbank uebernommen; E-Mail-Erinnerungen sind in Phase 1 deaktiviert.