|
@@ -2,9 +2,9 @@ package com.tsystems.inwo.vgl.web;
|
|
|
|
|
|
|
|
import com.fasterxml.jackson.databind.*;
|
|
import com.fasterxml.jackson.databind.*;
|
|
|
import com.fasterxml.jackson.databind.node.*;
|
|
import com.fasterxml.jackson.databind.node.*;
|
|
|
-import com.tsystems.inwo.vgl.service.SchemaValidationService;
|
|
|
|
|
-import com.tsystems.inwo.vgl.util.FormatUtil;
|
|
|
|
|
|
|
+import com.tsystems.inwo.vgl.service.*;
|
|
|
import com.tsystems.inwo.vgl.view.*;
|
|
import com.tsystems.inwo.vgl.view.*;
|
|
|
|
|
+import com.tsystems.inwo.vgl.util.FormatUtil;
|
|
|
import org.springframework.stereotype.Controller;
|
|
import org.springframework.stereotype.Controller;
|
|
|
import org.springframework.ui.Model;
|
|
import org.springframework.ui.Model;
|
|
|
import org.springframework.web.bind.annotation.*;
|
|
import org.springframework.web.bind.annotation.*;
|
|
@@ -16,10 +16,19 @@ import java.util.stream.Collectors;
|
|
|
public class VergleichController {
|
|
public class VergleichController {
|
|
|
private final ObjectMapper om;
|
|
private final ObjectMapper om;
|
|
|
private final SchemaValidationService validator;
|
|
private final SchemaValidationService validator;
|
|
|
-
|
|
|
|
|
- public VergleichController(ObjectMapper om, SchemaValidationService v) {
|
|
|
|
|
|
|
+ private final JsonNavigationService jsonNavigationService;
|
|
|
|
|
+ private final DataComparisonService comparisonService;
|
|
|
|
|
+ private final VergleichDataExtractor dataExtractor;
|
|
|
|
|
+
|
|
|
|
|
+ public VergleichController(ObjectMapper om, SchemaValidationService validator,
|
|
|
|
|
+ JsonNavigationService jsonNavigationService,
|
|
|
|
|
+ DataComparisonService comparisonService,
|
|
|
|
|
+ VergleichDataExtractor dataExtractor) {
|
|
|
this.om = om;
|
|
this.om = om;
|
|
|
- this.validator = v;
|
|
|
|
|
|
|
+ this.validator = validator;
|
|
|
|
|
+ this.jsonNavigationService = jsonNavigationService;
|
|
|
|
|
+ this.comparisonService = comparisonService;
|
|
|
|
|
+ this.dataExtractor = dataExtractor;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@GetMapping("/test")
|
|
@GetMapping("/test")
|
|
@@ -72,20 +81,20 @@ public class VergleichController {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// --- Rechts (Master) Zeitraum wählen ---
|
|
// --- Rechts (Master) Zeitraum wählen ---
|
|
|
- List<Z> zeitrechte = extractZeitraeume(right);
|
|
|
|
|
|
|
+ List<Z> zeitrechte = jsonNavigationService.extractZeitraeume(right);
|
|
|
if (zeitrechte.isEmpty()) throw new IllegalArgumentException("Right JSON hat keine Zeiträume");
|
|
if (zeitrechte.isEmpty()) throw new IllegalArgumentException("Right JSON hat keine Zeiträume");
|
|
|
if (rightZeitraumId == null) rightZeitraumId = zeitrechte.get(0).id;
|
|
if (rightZeitraumId == null) rightZeitraumId = zeitrechte.get(0).id;
|
|
|
- JsonNode rightZeitraum = findZeitraumById(right, rightZeitraumId);
|
|
|
|
|
|
|
+ JsonNode rightZeitraum = jsonNavigationService.findZeitraumById(right, rightZeitraumId);
|
|
|
|
|
|
|
|
// --- Find the stammvorgang that contains the selected zeitraum ---
|
|
// --- Find the stammvorgang that contains the selected zeitraum ---
|
|
|
- JsonNode stammvorgangJsonNode = findStammvorgangByZeitraumId(right, rightZeitraumId);
|
|
|
|
|
- StammvorgangInfo stammvorgangRight = createStammvorgangInfo(stammvorgangJsonNode);
|
|
|
|
|
|
|
+ JsonNode stammvorgangJsonNode = jsonNavigationService.findStammvorgangByZeitraumId(right, rightZeitraumId);
|
|
|
|
|
+ StammvorgangInfo stammvorgangRight = jsonNavigationService.createStammvorgangInfo(stammvorgangJsonNode);
|
|
|
|
|
|
|
|
// --- Extract stammakte info if available ---
|
|
// --- Extract stammakte info if available ---
|
|
|
- StammakteInfo stammakteRight = extractStammakteInfo(right);
|
|
|
|
|
|
|
+ StammakteInfo stammakteRight = jsonNavigationService.extractStammakteInfo(right);
|
|
|
|
|
|
|
|
// --- Links (Baseline) Kandidaten & Auswahl ---
|
|
// --- Links (Baseline) Kandidaten & Auswahl ---
|
|
|
- List<Vorgang> leftCand = parseLeftCandidates(left);
|
|
|
|
|
|
|
+ List<Vorgang> leftCand = jsonNavigationService.parseLeftCandidates(left);
|
|
|
List<Z> leftZ = List.of();
|
|
List<Z> leftZ = List.of();
|
|
|
JsonNode leftZeitraum = null;
|
|
JsonNode leftZeitraum = null;
|
|
|
if (!leftCand.isEmpty()) {
|
|
if (!leftCand.isEmpty()) {
|
|
@@ -93,19 +102,21 @@ public class VergleichController {
|
|
|
final String finalLeftVorgangId = leftVorgangId;
|
|
final String finalLeftVorgangId = leftVorgangId;
|
|
|
leftZ = leftCand.stream().filter(v -> v.id.equals(finalLeftVorgangId)).findFirst().orElse(leftCand.get(0)).zeitraeume;
|
|
leftZ = leftCand.stream().filter(v -> v.id.equals(finalLeftVorgangId)).findFirst().orElse(leftCand.get(0)).zeitraeume;
|
|
|
if (leftZeitraumId == null && !leftZ.isEmpty()) leftZeitraumId = leftZ.get(0).id;
|
|
if (leftZeitraumId == null && !leftZ.isEmpty()) leftZeitraumId = leftZ.get(0).id;
|
|
|
- if (leftZeitraumId != null) leftZeitraum = findZeitraumById(getRootOfVorgang(left, leftVorgangId), leftZeitraumId);
|
|
|
|
|
|
|
+ if (leftZeitraumId != null) leftZeitraum = jsonNavigationService.findZeitraumById(jsonNavigationService.getRootOfVorgang(left, leftVorgangId), leftZeitraumId);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// --- Personenliste rechts + Default personId ---
|
|
// --- Personenliste rechts + Default personId ---
|
|
|
- List<PersonRef> personenRight = listRightPersons(rightZeitraum);
|
|
|
|
|
|
|
+ List<PersonRef> personenRight = jsonNavigationService.listRightPersons(rightZeitraum);
|
|
|
if (personId == null && !personenRight.isEmpty()) personId = personenRight.get(0).id;
|
|
if (personId == null && !personenRight.isEmpty()) personId = personenRight.get(0).id;
|
|
|
|
|
|
|
|
// --- Personen-Tabelle (UNION via person.id) ---
|
|
// --- Personen-Tabelle (UNION via person.id) ---
|
|
|
- Map<String, Pair<JsonNode, JsonNode>> personenPairs = unionByKey(
|
|
|
|
|
- personsArray(rightZeitraum), personsArray(leftZeitraum), n -> n.path("person").path("id").asText()
|
|
|
|
|
|
|
+ Map<String, Pair<JsonNode, JsonNode>> personenPairs = comparisonService.unionByKey(
|
|
|
|
|
+ jsonNavigationService.personsArray(rightZeitraum),
|
|
|
|
|
+ jsonNavigationService.personsArray(leftZeitraum),
|
|
|
|
|
+ n -> n.path("person").path("id").asText()
|
|
|
);
|
|
);
|
|
|
List<MitgliedRow> mitgliederRows = personenPairs.entrySet().stream()
|
|
List<MitgliedRow> mitgliederRows = personenPairs.entrySet().stream()
|
|
|
- .map(e -> buildMitgliedRow(e.getKey(), e.getValue().right, e.getValue().left))
|
|
|
|
|
|
|
+ .map(e -> dataExtractor.buildMitgliedRow(e.getKey(), e.getValue().right, e.getValue().left))
|
|
|
.sorted(Comparator.comparing(MitgliedRow::personId))
|
|
.sorted(Comparator.comparing(MitgliedRow::personId))
|
|
|
.toList();
|
|
.toList();
|
|
|
|
|
|
|
@@ -119,9 +130,9 @@ public class VergleichController {
|
|
|
Map<String, String> personNameByIdRight = personenRight.stream().collect(Collectors.toMap(p -> p.id, p -> p.label));
|
|
Map<String, String> personNameByIdRight = personenRight.stream().collect(Collectors.toMap(p -> p.id, p -> p.label));
|
|
|
|
|
|
|
|
// Einkommen / Freibeträge / Kita: jeweils UNION per id
|
|
// Einkommen / Freibeträge / Kita: jeweils UNION per id
|
|
|
- List<EinkommenZeile> einkommenZeilen = buildEinkommenZeilen(rightPerson, leftPerson);
|
|
|
|
|
- List<FreibetragZeile> freibetragZeilen = buildFreibetragZeilen(rightPerson, leftPerson);
|
|
|
|
|
- List<KitaZeile> kitaZeilen = buildKitaZeilen(rightPerson, leftPerson, personNameByIdRight);
|
|
|
|
|
|
|
+ List<EinkommenZeile> einkommenZeilen = dataExtractor.buildEinkommenZeilen(rightPerson, leftPerson);
|
|
|
|
|
+ List<FreibetragZeile> freibetragZeilen = dataExtractor.buildFreibetragZeilen(rightPerson, leftPerson);
|
|
|
|
|
+ List<KitaZeile> kitaZeilen = dataExtractor.buildKitaZeilen(rightPerson, leftPerson, personNameByIdRight);
|
|
|
|
|
|
|
|
// --- Model füllen & rendern ---
|
|
// --- Model füllen & rendern ---
|
|
|
model.addAttribute("leftJson", leftJson);
|
|
model.addAttribute("leftJson", leftJson);
|
|
@@ -161,852 +172,4 @@ public class VergleichController {
|
|
|
left = l;
|
|
left = l;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- // ===== Helper-Methoden =====
|
|
|
|
|
- private static StammvorgangInfo createStammvorgangInfo(JsonNode stammvorgangNode) {
|
|
|
|
|
- if (stammvorgangNode == null || stammvorgangNode.isMissingNode()) {
|
|
|
|
|
- return new StammvorgangInfo("", "", "");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- String id = stammvorgangNode.path("id").asText("");
|
|
|
|
|
- String vorgangstyp = stammvorgangNode.path("vorgangstyp").asText("");
|
|
|
|
|
- String vorgangsstatus = stammvorgangNode.path("vorgangsstatus").asText("");
|
|
|
|
|
-
|
|
|
|
|
- return new StammvorgangInfo(id, vorgangstyp, vorgangsstatus);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static StammakteInfo extractStammakteInfo(JsonNode root) {
|
|
|
|
|
- JsonNode stammakte = root.path("stammakte");
|
|
|
|
|
- if (stammakte.isMissingNode()) {
|
|
|
|
|
- return new StammakteInfo("", "", "", "");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- String aktenzeichen = stammakte.path("aktenzeichen").asText("");
|
|
|
|
|
- String antragstellername = stammakte.path("antragstellername").asText("");
|
|
|
|
|
- String antragstellerGeburtsdatum = stammakte.path("antragstellerGeburtsdatum").asText("");
|
|
|
|
|
-
|
|
|
|
|
- JsonNode anschrift = stammakte.path("anschrift");
|
|
|
|
|
- String anschriftStr = "";
|
|
|
|
|
- if (!anschrift.isMissingNode()) {
|
|
|
|
|
- StringBuilder sb = new StringBuilder();
|
|
|
|
|
- String strasse = anschrift.path("strasse").asText("");
|
|
|
|
|
- String hausnummer = anschrift.path("hausnummer").asText("");
|
|
|
|
|
- String zusatz = anschrift.path("zusatz").asText("");
|
|
|
|
|
- String plz = anschrift.path("plz").asText("");
|
|
|
|
|
- String ort = anschrift.path("ort").asText("");
|
|
|
|
|
-
|
|
|
|
|
- if (!strasse.isEmpty()) sb.append(strasse);
|
|
|
|
|
- if (!hausnummer.isEmpty()) sb.append(" ").append(hausnummer);
|
|
|
|
|
- if (!zusatz.isEmpty()) sb.append(" ").append(zusatz);
|
|
|
|
|
- if (!plz.isEmpty() || !ort.isEmpty()) {
|
|
|
|
|
- if (sb.length() > 0) sb.append(", ");
|
|
|
|
|
- if (!plz.isEmpty()) sb.append(plz);
|
|
|
|
|
- if (!ort.isEmpty()) sb.append(" ").append(ort);
|
|
|
|
|
- }
|
|
|
|
|
- anschriftStr = sb.toString();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return new StammakteInfo(aktenzeichen, antragstellername, antragstellerGeburtsdatum, anschriftStr);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static List<Z> extractZeitraeume(JsonNode root) {
|
|
|
|
|
- List<Z> result = new ArrayList<>();
|
|
|
|
|
-
|
|
|
|
|
- // Check if this is the new stammakte structure
|
|
|
|
|
- if (root.has("stammakte")) {
|
|
|
|
|
- JsonNode stammakte = root.get("stammakte");
|
|
|
|
|
- if (stammakte.has("vorgaenge") && stammakte.get("vorgaenge").isArray()) {
|
|
|
|
|
- for (JsonNode vorgang : stammakte.get("vorgaenge")) {
|
|
|
|
|
- if (vorgang.has("zeitraeume") && vorgang.get("zeitraeume").isArray()) {
|
|
|
|
|
- for (JsonNode zeitraum : vorgang.get("zeitraeume")) {
|
|
|
|
|
- String id = zeitraum.path("id").asText("");
|
|
|
|
|
- String beginn = zeitraum.path("beginn").asText("");
|
|
|
|
|
- String ende = zeitraum.path("ende").asText("");
|
|
|
|
|
- result.add(new Z(id, beginn, ende));
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- // Check if this is the vorgaenge structure
|
|
|
|
|
- else if (root.has("vorgaenge") && root.get("vorgaenge").isArray()) {
|
|
|
|
|
- // For vorgaenge structure, collect zeitraeume from all vorgaenge
|
|
|
|
|
- for (JsonNode vorgang : root.get("vorgaenge")) {
|
|
|
|
|
- if (vorgang.has("zeitraeume") && vorgang.get("zeitraeume").isArray()) {
|
|
|
|
|
- for (JsonNode zeitraum : vorgang.get("zeitraeume")) {
|
|
|
|
|
- String id = zeitraum.path("id").asText("");
|
|
|
|
|
- String beginn = zeitraum.path("beginn").asText("");
|
|
|
|
|
- String ende = zeitraum.path("ende").asText("");
|
|
|
|
|
- result.add(new Z(id, beginn, ende));
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- } else if (root.has("zeitraeume") && root.get("zeitraeume").isArray()) {
|
|
|
|
|
- // Legacy single vorgang structure
|
|
|
|
|
- for (JsonNode zeitraum : root.get("zeitraeume")) {
|
|
|
|
|
- String id = zeitraum.path("id").asText("");
|
|
|
|
|
- String beginn = zeitraum.path("beginn").asText("");
|
|
|
|
|
- String ende = zeitraum.path("ende").asText("");
|
|
|
|
|
- result.add(new Z(id, beginn, ende));
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return result;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static JsonNode findZeitraumById(JsonNode root, String id) {
|
|
|
|
|
- // Check if this is the new stammakte structure
|
|
|
|
|
- if (root.has("stammakte")) {
|
|
|
|
|
- JsonNode stammakte = root.get("stammakte");
|
|
|
|
|
- if (stammakte.has("vorgaenge") && stammakte.get("vorgaenge").isArray()) {
|
|
|
|
|
- for (JsonNode vorgang : stammakte.get("vorgaenge")) {
|
|
|
|
|
- if (vorgang.has("zeitraeume") && vorgang.get("zeitraeume").isArray()) {
|
|
|
|
|
- for (JsonNode zeitraum : vorgang.get("zeitraeume")) {
|
|
|
|
|
- if (id.equals(zeitraum.path("id").asText(""))) {
|
|
|
|
|
- return zeitraum;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- // Check if this is the vorgaenge structure
|
|
|
|
|
- else if (root.has("vorgaenge") && root.get("vorgaenge").isArray()) {
|
|
|
|
|
- // Search in all vorgaenge
|
|
|
|
|
- for (JsonNode vorgang : root.get("vorgaenge")) {
|
|
|
|
|
- if (vorgang.has("zeitraeume") && vorgang.get("zeitraeume").isArray()) {
|
|
|
|
|
- for (JsonNode zeitraum : vorgang.get("zeitraeume")) {
|
|
|
|
|
- if (id.equals(zeitraum.path("id").asText(""))) {
|
|
|
|
|
- return zeitraum;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- } else if (root.has("zeitraeume") && root.get("zeitraeume").isArray()) {
|
|
|
|
|
- // Legacy single vorgang structure
|
|
|
|
|
- for (JsonNode zeitraum : root.get("zeitraeume")) {
|
|
|
|
|
- if (id.equals(zeitraum.path("id").asText(""))) {
|
|
|
|
|
- return zeitraum;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return NullNode.getInstance();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static JsonNode findStammvorgangByZeitraumId(JsonNode root, String zeitraumId) {
|
|
|
|
|
- // Check if this is the new stammakte structure
|
|
|
|
|
- if (root.has("stammakte")) {
|
|
|
|
|
- JsonNode stammakte = root.get("stammakte");
|
|
|
|
|
- if (stammakte.has("vorgaenge") && stammakte.get("vorgaenge").isArray()) {
|
|
|
|
|
- for (JsonNode vorgang : stammakte.get("vorgaenge")) {
|
|
|
|
|
- if (vorgang.has("zeitraeume") && vorgang.get("zeitraeume").isArray()) {
|
|
|
|
|
- for (JsonNode zeitraum : vorgang.get("zeitraeume")) {
|
|
|
|
|
- if (zeitraumId.equals(zeitraum.path("id").asText(""))) {
|
|
|
|
|
- return vorgang.path("stammvorgang");
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- // Check if this is the vorgaenge structure
|
|
|
|
|
- else if (root.has("vorgaenge") && root.get("vorgaenge").isArray()) {
|
|
|
|
|
- // Search in all vorgaenge for the one containing this zeitraum
|
|
|
|
|
- for (JsonNode vorgang : root.get("vorgaenge")) {
|
|
|
|
|
- if (vorgang.has("zeitraeume") && vorgang.get("zeitraeume").isArray()) {
|
|
|
|
|
- for (JsonNode zeitraum : vorgang.get("zeitraeume")) {
|
|
|
|
|
- if (zeitraumId.equals(zeitraum.path("id").asText(""))) {
|
|
|
|
|
- return vorgang.path("stammvorgang");
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- // Legacy single vorgang structure - stammvorgang is at root level
|
|
|
|
|
- return root.path("stammvorgang");
|
|
|
|
|
- }
|
|
|
|
|
- return NullNode.getInstance();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static List<Vorgang> parseLeftCandidates(JsonNode left) {
|
|
|
|
|
- List<Vorgang> result = new ArrayList<>();
|
|
|
|
|
-
|
|
|
|
|
- // Check if this is the new stammakte structure
|
|
|
|
|
- if (left.has("stammakte")) {
|
|
|
|
|
- JsonNode stammakte = left.get("stammakte");
|
|
|
|
|
- if (stammakte.has("vorgaenge") && stammakte.get("vorgaenge").isArray()) {
|
|
|
|
|
- for (JsonNode v : stammakte.get("vorgaenge")) {
|
|
|
|
|
- JsonNode stammvorgang = v.path("stammvorgang");
|
|
|
|
|
- String id = stammvorgang.path("id").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, vorgangstyp, zeitraeume));
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- else if (left.has("vorgaenge") && left.get("vorgaenge").isArray()) {
|
|
|
|
|
- // a) wenn left.vorgaenge Array
|
|
|
|
|
- for (JsonNode v : left.get("vorgaenge")) {
|
|
|
|
|
- JsonNode stammvorgang = v.path("stammvorgang");
|
|
|
|
|
- String id = stammvorgang.path("id").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, vorgangstyp, zeitraeume));
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- // b) sonst: single vorgang
|
|
|
|
|
- JsonNode stammvorgang = left.path("stammvorgang");
|
|
|
|
|
- String id = stammvorgang.path("id").asText("");
|
|
|
|
|
- String vorgangstyp = stammvorgang.path("vorgangstyp").asText("");
|
|
|
|
|
- List<Z> zeitraeume = extractZeitraeume(left);
|
|
|
|
|
- result.add(new Vorgang(id, vorgangstyp, zeitraeume));
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return result;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static JsonNode getRootOfVorgang(JsonNode left, String vorgangId) {
|
|
|
|
|
- // Check if this is the new stammakte structure
|
|
|
|
|
- if (left.has("stammakte")) {
|
|
|
|
|
- JsonNode stammakte = left.get("stammakte");
|
|
|
|
|
- if (stammakte.has("vorgaenge") && stammakte.get("vorgaenge").isArray()) {
|
|
|
|
|
- for (JsonNode v : stammakte.get("vorgaenge")) {
|
|
|
|
|
- if (vorgangId.equals(v.path("stammvorgang").path("id").asText(""))) {
|
|
|
|
|
- return v;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- else if (left.has("vorgaenge") && left.get("vorgaenge").isArray()) {
|
|
|
|
|
- for (JsonNode v : left.get("vorgaenge")) {
|
|
|
|
|
- if (vorgangId.equals(v.path("stammvorgang").path("id").asText(""))) {
|
|
|
|
|
- return v;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return left;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static List<PersonRef> listRightPersons(JsonNode zeitraum) {
|
|
|
|
|
- List<PersonRef> result = new ArrayList<>();
|
|
|
|
|
- if (zeitraum != null && zeitraum.has("personen") && zeitraum.get("personen").isArray()) {
|
|
|
|
|
- for (JsonNode personImZeitraum : zeitraum.get("personen")) {
|
|
|
|
|
- JsonNode person = personImZeitraum.path("person");
|
|
|
|
|
- String id = person.path("id").asText("");
|
|
|
|
|
- String nachname = person.path("nachname").asText("");
|
|
|
|
|
- String vorname = person.path("vorname").asText("");
|
|
|
|
|
- String geburtsdatum = person.path("geburtsdatum").asText("");
|
|
|
|
|
-
|
|
|
|
|
- String label = nachname + ", " + vorname;
|
|
|
|
|
- if (!geburtsdatum.isEmpty()) {
|
|
|
|
|
- label += " (" + FormatUtil.date(geburtsdatum) + ")";
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- result.add(new PersonRef(id, label));
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return result;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static ArrayNode personsArray(JsonNode zeitraum) {
|
|
|
|
|
- return zeitraum != null && zeitraum.has("personen") && zeitraum.get("personen").isArray()
|
|
|
|
|
- ? (ArrayNode) zeitraum.get("personen")
|
|
|
|
|
- : new ObjectMapper().createArrayNode();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static <T> Map<String, Pair<JsonNode, JsonNode>> unionByKey(ArrayNode rightArr, ArrayNode leftArr, Function<JsonNode, String> keyFn) {
|
|
|
|
|
- Map<String, Pair<JsonNode, JsonNode>> result = new LinkedHashMap<>();
|
|
|
|
|
-
|
|
|
|
|
- // Add all right items
|
|
|
|
|
- for (JsonNode node : rightArr) {
|
|
|
|
|
- String key = keyFn.apply(node);
|
|
|
|
|
- result.put(key, new Pair<>(node, null));
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Merge left items
|
|
|
|
|
- for (JsonNode node : leftArr) {
|
|
|
|
|
- String key = keyFn.apply(node);
|
|
|
|
|
- result.merge(key, new Pair<>(null, node), (existing, newPair) ->
|
|
|
|
|
- new Pair<>(existing.right, newPair.left));
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return result;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static MitgliedRow buildMitgliedRow(String personId, JsonNode rightItem, JsonNode leftItem) {
|
|
|
|
|
- // Extract person data from both items
|
|
|
|
|
- JsonNode rightPerson = rightItem != null ? rightItem.path("person") : null;
|
|
|
|
|
- JsonNode leftPerson = leftItem != null ? leftItem.path("person") : null;
|
|
|
|
|
-
|
|
|
|
|
- // Determine status and detect changes
|
|
|
|
|
- String symbol = "";
|
|
|
|
|
- String symbolClass = "";
|
|
|
|
|
- String rowClass = "";
|
|
|
|
|
- boolean hasChanges = false;
|
|
|
|
|
-
|
|
|
|
|
- if (rightItem != null && leftItem != null) {
|
|
|
|
|
- // Both exist - check for changes
|
|
|
|
|
- hasChanges = detectPersonChanges(rightItem, rightPerson, leftItem, leftPerson);
|
|
|
|
|
- if (hasChanges) {
|
|
|
|
|
- symbol = symFor("CHANGED");
|
|
|
|
|
- symbolClass = symClassFor("CHANGED");
|
|
|
|
|
- rowClass = "";
|
|
|
|
|
- }
|
|
|
|
|
- } else if (rightItem != null && leftItem == null) {
|
|
|
|
|
- symbol = symFor("ADDED");
|
|
|
|
|
- symbolClass = symClassFor("ADDED");
|
|
|
|
|
- rowClass = rowClassFor("ADDED");
|
|
|
|
|
- } else if (rightItem == null && leftItem != null) {
|
|
|
|
|
- symbol = symFor("REMOVED");
|
|
|
|
|
- symbolClass = symClassFor("REMOVED");
|
|
|
|
|
- rowClass = rowClassFor("REMOVED");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Use right (current) data as primary, fall back to left for missing data
|
|
|
|
|
- JsonNode primaryPerson = rightPerson != null ? rightPerson : leftPerson;
|
|
|
|
|
- JsonNode primaryItem = rightItem != null ? rightItem : leftItem;
|
|
|
|
|
-
|
|
|
|
|
- if (primaryPerson == null) {
|
|
|
|
|
- return new MitgliedRow(
|
|
|
|
|
- personId, symbol, symbolClass, rowClass,
|
|
|
|
|
- "", "", "", "", "", "", "", "", "", "", "",
|
|
|
|
|
- "", "", "", "", "", "", "", "", "", ""
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 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(""),
|
|
|
|
|
- hasChanges
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- String geschlechtHtml = buildPersonFieldHtml(
|
|
|
|
|
- rightPerson, leftPerson,
|
|
|
|
|
- p -> p.path("geschlecht").asText(""),
|
|
|
|
|
- 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("verwandtschaftsverhaeltnis").asText(""),
|
|
|
|
|
- hasChanges
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- 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 antragstellerHtml = buildItemFieldHtml(
|
|
|
|
|
- rightItem, leftItem,
|
|
|
|
|
- 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, blocknameHtml, geburtsdatumHtml, sterbedatumHtml,
|
|
|
|
|
- familienstandHtml, geschlechtHtml, anredeHtml,
|
|
|
|
|
- verwandtschaftHtml, ausschlussgrundHtml, sozialstellungHtml, aufenthaltstitelEndeHtml,
|
|
|
|
|
- antragstellerHtml, zahlungsempfaengerHtml,
|
|
|
|
|
- titelHtml, geburtsortHtml, telefonHtml, aktenzeichenSozialHtml,
|
|
|
|
|
- geburtsnameHtml, alterNameHtml, emailHtml, postabrutionsnummerHtml
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static boolean detectPersonChanges(JsonNode rightItem, JsonNode rightPerson, JsonNode leftItem, JsonNode leftPerson) {
|
|
|
|
|
- if (rightPerson == null || leftPerson == null) return false;
|
|
|
|
|
-
|
|
|
|
|
- // Check person fields
|
|
|
|
|
- return !Objects.equals(rightPerson.path("nachname").asText(""), leftPerson.path("nachname").asText("")) ||
|
|
|
|
|
- !Objects.equals(rightPerson.path("vorname").asText(""), leftPerson.path("vorname").asText("")) ||
|
|
|
|
|
- !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("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) {
|
|
|
|
|
- if (!hasChanges || rightPerson == null) {
|
|
|
|
|
- JsonNode primary = rightPerson != null ? rightPerson : leftPerson;
|
|
|
|
|
- return primary != null ? escape(extractor.apply(primary)) : "";
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- String rightVal = rightPerson != null ? extractor.apply(rightPerson) : "";
|
|
|
|
|
- String leftVal = leftPerson != null ? extractor.apply(leftPerson) : "";
|
|
|
|
|
-
|
|
|
|
|
- if (!Objects.equals(rightVal, leftVal)) {
|
|
|
|
|
- return cellHtml(rightVal, leftVal, true);
|
|
|
|
|
- } else {
|
|
|
|
|
- return escape(rightVal);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static String buildItemFieldHtml(JsonNode rightItem, JsonNode leftItem, Function<JsonNode, String> extractor, boolean hasChanges) {
|
|
|
|
|
- if (!hasChanges || rightItem == null) {
|
|
|
|
|
- JsonNode primary = rightItem != null ? rightItem : leftItem;
|
|
|
|
|
- return primary != null ? escape(extractor.apply(primary)) : "";
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- String rightVal = rightItem != null ? extractor.apply(rightItem) : "";
|
|
|
|
|
- String leftVal = leftItem != null ? extractor.apply(leftItem) : "";
|
|
|
|
|
-
|
|
|
|
|
- if (!Objects.equals(rightVal, leftVal)) {
|
|
|
|
|
- return cellHtml(rightVal, leftVal, true);
|
|
|
|
|
- } else {
|
|
|
|
|
- return escape(rightVal);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static List<EinkommenZeile> buildEinkommenZeilen(JsonNode rightPerson, JsonNode leftPerson) {
|
|
|
|
|
- // Extract einkommen arrays from both persons
|
|
|
|
|
- ArrayNode rightEinkommen = rightPerson != null && rightPerson.has("einkommen") && rightPerson.get("einkommen").isArray()
|
|
|
|
|
- ? (ArrayNode) rightPerson.get("einkommen")
|
|
|
|
|
- : new ObjectMapper().createArrayNode();
|
|
|
|
|
- ArrayNode leftEinkommen = leftPerson != null && leftPerson.has("einkommen") && leftPerson.get("einkommen").isArray()
|
|
|
|
|
- ? (ArrayNode) leftPerson.get("einkommen")
|
|
|
|
|
- : new ObjectMapper().createArrayNode();
|
|
|
|
|
-
|
|
|
|
|
- // UNION über einkommen[].id
|
|
|
|
|
- Map<String, Pair<JsonNode, JsonNode>> einkommenPairs = unionByKey(
|
|
|
|
|
- rightEinkommen, leftEinkommen, n -> n.path("id").asText()
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- List<EinkommenZeile> result = new ArrayList<>();
|
|
|
|
|
- for (Map.Entry<String, Pair<JsonNode, JsonNode>> entry : einkommenPairs.entrySet()) {
|
|
|
|
|
- JsonNode rightItem = entry.getValue().right;
|
|
|
|
|
- JsonNode leftItem = entry.getValue().left;
|
|
|
|
|
-
|
|
|
|
|
- // Determine status and changes
|
|
|
|
|
- String symbol = "";
|
|
|
|
|
- String symbolClass = "";
|
|
|
|
|
- String rowClass = "";
|
|
|
|
|
- boolean hasChanges = false;
|
|
|
|
|
-
|
|
|
|
|
- if (rightItem != null && leftItem != null) {
|
|
|
|
|
- hasChanges = detectEinkommenChanges(rightItem, leftItem);
|
|
|
|
|
- if (hasChanges) {
|
|
|
|
|
- symbol = symFor("CHANGED");
|
|
|
|
|
- symbolClass = symClassFor("CHANGED");
|
|
|
|
|
- }
|
|
|
|
|
- } else if (rightItem != null && leftItem == null) {
|
|
|
|
|
- symbol = symFor("ADDED");
|
|
|
|
|
- symbolClass = symClassFor("ADDED");
|
|
|
|
|
- rowClass = rowClassFor("ADDED");
|
|
|
|
|
- } else if (rightItem == null && leftItem != null) {
|
|
|
|
|
- symbol = symFor("REMOVED");
|
|
|
|
|
- symbolClass = symClassFor("REMOVED");
|
|
|
|
|
- rowClass = rowClassFor("REMOVED");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Use primary data (right first, then left)
|
|
|
|
|
- JsonNode primaryItem = rightItem != null ? rightItem : leftItem;
|
|
|
|
|
-
|
|
|
|
|
- if (primaryItem != null) {
|
|
|
|
|
- String artHtml = buildEinkommenFieldHtml(rightItem, leftItem,
|
|
|
|
|
- i -> {
|
|
|
|
|
- String art = i.path("art").asText("");
|
|
|
|
|
- String artZusatz = i.path("artZusatz").asText("");
|
|
|
|
|
- return !artZusatz.isEmpty() ? art + " (" + artZusatz + ")" : art;
|
|
|
|
|
- }, hasChanges);
|
|
|
|
|
-
|
|
|
|
|
- String faktorHtml = buildEinkommenFieldHtml(rightItem, leftItem,
|
|
|
|
|
- i -> {
|
|
|
|
|
- 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,
|
|
|
|
|
- i -> String.format("%.2f €", i.path("betrag").asDouble(0.0)), hasChanges);
|
|
|
|
|
-
|
|
|
|
|
- String zuschlagHtml = buildEinkommenFieldHtml(rightItem, leftItem,
|
|
|
|
|
- i -> {
|
|
|
|
|
- double zuschlag = i.path("zuschlag").asDouble(0.0);
|
|
|
|
|
- return zuschlag > 0 ? String.format("%.2f €", zuschlag) : "";
|
|
|
|
|
- }, hasChanges);
|
|
|
|
|
-
|
|
|
|
|
- String abschlagHtml = buildEinkommenFieldHtml(rightItem, leftItem,
|
|
|
|
|
- i -> {
|
|
|
|
|
- double abschlag = i.path("abschlag").asDouble(0.0);
|
|
|
|
|
- return abschlag > 0 ? String.format("%.2f €", abschlag) : "";
|
|
|
|
|
- }, hasChanges);
|
|
|
|
|
-
|
|
|
|
|
- String werbungskostenHtml = buildEinkommenFieldHtml(rightItem, leftItem,
|
|
|
|
|
- i -> {
|
|
|
|
|
- double werbungskosten = i.path("werbungskosten").asDouble(0.0);
|
|
|
|
|
- return werbungskosten > 0 ? String.format("%.2f €", werbungskosten) : "";
|
|
|
|
|
- }, hasChanges);
|
|
|
|
|
-
|
|
|
|
|
- result.add(new EinkommenZeile(
|
|
|
|
|
- symbol, symbolClass, rowClass,
|
|
|
|
|
- artHtml, faktorHtml, betragHtml,
|
|
|
|
|
- zuschlagHtml, abschlagHtml, werbungskostenHtml
|
|
|
|
|
- ));
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return result;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- 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("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);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static String buildEinkommenFieldHtml(JsonNode rightItem, JsonNode leftItem, Function<JsonNode, String> extractor, boolean hasChanges) {
|
|
|
|
|
- if (!hasChanges || rightItem == null) {
|
|
|
|
|
- JsonNode primary = rightItem != null ? rightItem : leftItem;
|
|
|
|
|
- return primary != null ? escape(extractor.apply(primary)) : "";
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- String rightVal = rightItem != null ? extractor.apply(rightItem) : "";
|
|
|
|
|
- String leftVal = leftItem != null ? extractor.apply(leftItem) : "";
|
|
|
|
|
-
|
|
|
|
|
- if (!Objects.equals(rightVal, leftVal)) {
|
|
|
|
|
- return cellHtml(rightVal, leftVal, true);
|
|
|
|
|
- } else {
|
|
|
|
|
- return escape(rightVal);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static List<FreibetragZeile> buildFreibetragZeilen(JsonNode rightPerson, JsonNode leftPerson) {
|
|
|
|
|
- // Extract freibetraege arrays from both persons
|
|
|
|
|
- ArrayNode rightFreibetraege = rightPerson != null && rightPerson.has("freibetraege") && rightPerson.get("freibetraege").isArray()
|
|
|
|
|
- ? (ArrayNode) rightPerson.get("freibetraege")
|
|
|
|
|
- : new ObjectMapper().createArrayNode();
|
|
|
|
|
- ArrayNode leftFreibetraege = leftPerson != null && leftPerson.has("freibetraege") && leftPerson.get("freibetraege").isArray()
|
|
|
|
|
- ? (ArrayNode) leftPerson.get("freibetraege")
|
|
|
|
|
- : new ObjectMapper().createArrayNode();
|
|
|
|
|
-
|
|
|
|
|
- // UNION über freibetraege[].id
|
|
|
|
|
- Map<String, Pair<JsonNode, JsonNode>> freibetragPairs = unionByKey(
|
|
|
|
|
- rightFreibetraege, leftFreibetraege, n -> n.path("id").asText()
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- List<FreibetragZeile> result = new ArrayList<>();
|
|
|
|
|
- for (Map.Entry<String, Pair<JsonNode, JsonNode>> entry : freibetragPairs.entrySet()) {
|
|
|
|
|
- JsonNode rightItem = entry.getValue().right;
|
|
|
|
|
- JsonNode leftItem = entry.getValue().left;
|
|
|
|
|
-
|
|
|
|
|
- // Determine status and changes
|
|
|
|
|
- String symbol = "";
|
|
|
|
|
- String symbolClass = "";
|
|
|
|
|
- String rowClass = "";
|
|
|
|
|
- boolean hasChanges = false;
|
|
|
|
|
-
|
|
|
|
|
- if (rightItem != null && leftItem != null) {
|
|
|
|
|
- hasChanges = detectFreibetragChanges(rightItem, leftItem);
|
|
|
|
|
- if (hasChanges) {
|
|
|
|
|
- symbol = symFor("CHANGED");
|
|
|
|
|
- symbolClass = symClassFor("CHANGED");
|
|
|
|
|
- }
|
|
|
|
|
- } else if (rightItem != null && leftItem == null) {
|
|
|
|
|
- symbol = symFor("ADDED");
|
|
|
|
|
- symbolClass = symClassFor("ADDED");
|
|
|
|
|
- rowClass = rowClassFor("ADDED");
|
|
|
|
|
- } else if (rightItem == null && leftItem != null) {
|
|
|
|
|
- symbol = symFor("REMOVED");
|
|
|
|
|
- symbolClass = symClassFor("REMOVED");
|
|
|
|
|
- rowClass = rowClassFor("REMOVED");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Use primary data (right first, then left)
|
|
|
|
|
- JsonNode primaryItem = rightItem != null ? rightItem : leftItem;
|
|
|
|
|
-
|
|
|
|
|
- if (primaryItem != null) {
|
|
|
|
|
- String artHtml = buildFreibetragFieldHtml(rightItem, leftItem,
|
|
|
|
|
- i -> i.path("art").asText(""), hasChanges);
|
|
|
|
|
-
|
|
|
|
|
- String frequenzHtml = buildFreibetragFieldHtml(rightItem, leftItem,
|
|
|
|
|
- i -> {
|
|
|
|
|
- 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,
|
|
|
|
|
- i -> String.format("%.2f €", i.path("betrag").asDouble(0.0)), hasChanges);
|
|
|
|
|
-
|
|
|
|
|
- String fristHtml = buildFreibetragFieldHtml(rightItem, leftItem,
|
|
|
|
|
- i -> {
|
|
|
|
|
- String fristDatum = i.path("fristDatum").asText("");
|
|
|
|
|
- return !fristDatum.isEmpty() ? FormatUtil.date(fristDatum) : "";
|
|
|
|
|
- }, hasChanges);
|
|
|
|
|
-
|
|
|
|
|
- String typ = primaryItem.path("typ").asText("F"); // Default to "F" if not specified
|
|
|
|
|
-
|
|
|
|
|
- result.add(new FreibetragZeile(
|
|
|
|
|
- symbol, symbolClass, rowClass,
|
|
|
|
|
- typ, artHtml, frequenzHtml, betragHtml, fristHtml
|
|
|
|
|
- ));
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return result;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static boolean detectFreibetragChanges(JsonNode rightItem, JsonNode leftItem) {
|
|
|
|
|
- return !Objects.equals(rightItem.path("art").asText(""), leftItem.path("art").asText("")) ||
|
|
|
|
|
- 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(""));
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static String buildFreibetragFieldHtml(JsonNode rightItem, JsonNode leftItem, Function<JsonNode, String> extractor, boolean hasChanges) {
|
|
|
|
|
- if (!hasChanges || rightItem == null) {
|
|
|
|
|
- JsonNode primary = rightItem != null ? rightItem : leftItem;
|
|
|
|
|
- return primary != null ? escape(extractor.apply(primary)) : "";
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- String rightVal = rightItem != null ? extractor.apply(rightItem) : "";
|
|
|
|
|
- String leftVal = leftItem != null ? extractor.apply(leftItem) : "";
|
|
|
|
|
-
|
|
|
|
|
- if (!Objects.equals(rightVal, leftVal)) {
|
|
|
|
|
- return cellHtml(rightVal, leftVal, true);
|
|
|
|
|
- } else {
|
|
|
|
|
- return escape(rightVal);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static List<KitaZeile> buildKitaZeilen(JsonNode rightPerson, JsonNode leftPerson, Map<String, String> personNameByIdRight) {
|
|
|
|
|
- // Extract kinderbetreuung arrays from both persons
|
|
|
|
|
- ArrayNode rightKinderbetreuung = rightPerson != null && rightPerson.has("kinderbetreuung") && rightPerson.get("kinderbetreuung").isArray()
|
|
|
|
|
- ? (ArrayNode) rightPerson.get("kinderbetreuung")
|
|
|
|
|
- : new ObjectMapper().createArrayNode();
|
|
|
|
|
- ArrayNode leftKinderbetreuung = leftPerson != null && leftPerson.has("kinderbetreuung") && leftPerson.get("kinderbetreuung").isArray()
|
|
|
|
|
- ? (ArrayNode) leftPerson.get("kinderbetreuung")
|
|
|
|
|
- : new ObjectMapper().createArrayNode();
|
|
|
|
|
-
|
|
|
|
|
- // UNION über kinderbetreuung[].id
|
|
|
|
|
- Map<String, Pair<JsonNode, JsonNode>> kitaPairs = unionByKey(
|
|
|
|
|
- rightKinderbetreuung, leftKinderbetreuung, n -> n.path("id").asText()
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- List<KitaZeile> result = new ArrayList<>();
|
|
|
|
|
- for (Map.Entry<String, Pair<JsonNode, JsonNode>> entry : kitaPairs.entrySet()) {
|
|
|
|
|
- JsonNode rightItem = entry.getValue().right;
|
|
|
|
|
- JsonNode leftItem = entry.getValue().left;
|
|
|
|
|
-
|
|
|
|
|
- // Determine status and changes
|
|
|
|
|
- String symbol = "";
|
|
|
|
|
- String symbolClass = "";
|
|
|
|
|
- String rowClass = "";
|
|
|
|
|
- boolean hasChanges = false;
|
|
|
|
|
-
|
|
|
|
|
- if (rightItem != null && leftItem != null) {
|
|
|
|
|
- hasChanges = detectKitaChanges(rightItem, leftItem);
|
|
|
|
|
- if (hasChanges) {
|
|
|
|
|
- symbol = symFor("CHANGED");
|
|
|
|
|
- symbolClass = symClassFor("CHANGED");
|
|
|
|
|
- }
|
|
|
|
|
- } else if (rightItem != null && leftItem == null) {
|
|
|
|
|
- symbol = symFor("ADDED");
|
|
|
|
|
- symbolClass = symClassFor("ADDED");
|
|
|
|
|
- rowClass = rowClassFor("ADDED");
|
|
|
|
|
- } else if (rightItem == null && leftItem != null) {
|
|
|
|
|
- symbol = symFor("REMOVED");
|
|
|
|
|
- symbolClass = symClassFor("REMOVED");
|
|
|
|
|
- rowClass = rowClassFor("REMOVED");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Use primary data (right first, then left)
|
|
|
|
|
- JsonNode primaryItem = rightItem != null ? rightItem : leftItem;
|
|
|
|
|
-
|
|
|
|
|
- if (primaryItem != null) {
|
|
|
|
|
- String kindNameHtml = buildKitaFieldHtml(rightItem, leftItem,
|
|
|
|
|
- i -> {
|
|
|
|
|
- String kindRef = i.path("kindRef").asText("");
|
|
|
|
|
- return personNameByIdRight.getOrDefault(kindRef, "Kind ID: " + kindRef);
|
|
|
|
|
- }, hasChanges);
|
|
|
|
|
-
|
|
|
|
|
- String betragHtml = buildKitaFieldHtml(rightItem, leftItem,
|
|
|
|
|
- i -> String.format("%.2f €", i.path("betrag").asDouble(0.0)), hasChanges);
|
|
|
|
|
-
|
|
|
|
|
- result.add(new KitaZeile(
|
|
|
|
|
- symbol, symbolClass, rowClass,
|
|
|
|
|
- kindNameHtml, betragHtml
|
|
|
|
|
- ));
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return result;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static boolean detectKitaChanges(JsonNode rightItem, JsonNode leftItem) {
|
|
|
|
|
- return !Objects.equals(rightItem.path("kindRef").asText(""), leftItem.path("kindRef").asText("")) ||
|
|
|
|
|
- rightItem.path("betrag").asDouble(0.0) != leftItem.path("betrag").asDouble(0.0);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static String buildKitaFieldHtml(JsonNode rightItem, JsonNode leftItem, Function<JsonNode, String> extractor, boolean hasChanges) {
|
|
|
|
|
- if (!hasChanges || rightItem == null) {
|
|
|
|
|
- JsonNode primary = rightItem != null ? rightItem : leftItem;
|
|
|
|
|
- return primary != null ? escape(extractor.apply(primary)) : "";
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- String rightVal = rightItem != null ? extractor.apply(rightItem) : "";
|
|
|
|
|
- String leftVal = leftItem != null ? extractor.apply(leftItem) : "";
|
|
|
|
|
-
|
|
|
|
|
- if (!Objects.equals(rightVal, leftVal)) {
|
|
|
|
|
- return cellHtml(rightVal, leftVal, true);
|
|
|
|
|
- } else {
|
|
|
|
|
- return escape(rightVal);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Hilfs-Bausteine für Status und Zellen:
|
|
|
|
|
- private static String symFor(String status) {
|
|
|
|
|
- return switch (status) {
|
|
|
|
|
- case "ADDED" -> "+";
|
|
|
|
|
- case "REMOVED" -> "−";
|
|
|
|
|
- case "CHANGED" -> "!";
|
|
|
|
|
- default -> "";
|
|
|
|
|
- };
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static String symClassFor(String status) {
|
|
|
|
|
- return switch (status) {
|
|
|
|
|
- case "ADDED" -> "plus";
|
|
|
|
|
- case "REMOVED" -> "minus";
|
|
|
|
|
- case "CHANGED" -> "warn";
|
|
|
|
|
- default -> "";
|
|
|
|
|
- };
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static String rowClassFor(String status) {
|
|
|
|
|
- return switch (status) {
|
|
|
|
|
- case "ADDED" -> "diff-row-added";
|
|
|
|
|
- case "REMOVED" -> "diff-row-removed";
|
|
|
|
|
- default -> "";
|
|
|
|
|
- };
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static String cellHtml(String newVal, String oldVal, boolean changed) {
|
|
|
|
|
- if (!changed) {
|
|
|
|
|
- return escape(newVal);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Handle empty old values
|
|
|
|
|
- String displayOldVal = (oldVal == null || oldVal.trim().isEmpty()) ? "leer" : oldVal;
|
|
|
|
|
-
|
|
|
|
|
- // Return with cell-changed class and proper old value display
|
|
|
|
|
- return "<span class='cell-changed'>" + escape(newVal) +
|
|
|
|
|
- "<div class='old'>Alt: " + escape(displayOldVal) + "</div></span>";
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static String escape(String s) {
|
|
|
|
|
- return s == null ? "" : s.replace("&", "&").replace("<", "<").replace(">", ">");
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|