Rechnungsbetrag 0,00 bei ASVREC-Storno validieren
This commit is contained in:
@@ -29,6 +29,10 @@ import de.gecheckt.asv.validation.model.ValidationSeverity;
|
||||
* 11. UNH muss vor UNT stehen
|
||||
* 12. Der Nachrichtentyp in UNH/S009/0065 darf nur ASVREC oder ASVFEH sein
|
||||
* 13. Für Nachrichten vom Typ ASVREC müssen die Segmente IFA, REA und IVA vorhanden sein
|
||||
* 14. Für Nachrichten vom Typ ASVREC muss die Reihenfolge IFA vor REA vor IVA eingehalten werden
|
||||
* 15. Für ASVREC mit Rechnungskennzeichen "0" in REA müssen DGN und LEA vorhanden sein
|
||||
* 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
|
||||
*/
|
||||
public class DefaultStructureValidator implements StructureValidator {
|
||||
|
||||
@@ -129,6 +133,15 @@ public class DefaultStructureValidator implements StructureValidator {
|
||||
|
||||
// Neue Regel: Für ASVREC-Nachrichten müssen IFA, REA und IVA vorhanden sein
|
||||
validateAsvrecRequiredSegments(message, errors);
|
||||
|
||||
// Neue Regel: Für ASVREC-Nachrichten muss die Reihenfolge IFA vor REA vor IVA eingehalten werden
|
||||
validateAsvrecSegmentOrder(message, errors);
|
||||
|
||||
// Neue Regel: Für ASVREC abhängig vom Rechnungskennzeichen in REA DGN/LEA prüfen
|
||||
validateAsvrecRechnungskennzeichen(message, errors);
|
||||
|
||||
// Neue Regel: Für ASVREC mit Kennzeichen "1" muss Rechnungsbetrag "0,00" sein
|
||||
validateAsvrecRechnungsbetragBeiKennzeichen1(message, errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -499,6 +512,215 @@ public class DefaultStructureValidator implements StructureValidator {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validiert, dass für ASVREC-Nachrichten die Reihenfolge IFA vor REA vor IVA eingehalten wird.
|
||||
* Die Regel greift nur, wenn alle drei Segmente vorhanden sind; fehlt eines, wird kein
|
||||
* zusätzlicher Reihenfolgefehler erzeugt (die Präsenzregel greift bereits).
|
||||
*
|
||||
* @param message die zu validierende Nachricht
|
||||
* @param errors die Liste zum Hinzufügen von Validierungsfehlern
|
||||
*/
|
||||
private void validateAsvrecSegmentOrder(Message message, List<ValidationError> errors) {
|
||||
var unhSegment = message.getFirstSegment("UNH");
|
||||
if (unhSegment.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!"ASVREC".equals(extractMessageType(unhSegment.get()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
var ifaOpt = message.getFirstSegment("IFA");
|
||||
var reaOpt = message.getFirstSegment("REA");
|
||||
var ivaOpt = message.getFirstSegment("IVA");
|
||||
|
||||
// Nur prüfen, wenn alle drei Segmente vorhanden sind
|
||||
if (ifaOpt.isEmpty() || reaOpt.isEmpty() || ivaOpt.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int ifaPos = ifaOpt.get().segmentPosition();
|
||||
int reaPos = reaOpt.get().segmentPosition();
|
||||
int ivaPos = ivaOpt.get().segmentPosition();
|
||||
|
||||
// IFA muss vor REA stehen
|
||||
if (ifaPos > reaPos) {
|
||||
errors.add(createError(
|
||||
"STRUCTURE_016",
|
||||
"In ASVREC-Nachrichten muss IFA vor REA stehen",
|
||||
ValidationSeverity.ERROR,
|
||||
"IFA/REA",
|
||||
message.messagePosition(),
|
||||
"",
|
||||
0,
|
||||
"IFA an Position " + ifaPos + ", REA an Position " + reaPos,
|
||||
"Reihenfolge IFA vor REA vor IVA erforderlich"
|
||||
));
|
||||
}
|
||||
|
||||
// REA muss vor IVA stehen
|
||||
if (reaPos > ivaPos) {
|
||||
errors.add(createError(
|
||||
"STRUCTURE_016",
|
||||
"In ASVREC-Nachrichten muss REA vor IVA stehen",
|
||||
ValidationSeverity.ERROR,
|
||||
"REA/IVA",
|
||||
message.messagePosition(),
|
||||
"",
|
||||
0,
|
||||
"REA an Position " + reaPos + ", IVA an Position " + ivaPos,
|
||||
"Reihenfolge IFA vor REA vor IVA erforderlich"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft für ASVREC-Nachrichten, ob DGN und LEA abhängig vom Rechnungskennzeichen in REA
|
||||
* vorhanden oder abwesend sind.
|
||||
* <ul>
|
||||
* <li>Rechnungskennzeichen "0": DGN und LEA müssen vorhanden sein.</li>
|
||||
* <li>Rechnungskennzeichen "1": DGN und LEA dürfen nicht vorhanden sein.</li>
|
||||
* <li>Ist REA nicht vorhanden oder das Kennzeichen nicht auswertbar, wird kein Fehler erzeugt.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param message die zu validierende Nachricht
|
||||
* @param errors die Liste zum Hinzufügen von Validierungsfehlern
|
||||
*/
|
||||
private void validateAsvrecRechnungskennzeichen(Message message, List<ValidationError> errors) {
|
||||
var unhSegment = message.getFirstSegment("UNH");
|
||||
if (unhSegment.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!"ASVREC".equals(extractMessageType(unhSegment.get()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Rechnungskennzeichen aus erstem Feld von REA lesen
|
||||
var reaOpt = message.getFirstSegment("REA");
|
||||
if (reaOpt.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
var reaFields = reaOpt.get().fields();
|
||||
if (reaFields.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
String kennzeichen = reaFields.get(0).rawValue();
|
||||
|
||||
if ("0".equals(kennzeichen)) {
|
||||
// DGN muss vorhanden sein
|
||||
if (message.getFirstSegment("DGN").isEmpty()) {
|
||||
errors.add(createError(
|
||||
"STRUCTURE_017",
|
||||
"ASVREC-Nachricht mit Rechnungskennzeichen '0' muss ein DGN-Segment enthalten",
|
||||
ValidationSeverity.ERROR,
|
||||
"REA",
|
||||
message.messagePosition(),
|
||||
"",
|
||||
0,
|
||||
"0",
|
||||
"DGN-Segment erforderlich bei Rechnungskennzeichen '0'"
|
||||
));
|
||||
}
|
||||
// LEA muss vorhanden sein
|
||||
if (message.getFirstSegment("LEA").isEmpty()) {
|
||||
errors.add(createError(
|
||||
"STRUCTURE_018",
|
||||
"ASVREC-Nachricht mit Rechnungskennzeichen '0' muss ein LEA-Segment enthalten",
|
||||
ValidationSeverity.ERROR,
|
||||
"REA",
|
||||
message.messagePosition(),
|
||||
"",
|
||||
0,
|
||||
"0",
|
||||
"LEA-Segment erforderlich bei Rechnungskennzeichen '0'"
|
||||
));
|
||||
}
|
||||
} else if ("1".equals(kennzeichen)) {
|
||||
// DGN darf nicht vorhanden sein
|
||||
if (message.getFirstSegment("DGN").isPresent()) {
|
||||
errors.add(createError(
|
||||
"STRUCTURE_019",
|
||||
"ASVREC-Nachricht mit Rechnungskennzeichen '1' darf kein DGN-Segment enthalten",
|
||||
ValidationSeverity.ERROR,
|
||||
"REA",
|
||||
message.messagePosition(),
|
||||
"",
|
||||
0,
|
||||
"1",
|
||||
"DGN-Segment verboten bei Rechnungskennzeichen '1'"
|
||||
));
|
||||
}
|
||||
// LEA darf nicht vorhanden sein
|
||||
if (message.getFirstSegment("LEA").isPresent()) {
|
||||
errors.add(createError(
|
||||
"STRUCTURE_020",
|
||||
"ASVREC-Nachricht mit Rechnungskennzeichen '1' darf kein LEA-Segment enthalten",
|
||||
ValidationSeverity.ERROR,
|
||||
"REA",
|
||||
message.messagePosition(),
|
||||
"",
|
||||
0,
|
||||
"1",
|
||||
"LEA-Segment verboten bei Rechnungskennzeichen '1'"
|
||||
));
|
||||
}
|
||||
}
|
||||
// Anderes Kennzeichen: keine Prüfung durch diese Regel
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft für ASVREC-Nachrichten mit Rechnungskennzeichen "1", ob der Rechnungsbetrag
|
||||
* im zweiten Feld von REA den Wert "0,00" hat.
|
||||
* <ul>
|
||||
* <li>Ist REA nicht vorhanden, wird kein Fehler erzeugt.</li>
|
||||
* <li>Ist das Kennzeichen nicht "1", wird kein Fehler erzeugt.</li>
|
||||
* <li>Ist das Betragsfeld nicht vorhanden oder nicht auswertbar, wird kein Fehler erzeugt.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param message die zu validierende Nachricht
|
||||
* @param errors die Liste zum Hinzufügen von Validierungsfehlern
|
||||
*/
|
||||
private void validateAsvrecRechnungsbetragBeiKennzeichen1(Message message, List<ValidationError> errors) {
|
||||
var unhSegment = message.getFirstSegment("UNH");
|
||||
if (unhSegment.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!"ASVREC".equals(extractMessageType(unhSegment.get()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
var reaOpt = message.getFirstSegment("REA");
|
||||
if (reaOpt.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
var reaFields = reaOpt.get().fields();
|
||||
if (reaFields.size() < 1) {
|
||||
return;
|
||||
}
|
||||
String kennzeichen = reaFields.get(0).rawValue();
|
||||
if (!"1".equals(kennzeichen)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Rechnungsbetrag ist das zweite Feld von REA
|
||||
if (reaFields.size() < 2) {
|
||||
return;
|
||||
}
|
||||
String betrag = reaFields.get(1).rawValue();
|
||||
if (!"0,00".equals(betrag)) {
|
||||
errors.add(createError(
|
||||
"STRUCTURE_021",
|
||||
"ASVREC-Nachricht mit Rechnungskennzeichen '1' muss Rechnungsbetrag '0,00' haben",
|
||||
ValidationSeverity.ERROR,
|
||||
"REA",
|
||||
message.messagePosition(),
|
||||
"",
|
||||
0,
|
||||
betrag,
|
||||
"Rechnungsbetrag muss '0,00' sein bei Rechnungskennzeichen '1'"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extrahiert den Nachrichtentyp aus dem UNH-Segment.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
package de.gecheckt.asv.validation.structure;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Tests für die Rechnungsbetrag-Regel bei ASVREC mit Rechnungskennzeichen "1":
|
||||
* - Kennzeichen "1" + Betrag "0,00" -> kein neuer Fehler
|
||||
* - Kennzeichen "1" + Betrag ungleich "0,00" -> genau ein Fehler STRUCTURE_021
|
||||
* - Kennzeichen "0" -> kein Fehler durch diese Regel
|
||||
* - ASVFEH -> kein Fehler durch diese Regel
|
||||
* - REA fehlt oder Betrag nicht auswertbar -> kein zusätzlicher Fehler
|
||||
*/
|
||||
class DefaultStructureValidatorAsvrecRechnungsbetragTest {
|
||||
|
||||
private DefaultStructureValidator validator;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
validator = new DefaultStructureValidator();
|
||||
}
|
||||
|
||||
// --- Hilfsmethoden ---
|
||||
|
||||
/**
|
||||
* Erstellt eine minimale ASVREC-Nachricht mit den angegebenen inneren Segmenten.
|
||||
* UNH an Position 1, UNT an letzter Position; Segmentanzahl wird automatisch gesetzt.
|
||||
*/
|
||||
private InputFile buildAsvrec(List<Segment> innerSegments) {
|
||||
Segment unh = new Segment("UNH", 1,
|
||||
List.of(new Field(1, "12345"), new Field(2, "ASVREC:D:03B:UN:EAN008")));
|
||||
|
||||
int untPos = 2 + innerSegments.size();
|
||||
int totalSegments = 1 + innerSegments.size() + 1;
|
||||
Segment unt = new Segment("UNT", untPos,
|
||||
List.of(new Field(1, String.valueOf(totalSegments)), new Field(2, "12345")));
|
||||
|
||||
var allSegments = new ArrayList<Segment>();
|
||||
allSegments.add(unh);
|
||||
allSegments.addAll(innerSegments);
|
||||
allSegments.add(unt);
|
||||
|
||||
Message message = new Message(1, allSegments);
|
||||
return new InputFile("test.txt", List.of(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt ein REA-Segment mit Rechnungskennzeichen und Rechnungsbetrag.
|
||||
*/
|
||||
private Segment rea(int pos, String kennzeichen, String betrag) {
|
||||
return new Segment("REA", pos,
|
||||
List.of(new Field(1, kennzeichen), new Field(2, betrag)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt ein REA-Segment mit nur dem Rechnungskennzeichen (kein Betrag).
|
||||
*/
|
||||
private Segment reaOhneBetrag(int pos, String kennzeichen) {
|
||||
return new Segment("REA", pos, List.of(new Field(1, kennzeichen)));
|
||||
}
|
||||
|
||||
// --- Test 1: Kennzeichen "1" + Betrag "0,00" -> kein neuer Fehler ---
|
||||
|
||||
@Test
|
||||
void validate_kennzeichen1_betrag0Komma00_keinFehler() {
|
||||
InputFile inputFile = buildAsvrec(List.of(
|
||||
new Segment("IFA", 2),
|
||||
rea(3, "1", "0,00"),
|
||||
new Segment("IVA", 4)
|
||||
));
|
||||
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
assertFalse(result.hasErrors(),
|
||||
"Kein Fehler erwartet bei Kennzeichen '1' und Betrag '0,00'");
|
||||
}
|
||||
|
||||
// --- Test 2: Kennzeichen "1" + Betrag ungleich "0,00" -> genau ein Fehler STRUCTURE_021 ---
|
||||
|
||||
@Test
|
||||
void validate_kennzeichen1_betragUngleich0Komma00_fehlerSTRUCTURE021() {
|
||||
InputFile inputFile = buildAsvrec(List.of(
|
||||
new Segment("IFA", 2),
|
||||
rea(3, "1", "123,45"),
|
||||
new Segment("IVA", 4)
|
||||
));
|
||||
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
assertTrue(result.hasErrors());
|
||||
assertEquals(1, result.getErrors().size());
|
||||
|
||||
ValidationError error = result.getErrors().get(0);
|
||||
assertEquals("STRUCTURE_021", error.errorCode());
|
||||
assertEquals("ASVREC-Nachricht mit Rechnungskennzeichen '1' muss Rechnungsbetrag '0,00' haben",
|
||||
error.description());
|
||||
assertEquals("REA", error.segmentName());
|
||||
assertEquals(1, error.segmentPosition());
|
||||
assertEquals("123,45", error.actualValue());
|
||||
assertEquals("Rechnungsbetrag muss '0,00' sein bei Rechnungskennzeichen '1'",
|
||||
error.expectedRule());
|
||||
}
|
||||
|
||||
// --- Test 3: Kennzeichen "1" + leerer Betrag -> genau ein Fehler STRUCTURE_021 ---
|
||||
|
||||
@Test
|
||||
void validate_kennzeichen1_betragLeer_fehlerSTRUCTURE021() {
|
||||
InputFile inputFile = buildAsvrec(List.of(
|
||||
new Segment("IFA", 2),
|
||||
rea(3, "1", ""),
|
||||
new Segment("IVA", 4)
|
||||
));
|
||||
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
assertTrue(result.hasErrors());
|
||||
long count021 = result.getErrors().stream()
|
||||
.filter(e -> "STRUCTURE_021".equals(e.errorCode()))
|
||||
.count();
|
||||
assertEquals(1, count021, "Genau ein STRUCTURE_021-Fehler erwartet");
|
||||
}
|
||||
|
||||
// --- Test 4: Kennzeichen "0" -> kein Fehler durch diese Regel ---
|
||||
|
||||
@Test
|
||||
void validate_kennzeichen0_keinFehlerDurchDieseRegel() {
|
||||
InputFile inputFile = buildAsvrec(List.of(
|
||||
new Segment("IFA", 2),
|
||||
rea(3, "0", "500,00"),
|
||||
new Segment("IVA", 4),
|
||||
new Segment("DGN", 5),
|
||||
new Segment("LEA", 6)
|
||||
));
|
||||
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
assertFalse(result.getErrors().stream()
|
||||
.anyMatch(e -> "STRUCTURE_021".equals(e.errorCode())),
|
||||
"Kein STRUCTURE_021-Fehler erwartet bei Kennzeichen '0'");
|
||||
}
|
||||
|
||||
// --- Test 5: ASVFEH -> kein Fehler durch diese Regel ---
|
||||
|
||||
@Test
|
||||
void validate_asvfeh_keinFehlerDurchDieseRegel() {
|
||||
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(new Field(1, "1"), new Field(2, "999,99")));
|
||||
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));
|
||||
InputFile inputFile = new InputFile("test.txt", List.of(message));
|
||||
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
assertFalse(result.getErrors().stream()
|
||||
.anyMatch(e -> "STRUCTURE_021".equals(e.errorCode())),
|
||||
"Kein STRUCTURE_021-Fehler erwartet für ASVFEH");
|
||||
}
|
||||
|
||||
// --- Test 6: REA fehlt -> kein zusätzlicher Fehler durch diese Regel ---
|
||||
|
||||
@Test
|
||||
void validate_reaFehlt_keinFehlerDurchDieseRegel() {
|
||||
// REA fehlt -> STRUCTURE_014 wird erzeugt, aber kein STRUCTURE_021
|
||||
InputFile inputFile = buildAsvrec(List.of(
|
||||
new Segment("IFA", 2),
|
||||
new Segment("IVA", 3)
|
||||
));
|
||||
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
assertFalse(result.getErrors().stream()
|
||||
.anyMatch(e -> "STRUCTURE_021".equals(e.errorCode())),
|
||||
"Kein STRUCTURE_021-Fehler erwartet wenn REA fehlt");
|
||||
}
|
||||
|
||||
// --- Test 7: REA ohne Betragsfeld -> kein zusätzlicher Fehler durch diese Regel ---
|
||||
|
||||
@Test
|
||||
void validate_reaOhneBetragsfeld_keinFehlerDurchDieseRegel() {
|
||||
// REA hat nur Kennzeichen "1", kein Betragsfeld -> nicht auswertbar -> kein STRUCTURE_021
|
||||
InputFile inputFile = buildAsvrec(List.of(
|
||||
new Segment("IFA", 2),
|
||||
reaOhneBetrag(3, "1"),
|
||||
new Segment("IVA", 4)
|
||||
));
|
||||
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
assertFalse(result.getErrors().stream()
|
||||
.anyMatch(e -> "STRUCTURE_021".equals(e.errorCode())),
|
||||
"Kein STRUCTURE_021-Fehler erwartet wenn Betragsfeld fehlt");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* Tests für die Rechnungskennzeichen-Regel in ASVREC-Nachrichten:
|
||||
* - Kennzeichen "0": DGN und LEA müssen vorhanden sein.
|
||||
* - Kennzeichen "1": DGN und LEA dürfen nicht vorhanden sein.
|
||||
*/
|
||||
class DefaultStructureValidatorAsvrecRechnungskennzeichenTest {
|
||||
|
||||
private DefaultStructureValidator validator;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
validator = new DefaultStructureValidator();
|
||||
}
|
||||
|
||||
// --- Hilfsmethoden ---
|
||||
|
||||
/**
|
||||
* Erstellt eine minimale ASVREC-Nachricht mit den angegebenen Segmenten.
|
||||
* UNH ist an Position 1, UNT an der letzten Position.
|
||||
* Die Segmentanzahl im UNT wird automatisch gesetzt.
|
||||
*/
|
||||
private InputFile buildAsvrec(List<Segment> innerSegments) {
|
||||
// UNH an Position 1
|
||||
Segment unh = new Segment("UNH", 1,
|
||||
List.of(new Field(1, "12345"), new Field(2, "ASVREC:D:03B:UN:EAN008")));
|
||||
|
||||
// UNT an letzter Position
|
||||
int untPos = 2 + innerSegments.size();
|
||||
// Gesamtanzahl: UNH + innerSegments + UNT
|
||||
int totalSegments = 1 + innerSegments.size() + 1;
|
||||
Segment unt = new Segment("UNT", untPos,
|
||||
List.of(new Field(1, String.valueOf(totalSegments)), new Field(2, "12345")));
|
||||
|
||||
var allSegments = new java.util.ArrayList<Segment>();
|
||||
allSegments.add(unh);
|
||||
allSegments.addAll(innerSegments);
|
||||
allSegments.add(unt);
|
||||
|
||||
Message message = new Message(1, allSegments);
|
||||
return new InputFile("test.txt", List.of(message));
|
||||
}
|
||||
|
||||
/** Erstellt ein REA-Segment mit dem angegebenen Rechnungskennzeichen. */
|
||||
private Segment rea(int pos, String kennzeichen) {
|
||||
return new Segment("REA", pos, List.of(new Field(1, kennzeichen)));
|
||||
}
|
||||
|
||||
// --- Test 1: Kennzeichen "0" + DGN und LEA vorhanden -> kein neuer Fehler ---
|
||||
|
||||
@Test
|
||||
void validate_kennzeichen0_dgnUndLeaVorhanden_keinFehler() {
|
||||
// IFA(2), REA(3, "0"), IVA(4), DGN(5), LEA(6)
|
||||
InputFile inputFile = buildAsvrec(List.of(
|
||||
new Segment("IFA", 2),
|
||||
rea(3, "0"),
|
||||
new Segment("IVA", 4),
|
||||
new Segment("DGN", 5),
|
||||
new Segment("LEA", 6)
|
||||
));
|
||||
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
assertFalse(result.hasErrors(),
|
||||
"Kein Fehler erwartet bei Kennzeichen '0' mit DGN und LEA vorhanden");
|
||||
}
|
||||
|
||||
// --- Test 2: Kennzeichen "0" + fehlendes DGN -> genau ein Fehler STRUCTURE_017 ---
|
||||
|
||||
@Test
|
||||
void validate_kennzeichen0_dgnFehlt_fehlerSTRUCTURE017() {
|
||||
// IFA(2), REA(3, "0"), IVA(4), LEA(5) – kein DGN
|
||||
InputFile inputFile = buildAsvrec(List.of(
|
||||
new Segment("IFA", 2),
|
||||
rea(3, "0"),
|
||||
new Segment("IVA", 4),
|
||||
new Segment("LEA", 5)
|
||||
));
|
||||
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
assertTrue(result.hasErrors());
|
||||
assertEquals(1, result.getErrors().size());
|
||||
|
||||
ValidationError error = result.getErrors().get(0);
|
||||
assertEquals("STRUCTURE_017", error.errorCode());
|
||||
assertEquals("ASVREC-Nachricht mit Rechnungskennzeichen '0' muss ein DGN-Segment enthalten",
|
||||
error.description());
|
||||
assertEquals("REA", error.segmentName());
|
||||
assertEquals(1, error.segmentPosition());
|
||||
assertEquals("DGN-Segment erforderlich bei Rechnungskennzeichen '0'", error.expectedRule());
|
||||
}
|
||||
|
||||
// --- Test 3: Kennzeichen "0" + fehlendes LEA -> genau ein Fehler STRUCTURE_018 ---
|
||||
|
||||
@Test
|
||||
void validate_kennzeichen0_leaFehlt_fehlerSTRUCTURE018() {
|
||||
// IFA(2), REA(3, "0"), IVA(4), DGN(5) – kein LEA
|
||||
InputFile inputFile = buildAsvrec(List.of(
|
||||
new Segment("IFA", 2),
|
||||
rea(3, "0"),
|
||||
new Segment("IVA", 4),
|
||||
new Segment("DGN", 5)
|
||||
));
|
||||
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
assertTrue(result.hasErrors());
|
||||
assertEquals(1, result.getErrors().size());
|
||||
|
||||
ValidationError error = result.getErrors().get(0);
|
||||
assertEquals("STRUCTURE_018", error.errorCode());
|
||||
assertEquals("ASVREC-Nachricht mit Rechnungskennzeichen '0' muss ein LEA-Segment enthalten",
|
||||
error.description());
|
||||
assertEquals("REA", error.segmentName());
|
||||
assertEquals(1, error.segmentPosition());
|
||||
assertEquals("LEA-Segment erforderlich bei Rechnungskennzeichen '0'", error.expectedRule());
|
||||
}
|
||||
|
||||
// --- Test 4: Kennzeichen "1" + vorhandenes DGN -> genau ein Fehler STRUCTURE_019 ---
|
||||
|
||||
@Test
|
||||
void validate_kennzeichen1_dgnVorhanden_fehlerSTRUCTURE019() {
|
||||
// IFA(2), REA(3, "1"), IVA(4), DGN(5) – kein LEA
|
||||
InputFile inputFile = buildAsvrec(List.of(
|
||||
new Segment("IFA", 2),
|
||||
rea(3, "1"),
|
||||
new Segment("IVA", 4),
|
||||
new Segment("DGN", 5)
|
||||
));
|
||||
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
assertTrue(result.hasErrors());
|
||||
assertEquals(1, result.getErrors().size());
|
||||
|
||||
ValidationError error = result.getErrors().get(0);
|
||||
assertEquals("STRUCTURE_019", error.errorCode());
|
||||
assertEquals("ASVREC-Nachricht mit Rechnungskennzeichen '1' darf kein DGN-Segment enthalten",
|
||||
error.description());
|
||||
assertEquals("REA", error.segmentName());
|
||||
assertEquals(1, error.segmentPosition());
|
||||
assertEquals("DGN-Segment verboten bei Rechnungskennzeichen '1'", error.expectedRule());
|
||||
}
|
||||
|
||||
// --- Test 5: Kennzeichen "1" + vorhandenes LEA -> genau ein Fehler STRUCTURE_020 ---
|
||||
|
||||
@Test
|
||||
void validate_kennzeichen1_leaVorhanden_fehlerSTRUCTURE020() {
|
||||
// IFA(2), REA(3, "1"), IVA(4), LEA(5) – kein DGN
|
||||
InputFile inputFile = buildAsvrec(List.of(
|
||||
new Segment("IFA", 2),
|
||||
rea(3, "1"),
|
||||
new Segment("IVA", 4),
|
||||
new Segment("LEA", 5)
|
||||
));
|
||||
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
assertTrue(result.hasErrors());
|
||||
assertEquals(1, result.getErrors().size());
|
||||
|
||||
ValidationError error = result.getErrors().get(0);
|
||||
assertEquals("STRUCTURE_020", error.errorCode());
|
||||
assertEquals("ASVREC-Nachricht mit Rechnungskennzeichen '1' darf kein LEA-Segment enthalten",
|
||||
error.description());
|
||||
assertEquals("REA", error.segmentName());
|
||||
assertEquals(1, error.segmentPosition());
|
||||
assertEquals("LEA-Segment verboten bei Rechnungskennzeichen '1'", error.expectedRule());
|
||||
}
|
||||
|
||||
// --- Test 6: Kennzeichen "1" + weder DGN noch LEA -> kein Fehler ---
|
||||
|
||||
@Test
|
||||
void validate_kennzeichen1_wederDgnNochLea_keinFehler() {
|
||||
// IFA(2), REA(3, "1"), IVA(4) – kein DGN, kein LEA
|
||||
InputFile inputFile = buildAsvrec(List.of(
|
||||
new Segment("IFA", 2),
|
||||
rea(3, "1"),
|
||||
new Segment("IVA", 4)
|
||||
));
|
||||
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
assertFalse(result.hasErrors(),
|
||||
"Kein Fehler erwartet bei Kennzeichen '1' ohne DGN und LEA");
|
||||
}
|
||||
|
||||
// --- Test 7: ASVFEH -> kein Fehler durch diese Regel ---
|
||||
|
||||
@Test
|
||||
void validate_asvfeh_keineRechnungskennzeichenRegel() {
|
||||
// ASVFEH mit REA Kennzeichen "0" 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(new Field(1, "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));
|
||||
InputFile inputFile = new InputFile("test.txt", List.of(message));
|
||||
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
assertFalse(result.getErrors().stream()
|
||||
.anyMatch(e -> "STRUCTURE_017".equals(e.errorCode())
|
||||
|| "STRUCTURE_018".equals(e.errorCode())
|
||||
|| "STRUCTURE_019".equals(e.errorCode())
|
||||
|| "STRUCTURE_020".equals(e.errorCode())),
|
||||
"Kein Rechnungskennzeichen-Fehler erwartet für ASVFEH");
|
||||
assertFalse(result.hasErrors(), "Kein Fehler erwartet für ASVFEH");
|
||||
}
|
||||
|
||||
// --- Test 8: REA ohne Felder -> kein neuer Fehler ---
|
||||
|
||||
@Test
|
||||
void validate_reaOhneFelder_keinNeuerFehler() {
|
||||
// REA ohne Felder -> Kennzeichen nicht auswertbar -> kein neuer Fehler
|
||||
InputFile inputFile = buildAsvrec(List.of(
|
||||
new Segment("IFA", 2),
|
||||
new Segment("REA", 3), // kein Feld
|
||||
new Segment("IVA", 4)
|
||||
));
|
||||
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
assertFalse(result.getErrors().stream()
|
||||
.anyMatch(e -> "STRUCTURE_017".equals(e.errorCode())
|
||||
|| "STRUCTURE_018".equals(e.errorCode())
|
||||
|| "STRUCTURE_019".equals(e.errorCode())
|
||||
|| "STRUCTURE_020".equals(e.errorCode())),
|
||||
"Kein Rechnungskennzeichen-Fehler erwartet wenn REA keine Felder hat");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* Tests für die Reihenfolgeregel der Pflichtsegmente in ASVREC-Nachrichten:
|
||||
* IFA muss vor REA stehen, REA muss vor IVA stehen.
|
||||
*/
|
||||
class DefaultStructureValidatorAsvrecSegmentOrderTest {
|
||||
|
||||
private DefaultStructureValidator validator;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
validator = new DefaultStructureValidator();
|
||||
}
|
||||
|
||||
// --- Hilfsmethode ---
|
||||
|
||||
/**
|
||||
* Erstellt eine minimale, strukturell gültige ASVREC-Nachricht mit den angegebenen
|
||||
* Segmentpositionen für IFA, REA und IVA.
|
||||
*/
|
||||
private InputFile buildAsvrec(int ifaPos, int reaPos, int ivaPos) {
|
||||
// UNH an Position 1, UNT an der letzten Position
|
||||
// Segmentanzahl = 5 (UNH, IFA, REA, IVA, UNT)
|
||||
int untPos = Math.max(Math.max(ifaPos, reaPos), ivaPos) + 1;
|
||||
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", ifaPos);
|
||||
Segment rea = new Segment("REA", reaPos);
|
||||
Segment iva = new Segment("IVA", ivaPos);
|
||||
Segment unt = new Segment("UNT", untPos,
|
||||
List.of(new Field(1, String.valueOf(untPos)), new Field(2, "12345")));
|
||||
Message message = new Message(1, List.of(unh, ifa, rea, iva, unt));
|
||||
return new InputFile("test.txt", List.of(message));
|
||||
}
|
||||
|
||||
// --- Test 1: gültiger Fall ---
|
||||
|
||||
@Test
|
||||
void validate_shouldNotReportOrderErrorForAsvrecWithCorrectOrder() {
|
||||
// IFA(2) < REA(3) < IVA(4) -> kein Fehler
|
||||
InputFile inputFile = buildAsvrec(2, 3, 4);
|
||||
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
assertFalse(result.hasErrors(),
|
||||
"Kein Fehler erwartet bei korrekter Reihenfolge IFA < REA < IVA");
|
||||
}
|
||||
|
||||
// --- Test 2: REA vor IFA ---
|
||||
|
||||
@Test
|
||||
void validate_shouldReportExactlyOneErrorWhenReaBeforeIfa() {
|
||||
// REA(2) < IFA(3) < IVA(4) -> IFA nicht vor REA
|
||||
InputFile inputFile = buildAsvrec(3, 2, 4);
|
||||
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
assertTrue(result.hasErrors());
|
||||
assertEquals(1, result.getErrors().size());
|
||||
|
||||
ValidationError error = result.getErrors().get(0);
|
||||
assertEquals("STRUCTURE_016", error.errorCode());
|
||||
assertEquals("In ASVREC-Nachrichten muss IFA vor REA stehen", error.description());
|
||||
assertEquals("IFA/REA", error.segmentName());
|
||||
assertEquals(1, error.segmentPosition());
|
||||
assertEquals("Reihenfolge IFA vor REA vor IVA erforderlich", error.expectedRule());
|
||||
}
|
||||
|
||||
// --- Test 3: IVA vor REA ---
|
||||
|
||||
@Test
|
||||
void validate_shouldReportExactlyOneErrorWhenIvaBeforeRea() {
|
||||
// IFA(2) < IVA(3) < REA(4) -> REA nicht vor IVA
|
||||
InputFile inputFile = buildAsvrec(2, 4, 3);
|
||||
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
assertTrue(result.hasErrors());
|
||||
assertEquals(1, result.getErrors().size());
|
||||
|
||||
ValidationError error = result.getErrors().get(0);
|
||||
assertEquals("STRUCTURE_016", error.errorCode());
|
||||
assertEquals("In ASVREC-Nachrichten muss REA vor IVA stehen", error.description());
|
||||
assertEquals("REA/IVA", error.segmentName());
|
||||
assertEquals(1, error.segmentPosition());
|
||||
assertEquals("Reihenfolge IFA vor REA vor IVA erforderlich", error.expectedRule());
|
||||
}
|
||||
|
||||
// --- Test 4: fehlendes REA -> kein zusätzlicher Reihenfolgefehler ---
|
||||
|
||||
@Test
|
||||
void validate_shouldNotReportOrderErrorWhenReaIsMissing() {
|
||||
// REA fehlt -> Präsenzregel (STRUCTURE_014) greift, aber kein STRUCTURE_016
|
||||
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 iva = new Segment("IVA", 3);
|
||||
Segment unt = new Segment("UNT", 4,
|
||||
List.of(new Field(1, "4"), new Field(2, "12345")));
|
||||
Message message = new Message(1, List.of(unh, ifa, iva, unt));
|
||||
InputFile inputFile = new InputFile("test.txt", List.of(message));
|
||||
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
// Kein STRUCTURE_016-Fehler
|
||||
assertFalse(result.getErrors().stream()
|
||||
.anyMatch(e -> "STRUCTURE_016".equals(e.errorCode())),
|
||||
"Kein Reihenfolgefehler erwartet, wenn REA fehlt");
|
||||
|
||||
// Genau ein Fehler: STRUCTURE_014 (REA fehlt)
|
||||
assertEquals(1, result.getErrors().size());
|
||||
assertEquals("STRUCTURE_014", result.getErrors().get(0).errorCode());
|
||||
}
|
||||
|
||||
// --- Test 5: ASVFEH -> kein Fehler durch diese Regel ---
|
||||
|
||||
@Test
|
||||
void validate_shouldNotReportOrderErrorForAsvfeh() {
|
||||
// ASVFEH mit umgekehrter Reihenfolge der Segmente -> 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));
|
||||
InputFile inputFile = new InputFile("test.txt", List.of(message));
|
||||
|
||||
ValidationResult result = validator.validate(inputFile);
|
||||
|
||||
assertFalse(result.getErrors().stream()
|
||||
.anyMatch(e -> "STRUCTURE_016".equals(e.errorCode())),
|
||||
"Kein Reihenfolgefehler erwartet für ASVFEH");
|
||||
assertFalse(result.hasErrors(),
|
||||
"Kein Fehler erwartet für ASVFEH");
|
||||
}
|
||||
}
|
||||
@@ -32,20 +32,24 @@ class DefaultStructureValidatorTestAdditional {
|
||||
|
||||
@Test
|
||||
void validate_shouldNotReportErrorWhenMessageTypeIsASVREC() {
|
||||
// Given
|
||||
// Given: vollständige ASVREC-Nachricht mit allen Pflichtsegmenten (IFA, REA, IVA).
|
||||
// REA-Feld 1 enthält keinen der Sonderwerte "0" oder "1", damit keine
|
||||
// Rechnungskennzeichen-Regel (STRUCTURE_017–020) greift.
|
||||
Segment unh = new Segment("UNH", 1, List.of(new Field(1, "12345"), new Field(2, "ASVREC: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 ifa = new Segment("IFA", 2, List.of(new Field(1, "IFA-Wert")));
|
||||
Segment rea = new Segment("REA", 3, List.of(new Field(1, "9")));
|
||||
Segment iva = new Segment("IVA", 4, List.of(new Field(1, "IVA-Wert")));
|
||||
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
|
||||
// Should not have STRUCTURE_012 error for valid ASVREC message type
|
||||
// Then: kein STRUCTURE_012-Fehler für gültigen ASVREC-Nachrichtentyp
|
||||
assertFalse(result.getErrors().stream()
|
||||
.anyMatch(error -> "STRUCTURE_012".equals(error.errorCode())));
|
||||
assertFalse(result.hasErrors(), "There should be no validation errors for valid ASVREC message type");
|
||||
assertFalse(result.hasErrors(), "Keine Validierungsfehler für gültige ASVREC-Nachricht erwartet");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user