From 947bed1ede4df3af098f92f0f789b5aaf0d684d7 Mon Sep 17 00:00:00 2001 From: Ralph Soika Date: Sun, 8 Dec 2024 11:20:45 +0100 Subject: [PATCH] =?UTF-8?q?Anpassung=20excel=20export=20f=C3=BCr=20Steuerb?= =?UTF-8?q?elege?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AGLAnalyticExcelAdapter.java | 442 +++++++++--------- templates/steuerbescheid-liste_template.xlsx | Bin 50475 -> 50477 bytes 2 files changed, 224 insertions(+), 218 deletions(-) diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticExcelAdapter.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticExcelAdapter.java index 30f2ee4..8af8716 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticExcelAdapter.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticExcelAdapter.java @@ -61,239 +61,245 @@ import jakarta.inject.Inject; */ public class AGLAnalyticExcelAdapter extends POIFindReplaceAdapter { - private static Logger logger = Logger.getLogger(AGLAnalyticExcelAdapter.class.getName()); + private static Logger logger = Logger.getLogger(AGLAnalyticExcelAdapter.class.getName()); - @Inject - WorkflowService workflowService; + @Inject + WorkflowService workflowService; - @Inject - InvoiceService opListExportService; + @Inject + InvoiceService opListExportService; - @Inject - DocumentService documentService; + @Inject + DocumentService documentService; - @Inject - ConfigService configService; + @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 { + /** + * 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")); - row.getCell(4, MissingCellPolicy.CREATE_NULL_AS_BLANK) - .setCellValue(invoice.getItemValueString("space.name")); - 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(); + // 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"); } - } - } - } - /** - * 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 { + // 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)); - 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!"); + 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; } - // load the text block - FileData fileData = opListExportService.loadTextBlockFileData(textblockRef, template); + /** + * 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 { - // 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!"); + // 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(); + } + } + } } - fileData.setName(targetName); - // append document - logger.info("...append new steuerbescheide Template: " + targetName); - document.addFileData(fileData); - } + /** + * 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; - } + + /** + * 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; + } } \ No newline at end of file diff --git a/templates/steuerbescheid-liste_template.xlsx b/templates/steuerbescheid-liste_template.xlsx index 8a39026e405d1e569c980a0d6270464385ce16e5..6a425a1d6c4124201c2ecf66047949d8a2fcf2e5 100644 GIT binary patch delta 7151 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