Roadmap & 4-Wochen-Umsetzungsplan
Ziel: Go-Live der vollständigen Plattform in 4 Wochen (Start Mo 30.06.2026 → Go-Live Fr 25.07.2026). Aggressiv, aber machbar — KI-gestützte Umsetzung direkt aus dem entscheidungs-vollständigen Pflichtenheft, parallele Arbeitsströme und tägliche Preview-Deploys. Die Detail-Phasen P0–P4 (unten) bleiben gültig und werden auf die vier Wochen verdichtet/parallelisiert abgebildet.
4-Wochen-Umsetzungsplan bis Go-Live
Diagramm wird geladen …
Woche für Woche
| Woche | Fokus | Liefergegenstand (Module) |
|---|---|---|
| W1 · 30.06–04.07 | Fundament + Stammdaten | Supabase + Auth (Entra/Supabase) + RLS + alle 48 Tabellen migriert, i18n, Audit-Log, Design-System, CI/Deploy · Modul 1 CRM · Modul 3 Produkte/Preise |
| W2 · 07.07–11.07 | Kommerziell + Logistik | Modul 4 Offerten (PDF/WhatsApp, Code-Rabatt) · Modul 5 Logistik (Boxen UUID+QR, 10 Tracking-Schritte, Container, Depot, Fahrer-App) |
| W3 · 14.07–18.07 | Finanzen + Automatisierung | Modul 6 Finanzen (movements-Ledger, Auto-Posting, MWST-befreit, Abschluss) + Mehrwährung CHF/DOP + Revolut · Modul 7 Affiliate · Modul 2 WhatsApp-API + OCR (self-hosted) |
| W4 · 21.07–25.07 | Härtung + Migration + Go-Live | Security/RLS-Review, E2E-Tests, Audit-Restpunkte · Datenmigration Alt-ERP/Excel + Cutover · Schulung/Abnahme · 🚀 Go-Live 25.07 |
Parallel-Track (extern — blockiert Go-Live nicht)
Läuft neben dem Bau und kann um/nach dem Go-Live abschließen:
- Recht: Datenschutzerklärung anwaltlich prüfen · DPAs zeichnen (Supabase/Vercel/Resend/WhatsApp/Revolut) · Supabase-Region = EU fixieren.
- GS1-Mitgliedschaft + GCP → SSCC danach scharfschaltbar (Caja ist SSCC-ready, kein Umbau).
Was 4 Wochen tragfähig macht
- Spec ist entscheidungs-vollständig — keine offenen Architektur-/Geschäftsentscheide mehr im kritischen Pfad.
- Schema + Module direkt aus dem Pflichtenheft generierbar (48 Tabellen, RLS-Matrix, Auto-Posting-Funktionen sind spezifiziert).
- Parallele Ströme (Daten-Layer ‖ UI ‖ Migration) statt strenger Phasen-Sequenz.
- Tägliche Preview-Deploys (Supabase-Branch je Vercel-Preview) → kontinuierliche Abnahme statt Big-Bang am Ende.
Top-Risiken (früh angehen)
- Datenmigration Alt-ERP/Excel (Datenqualität, Mapping) → ab W1 parallel vorbereiten, nicht erst W4.
- OCR self-hosted (Proxmox) Setup + Revolut-API-Onboarding → in W3, mit Fallback (manueller KYC-Review / CSV-Statement-Import), falls Provider-Onboarding länger dauert.
- WhatsApp-Business-API-Freigabe (Meta Templates/Opt-in) → Antrag in W1 stellen (Vorlaufzeit).
Übersicht
| Phase | Name | Kerngewinn | → im 4-Wochen-Plan |
|---|---|---|---|
| P0 | Fundament | Sicheres, mehrsprachiges Grundgerüst; kein Fach-Feature ohne diese Basis | W1 |
| P1 | CRM + Finanzen-Kern | Erster produktiver Ersatz des Excel-ERP; Ledger läuft, Kunden sind drin | W1 (CRM) + W3 (Finanzen) |
| P2 | Produkte, Preise, Offerten, Affiliate-Codes | Vollständiger kommerzieller Zyklus: Preisliste → Offerte → Auftrag → Provision | W1 (Produkte) + W2 (Offerten) + W3 (Affiliate) |
| P3 | Logistik: Boxen, Tracking, Container | Operativer Kernfluss sichtbar; Box-QR, 10 Schritte, Container-Board, Depot-Lifecycle | W2 |
| P4 | WhatsApp + OCR + Dedup | Automatisierter Lead-Intake; Ausweis-Scan → Kontakt; Duplikat-Bereinigung | W1 (Dedup) + W3 (WhatsApp/OCR) |
Die klassische sequentielle Schätzung (~18–22 Wochen) wird im 4-Wochen-Plan oben durch Parallelisierung + KI-gestützte Generierung aus der Spec verdichtet. Die Phasen-Detailbeschreibungen unten bleiben unverändert gültig — sie sind die fachliche Aufschlüsselung dessen, was in den vier Wochen gebaut wird.
P0 — Fundament
Ziele
Alle technischen Querschnittsthemen sind produktionsreif, bevor auch nur eine einzige Zeile Fach-Code geschrieben wird. Das verhindert spätere Sicherheits-Nachbesserungen und macht i18n, Audit und RLS zur Selbstverständlichkeit im gesamten Projekt.
Enthaltene Tabellen / Objekte
Auth & Identität:
roles(Seed:ADMIN,BUCHHALTUNG,OPERATIONS,FAHRER,AFFILIATE,READONLY)app_users(Triggerhandle_new_user()aufauth.users)user_roles(n:m)affiliate_users(1:1-Bridge, Struktur bereits, Daten kommen erst in P2)- RLS-Hilfsfunktionen:
has_role(),is_internal(),current_affiliate_id()
i18n:
locale_strings(Namespace/Key/Locale)- Seed-Skript aus
GLOSSAR-ERP_DE-ES-EN.csv(alle drei Locales, alle Namespaces) label_de/es/en-Spalten an allen Lookup-Tabellen
Geteilte Fach-Lookups (Heimat P0, konsumiert ab P1):
movement_types(12 Codes mit Verhaltens-Flags, Seed)payment_methods(5 Codes inkl.channel-Klassifikation, Seed)vat_rates(CH-Sätze,active=false, schema-ready)
Audit & Periodensperre:
audit_log(append-only, Bigint-PK, Rules + Grant-Entzug)- Trigger-Funktion
fn_audit()(generisch, wird an Fach-Tabellen gehängt) monthly_closings(Struktur; Abschlüsse kommen erst ab P1)fn_guard_closed_period()(Guard-Funktion)
Storage:
storage_objects(Metadaten-Tabelle; physische Bucketskyc,documents,receipts,public-assetsin Supabase Storage mit RLS)
Notifications (Outbox-Struktur):
notifications(Tabelle +dedupe_key-Unique)- Erste Templates:
AUTH_MAGIC_LINK(Supabase-SMTP)
Plattform-Screens:
- Login / Magic-Link + OTP-Fallback (dreisprachig, ES-Default)
- Team & Zugriff — ADMIN (Nutzer/Rollen verwalten, Affiliate-Verknüpfung)
- Profil & Spracheinstellungen
- Audit-Journal (read-only, Desktop Tabelle / Handy Timeline)
- Dokument-Browser (Grundgerüst, Buckets navigierbar)
Infrastruktur:
- Next.js App Router + TypeScript + Tailwind 4 + shadcn/ui (Atlas-Stack)
- Supabase-Projekt (Region
eu-central-1), Migrationen viasupabase/migrations/ - Vercel-Deployment,
RESEND_API_KEY/RESEND_FROMin Vercel-Env - Design-Tokens (Light/Dark, CSS-Variablen)
- Bottom-Tab-Bar + Drawer (Handy) / Sidebar (Desktop) — leere Navigationsstruktur
- Command-Palette (⌘K) — Grundgerüst
pref_lang-Cookie + i18n-Resolver (RSC-Layout)- PWA-Manifest + Service-Worker-Grundgerüst (Offline-Shell)
- Vercel Web Analytics aktiviert
Definition of Done P0
- Alle Tabellen migriert, alle Constraints aktiv
- Magic-Link-Login funktioniert für alle vier Team-Mitglieder
-
ADMIN-Rolle kann anderen Nutzern Rollen zuweisen - Sprachumschalter (DE/ES/EN) funktioniert,
preferred_localewird persistiert - Audit-Journal zeigt
action=loginnach erfolgreichem Login -
audit_log: UPDATE/DELETE schlagen auch fürservice_rolefehl (Rules-Test) - Alle RLS-Policies auf Plattform-Tabellen aktiv; ohne Rolle kein Datenzugriff
- Vercel-Deployment grün;
eu-central-1-Region bestätigt
Abhängigkeiten P0
Keine — P0 ist die Wurzel. Alles andere hängt davon ab.
Aufwandsklasse
XL — komplex wegen Auth-Trigger, RLS-Architektur, i18n-Seed, Audit-Unveränderbarkeit und PWA-Grundgerüst. Fehler hier kosten in späteren Phasen das Dreifache.
P1 — CRM + Finanzen-Kern
Ziele
Das Excel-ERP (Erfassung_Digitar) wird durch das movements-Ledger abgelöst. Alle Bestands-Buchungen werden migriert. Das Team kann ab P1-Ende ohne Excel arbeiten: Kontakte anlegen, Buchungen erfassen, Kasse/Dashboard einsehen, Monatsabschluss vorbereiten.
Der Website-Lead-Intake (POST /api/send-contact) wird direkt in die contacts-Tabelle geleitet — erstes produktives Ende-zu-Ende-Feature.
Enthaltene Tabellen
CRM (Modul 1):
contacts(Golden Record, inkl. i18npreferred_lang,lead_source, KYC-Felder als Spalten — OCR-Befüllung kommt erst P4)contact_roles(Bridgecustomer/lead/supplier/affiliate/recipient_dr)contact_role_labels(i18n-Seed)recipients(Empfänger DR — Struktur; produktive Nutzung ab P3 beim Auftrag)contact_merge_log(append-only, Merge-Workflow kommt in P4)
Finanzen (Modul 6):
movements(Kern-Ledger, inkl.source_type/source_id-Unique,voided_at)payment_links(explizite Zahlungsverknüpfung)monthly_closings- Views:
v_movements,v_kasse,v_debitoren,v_kreditoren,v_anzahlungen,v_dashboard,v_monatskontrolle fn_audit()-Trigger aufmovementsundmonthly_closingsfn_guard_closed_period()-Trigger aufmovements
Screens CRM:
- Kontaktliste (adaptiv: Karten Handy / Tabelle Desktop, Suche, Rollen-Filter)
- Kontaktdetail (Tabs: Info / Empfänger / Offerten / Aufträge / Finanzen / Dokumente)
- Kontakt anlegen / bearbeiten (Bottom-Sheet-Formular)
- Lead-Schnellerfassung WhatsApp (minimales Formular, Duplikat-Check live)
- Empfänger anlegen / bearbeiten (Grundgerüst; OCR-Befüllung folgt P4)
Screens Finanzen:
- Finanz-Dashboard (KPI-Kacheln aus
v_dashboard, Monatsfilter) - Kasse / Cashflow (
v_kasse, chronologisches Journal, laufender Saldo) - Schnellerfassung Quick-Entry (Bottom-Sheet: Vorgang → Zahlweise → Betrag → Datum → Partei → Notiz)
- Debitoren / Kreditoren (
v_debitoren/v_kreditoren, Ampeln, Swipe „Zahlung erfassen") - Anzahlungen / Depot (
v_anzahlungen, Grundgerüst; Depot-Lifecycle folgt P3) - Monatsabschluss (
v_monatskontrolle, Abschluss-Button) - Treuhänder-Export (CSV/Excel/PDF-Dialog, Edge-Function)
- Notifications-Protokoll (Outbox-Ansicht für interne Nutzer)
Migration (Excel → Caja, läuft parallel zu P1-Entwicklung)
- Migrations-Skript liest
Erfassung_Digitar(CSV-Export) und bulk-inserted inmovementsmitsource_type='import' period_keyausentry_dateberechnet (keine String-Parsung mehr)payment_methods.channelaus Zahlweise-Mapping (ersetzt Excel-classifyPaymentChannel)- Anfangssalden (
Saldo inicial caja/banco) alsmonthly_closings-Opening der ältesten Periode oder Settings-Wert (🔲 zu bestätigen) - Test:
v_dashboard-Werte gegen Excel-Summen validieren (Differenz < CHF 1.00)
Website-Lead-Wire-in
/api/send-contact(bereits produktiv) erhält zusätzlichensupabase-Client-Aufruf: Fuzzy-Check aufcontacts(E.164-Normalisierung + Name-Match), Insert oder Update,lead_source='website_form'- Notification an Mariela/Marcel (Kanal
email, TemplateLEAD_INTAKE_INTERNAL— neu) - Honeypot-Prüfung bleibt erhalten; Resend-E-Mail an Kunden bleibt erhalten
Definition of Done P1
- Alle Excel-Bewegungen migriert;
v_dashboard-Summen validiert - Website-Formular speist
contactslive (Test: Testsubmission erscheint in Caja) - Quick-Entry: manuelle Buchung erfassen, Kasse-Saldo aktualisiert sich sofort
- Zahlung gegen offene INVOICE:
payment_links-Eintrag, Status wechselt zuPAID - Monatsabschluss: Periode schliessen, anschliessender Insert in geschlossener Periode schlägt fehl
- Audit-Journal zeigt jede
movements-Mutation mitold_data/new_data - RLS:
AFFILIATE-Session sieht keinemovements-Zeilen;FAHRERsieht nur eigene Logistik-Quellen (noch leer) - Handy (375px): Karten-Layout überall, kein Horizontal-Scroll, alle Targets ≥ 44px
Abhängigkeiten P1
- Voraussetzung: P0 vollständig (Auth, Rollen, RLS-Helfer,
movement_types,payment_methods, Audit-Framework) contactsist Voraussetzung für alle weiteren Module (P2–P4 referenzierencontacts.id)movementsist Voraussetzung für Auto-Posting aus P2 (Offerten→Rechnungen) und P3 (Depot-Zahlungen)
Aufwandsklasse
XL — grösster Einzel-Aufwand der Roadmap wegen Excel-Migrations-Skript (Daten-Validierung aufwändig), der v_movements-View-Logik (1:1-Nachbau der Excel-Routing-Formeln H..N) und dem Ende-zu-Ende-Lead-Intake.
P2 — Produkte, Preise, Offerten, Affiliate-Codes
Ziele
Der vollständige kommerzielle Zyklus läuft: Preisliste konfiguriert → Offerte erstellt aus Katalog → PDF erzeugt → per WhatsApp/E-Mail versendet → akzeptiert → Auftrag + Rechnung angelegt → Affiliate-Provision vorgemerkt. Affiliate-Codes sind aktiv und werden durch den Offerten-Flow hindurchgereicht.
Enthaltene Tabellen
Produkte & Preise (Modul 3):
box_products(Seed: 9 Produkte +SRV_ABHOLUNG,label_de/es/en)zones(Seed: erste Zonen-Taxonomie DR, 🔲 finale Liste aus Betrieb)price_lists(effective-dated,is_default)price_list_items(Preis je Produkt × Zone; unveränderlich nach Aktivierung)product_depot_rates(Depot-Satz je Produkt × Preisliste)- View
v_current_prices - Preis-Resolver (Edge-Function oder Server-Action)
Offerten (Modul 4):
quotes(inkl.quote_status-Enum, Betragsfelder als Snapshots,referral_code_id)quote_lines(Produkt-Snapshot,price_list_entry_id, Rabatt-Felder)- Sequenz
quote_number_seq - Cron-Job: täglicher Expired-Lauf (
valid_until < today AND status='SENT') - PDF-Generierung (Server-Action, Bibliothek 🔲 zu bestätigen)
- Notifications:
QUOTE_SENT(E-Mail via Resend + WhatsApp-Deep-Link)
Affiliate (Modul 7 — Basis):
affiliates(Stammdaten, FK aufcontacts,affiliate_status)referral_codes(Code, Rabatt-Regel, Provisions-Regel, Gültigkeitszeitraum)commission_entries(Entstehungpendingbei Offerte-Konversion; Freigabeapprovedbeim Tracking-DELIVERED, #19/F-13 — nicht mehr beiinvoices.status → PAID)commission_payouts(gebündelte Auszahlung — Abrechnung folgt P2b oder als eigenes Increment)affiliate_users(Verknüpfung Login ↔ Affiliate-Konto, Struktur war P0, Daten jetzt)- RLS: Affiliate sieht nur eigene
quotes, eigenecommission_entries
Screens Produkte & Preise:
- Produktkatalog-Übersicht (Karten/Grid, Kategorie-Filter)
- Produktdetail (Tabs: Info / Preise / Depot / Historie)
- Preislisten-Verwaltung (Liste, Draft/Aktiv/Abgelaufen, Neu-Formular)
- Preislisten-Items befüllen (Raster Produkt × Zone; CSV-Import Drag & Drop)
- Preis-Resolver Testscreen (Admin, Desktop/Tablet)
Screens Offerten:
- Offerten-Liste (adaptiv: Karten Handy / Tabelle Desktop, Status-Filter)
- Offerte erstellen/bearbeiten (Wizard-Stepper Handy / Einseitiges Formular Tablet+)
- Offerten-Detail (Header + Aktionsleiste, Versand-History, Links zu Auftrag/Rechnung)
- Versand-Dialog (Bottom-Sheet Handy / Modal Tablet+, Kanal-Toggle, Vorschau)
- PDF-Vorschau (signierte URL, 1h TTL)
Screens Affiliate (intern):
- Affiliate-Liste (Karten mit Status-Badge)
- Affiliate-Detail (Stammdaten, Codes, Provisions-Historie)
- Referral-Code-Verwaltung (Anlegen, Aktivieren, Deaktivieren)
- Provisions-Übersicht / Abrechnungs-Vorbereitung
Auto-Posting P2
Bei quote.CONVERTED → Server-Action (transaktional):
orders.INSERT(quote_id,customer_id,referral_code_id,affiliate_id,status='OPEN')invoices.INSERT→ Auto-Posting:movementsUpsert (source_type='invoice_issued',movement_type='INVOICE',total_chf=grand_total)- Bei
affiliate_id IS NOT NULL:commission_entries.INSERT(status='pending', #19 — Entstehung hier bei Konversion). Freigabe (approved) erfolgt später beim Tracking-DELIVEREDder Sendung (P3), nicht beiinvoices → PAID(Modul 7 §4.5).
Definition of Done P2
- Preis-Resolver: deterministisch für alle Produkt-/Zonen-Kombinationen; Test
AMBIGUOUS_PRICEundPRICE_NOT_FOUND - Offerte erstellen → PDF generiert → E-Mail via Resend versendet (Template dreisprachig)
- Offerte akzeptieren →
orders-Eintrag +movements-INVOICE-Eintrag automatisch vorhanden - Affiliate-Code auf Offerte →
commission_entries-Eintrag (pending) bei Offerte-Konversion (#19) - Affiliate-Login: sieht nur eigene Offerten und eigene Provisionen (RLS-Test)
- Preislisten-Item unveränderlich nach Aktivierung (UI + DB-Constraint)
- Cron-Job: Expired-Offerten werden täglich gesetzt
Abhängigkeiten P2
- Voraussetzung P0: Auth, Rollen, RLS-Helfer,
audit_log, Storage-Buckets - Voraussetzung P1:
contacts(Offerte brauchtcontact_id),movements(Auto-Posting) box_productsaus P2 wird von P3 (Boxen-Materialisierung) und P4 (OCR-Empfänger) referenziertordersaus P2 ist Eingangs-Punkt für P3 (Logistik)referral_codesaus P2 werden in P3 (orders.referral_code_id) durchgereicht
Aufwandsklasse
L — Preis-Resolver und PDF-Generierung sind die technischen Knackpunkte; der Rest sind gut definierte CRUD-Flows.
P3 — Logistik: Boxen, Tracking, Container
Ziele
Der operative Kern von Dominicano Express läuft in Caja: aus einem Auftrag (P2) entstehen physische Boxen mit UUID/QR-Code, die entlang der 10 Tracking-Schritte verfolgt werden. Arkys kann am Depot Boxen scannen und Status setzen — auch offline. Container werden geplant und verwaltet. Das Depot-Lifecycle der Fässer ist vollständig abgebildet inkl. Rückzahlung/Verfall als Auto-Posting ins Ledger. Das Tracking-DELIVERED löst die Affiliate-Provisions-Freigabe aus (commission_entries.status pending → approved per DB-Trigger, #19/F-13) — die Provision selbst entsteht bereits bei der Offerte-Konversion (P2).
Enthaltene Tabellen
Logistik (Modul 5):
- Lookups:
tracking_statuses(9 Codes + Phase/Sort),tracking_phases(CH/OCEAN/RD),box_conditions,shipment_statuses,container_statuses,order_statuses,deposit_refund_kinds containers(Planung,next_departure, Kapazität, BL-Nummer)shipments(logistisches Bündel; FK auforders,recipients,containers)boxes(UUID = QR-Inhalt,box_no,current_status,is_returnable)tracking_events(append-only, RLS kein UPDATE/DELETE)fn_record_tracking_event()(SECURITY DEFINER, atomar Event + Statuswechsel + Idempotenz viaclient_event_id)deposit_orders(ALTER:box_id-Spalte hinzufügen)deposit_payments(Zahlungen gegen Depot-Auftrag)deposit_refunds+deposit_refund_kinds(Rückzahlung / Verfall / Teilrückerstattung)- Views:
v_box_tracking,v_shipment_progress,v_container_load,v_open_box_deposits fn_bulk_track()(Bulk-Tracking-Event für Container-Verladung)fn_audit()-Trigger aufboxes,shipments,containers,deposit_orders,deposit_refunds,tracking_events
Screens Logistik:
- Scan-Screen (Camera-First, Vollbild-QR, Bottom-Sheet mit nächstem Schritt, Offline-Queue)
- Boxen-Liste (adaptiv: Karten Handy / TanStack-Table Desktop, Status-Filter-Chips)
- Box-Detail / Tracking-Timeline (vertikale Timeline, QR-Anzeige, Storno-Events)
- Container-Board (Karten je Status, Progress-Bar,
próxima salida) - Container-Detail + Bulk-Aktionen (Multi-Select, Bulk-Status-Wechsel)
- Auftrag-Detail Logistik-Sicht (Boxen anlegen, Sendungen bilden, Empfänger zuordnen)
- Depot-Lifecycle-Screen (
v_open_box_deposits, Swipe „zurück" / „verfallen")
Offline-PWA:
- IndexedDB-Queue für Tracking-Events (jedes Event mit
client_event_id) - Sync-Logik beim Reconnect (Idempotenz via Unique-Index)
- Offline-Status-Badge in der Navigationsleiste
Auto-Posting P3
| Operatives Event | movement_type | source_type |
|---|---|---|
deposit_payment erfasst | DEPOSIT_CASH (Eingang, Kanal nach payment_methods.channel) | deposit_payment |
deposit_refund RETURNED/PARTIAL | EXPENSE (Ausgang, Depot-Rückzahlung) | deposit_refund |
deposit_refund FORFEITED | INCOME (Ertragsbuchung, 🔲) | deposit_refund |
Notification bei box.status → DELIVERED: SHIPMENT_STATUS-Template an Kunde + Empfänger-DR (WhatsApp-Deep-Link / E-Mail).
Affiliate-Provision (#19): Entstehung bereits bei Offerte-Konversion (pending, P2); Freigabe (status → 'approved') automatisch per DB-Trigger beim Tracking-DELIVERED der Sendung (auf tracking_events/Sendungsabschluss) — DELIVERED ist der Auslöser der Fälligkeit, nicht mehr invoices → PAID (F-13).
Definition of Done P3
- Auftrag (aus P2) → Boxen materialisieren → UUID/QR-PDF drucken
- Box-QR scannen am Handy → Status-Wechsel in unter 5 Sekunden
- Offline-Scan: Event in IndexedDB; nach Reconnect synct er idempotent (kein Duplikat)
- Container-Board: Box verladen →
CONTAINER_LOADED-Bulk-Event für alle Boxen -
DELIVERED-Event → Kunden-Notification ausgelöst (Outbox-Eintrag vorhanden) -
DELIVERED-Event →commission_entries.statusder vermittelten Sendung wechseltpending → approved(DB-Trigger, #19) - Depot-Rückzahlung →
movements-EXPENSE-Eintrag automatisch (Test:source_ideindeutig) -
FAHRER-Rolle: kann nurtracking_eventsINSERT + SELECT, kein Finanz-Zugriff (RLS-Test) - Tracking-Events: UPDATE/DELETE schlägt für alle Rollen fehl
Abhängigkeiten P3
- Voraussetzung P0: Auth, RLS, Audit, Storage (Box-Fotos)
- Voraussetzung P1:
contacts(Kunden, Fahrer-Actor),movements/Auto-Posting-Kontrakt - Voraussetzung P2:
orders(Eingangs-Punkt),box_products(Katalog),referral_codes+commission_entries(pending, deren Freigabe derDELIVERED-Trigger in P3 auslöst — #19) recipients(P1-Struktur) wird hier produktiv befüllt
Aufwandsklasse
XL — fn_record_tracking_event() (Atomar-Transaktion + Idempotenz), Offline-IndexedDB-Queue, Container-Bulk-Aktionen und Depot-Auto-Posting sind komplex. Kamera-First auf Mittelklasse-Android muss Performance-Budget einhalten.
P4 — WhatsApp + OCR + Dedup
Ziele
Der Lead-Intake wird vollständig automatisiert: WhatsApp-Medien-Nachrichten landen in einer Eingangs-Queue, ein OCR-/Vision-Dienst extrahiert Ausweis-Daten und befüllt den Kontaktsatz vor. Manuelle Bestätigung bleibt Pflicht (revDSG). Die Duplikat-Bereinigung (Golden-Record-Workflow) wird produktiv — alle bisher angesammelten Duplikate werden aufgeräumt.
Enthaltene Tabellen
WhatsApp + OCR (Modul 2):
whatsapp_inbound(Eingangs-Queue: Sender-Phone, Media-URL/Path, Timestamp, Verarbeitungs-Status)kyc_scans(Scan-Versuch:doc_type,extracted_fieldsJSONB,raw_image_path,ocr_provider,ocr_confidence,review_status)kyc_field_corrections(append-only Audit-Journal manueller Korrekturen)- Enums:
kyc_doc_type,kyc_ocr_provider,kyc_review_status fn_audit()-Trigger aufkyc_scans- Storage-Bucket
kyc: Scan-Upload via signierte URL (Edge-Function), RLS strikt (is_sensitive=true) - Webhook-Receiver (Edge-Function): eingehende WhatsApp-Medien →
whatsapp_inbound-Insert → OCR-Job - OCR-Edge-Function: Vision-API-Call (Anbieter 🔲) →
kyc_scans-Insert mitextracted_fields - Manuelle Review-UI → OCR-Felder bestätigen →
contacts-Update
CRM Dedup (Modul 1 — Ergänzung zu P1):
contact_duplicates(Fuzzy-Score-Paare,resolution-Enum)- Duplikat-Erkennungs-Job (Edge-Function / pg_cron:
pg_trgm-Trigram-Score auf Name/Telefon/E-Mail, 🔲 Algorithmus bestätigen) - Merge-Server-Action (transaktional: Golden Record, FKs umbiegen,
contact_merge_log,contact_duplicates.resolution='merged')
Screens OCR / KYC:
- Ausweis-Scan-Screen (Camera-First: Kamera-Auslöser → Upload → OCR-Status-Spinner → Review-Formular)
- KYC-Review-Queue (Pendig-Scans, je Scan extrahierte Felder vs. Formular-Felder, Bestätigen/Ablehnen)
- Scan-Historie pro Kontakt (Tab „Dokumente" im Kontaktdetail, P1-Grundgerüst wird befüllt)
Screens Dedup:
- Duplikat-Review-Queue (Liste Duplikat-Paare sortiert nach Score)
- Merge-Screen (Spalten A vs. B, Radiogruppe je Feld, Bestätigung → Transaktion)
- Nicht-Duplikat-Markierung (Swipe/Button)
Definition of Done P4
- WhatsApp-Bild-Eingang →
whatsapp_inbound-Queue → OCR →kyc_scans(End-to-End-Test mit echter Cédula) - OCR-Felder im Review-Formular bestätigen →
contacts.id_number,id_typeaktualisiert,audit_log-Eintrag - KYC-Scan-Bild: nur abrufbar mit signierter URL, kein öffentlicher Zugriff (Storage-Test)
- Duplikat-Queue zeigt Paare mit Score ≥ 0.75; Merge läuft transaktional (alle FKs umgebogen)
- Nicht-Duplikat-Markierung: Paar verschwindet aus Queue
- DSG-Löschung: Scan-Datei aus Storage löschbar, Audit-Eintrag bleibt (Lifecycle-Job-Grundgerüst)
Abhängigkeiten P4
- Voraussetzung P0: Auth, Storage-Buckets (
kyc), RLS, Audit - Voraussetzung P1:
contacts(Ziel-Entität),contact_merge_log(Struktur bereits vorhanden) - P4 ist bewusst am Ende: die Fuzzy-Dedup-Qualität steigt, je mehr Kontakte ab P1 akkumuliert sind
- OCR-Anbieter (🔲 Claude Vision / Google Vision / Azure) muss vor Implementierung entschieden werden
Aufwandsklasse
L — OCR-Integration und Webhook-Receiver sind einmalig komplex; die Merge-Transaktion ist heikel (FK-Kaskade über alle Module). Danach ist die Duplikat-UI geradlinig.
Phasen-Abhängigkeitsdiagramm
P0 Fundament
│ Auth, Rollen, RLS-Helfer, i18n, Audit, Storage, movement_types, payment_methods
│
└─► P1 CRM + Finanzen-Kern
│ contacts, movements, v_movements, v_dashboard, Excel-Migration, Lead-Wire-in
│
└─► P2 Produkte + Preise + Offerten + Affiliate-Codes
│ box_products, price_lists, quotes, orders, invoice→movements, referral_codes
│
└─► P3 Logistik: Boxen, Tracking, Container
│ boxes (UUID/QR), tracking_events, containers, deposit_lifecycle→movements
│
└─► P4 WhatsApp + OCR + Dedup
whatsapp_inbound, kyc_scans, contact_duplicates, merge-workflow
Alle Pfeile sind harte Voraussetzungen: keine Phase kann ohne die vollständige Vorgänger-Phase in Produktion gehen.
Modul-zu-Phase-Zuordnung
| Modul-Spec | Enthaltende Phase(n) |
|---|---|
| Modul 8 — Plattform (Auth, Rollen, i18n, Audit, Storage, Notifications) | P0 vollständig |
| Modul 1 — CRM (contacts, contact_roles, recipients-Struktur) | P1 (Grundstruktur + Lead-Wire-in) + P4 (Dedup, Merge) |
| Modul 6 — Finanzen (movements, Views, Abschluss, Migration) | P1 vollständig + P2/P3 Auto-Posting |
| Modul 3 — Produkte & Preise (box_products, price_lists, Resolver) | P2 vollständig |
| Modul 4 — Offerten (quotes, PDF, Status-Flows, Konversion) | P2 vollständig |
| Modul 7 — Affiliate (affiliates, referral_codes, commissions, payouts) | P2 (Kern) |
| Modul 5 — Logistik (boxes, tracking_events, containers, deposit_lifecycle) | P3 vollständig |
| Modul 2 — WhatsApp + OCR (kyc_scans, whatsapp_inbound, Webhook) | P4 vollständig |
| Modul 18 — Design/Mobile (Design-Tokens, PWA, Offline, Kamera) | P0 (Tokens/Shell) + Inkrementell P1–P4 |
Geteilte Kern-Entitäten — wann sie entstehen
| Entität | Entsteht in | Erweitert in |
|---|---|---|
contacts | P1 | P4 (KYC-Felder befüllt via OCR) |
movements | P1 | P2 (invoice-Auto-Posting), P3 (deposit-Auto-Posting), P2 (affiliate-payout) |
movement_types / payment_methods | P0 (Seed) | — |
box_products | P2 (Seed) | — |
boxes (UUID) | P3 | — |
shipments | P3 | — |
containers | P3 | — |
quotes | P2 | — |
orders | P2 (aus Quote-Konversion) | P3 (Boxen-Materialisierung) |
invoices | P2 (Auto-Insert) | — |
deposits (deposit_orders/payments/refunds) | P3 | — |
affiliates / referral_codes | P2 | — |
audit_log | P0 | Trigger auf je neue Tabelle in P1–P4 |
storage_objects | P0 (Buckets) | P2 (Offerte-PDF), P3 (Box-Fotos), P4 (KYC-Scans) |
notifications | P0 (Outbox-Struktur) | P1 (Lead-Intake), P2 (QUOTE_SENT), P3 (SHIPMENT_STATUS), P2 (AFFILIATE_STATEMENT) |
Offene Punkte mit Phasen-Relevanz
Die folgenden Entscheide müssen vor der jeweiligen Phase geklärt sein:
| Entscheid | Erforderlich vor | Referenz-Spec |
|---|---|---|
Anfangssalden-Strategie (Excel Saldo inicial) | P1-Start | Modul 6 §9 |
| Anfangs-Zonen-Taxonomie DR (SDQ/STI/PROV/…) | P2-Start | Modul 3 §9 |
PDF-Bibliothek (@react-pdf/renderer vs. Puppeteer) | P2-Start | Modul 4 §9 O-02 |
grand_total_chf, auf PDF) | — | Modul 4 §9 O-08 |
| Returnable-Definition pro Produkt (welche Boxen haben Depot?) | P2-Start | Modul 5 §9 |
| OCR-Anbieter (Claude Vision / Google Vision / Azure) | P4-Start | Modul 2 §3 |
Duplikat-Score-Algorithmus (pg_trgm vs. Edge-Function) | P4-Start | Modul 1 §9 |
| KYC-Aufbewahrungsfrist (DSG-Minimierung vs. Zollanforderung) | P4-Start | Modul 8 §9 |