Преглед на файлове

ajax ui updater eingebaut für meite

Leonard Schulz преди 1 година
родител
ревизия
11d9f29760

+ 112 - 0
src/main/java/com/tsystems/inwo/vgl/web/MieteController.java

@@ -120,9 +120,121 @@ public class MieteController {
         return "miete";
     }
 
+    @PostMapping("/api/vorgang-change")
+    @ResponseBody
+    public ResponseEntity<VorgangChangeResponse> changeLeftVorgang(
+            @RequestParam String leftJson,
+            @RequestParam String rightJson,
+            @RequestParam String rightZeitraumId,
+            @RequestParam String newLeftVorgangId) throws Exception {
+
+        JsonNode left = om.readTree(leftJson);
+        JsonNode right = om.readTree(rightJson);
+
+        // --- Links (Baseline) Kandidaten & neue Auswahl ---
+        List<Vorgang> leftCand = jsonNavigationService.parseLeftCandidates(left);
+        
+        final String finalLeftVorgangId = newLeftVorgangId;
+        List<Z> leftZ = leftCand.stream()
+                .filter(v -> v.id().equals(finalLeftVorgangId))
+                .findFirst()
+                .orElse(leftCand.get(0))
+                .zeitraeume();
+        
+        // Ersten Zeitraum des neuen Vorgangs als Default wählen
+        String newLeftZeitraumId = !leftZ.isEmpty() ? leftZ.get(0).id() : null;
+        
+        JsonNode rightZeitraum = jsonNavigationService.findZeitraumById(right, rightZeitraumId);
+        JsonNode leftZeitraum = null;
+        if (newLeftZeitraumId != null) {
+            leftZeitraum = jsonNavigationService.findZeitraumById(jsonNavigationService.getRootOfVorgang(left, newLeftVorgangId), newLeftZeitraumId);
+        }
+
+        // --- Wohnungsvergleich ---
+        WohnungZeile wohnungZeile = dataExtractor.buildWohnungZeile(rightZeitraum, leftZeitraum);
+
+        // --- Mietedaten ---
+        MieteZeile mieteZeile = dataExtractor.buildMieteZeile(rightZeitraum, leftZeitraum);
+
+        // --- Untermieter und Mitbewohner ---
+        List<UntermieterZeile> untermieterZeilen = dataExtractor.buildUntermieterZeilen(rightZeitraum, leftZeitraum);
+        List<MitbewohnerZeile> mitbewohnerZeilen = dataExtractor.buildMitbewohnerZeilen(rightZeitraum, leftZeitraum);
+
+        VorgangChangeResponse response = new VorgangChangeResponse(
+                newLeftVorgangId,
+                newLeftZeitraumId,
+                leftZ,
+                wohnungZeile,
+                mieteZeile,
+                untermieterZeilen,
+                mitbewohnerZeilen
+        );
+
+        return ResponseEntity.ok(response);
+    }
+
+    @PostMapping("/api/zeitraum-change")
+    @ResponseBody
+    public ResponseEntity<ZeitraumChangeResponse> changeLeftZeitraum(
+            @RequestParam String leftJson,
+            @RequestParam String rightJson,
+            @RequestParam String leftVorgangId,
+            @RequestParam String rightZeitraumId,
+            @RequestParam String newLeftZeitraumId) throws Exception {
+
+        JsonNode left = om.readTree(leftJson);
+        JsonNode right = om.readTree(rightJson);
+
+        JsonNode rightZeitraum = jsonNavigationService.findZeitraumById(right, rightZeitraumId);
+        JsonNode leftZeitraum = null;
+        if (newLeftZeitraumId != null) {
+            leftZeitraum = jsonNavigationService.findZeitraumById(jsonNavigationService.getRootOfVorgang(left, leftVorgangId), newLeftZeitraumId);
+        }
+
+        // --- Wohnungsvergleich ---
+        WohnungZeile wohnungZeile = dataExtractor.buildWohnungZeile(rightZeitraum, leftZeitraum);
+
+        // --- Mietedaten ---
+        MieteZeile mieteZeile = dataExtractor.buildMieteZeile(rightZeitraum, leftZeitraum);
+
+        // --- Untermieter und Mitbewohner ---
+        List<UntermieterZeile> untermieterZeilen = dataExtractor.buildUntermieterZeilen(rightZeitraum, leftZeitraum);
+        List<MitbewohnerZeile> mitbewohnerZeilen = dataExtractor.buildMitbewohnerZeilen(rightZeitraum, leftZeitraum);
+
+        ZeitraumChangeResponse response = new ZeitraumChangeResponse(
+                newLeftZeitraumId,
+                wohnungZeile,
+                mieteZeile,
+                untermieterZeilen,
+                mitbewohnerZeilen
+        );
+
+        return ResponseEntity.ok(response);
+    }
+
     // ===== Private Helper Methods =====
     
     private String resolveActiveKunde(String kunde, String kundeHeader) {
         return kunde != null ? kunde : kundeHeader;
     }
+
+    // ===== Response Classes =====
+    
+    public record VorgangChangeResponse(
+        String newLeftVorgangId,
+        String newLeftZeitraumId,
+        List<Z> leftZeitraeumeOfSelected,
+        WohnungZeile wohnungZeile,
+        MieteZeile mieteZeile,
+        List<UntermieterZeile> untermieterZeilen,
+        List<MitbewohnerZeile> mitbewohnerZeilen
+    ) {}
+    
+    public record ZeitraumChangeResponse(
+        String newLeftZeitraumId,
+        WohnungZeile wohnungZeile,
+        MieteZeile mieteZeile,
+        List<UntermieterZeile> untermieterZeilen,
+        List<MitbewohnerZeile> mitbewohnerZeilen
+    ) {}
 }

+ 0 - 188
src/main/resources/static/example-miete-neu.json

@@ -113,194 +113,6 @@
               "wofgJn": false,
               "foerderungJn": true
             }
-          },
-          {
-            "id": "zeitraum-2",
-            "beginn": "2024-01-01",
-            "ende": "2024-06-30",
-            "miete": {
-              "id": "miete-2",
-              "miete": 720.00,
-              "mieteAlt": 680.00,
-              "sonstZuschlag": 45.00,
-              "sonstZuschlagBeschr": "Hausmeisterservice + Garten + Reinigung",
-              "beitragDritter": 120.00,
-              "heizung": 150.00,
-              "heizungP": "Fernwärme + Solar",
-              "warmwasser": 65.00,
-              "warmwasserP": "zentral + Solar",
-              "moeblierung": 75.00,
-              "moeblierungP": "Küche + Wohnzimmer möbliert",
-              "umietZuschlag": 50.00,
-              "gewerbeZuschlag": 45.00,
-              "gewerbeZuschlagP": "Büronutzung + Lager",
-              "gewerbeFlaeche": 15.50,
-              "jhrlFernwarmw": 780.00,
-              "jhrlFernwaerme": 1800.00,
-              "antMietWw": 65.00,
-              "antBrutWw": 70.00,
-              "jhrlFernWUndW": 2580.00,
-              "lstgAufenthaltPerson": 150.00,
-              "sonstWogFaehigBetrag": 40.00,
-              "sonstWogFaehigText": "Balkon + Gartennutzung + Keller",
-              "energie": 105.00,
-              "energieP": "Strom + Gas + Ökostrom",
-              "stellplatz": 40.00,
-              "stellplatzP": "Tiefgarage",
-              "untermieter": [
-                {
-                  "miId": "miete-2",
-                  "lfdnr": 1,
-                  "name": "Chen",
-                  "vorname": "Li",
-                  "flaeche": 22.0,
-                  "miete": 220.00,
-                  "heizung": 35.00,
-                  "heizungP": "anteilig",
-                  "warmwasser": 18.00,
-                  "warmwasserP": "anteilig",
-                  "moeblierung": 40.00,
-                  "moeblierungP": "vollmöbliert",
-                  "sonst": 12.00
-                }
-              ],
-              "mitbewohner": [
-                {
-                  "miId": "miete-2",
-                  "lfdnr": 1,
-                  "name": "Garcia",
-                  "vorname": "Maria",
-                  "entgelt": 160.00,
-                  "heizung": 22.00,
-                  "heizungP": "anteilig",
-                  "warmwasser": 13.00,
-                  "warmwasserP": "anteilig",
-                  "sonst": 7.00
-                },
-                {
-                  "miId": "miete-2",
-                  "lfdnr": 2,
-                  "name": "Brown",
-                  "vorname": "James",
-                  "entgelt": 170.00,
-                  "heizung": 24.00,
-                  "heizungP": "anteilig",
-                  "warmwasser": 14.00,
-                  "warmwasserP": "anteilig",
-                  "sonst": 8.00
-                }
-              ]
-            },
-            "ausstattung": {
-              "id": 200002,
-              "flaeche": 78.90,
-              "heizung": "ZH",
-              "bad": "V",
-              "sozBauJn": false,
-              "mietverhaeltnis": "EM",
-              "eigentum": "PRI",
-              "fertigDat": "2010-06-20",
-              "nameEig": "Müller Immobilien GmbH",
-              "adresseEig": "Potsdamer Platz 5, 10785 Berlin",
-              "telefonEig": "030-98765432",
-              "wofgJn": false,
-              "foerderungJn": true
-            }
-          },
-          {
-            "id": "zeitraum-3",
-            "beginn": "2024-07-01",
-            "miete": {
-              "id": "miete-3",
-              "miete": 750.00,
-              "mieteAlt": 720.00,
-              "sonstZuschlag": 55.00,
-              "sonstZuschlagBeschr": "Vollservice + Premium-Ausstattung",
-              "beitragDritter": 150.00,
-              "heizung": 160.00,
-              "heizungP": "Fernwärme + Solar + Wärmepumpe",
-              "warmwasser": 75.00,
-              "warmwasserP": "zentral + Solar + dezentral",
-              "moeblierung": 120.00,
-              "moeblierungP": "vollständig möbliert + Elektrogeräte",
-              "umietZuschlag": 65.00,
-              "gewerbeZuschlag": 95.00,
-              "gewerbeZuschlagP": "Büro + Praxis-Nutzung + Konferenzraum",
-              "gewerbeFlaeche": 32.50,
-              "jhrlFernwarmw": 900.00,
-              "jhrlFernwaerme": 1920.00,
-              "antMietWw": 75.00,
-              "antBrutWw": 80.00,
-              "jhrlFernWUndW": 2820.00,
-              "lstgAufenthaltPerson": 300.00,
-              "sonstWogFaehigBetrag": 80.00,
-              "sonstWogFaehigText": "Dachterrasse + Gartenanteil + Keller + Abstellraum",
-              "energie": 125.00,
-              "energieP": "Vollökostrom + Gas + Solar + E-Mobility",
-              "stellplatz": 60.00,
-              "stellplatzP": "Duplex-Tiefgarage mit E-Ladestation + Überdachung",
-              "untermieter": [
-                {
-                  "miId": "miete-3",
-                  "lfdnr": 1,
-                  "name": "Andersen",
-                  "vorname": "Lars",
-                  "flaeche": 28.0,
-                  "miete": 280.00,
-                  "heizung": 45.00,
-                  "heizungP": "anteilig",
-                  "warmwasser": 22.00,
-                  "warmwasserP": "anteilig",
-                  "moeblierung": 60.00,
-                  "moeblierungP": "Designer-Möbel",
-                  "sonst": 18.00
-                },
-                {
-                  "miId": "miete-3",
-                  "lfdnr": 2,
-                  "name": "Kowalski",
-                  "vorname": "Anna",
-                  "flaeche": 20.0,
-                  "miete": 200.00,
-                  "heizung": 32.00,
-                  "heizungP": "anteilig",
-                  "warmwasser": 16.00,
-                  "warmwasserP": "anteilig",
-                  "moeblierung": 35.00,
-                  "moeblierungP": "standard möbliert",
-                  "sonst": 10.00
-                }
-              ],
-              "mitbewohner": [
-                {
-                  "miId": "miete-3",
-                  "lfdnr": 1,
-                  "name": "Rossi",
-                  "vorname": "Marco",
-                  "entgelt": 200.00,
-                  "heizung": 28.00,
-                  "heizungP": "anteilig",
-                  "warmwasser": 16.00,
-                  "warmwasserP": "anteilig",
-                  "sonst": 12.00
-                }
-              ]
-            },
-            "ausstattung": {
-              "id": 200003,
-              "flaeche": 95.20,
-              "heizung": "WP",
-              "bad": "L",
-              "sozBauJn": false,
-              "mietverhaeltnis": "EM",
-              "eigentum": "PRI",
-              "fertigDat": "2020-12-15",
-              "nameEig": "Premium Living Berlin GmbH",
-              "adresseEig": "Unter den Linden 77, 10117 Berlin",
-              "telefonEig": "030-11111111",
-              "wofgJn": false,
-              "foerderungJn": false
-            }
           }
         ]
       }

+ 53 - 39
src/main/resources/static/js/ajax-handlers.js

@@ -1,86 +1,81 @@
 // AJAX-Funktionen für Vorgang- und Zeitraumauswahl
 
-// AJAX-Funktion für Vorgangsauswahl - MIT DEBUG LOGGING
+// Hilfsfunktion um die aktuelle Base-URL zu ermitteln
+function getBaseUrl() {
+  const currentPath = window.location.pathname;
+  if (currentPath.includes('/miete')) {
+    return '/miete';
+  } else if (currentPath.includes('/hhm-einkommen')) {
+    return '/hhm-einkommen';
+  }
+  // Default fallback
+  return '/hhm-einkommen';
+}
+
+// AJAX-Funktion für Vorgangsauswahl
 function changeLeftVorgang(newLeftVorgangId) {
-  console.log('🚀 changeLeftVorgang aufgerufen mit:', newLeftVorgangId);
-  
   const currentLeftVorgangId = document.getElementById('leftVorgangId').value;
-  console.log('📊 Aktueller leftVorgangId:', currentLeftVorgangId);
   
   // Prüfen ob bereits der ausgewählte Vorgang
   if (currentLeftVorgangId === newLeftVorgangId) {
-    console.log('⚠️ Gleicher Vorgang bereits ausgewählt, überspringe');
     return; // Nichts zu tun
   }
 
   // Loading-Zustand anzeigen
   showLoadingState();
-  console.log('⏳ Loading-Zustand angezeigt');
   
   // Parameter sammeln
   const leftJson = document.getElementById('leftJson').value;
   const rightJson = document.getElementById('rightJson').value;
   const rightZeitraumId = document.getElementById('rightZeitraumId').value;
-  const personId = document.getElementById('currentPersonId').value;
-  
-  console.log('📋 Request Parameter:');
-  console.log('  leftJson length:', leftJson ? leftJson.length : 'null');
-  console.log('  rightJson length:', rightJson ? rightJson.length : 'null');
-  console.log('  rightZeitraumId:', rightZeitraumId);
-  console.log('  personId:', personId);
-  console.log('  newLeftVorgangId:', newLeftVorgangId);
+  const personId = document.getElementById('currentPersonId') ? document.getElementById('currentPersonId').value : null;
   
   // AJAX-Request senden
   const formData = new FormData();
   formData.append('leftJson', leftJson);
   formData.append('rightJson', rightJson);
   formData.append('rightZeitraumId', rightZeitraumId);
-  formData.append('personId', personId);
+  if (personId) {
+    formData.append('personId', personId);
+  }
   formData.append('newLeftVorgangId', newLeftVorgangId);
 
-  console.log('🌐 Sende AJAX-Request an /hhm-einkommen/api/vorgang-change');
+  const baseUrl = getBaseUrl();
+  const apiUrl = baseUrl + '/api/vorgang-change';
   
-  fetch('/hhm-einkommen/api/vorgang-change', {
+  fetch(apiUrl, {
     method: 'POST',
     body: formData
   })
   .then(response => {
-    console.log('📡 Response erhalten:', response.status, response.statusText);
     if (!response.ok) {
       throw new Error('Netzwerk-Antwort war nicht ok: ' + response.status);
     }
     return response.json();
   })
   .then(data => {
-    console.log('✅ Erfolgreiche Antwort:', data);
     // Erfolgreiche Antwort verarbeiten
     updateAfterVorgangChange(data);
     hideLoadingState();
-    console.log('🔄 UI erfolgreich aktualisiert');
   })
   .catch(error => {
-    console.error('❌ Fehler beim Ändern des Vorgangs:', error);
+    console.error('Fehler beim Ändern des Vorgangs:', error);
     hideLoadingState();
     alert('Fehler beim Ändern des Vorgangs: ' + error.message + '. Bitte versuchen Sie es erneut.');
   });
 }
 
-// AJAX-Funktion für Zeitraumauswahl - MIT DEBUG LOGGING  
+// AJAX-Funktion für Zeitraumauswahl
 function changeLeftZeitraum(newLeftZeitraumId) {
-  console.log('🚀 changeLeftZeitraum aufgerufen mit:', newLeftZeitraumId);
-  
   const currentLeftZeitraumId = document.getElementById('leftZeitraumId').value;
-  console.log('📊 Aktueller leftZeitraumId:', currentLeftZeitraumId);
   
   // Prüfen ob bereits der ausgewählte Zeitraum
   if (currentLeftZeitraumId === newLeftZeitraumId) {
-    console.log('⚠️ Gleicher Zeitraum bereits ausgewählt, überspringe');
     return; // Nichts zu tun
   }
 
   // Loading-Zustand anzeigen
   showLoadingState();
-  console.log('⏳ Loading-Zustand angezeigt');
   
   // AJAX-Request senden
   const formData = new FormData();
@@ -88,31 +83,32 @@ function changeLeftZeitraum(newLeftZeitraumId) {
   formData.append('rightJson', document.getElementById('rightJson').value);
   formData.append('leftVorgangId', document.getElementById('leftVorgangId').value);
   formData.append('rightZeitraumId', document.getElementById('rightZeitraumId').value);
-  formData.append('personId', document.getElementById('currentPersonId').value);
+  const personId = document.getElementById('currentPersonId') ? document.getElementById('currentPersonId').value : null;
+  if (personId) {
+    formData.append('personId', personId);
+  }
   formData.append('newLeftZeitraumId', newLeftZeitraumId);
 
-  console.log('🌐 Sende AJAX-Request an /hhm-einkommen/api/zeitraum-change');
+  const baseUrl = getBaseUrl();
+  const apiUrl = baseUrl + '/api/zeitraum-change';
 
-  fetch('/hhm-einkommen/api/zeitraum-change', {
+  fetch(apiUrl, {
     method: 'POST',
     body: formData
   })
   .then(response => {
-    console.log('📡 Response erhalten:', response.status, response.statusText);
     if (!response.ok) {
       throw new Error('Netzwerk-Antwort war nicht ok: ' + response.status);
     }
     return response.json();
   })
   .then(data => {
-    console.log('✅ Erfolgreiche Antwort:', data);
     // Erfolgreiche Antwort verarbeiten
     updateAfterZeitraumChange(data);
     hideLoadingState();
-    console.log('🔄 UI erfolgreich aktualisiert');
   })
   .catch(error => {
-    console.error('❌ Fehler beim Ändern des Zeitraums:', error);
+    console.error('Fehler beim Ändern des Zeitraums:', error);
     hideLoadingState();
     alert('Fehler beim Ändern des Zeitraums: ' + error.message + '. Bitte versuchen Sie es erneut.');
   });
@@ -123,12 +119,16 @@ function selectPersonAjax(personId) {
   const currentPersonId = document.getElementById('currentPersonId');
   
   // Prüfen ob bereits die ausgewählte Person
-  if (currentPersonId.value === personId) {
+  if (currentPersonId && currentPersonId.value === personId) {
     return; // Nichts zu tun
   }
 
   // Loading-Zustand anzeigen
   const detailsTitle = document.getElementById('person-details-title');
+  if (!detailsTitle) {
+    return;
+  }
+  
   const originalTitle = detailsTitle.innerHTML;
   detailsTitle.innerHTML = 'Details werden geladen...';
   
@@ -141,7 +141,10 @@ function selectPersonAjax(personId) {
   formData.append('rightZeitraumId', document.getElementById('rightZeitraumId').value);
   formData.append('personId', personId);
 
-  fetch('/hhm-einkommen/api/person-details', {
+  const baseUrl = getBaseUrl();
+  const apiUrl = baseUrl + '/api/person-details';
+
+  fetch(apiUrl, {
     method: 'POST',
     body: formData
   })
@@ -156,7 +159,9 @@ function selectPersonAjax(personId) {
     updatePersonDetails(data);
     
     // Aktuelle Person aktualisieren
-    currentPersonId.value = personId;
+    if (currentPersonId) {
+      currentPersonId.value = personId;
+    }
     
     // Person-Selected-Klasse aktualisieren
     updateSelectedPersonRow(personId);
@@ -175,10 +180,19 @@ function selectPersonAjax(personId) {
 }
 
 function showLoadingState() {
+  // Verschiedene Loading-Zustände je nach Seite
   const detailsTitle = document.getElementById('person-details-title');
-  detailsTitle.innerHTML = 'Daten werden geladen...';
+  if (detailsTitle) {
+    detailsTitle.innerHTML = 'Daten werden geladen...';
+  }
+  
+  // Alternativ für Miete-Seite könnte ein anderer Loading-Indikator verwendet werden
+  const mieteContainer = document.querySelector('.miete-container');
+  if (mieteContainer && !detailsTitle) {
+    // Miete-spezifischer Loading-Zustand
+  }
 }
 
 function hideLoadingState() {
-  // Loading-Zustand wird durch updatePersonDetails überschrieben
+  // Loading-Zustand wird durch entsprechende Update-Funktionen überschrieben
 }

+ 43 - 30
src/main/resources/static/js/ui-updater.js → src/main/resources/static/js/ui-updater-hhm-einkommen.js

@@ -1,54 +1,63 @@
-// UI-Update-Funktionen
+// UI-Update-Funktionen für HHM-Einkommen
 
 function updateAfterVorgangChange(data) {
-  console.log('🔄 updateAfterVorgangChange aufgerufen mit:', data);
-  
   // Hidden fields aktualisieren
-  document.getElementById('leftVorgangId').value = data.leftVorgangId;
-  document.getElementById('leftZeitraumId').value = data.leftZeitraumId;
-  document.getElementById('currentPersonId').value = data.personId;
+  document.getElementById('leftVorgangId').value = data.newLeftVorgangId || data.leftVorgangId;
+  document.getElementById('leftZeitraumId').value = data.newLeftZeitraumId || data.leftZeitraumId;
+  
+  const currentPersonId = document.getElementById('currentPersonId');
+  if (currentPersonId) {
+    currentPersonId.value = data.personId;
+  }
   
   // Vorgang-Dropdown aktualisieren (falls erforderlich)
   const vorgangSelect = document.getElementById('leftVorgangSelect');
-  if (vorgangSelect.value !== data.leftVorgangId) {
-    vorgangSelect.value = data.leftVorgangId;
+  if (vorgangSelect && (data.newLeftVorgangId || data.leftVorgangId)) {
+    const newVorgangId = data.newLeftVorgangId || data.leftVorgangId;
+    if (vorgangSelect.value !== newVorgangId) {
+      vorgangSelect.value = newVorgangId;
+    }
   }
   
   // Zeitraum-Dropdown aktualisieren
   const zeitraumSelect = document.getElementById('leftZeitraumSelect');
-  zeitraumSelect.innerHTML = '';
-  data.leftZeitraeume.forEach(zeitraum => {
-    const option = document.createElement('option');
-    option.value = zeitraum.id;
-    option.textContent = zeitraum.beginn + ' - ' + zeitraum.ende;
-    if (zeitraum.id === data.leftZeitraumId) {
-      option.selected = true;
-    }
-    zeitraumSelect.appendChild(option);
-  });
-  
-  console.log('📋 Zeitraum-Dropdown aktualisiert mit', data.leftZeitraeume.length, 'Einträgen');
+  if (zeitraumSelect && (data.leftZeitraeumeOfSelected || data.leftZeitraeume)) {
+    zeitraumSelect.innerHTML = '';
+    const zeitraeume = data.leftZeitraeumeOfSelected || data.leftZeitraeume;
+    zeitraeume.forEach(zeitraum => {
+      const option = document.createElement('option');
+      option.value = zeitraum.id;
+      option.textContent = zeitraum.beginn + ' - ' + zeitraum.ende;
+      if (zeitraum.id === (data.newLeftZeitraumId || data.leftZeitraumId)) {
+        option.selected = true;
+      }
+      zeitraumSelect.appendChild(option);
+    });
+  }
   
   // Haushaltsmitglieder-Tabelle aktualisieren
   updateHaushaltsmitgliederTable(data.mitgliederRows, data.personId);
   
   // Personendetails aktualisieren
   updatePersonDetails(data);
-  
-  console.log('✅ updateAfterVorgangChange abgeschlossen');
 }
 
 function updateAfterZeitraumChange(data) {
-  console.log('🔄 updateAfterZeitraumChange aufgerufen mit:', data);
-  
   // Hidden field aktualisieren
-  document.getElementById('leftZeitraumId').value = data.leftZeitraumId;
-  document.getElementById('currentPersonId').value = data.personId;
+  document.getElementById('leftZeitraumId').value = data.newLeftZeitraumId || data.leftZeitraumId;
+  
+  const currentPersonId = document.getElementById('currentPersonId');
+  if (currentPersonId) {
+    currentPersonId.value = data.personId;
+  }
   
   // Zeitraum-Dropdown aktualisieren (falls erforderlich)
   const zeitraumSelect = document.getElementById('leftZeitraumSelect');
-  if (zeitraumSelect.value !== data.leftZeitraumId) {
-    zeitraumSelect.value = data.leftZeitraumId;
+  if (zeitraumSelect) {
+    const newZeitraumId = data.newLeftZeitraumId || data.leftZeitraumId;
+    if (zeitraumSelect.value !== newZeitraumId) {
+      zeitraumSelect.value = newZeitraumId;
+    }
   }
   
   // Haushaltsmitglieder-Tabelle aktualisieren
@@ -56,12 +65,12 @@ function updateAfterZeitraumChange(data) {
   
   // Personendetails aktualisieren
   updatePersonDetails(data);
-  
-  console.log('✅ updateAfterZeitraumChange abgeschlossen');
 }
 
 function updateHaushaltsmitgliederTable(mitgliederRows, selectedPersonId) {
   const table = document.getElementById('haushaltsmitglieder-table');
+  if (!table) return;
+  
   const tbody = table.querySelector('tbody');
   
   // Tbody leeren
@@ -179,6 +188,8 @@ function updateHaushaltsmitgliederTable(mitgliederRows, selectedPersonId) {
 function updatePersonDetails(data) {
   // Titel aktualisieren
   const detailsTitle = document.getElementById('person-details-title');
+  if (!detailsTitle) return; // Nur für HHM-Einkommen verfügbar
+  
   detailsTitle.innerHTML = 'Details zu ' + (data.personLabel && data.personLabel !== '(keine Person)' ? data.personLabel : '');
   
   // Einkommen-Tabelle aktualisieren
@@ -207,6 +218,8 @@ function updatePersonDetails(data) {
 
 function updateTable(tableId, data, columns) {
   const table = document.getElementById(tableId);
+  if (!table || !data) return;
+  
   const tbody = table.querySelector('tbody');
   
   // Tbody leeren

+ 291 - 0
src/main/resources/static/js/ui-updater-miete.js

@@ -0,0 +1,291 @@
+// UI-Update-Funktionen für Miete
+
+function updateAfterVorgangChange(data) {
+  // Hidden fields aktualisieren
+  document.getElementById('leftVorgangId').value = data.newLeftVorgangId || data.leftVorgangId;
+  document.getElementById('leftZeitraumId').value = data.newLeftZeitraumId || data.leftZeitraumId;
+  
+  // Vorgang-Dropdown aktualisieren (falls erforderlich)
+  const vorgangSelect = document.getElementById('leftVorgangSelect');
+  if (vorgangSelect && (data.newLeftVorgangId || data.leftVorgangId)) {
+    const newVorgangId = data.newLeftVorgangId || data.leftVorgangId;
+    if (vorgangSelect.value !== newVorgangId) {
+      vorgangSelect.value = newVorgangId;
+    }
+  }
+  
+  // Zeitraum-Dropdown aktualisieren
+  const zeitraumSelect = document.getElementById('leftZeitraumSelect');
+  if (zeitraumSelect && (data.leftZeitraeumeOfSelected || data.leftZeitraeume)) {
+    zeitraumSelect.innerHTML = '';
+    const zeitraeume = data.leftZeitraeumeOfSelected || data.leftZeitraeume;
+    zeitraeume.forEach(zeitraum => {
+      const option = document.createElement('option');
+      option.value = zeitraum.id;
+      option.textContent = zeitraum.beginn + ' - ' + zeitraum.ende;
+      if (zeitraum.id === (data.newLeftZeitraumId || data.leftZeitraumId)) {
+        option.selected = true;
+      }
+      zeitraumSelect.appendChild(option);
+    });
+  }
+  
+  // Miete-spezifische Updates
+  updateMieteTables(data);
+}
+
+function updateAfterZeitraumChange(data) {
+  // Hidden field aktualisieren
+  document.getElementById('leftZeitraumId').value = data.newLeftZeitraumId || data.leftZeitraumId;
+  
+  // Zeitraum-Dropdown aktualisieren (falls erforderlich)
+  const zeitraumSelect = document.getElementById('leftZeitraumSelect');
+  if (zeitraumSelect) {
+    const newZeitraumId = data.newLeftZeitraumId || data.leftZeitraumId;
+    if (zeitraumSelect.value !== newZeitraumId) {
+      zeitraumSelect.value = newZeitraumId;
+    }
+  }
+  
+  // Miete-spezifische Updates
+  updateMieteTables(data);
+}
+
+function updateMieteTables(data) {
+  // Wohnung-Tabelle aktualisieren
+  if (data.wohnungZeile) {
+    updateWohnungTable(data.wohnungZeile);
+  }
+  
+  // Miete-Tabelle aktualisieren
+  if (data.mieteZeile) {
+    updateMieteTable(data.mieteZeile);
+  }
+  
+  // Untermieter-Tabelle aktualisieren
+  if (data.untermieterZeilen) {
+    updateUntermieterTable(data.untermieterZeilen);
+  }
+  
+  // Mitbewohner-Tabelle aktualisieren
+  if (data.mitbewohnerZeilen) {
+    updateMitbewohnerTable(data.mitbewohnerZeilen);
+  }
+}
+
+function updateWohnungTable(wohnungZeile) {
+  const wohnungContainer = document.querySelector('#wohnung-content .wohnung-grid-table');
+  if (!wohnungContainer) {
+    return;
+  }
+  
+  // Update der Tabellenzellen basierend auf der Template-Struktur
+  const rows = wohnungContainer.querySelectorAll('tr');
+  
+  if (rows.length >= 2) {
+    // Erste Zeile
+    const firstRowCells = rows[0].querySelectorAll('.value-cell');
+    if (firstRowCells.length >= 5) {
+      firstRowCells[0].innerHTML = wohnungZeile.mietverhaeltnisHtml || '';
+      firstRowCells[1].innerHTML = wohnungZeile.flaecheHtml || '';
+      firstRowCells[2].innerHTML = wohnungZeile.badHtml || '';
+      firstRowCells[3].innerHTML = wohnungZeile.nameEigHtml || '';
+      firstRowCells[4].innerHTML = wohnungZeile.telefonEigHtml || '';
+    }
+    
+    // Zweite Zeile
+    const secondRowCells = rows[1].querySelectorAll('.value-cell');
+    if (secondRowCells.length >= 4) {
+      secondRowCells[0].innerHTML = wohnungZeile.sozBauJnHtml || '';
+      secondRowCells[1].innerHTML = wohnungZeile.fertigDatHtml || '';
+      secondRowCells[2].innerHTML = wohnungZeile.heizungHtml || '';
+      secondRowCells[3].innerHTML = wohnungZeile.adresseEigHtml || '';
+    }
+  }
+  
+  // Container-Klasse aktualisieren
+  const container = wohnungContainer.closest('div');
+  if (container && wohnungZeile.rowClass) {
+    container.className = wohnungZeile.rowClass;
+  }
+}
+
+function updateMieteTable(mieteZeile) {
+  const mieteContainer = document.querySelector('#miete-content .miete-grid-table');
+  if (!mieteContainer) {
+    return;
+  }
+  
+  // Die Miete-Tabelle hat eine komplexe Struktur - wir aktualisieren nur die Werte
+  const tbody = mieteContainer.querySelector('tbody');
+  if (!tbody) {
+    return;
+  }
+  
+  const rows = tbody.querySelectorAll('tr');
+  
+  if (rows.length >= 9) {
+    // Erste Zeile: * Gesamtmiete, Heizung, Heizungspauschale
+    if (rows[0]) {
+      const row1Cells = rows[0].querySelectorAll('.value-cell, .pauschale-cell');
+      if (row1Cells.length >= 3) {
+        row1Cells[0].innerHTML = mieteZeile.gesamtmieteHtml || '';
+        row1Cells[1].innerHTML = mieteZeile.heizungHtml || '';
+        row1Cells[2].innerHTML = mieteZeile.heizungPauschaleHtml || '';
+      }
+    }
+    
+    // Zweite Zeile: Zuschläge, Warmwasser, Warmwasserpauschale
+    if (rows[1]) {
+      const row2Cells = rows[1].querySelectorAll('.value-cell, .pauschale-cell');
+      if (row2Cells.length >= 3) {
+        row2Cells[0].innerHTML = mieteZeile.zuschlägeHtml || '';
+        row2Cells[1].innerHTML = mieteZeile.warmwasserHtml || '';
+        row2Cells[2].innerHTML = mieteZeile.warmwasserPauschaleHtml || '';
+      }
+    }
+    
+    // Dritte Zeile: Art des Zuschlages, Kosten HH-Energie, Energiepauschale
+    if (rows[2]) {
+      const row3Cells = rows[2].querySelectorAll('.value-cell, .pauschale-cell');
+      if (row3Cells.length >= 3) {
+        row3Cells[0].innerHTML = mieteZeile.artZuschlagHtml || '';
+        row3Cells[1].innerHTML = mieteZeile.kostenHhEnergieHtml || '';
+        row3Cells[2].innerHTML = mieteZeile.energiePauschaleHtml || '';
+      }
+    }
+    
+    // Vierte Zeile: Garage/Stellplatz, Stellplatzpauschale
+    if (rows[3]) {
+      const row4Cells = rows[3].querySelectorAll('.value-cell, .pauschale-cell');
+      if (row4Cells.length >= 2) {
+        row4Cells[0].innerHTML = mieteZeile.stellplatzHtml || '';
+        row4Cells[1].innerHTML = mieteZeile.stellplatzPauschaleHtml || '';
+      }
+    }
+    
+    // Fünfte Zeile: Fernwärme, Möblierungszuschläge, Möblierungspauschale
+    if (rows[4]) {
+      const row5Cells = rows[4].querySelectorAll('.value-cell, .pauschale-cell');
+      if (row5Cells.length >= 3) {
+        row5Cells[0].innerHTML = mieteZeile.jhrlFernwaermeHtml || '';
+        row5Cells[1].innerHTML = mieteZeile.moeblierungszuschlagHtml || '';
+        row5Cells[2].innerHTML = mieteZeile.moeblierungPauschaleHtml || '';
+      }
+    }
+    
+    // Sechste Zeile: Fernwarmwasser, Gewerbezuschläge, Gewerbepauschale, Gewerbefläche
+    if (rows[5]) {
+      const row6Cells = rows[5].querySelectorAll('.value-cell, .pauschale-cell, .gewerbefläche-cell');
+      if (row6Cells.length >= 4) {
+        row6Cells[0].innerHTML = mieteZeile.jhrlFernwarmwasserHtml || '';
+        row6Cells[1].innerHTML = mieteZeile.gewerbezuschlagHtml || '';
+        row6Cells[2].innerHTML = mieteZeile.gewerbePauschaleHtml || '';
+        row6Cells[3].innerHTML = mieteZeile.gewerbeflaecheHtml || '';
+      }
+    }
+    
+    // Siebte Zeile: Fernwärme und -warmwasser, Untermietzuschläge
+    if (rows[6]) {
+      const row7Cells = rows[6].querySelectorAll('.value-cell');
+      if (row7Cells.length >= 2) {
+        row7Cells[0].innerHTML = mieteZeile.jhrlFernwUndWHtml || '';
+        row7Cells[1].innerHTML = mieteZeile.untermietzuschlagHtml || '';
+      }
+    }
+    
+    // Achte Zeile: sonstige Zuschläge, sonstige Pauschale
+    if (rows[7]) {
+      const row8Cells = rows[7].querySelectorAll('.value-cell, .pauschale-cell');
+      if (row8Cells.length >= 2) {
+        row8Cells[0].innerHTML = mieteZeile.sonstZuschlagHtml || '';
+        row8Cells[1].innerHTML = mieteZeile.sonstPauschaleHtml || '';
+      }
+    }
+    
+    // Neunte Zeile: Leistungen Dritter
+    if (rows[8]) {
+      const row9Cells = rows[8].querySelectorAll('.value-cell');
+      if (row9Cells.length >= 1) {
+        row9Cells[0].innerHTML = mieteZeile.leistungenDritterHtml || '';
+      }
+    }
+  }
+  
+  // Container-Klasse aktualisieren
+  const container = mieteContainer.closest('div');
+  if (container && mieteZeile.rowClass) {
+    container.className = mieteZeile.rowClass;
+  }
+}
+
+function updateUntermieterTable(untermieterZeilen) {
+  const table = document.getElementById('untermieter-table');
+  if (!table) return;
+  
+  const tbody = table.querySelector('tbody');
+  tbody.innerHTML = '';
+  
+  untermieterZeilen.forEach(zeile => {
+    const tr = document.createElement('tr');
+    tr.className = zeile.rowClass || '';
+    
+    // Symbol-Spalte
+    const symbolTd = document.createElement('td');
+    symbolTd.className = 'sym ' + (zeile.symbolClass || '');
+    symbolTd.textContent = zeile.symbol || '';
+    const ariaLabel = zeile.symbol === '+' ? 'Hinzugefügt' : 
+                     (zeile.symbol === '-' ? 'Gelöscht' : 
+                     (zeile.symbol === '!' ? 'Geändert' : 'Unverändert'));
+    symbolTd.setAttribute('aria-label', ariaLabel);
+    tr.appendChild(symbolTd);
+    
+    // Alle Spalten entsprechend der Template-Struktur
+    const columns = ['nameHtml', 'vornameHtml', 'mieteHtml', 'flaecheHtml', 'sonstHtml', 
+                    'heizungHtml', 'heizungPHtml', 'warmwasserHtml', 'warmwasserPHtml', 
+                    'moeblierungHtml', 'moeblierungPHtml'];
+    
+    columns.forEach(column => {
+      const td = document.createElement('td');
+      td.innerHTML = zeile[column] || '';
+      tr.appendChild(td);
+    });
+    
+    tbody.appendChild(tr);
+  });
+}
+
+function updateMitbewohnerTable(mitbewohnerZeilen) {
+  const table = document.getElementById('mitbewohner-table');
+  if (!table) return;
+  
+  const tbody = table.querySelector('tbody');
+  tbody.innerHTML = '';
+  
+  mitbewohnerZeilen.forEach(zeile => {
+    const tr = document.createElement('tr');
+    tr.className = zeile.rowClass || '';
+    
+    // Symbol-Spalte
+    const symbolTd = document.createElement('td');
+    symbolTd.className = 'sym ' + (zeile.symbolClass || '');
+    symbolTd.textContent = zeile.symbol || '';
+    const ariaLabel = zeile.symbol === '+' ? 'Hinzugefügt' : 
+                     (zeile.symbol === '-' ? 'Gelöscht' : 
+                     (zeile.symbol === '!' ? 'Geändert' : 'Unverändert'));
+    symbolTd.setAttribute('aria-label', ariaLabel);
+    tr.appendChild(symbolTd);
+    
+    // Alle Spalten entsprechend der Template-Struktur
+    const columns = ['nameHtml', 'vornameHtml', 'entgeltHtml', 'sonstHtml', 
+                    'heizungHtml', 'heizungPHtml', 'warmwasserHtml', 'warmwasserPHtml'];
+    
+    columns.forEach(column => {
+      const td = document.createElement('td');
+      td.innerHTML = zeile[column] || '';
+      tr.appendChild(td);
+    });
+    
+    tbody.appendChild(tr);
+  });
+}

+ 1 - 1
src/main/resources/templates/hhm-einkommen.html

@@ -11,7 +11,7 @@
     <!-- JavaScript Module -->
     <script src="/js/cookie-utils.js"></script>
     <script src="/js/ajax-handlers.js"></script>
-    <script src="/js/ui-updater.js"></script>
+    <script src="/js/ui-updater-hhm-einkommen.js"></script>
     <script src="/js/interactions.js"></script>
     <script src="/js/color-scheme.js"></script>
     <script src="/js/app.js"></script>

+ 1 - 1
src/main/resources/templates/miete.html

@@ -11,7 +11,7 @@
     <!-- JavaScript Module -->
     <script src="/js/cookie-utils.js"></script>
     <script src="/js/ajax-handlers.js"></script>
-    <script src="/js/ui-updater.js"></script>
+    <script src="/js/ui-updater-miete.js"></script>
     <script src="/js/interactions.js"></script>
     <script src="/js/color-scheme.js"></script>
     <script src="/js/app.js"></script>