FHL-Pflichtsegment für ASVFEH validieren

This commit is contained in:
2026-03-26 20:32:23 +01:00
parent 4299baeec0
commit b7776a564a
6 changed files with 180 additions and 18 deletions

View File

@@ -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<ValidationError> 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.
*

View File

@@ -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");
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

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