package com.alexanderlogistics; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; import org.apache.poi.ss.usermodel.CellCopyPolicy; import org.apache.poi.ss.usermodel.Row.MissingCellPolicy; import org.apache.poi.ss.util.CellReference; import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; 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.AdapterException; import org.imixs.workflow.exceptions.PluginException; import org.imixs.workflow.exceptions.QueryException; import org.imixs.workflow.office.config.ConfigService; import org.imixs.workflow.poi.POIFindReplaceAdapter; import org.imixs.workflow.util.XMLParser; import jakarta.inject.Inject; /** * Der AGLAnalyticExcelAdapter exportiert importiert eine Excel Datei aus einem * Textblock und fügt die offenen Steuerbescheid Daten ein * * Der Adapter wird im Modell wie folgt konfiguriert * *
 * {@code
        
            textblock-ref
            
            filename
         
   }
 * 
* *

* Der Adapter erweitert den POIAdapter somit können Felder aktualisiert werden * (siehe POIFineReplaceAdapter). * *

* Der Adapter kopiert die Steuerbescheiddaten in neue Zeilen, welche ab * Zeilennummer * 16 eingefügt werden. *

* * Abschliessend wird die Zelle 'TOTAL' noch aktualisiert. * * * @version 1.0 * @author rsoika */ public class AGLAnalyticExcelAdapter extends POIFindReplaceAdapter { private static Logger logger = Logger.getLogger(AGLAnalyticExcelAdapter.class.getName()); @Inject WorkflowService workflowService; @Inject InvoiceService opListExportService; @Inject DocumentService documentService; @Inject ConfigService configService; /** * This method finds or create the Zahlungsavis and adds a reference * ($workitemref) to the current invoice. * * @throws PluginException */ @SuppressWarnings("unchecked") @Override public ItemCollection execute(ItemCollection document, ItemCollection event) throws AdapterException, PluginException { // ermittle die Hauptwährungen ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION"); List currencyList = configItemCollection.getItemValue("currency.out"); if (currencyList == null || currencyList.size() < 2) { throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG, "missing Currency configuration - please check parameters"); } // read the Steuerbescheide options ItemCollection steuerbescheideConfig = workflowService.evalWorkflowResult(event, "steuerbescheide", document, false); if (steuerbescheideConfig == null || !steuerbescheideConfig.hasItem("excel-export")) { throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), CONFIG_ERROR, "missing Steuerbescheide configuration in model event - please check model configuration"); } List excelDefList = steuerbescheideConfig.getItemValue("excel-export"); ItemCollection excelDefCollection = XMLParser.parseItemStructure(excelDefList.get(0)); String textblock = excelDefCollection.getItemValueString("textblock"); String template = excelDefCollection.getItemValueString("template"); String targetName = excelDefCollection.getItemValueString("target-name"); // adapt text.... targetName = workflowService.adaptText(targetName, document); try { appendExcelTemplate(document, textblock, template, targetName); insertInvoiceRows(document, targetName); logger.info("... loading poi configuration.."); // Process POI instructions // read the config ItemCollection poiConfig = workflowService.evalWorkflowResult(event, "poi-update", document, false); if (poiConfig == null || !poiConfig.hasItem("findreplace")) { throw new PluginException(POIFindReplaceAdapter.class.getSimpleName(), CONFIG_ERROR, "missing poi configuration"); } List replaceDevList = poiConfig.getItemValue("findreplace"); String eval = poiConfig.getItemValueString("eval"); logger.info("... update template with normal poi information.."); this.updateFileData(document.getFileData(targetName), document, replaceDevList, eval); } catch (PluginException | IOException | QueryException e) { throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG, "failed to update steuerbescheide: " + e.getMessage()); } logger.info("... completed!"); return document; } /** * This helper method inserts a row for each invoice at row 16 * into the excel template file *

* The method copies the Row A16 as a reference row *

* The named cell 'TOTAL' should contain the summary formula. It will be * evaluated at the end. * * @throws PluginException * @throws QueryException */ private void insertInvoiceRows(ItemCollection document, String fileName) throws PluginException, QueryException { // load dummy rechnungen List invoices = loadSteuerbescheide(); FileData fileData = document.getFileData(fileName); // load XSSFWorkbook XSSFWorkbook doc = null; try (InputStream imputStream = new ByteArrayInputStream(fileData.getContent())) { doc = new XSSFWorkbook(imputStream); // NOTE: we only take the first sheet ! XSSFSheet sheet = doc.getSheetAt(0); CellReference invoiceRefCell = new CellReference("A11"); XSSFRow referenceRowInvoice = sheet.getRow(invoiceRefCell.getRow()); int referenceRowPos = 10; int rowPos = referenceRowPos; int lastRow = 2999; logger.finest("Last rownum=" + lastRow); // jetzt füge alle Rechnungen an. int totalRowCount = invoices.size(); sheet.shiftRows(referenceRowPos, lastRow, totalRowCount, true, true); for (ItemCollection invoice : invoices) { logger.fine("......add invoice " + invoice.getUniqueID()); // now create a new line.. XSSFRow row = sheet.createRow(rowPos); row.copyRowFrom(referenceRowInvoice, new CellCopyPolicy()); // insert values row.getCell(0) .setCellValue(invoice.getItemValueDate("$created")); row.getCell(1, MissingCellPolicy.CREATE_NULL_AS_BLANK) .setCellValue(invoice.getItemValueString("invoice.number")); row.getCell(2, MissingCellPolicy.CREATE_NULL_AS_BLANK) .setCellValue(invoice.getItemValueDate("invoice.date")); row.getCell(3, MissingCellPolicy.CREATE_NULL_AS_BLANK) .setCellValue(invoice.getItemValueDate("invoice.booking_date")); // Änderung Frau Mwangi am 5.12.2025 // Nun soll anstatt des Departments der Anmelder ausgegeben werden // row.getCell(4, MissingCellPolicy.CREATE_NULL_AS_BLANK) // .setCellValue(invoice.getItemValueString("space.name")); row.getCell(4, MissingCellPolicy.CREATE_NULL_AS_BLANK) .setCellValue(invoice.getItemValueString("steuer.anmelder")); row.getCell(5, MissingCellPolicy.CREATE_NULL_AS_BLANK) .setCellValue(invoice.getItemValueString("invoice.text")); row.getCell(6, MissingCellPolicy.CREATE_NULL_AS_BLANK) .setCellValue(invoice.getItemValueString("invoice.atc.number")); row.getCell(7, MissingCellPolicy.CREATE_NULL_AS_BLANK) .setCellValue(invoice.getItemValueDouble("invoice.total.net")); row.getCell(8, MissingCellPolicy.CREATE_NULL_AS_BLANK) .setCellValue(invoice.getItemValueDouble("invoice.saldo")); row.getCell(9, MissingCellPolicy.CREATE_NULL_AS_BLANK) .setCellValue(invoice.getItemValueDouble("steuer.eust")); row.getCell(10, MissingCellPolicy.CREATE_NULL_AS_BLANK) .setCellValue(invoice.getItemValueDate("steuer.eust.due")); row.getCell(11, MissingCellPolicy.CREATE_NULL_AS_BLANK) .setCellValue(invoice.getItemValueDouble("steuer.zoll")); row.getCell(12, MissingCellPolicy.CREATE_NULL_AS_BLANK) .setCellValue(invoice.getItemValueDate("steuer.zoll.due")); rowPos++; } // Leerzeile // categoryRow = sheet.createRow(rowPos); rowPos++; // write back the file ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); doc.write(byteArrayOutputStream); byte[] newContent = byteArrayOutputStream.toByteArray(); FileData fileDataNew = new FileData(fileData.getName(), newContent, fileData.getContentType(), null); // update the fileData document.addFileData(fileDataNew); logger.finest("......new document added"); } catch (IOException e) { throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG, "failed to update opliste: " + e.getMessage()); } finally { if (doc != null) { try { doc.close(); } catch (IOException e) { logger.severe("Failed to close workbook: " + e.getMessage()); e.printStackTrace(); } } } } /** * This method loads a text-block for a specified ref and appends the named * fileData object of this document. * * @param document * @throws PluginException */ private void appendExcelTemplate(ItemCollection document, String textblockRef, String template, String targetName) throws PluginException { if ((template == null || template.isEmpty()) || (textblockRef == null || textblockRef.isEmpty())) { throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG, "invalid steuerbescheide configuration in model event - textblock/template reference not defined!"); } // load the text block FileData fileData = opListExportService.loadTextBlockFileData(textblockRef, template); // do we found the document? if (fileData == null) { throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG, "invalid steuerbescheide configuration in model event - template=" + template + " not found!"); } fileData.setName(targetName); // append document logger.info("...append new steuerbescheide Template: " + targetName); document.addFileData(fileData); } /** * Diese Methode läd alle offenen Steuerbescheide * * @return */ private List loadSteuerbescheide() { List result = new ArrayList<>(); try { result = documentService.find( "$modelversion:steuerbescheid-* AND type:workitem", 9999, 0, "invoice.number", false); } catch (QueryException e) { e.printStackTrace(); } return result; } }