UNT-Segmentanzahl gegen tatsächliche Nachrichtenstruktur validieren

This commit is contained in:
2026-03-26 09:08:49 +01:00
parent 6a7d150007
commit 6152fe58d2
7 changed files with 178 additions and 1 deletions

View File

@@ -23,6 +23,7 @@ import de.gecheckt.asv.validation.model.ValidationSeverity;
* 5. Segmentpositionen innerhalb einer Nachricht müssen eindeutig und positiv sein * 5. Segmentpositionen innerhalb einer Nachricht müssen eindeutig und positiv sein
* 6. Nachrichtenpositionen innerhalb einer Eingabedatei 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 * 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 { public class DefaultStructureValidator implements StructureValidator {
@@ -87,6 +88,52 @@ public class DefaultStructureValidator implements StructureValidator {
// Regel 7: UNH- und UNT-Referenznummern müssen übereinstimmen // Regel 7: UNH- und UNT-Referenznummern müssen übereinstimmen
validateUnhUntReferenceNumbers(message, errors); 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<ValidationError> 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
}
}
} }
} }

View File

@@ -211,4 +211,98 @@ class DefaultStructureValidatorTest {
assertFalse(result.getErrors().stream() assertFalse(result.getErrors().stream()
.anyMatch(error -> "STRUCTURE_007".equals(error.errorCode()))); .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());
}
} }

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -6,4 +6,4 @@ LIN+1++Product123:SA'
QTY+21:10:PCE' QTY+21:10:PCE'
UNS+S' UNS+S'
CNT+2:1' CNT+2:1'
UNT+6+54321' UNT+9+54321'

View File

@@ -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'