diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportAdapter.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportAdapter.java new file mode 100644 index 0000000..7dd40a1 --- /dev/null +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportAdapter.java @@ -0,0 +1,335 @@ +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.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; + +import javax.inject.Inject; + +import org.apache.poi.ss.usermodel.CellCopyPolicy; +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.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.AdapterException; +import org.imixs.workflow.exceptions.PluginException; +import org.imixs.workflow.exceptions.QueryException; +import org.imixs.workflow.poi.POIFindReplaceAdapter; + +/** + * Der OPListExportAdapter importiert eine Excel Datei aus einem Textblock + * + *
+ * {@code
+        textblock-ref
+        filename
+        filename
+   }
+ * 
+ *

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

+ * Der Adapter kopiert die Rechnungsdaten in neue Zeilen, welche ab Zeilnenummer + * 16 eingefügt werden. + *

+ * + * Abschliessend wird die Zelle 'TOTAL' noch aktualisiert. + * + * @version 1.0 + * @author rsoika + */ +public class OPListExportAdapter extends POIFindReplaceAdapter { + + private static Logger logger = Logger.getLogger(OPListExportAdapter.class.getName()); + + public static final String ERROR_CONFIG = "CONFIG_ERROR"; + final String TYPE_TEXTBLOCK = "textblock"; + + @Inject + WorkflowService workflowService; + + @Inject + DocumentService documentService; + + @Inject + SnapshotService snapshotService; + + /** + * 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 { + + // read the zahlungsavis options + ItemCollection evalItemCollection = workflowService.evalWorkflowResult(event, "opliste", document, false); + if (evalItemCollection == null) { + throw new PluginException(OPListExportAdapter.class.getSimpleName(), ERROR_CONFIG, + "missing op-list configuration in model event - please check model configuration"); + } + String textblock = evalItemCollection.getItemValueString("textblock"); + String template = evalItemCollection.getItemValueString("template"); + String targetName = evalItemCollection.getItemValueString("target-name"); + // adapt text.... + targetName = workflowService.adaptText(targetName, document); + + appendExcelTemplate(document, textblock, template, targetName); + + // 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"); + try { + this.updateFileData(document.getFileData(targetName), document, replaceDevList, eval); + + // now we add separate lines for each invoice.... + insertInvoiceRows(document, targetName); + } catch (PluginException | IOException | QueryException e) { + throw new PluginException(OPListExportAdapter.class.getSimpleName(), ERROR_CONFIG, + "failed to update op-liste: " + e.getMessage()); + } + return document; + } + + /** + * This helper method inserts a row for each invoice of the current Zahlunsavis + * 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 { + + double saldo = 0; + // load dummy rechnungen + String spaceID = document.getItemValueString("space.ref"); + + Map> invoiceMap = groupInvoicesBySpaceID(spaceID); + + FileData fileData = document.getFileData(fileName); + + // 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); + CellReference cr = new CellReference("A12"); + XSSFRow referenceRow = sheet.getRow(cr.getRow()); + int referenceRowPos = 12; + int rowPos = 12; + // int lastRow = sheet.getLastRowNum(); + int lastRow = 9999; + logger.finest("Last rownum=" + lastRow); + + int totalRowCount = invoiceMap.size(); + for (List list : invoiceMap.values()) { + totalRowCount = totalRowCount + list.size(); + } + + sheet.shiftRows(rowPos, lastRow, totalRowCount, true, true); + for (Map.Entry> entry : invoiceMap.entrySet()) { + String dbtrNumber = entry.getKey(); + List invoices = entry.getValue(); + + // erzeuge eine Zwischenüberschrift für den Debitor.... + XSSFRow categoryRow = sheet.createRow(rowPos); + categoryRow.copyRowFrom(referenceRow, new CellCopyPolicy()); + // insert values + categoryRow.getCell(0).setCellValue(dbtrNumber); + String debitorName = invoices.get(0).getItemValueString("dbtr.name"); + categoryRow.getCell(1).setCellValue(debitorName); + + // calculate summ of all invoices + double catSum = 0; + for (ItemCollection invoice : invoices) { + catSum = catSum + invoice.getItemValueDouble("invoice.total"); + } + if (catSum < 0) { + // SOLL + categoryRow.getCell(6).setCellValue(catSum); + } else { + categoryRow.getCell(8).setCellValue(catSum); + } + + rowPos++; + // jetzt füge alle Rechnungen an. + 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(2).setCellValue(invoice.getItemValueString("invoice.number")); + row.getCell(3).setCellValue(invoice.getItemValueString("invoice.text")); + row.getCell(4).setCellValue(invoice.getItemValueDate("invoice.date")); + row.getCell(5).setCellValue(invoice.getItemValueDate("invoice.duedate")); + double total = invoice.getItemValueDouble("invoice.total"); + if (total < 0) { + // SOLL + row.getCell(6).setCellValue(total); + row.getCell(7).setCellValue(invoice.getItemValueString("invoice.currency")); + } else { + // HABEN + row.getCell(8).setCellValue(total); + row.getCell(9).setCellValue(invoice.getItemValueString("invoice.currency")); + } + + saldo = saldo + total; + rowPos++; + } + } + // delete reference row 12 + sheet.shiftRows(referenceRowPos, lastRow + totalRowCount, -1, true, true); + + // finally update the total formula... + evalXSSFSheet(doc, sheet, "TOTAL"); + + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + // write back the file + + doc.write(byteArrayOutputStream); + doc.close(); + byte[] newContent = byteArrayOutputStream.toByteArray(); + FileData fileDataNew = new FileData(fileData.getName(), newContent, fileData.getContentType(), null); + // update the fileData + document.addFileData(fileDataNew); + document.setItemValue("space.saldo", saldo); + logger.finest("......new document added"); + + } catch (IOException e) { + throw new PluginException(OPListExportAdapter.class.getSimpleName(), ERROR_CONFIG, + "failed to update zahlungsavis: " + e.getMessage()); + } + } + + /** + * 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(OPListExportAdapter.class.getSimpleName(), ERROR_CONFIG, + "invalid op-list configuration in model event - textblock/template reference not defined!"); + } + + // load the text block + FileData fileData = loadTextBlockFileData(textblockRef, template); + + // do we found the document? + if (fileData == null) { + throw new PluginException(OPListExportAdapter.class.getSimpleName(), ERROR_CONFIG, + "invalid op-list configuration in model event - template=" + template + " not found!"); + } + fileData.setName(targetName); + // append document + logger.info("...append new op-list Template: " + targetName); + + document.addFileData(fileData); + + } + + /** + * 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; + } + + /** + * Diese Methode groupiert eine Rechnugnsliste nach Debitorennummern + * + * @param spaceID + * @return + */ + private Map> groupInvoicesBySpaceID(String spaceID) { + + Map> result = new HashMap<>(); + + try { + List invoices = documentService.find( + "$modelversion:rechnungsausgang-de-1.0 AND type:workitem AND $uniqueidref:" + spaceID, 9999, 0); + + for (ItemCollection invoice : invoices) { + + String dbtrNumber = invoice.getItemValueString("dbtr.number"); + // haben wir shon ein listchen? + List invoiceList = result.get(dbtrNumber); + if (invoiceList == null) { + // erzeuge eine neue liste + invoiceList = new ArrayList(); + } + + invoiceList.add(invoice); + + // speicher wieder die neue liste + result.put(dbtrNumber, invoiceList); + + } + + } catch (QueryException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return result; + } + +} \ No newline at end of file diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportController.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportController.java new file mode 100644 index 0000000..ff053d1 --- /dev/null +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportController.java @@ -0,0 +1,81 @@ +package com.alexanderlogistics; +/******************************************************************************* + * Imixs Workflow Technology + * Copyright (C) 2003, 2008 Imixs Software Solutions GmbH, + * http://www.imixs.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You can receive a copy of the GNU General Public + * License at http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Imixs Software Solutions GmbH - initial API and implementation + * Ralph Soika + * + *******************************************************************************/ + +import java.util.logging.Logger; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Named; + + +import org.imixs.workflow.engine.scheduler.SchedulerController; + +/** + * The DatevController is used to configure the DatevScheduler. This service is + * used to generate datev export workitems. + *

+ * The Controller creates a configuration entity "type=configuration; + * txtname=datev". + *

+ * The following config items are defined: + * + * The following config items are defined: + * + *

+ * _model_version = model version for the SEPA export
+ * _initial_task = inital task ID
+ * 
+ * + * + * @author rsoika + * + */ +@Named(value = "oplistExportController") +@RequestScoped +public class OPListExportController extends SchedulerController { + + public static final String OPLIST_EXPORT_CONFIGURATION = "OPLIST_EXPORT_CONFIGURATION"; + + private static final long serialVersionUID = 1L; + private static Logger logger = Logger.getLogger(OPListExportController.class.getName()); + + @Override + public String getName() { + return OPLIST_EXPORT_CONFIGURATION; + } + + /** + * Returns the sepa scheduler class name. This name depends on the _export_type. + * + * There are two export interfaces available - csv and XML + * + */ + @Override + public String getSchedulerClass() { + String schedulerClass = OPListExportScheduler.class.getName(); + logger.finest("...... scheduler: " + schedulerClass); + return schedulerClass; + } + +} diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportScheduler.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportScheduler.java new file mode 100644 index 0000000..5fcc169 --- /dev/null +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportScheduler.java @@ -0,0 +1,140 @@ +/******************************************************************************* + * Imixs Workflow Technology + * Copyright (C) 2001, 2008 Imixs Software Solutions GmbH, + * http://www.imixs.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You can receive a copy of the GNU General Public + * License at http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Imixs Software Solutions GmbH - initial API and implementation + * Ralph Soika + *******************************************************************************/ +package com.alexanderlogistics; + +import java.text.DateFormat; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.logging.Logger; + +import javax.ejb.EJB; + +import org.imixs.marty.team.TeamService; +import org.imixs.workflow.FileData; +import org.imixs.workflow.ItemCollection; +import org.imixs.workflow.engine.DocumentService; +import org.imixs.workflow.engine.ModelService; +import org.imixs.workflow.engine.ReportService; +import org.imixs.workflow.engine.WorkflowService; +import org.imixs.workflow.engine.scheduler.Scheduler; +import org.imixs.workflow.engine.scheduler.SchedulerException; +import org.imixs.workflow.engine.scheduler.SchedulerService; +import org.imixs.workflow.exceptions.AccessDeniedException; +import org.imixs.workflow.exceptions.ModelException; +import org.imixs.workflow.exceptions.PluginException; +import org.imixs.workflow.exceptions.ProcessingErrorException; +import org.imixs.workflow.exceptions.QueryException; + +/** + * The OPListExportScheduler erzeugt für jeden Berich ein neues Workitem mit + * einer Excel Datei mit allen offenen Rechnungen. + *

+ * The class implements the interface + * _org.imixs.workflow.engine.scheduler.Scheduler_ and can be used in + * combination with the Imxis-Workflow Scheduler Service. + * + * @see SchedulerService + * @author rsoika + * + */ +public class OPListExportScheduler implements Scheduler { + + public static final int MAX_COUNT = 999; + public static final String OPLIST_ERROR = "OPLIST_ERROR"; + + public static DateFormat dateFormat = new SimpleDateFormat("ddMMyyyy"); + public static DecimalFormat decimalFormat = new DecimalFormat("0.00", + new DecimalFormatSymbols(java.util.Locale.GERMANY)); + + @EJB + DocumentService documentService; + + @EJB + WorkflowService workflowService; + + @EJB + ModelService modelService; + + @EJB + TeamService teamService; + + @EJB + ReportService reportService; + + private static Logger logger = Logger.getLogger(OPListExportScheduler.class.getName()); + + /** + * This is the method which processes the timeout event depending on the running + * timer settings. + * + * + * + * @param timer + * @throws QueryException + */ + public ItemCollection run(ItemCollection configuration) throws SchedulerException { + List invoices = null; + StringBuffer buffer = new StringBuffer(); + int lines = 0; + + configuration.removeItem("_scheduler_logmessage"); + logMessage(configuration, "....export OP-Listen..."); + + // Hole dir alle Bereiche + + List spaces = teamService.getSpaces(); + // Jezt erzeugen wir für jeden Berich ein neues Workitem falls offene Rechnungen + for (ItemCollection space : spaces) { + + ItemCollection workitem = new ItemCollection(); + workitem.setItemValue("space.ref", space.getUniqueID()); + + try { + workflowService.processWorkItem( // + workitem.task(1000). // + event(100). // + model("opliste-de-1.0")); + } catch (AccessDeniedException | ProcessingErrorException | PluginException | ModelException e) { + e.printStackTrace(); + } + } + + // transfer file via FTP... + DateFormat formatter = new SimpleDateFormat("yyyyMMddHHmm"); + // MMM d, yyyy HH:mm a + FileData fileData = new FileData("OPD_" + formatter.format(new Date()) + ".txt", + String.valueOf(buffer).getBytes(), null, null); + + logMessage(configuration, "...completed: " + lines + " lines"); + return configuration; + } + + private void logMessage(ItemCollection configuration, String message) { + configuration.appendItemValue("_scheduler_logmessage", message); + logger.info(message); + } + +} diff --git a/office-alexander-logistics-app/src/main/webapp/layout/commandbox_admin_custom.xhtml b/office-alexander-logistics-app/src/main/webapp/layout/commandbox_admin_custom.xhtml index 15a47ee..9e3c874 100644 --- a/office-alexander-logistics-app/src/main/webapp/layout/commandbox_admin_custom.xhtml +++ b/office-alexander-logistics-app/src/main/webapp/layout/commandbox_admin_custom.xhtml @@ -9,6 +9,7 @@

  • Cargosoft Export
  • Analyse Rechnungseingang
  • Kreditoren/Debitoren
  • +
  • OP Listen Export
  • diff --git a/office-alexander-logistics-app/src/main/webapp/pages/admin/oplist_export.xhtml b/office-alexander-logistics-app/src/main/webapp/pages/admin/oplist_export.xhtml new file mode 100644 index 0000000..ccf45a9 --- /dev/null +++ b/office-alexander-logistics-app/src/main/webapp/pages/admin/oplist_export.xhtml @@ -0,0 +1,83 @@ + + + + + + + + + +
    +
    +

    OP-Listen Verwaltung

    + + + + +
    +
    +
    +

    + Die OP-Listen Verwaltung erzeugt regelmäßig + Workitems mit einem aktuellen Abbild der offenen Ausgangsrechnungen, sortiert nach Bereichen. +

    +
    + +
    +

    Scheduler

    + + + + +
    +
    + + + + +
    +
    +
    +
    + + + +
    diff --git a/templates/op-liste_template.xlsx b/templates/op-liste_template.xlsx new file mode 100644 index 0000000..033c0d8 Binary files /dev/null and b/templates/op-liste_template.xlsx differ diff --git a/workflow/opliste-de-1.0.0.bpmn b/workflow/opliste-de-1.0.0.bpmn new file mode 100644 index 0000000..bce14e2 --- /dev/null +++ b/workflow/opliste-de-1.0.0.bpmn @@ -0,0 +1,514 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<itemvalue>$workflowstatus</itemvalue> + +]]> + + + +]]> + + + + + + + + + + StartEvent_1 + Task_1 + EndEvent_3 + Task_2 + Task_4 + IntermediateCatchEvent_8 + IntermediateCatchEvent_3 + IntermediateCatchEvent_1 + IntermediateCatchEvent_2 + + + + SequenceFlow_4 + + + + + sequencenumber $created - space.name]]> + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_16 + SequenceFlow_1 + SequenceFlow_17 + + + SequenceFlow_19 + + + + + sequencenumber $created - space.name]]> + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_4 + SequenceFlow_3 + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + home +Mahnwesen +OP-Liste Template + op-liste_template.xlsx +op-liste_space.name_$lasteventdate.xlsx + + + C6 + sequencenumber + text + + + C8 + space.name + text + + + + I6 + $lasteventdate + date + +]]> + + + + + + + + + + + + false + + + + + + + + SequenceFlow_5 + SequenceFlow_16 + + + DataOutput_2 + + + DataOutput_2 + + + + + + + + sequencenumber $created - space.name]]> + + + true + + + + + + + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_18 + SequenceFlow_19 + + + + + + + + SequenceFlow_17 + SequenceFlow_18 + + + + + + + + + + + SequenceFlow_1 + + + + + + + + + + + + + SequenceFlow_3 + SequenceFlow_5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file