introduced new DataGroups/DataViews

This commit is contained in:
Ralph Soika 2025-08-12 23:12:13 +02:00
parent 73dd3c37a5
commit 22d23226e4
15 changed files with 21 additions and 1403 deletions

View file

@ -5,6 +5,7 @@
- Finalisierung Business Partner Interface
|- Neue Plugin Logik (Aktualisierung der BP Nummer und Aktivierung von BP Objekten falls diese archiviert waren)
- Zoho Schnittstelle
- Neues DataView und DataGroups Konzept implementiert
### Migration

View file

@ -57,11 +57,11 @@ services:
# Tika OCR Service
###############################################
tika:
image: apache/tika:3.2.0.0-full
environment:
TZ: "Europe/Berlin"
ports:
- "9998:9998"
image: apache/tika:3.2.0.0-full
environment:
TZ: "Europe/Berlin"
ports:
- "9998:9998"
##################
# Exporter Service
@ -88,10 +88,10 @@ services:
###############################################
# Imixs-Admin
###############################################
imixsadmin:
image: imixs/imixs-admin
ports:
- "8888:8080"
# imixsadmin:
# image: imixs/imixs-admin
# ports:
# - "8888:8080"
###############################################
# Mailgateway

View file

@ -282,6 +282,10 @@
<groupId>org.imixs.workflow</groupId>
<artifactId>imixs-data-groups</artifactId>
</dependency>
<dependency>
<groupId>org.imixs.workflow</groupId>
<artifactId>imixs-data-views</artifactId>
</dependency>
<!-- SEPA Adapter -->
<dependency>

View file

@ -12,12 +12,12 @@ import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.dataview.DataViewExportEvent;
import org.imixs.workflow.engine.DocumentService;
import org.imixs.workflow.engine.WorkflowService;
import org.imixs.workflow.exceptions.PluginException;
import org.imixs.workflow.exceptions.QueryException;
import org.imixs.workflow.office.config.ConfigService;
import org.imixs.workflow.office.dataview.DataViewExportEvent;
import org.imixs.workflow.poi.POIFindReplaceAdapter;
import org.imixs.workflow.poi.POIUtil;

View file

@ -1,64 +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.dataview;
import java.io.Serializable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.imixs.workflow.ItemCollection;
import jakarta.enterprise.context.SessionScoped;
import jakarta.inject.Named;
/**
* The DataViewCache implements a sessionScoped cache for the dataView filter.
* The cache uses the cacheId provided in the URL - see DataViewController
* onLoad
*
* @author rsoika
* @version 1.0
*/
@Named
@SessionScoped
public class DataViewCache implements Serializable {
private static final long serialVersionUID = 1L;
private Map<String, ItemCollection> dataViewStates = new ConcurrentHashMap<>();
public void put(String key, ItemCollection data) {
dataViewStates.put(key, data);
}
public ItemCollection get(String key) {
ItemCollection result = dataViewStates.get(key);
if (result == null) {
// init new empty cache object!
result = new ItemCollection();
dataViewStates.put(key, result);
}
return result;
}
}

View file

@ -1,429 +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.dataview;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import org.imixs.archive.core.SnapshotService;
import org.imixs.workflow.FileData;
import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.engine.DocumentService;
import org.imixs.workflow.engine.WorkflowService;
import org.imixs.workflow.exceptions.ModelException;
import org.imixs.workflow.exceptions.PluginException;
import org.imixs.workflow.exceptions.QueryException;
import org.imixs.workflow.faces.data.ViewController;
import org.imixs.workflow.faces.data.ViewHandler;
import org.imixs.workflow.faces.data.WorkflowController;
import org.imixs.workflow.office.forms.CustomFormController;
import org.imixs.workflow.office.forms.CustomFormSection;
import com.alexanderlogistics.mahnlauf.InkassoExportAdapter;
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 DataViewController is used to display a data view
* <p>
* The controller uses the uniqueId from the URL to load the definition.
* The bean reads optional cached query data form
* a session scoped cache EJB and reloads the last state. This is useful for
* situations where the user navigates to a new page (e.g. open a workitem) and
* late uses browser history back.
* <p>
* Note: This bean is ConversationScoped, because it uses the
* CustomFormController which expects conversion scope!
*
*
* @author rsoika
* @version 1.0
*/
@Named
@ConversationScoped
public class DataViewController extends ViewController {
private static final long serialVersionUID = 1L;
public static final String ERROR_CONFIG = "CONFIG_ERROR";
public static final int MAX_ROWS = 9999;
private List<CustomFormSection> sections = null;
private List<ItemCollection> viewItemDefinitions = null;
protected ItemCollection dataViewDefinition = null;
private ItemCollection filter;
private String query;
private String errorMessage;
@Inject
DataViewCache dataViewCache;
@Inject
private Conversation conversation;
@Inject
private DocumentService documentService;
@Inject
private WorkflowService workflowService;
@Inject
SnapshotService snapshotService;
@Inject
CustomFormController customFormController;
@Inject
WorkflowController workflowController;
@Inject
DataViewDefaultExporter dataViewDefaultExporter;
@Inject
ViewHandler viewHandler;
@Inject
DataViewDefinitionController dataViewDefinitionController;
private static Logger logger = Logger.getLogger(DataViewController.class.getName());
@Override
@PostConstruct
public void init() {
super.init();
// this.setQuery(dataViewController.getQuery());
this.setSortBy("$modified");
this.setSortReverse(false);
this.setPageSize(100);
this.setLoadStubs(false);
}
/**
* This method loads the custom form sections
*/
public void onLoad() {
String uniqueid = null;
// Important: start a new conversation beause of the usage of the
// CustomFormController!
startConversation();
FacesContext facesContext = FacesContext.getCurrentInstance();
if (!facesContext.isPostback() && !facesContext.isValidationFailed()) {
// ...
FacesContext fc = FacesContext.getCurrentInstance();
Map<String, String> paramMap = fc.getExternalContext().getRequestParameterMap();
// try to extract the uniqueid form the query string...
uniqueid = paramMap.get("id");
if (uniqueid == null || uniqueid.isEmpty()) {
// alternative 'workitem=...'
uniqueid = paramMap.get("workitem");
}
dataViewDefinition = documentService.load(uniqueid);
}
if (uniqueid != null && !uniqueid.isEmpty()) {
filter = dataViewCache.get(uniqueid);
} else {
filter = new ItemCollection();
}
try {
// Init new Filter....
if (dataViewDefinition != null) {
filter.setItemValue("txtWorkflowEditorCustomForm", dataViewDefinition.getItemValue("form"));
filter.setItemValue("name", dataViewDefinition.getItemValueString("name"));
filter.setItemValue("description", dataViewDefinition.getItemValueString("description"));
viewItemDefinitions = DataViewDefinitionController
.computeDataViewItemDefinitions(dataViewDefinition);
customFormController.computeFieldDefinition(filter);
sections = customFormController.getSections();
// Update View Handler settings
String sortBy = dataViewDefinition.getItemValueString("sort.by");
if (sortBy.isEmpty()) {
sortBy = "$modified";
}
this.setSortBy(sortBy);
this.setSortReverse(dataViewDefinition.getItemValueBoolean("sort.reverse"));
this.setPageIndex(filter.getItemValueInteger("pageIndex"));
if (!filter.getItemValueString("query").isEmpty()) {
query = filter.getItemValueString("query");
// Prefetch data to update total count and page count
viewHandler.getData(this);
}
}
} catch (ModelException | QueryException e) {
logger.warning("Failed to load dataview definition: " + e.getMessage());
}
}
@Override
public List<ItemCollection> loadData() throws QueryException {
try {
return super.loadData();
} catch (Exception e) {
logger.severe("Failed to load view: " + e.getMessage());
return new ArrayList<>();
}
}
public ItemCollection getDataViewDefinition() {
return dataViewDefinition;
}
public List<CustomFormSection> getSections() {
return sections;
}
public List<ItemCollection> getViewItemDefinitions() {
return viewItemDefinitions;
}
public ItemCollection getFilter() {
return filter;
}
public void setFilter(ItemCollection filter) {
this.filter = filter;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
/**
* This helper method builds a query from the query definition and the current
* filter criteria.
*
* The method loads the query form the definition and replaces all {<itemname>}
* elements with the values from the filter
*
*
* @throws QueryException
*/
public void run() throws PluginException, QueryException {
reset();
// build new query from template
query = dataViewDefinition.getItemValueString("query");
query = parseQuery(query, filter);
query = parseQuery(query, workflowController.getWorkitem());
logger.info("query=" + query);
filter.setItemValue("query", query);
// Prefetch data to update total count and page count
viewHandler.getData(this);
// cache filter
dataViewCache.put(dataViewDefinition.getUniqueID(), filter);
}
private String parseQuery(String query, ItemCollection filter) {
List<String> filterItems = filter.getItemNames();
for (String itemName : filterItems) {
String itemValue = filter.getItemValueString(itemName);
// is date?
if (filter.getItemValueDate(itemName) != null) {
String sDateFrom = "191401070000"; // because * did not work here
String sDateTo = "211401070000";
SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMMddHHmm");
itemValue = dateformat.format(filter.getItemValueDate(itemName));
}
// Create regex pattern to match {itemName} (case-sensitive)
// The Pattern.quote is used to escape any special regex characters in the
// itemName
// Replace all occurrences in the query case-insensitive.
query = query.replaceAll("(?i)\\{" + Pattern.quote(itemName) + "\\}", itemValue);
}
return query;
}
/**
* Returns the current query
*
* @return
*/
public String getQuery() {
return query;
}
/**
* This method navigates back in the page index and caches the current page
* index
*/
public void back() {
viewHandler.back(this);
filter.setItemValue("pageIndex", this.getPageIndex());
}
/**
* This method navigates forward in the page index and caches the current page
* index
*/
public void forward() {
viewHandler.forward(this);
filter.setItemValue("pageIndex", this.getPageIndex());
}
/**
* 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());
}
}
/**
* 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 query and prepare dataset
run();
SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMMddHHmm");
String targetFileName = dataViewDefinition.getItemValueString("poi.targetFilename");
if (targetFileName.isEmpty()) {
throw new PluginException(InkassoExportAdapter.class.getSimpleName(), ERROR_CONFIG,
"Missing Excel Export definition - check configuration!");
}
logger.info("start export : " + targetFileName + "...");
logger.fine(query);
// load template
FileData fileData = loadTemplate();
if (fileData == null) {
// we did not found the template!
throw new PluginException(InkassoExportAdapter.class.getSimpleName(), ERROR_CONFIG,
"Missing Excel Export Template - check DataView definition!");
}
targetFileName = targetFileName + "_" + dateformat.format(new Date()) + ".xlsx";
try {
// test if query exceeds max count
int totalCount = documentService.count(query);
if (totalCount > MAX_ROWS) {
throw new PluginException(InkassoExportAdapter.class.getSimpleName(), ERROR_CONFIG,
"Data can not be exported into Excel because dataset exceeds " + MAX_ROWS + " rows!");
}
String sortBy = dataViewDefinition.getItemValueString("sort.by");
if (sortBy.isEmpty()) {
sortBy = "$modified"; // default
}
List<ItemCollection> invoices = documentService.find(query, MAX_ROWS, 0, sortBy,
dataViewDefinition.getItemValueBoolean("sort.reverse"));
if (invoices.size() > 0) {
dataViewDefaultExporter.insertDataRows(invoices, dataViewDefinition, viewItemDefinitions, fileData);
}
// create a temp event
ItemCollection event = new ItemCollection().setItemValue("txtActivityResult",
dataViewDefinition.getItemValue("poi.update"));
ItemCollection poiConfig = workflowService.evalWorkflowResult(event, "poi-update", dataViewDefinition,
false);
// merge workitem fields (Workaround because custom forms did hard coded map to
// workflowController instead of workitem
filter.copy(workflowController.getWorkitem());
DataViewPOIHelper.poiUpdate(filter, fileData, poiConfig, workflowService);
fileData.setName(targetFileName);
// See:
// https://stackoverflow.com/questions/9391838/how-to-provide-a-file-download-from-a-jsf-backing-bean
DataViewPOIHelper.downloadExcelFile(fileData);
} catch (IOException | QueryException e) {
throw new PluginException(InkassoExportAdapter.class.getSimpleName(), ERROR_CONFIG,
"Failed to generate Excel Export: " + e.getMessage());
}
// return "/pages/admin/excel_export_rechnungsausgang.jsf?faces-redirect=true";
return "";
}
/**
* This method returns the first excel poi template from the Data Definition
*
* @param name in attribute txtname
*
*
*/
private FileData loadTemplate() {
// first filename
List<FileData> fileDataList = dataViewDefinition.getFileData();
if (fileDataList != null && fileDataList.size() > 0) {
String fileName = fileDataList.get(0).getName();
return snapshotService.getWorkItemFile(dataViewDefinition.getUniqueID(), fileName);
}
// no file data available!
return null;
}
}

View file

@ -1,145 +0,0 @@
package org.imixs.workflow.office.dataview;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.List;
import java.util.logging.Logger;
import org.apache.poi.ss.usermodel.CellCopyPolicy;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.imixs.workflow.FileData;
import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.exceptions.PluginException;
import org.imixs.workflow.office.forms.AnalyticController;
import jakarta.enterprise.context.ConversationScoped;
import jakarta.enterprise.event.Event;
import jakarta.inject.Inject;
import jakarta.inject.Named;
/**
* The DataViewDefaultExporter exports a dataset into a excel template. The
* exporter sends a DataViewExportEvent. An observer CID bean can implement
* alternative exporters.
* With the event property 'completed' a client can signal that the export
* process is completed. Otherwise the default behavior will be adapted.
*
* @author rsoika
*
*/
@Named
@ConversationScoped
public class DataViewDefaultExporter implements Serializable {
public static final String ERROR_CONFIG = "CONFIG_ERROR";
private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(AnalyticController.class.getName());
@Inject
protected Event<DataViewExportEvent> dataViewExportEvents;
/**
* This helper method inserts a row for each invoice
*
* @throws PluginException
*/
public void insertDataRows(List<ItemCollection> dataset, ItemCollection dataViewDefinition,
List<ItemCollection> viewItemDefinitions,
FileData fileData) throws PluginException {
// load XSSFWorkbook
try (InputStream imputStream = new ByteArrayInputStream(fileData.getContent())) {
XSSFWorkbook doc = new XSSFWorkbook(imputStream);
// send DataViewExportEvent....
if (dataViewExportEvents != null) {
DataViewExportEvent event = new DataViewExportEvent(dataset, dataViewDefinition, viewItemDefinitions,
doc);
dataViewExportEvents.fire(event); // found FileData?
if (!event.isCompleted()) {
// Default behavior
defaultExport(dataset, dataViewDefinition, viewItemDefinitions, doc);
}
}
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// write back the file
doc.write(byteArrayOutputStream);
doc.close();
byte[] newContent = byteArrayOutputStream.toByteArray();
fileData.setContent(newContent);
} catch (IOException e) {
throw new PluginException(DataViewPOIHelper.class.getSimpleName(), ERROR_CONFIG,
"failed to update excel export: " + e.getMessage());
}
}
/**
* This is the default implementation to insert a dataset into a XSSFSheet
*
* @param dataset
* @param referenceCell
* @param viewItemDefinitions
* @param doc
*/
private void defaultExport(List<ItemCollection> dataset, ItemCollection dataViewDefinition,
List<ItemCollection> viewItemDefinitions, XSSFWorkbook doc) {
String referenceCell = dataViewDefinition.getItemValueString("poi.referenceCell");
// NOTE: we only take the first sheet !
XSSFSheet sheet = doc.getSheetAt(0);
CellReference cr = new CellReference(referenceCell);
XSSFRow referenceRow = sheet.getRow(cr.getRow());
int referenceRowPos = referenceRow.getRowNum() + 1;
int rowPos = referenceRowPos;
// int lastRow = sheet.getLastRowNum();
int lastRow = 999;
logger.finest("Last rownum=" + lastRow);
sheet.shiftRows(rowPos, lastRow, dataset.size(), true, true);
for (ItemCollection workitem : dataset) {
logger.finest("......copy row...");
// now create a new line..
XSSFRow row = sheet.createRow(rowPos);
row.copyRowFrom(referenceRow, new CellCopyPolicy());
// insert values
int cellNum = 0;
for (ItemCollection itemDef : viewItemDefinitions) {
String type = itemDef.getItemValueString("item.type");
String name = itemDef.getItemValueString("item.name");
switch (type) {
case "xs:double":
row.getCell(cellNum).setCellValue(workitem.getItemValueDouble(name));
break;
case "xs:float":
row.getCell(cellNum).setCellValue(workitem.getItemValueFloat(name));
break;
case "xs:int":
row.getCell(cellNum).setCellValue(workitem.getItemValueInteger(name));
break;
case "xs:date":
row.getCell(cellNum).setCellValue(workitem.getItemValueDate(name));
break;
default:
row.getCell(cellNum).setCellValue(workitem.getItemValueString(name));
}
cellNum++;
}
rowPos++;
}
// delete reference row
sheet.shiftRows(referenceRowPos, lastRow + dataset.size(), -1, true, true);
}
}

View file

@ -1,322 +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.dataview;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.exceptions.AccessDeniedException;
import org.imixs.workflow.exceptions.QueryException;
import org.imixs.workflow.faces.data.AbstractDataController;
import org.imixs.workflow.faces.data.DocumentController;
import com.oracle.svm.core.annotate.Inject;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.SessionScoped;
import jakarta.faces.context.ExternalContext;
import jakarta.faces.context.FacesContext;
import jakarta.inject.Named;
/**
* The DataViewDefinitionController is used to configure a dataview definition
*
* @author rsoika
* @version 1.0
*/
@Named
@SessionScoped
public class DataViewDefinitionController extends AbstractDataController {
private static final long serialVersionUID = 1L;
protected List<ItemCollection> attributeList = null;
@Inject
DocumentController documentController;
protected List<ItemCollection> dataViewDefinitions = null;
private static Logger logger = Logger.getLogger(DataViewDefinitionController.class.getName());
/**
*
*
*/
@PostConstruct
public void init() {
}
public List<ItemCollection> getDefinitions() {
if (dataViewDefinitions == null) {
// lazy loading
String quuery = "(type:\"dataview\")";
try {
dataViewDefinitions = this.getDocumentService().find(quuery, 999, 0, "name", false);
} catch (QueryException e) {
logger.severe("Failed to load DataView definition list: " + e.getMessage());
}
}
return dataViewDefinitions;
}
/**
* Finds all dataview definitions assigned to a process by its name
*
* @param name
* @return
*/
public List<ItemCollection> findDefinitionByProcessRef(String id) {
List<ItemCollection> result = new ArrayList<>();
if (id == null) {
return result;
}
getDefinitions();
for (ItemCollection def : dataViewDefinitions) {
if (id.equals(def.getItemValueString("process.ref"))) {
result.add(def);
}
}
return result;
}
/**
* Returns the current workItem. If no workitem is defined the method
* Instantiates a empty ItemCollection.
*
* @return - current workItem or null if not set
*/
public ItemCollection getData() {
// do initialize an empty workItem here if null
if (data == null) {
reset();
}
return data;
}
/**
* This method returns a unique view URI to be used zu display the View Data.
* The uri contains a unique random ID to support caching over different browser
* tabs
*
* @return
*/
public String getViewURI(ItemCollection dataDef) {
return "/pages/dataviews/data.xhtml?id=" + dataDef.getUniqueID() + "&faces-redirect=true";
}
@Override
public void load(String uniqueid) {
super.load(uniqueid);
attributeList = computeDataViewItemDefinitions(this.data);
}
/**
* Returns a List of ItemCollection instances representing the view column
* description.
* Each column has the items:
*
* name,label,format,convert
*
* @param dataViewDefinition
* @return
*
*/
@SuppressWarnings("unchecked")
public static List<ItemCollection> computeDataViewItemDefinitions(ItemCollection dataViewDefinition) {
ArrayList<ItemCollection> result = new ArrayList<ItemCollection>();
List<Object> mapItems = dataViewDefinition.getItemValue("dataview.items");
for (Object mapOderItem : mapItems) {
if (mapOderItem instanceof Map) {
ItemCollection itemCol = new ItemCollection((Map) mapOderItem);
// check label
String itemLabel = itemCol.getItemValueString("item.label");
if (itemLabel.isEmpty()) {
itemCol.setItemValue("item.label", itemLabel);
}
// check type
String itemType = itemCol.getItemValueString("item.type");
if (itemType.isEmpty()) {
itemCol.setItemValue("item.type", "xs:string");
}
result.add(itemCol);
}
}
// if no columns are defined we create the default columns
if (result.size() == 0) {
ItemCollection itemCol = new ItemCollection();
itemCol.setItemValue("item.name", "$workflowSummary");
itemCol.setItemValue("item.label", "Name");
itemCol.setItemValue("item.type", "xs:anyURI");
result.add(itemCol);
itemCol = new ItemCollection();
itemCol.setItemValue("item.name", "$modified");
itemCol.setItemValue("item.label", "Modified");
itemCol.setItemValue("item.type", "xs:date");
result.add(itemCol);
}
return result;
}
/**
* creates an empty new dataview definition
*/
public void create() {
data = new ItemCollection();
data.setType("dataview");
}
/**
* This method saves the current document.
*
* @throws AccessDeniedException - if user has insufficient access rights.
*/
public void save() throws AccessDeniedException {
// save definition ...
data.setType("dataview");
FacesContext context = FacesContext.getCurrentInstance();
ExternalContext externalContext = context
.getExternalContext();
String sUser = externalContext.getRemoteUser();
data.replaceItemValue("$editor", sUser);
// implode attributes
if (attributeList != null) {
List<Map> mapItemList = new ArrayList<Map>();
logger.fine("Convert attribute items into Map...");
for (ItemCollection attrItem : attributeList) {
ItemCollection ding = (ItemCollection) attrItem.clone();
mapItemList.add(ding.getAllItems());
}
data.replaceItemValue("dataview.items", mapItemList);
}
data = this.getDocumentService().save(data);
// reset view
dataViewDefinitions = null;
}
public String openTestView() {
this.save();
return "/pages/dataviews/data.xhtml?id=" + data.getUniqueID() + "&faces-redirect=true";
}
public List<ItemCollection> getAttributeList() {
if (attributeList == null) {
attributeList = new ArrayList<>();
}
return attributeList;
}
/**
* Adds a new attribute object.
*/
public void addAttribute() {
if (attributeList == null) {
attributeList = new ArrayList<ItemCollection>();
}
ItemCollection source = new ItemCollection();
attributeList.add(source);
}
/**
* Removes an attribute item from the list
*
* @param name - name of attribute
*/
public void removeAttribute(String name) {
if (name != null && attributeList != null) {
for (ItemCollection item : attributeList) {
if (name.equals(item.getItemValueString("item.name"))) {
attributeList.remove(item);
break;
}
}
}
}
/**
* Moves an attribute item up in the list
*
* List<ItemCollection> attributeList
*
* @param name - name of attribute
*/
public void moveAttributeUp(String name) {
if (name != null && attributeList != null) {
logger.info("move up: " + name);
for (int i = 0; i < attributeList.size(); i++) {
ItemCollection item = attributeList.get(i);
if (name.equals(item.getItemValueString("item.name"))) {
// Check if it's not already at the top
if (i > 0) {
// Swap with previous element
Collections.swap(attributeList, i, i - 1);
}
break; // Exit loop once found and processed
}
}
}
}
/**
* Moves an attribute item up down the list
*
* List<ItemCollection> attributeList
*
* @param name - name of attribute
*/
public void moveAttributeDown(String name) {
if (name != null && attributeList != null) {
logger.info("move down: " + name);
for (int i = 0; i < attributeList.size(); i++) {
ItemCollection item = attributeList.get(i);
if (name.equals(item
.getItemValueString("item.name"))) {
// Check if it's not already at the bottom
if (i < attributeList.size() - 1) {
// Swap with next element
Collections.swap(attributeList, i, i + 1);
}
break; // Exit loop once found and processed
}
}
}
}
}

View file

@ -1,72 +0,0 @@
package org.imixs.workflow.office.dataview;
import java.util.List;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.imixs.workflow.ItemCollection;
/**
* An DataViewExportEvent is send by the DataViewController during exporting a
* data view into a Excel Template.
* An observer can customize the export behavior and mark an export as
* completed by setting the flag 'completed' to true
*
*/
public class DataViewExportEvent {
private List<ItemCollection> dataset = null;
private ItemCollection dataViewDefinition = null;
private List<ItemCollection> viewItemDefinitions = null;
private XSSFWorkbook xssfWorkbook = null;
private boolean completed = false;
public DataViewExportEvent(List<ItemCollection> dataset, ItemCollection dataViewDefinition,
List<ItemCollection> viewItemDefinitions,
XSSFWorkbook xssfWorkbook) {
this.dataset = dataset;
this.dataViewDefinition = dataViewDefinition;
this.xssfWorkbook = xssfWorkbook;
this.viewItemDefinitions = viewItemDefinitions;
}
public boolean isCompleted() {
return completed;
}
public void setCompleted(boolean completed) {
this.completed = completed;
}
public List<ItemCollection> getDataset() {
return dataset;
}
public void setDataset(List<ItemCollection> dataset) {
this.dataset = dataset;
}
public ItemCollection getDataViewDefinition() {
return dataViewDefinition;
}
public void setDataViewDefinition(ItemCollection dataViewDefinition) {
this.dataViewDefinition = dataViewDefinition;
}
public List<ItemCollection> getViewItemDefinitions() {
return viewItemDefinitions;
}
public void setViewItemDefinitions(List<ItemCollection> viewItemDefinitions) {
this.viewItemDefinitions = viewItemDefinitions;
}
public XSSFWorkbook getXssfWorkbook() {
return xssfWorkbook;
}
public void setXssfWorkbook(XSSFWorkbook xssfWorkbook) {
this.xssfWorkbook = xssfWorkbook;
}
}

View file

@ -1,358 +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.dataview;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Name;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.imixs.workflow.FileData;
import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.engine.WorkflowService;
import org.imixs.workflow.exceptions.PluginException;
import org.imixs.workflow.util.XMLParser;
import jakarta.faces.context.ExternalContext;
import jakarta.faces.context.FacesContext;
/**
* The DataViewPOIHelper provides methods to update a excel template with the
* help of the Apache POI framework.
*
*
* @author rsoika
* @version 1.0
*/
public class DataViewPOIHelper {
private static Logger logger = Logger.getLogger(DataViewPOIHelper.class.getName());
public static final String ERROR_CONFIG = "CONFIG_ERROR";
/**
* Helper method to initialize a file download
*
* @throws IOException
*/
public static void downloadExcelFile(FileData fileData) throws IOException {
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
externalContext.responseReset();
externalContext.setResponseContentType("application/vnd.ms-excel");
externalContext.setResponseContentLength(fileData.getContent().length);
externalContext.setResponseHeader("Content-Disposition", "attachment; filename=\"" + fileData.getName() + "\"");
OutputStream output = externalContext.getResponseOutputStream();
// Now you can write the InputStream of the file to the above OutputStream the
// usual way.
output.write(fileData.getContent());
facesContext.responseComplete(); // Important! Otherwise Faces will attempt to render the response which
// obviously will fail since it's already written with a file and closed.
}
/**
* This helper method inserts a row for each invoice
*
* @throws PluginException
*/
// public static void insertDataRows(List<ItemCollection> dataset, String
// referenceCell,
// List<ItemCollection> viewItemDefinitions,
// FileData fileData) throws PluginException {
// // load XSSFWorkbook
// try (InputStream imputStream = new
// ByteArrayInputStream(fileData.getContent())) {
// XSSFWorkbook doc = new XSSFWorkbook(imputStream);
// // NOTE: we only take the first sheet !
// XSSFSheet sheet = doc.getSheetAt(0);
// CellReference cr = new CellReference(referenceCell);
// XSSFRow referenceRow = sheet.getRow(cr.getRow());
// int referenceRowPos = referenceRow.getRowNum() + 1;
// int rowPos = referenceRowPos;
// // int lastRow = sheet.getLastRowNum();
// int lastRow = 999;
// logger.finest("Last rownum=" + lastRow);
// sheet.shiftRows(rowPos, lastRow, dataset.size(), true, true);
// for (ItemCollection workitem : dataset) {
// logger.finest("......copy row...");
// // now create a new line..
// XSSFRow row = sheet.createRow(rowPos);
// row.copyRowFrom(referenceRow, new CellCopyPolicy());
// // insert values
// int cellNum = 0;
// for (ItemCollection itemDef : viewItemDefinitions) {
// String type = itemDef.getItemValueString("item.type");
// String name = itemDef.getItemValueString("item.name");
// switch (type) {
// case "xs:double":
// row.getCell(cellNum).setCellValue(workitem.getItemValueDouble(name));
// break;
// case "xs:float":
// row.getCell(cellNum).setCellValue(workitem.getItemValueFloat(name));
// break;
// case "xs:int":
// row.getCell(cellNum).setCellValue(workitem.getItemValueInteger(name));
// break;
// case "xs:date":
// row.getCell(cellNum).setCellValue(workitem.getItemValueDate(name));
// break;
// default:
// row.getCell(cellNum).setCellValue(workitem.getItemValueString(name));
// }
// cellNum++;
// }
// rowPos++;
// }
// // delete reference row
// sheet.shiftRows(referenceRowPos, lastRow + dataset.size(), -1, true, true);
// ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// // write back the file
// doc.write(byteArrayOutputStream);
// doc.close();
// byte[] newContent = byteArrayOutputStream.toByteArray();
// fileData.setContent(newContent);
// } catch (IOException e) {
// throw new PluginException(DataViewPOIHelper.class.getSimpleName(),
// ERROR_CONFIG,
// "failed to update excel export: " + e.getMessage());
// }
// }
/**
* This helper method applies the POI update defintiions a row for each invoice
*
* @throws PluginException
*/
public static void poiUpdate(ItemCollection dataDefinition, FileData fileData,
ItemCollection poiConfig, WorkflowService workflowService) throws PluginException {
// update $modified for Now function
dataDefinition.setItemValue("$modified", new Date());
if (poiConfig == null || !poiConfig.hasItem("findreplace")) {
// no config found
return;
}
List<String> replaceDevList = poiConfig.getItemValue("findreplace");
String eval = poiConfig.getItemValueString("eval");
// load XSSFWorkbook
try (InputStream imputStream = new ByteArrayInputStream(fileData.getContent())) {
XSSFWorkbook workbook = new XSSFWorkbook(imputStream);
// NOTE: we only take the first sheet !
XSSFSheet sheet = workbook.getSheetAt(0);
updateXSSFWorkbook(workbook, dataDefinition, replaceDevList, workflowService);
// Update Eval list
if (eval != null && !eval.isEmpty()) {
// iterate over all cells to be evaluated
String[] cellPositions = eval.split(";");
for (String cellPos : cellPositions) {
evalXSSFSheet(workbook, sheet, cellPos);
}
logger.fine("formula evaluation completed");
}
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// write back the file
workbook.write(byteArrayOutputStream);
workbook.close();
byte[] newContent = byteArrayOutputStream.toByteArray();
fileData.setContent(newContent);
} catch (IOException e) {
throw new PluginException(DataViewPOIHelper.class.getSimpleName(), ERROR_CONFIG,
"failed to update excel export: " + e.getMessage());
}
}
/**
* Returns a Cell by name or an optional absolute cell postion
* <p>
* Examples for refs are 'A1', 'AB3', 'MyCell' where 'MyCell' is a named cell.
* <p>
*
*/
public static XSSFCell getCellByRef(XSSFWorkbook doc, XSSFSheet sheet, String cellReference) {
XSSFCell cell = null;
// first we test if the cellName is a named cell
Name aNamedCell = doc.getName(cellReference);
if (aNamedCell != null) {
// yes its a named cell so we need to get the referrer Formula
logger.finest("...resolving named cell = " + aNamedCell.getNameName());
cellReference = aNamedCell.getRefersToFormula();
// now we can find the cell by its ref
}
CellReference cr = new CellReference(cellReference);
XSSFRow row = sheet.getRow(cr.getRow());
if (row == null) {
logger.severe("Unable to resolve cell ref '" + cellReference + "'!");
return null;
}
cell = row.getCell(cr.getCol());
return cell;
}
/**
* This method updates the XSSFWorkbook document. The method can be overwritten
* by subclasses to add additional logic
*
* @param workbook
* @param workitem
* @param replaceDevList
* @throws PluginException
*/
public static void updateXSSFWorkbook(XSSFWorkbook workbook, ItemCollection workitem, List<String> replaceDevList,
WorkflowService workflowService)
throws PluginException {
logger.fine("XSSFWorkbook loaded");
// NOTE: we only take the first sheet !
XSSFSheet sheet = workbook.getSheetAt(0);
for (String entityDev : replaceDevList) {
ItemCollection entityData = XMLParser.parseItemStructure(entityDev);
if (entityData != null) {
String find = entityData.getItemValueString("find");
String replace = entityData.getItemValueString("replace");
replace = workflowService.adaptText(replace, workitem);
// optional itename
String itemname = entityData.getItemValueString("itemname");
// replace with item value?
if (!itemname.isEmpty()) {
List<?> valueList = workitem.getItemValue(itemname);
if (valueList.size() > 0) {
// provide the first value only
replaceXSSFSheetItemValue(workbook, sheet, find, valueList.get(0));
}
} else {
replaceXSSFSheetStringValue(workbook, sheet, find, replace);
}
}
}
}
/**
* Helper method replaces a given cell of a XSSFSheet with a typed item value
*
* @throws PluginException
*/
public static void replaceXSSFSheetItemValue(XSSFWorkbook doc, XSSFSheet sheet, String find, Object itemValue)
throws PluginException {
logger.finest("update cell " + find);
XSSFCell cell = getCellByRef(doc, sheet, find);
if (cell == null) {
logger.warning("Cell " + find + " not found.");
return;
}
if (itemValue instanceof Date) {
cell.setCellValue((Date) itemValue);
} else if (itemValue instanceof Double) {
cell.setCellValue((Double) itemValue);
} else {
// default to text
cell.setCellValue(itemValue.toString());
}
}
/**
* Helper method replaces a given cell of a XSSFSheet with a string value
*
* @throws PluginException
*/
private static void replaceXSSFSheetStringValue(XSSFWorkbook doc, XSSFSheet sheet, String find, String replace)
throws PluginException {
logger.finest("update cell " + find);
XSSFCell cell = getCellByRef(doc, sheet, find);
if (cell == null) {
logger.warning("Cell " + find + " not found.");
return;
}
try {
// we try to set first as float value if possible
float f = Float.parseFloat(replace);
cell.setCellValue(f);
} catch (NumberFormatException e) {
// set value as string
cell.setCellValue(replace);
}
}
/**
* Evaluates a given list of cells in a given XSWorkbook
*
* @param doc
* @param sheet
* @param cell
* @throws PluginException
*/
public static void evalXSSFSheet(XSSFWorkbook doc, XSSFSheet sheet, String cell) throws PluginException {
FormulaEvaluator evaluator = doc.getCreationHelper().createFormulaEvaluator();
XSSFCell c = getCellByRef(doc, sheet, cell);
if (c == null) {
logger.warning("Cell " + cell + " not found.");
return;
}
if (c.getCellType() == CellType.FORMULA) {
logger.finest("...eval cell " + cell);
try {
CellType evalResult = evaluator.evaluateFormulaCell(c);
if (evalResult == CellType.ERROR) {
logger.warning("...unable to evaluate cell " + cell);
}
} catch (Exception poie) {
logger.warning("...failed to evaluate cell " + cell + " : " + poie.getMessage());
}
}
}
}

View file

@ -14,9 +14,6 @@
<li>
<h:link outcome="/pages/admin/analyse_invoicing">Analyse Rechnungseingang</h:link>
</li>
<li>
<h:link outcome="/pages/admin/dataViewDefinitions">Data Views</h:link>
</li>
<li>
<h:link outcome="/pages/admin/excel_export_rechnungsausgang">Excel Export Rechnungsausgang</h:link>

View file

@ -294,6 +294,12 @@
<version>${org.imixs.data.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.imixs.workflow</groupId>
<artifactId>imixs-data-views</artifactId>
<version>${org.imixs.data.version}</version>
<scope>compile</scope>
</dependency>
<!-- Melman Version Management hinzufügen -->
<dependency>