office-alexander-logistics/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/ksef/api/KSeFAdapterTest.java
2026-04-27 16:31:42 +02:00

296 lines
11 KiB
Java

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.
* <p>
* 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<Object> 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<Object> 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());
/**
* <NrWierszaFa>1</NrWierszaFa>
* <UU_ID>7f846436-7c29-48b3-8f5a-fed996ea30a3</UU_ID>
* <P_7>IM-POL-2512-008</P_7>
* <P_8A>szt.</P_8A>
* <P_8B>1</P_8B>
* <P_9A>0.00</P_9A>
* <P_11>0.00</P_11>
* </FaWiersz>
* <FaWiersz>
* <NrWierszaFa>2</NrWierszaFa>
* <UU_ID>f3077e37-9198-4431-a82b-68d9439b587e</UU_ID>
* <P_7>IM-POL-2512-008</P_7>
* <P_8A>szt.</P_8A>
* <P_8B>1</P_8B>
* <P_9A>-900.00</P_9A>
* <P_11>-900.00</P_11>
* </FaWiersz>
* <FaWiersz>
* <NrWierszaFa>3</NrWierszaFa>
* <UU_ID>31fdfe9e-671e-42f6-9f7e-ceb227a3a42c</UU_ID>
* <P_7>IM-POL-2512-008</P_7>
* <P_8A>szt.</P_8A>
* <P_8B>1</P_8B>
* <P_9A>0.00</P_9A>
* <P_11>0.00</P_11>
* </FaWiersz>
* <FaWiersz>
* <NrWierszaFa>4</NrWierszaFa>
* <UU_ID>79ffd1e8-be87-478b-97b5-d37aa302d090</UU_ID>
* <P_7>IM-POL-2512-008</P_7>
* <P_8A>szt.</P_8A>
* <P_8B>1</P_8B>
* <P_9A>0.00</P_9A>
* <P_11>0.00</P_11>
* </FaWiersz>
* <FaWiersz>
* <NrWierszaFa>5</NrWierszaFa>
* <UU_ID>38e8cbd3-e558-4b0e-875c-c4f2d96d1659</UU_ID>
* <P_7>IM-POL-2512-008</P_7>
* <P_8A>szt.</P_8A>
* <P_8B>1</P_8B>
* <P_9A>900.00</P_9A>
* <P_11>900.00</P_11>
* </FaWiersz>
* <FaWiersz>
* <NrWierszaFa>6</NrWierszaFa>
* <UU_ID>3774dab3-b6c4-433e-8298-60ecf6cbab74</UU_ID>
* <P_7>IM-POL-2512-008</P_7>
* <P_8A>szt.</P_8A>
* <P_8B>1</P_8B>
* <P_9A>0.00</P_9A>
* <P_11>0.00</P_11>
* </FaWiersz>
* <FaWiersz>
* <NrWierszaFa>7</NrWierszaFa>
* <UU_ID>f19846ab-3567-4e5d-823b-807d474af062</UU_ID>
* <P_7>IM-POL-2512-008</P_7>
* <P_8A>szt.</P_8A>
* <P_8B>1</P_8B>
* <P_9A>0.00</P_9A>
* <P_11>0.00</P_11>
*/
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("──────────────────────────────────────────────");
}
}