Technology Blogs 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: 
patrick_winkler
Product and Topic Expert
Product and Topic Expert
2,497

Introduction

You have created a RAP BO based on one or more customizing tables by using the BC Maintenance Object ADT Wizard as described in this tutorial.

You want to extend the RAP BO by adding an additional customizing table to the data model.

The business configuration maintenance object supports RAP BO whose height of the composition tree is less than or equal to 2. For example, root entity A has two child entities B and C. Entity C has a child entity D. D cannot have any further child entities.
Depending on the back-end version, while the ADT wizard can handle multiple tables at the same time, the most complex supported scenarios require a manual enhancement after generation.

This blog is relevant for:

Further reading:

Example RAP BO

You have used the following table ZCB_HEAD with the ADT wizard to generate a maintenance object:

 

@EndUserText.label : 'Header'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #C
@AbapCatalog.dataMaintenance : #ALLOWED
define table zcb_head {
  key client            : abap.clnt not null;
  key head_key          : zcb_id not null;
  head_content          : abap.char(30);
  last_changed_at       : abp_lastchange_tstmpl;
  local_last_changed_at : abp_locinst_lastchange_tstmpl;
}

 

The table ZCB_ITEM that you want to add has the following structure and is a child entity of table ZCB_HEAD:

 

@EndUserText.label : 'Item'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #C
@AbapCatalog.dataMaintenance : #ALLOWED
define table zcb_item {
  key client            : abap.clnt not null;
  @AbapCatalog.foreignKey.keyType : #KEY
  @AbapCatalog.foreignKey.screenCheck : false
  key head_key          : zcb_id not null
    with foreign key zcb_head
      where client = zcb_item.client
        and head_key = zcb_item.head_key;
  key item_key          : abap.numc(3) not null;
  item_content          : abap.char(30);
  local_last_changed_at : abp_locinst_lastchange_tstmpl;
}

 

Data model & behavior

CDS View

Create a CDS view for table ZCB_ITEM with

  • an association to parent table ZCB_HEAD
  • an association to the singleton entity based on a calculated field SingletonID
  • if you do not have a projection layer, add the annotation @Metadata.allowExtensions: true

 

@EndUserText.label: 'Header'
@AccessControl.authorizationCheck: #CHECK
@Metadata.allowExtensions: true
define view entity ZI_Item
  as select from zcb_item
  association        to parent ZI_Header as _Header    on $projection.HeadKey = _Header.HeadKey
  association [1..1] to ZI_Header_S      as _HeaderAll on $projection.SingletonID = _HeaderAll.SingletonID
{
  key head_key              as HeadKey,
  key item_key              as ItemKey,
      item_content          as ItemContent,
      @Semantics.systemDateTime.localInstanceLastChangedAt: true
      @Consumption.hidden: true
      local_last_changed_at as LocalLastChangedAt,
      @Consumption.hidden: true
      1                     as SingletonID,
      _Header,
      _HeaderAll
}

 

Add a composition to ZI_Item in the CDS View of table ZCB_HEAD:

 

@EndUserText.label: 'Header'
@AccessControl.authorizationCheck: #CHECK
define view entity ZI_Header
  as select from zcb_head
  association to parent ZI_Header_S as _HeaderAll on $projection.SingletonID = _HeaderAll.SingletonID
  composition [0..*] of ZI_Item     as _Item
{
  key head_key              as HeadKey,
      head_content          as HeadContent,
      @Semantics.systemDateTime.lastChangedAt: true
      last_changed_at       as LastChangedAt,
      @Semantics.systemDateTime.localInstanceLastChangedAt: true
      @Consumption.hidden: true
      local_last_changed_at as LocalLastChangedAt,
      @Consumption.hidden: true
      1                     as SingletonID,
      _HeaderAll,
      _Item
}

 

If you have a projection layer, create a projection CDS view for ZI_Item with redirection to entity ZC_Header and the singleton entity. Save and activate.

 

@AccessControl.authorizationCheck: #CHECK
@Metadata.allowExtensions: true
@EndUserText.label: 'Maintain Item'
define view entity ZC_Item as projection on ZI_Item
{
  key HeadKey,
  key ItemKey,
  ItemContent,
  @Consumption.hidden: true
  LocalLastChangedAt,
  @Consumption.hidden: true
  SingletonID,
  _Header : redirected to parent ZC_Header,
  _HeaderAll : redirected to ZC_Header_S
}

 

If you have a projection layer, add a redirection to composition child ZI_Item in the projection CDS view of ZI_Header:

 

@EndUserText.label: 'Maintain Header'
@AccessControl.authorizationCheck: #CHECK
@Metadata.allowExtensions: true
define view entity ZC_Header
  as projection on ZI_Header
{
  key HeadKey,
      HeadContent,
      LastChangedAt,
      @Consumption.hidden: true
      LocalLastChangedAt,
      @Consumption.hidden: true
      SingletonID,
      _HeaderAll : redirected to parent ZC_Header_S,
      _Item      : redirected to composition child ZC_Item
}

 

Behavior definition

In the behavior definition ZI_HEADER_S, add the following definition for entity ZI_Item:

 

define behavior for ZI_Item alias Item
persistent table ZCB_ITEM
draft table ZCB_ITEM_D
etag master LocalLastChangedAt
lock dependent by _HeaderAll
authorization dependent by _HeaderAll

{
  field ( mandatory : create )
   ItemKey;

  field ( readonly )
   SingletonID,
   HeadKey,
   LocalLastChangedAt;

  field ( readonly : update )
   ItemKey;

  field ( notrigger )
   SingletonID,
   LocalLastChangedAt;


  update( features : global );
  delete( features : global );

  mapping for ZCB_ITEM
  {
    HeadKey = HEAD_KEY;
    ItemKey = ITEM_KEY;
    ItemContent = ITEM_CONTENT;
    LocalLastChangedAt = LOCAL_LAST_CHANGED_AT;
  }

  association _HeaderAll { with draft; }
  association _Header { with draft; }

  validation ValidateTransportRequest on save ##NOT_ASSIGNED_TO_DETACT { create; update; delete; }
}

 

In the entity definition of ZI_Header, add an association to ZI_Item:

 

association _Item { create ( features : global ); with draft; }

 

Save the behavior definition. Place the cursor on the draft table ZCB_ITEM_D and use the quick assist (Ctrl+1) to generate the draft table. After activating the draft table, activate the behavior definition.
Place the cursor on ZI_Item underlined by a warning message and use the quick assist to generate the missing method implementations.

If you have a projection layer, adjust the behavior projection:

 

projection;
strict;
use draft;

define behavior for ZC_Header_S alias HeaderAll

{
  use action Edit;
  use action Activate;
  use action Discard;
  use action Resume;
  use action Prepare;
  use action SelectCustomizingTransptReq;

  use association _Header { create; with draft; }
}

define behavior for ZC_Header alias Header

{
  use update;
  use delete;

  use association _HeaderAll { with draft; }
  use association _Item { create; with draft; }
}

define behavior for ZC_Item alias Item

{
  use update;
  use delete;

  use association _HeaderAll { with draft; }
  use association _Header { with draft; }
}

 

Behavior implementation

Edit the behavior implementation class. 
Add the table entity relation for ZCB_ITEM:

 

CLASS LHC_RAP_TDAT_CTS IMPLEMENTATION.
  METHOD GET.
    result = mbc_cp_api=>rap_tdat_cts( tdat_name = 'ZHEADER'
                                       table_entity_relations = VALUE #(
                                         ( entity = 'Header' table = 'ZCB_HEAD' )
                                         ( entity = 'Item' table = 'ZCB_ITEM' )
                                       ) ) ##NO_TEXT.
  ENDMETHOD.
ENDCLASS.

 

Implement the methods of LHC_ZI_ITEM and move the class definition LHC_RAP_TDAT_CTS up:

 

CLASS lhc_rap_tdat_cts DEFINITION FINAL.
  PUBLIC SECTION.
    CLASS-METHODS:
      get
        RETURNING
          VALUE(result) TYPE REF TO if_mbc_cp_rap_tdat_cts.

ENDCLASS.

CLASS lhc_item DEFINITION INHERITING FROM cl_abap_behavior_handler.

  PRIVATE SECTION.

    METHODS get_global_features FOR GLOBAL FEATURES
      IMPORTING REQUEST requested_features FOR Item RESULT result.

    METHODS ValidateTransportRequest FOR VALIDATE ON SAVE
      IMPORTING keys FOR Item~ValidateTransportRequest.

ENDCLASS.

CLASS lhc_item IMPLEMENTATION.

  METHOD get_global_features.
    DATA edit_flag TYPE abp_behv_flag VALUE if_abap_behv=>fc-o-enabled.
    IF lhc_rap_tdat_cts=>get( )->is_editable( ) = abap_false.
      edit_flag = if_abap_behv=>fc-o-disabled.
    ENDIF.
    result-%update = edit_flag.
    result-%delete = edit_flag.
  ENDMETHOD.

  METHOD ValidateTransportRequest.
    DATA change TYPE REQUEST FOR CHANGE ZI_Header_S.
    SELECT SINGLE TransportRequestID
      FROM zcb_head_d_s
      WHERE SingletonID = 1
      INTO (TransportRequestID).
    lhc_rap_tdat_cts=>get( )->validate_changes(
                                transport_request = TransportRequestID
                                table             = 'ZCB_ITEM'
                                keys              = REF #( keys )
                                reported          = REF #( reported )
                                failed            = REF #( failed )
                                change            = REF #( change-item ) ).
  ENDMETHOD.

ENDCLASS.

 

If you are using the VALIDATE_ALL_CHANGES method, delete the ValidateTransportRequest method from the LHC_ITEM class and adjust the ValidateTransportRequest method of the LHC_ZI_HEADER class:

 

VALIDATETRANSPORTREQUEST FOR VALIDATE ON SAVE
        IMPORTING
          KEYS_HEADER FOR Header~ValidateTransportRequest
          KEYS_ITEM FOR Item~ValidateTransportRequest.
[...]
  METHOD VALIDATETRANSPORTREQUEST.
    DATA change TYPE REQUEST FOR CHANGE ZI_Header_S.
    IF keys_Header IS NOT INITIAL.
      DATA(is_draft) = keys_Header[ 1 ]-%IS_DRAFT.
    ELSEIF keys_Item IS NOT INITIAL.
      is_draft = keys_Item[ 1 ]-%IS_DRAFT.
    ELSE.
      RETURN.
    ENDIF.
    READ ENTITY IN LOCAL MODE ZI_Header_S
    FROM VALUE #( ( %IS_DRAFT = is_draft
                    SingletonID = 1
                    %CONTROL-TransportRequestID = if_abap_behv=>mk-on ) )
    RESULT FINAL(transport_from_singleton).
    IF lines( transport_from_singleton ) = 1.
      DATA(transport_request) = transport_from_singleton[ 1 ]-TransportRequestID.
    ENDIF.
    lhc_rap_tdat_cts=>get( )->validate_all_changes(
                                transport_request     = transport_request
                                table_validation_keys = VALUE #(
                                                          ( table = 'ZCB_HEAD' keys = REF #( keys_Header ) )
                                                          ( table = 'ZCB_ITEM' keys = REF #( keys_Item ) )
                                                               )
                                reported              = REF #( reported )
                                failed                = REF #( failed )
                                change                = REF #( change ) ).
  ENDMETHOD.

 

Add the %ASSOC-_Item statement to the GET_GLOBAL_FEATURES method of LHC_ZI_HEADER:

 

  METHOD get_global_features.
    DATA edit_flag TYPE abp_behv_flag VALUE if_abap_behv=>fc-o-enabled.
    IF lhc_rap_tdat_cts=>get( )->is_editable( ) = abap_false.
      edit_flag = if_abap_behv=>fc-o-disabled.
    ENDIF.
    result-%update = edit_flag.
    result-%delete = edit_flag.
    result-%ASSOC-_Item = edit_flag.
  ENDMETHOD.

 

Metadata extension

Create a metadata extension for ZI_Item or ZC_Item when using a projection layer:

 

@Metadata.layer: #CUSTOMER
@UI: {
  headerInfo: {
    typeName: 'Item', 
    typeNamePlural: 'Items', 
    title: {
      type: #STANDARD, 
      label: 'Item', 
      value: 'ItemKey'
    }
  }
}
annotate entity ZC_Item with
{
  @UI.identification: [ {
    position: 1 , 
    label: 'ItemKey'
  } ]
  @UI.lineItem: [ {
    position: 1 , 
    label: 'ItemKey'
  } ]
  @UI.facet: [ {
    id: 'ZI_Item', 
    purpose: #STANDARD, 
    type: #IDENTIFICATION_REFERENCE, 
    label: 'Item', 
    position: 1 
  } ]
  ItemKey;
  
  @UI.identification: [ {
    position: 2 , 
    label: 'ItemContent'
  } ]
  @UI.lineItem: [ {
    position: 2 , 
    label: 'ItemContent'
  } ]
  ItemContent;
}

 

Add a facet to _Item in the metadata extension of ZI_Header or ZC_Header when using a projection layer:

 

[...]
annotate entity ZC_Header with
{
  @UI.identification: [ {
    position: 1 
  } ]
  @UI.lineItem: [ {
    position: 1 
  } ]
  @UI.facet: [ {
    id: 'ZI_Header', 
    purpose: #STANDARD, 
    type: #IDENTIFICATION_REFERENCE, 
    label: 'Header', 
    position: 1 
  }, 
  {
    id: 'ZI_Item', 
    type: #LINEITEM_REFERENCE, 
    label: 'Item', 
    position: 2 , 
    targetElement: '_Item'
  } ]
  HeadKey;
[...]

 

Access Control

Create an access control object for ZI_Item:

 

@MappingRole: true
define role ZI_Item {
  grant select on ZI_Item
  where INHERITING CONDITIONS FROM ENTITY ZI_Header;
}

 

If you are using a projection layer, create an access control object for ZC_Item:

 

@MappingRole: true
define role ZC_Item {
  grant select on ZC_Item
  where INHERITING CONDITIONS FROM ENTITY ZI_Item;
}

 

Transport Object

Add table ZCB_ITEM to the transport object definition ZHEADER:

 

  "tableItems": [
    {
      "tableName": "ZCB_HEAD",
      "isPrimaryTable": true
    },
    {
      "tableName": "ZCB_ITEM"
    }
  ]

 

If you are using SAP S/4HANA On Premises 2023, use transaction SOBJ instead of ADT to adjust the transport object definition.

Business Service

Add entity ZC_Item to the service definition:

 

define service ZUI_HEADER {
  expose ZC_Header_S as HeaderAll;
  expose ZC_Header   as Header;
  expose ZC_Item     as Item;
}

 

Maintenance Object

No adjustments are necessary. You have the option to define specific table settings for the new entity, for example, to change the table creation mode.

You can now restart the Custom Business Configurations app to review the changes.