# Handover: Texte und Logik in `app_verbrauch/service.py`

Stand: 2026-09-25, Branches `dev` und `main`

Anlass war die Prüfung der Erklärtexte zum Verbrauchsvergleich
(`DIAGRAMM_STYLE_AND_TEXT` in `app_verbrauch/service.py`). Ein Teil ist
bereits behoben, die folgenden Punkte sind noch offen.

## Bereits erledigt

| Commit | Inhalt |
|---|---|
| `40f86f3` | Trinkwasser (110): Text sprach fälschlich von Raumwärme/Energiebedarf; Hinweis „Weitere Informationen zum normierten Energieverbrauch" wird bei den reinen Wassermengen (110, 111) nicht mehr angehängt |
| `727a37f` | HKV (99) spricht jetzt vom Verbrauch der Heizkostenverteiler statt vom Energiebedarf; 101 `MassEinheitSpez` auf `kWh/m²` korrigiert; 102 auf „gelieferte Wärme" mit Hinweis auf die fehlende Trennung Raumwärme/Trinkwarmwasser; neuer Helper `_de_zahl()` |
| `328e4d4` | Tippfehler „Durchnittsnutzers" und „Energieverbruach"; `TitleSpez` von 99 an gHKV/m² angeglichen; Verbrauchsart 102 in `DIAGRAMM_TYPES` und `DIAGRAMM_TYPES_SPEZ` aufgenommen |

## Offene Punkte

### 1. Verbrauchsart 102 gegen echte Daten testen

102 war bis `328e4d4` in keiner Ausgabeliste enthalten und wurde damit nie
ausgeliefert. Jetzt ist sie in `DIAGRAMM_TYPES` und `DIAGRAMM_TYPES_SPEZ`
aufgenommen. Beide Schleifen filtern gegen die tatsächlich vorhandenen
Verbrauchsrahmen, die Karte erscheint also nur bei Nutzern mit 102-Daten.
Geprüft wurde bisher nur statisch: das Frontend rendert generisch pro
`ChartType` (`verbrauch_neu.html`, `verbrauch_spez.html`), es gibt dort keine
Whitelist. Ein Test mit einer Einheit mit zentraler Wärmeversorgung steht aus.

### 2. Verbrauchsart 111 erscheint in der normierten Ansicht ohne Erklärtext

`get_all_chart_data_spez()` iteriert über `DIAGRAMM_TYPES` statt über
`DIAGRAMM_TYPES_SPEZ`. Damit kommt auch 111 (Trinkwarmwasser, m³) in die
normierte Ansicht, obwohl:

* `TextSpez` von 111 leer ist — die Karte hat dort keinen Erklärtext,
* `TitleSpez` „Verbrauch für Trinkwarmwasser" lautet und gar nicht als
  normiert gekennzeichnet ist.

`DIAGRAMM_TYPES_SPEZ` wird aktuell nirgends verwendet und ist damit eine tote
Konstante.

Zu entscheiden: entweder `get_all_chart_data_spez()` auf
`DIAGRAMM_TYPES_SPEZ` umstellen (dann fällt 111 aus der normierten Ansicht
heraus), oder 111 einen `TextSpez` und einen sprechenden `TitleSpez` geben.

### 3. Vergleich Volljahr gegen Teiljahr

In allen drei Chart-Funktionen entsteht der Vergleichssatz, sobald die
*Einheit* zwölf Monatswerte hat (`if len(rows) == 12`). Die Referenzreihe
`ref_rows` kann weniger Monate enthalten. Dann wird eine Jahressumme gegen
eine Teiljahressumme gestellt und als „höher als der Durchschnittsnutzer"
ausgegeben — die Aussage ist in dem Fall falsch.

Vorschlag: zusätzlich `len(ref_rows) == 12` prüfen, oder beide Reihen auf die
gemeinsamen Monate beschränken.

### 4. `delta == 0` landet im else-Zweig

In `get_verbrauchs_chart_daten_spez()` führt ein Verbrauch exakt auf
Referenzniveau zur Ausgabe „war somit 0,00 kWh/m² niedriger". Für diesen Fall
fehlt ein eigener Satz, etwa „entsprach dem Verbrauch unseres
Durchschnittsnutzers".

### 5. Kleine Grammatikfehler in den statischen Texten

* Art 99, `Text`: „Weitere Information zur Gewichtung" → „Weitere Informationen"
* Art 999, `Text`: „Keine Weiteren Informationen hinterlegt" → „weiteren"

### 6. Verbrauchsrahmen ohne `MietVID` werden von `service.py` nicht gefunden

> **Erledigt (29.10.2026):** Dashcards (`get_all_dash_cards_data`,
> `get_dash_card_daten`, `_check_verbrauchsrahmen_exists`) und Diagrammwerte
> (`_fetch_verbrauchswerte`) suchen jetzt über `REUID` + `PersonenID`;
> `_get_mietv_id` ist entfernt. Tests: `app_verbrauch/tests.py`. Nicht
> umgestellt sind die unbenutzten Altfunktionen `get_verbrauchs_chart_daten`,
> `_fetch_reu_data` und `process_mietv_id`.

Seit Migration `0006` darf `tbl_v_i_sys_ver_rahmen.MietVID` NULL sein. Grund:
der VISys-Sync ordnet Verbrauchsrahmen inzwischen über `PersonenID` + `REUID`
zu statt über den Mietvertrag. Vorher stand die Spalte in der DB auf NOT NULL
(das Modell sagte schon immer `null=True`, die Tabelle ist `managed=False`),
und `PUT /api/verbrrahmen` lief für solche Rahmen in
`IntegrityError 1048: Column 'MietVID' cannot be null` — der Sync brach mit
HTTP 500 ab.

Damit entstehen jetzt regulär Rahmen mit `MietVID IS NULL`. Drei Raw-SQL-
Abfragen in `app_verbrauch/service.py` filtern aber weiterhin über den
Mietvertrag:

* `service.py:381` — `WHERE vr.MietVID = %s AND vr.VISysVerbrauchsArtID = %s`
* `service.py:414` — dieselbe Bedingung
* `service.py:482` — `WHERE VISysVerbrauchsArtID = %s AND MietVID = %s`

Diese Rahmen fallen aus der Auswertung heraus; der Verbrauch der betroffenen
Nutzer erscheint im Portal nicht. Die Migration allein behebt das nicht — sie
sorgt nur dafür, dass die Daten überhaupt ankommen.

Zu entscheiden: Fallback auf `PersonenID`/`REUID`, wenn `MietVID` NULL ist,
oder die drei Abfragen ganz auf das neue Paar umstellen. Dazu passt der
bestehende `# TODO: Diese Funktion muss weg!!!!` an `_get_mietv_id()`
(`service.py:618`), das die Umkehrung MietVID ← REUID + NutzerID macht.

## Hinweise zum Code

* Zahlen im Fließtext ausschließlich über `_de_zahl()` formatieren. Vorher lief
  die Ersetzung `,`→`.` / `.`→`,` über den ganzen Satz und hat die Satzpunkte
  zu Kommas gemacht („… im gleichen Zeitraum,"). Behoben in `727a37f`.
* `get_verbrauchs_chart_daten()` und `get_verbrauchs_chart_daten_neu()` enthalten
  denselben Textbaustein doppelt. Textänderungen also an beiden Stellen
  nachziehen, oder die beiden Funktionen zusammenführen.
