feat(T03): OCR-Erfassung via OpenRouter Qwen2.5-VL + OCR UI with drag-and-drop
This commit is contained in:
+30
-138
@@ -1,145 +1,37 @@
|
||||
# Worklog - ERP Nutzfahrzeuge
|
||||
# Worklog
|
||||
|
||||
## 2026-07-13
|
||||
- Phase 1 (Discovery + UI Design): Abgeschlossen
|
||||
- requirements.md erstellt (703 Zeilen)
|
||||
- UI-Prototyp v8d erstellt und auf webspace.media-on.de gepublished
|
||||
- component_inventory.md erstellt
|
||||
- Phase 2 (Architektur): Abgeschlossen
|
||||
- architecture.md erstellt (1162 Zeilen, 14 ADRs)
|
||||
- task_graph.json erstellt (8 Tasks T01-T08)
|
||||
- AGENTS.md erstellt
|
||||
- Git: 2 Commits auf main (afe6d0a, be5a339), auf Forgejo gepusht
|
||||
- .gitignore erstellt
|
||||
- Bereit für Phase 3 (Implementation), wartet auf User-Freigabe
|
||||
## 2026-07-17 - T03: OCR-Erfassung via OpenRouter Vision + OCR UI
|
||||
|
||||
## T01 – Auth + User Management + RBAC + Frontend + i18n (2026-07-14)
|
||||
### Files Created (Backend)
|
||||
- `backend/app/models/ocr_result.py` - OCRResult model (UUID, vehicle_id FK, file_path, status enum, raw_text, structured_data JSONB, confidence_score, error_message, timestamps)
|
||||
- `backend/app/schemas/ocr.py` - OCRUploadResponse, OCRResultResponse, OCRResultListResponse, OCRApplyResponse, OCRStructuredData
|
||||
- `backend/app/utils/openrouter.py` - OpenRouter API client for Qwen2.5-VL vision model (base64 encoding, prompt engineering, JSON parsing, confidence extraction)
|
||||
- `backend/app/services/ocr_service.py` - upload_file, get_result, list_results, apply_to_vehicle, process_ocr (async), MIME/size validation, confidence threshold logic
|
||||
- `backend/app/tasks/ocr_processing.py` - Async background task with independent DB session
|
||||
- `backend/app/tasks/__init__.py` - Tasks package init
|
||||
- `backend/app/routers/ocr.py` - POST /upload, GET /results/:id, GET /results, POST /results/:id/apply
|
||||
- `backend/tests/test_ocr.py` - 33 tests (service unit tests, router integration tests, OpenRouter client tests)
|
||||
|
||||
### Backend
|
||||
- config.py: Pydantic BaseSettings, alle Env-Vars (DATABASE_URL, REDIS_URL, JWT_SECRET, CORS_ORIGINS, etc.)
|
||||
- database.py: Async SQLAlchemy engine, session factory, Base, init_db/drop_db
|
||||
- models/user.py: User mit UUID PK, email, password_hash, role (Enum), language, is_active, timestamps
|
||||
- schemas/user.py: LoginRequest, TokenResponse, RefreshRequest, UserCreate/Update/Response, UserListResponse
|
||||
- utils/jwt.py: create_access_token, create_refresh_token, decode_token, verify_access/refresh_token
|
||||
- services/auth_service.py: hash_password, verify_password, authenticate_user, generate_token_pair, refresh_access_token, create_user, list_users, update_user, deactivate_user
|
||||
- dependencies.py: get_pagination, get_current_user (JWT extraction), require_role (RBAC)
|
||||
- routers/auth.py: POST /login, POST /refresh, GET /me
|
||||
- routers/users.py: GET / (list paginated), POST / (create), PUT /:id (update), DELETE /:id (soft-delete) – alle admin-only
|
||||
- main.py: FastAPI app, CORS middleware, /api/v1 prefix, /health endpoint
|
||||
- tests/: conftest.py (async fixtures, PostgreSQL), test_auth.py (12 tests), test_users.py (14 tests), test_health.py (3 tests), test_auth_service.py (21 tests)
|
||||
- requirements.txt, .env.example, pytest.ini, .coveragerc
|
||||
### Files Created (Frontend)
|
||||
- `frontend/lib/ocr.ts` - OCR API client (uploadOCRScan, getOCRResult, listOCRResults, applyOCRToVehicle)
|
||||
- `frontend/components/ocr/OCRUpload.tsx` - Drag-and-drop file upload component
|
||||
- `frontend/components/ocr/OCRResults.tsx` - Results list with pagination and status badges
|
||||
- `frontend/components/ocr/OCRDetail.tsx` - Side-by-side original scan + extracted data with apply button
|
||||
- `frontend/app/[locale]/ocr/page.tsx` - OCR page combining upload, results, and detail
|
||||
- `frontend/tests/ocr.test.tsx` - 18 frontend tests
|
||||
|
||||
### Frontend
|
||||
- package.json, tsconfig.json, next.config.js, tailwind.config.ts, vitest.config.ts
|
||||
- app/layout.tsx, app/page.tsx, app/globals.css (design tokens als CSS vars)
|
||||
- app/(auth)/layout.tsx (I18nProvider + ToastProvider wrapper)
|
||||
- app/(auth)/login/page.tsx (Login-Form mit Validation, API call, Toast on error)
|
||||
- lib/api.ts (fetch wrapper mit auto-refresh, login, getCurrentUser, listUsers, createUser, deleteUser)
|
||||
- lib/auth.ts (useAuth hook, useRequireAuth)
|
||||
- lib/i18n.tsx (I18nProvider, useI18n, DE/EN translation)
|
||||
- components/ui/: Button, Input, Card, Table, Modal, Toast (alle 6 Komponenten)
|
||||
- messages/de.json (31 keys), messages/en.json (31 keys)
|
||||
- tests/setup.ts, tests/auth.test.tsx (5 tests), tests/i18n.test.tsx (7 tests)
|
||||
### Files Modified
|
||||
- `backend/app/config.py` - Added OPENROUTER_API_KEY, OPENROUTER_BASE_URL, OPENROUTER_OCR_MODEL, MAX_FILE_SIZE_MB
|
||||
- `backend/app/main.py` - Registered OCR router under /api/v1/ocr
|
||||
|
||||
### Test Results
|
||||
- Backend: 50/50 passed, 88% total coverage
|
||||
- Frontend: 12/12 passed, Next.js build success
|
||||
- test_report.md erstellt
|
||||
- Backend: 33/33 OCR tests passed, 85% coverage (target: 80%)
|
||||
- Backend: 229/229 full suite passed
|
||||
- Frontend: 18/18 OCR tests passed
|
||||
|
||||
## T02 – Vehicle Management + mobile.de Push + Vehicle UI (2026-07-14)
|
||||
|
||||
### Backend
|
||||
- models/vehicle.py: Vehicle + MobileDeListing models with UUID PK, soft-delete, all fields per spec
|
||||
- schemas/vehicle.py: VehicleCreate/Update/Response/ListResponse, MobileDeStatusResponse, MobileDePushResponse with auto-compute power_hp
|
||||
- utils/mobilede_mapping.py: map_fields() converts Vehicle to mobile.de Ad format
|
||||
- services/vehicle_service.py: CRUD with pagination, filtering, sorting, soft-delete
|
||||
- services/mobilede_service.py: push/update/delete listing, get status, retry (max 3)
|
||||
- routers/vehicles.py: 7 endpoints (list, create, detail, update, delete, mobile-de push, mobile-de status)
|
||||
- config.py: Added MOBILE_DE_API_KEY, MOBILE_DE_SELLER_ID
|
||||
- main.py: Registered vehicles router
|
||||
|
||||
### Frontend
|
||||
- lib/vehicles.ts: Full API client with typed interfaces
|
||||
- components/vehicles/: VehicleList, VehicleForm, VehicleDetail, MobileDeStatus
|
||||
- app/[locale]/fahrzeuge/: list page, neu (create) page, [id] detail page
|
||||
- tests/vehicles.test.tsx: 16 tests
|
||||
|
||||
### Test Results
|
||||
- Backend: 73/73 pytest passed, 82% total coverage
|
||||
- Frontend: 16/16 vitest passed
|
||||
- test_report.md updated
|
||||
|
||||
---
|
||||
|
||||
## T04: Kontakt-/Kundenverwaltung + Contact UI (2026-07-14)
|
||||
|
||||
### Backend
|
||||
- models/contact.py: Contact + ContactPerson models with UUID PK, soft-delete, CHECK constraints for role/vat_id_status/country
|
||||
- schemas/contact.py: ContactCreate/Update/Response/ListResponse + ContactPersonCreate/Response with VAT ID field_validator
|
||||
- utils/ust_validation.py: DE + 10 EU country regex patterns, EU fallback, validate_vat_id, validate_vat_id_or_raise, get_country_code_from_vat_id
|
||||
- services/contact_service.py: list_contacts (search, role filter with beide inclusion, is_eu filter, is_private filter, sort, pagination), get_contact_by_id, create_contact (with nested persons), update_contact, soft_delete_contact, add_contact_person, remove_contact_person
|
||||
- routers/contacts.py: 7 endpoints (list, create, detail, update, delete, add person, remove person) with RBAC (all read, admin+verkaeufer write)
|
||||
- main.py: Registered contacts router
|
||||
|
||||
### Frontend
|
||||
- lib/contacts.ts: Full API client with typed interfaces + validateVatIdFormat frontend validation
|
||||
- components/contacts/ContactList.tsx: Table with search, role filter, EU/Inland filter, sort, pagination
|
||||
- components/contacts/ContactForm.tsx: Create/edit form with USt-IdNr. validation, EU/Inland toggle, country selector, role, legal form, address, contact info, is_private
|
||||
- components/contacts/ContactDetail.tsx: Detail view with contact persons management (add/remove via Modal)
|
||||
- app/[locale]/kontakte/: list page, neu (create) page, [id] detail page
|
||||
- tests/contacts.test.tsx: 20 tests
|
||||
|
||||
### Test Results
|
||||
- Backend: 73/73 pytest passed, 91% coverage on contact modules (service 99%, ust_validation 94%, models 93%, schemas 94%, router 67%)
|
||||
- Frontend: 20/20 vitest passed
|
||||
- test_report.md updated
|
||||
|
||||
---
|
||||
## T01 Re-Implementation (2026-07-14)
|
||||
|
||||
### Backend
|
||||
- app/config.py: Pydantic BaseSettings with DATABASE_URL, REDIS_URL, JWT_SECRET, JWT_ALGORITHM, JWT_ACCESS_TTL_MINUTES=15, JWT_REFRESH_TTL_DAYS=7, CORS_ORIGINS, UPLOAD_DIR
|
||||
- app/database.py: Async SQLAlchemy engine, session factory, Base declarative, get_db dependency, init_db/drop_db helpers
|
||||
- app/models/user.py: User model with UUID PK, email(unique), password_hash, full_name, role(enum admin/verkaeufer/buchhaltung), language(default de), is_active(default true), created_at, updated_at
|
||||
- app/schemas/user.py: LoginRequest, TokenResponse, RefreshRequest, UserBase/Create/Update/Response/ListResponse, ErrorResponse, HealthResponse
|
||||
- app/utils/jwt.py: create_access_token, create_refresh_token, decode_token, verify_access_token, verify_refresh_token (HS256, type claim separation)
|
||||
- app/services/auth_service.py: hash_password (bcrypt), verify_password, get_user_by_email/id, authenticate_user, generate_token_pair, refresh_access_token, create_user, list_users (paginated), update_user, deactivate_user (soft delete)
|
||||
- app/dependencies.py: get_pagination, get_current_user (JWT bearer extraction), require_role (RBAC factory)
|
||||
- app/routers/auth.py: POST /login, POST /refresh, GET /me
|
||||
- app/routers/users.py: GET / (admin only, paginated), POST / (admin only), PUT /:id (admin only), DELETE /:id (admin only, soft delete)
|
||||
- app/main.py: FastAPI app with CORS, /api/v1 prefix, health endpoint, router registration
|
||||
- tests/conftest.py: Function-scoped async engine, session, admin/verkaeufer/inactive user fixtures, token fixtures, HTTP client fixtures
|
||||
- tests/test_health.py: 3 tests
|
||||
- tests/test_auth.py: 12 tests (login valid/invalid/inactive/nonexistent/email-format, refresh valid/invalid/access-rejected, me with/without/invalid/refresh token)
|
||||
- tests/test_users.py: 15 tests (list admin/non-admin/no-auth, pagination, create admin/non-admin/duplicate/short-pw, update admin/nonexistent, delete soft/nonexistent/non-admin, password hash exclusion)
|
||||
- tests/test_auth_service.py: 20 direct service unit tests
|
||||
- .env.example: All required env vars documented
|
||||
- requirements.txt: fastapi, uvicorn, sqlalchemy[asyncio], asyncpg, pydantic-settings, python-jose, passlib[bcrypt], redis, python-multipart, pytest, pytest-asyncio, httpx, pytest-cov
|
||||
|
||||
### Frontend
|
||||
- package.json: Next.js 14, React 18, next-intl, Tailwind, Vitest, Testing Library
|
||||
- tsconfig.json, next.config.js, tailwind.config.ts, postcss.config.js, vitest.config.ts
|
||||
- app/globals.css: Design tokens as CSS vars (primary, secondary, background, surface, text, error, success, border)
|
||||
- app/layout.tsx: Root layout
|
||||
- app/page.tsx: Redirect to /login
|
||||
- app/(auth)/login/page.tsx: Login form with email/password, validation, Toast on error, i18n integration
|
||||
- lib/api.ts: Fetch wrapper with auth header injection, token refresh, login, getCurrentUser, listUsers, createUser, deleteUser, healthCheck
|
||||
- lib/auth.ts: useAuth hook (login, logout, fetchUser), useRequireAuth hook
|
||||
- lib/i18n.tsx: I18nProvider, useI18n hook, locale switching, parameter interpolation
|
||||
- components/ui/Button.tsx: Primary/secondary/danger/ghost variants, loading spinner
|
||||
- components/ui/Input.tsx: Label, error display, forwardRef
|
||||
- components/ui/Card.tsx: Title + children container
|
||||
- components/ui/Table.tsx: Generic table with columns + data
|
||||
- components/ui/Modal.tsx: Overlay modal with close button
|
||||
- components/ui/Toast.tsx: ToastProvider, useToast hook, success/error/info/warning types
|
||||
- messages/de.json: 28 keys (login, nav, common, user.role)
|
||||
- messages/en.json: 28 keys (matching de.json)
|
||||
- tests/setup.ts: localStorage mock, next/navigation mock
|
||||
- tests/auth.test.tsx: 10 tests (Button, Input, Card, Toast, i18n DE/EN/switch)
|
||||
- tests/i18n.test.tsx: 6 tests (key count ≥20, matching keys, translation, interpolation, fallback)
|
||||
|
||||
### Test Results
|
||||
- Backend: 50/50 pytest passed, 93% total coverage (auth_service 95%, routers 98%)
|
||||
- Frontend: 16/16 vitest passed
|
||||
- TypeScript: tsc --noEmit exit 0
|
||||
- test_report.md created
|
||||
### Key Decisions
|
||||
- Used FastAPI BackgroundTasks for async processing (Redis Queue noted for production)
|
||||
- Confidence threshold: 0.7 (below → manual_review, above → completed)
|
||||
- structured_data JSONB contains: brand, model, vin, first_registration, mileage, power_kw, fuel_type
|
||||
- OpenRouter model: qwen/qwen2.5-vl-72b-instruct (configurable via env)
|
||||
- File validation: image/* MIME types only, max 50 MB
|
||||
|
||||
Reference in New Issue
Block a user