generated from bastian/boilerplate
mvp
This commit is contained in:
123
docs/hauspilot-auth-integration.md
Normal file
123
docs/hauspilot-auth-integration.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# HausPilot: Authentifizierungs- und Benutzerintegration
|
||||
|
||||
## Ergebnis der Boilerplate-Analyse
|
||||
|
||||
HausPilot verwendet unveraendert die vorhandene Backend-for-Frontend-
|
||||
Authentifizierung. Das Boilerplate spricht keinen bestimmten Hersteller fest,
|
||||
sondern einen ueber `OIDC_ISSUER` konfigurierten OpenID-Connect-Provider. Der
|
||||
Login ist ein OIDC Authorization Code Flow mit PKCE, `state` und `nonce`.
|
||||
|
||||
Der Browser startet den Login ueber `GET /api/auth/login`. Der Callback liegt
|
||||
fest unter `<APP_BASE_URL>/api/auth/callback`. Der Backend-Callback validiert
|
||||
Issuer, Audience, Signaturalgorithmus, Nonce und Subject, laedt gegebenenfalls
|
||||
UserInfo und legt den lokalen Benutzer an oder aktualisiert ihn. Die stabile
|
||||
Identitaet beim Provider ist `(issuer, subject)`; fachliche Beziehungen verwenden
|
||||
ausschliesslich die interne UUID `users.id`.
|
||||
|
||||
OIDC Access-, Refresh- und ID-Tokens werden verschluesselt in der MySQL-Tabelle
|
||||
`sessions` gespeichert. Der Browser erhaelt nur eine signierte, HttpOnly
|
||||
Session-ID und ein lesbares CSRF-Cookie. Schreibende Requests senden dieses
|
||||
Cookie ueber den vorhandenen `csrfInterceptor` als `X-CSRF-Token`. Idle- und
|
||||
Absolute-Timeout werden beim serverseitigen Session-Resolve geprueft. Es gibt
|
||||
keinen zweiten Token-Refresh im Browser.
|
||||
|
||||
Der globale `PermissionsGuard` loest die Session auf, prueft den globalen
|
||||
Benutzerstatus und setzt `AuthenticatedRequest.user` mit interner UUID,
|
||||
Session-ID und effektiven Permissions. Controller verwenden weiterhin
|
||||
`@RequirePermissions(...)`. Deaktivierte Benutzer werden beim Login und bei
|
||||
jeder Session-Aufloesung abgewiesen; ihre historischen fachlichen Beziehungen
|
||||
werden nicht geloescht.
|
||||
|
||||
Angular ermittelt den aktuellen Benutzer ueber `AuthService.ensureLoaded()` und
|
||||
`GET /api/me`. Der Auth-State liegt ausschliesslich in dessen Signals. Der
|
||||
vorhandene `permissionGuard` steuert nur Navigation und Darstellung; das Backend
|
||||
bleibt verbindlich. Logout erfolgt ausschliesslich ueber `/api/auth/logout`,
|
||||
widerruft die lokale Session, loescht Session- und CSRF-Cookie und verwendet
|
||||
danach den OIDC `end_session_endpoint` beziehungsweise `OIDC_LOGOUT_URL`.
|
||||
|
||||
## Registrierung und Benutzerprofil
|
||||
|
||||
Das Boilerplate besitzt keinen lokalen Registrierungs- oder Passwortdialog und
|
||||
keine lokale Passwort-Entity. Konten werden durch den vorhandenen Identity
|
||||
Provider registriert oder bereitgestellt und beim ersten erfolgreichen OIDC-
|
||||
Login lokal synchronisiert. HausPilot fuehrt daher keine zweite Registrierung
|
||||
ein. Nach dem ersten Login werden offene Einladungen nur angezeigt, niemals
|
||||
automatisch angenommen.
|
||||
|
||||
Die lokale `UserEntity` speichert UUID, Issuer, Subject, Anzeigename, E-Mail,
|
||||
Aktivstatus, globale Rollen und Einstellungen. Name und E-Mail werden bei jedem
|
||||
Login aus dem OIDC-Profil aktualisiert. Aktive Mitgliedschaften bleiben auch bei
|
||||
einer E-Mail-Aenderung ueber `users.id` stabil. Offene Einladungen bleiben an die
|
||||
normalisierte urspruengliche Zieladresse gebunden.
|
||||
|
||||
Die Integration uebernimmt ausserdem den standardisierten OIDC-Claim
|
||||
`email_verified`. Eine Einladung kann nur angenommen werden, wenn der Provider
|
||||
die aktuelle Zieladresse als verifiziert bestaetigt. Fehlt diese Bestaetigung,
|
||||
wird die Annahme sicher abgewiesen.
|
||||
|
||||
## Globale und projektbezogene Autorisierung
|
||||
|
||||
Globale Rollen (`admin`, `user` und verwaltete globale Rollen) bleiben von den
|
||||
HausPilot-Projektrollen getrennt. Eine globale Basis-Permission erlaubt nur die
|
||||
Verwendung der Projekt-API. Sie erzeugt weder eine Mitgliedschaft noch einen
|
||||
Zugriff auf ein privates Projekt. Insbesondere erhaelt ein globaler Administrator
|
||||
keinen impliziten Projektzugriff.
|
||||
|
||||
Projektrollen sind `owner`, `administrator`, `editor` und `reader`. Jede
|
||||
Projektabfrage prueft serverseitig eine aktive Mitgliedschaft und die fuer die
|
||||
Aktion erforderliche Rolle. Unterentitaeten werden immer zusammen mit der
|
||||
Projekt-ID geladen beziehungsweise geprueft. Eigentuermer-, Autor- und
|
||||
Einladender-IDs sind keine DTO-Felder, sondern stammen aus dem vorhandenen
|
||||
serverseitigen Current-User-Kontext.
|
||||
|
||||
Projektanlage, Eigentuemermitgliedschaft und Erstellungsaktivitaet werden in
|
||||
einer Datenbanktransaktion gespeichert. Projektrollen entstehen nur durch diese
|
||||
Projektanlage, explizite Einladungsannahme oder eine berechtigte Aenderung im
|
||||
Projekt; OIDC-Gruppen und globale Rollen werden nicht abgebildet.
|
||||
|
||||
## Einladungen und sicherer Rueckkehrpfad
|
||||
|
||||
Einladungen speichern die normalisierte E-Mail-Adresse und optional die bekannte
|
||||
interne Benutzer-ID. Der zufaellige Einladungstoken wird nur im Link ausgegeben;
|
||||
in MySQL liegt ausschliesslich sein SHA-256-Hash. Annahme und Ablehnung verlangen
|
||||
eine gueltige bestehende Session. Das Backend prueft Status, Ablauf,
|
||||
Widerruf, Zieladresse, `email_verified`, aktiven Benutzer und eine noch nicht
|
||||
bestehende aktive Mitgliedschaft. Fehlermeldungen geben nur eine maskierte
|
||||
Adresse aus.
|
||||
|
||||
Ist ein Besucher nicht angemeldet, verweist Angular auf den bestehenden
|
||||
`/api/auth/login`-Flow. Der gewuenschte Rueckweg wird serverseitig im kurzlebigen
|
||||
OIDC-Login-State gehalten. Erlaubt sind nur interne Pfade; Schemes,
|
||||
Protokoll-relative URLs und Backslashes werden verworfen. Nach dem Callback baut
|
||||
das Backend das Ziel relativ zu `FRONTEND_BASE_URL` auf. Tokens oder OIDC-Daten
|
||||
werden nie in Rueckkehrparametern gespeichert.
|
||||
|
||||
## Benachrichtigungen, Mail und Cleanup
|
||||
|
||||
Das vorhandene `NotificationsService` erzeugt fuer bereits bekannte aktive
|
||||
Benutzer eine persoenliche In-App-Benachrichtigung. Normale Endpunkte bleiben auf
|
||||
den Session-Benutzer beschraenkt. Das Boilerplate hat keinen Mail-Service, keine
|
||||
Templates, Queue oder Retry-Jobs. HausPilot fuehrt keine parallele
|
||||
Mail-Infrastruktur ein; der Versandstatus einer Einladung wird daher explizit
|
||||
als `not_configured` gespeichert. Der Einladungsdatensatz bleibt konsistent und
|
||||
kann spaeter an eine architektonisch beschlossene Mail-Komponente angebunden
|
||||
werden.
|
||||
|
||||
Projektseiten halten Daten nur komponentenlokal. Bei 401 setzt der vorhandene
|
||||
globale Auth-State den Benutzer zurueck; dadurch werden geschuetzte Seiten
|
||||
entfernt und das bestehende Notification-Polling samt personenbezogenem Cache
|
||||
gestoppt. Ein regulaerer Logout verlaesst die SPA und startet sie ohne alten
|
||||
In-Memory-State neu. HausPilot fuehrt keine Live-Verbindungen ein.
|
||||
|
||||
## Relevante Konfiguration
|
||||
|
||||
- OIDC: `OIDC_ISSUER`, `OIDC_CLIENT_ID`, `OIDC_CLIENT_SECRET`,
|
||||
`OIDC_SCOPES`, `OIDC_ALLOWED_ALGORITHMS`, optional `OIDC_LOGOUT_URL`
|
||||
- Redirects: `APP_BASE_URL`, `FRONTEND_BASE_URL`
|
||||
- Session: `SESSION_COOKIE_NAME`, Idle-/Absolute-Timeout,
|
||||
`SESSION_SECRET`, `SESSION_ENCRYPTION_KEY`
|
||||
- Browser-Schutz: `CORS_ORIGINS`, `CSRF_HEADER_NAME`, Secure/SameSite-Cookies,
|
||||
Helmet, Rate Limits und Logging-Redaction
|
||||
|
||||
Es wurde keine parallele Authentifizierungs-, Benutzer-, Passwort-, Token-,
|
||||
Registrierungs-, Mail-, Queue- oder Live-Connection-Infrastruktur eingefuehrt.
|
||||
215
docs/hauspilot-domain.md
Normal file
215
docs/hauspilot-domain.md
Normal file
@@ -0,0 +1,215 @@
|
||||
# HausPilot-Fachmodule
|
||||
|
||||
## MVP-Abschluss 2026-07
|
||||
|
||||
### Neue Migration und Datenmodelle
|
||||
|
||||
`1720000005000-AddMentionsAndReminders.ts` ergänzt `task_comment_mentions`, `reminder_deliveries` und `applied_project_templates`. Eindeutige Constraints deduplizieren Benutzer pro Kommentar, Reminder-Schlüssel sowie Vorlagen je Projekt und Ziel. Die Tabellen referenzieren weiterhin ausschließlich bestehende Benutzer- und Projekt-UUIDs.
|
||||
|
||||
### Listen-API
|
||||
|
||||
Räume, Aufgaben, Meilensteine, Ausgaben, Dokumente, Kommentare und Aktivitäten liefern serverseitig `{ items, page, pageSize, totalItems, totalPages }`. `pageSize` ist auf 100 begrenzt. Filter enthalten stets `projectId`. Sortierfelder sind DTO-Whitelists und werden nie als frei übergebener SQL-Ausdruck ausgeführt. Aufgaben unterstützen Suchtext, mehrere Statuswerte, Priorität, Raum, Verantwortlichen, Kategorie, Datumsbereiche, überfällig, blockiert, nicht zugewiesen und „nur meine“. Ausgaben und Dokumente besitzen Zuordnungs- und Datumsfilter.
|
||||
|
||||
Neue gezielte Leseendpunkte sind `GET /projects/:projectId/tasks/:taskId` und `GET /projects/:projectId/calendar?from=YYYY-MM-DD&to=YYYY-MM-DD`. Die stabile Angular-Route `/projekte/:projectId/aufgaben/:taskId` enthält Stammdaten, Schnellstatus, Checkliste, Vorgänger und Nachfolger, Kommentare, Erwähnungen, Dokumente und den Aktivitätsauszug. Der Zeitplan bietet eine responsive Monats- und Agendaansicht ohne neue UI-Bibliothek und lädt nur den benötigten Zeitraum.
|
||||
|
||||
### Vorlagen
|
||||
|
||||
Die unveränderlichen Systemvorlagen „Reihenhaus-Renovierung“, „Schlüsselübergabe“, „Renovierung eines Raums“ und „Umzug“ werden transaktional kopiert. Die Reihenhausvorlage enthält fünf Bereiche, 15 Räume, 13 Budgetkategorien, Meilensteine und allgemeine Aufgaben. Die Raumvorlage erzeugt 14 Aufgaben samt Abhängigkeitskette. `applied_project_templates` erkennt Wiederholungen je Projekt und Zielraum; erneute Anwendung verlangt `confirmDuplicate`. Fehler rollen alle Kopien und die Aktivität gemeinsam zurück.
|
||||
|
||||
### Strukturierte Erwähnungen
|
||||
|
||||
Kommentare übertragen neben sichtbarem `@Anzeigename` eine validierte Liste stabiler Benutzer-UUIDs. Nur aktive Mitglieder desselben Projekts werden akzeptiert. Mehrfachnennungen werden dedupliziert, Selbst-Erwähnungen erzeugen keine Nachricht und beim Bearbeiten werden nur neu hinzugekommene Benutzer benachrichtigt. Die In-App-Nachricht verweist direkt auf Aufgabe und Kommentar.
|
||||
|
||||
### Scheduler und Reminder
|
||||
|
||||
Der offizielle Nest-Scheduler führt den `ReminderService` standardmäßig alle 15 Minuten aus. Er erfasst bald fällige und überfällige Aufgaben, Meilensteine, überfällige offene Ausgaben sowie bald ablaufende Einladungen mit bekanntem Benutzer. Entfernte oder global deaktivierte Mitglieder erhalten nichts. `reminder_deliveries.dedupe_key` enthält Reminder-Art, Entität, Empfänger und Bezugsdatum; der Unique Constraint schützt auch parallele Läufe. Eine Datumsänderung erzeugt einen neuen fachlich relevanten Schlüssel.
|
||||
|
||||
- `REMINDER_INTERVAL_MS`: mindestens 60.000, Standard 900.000
|
||||
- `REMINDER_DUE_SOON_DAYS`: Standard 3
|
||||
|
||||
### Development-Seed
|
||||
|
||||
Nach Migrationen und mindestens einem vorhandenen aktiven SSO-Benutzer wird der ausschließlich explizite Seed gestartet:
|
||||
|
||||
```text
|
||||
npm run seed:development
|
||||
```
|
||||
|
||||
Er erzeugt idempotent „Umzug Reihenhaus“ mit fünf Etagen, 15 Räumen, 30 Aufgaben, zehn Abhängigkeiten, 20 Checklistenpunkten, zehn Kommentaren, acht Meilensteinen, 13 Budgetkategorien, 15 Ausgaben, vier Dokumentmetadaten, 20 Aktivitäten, In-App-Benachrichtigungen und – sofern vorhanden – bis zu vier bestehende Benutzer in unterschiedlichen Rollen. Er legt weder Benutzer noch Passwörter an. In `NODE_ENV=production` verweigert er Ausführung und Reset. Der gezielte Reset entfernt nur das eindeutig markierte Seed-Projekt:
|
||||
|
||||
```text
|
||||
npm run seed:development -- --reset
|
||||
```
|
||||
|
||||
Seed-Dokumente enthalten absichtlich nur Metadaten, keine fingierten Binärdateien.
|
||||
|
||||
### Abhängigkeitssicherheit und Grenzen
|
||||
|
||||
Die produktiven Advisories wurden ohne Major-Upgrade geschlossen: `@nestjs/swagger` 11.4.6 und TypeORM 0.3.31 beheben die gemeldeten transitiven Risiken in `js-yaml`, `lodash`, `path-to-regexp` und TypeORM. `npm audit --omit=dev` meldet danach keine bekannte Schwachstelle. `@nestjs/schedule` 6.1.3 (MIT, Nest-10/11-kompatibel) ist die einzige neue Laufzeitabhängigkeit.
|
||||
|
||||
Der Scheduler läuft pro Backend-Instanz; der Datenbank-Constraint macht die Auslieferung mehrfach laufender Instanzen idempotent, ersetzt bei sehr großen Installationen jedoch kein verteiltes Job-Leasing. Komplexe Gantt-, Offline-, E-Mail- und externe Kalenderfunktionen bleiben außerhalb des MVP.
|
||||
|
||||
## Ausgangsarchitektur und Wiederverwendung
|
||||
|
||||
Die Umsetzung erweitert die bestehende Angular-/NestJS-Anwendung. Globale Anmeldung, OIDC, serverseitige MySQL-Session, CSRF, `PermissionsGuard`, `ProjectAccessService`, `ProjectMembershipEntity`, `NotificationsService` und `ProjectActivityEntity` bleiben die einzigen Mechanismen für Identität, Projektzugriff, Benachrichtigungen und Aktivitäten. Es gibt keine zweite Benutzer-, Login-, Session- oder Projektverwaltung.
|
||||
|
||||
Die fachlichen Controller tragen weiterhin `@RequirePermissions(Permission.ProjectsUse)`. Danach prüft `ProjectAccessService` die aktive Mitgliedschaft und die Projektrolle. Jede Unterentität wird zusätzlich mit der Kombination aus `id` und `projectId` geladen. Dadurch reicht weder eine gültige Anmeldung noch eine fremde Entitäts-ID zum Zugriff.
|
||||
|
||||
## Datenmodell und Module
|
||||
|
||||
`RenovationModule` bündelt die eng zusammenhängende Renovierungsdomäne und besteht aus Controller, Service, Persistence-Repository, Dokumentablage und DTO-Validierung. Die Migration `1720000004000-AddRenovationDomain.ts` ergänzt:
|
||||
|
||||
- Gebäude, Etagen und Räume
|
||||
- Renovierungsaufgaben, Checklisten, Abhängigkeiten und Kommentare
|
||||
- Meilensteine
|
||||
- Budgetkategorien und Ausgaben
|
||||
- projektbezogene Dokumentmetadaten
|
||||
- Projektstatus, Währung und Gesamtbudget-Grundlage
|
||||
|
||||
Historische Benutzerbeziehungen verwenden die bestehende interne User-UUID. Beim Entfernen eines Mitglieds werden offene Zuweisungen in derselben Transaktion geleert; abgeschlossene und historische Beziehungen bleiben erhalten.
|
||||
|
||||
## API
|
||||
|
||||
Die API liegt unter `/api/projects/:projectId` und stellt Listen sowie Create-/Patch-/Delete-Endpunkte für `buildings`, `floors`, `rooms`, `tasks`, `milestones`, `budget-categories`, `expenses` und `documents` bereit. Aufgaben besitzen zusätzlich `checklist`, `dependencies` und `comments`. Weitere gezielte Endpunkte sind:
|
||||
|
||||
- `GET /projects/:projectId/dashboard`
|
||||
- `GET /projects/:projectId/activities`
|
||||
- `GET /templates`
|
||||
- `POST /projects/:projectId/apply-template/:templateId`
|
||||
- `GET /projects/:projectId/documents/:id/download`
|
||||
|
||||
Request-DTOs enthalten weder Ersteller noch handelnde Benutzer. Diese IDs stammen ausschließlich aus dem bestehenden Sessionkontext.
|
||||
|
||||
## Rollen
|
||||
|
||||
- Eigentümer und Projektadministratoren verwalten die gesamte Struktur, Budgets und Vorlagen.
|
||||
- Bearbeiter verwalten Räume, Aufgaben, Checklisten, Kommentare, Ausgaben und Dokumente.
|
||||
- Leser können ausschließlich lesen.
|
||||
|
||||
Budgetkategorien sind bewusst Eigentümern und Projektadministratoren vorbehalten, weil ihre Änderung den finanziellen Projektrahmen verändert. Globale SSO-Rollen erzeugen weiterhin keine Projektmitgliedschaft.
|
||||
|
||||
## Fortschritt und Abhängigkeiten
|
||||
|
||||
Die einzige Fortschrittsberechnung liegt in `renovation/progress.ts`: Idee 0, geplant 10, beauftragt 25, in Arbeit 50, blockiert 25, Abnahme 90 und erledigt 100 Prozent. Entfallene Aufgaben werden ausgeschlossen. Das optionale positive Gewicht bildet einen gewichteten Mittelwert; ohne fachliches Gewicht wird `1` verwendet.
|
||||
|
||||
Die zentrale Zykluserkennung erweitert den gerichteten Graphen probeweise um die neue Kante und führt eine Tiefensuche mit `visiting`-/`visited`-Mengen aus. Selbstbezüge sowie direkte und indirekte Zyklen werden mit HTTP 409 abgewiesen. Eine Aufgabe ist blockiert, sobald ein zwingender Vorgänger nicht erledigt ist; der Start wird dann serverseitig verhindert.
|
||||
|
||||
## Dashboard und Budget
|
||||
|
||||
Der Dashboard-Endpunkt berechnet aggregiert Gesamtfortschritt, offene, überfällige, blockierte, kritische, eigene und nicht zugewiesene Aufgaben, Raumstatus, nächste/gefährdete Meilensteine, aktive Mitglieder sowie geplante, tatsächliche, offene und bezahlte Kosten. Handlungsorientierte Hinweise werden serverseitig aus denselben Daten erzeugt.
|
||||
|
||||
Geldbeträge werden in MySQL als `DECIMAL` gespeichert und erst für Aggregationen explizit in Zahlen überführt. Stornierte Ausgaben fließen nicht in tatsächliche Kosten ein.
|
||||
|
||||
## Optimistische Nebenläufigkeit
|
||||
|
||||
Gebäude, Etagen, Räume, Aufgaben, Meilensteine, Budgetkategorien, Ausgaben und Dokumentmetadaten besitzen eine Versionsnummer. Updates verwenden atomar `WHERE id = ? AND project_id = ? AND version = ?` und erhöhen die Version in derselben SQL-Anweisung. Ein veralteter Stand überschreibt daher keine neueren Daten und liefert HTTP 409. Angular zeigt dafür einen konkreten Konflikthinweis und führt keine verlustbehaftete automatische Zusammenführung durch.
|
||||
|
||||
## Dokumentablage
|
||||
|
||||
Dateien werden nicht öffentlich ausgeliefert. Upload und Download laufen nach Projektzugriffsprüfung über das Backend. Erlaubt sind PDF, JPEG, PNG und WebP bis zum konfigurierten Limit. Endung, MIME-Typ und Dateisignatur müssen zusammenpassen; der Speichername ist eine zufällige UUID und Pfadbestandteile aus dem Client werden nie übernommen. Metadaten liegen in MySQL, Inhalte unter `DOCUMENT_STORAGE_PATH`.
|
||||
|
||||
Konfiguration:
|
||||
|
||||
- `DOCUMENT_STORAGE_PATH` (Standard `storage/documents`)
|
||||
- `DOCUMENT_MAX_FILE_SIZE_BYTES` (Standard 10 MiB)
|
||||
|
||||
Für mehrere Backend-Instanzen muss der Pfad als gemeinsames, geschütztes Volume bereitgestellt werden. Virenscanning und objektbasierter Cloud-Speicher sind sinnvolle Produktionsausbaustufen.
|
||||
|
||||
## Frontend
|
||||
|
||||
Der responsive Projektarbeitsbereich nutzt ausschließlich bestehende Design-Tokens und UI-Komponenten. Die horizontale, tastaturbedienbare Projektnavigation umfasst Übersicht, Räume, Aufgaben, Zeitplan, Budget, Dokumente, Aktivitäten und die bestehende Mitgliederseite. Listen wechseln auf kleinen Displays in Karten; Hauptaktionen benötigen kein Hover.
|
||||
|
||||
## Vorlagen und Erweiterbarkeit
|
||||
|
||||
Systemvorlagen werden als unveränderliche Definitionen angeboten. Die Raumrenovierung kopiert Aufgaben und Abhängigkeiten; ein vorhandener Aufgabenbestand verlangt eine explizite Duplikatbestätigung. Die Servicegrenze ist für weitere transaktionale Vorlagen vorbereitet.
|
||||
|
||||
Spätere Module für Kartons/QR-Codes, Inventar, Lieferanten/Angebote, Materialmengen, Rechnungserkennung, KI-Planung, E-Mail-Erinnerungen und Kalenderintegration können über `projectId`, bestehende Mitgliedschaften, Dokumentreferenzen, Aktivitäten und Benachrichtigungen angebunden werden, ohne Authentifizierung oder Projektzugriff zu duplizieren.
|
||||
|
||||
## Tests
|
||||
|
||||
Die Verhaltenstests decken insbesondere die gewichtete Fortschrittsberechnung, ausgeschlossene Aufgaben, direkte/indirekte Abhängigkeitszyklen, kombinierte Aufgabenfilter und den Concurrency-Hinweis ab. Bestehende Tests sichern SSO, Projektzugriff, Einladungen, Sessions und Benachrichtigungen weiterhin ab.
|
||||
|
||||
## Frühere MVP-Grenzen
|
||||
|
||||
Die zuvor fehlenden Vorlagen, strukturierten Erwähnungen und deduplizierten Reminder wurden mit dem oben dokumentierten MVP-Abschluss umgesetzt. Eine Queue wurde dafür bewusst nicht eingeführt.
|
||||
|
||||
# Möbel- und Einrichtungsplanung
|
||||
|
||||
## AG-Grid-Arbeitsoberfläche
|
||||
|
||||
Die Möbelverwaltung nutzt `ag-grid-angular` und `ag-grid-community` 36.0.1 (MIT, Community
|
||||
Edition). Registriert wird ausschließlich `AllCommunityModule`. Enterprise-Funktionen wie Row
|
||||
Grouping und Master/Detail werden weder importiert noch vorausgesetzt. Bedarfe, Alternativen,
|
||||
Bestellungen und Szenarien sind getrennte Grid-Ansichten; die vorhandenen Formulare bleiben für
|
||||
die vollständige Detailbearbeitung zuständig.
|
||||
|
||||
Listen verwenden die bestehende Page-Antwort (`items`, `page`, `pageSize`, `totalItems`,
|
||||
`totalPages`). Bedarfe unterstützen zusätzlich `openDecision` und `overBudget`; die projektweite
|
||||
Alternativenliste unterstützt `roomId`, `requirementId`, `status`, `availability`, `favorite`,
|
||||
`selected`, `ordered` und `delayed`. Sortierfelder werden im Service gegen Positivlisten geprüft.
|
||||
Bedarfsantworten enthalten aggregierte Optionen, Preise, Budgetabweichung, Bestellstatus und
|
||||
Lieferdatum. Die Repository-Abfragen laden Optionen gebündelt für die aktuelle Seite.
|
||||
|
||||
Inline-Änderungen senden die vorhandene Versionsnummer. Bei Fehlern wird der alte Zellwert
|
||||
wiederhergestellt; bei HTTP 409 wird der Serverstand neu geladen und der Konflikt sichtbar gemeldet.
|
||||
Favorit und Auswahl verwenden die bestehenden Fachendpunkte. Leser erhalten keine Editoren; die
|
||||
serverseitige Projektberechtigung bleibt verbindlich.
|
||||
|
||||
Spaltenzustand wird ohne Fachdaten lokal unter Benutzer-, Projekt- und Ansichtsschlüssel gespeichert.
|
||||
Die Suche wird um 300 ms entprellt, Row-IDs sind stabile Datenbank-IDs und CSV-Export nutzt die
|
||||
Community-Funktion. Auf kleinen Displays gelten eine reduzierte Höhe und die vorhandene
|
||||
Dialogbearbeitung für komplexe Eingaben.
|
||||
|
||||
Die Möbelplanung ist Bestandteil des vorhandenen `RenovationModule` und verwendet dessen
|
||||
`ProjectAccessService`, Dokumentablage, Ausgaben, Aktivitäten und Benachrichtigungen. Ein
|
||||
`FurnitureRequirement` beschreibt den Bedarf eines Raums; konkrete Produkte werden als
|
||||
`FurnitureOption` gespeichert. `FurnitureScenario` und `FurnitureScenarioSelection`
|
||||
kombinieren höchstens eine Alternative je Bedarf zu vergleichbaren Einrichtungsvarianten.
|
||||
|
||||
## Preis- und Kostenregeln
|
||||
|
||||
Der geplante Gesamtpreis lautet `Einzelpreis × Menge + Versand + Zusatzkosten − Rabatt`.
|
||||
Bei vorhandenem Mobiliar entfallen Anschaffungskosten; Umzug und Aufbereitung werden dennoch
|
||||
berücksichtigt. Alle persistierten Geldwerte sind `DECIMAL(13,2)` und werden im Backend als
|
||||
Strings verarbeitet; die zentrale Berechnung in `furniture-pricing.ts` rechnet in Cent.
|
||||
Planwerte stammen aus der ausgewählten Alternative bzw. einem Szenario. Ist-Kosten stammen
|
||||
ausschließlich aus nicht stornierten, über `furniture_option_id` verknüpften Ausgaben. Damit
|
||||
wird eine Bestellung nicht zugleich als Produktpreis und Ausgabe doppelt gezählt.
|
||||
|
||||
## API und Listenmodell
|
||||
|
||||
Die Endpunkte liegen unter `/projects/:projectId/furniture-requirements`,
|
||||
`/furniture-options`, `/furniture-scenarios`, `/furniture-summary` sowie
|
||||
`/rooms/:roomId/furniture-summary`. Bedarfslisten unterstützen `page`, `pageSize` (maximal
|
||||
100), `search`, Raum, Kategorie, Status, Priorität, Verantwortlichen, Favorit, Auswahl,
|
||||
Bestell-/Lieferstatus und Lieferverzug. Erlaubte Sortierungen sind Name, Raum, Kategorie,
|
||||
Preis, Priorität, Status, Lieferdatum, Änderungsdatum und Sortierreihenfolge.
|
||||
|
||||
## Auswahl, Szenarien und Nebenläufigkeit
|
||||
|
||||
Das Auswählen eines Produkts sperrt die betroffene Alternative und deren Bedarf
|
||||
transaktional, prüft die Version, entfernt die bisherige Auswahl und erzeugt eine Aktivität.
|
||||
Damit bleibt auch bei parallelen Anforderungen höchstens eine aktive Auswahl bestehen. Bedarfe,
|
||||
Alternativen und Szenarien besitzen Versionsnummern; veraltete Änderungen liefern HTTP 409.
|
||||
Automatische Szenarien wählen günstigste, bevorzugte (Auswahl, Favorit, günstigste), teuerste
|
||||
oder vorhandene Alternativen und können danach manuell verändert werden.
|
||||
|
||||
## Dokumente, Ausgaben, Status und Sicherheit
|
||||
|
||||
`furniture_option_documents` referenziert ausschließlich geschützte Projektdokumente; es
|
||||
existiert keine zweite Dateiablage. Ausgaben können Bedarf und Alternative referenzieren.
|
||||
Sämtliche Unterentitäten werden zusätzlich zur authentifizierten Projektmitgliedschaft gegen
|
||||
die Pfad-`projectId` geprüft. Leser dürfen lesen und vergleichen; Eigentümer,
|
||||
Administratoren und Bearbeiter verwenden die bestehende Projektaktion `edit`.
|
||||
Bestellungen speichern Besteller, Nummer und Liefertermin. Teil- und Komplettlieferung sowie
|
||||
Verspätung werden ausgewertet. Liefererinnerungen laufen über den vorhandenen Scheduler und
|
||||
die bestehende `ReminderDelivery`-Deduplizierung; eine Terminänderung erzeugt durch das Datum
|
||||
im Schlüssel eine neue, wiederholte Jobläufe dagegen keine weitere Benachrichtigung.
|
||||
|
||||
## Migration und Development-Seed
|
||||
|
||||
Migration `1720000007000-AddFurniturePlanning` erstellt die Möbel- und Szenariotabellen und
|
||||
ergänzt optionale Expense-Referenzen. Der Development-Seed bleibt produktionsgesperrt und
|
||||
legt 16 Bedarfe, 48 Alternativen, drei Szenarien, Favoriten, Auswahlen, Bestellungen, eine
|
||||
Lieferung, einen Lieferverzug, sichere Dokumentmetadaten und verknüpfte Ausgaben an. Die
|
||||
Etagen des Seeds sind Keller, Erdgeschoss, 1. Stock und Dachboden.
|
||||
Reference in New Issue
Block a user