Files
leocrm/specs/current/requirements.md
T

122 lines
5.9 KiB
Markdown
Raw Normal View History

# leocrm — Requirements (v0.1, Mini-CRM, Single-Tenant)
## 1. Ziel
Ein schlankes, in sich geschlossenes Mini-CRM, das folgende Kern-Workflows abdeckt:
- Anmeldung (Login/Logout) per Session-Cookie
- Verwaltung von **Firmen** (Anlegen, Anzeigen, Bearbeiten, Löschen, Liste mit Suche)
- Verwaltung von **Kontaktpersonen** (Anlegen, Anzeigen, Bearbeiten, Löschen, Zuordnung zu Firma)
- Übersichts-Dashboard mit Anzahl Firmen, Kontakten und letzten Änderungen
Single-Tenant, eine Org, eine SQLite-Datei. Keine Rollen, keine Multi-User-Berechtigungen jenseits Login ja/nein.
## 2. Personas
- **Admin/Nutzer** (1 Person pro Org): meldet sich an, verwaltet Daten.
- **Entwickler**: betreibt die App lokal und in Coolify.
## 3. Funktionale Anforderungen (Must-Have)
| ID | Anforderung | Akzeptanzkriterium |
|----|-------------|---------------------|
| F-1 | Login-Formular | GET `/login` zeigt Formular; POST mit gültigen Credentials setzt Session-Cookie und leitet auf `/` weiter; ungültige Credentials → 401 + Fehlermeldung. |
| F-2 | Logout | POST `/logout` löscht Session und leitet auf `/login` weiter. |
| F-3 | Auth-Schutz | Alle Seiten außer `/login` und `/api/health` erfordern eine aktive Session; ohne Session → 302 → `/login`. |
| F-4 | Dashboard | GET `/` zeigt Anzahl Companies, Anzahl Contacts, letzte 5 Änderungen (Companies + Contacts). |
| F-5 | Companies-Liste | GET `/companies` zeigt Tabelle mit Name, Stadt, Land, Anzahl Contacts; Volltextsuche über Name/Stadt filtert. |
| F-6 | Company anlegen | GET `/companies/new` zeigt Formular; POST erstellt Datensatz, Redirect auf Detail. |
| F-7 | Company-Detail | GET `/companies/{id}` zeigt Stammdaten + Liste der zugeordneten Contacts + Link „Contact hinzufügen". |
| F-8 | Company bearbeiten | GET `/companies/{id}/edit` zeigt vorausgefülltes Formular; PATCH speichert, Redirect auf Detail. |
| F-9 | Company löschen | POST `/companies/{id}/delete` löscht inkl. zugeordneter Contacts (cascade). |
| F-10 | Contact anlegen | GET `/contacts/new?company_id={id}` zeigt Formular; POST erstellt Datensatz mit FK auf Company. |
| F-11 | Contact-Detail | GET `/contacts/{id}` zeigt alle Felder + Link zur Firma. |
| F-12 | Contact bearbeiten | GET `/contacts/{id}/edit` zeigt Formular; PATCH speichert. |
| F-13 | Contact löschen | POST `/contacts/{id}/delete` löscht. FK-Company bleibt erhalten. |
| F-14 | API parallel zu HTML | Für jede HTML-Aktion gibt es einen äquivalenten JSON-API-Endpoint (siehe API-Spec unten), damit Tests Headless durchlaufen können. |
| F-15 | Health-Endpoint | GET `/api/health` → 200 `{"status":"ok","db":"ok"}`. |
| F-16 | Demo-Seed | Beim ersten Start wird automatisch 1 Admin-User (`admin`/`admin`), 2 Beispiel-Firmen und 3 Beispiel-Kontakte angelegt, falls DB leer. |
## 4. Datenmodell
```
User
id INTEGER PK
username TEXT UNIQUE NOT NULL
password_hash TEXT NOT NULL
created_at TIMESTAMP DEFAULT now
Company
id INTEGER PK
name TEXT NOT NULL
street TEXT
zip TEXT
city TEXT
country TEXT DEFAULT 'DE'
email TEXT
phone TEXT
website TEXT
notes TEXT
created_at TIMESTAMP DEFAULT now
updated_at TIMESTAMP DEFAULT now
Contact
id INTEGER PK
company_id INTEGER FK -> Company.id ON DELETE CASCADE
first_name TEXT NOT NULL
last_name TEXT NOT NULL
email TEXT
phone TEXT
position TEXT
notes TEXT
created_at TIMESTAMP DEFAULT now
updated_at TIMESTAMP DEFAULT now
```
## 5. API-Spec (für Headless-Tests)
| Methode | Pfad | Body / Params | Erfolg | Fehler |
|--------|------|---------------|--------|--------|
| GET | `/api/health` | — | 200 | — |
| POST | `/api/auth/login` | `{username,password}` | 200 + Set-Cookie | 401 |
| POST | `/api/auth/logout` | — | 204 | — |
| GET | `/api/companies` | `?q=&limit=&offset=` | 200 List | — |
| POST | `/api/companies` | JSON | 201 | 400 |
| GET | `/api/companies/{id}` | — | 200 | 404 |
| PATCH | `/api/companies/{id}` | JSON | 200 | 404, 400 |
| DELETE | `/api/companies/{id}` | — | 204 | 404 |
| GET | `/api/contacts` | `?company_id=` | 200 | — |
| POST | `/api/contacts` | JSON | 201 | 400, 404 (Company) |
| GET | `/api/contacts/{id}` | — | 200 | 404 |
| PATCH | `/api/contacts/{id}` | JSON | 200 | 404 |
| DELETE | `/api/contacts/{id}` | — | 204 | 404 |
## 6. Nicht-Ziele (Out of Scope für v0.1)
- Multi-Tenant / Multi-Org
- Rollen-/Rechtesystem (jeder Login-User darf alles)
- E-Mail-Versand, Datei-Upload, Bilder
- Audit-Log, Soft-Delete, Papierkorb
- 2FA, OAuth, Passwort-Reset per Mail
- Internationalisierung (deutsche UI, hartkodiert)
- Mobile-Apps, Push-Notifications
- Reporting, Dashboards mit Charts (nur Counts)
## 7. Annahmen
- Coolify-Host `server.media-on.de` ist erreichbar; Coolify-Token liegt in der Plugin-Config
- Forgejo-Host `forgejo.media-on.de` ist erreichbar; Token liegt in der Plugin-Config
- SQLite reicht für v0.1 (< 100k Datensätze), Migration auf Postgres später möglich
- Deployment erfolgt als einzelner Coolify-Service (Docker Compose mit 1 Container)
- Default-Admin-Credentials `admin/admin` werden beim ersten Start geseeded und sind nur in v0.1; im v0.2 verpflichtender Passwort-Change
## 8. Qualitätskriterien
- **Type-Check**: `python -m mypy app/` ohne Fehler (oder bewusst als nicht-blockend dokumentiert)
- **Tests**: `pytest` mit mindestens **15 Tests**, alle grün (Health, Auth happy + fail, Company CRUD 5, Contact CRUD 5, Cascade-Delete 1, Search 1)
- **Server-Start**: `uvicorn app.main:app` startet ohne ImportError oder 500er
- **Health-Endpoint**: 200, nicht 500
- **API-Endpoints**: alle 13 oben genannten Endpunkte liefern die spezifizierten Status-Codes
- **Build**: Docker-Image buildet ohne Fehler, Image-Größe < 300 MB
- **Deploy**: Coolify-Service ist `running:healthy` nach `POST /api/v1/deploy`
- **Git**: alle Änderungen committed, `git status` ist sauber