Application Development and Automation Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
Dadapeer
Explorer
390

Introduction 

This blo
g provides a step-by-step guide on creating a Inventory Management and archiving the Determine action using RAP in a managed scenario. In RAP, determinations and validations are typically executed implicitly during save operations.

In this blog, where I will calculate the total value in an Inventory object only after the lastchangeat timestamp was set. We'll solve this challenge using a Determine Action. Determine action allows to execute Determination and validation on request. Whenever a determine action is executed. 

Scenario for Determine action: 

When a user creates an Inventory entry with product details, the following must happen: 

  • A default RAP determination should calculate total_value  during create. 
  • The total_value should be calculated after the lastchangeat field is populated (which happens after the entity is saved). 
  • To achieve this, a custom Determine Action should be used post-save to trigger the actual determination. 

Procedure:   

Created one database table 'ZDP_inventory

@EndUserText.label : 'inventory table'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zdp_inventory {

  key product_id : abap.int1 not null;
  product_name   : abap.char(50);
  quantity       : zdp_de_pdt_quantity;
  @Semantics.amount.currencyCode : 'zdp_inventory.currency_code'
  unit_price     : zdp_de_unit_price;
  currency_code  : abap.cuky;
  @Semantics.amount.currencyCode : 'zdp_inventory.currency_code'
  total_value    : abap.curr(13,2);
  last_change_at : timestampl;

}

Now we need to create an interface view on top of DB table. 

@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'interface entity view for inventory'
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
    serviceQuality: #X,
    sizeCategory: #S,
    dataClass: #MIXED
}
@Metadata.allowExtensions: true
define root view entity zdp_i_cds_inventory as 
select from zdp_inventory

{
    key product_id as ProductId,
    product_name as ProductName,
    quantity as Quantity,
    @Semantics.amount.currencyCode : 'currencycode'
    unit_price as UnitPrice,
    currency_code as CurrencyCode,
    @Semantics.amount.currencyCode : 'currencycode'
    total_value as totalvalue,
    @Semantics.systemDateTime.lastChangedAt: true
    last_change_at as lastchangeat
}

 Now we need to create a projection view on top of Interface view  

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'projection view on inventory'
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
define root view entity zdp_p_cds_inventory 
provider contract transactional_query 
as projection on zdp_i_cds_inventory
{
    key ProductId,
    ProductName,
    Quantity,
    @Semantics.amount.currencyCode : 'currencycode'
    UnitPrice,
    CurrencyCode,
    @Semantics.amount.currencyCode : 'currencycode'
    totalvalue,
    lastchangeat
}

Now we need to create the behavior definition on top of interface view. 

managed implementation in class zbp_dp_i_cds_inventory unique;
strict ( 2 );

define behavior for zdp_i_cds_inventory //alias <alias_name>
persistent table zdp_inventory
lock master
authorization master ( none )

{
  create ;
  update ( authorization : none  );
  delete ;
  
  field ( readonly ) lastchangeat;
  action Trigger_detmine_action result[1] $self;

  determine action calc_total
    {
       determination ( always ) totalCalc;
      validation checkQuantity;
    }

  determination totalCalc
    on save
    { field lastchangeat; create; }

  validation checkQuantity
    on save
    { field Quantity; }

  mapping for zdp_inventory
  {
   ProductId = product_id;
   ProductName = product_name;
   Quantity = quantity;
   UnitPrice = unit_price;
   CurrencyCode = currency_code;
   totalvalue = total_value;
   lastchangeat = last_change_at;

  }
}

Now create a behavior definition on top of consumption view:    

Code of Behavior Definition of Projection: 

projection;
strict ( 2 );

define behavior for zdp_p_cds_inventory //alias <alias_name>
{
  use create;
  use update;
  use delete;
  use action Trigger_detmine_action;
  use action calc_total;
}

To declare Determine action in Behavior Definition.

Dadapeer_0-1751279317704.png

To trigger Determine action :
The determine action calc_total is executed with the statement MODIFY ENTITIES for entity instances.

Dadapeer_0-1751278622761.png

Behavior implementation. 

CLASS lhc_zdp_i_cds_inventory DEFINITION INHERITING FROM cl_abap_behavior_handler.
  PRIVATE SECTION.

    METHODS checkquantity FOR VALIDATE ON SAVE
      IMPORTING keys FOR zdp_i_cds_inventory~checkquantity.
    METHODS trigger_detmine_action FOR MODIFY
      IMPORTING keys FOR ACTION zdp_i_cds_inventory~trigger_detmine_action RESULT result.
    METHODS totalcalc FOR DETERMINE ON SAVE
      IMPORTING keys FOR zdp_i_cds_inventory~totalcalc.

ENDCLASS.

CLASS lhc_zdp_i_cds_inventory IMPLEMENTATION.

  METHOD checkQuantity.
    READ ENTITIES OF zdp_i_cds_inventory IN LOCAL MODE
    ENTITY zdp_i_cds_inventory
    FIELDS ( quantity )
    WITH CORRESPONDING #( keys )
    RESULT DATA(lt_result_qua).

    DATA(ls_res_qua) = lt_result_qua[ 1 ].
    IF ls_res_qua-Quantity IS INITIAL.
      APPEND VALUE #( %tky = ls_res_qua-%tky
                    %msg = new_message_with_text(
                    severity = if_abap_behv_message=>severity-error
                    text = 'Please provide quantity'
                    ) ) TO reported-zdp_i_cds_inventory.
    ENDIF.

  ENDMETHOD.

  METHOD Trigger_detmine_action.
    READ ENTITIES OF zdp_i_cds_inventory IN LOCAL MODE
    ENTITY zdp_i_cds_inventory
    FIELDS ( ProductId )
    WITH CORRESPONDING #( keys )
    RESULT DATA(lt_result_prdt).


    LOOP AT lt_result_prdt INTO DATA(ls_result).
      MODIFY ENTITIES OF zdp_i_cds_inventory IN LOCAL MODE
           ENTITY zdp_i_cds_inventory
           EXECUTE calc_total
           FROM VALUE #( ( ProductId = ls_result-ProductId ) )

           MAPPED FINAL(lt_mapped)
           FAILED FINAL(lt_failed)
           REPORTED FINAL(lt_reported).
      mapped-zdp_i_cds_inventory = lt_mapped-zdp_i_cds_inventory.
*    COMMIT ENTITIES.
    ENDLOOP.
  ENDMETHOD.

  METHOD totalCalc.

    READ ENTITIES OF zdp_i_cds_inventory IN LOCAL MODE
    ENTITY zdp_i_cds_inventory
    FIELDS ( quantity  unitprice  )
    WITH CORRESPONDING #( keys )
    RESULT DATA(lt_result).



    LOOP AT keys ASSIGNING FIELD-SYMBOL(<fs_keys>).
      LOOP AT lt_result INTO DATA(ls_result).
        IF ls_result-%tky = <fs_keys>-%tky.
          ls_result-totalvalue = ls_result-quantity * ls_result-unitprice.

          MODIFY ENTITIES OF zdp_i_cds_inventory IN LOCAL MODE
          ENTITY zdp_i_cds_inventory
          UPDATE
          FIELDS ( totalvalue ) WITH VALUE #( ( %tky = <fs_keys>-%tky
                                                 totalvalue = ls_result-totalvalue ) ).
        ENDIF.
      ENDLOOP.
    ENDLOOP.
    .


  ENDMETHOD.

ENDCLASS.

 Output:   

Dadapeer_0-1751278237864.png

When we click on the action button at that time, determine action will trigger the total value is calculated.  

Dadapeer_0-1751278084388.png