Files
erp-nutzfahrzeuge/.a0/task_graph.json
T

512 lines
29 KiB
JSON

{
"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
}
}