Technology Blog Posts by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
Umesh_Gauda
Explorer
358

Introduction

In this blog, we will explore how to implement a deep action in the ABAP RESTful Application Programming Model (RAP). Our use case involves bulk approval of Purchase Orders (POs) along with their respective line items.

Deep Action :

A Deep Action in the ABAP RESTful Application Programming Model (RAP) is a custom behavior action that allows processing and manipulation of a hierarchical structure of entities—including the root entity and its related child entities—in a single transactional call.

 

Steps for end-to-end implementation,

CDS view creation

Behavior definitions

Abstract entities

Deep action logic

Metadata extensions

Service definition

Testing

Business scenario.

multiple purchase orders (POs) for procuring raw materials and equipment. Each PO contains several line items detailing the materials, quantities, and suppliers. Streamline operations, the company wants to implement a feature that allows bulk approval of selected POs along with their respective line items.

Implementation Steps:

Define the Data Model:

 

Behavior Definition:

Action Definition: In the behavior definition (behavior definition), define a deep action Approves that operates on PO Header and its associated PO Item entities.

Behavior Implementation:

Purchase Order Header Entity (PO Header):

Represents the main details of the purchase order, such as PO number, supplier, and approval status.

Database Table.

@EndUserText.label : 'purchase order header'

@AbapCatalog.enhancement.category : #EXTENSIBLE_ANY

@AbapCatalog.tableCategory : #TRANSPARENT

@AbapCatalog.deliveryClass : #A

@AbapCatalog.dataMaintenance : #RESTRICTED

define table zug_dt_poheader {



key client : abap.clnt not null;

key ebeln : ebeln not null;

bukrs : bukrs;

lifnr : lifnr;

ekorg : ekorg;

ekgrp : ekgrp;

bstyp : bstyp;

status : zug_postatus;



}

Purchase Order Item Entity (PO Item):

Contains details of each line item within a PO, including material ID, quantity, price, and item-specific approval status.


POitem.

@EndUserText.label : 'purchase order item'

@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE

@AbapCatalog.tableCategory : #TRANSPARENT

@AbapCatalog.deliveryClass : #A

@AbapCatalog.dataMaintenance : #RESTRICTED

define table zug_dt_poitem {



key client : abap.clnt not null;

@AbapCatalog.foreignKey.screenCheck : false

key ebeln : ebeln not null

with foreign key [0..*,1] zug_dt_poheader

where ebeln = zug_dt_poitem.ebeln;

key ebelp : ebelp not null;

matnr : matnr;

munit : abap.unit(3);

@Semantics.quantity.unitOfMeasure : 'zug_dt_poitem.munit'

menge : menge_d;

cunit : abap.cuky;

@Semantics.amount.currencyCode : 'zug_dt_poitem.cunit'

netpr : abap.curr(10,2);

status : zug_postatus;



}


Behavior Definition:

Action Definition: In the behavior definition (behavior definition), define a deep action Approves that operates on PO Header and its associated PO Item entities.

Interface entity.

unmanaged implementation in class zbp_ug_i_poheader2 unique;

strict ( 2 );
define behavior for zug_i_poheader1 

lock master

authorization master ( instance )

{
static action Bulkorderapprove deep parameter zug_s_poheader;

create;

update;

delete;
association _poitem { create; }
mapping for zug_dt_poheader

{
Bstyp = bstyp;

Bukrs = bukrs;

Ebeln = ebeln;

Ekgrp = ekgrp;

Ekorg = ekorg;

Lifnr = lifnr;

Status = status;

}

}

define behavior for zug_i_poitem1 


lock dependent by _poheader

authorization dependent by _poheader

{

update;

delete;

field ( readonly ) Ebeln;

association _poheader;

mapping for zug_dt_poitem

{

Ebeln = ebeln;

Ebelp = ebelp;

Matnr = matnr;

Menge = menge;

Munit = munit;

Netpr = netpr;

Status = status;

}

}

Association: Establish a composition association between PO Header and PO Item to model the hierarchical relationship.

Projection view.

projection;
strict ( 2 );
define behavior for zug_p_poheader1 //alias <alias_name>

{
use create;

use update;

use delete;

use action Bulkorderapprove;

use association _poitem { create; }

}
define behavior for zug_p_poitem //alias <alias_name>
{

use update;

use delete;

use association _poheader;

}

 

Abstract entity.

abstract;

strict ( 2 );

with hierarchy;

define behavior for zug_s_poheader //alias <alias_name>

{

association _poitem;

}



define behavior for zug_s_poitem //alias <alias_name>

{

}



Abstract Entities

@EndUserText.label: 'abstract entity'

define root abstract entity zug_s_poheader

{

key ebeln : ebeln ;

status : zug_postatus ;

last_changed_by : abp_locinst_lastchange_user;

last_changed_at : abp_locinst_lastchange_tstmpl;

_poitem : composition [0..*] of zug_s_poitem;

}

For item

@EndUserText.label: 'poitem abstarct entity'

define abstract entity zug_s_poitem



{

key ebeln : ebeln ;

key ebelp : ebelp ;

status : zug_postatus ;

last_changed_by : abp_locinst_lastchange_user;

last_changed_at : abp_locinst_lastchange_tstmpl;

_poheader : association to parent zug_s_poheader on $projection.ebeln = _poheader.ebeln;

}

Meta data extension for header

@Metadata.layer: #CORE

annotate entity zug_p_poheader1

with

{

@UI.facet: [{

id: 'Poheader',

purpose: #STANDARD,

position: 10 ,

label: 'Poheader',

type: #IDENTIFICATION_REFERENCE

},

{

id: 'Poitem',

purpose: #STANDARD,

position: 20 ,

label: 'Poitem',

type: #LINEITEM_REFERENCE,

targetElement: '_poitem'

}

]

@UI.lineItem: [{ position: 10 }]

@UI.identification: [{ position: 10 }]

Ebeln;

@UI.lineItem: [{ position: 20 }]

@UI.identification: [{ position: 20 }]

Bukrs;

@UI.lineItem: [{ position: 20 }]

@UI.identification: [{ position: 30 }]

Lifnr;

@UI.lineItem: [{ position: 40 }]

@UI.identification: [{ position: 40 }]

Ekorg;

@UI.lineItem: [{ position: 50 }]

@UI.identification: [{ position: 50 }]

Ekgrp;

@UI.lineItem: [{ position: 60 }]

@UI.identification: [{ position: 60 }]

Bstyp;

@UI.lineItem: [{ position: 70 }

, { type: #FOR_ACTION , dataAction: 'Bulkorderapprove' , label : 'Bulkorderapprove' }

]

@UI.identification: [{ position: 70 }]

Status;

}


Action Handler: Implement the Approves action in the behavior implementation class. This method will loop through the selected POs and their items, updating their approval statuses.

Implementing class

CLASS lhc_zug_i_poheader1 DEFINITION INHERITING FROM cl_abap_behavior_handler.

PRIVATE SECTION.

METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION

IMPORTING keys REQUEST requested_authorizations FOR zug_i_poheader1 RESULT result.

METHODS create FOR MODIFY

IMPORTING entities FOR CREATE zug_i_poheader1.

METHODS update FOR MODIFY

IMPORTING entities FOR UPDATE zug_i_poheader1.
METHODS delete FOR MODIFY

IMPORTING keys FOR DELETE zug_i_poheader1.

METHODS read FOR READ

IMPORTING keys FOR READ zug_i_poheader1 RESULT result.

METHODS lock FOR LOCK

IMPORTING keys FOR LOCK zug_i_poheader1.

METHODS rba_Poitem FOR READ

IMPORTING keys_rba FOR READ zug_i_poheader1\_Poitem FULL result_requested RESULT result LINK association_links.

METHODS cba_Poitem FOR MODIFY

IMPORTING entities_cba FOR CREATE zug_i_poheader1\_Poitem.

METHODS Bulkorderapprove FOR MODIFY

IMPORTING keys FOR ACTION zug_i_poheader1~Bulkorderapprove.

ENDCLASS.

CLASS lhc_zug_i_poheader1 IMPLEMENTATION.
METHOD get_instance_authorizations.

ENDMETHOD.

METHOD create.
data ls_c_o TYPE zug_dt_poheader.
data lt_c_o TYPE TABLE of zug_dt_poheader.
lt_c_o = CORRESPONDING #( entities mapping from ENTITY ).
ls_c_o = lt_c_o[ 1 ].
zcl_ug_h=>gs_tab_c = ls_c_o
ENDMETHOD.

METHOD update.
data ls_u_o TYPE zug_dt_poheader
data lt_u_o TYPE TABLE of zug_dt_poheader.

lt_u_o = CORRESPONDING #( entities mapping from ENTITY ).

ls_u_o = lt_u_o[ 1 ].

zcl_ug_h=>gs_tab_u = ls_u_o.
ENDMETHOD.


METHOD delete.

ENDMETHOD.

METHOD read.

SELECT from zug_dt_poheader FIELDS *

INTO TABLE (lt_porders).

result = CORRESPONDING #( lt_porders mapping to entity ).

ENDMETHOD.



METHOD lock.

ENDMETHOD.



METHOD rba_Poitem.

ENDMETHOD.



METHOD cba_Poitem.

ENDMETHOD.



METHOD Bulkorderapprove.

DATA lt_order TYPE TABLE for UPDATE zug_i_poheader1.

DATA lt_item TYPE TABLE for UPDATE zug_i_poitem1.
data(ls_s) = VALUE #( keys[ 1 ] OPTIONAL ).
lt_order = value #( ( ebeln = ls_s-%param-ebeln
status = ls_s-%param-status
) ).

lt_item = value #( for ls_i in ls_s-%param-_poitem ( ebeln = ls_i-ebeln

ebelp = ls_i-ebelp
) ).




MODIFY ENTITIES OF zug_i_poheader1 IN LOCAL MODE
ENTITY zug_i_poheader1
UPDATE from lt_order
ENTITY zug_i_poitem1
UPDATE FROM lt_item
FAILED DATA(lt_failed)

REPORTED data(lt_reported).
ENDMETHOD.

ENDCLASS.

CLASS lhc_zug_i_poitem1 DEFINITION INHERITING FROM cl_abap_behavior_handler.

PRIVATE SECTION.

METHODS update FOR MODIFY

IMPORTING entities FOR UPDATE zug_i_poitem1.

METHODS delete FOR MODIFY

IMPORTING keys FOR DELETE zug_i_poitem1.


METHODS read FOR READ

IMPORTING keys FOR READ zug_i_poitem1 RESULT result


METHODS rba_Poheader FOR READ

IMPORTING keys_rba FOR READ zug_i_poitem1\_Poheader FULL result_requested RESULT result LINK association_links

ENDCLASS.

CLASS lhc_zug_i_poitem1 IMPLEMENTATION.

METHOD update.
ENDMETHOD.

METHOD delete.
ENDMETHOD.

METHOD read.
ENDMETHOD.

METHOD rba_Poheader.
ENDMETHOD.

ENDCLASS.

CLASS lsc_ZUG_I_POHEADER1 DEFINITION INHERITING FROM cl_abap_behavior_saver.

PROTECTED SECTION.

METHODS finalize REDEFINITION.

METHODS check_before_save REDEFINITION.

METHODS save REDEFINITION

METHODS cleanup REDEFINITION.

METHODS cleanup_finalize REDEFINITION
ENDCLASS.

CLASS lsc_ZUG_I_POHEADER1 IMPLEMENTATION.
METHOD finalize.
ENDMETHOD.

METHOD check_before_save.
ENDMETHOD.

METHOD save.
if zcl_ug_h=>gs_tab_c is NOT INITIAL.
INSERT zug_dt_poheader from @zcl_ug_h=>gs_tab_c.
ELSEIF zcl_ug_h=>gs_tab_u Is NOT INITIAL.
INSERT zug_dt_poheader from @zcl_ug_h=>gs_tab_u.
ENDIF.

ENDMETHOD.

METHOD cleanup.

ENDMETHOD.

METHOD cleanup_finalize.

ENDMETHOD.

ENDCLASS.



DATA lt_order TYPE TABLE for UPDATE zug_i_poheader1.
DATA lt_item TYPE TABLE for UPDATE zug_i_poitem1
data(ls_s) = VALUE #( keys[ 1 ] OPTIONAL ).

lt_order = value #( ( ebeln = ls_s-%param-ebeln

status = ls_s-%param-status

) ).

lt_item = value #( for ls_i in ls_s-%param-_poitem ( ebeln = ls_i-ebeln

ebelp = ls_i-ebelp

) ).

MODIFY ENTITIES OF zug_i_poheader1 IN LOCAL MODE

ENTITY zug_i_poheader1

UPDATE

from lt_order

ENTITY zug_i_poitem1

UPDATE FROM

lt_item
FAILED DATA(lt_failed)

REPORTED data(lt_reported).

Service Exposure: 

Service Definition: Expose the PO Header and PO Item entities through a service definition to make them available for consumption. 

Service Binding: Create a service binding to expose the service via OData. 

Service binding  

Umesh_Gauda_0-1745585766202.png

Steps to test 

Set Auth  

Umesh_Gauda_1-1745585766203.png

 

 In header you can pass  these the click on get  

Umesh_Gauda_2-1745585766204.png

After that we can post request and get the result 

Umesh_Gauda_3-1745585766205.png

 

 

 

 

Umesh_Gauda_4-1745585766207.png

Header. 

Umesh_Gauda_5-1745585766208.png

 

 Po item. 

Umesh_Gauda_6-1745585766208.png

 

 

 

 

 

 

 

 

 

Labels in this area