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; import java.time.YearMonth; 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; import org.imixs.workflow.engine.DocumentService; import org.imixs.workflow.exceptions.PluginException; import org.imixs.workflow.exceptions.QueryException; 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; import jakarta.faces.context.FacesContext; import jakarta.inject.Inject; import jakarta.inject.Named; /** * Der ExcelExportController exportiert rechnungen anhand von Filterangaben * (excel_export_rechnungsausgang.xhmlt) nach Excel. * * Die Ausgabe ist auf maximal 3000 Zeilen eingeschränkt * * @author rsoika * */ @Named @ConversationScoped public class ExcelExportController implements Serializable { private static final long serialVersionUID = 1L; private static Logger logger = Logger.getLogger(ExcelExportController.class.getName()); public static final String ERROR_CONFIG = "CONFIG_ERROR"; final String TYPE_TEXTBLOCK = "textblock"; public static final int MAX_ROWS = 3000; @Inject protected DocumentService documentService; @Inject WorkflowController workflowController; @Inject DocumentController documentController; @Inject SnapshotService snapshotService; private ItemCollection filter; private CustomFormItem dbtrItem; public ExcelExportController() { } @PostConstruct public void init() { reset(); } public ItemCollection getFilter() { return filter; } public void setFilter(ItemCollection filter) { this.filter = filter; } /** * Fuert verschiedene Queries aus um eine Analyse der Sachprufung durchzuführen. * * @throws PluginException */ 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? String sDateFrom = "191401070000"; // because * did not work here String sDateTo = "211401070000"; SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMMddHHmm"); if (start != null) { Calendar cal = Calendar.getInstance(); cal.setTime(start); sDateFrom = dateformat.format(cal.getTime()); } if (stop != null) { Calendar cal = Calendar.getInstance(); cal.setTime(stop); cal.add(Calendar.DATE, 1); sDateTo = dateformat.format(cal.getTime()); } // query zeit String query = "(type:workitem OR type:workitemarchive) AND ($workflowgroup:Rechnungsausgang) "; if (start != null && stop != null) { query += "AND (invoice.date:[" + sDateFrom + " TO " + sDateTo + "]) "; } else { throw new PluginException("ERROR", "Bitte geben Sie einen Zeitraum an", null); } logger.info(query); // query Sachprüfung if (!filter.getItemValueString("space.ref").isEmpty() && !"-".equals(filter.getItemValueString("space.ref"))) { query += " AND ($uniqueidref:" + filter.getItemValueString("space.ref") + ")"; } 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"; FileData fileData = loadTextBlockFileData("Excel-Export Template", template); // do we found the document? if (fileData == null) { throw new PluginException(InkassoExportAdapter.class.getSimpleName(), ERROR_CONFIG, "Missing Excel Export Template - template=" + template + " not found!"); } String targetName = "ausgangsrechnungen-export_" + dateformat.format(new Date()) + ".xlsx"; try { List invoices = documentService.find(query, MAX_ROWS, 0, "invoice.date", true); 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 | QueryException e) { throw new PluginException(InkassoExportAdapter.class.getSimpleName(), ERROR_CONFIG, "Failed to generate Excel Export: " + e.getMessage()); } logger.info("Query=" + query); return "/pages/admin/excel_export_rechnungsausgang.jsf?faces-redirect=true"; } /** * Helper method to initialize a download * * @throws IOException */ public void download(FileData fileData) throws IOException { FacesContext facesContext = FacesContext.getCurrentInstance(); ExternalContext externalContext = facesContext.getExternalContext(); externalContext.responseReset(); externalContext.setResponseContentType("application/vnd.ms-excel"); externalContext.setResponseContentLength(fileData.getContent().length); externalContext.setResponseHeader("Content-Disposition", "attachment; filename=\"" + fileData.getName() + "\""); OutputStream output = externalContext.getResponseOutputStream(); // Now you can write the InputStream of the file to the above OutputStream the // usual way. output.write(fileData.getContent()); facesContext.responseComplete(); // Important! Otherwise Faces will attempt to render the response which // obviously will fail since it's already written with a file and closed. } /** * This method returns a text-block ItemCollection for a specified name. * * @param name in attribute txtname * * */ private 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 reset the search and input state. */ public void reset() { filter = new ItemCollection(); // compute start stop based on current month YearMonth startYearMonth = YearMonth.now(); java.time.LocalDate startOfMonthDate = startYearMonth.atDay(1); java.time.LocalDate endOfMonthDate = startYearMonth.atEndOfMonth(); filter.setItemValue("start", java.util.Date.from(startOfMonthDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant())); filter.setItemValue("stop", java.util.Date.from(endOfMonthDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant())); workflowController.setWorkitem(new ItemCollection()); // debtr item dbtrItem = new CustomFormItem("dbtr.number", "text", "", false, false, false, "", null, false, 0); } public CustomFormItem getItem() { // dbtrItem.setName("dbtr.number"); 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("space.name")); row.getCell(3).setCellValue(invoice.getItemValueString("invoice.text")); row.getCell(4).setCellValue(invoice.getItemValueDate("invoice.date")); row.getCell(5).setCellValue(invoice.getItemValueDate("invoice.duedate")); row.getCell(6).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; } }