Skip to Content
Technical Articles

Controlling CAP actions on Fiori UI

Motivation

I’m going develop a call center app (just for demonstration purpose), where incoming queries are posted into the system. When an operator receives a new query, they set its status to “started”. When the query is resolved, they set its status to “closed”.

The key here is how to enable/disable action buttons according to the query’s status.
I have achieved this using CAP annotations and service implementations.

 

Actions and Functions

Actions and Functions allow encapsulating logic for modifying or requesting data that goes beyond simple CRUD. In the OData specification document, Functions and Actions are defined as below.

Actions

Actions are operations exposed by an OData service that MAY have side effects when invoked. Actions MAY return data but MUST NOT be further composed with additional path segments. 

Functions

Functions are operations exposed by an OData service that MUST return data and MUST have no observable side effects.

Simply put, Actions can be used to modify data, while Functions are used to get specific (set of) data.

Bound Actions/Functions and Unbound Actions/Functions

There are two types of Actions and Functions: Bound Actions/Functions and Unbound Actions/Functions.
Bound Actions/Functions are associated with individual entities. Bound Actions/Functions receive key fields of the entity which they are bound to, and you can use those keys to get detail of the entity or manipulate the entity.
Unbound Actions/Functions are not associated with particular entity.
How to define Actions and Functions is described in the cap document.

 

Development

In this section, I’m going to show you how to implement bound actions and enable/disable the actions according to the query’s status.

1. Schema definition

I have defined one entity for inquires and two for value help (category & status).

namespace demo.callcenter;
using { cuid, managed, sap.common } from '@sap/cds/common';

entity Inquiries: cuid, managed{
    category: Association to Category @title: 'Category';
    inquiry: String(1000) @title: 'Inquiry';
    startedAt: DateTime @title: 'Started At'; 
    answer: String(1000) @title: 'Answer';
    status: Association to Status @title: 'Status';
    satisfaction: Integer @title: 'Satisfacton' 
        enum {
            veryUnsatisfied = 1;
            unsatisfied = 2;
            neutral = 3;
            satisfied = 4;
            verySatisfied = 5
        };
    virtual startEnabled: Boolean;
    virtual closeEnabled: Boolean;    
}

entity Category: common.CodeList {
    key code   : String(1)
}

entity Status: common.CodeList {
    key code: String(1)
}

 

The important part is below. These are virtual elements for controlling actions.
As Oliver Klemenz suggested in his comment, virtual elements don’t create fields on persistence layer, thus suit the purpose.

I’ll implement the logic for filling these fields later.

    virtual startEnabled: Boolean;
    virtual closeEnabled: Boolean;    

 

An inquiry has 3 statuses. These statuses will be used for controlling actions.

  1. New
  2. In Process
  3. Closed

 

2. Service definition

I have defined two bound actions: start and close.

using { demo.callcenter as call } from '../db/schema';

service CallCenterService {
    entity Inquiries as projection on call.Inquiries
    actions {
        @sap.applicable.path: 'startEnabled'
        action start();
        @sap.applicable.path: 'closeEnabled'
        action close(satisfaction: Integer);
    }   
}

 

The important part is the following annotation. Based on the filed specified here, the service will determine if the action should be enabled. I fond this in UI5 documentation.

@sap.applicable.path: 'startEnabled'

 

3. UI annotations

Below part is relevant to showing actions on Fiori UI.

        LineItem: [
            { $Type: 'UI.DataFieldForAction', Action: 'CallCenterService.EntityContainer/Inquiries_start', Label: 'Start',  Visible, Enabled},
            { $Type: 'UI.DataFieldForAction', Action: 'CallCenterService.EntityContainer/Inquiries_close', Label: 'Close',  Visible, Enabled},
            { Value: category_code },
            { Value: inquiry },
            { Value: status_code },
            { Value: startedAt },
            { Value: satisfaction },
            { Value: createdAt },
            { Value: modifiedAt }
        ]

 

4. Service implementation

In the service implementation, I have three methods.

The first one is for toggling the actions. As defined in the service definition, if “startEnabled” is true, then the start action will be enabled. I want the start action to be enabled only for new inquires (status: “1”). Similarly, I want the close action to be enabled only for active inquires (status: not closed(3)).

const cds = require('@sap/cds')

module.exports = function () {
    const { Inquiries } = cds.entities 

    this.after('READ', 'Inquiries', (each) => {
        //for new inquires only
        if (each.status_code === '1' ) { 
            each.startEnabled = true
        }
        //for active inquires only
        if (each.status_code !== '3') {
            each.closeEnabled = true
        }
    })      
    ...
}

 

The second and the third methods are action implementations. For bound actions, entity’s key is passed via request parameters.

    this.on ('start', async (req)=> {
        const id = req.params[0]
        const n = await UPDATE(Inquiries).set({ 
            status_code:'2',
            startedAt: Date.now()
        }).where ({ID:id}).and({status_code:'1'})
        n > 0 || req.error (404) 
    })

    this.on('close', async (req) => {
        const id = req.params[0]
        const { satisfaction } = req.data
        const n = await UPDATE(Inquiries).set({ 
            satisfaction: satisfaction,
            status_code: '3'
        }).where ({ID:id}).and({status_code:{'<>':'3'}})
        n > 0 || req.error (404)        
    })

 

I can get the key with the following code and use it for updating the entity.

const id = req.params[0]

 

5. Actions on UI

I used Fiori Tools and OData v2 adapter for creating a list report app. How to achieve this is written in my previous blog.
All you have to do is to generate a list report app using Fiori Tools. No local annotations nor extensions are required. Here is the resulting app.

When I select a record with status “1”, both start and close buttons are enabled.

When I select a record with status “2”, only close action is enabled.

For closed record, no action is enabled.

 

Summery

Using annotation@sap.applicable.path: 'startEnabled' and a virtual field, you can control actions on Fiori UI. You can implement the logic to enable actions in the CAP service implementation.

 

References

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