Files
erp-nutzfahrzeuge/AGENTS.md
T

491 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)