op-listen udpate

This commit is contained in:
Ralph Soika 2024-12-01 09:46:54 +01:00
parent 633bcdd3d6
commit 61a7fa8399
5 changed files with 151 additions and 130 deletions

View file

@ -84,7 +84,7 @@ public class MetricDebitorRestService {
@Path("/init")
@Produces({ MediaType.TEXT_PLAIN })
public Response initMetrics() {
Map<String, DbtrData> dbtrDataCache = new HashMap<String, DbtrData>();
Map<String, ItemCollection> metricCache = new HashMap<String, ItemCollection>();
long l = System.currentTimeMillis();
logger.info("├── init metrics...");
try {
@ -97,32 +97,34 @@ public class MetricDebitorRestService {
String spaceName = space.getItemValueString("space.name");
spaceNames.add(spaceName);
logger.info("│   ├── grouping invoices by debitor...");
groupInvoicesByWeek(space.getUniqueID(), spaceName, dbtrDataCache);
groupInvoicesByWeek(space.getUniqueID(), spaceName, metricCache);
}
logger.info("│   ├── grouping invoices finished in " + (System.currentTimeMillis() - l) + "ms");
rebuildMetrics(dbtrDataCache);
rebuildMetrics(metricCache);
logger.info("├── init metrics completed in " + (System.currentTimeMillis() - l) + "ms");
return Response.ok().entity("init metrics completed in " + (System.currentTimeMillis() - l) + "ms").build();
} catch (Exception e) {
e.printStackTrace();
return Response.serverError()
.entity("Failed to initialize metrics: " + e.getMessage())
.build();
}
}
private void rebuildMetrics(Map<String, DbtrData> dbtrDataCache) throws PluginException {
private void rebuildMetrics(Map<String, ItemCollection> metricCache) throws PluginException {
logger.info("│   ├── rebuild metrics...");
for (String dbtrNumber : dbtrDataCache.keySet()) {
DbtrData dbtrData = dbtrDataCache.get(dbtrNumber);
String dbtrName = dbtrData.dbtrName;
for (String dbtrNumber : metricCache.keySet()) {
ItemCollection metricData = metricCache.get(dbtrNumber);
// String dbtrName = metricData.getItemValueString("dbtr.name");
// ItemCollection metric = metricService.loadMetric(dbtrNumber, dbtrName);
// metric.setItemValue(MetricDebitorService.ITEM_TOTALS, metricData.totals);
documentService.save(metricData);
metricService.initMetric(metricData);
ItemCollection metric = metricService.loadMetric(dbtrNumber, dbtrName);
metric.setItemValue(MetricDebitorService.ITEM_SALDO, dbtrData.getTotal("EUR"));
documentService.save(metric);
metricService.initMetric(metric);
}
}
@ -132,17 +134,19 @@ public class MetricDebitorRestService {
* @param spaceID - Space Ref to select a list of invoices associated with
* a
* space
* @param dbtrDataCache - a local cache storing all invoices by week
* @param metricCache - a local cache storing all invoices by week
*
*
*/
private void groupInvoicesByWeek(String spaceID, String spaceName,
Map<String, DbtrData> dbtrDataCache) {
Map<String, ItemCollection> metricCache) {
logger.info("│   │   ├── group invoices for " + spaceName + "/" + spaceID);
try {
List<ItemCollection> invoices = documentService.find(
"$modelversion:rechnungsausgang-* AND type:workitem AND $uniqueidref:" + spaceID, 9999, 0,
"$modelversion:rechnungsausgang-* AND type:workitem AND $uniqueidref:" +
spaceID,
9999, 0,
"invoice.number", false);
logger.fine(" found " + invoices.size() + " for space " + spaceName);
@ -151,14 +155,15 @@ public class MetricDebitorRestService {
String debitorName = invoice.getItemValueString("dbtr.name");
String debitorNumber = invoice.getItemValueString("dbtr.number");
DbtrData invoiceData = dbtrDataCache.get(debitorNumber);
if (invoiceData == null) {
invoiceData = new DbtrData(debitorNumber, debitorName);
ItemCollection metricData = metricCache.get(debitorNumber);
if (metricData == null) {
metricData = metricService.createMetaData(debitorNumber, debitorName);
}
// Jetzt Rechnung addieren
invoiceData.add(invoice);
dbtrDataCache.put(debitorNumber, invoiceData);
addInvoice(metricData, invoice);
// invoiceData.add(invoice);
metricCache.put(debitorNumber, metricData);
}
} catch (QueryException e) {
@ -168,41 +173,17 @@ public class MetricDebitorRestService {
}
/**
* Data Element for invoice totals by debitor.
* The object holds multiple currencies.
* Addiert den saldo einer Invoice zu einem metricData object
*
* @param metricData
* @param invoice
*/
class DbtrData {
String dbtrName;
String dbtrNumber;
Map<String, Double> totals = new HashMap<>();
public DbtrData(String dbtrNumber, String dbtrName) {
this.dbtrName = dbtrName;
this.dbtrNumber = dbtrNumber;
}
public void add(ItemCollection invoice) {
public void addInvoice(ItemCollection metricData, ItemCollection invoice) {
String currency = invoice.getItemValueString("invoice.currency");
Double total = invoice.getItemValueDouble("invoice.saldo");
Double totalCurrency = totals.get(currency);
if (totalCurrency == null) {
totalCurrency = 0.0;
}
totalCurrency = totalCurrency + total;
totals.put(currency, InvoiceUtil.round(totalCurrency));
}
double saldo = invoice.getItemValueDouble("invoice.saldo");
double saldoOld = metricData.getItemValueDouble(MetricDebitorService.ITEM_SALDO + "." + currency);
double saldoNew = InvoiceUtil.round(saldoOld + saldo);
public double getTotal(String currency) {
Double result = totals.get(currency);
if (result != null) {
return result;
} else {
return 0.0;
metricData.setItemValue(MetricDebitorService.ITEM_SALDO + "." + currency, saldoNew);
}
}
}
}

View file

@ -47,9 +47,8 @@ import jakarta.inject.Inject;
public class MetricDebitorService {
private static Logger logger = Logger.getLogger(MetricDebitorService.class.getName());
private final ConcurrentHashMap<String, Double> currentSaldos = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, ItemCollection> metricCache = new ConcurrentHashMap<>();
public static final String METRIC_INVOICES = "invoices";
public static final String TYPE_METRIC_DEBITOR = "metric.debitor";
public static final String ITEM_SALDO = "invoice.saldo";
@ -76,36 +75,38 @@ public class MetricDebitorService {
// verify if saldo has changed.....
double invoiceSaldo = invoice.getItemValueDouble(ITEM_SALDO);
String dbtrNumber = invoice.getItemValueString("dbtr.number");
String dbtrName = invoice.getItemValueString("dbtr.name");
String currency = invoice.getItemValueString("invoice.currency");
// load last metric...
double lastInvoiceSaldo = readDirtySaldo(invoice.getUniqueID());
if (invoiceSaldo == lastInvoiceSaldo) {
// no change - no metric update!
return;
}
// update metric and cache last value
// update metric and the metric cache
if (processingEvent.getEventType() == ProcessingEvent.AFTER_PROCESS) {
String dbtrNumber = invoice.getItemValueString("dbtr.number");
String dbtrName = invoice.getItemValueString("dbtr.name");
ItemCollection metric = loadMetric(dbtrNumber, dbtrName);
// Saldo-Berechnung wie bisher
debitorSaldo = metric.getItemValueDouble(ITEM_SALDO);
ItemCollection metricData = metricCache.get(dbtrNumber);
if (metricData == null) {
metricData = loadMetric(dbtrNumber, dbtrName);
}
// Saldo-Berechnung
debitorSaldo = metricData.getItemValueDouble(ITEM_SALDO + "." + currency); // totals.get(currency);
debitorSaldo = debitorSaldo - lastInvoiceSaldo;
// update debitor saldo
debitorSaldo = InvoiceUtil.round(debitorSaldo + invoiceSaldo);
metricData.setItemValue(ITEM_SALDO + "." + currency, debitorSaldo);
metricCache.put(dbtrNumber, metricData);
// Speichern in der Datenbank
metric.setItemValue(ITEM_SALDO, debitorSaldo);
logger.info("--- update Debitor Saldo " + dbtrNumber + " -> " + debitorSaldo);
documentService.save(metric);
logger.info("Update metric for debitor: " + dbtrNumber + " -> " + debitorSaldo);
documentService.save(metricData);
// Aktuellen Saldo im Memory speichern
currentSaldos.put(dbtrNumber, debitorSaldo);
// Gauge registrieren
registerGauge(dbtrNumber, metric.getItemValueString("dbtr.name"));
logger.info("--- new gauge value for " + dbtrNumber + " -> " + debitorSaldo);
registerGauge(metricData, currency);
}
}
@ -119,8 +120,11 @@ public class MetricDebitorService {
public double readDirtySaldo(String uniqueid) {
double result = 0;
ItemCollection dirtyInvoice = documentService.load(uniqueid);
if (dirtyInvoice == null) {
return 0.0;
}
result = dirtyInvoice.getItemValueDouble(ITEM_SALDO);
logger.info("---Dirty last Saldo was : " + result);
// logger.info("---Dirty last Saldo was : " + result);
return result;
}
@ -147,12 +151,9 @@ public class MetricDebitorService {
}
if (debitorMetric == null) {
// create a new instance
logger.info("creating new debitor metric");
debitorMetric = new ItemCollection();
debitorMetric.setType(TYPE_METRIC_DEBITOR);
debitorMetric.setItemValue("name", dbtrNumber);
debitorMetric.setItemValue("dbtr.number", dbtrNumber);
debitorMetric.setItemValue("dbtr.name", dbtrName);
// logger.info("creating new debitor metric");
debitorMetric = createMetaData(dbtrNumber, dbtrName);
}
} catch (QueryException e) {
throw new PluginException(PluginException.class.getName(), "QUERY ERROR", e.getMessage(), e);
@ -161,6 +162,27 @@ public class MetricDebitorService {
return debitorMetric;
}
/**
* Creates an empty Debitor Meta Data Object (ItemCollection)
* <p>
* The ItemCollection stores the name and number and also all saldos for all
* currencies
*
* @param dbtrNumber
* @param dbtrName
* @return
*/
public ItemCollection createMetaData(String dbtrNumber, String dbtrName) {
ItemCollection metricData = new ItemCollection();
metricData = new ItemCollection();
metricData.setType(TYPE_METRIC_DEBITOR);
metricData.setItemValue("name", dbtrNumber);
metricData.setItemValue("dbtr.number", dbtrNumber);
metricData.setItemValue("dbtr.name", dbtrName);
return metricData;
}
/**
* Hilfsmethode um Metriken initial aufzubauen. Wird vom Metric Rest Service
* verwendet.
@ -168,13 +190,18 @@ public class MetricDebitorService {
*/
protected void initMetric(ItemCollection metric) {
String dbtrNumber = metric.getItemValueString("dbtr.number");
double saldo = metric.getItemValueDouble(ITEM_SALDO);
// Aktuellen Saldo in Map speichern
currentSaldos.put(dbtrNumber, saldo);
// Gauge registrieren
registerGauge(dbtrNumber, metric.getItemValueString("dbtr.name"));
logger.info("Initialized metric for debitor " + dbtrNumber + " -> " + saldo);
metricCache.put(dbtrNumber, metric);
// Gauge für jede Währung registrieren
for (String itemName : metric.getItemNames()) {
if (itemName.startsWith(ITEM_SALDO + ".")) {
// currency value found
int pos = (ITEM_SALDO + ".").length();
String currency = itemName.substring(pos + 1);
logger.info("Init metric for debitor " + dbtrNumber + " -> " + currency);
registerGauge(metric, currency);
}
}
}
/**
@ -183,20 +210,25 @@ public class MetricDebitorService {
* @param dbtrNumber - the debitor number
* @param dbtrName - the debitor name
*/
private void registerGauge(String dbtrNumber, String dbtrName) {
private void registerGauge(ItemCollection metricData, String currency) {
String dbtrNumber = metricData.getItemValueString("dbtr.number");
String dbtrName = metricData.getItemValueString("dbtr.name");
List<Tag> tags = new ArrayList<>();
tags.add(new Tag("type", "dbtr"));
tags.add(new Tag("number", dbtrNumber));
tags.add(new Tag("name", dbtrName));
tags.add(new Tag("currency", currency));
logger.info("Register metric for debitor: " + dbtrNumber + " -> " + currency);
Metadata metadata = Metadata.builder()
.withName("dbtr.invoice.saldo")
.withDescription("Debitor Balance")
.withUnit("EUR")
.withDescription("Debitor Balance by Currency")
.build();
metricRegistry.gauge(metadata,
() -> currentSaldos.getOrDefault(dbtrNumber, 0.0),
() -> metricCache.getOrDefault(dbtrNumber, createMetaData(dbtrNumber, dbtrName))
.getItemValueDouble(ITEM_SALDO + "." + currency),
tags.toArray(new Tag[0]));
}
}

View file

@ -161,7 +161,7 @@ public class OPListExportScheduler implements Scheduler {
workflowService.processWorkItem( //
workitem.task(1000). //
event(100). //
model("opliste-de-1.0"));
model("(^analyse-opliste-.*)"));
}

View file

@ -189,9 +189,7 @@ th { font-weight: bold;}
<p>
Bitte prüfen Sie die angefügte OP-Liste. Diese enthält sämtlich noch offenen Rechnungen zum Zeitpunkt des Exports für diesen Fachbereich.
</p>
<p>
Nach Prüfung schließen Sie bitte den Vorgang über die Schaltfläche "Erledigt" ab.
</p>
<a href="<propertyvalue>application.url</propertyvalue>pages/workitems/workitem.jsf?id=<itemvalue>$uniqueid</itemvalue>"><propertyvalue>application.url</propertyvalue>pages/workitems/workitem.jsf?id=<itemvalue>$uniqueid</itemvalue></a>
<attachments></attachments>
<bpmn2:message>Html-Footer</bpmn2:message>]]></imixs:value>
@ -231,7 +229,9 @@ Nach Prüfung schließen Sie bitte den Vorgang über die Schaltfläche "Erledigt
<imixs:item name="keyupdateacl" type="xs:boolean">
<imixs:value>false</imixs:value>
</imixs:item>
<imixs:item name="nammailreceiver" type="xs:string"/>
<imixs:item name="nammailreceiver" type="xs:string">
<imixs:value/>
</imixs:item>
<imixs:item name="keymailreceiverfields" type="xs:string">
<imixs:value><![CDATA[process.manager]]></imixs:value>
</imixs:item>
@ -251,14 +251,19 @@ Nach Prüfung schließen Sie bitte den Vorgang über die Schaltfläche "Erledigt
<find>B7</find>
<replace><itemvalue>space.name</itemvalue></replace>
<type>text</type>
</poi-update>
</poi-update>
<poi-update name="findreplace">
<find>F6</find>
<replace><itemvalue format="dd.MM.yyyy">$lasteventdate</itemvalue></replace>
<type>date</type>
</poi-update>]]></imixs:value>
</imixs:item>
<imixs:item name="nammailreceivercc" type="xs:string">
<imixs:value/>
</imixs:item>
<imixs:item name="nammailreceiverbcc" type="xs:string">
<imixs:value/>
</imixs:item>
</bpmn2:extensionElements>
<bpmn2:documentation id="Documentation_1"><![CDATA[OP-Liste an Fachbereich versenden]]></bpmn2:documentation>
<bpmn2:incoming>SequenceFlow_5</bpmn2:incoming>
@ -359,30 +364,6 @@ Nach Prüfung schließen Sie bitte den Vorgang über die Schaltfläche "Erledigt
<imixs:item name="keypublicresult" type="xs:string">
<imixs:value><![CDATA[1]]></imixs:value>
</imixs:item>
<imixs:item name="txtactivityresult" type="xs:string">
<imixs:value><![CDATA[<opliste name="excel-export">
<textblock>OP-Liste Template</textblock>
<template>op-liste_template.xlsx</template>
<target-name>op-liste_<itemvalue>dbtr.number</itemvalue>_<itemvalue format="yyyy-MM-dd">$lasteventdate</itemvalue>.xlsx</target-name>
</opliste>
<poi-update name="findreplace">
<find>C6</find>
<replace><itemvalue>sequencenumber</itemvalue></replace>
<type>text</type>
</poi-update>
<poi-update name="findreplace">
<find>B7</find>
<replace><itemvalue>space.name</itemvalue></replace>
<type>text</type>
</poi-update>
<poi-update name="findreplace">
<find>F6</find>
<replace><itemvalue format="dd.MM.yyyy">$lasteventdate</itemvalue></replace>
<type>date</type>
</poi-update>]]></imixs:value>
</imixs:item>
<imixs:item name="txtbusinessrule" type="xs:string">
<imixs:value><![CDATA[var result={};
var refField="space.ref";
@ -393,6 +374,18 @@ if (workitem.get(refField) == null || ''==workitem.get(refField)[0]) {
result.errorMessage='Bitte wählen Sie einen Fachbereich aus.';
}]]></imixs:value>
</imixs:item>
<imixs:item name="txtactivityresult" type="xs:string">
<imixs:value><![CDATA[<opliste name="excel-export">
<textblock>OP-Liste Template</textblock>
<template>op-liste_template.xlsx</template>
<target-name>op-liste_<itemvalue>dbtr.number</itemvalue>_<itemvalue format="yyyy-MM-dd">$lasteventdate</itemvalue>.xlsx</target-name>
</opliste>
<poi-update name="findreplace">
<find>A8</find>
<replace><itemvalue format="dd.MM.yyyy">$lasteventdate</itemvalue></replace>
<type>date</type>
</poi-update>]]></imixs:value>
</imixs:item>
</bpmn2:extensionElements>
<bpmn2:outgoing>SequenceFlow_6</bpmn2:outgoing>
<bpmn2:signalEventDefinition id="SignalEventDefinition_2" signalRef="Signal_1"/>
@ -443,13 +436,23 @@ if (workitem.get(refField) == null || ''==workitem.get(refField)[0]) {
<template>op-liste_kw_template_de.xlsx</template>
<target-name>op_liste_nach_kw_<itemvalue format="yyyy-MM-dd">$lasteventdate</itemvalue>.xlsx</target-name>
</opliste>
<poi-update name="findreplace">
<find>A8</find>
<find>C6</find>
<replace><itemvalue>sequencenumber</itemvalue></replace>
<type>text</type>
</poi-update>
<poi-update name="findreplace">
<find>B7</find>
<replace><itemvalue>space.name</itemvalue></replace>
<type>text</type>
</poi-update>
<poi-update name="findreplace">
<find>F6</find>
<replace><itemvalue format="dd.MM.yyyy">$lasteventdate</itemvalue></replace>
<type>date</type>
</poi-update>
]]></imixs:value>
</poi-update>]]></imixs:value>
</imixs:item>
</bpmn2:extensionElements>
<bpmn2:signalEventDefinition id="signalEventDefinition_7PWLCA" signalRef="signal_2"/>

View file

@ -187,10 +187,7 @@ th { font-weight: bold;}
<imixs:value><![CDATA[<bpmn2:message>Html-Header</bpmn2:message>
<p>Hallo,</p>
<p>
Bitte prüfen Sie die angefügte OP-Liste. Diese enthält sämtlich noch offenen Rechnungen zum Zeitpunkt des Exports für diesen Fachbereich.
</p>
<p>
Nach Prüfung schließen Sie bitte den Vorgang über die Schaltfläche "Erledigt" ab.
Please check the attached list of outstanding payments. This list contains all outstanding invoices at the time of export for this department.
</p>
<a href="<propertyvalue>application.url</propertyvalue>pages/workitems/workitem.jsf?id=<itemvalue>$uniqueid</itemvalue>"><propertyvalue>application.url</propertyvalue>pages/workitems/workitem.jsf?id=<itemvalue>$uniqueid</itemvalue></a>
<attachments></attachments>
@ -231,7 +228,9 @@ Nach Prüfung schließen Sie bitte den Vorgang über die Schaltfläche "Erledigt
<imixs:item name="keyupdateacl" type="xs:boolean">
<imixs:value>false</imixs:value>
</imixs:item>
<imixs:item name="nammailreceiver" type="xs:string"/>
<imixs:item name="nammailreceiver" type="xs:string">
<imixs:value/>
</imixs:item>
<imixs:item name="keymailreceiverfields" type="xs:string">
<imixs:value><![CDATA[process.manager]]></imixs:value>
</imixs:item>
@ -259,6 +258,12 @@ Nach Prüfung schließen Sie bitte den Vorgang über die Schaltfläche "Erledigt
<type>date</type>
</poi-update>]]></imixs:value>
</imixs:item>
<imixs:item name="nammailreceivercc" type="xs:string">
<imixs:value/>
</imixs:item>
<imixs:item name="nammailreceiverbcc" type="xs:string">
<imixs:value/>
</imixs:item>
</bpmn2:extensionElements>
<bpmn2:documentation id="Documentation_1"><![CDATA[OP-Liste an Fachbereich versenden]]></bpmn2:documentation>
<bpmn2:incoming>SequenceFlow_5</bpmn2:incoming>