# Grobe Leistungsbilanz (LPH 2/3) Eigenes Modul für die Leistungsbilanz der Vor- und Entwurfsplanung, gegliedert nach Kostengruppen (DIN 276, KG 400). Es gibt dort noch keine Verteilungen und Stromkreise: Jedes Gewerk trägt unter seiner Kostengruppe Verbraucher als Stückzahl mit Einzelleistung oder als Fläche mit W/m² ein. Aufbau und Rechnung folgen der bisherigen Excel-Vorlage „Leistungsbilanz – KG400“. Abstimmung: Issue #7. Das Modul ist **nicht** mit der Stromkreisstruktur verknüpft. Es liest die Gerätezeilen nur für den Vergleich „geschätzt ↔ detailliert“ und schreibt nichts automatisch in Stromkreislisten, Verteilungen oder Projektgeräte. Eine ausdrückliche Einzelaktion kann die Werte einer Grobbilanz-Zeile als Vorschlag in den bestehenden Projektgeräte-Dialog übernehmen; erst dessen Bestätigung legt über `project-device.insert` ein unplatziertes Projektgerät an. ## Aufbau ``` src/shared/estimate-balance/estimate-balance.ts Datenform, Rechnung, Vergleich (rein, ohne Imports) src/estimate-balance/domain/estimate-balance-command.model.ts Snapshot-Schema (Zod), Befehle src/estimate-balance/domain/estimate-balance-project-command.store.ts Store-Port src/estimate-balance/api/estimate-balance-request.schemas.ts Request-Schemas src/db/schema/estimate-balances.ts Tabellen (Migration 0010) src/db/repositories/estimate-balance*.ts Lesen, Schreiben, Befehls-Store src/server/controllers/estimate-balance.controller.ts API src/app/projects/[projectId]/grobbilanz/ Übersicht der Stände und Editor src/frontend/estimate-balance/ Editor-Komponenten und Helfer ``` Die gemeinsame Datei unter `src/shared/estimate-balance` hat keine Imports, weil das Frontend sie direkt nutzt (Turbopack löst die `.js`-Suffixe der API nicht auf). ## Datenmodell | Tabelle | Inhalt | |---|---| | `estimate_balances` | ein **Stand** je Projekt (z. B. Vorplanung, Entwurfsplanung): Name, LPH, Datum, Reserve AV/SV in %, Sortierung | | `estimate_groups` | Kostengruppe (`"410"` … `"480"`, `""` für „Allgemein“), Titel, „bearbeitet von“, Sortierung | | `estimate_rows` | Versorgung (Auswahl ELT/MSR oder Freitext), Verbraucher/Fläche, Anzahl bzw. Fläche, Einheit (`piece` = St., `area` = m²), Anschluss (fest/Steckdose), Einzelleistung in W bzw. W/m², GLF, cos φ, SV ja/nein, Anmerkung | Neue Stände entstehen aus der Vorlage KG 400 (410 Sanitär, 420 Wärme, 430 Luft, 434 Kälte, 440 Starkstrom, 450 Schwachstrom, 460 Förder, 470 Nutzungsspezifisch, 480 GA, Allgemein; Reserve AV 10 %), leer oder als Kopie eines bestehenden Stands mit neuen IDs. Stände bleiben bearbeitbar; ein zusätzlicher Status oder eine Bearbeitungssperre ist derzeit nicht vorgesehen. ## Rechnung - Zeile: P = Anzahl bzw. Fläche × Einzelleistung × GLF, S = P / cos φ. - **AV** enthält alle Verbraucher (Normalversorgung). **SV** listet zusätzlich die Zeilen mit „SV = ja“, die auch aus der Sicherheitsstromversorgung versorgt werden müssen. Das ist die Lesart der Vorlage; die Regel liegt in `calculateEstimateRow` und lässt sich dort zentral ändern. - Summen je Kostengruppe, Gesamtsumme, Reserve in % (AV und SV getrennt), Leistung inklusive Reserve und Maximalstrom I = S / (√3 · U) mit der Drehstromspannung aus den Projekteinstellungen. - Anzeige wie in der Tabellenkalkulation gerundet (`roundEstimateValue`, 40,175 → 40,18). Der Test `tests/estimate-balance.test.ts` rechnet das Beispiel der Vorlage vollständig nach (143,24 kW, 148,48 kVA, mit 10 % Reserve 157,56 kW / 163,33 kVA, 235,75 A). ## Vergleich geschätzt ↔ detailliert `GET …/comparison` summiert alle Gerätezeilen aller Stromkreislisten des Projekts (Anzahl × Leistung je Stück × Geräte-GZF, wie der Stromkreisbaum) und ordnet sie über das Feld **Kostengruppe** der Gerätezeile zu. Maßgeblich ist das längste passende Präfix: `434` → Gruppe 434, `431` → Gruppe 430, `444` → 440. Zeilen ohne passende Kostengruppe erscheinen als „nicht zugeordnet“. Der Verteiler-GZF wird auf keiner Seite angewendet; ein fehlendes cos φ zählt als 1. Verglichen wird die AV-Leistung (kW) mit Differenz absolut und in %. ## Übernahme als Projektgerät Die Zeilenaktion „Als Projektgerät übernehmen“ ist eine einmalige, ausdrücklich bestätigte Kopie. Vor dem Öffnen des Dialogs werden ausstehende Änderungen der Grobbilanz gespeichert. Das Anlegen läuft anschließend mit der aktuellen Projektrevision über den normalen Projektgeräte-Befehl und ist dadurch wie ein manuell angelegtes Projektgerät rückgängig machbar. - Name und Anzeigename kommen aus dem Zeilennamen; Kostengruppe, GZF, cos φ und Anmerkung werden vorgeschlagen. `fixed` und `socket` erscheinen als „Festanschluss“ und „Steckdose“. - W werden für Stückzeilen in kW umgerechnet. Die Projektgeräte-Anzahl ist 1, weil das Projektgerät als Vorlage für eine spätere Gerätezeile dient; die ursprüngliche Stückzahl bleibt im Herkunftshinweis sichtbar. - Die Kategorie ist mangels Phaseninformation in der Grobbilanz mit „1-phasig“ vorbelegt und muss im Dialog fachlich geprüft werden. - Flächenzeilen dürfen übernommen werden. Anzahl und Leistung je Stück bleiben leer und müssen manuell angegeben werden, da das Projektgerät noch keine Einheit W/m² kennt (siehe Issue #20). - SV wird nur im Herkunftshinweis gezeigt. Die tatsächliche Versorgungsart gehört zur Verteilung. - Gleichnamige vorhandene Projektgeräte erzeugen eine Warnung, sperren das Anlegen aber nicht. Zwischen Grobbilanz-Zeile und Projektgerät wird keine Verbindung gespeichert. Es gibt keine Stapelübernahme und keine automatische Platzierung in einer Verteilung oder einem Stromkreis. Snapshot-, Transfer- und Grobbilanz-Befehlsformate ändern sich dadurch nicht. ## Befehle, Undo/Redo Alle Änderungen laufen über den bestehenden Projekt-Befehlsmechanismus mit Revision, Inverse und persistentem Undo/Redo: - `estimate-balance.insert` / `estimate-balance.delete` mit dem vollständigen Stand (Gruppen und Zeilen) - `estimate-balance.update` mit vollständigem `expected`/`target`-Dokument; der Store prüft, dass der gespeicherte Stand genau `expected` entspricht, und ersetzt ihn atomar. Die Inverse ist das vertauschte Paar. Der Editor speichert das ganze Dokument verzögert (Autosave); jeder Speichervorgang ist ein Undo-Schritt. Unveränderte Dokumente erzeugen keine Revision (`{ unchanged: true }`). ## Projekttransfer und bewusste Abgrenzung - Das reguläre SQLite-Datenbank-Backup sichert die Grobbilanz-Tabellen zusammen mit allen übrigen Anwendungsdaten. - Benannte und automatische Projektsnapshots enthalten keine Grobbilanzen; das Snapshot-Schema bleibt Version 7. Snapshot-Restore und dessen Undo/Redo lassen vorhandene Grobbilanzen unverändert. - Der portable Projektexport enthält alle Grobbilanzen ab Transferformat 2. Ersetzen übernimmt sie atomar und rückgängig machbar; Duplizieren vergibt neue IDs für Bilanz, Gruppen und Zeilen. Alte Transferdateien in Version 1 bleiben importierbar und enthalten definitionsgemäß keine Grobbilanzen. - Keine Rechte je Gewerk; „bearbeitet von“ ist ein Freitext je Kostengruppe. - Kostengruppen der Gerätezeilen sind nicht normiert; der Vergleich arbeitet mit Präfixen. - Der erste Vergleich zeigt die geschätzte und detaillierte AV-Wirkleistung in kW. SV- und kVA-Vergleiche können später ergänzt werden. - Eine Stromkreisnummer gehört nicht zur Grobbilanz, weil sie eine frühe Abschätzung ohne Stromkreisplanung ist. ## API - `GET /api/projects/:projectId/estimate-balances` - `POST /api/projects/:projectId/estimate-balances` – `{ "expectedRevision": 7, "name": "Entwurfsplanung", "workStage": "LPH 3", "dateIso": "2026-09-26", "template": "kg400" }` (`template`: `kg400` | `empty` | `copy` mit `copyFromBalanceId`) - `GET /api/projects/:projectId/estimate-balances/:balanceId` - `PUT /api/projects/:projectId/estimate-balances/:balanceId` – `{ "expectedRevision": 8, "balance": { …vollständiges Dokument… } }` - `DELETE /api/projects/:projectId/estimate-balances/:balanceId` – `{ "expectedRevision": 9 }` - `GET /api/projects/:projectId/estimate-balances/:balanceId/comparison` Validierung: Name 1–200 Zeichen, getrimmte Texte, Anzahl/Fläche und Einzelleistung ≥ 0, GLF 0–1, cos φ > 0 bis 1, Reserve 0–100 %, Datum `YYYY-MM-DD`, eindeutige Gruppen- und Zeilen-IDs, unbekannte Felder werden abgelehnt.