package com.alexanderlogistics; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.StringTokenizer; import java.util.logging.Logger; import java.util.regex.Pattern; import javax.inject.Inject; import org.imixs.marty.ejb.SequenceService; import org.imixs.workflow.ItemCollection; import org.imixs.workflow.SignalAdapter; import org.imixs.workflow.WorkflowKernel; import org.imixs.workflow.engine.WorkflowService; import org.imixs.workflow.engine.plugins.SplitAndJoinPlugin; import org.imixs.workflow.exceptions.AdapterException; import org.imixs.workflow.exceptions.ModelException; import org.imixs.workflow.exceptions.PluginException; /** * Der CargosoftSplitAdapter erezeugt anhand der Positionstablelle eignee * cargosoft export workitems, getrennt für jede Buchungsperdiode. Dies ist * notwendig, da Cargosoft selbst nicht mit unterschiedlichen Buchungsperioden * in einer Rechnung umgehen kann. *

* adapter exports the invoice data to a ftp server connected to cargosoft. Da * auch eine fortlaufene Rechnungsnummer von Cargosoft zwingend vorgeschrieben * ist, erzeugt der adapter auch für jedes weiteres cargosoft export worktiem * ein neue Sequencenummer die im Hautpworkitem gespeichert wird. *

* Der Adapter wird im Modell wie folgt konfiguriert * *

 * {@code
        
            cargosoft-export-1.0
            1000
            100
            (?!txtworkflowhistory)(^[a-zA-Z]|^_)
        
       
   }
 * 
*

* Because we also export the attachment data to cargosoft, the adaper lookups * the conente of the attachment in the snapshot of the origin workitem * * * @version 1.0 * @author rsoika */ public class CargosoftSplitAdapter implements SignalAdapter { public static final String CONFIG_ERROR = "CONFIG_ERROR"; private static Logger logger = Logger.getLogger(CargosoftSplitAdapter.class.getName()); @Inject WorkflowService workflowService; @Inject SequenceService sequenceService; /** * This method computes the cargosoft export data file * * @throws PluginException */ @SuppressWarnings("rawtypes") @Override public ItemCollection execute(ItemCollection workitem, ItemCollection event) throws AdapterException, PluginException { logger.info("......starting export..."); try { // read the cargosoft split options ItemCollection evalItemCollection = workflowService.evalWorkflowResult(event, "cargosoft", workitem, false); if (evalItemCollection == null) { throw new PluginException(CargosoftSplitAdapter.class.getSimpleName(), CONFIG_ERROR, "missign cargosoft configuration in model event - please check model configuration"); } String model = evalItemCollection.getItemValueString("model"); int taskID = evalItemCollection.getItemValueInteger("task"); int eventID = evalItemCollection.getItemValueInteger("event"); String items = evalItemCollection.getItemValueString("items"); if (model.isEmpty() || taskID == 0 || eventID == 0) { throw new PluginException(CargosoftSplitAdapter.class.getSimpleName(), CONFIG_ERROR, "missign cargosoft configuration 'model', 'task', 'event' - please check model configuration"); } // Zunaechst müssen wir festelstellen, ob es mehrere unterschiedliche // Buchungsperioden in der Positionstabelle gibt. List buchungsPersioden = new ArrayList(); // add haupt buchungsperiode buchungsPersioden.add(workitem.getItemValueString("invoice.period")); List positionsTabelle = InvoicePlugin.explodeChildList(workitem); for (ItemCollection posItem : positionsTabelle) { String childPeriod = posItem.getItemValueString(InvoicePlugin.ITEM_INVOICE_PERIOD); if (!childPeriod.isEmpty() && !buchungsPersioden.contains(childPeriod)) { buchungsPersioden.add(childPeriod); } } // nun wird für jede Buchungsperiode ein eigener corgosoft Export angestoßen.... boolean nextSequencenumber = false; for (String buchungsPeriode : buchungsPersioden) { // nun entnehmen wir aus der positionsTabelle alle einträge die zu dieser // Periode passen... // (die erste Periode ist die default periode welche wir nehemn wenn keine // Periode angegeben wurde). ItemCollection workitemSubProcess = new ItemCollection(); // now clone the field list... copyItemList(items, workitem, workitemSubProcess); // in die _childs Liste kommen jetzt nur passende Positionseinträge mit der // selben Buchungsperiode List splitPositionen = new ArrayList(); ListIterator iter = positionsTabelle.listIterator(); int row = 1; float netto = 0; float total = 0; // float tax=0; while (iter.hasNext()) { ItemCollection posItem = iter.next(); String childPeriod = posItem.getItemValueString(InvoicePlugin.ITEM_INVOICE_PERIOD); if (childPeriod.isEmpty() || buchungsPeriode.equals(childPeriod)) { posItem.setItemValue("numpos", row); splitPositionen.add(posItem.getAllItems()); iter.remove(); row++; netto = netto + posItem.getItemValueFloat("amount"); total = total + posItem.getItemValueFloat("total"); } } workitemSubProcess.setItemValue(InvoicePlugin.ITEM_INVOICE_PERIOD, buchungsPeriode); // jetzt muessen wir die neue Rechnungssumme bilden.... workitemSubProcess.setItemValue("order.total", total); workitemSubProcess.setItemValue("order.total.tax", total - netto); workitemSubProcess.setItemValue("order.total.netto", netto); logger.info("......create cargosoft split buchung - invoice.period=" + buchungsPeriode); // update the child items (reduzierte Positionstabelle) workitemSubProcess.setItemValue(InvoicePlugin.CHILD_ITEM_PROPERTY, splitPositionen); workitemSubProcess.model(model).task(taskID).event(eventID); workitemSubProcess.replaceItemValue(WorkflowService.UNIQUEIDREF, workitem.getUniqueID()); // ab dem zweiten durchlauf benötigen wir immer eine neue sequencenumer if (nextSequencenumber) { workitemSubProcess.removeItem("numsequencenumber"); // wegen numernkreis bildung muss hier die WrokflowGroup kurzfirstig auf // 'Rechnungseingang' gesetzt werden! workitemSubProcess.setItemValue(WorkflowKernel.WORKFLOWGROUP, "Rechnungseingang"); sequenceService.computeSequenceNumber(workitemSubProcess); logger.info(".......compute new sequence number: " + workitemSubProcess.getItemValueLong("numsequencenumber")); workitem.appendItemValueUnique("numsequencenumber_sub", workitemSubProcess.getItemValueLong("numsequencenumber")); } else { nextSequencenumber = true; } workitemSubProcess = workflowService.processWorkItem(workitemSubProcess); // finally add the new workitemRef into the origin workitem.appendItemValueUnique(SplitAndJoinPlugin.LINK_PROPERTY, workitemSubProcess.getUniqueID()); } } catch (ModelException e) { logger.severe("cargosoft split failed: " + e.getMessage()); throw new PluginException(CargosoftSplitAdapter.class.getSimpleName(), CONFIG_ERROR, e.getMessage(), e); } return workitem; } /** * This Method copies the fields defined in 'items' into the targetWorkitem. * Multiple values are separated with comma ','. *

* In case a item name contains '|' the target field name will become the right * part of the item name. *

* Example: {@code * txttitle,txtfirstname * * txttitle|newitem1,txtfirstname|newitem2 * * } * *

* Optional also reg expressions are supported. In this case mapping of the item * name is not supported. *

* Example: {@code * (^artikel$|^invoice$),txtTitel|txtNewTitel * * * } A reg expression must be includes in brackets. * */ protected void copyItemList(String items, ItemCollection source, ItemCollection target) { // clone the field list... StringTokenizer st = new StringTokenizer(items, ","); while (st.hasMoreTokens()) { String field = st.nextToken().trim(); // test if field is a reg ex if (field.startsWith("(") && field.endsWith(")")) { Pattern itemPattern = Pattern.compile(field); Map> map = source.getAllItems(); for (String itemName : map.keySet()) { if (itemPattern.matcher(itemName).find()) { target.replaceItemValue(itemName, source.getItemValue(itemName)); } } } else { // default behavior without reg ex int pos = field.indexOf('|'); if (pos > -1) { target.replaceItemValue(field.substring(pos + 1).trim(), source.getItemValue(field.substring(0, pos).trim())); } else { target.replaceItemValue(field, source.getItemValue(field)); } } } } }