Skip to Content
Author's profile photo Ryan Crosby

Flexible and Simple ABAP Proxies to expose BAPI interfaces

Background: You have a requirement to expose some type of BAPI for A2A integration and you don’t want to use RFC but are aiming to keep it as simple as possible.

Disclaimer: In a lot of organizations the integration expert and the ABAP developers may be segregated, but it is not the case where I am so my apologies if I end up over-simplifying this as a result.  Also, the implementation for ‘ZCL_ABAP_PROXY_MAPPER’ is complete for my requirements but may not work fully should you choose to try it so pay close attention to the typekind logic for your situation.  ** Obsolete as of NW 7.5 ** – SAP has provided MOVE-CORRESPONDING xx TO yy EXPANDING NESTED TABLES.

How it first started: Below I’ve included a fake example (for simplicity reasons) that illustrates a common occurrence for passing data to the output after executing some BAPI, but it started because I got tired of always writing these static code sections for custom implementations.  I want to pass the data back and I end up writing a bunch of static code with LOOP, APPEND, etc. to fill the output.  Sometimes, I can use MOVE-CORRESPONDING as highlighted with the comment at line 42 but only if I don’t have mismatches like lines 50-51 and that still doesn’t help avoid a mountain of static code for deeply nested structures.

Capture1.PNG

Direct Assignment is Not Possible: Foiled by the darn CONTROLLER (and even then I would have to declare ALL fields of the BAPI table structure to even try that)-

RTTS to the Rescue: A custom Z class that uses RTTS to handle both inward/outward conversion dynamically.

1. Public Section

class ZCL_ABAP_PROXY_MAPPER definition
   public
   final
   create public .

 *"* public components of class ZCL_ABAP_PROXY_MAPPER
 *"* do not include other source files here!!!
 public section.

   class-data PROXY_TO_OTHER type ZCONV_MODE value 2. "#EC NOTEXT .
   class-data OTHER_TO_PROXY type ZCONV_MODE value 1. "#EC NOTEXT .

   class-methods MAP_STRUCTURE
     importing
       !DIRECTION type ZCONV_MODE
       !INPUT type ANY
     changing
       !OUTPUT type ANY .
   class-methods MAP_TABLE
     importing
       !DIRECTION type ZCONV_MODE
       !INPUT type STANDARD TABLE
     changing
       !OUTPUT type STANDARD TABLE

2. Private Section

*"* private components of class ZCL_ABAP_PROXY_MAPPER
 *"* do not include other source files here!!!
 private section.

   class-methods READ_COMPONENTS
     importing
       !INDEX type ZCONV_MODE
       !INPUT type ANY
     changing
       !OUTPUT type ANY

3. MAP_TABLE Method

METHOD MAP_TABLE.

   DATA: wa_to TYPE REF TO data.

   FIELD-SYMBOLS: <wa_from> TYPE ANY,
                  <wa_to>   TYPE ANY.

 * add each entry of table to output
   CREATE DATA wa_to LIKE LINE OF output.
   ASSIGN wa_to->* TO <wa_to>.
   LOOP AT input ASSIGNING <wa_from>.
     CLEAR <wa_to>.
     zcl_abap_proxy_mapper=>read_components( EXPORTING
                                               index      = direction
                                               input      = <wa_from>
                                             CHANGING
                                               output     = <wa_to> ).
     APPEND <wa_to> TO output.
   ENDLOOP.

 ENDMETHOD.

4. MAP_STRUCTURE Method

METHOD MAP_STRUCTURE.

 * Read components of structure for mapping
   zcl_abap_proxy_mapper=>read_components( EXPORTING
                                             index      = direction
                                             input      = input
                                           CHANGING
                                             output     = output ).

 ENDMETHOD.

5. READ_COMPONENTS Method

METHOD READ_COMPONENTS.

   DATA: type_ref      TYPE REF TO cl_abap_typedescr,
         stru_ref_from TYPE REF TO cl_abap_structdescr,
         lt_comp_from  TYPE abap_compdescr_tab,
         wa_comp_from  TYPE abap_compdescr,
         stru_ref_to   TYPE REF TO cl_abap_structdescr,
         lt_comp_to    TYPE abap_compdescr_tab,
         wa_comp_to    TYPE abap_compdescr.

   FIELD-SYMBOLS: <from> TYPE ANY,
                  <to>   TYPE ANY.

 * Get structure reference from RTTS
   stru_ref_from ?= cl_abap_typedescr=>describe_by_data( input ).
   lt_comp_from = stru_ref_from->components.
   stru_ref_to ?= cl_abap_typedescr=>describe_by_data( output ).
   lt_comp_to = stru_ref_to->components.
   SORT lt_comp_to BY name.
   type_ref ?= stru_ref_from.

 * Always skip controller as first value
   LOOP AT lt_comp_from INTO wa_comp_from FROM index.
 *   Only perform match if names with same type kind exist in both from and to
     READ TABLE lt_comp_to INTO wa_comp_to WITH KEY name = wa_comp_from-name
                           BINARY SEARCH.
     IF sy-subrc = 0 AND
 *     Types must match exactly or both be structures
       ( ( wa_comp_to-type_kind = wa_comp_from-type_kind ) OR
         ( wa_comp_to-type_kind = type_ref->typekind_struct1 AND
           wa_comp_from-type_kind = type_ref->typekind_struct2 ) OR
         ( wa_comp_to-type_kind = type_ref->typekind_struct2 AND
           wa_comp_from-type_kind = type_ref->typekind_struct1 ) ).
 *     Peform assignment and mapping
       ASSIGN COMPONENT wa_comp_from-name OF STRUCTURE input  TO <from>.
       ASSIGN COMPONENT wa_comp_from-name OF STRUCTURE output TO <to>.
       IF <from> IS ASSIGNED AND
          <to>   IS ASSIGNED.
         IF wa_comp_from-type_kind = type_ref->typekind_table.         "Table type
           zcl_abap_proxy_mapper=>map_table( EXPORTING
                                               direction = index
                                               input     = <from>
                                             CHANGING
                                               output    = <to> ).
         ELSEIF wa_comp_from-type_kind = type_ref->typekind_struct1 OR "Either flat or nested structures
                wa_comp_from-type_kind = type_ref->typekind_struct2.
           zcl_abap_proxy_mapper=>map_structure( EXPORTING
                                                   direction = index
                                                   input     = <from>
                                                 CHANGING
                                                   output    = <to> ).
         ELSE.                                                         "Anything else - direct assignment
           <to> = <from>.
         ENDIF.
       ENDIF.
     ENDIF.
   ENDLOOP.

 ENDMETHOD.

The Result (with Earlier Sample): See the difference in the required ABAP effort.
1. Local Types

TYPES: BEGIN OF ty_request,
          customer_number       TYPE kunnr,
          sales_organization    TYPE vkorg,
          material              TYPE matnr,
          document_date         TYPE datum,
          document_date_to      TYPE datum,
          transaction_group     TYPE char01,
          purchase_order_number TYPE bstkd,
        END OF ty_request,

        BEGIN OF ty_response,
          return TYPE bapireturn,
          sales_orders TYPE sra_bapiorders_t,
        END OF ty_response.

2. Proxy Method

METHOD zii_sales_processing_in1~sales_order_dynamic_query.

   DATA: ls_request  TYPE ty_request,
         ls_response TYPE ty_response.

 * Initialize
   CLEAR: ls_request, ls_response.

 * Convert external request format to internal request format dynamically
   zcl_abap_proxy_mapper=>map_structure(
     EXPORTING
       direction = zcl_abap_proxy_mapper=>proxy_to_other
       input     = input-sales_order_query_dynamic_requ
     CHANGING
       output    = ls_request ).

 * Conversion exit for customer
   CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
     EXPORTING
       input  = ls_request-customer_number
     IMPORTING
       output = ls_request-customer_number.

 * Execute BAPI search
   CALL FUNCTION 'BAPI_SALESORDER_GETLIST'
     EXPORTING
       customer_number       = ls_request-customer_number
       sales_organization    = ls_request-sales_organization
       material              = ls_request-material
       document_date         = ls_request-document_date
       document_date_to      = ls_request-document_date_to
       transaction_group     = ls_request-transaction_group
       purchase_order_number = ls_request-purchase_order_number
     IMPORTING
       return                = ls_response-return
     TABLES
       sales_orders          = ls_response-sales_orders.

 * Convert internal response format to external response format dynamically
   zcl_abap_proxy_mapper=>map_structure(
     EXPORTING
       direction = zcl_abap_proxy_mapper=>other_to_proxy
       input     = ls_response
     CHANGING
       output    = output-sales_order_query_dynamic_resp ).

 ENDMETHOD

But Wait – What about Performance?: I tested this rigorously over the period of several days and across multiple projects for each time I had a chance to use it and I did not observe any noticeable performance differences, and in some cases even found that the dynamic examples ran faster than the static counterparts.  However, that said, I would not recommend such an approach for deeply nested structures with many levels and structures that are many, many columns wide because of the recursive algorithm.

Benefits:

1. Simplify and shorten ABAP coding effort.

2. If I start by plugging in types for all the BAPI parameters internally then new fields/structures can be made available for input or output by changing the corresponding request/response data type and merely executing a proxy regeneration.

3. If I want to expose friendlier business object names for non-SAP counterparts then I can use the mapping runtime in PI to handle such cases so I can still use dynamic RTTS internally.  I could also choose to abstract a more complicated piece of a BAPI interface such as the EXTENSIONIN table in ‘BAPI_SALESORDER_SIMULATE’ for example.

Hope some users might find this approach helpful in some way and I would always welcome feedback.

Regards,

Ryan Crosby

Assigned Tags

      Be the first to leave a comment
      You must be Logged on to comment or reply to a post.