From 2c6636bfbc983594fe7cadf5d43106244cc48c66 Mon Sep 17 00:00:00 2001 From: Ralph Soika Date: Tue, 8 Apr 2025 19:53:27 +0200 Subject: [PATCH] Excel Export Funktion --- .../mahnlauf/ExcelExportController.java | 121 ++++++++++++++++-- .../admin/excel_export_rechnungsausgang.xhtml | 57 ++------- .../ausgangsrechnungen-export_template.xlsx | Bin 50320 -> 49953 bytes 3 files changed, 120 insertions(+), 58 deletions(-) diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/ExcelExportController.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/ExcelExportController.java index 4ebd982..c4d887e 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/ExcelExportController.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/ExcelExportController.java @@ -1,6 +1,9 @@ package com.alexanderlogistics.mahnlauf; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.io.Serializable; import java.text.SimpleDateFormat; @@ -9,8 +12,16 @@ import java.time.ZoneId; import java.util.Calendar; import java.util.Collection; import java.util.Date; +import java.util.List; import java.util.logging.Logger; +import org.apache.poi.ss.usermodel.CellCopyPolicy; +import org.apache.poi.ss.usermodel.Name; +import org.apache.poi.ss.util.CellReference; +import org.apache.poi.xssf.usermodel.XSSFCell; +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.imixs.archive.core.SnapshotService; import org.imixs.workflow.FileData; import org.imixs.workflow.ItemCollection; @@ -21,6 +32,8 @@ import org.imixs.workflow.faces.data.DocumentController; import org.imixs.workflow.faces.data.WorkflowController; import org.imixs.workflow.office.forms.CustomFormItem; +import com.alexanderlogistics.ZahlungsavisExportAdapter; + import jakarta.annotation.PostConstruct; import jakarta.enterprise.context.ConversationScoped; import jakarta.faces.context.ExternalContext; @@ -47,6 +60,7 @@ public class ExcelExportController implements Serializable { public static final String ERROR_CONFIG = "CONFIG_ERROR"; final String TYPE_TEXTBLOCK = "textblock"; + public static final int MAX_ROWS = 3000; @Inject protected DocumentService documentService; @@ -86,10 +100,8 @@ public class ExcelExportController implements Serializable { public String export() throws PluginException { logger.info("start export...."); - Date start = filter.getItemValueDate("start"); Date stop = filter.getItemValueDate("stop"); - logger.info("...daterange=" + start + " - " + stop); // serach date range? @@ -123,11 +135,13 @@ public class ExcelExportController implements Serializable { if (!filter.getItemValueString("space.name").isEmpty()) { query += " AND (space.name:" + filter.getItemValueString("space.name") + ")"; } - if (workflowController.getWorkitem() != null && !workflowController.getWorkitem().getItemValueString("dbtr.number").isEmpty()) { query += " AND (dbtr.number:" + workflowController.getWorkitem().getItemValueString("dbtr.number") + ")"; } + if (!filter.getItemValueString("invoice.text").isEmpty()) { + query += " AND (invoice.positions:" + filter.getItemValueString("invoice.text") + ")"; + } // load the text block String template = "ausgangsrechnungen-export_template.xlsx"; @@ -139,13 +153,17 @@ public class ExcelExportController implements Serializable { "Missing Excel Export Template - template=" + template + " not found!"); } String targetName = "ausgangsrechnungen-export_" + dateformat.format(new Date()) + ".xlsx"; - fileData.setName(targetName); - try { + List invoices = documentService.find(query, MAX_ROWS, 0); + if (invoices.size() > 0) { + insertInvoiceRows(invoices, fileData); + } + fileData.setName(targetName); + // See: // https://stackoverflow.com/questions/9391838/how-to-provide-a-file-download-from-a-jsf-backing-bean download(fileData); - } catch (IOException e) { + } catch (IOException | QueryException e) { throw new PluginException(InkassoExportAdapter.class.getSimpleName(), ERROR_CONFIG, "Failed to generate Excel Export: " + e.getMessage()); } @@ -229,10 +247,6 @@ public class ExcelExportController implements Serializable { // debtr item dbtrItem = new CustomFormItem("dbtr.number", "text", "", false, false, false, "", null, false, 0); - // public CustomFormItem(String name, String type, String label, boolean - // required, boolean readonly, boolean disabled, - // String options, - // String path, boolean hide, int span) { } public CustomFormItem getItem() { @@ -240,4 +254,91 @@ public class ExcelExportController implements Serializable { return dbtrItem; } + /** + * This helper method inserts a row for each invoice + * + * @throws PluginException + */ + private void insertInvoiceRows(List invoices, FileData fileData) throws PluginException { + // load XSSFWorkbook + try (InputStream imputStream = new ByteArrayInputStream(fileData.getContent())) { + XSSFWorkbook doc = new XSSFWorkbook(imputStream); + // NOTE: we only take the first sheet ! + XSSFSheet sheet = doc.getSheetAt(0); + + XSSFCell standCell = getCellByRef(doc, sheet, "F6"); + standCell.setCellValue(new Date()); + + CellReference cr = new CellReference("A11"); + XSSFRow referenceRow = sheet.getRow(cr.getRow()); + int referenceRowPos = referenceRow.getRowNum() + 1; + int rowPos = referenceRowPos; + // int lastRow = sheet.getLastRowNum(); + int lastRow = 999; + logger.finest("Last rownum=" + lastRow); + sheet.shiftRows(rowPos, lastRow, invoices.size(), true, true); + + for (ItemCollection invoice : invoices) { + logger.finest("......copy row..."); + + // now create a new line.. + XSSFRow row = sheet.createRow(rowPos); + row.copyRowFrom(referenceRow, new CellCopyPolicy()); + // insert values + row.getCell(0).setCellValue(invoice.getItemValueString("invoice.number")); + row.getCell(1).setCellValue(invoice.getItemValueString("dbtr.number")); + row.getCell(2).setCellValue(invoice.getItemValueString("invoice.text")); + + row.getCell(3).setCellValue(invoice.getItemValueDate("invoice.date")); + row.getCell(4).setCellValue(invoice.getItemValueDate("invoice.duedate")); + + row.getCell(5).setCellValue(invoice.getItemValueDouble("invoice.total")); + + rowPos++; + } + // delete reference row + sheet.shiftRows(referenceRowPos, lastRow + invoices.size(), -1, true, true); + + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + // write back the file + doc.write(byteArrayOutputStream); + doc.close(); + byte[] newContent = byteArrayOutputStream.toByteArray(); + fileData.setContent(newContent); + + } catch (IOException e) { + throw new PluginException(ZahlungsavisExportAdapter.class.getSimpleName(), ERROR_CONFIG, + "failed to update excel export: " + e.getMessage()); + } + } + + /** + * Returns a Cell by name or an optional absolute cell postion + *

+ * Examples for refs are 'A1', 'AB3', 'MyCell' where 'MyCell' is a named cell. + *

+ * + */ + public static XSSFCell getCellByRef(XSSFWorkbook doc, XSSFSheet sheet, String cellReference) { + XSSFCell cell = null; + + // first we test if the cellName is a named cell + Name aNamedCell = doc.getName(cellReference); + if (aNamedCell != null) { + // yes its a named cell so we need to get the referrer Formula + logger.finest("...resolving named cell = " + aNamedCell.getNameName()); + cellReference = aNamedCell.getRefersToFormula(); + // now we can find the cell by its ref + } + + CellReference cr = new CellReference(cellReference); + XSSFRow row = sheet.getRow(cr.getRow()); + if (row == null) { + logger.severe("Unable to resolve cell ref '" + cellReference + "'!"); + return null; + } + cell = row.getCell(cr.getCol()); + return cell; + } + } diff --git a/office-alexander-logistics-app/src/main/webapp/pages/admin/excel_export_rechnungsausgang.xhtml b/office-alexander-logistics-app/src/main/webapp/pages/admin/excel_export_rechnungsausgang.xhtml index 7814577..64f9bc8 100644 --- a/office-alexander-logistics-app/src/main/webapp/pages/admin/excel_export_rechnungsausgang.xhtml +++ b/office-alexander-logistics-app/src/main/webapp/pages/admin/excel_export_rechnungsausgang.xhtml @@ -7,40 +7,30 @@ - - - - - + -

Excle Export Ausgangsrechnungen

- - -
- -

Der folgende Report exportiert Ausgangsrechnungen, in eine Excel Datei. Der Export ist auf maximal 3000 Einträge pro Tabelle beschränkt

@@ -56,12 +46,8 @@ - - - -
Bis:* @@ -74,9 +60,6 @@
- - -
Abteilung: @@ -88,7 +71,6 @@
-
Debitor: @@ -101,48 +83,27 @@
-
Positionsnummer:
- + value="#{excelExportController.filter.item['invoice.text']}">
- -
- x - -
- - - - - -
-
- - - - - - - \ No newline at end of file diff --git a/templates/ausgangsrechnungen-export_template.xlsx b/templates/ausgangsrechnungen-export_template.xlsx index 1d2475b6f8b8f75538372f3018a483eb8506821c..82a50e0b92d62ab4a4f1f96c8a9a1e084ccbab02 100644 GIT binary patch delta 10467 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 delta 11359 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 NP>