Quellcode durchsuchen

Aktueller Stand mit verbesserten Schema und Daten

Leonard Schulz vor 1 Jahr
Ursprung
Commit
8d890d2349

+ 2 - 2
src/main/java/com/tsystems/inwo/vgl/view/EinkommenZeile.java

@@ -1,5 +1,5 @@
 package com.tsystems.inwo.vgl.view;
 
 public record EinkommenZeile(String symbol, String symbolClass, String rowClass,
-                             String artHtml, String frequenzHtml, String betragHtml,
-                             String zuschlagHtml, String abschlagHtml, String werbungskostenHtml, String berechnungsartHtml) {}
+                             String artHtml, String faktorHtml, String betragHtml,
+                             String zuschlagHtml, String abschlagHtml, String werbungskostenHtml) {}

+ 1 - 1
src/main/java/com/tsystems/inwo/vgl/view/FreibetragZeile.java

@@ -1,4 +1,4 @@
 package com.tsystems.inwo.vgl.view;
 
 public record FreibetragZeile(String symbol, String symbolClass, String rowClass,
-                              String typ, String artHtml, String frequenzHtml, String betragHtml, String fristHtml) {}
+                              String typ, String artHtml, String faktorHtml, String betragHtml, String fristHtml) {}

+ 29 - 3
src/main/java/com/tsystems/inwo/vgl/view/MitgliedRow.java

@@ -1,5 +1,31 @@
 package com.tsystems.inwo.vgl.view;
 
-public record MitgliedRow(String personId, String symbol, String symbolClass, String rowClass,
-                          String nameHtml, String geburtsdatumHtml, String familienstandHtml,
-                          String geschlechtHtml, String verwandtschaftHtml, String sozialstellungHtml, String istKindHtml) {}
+public record MitgliedRow(
+    String personId, 
+    String symbol, 
+    String symbolClass, 
+    String rowClass,
+    String nameHtml, 
+    String blocknameHtml,
+    String geburtsdatumHtml, 
+    String sterbedatumHtml,
+    String familienstandHtml,
+    String geschlechtHtml, 
+    String anredeHtml,
+    String verwandtschaftHtml, 
+    String ausschlussgrundHtml,
+    String sozialstellungHtml, 
+    String aufenthaltstitelEndeHtml,
+    // Neue Checkboxen für Tabelle
+    String antragstellerHtml,
+    String zahlungsempfaengerHtml,
+    // Zusatzfelder für aufklappbaren Bereich
+    String titelHtml,
+    String geburtsortHtml,
+    String telefonHtml,
+    String aktenzeichenSozialHtml,
+    String geburtsnameHtml,
+    String alterNameHtml,
+    String emailHtml,
+    String postabrutionsnummerHtml
+) {}

+ 135 - 54
src/main/java/com/tsystems/inwo/vgl/web/VergleichController.java

@@ -149,9 +149,9 @@ public class VergleichController {
 
     // ===== Helper-Typen =====
     public record Z(String id, String beginn, String ende) {}
-    public record Vorgang(String id, String aktenzeichen, List<Z> zeitraeume) {}
+    public record Vorgang(String id, String vorgangstyp, List<Z> zeitraeume) {}
     public record PersonRef(String id, String label) {}
-    public record StammvorgangInfo(String id, String aktenzeichen, String beginn, String ende, String bemerkung) {}
+    public record StammvorgangInfo(String id, String vorgangstyp, String vorgangsstatus) {}
     public record StammakteInfo(String aktenzeichen, String antragstellername, String antragstellerGeburtsdatum, String anschrift) {}
     public static final class Pair<R, L> {
         public final R right;
@@ -165,16 +165,14 @@ public class VergleichController {
     // ===== Helper-Methoden =====
     private static StammvorgangInfo createStammvorgangInfo(JsonNode stammvorgangNode) {
         if (stammvorgangNode == null || stammvorgangNode.isMissingNode()) {
-            return new StammvorgangInfo("", "", "", "", "");
+            return new StammvorgangInfo("", "", "");
         }
         
         String id = stammvorgangNode.path("id").asText("");
-        String aktenzeichen = stammvorgangNode.path("aktenzeichen").asText("");
-        String beginn = stammvorgangNode.path("beginn").asText("");
-        String ende = stammvorgangNode.path("ende").asText("");
-        String bemerkung = stammvorgangNode.path("bemerkung").asText("");
+        String vorgangstyp = stammvorgangNode.path("vorgangstyp").asText("");
+        String vorgangsstatus = stammvorgangNode.path("vorgangsstatus").asText("");
         
-        return new StammvorgangInfo(id, aktenzeichen, beginn, ende, bemerkung);
+        return new StammvorgangInfo(id, vorgangstyp, vorgangsstatus);
     }
 
     private static StammakteInfo extractStammakteInfo(JsonNode root) {
@@ -340,10 +338,10 @@ public class VergleichController {
                 for (JsonNode v : stammakte.get("vorgaenge")) {
                     JsonNode stammvorgang = v.path("stammvorgang");
                     String id = stammvorgang.path("id").asText("");
-                    String aktenzeichen = stammvorgang.path("aktenzeichen").asText("");
+                    String vorgangstyp = stammvorgang.path("vorgangstyp").asText("");
                     List<Z> zeitraeume = extractZeitraeume(JsonNodeFactory.instance.objectNode().set("vorgaenge", 
                         JsonNodeFactory.instance.arrayNode().add(v)));
-                    result.add(new Vorgang(id, aktenzeichen, zeitraeume));
+                    result.add(new Vorgang(id, vorgangstyp, zeitraeume));
                 }
             }
         }
@@ -352,18 +350,18 @@ public class VergleichController {
             for (JsonNode v : left.get("vorgaenge")) {
                 JsonNode stammvorgang = v.path("stammvorgang");
                 String id = stammvorgang.path("id").asText("");
-                String aktenzeichen = stammvorgang.path("aktenzeichen").asText("");
+                String vorgangstyp = stammvorgang.path("vorgangstyp").asText("");
                 List<Z> zeitraeume = extractZeitraeume(JsonNodeFactory.instance.objectNode().set("vorgaenge", 
                     JsonNodeFactory.instance.arrayNode().add(v)));
-                result.add(new Vorgang(id, aktenzeichen, zeitraeume));
+                result.add(new Vorgang(id, vorgangstyp, zeitraeume));
             }
         } else {
             // b) sonst: single vorgang
             JsonNode stammvorgang = left.path("stammvorgang");
             String id = stammvorgang.path("id").asText("");
-            String aktenzeichen = stammvorgang.path("aktenzeichen").asText("");
+            String vorgangstyp = stammvorgang.path("vorgangstyp").asText("");
             List<Z> zeitraeume = extractZeitraeume(left);
-            result.add(new Vorgang(id, aktenzeichen, zeitraeume));
+            result.add(new Vorgang(id, vorgangstyp, zeitraeume));
         }
         
         return result;
@@ -471,22 +469,38 @@ public class VergleichController {
         JsonNode primaryItem = rightItem != null ? rightItem : leftItem;
         
         if (primaryPerson == null) {
-            return new MitgliedRow(personId, symbol, symbolClass, rowClass, "", "", "", "", "", "", "");
+            return new MitgliedRow(
+                personId, symbol, symbolClass, rowClass, 
+                "", "", "", "", "", "", "", "", "", "", "",
+                "", "", "", "", "", "", "", "", "", ""
+            );
         }
         
-        // Extract data with change detection for HTML
+        // Extract data with change detection for HTML - Hauptfelder
         String nameHtml = buildPersonFieldHtml(
             rightPerson, leftPerson, 
             p -> p.path("nachname").asText("") + ", " + p.path("vorname").asText(""),
             hasChanges
         );
         
+        String blocknameHtml = buildPersonFieldHtml(
+            rightPerson, leftPerson,
+            p -> p.path("blockname").asBoolean(false) ? "☑" : "☐",
+            hasChanges
+        );
+        
         String geburtsdatumHtml = buildPersonFieldHtml(
             rightPerson, leftPerson,
             p -> !p.path("geburtsdatum").asText("").isEmpty() ? FormatUtil.date(p.path("geburtsdatum").asText("")) : "",
             hasChanges
         );
         
+        String sterbedatumHtml = buildPersonFieldHtml(
+            rightPerson, leftPerson,
+            p -> !p.path("sterbedatum").asText("").isEmpty() ? FormatUtil.date(p.path("sterbedatum").asText("")) : "",
+            hasChanges
+        );
+        
         String familienstandHtml = buildPersonFieldHtml(
             rightPerson, leftPerson,
             p -> p.path("familienstand").asText(""),
@@ -499,36 +513,106 @@ public class VergleichController {
             hasChanges
         );
         
+        String anredeHtml = buildPersonFieldHtml(
+            rightPerson, leftPerson,
+            p -> p.path("anrede").asText(""),
+            hasChanges
+        );
+        
+        // Item-level fields - angepasst an Schema
         String verwandtschaftHtml = buildItemFieldHtml(
             rightItem, leftItem,
-            i -> i.path("verwandtschaft").asText(""),
+            i -> i.path("verwandtschaftsverhaeltnis").asText(""),
             hasChanges
         );
         
-        String sozialstellungHtml = buildItemFieldHtml(
-            rightItem, leftItem,
-            i -> i.path("sozialstellung").asText(""),
+        String ausschlussgrundHtml = buildPersonFieldHtml(
+            rightPerson, leftPerson,
+            p -> p.path("ausschlussgrund").asText(""),
+            hasChanges
+        );
+        
+        String sozialstellungHtml = buildPersonFieldHtml(
+            rightPerson, leftPerson,
+            p -> p.path("sozialstellung").asText(""),
+            hasChanges
+        );
+        
+        String aufenthaltstitelEndeHtml = buildPersonFieldHtml(
+            rightPerson, leftPerson,
+            p -> !p.path("aufenthaltstitelEnde").asText("").isEmpty() ? FormatUtil.date(p.path("aufenthaltstitelEnde").asText("")) : "",
             hasChanges
         );
         
-        String istKindHtml = buildItemFieldHtml(
+        String antragstellerHtml = buildItemFieldHtml(
             rightItem, leftItem,
-            i -> i.path("istKind").asBoolean(false) ? "Ja" : "Nein",
+            i -> i.path("rolleImHaushalt").asText("").equals("Antragsteller") ? "☑" : "☐",
+            hasChanges
+        );
+        
+        String zahlungsempfaengerHtml = buildPersonFieldHtml(
+            rightPerson, leftPerson,
+            p -> p.path("zahlungsempfaenger").asBoolean(false) ? "☑" : "☐",
+            hasChanges
+        );
+        
+        // Zusatzfelder
+        String titelHtml = buildPersonFieldHtml(
+            rightPerson, leftPerson,
+            p -> p.path("titel").asText(""),
+            hasChanges
+        );
+        
+        String geburtsortHtml = buildPersonFieldHtml(
+            rightPerson, leftPerson,
+            p -> p.path("geburtsort").asText(""),
+            hasChanges
+        );
+        
+        String telefonHtml = buildPersonFieldHtml(
+            rightPerson, leftPerson,
+            p -> p.path("telefon").asText(""),
+            hasChanges
+        );
+        
+        String aktenzeichenSozialHtml = buildPersonFieldHtml(
+            rightPerson, leftPerson,
+            p -> p.path("aktenzeichenSozial").asText(""),
+            hasChanges
+        );
+        
+        String geburtsnameHtml = buildPersonFieldHtml(
+            rightPerson, leftPerson,
+            p -> p.path("geburtsname").asText(""),
+            hasChanges
+        );
+        
+        String alterNameHtml = buildPersonFieldHtml(
+            rightPerson, leftPerson,
+            p -> p.path("alterName").asText(""),
+            hasChanges
+        );
+        
+        String emailHtml = buildPersonFieldHtml(
+            rightPerson, leftPerson,
+            p -> p.path("email").asText(""),
+            hasChanges
+        );
+        
+        String postabrutionsnummerHtml = buildPersonFieldHtml(
+            rightPerson, leftPerson,
+            p -> p.path("postabrutionsnummer").asText(""),
             hasChanges
         );
         
         return new MitgliedRow(
-            personId,
-            symbol,
-            symbolClass,
-            rowClass,
-            nameHtml,
-            geburtsdatumHtml,
-            familienstandHtml,
-            geschlechtHtml,
-            verwandtschaftHtml,
-            sozialstellungHtml,
-            istKindHtml
+            personId, symbol, symbolClass, rowClass,
+            nameHtml, blocknameHtml, geburtsdatumHtml, sterbedatumHtml,
+            familienstandHtml, geschlechtHtml, anredeHtml,
+            verwandtschaftHtml, ausschlussgrundHtml, sozialstellungHtml, aufenthaltstitelEndeHtml,
+            antragstellerHtml, zahlungsempfaengerHtml,
+            titelHtml, geburtsortHtml, telefonHtml, aktenzeichenSozialHtml,
+            geburtsnameHtml, alterNameHtml, emailHtml, postabrutionsnummerHtml
         );
     }
     
@@ -541,9 +625,10 @@ public class VergleichController {
                !Objects.equals(rightPerson.path("geburtsdatum").asText(""), leftPerson.path("geburtsdatum").asText("")) ||
                !Objects.equals(rightPerson.path("familienstand").asText(""), leftPerson.path("familienstand").asText("")) ||
                !Objects.equals(rightPerson.path("geschlecht").asText(""), leftPerson.path("geschlecht").asText("")) ||
-               !Objects.equals(rightItem.path("verwandtschaft").asText(""), leftItem.path("verwandtschaft").asText("")) ||
-               !Objects.equals(rightItem.path("sozialstellung").asText(""), leftItem.path("sozialstellung").asText("")) ||
-               rightItem.path("istKind").asBoolean(false) != leftItem.path("istKind").asBoolean(false);
+               !Objects.equals(rightItem.path("verwandtschaftsverhaeltnis").asText(""), leftItem.path("verwandtschaftsverhaeltnis").asText("")) ||
+               !Objects.equals(rightPerson.path("sozialstellung").asText(""), leftPerson.path("sozialstellung").asText("")) ||
+               !Objects.equals(rightItem.path("rolleImHaushalt").asText(""), leftItem.path("rolleImHaushalt").asText("")) ||
+               rightPerson.path("zahlungsempfaenger").asBoolean(false) != leftPerson.path("zahlungsempfaenger").asBoolean(false);
     }
     
     private static String buildPersonFieldHtml(JsonNode rightPerson, JsonNode leftPerson, Function<JsonNode, String> extractor, boolean hasChanges) {
@@ -630,12 +715,12 @@ public class VergleichController {
                         return !artZusatz.isEmpty() ? art + " (" + artZusatz + ")" : art;
                     }, hasChanges);
                 
-                String frequenzHtml = buildEinkommenFieldHtml(rightItem, leftItem,
+                String faktorHtml = buildEinkommenFieldHtml(rightItem, leftItem,
                     i -> {
-                        int frequenz = i.path("frequenz").asInt(1);
-                        return frequenz == 1 ? "monatlich" : 
-                               frequenz == 12 ? "jährlich" : 
-                               frequenz + "x/Jahr";
+                        double faktor = i.path("faktor").asDouble(12);
+                        return faktor == 12 ? "monatlich" : 
+                               faktor == 1 ? "jährlich" : 
+                               String.format("%.0fx/Jahr", faktor);
                     }, hasChanges);
                 
                 String betragHtml = buildEinkommenFieldHtml(rightItem, leftItem,
@@ -659,13 +744,10 @@ public class VergleichController {
                         return werbungskosten > 0 ? String.format("%.2f €", werbungskosten) : "";
                     }, hasChanges);
                 
-                String berechnungsartHtml = buildEinkommenFieldHtml(rightItem, leftItem,
-                    i -> i.path("berechnungsart").asText(""), hasChanges);
-                
                 result.add(new EinkommenZeile(
                     symbol, symbolClass, rowClass,
-                    artHtml, frequenzHtml, betragHtml,
-                    zuschlagHtml, abschlagHtml, werbungskostenHtml, berechnungsartHtml
+                    artHtml, faktorHtml, betragHtml,
+                    zuschlagHtml, abschlagHtml, werbungskostenHtml
                 ));
             }
         }
@@ -676,12 +758,11 @@ public class VergleichController {
     private static boolean detectEinkommenChanges(JsonNode rightItem, JsonNode leftItem) {
         return !Objects.equals(rightItem.path("art").asText(""), leftItem.path("art").asText("")) ||
                !Objects.equals(rightItem.path("artZusatz").asText(""), leftItem.path("artZusatz").asText("")) ||
-               rightItem.path("frequenz").asInt(1) != leftItem.path("frequenz").asInt(1) ||
+               rightItem.path("faktor").asDouble(12) != leftItem.path("faktor").asDouble(12) ||
                rightItem.path("betrag").asDouble(0.0) != leftItem.path("betrag").asDouble(0.0) ||
                rightItem.path("zuschlag").asDouble(0.0) != leftItem.path("zuschlag").asDouble(0.0) ||
                rightItem.path("abschlag").asDouble(0.0) != leftItem.path("abschlag").asDouble(0.0) ||
-               rightItem.path("werbungskosten").asDouble(0.0) != leftItem.path("werbungskosten").asDouble(0.0) ||
-               !Objects.equals(rightItem.path("berechnungsart").asText(""), leftItem.path("berechnungsart").asText(""));
+               rightItem.path("werbungskosten").asDouble(0.0) != leftItem.path("werbungskosten").asDouble(0.0);
     }
     
     private static String buildEinkommenFieldHtml(JsonNode rightItem, JsonNode leftItem, Function<JsonNode, String> extractor, boolean hasChanges) {
@@ -750,10 +831,10 @@ public class VergleichController {
                 
                 String frequenzHtml = buildFreibetragFieldHtml(rightItem, leftItem,
                     i -> {
-                        int frequenz = i.path("frequenz").asInt(1);
-                        return frequenz == 1 ? "monatlich" : 
-                               frequenz == 12 ? "jährlich" : 
-                               frequenz + "x/Jahr";
+                        double faktor = i.path("faktor").asDouble(12);
+                        return faktor == 12 ? "monatlich" : 
+                               faktor == 1 ? "jährlich" : 
+                               String.format("%.0fx/Jahr", faktor);
                     }, hasChanges);
                 
                 String betragHtml = buildFreibetragFieldHtml(rightItem, leftItem,
@@ -779,7 +860,7 @@ public class VergleichController {
     
     private static boolean detectFreibetragChanges(JsonNode rightItem, JsonNode leftItem) {
         return !Objects.equals(rightItem.path("art").asText(""), leftItem.path("art").asText("")) ||
-               rightItem.path("frequenz").asInt(1) != leftItem.path("frequenz").asInt(1) ||
+               rightItem.path("faktor").asDouble(12) != leftItem.path("faktor").asDouble(12) ||
                rightItem.path("betrag").asDouble(0.0) != leftItem.path("betrag").asDouble(0.0) ||
                !Objects.equals(rightItem.path("fristDatum").asText(""), leftItem.path("fristDatum").asText(""));
     }

+ 51 - 22
src/main/resources/schemas/haushalt-einkommen.schema.json

@@ -92,19 +92,13 @@
         "id": {
           "type": "string"
         },
-        "aktenzeichen": {
-          "type": "string"
-        },
-        "beginn": {
+        "vorgangstyp": {
           "type": "string",
-          "format": "date"
+          "description": "Typ des Vorgangs (Ersatz für bemerkung)"
         },
-        "ende": {
+        "vorgangsstatus": {
           "type": "string",
-          "format": "date"
-        },
-        "bemerkung": {
-          "type": "string"
+          "description": "Status des Vorgangs"
         }
       },
       "required": ["id"]
@@ -148,10 +142,31 @@
         "geburtsname": {
           "type": "string"
         },
+        "alterName": {
+          "type": "string",
+          "description": "Alter Name der Person"
+        },
+        "titel": {
+          "type": "string",
+          "description": "Akademische Titel oder Anreden"
+        },
+        "anrede": {
+          "type": "string",
+          "description": "Anrede (Herr, Frau, etc.)"
+        },
         "geburtsdatum": {
           "type": "string",
           "format": "date"
         },
+        "sterbedatum": {
+          "type": "string",
+          "format": "date",
+          "description": "Sterbedatum falls verstorben"
+        },
+        "geburtsort": {
+          "type": "string",
+          "description": "Geburtsort der Person"
+        },
         "geschlecht": {
           "type": "string"
         },
@@ -176,6 +191,18 @@
           "type": "string",
           "description": "Beziehung zum/ zur Antragstellenden in diesem Zeitraum"
         },
+        "ausschlussgrund": {
+          "type": "string",
+          "description": "Grund für Ausschluss aus der Bedarfsgemeinschaft"
+        },
+        "blockname": {
+          "type": "boolean",
+          "description": "Checkbox für Blockname"
+        },
+        "aktenzeichenSozial": {
+          "type": "string",
+          "description": "Aktenzeichen des Sozialamtes"
+        },
         "transferleistungJN": {
           "type": "string",
           "maxLength": 1,
@@ -197,6 +224,14 @@
         "istKind": {
           "type": "boolean",
           "description": "true, falls Kind im Sinne der Berechnung"
+        },
+        "zahlungsempfaenger": {
+          "type": "boolean",
+          "description": "Checkbox für Zahlungsempfänger"
+        },
+        "postabrutionsnummer": {
+          "type": "string",
+          "description": "Postabrutionsnummer der Person"
         }
       },
       "required": [
@@ -219,9 +254,9 @@
         "artZusatz": {
           "type": "string"
         },
-        "frequenz": {
-          "type": "integer",
-          "description": "z.B. 1=monatlich, 12=jährlich o. ä."
+        "faktor": {
+          "type": "number",
+          "description": "Faktor: 1 für jährlich, 12 für monatlich, etc."
         },
         "betrag": {
           "type": "number",
@@ -231,10 +266,6 @@
           "type": "number",
           "minimum": 0
         },
-        "berechnungsart": {
-          "type": "string",
-          "description": "intern/extern/pauschal etc."
-        },
         "verglRechnungJN": {
           "type": "string",
           "maxLength": 1
@@ -270,8 +301,9 @@
           "type": "string",
           "description": "z.B. Erwerbstätigenfreibetrag, Alleinerziehendenfreibetrag"
         },
-        "frequenz": {
-          "type": "integer"
+        "faktor": {
+          "type": "number",
+          "description": "Faktor: 1 für jährlich, 12 für monatlich, etc."
         },
         "betrag": {
           "type": "number",
@@ -364,9 +396,6 @@
           "type": "string",
           "format": "date"
         },
-        "grund": {
-          "type": "string"
-        },
         "personen": {
           "type": "array",
           "items": {

+ 65 - 72
src/main/resources/static/example-alt.json

@@ -1,111 +1,104 @@
 {
   "stammakte": {
-    "aktenzeichen": "SA-2026-001",
+    "aktenzeichen": "01/01/119979",
     "antragstellername": "Müller, Tobias",
-    "antragstellerGeburtsdatum": "1990-01-04",
+    "antragstellerGeburtsdatum": "1990-05-15",
     "anschrift": {
-      "strasse": "Musterstraße",
-      "hausnummer": "123",
-      "plz": "12345",
-      "ort": "Musterstadt"
+      "strasse": "Unter den Linden",
+      "hausnummer": "1",
+      "plz": "10117",
+      "ort": "Berlin"
     },
     "vorgaenge": [
       {
         "stammvorgang": {
-          "id": "SV-001",
-          "aktenzeichen": "01/01/119979",
-          "beginn": "2026-01-01",
-          "ende": "2027-06-30"
+          "id": "stammvorgang-001",
+          "vorgangstyp": "Erstantrag",
+          "vorgangsstatus": "abgeschlossen"
         },
         "zeitraeume": [
           {
-            "id": "Z1",
-            "beginn": "2026-01-01",
-            "ende": "2026-06-30",
+            "id": "zeitraum-001",
+            "beginn": "2024-01-01",
+            "ende": "2024-12-31",
             "personen": [
               {
                 "rolleImHaushalt": "Antragsteller",
                 "person": {
-                  "id": "P1",
+                  "id": "person-001",
                   "nachname": "Müller",
                   "vorname": "Tobias",
-                  "geburtsdatum": "1990-01-04",
-                  "geschlecht": "Männlich",
+                  "geburtsdatum": "1990-05-15",
+                  "geschlecht": "männlich",
                   "familienstand": "verheiratet",
-                  "verwandtschaftsverhaeltnis": "Ehe-/Lebenspartner",
-                  "sozialstellung": "Angestellter"
+                  "zahlungsempfaenger": true
                 },
                 "einkommen": [
-                  { "id": "E1", "art": "NS - Eink. aus nichtselbst. Tätigkeit", "frequenz": 12, "betrag": 800000, "zuschlag": 0, "abschlag": 0, "werbungskosten": 0 },
-                  { "id": "E3", "art": "Nebenjob (Minijob)", "frequenz": 12, "betrag": 100000 }
+                  {
+                    "id": "einkommen-001",
+                    "art": "Erwerbstätigkeit",
+                    "artZusatz": "Vollzeit",
+                    "faktor": 12,
+                    "betrag": 2800.00,
+                    "werbungskosten": 100.00
+                  }
                 ],
                 "freibetraege": [
-                  { "id": "P1", "typ": "P", "art": "Krankenkasse", "frequenz": 12, "betrag": 80000 },
-                  { "id": "P2", "typ": "P", "art": "Steuern", "frequenz": 12, "betrag": 60000 },
-                  { "id": "P3", "typ": "P", "art": "Rentenversicherung", "frequenz": 12, "betrag": 45000 }
+                  {
+                    "id": "freibetrag-001",
+                    "typ": "F",
+                    "art": "Erwerbstätigenfreibetrag",
+                    "faktor": 12,
+                    "betrag": 300.00
+                  },
+                  {
+                    "id": "pauschale-001",
+                    "typ": "P",
+                    "art": "Versicherungspauschale",
+                    "faktor": 12,
+                    "betrag": 30.00
+                  }
                 ]
               },
               {
-                "rolleImHaushalt": "Partnerin",
+                "rolleImHaushalt": "Partner",
                 "person": {
-                  "id": "P2",
+                  "id": "person-002",
                   "nachname": "Müller",
-                  "vorname": "Annemarie",
-                  "geburtsdatum": "1991-08-08",
-                  "geschlecht": "Weiblich",
-                  "familienstand": "verheiratet",
-                  "verwandtschaftsverhaeltnis": "Ehe-/Lebenspartner",
-                  "sozialstellung": "Angestellte"
+                  "vorname": "Anna",
+                  "geburtsdatum": "1992-08-20",
+                  "geschlecht": "weiblich",
+                  "familienstand": "verheiratet"
                 },
                 "einkommen": [
-                  { "id": "E2", "art": "NS - Eink. aus nichtselbst. Tätigkeit", "frequenz": 12, "betrag": 600000 }
-                ],
-                "freibetraege": [
-                  { "id": "F1", "typ": "F", "art": "Erwerbstätigenfreibetrag", "frequenz": 12, "betrag": 10000 },
-                  { "id": "P4", "typ": "P", "art": "Krankenkasse", "frequenz": 12, "betrag": 60000 },
-                  { "id": "P5", "typ": "P", "art": "Steuern", "frequenz": 12, "betrag": 45000 },
-                  { "id": "P6", "typ": "P", "art": "Rentenversicherung", "frequenz": 12, "betrag": 36000 }
+                  {
+                    "id": "einkommen-002",
+                    "art": "Minijob",
+                    "faktor": 12,
+                    "betrag": 520.00
+                  }
                 ]
-              }
-            ]
-          },
-          {
-            "id": "Z2",
-            "beginn": "2026-07-01",
-            "ende": "2026-12-31",
-            "personen": [
+              },
               {
-                "rolleImHaushalt": "Antragsteller",
-                "person": { "id": "P1", "nachname": "Müller", "vorname": "Tobias" },
-                "einkommen": [
-                  { "id": "E1", "art": "NS - Eink. aus nichtselbst. Tätigkeit", "frequenz": 12, "betrag": 810000 }
+                "rolleImHaushalt": "Kind",
+                "person": {
+                  "id": "person-003",
+                  "nachname": "Müller",
+                  "vorname": "Max",
+                  "geburtsdatum": "2018-03-10",
+                  "geschlecht": "männlich"
+                },
+                "kinderbetreuung": [
+                  {
+                    "id": "kita-001",
+                    "kindRef": "person-003",
+                    "betrag": 150.00
+                  }
                 ]
               }
             ]
           }
         ]
-      },
-      {
-        "stammvorgang": {
-          "id": "SV-002",
-          "aktenzeichen": "02/02/202000",
-          "beginn": "2025-01-01",
-          "ende": "2025-12-31",
-          "bemerkung": "Altfall"
-        },
-        "zeitraeume": [
-          {
-            "id": "Z9",
-            "beginn": "2025-01-01",
-            "ende": "2025-06-30",
-            "personen": [
-              {
-                "rolleImHaushalt": "Antragsteller",
-                "person": { "id": "P9", "nachname": "Beispiel", "vorname": "Alice" }
-              }
-            ]
-          }
-        ]
       }
     ]
   }

+ 351 - 43
src/main/resources/static/example-neu.json

@@ -1,86 +1,394 @@
 {
   "stammakte": {
-    "aktenzeichen": "SA-2026-001",
+    "aktenzeichen": "01/01/119979",
     "antragstellername": "Müller, Tobias",
-    "antragstellerGeburtsdatum": "1990-01-04",
+    "antragstellerGeburtsdatum": "1990-05-15",
     "anschrift": {
-      "strasse": "Musterstraße",
-      "hausnummer": "123",
-      "zusatz": "Wohnung 2A",
-      "plz": "12345",
-      "ort": "Musterstadt"
+      "strasse": "Unter den Linden",
+      "hausnummer": "1",
+      "plz": "10117",
+      "ort": "Berlin"
     },
     "vorgaenge": [
       {
         "stammvorgang": {
-          "id": "SV-001",
-          "aktenzeichen": "01/01/119979",
-          "beginn": "2026-01-01",
-          "ende": "2027-06-30",
-          "bemerkung": "Weiterleistungsantrag"
+          "id": "stammvorgang-001",
+          "vorgangstyp": "Erstantrag",
+          "vorgangsstatus": "abgeschlossen"
         },
         "zeitraeume": [
           {
-            "id": "Z1",
-            "beginn": "2026-01-01",
-            "ende": "2026-06-30",
+            "id": "zeitraum-001",
+            "beginn": "2024-01-01",
+            "ende": "2024-12-31",
             "personen": [
               {
                 "rolleImHaushalt": "Antragsteller",
                 "person": {
-                  "id": "P1",
+                  "id": "person-001",
                   "nachname": "Müller",
                   "vorname": "Tobias",
-                  "geburtsdatum": "1990-01-04",
-                  "geschlecht": "Männlich",
+                  "geburtsdatum": "1990-05-15",
+                  "geschlecht": "männlich",
                   "familienstand": "verheiratet",
-                  "verwandtschaftsverhaeltnis": "Ehe-/Lebenspartner",
-                  "sozialstellung": "Angestellter"
+                  "zahlungsempfaenger": true
                 },
                 "einkommen": [
-                  { "id": "E1", "art": "NS - Eink. aus nichtselbst. Tätigkeit", "frequenz": 12, "betrag": 1000000, "werbungskosten": 10000 }
+                  {
+                    "id": "einkommen-001",
+                    "art": "Erwerbstätigkeit",
+                    "artZusatz": "Vollzeit",
+                    "faktor": 12,
+                    "betrag": 2800.00,
+                    "werbungskosten": 100.00
+                  }
                 ],
                 "freibetraege": [
-                  { "id": "P1", "typ": "P", "art": "Krankenkasse", "frequenz": 12, "betrag": 100000 },
-                  { "id": "P2", "typ": "P", "art": "Steuern", "frequenz": 12, "betrag": 75000 },
-                  { "id": "P3", "typ": "P", "art": "Rentenversicherung", "frequenz": 12, "betrag": 55000 }
+                  {
+                    "id": "freibetrag-001",
+                    "typ": "F",
+                    "art": "Erwerbstätigenfreibetrag",
+                    "faktor": 12,
+                    "betrag": 300.00
+                  },
+                  {
+                    "id": "pauschale-001",
+                    "typ": "P",
+                    "art": "Versicherungspauschale",
+                    "faktor": 12,
+                    "betrag": 30.00
+                  }
+                ]
+              },
+              {
+                "rolleImHaushalt": "Partner",
+                "person": {
+                  "id": "person-002",
+                  "nachname": "Müller",
+                  "vorname": "Anna",
+                  "geburtsdatum": "1992-08-20",
+                  "geschlecht": "weiblich",
+                  "familienstand": "verheiratet"
+                },
+                "einkommen": [
+                  {
+                    "id": "einkommen-002",
+                    "art": "Minijob",
+                    "faktor": 12,
+                    "betrag": 520.00
+                  }
                 ]
               },
               {
-                "rolleImHaushalt": "Partnerin",
+                "rolleImHaushalt": "Kind",
                 "person": {
-                  "id": "P2",
+                  "id": "person-003",
                   "nachname": "Müller",
-                  "vorname": "Annemarie",
-                  "geburtsdatum": "1991-08-08",
-                  "geschlecht": "Weiblich",
+                  "vorname": "Max",
+                  "geburtsdatum": "2018-03-10",
+                  "geschlecht": "männlich"
+                },
+                "kinderbetreuung": [
+                  {
+                    "id": "kita-001",
+                    "kindRef": "person-003",
+                    "betrag": 150.00
+                  }
+                ]
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "stammvorgang": {
+          "id": "stammvorgang-002",
+          "vorgangstyp": "Weiterleistungsantrag",
+          "vorgangsstatus": "in Bearbeitung"
+        },
+        "zeitraeume": [
+          {
+            "id": "zeitraum-002",
+            "beginn": "2025-01-01",
+            "ende": "2025-06-30",
+            "personen": [
+              {
+                "rolleImHaushalt": "Antragsteller",
+                "person": {
+                  "id": "person-001",
+                  "nachname": "Müller",
+                  "vorname": "Tobias",
+                  "geburtsdatum": "1990-05-15",
+                  "geschlecht": "männlich",
                   "familienstand": "verheiratet",
-                  "verwandtschaftsverhaeltnis": "Ehe-/Lebenspartner",
-                  "sozialstellung": "Angestellte"
+                  "zahlungsempfaenger": true
                 },
                 "einkommen": [
-                  { "id": "E2", "art": "NS - Eink. aus nichtselbst. Tätigkeit", "frequenz": 12, "betrag": 620000, "werbungskosten": 10000 }
+                  {
+                    "id": "einkommen-003",
+                    "art": "Erwerbstätigkeit",
+                    "artZusatz": "Vollzeit",
+                    "faktor": 12,
+                    "betrag": 2950.00,
+                    "werbungskosten": 120.00,
+                    "zuschlag": 100.00
+                  }
                 ],
                 "freibetraege": [
-                  { "id": "F1", "typ": "F", "art": "Erwerbstätigenfreibetrag", "frequenz": 12, "betrag": 12000 },
-                  { "id": "P4", "typ": "P", "art": "Krankenkasse", "frequenz": 12, "betrag": 62000 },
-                  { "id": "P5", "typ": "P", "art": "Steuern", "frequenz": 12, "betrag": 47000 },
-                  { "id": "P6", "typ": "P", "art": "Rentenversicherung", "frequenz": 12, "betrag": 37000 }
+                  {
+                    "id": "freibetrag-002",
+                    "typ": "F",
+                    "art": "Erwerbstätigenfreibetrag",
+                    "faktor": 12,
+                    "betrag": 320.00
+                  },
+                  {
+                    "id": "pauschale-002",
+                    "typ": "P",
+                    "art": "Versicherungspauschale",
+                    "faktor": 12,
+                    "betrag": 35.00
+                  }
+                ]
+              },
+              {
+                "rolleImHaushalt": "Partner",
+                "person": {
+                  "id": "person-002",
+                  "nachname": "Müller",
+                  "vorname": "Anna",
+                  "geburtsdatum": "1992-08-20",
+                  "geschlecht": "weiblich",
+                  "familienstand": "verheiratet"
+                },
+                "einkommen": [
+                  {
+                    "id": "einkommen-004",
+                    "art": "Teilzeit",
+                    "faktor": 12,
+                    "betrag": 800.00
+                  }
+                ]
+              },
+              {
+                "rolleImHaushalt": "Kind",
+                "person": {
+                  "id": "person-003",
+                  "nachname": "Müller",
+                  "vorname": "Max",
+                  "geburtsdatum": "2018-03-10",
+                  "geschlecht": "männlich"
+                },
+                "kinderbetreuung": [
+                  {
+                    "id": "kita-002",
+                    "kindRef": "person-003",
+                    "betrag": 180.00
+                  }
+                ]
+              },
+              {
+                "rolleImHaushalt": "Kind",
+                "person": {
+                  "id": "person-004",
+                  "nachname": "Müller",
+                  "vorname": "Lisa",
+                  "geburtsdatum": "2021-11-05",
+                  "geschlecht": "weiblich"
+                }
+              }
+            ]
+          },
+          {
+            "id": "zeitraum-003",
+            "beginn": "2025-07-01",
+            "ende": "2025-12-31",
+            "personen": [
+              {
+                "rolleImHaushalt": "Antragsteller",
+                "person": {
+                  "id": "person-001",
+                  "nachname": "Müller",
+                  "vorname": "Tobias",
+                  "geburtsdatum": "1990-05-15",
+                  "geschlecht": "männlich",
+                  "familienstand": "verheiratet",
+                  "zahlungsempfaenger": true
+                },
+                "einkommen": [
+                  {
+                    "id": "einkommen-005",
+                    "art": "Erwerbstätigkeit",
+                    "artZusatz": "Vollzeit",
+                    "faktor": 12,
+                    "betrag": 3100.00,
+                    "werbungskosten": 120.00,
+                    "zuschlag": 150.00
+                  }
                 ],
+                "freibetraege": [
+                  {
+                    "id": "freibetrag-003",
+                    "typ": "F",
+                    "art": "Erwerbstätigenfreibetrag",
+                    "faktor": 12,
+                    "betrag": 350.00
+                  },
+                  {
+                    "id": "pauschale-003",
+                    "typ": "P",
+                    "art": "Versicherungspauschale",
+                    "faktor": 12,
+                    "betrag": 40.00
+                  }
+                ]
+              },
+              {
+                "rolleImHaushalt": "Partner",
+                "person": {
+                  "id": "person-002",
+                  "nachname": "Müller",
+                  "vorname": "Anna",
+                  "geburtsdatum": "1992-08-20",
+                  "geschlecht": "weiblich",
+                  "familienstand": "verheiratet"
+                },
+                "einkommen": [
+                  {
+                    "id": "einkommen-006",
+                    "art": "Teilzeit",
+                    "faktor": 12,
+                    "betrag": 1200.00
+                  }
+                ]
+              },
+              {
+                "rolleImHaushalt": "Kind",
+                "person": {
+                  "id": "person-003",
+                  "nachname": "Müller",
+                  "vorname": "Max",
+                  "geburtsdatum": "2018-03-10",
+                  "geschlecht": "männlich"
+                },
                 "kinderbetreuung": [
-                  { "id": "K1", "kindRef": "P3", "betrag": 10000 }
+                  {
+                    "id": "kita-003",
+                    "kindRef": "person-003",
+                    "betrag": 200.00
+                  }
+                ]
+              },
+              {
+                "rolleImHaushalt": "Kind",
+                "person": {
+                  "id": "person-004",
+                  "nachname": "Müller",
+                  "vorname": "Lisa",
+                  "geburtsdatum": "2021-11-05",
+                  "geschlecht": "weiblich"
+                },
+                "kinderbetreuung": [
+                  {
+                    "id": "kita-004",
+                    "kindRef": "person-004",
+                    "betrag": 120.00
+                  }
+                ]
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "stammvorgang": {
+          "id": "stammvorgang-003",
+          "vorgangstyp": "Änderungsantrag",
+          "vorgangsstatus": "eingereicht"
+        },
+        "zeitraeume": [
+          {
+            "id": "zeitraum-004",
+            "beginn": "2026-01-01",
+            "ende": "2026-12-31",
+            "personen": [
+              {
+                "rolleImHaushalt": "Antragsteller",
+                "person": {
+                  "id": "person-001",
+                  "nachname": "Müller",
+                  "vorname": "Tobias",
+                  "geburtsdatum": "1990-05-15",
+                  "geschlecht": "männlich",
+                  "familienstand": "verheiratet",
+                  "zahlungsempfaenger": true
+                },
+                "einkommen": [
+                  {
+                    "id": "einkommen-007",
+                    "art": "Erwerbstätigkeit",
+                    "artZusatz": "Vollzeit",
+                    "faktor": 12,
+                    "betrag": 3200.00,
+                    "werbungskosten": 130.00
+                  }
+                ],
+                "freibetraege": [
+                  {
+                    "id": "freibetrag-004",
+                    "typ": "F",
+                    "art": "Erwerbstätigenfreibetrag",
+                    "faktor": 12,
+                    "betrag": 360.00
+                  }
                 ]
               },
+              {
+                "rolleImHaushalt": "Partner",
+                "person": {
+                  "id": "person-002",
+                  "nachname": "Müller",
+                  "vorname": "Anna",
+                  "geburtsdatum": "1992-08-20",
+                  "geschlecht": "weiblich",
+                  "familienstand": "verheiratet"
+                },
+                "einkommen": [
+                  {
+                    "id": "einkommen-008",
+                    "art": "Vollzeit",
+                    "faktor": 12,
+                    "betrag": 2400.00
+                  }
+                ],
+                "freibetraege": [
+                  {
+                    "id": "freibetrag-005",
+                    "typ": "F",
+                    "art": "Erwerbstätigenfreibetrag",
+                    "faktor": 12,
+                    "betrag": 240.00
+                  }
+                ]
+              },
+              {
+                "rolleImHaushalt": "Kind",
+                "person": {
+                  "id": "person-003",
+                  "nachname": "Müller",
+                  "vorname": "Max",
+                  "geburtsdatum": "2018-03-10",
+                  "geschlecht": "männlich"
+                }
+              },
               {
                 "rolleImHaushalt": "Kind",
                 "person": {
-                  "id": "P3",
+                  "id": "person-004",
                   "nachname": "Müller",
-                  "vorname": "Sofie",
-                  "geburtsdatum": "2025-01-16",
-                  "geschlecht": "Weiblich",
-                  "familienstand": "ledig",
-                  "istKind": true
+                  "vorname": "Lisa",
+                  "geburtsdatum": "2021-11-05",
+                  "geschlecht": "weiblich"
                 }
               }
             ]

+ 0 - 718
src/main/resources/static/styles-accessible.css

@@ -1,718 +0,0 @@
-:root {
-  /* Standard Farben mit verbessertem Kontrast */
-  --rot-hell: #ffe6e6;
-  --gelb-hell: #fff8db;
-  --gruen-hell: #e8f5e8;
-  --rot: #b30000;      /* Verbessert von #800000 für besseren Kontrast */
-  --gelb: #9d5502;     /* Verbessert von #784801 für besseren Kontrast */
-  --gruen: #004d26;    /* Verbessert von #006633 für besseren Kontrast */
-  --weiss: #ffffff;
-  --schwarz: #000000;
-  --grau-hell: #f7f9fa;
-  --grau-stark: #d7d8d9;
-  --berlin-rot: #e20613;
-  --diwo-blau: #145c9e;
-  --diwo-blau-hell: #cfe8fc;
-  
-  /* Focus Ring Farbe für bessere Sichtbarkeit */
-  --focus-color: #0066cc;
-  --focus-outline: 3px solid var(--focus-color);
-}
-
-/* Ampelfarben (Klassische Ampel) mit besserem Kontrast */
-:root[data-color-scheme="ampel"] {
-  --rot-hell: #ffe6e6;
-  --gelb-hell: #fffacc;
-  --gruen-hell: #e6ffe6;
-  --rot: #990000;      /* Verbessert für besseren Kontrast */
-  --gelb: #996600;     /* Verbessert für besseren Kontrast */
-  --gruen: #006600;    /* Verbessert für besseren Kontrast */
-}
-
-/* Alternative Farben mit besserem Kontrast */
-:root[data-color-scheme="alternative"] {
-  --rot-hell: #ffe6f5;
-  --gelb-hell: #ffeccc;
-  --gruen-hell: #e6f9ff;
-  --rot: #cc0044;      /* Verbessert für besseren Kontrast */
-  --gelb: #b8620e;     /* Verbessert für besseren Kontrast */
-  --gruen: #008a8a;    /* Verbessert für besseren Kontrast */
-}
-
-* {
-  box-sizing: border-box;
-}
-
-body {
-  margin: 0;
-  padding: 16px;
-  background: var(--grau-hell);
-  color: var(--schwarz);
-  font: 14px/1.5 system-ui, sans-serif; /* Verbesserte Zeilenhöhe für bessere Lesbarkeit */
-}
-
-/* Focus-Stile für bessere Keyboard-Navigation */
-*:focus {
-  outline: var(--focus-outline);
-  outline-offset: 2px;
-}
-
-/* Verbesserter Focus für interaktive Elemente */
-button:focus,
-select:focus,
-input:focus,
-.person-row:focus,
-.section-header:focus {
-  outline: var(--focus-outline);
-  outline-offset: 2px;
-}
-
-h1, h2, h3 {
-  margin: 0.2rem 0 0.6rem;
-  line-height: 1.3; /* Verbesserte Zeilenhöhe für Überschriften */
-}
-
-.muted {
-  opacity: 0.8;
-}
-
-.row {
-  display: flex;
-  gap: 0.5rem;
-  align-items: center;
-}
-
-.grid-2 {
-  display: grid;
-  grid-template-columns: 1fr 1fr;
-  gap: 12px;
-}
-
-.grid-2 .full {
-  grid-column: 1/-1;
-}
-
-textarea,
-input,
-select {
-  width: 100%;
-  padding: 0.5rem;
-  background: var(--weiss);
-  color: var(--schwarz);
-  border: 2px solid var(--grau-stark); /* Dickerer Border für bessere Sichtbarkeit */
-  border-radius: 4px;
-  font-size: 14px; /* Explizite Schriftgröße für bessere Lesbarkeit */
-}
-
-/* Hover-Effekte für bessere Interaktivität */
-textarea:hover,
-input:hover,
-select:hover {
-  border-color: var(--diwo-blau);
-}
-
-button {
-  padding: 0.5rem 0.9rem;
-  border: 2px solid var(--grau-stark);
-  background: #fff;
-  cursor: pointer;
-  font-size: 14px;
-  min-height: 44px; /* Mindesthöhe für Touch-Geräte */
-  transition: all 0.2s ease;
-}
-
-button:hover {
-  background: var(--grau-hell);
-  border-color: var(--diwo-blau);
-}
-
-button:active {
-  transform: translateY(1px);
-}
-
-.error-box {
-  background: var(--rot-hell);
-  border-left: 4px solid var(--rot);
-  padding: 0.6rem;
-  margin: 0.6rem 0;
-  border-radius: 4px;
-}
-
-/* DIWO Header Styles */
-.diwo-header {
-  background: var(--weiss);
-  border: 1px solid var(--grau-stark);
-  padding: 12px 16px;
-  margin-bottom: 16px;
-  border-radius: 4px;
-}
-
-.header-row-1 {
-  display: flex;
-  align-items: center;
-  gap: 20px;
-  margin-bottom: 8px;
-  padding-bottom: 8px;
-  border-bottom: 1px solid var(--grau-stark);
-}
-
-.kunde-logo {
-  width: auto;
-  height: 50px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-}
-
-.kunde-logo-container {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  width: 100%;
-  height: 100%;
-}
-
-.kunde-logo-image {
-  max-width: auto;
-  max-height: 50px;
-  object-fit: contain;
-  border-radius: 4px;
-  border: 1px solid var(--grau-stark); /* Border für bessere Definition */
-}
-
-.diwo-title {
-  display: flex;
-  flex-direction: column;
-  align-items: flex-start;
-}
-
-.diwo-title h1 {
-  font-size: 24px;
-  font-weight: bold;
-  margin: 0;
-  color: var(--diwo-blau);
-}
-
-.diwo-title h2 {
-  font-size: 18px;
-  font-weight: normal;
-  margin: 0;
-  color: var(--schwarz);
-}
-
-.page-title {
-  font-size: 16px;
-  font-weight: bold;
-  margin-left: auto;
-}
-
-.header-row-2 {
-  display: grid;
-  grid-template-columns: auto 1fr auto;
-  gap: 20px;
-  margin-bottom: 8px;
-  align-items: center;
-}
-
-.aktenzeichen {
-  font-weight: bold;
-  font-size: 14px;
-}
-
-.antragsteller-info {
-  text-align: center;
-  font-weight: bold;
-}
-
-.location-info {
-  text-align: right;
-  font-size: 13px;
-}
-
-.header-row-3 {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-top: 8px;
-  padding-top: 8px;
-  border-top: 1px solid var(--grau-stark);
-}
-
-.vorgang-info {
-  font-size: 13px;
-}
-
-.zeitraum-controls {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-}
-
-.zeitraum-form {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-}
-
-.zeitraum-form label {
-  font-size: 13px;
-  font-weight: bold;
-  white-space: nowrap;
-}
-
-.zeitraum-select {
-  width: 200px;
-  padding: 4px 8px;
-  font-size: 13px;
-  min-height: 32px; /* Mindesthöhe für bessere Bedienbarkeit */
-}
-
-.wechseln-btn {
-  padding: 4px 8px;
-  font-size: 12px;
-  min-width: 32px;
-  min-height: 32px; /* Mindesthöhe für Touch-Geräte */
-}
-
-/* Vergleichslegende */
-.vergleich-legende {
-  display: flex;
-  align-items: center;
-  gap: 12px;
-  margin: 12px 0;
-  padding: 8px 12px;
-  background: var(--grau-hell);
-  border: 1px solid var(--grau-stark);
-  border-radius: 4px;
-  font-size: 13px;
-}
-
-.legende-item {
-  display: flex;
-  align-items: center;
-  gap: 4px;
-  padding: 4px 8px;
-  border-radius: 3px;
-}
-
-.legende-item:has(.plus) {
-  background: var(--gruen-hell);
-  border: 1px solid var(--gruen); /* Border für bessere Definition */
-}
-
-.legende-item:has(.minus) {
-  background: var(--rot-hell);
-  border: 1px solid var(--rot);
-}
-
-.legende-item:has(.warn) {
-  background: var(--gelb-hell);
-  border: 1px solid var(--gelb);
-}
-
-.legende-title {
-  margin-right: 8px;
-  font-size: 13px;
-}
-
-.legende-spacer {
-  color: var(--grau-stark);
-  font-weight: bold;
-}
-
-.selected-indicator-text {
-  border: 3px solid var(--diwo-blau);
-  padding: 2px 4px;
-  border-radius: 2px;
-  display: inline-block;
-  background: var(--diwo-blau-hell);
-}
-
-table {
-  width: 100%;
-  border-collapse: collapse;
-  background: var(--weiss);
-  border: 1px solid var(--grau-stark);
-  margin: 0.6rem 0 1rem;
-}
-
-th, td {
-  border: 1px solid var(--grau-stark);
-  padding: 0.5rem;
-  vertical-align: top;
-  text-align: left; /* Explizite Textausrichtung */
-}
-
-th {
-  background: var(--grau-stark);
-  position: sticky;
-  top: 0;
-  font-weight: bold;
-  color: var(--schwarz); /* Sicherstellen dass Text lesbar ist */
-}
-
-.sym {
-  width: 2.5rem;
-  min-width: 2.5rem;
-  max-width: 2.5rem;
-  text-align: center;
-  font-weight: 700;
-  font-size: 16px; /* Größere Symbole für bessere Sichtbarkeit */
-}
-
-.sym.plus {
-  color: var(--gruen);
-}
-
-.sym.minus {
-  color: var(--rot);
-}
-
-.sym.warn {
-  color: var(--gelb);
-}
-
-/* Verbesserte Diff-Zeilen mit Patterns für Farbenblinde */
-.diff-row-added {
-  background: var(--gruen-hell);
-  background-image: repeating-linear-gradient(
-    45deg,
-    transparent,
-    transparent 5px,
-    rgba(0, 77, 38, 0.1) 5px,
-    rgba(0, 77, 38, 0.1) 10px
-  );
-}
-
-.diff-row-removed {
-  background: var(--rot-hell);
-  background-image: repeating-linear-gradient(
-    -45deg,
-    transparent,
-    transparent 5px,
-    rgba(179, 0, 0, 0.1) 5px,
-    rgba(179, 0, 0, 0.1) 10px
-  );
-}
-
-.cell-changed,
-td:has(.cell-changed) {
-  background: var(--gelb-hell);
-  background-image: repeating-linear-gradient(
-    90deg,
-    transparent,
-    transparent 3px,
-    rgba(157, 85, 2, 0.1) 3px,
-    rgba(157, 85, 2, 0.1) 6px
-  );
-}
-
-/* Verbessertes Icon für geänderte Zellen */
-.cell-changed:not(.sym)::before {
-  content: "! ";
-  color: var(--gelb);
-  font-weight: bold;
-  font-size: 0.9em;
-  margin-right: 4px;
-}
-
-.old {
-  display: block;
-  font-size: 0.85em;
-  margin-top: 0.2rem;
-  opacity: 0.9;
-  text-decoration: line-through;
-}
-
-.header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 8px;
-}
-
-.legend {
-  margin: 0.5rem 0 1rem;
-}
-
-/* Person Selection - Blue outline for selected person */
-.person-selected {
-  outline: 3px solid var(--diwo-blau);
-  outline-offset: -3px;
-  background: var(--diwo-blau-hell);
-}
-
-/* Clickable table rows mit verbesserter Accessibility */
-.person-row {
-  cursor: pointer;
-  transition: background-color 0.2s ease;
-  position: relative;
-}
-
-.person-row:hover {
-  background-color: var(--diwo-blau-hell);
-}
-
-.person-row:focus {
-  background-color: var(--diwo-blau-hell);
-}
-
-/* Skip to content für Screen Reader */
-.skip-link {
-  position: absolute;
-  top: -40px;
-  left: 6px;
-  background: var(--diwo-blau);
-  color: var(--weiss);
-  padding: 8px;
-  text-decoration: none;
-  border-radius: 4px;
-  z-index: 1000;
-}
-
-.skip-link:focus {
-  top: 6px;
-}
-
-.table-action {
-  padding: 0.25rem 0.5rem;
-  border: 1px solid var(--grau-stark);
-  background: #fff;
-  cursor: pointer;
-  font-size: 0.85rem;
-  min-height: 32px;
-}
-
-.table-action:hover {
-  filter: brightness(0.98);
-}
-
-/* Farbwahl in der Legende */
-.color-picker-container {
-  position: relative;
-  display: flex;
-  align-items: center;
-  gap: 4px;
-  margin-left: auto;
-}
-
-.color-picker-toggle {
-  background: none;
-  border: 2px solid var(--grau-stark);
-  cursor: pointer;
-  font-size: 16px;
-  padding: 4px;
-  border-radius: 4px;
-  transition: all 0.2s ease;
-  min-height: 32px;
-  min-width: 32px;
-}
-
-.color-picker-toggle:hover {
-  background: var(--grau-stark);
-  border-color: var(--diwo-blau);
-}
-
-.color-picker-popup {
-  position: absolute;
-  top: 100%;
-  right: 0;
-  background: var(--weiss);
-  border: 2px solid var(--grau-stark);
-  border-radius: 4px;
-  padding: 12px;
-  box-shadow: 0 4px 12px rgba(20, 92, 158, 0.2);
-  z-index: 1000;
-  font-size: 13px;
-  min-width: 160px;
-}
-
-.color-picker-popup.hidden {
-  display: none;
-}
-
-.color-picker-popup label {
-  font-weight: bold;
-  margin-bottom: 6px;
-  display: block;
-}
-
-.color-scheme-select {
-  width: 100%;
-  padding: 6px 8px;
-  font-size: 13px;
-  border: 2px solid var(--grau-stark);
-  border-radius: 4px;
-  min-height: 32px;
-}
-
-/* Responsive Layout für Freibeträge und Kinderbetreuung */
-.freibetrag-container {
-  display: flex;
-  gap: 16px;
-  margin: 0.6rem 0 1rem;
-}
-
-.freibetrag-section {
-  flex: 1;
-  min-width: 0;
-}
-
-.freibetrag-section h3 {
-  margin: 0 0 0.6rem;
-  font-size: 16px;
-}
-
-.kinderbetreuung-container {
-  max-width: 50%;
-}
-
-/* Bei kleinen Bildschirmen: Untereinander anordnen */
-@media (max-width: 1200px) {
-  .freibetrag-container {
-    flex-direction: column;
-  }
-
-  .kinderbetreuung-container {
-    max-width: 100%;
-  }
-}
-
-/* Ausklappbare Sektionen */
-.section-container {
-  margin: 0.6rem 0 1rem;
-}
-
-.section-header {
-  background: var(--grau-stark);
-  padding: 12px 16px;
-  border: 2px solid var(--grau-stark);
-  border-radius: 4px 4px 0 0;
-  cursor: pointer;
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  user-select: none;
-  transition: all 0.2s ease;
-  min-height: 44px; /* Mindesthöhe für Touch-Geräte */
-}
-
-.section-header:hover {
-  background: #c5c6c7;
-  border-color: var(--diwo-blau);
-}
-
-.section-header:focus {
-  background: #c5c6c7;
-  border-color: var(--diwo-blau);
-}
-
-.section-header h3 {
-  margin: 0;
-  font-size: 16px;
-  font-weight: bold;
-}
-
-.toggle-icon {
-  font-size: 14px;
-  transition: transform 0.2s ease;
-  font-weight: bold;
-}
-
-.toggle-icon.collapsed {
-  transform: rotate(-90deg);
-}
-
-.section-content {
-  border: 2px solid var(--grau-stark);
-  border-top: none;
-  border-radius: 0 0 4px 4px;
-  transition: max-height 0.3s ease;
-  overflow: hidden;
-}
-
-.section-content.collapsed {
-  display: none;
-}
-
-.section-content table {
-  margin: 0;
-  border: none;
-}
-
-/* Screen Reader only content */
-.sr-only {
-  position: absolute;
-  width: 1px;
-  height: 1px;
-  padding: 0;
-  margin: -1px;
-  overflow: hidden;
-  clip: rect(0, 0, 0, 0);
-  white-space: nowrap;
-  border: 0;
-}
-
-/* High Contrast Mode Support */
-@media (prefers-contrast: high) {
-  :root {
-    --rot: #000000;
-    --gelb: #000000;
-    --gruen: #000000;
-    --grau-stark: #000000;
-  }
-  
-  .sym {
-    font-weight: 900;
-    text-shadow: 1px 1px 1px rgba(255, 255, 255, 0.5);
-  }
-  
-  button, input, select {
-    border-width: 3px;
-  }
-}
-
-/* Reduced Motion Support */
-@media (prefers-reduced-motion: reduce) {
-  *, *::before, *::after {
-    animation-duration: 0.01ms !important;
-    animation-iteration-count: 1 !important;
-    transition-duration: 0.01ms !important;
-  }
-  
-  .toggle-icon {
-    transition: none;
-  }
-  
-  .section-content {
-    transition: none;
-  }
-}
-
-/* Print Styles */
-@media print {
-  .color-picker-container,
-  .zeitraum-controls,
-  .section-header {
-    display: none;
-  }
-  
-  .section-content {
-    display: block !important;
-  }
-  
-  .diff-row-added::after {
-    content: " [HINZUGEFÜGT]";
-    font-weight: bold;
-  }
-  
-  .diff-row-removed::after {
-    content: " [GELÖSCHT]";
-    font-weight: bold;
-  }
-  
-  .cell-changed::after {
-    content: " [GEÄNDERT]";
-    font-weight: bold;
-  }
-}

+ 147 - 2
src/main/resources/static/styles.css

@@ -403,7 +403,6 @@ td:has(.cell-changed) {
   margin: 0.5rem 0 1rem;
 }
 
-/* Person Selection - Blue outline for selected person */
 .person-selected {
   outline: 3px solid var(--diwo-blau);
   outline-offset: -3px;
@@ -422,7 +421,6 @@ td:has(.cell-changed) {
 
 .person-row:focus {
   background-color: var(--diwo-blau-hell);
-  /* Verbesserte Focus-Umrahmung für Tabellenzeilen */
   outline: var(--focus-outline);
   outline-offset: 1px; /* Reduzierter Offset für bessere Sichtbarkeit in Tabellen */
   z-index: 1; /* Stelle sicher, dass der Focus-Ring über anderen Elementen liegt */
@@ -615,3 +613,150 @@ tr:focus {
   margin: 0;
   border: none;
 }
+
+/* ===== Erweiterte Stile für aufklappbare Personendetails ===== */
+
+/* Expandable table styles */
+.expandable-table {
+  width: 100%;
+  border-collapse: collapse;
+  background: var(--weiss);
+  border: 1px solid var(--grau-stark);
+  margin: 0.6rem 0 1rem;
+  border-spacing: 0;
+}
+
+/* Expand column für den Aufklapp-Button */
+.expand-col {
+  width: 40px;
+  min-width: 40px;
+  max-width: 40px;
+  text-align: center;
+  padding: 0.25rem;
+}
+
+/* Aufklapp-Button Stile */
+.expand-btn {
+  background: none;
+  border: 1px solid var(--grau-stark);
+  cursor: pointer;
+  font-size: 14px;
+  padding: 4px 6px;
+  border-radius: 3px;
+  transition: all 0.2s ease;
+  min-height: 28px;
+  min-width: 28px;
+  color: var(--diwo-blau);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.expand-btn:hover {
+  background: var(--diwo-blau-hell);
+  border-color: var(--diwo-blau);
+}
+
+.expand-btn:focus {
+  outline: 2px solid var(--focus-color);
+  outline-offset: 1px;
+}
+
+.expand-btn span {
+  transition: transform 0.2s ease;
+  font-weight: bold;
+}
+
+.expand-btn[aria-expanded="true"] span {
+  transform: rotate(90deg);
+}
+
+/* Detail-Zeilen Stile */
+.person-details-row {
+  transition: all 0.3s ease;
+}
+
+.person-details-row.hidden {
+  display: none;
+}
+
+.details-cell {
+  padding: 16px;
+  background: var(--grau-stark) !important;
+  border-left: 4px solid var(--diwo-blau);
+}
+
+.details-container {
+  max-width: 100%;
+}
+
+/* Details-Tabelle Styling */
+.details-table {
+  width: 100%;
+  border-collapse: collapse;
+  background: var(--weiss);
+  border: 1px solid var(--grau-stark);
+  margin-top: 8px;
+  font-size: 14px;
+}
+
+.details-table td {
+  border: 1px solid var(--grau-stark);
+  padding: 6px 8px;
+  vertical-align: top;
+  background: var(--weiss);
+}
+
+.detail-label {
+  font-weight: bold;
+  background: var(--grau-hell);
+  color: var(--schwarz);
+  width: 12.5%;
+  min-width: 80px;
+}
+
+
+/* Responsive Anpassungen für Details-Tabelle */
+@media (max-width: 768px) {
+  .details-table {
+    font-size: 13px;
+  }
+  
+  .details-table td {
+    padding: 4px 6px;
+  }
+  
+  .detail-label {
+    min-width: 60px;
+  }
+}
+
+/* Verbesserte Keyboard-Navigation für aufklappbare Bereiche */
+.expand-btn:focus-visible {
+  outline: 3px solid var(--focus-color);
+  outline-offset: 2px;
+}
+
+/* Animation für das Auf- und Zuklappen */
+@media (prefers-reduced-motion: no-preference) {
+  .person-details-row {
+    animation-duration: 0.3s;
+    animation-fill-mode: both;
+  }
+  
+  .person-details-row:not(.hidden) {
+    animation-name: slideDown;
+  }
+}
+
+@keyframes slideDown {
+  from {
+    opacity: 0;
+    transform: translateY(-10px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+

+ 0 - 513
src/main/resources/templates/vergleich-accessible.html

@@ -1,513 +0,0 @@
-<!DOCTYPE html>
-<html lang="de" xmlns:th="http://www.thymeleaf.org">
-<head>
-    <meta charset="UTF-8"/>
-    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
-    <title>DIWO Vergleich – Haushaltsmitglieder/Einkommen</title>
-    <link rel="stylesheet" href="/styles.css"/>
-</head>
-<body>
-
-  <header class="diwo-header" role="banner">
-    <div class="header-row-1">
-      <div class="kunde-logo">
-        <div th:if="${activeKunde != null and !#strings.isEmpty(activeKunde)}" class="kunde-logo-container">
-          <img th:src="@{'/images/' + ${#strings.toLowerCase(activeKunde)} + '.jpg'}" 
-               th:alt="${activeKunde != null and !#strings.isEmpty(activeKunde) ? activeKunde + ' Logo' : 'Kunden-Logo'}" 
-               class="kunde-logo-image"
-               th:onerror="'this.style.display=\\'none\\''"/>
-        </div>
-      </div>
-      <div class="diwo-title">
-        <h1>DIWO</h1>
-        <h2>Vergleich</h2>
-      </div>
-      <div class="page-title" role="heading" aria-level="2">Haushaltsmitglieder/Einkommen</div>
-    </div>
-    
-    <div class="header-row-2">
-      <div class="aktenzeichen" th:if="${stammakteRight != null and !#strings.isEmpty(stammakteRight.aktenzeichen)}" role="contentinfo" aria-label="Aktenzeichen">
-        <span th:text="${stammakteRight.aktenzeichen}">01/01/119979</span>
-      </div>
-      <div class="aktenzeichen" th:if="${(stammakteRight == null or #strings.isEmpty(stammakteRight.aktenzeichen)) and stammvorgangRight != null}" role="contentinfo" aria-label="Aktenzeichen">
-        <span th:text="${stammvorgangRight.aktenzeichen}">01/01/119979</span>
-      </div>
-      
-      <div class="antragsteller-info" th:if="${stammakteRight != null}" role="contentinfo" aria-label="Antragsteller-Informationen">
-        <span th:if="${!#strings.isEmpty(stammakteRight.antragstellername)}" th:text="${stammakteRight.antragstellername}">Müller, Tobias</span>
-        <span th:if="${!#strings.isEmpty(stammakteRight.antragstellerGeburtsdatum)}"> - <span th:text="${stammakteRight.antragstellerGeburtsdatum}">01.01.2000</span></span>
-      </div>
-      
-      <div class="location-info" th:if="${stammakteRight != null and !#strings.isEmpty(stammakteRight.anschrift)}" role="contentinfo" aria-label="Anschrift">
-        <span th:text="${stammakteRight.anschrift}">Unter den Linden 1, 10117 Berlin</span>
-      </div>
-    </div>
-    
-    <div class="header-row-3">
-      <div class="vorgang-info" th:if="${stammvorgangRight != null}" role="contentinfo" aria-label="Vorgang in Bearbeitung">
-        <span>Vorgang in der Bearbeitung: </span>
-        <span th:if="${!#strings.isEmpty(stammvorgangRight.bemerkung)}" th:text="${stammvorgangRight.bemerkung}">Weiterleistungsantrag</span>
-        <span th:if="${#strings.isEmpty(stammvorgangRight.bemerkung)}">-</span>
-        <span th:if="${!#strings.isEmpty(stammvorgangRight.beginn) and !#strings.isEmpty(stammvorgangRight.ende)}">
-          (<span th:text="${stammvorgangRight.beginn}">01.01.2026</span> - <span th:text="${stammvorgangRight.ende}">31.12.2027</span>)
-        </span>
-      </div>
-      
-      <div class="zeitraum-controls">
-        <form method="post" action="/vergleich" class="zeitraum-form" role="form" aria-label="Zeitraum wechseln">
-          <input type="hidden" name="schemaName" th:value="${schemaName}"/>
-          <input type="hidden" name="validate" th:value="${validate}"/>
-          <input type="hidden" name="leftJson" th:value="${leftJson}"/>
-          <input type="hidden" name="rightJson" th:value="${rightJson}"/>
-          <input type="hidden" name="leftVorgangId" th:value="${leftVorgangId}"/>
-          <input type="hidden" name="leftZeitraumId" th:value="${leftZeitraumId}"/>
-          <input type="hidden" name="kunde" th:value="${activeKunde}"/>
-          
-          <label for="rightZeitraumId">Zeitraum:</label>
-          <select name="rightZeitraumId" id="rightZeitraumId" class="zeitraum-select" aria-describedby="zeitraum-hint">
-            <option th:each="z : ${zeitraeumeRight}" th:value="${z.id}"
-                    th:selected="${z.id == rightZeitraumId}"
-                    th:text="${z.beginn} + ' - ' + ${z.ende}"></option>
-          </select>
-          <div id="zeitraum-hint" class="sr-only">Wählen Sie einen anderen Zeitraum für den Vergleich aus</div>
-          <button type="submit" class="wechseln-btn" aria-label="Zeitraum wechseln">▼</button>
-        </form>
-      </div>
-    </div>
-  </header>
-
-  <!-- Vergleichslegende -->
-  <section class="vergleich-legende" role="complementary" aria-labelledby="legende-title">
-    <div class="legende-title" id="legende-title">
-      <strong>Legende:</strong>
-    </div>
-    <div class="legende-item">
-      <span class="sym plus" aria-label="Hinzugefügt">+</span> 
-      <span>hinzugefügt (<span id="addedColorName">grün</span>)</span>
-    </div>
-    <div class="legende-spacer" aria-hidden="true">|</div>
-    <div class="legende-item">
-      <span class="sym minus" aria-label="Gelöscht">−</span> 
-      <span>gelöscht (<span id="removedColorName">rot</span>)</span>
-    </div>
-    <div class="legende-spacer" aria-hidden="true">|</div>
-    <div class="legende-item">
-      <span class="sym warn" aria-label="Geändert">!</span> 
-      <span>geändert (<span id="changedColorName">gelb</span>)</span>
-    </div>
-    <div class="legende-spacer" aria-hidden="true">|</div>
-    <div class="legende-item">
-      <span class="selected-indicator-text">ausgewählte Person (blauer Rahmen)</span>
-    </div>
-    
-    <!-- Farbwahl -->
-    <div class="color-picker-container">
-      <button class="color-picker-toggle" onclick="toggleColorPicker()" aria-label="Farbschema-Einstellungen öffnen" aria-expanded="false" aria-controls="colorPickerPopup">⚙️</button>
-      <div class="color-picker-popup hidden" id="colorPickerPopup" role="dialog" aria-labelledby="color-picker-label">
-        <label id="color-picker-label" for="colorSchemeSelect">Farbwahl:</label>
-        <select id="colorSchemeSelect" class="color-scheme-select" onchange="changeColorScheme(this.value)" aria-describedby="color-scheme-hint">
-          <option value="standard">Standard</option>
-          <option value="ampel">Ampelfarben (neon)</option>
-          <option value="alternative">Pastell</option>
-        </select>
-        <div id="color-scheme-hint" class="sr-only">Ändern Sie das Farbschema für bessere Sichtbarkeit</div>
-      </div>
-    </div>
-  </section>
-
-  <!-- Haushaltsmitglieder - klickbare Zeilen -->
-  <main role="main">
-    <div class="section-container">
-      <div class="section-header" onclick="toggleSection('haushaltsmitglieder')" role="button" tabindex="0" 
-           onkeypress="handleKeyPress(event, 'haushaltsmitglieder')" aria-expanded="true" aria-controls="haushaltsmitglieder-content">
-        <span class="toggle-icon" id="haushaltsmitglieder-icon" aria-hidden="true">▼</span>
-        <h3>Haushaltsmitglieder</h3>
-      </div>
-      <div class="section-content" id="haushaltsmitglieder-content">
-        <table role="table" aria-labelledby="haushaltsmitglieder-caption">
-          <caption id="haushaltsmitglieder-caption" class="sr-only">
-            Vergleich der Haushaltsmitglieder zwischen zwei Zeiträumen. Klicken Sie auf eine Zeile, um Details zu dieser Person anzuzeigen.
-          </caption>
-          <thead>
-            <tr>
-              <th scope="col">Änderung</th>
-              <th scope="col">Name</th>
-              <th scope="col">Geburtsdatum</th>
-              <th scope="col">Familienstand</th>
-              <th scope="col">Geschlecht</th>
-              <th scope="col">Verwandtschaft</th>
-              <th scope="col">Sozialstellung</th>
-              <th scope="col">Kind</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr th:each="r : ${mitgliederRows}"
-                th:class="'person-row ' + ${r.rowClass} + ((${r.personId} == ${personId}) ? ' person-selected' : '')"
-                th:data-person-id="${r.personId}"
-                role="button"
-                tabindex="0"
-                th:aria-label="'Person auswählen: ' + ${r.nameHtml}"
-                onkeypress="handleRowKeyPress(event, this)">
-              <td class="sym" th:classappend="' ' + ${r.symbolClass}" th:text="${r.symbol}" 
-                  th:aria-label="${r.symbol == '+' ? 'Hinzugefügt' : (r.symbol == '-' ? 'Gelöscht' : (r.symbol == '!' ? 'Geändert' : 'Unverändert'))}"></td>
-              <td th:utext="${r.nameHtml}"></td>
-              <td th:utext="${r.geburtsdatumHtml}"></td>
-              <td th:utext="${r.familienstandHtml}"></td>
-              <td th:utext="${r.geschlechtHtml}"></td>
-              <td th:utext="${r.verwandtschaftHtml}"></td>
-              <td th:utext="${r.sozialstellungHtml}"></td>
-              <td th:utext="${r.istKindHtml}"></td>
-            </tr>
-          </tbody>
-        </table>
-      </div>
-    </div>
-
-    <!-- Hidden form for person selection -->
-    <form id="personForm" method="post" action="/vergleich" style="display: none;" aria-hidden="true">
-      <input type="hidden" name="leftJson" th:value="${leftJson}"/>
-      <input type="hidden" name="rightJson" th:value="${rightJson}"/>
-      <input type="hidden" name="validate" th:value="${validate}"/>
-      <input type="hidden" name="schemaName" th:value="${schemaName}"/>
-      <input type="hidden" name="leftVorgangId" th:value="${leftVorgangId}"/>
-      <input type="hidden" name="leftZeitraumId" th:value="${leftZeitraumId}"/>
-      <input type="hidden" name="rightZeitraumId" th:value="${rightZeitraumId}"/>
-      <input type="hidden" name="kunde" th:value="${activeKunde}"/>
-      <input type="hidden" name="personId" id="selectedPersonId"/>
-    </form>
-
-    <h2>Details zu <span th:if="${personLabel != null and !#strings.isEmpty(personLabel) and personLabel != '(keine Person)'}"><span th:text="${personLabel}"> - </span></span></h2>
-    
-    <!-- Einkommen -->
-    <div class="section-container">
-      <div class="section-header" onclick="toggleSection('einkommen')" role="button" tabindex="0" 
-           onkeypress="handleKeyPress(event, 'einkommen')" aria-expanded="true" aria-controls="einkommen-content">
-        <span class="toggle-icon" id="einkommen-icon" aria-hidden="true">▼</span>
-        <h3>Einkommen</h3>
-      </div>
-      <div class="section-content" id="einkommen-content">
-        <table role="table" aria-labelledby="einkommen-caption">
-          <caption id="einkommen-caption" class="sr-only">
-            Vergleich der Einkommensdaten zwischen zwei Zeiträumen
-          </caption>
-          <thead>
-            <tr>
-              <th scope="col">Änderung</th>
-              <th scope="col">Art</th>
-              <th scope="col">Frequenz</th>
-              <th scope="col">Betrag</th>
-              <th scope="col">Zuschlag</th>
-              <th scope="col">Abschlag</th>
-              <th scope="col">Werbungskosten</th>
-              <th scope="col">Berechnungsart</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr th:each="z : ${einkommenZeilen}" th:class="${z.rowClass}">
-              <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"
-                  th:aria-label="${z.symbol == '+' ? 'Hinzugefügt' : (z.symbol == '-' ? 'Gelöscht' : (z.symbol == '!' ? 'Geändert' : 'Unverändert'))}"></td>
-              <td th:utext="${z.artHtml}"></td>
-              <td th:utext="${z.frequenzHtml}"></td>
-              <td th:utext="${z.betragHtml}"></td>
-              <td th:utext="${z.zuschlagHtml}"></td>
-              <td th:utext="${z.abschlagHtml}"></td>
-              <td th:utext="${z.werbungskostenHtml}"></td>
-              <td th:utext="${z.berechnungsartHtml}"></td>
-            </tr>
-          </tbody>
-        </table>
-      </div>
-    </div>
-
-    <div class="freibetrag-container">
-      <div class="freibetrag-section">
-        <div class="section-container">
-          <div class="section-header" onclick="toggleSection('pauschale')" role="button" tabindex="0" 
-               onkeypress="handleKeyPress(event, 'pauschale')" aria-expanded="true" aria-controls="pauschale-content">
-            <span class="toggle-icon" id="pauschale-icon" aria-hidden="true">▼</span>
-            <h3>Pauschale Abzüge</h3>
-          </div>
-          <div class="section-content" id="pauschale-content">
-            <table role="table" aria-labelledby="pauschale-caption">
-              <caption id="pauschale-caption" class="sr-only">
-                Vergleich der pauschalen Abzüge zwischen zwei Zeiträumen
-              </caption>
-              <thead>
-                <tr>
-                  <th scope="col">Änderung</th>
-                  <th scope="col">Art</th>
-                  <th scope="col">Frequenz</th>
-                  <th scope="col">Betrag</th>
-                </tr>
-              </thead>
-              <tbody>
-                <tr th:each="z : ${freibetragZeilen}" th:if="${z.typ == 'P'}" th:class="${z.rowClass}">
-                  <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"
-                      th:aria-label="${z.symbol == '+' ? 'Hinzugefügt' : (z.symbol == '-' ? 'Gelöscht' : (z.symbol == '!' ? 'Geändert' : 'Unverändert'))}"></td>
-                  <td th:utext="${z.artHtml}"></td>
-                  <td th:utext="${z.frequenzHtml}"></td>
-                  <td th:utext="${z.betragHtml}"></td>
-                </tr>
-              </tbody>
-            </table>
-          </div>
-        </div>
-      </div>
-      
-      <div class="freibetrag-section">
-        <div class="section-container">
-          <div class="section-header" onclick="toggleSection('freibetraege')" role="button" tabindex="0" 
-               onkeypress="handleKeyPress(event, 'freibetraege')" aria-expanded="true" aria-controls="freibetraege-content">
-            <span class="toggle-icon" id="freibetraege-icon" aria-hidden="true">▼</span>
-            <h3>Freibeträge</h3>
-          </div>
-          <div class="section-content" id="freibetraege-content">
-            <table role="table" aria-labelledby="freibetraege-caption">
-              <caption id="freibetraege-caption" class="sr-only">
-                Vergleich der Freibeträge zwischen zwei Zeiträumen
-              </caption>
-              <thead>
-                <tr>
-                  <th scope="col">Änderung</th>
-                  <th scope="col">Art</th>
-                  <th scope="col">Frequenz</th>
-                  <th scope="col">Betrag</th>
-                  <th scope="col">Frist</th>
-                </tr>
-              </thead>
-              <tbody>
-                <tr th:each="z : ${freibetragZeilen}" th:if="${z.typ == 'F'}" th:class="${z.rowClass}">
-                  <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"
-                      th:aria-label="${z.symbol == '+' ? 'Hinzugefügt' : (z.symbol == '-' ? 'Gelöscht' : (z.symbol == '!' ? 'Geändert' : 'Unverändert'))}"></td>
-                  <td th:utext="${z.artHtml}"></td>
-                  <td th:utext="${z.frequenzHtml}"></td>
-                  <td th:utext="${z.betragHtml}"></td>
-                  <td th:utext="${z.fristHtml}"></td>
-                </tr>
-              </tbody>
-            </table>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Kinderbetreuungskosten -->
-    <div class="kinderbetreuung-container">
-      <div class="section-container">
-        <div class="section-header" onclick="toggleSection('kinderbetreuung')" role="button" tabindex="0" 
-             onkeypress="handleKeyPress(event, 'kinderbetreuung')" aria-expanded="true" aria-controls="kinderbetreuung-content">
-          <span class="toggle-icon" id="kinderbetreuung-icon" aria-hidden="true">▼</span>
-          <h3>Kinderbetreuungskosten</h3>
-        </div>
-        <div class="section-content" id="kinderbetreuung-content">
-          <table role="table" aria-labelledby="kinderbetreuung-caption">
-            <caption id="kinderbetreuung-caption" class="sr-only">
-              Vergleich der Kinderbetreuungskosten zwischen zwei Zeiträumen
-            </caption>
-            <thead>
-              <tr>
-                <th scope="col">Änderung</th>
-                <th scope="col">Kind</th>
-                <th scope="col">Betrag</th>
-              </tr>
-            </thead>
-            <tbody>
-              <tr th:each="z : ${kitaZeilen}" th:class="${z.rowClass}">
-                <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"
-                    th:aria-label="${z.symbol == '+' ? 'Hinzugefügt' : (z.symbol == '-' ? 'Gelöscht' : (z.symbol == '!' ? 'Geändert' : 'Unverändert'))}"></td>
-                <td th:utext="${z.kindNameHtml}"></td>
-                <td th:utext="${z.betragHtml}"></td>
-              </tr>
-            </tbody>
-          </table>
-        </div>
-      </div>
-    </div>
-  </main>
-
-  <script>
-    // Cookie Verwaltung
-    function setCookie(name, value, days) {
-      const expires = new Date();
-      expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000));
-      document.cookie = name + '=' + value + ';expires=' + expires.toUTCString() + ';path=/';
-    }
-
-    function getCookie(name) {
-      const nameEQ = name + '=';
-      const ca = document.cookie.split(';');
-      for(let i = 0; i < ca.length; i++) {
-        let c = ca[i];
-        while (c.charAt(0) === ' ') c = c.substring(1, c.length);
-        if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
-      }
-      return null;
-    }
-
-    // Keyboard support for toggles
-    function handleKeyPress(event, sectionId) {
-      if (event.key === 'Enter' || event.key === ' ') {
-        event.preventDefault();
-        toggleSection(sectionId);
-      }
-    }
-
-    // Keyboard support for table rows
-    function handleRowKeyPress(event, row) {
-      if (event.key === 'Enter' || event.key === ' ') {
-        event.preventDefault();
-        row.click();
-      }
-    }
-
-    // Farbwahl-Funktionen
-    function toggleColorPicker() {
-      const popup = document.getElementById('colorPickerPopup');
-      const button = document.querySelector('.color-picker-toggle');
-      const isHidden = popup.classList.contains('hidden');
-      
-      popup.classList.toggle('hidden');
-      button.setAttribute('aria-expanded', !isHidden);
-      
-      if (!isHidden) {
-        // Focus the select when opening
-        document.getElementById('colorSchemeSelect').focus();
-      }
-    }
-
-    function updateColorNames(scheme) {
-      const colorNames = {
-        'standard': { added: 'grün', removed: 'rot', changed: 'gelb' },
-        'ampel': { added: 'grün', removed: 'rot', changed: 'gelb' },
-        'alternative': { added: 'blau', removed: 'pink', changed: 'orange' }
-      };
-      
-      const names = colorNames[scheme] || colorNames['standard'];
-      
-      document.getElementById('addedColorName').textContent = names.added;
-      document.getElementById('removedColorName').textContent = names.removed;
-      document.getElementById('changedColorName').textContent = names.changed;
-    }
-
-    function changeColorScheme(scheme) {
-      const html = document.documentElement;
-      
-      // Entferne alle existierenden Farbschema-Attribute
-      html.removeAttribute('data-color-scheme');
-      
-      // Setze das neue Farbschema (außer für "standard")
-      if (scheme !== 'standard') {
-        html.setAttribute('data-color-scheme', scheme);
-      }
-      
-      // Aktualisiere die Farbnamen in der Legende
-      updateColorNames(scheme);
-      
-      // Speichere die Auswahl im Cookie (365 Tage gültig)
-      setCookie('colorScheme', scheme, 365);
-      
-      // Schließe das Popup nach der Auswahl
-      const popup = document.getElementById('colorPickerPopup');
-      const button = document.querySelector('.color-picker-toggle');
-      popup.classList.add('hidden');
-      button.setAttribute('aria-expanded', 'false');
-    }
-
-    function initializeColorScheme() {
-      // Lade das gespeicherte Farbschema aus dem Cookie (Standard: "standard" = Ihre ursprünglichen Farben)
-      const savedScheme = getCookie('colorScheme') || 'standard';
-      
-      // Setze das Farbschema
-      const html = document.documentElement;
-      if (savedScheme !== 'standard') {
-        html.setAttribute('data-color-scheme', savedScheme);
-      }
-      
-      // Aktualisiere die Farbnamen
-      updateColorNames(savedScheme);
-      
-      // Setze den Wert in der Dropdown-Box
-      const select = document.getElementById('colorSchemeSelect');
-      if (select) {
-        select.value = savedScheme;
-      }
-    }
-
-    // Escape key support for popup
-    document.addEventListener('keydown', function(event) {
-      if (event.key === 'Escape') {
-        const popup = document.getElementById('colorPickerPopup');
-        const button = document.querySelector('.color-picker-toggle');
-        if (!popup.classList.contains('hidden')) {
-          popup.classList.add('hidden');
-          button.setAttribute('aria-expanded', 'false');
-          button.focus();
-        }
-      }
-    });
-
-    // Schließe das Popup wenn außerhalb geklickt wird
-    document.addEventListener('click', function(event) {
-      const container = document.querySelector('.color-picker-container');
-      const popup = document.getElementById('colorPickerPopup');
-      const button = document.querySelector('.color-picker-toggle');
-      
-      if (container && !container.contains(event.target)) {
-        popup.classList.add('hidden');
-        button.setAttribute('aria-expanded', 'false');
-      }
-    });
-
-    // Toggle-Funktion für ausklappbare Sektionen
-    function toggleSection(sectionId) {
-      const content = document.getElementById(sectionId + '-content');
-      const icon = document.getElementById(sectionId + '-icon');
-      const header = content.parentElement.querySelector('.section-header');
-      
-      if (content.classList.contains('collapsed')) {
-        content.classList.remove('collapsed');
-        icon.classList.remove('collapsed');
-        icon.textContent = '▼';
-        header.setAttribute('aria-expanded', 'true');
-      } else {
-        content.classList.add('collapsed');
-        icon.classList.add('collapsed');
-        icon.textContent = '▶';
-        header.setAttribute('aria-expanded', 'false');
-      }
-    }
-
-    document.addEventListener('DOMContentLoaded', function() {
-      // Initialisiere das Farbschema
-      initializeColorScheme();
-      
-      // Add click event listeners to all person rows
-      const personRows = document.querySelectorAll('.person-row');
-      personRows.forEach(function(row) {
-        row.addEventListener('click', function() {
-          const personId = this.dataset.personId;
-          if (personId) {
-            document.getElementById('selectedPersonId').value = personId;
-            document.getElementById('personForm').submit();
-          }
-        });
-      });
-    });
-  </script>
-
-  <!-- Screen reader only styles -->
-  <style>
-    .sr-only {
-      position: absolute;
-      width: 1px;
-      height: 1px;
-      padding: 0;
-      margin: -1px;
-      overflow: hidden;
-      clip: rect(0, 0, 0, 0);
-      white-space: nowrap;
-      border: 0;
-    }
-  </style>
-</body>
-</html>

+ 152 - 35
src/main/resources/templates/vergleich.html

@@ -30,7 +30,7 @@
         <span th:text="${stammakteRight.aktenzeichen}">01/01/119979</span>
       </div>
       <div class="aktenzeichen" th:if="${(stammakteRight == null or #strings.isEmpty(stammakteRight.aktenzeichen)) and stammvorgangRight != null}" role="contentinfo" aria-label="Aktenzeichen">
-        <span th:text="${stammvorgangRight.aktenzeichen}">01/01/119979</span>
+        <span th:text="${stammvorgangRight.id}">01/01/119979</span>
       </div>
       
       <div class="antragsteller-info" th:if="${stammakteRight != null}" role="contentinfo" aria-label="Antragsteller-Informationen">
@@ -46,10 +46,10 @@
     <div class="header-row-3">
       <div class="vorgang-info" th:if="${stammvorgangRight != null}" role="contentinfo" aria-label="Vorgang in Bearbeitung">
         <span>Vorgang in der Bearbeitung: </span>
-        <span th:if="${!#strings.isEmpty(stammvorgangRight.bemerkung)}" th:text="${stammvorgangRight.bemerkung}">Weiterleistungsantrag</span>
-        <span th:if="${#strings.isEmpty(stammvorgangRight.bemerkung)}">-</span>
-        <span th:if="${!#strings.isEmpty(stammvorgangRight.beginn) and !#strings.isEmpty(stammvorgangRight.ende)}">
-          (<span th:text="${stammvorgangRight.beginn}">01.01.2026</span> - <span th:text="${stammvorgangRight.ende}">31.12.2027</span>)
+        <span th:if="${!#strings.isEmpty(stammvorgangRight.vorgangstyp)}" th:text="${stammvorgangRight.vorgangstyp}">Weiterleistungsantrag</span>
+        <span th:if="${#strings.isEmpty(stammvorgangRight.vorgangstyp)}">-</span>
+        <span th:if="${!#strings.isEmpty(stammvorgangRight.vorgangsstatus)}">
+          (Status: <span th:text="${stammvorgangRight.vorgangsstatus}">aktiv</span>)
         </span>
       </div>
       
@@ -112,7 +112,7 @@
     </div>
   </section>
 
-  <!-- Haushaltsmitglieder - klickbare Zeilen -->
+  <!-- Haushaltsmitglieder - erweiterte Tabelle mit aufklappbaren Zeilen -->
   <main role="main">
     <div class="section-container">
       <div class="section-header" onclick="toggleSection('haushaltsmitglieder')" role="button" tabindex="0" 
@@ -121,37 +121,98 @@
         <h3>Haushaltsmitglieder</h3>
       </div>
       <div class="section-content" id="haushaltsmitglieder-content">
-        <table role="table" aria-labelledby="haushaltsmitglieder-caption">
+        <table role="table" aria-labelledby="haushaltsmitglieder-caption" class="expandable-table">
           <thead>
             <tr>
+              <th scope="col" class="expand-col"></th>
               <th scope="col">Änderung</th>
               <th scope="col">Name</th>
+              <th scope="col">Blockname</th>
               <th scope="col">Geburtsdatum</th>
+              <th scope="col">Sterbedatum</th>
               <th scope="col">Familienstand</th>
               <th scope="col">Geschlecht</th>
+              <th scope="col">Anrede</th>
               <th scope="col">Verwandtschaft</th>
-              <th scope="col">Sozialstellung</th>
-              <th scope="col">Kind</th>
+              <th scope="col">Ausschlussgrund</th>
+              <th scope="col">Soz. Stellung</th>
+              <th scope="col">Ende Aufenthaltstitel</th>
+              <th scope="col">Antragsteller</th>
+              <th scope="col">Zahlungsempfänger</th>
             </tr>
           </thead>
           <tbody>
-            <tr th:each="r : ${mitgliederRows}"
-                th:class="'person-row ' + ${r.rowClass} + ((${r.personId} == ${personId}) ? ' person-selected' : '')"
-                th:data-person-id="${r.personId}"
-                role="button"
-                tabindex="0"
-                th:aria-label="'Person auswählen: ' + ${r.nameHtml}"
-                onkeypress="handleRowKeyPress(event, this)">
-              <td class="sym" th:classappend="' ' + ${r.symbolClass}" th:text="${r.symbol}" 
-                  th:aria-label="${r.symbol == '+' ? 'Hinzugefügt' : (r.symbol == '-' ? 'Gelöscht' : (r.symbol == '!' ? 'Geändert' : 'Unverändert'))}"></td>
-              <td th:utext="${r.nameHtml}"></td>
-              <td th:utext="${r.geburtsdatumHtml}"></td>
-              <td th:utext="${r.familienstandHtml}"></td>
-              <td th:utext="${r.geschlechtHtml}"></td>
-              <td th:utext="${r.verwandtschaftHtml}"></td>
-              <td th:utext="${r.sozialstellungHtml}"></td>
-              <td th:utext="${r.istKindHtml}"></td>
-            </tr>
+            <th:block th:each="r : ${mitgliederRows}">
+              <!-- Hauptzeile -->
+              <tr th:class="'person-row ' + ${r.rowClass} + ((${r.personId} == ${personId}) ? ' person-selected' : '')"
+                  th:data-person-id="${r.personId}"
+                  role="button"
+                  tabindex="0"
+                  th:aria-label="'Person auswählen: ' + ${r.nameHtml}"
+                  onkeypress="handleRowKeyPress(event, this)">
+                <td class="expand-col">
+                  <button type="button" 
+                          class="expand-btn" 
+                          th:data-person-id="${r.personId}"
+                          onclick="togglePersonDetailsFromButton(this)"
+                          onkeypress="handleExpandKeyPressFromButton(event, this)"
+                          aria-expanded="false"
+                          th:aria-controls="'person-details-' + ${r.personId}"
+                          th:aria-label="'Details für ' + ${r.nameHtml} + ' anzeigen'">
+                    <span aria-hidden="true">▶</span>
+                  </button>
+                </td>
+                <td class="sym" th:classappend="' ' + ${r.symbolClass}" th:text="${r.symbol}" 
+                    th:aria-label="${r.symbol == '+' ? 'Hinzugefügt' : (r.symbol == '-' ? 'Gelöscht' : (r.symbol == '!' ? 'Geändert' : 'Unverändert'))}"></td>
+                <td th:utext="${r.nameHtml}"></td>
+                <td th:utext="${r.blocknameHtml}"></td>
+                <td th:utext="${r.geburtsdatumHtml}"></td>
+                <td th:utext="${r.sterbedatumHtml}"></td>
+                <td th:utext="${r.familienstandHtml}"></td>
+                <td th:utext="${r.geschlechtHtml}"></td>
+                <td th:utext="${r.anredeHtml}"></td>
+                <td th:utext="${r.verwandtschaftHtml}"></td>
+                <td th:utext="${r.ausschlussgrundHtml}"></td>
+                <td th:utext="${r.sozialstellungHtml}"></td>
+                <td th:utext="${r.aufenthaltstitelEndeHtml}"></td>
+                <td th:utext="${r.antragstellerHtml}"></td>
+                <td th:utext="${r.zahlungsempfaengerHtml}"></td>
+              </tr>
+              
+              <!-- Aufklappbare Detailzeile -->
+              <tr th:id="'person-details-' + ${r.personId}" 
+                  class="person-details-row hidden"
+                  th:aria-labelledby="'person-details-label-' + ${r.personId}">
+                <td colspan="15" class="details-cell">
+                  <div class="details-container">
+                    <table class="details-table">
+                      <tbody>
+                        <tr>
+                          <td class="detail-label">Titel:</td>
+                          <td th:utext="${r.titelHtml}"></td>
+                          <td class="detail-label">Geburtsort:</td>
+                          <td th:utext="${r.geburtsortHtml}"></td>
+                          <td class="detail-label">Telefon:</td>
+                          <td th:utext="${r.telefonHtml}"></td>
+                          <td class="detail-label">Aktenzeichen Soz.:</td>
+                          <td th:utext="${r.aktenzeichenSozialHtml}"></td>
+                        </tr>
+                        <tr>
+                          <td class="detail-label">Geburtsname:</td>
+                          <td th:utext="${r.geburtsnameHtml}"></td>
+                          <td class="detail-label">Alter Name:</td>
+                          <td th:utext="${r.alterNameHtml}"></td>
+                          <td class="detail-label">E-Mail:</td>
+                          <td th:utext="${r.emailHtml}"></td>
+                          <td class="detail-label">Postabrutionsnummer:</td>
+                          <td th:utext="${r.postabrutionsnummerHtml}"></td>
+                        </tr>
+                      </tbody>
+                    </table>
+                  </div>
+                </td>
+              </tr>
+            </th:block>
           </tbody>
         </table>
       </div>
@@ -185,12 +246,11 @@
             <tr>
               <th scope="col">Änderung</th>
               <th scope="col">Art</th>
-              <th scope="col">Frequenz</th>
+              <th scope="col">Faktor</th>
               <th scope="col">Betrag</th>
               <th scope="col">Zuschlag</th>
               <th scope="col">Abschlag</th>
               <th scope="col">Werbungskosten</th>
-              <th scope="col">Berechnungsart</th>
             </tr>
           </thead>
           <tbody>
@@ -198,12 +258,11 @@
               <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"
                   th:aria-label="${z.symbol == '+' ? 'Hinzugefügt' : (z.symbol == '-' ? 'Gelöscht' : (z.symbol == '!' ? 'Geändert' : 'Unverändert'))}"></td>
               <td th:utext="${z.artHtml}"></td>
-              <td th:utext="${z.frequenzHtml}"></td>
+              <td th:utext="${z.faktorHtml}"></td>
               <td th:utext="${z.betragHtml}"></td>
               <td th:utext="${z.zuschlagHtml}"></td>
               <td th:utext="${z.abschlagHtml}"></td>
               <td th:utext="${z.werbungskostenHtml}"></td>
-              <td th:utext="${z.berechnungsartHtml}"></td>
             </tr>
           </tbody>
         </table>
@@ -224,7 +283,7 @@
                 <tr>
                   <th scope="col">Änderung</th>
                   <th scope="col">Art</th>
-                  <th scope="col">Frequenz</th>
+                  <th scope="col">Faktor</th>
                   <th scope="col">Betrag</th>
                 </tr>
               </thead>
@@ -233,7 +292,7 @@
                   <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"
                       th:aria-label="${z.symbol == '+' ? 'Hinzugefügt' : (z.symbol == '-' ? 'Gelöscht' : (z.symbol == '!' ? 'Geändert' : 'Unverändert'))}"></td>
                   <td th:utext="${z.artHtml}"></td>
-                  <td th:utext="${z.frequenzHtml}"></td>
+                  <td th:utext="${z.faktorHtml}"></td>
                   <td th:utext="${z.betragHtml}"></td>
                 </tr>
               </tbody>
@@ -255,7 +314,7 @@
                 <tr>
                   <th scope="col">Änderung</th>
                   <th scope="col">Art</th>
-                  <th scope="col">Frequenz</th>
+                  <th scope="col">Faktor</th>
                   <th scope="col">Betrag</th>
                   <th scope="col">Frist</th>
                 </tr>
@@ -265,7 +324,7 @@
                   <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"
                       th:aria-label="${z.symbol == '+' ? 'Hinzugefügt' : (z.symbol == '-' ? 'Gelöscht' : (z.symbol == '!' ? 'Geändert' : 'Unverändert'))}"></td>
                   <td th:utext="${z.artHtml}"></td>
-                  <td th:utext="${z.frequenzHtml}"></td>
+                  <td th:utext="${z.faktorHtml}"></td>
                   <td th:utext="${z.betragHtml}"></td>
                   <td th:utext="${z.fristHtml}"></td>
                 </tr>
@@ -326,6 +385,55 @@
       return null;
     }
 
+    // Person Details Toggle-Funktionalität
+    function togglePersonDetails(button, personId) {
+      const detailsRow = document.getElementById('person-details-' + personId);
+      const isExpanded = button.getAttribute('aria-expanded') === 'true';
+      
+      if (isExpanded) {
+        // Zuklappen
+        detailsRow.classList.add('hidden');
+        button.setAttribute('aria-expanded', 'false');
+        button.querySelector('span').textContent = '▶';
+        button.setAttribute('aria-label', button.getAttribute('aria-label').replace('ausblenden', 'anzeigen'));
+      } else {
+        // Aufklappen
+        detailsRow.classList.remove('hidden');
+        button.setAttribute('aria-expanded', 'true');
+        button.querySelector('span').textContent = '▼';
+        button.setAttribute('aria-label', button.getAttribute('aria-label').replace('anzeigen', 'ausblenden'));
+      }
+    }
+
+    // New function to handle button clicks using data attribute
+    function togglePersonDetailsFromButton(button) {
+      const personId = button.getAttribute('data-person-id');
+      if (personId) {
+        togglePersonDetails(button, personId);
+      }
+    }
+
+    // Keyboard support for person details toggle
+    function handleExpandKeyPress(event, button, personId) {
+      if (event.key === 'Enter' || event.key === ' ') {
+        event.preventDefault();
+        event.stopPropagation(); // Verhindert, dass die Zeile auch geklickt wird
+        togglePersonDetails(button, personId);
+      }
+    }
+
+    // New function to handle keyboard events using data attribute
+    function handleExpandKeyPressFromButton(event, button) {
+      if (event.key === 'Enter' || event.key === ' ') {
+        event.preventDefault();
+        event.stopPropagation(); // Verhindert, dass die Zeile auch geklickt wird
+        const personId = button.getAttribute('data-person-id');
+        if (personId) {
+          togglePersonDetails(button, personId);
+        }
+      }
+    }
+
     // Keyboard support for toggles
     function handleKeyPress(event, sectionId) {
       if (event.key === 'Enter' || event.key === ' ') {
@@ -337,6 +445,10 @@
     // Keyboard support for table rows
     function handleRowKeyPress(event, row) {
       if (event.key === 'Enter' || event.key === ' ') {
+        // Prüfen ob das Event von einem Expand-Button kommt
+        if (event.target.classList.contains('expand-btn') || event.target.closest('.expand-btn')) {
+          return; // Lass den expand button sein eigenes Event handhaben
+        }
         event.preventDefault();
         row.click();
       }
@@ -465,7 +577,12 @@
       // Add click event listeners to all person rows
       const personRows = document.querySelectorAll('.person-row');
       personRows.forEach(function(row) {
-        row.addEventListener('click', function() {
+        row.addEventListener('click', function(event) {
+          // Prüfen ob das Event von einem Expand-Button kommt
+          if (event.target.classList.contains('expand-btn') || event.target.closest('.expand-btn')) {
+            return; // Button hat sein eigenes Click-Event
+          }
+          
           const personId = this.dataset.personId;
           if (personId) {
             document.getElementById('selectedPersonId').value = personId;