office-alexander-logistics/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/CargosoftSplitAdapter.java

237 lines
No EOL
10 KiB
Java

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 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;
import org.imixs.workflow.office.util.SequenceService;
import jakarta.inject.Inject;
/**
* 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.
* <p>
* 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.
* <p>
* Der Adapter wird im Modell wie folgt konfiguriert
*
* <pre>
* {@code
<cargosoft>
<modelversion>cargosoft-export-1.0</modelversion>
<task>1000</task>
<event>100</event>
<items>(?!txtworkflowhistory)(^[a-zA-Z]|^_)</items>
</cargosoft>
}
* </pre>
* <p>
* 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<String> buchungsPersioden = new ArrayList<String>();
// add haupt buchungsperiode
buchungsPersioden.add(workitem.getItemValueString("invoice.period"));
List<ItemCollection> positionsTabelle = InvoiceUtil.explodeChildList(workitem);
for (ItemCollection posItem : positionsTabelle) {
String childPeriod = posItem.getItemValueString(InvoiceUtil.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<Map> splitPositionen = new ArrayList<Map>();
ListIterator<ItemCollection> 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(InvoiceUtil.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(InvoiceUtil.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(InvoiceUtil.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 WorkflowGroup kurzfristig auf
// 'Rechnungseingang' gesetzt werden!
workitemSubProcess.setItemValue(WorkflowKernel.WORKFLOWGROUP, workitem.getWorkflowGroup());
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 ','.
* <p>
* In case a item name contains '|' the target field name will become the right
* part of the item name.
* <p>
* Example: {@code
* txttitle,txtfirstname
*
* txttitle|newitem1,txtfirstname|newitem2
*
* }
*
* <p>
* Optional also reg expressions are supported. In this case mapping of the item
* name is not supported.
* <p>
* 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<String, List<Object>> 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));
}
}
}
}
}