update invoice xml import

This commit is contained in:
Ralph Soika 2024-05-15 16:21:26 +02:00
parent 545bd30d92
commit 8d9a0bba53

View file

@ -32,7 +32,11 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties; import java.util.Properties;
import java.util.TimeZone;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.ejb.EJB; import javax.ejb.EJB;
@ -41,6 +45,11 @@ import javax.enterprise.event.Observes;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; 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.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPClient;
@ -48,6 +57,7 @@ import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPSClient; import org.apache.commons.net.ftp.FTPSClient;
import org.imixs.archive.importer.DocumentImportEvent; import org.imixs.archive.importer.DocumentImportEvent;
import org.imixs.archive.importer.DocumentImportService; import org.imixs.archive.importer.DocumentImportService;
import org.imixs.workflow.FileData;
import org.imixs.workflow.ItemCollection; import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.engine.ModelService; import org.imixs.workflow.engine.ModelService;
import org.imixs.workflow.engine.WorkflowService; import org.imixs.workflow.engine.WorkflowService;
@ -151,105 +161,117 @@ public class CargosoftXMLInvoiceImportImportService {
// set default port // set default port
ftpPort = "21"; ftpPort = "21";
} }
FTPClient ftpClient = null;
try { try {
logger.finest("......read directories ...");
documentImportService.logMessage("...connecting to FTP server: " + ftpServer, event); documentImportService.logMessage("...connecting to FTP server: " + ftpServer, event);
documentImportService.logMessage("...working directory: " + ftpPath, event);
// TLS FTPClient ftpClient = connectServer(ftpServer, Integer.parseInt(ftpPort), ftpUser, ftpPassword);
ftpClient = new FTPSClient("TLS", false); scannFTPDirectory(ftpClient, ftpPath, mandantID, event);
ftpClient.setControlEncoding("UTF-8"); } catch (PluginException pe) {
ftpClient.connect(ftpServer, Integer.parseInt(ftpPort)); documentImportService.logMessage(pe.getMessage(), event);
if (ftpClient.login(ftpUser, ftpPassword) == false) {
documentImportService.logMessage("FTP file transfer failed: login failed!", event);
event.setResult(DocumentImportEvent.PROCESSING_ERROR); event.setResult(DocumentImportEvent.PROCESSING_ERROR);
return; return;
} }
// completed
event.setResult(DocumentImportEvent.PROCESSING_COMPLETED);
}
/**
* Connects to the FTP Server and returns a ftpClient instance.
*/
private FTPClient connectServer(String ftpServer, int ftpPort, String ftpUser, String ftpPassword)
throws PluginException {
FTPClient ftpClient = null;
// TLS
ftpClient = new FTPSClient("TLS", false);
ftpClient.setControlEncoding("UTF-8");
try {
ftpClient.connect(ftpServer, ftpPort);
if (ftpClient.login(ftpUser, ftpPassword) == false) {
throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(),
"FTP_ERROR", "FTP file transfer failed: login failed!");
}
ftpClient.enterLocalPassiveMode(); ftpClient.enterLocalPassiveMode();
logger.finest("...... FileType=" + FTP.BINARY_FILE_TYPE); logger.finest("...... FileType=" + FTP.BINARY_FILE_TYPE);
ftpClient.setFileType(FTP.BINARY_FILE_TYPE); ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
ftpClient.setControlEncoding("UTF-8"); ftpClient.setControlEncoding("UTF-8");
// try to enter the working directory.... } catch (IOException e) {
throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(),
"FTP_ERROR", e.getMessage());
}
return ftpClient;
}
/**
* Helper method reads the working FTP directory and starts importing the files.
*
* @param ftpClient
* @param ftpPath
* @throws PluginException
*/
private void scannFTPDirectory(FTPClient ftpClient, String ftpPath, String mandantID, DocumentImportEvent event)
throws PluginException {
try {
logger.finest("......read directory " + ftpPath);
// try to enter the working directory and read all files...
boolean bWorkingDir = ftpClient.changeWorkingDirectory(ftpPath); boolean bWorkingDir = ftpClient.changeWorkingDirectory(ftpPath);
if (bWorkingDir == true) { if (bWorkingDir == false) {
throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(),
"FTP_ERROR", "...failed to change into working directory: ");
}
documentImportService.logMessage("...working directory: " + ftpClient.printWorkingDirectory(), event); FTPFile[] allFiles = ftpClient.listFiles(ftpPath);
// scann directories...
FTPFile[] subDirectories = ftpClient.listDirectories(ftpPath);
for (FTPFile ftpSubDirectory : subDirectories) {
ItemCollection space = null;
String ftpSubDirectoryName = ftpSubDirectory.getName();
logger.info("......scan files from path=" + ftpPath
+ ftpSubDirectory.getName());
/*
* Nun importieren wir alle Files
*/
FTPFile[] allFiles = ftpClient.listFiles(ftpPath + ftpSubDirectory.getName());
/*
* create a new workitem for each document
*/
// FTPFile[] allFiles = ftpClient.listFiles();
int count = 0; int count = 0;
if (allFiles.length > 0) { if (allFiles.length > 0) {
documentImportService.logMessage("..." + allFiles.length + " files found ", event); documentImportService.logMessage("..." + allFiles.length + " files found ", event);
ftpClient.changeWorkingDirectory(ftpPath + ftpSubDirectory.getName());
for (FTPFile file : allFiles) { for (FTPFile file : allFiles) {
// if this is a directory or symlink then we do ignore this entry // if this is a directory or symlink then we do ignore this entry
if (!file.isFile()) { if (!file.isFile()) {
documentImportService.logMessage( logger.warning("...'" + file.getName() + "' is not a valid file, object will be ignored!");
"...'" + file.getName() + "' is not a valid file, object will be ignored!",
event);
continue; continue;
} }
ItemCollection invoice = null;
logger.info("import file " + file.getName() + "..."); logger.info("import file " + file.getName() + "...");
// String fullFileName = ftpPath + "/" + file.getName(); // String fullFileName = ftpPath + "/" + file.getName();
try (ByteArrayOutputStream is = new ByteArrayOutputStream();) { try (ByteArrayOutputStream is = new ByteArrayOutputStream();) {
ftpClient.retrieveFile(file.getName(), is); ftpClient.retrieveFile(file.getName(), is);
byte[] rawData = is.toByteArray(); byte[] rawData = is.toByteArray();
if (rawData != null && rawData.length > 0) { if (rawData != null && rawData.length > 0) {
logger.finest("......file '" + file.getName() + "' successfull read - bytes size = " logger.finest("......file '" + file.getName() + "' successful read - bytes size = "
+ rawData.length); + rawData.length);
// create new workitem // create new workitem
createWorkitem(event.getSource(), file.getName(), rawData, space); invoice = createWorkitem(event.getSource(), file.getName(), rawData);
// documentImportService.logMessage("....imported '" + file.getName() + "'",
// event); // persist workitem only if not exists and with a matching mandant.id
if (!invoice.getItemValueString("mandant.id").equals(mandantID)) {
throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(),
"FTP_ERROR", "Invoice does not match mandantID " + mandantID);
}
count++; count++;
} else { } else {
documentImportService.logMessage("...Warning - invalid file content '" logger.warning("...Warning - invalid file content '"
+ file.getName() + "' - file will be deleted!", event); + file.getName() + "' - file will be deleted!");
}
if (invoice != null) {
workflowService.processWorkItem(invoice);
} }
// finally delete the file.... // finally delete the file....
ftpClient.deleteFile(file.getName()); // ftpClient.deleteFile(file.getName());
} catch (AccessDeniedException | ProcessingErrorException | PluginException } catch (AccessDeniedException | ProcessingErrorException | PluginException
| ModelException e) { | ModelException e) {
throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(),
documentImportService.logMessage("...FTP import failed: " + e.getMessage(), event); "FTP_ERROR", e.getMessage());
event.setResult(DocumentImportEvent.PROCESSING_ERROR);
return;
} }
} }
documentImportService.logMessage("..." + count + " new files imported.", event); documentImportService.logMessage("..." + count + " new files imported.", event);
} else {
documentImportService.logMessage("...no files found, directory '" + ftpPath + "' is empty",
event);
}
}
} else {
documentImportService.logMessage("...failed to change into working directory: " + ftpPath, event);
} }
} catch (IOException e) { } catch (IOException e) {
@ -257,10 +279,8 @@ public class CargosoftXMLInvoiceImportImportService {
int r = ftpClient.getReplyCode(); int r = ftpClient.getReplyCode();
logger.severe("FTP ReplyCode=" + r); logger.severe("FTP ReplyCode=" + r);
documentImportService.logMessage("...FTP file transfer failed (replyCode=" + r + ") : " + e.getMessage(), throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(),
event); "FTP_ERROR", e.getMessage());
event.setResult(DocumentImportEvent.PROCESSING_ERROR);
return;
} finally { } finally {
// do logout.... // do logout....
@ -270,14 +290,10 @@ public class CargosoftXMLInvoiceImportImportService {
ftpClient.disconnect(); ftpClient.disconnect();
} catch (IOException e) { } catch (IOException e) {
documentImportService.logMessage("...FTP file transfer failed: " + e.getMessage(), event); documentImportService.logMessage("...FTP file transfer failed: " + e.getMessage(), event);
event.setResult(DocumentImportEvent.PROCESSING_ERROR); throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(),
return; "FTP_ERROR", e.getMessage());
} }
} }
// completed
event.setResult(DocumentImportEvent.PROCESSING_COMPLETED);
} }
/** /**
@ -291,7 +307,7 @@ public class CargosoftXMLInvoiceImportImportService {
* @throws ProcessingErrorException * @throws ProcessingErrorException
* @throws AccessDeniedException * @throws AccessDeniedException
*/ */
public ItemCollection createWorkitem(ItemCollection source, String fileName, byte[] rawData, ItemCollection space) public ItemCollection createWorkitem(ItemCollection source, String fileName, byte[] rawData)
throws AccessDeniedException, ProcessingErrorException, PluginException, ModelException { throws AccessDeniedException, ProcessingErrorException, PluginException, ModelException {
ItemCollection workitem = new ItemCollection(); ItemCollection workitem = new ItemCollection();
workitem.model(source.getItemValueString(DocumentImportService.SOURCE_ITEM_MODELVERSION)); workitem.model(source.getItemValueString(DocumentImportService.SOURCE_ITEM_MODELVERSION));
@ -307,12 +323,100 @@ public class CargosoftXMLInvoiceImportImportService {
try { try {
documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = documentBuilder.parse(inputSource); Document doc = documentBuilder.parse(inputSource);
Node node = doc.importNode(doc.getDocumentElement(), true);
} catch (ParserConfigurationException | SAXException | IOException e) { // Now we parse the following item values
// TODO Auto-generated catch block // invoice.number
e.printStackTrace(); // invoice.currency
// invoice.text
// invoice.date
// dbtr.number
// invoice.duedate
// invoice.reminder
// payment.term
// invoice.rate
// invoice.base.amount
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Client/Codes/Code", workitem,
"mandant.id", String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/NetAmount/Amount/Value",
workitem, "invoice.total", String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceCurrency/Codes/Code",
workitem, "invoice.currency", String.class);
// invoice.date <InvoiceDate>2024-05-13T00:00:00+02:00</InvoiceDate>
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceDate",
workitem, "invoice.date", Date.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAddress/Codes/Code",
workitem, "dbtr.number", String.class);
String dbtrNumber = workitem.getItemValueString("dbtr.number");
if (dbtrNumber.startsWith("K") || dbtrNumber.startsWith("D")) {
workitem.setItemValue("dbtr.number", dbtrNumber.substring(1));
}
// read filename
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Attachments/Attachment/Filename",
workitem, "import.filename", String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Attachments/Attachment/Content",
workitem, "import.filedata", String.class);
String fileDataString = workitem.getItemValueString("import.filedata");
byte[] decodedData = java.util.Base64.getDecoder().decode(fileDataString);
FileData fileData = new FileData(workitem.getItemValueString("import.filename"),
decodedData, "application/pdf", null);
workitem.addFileData(fileData);
workitem.removeItem("import.filename");
workitem.removeItem("import.filedata");
} catch (ParserConfigurationException | SAXException | IOException | XPathExpressionException e) {
throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(),
"XML_ERROR", e.getMessage());
} }
return workitem; return workitem;
} }
public <T> void getMist(String itemName, Class<T> itemType) {
}
/**
* Reads a tag value from the xml tree and set the value into the given
* workitem.
*
* /Invoices/Invoice/InvoiceHeader/Client/Code
*
* @param doc - xml doc
* @param expression - xpath expression
* @param workitem
* @param itemName
* @param itemType
* @throws XPathExpressionException
* @throws PluginException
*/
private <T> void readXMLValue(Document doc, String expression, ItemCollection workitem, String itemName,
Class<T> itemType) throws XPathExpressionException {
// create XPath...
XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath();
XPathExpression 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("yyyy-MM-dd'T'HH:mm:ssXXX");
formatter.setTimeZone(TimeZone.getTimeZone("CET"));
try {
workitem.setItemValue(itemName, formatter.parse(value));
} catch (ParseException e) {
logger.warning("Invalid Date Format");
}
} else {
workitem.setItemValue(itemName, value);
}
}
}
} }