From b7776a564ace0a968acda59c0c8867f98e303e50 Mon Sep 17 00:00:00 2001 From: Marcus van Elst Date: Thu, 26 Mar 2026 20:32:23 +0100 Subject: [PATCH] =?UTF-8?q?FHL-Pflichtsegment=20f=C3=BCr=20ASVFEH=20validi?= =?UTF-8?q?eren?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../structure/DefaultStructureValidator.java | 52 +++++++- ...tructureValidatorAsvfehFhlSegmentTest.java | 114 ++++++++++++++++++ ...lidatorAsvrecRechnungskennzeichenTest.java | 9 +- ...ValidatorAsvrecSegmentCardinalityTest.java | 7 +- ...uctureValidatorAsvrecSegmentOrderTest.java | 9 +- ...tStructureValidatorAsvrecSegmentsTest.java | 7 +- 6 files changed, 180 insertions(+), 18 deletions(-) create mode 100644 src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorAsvfehFhlSegmentTest.java diff --git a/src/main/java/de/gecheckt/asv/validation/structure/DefaultStructureValidator.java b/src/main/java/de/gecheckt/asv/validation/structure/DefaultStructureValidator.java index d827af1..13f3cf6 100644 --- a/src/main/java/de/gecheckt/asv/validation/structure/DefaultStructureValidator.java +++ b/src/main/java/de/gecheckt/asv/validation/structure/DefaultStructureValidator.java @@ -34,6 +34,7 @@ import de.gecheckt.asv.validation.model.ValidationSeverity; * 16. Für ASVREC mit Rechnungskennzeichen "1" in REA dürfen DGN und LEA nicht vorhanden sein * 17. Für ASVREC mit Rechnungskennzeichen "1" in REA muss der Rechnungsbetrag "0,00" sein * 18. Für ASVREC müssen IFA, REA und IVA jeweils genau einmal vorkommen + * 19. Für ASVFEH-Nachrichten muss mindestens ein FHL-Segment vorhanden sein */ public class DefaultStructureValidator implements StructureValidator { @@ -146,6 +147,9 @@ public class DefaultStructureValidator implements StructureValidator { // Neue Regel: Für ASVREC müssen IFA, REA und IVA jeweils genau einmal vorkommen validateAsvrecSegmentCardinality(message, errors); + + // Neue Regel: Für ASVFEH-Nachrichten muss mindestens ein FHL-Segment vorhanden sein + validateAsvfehRequiredFhlSegment(message, errors); } } } @@ -529,7 +533,9 @@ public class DefaultStructureValidator implements StructureValidator { if (unhSegment.isEmpty()) { return; } - if (!"ASVREC".equals(extractMessageType(unhSegment.get()))) { + String messageType = extractMessageType(unhSegment.get()); + // Nur für ASVREC-Nachrichten prüfen + if (!"ASVREC".equals(messageType)) { return; } @@ -594,7 +600,9 @@ public class DefaultStructureValidator implements StructureValidator { if (unhSegment.isEmpty()) { return; } - if (!"ASVREC".equals(extractMessageType(unhSegment.get()))) { + String messageType = extractMessageType(unhSegment.get()); + // Nur für ASVREC-Nachrichten prüfen + if (!"ASVREC".equals(messageType)) { return; } @@ -689,7 +697,9 @@ public class DefaultStructureValidator implements StructureValidator { if (unhSegment.isEmpty()) { return; } - if (!"ASVREC".equals(extractMessageType(unhSegment.get()))) { + String messageType = extractMessageType(unhSegment.get()); + // Nur für ASVREC-Nachrichten prüfen + if (!"ASVREC".equals(messageType)) { return; } @@ -738,7 +748,9 @@ public class DefaultStructureValidator implements StructureValidator { if (unhSegment.isEmpty()) { return; } - if (!"ASVREC".equals(extractMessageType(unhSegment.get()))) { + String messageType = extractMessageType(unhSegment.get()); + // Nur für ASVREC-Nachrichten prüfen + if (!"ASVREC".equals(messageType)) { return; } @@ -787,6 +799,38 @@ public class DefaultStructureValidator implements StructureValidator { } } + /** + * Validiert, dass für ASVFEH-Nachrichten mindestens ein FHL-Segment vorhanden ist. + * + * @param message die zu validierende Nachricht + * @param errors die Liste zum Hinzufügen von Validierungsfehlern + */ + private void validateAsvfehRequiredFhlSegment(Message message, List errors) { + // Prüfe den Nachrichtentyp aus dem UNH-Segment + var unhSegment = message.getFirstSegment("UNH"); + if (unhSegment.isPresent()) { + String messageType = extractMessageType(unhSegment.get()); + + // Nur für ASVFEH-Nachrichten prüfen + if ("ASVFEH".equals(messageType)) { + // Prüfe, ob mindestens ein FHL-Segment vorhanden ist + if (message.getFirstSegment("FHL").isEmpty()) { + errors.add(createError( + "STRUCTURE_025", + "ASVFEH-Nachricht muss mindestens ein FHL-Segment enthalten", + ValidationSeverity.ERROR, + "", + message.messagePosition(), + "", + 0, + "", + "FHL-Segment erforderlich für ASVFEH-Nachrichten" + )); + } + } + } + } + /** * Extrahiert den Nachrichtentyp aus dem UNH-Segment. * diff --git a/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorAsvfehFhlSegmentTest.java b/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorAsvfehFhlSegmentTest.java new file mode 100644 index 0000000..247e042 --- /dev/null +++ b/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorAsvfehFhlSegmentTest.java @@ -0,0 +1,114 @@ +package de.gecheckt.asv.validation.structure; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import de.gecheckt.asv.domain.model.Field; +import de.gecheckt.asv.domain.model.InputFile; +import de.gecheckt.asv.domain.model.Message; +import de.gecheckt.asv.domain.model.Segment; +import de.gecheckt.asv.validation.model.ValidationError; +import de.gecheckt.asv.validation.model.ValidationResult; + +/** + * Testfälle für die Validierung der Mindestpräsenz des FHL-Segments in ASVFEH-Nachrichten. + * + * Testet spezifisch die neue Regel: + * Für ASVFEH-Nachrichten muss mindestens ein FHL-Segment vorhanden sein. + */ +class DefaultStructureValidatorAsvfehFhlSegmentTest { + + private DefaultStructureValidator validator; + + @BeforeEach + void setUp() { + validator = new DefaultStructureValidator(); + } + + @Test + void validate_shouldNotReportErrorForAsvfehWithOneFhlSegment() { + // Given: ASVFEH-Nachricht mit einem FHL-Segment + Segment unh = new Segment("UNH", 1, List.of(new Field(1, "12345"), new Field(2, "ASVFEH:D:03B:UN:EAN008"))); + Segment fhl = new Segment("FHL", 2, List.of(new Field(1, "1"))); + Segment unt = new Segment("UNT", 3, List.of(new Field(1, "3"), new Field(2, "12345"))); + Message message = new Message(1, List.of(unh, fhl, unt)); + InputFile inputFile = new InputFile("test.txt", List.of(message)); + + // When + ValidationResult result = validator.validate(inputFile); + + // Then: Kein Fehler STRUCTURE_025 + assertFalse(result.hasErrors(), "Es sollte keine Fehler geben"); + assertTrue(result.getErrors().stream() + .noneMatch(error -> "STRUCTURE_025".equals(error.errorCode())), + "STRUCTURE_025 Fehler sollte nicht auftreten"); + } + + @Test + void validate_shouldNotReportErrorForAsvfehWithMultipleFhlSegments() { + // Given: ASVFEH-Nachricht mit mehreren FHL-Segmenten + Segment unh = new Segment("UNH", 1, List.of(new Field(1, "12345"), new Field(2, "ASVFEH:D:03B:UN:EAN008"))); + Segment fhl1 = new Segment("FHL", 2, List.of(new Field(1, "1"))); + Segment fhl2 = new Segment("FHL", 3, List.of(new Field(1, "2"))); + Segment unt = new Segment("UNT", 4, List.of(new Field(1, "4"), new Field(2, "12345"))); + Message message = new Message(1, List.of(unh, fhl1, fhl2, unt)); + InputFile inputFile = new InputFile("test.txt", List.of(message)); + + // When + ValidationResult result = validator.validate(inputFile); + + // Then: Kein Fehler STRUCTURE_025 + assertFalse(result.hasErrors(), "Es sollte keine Fehler geben"); + assertTrue(result.getErrors().stream() + .noneMatch(error -> "STRUCTURE_025".equals(error.errorCode())), + "STRUCTURE_025 Fehler sollte nicht auftreten"); + } + + @Test + void validate_shouldReportErrorForAsvfehWithoutFhlSegment() { + // Given: ASVFEH-Nachricht ohne FHL-Segment + Segment unh = new Segment("UNH", 1, List.of(new Field(1, "12345"), new Field(2, "ASVFEH:D:03B:UN:EAN008"))); + Segment seg = new Segment("SEG", 2, List.of(new Field(1, "value"))); + Segment unt = new Segment("UNT", 3, List.of(new Field(1, "3"), new Field(2, "12345"))); + Message message = new Message(1, List.of(unh, seg, unt)); + InputFile inputFile = new InputFile("test.txt", List.of(message)); + + // When + ValidationResult result = validator.validate(inputFile); + + // Then: Genau ein Fehler STRUCTURE_025 + assertTrue(result.hasErrors(), "Es sollte einen Fehler geben"); + assertEquals(1, result.getErrors().size(), "Es sollte genau einen Fehler geben"); + + ValidationError error = result.getErrors().get(0); + assertEquals("STRUCTURE_025", error.errorCode()); + assertEquals("ASVFEH-Nachricht muss mindestens ein FHL-Segment enthalten", error.description()); + assertEquals("", error.segmentName()); + assertEquals(1, error.segmentPosition()); + assertEquals("FHL-Segment erforderlich für ASVFEH-Nachrichten", error.expectedRule()); + } + + @Test + void validate_shouldNotReportErrorForAsvrecWithoutFhlSegment() { + // Given: ASVREC-Nachricht ohne FHL-Segment (sollte keinen Fehler erzeugen) + Segment unh = new Segment("UNH", 1, List.of(new Field(1, "12345"), new Field(2, "ASVREC:D:03B:UN:EAN008"))); + Segment ifa = new Segment("IFA", 2); + Segment rea = new Segment("REA", 3); + Segment iva = new Segment("IVA", 4); + Segment unt = new Segment("UNT", 5, List.of(new Field(1, "5"), new Field(2, "12345"))); + Message message = new Message(1, List.of(unh, ifa, rea, iva, unt)); + InputFile inputFile = new InputFile("test.txt", List.of(message)); + + // When + ValidationResult result = validator.validate(inputFile); + + // Then: Kein Fehler STRUCTURE_025 + assertFalse(result.getErrors().stream() + .anyMatch(error -> "STRUCTURE_025".equals(error.errorCode())), + "STRUCTURE_025 Fehler sollte nicht auftreten für ASVREC"); + } +} \ No newline at end of file diff --git a/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorAsvrecRechnungskennzeichenTest.java b/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorAsvrecRechnungskennzeichenTest.java index b21db69..4542564 100644 --- a/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorAsvrecRechnungskennzeichenTest.java +++ b/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorAsvrecRechnungskennzeichenTest.java @@ -213,7 +213,7 @@ class DefaultStructureValidatorAsvrecRechnungskennzeichenTest { @Test void validate_asvfeh_keineRechnungskennzeichenRegel() { - // ASVFEH mit REA Kennzeichen "0" und ohne DGN/LEA -> kein STRUCTURE_017/018 + // ASVFEH mit REA Kennzeichen "0", FHL und ohne DGN/LEA -> kein STRUCTURE_017/018 Segment unh = new Segment("UNH", 1, List.of(new Field(1, "12345"), new Field(2, "ASVFEH:D:03B:UN:EAN008"))); Segment rea = new Segment("REA", 2, List.of( @@ -222,9 +222,10 @@ class DefaultStructureValidatorAsvrecRechnungskennzeichenTest { new Field(3, "RNR-001"), new Field(4, "0") )); - Segment unt = new Segment("UNT", 3, - List.of(new Field(1, "3"), new Field(2, "12345"))); - Message message = new Message(1, List.of(unh, rea, unt)); + Segment fhl = new Segment("FHL", 3, List.of(new Field(1, "1"))); + Segment unt = new Segment("UNT", 4, + List.of(new Field(1, "4"), new Field(2, "12345"))); + Message message = new Message(1, List.of(unh, rea, fhl, unt)); InputFile inputFile = new InputFile("test.txt", List.of(message)); ValidationResult result = validator.validate(inputFile); diff --git a/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorAsvrecSegmentCardinalityTest.java b/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorAsvrecSegmentCardinalityTest.java index a272132..b998207 100644 --- a/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorAsvrecSegmentCardinalityTest.java +++ b/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorAsvrecSegmentCardinalityTest.java @@ -259,10 +259,11 @@ class DefaultStructureValidatorAsvrecSegmentCardinalityTest { Segment rea2 = new Segment("REA", 5); Segment iva1 = new Segment("IVA", 6); Segment iva2 = new Segment("IVA", 7); - Segment unt = new Segment("UNT", 8, - List.of(new Field(1, "8"), new Field(2, "12345"))); + Segment fhl = new Segment("FHL", 8, List.of(new Field(1, "1"))); + Segment unt = new Segment("UNT", 9, + List.of(new Field(1, "9"), new Field(2, "12345"))); - Message message = new Message(1, List.of(unh, ifa1, ifa2, rea1, rea2, iva1, iva2, unt)); + Message message = new Message(1, List.of(unh, ifa1, ifa2, rea1, rea2, iva1, iva2, fhl, unt)); InputFile inputFile = new InputFile("test.txt", List.of(message)); ValidationResult result = validator.validate(inputFile); diff --git a/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorAsvrecSegmentOrderTest.java b/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorAsvrecSegmentOrderTest.java index d591396..e9af190 100644 --- a/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorAsvrecSegmentOrderTest.java +++ b/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorAsvrecSegmentOrderTest.java @@ -131,15 +131,16 @@ class DefaultStructureValidatorAsvrecSegmentOrderTest { @Test void validate_shouldNotReportOrderErrorForAsvfeh() { - // ASVFEH mit umgekehrter Reihenfolge der Segmente -> kein STRUCTURE_016 + // ASVFEH mit umgekehrter Reihenfolge der Segmente und FHL -> kein STRUCTURE_016 Segment unh = new Segment("UNH", 1, List.of(new Field(1, "12345"), new Field(2, "ASVFEH:D:03B:UN:EAN008"))); Segment iva = new Segment("IVA", 2); Segment rea = new Segment("REA", 3); Segment ifa = new Segment("IFA", 4); - Segment unt = new Segment("UNT", 5, - List.of(new Field(1, "5"), new Field(2, "12345"))); - Message message = new Message(1, List.of(unh, iva, rea, ifa, unt)); + Segment fhl = new Segment("FHL", 5, List.of(new Field(1, "1"))); + Segment unt = new Segment("UNT", 6, + List.of(new Field(1, "6"), new Field(2, "12345"))); + Message message = new Message(1, List.of(unh, iva, rea, ifa, fhl, unt)); InputFile inputFile = new InputFile("test.txt", List.of(message)); ValidationResult result = validator.validate(inputFile); diff --git a/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorAsvrecSegmentsTest.java b/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorAsvrecSegmentsTest.java index 93d051a..2b01fba 100644 --- a/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorAsvrecSegmentsTest.java +++ b/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorAsvrecSegmentsTest.java @@ -168,10 +168,11 @@ class DefaultStructureValidatorAsvrecSegmentsTest { @Test void validate_shouldNotReportErrorForAsvfehWithoutIfaReaIvaSegments() { - // Given + // Given: ASVFEH mit FHL, aber ohne IFA/REA/IVA Segment unh = new Segment("UNH", 1, List.of(new Field(1, "12345"), new Field(2, "ASVFEH:D:03B:UN:EAN008"))); - Segment unt = new Segment("UNT", 2, List.of(new Field(1, "2"), new Field(2, "12345"))); - Message message = new Message(1, List.of(unh, unt)); + Segment fhl = new Segment("FHL", 2, List.of(new Field(1, "1"))); + Segment unt = new Segment("UNT", 3, List.of(new Field(1, "3"), new Field(2, "12345"))); + Message message = new Message(1, List.of(unh, fhl, unt)); InputFile inputFile = new InputFile("test.txt", List.of(message)); // When