update invoice xml import
This commit is contained in:
parent
545bd30d92
commit
8d9a0bba53
1 changed files with 198 additions and 94 deletions
|
|
@ -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);
|
||||||
|
FTPClient ftpClient = connectServer(ftpServer, Integer.parseInt(ftpPort), ftpUser, ftpPassword);
|
||||||
|
scannFTPDirectory(ftpClient, ftpPath, mandantID, event);
|
||||||
|
} catch (PluginException pe) {
|
||||||
|
documentImportService.logMessage(pe.getMessage(), event);
|
||||||
|
event.setResult(DocumentImportEvent.PROCESSING_ERROR);
|
||||||
|
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);
|
||||||
|
|
||||||
// TLS
|
|
||||||
ftpClient = new FTPSClient("TLS", false);
|
|
||||||
ftpClient.setControlEncoding("UTF-8");
|
|
||||||
ftpClient.connect(ftpServer, Integer.parseInt(ftpPort));
|
|
||||||
if (ftpClient.login(ftpUser, ftpPassword) == false) {
|
if (ftpClient.login(ftpUser, ftpPassword) == false) {
|
||||||
documentImportService.logMessage("FTP file transfer failed: login failed!", event);
|
throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(),
|
||||||
event.setResult(DocumentImportEvent.PROCESSING_ERROR);
|
"FTP_ERROR", "FTP file transfer failed: login failed!");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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[] allFiles = ftpClient.listFiles();
|
||||||
|
int count = 0;
|
||||||
|
if (allFiles.length > 0) {
|
||||||
|
documentImportService.logMessage("..." + allFiles.length + " files found ", event);
|
||||||
|
for (FTPFile file : allFiles) {
|
||||||
|
// if this is a directory or symlink then we do ignore this entry
|
||||||
|
if (!file.isFile()) {
|
||||||
|
logger.warning("...'" + file.getName() + "' is not a valid file, object will be ignored!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ItemCollection invoice = null;
|
||||||
|
logger.info("import file " + file.getName() + "...");
|
||||||
|
// String fullFileName = ftpPath + "/" + file.getName();
|
||||||
|
try (ByteArrayOutputStream is = new ByteArrayOutputStream();) {
|
||||||
|
ftpClient.retrieveFile(file.getName(), is);
|
||||||
|
byte[] rawData = is.toByteArray();
|
||||||
|
if (rawData != null && rawData.length > 0) {
|
||||||
|
logger.finest("......file '" + file.getName() + "' successful read - bytes size = "
|
||||||
|
+ rawData.length);
|
||||||
|
// create new workitem
|
||||||
|
invoice = createWorkitem(event.getSource(), file.getName(), rawData);
|
||||||
|
|
||||||
FTPFile[] subDirectories = ftpClient.listDirectories(ftpPath);
|
// persist workitem only if not exists and with a matching mandant.id
|
||||||
|
if (!invoice.getItemValueString("mandant.id").equals(mandantID)) {
|
||||||
for (FTPFile ftpSubDirectory : subDirectories) {
|
throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(),
|
||||||
ItemCollection space = null;
|
"FTP_ERROR", "Invoice does not match mandantID " + mandantID);
|
||||||
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
if (allFiles.length > 0) {
|
|
||||||
documentImportService.logMessage("..." + allFiles.length + " files found ", event);
|
|
||||||
ftpClient.changeWorkingDirectory(ftpPath + ftpSubDirectory.getName());
|
|
||||||
|
|
||||||
for (FTPFile file : allFiles) {
|
|
||||||
// if this is a directory or symlink then we do ignore this entry
|
|
||||||
if (!file.isFile()) {
|
|
||||||
documentImportService.logMessage(
|
|
||||||
"...'" + file.getName() + "' is not a valid file, object will be ignored!",
|
|
||||||
event);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
logger.info("import file " + file.getName() + "...");
|
|
||||||
// String fullFileName = ftpPath + "/" + file.getName();
|
|
||||||
try (ByteArrayOutputStream is = new ByteArrayOutputStream();) {
|
|
||||||
ftpClient.retrieveFile(file.getName(), is);
|
|
||||||
byte[] rawData = is.toByteArray();
|
|
||||||
if (rawData != null && rawData.length > 0) {
|
|
||||||
logger.finest("......file '" + file.getName() + "' successfull read - bytes size = "
|
|
||||||
+ rawData.length);
|
|
||||||
// create new workitem
|
|
||||||
createWorkitem(event.getSource(), file.getName(), rawData, space);
|
|
||||||
// documentImportService.logMessage("....imported '" + file.getName() + "'",
|
|
||||||
// event);
|
|
||||||
count++;
|
|
||||||
} else {
|
|
||||||
documentImportService.logMessage("...Warning - invalid file content '"
|
|
||||||
+ file.getName() + "' - file will be deleted!", event);
|
|
||||||
}
|
|
||||||
// finally delete the file....
|
|
||||||
ftpClient.deleteFile(file.getName());
|
|
||||||
} catch (AccessDeniedException | ProcessingErrorException | PluginException
|
|
||||||
| ModelException e) {
|
|
||||||
|
|
||||||
documentImportService.logMessage("...FTP import failed: " + e.getMessage(), event);
|
count++;
|
||||||
event.setResult(DocumentImportEvent.PROCESSING_ERROR);
|
} else {
|
||||||
return;
|
logger.warning("...Warning - invalid file content '"
|
||||||
}
|
+ file.getName() + "' - file will be deleted!");
|
||||||
}
|
}
|
||||||
documentImportService.logMessage("..." + count + " new files imported.", event);
|
|
||||||
|
|
||||||
} else {
|
if (invoice != null) {
|
||||||
documentImportService.logMessage("...no files found, directory '" + ftpPath + "' is empty",
|
workflowService.processWorkItem(invoice);
|
||||||
event);
|
}
|
||||||
|
// finally delete the file....
|
||||||
|
// ftpClient.deleteFile(file.getName());
|
||||||
|
} catch (AccessDeniedException | ProcessingErrorException | PluginException
|
||||||
|
| ModelException e) {
|
||||||
|
throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(),
|
||||||
|
"FTP_ERROR", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
documentImportService.logMessage("..." + count + " new files imported.", event);
|
||||||
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue