package com.alexanderlogistics.ksef.api; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URI; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Base64; import java.util.Collection; import java.util.List; import java.util.logging.Logger; import java.util.stream.Collectors; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.font.PDType1Font; import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; import org.imixs.archive.core.SnapshotService; import org.imixs.workflow.FileData; import org.imixs.workflow.ItemCollection; import org.imixs.workflow.engine.DocumentService; import org.imixs.workflow.engine.WorkflowService; import org.imixs.workflow.exceptions.PluginException; import org.imixs.workflow.exceptions.QueryException; import com.google.zxing.BarcodeFormat; import com.google.zxing.WriterException; import com.google.zxing.client.j2se.MatrixToImageWriter; import com.google.zxing.common.BitMatrix; import com.google.zxing.qrcode.QRCodeWriter; import jakarta.annotation.security.DeclareRoles; import jakarta.annotation.security.RolesAllowed; import jakarta.annotation.security.RunAs; import jakarta.ejb.Singleton; import jakarta.inject.Inject; import jakarta.json.JsonArray; import jakarta.json.JsonObject; import jakarta.json.JsonString; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; /** * THe KSeFAPIService provides methods to open an interactive session and upload * an invoice. * */ @DeclareRoles({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS", "org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS", "org.imixs.ACCESSLEVEL.MANAGERACCESS" }) @RolesAllowed({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS", "org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS", "org.imixs.ACCESSLEVEL.MANAGERACCESS" }) @Singleton @RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS") public class KSeFAPIService { private static Logger logger = Logger.getLogger(KSeFAPIService.class.getName()); public static final String ERROR_API = "API_ERROR"; public static final String DOCUMENT_ERROR = "DOCUMENT_ERROR"; public static final String CONFIG_ERROR = "CONFIG_ERROR"; final String TYPE_TEXTBLOCK = "textblock"; @Inject KSeFAuthManager kseFAuthManager; @Inject DocumentService documentService; @Inject WorkflowService workflowService; @Inject SnapshotService snapshotService; /** * This method uploads a KSeF Invoice document (XML). * The response of the upload is a 'referenceNumber' which is stored into the * item ksef.referenceNumber * * @param workitem workitem containing the invoice * @param fileName name of the XML file to upload * @return referenceNumber from KSeF * @throws PluginException */ public void uploadInvoice(ItemCollection workitem, String fileName) throws PluginException { // First open an interactive session (reuses existing if valid) kseFAuthManager.openSession(workitem); logger.info("β”œβ”€β”€ πŸ“€ Upload Invoice..."); // 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!"); } // Load invoice XML byte[] invoiceXml = null; FileData fileData = workitem.getFileData(fileName); // test if we need to load the snapshot data... if (fileData.getContent().length == 0) { fileData = snapshotService.getWorkItemFile(workitem.getUniqueID(), fileName); if (fileData != null) { invoiceXml = fileData.getContent(); } } else { invoiceXml = fileData.getContent(); } logger.info("β”‚ β”œβ”€β”€ XML Invoice loaded - " + invoiceXml.length + " bytes"); try { // Get the session encryption keys (already sent to KSeF during session // creation!) SessionEncryption encryption = kseFAuthManager.getSessionEncryption(); // Encrypt invoice with session keys byte[] encryptedInvoice = encryptInvoiceWithSessionKeys(invoiceXml, encryption); // Calculate hashes String invoiceHash = calculateSHA256Hash(invoiceXml); workitem.setItemValue("ksef.invoiceHash", invoiceHash); String encryptedInvoiceHash = calculateSHA256Hash(encryptedInvoice); workitem.setItemValue("ksef.encryptedInvoiceHash", encryptedInvoiceHash); // Build JSON request body String jsonBody = String.format( "{" + "\"invoiceHash\": \"%s\"," + "\"invoiceSize\": %d," + "\"encryptedInvoiceHash\": \"%s\"," + "\"encryptedInvoiceSize\": %d," + "\"encryptedInvoiceContent\": \"%s\"," + "\"offlineMode\": false" + "}", invoiceHash, invoiceXml.length, encryptedInvoiceHash, encryptedInvoice.length, Base64.getEncoder().encodeToString(encryptedInvoice)); // Send upload request String uri = kseFAuthManager.getBaseURI() + "/sessions/online/" + kseFAuthManager.getSessionRefNumber() + "/invoices"; logger.info("β”‚ β”œβ”€β”€ POST: " + uri); if (kseFAuthManager.isDebug()) { logger.info("β”‚ β”œβ”€β”€ Request Body: " + jsonBody); } HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(uri)) .header("Content-Type", "application/json") .header("Authorization", "Bearer " + kseFAuthManager.getAccessToken()) .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) .build(); HttpResponse response = kseFAuthManager.getHttpClient().send( request, HttpResponse.BodyHandlers.ofString()); logger.info("β”‚ β”œβ”€β”€ HTTP Response: " + response.statusCode()); if (kseFAuthManager.isDebug()) { logger.info("β”‚ β”œβ”€β”€ Response Body: " + response.body()); } if (response.statusCode() != 202) { throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API, "Invoice upload failed with status: " + response.statusCode() + " - " + response.body()); } // Parse response and get reference number String referenceNumber = null; try (Jsonb jsonb = JsonbBuilder.create()) { JsonObject jsonObject = jsonb.fromJson(response.body(), JsonObject.class); referenceNumber = jsonObject.getString("referenceNumber"); logger.info("β”‚ β”œβ”€β”€ Reference Number: " + referenceNumber); } catch (Exception e) { throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API, "Error parsing upload response: " + e.getMessage()); } // Store reference number in file metadata List textlist = new ArrayList<>(); textlist.add(referenceNumber); fileData.setAttribute("ksef.referenceNumber", textlist); workitem.setItemValue("ksef.referenceNumber", referenceNumber); logger.info("β”œβ”€β”€ βœ… Upload successful - Reference: " + referenceNumber); // Close Session kseFAuthManager.closeInteractiveSession(); // Status abfragen // Now we need to wait until auth/{AuthRef} will return 200 int code = -1; long start = System.currentTimeMillis(); long timeout = 150_000; // 2,5 Minuten boolean timeoutStatus = true; while (System.currentTimeMillis() - start < timeout) { code = kseFAuthManager.checkSessionStatus(workitem); if (code >= 200) { timeoutStatus = false; break; // ok or broken } // kleine Pause, um CPU zu schonen kseFAuthManager.waitSomeTime(3000); } if (timeoutStatus) { logger.severe("β”œβ”€β”€ ⚠️ Status timeout"); throw new PluginException(KSeFAuthManager.class.getSimpleName(), "Session Status: " + code, "Invoice was not processed within expected timeout " + timeout + " upload canceled"); } // In case we have an error we print out the reason... if (code > 200) { checkSessionStatusInvoices(workitem); // convert KSeF Error status into a PluginException String errorCode = workitem.getItemValueString("ksef.status.code"); String errorMessage = workitem.getItemValueString("ksef.status.description"); throw new PluginException(KSeFAuthManager.class.getSimpleName(), errorCode, errorMessage); } // if status code 200 and if we have a ksef.upo.referencenumber we download the // document if (code == 200 && !referenceNumber.isEmpty()) { checkSessionStatusInvoices(workitem); FileData upoFileData = downloadUOPXML(workitem); workitem.addFileData(upoFileData); // store verification url generateVerificationUrl(workitem); // store QR Code generateQRCode(workitem); // print QR Code to pdf embedQRCode(workitem); } } catch (IOException | NoSuchAlgorithmException | InterruptedException e) { logger.severe("β”œβ”€β”€ ⚠️ Invoice upload failed: " + e.getMessage()); kseFAuthManager.closeInteractiveSession(); throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API, "Invoice upload failed: " + e.getMessage()); } finally { // finally we close the session kseFAuthManager.deleteCurrentAuthSession(); } } /** * This method verifies the actual status of the status of all invoices * * The method returns the aut status code (not the HTTP Response code!) * * https://ksef-test.mf.gov.pl/docs/v2/index.html#tag/Status-wysylki-i-UPO/paths/~1api~1v2~1sessions~1%7BreferenceNumber%7D/get * * @return Session Status Code * @throws PluginException */ public void checkSessionStatusInvoices(ItemCollection workitem) throws PluginException { logger.info("β”œβ”€β”€ 🚸 KSeF API check session invoice status..."); String uri = kseFAuthManager.getBaseURI() + "/sessions/" + kseFAuthManager.getSessionRefNumber() + "/invoices"; int httpResponse = -1; int statusCode = -1; String statusDescription; logger.info("β”‚ β”œβ”€β”€ GET: " + uri); HttpResponse response = null; try { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(uri)) .header("Accept", "application/json") .header("Content-Type", "application/json") .header("Authorization", "Bearer " + kseFAuthManager.getAccessToken()) .GET() .build(); response = kseFAuthManager.getHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); } catch (IOException | InterruptedException e) { throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API, "API Error: Unable to request auth status: " + e.getMessage()); } httpResponse = response.statusCode(); logger.info("β”‚ β”œβ”€β”€ HTTP Response: " + httpResponse); logger.info("β”‚ β”œβ”€β”€ " + response.body()); try (Jsonb jsonb = JsonbBuilder.create()) { JsonObject jsonObject = jsonb.fromJson(response.body(), JsonObject.class); // Extract status from the first invoice in the invoices array if (jsonObject.containsKey("invoices")) { JsonArray invoicesArray = jsonObject.getJsonArray("invoices"); if (invoicesArray != null && !invoicesArray.isEmpty()) { JsonObject firstInvoice = invoicesArray.getJsonObject(0); if (firstInvoice.containsKey("ksefNumber")) { String ksefNumber = firstInvoice.getString("ksefNumber"); logger.info("β”‚ β”œβ”€β”€ ksefNumber: " + ksefNumber); workitem.setItemValue("ksef.number", ksefNumber); } // Status Code aus dem status-Objekt extrahieren if (firstInvoice.containsKey("status")) { JsonObject statusObject = firstInvoice.getJsonObject("status"); if (statusObject.containsKey("code")) { statusCode = statusObject.getInt("code"); statusDescription = statusObject.getString("description"); // Convert details array to a single string if (statusObject.containsKey("details")) { JsonArray detailsArray = statusObject.getJsonArray("details"); String details = detailsArray.stream() .map(v -> ((JsonString) v).getString()) .collect(Collectors.joining("; ")); statusDescription = statusDescription + ": " + details; } logger.info("β”‚ β”œβ”€β”€ Status Code: " + statusCode); workitem.setItemValue("ksef.status.code", statusCode); workitem.setItemValue("ksef.status.description", statusDescription); } } } else { throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API, "Unable to read invoice status from interactive session!"); } } } 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()); } } /** * This method downloads the UPO XML document based on the KSeF reference * number * * https://ksef-test.mf.gov.pl/docs/v2/index.html#tag/Status-wysylki-i-UPO/paths/~1api~1v2~1sessions~1%7BreferenceNumber%7D~1invoices~1%7BinvoiceReferenceNumber%7D~1upo/get * * @return Session Status Code * @throws PluginException */ public FileData downloadUOPXML(ItemCollection workitem) throws PluginException { String ksefRefNumber = workitem.getItemValueString("ksef.referenceNumber"); if (ksefRefNumber == null || ksefRefNumber.isEmpty()) { logger.severe("β”œβ”€β”€ ⚠️ Error parsing ksef.referenceNumber"); throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API, "Error parsing ksef.referenceNumber"); } logger.info("β”œβ”€β”€ πŸ“₯ KSeF API download UPO XML..."); String uri = kseFAuthManager.getBaseURI() + "/sessions/" + kseFAuthManager.getSessionRefNumber() + "/invoices/" + ksefRefNumber + "/upo"; int httpResponse = -1; logger.info("β”‚ β”œβ”€β”€ GET: " + uri); HttpResponse response = null; try { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(uri)) .header("Accept", "application/json") .header("Content-Type", "application/json") .header("Authorization", "Bearer " + kseFAuthManager.getAccessToken()) .GET() .build(); response = kseFAuthManager.getHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); } catch (IOException | InterruptedException e) { throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API, "API Error: Unable to request auth status: " + e.getMessage()); } httpResponse = response.statusCode(); logger.info("β”‚ β”œβ”€β”€ HTTP Response: " + httpResponse); if (kseFAuthManager.isDebug()) { logger.info("β”‚ β”œβ”€β”€ " + response.body()); } FileData fileData = new FileData(ksefRefNumber + ".xml", response.body().getBytes(), "application/xml", null); return fileData; } /** * Generate KSeF verification URL for QR code (KOD I) and stores the URL into * the item ksef.VerificationUrl * * @param workitem */ public void generateVerificationUrl(ItemCollection workitem) throws NoSuchAlgorithmException { logger.info("β”œβ”€β”€ πŸ”² Generate QR-Code..."); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy"); String invoiceDate = formatter.format(workitem.getItemValueLocalDate("invoice.date")); String baseURL = kseFAuthManager.getBaseURI(); String invoiceHash = workitem.getItemValueString("ksef.invoiceHash"); byte[] decoded = Base64.getDecoder().decode(invoiceHash); // Base64URL ohne Padding String base64Url = Base64.getUrlEncoder().withoutPadding().encodeToString(decoded); // Die Client App URL muss angepasst werden. Das /v2/ element aus der Basis URL // muss mit 'client-app/invoice' ausgetauscht werden. // Es entsteht dann https://api-test.ksef.mf.gov.pl/client-app/invoice/ // BASE URL: https://api.ksef.mf.gov.pl/api/v2" // /api/v2 muss ersetzt werden: baseURL = baseURL.replace("/api/v2", "/client-app/invoice"); // remove resource /api/ // https://api.ksef.mf.gov.pl/api/client-app/invoice/9552521552/01-04-2026/88Yl-F_6vhLeMUONRz2LBcAHF21NpbLk7ikm4ZqV1bg // => // https://api.ksef.mf.gov.pl/client-app/invoice/9552521552/01-04-2026/88Yl-F_6vhLeMUONRz2LBcAHF21NpbLk7ikm4ZqV1bg // Build verification URL workitem.setItemValue("ksef.VerificationUrl", baseURL + "/" + kseFAuthManager.getKsefNip() + "/" + invoiceDate + "/" + base64Url); } /** * Generates a QR code image. * * If the generation fails we simply print a warning but do not stop the * complete process. * * @param text * @param width * @param height * @param filePath * @throws PluginException * @throws WriterException * @throws IOException */ public void generateQRCode(ItemCollection workitem) { QRCodeWriter qrCodeWriter = new QRCodeWriter(); String text = workitem.getItemValueString("ksef.verificationurl"); BitMatrix bitMatrix; try { bitMatrix = qrCodeWriter.encode(text, BarcodeFormat.QR_CODE, 300, 300); ByteArrayOutputStream pngOutputStream = new ByteArrayOutputStream(); MatrixToImageWriter.writeToStream(bitMatrix, "PNG", pngOutputStream); // store qr code String ksefRefNumber = workitem.getItemValueString("ksef.referenceNumber"); FileData fileData = new FileData(ksefRefNumber + ".png", pngOutputStream.toByteArray(), "image/png", null); workitem.addFileData(fileData); } catch (WriterException | IOException e) { logger.warning("β”œβ”€β”€ ⚠️ Failed to generate QR Code:: " + e.getMessage()); } } /** * This helper method prints the QR Code on the AGL Invoice document * * @param workitem * @throws PluginException */ public void embedQRCode(ItemCollection workitem) { try { FileData pdfFileData = this.loadPDF(workitem); String ksefRefNumber = workitem.getItemValueString("ksef.referenceNumber"); FileData qrCodeFileData = workitem.getFileData(ksefRefNumber + ".png"); if (qrCodeFileData.getContent().length == 0) { qrCodeFileData = snapshotService.getWorkItemFile(workitem.getUniqueID(), ksefRefNumber + ".png"); } // now we have the QR Code (qrCodeFileData) and the PDF (pdfFileData) PDDocument document = PDDocument.load(pdfFileData.getContent()); logger.info("pdf file loaded, embed qrcode..."); // place thee PNG on the first page 10x10 cm from left upper corner // .... PDPage page = document.getPage(0); PDImageXObject pdImage = PDImageXObject.createFromByteArray(document, qrCodeFileData.getContent(), pdfFileData.getName()); // PDPageContentStream contentStream = new PDPageContentStream(document, page); PDPageContentStream contentStream = new PDPageContentStream( document, page, PDPageContentStream.AppendMode.APPEND, // Append statt Overwrite true, // compress true // resetContext - wichtig fΓΌr konsistente Grafik-ZustΓ€nde ); // Mit Grâßenangabe (Breite und HΓΆhe in Points): // QR-Code zeichnen // SeitenhΓΆhe auslesen float pageHeight = page.getMediaBox().getHeight(); // Position von OBEN berechnen (z.B. 50 Points vom oberen Rand) float marginFromTop = 230; float imageWidth = 50; float imageHeight = 50; float imageX = 350; float imageY = pageHeight - marginFromTop - imageHeight; // Von oben berechnet! contentStream.drawImage(pdImage, imageX, imageY, imageWidth, imageHeight); // Text darunter schreiben String text = "KSeF-No.: " + workitem.getItemValueString("ksef.number"); float fontSize = 7; PDType1Font font = PDType1Font.COURIER; float textX = imageX + 5; float textY = imageY - 5; // 10 Points unter dem Bild contentStream.beginText(); contentStream.setFont(font, fontSize); contentStream.newLineAtOffset(textX, textY); contentStream.showText(text); contentStream.endText(); contentStream.close(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); document.save(baos); byte[] eInvoiceData = baos.toByteArray(); document.close(); baos.close(); pdfFileData.setContent(eInvoiceData); // finally update the pdf file workitem.removeFile(pdfFileData.getName()); // set new pdf file... workitem.addFileData(pdfFileData); } catch (IOException | PluginException e) { logger.warning("Failed to embed QR Code on PDF file: " + e.getMessage()); } } /** * Encrypt invoice XML with the session encryption keys * * @param invoiceXml the invoice XML content * @param encryption the session encryption containing AES key and IV * @return encrypted invoice bytes * @throws PluginException if encryption fails */ private byte[] encryptInvoiceWithSessionKeys(byte[] invoiceXml, SessionEncryption encryption) throws PluginException { try { logger.info("β”‚ β”œβ”€β”€ Encrypting invoice with session keys..."); // Use the SAME keys that were sent during session creation! SecretKeySpec aesKey = new SecretKeySpec(encryption.getAesKeyBytes(), "AES"); IvParameterSpec iv = new IvParameterSpec(encryption.getInitializationVector()); // Encrypt with AES-256-CBC Cipher aesCipher; aesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); aesCipher.init(Cipher.ENCRYPT_MODE, aesKey, iv); byte[] encryptedData = aesCipher.doFinal(invoiceXml); logger.info("β”‚ β”œβ”€β”€ Invoice encrypted - " + encryptedData.length + " bytes"); return encryptedData; } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { e.printStackTrace(); throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API, "Error encrypt Invoice: " + e.getMessage()); } } /** * Calculate SHA-256 hash of data * * @param data byte array to hash * @return Base64-encoded hash * @throws NoSuchAlgorithmException if SHA-256 is not available */ private String calculateSHA256Hash(byte[] data) throws NoSuchAlgorithmException { MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); byte[] hashBytes = sha256.digest(data); return Base64.getEncoder().encodeToString(hashBytes); } /** * This helper method tries to find the pdf file matching the item * 'cargosoft.import.filename'. If not found the method returns the first .pdf * file form the list of attachments. The method returns null if no pdf file * exits * * @param workitem * @return the pdf FileData object * @throws PluginException if not pdf file was found */ public FileData loadPDF(ItemCollection workitem) throws PluginException { FileData pdfFileData = null; String cargosoftFileName = workitem.getItemValueString("cargosoft.import.filename"); if (!cargosoftFileName.isEmpty()) { cargosoftFileName = cargosoftFileName.replace(".xml", ".pdf"); // try to load filedata.... pdfFileData = snapshotService.getWorkItemFile(workitem.getUniqueID(), cargosoftFileName); } if (pdfFileData == null) { // take first pdf file available List fileNames = workitem.getFileNames(); for (String filename : fileNames) { if (filename.toLowerCase().endsWith(".pdf")) { pdfFileData = snapshotService.getWorkItemFile(workitem.getUniqueID(), filename); break; } } } if (pdfFileData == null) { throw new PluginException(KSeFAPIService.class.getSimpleName(), DOCUMENT_ERROR, "DOCUMENT_ERROR: no pdf file found!"); } return pdfFileData; } /** * This method returns a text-block ItemCollection for a specified name. * * @param name in attribute txtname * * */ public FileData loadTextBlockFileData(String name, String fileName) { ItemCollection textBlockItemCollection = null; // load text-block by name.... String sQuery = "(type:\"" + TYPE_TEXTBLOCK + "\" AND txtname:\"" + name + "\")"; Collection col; try { // find the textblock... col = documentService.find(sQuery, 1, 0); if (col.size() > 0) { textBlockItemCollection = col.iterator().next(); // fetch the fileData... return snapshotService.getWorkItemFile(textBlockItemCollection.getUniqueID(), fileName); } else { logger.warning("Missing text-block : '" + name + "'"); } } catch (QueryException e) { logger.warning("getTextBlock - invalid query: " + e.getMessage()); } return null; } /** * This method loads a text-block for a specified ref and appends the named * fileData object of this document. * * @param document * @throws PluginException */ public FileData loadXMLTemplate(ItemCollection workitem, ItemCollection config) throws PluginException { String textblock = config.getItemValueString("textblock"); String template = config.getItemValueString("template"); String sourceName = config.getItemValueString("source"); String targetName = "factur-x.xml"; // adapt text.... sourceName = workflowService.adaptText(sourceName, workitem); if ((template == null || template.isEmpty()) || (textblock == null || textblock.isEmpty())) { throw new PluginException(KSeFAPIService.class.getSimpleName(), KSeFAPIService.CONFIG_ERROR, "invalid e-invoice configuration in model event - textblock/template reference not defined!"); } // load the text block FileData fileData = this.loadTextBlockFileData(textblock, template); // do we found the document? if (fileData == null) { throw new PluginException(KSeFAPIService.class.getSimpleName(), KSeFAPIService.CONFIG_ERROR, "invalid e-invoice configuration in model event - textblock/template: " + textblock + "/" + template + " not found!"); } fileData.setName(targetName); return fileData; } }