# 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 in Stromkreislisten, Verteilungen oder Projektgeräte. ## 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 %. ## 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.