This commit is contained in:
Ralph Soika 2025-11-18 16:33:58 +01:00
parent f61c7feca5
commit c232841ed3
8 changed files with 3921 additions and 375 deletions

View file

@ -120,7 +120,7 @@ public class EInvoiceAdapter implements SignalAdapter {
throw new PluginException(EInvoiceAdapter.class.getSimpleName(), CONFIG_ERROR, throw new PluginException(EInvoiceAdapter.class.getSimpleName(), CONFIG_ERROR,
"missing e-invoice configuration in model event - please check model configuration"); "missing e-invoice configuration in model event - please check model configuration");
} }
ItemCollection createDefinition = XMLParser.parseItemStructure(eInvoiceConfig.getItemValueString("create")); ItemCollection createDefinition = XMLParser.parseItemStructure(eInvoiceConfig.getItemValueString("CREATE"));
try { try {
// Load the e-invoice template.... // Load the e-invoice template....
FileData xmlFileData = loadXMLTemplate(workitem, createDefinition); FileData xmlFileData = loadXMLTemplate(workitem, createDefinition);

View file

@ -15,26 +15,33 @@ import com.alexanderlogistics.ksef.api.KSeFAPIService;
import jakarta.inject.Inject; import jakarta.inject.Inject;
/** /**
* This adapter exports the invoice data to KSeF * This adapter exports the invoice data to KSeF and can poll the current status
* of an invoice
* *
* To activate debug add the following event definition * To activate debug add the following event definition
* *
* <pre> * <pre>
* {@code * {@code
<ksef name="EXPORT"> <ksef name="EXPORT">
<debug>true</debug> <debug>true</debug>
</ksef> </ksef>
<ksef name="STATUS">
<debug>true</debug>
</ksef>
* } * }
* </pre> * </pre>
* *
* @version 1.0 * @version 1.0
* @author rsoika * @author rsoika
*/ */
public class KSeFExportAdapter implements SignalAdapter { public class KSeFAPIAdapter implements SignalAdapter {
public static final String CONFIG_ERROR = "CONFIG_ERROR"; public static final String CONFIG_ERROR = "CONFIG_ERROR";
private static Logger logger = Logger.getLogger(KSeFExportAdapter.class.getName()); private static Logger logger = Logger.getLogger(KSeFAPIAdapter.class.getName());
private boolean debug = false; private boolean debug = false;
@Inject @Inject
@ -56,25 +63,33 @@ public class KSeFExportAdapter implements SignalAdapter {
throws AdapterException { throws AdapterException {
// read debug flag // read debug flag
try { try {
List<ItemCollection> ksefDefinitions = workflowService.evalWorkflowResultXML(event, "ksef", List<ItemCollection> ksefExportDefinitions = workflowService.evalWorkflowResultXML(event, "ksef",
"EXPORT", workitem, false); "EXPORT", workitem, false);
if (ksefDefinitions != null && ksefDefinitions.size() > 0) { // List<ItemCollection> ksefStatusDefinitions =
ItemCollection ksefDefinition = ksefDefinitions.get(0); // workflowService.evalWorkflowResultXML(event, "ksef",
// "STATUS", workitem, false);
// EXPORT
if (ksefExportDefinitions != null && ksefExportDefinitions.size() > 0) {
ItemCollection ksefDefinition = ksefExportDefinitions.get(0);
debug = ksefDefinition.getItemValueBoolean("debug"); debug = ksefDefinition.getItemValueBoolean("debug");
kSeFAPIService.uploadInvoice(workitem, "ksef.xml");
} }
} catch (PluginException ee) {
// no op
}
try {
kSeFAPIService.uploadInvoice(workitem, "ksef.xml"); // // STATUS
// if (ksefStatusDefinitions != null && ksefStatusDefinitions.size() > 0) {
// ItemCollection ksefDefinition = ksefStatusDefinitions.get(0);
// debug = ksefDefinition.getItemValueBoolean("debug");
// kSeFAPIService.getInvoiceStatus(workitem);
// }
} catch (PluginException e) { } catch (PluginException e) {
// convert PluginException into a AdapterException to handle the error via the
// model!
logger.warning("Failed to export data to Zoho: " + e.getMessage());
throw new AdapterException(e); throw new AdapterException(e);
} }
return workitem; return workitem;
} }

View file

@ -40,11 +40,12 @@ import jakarta.inject.Inject;
* *
* <pre> * <pre>
* {@code * {@code
<e-invoice name="ksef"> <ksef name="create">
<textblock>textblock-ref</textblock> <textblock>textblock-ref</textblock>
<template>filename</template> <template>filename</template>
<debug>true</debug> <debug>true</debug>
</e-invoice> </ksef>
} }
* </pre> * </pre>
* *
@ -92,18 +93,18 @@ public class KSeFAdapter implements SignalAdapter {
logger.info("├── 🔜 Convert Invoice to KSeF..."); logger.info("├── 🔜 Convert Invoice to KSeF...");
// read configuration.... // read configuration....
ItemCollection eInvoiceConfig = workflowService.evalWorkflowResult(event, "e-invoice", ItemCollection eInvoiceConfig = workflowService.evalWorkflowResult(event, "ksef",
workitem, workitem,
false); false);
if (eInvoiceConfig == null || !eInvoiceConfig.hasItem("ksef")) { if (eInvoiceConfig == null || !eInvoiceConfig.hasItem("CREATE")) {
throw new PluginException(EInvoiceAdapter.class.getSimpleName(), CONFIG_ERROR, throw new PluginException(EInvoiceAdapter.class.getSimpleName(), CONFIG_ERROR,
"missing e-invoice/ksef configuration in model event - please check model configuration"); "missing e-invoice/ksef configuration in model event - please check model configuration");
} }
ItemCollection ksefDefinition = XMLParser.parseItemStructure(eInvoiceConfig.getItemValueString("ksef")); ItemCollection ksefCreateDefinition = XMLParser.parseItemStructure(eInvoiceConfig.getItemValueString("CREATE"));
try { try {
// Load the e-invoice template.... // Load the e-invoice template....
FileData xmlFileData = loadXMLTemplate(workitem, ksefDefinition); FileData xmlFileData = loadXMLTemplate(workitem, ksefCreateDefinition);
updateEInvoice(xmlFileData, workitem); updateEInvoice(xmlFileData, workitem);

View file

@ -67,7 +67,7 @@ public class KSeFAPIService {
* @return referenceNumber from KSeF * @return referenceNumber from KSeF
* @throws PluginException * @throws PluginException
*/ */
public String uploadInvoice(ItemCollection workitem, String fileName) throws PluginException { public void uploadInvoice(ItemCollection workitem, String fileName) throws PluginException {
// First open an interactive session (reuses existing if valid) // First open an interactive session (reuses existing if valid)
kseFAuthManager.openSession(); kseFAuthManager.openSession();
@ -182,9 +182,27 @@ public class KSeFAPIService {
textlist.add(referenceNumber); textlist.add(referenceNumber);
fileData.setAttribute("ksef.referenceNumber", textlist); fileData.setAttribute("ksef.referenceNumber", textlist);
workitem.setItemValue("ksef.referenceNumber", referenceNumber);
logger.info("├── ✅ Upload successful - Reference: " + referenceNumber); logger.info("├── ✅ Upload successful - Reference: " + referenceNumber);
return referenceNumber; // Close Session
kseFAuthManager.closeInteractiveSession();
// Status abfragen
// Now we need to wait until auth/{AuthRef} will return 200
long start = System.currentTimeMillis();
long timeout = 20_000; // 20 Sekunden
while (System.currentTimeMillis() - start < timeout) {
int code = kseFAuthManager.checkSessionStatus();
if (code >= 200) {
break; // ok or borken
}
// kleine Pause, um CPU zu schonen
kseFAuthManager.waitSomeTime(500);
}
} catch (Exception e) { } catch (Exception e) {
logger.severe("├── ⚠️ Invoice upload failed: " + e.getMessage()); logger.severe("├── ⚠️ Invoice upload failed: " + e.getMessage());
@ -194,11 +212,101 @@ public class KSeFAPIService {
} finally { } finally {
// finally we close the session // finally we close the session
kseFAuthManager.closeInteractiveSession();
kseFAuthManager.deleteCurrentAuthSession(); kseFAuthManager.deleteCurrentAuthSession();
} }
} }
/**
* Poll Invoice Status
*
* https://ksef-test.mf.gov.pl/docs/v2/index.html#tag/Status-wysylki-i-UPO/paths/~1api~1v2~1sessions~1%7BreferenceNumber%7D/get
*
* @param workitem
* @return
* @throws PluginException
*/
private int xxxgetInvoiceStatus(ItemCollection workitem) throws PluginException {
logger.info("├── 🔎 Get Invoice Status...");
// Validate session
if (kseFAuthManager.getAccessToken() == null) {
throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API,
"API Error - missing AccessToken!");
}
if (kseFAuthManager.getSessionRefNumber() == null) {
throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API,
"API Error - missing SessionRefNumber!");
}
if (kseFAuthManager.getSessionEncryption() == null) {
throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API,
"API Error - missing Session Encryption!");
}
try {
String referenceNumber = workitem.getItemValueString("ksef.referenceNumber");
if (referenceNumber.isBlank()) {
throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API,
"API Error - missing invoice referenceNumber!");
}
// Calculate hashes
// Send upload request
String uri = kseFAuthManager.getBaseURI() + "/sessions/" + kseFAuthManager.getSessionRefNumber();
logger.info("│ ├── GET: " + uri);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + kseFAuthManager.getAccessToken())
.GET()
.build();
HttpResponse<String> response = kseFAuthManager.getHttpClient().send(
request, HttpResponse.BodyHandlers.ofString());
logger.info("│ ├── HTTP Response: " + response.statusCode());
logger.info("│ ├── Response Body: " + response.body());
int statusCode = -1;
String statusDescription = "";
try (Jsonb jsonb = JsonbBuilder.create()) {
JsonObject jsonObject = jsonb.fromJson(response.body(), JsonObject.class);
// Status Code aus dem status-Objekt extrahieren
if (jsonObject.containsKey("status")) {
JsonObject statusObject = jsonObject.getJsonObject("status");
if (statusObject.containsKey("code")) {
statusCode = statusObject.getInt("code");
statusDescription = statusObject.getString("description");
logger.info("│ ├── Status Code: " + statusCode);
logger.info("│ ├── Status Description: " + statusDescription);
}
}
} catch (Exception e) {
logger.severe("├── ⚠️ Error parsing JSON response: " + e.getMessage());
throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API,
"Error parsing JSON response: " + e.getMessage());
}
workitem.setItemValue("ksef.status.code", statusCode);
workitem.setItemValue("ksef.status.description", statusDescription);
return statusCode;
} catch (Exception e) {
logger.severe("├── ⚠️ Get Invoice status failed: " + e.getMessage());
throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API,
"Invoice upload failed: " + e.getMessage());
}
}
/** /**
* Encrypt invoice XML with the session encryption keys * Encrypt invoice XML with the session encryption keys
* *

View file

@ -535,7 +535,7 @@ public class KSeFAuthManager {
/** /**
* waits some time.. * waits some time..
*/ */
private void waitSomeTime(long millis) { public void waitSomeTime(long millis) {
logger.info("│ ├── waiting " + millis + "ms...."); logger.info("│ ├── waiting " + millis + "ms....");
try { try {
Thread.sleep(millis); Thread.sleep(millis);
@ -1105,7 +1105,7 @@ public class KSeFAuthManager {
logger.info("Response: " + response.body()); logger.info("Response: " + response.body());
} }
sessionRefNumber = null; // sessionRefNumber = null;
} }
} }

View file

@ -63,7 +63,7 @@ public class KSeFAPIServiceTest {
workitem = createWorkitem(); workitem = createWorkitem();
// Execute upload // Execute upload
referenceNumber = apiService.uploadInvoice(workitem, "example-invoice-01.xml"); apiService.uploadInvoice(workitem, "example-invoice-01.xml");
} catch (IOException | PluginException e) { } catch (IOException | PluginException e) {
logger.info("⚠️ Upload failed: " + e.getMessage()); logger.info("⚠️ Upload failed: " + e.getMessage());
fail(e); fail(e);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff