Erste fachliche Strukturregel ergänzen: UNH-/UNT-Referenznummer prüfen
This commit is contained in:
@@ -13,15 +13,16 @@ import de.gecheckt.asv.validation.model.ValidationResult;
|
||||
import de.gecheckt.asv.validation.model.ValidationSeverity;
|
||||
|
||||
/**
|
||||
* Default implementation of StructureValidator that checks general structural rules.
|
||||
* Standardimplementierung des StructureValidator, die allgemeine Strukturregeln prüft.
|
||||
*
|
||||
* Rules checked:
|
||||
* 1. InputFile must contain at least one Message
|
||||
* 2. Each Message must contain at least one Segment
|
||||
* 3. Segment names must not be empty
|
||||
* 4. Field positions within a Segment must be unique and positive
|
||||
* 5. Segment positions within a Message must be unique and positive
|
||||
* 6. Message positions within an InputFile must be unique and positive
|
||||
* Geprüfte Regeln:
|
||||
* 1. Die Eingabedatei muss mindestens eine Nachricht enthalten
|
||||
* 2. Jede Nachricht muss mindestens ein Segment enthalten
|
||||
* 3. Segmentnamen dürfen nicht leer sein
|
||||
* 4. Feldpositionen innerhalb eines Segments 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
|
||||
* 7. UNH- und UNT-Referenznummern müssen innerhalb einer Nachricht übereinstimmen
|
||||
*/
|
||||
public class DefaultStructureValidator implements StructureValidator {
|
||||
|
||||
@@ -44,10 +45,10 @@ public class DefaultStructureValidator implements StructureValidator {
|
||||
"",
|
||||
0,
|
||||
"",
|
||||
"At least one message required"
|
||||
"Mindestens eine Nachricht erforderlich"
|
||||
));
|
||||
} else {
|
||||
// Process messages if they exist
|
||||
// Verarbeite Nachrichten, wenn sie vorhanden sind
|
||||
validateMessages(inputFile.messages(), errors);
|
||||
}
|
||||
|
||||
@@ -55,10 +56,10 @@ public class DefaultStructureValidator implements StructureValidator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates all messages in the input file.
|
||||
* Validiert alle Nachrichten in der Eingabedatei.
|
||||
*
|
||||
* @param messages the list of messages to validate
|
||||
* @param errors the list to add validation errors to
|
||||
* @param messages die Liste der zu validierenden Nachrichten
|
||||
* @param errors die Liste zum Hinzufügen von Validierungsfehlern
|
||||
*/
|
||||
private void validateMessages(List<Message> messages, List<ValidationError> errors) {
|
||||
var messagePositions = new HashSet<Integer>();
|
||||
@@ -66,35 +67,83 @@ public class DefaultStructureValidator implements StructureValidator {
|
||||
for (var message : messages) {
|
||||
var messagePosition = message.messagePosition();
|
||||
|
||||
// Rule 6: Message positions must be unique and positive
|
||||
// Regel 6: Nachrichtenpositionen müssen eindeutig und positiv sein
|
||||
if (!messagePositions.add(messagePosition)) {
|
||||
errors.add(createError(
|
||||
"STRUCTURE_006",
|
||||
"Duplicate message position: " + messagePosition,
|
||||
"Doppelte Nachrichtenposition: " + messagePosition,
|
||||
ValidationSeverity.ERROR,
|
||||
"",
|
||||
messagePosition,
|
||||
"",
|
||||
0,
|
||||
String.valueOf(messagePosition),
|
||||
"Unique positive message positions required"
|
||||
"Einzigartige positive Nachrichtenpositionen erforderlich"
|
||||
));
|
||||
}
|
||||
|
||||
// Validate segments in this message
|
||||
// Validiere Segmente in dieser Nachricht
|
||||
validateSegments(message.segments(), messagePosition, errors);
|
||||
|
||||
// Regel 7: UNH- und UNT-Referenznummern müssen übereinstimmen
|
||||
validateUnhUntReferenceNumbers(message, errors);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates all segments in a message.
|
||||
* Validiert, dass die Referenznummern in UNH und UNT übereinstimmen.
|
||||
*
|
||||
* @param segments the list of segments to validate
|
||||
* @param messagePosition the position of the parent message
|
||||
* @param errors the list to add validation errors to
|
||||
* @param message die zu validierende Nachricht
|
||||
* @param errors die Liste zum Hinzufügen von Validierungsfehlern
|
||||
*/
|
||||
private void validateUnhUntReferenceNumbers(Message message, List<ValidationError> errors) {
|
||||
var unhSegment = message.getFirstSegment("UNH");
|
||||
var untSegment = message.getFirstSegment("UNT");
|
||||
|
||||
// Prüft, ob beide Segmente vorhanden sind, bevor eine Validierung erfolgt
|
||||
if (unhSegment.isPresent() && untSegment.isPresent()) {
|
||||
var unhFields = unhSegment.get().fields();
|
||||
var untFields = untSegment.get().fields();
|
||||
|
||||
// UNH-Referenznummer ist im 1. Feld (erstes Feld nach dem Segmentnamen)
|
||||
// UNT-Referenznummer ist im 2. Feld (zweites Feld nach dem Segmentnamen)
|
||||
if (!unhFields.isEmpty() && untFields.size() >= 2) {
|
||||
var unhReference = unhFields.get(0).rawValue();
|
||||
var untReference = untFields.get(1).rawValue();
|
||||
|
||||
// Entferne möglicherweise vorhandenes abschließendes Hochkomma bei untReference
|
||||
String normalizedUntReference = untReference;
|
||||
if (untReference.endsWith("'")) {
|
||||
normalizedUntReference = untReference.substring(0, untReference.length() - 1);
|
||||
}
|
||||
|
||||
// Vergleiche die Referenznummern
|
||||
if (!unhReference.equals(normalizedUntReference)) {
|
||||
errors.add(createError(
|
||||
"STRUCTURE_007",
|
||||
"UNH and UNT reference numbers do not match",
|
||||
ValidationSeverity.ERROR,
|
||||
"UNH/UNT",
|
||||
message.messagePosition(),
|
||||
"",
|
||||
0,
|
||||
unhReference + " != " + normalizedUntReference,
|
||||
"Reference numbers in UNH and UNT must match"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validiert alle Segmente einer Nachricht.
|
||||
*
|
||||
* @param segments die Liste der zu validierenden Segmente
|
||||
* @param messagePosition die Position der übergeordneten Nachricht
|
||||
* @param errors die Liste zum Hinzufügen von Validierungsfehlern
|
||||
*/
|
||||
private void validateSegments(List<Segment> segments, int messagePosition, List<ValidationError> errors) {
|
||||
// Rule 2: Each Message must contain at least one Segment
|
||||
// Regel 2: Jede Nachricht muss mindestens ein Segment enthalten
|
||||
if (segments.isEmpty()) {
|
||||
errors.add(createError(
|
||||
"STRUCTURE_002",
|
||||
@@ -105,9 +154,9 @@ public class DefaultStructureValidator implements StructureValidator {
|
||||
"",
|
||||
0,
|
||||
"",
|
||||
"At least one segment required per message"
|
||||
"Mindestens ein Segment pro Nachricht erforderlich"
|
||||
));
|
||||
return; // No need to validate segments if there are none
|
||||
return; // Keine Validierung der Segmente notwendig, wenn keine vorhanden sind
|
||||
}
|
||||
|
||||
var segmentPositions = new HashSet<Integer>();
|
||||
@@ -116,22 +165,22 @@ public class DefaultStructureValidator implements StructureValidator {
|
||||
var segmentName = segment.segmentName();
|
||||
var segmentPosition = segment.segmentPosition();
|
||||
|
||||
// Rule 3: Segment names must not be empty
|
||||
// Regel 3: Segmentnamen dürfen nicht leer sein
|
||||
if (segmentName == null || segmentName.isEmpty()) {
|
||||
errors.add(createError(
|
||||
"STRUCTURE_003",
|
||||
"Segment name must not be empty",
|
||||
"Segmentname darf nicht leer sein",
|
||||
ValidationSeverity.ERROR,
|
||||
segmentName != null ? segmentName : "",
|
||||
segmentPosition,
|
||||
"",
|
||||
0,
|
||||
segmentName != null ? segmentName : "null",
|
||||
"Non-empty segment name required"
|
||||
"Nicht-leerer Segmentname erforderlich"
|
||||
));
|
||||
}
|
||||
|
||||
// Rule 5: Segment positions must be unique and positive
|
||||
// Regel 5: Segmentpositionen müssen eindeutig und positiv sein
|
||||
if (!segmentPositions.add(segmentPosition)) {
|
||||
errors.add(createError(
|
||||
"STRUCTURE_005",
|
||||
@@ -142,7 +191,7 @@ public class DefaultStructureValidator implements StructureValidator {
|
||||
"",
|
||||
0,
|
||||
String.valueOf(segmentPosition),
|
||||
"Unique positive segment positions required"
|
||||
"Einzigartige positive Segmentpositionen erforderlich"
|
||||
));
|
||||
}
|
||||
|
||||
@@ -152,12 +201,12 @@ public class DefaultStructureValidator implements StructureValidator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates all fields in a segment.
|
||||
* Validiert alle Felder eines Segments.
|
||||
*
|
||||
* @param fields the list of fields to validate
|
||||
* @param segmentName the name of the parent segment
|
||||
* @param segmentPosition the position of the parent segment
|
||||
* @param errors the list to add validation errors to
|
||||
* @param fields die Liste der zu validierenden Felder
|
||||
* @param segmentName der Name des übergeordneten Segments
|
||||
* @param segmentPosition die Position des übergeordneten Segments
|
||||
* @param errors die Liste zum Hinzufügen von Validierungsfehlern
|
||||
*/
|
||||
private void validateFields(List<Field> fields, String segmentName, int segmentPosition, List<ValidationError> errors) {
|
||||
var fieldPositions = new HashSet<Integer>();
|
||||
@@ -165,7 +214,7 @@ public class DefaultStructureValidator implements StructureValidator {
|
||||
for (var field : fields) {
|
||||
var fieldPosition = field.fieldPosition();
|
||||
|
||||
// Rule 4: Field positions must be unique and positive
|
||||
// Regel 4: Feldpositionen müssen eindeutig und positiv sein
|
||||
if (!fieldPositions.add(fieldPosition)) {
|
||||
errors.add(createError(
|
||||
"STRUCTURE_004",
|
||||
@@ -176,25 +225,25 @@ public class DefaultStructureValidator implements StructureValidator {
|
||||
field.getFieldName().orElse(""),
|
||||
fieldPosition,
|
||||
String.valueOf(fieldPosition),
|
||||
"Unique positive field positions required"
|
||||
"Einzigartige positive Feldpositionen erforderlich"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a ValidationError with consistent parameters.
|
||||
* Hilfsmethode zur Erstellung eines ValidationError mit konsistenten Parametern.
|
||||
*
|
||||
* @param errorCode the error code
|
||||
* @param description the error description
|
||||
* @param severity the validation severity
|
||||
* @param segmentName the segment name
|
||||
* @param segmentPosition the segment position
|
||||
* @param fieldName the field name
|
||||
* @param fieldPosition the field position
|
||||
* @param actualValue the actual value
|
||||
* @param expectedRule the expected rule
|
||||
* @return a new ValidationError instance
|
||||
* @param errorCode der Fehlercode
|
||||
* @param description die Fehlerbeschreibung
|
||||
* @param severity die Validierungsschweregrad
|
||||
* @param segmentName der Segmentname
|
||||
* @param segmentPosition die Segmentposition
|
||||
* @param fieldName der Feldname
|
||||
* @param fieldPosition die Feldposition
|
||||
* @param actualValue der tatsächliche Wert
|
||||
* @param expectedRule die erwartete Regel
|
||||
* @return eine neue ValidationError-Instanz
|
||||
*/
|
||||
private ValidationError createError(String errorCode,
|
||||
String description,
|
||||
|
||||
@@ -2,6 +2,9 @@ package de.gecheckt.asv.validation.structure;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -12,6 +15,10 @@ 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.parser.DefaultInputFileParser;
|
||||
import de.gecheckt.asv.parser.DefaultSegmentLineTokenizer;
|
||||
import de.gecheckt.asv.parser.InputFileParseException;
|
||||
import de.gecheckt.asv.parser.SegmentLineTokenizer;
|
||||
import de.gecheckt.asv.validation.model.ValidationError;
|
||||
import de.gecheckt.asv.validation.model.ValidationResult;
|
||||
|
||||
@@ -119,4 +126,89 @@ class DefaultStructureValidatorTest {
|
||||
assertFalse(result.hasInfos());
|
||||
assertTrue(result.getAllErrors().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void validate_shouldReportErrorWhenUnhAndUntReferenceNumbersDoNotMatch() throws IOException, InputFileParseException {
|
||||
// Given
|
||||
SegmentLineTokenizer tokenizer = new DefaultSegmentLineTokenizer();
|
||||
DefaultInputFileParser parser = new DefaultInputFileParser(tokenizer);
|
||||
String fileName = "unh-unt-mismatch.asv";
|
||||
Path filePath = Path.of("src/test/resources/unh-unt-mismatch.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_007", error.errorCode());
|
||||
assertEquals("UNH and UNT reference numbers do not match", error.description());
|
||||
assertEquals("UNH/UNT", error.segmentName());
|
||||
assertEquals(1, error.segmentPosition());
|
||||
assertEquals("12345 != 54321", error.actualValue());
|
||||
assertEquals("Reference numbers in UNH and UNT must match", error.expectedRule());
|
||||
}
|
||||
|
||||
@Test
|
||||
void validate_shouldNotReportErrorWhenUnhAndUntReferenceNumbersMatch() throws IOException, InputFileParseException {
|
||||
// Given
|
||||
SegmentLineTokenizer tokenizer = new DefaultSegmentLineTokenizer();
|
||||
DefaultInputFileParser parser = new DefaultInputFileParser(tokenizer);
|
||||
String fileName = "unh-unt-match.asv";
|
||||
Path filePath = Path.of("src/test/resources/unh-unt-match.asv");
|
||||
String fileContent = Files.readString(filePath);
|
||||
|
||||
// When
|
||||
InputFile inputFile = parser.parse(fileName, fileContent);
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
// Then
|
||||
// Check that we don't have the STRUCTURE_007 error
|
||||
assertFalse(result.getErrors().stream()
|
||||
.anyMatch(error -> "STRUCTURE_007".equals(error.errorCode())));
|
||||
}
|
||||
|
||||
@Test
|
||||
void validate_shouldNotReportAdditionalErrorWhenUnhIsMissing() throws IOException, InputFileParseException {
|
||||
// Given
|
||||
SegmentLineTokenizer tokenizer = new DefaultSegmentLineTokenizer();
|
||||
DefaultInputFileParser parser = new DefaultInputFileParser(tokenizer);
|
||||
String fileName = "no-unh.asv";
|
||||
Path filePath = Path.of("src/test/resources/no-unh.asv");
|
||||
String fileContent = Files.readString(filePath);
|
||||
|
||||
// When
|
||||
InputFile inputFile = parser.parse(fileName, fileContent);
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
// Then
|
||||
// Should not have STRUCTURE_007 error for mismatch
|
||||
// Any existing structural errors are acceptable but not our new error
|
||||
assertFalse(result.getErrors().stream()
|
||||
.anyMatch(error -> "STRUCTURE_007".equals(error.errorCode())));
|
||||
}
|
||||
|
||||
@Test
|
||||
void validate_shouldNotReportAdditionalErrorWhenUntIsMissing() throws IOException, InputFileParseException {
|
||||
// Given
|
||||
SegmentLineTokenizer tokenizer = new DefaultSegmentLineTokenizer();
|
||||
DefaultInputFileParser parser = new DefaultInputFileParser(tokenizer);
|
||||
String fileName = "no-unt.asv";
|
||||
Path filePath = Path.of("src/test/resources/no-unt.asv");
|
||||
String fileContent = Files.readString(filePath);
|
||||
|
||||
// When
|
||||
InputFile inputFile = parser.parse(fileName, fileContent);
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
// Then
|
||||
// Should not have STRUCTURE_007 error for mismatch
|
||||
// Any existing structural errors are acceptable but not our new error
|
||||
assertFalse(result.getErrors().stream()
|
||||
.anyMatch(error -> "STRUCTURE_007".equals(error.errorCode())));
|
||||
}
|
||||
}
|
||||
8
src/test/resources/no-unh.asv
Normal file
8
src/test/resources/no-unh.asv
Normal file
@@ -0,0 +1,8 @@
|
||||
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'
|
||||
8
src/test/resources/no-unt.asv
Normal file
8
src/test/resources/no-unt.asv
Normal file
@@ -0,0 +1,8 @@
|
||||
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'
|
||||
9
src/test/resources/unh-unt-match.asv
Normal file
9
src/test/resources/unh-unt-match.asv
Normal 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'
|
||||
9
src/test/resources/unh-unt-mismatch.asv
Normal file
9
src/test/resources/unh-unt-mismatch.asv
Normal 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+6+54321'
|
||||
Reference in New Issue
Block a user