This blog post, as part 4 of BPM OData blog series, refers to the OData Service in SAP NetWeaver BPM available with SAP NetWeaver 7.3 EHP 1 SP 09 and higher. The features described in this blog post are available with SAP NetWeaver 7.3 EHP 1 SP 12 and higher. Before reading this blog post, it is recommended to read the previous parts of the BPM OData blog series.

Overview

In the previous blog posts, we were considering the following business scenario: a customer record is created, its data is verified by a customer service employee and in the end a financial specialist defines a credit limit for the created customer based on the provided data. In such a credit institution, a lot of these processes can be initiated from day to day. That means that both the customer service employee and the financial specialist need a list of the corresponding tasks to work on. Obviously, each of them should be able to see only the tasks he/she is responsible for and the tasks, which require processing. In addition, a task inbox should support the employee in organizing his/her work. It might be desirable to sort the tasks, for example, by date to ensure that the customer who initiated the process first also gets the institution’s response first. Fortunately, SAP NetWeaver BPM provides a tool called BPM Inbox, which meets the aforementioned requirements, and even more. But what if you have some special requirements, which the standard BPM Inbox cannot fulfill? Don’t worry! You can now implement your own inbox. And this blog post can be used as a starting point.

BPM Tasks OData Service

The previous blog posts about the BPM OData service mostly described a custom task execution UI. The main information on that UI is the task data. Nevertheless, before working with the task data you see information about the task itself such as its title, status and priority. To work on a task it had to be claimed first. That was the time when the BPM Tasks OData service came into play. At that time, the service was briefly described. It supported only the receiving of information about a particular task and the claiming of a task. Now, let us describe this service in more detail because it is the functionality we will use to implement a custom inbox.

Some technical details of the BPM Tasks OData service have already been mentioned in this blog post. Just as a refresher: the BPM Tasks OData service has the name ‘tasks.svc’ and is available under ‘bpmodata’ root URL as all other BPM OData services. This means that the following pattern can be used to represent the service URLs:

http://<host>:<port>/bpmodata/tasks.svc/<OData_resource_path_and_query_options>


Starting from SAP NetWeaver 7.3 EHP 1 SP 12, this service provides more functionality regarding the access to task-related information and task-related operations exposed in the BPM Public API. The service provides the following set of features:

  • Access to a collection of available BPM Tasks
  • Access to a specific BPM Task
  • Claim a BPM Task
  • Release a BPM Task
  • Forward a BPM Task
  • Search for BPM end-users
  • Access to BPM Task Definitions
  • Access to custom attributes of a BPM Task
  • Access to custom actions of a BPM Task
  • Execute a custom action of a BPM Task

To support all the aforementioned operations, the service defines the corresponding entity model. The most important entity in this model is Task. This entity contains all the information about a task including title, status, priority, etc. Task entity has already been mentioned in this blog post when the retrieval of task metadata was described.

As you can see, the BPM Tasks service provides many features and if this blog post would describe all of them in detail, it probably would not have been published yet. Moreover, a custom inbox for the aforementioned business scenario in a credit institution can be implemented using only a sub-set of the service functionalities such as:

Some of these operations have already been described in the previous blog posts. Therefore we will focus only on accessing a collection of tasks and releasing a task, in this blog post. More information about the functionality that is provided by the BPM Tasks OData service, the supported URLs and the entity model can be found in the official documentation.

Accessing a Collection of Tasks

Usually, the main page of an inbox such as a task inbox or an e-mail inbox represents a number of the corresponding items, in our case tasks. In order to get a collection of tasks, the BPM Tasks OData service provides a TaskCollection entity set. The service response for the entity set contains Task entities corresponding to the tasks that are visible to the current user, which means the tasks, for which the current user is a potential or actual owner. Since the potential owner can see all his tasks, including canceled and completed tasks, the service response for the collection of tasks can contain a huge amount of tasks. In such a situation, it will be difficult for the user to find the tasks, which really require processing among the number of already completed tasks. Moreover, providing all the visible tasks for the user takes a lot of processing time and affects the service performance on the server. To prevent the aforementioned situation, all the requests for the TaskCollection entity set must have a $filter OData query option specified. The purpose of the query option is to specify filtering criteria to get only the tasks matching the criteria in the service response.

The table below shows the URL used to access a collection of tasks along with the service response:

HTTP Method GET
URL

…/bpmodata/tasks.svc/TaskCollection?$filter=Status eq ‘READY’ or Status eq ‘RESERVED’

&$orderby=CreatedOn desc&$format=json

Response Body

(simplified)


{
-d: {
  -results: [
   -{
    +__metadata: { … },
    TaskDefinitionName: "Verify Customer Data",
    TaskTitle: "Verify Customer Data",
    Priority: "HIGH",
    Status: "READY",
    CreatedOn: "/Date(1409673092830)/",
    CreatedBy: "Administrator",
    CreatedByName: "Administrator",
    Processor: "",
    StartDeadLine: "/Date(1409674893053)/",
    CompletionDeadLine: "/Date(1409675193053)/",
    ExpiryDate: "/Date(1409676693053)/",
    IsEscalated: false,
    SupportsClaim: true,
    SupportsRelease: false,
    SupportsForward: true,
    SupportsComments: true,
    IsSubstituted: false,
    SubstitutedUser: "",
    +UIExecutionLink: { … }
    +TaskDefinitionData: { … }
    +CustomAttributeData: { … }
    +Description: { … }
    +Comments: { … }
    ...
}








The URL given in the example above requests all ready and reserved tasks for the current user.

If besides filtering the returned tasks should also be ordered by a particular task attribute, the $orderby OData query option can be used. In the example, the returned tasks will be ordered by creation date in a descending order, which is the default ordering for TaskCollection.

Besides $filter and $orderby query options, other OData query options are supported for the TaskCollection entity set. More information about the OData query options can be found at the odata.org website. Note that not all OData query options are supported for the given Task entity; the list of the supported URLs for the entity set can be found in the official documentation.

Releasing a Task

While processing a task, the following situation can occur: a person started to work on a task (i.e. claimed the task) and after that it was decided that the task should be released to be available for another person. In our business scenario, we can have a situation like the following: a financial specialist started to work on customer data to determine a credit limit but after that, he realized that his colleague has a great experience working with customers from this particular area of business and this colleague can determine the credit limit more precisely. In this case, the task should be released by the initial user to be claimed by another colleague.

To release a task, the BPM Tasks OData service provides Release function import, which takes the task’s instance ID as a parameter. Only the user who is working on a task, i.e. the task’s actual owner, can release the task.

The table below shows the URL used to release a task along with the service response:

HTTP Method POST
URL
…/bpmodata/tasks.svc/Release?InstanceID=’02f6e30632b911e485d300000034b6ba’
Request Headers
Authorization Basic dXNlcm5hbWU6cGFzc3dvcmQ=
X-CSRF-Token 781057a9-b96a-468c-b393-981f98292335
Accept application/json

Response Body

(simplified)


{
-d: {
  -results: [
   -{
    +__metadata: { … },
    TaskDefinitionName: "Verify Customer Data",
    TaskTitle: "Verify Customer Data",
    Priority: "HIGH",
    Status: "READY",
    CreatedOn: "/Date(1409673092830)/",
    CreatedBy: "Administrator",
    CreatedByName: "Administrator",
    Processor: "",
    StartDeadLine: "/Date(1409674893053)/",
    CompletionDeadLine: "/Date(1409675193053)/",
    ExpiryDate: "/Date(1409676693053)/",
    IsEscalated: false,
    SupportsClaim: true,
    SupportsRelease: false,
    SupportsForward: true,
    SupportsComments: true,
    IsSubstituted: false,
    SubstitutedUser: "",
    +UIExecutionLink: { … }
    +TaskDefinitionData: { … }
    +CustomAttributeData: { … }
    +Description: { … }
    +Comments: { … }
    ...
}








The service response for the function import contains Task entity, which corresponds to the released task.

Implementing the UI

Having all the necessary information about how to consume the BPM Tasks OData service for the simple business scenario, it is time to implement the UI for the aforementioned operations. This section describes how to create a UI for accessing a collection of tasks, including filtering and ordering of tasks, as well as how to create a UI to release a task.

Implementing a UI for Accessing a Collection of Tasks

A simple UI for accessing a collection of tasks can be implemented in a form of a table with rows corresponding to the available tasks for the current user:

/wp-content/uploads/2014/09/tasks_table_532083.png

As usual, the corresponding SAPUI5 view and the controller have to be implemented. At this step, it is enough to implement only onInit() function in the controller:


onInit : function() {
    var tasksServicePath = "/bpmodata/tasks.svc/";
    var tasksODataModel = new sap.ui.model.odata.ODataModel(tasksServicePath, true);
    tasksODataModel.setDefaultCountMode(sap.ui.model.odata.CountMode.None);
    tasksODataModel.setDefaultBindingMode(sap.ui.model.BindingMode.OneWay);
    this.getView().setModel(tasksODataModel);
}


















In the function, ODataModel to communicate with the BPM Tasks OData service is created. The BPM Tasks OData service allows only reading of Task entities; therefore, sap.ui.model.BindingMode.OneWay binding mode can be used.

This is it! Just some lines of code in the controller are needed to be able to read the collection of tasks.

In order to represent a collection of tasks as a table the following sap.ui.table.Table should be created in the SAPUI5 view:


var tasksTable = new sap.ui.table.Table({title: "Tasks"});
tasksTable.addColumn(new sap.ui.table.Column({
    label: new sap.ui.commons.Label({text: "Task Title"}),
    template: new sap.ui.commons.TextView({text: "{TaskTitle}"})
}));
tasksTable.addColumn(new sap.ui.table.Column({
    label: new sap.ui.commons.Label({text: "Status"}),
    template: new sap.ui.commons.TextView({text: "{Status}"})
}));
tasksTable.addColumn(new sap.ui.table.Column({
    label: new sap.ui.commons.Label({text: "Priority"}),
    template: new sap.ui.commons.TextView({text: "{Priority}"})
}));
tasksTable.addColumn(new sap.ui.table.Column({
    label: new sap.ui.commons.Label({text: "Creation Date"}),
    template: new sap.ui.commons.TextView({text: {
        path: "CreatedOn",
        formatter: function(date) {
            if (date) {
                var dateFormatter = sap.ui.core.format.DateFormat.getDateTimeInstance({style: "medium"});
                return dateFormatter.format(date);
            }
            return "";
        }
    }})
}));
tasksTable.bindRows("/TaskCollection", null, null, null);














The table created by the snippet above is implemented to show title, status, priority and creation date of a particular task. To show values of the corresponding properties of the Task entity type, binding expressions such as {TaskTitle} are used. The table is bound to the TaskCollection entity set using bindRows() method. The first method parameter specifies the OData resource path that is relative to the URL, for which the ODataModel has been created. During the binding, a service request to the following URL will be sent:

http://<host>:<port>/bpmodata/tasks.svc/TaskCollection

Filtering of Tasks on the UI

Having the code to create the table does not mean that the created table will be populated with the tasks. As you remember, it was mentioned that requests to the TaskCollection are not allowed without $filter query option. This means that $filter query option should be defined before sending requests to the OData service.

To specify filtering criteria, SAPUI5 provides sap.ui.model.Filter class. Let us assume that we need to create criteria to get only ready tasks. For that purpose, the following Filter should be created:


var readyStatus = new sap.ui.model.Filter("Status", sap.ui.model.FilterOperator.EQ, "READY");














The constructor requires binding path, operator and filtering value to be provided. In our case, we create a Filter to specify that Status property of Task entity should be equal to READY value. Now it is time to use the created Filter during sending requests to the OData service. For that purpose the created Filter should be specified as a parameter of bindRows() method for the table:


tasksTable.bindRows("/TaskCollection", null, null, [readyStatus]);














As a result, the following URL will be sent to the service during the binding:

http://<host>:<port>/bpmodata/tasks.svc/TaskCollection?$filter=Status eq ‘READY’

Now, during the view rendering, the created table will be populated with ready tasks, for which the current user is a potential owner.

In the previous example, only one Filter has been created. But what if multiple Filters are required? Let us assume that all the ready and reserved tasks for the current user should be shown in the table. For that purpose, a new Filter should be created to indicate that reserved tasks should be returned as well.


var reservedStatus = new sap.ui.model.Filter("Status", sap.ui.model.FilterOperator.EQ, "RESERVED");














After that the created filter should be added to the array of filters that is passed to bindRows() method:


tasksTable.bindRows("/TaskCollection", null, null, [readyStatus, reservedStatus]);














As a result, the following URL will be sent to the service during the binding:

http://<host>:<port>/bpmodata/tasks.svc/TaskCollection?$filter=Status eq ‘READY’ or Status eq ‘RESERVED’

It shows that if multiple Filters are provided for the bindRows() method, they are connected with OR operator in the $filter expression. In case the Filters should be connected with AND operator, for example, to filter tasks by status and priority, a combined Filter should be created:


var readyStatus = new sap.ui.model.Filter("Status", sap.ui.model.FilterOperator.EQ, "READY");
var highPriority = new sap.ui.model.Filter("Priority", sap.ui.model.FilterOperator.EQ, "HIGH");
var filterByStatusAndPriority = new sap.ui.model.Filter([readyStatus, highPriority], true);














The last argument of the Filter constructor indicates whether the specified Filters should be connected with AND operator. Providing such a Filter for bindRows() method:


tasksTable.bindRows("/TaskCollection", null, null, [filterByStatusAndPriority]);














leads to the following URL sent to the service

http://<host>:<port>/bpmodata/tasks.svc/TaskCollection?$filter=Status eq ‘READY’ and Priority eq ‘HIGH’

Ordering of Tasks on the UI

In the previous section, filtering of tasks was described. But what if the returned tasks should also be ordered by specific attributes? For that purpose, SAPUI5 provides sap.ui.model.Sorter class. Let us assume that we need to order tasks by the creation date to show the latest tasks at the beginning of the table. This means that the ordering by the creation date should be descending. For that purpose, the following Sorter should be created:


var sorter = new sap.ui.model.Sorter("CreatedOn", true);














The last parameter of the constructor indicates whether the sorting should be descending. To apply the Sorter it should be specified as a parameter for the bindRows() method:


tasksTable.bindRows("/TaskCollection", null, sorter, [readyStatus, reservedStatus]);














As a result, the following URL will be sent to the service during the binding:

http://<host>:<port>/bpmodata/tasks.svc/TaskCollection?$filter=Status eq ‘READY’ or Status eq ‘RESERVED’&$orderby=CreatedOn desc

Implementing a UI to Release a Task

As it was mentioned before, to release a task, POST HTTP request should be sent to the service to call Release function import. For that purpose, a new function should be implemented in the SAPUI5 controller:


release : function(taskInstanceId) {
    if (taskInstanceId) {
        var urlParameters = {
            "InstanceID" : decodeURIComponent(taskInstanceId)
        };
        var functionParameters = {};
        functionParameters.method = "POST";
        functionParameters.urlParameters = urlParameters;
        functionParameters.success = function() {
            alert("Task released!");
        };
        functionParameters.error = function() {
            alert("Release failed!");
        };
        var tasksODataModel = this.getView().getModel();
        tasksODataModel.callFunction("Release", functionParameters);
    }
}

















The function takes an encoded task instance ID as a parameter. That is exactly how it is returned by the BPM Tasks OData service. To call a function import, ODataModel provides the callFunction() function. The function requires two input parameters: function import name and configuration object to specify how the function import should be called. The configuration object is used to specify the HTTP method that should be used to call the function import, a map of the function import URL parameters and functions that should be called in case the function import call is succeeded or failed. Once the specified function import is called, callFunction() function refreshes the ODataModel. This means that once the task is released the corresponding row in the table of tasks on the UI will be updated automatically.

Conclusion

This part has shown the implementation of a UI for accessing a collection of tasks and releasing a task. The technical details of this functionality have been described and it was shown how to consume the corresponding operations from SAPUI5. Having UIs for working with task data, task metadata and claiming a task along with the UIs described in this blog post, it is possible to implement a custom inbox that combines all the mentioned features in one place. Such an inbox can also be enhanced by adding support of other task-related operations that are supported by the BPM Tasks OData service. More information about other functionalities that are provided by the BPM Tasks OData service can be found in the official documentation.

To report this post you need to login first.

7 Comments

You must be Logged on to comment or reply to a post.

  1. VINCENZO TURCO

    Hi Vitaly,

    great blog! I’m trying to achieve something similar, consuming the odata bpm service through a Java client (Olingo). Unfortunately, the data returned from the odata service for the edm:dateTime looks invalid like:

    StartDeadLine: “”

    CompletionDeadLine: “”

    ExpiryDate: “”

    Unfortunately the empty string is not valid edm:DateTime and parsing fails.

    The right value would be <ExpiryDate null=”true” />

    Any idea why this happens?

    Thanks

    Vincenzo

    (0) 
  2. Dinesh Chandra

    Hi Vitaly,

    Many thanks for well explained blog !!!

    I am able to fetch all the task in the custom Inbox as explained in the blog.

    Now i am looking for the task URL so that on selecting a particular task from Inbox,I can open the task UI with all the data of the task.

    Please guide me… how can we achieve this UWL like functionality in our custom Inbox?

    Thanks,

    Dinesh Chandra

    (0) 
    1. Vitaly Yarmolik Post author

      Hi Dinesh,

      To get URL for the task execution UI, you can send request to one of the following URIs:

      • …/bpmodata/tasks.svc/TaskCollection(‘<task-instance-id>’)/UIExecutionLink The service response for such a request will contain only an entity for UIExecution entity type
      • …/bpmodata/tasks.svc/TaskCollection(‘<task-instance-id>’)?$expand=UIExecutionLink The service response will contain both, an entity for the task as well as the nested entity for UI execution link

      Best Regards,

      Vitaly

      (0) 
  3. Antony FERMINUS

    Hello,

    I am folowing the tutorial.

    When i use the onInit code the line tasksODataModel.setDefaultCountMode(sap.ui.model.odata.CountMode.None);   gives me error so set it in comments. then i get the table view with several rows but the the values are empty.

    when i enable the countMode i get the error

    TypeError: sap.ui.model.odata.CountMode is undefined


    Can anyone give me some ideas to solve this issue?


    Thanks in advance.


    Regards,

    Antony.odataError.PNG

    (0) 

Leave a Reply