112 lines
6.2 KiB
Markdown
112 lines
6.2 KiB
Markdown
# 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. Abgestimmt in Issue #6: Das Modell ist so bestätigt, und Gebäude mit
|
||
zugeordneten Etagen lassen sich nicht löschen.
|
||
|
||
## 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 |
|
||
| `rooms` | `area_m2` (nullable `REAL`) | optional gepflegte Netto-Raumfläche in m² |
|
||
|
||
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. Vor dem Anwenden der Migration
|
||
auf eine bestehende Datenbank ist wie bei jeder Schemamigration eine separate
|
||
Datenbanksicherung anzulegen.
|
||
|
||
Migration `0011_glossy_norman_osborn` ergänzt ausschließlich das nullable Feld
|
||
`rooms.area_m2`. Bestehende Räume behalten damit eine unbekannte Fläche. Vor
|
||
dem Anwenden auf eine bestehende Datenbank ist ebenfalls eine separate
|
||
Datenbanksicherung erforderlich.
|
||
|
||
## 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 `project-room.insert/.update/.delete` haben seit Schema-Version
|
||
**2** das Feld `areaM2`. Version-1-Befehle werden beim Einlesen mit
|
||
`areaM2 = null` hochgestuft. Vor einem alten Update oder Delete wird weiterhin
|
||
der vollständige erwartete Datensatz verglichen; eine inzwischen vorhandene
|
||
Fläche kann deshalb nicht unbemerkt gelöscht werden.
|
||
|
||
## Snapshots und Transfer
|
||
|
||
Projekt-Snapshot-Schema **8** enthält zusätzlich `rooms[].areaM2`. Version 7
|
||
wird mit `areaM2 = null` hochgestuft; die älteren unterstützten Versionen 1–6
|
||
werden außerdem 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 und Projekttransfer erhalten die Raumfläche. Das portable
|
||
Transferformat bleibt Version 2, weil es den versionierten Projektsnapshot
|
||
bereits einbettet; beim Duplizieren werden nur IDs und Relationen umgesetzt.
|
||
|
||
## 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.
|
||
- `POST/PUT /api/projects/:projectId/rooms[/:roomId]` akzeptieren zusätzlich
|
||
optional `areaM2` (`number | null`, endlich und ≥ 0). Beim Anlegen bedeutet
|
||
ein fehlendes Feld `null`; beim Bearbeiten bleibt die vorhandene Fläche
|
||
erhalten, wenn das Feld fehlt.
|
||
|
||
## 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.
|
||
|
||
Der Raumdialog pflegt die optionale **Fläche [m²]** mit deutscher
|
||
Dezimaltrennzeichen-Eingabe. Gemeint ist die Netto-Raumfläche, wie sie der
|
||
Revit-Raumparameter „Fläche“ liefert, nicht eine Flächenart nach DIN 277. Die
|
||
Fläche wird ohne fachliche Rundung gespeichert und mit zwei Nachkommastellen
|
||
angezeigt. Die Gebäudestruktur zeigt die Fläche je Raum sowie Summen je Etage,
|
||
je Gebäude, „Ohne Etage“ und „Ohne Gebäude“. Fehlende Flächen werden nicht als
|
||
Nullfläche ausgegeben, sondern separat gezählt, damit Teilsummen erkennbar
|
||
bleiben.
|
||
|
||
## Offen
|
||
|
||
- Revit: Eine Übernahme des Gebäudes aus Revit ist derzeit nicht geplant. Die
|
||
abgeschlossene Phase 14 verwendet Linear-XLSX; das Modell steht der expliziten
|
||
Raumzuordnung im Importwizard nicht im Weg.
|
||
- Raumflächen werden derzeit ausschließlich manuell gepflegt. Ein späterer
|
||
Revit-Abgleich muss die Phase-14-Regeln mit externem Modellzustand, Vorschau
|
||
und ausdrücklicher Bestätigung einhalten.
|
||
- Eine W/m²-Auswertung oder Übernahme der Raumfläche in Flächenlasten ist nicht
|
||
Teil dieser Funktion; das ist Folgethema von Issue #20.
|
||
- Sortierung von Gebäuden per Drag-and-drop ist noch nicht umgesetzt; neue
|
||
Gebäude werden hinten angehängt.
|