430 lines
15 KiB
Java
430 lines
15 KiB
Java
/*******************************************************************************
|
|
* Imixs Workflow
|
|
* Copyright (C) 2001, 2011 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
|
|
*
|
|
* Project:
|
|
* http://www.imixs.org
|
|
* http://java.net/projects/imixs-workflow
|
|
*
|
|
* Contributors:
|
|
* Imixs Software Solutions GmbH - initial API and implementation
|
|
* Ralph Soika - Software Developer
|
|
*******************************************************************************/
|
|
|
|
package com.alexanderlogistics.api;
|
|
|
|
import java.io.ByteArrayInputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.Serializable;
|
|
import java.text.ParseException;
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.Date;
|
|
import java.util.List;
|
|
import java.util.TimeZone;
|
|
import java.util.logging.Logger;
|
|
|
|
import javax.ejb.Stateless;
|
|
import javax.inject.Inject;
|
|
import javax.ws.rs.GET;
|
|
import javax.ws.rs.Path;
|
|
import javax.ws.rs.Produces;
|
|
import javax.ws.rs.core.MediaType;
|
|
import javax.xml.parsers.DocumentBuilder;
|
|
import javax.xml.parsers.DocumentBuilderFactory;
|
|
import javax.xml.parsers.ParserConfigurationException;
|
|
import javax.xml.xpath.XPath;
|
|
import javax.xml.xpath.XPathConstants;
|
|
import javax.xml.xpath.XPathExpression;
|
|
import javax.xml.xpath.XPathExpressionException;
|
|
import javax.xml.xpath.XPathFactory;
|
|
|
|
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.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;
|
|
import org.w3c.dom.Document;
|
|
import org.w3c.dom.Node;
|
|
import org.xml.sax.InputSource;
|
|
import org.xml.sax.SAXException;
|
|
|
|
import com.alexanderlogistics.InvoiceUtil;
|
|
import com.alexanderlogistics.xml.CargosoftXMLInvoiceImportService;
|
|
|
|
/**
|
|
** Dieser Service korrigiert falsch importierte Cargosoft daten.
|
|
* Ausgangsrechnungen die ein falsches Jahr haben.
|
|
*
|
|
* Oder DAten die über die XML Schnittstelle falsch reinkamen...
|
|
*
|
|
* @author rsoika
|
|
* @version 1.1
|
|
*/
|
|
@Stateless
|
|
@Produces({ MediaType.TEXT_HTML, MediaType.APPLICATION_XHTML_XML, MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON,
|
|
MediaType.TEXT_XML })
|
|
@Path("/cargosoft")
|
|
public class CargosoftMigrationRestService implements Serializable {
|
|
|
|
private static final long serialVersionUID = 1L;
|
|
String log = "";
|
|
int errors = 0;
|
|
int count = 0;
|
|
public static String _QUERY = "($modelversion:rechnungsausgang-de-1.0)\n" +
|
|
"AND $created:[20240604 TO 20240701]";
|
|
// public static String _QUERY = "($modelversion:rechnungsausgang-dwc-1.0)\n" +
|
|
// //
|
|
// "AND $created:[20240604 TO 20240619]";
|
|
|
|
@Inject
|
|
DocumentService documentService;
|
|
|
|
@Inject
|
|
WorkflowService workflowService;
|
|
|
|
private static Logger logger = Logger.getLogger(CargosoftMigrationRestService.class.getName());
|
|
|
|
public CargosoftMigrationRestService() {
|
|
super();
|
|
}
|
|
|
|
/**
|
|
* This method loads taxonomy data for a workflow group within a given process
|
|
* and builds a ChartJS data structure in JSON format
|
|
*
|
|
* @param workflowgroup
|
|
* @param task
|
|
* @return
|
|
* @throws QueryException
|
|
*/
|
|
@GET
|
|
@Path("/test")
|
|
@Produces({ MediaType.TEXT_PLAIN })
|
|
public String testeFehlerhafteCargosoftDaten() throws QueryException {
|
|
int falsch = 0;
|
|
logger.info("query=" + _QUERY);
|
|
log = "Query\n" + _QUERY;
|
|
List<ItemCollection> result = documentService.find(_QUERY, 9999, 0);
|
|
|
|
log = log + "\n\nCount=" + result.size();
|
|
|
|
if (result.size() > 9999) {
|
|
log = log + "\n\nACHTUNG ES GIBT MEHR ALS 10000 RECHNUNGEN !";
|
|
}
|
|
|
|
for (ItemCollection workitem : result) {
|
|
String type = workitem.getItemValueString("invoice.type");
|
|
if ("G".equals(type) || "SR".equals(type)) {
|
|
double total = workitem.getItemValueDouble("invoice.saldo");
|
|
if (total >= 0) {
|
|
logger.info("---Falsch: " + workitem.getUniqueID());
|
|
falsch++;
|
|
workitem.setItemValue("invoice.saldo", -total);
|
|
// documentService.save(workitem);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
log = log + "\n\n";
|
|
log = log + "" + result.size() + " rechnungen geprüft";
|
|
log = log + "" + falsch + " davon falsch";
|
|
log = log + "\n\n";
|
|
return log;
|
|
}
|
|
|
|
/**
|
|
* This method fixes the wrong amount of a imported xml invoice.
|
|
*
|
|
* @param workflowgroup
|
|
* @param task
|
|
* @return
|
|
* @throws QueryException
|
|
* @throws ModelException
|
|
* @throws PluginException
|
|
* @throws ProcessingErrorException
|
|
* @throws AccessDeniedException
|
|
*/
|
|
public String fixMissingDueDate()
|
|
throws QueryException, AccessDeniedException, ProcessingErrorException, PluginException, ModelException {
|
|
|
|
logger.info("query=" + _QUERY);
|
|
log = "Query\n\n" + _QUERY;
|
|
|
|
List<ItemCollection> result = documentService.find(_QUERY, 9999, 0);
|
|
|
|
log = log + "\n\nSelection Count=" + result.size();
|
|
|
|
log = log + "\n\nFixes\n\n";
|
|
|
|
for (ItemCollection workitem : result) {
|
|
|
|
if (!workitem.hasItem("invoice.reminder")) {
|
|
Date dueDate = workitem.getItemValueDate("invoice.duedate");
|
|
workitem.setItemValue("invoice.reminder", dueDate);
|
|
count++;
|
|
// save only
|
|
documentService.save(workitem);
|
|
}
|
|
}
|
|
|
|
log = log + "\n\n=== Completed! " + count + " fixes! ===\n\n";
|
|
return log;
|
|
}
|
|
|
|
@GET
|
|
@Path("/fix")
|
|
@Produces({ MediaType.TEXT_PLAIN })
|
|
public String fixMissingRate()
|
|
throws QueryException, AccessDeniedException, ProcessingErrorException, PluginException, ModelException {
|
|
|
|
logger.info("query=" + _QUERY);
|
|
log = "Query\n\n" + _QUERY;
|
|
|
|
List<ItemCollection> result = documentService.find(_QUERY, 9999, 0);
|
|
|
|
log = log + "\n\nSelection Count=" + result.size();
|
|
|
|
log = log + "\n\nFixes\n\n";
|
|
|
|
for (ItemCollection workitem : result) {
|
|
|
|
if (!workitem.hasItem("invoice.rate")) {
|
|
// Fetch frist datev.kurs von _childitems
|
|
|
|
List<ItemCollection> childs = InvoiceUtil.explodeChildList(workitem);
|
|
if (childs != null && childs.size() > 0) {
|
|
ItemCollection child = childs.get(0);
|
|
double rate = child.getItemValueDouble("datev.kurs");
|
|
logger.info("..fix missing rate = " + rate);
|
|
workitem.setItemValue("invoice.rate", rate);
|
|
// save only
|
|
documentService.save(workitem);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
log = log + "\n\n=== Completed! " + count + " fixes! ===\n\n";
|
|
return log;
|
|
}
|
|
|
|
public String fixFehlerhafteCargosoftDatenMitSnapshot()
|
|
throws QueryException, AccessDeniedException, ProcessingErrorException, PluginException, ModelException {
|
|
|
|
logger.info("query=" + _QUERY);
|
|
log = "Query\n\n" + _QUERY;
|
|
|
|
List<ItemCollection> result = documentService.find(_QUERY, 9999, 0);
|
|
|
|
log = log + "\n\nSelection Count=" + result.size();
|
|
|
|
log = log + "\n\nFixes\n\n";
|
|
|
|
for (ItemCollection workitem : result) {
|
|
ItemCollection snapshot = null;
|
|
// load snapshot
|
|
String snapshotID = workitem.getItemValueString("$snapshotid");
|
|
if (!snapshotID.isEmpty()) {
|
|
snapshot = documentService.load(snapshotID);
|
|
}
|
|
|
|
if (snapshot == null) {
|
|
logger.warning("Unable to load snapshot for document " + workitem.getUniqueID());
|
|
log = log + "Unable to load snapshot for document " + workitem.getUniqueID() + " \n";
|
|
errors++;
|
|
continue;
|
|
}
|
|
|
|
// Get raw data....
|
|
// find the XML Attachment
|
|
String xmlFileName = null;
|
|
List<String> fileNames = snapshot.getFileNames();
|
|
for (String fileName : fileNames) {
|
|
if (fileName.endsWith(".xml")) {
|
|
xmlFileName = fileName;
|
|
break;
|
|
}
|
|
}
|
|
if (xmlFileName == null) {
|
|
logger.warning("Unable to load XML from document " + workitem.getUniqueID());
|
|
log = log + "Unable to load XML from document " + workitem.getUniqueID() + " \n";
|
|
errors++;
|
|
continue;
|
|
}
|
|
|
|
FileData xmlFileData = snapshot.getFileData(xmlFileName);
|
|
Document xmlDoc = createXMLTree(xmlFileData.getContent());
|
|
|
|
migrateDocument(workitem, xmlDoc);
|
|
}
|
|
|
|
log = log + "\n\n=== Completed! " + count + " fixes! ===\n\n";
|
|
return log;
|
|
}
|
|
|
|
/**
|
|
* Diese Methode korrigiert den Saldo um die Netto summe....
|
|
*
|
|
* @param workitem
|
|
* @param xmlDoc
|
|
* @throws ModelException
|
|
* @throws PluginException
|
|
* @throws ProcessingErrorException
|
|
* @throws AccessDeniedException
|
|
*/
|
|
private void migrateDocument(ItemCollection workitem, Document doc)
|
|
throws AccessDeniedException, ProcessingErrorException, PluginException, ModelException {
|
|
|
|
// Haben wir schon das neue Feld 'invoice.total.net' mit dem Netto Betrag?
|
|
if (workitem.hasItem("invoice.total.tax")) {
|
|
// no migration needed
|
|
return;
|
|
}
|
|
|
|
// Net und Tax ermitteln
|
|
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/NetAmount/Amount/Value",
|
|
workitem, "invoice.total.net", Double.class);
|
|
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/VATInformation/VATAmount/Amount/Value",
|
|
workitem, "invoice.total.tax", Double.class);
|
|
|
|
// Der Total entspricht nun dem Net Amount.
|
|
// Wenn wir eine Tax haben, dann müssen wir den Total sowie den Saldo um
|
|
// diese Tax erhöhen.
|
|
// Damit ist dann die Migration abgeschlossen.
|
|
double tax = workitem.getItemValueDouble("invoice.total.tax");
|
|
if (tax > 0) {
|
|
log = log + "...Migriere " + workitem.getItemValueString("invoice.number") + " Korrektur Betrag " +
|
|
tax + "\n";
|
|
double total = workitem.getItemValueDouble("invoice.total");
|
|
double saldo = workitem.getItemValueDouble("invoice.saldo");
|
|
// Gutschrift?
|
|
if (total < 0) {
|
|
total = total - tax;
|
|
saldo = saldo - tax;
|
|
|
|
} else {
|
|
// Rechnung
|
|
total = total + tax;
|
|
saldo = saldo + tax;
|
|
}
|
|
workitem.setItemValue("invoice.total", total);
|
|
workitem.setItemValue("invoice.saldo", saldo);
|
|
workitem.setItemValue("cargosoft.fix.xmlerror", tax);
|
|
log = log + "....... total neu=" + total + " saldo neu=" + saldo + "\n";
|
|
|
|
if (5900 == workitem.getTaskID()) {
|
|
workitem.setEventID(942);
|
|
workflowService.processWorkItem(workitem);
|
|
|
|
} else {
|
|
// save only
|
|
documentService.save(workitem);
|
|
}
|
|
count++;
|
|
} else {
|
|
log = log + "...Keine Migration notwendig " + workitem.getItemValueString("invoice.number") +
|
|
" tax=" + tax + "\n";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Erzeugt einen XML Baum aus dem XML Raw Daten
|
|
*
|
|
* @param snapshot
|
|
* @return
|
|
*/
|
|
private Document createXMLTree(byte[] rawData) {
|
|
Document doc = null;
|
|
InputStream inputStream = new ByteArrayInputStream(rawData);
|
|
InputSource inputSource = new InputSource(inputStream);
|
|
|
|
DocumentBuilder documentBuilder;
|
|
try {
|
|
documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
|
doc = documentBuilder.parse(inputSource);
|
|
} catch (ParserConfigurationException | SAXException | IOException e) {
|
|
log = log + "Unable to load XML tree: " + e.getMessage() + " \n";
|
|
return null;
|
|
}
|
|
|
|
return doc;
|
|
}
|
|
|
|
/**
|
|
* Reads a tag value from the xml tree and set the value into the given
|
|
* workitem.
|
|
*
|
|
* /Invoices/Invoice/InvoiceHeader/Client/Code
|
|
*
|
|
* Beispiel Datum:
|
|
* <InvoiceDate>2024-05-13T00:00:00+02:00</InvoiceDate>
|
|
*
|
|
* @param doc - xml doc
|
|
* @param expression - xpath expression
|
|
* @param workitem
|
|
* @param itemName
|
|
* @param itemType
|
|
*/
|
|
private <T> void readXMLValue(Document doc, String expression, ItemCollection workitem, String itemName,
|
|
Class<T> itemType) {
|
|
// create XPath...
|
|
XPathFactory xpathFactory = XPathFactory.newInstance();
|
|
XPath xpath = xpathFactory.newXPath();
|
|
XPathExpression xPathExpression;
|
|
try {
|
|
xPathExpression = xpath.compile(expression);
|
|
|
|
// extract node value
|
|
Node valueNode = (Node) xPathExpression.evaluate(doc, XPathConstants.NODE);
|
|
if (valueNode != null) {
|
|
String value = valueNode.getTextContent();
|
|
|
|
if (itemType == Date.class) {
|
|
// 2024-05-13T00:00:00+02:00
|
|
SimpleDateFormat formatter = new SimpleDateFormat(CargosoftXMLInvoiceImportService.DATE_FORMAT);
|
|
formatter.setTimeZone(TimeZone.getTimeZone("CET"));
|
|
try {
|
|
workitem.setItemValue(itemName, formatter.parse(value));
|
|
} catch (ParseException e) {
|
|
logger.warning("Invalid Date Format");
|
|
}
|
|
return;
|
|
}
|
|
if (itemType == Double.class && value != null && !value.isEmpty()) {
|
|
workitem.setItemValue(itemName, Double.parseDouble(value));
|
|
return;
|
|
}
|
|
if (itemType == Integer.class && value != null && !value.isEmpty()) {
|
|
workitem.setItemValue(itemName, Integer.parseInt(value));
|
|
return;
|
|
}
|
|
// Default String format
|
|
workitem.setItemValue(itemName, value);
|
|
|
|
}
|
|
} catch (XPathExpressionException e) {
|
|
logger.warning("Unable to read data field '" + expression + "' : " + e.getMessage());
|
|
|
|
}
|
|
}
|
|
}
|