feat: initial architecture - requirements, architecture.md, task_graph, AGENTS.md, UI prototype v8d

This commit is contained in:
2026-07-13 03:34:35 +02:00
commit afe6d0a168
10 changed files with 4991 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
# UI-Prototyp State - ERP Nutzfahrzeuge
## Version: 4 (v4)
## Datum: 2026-07-11
## Status: Vierte Iteration - wartet auf User-Feedback
## Prototyp-URL
https://webspace.media-on.de/erp-ui-v1/index.html?v=4
## v4 Aenderungen (User-Feedback)
1. **Fahrzeug-Formular in Tabs/Reiter** - 11 Tabs: Allgemein, Technische Daten, Abmessungen, PKW/LKW/Baumaschine/Stapler (conditional), Preise & Kosten, Bilder, Dokumente, mobile.de
2. **Felder erweitert** - HSN/TSN, EG-Typgenehmigung, Vorbesitzer, Unfallschaeden, TUEV/AU, Service-Intervall, Abmessungen (Laenge/Breite/Hoehe), Gewichte (Leergewicht/Gesamtgewicht), Anhaengelast, Kuppelast, Bordgeraet, Hubwerk, Ladebordwand, Klimaanlage, Schlafplatz, Hydraulik, ZB I/ZB II Feld-Referenzen
3. **Bild-Upload per Datei-Auswahl** - File input button neben Drag&Drop Zone
4. **Dashboard konfigurierbar** - Config-Button oeffnet Modal mit Toggle pro Widget (KPIs, Schnellzugriffe, Aktivitaeten, Tasks, Top-Fahrzeuge), gespeichert in localStorage
## Verifiziert (Screenshots)
- Dashboard v4 mit Config-Button: ✅
- Fahrzeug-Formular v4 mit Tabs: ✅
## Nächste Schritte
- User-Feedback zu v4 abwarten
+511
View File
@@ -0,0 +1,511 @@
{
"project": "erp-nutzfahrzeuge",
"version": "1.0",
"created": "2026-07-12",
"status": "approved_for_implementation",
"total_tasks": 8,
"tasks": [
{
"id": "T01",
"title": "Auth + User Management + RBAC + Base Frontend Layout + i18n Setup",
"category": "foundation",
"description": "Komplettes Auth-System: JWT login/refresh, User CRUD (admin only), RBAC Middleware (admin/verkaeufer/buchhaltung). Backend: User Model, auth_service, auth router, users router, JWT utils, RBAC dependency. Frontend: Root layout, Login page, Auth context/hooks, i18n setup (next-intl oder react-i18next), de.json + en.json Basistranslationen, API client (fetch wrapper mit Auth-Header), Base UI components (Button, Input, Card, Table, Modal, Toast). Health endpoint /api/health. Config via Pydantic BaseSettings (DB URL, Redis URL, JWT Secret, OpenRouter Key, mobile.de credentials).",
"assigned_subagent": "implementation_engineer",
"dependencies": [],
"requirement_ids": ["M6-F1", "M6-F2", "AUTH-1", "AUTH-2", "AUTH-3", "AUTH-4", "I18N-1", "I18N-2"],
"estimated_effort": "L",
"estimated_lines": 600,
"acceptance_criteria": [
"POST /api/auth/login mit valid credentials → 200 + JWT access_token + refresh_token",
"POST /api/auth/login mit invalid credentials → 401",
"POST /api/auth/refresh mit valid refresh_token → 200 + new access_token",
"GET /api/auth/me mit valid JWT → 200 + user object (email, full_name, role)",
"GET /api/users ohne admin role → 403",
"GET /api/users mit admin role → 200 + paginated user list",
"POST /api/users mit valid data → 201 + user object",
"DELETE /api/users/:id → 200 + user.is_active=false",
"GET /api/health → 200 + {status: 'ok'}",
"Frontend Login page rendert und sendet POST /api/auth/login",
"Frontend zeigt Toast bei login error",
"i18n: de.json und en.json existieren mit mindestens 20 keys",
"Sprachumschaltung DE/EN wechselt UI-Texte live",
"RBAC: /api/users blocked für verkaeufer role → 403",
"pytest coverage >= 80% für auth module"
],
"test_spec": {
"commands": [
"cd backend && python -m pytest tests/test_auth.py tests/test_users.py --cov=app/services/auth_service --cov=app/routers/auth --cov=app/routers/users --cov-report=term-missing -v",
"cd backend && python -m pytest tests/test_health.py -v",
"cd frontend && npx vitest run tests/auth.test.tsx tests/i18n.test.tsx --coverage"
],
"expected_results": "All auth tests pass (login, refresh, RBAC, user CRUD). Health endpoint returns 200. Frontend login form renders and submits. i18n loads DE/EN translations. Coverage >= 80% backend auth, >= 70% frontend auth.",
"test_files": [
"backend/tests/test_auth.py",
"backend/tests/test_users.py",
"backend/tests/test_health.py",
"backend/tests/conftest.py",
"frontend/tests/auth.test.tsx",
"frontend/tests/i18n.test.tsx"
],
"coverage_target": 80
},
"files_to_create": [
"backend/app/main.py",
"backend/app/config.py",
"backend/app/database.py",
"backend/app/dependencies.py",
"backend/app/models/user.py",
"backend/app/schemas/user.py",
"backend/app/routers/auth.py",
"backend/app/routers/users.py",
"backend/app/services/auth_service.py",
"backend/app/utils/jwt.py",
"backend/app/utils/i18n.py",
"backend/tests/test_auth.py",
"backend/tests/test_users.py",
"backend/tests/test_health.py",
"backend/tests/conftest.py",
"frontend/app/layout.tsx",
"frontend/app/(auth)/login/page.tsx",
"frontend/lib/api.ts",
"frontend/lib/auth.ts",
"frontend/lib/i18n.ts",
"frontend/messages/de.json",
"frontend/messages/en.json",
"frontend/components/ui/Button.tsx",
"frontend/components/ui/Input.tsx",
"frontend/components/ui/Card.tsx",
"frontend/components/ui/Table.tsx",
"frontend/components/ui/Modal.tsx",
"frontend/components/ui/Toast.tsx",
"frontend/tests/auth.test.tsx",
"frontend/tests/i18n.test.tsx"
]
},
{
"id": "T02",
"title": "Vehicle Management + mobile.de Push + Vehicle UI",
"category": "vehicle",
"description": "Komplettes Fahrzeug-Modul: Vehicle Model (LKW, Baumaschine, PKW, Stapler, Transporter), vehicle_service mit CRUD + filter/sort/paginate, vehicles router mit allen Endpoints. mobile.de Seller API Integration (Push only): mobilede_service, mobilede_push background task, Ad-Format Mapping, Retry-Queue via Redis. Frontend: Vehicle List page mit Filter (type, status, brand) + Pagination + Sort, Vehicle Detail page, Vehicle Create/Edit form, mobile.de Push Button + Status indicator. Soft-Delete Implementierung (status='deleted').",
"assigned_subagent": "implementation_engineer",
"dependencies": ["T01"],
"requirement_ids": ["M1-F1", "M1-F2", "M1-F3", "M1-F4", "M1-F5", "M1-F6", "M1-F7"],
"estimated_effort": "L",
"estimated_lines": 700,
"acceptance_criteria": [
"GET /api/vehicles → 200 + paginated list (page, per_page, total)",
"GET /api/vehicles?type=lkw&status=in_stock → 200 + filtered list",
"GET /api/vehicles?sort=-created_at → 200 + sorted list",
"GET /api/vehicles/:id → 200 + vehicle detail object",
"GET /api/vehicles/:nonexistent → 404",
"POST /api/vehicles mit valid data → 201 + vehicle object",
"POST /api/vehicles mit missing brand → 422 validation error",
"PUT /api/vehicles/:id → 200 + updated vehicle",
"DELETE /api/vehicles/:id → 200 + status='deleted' (soft delete)",
"POST /api/vehicles/:id/mobilede-push → 202 (async push queued)",
"GET /api/vehicles/:id/mobilede-status → 200 + {synced: bool, ad_id, synced_at}",
"mobile.de Push sendet korrektes Ad-Format an Seller API",
"mobile.de Push Fehler → Retry-Queue (Redis), max 3 retries",
"Frontend Vehicle List zeigt Tabelle mit Filter + Pagination",
"Frontend Vehicle Detail zeigt alle Felder + mobile.de Status",
"Frontend Vehicle Create Form validiert Pflichtfelder",
"Frontend mobile.de Push Button → zeigt Sync-Status",
"pytest coverage >= 80% für vehicle module"
],
"test_spec": {
"commands": [
"cd backend && python -m pytest tests/test_vehicles.py tests/test_mobilede.py --cov=app/services/vehicle_service --cov=app/services/mobilede_service --cov=app/routers/vehicles --cov-report=term-missing -v",
"cd frontend && npx vitest run tests/vehicles.test.tsx --coverage"
],
"expected_results": "All vehicle CRUD tests pass. Filter, sort, pagination work. mobile.de push queues async task. Soft delete sets status=deleted. Frontend list renders with filters. Coverage >= 80% backend, >= 70% frontend.",
"test_files": [
"backend/tests/test_vehicles.py",
"backend/tests/test_mobilede.py",
"frontend/tests/vehicles.test.tsx"
],
"coverage_target": 80
},
"files_to_create": [
"backend/app/models/vehicle.py",
"backend/app/schemas/vehicle.py",
"backend/app/routers/vehicles.py",
"backend/app/services/vehicle_service.py",
"backend/app/services/mobilede_service.py",
"backend/app/tasks/mobilede_push.py",
"backend/app/utils/mobilede_mapping.py",
"backend/tests/test_vehicles.py",
"backend/tests/test_mobilede.py",
"frontend/app/vehicles/page.tsx",
"frontend/app/vehicles/[id]/page.tsx",
"frontend/app/vehicles/new/page.tsx",
"frontend/components/vehicles/VehicleList.tsx",
"frontend/components/vehicles/VehicleForm.tsx",
"frontend/components/vehicles/VehicleDetail.tsx",
"frontend/components/vehicles/MobileDeStatus.tsx",
"frontend/tests/vehicles.test.tsx"
]
},
{
"id": "T03",
"title": "OCR-Erfassung (ZB I/II) via OpenRouter Vision + OCR UI",
"category": "ocr",
"description": "Komplettes OCR-Modul: OCRResult Model, ocr_service mit OpenRouter Qwen2.5-VL Integration, OCR router (upload, get results, apply to vehicle). Async OCR processing via Redis Queue. Prompt-Engineering für ZB I/II: structured JSON output (brand, model, vin, first_registration, mileage, power_kw, fuel_type). Confidence-Score Berechnung. Manual Review bei confidence < 0.7. Response-Caching für identische Scans. Frontend: OCR Upload page (drag-and-drop), OCR Results view mit side-by-side Original Scan + Extracted Data, Apply-to-Vehicle Button.",
"assigned_subagent": "implementation_engineer",
"dependencies": ["T01", "T02"],
"requirement_ids": ["M2-F1", "M2-F2", "M2-F3", "M2-F4", "M2-F5"],
"estimated_effort": "L",
"estimated_lines": 600,
"acceptance_criteria": [
"POST /api/ocr/upload mit image file → 202 + ocr_result_id (async processing)",
"POST /api/ocr/upload ohne file → 422",
"POST /api/ocr/upload mit invalid MIME type → 422",
"GET /api/ocr/results/:id → 200 + {status, raw_text, structured_data, confidence_score}",
"GET /api/ocr/results?vehicle_id=X → 200 + list of OCR results for vehicle",
"POST /api/ocr/results/:id/apply → 200 + vehicle updated with OCR data",
"OCR processing mit mocked OpenRouter → structured_data enthält brand, model, vin",
"Confidence score < 0.7 → status='pending' (manual review needed)",
"Confidence score >= 0.7 → status='completed'",
"OCR Fehler (OpenRouter unavailable) → status='failed' + error message",
"Frontend OCR Upload akzeptiert Drag-and-Drop",
"Frontend OCR Results zeigt Original + Extracted Data side-by-side",
"Frontend Apply Button → sendet POST /api/ocr/results/:id/apply",
"pytest coverage >= 80% für ocr module"
],
"test_spec": {
"commands": [
"cd backend && python -m pytest tests/test_ocr.py --cov=app/services/ocr_service --cov=app/routers/ocr --cov=app/utils/openrouter --cov-report=term-missing -v",
"cd frontend && npx vitest run tests/ocr.test.tsx --coverage"
],
"expected_results": "All OCR tests pass with mocked OpenRouter. Upload queues async task. Results retrievable. Apply updates vehicle. Confidence threshold works. Frontend upload + results render. Coverage >= 80% backend, >= 70% frontend.",
"test_files": [
"backend/tests/test_ocr.py",
"frontend/tests/ocr.test.tsx"
],
"coverage_target": 80
},
"files_to_create": [
"backend/app/models/ocr_result.py",
"backend/app/schemas/ocr.py",
"backend/app/routers/ocr.py",
"backend/app/services/ocr_service.py",
"backend/app/tasks/ocr_processing.py",
"backend/app/utils/openrouter.py",
"backend/tests/test_ocr.py",
"frontend/app/ocr/page.tsx",
"frontend/components/ocr/OCRUpload.tsx",
"frontend/components/ocr/OCRResults.tsx",
"frontend/components/ocr/OCRDetail.tsx",
"frontend/tests/ocr.test.tsx"
]
},
{
"id": "T04",
"title": "Kontakt-/Kundenverwaltung + Contact UI",
"category": "contact",
"description": "Komplettes Kontakt-Modul: Contact Model (Kunde/Lieferant/Beide, EU/Inland, USt-IdNr.), contact_service mit CRUD + search + filter (type, eu/inland), contacts router. USt-IdNr.-Feld mit Format-Validierung (DE + EU Formate). Contact-Search (name, company, city, email). Frontend: Contact List mit Search + Filter, Contact Detail page, Contact Create/Edit form. EU/Inland Toggle. USt-IdNr. input mit Format-Validierung frontend-seitig.",
"assigned_subagent": "implementation_engineer",
"dependencies": ["T01"],
"requirement_ids": ["M3-F1", "M3-F2", "M3-F3", "M3-F4", "M3-F5", "M3-F6"],
"estimated_effort": "M",
"estimated_lines": 500,
"acceptance_criteria": [
"GET /api/contacts → 200 + paginated list",
"GET /api/contacts?type=kunde → 200 + filtered list",
"GET /api/contacts?is_eu=true → 200 + EU contacts only",
"GET /api/contacts?search=mueller → 200 + matching contacts",
"GET /api/contacts/:id → 200 + contact detail",
"GET /api/contacts/:nonexistent → 404",
"POST /api/contacts mit valid data → 201 + contact object",
"POST /api/contacts mit invalid ust_id_nr format → 422",
"PUT /api/contacts/:id → 200 + updated contact",
"DELETE /api/contacts/:id → 200 (or 404 if not found)",
"Contact mit contact_type='beide' erscheint in kunde und lieferant filter",
"Frontend Contact List zeigt Tabelle mit Search + Filter",
"Frontend Contact Form validiert USt-IdNr. Format",
"Frontend EU/Inland Toggle ändert Pflichtfelder",
"pytest coverage >= 80% für contact module"
],
"test_spec": {
"commands": [
"cd backend && python -m pytest tests/test_contacts.py --cov=app/services/contact_service --cov=app/routers/contacts --cov-report=term-missing -v",
"cd frontend && npx vitest run tests/contacts.test.tsx --coverage"
],
"expected_results": "All contact CRUD tests pass. Search, filter, pagination work. USt-IdNr. validation rejects invalid formats. Frontend list renders with search/filter. Coverage >= 80% backend, >= 70% frontend.",
"test_files": [
"backend/tests/test_contacts.py",
"frontend/tests/contacts.test.tsx"
],
"coverage_target": 80
},
"files_to_create": [
"backend/app/models/contact.py",
"backend/app/schemas/contact.py",
"backend/app/routers/contacts.py",
"backend/app/services/contact_service.py",
"backend/app/utils/ust_validation.py",
"backend/tests/test_contacts.py",
"frontend/app/contacts/page.tsx",
"frontend/app/contacts/[id]/page.tsx",
"frontend/app/contacts/new/page.tsx",
"frontend/components/contacts/ContactList.tsx",
"frontend/components/contacts/ContactForm.tsx",
"frontend/components/contacts/ContactDetail.tsx",
"frontend/tests/contacts.test.tsx"
]
},
{
"id": "T05",
"title": "Dateiablage pro Fahrzeug + File UI",
"category": "file",
"description": "Komplettes Dateiablage-Modul: File Model, file_service mit Upload/Download/Delete, files router. MIME-Type-Validierung (images: jpg/png/webp, documents: pdf/doc/docx, scans: jpg/png). Max 20MB pro Datei. Dateien gespeichert im uploads_data Volume. Thumbnail-Generierung für Images. Frontend: File Upload Component (Drag-and-Drop, multi-file), File List mit Thumbnails, File Download, File Delete mit Confirm. Gallery view für Fahrzeug-Bilder.",
"assigned_subagent": "implementation_engineer",
"dependencies": ["T01", "T02"],
"requirement_ids": ["M4-F1", "M4-F2", "M4-F3", "M4-F4", "M4-F5"],
"estimated_effort": "M",
"estimated_lines": 500,
"acceptance_criteria": [
"GET /api/vehicles/:id/files → 200 + list of files",
"POST /api/vehicles/:id/files mit multipart image → 201 + file object",
"POST /api/vehicles/:id/files mit >20MB file → 413",
"POST /api/vehicles/:id/files mit invalid MIME type → 422",
"GET /api/vehicles/:id/files/:fileId → 200 + file content (download)",
"DELETE /api/vehicles/:id/files/:fileId → 200 + file deleted",
"File gespeichert in uploads_data volume, path in DB gespeichert",
"Image files → Thumbnail generiert (200x200)",
"Frontend File Upload akzeptiert Drag-and-Drop multi-file",
"Frontend File List zeigt Thumbnails für images",
"Frontend File Delete zeigt Confirm Dialog",
"Frontend Gallery view für Fahrzeug-Bilder",
"pytest coverage >= 80% für file module"
],
"test_spec": {
"commands": [
"cd backend && python -m pytest tests/test_files.py --cov=app/services/file_service --cov=app/routers/files --cov-report=term-missing -v",
"cd frontend && npx vitest run tests/files.test.tsx --coverage"
],
"expected_results": "All file CRUD tests pass. MIME validation rejects invalid types. Size limit enforced. Download returns file content. Thumbnails generated for images. Frontend upload + list + gallery render. Coverage >= 80% backend, >= 70% frontend.",
"test_files": [
"backend/tests/test_files.py",
"frontend/tests/files.test.tsx"
],
"coverage_target": 80
},
"files_to_create": [
"backend/app/models/file.py",
"backend/app/schemas/file.py",
"backend/app/routers/files.py",
"backend/app/services/file_service.py",
"backend/app/utils/thumbnails.py",
"backend/tests/test_files.py",
"frontend/components/files/FileUpload.tsx",
"frontend/components/files/FileList.tsx",
"frontend/components/files/FileGallery.tsx",
"frontend/components/files/FilePreview.tsx",
"frontend/tests/files.test.tsx"
]
},
{
"id": "T06",
"title": "Verkaufsmodul + Rechtsdokumente + DATEV-Export + Sales UI",
"category": "sales",
"description": "Komplettes Verkaufsmodul: Sale Model, sale_service mit CRUD, sales router. Contract PDF Generierung (Verkaufvertrag mit Vehicle + Contact Daten, GwG-Klausel, USt-IdNr. Feld). DATEV-Export: datev_service, DATEV CSV Format (Buchungsstapel: Datum, Konto, Gegenkonto, Betrag, Belegfeld, Buchungstext). GwG-Logik: Sofortabzug bei <= 800EUR (§6 Abs. 2 EStG). USt-IdNr.-Prüfung: BZSt API endpoint (Feature-Flag bzst_api_enabled, aktuell disabled → manual review). DATEVExport Model + router. Frontend: Sales List, Sale Detail mit Contract Preview, Sale Create form (Vehicle select, Contact select, Price, GwG toggle), DATEV Export page (Zeitraum wählen, Download CSV).",
"assigned_subagent": "implementation_engineer",
"dependencies": ["T02", "T04"],
"requirement_ids": ["M5-F1", "M5-F2", "M5-F3", "M5-F4", "M5-F5", "M5-F6", "M5-F7", "M5-F8", "M5-F9"],
"estimated_effort": "XL",
"estimated_lines": 800,
"acceptance_criteria": [
"GET /api/sales → 200 + paginated list with filter (date range, status)",
"GET /api/sales/:id → 200 + sale detail with vehicle + contact nested",
"POST /api/sales mit valid data → 201 + sale object + contract_pdf_path",
"POST /api/sales ohne vehicle_id → 422",
"POST /api/sales ohne buyer_contact_id → 422",
"PUT /api/sales/:id → 200 + updated sale",
"DELETE /api/sales/:id → 200 (sale cancelled, vehicle status back to in_stock)",
"POST /api/sales/:id/contract → 200 + regenerated contract PDF path",
"GET /api/sales/:id/contract → 200 + PDF content (application/pdf)",
"Sale mit is_gwg=true und sale_price <= 800 → GwG-Klausel in contract",
"POST /api/sales/:id/verify-ust-id → 200 + {verified: false, message: 'BZSt API not available'} (feature flag disabled)",
"POST /api/datev/export mit date range → 201 + DATEVExport object",
"POST /api/datev/export mit invalid date range → 422",
"GET /api/datev/exports → 200 + list of exports",
"GET /api/datev/exports/:id/download → 200 + CSV content (text/csv)",
"DATEV CSV enthält korrekte Felder: Datum, Konto, Gegenkonto, Betrag, Belegfeld, Buchungstext",
"Sale creation → Vehicle status='sold'",
"Sale deletion → Vehicle status='in_stock'",
"Frontend Sales List zeigt Tabelle mit Date Filter",
"Frontend Sale Detail zeigt Contract Preview (PDF embed)",
"Frontend Sale Create Form mit Vehicle + Contact Selectors",
"Frontend DATEV Export page mit Date Range Picker + Download",
"pytest coverage >= 80% für sales + datev module"
],
"test_spec": {
"commands": [
"cd backend && python -m pytest tests/test_sales.py tests/test_datev.py --cov=app/services/sale_service --cov=app/services/datev_service --cov=app/routers/sales --cov=app/routers/datev --cov-report=term-missing -v",
"cd frontend && npx vitest run tests/sales.test.tsx tests/datev.test.tsx --coverage"
],
"expected_results": "All sales CRUD tests pass. Contract PDF generated. DATEV CSV export with correct format. GwG logic applied. Vehicle status changes on sale create/delete. USt-IdNr. endpoint returns feature-flag message. Frontend list + detail + form + DATEV export render. Coverage >= 80% backend, >= 70% frontend.",
"test_files": [
"backend/tests/test_sales.py",
"backend/tests/test_datev.py",
"frontend/tests/sales.test.tsx",
"frontend/tests/datev.test.tsx"
],
"coverage_target": 80
},
"files_to_create": [
"backend/app/models/sale.py",
"backend/app/models/datev_export.py",
"backend/app/schemas/sale.py",
"backend/app/schemas/datev.py",
"backend/app/routers/sales.py",
"backend/app/routers/datev.py",
"backend/app/services/sale_service.py",
"backend/app/services/datev_service.py",
"backend/app/utils/contract_pdf.py",
"backend/app/utils/datev.py",
"backend/tests/test_sales.py",
"backend/tests/test_datev.py",
"frontend/app/sales/page.tsx",
"frontend/app/sales/[id]/page.tsx",
"frontend/app/sales/new/page.tsx",
"frontend/components/sales/SaleList.tsx",
"frontend/components/sales/SaleForm.tsx",
"frontend/components/sales/ContractPreview.tsx",
"frontend/components/sales/DatevExport.tsx",
"frontend/tests/sales.test.tsx",
"frontend/tests/datev.test.tsx"
]
},
{
"id": "T07",
"title": "KI-Copilot (Text + Sprache) + Systemsteuerung + Copilot UI",
"category": "copilot",
"description": "Komplettes KI-Copilot-Modul: Copilot chat_service mit OpenRouter (Claude/GPT-4), copilot router (chat, voice, history, action). System-Prompt mit ERP-Kontext (verfügbare Aktionen: vehicle search/create, contact search, sale overview). Action-System: Copilot kann strukturierte Actions ausführen (GET /api/vehicles, POST /api/contacts, etc.) via function-calling. Voice Input: STT via Browser Web Speech API → text → chat. Chat History (paginated, pro User). Frontend: Copilot Chat Interface (message list, input, send), Voice Input Button (microphone), Action Preview (zeigt was Copilot tun will vor Ausführung), Chat History sidebar.",
"assigned_subagent": "implementation_engineer",
"dependencies": ["T01", "T02"],
"requirement_ids": ["M7-F1", "M7-F2", "M7-F3", "M7-F4", "M7-F5", "M7-F6", "M7-F7"],
"estimated_effort": "L",
"estimated_lines": 700,
"acceptance_criteria": [
"POST /api/copilot/chat mit {message: 'Zeige alle LKWs'} → 200 + {response: '...', actions: [{type: 'search_vehicles', params: {type: 'lkw'}}]}",
"POST /api/copilot/chat mit empty message → 422",
"POST /api/copilot/action mit {action: 'search_vehicles', params: {type: 'lkw'}} → 200 + {result: [...vehicles...]}",
"POST /api/copilot/action mit invalid action → 400",
"GET /api/copilot/history → 200 + paginated chat history for current user",
"POST /api/copilot/voice → 200 (accepts audio, transcribes, returns chat response)",
"Copilot System-Prompt enthält ERP-Kontext und verfügbare Actions",
"Copilot kann vehicle search action auslösen (mocked OpenRouter)",
"Copilot kann contact search action auslösen (mocked OpenRouter)",
"Copilot response enthält action proposal (nicht direkt ausgeführt, User bestätigt)",
"Frontend Copilot Chat rendert message list + input",
"Frontend Voice Button startet Web Speech API",
"Frontend Action Preview zeigt geplante Aktion vor Bestätigung",
"Frontend Chat History sidebar zeigt vergangene Konversationen",
"pytest coverage >= 80% für copilot module (mit mocked OpenRouter)"
],
"test_spec": {
"commands": [
"cd backend && python -m pytest tests/test_copilot.py --cov=app/services/copilot_service --cov=app/routers/copilot --cov-report=term-missing -v",
"cd frontend && npx vitest run tests/copilot.test.tsx --coverage"
],
"expected_results": "All copilot tests pass with mocked OpenRouter. Chat returns response + action proposals. Action endpoint executes approved actions. History paginated. Voice endpoint accepts audio. Frontend chat + voice + action preview render. Coverage >= 80% backend, >= 70% frontend.",
"test_files": [
"backend/tests/test_copilot.py",
"frontend/tests/copilot.test.tsx"
],
"coverage_target": 80
},
"files_to_create": [
"backend/app/models/copilot.py",
"backend/app/schemas/copilot.py",
"backend/app/routers/copilot.py",
"backend/app/services/copilot_service.py",
"backend/app/utils/copilot_actions.py",
"backend/app/utils/copilot_prompt.py",
"backend/tests/test_copilot.py",
"frontend/app/copilot/page.tsx",
"frontend/components/copilot/ChatInterface.tsx",
"frontend/components/copilot/MessageList.tsx",
"frontend/components/copilot/ChatInput.tsx",
"frontend/components/copilot/VoiceInput.tsx",
"frontend/components/copilot/ActionPreview.tsx",
"frontend/components/copilot/ChatHistory.tsx",
"frontend/tests/copilot.test.tsx"
]
},
{
"id": "T08",
"title": "Bildretusche + Preisvergleich (Flux.1-Pro + mobile.de) + Retouch UI",
"category": "retouch",
"description": "Komplettes Bildretusche-Modul: Retouch service mit OpenRouter Flux.1-Pro, retouch router (process, get result, price-compare). Bild-Verarbeitung: Upload → Flux.1-Pro Retusche (Hintergrund entfernen/verbessern, Spiegelungen, Farbkorrektur) → retouched image gespeichert. Preisvergleich: mobile.de Listings Scraper/API → comparable vehicles (brand, model, year, price) → Durchschnittspreis berechnet. Frontend: Retouch Upload page (Bild wählen, Retusche starten), Before/After Vergleich (slider), Preisvergleich view (Tabelle mit comparable listings + Durchschnittspreis).",
"assigned_subagent": "implementation_engineer",
"dependencies": ["T01", "T02", "T05"],
"requirement_ids": ["M8-F1", "M8-F2", "M8-F3", "M8-F4", "M8-F5"],
"estimated_effort": "L",
"estimated_lines": 600,
"acceptance_criteria": [
"POST /api/retouch/process mit image file → 202 + {retouch_id} (async processing)",
"POST /api/retouch/process ohne image → 422",
"POST /api/retouch/process mit invalid MIME → 422",
"GET /api/retouch/results/:id → 200 + {status, original_url, retouched_url}",
"GET /api/retouch/results/:id vor completion → 200 + {status: 'processing'}",
"Retouch Fehler (OpenRouter unavailable) → 200 + {status: 'failed', error: '...'}",
"POST /api/retouch/price-compare mit {vehicle_id} → 200 + {comparable_listings: [...], average_price: X}",
"POST /api/retouch/price-compare ohne vehicle_id → 422",
"Price-compare mit no comparable listings → 200 + {comparable_listings: [], average_price: null}",
"Frontend Retouch Upload → Bild auswählen + Retusche starten",
"Frontend Before/After Slider → vergleicht Original vs Retuschiert",
"Frontend Preisvergleich Tabelle → zeigt comparable listings + Durchschnittspreis",
"pytest coverage >= 80% für retouch module (mit mocked OpenRouter)"
],
"test_spec": {
"commands": [
"cd backend && python -m pytest tests/test_retouch.py --cov=app/services/retouch_service --cov=app/routers/image_retouch --cov-report=term-missing -v",
"cd frontend && npx vitest run tests/retouch.test.tsx --coverage"
],
"expected_results": "All retouch tests pass with mocked OpenRouter. Process queues async task. Results retrievable with status. Price-compare returns listings + average. Frontend upload + before/after + price comparison render. Coverage >= 80% backend, >= 70% frontend.",
"test_files": [
"backend/tests/test_retouch.py",
"frontend/tests/retouch.test.tsx"
],
"coverage_target": 80
},
"files_to_create": [
"backend/app/models/retouch.py",
"backend/app/schemas/retouch.py",
"backend/app/routers/image_retouch.py",
"backend/app/services/retouch_service.py",
"backend/app/services/price_compare_service.py",
"backend/app/tasks/retouch_processing.py",
"backend/tests/test_retouch.py",
"frontend/app/retouch/page.tsx",
"frontend/components/retouch/RetouchUpload.tsx",
"frontend/components/retouch/BeforeAfterSlider.tsx",
"frontend/components/retouch/PriceComparison.tsx",
"frontend/tests/retouch.test.tsx"
]
}
],
"dependency_graph": {
"T01": [],
"T02": ["T01"],
"T03": ["T01", "T02"],
"T04": ["T01"],
"T05": ["T01", "T02"],
"T06": ["T02", "T04"],
"T07": ["T01", "T02"],
"T08": ["T01", "T02", "T05"]
},
"execution_order": ["T01", "T02", "T04", "T03", "T05", "T06", "T07", "T08"],
"summary": {
"total_tasks": 8,
"tasks_with_test_spec": 8,
"tasks_with_acceptance_criteria": 8,
"tasks_with_dependencies": 8,
"total_requirement_ids": 45,
"total_acceptance_criteria": 115,
"total_files_to_create": 120,
"estimated_total_lines": 5000
}
}
+490
View File
@@ -0,0 +1,490 @@
# AGENTS.md ERP Nutzfahrzeuge
> Build & Test Commands, Test Rules, Conventions für Implementierung
---
## 1. Build & Test Commands
### 1.1 Backend (Python/FastAPI)
```bash
# Dependencies installieren
cd backend
pip install -r requirements.txt
# DB Migration (Alembic)
alembic upgrade head
# Dev Server starten
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
# Tests ausführen (alle)
pytest --cov=app --cov-report=term-missing -v
# Tests für spezifisches Modul
pytest tests/test_vehicles.py --cov=app/services/vehicle_service --cov=app/routers/vehicles --cov-report=term-missing -v
# Type checking
mypy app/ --ignore-missing-imports
# Linting
ruff check app/
# Format check
black --check app/
```
### 1.2 Frontend (Next.js)
```bash
# Dependencies installieren
cd frontend
npm install
# Dev Server starten
npm run dev
# Build (production)
npm run build
# Tests ausführen (Unit)
npx vitest run --coverage
# E2E Tests
npx playwright test
# Type checking
npx tsc --noEmit
# Linting
npm run lint
```
### 1.3 Docker (Full Stack)
```bash
# Alle Container starten
docker compose up -d
# Logs anzeigen
docker compose logs -f backend
docker compose logs -f frontend
# Rebuild
docker compose up -d --build
# Stop
docker compose down
# Test environment
docker compose -f docker-compose.test.yml up --abort-on-container-exit
```
### 1.4 Database
```bash
# PostgreSQL Shell
docker compose exec postgres psql -U erp_user -d erp_db
# Backup erstellen
docker compose exec postgres pg_dump -U erp_user erp_db > backup.sql
# Restore
docker compose exec -T postgres psql -U erp_user -d erp_db < backup.sql
# Migration erstellen
cd backend
alembic revision --autogenerate -m "description"
alembic upgrade head
```
---
## 2. Test Rules (MANDATORY)
### 2.1 TDD Test Driven Development
- **Tests werden NICHT modifiziert** Tests sind die Spec, Code muss sich anpassen
- **Kein 'done' ohne Test-Evidence** Build, Test, Smoke-Test müssen durchlaufen
- **Coverage Target**: ≥ 80% Backend, ≥ 70% Frontend
- **Test-First**: Tests schreiben → Code implementieren → Tests grün
- **Kein Skip**: `pytest.skip` oder `it.skip` nur mit Begründung als Kommentar
### 2.2 Test-Struktur Backend
```
tests/
├── conftest.py # Fixtures: test client, DB session, mock OpenRouter
├── test_auth.py # Auth + JWT + RBAC tests
├── test_users.py # User CRUD tests
├── test_health.py # Health endpoint test
├── test_vehicles.py # Vehicle CRUD + filter + pagination tests
├── test_mobilede.py # mobile.de push + retry queue tests
├── test_ocr.py # OCR upload + processing + apply tests
├── test_contacts.py # Contact CRUD + search + USt-IdNr. validation tests
├── test_files.py # File upload + download + MIME validation tests
├── test_sales.py # Sale CRUD + contract PDF + GwG tests
├── test_datev.py # DATEV export + CSV format tests
├── test_copilot.py # Copilot chat + action + history tests
└── test_retouch.py # Retouch + price comparison tests
```
### 2.3 Test-Struktur Frontend
```
tests/
├── auth.test.tsx # Login form, auth context, token refresh
├── i18n.test.tsx # Translation loading, language switch
├── vehicles.test.tsx # Vehicle list, form, detail, mobile.de status
├── ocr.test.tsx # OCR upload, results, apply
├── contacts.test.tsx # Contact list, form, USt-IdNr. validation
├── files.test.tsx # File upload, list, gallery
├── sales.test.tsx # Sale list, form, contract preview
├── datev.test.tsx # DATEV export, download
├── copilot.test.tsx # Chat interface, voice input, action preview
└── retouch.test.tsx # Retouch upload, before/after, price comparison
```
### 2.4 Test-Fixtures (conftest.py)
```python
# Pflicht-Fixtures in conftest.py:
# - test_client: httpx.AsyncClient mit Test-App
# - db_session: async SQLAlchemy Session mit rollback
# - mock_openrouter: Mock für OpenRouter API (OCR, Copilot, Retouch)
# - mock_mobilede: Mock für mobile.de Seller API
# - auth_headers: JWT headers für admin/verkaeufer/buchhaltung roles
# - test_vehicle: Pre-created vehicle for tests
# - test_contact: Pre-created contact for tests
```
### 2.5 Mocking-Regeln
- **OpenRouter API**: IMMER mocken in Tests (kein realer API-Call)
- **mobile.de API**: IMMER mocken in Tests
- **BZSt API**: IMMER mocken (Feature-Flag disabled in tests)
- **Redis**: Test mit fakeredis oder in-memory mock
- **PostgreSQL**: Test-DB mit SQLite (async) oder testcontainers
- **File Storage**: Temp-Verzeichnis (tmp_path fixture)
---
## 3. Code-Konventionen
### 3.1 Backend
#### Python Style
- **Formatter**: black (line-length=100)
- **Linter**: ruff
- **Type Checker**: mypy (strict für services)
- **Import Order**: stdlib → third-party → local (isort)
#### FastAPI Patterns
```python
# Router-Struktur (Pflicht):
router = APIRouter(prefix="/api/vehicles", tags=["vehicles"])
# Async für alle DB-Operationen
async def get_vehicle(vehicle_id: UUID, db: AsyncSession) -> Vehicle:
...
# Pydantic Schema für Request/Response
class VehicleCreate(BaseModel):
brand: str = Field(..., min_length=1, max_length=100)
model: str = Field(..., min_length=1, max_length=150)
...
# Dependency Injection für Auth
async def get_current_user(
token: str = Depends(oauth2_scheme),
db: AsyncSession = Depends(get_db)
) -> User:
...
# RBAC Dependency
def require_role(*roles: str):
async def role_checker(user: User = Depends(get_current_user)) -> User:
if user.role not in roles:
raise HTTPException(status_code=403, detail="Forbidden")
return user
return role_checker
```
#### Error-Format (Pflicht)
```json
{
"error": {
"code": "VEHICLE_NOT_FOUND",
"message": "Vehicle with ID abc123 not found",
"details": {}
}
}
```
#### SQLAlchemy Model Pattern
```python
class Vehicle(Base):
__tablename__ = "vehicles"
id: Mapped[UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid4)
brand: Mapped[str] = mapped_column(String(100), nullable=False)
created_at: Mapped[datetime] = mapped_column(
TIMESTAMPTZ, server_default=func.now()
)
```
### 3.2 Frontend
#### TypeScript Style
- **Strict Mode**: `"strict": true` in tsconfig.json
- **No any**: `"noImplicitAny": true`
- **Import**: absolute imports via `@/` prefix
#### Next.js Patterns
```tsx
// Server Component (default)
export default async function VehicleListPage() {
const vehicles = await fetch(`${API_URL}/api/vehicles`, { cache: 'no-store' });
...
}
// Client Component (use 'use client' directive)
'use client'
export function VehicleForm() {
const [brand, setBrand] = useState('');
...
}
// API Client (zentral in lib/api.ts)
export async function apiFetch<T>(path: string, options?: RequestInit): Promise<T> {
const token = getAuthToken();
const res = await fetch(`${API_URL}${path}`, {
...options,
headers: {
'Content-Type': 'application/json',
...(token ? { Authorization: `Bearer ${token}` } : {}),
...options?.headers,
},
});
if (!res.ok) throw new ApiError(res.status, await res.json());
return res.json();
}
```
#### Tailwind CSS
- **Kein CSS-in-JS** (keine styled-components, emotion)
- **Utility-First**: Tailwind-Klassen direkt in JSX
- **Responsive**: mobile-first (`sm:`, `md:`, `lg:`)
- **Dark Mode**: `dark:` prefix (geplant für v2)
### 3.3 i18n
#### Translation File Format (JSON)
```json
{
"vehicles": {
"title": "Fahrzeugbestand",
"create": "Neues Fahrzeug",
"brand": "Marke",
"model": "Modell",
"status": {
"in_stock": "Auf Lager",
"reserved": "Reserviert",
"sold": "Verkauft"
}
},
"common": {
"save": "Speichern",
"cancel": "Abbrechen",
"delete": "Löschen",
"confirm": "Bestätigen"
}
}
```
#### i18n Usage in Components
```tsx
import { useTranslations } from 'next-intl';
function VehicleList() {
const t = useTranslations('vehicles');
return <h1>{t('title')}</h1>;
}
```
---
## 4. Git-Konventionen
### 4.1 Commit Messages
``nfeat: add vehicle CRUD with mobile.de push
fix: correct OCR confidence threshold logic
docs: update architecture ADR-004
test: add contact USt-IdNr. validation tests
refactor: extract OpenRouter client to utils
```
### 4.2 Branch Naming
```
feature/T01-auth-foundation
feature/T02-vehicle-module
fix/ocr-confidence-threshold
hotfix/datev-csv-format
```
### 4.3 PR Rules
- PRs erforderlich für `main` Branch
- Mindestens 1 Reviewer
- Alle CI Checks müssen grün sein
- Coverage darf nicht sinken
---
## 5. Environment Variables
### 5.1 Backend (.env)
```
DATABASE_URL=postgresql+asyncpg://erp_user:erp_pass@postgres:5432/erp_db
REDIS_URL=redis://redis:6379/0
JWT_SECRET=<secure-random-string>
JWT_ACCESS_EXPIRE_MINUTES=15
JWT_REFRESH_EXPIRE_DAYS=7
OPENROUTER_API_KEY=<key>
MOBILEDE_SELLER_API_KEY=<key>
MOBILEDE_SELLER_API_URL=https://api.mobile.de/seller/v1
BZST_API_ENABLED=false
UPLOAD_DIR=/data/uploads
MAX_FILE_SIZE_MB=20
CORS_ORIGINS=https://erp.media-on.de
```
### 5.2 Frontend (.env.local)
```
NEXT_PUBLIC_API_URL=https://erp.media-on.de/api
NEXT_PUBLIC_DEFAULT_LOCALE=de
```
---
## 6. Task-Ausführungs-Regeln
### 6.1 Task-Reihenfolge (Dependency-basiert)
```
T01 (Auth+i18n) → T02 (Vehicle) → T04 (Contacts) → T03 (OCR) → T05 (Files) → T06 (Sales) → T07 (Copilot) → T08 (Retouch)
```
### 6.2 Pro Task
1. Lese task_graph.json für Task-Details
2. Implementiere alle `files_to_create` für den Task
3. Schreibe Tests zuerst (TDD)
4. Führe `test_spec.commands` aus
5. Verifiziere `acceptance_criteria`
6. Coverage >= `coverage_target` muss erreicht sein
7. Keine Dateien aus anderen Tasks erstellen
### 6.3 Verboten
- **Keine Micro-Tasks**: Ein Task = ein komplettes Modul
- **Keine Tests modifizieren**: Tests sind die Spec
- **Kein 'done' ohne Evidence**: Test-Output als Beweis
- **Keine Secrets im Code**: Nur Environment Variables
- **Keine Hardcoded URLs**: Config via BaseSettings
- **Kein sync Code für DB**: async/await Pflicht
- **Kein `any` in TypeScript**: strict mode
---
## 7. Quality Gates
| Gate | Kriterium | Tool |
|---|---|---|
| Lint | ruff check, eslint | ruff, eslint |
| Format | black --check, prettier --check | black, prettier |
| Types | mypy, tsc --noEmit | mypy, tsc |
| Tests | pytest, vitest | pytest, vitest |
| Coverage | >= 80% backend, >= 70% frontend | pytest-cov, vitest coverage |
| Build | docker compose build | docker |
| Security | pip-audit, npm audit | pip-audit, npm audit |
---
## 8. Deployment (Coolify)
### 8.1 docker-compose.yml (Produktion)
```yaml
version: '3.8'
services:
frontend:
build: ./frontend
ports:
- "3000:3000"
environment:
- NEXT_PUBLIC_API_URL=https://erp.media-on.de/api
depends_on:
- backend
restart: unless-stopped
backend:
build: ./backend
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql+asyncpg://erp_user:${POSTGRES_PASSWORD}@postgres:5432/erp_db
- REDIS_URL=redis://redis:6379/0
- JWT_SECRET=${JWT_SECRET}
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
depends_on:
- postgres
- redis
restart: unless-stopped
postgres:
image: postgres:16-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_USER=erp_user
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=erp_db
restart: unless-stopped
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
restart: unless-stopped
volumes:
postgres_data:
redis_data:
uploads_data:
```
### 8.2 Health Endpoints
- Backend: `GET /api/health` → `{"status": "ok"}`
- Frontend: `GET /` → HTTP 200
### 8.3 Backup
- PostgreSQL: Daily dump via Coolify cron
- Uploads: Volume backup
- Redis: Optional (cache can be rebuilt)
---
## 9. Open Questions / TODOs
| Frage | Status | Verantwortlich |
|---|---|---|
| BZSt API Zugang beantragen | Offen (Feature-Flag disabled) | Admin |
| mobile.de Seller API Credentials | Benötigt (in Coolify env vars) | Admin |
| OpenRouter API Key | Benötigt (in Coolify env vars) | Admin |
| Domain erp.media-on.de DNS | Zu konfigurieren | DevOps |
| DATEV Berater-Nummer | Zu klären mit Buchhaltung | Buchhaltung |
| Contract PDF Template | Zu definieren (Rechtstexte) | Admin + Rechtsanwalt |
---
## 10. Handoff Summary
- **architecture.md**: ✅ Complete (Stack, Module, Data Model, API, ADRs, Test Strategy)
- **task_graph.json**: ✅ Complete (8 Tasks, 115 Acceptance Criteria, alle mit test_spec)
- **AGENTS.md**: ✅ Complete (Build Commands, Test Rules, Conventions)
- **Ready for Implementation**: ✅ Yes (nach Plan Mode Transition zu implementation_allowed)
+1162
View File
File diff suppressed because it is too large Load Diff
+412
View File
@@ -0,0 +1,412 @@
# AGENTS.md ERP Nutzfahrzeuge Implementation Guide
**Version:** 1.0.0
**Datum:** 2026-07-12
---
## 1. Project Overview
ERP-System für Nutzfahrzeug-/Baumaschinen-Handel (~10 Nutzer).
- **Backend:** Python 3.12 / FastAPI / SQLAlchemy 2.0 async / PostgreSQL 16 / Redis 7
- **Frontend:** Next.js 14 (App Router) / React 18 / TypeScript / Tailwind CSS / next-intl
- **Hosting:** Coolify (Docker Compose) auf coolify-01 (46.225.91.159)
- **KI:** OpenRouter (Qwen2.5-VL OCR, Flux.1-Pro Bild, Claude/GPT-4 Copilot)
- **External:** mobile.de Seller API (Push-Only)
### Architecture Reference
- `docs/architecture.md` Complete architecture, DB schema, API design, ADRs
- `docs/task_graph.json` Task breakdown with test specs
- `docs/requirements.md` Full requirements (8 modules, 23 features)
- `docs/component_inventory.md` UI components and states
---
## 2. Build & Test Commands
### Backend
```bash
# Install dependencies
cd backend
pip install -r requirements.txt
# Run dev server
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
# Run all tests
python -m pytest tests/ -v --cov=app --cov-report=term-missing --cov-report=html
# Run specific test file
python -m pytest tests/test_vehicles.py -v
# Run with coverage for specific module
python -m pytest tests/test_auth.py -v --cov=app/services/auth_service --cov-report=term-missing
# Lint
ruff check app/
ruff format app/ --check
# Database migrations
alembic revision --autogenerate -m "description"
alembic upgrade head
alembic downgrade -1
# Type check (optional but recommended)
mypy app/ --ignore-missing-imports
```
### Frontend
```bash
cd frontend
npm install
# Run dev server
npm run dev
# Run all tests
npx vitest run --coverage
# Run specific test
npx vitest run src/components/vehicles --coverage
# Lint
npx eslint src/ --max-warnings 0
npx prettier --check src/
# Build
npm run build
# Type check
npx tsc --noEmit
```
### Docker
```bash
# Build and start all services
docker-compose up -d --build
# View logs
docker-compose logs -f backend
docker-compose logs -f frontend
# Run database migration in container
docker-compose exec backend alembic upgrade head
# Run tests in container
docker-compose exec backend python -m pytest tests/ -v
```
---
## 3. Test Rules (MANDATORY)
### TDD Principle
- **Write tests first or alongside implementation** never after
- Every PR must include tests
- Coverage target: **≥80%** per module
### Do NOT Modify Existing Tests
- Tests are written by the task spec and must not be modified to make them pass
- If a test fails, fix the **implementation**, not the test
- Exception: test data fixtures can be extended, but existing assertions must not be weakened
### Backend Test Conventions
- **Framework:** pytest + pytest-asyncio + httpx.AsyncClient
- **Test DB:** Separate PostgreSQL database `erp_test` (NOT SQLite)
- **Fixtures:** `conftest.py` provides:
- `async_db` async SQLAlchemy session (rolled back after each test)
- `client` httpx AsyncClient with app
- `auth_headers` JWT headers for admin/verkaeufer/buchhaltung roles
- `seed_data` minimal seed data (users, vehicle, contact)
- **Mocking:** OpenRouter API MUST be mocked in all tests (no real API calls)
- **Naming:** `test_<feature>_<scenario>.py` or `test_<module>.py`
### Frontend Test Conventions
- **Framework:** Vitest + React Testing Library
- **E2E:** Playwright for critical paths (login, vehicle create, sale wizard)
- **Mocking:** API calls mocked via `vi.mock()` or MSW (Mock Service Worker)
- **Naming:** `ComponentName.test.tsx` in `__tests__/` folder
### Test Spec Compliance
Every task in `task_graph.json` has a `test_spec` with:
- `commands` Exact commands to run
- `expected_results` What success looks like
- `test_files` Expected test file paths
- `coverage_target` Minimum coverage percentage
**ALL commands in test_spec.commands MUST pass before a task is considered done.**
---
## 4. Code Conventions
### Python (Backend)
```python
# Naming
snake_case for variables, functions, methods, modules
PascalCase for classes (Models, Schemas, Services)
UPPER_SNAKE_CASE for constants
# Imports (ruff isort)
# Standard library
import os
from datetime import datetime
# Third party
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
# Local
from app.deps import get_db, get_current_user
from app.models.vehicle import Vehicle
from app.schemas.vehicle import VehicleCreate, VehicleResponse
# Type hints (mandatory)
async def get_vehicle(vehicle_id: UUID, db: AsyncSession) -> Vehicle:
...
# Async everywhere (SQLAlchemy 2.0 async)
result = await db.execute(select(Vehicle).where(Vehicle.id == vehicle_id))
vehicle = result.scalar_one_or_none()
# Error handling
if vehicle is None:
raise HTTPException(status_code=404, detail={"error": {"code": "NOT_FOUND", "message": "Vehicle not found"}})
```
### TypeScript (Frontend)
```typescript
// Naming
camelCase for variables, functions, hooks
PascalCase for components, types, interfaces
UPPER_SNAKE_CASE for constants
// Imports order
// 1. React/Next
import { useState, useEffect } from 'react';
import { useRouter } from 'next/navigation';
// 2. Third party
import { useTranslations } from 'next-intl';
// 3. Local
import { Button } from '@/components/ui/Button';
import { apiClient } from '@/lib/api-client';
// Components: function declaration with explicit return type
export function FahrzeugListe(): JSX.Element {
...
}
// Types: interface for props
type FahrzeugListeProps = {
vehicles: Vehicle[];
isLoading: boolean;
};
```
### File Organization
- **One model per file** in `models/`
- **One router per module** in `routers/`
- **One service per domain** in `services/`
- **One schema set per module** in `schemas/`
- **Components grouped by feature** in `components/<feature>/`
---
## 5. Forbidden Patterns
### Backend
-**No raw SQL queries** always use SQLAlchemy ORM
-**No synchronous database calls** always async (`await db.execute(...)`)
-**No secrets in code** use environment variables / pydantic-settings
-**No `print()` in production code** use Python `logging`
-**No bare `except:`** always catch specific exceptions
-**No `# type: ignore`** without comment explaining why
-**No circular imports** use dependency injection
-**No business logic in routers** routers only validate + call service
-**No direct model access from routers** always go through service layer
-**No unencrypted PII** Ausweisdaten MUST be AES-256 encrypted
-**No real OpenRouter API calls in tests** always mock
-**No `commit()` in services without explicit reason** let caller control transactions
### Frontend
-**No `any` type** use proper TypeScript types
-**No `console.log` in production** use a logger utility
-**No inline styles** use Tailwind classes or CSS modules
-**No direct `fetch()` without auth wrapper** use `apiClient`
-**No hardcoded API URLs** use `NEXT_PUBLIC_API_URL` env var
-**No hardcoded translation strings** use `t('key')` from next-intl
-**No prop drilling > 2 levels** use context or state management
-**No `useEffect` for data fetching without loading/error states**
-**No forms without validation** all inputs must have validation
-**No missing `aria-label`** on icon-only buttons
### General
-**No commits to main without passing tests**
-**No large PRs** max 800 lines per task
-**No TODO comments without ticket reference**
-**No commented-out code in PRs**
-**No files > 500 lines** split into modules
---
## 6. Token Rule for File Operations
When working with files (reading, creating, updating):
### Backend (Forgejo API)
```json
// READ file (only for MODIFY, not for reference)
{"tool_name":"forgejo","tool_args":{"action":"files_get","owner":"Leopoldadmin","repo":"erp-nutzfahrzeuge","path":"backend/app/models/vehicle.py"}}
// CREATE new file
{"tool_name":"forgejo","tool_args":{"action":"files_create","owner":"Leopoldadmin","repo":"erp-nutzfahrzeuge","path":"backend/app/models/vehicle.py","content":"<content>","message":"Add vehicle model","branch":"main"}}
// UPDATE existing file (requires SHA from files_get)
{"tool_name":"forgejo","tool_args":{"action":"files_update","owner":"Leopoldadmin","repo":"erp-nutzfahrzeuge","path":"backend/app/models/vehicle.py","content":"<updated content>","sha":"<SHA>","message":"Update vehicle model","branch":"main"}}
// CHECK file existence
{"tool_name":"forgejo","tool_args":{"action":"files_list","owner":"Leopoldadmin","repo":"erp-nutzfahrzeuge","path":"backend/app/models/"}}
```
### Rules
- Use `files_get` ONLY when you need to modify an existing file (need SHA)
- Use `files_create` for new files
- Use `files_list` to check existence before creating
- **NEVER** copy file contents inline in task descriptions reference by path
- **NEVER** read entire files just for reference use `curl + sed` for snippets
- For large reference content (architecture.md, requirements.md): reference by path, don't inline
---
## 7. Task Execution Workflow
### For implementation_engineer
1. **Read the task** from `task_graph.json` (task ID: T0X)
2. **Read architecture.md** sections relevant to the task (DB schema, API design, ADRs)
3. **Read requirements.md** for the specific feature IDs listed in `requirement_ids`
4. **Implement backend first:** Model → Schema → Service → Router → Tests
5. **Implement frontend:** Components → Pages → Tests
6. **Run ALL test_spec.commands** and ensure they pass
7. **Run lint** (ruff for backend, eslint for frontend)
8. **Run build** (npm run build for frontend)
9. **Report results** with test output evidence (not just "done")
### Evidence Requirements (MANDATORY)
- Test command output showing pass/fail counts
- Coverage report showing ≥80%
- Lint output showing 0 errors
- Build output showing success
- "File written" is NOT evidence. "Commit made" is NOT evidence.
- Test output with pass counts IS evidence.
### Task Completion Checklist
- [ ] All acceptance_criteria from task_graph.json verified
- [ ] All test_spec.commands pass
- [ ] Coverage ≥ coverage_target for covered modules
- [ ] Ruff lint clean (backend)
- [ ] ESLint clean (frontend)
- [ ] Build succeeds (frontend)
- [ ] No forbidden patterns introduced
- [ ] Test evidence provided in report
---
## 8. Environment Variables (Names Only)
### Backend
```
DATABASE_URL=postgresql+asyncpg://erp_user:${DB_PASSWORD}@postgres:5432/erp_db
REDIS_URL=redis://redis:6379/0
JWT_SECRET=${JWT_SECRET}
JWT_ALGORITHM=HS256
JWT_ACCESS_TTL_MINUTES=15
JWT_REFRESH_TTL_DAYS=7
ENCRYPTION_KEY=${ENCRYPTION_KEY}
OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
MOBILE_DE_API_KEY=${MOBILE_DE_API_KEY}
MOBILE_DE_SELLER_ID=${MOBILE_DE_SELLER_ID}
UPLOAD_DIR=/data/uploads
MAX_FILE_SIZE_MB=50
CORS_ORIGINS=https://erp.domain.tld
```
### Frontend
```
NEXT_PUBLIC_API_URL=http://backend:8000/api/v1
NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
```
---
## 9. Git Workflow
### Branches
- `main` Production branch, deployed via Coolify
- `feature/T0X-<short-name>` Feature branch per task
- `fix/T0X-<short-name>` Fix branch
### Commit Messages
```
feat(T02): add vehicle CRUD with type-specific fields
fix(T02): correct FIN validation for 17-char check
test(T02): add mobile.de batch push tests
refactor(T02): extract field mapping to separate function
docs(T02): update vehicle API documentation
```
### PR Process
1. Create feature branch from main
2. Implement + test + lint + build
3. Commit with conventional commit format
4. Push branch
5. Create PR with task ID reference
6. CI runs: pytest, ruff, vitest, eslint, build
7. Merge after CI passes
---
## 10. Database Migration Rules
- **Always use Alembic** for schema changes
- **Never** modify database directly (psql, pgadmin)
- **One migration per schema change** don't bundle multiple changes
- **Test migration up AND down** before committing
- **Seed data:** Use a separate seed script (`scripts/seed.py`), not migrations
- **Cleanup after migration tasks:**
```bash
alembic upgrade head # Apply
# Verify all tables created
python -c "from app.database import engine; from sqlalchemy import inspect; insp = inspect(engine); print(insp.get_table_names())"
# Run seed data
python scripts/seed.py
# Verify foreign keys
docker-compose exec postgres psql -U erp_user -d erp_db -c "\d vehicles"
```
---
## 11. OpenRouter Integration Rules
- **Always use** `openrouter_client.py` shared client never call OpenRouter API directly
- **Always set timeout** to 30 seconds
- **Always log** interaction to `ai_interactions` table (type, model, tokens, metadata)
- **Never send** Ausweisdaten or personal customer data to OpenRouter
- **Mock OpenRouter** in all automated tests
- **Handle errors gracefully:** API error → 502, timeout → 504, rate limit → 429
- **Model selection** via config, not hardcoded in service
---
## 12. Security Checklist
- [ ] JWT authentication on all endpoints (except /auth/login, /auth/refresh, /health)
- [ ] RBAC enforced (require_role decorator on every router)
- [ ] Input validation via Pydantic schemas on all endpoints
- [ ] SQL injection protection via SQLAlchemy ORM (no raw SQL)
- [ ] File upload: MIME-type allowlist + size limit
- [ ] Ausweisdaten: AES-256-GCM encryption
- [ ] CORS: only configured origins
- [ ] Audit log: all create/update/delete actions logged
- [ ] No secrets in code or git
- [ ] HTTPS via Traefik/Let's Encrypt
- [ ] DSGVO: no PII to OpenRouter, soft-delete with retention concept
+1156
View File
File diff suppressed because it is too large Load Diff
+95
View File
@@ -0,0 +1,95 @@
# Component Inventory - ERP Nutzfahrzeuge
## Version: 1.0
## Datum: 2026-07-10
## Shared Components
| Component | Description | States |
|---|---|---|
| **Icon** | SVG-Icon-System mit 50+ Icons | default |
| **Badge** | Status-Badge (success, warning, error, info, neutral, primary) | default |
| **Button** | Button-Komponente (primary, secondary, ghost, danger) mit Größen (sm, md, lg) | default, hover, focus, active, disabled |
| **Card** | Container-Karte mit Header und Body | default |
| **KpiCard** | KPI-Kennzahlen-Karte mit Label, Value, Trend, Icon | default, hover |
| **Spinner** | Lade-Indikator (sm, lg) | loading |
| **EmptyState** | Leer-Zustand mit Icon, Titel, Text, CTA-Button | empty |
| **ErrorState** | Fehler-Zustand mit Icon, Titel, Text, Retry-Button | error |
| **Avatar** | Nutzer-Avatar mit Initialen (md, lg) | default |
| **Toast** | Toast-Notification (success, error, warning, info) mit Auto-Dismiss | default, exit-animation |
| **ToastContainer** | Container für Toast-Stack | - |
## Layout Components
| Component | Description | States |
|---|---|---|
| **App** | Root-Komponente mit Router, Sidebar, Topbar, Content | - |
| **Sidebar** | Navigation-Sidebar mit Sektionen, Rollen-Filter, User-Profil | active, hover, collapsed (mobile) |
| **Topbar** | Header mit Breadcrumb, Sprachumschalter, Dark-Mode-Toggle, Profil-Menü | default, dropdown-open |
| **useRouter** | Hash-based SPA Router | - |
## View Components
| Component | View | Description | States |
|---|---|---|---|
| **DashboardView** | #/dashboard | KPIs, Schnellzugriffe, Letzte Aktivitäten, Bevorstehende Tasks, Top-Fahrzeuge | loading, error, happy |
| **FahrzeugListe** | #/fahrzeuge | Filterbare/sortierbare Tabelle mit Suche, Pagination, mobile.de-Status | loading, error, empty, happy, partial |
| **FahrzeugDetail** | #/fahrzeug/:id | Detailansicht mit 7 Tabs (Allgemein, Technisch, Baumaschinen, Verkaufsinfo, Dokumente, Fotos, mobile.de) | loading, error, not-found, happy |
| **FahrzeugForm** | #/fahrzeuge/neu | Formular mit OCR-Upload, Standarddaten, Baumaschinen-Feldern, LKW-Feldern, Validierung | default, ocr-loading, saving, validation-error |
| **KontakteListe** | #/kontakte | Kontakt-Liste mit Filter (Rolle, Land), USt-IdNr.-Status, Sortierung | loading, error, empty, happy |
| **VerkaufsModul** | #/verkauf | 4-Step Wizard: Fahrzeug/Kunde → Vertragsdetails → Prüfung → Abschluss | step-1, step-2, step-3, step-4, generating, gwg-warning, ust-warning |
| **KiCopilot** | #/ki-copilot | Chat-Interface mit Beispiel-Befehlen, Spracheingabe-Button, KI-Funktionen-Übersicht | default, loading, listening |
| **Einstellungen** | #/einstellungen | 4 Tabs: Stammdaten, Vertragsvorlagen, Benutzerverwaltung, Firmenprofil | default |
## Form Components
| Component | Description | States |
|---|---|---|
| **Input** | Text-Eingabefeld | default, focus, disabled, error |
| **Select** | Dropdown-Auswahl | default, focus, disabled |
| **Textarea** | Mehrzeilige Text-Eingabe | default, focus, disabled |
| **Label** | Form-Label mit Required-Indikator | default |
| **Dropzone** | Drag-and-Drop Upload-Zone | default, hover, dragover, loading |
## Interactive Components
| Component | Description | States |
|---|---|---|
| **Tabs** | Tab-Navigation mit aktiven/inaktiven Tabs | default, active, hover |
| **Pagination** | Seiten-Navigation mit Info und Controls | default, active, disabled |
| **Toggle** | Switch-Toggle (z.B. Dark Mode) | default, active |
| **ProgressBar** | Fortschrittsbalken | default |
| **WarningBanner** | Warnungs-Banner mit Icon und Text | default |
## Data Display Components
| Component | Description | States |
|---|---|---|
| **DataTable** | Sortierbare Tabelle mit Header, Zeilen, Hover-Effekt | default, sorted-asc, sorted-desc, hover |
| **InfoGrid** | Grid mit Info-Items (Label + Value) | default |
| **ImageGrid** | Grid für Foto-Galerie | default, hover |
## i18n System
| Component | Description |
|---|---|
| **translations** | DE + EN Übersetzungs-Objekt (250+ Keys) |
| **t(key)** | Übersetzungs-Funktion mit Fallback |
| **setLang(lang)** | Sprache setzen und anwenden |
| **applyTranslations()** | Alle data-i18n Attribute aktualisieren |
## Rollen-System
| Rolle | Berechtigungen |
|---|---|
| **Admin** | Vollzugriff auf alle Module |
| **Verkäufer** | Fahrzeugbestand, mobile.de, Kontakte, Verkauf, KI-Copilot, Bildretusche |
| **Buchhaltung** | Lesen: Fahrzeuge/Kontakte/Verkauf, Schreiben: USt-IdNr./GwG/Rechnungen/DATEV |
## Accessibility Features
- Skip-Link zum Hauptinhalt
- ARIA-Labels für Icon-Buttons
- :focus-visible Outline
- Semantisches HTML (nav, main, header, aside, table)
- Keyboard-Navigation unterstützt
- Kontrast-Level WCAG 2.1 AA
+88
View File
@@ -0,0 +1,88 @@
# Offene Fragen ERP Nutzfahrzeug-/Baumaschinen-Handel
**Stand:** 2026-07-10
**Status:** Discovery abgeschlossen alle Fragen geklärt
---
## 1. Tech-Stack
1. **Backend-Präferenz?** ✅ RESOLVED → Python / FastAPI
2. **Frontend-Präferenz?** ✅ RESOLVED → React / Next.js (TypeScript)
3. **Datenbank?** ✅ RESOLVED → PostgreSQL 16+
4. **PDF-Generierung?** ✅ RESOLVED → Server-seitig (WeasyPrint oder ReportLab)
## 2. Hosting & Deployment
5. **Hosting?** ✅ RESOLVED → Coolify self-hosted (Docker) auf eigenem Server
6. **CI/CD?** ✅ RESOLVED → Forgejo Actions → Docker Build → Coolify Deploy. Dev + Prod Environments.
7. **Domain?** ⏳ SPÄTER → Zu klären mit Coolify (vorhandene Domain nutzbar)
## 3. mobile.de Integration
8. **mobile.de Händleraccount vorhanden?** ✅ RESOLVED → JA, aktiver Account mit Seller API-Zugang
9. **API-Zugangsdaten?** ✅ RESOLVED → JA, vorhanden (Customer-ID und API-Key)
10. **Sync-Richtung?** ✅ RESOLVED → Push-Only (ERP → mobile.de), NICHT bidirektional
11. **Sync-Frequenz?** ✅ RESOLVED → Manuell im MVP, ggf. Cron-Job später
12. **Nutzfahrzeug-spezifische Felder?** ✅ RESOLVED → JA: LKW-Typ, Art (Bagger, Radlader, etc.), Aufbau, Betriebsstunden statt KM, Leistung kW/PS
## 4. OCR (Zulassungsbescheinigung)
13. **OCR-Anbieter-Präferenz?** ✅ RESOLVED → OpenRouter Vision-Modell (Qwen2.5-VL), keine Spezial-API
14. **Budget für OCR-API?** ✅ RESOLVED → OpenRouter pay-per-use, Budget vorhanden
15. **Datenschutz?** ✅ RESOLVED → DSGVO: Cloud-Verarbeitung via OpenRouter akzeptiert
16. **Offline-OCR?** ✅ RESOLVED → Nein, Internetverbindung erforderlich (Cloud)
## 5. Rechtsdokumente & Compliance
17. **Rechtsberatung?** ✅ RESOLVED → System generiert Verträge aus Vorlagen. Stammdaten in Einstellungen hinterlegt.
18. **GwG-Schwellwert?** ✅ RESOLVED → 10.000 € Barzahlung (gesetzlicher Standard)
19. **Ausweisdaten-Speicherung?** ✅ RESOLVED → JA, DSGVO-konform verschlüsselt (AES-256), Zugriff nur Admin + Buchhaltung
20. **BZSt-API-Zugang?** ✅ RESOLVED → NEIN, kein Zugang vorhanden. Manuelle Prüfung im MVP, API-Integration später.
21. **Steuerberater-Einbindung?** ✅ RESOLVED → JA, DATEV-Export (CSV) eingeplant
## 6. KI-Copilot
22. **Sprachsteuerung?** ✅ RESOLVED → Beides: Spracheingabe (Microphone) + Text-Eingabe
23. **LLM-Präferenz?** ✅ RESOLVED → OpenRouter (flexibel, z.B. Claude/GPT-4 über OpenRouter)
24. **KI-Kosten?** ✅ RESOLVED → OpenRouter pay-per-use, Budget vorhanden
25. **KI-Autonomie?** ✅ RESOLVED → KI darf selbstständig handeln (nach User-Bestätigung)
## 7. KI Bildretusche
26. **Bildretusche-Anbieter?** ✅ RESOLVED → OpenRouter Flux.1-Pro (Hintergrund entfernen, Spiegelungen retuschieren)
27. **Batch-Größe?** ✅ RESOLVED → Batch-Verarbeitung für mehrere Fotos pro Fahrzeug
28. **Kosten pro Bild?** ✅ RESOLVED → OpenRouter pay-per-use, Budget vorhanden
## 8. Budget & Zeitrahmen
29. **Budget?** ✅ RESOLVED → OpenRouter pay-per-use + mobile.de Händleraccount. Keine harten Budget-Limits.
30. **Zeitrahmen?** ✅ RESOLVED → MVP-Phasen sequenziell, keine harten Deadlines
31. **MVP-Prioritäten?** ✅ RESOLVED → Logischer Ablauf: Bestand → Kontakte → Verkauf etc. (7 Phasen definiert)
## 9. Nutzer & Rollen
32. **Anzahl Nutzer?** ✅ RESOLVED → ~10 Nutzer
33. **Rollen?** ✅ RESOLVED → 3 Rollen: Admin, Verkäufer, Buchhaltung (Berechtigungs-Matrix definiert)
34. **Auth-Methode?** ✅ RESOLVED → JWT-basiert (Benutzername/Passwort), Session-Timeout 30min
## 10. Datenmigration
35. **Bestehende Daten?** ✅ RESOLVED → Keine Datenmigration nötig
36. **Format?** ✅ RESOLVED → N/A (keine Bestandsdaten)
37. **Menge?** ✅ RESOLVED → N/A (keine Bestandsdaten)
## 11. Sonstiges
38. **Projektname?** ⏳ SPÄTER → Zu klären mit User (Branding)
39. **Sprachen?** ✅ RESOLVED → Deutsch + Englisch (beides, i18n von Anfang an)
40. **Mobile App später?** ✅ RESOLVED → Nein, nur responsive Web-UI (kein native App)
---
## Zusammenfassung
- **Resolved:** 38/40
- **Später:** 2/40 (Domain, Projektname/Branding)
- **Unresolved:** 0/40
- **Blockiert für Architecture:** Nein (keine kritischen offenen Fragen)
+703
View File
@@ -0,0 +1,703 @@
# ERP-System für Nutzfahrzeug-/Baumaschinen-Handel
## Requirements Specification
**Version:** 1.0.0
**Datum:** 2026-07-10
**Status:** Final Discovery abgeschlossen
**Analyst:** Requirements Analyst (A0)
---
## 1. Projektüberblick
Ein webbasiertes ERP-System für Händler von Nutzfahrzeugen und Baumaschinen (~10 Nutzer). Das System verwaltet Fahrzeugbestände mit Baumaschinen-spezifischen Feldern, Kundenkontakte, Verkaufsprozesse mit Rechtsdokumenten, OCR-basierte Datenerfassung via Vision-Modellen, mobile.de Push-Sync, KI-Copilot (Text + Sprache) und KI-Bildretusche. Alle KI-Funktionen laufen über OpenRouter.
### Domain Knowledge
- **Domain:** Nutzfahrzeug- und Baumaschinenhandel (B2B/B2C)
- **Zulassungsdokumente:** Zulassungsbescheinigung Teil I (ZB I = Fahrzeugschein) und Teil II (ZB II = Fahrzeugbrief), EU-einheitlich seit 2005
- **mobile.de:** Deutschlands größter Fahrzeugmarkt mit Seller API (REST) für Händler
- **Baumaschinen-spezifisch:** LKW-Typ, Art (Bagger, Radlader, etc.), Aufbau, Betriebsstunden statt Kilometerstand, Leistung in kW/PS
- **USt-IdNr.-Prüfung:** Gesetzlich vorgeschrieben bei innergemeinschaftlichen Lieferungen (§ 6a UStG, § 27a UStG). BZSt eVatR API-Zugang aktuell NICHT vorhanden manuelle Prüfung im MVP
- **Geldwäsche-Prävention:** Identifikation, Meldepflichten bei Barzahlungen > 10.000 € (GwG)
- **Kaufverträge/Rechnungen:** Unterscheidung EU-Inland, EU-Ausland, Dritland
- **DATEV:** Standard-Exportformat für Steuerberater-Übergabe
---
## 2. Tech-Stack Entscheidungen
| Komponente | Entscheidung | Begründung |
|---|---|---|
| **Backend** | Python / FastAPI | Async, OpenAPI-Auto-Docs, Python-Ökosystem für KI/OCR-Integration, schnell zu entwickeln |
| **Frontend** | React / Next.js | SSR, große Community, TypeScript-Support, Component-Ökosystem |
| **Datenbank** | PostgreSQL | Relational, robust, JSON-Support für flexible Felder, Full-Text-Search |
| **Hosting** | Coolify self-hosted (Docker) | Bereits vorhanden, keine Cloud-Abhängigkeit, volle Kontrolle |
| **OCR** | OpenRouter Vision-Modell (Qwen2.5-VL) | Keine Spezial-API nötig, DSGVO: Cloud-Verarbeitung akzeptiert |
| **KI-Copilot** | OpenRouter (bestes Modell, z.B. Claude/GPT-4) | Flexibel, kein Vendor-Lock-in, KI darf selbstständig handeln |
| **KI-Bildretusche** | OpenRouter Flux.1-Pro | Hintergrund entfernen, Spiegelungen retuschieren |
| **KI-Vision/Analyse** | OpenRouter Qwen2.5-VL | Fahrzeugfoto-Analyse, ZB I/II Felderkennung |
| **i18n** | Deutsch + Englisch | Beide Sprachen von Anfang an |
| **DATEV-Export** | CSV/DATEV-kompatibel | Für Steuerberater-Übergabe |
### Coding Guidelines
- **Naming:** snake_case (Python), camelCase (JS/TS), PascalCase (React Components)
- **Struktur:** Modulare Feature-Ordner, Shared-Libraries für Common-Logic
- **Linting:** ruff (Python), eslint + prettier (TypeScript)
- **Tests:** pytest (Backend), jest/playwright (Frontend), Coverage-Target ≥80%
- **API-Stil:** RESTful, OpenAPI 3.0, versionierte Endpoints (/api/v1/...)
### Deployment
- **Environments:** Dev + Prod via Coolify (Docker Compose)
- **CI/CD:** Forgejo Actions → Docker Build → Coolify Deploy
- **Secrets:** Umgebungsvariablen in Coolify, niemals im Code
---
## 3. Module und Features
### Modul M1: Fahrzeugbestand + mobile.de Push-Sync
#### [F-M1-01] Fahrzeugbestand verwalten
**Anforderung:** Vollständige Verwaltung des Fahrzeugbestands mit Standard- und Baumaschinen-spezifischen Feldern. Standardfelder: Marke, Modell/Typ, FIN (Fahrgestellnummer), Baujahr, Erstzulassung, Leistung (kW/PS), Kraftstoffart, Getriebe, Farbe, Zustand, Standort, Verfügbarkeit, Preis. Baumaschinen-spezifische Felder: LKW-Typ, Art (Bagger, Radlader, Kran, etc.), Aufbau, Betriebsstunden (statt KM-Stand), Betriebsstunden-Einheit (h/min).
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** Neues Baumaschinen-Fahrzeug wird mit allen Pflichtfeldern inkl. Betriebsstunden angelegt. Erwartet: Fahrzeug erscheint in Bestandsliste, FIN validiert (17 Zeichen), Betriebsstunden-Feld sichtbar, alle Felder gespeichert.
2. **Edge Case:** Fahrzeug mit doppelter FIN wird angelegt. Erwartet: Fehlermeldung „FIN bereits vorhanden“, kein Speichern.
3. **Integration:** Fahrzeug wird angelegt und danach über die Suchfunktion gefunden. Erwartet: Fahrzeug erscheint in Suchergebnissen mit allen eingegebenen Daten inkl. Baumaschinen-Felder.
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- Fahrzeugbestand-Seite lädt, Formular mit Baumaschinen-Feldern, Bestandsliste zeigt alle Fahrzeuge
---
#### [F-M1-02] mobile.de Push-Sync (nur Push)
**Anforderung:** Fahrzeuge aus dem Bestand können auf mobile.de gelistet/aktualisiert werden. Über die mobile.de Seller API (REST) werden Fahrzeugdaten übertragen (Push-Only, NICHT bidirektional). Feldmapping zwischen ERP-Feldern und mobile.de-Feldern. Sync-Status wird pro Fahrzeug angezeigt (Entwurf → gelistet → aktualisiert → verkauft/entfernt). Preisänderungen im ERP können gepusht werden.
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** Fahrzeug mit vollständigen Daten wird an mobile.de gesendet. Erwartet: API-Response 200/201, Fahrzeug erscheint auf mobile.de, Status im ERP wird auf „gelistet" aktualisiert.
2. **Edge Case:** Fahrzeug mit unvollständigen mobile.de-Pflichtfeldern wird gesendet. Erwartet: API-Fehlermeldung wird im ERP angezeigt, Fahrzeug wird nicht gelistet, Status bleibt „Entwurf".
3. **Integration:** Fahrzeug wird auf mobile.de gelistet, dann im ERP Preis geändert, Push-Sync erneut ausgeführt. Erwartet: Preis auf mobile.de aktualisiert, Sync-Status „aktualisiert".
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- API-Verbindung zu mobile.de konfigurierbar, Push-Listing funktioniert, Status-Tracking korrekt
---
#### [F-M1-03] mobile.de Listing-Verwaltung
**Anforderung:** Übersicht aller Fahrzeuge mit mobile.de-Status. Filterung nach Status (nicht gelistet, gelistet, Fehler). Batch-Push für mehrere Fahrzeuge. Fehler-Logs pro Fahrzeug einsehbar. Entfernen von Listings auf mobile.de (Delisting).
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** 3 Fahrzeuge werden per Batch-Push an mobile.de gesendet. Erwartet: Alle 3 erhalten Status „gelistet", Fortschrittsanzeige während Sync.
2. **Edge Case:** Ein Fahrzeug von 3 hat fehlende Pflichtfelder. Erwartet: 2 erfolgreich gepusht, 1 mit Fehlermeldung, Fehler-Log einsehbar.
3. **Integration:** Fahrzeug wird auf mobile.de delisted. Erwartet: Listing auf mobile.de entfernt, Status im ERP „entfernt".
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- Batch-Push, Status-Übersicht, Fehler-Logs und Delisting funktionieren
---
### Modul M2: OCR Fahrzeugdaten-Erfassung
#### [F-M2-01] OCR-Erfassung Zulassungsbescheinigung Teil I (ZB I / Fahrzeugschein)
**Anforderung:** Foto des Fahrzeugscheins (ZB I) hochladen, OpenRouter Vision-Modell (Qwen2.5-VL) erkennt Felder automatisch (Felder AK gemäß KBA-Standard): Marke, Modell, FIN, Erstzulassung, Kennzeichen, Leistung, Hubraum, Kraftstoffart, Schadstoffklasse, etc. Daten werden in Fahrzeugformular eingetragen, User kann korrigieren.
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** Gut belichtetes Foto einer ZB I wird hochgeladen. Erwartet: Qwen2.5-VL erkennt alle relevanten Felder mit >90% Genauigkeit, Daten werden in Fahrzeugformular eingetragen, User kann korrigieren.
2. **Edge Case:** Verschmutztes oder schiefes Foto wird hochgeladen. Erwartet: System gibt Warnung „Bildqualität niedrig", best-effort Erkennung, alle Felder editierbar.
3. **Integration:** OCR-Daten werden ins Fahrzeugformular eingetragen, User speichert. Erwartet: Fahrzeug wird mit OCR-Daten angelegt, Felder korrekt übernommen.
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- Upload funktioniert, Qwen2.5-VL via OpenRouter erkennt Felder, Daten fließen in Formular
---
#### [F-M2-02] OCR-Erfassung Zulassungsbescheinigung Teil II (ZB II / Fahrzeugbrief)
**Anforderung:** Foto des Fahrzeugbriefs (ZB II) hochladen, OpenRouter Qwen2.5-VL erkennt Felder: FIN, Hersteller, Typ, Variante, Fahrzeugklasse, Antriebsart, Leistung, Hubraum, zul. Gesamtgewicht, Datum der Erstzulassung, Previous Owner, etc. Daten werden in Fahrzeugformular eingetragen.
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** Gut belichtetes Foto einer ZB II wird hochgeladen. Erwartet: Alle relevanten Felder werden erkannt, Daten ins Fahrzeugformular eingetragen.
2. **Edge Case:** ZB II mit Stempeln/Aufklebern die Teile des Textes verdecken. Erwartet: System warnt, best-effort Erkennung, manuelle Korrektur möglich.
3. **Integration:** ZB I und ZB II für dasselbe Fahrzeug hochgeladen. Erwartet: Daten werden zusammengeführt, FIN-Abgleich zwischen ZB I und ZB II, Warnung bei Diskrepanz.
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- Upload funktioniert, Qwen2.5-VL erkennt ZB II-Felder, Zusammenführung mit ZB I
---
### Modul M3: Kontakt- & Kundenverwaltung
#### [F-M3-01] Kontakt- und Kundenverwaltung
**Anforderung:** Verwaltung von Firmen und Ansprechpartnern. Felder: Firmenname, Rechtsform, Ansprechpartner (Name, Funktion), Adresse (Straße, PLZ, Ort, Land), USt-IdNr., Telefon, E-Mail, Website. Unterscheidung Käufer/Verkäufer. EU-International und Inland. ~10 Nutzer arbeiten mit den Daten.
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** Neue Firma mit Ansprechpartner wird angelegt als „Käufer". Erwartet: Firma erscheint in Kontaktliste mit Rolle „Käufer", alle Felder gespeichert.
2. **Edge Case:** Firma ohne USt-IdNr. (Privatkunde/Dritland) wird angelegt. Erwartet: USt-IdNr. optional, Firma wird gespeichert, Rolle „Käufer" zugewiesen.
3. **Integration:** Firma wird als Käufer angelegt, dann Verkauf an diese Firma gestartet. Erwartet: Firmendaten fließen in Kaufvertrag ein.
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- Kontaktliste zeigt Firmen mit Rollen, Anlegen/Editieren funktioniert
---
#### [F-M3-02] Kundensuche und Filter
**Anforderung:** Kunden/Kontakte können gesucht und gefiltert werden nach: Name, Land, Rolle (Käufer/Verkäufer), USt-IdNr.-Status (geprüft/ungeprüft/manuell bestätigt).
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** Suche nach „Müller" in Kontakten. Erwartet: Alle Kontakte mit „Müller" im Namen erscheinen.
2. **Edge Case:** Filter „Land = Deutschland" und „Rolle = Käufer" kombiniert. Erwartet: Nur deutsche Käufer-Firmen erscheinen.
3. **Integration:** Gefundener Kontakt wird für Verkauf ausgewählt. Erwartet: Kontakt wird in Verkaufsformular übernommen.
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- Such- und Filterfunktion liefert korrekte Ergebnisse
---
### Modul M4: Dateiablage pro Fahrzeug
#### [F-M4-01] Dateiablage pro Fahrzeug
**Anforderung:** Jedes Fahrzeug hat einen eigenen Dateibereich. Dokumente (PDF, Verträge, Rechnungen), Fotos (JPG/PNG) und andere Dateien können hochgeladen, kategorisiert, umbenannt und gelöscht werden. Dateivorschau für Bilder und PDFs. Max 50 MB pro Datei. Kategorien: Vertrag, Rechnung, ZB I, ZB II, Foto, Sonstiges.
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** PDF-Vertrag wird zu Fahrzeug hochgeladen. Erwartet: Datei erscheint in Fahrzeug-Dateibereich, Vorschau sichtbar, Datei kategorisierbar als „Vertrag".
2. **Edge Case:** Datei > 50 MB wird hochgeladen. Erwartet: Fehlermeldung „Datei zu groß", kein Upload.
3. **Integration:** Foto wird hochgeladen und für KI-Bildretusche (M8) verwendet. Erwartet: Foto ist in Dateiablage sichtbar und für Retusche-Modul auswählbar.
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- Datei-Upload, Vorschau, Kategorisierung und Löschung funktionieren
---
### Modul M5: Verkaufsmodul mit Rechtsdokumenten
#### [F-M5-01] Kaufvertrag erstellen
**Anforderung:** Verkauf eines Fahrzeugs an einen Kunden. Kaufvertrag wird aus Vertragsvorlagen generiert mit: Fahrzeugdaten, Käufer-/Verkäuferdaten, Preis, Zahlungsbedingungen, Übergabedatum. PDF-Generierung. Unterscheidung EU-Inland, EU-Ausland, Dritland. Vertragsvorlagen basieren auf Stammdaten aus Einstellungen.
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** Verkauf an deutsche Firma (EU-Inland). Erwartet: Kaufvertrag-PDF wird generiert mit allen Daten, USt ausgewiesen, Vertragsvorlage korrekt angewendet.
2. **Edge Case:** Verkauf an Schweizer Firma (Dritland). Erwartet: Kaufvertrag mit 0% USt (Ausfuhrlieferung), Hinweis auf Ausfuhr, andere Vorlage.
3. **Integration:** Verkauf startet USt-IdNr.-Prüfung für EU-Ausland-Kunde. Erwartet: Manuelle Prüfung wird angeboten, Ergebnis im Vertrag dokumentiert.
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- Kaufvertrag-PDF generiert, alle Varianten (Inland/EU/Dritland) korrekt, Vorlagen aus Stammdaten
---
#### [F-M5-02] USt-IdNr.-Überprüfung (manuell)
**Anforderung:** Manuelle USt-IdNr.-Prüfung im MVP (kein BZSt eVatR API-Zugang vorhanden). User gibt Prüfergebnis manuell ein (gültig/ungültig, Prüdatum, Art der Prüfung). Prüfergebnis wird dokumentiert. BZSt API-Integration als „später" markiert. System warnt bei EU-Ausland-Verkauf ohne gültige USt-IdNr.
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** User prüft USt-IdNr. manuell (extern) und trägt Ergebnis „gültig" ein. Erwartet: Prüfergebnis mit Datum gespeichert, Status „geprüft".
2. **Edge Case:** EU-Ausland-Verkauf ohne geprüfte USt-IdNr. Erwartet: Warnung „USt-IdNr. nicht geprüft", Verkauf kann erst nach Bestätigung fortgesetzt werden.
3. **Integration:** USt-IdNr.-Prüfung wird im Verkaufsprozess angefordert. Erwartet: Prüfung angefordert, User trägt Ergebnis ein, Vertrag wird fortgesetzt.
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- Manuelle Prüfung dokumentiert, Warnung bei fehlender Prüfung, BZSt API als „später" markiert
---
#### [F-M5-03] Geldwäsche-Prävention
**Anforderung:** Identifikation des Vertragspartners (Ausweisdaten: Typ, Nummer, Ausstellungsland). Meldepflicht bei Barzahlungen > 10.000 € (GwG). System warnt automatisch bei Überschreitung. Dokumentation der Identifikation und etwaiger Meldungen. Ausweisdaten werden DSGVO-konform gespeichert (verschlüsselt, Zugriffsbeschränkung auf Admin/Buchhaltung).
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** Verkauf mit Barzahlung 8.000 €. Erwartet: Keine Warnung, Identifikation wird erfasst aber keine Meldepflicht.
2. **Edge Case:** Verkauf mit Barzahlung 12.000 €. Erwartet: Warnung „Meldepflicht nach GwG", Meldedokument wird vorbereitet, Verkauf kann erst nach Bestätigung fortgesetzt werden.
3. **Integration:** Identifikation wird im Kaufvertrag dokumentiert. Erwartet: Ausweisdaten (Typ, Nummer, Ausstellungsland) im Vertrag enthalten, verschlüsselt in DB gespeichert.
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- GwG-Warngrenze funktioniert, Identifikation erfasst, DSGVO-konforme Speicherung
---
#### [F-M5-04] Rechnung und Lieferbescheinigung
**Anforderung:** Rechnung wird aus Verkauf generiert (PDF). Lieferbescheinigung für EU-Innergemeinschaftliche Lieferungen. Rechnungsnummer fortlaufend, steuerrechtlich korrekt für Inland/EU-Ausland/Dritland.
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** Rechnung für EU-Inland-Verkauf wird generiert. Erwartet: Rechnungs-PDF mit USt, fortlaufende Nummer, Lieferbescheinigung beiliegend.
2. **Edge Case:** Rechnung für Dritland-Verkauf (Ausfuhrlieferung). Erwartet: Rechnung mit 0% USt, Ausfuhrvermerke, Lieferbescheinigung.
3. **Integration:** Rechnung wird aus abgeschlossenem Kaufvertrag generiert. Erwartet: Alle Daten aus Vertrag übernommen, Nummer inkrementiert.
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- Rechnungs-PDF generiert, Nummerierung korrekt, alle Varianten
---
#### [F-M5-05] DATEV-Export
**Anforderung:** Export von Verkaufs- und Rechnungsdaten im DATEV-kompatiblen Format (CSV) für die Übergabe an den Steuerberater. Filterung nach Zeitraum. Export enthält: Belegnummer, Datum, Konto, Betrag, USt, Gegenkonto, Kundennummer.
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** DATEV-Export für Januar 2026 wird ausgeführt. Erwartet: CSV-Datei mit allen Verkäufen des Monats, DATEV-Format korrekt, herunterladbar.
2. **Edge Case:** Export für Zeitraum ohne Verkäufe. Erwartet: Leere CSV mit Headern, Hinweis „Keine Daten im Zeitraum".
3. **Integration:** DATEV-Export nach Verkaufsabschluss. Erwartet: Verkauf erscheint im nächsten Export, Beträge korrekt.
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- DATEV-CSV korrekt formatiert, Filterung funktioniert, Download verfügbar
---
#### [F-M5-06] Vertragsvorlagen-Verwaltung (Stammdaten)
**Anforderung:** Vertragsvorlagen werden in den Einstellungen als Stammdaten hinterlegt. Pro Vertragsart (Kaufvertrag Inland, EU-Ausland, Dritland, Rechnung, Lieferbescheinigung) wird eine Vorlage definiert. Variablen werden markiert ({{fahrzeug}}, {{kaeufer}}, {{preis}}, etc.). Admin kann Vorlagen editieren.
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** Admin erstellt neue Vertragsvorlage für EU-Ausland-Verkauf. Erwartet: Vorlage wird gespeichert, Variablen markiert, bei Verkauf automatisch angewendet.
2. **Edge Case:** Vorlage ohne Variablen wird gespeichert. Erwartet: Warnung „Keine Variablen gefunden", Vorlage speicherbar aber nicht funktional.
3. **Integration:** Vorlage wird geändert, danach neuer Kaufvertrag erstellt. Erwartet: Neue Vorlage wird verwendet, Variablen korrekt ersetzt.
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- Vorlagen-Editor funktioniert, Variablen werden ersetzt, pro Vertragsart zuweisbar
---
### Modul M6: Responsive UI (Querschnitt)
#### [F-M6-01] Responsive Web-UI
**Anforderung:** Web-UI funktioniert auf Desktop (≥1280px), Tablet (7681279px) und Mobile (<768px). Alle Formulare sind touch-optimiert. Navigation passt sich an Bildschirmgröße an (Hamburger-Menü auf Mobile). Gilt für alle Module (Querschnitt).
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** Desktop-Ansicht bei 1920px Breite. Erwartet: Alle Navigationselemente sichtbar, Formulare nebeneinander angeordnet.
2. **Edge Case:** Mobile-Ansicht bei 375px Breite. Erwartet: Hamburger-Menü, Formulare untereinander, Buttons groß genug für Touch.
3. **Integration:** Fahrzeugformular auf Mobile ausgefüllt. Erwartet: Alle Felder zugänglich, OCR-Upload funktioniert, Speichern funktioniert.
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- UI funktioniert auf allen 3 Breakpoints
---
#### [F-M6-02] Internationalisierung (i18n: Deutsch + Englisch)
**Anforderung:** Alle UI-Texte, Labels, Fehlermeldungen und Systemmeldungen sind auf Deutsch und Englisch verfügbar. Sprache kann pro User eingestellt werden. Default: Deutsch. Datums-/Zahlenformate passen sich an Sprache an.
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** User stellt Sprache auf Englisch um. Erwartet: Alle UI-Texte auf Englisch, Datumsformat MM/DD/YYYY, Dezimaltrennzeichen Punkt.
2. **Edge Case:** Fehlende Übersetzung für ein Label. Erwartet: Fallback auf Deutsch, Warnung im Dev-Log.
3. **Integration:** Sprache wird umgestellt, neues Fahrzeug angelegt. Erwartet: Formular-Labels auf Englisch, Daten werden sprachunabhängig gespeichert.
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- DE + EN vollständig, Sprache pro User einstellbar, Formate passen sich an
---
### Modul M7: KI-Copilot
#### [F-M7-01] KI-Copilot Fahrzeug anlegen
**Anforderung:** KI-Assistent versteht natürlichsprachliche Befehle (Text/Sprache). Beispiel: „Lege ein neues Fahrzeug an: Mercedes Actros, Baujahr 2019, 150.000 km, 45.000 Euro". KI extrahiert Daten und füllt Fahrzeugformular aus. KI darf selbstständig handeln (Auto-Save nach Bestätigung). LLM via OpenRouter.
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** „Neues Fahrzeug: MAN TGL 2018, 120000 km, 32000 €". Erwartet: Fahrzeugformular wird vorausgefüllt mit Marke=MAN, Modell=TGL, Baujahr=2018, KM=120000, Preis=32000. KI speichert nach Bestätigung.
2. **Edge Case:** „Füge ein Fahrzeug hinzu" ohne weitere Daten. Erwartet: KI fragt nach fehlenden Pflichtfeldern.
3. **Integration:** KI legt Fahrzeug an und listet es gleichzeitig auf mobile.de. Erwartet: Fahrzeug wird gespeichert und mobile.de Push-Sync wird ausgelöst.
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- KI versteht Befehl via OpenRouter, füllt Formular, speichert nach Bestätigung
---
#### [F-M7-02] KI-Copilot Suchen und Verkaufen
**Anforderung:** KI kann Fahrzeuge suchen („Finde alle LKW unter 30.000 €") und Verkaufsprozesse starten („Verkaufe Fahrzeug FIN XYZ123 an Müller GmbH"). KI darf selbstständig handeln führt Aktionen nach Bestätigung aus.
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** „Finde alle Fahrzeuge über 50.000 €". Erwartet: Gefilterte Bestandsliste mit Fahrzeugen > 50.000 €.
2. **Edge Case:** „Verkaufe Fahrzeug an Müller GmbH" ohne FIN. Erwartet: KI fragt nach Fahrzeug-Identifikation.
3. **Integration:** „Verkaufe FIN XYZ an Müller GmbH, Barzahlung 15000 €". Erwartet: Verkaufsprozess startet, GwG-Warnung erscheint (unter 10.000 → keine, über 10.000 → Warnung).
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- KI führt Suchen aus und startet Verkaufsprozesse nach Bestätigung
---
#### [F-M7-03] KI-Copilot Dokumente erstellen
**Anforderung:** KI kann Dokumente erstellen („Erstelle Kaufvertrag für Fahrzeug FIN XYZ an Müller GmbH"). KI sammelt alle nötigen Daten und generiert PDF. KI darf selbstständig handeln.
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** „Erstelle Rechnung für letzten Verkauf". Erwartet: Rechnungs-PDF wird generiert mit Daten aus letztem Verkauf.
2. **Edge Case:** „Erstelle Kaufvertrag" ohne Kunden- oder Fahrzeugdaten. Erwartet: KI fragt nach fehlenden Daten.
3. **Integration:** KI erstellt Kaufvertrag und löst USt-IdNr.-Prüfung aus. Erwartet: Vertrag generiert, manuelle Prüfung angefordert, Ergebnis im Vertrag.
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- KI generiert Dokumente korrekt via OpenRouter
---
#### [F-M7-04] KI-Copilot Spracheingabe
**Anforderung:** KI-Assistent akzeptiert Spracheingabe (Microphone-Input) zusätzlich zu Text. Sprache wird zu Text transkribiert (via OpenRouter oder Browser Web Speech API) und dann als Befehl verarbeitet.
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** User spricht „Lege neues Fahrzeug an: Volvo FH16, Baujahr 2020, 80000 Stunden, 55000 Euro". Erwartet: Sprache wird transkribiert, Fahrzeugformular vorausgefüllt.
2. **Edge Case:** Unverständliche Spracheingabe. Erwartet: KI fragt „Konnte Sie nicht verstehen, bitte wiederholen".
3. **Integration:** Spracheingabe wird genutzt um Verkauf zu starten. Erwartet: Transkribierter Text wird als Befehl verarbeitet, Verkaufsprozess startet.
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- Spracheingabe funktioniert, Transkription korrekt, Befehl wird ausgeführt
---
### Modul M8: KI Bildretusche & Preisvergleich
#### [F-M8-01] KI Bildretusche Hintergrund entfernen
**Anforderung:** Fahrzeugfoto hochladen, OpenRouter Flux.1-Pro entfernt Hintergrund und ersetzt durch neutralen (weiß/grau). Ergebnis kann gespeichert werden. Foto wird in Fahrzeug-Dateiablage abgelegt.
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** Foto eines LKW vor einer Werkstatt wird hochgeladen. Erwartet: Flux.1-Pro entfernt Hintergrund, neutraler Hintergrund, Fahrzeug klar erkennbar.
2. **Edge Case:** Foto mit sehr geringer Auflösung. Erwartet: Warnung „Niedrige Auflösung", best-effort Retusche, User kann Original behalten.
3. **Integration:** Retuschiertes Foto wird in Fahrzeug-Dateiablage gespeichert. Erwartet: Foto erscheint in Dateiablage, kann für mobile.de-Listing verwendet werden.
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- Bildretusche via Flux.1-Pro funktioniert, Ergebnis speicherbar
---
#### [F-M8-02] KI Bildretusche Spiegelungen entfernen
**Anforderung:** Spiegelungen und Reflexionen auf Fahrzeugoberfläche werden durch OpenRouter Flux.1-Pro reduziert/entfernt. Batch-Verarbeitung für mehrere Fotos eines Fahrzeugs.
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** Foto mit starker Spiegelung wird hochgeladen. Erwartet: Flux.1-Pro reduziert Spiegelung, Fahrzeugoberfläche klarer.
2. **Edge Case:** Foto ohne Spiegelungen. Erwartet: Foto bleibt unverändert, Hinweis „Keine Retusche nötig".
3. **Integration:** Batch von 5 Fotos wird verarbeitet. Erwartet: Alle 5 Fotos retuschiert, Fortschrittsanzeige, alle speicherbar.
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- Spiegelungs-Retusche und Batch-Verarbeitung via Flux.1-Pro funktionieren
---
#### [F-M8-03] Preisvergleich mobile.de für ähnliche Fahrzeuge
**Anforderung:** Für ein Fahrzeug im Bestand werden ähnliche Inserate auf mobile.de gesucht (gleiche Marke/Modell, ähnliches Baujahr/KM/Stunden). Vergleichspreise werden grafisch aufbereitet (Durchschnitt, Median, Preisverteilung). Read-Only (kein Import).
**Test Scenarios (Pflicht, mind. 2):**
1. **Happy Path:** Für Mercedes Actros 2019 werden mobile.de-Vergleichspreise abgerufen. Erwartet: Liste mit 5-20 vergleichbaren Fahrzeugen, Durchschnittspreis berechnet.
2. **Edge Case:** Sehr seltenes Fahrzeug, keine Vergleiche auf mobile.de. Erwartet: Hinweis „Keine Vergleichsfahrzeuge", Empfehlung basierend auf weiter gefassten Kriterien.
3. **Integration:** Preisvergleich wird angezeigt, User entscheidet Preisanpassung. Erwartet: Preis wird im ERP aktualisiert, optional mobile.de Push-Sync.
**Akzeptanzkriterium:**
- Build erfolgreich
- Alle 3 Tests grün
- Preisvergleich zeigt mobile.de-Daten, grafische Aufbereitung, Read-Only
---
## 4. Rollen & Berechtigungen
### Rollen-Definition
| Rolle | Beschreibung |
|---|---|
| **Admin** | Vollzugriff auf alle Module, Einstellungen, Vorlagen, Benutzer-Verwaltung |
| **Verkäufer** | Fahrzeugbestand, mobile.de Sync, Kontakte, Verkauf, Dateiablage, KI-Copilot, Bildretusche |
| **Buchhaltung** | Verkauf (Lesen), Rechnungen, DATEV-Export, Ausweisdaten (GwG), USt-IdNr.-Prüfung |
### Berechtigungs-Matrix
| Modul / Feature | Admin | Verkäufer | Buchhaltung |
|---|---|---|---|
| M1: Fahrzeugbestand (CRUD) | ✅ | ✅ | 📖 |
| M1: mobile.de Push-Sync | ✅ | ✅ | ❌ |
| M2: OCR Erfassung | ✅ | ✅ | ❌ |
| M3: Kontaktverwaltung (CRUD) | ✅ | ✅ | 📖 |
| M4: Dateiablage | ✅ | ✅ | 📖 |
| M5: Kaufvertrag erstellen | ✅ | ✅ | 📖 |
| M5: USt-IdNr.-Prüfung | ✅ | ❌ | ✅ |
| M5: Geldwäsche-Prävention | ✅ | 📖 | ✅ |
| M5: Rechnung & Lieferbescheinigung | ✅ | 📖 | ✅ |
| M5: DATEV-Export | ✅ | ❌ | ✅ |
| M5: Vertragsvorlagen-Verwaltung | ✅ | ❌ | ❌ |
| M6: Responsive UI | ✅ | ✅ | ✅ |
| M6: i18n (Sprache wählen) | ✅ | ✅ | ✅ |
| M7: KI-Copilot | ✅ | ✅ | 📖 |
| M8: KI Bildretusche | ✅ | ✅ | ❌ |
| M8: Preisvergleich | ✅ | ✅ | 📖 |
| Einstellungen / Stammdaten | ✅ | ❌ | ❌ |
| Benutzer-Verwaltung | ✅ | ❌ | ❌ |
✅ = Vollzugriff | 📖 = Nur Lesen | ❌ = Kein Zugriff
---
## 5. mobile.de Seller API Push-Only Felder
### Sync-Richtung: **Push-Only** (ERP → mobile.de, NICHT bidirektional)
### Feldmapping ERP → mobile.de
| ERP-Feld | mobile.de Seller API Feld | Anmerkung |
|---|---|---|
| Marke | make | Pflichtfeld |
| Modell/Typ | model | Pflichtfeld |
| FIN | vin | 17 Zeichen |
| Baujahr | firstRegistration | YYYY-MM |
| Kilometerstand / Betriebsstunden | mileage | Einheit: km oder h (mobile.de supported hours für Baumaschinen) |
| Preis | price | EUR, netto/brutto kennzeichnen |
| Leistung kW | powerKw | Pflichtfeld für LKW |
| Leistung PS | powerHp | Berechnet aus kW |
| Kraftstoffart | fuelType | diesel, petrol, electric, etc. |
| Getriebe | transmission | manual, automatic |
| Farbe | color | |
| Zustand | condition | new, used |
| LKW-Typ | category | LKW-spezifische Kategorie |
| Art (Bagger, Radlader, etc.) | bodyType | Baumaschinen-spezifisch |
| Aufbau | equipment | Freitext |
| Standort | sellerLocation | PLZ, Ort |
| Fotos | images | URLs oder Base64 |
| Beschreibung | description | Freitext, multi-language |
| Verfügbarkeit | availabilityStatus | available, reserved, sold |
### API-Endpunkte (Push)
- **Listing erstellen:** POST /api/seller/listings
- **Listing aktualisieren:** PUT /api/seller/listings/{listingId}
- **Listing entfernen:** DELETE /api/seller/listings/{listingId}
- **Listing-Status:** GET /api/seller/listings/{listingId}/status
---
## 6. OpenRouter Modell-Empfehlungen
| Use Case | Modell | OpenRouter ID | Begründung |
|---|---|---|---|
| **KI-Copilot (Text + Sprache)** | Bestes verfügbares Modell | openrouter/auto oder claude-3.5-sonnet, gpt-4o | Flexibel, Auto-Routing wählt bestes Modell |
| **OCR ZB I/II (Vision)** | Qwen2.5-VL | qwen/qwen-2.5-vl-72b-instruct | Spezialisiert auf Dokumenterkennung, Multilingual |
| **Bildretusche (Hintergrund/Spiegelungen)** | Flux.1-Pro | black-forest-labs/flux-1.1-pro | State-of-the-art Bildgenerierung/-bearbeitung |
| **Fahrzeugfoto-Analyse** | Qwen2.5-VL | qwen/qwen-2.5-vl-72b-instruct | Vision-Modell für Bildverständnis |
### DSGVO-Hinweis
- OpenRouter verarbeitet Daten in der Cloud (User hat zugestimmt)
- Keine sensiblen Personendaten an OpenRouter senden (Ausweisdaten NICHT an KI übertragen)
- Fahrzeugdaten und Fotos werden an OpenRouter gesendet (keine Ausweisdaten auf Fotos)
---
## 7. MVP-Phasen-Vorschlag
| Phase | Module | Inhalt | Priorität |
|---|---|---|---|
| **Phase 1** | M1 | Fahrzeugbestand + mobile.de Push-Sync | Höchste Kernfunktion |
| **Phase 2** | M3 | Kontakt- & Kundenverwaltung | Hoch Voraussetzung für Verkauf |
| **Phase 3** | M4 | Dateiablage pro Fahrzeug | Hoch Voraussetzung für OCR/Bildretusche |
| **Phase 4** | M2 | OCR Fahrzeugdaten-Erfassung | Mittel Effizienz-Feature |
| **Phase 5** | M5 | Verkaufsmodul mit Rechtsdokumenten | Hoch Geschäftsprozess |
| **Phase 6** | M7 | KI-Copilot | Mittel Automatisierung |
| **Phase 7** | M8 | KI Bildretusche & Preisvergleich | Niedrig Optimierung |
| **Querschnitt** | M6 | Responsive UI + i18n | Alle Phasen gilt überall |
### Phasen-Abhängigkeiten
```
Phase 1 (M1) → Phase 2 (M3) → Phase 3 (M4) → Phase 4 (M2)
Phase 5 (M5) ←Phase 2 (M3) ←Phase 1 (M1)
Phase 6 (M7) → Phase 7 (M8)
```
M6 (Responsive UI + i18n) läuft parallel zu allen Phasen.
---
## 8. Sicherheit & Compliance
### DSGVO-Konformität
- **Ausweisdaten:** Verschlüsselt in PostgreSQL gespeichert (AES-256), Zugriff nur Admin + Buchhaltung
- **Personendaten:** DSGVO-konform, Löschkonzept für Kunden-Daten (Aufbewahrungsfristen beachten)
- **OpenRouter:** Cloud-Verarbeitung akzeptiert, keine Ausweisdaten an KI senden
- **Audit-Log:** Alle Änderungen an Kundendaten werden protokolliert (Wer, Was, Wann)
### Geldwäsche-Prävention (GwG)
- Identifikation bei Barzahlungen > 10.000 € (Ausweisdaten erfassen)
- Meldepflicht wird systemseitig angezeigt
- Meldedokument wird vorbereitet
- Dokumentation der Identifikation im Kaufvertrag
### Technische Sicherheit
- **CSRF:** Token-basiert (FastAPI + Next.js)
- **XSS:** Input-Sanitization, CSP-Headers
- **SQL-Injection:** ORM (SQLAlchemy), keine raw SQL queries
- **File-Upload-Validation:** MIME-Type-Check, Größenlimit 50MB, Virenscan empfohlen
- **Auth:** JWT-basiert, Session-Timeout 30min, Refresh-Token
- **HTTPS:** Via Coolify/Traefik (Let's Encrypt)
---
## 9. Constraints (Technische Rahmenbedingungen)
### Tech-Stack (finalisiert)
- **Backend:** Python 3.12+ / FastAPI
- **Frontend:** React 18+ / Next.js 14+ / TypeScript
- **Datenbank:** PostgreSQL 16+
- **ORM:** SQLAlchemy 2.0 (async)
- **KI/OCR/Bild:** OpenRouter API (Qwen2.5-VL, Flux.1-Pro, Claude/GPT-4)
- **Auth:** JWT (python-jose) + bcrypt
- **PDF-Generierung:** WeasyPrint oder ReportLab
- **DATEV-Export:** CSV (Python csv module)
- **i18n:** next-intl (Next.js) + FastAPI gettext
### Hosting
- **Plattform:** Coolify self-hosted auf eigenem Server
- **Container:** Docker Compose (app, db, redis optional)
- **SSL:** Let's Encrypt via Traefik
- **Domain:** Zu klären (Coolify verwaltet)
### Externe APIs
- **mobile.de Seller API:** REST API (Push-Only)
- **OpenRouter:** LLM + Vision + Bildgenerierung
- **BZSt eVatR API:** NICHT verfügbar im MVP (manuelle Prüfung)
- **DATEV:** Keine API, CSV-Export
### Budget/Timeline
- API-Kosten: OpenRouter (pay-per-use), mobile.de (Händleraccount)
- Timeline: MVP-Phasen sequenziell, keine harten Deadlines
---
## 10. Non-Goals (Explizit nicht Teil des Systems)
- **Kein** komplettes Buchhaltungssystem (Fibu, Bilanz, Steuererklärung)
- **Kein** Werkstatt-/Reparatur-Auftragsmanagement
- **Kein** Ersatzteil-/Lagerverwaltung
- **Kein** Fuhrparkmanagement / Telematik
- **Kein** eigenes Zahlungs-Gateway (Zahlungsabwicklung extern)
- **Kein** mobile App (nur responsive Web-UI)
- **Kein** eigenes KI-Modell-Training (Nutzung externer APIs via OpenRouter)
- **Kein** Marketing-/Newsletter-System
- **Kein** Versandlogistik-/Transportmanagement
- **Kein** mobile.de Import (nur Push, NICHT bidirektional)
- **Kein** BZSt eVatR API-Integration im MVP (manuelle Prüfung)
- **Keine** Datenmigration (keine Bestandsdaten zu importieren)
---
## 11. Annahmen (Assumptions)
1. Der User hat einen aktiven mobile.de Händleraccount mit Seller API-Zugang
2. Der User verfügt über deutsche Zulassungsbescheinigungen (ZB I und ZB II) für OCR-Erfassung
3. Der User ist umsatzsteuerpflichtig und benötigt USt-IdNr.-Prüfung für EU-Lieferungen
4. Der User verkauft an B2B (Firmen) und ggf. B2C (Privatkunden)
5. Der User hat Budget für OpenRouter API-Kosten (pay-per-use)
6. Das System wird von ~10 Nutzern im Büro genutzt (kein Massen-System)
7. Rechtsdokumente basieren auf deutschem/EU-Recht
8. KI-Copilot benötigt Internetverbindung (OpenRouter Cloud)
9. DSGVO: Cloud-Verarbeitung via OpenRouter ist akzeptiert
10. BZSt eVatR API-Zugang ist NICHT vorhanden manuelle Prüfung im MVP
11. Keine Datenmigration von Altsystemen nötig
12. Vertragsvorlagen werden als Stammdaten vom Admin gepflegt
13. KI darf selbstständig handeln (nach User-Bestätigung)
14. ~10 Nutzer mit Rollen Admin/Verkäufer/Buchhaltung
15. i18n: Deutsch + Englisch von Anfang an
---
## 12. Discovery Checklist (20 Kategorien)
| # | Kategorie | Status | Anmerkung |
|---|-----------|--------|-----------|
| 1 | Auth | **ja** | JWT-Auth, 3 Rollen (Admin/Verkäufer/Buchhaltung), ~10 Nutzer, Session-Timeout 30min |
| 2 | Daten | **ja** | Validierung, Pagination, Search/Filter in M1/M3 definiert, Soft-Delete für Fahrzeuge/Kontakte |
| 3 | Fehler | **ja** | Toast-Notifications, Error-Pages (404/500), API-Fehler-Handling, Error-Logging |
| 4 | Skalierung | **nein** | ~10 Nutzer, keine Skalierungsanforderungen, keine Caching/Rate-Limiting nötig |
| 5 | Sicherheit | **ja** | CSRF, XSS, SQL-Injection-Schutz, File-Upload-Validation, DSGVO, Ausweisdaten verschlüsselt |
| 6 | UX | **ja** | Responsive (M6), Loading-States, Empty-States, Confirmation-Dialogs, i18n DE+EN |
| 7 | Infrastruktur | **später** | Backup/Restore via Coolify, Monitoring/Alerting später, Health-Checks via FastAPI |
| 8 | Multi-User | **ja** | ~10 Nutzer, 3 Rollen, Concurrency via DB-Locking (optimistic), keine Real-time-Updates nötig |
| 9 | Migration | **nein** | Keine Datenmigration nötig (keine Altsysteme) |
| 10 | Mobile | **ja** | Responsive Web-UI (M6), keine native App, Touch-optimiert |
| 11 | Integration | **ja** | mobile.de Seller API (Push), OpenRouter (LLM/Vision/Bild), DATEV-Export (CSV) |
| 12 | Compliance | **ja** | DSGVO, Geldwäsche-Prävention (GwG), USt-IdNr.-Prüfung (manuell), Audit-Log |
| 13 | Performance | **später** | Standard-Optimierung, PostgreSQL-Indizes, Lazy Loading, keine speziellen Anforderungen |
| 14 | i18n | **ja** | Deutsch + Englisch, Datums-/Zahlenformate, Sprache pro User einstellbar |
| 15 | Accessibility | **später** | Grundlegende WCAG-Konformität (Keyboard-Nav, Kontrast), Standard-Implementation |
| 16 | Analytics/Tracking | **nein** | Kein Usage-Tracking im MVP |
| 17 | Environments | **ja** | Dev + Prod via Coolify (Docker), Env-Variablen in Coolify, Secrets-Management via Coolify |
| 18 | Dokumentation | **später** | User-Docs/API-Docs nach MVP, OpenAPI-Auto-Docs via FastAPI verfügbar |
| 19 | Testing-Strategie | **ja** | pytest (Backend), jest/playwright (Frontend), Coverage-Target ≥80%, E2E für Kernprozesse |
| 20 | Naming/Branding | **später** | Projektname/Branding zu klären mit User, Domain via Coolify |
| 21 | Scheduling/Background-Jobs | **später** | mobile.de Sync ggf. als Cron-Job MVP: manuell, OpenRouter API-Calls async |
**Status: 21/21 Kategorien beantwortet** (Kategorie 21 = Scheduling, optional)
---
## 13. Test Coverage Summary
| Feature ID | Feature | Test Scenarios | Status |
|---|---|---|---|
| F-M1-01 | Fahrzeugbestand verwalten | 3 | ✅ |
| F-M1-02 | mobile.de Push-Sync | 3 | ✅ |
| F-M1-03 | mobile.de Listing-Verwaltung | 3 | ✅ |
| F-M2-01 | OCR ZB I (Qwen2.5-VL) | 3 | ✅ |
| F-M2-02 | OCR ZB II (Qwen2.5-VL) | 3 | ✅ |
| F-M3-01 | Kontakt- und Kundenverwaltung | 3 | ✅ |
| F-M3-02 | Kundensuche und Filter | 3 | ✅ |
| F-M4-01 | Dateiablage pro Fahrzeug | 3 | ✅ |
| F-M5-01 | Kaufvertrag erstellen | 3 | ✅ |
| F-M5-02 | USt-IdNr.-Überprüfung (manuell) | 3 | ✅ |
| F-M5-03 | Geldwäsche-Prävention | 3 | ✅ |
| F-M5-04 | Rechnung und Lieferbescheinigung | 3 | ✅ |
| F-M5-05 | DATEV-Export | 3 | ✅ |
| F-M5-06 | Vertragsvorlagen-Verwaltung | 3 | ✅ |
| F-M6-01 | Responsive Web-UI | 3 | ✅ |
| F-M6-02 | Internationalisierung (i18n) | 3 | ✅ |
| F-M7-01 | KI-Copilot Fahrzeug anlegen | 3 | ✅ |
| F-M7-02 | KI-Copilot Suchen und Verkaufen | 3 | ✅ |
| F-M7-03 | KI-Copilot Dokumente erstellen | 3 | ✅ |
| F-M7-04 | KI-Copilot Spracheingabe | 3 | ✅ |
| F-M8-01 | KI Bildretusche Hintergrund (Flux.1-Pro) | 3 | ✅ |
| F-M8-02 | KI Bildretusche Spiegelungen (Flux.1-Pro) | 3 | ✅ |
| F-M8-03 | Preisvergleich mobile.de | 3 | ✅ |
**Test Coverage: 23/23 Features mit Test-Szenarien (100%)**
---
## 14. Handoff Summary
- **Requirements Status:** Final Discovery abgeschlossen
- **Discovery Checklist:** 21/21 Kategorien beantwortet
- **Test Coverage:** 23/23 Features mit je 3 Test-Szenarien (100%)
- **Module:** 8 (M1-M8)
- **Features:** 23
- **MVP-Phasen:** 7 Phasen + 1 Querschnitt (M6)
- **Open Questions:** Siehe open_questions.md (alle resolved oder als „später" markiert)
- **Ready for UI Design:** YES
- **Ready for Architecture:** NO (erst nach UI Design Approval)
---
## 15. Offene Fragen
Siehe `open_questions.md` für kategorisierte Klärungsfragen mit resolved/unresolved Status.
+353
View File
@@ -0,0 +1,353 @@
{
"project": "erp-nutzfahrzeuge",
"version": "1.0.0",
"created": "2026-07-12",
"mvp_phase_order": ["T01", "T02", "T03", "T04", "T05", "T06", "T07"],
"cross_cutting": "M6 (Responsive UI + i18n) is integrated into every task",
"total_tasks": 7,
"tasks": [
{
"id": "T01",
"title": "Core Infrastructure + Auth + Shared UI Foundation",
"module": "M6 (cross-cutting) + Auth + Users",
"mvp_phase": "Phase 0 Foundation",
"description": "Complete project setup for backend (FastAPI + SQLAlchemy async + Alembic) and frontend (Next.js 14 + Tailwind + next-intl). Implements: database connection, config management (pydantic-settings), JWT auth service (login, refresh, logout with Redis token store), RBAC middleware, user management CRUD (admin only), audit_log model + middleware, health endpoint, i18n backend (Accept-Language). Frontend: App shell with Sidebar + Topbar, login page, JWT token management, API client, i18n setup (de/en), all shared UI components (Button, Card, Input, Select, Textarea, Label, Badge, Spinner, EmptyState, ErrorState, Avatar, Toast, ToastContainer, Tabs, Pagination, Toggle, ProgressBar, WarningBanner, DataTable, InfoGrid, Dropzone, Icon system). Dashboard view with KPI cards.",
"dependencies": [],
"requirement_ids": ["F-M6-01", "F-M6-02", "Auth", "UserManagement"],
"estimated_lines": 800,
"assigned_subagent": "implementation_engineer",
"acceptance_criteria": [
"POST /api/v1/auth/login with valid credentials returns 200 + JWT + refresh_token",
"POST /api/v1/auth/login with invalid credentials returns 401",
"POST /api/v1/auth/refresh with valid refresh_token returns 200 + new JWT",
"POST /api/v1/auth/refresh with invalid refresh_token returns 401",
"GET /api/v1/auth/me with valid JWT returns 200 + user data + role",
"GET /api/v1/auth/me without JWT returns 401",
"GET /api/v1/users without admin role returns 403",
"GET /api/v1/users with admin role returns 200 + paginated user list",
"POST /api/v1/users creates user, returns 201",
"PUT /api/v1/users/:id updates user, returns 200",
"DELETE /api/v1/users/:id soft-deletes user (is_active=false), returns 200",
"GET /api/v1/health returns 200",
"Audit log entry created on login, user create, user update, user delete",
"Frontend: Login page renders, login form submits, JWT stored, redirect to dashboard",
"Frontend: Sidebar shows navigation items filtered by role",
"Frontend: Language switch (DE/EN) updates all UI texts",
"Frontend: Responsive layout works at 375px, 768px, 1920px breakpoints",
"All shared UI components render correctly with all states"
],
"test_spec": {
"commands": [
"cd backend && python -m pytest tests/test_auth.py tests/test_users.py -v --cov=app --cov-report=term-missing",
"cd backend && ruff check app/",
"cd frontend && npx vitest run src/components/ui src/lib --coverage",
"cd frontend && npx eslint src/ --max-warnings 0",
"cd frontend && npm run build"
],
"expected_results": "All auth tests pass (login, refresh, logout, RBAC). User CRUD tests pass. Ruff lint clean. Frontend component tests pass. ESLint clean. Next.js build succeeds.",
"test_files": [
"backend/tests/test_auth.py",
"backend/tests/test_users.py",
"backend/tests/conftest.py",
"frontend/src/components/ui/__tests__/Button.test.tsx",
"frontend/src/components/ui/__tests__/Input.test.tsx",
"frontend/src/lib/__tests__/auth.test.ts",
"frontend/src/lib/__tests__/api-client.test.ts"
],
"coverage_target": 80
}
},
{
"id": "T02",
"title": "Vehicle Management + mobile.de Push-Sync",
"module": "M1",
"mvp_phase": "Phase 1 Core Function",
"description": "Complete vehicle management system. Backend: vehicles model (with type-specific fields for LKW/PKW/Baumaschine/Stapler/Transporter), vehicle_service (CRUD + filter by type/availability/price range + search + sort + pagination + soft-delete), vehicle router (5 endpoints), FIN validation (17 chars, unique). mobile_de_listings model, mobile_de_service (push_listing, update_listing, delete_listing, get_status, batch_push, field mapping ERP→mobile.de), mobile_de router (5 endpoints). Frontend: FahrzeugListe (filterable/sortable table with search, pagination, mobile.de status badges), FahrzeugDetail (7 tabs: Allgemein, Technisch, Baumaschinen, Verkaufsinfo, Dokumente, Fotos, mobile.de), FahrzeugForm (with type-specific conditional fields, OCR upload placeholder, validation), mobile.de listing management view with batch push and error logs.",
"dependencies": ["T01"],
"requirement_ids": ["F-M1-01", "F-M1-02", "F-M1-03"],
"estimated_lines": 700,
"assigned_subagent": "implementation_engineer",
"acceptance_criteria": [
"GET /api/v1/vehicles returns 200 with paginated, filterable, sortable vehicle list",
"GET /api/v1/vehicles?type=baumaschine&min_price=10000&max_price=50000 returns only matching vehicles",
"GET /api/v1/vehicles?search=mercedes returns vehicles matching make or model",
"POST /api/v1/vehicles with valid data returns 201, vehicle appears in list",
"POST /api/v1/vehicles with duplicate FIN returns 409 'FIN bereits vorhanden'",
"POST /api/v1/vehicles with FIN != 17 chars returns 422 validation error",
"POST /api/v1/vehicles with vehicle_type=baumaschine and operating_hours saves correctly",
"GET /api/v1/vehicles/:id returns 200 with full vehicle detail",
"PUT /api/v1/vehicles/:id updates fields, returns 200",
"DELETE /api/v1/vehicles/:id soft-deletes (deleted_at set), returns 200, not in list",
"POST /api/v1/vehicles/:id/mobile-de/push with complete data returns 200, sync_status='gelistet'",
"POST /api/v1/vehicles/:id/mobile-de/push with missing required fields returns 400, sync_status='fehler'",
"POST /api/v1/mobile-de/batch-push with 3 vehicles pushes all, returns summary",
"DELETE /api/v1/vehicles/:id/mobile-de/listing returns 200, sync_status='entfernt'",
"GET /api/v1/mobile-de/listings returns listings filtered by sync_status",
"Buchhaltung role: GET /api/v1/vehicles returns 200 (read-only), POST returns 403",
"Frontend: FahrzeugListe renders with filter bar, sortable columns, pagination",
"Frontend: FahrzeugForm shows Baumaschine fields conditionally when type=baumaschine",
"Frontend: FahrzeugDetail shows 7 tabs with correct data",
"Frontend: mobile.de batch push shows progress, errors per vehicle"
],
"test_spec": {
"commands": [
"cd backend && python -m pytest tests/test_vehicles.py tests/test_mobile_de.py -v --cov=app/routers/vehicles --cov=app/routers/mobile_de --cov=app/services/vehicle_service --cov=app/services/mobile_de_service --cov-report=term-missing",
"cd backend && ruff check app/routers/vehicles.py app/routers/mobile_de.py app/services/vehicle_service.py app/services/mobile_de_service.py app/models/vehicle.py app/models/mobile_de.py",
"cd frontend && npx vitest run src/components/vehicles --coverage",
"cd frontend && npm run build"
],
"expected_results": "All vehicle CRUD tests pass. FIN validation tests pass. mobile.de push/batch/delisting tests pass (mocked API). Filter/search/pagination tests pass. RBAC tests pass. Ruff lint clean. Frontend component tests pass. Build succeeds.",
"test_files": [
"backend/tests/test_vehicles.py",
"backend/tests/test_mobile_de.py",
"frontend/src/components/vehicles/__tests__/FahrzeugListe.test.tsx",
"frontend/src/components/vehicles/__tests__/FahrzeugForm.test.tsx",
"frontend/src/components/vehicles/__tests__/FahrzeugDetail.test.tsx"
],
"coverage_target": 80
}
},
{
"id": "T03",
"title": "Contact & Customer Management",
"module": "M3",
"mvp_phase": "Phase 2 Precondition for Sales",
"description": "Complete contact management system. Backend: contacts model (company_name, legal_form, address, country, vat_id, role kaeufer/verkaeufer/beide, vat_id_status, is_private, soft-delete), contact_persons model (name, function, phone, email), ust_id_status_history model. contact_service (CRUD + search by name/country/role/vat_id_status + filter + sort + pagination + soft-delete + cascade delete contact_persons), contact router (5 endpoints). Frontend: KontakteListe (with filter by role, country, USt-IdNr.-status, search, sort, pagination), contact detail/form with contact persons sub-form, USt-IdNr. status badge component.",
"dependencies": ["T01"],
"requirement_ids": ["F-M3-01", "F-M3-02"],
"estimated_lines": 450,
"assigned_subagent": "implementation_engineer",
"acceptance_criteria": [
"GET /api/v1/contacts returns 200 with paginated, filterable contact list",
"GET /api/v1/contacts?role=kaeufer&country=DE returns only German buyers",
"GET /api/v1/contacts?search=Müller returns contacts matching company_name",
"GET /api/v1/contacts?vat_id_status=geprueft returns only checked contacts",
"POST /api/v1/contacts with valid company data returns 201",
"POST /api/v1/contacts with is_private=true and no vat_id returns 201 (vat_id optional for private)",
"GET /api/v1/contacts/:id returns 200 with contact + contact_persons array",
"PUT /api/v1/contacts/:id updates fields including contact_persons, returns 200",
"DELETE /api/v1/contacts/:id soft-deletes, contact_persons cascaded, returns 200",
"Buchhaltung role: GET returns 200 (read-only), POST/PUT/DELETE returns 403",
"Frontend: KontakteListe renders with filters, search, pagination",
"Frontend: Contact form with contact persons sub-form (add/remove persons)",
"Frontend: USt-IdNr. status badge shows correct color per status"
],
"test_spec": {
"commands": [
"cd backend && python -m pytest tests/test_contacts.py -v --cov=app/routers/contacts --cov=app/services/contact_service --cov=app/models/contact --cov-report=term-missing",
"cd backend && ruff check app/routers/contacts.py app/services/contact_service.py app/models/contact.py",
"cd frontend && npx vitest run src/components/contacts --coverage",
"cd frontend && npm run build"
],
"expected_results": "All contact CRUD tests pass. Search/filter tests pass. Contact persons cascade test passes. Soft-delete test passes. RBAC tests pass. Ruff lint clean. Frontend tests pass. Build succeeds.",
"test_files": [
"backend/tests/test_contacts.py",
"frontend/src/components/contacts/__tests__/KontakteListe.test.tsx",
"frontend/src/components/contacts/__tests__/ContactForm.test.tsx"
],
"coverage_target": 80
}
},
{
"id": "T04",
"title": "Document Management & File Storage",
"module": "M4",
"mvp_phase": "Phase 3 Precondition for OCR/Retouch",
"description": "Complete document/file management system. Backend: documents model (vehicle_id FK, sale_id FK nullable, filename, file_path, file_type MIME, file_size, category, uploaded_by). document_service (upload with MIME-type allowlist + size validation ≤50MB, storage to local filesystem under /data/uploads/vehicles/{vehicle_id}/, thumbnail generation for images via Pillow, file delete, list with category filter, StorageBackend abstraction interface for future S3). document router (3 endpoints: list, upload, delete + download endpoint). Frontend: document upload Dropzone component (drag-and-drop, progress, validation), document grid with category filter, image preview lightbox, PDF iframe preview, file size display.",
"dependencies": ["T02"],
"requirement_ids": ["F-M4-01"],
"estimated_lines": 400,
"assigned_subagent": "implementation_engineer",
"acceptance_criteria": [
"GET /api/v1/vehicles/:id/documents returns 200 with document list filtered by category",
"POST /api/v1/vehicles/:id/documents with valid PDF (≤50MB) returns 201, file stored, document row created",
"POST /api/v1/vehicles/:id/documents with file >50MB returns 413 'Datei zu groß'",
"POST /api/v1/vehicles/:id/documents with invalid MIME type returns 415",
"POST /api/v1/vehicles/:id/documents with image generates thumbnail",
"DELETE /api/v1/vehicles/:id/documents/:doc_id removes file from disk + DB row, returns 200",
"GET document download returns file with correct Content-Type header",
"Buchhaltung role: GET returns 200 (read-only), POST/DELETE returns 403",
"Frontend: Dropzone accepts drag-and-drop, shows upload progress",
"Frontend: Document grid shows files with category icons, size, upload date",
"Frontend: Image preview opens in lightbox, PDF opens in iframe",
"Frontend: Category filter works (Vertrag, Rechnung, ZB I, ZB II, Foto, Sonstiges)"
],
"test_spec": {
"commands": [
"cd backend && python -m pytest tests/test_documents.py -v --cov=app/routers/documents --cov=app/services/document_service --cov=app/models/document --cov-report=term-missing",
"cd backend && ruff check app/routers/documents.py app/services/document_service.py app/models/document.py app/utils/",
"cd frontend && npx vitest run src/components/documents --coverage",
"cd frontend && npm run build"
],
"expected_results": "All document upload/delete/list tests pass. MIME validation test passes. Size limit test passes. Thumbnail generation test passes. RBAC tests pass. Ruff lint clean. Frontend tests pass. Build succeeds.",
"test_files": [
"backend/tests/test_documents.py",
"frontend/src/components/documents/__tests__/Dropzone.test.tsx",
"frontend/src/components/documents/__tests__/DocumentGrid.test.tsx"
],
"coverage_target": 80
}
},
{
"id": "T05",
"title": "OCR Service + OpenRouter Integration",
"module": "M2",
"mvp_phase": "Phase 4 Efficiency Feature",
"description": "Complete OCR integration via OpenRouter. Backend: openrouter_client.py (shared async HTTP client with httpx, chat_completion for LLM, vision_completion for Qwen2.5-VL, image_generation for Flux.1-Pro, timeout handling, error handling, token tracking). ocr_service.py (ZB I field extraction, ZB II field extraction, image quality check warning, FIN cross-validation between ZB I and ZB II with discrepancy warning, field mapping to vehicle schema). ai_interactions model (log all AI calls with type, input, output, model, tokens, metadata). OCR router endpoints (2: POST zb1, POST zb2). Frontend: OCR upload Dropzone integrated into FahrzeugForm, OCR loading state, OCR results display with editable fields, quality warnings display, FIN mismatch warning.",
"dependencies": ["T02", "T04"],
"requirement_ids": ["F-M2-01", "F-M2-02"],
"estimated_lines": 500,
"assigned_subagent": "implementation_engineer",
"acceptance_criteria": [
"POST /api/v1/vehicles/ocr/zb1 with valid image returns 200 + extracted fields (make, fin, first_registration, etc.)",
"POST /api/v1/vehicles/ocr/zb2 with valid image returns 200 + extracted fields (fin, hersteller, typ, etc.)",
"OCR response includes confidence score and warnings array",
"OCR response with low-quality image includes warning 'Bildqualität niedrig'",
"POST /api/v1/vehicles/ocr/zb1 logs interaction in ai_interactions table",
"OCR endpoint rejects non-image files with 415",
"OCR endpoint rejects images >10MB with 413",
"OpenRouter API error returns 502 with graceful error message",
"OpenRouter client timeout (30s) returns 504",
"Buchhaltung role: POST OCR returns 403",
"Frontend: OCR upload in FahrzeugForm triggers loading state",
"Frontend: OCR results auto-fill form fields, all editable",
"Frontend: Quality warning banner displayed when warnings present",
"Frontend: FIN mismatch warning when ZB I and ZB II FINs differ",
"Tests mock OpenRouter API (no real API calls in CI)"
],
"test_spec": {
"commands": [
"cd backend && python -m pytest tests/test_ocr.py tests/test_openrouter_client.py -v --cov=app/services/ocr_service --cov=app/services/openrouter_client --cov=app/routers/ai --cov-report=term-missing",
"cd backend && ruff check app/services/ocr_service.py app/services/openrouter_client.py app/routers/ai.py",
"cd frontend && npx vitest run src/components/vehicles/__tests__/OCRUpload.test.tsx --coverage",
"cd frontend && npm run build"
],
"expected_results": "All OCR tests pass with mocked OpenRouter. Field extraction tests pass. Quality warning test passes. FIN cross-validation test passes. Token tracking test passes. Error handling tests pass (timeout, API error). RBAC test passes. Ruff lint clean. Frontend tests pass. Build succeeds.",
"test_files": [
"backend/tests/test_ocr.py",
"backend/tests/test_openrouter_client.py",
"frontend/src/components/vehicles/__tests__/OCRUpload.test.tsx"
],
"coverage_target": 80
}
},
{
"id": "T06",
"title": "Sales Module + Legal Documents + Compliance",
"module": "M5",
"mvp_phase": "Phase 5 Business Process",
"description": "Complete sales module with legal document generation and compliance features. Backend: sales model (vehicle_id, buyer_id, seller_id, sale_type, price_net/gross, vat_rate, payment_method, status, contract_number, invoice_number). identification_data model (AES-256-GCM encrypted ID number). ust_id_checks model. contract_templates model. settings model (firmenprofil, system config). sale_service (CRUD + status workflow + contract number generation + invoice number generation). contract_service (WeasyPrint PDF generation from templates with Jinja2 variable replacement, supports inland/eu_ausland/drittland variants). datev_service (CSV export with date range filter, DATEV format). gwg_service (GwG warning >10000 EUR bar, identification data encryption/decryption). ust_id_service (manual check entry, warning for EU without check). settings_service (firmenprofil, master_data CRUD, contract_templates CRUD). Routers: sales (6 endpoints), compliance (3 endpoints), datev (1), settings (6+ endpoints). Frontend: VerkaufsModul 4-step wizard (Fahrzeug/Kunde → Vertragsdetails → Prüfung → Abschluss), GwG warning banner, USt-IdNr. warning, Einstellungen view (4 tabs: Stammdaten, Vertragsvorlagen, Benutzerverwaltung, Firmenprofil).",
"dependencies": ["T02", "T03"],
"requirement_ids": ["F-M5-01", "F-M5-02", "F-M5-03", "F-M5-04", "F-M5-05", "F-M5-06"],
"estimated_lines": 800,
"assigned_subagent": "implementation_engineer",
"acceptance_criteria": [
"GET /api/v1/sales returns 200 with paginated, filterable sales list",
"POST /api/v1/sales with valid vehicle_id + buyer_id returns 201",
"GET /api/v1/sales/:id returns 200 with sale detail",
"PUT /api/v1/sales/:id updates status/data, returns 200",
"POST /api/v1/sales/:id/contract generates PDF, returns 200 + PDF download",
"Contract PDF for inland sale includes USt (19%)",
"Contract PDF for drittland sale includes 0% USt + Ausfuhrvermerke",
"POST /api/v1/sales/:id/invoice generates invoice PDF with sequential invoice_number",
"POST /api/v1/sales/:id/ust-id-check with manual result returns 200, updates contact vat_id_status",
"POST /api/v1/sales/:id/identification with GwG data encrypts ID number (AES-256), returns 200",
"GET identification data decrypts only for admin/buchhaltung role",
"Sale with barzahlung >10000 EUR triggers GwG warning in response",
"EU-Ausland sale without checked USt-IdNr. triggers warning in response",
"GET /api/v1/sales/datev-export?from=2026-01-01&to=2026-01-31 returns CSV file",
"DATEV export with no sales in range returns CSV with headers only",
"GET /api/v1/settings/contract-templates returns 200 with templates list",
"POST /api/v1/settings/contract-templates creates template, returns 201",
"PUT /api/v1/settings/contract-templates/:id updates template, returns 200",
"GET /api/v1/settings/master-data?category=kraftstoffart returns 200 with entries",
"RBAC: Verkäufer cannot access invoice/ust-id/identification/datev endpoints (403)",
"RBAC: Buchhaltung cannot create sales (403), can access invoice/ust-id/datev (200)",
"Frontend: 4-step wizard renders, navigation between steps works",
"Frontend: GwG warning banner appears when barzahlung >10000",
"Frontend: USt-IdNr. warning appears for EU-Ausland without check",
"Frontend: Einstellungen 4 tabs render with correct content per role",
"Frontend: Contract template editor with variable highlighting"
],
"test_spec": {
"commands": [
"cd backend && python -m pytest tests/test_sales.py tests/test_datev.py tests/test_compliance.py -v --cov=app/routers/sales --cov=app/services/sale_service --cov=app/services/contract_service --cov=app/services/datev_service --cov=app/services/gwg_service --cov=app/services/ust_id_service --cov=app/routers/settings --cov-report=term-missing",
"cd backend && ruff check app/routers/sales.py app/services/ app/utils/crypto.py app/utils/pdf.py",
"cd frontend && npx vitest run src/components/sales src/components/settings --coverage",
"cd frontend && npm run build"
],
"expected_results": "All sales CRUD tests pass. Contract PDF generation tests pass (inland/eu/drittland). Invoice generation test passes. GwG warning test passes. USt-IdNr. check test passes. DATEV export format test passes. AES encryption/decryption test passes. Settings/contract template CRUD tests pass. RBAC tests pass (all 3 roles). Ruff lint clean. Frontend tests pass. Build succeeds.",
"test_files": [
"backend/tests/test_sales.py",
"backend/tests/test_datev.py",
"backend/tests/test_compliance.py",
"frontend/src/components/sales/__tests__/VerkaufsModul.test.tsx",
"frontend/src/components/settings/__tests__/Einstellungen.test.tsx"
],
"coverage_target": 80
}
},
{
"id": "T07",
"title": "KI Copilot + Image Retouch + Price Comparison",
"module": "M7 + M8",
"mvp_phase": "Phase 6+7 Automation & Optimization",
"description": "Complete AI features. Backend: copilot_service.py (parse natural language commands via OpenRouter LLM, structured JSON response with action type + data, execute actions: create vehicle, search vehicles, start sale, generate document, ask for missing data). copilot router (3 endpoints: text, voice, history). voice transcription via OpenRouter or browser Web Speech API. image_retouch_service.py (background removal via Flux.1-Pro, reflection reduction, batch processing). image retouch router (2 endpoints: single, batch). price_comparison_service.py (search mobile.de for similar vehicles, calculate average/median, return comparison data). price comparison router (1 endpoint). All AI interactions logged in ai_interactions. Frontend: KiCopilot chat interface (message list, input box, example commands, voice input button with microphone, loading state), image retouch UI (select photo from vehicle documents, retouch button, before/after preview, save to documents), price comparison view (bar chart with vehicle prices, average/median lines, price distribution).",
"dependencies": ["T02", "T04", "T05", "T06"],
"requirement_ids": ["F-M7-01", "F-M7-02", "F-M7-03", "F-M7-04", "F-M8-01", "F-M8-02", "F-M8-03"],
"estimated_lines": 600,
"assigned_subagent": "implementation_engineer",
"acceptance_criteria": [
"POST /api/v1/ai/copilot with 'Neues Fahrzeug: MAN TGL 2018, 120000 km, 32000 €' returns 200 + structured action {action: 'create_vehicle', data: {make: 'MAN', model: 'TGL', year: 2018, mileage_km: 120000, price: 32000}}",
"POST /api/v1/ai/copilot with 'Füge ein Fahrzeug hinzu' (no data) returns 200 + {action: 'ask_for_data', missing_fields: ['make', 'model', 'price']}",
"POST /api/v1/ai/copilot with 'Finde alle Fahrzeuge über 50000 €' returns 200 + {action: 'search_vehicles', filters: {min_price: 50000}}",
"POST /api/v1/ai/copilot with 'Verkaufe FIN XYZ an Müller GmbH' returns 200 + {action: 'start_sale', vehicle_id: '...', buyer_id: '...'}",
"POST /api/v1/ai/copilot/voice with audio file returns 200 + transcribed text + processed action",
"GET /api/v1/ai/interactions returns 200 with paginated AI interaction history",
"POST /api/v1/ai/image/retouch with image returns 200 + retouched image URL",
"POST /api/v1/ai/image/batch-retouch with multiple images returns 200 + progress per image",
"GET /api/v1/vehicles/:id/price-comparison returns 200 + similar vehicles list with prices + average + median",
"Price comparison for rare vehicle returns 200 + {comparisons: [], message: 'Keine Vergleichsfahrzeuge'}",
"All AI endpoints log to ai_interactions table",
"Buchhaltung role: GET copilot history returns 200 (read-only), POST copilot returns 403",
"Buchhaltung role: POST image retouch returns 403, GET price comparison returns 200 (read-only)",
"Frontend: KiCopilot chat renders messages, input, example commands",
"Frontend: Voice input button starts recording, shows listening state",
"Frontend: Image retouch UI shows before/after preview",
"Frontend: Price comparison chart renders with bars, average line, median line",
"Tests mock OpenRouter API (no real API calls in CI)"
],
"test_spec": {
"commands": [
"cd backend && python -m pytest tests/test_copilot.py tests/test_image_retouch.py tests/test_price_comparison.py -v --cov=app/services/copilot_service --cov=app/services/image_retouch_service --cov=app/services/price_comparison_service --cov=app/routers/ai --cov-report=term-missing",
"cd backend && ruff check app/services/copilot_service.py app/services/image_retouch_service.py app/services/price_comparison_service.py app/routers/ai.py",
"cd frontend && npx vitest run src/components/ai --coverage",
"cd frontend && npm run build"
],
"expected_results": "All copilot tests pass with mocked OpenRouter (text + voice). Action parsing tests pass. Image retouch tests pass with mocked Flux.1-Pro. Batch processing test passes. Price comparison test passes. RBAC tests pass. All AI interactions logged. Ruff lint clean. Frontend tests pass. Build succeeds.",
"test_files": [
"backend/tests/test_copilot.py",
"backend/tests/test_image_retouch.py",
"backend/tests/test_price_comparison.py",
"frontend/src/components/ai/__tests__/KiCopilot.test.tsx",
"frontend/src/components/ai/__tests__/ImageRetouch.test.tsx",
"frontend/src/components/ai/__tests__/PriceComparison.test.tsx"
],
"coverage_target": 80
}
}
],
"cleanup_rules": {
"after_migration_tasks": "Run alembic upgrade head, verify all tables created, run seed data script, verify foreign keys, remove any temporary migration scripts",
"after_each_task": "Run ruff --fix, run eslint --fix, remove dead code, verify no console.log/print left in production code"
},
"dependency_graph": {
"T01": [],
"T02": ["T01"],
"T03": ["T01"],
"T04": ["T02"],
"T05": ["T02", "T04"],
"T06": ["T02", "T03"],
"T07": ["T02", "T04", "T05", "T06"]
}
}