Technology Blog Posts by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
SrinivasaMural
Product and Topic Expert
Product and Topic Expert
11,288

In the Blog, we will understand how to set up Joule through BTP account, Connect to an On-Premise API, build a capability with the help of Joule and Integrate the bot to your application.

Create a Sample Application

Let’s create a sample RAP based application with a header table and an item table as shown below:

SrinivasaMural_0-1718612205146.png

Order category has a domain with fixed values as seen below:

SrinivasaMural_1-1718612205149.png

SrinivasaMural_2-1718612205152.png

As we Know, to use RAP based application we must create a Root View and a projection view. Let’s create for both Header and item table.

Header Entity

 

 

 

 

@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Header Table'
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
  serviceQuality: #X,
  sizeCategory: #S,
  dataClass: #MIXED
}
define root view entity ZI_JOULE_HEADER
  as select from zjoule_header
  composition[0..*] of ZI_JOULE_ITEM as _Item
{
  key order_id       as OrderId,
      order_category as OrderCategory,
      created_on     as CreatedOn,
      created_by     as CreatedBy,
      _Item
}

 

 

 

Item Entity:

 

 

 

 

@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Joule Item'
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
  serviceQuality: #X,
  sizeCategory: #S,
  dataClass: #MIXED
}
define view entity ZI_JOULE_ITEM
  as select from zjoule_item
  association to parent ZI_JOULE_HEADER as _header on $projection.OrderId = _header.OrderId
{
  key cast(order_id as vbeln_va preserving type ) as OrderId,
  key order_item as OrderItem,
      @Semantics.quantity.unitOfMeasure: 'Unit'
      quantity   as Quantity,
      unit       as Unit,
      @Semantics.amount.currencyCode: 'Currency'
      price      as Price,
      currency   as Currency,
      model      as Model,
      _header
}

 

 

 

Header Projection:

 

 

 

 

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Projection for Header Table'
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
@ObjectModel.usageType:{
    serviceQuality: #X,
    sizeCategory: #S,
    dataClass: #MIXED
}

define root view entity ZC_JOULE_HEADER
  provider contract transactional_query
  as projection on ZI_JOULE_HEADER
{
  key OrderId,
      OrderCategory,
      CreatedOn,
      CreatedBy,
      /* Associations */
      _Item : redirected to composition child ZC_JOULE_ITEM
}

 

 

 

 

Item Projection:

 

 

 

 

@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Projection for Item Table'
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
@ObjectModel.usageType:{
  serviceQuality: #X,
  sizeCategory: #S,
  dataClass: #MIXED
}
define view entity ZC_JOULE_ITEM
  as projection on ZI_JOULE_ITEM
{
  key OrderId,
  key OrderItem,
      @Semantics.quantity.unitOfMeasure: 'Unit'
      Quantity,
      Unit,
      @Semantics.amount.currencyCode: 'Currency'
      Price,
      Currency,
      Model,
      /* Associations */
      _header : redirected to parent ZC_JOULE_HEADER
}

 

 

 

Let’s also create the Metadata extension for both the projection views.

Header table:

 

 

 

 

@Metadata.layer: #CORE
@UI:{ headerInfo: {
    typeName: 'Order',
    typeNamePlural: 'Orders',
    title: {
        type: #STANDARD,
        value: 'OrderId'
    }
} }
annotate entity ZC_JOULE_HEADER with
{
  .facet: [{
                id: 'Order',
                purpose: #STANDARD,
                position: 10,
                label: 'Order',
                type: #IDENTIFICATION_REFERENCE
              },
              { 
                id: 'Items',
                purpose: #STANDARD,
                position: 20,
                label: 'Booking',
                type: #LINEITEM_REFERENCE,
                targetElement: '_Item'
             }]   
   
  :{ lineItem: [{position: 10,importance: #HIGH }],
           identification: [{position: 10 }],
           selectionField: [{position: 10 }] }
  OrderId;
  :{ lineItem: [{position: 20,importance: #HIGH }],
        identification: [{position: 20 }],selectionField: [{position: 20 }] }  
  OrderCategory;
  :{ lineItem: [{position: 30,importance: #MEDIUM }],
         identification: [{position: 30 }],selectionField: [{position: 30 }] }  
  CreatedOn;
  :{ lineItem: [{position: 40,importance: #MEDIUM }],
       identification: [{position: 40 }] }  
  CreatedBy;
}

 

 

 

 

Item Table:

 

 

 

 

@Metadata.layer: #CORE
@UI.headerInfo: {
    typeName: 'Order',
    typeNamePlural: 'Orders',
    title: {
        type: #STANDARD,
        label: 'Order',
        value: 'OrderId'
    }
}
annotate entity ZC_JOULE_ITEM with
{
  .facet: [{id: 'Order',
                purpose: #STANDARD,
                type: #IDENTIFICATION_REFERENCE,
                label: 'Order',
                position: 10 }]
                
  :{lineItem: [{  position: 10,importance: #HIGH }],
                identification: [{position: 10 }]
  }
  OrderId;
  :{lineItem: [{  position: 20,importance: #HIGH }],
                identification: [{position: 20 }]
  }  
  OrderItem;
  :{lineItem: [{  position: 30,importance: #HIGH }],
                identification: [{position: 30 }]
  }  
  Quantity;
  :{lineItem: [{  position: 40,importance: #HIGH }],
                identification: [{position: 40 }]
  }  
  Unit;
  :{lineItem: [{  position: 50,importance: #HIGH }],
                identification: [{position: 50 }]
  }  
  Price;
  :{lineItem: [{  position: 60,importance: #HIGH }],
                identification: [{position: 60 }]
  }  
  Currency;
  :{lineItem: [{  position: 70,importance: #HIGH }],
                identification: [{position: 70 }] }  
  Model;
}

 

 

 

Once we create Entities, we will create a behaviour definition with managed scenario. As we are using ODATA V4, also create a draft table for the same. We will not be implementing the class since it’s a managed scenario.

 

 

 

managed implementation in class zbp_i_joule_header unique;
strict ( 2 );
with draft;

with privileged mode disabling NoCheckWhenPrivileged;
define authorization context NoCheckWhenPrivileged { 'O_OIU_TXN'; }
define own authorization context { 'O_OIU_TXN'; }

define behavior for ZI_JOULE_HEADER alias Order
persistent table zjoule_header
draft table zjoule_draft_hd
lock master total etag CreatedOn
authorization master ( instance )
etag master CreatedOn
{
  create;
  update;
  delete;
  association _Item { create; with draft; }
  draft action Edit;
  draft action Discard;
  draft action Resume;
  draft action Activate optimized;
  draft determine action Prepare { }
  field ( mandatory ) OrderCategory;
  mapping for zjoule_header
    {
      OrderId       = order_id;
      OrderCategory = order_category;
      CreatedOn     = created_on;
      CreatedBy     = created_by;
    }
}

define behavior for ZI_JOULE_ITEM alias Items
persistent table zjoule_item
draft table zjoule_draft_it
lock dependent by _header
authorization dependent by _header
//etag master <field_name>
{
  update;
  delete;
  field ( readonly ) OrderId;
  field(mandatory) Currency, Price, Quantity, Unit, Model;
  association _header { with draft; }
  mapping for zjoule_item
    {
      OrderId   = order_id;
      OrderItem = order_item;
      Currency  = currency;
      Price     = price;
      Quantity  = quantity;
      Unit      = unit;
      Model     = model;
    }
}

 

 

 

Create a Service Definition and a Service binding, Register the service to test.

 

 

 

 

@EndUserText.label: 'Service Definitions'
define service ZU_JOULEHEADER {
  expose ZC_JOULE_HEADER as Orders;
  expose ZC_JOULE_ITEM   as Items;
}

 

 

 

 

SrinivasaMural_7-1718612205166.png

I have created some sample data to test the application.

SrinivasaMural_8-1718612205170.png

Connect your BTP account to Access Joule services

You must subscribe to SAP DAS and then create an instance of SAP DAS. You must be a global account admin to perform those tasks. Then, add the entitlement SAP DAS for your global account in the SAP BTP control center. Kindly refer to the below url for Global account setup

https://help.sap.com/docs/joule/serviceguide/prerequisites?locale=en-US

In the SAP BTP cockpit, create a subaccount. Open your sub account and establish Trust configurations( Preferably create a Custom IDP ). Then, go to service marketplace, look for SAP DAS and open the same. Based on the entitlements, you will see the development plan under Application plans. Click on the actions menu and select create. Now, you have subscribed to SAP DAS.

To develop capabilities with Joule, you need to assign roles to your user. Refer the below URL

https://help.sap.com/docs/joule/service-guide/assign-roles?locale=en-US 

Create a Service Instance and Service Key

Inside your subaccount, go to the service marketplace, look for the app SAP Digital Assistant and click to open. After adding your entitlements you will see designer plan under service plans. Click on the actions to create the instance.

Select the SAP Digital Assistant that you created, click on the actions menu to choose service key. Choose a name for your service and proceed.

Installing SAPDAS CLI:

Prerequisites - You should have installed Node.js( minimum version 18 ). Open the terminal and run the following commands to:

Install sapdas

 

 

 

 

npm install -g sapdas-cli

 

 

 

 

Get a version

 

 

 

 

npm install -g sapdas-cli@<version>

 

 

 

Replace <version> with a version number.

 

Thanks,

Srinivasa Muralidhar

 

 

10 Comments