From 51ccd40f18c6c3f0ee0a941752f16db6e3eea935 Mon Sep 17 00:00:00 2001 From: Ralph Soika Date: Sat, 1 Feb 2025 12:49:05 +0100 Subject: [PATCH 01/12] refactoring inmemory metrics --- .../metrics/MetricCreditorRestService.java | 82 +------- .../metrics/MetricCreditorService.java | 188 +++++++----------- .../metrics/MetricDataService.java | 60 ------ .../metrics/MetricDebitorRestService.java | 93 +-------- .../metrics/MetricDebitorService.java | 180 +++++++---------- 5 files changed, 148 insertions(+), 455 deletions(-) diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorRestService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorRestService.java index 773252a..c7ee05e 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorRestService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorRestService.java @@ -1,6 +1,5 @@ package com.alexanderlogistics.metrics; -import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; @@ -15,7 +14,6 @@ import jakarta.ejb.Stateless; 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.core.MediaType; import jakarta.ws.rs.core.Response; @@ -62,9 +60,6 @@ public class MetricCreditorRestService { // first clear the metric cache metricCreditorService.reset(); log("│   ├── reset metric cache", messageBuffer); - // run in new transaction! - metricDataService.deleteAllMetrics(MetricCreditorService.TYPE_METRIC_CREDITOR); - log("│   ├── delete metrics", messageBuffer); computeMetrics(); log("│   ├── computing metrics finished in " + (System.currentTimeMillis() - l) + "ms", messageBuffer); @@ -87,81 +82,6 @@ public class MetricCreditorRestService { } } - /** - * This method initializes the metrics for all creditors with open invoices. - * - * The method deletes all existing metrics and creates new metric entires for - * each creditor. - * - * @return - */ - @GET - @Path("/rebuild/{cdtrnumber}") - @Produces({ MediaType.TEXT_PLAIN }) - public Response rebuildMetricsForCreditor(@PathParam("cdtrnumber") String cdtrnumber) { - StringBuffer messageBuffer = new StringBuffer(); - long l = System.currentTimeMillis(); - log("├── init cdtr metrics for " + cdtrnumber + "...", messageBuffer); - try { - - logger.info("│   │   ├── group invoices by creditor " + cdtrnumber + "..."); - - int count = 0; - - // First we need to remove all metrics for this creditor - String bpID = InvoiceUtil.buildBPID(cdtrnumber); - logger.info("│   │   ├── delete metrics for " + bpID + "..."); - List oldMetricKeys = metricDataService - .deleteAllMetricsByBPID(MetricCreditorService.TYPE_METRIC_CREDITOR, bpID); - logger.info("│   │   ├── found " + oldMetricKeys.size() + " metrics for " + bpID + "..."); - metricCreditorService.reset(oldMetricKeys); - // next rebuild gauges and metrics for this creditor - List invoices = documentService.find( - "($modelversion:rechnungseingang-* OR $modelversion:gutschriftabgleich-*) " + - " AND type:workitem AND cdtr.number:" + cdtrnumber, - 9999, 0, "invoice.number", false); - logger.info("│   │   ├──found " + invoices.size() + " open invoices"); - - List newMetricKeys = new ArrayList<>(); - for (ItemCollection invoice : invoices) { - try { - ItemCollection metricData = metricCreditorService.getMetricByInvoice(invoice); - newMetricKeys.add(metricData.getItemValueString("name")); - // Jetzt Rechnung addieren - metricCreditorService.addInvoice(metricData, invoice); - logger.info("│   │   │   ├──update metric " + InvoiceUtil.getBPId(invoice) + " Invoice: " - + invoice.getItemValueString("invoice.number") + " Saldo: " - + invoice.getItemValueDouble(MetricCreditorService.ITEM_TOTAL)); - - metricCreditorService.putMetric(metricData); - count++; - } catch (PluginException e) { - // invalid invoice - e.g. no cdtr. number - } - } - - logger.info("│   │   ├── updated metric for " + count + " invoices."); - - log("│   ├── computing metrics finished in " + (System.currentTimeMillis() - l) + "ms", messageBuffer); - - logger.info("│   ├── save and init metrics..."); - // run in new transaction! - metricCreditorService.refreshGauges(newMetricKeys); - - String message = "├── rebuild cdtr metrics completed in " - + (System.currentTimeMillis() - l) - + "ms"; - log(message, messageBuffer); - return Response.ok().entity(messageBuffer.toString()).build(); - - } catch (Exception e) { - e.printStackTrace(); - return Response.serverError() - .entity("Failed to initialize metrics: " + e.getMessage()) - .build(); - } - } - /** * Diese Methode berechnet alle Metriken auf basis der existierenden Rechnungen * neu @@ -184,7 +104,7 @@ public class MetricCreditorRestService { // Jetzt Rechnung addieren metricCreditorService.addInvoice(metricData, invoice); logger.info("│   │   │   ├──update metric " + InvoiceUtil.getBPId(invoice)); - metricCreditorService.putMetric(metricData); + metricCreditorService.updateMetric(metricData); count++; } catch (PluginException e) { // invalid invoice - e.g. no cdtr. number diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorService.java index 1905456..867bb2a 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorService.java @@ -16,7 +16,6 @@ import org.imixs.workflow.ItemCollection; import org.imixs.workflow.engine.DocumentService; import org.imixs.workflow.engine.ProcessingEvent; import org.imixs.workflow.exceptions.PluginException; -import org.imixs.workflow.exceptions.QueryException; import com.alexanderlogistics.InvoiceUtil; import com.alexanderlogistics.KreditorDebitorService; @@ -24,8 +23,6 @@ import com.alexanderlogistics.KreditorDebitorService; import jakarta.annotation.security.DeclareRoles; import jakarta.annotation.security.RolesAllowed; import jakarta.annotation.security.RunAs; -import jakarta.ejb.TransactionAttribute; -import jakarta.ejb.TransactionAttributeType; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Observes; import jakarta.inject.Inject; @@ -60,8 +57,10 @@ public class MetricCreditorService { private final Set registeredGauges = ConcurrentHashMap.newKeySet(); public static final String TYPE_METRIC_CREDITOR = "metric.creditor"; - public static final String ITEM_TOTAL = "invoice.total"; - public static final String ITEM_SALDO = "invoice.saldo"; + public static final String ITEM_INVOICE_TOTAL = "invoice.total"; + public static final String ITEM_INVOICE_SALDO = "invoice.saldo"; + public static final String ITEM_METRIC_BALANCE = "invoice.balance"; + public static final String ITEM_METRIC_SALES = "invoice.sales"; @Inject @RegistryScope(scope = MetricRegistry.APPLICATION_SCOPE) @@ -88,17 +87,6 @@ public class MetricCreditorService { registeredGauges.clear(); } - /** - * Reset the internal metricCache and clears all registered Gauges for a list of - * metric keys. - */ - public void reset(List metricKeys) { - for (String metricKey : metricKeys) { - metricCache.remove(metricKey); - metricRegistry.remove(metricKey); - } - } - /** * Process Metric only if some data has changed.... * @@ -124,9 +112,11 @@ public class MetricCreditorService { if (lastInvoice != null) { try { ItemCollection lastMetricData = getMetricByInvoice(lastInvoice); - subtractInvoice(lastMetricData, lastInvoice); - putMetric(lastMetricData); - metricDataService.saveMetric(lastMetricData); + // update last metric only if exists... + if (!isNewMetric(lastMetricData)) { + subtractInvoice(lastMetricData, lastInvoice); + updateMetric(lastMetricData); + } } catch (PluginException e) { // invalid invoice - e.g. no cdtr. number } @@ -137,11 +127,7 @@ public class MetricCreditorService { ItemCollection metricData = getMetricByInvoice(invoice); // Saldo-Berechnung addInvoice(metricData, invoice); - putMetric(metricData); - metricDataService.saveMetric(metricData); - - // Update the Gauge - updateGauge(metricData); + updateMetric(metricData); logger.info("Metric cdtr update took " + (System.currentTimeMillis() - l) + "ms"); } catch (PluginException e) { // invalid invoice - e.g. no cdtr. number @@ -150,6 +136,17 @@ public class MetricCreditorService { } + /** + * Returns true if the metric is not yet registered. This means we do not have + * sales or balances for this metric + * + * @param metricData + * @return + */ + public boolean isNewMetric(ItemCollection metricData) { + return (!metricCache.containsKey(metricData.getItemValueString("name"))); + } + /** * Returns the corresponding metric data object for an invoice. The method uses * an internal cache. If the metric was not yet cached the method loads the @@ -169,12 +166,9 @@ public class MetricCreditorService { } String metricKey = MetricDataService.buildKeyByInvoice(invoice); ItemCollection metricData = metricCache.get(metricKey); - if (metricData == null) { - metricData = loadMetric(invoice); - } // if metric still null we create a new metric data object. if (metricData == null) { - metricData = createMetaData(invoice); + metricData = createMetricData(invoice); } return metricData; } @@ -189,15 +183,6 @@ public class MetricCreditorService { return metricCache.get(key); } - /** - * Puts a metric data object into the cache. - * - * @param metricData - */ - public void putMetric(ItemCollection metricData) { - metricCache.put(metricData.getItemValueString("name"), metricData); - } - /** * Returns a list with all cached metric keys. * The method returns an unmodifiable list to prevent modifications. @@ -209,44 +194,17 @@ public class MetricCreditorService { } /** - * This method loads a metric entity for a given invoice workitem. If no metric - * entity exits, the method - * creates a new metric entity. - * - * @param invoice - * @return - * @throws PluginException - */ - private ItemCollection loadMetric(ItemCollection invoice) throws PluginException { - ItemCollection creditorMetric = null; - if (invoice == null) { - return null; - } - try { - String metricKey = MetricDataService.buildKeyByInvoice(invoice); - String query = "(type:" + TYPE_METRIC_CREDITOR + ") AND (name:" + metricKey + ")"; - List result = documentService.find(query, 1, 0, "$modified", true); - if (result.size() > 0) { - creditorMetric = result.get(0); - } - } catch (IllegalArgumentException | QueryException e) { - throw new PluginException(PluginException.class.getName(), - "Failed to load metric object for invoice " + invoice.getUniqueID() + ": ", e.getMessage(), e); - } - return creditorMetric; - } - - /** - * Creates an empty Creditor Meta Data Object (ItemCollection) + * Creates an empty Creditor Metric Data Object (ItemCollection) *

- * The ItemCollection stores the name and number and also all saldos for all - * currencies + * The ItemCollection stores the name and number and all categories. + * A new metric object does not yet have the items 'invoice.saldo' and + * 'invoice.total' * * @param invoice - invoice ItemCollection * @return * @throws PluginException */ - private ItemCollection createMetaData(ItemCollection invoice) throws PluginException { + private ItemCollection createMetricData(ItemCollection invoice) throws PluginException { if (invoice == null) { return null; } @@ -269,17 +227,20 @@ public class MetricCreditorService { * @param cdtrNumber - the creditor number * @param cdtrName - the creditor name */ - public void updateGauge(ItemCollection metricData) { + public void updateMetric(ItemCollection metricData) { String metricKey = metricData.getItemValueString("name"); - String bpName = metricData.getItemValueString("bp.name"); - String bpId = metricData.getItemValueString("bp.id"); - String country = metricData.getItemValueString("country"); - String department = metricData.getItemValueString("department"); - String currency = metricData.getItemValueString("currency"); + // Cache aktualisieren + metricCache.put(metricKey, metricData); // Prüfen ob Gauge bereits registriert ist if (registeredGauges.add(metricKey)) { // returns true newly added + String bpName = metricData.getItemValueString("bp.name"); + String bpId = metricData.getItemValueString("bp.id"); + String country = metricData.getItemValueString("country"); + String department = metricData.getItemValueString("department"); + String currency = metricData.getItemValueString("currency"); + List tags = new ArrayList<>(); tags.add(new Tag("type", "cdtr")); tags.add(new Tag("id", bpId)); @@ -287,20 +248,24 @@ public class MetricCreditorService { tags.add(new Tag("country", country)); tags.add(new Tag("currency", currency)); tags.add(new Tag("department", department)); - logger.fine("register new metric for department: " + department + - ", " + metricData.getItemValueString(ITEM_SALDO) + - " " + currency); - Metadata metadata = Metadata.builder() + + // Saldo Gauge + Metadata balanceMetadata = Metadata.builder() .withName("cdtr.balance") .withDescription("Creditor Balance") .build(); - - metricCache.get(metricKey); - metricRegistry.gauge(metadata, - () -> metricCache.get(metricKey).getItemValueDouble(ITEM_SALDO), + metricRegistry.gauge(balanceMetadata, + () -> metricCache.get(metricKey).getItemValueDouble(ITEM_METRIC_BALANCE), + tags.toArray(new Tag[0])); + + // Umsatz Gauge + Metadata revenueMetadata = Metadata.builder() + .withName("cdtr.sales") + .withDescription("Creditor Sales") + .build(); + metricRegistry.gauge(revenueMetadata, + () -> metricCache.get(metricKey).getItemValueDouble(ITEM_METRIC_SALES), tags.toArray(new Tag[0])); - } else { - logger.fine("Cdtr Gauge already registered for department: " + department); } } @@ -312,57 +277,54 @@ public class MetricCreditorService { * @param invoice */ public void addInvoice(ItemCollection metricData, ItemCollection invoice) { - double invoiceTotal = invoice.getItemValueDouble(ITEM_TOTAL); + if (metricData == null || invoice == null) { + return; + } + double invoiceSaldo = invoice.getItemValueDouble(ITEM_INVOICE_TOTAL); + double invoiceTotal = invoice.getItemValueDouble(ITEM_INVOICE_TOTAL); if (!"workitem".equals(invoice.getType()) || invoice.getTaskID() >= 5800) { // vorgang ist archiviert oder gelöscht worden => saldo = 0! - invoiceTotal = 0.0; - } - // is the metric new? - if (!metricData.hasItem(ITEM_SALDO)) { - // init metric with the total value! - metricData.setItemValue(ITEM_SALDO, InvoiceUtil.round(invoiceTotal)); - } else { - double lastSaldo = metricData.getItemValueDouble(ITEM_SALDO); - metricData.setItemValue(ITEM_SALDO, InvoiceUtil.round(lastSaldo + invoiceTotal)); + invoiceSaldo = 0.0; } + // update balance + double lastBalance = metricData.getItemValueDouble(ITEM_METRIC_BALANCE); + metricData.setItemValue(ITEM_METRIC_BALANCE, InvoiceUtil.round(lastBalance + invoiceSaldo)); + + // update sales + double lastTotal = metricData.getItemValueDouble(ITEM_METRIC_SALES); + metricData.setItemValue(ITEM_METRIC_SALES, InvoiceUtil.round(lastTotal + invoiceTotal)); + } public void subtractInvoice(ItemCollection metricData, ItemCollection invoice) { if (metricData == null || invoice == null) { return; } - double invoiceTotal = invoice.getItemValueDouble(ITEM_TOTAL); + double invoiceTotal = invoice.getItemValueDouble(ITEM_INVOICE_TOTAL); + double invoiceSaldo = invoice.getItemValueDouble(ITEM_INVOICE_TOTAL); if (!"workitem".equals(invoice.getType()) || invoice.getTaskID() >= 5800) { // vorgang ist archiviert oder gelöscht worden => saldo = 0! - invoiceTotal = 0.0; + invoiceSaldo = 0.0; } // subtract only if metric saldo exists - if (metricData.hasItem(ITEM_SALDO)) { - double lastSaldo = metricData.getItemValueDouble(ITEM_SALDO); - metricData.setItemValue(ITEM_SALDO, InvoiceUtil.round(lastSaldo - invoiceTotal)); - } + double lastBalance = metricData.getItemValueDouble(ITEM_METRIC_BALANCE); + metricData.setItemValue(ITEM_METRIC_BALANCE, InvoiceUtil.round(lastBalance - invoiceSaldo)); + + // Neue Umsatz Logik + double lastSales = metricData.getItemValueDouble(ITEM_METRIC_SALES); + metricData.setItemValue(ITEM_METRIC_SALES, InvoiceUtil.round(lastSales - invoiceTotal)); + } /** * Helper Method that refreshes all gauges. The method is called by the * RestService during a rebuild. */ - @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) public void refreshGauges() { List keys = getMetricKeys(); - refreshGauges(keys); - } - - /** - * Helper Method that refreshes all gauges. The method is called by the - * RestService during a rebuild. - */ - @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) - public void refreshGauges(List keys) { for (String hashKey : keys) { ItemCollection metricData = getMetric(hashKey); - documentService.save(metricData); - updateGauge(metricData); + updateMetric(metricData); } } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDataService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDataService.java index aa0b03e..52fbf0d 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDataService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDataService.java @@ -1,14 +1,10 @@ package com.alexanderlogistics.metrics; -import java.util.ArrayList; -import java.util.List; import java.util.Objects; import org.imixs.workflow.ItemCollection; import org.imixs.workflow.engine.DocumentService; -import org.imixs.workflow.engine.index.SearchService; import org.imixs.workflow.exceptions.PluginException; -import org.imixs.workflow.exceptions.QueryException; import com.alexanderlogistics.InvoiceUtil; @@ -34,15 +30,6 @@ public class MetricDataService { @Inject DocumentService documentService; - /** - * Runs with Manager access - * - * @param metricData - */ - public void saveMetric(ItemCollection metricData) { - documentService.save(metricData); - } - /** * This helper method reads a 'dirty' workitem in a new transaction. This is * used for calculating the new metric values @@ -55,53 +42,6 @@ public class MetricDataService { return dirtyInvoice; } - /** - * This method deletes all metrics - * - * @throws PluginException - * - */ - @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) - public void deleteAllMetrics(String metricType) throws PluginException { - try { - String query = "(type:" + metricType + ")"; - List result = documentService.find(query, SearchService.DEFAULT_MAX_SEARCH_RESULT, 0); - for (ItemCollection metric : result) { - documentService.remove(metric); - } - - } catch (IllegalArgumentException | QueryException e) { - throw new PluginException(MetricDataService.class.getName(), - "Failed to delete metrics", e.getMessage(), e); - } - } - - /** - * This method deletes all metrics for a given BP ID - * - * @throws PluginException - * - */ - @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) - public List deleteAllMetricsByBPID(String metricType, String bpID) throws PluginException { - List result = new ArrayList<>(); - try { - String query = "(type:" + metricType + ")"; - List metricList = documentService.find(query, SearchService.DEFAULT_MAX_SEARCH_RESULT, 0); - for (ItemCollection metric : metricList) { - if (metric.getItemValueString("bp.id").equals(bpID)) { - result.add(metric.getItemValueString("name")); - documentService.remove(metric); - } - } - - } catch (IllegalArgumentException | QueryException e) { - throw new PluginException(MetricDataService.class.getName(), - "Failed to delete metrics", e.getMessage(), e); - } - return result; - } - /** * Builds the metric hash key by the invoice attributes. The returned key can be * used for caching the metric. diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorRestService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorRestService.java index 34c9c35..f41b97b 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorRestService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorRestService.java @@ -1,6 +1,5 @@ package com.alexanderlogistics.metrics; -import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; @@ -15,7 +14,6 @@ import jakarta.ejb.Stateless; 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.core.MediaType; import jakarta.ws.rs.core.Response; @@ -64,9 +62,6 @@ public class MetricDebitorRestService { // first clear the metric cache metricDebitorService.reset(); logger.info("│   ├── reset metric cache"); - // run in new transaction! - metricDataService.deleteAllMetrics(MetricDebitorService.TYPE_METRIC_DEBITOR); - logger.info("│   ├── delete metrics"); computeMetrics(); logger.info("│   ├── computing metrics finished in " + (System.currentTimeMillis() - l) + "ms"); @@ -89,92 +84,6 @@ public class MetricDebitorRestService { } } - /** - * This method initializes the metrics for all creditors with open invoices. - * - * The method deletes all existing metrics and creates new metric entires for - * each creditor. - * - * @return - */ - @GET - @Path("/rebuild/{dbtrnumber}") - @Produces({ MediaType.TEXT_PLAIN }) - public Response rebuildMetricsForDebitor(@PathParam("dbtrnumber") String dbtrnumber) { - StringBuffer messageBuffer = new StringBuffer(); - long l = System.currentTimeMillis(); - log("├── init dbtr metrics for " + dbtrnumber + "...", messageBuffer); - try { - - logger.info("│   │   ├── group invoices by creditor " + dbtrnumber + "..."); - - int count = 0; - - // First we need to remove all metrics for this creditor - String bpID = InvoiceUtil.buildBPID(dbtrnumber); - logger.info("│   │   ├── delete metrics for " + bpID + "..."); - List oldMetricKeys = metricDataService - .deleteAllMetricsByBPID(MetricDebitorService.TYPE_METRIC_DEBITOR, bpID); - logger.info("│   │   ├── found " + oldMetricKeys.size() + " metrics for " + bpID + "..."); - metricDebitorService.reset(oldMetricKeys); - // next rebuild gauges and metrics for this creditor - String queryDbtrNumber = dbtrnumber; - if (queryDbtrNumber.startsWith("D")) { - queryDbtrNumber = queryDbtrNumber.substring(1); // DAS IST DER CARGOSOFT IRRSINN - } - String query = "$modelversion:rechnungsausgang-* AND type:workitem " + - " AND type:workitem AND dbtr.number:" + queryDbtrNumber; - logger.fine("Query = " + query); - List invoices = documentService.find( - query, - 9999, 0, "invoice.number", false); - logger.info("│   │   ├──found " + invoices.size() + " open invoices"); - - List newMetricKeys = new ArrayList<>(); - for (ItemCollection invoice : invoices) { - try { - ItemCollection metricData = metricDebitorService.getMetricByInvoice(invoice); - String metricKey = metricData.getItemValueString("name"); - newMetricKeys.add(metricKey); - // Jetzt Rechnung addieren - logger.info("│   │   │   ├──update metric (" + metricKey + ")" + InvoiceUtil.getBPId(invoice) - + " Invoice: " - + invoice.getItemValueString("invoice.number") + " Saldo: " - + invoice.getItemValueDouble(MetricDebitorService.ITEM_SALDO)); - logger.info("│   │   │   │   ├── last metric saldo: " - + metricData.getItemValueDouble(MetricDebitorService.ITEM_SALDO)); - metricDebitorService.addInvoice(metricData, invoice); - logger.info("│   │   │   │   ├── new metric saldo: " - + metricData.getItemValueDouble(MetricDebitorService.ITEM_SALDO)); - metricDebitorService.putMetric(metricData); - count++; - } catch (PluginException e) { - // invalid invoice - e.g. no dbtr. number - } - } - - logger.info("│   │   ├── updated metric for " + count + " invoices."); - - log("│   ├── computing metrics finished in " + (System.currentTimeMillis() - l) + "ms", messageBuffer); - - logger.info("│   ├── save and init metrics..."); - // run in new transaction! - metricDebitorService.refreshGauges(newMetricKeys); - - String message = "├── rebuild dbtr metrics completed in " - + (System.currentTimeMillis() - l) - + "ms"; - log(message, messageBuffer); - return Response.ok().entity(messageBuffer.toString()).build(); - - } catch (Exception e) { - e.printStackTrace(); - return Response.serverError() - .entity("Failed to initialize metrics: " + e.getMessage()) - .build(); - } - } - /** * Diese Methode berechnet alle Metriken auf basis der existierenden Rechnungen * neu @@ -199,7 +108,7 @@ public class MetricDebitorRestService { // Jetzt Rechnung addieren metricDebitorService.addInvoice(metricData, invoice); logger.info("│   │   │   ├──update metric " + InvoiceUtil.getBPId(invoice)); - metricDebitorService.putMetric(metricData); + metricDebitorService.updateMetric(metricData); count++; } catch (PluginException e) { // invalid invoice - e.g. no dbtr. number diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorService.java index f2af247..5fc057f 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorService.java @@ -16,15 +16,12 @@ import org.imixs.workflow.ItemCollection; import org.imixs.workflow.engine.DocumentService; import org.imixs.workflow.engine.ProcessingEvent; import org.imixs.workflow.exceptions.PluginException; -import org.imixs.workflow.exceptions.QueryException; import com.alexanderlogistics.InvoiceUtil; import jakarta.annotation.security.DeclareRoles; import jakarta.annotation.security.RolesAllowed; import jakarta.annotation.security.RunAs; -import jakarta.ejb.TransactionAttribute; -import jakarta.ejb.TransactionAttributeType; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Observes; import jakarta.inject.Inject; @@ -56,7 +53,10 @@ public class MetricDebitorService { private final Set registeredGauges = ConcurrentHashMap.newKeySet(); public static final String TYPE_METRIC_DEBITOR = "metric.debitor"; - public static final String ITEM_SALDO = "invoice.saldo"; + public static final String ITEM_INVOICE_TOTAL = "invoice.total"; + public static final String ITEM_INVOICE_SALDO = "invoice.saldo"; + public static final String ITEM_METRIC_BALANCE = "invoice.balance"; + public static final String ITEM_METRIC_SALES = "invoice.sales"; @Inject @RegistryScope(scope = MetricRegistry.APPLICATION_SCOPE) @@ -80,17 +80,6 @@ public class MetricDebitorService { registeredGauges.clear(); } - /** - * Reset the internal metricCache and clears all registered Gauges for a list of - * metric keys. - */ - public void reset(List metricKeys) { - for (String metricKey : metricKeys) { - metricCache.remove(metricKey); - metricRegistry.remove(metricKey); - } - } - /** * Process Metric only if some data has changed.... * @@ -116,9 +105,10 @@ public class MetricDebitorService { if (lastInvoice != null) { try { ItemCollection lastMetricData = getMetricByInvoice(lastInvoice); - subtractInvoice(lastMetricData, lastInvoice); - putMetric(lastMetricData); - metricDataService.saveMetric(lastMetricData); + if (!isNewMetric(lastMetricData)) { + subtractInvoice(lastMetricData, lastInvoice); + updateMetric(lastMetricData); + } } catch (PluginException e) { // invalid invoice - e.g. no cdtr. number } @@ -129,11 +119,8 @@ public class MetricDebitorService { ItemCollection metricData = getMetricByInvoice(invoice); // Saldo-Berechnung addInvoice(metricData, invoice); - putMetric(metricData); - metricDataService.saveMetric(metricData); + updateMetric(metricData); - // Update the Gauge - updateGauge(metricData); logger.info("Metric dbtr update took " + (System.currentTimeMillis() - l) + "ms"); } catch (PluginException e) { // invalid invoice - e.g. no dbtr. number @@ -142,6 +129,17 @@ public class MetricDebitorService { } + /** + * Returns true if the metric is not yet registered. This means we do not have + * sales or balances for this metric + * + * @param metricData + * @return + */ + public boolean isNewMetric(ItemCollection metricData) { + return (!metricCache.containsKey(metricData.getItemValueString("name"))); + } + /** * Returns the corresponding metric data object for an invoice. The method uses * an internal cache. If the metric was not yet cached the method loads the @@ -161,9 +159,6 @@ public class MetricDebitorService { } String metricKey = MetricDataService.buildKeyByInvoice(invoice); ItemCollection metricData = metricCache.get(metricKey); - if (metricData == null) { - metricData = loadMetric(invoice); - } // if metric still null we create a new metric data object. if (metricData == null) { metricData = createMetaData(invoice); @@ -181,15 +176,6 @@ public class MetricDebitorService { return metricCache.get(key); } - /** - * Puts a metric data object into the cache. - * - * @param metricData - */ - public void putMetric(ItemCollection metricData) { - metricCache.put(metricData.getItemValueString("name"), metricData); - } - /** * Returns a list with all cached metric keys. * The method returns an unmodifiable list to prevent modifications. @@ -200,34 +186,6 @@ public class MetricDebitorService { return Collections.unmodifiableList(new ArrayList<>(metricCache.keySet())); } - /** - * This method loads a metric entity for a given invoice workitem. If no metric - * entity exits, the method - * creates a new metric entity. - * - * @param invoice - * @return - * @throws PluginException - */ - private ItemCollection loadMetric(ItemCollection invoice) throws PluginException { - ItemCollection debitorMetric = null; - if (invoice == null) { - return null; - } - try { - String metricKey = MetricDataService.buildKeyByInvoice(invoice); - String query = "(type:" + TYPE_METRIC_DEBITOR + ") AND (name:" + metricKey + ")"; - List result = documentService.find(query, 1, 0, "$modified", true); - if (result.size() > 0) { - debitorMetric = result.get(0); - } - } catch (IllegalArgumentException | QueryException e) { - throw new PluginException(PluginException.class.getName(), - "Failed to load metric object for invoice " + invoice.getUniqueID() + ": ", e.getMessage(), e); - } - return debitorMetric; - } - /** * Creates an empty Debitor Meta Data Object (ItemCollection) *

@@ -260,17 +218,20 @@ public class MetricDebitorService { * * @param metricData - the metricData ItemCollection */ - public void updateGauge(ItemCollection metricData) { - + public void updateMetric(ItemCollection metricData) { String metricKey = metricData.getItemValueString("name"); - String bpName = metricData.getItemValueString("bp.name"); - String bpId = metricData.getItemValueString("bp.id"); - String country = metricData.getItemValueString("country"); - String department = metricData.getItemValueString("department"); - String currency = metricData.getItemValueString("currency"); + + // Cache aktualisieren + metricCache.put(metricKey, metricData); // Prüfen ob Gauge bereits registriert ist if (registeredGauges.add(metricKey)) { // returns true newly added + String bpName = metricData.getItemValueString("bp.name"); + String bpId = metricData.getItemValueString("bp.id"); + String country = metricData.getItemValueString("country"); + String department = metricData.getItemValueString("department"); + String currency = metricData.getItemValueString("currency"); + List tags = new ArrayList<>(); tags.add(new Tag("type", "dbtr")); tags.add(new Tag("id", bpId)); @@ -279,19 +240,25 @@ public class MetricDebitorService { tags.add(new Tag("currency", currency)); tags.add(new Tag("department", department)); logger.fine("register new metric for department: " + department + - ", " + metricData.getItemValueString(ITEM_SALDO) + + ", " + metricData.getItemValueString(ITEM_METRIC_BALANCE) + " " + currency); - Metadata metadata = Metadata.builder() + // Saldo Gauge + Metadata balanceMetadata = Metadata.builder() .withName("dbtr.balance") .withDescription("Debitor Balance") .build(); - - metricCache.get(metricKey); - metricRegistry.gauge(metadata, - () -> metricCache.get(metricKey).getItemValueDouble(ITEM_SALDO), + metricRegistry.gauge(balanceMetadata, + () -> metricCache.get(metricKey).getItemValueDouble(ITEM_METRIC_BALANCE), + tags.toArray(new Tag[0])); + + // Umsatz Gauge + Metadata revenueMetadata = Metadata.builder() + .withName("dbtr.sales") + .withDescription("Debitor Sales") + .build(); + metricRegistry.gauge(revenueMetadata, + () -> metricCache.get(metricKey).getItemValueDouble(ITEM_METRIC_SALES), tags.toArray(new Tag[0])); - } else { - logger.fine("Gauge already registered for department: " + department); } } @@ -303,64 +270,59 @@ public class MetricDebitorService { * @param invoice */ public void addInvoice(ItemCollection metricData, ItemCollection invoice) { - double invoiceTotal = invoice.getItemValueDouble(ITEM_SALDO); + double invoiceSaldo = invoice.getItemValueDouble(ITEM_INVOICE_SALDO); + double invoiceTotal = invoice.getItemValueDouble(ITEM_INVOICE_TOTAL); if (!"workitem".equals(invoice.getType()) || invoice.getTaskID() >= 5800) { // vorgang ist archiviert oder gelöscht worden => saldo = 0! - invoiceTotal = 0.0; + invoiceSaldo = 0.0; } logger.fine("│   │   │   │   ├── Invoice: " + invoice.getItemValueString("invoice.number") + " Saldo=" - + invoiceTotal); - // is the metric new? - if (!metricData.hasItem(ITEM_SALDO)) { - // init metric with the total value! - metricData.setItemValue(ITEM_SALDO, InvoiceUtil.round(invoiceTotal)); - } else { - double lastSaldo = metricData.getItemValueDouble(ITEM_SALDO); - logger.fine("│   │   │   │   ├──letzter Metric Saldo=" + lastSaldo); - metricData.setItemValue(ITEM_SALDO, InvoiceUtil.round(lastSaldo + invoiceTotal)); - } + + invoiceSaldo); + // update saldo + double lastSaldo = metricData.getItemValueDouble(ITEM_METRIC_BALANCE); + logger.fine("│   │   │   │   ├── last metric balance=" + lastSaldo); + metricData.setItemValue(ITEM_METRIC_BALANCE, InvoiceUtil.round(lastSaldo + invoiceSaldo)); + + // Umsatz-Berechnung + double lastTotal = metricData.getItemValueDouble(ITEM_METRIC_SALES); + metricData.setItemValue(ITEM_METRIC_SALES, InvoiceUtil.round(lastTotal + invoiceTotal)); + } public void subtractInvoice(ItemCollection metricData, ItemCollection invoice) { if (metricData == null || invoice == null) { return; } - double invoiceTotal = invoice.getItemValueDouble(ITEM_SALDO); + double invoiceSaldo = invoice.getItemValueDouble(ITEM_INVOICE_SALDO); + double invoiceTotal = invoice.getItemValueDouble(ITEM_INVOICE_TOTAL); logger.fine("│   │   │   │   ├──Invoice: " + invoice.getItemValueString("invoice.number") + " Saldo=" - + invoiceTotal); + + invoiceSaldo); if (!"workitem".equals(invoice.getType()) || invoice.getTaskID() >= 5800) { // vorgang ist archiviert oder gelöscht worden => saldo = 0! - invoiceTotal = 0.0; + invoiceSaldo = 0.0; } // subtract only if metric saldo exists - if (metricData.hasItem(ITEM_SALDO)) { - double lastSaldo = metricData.getItemValueDouble(ITEM_SALDO); - logger.fine("│   │   │   │   ├──letzter Metric Saldo=" + lastSaldo); - metricData.setItemValue(ITEM_SALDO, InvoiceUtil.round(lastSaldo - invoiceTotal)); - } + double lastSaldo = metricData.getItemValueDouble(ITEM_METRIC_BALANCE); + logger.fine("│   │   │   │   ├── last Metric balance=" + lastSaldo); + metricData.setItemValue(ITEM_METRIC_BALANCE, InvoiceUtil.round(lastSaldo - invoiceSaldo)); + + // update Umsatz + double lastTotal = metricData.getItemValueDouble(ITEM_METRIC_SALES); + metricData.setItemValue(ITEM_METRIC_SALES, InvoiceUtil.round(lastTotal - invoiceTotal)); + } /** * Helper Method that refreshes all gauges. The method is called by the * RestService during a rebuild. */ - @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) + public void refreshGauges() { List keys = getMetricKeys(); - refreshGauges(keys); - } - - /** - * Helper Method that refreshes all gauges. The method is called by the - * RestService during a rebuild. - */ - @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) - public void refreshGauges(List keys) { for (String hashKey : keys) { ItemCollection metricData = getMetric(hashKey); - documentService.save(metricData); - updateGauge(metricData); + updateMetric(metricData); } } From 017ef6a75233afc7045c270dbc327ab7fb8a825c Mon Sep 17 00:00:00 2001 From: Ralph Soika Date: Sat, 1 Feb 2025 17:21:49 +0100 Subject: [PATCH 02/12] refactoring metric logik --- .../metrics/MetricCreditorRestService.java | 70 ++++++++----- .../metrics/MetricCreditorService.java | 98 +++++++++++++++---- .../metrics/MetricDataService.java | 23 +++++ .../metrics/MetricDebitorRestService.java | 75 ++++++++------ .../metrics/MetricDebitorService.java | 84 +++++++++++++--- 5 files changed, 261 insertions(+), 89 deletions(-) diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorRestService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorRestService.java index c7ee05e..a18ba93 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorRestService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorRestService.java @@ -55,18 +55,15 @@ public class MetricCreditorRestService { public Response rebuildMetrics() { StringBuffer messageBuffer = new StringBuffer(); long l = System.currentTimeMillis(); - log("├── init cdtr metrics...", messageBuffer); + log("├── rebuild cdtr metrics...", messageBuffer); try { + log("│   ├── delete all metrics", messageBuffer); + metricDataService.deleteAllMetrics(MetricCreditorService.TYPE_METRIC_CREDITOR); // first clear the metric cache metricCreditorService.reset(); log("│   ├── reset metric cache", messageBuffer); - computeMetrics(); - log("│   ├── computing metrics finished in " + (System.currentTimeMillis() - l) + "ms", messageBuffer); - - logger.info("│   ├── save and init metrics..."); - // run in new transaction! - metricCreditorService.refreshGauges(); + computeMetrics(messageBuffer); String message = "├── rebuild cdtr metrics completed in " + (System.currentTimeMillis() - l) @@ -76,8 +73,9 @@ public class MetricCreditorRestService { } catch (Exception e) { e.printStackTrace(); + log("Failed to initialize metrics: " + e.getMessage(), messageBuffer); return Response.serverError() - .entity("Failed to initialize metrics: " + e.getMessage()) + .entity(messageBuffer.toString() + e.getMessage()) .build(); } } @@ -86,35 +84,55 @@ public class MetricCreditorRestService { * Diese Methode berechnet alle Metriken auf basis der existierenden Rechnungen * neu * + * @throws QueryException + * @throws InterruptedException + * */ - public void computeMetrics() { + public void computeMetrics(StringBuffer messageBuffer) throws QueryException, InterruptedException { + long l = System.currentTimeMillis(); + int batchSize = 500; + int totalInvoices = 0; + log("│   │   ├── recalculate metrics...", messageBuffer); + + String query = "($modelversion:rechnungseingang-* OR $modelversion:gutschriftabgleich-*) " + + " AND type:workitem"; + // Gesamtanzahl ermitteln + int totalCount = documentService.count(query); + log("│   │   ├── found " + totalCount + " open invoices", 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 invoices = documentService.find(query, batchSize, pageIndex); - logger.info("│   │   ├── group invoices by creditor..."); - try { - int count = 0; - List invoices = documentService.find( - "($modelversion:rechnungseingang-* OR $modelversion:gutschriftabgleich-*) " + - " AND type:workitem", - 9999, 0, - "invoice.number", false); - logger.info("│   │   ├──found " + invoices.size() + " open invoices"); for (ItemCollection invoice : invoices) { try { ItemCollection metricData = metricCreditorService.getMetricByInvoice(invoice); // Jetzt Rechnung addieren metricCreditorService.addInvoice(metricData, invoice); - logger.info("│   │   │   ├──update metric " + InvoiceUtil.getBPId(invoice)); - metricCreditorService.updateMetric(metricData); - count++; + logger.fine("│   │   │   ├── update metric " + InvoiceUtil.getBPId(invoice)); + metricCreditorService.updateMetric(metricData, true); + totalInvoices++; } catch (PluginException e) { - // invalid invoice - e.g. no cdtr. number + // invalid invoice - e.g. no dbtr. number } - } - logger.info("│   │   ├── updated metric for " + count + " invoices."); - } catch (QueryException e) { - e.printStackTrace(); + + // Fortschritt loggen + log("│   │ ├── Processed page " + (pageIndex + 1) + " of " + totalPages + + " (" + totalInvoices + " of " + totalCount + " invoices)", messageBuffer); + // Optional: Kurze Pause nach jedem 5. Batch + Thread.sleep(100); + } + long duration = System.currentTimeMillis() - l; + double invoicesPerSecond = totalInvoices / (duration / 1000.0); + log("│   │   ├── Successfully processed " + totalInvoices + " invoices in " + + duration + "ms (" + String.format("%.1f", invoicesPerSecond) + " invoices/sec)", messageBuffer); + log("│   │   ├── Updated " + metricCreditorService.getMetricCount() + " metrics.", messageBuffer); + } private void log(String message, StringBuffer messageLog) { diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorService.java index 867bb2a..50de7c5 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorService.java @@ -15,6 +15,7 @@ import org.eclipse.microprofile.metrics.annotation.RegistryScope; import org.imixs.workflow.ItemCollection; import org.imixs.workflow.engine.DocumentService; import org.imixs.workflow.engine.ProcessingEvent; +import org.imixs.workflow.engine.SetupEvent; import org.imixs.workflow.exceptions.PluginException; import com.alexanderlogistics.InvoiceUtil; @@ -79,6 +80,54 @@ public class MetricCreditorService { @ConfigProperty(name = "metrics.enabled", defaultValue = "false") private boolean metricsEnabled; + /** + * Init all metrics during setup. Called by the Imixs SetupService + */ + public void initializeMetrics(@Observes SetupEvent setupEvent) { + if (!metricsEnabled) { + return; + } + + long l = System.currentTimeMillis(); + int batchSize = 500; + int totalMetrics = 0; + + try { + logger.info("├── Initializing creditor metrics from database..."); + String query = "(type:" + TYPE_METRIC_CREDITOR + ")"; + + // Gesamtanzahl ermitteln + int totalCount = documentService.count(query); + + // 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 metrics = documentService.find(query, batchSize, pageIndex); + + for (ItemCollection metric : metrics) { + updateMetric(metric, false); + totalMetrics++; + } + // Fortschritt loggen + logger.info("│ ├── Processed page " + (pageIndex + 1) + " of " + totalPages + + " (" + totalMetrics + " of " + totalCount + " metrics)"); + // Optional: Kurze Pause nach jedem Batch + Thread.sleep(100); + } + + long duration = System.currentTimeMillis() - l; + double metricsPerSecond = totalMetrics / (duration / 1000.0); + + logger.info("├── Successfully initialized " + totalMetrics + " metrics in " + + duration + "ms (" + String.format("%.1f", metricsPerSecond) + " metrics/sec)"); + + } catch (Exception e) { + logger.warning("Failed to initialize metrics: " + e.getMessage()); + } + } + /** * Reset the internal metricCache and clears all registered Gauges. */ @@ -87,6 +136,10 @@ public class MetricCreditorService { registeredGauges.clear(); } + public long getMetricCount() { + return metricCache.size(); + } + /** * Process Metric only if some data has changed.... * @@ -115,7 +168,7 @@ public class MetricCreditorService { // update last metric only if exists... if (!isNewMetric(lastMetricData)) { subtractInvoice(lastMetricData, lastInvoice); - updateMetric(lastMetricData); + updateMetric(lastMetricData, true); } } catch (PluginException e) { // invalid invoice - e.g. no cdtr. number @@ -127,7 +180,7 @@ public class MetricCreditorService { ItemCollection metricData = getMetricByInvoice(invoice); // Saldo-Berechnung addInvoice(metricData, invoice); - updateMetric(metricData); + updateMetric(metricData, true); logger.info("Metric cdtr update took " + (System.currentTimeMillis() - l) + "ms"); } catch (PluginException e) { // invalid invoice - e.g. no cdtr. number @@ -222,17 +275,23 @@ public class MetricCreditorService { } /** - * Helper method to register a gauge for a creditor + * This method registers and updates the metric meta data objects based on a + * given metricData object. Optional the metric data object is persisted. * - * @param cdtrNumber - the creditor number - * @param cdtrName - the creditor name + * @param metricData - the metricData ItemCollection + * @param persist - if true the metricData entity will be persisted */ - public void updateMetric(ItemCollection metricData) { - + public void updateMetric(ItemCollection metricData, boolean persist) { String metricKey = metricData.getItemValueString("name"); + // Cache aktualisieren metricCache.put(metricKey, metricData); + // In Datenbank persistieren + if (persist) { + documentService.save(metricData); + } + // Prüfen ob Gauge bereits registriert ist if (registeredGauges.add(metricKey)) { // returns true newly added String bpName = metricData.getItemValueString("bp.name"); @@ -267,9 +326,20 @@ public class MetricCreditorService { () -> metricCache.get(metricKey).getItemValueDouble(ITEM_METRIC_SALES), tags.toArray(new Tag[0])); } - } + // /** + // * Helper Method that refreshes all gauges. The method is called by the + // * RestService during a rebuild. + // */ + // public void updateAllMetrics() { + // List keys = getMetricKeys(); + // for (String hashKey : keys) { + // ItemCollection metricData = getMetric(hashKey); + // updateMetric(metricData, false); + // } + // } + /** * Addiert den saldo einer Invoice zu einem metricData object * @@ -316,16 +386,4 @@ public class MetricCreditorService { } - /** - * Helper Method that refreshes all gauges. The method is called by the - * RestService during a rebuild. - */ - public void refreshGauges() { - List keys = getMetricKeys(); - for (String hashKey : keys) { - ItemCollection metricData = getMetric(hashKey); - updateMetric(metricData); - } - } - } \ No newline at end of file diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDataService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDataService.java index 52fbf0d..f49dd31 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDataService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDataService.java @@ -1,10 +1,13 @@ package com.alexanderlogistics.metrics; +import java.util.List; import java.util.Objects; import org.imixs.workflow.ItemCollection; import org.imixs.workflow.engine.DocumentService; +import org.imixs.workflow.engine.index.SearchService; import org.imixs.workflow.exceptions.PluginException; +import org.imixs.workflow.exceptions.QueryException; import com.alexanderlogistics.InvoiceUtil; @@ -77,4 +80,24 @@ public class MetricDataService { return "HASH" + hash; } + /** + * This method deletes all metrics + * + * @throws PluginException + * + */ + @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) + public void deleteAllMetrics(String metricType) throws PluginException { + try { + String query = "(type:" + metricType + ")"; + List result = documentService.find(query, SearchService.DEFAULT_MAX_SEARCH_RESULT, 0); + for (ItemCollection metric : result) { + documentService.remove(metric); + } + + } catch (IllegalArgumentException | QueryException e) { + throw new PluginException(MetricDataService.class.getName(), + "Failed to delete metrics", e.getMessage(), e); + } + } } \ No newline at end of file diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorRestService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorRestService.java index f41b97b..4e30fc1 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorRestService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorRestService.java @@ -53,33 +53,30 @@ public class MetricDebitorRestService { @Path("/rebuild") @Produces({ MediaType.TEXT_PLAIN }) public Response rebuildMetrics() { - // Map metricCache = new HashMap(); + + StringBuffer messageBuffer = new StringBuffer(); long l = System.currentTimeMillis(); - logger.info("├── init dbtr metrics..."); + log("├── rebuild dbtr metrics...", messageBuffer); try { + log("│   ├── delete all metrics", messageBuffer); // first clear the metric cache metricDebitorService.reset(); - logger.info("│   ├── reset metric cache"); + log("│   ├── reset metric cache", messageBuffer); - computeMetrics(); - logger.info("│   ├── computing metrics finished in " + (System.currentTimeMillis() - l) + "ms"); - - logger.info("│   ├── save and init metrics ..."); - // run in new transaction! - metricDebitorService.refreshGauges(); + computeMetrics(messageBuffer); String message = "├── rebuild dbtr metrics completed in " + (System.currentTimeMillis() - l) + "ms"; - logger.info(message); - return Response.ok().entity(message).build(); + log(message, messageBuffer); + return Response.ok().entity(messageBuffer.toString()).build(); } catch (Exception e) { e.printStackTrace(); + log("Failed to initialize metrics: " + e.getMessage(), messageBuffer); return Response.serverError() - .entity("Failed to initialize metrics: " + e.getMessage()) + .entity(messageBuffer.toString() + e.getMessage()) .build(); } } @@ -88,37 +85,53 @@ public class MetricDebitorRestService { * Diese Methode berechnet alle Metriken auf basis der existierenden Rechnungen * neu * + * @throws QueryException + * @throws InterruptedException + * */ - public void computeMetrics() { + public void computeMetrics(StringBuffer messageBuffer) throws QueryException, InterruptedException { + long l = System.currentTimeMillis(); + int batchSize = 500; + int totalInvoices = 0; + log("│   │   ├── recalculate metrics...", messageBuffer); - logger.info("│   │   ├── group invoices by debitor..."); - try { - int count = 0; - List invoices = documentService.find( - "$modelversion:rechnungsausgang-* AND type:workitem", - 9999, 0, - "invoice.number", false); + String query = "$modelversion:rechnungsausgang-* AND type:workitem"; + // Gesamtanzahl ermitteln + int totalCount = documentService.count(query); + log("│   │   ├── found " + totalCount + " open invoices", 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 invoices = documentService.find(query, batchSize, pageIndex); - logger.info("│   │   ├── found " + invoices.size() + " open invoices"); for (ItemCollection invoice : invoices) { try { - ItemCollection metricData = metricDebitorService.getMetricByInvoice(invoice); - // Jetzt Rechnung addieren metricDebitorService.addInvoice(metricData, invoice); - logger.info("│   │   │   ├──update metric " + InvoiceUtil.getBPId(invoice)); - metricDebitorService.updateMetric(metricData); - count++; + logger.fine("│   │   │   ├── update metric " + InvoiceUtil.getBPId(invoice)); + metricDebitorService.updateMetric(metricData, true); + totalInvoices++; } catch (PluginException e) { // invalid invoice - e.g. no dbtr. number } - } - logger.info("│   │   ├── updated metric for " + count + " invoices."); - } catch (QueryException e) { - e.printStackTrace(); + + // Fortschritt loggen + log("│   │ ├── Processed page " + (pageIndex + 1) + " of " + totalPages + + " (" + totalInvoices + " of " + totalCount + " invoices)", messageBuffer); + // Optional: Kurze Pause nach jedem 5. Batch + Thread.sleep(100); + } + long duration = System.currentTimeMillis() - l; + double invoicesPerSecond = totalInvoices / (duration / 1000.0); + log("│   │   ├── Successfully processed " + totalInvoices + " invoices in " + + duration + "ms (" + String.format("%.1f", invoicesPerSecond) + " invoices/sec)", messageBuffer); + log("│   │   ├── Updated " + metricDebitorService.getMetricCount() + " metrics.", messageBuffer); } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorService.java index 5fc057f..01a97b2 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorService.java @@ -15,6 +15,7 @@ import org.eclipse.microprofile.metrics.annotation.RegistryScope; import org.imixs.workflow.ItemCollection; import org.imixs.workflow.engine.DocumentService; import org.imixs.workflow.engine.ProcessingEvent; +import org.imixs.workflow.engine.SetupEvent; import org.imixs.workflow.exceptions.PluginException; import com.alexanderlogistics.InvoiceUtil; @@ -72,6 +73,54 @@ public class MetricDebitorService { @ConfigProperty(name = "metrics.enabled", defaultValue = "false") private boolean metricsEnabled; + /** + * Init all metrics during setup. Called by the Imixs SetupService + */ + public void initializeMetrics(@Observes SetupEvent setupEvent) { + if (!metricsEnabled) { + return; + } + + long l = System.currentTimeMillis(); + int batchSize = 500; + int totalMetrics = 0; + + try { + logger.info("├── Initializing debitor metrics from database..."); + String query = "(type:" + TYPE_METRIC_DEBITOR + ")"; + + // Gesamtanzahl ermitteln + int totalCount = documentService.count(query); + + // 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 metrics = documentService.find(query, batchSize, pageIndex); + + for (ItemCollection metric : metrics) { + updateMetric(metric, false); + totalMetrics++; + } + // Fortschritt loggen + logger.info("│ ├── Processed page " + (pageIndex + 1) + " of " + totalPages + + " (" + totalMetrics + " of " + totalCount + " metrics)"); + // Optional: Kurze Pause nach jedem Batch + Thread.sleep(100); + } + + long duration = System.currentTimeMillis() - l; + double metricsPerSecond = totalMetrics / (duration / 1000.0); + + logger.info("├── Successfully initialized " + totalMetrics + " metrics in " + + duration + "ms (" + String.format("%.1f", metricsPerSecond) + " metrics/sec)"); + + } catch (Exception e) { + logger.warning("Failed to initialize metrics: " + e.getMessage()); + } + } + /** * Reset internal metricCache and clear registered Gauges. */ @@ -80,6 +129,10 @@ public class MetricDebitorService { registeredGauges.clear(); } + public long getMetricCount() { + return metricCache.size(); + } + /** * Process Metric only if some data has changed.... * @@ -107,7 +160,7 @@ public class MetricDebitorService { ItemCollection lastMetricData = getMetricByInvoice(lastInvoice); if (!isNewMetric(lastMetricData)) { subtractInvoice(lastMetricData, lastInvoice); - updateMetric(lastMetricData); + updateMetric(lastMetricData, true); } } catch (PluginException e) { // invalid invoice - e.g. no cdtr. number @@ -119,7 +172,7 @@ public class MetricDebitorService { ItemCollection metricData = getMetricByInvoice(invoice); // Saldo-Berechnung addInvoice(metricData, invoice); - updateMetric(metricData); + updateMetric(metricData, true); logger.info("Metric dbtr update took " + (System.currentTimeMillis() - l) + "ms"); } catch (PluginException e) { @@ -214,16 +267,23 @@ public class MetricDebitorService { } /** - * Helper method to register a gauge for a debitor + * This method registers and updates the metric meta data objects based on a + * given metricData object. Optional the metric data object is persisted. * * @param metricData - the metricData ItemCollection + * @param persist - if true the metricData entity will be persisted */ - public void updateMetric(ItemCollection metricData) { + public void updateMetric(ItemCollection metricData, boolean persist) { String metricKey = metricData.getItemValueString("name"); // Cache aktualisieren metricCache.put(metricKey, metricData); + // In Datenbank persistieren + if (persist) { + documentService.save(metricData); + } + // Prüfen ob Gauge bereits registriert ist if (registeredGauges.add(metricKey)) { // returns true newly added String bpName = metricData.getItemValueString("bp.name"); @@ -242,6 +302,7 @@ public class MetricDebitorService { logger.fine("register new metric for department: " + department + ", " + metricData.getItemValueString(ITEM_METRIC_BALANCE) + " " + currency); + // Saldo Gauge Metadata balanceMetadata = Metadata.builder() .withName("dbtr.balance") @@ -317,13 +378,12 @@ public class MetricDebitorService { * Helper Method that refreshes all gauges. The method is called by the * RestService during a rebuild. */ - - public void refreshGauges() { - List keys = getMetricKeys(); - for (String hashKey : keys) { - ItemCollection metricData = getMetric(hashKey); - updateMetric(metricData); - } - } + // public void refreshGauges() { + // List keys = getMetricKeys(); + // for (String hashKey : keys) { + // ItemCollection metricData = getMetric(hashKey); + // updateMetric(metricData, false); + // } + // } } \ No newline at end of file From 90166229bdbdf72ea6100d5c367435070eba4c14 Mon Sep 17 00:00:00 2001 From: Ralph Soika Date: Thu, 6 Feb 2025 15:44:33 +0100 Subject: [PATCH 03/12] changes --- .../BusinessPartnerController.java | 3 +- .../metrics/MetricCreditorRestService.java | 67 +++++++++++++++---- .../metrics/MetricCreditorService.java | 5 ++ .../metrics/MetricDebitorRestService.java | 65 ++++++++++++++---- .../metrics/MetricDebitorService.java | 17 ++--- .../xml/BusinessPartnerImportService.java | 23 ++++++- workflow/dwc/rechnungsausgang-dwc-1.0.2.bpmn | 4 +- workflow/sepa-export-manual-3.0.0.bpmn | 42 ++++++------ 8 files changed, 160 insertions(+), 66 deletions(-) diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerController.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerController.java index c02a15f..390439f 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerController.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerController.java @@ -64,8 +64,7 @@ public class BusinessPartnerController implements Serializable { /** * WorkflowEvent listener to convert the IBAN child list embeded HashMaps into - * ItemCollections and - * reconvert them before processing + * ItemCollections and reconvert them before processing * * @param workflowEvent * @throws AccessDeniedException diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorRestService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorRestService.java index a18ba93..bbb0fe3 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorRestService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorRestService.java @@ -11,6 +11,8 @@ import org.imixs.workflow.exceptions.QueryException; import com.alexanderlogistics.InvoiceUtil; import jakarta.ejb.Stateless; +import jakarta.ejb.TransactionAttribute; +import jakarta.ejb.TransactionAttributeType; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @@ -86,9 +88,11 @@ public class MetricCreditorRestService { * * @throws QueryException * @throws InterruptedException + * @throws PluginException * */ - public void computeMetrics(StringBuffer messageBuffer) throws QueryException, InterruptedException { + public void computeMetrics(StringBuffer messageBuffer) + throws QueryException, InterruptedException, PluginException { long l = System.currentTimeMillis(); int batchSize = 500; int totalInvoices = 0; @@ -105,20 +109,24 @@ public class MetricCreditorRestService { // Verarbeite Page für Page for (int pageIndex = 0; pageIndex < totalPages; pageIndex++) { - List invoices = documentService.find(query, batchSize, pageIndex); - for (ItemCollection invoice : invoices) { - try { - ItemCollection metricData = metricCreditorService.getMetricByInvoice(invoice); - // Jetzt Rechnung addieren - metricCreditorService.addInvoice(metricData, invoice); - logger.fine("│   │   │   ├── update metric " + InvoiceUtil.getBPId(invoice)); - metricCreditorService.updateMetric(metricData, true); - totalInvoices++; - } catch (PluginException e) { - // invalid invoice - e.g. no dbtr. number - } - } + totalInvoices = totalInvoices + computeInvoiceMetrics(query, batchSize, pageIndex); + // List invoices = documentService.find(query, batchSize, + // pageIndex); + + // for (ItemCollection invoice : invoices) { + // try { + // ItemCollection metricData = + // metricCreditorService.getMetricByInvoice(invoice); + // // Jetzt Rechnung addieren + // metricCreditorService.addInvoice(metricData, invoice); + // logger.fine("│   │   │   ├── update metric " + InvoiceUtil.getBPId(invoice)); + // metricCreditorService.updateMetric(metricData, true); + // totalInvoices++; + // } catch (PluginException e) { + // // invalid invoice - e.g. no dbtr. number + // } + // } // Fortschritt loggen log("│   │ ├── Processed page " + (pageIndex + 1) + " of " + totalPages + @@ -135,6 +143,37 @@ public class MetricCreditorRestService { } + /** + * Helper method runs in new transaction + * + * @param query + * @param batchSize + * @param pageIndex + * @throws PluginException + * @throws QueryException + */ + @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) + public int computeInvoiceMetrics(String query, int batchSize, int pageIndex) + throws PluginException, QueryException { + + int updates = 0; + List invoices = documentService.find(query, batchSize, pageIndex); + + for (ItemCollection invoice : invoices) { + try { + ItemCollection metricData = metricCreditorService.getMetricByInvoice(invoice); + // Jetzt Rechnung addieren + metricCreditorService.addInvoice(metricData, invoice); + logger.fine("│   │   │   ├── update metric " + InvoiceUtil.getBPId(invoice)); + metricCreditorService.updateMetric(metricData, true); + updates++; + } catch (PluginException e) { + // invalid invoice - e.g. no dbtr. number + } + } + return updates; + } + 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/metrics/MetricCreditorService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorService.java index 50de7c5..3da4b99 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorService.java @@ -12,6 +12,7 @@ import org.eclipse.microprofile.metrics.Metadata; import org.eclipse.microprofile.metrics.MetricRegistry; import org.eclipse.microprofile.metrics.Tag; import org.eclipse.microprofile.metrics.annotation.RegistryScope; +import org.imixs.archive.core.SnapshotService; import org.imixs.workflow.ItemCollection; import org.imixs.workflow.engine.DocumentService; import org.imixs.workflow.engine.ProcessingEvent; @@ -24,6 +25,7 @@ import com.alexanderlogistics.KreditorDebitorService; import jakarta.annotation.security.DeclareRoles; import jakarta.annotation.security.RolesAllowed; import jakarta.annotation.security.RunAs; +import jakarta.ejb.Singleton; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Observes; import jakarta.inject.Inject; @@ -50,6 +52,7 @@ import jakarta.inject.Inject; "org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS", "org.imixs.ACCESSLEVEL.MANAGERACCESS" }) @RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS") +@Singleton @ApplicationScoped public class MetricCreditorService { @@ -265,6 +268,7 @@ public class MetricCreditorService { ItemCollection metricData = new ItemCollection(); metricData.setType(TYPE_METRIC_CREDITOR); metricData.setItemValue("name", key); + metricData.setItemValue(SnapshotService.NOSNAPSHOT, true); metricData.setItemValue("bp.id", InvoiceUtil.getBPId(invoice)); metricData.setItemValue("bp.name", InvoiceUtil.getBPName(invoice)); metricData.setItemValue("country", invoice.getItemValueString("invoice.country")); @@ -289,6 +293,7 @@ public class MetricCreditorService { // In Datenbank persistieren if (persist) { + metricData.setItemValue(SnapshotService.NOSNAPSHOT, true); documentService.save(metricData); } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorRestService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorRestService.java index 4e30fc1..dba54f0 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorRestService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorRestService.java @@ -11,6 +11,8 @@ import org.imixs.workflow.exceptions.QueryException; import com.alexanderlogistics.InvoiceUtil; import jakarta.ejb.Stateless; +import jakarta.ejb.TransactionAttribute; +import jakarta.ejb.TransactionAttributeType; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @@ -87,9 +89,11 @@ public class MetricDebitorRestService { * * @throws QueryException * @throws InterruptedException + * @throws PluginException * */ - public void computeMetrics(StringBuffer messageBuffer) throws QueryException, InterruptedException { + public void computeMetrics(StringBuffer messageBuffer) + throws QueryException, InterruptedException, PluginException { long l = System.currentTimeMillis(); int batchSize = 500; int totalInvoices = 0; @@ -105,20 +109,22 @@ public class MetricDebitorRestService { // Verarbeite Page für Page for (int pageIndex = 0; pageIndex < totalPages; pageIndex++) { - List invoices = documentService.find(query, batchSize, pageIndex); + totalInvoices = totalInvoices + computeInvoiceMetrics(query, batchSize, pageIndex); + // List invoices = documentService.find(query, batchSize, + // pageIndex); - for (ItemCollection invoice : invoices) { - try { - ItemCollection metricData = metricDebitorService.getMetricByInvoice(invoice); - // Jetzt Rechnung addieren - metricDebitorService.addInvoice(metricData, invoice); - logger.fine("│   │   │   ├── update metric " + InvoiceUtil.getBPId(invoice)); - metricDebitorService.updateMetric(metricData, true); - totalInvoices++; - } catch (PluginException e) { - // invalid invoice - e.g. no dbtr. number - } - } + // for (ItemCollection invoice : invoices) { + // try { + // ItemCollection metricData = metricDebitorService.getMetricByInvoice(invoice); + // // Jetzt Rechnung addieren + // metricDebitorService.addInvoice(metricData, invoice); + // logger.fine("│   │   │   ├── update metric " + InvoiceUtil.getBPId(invoice)); + // metricDebitorService.updateMetric(metricData, true); + // totalInvoices++; + // } catch (PluginException e) { + // // invalid invoice - e.g. no dbtr. number + // } + // } // Fortschritt loggen log("│   │ ├── Processed page " + (pageIndex + 1) + " of " + totalPages + @@ -135,6 +141,37 @@ public class MetricDebitorRestService { } + /** + * Helper method runs in new transaction + * + * @param query + * @param batchSize + * @param pageIndex + * @throws PluginException + * @throws QueryException + */ + @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) + public int computeInvoiceMetrics(String query, int batchSize, int pageIndex) + throws PluginException, QueryException { + + int updates = 0; + List invoices = documentService.find(query, batchSize, pageIndex); + + for (ItemCollection invoice : invoices) { + try { + ItemCollection metricData = metricDebitorService.getMetricByInvoice(invoice); + // Jetzt Rechnung addieren + metricDebitorService.addInvoice(metricData, invoice); + logger.fine("│   │   │   ├── update metric " + InvoiceUtil.getBPId(invoice)); + metricDebitorService.updateMetric(metricData, true); + updates++; + } catch (PluginException e) { + // invalid invoice - e.g. no dbtr. number + } + } + return updates; + } + 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/metrics/MetricDebitorService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorService.java index 01a97b2..1e18e07 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorService.java @@ -12,6 +12,7 @@ import org.eclipse.microprofile.metrics.Metadata; import org.eclipse.microprofile.metrics.MetricRegistry; import org.eclipse.microprofile.metrics.Tag; import org.eclipse.microprofile.metrics.annotation.RegistryScope; +import org.imixs.archive.core.SnapshotService; import org.imixs.workflow.ItemCollection; import org.imixs.workflow.engine.DocumentService; import org.imixs.workflow.engine.ProcessingEvent; @@ -23,6 +24,7 @@ import com.alexanderlogistics.InvoiceUtil; import jakarta.annotation.security.DeclareRoles; import jakarta.annotation.security.RolesAllowed; import jakarta.annotation.security.RunAs; +import jakarta.ejb.Singleton; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Observes; import jakarta.inject.Inject; @@ -46,6 +48,7 @@ import jakarta.inject.Inject; "org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS", "org.imixs.ACCESSLEVEL.MANAGERACCESS" }) @RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS") +@Singleton @ApplicationScoped public class MetricDebitorService { @@ -257,6 +260,7 @@ public class MetricDebitorService { ItemCollection metricData = new ItemCollection(); metricData.setType(TYPE_METRIC_DEBITOR); metricData.setItemValue("name", key); + metricData.setItemValue(SnapshotService.NOSNAPSHOT, true); metricData.setItemValue("bp.id", InvoiceUtil.getBPId(invoice)); metricData.setItemValue("bp.name", InvoiceUtil.getBPName(invoice)); metricData.setItemValue("country", invoice.getItemValueString("invoice.country")); @@ -281,6 +285,7 @@ public class MetricDebitorService { // In Datenbank persistieren if (persist) { + metricData.setItemValue(SnapshotService.NOSNAPSHOT, true); documentService.save(metricData); } @@ -374,16 +379,4 @@ public class MetricDebitorService { } - /** - * Helper Method that refreshes all gauges. The method is called by the - * RestService during a rebuild. - */ - // public void refreshGauges() { - // List keys = getMetricKeys(); - // for (String hashKey : keys) { - // ItemCollection metricData = getMetric(hashKey); - // updateMetric(metricData, false); - // } - // } - } \ No newline at end of file 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 84c4974..3e0ee27 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 @@ -31,6 +31,7 @@ package com.alexanderlogistics.xml; import java.util.List; import java.util.logging.Logger; +import org.imixs.archive.core.SnapshotService; import org.imixs.workflow.ItemCollection; import org.imixs.workflow.engine.DocumentEvent; import org.imixs.workflow.engine.DocumentService; @@ -44,6 +45,9 @@ import org.imixs.workflow.exceptions.QueryException; import com.alexanderlogistics.InvoiceUtil; +import jakarta.annotation.security.DeclareRoles; +import jakarta.annotation.security.RolesAllowed; +import jakarta.annotation.security.RunAs; import jakarta.ejb.EJB; import jakarta.ejb.Stateless; import jakarta.enterprise.event.Observes; @@ -56,10 +60,19 @@ import jakarta.enterprise.event.Observes; * ein und prüft ob der Workflow schon existiert oder ggf. aktualisiert werden * muss. * + * The service set also the flag NOSNAPSHOT=true for the crgosoftcreditor + * document type. * * @author rsoika * */ +@DeclareRoles({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS", + "org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS", + "org.imixs.ACCESSLEVEL.MANAGERACCESS" }) +@RolesAllowed({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS", + "org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS", + "org.imixs.ACCESSLEVEL.MANAGERACCESS" }) +@RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS") @Stateless public class BusinessPartnerImportService { @@ -92,8 +105,16 @@ public class BusinessPartnerImportService { if (event.getEventType() == DocumentEvent.ON_DOCUMENT_SAVE) { // check type! if ("cargosoftkreditor".equals(event.getDocument().getType())) { - logger.info("Verify business partner object...."); + // set NOSNAPSHOT=true + event.getDocument().setItemValue(SnapshotService.NOSNAPSHOT, true); + // run only if we have the model 'businesspartner-*'; + if (modelService.findVersionsByRegEx("(businesspartner*)").size() == 0) { + // no business partner workflow found + return; + } + + logger.info("Verify business partner object...."); ItemCollection importDoc = event.getDocument(); String name = event.getDocument().getItemValueString("name"); diff --git a/workflow/dwc/rechnungsausgang-dwc-1.0.2.bpmn b/workflow/dwc/rechnungsausgang-dwc-1.0.2.bpmn index f856ad9..2a3bafe 100644 --- a/workflow/dwc/rechnungsausgang-dwc-1.0.2.bpmn +++ b/workflow/dwc/rechnungsausgang-dwc-1.0.2.bpmn @@ -1291,7 +1291,7 @@ Wiedervorlage wird um 7 bzw. 5 Tage erhöht - + @@ -1404,7 +1404,7 @@ Wiedervorlage wird um 7 bzw. 5 Tage erhöht - + diff --git a/workflow/sepa-export-manual-3.0.0.bpmn b/workflow/sepa-export-manual-3.0.0.bpmn index b774774..38fdf5e 100644 --- a/workflow/sepa-export-manual-3.0.0.bpmn +++ b/workflow/sepa-export-manual-3.0.0.bpmn @@ -54,10 +54,10 @@ - + - + IntermediateCatchEvent_6 @@ -800,7 +800,7 @@ result.isValid=true; - + @@ -859,7 +859,7 @@ result.isValid=true; - + @@ -1012,7 +1012,7 @@ result.isValid=true; - + @@ -1073,7 +1073,7 @@ result.isValid=true; - + @@ -1106,7 +1106,7 @@ result.isValid=true; - + @@ -1140,9 +1140,9 @@ result.isValid=true; - - - + + + @@ -1158,21 +1158,21 @@ result.isValid=true; - - - + + + - + - + @@ -1188,19 +1188,19 @@ result.isValid=true; - + - + - + - - - + + + From dfa089ba3124a5cdd98bf250da0d88cc02097783 Mon Sep 17 00:00:00 2001 From: Ralph Soika Date: Wed, 12 Feb 2025 21:04:02 +0100 Subject: [PATCH 04/12] BusinessPartner Suche --- .../BusinessPartnerController.java | 161 +- .../BusinessPartnerService.java | 106 + .../xml/BusinessPartnerImportService.java | 96 +- .../sub_businesspartner_ibanlist.xhtml | 18 +- .../alexander/businesspartner_search.xhtml | 79 + workflow/businesspartner-de-1.0.0.bpmn | 4 +- .../rechnungseingang-de-1.2.40-debug_BP.bpmn | 8495 +++++++++++++++++ 7 files changed, 8936 insertions(+), 23 deletions(-) create mode 100644 office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerService.java create mode 100644 office-alexander-logistics-app/src/main/webapp/pages/workitems/parts/alexander/businesspartner_search.xhtml create mode 100644 workflow/rechnungseingang-de-1.2.40-debug_BP.bpmn diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerController.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerController.java index 390439f..6951468 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerController.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerController.java @@ -23,45 +23,121 @@ *******************************************************************************/ package com.alexanderlogistics; +import java.io.IOException; import java.io.Serializable; +import java.io.StringWriter; +import java.io.Writer; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.imixs.workflow.ItemCollection; import org.imixs.workflow.engine.DocumentService; import org.imixs.workflow.exceptions.AccessDeniedException; import org.imixs.workflow.faces.data.WorkflowEvent; -import jakarta.ejb.EJB; +import jakarta.enterprise.context.ConversationScoped; import jakarta.enterprise.event.Observes; -import jakarta.faces.view.ViewScoped; +import jakarta.faces.context.FacesContext; +import jakarta.inject.Inject; import jakarta.inject.Named; +import jakarta.json.Json; +import jakarta.json.JsonObject; +import jakarta.json.JsonObjectBuilder; /** - * Der BusinessPartnerController stellt methoden für die BusinessPartner Forms - * bereit. + * Der BusinessPartnerController stellt Methoden für die BusinessPartner Forms + * sowie für die das Suche-Widget 'businesspartner_search' bereit. * * * @author rsoika * @version 1.0 */ - @Named -@ViewScoped +@ConversationScoped public class BusinessPartnerController implements Serializable { private static final long serialVersionUID = 1L; protected List ibanList = null; - @EJB + @Inject DocumentService documentService; + @Inject + BusinessPartnerService businessPartnerService; + + private List searchResult = null; + private static Logger logger = Logger.getLogger(BusinessPartnerController.class.getName()); + /** + * This method searches a text phrase within the list of DATEV kreditoren + *

+ * JSF Integration: + * + * {@code + + * } + */ + public void search(String regexPattern) { + List resultList = null; + searchResult = new ArrayList(); + // get the param from faces context.... + FacesContext fc = FacesContext.getCurrentInstance(); + String phrase = fc.getExternalContext().getRequestParameterMap().get("phrase"); + if (phrase == null) { + return; + } + + logger.fine("search prase '" + phrase + "'"); + if (phrase == null || phrase.length() < 2) { + return; + } + logger.fine("search for=" + phrase); + resultList = businessPartnerService.search(phrase); + logger.info("found " + resultList.size() + " businesspartners"); + // Compile the regex pattern + logger.fine("regex=" + regexPattern); + Pattern pattern = null; + if (regexPattern != null && !regexPattern.isEmpty()) { + pattern = Pattern.compile(regexPattern); + } + + for (ItemCollection businessPartner : resultList) { + String name = businessPartner.getItemValueString("name"); + // Prüfen, ob der name der Regex entspricht + if (pattern != null) { + Matcher matcher = pattern.matcher(name); + if (!matcher.matches()) { + // Kein passendes Konto + continue; + } + } + String display = businessPartner.getItemValueString("$workflowsummary"); + ; + display = display.replace("\"", ""); + display = display.replace("'", ""); + searchResult.add(businessPartner); + } + } + + /** + * Die Resultliste wird als eine Liste einen Arrays zurückgegeben. Der Erste + * Eintrag + * + * @return + */ + public List getSearchResult() { + return searchResult; + } + /** * WorkflowEvent listener to convert the IBAN child list embeded HashMaps into * ItemCollections and reconvert them before processing @@ -112,10 +188,10 @@ public class BusinessPartnerController implements Serializable { * * @param name - name of dbtr */ - public void removeIBAN(String name) { - if (name != null && ibanList != null) { + public void removeIBAN(String id) { + if (id != null && ibanList != null) { for (ItemCollection cdtr : ibanList) { - if (name.equals(cdtr.getItemValueString("name"))) { + if (id.equals(cdtr.getItemValueString("id"))) { ibanList.remove(cdtr); break; } @@ -155,4 +231,69 @@ public class BusinessPartnerController implements Serializable { } + /** + * Liefert die ChildItems mit den IBAN daten + * + * @param workitem + * @return + */ + public List getIBANList(ItemCollection workitem) { + List result = new ArrayList<>(); + List mapOrderItems = workitem.getItemValue("partner.iban.list"); + Iterator var4 = mapOrderItems.iterator(); + while (var4.hasNext()) { + Object mapOderItem = var4.next(); + if (mapOderItem instanceof Map) { + ItemCollection itemCol = new ItemCollection((Map) mapOderItem); + result.add(itemCol); + } + } + return result; + } + + /** + * Hilfsmethode die eine JSON Struktur mit allen relevanten Daten für einen + * BusinessPartner erzeugt. + * + * @return + */ + private String buildJsonData(ItemCollection businessPartner) { + JsonObjectBuilder objectBuilder = Json.createObjectBuilder(). // + add("name", jsonVal(businessPartner.getItemValueString("name"))). // + add("cdtr.number", jsonVal(businessPartner.getItemValueString("cdtr.number"))). // + add("dbtr.number", jsonVal(businessPartner.getItemValueString("dbtr.number"))). // + add("name", jsonVal(businessPartner.getItemValueString("partner.name"))); + + // get iban list + // {iban=[Dxxxxxx1], name=[Bank 1], id=[bank1], bic=[CITIDEFF]} + ibanList = getIBANList(businessPartner); + int i = 1; + for (ItemCollection iban : ibanList) { + objectBuilder.add("iban" + 1, jsonVal(iban.getItemValueString("iban"))). // + add("bic" + 1, jsonVal(iban.getItemValueString("bic")));// + } + + JsonObject jsonObject = objectBuilder.build(); + + String jsonString = "{}"; + try (Writer writer = new StringWriter()) { + Json.createWriter(writer).write(jsonObject); + jsonString = writer.toString(); + } catch (IOException e) { + logger.warning("Unable to build json structure"); + } + return jsonString; + } + + /** + * Helper method to remove " and ' characters - causing problems + * + * @param val + * @return + */ + public static String jsonVal(String val) { + val = val.replace("\"", ""); + val = val.replace("'", ""); + return val; + } } 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 new file mode 100644 index 0000000..70b8571 --- /dev/null +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerService.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * 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; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.logging.Logger; + +import org.imixs.workflow.ItemCollection; +import org.imixs.workflow.ItemCollectionComparator; +import org.imixs.workflow.engine.DocumentService; +import org.imixs.workflow.engine.index.SchemaService; + +import jakarta.annotation.security.DeclareRoles; +import jakarta.annotation.security.RolesAllowed; +import jakarta.annotation.security.RunAs; +import jakarta.ejb.Singleton; +import jakarta.inject.Inject; + +/** + * Der BusinessPartnerService stellt Methoden für den Zugriff auf Business + * Partner bereit. + * + * @author rsoika + * + */ + +@DeclareRoles({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS", + "org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS", + "org.imixs.ACCESSLEVEL.MANAGERACCESS" }) +@RolesAllowed({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS", + "org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS", + "org.imixs.ACCESSLEVEL.MANAGERACCESS" }) +@Singleton +@RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS") +public class BusinessPartnerService { + + public static final int MAX_SEARCH_RESULT = 100; + + private static Logger logger = Logger.getLogger(BusinessPartnerService.class.getName()); + + @Inject + DocumentService documentService; + + @Inject + SchemaService schemaService; + + /** + * Diese Methode sucht Business Partner anhand einer Suchphrase + * + * @param phrase + * - search phrase + * @return - list of matching business partners + */ + public List search(String phrase) { + + List searchResult = new ArrayList(); + if (phrase == null || phrase.isEmpty()) { + return searchResult; + } + + try { + phrase = phrase.trim(); + // phrase = LuceneSearchService.escapeSearchTerm(phrase); + phrase = schemaService.normalizeSearchTerm(phrase); + String sQuery = "(type:\"workitem\") AND ($modelversion:businesspartner-*)"; + sQuery += " AND (" + phrase + "*)"; + + logger.finest("SearchQuery= " + sQuery); + + searchResult = documentService.find(sQuery, MAX_SEARCH_RESULT, 0); + } catch (Exception e) { + logger.warning(" lucene error - " + e.getMessage()); + } + + // sort by txtname.. + Collections.sort(searchResult, new ItemCollectionComparator("$workflowsummary", true)); + return searchResult; + } +} 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 3e0ee27..a91aec8 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 @@ -28,7 +28,10 @@ package com.alexanderlogistics.xml; +import java.util.ArrayList; +import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.logging.Logger; import org.imixs.archive.core.SnapshotService; @@ -44,6 +47,7 @@ import org.imixs.workflow.exceptions.ProcessingErrorException; import org.imixs.workflow.exceptions.QueryException; import com.alexanderlogistics.InvoiceUtil; +import com.alexanderlogistics.KreditorDebitorService; import jakarta.annotation.security.DeclareRoles; import jakarta.annotation.security.RolesAllowed; @@ -60,6 +64,9 @@ import jakarta.enterprise.event.Observes; * ein und prüft ob der Workflow schon existiert oder ggf. aktualisiert werden * muss. * + * Der Service migriert auch die alten zusätzlichen IBAN/BIC felder wenn der + * Business partner erstmals neu angelegt wird. + * * The service set also the flag NOSNAPSHOT=true for the crgosoftcreditor * document type. * @@ -124,7 +131,7 @@ public class BusinessPartnerImportService { // Prüfen ob es diesen partner schon gibt? ItemCollection businesspartner = lookupBusinessPartner(partnerID); if (businesspartner == null) { - businesspartner = createBusinessPartner(); + businesspartner = createBusinessPartner(partnerID); } ItemCollection oldBussinessPartnerItemColl = (ItemCollection) businesspartner.clone(); @@ -168,12 +175,97 @@ public class BusinessPartnerImportService { * * @return */ - private ItemCollection createBusinessPartner() { + private ItemCollection createBusinessPartner(String partnerID) { ItemCollection businessPartner = new ItemCollection(); businessPartner.setType("workitem"); businessPartner.setWorkflowGroup("Business Partner"); businessPartner.task(1000); + // Hier migrieren wir jetzt auch die alten IBAN/BIC Daten + try { + String cargoShortID = partnerID.substring(2); + String query = "(type:" + KreditorDebitorService.TYPE_CARGOSOFTKREDITOR + ")"; + query = query + " AND ( name:K7" + cargoShortID + " OR name:D1" + cargoShortID + " )"; + + List oldList = documentService.find(query, 2, 0); + + // es können 2 Datensätze ankommen + + // Items: + // cdtr.mail (Liste) + // cdtr.creditperiod + // cdtr.iban , cdtr.iban2 - 4 + // cdtr.bic , cdtr.bic2 - 4 + List ibanList = new ArrayList<>(); + List bicList = new ArrayList<>(); + for (ItemCollection oldData : oldList) { + if (!oldData.getItemValueString("cdtr.mail").isEmpty()) { + businessPartner.setItemValue("cdtr.mail", oldData.getItemValue("cdtr.mail")); + } + if (!oldData.getItemValueString("cdtr.creditperiod").isEmpty()) { + businessPartner.setItemValue("cdtr.creditperiod", oldData.getItemValue("cdtr.creditperiod")); + } + + if (!oldData.getItemValueString("cdtr.iban").isEmpty()) { + ibanList.add(oldData.getItemValueString("cdtr.iban")); + } + if (!oldData.getItemValueString("cdtr.bic").isEmpty()) { + bicList.add(oldData.getItemValueString("cdtr.bic")); + } + + if (!oldData.getItemValueString("cdtr.iban2").isEmpty()) { + ibanList.add(oldData.getItemValueString("cdtr.iban2")); + } + if (!oldData.getItemValueString("cdtr.bic2").isEmpty()) { + bicList.add(oldData.getItemValueString("cdtr.bic2")); + } + if (!oldData.getItemValueString("cdtr.iban3").isEmpty()) { + ibanList.add(oldData.getItemValueString("cdtr.iban3")); + } + if (!oldData.getItemValueString("cdtr.bic3").isEmpty()) { + bicList.add(oldData.getItemValueString("cdtr.bic3")); + } + if (!oldData.getItemValueString("cdtr.iban4").isEmpty()) { + ibanList.add(oldData.getItemValueString("cdtr.iban4")); + } + if (!oldData.getItemValueString("cdtr.bic4").isEmpty()) { + bicList.add(oldData.getItemValueString("cdtr.bic4")); + } + + } + + // build partner.iban.list + if (ibanList.size() > 0) { + List bankList = new ArrayList<>(); + for (int i = 0; i < ibanList.size(); i++) { + String iban = ibanList.get(i); + String bic = ""; + if (bicList.size() > i) { + bic = bicList.get(i); + } + ItemCollection bank = new ItemCollection(); + // {cdtr.iban=[A], name=[cdtr1], cdtr.name=[a], cdtr.bic=[a]} + bank.setItemValue("id", "bank" + i + 1); + bank.setItemValue("name", "Bank " + i + 1); + bank.setItemValue("iban", iban); + bank.setItemValue("bic", bic); + + bankList.add(bank); + } + Iterator var3 = bankList.iterator(); + List mapOrderItems = new ArrayList(); + while (var3.hasNext()) { + ItemCollection orderItem = (ItemCollection) var3.next(); + mapOrderItems.add(orderItem.getAllItems()); + } + businessPartner.setItemValue("partner.iban.list", mapOrderItems); + } + + } catch (QueryException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return businessPartner; } diff --git a/office-alexander-logistics-app/src/main/webapp/pages/workitems/forms/alexander/sub_businesspartner_ibanlist.xhtml b/office-alexander-logistics-app/src/main/webapp/pages/workitems/forms/alexander/sub_businesspartner_ibanlist.xhtml index bee7855..54bf918 100644 --- a/office-alexander-logistics-app/src/main/webapp/pages/workitems/forms/alexander/sub_businesspartner_ibanlist.xhtml +++ b/office-alexander-logistics-app/src/main/webapp/pages/workitems/forms/alexander/sub_businesspartner_ibanlist.xhtml @@ -10,35 +10,35 @@ - - + + - +
NameBank - *ID + *Bank IBAN BIC
- + - - - + + actionListener="#{businessPartnerController.removeIBAN(bank.item['id'])}"> diff --git a/office-alexander-logistics-app/src/main/webapp/pages/workitems/parts/alexander/businesspartner_search.xhtml b/office-alexander-logistics-app/src/main/webapp/pages/workitems/parts/alexander/businesspartner_search.xhtml new file mode 100644 index 0000000..292190f --- /dev/null +++ b/office-alexander-logistics-app/src/main/webapp/pages/workitems/parts/alexander/businesspartner_search.xhtml @@ -0,0 +1,79 @@ + + + + + + + + +
+ #{workitem.item[item_konto_name]} + + + + + + + +
+ + + +
+
+
+ +
+ + + + + + +
\ No newline at end of file diff --git a/workflow/businesspartner-de-1.0.0.bpmn b/workflow/businesspartner-de-1.0.0.bpmn index 9fc7a62..a2f0880 100644 --- a/workflow/businesspartner-de-1.0.0.bpmn +++ b/workflow/businesspartner-de-1.0.0.bpmn @@ -164,13 +164,13 @@ - + - + diff --git a/workflow/rechnungseingang-de-1.2.40-debug_BP.bpmn b/workflow/rechnungseingang-de-1.2.40-debug_BP.bpmn new file mode 100644 index 0000000..f556232 --- /dev/null +++ b/workflow/rechnungseingang-de-1.2.40-debug_BP.bpmn @@ -0,0 +1,8495 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + Task_2 + ExclusiveGateway_1 + StartEvent_1 + IntermediateCatchEvent_6 + IntermediateCatchEvent_27 + EventBasedGateway_1 + Task_10 + EndEvent_5 + IntermediateCatchEvent_40 + IntermediateThrowEvent_4 + IntermediateCatchEvent_49 + IntermediateCatchEvent_19 + IntermediateThrowEvent_3 + IntermediateCatchEvent_31 + IntermediateCatchEvent_13 + Task_14 + EventBasedGateway_3 + IntermediateCatchEvent_30 + Task_13 + EndEvent_4 + IntermediateCatchEvent_56 + IntermediateThrowEvent_9 + IntermediateCatchEvent_60 + IntermediateCatchEvent_3 + ExclusiveGateway_9 + IntermediateCatchEvent_5000-20 + + DataObject_5 + DataObject_2 + TextAnnotation_4 + + + + Task_1 + IntermediateCatchEvent_9 + IntermediateCatchEvent_10 + IntermediateCatchEvent_11 + IntermediateThrowEvent_5 + IntermediateCatchEvent_1 + IntermediateCatchEvent_45 + IntermediateCatchEvent_61 + IntermediateCatchEvent_64 + DataObject_7 + event_GwjqFw + + + + Task_5005 + IntermediateCatchEvent_5005-20 + IntermediateCatchEvent_5005-30 + IntermediateCatchEvent_2 + IntermediateCatchEvent_36 + IntermediateCatchEvent_42 + IntermediateCatchEvent_55 + EventBasedGateway_2 + ExclusiveGateway_6 + ExclusiveGateway_4 + TextAnnotation_1 + TextAnnotation_3 + Task_4 + Task_12 + IntermediateCatchEvent_5005-10 + IntermediateCatchEvent_12 + EndEvent_1 + IntermediateCatchEvent_21 + IntermediateThrowEvent_2 + IntermediateCatchEvent_25 + IntermediateCatchEvent_26 + IntermediateCatchEvent_28 + IntermediateCatchEvent_32 + IntermediateThrowEvent_8 + ExclusiveGateway_7 + ExclusiveGateway_10 + event_bBR4QQ + event_NvdVrw + DataObject_1 + + + + IntermediateCatchEvent_44 + IntermediateCatchEvent_53 + IntermediateCatchEvent_54 + IntermediateThrowEvent_7 + IntermediateCatchEvent_50 + DataObject_6 + Task_17 + Task_18 + IntermediateCatchEvent_43 + IntermediateCatchEvent_47 + IntermediateCatchEvent_51 + IntermediateThrowEvent_6 + IntermediateCatchEvent_52 + ExclusiveGateway_3 + EventBasedGateway_5 + EventBasedGateway_6 + ExclusiveGateway_8 + + + EndEvent_3 + Task_5000 + IntermediateCatchEvent_5000-10 + IntermediateCatchEvent_33 + IntermediateCatchEvent_38 + IntermediateCatchEvent_15 + EndEvent_6 + EventBasedGateway_4 + Task_16 + IntermediateCatchEvent_37 + IntermediateCatchEvent_24 + Task_8 + IntermediateCatchEvent_16 + Task_9 + IntermediateCatchEvent_7 + ExclusiveGateway_2 + IntermediateCatchEvent_29 + IntermediateCatchEvent_39 + IntermediateCatchEvent_20 + Task_3 + IntermediateCatchEvent_17 + ExclusiveGateway_5 + IntermediateCatchEvent_35 + IntermediateThrowEvent_1 + IntermediateCatchEvent_58 + IntermediateCatchEvent_22 + Task_5 + Task_15 + IntermediateCatchEvent_48 + Task_11 + IntermediateCatchEvent_4 + IntermediateCatchEvent_14 + IntermediateCatchEvent_57 + Task_7 + IntermediateCatchEvent_18 + EndEvent_2 + IntermediateCatchEvent_5 + Task_6 + IntermediateCatchEvent_46 + Task_19 + IntermediateCatchEvent_63 + IntermediateCatchEvent_34 + IntermediateCatchEvent_59 + IntermediateCatchEvent_41 + IntermediateCatchEvent_65 + IntermediateCatchEvent_66 + + event_TyXP5Q + DataObject_3 + IntermediateCatchEvent_62 + TextAnnotation_5 + TextAnnotation_2 + event_e0gqXQ + gateway_ApXfeg + + + + + + + + + txtlastcomment +numsequencenumber_sub]]> + + + + + + + + + _imgnumsequencenumber: cdtr.name invoice.number (invoice.currency invoice.total) space.name]]> + + + true + + + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_31 + SequenceFlow_1 + SequenceFlow_123 + SequenceFlow_32 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + false + + + + SequenceFlow_31 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home + + stop + false + + + start + false +]]> + + + + + + + + + space.name]]> + + + false + + + + + + + SequenceFlow_46 + SequenceFlow_122 + + + SequenceFlow_0 + SequenceFlow_21 + SequenceFlow_125 + SequenceFlow_33 + + + + + + + SequenceFlow_0 + + + + + + + + + + + + txtlastcomment]]> + + + + + + + + + _imgnumsequencenumber: cdtr.name invoice.number (invoice.currency invoice.total) space.name]]> + + + true + + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus +Zollkurse +]]> + SequenceFlow_38 + SequenceFlow_83 + SequenceFlow_106 + SequenceFlow_104 + SequenceFlow_121 + SequenceFlow_134 + SequenceFlow_37 + SequenceFlow_10 + sequenceFlow_wq0Y0w + + + + + + + + + + + + + + _subject]]> + + + + + + + + + _amount (Brutto € _amount_brutto) +_description]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +space.name + +cargosoft-export-1.0 +1000 +100 +(?!txtworkflowhistory)(^[a-zA-Z]|^_) + + + stop + false +]]> + + + + + + + + + + + + false + + + 0 && (parseFloat(a)!=parseFloat(b)) ) { + result.isValid=false; + result.errorMessage="Der Rechnungsbetrag " + a+ " stimmt nicht mit dem Positionsbetrag " + b + " überein."; + }]]> + + + + SequenceFlow_8 + SequenceFlow_111 + SequenceFlow_51 + + + DataOutput_1 + + + DataOutput_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +false]]> + + + + + + + + + + + + false + + + + SequenceFlow_38 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +space.name + +false]]> + + + + + + + + + + + + false + + + + + + + SequenceFlow_10 + SequenceFlow_97 + SequenceFlow_110 + + + SequenceFlow_37 + SequenceFlow_8 + SequenceFlow_54 + SequenceFlow_96 + + + + + + + + + + + + + + + txtlastcomment]]> + + + + + + + + + cdtr.name invoice.number (invoice.currency invoice.total) space.name]]> + + + true + + + + + + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_33 + SequenceFlow_95 + SequenceFlow_49 + SequenceFlow_20 + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +suggest +false + + start + false +]]> + + + + + + + + + + + + false + + + + + SequenceFlow_22 + SequenceFlow_77 + + + + + + + + txtlastcomment +numsequencenumber_sub]]> + + + + + + + + + _imgnumsequencenumber: cdtr.name invoice.number (invoice.currency invoice.total) space.name]]> + + + true + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_56 + SequenceFlow_15 + SequenceFlow_24 + SequenceFlow_55 + SequenceFlow_50 + SequenceFlow_81 + SequenceFlow_88 + sequenceFlow_dbshCw + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +false]]> + + + + + + + + + + + + false + + + + + + + SequenceFlow_29 + SequenceFlow_2 + + + SequenceFlow_32 + SequenceFlow_29 + SequenceFlow_30 + + + + + + + + + + + + _subject]]> + + + + + + + + + _amount (Brutto € _amount_brutto) +_description]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home]]> + + + + + + + + + + + + false + + + + + + + SequenceFlow_30 + SequenceFlow_72 + SequenceFlow_67 + + + + + + + + + + + + + + + + + txtlastcomment +numsequencenumber_sub]]> + + + + + + + + + _imgnumsequencenumber: cdtr.name invoice.number (invoice.currency invoice.total) space.name]]> + + + true + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_44 + SequenceFlow_90 + SequenceFlow_47 + sequenceFlow_ucyuIw + sequenceFlow_SvEjiw + + + SequenceFlow_47 + + + + + + + + + + + + + + + + + + _subject]]> + + + + + + + + + _amount (Brutto € _amount_brutto) +_description]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home]]> + + + + + + + + + + + + false + + + + + + + SequenceFlow_55 + SequenceFlow_12 + + + + + + SequenceFlow_18 + SequenceFlow_1 + SequenceFlow_68 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + SequenceFlow_6 + SequenceFlow_18 + + + + SequenceFlow_51 + SequenceFlow_6 + + + + + + + SequenceFlow_126 + + + + + + + + + + + SequenceFlow_12 + SequenceFlow_53 + SequenceFlow_19 + SequenceFlow_25 + + + + + + sepa-export-manual-3.0 +1000 +payment.type +70 +20]]> + + + + + + + + + SequenceFlow_19 + SequenceFlow_23 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Rechnungscontrolling +]]> + + + + + + + + + + + + false + + + + SequenceFlow_21 + + + + + + + + + + + + + txtlastcomment]]> + + + + + + + + + _imgnumsequencenumber: cdtr.name invoice.number (invoice.currency invoice.total) space.name]]> + + + true + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_26 + SequenceFlow_11 + SequenceFlow_118 + SequenceFlow_132 + SequenceFlow_28 + SequenceFlow_36 + SequenceFlow_127 + sequenceFlow_Dy0E0A + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + false + + + + SequenceFlow_26 + + + + + + + + + + + + + + _subject]]> + + + + + + + + + _amount (Brutto € _amount_brutto) +_description]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +space.name + + stop + false + + + start + false +]]> + + + + + + + + + $owner]]> + + + false + + + + + + + SequenceFlow_28 + SequenceFlow_105 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +space.name +false + + stop + false +]]> + + + + + + + + + + + + true + + + + + + + + + + + SequenceFlow_36 + SequenceFlow_35 + + + SequenceFlow_35 + + + + + + + + + + + SequenceFlow_39 + + + + + + + + + + txtlastcomment]]> + + + + + + + + + numsequencenumber: cdtr.name invoice.number (invoice.currency invoice.total) space.name]]> + + + true + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_39 + SequenceFlow_59 + SequenceFlow_40 + + + SequenceFlow_40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home]]> + + + + + + + + + space.name]]> + + + false + + + + SequenceFlow_11 + + + + + + + + + + + txtlastcomment +numsequencenumber_sub]]> + + + + + + + + + _imgnumsequencenumber: cdtr.name invoice.number (invoice.currency invoice.total) space.name]]> + + + true + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_23 + SequenceFlow_41 + SequenceFlow_14 + sequenceFlow_swRy5A + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + SequenceFlow_14 + SequenceFlow_42 + + + + + + + + + + + + + + + + txtlastcomment +numsequencenumber_sub]]> + + + + + + + + + _imgnumsequencenumber: cdtr.name invoice.number (invoice.currency invoice.total) space.name]]> + + + true + + + + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_42 + SequenceFlow_3 + SequenceFlow_43 + + + + + + + + + + + _subject]]> + + + + + + + + + _amount (Brutto € _amount_brutto) +_description]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home]]> + + + + + + + + + + + + false + + + + SequenceFlow_43 + SequenceFlow_5 + SequenceFlow_44 + + + + + + + + + + + + + + + + + _subject]]> + + + + + + + + + _amount (Brutto € _amount_brutto) +_description]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home]]> + + + + + + + + + + + + false + + + + + + + + + + + + + + + SequenceFlow_50 + SequenceFlow_53 + + + + + + + + + + + + + + + + cdtr.name invoice.number]]> + + + + + + + + + cdtr.name +Rechnungsnummer: invoice.number +Betrag: invoice.total invoice.currency + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +false]]> + + + + + + + + + + + + false + + + + + + + + + + SequenceFlow_54 + SequenceFlow_7 + + + + + + + SequenceFlow_63 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + txtlastcomment +numsequencenumber_sub]]> + + + + + + + + + numsequencenumber: cdtr.name invoice.number (invoice.currency invoice.total) space.name]]> + + + true + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_17 + SequenceFlow_60 + SequenceFlow_9 + + + SequenceFlow_9 + + + + + + + SequenceFlow_17 + + + + + + + + SequenceFlow_20 + SequenceFlow_65 + SequenceFlow_71 + SequenceFlow_22 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +false +Wollen Sie den Vorgang wirklich löschen? + + stop + false + +cancel]]> + + + + + + + + + + + + false + + + + + + + SequenceFlow_49 + SequenceFlow_76 + SequenceFlow_48 + + + SequenceFlow_48 + + + + + + + + + + + + + + + + txtlastcomment +numsequencenumber_sub]]> + + + + + + + + + _imgnumsequencenumber: cdtr.name invoice.number (invoice.currency invoice.total) space.name]]> + + + true + + + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_2 + SequenceFlow_78 + SequenceFlow_89 + SequenceFlow_72 + SequenceFlow_86 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + false + + + + SequenceFlow_3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +false]]> + + + + + + + + + + + + false + + + + SequenceFlow_60 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +false]]> + + + + + + + + + + + + false + + + + SequenceFlow_59 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + suggest +false + + gutschriftabgleich-de-1.0 + 5001 + 980 + ]]> + + + + + + + + + + + + false + + + + + + + SequenceFlow_65 + SequenceFlow_69 + + + + + + + + + + + + + + + + + + + txtlastcomment]]> + + + + + + + + + cdtr.name invoice.number (invoice.currency invoice.total) space.name]]> + + + true + + + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_69 + SequenceFlow_70 + + + + + + SequenceFlow_70 + + + + + + + + + + + + txtlastcomment +numsequencenumber_sub]]> + + + + + + + + + numsequencenumber: cdtr.name invoice.number (invoice.currency invoice.total) space.name]]> + + + true + + + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_4 + SequenceFlow_34 + SequenceFlow_119 + SequenceFlow_5 + + + + workitem['payment.type'][0]=="no_sepa" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + txtlastcomment]]> + + + + + + + + + _imgnumsequencenumber: cdtr.name invoice.number (invoice.currency invoice.total) space.name]]> + + + true + + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_7 + SequenceFlow_62 + SequenceFlow_61 + SequenceFlow_73 + + + + + + + + + + + cdtr.name invoice.number]]> + + + + + + + + + cdtr.name +Rechnungsnummer: invoice.number +Betrag: invoice.total invoice.currency + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +false +Wollen Sie den Vorgang wirklich archivieren? +cancel]]> + + + + + + + + + + + + false + + + + + + + + SequenceFlow_61 + SequenceFlow_63 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + false + + + + SequenceFlow_62 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + false + + + + SequenceFlow_56 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + suggest +false + + rechnungseingang-sachrechnung-de-1.0 + 5001 + 980 + ]]> + + + + + + + + + + + + false + + + + + + + SequenceFlow_71 + SequenceFlow_64 + + + + + + + + + txtlastcomment]]> + + + + + + + + + cdtr.name invoice.number (invoice.currency invoice.total) space.name]]> + + + true + + + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_64 + SequenceFlow_66 + + + SequenceFlow_66 + + + + + + + + + + + + + + + + + + txtlastcomment]]> + + + + + + + + + _imgcdtr.name invoice.number (invoice.currency invoice.total) space.name]]> + + + true + + + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_74 + SequenceFlow_77 + SequenceFlow_126 + SequenceFlow_46 + SequenceFlow_85 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + false + + + + + SequenceFlow_74 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +space.name +false]]> + + + + + + + + + + + + false + + + + + + + SequenceFlow_73 + SequenceFlow_75 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + false + + + + SequenceFlow_78 + + + + + + + + + + + txtlastcomment]]> + + + + + + + + + _imgnumsequencenumber: cdtr.name invoice.number (invoice.currency invoice.total) space.name]]> + + + true + + + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_79 + SequenceFlow_124 + SequenceFlow_131 + SequenceFlow_57 + + + + + + + + + + + _subject]]> + + + + + + + + + _amount (Brutto € _amount_brutto) +_description]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home]]> + + + + + + + + + + + + false + + + + SequenceFlow_57 + SequenceFlow_58 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + false + + + + SequenceFlow_79 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +false +Wollen Sie den Vorgang wirklich löschen?]]> + + + + + + + + + + + + false + + + + + + + SequenceFlow_81 + SequenceFlow_80 + + + SequenceFlow_80 + + + + + + + + + + + SequenceFlow_82 + + + + + + + + + txtlastcomment +numsequencenumber_sub]]> + + + + + + + + + numsequencenumber: cdtr.name invoice.number (invoice.currency invoice.total) space.name]]> + + + true + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_84 + SequenceFlow_87 + SequenceFlow_82 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +false]]> + + + + + + + + + + + + false + + + + SequenceFlow_84 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +false]]> + + + + + + + + + + + + false + + + + + + + SequenceFlow_86 + SequenceFlow_87 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +false]]> + + + + + + + + + + + + false + + + + + + + SequenceFlow_88 + SequenceFlow_89 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +false +]]> + + + + + + + + + + + + false + + + + + + + SequenceFlow_83 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +false +Wollen Sie den Vorgang wirklich archivieren? + + stop + false + +cancel]]> + + + + + + + + + + + + false + + + + + + + SequenceFlow_16 + SequenceFlow_13 + + + SequenceFlow_13 + + + + + + + + + + + SequenceFlow_85 + SequenceFlow_76 + SequenceFlow_16 + SequenceFlow_116 + + + + + + + SequenceFlow_90 + + + + + + + + + + + + + + + + _subject]]> + + + + + + + + + _amount (Brutto € _amount_brutto) +_description]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +false +space.name]]> + + + + + + + + + $owner]]> + + + false + + + + + + + SequenceFlow_96 + SequenceFlow_120 + + + + + + + + txtlastcomment]]> + + + + + + + + + _imgnumsequencenumber: cdtr.name invoice.number (invoice.currency invoice.total) space.name]]> + + + true + + + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_91 + SequenceFlow_92 + SequenceFlow_94 + SequenceFlow_117 + SequenceFlow_100 + SequenceFlow_101 + SequenceFlow_93 + + + + + + + + + + + txtlastcomment]]> + + + + + + + + + _imgnumsequencenumber: cdtr.name invoice.number (invoice.currency invoice.total) space.name]]> + + + true + + + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_102 + SequenceFlow_114 + SequenceFlow_109 + SequenceFlow_112 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + false + + + + SequenceFlow_94 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +false +]]> + + + + + + + + + + + + false + + + + + + + SequenceFlow_92 + + + + + + + + + + + + SequenceFlow_101 + SequenceFlow_98 + SequenceFlow_111 + + + + + + + + + cdtr.name invoice.number]]> + + + + + + + + + cdtr.name +Rechnungsnummer: invoice.number +Betrag: invoice.total invoice.currency + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +false]]> + + + + + + + + + + + + false + + + + + + + + + + SequenceFlow_98 + SequenceFlow_102 + + + + + + + + + + + + + + + + + + cdtr.name invoice.number]]> + + + + + + + + + cdtr.name +Rechnungsnummer: invoice.number +Betrag: invoice.total invoice.currency + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +false +Wollen Sie den Vorgang wirklich archivieren? +cancel]]> + + + + + + + + + + + + false + + + + + + + + SequenceFlow_109 + SequenceFlow_108 + + + + SequenceFlow_108 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home +space.name +false]]> + + + + + + + + + + + + false + + + + + + + SequenceFlow_112 + SequenceFlow_113 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + false + + + + SequenceFlow_114 + + + + + + + + + + + + + + + + + + + + space.name)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + Message-Eskalation +]]> + + + + + + + + + SequenceFlow_99 + SequenceFlow_103 + + + + + + + + SequenceFlow_93 + SequenceFlow_97 + SequenceFlow_99 + + + + + + + + + + + + + SequenceFlow_105 + SequenceFlow_106 + SequenceFlow_107 + + + + + + + + + + workitem['sb.assist'] + && ( workitem['sb.assist'][0]!="-" && workitem['sb.assist'][0]!="") + + + + SequenceFlow_103 + + + + + SequenceFlow_110 + + + + + + + + SequenceFlow_113 + SequenceFlow_107 + SequenceFlow_117 + + + + + + + SequenceFlow_118 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + false + + + + SequenceFlow_34 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + false + + + + SequenceFlow_41 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + posteingang-de-2.0 + 120 + 1 +]]> + + + + + + + + + + + + false + + + SequenceFlow_95 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false +]]> + + + + + + + + + + + + false + + + + + + + SequenceFlow_100 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false +]]> + + + + + + + + + + + + false + + + + + + + SequenceFlow_104 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + stop + false +]]> + + + + + + + + + + + + false + + + + + + + SequenceFlow_116 + SequenceFlow_115 + + + SequenceFlow_115 + + + + + + + + + + + + + + + + + + + SequenceFlow_119 + + + DataOutput_2 + + + DataOutput_2 + + + + + + + + + SequenceFlow_120 + SequenceFlow_91 + SequenceFlow_121 + + + + + + + + + + + + + + SequenceFlow_67 + SequenceFlow_4 + SequenceFlow_15 + + + + + + + + + + + + + + + + + + SequenceFlow_123 + + + DataOutput_3 + + + DataOutput_3 + + + + + + + + + + + + + + + + + SequenceFlow_124 + + + DataOutput_4 + + + DataOutput_4 + + + + + + + + + SequenceFlow_125 + + + + + + + + + + + + + + + + _subject]]> + + + + + + + + + _amount (Brutto € _amount_brutto) +_description]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +$editor +space.name + + stop + false + + + start + false +]]> + + + + + + + + + $owner]]> + + + true + + + + + + + + + + + SequenceFlow_127 + SequenceFlow_128 + + + + + + + + + + + + + + txtlastcomment]]> + + + + + + + + + _imgnumsequencenumber: cdtr.name invoice.number (invoice.currency invoice.total) space.name]]> + + + true + + + + + + + + + + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_68 + SequenceFlow_129 + SequenceFlow_133 + SequenceFlow_130 + + + + + + + + + + + _subject]]> + + + + + + + + + _amount (Brutto € _amount_brutto) +_description]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home]]> + + + + + + + + + + + + false + + + + + + + SequenceFlow_130 + SequenceFlow_131 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + false + + + + SequenceFlow_129 + + + + + + + + + + + + SequenceFlow_122 + SequenceFlow_132 + SequenceFlow_135 + + + + + + + + + + SequenceFlow_75 + SequenceFlow_128 + SequenceFlow_136 + SequenceFlow_134 + + + + + + + + + sb.team]]> + + + + + + SequenceFlow_135 + SequenceFlow_136 + + + + workitem['sb.team'] + && ( workitem['sb.team'][0]!="-" && workitem['sb.team'][0]!="") + + + + + + + + + + + + + + + + + + SequenceFlow_24 + + + DataOutput_5 + + + DataOutput_5 + + + + + + + + + + + + + + + + + SequenceFlow_133 + + + DataOutput_6 + + + DataOutput_6 + + + + + + + + + + + + Es handelt sich um einen SEPA Lauf. Die Rechnung muss explizit freigegeben werden. + Ändern von IBAN und Fälligkeit ist hier möglich + + + + + + + Datenübergabe an Cargosoft. + + Export erfolgt über eine asynchrone Verarbeitung im Modell 'cargosoft-export' + + + + + + + + + + Liegt eine Logistik Leistung vor, wird ein Fachbereich ausgewählt + + + + + + + Buchhaltung setzt Flag, wenn Mahnung eingetroffen ist + + + + + + + + + + + + + + + + + + + Auslandszahlungen werden gleich ausgeführt + + + + + + + + + payment.type ]]> + + + + + + + + + + + + sequenceFlow_swRy5A + sequenceFlow_dbshCw + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + false + + + + + + + sequenceFlow_ucyuIw + + + + + + + + + + + + + + space.name)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + Message-Eskalation +]]> + + + + + + + + + + + sequenceFlow_wq0Y0w + sequenceFlow_iH4SSA + + + + + + + + sequenceFlow_iH4SSA + + + + + + + + + + + + + + space.name)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + Message-Eskalation +]]> + + + + + + + + + + + sequenceFlow_Dy0E0A + + + + + + + SequenceFlow_25 + sequenceFlow_SvEjiw + SequenceFlow_58 + + + + + + + + + + space.name +Kreditor: cdtr.name +Rechnungsnummer: invoice.number +Betrag: invoice.total invoice.currency + +application.urlindex.jsf?workitem=$uniqueid + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 4f0f4aefd8bb8e4dea233b73650873f1ecaef1ba Mon Sep 17 00:00:00 2001 From: Ralph Soika Date: Wed, 12 Feb 2025 23:23:53 +0100 Subject: [PATCH 05/12] Businesspartner search --- .../BusinessPartnerController.java | 15 +++--- .../BusinessPartnerSearchEntry.java | 48 +++++++++++++++++++ .../alexander/businesspartner_search.xhtml | 26 +++++----- .../rechnungseingang-de-1.2.40-debug_BP.bpmn | 2 +- 4 files changed, 71 insertions(+), 20 deletions(-) create mode 100644 office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerSearchEntry.java diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerController.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerController.java index 6951468..d9bdb5d 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerController.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerController.java @@ -71,7 +71,7 @@ public class BusinessPartnerController implements Serializable { @Inject BusinessPartnerService businessPartnerService; - private List searchResult = null; + private List searchResult = null; private static Logger logger = Logger.getLogger(BusinessPartnerController.class.getName()); @@ -88,7 +88,7 @@ public class BusinessPartnerController implements Serializable { */ public void search(String regexPattern) { List resultList = null; - searchResult = new ArrayList(); + searchResult = new ArrayList(); // get the param from faces context.... FacesContext fc = FacesContext.getCurrentInstance(); String phrase = fc.getExternalContext().getRequestParameterMap().get("phrase"); @@ -110,6 +110,7 @@ public class BusinessPartnerController implements Serializable { pattern = Pattern.compile(regexPattern); } + // Filter by Regex and convert ItemCollection into a BusinessPartnerSearchEntry for (ItemCollection businessPartner : resultList) { String name = businessPartner.getItemValueString("name"); // Prüfen, ob der name der Regex entspricht @@ -121,10 +122,12 @@ public class BusinessPartnerController implements Serializable { } } String display = businessPartner.getItemValueString("$workflowsummary"); - ; + display = display.replace("\"", ""); display = display.replace("'", ""); - searchResult.add(businessPartner); + + searchResult.add(new BusinessPartnerSearchEntry(name, display, buildJsonData(businessPartner))); + } } @@ -134,7 +137,7 @@ public class BusinessPartnerController implements Serializable { * * @return */ - public List getSearchResult() { + public List getSearchResult() { return searchResult; } @@ -262,7 +265,7 @@ public class BusinessPartnerController implements Serializable { add("name", jsonVal(businessPartner.getItemValueString("name"))). // add("cdtr.number", jsonVal(businessPartner.getItemValueString("cdtr.number"))). // add("dbtr.number", jsonVal(businessPartner.getItemValueString("dbtr.number"))). // - add("name", jsonVal(businessPartner.getItemValueString("partner.name"))); + add("partner.name", jsonVal(businessPartner.getItemValueString("partner.name"))); // get iban list // {iban=[Dxxxxxx1], name=[Bank 1], id=[bank1], bic=[CITIDEFF]} diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerSearchEntry.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerSearchEntry.java new file mode 100644 index 0000000..dcd7234 --- /dev/null +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerSearchEntry.java @@ -0,0 +1,48 @@ +package com.alexanderlogistics; + +/** + * Diese Kleine Hilfsklasse dient als DTO für die Übertragugn eines + * Sucheregenisses nach kreditoren an das Frontend. Die Klasse besteht aus einem + * Key, einer DisplayZeile für die Darstellung und einen JSON String der + * beliebige Daten enthalten kann. + * + * @author rsoika + * + */ +public class BusinessPartnerSearchEntry { + + private String key; + private String display; + private String data; + + public BusinessPartnerSearchEntry(String key, String display, String data) { + super(); + this.key = key; + this.display = display; + this.data = data; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getDisplay() { + return display; + } + + public void setDisplay(String display) { + this.display = display; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } +} diff --git a/office-alexander-logistics-app/src/main/webapp/pages/workitems/parts/alexander/businesspartner_search.xhtml b/office-alexander-logistics-app/src/main/webapp/pages/workitems/parts/alexander/businesspartner_search.xhtml index 292190f..2173bb7 100644 --- a/office-alexander-logistics-app/src/main/webapp/pages/workitems/parts/alexander/businesspartner_search.xhtml +++ b/office-alexander-logistics-app/src/main/webapp/pages/workitems/parts/alexander/businesspartner_search.xhtml @@ -3,11 +3,11 @@ xmlns:ui="http://xmlns.jcp.org/jsf/facelets" xmlns:pt="http://xmlns.jcp.org/jsf/passthrough" xmlns:marty="http://xmlns.jcp.org/jsf/composite/marty" xmlns:i="http://xmlns.jcp.org/jsf/composite/imixs"> - - - + + +
diff --git a/workflow/rechnungseingang-de-1.2.40-debug_BP.bpmn b/workflow/rechnungseingang-de-1.2.40-debug_BP.bpmn index f556232..83ab4a1 100644 --- a/workflow/rechnungseingang-de-1.2.40-debug_BP.bpmn +++ b/workflow/rechnungseingang-de-1.2.40-debug_BP.bpmn @@ -5535,7 +5535,7 @@ result.isValid=true; - + From ccc4584f91e95459abd6b34a6f40562b9f5d7dc0 Mon Sep 17 00:00:00 2001 From: Ralph Soika Date: Thu, 13 Feb 2025 08:47:59 +0100 Subject: [PATCH 06/12] finalized 5.0.4 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b6e3c92..6f9dd46 100644 --- a/pom.xml +++ b/pom.xml @@ -26,10 +26,10 @@ 10.0.0 5.0.4-SNAPSHOT - 6.0.9-SNAPSHOT + 6.0.9 5.0.0 3.0.2 - 3.0.3-SNAPSHOT + 3.0.3 2.0.2 6.0 9.9.1-4 From 90d624d5857bc794d2b4c98a6e1cfbe6ff638f78 Mon Sep 17 00:00:00 2001 From: Ralph Soika Date: Thu, 13 Feb 2025 18:23:07 +0100 Subject: [PATCH 07/12] =?UTF-8?q?BusinessPartner=20Suche=20=C3=BCber=20Res?= =?UTF-8?q?t=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/BUSINESSPARTNER.md | 39 ++ doc/images/businesspartner-widget.png | Bin 0 -> 7702 bytes docker-compose.yml | 5 + .../BusinessPartnerController.java | 64 +-- .../BusinessPartnerService.java | 182 ++++++- .../api/BusinessPartnerRestService.java | 104 ++++ .../xml/BusinessPartnerImportService.java | 60 ++- ...=> section_businesspartner_banklist.xhtml} | 0 pom.xml | 2 +- workflow/businesspartner-de-1.0.0.bpmn | 479 ++++++++++++++---- .../rechnungseingang-de-1.2.40-debug_BP.bpmn | 4 +- 11 files changed, 766 insertions(+), 173 deletions(-) create mode 100644 doc/BUSINESSPARTNER.md create mode 100644 doc/images/businesspartner-widget.png create mode 100644 office-alexander-logistics-app/src/main/java/com/alexanderlogistics/api/BusinessPartnerRestService.java rename office-alexander-logistics-app/src/main/webapp/pages/workitems/forms/alexander/{sub_businesspartner_ibanlist.xhtml => section_businesspartner_banklist.xhtml} (100%) diff --git a/doc/BUSINESSPARTNER.md b/doc/BUSINESSPARTNER.md new file mode 100644 index 0000000..7a9b900 --- /dev/null +++ b/doc/BUSINESSPARTNER.md @@ -0,0 +1,39 @@ +# BusinessPartner + +Wir haben einen Workflow für die Verwaltung von BusinessPartner. +Ein BusinessPartner wird über die bestehende Cargosoft Schnittstelle aus den Debitoren/Kreditoren automatisch importiert und synchronisiert. + +In Imixs ist es dann aber möglich zusätzliche Attribute zu einem Businesspartner zu speichern. Beispielsweise: + +- Zahlungsziel +- Bankverbindungen +- Emailadressen für Mahnwesen + +Die Businessparnter werden zentral nur in dem Hauptsystem in Bremen importiert und verwaltet. Damit ein AGL System auf die BusinessPartner zugreifen kann wird eine Rest API Schnittstelle verwendet. Diese kann über Environmentvariablen aktiviert werden: + + # Rest Service BusinessPartner API + WORKFLOW_SERVICE_ENDPOINT: "http://app:8080/api" + WORKFLOW_SERVICE_USER: "admin" + WORKFLOW_SERVICE_PASSWORD: "xxxxxxxx" + +## BusinessPartner Suche + +Es gibt einen Controller und ein widget um nach Business Partnern zu suchen. + + + +Z.b. kann das als Custom Part in eine Form eingebunden werden: + +```xml + +``` + +Das widget legt dann automatisch die felder `bpid` und `bpidname` an. + +Alternativ kann im Backend über die EJB BusinessPartnerService nach bpid gesucht werden: + +```java + List resultList = businessPartnerService.search(phrase); + // oder + ItemCollection bp= businessPartnerService.getBusinessPartnerByID("BP0001"); +``` diff --git a/doc/images/businesspartner-widget.png b/doc/images/businesspartner-widget.png new file mode 100644 index 0000000000000000000000000000000000000000..2aab829425f7902d2472eb312c7d06bdb1d60458 GIT binary patch literal 7702 zcmaiZbyQX1x9tH8L?u*E5u}cSDBUg10i+uQX{5UjB_fST3Q|hzp}P^KQ(6Q>TDlwI zt#g0(pZCVQ<1!rJ;oI!J_P5rWYtH$FC@Dx^Cm<(4AQ0D4G7>5X1db8>UjzRVeBb+; z>;gY7IEkUu@Zrk~-y|45-*uMMa#po7b9OUyG)0)(+S!;gIT<^en%X*9*g0?DH44K; zkFXbsJDM6gzqGTZR(ok&7 zLR8H?d2P}|Pu=wF>sH_M3(WqsntJw4G@{R(Sl>&aNc6-+iz(harw=)|$N3O^Mp{1pF05L8q8rfksR6+PO8oA_~BHtX1L~%d7S&T$QM|4O$K}DMwvy;e0BgYc{ z*QNdThck`;*6gm`Vf9;Gwbl*VZp27lwHO~6QI;9FsfcE|8bKwJI@=bB_fh4uy{l`s zcB$DlD&b_ao)7A%7uD6(bBl|{oa7i2@5qv>DqhRcf{4Px!hCU@*EsAPpY(++eiA#^ z%xt}Use~c>IX1>EjPk^t)QqSy{2u6zMjLY{@p&DxMny$U)VN?|aZgTn`a&WiT6%i& zWk(KjWfc|8oBS^#k;tF?u}1WY{X)0#fQyU!v(!A2-B(NN4h{|ulAa##T|$D4yF0I_sAxc7 z;0;pJmTIR(#2wZb2>6X&t?PBayBuaeO3f7sDR`g4b@k{2v%}443O+{(?M7n%VT>k+ z`{oOOfB!ptj>xU8t63(%l;hc{hgA5TrmwH?_}tbh zl$Dk7X@bMT=%}gvDyMvEJ1cm&x#Ru(5DKY+9XV+}vhc8Jf?Z+Sf&4|>hb!FnQ`<>~zw|shbvT^IyE&Jo$r2&Cz)J^vlruKFO z5z&v8>466cY!}v7qP^p}pu?D{XNNFxG#u50Po_f{Q*J~HOz_GQpwJ6uAabEh?5krrC5(x>3 zdQJTUX8pSMvX_I{ebXtoOz1I7PEOw3-eyG8+l{H5@|Y9eA|o3Oth9NpU%&3E^#E0K z`5-P>NJ!|ja>gai0zDcbp_Fo~(Sc%P>Ex7@uH^H5w;uum0yl|?pL%%-#?UKbTO304 zcU}7xr)$;~&-7|0DFA|jw|keCrl_xfUt3!nQMc84kE|<}({6WVH1cR?vDB!AfZOI* z^E(>xzoUf)Z;W3~RIwqRC2?P*=HR%Q(H|_EWpuPXzxumAsk>WV{^3o;n>TMDcuw+& zlDDG?;DLV7HGLLtI@Y#zdkk9%GVeyx6)jBbLpa}7%ry`0r~aAlkYoyn!IMH zXvW2c8(WydhS0P$Dlz=u?z7jUV`9eY(L6LXG^V}2v#r;|re6+b_cwT>8&0?7V9{eG zzfueKHD9N^L?^poj#-$2>)F$zngR_>B9~QsOIh?)aTyu>`}gnX>6C|NW+KU$beemT zcB$hz&OHbrrV$+s7*3~4w})UReLRQ;fE7@pxNC3j!DgjesmXEgjp z-Fao?dg++Fku@HF1zL6y62Z&M%fOSQQ(@f!7ajsAqvYl1R>w-A9<#U; zqoSHt3LD=>MiP0}r{1JjK>Wx=#h^navsBnDeo)7=81w?XElpH&TzdTqmRUV_Ej)Ms zHvR3-_^g^k2arqoC(XXzq z#*#aG$i`2WniDN8E&bi|G%_;!VuH>et8sY@-SvHZJjt{xezF{9-u=82+2#jajOOO% z05o0L(x|N!KtNm0pC0Y3j#oy;#@+(dws&+in(vH5s1@o{vaqoD9L?XhoveA2lyvWV z5D`n`+BP$ww!cCS9bhd$fBLZH%aOdhsi~=m!^1=SKVNYG&;v_LIaum8ap|POzglx^ zGQ@}6q%X0Z)61Po_RW|@ghGLp9 zwih6#)Gy63SKP++tpo)H?VX*=d`cCfGlOIwpdA`&VTL$5IUOxy3UhTU3-Z55A(yV~ zv^QAIC?gBCqme@R|2^leKfFVtO4zjN;^9Xz+S5|ZB;~8~XI;} zJUH{E(j9G%?Bxoq2LsyqUu^}ka@u4lq3Z+k)a0UfO<0YEOL0_ZD9VI1g@#P@mL#kv z?!Btef*g~8=QWJ7|hXan{P+5d-(oU3Z&X*GqCKDMR|yqFmL0Oe^ES4UOiEQD(TjH5?jkuGp5D~F(d3PtUmDx^``vI3JwHjqsQB`HTbFHej zcCgaUK&Q;YVM9PPI#W7y%-ZaCod@7+;QD0k<5{`m#JWkyxQwjq%=y{Ixe|uT=ULGyOVf`sHmtAmLqv6NVjC-WkL#S8k$#tBHH1;YvrRBKR&)*Ym41JHQqI!yizKb9qoc~gm8(~0p*bl; ze3CC+C;zj&9E!nU*f*l+q*?D=J3KxnyNnrT6w-;6QBYW@UQAW3p{{WvevDwuP+klr106HG1PC|`4;q6smqE+>sv}Wd3pJ)8eWX0 z`iEe-1o?*wDf}&aE2CX&Okx@RABQdD&eqq!_(U!%aH+`r5|FYln6rXoX~6Wizy02p~s) zt}T5#&D1umUzG)iZw^cOs@C9uQ5W_2;c?&=xpYva0A0Hp?uwvPpTfroB_HJ%%7M#%^K6C=qepbiC50oJ`*D9w< z9A>4u%{cb=_v-+QO&`wST_*gqx|%o9UaP9COadsM?R$PkHmlA^`tNv8HL$*}j)9TU zsO>E!T>J0jpw#=s^`U&iQ>d5?mqF;M0I%bv97H%!V}KcQ9$V}FRqb!X!*Q(<_hO=> z{nOIYN+zsKeXsbw_`*q2TV4GqC5YR0BBAR&OQ~rWS(wB`J=%@mWjUa}UZ~u1n6cDB z55MUZ-sR1`z4Bk~XFGi&#_Wl)`Wan=nW&qt8^0rnapPpO!lx?js8&V`%_piH+}7+> zqchuLG%%7G{aPsn{T7`mXRLznxM#*E$If&!q^>{E*4-JmYXk+LY+JkD;C-^Wwbj($ zzg57NnVk&`iz#{&M$pe9L%iA6hzuC$ZqI{YpCgqsq#mUKso`_qq~_z}tE#Hf>V6Lt z4VXxro@z<5;hM;O{US97+TQN&=HI{X$8<017RYRB?bRLnyQPI4^4hMXRD8JwN`>VMn&a@ZiTfd{wo+rERClnlHy)QMzmu1Bl)`K z#n#Zz9`kXUjHHFI3EVKqe-;~SmzZ4X@9z)4%W;Xpr{oK8>F|3zgs|VsBtL^b53mIC zU>FU+k$HZ% zx3?FNw{^rYmUbM5LbUbj{TsyuMbBPWAW;La&9iy?gaqRhni&rk+0w=~sg0uGnS48m z*K;S*d~;1T_(`mEBAR1)FYhGowSNFPtVqbQz_G&ipjULza!#D7dO+kaf>@iLmGYc^ zrZe(s*6sI5l;?%dO4TBf*~2nw1x!@79S70aCue@4OZ(4E#ZISaYx##pFwgGAK%B^q zRukI)nzL<;BvgX#@7#WSc^iM85za0RFv~|xUl0b zM8B|cLyIaNdf1sirDf$GBP|C<43PAw#6(y#U+7NEI^Or6Va|44{n-SY%G9cu!20Tw zc#uXe-6+~&dk)obv~Vw5F-?En>!9WzQ4+B~NcEYSnJUjcMxFflg`T9FckgPODSkh& zgmMO<8vuheYzs?DL9VE4ymdZbe?+nY3JnXRTeEG01x{5WiANDZzVV;l=C;e0s|Ao}{!q)P*O@+`<^Yo;Yx z&?6c;wIBL8T#BwOlB#j>?1tUOElp2Bb$*hoG}v1M`D$rvlfLVVK6enq7d~0#n3F*C3XMRfThJrxWi2S|=8nL~-U1`5xRZ&QNp~2qX{v+HCv^p^{G2)g%tN_5g z28QXNDiXGbMm(^{eap!I^=ps)G4nC77=T**N3AFDI?%3+o=ni9P$nExpO)=|B zeFmfS?Ch-bh)zgK;z3qsW-!pl_X2$tHscZ|CYeBaXzA$0uaR@gn3$O0$8;d|J?2TH zVq=j3#6~Tlq%*UTB4lFt#h{+BY6rW}ZB|Mw77R8>DCir4jD7|J0)plQHsfYsg|SDY z`MNn?XDbHhjJ6Y1G;kA`-A4WCl4AHY0G|@T0AYyV09JY%GM_PA1rq(yNS?Mao9#?< zh|%fc)(jXhvU=vd$s|WdNAOqh4oonn-A8#rpduU2&(Lt!ix)2fDyV9o10!JxApT~; z9@5?}Q|z>;0E;kCNWe@=FDg=k#DoA!={C2v>P#B2v9Zl8rujCkI8>?n_%yf!`NZx) z0*Coq)`trdPmT`2+EN7(rZx(2?>U_-QG1S zKF7?%e+N@>(;$<|#M;0?d~USAItDLVQd-*bQS@~{hY91s=`(l`RQe2PURY8{6J`RZ zOxg0>W}-^jYqROH#z7%*%VP7?`I#Gy#8&k{p@B%T{j`{=`&U1lbU>9v9)5nEJJ&9O zc?i!U0o|ApK&~R@aEk+Vr=oB|2x3>S#EV3c$frc z7}yB7eKe0Ag@AHbDm8nS6;FKY7Is@=U0nYDzyVG_Z|MU9QQvdv0Vnx&{*<@3_1&8+ z^?>_f#ik$NtP}89(+^;UfW~lMpBPK$SqxnsEu?Z;8Bw3H{8jdOdw8O)EvT%lthN

PSrIM18dHVG=Lv;-e4cI{iUZkU=BPi0kfhWrP9^j;z0M*9I{w+^5 z8gxy87bKVuShjK?C>otFryzv~K%(z@3c3IEEKPY}M>!K7QKyRWY=0zeAr zqkxHJ43Bxb)syD2v+!tAb*jb%TuhyJQBk6@vIGF6PaSRs_FJfpsBe>&NI;HTdP+)c zspE3BOS8AN^EGty4uD1Fsxg9_rasog#8L3`rxxLTLHwsuQkMWs8?7k7o(1azZ~<%o zt^O?T+~?)vd(;K2^Ic3#MjnJPy0S-h?&sF(LT}YJ?FwGS6TPZ;U`=4fKze$50=p>z zw5VR4d+`uIB?ZMH^hO?M7Kkgx6_!FlH2Oq^M_8`mgZNfU3Gz|=wJD!#asWTXR8-&` zqy1(I2GYO~aXb*e;?QQLo=1t1iXbmG@4K(>wIw+)I7lLftw*eISwBLb9Q(1~OpS@T zd7D{JDG*|l1L>&^Pi{HovK%T3UfcI}T0HVbR|BV4>7`<5Xn*fgs^D3s#!rIw^mK%P zdz1N$%TtCyDM0vJ-5h;d;UX@}x4;Wq%!m|bbgCAxa%E;_HZeD!ou0<|3don&s;-}; z1r$Az+lCy=)B%Zy@^wd->)ccwn~wgxCi=1DjK!QGug)+!Ufu*@@8crRJxelXJsBXP zel2g9goK`>en3cA^c_2k&LPi>b6G4ZpS7~x&;-~VoPl%-!axDA4+LNCV-=ax&cd_0y1Mr6?l~BM2++6%deuhdd0<}` z{YT<~^=Um>Q(iKS+fp6|#AvhgaUm-pyb4!FXM0CS)9^59ih{I{k1zlZsSYz7W&D?c z`VH)^^mJO&Uw|ZRh73UYui0_b*rd8fVnq3Reo- zQGgPF%zV8$=+j(v3>mx>oW{gyzXekf^y4g`Dw(33T4vqJaF*O{7=_u%JW#KlZ&rKP z+}zwWQ;U1Lx@O_b;fvDU2UGl5XS8;s0W%R?uzIA6C6Kf8^OBIr+bsGL_@c0XFskBo z`qqGCvXVzYSYQojCW{Qr&ZgcsxCgKW;P~e1ZO6T3b)bp4&uh6smR$~QJcgoYb)1s{ z3m%B&?4;Y2B;*`UNuW^Lllh#KKC8h=)z{?=6tpBL#O>`amf6)+dFYZOP|)ih#wWW= z-+$Mm;S@>TC_fL-4_Mz_erE=-ytB~<>!io#;6ET4841Ai1_!1CI}2WdC5&a{6BBx% zQ>4JV@tdh=EGy##eijHG6i}btweeBQyRgN^Kx3eg+1=LlFm0kagO9qf^b`m0607Y5 zZvZjaH2OYArW6zu*p_!%>}49hkUqF{IGA=85EBzKa1Cc9@?>p(vJjk>&DMK%#FUgh zz*VsI4CDUq-m%I!6&YD8%%*m?DeoZcpR9ZG0OW!R@*_u)3MvpwZGaXC{@WddqRkHp z37IbK6E+^mL;>3|G&h%q{<}gz@MZT*2{R1d8S(x53Sip*H|G(aI|;UVLQCtvxoHmv z4K|dayHpv{k!H{jWfuLoG#5?|Hq61%@j9FisvvykDK@#ovI1(T@J%Ek%?01nt;--^ zK%IP2`3wc_l`EnVeRj|ehmMuXpVdHv1_Cetq=8{e>Ji) var3 = this.ibanList.iterator(); - - while (var3.hasNext()) { - ItemCollection orderItem = (ItemCollection) var3.next(); - mapOrderItems.add(orderItem.getAllItems()); - } - - workitem.replaceItemValue("partner.iban.list", mapOrderItems); - } - - } - - protected void explodeIBANList(ItemCollection workitem) { - this.ibanList = new ArrayList(); - List mapOrderItems = workitem.getItemValue("partner.iban.list"); - Iterator var4 = mapOrderItems.iterator(); - while (var4.hasNext()) { - Object mapOderItem = var4.next(); - if (mapOderItem instanceof Map) { - ItemCollection itemCol = new ItemCollection((Map) mapOderItem); - - this.ibanList.add(itemCol); - - } - } - - } - - /** - * Liefert die ChildItems mit den IBAN daten - * - * @param workitem - * @return - */ - public List getIBANList(ItemCollection workitem) { - List result = new ArrayList<>(); - List mapOrderItems = workitem.getItemValue("partner.iban.list"); - Iterator var4 = mapOrderItems.iterator(); - while (var4.hasNext()) { - Object mapOderItem = var4.next(); - if (mapOderItem instanceof Map) { - ItemCollection itemCol = new ItemCollection((Map) mapOderItem); - result.add(itemCol); - } - } - return result; - } - /** * Hilfsmethode die eine JSON Struktur mit allen relevanten Daten für einen * BusinessPartner erzeugt. @@ -269,7 +214,7 @@ public class BusinessPartnerController implements Serializable { // get iban list // {iban=[Dxxxxxx1], name=[Bank 1], id=[bank1], bic=[CITIDEFF]} - ibanList = getIBANList(businessPartner); + ibanList = BusinessPartnerService.getBanks(businessPartner); int i = 1; for (ItemCollection iban : ibanList) { objectBuilder.add("iban" + 1, jsonVal(iban.getItemValueString("iban"))). // @@ -299,4 +244,5 @@ public class BusinessPartnerController implements Serializable { val = val.replace("'", ""); return val; } + } 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 70b8571..8533054 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 @@ -27,16 +27,24 @@ package com.alexanderlogistics; +import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collections; +import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.logging.Logger; +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.imixs.melman.DocumentClient; +import org.imixs.melman.FormAuthenticator; +import org.imixs.melman.RestAPIException; import org.imixs.workflow.ItemCollection; import org.imixs.workflow.ItemCollectionComparator; -import org.imixs.workflow.engine.DocumentService; import org.imixs.workflow.engine.index.SchemaService; +import jakarta.annotation.PostConstruct; import jakarta.annotation.security.DeclareRoles; import jakarta.annotation.security.RolesAllowed; import jakarta.annotation.security.RunAs; @@ -62,15 +70,79 @@ import jakarta.inject.Inject; public class BusinessPartnerService { public static final int MAX_SEARCH_RESULT = 100; + public static final String ITEM_IBAN_LIST = "partner.bank.list"; + public static final String WORKFLOW_SERVICE_ENDPOINT = "workflow.service.endpoint"; + public static final String WORKFLOW_SERVICE_USER = "workflow.service.user"; + public static final String WORKFLOW_SERVICE_PASSWORD = "workflow.service.password"; private static Logger logger = Logger.getLogger(BusinessPartnerService.class.getName()); + private FormAuthenticator formAuthenticator; + @Inject - DocumentService documentService; + @ConfigProperty(name = WORKFLOW_SERVICE_ENDPOINT) + Optional workflowServiceEndpoint; + + @Inject + @ConfigProperty(name = WORKFLOW_SERVICE_USER) + Optional workflowServiceUser; + + @Inject + @ConfigProperty(name = WORKFLOW_SERVICE_PASSWORD) + Optional workflowServicePassword; + + // @Inject + // DocumentService documentService; @Inject SchemaService schemaService; + /** + * Initialize the rest clients + */ + @PostConstruct + public void init() { + // init clients + try { + initAuthenticator(); + } catch (RestAPIException e) { + logger.severe("Failed to initalize Rest Clients: " + e.getMessage()); + } + } + + /** + * Diese Methode sucht einen Business Partner anhand seiner BPID + * + * @param bpid + * - business partner id phrase + * @return - matching business partner + */ + public ItemCollection getBusinessPartnerByID(String bpid) { + long l = System.currentTimeMillis(); + if (bpid == null || bpid.isEmpty()) { + return null; + } + List result; + try { + String sQuery = "(type:\"workitem\") AND ($modelversion:businesspartner-*)"; + sQuery += " AND (name:" + bpid.trim() + ")"; + logger.finest("SearchQuery= " + sQuery); + + // register client + DocumentClient documentClient = getDocumentClient(); + documentClient.setPageSize(MAX_SEARCH_RESULT); + documentClient.setPageSize(1); + result = documentClient.searchDocuments(sQuery); + if (result.size() > 0) { + logger.info("🕐 get BusinessPartner by id in " + (System.currentTimeMillis() - l) + "ms"); + return result.get(0); + } + } catch (RestAPIException | UnsupportedEncodingException e) { + logger.warning("Failed to get BusinessPartner: " + e.getMessage()); + } + return null; + } + /** * Diese Methode sucht Business Partner anhand einer Suchphrase * @@ -79,7 +151,7 @@ public class BusinessPartnerService { * @return - list of matching business partners */ public List search(String phrase) { - + long l = System.currentTimeMillis(); List searchResult = new ArrayList(); if (phrase == null || phrase.isEmpty()) { return searchResult; @@ -94,13 +166,113 @@ public class BusinessPartnerService { logger.finest("SearchQuery= " + sQuery); - searchResult = documentService.find(sQuery, MAX_SEARCH_RESULT, 0); - } catch (Exception e) { + // register client + DocumentClient documentClient = getDocumentClient(); + documentClient.setPageSize(MAX_SEARCH_RESULT); + searchResult = documentClient.searchDocuments(sQuery); + + } catch (RestAPIException | UnsupportedEncodingException e) { logger.warning(" lucene error - " + e.getMessage()); + formAuthenticator = null; } // sort by txtname.. Collections.sort(searchResult, new ItemCollectionComparator("$workflowsummary", true)); + + logger.info("🕐 BusinessPartner search: " + searchResult.size() + " entries in " + + (System.currentTimeMillis() - l) + "ms"); return searchResult; } + + private DocumentClient getDocumentClient() { + DocumentClient documentClient = null; + try { + if (formAuthenticator == null) { + initAuthenticator(); + } + documentClient = new DocumentClient(workflowServiceEndpoint.get()); + documentClient.registerClientRequestFilter(formAuthenticator); + } catch (RestAPIException e) { + logger.warning("Failed to init RestClient: " + e.getMessage()); + formAuthenticator = null; + } + + return documentClient; + } + + /** + * Packt die Liste der Bank Details (ItemCollections) in ein Workitem + * + * @param workitem + */ + public static void implodeBanks(ItemCollection workitem, List ibanList) { + List mapOrderItems = new ArrayList(); + if (ibanList != null) { + logger.fine("Convert child items into Map..."); + Iterator var3 = ibanList.iterator(); + + while (var3.hasNext()) { + ItemCollection orderItem = (ItemCollection) var3.next(); + mapOrderItems.add(orderItem.getAllItems()); + } + + workitem.replaceItemValue(BusinessPartnerService.ITEM_IBAN_LIST, mapOrderItems); + } + + } + + /** + * Enpackt die iban Map Liste von eiem Worktiem in eine Liste von ItemCollection + * + * @param workitem + */ + public static List explodeBanks(ItemCollection workitem) { + List ibanList = new ArrayList(); + List mapOrderItems = workitem.getItemValue(BusinessPartnerService.ITEM_IBAN_LIST); + Iterator var4 = mapOrderItems.iterator(); + while (var4.hasNext()) { + Object mapOderItem = var4.next(); + if (mapOderItem instanceof Map) { + ItemCollection itemCol = new ItemCollection((Map) mapOderItem); + + ibanList.add(itemCol); + + } + } + return ibanList; + } + + /** + * Liefert die ChildItems mit den IBAN daten + * + * @param workitem + * @return + */ + public static List getBanks(ItemCollection workitem) { + List result = new ArrayList<>(); + List mapOrderItems = workitem.getItemValue(BusinessPartnerService.ITEM_IBAN_LIST); + Iterator var4 = mapOrderItems.iterator(); + while (var4.hasNext()) { + Object mapOderItem = var4.next(); + if (mapOderItem instanceof Map) { + ItemCollection itemCol = new ItemCollection((Map) mapOderItem); + result.add(itemCol); + } + } + return result; + } + + /** + * Helper method to initalize a Melman FormAuthenticator + * + * @throws RestAPIException + */ + public void initAuthenticator() throws RestAPIException { + logger.info("⚡ Init FormAuthenticator..."); + + // form authenticator + formAuthenticator = new FormAuthenticator(workflowServiceEndpoint.get(), workflowServiceUser.get(), + workflowServicePassword.get()); + + } } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/api/BusinessPartnerRestService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/api/BusinessPartnerRestService.java new file mode 100644 index 0000000..bf0771c --- /dev/null +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/api/BusinessPartnerRestService.java @@ -0,0 +1,104 @@ +/* + * Imixs-Workflow + * + * Copyright (C) 2001-2020 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: + * https://www.imixs.org + * https://github.com/imixs/imixs-workflow + * + * Contributors: + * Imixs Software Solutions GmbH - Project Management + * Ralph Soika - Software Developer + */ + +package com.alexanderlogistics.api; + +import java.util.logging.Logger; + +import org.imixs.workflow.ItemCollection; +import org.imixs.workflow.exceptions.QueryException; +import org.imixs.workflow.jaxrs.DocumentRestService; + +import com.alexanderlogistics.BusinessPartnerService; + +import jakarta.ejb.Stateless; +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; +import jakarta.ws.rs.core.Response; + +/** + * BusinessPartnerRestService api endpoint '/businesspartner' provides methods + * to read and post business partner data + * + * @version 1.0 + * @author rsoika + * + */ +@Stateless +@Produces({ MediaType.TEXT_HTML, MediaType.APPLICATION_XHTML_XML, MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, + MediaType.TEXT_XML }) +@Path("/businesspartner") +public class BusinessPartnerRestService { + + @Inject + DocumentRestService documentRestService; + + @Inject + BusinessPartnerService businessPartnerService; + + private static Logger logger = Logger.getLogger(BusinessPartnerRestService.class.getName()); + + @GET + @Path("/ping") + public String ping() { + logger.info("GET ping"); + return "ping: " + System.currentTimeMillis(); + } + + /** + * Test method + * + * @param workflowgroup + * @param task + * @return + * @throws QueryException + */ + @GET + @Path("/{bpid}") + @Produces({ MediaType.TEXT_HTML }) + public Response getBusinessPartnerByID(@PathParam("bpid") String bpid, + @QueryParam("items") String items, + @QueryParam("format") String format) + throws QueryException { + + ItemCollection workitem = businessPartnerService.getBusinessPartnerByID(bpid); + if (workitem == null) { + // document not found + return Response.status(Response.Status.NOT_FOUND).build(); + } + + return documentRestService.convertResult(workitem, items, format); + + } + +} 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 a91aec8..8c238b6 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 @@ -36,6 +36,7 @@ import java.util.logging.Logger; import org.imixs.archive.core.SnapshotService; import org.imixs.workflow.ItemCollection; +import org.imixs.workflow.WorkflowKernel; import org.imixs.workflow.engine.DocumentEvent; import org.imixs.workflow.engine.DocumentService; import org.imixs.workflow.engine.ModelService; @@ -46,6 +47,7 @@ import org.imixs.workflow.exceptions.PluginException; import org.imixs.workflow.exceptions.ProcessingErrorException; import org.imixs.workflow.exceptions.QueryException; +import com.alexanderlogistics.BusinessPartnerService; import com.alexanderlogistics.InvoiceUtil; import com.alexanderlogistics.KreditorDebitorService; @@ -158,7 +160,33 @@ public class BusinessPartnerImportService { // hat sich das Objekt verändert? if (!businesspartner.equals(oldBussinessPartnerItemColl)) { - businesspartner.event(201); // update/create + + // Prüfe IBAN auf plausibilität + List bankList = BusinessPartnerService.getBanks(businesspartner); + boolean ibanDublette = false; + List dublettenIban = new ArrayList<>(); + for (ItemCollection bank : bankList) { + String iban = bank.getItemValueString("iban"); + // erkenne fehlerhaften kontonummern + if (dublettenIban.contains(iban)) { + // ACHTUNG - uneindeutige Bankverbindung + ibanDublette = true; + } else { + dublettenIban.add(iban); + } + } + + int eventID = 200; // default + // event errechnen + if (!businesspartner.hasItem(WorkflowKernel.LASTEVENT)) { + // create new! + if (ibanDublette) { + eventID = 300; // Exception + } else { + eventID = 100; // update/create + } + } + businesspartner.setEventID(eventID); try { workflowService.processWorkItemByNewTransaction(businesspartner); } catch (AccessDeniedException | ProcessingErrorException | PluginException | ModelException e) { @@ -234,7 +262,10 @@ public class BusinessPartnerImportService { } - // build partner.iban.list + // build partner.iban.list - dublettenprüfung und Trim + List dubletten = new ArrayList<>(); + List dublettenIban = new ArrayList<>(); + if (ibanList.size() > 0) { List bankList = new ArrayList<>(); for (int i = 0; i < ibanList.size(); i++) { @@ -243,10 +274,20 @@ public class BusinessPartnerImportService { if (bicList.size() > i) { bic = bicList.get(i); } + + iban = superTrim(iban); + bic = superTrim(bic); + if (dubletten.contains(iban + "/" + bic)) { + // voll dublette + continue; + } else { + dubletten.add(iban + "/" + bic); + } + ItemCollection bank = new ItemCollection(); // {cdtr.iban=[A], name=[cdtr1], cdtr.name=[a], cdtr.bic=[a]} - bank.setItemValue("id", "bank" + i + 1); - bank.setItemValue("name", "Bank " + i + 1); + bank.setItemValue("id", "bank" + (i + 1)); + bank.setItemValue("name", "Bank " + (i + 1)); bank.setItemValue("iban", iban); bank.setItemValue("bic", bic); @@ -258,7 +299,7 @@ public class BusinessPartnerImportService { ItemCollection orderItem = (ItemCollection) var3.next(); mapOrderItems.add(orderItem.getAllItems()); } - businessPartner.setItemValue("partner.iban.list", mapOrderItems); + businessPartner.setItemValue(BusinessPartnerService.ITEM_IBAN_LIST, mapOrderItems); } } catch (QueryException e) { @@ -269,6 +310,15 @@ public class BusinessPartnerImportService { return businessPartner; } + /** + * entfernt alle leerzeichen + */ + public String superTrim(String data) { + String result = data.trim(); + result = result.replace(" ", ""); + return result; + } + /** * Sucht nach einem Business partner * diff --git a/office-alexander-logistics-app/src/main/webapp/pages/workitems/forms/alexander/sub_businesspartner_ibanlist.xhtml b/office-alexander-logistics-app/src/main/webapp/pages/workitems/forms/alexander/section_businesspartner_banklist.xhtml similarity index 100% rename from office-alexander-logistics-app/src/main/webapp/pages/workitems/forms/alexander/sub_businesspartner_ibanlist.xhtml rename to office-alexander-logistics-app/src/main/webapp/pages/workitems/forms/alexander/section_businesspartner_banklist.xhtml diff --git a/pom.xml b/pom.xml index 6f9dd46..f634655 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ 10.0.0 - 5.0.4-SNAPSHOT + 5.0.4 6.0.9 5.0.0 3.0.2 diff --git a/workflow/businesspartner-de-1.0.0.bpmn b/workflow/businesspartner-de-1.0.0.bpmn index a2f0880..99777e7 100644 --- a/workflow/businesspartner-de-1.0.0.bpmn +++ b/workflow/businesspartner-de-1.0.0.bpmn @@ -4,6 +4,7 @@ + association_83sZPw @@ -25,9 +26,12 @@ - - - + + + + + + @@ -49,10 +53,30 @@ textAnnotation_LyGTCw event_TuyuwQ event_h0Kk5g + task_delO7Q + event_1OVorA + event_baWU4w + event_ofjVfg + event_i7yQVw + gateway_uK0c6g + event_6250fg - + + + + + + + + + + + + + + sequenceFlow_25otUg sequenceFlow_S9LVXg @@ -63,7 +87,6 @@ - sequenceFlow_NNbMhg sequenceFlow_rwdWxw @@ -77,13 +100,40 @@ partner.name (partner.id)]]> + + Adresse:
+partner.name
+partner.address
+partner.zip partner.city
+
+ +BPID: partner.id
+Kreditoren-Nr.: cdtr.number
+Debitoren-Nr.: dbtr.number
+]]>
+
+ + + + + + + + + + + + + + +
- sequenceFlow_knUYpQ - sequenceFlow_2uNvPw - sequenceFlow_NNbMhg sequenceFlow_avahXg sequenceFlow_9atLFA + sequenceFlow_T5L9aQ + sequenceFlow_b4BcVA + sequenceFlow_8O1EtQ
@@ -93,34 +143,59 @@ + + Adresse:
+partner.name
+partner.address
+partner.zip partner.city
+
+ +BPID: partner.id
+Kreditoren-Nr.: cdtr.number
+Debitoren-Nr.: dbtr.number
+]]>
+
+ + + + + + + + + + + + + + +
sequenceFlow_1mXO8A sequenceFlow_rwdWxw sequenceFlow_gC0I9w + sequenceFlow_kS2fMA
- + - + Partnermanagement]]> - sequenceFlow_S9LVXg - sequenceFlow_knUYpQ + sequenceFlow_jAU3VA + sequenceFlow_8O1EtQ - + - - - @@ -128,18 +203,13 @@ - sequenceFlow_2uNvPw sequenceFlow_1mXO8A + sequenceFlow_b0Bj3Q + sequenceFlow_T5L9aQ - - - - - - @@ -174,13 +244,10 @@ - + ]]> - - - sequenceFlow_avahXg @@ -196,10 +263,10 @@ - + - + @@ -207,6 +274,9 @@ + + + sequenceFlow_9atLFA @@ -229,139 +299,346 @@ + + + + + + + partner.name (partner.id)]]> + + + Adresse:
+partner.name
+partner.address
+partner.zip partner.city
+
+ +BPID: partner.id
+Kreditoren-Nr.: cdtr.number
+Debitoren-Nr.: dbtr.number
+]]>
+
+ + + + + + + + + + + + + + + + + + +
+ + sequenceFlow_NNbMhg + sequenceFlow_avahXg + sequenceFlow_pvIENQ + sequenceFlow_BWOtzA + sequenceFlow_ofWWuw + sequenceFlow_b0Bj3Q + sequenceFlow_BHsRkQ +
+ + + + + + + + + + + + + + sequenceFlow_iwMsIg + sequenceFlow_BHsRkQ + + + + + + + + + sequenceFlow_pvIENQ + sequenceFlow_b4BcVA + + + + + + + + + + + + + + + + + + + + + sequenceFlow_BWOtzA + + + + + + + sequenceFlow_ofWWuw + + + + + + + + + + + + + sequenceFlow_S9LVXg + sequenceFlow_jAU3VA + sequenceFlow_iwMsIg + + + + + + + + + + + + + + + + + + + sequenceFlow_kS2fMA + + + +
- + - + - + - + - + - + - + - - + + - + - + - + - + - - - - - - - + + + - + - + - - - - - - - - - - + + - - - + + - + - + - - - - - - + - + - - - - - - - + + + + - - - + + + + - + - - - - - + - + - + - - - - + + + - + - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/workflow/rechnungseingang-de-1.2.40-debug_BP.bpmn b/workflow/rechnungseingang-de-1.2.40-debug_BP.bpmn index 83ab4a1..d747661 100644 --- a/workflow/rechnungseingang-de-1.2.40-debug_BP.bpmn +++ b/workflow/rechnungseingang-de-1.2.40-debug_BP.bpmn @@ -5547,8 +5547,8 @@ result.isValid=true; - - + + From 1acd22f334394215c155dca68fe1dd2ac53b58cc Mon Sep 17 00:00:00 2001 From: Ralph Soika Date: Mon, 24 Feb 2025 18:05:40 +0100 Subject: [PATCH 08/12] update businesspartner workflow --- Dockerfile | 3 +- doc/BUSINESSPARTNER.md | 10 + doc/METRICS.md | 35 ++ .../AGLAnalyticControllerDebitor.java | 10 +- .../BusinessPartnerController.java | 67 ++- .../api/BusinessPartnerRestService.java | 104 ----- .../xml/BusinessPartnerImportService.java | 2 +- .../src/main/webapp/layout/css/custom.css | 26 ++ .../forms/alexander/section_bp_dunnings.xhtml | 49 ++ .../alexander/section_bp_invoices_in.xhtml | 49 ++ .../alexander/section_bp_invoices_out.xhtml | 51 +++ pom.xml | 4 +- workflow/businesspartner-de-1.0.0.bpmn | 422 ++++++++++++++---- 13 files changed, 630 insertions(+), 202 deletions(-) create mode 100644 doc/METRICS.md delete mode 100644 office-alexander-logistics-app/src/main/java/com/alexanderlogistics/api/BusinessPartnerRestService.java create mode 100644 office-alexander-logistics-app/src/main/webapp/pages/workitems/forms/alexander/section_bp_dunnings.xhtml create mode 100644 office-alexander-logistics-app/src/main/webapp/pages/workitems/forms/alexander/section_bp_invoices_in.xhtml create mode 100644 office-alexander-logistics-app/src/main/webapp/pages/workitems/forms/alexander/section_bp_invoices_out.xhtml diff --git a/Dockerfile b/Dockerfile index f09b0f9..fcc2d39 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,5 @@ -FROM imixs/imixs-office-workflow:5.0.3-SNAPSHOT +FROM imixs/imixs-office-workflow:5.1.1-SNAPSHOT +#FROM imixs/imixs-office-workflow:5.0.3-SNAPSHOT # Setup debug configuration diff --git a/doc/BUSINESSPARTNER.md b/doc/BUSINESSPARTNER.md index 7a9b900..b6c7613 100644 --- a/doc/BUSINESSPARTNER.md +++ b/doc/BUSINESSPARTNER.md @@ -37,3 +37,13 @@ Alternativ kann im Backend über die EJB BusinessPartnerService nach bpid gesuch // oder ItemCollection bp= businessPartnerService.getBusinessPartnerByID("BP0001"); ``` + +# Daten Migration + +\_vendor_zip_code + +\_vendor_fax + + (type:cargosoftkreditor) AND ($modified:[20010101 TO 20250215]) + +Wir müssen gundsätzlich erstmal über all $nosnapshot = true eintragen! diff --git a/doc/METRICS.md b/doc/METRICS.md new file mode 100644 index 0000000..1b790d3 --- /dev/null +++ b/doc/METRICS.md @@ -0,0 +1,35 @@ +Curl + + $ curl -s http://localhost:9990/metrics + + + $ curl -s http://localhost:9990/metrics | grep BP4218 + +``` + +application_dbtr_balance{country="", currency="EUR",department="Import Zellulose/Holz",id="BP4218",name="Rayonier Advanced Materials",type="dbtr"} 60911.28 +application_dbtr_balance{country="US",currency="EUR",department="Import Zellulose/Holz",id="BP4218",name="Rayonier Advanced Materials",type="dbtr"} 257955.41 + + +application_dbtr_sales{country="", currency="EUR",department="Import Zellulose/Holz",id="BP4218",name="Rayonier Advanced Materials",type="dbtr"} 60911.28 +application_dbtr_sales{country="US", currency="EUR",department="Import Zellulose/Holz",id="BP4218",name="Rayonier Advanced Materials",type="dbtr"} 279028.91 + + +``` + +Rayonier Advanced Materials + +Debitor 74218 + +293.535,19 EUR + +Metric saldo: 318866.69 + +# Jetzt berechnen wir neu... + +https://alexander-logistics.office-workflow.de/api/metrics/dbtr/rebuild + +application_dbtr_balance{country="", currency="EUR",department="Import Zellulose/Holz",id="BP4218",name="Rayonier Advanced Materials",type="dbtr"} 60911.28 +application_dbtr_balance{country="US",currency="EUR",department="Import Zellulose/Holz",id="BP4218",name="Rayonier Advanced Materials",type="dbtr"} 364457.85 +application_dbtr_sales{country="", currency="EUR",department="Import Zellulose/Holz",id="BP4218",name="Rayonier Advanced Materials",type="dbtr"} 60911.28 +application_dbtr_sales{country="US", currency="EUR",department="Import Zellulose/Holz",id="BP4218",name="Rayonier Advanced Materials",type="dbtr"} 364457.85 diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticControllerDebitor.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticControllerDebitor.java index 81c382b..d879912 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticControllerDebitor.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticControllerDebitor.java @@ -84,11 +84,17 @@ public class AGLAnalyticControllerDebitor implements Serializable { String chartData = ""; public void onEvent(@Observes AnalyticEvent event) { - if (!"workitem".equals(event.getWorkitem().getType()) - || !event.getWorkitem().getModelVersion().startsWith("analyse-debitor")) { + if (!"workitem".equals(event.getWorkitem().getType())) { // no op return; } + + if (!event.getWorkitem().getModelVersion().startsWith("businesspartner") + && !event.getWorkitem().getModelVersion().startsWith("analyse-debitor")) { + // no op + return; + } + String dbtrNumber = event.getWorkitem().getItemValueString("dbtr.number"); String dbtrNumberLast = event.getWorkitem().getItemValueString("dbtr.number.last"); diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerController.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerController.java index 614f694..c1d3b9a 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerController.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerController.java @@ -36,6 +36,7 @@ import java.util.regex.Pattern; import org.imixs.workflow.ItemCollection; import org.imixs.workflow.engine.DocumentService; import org.imixs.workflow.exceptions.AccessDeniedException; +import org.imixs.workflow.exceptions.QueryException; import org.imixs.workflow.faces.data.WorkflowEvent; import jakarta.enterprise.context.ConversationScoped; @@ -62,6 +63,9 @@ public class BusinessPartnerController implements Serializable { private static final long serialVersionUID = 1L; protected List ibanList = null; + protected List invoicesOut = null; + protected List invoicesIn = null; + protected List dunnings = null; @Inject DocumentService documentService; @@ -73,6 +77,27 @@ public class BusinessPartnerController implements Serializable { private static Logger logger = Logger.getLogger(BusinessPartnerController.class.getName()); + public List getInvoicesOut() { + if (invoicesOut == null) { + invoicesOut = new ArrayList<>(); + } + return invoicesOut; + } + + public List getInvoicesIn() { + if (invoicesIn == null) { + invoicesIn = new ArrayList<>(); + } + return invoicesIn; + } + + public List getDunnings() { + if (dunnings == null) { + dunnings = new ArrayList<>(); + } + return dunnings; + } + /** * This method searches a text phrase within the list of DATEV kreditoren *

@@ -149,7 +174,7 @@ public class BusinessPartnerController implements Serializable { int eventType = workflowEvent.getEventType(); ItemCollection workitem = workflowEvent.getWorkitem(); - if (workitem == null) { + if (workitem == null || !workitem.getModelVersion().startsWith("businesspartner-")) { return; } @@ -157,6 +182,7 @@ public class BusinessPartnerController implements Serializable { if (WorkflowEvent.WORKITEM_CHANGED == eventType || WorkflowEvent.WORKITEM_CREATED == eventType) { // reset state ibanList = BusinessPartnerService.explodeBanks(workitem); + loadStats(workitem); } // before the workitem is saved we update the field txtOrderItems @@ -171,6 +197,45 @@ public class BusinessPartnerController implements Serializable { } + /** + * Hilfsmethode die die offenen Rechnungen läd. Wird von WorktiemChanged Event + * aufgerufen. + * + * @param workitem + */ + private void loadStats(ItemCollection workitem) { + + try { + // Ausgangsrechnungen + String dbtrNumber = workitem.getItemValueString("dbtr.number"); + if (!dbtrNumber.isEmpty()) { + if (dbtrNumber.startsWith("D")) { + dbtrNumber = dbtrNumber.substring(1); + } + String query = "(type:workitem) AND ($modelversion:rechnungsausgang-*) " + + " AND (dbtr.number:" + dbtrNumber + ")"; + invoicesOut = documentService.findStubs(query, 999, 0, "invoice.number", false); + + // Mahnungen + query = "(type:workitem) AND ($modelversion:mahnlauf-*) " + + " AND (dbtr.number:" + dbtrNumber + ")"; + dunnings = documentService.findStubs(query, 999, 0, "invoice.number", false); + } + + // Eingangsrechnungen + String cdtrNumber = workitem.getItemValueString("cdtr.number"); + if (!cdtrNumber.isEmpty()) { + String query = "(type:workitem) AND ($modelversion:rechnungseingang-*) " + + " AND (cdtr.number:" + cdtrNumber + ")"; + invoicesIn = documentService.findStubs(query, 999, 0, "invoice.number", false); + } + + } catch (QueryException e) { + logger.severe("failed to load stats: " + e.getMessage()); + } + + } + public List getIbanList() { return ibanList; } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/api/BusinessPartnerRestService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/api/BusinessPartnerRestService.java deleted file mode 100644 index bf0771c..0000000 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/api/BusinessPartnerRestService.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Imixs-Workflow - * - * Copyright (C) 2001-2020 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: - * https://www.imixs.org - * https://github.com/imixs/imixs-workflow - * - * Contributors: - * Imixs Software Solutions GmbH - Project Management - * Ralph Soika - Software Developer - */ - -package com.alexanderlogistics.api; - -import java.util.logging.Logger; - -import org.imixs.workflow.ItemCollection; -import org.imixs.workflow.exceptions.QueryException; -import org.imixs.workflow.jaxrs.DocumentRestService; - -import com.alexanderlogistics.BusinessPartnerService; - -import jakarta.ejb.Stateless; -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; -import jakarta.ws.rs.core.Response; - -/** - * BusinessPartnerRestService api endpoint '/businesspartner' provides methods - * to read and post business partner data - * - * @version 1.0 - * @author rsoika - * - */ -@Stateless -@Produces({ MediaType.TEXT_HTML, MediaType.APPLICATION_XHTML_XML, MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, - MediaType.TEXT_XML }) -@Path("/businesspartner") -public class BusinessPartnerRestService { - - @Inject - DocumentRestService documentRestService; - - @Inject - BusinessPartnerService businessPartnerService; - - private static Logger logger = Logger.getLogger(BusinessPartnerRestService.class.getName()); - - @GET - @Path("/ping") - public String ping() { - logger.info("GET ping"); - return "ping: " + System.currentTimeMillis(); - } - - /** - * Test method - * - * @param workflowgroup - * @param task - * @return - * @throws QueryException - */ - @GET - @Path("/{bpid}") - @Produces({ MediaType.TEXT_HTML }) - public Response getBusinessPartnerByID(@PathParam("bpid") String bpid, - @QueryParam("items") String items, - @QueryParam("format") String format) - throws QueryException { - - ItemCollection workitem = businessPartnerService.getBusinessPartnerByID(bpid); - if (workitem == null) { - // document not found - return Response.status(Response.Status.NOT_FOUND).build(); - } - - return documentRestService.convertResult(workitem, items, format); - - } - -} 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 8c238b6..286076f 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 @@ -123,7 +123,7 @@ public class BusinessPartnerImportService { return; } - logger.info("Verify business partner object...."); + logger.fine("Verify business partner object...."); ItemCollection importDoc = event.getDocument(); String name = event.getDocument().getItemValueString("name"); diff --git a/office-alexander-logistics-app/src/main/webapp/layout/css/custom.css b/office-alexander-logistics-app/src/main/webapp/layout/css/custom.css index 47eb6c4..007f3b9 100644 --- a/office-alexander-logistics-app/src/main/webapp/layout/css/custom.css +++ b/office-alexander-logistics-app/src/main/webapp/layout/css/custom.css @@ -7,4 +7,30 @@ !important; background-position: 4px 6px, center !important; padding-left: 22px !important; +} +.tooltip-link { + position: relative; + display: inline-block; +} + +.tooltip-text { + visibility: hidden; + width: 300px; + background-color: black; + color: #fff; + text-align: center; + border-radius: 5px; + padding: 5px; + position: absolute; + z-index: 1; + bottom: 125%; /* Position above the link */ + left: 50%; + margin-left: -150px; /* Center the tooltip */ + opacity: 0; + transition: opacity 0.3s; +} + +.tooltip-link:hover .tooltip-text { + visibility: visible; + opacity: 1; } \ No newline at end of file diff --git a/office-alexander-logistics-app/src/main/webapp/pages/workitems/forms/alexander/section_bp_dunnings.xhtml b/office-alexander-logistics-app/src/main/webapp/pages/workitems/forms/alexander/section_bp_dunnings.xhtml new file mode 100644 index 0000000..2fe56af --- /dev/null +++ b/office-alexander-logistics-app/src/main/webapp/pages/workitems/forms/alexander/section_bp_dunnings.xhtml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MahnungStatus#{message.modified}
+ #{dunning.item['$workflowsummary']} + + #{dunning.item['$workflowstatus']} + + #{message.by} #{userController.getUserName(dunning.item['$editor'])}
+
+ + + + + +
\ No newline at end of file diff --git a/office-alexander-logistics-app/src/main/webapp/pages/workitems/forms/alexander/section_bp_invoices_in.xhtml b/office-alexander-logistics-app/src/main/webapp/pages/workitems/forms/alexander/section_bp_invoices_in.xhtml new file mode 100644 index 0000000..c18ea6e --- /dev/null +++ b/office-alexander-logistics-app/src/main/webapp/pages/workitems/forms/alexander/section_bp_invoices_in.xhtml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RechnungStatus#{message.modified}
+ #{invoice.item['$workflowsummary']} + + #{invoice.item['$workflowstatus']} + + #{message.by} #{userController.getUserName(invoice.item['$editor'])}
+
+ + + + + +
\ No newline at end of file diff --git a/office-alexander-logistics-app/src/main/webapp/pages/workitems/forms/alexander/section_bp_invoices_out.xhtml b/office-alexander-logistics-app/src/main/webapp/pages/workitems/forms/alexander/section_bp_invoices_out.xhtml new file mode 100644 index 0000000..5068d04 --- /dev/null +++ b/office-alexander-logistics-app/src/main/webapp/pages/workitems/forms/alexander/section_bp_invoices_out.xhtml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RechnungStatus#{message.modified}
+ #{invoice.item['$workflowsummary']} + + + + + + #{invoice.item['$workflowstatus']} + + #{message.by} #{userController.getUserName(invoice.item['$editor'])}
+
+ + + + + +
\ No newline at end of file diff --git a/pom.xml b/pom.xml index f634655..4fa2f62 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,9 @@ 5.0.4 6.0.9 5.0.0 - 3.0.2 + + + 3.1.1-SNAPSHOT 3.0.3 2.0.2 6.0 diff --git a/workflow/businesspartner-de-1.0.0.bpmn b/workflow/businesspartner-de-1.0.0.bpmn index 99777e7..226b47c 100644 --- a/workflow/businesspartner-de-1.0.0.bpmn +++ b/workflow/businesspartner-de-1.0.0.bpmn @@ -60,6 +60,13 @@ event_i7yQVw gateway_uK0c6g event_6250fg + event_VM0fEQ + gateway_s0J53g + task_NohgrQ + event_AMs05g + event_feLZFQ + event_GTXFMw + event_u8OBFg @@ -134,6 +141,8 @@ sequenceFlow_T5L9aQ sequenceFlow_b4BcVA sequenceFlow_8O1EtQ + sequenceFlow_ShuXXA + sequenceFlow_03b60w @@ -170,6 +179,9 @@ + + + sequenceFlow_1mXO8A @@ -201,11 +213,14 @@ + + home]]> + sequenceFlow_1mXO8A sequenceFlow_b0Bj3Q - sequenceFlow_T5L9aQ + sequenceFlow_neSsFQ @@ -214,37 +229,59 @@ - - - - - - + - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - + - - - - - + + + + + - + + + + + + + + + + + + + + + + + + + + + + + ]]> @@ -352,22 +389,28 @@ - - - + + Partnermanagement]]> + - sequenceFlow_iwMsIg sequenceFlow_BHsRkQ + sequenceFlow_5pT4Tw - + + + + + + home]]> + sequenceFlow_pvIENQ @@ -384,9 +427,6 @@ - - - @@ -398,6 +438,14 @@ + + + Partnermanagement]]> + + + + + sequenceFlow_ofWWuw @@ -407,21 +455,18 @@ - + sequenceFlow_S9LVXg sequenceFlow_jAU3VA - sequenceFlow_iwMsIg + sequenceFlow_8BTEtw - - - @@ -438,6 +483,122 @@ + + + + Partnermanagement]]> + + + + + + + sequenceFlow_T5L9aQ + sequenceFlow_MPlPww + + sequenceFlow_rNZ3tw + + + + sequenceFlow_MPlPww + sequenceFlow_aH0vCQ + sequenceFlow_ShuXXA + + + + + + + + partner.name (partner.id)]]> + + + Adresse:
+partner.name
+partner.address
+partner.zip partner.city
+
+ +BPID: partner.id
+Kreditoren-Nr.: cdtr.number
+Debitoren-Nr.: dbtr.number
+]]>
+
+ + + + + + + + + + + + + + + + + + +
+ + sequenceFlow_9atLFA + sequenceFlow_8O1EtQ + sequenceFlow_aH0vCQ + sequenceFlow_neSsFQ + sequenceFlow_zd6RWA + sequenceFlow_rNZ3tw +
+ + + + + + + + + + + + + + + + + sequenceFlow_5pT4Tw + + + + + sequenceFlow_8BTEtw + + + + + + + + + + + sequenceFlow_03b60w + + + + + + + + sequenceFlow_zd6RWA + + + + + + + @@ -467,7 +628,7 @@ - + @@ -497,43 +658,43 @@ - + - + - + - + - - - + + + - - - - + + + + - + - + - - - + + + @@ -547,58 +708,58 @@ - + - + - + - + - + - - + + - - + + - + - + - - - + + + - + - + - - - + + + - + - - - + + + @@ -610,24 +771,19 @@ - - - - - - - + + - - - - + + + + - + @@ -640,6 +796,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 632f674bfa39aed453ec66c19b3711de98be7bbe Mon Sep 17 00:00:00 2001 From: Ralph Soika Date: Mon, 24 Feb 2025 18:58:43 +0100 Subject: [PATCH 09/12] started version 1.3.2 --- office-alexander-logistics-app/pom.xml | 2 +- pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/office-alexander-logistics-app/pom.xml b/office-alexander-logistics-app/pom.xml index 3809eb2..f427508 100644 --- a/office-alexander-logistics-app/pom.xml +++ b/office-alexander-logistics-app/pom.xml @@ -6,7 +6,7 @@ office-alexander-logistics com.alexander-logistics - 1.3.1 + 1.3.2 office-alexander-logistics-app war diff --git a/pom.xml b/pom.xml index 4fa2f62..b6f3eab 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.alexander-logistics office-alexander-logistics - 1.3.1 + 1.3.2 pom Imixs Office Workflow - Custom Build @@ -37,7 +37,7 @@ 9.9.1-4 2.0.26 - 1.1.1-SNAPSHOT + 1.1.1 src/main/webapp From 021e7cec344a839be62d66817325cf861d85f3de Mon Sep 17 00:00:00 2001 From: Ralph Soika Date: Mon, 24 Feb 2025 18:59:02 +0100 Subject: [PATCH 10/12] business partner import service --- doc/METRICS.md | 12 + docker-compose.yml | 24 +- .../BusinessPartnerService.java | 78 +- .../com/alexanderlogistics/InvoiceUtil.java | 27 - .../api/CargosoftMigrationRestService.java | 105 +++ .../metrics/MetricCreditorRestService.java | 183 ---- .../metrics/MetricCreditorService.java | 394 -------- .../metrics/MetricDataService.java | 103 -- .../metrics/MetricDebitorRestService.java | 180 ---- .../metrics/MetricDebitorService.java | 382 -------- workflow/businesspartner-en-1.0.0.bpmn | 883 ++++++++++++++++++ 11 files changed, 1024 insertions(+), 1347 deletions(-) delete mode 100644 office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorRestService.java delete mode 100644 office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorService.java delete mode 100644 office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDataService.java delete mode 100644 office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorRestService.java delete mode 100644 office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorService.java create mode 100644 workflow/businesspartner-en-1.0.0.bpmn diff --git a/doc/METRICS.md b/doc/METRICS.md index 1b790d3..b7326b1 100644 --- a/doc/METRICS.md +++ b/doc/METRICS.md @@ -1,3 +1,15 @@ +# Metrics + +**Konzept Verworfen!** + +## Background: + +https://blog.imixs.org/2025/02/02/business-intelligence-built-on-metrics-part-ii/ + +**Die Implementierung war Teil von Version 1.3.1!** + +## Testing + Curl $ curl -s http://localhost:9990/metrics diff --git a/docker-compose.yml b/docker-compose.yml index e2a4687..5c1c8b4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -44,7 +44,7 @@ services: LLM_SERVICE_ENDPOINT_USER: "admin" LLM_SERVICE_ENDPOINT_PASSWORD: "imixs4.null" - METRICS_ENABLED: "true" + METRICS_ENABLED: "false" ports: - "8080:8080" @@ -105,18 +105,18 @@ services: EXIM_PASSWORD: "www149.your-server.de:webmaster@imixs.com:$MAILPASSWORD" EXIM_ALLOWED_SENDERS: "10.0.0.0/8:172.18.0.0/12:192.168.0.0/16" - prometheus: - image: prom/prometheus:latest - ports: - - "9090:9090" - volumes: - - ./docker/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml - - prometheusdata:/prometheus/ + # prometheus: + # image: prom/prometheus:latest + # ports: + # - "9090:9090" + # volumes: + # - ./docker/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml + # - prometheusdata:/prometheus/ - grafana: - image: grafana/grafana:latest - ports: - - "3000:3000" + # grafana: + # image: grafana/grafana:latest + # ports: + # - "3000:3000" volumes: dbdata: 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 8533054..9addc98 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 @@ -27,7 +27,6 @@ package com.alexanderlogistics; -import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; @@ -37,11 +36,9 @@ import java.util.Optional; import java.util.logging.Logger; import org.eclipse.microprofile.config.inject.ConfigProperty; -import org.imixs.melman.DocumentClient; -import org.imixs.melman.FormAuthenticator; -import org.imixs.melman.RestAPIException; import org.imixs.workflow.ItemCollection; import org.imixs.workflow.ItemCollectionComparator; +import org.imixs.workflow.engine.DocumentService; import org.imixs.workflow.engine.index.SchemaService; import jakarta.annotation.PostConstruct; @@ -77,8 +74,6 @@ public class BusinessPartnerService { private static Logger logger = Logger.getLogger(BusinessPartnerService.class.getName()); - private FormAuthenticator formAuthenticator; - @Inject @ConfigProperty(name = WORKFLOW_SERVICE_ENDPOINT) Optional workflowServiceEndpoint; @@ -91,8 +86,8 @@ public class BusinessPartnerService { @ConfigProperty(name = WORKFLOW_SERVICE_PASSWORD) Optional workflowServicePassword; - // @Inject - // DocumentService documentService; + @Inject + DocumentService documentService; @Inject SchemaService schemaService; @@ -102,12 +97,7 @@ public class BusinessPartnerService { */ @PostConstruct public void init() { - // init clients - try { - initAuthenticator(); - } catch (RestAPIException e) { - logger.severe("Failed to initalize Rest Clients: " + e.getMessage()); - } + } /** @@ -118,27 +108,20 @@ public class BusinessPartnerService { * @return - matching business partner */ public ItemCollection getBusinessPartnerByID(String bpid) { - long l = System.currentTimeMillis(); if (bpid == null || bpid.isEmpty()) { return null; } List result; try { - String sQuery = "(type:\"workitem\") AND ($modelversion:businesspartner-*)"; + String sQuery = "(type:\"workitem\" OR type:\"workitemarchive\" ) AND ($modelversion:businesspartner-*)"; sQuery += " AND (name:" + bpid.trim() + ")"; logger.finest("SearchQuery= " + sQuery); - - // register client - DocumentClient documentClient = getDocumentClient(); - documentClient.setPageSize(MAX_SEARCH_RESULT); - documentClient.setPageSize(1); - result = documentClient.searchDocuments(sQuery); + result = documentService.find(sQuery, 1, 0); if (result.size() > 0) { - logger.info("🕐 get BusinessPartner by id in " + (System.currentTimeMillis() - l) + "ms"); return result.get(0); } - } catch (RestAPIException | UnsupportedEncodingException e) { - logger.warning("Failed to get BusinessPartner: " + e.getMessage()); + } catch (Exception e) { + logger.warning(" lucene error - " + e.getMessage()); } return null; } @@ -151,7 +134,7 @@ public class BusinessPartnerService { * @return - list of matching business partners */ public List search(String phrase) { - long l = System.currentTimeMillis(); + List searchResult = new ArrayList(); if (phrase == null || phrase.isEmpty()) { return searchResult; @@ -161,45 +144,21 @@ public class BusinessPartnerService { phrase = phrase.trim(); // phrase = LuceneSearchService.escapeSearchTerm(phrase); phrase = schemaService.normalizeSearchTerm(phrase); - String sQuery = "(type:\"workitem\") AND ($modelversion:businesspartner-*)"; + String sQuery = "(type:\"workitem\" OR type:\"workitemarchive\") AND ($modelversion:businesspartner-*)"; sQuery += " AND (" + phrase + "*)"; logger.finest("SearchQuery= " + sQuery); - // register client - DocumentClient documentClient = getDocumentClient(); - documentClient.setPageSize(MAX_SEARCH_RESULT); - searchResult = documentClient.searchDocuments(sQuery); - - } catch (RestAPIException | UnsupportedEncodingException e) { + searchResult = documentService.find(sQuery, MAX_SEARCH_RESULT, 0); + } catch (Exception e) { logger.warning(" lucene error - " + e.getMessage()); - formAuthenticator = null; } // sort by txtname.. Collections.sort(searchResult, new ItemCollectionComparator("$workflowsummary", true)); - - logger.info("🕐 BusinessPartner search: " + searchResult.size() + " entries in " - + (System.currentTimeMillis() - l) + "ms"); return searchResult; } - private DocumentClient getDocumentClient() { - DocumentClient documentClient = null; - try { - if (formAuthenticator == null) { - initAuthenticator(); - } - documentClient = new DocumentClient(workflowServiceEndpoint.get()); - documentClient.registerClientRequestFilter(formAuthenticator); - } catch (RestAPIException e) { - logger.warning("Failed to init RestClient: " + e.getMessage()); - formAuthenticator = null; - } - - return documentClient; - } - /** * Packt die Liste der Bank Details (ItemCollections) in ein Workitem * @@ -262,17 +221,4 @@ public class BusinessPartnerService { return result; } - /** - * Helper method to initalize a Melman FormAuthenticator - * - * @throws RestAPIException - */ - public void initAuthenticator() throws RestAPIException { - logger.info("⚡ Init FormAuthenticator..."); - - // form authenticator - formAuthenticator = new FormAuthenticator(workflowServiceEndpoint.get(), workflowServiceUser.get(), - workflowServicePassword.get()); - - } } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceUtil.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceUtil.java index b6bc313..24cdc3e 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceUtil.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceUtil.java @@ -8,9 +8,6 @@ import java.util.Map; import java.util.regex.Pattern; import org.imixs.workflow.ItemCollection; -import org.imixs.workflow.exceptions.PluginException; - -import com.alexanderlogistics.metrics.MetricDataService; /** * Hilfsmethoden für die Berechnung und Validierung von Ein und @@ -185,30 +182,6 @@ public class InvoiceUtil { } - /** - * Diese Hilfsmethode berechnet die BusinessPartnerID aus einer Invoice - * - * @param Invoice - * @return - * @throws PluginException - */ - public static String getBPId(ItemCollection invoice) throws PluginException { - String key = ""; - if (isCreditorInvoice(invoice)) { - // key ist cdtr.number - key = invoice.getItemValueString("cdtr.number"); - } else { - // key ist dbtr.number - key = invoice.getItemValueString("dbtr.number"); - } - if (key.isEmpty()) { - throw new PluginException(MetricDataService.class.getName(), ERROR_INVALID_INVOICEDATA, - "Failed to create metrics - missing dbtr/cdtr number!"); - } - - return InvoiceUtil.buildBPID(key); - } - /** * Diese Hilfmethode berechnet die BusinessPartner Namen aus einer Invoice * 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 4a20ba9..5de1489 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 @@ -63,11 +63,15 @@ 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.KreditorDebitorService; import com.alexanderlogistics.mahnlauf.MahnlaufService; import com.alexanderlogistics.xml.CargosoftXMLInvoiceImportService; import jakarta.ejb.Stateless; +import jakarta.ejb.TransactionAttribute; +import jakarta.ejb.TransactionAttributeType; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @@ -112,6 +116,9 @@ public class CargosoftMigrationRestService implements Serializable { @Inject TeamService teamService; + @Inject + BusinessPartnerService businessPartnerService; + private static Logger logger = Logger.getLogger(CargosoftMigrationRestService.class.getName()); public CargosoftMigrationRestService() { @@ -641,6 +648,98 @@ public class CargosoftMigrationRestService implements Serializable { } } + /** + * 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. + * + * @return + * @throws QueryException + * @throws AccessDeniedException + * @throws ProcessingErrorException + * @throws PluginException + * @throws ModelException + */ + @GET + @Path("/bp-import") + @Produces({ MediaType.TEXT_PLAIN }) + public String importBusinessPartner() + throws QueryException, AccessDeniedException, ProcessingErrorException, PluginException, ModelException { + StringBuffer messageBuffer = new StringBuffer(); + int MAX_COUNT = 1000; + String query = "(type:" + KreditorDebitorService.TYPE_CARGOSOFTKREDITOR + ")"; + int updates = 0; + long l = System.currentTimeMillis(); + int batchSize = 500; + int totalObjects = 0; + log("├── Migrate " + KreditorDebitorService.TYPE_CARGOSOFTKREDITOR, messageBuffer); + + // Gesamtanzahl ermitteln + int totalCount = documentService.count(query); + log("│   ├── found " + totalCount + " " + KreditorDebitorService.TYPE_CARGOSOFTKREDITOR + " 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); + + for (ItemCollection cargosoftItemCol : cargosoftDataList) { + totalObjects++; + if (verifyBusinessPartner(cargosoftItemCol, messageBuffer)) { + updates++; + } + if (updates >= MAX_COUNT) + break; + } + + // Fortschritt loggen + log("│ ├── Processed page " + (pageIndex + 1) + " of " + totalPages + + " (" + updates + " total updates)", messageBuffer); + // Optional: Kurze Pause nach jedem 5. Batch + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + if (updates >= MAX_COUNT) + break; + + } + long duration = System.currentTimeMillis() - l; + double objectsPerSecond = totalObjects / (duration / 1000.0); + log("├── Successfully imported " + updates + " business partner objects in " + + duration + "ms (" + String.format("%.1f", objectsPerSecond) + " objects/sec)", messageBuffer); + + return messageBuffer.toString(); + } + + /** + * Hilfsmethode speichert eine cargoosft kreditor object... + * + * @param bpID + * @param messageBuffer + * @return + */ + @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) + public boolean verifyBusinessPartner(ItemCollection cargosoftItemCol, StringBuffer messageBuffer) { + String vendorNummer = cargosoftItemCol.getItemValueString("_vendor_num"); + String bpID = InvoiceUtil.buildBPID(vendorNummer); + ItemCollection businessPartner = businessPartnerService.getBusinessPartnerByID(bpID); + if (businessPartner == null) { + documentService.save(cargosoftItemCol); + logger.finest("│   │   ├── import " + bpID); + return true; + } + return false; + } + /** * Erzeugt einen XML Baum aus dem XML Raw Daten * @@ -721,4 +820,10 @@ public class CargosoftMigrationRestService implements Serializable { } } + + 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/metrics/MetricCreditorRestService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorRestService.java deleted file mode 100644 index bbb0fe3..0000000 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorRestService.java +++ /dev/null @@ -1,183 +0,0 @@ -package com.alexanderlogistics.metrics; - -import java.util.List; -import java.util.logging.Logger; - -import org.imixs.workflow.ItemCollection; -import org.imixs.workflow.engine.DocumentService; -import org.imixs.workflow.exceptions.PluginException; -import org.imixs.workflow.exceptions.QueryException; - -import com.alexanderlogistics.InvoiceUtil; - -import jakarta.ejb.Stateless; -import jakarta.ejb.TransactionAttribute; -import jakarta.ejb.TransactionAttributeType; -import jakarta.inject.Inject; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; - -@Path("metrics/cdtr") -@Stateless -public class MetricCreditorRestService { - - private static Logger logger = Logger.getLogger(MetricCreditorRestService.class.getName()); - - @Inject - DocumentService documentService; - - @Inject - MetricCreditorService metricCreditorService; - - @Inject - MetricDataService metricDataService; - - @GET - @Path("/ping") - @Produces({ MediaType.TEXT_PLAIN }) - public String ping() { - logger.info("GET ping"); - return "ping: " + System.currentTimeMillis(); - } - - /** - * This method initializes the metrics for all creditors with open invoices. - * - * The method deletes all existing metrics and creates new metric entires for - * each creditor. - * - * @return - */ - @GET - @Path("/rebuild") - @Produces({ MediaType.TEXT_PLAIN }) - public Response rebuildMetrics() { - StringBuffer messageBuffer = new StringBuffer(); - long l = System.currentTimeMillis(); - log("├── rebuild cdtr metrics...", messageBuffer); - try { - log("│   ├── delete all metrics", messageBuffer); - metricDataService.deleteAllMetrics(MetricCreditorService.TYPE_METRIC_CREDITOR); - // first clear the metric cache - metricCreditorService.reset(); - log("│   ├── reset metric cache", messageBuffer); - - computeMetrics(messageBuffer); - - String message = "├── rebuild cdtr metrics completed in " - + (System.currentTimeMillis() - l) - + "ms"; - log(message, messageBuffer); - return Response.ok().entity(messageBuffer.toString()).build(); - - } catch (Exception e) { - e.printStackTrace(); - log("Failed to initialize metrics: " + e.getMessage(), messageBuffer); - return Response.serverError() - .entity(messageBuffer.toString() + e.getMessage()) - .build(); - } - } - - /** - * Diese Methode berechnet alle Metriken auf basis der existierenden Rechnungen - * neu - * - * @throws QueryException - * @throws InterruptedException - * @throws PluginException - * - */ - public void computeMetrics(StringBuffer messageBuffer) - throws QueryException, InterruptedException, PluginException { - long l = System.currentTimeMillis(); - int batchSize = 500; - int totalInvoices = 0; - log("│   │   ├── recalculate metrics...", messageBuffer); - - String query = "($modelversion:rechnungseingang-* OR $modelversion:gutschriftabgleich-*) " + - " AND type:workitem"; - // Gesamtanzahl ermitteln - int totalCount = documentService.count(query); - log("│   │   ├── found " + totalCount + " open invoices", 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++) { - - totalInvoices = totalInvoices + computeInvoiceMetrics(query, batchSize, pageIndex); - // List invoices = documentService.find(query, batchSize, - // pageIndex); - - // for (ItemCollection invoice : invoices) { - // try { - // ItemCollection metricData = - // metricCreditorService.getMetricByInvoice(invoice); - // // Jetzt Rechnung addieren - // metricCreditorService.addInvoice(metricData, invoice); - // logger.fine("│   │   │   ├── update metric " + InvoiceUtil.getBPId(invoice)); - // metricCreditorService.updateMetric(metricData, true); - // totalInvoices++; - // } catch (PluginException e) { - // // invalid invoice - e.g. no dbtr. number - // } - // } - - // Fortschritt loggen - log("│   │ ├── Processed page " + (pageIndex + 1) + " of " + totalPages + - " (" + totalInvoices + " of " + totalCount + " invoices)", messageBuffer); - // Optional: Kurze Pause nach jedem 5. Batch - Thread.sleep(100); - - } - long duration = System.currentTimeMillis() - l; - double invoicesPerSecond = totalInvoices / (duration / 1000.0); - log("│   │   ├── Successfully processed " + totalInvoices + " invoices in " + - duration + "ms (" + String.format("%.1f", invoicesPerSecond) + " invoices/sec)", messageBuffer); - log("│   │   ├── Updated " + metricCreditorService.getMetricCount() + " metrics.", messageBuffer); - - } - - /** - * Helper method runs in new transaction - * - * @param query - * @param batchSize - * @param pageIndex - * @throws PluginException - * @throws QueryException - */ - @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) - public int computeInvoiceMetrics(String query, int batchSize, int pageIndex) - throws PluginException, QueryException { - - int updates = 0; - List invoices = documentService.find(query, batchSize, pageIndex); - - for (ItemCollection invoice : invoices) { - try { - ItemCollection metricData = metricCreditorService.getMetricByInvoice(invoice); - // Jetzt Rechnung addieren - metricCreditorService.addInvoice(metricData, invoice); - logger.fine("│   │   │   ├── update metric " + InvoiceUtil.getBPId(invoice)); - metricCreditorService.updateMetric(metricData, true); - updates++; - } catch (PluginException e) { - // invalid invoice - e.g. no dbtr. number - } - } - return updates; - } - - 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/metrics/MetricCreditorService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorService.java deleted file mode 100644 index 3da4b99..0000000 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricCreditorService.java +++ /dev/null @@ -1,394 +0,0 @@ -package com.alexanderlogistics.metrics; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Logger; - -import org.eclipse.microprofile.config.inject.ConfigProperty; -import org.eclipse.microprofile.metrics.Metadata; -import org.eclipse.microprofile.metrics.MetricRegistry; -import org.eclipse.microprofile.metrics.Tag; -import org.eclipse.microprofile.metrics.annotation.RegistryScope; -import org.imixs.archive.core.SnapshotService; -import org.imixs.workflow.ItemCollection; -import org.imixs.workflow.engine.DocumentService; -import org.imixs.workflow.engine.ProcessingEvent; -import org.imixs.workflow.engine.SetupEvent; -import org.imixs.workflow.exceptions.PluginException; - -import com.alexanderlogistics.InvoiceUtil; -import com.alexanderlogistics.KreditorDebitorService; - -import jakarta.annotation.security.DeclareRoles; -import jakarta.annotation.security.RolesAllowed; -import jakarta.annotation.security.RunAs; -import jakarta.ejb.Singleton; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.event.Observes; -import jakarta.inject.Inject; - -/** - * Dieser Service reagiert auch ProcessingEvents und speichert/aktualisiert die - * zugehörige Creditor Metric Entity (type=metric.creditor). - *

- * Beim Rechnungseingang gilt eine Rechnung als Bezahlt wenn diese einen finalen - * Status erreicht hat (>=5800) - *

- * Der Service liest in einem AFTER_PROCESS Event den alten invoice.saldo aus. - * Hierzu wird die Rechnung in einer neuen Transaktion geladen was einem - * 'Dirty-Read' entspricht. Dadurch kennt die Methode den letzten saldo der - * Rechnung. Hat sich nun der aktuelle Saldo geändert, aktualisiert der Serivce - * die entsprechende Metric. - * - * - */ -@DeclareRoles({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS", - "org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS", - "org.imixs.ACCESSLEVEL.MANAGERACCESS" }) -@RolesAllowed({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS", - "org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS", - "org.imixs.ACCESSLEVEL.MANAGERACCESS" }) -@RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS") -@Singleton -@ApplicationScoped -public class MetricCreditorService { - - private static Logger logger = Logger.getLogger(MetricCreditorService.class.getName()); - private final ConcurrentHashMap metricCache = new ConcurrentHashMap<>(); - private final Set registeredGauges = ConcurrentHashMap.newKeySet(); - - public static final String TYPE_METRIC_CREDITOR = "metric.creditor"; - public static final String ITEM_INVOICE_TOTAL = "invoice.total"; - public static final String ITEM_INVOICE_SALDO = "invoice.saldo"; - public static final String ITEM_METRIC_BALANCE = "invoice.balance"; - public static final String ITEM_METRIC_SALES = "invoice.sales"; - - @Inject - @RegistryScope(scope = MetricRegistry.APPLICATION_SCOPE) - MetricRegistry metricRegistry; - - @Inject - DocumentService documentService; - - @Inject - KreditorDebitorService kreditorDebitorService; - - @Inject - MetricDataService metricDataService; - - @Inject - @ConfigProperty(name = "metrics.enabled", defaultValue = "false") - private boolean metricsEnabled; - - /** - * Init all metrics during setup. Called by the Imixs SetupService - */ - public void initializeMetrics(@Observes SetupEvent setupEvent) { - if (!metricsEnabled) { - return; - } - - long l = System.currentTimeMillis(); - int batchSize = 500; - int totalMetrics = 0; - - try { - logger.info("├── Initializing creditor metrics from database..."); - String query = "(type:" + TYPE_METRIC_CREDITOR + ")"; - - // Gesamtanzahl ermitteln - int totalCount = documentService.count(query); - - // 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 metrics = documentService.find(query, batchSize, pageIndex); - - for (ItemCollection metric : metrics) { - updateMetric(metric, false); - totalMetrics++; - } - // Fortschritt loggen - logger.info("│ ├── Processed page " + (pageIndex + 1) + " of " + totalPages + - " (" + totalMetrics + " of " + totalCount + " metrics)"); - // Optional: Kurze Pause nach jedem Batch - Thread.sleep(100); - } - - long duration = System.currentTimeMillis() - l; - double metricsPerSecond = totalMetrics / (duration / 1000.0); - - logger.info("├── Successfully initialized " + totalMetrics + " metrics in " + - duration + "ms (" + String.format("%.1f", metricsPerSecond) + " metrics/sec)"); - - } catch (Exception e) { - logger.warning("Failed to initialize metrics: " + e.getMessage()); - } - } - - /** - * Reset the internal metricCache and clears all registered Gauges. - */ - public void reset() { - metricCache.clear(); - registeredGauges.clear(); - } - - public long getMetricCount() { - return metricCache.size(); - } - - /** - * Process Metric only if some data has changed.... - * - * @param processingEvent - * @throws PluginException - */ - public void onProcessingEvent(@Observes ProcessingEvent processingEvent) { - long l = System.currentTimeMillis(); - if (!metricsEnabled) { - return; - } - - ItemCollection invoice = processingEvent.getDocument(); - if (!InvoiceUtil.isCreditorInvoice(invoice)) { - // skip event - return; - } - - // update metric and the metric cache - if (processingEvent.getEventType() == ProcessingEvent.AFTER_PROCESS) { - // load the last invoice metric and reduce the saldo... - ItemCollection lastInvoice = metricDataService.readDirtyWorkitem(invoice.getUniqueID()); - if (lastInvoice != null) { - try { - ItemCollection lastMetricData = getMetricByInvoice(lastInvoice); - // update last metric only if exists... - if (!isNewMetric(lastMetricData)) { - subtractInvoice(lastMetricData, lastInvoice); - updateMetric(lastMetricData, true); - } - } catch (PluginException e) { - // invalid invoice - e.g. no cdtr. number - } - } - - try { - // load the invoice metric and add the saldo... - ItemCollection metricData = getMetricByInvoice(invoice); - // Saldo-Berechnung - addInvoice(metricData, invoice); - updateMetric(metricData, true); - logger.info("Metric cdtr update took " + (System.currentTimeMillis() - l) + "ms"); - } catch (PluginException e) { - // invalid invoice - e.g. no cdtr. number - } - } - - } - - /** - * Returns true if the metric is not yet registered. This means we do not have - * sales or balances for this metric - * - * @param metricData - * @return - */ - public boolean isNewMetric(ItemCollection metricData) { - return (!metricCache.containsKey(metricData.getItemValueString("name"))); - } - - /** - * Returns the corresponding metric data object for an invoice. The method uses - * an internal cache. If the metric was not yet cached the method loads the - * metric from the database. If not metric exists in the database the method - * automatically creates a new metric data object. - * - * The method throws a PluginException if no metric can be build from this - * invoice (e.g. missing cdtr.number). - * - * @param invoice - * @return - * @throws PluginException - */ - public ItemCollection getMetricByInvoice(ItemCollection invoice) throws PluginException { - if (invoice == null) { - return null; - } - String metricKey = MetricDataService.buildKeyByInvoice(invoice); - ItemCollection metricData = metricCache.get(metricKey); - // if metric still null we create a new metric data object. - if (metricData == null) { - metricData = createMetricData(invoice); - } - return metricData; - } - - /** - * Returns a metric data object by key - * - * @param key - * @return - */ - public ItemCollection getMetric(String key) { - return metricCache.get(key); - } - - /** - * Returns a list with all cached metric keys. - * The method returns an unmodifiable list to prevent modifications. - * - * @return - */ - public List getMetricKeys() { - return Collections.unmodifiableList(new ArrayList<>(metricCache.keySet())); - } - - /** - * Creates an empty Creditor Metric Data Object (ItemCollection) - *

- * The ItemCollection stores the name and number and all categories. - * A new metric object does not yet have the items 'invoice.saldo' and - * 'invoice.total' - * - * @param invoice - invoice ItemCollection - * @return - * @throws PluginException - */ - private ItemCollection createMetricData(ItemCollection invoice) throws PluginException { - if (invoice == null) { - return null; - } - String key = MetricDataService.buildKeyByInvoice(invoice); - ItemCollection metricData = new ItemCollection(); - metricData.setType(TYPE_METRIC_CREDITOR); - metricData.setItemValue("name", key); - metricData.setItemValue(SnapshotService.NOSNAPSHOT, true); - metricData.setItemValue("bp.id", InvoiceUtil.getBPId(invoice)); - metricData.setItemValue("bp.name", InvoiceUtil.getBPName(invoice)); - metricData.setItemValue("country", invoice.getItemValueString("invoice.country")); - metricData.setItemValue("currency", invoice.getItemValueString("invoice.currency")); - metricData.setItemValue("department", invoice.getItemValueString("space.name")); - - return metricData; - } - - /** - * This method registers and updates the metric meta data objects based on a - * given metricData object. Optional the metric data object is persisted. - * - * @param metricData - the metricData ItemCollection - * @param persist - if true the metricData entity will be persisted - */ - public void updateMetric(ItemCollection metricData, boolean persist) { - String metricKey = metricData.getItemValueString("name"); - - // Cache aktualisieren - metricCache.put(metricKey, metricData); - - // In Datenbank persistieren - if (persist) { - metricData.setItemValue(SnapshotService.NOSNAPSHOT, true); - documentService.save(metricData); - } - - // Prüfen ob Gauge bereits registriert ist - if (registeredGauges.add(metricKey)) { // returns true newly added - String bpName = metricData.getItemValueString("bp.name"); - String bpId = metricData.getItemValueString("bp.id"); - String country = metricData.getItemValueString("country"); - String department = metricData.getItemValueString("department"); - String currency = metricData.getItemValueString("currency"); - - List tags = new ArrayList<>(); - tags.add(new Tag("type", "cdtr")); - tags.add(new Tag("id", bpId)); - tags.add(new Tag("name", bpName)); - tags.add(new Tag("country", country)); - tags.add(new Tag("currency", currency)); - tags.add(new Tag("department", department)); - - // Saldo Gauge - Metadata balanceMetadata = Metadata.builder() - .withName("cdtr.balance") - .withDescription("Creditor Balance") - .build(); - metricRegistry.gauge(balanceMetadata, - () -> metricCache.get(metricKey).getItemValueDouble(ITEM_METRIC_BALANCE), - tags.toArray(new Tag[0])); - - // Umsatz Gauge - Metadata revenueMetadata = Metadata.builder() - .withName("cdtr.sales") - .withDescription("Creditor Sales") - .build(); - metricRegistry.gauge(revenueMetadata, - () -> metricCache.get(metricKey).getItemValueDouble(ITEM_METRIC_SALES), - tags.toArray(new Tag[0])); - } - } - - // /** - // * Helper Method that refreshes all gauges. The method is called by the - // * RestService during a rebuild. - // */ - // public void updateAllMetrics() { - // List keys = getMetricKeys(); - // for (String hashKey : keys) { - // ItemCollection metricData = getMetric(hashKey); - // updateMetric(metricData, false); - // } - // } - - /** - * Addiert den saldo einer Invoice zu einem metricData object - * - * @param metricData - * @param invoice - */ - public void addInvoice(ItemCollection metricData, ItemCollection invoice) { - if (metricData == null || invoice == null) { - return; - } - double invoiceSaldo = invoice.getItemValueDouble(ITEM_INVOICE_TOTAL); - double invoiceTotal = invoice.getItemValueDouble(ITEM_INVOICE_TOTAL); - if (!"workitem".equals(invoice.getType()) || invoice.getTaskID() >= 5800) { - // vorgang ist archiviert oder gelöscht worden => saldo = 0! - invoiceSaldo = 0.0; - } - // update balance - double lastBalance = metricData.getItemValueDouble(ITEM_METRIC_BALANCE); - metricData.setItemValue(ITEM_METRIC_BALANCE, InvoiceUtil.round(lastBalance + invoiceSaldo)); - - // update sales - double lastTotal = metricData.getItemValueDouble(ITEM_METRIC_SALES); - metricData.setItemValue(ITEM_METRIC_SALES, InvoiceUtil.round(lastTotal + invoiceTotal)); - - } - - public void subtractInvoice(ItemCollection metricData, ItemCollection invoice) { - if (metricData == null || invoice == null) { - return; - } - double invoiceTotal = invoice.getItemValueDouble(ITEM_INVOICE_TOTAL); - double invoiceSaldo = invoice.getItemValueDouble(ITEM_INVOICE_TOTAL); - if (!"workitem".equals(invoice.getType()) || invoice.getTaskID() >= 5800) { - // vorgang ist archiviert oder gelöscht worden => saldo = 0! - invoiceSaldo = 0.0; - } - // subtract only if metric saldo exists - double lastBalance = metricData.getItemValueDouble(ITEM_METRIC_BALANCE); - metricData.setItemValue(ITEM_METRIC_BALANCE, InvoiceUtil.round(lastBalance - invoiceSaldo)); - - // Neue Umsatz Logik - double lastSales = metricData.getItemValueDouble(ITEM_METRIC_SALES); - metricData.setItemValue(ITEM_METRIC_SALES, InvoiceUtil.round(lastSales - invoiceTotal)); - - } - -} \ No newline at end of file diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDataService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDataService.java deleted file mode 100644 index f49dd31..0000000 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDataService.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.alexanderlogistics.metrics; - -import java.util.List; -import java.util.Objects; - -import org.imixs.workflow.ItemCollection; -import org.imixs.workflow.engine.DocumentService; -import org.imixs.workflow.engine.index.SearchService; -import org.imixs.workflow.exceptions.PluginException; -import org.imixs.workflow.exceptions.QueryException; - -import com.alexanderlogistics.InvoiceUtil; - -import jakarta.annotation.security.DeclareRoles; -import jakarta.annotation.security.RunAs; -import jakarta.ejb.Stateless; -import jakarta.ejb.TransactionAttribute; -import jakarta.ejb.TransactionAttributeType; -import jakarta.inject.Inject; - -/** - * The MetricDataService provides methods to lookup and update metric data - * objects. - * The service runs with manager access. - * - * - */ -@DeclareRoles({ "org.imixs.ACCESSLEVEL.MANAGERACCESS" }) -@RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS") -@Stateless -public class MetricDataService { - - @Inject - DocumentService documentService; - - /** - * This helper method reads a 'dirty' workitem in a new transaction. This is - * used for calculating the new metric values - * - * @param uniqueId - */ - @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) - public ItemCollection readDirtyWorkitem(String uniqueId) { - ItemCollection dirtyInvoice = documentService.load(uniqueId); - return dirtyInvoice; - } - - /** - * Builds the metric hash key by the invoice attributes. The returned key can be - * used for caching the metric. - * - * The metric key does not include the dbtr/cdtr name as this name is not - * relevant for building a hashkey. - * - * @param invoice - The invoice ItemCollection containing the necessary - * attributes - * @return A unique hash string based on the invoice attributes - * @throws PluginException - * @throws IllegalArgumentException if the invoice is null - */ - public static String buildKeyByInvoice(ItemCollection invoice) throws PluginException { - // Validate input - Objects.requireNonNull(invoice, "Invoice must not be null"); - - String bpNumber = InvoiceUtil.getBPId(invoice); - String country = invoice.getItemValueString("invoice.country"); - String currency = invoice.getItemValueString("invoice.currency"); - String department = invoice.getItemValueString("space.name"); - - // Concatenate the values and create a hash - String combinedValue = String.format("%s::%s::%s::%s", - bpNumber, - country, - currency, - department); - - String hash = String.valueOf(combinedValue.hashCode()); - // a hash can start with '-' which we need to avoid and create a alphanumeric - // key instead! - return "HASH" + hash; - } - - /** - * This method deletes all metrics - * - * @throws PluginException - * - */ - @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) - public void deleteAllMetrics(String metricType) throws PluginException { - try { - String query = "(type:" + metricType + ")"; - List result = documentService.find(query, SearchService.DEFAULT_MAX_SEARCH_RESULT, 0); - for (ItemCollection metric : result) { - documentService.remove(metric); - } - - } catch (IllegalArgumentException | QueryException e) { - throw new PluginException(MetricDataService.class.getName(), - "Failed to delete metrics", e.getMessage(), e); - } - } -} \ No newline at end of file diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorRestService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorRestService.java deleted file mode 100644 index dba54f0..0000000 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorRestService.java +++ /dev/null @@ -1,180 +0,0 @@ -package com.alexanderlogistics.metrics; - -import java.util.List; -import java.util.logging.Logger; - -import org.imixs.workflow.ItemCollection; -import org.imixs.workflow.engine.DocumentService; -import org.imixs.workflow.exceptions.PluginException; -import org.imixs.workflow.exceptions.QueryException; - -import com.alexanderlogistics.InvoiceUtil; - -import jakarta.ejb.Stateless; -import jakarta.ejb.TransactionAttribute; -import jakarta.ejb.TransactionAttributeType; -import jakarta.inject.Inject; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; - -@Path("metrics/dbtr") -@Stateless -public class MetricDebitorRestService { - - private static Logger logger = Logger.getLogger(MetricDebitorRestService.class.getName()); - - @Inject - DocumentService documentService; - - @Inject - MetricDebitorService metricDebitorService; - - @Inject - MetricDataService metricDataService; - - @GET - @Path("/ping") - @Produces({ MediaType.TEXT_PLAIN }) - public String ping() { - logger.info("GET ping"); - return "ping: " + System.currentTimeMillis(); - } - - /** - * This method initializes the metrics for all debitors with open invoices. - * - * The method first deletes all existing metrics and than creates or updates the - * metric entires for each debitor. - * - * @return - */ - @GET - @Path("/rebuild") - @Produces({ MediaType.TEXT_PLAIN }) - public Response rebuildMetrics() { - - StringBuffer messageBuffer = new StringBuffer(); - long l = System.currentTimeMillis(); - log("├── rebuild dbtr metrics...", messageBuffer); - - try { - log("│   ├── delete all metrics", messageBuffer); - // first clear the metric cache - metricDebitorService.reset(); - log("│   ├── reset metric cache", messageBuffer); - - computeMetrics(messageBuffer); - - String message = "├── rebuild dbtr metrics completed in " - + (System.currentTimeMillis() - l) - + "ms"; - log(message, messageBuffer); - return Response.ok().entity(messageBuffer.toString()).build(); - - } catch (Exception e) { - e.printStackTrace(); - log("Failed to initialize metrics: " + e.getMessage(), messageBuffer); - return Response.serverError() - .entity(messageBuffer.toString() + e.getMessage()) - .build(); - } - } - - /** - * Diese Methode berechnet alle Metriken auf basis der existierenden Rechnungen - * neu - * - * @throws QueryException - * @throws InterruptedException - * @throws PluginException - * - */ - public void computeMetrics(StringBuffer messageBuffer) - throws QueryException, InterruptedException, PluginException { - long l = System.currentTimeMillis(); - int batchSize = 500; - int totalInvoices = 0; - log("│   │   ├── recalculate metrics...", messageBuffer); - - String query = "$modelversion:rechnungsausgang-* AND type:workitem"; - // Gesamtanzahl ermitteln - int totalCount = documentService.count(query); - log("│   │   ├── found " + totalCount + " open invoices", 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++) { - totalInvoices = totalInvoices + computeInvoiceMetrics(query, batchSize, pageIndex); - // List invoices = documentService.find(query, batchSize, - // pageIndex); - - // for (ItemCollection invoice : invoices) { - // try { - // ItemCollection metricData = metricDebitorService.getMetricByInvoice(invoice); - // // Jetzt Rechnung addieren - // metricDebitorService.addInvoice(metricData, invoice); - // logger.fine("│   │   │   ├── update metric " + InvoiceUtil.getBPId(invoice)); - // metricDebitorService.updateMetric(metricData, true); - // totalInvoices++; - // } catch (PluginException e) { - // // invalid invoice - e.g. no dbtr. number - // } - // } - - // Fortschritt loggen - log("│   │ ├── Processed page " + (pageIndex + 1) + " of " + totalPages + - " (" + totalInvoices + " of " + totalCount + " invoices)", messageBuffer); - // Optional: Kurze Pause nach jedem 5. Batch - Thread.sleep(100); - - } - long duration = System.currentTimeMillis() - l; - double invoicesPerSecond = totalInvoices / (duration / 1000.0); - log("│   │   ├── Successfully processed " + totalInvoices + " invoices in " + - duration + "ms (" + String.format("%.1f", invoicesPerSecond) + " invoices/sec)", messageBuffer); - log("│   │   ├── Updated " + metricDebitorService.getMetricCount() + " metrics.", messageBuffer); - - } - - /** - * Helper method runs in new transaction - * - * @param query - * @param batchSize - * @param pageIndex - * @throws PluginException - * @throws QueryException - */ - @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) - public int computeInvoiceMetrics(String query, int batchSize, int pageIndex) - throws PluginException, QueryException { - - int updates = 0; - List invoices = documentService.find(query, batchSize, pageIndex); - - for (ItemCollection invoice : invoices) { - try { - ItemCollection metricData = metricDebitorService.getMetricByInvoice(invoice); - // Jetzt Rechnung addieren - metricDebitorService.addInvoice(metricData, invoice); - logger.fine("│   │   │   ├── update metric " + InvoiceUtil.getBPId(invoice)); - metricDebitorService.updateMetric(metricData, true); - updates++; - } catch (PluginException e) { - // invalid invoice - e.g. no dbtr. number - } - } - return updates; - } - - 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/metrics/MetricDebitorService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorService.java deleted file mode 100644 index 1e18e07..0000000 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/metrics/MetricDebitorService.java +++ /dev/null @@ -1,382 +0,0 @@ -package com.alexanderlogistics.metrics; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Logger; - -import org.eclipse.microprofile.config.inject.ConfigProperty; -import org.eclipse.microprofile.metrics.Metadata; -import org.eclipse.microprofile.metrics.MetricRegistry; -import org.eclipse.microprofile.metrics.Tag; -import org.eclipse.microprofile.metrics.annotation.RegistryScope; -import org.imixs.archive.core.SnapshotService; -import org.imixs.workflow.ItemCollection; -import org.imixs.workflow.engine.DocumentService; -import org.imixs.workflow.engine.ProcessingEvent; -import org.imixs.workflow.engine.SetupEvent; -import org.imixs.workflow.exceptions.PluginException; - -import com.alexanderlogistics.InvoiceUtil; - -import jakarta.annotation.security.DeclareRoles; -import jakarta.annotation.security.RolesAllowed; -import jakarta.annotation.security.RunAs; -import jakarta.ejb.Singleton; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.event.Observes; -import jakarta.inject.Inject; - -/** - * Dieser Service reagiert auch ProcessingEvents und speichert/aktualisiert die - * zugehörige Debitoren Metric Entity (type=metric.debitor). - *

- * Der Service liest in einem AFTER_PROCESS Event den alten invoice.saldo aus. - * Hierzu wird die Rechnung in einer neuen Transaktion geladen was einem - * 'Dirty-Read' entspricht. Dadurch kennt die Methode den letzten saldo der - * Rechnung. Hat sich nun der aktuelle Saldo geändert, aktualisiert der Serivce - * die entsprechende Metric. - * - * - */ -@DeclareRoles({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS", - "org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS", - "org.imixs.ACCESSLEVEL.MANAGERACCESS" }) -@RolesAllowed({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS", - "org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS", - "org.imixs.ACCESSLEVEL.MANAGERACCESS" }) -@RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS") -@Singleton -@ApplicationScoped -public class MetricDebitorService { - - private static Logger logger = Logger.getLogger(MetricDebitorService.class.getName()); - private final ConcurrentHashMap metricCache = new ConcurrentHashMap<>(); - private final Set registeredGauges = ConcurrentHashMap.newKeySet(); - - public static final String TYPE_METRIC_DEBITOR = "metric.debitor"; - public static final String ITEM_INVOICE_TOTAL = "invoice.total"; - public static final String ITEM_INVOICE_SALDO = "invoice.saldo"; - public static final String ITEM_METRIC_BALANCE = "invoice.balance"; - public static final String ITEM_METRIC_SALES = "invoice.sales"; - - @Inject - @RegistryScope(scope = MetricRegistry.APPLICATION_SCOPE) - MetricRegistry metricRegistry; - - @Inject - DocumentService documentService; - - @Inject - MetricDataService metricDataService; - - @Inject - @ConfigProperty(name = "metrics.enabled", defaultValue = "false") - private boolean metricsEnabled; - - /** - * Init all metrics during setup. Called by the Imixs SetupService - */ - public void initializeMetrics(@Observes SetupEvent setupEvent) { - if (!metricsEnabled) { - return; - } - - long l = System.currentTimeMillis(); - int batchSize = 500; - int totalMetrics = 0; - - try { - logger.info("├── Initializing debitor metrics from database..."); - String query = "(type:" + TYPE_METRIC_DEBITOR + ")"; - - // Gesamtanzahl ermitteln - int totalCount = documentService.count(query); - - // 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 metrics = documentService.find(query, batchSize, pageIndex); - - for (ItemCollection metric : metrics) { - updateMetric(metric, false); - totalMetrics++; - } - // Fortschritt loggen - logger.info("│ ├── Processed page " + (pageIndex + 1) + " of " + totalPages + - " (" + totalMetrics + " of " + totalCount + " metrics)"); - // Optional: Kurze Pause nach jedem Batch - Thread.sleep(100); - } - - long duration = System.currentTimeMillis() - l; - double metricsPerSecond = totalMetrics / (duration / 1000.0); - - logger.info("├── Successfully initialized " + totalMetrics + " metrics in " + - duration + "ms (" + String.format("%.1f", metricsPerSecond) + " metrics/sec)"); - - } catch (Exception e) { - logger.warning("Failed to initialize metrics: " + e.getMessage()); - } - } - - /** - * Reset internal metricCache and clear registered Gauges. - */ - public void reset() { - metricCache.clear(); - registeredGauges.clear(); - } - - public long getMetricCount() { - return metricCache.size(); - } - - /** - * Process Metric only if some data has changed.... - * - * @param processingEvent - * @throws PluginException - */ - public void onProcessingEvent(@Observes ProcessingEvent processingEvent) { - long l = System.currentTimeMillis(); - if (!metricsEnabled) { - return; - } - - ItemCollection invoice = processingEvent.getDocument(); - if (!invoice.getModelVersion().startsWith("rechnungsausgang-")) { - // skip event - return; - } - - // update metric and the metric cache - if (processingEvent.getEventType() == ProcessingEvent.AFTER_PROCESS) { - // load the last invoice metric and reduce the saldo... - ItemCollection lastInvoice = metricDataService.readDirtyWorkitem(invoice.getUniqueID()); - if (lastInvoice != null) { - try { - ItemCollection lastMetricData = getMetricByInvoice(lastInvoice); - if (!isNewMetric(lastMetricData)) { - subtractInvoice(lastMetricData, lastInvoice); - updateMetric(lastMetricData, true); - } - } catch (PluginException e) { - // invalid invoice - e.g. no cdtr. number - } - } - - try { - // load the invoice metric and add the saldo... - ItemCollection metricData = getMetricByInvoice(invoice); - // Saldo-Berechnung - addInvoice(metricData, invoice); - updateMetric(metricData, true); - - logger.info("Metric dbtr update took " + (System.currentTimeMillis() - l) + "ms"); - } catch (PluginException e) { - // invalid invoice - e.g. no dbtr. number - } - } - - } - - /** - * Returns true if the metric is not yet registered. This means we do not have - * sales or balances for this metric - * - * @param metricData - * @return - */ - public boolean isNewMetric(ItemCollection metricData) { - return (!metricCache.containsKey(metricData.getItemValueString("name"))); - } - - /** - * Returns the corresponding metric data object for an invoice. The method uses - * an internal cache. If the metric was not yet cached the method loads the - * metric from the database. If not metric exists in the database the method - * automatically creates a new metric data object. - * - * The method throws a PluginException if no metric can be build from this - * invoice (e.g. missing dbtr.number). - * - * @param invoice - * @return - * @throws PluginException - */ - public ItemCollection getMetricByInvoice(ItemCollection invoice) throws PluginException { - if (invoice == null) { - return null; - } - String metricKey = MetricDataService.buildKeyByInvoice(invoice); - ItemCollection metricData = metricCache.get(metricKey); - // if metric still null we create a new metric data object. - if (metricData == null) { - metricData = createMetaData(invoice); - } - return metricData; - } - - /** - * Returns a metric data object by key - * - * @param key - * @return - */ - public ItemCollection getMetric(String key) { - return metricCache.get(key); - } - - /** - * Returns a list with all cached metric keys. - * The method returns an unmodifiable list to prevent modifications. - * - * @return - */ - public List getMetricKeys() { - return Collections.unmodifiableList(new ArrayList<>(metricCache.keySet())); - } - - /** - * Creates an empty Debitor Meta Data Object (ItemCollection) - *

- * The ItemCollection stores the name and number and also all saldos for all - * currencies - * - * @param invoice - invoice ItemCollection - * @return - * @throws PluginException - */ - private ItemCollection createMetaData(ItemCollection invoice) throws PluginException { - if (invoice == null) { - return null; - } - String key = MetricDataService.buildKeyByInvoice(invoice); - ItemCollection metricData = new ItemCollection(); - metricData.setType(TYPE_METRIC_DEBITOR); - metricData.setItemValue("name", key); - metricData.setItemValue(SnapshotService.NOSNAPSHOT, true); - metricData.setItemValue("bp.id", InvoiceUtil.getBPId(invoice)); - metricData.setItemValue("bp.name", InvoiceUtil.getBPName(invoice)); - metricData.setItemValue("country", invoice.getItemValueString("invoice.country")); - metricData.setItemValue("currency", invoice.getItemValueString("invoice.currency")); - metricData.setItemValue("department", invoice.getItemValueString("space.name")); - - return metricData; - } - - /** - * This method registers and updates the metric meta data objects based on a - * given metricData object. Optional the metric data object is persisted. - * - * @param metricData - the metricData ItemCollection - * @param persist - if true the metricData entity will be persisted - */ - public void updateMetric(ItemCollection metricData, boolean persist) { - String metricKey = metricData.getItemValueString("name"); - - // Cache aktualisieren - metricCache.put(metricKey, metricData); - - // In Datenbank persistieren - if (persist) { - metricData.setItemValue(SnapshotService.NOSNAPSHOT, true); - documentService.save(metricData); - } - - // Prüfen ob Gauge bereits registriert ist - if (registeredGauges.add(metricKey)) { // returns true newly added - String bpName = metricData.getItemValueString("bp.name"); - String bpId = metricData.getItemValueString("bp.id"); - String country = metricData.getItemValueString("country"); - String department = metricData.getItemValueString("department"); - String currency = metricData.getItemValueString("currency"); - - List tags = new ArrayList<>(); - tags.add(new Tag("type", "dbtr")); - tags.add(new Tag("id", bpId)); - tags.add(new Tag("name", bpName)); - tags.add(new Tag("country", country)); - tags.add(new Tag("currency", currency)); - tags.add(new Tag("department", department)); - logger.fine("register new metric for department: " + department + - ", " + metricData.getItemValueString(ITEM_METRIC_BALANCE) + - " " + currency); - - // Saldo Gauge - Metadata balanceMetadata = Metadata.builder() - .withName("dbtr.balance") - .withDescription("Debitor Balance") - .build(); - metricRegistry.gauge(balanceMetadata, - () -> metricCache.get(metricKey).getItemValueDouble(ITEM_METRIC_BALANCE), - tags.toArray(new Tag[0])); - - // Umsatz Gauge - Metadata revenueMetadata = Metadata.builder() - .withName("dbtr.sales") - .withDescription("Debitor Sales") - .build(); - metricRegistry.gauge(revenueMetadata, - () -> metricCache.get(metricKey).getItemValueDouble(ITEM_METRIC_SALES), - tags.toArray(new Tag[0])); - } - - } - - /** - * Addiert den saldo einer Invoice zu einem metricData object - * - * @param metricData - * @param invoice - */ - public void addInvoice(ItemCollection metricData, ItemCollection invoice) { - double invoiceSaldo = invoice.getItemValueDouble(ITEM_INVOICE_SALDO); - double invoiceTotal = invoice.getItemValueDouble(ITEM_INVOICE_TOTAL); - if (!"workitem".equals(invoice.getType()) || invoice.getTaskID() >= 5800) { - // vorgang ist archiviert oder gelöscht worden => saldo = 0! - invoiceSaldo = 0.0; - } - - logger.fine("│   │   │   │   ├── Invoice: " + invoice.getItemValueString("invoice.number") + " Saldo=" - + invoiceSaldo); - // update saldo - double lastSaldo = metricData.getItemValueDouble(ITEM_METRIC_BALANCE); - logger.fine("│   │   │   │   ├── last metric balance=" + lastSaldo); - metricData.setItemValue(ITEM_METRIC_BALANCE, InvoiceUtil.round(lastSaldo + invoiceSaldo)); - - // Umsatz-Berechnung - double lastTotal = metricData.getItemValueDouble(ITEM_METRIC_SALES); - metricData.setItemValue(ITEM_METRIC_SALES, InvoiceUtil.round(lastTotal + invoiceTotal)); - - } - - public void subtractInvoice(ItemCollection metricData, ItemCollection invoice) { - if (metricData == null || invoice == null) { - return; - } - double invoiceSaldo = invoice.getItemValueDouble(ITEM_INVOICE_SALDO); - double invoiceTotal = invoice.getItemValueDouble(ITEM_INVOICE_TOTAL); - logger.fine("│   │   │   │   ├──Invoice: " + invoice.getItemValueString("invoice.number") + " Saldo=" - + invoiceSaldo); - if (!"workitem".equals(invoice.getType()) || invoice.getTaskID() >= 5800) { - // vorgang ist archiviert oder gelöscht worden => saldo = 0! - invoiceSaldo = 0.0; - } - // subtract only if metric saldo exists - double lastSaldo = metricData.getItemValueDouble(ITEM_METRIC_BALANCE); - logger.fine("│   │   │   │   ├── last Metric balance=" + lastSaldo); - metricData.setItemValue(ITEM_METRIC_BALANCE, InvoiceUtil.round(lastSaldo - invoiceSaldo)); - - // update Umsatz - double lastTotal = metricData.getItemValueDouble(ITEM_METRIC_SALES); - metricData.setItemValue(ITEM_METRIC_SALES, InvoiceUtil.round(lastTotal - invoiceTotal)); - - } - -} \ No newline at end of file diff --git a/workflow/businesspartner-en-1.0.0.bpmn b/workflow/businesspartner-en-1.0.0.bpmn new file mode 100644 index 0000000..fb3fc66 --- /dev/null +++ b/workflow/businesspartner-en-1.0.0.bpmn @@ -0,0 +1,883 @@ + + + + + + + association_83sZPw + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + task_ToMZaQ + event_WmkBvw + event_1clvlg + task_Q80A1Q + task_B8Pi7A + event_aSdkwg + event_Tr0aug + dataObject_UQIXAQ + event_vwH0cA + textAnnotation_LyGTCw + event_TuyuwQ + event_h0Kk5g + task_delO7Q + event_1OVorA + event_baWU4w + event_ofjVfg + event_i7yQVw + gateway_uK0c6g + event_6250fg + event_VM0fEQ + gateway_s0J53g + task_NohgrQ + event_AMs05g + event_feLZFQ + event_GTXFMw + event_u8OBFg + + + + + + + + + + + + + + + + + + + sequenceFlow_25otUg + sequenceFlow_S9LVXg + + + + sequenceFlow_25otUg + + + + sequenceFlow_rwdWxw + + + + + + + + + + + partner.name (partner.id)]]> + + + Adresse:
+partner.name
+partner.address
+partner.zip partner.city
+
+ +BPID: partner.id
+Kreditoren-Nr.: cdtr.number
+Debitoren-Nr.: dbtr.number
+]]>
+
+ + + + + + + + + + + + + + + +
+ + sequenceFlow_avahXg + sequenceFlow_9atLFA + sequenceFlow_T5L9aQ + sequenceFlow_b4BcVA + sequenceFlow_8O1EtQ + sequenceFlow_ShuXXA + sequenceFlow_03b60w +
+ + + + partner.name (partner.id)]]> + + + + + + Adresse:
+partner.name
+partner.address
+partner.zip partner.city
+
+ +BPID: partner.id
+Kreditoren-Nr.: cdtr.number
+Debitoren-Nr.: dbtr.number
+]]>
+
+ + + + + + + + + + + + + + + + + + +
+ + sequenceFlow_1mXO8A + sequenceFlow_rwdWxw + sequenceFlow_gC0I9w + sequenceFlow_kS2fMA +
+ + + + + + + + + + Partnermanagement]]> + + + + sequenceFlow_jAU3VA + sequenceFlow_8O1EtQ + + + + + + + + + + + home]]> + + + + sequenceFlow_1mXO8A + sequenceFlow_b0Bj3Q + sequenceFlow_neSsFQ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + sequenceFlow_avahXg + + + + + + + + + + + + + + + + + + + + + + + + + + + + + sequenceFlow_9atLFA + + + + + + + + + + + + + + + sequenceFlow_gC0I9w + + + + + + + + + + + partner.name (partner.id)]]> + + + Adresse:
+partner.name
+partner.address
+partner.zip partner.city
+
+ +BPID: partner.id
+Kreditoren-Nr.: cdtr.number
+Debitoren-Nr.: dbtr.number
+]]>
+
+ + + + + + + + + + + + + + + + + + +
+ + sequenceFlow_NNbMhg + sequenceFlow_avahXg + sequenceFlow_pvIENQ + sequenceFlow_BWOtzA + sequenceFlow_ofWWuw + sequenceFlow_b0Bj3Q + sequenceFlow_BHsRkQ +
+ + + + + + + + + + Partnermanagement]]> + + + + sequenceFlow_BHsRkQ + sequenceFlow_5pT4Tw + + + + + + + + + + + home]]> + + + + sequenceFlow_pvIENQ + sequenceFlow_b4BcVA + + + + + + + + + + + + + + + + + + sequenceFlow_BWOtzA + + + + + + + + Partnermanagement]]> + + + + + + + sequenceFlow_ofWWuw + + + + + + + + + + + + + sequenceFlow_S9LVXg + sequenceFlow_jAU3VA + sequenceFlow_8BTEtw + + + + + + + + + + + + + + + + sequenceFlow_kS2fMA + + + + + + + + Partnermanagement]]> + + + + + + + sequenceFlow_T5L9aQ + sequenceFlow_MPlPww + + sequenceFlow_rNZ3tw + + + + sequenceFlow_MPlPww + sequenceFlow_aH0vCQ + sequenceFlow_ShuXXA + + + + + + + + partner.name (partner.id)]]> + + + Adresse:
+partner.name
+partner.address
+partner.zip partner.city
+
+ +BPID: partner.id
+Kreditoren-Nr.: cdtr.number
+Debitoren-Nr.: dbtr.number
+]]>
+
+ + + + + + + + + + + + + + + + + + +
+ + sequenceFlow_9atLFA + sequenceFlow_8O1EtQ + sequenceFlow_aH0vCQ + sequenceFlow_neSsFQ + sequenceFlow_zd6RWA + sequenceFlow_rNZ3tw +
+ + + + + + + + + + + + + + + + + sequenceFlow_5pT4Tw + + + + + sequenceFlow_8BTEtw + + + + + + + + + + + sequenceFlow_03b60w + + + + + + + + sequenceFlow_zd6RWA + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
From d33fed470755340b39bffe7c8a8c6bcc9dcd86ae Mon Sep 17 00:00:00 2001 From: Ralph Soika Date: Tue, 25 Feb 2025 11:39:48 +0100 Subject: [PATCH 11/12] model fix posteingang --- workflow/businesspartner-en-1.0.0.bpmn | 14 +- workflow/posteingang-de-2.0.1.bpmn | 1625 +++++++++++++++++++++++ workflow/posteingang-en-2.0.1.bpmn | 1626 ++++++++++++++++++++++++ 3 files changed, 3258 insertions(+), 7 deletions(-) create mode 100644 workflow/posteingang-de-2.0.1.bpmn create mode 100644 workflow/posteingang-en-2.0.1.bpmn diff --git a/workflow/businesspartner-en-1.0.0.bpmn b/workflow/businesspartner-en-1.0.0.bpmn index fb3fc66..0fd6526 100644 --- a/workflow/businesspartner-en-1.0.0.bpmn +++ b/workflow/businesspartner-en-1.0.0.bpmn @@ -10,7 +10,7 @@ true - + @@ -26,12 +26,12 @@ - - - - - - + + + + + + diff --git a/workflow/posteingang-de-2.0.1.bpmn b/workflow/posteingang-de-2.0.1.bpmn new file mode 100644 index 0000000..7931e8e --- /dev/null +++ b/workflow/posteingang-de-2.0.1.bpmn @@ -0,0 +1,1625 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + BoundaryEvent_1 + Task_4 + IntermediateCatchEvent_2 + Task_2 + IntermediateCatchEvent_3 + StartEvent_1 + EndEvent_1 + + dataObject_FS1r0Q + task_uB6BGQ + textAnnotation_CtU05A + task_PsBytg + dataObject_CRa7xA + event_Zxhr0w + dataObject_vVKXIg + event_yQLmNA + task_0YaQ2w + dataObject_HHM58Q + event_xOeBKg + event_pDFoXg + dataObject_ZhBiZg + event_4DNGzg + dataObject_KPUiiw + event_wmNwPA + event_tFxV0w + event_88sCAA + event_T901Jg + event_7rbeVA + gateway_qDAhHw + event_9mJrhQ + gateway_gd9zQQ + TextAnnotation_1 + gateway_MgL04w + event_VEvZRQ + event_Xk1mPA + event_a1P5WQ + + + + + + + + + + + + _subject]]> + + + + + + + + + _amount (Brutto € _amount_brutto) +_description]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + home +Empfang +]]> + + + + + + + + + + + + false + + + + sequenceFlow_fRPOrw + sequenceFlow_4gpPgw + + + + sequenceFlow_fRPOrw + + + + + + + + + + + + + + + + + true + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_9 + sequenceFlow_4gpPgw + sequenceFlow_CbkmwA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +X-Tika-OCRLanguage=eng+deu +X-Tika-PDFocrStrategy=OCR_ONLY +(PDF|pdf)$ +10 +Empfang]]> + + + + + + + + + + + + false + + + + SequenceFlow_9 + SequenceFlow_2 + + + DataOutput_1 + + + DataOutput_1 + + + sequenceFlow_VlPVJg + + + + + + + + txtlastcomment]]> + + + + + + + + + + + + + + + + + + + + + + + + cdtr.name (invoice.language)]]> + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_12 + sequenceFlow_gXJm0w + + + SequenceFlow_12 + + + + + + + SequenceFlow_2 + + 1000 + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + true + + + + + + + + + sequenceFlow_fN22Gg + sequenceFlow_bh4DIw + sequenceFlow_ofQKHg + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + document.type ⇒ document.company (Import: document.import.type - Langauge: document.language) ]]> + + + true + + + + + + + + document.type
+Company: document.company
]]>
+
+
+ + sequenceFlow_JpNUKg + sequenceFlow_8SpIiA + sequenceFlow_I8o4DA + sequenceFlow_KKWo0w +
+ + + + + + + + + + + + +]]> + + + + + + + + sequenceFlow_3YcMGQ + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + https://llama.cpp.imixs.com/ + XML + false + + + cdtr.name,invoice.number,invoice.date,invoice.total,payment.date,cdtr.iban,cdtr.bic,total + ON + +false + +]]> + + + + + + DataOutput_2 + + + DataOutput_2 + + + sequenceFlow_aC0skQ + sequenceFlow_YMtfiw + sequenceFlow_PWXjog + + + + + + + + + + + + + + document.type ⇒ document.company (Import: document.import.type - Langauge: document.language) ]]> + + + true + + + + + + + + + sequenceFlow_JpNUKg + sequenceFlow_lmU9IA + sequenceFlow_aC0skQ + sequenceFlow_A62nqQ + sequenceFlow_DdWK6A + + + + + + + + + + + {"n_predict": 4096, "temperature": 0 } + invoice.summary + +[INST]Transfer the invoice data into an XML object with the following structure: + + + ... + ... + 2024-12-31 + 2024-12-31 + 1234.00 + ... + ... + + +Transfer the individual invoice data to the XML tags, taking into account the following suggestions for mapping: + + - Company name ==> "cdtr.name" (the name of the company that issued the invoice document, not the recipient) + - Invoice number ==> "invoice.number" + - Invoice Date ==> "invoice.date" + - Total ==> "invoice.total" (in EUR or if not available in USD or PLN) + - IBAN ==> "cdtr.iban" + - BIC or SWIFT ==> "cdtr.bic" + - Payment date / Due date ==> "invoice.duedate" + +Note: Output only the XML object! Don't add explanations or comments. Use only the XML structure specified in this example and do not create any other XML tags. If you don't have data for some fields, leave the corresponding XML tags blank. Format date values (invoice.date, invoice.duedate) into the ISO 8601 format (YYYY-MM-DD). Format numbers and amounts (type="double") according to ISO 4217. + +<> + +[/INST] + +]]]]> + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + (^rechnungseingang-[a-z]{2,3}-\d+(?:\.\d+)?$) + 5000 + 990 + +]]> + + + + + + DataOutput_2 + + + DataOutput_2 + + sequenceFlow_gXJm0w + sequenceFlow_gpPDLg + sequenceFlow_eccehA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + false + + https://llama.cpp.imixs.com/ + XML + +Empfang + + +]]> + + + + + + DataOutput_2 + + + DataOutput_2 + + + sequenceFlow_fN22Gg + sequenceFlow_naZnAA + sequenceFlow_PgyOMw + + + + + {"n_predict": 512, "temperature": 0 } + ^.+\.([pP][dD][fF])$ + +[INST] Assign the invoice to one of the following categories: + +- Cargo-Invoice - in case the invoice is about cargo and logistic services +- Credit - in case of a credit note +- Invoice - in all other cases + +Extract also the company name and the language the invoice is written in. + +Note: The company name is the name of the company that issued the invoice document and not the name of the recipient. As a rule, it is not Alexander Global Logistics either. The company name is often at the beginning or end of the invoice document. + +Output the infromation as one XML object that has the following structure: + + + Type + Kraxi GmbH + German + + +Note: Do not generate any other information instead of the XML object. Do not generate more than one XML Object. + +[/INST] +]]]]> + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + https://llama.cpp.imixs.com/ + invoice.summary + false + +false +]]> + + + + + + DataOutput_2 + + + DataOutput_2 + + + sequenceFlow_8SpIiA + sequenceFlow_A62nqQ + sequenceFlow_Di4BQA + + + + + + + + + + + {"n_predict": 4096, "temperature": 0} + ^.+\.([pP][dD][fF])$ + +[INST] + +Summarize the data from this invoice document: + + - Vendor information + - General Billing data + - Invoice total information + - Payment summary (including bank data) + - Invoice items + +Note: The vendor information refers to the sender of the invoice. The company name is the name of the company that issued the invoice document and not the name of the recipient. As a rule, it is not Alexander Global Logistics either. The company name is often at the beginning or end of the invoice document. + +Use only the default values and do not perform any calculations, summing or changes to these values yourself. Format numbers and amounts according to ISO 4217. + +If possible summarize the invoice lines in a table. + +[/INST] +]]]]> + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + sequenceFlow_3YcMGQ + sequenceFlow_p0asUQ + + + + + + sequenceFlow_DdWK6A + + + + + + + + sequenceFlow_PgyOMw + + + + + sequenceFlow_Di4BQA + + + + + + + + + + + sequenceFlow_PWXjog + + + + + + + sequenceFlow_naZnAA + sequenceFlow_tcjWAA + sequenceFlow_I8o4DA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + adapter.error_code: adapter.error_message]]> + + + false + + + + + + DataOutput_2 + + + DataOutput_2 + + sequenceFlow_tcjWAA + sequenceFlow_gpPDLg + sequenceFlow_d0zSHA + + + + + + + + + + + + + + + sequenceFlow_I8o4DA + sequenceFlow_VlPVJg + sequenceFlow_bh4DIw + sequenceFlow_d0zSHA + + + + + + + + + + + + + For the OCR Adapter the following minimal options should be set: + + * X-Tika-PDFocrStrategy=OCR_AND_TEXT_EXTRACTION + * X-Tika-OCRLanguage=eng+deu + +These options allow OCR and text extraction suporting English and German language. + +Additional Tika Options can be set but are NOT needed in most cases: + + * X-Tika-PDFOcrImageType=RGB (setting the RGB color mode) + * X-Tika-PDFOcrDPI=400 (setting DPI) + +Setting the OcrDPI is only recommended if the DPI is know! + +Possible ImageTypes are: + + * ARGB Alpha, Red, Green, Blue + * BINARY Black or white. + * GRAY Shades of gray + * RGB Red, Green, Blue + + + + + + sequenceFlow_YMtfiw + sequenceFlow_eccehA + sequenceFlow_p0asUQ + + + + + + + + + + + + + + + + + sequenceFlow_KKWo0w + + + + + + + + sequenceFlow_ofQKHg + + + + + + + + sequenceFlow_CbkmwA + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/workflow/posteingang-en-2.0.1.bpmn b/workflow/posteingang-en-2.0.1.bpmn new file mode 100644 index 0000000..f7f51fc --- /dev/null +++ b/workflow/posteingang-en-2.0.1.bpmn @@ -0,0 +1,1626 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + BoundaryEvent_1 + Task_4 + IntermediateCatchEvent_2 + Task_2 + IntermediateCatchEvent_3 + StartEvent_1 + EndEvent_1 + + dataObject_FS1r0Q + task_uB6BGQ + textAnnotation_CtU05A + task_PsBytg + dataObject_CRa7xA + event_Zxhr0w + dataObject_vVKXIg + event_yQLmNA + task_0YaQ2w + dataObject_HHM58Q + event_xOeBKg + event_pDFoXg + dataObject_ZhBiZg + event_4DNGzg + dataObject_KPUiiw + event_wmNwPA + event_tFxV0w + event_88sCAA + event_T901Jg + event_7rbeVA + gateway_qDAhHw + event_9mJrhQ + gateway_gd9zQQ + TextAnnotation_1 + gateway_1py0TA + event_kjzDVg + event_VQZVwg + event_OKWZDg + + + + + + + + + + + + _subject]]> + + + + + + + + + _amount (Brutto € _amount_brutto) +_description]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + home +Front Desk +]]> + + + + + + + + + + + + false + + + + sequenceFlow_fRPOrw + sequenceFlow_4gpPgw + + + + sequenceFlow_fRPOrw + + + + + + + + + + + + + + + + + true + + + + + + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_9 + sequenceFlow_4gpPgw + sequenceFlow_B0T5KA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +X-Tika-OCRLanguage=eng+deu +X-Tika-PDFocrStrategy=OCR_ONLY +(PDF|pdf)$ +10 +Front Desk]]> + + + + + + + + + + + + false + + + + SequenceFlow_9 + SequenceFlow_2 + + + DataOutput_1 + + + DataOutput_1 + + + sequenceFlow_VlPVJg + + + + + + + + txtlastcomment]]> + + + + + + + + + + + + + + + + + + + + + + + + cdtr.name (invoice.language)]]> + + + $workflowgroup - $workflowstatus]]> + SequenceFlow_12 + sequenceFlow_gXJm0w + + + SequenceFlow_12 + + + + + + + SequenceFlow_2 + + 1000 + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + true + + + + + + + + + sequenceFlow_fN22Gg + sequenceFlow_bh4DIw + sequenceFlow_WBx7JQ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + document.type ⇒ document.company (Import: document.import.type - Langauge: document.language) ]]> + + + true + + + + + + + + document.type
+Company: document.company
]]>
+
+
+ + sequenceFlow_JpNUKg + sequenceFlow_8SpIiA + sequenceFlow_I8o4DA + sequenceFlow_n3o55Q +
+ + + + + + + + + + + + +]]> + + + + + + + + sequenceFlow_Z8050g + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + https://llama.cpp.imixs.com/ + XML + true + + + cdtr.name,invoice.number,invoice.date,invoice.total,payment.date,cdtr.iban,cdtr.bic,total + ON + +false + +]]> + + + + + + DataOutput_2 + + + DataOutput_2 + + + sequenceFlow_aC0skQ + sequenceFlow_YMtfiw + sequenceFlow_PWXjog + + + + + + + + + + + + + + document.type ⇒ document.company (Import: document.import.type - Langauge: document.language) ]]> + + + true + + + + + + + + + sequenceFlow_JpNUKg + sequenceFlow_lmU9IA + sequenceFlow_aC0skQ + sequenceFlow_A62nqQ + sequenceFlow_WIiVnQ + + + + + + + + + + + {"n_predict": 4096, "temperature": 0 } + invoice.summary + +[INST]Transfer the invoice data into an XML object with the following structure: + + + ... + ... + 2024-12-31 + 2024-12-31 + 1234.00 + ... + ... + + +Transfer the individual invoice data to the XML tags, taking into account the following suggestions for mapping: + + - Company name ==> "cdtr.name" (the name of the company that issued the invoice document, not the recipient) + - Invoice number ==> "invoice.number" + - Invoice Date ==> "invoice.date" + - Total ==> "invoice.total" (in EUR or if not available in USD or PLN) + - IBAN ==> "cdtr.iban" + - BIC or SWIFT ==> "cdtr.bic" + - Payment date / Due date ==> "invoice.duedate" + +Note: Output only the XML object! Don't add explanations or comments. Use only the XML structure specified in this example and do not create any other XML tags. If you don't have data for some fields, leave the corresponding XML tags blank. Format date values (invoice.date, invoice.duedate) into the ISO 8601 format (YYYY-MM-DD). Format numbers and amounts (type="double") according to ISO 4217. + +<> + +[/INST] + +]]]]> + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + (^rechnungseingang-[a-z]{2,3}-\d+(?:\.\d+)?$) + 5000 + 990 + +]]> + + + + + + DataOutput_2 + + + DataOutput_2 + + sequenceFlow_gXJm0w + sequenceFlow_gpPDLg + sequenceFlow_I8Pggg + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + false + + https://llama.cpp.imixs.com/ + XML + + + +]]> + + + + + + DataOutput_2 + + + DataOutput_2 + + + sequenceFlow_fN22Gg + sequenceFlow_naZnAA + sequenceFlow_PgyOMw + + + + + {"n_predict": 512, "temperature": 0 } + ^.+\.([pP][dD][fF])$ + +[INST] Assign the invoice to one of the following categories: + +- Cargo-Invoice - in case the invoice is about cargo and logistic services +- Credit - in case of a credit note +- Invoice - in all other cases + +Extract also the company name and the language the invoice is written in. + +Note: The company name is the name of the company that issued the invoice document and not the name of the recipient. As a rule, it is not Alexander Global Logistics either. The company name is often at the beginning or end of the invoice document. + +Output the infromation as one XML object that has the following structure: + + + Type + Kraxi GmbH + German + + +Note: Do not generate any other information instead of the XML object. Do not generate more than one XML Object. + +[/INST] +]]]]> + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + https://llama.cpp.imixs.com/ + invoice.summary + true + +false + +]]> + + + + + + DataOutput_2 + + + DataOutput_2 + + + sequenceFlow_8SpIiA + sequenceFlow_A62nqQ + sequenceFlow_Di4BQA + + + + + + + + + + + {"n_predict": 4096, "temperature": 0} + ^.+\.([pP][dD][fF])$ + +[INST] + +Summarize the data from this invoice document: + + - Vendor information + - General Billing data + - Invoice total information + - Payment summary (including bank data) + - Invoice items + +Note: The vendor information refers to the sender of the invoice. The company name is the name of the company that issued the invoice document and not the name of the recipient. As a rule, it is not Alexander Global Logistics either. The company name is often at the beginning or end of the invoice document. + +Use only the default values and do not perform any calculations, summing or changes to these values yourself. Format numbers and amounts according to ISO 4217. + +If possible summarize the invoice lines in a table. + +[/INST] +]]]]> + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + sequenceFlow_Z8050g + sequenceFlow_0XHjPA + + + + + + sequenceFlow_WIiVnQ + + + + + + + + sequenceFlow_PgyOMw + + + + + sequenceFlow_Di4BQA + + + + + + + + + + + sequenceFlow_PWXjog + + + + + + + sequenceFlow_naZnAA + sequenceFlow_tcjWAA + sequenceFlow_I8o4DA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + adapter.error_code: adapter.error_message]]> + + + false + + + + + + DataOutput_2 + + + DataOutput_2 + + sequenceFlow_tcjWAA + sequenceFlow_gpPDLg + sequenceFlow_d0zSHA + + + + + + + + + + + + + + + sequenceFlow_I8o4DA + sequenceFlow_VlPVJg + sequenceFlow_bh4DIw + sequenceFlow_d0zSHA + + + + + + + + + + + + + For the OCR Adapter the following minimal options should be set: + + * X-Tika-PDFocrStrategy=OCR_AND_TEXT_EXTRACTION + * X-Tika-OCRLanguage=eng+deu + +These options allow OCR and text extraction suporting English and German language. + +Additional Tika Options can be set but are NOT needed in most cases: + + * X-Tika-PDFOcrImageType=RGB (setting the RGB color mode) + * X-Tika-PDFOcrDPI=400 (setting DPI) + +Setting the OcrDPI is only recommended if the DPI is know! + +Possible ImageTypes are: + + * ARGB Alpha, Red, Green, Blue + * BINARY Black or white. + * GRAY Shades of gray + * RGB Red, Green, Blue + + + + + + sequenceFlow_YMtfiw + sequenceFlow_I8Pggg + sequenceFlow_0XHjPA + + + + + + + + + + + + + + + + + sequenceFlow_n3o55Q + + + + + + + + sequenceFlow_WBx7JQ + + + + + + + + sequenceFlow_B0T5KA + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
From 15fe8e14e6f478ceef151bebe023c85db1e9e356 Mon Sep 17 00:00:00 2001 From: Ralph Soika Date: Mon, 3 Mar 2025 11:19:22 +0100 Subject: [PATCH 12/12] draft --- workflow/posteingang-en-2.0.1.bpmn | 6 +-- workflow/rechnungsausgang-de-1.0.12.bpmn | 54 ++++++++++++------------ 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/workflow/posteingang-en-2.0.1.bpmn b/workflow/posteingang-en-2.0.1.bpmn index f7f51fc..bbb9d77 100644 --- a/workflow/posteingang-en-2.0.1.bpmn +++ b/workflow/posteingang-en-2.0.1.bpmn @@ -390,7 +390,7 @@ Assign invoice to process 'Front Desk']]> - document.type ⇒ document.company (Import: document.import.type - Langauge: document.language) ]]> + document.type ⇒ document.company (Import: document.import.type - Language: document.language) ]]> true @@ -549,7 +549,7 @@ Company: document.company
]]>
- document.type ⇒ document.company (Import: document.import.type - Langauge: document.language) ]]> + document.type ⇒ document.company (Import: document.import.type - Language: document.language) ]]> true @@ -1016,7 +1016,7 @@ If possible summarize the invoice lines in a table. - + diff --git a/workflow/rechnungsausgang-de-1.0.12.bpmn b/workflow/rechnungsausgang-de-1.0.12.bpmn index 8b89f6c..1d26998 100644 --- a/workflow/rechnungsausgang-de-1.0.12.bpmn +++ b/workflow/rechnungsausgang-de-1.0.12.bpmn @@ -96,7 +96,7 @@ th { font-weight: bold;} - + Task_2 @@ -3638,8 +3638,8 @@ if (workitem.getItemValueString('txtcomment') == '' ) { - - + + @@ -3652,8 +3652,8 @@ if (workitem.getItemValueString('txtcomment') == '' ) { - - + + @@ -3691,13 +3691,13 @@ if (workitem.getItemValueString('txtcomment') == '' ) { - + - + @@ -3727,7 +3727,7 @@ if (workitem.getItemValueString('txtcomment') == '' ) { - + @@ -3736,13 +3736,13 @@ if (workitem.getItemValueString('txtcomment') == '' ) { - + - + @@ -3761,14 +3761,14 @@ if (workitem.getItemValueString('txtcomment') == '' ) { - + - + @@ -3782,7 +3782,7 @@ if (workitem.getItemValueString('txtcomment') == '' ) { - + @@ -3792,7 +3792,7 @@ if (workitem.getItemValueString('txtcomment') == '' ) { - + @@ -3807,8 +3807,8 @@ if (workitem.getItemValueString('txtcomment') == '' ) { - - + + @@ -3865,13 +3865,13 @@ if (workitem.getItemValueString('txtcomment') == '' ) { - + - + @@ -3942,7 +3942,7 @@ if (workitem.getItemValueString('txtcomment') == '' ) { - + @@ -3968,20 +3968,20 @@ if (workitem.getItemValueString('txtcomment') == '' ) { - - + + - - + + - - + + @@ -4105,8 +4105,8 @@ if (workitem.getItemValueString('txtcomment') == '' ) { - - + +