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 6af5de3..f275faa 100644 --- a/src/main/java/de/gecheckt/asv/validation/structure/DefaultStructureValidator.java +++ b/src/main/java/de/gecheckt/asv/validation/structure/DefaultStructureValidator.java @@ -23,6 +23,7 @@ import de.gecheckt.asv.validation.model.ValidationSeverity; * 5. Segmentpositionen innerhalb einer Nachricht müssen eindeutig und positiv sein * 6. Nachrichtenpositionen innerhalb einer Eingabedatei müssen eindeutig und positiv sein * 7. UNH- und UNT-Referenznummern müssen innerhalb einer Nachricht übereinstimmen + * 8. Die im UNT angegebene Segmentanzahl muss der tatsächlichen Anzahl der Segmente entsprechen */ public class DefaultStructureValidator implements StructureValidator { @@ -87,6 +88,52 @@ public class DefaultStructureValidator implements StructureValidator { // Regel 7: UNH- und UNT-Referenznummern müssen übereinstimmen validateUnhUntReferenceNumbers(message, errors); + + // Regel 8: UNT-Segmentanzahl muss mit tatsächlicher Segmentanzahl übereinstimmen + validateUntSegmentCount(message, errors); + } + } + + /** + * Validiert, dass die im UNT angegebene Segmentanzahl der tatsächlichen Anzahl entspricht. + * + * @param message die zu validierende Nachricht + * @param errors die Liste zum Hinzufügen von Validierungsfehlern + */ + private void validateUntSegmentCount(Message message, List errors) { + var untSegment = message.getFirstSegment("UNT"); + + // Nur prüfen, wenn UNT-Segment vorhanden ist + if (untSegment.isPresent()) { + var fields = untSegment.get().fields(); + + // Prüfen, ob das erste Feld (Segmentanzahl) vorhanden ist + if (!fields.isEmpty()) { + var countField = fields.get(0); // 0074 - Anzahl der Segmente + var countValue = countField.rawValue(); + + // Nur prüfen, wenn der Wert numerisch interpretiert werden kann + try { + int declaredCount = Integer.parseInt(countValue); + int actualCount = message.segments().size(); + + if (declaredCount != actualCount) { + errors.add(createError( + "STRUCTURE_008", + "Die im UNT angegebene Segmentanzahl entspricht nicht der tatsächlichen Anzahl", + ValidationSeverity.ERROR, + "UNT", + message.messagePosition(), + "0074", + 1, + declaredCount + " != " + actualCount, + "Segmentanzahl in UNT muss mit tatsächlicher Anzahl übereinstimmen" + )); + } + } catch (NumberFormatException e) { + // Nicht-numerischer Wert - keine zusätzliche Fehlermeldung gemäß Anforderung + } + } } } diff --git a/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorTest.java b/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorTest.java index c2b3438..54b0afb 100644 --- a/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorTest.java +++ b/src/test/java/de/gecheckt/asv/validation/structure/DefaultStructureValidatorTest.java @@ -211,4 +211,98 @@ class DefaultStructureValidatorTest { assertFalse(result.getErrors().stream() .anyMatch(error -> "STRUCTURE_007".equals(error.errorCode()))); } + + @Test + void validate_shouldReportErrorWhenUntSegmentCountDoesNotMatch() throws IOException, InputFileParseException { + // Given + SegmentLineTokenizer tokenizer = new DefaultSegmentLineTokenizer(); + DefaultInputFileParser parser = new DefaultInputFileParser(tokenizer); + String fileName = "invalid-segment-count-too-small.asv"; + Path filePath = Path.of("src/test/resources/invalid-segment-count-too-small.asv"); + String fileContent = Files.readString(filePath); + + // When + InputFile inputFile = parser.parse(fileName, fileContent); + ValidationResult result = validator.validate(inputFile); + + // Then + assertTrue(result.hasErrors()); + assertEquals(1, result.getErrors().size()); + + ValidationError error = result.getErrors().get(0); + assertEquals("STRUCTURE_008", error.errorCode()); + assertEquals("Die im UNT angegebene Segmentanzahl entspricht nicht der tatsächlichen Anzahl", error.description()); + assertEquals("UNT", error.segmentName()); + assertEquals(1, error.segmentPosition()); + assertEquals("0074", error.fieldName()); + assertEquals(1, error.fieldPosition()); + assertEquals("8 != 9", error.actualValue()); + assertEquals("Segmentanzahl in UNT muss mit tatsächlicher Anzahl übereinstimmen", error.expectedRule()); + } + + @Test + void validate_shouldNotReportErrorWhenUntSegmentCountMatches() throws IOException, InputFileParseException { + // Given + SegmentLineTokenizer tokenizer = new DefaultSegmentLineTokenizer(); + DefaultInputFileParser parser = new DefaultInputFileParser(tokenizer); + String fileName = "valid-segment-count.asv"; + Path filePath = Path.of("src/test/resources/valid-segment-count.asv"); + String fileContent = Files.readString(filePath); + + // When + InputFile inputFile = parser.parse(fileName, fileContent); + ValidationResult result = validator.validate(inputFile); + + // Then + // Should not have STRUCTURE_008 error + assertFalse(result.getErrors().stream() + .anyMatch(error -> "STRUCTURE_008".equals(error.errorCode()))); + } + + @Test + void validate_shouldNotReportErrorWhenUntSegmentCountIsNonNumeric() throws IOException, InputFileParseException { + // Given + SegmentLineTokenizer tokenizer = new DefaultSegmentLineTokenizer(); + DefaultInputFileParser parser = new DefaultInputFileParser(tokenizer); + String fileName = "invalid-segment-count-non-numeric.asv"; + Path filePath = Path.of("src/test/resources/invalid-segment-count-non-numeric.asv"); + String fileContent = Files.readString(filePath); + + // When + InputFile inputFile = parser.parse(fileName, fileContent); + ValidationResult result = validator.validate(inputFile); + + // Then + // Should not have STRUCTURE_008 error (because count field is non-numeric) + assertFalse(result.getErrors().stream() + .anyMatch(error -> "STRUCTURE_008".equals(error.errorCode()))); + } + + @Test + void validate_shouldReportErrorWhenUntSegmentCountIsTooLarge() throws IOException, InputFileParseException { + // Given + SegmentLineTokenizer tokenizer = new DefaultSegmentLineTokenizer(); + DefaultInputFileParser parser = new DefaultInputFileParser(tokenizer); + String fileName = "invalid-segment-count-too-large.asv"; + Path filePath = Path.of("src/test/resources/invalid-segment-count-too-large.asv"); + String fileContent = Files.readString(filePath); + + // When + InputFile inputFile = parser.parse(fileName, fileContent); + ValidationResult result = validator.validate(inputFile); + + // Then + assertTrue(result.hasErrors()); + assertEquals(1, result.getErrors().size()); + + ValidationError error = result.getErrors().get(0); + assertEquals("STRUCTURE_008", error.errorCode()); + assertEquals("Die im UNT angegebene Segmentanzahl entspricht nicht der tatsächlichen Anzahl", error.description()); + assertEquals("UNT", error.segmentName()); + assertEquals(1, error.segmentPosition()); + assertEquals("0074", error.fieldName()); + assertEquals(1, error.fieldPosition()); + assertEquals("10 != 9", error.actualValue()); + assertEquals("Segmentanzahl in UNT muss mit tatsächlicher Anzahl übereinstimmen", error.expectedRule()); + } } \ No newline at end of file diff --git a/src/test/resources/invalid-segment-count-non-numeric.asv b/src/test/resources/invalid-segment-count-non-numeric.asv new file mode 100644 index 0000000..82952fe --- /dev/null +++ b/src/test/resources/invalid-segment-count-non-numeric.asv @@ -0,0 +1,9 @@ +UNH+12345+ORDERS:D:03B:UN:EAN008' +BGM+220+100001' +DTM+137:20260325:102' +NAD+BY+5000000000000:16++Customer Name' +LIN+1++Product123:SA' +QTY+21:10:PCE' +UNS+S' +CNT+2:1' +UNT+abc+12345' \ No newline at end of file diff --git a/src/test/resources/invalid-segment-count-too-large.asv b/src/test/resources/invalid-segment-count-too-large.asv new file mode 100644 index 0000000..5598cb3 --- /dev/null +++ b/src/test/resources/invalid-segment-count-too-large.asv @@ -0,0 +1,9 @@ +UNH+12345+ORDERS:D:03B:UN:EAN008' +BGM+220+100001' +DTM+137:20260325:102' +NAD+BY+5000000000000:16++Customer Name' +LIN+1++Product123:SA' +QTY+21:10:PCE' +UNS+S' +CNT+2:1' +UNT+10+12345' \ No newline at end of file diff --git a/src/test/resources/invalid-segment-count-too-small.asv b/src/test/resources/invalid-segment-count-too-small.asv new file mode 100644 index 0000000..3dd4045 --- /dev/null +++ b/src/test/resources/invalid-segment-count-too-small.asv @@ -0,0 +1,9 @@ +UNH+12345+ORDERS:D:03B:UN:EAN008' +BGM+220+100001' +DTM+137:20260325:102' +NAD+BY+5000000000000:16++Customer Name' +LIN+1++Product123:SA' +QTY+21:10:PCE' +UNS+S' +CNT+2:1' +UNT+8+12345' \ No newline at end of file diff --git a/src/test/resources/unh-unt-mismatch.asv b/src/test/resources/unh-unt-mismatch.asv index fd00d7d..9e93a3f 100644 --- a/src/test/resources/unh-unt-mismatch.asv +++ b/src/test/resources/unh-unt-mismatch.asv @@ -6,4 +6,4 @@ LIN+1++Product123:SA' QTY+21:10:PCE' UNS+S' CNT+2:1' -UNT+6+54321' \ No newline at end of file +UNT+9+54321' \ No newline at end of file diff --git a/src/test/resources/valid-segment-count.asv b/src/test/resources/valid-segment-count.asv new file mode 100644 index 0000000..92b1928 --- /dev/null +++ b/src/test/resources/valid-segment-count.asv @@ -0,0 +1,9 @@ +UNH+12345+ORDERS:D:03B:UN:EAN008' +BGM+220+100001' +DTM+137:20260325:102' +NAD+BY+5000000000000:16++Customer Name' +LIN+1++Product123:SA' +QTY+21:10:PCE' +UNS+S' +CNT+2:1' +UNT+9+12345' \ No newline at end of file