diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c3e0a3b..77ac3e3 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -3,6 +3,7 @@ ### 1.3.4 (Development) - Finalizierung Business Partner Interface + |- Neue Plugin Logik (Aktualisierung der BP Nummer und Aktivierung von BP Objekten falls diese archiviert waren) - Zoho Schnittstelle **Migration** diff --git a/doc/BUSINESSPARTNER.md b/doc/BUSINESSPARTNER.md index dff4b38..aed38e8 100644 --- a/doc/BUSINESSPARTNER.md +++ b/doc/BUSINESSPARTNER.md @@ -11,6 +11,15 @@ In Imixs ist es dann aber möglich zusätzliche Attribute zu einem Businesspartn Die Businessparnter werden für jedes System separat importiert und verwaltet. +Folgende zentrale Stati werden über das BusinessPartner Modell festgelegt: + +- TASK_ACTIVE = 1100 +- TASK_VERIFICATION = 1300 +- TASK_INACTIVE = 1700 +- TASK_LOCKED = 1800 + +Die Invoice Plugins aktualisierne automatisch die BusinessPartner Attribute `partner.id` und `partner.name`. Zusätzlich unterbinden die Plugins eine Verarbeitung falls das Business Objekt im Satus LOCKED oder VERIFICATION ist! + ## BusinessPartner Suche Es gibt einen Controller und ein widget um nach Business Partnern zu suchen. @@ -23,8 +32,6 @@ Z.b. kann das als Custom Part in eine Form eingebunden werden: ``` -Das widget legt dann automatisch die Items `bpid` und `bpidname` an. - Alternativ kann im Backend über die EJB BusinessPartnerService nach bpid gesucht werden: ```java @@ -33,6 +40,10 @@ Alternativ kann im Backend über die EJB BusinessPartnerService nach bpid gesuch ItemCollection bp= businessPartnerService.getBusinessPartnerByID("BP0001"); ``` +## Invoice Plugins + +Die Invoice Plugins aktualisieren automatisch partner.id und partner.name falls diese Items noch nicht exiistieren + # Daten Migration Der `BusinessPartnerImportService` hängt sich über ein CDI Observer Pattern an den standard CSVImport Service diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerService.java index 9addc98..0b21891 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerService.java @@ -39,7 +39,12 @@ import org.eclipse.microprofile.config.inject.ConfigProperty; import org.imixs.workflow.ItemCollection; import org.imixs.workflow.ItemCollectionComparator; import org.imixs.workflow.engine.DocumentService; +import org.imixs.workflow.engine.WorkflowService; import org.imixs.workflow.engine.index.SchemaService; +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 jakarta.annotation.PostConstruct; import jakarta.annotation.security.DeclareRoles; @@ -72,6 +77,16 @@ public class BusinessPartnerService { public static final String WORKFLOW_SERVICE_USER = "workflow.service.user"; public static final String WORKFLOW_SERVICE_PASSWORD = "workflow.service.password"; + public static final String ERROR_BUSINESSPARTNER_MISSING = "ERROR_BUSINESSPARTNER_MISSING"; + public static final String ERROR_BUSINESSPARTNER_LOCKED = "ERROR_BUSINESSPARTNER_LOCKED"; + public static final String ERROR_BUSINESSPARTNER_VERIFICATION = "ERROR_BUSINESSPARTNER_VERIFICATION"; + + public static final int TASK_ACTIVE = 1100; + public static final int TASK_VERIFICATION = 1300; + public static final int TASK_INACTIVE = 1700; + public static final int TASK_LOCKED = 1800; + public static final int EVENT_ACTIVATE = 210; + private static Logger logger = Logger.getLogger(BusinessPartnerService.class.getName()); @Inject @@ -89,6 +104,9 @@ public class BusinessPartnerService { @Inject DocumentService documentService; + @Inject + WorkflowService workflowService; + @Inject SchemaService schemaService; @@ -100,6 +118,49 @@ public class BusinessPartnerService { } + /** + * Diese Methode aktualisiert das business Partner object. Sie wird von den + * Invoice Plugins genutzt. + * Die Methode wirft eine PluginException falls das BusinessPartner Objekt + * gesperrt oder in verification ist! + * + * @param partnerID + * @param workitem + * @throws PluginException + */ + public void updateBusinessPartner(String partnerID, ItemCollection workitem) throws PluginException { + workitem.setItemValue("partner.id", partnerID); + ItemCollection businessPartner = getBusinessPartnerByID(partnerID); + if (businessPartner != null) { + workitem.setItemValue("partner.name", businessPartner.getItemValueString("partner.name")); + if (businessPartner.getTaskID() == TASK_VERIFICATION) { + throw new PluginException(InvoicePlugin.class.getName(), + ERROR_BUSINESSPARTNER_VERIFICATION, + "Businesspartner is in verification. Invoice can't be processed!"); + } + if (businessPartner.getTaskID() == TASK_LOCKED) { + throw new PluginException(InvoicePlugin.class.getName(), + ERROR_BUSINESSPARTNER_LOCKED, + "Businesspartner is locked. Invoice can't be processed!"); + } + // update business partner object... + try { + if (businessPartner.getTaskID() == TASK_INACTIVE) { + // Workitem erneut aktivieren + businessPartner.event(EVENT_ACTIVATE); + workflowService.processWorkItem(businessPartner); + } else { + // workItem einfach nur speichern! + documentService.save(businessPartner); + } + } catch (PluginException | ProcessingErrorException | AccessDeniedException | ModelException e) { + logger.warning("Failed to update BusinessPartner object '" + partnerID + "'!"); + } + } else { + logger.warning("BusinessPartner '" + partnerID + "' not found!"); + } + } + /** * Diese Methode sucht einen Business Partner anhand seiner BPID * diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceOutgoingPlugin.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceOutgoingPlugin.java index 33de291..587b5b6 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceOutgoingPlugin.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceOutgoingPlugin.java @@ -20,6 +20,9 @@ public class InvoiceOutgoingPlugin extends AbstractPlugin { @Inject KreditorDebitorService kreditorService; + @Inject + BusinessPartnerService businessPartnerService; + /** * * @throws PluginException - if data is missing @@ -45,6 +48,11 @@ public class InvoiceOutgoingPlugin extends AbstractPlugin { } workitem.setItemValue("_img", img); + + // Update BUsiness Partner Data + String partnerID = InvoiceUtil.buildBPID(workitem.getItemValueString("dbtr.number")); + businessPartnerService.updateBusinessPartner(partnerID, workitem); + return workitem; } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoicePlugin.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoicePlugin.java index d4a1262..35c150c 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoicePlugin.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoicePlugin.java @@ -37,6 +37,8 @@ import jakarta.inject.Inject; * 2.2.22 - Das Plugin erzeugt auch das Feld _img welches die Images von * Sofortüberweisung, Mahnung und Ablehnung anzeigt. * + * 26.4.25 - Das Plugin setzt nun auch bpid.name und bpid + * zusätzlich processed es das BP Workitem * * @author rsoika * @version 1.0 @@ -70,6 +72,9 @@ public class InvoicePlugin extends AbstractPlugin { @EJB KreditorDebitorService kreditorService; + @Inject + BusinessPartnerService businessPartnerService; + @Inject ResourceBundleHandler resourceBundleHandler; @@ -203,6 +208,10 @@ public class InvoicePlugin extends AbstractPlugin { } + // Update BUsiness Partner Data + String partnerID = InvoiceUtil.buildBPID(workitem.getItemValueString("cdtr.number")); + businessPartnerService.updateBusinessPartner(partnerID, workitem); + return workitem; } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/api/CargosoftMigrationRestService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/api/CargosoftMigrationRestService.java index 29b6f7b..91863ae 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/api/CargosoftMigrationRestService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/api/CargosoftMigrationRestService.java @@ -27,29 +27,11 @@ 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.Map; -import java.util.TimeZone; import java.util.logging.Logger; -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.marty.team.TeamService; -import org.imixs.workflow.FileData; import org.imixs.workflow.ItemCollection; import org.imixs.workflow.engine.DocumentService; import org.imixs.workflow.engine.WorkflowService; @@ -59,23 +41,17 @@ 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.BusinessPartnerService; import com.alexanderlogistics.InvoiceUtil; import com.alexanderlogistics.mahnlauf.MahnlaufService; -import com.alexanderlogistics.xml.CargosoftXMLInvoiceImportService; -import jakarta.ejb.Stateless; import jakarta.ejb.TransactionAttribute; import jakarta.ejb.TransactionAttributeType; +import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; @@ -89,7 +65,7 @@ import jakarta.ws.rs.core.MediaType; * @author rsoika * @version 1.1 */ -@Stateless +@ApplicationScoped @Produces({ MediaType.TEXT_HTML, MediaType.APPLICATION_XHTML_XML, MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, MediaType.TEXT_XML }) @Path("/cargosoft") @@ -99,11 +75,7 @@ public class CargosoftMigrationRestService implements Serializable { 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]"; + boolean isRunning = false; @Inject DocumentService documentService; @@ -129,535 +101,17 @@ public class CargosoftMigrationRestService implements Serializable { super(); } - /** - * Dieser Endpunkt akutalisiert alle ausgehenden noch offenen Rechnungen in - * Bezug auf die Abteilungszugehörigkeit. - * - * - * @return - * @throws QueryException - * @throws AccessDeniedException - * @throws ProcessingErrorException - * @throws PluginException - * @throws ModelException - */ - @GET - @Path("/invoices/fix/spaces") - @Produces({ MediaType.TEXT_PLAIN }) - public String updateInvoiceSpacesAssignment() - throws QueryException, AccessDeniedException, ProcessingErrorException, PluginException, ModelException { - count = 0; - String query = "($modelversion:rechnungsausgang-de-1.0) AND (type:workitem)"; - // "AND $taskid:[20240604 TO 20240701]"; - logger.info("query=" + query); - log = "Update Space Assignment in Outgoing Invoices\n\n" + query; - - List result = documentService.find(query, 9999, 0); - log = log + " " + new Date() + "\n\n"; - log = log + "\n\nFound " + result.size() + " open invoices."; - - // load spaces Pos Expressions - Map> spacePosMappings = mahnlaufService.loadSpacePosMappings(); - - for (ItemCollection invoice : result) { - - String oldSpaceRef = invoice.getItemValueString("space.ref"); - mahnlaufService.mapInvoiceTextToSpace(invoice, spacePosMappings); - String newSpaceRef = invoice.getItemValueString("space.ref"); - if (newSpaceRef.isEmpty()) { - // wir dürfen die space.ref nicht löschen! - continue; - } - // change? - if (!oldSpaceRef.equals(newSpaceRef)) { - count++; - logger.info("...fix invoice: " + invoice.getUniqueID()); - // lookup space - ItemCollection space = documentService.load(newSpaceRef); - invoice.setItemValue("space.name", space.getItemValueString("name")); - invoice.setItemValue("migration.space.ref", oldSpaceRef); - documentService.save(invoice); - - } - - } - - log = log + "\n\n=== Completed! " + count + " fixes! ===\n\n"; - return log; - } - - /** - * Dieser Endpunkt akutalisiert alle ausgehenden noch offenen Rechnungen in - * Bezug auf die Abteilungszugehörigkeit. - * - * - * @return - * @throws QueryException - * @throws AccessDeniedException - * @throws ProcessingErrorException - * @throws PluginException - * @throws ModelException - */ - @GET - @Path("/invoices/fix/index/{page}") - @Produces({ MediaType.TEXT_PLAIN }) - public String updateInvoiceIndex(@PathParam("page") int page) - throws QueryException, AccessDeniedException, ProcessingErrorException, PluginException, ModelException { - count = 0; - String query = "($modelversion:rechnungsausgang-de-1.0) AND (type:workitem)"; - - logger.info("query=" + query); - log = "Update index Invoices\n\n" + query; - - List result = documentService.find(query, 100, page); - log = log + " " + new Date() + "\n\n"; - log = log + " size= 500 page=" + page + "\n\n"; - log = log + "\n\nFound " + result.size() + " open invoices."; - - logger.info("Found " + result.size() + " open invoices."); - for (ItemCollection invoice : result) { - - documentService.save(invoice); - - count++; - logger.info("... update index " + invoice.getUniqueID()); - - } - - log = log + "\n\n=== Completed! " + count + " fixes! ===\n\n"; - return log; - } - - /** - * Dieser Endpunkt akutalisiert alle ausgehenden noch offenen Rechnungen in - * Bezug auf die Abteilungszugehörigkeit. - * - * - * @return - * @throws QueryException - * @throws AccessDeniedException - * @throws ProcessingErrorException - * @throws PluginException - * @throws ModelException - */ - @GET - @Path("/steuerbescheide/fix/spaces") - @Produces({ MediaType.TEXT_PLAIN }) - public String updateSteuerbescheideSpacesAssignment() - throws QueryException, AccessDeniedException, ProcessingErrorException, PluginException, ModelException { - count = 0; - String query = "($modelversion:steuerbescheid-de-1.0) AND (type:workitem)"; - // "AND $taskid:[20240604 TO 20240701]"; - logger.info("query=" + query); - log = "Update Space Assignment in Steuerbescheide\n\n" + query; - - List result = documentService.find(query, 9999, 0); - - log = log + " " + new Date() + "\n\n"; - log = log + "\n\nFound " + result.size() + " open steuerbescheide."; - - // load spaces Pos Expressions - Map> spacePosMappings = mahnlaufService.loadSpacePosMappings(); - - for (ItemCollection invoice : result) { - - String oldSpaceRef = invoice.getItemValueString("space.ref"); - mahnlaufService.mapInvoiceTextToSpace(invoice, spacePosMappings); - String newSpaceRef = invoice.getItemValueString("space.ref"); - if (newSpaceRef.isEmpty()) { - // wir dürfen die space.ref nicht löschen! - continue; - } - // change? - if (!oldSpaceRef.equals(newSpaceRef)) { - count++; - logger.info("...fix invoice: " + invoice.getUniqueID()); - // lookup space - ItemCollection space = documentService.load(newSpaceRef); - invoice.setItemValue("space.name", space.getItemValueString("name")); - invoice.setItemValue("migration.space.ref", oldSpaceRef); - documentService.save(invoice); - - } - - } - - log = log + "\n\n=== Completed! " + count + " fixes! ===\n\n"; - return log; - } - - /** - * 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 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 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 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 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; - } - - /** - * Dieser agent prüft die Rechnungen der letzten 2 Wochen auf einen Billingtext - * der mit "24DE" beginnt und übernimmt dann diesen Text als neue ATC number - * - * @return - * @throws QueryException - * @throws AccessDeniedException - * @throws ProcessingErrorException - * @throws PluginException - * @throws ModelException - */ - @GET - @Path("/fix-atc") - @Produces({ MediaType.TEXT_PLAIN }) - public String fixATCNumber() - throws QueryException, AccessDeniedException, ProcessingErrorException, PluginException, ModelException { - String query = "($modelversion:rechnungsausgang-de-1.0) " + - "AND $created:[20241001 TO 20241201]"; - logger.info("query=" + query); - log = "Query\n\n" + query; - - List result = documentService.find(query, 9999, 0); - - log = log + "\n\nSelection Count=" + result.size(); - log = log + "\n\nFixes:\n\n"; - - for (ItemCollection workitem : result) { - logger.info("verify: " + workitem.getUniqueID()); - - if (!workitem.hasItem("invoice.atc.number")) { - // Fetch _childitems and check billingText - List childs = InvoiceUtil.explodeChildList(workitem); - for (ItemCollection child : childs) { - List billingTexts = child.getItemValueList("billingtext", String.class); - boolean contains24DE = false; - String atcNumber = ""; - for (String str : billingTexts) { - if (str.startsWith("24DE")) { - contains24DE = true; - atcNumber = str; - logger.info(" found in " + workitem.getUniqueID()); - break; - } - } - if (contains24DE) { - count++; - workitem.setItemValue("invoice.atc.number", atcNumber); - // save only - logger.info(" update: " + workitem.getUniqueID()); - log = log + "\n - " + workitem.getUniqueID(); - documentService.save(workitem); - break; - } - } - - } - } - - log = log + "\n\n=== Completed! " + count + " fixes! ===\n\n"; - return log; - } - - /** - * Dieser agent prüft die Rechnungen der letzten 2 Wochen auf einen Billingtext - * der mit "24DE" beginnt und übernimmt dann diesen Text als neue ATC number - * - * @return - * @throws QueryException - * @throws AccessDeniedException - * @throws ProcessingErrorException - * @throws PluginException - * @throws ModelException - */ - @GET - @Path("/fix-atc2") - @Produces({ MediaType.TEXT_PLAIN }) - public String fixATCNumber2() - throws QueryException, AccessDeniedException, ProcessingErrorException, PluginException, ModelException { - String query = "($modelversion:rechnungsausgang-de-1.0) " + - "AND $created:[20241001 TO 20241201]"; - logger.info("query=" + query); - log = "Query\n\n" + query; - - List result = documentService.find(query, 9999, 0); - - log = log + "\n\nSelection Count=" + result.size(); - log = log + "\n\nFixes:\n\n"; - - for (ItemCollection workitem : result) { - logger.info("verify: " + workitem.getUniqueID()); - - if (workitem.hasItem("invoice.atc.number")) { - // Fetch _childitems and check billingText - List childs = InvoiceUtil.explodeChildList(workitem); - for (ItemCollection child : childs) { - List billingTexts = child.getItemValueList("billingtext", String.class); - boolean contains24DE = false; - String atcNumber = ""; - for (String str : billingTexts) { - if (str.startsWith("24DE")) { - contains24DE = true; - atcNumber = str; - logger.info(" found in " + workitem.getUniqueID()); - child.setItemValue("atc.number", atcNumber); - break; - } - } - if (contains24DE) { - count++; - - InvoiceUtil.implodeChildList(workitem, childs); - // save only - logger.info(" update: " + workitem.getUniqueID()); - log = log + "\n - " + workitem.getUniqueID(); - documentService.save(workitem); - break; - } - } - - } - } - - 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 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 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", InvoiceUtil.round(total)); - workitem.setItemValue("invoice.saldo", InvoiceUtil.round(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"; - } - } - /** * Dieser agent prüft ob es für Cargosoft Krediotren/Debitoren Daten schon ein * passendes Businesspartner Workitem gibt. * Wenn nicht speichert der Service einmal das cargosft entity was dann zur * automatischen Neuanlage des businesspartner workflows führt. * + * + * curl -H "Cookie: + * JSESSIONID=ShAegfNiXmqLFLSFwcUZ7hrLLQDKZGKiu4oj_xeX.imixs-office-workflow-8586d7d58d-h6k4q" + * https://alexander-logistics-dwc.office-workflow.de/api/cargosoft/bp-sync?maxcount=1000 + * * @return * @throws QueryException * @throws AccessDeniedException @@ -674,14 +128,21 @@ public class CargosoftMigrationRestService implements Serializable { if (maxcount <= 0) { maxcount = 100; } - String query = "(type:cargosoftkreditor) AND ($modified:[20100130000000 TO 20250324200000])"; + String query = "(type:cargosoftkreditor)"; int syncs = 0; long l = System.currentTimeMillis(); int batchSize = 100; int totalObjects = 0; log("├── sync business partner objects....", messageBuffer); + + if (isRunning) { + log("├── sync process already running!", messageBuffer); + return messageBuffer.toString(); + } + isRunning = true; + int totalCount = documentService.count(query); - log("│   ├── found " + totalCount + " entities ", + log("│   ├── found " + totalCount + " cargosoft entries", messageBuffer); // Berechne Anzahl der benötigten Pages @@ -698,7 +159,6 @@ public class CargosoftMigrationRestService implements Serializable { syncs++; // Explicit flush the lucene search event log updateService.updateIndex(); - } if (syncs >= maxcount) break; @@ -724,6 +184,7 @@ public class CargosoftMigrationRestService implements Serializable { log("├── Successfully " + syncs + " business partner objects synced in " + duration + "ms (" + String.format("%.1f", objectsPerSecond) + " objects/sec)", messageBuffer); + isRunning = false; return messageBuffer.toString(); } @@ -749,6 +210,10 @@ public class CargosoftMigrationRestService implements Serializable { * Dieser agent löscht einfach alle Businesspartner objekte. Wird eigentlich * nicht mehr benötigt. * + * curl -H "Cookie: + * JSESSIONID=ShAegfNiXmqLFLSFwcUZ7hrLLQDKZGKiu4oj_xeX.imixs-office-workflow-8586d7d58d-h6k4q" + * https://alexander-logistics-dwc.office-workflow.de/api/cargosoft/bp-delete?maxcount=10000 + * * @return * @throws QueryException * @throws AccessDeniedException @@ -763,37 +228,50 @@ public class CargosoftMigrationRestService implements Serializable { throws QueryException, AccessDeniedException, ProcessingErrorException, PluginException, ModelException { StringBuffer messageBuffer = new StringBuffer(); - String query = "(type:workitem) AND ($modelversion: businesspartner*) AND ($modified:[20210130000000 TO 20250324200000])"; + String query = "(type:workitem*) AND ($modelversion: businesspartner*)"; int deletions = 0; long l = System.currentTimeMillis(); int batchSize = 100; int totalObjects = 0; log("├── delete old business partner objects....", messageBuffer); + + if (isRunning) { + log("├── sync process already running!", messageBuffer); + return messageBuffer.toString(); + } + isRunning = true; + + log("│   ├── maxcount= " + maxcount, messageBuffer); int totalCount = documentService.count(query); - log("│   ├── found " + totalCount + " entities ", - messageBuffer); + log("│   ├── found " + totalCount + " entities ", messageBuffer); // Berechne Anzahl der benötigten Pages int totalPages = (int) Math.ceil((double) totalCount / batchSize); // Verarbeite Page für Page - for (int pageIndex = 0; pageIndex < totalPages; pageIndex++) { - - List cargosoftDataList = documentService.find(query, batchSize, pageIndex); + int iterations = 0; + while (deletions < maxcount) { + List cargosoftDataList = documentService.find(query, batchSize, 0); + if (cargosoftDataList.size() == 0) { + break; + } for (ItemCollection cargosoftItemCol : cargosoftDataList) { totalObjects++; - if (deleteBusinessPartner(cargosoftItemCol, messageBuffer)) { deletions++; } if (deletions >= maxcount) break; } + // Explicit flush the lucene search event log + updateService.updateIndex(); // Fortschritt loggen - log("│ ├── Processed page " + (pageIndex + 1) + " of " + totalPages + + iterations++; + log("│ ├── Processed page " + (iterations) + " of " + totalPages + " (" + deletions + " total deletions)", messageBuffer); + // Optional: Kurze Pause nach jedem 5. Batch try { Thread.sleep(100); @@ -811,6 +289,7 @@ public class CargosoftMigrationRestService implements Serializable { log("├── Successfully " + deletions + " business partner objects deleted in " + duration + "ms (" + String.format("%.1f", objectsPerSecond) + " objects/sec)", messageBuffer); + isRunning = false; return messageBuffer.toString(); } @@ -827,87 +306,6 @@ public class CargosoftMigrationRestService implements Serializable { return true; } - /** - * 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: - * 2024-05-13T00:00:00+02:00 - * - * @param doc - xml doc - * @param expression - xpath expression - * @param workitem - * @param itemName - * @param itemType - */ - private void readXMLValue(Document doc, String expression, ItemCollection workitem, String itemName, - Class 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()); - - } - } - private void log(String message, StringBuffer messageLog) { logger.info(message); messageLog.append(message + "\n"); diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/BusinessPartnerImportService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/BusinessPartnerImportService.java index 422742f..9988454 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/BusinessPartnerImportService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/BusinessPartnerImportService.java @@ -179,14 +179,14 @@ public class BusinessPartnerImportService { } } - int eventID = 200; // default + int eventID = 200; // default update // event errechnen if (!businesspartner.hasItem(WorkflowKernel.LASTEVENT)) { // create new! if (ibanDublette) { eventID = 300; // Exception } else { - eventID = 100; // update/create + eventID = 100; // new import } } businesspartner.setEventID(eventID); diff --git a/workflow/businesspartner-de-1.0.0.bpmn b/workflow/businesspartner-de-1.0.0.bpmn index 6412d90..8affaa0 100644 --- a/workflow/businesspartner-de-1.0.0.bpmn +++ b/workflow/businesspartner-de-1.0.0.bpmn @@ -60,13 +60,19 @@ event_i7yQVw gateway_uK0c6g event_6250fg - event_VM0fEQ - gateway_s0J53g task_NohgrQ event_AMs05g event_feLZFQ event_GTXFMw event_u8OBFg + gateway_whpM0w + event_Idvwtg + event_8yN0uw + gateway_Kjobpw + event_pYaI6A + event_00CWpg + textAnnotation_pSbYVQ + event_AOFZ0A @@ -138,11 +144,9 @@ sequenceFlow_avahXg sequenceFlow_9atLFA - sequenceFlow_T5L9aQ sequenceFlow_b4BcVA - sequenceFlow_8O1EtQ - sequenceFlow_ShuXXA sequenceFlow_03b60w + sequenceFlow_0Icsbw @@ -195,7 +199,7 @@ - + Partnermanagement]]> @@ -219,8 +223,7 @@ sequenceFlow_1mXO8A - sequenceFlow_b0Bj3Q - sequenceFlow_neSsFQ + sequenceFlow_X4XnjQ @@ -297,22 +300,19 @@ - + - + - - - - + @@ -321,13 +321,13 @@ - + - - + + @@ -400,17 +400,29 @@ sequenceFlow_BHsRkQ sequenceFlow_5pT4Tw - + - - - home]]> + + + + + + + + + + + + + + + sequenceFlow_pvIENQ @@ -422,13 +434,13 @@ - + - + @@ -452,12 +464,9 @@ - + - - - sequenceFlow_S9LVXg @@ -473,7 +482,7 @@ - + @@ -483,27 +492,6 @@ - - - - Partnermanagement]]> - - - - - - - sequenceFlow_T5L9aQ - sequenceFlow_MPlPww - - sequenceFlow_rNZ3tw - - - - sequenceFlow_MPlPww - sequenceFlow_aH0vCQ - sequenceFlow_ShuXXA - @@ -546,22 +534,14 @@ sequenceFlow_9atLFA sequenceFlow_8O1EtQ - sequenceFlow_aH0vCQ sequenceFlow_neSsFQ sequenceFlow_zd6RWA - sequenceFlow_rNZ3tw + sequenceFlow_8O1EtQ + sequenceFlow_Zl0LZA + sequenceFlow_BbA3Gg + sequenceFlow_3SU0Xw - - - - - - - - - - - + @@ -583,9 +563,9 @@ - sequenceFlow_03b60w + sequenceFlow_OwZZBw - + @@ -596,26 +576,133 @@ - - + + + sequenceFlow_b0Bj3Q + sequenceFlow_neSsFQ + sequenceFlow_X4XnjQ + + + + + + + + + + + + + + + + + + sequenceFlow_Zl0LZA + sequenceFlow_0Icsbw + + + + + + + + + + + + + + home]]> + + + + + + + + + + + + + + + + + + + sequenceFlow_RkjrPQ + + sequenceFlow_b0d63g + + + + sequenceFlow_03b60w + sequenceFlow_OwZZBw + sequenceFlow_RkjrPQ + + + + + + + + + + + sequenceFlow_b0d63g + + + + + + + + sequenceFlow_BbA3Gg + + + + + + + + + + + + + + + + + + + + + + sequenceFlow_3SU0Xw + + + - + - + - + - + - + @@ -624,259 +711,320 @@ - - + + - + - + - + - + - - + + - + - + - - + + - - + + - + - + - + - + - - - + + + + - - - - + + - + - + - + - - - + + + + - + - + - - - + + + + - + - + - + - + - + - - + + + - - + + - + - + - - - + + + + - + - + - - - + + + + - - - - - - - - + + + + - + - + - - + + - - + + - - - - + + + + - - - + + + - + - + - - - + + + + - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - + + + - + - + - + - + - - + + - - + + + - + - + - - - - - + + + - + - + - - - + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/workflow/businesspartner-en-1.0.0.bpmn b/workflow/businesspartner-en-1.0.0.bpmn index 2f16649..d95283e 100644 --- a/workflow/businesspartner-en-1.0.0.bpmn +++ b/workflow/businesspartner-en-1.0.0.bpmn @@ -60,13 +60,18 @@ event_i7yQVw gateway_uK0c6g event_6250fg - event_VM0fEQ - gateway_s0J53g task_NohgrQ event_AMs05g event_feLZFQ event_GTXFMw event_u8OBFg + gateway_ERhYvA + event_RsEmoA + gateway_Sg02EQ + event_0vmPSg + event_ZYEq8w + textAnnotation_fFO8tQ + event_kT136g @@ -138,10 +143,7 @@ sequenceFlow_avahXg sequenceFlow_9atLFA - sequenceFlow_T5L9aQ sequenceFlow_b4BcVA - sequenceFlow_8O1EtQ - sequenceFlow_ShuXXA sequenceFlow_03b60w @@ -184,10 +186,10 @@ - sequenceFlow_1mXO8A sequenceFlow_rwdWxw sequenceFlow_gC0I9w sequenceFlow_kS2fMA + sequenceFlow_Hrl9Qg @@ -195,7 +197,7 @@ - + Partnermanagement]]> @@ -218,13 +220,9 @@ - sequenceFlow_1mXO8A - sequenceFlow_b0Bj3Q sequenceFlow_neSsFQ + sequenceFlow_Hrl9Qg - - - @@ -297,22 +295,22 @@ - + - + - + - + @@ -321,13 +319,16 @@ - + - + + + + @@ -422,13 +423,13 @@ - + - + @@ -452,12 +453,9 @@ - + - - - sequenceFlow_S9LVXg @@ -470,10 +468,7 @@ - - - - + @@ -483,27 +478,6 @@ - - - - Partnermanagement]]> - - - - - - - sequenceFlow_T5L9aQ - sequenceFlow_MPlPww - - sequenceFlow_rNZ3tw - - - - sequenceFlow_MPlPww - sequenceFlow_aH0vCQ - sequenceFlow_ShuXXA - @@ -546,22 +520,13 @@ sequenceFlow_9atLFA sequenceFlow_8O1EtQ - sequenceFlow_aH0vCQ - sequenceFlow_neSsFQ sequenceFlow_zd6RWA - sequenceFlow_rNZ3tw + sequenceFlow_8O1EtQ + sequenceFlow_u2Ftag + sequenceFlow_cqiAow + sequenceFlow_5iTFQQ - - - - - - - - - - - + @@ -583,9 +548,9 @@ - sequenceFlow_03b60w + sequenceFlow_oc3zTQ - + @@ -596,26 +561,123 @@ - - + + + + + + sequenceFlow_b0Bj3Q + sequenceFlow_neSsFQ + sequenceFlow_u2Ftag + + + + + + + + + + + + + + + + + + + + home]]> + + + + + + + + + + + + + + + + sequenceFlow_izMKCw + sequenceFlow_i0zvhQ + + + + + sequenceFlow_03b60w + sequenceFlow_oc3zTQ + sequenceFlow_izMKCw + + + + + + + + + + + sequenceFlow_i0zvhQ + + + + + sequenceFlow_cqiAow + + + + + + + + + + + + + + + + + + + + + + + + + + + + sequenceFlow_5iTFQQ + + + - + - + - + - + - + @@ -624,259 +686,302 @@ - - + + - + - + - + - + - - + + - + - + - - - - - - + + - + - + - + - + - - - + + - - - - + + + - + - + - + - - - + + + - + - + - - - + + + - + - + - + - + - + - - + + - - + + - + - + - - - + + + + - + - + - - - + + + + - - - - - - - - + + + + - + - + - - + + - - + + - - - - - - - - - + + + - + - + - - - + + + - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - + + + - + - + - + - + - - + + - - + + + - + - + - - - - - + + + - + - + - - - + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +