263 lines
14 KiB
Markdown
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.
|