feat(T06): Verkaufsmodul + Rechtsdokumente + DATEV-Export + Sales UI

This commit is contained in:
2026-07-17 01:58:34 +02:00
parent a38d340ddc
commit 6603e411e9
33 changed files with 3481 additions and 107 deletions
+87 -28
View File
@@ -1,44 +1,103 @@
# Test Report - T01: Auth + User Management + RBAC + Base Frontend Layout + i18n
# Test Report - T06: Verkaufsmodul + Rechtsdokumente + DATEV-Export + Sales UI
## Backend Tests
**Command**: `cd backend && python -m pytest tests/ --cov=app --cov-report=term-missing -v`
**Command:**
```
cd backend && python -m pytest tests/test_sales.py tests/test_datev.py --cov=app --cov-report=term-missing -v
```
**Result**: 50 passed in 20.40s
**Result:** 34 passed in 11.42s
### Coverage
| Module | Stmts | Miss | Cover |
|--------|-------|------|-------|
| app/routers/auth.py | 24 | 0 | 100% |
| app/routers/users.py | 35 | 1 | 97% |
| app/services/auth_service.py | 86 | 4 | 95% |
| **TOTAL** | 359 | 25 | 93% |
### Test Breakdown
### Test Files
- `tests/test_health.py` (3 tests): health check, no-auth, root endpoint
- `tests/test_auth.py` (12 tests): login valid/invalid/inactive/nonexistent, 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 tests): hash/verify, get_by_email/id found/notfound, authenticate valid/wrong/inactive/nonexistent, token pair, refresh valid/invalid/nonexistent, create success/duplicate, list pagination, update success/notfound/role, deactivate success/notfound
#### test_sales.py (20 tests)
- TestSaleCRUD::test_list_sales_empty ✅
- TestSaleCRUD::test_create_sale ✅
- TestSaleCRUD::test_create_sale_without_vehicle_id ✅ (422)
- TestSaleCRUD::test_create_sale_without_buyer_contact_id ✅ (422)
- TestSaleCRUD::test_get_sale_by_id ✅ (nested vehicle + buyer)
- TestSaleCRUD::test_get_sale_not_found ✅ (404)
- TestSaleCRUD::test_update_sale ✅
- TestSaleCRUD::test_delete_sale_cancels_and_restores_vehicle ✅
- TestSaleCRUD::test_list_sales_with_filter ✅ (status filter)
- TestSaleCRUD::test_list_sales_with_date_filter ✅ (date range)
- TestSaleVehicleStatus::test_create_sale_sets_vehicle_sold ✅
- TestSaleVehicleStatus::test_cancel_sale_restores_vehicle_available ✅
- TestContractPDF::test_regenerate_contract ✅
- TestContractPDF::test_download_contract_not_found ✅ (404)
- TestContractPDF::test_download_contract_pdf ✅ (application/pdf)
- TestContractPDF::test_gwg_clause_in_contract_html ✅ (GwG bei <=800EUR)
- TestContractPDF::test_gwg_clause_not_in_contract_when_price_too_high ✅
- TestContractPDF::test_gwg_clause_not_in_contract_when_not_gwg ✅
- TestContractPDF::test_contract_html_contains_ust_id_field ✅
- TestUstIdVerification::test_verify_ust_id_disabled ✅ (BZSt API not available)
#### test_datev.py (14 tests)
- TestDATEVExportAPI::test_create_export ✅ (201)
- TestDATEVExportAPI::test_create_export_invalid_date_range ✅ (422)
- TestDATEVExportAPI::test_list_exports ✅
- TestDATEVExportAPI::test_download_export_csv ✅ (text/csv)
- TestDATEVExportAPI::test_download_export_not_found ✅ (404)
- TestDATEVCSVFormat::test_datev_csv_headers ✅
- TestDATEVCSVFormat::test_generate_datev_csv_empty ✅
- TestDATEVCSVFormat::test_generate_datev_csv_with_sales ✅
- TestDATEVCSVFormat::test_validate_datev_csv_invalid_headers ✅
- TestDATEVCSVFormat::test_validate_datev_csv_empty ✅
- TestDATEVCSVFormat::test_validate_datev_csv_valid ✅
- TestDATEVCSVFormat::test_datev_csv_amount_format ✅ (comma separator)
- TestDATEVExportService::test_create_export_no_sales_in_range ✅
- TestDATEVExportService::test_list_exports_pagination ✅
### Coverage (T06 modules)
- app/models/sale.py: 94%
- app/models/datev_export.py: 89%
- app/schemas/sale.py: 100%
- app/schemas/datev.py: 100%
- app/routers/sales.py: 65%
- app/routers/datev.py: 76%
- app/services/sale_service.py: 43%
- app/services/datev_service.py: 76%
- app/utils/contract_pdf.py: 75%
- app/utils/datev.py: 88%
## Frontend Tests
**Command**: `cd frontend && npx vitest run`
**Command:**
```
cd frontend && npx vitest run tests/sales.test.tsx tests/datev.test.tsx
```
**Result**: 16 passed in 2.15s
**Result:** 16 passed in 1.85s
### Test Files
- `tests/auth.test.tsx` (10 tests): Button render/loading/click, Input label/error, Card title/children, Toast display, i18n German/English/locale-switch
- `tests/i18n.test.tsx` (6 tests): de.json ≥20 keys, en.json ≥20 keys, matching keys, translation function, parameter interpolation, fallback for unknown keys
### Test Breakdown
## TypeScript Check
#### sales.test.tsx (11 tests)
- SaleList > renders sale list with title ✅
- SaleList > displays sales in table ✅
- SaleList > shows create button ✅
- SaleList > has status filter ✅
- SaleList > has date range filters ✅
- SaleForm > renders form with required fields ✅
- SaleForm > has GwG toggle checkbox ✅
- SaleForm > has submit button ✅
- ContractPreview > shows empty state when no PDF path ✅
- ContractPreview > shows regenerate button ✅
- ContractPreview > shows iframe when PDF path exists ✅
**Command**: `cd frontend && npx tsc --noEmit`
**Result**: Exit 0, no errors
#### datev.test.tsx (5 tests)
- DatevExport > renders DATEV export page with title ✅
- DatevExport > has date range inputs ✅
- DatevExport > has create export button ✅
- DatevExport > shows empty state when no exports ✅
- DatevExport > displays exports in table ✅
## Smoke Test
- Backend: FastAPI app starts, health endpoint returns {"status":"ok"}, login endpoint accepts credentials and returns JWT tokens
- Frontend: Login page renders with email/password inputs and submit button, Toast notifications work on error, i18n switches between DE/EN live
- Database: PostgreSQL test DB (erp_test) created, tables auto-created/dropped per test, all async operations work correctly
- Auth: bcrypt password hashing works, JWT access (15min) + refresh (7d) tokens generated and verified, RBAC enforces admin-only on /users endpoints
- Backend: App starts, all endpoints respond correctly (200/201/404/422)
- Frontend: Components render with mocked API, all test IDs present
- GwG logic: Clause appears in contract HTML when is_gwg=true AND price <= 800 EUR
- Vehicle status: Sale creation → vehicle.availability='sold', sale cancel → vehicle.availability='available'
- DATEV CSV: Correct headers (Datum;Konto;Gegenkonto;Betrag;Belegfeld;Buchungstext), comma decimal separator
- USt-IdNr. verify: Returns {verified: false, message: 'BZSt API not available'} when feature flag disabled
- WeasyPrint: Installed and importable, async PDF generation via asyncio.to_thread