From eb6808da788f67199b8026b7db6844748c4913bc Mon Sep 17 00:00:00 2001 From: Ralph Soika Date: Mon, 27 Jan 2025 15:57:35 +0100 Subject: [PATCH] =?UTF-8?q?Einf=C3=BChrung=20BPID=20und=20verbesserung=20M?= =?UTF-8?q?etric=20service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/alexanderlogistics/InvoiceUtil.java | 85 +++++++++++++ .../metrics/MetricCreditorRestService.java | 120 +++++++----------- .../metrics/MetricCreditorService.java | 96 +++++++------- .../metrics/MetricDataService.java | 19 ++- .../metrics/MetricDebitorRestService.java | 103 ++++++--------- .../metrics/MetricDebitorService.java | 91 +++++++------ 6 files changed, 288 insertions(+), 226 deletions(-) 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 ebdff55..f7cc9cb 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,6 +8,9 @@ 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 @@ -25,6 +28,8 @@ import org.imixs.workflow.ItemCollection; */ public class InvoiceUtil { + public static final String ERROR_INVALID_INVOICEDATA = "ERROR_INVALID_INVOICEDATA"; + public static final String CHILD_ITEM_PROPERTY = "_ChildItems"; public static final String ITEM_INVOICE_PERIOD = "invoice.period"; public static final String ITEM_INVOICE_POSITIONS = "invoice.positions"; @@ -157,4 +162,84 @@ public class InvoiceUtil { } + /** + * Diese Hilfsmethode harmonisiert die Debitoren/Kreditoren ID von Cargosoft und + * liefert eine Geschäftspartner ID zurück. + * + * Aus Kreditor K70153 und Debitor D10153 wird die einheitliche ID: BP0153 + * + * @param key + * @return + */ + public static String buildBPID(String key) { + key = key.toUpperCase(); + if (key.startsWith("D") || key.startsWith("K")) { + key = key.substring(1); + } + if (!key.startsWith("BP")) { + // cut first digit (debitor/creditor) + key = "BP" + key.substring(1); + } + + return key; + + } + + /** + * 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 + * + * @param Invoice + * @return + */ + public static String getBPName(ItemCollection invoice) { + String name = ""; + if (isCreditorInvoice(invoice)) { + // key ist cdtr.number + name = invoice.getItemValueString("cdtr.name"); + } else { + // key ist dbtr.number + name = invoice.getItemValueString("dbtr.name"); + } + + return name; + } + + /** + * Gibt true zurück wenn es sich um eine Kreditoren Rechnung/Gutschrift handelt. + * + * @param invoice + * @return + */ + public static boolean isCreditorInvoice(ItemCollection invoice) { + boolean result = false; + if (invoice.getModelVersion().startsWith("rechnungseingang") + || invoice.getModelVersion().startsWith("gutschriftabgleich")) { + result = true; + } + return result; + } } 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 5f7da66..9aca753 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 @@ -8,6 +8,8 @@ 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.inject.Inject; import jakarta.ws.rs.GET; @@ -26,7 +28,7 @@ public class MetricCreditorRestService { DocumentService documentService; @Inject - MetricCreditorService metricService; + MetricCreditorService metricCreditorService; @Inject MetricDataService metricDataService; @@ -39,66 +41,41 @@ public class MetricCreditorRestService { return "ping: " + System.currentTimeMillis(); } - /** - * This method refreshes all creditor metrics by iterating through the metric - * entities. - * This will refresh the metrics view in Wildfly only and not computing the - * metrics itself. - * - * @return - */ - @GET - @Path("/refresh") - @Produces({ MediaType.TEXT_PLAIN }) - public Response refreshMetrics() { - try { - // Alle Creditor Metriken laden - String query = "(type:" + MetricCreditorService.TYPE_METRIC_CREDITOR + ")"; - List metrics = documentService.find(query, 9999, 0); - - // Metriken initialisieren - for (ItemCollection metric : metrics) { - metricService.initMetric(metric); - } - return Response.ok().entity("Initialized " + metrics.size() + " creditor metrics").build(); - } catch (Exception e) { - return Response.serverError() - .entity("Failed to initialize metrics: " + e.getMessage()) - .build(); - } - } - /** * 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("/init") + @Path("/rebuild") @Produces({ MediaType.TEXT_PLAIN }) - public Response initMetrics() { - + public Response rebuildMetrics() { + StringBuffer messageBuffer = new StringBuffer(); long l = System.currentTimeMillis(); - logger.info("├── init cdtr metrics..."); + log("├── init cdtr metrics...", messageBuffer); try { // first clear the metric cache - metricService.reset(); - logger.info("│   ├── reset metric cache"); - + metricCreditorService.reset(); + log("│   ├── reset metric cache", messageBuffer); + // run in new transaction! metricDataService.deleteAllMetrics(MetricCreditorService.TYPE_METRIC_CREDITOR); - logger.info("│   ├── delete metrics"); + log("│   ├── delete metrics", messageBuffer); - groupInvoicesByCreditor(); - logger.info("│   ├── grouping invoices finished in " + (System.currentTimeMillis() - l) + "ms"); + computeMetrics(); + log("│   ├── computing metrics finished in " + (System.currentTimeMillis() - l) + "ms", messageBuffer); - rebuildMetrics(); - String message = "├── init cdtr metrics completed in " + logger.info("│   ├── save and init metrics..."); + // run in new transaction! + metricCreditorService.refreshGauges(); + + String message = "├── rebuild cdtr 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(); @@ -108,30 +85,16 @@ public class MetricCreditorRestService { } } - private void rebuildMetrics() throws PluginException { - logger.info("│   ├── rebuild metrics..."); - List keys = metricService.getMetricKeys(); - for (String hashKey : keys) { - ItemCollection metricData = metricService.getMetric(hashKey); - // metricCache.get(dbtrNumber); - documentService.save(metricData); - metricService.initMetric(metricData); - } - } - /** - * Diese Methode gruppiert eine Rechnungsliste nach creditoren - * - * @param spaceID - Space Ref to select a list of invoices associated with - * a - * space - * @param metricCache - a local cache storing all invoices by week - * + * Diese Methode berechnet alle Metriken auf basis der existierenden Rechnungen + * neu * */ - private void groupInvoicesByCreditor() { - logger.info("│   ├── group invoices by creditor..."); + public void computeMetrics() { + + logger.info("│   │   ├── group invoices by creditor..."); try { + int count = 0; List invoices = documentService.find( "($modelversion:rechnungseingang-* OR $modelversion:gutschriftabgleich-*) " + " AND type:workitem", @@ -139,19 +102,28 @@ public class MetricCreditorRestService { "invoice.number", false); logger.info("│   │   ├──found " + invoices.size() + " open invoices"); for (ItemCollection invoice : invoices) { - if (invoice.getItemValueString("cdtr.number").trim().isEmpty()) { - // skip event - continue; + try { + ItemCollection metricData = metricCreditorService.getMetricByInvoice(invoice); + // Jetzt Rechnung addieren + metricCreditorService.addInvoice(metricData, invoice); + logger.info("│   │   │   ├──update metric " + InvoiceUtil.getBPId(invoice)); + metricCreditorService.putMetric(metricData); + count++; + } catch (PluginException e) { + // invalid invoice - e.g. no cdtr. number } - ItemCollection metricData = metricService.getMetricByInvoice(invoice); - // Jetzt Rechnung addieren - metricService.addInvoice(metricData, invoice); - logger.fine("....put invoice " + invoice.getUniqueID()); - metricService.putMetric(metricData); + } - logger.info("│   │   ├── grouped " + invoices.size() + " invoices."); - } catch (QueryException | PluginException e) { + logger.info("│   │   ├── updated metric for " + count + " invoices."); + } catch (QueryException e) { e.printStackTrace(); } } + + 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 a5396eb..bfcf99c 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 @@ -24,6 +24,8 @@ 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; @@ -98,33 +100,28 @@ public class MetricCreditorService { return; } - // double creditorSaldo = 0; ItemCollection invoice = processingEvent.getDocument(); - if (!invoice.getModelVersion().startsWith("rechnungseingang-") - && !invoice.getModelVersion().startsWith("gutschriftabgleich-")) { + if (!InvoiceUtil.isCreditorInvoice(invoice)) { // skip event return; } - // if we do not have a cdtr.number skip - if (invoice.getItemValueString("cdtr.number").trim().isEmpty()) { - // skip event - return; - } - - try { - // 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) { + // 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); subtractInvoice(lastMetricData, lastInvoice); putMetric(lastMetricData); metricDataService.saveMetric(lastMetricData); + } 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 @@ -135,10 +132,11 @@ public class MetricCreditorService { // Update the Gauge updateGauge(metricData); logger.info("Metric cdtr update took " + (System.currentTimeMillis() - l) + "ms"); + } catch (PluginException e) { + // invalid invoice - e.g. no cdtr. number } - } catch (PluginException e) { - logger.warning("unable to process metric: " + e.getMessage()); } + } /** @@ -147,6 +145,9 @@ public class MetricCreditorService { * 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 @@ -221,20 +222,20 @@ public class MetricCreditorService { throw new PluginException(PluginException.class.getName(), "Failed to load metric object for invoice " + invoice.getUniqueID() + ": ", e.getMessage(), e); } - return creditorMetric; } /** - * Creates an empty Debitor Meta Data Object (ItemCollection) + * Creates an empty Creditor 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) { + private ItemCollection createMetaData(ItemCollection invoice) throws PluginException { if (invoice == null) { return null; } @@ -242,9 +243,12 @@ public class MetricCreditorService { ItemCollection metricData = new ItemCollection(); metricData.setType(TYPE_METRIC_CREDITOR); metricData.setItemValue("name", key); - metricData.setItemValue("country", invoice.getItemValueString("invoice.country")); - metricData.setItemValue("currency", invoice.getItemValueString("invoice.currency")); - metricData.setItemValue("department", invoice.getItemValueString("space.name")); + metricData.setItemValue("bp_id", InvoiceUtil.getBPId(invoice)); + metricData.setItemValue("bp_name", InvoiceUtil.getBPName(invoice)); + metricData.setItemValue("invoice_country", invoice.getItemValueString("invoice.country")); + metricData.setItemValue("invoice_currency", invoice.getItemValueString("invoice.currency")); + metricData.setItemValue("invoice_department", invoice.getItemValueString("space.name")); + return metricData; } @@ -254,21 +258,27 @@ public class MetricCreditorService { * @param cdtrNumber - the creditor number * @param cdtrName - the creditor name */ - private void updateGauge(ItemCollection metricData) { + public void updateGauge(ItemCollection metricData) { + String metricKey = metricData.getItemValueString("name"); - String country = metricData.getItemValueString("country"); - String department = metricData.getItemValueString("department"); - String currency = metricData.getItemValueString("currency"); + String bpName = metricData.getItemValueString("bp_name"); + String bpId = metricData.getItemValueString("bp_id"); + String country = metricData.getItemValueString("invoice_country"); + String department = metricData.getItemValueString("invoice_department"); + String currency = metricData.getItemValueString("invoice_currency"); + // Prüfen ob Gauge bereits registriert ist if (registeredGauges.add(metricKey)) { // returns true newly added 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)); - logger.info("register new metric for department: " + department + + logger.fine("register new metric for department: " + department + ", " + metricData.getItemValueString(ITEM_SALDO) + - " " + metricData.getItemValueString("invoice.currency")); + " " + currency); Metadata metadata = Metadata.builder() .withName("cdtr.balance") .withDescription("Creditor Balance") @@ -284,19 +294,6 @@ public class MetricCreditorService { } - /** - * Hilfsmethode um Metriken initial aufzubauen. Wird vom Metric Rest Service - * verwendet. - * - */ - protected void initMetric(ItemCollection metric) { - String key = metric.getItemValueString("name"); - // Aktuelle metric cachen - metricCache.put(key, metric); - logger.info("│   │   ├──init metric for creditor " + key); - updateGauge(metric); - } - /** * Addiert den saldo einer Invoice zu einem metricData object * @@ -335,4 +332,17 @@ public class MetricCreditorService { } } + /** + * 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(); + for (String hashKey : keys) { + ItemCollection metricData = getMetric(hashKey); + documentService.save(metricData); + updateGauge(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 cec8baa..09b00ad 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 @@ -5,9 +5,12 @@ 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; @@ -57,16 +60,17 @@ public class MetricDataService { * @throws PluginException * */ + @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) public void deleteAllMetrics(String metricType) throws PluginException { try { String query = "(type:" + metricType + ")"; - List result = documentService.find(query, -1, 0); + 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(PluginException.class.getName(), + throw new PluginException(MetricDataService.class.getName(), "Failed to delete metrics", e.getMessage(), e); } } @@ -75,20 +79,27 @@ public class MetricDataService { * 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) { + 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", + String combinedValue = String.format("%s::%s::%s::%s", + bpNumber, country, currency, department); 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 9cfd925..c97f18b 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 @@ -8,6 +8,8 @@ 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.inject.Inject; import jakarta.ws.rs.GET; @@ -26,7 +28,7 @@ public class MetricDebitorRestService { DocumentService documentService; @Inject - MetricDebitorService metricService; + MetricDebitorService metricDebitorService; @Inject MetricDataService metricDataService; @@ -39,45 +41,18 @@ public class MetricDebitorRestService { return "ping: " + System.currentTimeMillis(); } - /** - * This method refreshes all debitor metrics by iterating through the metric - * entities. - * - * @return - */ - @GET - @Path("/refresh") - @Produces({ MediaType.TEXT_PLAIN }) - public Response refreshMetrics() { - try { - // Alle Debitoren Metriken laden - String query = "(type:" + MetricDebitorService.TYPE_METRIC_DEBITOR + ")"; - List metrics = documentService.find(query, 9999, 0); - - // Metriken initialisieren - for (ItemCollection metric : metrics) { - metricService.initMetric(metric); - } - - return Response.ok().entity("Initialized " + metrics.size() + " debitor metrics").build(); - - } catch (Exception e) { - return Response.serverError() - .entity("Failed to initialize metrics: " + e.getMessage()) - .build(); - } - } - /** * This method initializes the metrics for all debitors with open invoices. - * The method creates or updates the metric entires for each debitor. + * + * The method first deletes all existing metrics and than creates or updates the + * metric entires for each debitor. * * @return */ @GET - @Path("/init") + @Path("/rebuild") @Produces({ MediaType.TEXT_PLAIN }) - public Response initMetrics() { + public Response rebuildMetrics() { // Map metricCache = new HashMap(); long l = System.currentTimeMillis(); @@ -85,16 +60,20 @@ public class MetricDebitorRestService { try { // first clear the metric cache - metricService.reset(); + metricDebitorService.reset(); logger.info("│   ├── reset metric cache"); + // run in new transaction! metricDataService.deleteAllMetrics(MetricDebitorService.TYPE_METRIC_DEBITOR); logger.info("│   ├── delete metrics"); - groupInvoicesByDebitor(); - logger.info("│   ├── grouping invoices finished in " + (System.currentTimeMillis() - l) + "ms"); + computeMetrics(); + logger.info("│   ├── computing metrics finished in " + (System.currentTimeMillis() - l) + "ms"); - rebuildMetrics(); - String message = "├── init cdtr metrics completed in " + logger.info("│   ├── save and init metrics ..."); + // run in new transaction! + metricDebitorService.refreshGauges(); + + String message = "├── rebuild dbtr metrics completed in " + (System.currentTimeMillis() - l) + "ms"; logger.info(message); @@ -108,47 +87,39 @@ public class MetricDebitorRestService { } } - private void rebuildMetrics() throws PluginException { - logger.info("│   ├── rebuild metrics..."); - - List keys = metricService.getMetricKeys(); - for (String hashKey : keys) { - ItemCollection metricData = metricService.getMetric(hashKey); - // metricCache.get(dbtrNumber); - documentService.save(metricData); - metricService.initMetric(metricData); - } - } - /** - * Diese Methode gruppiert eine Rechnungsliste nach debitoren - * - * @param spaceID - Space Ref to select a list of invoices associated with - * a - * space - * @param metricCache - a local cache storing all invoices by week - * + * Diese Methode berechnet alle Metriken auf basis der existierenden Rechnungen + * neu * */ - private void groupInvoicesByDebitor() { + public void computeMetrics() { logger.info("│   │   ├── group invoices by debitor..."); try { + int count = 0; List invoices = documentService.find( "$modelversion:rechnungsausgang-* AND type:workitem", 9999, 0, "invoice.number", false); - logger.info("│   │   ├──found " + invoices.size() + " open invoices"); + logger.info("│   │   ├── found " + invoices.size() + " open invoices"); for (ItemCollection invoice : invoices) { - ItemCollection metricData = metricService.getMetricByInvoice(invoice); - // Jetzt Rechnung addieren - metricService.addInvoice(metricData, invoice); - logger.fine("....put invoice " + invoice.getUniqueID()); - metricService.putMetric(metricData); + try { + + ItemCollection metricData = metricDebitorService.getMetricByInvoice(invoice); + + // Jetzt Rechnung addieren + metricDebitorService.addInvoice(metricData, invoice); + logger.info("│   │   │   ├──update metric " + InvoiceUtil.getBPId(invoice)); + metricDebitorService.putMetric(metricData); + count++; + } catch (PluginException e) { + // invalid invoice - e.g. no dbtr. number + } + } - logger.info("│   │   ├── grouped " + invoices.size() + " invoices."); - } catch (QueryException | PluginException e) { + logger.info("│   │   ├── updated metric for " + count + " invoices."); + } catch (QueryException e) { e.printStackTrace(); } 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 1117704..d1961ee 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 @@ -23,6 +23,8 @@ 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; @@ -90,30 +92,28 @@ public class MetricDebitorService { return; } - // double debitorSaldo = 0; ItemCollection invoice = processingEvent.getDocument(); if (!invoice.getModelVersion().startsWith("rechnungsausgang-")) { // skip event return; } - // if we do not have a dbtr.number skip - if (invoice.getItemValueString("dbtr.number").trim().isEmpty()) { - // skip event - return; - } - try { - // 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) { + // 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); subtractInvoice(lastMetricData, lastInvoice); putMetric(lastMetricData); metricDataService.saveMetric(lastMetricData); + } 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 @@ -124,10 +124,11 @@ public class MetricDebitorService { // 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 } - } catch (PluginException e) { - logger.warning("unable to process metric: " + e.getMessage()); } + } /** @@ -136,6 +137,9 @@ public class MetricDebitorService { * 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 @@ -195,10 +199,10 @@ public class MetricDebitorService { * @throws PluginException */ private ItemCollection loadMetric(ItemCollection invoice) throws PluginException { + ItemCollection debitorMetric = null; if (invoice == null) { return null; } - ItemCollection debitorMetric = null; try { String metricKey = MetricDataService.buildKeyByInvoice(invoice); String query = "(type:" + TYPE_METRIC_DEBITOR + ") AND (name:" + metricKey + ")"; @@ -221,8 +225,9 @@ public class MetricDebitorService { * * @param invoice - invoice ItemCollection * @return + * @throws PluginException */ - private ItemCollection createMetaData(ItemCollection invoice) { + private ItemCollection createMetaData(ItemCollection invoice) throws PluginException { if (invoice == null) { return null; } @@ -230,9 +235,12 @@ public class MetricDebitorService { ItemCollection metricData = new ItemCollection(); metricData.setType(TYPE_METRIC_DEBITOR); metricData.setItemValue("name", key); - metricData.setItemValue("country", invoice.getItemValueString("invoice.country")); - metricData.setItemValue("currency", invoice.getItemValueString("invoice.currency")); - metricData.setItemValue("department", invoice.getItemValueString("space.name")); + metricData.setItemValue("bp_id", InvoiceUtil.getBPId(invoice)); + metricData.setItemValue("bp_name", InvoiceUtil.getBPName(invoice)); + metricData.setItemValue("invoice_country", invoice.getItemValueString("invoice.country")); + metricData.setItemValue("invoice_currency", invoice.getItemValueString("invoice.currency")); + metricData.setItemValue("invoice_department", invoice.getItemValueString("space.name")); + return metricData; } @@ -241,21 +249,25 @@ public class MetricDebitorService { * * @param metricData - the metricData ItemCollection */ - private void updateGauge(ItemCollection metricData) { - // String dbtrNumber = metricData.getItemValueString("dbtr.number"); - // String dbtrName = metricData.getItemValueString("dbtr.name"); - String country = metricData.getItemValueString("country"); - String department = metricData.getItemValueString("department"); - String currency = metricData.getItemValueString("currency"); + public void updateGauge(ItemCollection metricData) { + String metricKey = metricData.getItemValueString("name"); + String bpName = metricData.getItemValueString("bp_name"); + String bpId = metricData.getItemValueString("bp_id"); + String country = metricData.getItemValueString("invoice_country"); + String department = metricData.getItemValueString("invoice_department"); + String currency = metricData.getItemValueString("invoice_currency"); + // Prüfen ob Gauge bereits registriert ist if (registeredGauges.add(metricKey)) { // returns true newly added 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.info("register new metric for department: " + department + + logger.fine("register new metric for department: " + department + ", " + metricData.getItemValueString(ITEM_SALDO) + " " + currency); Metadata metadata = Metadata.builder() @@ -273,19 +285,6 @@ public class MetricDebitorService { } - /** - * Hilfsmethode um Metriken initial aufzubauen. Wird vom Metric Rest Service - * verwendet. - * - */ - protected void initMetric(ItemCollection metric) { - String key = metric.getItemValueString("name"); - // Aktuelle metric cachen - metricCache.put(key, metric); - logger.info("│   │   ├──init metric for debitor " + key); - updateGauge(metric); - } - /** * Addiert den saldo einer Invoice zu einem metricData object * @@ -323,4 +322,18 @@ public class MetricDebitorService { metricData.setItemValue(ITEM_SALDO, InvoiceUtil.round(lastSaldo - 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(); + for (String hashKey : keys) { + ItemCollection metricData = getMetric(hashKey); + documentService.save(metricData); + updateGauge(metricData); + } + } } \ No newline at end of file