diff --git a/rhino/elemente_uebersicht.py b/rhino/elemente_uebersicht.py
index 82636f6..0df407c 100644
--- a/rhino/elemente_uebersicht.py
+++ b/rhino/elemente_uebersicht.py
@@ -208,6 +208,88 @@ class ElementeUebersichtBridge(panel_base.BaseBridge):
print("[UEBERSICHT] zoom:", ex)
except Exception as ex:
print("[UEBERSICHT] zoom find:", ex)
+ elif t == "EXPORT_BILANZ":
+ self._export_bilanz()
+
+ def _export_bilanz(self):
+ """Exportiert SIA-416 Bilanz als CSV (Excel-kompatibel: Semikolon-
+ Separator + UTF-8 BOM + Komma als Dezimaltrenner). Wide-Format:
+ eine Spalte pro Geschoss + Total-Spalte, Zeilen pro Kategorie.
+ """
+ doc = Rhino.RhinoDoc.ActiveDoc
+ if doc is None: return
+ # Geschoss-Liste (geordnet) + Total am Ende
+ geschosse = _elm._load_geschosse(doc) or []
+ gs_list = [g for g in geschosse
+ if isinstance(g, dict) and g.get("isGeschoss")]
+ # Bilanz pro Geschoss + Total via compute_sia_bilanz
+ per_gid = {} # gid → bilanz dict
+ for g in gs_list:
+ per_gid[g["id"]] = _elm.compute_sia_bilanz(
+ doc, "geschoss:" + g["id"])
+ total = _elm.compute_sia_bilanz(doc, "total")
+ # SaveFileDialog
+ try:
+ from Rhino.UI import SaveFileDialog
+ sfd = SaveFileDialog()
+ sfd.DefaultExt = "csv"
+ sfd.Filter = "CSV (*.csv)|*.csv"
+ sfd.FileName = "sia_bilanz.csv"
+ ok = False
+ try: ok = sfd.ShowSaveDialog()
+ except Exception:
+ try: ok = sfd.ShowDialog()
+ except Exception: ok = False
+ if not ok:
+ print("[UEBERSICHT] Bilanz-Export abgebrochen"); return
+ path = sfd.FileName
+ except Exception as ex:
+ print("[UEBERSICHT] SaveFileDialog:", ex); return
+
+ # Zeilen-Definition: (Label, Bilanz-Key, ist_personen?)
+ rows = [
+ ("HNF (m²)", "hnf", False),
+ ("NNF (m²)", "nnf", False),
+ ("NF (m²)", "nf", False),
+ ("VF (m²)", "vf", False),
+ ("FF (m²)", "ff", False),
+ ("NGF (m²)", "ngf", False),
+ ("GF (m²)", "gf", False),
+ ("AGF (m²)", "agf", False),
+ ("Räume", "count", True),
+ ("Personen", "personen", True),
+ ]
+
+ def _fmt(val, is_count):
+ if val is None: return ""
+ if is_count: return str(int(val))
+ return "{:.2f}".format(float(val)).replace(".", ",")
+
+ def _esc(s):
+ s = str(s)
+ if ";" in s or '"' in s or "\n" in s:
+ return '"' + s.replace('"', '""') + '"'
+ return s
+
+ try:
+ import io
+ with io.open(path, "w", encoding="utf-8-sig", newline="") as f:
+ # Header — Kategorie + Geschoss-Namen + Total
+ header = ["Kategorie"]
+ for g in gs_list: header.append(_esc(g.get("name") or "?"))
+ header.append("Total")
+ f.write(";".join(header) + "\n")
+ for label, key, is_count in rows:
+ line = [_esc(label)]
+ for g in gs_list:
+ b = per_gid.get(g["id"], {})
+ line.append(_fmt(b.get(key, 0), is_count))
+ line.append(_fmt(total.get(key, 0), is_count))
+ f.write(";".join(line) + "\n")
+ print("[UEBERSICHT] SIA-Bilanz exportiert: {} ({} Geschosse + Total)".format(
+ path, len(gs_list)))
+ except Exception as ex:
+ print("[UEBERSICHT] CSV schreiben:", ex)
def open_as_window():
diff --git a/src/ElementeUebersichtApp.jsx b/src/ElementeUebersichtApp.jsx
index 11e74bc..bd0a9ab 100644
--- a/src/ElementeUebersichtApp.jsx
+++ b/src/ElementeUebersichtApp.jsx
@@ -128,6 +128,9 @@ export default function ElementeUebersichtApp() {
}} />
+ send('EXPORT_BILANZ', {})}
+ title="SIA-416 Bilanz als CSV exportieren (eine Spalte pro Geschoss + Total)" />
{/* Kind-Filter Chips */}