Files

14 KiB
Raw Permalink Blame History

AGENTS.md ERP Nutzfahrzeuge Implementation Guide

Version: 1.0.0
Datum: 2026-07-12


1. Project Overview

ERP-System für Nutzfahrzeug-/Baumaschinen-Handel (~10 Nutzer).

  • Backend: Python 3.12 / FastAPI / SQLAlchemy 2.0 async / PostgreSQL 16 / Redis 7
  • Frontend: Next.js 14 (App Router) / React 18 / TypeScript / Tailwind CSS / next-intl
  • Hosting: Coolify (Docker Compose) auf coolify-01 (46.225.91.159)
  • KI: OpenRouter (Qwen2.5-VL OCR, Flux.1-Pro Bild, Claude/GPT-4 Copilot)
  • External: mobile.de Seller API (Push-Only)

Architecture Reference

  • docs/architecture.md Complete architecture, DB schema, API design, ADRs
  • docs/task_graph.json Task breakdown with test specs
  • docs/requirements.md Full requirements (8 modules, 23 features)
  • docs/component_inventory.md UI components and states

2. Build & Test Commands

Backend

# Install dependencies
cd backend
pip install -r requirements.txt

# Run dev server
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000

# Run all tests
python -m pytest tests/ -v --cov=app --cov-report=term-missing --cov-report=html

# Run specific test file
python -m pytest tests/test_vehicles.py -v

# Run with coverage for specific module
python -m pytest tests/test_auth.py -v --cov=app/services/auth_service --cov-report=term-missing

# Lint
ruff check app/
ruff format app/ --check

# Database migrations
alembic revision --autogenerate -m "description"
alembic upgrade head
alembic downgrade -1

# Type check (optional but recommended)
mypy app/ --ignore-missing-imports

Frontend

cd frontend
npm install

# Run dev server
npm run dev

# Run all tests
npx vitest run --coverage

# Run specific test
npx vitest run src/components/vehicles --coverage

# Lint
npx eslint src/ --max-warnings 0
npx prettier --check src/

# Build
npm run build

# Type check
npx tsc --noEmit

Docker

# Build and start all services
docker-compose up -d --build

# View logs
docker-compose logs -f backend
docker-compose logs -f frontend

# Run database migration in container
docker-compose exec backend alembic upgrade head

# Run tests in container
docker-compose exec backend python -m pytest tests/ -v

3. Test Rules (MANDATORY)

TDD Principle

  • Write tests first or alongside implementation never after
  • Every PR must include tests
  • Coverage target: ≥80% per module

Do NOT Modify Existing Tests

  • Tests are written by the task spec and must not be modified to make them pass
  • If a test fails, fix the implementation, not the test
  • Exception: test data fixtures can be extended, but existing assertions must not be weakened

Backend Test Conventions

  • Framework: pytest + pytest-asyncio + httpx.AsyncClient
  • Test DB: Separate PostgreSQL database erp_test (NOT SQLite)
  • Fixtures: conftest.py provides:
    • async_db async SQLAlchemy session (rolled back after each test)
    • client httpx AsyncClient with app
    • auth_headers JWT headers for admin/verkaeufer/buchhaltung roles
    • seed_data minimal seed data (users, vehicle, contact)
  • Mocking: OpenRouter API MUST be mocked in all tests (no real API calls)
  • Naming: test_<feature>_<scenario>.py or test_<module>.py

Frontend Test Conventions

  • Framework: Vitest + React Testing Library
  • E2E: Playwright for critical paths (login, vehicle create, sale wizard)
  • Mocking: API calls mocked via vi.mock() or MSW (Mock Service Worker)
  • Naming: ComponentName.test.tsx in __tests__/ folder

Test Spec Compliance

Every task in task_graph.json has a test_spec with:

  • commands Exact commands to run
  • expected_results What success looks like
  • test_files Expected test file paths
  • coverage_target Minimum coverage percentage

ALL commands in test_spec.commands MUST pass before a task is considered done.


4. Code Conventions

Python (Backend)

# Naming
snake_case for variables, functions, methods, modules
PascalCase for classes (Models, Schemas, Services)
UPPER_SNAKE_CASE for constants

# Imports (ruff isort)
# Standard library
import os
from datetime import datetime
# Third party
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
# Local
from app.deps import get_db, get_current_user
from app.models.vehicle import Vehicle
from app.schemas.vehicle import VehicleCreate, VehicleResponse

# Type hints (mandatory)
async def get_vehicle(vehicle_id: UUID, db: AsyncSession) -> Vehicle:
    ...

# Async everywhere (SQLAlchemy 2.0 async)
result = await db.execute(select(Vehicle).where(Vehicle.id == vehicle_id))
vehicle = result.scalar_one_or_none()

# Error handling
if vehicle is None:
    raise HTTPException(status_code=404, detail={"error": {"code": "NOT_FOUND", "message": "Vehicle not found"}})

TypeScript (Frontend)

// Naming
camelCase for variables, functions, hooks
PascalCase for components, types, interfaces
UPPER_SNAKE_CASE for constants

// Imports order
// 1. React/Next
import { useState, useEffect } from 'react';
import { useRouter } from 'next/navigation';
// 2. Third party
import { useTranslations } from 'next-intl';
// 3. Local
import { Button } from '@/components/ui/Button';
import { apiClient } from '@/lib/api-client';

// Components: function declaration with explicit return type
export function FahrzeugListe(): JSX.Element {
  ...
}

// Types: interface for props
type FahrzeugListeProps = {
  vehicles: Vehicle[];
  isLoading: boolean;
};

File Organization

  • One model per file in models/
  • One router per module in routers/
  • One service per domain in services/
  • One schema set per module in schemas/
  • Components grouped by feature in components/<feature>/

5. Forbidden Patterns

Backend

  • No raw SQL queries always use SQLAlchemy ORM
  • No synchronous database calls always async (await db.execute(...))
  • No secrets in code use environment variables / pydantic-settings
  • No print() in production code use Python logging
  • No bare except: always catch specific exceptions
  • No # type: ignore without comment explaining why
  • No circular imports use dependency injection
  • No business logic in routers routers only validate + call service
  • No direct model access from routers always go through service layer
  • No unencrypted PII Ausweisdaten MUST be AES-256 encrypted
  • No real OpenRouter API calls in tests always mock
  • No commit() in services without explicit reason let caller control transactions

Frontend

  • No any type use proper TypeScript types
  • No console.log in production use a logger utility
  • No inline styles use Tailwind classes or CSS modules
  • No direct fetch() without auth wrapper use apiClient
  • No hardcoded API URLs use NEXT_PUBLIC_API_URL env var
  • No hardcoded translation strings use t('key') from next-intl
  • No prop drilling > 2 levels use context or state management
  • No useEffect for data fetching without loading/error states
  • No forms without validation all inputs must have validation
  • No missing aria-label on icon-only buttons

General

  • No commits to main without passing tests
  • No large PRs max 800 lines per task
  • No TODO comments without ticket reference
  • No commented-out code in PRs
  • No files > 500 lines split into modules

6. Token Rule for File Operations

When working with files (reading, creating, updating):

Backend (Forgejo API)

// READ file (only for MODIFY, not for reference)
{"tool_name":"forgejo","tool_args":{"action":"files_get","owner":"Leopoldadmin","repo":"erp-nutzfahrzeuge","path":"backend/app/models/vehicle.py"}}

// CREATE new file
{"tool_name":"forgejo","tool_args":{"action":"files_create","owner":"Leopoldadmin","repo":"erp-nutzfahrzeuge","path":"backend/app/models/vehicle.py","content":"<content>","message":"Add vehicle model","branch":"main"}}

// UPDATE existing file (requires SHA from files_get)
{"tool_name":"forgejo","tool_args":{"action":"files_update","owner":"Leopoldadmin","repo":"erp-nutzfahrzeuge","path":"backend/app/models/vehicle.py","content":"<updated content>","sha":"<SHA>","message":"Update vehicle model","branch":"main"}}

// CHECK file existence
{"tool_name":"forgejo","tool_args":{"action":"files_list","owner":"Leopoldadmin","repo":"erp-nutzfahrzeuge","path":"backend/app/models/"}}

Rules

  • Use files_get ONLY when you need to modify an existing file (need SHA)
  • Use files_create for new files
  • Use files_list to check existence before creating
  • NEVER copy file contents inline in task descriptions reference by path
  • NEVER read entire files just for reference use curl + sed for snippets
  • For large reference content (architecture.md, requirements.md): reference by path, don't inline

7. Task Execution Workflow

For implementation_engineer

  1. Read the task from task_graph.json (task ID: T0X)
  2. Read architecture.md sections relevant to the task (DB schema, API design, ADRs)
  3. Read requirements.md for the specific feature IDs listed in requirement_ids
  4. Implement backend first: Model → Schema → Service → Router → Tests
  5. Implement frontend: Components → Pages → Tests
  6. Run ALL test_spec.commands and ensure they pass
  7. Run lint (ruff for backend, eslint for frontend)
  8. Run build (npm run build for frontend)
  9. Report results with test output evidence (not just "done")

Evidence Requirements (MANDATORY)

  • Test command output showing pass/fail counts
  • Coverage report showing ≥80%
  • Lint output showing 0 errors
  • Build output showing success
  • "File written" is NOT evidence. "Commit made" is NOT evidence.
  • Test output with pass counts IS evidence.

Task Completion Checklist

  • All acceptance_criteria from task_graph.json verified
  • All test_spec.commands pass
  • Coverage ≥ coverage_target for covered modules
  • Ruff lint clean (backend)
  • ESLint clean (frontend)
  • Build succeeds (frontend)
  • No forbidden patterns introduced
  • Test evidence provided in report

8. Environment Variables (Names Only)

Backend

DATABASE_URL=postgresql+asyncpg://erp_user:${DB_PASSWORD}@postgres:5432/erp_db
REDIS_URL=redis://redis:6379/0
JWT_SECRET=${JWT_SECRET}
JWT_ALGORITHM=HS256
JWT_ACCESS_TTL_MINUTES=15
JWT_REFRESH_TTL_DAYS=7
ENCRYPTION_KEY=${ENCRYPTION_KEY}
OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
MOBILE_DE_API_KEY=${MOBILE_DE_API_KEY}
MOBILE_DE_SELLER_ID=${MOBILE_DE_SELLER_ID}
UPLOAD_DIR=/data/uploads
MAX_FILE_SIZE_MB=50
CORS_ORIGINS=https://erp.domain.tld

Frontend

NEXT_PUBLIC_API_URL=http://backend:8000/api/v1
NEXTAUTH_SECRET=${NEXTAUTH_SECRET}

9. Git Workflow

Branches

  • main Production branch, deployed via Coolify
  • feature/T0X-<short-name> Feature branch per task
  • fix/T0X-<short-name> Fix branch

Commit Messages

feat(T02): add vehicle CRUD with type-specific fields
fix(T02): correct FIN validation for 17-char check
test(T02): add mobile.de batch push tests
refactor(T02): extract field mapping to separate function
docs(T02): update vehicle API documentation

PR Process

  1. Create feature branch from main
  2. Implement + test + lint + build
  3. Commit with conventional commit format
  4. Push branch
  5. Create PR with task ID reference
  6. CI runs: pytest, ruff, vitest, eslint, build
  7. Merge after CI passes

10. Database Migration Rules

  • Always use Alembic for schema changes
  • Never modify database directly (psql, pgadmin)
  • One migration per schema change don't bundle multiple changes
  • Test migration up AND down before committing
  • Seed data: Use a separate seed script (scripts/seed.py), not migrations
  • Cleanup after migration tasks:
    alembic upgrade head  # Apply
    # Verify all tables created
    python -c "from app.database import engine; from sqlalchemy import inspect; insp = inspect(engine); print(insp.get_table_names())"
    # Run seed data
    python scripts/seed.py
    # Verify foreign keys
    docker-compose exec postgres psql -U erp_user -d erp_db -c "\d vehicles"
    

11. OpenRouter Integration Rules

  • Always use openrouter_client.py shared client never call OpenRouter API directly
  • Always set timeout to 30 seconds
  • Always log interaction to ai_interactions table (type, model, tokens, metadata)
  • Never send Ausweisdaten or personal customer data to OpenRouter
  • Mock OpenRouter in all automated tests
  • Handle errors gracefully: API error → 502, timeout → 504, rate limit → 429
  • Model selection via config, not hardcoded in service

12. Security Checklist

  • JWT authentication on all endpoints (except /auth/login, /auth/refresh, /health)
  • RBAC enforced (require_role decorator on every router)
  • Input validation via Pydantic schemas on all endpoints
  • SQL injection protection via SQLAlchemy ORM (no raw SQL)
  • File upload: MIME-type allowlist + size limit
  • Ausweisdaten: AES-256-GCM encryption
  • CORS: only configured origins
  • Audit log: all create/update/delete actions logged
  • No secrets in code or git
  • HTTPS via Traefik/Let's Encrypt
  • DSGVO: no PII to OpenRouter, soft-delete with retention concept