
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
Steps to test
Set Auth
In header you can pass these the click on get
After that we can post request and get the result
Header.
Po item.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
8 | |
5 | |
4 | |
3 | |
3 | |
3 | |
3 | |
3 | |
3 | |
2 |