353 lines
28 KiB
JSON
353 lines
28 KiB
JSON
{
|
||
"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"]
|
||
}
|
||
} |