Migration office

This commit is contained in:
Ralph Soika 2026-08-15 14:19:38 +02:00
parent e14af8449f
commit 01e0e89e14
29 changed files with 125 additions and 852 deletions

2
devi
View file

@ -53,9 +53,7 @@ fi
if [[ "$(strip_dash $1)" == "hot" ]]; then if [[ "$(strip_dash $1)" == "hot" ]]; then
echo "* Hotdeploy..." echo "* Hotdeploy..."
cd *-app
mvn manik-hotdeploy:hotdeploy mvn manik-hotdeploy:hotdeploy
cd ..
fi fi
if [[ "$(strip_dash $1)" == "deploy" ]]; then if [[ "$(strip_dash $1)" == "deploy" ]]; then

60
pom.xml
View file

@ -60,13 +60,12 @@
<!-- Dependency Versions --> <!-- Dependency Versions -->
<jakarta.version>10.0.0</jakarta.version> <jakarta.version>10.0.0</jakarta.version>
<microprofile.version>6.0</microprofile.version> <microprofile.version>6.0</microprofile.version>
<org.imixs.office.version>5.1.9-SNAPSHOT</org.imixs.office.version>
<org.imixs.workflow.version>6.2.8-SNAPSHOT</org.imixs.workflow.version> <org.imixs.workflow.version>6.2.8-SNAPSHOT</org.imixs.workflow.version>
<org.imixs.office.version>5.1.8</org.imixs.office.version>
<org.imixs.marty.version>5.2.0</org.imixs.marty.version> <org.imixs.marty.version>5.2.0</org.imixs.marty.version>
<org.imixs.adapter.version>3.2.3</org.imixs.adapter.version>
<org.imixs.data.version>1.0.3</org.imixs.data.version>
<org.imixs.e-invoice.version>1.0.3-SNAPSHOT</org.imixs.e-invoice.version> <org.imixs.e-invoice.version>1.0.3-SNAPSHOT</org.imixs.e-invoice.version>
<org.imixs.adapter.version>3.2.4-SNAPSHOT</org.imixs.adapter.version>
<org.imixs.data.version>1.0.4-SNAPSHOT</org.imixs.data.version>
<apache.poi.version>4.0.0</apache.poi.version> <apache.poi.version>4.0.0</apache.poi.version>
<net.sf.saxon.version>9.9.1-4</net.sf.saxon.version> <net.sf.saxon.version>9.9.1-4</net.sf.saxon.version>
@ -305,11 +304,12 @@
<groupId>org.imixs.workflow</groupId> <groupId>org.imixs.workflow</groupId>
<artifactId>imixs-office-workflow-app</artifactId> <artifactId>imixs-office-workflow-app</artifactId>
<excludes> <excludes>
<exclude>WEB-INF/lib/*.jar</exclude> <exclude>WEB-INF/lib/imixs-data-*.jar</exclude>
</excludes> </excludes>
</overlay> </overlay>
</overlays> </overlays>
--> -->
</configuration> </configuration>
</plugin> </plugin>
@ -382,37 +382,6 @@
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- Imixs Workflow -->
<dependency>
<groupId>org.imixs.workflow</groupId>
<artifactId>imixs-workflow-core</artifactId>
<type>jar</type>
<version>${org.imixs.workflow.version}</version>
</dependency>
<dependency>
<groupId>org.imixs.workflow</groupId>
<artifactId>imixs-workflow-engine</artifactId>
<type>jar</type>
<version>${org.imixs.workflow.version}</version>
</dependency>
<dependency>
<groupId>org.imixs.workflow</groupId>
<artifactId>imixs-workflow-jax-rs</artifactId>
<type>jar</type>
<version>${org.imixs.workflow.version}</version>
</dependency>
<dependency>
<groupId>org.imixs.workflow</groupId>
<artifactId>imixs-workflow-faces</artifactId>
<type>jar</type>
<version>${org.imixs.workflow.version}</version>
</dependency>
<dependency>
<groupId>org.imixs.workflow</groupId>
<artifactId>imixs-workflow-index-lucene</artifactId>
<version>${org.imixs.workflow.version}</version>
</dependency>
<!-- Imixs-Office-Workflow Overlay --> <!-- Imixs-Office-Workflow Overlay -->
<dependency> <dependency>
<groupId>org.imixs.workflow</groupId> <groupId>org.imixs.workflow</groupId>
@ -427,27 +396,11 @@
</dependency> </dependency>
<!-- Imixs Data --> <!-- Imixs Data -->
<dependency>
<groupId>org.imixs.workflow</groupId>
<artifactId>imixs-data-groups</artifactId>
<version>${org.imixs.data.version}</version>
</dependency>
<dependency>
<groupId>org.imixs.workflow</groupId>
<artifactId>imixs-data-views</artifactId>
<version>${org.imixs.data.version}</version>
</dependency>
<dependency>
<groupId>org.imixs.workflow</groupId>
<artifactId>imixs-data-documents</artifactId>
<version>${org.imixs.data.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.imixs.workflow</groupId> <groupId>org.imixs.workflow</groupId>
<artifactId>imixs-data-importer</artifactId> <artifactId>imixs-data-importer</artifactId>
<version>${org.imixs.data.version}</version> <version>${org.imixs.data.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.pdfbox</groupId> <groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId> <artifactId>pdfbox</artifactId>
@ -467,12 +420,13 @@
<artifactId>imixs-adapters-datev</artifactId> <artifactId>imixs-adapters-datev</artifactId>
<version>${org.imixs.adapter.version}</version> <version>${org.imixs.adapter.version}</version>
</dependency> </dependency>
<!-- POI Adapter --> <!-- POI Adapter
<dependency> <dependency>
<groupId>org.imixs.workflow</groupId> <groupId>org.imixs.workflow</groupId>
<artifactId>imixs-adapters-poi</artifactId> <artifactId>imixs-adapters-poi</artifactId>
<version>${org.imixs.adapter.version}</version> <version>${org.imixs.adapter.version}</version>
</dependency> </dependency>
-->
<!-- QR Code --> <!-- QR Code -->
<dependency> <dependency>
<groupId>org.imixs.workflow</groupId> <groupId>org.imixs.workflow</groupId>

View file

@ -120,55 +120,57 @@ public class AGLAnalyticControllerDebitor implements Serializable {
link = link + "&processref=" + process.getUniqueID() + "&phrase=" + getDbtNr(); link = link + "&processref=" + process.getUniqueID() + "&phrase=" + getDbtNr();
} }
if ("analytic.invoices.count.all".equals(event.getKey())) { if ("analytic.invoices.count.all".equals(event.getKey())) {
event.setValue("" + countAll); event.getData().setItemValue("value", countAll);
event.setLabel(formatTotals(totalAllCurrency1, totalAllCurrency2)); event.getData().setItemValue("label", formatTotals(totalAllCurrency1, totalAllCurrency2));
event.setDescription("Total invoices"); event.getData().setItemValue("description", "Total invoices");
event.getWorkitem().setItemValue("invoices.total", event.getValue()); event.getWorkitem().setItemValue("invoices.total", countAll);
event.setLink(link); event.getWorkitem().setItemValue("link", link);
} }
if ("analytic.invoices.count.open".equals(event.getKey())) { if ("analytic.invoices.count.open".equals(event.getKey())) {
event.setValue("" + countOpen); event.getData().setItemValue("value", "" + countOpen);
event.setLabel(formatTotals(totalOpenCurrency1, totalOpenCurrency2)); event.getData().setItemValue("label", formatTotals(totalOpenCurrency1, totalOpenCurrency2));
event.setDescription("Total invoices not yet in due"); event.getData().setItemValue("description", "Total invoices not yet in due");
event.getWorkitem().setItemValue("invoices.total.open", event.getValue()); event.getWorkitem().setItemValue("invoices.total.open", countOpen);
event.setLink(link); event.getWorkitem().setItemValue("link", link);
} }
if ("analytic.invoices.count.due".equals(event.getKey())) { if ("analytic.invoices.count.due".equals(event.getKey())) {
event.setValue("" + countDue); event.getData().setItemValue("value", "" + countDue);
event.setLabel(formatTotals(totalDueCurrency1, totalDueCurrency2)); event.getData().setItemValue("label", formatTotals(totalDueCurrency1, totalDueCurrency2));
event.setDescription("Total invoices in due"); event.getData().setItemValue("description", "Total invoices in due");
event.getWorkitem().setItemValue("invoices.total.due", event.getValue()); event.getWorkitem().setItemValue("invoices.total.due", countDue);
event.setLink(link); event.getWorkitem().setItemValue("link", link);
} }
if ("analytic.invoices.count.dunning".equals(event.getKey())) { if ("analytic.invoices.count.dunning".equals(event.getKey())) {
event.setValue("" + countDunning); event.getData().setItemValue("value", "" + countDunning);
event.setLabel(formatTotals(totalDunningCurrency1, totalDunningCurrency2)); event.getData().setItemValue("label", formatTotals(totalDunningCurrency1, totalDunningCurrency2));
event.setDescription("Total invoices in dunning"); event.getData().setItemValue("description", "Total invoices in dunning");
event.getWorkitem().setItemValue("invoices.total.dunning", event.getValue()); event.getWorkitem().setItemValue("invoices.total.dunning", countDunning);
event.setLink(link); event.getWorkitem().setItemValue("link", link);
} }
if ("analytic.payment.avg.due".equals(event.getKey())) { if ("analytic.payment.avg.due".equals(event.getKey())) {
event.setValue("" + averagePaymentDue); event.getData().setItemValue("value", "" + averagePaymentDue);
event.setLabel("days"); event.getData().setItemValue("label", "days");
event.setDescription("Average terms of credit in the last 12 months."); event.getData().setItemValue("description", "Average terms of credit in the last 12 months.");
event.getWorkitem().setItemValue("payment.avg.due", event.getValue()); event.getWorkitem().setItemValue("payment.avg.due", averagePaymentDue);
event.setLink(link); event.getWorkitem().setItemValue("link", link);
} }
if ("analytic.payment.avg.days".equals(event.getKey())) { if ("analytic.payment.avg.days".equals(event.getKey())) {
event.setValue("" + averagePaymentDays); event.getData().setItemValue("value", "" + averagePaymentDays);
event.setLabel("days"); event.getData().setItemValue("label", "days");
event.setDescription("Average duration for payment during the last 12 months."); event.getData().setItemValue("description", "Average duration for payment during the last 12 months.");
event.getWorkitem().setItemValue("payment.avg.days", event.getValue()); event.getWorkitem().setItemValue("payment.avg.days", averagePaymentDays);
event.setLink(link); event.getWorkitem().setItemValue("link", link);
} }
if ("analytic.invoices.trend".equals(event.getKey())) { if ("analytic.invoices.trend".equals(event.getKey())) {
event.setValue(chartData); event.getData().setItemValue("value", chartData);
event.setLabel("Payment duration in days"); event.getData().setItemValue("label", "Payment duration in days");
event.setDescription("Payment practice by week"); event.getData().setItemValue("description", "Payment practice by week");
} }
} }

View file

@ -100,34 +100,39 @@ public class AGLAnalyticControllerOPListe implements Serializable {
+ "&spaceref=" + departmentID; + "&spaceref=" + departmentID;
} }
if ("analytic.invoices.count.all".equals(event.getKey())) { if ("analytic.invoices.count.all".equals(event.getKey())) {
event.setValue("" + countAll); event.getData().setItemValue("value", "" + countAll);
event.setLabel(resourceBundleHandler.findMessage("oplist.invoices")); event.getData().setItemValue("label", resourceBundleHandler.findMessage("oplist.invoices"));
event.setDescription(resourceBundleHandler.findMessage("oplist.invoices.description.all")); event.getData().setItemValue("description",
event.getWorkitem().setItemValue("invoices.total", event.getValue()); resourceBundleHandler.findMessage("oplist.invoices.description.all"));
event.setLink(link); event.getWorkitem().setItemValue("invoices.total", countAll);
event.getWorkitem().setItemValue("link", link);
} }
if ("analytic.invoices.count.open".equals(event.getKey())) { if ("analytic.invoices.count.open".equals(event.getKey())) {
event.setValue("" + (countOpen)); event.getData().setItemValue("value", "" + (countOpen));
event.setLabel(resourceBundleHandler.findMessage("oplist.invoices")); event.getData().setItemValue("label", resourceBundleHandler.findMessage("oplist.invoices"));
event.setDescription(resourceBundleHandler.findMessage("oplist.invoices.description.open")); event.getData().setItemValue("description",
event.getWorkitem().setItemValue("invoices.total.open", event.getValue()); resourceBundleHandler.findMessage("oplist.invoices.description.open"));
event.setLink(link); event.getWorkitem().setItemValue("invoices.total.open", countOpen);
event.getWorkitem().setItemValue("link", link);
} }
if ("analytic.invoices.count.due".equals(event.getKey())) { if ("analytic.invoices.count.due".equals(event.getKey())) {
event.setValue("" + countDue); event.getData().setItemValue("value", "" + countDue);
event.setLabel(resourceBundleHandler.findMessage("oplist.invoices")); event.getData().setItemValue("label", resourceBundleHandler.findMessage("oplist.invoices"));
event.setDescription(resourceBundleHandler.findMessage("oplist.invoices.description.due")); event.getData().setItemValue("description",
event.getWorkitem().setItemValue("invoices.total.due", event.getValue()); resourceBundleHandler.findMessage("oplist.invoices.description.due"));
event.setLink(link); event.getWorkitem().setItemValue("invoices.total.due", countDue);
event.getWorkitem().setItemValue("link", link);
} }
if ("analytic.invoices.count.dunning".equals(event.getKey())) { if ("analytic.invoices.count.dunning".equals(event.getKey())) {
event.setValue("" + countDunning); event.getData().setItemValue("value", "" + countDunning);
event.setLabel(resourceBundleHandler.findMessage("oplist.invoices")); event.getData().setItemValue("label", resourceBundleHandler.findMessage("oplist.invoices"));
event.setDescription(resourceBundleHandler.findMessage("oplist.invoices.description.dunning")); event.getData().setItemValue("description",
event.getWorkitem().setItemValue("invoices.total.dunning", event.getValue()); resourceBundleHandler.findMessage("oplist.invoices.description.dunning"));
event.setLink(link); event.getWorkitem().setItemValue("invoices.total.dunning", countDunning);
event.getWorkitem().setItemValue("link", link);
} }
} }

View file

@ -69,53 +69,57 @@ public class AGLAnalyticControllerSteuer implements Serializable {
link = link + "?processref=" + process.getUniqueID() + "&workflowgroup=Steuerbescheid"; link = link + "?processref=" + process.getUniqueID() + "&workflowgroup=Steuerbescheid";
} }
if ("analytic.steuer.eust".equals(event.getKey())) { if ("analytic.steuer.eust".equals(event.getKey())) {
event.setValue(computeSteuerbescheideEUST()); Object eustTotal = computeSteuerbescheideEUST();
event.setLabel("EUR"); event.getData().setItemValue("value", eustTotal);
event.setDescription("Gesamt Einfuhrumsatzsteuer (EUSt)"); event.getData().setItemValue("label", "EUR");
event.getWorkitem().setItemValue("eust.total", event.getValue()); event.getData().setItemValue("description", "Gesamt Einfuhrumsatzsteuer (EUSt)");
event.setLink(link); event.getWorkitem().setItemValue("eust.total", eustTotal);
event.getWorkitem().setItemValue("link", link);
} }
if ("analytic.steuer.zoll".equals(event.getKey())) { if ("analytic.steuer.zoll".equals(event.getKey())) {
event.setValue(computeSteuerbescheideZoll()); event.getData().setItemValue("value", computeSteuerbescheideZoll());
event.setLabel("EUR"); event.getData().setItemValue("label", "EUR");
event.setDescription("Gesamt Zölle (ZOLLEU)"); event.getData().setItemValue("description", "Gesamt Zölle (ZOLLEU)");
event.setLink(link); event.getWorkitem().setItemValue("link", link);
} }
if ("analytic.steuer.total".equals(event.getKey())) { if ("analytic.steuer.total".equals(event.getKey())) {
event.setValue(computeSteuerbescheideTotal()); event.getData().setItemValue("value", computeSteuerbescheideTotal());
event.setLabel("EUR"); event.getData().setItemValue("label", "EUR");
event.setDescription("Alle Steuerbescheide die noch nicht abgerechnet wurden."); event.getData().setItemValue("description", "Alle Steuerbescheide die noch nicht abgerechnet wurden.");
event.setLink(link); event.getWorkitem().setItemValue("link", link);
} }
if ("analytic.steuer.saldo".equals(event.getKey())) { if ("analytic.steuer.saldo".equals(event.getKey())) {
event.setValue(computeSteuerbescheideSaldo()); event.getData().setItemValue("value", computeSteuerbescheideSaldo());
event.setLabel("EUR"); event.getData().setItemValue("label", "EUR");
event.setDescription("Saldo aller Steuerbescheide die noch nicht abgerechnet wurden."); event.getData().setItemValue("description",
event.setLink(link); "Saldo aller Steuerbescheide die noch nicht abgerechnet wurden.");
event.getWorkitem().setItemValue("link", link);
} }
if ("analytic.steuer.due".equals(event.getKey())) { if ("analytic.steuer.due".equals(event.getKey())) {
event.setValue(computeSteuerbescheideSaldoDue()); event.getData().setItemValue("value", computeSteuerbescheideSaldoDue());
event.setLabel("EUR"); event.getData().setItemValue("label", "EUR");
event.setDescription("Saldo aller überfälligen Steuerbescheide die noch nicht abgerechnet wurden."); event.getData().setItemValue("description",
// event.setLink(link + "&task=Überfällig"); "Saldo aller überfälligen Steuerbescheide die noch nicht abgerechnet wurden.");
event.setLink(link + "&task=1200"); // event.getWorkitem().setItemValue("link", link + "&task=Überfällig");
event.getWorkitem().setItemValue("link", link + "&task=1200");
} }
// Charts // Charts
if ("analytic.steuer.trend".equals(event.getKey())) { if ("analytic.steuer.trend".equals(event.getKey())) {
event.setValue(accumulateSteuerbescheideByWeek()); event.getData().setItemValue("value", accumulateSteuerbescheideByWeek());
event.setLabel("Zoll & EUST"); event.getData().setItemValue("label", "Zoll & EUST");
event.setDescription("Fälligkeiten nach KW"); event.getData().setItemValue("description", "Fälligkeiten nach KW");
} }
if ("analytic.steuer.trend.abteilung".equals(event.getKey())) { if ("analytic.steuer.trend.abteilung".equals(event.getKey())) {
event.setValue(accumulateSteuerbescheideAbteilungByWeek()); event.getData().setItemValue("value", accumulateSteuerbescheideAbteilungByWeek());
event.setLabel("Nach Abteilungen"); event.getData().setItemValue("label", "Nach Abteilungen");
event.setDescription("Offene Salden nach KW pro Abteilung"); event.getData().setItemValue("description", "Offene Salden nach KW pro Abteilung");
} }
} }

View file

@ -1,181 +0,0 @@
package org.imixs.workflow.office.forms;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.faces.data.WorkflowController;
import jakarta.enterprise.context.ConversationScoped;
import jakarta.enterprise.event.Event;
import jakarta.inject.Inject;
import jakarta.inject.Named;
/**
* The AnalyticController is a conversationScoped controller that provides
* values for the analytic-custom parts.
* <p>
* A custom implementation can react on AnalyticEvent to compute values and
* datasets.
* <p>
* The controller implements a caching mechanism to avoid repeated calls for new
* analytic values. If the analytic value is already stored in the current
* workitem, no new value will be fired.
*
*
* @author rsoika
*
*/
@Named
@ConversationScoped
public class AnalyticController implements Serializable {
private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(AnalyticController.class.getName());
@Inject
protected Event<AnalyticEvent> analyticEvents;
@Inject
protected WorkflowController workflowController;
/**
* Returns a analytic value as a String for a given key.
*
* @param key
* @return
*/
public String getAsString(String key) {
ItemCollection analyticData = computeValue(key);
return analyticData.getItemValueString("value");
}
/**
* Returns a analytic value as a Json String for a given key.
*
* @param key
* @return
*/
public String getAsJson(String key) {
ItemCollection analyticData = computeValue(key);
String jsonval = analyticData.getItemValueString("value");
if (jsonval == null || jsonval.isEmpty()) {
return "null";
} else {
return jsonval;
}
}
/**
* Returns a analytic value as a Double for a given key.
*
* @param key
* @return
*/
public double getAsDouble(String key) {
ItemCollection analyticData = computeValue(key);
return analyticData.getItemValueDouble("value");
}
/**
* Returns the analytic label for a given key
*
* @param key
* @return
*/
public String getLabel(String key) {
ItemCollection analyticData = computeValue(key);
return analyticData.getItemValueString("label");
}
/**
* Returns the analytic optional link for a given key
*
* @param key
* @return
*/
public String getLink(String key) {
ItemCollection analyticData = computeValue(key);
return analyticData.getItemValueString("link");
}
/**
* Returns the analytic description for a given key
*
* @param key
* @return
*/
public String getDescription(String key) {
ItemCollection analyticData = computeValue(key);
return analyticData.getItemValueString("description");
}
/**
* Computes an analytic value. The method cache the value in the item key.
*
* An observer controller is responsible to cache or reset the cached values if
* needed.
*
* @param key
* @return
*/
protected ItemCollection computeValue(String key) {
if (workflowController.getWorkitem() != null) {
logger.fine("fire analytic event for key '" + key + "'");
// Fire the Analytics Event for this key
AnalyticEvent event = new AnalyticEvent(key, workflowController.getWorkitem());
if (analyticEvents != null) {
analyticEvents.fire(event);
if (event.getValue() != null) {
ItemCollection details = new ItemCollection();
details.setItemValue("value", event.getValue());
details.setItemValue("label", event.getLabel());
details.setItemValue("description", event.getDescription());
details.setItemValue("link", event.getLink());
// cache result
implodeDetails(key, details);
}
}
}
// analytic value is now already cached!
return explodeDetails(key);
}
/**
* Convert the List of ItemCollections back into a List of Map elements
*
* @param workitem
*/
@SuppressWarnings({ "rawtypes" })
private void implodeDetails(String key, ItemCollection details) {
// convert the child ItemCollection elements into a List of Map
List<Map> detailsList = new ArrayList<Map>();
detailsList.add(details.getAllItems());
workflowController.getWorkitem().replaceItemValue(key, detailsList);
}
/**
* converts the Map List of a workitem into a List of ItemCollectons
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private ItemCollection explodeDetails(String key) {
// convert current list of childItems into ItemCollection elements
List<Object> mapOrderItems = workflowController.getWorkitem().getItemValue(key);
if (mapOrderItems != null && mapOrderItems.size() > 0) {
ItemCollection itemCol = new ItemCollection((Map) mapOrderItems.get(0));
return itemCol;
}
// return empty collection
ItemCollection dummy = new ItemCollection();
dummy.setItemValue("value", "");
dummy.setItemValue("label", "");
dummy.setItemValue("description", "No data available");
return dummy;
}
}

View file

@ -1,66 +0,0 @@
package org.imixs.workflow.office.forms;
import org.imixs.workflow.ItemCollection;
/**
* An AnalyticEvent is send by the AnalyticController during initialization
*/
public class AnalyticEvent {
private String key;
private Object value = null;
private String label = "";
private String description = "";
private String link = "";
private ItemCollection workitem = null;
public AnalyticEvent(String key, ItemCollection workitem) {
this.key = key;
this.workitem = workitem;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
public ItemCollection getWorkitem() {
return workitem;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

View file

@ -1,173 +0,0 @@
/*******************************************************************************
* Imixs Workflow Technology
* Copyright (C) 2003, 2008 Imixs Software Solutions GmbH,
* http://www.imixs.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* Imixs Software Solutions GmbH - initial API and implementation
* Ralph Soika
*
*******************************************************************************/
package org.imixs.workflow.office.forms;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.imixs.workflow.dataview.DataViewService;
import org.imixs.workflow.exceptions.QueryException;
import org.imixs.workflow.faces.data.WorkflowController;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.Conversation;
import jakarta.enterprise.context.ConversationScoped;
import jakarta.faces.context.FacesContext;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.servlet.http.HttpServletRequest;
/**
* The DataViewFormSectionController provides methods to display a data view in
* a custom form section
*
* @see pages/workitems/sections/dataview.xhtml
* @author rsoika
* @version 1.0
*/
@Named
@ConversationScoped
public class DataViewSectionController implements Serializable {
private static final long serialVersionUID = 1L;
public static final int MAX_SEARCH_RESULT = 1000;
public static Logger logger = Logger.getLogger(DataViewSectionController.class.getName());
@Inject
WorkflowController workflowController;
@Inject
private Conversation conversation;
@Inject
DataViewService dataViewService;
private Map<String, DataViewSectionDataSet> dataSets = null;
@PostConstruct
public void init() {
startConversation();
dataSets = new HashMap<>();
}
/**
* Starts a new conversation
*/
protected void startConversation() {
if (conversation.isTransient()) {
conversation.setTimeout(
((HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest())
.getSession().getMaxInactiveInterval() * 1000);
conversation.begin();
logger.log(Level.FINEST, "......start new conversation, id={0}",
conversation.getId());
}
}
/**
* Returns a single value from a Option key/value list
*
* @param options - options
* @param key - option key
* @return option value
*/
public String getOptionValue(String options, String key) {
// Null checks
if (options == null || key == null || options.trim().isEmpty() || key.trim().isEmpty()) {
return null;
}
// Split options into key/value pairs (separated by semicolon)
String[] pairs = options.split(";");
for (String pair : pairs) {
// Split each pair into key and value (separated by equals sign)
String[] keyValue = pair.split("=", 2); // Limit to 2 in case value contains "="
if (keyValue.length == 2) {
String currentKey = keyValue[0].trim();
String currentValue = keyValue[1].trim();
// Check if the searched key was found
if (key.equals(currentKey)) {
return currentValue;
}
}
}
// Key not found
return null;
}
/**
* Initializes a new DataViewSectionDataSet based on the given options
* (parameter 'name').
*
* The expected format of the options string is:
* <p>
* "name=DATAVIEWNAME"
* <p>
* The method loads the corresponding dataView definition immediately and stores
* the dataSet in a local cache.
*
* @return view result
* @throws QueryException
*/
public DataViewSectionDataSet loadDataSet(String options) throws QueryException {
DataViewSectionDataSet dataSet = dataSets.get(options);
// Extract and set dataViewName from options
if (dataSet == null) {
String _dataViewName = getOptionValue(options, "name");
if (_dataViewName != null) {
logger.info("...build new dataSet by options: " + options);
dataSet = new DataViewSectionDataSet(_dataViewName, workflowController.getWorkitem(), dataViewService);
if (dataSet.getDataViewDefinition() != null) {
dataSets.put(options, dataSet);
}
}
}
return dataSet;
}
public boolean hasDataSet(String options) {
DataViewSectionDataSet dataSet = dataSets.get(options);
// Extract and set dataViewName from options
if (dataSet != null) {
return true;
}
// test if view exists..
String _dataViewName = getOptionValue(options, "name");
if (dataViewService.loadDataViewDefinition(_dataViewName) != null) {
return true;
}
return false;
}
}

View file

@ -1,279 +0,0 @@
package org.imixs.workflow.office.forms;
import java.io.IOException;
import java.io.Serializable;
import java.util.List;
import java.util.UUID;
import java.util.logging.Logger;
import org.imixs.workflow.FileData;
import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.dataview.DataViewController;
import org.imixs.workflow.dataview.DataViewExportEvent;
import org.imixs.workflow.dataview.DataViewPOIHelper;
import org.imixs.workflow.dataview.DataViewService;
import org.imixs.workflow.exceptions.PluginException;
import org.imixs.workflow.exceptions.QueryException;
public class DataViewSectionDataSet implements Serializable {
private static final long serialVersionUID = 1L;
public static Logger logger = Logger.getLogger(DataViewSectionController.class.getName());
List<ItemCollection> data = null;
private ItemCollection dataViewDefinition;
private String dataViewName;
private List<ItemCollection> viewItemDefinitions = null;
private String query = null;
private boolean endOfList = false;
private long totalCount = 0;
private long totalPages = 0;
private String sortBy = null;
private boolean sortReverse = false;
private int pageSize = 10;
private int pageIndex = 0;
private String viewId;
private DataViewService dataViewService;
private ItemCollection workitem;
public DataViewSectionDataSet(String dataViewName, ItemCollection workitem, DataViewService dataViewService) {
this.dataViewName = dataViewName;
this.workitem = workitem;
this.dataViewService = dataViewService;
// Generate a unique view ID for JSF component identification
this.viewId = "dv_" + UUID.randomUUID().toString().replace("-", "");
dataViewDefinition = dataViewService.loadDataViewDefinition(dataViewName);
if (dataViewDefinition == null) {
return;
}
boolean debug = dataViewDefinition.getItemValueBoolean("debug");
if (debug) {
logger.info("resolve query by dataView '" + dataViewName + "'");
}
// preload the viewItem definitions
viewItemDefinitions = this.dataViewService.computeDataViewItemDefinitions(dataViewDefinition);
// resove query by dataView
query = this.dataViewService.parseQuery(dataViewDefinition, this.workitem);
sortBy = dataViewDefinition.getItemValueString("sort.by");
sortReverse = dataViewDefinition.getItemValueBoolean("sort.reverse");
loadData();
}
/**
* return unique data view id for jsf pages
*
* @return
*/
public String getViewId() {
return viewId;
}
public void forward() {
if (!isEndOfList()) {
pageIndex++;
loadData();
}
}
public void back() {
if (pageIndex > 0) {
pageIndex--;
loadData();
}
}
/**
* load current page
*/
private void loadData() {
try {
data = dataViewService.getWorkflowService().getDocumentService().find(query, getPageSize(), getPageIndex(),
getSortBy(), isSortReverse());
// The end of a list is reached when the size is below or equal the
// pageSize. See issue #287
totalCount = dataViewService.getWorkflowService().getDocumentService().count(query);
totalPages = (long) Math.ceil((double) totalCount / pageSize);
if (data.size() < pageSize) {
setEndOfList(true);
} else {
// look ahead if we have more entries...
int iAhead = (getPageSize() * (getPageIndex() + 1)) + 1;
if (totalCount < iAhead) {
// there is no more data
setEndOfList(true);
} else {
setEndOfList(false);
}
}
} catch (QueryException e) {
logger.warning("Failed to load data: " + e.getMessage());
}
}
public List<ItemCollection> getData() {
return data;
}
public ItemCollection getDataViewDefinition() {
return dataViewDefinition;
}
public long getTotalCount() {
return totalCount;
}
public long getTotalPages() {
return totalPages;
}
public int getPageIndex() {
return pageIndex;
}
public void setPageIndex(int pageIndex) {
this.pageIndex = pageIndex;
}
public boolean isEndOfList() {
return endOfList;
}
public void setEndOfList(boolean endOfList) {
this.endOfList = endOfList;
}
public String getDataViewName() {
return dataViewName;
}
public List<ItemCollection> getViewItemDefinitions() {
return viewItemDefinitions;
}
/**
* returns the maximum size of a search result
*
* @return
*/
public int getPageSize() {
return pageSize;
}
/**
* set the maximum size of a search result
*
* @param searchCount
*/
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public String getSortBy() {
return sortBy;
}
public void setSortBy(String sortBy) {
this.sortBy = sortBy;
}
public boolean isSortReverse() {
return sortReverse;
}
public void setSortReverse(boolean sortReverse) {
this.sortReverse = sortReverse;
}
/**
* Exports data into a excel template processed by apache-poi. The method sends
* a DataViewExport event to allow clients to adapt the export process.
*
* @see DataViewExportEvent
*
* @throws PluginException
* @throws QueryException
*/
public String export() throws PluginException, QueryException {
// Build target filename
boolean debug = dataViewDefinition.getItemValueBoolean("debug");
// start export
if (debug) {
logger.info("│ ├── Query: " + query);
}
try {
// test if query exceeds max count
int totalCount = dataViewService.getWorkflowService().getDocumentService().count(query);
// start export
if (debug) {
logger.info("│ ├── Count: " + totalCount);
}
if (totalCount > DataViewService.MAX_ROWS) {
throw new PluginException(DataViewController.class.getSimpleName(), DataViewService.ERROR_CONFIG,
"Data can not be exported into Excel because dataset exceeds " + DataViewService.MAX_ROWS
+ " rows!");
}
String sortBy = dataViewDefinition.getItemValueString("sort.by");
if (sortBy.isEmpty()) {
sortBy = "$modified"; // default
}
List<ItemCollection> workitems = dataViewService.getWorkflowService().getDocumentService().find(query,
DataViewService.MAX_ROWS, 0, sortBy,
dataViewDefinition.getItemValueBoolean("sort.reverse"));
FileData fileDataExport = dataViewService.poiExport(workitems, dataViewDefinition, viewItemDefinitions);
// create a temp event
ItemCollection event = new ItemCollection().setItemValue("txtActivityResult",
dataViewDefinition.getItemValue("poi.update"));
ItemCollection poiConfig = dataViewService.getWorkflowService().evalWorkflowResult(event, "poi-update",
dataViewDefinition,
false);
// merge workitem fields (Workaround because custom forms did hard coded map to
// workflowController instead of workitem
DataViewPOIHelper.poiUpdate(workitem, fileDataExport, poiConfig, dataViewService.getWorkflowService());
if (debug) {
logger.info("├── POI Export completed!");
}
// See:
// https://stackoverflow.com/questions/9391838/how-to-provide-a-file-download-from-a-jsf-backing-bean
DataViewPOIHelper.downloadExcelFile(fileDataExport);
} catch (IOException | QueryException e) {
throw new PluginException(DataViewController.class.getSimpleName(), DataViewService.ERROR_CONFIG,
"Failed to generate Excel Export: " + e.getMessage());
}
// return "/pages/admin/excel_export_rechnungsausgang.jsf?faces-redirect=true";
return "";
}
/**
* Returns true if a poi export is defined
*
* @return
*/
public boolean hasPoiExport() {
if (dataViewDefinition != null && !dataViewDefinition.getItemValueString("poi.targetfilename").isBlank()) {
return true;
}
return false;
}
}

View file

@ -0,0 +1,7 @@
# Build Information
build.version=${project.version}
build.timestamp=${org.imixs.build.timestamp}
build.office.version=${org.imixs.office.version}
build.marty.version=${org.imixs.marty.version}
build.workflow.version=${org.imixs.workflow.version}
build.name=${imixs-office.applicationname}

View file

@ -27,6 +27,7 @@
width: 100%; width: 100%;
} }
</style> </style>
<ui:param name="data" value="#{analyticController.getData(workitem,item.name, item.options)}"></ui:param>
<div class="analytic-card-chart"> <div class="analytic-card-chart">
<div class="analytic-card-chart-detail"> <div class="analytic-card-chart-detail">
<canvas id="analytics_canvas_#{item.name}" style=""></canvas> <canvas id="analytics_canvas_#{item.name}" style=""></canvas>
@ -40,7 +41,7 @@
<script type="text/javascript"> <script type="text/javascript">
/*<![CDATA[*/ /*<![CDATA[*/
var ctx = document.getElementById("analytics_canvas_#{item.name}").getContext("2d"); var ctx = document.getElementById("analytics_canvas_#{item.name}").getContext("2d");
var chartData = #{ analyticController.getAsJson(item.name)}; var chartData = #{ data.item['value']};
new Chart(ctx, chartData); new Chart(ctx, chartData);
/*]]>*/ /*]]>*/
</script> </script>

View file

@ -41,23 +41,24 @@
margin-left: 20px; margin-left: 20px;
} }
</style> </style>
<ui:param name="data" value="#{analyticController.getData(workitem,item.name, item.options)}"></ui:param>
<div class="analytic-card"> <div class="analytic-card">
<div class="analytic-card-header"> <div class="analytic-card-header">
<h:outputText value="#{analyticController.getAsString(item.name)}"> <h:outputText value="#{data.item['value']}">
</h:outputText> </h:outputText>
</div> </div>
<div class="analytic-card-content"> <div class="analytic-card-content">
<h:outputText escape="false" value="#{analyticController.getLabel(item.name)}" /> <h:outputText escape="false" value="#{data.item['label']}" />
</div> </div>
<div class="analytic-card-footer"> <div class="analytic-card-footer">
<span>#{analyticController.getDescription(item.name)}</span> <span>#{data.item['description']}</span>
<h:panelGroup styleClass="analytic-card-link" rendered="#{!empty analyticController.getLink(item.name)}"> <h:panelGroup styleClass="analytic-card-link" rendered="#{!empty data.item['link']}}">
<a href="#{analyticController.getLink(item.name)}"><span class="typcn typcn-zoom-outline" /></a> <a href="#{data.item['link']}"><span class="typcn typcn-zoom-outline" /></a>
</h:panelGroup> </h:panelGroup>