neue Ansicht Businesspartner
This commit is contained in:
parent
a65ae59950
commit
c15efc106c
6 changed files with 1664 additions and 0 deletions
|
|
@ -0,0 +1,156 @@
|
|||
/*******************************************************************************
|
||||
* 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
|
||||
* as 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);
|
||||
dataSets.put(options, dataSet);
|
||||
}
|
||||
}
|
||||
return dataSet;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,260 @@
|
|||
package org.imixs.workflow.office.forms;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
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 DataViewService dataViewService;
|
||||
|
||||
private ItemCollection workitem;
|
||||
|
||||
public DataViewSectionDataSet(String dataViewName, ItemCollection workitem, DataViewService dataViewService) {
|
||||
this.dataViewName = dataViewName;
|
||||
this.workitem = workitem;
|
||||
this.dataViewService = dataViewService;
|
||||
|
||||
dataViewDefinition = dataViewService.loadDataViewDefinition(dataViewName);
|
||||
|
||||
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);
|
||||
|
||||
loadData();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||
xmlns:f="http://java.sun.com/jsf/core" xmlns:c="http://java.sun.com/jsp/jstl/core"
|
||||
xmlns:h="http://java.sun.com/jsf/html" xmlns:i="http://java.sun.com/jsf/composite/imixs"
|
||||
xmlns:marty="http://java.sun.com/jsf/composite/marty">
|
||||
|
||||
<!-- preload view options -->
|
||||
|
||||
|
||||
<h:panelGroup styleClass="imixs-view imixs-search" layout="block" id="dataview_result_id">
|
||||
<ui:param name="dataViewDataSet" value="#{dataViewSectionController.loadDataSet(options)}" />
|
||||
<ui:param name="searchPage"
|
||||
value="#{(dataViewDataSet.getPageIndex()+1)}/#{(dataViewDataSet.getTotalPages())}" />
|
||||
<table class="imixsdatatable imixs-orderitems">
|
||||
<tr>
|
||||
<ui:repeat value="#{dataViewDataSet.getViewItemDefinitions()}" var="columnDef">
|
||||
<th>#{columnDef.item['item.label']}</th>
|
||||
</ui:repeat>
|
||||
</tr>
|
||||
<ui:repeat value="#{dataViewDataSet.data}" var="record">
|
||||
<tr>
|
||||
<ui:repeat value="#{dataViewDataSet.getViewItemDefinitions()}" var="columnDef">
|
||||
<td>
|
||||
<ui:fragment rendered="#{columnDef.item['item.type'] eq 'xs:anyURI'}">
|
||||
<h:link outcome="/pages/workitems/workitem.xhtml?faces-redirect=true"
|
||||
styleClass="imixs-viewentry-main-link">
|
||||
<h:outputText value="#{record.item[columnDef.item['item.name']]}" />
|
||||
<f:param name="id" value="#{record.item['$uniqueid']}" />
|
||||
</h:link>
|
||||
</ui:fragment>
|
||||
|
||||
<ui:fragment rendered="#{columnDef.item['item.type'] eq 'xs:string'}">
|
||||
<h:outputText value="#{record.item[columnDef.item['item.name']]}" />
|
||||
</ui:fragment>
|
||||
|
||||
<ui:fragment rendered="#{columnDef.item['item.type'] eq 'xs:date'}"
|
||||
styleClass="align-center">
|
||||
<h:outputText value="#{record.item[columnDef.item['item.name']]}">
|
||||
<f:convertDateTime timeZone="#{message.timeZone}" type="both"
|
||||
pattern="#{message.datePatternShort}" />
|
||||
</h:outputText>
|
||||
</ui:fragment>
|
||||
|
||||
<ui:fragment rendered="#{columnDef.item['item.type'] eq 'xs:double'}"
|
||||
styleClass="align-right">
|
||||
<h:outputText value="#{record.item[columnDef.item['item.name']]}">
|
||||
<f:convertNumber minFractionDigits="2" locale="de" />
|
||||
</h:outputText>
|
||||
</ui:fragment>
|
||||
<ui:fragment rendered="#{columnDef.item['item.type'] eq 'xs:float'}"
|
||||
styleClass="align-right">
|
||||
<h:outputText value="#{record.item[columnDef.item['item.name']]}">
|
||||
<f:convertNumber minFractionDigits="2" locale="de" />
|
||||
</h:outputText>
|
||||
</ui:fragment>
|
||||
|
||||
<ui:fragment rendered="#{columnDef.item['item.type'] eq 'xs:dateTime'}">
|
||||
<f:facet name="header">#{columnDef.item['item.label']}</f:facet>
|
||||
<h:outputText value="#{record.item[columnDef.item['item.name']]}">
|
||||
<f:convertDateTime timeZone="#{message.timeZone}" type="both"
|
||||
pattern="#{message.dateTimePatternShort}" />
|
||||
</h:outputText>
|
||||
</ui:fragment>
|
||||
</td>
|
||||
</ui:repeat>
|
||||
</tr>
|
||||
</ui:repeat>
|
||||
</table>
|
||||
<!-- navigation -->
|
||||
<div
|
||||
style="background-color: #f9f9f9; font-size:0.85rem; border-bottom: 1px solid var(--input-border);border-left: 1px solid var(--input-border);border-right: 1px solid var(--input-border); height: 30px; display: flex; justify-content: space-between; align-items: center;padding: 5px 10px;">
|
||||
<span style="display: flex; align-items: center;gap: 10px;">
|
||||
<h:commandLink title="Excel Export" rendered="false" actionListener="#{dataGroupController.export()}"
|
||||
onclick="handleSubmit(this)">
|
||||
<i class="fa-solid fa-file-csv"></i>
|
||||
</h:commandLink>
|
||||
<h:commandLink title="Excel Export" rendered="#{dataViewDataSet.hasPoiExport()}"
|
||||
actionListener="#{dataViewDataSet.export()}" onclick="handleSubmit(this)">
|
||||
<span class="typcn typcn-vendor-microsoft"></span>
|
||||
</h:commandLink>
|
||||
</span>
|
||||
<span style="display: flex; align-items: center; gap: 10px;">
|
||||
<h:outputText value=" #{(dataViewDataSet.pageIndex+1)} / #{dataViewDataSet.totalPages}" />
|
||||
<h:commandLink value="" action="#{dataViewDataSet.back()}">
|
||||
<f:ajax render="dataview_result_id" />
|
||||
<i class="fa-solid fa-backward" style=" #{(dataViewDataSet.pageIndex == 0)?'opacity:0.5':''}"></i>
|
||||
</h:commandLink>
|
||||
<h:commandLink value="" action="#{dataViewDataSet.forward()}">
|
||||
<f:ajax render="dataview_result_id" />
|
||||
<i class="fa-solid fa-forward" style=" #{(dataViewDataSet.endOfList)?'opacity:0.5':''}"></i>
|
||||
</h:commandLink>
|
||||
</span>
|
||||
</div>
|
||||
</h:panelGroup>
|
||||
<script type="text/javascript">
|
||||
/*<![CDATA[*/
|
||||
function handleSubmit(event) {
|
||||
// remove ajax loader 1sec after trigger excel export
|
||||
setTimeout(function () {
|
||||
document.body.classList.remove('loading');
|
||||
}, 2000);
|
||||
}
|
||||
/*]]>*/
|
||||
</script>
|
||||
|
||||
</ui:composition>
|
||||
BIN
templates/invoice_outbound_de_template.xlsx
Normal file
BIN
templates/invoice_outbound_de_template.xlsx
Normal file
Binary file not shown.
BIN
templates/invoice_outbound_en_template.xlsx
Normal file
BIN
templates/invoice_outbound_en_template.xlsx
Normal file
Binary file not shown.
1143
workflow/businesspartner-de-1.0.4.bpmn
Normal file
1143
workflow/businesspartner-de-1.0.4.bpmn
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue