Odoo Online (SaaS) als Multi-Vertical-Backend

Overview

Replacement fuer Plan 003 Phase A (Self-Host Odoo auf Fargate verworfen wegen Aufwand + Kosten — siehe Plan 003 Document-Review-Findings S1/S2/S3). Stattdessen: Odoo Online (Odoo’s eigener Cloud-SaaS), 0–24 EUR/Mo statt 70–85 EUR/Mo, kein Hosting-Setup, in 1 Tag live.

Zwei Ziele in einem Plan:

  1. Aylem-Reference-Case-UI — Inhaber-Backend fuer Reservierungen + Bestellungen (Tabelle, Tischplan, Filter, Mobile-App).
  2. Multi-Vertical-Bundle-Asset — gleiche _odoo.py-Helper-Schicht funktioniert fuer Restaurant + Friseur + Werkstatt + Praxis + Hotel. Pro Vertical eigener Odoo-Workspace, gleicher MCP-Tool-Code.

Das ersetzt die Frage „wo speichern wir Reservierungen/Bestellungen” mit einer Antwort die nicht nur fuer Aylem skaliert, sondern fuer jeden zukuenftigen KMU-Service-Kunden.

Problem Frame

Plan 002 hat den Voice-Bot mit SQLite + Telegram-Push fuer Aylem-Demo lauffaehig gemacht. Plan 003 versuchte Self-Host-Odoo zu addieren — Reviewer-Konsens: ueberdimensioniert (50 EUR/Mo Infra + 2–3 Wochen Setup + Cloudflare-Tunnel + RDS + Fargate-Memory-Tuning) fuer ein „einfaches” Reference-Case-Asset.

Marvin’s Klarstellung: Vereinfachen + guenstig. Plus: das Backend soll multi-vertical sein — Friseur, Werkstatt, Praxis, Hotel sollen mit derselben Plattform bedient werden koennen, sonst ist es kein Bundle-Asset.

Loesung: Odoo Online (gehosteter Odoo-SaaS direkt von Odoo S.A.) mit pro-Tenant-Workspace. Marvin hostet nichts selbst, hat aber denselben JSON-RPC-API-Layer wie Self-Host — der _odoo.py-Helper im tools-mcp funktioniert identisch. Migration auf Self-Host bleibt offen als Option fuer DSGVO-strenge Industriekunden in der Zukunft.

Requirements Trace

  • R1. Aylem-Workspace in Odoo Online angelegt: Tisch-Layout (~20 Tische), Speisekarte (mind. die Items aus knowledge/aylem/menu.md), Oeffnungszeiten, Reservierungs-Form aktiv. Erreichbar unter <workspace>.odoo.com.
  • R2. tools-mcp/_odoo.py Helper-Modul mit JSON-RPC-Wrapper, schreibt Reservierungen + Bestellungen ueber ODOO_URL + ODOO_DB + ODOO_API_KEY (alles per ENV).
  • R3. Feature-Flag STORAGE_BACKEND=sqlite|odoo|both in tools-mcp/server.py — beide Pfade lauffaehig, Default both auf Marvin’s Mac (SQLite als lokaler Audit + Odoo als Source-of-Truth).
  • R4. Multi-Vertical-Pattern dokumentiert: pro Vertical (Restaurant, Friseur, Werkstatt, Praxis, Hotel) ein kurzes Onboarding-File mit Odoo-Modulen + Workspace-Setup + Beispiel-Stammdaten. Marvin kann Vertical Nr 2 in ~1 h aufsetzen.
  • R5. Telegram-Push bleibt parallel zum Odoo-Insert aktiv — Push ist instant-Mobile-Channel, Odoo ist Read-on-demand-Inspector.
  • R6. End-to-End-Smoke: Voice-Call macht book_reservation → Eintrag in Odoo + SQLite + Telegram-Push.

Scope Boundaries

In Scope:

  • Odoo Online Workspace fuer Aylem (Restaurant)
  • tools-mcp/_odoo.py als stdlib-JSON-RPC-Helper
  • STORAGE_BACKEND-Feature-Flag in tools-mcp/server.py
  • 5 Multi-Vertical-Pattern-Docs (Restaurant, Friseur, Werkstatt, Praxis, Hotel) als Skelett
  • ENV-Setup-Anleitung
  • Smoke-Test Voice-Bot → Odoo-Insert
  • Tests fuer _odoo.py

Out of Scope (Folge-Plaene):

  • WhatsApp-Kanal — eigener Plan (Plan 003, depends on dieser Plan)
  • Self-Host Odoo auf Fargate (kommt zurueck wenn DSGVO-strenger Industriekunde SaaS-Veto einlegt)
  • Multi-Tenant innerhalb eines Workspaces (Odoo Online macht das ueber separate Workspaces pro Kunde — sauberer)
  • Echte Tenant-Onboarding fuer Friseur/Werkstatt/Praxis/Hotel (heute nur Pattern-Doku, kein lebender zweiter Tenant)
  • Bondrucker-Hardware (Odoo unterstuetzt ESC/POS via App, aber Hardware-Setup ist eigener Plan)
  • Stripe-Payment / Online-Shop
  • LMIV-Allergen-Audit fuer echten Live-Pilot
  • Aylem-Inhaber-Live-Pilot mit echter AVV

Context & Research

Relevant Code and Patterns

  • tools-mcp/_telegram.py — stdlib-only-Wrapper-Pattern, kein urllib3, kein Cross-Repo. _odoo.py folgt demselben Stil (urllib.request + json).
  • tools-mcp/_knowledge.py — Helper-Modul-Pattern mit Validation, fail-open, Logging, Test-Coverage.
  • tools-mcp/server.py — bestehender MCP-Server mit 9 Tools. Wird um Feature-Flag erweitert, nicht refactored. book_reservation / take_order / escalate_to_human kriegen einen zweiten Persistence-Pfad.
  • tools-mcp/tests/ — pytest-Setup ist da, 33 Tests gruen. Neue test_odoo.py integriert sich nahtlos.

Institutional Learnings

  • Brain+MCPs-Modell (ADR keine-eigene-plattform): Backend-Schreibrecht lebt im MCP, Channels (Voice + WhatsApp) sind Adapter. _odoo.py ist konsequent — kein eigenes Odoo-MCP als separates Repo (Overhead fuer 1 Tenant).
  • Stdlib-only-Helper-Pattern (_telegram.py Lesson, Plan 002): Cross-Repo-Bindungen sind Fallen. _odoo.py braucht keine externe Library — urllib.request reicht fuer JSON-RPC.
  • Voice-Bot-DE-Pattern (voice-bot-de-pattern.md): Persona + Tool-Discipline + Anti-IVR-Liste — unveraendert. Odoo ist Persistence-Schicht, beruehrt das nicht.
  • MCP-as-a-Service-Vision (Memory): hosted MCPs in av-production mit OAuth + Multi-Tenant. Odoo Online ersetzt den Self-Host-Teil dieser Vision fuers Restaurant-Vertical — wir nutzen Odoo’s Hosting-Layer, sparen eigenen Aufwand.

External References

Key Technical Decisions

  • Odoo Online (SaaS) statt Self-Host. Begruendung: Reviewer-Konsens aus Plan 003: 50 EUR/Mo Self-Host-Aufwand + 2–3 Wochen Setup waren overkill fuer Reference-Case. Odoo Online: 0 EUR Free-Tier oder 24 EUR/Mo Standard, kein Setup, kein Wartungsaufwand, automatische Updates. Self-Host bleibt offene Option fuer DSGVO-strenge Folge-Kunden (Folge-Plan).
  • Workspace-pro-Tenant statt Multi-Tenant in einer DB. Begruendung: Odoo Online macht das nativ — aylem-demo.odoo.com ist eine eigene DB, friseur-mueller.odoo.com ist eine andere. Saubere Trennung, keine Cross-Tenant-Leak-Risiken, kein Custom-Permission-Layer noetig.
  • _odoo.py als lokales Helper-Modul. Begruendung: gleiche Logik wie _telegram.py und _knowledge.py. Stdlib-only (urllib.request fuer JSON-RPC), kein Cross-Repo, kein externes pip-Paket. Tenant-Switch ueber ENV-Variablen (ODOO_URL, ODOO_DB, ODOO_API_KEY).
  • Feature-Flag STORAGE_BACKEND=sqlite|odoo|both, Default both auf Marvin’s Mac. Begruendung: SQLite bleibt lokaler Audit-Trail (gleich-Filesystem wie der Voice-Worker), Odoo wird Source-of-Truth fuer Inhaber-UI. Bei Odoo-Down geht’s lokal weiter (Mac hat SQLite). Migration spaeter auf odoo-only nach 4 Wochen produktivem Lauf.
  • Multi-Vertical-Doku in intern/wissen/prozesse/odoo-pattern-<vertical>.md statt im Plan-File. Begruendung: Plan ist Aufgaben-Doku, Pattern-Docs sind Bundle-Assets fuer kuenftige Onboardings. Pro Vertical: Odoo-Module-Liste, Workspace-Setup-Schritte, Stammdaten-Beispiele, MCP-Tool-Mapping.
  • Telegram-Push parallel zu Odoo-Insert. Begruendung: Push ist Mobile-instant, Odoo ist Read-on-demand. Restaurant-Inhaber checkt nicht permanent Odoo, Telegram bleibt primary-Notification. Odoo-eigene-Notifications werden im Workspace deaktiviert (Doppel-Push vermeiden).
  • API-Key statt Username-Password fuer JSON-RPC. Begruendung: Standard seit Odoo 14, scoped, rotierbar, in AWS Secrets Manager hinterlegbar bei spaeterem Fargate-Worker. Heute: .env.local reicht.
  • Free-Tier-Pruefung vor Standard-Plan-Commit. Odoo Online hat 1-App-Free-Tier. Restaurant-POS + Appointment passen in eine „App”-Kategorie. Bei knapp: 24 EUR/Mo Standard. Marvin checkt das beim Setup live.

Open Questions

Resolved During Planning

  • SaaS vs Self-Host → SaaS (Odoo Online), Self-Host als Folge-Option offen.
  • Multi-Vertical-Eignung → Odoo deckt Restaurant + Friseur + Werkstatt + Praxis + Hotel via separate Module ab. Bundle-Asset-Wert ist real.
  • WhatsApp-Channel in diesem Plan? → Nein, eigener Plan 003 depends on diesen.
  • Tisch-Slot-Race-Verifikation → nachrangig (Marvin’s Klarstellung), Restaurant-Inhaber regelt manuell bei Konflikt.

Deferred to Implementation

  • Odoo Online Free-Tier-Grenzen 2026: Werden 2 Module (POS-Restaurant + Appointment) als „1 App” gezaehlt oder als 2? Marvin pruefts beim Setup; bei 2 Apps: 24 EUR/Mo Standard-Plan.
  • Konkrete Odoo-Modell-Namen fuer Reservierung vs POS-Order: je nach Odoo-Version unterschiedlich (calendar.event vs pos.order vs restaurant.table.booking). Beim Schreiben von _odoo.py via Odoo-Developer-Mode-Inspector verifizieren.
  • Aylem-Workspace-Name: aylem-demo.odoo.com oder generisch demo-restaurant.odoo.com (siehe Aylem-Name-Risiko in Risks). Entscheidet Marvin beim Setup.
  • API-Rate-Limits bei Odoo Online Free-Tier: Plan rechnet mit MVP-Volumen (Marvin’s Test-Calls), Production-Limits werden beim Live-Pilot relevant.

High-Level Technical Design

Directional guidance, kein Implementation-Spec.

flowchart TB
    A[Voice-Anrufer]
    L[LiveKit Worker - Mac]
    M[tools-mcp Subprocess]
    O[(Odoo Online SaaS - Belgien)]
    S[(SQLite - lokal Mac)]
    T[Telegram Bot API]

    A --> L
    L <-->|stdio| M
    M -->|JSON-RPC| O
    M -->|write| S
    M --> T

    style O fill:#fff4e1
    style M fill:#e1f5e1

_odoo.py Skelett (Pseudo-Code, directional)

# tools-mcp/_odoo.py — stdlib + json + urllib.request (kein urllib3, kein odoorpc)
 
class OdooClient:
    def __init__(self, url, db, api_key, uid_cache=None):
        self.url = url; self.db = db; self.api_key = api_key
        self.uid = uid_cache or self._authenticate()
 
    def call(self, model, method, args=None, kwargs=None) -> Any:
        # JSON-RPC POST zu /jsonrpc, model + method + auth
        ...
 
    def create_reservation(self, date, time, guests, name, phone, notes=None) -> Optional[int]:
        # Returnt Odoo-Reservation-ID oder None bei Fehler
        ...
 
    def create_pos_order(self, items: list, customer_name, phone, address=None, pickup_time=None) -> Optional[int]:
        ...
 
    def list_today_reservations() -> list[dict]:  # fuer Inhaber-Sicht oder Status-Tool
        ...
 
# Fail-Behavior (analog _telegram.py): exceptions swallowed + log.warning, returnt None
# Caller (server.py) checkt: if odoo_id is not None: success else: SQLite-only-Pfad

Multi-Vertical-Pattern-Skizze

VerticalOdoo-ModuleWorkspace-BeispielKey MCP-Tool-Mapping
Restaurantpoint_of_sale_restaurant + pos_restaurant_appointmentaylem-demo.odoo.combook_reservationcalendar.event; take_orderpos.order
Friseurappointment + hr_attendance (Mitarbeiter-Planung)friseur-mueller.odoo.combook_reservationcalendar.event (Stylist-Slot)
Werkstattrepair + appointmentwerkstatt-schmidt.odoo.combook_reservationrepair.order (Annahme-Termin)
Arztpraxisappointment + hr_attendancepraxis-meier.odoo.combook_reservationcalendar.event (Sprechstunde)
Hotelhotel_management (Community-Modul)hotel-stadt.odoo.combook_reservationhotel.reservation

Gemeinsamer _odoo.py-Code mit Vertical-spezifischer Mapping-Schicht. Pro neuem Tenant: ENV-Variablen umstellen, ggf. ein Mapping-Dict erweitern. Kein Code-Patch.

Implementation Units

flowchart TB
    U1[Unit 1: Odoo Online Aylem-Workspace anlegen + befuellen]
    U2[Unit 2: _odoo.py + STORAGE_BACKEND-Flag + Tests]
    U3[Unit 3: Multi-Vertical-Pattern-Docs - 5 Markdown-Files]

    U1 --> U2
    U1 -.->|parallel| U3
  • Unit 1: Odoo Online — Aylem-Workspace anlegen + befuellen

    Goal: <workspace>.odoo.com erreichbar mit Admin-Login. POS-Restaurant + Appointment-Module installiert. 20 Tische als Layout. 20 Menu-Items aus knowledge/aylem/menu.md importiert. Oeffnungszeiten in Resource-Calendar. Bot-User mit API-Key.

    Requirements: R1

    Dependencies: keine

    Files:

    • Modify: Odoo-Online-Web-UI (Daten-Setup, kein Code) — Schritte in docs/aylem-odoo-setup.md als Runbook
    • Create: intern/projekte/telefon-assistent-aws/docs/aylem-odoo-setup.md (Onboarding-Runbook fuer kuenftige Tenants)
    • Modify: AWS Secrets Manager (falls Marvin spaeter Fargate dazu nimmt) — heute reicht .env.local

    Approach:

    • Account-Setup: https://www.odoo.com/trial → Datenbank anlegen, Admin-Email = Marvin’s, Workspace-Name aylem-demo (siehe Aylem-Name-Risiko, ggf. demo-restaurant). Belgien-Region default = EU-DSGVO-tauglich.
    • Module-Aktivierung: Apps → „Point of Sale - Restaurants” + „Appointment”. Free-Tier check: wenn das als 1 App zaehlt, gratis. Sonst Standard-Plan 24 EUR/Mo akzeptieren oder einen Modul-Tradeoff machen.
    • Tisch-Layout: Settings → POS → Restaurant → Floor Plan. Drag-Drop 20 Tische (3× 8er, 8× 4er, 9× 2er, siehe Plan 002 Aylem-Spezifikation).
    • Speisekarte: Products → bulk import von knowledge/aylem/menu.md. 20 Items mit Preisen + Allergen-Tags (Allergene als Odoo-Tags).
    • Oeffnungszeiten: Resource → Working Hours → identisch zu knowledge/aylem/hours.md.
    • Bot-User: Users → neuer User bot@aylem.demo, Berechtigung „User: All Documents” auf POS + Appointment, KEIN Admin. API-Key generieren in dessen User-Settings.
    • Stammdaten in .env.local: ODOO_URL=https://aylem-demo.odoo.com, ODOO_DB=aylem-demo, ODOO_API_USER=bot@aylem.demo, ODOO_API_KEY=<aus-Odoo>.
    • Runbook schreiben parallel zum Setup — jeder Klick dokumentiert. Naechster Tenant kann in 1 h analog gesetzt werden.

    Patterns to follow:

    • Plan 002 Aylem-Daten-Spezifikation (Tisch-Anzahl, Karte, Hours) als Inhalts-Quelle
    • Odoo Online Onboarding
    • Vault-Convention fuer Setup-Runbooks: numerierte Schritte, jeder Klick + Screenshot-Hint

    Test scenarios:

    • Happy path: Login klappt im Browser, POS-Restaurant + Appointment-Module sichtbar in App-Menue
    • Happy path: Floor Plan zeigt 20 Tische in grafischer Anordnung
    • Happy path: Products-Liste hat 20 Items mit Preisen + Allergen-Tags
    • Happy path: API-Key funktioniert — Smoke-curl curl -X POST https://...odoo.com/jsonrpc -d '{"jsonrpc":"2.0","method":"call","params":{"service":"common","method":"authenticate",...}}' returnt UID
    • Edge case: Free-Tier-Limit-Check — wenn 2 Module = 2 Apps, Plan-Wahl entscheiden
    • Edge case: 25. Tisch versucht hinzuzufuegen — Tisch-Layout-UI verhaelt sich vernuenftig (Default-Limit?)
    • Integration: aylem-odoo-setup.md ist vollstaendig + reproduzierbar (Marvin koennte einen demo-friseur-Tenant in 1 h analog anlegen)

    Verification: Browser-Test komplett. API-Key liefert via JSON-RPC die Liste der Produkte zurueck. Runbook in docs/aylem-odoo-setup.md ist fertig.

  • Unit 2: _odoo.py Helper + STORAGE_BACKEND-Feature-Flag im tools-mcp

    Goal: tools-mcp/server.py kann Reservierungen + Bestellungen ueber JSON-RPC in Odoo Online schreiben. Feature-Flag erlaubt SQLite-only-, Odoo-only- oder Both-Mode.

    Requirements: R2, R3, R5

    Dependencies: Unit 1 (Odoo erreichbar + API-Key vorhanden)

    Files:

    • Create: intern/projekte/telefon-assistent-aws/tools-mcp/_odoo.py (stdlib + urllib.request, kein urllib3)
    • Modify: intern/projekte/telefon-assistent-aws/tools-mcp/server.pySTORAGE_BACKEND ENV, book_reservation + take_order + escalate_to_human parallel-write je nach Flag
    • Modify: intern/projekte/telefon-assistent-aws/tools-mcp/.env.exampleSTORAGE_BACKEND, ODOO_URL, ODOO_DB, ODOO_API_USER, ODOO_API_KEY
    • Modify: intern/projekte/telefon-assistent-aws/tools-mcp/README.md — Setup-Block fuer Odoo
    • Create: intern/projekte/telefon-assistent-aws/tools-mcp/tests/test_odoo.py (mit mocked JSON-RPC, kein Live-Call in CI)

    Approach:

    • OdooClient-Class mit Methoden: authenticate() (einmalig, uid wird gecached), call(model, method, args, kwargs) (JSON-RPC-Wrapper via urllib.request), create_reservation(), create_pos_order(), optional list_today_reservations() als reuse.
    • Auth: API-Key-basiert ueber common.authenticate(db, user, api_key) → liefert UID. UID + API-Key fuer alle weiteren object.execute_kw-Calls.
    • Datentypen-Coercion: Many2One-Felder kommen als [id, name]-Tupel, Boolean-empty-Listen als false. Helper-Funktionen in _odoo.py normalisieren das.
    • Fail-Behavior (differenziert):
      • Odoo unreachable (Network/5xx nach 3 Retries) → log.warning, returnt None.
      • Bei STORAGE_BACKEND=both: SQLite-Pfad uebernimmt, Bot antwortet normal, Telegram-Push enthaelt Warning „⚠ Odoo nicht erreicht, Eintrag nur in SQLite”.
      • Bei STORAGE_BACKEND=odoo: Bot antwortet User degraded („Es gibt gerade ein technisches Problem, bitte rufen Sie das Restaurant direkt unter 02381 9153000 an”). KEINE falsche „gebucht”-Bestaetigung.
      • Auth-Fehler → log.exception, urgent-Telegram-Alert.
      • 5xx → Retry 3× mit Exponential-Backoff max 3 s gesamt.
    • Feature-Flag in server.py (Default both):
      if STORAGE_BACKEND in ("sqlite", "both"): _sqlite_insert(...)
      if STORAGE_BACKEND in ("odoo", "both"):   odoo_id = _odoo.create_reservation(...)
      _telegram.send(..., text=f"Reservierung {res_id}{' (Odoo #'+str(odoo_id)+')' if odoo_id else ''} ...")
      
    • ID-Korrelation in Telegram: SQLite-ID RES-XXXXXX + Odoo numerische ID, beide im Push falls vorhanden.

    Patterns to follow:

    Test scenarios:

    • Happy path: STORAGE_BACKEND=both, book_reservation(...) legt Reservation in Odoo + SQLite an, ID-Korrelation in Telegram-Push
    • Happy path: STORAGE_BACKEND=odoo, book_reservation(...) legt nur in Odoo an, kein SQLite-Eintrag
    • Happy path: STORAGE_BACKEND=sqlite, book_reservation(...) legt nur in SQLite an, Odoo nicht aufgerufen
    • Edge case: ODOO_API_KEY fehlt → log.warning beim Modul-Import, alle Odoo-Methoden returnen None, Bot faellt auf SQLite zurueck (im both-Mode)
    • Edge case: leeres STORAGE_BACKEND → default both
    • Edge case: Odoo-Many2One-Return [42, 'Tisch 7'] wird zu Integer 42 normalisiert
    • Error path: Odoo gibt 500 zurueck → 3× Retry mit Backoff, dann log.exception + None
    • Error path: Auth fehlgeschlagen → urgent-Telegram-Alert an Marvin
    • Error path: STORAGE_BACKEND=odoo aber Odoo unreachable → Bot-Antwort degraded mit Restaurant-Phone, KEIN positive „gebucht”
    • Integration: voller book_reservation-Flow ueber server.py, Odoo-UI zeigt die Reservation, SQLite enthaelt sie, Telegram-Push hat beide IDs

    Verification: Live-Tool-Call gegen Odoo Online: python -c "from server import book_reservation; ..." legt Test-Reservierung an, Odoo-UI im Browser zeigt sie binnen 2 s, SQLite-DB enthaelt sie, Telegram-Push hat beide IDs. 6+ pytest-Tests gruen.

  • Unit 3: Multi-Vertical-Pattern-Docs (5 Markdown-Files)

    Goal: Onboarding-Pattern dokumentiert, dass Marvin in ~1 h einen neuen Tenant (Friseur, Werkstatt, Praxis, Hotel) aufsetzen kann — kein Code-Patch noetig.

    Requirements: R4

    Dependencies: Unit 2 ist optional (Doku kann parallel laufen, aber profitiert vom Erfahrungs-Lernen aus Unit 2)

    Files:

    • Create: intern/wissen/prozesse/odoo-pattern-restaurant.md (basiert auf Aylem-Erfahrung aus Unit 1+2)
    • Create: intern/wissen/prozesse/odoo-pattern-friseur.md
    • Create: intern/wissen/prozesse/odoo-pattern-werkstatt.md
    • Create: intern/wissen/prozesse/odoo-pattern-praxis.md
    • Create: intern/wissen/prozesse/odoo-pattern-hotel.md
    • Modify: intern/wissen/prozesse/_index.md — Verlinkung
    • Modify: intern/firma/produkt-bundle.md — KMU-Service-Vertical-Bundle als Capability ergaenzen

    Approach:

    • Pro Vertical-Datei mit Frontmatter (type: process, status: draft|stable) und Sections:
      • Welche Odoo-Module noetig (Restaurant: point_of_sale_restaurant+pos_restaurant_appointment; Friseur: appointment+hr_attendance; etc.)
      • Workspace-Setup-Schritte (Klick-Anleitung — copy von aylem-odoo-setup.md, vertical-spezifisch adaptiert)
      • Stammdaten-Beispiele (Friseur: Stylist-Liste, Service-Katalog mit Dauer; Werkstatt: Auftragstypen, Annahme-Slots; Praxis: Arzt-Termine; Hotel: Zimmer-Typen)
      • MCP-Tool-Mapping: welches book_reservation/take_order auf welches Odoo-Modell (calendar.event vs pos.order vs repair.order vs hotel.reservation)
      • Tonalitaet-Adjustments im System-Prompt (Friseur: lockerer, Praxis: foermlicher) — Verweis auf voice-bot-de-pattern.md.
      • Pricing-Hint: Odoo Online Free-Tier vs Standard-Tier je nach Module-Anzahl.
    • Restaurant-Pattern ist am detailliertesten (basiert auf Aylem-Realwelt-Erfahrung). Andere 4 sind Skelette mit Standard-Modul-Liste — werden ergaenzt wenn ein echter Kunde dazu kommt.
    • Cross-Link zum Master-Plan: produkt-bundle.md bekommt einen neuen Eintrag „KMU-Service-Vertical-Bundle: Voice-Bot + Odoo-Online-Backend, pro Vertical Pattern unter intern/wissen/prozesse/odoo-pattern-<x>. Setup-Zeit pro neuem Tenant: ~1 h”.

    Patterns to follow:

    Test expectation: none — Dokumentations-Artefakte. Verifikation ueber zwei Wege:

    • Pruefen: ist jede Datei lesbar + vollstaendig fuer einen Implementer der den Vertical NICHT kennt?
    • Hypothetisch: koennte Marvin in 1 h einen demo-friseur.odoo.com analog zu Aylem aufsetzen, nur mit der Doku in der Hand?

    Verification: Marvin macht 30-min-Selftest: liest odoo-pattern-friseur.md, checked ob alle Setup-Schritte + Module + Tool-Mapping klar sind. Wenn ja → fertig. Wenn Lueck: ergaenzen.

System-Wide Impact

  • Interaction graph: Bestehender LiveKit-Worker auf Mac bleibt unveraendert. tools-mcp wird erweitert um _odoo.py + Feature-Flag — bestehende Tools (book_reservation, take_order, escalate_to_human) bekommen einen zweiten Persistence-Pfad. WhatsApp-Worker (Plan 003, separat) wird denselben _odoo.py-Pfad nutzen — kein Duplikation-Risiko.
  • Error propagation: Odoo unreachable → fail-open auf SQLite (im both-Mode) oder degraded-Reply (im odoo-Mode). Auth-Fehler → urgent-Telegram-Alert. Telegram-Push-Fehler → silent log, beeinflusst Tool-Call nicht.
  • State lifecycle risks: Race-Condition Voice + WhatsApp auf denselben Tisch-Slot — laut Marvin nachrangig, manuelle Konfliktloesung durch Inhaber. Bei spaeterem Skalierungs-Bedarf: application-level Locking in _odoo.py.
  • API surface parity: tools-mcp Tool-Liste unveraendert (immer noch 9 Tools), nur Persistence-Pfad zusaetzlich. Externe Claude-Code/Desktop-Konsumenten brauchen kein Update.
  • Integration coverage: End-to-End nur durch manuellen Voice-Call + Odoo-UI-Inspection pruefbar. Mocking nur fuer pytest-Unit-Tests sinnvoll.
  • Unchanged invariants: LiveKit-Worker auf Mac bleibt. SQLite-Schema bleibt rueckwaertskompatibel. ID-Format RES-XXXXXX / ORD-XXXXXX bleibt. Telegram-Push-Format bleibt. Voice-Bot funktioniert auch ohne Odoo (SQLite-Pfad bleibt als Fallback).

Risks & Dependencies

RiskLikelihoodImpactMitigation
Odoo Online Free-Tier zu klein (2 Apps statt 1) → 24 EUR/Mo Standard noetigMittelNiedrigBeim Setup pruefen. Bei 24 EUR/Mo: akzeptabel, im Bundle-Pricing einkalkulieren. Self-Host bleibt offen als Option.
Odoo Online Hosting in Belgien — DSGVO-AVV mit Odoo S.A. pruefenNiedrigMittelOdoo Online ist DSGVO-konform (offiziell). AVV-Standardvertrag verfuegbar. Fuer Reference-Case-Demo ohne Live-Anrufer-Daten unkritisch. Bei Live-Pilot: AVV-Unterschrift Pflicht.
Aylem-Name-Nutzung ohne Inhaber-KonsensMittelMittelWorkspace-Name generisch waehlen (demo-restaurant-001 statt aylem-demo), interne Daten von Aylem-Realitaet inspiriert aber nicht 1:1. Bei Demo gegenueber Lead: „anonymisiertes Beispiel-Restaurant” framing.
Odoo Online API-Rate-Limits bei MVP-VolumenNiedrigNiedrigMarvin’s Test-Calls sind <10/Tag. Live-Pilot mit 100+ Reservierungen/Tag braucht Limit-Check, Folge-Plan.
Multi-Vertical-Docs sind Skelett, bei realem Friseur-Kunde nicht ausreichendMittelNiedrigBewusst nur Pattern-Skelett im Plan. Pro echtem Kunde wird das File ausgefuellt mit dessen Realwelt-Daten. Restaurant-Pattern (Aylem) ist Vorlage.
_odoo.py Datentypen-Quirks (Many2One als Tupel etc.)MittelNiedrigPre-Coding: 30-min-Inspector in Odoo-Web-UI (Developer-Mode) fuer alle benoetigten Modelle. Coercion-Helper in _odoo.py.
Workspace-Lock-in bei Odoo S.A.NiedrigMittelOdoo-Daten sind exportierbar (XML/CSV). Migration auf Self-Host-Odoo bleibt offene Option. _odoo.py-Code ist gleichgueltig ob SaaS oder Self-Host.
Setup-Aufwand fuer Plan 004 unterschaetztNiedrigNiedrigRealistic: 1 Tag Unit 1 + 1 Tag Unit 2 + 0.5 Tag Unit 3 = 2.5 Werktage. Im Gegensatz zu Plan 003 (2-3 Wochen Self-Host) sehr ueberschaubar.

Documentation Plan

  • Update _index.md Open-Loops — Plan 004 als „Odoo SaaS-Backend, ersetzt Plan 003 Phase A”
  • Update Master-Plan 2026-05-13-001 Open-Loops „echte Bestell-Anbindung” — entschieden via Plan 004
  • Update Plan 003 — Frontmatter + Overview: depends on Plan 004, fokussiert jetzt nur noch auf WhatsApp-Channel
  • Create 5 odoo-pattern-Files unter intern/wissen/prozesse/ (Unit 3 Deliverable)
  • Update intern/wissen/prozesse/_index.md — Verlinkung der 5 neuen Files
  • Update intern/firma/produkt-bundle.md — „KMU-Service-Vertical-Bundle” als neue Capability mit Verweis auf Plan 004

Operational / Rollout Notes

  • Rollout-Reihenfolge: Unit 1 zuerst (Odoo-Workspace anlegen). Unit 2 (_odoo.py) kann erst loslegen wenn Workspace + API-Key da sind. Unit 3 (Docs) kann parallel zu Unit 2 laufen.
  • Aufwand: realistisch 2.5 Werktage. Unit 1 ~1 Tag (Account, Module, Tisch-Layout, Karte importieren, Hours, API-Key, Runbook). Unit 2 ~1 Tag (_odoo.py + Feature-Flag + Tests). Unit 3 ~0.5 Tag (5 Doku-Files, Restaurant-Pattern ausfuehrlich, andere 4 als Skelett).
  • Kosten: 0–24 EUR/Mo (Odoo Online Free-Tier oder Standard). Im Vergleich zu Plan-003-Self-Host: ~50 EUR/Mo Ersparnis, ~1 Woche Setup-Ersparnis.
  • STORAGE_BACKEND-Migration: Start mit both (Default), nach 4 Wochen produktivem Lauf Switch auf odoo. SQLite bleibt als Read-Only-Historical-Archive auf Mac.
  • Reload-Strategie: _odoo.py und server.py aendern → MCP-Subprocess wird beim naechsten Job neu gespawned. Bei laufendem Worker: Ctrl-C + python agent.py dev neu (~5 s). Knowledge-Markdown-Edits bleiben ohne Restart wirksam.
  • Plan 003 Status: wird zu „depends on Plan 004”, fokussiert nur noch WhatsApp-Channel. Phase A aus Plan 003 ist via Plan 004 ersetzt.

Sources & References