Cloud ready RAP object using legacy BAPI – Clean Core objective
2024-1-4 09:10:6 Author: blogs.sap.com(查看原文) 阅读量:34 收藏

INTRODUCTION

Technical prerequisites: S/4 Hana On premise or S/4 Hana private cloud edition environment.

In this blog, the aim is to create a Fiori application using the RAP framework, based on an existing BAPI, while complying with SAP guidelines to achieve the clean core objective.

Use case: For this presentation, we’ll be looking to see whether deliveries have existing HUs (Handling Units). If so, we need to be able to delete these HUs.

In legacy ABAP, we can use the HU_DELETE_FROM_DEL BAPI.

We’ll now look at how to integrate this into a RAP object, following the SAP recommendations for a clean core.

I’ll also come back to the use of the RAP framework with the use of ABAP Standard, without the objective of clean core or cloud compliance, to show you some of the differences.

ABAP RAP, in the context of ABAP CLOUD

Let’s take a very brief look at the ABAP RAP framework and its structure to provide the main foundations for this blog, all in the context of ABAP Cloud development.

However, I recommend that you read the SAPPRESS book “ABAP RESTful Application Programming Model – A comprehensive guide” (1), which will explain all the details of this framework perfectly.

Before delving into the structure of the framework, let’s return to the notion of LUW in the cloud, as this will have a major impact on our development.

As mentioned in detail in this SAP Blog (2), while it used to be recommended to use IN UPDATE TASK (which, among other things, avoided writing to databases when there were implicit commits and increased performance, but I invite you to read the article cited above) when it was desired to write to the SAP database using the ABAP standard. This is no longer the case with ABAP cloud, since the performance of the HANA database means that it can now be dispensed with. However, this implies that database writes only take place at the end of the SAP LUW (during the save sequence, see later in the explanation of the RAP framework), since in the event of an implicit commit, database modifications would be made and we would no longer respect ACID rules (again, see the article cited above).

This is where the structure of the RAP framework comes in:

The RAP framework can be broken down into 2 main phases:

– Interaction phase

– Save sequence

During the interaction phase, data is placed in a buffer, where it can be enriched and verified.

During the save sequence, the data in the buffer can no longer be modified and is saved in the database (this is the end of the SAP LUW).

These are the rules followed by the RAP framework in the case of ABAP cloud development:

The following violations will always lead to a runtime error in a RAP context (Even if ABAP CLOUD is used or STANDARD ABAP is used) :

– Explicit use of COMMIT WORK or ROLLBACK WORK statements, as the transaction handling and database commit is exclusively handled by RAP.

– Calling the update or background task (bgPF) in the interaction phase or early-save phase.

Some other operations only lead to runtime errors, if the RAP behavior definition is implemented in ABAP for Cloud Development and the strict mode is applied:

– Invoking authorization checks in the early-save or late-save phase.

Modifying database operations in the interaction phase or early-save phase.

– Raising business events in the interaction phase or early-save phase.

(Implicit or explicit) database commits for the primary database connection in the late-save phase.

NOTE FOR STANDARD ABAP USAGE : Looking at this rules, if you’re not in the optic of clean core, you can develop in STANDARD ABAP, and perform implicit commit during late phase for example, or use IN UPDATE TASK statement, but in my case I prefer following the more stricter rules of ABAP RAP FRAMEWORK even if I’m developing in STANDARD ABAP.

THE 2 POSSIBLE SCENARIOS IN THE RAP FRAMEWORK

There are 2 types of scenarios for implementing the process: managed or unmanaged.

With the managed scenario, the SAP system automatically generates the methods and the business object. With the unmanaged scenario, everything must be developed by hand.

  • The use case for the managed scenario is as follows: Creating a new application from scratch (greenfield approach) – typically, data will be stored persistently in new Z tables. Then enhance the standard generated via validations, determinations, and actions.

This is not the case here, as we want to store/modify our data in standard tables using legacy objects (a BAPI).

  • The use case for the unmanaged scenario is as follows: Revamping an existing application (brownfield approach). The existing application provides an API that can and should be used for the implementation of interaction phase and save sequence due to its characteristics (structure, decoupling of business logic and technical aspects).

In this case, we’d have to develop everything from the phase interaction to the save sequence. We’re not interested in this implementation, as we don’t have an API to manage buffering for our needs, and would like to use the SAP standard for the interaction part.

  • In this case there is a 3rd scenario: Managed scenario with unmanaged save: Using parts of an existing application (e.g., a BAPI) in the save sequence ONLY.

Note : it exists also Managed scenario with additional save : Adding further update logic to the save sequence (e.g., in the application log) that doesn’t affect the actual business object. But we will not use it.

HOW TO USE THE BAPI IN CLOUD ABAP CONTEXT ?

SAP provides the objects it wishes to expose for cloud development by Whitelisting them. If these objects are not exposed, SAP can also propose their successors (e.g. in the case of BAPIs, if not exposed, a RAP behavior can be proposed instead).

This can be verified either directly in ADT (3), or on GitHub (4).

In our case, the desired BAPI is neither exposed nor replaced.

Following SAP guidelines (5) (6), we will create a wrapper around this BAPI, and whitelist this wrapper. When SAP proposes a successor to this BAPI, we can remove the wrapper.

To create a wrapper around a BAPI, follow this tutorial (7).

Here, we’ve created the ZCL_WRAP_DEL_HU_FAC ( and ZCL_WRAP_DEL_HU using ZIF_WRAP_BAP_HU_DEL) class, which will be used to delete HUs.

INTERFACE zif_wrap_bapi_hu_del
  PUBLIC .
  "Delivery Number
  TYPES de_number             TYPE BAPIDELICIOUSDELIVERY-DELIVERY_NUMBER.
  "HU
  TYPES de_hu                 TYPE BAPIHUKEY-HU_EXID.
  "Return table
  TYPES de_returns      TYPE bapirettab.

METHODS delete
IMPORTING iv_number type de_number
          iv_hu type de_hu
RETURNING VALUE(rt_results) TYPE de_returns.

ENDINTERFACE.
CLASS zcl_wrap_del_hu DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    INTERFACES zif_wrap_bapi_hu_del.
      PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.



CLASS zcl_wrap_del_hu IMPLEMENTATION.

 METHOD zif_wrap_bapi_hu_del~delete.
    CALL FUNCTION 'BAPI_HU_DELETE_FROM_DEL'
    EXPORTING
    delivery = iv_number
    hukey = iv_hu
    TABLES
    return = rt_results.
    .
 ENDMETHOD.

ENDCLASS.
CLASS zcl_wrap_del_hu_fac DEFINITION
  PUBLIC
  FINAL
  CREATE PRIVATE .

  PUBLIC SECTION.
      CLASS-METHODS create_instance
      RETURNING VALUE(result) TYPE REF TO zif_wrap_bapi_hu_del.
  PROTECTED SECTION.
  PRIVATE SECTION.
    METHODS constructor.

ENDCLASS.



CLASS zcl_wrap_del_hu_fac IMPLEMENTATION.

  METHOD create_instance.

    result = NEW zcl_wrap_del_hu(  ).
  ENDMETHOD.

  METHOD constructor.
  ENDMETHOD.

ENDCLASS.

And I have released the wrapper and its interface as with listed APIs

So, it can be used now in ABAP for cloud development.

RAP object definition and implementation

Now that we have our whitelisted object for use in our rap object, we’re going to start creating our business object.

I have created a package using ABAP for cloud development.

1) CDS creation

– We create a CDS Root view entity which will store the delivery number, and whether at least one HU is linked to this delivery. (I have create the private CDS view ZP_HU to find the first line of eah Handler Unit for a delivery and then I join this view in the CDS Root Entity to check if at list one HU exists).

Note here that I’m using the CDS released by sap, which replaces the standard vekp-type tables).

@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'CDS internal use HU'
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
    serviceQuality: #X,
    sizeCategory: #S,
    dataClass: #MIXED
}
define view entity ZP_HU 
as select from I_DeliveryDocument 
left outer join I_HandlingUnitHeader on I_HandlingUnitHeader.HandlingUnitPackingObjectKey = I_DeliveryDocument.DeliveryDocument 
{
    key I_DeliveryDocument.DeliveryDocument as deliveryDocument,
    min(I_HandlingUnitHeader.HandlingUnitInternalID) as hu,
    min(I_HandlingUnitHeader.HandlingUnitPackingObjectKey) as objectkey
}
group by
  I_DeliveryDocument.DeliveryDocument
having
  count(*) = 1
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Handling Units for Delivery'
define root view entity ZI_DELIV_HU as select from I_DeliveryDocument
left outer join ZP_HU on ZP_HU.deliveryDocument = I_DeliveryDocument.DeliveryDocument
{
    key I_DeliveryDocument.DeliveryDocument as DeliveryDocument,
    case
        when $projection.DeliveryDocument = ZP_HU.objectkey
          then cast ( 'X' as boole_d ) 
        else cast ( ' ' as boole_d ) 
        end                               as HUAssigned
}

– We create the associated projection view, which will be used to create the web service. The projection view reduces the business object to the simple use it will be put to in the Fiori application, whereas the ZI_DELIV_HU cds could be reused by other applications.

(Here there are no differences between the 2 cds, but there could have been).

@EndUserText.label: 'Projection view ZI_DELIV_HU'
@AccessControl.authorizationCheck: #NOT_REQUIRED
@Metadata.allowExtensions: true
define root view entity ZC_DELIV_HU as projection on ZI_DELIV_HU
{
    key DeliveryDocument,
    HUAssigned
}

Note: we could have created cds for additional value helps

2) We create the behavior definition

– We specify first the strict mode for all the necessary syntax checks, and the authorization method used (authorization check).

– Then we specify the operations that can be used: in this case, we only wish to update our business object to remove the HU assignment flag, so we specify “update”. (we’ll come back to internal).

– Next, we enrich the automatically-generated standard by adding a validation in which we use the BAPI in test mode to check that everything’s running smoothly.

– We also enrich the standard with an “unassign” action that will remove the HUs flag assigned to the CDS. This action will use the UPDATE operation internally to modify the entity, and user should not be able to modify by himself the flag : He has to use the unassign button (here the explanation why internal is specified in the definition : UPDATE could only be used internally, in the RAP business object implementation).

– finally, we specify Unmanaged save to use the BAPI in the save sequence and save the data in the database.

managed with unmanaged save implementation in class zbp_i_deliv_hu unique;
strict ( 2 );

define behavior for ZI_DELIV_HU //alias <alias_name>
//persistent table <???>
lock master
authorization master ( instance )
//etag master <field_name>
{
  //  create;
  internal update;
  //  delete;
  action unassign;
  validation validateDelivery on save { create; update; }
}

3) Create the implementation

– Creating the action

EML is used to update the object by deleting the flag. If the user attempts to remove the flag from an object already without a flag (without HUs), an error message is displayed.

– Validation creation (at save time)

Execute the BAPI in test mode to check that everything’s ok. If not, an error message is displayed.

– Create save sequence :

The BAPI is called in the last phase, at the end of the SAP LUW. Once the save sequence is complete, the RAP framework takes care of committing it.

CLASS lhc_ZI_DELIV_HU DEFINITION INHERITING FROM cl_abap_behavior_handler.
  PRIVATE SECTION.

    METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
      IMPORTING keys REQUEST requested_authorizations FOR zi_deliv_hu RESULT result.

    METHODS unassign FOR MODIFY
      IMPORTING keys FOR ACTION zi_deliv_hu~unassign.

    METHODS validateDelivery FOR VALIDATE ON SAVE
      IMPORTING keys FOR zi_deliv_hu~validateDelivery.

ENDCLASS.

CLASS lhc_ZI_DELIV_HU IMPLEMENTATION.

  METHOD get_instance_authorizations.
* Here you check authorizations
  ENDMETHOD.

  METHOD unassign.
* We read the current value of the key instances
    READ ENTITIES OF zi_deliv_hu IN LOCAL MODE
      ENTITY zi_deliv_hu
      ALL FIELDS
      WITH CORRESPONDING #( keys )
      RESULT DATA(lt_sh_for_unassign).
* We loop at the finding values to check if instances have HUs qssigned or no, If yes we report an error
    LOOP AT lt_sh_for_unassign ASSIGNING FIELD-SYMBOL(<ls_sh_for_unassign_err>) WHERE huassigned = ' '.
      reported-zi_deliv_hu = VALUE #( BASE reported-zi_deliv_hu
                                                            ( %tky = <ls_sh_for_unassign_err>-%tky
                                                              %msg = me->new_message( severity = if_abap_behv_message=>severity-error
                                                                                      id       = 'ZHU_MSG'
                                                                                      number   = '002'
                                                                                      v2 = |{ <ls_sh_for_unassign_err>-deliverydocument ALPHA = OUT }| ) ) ).

    ENDLOOP.
    DELETE lt_sh_for_unassign WHERE huassigned = ' '.
* We modify entities to delete the flag and un-assign the HUs linked to the delivery
    LOOP AT lt_sh_for_unassign ASSIGNING FIELD-SYMBOL(<ls_sh_for_unassign>).
      MODIFY ENTITIES OF zi_deliv_hu IN LOCAL MODE
          ENTITY zi_deliv_hu
          UPDATE FROM
              VALUE #( FOR sh IN lt_sh_for_unassign (
                %key = sh-%key
                deliverydocument = sh-deliverydocument
                huassigned = ' '
                %control-huassigned = if_abap_behv=>mk-on ) ).
    ENDLOOP.

  ENDMETHOD.

  METHOD validateDelivery.
* Validate here the data
* For example, if the BAPI have a test mode it is be possible to execute it in test mode and in case of error report errors
* However here it's not the case for BAPI_HU_DELETE_FROM_DEL
* It's still possible to add our own validation
  ENDMETHOD.

ENDCLASS.

CLASS lsc_ZI_DELIV_HU DEFINITION INHERITING FROM cl_abap_behavior_saver.
  PROTECTED SECTION.

    METHODS save_modified REDEFINITION.

    METHODS cleanup_finalize REDEFINITION.

ENDCLASS.

CLASS lsc_ZI_DELIV_HU IMPLEMENTATION.

  METHOD save_modified.
* We loop at the updated instances with deleted flag
    LOOP AT update-zi_deliv_hu INTO DATA(ls_update).

* In this loop we find all the HU linked to the delivery
      SELECT HandlingUnitExternalID
      FROM I_HandlingUnitHeader
      WHERE HandlingUnitPackingObjectKey = @ls_update-DeliveryDocument
      INTO TABLE @DATA(lt_hu_to_delete).

* Loop to all HUs assigned to delivery
      LOOP AT lt_hu_to_delete INTO DATA(ls_hu_to_delete).
* Call the bapi to delete the HU using the wrapper for the cloud
        DATA(lt_hu_delete) = zcl_wrap_del_hu_fac=>create_instance( )->delete(
          EXPORTING
          iv_number = ls_update-DeliveryDocument
          iv_hu = ls_hu_to_delete-HandlingUnitExternalID ).

* At this stage we can't report errors, it should has been verify that everything is fine before the save sequence
* But in case of error we still give an information message
        reported-zi_deliv_hu = VALUE #( BASE reported-zi_deliv_hu
        FOR r IN lt_hu_delete WHERE ( type = 'E' )
        ( deliverydocument = ls_update-deliverydocument
        %msg = me->new_message(
        id = r-id
        number = r-number
        severity = if_abap_behv_message=>severity-information
        v1 = r-message_v1
        v2 = r-message_v2
        v3 = r-message_v3
        v4 = r-message_v4 ) ) ).

        IF reported IS INITIAL.
          reported-zi_deliv_hu = VALUE #( BASE reported-zi_deliv_hu
                                                                ( %key = ls_update-%key
                                                                  %msg = me->new_message( severity = if_abap_behv_message=>severity-success
                                                                                          id       = 'ZHU_MSG'
                                                                                          number   = '002'
                                                                                          v1 = |{ ls_update-deliverydocument ALPHA = OUT }| ) ) ).
        ENDIF.
      ENDLOOP.
    ENDLOOP.
  ENDMETHOD.

  METHOD cleanup_finalize.
* Clean after save
  ENDMETHOD.

ENDCLASS.
CLASS lhc_ZI_DELIV_HU DEFINITION INHERITING FROM cl_abap_behavior_handler.
  PRIVATE SECTION.

    METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
      IMPORTING keys REQUEST requested_authorizations FOR zi_deliv_hu RESULT result.

    METHODS unassign FOR MODIFY
      IMPORTING keys FOR ACTION zi_deliv_hu~unassign.

    METHODS validateDelivery FOR VALIDATE ON SAVE
      IMPORTING keys FOR zi_deliv_hu~validateDelivery.

ENDCLASS.

CLASS lhc_ZI_DELIV_HU IMPLEMENTATION.

  METHOD get_instance_authorizations.
* Here you check authorizations
  ENDMETHOD.

  METHOD unassign.
* We read the current value of the key instances
    READ ENTITIES OF zi_deliv_hu IN LOCAL MODE
      ENTITY zi_deliv_hu
      ALL FIELDS
      WITH CORRESPONDING #( keys )
      RESULT DATA(lt_sh_for_unassign).
* We loop at the finding values to check if instances have HUs qssigned or no, If yes we report an error
    LOOP AT lt_sh_for_unassign ASSIGNING FIELD-SYMBOL(<ls_sh_for_unassign_err>) WHERE huassigned = ' '.
      reported-zi_deliv_hu = VALUE #( BASE reported-zi_deliv_hu
                                                            ( %tky = <ls_sh_for_unassign_err>-%tky
                                                              %msg = me->new_message( severity = if_abap_behv_message=>severity-error
                                                                                      id       = 'ZHU_MSG'
                                                                                      number   = '002'
                                                                                      v2 = |{ <ls_sh_for_unassign_err>-deliverydocument ALPHA = OUT }| ) ) ).

    ENDLOOP.
    DELETE lt_sh_for_unassign WHERE huassigned = ' '.
* We modify entities to delete the flag and un-assign the HUs linked to the delivery
    LOOP AT lt_sh_for_unassign ASSIGNING FIELD-SYMBOL(<ls_sh_for_unassign>).
      MODIFY ENTITIES OF zi_deliv_hu IN LOCAL MODE
          ENTITY zi_deliv_hu
          UPDATE FROM
              VALUE #( FOR sh IN lt_sh_for_unassign (
                %key = sh-%key
                deliverydocument = sh-deliverydocument
                huassigned = ' '
                %control-huassigned = if_abap_behv=>mk-on ) ).
    ENDLOOP.

  ENDMETHOD.

  METHOD validateDelivery.
* Validate here the data
* For example, if the BAPI have a test mode it is be possible to execute it in test mode and in case of error report errors
* However here it's not the case for BAPI_HU_DELETE_FROM_DEL
* It's still possible to add our own validation
  ENDMETHOD.

ENDCLASS.

CLASS lsc_ZI_DELIV_HU DEFINITION INHERITING FROM cl_abap_behavior_saver.
  PROTECTED SECTION.

    METHODS save_modified REDEFINITION.

    METHODS cleanup_finalize REDEFINITION.

ENDCLASS.

CLASS lsc_ZI_DELIV_HU IMPLEMENTATION.

  METHOD save_modified.
* We loop at the updated instances with deleted flag
    LOOP AT update-zi_deliv_hu INTO DATA(ls_update).

* In this loop we find all the HU linked to the delivery
      SELECT HandlingUnitExternalID
      FROM I_HandlingUnitHeader
      WHERE HandlingUnitPackingObjectKey = @ls_update-DeliveryDocument
      INTO TABLE @DATA(lt_hu_to_delete).

* Loop to all HUs assigned to delivery
      LOOP AT lt_hu_to_delete INTO DATA(ls_hu_to_delete).
* Call the bapi to delete the HU using the wrapper for the cloud
        DATA(lt_hu_delete) = zcl_wrap_del_hu_fac=>create_instance( )->delete(
          EXPORTING
          iv_number = ls_update-DeliveryDocument
          iv_hu = ls_hu_to_delete-HandlingUnitExternalID ).

* At this stage we can't report errors, it should has been verify that everything is fine before the save sequence
* But in case of error we still give an information message
        reported-zi_deliv_hu = VALUE #( BASE reported-zi_deliv_hu
        FOR r IN lt_hu_delete WHERE ( type = 'E' )
        ( deliverydocument = ls_update-deliverydocument
        %msg = me->new_message(
        id = r-id
        number = r-number
        severity = if_abap_behv_message=>severity-information
        v1 = r-message_v1
        v2 = r-message_v2
        v3 = r-message_v3
        v4 = r-message_v4 ) ) ).

        IF reported IS INITIAL.
          reported-zi_deliv_hu = VALUE #( BASE reported-zi_deliv_hu
                                                                ( %key = ls_update-%key
                                                                  %msg = me->new_message( severity = if_abap_behv_message=>severity-success
                                                                                          id       = 'ZHU_MSG'
                                                                                          number   = '002'
                                                                                          v1 = |{ ls_update-deliverydocument ALPHA = OUT }| ) ) ).
        ENDIF.
      ENDLOOP.
    ENDLOOP.
  ENDMETHOD.

  METHOD cleanup_finalize.
* Clean after save
  ENDMETHOD.

ENDCLASS.

4) We create a behavior consumption definition to reduce CRUD operations to the application if necessary (here I create the consumption but it is identical to ZI_DELIV_HU )

projection;
strict ( 2 );

define behavior for ZC_DELIV_HU //alias <alias_name>
{

  use action unassign;
}

5) We create the metadatas, adding the extra button for Unassigned HUs.

@Metadata.layer: #CORE
annotate entity ZC_DELIV_HU
    with 
{
  @UI: { lineItem: [{ position: 10, importance: #HIGH },
                    { type: #FOR_ACTION, dataAction: 'unassign', label: 'Unassign' }],
         selectionField: [{ position: 10 }] }
  DeliveryDocument;
  @UI.lineItem: [{ position: 20 },
                 { label : 'HU(s) assigned ?' }]
  @UI.selectionField: [{ position: 20 }]               
  @UI.identification:[ { label: 'HU(s) assigned ?' } ]
  @EndUserText.label: 'HU(s) assigned ?'
  HUAssigned;
}

6) Create the definition service

@EndUserText.label: 'Service def deliv hu'
define service ZUI_DELIV_HU {
  expose ZC_DELIV_HU;
}

7) create the binding service

8) All that’s left to do is create the Fiori application with VS Code or SAP BAS (8) using SAP Fiori elements

CONCLUSION

This is how I create Fiori applications using the Rap framework, taking into account the SAP guidelines for the cloud.

For more similar content, follow the ABAP Development environment Topic page (https://community.sap.com/topics/abap), post and answer questions (https://answers.sap.com/tags/833755570260738661924709785639136), and read other posts on the topic (https://blogs.sap.com/tags/833755570260738661924709785639136/)
You can also follow my profile for similar content.

(1) Learn the ABAP RESTful Application Programming Model | – by SAP PRESS (sap-press.com)

(2) https://blogs.sap.com/2022/12/05/the-sap-luw-in-abap-cloud/

(3) https://blogs.sap.com/2023/08/15/smooth-transition-to-abap-for-cloud-developmentcheat-sheet/comment-page-1/#comment-685200

(4) https://raw.githubusercontent.com/SAP/abap-atc-cr-cv-s4hc/main/src/objectReleaseInfo_PCE2022.json

(5) https://www.sap.com/documents/2023/05/b0bd8ae6-747e-0010-bca6-c68f7e60039b.html

(6) https://blogs.sap.com/2022/10/25/how-to-use-embedded-steampunk-in-sap-s-4hana-cloud-private-edition-and-in-on-premise-the-new-abap-extensibility-guide/

(7) https://developers.sap.com/tutorials/abap-s4hanacloud-purchasereq-create-wrapper.html

(8) https://blogs.sap.com/2022/10/31/fiori-development-with-vscode-and-nodejs/


文章来源: https://blogs.sap.com/2024/01/04/cloud-ready-rap-object-using-legacy-bapi-clean-core-objective/
如有侵权请联系:admin#unsafe.sh