Compare commits
No commits in common. "ea61d6c301b3a9416aceac049ac0d1a2f4f4924c" and "16ef33a1c9aeb46f2552b161eed7f2d6d95b1b51" have entirely different histories.
ea61d6c301
...
16ef33a1c9
|
|
@ -1,498 +0,0 @@
|
||||||
package de.hsmannheim.faktura.dokumente.service;
|
|
||||||
|
|
||||||
import de.hsmannheim.faktura.dokumente.domain.*;
|
|
||||||
import de.hsmannheim.faktura.dokumente.exception.*;
|
|
||||||
import de.hsmannheim.faktura.dokumente.export.PdfExportService;
|
|
||||||
import de.hsmannheim.faktura.dokumente.export.SimplePdfExportService;
|
|
||||||
import de.hsmannheim.faktura.dokumente.lookup.*;
|
|
||||||
import de.hsmannheim.faktura.dokumente.repository.InMemoryDokumentRepository;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.*;
|
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Modultestplan für den Dokumentenprozess (Gruppe F).
|
|
||||||
* Alle 16 Testfälle MT-DP-01 bis MT-DP-16 aus dem Pflichtenheft.
|
|
||||||
*
|
|
||||||
* Keine JavaFX-Abhängigkeit – alle Tests laufen ohne GUI-Start (PH-DP-16, PH-NF-DP-05).
|
|
||||||
*/
|
|
||||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
|
||||||
class DokumentServiceTest {
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Test-Infrastruktur: Fake-Implementierungen
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/** Fake-Produktverwaltung mit festen Testprodukten (MT-DP-01). */
|
|
||||||
static class FakeProductLookup implements ProductLookup {
|
|
||||||
private final Map<Long, ProductDto> produkte = new HashMap<>();
|
|
||||||
|
|
||||||
FakeProductLookup() {
|
|
||||||
produkte.put(20001L, new ProductDto(20001L, "Produkt Alpha",
|
|
||||||
new BigDecimal("100.00"), new BigDecimal("0.19")));
|
|
||||||
produkte.put(20002L, new ProductDto(20002L, "Produkt Beta",
|
|
||||||
new BigDecimal("50.00"), new BigDecimal("0.07")));
|
|
||||||
produkte.put(20003L, new ProductDto(20003L, "Produkt Gamma",
|
|
||||||
new BigDecimal("200.00"), new BigDecimal("0.19")));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<ProductDto> findProduct(long produktId) {
|
|
||||||
return Optional.ofNullable(produkte.get(produktId));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Ändert den Preis eines Produkts – für MT-DP-13 (Snapshot-Test). */
|
|
||||||
void setztePreis(long produktId, BigDecimal neuerPreis) {
|
|
||||||
ProductDto alt = produkte.get(produktId);
|
|
||||||
if (alt != null) {
|
|
||||||
produkte.put(produktId, new ProductDto(
|
|
||||||
alt.getProduktId(), alt.getBezeichnung(), neuerPreis, alt.getMwstSatz()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Fake-Kundenverwaltung mit festen Testkunden. */
|
|
||||||
static class FakeCustomerLookup implements CustomerLookup {
|
|
||||||
private final Map<Long, CustomerDto> kunden = new HashMap<>();
|
|
||||||
|
|
||||||
FakeCustomerLookup() {
|
|
||||||
kunden.put(10001L, new CustomerDto(10001L, "Max Mustermann",
|
|
||||||
"Musterweg 5, 68159 Mannheim", "DE123456789"));
|
|
||||||
kunden.put(10002L, new CustomerDto(10002L, "Erika Muster",
|
|
||||||
"Beispielgasse 3, 68161 Mannheim", null));
|
|
||||||
// Kunde 10002 hat keine Steuernummer → für MT-DP-10 (fehlende Pflichtangaben)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<CustomerDto> findCustomer(long kundenId) {
|
|
||||||
return Optional.ofNullable(kunden.get(kundenId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Setup
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
private InMemoryDokumentRepository repository;
|
|
||||||
private FakeProductLookup productLookup;
|
|
||||||
private FakeCustomerLookup customerLookup;
|
|
||||||
private DokumentService service;
|
|
||||||
|
|
||||||
@TempDir
|
|
||||||
Path tempDir;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void setUp() {
|
|
||||||
repository = new InMemoryDokumentRepository();
|
|
||||||
productLookup = new FakeProductLookup();
|
|
||||||
customerLookup = new FakeCustomerLookup();
|
|
||||||
PdfExportService pdfExport = new SimplePdfExportService(tempDir);
|
|
||||||
service = new DokumentServiceImpl(repository, productLookup, customerLookup, pdfExport);
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
// MT-DP-01: Angebot anlegen – Happy Path (PH-DP-01)
|
|
||||||
// =========================================================================
|
|
||||||
@Test
|
|
||||||
@Order(1)
|
|
||||||
@DisplayName("MT-DP-01: Angebot anlegen – Happy Path")
|
|
||||||
void testAngebotAnlegen_HappyPath() {
|
|
||||||
List<PositionCommand> positionen = List.of(
|
|
||||||
new PositionCommand(20001L, 2),
|
|
||||||
new PositionCommand(20002L, 1)
|
|
||||||
);
|
|
||||||
|
|
||||||
Angebot angebot = service.erstelleAngebot(10001L, positionen);
|
|
||||||
|
|
||||||
assertNotNull(angebot, "Angebot darf nicht null sein");
|
|
||||||
assertTrue(angebot.getDokumentNummer().startsWith("A-2026-"),
|
|
||||||
"Nummer muss mit A-2026- beginnen, war: " + angebot.getDokumentNummer());
|
|
||||||
assertEquals(DokumentStatus.OFFEN, angebot.getStatus(), "Status muss OFFEN sein");
|
|
||||||
assertEquals(2, angebot.getPositionen().size(), "Muss genau 2 Positionen haben");
|
|
||||||
assertEquals(10001L, angebot.getKundenId(), "Kunden-ID muss stimmen");
|
|
||||||
assertNotNull(angebot.getDatum(), "Datum darf nicht null sein");
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
// MT-DP-02: Summenberechnung Angebot (PH-DP-01, PH-DP-08)
|
|
||||||
// =========================================================================
|
|
||||||
@Test
|
|
||||||
@Order(2)
|
|
||||||
@DisplayName("MT-DP-02: Summenberechnung – Netto 250,00, USt 41,50, Brutto 291,50")
|
|
||||||
void testSummenberechnung_Angebot() {
|
|
||||||
// 2 x 100,00 EUR mit 19%, 1 x 50,00 EUR mit 7%
|
|
||||||
List<PositionCommand> positionen = List.of(
|
|
||||||
new PositionCommand(20001L, 2), // 2 x 100 @ 19%
|
|
||||||
new PositionCommand(20002L, 1) // 1 x 50 @ 7%
|
|
||||||
);
|
|
||||||
|
|
||||||
Angebot angebot = service.erstelleAngebot(10001L, positionen);
|
|
||||||
|
|
||||||
assertEquals(0, new BigDecimal("250.00").compareTo(angebot.getNettoSumme()),
|
|
||||||
"Nettosumme muss 250,00 EUR sein, war: " + angebot.getNettoSumme());
|
|
||||||
assertEquals(0, new BigDecimal("41.50").compareTo(angebot.getUstSumme()),
|
|
||||||
"USt-Summe muss 41,50 EUR sein, war: " + angebot.getUstSumme());
|
|
||||||
assertEquals(0, new BigDecimal("291.50").compareTo(angebot.getBruttoSumme()),
|
|
||||||
"Bruttosumme muss 291,50 EUR sein, war: " + angebot.getBruttoSumme());
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
// MT-DP-03: Fortlaufende Angebotsnummern (PH-DP-02)
|
|
||||||
// =========================================================================
|
|
||||||
@Test
|
|
||||||
@Order(3)
|
|
||||||
@DisplayName("MT-DP-03: Angebotsnummern sind fortlaufend und eindeutig")
|
|
||||||
void testFortlaufendeAngebotsnummern() {
|
|
||||||
List<PositionCommand> pos = List.of(new PositionCommand(20001L, 1));
|
|
||||||
|
|
||||||
Angebot a1 = service.erstelleAngebot(10001L, pos);
|
|
||||||
Angebot a2 = service.erstelleAngebot(10001L, pos);
|
|
||||||
|
|
||||||
assertNotEquals(a1.getDokumentNummer(), a2.getDokumentNummer(),
|
|
||||||
"Nummern müssen unterschiedlich sein");
|
|
||||||
assertTrue(a1.getDokumentNummer().endsWith("0001"),
|
|
||||||
"Erste Nummer muss auf 0001 enden, war: " + a1.getDokumentNummer());
|
|
||||||
assertTrue(a2.getDokumentNummer().endsWith("0002"),
|
|
||||||
"Zweite Nummer muss auf 0002 enden, war: " + a2.getDokumentNummer());
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
// MT-DP-04: Angebot nach Überführung sperren (PH-DP-03)
|
|
||||||
// =========================================================================
|
|
||||||
@Test
|
|
||||||
@Order(4)
|
|
||||||
@DisplayName("MT-DP-04: Überführtes Angebot kann nicht mehr bearbeitet werden")
|
|
||||||
void testAngebotNachUeberfuehrungGesperrt() {
|
|
||||||
List<PositionCommand> pos = List.of(new PositionCommand(20001L, 1));
|
|
||||||
Angebot angebot = service.erstelleAngebot(10001L, pos);
|
|
||||||
service.erstelleAuftragsbestaetigung(angebot.getDokumentNummer());
|
|
||||||
|
|
||||||
// Versuch Angebot nach Überführung zu bearbeiten → IllegalStateException
|
|
||||||
assertThrows(IllegalStateException.class,
|
|
||||||
() -> service.aktualisiereAngebot(angebot.getDokumentNummer(), pos),
|
|
||||||
"Bearbeitung eines überführten Angebots muss IllegalStateException werfen");
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
// MT-DP-05: Auftragsbestätigung erzeugen (PH-DP-04)
|
|
||||||
// =========================================================================
|
|
||||||
@Test
|
|
||||||
@Order(5)
|
|
||||||
@DisplayName("MT-DP-05: Auftragsbestätigung aus offenem Angebot erzeugen")
|
|
||||||
void testAuftragsbestaetigung_Erzeugen() {
|
|
||||||
List<PositionCommand> pos = List.of(
|
|
||||||
new PositionCommand(20001L, 2),
|
|
||||||
new PositionCommand(20002L, 1)
|
|
||||||
);
|
|
||||||
Angebot angebot = service.erstelleAngebot(10001L, pos);
|
|
||||||
|
|
||||||
Auftragsbestaetigung ab = service.erstelleAuftragsbestaetigung(angebot.getDokumentNummer());
|
|
||||||
|
|
||||||
assertNotNull(ab, "AB darf nicht null sein");
|
|
||||||
assertTrue(ab.getDokumentNummer().startsWith("AB-"),
|
|
||||||
"AB-Nummer muss mit AB- beginnen");
|
|
||||||
assertEquals(angebot.getDokumentNummer(), ab.getAngebotsNummer(),
|
|
||||||
"AB muss Angebotsnummer referenzieren");
|
|
||||||
assertEquals(2, ab.getPositionen().size(),
|
|
||||||
"AB muss dieselbe Anzahl Positionen wie Angebot haben");
|
|
||||||
assertEquals(DokumentStatus.UEBERFUEHRT,
|
|
||||||
((Angebot) repository.findByNummer(angebot.getDokumentNummer()).get()).getStatus(),
|
|
||||||
"Angebot muss nach AB-Erstellung auf UEBERFUEHRT stehen");
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
// MT-DP-06: Doppelte AB verhindern (PH-DP-05)
|
|
||||||
// =========================================================================
|
|
||||||
@Test
|
|
||||||
@Order(6)
|
|
||||||
@DisplayName("MT-DP-06: Zweite AB für dasselbe Angebot wird abgelehnt")
|
|
||||||
void testDoppelteAuftragsbestaetigung_Verhindern() {
|
|
||||||
List<PositionCommand> pos = List.of(new PositionCommand(20001L, 1));
|
|
||||||
Angebot angebot = service.erstelleAngebot(10001L, pos);
|
|
||||||
service.erstelleAuftragsbestaetigung(angebot.getDokumentNummer());
|
|
||||||
|
|
||||||
// Zweiter Versuch → DuplicateDocumentException
|
|
||||||
assertThrows(DuplicateDocumentException.class,
|
|
||||||
() -> service.erstelleAuftragsbestaetigung(angebot.getDokumentNummer()),
|
|
||||||
"Zweite AB für dasselbe Angebot muss DuplicateDocumentException werfen");
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
// MT-DP-07: Lieferschein erzeugen (PH-DP-06)
|
|
||||||
// =========================================================================
|
|
||||||
@Test
|
|
||||||
@Order(7)
|
|
||||||
@DisplayName("MT-DP-07: Lieferschein aus bestätigter AB erzeugen")
|
|
||||||
void testLieferschein_Erzeugen() {
|
|
||||||
List<PositionCommand> pos = List.of(new PositionCommand(20001L, 3));
|
|
||||||
Angebot angebot = service.erstelleAngebot(10001L, pos);
|
|
||||||
Auftragsbestaetigung ab = service.erstelleAuftragsbestaetigung(angebot.getDokumentNummer());
|
|
||||||
LocalDate lieferdatum = LocalDate.now().plusDays(3);
|
|
||||||
|
|
||||||
Lieferschein ls = service.erstelleLieferschein(ab.getDokumentNummer(), lieferdatum);
|
|
||||||
|
|
||||||
assertNotNull(ls, "Lieferschein darf nicht null sein");
|
|
||||||
assertTrue(ls.getDokumentNummer().startsWith("LS-"),
|
|
||||||
"LS-Nummer muss mit LS- beginnen");
|
|
||||||
assertEquals(ab.getDokumentNummer(), ls.getAuftragsNummer(),
|
|
||||||
"Lieferschein muss AB-Nummer referenzieren");
|
|
||||||
assertEquals(lieferdatum, ls.getLieferdatum(), "Lieferdatum muss übereinstimmen");
|
|
||||||
assertEquals(DokumentStatus.GELIEFERT, ls.getStatus(), "Status muss GELIEFERT sein");
|
|
||||||
assertEquals(1, ls.getPositionen().size(), "Muss 1 Position enthalten");
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
// MT-DP-08: Lieferschein enthält keine Preise (PH-DP-07)
|
|
||||||
// =========================================================================
|
|
||||||
@Test
|
|
||||||
@Order(8)
|
|
||||||
@DisplayName("MT-DP-08: Lieferschein-Export enthält keine Preisfelder")
|
|
||||||
void testLieferschein_EnthältKeinePreise() throws Exception {
|
|
||||||
List<PositionCommand> pos = List.of(new PositionCommand(20001L, 2));
|
|
||||||
Angebot angebot = service.erstelleAngebot(10001L, pos);
|
|
||||||
Auftragsbestaetigung ab = service.erstelleAuftragsbestaetigung(angebot.getDokumentNummer());
|
|
||||||
Lieferschein ls = service.erstelleLieferschein(ab.getDokumentNummer(), LocalDate.now());
|
|
||||||
|
|
||||||
// PDF exportieren und Inhalt prüfen
|
|
||||||
Path pdfPfad = service.exportiereAlsPdf(ls.getDokumentNummer());
|
|
||||||
String inhalt = Files.readString(pdfPfad);
|
|
||||||
|
|
||||||
assertTrue(inhalt.contains("Produkt Alpha"), "Bezeichnung muss enthalten sein");
|
|
||||||
assertFalse(inhalt.contains("Nettosumme:"), "Nettosumme darf nicht enthalten sein");
|
|
||||||
assertFalse(inhalt.contains("Bruttosumme:"), "Bruttosumme darf nicht enthalten sein");
|
|
||||||
assertFalse(inhalt.contains("USt:"), "USt-Betrag darf nicht enthalten sein");
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
// MT-DP-09: Rechnung erzeugen – Happy Path (PH-DP-08, PH-DP-09)
|
|
||||||
// =========================================================================
|
|
||||||
@Test
|
|
||||||
@Order(9)
|
|
||||||
@DisplayName("MT-DP-09: Rechnung aus Lieferschein erzeugen (Happy Path)")
|
|
||||||
void testRechnung_Erzeugen_HappyPath() {
|
|
||||||
Lieferschein ls = erstelleKompletteDokumentenkette(10001L);
|
|
||||||
|
|
||||||
Rechnung rechnung = service.erstelleRechnung(ls.getDokumentNummer());
|
|
||||||
|
|
||||||
assertAll("Rechnung muss vollständig sein",
|
|
||||||
() -> assertNotNull(rechnung, "Rechnung darf nicht null sein"),
|
|
||||||
() -> assertTrue(rechnung.getDokumentNummer().startsWith("R-"),
|
|
||||||
"Rechnungsnummer muss mit R- beginnen"),
|
|
||||||
() -> assertEquals(DokumentStatus.FESTGESCHRIEBEN, rechnung.getStatus(),
|
|
||||||
"Status muss FESTGESCHRIEBEN sein"),
|
|
||||||
() -> assertTrue(rechnung.isFestgeschrieben(), "Rechnung muss festgeschrieben sein"),
|
|
||||||
() -> assertNotNull(rechnung.getPflichtangaben(), "Pflichtangaben dürfen nicht null sein"),
|
|
||||||
() -> assertEquals(0, new BigDecimal("250.00").compareTo(rechnung.getNettoSumme()),
|
|
||||||
"Nettosumme muss 250,00 EUR sein"),
|
|
||||||
() -> assertEquals(0, new BigDecimal("291.50").compareTo(rechnung.getBruttoSumme()),
|
|
||||||
"Bruttosumme muss 291,50 EUR sein")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
// MT-DP-10: Pflichtangaben fehlen (PH-DP-09)
|
|
||||||
// =========================================================================
|
|
||||||
@Test
|
|
||||||
@Order(10)
|
|
||||||
@DisplayName("MT-DP-10: Fehlende Pflichtangaben verhindern Rechnungserstellung")
|
|
||||||
void testRechnung_PflichtangabenFehlen() {
|
|
||||||
// Wir manipulieren den CustomerLookup so, dass er einen Kunden
|
|
||||||
// mit leerer Anschrift zurückgibt → Validierung schlägt fehl
|
|
||||||
CustomerLookup schlechterLookup = id ->
|
|
||||||
Optional.of(new CustomerDto(id, "", "", null));
|
|
||||||
|
|
||||||
DokumentService serviceOhneKunde = new DokumentServiceImpl(
|
|
||||||
repository, productLookup, schlechterLookup, new SimplePdfExportService(tempDir));
|
|
||||||
|
|
||||||
List<PositionCommand> pos = List.of(new PositionCommand(20001L, 2),
|
|
||||||
new PositionCommand(20002L, 1));
|
|
||||||
|
|
||||||
// Erst Angebot → AB → LS aufbauen
|
|
||||||
Angebot angebot = serviceOhneKunde.erstelleAngebot(10001L, pos);
|
|
||||||
Auftragsbestaetigung ab = serviceOhneKunde.erstelleAuftragsbestaetigung(
|
|
||||||
angebot.getDokumentNummer());
|
|
||||||
Lieferschein ls = serviceOhneKunde.erstelleLieferschein(
|
|
||||||
ab.getDokumentNummer(), LocalDate.now());
|
|
||||||
|
|
||||||
// Rechnung erstellen → soll ValidationException werfen
|
|
||||||
assertThrows(ValidationException.class,
|
|
||||||
() -> serviceOhneKunde.erstelleRechnung(ls.getDokumentNummer()),
|
|
||||||
"Rechnung mit fehlenden Pflichtangaben muss ValidationException werfen");
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
// MT-DP-11: Rechnungsnummern lückenlos (PH-DP-10)
|
|
||||||
// =========================================================================
|
|
||||||
@Test
|
|
||||||
@Order(11)
|
|
||||||
@DisplayName("MT-DP-11: Drei Rechnungen erhalten Nummern 0001, 0002, 0003")
|
|
||||||
void testRechnungsnummern_Lueckenlos() {
|
|
||||||
List<PositionCommand> pos = List.of(
|
|
||||||
new PositionCommand(20001L, 2),
|
|
||||||
new PositionCommand(20002L, 1)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Drei vollständige Dokumentenketten erzeugen
|
|
||||||
List<String> rechnungsNummern = new ArrayList<>();
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
Lieferschein ls = erstelleKompletteDokumentenkette(10001L);
|
|
||||||
Rechnung rechnung = service.erstelleRechnung(ls.getDokumentNummer());
|
|
||||||
rechnungsNummern.add(rechnung.getDokumentNummer());
|
|
||||||
}
|
|
||||||
|
|
||||||
assertTrue(rechnungsNummern.get(0).endsWith("0001"),
|
|
||||||
"Erste Rechnung muss auf 0001 enden, war: " + rechnungsNummern.get(0));
|
|
||||||
assertTrue(rechnungsNummern.get(1).endsWith("0002"),
|
|
||||||
"Zweite Rechnung muss auf 0002 enden, war: " + rechnungsNummern.get(1));
|
|
||||||
assertTrue(rechnungsNummern.get(2).endsWith("0003"),
|
|
||||||
"Dritte Rechnung muss auf 0003 enden, war: " + rechnungsNummern.get(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
// MT-DP-12: Rechnung unveränderlich (PH-DP-11)
|
|
||||||
// =========================================================================
|
|
||||||
@Test
|
|
||||||
@Order(12)
|
|
||||||
@DisplayName("MT-DP-12: Festgeschriebene Rechnung kann nicht mehr geändert werden")
|
|
||||||
void testRechnung_Unveraenderlich() {
|
|
||||||
Lieferschein ls = erstelleKompletteDokumentenkette(10001L);
|
|
||||||
Rechnung rechnung = service.erstelleRechnung(ls.getDokumentNummer());
|
|
||||||
|
|
||||||
assertTrue(rechnung.isFestgeschrieben(), "Rechnung muss festgeschrieben sein");
|
|
||||||
|
|
||||||
// Versuch Positionen zu ändern → IllegalStateException
|
|
||||||
assertThrows(IllegalStateException.class,
|
|
||||||
() -> rechnung.pruefeAenderbar(),
|
|
||||||
"Änderung einer festgeschriebenen Rechnung muss IllegalStateException werfen");
|
|
||||||
|
|
||||||
// Rechnung erneut aus Repository laden → unverändert
|
|
||||||
Rechnung geladen = (Rechnung) repository.findByNummer(rechnung.getDokumentNummer()).get();
|
|
||||||
assertTrue(geladen.isFestgeschrieben(), "Geladene Rechnung muss noch festgeschrieben sein");
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
// MT-DP-13: Preis-Snapshot bleibt stabil (PH-DP-14)
|
|
||||||
// =========================================================================
|
|
||||||
@Test
|
|
||||||
@Order(13)
|
|
||||||
@DisplayName("MT-DP-13: Spätere Produktpreisänderung beeinflusst bestehende Angebote nicht")
|
|
||||||
void testPreisSnapshot_BleibtStabil() {
|
|
||||||
// Angebot mit Preis 100,00 EUR erzeugen
|
|
||||||
List<PositionCommand> pos = List.of(new PositionCommand(20001L, 1));
|
|
||||||
Angebot angebot = service.erstelleAngebot(10001L, pos);
|
|
||||||
|
|
||||||
BigDecimal preisImAngebot = angebot.getPositionen().get(0).getEinzelpreisNettoSnapshot();
|
|
||||||
assertEquals(0, new BigDecimal("100.00").compareTo(preisImAngebot),
|
|
||||||
"Preis im Angebot muss 100,00 EUR sein");
|
|
||||||
|
|
||||||
// Produktpreis ändern (Snapshot darf nicht beeinflusst werden)
|
|
||||||
productLookup.setztePreis(20001L, new BigDecimal("120.00"));
|
|
||||||
|
|
||||||
// Angebot erneut aus Repository laden
|
|
||||||
Angebot geladen = (Angebot) repository.findByNummer(angebot.getDokumentNummer()).get();
|
|
||||||
BigDecimal preisNachAenderung = geladen.getPositionen().get(0).getEinzelpreisNettoSnapshot();
|
|
||||||
|
|
||||||
assertEquals(0, new BigDecimal("100.00").compareTo(preisNachAenderung),
|
|
||||||
"Snapshot-Preis muss weiterhin 100,00 EUR sein (nicht 120,00)");
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
// MT-DP-14: PDF-Export erzeugt Datei (PH-DP-12)
|
|
||||||
// =========================================================================
|
|
||||||
@Test
|
|
||||||
@Order(14)
|
|
||||||
@DisplayName("MT-DP-14: PDF-Export erzeugt vorhandene, nicht-leere Datei")
|
|
||||||
void testPdfExport_DateiErzeugt() throws Exception {
|
|
||||||
List<PositionCommand> pos = List.of(new PositionCommand(20001L, 1));
|
|
||||||
Angebot angebot = service.erstelleAngebot(10001L, pos);
|
|
||||||
|
|
||||||
Path pdfPfad = service.exportiereAlsPdf(angebot.getDokumentNummer());
|
|
||||||
|
|
||||||
assertNotNull(pdfPfad, "Rückgabepfad darf nicht null sein");
|
|
||||||
assertTrue(Files.exists(pdfPfad), "PDF-Datei muss existieren: " + pdfPfad);
|
|
||||||
assertTrue(Files.size(pdfPfad) > 0, "PDF-Datei darf nicht leer sein");
|
|
||||||
|
|
||||||
String inhalt = Files.readString(pdfPfad);
|
|
||||||
assertTrue(inhalt.contains(angebot.getDokumentNummer()),
|
|
||||||
"PDF muss Dokumentnummer enthalten");
|
|
||||||
assertTrue(inhalt.contains("ANGEBOT"),
|
|
||||||
"PDF muss Dokumenttyp enthalten");
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
// MT-DP-15: Dokumentübersicht pro Kunde (PH-DP-13)
|
|
||||||
// =========================================================================
|
|
||||||
@Test
|
|
||||||
@Order(15)
|
|
||||||
@DisplayName("MT-DP-15: findByKunde liefert nur Dokumente des richtigen Kunden")
|
|
||||||
void testDokumentenuebersicht_ProKunde() {
|
|
||||||
List<PositionCommand> pos = List.of(new PositionCommand(20001L, 1));
|
|
||||||
|
|
||||||
// Dokumente für Kunde 10001
|
|
||||||
service.erstelleAngebot(10001L, pos);
|
|
||||||
service.erstelleAngebot(10001L, pos);
|
|
||||||
|
|
||||||
// Dokument für Kunde 10002 (anderen Kunden)
|
|
||||||
service.erstelleAngebot(10002L, pos);
|
|
||||||
|
|
||||||
List<Dokument> dokKunde1 = service.findByKunde(10001L);
|
|
||||||
List<Dokument> dokKunde2 = service.findByKunde(10002L);
|
|
||||||
|
|
||||||
assertEquals(2, dokKunde1.size(),
|
|
||||||
"Kunde 10001 muss genau 2 Dokumente haben");
|
|
||||||
assertEquals(1, dokKunde2.size(),
|
|
||||||
"Kunde 10002 muss genau 1 Dokument haben");
|
|
||||||
|
|
||||||
assertTrue(dokKunde1.stream().allMatch(d -> d.getKundenId() == 10001L),
|
|
||||||
"Alle Dokumente in der Liste müssen Kunden-ID 10001 haben");
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
// MT-DP-16: Stammdatenreferenz prüfen (PH-DP-15)
|
|
||||||
// =========================================================================
|
|
||||||
@Test
|
|
||||||
@Order(16)
|
|
||||||
@DisplayName("MT-DP-16: isProductReferenced gibt true/false korrekt zurück")
|
|
||||||
void testStammdatenreferenz_Pruefen() {
|
|
||||||
// Angebot mit Produkt 20001 anlegen
|
|
||||||
List<PositionCommand> pos = List.of(new PositionCommand(20001L, 1));
|
|
||||||
service.erstelleAngebot(10001L, pos);
|
|
||||||
|
|
||||||
// Produkt 20001 wird referenziert → true
|
|
||||||
assertTrue(repository.existsReferencedProdukt(20001L),
|
|
||||||
"Produkt 20001 muss als referenziert erkannt werden");
|
|
||||||
|
|
||||||
// Produkt 99999 existiert nicht in Dokumenten → false
|
|
||||||
assertFalse(repository.existsReferencedProdukt(99999L),
|
|
||||||
"Produkt 99999 darf nicht als referenziert gelten");
|
|
||||||
|
|
||||||
// Kunde 10001 wird referenziert → true
|
|
||||||
assertTrue(repository.existsReferencedKunde(10001L),
|
|
||||||
"Kunde 10001 muss als referenziert erkannt werden");
|
|
||||||
|
|
||||||
// Kunde 99998 nicht → false
|
|
||||||
assertFalse(repository.existsReferencedKunde(99998L),
|
|
||||||
"Kunde 99998 darf nicht als referenziert gelten");
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
// Hilfsmethode: Erstellt eine vollständige Dokumentenkette bis zum Lieferschein
|
|
||||||
// =========================================================================
|
|
||||||
private Lieferschein erstelleKompletteDokumentenkette(long kundenId) {
|
|
||||||
List<PositionCommand> pos = List.of(
|
|
||||||
new PositionCommand(20001L, 2),
|
|
||||||
new PositionCommand(20002L, 1)
|
|
||||||
);
|
|
||||||
Angebot angebot = service.erstelleAngebot(kundenId, pos);
|
|
||||||
Auftragsbestaetigung ab = service.erstelleAuftragsbestaetigung(angebot.getDokumentNummer());
|
|
||||||
return service.erstelleLieferschein(ab.getDokumentNummer(), LocalDate.now());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue