package com.alexanderlogistics.ksef.api; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.when; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.LocalDate; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.logging.Logger; import org.imixs.workflow.FileData; import org.imixs.workflow.ItemCollection; import org.imixs.workflow.engine.DocumentService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import com.alexanderlogistics.BusinessPartnerService; import com.alexanderlogistics.InvoiceService; import com.alexanderlogistics.TestLoggerConfig; import com.alexanderlogistics.einvoice.KSeFAdapter; /** * Der KSeFAdapterTest prüft die Umwandlung einer Rechnung in die KSeF XML * Struktur. * * Der Test simuliert eine Invoice und ruft dann den KSeFAdapter an um ein * Output File zu erzeugen. *

* Zum validieren kann man dann den validate_xml.sh Script hernehmen * */ @ExtendWith(MockitoExtension.class) public class KSeFAdapterTest { private static Logger logger = Logger.getLogger(KSeFAdapterTest.class.getName()); private static final String TEMPLATE_FILE = "ksef/ksef.xml"; ItemCollection businessPartner; @Mock private BusinessPartnerService businessPartnerService; @Mock private DocumentService documentService; @Mock private InvoiceService invoiceService; @InjectMocks private KSeFAdapter adapter; @BeforeEach void setup() throws Exception { TestLoggerConfig.setupTestLogger(); // Prepare mock business partner businessPartner = new ItemCollection(); businessPartner.setItemValue("partner.name", "Test Sp. z o.o."); businessPartner.setItemValue("partner.country", "PL"); businessPartner.setItemValue("partner.city", "Warszawa"); businessPartner.setItemValue("partner.zip", "00-001"); businessPartner.setItemValue("partner.address", "ul. Testowa 1"); businessPartner.setItemValue("partner.vat", "PL1234567890"); businessPartner.setItemValue("dbtr.number", "D-12345"); } /** * Erzeugt ein lokales XML File zum testen */ @Test @DisplayName("Test Simple Invoice") public void testSimpleInvoice() throws Exception { logger.info("==> Test: Upload Invoice XML"); // Prepare a workitem with typical invoice data ItemCollection workitem = new ItemCollection(); workitem.setItemValue("invoice.number", "FV/2025/001"); workitem.setItemValue("invoice.date", LocalDate.of(2025, 2, 10)); workitem.setItemValue("invoice.duedate", LocalDate.of(2025, 3, 10)); workitem.setItemValue("invoice.currency", "PLN"); workitem.setItemValue("invoice.total.net", 10000.00); workitem.setItemValue("invoice.total.tax", 23.0); workitem.setItemValue("invoice.total", 12300.00); workitem.setItemValue("invoice.correction", "false"); workitem.setItemValue("invoice.CorrectionInvoiceNumber", ""); workitem.setItemValue("partner.id", "BP-001"); workitem.setItemValue("partner.vat", "PL1234567890"); workitem.setItemValue("invoice.performancedate", new Date()); // Prepare child items (invoice line items) List childItems = new ArrayList<>(); ItemCollection lineItem1 = new ItemCollection(); lineItem1.setItemValue("numpos", "1"); lineItem1.setItemValue("datev.text", "Transport Berlin - Warsaw"); lineItem1.setItemValue("datev.umsatz", 6000.00); childItems.add(lineItem1.getAllItems()); ItemCollection lineItem2 = new ItemCollection(); lineItem2.setItemValue("numpos", "2"); lineItem2.setItemValue("datev.text", "Customs handling"); lineItem2.setItemValue("datev.umsatz", 4000.00); childItems.add(lineItem2.getAllItems()); workitem.setItemValue("_childitems", childItems); // Arrange when(businessPartnerService.getBusinessPartnerByID("BP-001")) .thenReturn(businessPartner); FileData xmlTemplate = loadTemplateFromResources(TEMPLATE_FILE); // Act adapter.updateEInvoice(xmlTemplate, workitem); // Assert - basic checks assertNotNull(xmlTemplate.getContent(), "XML content should not be null after update"); assertTrue(xmlTemplate.getContent().length > 0, "XML content should not be empty"); // Write output for manual inspection writeOutputToResources(xmlTemplate, "invoice-simple.xml"); } /** * Erzeugt ein lokales XML File zum testen */ @Test @DisplayName("Test Invoice Korrektur") public void testInvoiceKorektur() throws Exception { logger.info("==> Test: Upload Invoice XML"); // Prepare a workitem with typical invoice data ItemCollection workitem = new ItemCollection(); workitem.setItemValue("invoice.number", "FV/2025/001"); workitem.setItemValue("invoice.date", LocalDate.of(2025, 2, 10)); workitem.setItemValue("invoice.duedate", LocalDate.of(2025, 3, 10)); workitem.setItemValue("invoice.currency", "EUR"); workitem.setItemValue("invoice.total.net", 0.00); workitem.setItemValue("invoice.total.tax", 0.0); workitem.setItemValue("invoice.total", 0.00); workitem.setItemValue("partner.id", "BP-001"); workitem.setItemValue("partner.vat", "PL1234567890"); // simulation Korrekturrechnung workitem.setItemValue("invoice.correction", true); workitem.setItemValue("invoice.CorrectionInvoiceNumber", "6551"); workitem.setItemValue("invoice.performancedate", new Date()); // Prepare child items (invoice line items) List childItems = new ArrayList<>(); ItemCollection lineItem1 = new ItemCollection() .setItemValue("numpos", "1") .setItemValue("datev.shzeichen", "H") .setItemValue("datev.kurs", 0.236485) .setItemValue("datev.basisumsatz", -3805.74) .setItemValue("datev.text", "Transport Berlin - Warsaw") .setItemValue("billingtext", "usługa spedycyjna / transport w relacji, PL63 - CZ43") .setItemValue("datev.umsatz", -900.00) .setItemValue("datev.wkz", "EUR") .setItemValue("billingcode", "TRANSLKW"); childItems.add(lineItem1.getAllItems()); /** * 1 * 7f846436-7c29-48b3-8f5a-fed996ea30a3 * IM-POL-2512-008 * szt. * 1 * 0.00 * 0.00 * * * 2 * f3077e37-9198-4431-a82b-68d9439b587e * IM-POL-2512-008 * szt. * 1 * -900.00 * -900.00 * * * 3 * 31fdfe9e-671e-42f6-9f7e-ceb227a3a42c * IM-POL-2512-008 * szt. * 1 * 0.00 * 0.00 * * * 4 * 79ffd1e8-be87-478b-97b5-d37aa302d090 * IM-POL-2512-008 * szt. * 1 * 0.00 * 0.00 * * * 5 * 38e8cbd3-e558-4b0e-875c-c4f2d96d1659 * IM-POL-2512-008 * szt. * 1 * 900.00 * 900.00 * * * 6 * 3774dab3-b6c4-433e-8298-60ecf6cbab74 * IM-POL-2512-008 * szt. * 1 * 0.00 * 0.00 * * * 7 * f19846ab-3567-4e5d-823b-807d474af062 * IM-POL-2512-008 * szt. * 1 * 0.00 * 0.00 */ workitem.setItemValue("_childitems", childItems); // Arrange when(businessPartnerService.getBusinessPartnerByID("BP-001")) .thenReturn(businessPartner); FileData xmlTemplate = loadTemplateFromResources(TEMPLATE_FILE); // Act adapter.updateEInvoice(xmlTemplate, workitem); // Assert - basic checks assertNotNull(xmlTemplate.getContent(), "XML content should not be null after update"); assertTrue(xmlTemplate.getContent().length > 0, "XML content should not be empty"); // Write output for manual inspection writeOutputToResources(xmlTemplate, "test-korrekturrechnung.xml"); } // ── Helper methods ────────────────────────────────────────────── /** * Load the KSeF XML template from src/test/resources. */ private FileData loadTemplateFromResources(String filename) { try (InputStream is = getClass().getClassLoader().getResourceAsStream(filename)) { if (is == null) { fail("Template file not found in test resources: " + filename + "\nPlease place your KSeF XML template at: src/test/resources/" + filename); } byte[] content = is.readAllBytes(); return new FileData(filename, content, "application/xml", null); } catch (IOException e) { fail("Failed to read template: " + e.getMessage()); return null; } } /** * Write the resulting XML to src/test/resources/output/ for manual inspection. */ private void writeOutputToResources(FileData fileData, String filename) throws IOException { Path outputDir = Paths.get("src", "test", "resources", "ksef/output"); Files.createDirectories(outputDir); Path outputPath = outputDir.resolve(filename); Files.write(outputPath, fileData.getContent()); System.out.println("──────────────────────────────────────────────"); System.out.println("Output written to: " + outputPath.toAbsolutePath()); System.out.println("──────────────────────────────────────────────"); } }