chore: remove obsolete .a0/ and worklog.md (state migrated to plugin DB)

This commit is contained in:
Agent Zero
2026-06-10 21:05:50 +00:00
parent 451814874b
commit 054d4041e6
25 changed files with 95 additions and 3527 deletions
-24
View File
@@ -1,24 +0,0 @@
# AGENTS.md
## Project Overview
Brief description of the project.
## Tech Stack
- Language:
- Framework:
- Database:
- Deployment:
## Development Setup
Instructions to set up the development environment.
## Build & Test
Commands to build and test the project.
## Deployment
Basic deployment instructions.
## Conventions
- Code style
- Commit message format
- Branch naming
-10
View File
@@ -1,10 +0,0 @@
# Project Name
## Description
What this project does.
## Quick Start
How to get started quickly.
## Documentation
Links to key documentation files.
-273
View File
@@ -1,273 +0,0 @@
# Architecture Rentman.io Clone
> **Version:** 1.0
> **Date:** 2026-05-31
> **Project:** Rentman.io-Nachbau
> **Architect:** Solution Architect (A0 Software Orchestrator)
---
## Table of Contents
1. [Tech-Stack](#tech-stack)
2. [System Components](#system-components)
3. [API Design](#api-design)
4. [Authentication & Authorization](#authentication--authorization)
5. [Multi-Tenant Strategy](#multi-tenant-strategy)
6. [Deployment Approach](#deployment-approach)
7. [Module Boundaries & Data Flow](#module-boundaries--data-flow)
8. [Risk Notes](#risk-notes)
---
## Tech-Stack
### Backend
- **Language:** Python 3.12+
- **Framework:** FastAPI 0.115+ (async, high-performance, auto-docs)
- **ORM:** SQLAlchemy 2.0 with Alembic for migrations
- **Database:** SQLite for local development → PostgreSQL 16 for production
- **Caching:** Redis for session storage, rate limiting, and cache layer
- **Background Tasks:** Celery with Redis broker for async jobs (PDF generation, email)
- **File Storage:** Local filesystem for dev; S3-compatible (MinIO/AWS S3) for production
### Frontend
- **Language:** TypeScript 5.x (strict mode)
- **Framework:** React 19 with React Router 7 (routing), TanStack Router also considered
- **State Management:** Zustand (global), React Query (server state)
- **UI Library:** Tailwind CSS 4 + Radix UI (accessible headless components)
- **Build Tool:** Vite 6
- **Testing:** Vitest + React Testing Library
- **Mobile (PWA):** Service Worker with Workbox, Capacitor (for native camera/barcode)
### Mobile App (PWA)
- Progressive Web App with offline support (Service Workers)
- Barcode scanning via Capacitor's Barcode Scanner plugin
- IndexedDB for local data store; sync with backend via REST API
### DevOps & Deployment
- **Containerization:** Docker & Docker Compose
- **CI/CD:** GitHub Actions (build, test, lint, migrate, deploy)
- **Environment:** Local dev with Docker Compose; production on Coolify (CapRover alternative)
- **Monitoring:** Prometheus + Grafana (optional), Sentry for error tracking
---
## System Components
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ Rentman-Clone System │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ React SPA │ │ PWA Mobile │ │ External APIs│ │ Admin CLI │ │
│ │ (Browser) │ │ (Capacitor) │ │ (Webhooks) │ │ (optional) │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │ │
│ └────────────────┴────────┬────────┴─────────────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ Nginx Reverse │ │
│ │ Proxy (TLS) │ │
│ └────────┬────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ FastAPI App │ │
│ │ (Backend API) │ │
│ │ Port 8000 │ │
│ └───┬───────┬───────┘ │
│ │ │ │
│ ┌────────────▼──┐ ┌──▼────────────┐ │
│ │ Celery Worker │ │ PostgreSQL │ │
│ │ (Background) │ │ / SQLite │ │
│ │ + Redis Queue │ │ │ │
│ └───────┬────────┘ └───────────────┘ │
│ │ │
│ ┌──────────▼──────────┐ │
│ │ Object Storage │ │
│ │ (MinIO / S3) │ │
│ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
```
### Component Descriptions
- **React SPA:** Client-side rendered app, communicates via JSON REST API, handles UI rendering. Built with Vite for fast builds.
- **PWA Mobile App:** Same React codebase but served with Service Worker for offline capability; uses Capacitor for camera/barcode scanning. Can be deployed as TWA (Trusted Web Activity) on Android or via iOS WebAPKs.
- **External APIs (Webhooks):** Endpoints for third-party integrations (calendars, external CRM/ERP). Can also consume webhooks.
- **Nginx Reverse Proxy:** SSL termination, static file serving (for SPA), rate limiting, and optional load balancing.
- **FastAPI Backend:** RESTful API with JWT auth, RBAC, openapi schema. Implements all business logic. Uses SQLAlchemy async sessions.
- **Celery Worker:** Offloads long-running tasks: PDF generation, mass exports, email sending, webhook dispatching. Uses Redis as message broker.
- **Redis:** Used for Celery broker, also for caching query results (Redis Cache), session storage, and rate limiting counters.
- **PostgreSQL / SQLite:** SQLite for local dev (single file), PostgreSQL for production. Both accessed through same SQLAlchemy models, migrations applied via Alembic.
- **Object Storage (MinIO/S3):** Stores uploaded documents, equipment images, generated PDFs/CSVs. Provides signed URLs for download.
---
## API Design
### General Principles
- RESTful JSON API.
- Base URL: `/api/v1/` (versioned for future updates).
- Authentication: Bearer JWT token in `Authorization` header.
- Standard HTTP status codes: 200 OK, 201 Created, 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 422 Unprocessable, 500 Internal Server Error.
- Pagination: all list endpoints return JSON with `items`, `total`, `page`, `size`.
- Filtering: query parameters `?status=X&project_type=Y&sort=created_at&order=asc`.
- Nested resources for sub-entities (e.g., `/projects/{project_id}/subprojects`).
- OpenAPI (Swagger) auto-generated at `/docs` and `/redoc`.
### Endpoint Overview
**Auth:**
- `POST /api/v1/auth/login` login (email, password) → JWT token + refresh token
- `POST /api/v1/auth/refresh` refresh token
- `POST /api/v1/auth/register` self-service registration (tenant creation)
**Users & Roles:**
- `GET/POST /api/v1/users` list/create users (admin)
- `GET/PUT/DELETE /api/v1/users/{id}` user CRUD
- `GET/PUT /api/v1/roles` roles management
**Projects:**
- `GET/POST /api/v1/projects` list/create
- `GET/PUT/DELETE /api/v1/projects/{id}` project CRUD
- `GET /api/v1/projects/{id}/subprojects` subprojects
- `POST /api/v1/projects/{id}/subprojects` create subproject
- `GET/PUT/DELETE /api/v1/projects/{project_id}/subprojects/{id}` subproject CRUD
- Project function groups: `/projects/{project_id}/functiongroups` (nested)
- Project functions: `/projects/{project_id}/functiongroups/{group_id}/functions`
- Equipment groups: `/projects/{project_id}/equipmentgroups`
- Equipment items within group: `/projects/{project_id}/equipmentgroups/{group_id}/equipment`
- Crew assignments: `/projects/{project_id}/crew` (via functions)
- Vehicle assignments: `/projects/{project_id}/vehicles`
- Quotes: `/projects/{project_id}/quotes`
- Invoices: `/projects/{project_id}/invoices`
**Equipment Catalog:**
- `GET/POST /api/v1/equipment` list/create
- `GET/PUT/DELETE /api/v1/equipment/{id}` CRUD
- `GET /api/v1/equipment/{id}/availability` availability timeline
**Crew Catalog:**
- `GET/POST /api/v1/crew` list/create
- `GET/PUT/DELETE /api/v1/crew/{id}` CRUD
- `GET /api/v1/crew/{id}/availability` availability
**Vehicles:**
- `GET/POST /api/v1/vehicles` list/create
- `GET/PUT/DELETE /api/v1/vehicles/{id}` CRUD
**Contacts:**
- `GET/POST /api/v1/contacts` list/create
- `GET/PUT/DELETE /api/v1/contacts/{id}` CRUD
**Project Requests:**
- `GET/POST /api/v1/requests` public/private
- `GET/PUT /api/v1/requests/{id}` read/update
- `POST /api/v1/requests/{id}/convert` convert to project
**Documents:**
- `GET/POST /api/v1/documents` list/upload
- `GET/DELETE /api/v1/documents/{id}` download/delete
- Templates: `/api/v1/templates` (admin)
**Reports:**
- `GET /api/v1/reports/finance` financial KPIs
- `GET /api/v1/reports/equipment-utilization` equipment utilization
- `GET /api/v1/reports/project-stats` project statistics
**Export:**
- `POST /api/v1/export/{module}` asynchronous export to CSV/Excel
**Webhooks:**
- `GET/POST /api/v1/webhooks` manage webhook subscriptions
- `POST /api/v1/webhooks/{id}/trigger` test trigger
**Admin:**
- `GET /api/v1/audit-log` audit log entries
---
## Authentication & Authorization
### Authentication
- JWT access tokens (short-lived, 30 minutes) + refresh tokens (24 hours, stored in httpOnly cookie or secure storage).
- Password hashing with bcrypt.
- Optional OAuth2/OpenID Connect in V2 (not MVP).
- Self-service registration creates a new tenant account (company) and admin user.
### Authorization RBAC
- Role-based access control with four fixed roles: Admin, Project Manager, Warehouse Operator, Freelancer.
- Custom roles can be defined later; permissions are defined as granular rights:
- Module-level: `projects:read`, `projects:write`, `projects:delete`, `equipment:read`, etc.
- Object-level: user can only access projects they are assigned to (or all for admin).
- Roles are stored as list of permissions in `Role` model.
- Middleware checks token claims and resolves permissions; `Depends()` function in FastAPI routes.
### Multi-Tenant Strategy
- **Shared Database with Tenant ID:** Each table contains a `tenant_id` (UUID) foreign key to `Account` table.
- Row-Level Security: SQLAlchemy queries automatically filter by the tenant associated with the authenticated user's `account_id` (from JWT).
- Isolation: one account cannot see another's data.
- Onboarding: registration creates a new `Account` and an initial admin user.
---
## Deployment Approach
### Local Development
- Docker Compose with services: backend (FastAPI hot-reload), frontend (Vite dev server), PostgreSQL, Redis, MinIO.
- SQLite as default for quick dev (just set `DATABASE_URL=sqlite:///./rentman.db`), but Docker-Compose uses PostgreSQL.
- No volumes; data in container.
### Production (Coolify)
- Docker Compose as deployable artifact.
- Coolify handles SSL, domain routing, environment variables.
- CI/CD: GitHub Actions build test, then push image to registry; Coolify webhook triggers redeploy.
- Database backups via Coolify's built-in PostgreSQL backup feature.
- File storage: S3 bucket (MinIO or AWS S3).
### Scalability
- Backend stateless → horizontal scaling with multiple API instances behind load balancer.
- Celery workers can be scaled independently.
- PostgreSQL read replicas for heavy reporting.
---
## Module Boundaries & Data Flow
```
React SPA <──> FastAPI <──> SQLAlchemy <──> Database
<──> Celery (Redis) <──> Email/PDF/S3
```
**Modules (in backend):**
- `auth` login, register, refresh
- `users` user management, roles
- `projects` project CRUD, subprojects, function groups, functions
- `equipment` equipment catalog, categories, stock locations
- `crew` crew catalog, availability
- `vehicles` vehicles, assignments
- `contacts` contacts (companies/persons)
- `project_requests` public request handling
- `quotes_invoices` quotes, invoices, templates, PDF generation
- `documents` file uploads, downloads
- `reports` analytics endpoints
- `webhooks` manage subscriptions, dispatch
**Frontend (React):**
- `pages/` top-level routes
- `components/` reusable UI (forms, tables, modals)
- `hooks/` custom hooks for API calls
- `stores/` Zustand stores (global state)
- `services/` API client (axios/fetch wrappers)
- `i18n/` translations (DE, EN)
---
## Risk Notes
- **SQLite vs PostgreSQL discrepancies:** SQLite doesn't support some SQL features (e.g., `ALTER COLUMN`). Use Alembic to handle dialect differences; tests must run on PostgreSQL.
- **Offline Sync Conflicts:** Offline-first with Last-Write-Wins may cause data loss if two users edit same entity offline. For MVP, limited to equipment status and packlist checkmarks; acceptable.
- **Performance with large rental sets:** 500k equipment items → need indexing on tenant_id, status, and category. Reporting queries may be slow; consider materialized views.
- **Security:** JWT secret must be managed; never hardcoded. Use environment variable. Rate limiting prevents brute-force attacks.
- **Multi-Tenant Data Isolation:** Must ensure all queries include tenant filter. Implement a SQLAlchemy query filter on session creation to avoid accidental leaks.
- **PWA Barcode Scanning:** Capacitor camera plugin may require additional permissions; need to handle permission prompts gracefully.
- **Complexity of Project Hierarchy:** Project → Subproject → FunctionGroup → Function → (Crew/Equipment) is deep; ensure API design is consistent and documentation clear.
-18
View File
@@ -1,18 +0,0 @@
# Current Status
## Phase: 6 (Projects) T025 COMPLETE
## Last Completed
- T025: ProjectEquipment and ProjectCrew models
- Added ProjectEquipmentGroup, ProjectEquipment, ProjectCrew SQLAlchemy models
- Created Alembic migration and applied successfully
- Verified backend starts, health, auth, and existing endpoints return 200
## Next Tasks (Phase 6)
- T026: Project equipment group and items API
- T027: Project equipment management UI
- T028: Crew assignment API
- T029: Crew assignment UI
## Blockers
- None
-160
View File
@@ -1,160 +0,0 @@
# Architecture Decision Records (ADR) Rentman.io Clone
> **Version:** 1.0
> **Date:** 2026-05-31
> **Project:** Rentman.io-Nachbau
> **Architect:** Solution Architect (A0 Software Orchestrator)
---
## ADR-001: Backend-Framework
**Status:** Accepted
**Decision:** FastAPI (Python)
**Alternatives considered:**
- **NestJS (Node.js/TypeScript):** Same language for frontend/backend, but Python has stronger ecosystem for data processing, PDF generation, and ORM maturity (SQLAlchemy > TypeORM in stability). Higher learning curve for team.
- **Laravel (PHP):** Mature, batteries-included, but monolithic by nature; API-centric design is less natural.
- **Flask:** Too minimal; FastAPI provides async support, auto-generated OpenAPI docs, Pydantic validation for free.
**Rationale:** FastAPI combines high performance (async) with automatic documentation and validation that reduces manual work. Python's ecosystem for reports/exports (openpyxl, WeasyPrint) outweighs the language mismatch with frontend.
---
## ADR-002: Frontend-Strategie
**Status:** Accepted
**Decision:** React SPA with Vite + TypeScript, PWA via Service Workers + Capacitor
**Alternatives considered:**
- **Next.js (SSR):** Adds complexity for an internal business app where SEO is irrelevant and CSR is sufficient. SSR would require Node.js runtime in production.
- **Vue/Nuxt:** Simpler than React, but smaller ecosystem for complex interactive UI components (Gantt charts, drag-and-drop scheduling).
- **SvelteKit:** Promising but still too new; fewer UI component libraries.
**Rationale:** React has the largest ecosystem for business UIs (React Query, Zustand, Radix, TanStack Table). SPA (CSR) is sufficient because the app is used behind authentication. PWA support provides mobile app without separate native codebase. Capacitor grants access to barcode scanning via native plugin.
---
## ADR-003: Multi-Tenant-Strategie
**Status:** Accepted
**Decision:** Shared Database with `account_id` (tenant_id) column on every table
**Alternatives considered:**
- **Database-per-tenant:** Strongest isolation, but high operational overhead (managing hundreds of databases), harder cross-tenant analytics, expensive cloud costs.
- **Schema-per-tenant:** Middle ground, but ORM support is complex (dynamic schema switching), and PostgreSQL schema limits are lower than tables.
**Rationale:** Shared DB with tenant_id is simplest to implement and operate for an MVP. Row-level isolation is enforced at query level (SQLAlchemy session filter). For future scaling, read replicas or partition by tenant can be added.
---
## ADR-004: Datenbank-Wahl (Dev vs Production)
**Status:** Accepted
**Decision:** SQLite for local development, PostgreSQL for testing and production
**Alternatives considered:**
- **SQLite only:** Simple, but lacks concurrent write performance for 50+ users, no JSONB indexing, limited migration capabilities (ALTER COLUMN).
- **PostgreSQL only:** Requires every developer to run PostgreSQL locally or via Docker. Adds friction for quick tests.
**Rationale:** SQLite allows "clone and run" developer experience. PostgreSQL is required for realistic testing and production scale. SQLAlchemy + Alembic handle dialect differences. All CI tests run on PostgreSQL to catch incompatibilities early.
---
## ADR-005: Projekt-Hierarchie-Modellierung
**Status:** Accepted
**Decision:** Project → Subproject → FunctionGroup → Function (→ Crew/Equipment)
**Alternatives considered:**
- **Flat project with tags:** Simpler, but fails to represent hierarchical structure of real events (e.g., a festival with multiple stages, each with sound/light/video functions).
- **Nested Set (MPTT):** Complex for tree operations, harder to query for availability (time-based conflicts across subtrees).
**Rationale:** Rentman's original model is hierarchical because real AV projects are hierarchical. The 4-level depth with optional Subprojects and FunctionGroups gives maximum flexibility without over-complexity. Each level can have its own time periods, enabling partial rental (e.g., sound equipment only for 2 days within a 7-day project).
---
## ADR-006: Preisberechnung-Strategie
**Status:** Accepted
**Decision:** Calculated fields stored on Project (updated at query time or via trigger)
**Alternatives considered:**
- **Real-time calculation on every read:** Accurate but slow for list views showing 50+ projects.
- **Materialized view:** Efficient but complex to maintain (must refresh after any change to sub-items).
**Rationale:** Store calculated `price` and `cost` on the `Project` row, updated whenever sub-items (equipment, crew functions) are modified. Application-layer recalculation after each CUD operation on related entities. Cache in Redis for dashboard aggregations.
---
## ADR-007: PDF/Template-Engine
**Status:** Accepted
**Decision:** HTML-based templates with WeasyPrint rendering
**Alternatives considered:**
- **ReportLab:** Python-native, more control over layout, but template creation is code-only, requiring developer for each template.
- **LaTeX:** Professional quality, but heavy dependency; not UX-friendly for admin users to edit templates.
- **WYSIWYG (e.g., GrapesJS):** Full drag-and-drop, but generates messy HTML, hard to placeholders, and adds significant frontend complexity.
**Rationale:** HTML templates with placeholder syntax (`{{project_name}}`, `{{items_table}}`) offer the best balance: designers can create templates with CSS, non-technical admins can modify content, and WeasyPrint converts to high-quality PDF. This mirrors Rentman's approach (HTML-like template with placeholders).
---
## ADR-008: Offline-Sync-Strategie
**Status:** Accepted
**Decision:** Last-Write-Wins (LWW) with conflict warnings
**Alternatives considered:**
- **CRDT (Conflict-free Replicated Data Types):** No conflicts, but requires specialized data structures and complex merge logic.
- **OT (Operational Transformation):** Similar to Google Docs, overkill for checkmarks and status updates.
**Rationale:** Offline capabilities are needed for equipment packing (checkboxes, status changes) where conflicts are rare (usually one person per project scans items). LWW is simple, implementable with IndexedDB timestamps, and sufficient. When a conflict is detected (same item modified offline by two users), show a warning and let the user decide.
---
## ADR-009: API-Versionierung und -Dokumentation
**Status:** Accepted
**Decision:** URL-based versioning (`/api/v1/...`) with OpenAPI auto-generation
**Alternatives considered:**
- **Header-based versioning (`Accept: application/vnd.rentman.v1+json`):** Cleaner, but harder to test and document.
- **No versioning:** Faster now, but breaking changes inevitable.
**Rationale:** URL versioning is simplest for client developers (visible, testable via browser/curl). FastAPI's automatic OpenAPI generation ensures docs are always in sync with code.
---
## ADR-010: Deployment-Plattform
**Status:** Accepted
**Decision:** Docker Compose as deployable unit, Coolify for production management
**Alternatives considered:**
- **Kubernetes (K8s):** Overkill for a single-server deployment; adds complexity without immediate benefit.
- **Manual VPS setup:** Error-prone, hard to reproduce, no rollback.
- **PaaS (Heroku, Render):** Expensive at scale, less control over infrastructure.
**Rationale:** Docker Compose works identically on dev machines and production. Coolify provides a web-based management layer similar to CapRover/Porter without the Kubernetes complexity. Easy SSL, environment management, database backups, and app restarts.
---
## Open Decisions (Not Yet Resolved)
1. **Authentication Provider (V2):** Should OAuth2/OIDC (Google, Azure AD) be added in MVP or V2? Current decision: Basic JWT only for MVP; OIDC in V2.
2. **Barcode type:** QR-Code (primary) + Code128 (optional). Label size: 50×25mm standard. Label printer integration not in scope.
3. **Email provider:** SMTP (default) with optional SendGrid/Mailgun API. Transactional emails only.
4. **Datev or full accounting export:** Not in scope for MVP.
5. **Pricing model:** Open Source (MIT) core with optional SaaS hosting. Decision to be made by product owner.
-730
View File
@@ -1,730 +0,0 @@
# Database Design Rentman.io Clone
> **Version:** 1.0
> **Date:** 2026-05-31
> **Project:** Rentman.io-Nachbau
> **Architect:** Solution Architect (A0 Software Orchestrator)
---
## 1. Entity-Relationship Diagram (Textual)
```
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Account │ │ User │ │ Role │
│ (tenant) │ │ │ │ │
│ id (PK) │1───*│ id (PK) │*───*│ id (PK) │
│ name │ │ account_id (FK) │ │ name │
│ created_at │ │ email │ │ permissions (JSON│
└──────────────────┘ │ full_name │ └──────────────────┘
│ password_hash │
│ role_id (FK) │
└────────────────────┘
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Tag │ │ Contact │ │ Document │
│ id (PK) │*───*│ id (PK) │1───*│ id (PK) │
│ name │ │ account_id (FK) │ │ account_id (FK) │
│ color │ │ type │ │ name │
└──────────────────┘ │ company_name │ │ file_path │
│ first_name │ │ mime_type │
│ last_name │ │ size_bytes │
│ email, phone, mobile│ │ project_id (FK) │
│ street, number, │ │ uploaded_by (FK) │
│ postalcode, city, │ │ created_at │
│ country, tax_number│ └──────────────────┘
│ note │
└───────────────────┘
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Project │ │ Subproject │ │ ProjectFunctionGroup│
│ id (PK) │1───*│ id (PK) │1───*│ id (PK) │
│ account_id (FK) │ │ project_id (FK) │ │ project_id (FK) │
│ name │ │ name │ │ subproject_id (FK)│
│ reference │ │ custom_fields (JSON)│ │ name │
│ number │ └──────────────────┘ │ usageperiod_start/end│
│ customer_id (FK) │ │ planperiod_start/end│
│ contact_person_id │ │ remark │
│ account_manager_id│ └──────┬───────────┘
│ project_type │ │
│ planperiod_start/end│ │
│ usageperiod_start/end│ │
│ status │ ┌──────▼──────────┐
│ location_* │ │ ProjectFunction │
│ remark │ │ id (PK) │
│ price, cost │ │ project_id (FK) │
│ created_at, updated_at│ │ subproject_id (FK)│
│ archived │ │ group_id (FK) │
└──────┬────────────┘ │ type (enum) │
│ │ amount, usg/plan│
│ │ cost_rate, price_rate│
│ │ remark │
│ └──────┬──────────┘
│ │
│ ┌──────▼──────────┐
├───────────────────────────────────────────►│ ProjectCrew │
│ │ id (PK) │
│ ProjectEquipmentGroup │ project_id (FK) │
├───────────────────────────────────────────►│ function_id (FK) │
│ │ crew_id (FK) │
│ ProjectEquipment (within group) │ start, end │
│ └──────────────────┘
├──► Quote
│ (id, project_id, number, status, valid_until,
│ template, content_html, total_net, total_gross, created_at)
├──► Invoice
│ (id, project_id, number, status, due_date,
│ total_net, total_gross, created_at)
├──► ProjectRequest (id, name, contact fields, location, periods, status, remark)
│ │
│ └──► ProjectRequestEquipment (id, request_id, name, quantity, unit_price, linked_equipment_id)
└──► VehicleAssignment
(id, vehicle_id, project_id, start, end, driver_id)
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Equipment │ │ Crew │ │ Vehicle │
│ id (PK) │ │ id (PK) │ │ id (PK) │
│ account_id (FK) │ │ account_id (FK) │ │ account_id (FK) │
│ name │ │ first_name │ │ name │
│ category │ │ last_name │ │ license_plate │
│ serial_number │ │ email, phone │ │ type │
│ barcode, qr_code │ │ type (internal/ │ │ payload_kg │
│ stock_location_id │ │ freelancer) │ │ volume_m3 │
│ status │ │ hourly_rate │ │ is_active │
│ purchase_price │ │ daily_rate │ └──────────────────┘
│ current_value │ │ skills (JSON) │
│ weight_kg, dims, │ │ note │
│ power_watt, notes │ │ user_id (FK) │
│ custom_fields, img│ │ is_active │
└──────────────────┘ └────────┬──────────┘
┌──────────────────┐ ┌────────▼──────────┐
│ EquipmentGroup │ │ CrewAvailability │
│ (Bundle) │ │ id (PK) │
│ id (PK) │ │ crew_id (FK) │
│ account_id (FK) │ │ start, end │
│ name │ │ type (avail/unavail│
│ items (JSON/M2M) │ │ reason │
└──────────────────┘ └───────────────────┘
┌───────────────────┐
│ StockLocation │
│ id (PK) │
│ account_id (FK) │
│ name │
│ address │
│ is_default │
└───────────────────┘
┌───────────────────┐
│ AuditLog │
│ id (PK) │
│ account_id (FK) │
│ user_id (FK) │
│ action │
│ entity_type │
│ entity_id │
│ details (JSON) │
│ timestamp │
└───────────────────┘
```
## 2. Table Definitions
### 2.1 Account
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PRIMARY KEY, DEFAULT gen_random_uuid() (on PG) |
| name | VARCHAR(255) | NOT NULL |
| created_at | TIMESTAMP WITH TIME ZONE | NOT NULL, DEFAULT now() |
**Indexes:** None extra beyond PK.
---
### 2.2 User
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| account_id | UUID | NOT NULL, FK → Account(id) |
| email | VARCHAR(255) | NOT NULL, UNIQUE |
| full_name | VARCHAR(255) | NOT NULL |
| password_hash | VARCHAR(255) | NOT NULL |
| role_id | UUID | NOT NULL, FK → Role(id) |
| is_active | BOOLEAN | NOT NULL, DEFAULT TRUE |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT now() |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT now() |
**Indexes:**
- `ix_user_account_id` on `account_id`
- `ix_user_email` UNIQUE on `email`
---
### 2.3 Role
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| account_id | UUID | NOT NULL, FK → Account(id) |
| name | VARCHAR(100) | NOT NULL |
| permissions | JSONB | NOT NULL (array of strings like `["projects:read", "projects:write"]`) |
**Indexes:**
- `ix_role_account_id` on `account_id`
---
### 2.4 Tag
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| account_id | UUID | NOT NULL, FK → Account(id) |
| name | VARCHAR(100) | NOT NULL |
| color | VARCHAR(7) | NULLABLE (hex color) |
**Indexes:**
- `ix_tag_account_id` on `account_id`
**Relationships:**
- Many-to-Many with Equipment, Project, Crew (via join tables: `equipment_tags`, `project_tags`, `crew_tags`).
---
### 2.5 Contact
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| account_id | UUID | NOT NULL, FK → Account(id) |
| type | ENUM('company','person') | NOT NULL |
| company_name | VARCHAR(255) | NULLABLE |
| first_name | VARCHAR(100) | NULLABLE |
| last_name | VARCHAR(100) | NULLABLE |
| email | VARCHAR(255) | NULLABLE |
| phone | VARCHAR(50) | NULLABLE |
| mobile | VARCHAR(50) | NULLABLE |
| street | VARCHAR(255) | NULLABLE |
| number | VARCHAR(20) | NULLABLE |
| postalcode | VARCHAR(20) | NULLABLE |
| city | VARCHAR(100) | NULLABLE |
| country | VARCHAR(100) | NULLABLE |
| tax_number | VARCHAR(50) | NULLABLE |
| note | TEXT | NULLABLE |
**Indexes:**
- `ix_contact_account_id` on `account_id`
- `ix_contact_type` on `type`
---
### 2.6 Document
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| account_id | UUID | NOT NULL, FK → Account(id) |
| name | VARCHAR(255) | NOT NULL |
| original_filename | VARCHAR(255) | NOT NULL |
| file_path | VARCHAR(1024) | NOT NULL (relative to storage root) |
| mime_type | VARCHAR(127) | NOT NULL |
| size_bytes | INTEGER | NOT NULL |
| project_id | UUID | NULLABLE, FK → Project(id) |
| uploaded_by | UUID | NOT NULL, FK → User(id) |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT now() |
**Indexes:**
- `ix_document_account_id` on `account_id`
- `ix_document_project_id` on `project_id`
---
### 2.7 Project
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| account_id | UUID | NOT NULL, FK → Account(id) |
| name | VARCHAR(255) | NOT NULL |
| reference | VARCHAR(50) | NULLABLE |
| number | VARCHAR(50) | NOT NULL, automatically generated (prefix + sequential) |
| displayname | VARCHAR(512) | GENERATED (name + number) |
| customer_id | UUID | NULLABLE, FK → Contact(id) |
| contact_person_id | UUID | NULLABLE, FK → Contact(id) |
| account_manager_id | UUID | NULLABLE, FK → User(id) |
| project_type | ENUM('rental','service','sale') | NULLABLE |
| planperiod_start | TIMESTAMPTZ | NOT NULL |
| planperiod_end | TIMESTAMPTZ | NOT NULL |
| usageperiod_start | TIMESTAMPTZ | NOT NULL |
| usageperiod_end | TIMESTAMPTZ | NOT NULL |
| status | ENUM('request','planned','confirmed','active','completed','cancelled') | DEFAULT 'planned' |
| location_name | VARCHAR(255) | NULLABLE |
| location_street | VARCHAR(255) | NULLABLE |
| location_city | VARCHAR(100) | NULLABLE |
| location_postalcode | VARCHAR(20) | NULLABLE |
| location_country | VARCHAR(100) | NULLABLE |
| remark | TEXT | NULLABLE |
| price | DECIMAL(12,2) | NULLABLE (calculated field, updated by triggers) |
| cost | DECIMAL(12,2) | NULLABLE |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT now() |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT now() |
| archived | BOOLEAN | NOT NULL, DEFAULT FALSE |
**Indexes:**
- `ix_project_account_id` on `account_id`
- `ix_project_status` on `status`
- `ix_project_planperiod` on `planperiod_start`, `planperiod_end` (for availability queries)
- `ix_project_customer_id` on `customer_id`
---
### 2.8 Subproject
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| project_id | UUID | NOT NULL, FK → Project(id) ON DELETE CASCADE |
| name | VARCHAR(255) | NOT NULL |
| custom_fields | JSONB | NULLABLE |
**Indexes:**
- `ix_subproject_project_id` on `project_id`
---
### 2.9 ProjectFunctionGroup
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| project_id | UUID | NOT NULL, FK → Project(id) ON DELETE CASCADE |
| subproject_id | UUID | NULLABLE, FK → Subproject(id) |
| name | VARCHAR(255) | NOT NULL |
| usageperiod_start | TIMESTAMPTZ | NULLABLE |
| usageperiod_end | TIMESTAMPTZ | NULLABLE |
| planperiod_start | TIMESTAMPTZ | NULLABLE |
| planperiod_end | TIMESTAMPTZ | NULLABLE |
| remark | TEXT | NULLABLE |
**Indexes:**
- `ix_pfg_project_id` on `project_id`
---
### 2.10 ProjectFunction
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| project_id | UUID | NOT NULL, FK → Project(id) ON DELETE CASCADE |
| subproject_id | UUID | NULLABLE, FK → Subproject(id) |
| group_id | UUID | NULLABLE, FK → ProjectFunctionGroup(id) |
| name | VARCHAR(255) | NOT NULL |
| type | ENUM('crew_function','equipment_function','transport_function') | NOT NULL |
| amount | INTEGER | DEFAULT 1 |
| usageperiod_start | TIMESTAMPTZ | NULLABLE |
| usageperiod_end | TIMESTAMPTZ | NULLABLE |
| planperiod_start | TIMESTAMPTZ | NULLABLE |
| planperiod_end | TIMESTAMPTZ | NULLABLE |
| cost_rate | DECIMAL(10,2) | NULLABLE |
| price_rate | DECIMAL(10,2) | NULLABLE |
| remark | TEXT | NULLABLE |
**Indexes:**
- `ix_projectfunction_project_id` on `project_id`
- `ix_projectfunction_group_id` on `group_id`
---
### 2.11 ProjectCrew
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| project_id | UUID | NOT NULL, FK → Project(id) |
| function_id | UUID | NOT NULL, FK → ProjectFunction(id) ON DELETE CASCADE |
| crew_id | UUID | NOT NULL, FK → Crew(id) |
| start | TIMESTAMPTZ | NULLABLE (can default to project usage period) |
| end | TIMESTAMPTZ | NULLABLE |
**Indexes:**
- `ix_projectcrew_project_id` on `project_id`
- `ix_projectcrew_function_id` on `function_id`
- `ix_projectcrew_crew_id` on `crew_id`
---
### 2.12 ProjectEquipmentGroup
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| project_id | UUID | NOT NULL, FK → Project(id) ON DELETE CASCADE |
| subproject_id | UUID | NULLABLE, FK → Subproject(id) |
| name | VARCHAR(255) | NOT NULL |
| usageperiod_start | TIMESTAMPTZ | NULLABLE |
| usageperiod_end | TIMESTAMPTZ | NULLABLE |
| planperiod_start | TIMESTAMPTZ | NULLABLE |
| planperiod_end | TIMESTAMPTZ | NULLABLE |
| in_price_calculation | BOOLEAN | DEFAULT TRUE |
| remark | TEXT | NULLABLE |
**Indexes:**
- `ix_peg_project_id` on `project_id`
---
### 2.13 ProjectEquipment
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| project_id | UUID | NOT NULL, FK → Project(id) |
| equipment_group_id | UUID | NOT NULL, FK → ProjectEquipmentGroup(id) ON DELETE CASCADE |
| equipment_id | UUID | NULLABLE, FK → Equipment(id) |
| name | VARCHAR(255) | NOT NULL (can be free-text if equipment_id is NULL) |
| quantity | INTEGER | NOT NULL, DEFAULT 1 |
| quantity_total | INTEGER | NULLABLE (available inventory count) |
| unit_price | DECIMAL(10,2) | NULLABLE |
| discount | DECIMAL(5,2) | NULLABLE (percentage) |
| external_remark | TEXT | NULLABLE |
| internal_remark | TEXT | NULLABLE |
**Indexes:**
- `ix_projectequipment_group_id` on `equipment_group_id`
- `ix_projectequipment_equipment_id` on `equipment_id`
---
### 2.14 Equipment (Catalog)
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| account_id | UUID | NOT NULL, FK → Account(id) |
| name | VARCHAR(255) | NOT NULL |
| category | VARCHAR(100) | NULLABLE |
| brand | VARCHAR(100) | NULLABLE |
| model | VARCHAR(100) | NULLABLE |
| serial_number | VARCHAR(100) | NULLABLE |
| barcode | VARCHAR(100) | NULLABLE, UNIQUE |
| qr_code | VARCHAR(100) | NULLABLE, UNIQUE |
| stock_location_id | UUID | NULLABLE, FK → StockLocation(id) |
| status | ENUM('available','maintenance','defect','retired') | DEFAULT 'available' |
| purchase_price | DECIMAL(10,2) | NULLABLE |
| current_value | DECIMAL(10,2) | NULLABLE |
| weight_kg | DECIMAL(10,2) | NULLABLE |
| dimensions_cm | VARCHAR(50) | NULLABLE |
| power_watt | INTEGER | NULLABLE |
| custom_fields | JSONB | NULLABLE |
| notes | TEXT | NULLABLE |
| image_url | VARCHAR(2048) | NULLABLE |
**Indexes:**
- `ix_equipment_account_id` on `account_id`
- `ix_equipment_category` on `category`
- `ix_equipment_status` on `status`
- `ix_equipment_barcode` UNIQUE on `barcode`
- `ix_equipment_qrcode` UNIQUE on `qr_code`
---
### 2.15 StockLocation
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| account_id | UUID | NOT NULL, FK → Account(id) |
| name | VARCHAR(100) | NOT NULL |
| address | VARCHAR(255) | NULLABLE |
| is_default | BOOLEAN | DEFAULT FALSE |
**Indexes:**
- `ix_stocklocation_account_id` on `account_id`
---
### 2.16 Crew
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| account_id | UUID | NOT NULL, FK → Account(id) |
| first_name | VARCHAR(100) | NOT NULL |
| last_name | VARCHAR(100) | NOT NULL |
| email | VARCHAR(255) | NULLABLE |
| phone | VARCHAR(50) | NULLABLE |
| type | ENUM('internal','freelancer') | NOT NULL |
| user_id | UUID | NULLABLE, FK → User(id) (if they have login) |
| hourly_rate | DECIMAL(10,2) | NULLABLE |
| daily_rate | DECIMAL(10,2) | NULLABLE |
| skills | JSONB | NULLABLE (array of strings) |
| is_active | BOOLEAN | DEFAULT TRUE |
| note | TEXT | NULLABLE |
**Indexes:**
- `ix_crew_account_id` on `account_id`
- `ix_crew_type` on `type`
- `ix_crew_is_active` on `is_active`
---
### 2.17 CrewAvailability
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| crew_id | UUID | NOT NULL, FK → Crew(id) ON DELETE CASCADE |
| start | TIMESTAMPTZ | NOT NULL |
| end | TIMESTAMPTZ | NOT NULL |
| type | ENUM('available','unavailable','tentative') | DEFAULT 'unavailable' |
| reason | VARCHAR(255) | NULLABLE |
**Indexes:**
- `ix_crewavailability_crew_id` on `crew_id`
- `ix_crewavailability_period` on `start`, `end` (for overlap checks)
---
### 2.18 Vehicle
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| account_id | UUID | NOT NULL, FK → Account(id) |
| name | VARCHAR(100) | NOT NULL |
| license_plate | VARCHAR(20) | NULLABLE |
| type | VARCHAR(50) | NULLABLE |
| payload_kg | INTEGER | NULLABLE |
| volume_m3 | DECIMAL(10,2) | NULLABLE |
| is_active | BOOLEAN | DEFAULT TRUE |
**Indexes:**
- `ix_vehicle_account_id` on `account_id`
---
### 2.19 VehicleAssignment
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| vehicle_id | UUID | NOT NULL, FK → Vehicle(id) |
| project_id | UUID | NOT NULL, FK → Project(id) |
| start | TIMESTAMPTZ | NOT NULL |
| end | TIMESTAMPTZ | NOT NULL |
| driver_id | UUID | NULLABLE, FK → Crew(id) |
**Indexes:**
- `ix_vehicleassignment_vehicle_id` on `vehicle_id`
- `ix_vehicleassignment_project_id` on `project_id`
- `ix_vehicleassignment_period` on `start`, `end`
---
### 2.20 ProjectRequest
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| account_id | UUID | NOT NULL, FK → Account(id) |
| name | VARCHAR(255) | NOT NULL |
| contact_name | VARCHAR(255) | NULLABLE |
| contact_person_first_name | VARCHAR(100) | NULLABLE |
| contact_person_lastname | VARCHAR(100) | NULLABLE |
| contact_person_email | VARCHAR(255) | NULLABLE |
| location_name | VARCHAR(255) | NULLABLE |
| location_mailing_street | VARCHAR(255) | NULLABLE |
| location_mailing_number | VARCHAR(20) | NULLABLE |
| location_mailing_postalcode | VARCHAR(20) | NULLABLE |
| location_mailing_city | VARCHAR(100) | NULLABLE |
| location_mailing_country | VARCHAR(100) | NULLABLE |
| planperiod_start | TIMESTAMPTZ | NULLABLE |
| planperiod_end | TIMESTAMPTZ | NULLABLE |
| usageperiod_start | TIMESTAMPTZ | NULLABLE |
| usageperiod_end | TIMESTAMPTZ | NULLABLE |
| status | ENUM('new','processing','converted','rejected') | DEFAULT 'new' |
| remark | TEXT | NULLABLE |
| created_at | TIMESTAMPTZ | DEFAULT now() |
**Indexes:**
- `ix_projectrequest_account_id` on `account_id`
- `ix_projectrequest_status` on `status`
---
### 2.21 ProjectRequestEquipment
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| project_request_id | UUID | NOT NULL, FK → ProjectRequest(id) ON DELETE CASCADE |
| name | VARCHAR(255) | NOT NULL |
| quantity | INTEGER | NOT NULL |
| quantity_total | INTEGER | NULLABLE |
| unit_price | DECIMAL(10,2) | NULLABLE |
| linked_equipment_id | UUID | NULLABLE, FK → Equipment(id) |
**Indexes:**
- `ix_prequestequip_request_id` on `project_request_id`
---
### 2.22 Quote
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| account_id | UUID | NOT NULL, FK → Account(id) |
| project_id | UUID | NOT NULL, FK → Project(id) |
| number | VARCHAR(50) | NOT NULL (auto-generated) |
| status | ENUM('draft','sent','accepted','rejected') | DEFAULT 'draft' |
| valid_until | DATE | NULLABLE |
| template | VARCHAR(50) | NULLABLE |
| content_html | TEXT | NULLABLE (generated PDF content) |
| total_net | DECIMAL(12,2) | NULLABLE |
| total_gross | DECIMAL(12,2) | NULLABLE |
| created_at | TIMESTAMPTZ | DEFAULT now() |
**Indexes:**
- `ix_quote_account_id` on `account_id`
- `ix_quote_project_id` on `project_id`
- `ix_quote_status` on `status`
---
### 2.23 Invoice
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| account_id | UUID | NOT NULL, FK → Account(id) |
| project_id | UUID | NOT NULL, FK → Project(id) |
| number | VARCHAR(50) | NOT NULL (auto-generated) |
| status | ENUM('draft','sent','paid','overdue','cancelled') | DEFAULT 'draft' |
| due_date | DATE | NULLABLE |
| total_net | DECIMAL(12,2) | NULLABLE |
| total_gross | DECIMAL(12,2) | NULLABLE |
| created_at | TIMESTAMPTZ | DEFAULT now() |
**Indexes:**
- `ix_invoice_account_id` on `account_id`
- `ix_invoice_project_id` on `project_id`
- `ix_invoice_status` on `status`
---
### 2.24 Template
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| account_id | UUID | NOT NULL, FK → Account(id) |
| type | ENUM('quote','invoice','packing_list','delivery_note') | NOT NULL |
| name | VARCHAR(100) | NOT NULL |
| content_html | TEXT | NOT NULL |
| is_default | BOOLEAN | DEFAULT FALSE |
**Indexes:**
- `ix_template_account_id` on `account_id`
---
### 2.25 Webhook
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| account_id | UUID | NOT NULL, FK → Account(id) |
| url | VARCHAR(2048) | NOT NULL |
| secret | VARCHAR(255) | NOT NULL |
| events | JSONB | NOT NULL (array of event names) |
| is_active | BOOLEAN | DEFAULT TRUE |
**Indexes:**
- `ix_webhook_account_id` on `account_id`
---
### 2.26 WebhookLog
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| webhook_id | UUID | NOT NULL, FK → Webhook(id) ON DELETE CASCADE |
| event | VARCHAR(100) | NOT NULL |
| request_body | JSONB | NOT NULL |
| response_status | INTEGER | NOT NULL |
| response_body | TEXT | NULLABLE |
| success | BOOLEAN | NOT NULL |
| created_at | TIMESTAMPTZ | DEFAULT now() |
**Indexes:**
- `ix_webhooklog_webhook_id` on `webhook_id`
---
### 2.27 AuditLog
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| account_id | UUID | NOT NULL, FK → Account(id) |
| user_id | UUID | NOT NULL, FK → User(id) |
| action | VARCHAR(50) | NOT NULL (e.g., 'create', 'update', 'delete') |
| entity_type | VARCHAR(50) | NOT NULL (e.g., 'project', 'equipment') |
| entity_id | UUID | NOT NULL |
| details | JSONB | NULLABLE (changed fields) |
| timestamp | TIMESTAMPTZ | NOT NULL, DEFAULT now() |
**Indexes:**
- `ix_auditlog_account_id` on `account_id`
- `ix_auditlog_user_id` on `user_id`
- `ix_auditlog_entity` on `entity_type`, `entity_id`
- `ix_auditlog_timestamp` on `timestamp`
---
### 2.28 EquipmentGroup (Bundle)
| Column | Type | Constraints |
|--------|------|-------------|
| id | UUID | PK |
| account_id | UUID | NOT NULL, FK → Account(id) |
| name | VARCHAR(255) | NOT NULL |
**Relationships:**
- Items stored in junction table `equipment_group_items` with columns `equipment_group_id (FK)`, `equipment_id (FK)`, `quantity` (INTEGER).
---
## 3. Junction / Many-to-Many Tables
### 3.1 EquipmentGroupItems
| Column | Type | Constraints |
|--------|------|-------------|
| equipment_group_id | UUID | NOT NULL, FK → EquipmentGroup(id) ON DELETE CASCADE |
| equipment_id | UUID | NOT NULL, FK → Equipment(id) ON DELETE CASCADE |
| quantity | INTEGER | NOT NULL |
| PRIMARY KEY (equipment_group_id, equipment_id) |
### 3.2 EquipmentTags
| Column | Type | Constraints |
|--------|------|-------------|
| equipment_id | UUID | NOT NULL, FK → Equipment(id) ON DELETE CASCADE |
| tag_id | UUID | NOT NULL, FK → Tag(id) ON DELETE CASCADE |
| PRIMARY KEY (equipment_id, tag_id) |
### 3.3 ProjectTags
| Column | Type | Constraints |
|--------|------|-------------|
| project_id | UUID | NOT NULL, FK → Project(id) ON DELETE CASCADE |
| tag_id | UUID | NOT NULL, FK → Tag(id) ON DELETE CASCADE |
| PRIMARY KEY (project_id, tag_id) |
### 3.4 CrewTags
| Column | Type | Constraints |
|--------|------|-------------|
| crew_id | UUID | NOT NULL, FK → Crew(id) ON DELETE CASCADE |
| tag_id | UUID | NOT NULL, FK → Tag(id) ON DELETE CASCADE |
| PRIMARY KEY (crew_id, tag_id) |
---
## 4. Migration Strategy
We use Alembic for database migrations. The migration environment is configured with `async` support for SQLAlchemy (since our backend uses async sessions).
Initial migration creates all tables with UUID generation appropriate to the dialect:
- PostgreSQL: `server_default=func.gen_random_uuid()`
- SQLite: a Python side UUID generation on insert (via model default)
For development, SQLite will be used; however, tests should run against PostgreSQL to catch dialect-specific issues early.
---
## 5. Key Database Constraints
- **Unique constraints:** User `email` globally; Equipment `barcode` and `qr_code` per account (not globally, since multi-tenant). We'll make `(account_id, barcode)` unique if needed.
- **Foreign key cascades:** Sub-objects (subprojects, functions, equipment groups, crew assignments) should be deleted when the parent project is deleted (CASCADE). AuditLog and other history are kept (NO ACTION or SET NULL).
- **Check constraints:** Project `planperiod_end >= planperiod_start`, etc.
- **Default values:** Most ENUMs have a sensible default.
-14
View File
@@ -1,14 +0,0 @@
# Environment Variables
## Required
| Variable | Description | Default |
|---|---|---|
## Optional
| Variable | Description | Default |
|---|---|---|
## .env.example
```
# Copy to .env and fill in values
```
-12
View File
@@ -1,12 +0,0 @@
# Healthcheck
## Endpoint
## Expected Response
## How to Check
```bash
curl http://localhost:PORT/health
```
## Troubleshooting
-20
View File
@@ -1,20 +0,0 @@
# Known Errors
## Open Errors
- (none)
## Resolved Errors
- (none)
## Won't Fix
- (none)
## Error Log Format
```
### Error: <title>
- Status: open | in_progress | resolved | wont_fix
- Discovered: <timestamp>
- Severity: critical | high | medium | low
- Context: <description>
- Resolution: <how it was fixed or workaround>
```
-44
View File
@@ -1,44 +0,0 @@
# Next Steps
## Completed Phases
- Phase 0: Project Setup (T001-T003)
- Phase 1: Auth System (T004-T008)
- Phase 2: Contacts & Tags (T009-T011)
- Phase 3: Equipment Catalog (T012-T014)
- Phase 4: Crew Management (T015-T017)
- Phase 5: Vehicle Fleet (T018-T020)
## Current Phase: 6 Projects
### Completed in Phase 6
- **T021**: DB-Modelle Project-Hierarchie ✅
- **T022**: Project CRUD API ✅
- **T023**: Project List & Detail UI ✅
### Priority Tasks
1. **T024**: Project function group and function editor UI
- Inside project detail, allow CRUD of function groups and functions
- Drag-and-drop reordering
- Time period inheritance from group
- Amount, rate fields
- Type: crew, equipment, transport
- **Estimated**: 14h | **Depends on**: T023 ✅
2. **T025**: Create database models for ProjectEquipment and ProjectCrew
- Models: ProjectEquipmentGroup, ProjectEquipment (quantity, price, discount)
- ProjectCrew (with function link, crew_id, period)
- Alembic migration
- **Depends on**: T021
3. **T026**: Implement project equipment group and items API
4. **T027**: Project equipment management UI
5. **T028**: Implement crew assignment API
6. **T029**: Crew assignment UI in project
7. **T030**: Implement vehicle assignment API and UI
## Implementation Notes
- Use existing patterns from contacts/equipment modules: models → schemas → router → frontend pages
- All endpoints must use tenant isolation (account_id filter)
- All endpoints must use RBAC permissions via require_permission dependency
- Frontend uses existing API client, Zustand for auth, React state for data
- FunctionGroupEditor.tsx already exists as initial component (from T023)
-21
View File
@@ -1,21 +0,0 @@
{
"mode": "planning_only",
"allowed_actions": [
"read",
"analyze",
"write_specs",
"write_plan",
"ask_user"
],
"blocked_actions": [
"edit_source_code",
"install_dependency",
"run_deploy",
"push",
"delete"
],
"requires_user_approval_to_enter": [
"implementation_allowed",
"deployment_allowed"
]
}
-82
View File
@@ -1,82 +0,0 @@
{
"project": {
"name": "rentman-clone",
"root": "/a0/usr/workdir/dev-projects/rentman-clone",
"git_provider": "local",
"repo_url": "",
"default_branch": "main",
"active_branch": "main",
"deployment_target": "local-development"
},
"phase": {
"current": "implementation",
"previous": "architecture",
"next": "runtime_verification",
"blocked": false,
"blocker": null
},
"progress": {
"requirements_done": true,
"design_done": true,
"tasks_done": true,
"implementation_started": true,
"implementation_progress": {
"completed_phases": [
0,
1,
2,
3,
4,
5,
6
],
"completed_tasks": [
"T001",
"T002",
"T003",
"T004",
"T005",
"T006",
"T007",
"T008",
"T009",
"T010",
"T011",
"T012",
"T013",
"T014",
"T015",
"T016",
"T017",
"T018",
"T019",
"T020",
"T021",
"T022",
"T023",
"T024",
"T025"
],
"current_phase": 6,
"next_task": "T026"
},
"tests_run": true,
"runtime_verified": false,
"deployment_docs_ready": false,
"release_ready": false
},
"quality": {
"open_errors": 0,
"open_risks": 0,
"last_test_result": "passed",
"last_runtime_result": "not_run",
"last_artifact_check": "not_run",
"score": 0
},
"next_action": {
"summary": "Phase 6: Projects (T022-T030). T025 completed: Created DB models for ProjectEquipmentGroup, ProjectEquipment, ProjectCrew with migration. Next: T026 Implement project equipment group and items API.",
"owner_agent": "implementation_engineer",
"required_skill": "implementation-loop"
},
"plan_mode": "implementation_allowed"
}
-1092
View File
File diff suppressed because it is too large Load Diff
-241
View File
@@ -1,241 +0,0 @@
# Resume Rentman Clone (abgeschlossener Stand)
> **Letzte Aktualisierung:** 2026-05-31 13:10
> **Zuständiger Agent:** A0 Software Orchestrator
> **Nächster Agent kann nahtlos fortfahren.**
---
## Projektübersicht
**Ziel:** Vollständiger Nachbau von Rentman.io (Cloud-basierte Vermietungs- und Event-Management-Plattform) mit ≥90 % Funktionsumfang.
**Ansatz:** Projektzentrisch jedes Modul gehört zu einem Projekt.
**Status:** Implementierungsphase (plan_mode: implementation_allowed).
---
## Bisheriger Fortschritt
### ✅ Abgeschlossene Phasen (02)
| Phase | Tasks | Inhalt |
|-------|-------|--------|
| 0 Foundation | T001T003 | Projektstruktur, Docker Compose, FastAPI-Grundgerüst, React-Frontend mit Vite/Tailwind/Router/Zustand |
| 1 Auth-System | T004T008 | Account/User/Role-Modelle, JWT-Auth (Login/Register/Refresh), RBAC-Middleware, Admin-API für User & Roles, Login/Register/Admin-UI |
| 2 Contacts & Tags | T009T011 | Contact- und Tag-Modelle mit M2M, CRUD-API für Contacts (mit Suche, Typfilter, Tags), Tags-API, Contacts-UI (Liste, Formular, Detailseite) |
### 🔜 Nächste Phase (3) Equipment Catalog
| Task | Beschreibung |
|------|-------------|
| T012 | Datenbankmodelle: Equipment, StockLocation, EquipmentGroup (M2M) |
| T013 | Equipment-CRUD-API mit Suche, Filter, Barcode/QR-Generierung, CSV-Import/Export |
| T014 | Equipment-UI: Liste (Grid/Table-Toggle), Detailseite, Formular, Barcode-Druck |
**Offene Tasks:** T012T020 (Equipment, Crew, Vehicles), T021+ (Projektmanagement), Finanzen, Scheduling, Reporting, PWA etc. (siehe `todo.md` und `task_graph.json`).
---
## Technologie-Stack
### Backend (`/backend`)
- **Framework:** FastAPI (Python 3.12+, async)
- **ORM:** SQLAlchemy 2.0 (async engine mit aiosqlite für Dev)
- **Migrationen:** Alembic (render_as_batch für SQLite)
- **Auth:** JWT (python-jose) + Passwort-Hashing (passlib bcrypt)
- **Config:** Pydantic Settings (`.env` / `.env.example`)
- **Datenbank:** SQLite (`backend/data/rentman.db`) für Dev, PostgreSQL 16 für Prod geplant
- **Task Queue:** Celery + Redis (später)
- **Datei-Storage:** MinIO (Docker)
### Frontend (`/frontend`)
- **Framework:** React 19 + TypeScript
- **Build:** Vite
- **Styling:** Tailwind CSS 4 + Radix UI Primitives
- **State Management:** Zustand (Auth-Store) + TanStack React Query (API-Daten)
- **Routing:** React Router 7
- **HTTP-Client:** Axios mit Interceptor (Access-Token, Auto-Refresh)
- **Icons:** Lucide React
### Infrastruktur
- **Docker Compose:** Services: backend, frontend, redis, minio
- **Makefile:** Befehle: `dev`, `backend-dev`, `frontend-dev`, `migrate`, etc.
---
## Projektstruktur
```
rentman-clone/
├── backend/
│ ├── app/
│ │ ├── main.py # FastAPI-App, Lifespan, CORS
│ │ ├── core/
│ │ │ ├── config.py # Settings
│ │ │ └── security.py # JWT + bcrypt
│ │ ├── db/
│ │ │ ├── base.py # SQLAlchemy Base
│ │ │ └── session.py # AsyncEngine + Session
│ │ ├── models/ # SQLAlchemy-Modelle
│ │ │ ├── account.py
│ │ │ ├── user.py
│ │ │ ├── role.py
│ │ │ ├── contact.py # + Tag M2M
│ │ │ └── tag.py
│ │ ├── schemas/ # Pydantic-Schemas
│ │ │ ├── auth.py
│ │ │ ├── user.py
│ │ │ ├── role.py
│ │ │ └── contact.py
│ │ ├── api/v1/
│ │ │ ├── router.py # Registriert alle Sub-Router
│ │ │ ├── health.py
│ │ │ ├── auth.py
│ │ │ ├── users.py
│ │ │ ├── roles.py
│ │ │ ├── contacts.py
│ │ │ └── tags.py
│ │ └── api/deps.py # Dependency: get_current_user, require_permission
│ ├── alembic/
│ │ ├── env.py # Async + Batch
│ │ └── versions/ # Migrationen
│ ├── data/ # SQLite-DB
│ ├── requirements.txt
│ ├── Dockerfile
│ └── .env.example
├── frontend/
│ ├── src/
│ │ ├── main.tsx # Einstieg, QueryClient, Router
│ │ ├── App.tsx # Routing
│ │ ├── components/
│ │ │ ├── AppLayout.tsx # Sidebar, Header, Navigation
│ │ │ ├── ProtectedRoute.tsx
│ │ │ └── ContactForm.tsx
│ │ ├── pages/
│ │ │ ├── Home.tsx
│ │ │ ├── Login.tsx
│ │ │ ├── Register.tsx
│ │ │ ├── Dashboard.tsx
│ │ │ ├── Users.tsx # Admin
│ │ │ ├── Roles.tsx # Admin
│ │ │ ├── Contacts.tsx
│ │ │ ├── ContactsNew.tsx
│ │ │ ├── ContactsEdit.tsx
│ │ │ └── ContactDetail.tsx
│ │ ├── services/
│ │ │ └── api.ts # Axios-Client mit Token-Interceptor
│ │ ├── stores/
│ │ │ └── authStore.ts # Zustand Auth-Store (persistiert)
│ │ └── lib/
│ │ └── utils.ts # cn()-Funktion
│ ├── package.json
│ ├── vite.config.ts
│ └── Dockerfile
├── docker-compose.yml
├── Makefile
├── README.md
└── .a0/ # Orchestrator-Artefakte
├── project_state.json
├── requirements.md
├── architecture.md
├── design.md
├── task_graph.json
├── decisions.md
├── todo.md
├── worklog.md
├── current_status.md
├── next_steps.md
├── known_errors.md
└── resume.md ← DIESE DATEI
```
---
## Wichtige Code-Patterns & Konventionen
### Backend
1. **Model → Schema → Router → Frontend:** Dieses Pattern für jedes neue Modul verwenden.
2. **Tenant-Isolation:** Alle Datenbankabfragen müssen `account_id`-Filter enthalten. Der aktuelle User wird aus der JWT extrahiert (`get_current_user`) und liefert `current_user.account_id`.
3. **RBAC:** Jeder Endpunkt mit `require_permission("<resource>:<action>")` schützen. Vorhandene Permissions: `users:read/write/delete`, `roles:read/write`, `contacts:read/write/delete`, `tags:read/write`, `equipment:*` (noch zu vergeben).
4. **UUIDs:** Alle Primärschlüssel sind UUIDs (Python `uuid.uuid4()`).
5. **Migrationen:** Mit `alembic revision --autogenerate` erstellen, `render_as_batch=True` in env.py beachten. Nach Erstellung im `versions/`-Ordner manuell anpassen.
6. **API-Paginierung:** Standard: `limit` und `offset` als Query-Parameter, Antwort mit `items`-Array und `total`-Count.
### Frontend
1. **Auth-Store:** `authStore` enthält `token`, `user`, `isAuthenticated`. Bei Login/Refresh gespeichert, bei Logout gelöscht.
2. **API-Client:** `services/api.ts` Axios-Instanz mit `baseURL`, Access-Token-Interceptor (fügt `Authorization`-Header hinzu) und 401-Auto-Refresh (ruft `/auth/refresh` auf).
3. **Protected Route:** `<ProtectedRoute permission="...">` kapselt Zugriffsschutz. Umleitet auf `/login` wenn nicht authentifiziert, zeigt Fehler wenn Permission fehlt.
4. **React Query:** Für Datenabrufe `useQuery`, für Mutationen `useMutation` verwenden (z.B. `useMutation({ mutationFn: ... })`).
5. **Tailwind:** Utility-Klassen verwenden, keine eigenen CSS-Dateien. Theme-Farben im `index.css` definiert.
6. **Neue Seiten:** In `App.tsx` Route hinzufügen, ggf. mit ProtectedRoute. Sidebar-Links in `AppLayout.tsx` ergänzen.
---
## Startanweisungen
### Voraussetzungen
- Docker und Docker Compose installiert
- Python 3.12+, Node.js 22+
### Entwicklung starten
```bash
cd /a0/usr/workdir/dev-projects/rentman-clone
# Abhängigkeiten installieren
make install-backend # pip install -r backend/requirements.txt
make install-frontend # npm install
# Backend starten (uvicorn auf Port 8000)
make backend-dev
# Frontend starten (Vite auf Port 5173)
make frontend-dev
# Oder alles mit Docker:
docker compose up
```
### Datenbank-Migrationen
```bash
cd backend
# Neue Migration erzeugen
alembic revision --autogenerate -m "beschreibung"
# Migration anwenden
alembic upgrade head
```
### API testen
```bash
# Registrierung
curl -X POST http://localhost:8000/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"account_name":"Testfirma", "full_name":"Admin", "email":"admin@test.de", "password":"test123"}'
# Login
curl -X POST http://localhost:8000/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"admin@test.de", "password":"test123"}'
# Mit Token
curl http://localhost:8000/api/v1/auth/me \
-H "Authorization: Bearer <access_token>"
```
---
## Offene Punkte & Bekannte Einschränkungen
- **Keine automatischen Tests** vorhanden (später hinzufügen).
- **SQLite-Spezifika:** Einige PostgreSQL-Features (z.B. JSON-Felder in SQLite als Text) müssen bei Migration beachtet werden.
- **Image-Upload:** Noch nicht implementiert (Equipment-Bilder, Logos).
- **Frontend-Seiten** für Equipment, Crew, Vehicles, Projects, Finanzen etc. fehlen noch.
- **Auth-Flows** wie Passwort-Reset und E-Mail-Verifikation fehlen.
- **Internationalisierung** (i18n) nicht begonnen (DE+EN geplant).
---
## Nächste Aktion für neuen Agenten
1. **.a0/resume.md** (diese Datei) lesen, dann **.a0/project_state.json**, **.a0/task_graph.json**, **.a0/next_steps.md** und **.a0/requirements.md**.
2. **Plan Mode** prüfen: ist `implementation_allowed`.
3. **Phase 3 starten** mit Task **T012**: Equipment-Datenbankmodelle (models/equipment.py, StockLocation, EquipmentGroup), wie in `design.md` und `architecture.md` beschrieben.
4. Anschließend T013 (API) und T014 (UI).
5. Bei Problemen `known_errors.md` prüfen und ggf. erweitern.
6. Alle Änderungen in `.a0/`-Dateien dokumentieren (worklog, todo, current_status, next_steps).
**Viel Erfolg! 🚀**
-9
View File
@@ -1,9 +0,0 @@
# Rollback
## Pre-requisites
## Procedure
## Verification
## Emergency Contacts
-13
View File
@@ -1,13 +0,0 @@
# Runbook
## Start
## Stop
## Restart
## Logs
## Common Issues
## Monitoring
-15
View File
@@ -1,15 +0,0 @@
# Runtime Report
## Date
## Application Status
## Logs Summary
## Healthcheck
## Browser Smoke Test
## Errors Found
## Recommendations
-482
View File
@@ -1,482 +0,0 @@
[
{
"id": "T001",
"title": "Initialize project repository and Docker Compose",
"description": "Create project root with backend/ and frontend/ directories. Set up Docker Compose with services: backend (FastAPI), frontend (Vite React), PostgreSQL, Redis, MinIO. Create .env.example with required env vars.",
"dependencies": [],
"estimated_hours": 4,
"owner_agent": "implementation_engineer"
},
{
"id": "T002",
"title": "Set up FastAPI skeleton with SQLAlchemy and Alembic",
"description": "Initialize FastAPI app with async support, configure SQLAlchemy 2.0 engine and async session, set up Alembic for migrations, create initial migration with Account and User tables.",
"dependencies": ["T001"],
"estimated_hours": 8,
"owner_agent": "implementation_engineer"
},
{
"id": "T003",
"title": "Set up React SPA with Vite, Tailwind CSS, routing and Zustand",
"description": "Initialize Vite React project with TypeScript, add React Router, Zustand store, React Query, Tailwind CSS with Radix UI primitives. Create basic layout and routing structure.",
"dependencies": ["T001"],
"estimated_hours": 6,
"owner_agent": "implementation_engineer"
},
{
"id": "T004",
"title": "Create database models for Account, User, Role",
"description": "Implement SQLAlchemy models: Account, User (with password hashing), Role (with permissions JSON). Define relationships and tenant_id on User.",
"dependencies": ["T002"],
"estimated_hours": 4,
"owner_agent": "implementation_engineer"
},
{
"id": "T005",
"title": "Implement user authentication (login, refresh, register)",
"description": "Build auth endpoints: POST /auth/login (JWT access+refresh tokens), POST /auth/refresh, POST /auth/register (create tenant+admin). Use bcrypt, JWT handling.",
"dependencies": ["T004"],
"estimated_hours": 10,
"owner_agent": "implementation_engineer"
},
{
"id": "T006",
"title": "Implement RBAC middleware and permission check dependencies",
"description": "Create FastAPI dependency that extracts user from JWT, loads role permissions, and checks required permission. Add tenant filtering (account_id) to all queries.",
"dependencies": ["T005"],
"estimated_hours": 8,
"owner_agent": "implementation_engineer"
},
{
"id": "T007",
"title": "Create user and role management API (admin)",
"description": "CRUD endpoints for users: GET /users, POST /users, GET/PUT/DELETE /users/{id}. Role management: GET/PUT roles. Invitation workflow with email activation link.",
"dependencies": ["T006"],
"estimated_hours": 8,
"owner_agent": "implementation_engineer"
},
{
"id": "T008",
"title": "Login and user management UI",
"description": "Build login page, registration page, password reset flow. Admin pages for user list, create/edit user, role matrix editor. Route guards based on auth state.",
"dependencies": ["T003", "T005", "T007"],
"estimated_hours": 12,
"owner_agent": "implementation_engineer"
},
{
"id": "T009",
"title": "Create database models for Contacts and Tags",
"description": "SQLAlchemy models for Contact (company/person) and Tag. Create Alembic migration.",
"dependencies": ["T004"],
"estimated_hours": 3,
"owner_agent": "implementation_engineer"
},
{
"id": "T010",
"title": "Implement contacts CRUD API",
"description": "REST endpoints for contacts: GET /contacts (with search/filter), POST /contacts, GET/PUT/DELETE /contacts/{id}. Return associated projects.",
"dependencies": ["T009", "T006"],
"estimated_hours": 6,
"owner_agent": "implementation_engineer"
},
{
"id": "T011",
"title": "Contacts management UI (list, detail, form)",
"description": "Pages: contact list with search/filter, contact detail page showing linked projects, create/edit form for company/person.",
"dependencies": ["T010"],
"estimated_hours": 8,
"owner_agent": "implementation_engineer"
},
{
"id": "T012",
"title": "Create database models for Equipment catalog and StockLocation",
"description": "SQLAlchemy models: Equipment (with barcode, qr_code, category, status), StockLocation, and EquipmentGroup (Bundle) with M2M items. Migrations.",
"dependencies": ["T004"],
"estimated_hours": 4,
"owner_agent": "implementation_engineer"
},
{
"id": "T013",
"title": "Implement Equipment catalog API",
"description": "CRUD for equipment: GET /equipment (search, filter, pagination), POST /equipment, GET/PUT/DELETE /equipment/{id}. Auto-generate barcode/QR code. Batch import/export CSV.",
"dependencies": ["T012", "T006"],
"estimated_hours": 10,
"owner_agent": "implementation_engineer"
},
{
"id": "T014",
"title": "Equipment catalog UI",
"description": "Equipment list with grid/table toggle, search/filter by category, status, location. Detail page with image, edit form. Barcode/QR code display and print label.",
"dependencies": ["T013"],
"estimated_hours": 12,
"owner_agent": "implementation_engineer"
},
{
"id": "T015",
"title": "Create database models for Crew and CrewAvailability",
"description": "SQLAlchemy models: Crew (internal/freelancer, skills JSON), CrewAvailability (availability/block periods). Migrations.",
"dependencies": ["T004"],
"estimated_hours": 3,
"owner_agent": "implementation_engineer"
},
{
"id": "T016",
"title": "Implement Crew catalog and availability API",
"description": "CRUD for crew members, GET /crew/{id}/availability (timeline). Endpoints to manage availability slots.",
"dependencies": ["T015", "T006"],
"estimated_hours": 8,
"owner_agent": "implementation_engineer"
},
{
"id": "T017",
"title": "Crew management UI",
"description": "Crew list, detail page, edit form. Availability calendar per crew member. Skill tags editor.",
"dependencies": ["T016"],
"estimated_hours": 10,
"owner_agent": "implementation_engineer"
},
{
"id": "T018",
"title": "Create database models for Vehicles and VehicleAssignment",
"description": "SQLAlchemy models: Vehicle (type, payload, volume), VehicleAssignment (project, period, driver). Migrations.",
"dependencies": ["T004"],
"estimated_hours": 3,
"owner_agent": "implementation_engineer"
},
{
"id": "T019",
"title": "Implement Vehicle catalog and assignment API",
"description": "CRUD for vehicles, GET /vehicles/{id}/assignments. Endpoint for availability check.",
"dependencies": ["T018", "T006"],
"estimated_hours": 6,
"owner_agent": "implementation_engineer"
},
{
"id": "T020",
"title": "Vehicle management UI",
"description": "Vehicle list, detail, edit form. Calendar for assignments. Drag-and-drop to project (later).",
"dependencies": ["T019"],
"estimated_hours": 8,
"owner_agent": "implementation_engineer"
},
{
"id": "T021",
"title": "Create database models for Project hierarchy",
"description": "SQLAlchemy models: Project, Subproject, ProjectFunctionGroup, ProjectFunction (with type enum). Migrations. Define cascades.",
"dependencies": ["T004"],
"estimated_hours": 6,
"owner_agent": "implementation_engineer"
},
{
"id": "T022",
"title": "Implement Project CRUD API",
"description": "Endpoints: GET/POST /projects (filter, sort, paginate), GET/PUT/DELETE /projects/{id}. Include subproject endpoints, function group and function endpoints within project. Auto-generate project number. Status workflow with history.",
"dependencies": ["T021", "T006", "T010"],
"estimated_hours": 16,
"owner_agent": "implementation_engineer"
},
{
"id": "T023",
"title": "Project list and detail UI",
"description": "Project list page with advanced filters (status, customer, date range), sortable columns, pagination. Detail page with tabs: Overview, Functions, Equipment, Crew, Vehicles, Financial, Documents. Inline editing for some fields.",
"dependencies": ["T022"],
"estimated_hours": 20,
"owner_agent": "implementation_engineer"
},
{
"id": "T024",
"title": "Project function group and function editor UI",
"description": "Inside project detail, allow CRUD of function groups and functions (crew/equipment/transport). Drag-and-drop reordering. Time period inheritance from group. Amount, rate fields.",
"dependencies": ["T023"],
"estimated_hours": 14,
"owner_agent": "implementation_engineer"
},
{
"id": "T025",
"title": "Create database models for ProjectEquipment and ProjectCrew",
"description": "Models: ProjectEquipmentGroup, ProjectEquipment (with equipment link, quantity, price, discount), ProjectCrew (with function link, crew_id, period). Migrations.",
"dependencies": ["T021"],
"estimated_hours": 4,
"owner_agent": "implementation_engineer"
},
{
"id": "T026",
"title": "Implement project equipment group and items API",
"description": "Endpoints for project equipment groups: CRUD under /projects/{id}/equipmentgroups. Add equipment items to group (select from catalog or free-text). Include availability check.",
"dependencies": ["T025", "T022", "T013"],
"estimated_hours": 8,
"owner_agent": "implementation_engineer"
},
{
"id": "T027",
"title": "Project equipment management UI",
"description": "Within project, manage equipment groups and items. Search/autocomplete from catalog, set quantity, price, discount. Visual availability indicator. Drag-and-drop to add items (future).",
"dependencies": ["T026"],
"estimated_hours": 12,
"owner_agent": "implementation_engineer"
},
{
"id": "T028",
"title": "Implement crew assignment API",
"description": "Endpoints for assigning crew to project functions. POST /projects/{id}/functiongroups/{gid}/functions/{fid}/crew (assign crew member). Availability conflict detection.",
"dependencies": ["T025", "T016"],
"estimated_hours": 8,
"owner_agent": "implementation_engineer"
},
{
"id": "T029",
"title": "Crew assignment UI in project",
"description": "In project crew tab, show crew functions and assigned crew. Search/select crew, show availability conflicts. Drag-and-drop crew onto function.",
"dependencies": ["T028"],
"estimated_hours": 10,
"owner_agent": "implementation_engineer"
},
{
"id": "T030",
"title": "Implement vehicle assignment API and UI",
"description": "Endpoints to assign vehicles to project (similar to crew). UI under project transport tab.",
"dependencies": ["T025", "T019"],
"estimated_hours": 6,
"owner_agent": "implementation_engineer"
},
{
"id": "T031",
"title": "Create database models for Quotes, Invoices, Templates",
"description": "SQLAlchemy models: Quote, Invoice, Template. Migrations.",
"dependencies": ["T004"],
"estimated_hours": 3,
"owner_agent": "implementation_engineer"
},
{
"id": "T032",
"title": "Implement Quote and Invoice API",
"description": "Endpoints: POST /projects/{id}/quotes (generate from project items), GET/PUT quotes. POST invoices from quotes. Status transitions. Number auto-generation.",
"dependencies": ["T031", "T022"],
"estimated_hours": 10,
"owner_agent": "implementation_engineer"
},
{
"id": "T033",
"title": "Financial documents UI",
"description": "Quote list, create/edit form, PDF preview download. Invoice list with status badges. Payment status tracking. Email sending dialog.",
"dependencies": ["T032"],
"estimated_hours": 14,
"owner_agent": "implementation_engineer"
},
{
"id": "T034",
"title": "Template engine for quotes/invoices",
"description": "Backend: HTML template with placeholders, fill with project data, render to PDF (WeasyPrint). Admin UI for template CRUD with editor.",
"dependencies": ["T031"],
"estimated_hours": 10,
"owner_agent": "implementation_engineer"
},
{
"id": "T035",
"title": "Create database models for ProjectRequest",
"description": "Models: ProjectRequest, ProjectRequestEquipment. Migrations.",
"dependencies": ["T004"],
"estimated_hours": 2,
"owner_agent": "implementation_engineer"
},
{
"id": "T036",
"title": "Implement project request API and conversion",
"description": "Public endpoint POST /requests (without auth). Authenticated endpoints to list, update status, and convert request to project (copy fields, equipment items).",
"dependencies": ["T035", "T022"],
"estimated_hours": 8,
"owner_agent": "implementation_engineer"
},
{
"id": "T037",
"title": "Project request public form and admin UI",
"description": "Public page with form to submit a project request. Admin page to view requests, convert to project with confirmation dialog.",
"dependencies": ["T036"],
"estimated_hours": 8,
"owner_agent": "implementation_engineer"
},
{
"id": "T038",
"title": "Create database models for Documents",
"description": "SQLAlchemy model: Document (file metadata). Migrations. Configure file storage (local for dev, S3 for prod).",
"dependencies": ["T004"],
"estimated_hours": 3,
"owner_agent": "implementation_engineer"
},
{
"id": "T039",
"title": "Implement document upload/download API",
"description": "Endpoints: POST /documents (multipart upload), GET /documents/{id} (download or signed URL), DELETE. Associate with project.",
"dependencies": ["T038", "T006"],
"estimated_hours": 6,
"owner_agent": "implementation_engineer"
},
{
"id": "T040",
"title": "Document management UI",
"description": "File list within project, drag-and-drop upload, file preview (images, PDFs), delete with confirmation.",
"dependencies": ["T039"],
"estimated_hours": 6,
"owner_agent": "implementation_engineer"
},
{
"id": "T041",
"title": "Implement scheduling and timeline API",
"description": "Endpoints to fetch project timelines (Gantt data), resource utilization timelines (equipment, crew, vehicles availability overlaps).",
"dependencies": ["T022", "T013", "T016", "T019"],
"estimated_hours": 10,
"owner_agent": "implementation_engineer"
},
{
"id": "T042",
"title": "Gantt timeline and resource views UI",
"description": "Interactive Gantt chart for projects (horizontal bars, drag-resize to change dates). Resource utilization view with colored blocks. Zoom/pan.",
"dependencies": ["T041"],
"estimated_hours": 18,
"owner_agent": "implementation_engineer"
},
{
"id": "T043",
"title": "Implement iCal feed and external calendar integration",
"description": "Generate per-user iCal feed URL containing project events. Allow subscribing in Google Calendar etc.",
"dependencies": ["T022"],
"estimated_hours": 6,
"owner_agent": "implementation_engineer"
},
{
"id": "T044",
"title": "Create Task and TaskAssignment models and API",
"description": "Models for to-do tasks inside projects. CRUD endpoints with status (open, in progress, done), assignee.",
"dependencies": ["T004", "T021"],
"estimated_hours": 6,
"owner_agent": "implementation_engineer"
},
{
"id": "T045",
"title": "Task management UI",
"description": "Task list in project, personal to-do page, drag to change status, email reminders for due tasks.",
"dependencies": ["T044"],
"estimated_hours": 8,
"owner_agent": "implementation_engineer"
},
{
"id": "T046",
"title": "Reporting and analytics API",
"description": "Endpoints for financial KPIs, equipment utilization stats, project statistics. Use aggregation queries.",
"dependencies": ["T032", "T013"],
"estimated_hours": 8,
"owner_agent": "implementation_engineer"
},
{
"id": "T047",
"title": "Dashboard and reports UI",
"description": "Dashboard with configurable widgets (active projects, revenue, equipment utilization, open invoices). Drill-down links. Report pages with charts (using recharts). Export to CSV.",
"dependencies": ["T046"],
"estimated_hours": 16,
"owner_agent": "implementation_engineer"
},
{
"id": "T048",
"title": "Implement webhook management and dispatch",
"description": "Webhook CRUD for admins. Background task (Celery) to dispatch webhooks on configured events (project created, status changed, invoice created, etc.). Retry logic and log.",
"dependencies": ["T006", "T005"],
"estimated_hours": 8,
"owner_agent": "implementation_engineer"
},
{
"id": "T049",
"title": "Webhook configuration UI",
"description": "Admin page to manage webhooks: add URL, select events, secret key, test trigger, view delivery logs.",
"dependencies": ["T048"],
"estimated_hours": 6,
"owner_agent": "implementation_engineer"
},
{
"id": "T050",
"title": "Create audit log model and middleware",
"description": "AuditLog model. Middleware or event hook to record all CUD operations with user, action, entity, details. API to retrieve audit log (admin only).",
"dependencies": ["T004", "T006"],
"estimated_hours": 6,
"owner_agent": "implementation_engineer"
},
{
"id": "T051",
"title": "Audit log UI",
"description": "Admin page with searchable, filterable audit log table. Show who did what and when.",
"dependencies": ["T050"],
"estimated_hours": 4,
"owner_agent": "implementation_engineer"
},
{
"id": "T052",
"title": "Internationalization (i18n) setup",
"description": "Set up i18next in frontend, add DE and EN translation files. Backend: serve translations or use frontend-only. Implement language switcher.",
"dependencies": ["T003"],
"estimated_hours": 8,
"owner_agent": "implementation_engineer"
},
{
"id": "T053",
"title": "PWA (Progressive Web App) configuration and offline support for mobile",
"description": "Add Service Worker with Workbox, manifest.json, offline caching for project data, equipment lists, pack lists. Use IndexedDB for local storage. Implement sync logic.",
"dependencies": ["T003", "T022"],
"estimated_hours": 16,
"owner_agent": "implementation_engineer"
},
{
"id": "T054",
"title": "Barcode/QR scanning on mobile (PWA)",
"description": "Integrate Capacitor Barcode Scanner plugin. Implement camera scanning UI. On scan: show equipment info and allow status change, packing/unpacking.",
"dependencies": ["T053", "T014"],
"estimated_hours": 8,
"owner_agent": "implementation_engineer"
},
{
"id": "T055",
"title": "Mobile-specific UI (pack list, availability check)",
"description": "Build mobile-optimized views: pack list with checkboxes (offline capable), quick availability search. Use responsive design.",
"dependencies": ["T053"],
"estimated_hours": 10,
"owner_agent": "implementation_engineer"
},
{
"id": "T056",
"title": "Backend API documentation (OpenAPI/Swagger)",
"description": "Ensure all endpoints have proper Pydantic schemas, descriptions, and examples. Generate OpenAPI spec. Write developer README.",
"dependencies": ["T002"],
"estimated_hours": 6,
"owner_agent": "implementation_engineer"
},
{
"id": "T057",
"title": "Integration tests for critical flows",
"description": "Write pytest tests for auth, project workflow, equipment assignment, quote/invoice generation. Use test database (SQLite).",
"dependencies": ["T005", "T022", "T026", "T032"],
"estimated_hours": 16,
"owner_agent": "implementation_engineer"
},
{
"id": "T058",
"title": "CI/CD pipeline (GitHub Actions)",
"description": "Set up GitHub Actions workflow: run linting, type checks, tests, build Docker images, push to registry. (Optional) deploy to Coolify via webhook.",
"dependencies": ["T001"],
"estimated_hours": 8,
"owner_agent": "implementation_engineer"
},
{
"id": "T059",
"title": "Production deployment configuration (Coolify)",
"description": "Create Coolify service with Docker Compose, SSL, environment variables, database backup schedule. Document deployment steps.",
"dependencies": ["T001"],
"estimated_hours": 6,
"owner_agent": "implementation_engineer"
},
{
"id": "T060",
"title": "Performance optimization and final review",
"description": "Add database indexes missing, optimize heavy queries, implement Redis caching for dashboard stats, add pagination to all list endpoints. Final code review and cleanup.",
"dependencies": ["T047", "T041"],
"estimated_hours": 12,
"owner_agent": "implementation_engineer"
}
]
-14
View File
@@ -1,14 +0,0 @@
# Tasks
## Task List
| ID | Name | Complexity | Dependencies | Status |
|---|---|---|---|---|
## Task Details
### TASK-001: (example)
- **Complexity**: S
- **Dependencies**: none
- **Description**: ...
- **Definition of Done**: ...
-13
View File
@@ -1,13 +0,0 @@
# Test Report
## Date
## Test Commands
## Results
## Failures
## Coverage
## Known Issues
-44
View File
@@ -1,44 +0,0 @@
# Todo
## Open
- [ ] T025: Create database models for ProjectEquipment and ProjectCrew
- [ ] T026: Implement project equipment group and items API
- [ ] T027: Project equipment management UI
- [ ] T028: Implement crew assignment API
- [ ] T029: Crew assignment UI in project
- [ ] T030: Implement vehicle assignment API and UI
- [ ] T031-T060: Remaining tasks (Financial, Documents, Scheduling, PWA, etc.)
- [ ] Test and validate
- [ ] Verify runtime
- [ ] Prepare deployment
- [ ] Release audit
## In Progress
- (none)
## Done
- Project initialization
- T001: Initialize project repository and Docker Compose
- T002: Set up FastAPI skeleton with SQLAlchemy and Alembic
- T003: Set up React SPA with Vite, Tailwind CSS, routing and Zustand
- T004: Create database models for Account, User, Role
- T005: Implement user authentication (login, refresh, register)
- T006: Implement RBAC middleware and permission check dependencies
- T007: Create user and role management API (admin)
- T008: Login and user management UI
- T009: Create database models for Contacts and Tags
- T010: Implement contacts CRUD API
- T011: Contacts management UI (list, detail, form)
- T012: Create database models for Equipment catalog and StockLocation
- T013: Implement Equipment catalog API
- T014: Equipment catalog UI
- T015: Create database models for Crew and CrewAvailability
- T016: Implement Crew catalog and availability API
- T017: Crew management UI
- T018: Create database models for Vehicles and VehicleAssignment
- T019: Implement Vehicle catalog and assignment API
- T020: Vehicle management UI
- T021: Create database models for Project hierarchy
- T022: Implement Project CRUD API
- T023: Project list and detail UI
- T024: Project function group and function editor UI (function_type, start_date, end_date)
-29
View File
@@ -1,29 +0,0 @@
{
"forgejo": {
"available": false,
"can_create_repo": false,
"can_push": false,
"fallback": "document git steps and continue locally"
},
"coolify": {
"available": false,
"can_deploy": false,
"fallback": "prepare deploy/coolify.md and manual handoff"
},
"terminal": {
"available": true,
"can_run_tests": true
},
"browser": {
"available": false,
"fallback": "write manual browser QA checklist"
},
"git": {
"available": true,
"can_commit": true
},
"docker": {
"available": false,
"can_run_containers": false
}
}
-158
View File
@@ -1,158 +0,0 @@
# Worklog
## Log
| Timestamp | Phase | Work Block | Agent | Summary | Status |
|---|---|---|---|---|---|
| 2026-05-31 06:14 | init | 0 | a0_software_orchestrator | Project initialized | completed |
| 2026-05-31 12:32 | 0 | T001 | implementation_engineer | Created project structure, docker-compose.yml, .env.example, Makefile, README.md, .gitignore | completed |
| 2026-05-31 12:32 | 0 | T002 | implementation_engineer | Set up FastAPI app with async SQLAlchemy, Alembic migration (accounts, users), health endpoint | completed |
| 2026-05-31 12:32 | 0 | T003 | implementation_engineer | Initialized React+TS Vite project, Tailwind CSS, React Router, Zustand store, React Query, Radix UI, AppLayout, placeholder pages | completed |
| 2026-05-31 12:48 | 1 | T004 | implementation_engineer | Created Role model, updated User model with FK role_id, added Account.roles relationship, created Alembic migration for roles table, ran migrations | completed |
| 2026-05-31 12:48 | 1 | T005 | implementation_engineer | Implemented JWT security (core/security.py), auth schemas (schemas/auth.py), auth endpoints (POST /auth/register, POST /auth/login, POST /auth/refresh, GET /auth/me) with bcrypt + JWT | completed |
| 2026-05-31 12:48 | 1 | T006 | implementation_engineer | Created FastAPI dependencies (api/deps.py): get_current_user, get_current_active_user, require_permission with HTTPBearer JWT extraction, role permission check, and tenant filtering via account_id | completed |
| 2026-05-31 12:48 | 1 | T007 | implementation_engineer | Implemented user management API (GET/POST /users, GET/PUT/DELETE /users/{id}) and role management API (GET/POST /roles, PUT /roles/{id}) with admin permissions, tenant filtering, pagination | completed |
| 2026-05-31 12:48 | 1 | T008 | implementation_engineer | Completed Login/Register pages with API, ProtectedRoute component, auth-aware routing, admin Users/Roles pages with CRUD, AppLayout with conditional admin links, authStore with localStorage persistence, auto-refresh interceptor | completed |
| 2026-05-31 13:05 | 2 | T009 | implementation_engineer | Created Contact and Tag SQLAlchemy models, contact_tags M2M association table, updated Account model with contacts and tags relationships, generated and applied Alembic migration a5b1e93690ff | completed |
| 2026-05-31 13:05 | 2 | T010 | implementation_engineer | Created Pydantic schemas (schemas/contact.py), contacts CRUD router (api/v1/contacts.py) with search/filter/pagination/permissions, tags router (api/v1/tags.py) for listing/creating tags, registered both routers in api/v1/router.py | completed |
| 2026-05-31 13:05 | 2 | T011 | implementation_engineer | Created Contacts.tsx list page with search/filter/table/pagination, ContactForm.tsx shared component with company/person tabs, billing/shipping addresses, tag selector, ContactsNew.tsx create page, ContactsEdit.tsx edit page, ContactDetail.tsx detail page with info/addresses/notes/tags/projects placeholder, updated App.tsx with contacts routes, updated AppLayout.tsx with AddressBook sidebar link | completed |
## 2026-05-31 - T012: Equipment Catalog Datenbank-Modelle
**Status**: ✅ Abgeschlossen
### Erstellt
- `backend/app/models/equipment.py` Equipment-Modell mit allen Feldern
- `backend/app/models/stock_location.py` StockLocation-Modell
- `backend/app/models/equipment_group.py` EquipmentGroup mit M2M-Association-Table
- `backend/app/models/account.py` um back_populates-Beziehungen erweitert
- `backend/app/models/__init__.py` um neue Modelle erweitert
- `backend/alembic/versions/da13d44bd483_create_equipment_stocklocation_.py` Migration generiert und angewandt
### Nächster Task
**T013**: Equipment Catalog API (CRUD-Endpoints)
## 2026-05-31 - T013: Equipment Catalog API
**Status**: ✅ Abgeschlossen
### Erstellt
- Pydantic-Schemas (CRUD-Requests + Responses mit Location/Supplier-Refs)
- StockLocation-Schemas
- EquipmentGroup-Schemas mit Item-Mengen
- CRUD-Router mit search/filter/pagination/RBAC
- StockLocation-CRUD
- EquipmentGroup-CRUD mit Item-Management
- +3 neue Router registriert
### Nächster Task
**T014**: Equipment catalog UI (Frontend)
## 2026-05-31 - T014: Equipment Catalog UI
**Status**: ✅ Abgeschlossen
### Erstellt
- Formular mit Name, Kategorie, Status, Standort, Finanz-/Physik-Daten
- Liste mit Search, Kategorie/Status-Filter, Pagination, RBAC
- Create-Seite
- Edit-Seite
- Detailansicht mit allen Equipment-Feldern
- Routen für /equipment, /equipment/new, /equipment/:itemId, /equipment/:itemId/edit
### Nächster Task
**T015**: Crew Datenbank-Modelle
## 2026-05-31 - T015+T016: Crew Datenbank + API
**Status**: ✅ Abgeschlossen
### Erstellt T015
- Crew + CrewAvailability Modelle
- Alembic-Migration: 14f212e85ab6 (Tabellen: crew, crew_availabilities)
### Erstellt T016
- Pydantic-Schemas
- Router (Crew CRUD + Availabilities CRUD)
- Router in v1/router.py registriert
### Nächster Task
**T017**: Crew Frontend (UI)
## 2026-05-31 - T017: Crew Management UI
**Status**: ✅ Abgeschlossen
### Erstellt
-
- (Liste)
-
-
- (mit Availabilities-Anzeige)
- Crew-Routen registriert
### Nächster Task
**T018-T020**: Vehicles (Modelle → API → UI)
## 2026-05-31 - T018-T020: Vehicles (DB + API + UI)
**Status**: ✅ Abgeschlossen
### Erstellt T018
- Vehicle + VehicleAssignment Modelle (project_id ohne FK, da Projects später kommt)
- Alembic-Migration: 32b02c118683
- Account um vehicles-Beziehung erweitert
### Erstellt T019
- Pydantic-Schemas
- Router (Vehicle CRUD + Assignments)
- Router registriert
### Erstellt T020
-
- (Liste)
-
-
- (mit Assignments-Anzeige)
- Vehicle-Routen registriert
### Nächster Task
**T022**: Project CRUD API
## 2026-05-31 - T022: Project CRUD API
**Status**: ✅ Abgeschlossen
### Erstellt
- `backend/app/schemas/project.py` Pydantic-Schemas für Project, SubProject, ProjectFunctionGroup, ProjectFunction (Create/Update/Response/ListResponse-Varianten)
- `backend/app/api/v1/projects.py` Router mit vollständigen CRUD-Endpunkten:
- Project: GET (list mit search/filter/pagination), POST, GET/:id, PUT/:id, DELETE/:id
- SubProject: GET list, POST create, GET/:id, PUT/:id, DELETE/:id (unter /projects/{id}/subprojects)
- ProjectFunctionGroup: GET list, POST create, GET/:id, PUT/:id, DELETE/:id (unter /projects/{id}/function-groups)
- ProjectFunction: GET list, POST create, GET/:id, PUT/:id, DELETE/:id (unter /projects/{id}/function-groups/{fg_id}/functions)
- `backend/app/api/v1/router.py` Router registriert als projects_router
### Permission-Checks
- `projects:read` für GET-Endpunkte
- `projects:write` für POST/PUT-Endpunkte
- `projects:delete` für DELETE-Endpunkte
### Tenant-Isolation
- Alle Endpunkte filtern nach `current_user.account_id`
- SubProject/FunctionGroup/Function-Endpunkte validieren Projekt-Zugehörigkeit via account_id
### Syntax-Check
- `python -c "from app.api.v1.projects import router; print('OK')"` → OK
### Nächster Task
**T023**: Project list and detail UI
## 2026-05-31 - T025: ProjectEquipment and ProjectCrew Models + Migration
**Status**: ✅ Abgeschlossen
### Erstellt
- `backend/app/models/project.py` Added ProjectEquipmentGroup, ProjectEquipment, ProjectCrew SQLAlchemy models with relationships
- `backend/app/models/__init__.py` Imported new models
- `backend/alembic/versions/b183c967088f_add_project_equipment_and_crew.py` Alembic migration (autogenerated, applied)
### Checks
- Backend start: OK (no ImportError)
- Health: 200
- Auth: Registration + Login → Token
- Endpoints: /projects, /contacts, /equipment, /crew all return 200
### Nächster Task
**T026**: Implement project equipment group and items API
+95
View File
@@ -177,3 +177,98 @@ class ProjectFunctionListResponse(BaseModel):
total: int
page: int
size: int
# ===== ProjectEquipmentGroup Schemas =====
class ProjectEquipmentGroupCreateRequest(BaseModel):
"""Request body for creating an equipment group."""
name: str = Field(..., max_length=255)
sort_order: int = 0
start_date: datetime | None = None
end_date: datetime | None = None
class ProjectEquipmentGroupUpdateRequest(BaseModel):
"""Request body for updating an equipment group."""
name: str | None = Field(None, max_length=255)
sort_order: int | None = None
start_date: datetime | None = None
end_date: datetime | None = None
class ProjectEquipmentGroupResponse(BaseModel):
"""Public equipment group representation."""
id: str
name: str
project_id: str
sort_order: int = 0
start_date: datetime | None = None
end_date: datetime | None = None
model_config = {"from_attributes": True}
class ProjectEquipmentGroupListResponse(BaseModel):
"""Paginated list of equipment groups."""
items: list[ProjectEquipmentGroupResponse]
total: int
page: int
size: int
class ProjectEquipmentGroupDetailResponse(BaseModel):
"""Equipment group with nested items."""
id: str
name: str
project_id: str
sort_order: int = 0
start_date: datetime | None = None
end_date: datetime | None = None
items: list["ProjectEquipmentResponse"] = []
model_config = {"from_attributes": True}
# ===== ProjectEquipment Schemas =====
class ProjectEquipmentCreateRequest(BaseModel):
"""Request body for adding equipment to a project group."""
equipment_id: str | None = None
name: str = Field(..., max_length=255)
quantity: float = 1.0
price: float = 0.0
discount: float = 0.0
sort_order: int = 0
class ProjectEquipmentUpdateRequest(BaseModel):
"""Request body for updating project equipment."""
equipment_id: str | None = None
name: str | None = Field(None, max_length=255)
quantity: float | None = None
price: float | None = None
discount: float | None = None
sort_order: int | None = None
class ProjectEquipmentResponse(BaseModel):
"""Public project equipment representation."""
id: str
project_equipment_group_id: str
equipment_id: str | None = None
name: str
quantity: float = 1.0
price: float = 0.0
discount: float = 0.0
sort_order: int = 0
model_config = {"from_attributes": True}
class ProjectEquipmentListResponse(BaseModel):
"""Paginated list of project equipment."""
items: list[ProjectEquipmentResponse]
total: int
page: int
size: int
-9
View File
@@ -1,9 +0,0 @@
# Worklog
## T024 Function group and function editor UI improvements
- **Backend**: Added `start_date`, `end_date` columns to `project_function_groups` table (alembic migration `cfe638817921`). Added `function_type` column (String 50, default 'crew') to `project_functions` table.
- **Pydantic schemas**: Updated `ProjectFunctionGroupCreateRequest`, `ProjectFunctionGroupUpdateRequest`, `ProjectFunctionGroupResponse`, `ProjectFunctionCreateRequest`, `ProjectFunctionUpdateRequest`, `ProjectFunctionResponse` to include new fields.
- **Frontend**: Updated `FunctionGroupEditor.tsx` interfaces and state to support `function_type` dropdown (crew/equipment/transport), `start_date`, `end_date` date pickers for function groups. Added date display in group headers and function type badge in function rows.
- **Tests**: Backend health check passed (200). Frontend tsc --noEmit exit 0, vite build successful.
- **Commit**: `e14edd5` on master.