# Gebäudestruktur (Gebäude → Etagen → Räume) Projekte mit mehreren Gebäuden werden über eine eigene Ebene **Gebäude** gegliedert. Etagen können einem Gebäude zugeordnet sein, Räume und Verteilungen hängen wie bisher an der Etage. Das Gebäude eines Raums oder einer Verteilung ergibt sich also aus der Etage; eine zweite, konkurrierende Zuordnung gibt es nicht. Abstimmung mit Julian: Issue #6. ## Datenmodell | Tabelle | Feld | Bedeutung | |---|---|---| | `buildings` | `id`, `project_id` (FK, cascade), `name`, `sort_order` | Gebäude eines Projekts | | `floors` | `building_id` (nullable FK → `buildings`, `ON DELETE set null`) | optionales übergeordnetes Gebäude | Migration `0009_buildings`. Bestehende Etagen behalten `building_id = null` und erscheinen in der Oberfläche als „Ohne Gebäude“. Es gibt keine Zwangsmigration und kein automatisch angelegtes Standardgebäude. ## Befehle, Undo/Redo Alle Änderungen laufen über die bestehende Orts-Struktur (`ProjectLocationStructureProjectCommandRepository`) mit Revision, Inverse und persistentem Undo/Redo: - `project-building.insert` / `project-building.delete` mit vollständigem Gebäude-Snapshot (Schema-Version 1) - `project-building.update`: ändert nur den Namen - Löschen eines Gebäudes wird abgelehnt, solange Etagen zugeordnet sind („Das Gebäude kann nicht gelöscht werden, solange Etagen zugeordnet sind.“) - Etagenbefehle `project-floor.insert/.update/.delete` haben seit Schema-Version **2** das Feld `buildingId`. `project-floor.update` darf Name und/oder Gebäude ändern, nicht aber `sortOrder`. Ein referenziertes Gebäude muss zum Projekt gehören. - Version-1-Etagenbefehle aus bestehender Historie bleiben ausführbar. Sie stammen aus der Zeit vor den Gebäuden und werden vor der Ausführung mit `buildingId = null` auf Version 2 gehoben; auch die daraus neu gespeicherten Vorwärts- und Inversbefehle sind Version 2. Das ist bei linearer Historie exakt: Wird ein Gebäude später zugeordnet, liegt dieser neuere Befehl im Undo-Stapel vor dem alten Befehl und wird zuerst zurückgenommen. - Raumbefehle bleiben unverändert (Version 1). ## Snapshots und Transfer Projekt-Snapshot-Schema **7** enthält `buildings` und `floors[].buildingId`. Alle älteren unterstützten Versionen (1–6) werden beim Einlesen mit leerer Gebäudeliste und `buildingId = null` hochgestuft; gespeicherte Sicherungspunkte bleiben wiederherstellbar. Die Relationen werden geprüft (Gebäude gehört zum Projekt, `buildingId` verweist auf ein Gebäude des Snapshots). Restore löscht und schreibt Gebäude zusammen mit den Etagen; der Projekt-Transfer vergibt neue Gebäude-IDs und stellt die Etagenverweise um. ## API - `GET /api/projects/:projectId/buildings` – Gebäude sortiert nach `sortOrder`, Name - `POST /api/projects/:projectId/buildings` – `{ "name": "Haus A", "expectedRevision": 3 }` - `PUT /api/projects/:projectId/buildings/:buildingId` – `{ "name": "Haus B", "expectedRevision": 4 }` - `DELETE /api/projects/:projectId/buildings/:buildingId` – `{ "expectedRevision": 5 }` - `POST/PUT /api/projects/:projectId/floors[/:floorId]` akzeptieren zusätzlich optional `buildingId` (`string | null`). Fehlt das Feld, bleibt beim Bearbeiten das bisherige Gebäude erhalten; ältere Clients funktionieren unverändert. ## Oberfläche Gebäude, Etagen und Räume werden auf der eigenen Seite `/projects/:projectId/struktur` („Gebäudestruktur“) gepflegt, erreichbar über die Seitenleiste und die Kennzahlen der Projektübersicht. Drei Spalten Gebäude → Etagen → Räume filtern sich gegenseitig: Klick auf ein Gebäude zeigt nur dessen Etagen, Klick auf eine Etage nur deren Räume, „Alle“ hebt den Filter auf. Hinzufügen, Bearbeiten und Löschen laufen über Icon-Buttons wie in der Stromkreisliste. ## Offen - Revit: Das Gebäude kann später aus einem Revit-Parameter kommen und über die bestehende CSV-Zuordnung gemappt werden (Frage an Julian in Issue #6). - Sortierung von Gebäuden per Drag-and-drop ist noch nicht umgesetzt; neue Gebäude werden hinten angehängt.