business partner import service
This commit is contained in:
parent
632f674bfa
commit
021e7cec34
11 changed files with 1024 additions and 1347 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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<String> workflowServiceEndpoint;
|
||||
|
|
@ -91,8 +86,8 @@ public class BusinessPartnerService {
|
|||
@ConfigProperty(name = WORKFLOW_SERVICE_PASSWORD)
|
||||
Optional<String> 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<ItemCollection> 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<ItemCollection> search(String phrase) {
|
||||
long l = System.currentTimeMillis();
|
||||
|
||||
List<ItemCollection> searchResult = new ArrayList<ItemCollection>();
|
||||
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());
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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<ItemCollection> 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");
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ItemCollection> 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<ItemCollection> 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");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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).
|
||||
* <p>
|
||||
* Beim Rechnungseingang gilt eine Rechnung als Bezahlt wenn diese einen finalen
|
||||
* Status erreicht hat (>=5800)
|
||||
* <p>
|
||||
* 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<String, ItemCollection> metricCache = new ConcurrentHashMap<>();
|
||||
private final Set<String> 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<ItemCollection> 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<String> getMetricKeys() {
|
||||
return Collections.unmodifiableList(new ArrayList<>(metricCache.keySet()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty Creditor Metric Data Object (ItemCollection)
|
||||
* <p>
|
||||
* 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<Tag> 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<String> 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));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<ItemCollection> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ItemCollection> 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<ItemCollection> 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");
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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).
|
||||
* <p>
|
||||
* 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<String, ItemCollection> metricCache = new ConcurrentHashMap<>();
|
||||
private final Set<String> 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<ItemCollection> 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<String> getMetricKeys() {
|
||||
return Collections.unmodifiableList(new ArrayList<>(metricCache.keySet()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty Debitor Meta Data Object (ItemCollection)
|
||||
* <p>
|
||||
* 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<Tag> 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));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
883
workflow/businesspartner-en-1.0.0.bpmn
Normal file
883
workflow/businesspartner-en-1.0.0.bpmn
Normal file
|
|
@ -0,0 +1,883 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<bpmn2:definitions xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:BPMN2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:imixs="http://www.imixs.org/bpmn2" xmlns:open-bpmn="http://open-bpmn.org/XMLSchema" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" exporter="org.openbpmn" exporterVersion="1.0.0" targetNamespace="http://open-bpmn.org">
|
||||
<bpmn2:collaboration id="collaboration_1" name="Default Collaboration">
|
||||
<bpmn2:participant id="participant_d6UFsQ" name="Default Process" processRef="process_1"/>
|
||||
<bpmn2:participant id="participant_oxb3dg" name="Business Partner" processRef="process_Hw9Ryg">
|
||||
<bpmn2:documentation id="documentation_Jerngw"/>
|
||||
<bpmn2:outgoing>association_83sZPw</bpmn2:outgoing>
|
||||
</bpmn2:participant>
|
||||
</bpmn2:collaboration>
|
||||
<bpmn2:extensionElements>
|
||||
<open-bpmn:auto-align>true</open-bpmn:auto-align>
|
||||
<imixs:item name="txtworkflowmodelversion" type="xs:string">
|
||||
<imixs:value><![CDATA[businesspartner-de-1.0]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="txtplugins" type="xs:string">
|
||||
<imixs:value><![CDATA[org.imixs.workflow.engine.plugins.RulePlugin]]></imixs:value>
|
||||
<imixs:value><![CDATA[org.imixs.workflow.engine.plugins.SplitAndJoinPlugin]]></imixs:value>
|
||||
<imixs:value><![CDATA[org.imixs.workflow.engine.plugins.OwnerPlugin]]></imixs:value>
|
||||
<imixs:value><![CDATA[org.imixs.workflow.engine.plugins.ApproverPlugin]]></imixs:value>
|
||||
<imixs:value><![CDATA[org.imixs.workflow.engine.plugins.HistoryPlugin]]></imixs:value>
|
||||
<imixs:value><![CDATA[org.imixs.workflow.engine.plugins.ApplicationPlugin]]></imixs:value>
|
||||
<imixs:value><![CDATA[org.imixs.workflow.engine.plugins.IntervalPlugin]]></imixs:value>
|
||||
<imixs:value><![CDATA[org.imixs.workflow.engine.plugins.MailPlugin]]></imixs:value>
|
||||
<imixs:value><![CDATA[org.imixs.workflow.engine.plugins.ResultPlugin]]></imixs:value>
|
||||
<imixs:value><![CDATA[org.imixs.marty.team.TeamPlugin]]></imixs:value>
|
||||
<imixs:value><![CDATA[org.imixs.marty.plugins.SequenceNumberPlugin]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="txtfieldmapping" type="xs:string">
|
||||
<imixs:value><![CDATA[Ersteller | $creator]]></imixs:value>
|
||||
<imixs:value><![CDATA[Aktueller Bearbeiter | $editor]]></imixs:value>
|
||||
<imixs:value><![CDATA[Eigentümer | $owner]]></imixs:value>
|
||||
<imixs:value><![CDATA[Prozess-Verantwortliche| process.manager]]></imixs:value>
|
||||
<imixs:value><![CDATA[Prozess-Team | process.team]]></imixs:value>
|
||||
<imixs:value><![CDATA[Prozess-Assistenz | process.assist]]></imixs:value>
|
||||
</imixs:item>
|
||||
</bpmn2:extensionElements>
|
||||
<bpmn2:process definitionalCollaborationRef="collaboration_1" id="process_1" name="Default Process" processType="Public">
|
||||
<bpmn2:documentation id="documentation_bowXyw"/>
|
||||
</bpmn2:process>
|
||||
<bpmn2:process definitionalCollaborationRef="collaboration_1" id="process_Hw9Ryg" isExecutable="true" name="Business Partner" processType="Private">
|
||||
<bpmn2:laneSet id="laneset_nKajSg" name="Lane Set">
|
||||
<bpmn2:lane id="lane_Zxa2TA" name="Buchhaltung">
|
||||
<bpmn2:documentation id="documentation_jEAKkQ"/>
|
||||
<bpmn2:flowNodeRef>task_ToMZaQ</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>event_WmkBvw</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>event_1clvlg</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>task_Q80A1Q</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>task_B8Pi7A</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>event_aSdkwg</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>event_Tr0aug</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>dataObject_UQIXAQ</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>event_vwH0cA</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>textAnnotation_LyGTCw</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>event_TuyuwQ</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>event_h0Kk5g</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>task_delO7Q</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>event_1OVorA</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>event_baWU4w</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>event_ofjVfg</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>event_i7yQVw</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>gateway_uK0c6g</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>event_6250fg</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>event_VM0fEQ</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>gateway_s0J53g</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>task_NohgrQ</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>event_AMs05g</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>event_feLZFQ</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>event_GTXFMw</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>event_u8OBFg</bpmn2:flowNodeRef>
|
||||
</bpmn2:lane>
|
||||
</bpmn2:laneSet>
|
||||
<bpmn2:task id="task_ToMZaQ" imixs:processid="1000" name="Neuanlage">
|
||||
<bpmn2:extensionElements>
|
||||
<imixs:item name="keyupdateacl" type="xs:string">
|
||||
<imixs:value><![CDATA[true]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="namownershipnames" type="xs:string">
|
||||
<imixs:value/>
|
||||
</imixs:item>
|
||||
<imixs:item name="namaddreadaccess" type="xs:string">
|
||||
<imixs:value/>
|
||||
</imixs:item>
|
||||
<imixs:item name="namaddwriteaccess" type="xs:string">
|
||||
<imixs:value/>
|
||||
</imixs:item>
|
||||
</bpmn2:extensionElements>
|
||||
<bpmn2:documentation id="documentation_a83GTA"/>
|
||||
<bpmn2:incoming>sequenceFlow_25otUg</bpmn2:incoming>
|
||||
<bpmn2:outgoing>sequenceFlow_S9LVXg</bpmn2:outgoing>
|
||||
</bpmn2:task>
|
||||
<bpmn2:startEvent id="event_WmkBvw" name="Start">
|
||||
<bpmn2:documentation id="documentation_QXQddg"/>
|
||||
<bpmn2:outgoing>sequenceFlow_25otUg</bpmn2:outgoing>
|
||||
</bpmn2:startEvent>
|
||||
<bpmn2:endEvent id="event_1clvlg" name="Ende">
|
||||
<bpmn2:documentation id="documentation_obvnAg"/>
|
||||
<bpmn2:incoming>sequenceFlow_rwdWxw</bpmn2:incoming>
|
||||
</bpmn2:endEvent>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_25otUg" sourceRef="event_WmkBvw" targetRef="task_ToMZaQ">
|
||||
<bpmn2:documentation id="documentation_Tzm1bw"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:task id="task_Q80A1Q" imixs:processid="1100" name="Activ">
|
||||
<bpmn2:extensionElements>
|
||||
<imixs:item name="txtimageurl" type="xs:string">
|
||||
<imixs:value><![CDATA[typcn-user|typcn-tick,imixs-success]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="txtworkflowsummary" type="xs:string">
|
||||
<imixs:value><![CDATA[<itemvalue>partner.name</itemvalue> (<itemvalue>partner.id</itemvalue>)]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="txtworkflowabstract" type="xs:string">
|
||||
<imixs:value><![CDATA[<strong>Adresse:</strong><br />
|
||||
<itemvalue>partner.name</itemvalue><br />
|
||||
<itemvalue>partner.address</itemvalue><br />
|
||||
<itemvalue>partner.zip</itemvalue> <itemvalue>partner.city</itemvalue><br />
|
||||
<br />
|
||||
|
||||
<strong>BPID:</strong> <itemvalue>partner.id</itemvalue><br />
|
||||
<strong>Kreditoren-Nr.:</strong> <itemvalue>cdtr.number</itemvalue><br />
|
||||
<strong>Debitoren-Nr.:</strong> <itemvalue>dbtr.number</itemvalue><br />
|
||||
]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="keyupdateacl" type="xs:string">
|
||||
<imixs:value><![CDATA[true]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="namownershipnames" type="xs:string">
|
||||
<imixs:value/>
|
||||
</imixs:item>
|
||||
<imixs:item name="namaddreadaccess" type="xs:string">
|
||||
<imixs:value/>
|
||||
</imixs:item>
|
||||
<imixs:item name="namaddwriteaccess" type="xs:string">
|
||||
<imixs:value/>
|
||||
</imixs:item>
|
||||
<imixs:item name="keyaddwritefields" type="xs:string">
|
||||
<imixs:value><![CDATA[process.team]]></imixs:value>
|
||||
</imixs:item>
|
||||
</bpmn2:extensionElements>
|
||||
<bpmn2:documentation id="documentation_M7b42w"/>
|
||||
<bpmn2:incoming>sequenceFlow_avahXg</bpmn2:incoming>
|
||||
<bpmn2:incoming>sequenceFlow_9atLFA</bpmn2:incoming>
|
||||
<bpmn2:outgoing>sequenceFlow_T5L9aQ</bpmn2:outgoing>
|
||||
<bpmn2:incoming>sequenceFlow_b4BcVA</bpmn2:incoming>
|
||||
<bpmn2:incoming>sequenceFlow_8O1EtQ</bpmn2:incoming>
|
||||
<bpmn2:incoming>sequenceFlow_ShuXXA</bpmn2:incoming>
|
||||
<bpmn2:outgoing>sequenceFlow_03b60w</bpmn2:outgoing>
|
||||
</bpmn2:task>
|
||||
<bpmn2:task id="task_B8Pi7A" imixs:processid="1800" name="Locked">
|
||||
<bpmn2:extensionElements>
|
||||
<imixs:item name="txtworkflowsummary" type="xs:string">
|
||||
<imixs:value><![CDATA[<itemvalue>partner.name</itemvalue> (<itemvalue>partner.id</itemvalue>)]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="txtimageurl" type="xs:string">
|
||||
<imixs:value><![CDATA[typcn-user|typcn-times,imixs-error]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="txtworkflowabstract" type="xs:string">
|
||||
<imixs:value><![CDATA[<strong>Adresse:</strong><br />
|
||||
<itemvalue>partner.name</itemvalue><br />
|
||||
<itemvalue>partner.address</itemvalue><br />
|
||||
<itemvalue>partner.zip</itemvalue> <itemvalue>partner.city</itemvalue><br />
|
||||
<br />
|
||||
|
||||
<strong>BPID:</strong> <itemvalue>partner.id</itemvalue><br />
|
||||
<strong>Kreditoren-Nr.:</strong> <itemvalue>cdtr.number</itemvalue><br />
|
||||
<strong>Debitoren-Nr.:</strong> <itemvalue>dbtr.number</itemvalue><br />
|
||||
]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="keyupdateacl" type="xs:string">
|
||||
<imixs:value><![CDATA[true]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="namownershipnames" type="xs:string">
|
||||
<imixs:value/>
|
||||
</imixs:item>
|
||||
<imixs:item name="namaddreadaccess" type="xs:string">
|
||||
<imixs:value/>
|
||||
</imixs:item>
|
||||
<imixs:item name="namaddwriteaccess" type="xs:string">
|
||||
<imixs:value/>
|
||||
</imixs:item>
|
||||
<imixs:item name="keyaddwritefields" type="xs:string">
|
||||
<imixs:value><![CDATA[process.team]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="txttype" type="xs:string">
|
||||
<imixs:value><![CDATA[workitemarchive]]></imixs:value>
|
||||
</imixs:item>
|
||||
</bpmn2:extensionElements>
|
||||
<bpmn2:documentation id="documentation_0B9wKQ"/>
|
||||
<bpmn2:incoming>sequenceFlow_1mXO8A</bpmn2:incoming>
|
||||
<bpmn2:outgoing>sequenceFlow_rwdWxw</bpmn2:outgoing>
|
||||
<bpmn2:incoming>sequenceFlow_gC0I9w</bpmn2:incoming>
|
||||
<bpmn2:incoming>sequenceFlow_kS2fMA</bpmn2:incoming>
|
||||
</bpmn2:task>
|
||||
<bpmn2:intermediateCatchEvent id="event_aSdkwg" imixs:activityid="100" name="[import]">
|
||||
<bpmn2:extensionElements>
|
||||
<imixs:item name="keypublicresult" type="xs:string">
|
||||
<imixs:value><![CDATA[0]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="rtfresultlog" type="xs:string">
|
||||
<imixs:value><![CDATA[Business Partner aus Cargosoft importiert]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="txtactivityresult" type="xs:string">
|
||||
<imixs:value><![CDATA[<item name="process">Partnermanagement</item>]]></imixs:value>
|
||||
</imixs:item>
|
||||
</bpmn2:extensionElements>
|
||||
<bpmn2:documentation id="documentation_5409Kg"/>
|
||||
<bpmn2:incoming>sequenceFlow_jAU3VA</bpmn2:incoming>
|
||||
<bpmn2:outgoing>sequenceFlow_8O1EtQ</bpmn2:outgoing>
|
||||
</bpmn2:intermediateCatchEvent>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_S9LVXg" sourceRef="task_ToMZaQ" targetRef="gateway_uK0c6g">
|
||||
<bpmn2:documentation id="documentation_Idvd0Q"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:intermediateCatchEvent id="event_Tr0aug" imixs:activityid="30" name="Lock">
|
||||
<bpmn2:extensionElements>
|
||||
<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[<item name="action">home</item>]]></imixs:value>
|
||||
</imixs:item>
|
||||
</bpmn2:extensionElements>
|
||||
<bpmn2:documentation id="documentation_2XhDKQ"/>
|
||||
<bpmn2:outgoing>sequenceFlow_1mXO8A</bpmn2:outgoing>
|
||||
<bpmn2:incoming>sequenceFlow_b0Bj3Q</bpmn2:incoming>
|
||||
<bpmn2:incoming>sequenceFlow_neSsFQ</bpmn2:incoming>
|
||||
</bpmn2:intermediateCatchEvent>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_1mXO8A" sourceRef="event_Tr0aug" targetRef="task_B8Pi7A">
|
||||
<bpmn2:documentation id="documentation_JL48mA"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_rwdWxw" sourceRef="task_B8Pi7A" targetRef="event_1clvlg">
|
||||
<bpmn2:documentation id="documentation_8xdVSA"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:dataObject id="dataObject_UQIXAQ" name="Form">
|
||||
<bpmn2:documentation id="documentation_e1in1w"><![CDATA[<imixs-form>
|
||||
|
||||
<imixs-subform label="Überblick">
|
||||
<imixs-form-section columns="4" label="Ausgangsrechnungen">
|
||||
<item name="analytic.invoices.count.all" type="custom" path="alexander/analyze_plain" label="Alle Rechnungen:" />
|
||||
<item name="analytic.invoices.count.open" type="custom" path="alexander/analyze_plain" label="Offene Rechnungen:" />
|
||||
<item name="analytic.invoices.count.due" type="custom" path="alexander/analyze_plain" label="Fällige Rechnungen:" />
|
||||
<item name="analytic.invoices.count.dunning" type="custom" path="alexander/analyze_plain" label="Rechnungen in Mahnung:" />
|
||||
</imixs-form-section>
|
||||
|
||||
<imixs-form-section label="" path="alexander/section_bp_invoices_out" readonly="true" />
|
||||
<imixs-form-section columns="2" label="Zahlungsmoral">
|
||||
<item name="analytic.payment.avg.due" type="custom" path="alexander/analyze_plain" label="Average term of credit:" />
|
||||
<item name="analytic.payment.avg.days" type="custom" path="alexander/analyze_plain" label="Average payment duration:" />
|
||||
</imixs-form-section>
|
||||
<imixs-form-section columns="1" label="">
|
||||
<item name="analytic.invoices.trend" type="custom" path="alexander/analyze_chart"
|
||||
label="Trend der letzten 6 Monate:" />
|
||||
</imixs-form-section>
|
||||
<imixs-form-section label="Eingangsrechnungen" path="alexander/section_bp_invoices_in" readonly="true" />
|
||||
|
||||
|
||||
</imixs-subform>
|
||||
|
||||
<imixs-subform label="Stammdaten">
|
||||
<imixs-form-section columns="1" label="">
|
||||
<item name="partner.name" type="text" span="6" readonly="true" label="Name:"/>
|
||||
<item name="partner.id" type="text" span="6" readonly="true" label="Partner ID:"/>
|
||||
</imixs-form-section>
|
||||
|
||||
<imixs-form-section columns="1" label="" readonly="true">
|
||||
<item name="partner.address" type="text" span="6" required="false" label="Adresse:" />
|
||||
<item name="partner.address.sub1" type="text" span="6" required="false" label="Zusatz" />
|
||||
<item name="partner.zip" type="text" span="1" required="false" label="PLZ:" />
|
||||
<item name="partner.city" type="text" span="5" required="false" label="Stadt:" />
|
||||
<item name="partner.phone" type="text" readonly="false" span="6" label="Tel.:" />
|
||||
|
||||
<item name="partner.country" type="custom" path="country" span="6" readonly="false" label="Land:" />
|
||||
<item name="partner.language" type="text" readonly="false" span="6" label="Sprache:" />
|
||||
</imixs-form-section>
|
||||
|
||||
<imixs-form-section columns="1" label="Rechnungseingang ">
|
||||
<item name="cdtr.number" type="text" span="2" readonly="true" label="Kreditoren Nr.:" />
|
||||
<item name="cdtr.creditperiod" type="text" span="4" readonly="false" label="Zahlungsziel (Tage):" />
|
||||
<item name="cdtr.email" type="textlist" span="6" readonly="false" label="E-Mail:" />
|
||||
</imixs-form-section>
|
||||
<imixs-form-section columns="1" label="Rechnungsausgang ">
|
||||
<item name="dbtr.number" type="text" span="2" readonly="true" label="Debitoren Nr.:" />
|
||||
<item name="dbtr.creditperiod" type="text" span="4" readonly="false" label="Zahlungsziel (Tage):" />
|
||||
<item name="dbtr.email" type="textlist" span="6" readonly="false" label="E-Mail SOA/Mahnungen:" />
|
||||
</imixs-form-section>
|
||||
<imixs-form-section label="Banken" path="alexander/section_businesspartner_banklist" />
|
||||
</imixs-subform>
|
||||
|
||||
</imixs-form>]]></bpmn2:documentation>
|
||||
</bpmn2:dataObject>
|
||||
<bpmn2:intermediateCatchEvent id="event_vwH0cA" imixs:activityid="10" name="Save">
|
||||
<bpmn2:documentation id="documentation_WOUVTQ"/>
|
||||
<bpmn2:outgoing>sequenceFlow_avahXg</bpmn2:outgoing>
|
||||
</bpmn2:intermediateCatchEvent>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_avahXg" sourceRef="event_vwH0cA" targetRef="task_Q80A1Q">
|
||||
<bpmn2:documentation id="documentation_lhR6Lw"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:association id="association_uvV8uA" sourceRef="participant_oxb3dg" targetRef="task_Q80A1Q"/>
|
||||
<bpmn2:association id="association_mpLQEQ" sourceRef="dataObject_UQIXAQ" targetRef="task_Q80A1Q">
|
||||
<bpmn2:documentation id="documentation_oVnaJw"/>
|
||||
</bpmn2:association>
|
||||
<bpmn2:textAnnotation id="textAnnotation_LyGTCw" textFormat="">
|
||||
<bpmn2:text id="text_M05BXg"><![CDATA[Neuanalge erfolgt über den Cargosoft CSV Import im BusinessPartnerImportService]]></bpmn2:text>
|
||||
<bpmn2:documentation id="documentation_wdTBKA"/>
|
||||
</bpmn2:textAnnotation>
|
||||
<bpmn2:association id="association_83sZPw" sourceRef="participant_oxb3dg" targetRef="textAnnotation_LyGTCw">
|
||||
<bpmn2:documentation id="documentation_FKcIcg"/>
|
||||
</bpmn2:association>
|
||||
<bpmn2:intermediateCatchEvent id="event_TuyuwQ" imixs:activityid="200" name="[update]">
|
||||
<bpmn2:extensionElements>
|
||||
<imixs:item name="keypublicresult" type="xs:string">
|
||||
<imixs:value><![CDATA[0]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="txtactivityresult" type="xs:string">
|
||||
<imixs:value><![CDATA[Business Partner aktualisiert aus Cargosoft ]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="rtfresultlog" type="xs:string">
|
||||
<imixs:value><![CDATA[Business Partner aus Cargosoft aktualisiert]]></imixs:value>
|
||||
</imixs:item>
|
||||
</bpmn2:extensionElements>
|
||||
<bpmn2:documentation id="documentation_wy3OSA"/>
|
||||
<bpmn2:outgoing>sequenceFlow_9atLFA</bpmn2:outgoing>
|
||||
</bpmn2:intermediateCatchEvent>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_9atLFA" sourceRef="event_TuyuwQ" targetRef="task_Q80A1Q">
|
||||
<bpmn2:documentation id="documentation_LHM8vg"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:intermediateCatchEvent id="event_h0Kk5g" imixs:activityid="201" name="[update]">
|
||||
<bpmn2:extensionElements>
|
||||
<imixs:item name="keypublicresult" type="xs:string">
|
||||
<imixs:value><![CDATA[0]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="txtactivityresult" type="xs:string">
|
||||
<imixs:value><![CDATA[Business Partner aktualisiert aus Cargosoft ]]></imixs:value>
|
||||
</imixs:item>
|
||||
</bpmn2:extensionElements>
|
||||
<bpmn2:documentation id="documentation_AnqGdA"/>
|
||||
<bpmn2:outgoing>sequenceFlow_gC0I9w</bpmn2:outgoing>
|
||||
</bpmn2:intermediateCatchEvent>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_gC0I9w" sourceRef="event_h0Kk5g" targetRef="task_B8Pi7A">
|
||||
<bpmn2:documentation id="documentation_gfdNnQ"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:task id="task_delO7Q" imixs:processid="1300" name="Fraud Detection">
|
||||
<bpmn2:extensionElements>
|
||||
<imixs:item name="txtimageurl" type="xs:string">
|
||||
<imixs:value><![CDATA[typcn-user|typcn-tick,imixs-success]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="txtworkflowsummary" type="xs:string">
|
||||
<imixs:value><![CDATA[<itemvalue>partner.name</itemvalue> (<itemvalue>partner.id</itemvalue>)]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="txtworkflowabstract" type="xs:string">
|
||||
<imixs:value><![CDATA[<strong>Adresse:</strong><br />
|
||||
<itemvalue>partner.name</itemvalue><br />
|
||||
<itemvalue>partner.address</itemvalue><br />
|
||||
<itemvalue>partner.zip</itemvalue> <itemvalue>partner.city</itemvalue><br />
|
||||
<br />
|
||||
|
||||
<strong>BPID:</strong> <itemvalue>partner.id</itemvalue><br />
|
||||
<strong>Kreditoren-Nr.:</strong> <itemvalue>cdtr.number</itemvalue><br />
|
||||
<strong>Debitoren-Nr.:</strong> <itemvalue>dbtr.number</itemvalue><br />
|
||||
]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="keyupdateacl" type="xs:string">
|
||||
<imixs:value><![CDATA[true]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="keyownershipfields" type="xs:string">
|
||||
<imixs:value><![CDATA[process.team]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="namownershipnames" type="xs:string">
|
||||
<imixs:value/>
|
||||
</imixs:item>
|
||||
<imixs:item name="namaddreadaccess" type="xs:string">
|
||||
<imixs:value/>
|
||||
</imixs:item>
|
||||
<imixs:item name="namaddwriteaccess" type="xs:string">
|
||||
<imixs:value/>
|
||||
</imixs:item>
|
||||
<imixs:item name="keyaddwritefields" type="xs:string">
|
||||
<imixs:value><![CDATA[process.team]]></imixs:value>
|
||||
</imixs:item>
|
||||
</bpmn2:extensionElements>
|
||||
<bpmn2:documentation id="documentation_1TuLXg"/>
|
||||
<bpmn2:outgoing>sequenceFlow_NNbMhg</bpmn2:outgoing>
|
||||
<bpmn2:incoming>sequenceFlow_avahXg</bpmn2:incoming>
|
||||
<bpmn2:outgoing>sequenceFlow_pvIENQ</bpmn2:outgoing>
|
||||
<bpmn2:incoming>sequenceFlow_BWOtzA</bpmn2:incoming>
|
||||
<bpmn2:incoming>sequenceFlow_ofWWuw</bpmn2:incoming>
|
||||
<bpmn2:outgoing>sequenceFlow_b0Bj3Q</bpmn2:outgoing>
|
||||
<bpmn2:incoming>sequenceFlow_BHsRkQ</bpmn2:incoming>
|
||||
</bpmn2:task>
|
||||
<bpmn2:intermediateCatchEvent id="event_1OVorA" imixs:activityid="300" name="[iban/bic error]">
|
||||
<bpmn2:extensionElements>
|
||||
<imixs:item name="keypublicresult" type="xs:string">
|
||||
<imixs:value><![CDATA[0]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="rtfresultlog" type="xs:string">
|
||||
<imixs:value><![CDATA[IBAN/BIC fraud detection]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="txtactivityresult" type="xs:string">
|
||||
<imixs:value><![CDATA[<item name="process">Partnermanagement</item>]]></imixs:value>
|
||||
</imixs:item>
|
||||
</bpmn2:extensionElements>
|
||||
<bpmn2:documentation id="documentation_J8iRLQ"/>
|
||||
<bpmn2:outgoing>sequenceFlow_BHsRkQ</bpmn2:outgoing>
|
||||
<bpmn2:incoming>sequenceFlow_5pT4Tw</bpmn2:incoming>
|
||||
</bpmn2:intermediateCatchEvent>
|
||||
<bpmn2:intermediateCatchEvent id="event_baWU4w" imixs:activityid="20" name="Update">
|
||||
<bpmn2:extensionElements>
|
||||
<imixs:item name="rtfresultlog" type="xs:string">
|
||||
<imixs:value><![CDATA[Data updated]]></imixs:value>
|
||||
</imixs:item>
|
||||
<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[<item name="action">home</item>]]></imixs:value>
|
||||
</imixs:item>
|
||||
</bpmn2:extensionElements>
|
||||
<bpmn2:documentation id="documentation_q730KA"/>
|
||||
<bpmn2:incoming>sequenceFlow_pvIENQ</bpmn2:incoming>
|
||||
<bpmn2:outgoing>sequenceFlow_b4BcVA</bpmn2:outgoing>
|
||||
</bpmn2:intermediateCatchEvent>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_pvIENQ" sourceRef="task_delO7Q" targetRef="event_baWU4w">
|
||||
<bpmn2:documentation id="documentation_BYJ08A"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_b4BcVA" sourceRef="event_baWU4w" targetRef="task_Q80A1Q">
|
||||
<bpmn2:documentation id="documentation_5wWdHA"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:intermediateCatchEvent id="event_ofjVfg" imixs:activityid="200" name="[update]">
|
||||
<bpmn2:extensionElements>
|
||||
<imixs:item name="keypublicresult" type="xs:string">
|
||||
<imixs:value><![CDATA[0]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="rtfresultlog" type="xs:string">
|
||||
<imixs:value><![CDATA[Business Partner aus Cargosoft aktualisiert]]></imixs:value>
|
||||
</imixs:item>
|
||||
</bpmn2:extensionElements>
|
||||
<bpmn2:documentation id="documentation_A42bcQ"/>
|
||||
<bpmn2:outgoing>sequenceFlow_BWOtzA</bpmn2:outgoing>
|
||||
</bpmn2:intermediateCatchEvent>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_BWOtzA" sourceRef="event_ofjVfg" targetRef="task_delO7Q">
|
||||
<bpmn2:documentation id="documentation_QW3slw"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:intermediateCatchEvent id="event_i7yQVw" imixs:activityid="10" name="Save">
|
||||
<bpmn2:extensionElements>
|
||||
<imixs:item name="txtactivityresult" type="xs:string">
|
||||
<imixs:value><![CDATA[<item name="process">Partnermanagement</item>]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="keypublicresult" type="xs:string">
|
||||
<imixs:value><![CDATA[1]]></imixs:value>
|
||||
</imixs:item>
|
||||
</bpmn2:extensionElements>
|
||||
<bpmn2:documentation id="documentation_Il4Shw"/>
|
||||
<bpmn2:outgoing>sequenceFlow_ofWWuw</bpmn2:outgoing>
|
||||
</bpmn2:intermediateCatchEvent>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_ofWWuw" sourceRef="event_i7yQVw" targetRef="task_delO7Q">
|
||||
<bpmn2:documentation id="documentation_fXWIBw"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_b0Bj3Q" sourceRef="task_delO7Q" targetRef="event_Tr0aug">
|
||||
<bpmn2:documentation id="documentation_pVJeTQ"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_T5L9aQ" sourceRef="task_Q80A1Q" targetRef="event_VM0fEQ">
|
||||
<bpmn2:documentation id="documentation_0GJKgQ"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:exclusiveGateway gatewayDirection="Diverging" id="gateway_uK0c6g" name="">
|
||||
<bpmn2:documentation id="documentation_V9jDYA"/>
|
||||
<bpmn2:incoming>sequenceFlow_S9LVXg</bpmn2:incoming>
|
||||
<bpmn2:outgoing>sequenceFlow_jAU3VA</bpmn2:outgoing>
|
||||
<bpmn2:outgoing>sequenceFlow_8BTEtw</bpmn2:outgoing>
|
||||
</bpmn2:exclusiveGateway>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_jAU3VA" sourceRef="gateway_uK0c6g" targetRef="event_aSdkwg">
|
||||
<bpmn2:documentation id="documentation_ZNk0Bw"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_BHsRkQ" sourceRef="event_1OVorA" targetRef="task_delO7Q">
|
||||
<bpmn2:documentation id="documentation_SvoFWw"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:association id="association_WgLbbg" sourceRef="dataObject_UQIXAQ" targetRef="task_delO7Q">
|
||||
<bpmn2:documentation id="documentation_YhKaqg"/>
|
||||
</bpmn2:association>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_8O1EtQ" sourceRef="event_aSdkwg" targetRef="task_Q80A1Q">
|
||||
<bpmn2:documentation id="documentation_VTvROw"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:intermediateCatchEvent id="event_6250fg" imixs:activityid="10" name="Save">
|
||||
<bpmn2:documentation id="documentation_fcuXRg"/>
|
||||
<bpmn2:outgoing>sequenceFlow_kS2fMA</bpmn2:outgoing>
|
||||
</bpmn2:intermediateCatchEvent>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_kS2fMA" sourceRef="event_6250fg" targetRef="task_B8Pi7A">
|
||||
<bpmn2:documentation id="documentation_t0kr1g"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:intermediateCatchEvent id="event_VM0fEQ" imixs:activityid="900" name="[Stats]">
|
||||
<bpmn2:extensionElements>
|
||||
<imixs:item name="txtactivityresult" type="xs:string">
|
||||
<imixs:value><![CDATA[<item name="process">Partnermanagement</item>]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="keypublicresult" type="xs:string">
|
||||
<imixs:value><![CDATA[0]]></imixs:value>
|
||||
</imixs:item>
|
||||
</bpmn2:extensionElements>
|
||||
<bpmn2:documentation id="documentation_ZmJD9A"/>
|
||||
<bpmn2:incoming>sequenceFlow_T5L9aQ</bpmn2:incoming>
|
||||
<bpmn2:outgoing>sequenceFlow_MPlPww</bpmn2:outgoing>
|
||||
<bpmn2:timerEventDefinition id="timerEventDefinition_faqzJg"/>
|
||||
<bpmn2:incoming>sequenceFlow_rNZ3tw</bpmn2:incoming>
|
||||
</bpmn2:intermediateCatchEvent>
|
||||
<bpmn2:exclusiveGateway default="sequenceFlow_aH0vCQ" gatewayDirection="Diverging" id="gateway_s0J53g" name="">
|
||||
<bpmn2:documentation id="documentation_2Qn8Ng"/>
|
||||
<bpmn2:incoming>sequenceFlow_MPlPww</bpmn2:incoming>
|
||||
<bpmn2:outgoing>sequenceFlow_aH0vCQ</bpmn2:outgoing>
|
||||
<bpmn2:outgoing>sequenceFlow_ShuXXA</bpmn2:outgoing>
|
||||
</bpmn2:exclusiveGateway>
|
||||
<bpmn2:task id="task_NohgrQ" imixs:processid="1700" name="Inactiv">
|
||||
<bpmn2:extensionElements>
|
||||
<imixs:item name="txtimageurl" type="xs:string">
|
||||
<imixs:value><![CDATA[typcn-user|typcn-tick,imixs-success]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="txtworkflowsummary" type="xs:string">
|
||||
<imixs:value><![CDATA[<itemvalue>partner.name</itemvalue> (<itemvalue>partner.id</itemvalue>)]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="txtworkflowabstract" type="xs:string">
|
||||
<imixs:value><![CDATA[<strong>Adresse:</strong><br />
|
||||
<itemvalue>partner.name</itemvalue><br />
|
||||
<itemvalue>partner.address</itemvalue><br />
|
||||
<itemvalue>partner.zip</itemvalue> <itemvalue>partner.city</itemvalue><br />
|
||||
<br />
|
||||
|
||||
<strong>BPID:</strong> <itemvalue>partner.id</itemvalue><br />
|
||||
<strong>Kreditoren-Nr.:</strong> <itemvalue>cdtr.number</itemvalue><br />
|
||||
<strong>Debitoren-Nr.:</strong> <itemvalue>dbtr.number</itemvalue><br />
|
||||
]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="keyupdateacl" type="xs:string">
|
||||
<imixs:value><![CDATA[true]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="namownershipnames" type="xs:string">
|
||||
<imixs:value/>
|
||||
</imixs:item>
|
||||
<imixs:item name="namaddreadaccess" type="xs:string">
|
||||
<imixs:value/>
|
||||
</imixs:item>
|
||||
<imixs:item name="namaddwriteaccess" type="xs:string">
|
||||
<imixs:value/>
|
||||
</imixs:item>
|
||||
<imixs:item name="keyaddwritefields" type="xs:string">
|
||||
<imixs:value><![CDATA[process.team]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="txttype" type="xs:string">
|
||||
<imixs:value><![CDATA[workitemarchive]]></imixs:value>
|
||||
</imixs:item>
|
||||
</bpmn2:extensionElements>
|
||||
<bpmn2:documentation id="documentation_RI2Ing"/>
|
||||
<bpmn2:incoming>sequenceFlow_9atLFA</bpmn2:incoming>
|
||||
<bpmn2:incoming>sequenceFlow_8O1EtQ</bpmn2:incoming>
|
||||
<bpmn2:incoming>sequenceFlow_aH0vCQ</bpmn2:incoming>
|
||||
<bpmn2:outgoing>sequenceFlow_neSsFQ</bpmn2:outgoing>
|
||||
<bpmn2:outgoing>sequenceFlow_zd6RWA</bpmn2:outgoing>
|
||||
<bpmn2:outgoing>sequenceFlow_rNZ3tw</bpmn2:outgoing>
|
||||
</bpmn2:task>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_MPlPww" sourceRef="event_VM0fEQ" targetRef="gateway_s0J53g">
|
||||
<bpmn2:documentation id="documentation_saZUSw"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_aH0vCQ" sourceRef="gateway_s0J53g" targetRef="task_NohgrQ">
|
||||
<bpmn2:documentation id="documentation_0fUXrw"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_ShuXXA" sourceRef="gateway_s0J53g" targetRef="task_Q80A1Q">
|
||||
<bpmn2:documentation id="documentation_mhSkeg"/>
|
||||
<bpmn2:conditionExpression id="formalExpression_oEZj1g" xsi:type="bpmn2:tFormalExpression"><![CDATA[true;]]></bpmn2:conditionExpression>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_neSsFQ" sourceRef="task_NohgrQ" targetRef="event_Tr0aug">
|
||||
<bpmn2:documentation id="documentation_X3IiUg"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:intermediateCatchEvent id="event_AMs05g" name="[IBAN-ERROR]">
|
||||
<bpmn2:documentation id="documentation_CKfjew"/>
|
||||
<bpmn2:linkEventDefinition id="linkEventDefinition_QoDe7w"/>
|
||||
<bpmn2:outgoing>sequenceFlow_5pT4Tw</bpmn2:outgoing>
|
||||
</bpmn2:intermediateCatchEvent>
|
||||
<bpmn2:intermediateThrowEvent id="event_feLZFQ" name="[IBAN-ERROR]">
|
||||
<bpmn2:documentation id="documentation_ns8CKQ"/>
|
||||
<bpmn2:linkEventDefinition id="linkEventDefinition_ici5wA"/>
|
||||
<bpmn2:incoming>sequenceFlow_8BTEtw</bpmn2:incoming>
|
||||
</bpmn2:intermediateThrowEvent>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_5pT4Tw" sourceRef="event_AMs05g" targetRef="event_1OVorA">
|
||||
<bpmn2:documentation id="documentation_mQfndA"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_8BTEtw" sourceRef="gateway_uK0c6g" targetRef="event_feLZFQ">
|
||||
<bpmn2:documentation id="documentation_aB5qHw"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:intermediateThrowEvent id="event_GTXFMw" name="[IBAN-ERROR]">
|
||||
<bpmn2:documentation id="documentation_a8NiKA"/>
|
||||
<bpmn2:linkEventDefinition id="linkEventDefinition_YUfA0A"/>
|
||||
<bpmn2:incoming>sequenceFlow_03b60w</bpmn2:incoming>
|
||||
</bpmn2:intermediateThrowEvent>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_03b60w" sourceRef="task_Q80A1Q" targetRef="event_GTXFMw">
|
||||
<bpmn2:documentation id="documentation_i02Wsw"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:intermediateThrowEvent id="event_u8OBFg" name="[IBAN-ERROR]">
|
||||
<bpmn2:documentation id="documentation_0rGDzg"/>
|
||||
<bpmn2:linkEventDefinition id="linkEventDefinition_8g6AoA"/>
|
||||
<bpmn2:incoming>sequenceFlow_zd6RWA</bpmn2:incoming>
|
||||
</bpmn2:intermediateThrowEvent>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_zd6RWA" sourceRef="task_NohgrQ" targetRef="event_u8OBFg">
|
||||
<bpmn2:documentation id="documentation_ez15rg"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_rNZ3tw" sourceRef="task_NohgrQ" targetRef="event_VM0fEQ">
|
||||
<bpmn2:documentation id="documentation_AHyEzA"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
</bpmn2:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1" name="OpenBPMN Diagram">
|
||||
<bpmndi:BPMNPlane bpmnElement="collaboration_1" id="BPMNPlane_1">
|
||||
<bpmndi:BPMNShape bpmnElement="event_WmkBvw" id="BPMNShape_heefeQ">
|
||||
<dc:Bounds height="36.0" width="36.0" x="57.0" y="277.0"/>
|
||||
<bpmndi:BPMNLabel id="BPMNLabel_6qfN6w">
|
||||
<dc:Bounds height="20.0" width="100.0" x="25.0" y="316.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape bpmnElement="event_1clvlg" id="BPMNShape_85k4rg">
|
||||
<dc:Bounds height="36.0" width="36.0" x="1657.0" y="277.0"/>
|
||||
<bpmndi:BPMNLabel id="BPMNLabel_F7t2Ng">
|
||||
<dc:Bounds height="20.0" width="100.0" x="1625.0" y="259.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape bpmnElement="task_ToMZaQ" id="BPMNShape_CUpR6w">
|
||||
<dc:Bounds height="50.0" width="110.0" x="170.0" y="270.0"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape bpmnElement="participant_oxb3dg" id="BPMNShape_mqlDUA">
|
||||
<dc:Bounds height="820.0" width="1820.0" x="-30.0" y="40.0"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape bpmnElement="lane_Zxa2TA" id="BPMNShape_Lane_1cDN5A">
|
||||
<dc:Bounds height="820.0" width="1790.0" x="0.0" y="40.0"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_25otUg" id="BPMNEdge_NHnc3g" sourceElement="BPMNShape_heefeQ" targetElement="BPMNShape_CUpR6w">
|
||||
<di:waypoint x="93.0" y="295.0"/>
|
||||
<di:waypoint x="170.0" y="295.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape bpmnElement="task_Q80A1Q" id="BPMNShape_7JA0nw">
|
||||
<dc:Bounds height="50.0" width="110.0" x="660.0" y="270.0"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape bpmnElement="task_B8Pi7A" id="BPMNShape_ghujeA">
|
||||
<dc:Bounds height="50.0" width="110.0" x="1360.0" y="270.0"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape bpmnElement="event_aSdkwg" id="BPMNShape_uesqgg">
|
||||
<dc:Bounds height="36.0" width="36.0" x="437.0" y="277.0"/>
|
||||
<bpmndi:BPMNLabel id="BPMNLabel_AxKRKQ">
|
||||
<dc:Bounds height="20.0" width="100.0" x="405.0" y="316.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_S9LVXg" id="BPMNEdge_xoCSiA" sourceElement="BPMNShape_CUpR6w" targetElement="BPMNShape_6IZk7Q">
|
||||
<di:waypoint x="280.0" y="295.0"/>
|
||||
<di:waypoint x="340.0" y="295.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape bpmnElement="event_Tr0aug" id="BPMNShape_NEaJjA">
|
||||
<dc:Bounds height="36.0" width="36.0" x="1227.0" y="277.0"/>
|
||||
<bpmndi:BPMNLabel id="BPMNLabel_GkrCTA">
|
||||
<dc:Bounds height="20.0" width="100.0" x="1195.0" y="316.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_1mXO8A" id="BPMNEdge_7CorJA" sourceElement="BPMNShape_NEaJjA" targetElement="BPMNShape_ghujeA">
|
||||
<di:waypoint x="1263.0" y="295.0"/>
|
||||
<di:waypoint x="1360.0" y="295.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_rwdWxw" id="BPMNEdge_t88dUw" sourceElement="BPMNShape_ghujeA" targetElement="BPMNShape_85k4rg">
|
||||
<di:waypoint x="1470.0" y="295.0"/>
|
||||
<di:waypoint x="1657.0" y="295.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape bpmnElement="dataObject_UQIXAQ" id="BPMNShape_Mnop9Q">
|
||||
<dc:Bounds height="50.0" width="35.0" x="560.0" y="380.0"/>
|
||||
<bpmndi:BPMNLabel id="BPMNLabel_h0UuFw">
|
||||
<dc:Bounds height="20.0" width="100.0" x="527.5" y="435.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape bpmnElement="event_vwH0cA" id="BPMNShape_OXGg8Q">
|
||||
<dc:Bounds height="36.0" width="36.0" x="747.0" y="177.0"/>
|
||||
<bpmndi:BPMNLabel id="BPMNLabel_KaHfJQ">
|
||||
<dc:Bounds height="20.0" width="100.0" x="715.0" y="216.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_avahXg" id="BPMNEdge_odMtvQ" sourceElement="BPMNShape_OXGg8Q" targetElement="BPMNShape_7JA0nw">
|
||||
<di:waypoint x="747.0" y="196.0"/>
|
||||
<di:waypoint x="725.0" y="196.0"/>
|
||||
<di:waypoint x="725.0" y="270.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge bpmnElement="association_uvV8uA" id="BPMNEdge_3Mtxfw" sourceElement="BPMNShape_mqlDUA" targetElement="BPMNShape_7JA0nw"/>
|
||||
<bpmndi:BPMNEdge bpmnElement="association_mpLQEQ" id="BPMNEdge_qkEiPA" sourceElement="BPMNShape_Mnop9Q" targetElement="BPMNShape_7JA0nw">
|
||||
<di:waypoint x="588.0" y="380.0"/>
|
||||
<di:waypoint x="588.0" y="357.0"/>
|
||||
<di:waypoint x="680.0" y="357.0"/>
|
||||
<di:waypoint x="680.0" y="320.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape bpmnElement="textAnnotation_LyGTCw" id="BPMNShape_SbxnGw">
|
||||
<dc:Bounds height="85.0" width="228.0" x="80.0" y="80.0"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge bpmnElement="association_83sZPw" id="BPMNEdge_p0XWxg" sourceElement="BPMNShape_mqlDUA" targetElement="BPMNShape_SbxnGw"/>
|
||||
<bpmndi:BPMNShape bpmnElement="event_TuyuwQ" id="BPMNShape_fTy7mg">
|
||||
<dc:Bounds height="36.0" width="36.0" x="617.0" y="177.0"/>
|
||||
<bpmndi:BPMNLabel id="BPMNLabel_033n1A">
|
||||
<dc:Bounds height="20.0" width="100.0" x="585.0" y="216.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_9atLFA" id="BPMNEdge_BC4pwQ" sourceElement="BPMNShape_fTy7mg" targetElement="BPMNShape_7JA0nw">
|
||||
<di:waypoint x="653.0" y="196.0"/>
|
||||
<di:waypoint x="700.0" y="196.0"/>
|
||||
<di:waypoint x="700.0" y="270.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape bpmnElement="event_h0Kk5g" id="BPMNShape_T4k09g">
|
||||
<dc:Bounds height="36.0" width="36.0" x="1327.0" y="177.0"/>
|
||||
<bpmndi:BPMNLabel id="BPMNLabel_lX0KlA">
|
||||
<dc:Bounds height="20.0" width="100.0" x="1295.0" y="216.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_gC0I9w" id="BPMNEdge_0nCVvg" sourceElement="BPMNShape_T4k09g" targetElement="BPMNShape_ghujeA">
|
||||
<di:waypoint x="1360.0" y="185.0"/>
|
||||
<di:waypoint x="1390.0" y="185.0"/>
|
||||
<di:waypoint x="1390.0" y="270.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape bpmnElement="task_delO7Q" id="BPMNShape_rEOMWA">
|
||||
<dc:Bounds height="50.0" width="110.0" x="660.0" y="510.0"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape bpmnElement="event_1OVorA" id="BPMNShape_mG4csA">
|
||||
<dc:Bounds height="36.0" width="36.0" x="557.0" y="517.0"/>
|
||||
<bpmndi:BPMNLabel id="BPMNLabel_djIb8w">
|
||||
<dc:Bounds height="20.0" width="100.0" x="528.0" y="559.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape bpmnElement="event_baWU4w" id="BPMNShape_755HGg">
|
||||
<dc:Bounds height="36.0" width="36.0" x="697.0" y="407.0"/>
|
||||
<bpmndi:BPMNLabel id="BPMNLabel_oN6XKQ">
|
||||
<dc:Bounds height="20.0" width="100.0" x="665.0" y="446.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_pvIENQ" id="BPMNEdge_wXz5yA" sourceElement="BPMNShape_rEOMWA" targetElement="BPMNShape_755HGg">
|
||||
<di:waypoint x="715.0" y="510.0"/>
|
||||
<di:waypoint x="715.0" y="443.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_b4BcVA" id="BPMNEdge_1gQ5Nw" sourceElement="BPMNShape_755HGg" targetElement="BPMNShape_7JA0nw">
|
||||
<di:waypoint x="715.0" y="407.0"/>
|
||||
<di:waypoint x="715.0" y="320.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape bpmnElement="event_ofjVfg" id="BPMNShape_hDVKtg">
|
||||
<dc:Bounds height="36.0" width="36.0" x="617.0" y="607.0"/>
|
||||
<bpmndi:BPMNLabel id="BPMNLabel_HpXNFQ">
|
||||
<dc:Bounds height="20.0" width="100.0" x="585.0" y="646.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_BWOtzA" id="BPMNEdge_KW5PhQ" sourceElement="BPMNShape_hDVKtg" targetElement="BPMNShape_rEOMWA">
|
||||
<di:waypoint x="651.0" y="616.0"/>
|
||||
<di:waypoint x="685.0" y="616.0"/>
|
||||
<di:waypoint x="685.0" y="560.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape bpmnElement="event_i7yQVw" id="BPMNShape_KxnRjA">
|
||||
<dc:Bounds height="36.0" width="36.0" x="747.0" y="607.0"/>
|
||||
<bpmndi:BPMNLabel id="BPMNLabel_sJqwmA">
|
||||
<dc:Bounds height="20.0" width="100.0" x="715.0" y="646.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_ofWWuw" id="BPMNEdge_zlNOzQ" sourceElement="BPMNShape_KxnRjA" targetElement="BPMNShape_rEOMWA">
|
||||
<di:waypoint x="749.0" y="616.0"/>
|
||||
<di:waypoint x="735.0" y="616.0"/>
|
||||
<di:waypoint x="735.0" y="560.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_b0Bj3Q" id="BPMNEdge_CDn41A" sourceElement="BPMNShape_rEOMWA" targetElement="BPMNShape_NEaJjA">
|
||||
<di:waypoint x="770.0" y="540.0"/>
|
||||
<di:waypoint x="1240.0" y="540.0"/>
|
||||
<di:waypoint x="1240.0" y="312.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_T5L9aQ" id="BPMNEdge_dAQH1Q" sourceElement="BPMNShape_7JA0nw" targetElement="BPMNShape_FKXJtw">
|
||||
<di:waypoint x="770.0" y="295.0"/>
|
||||
<di:waypoint x="837.0" y="295.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape bpmnElement="gateway_uK0c6g" id="BPMNShape_6IZk7Q">
|
||||
<dc:Bounds height="50.0" width="50.0" x="340.0" y="270.0"/>
|
||||
<bpmndi:BPMNLabel id="BPMNLabel_JvBA0w">
|
||||
<dc:Bounds height="20.0" width="100.0" x="315.0" y="323.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_jAU3VA" id="BPMNEdge_2qLI2g" sourceElement="BPMNShape_6IZk7Q" targetElement="BPMNShape_uesqgg">
|
||||
<di:waypoint x="390.0" y="295.0"/>
|
||||
<di:waypoint x="437.0" y="295.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_BHsRkQ" id="BPMNEdge_1LX47g" sourceElement="BPMNShape_mG4csA" targetElement="BPMNShape_rEOMWA">
|
||||
<di:waypoint x="593.0" y="535.0"/>
|
||||
<di:waypoint x="660.0" y="535.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge bpmnElement="association_WgLbbg" id="BPMNEdge_Btz9CA" sourceElement="BPMNShape_Mnop9Q" targetElement="BPMNShape_rEOMWA">
|
||||
<di:waypoint x="575.0" y="430.0"/>
|
||||
<di:waypoint x="575.0" y="471.0"/>
|
||||
<di:waypoint x="675.0" y="471.0"/>
|
||||
<di:waypoint x="675.0" y="510.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_8O1EtQ" id="BPMNEdge_JxSLfA" sourceElement="BPMNShape_uesqgg" targetElement="BPMNShape_7JA0nw">
|
||||
<di:waypoint x="473.0" y="295.0"/>
|
||||
<di:waypoint x="660.0" y="295.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape bpmnElement="event_6250fg" id="BPMNShape_v5Xzvg">
|
||||
<dc:Bounds height="36.0" width="36.0" x="1457.0" y="177.0"/>
|
||||
<bpmndi:BPMNLabel id="BPMNLabel_IBnUDA">
|
||||
<dc:Bounds height="20.0" width="100.0" x="1425.0" y="216.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_kS2fMA" id="BPMNEdge_i80XvQ" sourceElement="BPMNShape_v5Xzvg" targetElement="BPMNShape_ghujeA">
|
||||
<di:waypoint x="1458.0" y="190.0"/>
|
||||
<di:waypoint x="1435.0" y="190.0"/>
|
||||
<di:waypoint x="1435.0" y="270.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape bpmnElement="event_VM0fEQ" id="BPMNShape_FKXJtw">
|
||||
<dc:Bounds height="36.0" width="36.0" x="837.0" y="277.0"/>
|
||||
<bpmndi:BPMNLabel id="BPMNLabel_CAiT2Q">
|
||||
<dc:Bounds height="20.0" width="100.0" x="805.0" y="316.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape bpmnElement="gateway_s0J53g" id="BPMNShape_gklxXQ">
|
||||
<dc:Bounds height="50.0" width="50.0" x="930.0" y="270.0"/>
|
||||
<bpmndi:BPMNLabel id="BPMNLabel_I4jC0g">
|
||||
<dc:Bounds height="20.0" width="100.0" x="905.0" y="323.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape bpmnElement="task_NohgrQ" id="BPMNShape_VljsIw">
|
||||
<dc:Bounds height="50.0" width="110.0" x="1060.0" y="270.0"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_MPlPww" id="BPMNEdge_rt2H3g" sourceElement="BPMNShape_FKXJtw" targetElement="BPMNShape_gklxXQ">
|
||||
<di:waypoint x="873.0" y="295.0"/>
|
||||
<di:waypoint x="930.0" y="295.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_aH0vCQ" id="BPMNEdge_YMnxQQ" sourceElement="BPMNShape_gklxXQ" targetElement="BPMNShape_VljsIw">
|
||||
<di:waypoint x="980.0" y="295.0"/>
|
||||
<di:waypoint x="1060.0" y="295.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_ShuXXA" id="BPMNEdge_DY4O8Q" sourceElement="BPMNShape_gklxXQ" targetElement="BPMNShape_7JA0nw">
|
||||
<di:waypoint x="956.0" y="319.0"/>
|
||||
<di:waypoint x="956.0" y="364.0"/>
|
||||
<di:waypoint x="746.0" y="364.0"/>
|
||||
<di:waypoint x="746.0" y="320.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_neSsFQ" id="BPMNEdge_b7C0wQ" sourceElement="BPMNShape_VljsIw" targetElement="BPMNShape_NEaJjA">
|
||||
<di:waypoint x="1170.0" y="295.0"/>
|
||||
<di:waypoint x="1227.0" y="295.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape bpmnElement="event_AMs05g" id="BPMNShape_rS0hsw">
|
||||
<dc:Bounds height="36.0" width="36.0" x="477.0" y="517.0"/>
|
||||
<bpmndi:BPMNLabel id="BPMNLabel_XcN2Og">
|
||||
<dc:Bounds height="20.0" width="100.0" x="445.0" y="556.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape bpmnElement="event_feLZFQ" id="BPMNShape_uklBfA">
|
||||
<dc:Bounds height="36.0" width="36.0" x="347.0" y="367.0"/>
|
||||
<bpmndi:BPMNLabel id="BPMNLabel_OengAQ">
|
||||
<dc:Bounds height="20.0" width="100.0" x="315.0" y="406.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_5pT4Tw" id="BPMNEdge_LZMXxg" sourceElement="BPMNShape_rS0hsw" targetElement="BPMNShape_mG4csA">
|
||||
<di:waypoint x="513.0" y="535.0"/>
|
||||
<di:waypoint x="557.0" y="535.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_8BTEtw" id="BPMNEdge_Os7FVw" sourceElement="BPMNShape_6IZk7Q" targetElement="BPMNShape_uklBfA">
|
||||
<di:waypoint x="365.0" y="320.0"/>
|
||||
<di:waypoint x="365.0" y="367.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape bpmnElement="event_GTXFMw" id="BPMNShape_5I6iNA">
|
||||
<dc:Bounds height="36.0" width="36.0" x="847.0" y="177.0"/>
|
||||
<bpmndi:BPMNLabel id="BPMNLabel_80daBg">
|
||||
<dc:Bounds height="20.0" width="100.0" x="815.0" y="216.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_03b60w" id="BPMNEdge_ehQBOQ" sourceElement="BPMNShape_7JA0nw" targetElement="BPMNShape_5I6iNA">
|
||||
<di:waypoint x="770.0" y="294.0"/>
|
||||
<di:waypoint x="809.0" y="294.0"/>
|
||||
<di:waypoint x="809.0" y="195.0"/>
|
||||
<di:waypoint x="847.0" y="195.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape bpmnElement="event_u8OBFg" id="BPMNShape_Kp730Q">
|
||||
<dc:Bounds height="36.0" width="36.0" x="1137.0" y="177.0"/>
|
||||
<bpmndi:BPMNLabel id="BPMNLabel_iUQ02A">
|
||||
<dc:Bounds height="20.0" width="100.0" x="1105.0" y="216.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_zd6RWA" id="BPMNEdge_6qKC4A" sourceElement="BPMNShape_VljsIw" targetElement="BPMNShape_Kp730Q">
|
||||
<di:waypoint x="1114.0" y="270.0"/>
|
||||
<di:waypoint x="1114.0" y="195.0"/>
|
||||
<di:waypoint x="1137.0" y="195.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_rNZ3tw" id="BPMNEdge_zrajzA" sourceElement="BPMNShape_VljsIw" targetElement="BPMNShape_FKXJtw">
|
||||
<di:waypoint x="1080.0" y="270.0"/>
|
||||
<di:waypoint x="1080.0" y="237.0"/>
|
||||
<di:waypoint x="855.0" y="237.0"/>
|
||||
<di:waypoint x="855.0" y="277.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn2:definitions>
|
||||
Loading…
Reference in a new issue