View source | Discuss this page | Page history | Printable version   

How to create a Standard Process Definition

Contents

Introduction

Standard UI pattern of Process Definition allows to create Parameter Windows defined in Application Dictionary, the UI for this windows is generated on demand so once defined those parameters, developer only needs to take care about process implementation.

This how to will add a new Standard Process Definition and create a menu entry to invoke it.

The implementation requires development experience. See the following concept pages for background information on action handlers and javascript development:

It also makes sense to study the following page: Openbravo_3_Architecture.

Bulbgraph.png   This feature is available from 3.0MP20

Example Module

This howto is supported by an example module which shows examples of the code shown and discussed.

The code of the example module can be downloaded from this mercurial repository: https://code.openbravo.com/erp/mods/org.openbravo.client.application.examples/

The example module is available through the Central Repository (See 'Client Application Examples'), for more information see the Examples Client Application project page.

Steps to implement the Process

Overview

Standard Process Definition processes take advantage of the same foundation concepts in the Application Dictionary, allowing to define parameters for the process as meta data that generates the UI when it is required without need of generate code, compile nor restart tomcat to apply the changes when developing.

This example process will have the following parameters:

When Done button is clicked the process is executed:

Implementation

Defining the Process

Adding Parameters

Adding it to the Menu

Adding a process to the menu allows to open it from menu as a new tab.

Java Implementation

As mentioned earlier you should be confident with the concept of an action handler.

In the case of a Process Definition action handler, you must extend from BaseProcessActionHandler and implement the doExecute method.

 
/*
 *************************************************************************
 * The contents of this file are subject to the Openbravo  Public  License
 * Version  1.1  (the  "License"),  being   the  Mozilla   Public  License
 * Version 1.1  with a permitted attribution clause; you may not  use this
 * file except in compliance with the License. You  may  obtain  a copy of
 * the License at http://www.openbravo.com/legal/license.html
 * Software distributed under the License  is  distributed  on  an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific  language  governing  rights  and  limitations
 * under the License.
 * The Original Code is Openbravo ERP.
 * The Initial Developer of the Original Code is Openbravo SLU
 * All portions are Copyright (C) 2013 Openbravo SLU
 * All Rights Reserved.
 * Contributor(s):  ______________________________________.
 ************************************************************************
 */
package org.openbravo.client.application.examples;
 
import java.math.BigDecimal;
import java.util.Map;
 
import org.apache.log4j.Logger;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.openbravo.client.application.process.BaseProcessActionHandler;
import org.openbravo.dal.service.OBDal;
import org.openbravo.erpCommon.utility.OBMessageUtils;
import org.openbravo.model.common.order.Order;
 
public class StandardProcessActionHandler extends BaseProcessActionHandler {
  private static final Logger log = Logger.getLogger(StandardProcessActionHandler.class);
 
  @Override
  protected JSONObject doExecute(Map<String, Object> parameters, String content) {
    try {
      JSONObject result = new JSONObject();
 
      JSONObject request = new JSONObject(content);
      JSONObject params = request.getJSONObject("_params");
 
      // Do validations on param values
      double min = params.getDouble("min");
      double max = params.getDouble("max");
 
      if (max < min) {
        // In case validations are not satisfied, show an error message and allow user to fix
        // parameters
        result.put("retryExecution", true);
 
        JSONObject msg = new JSONObject();
        msg.put("severity", "error");
        msg.put(
            "text",
            OBMessageUtils.getI18NMessage("OBEXAPP_MinGtMax", new String[] { Double.toString(min),
                Double.toString(max) }));
        result.put("message", msg);
        return result;
      }
 
      // Execute process and prepare an array with actions to be executed after execution
      JSONArray actions = new JSONArray();
 
      // 1. Sum amounts of all orders and show a message in process view
      JSONArray orders = params.getJSONArray("orders");
      BigDecimal totalAmnt = BigDecimal.ZERO;
      for (int i = 0; i < orders.length(); i++) {
        String orderId = orders.getString(i);
        Order order = OBDal.getInstance().get(Order.class, orderId);
        totalAmnt = totalAmnt.add(order.getGrandTotalAmount());
      }
      JSONObject msgTotal = new JSONObject();
      msgTotal.put("msgType", "info");
      // XXX: these two messages should be translatable, like OBEXAPP_MinGtMax above
      msgTotal.put("msgTitle", "Selected Orders");
      msgTotal.put("msgText", "Total amount: " + totalAmnt.toString());
 
      JSONObject msgTotalAction = new JSONObject();
      msgTotalAction.put("showMsgInProcessView", msgTotal);
 
      actions.put(msgTotalAction);
 
      // 2. If business partner is not null, open it in BP window and show a message in new tab
      if (!params.isNull("bp")) {
        String bpId = params.getString("bp");
        JSONObject recordInfo = new JSONObject();
        recordInfo.put("tabId", "220");
        recordInfo.put("recordId", bpId);
        recordInfo.put("wait", true);
 
        JSONObject openTabAction = new JSONObject();
        openTabAction.put("openDirectTab", recordInfo);
 
        actions.put(openTabAction);
 
        JSONObject msgInBPTab = new JSONObject();
        msgInBPTab.put("msgType", "success");
        msgInBPTab.put("msgTitle", "Process execution");
        msgInBPTab.put("msgText", "This record was opened from process execution");
 
        JSONObject msgInBPTabAction = new JSONObject();
        msgInBPTabAction.put("showMsgInView", msgInBPTab);
 
        actions.put(msgInBPTabAction);
      }
 
      result.put("responseActions", actions);
 
      return result;
    } catch (JSONException e) {
      log.error("Error in process", e);
      return new JSONObject();
    }
  }
}
Response

ActionHandlers return a JSONObject with the actions to be performed after execution.

Validations

It is possible to do validations in the backend before executing the actual process, when these validations are not satisfied, a message can be shown in the UI to allow the user to fix the problematic values.

When validations are not satisfied retryExecution: true property is included in the response. This allows the user to fix data and resubmit again. Additionally, a message can be added to show more information about the issue. The response would look similar to this:

 
{
  "retryExecution": true,
  "message": {
    "severity": "error",
    "text": "Min value (80.0) cannot be greater than Max value (10.0)"
  }
}
Returning Several Actions

After executing the process, it is possible to perform a series of actions. Additional information can be found here. The response should look like:

 
{
  "responseActions": [{
    "showMsgInProcessView": {
      "msgType": "info",
      "msgTitle": "Selected Orders",
      "msgText": "Total amount: 3020482.63"
    }
  }, {
    "openDirectTab": {
      "tabId": "220",
      "recordId": "A6750F0D15334FB890C254369AC750A8",
      "wait": true
    }
  }, {
    "showMsgInView": {
      "msgType": "success",
      "msgTitle": "Process execution",
      "msgText": "This record was opened from process execution"
    }
  }, {
    "refreshGrid": {
    }
  }, {
    "refreshGridParameter": {
      "gridName": "gridParameterName"
    }
  }]
}
Bulbgraph.png   This feature is available starting from 3.0PR17Q2.

The getResponseBuilder() method is available for classes extending BaseProcessActionHandler. This method returns a helper that can be used to build the result of the process with the desired standard response actions in an easy way. For example:

 
  @Override
  protected JSONObject doExecute(Map<String, Object> parameters, String content) {
    ...
    ...
    return getResponseBuilder()
        .showMsgInProcessView(MessageType.INFO, "Message Title", "This is a sample message")
        .openDirectTab("220", false).build();
  }

Testing the Process

After compiling and deploying (because a new java class is added, note this is not needed in case of just editing/adding paramters).

There is a new entry in the menu: Example Param Process, this entry opens the parameter window where all defined parameters are shown and a Done button is presented to submit values set for them. When the process is executed:

ParamWindowExecuted.png

Advanced Topics

Invoke the Process from a Tab

Standard Process Definition processes can be opened as a tab from the menu or as a modal popup from a button in a tab. This second option can be achieved by adding an extra column to the table used in the tab. More details on how to do it can be found here.

Read Only and Display Logic

Bulbgraph.png   Read Only logic is available from 3.0MP25

Parameters in Process Definition support display and read only logic. This allows to show or hide and to make editable or read only parameters based on values entered for other parameters.

Subordinated Combos

Bulbgraph.png   Subordinated Combos are available from 3.0MP25

The data that can be selected within a combo (selector) can be restricted based on values other parameters take using "Validation Rules". Logic of these validations is a HQL that is appended to its datasource. This is written in javascript being posible to use OBBindings, in the same way default value is written.

Parameter Grouping

Bulbgraph.png   Parameter Grouping logic is available from 3.0MP25

It is possible to do groups of parameters in the UI, by using the Field Group property when defining the paramter.

Showing results in the process window itself

It is possible to show the result of a process directly in the process window itself. This makes sense if the parameter section is small and you want to display directly.


Process window example.png


To accomplish this the data/json object returned from the server handler should set this parameter 'showResultsInProcessView' to true. It also makes sense to 'retryExecution' parameter to true:

{
  "retryExecution": true,
  "showResultsInProcessView": true
}

The called java script method gets an object with a _processView property which refers back to the overall process view. From the process view you can get to the resultLayout to show the result. The resultLayout is a Smartclient HLayout.

For example, a return action:

OB.Utilities.Action.set('openSaikuReport', function(paramObj) {
  var i, queries = paramObj.queries,
      processView = paramObj._processView,
      mainLayout = processView.resultLayout, reportView;
 
  reportView = isc.OBANALY_ShowSaikuReport.create({
    parameters: paramObj,
    queries: queries
  });
  mainLayout.addMember(reportView);

Placing a parameter in a particular column

Bulbgraph.png   This feature is available from PR14Q3

The Column Number field of the Parameter tab allows you to specify the column where the parameter should be placed. Grid parameters use always the four columns of the form, so this field does not apply to them.

Invoking a client side validation before calling the action handler

Bulbgraph.png   This feature is available from PR14Q3

The Client Side Validation field of the Process Definition tab allows you to define a function that will be executed before the request to the action handler is done. You can use this function to do client side validations.

This function must accept 3 parameters:

For instance:

OB.Utilities.TestClientSideValidation = function (view, actionHandlerCall, failureCallback) {
  var minNumber, maxNumber;
  minNumber = view.theForm.getItem('min_number').getValue();
  maxNumber = view.theForm.getItem('max_number').getValue();
  if (maxNumber >= minNumber) {
    // only execute the callback if the form values pass the validation
    actionHandlerCall();
  } else {
    failureCallback();
  }

In addition, starting from version PR17Q3 client side validation functions support a fourth parameter that contains additional information, like the pressed button:

OB.Utilities.TestClientSideValidation = function (view, actionHandlerCall, failureCallback, additionalInfo) {
  if (additionalInfo.buttonValue === 'OK') {
    // execute validations related to the 'OK' button
  } else {
    // do another validations
  }

To learn how to define new buttons in a standard process definition window, see here.

From PR20Q3 additional information can be added to the payload the process will receive. For example this code would add a new parameter named myParam:

 
  view.externalParams = { myParam:'value' };

Invoking a function when a non-grid parameter is changed

Bulbgraph.png   This feature is available from PR14Q3

The On Change Function field of the Parameter tab allows you to define a function that will be executed when a non-grid parameter is updated, after the parameter loses its focus. This function can be used to do validations or to implement client side callouts, among other things.

The function must accept four parameters:

How to set the value of non-grid parameters programmatically

It is possible to execute an On Change Function, besides when the parameter loses its focus, when setting the parameter value programmatically.

When setting the value of a parameter from code, it is recommended to use the setValueProgrammatically() function. This way, if the parameter has an On Change Function, it will be executed after setting the parameter value.

 
  var issotrx = form.getItem('issotrx');
  // Set the value for the item
  // If the 'issotrx' parameter has an 'On Change Function' it will be executed also
  issotrx.setValueProgrammatically('Y');
Bulbgraph.png   The setValueProgrammatically() method is available from PR16Q1

Invoking a function when all the non-grid parameters have been initialized

Bulbgraph.png   This feature is available from PR14Q3

The On Load Function field of the Process Definition tab allows you to define a function that will be executed once the parameters have been initialized.

Invoking a function when the process needs to be refreshed

Bulbgraph.png   This feature is available from PR14Q4

The On Refresh Function field of the Process Definition tab allows you to define a function that will be executed when the parameter window refresh action be invoked.

For example, if the process has a child-process, once the child-process finishes, it will invoke a refresh of the parent process.

Since each process has its particularities, a custom refresh function should be defined in case the process be susceptible of being refreshed/reloaded.

The function must accept, at least, one parameter:

Invoking when a grid parameter is loaded for the first time

Bulbgraph.png   This feature is available from PR14Q3

The initialization of the grid parameters is done asynchronously, so when the general onLoadFunction is invoked, it is not certain whether all the grid parameters have been loaded with their initial data. If you need to execute some code right after a grid is loaded for the first time, you should use the On Grid Load Function field. The function used here must accept one parameter, the grid itself.

For example:

OB.Utilities.TestOnGridLoad = function (grid) {
  var nRecordsReceived = grid.getData().getLength(),
      messageBar = grid.view.messageBar;
  messageBar.setMessage('info', 'The grid has been loaded with ' + nRecordsReceived + ' records');
}

Specifying the number of rows displayed in a grid parameter

Bulbgraph.png   This feature is available from PR14Q3

You can set the number of rows that should be shown at the first time in a grid parameter using the Number of Displayed Rows field. This field is used just for setting the height of the grid, if the grid has actually more rows than the Number of Displayed Rows, a scrollbar will be shown. The default value for this field is 5.

It is not possible to define the colspan of the grid parameters, because they always use the four available columns of the form.

Defining a display logic for the fields of a grid parameter

Bulbgraph.png   This feature is available from PR14Q3

The Display Logic for Grid Column field in the Field tab allows to define a display logic for the fields of grid parameters.

For instance, suppose that you have defined a parameter window with two parameters:

Let's suppose the grid has some fields that should be displayed only if the Show Advanced Column flag is checked. The Display Logic for Grid Column field of these fields should be set to:

@showAdvancedColumns@='Y'


Specifying a default value for the filter of a parameter grid field

Bulbgraph.png   This feature is available from PR14Q3

The Default Filter Expression field of the Field tab allows to define a default value for the filter of a field. This default value can be a constant, depend from another parameter or use OBBindings.

Hiding the parameter name of a grid parameter

Bulbgraph.png   This feature is available from PR14Q3

Although it is possible to define several grid parameters in a parameter window, it is likely that most of the times there will be at most one (for instance in pick and execute windows). In those cases, you might consider not showing the name of the grid parameter. You can do this by unckecking the flag 'Show Title' in the Parameter tab.

Adding new buttons

By default, process definitions have a single "Done" button (and a "Cancel" one in case they are shown in a popup from a standard window). It is possible to change that button or to add new ones.

To do it you can:

  1. Create a new Reference with Button List as Parent Reference. In the List Reference add as many records as buttons to be displayed in the process. The name of these elements will be seen in the button's label whereas the Search Key is the value that will be sent to the Handler in the backend within the _buttonValue field.
  2. Add a new parameter to the process with Button List as reference and the new reference just created as Reference Search Key. Note there should be, at most, only one parameter of Button List type.

Multi Record Process

A standard process can be defined as multi record process to be able to execute it for more than one record.

Bulbgraph.png   This feature is available starting from 3.0PR17Q2.

Uploading files

Bulbgraph.png   This feature is available starting from 3.0PR22Q2.

Process definitions can receive files as parameters. To do that, add a 'Process File upload' Reference as a process parameter. This parameter will show a file upload element in the process form where you can upload a single file.

Screenshot from 2021-11-26 13-16-57.png

By default, files uploaded using this component are limited to be 10MB at most. To override this configuration, you can set the Preference Maximum Process File Upload size (MB) and change this value. File size validation will be performed both client-side and server-side.

Once uploaded a file, file contents and additional data will be available as a parameter entry in the doExecute(parameters, content) function in the Process Event Handler. The entry corresponding to the file upload will contain a Map with the following information:

Map<String, Object> params = (Map<String, Object>) parameters.get(paramName);
 
String fileName = params.get(PARAM_FILE_NAME); // The file name
InputStream content = params.get(PARAM_FILE_CONTENT); // The content of the file as an Stream
Long size = params.get(PARAM_FILE_SIZE); // The file size in bytes


Downloading files

Bulbgraph.png   This feature is available starting from 3.0PR23Q3.

Process definition has the ability to generate and download a file.

In this case of Process Definition, you must extend from FileExportActionHandler and implement the generateFileToDownload and getDownloadFileName methods.

 
 
/**
 * Action handler example to export a file from a process definition
 */
public class ExportFileExample extends FileExportActionHandler {
  private static final String FILE_PREFIX = "Test";
  private static final String FILE_EXTENSION = ".txt";
 
  @Override
  protected Path generateFileToDownload(Map<String, Object> parameters, JSONObject data)
      throws IOException, JSONException {
    String tmpFileName = UUID.randomUUID().toString() + FILE_EXTENSION;
    File file = new File(ReportingUtils.getTempFolder(), tmpFileName);
    try (FileWriter outputfile = new FileWriter(file)) {
      outputfile.write("Hello World!");
    }
    return file.toPath();
  }
 
  @Override
  protected String getDownloadFileName(Map<String, Object> parameters, JSONObject data) {
    return FILE_PREFIX + FILE_EXTENSION;
  }
}

If the process definition is launched from a button in a standard window with a header and lines and it is not configured as multi-record, the generated file will be attached to the header by default.

This behavior can be modified overriding the method uploadAttachment.

 
 
 @Override
  protected void uploadAttachment(Path originalFile, Map<String, Object> parameters,
      JSONObject data) throws IOException, JSONException {
 
  }
 

See example: ExportFileExample

Limitations

References

Currently, not all references available in Standard windows are available in Process Definition. The following ones cannot be used as parameters:

Some other ones are supported starting from an older release:

UI Logic

Callouts are not implemented for parameters.


Migrating old Processes

Starting from 3.0PR14Q3 Process Definitions support several parameters. Previous to this release, they supported either no parameter or a single grid parameter. In order to implement this support, the way grid parameter value is sent to backend was changed and a new Compatibility with Legacy Grids flag was added, all processes defined before 3.0PR14Q3 were defaulted to be compatible and processes defined afterwards are not by default.

To migrate grid legacy compatible processes to new format, set Compatibility with Legacy Grids to false and depending on the case:

Old code New code
     
  JSONObject gridInfo = new JSONObject(content);
 
 
 
 
  JSONArray gridSelection = gridInfo.getJSONArray("_selection");
  JSONArray gridAllRows = gridInfo.getJSONArray("_allRows");
  
  JSONObject params = new JSONObject(content).getJSONObject("_params");
 
  // Replace here gridColumnName with the actual DB Column Name for your grid parameter
  JSONObject gridInfo = params.getJSONObject("gridColumnName");
 
  JSONArray gridSelection = gridInfo.getJSONArray("_selection");
  JSONArray gridAllRows = gridInfo.getJSONArray("_allRows");

Retrieved from "http://wiki.openbravo.com/wiki/How_to_create_a_Standard_Process_Definition"

This page has been accessed 42,151 times. This page was last modified on 31 May 2023, at 11:07. Content is available under Creative Commons Attribution-ShareAlike 2.5 Spain License.