Quantcast
Channel: SAP Gateway
Viewing all 137 articles
Browse latest View live

Step-by-Step procedure for CREATE_DEEP_ENTITY in POST operation

$
0
0

For beginners facing issues in creating deep entity for more than one item table for single header.


I will explain how to create SAP Netweaver gateway OData service which will implement  Create Deep Entity for POST operation.


This is for single header and multiple item table.

For example:Header Details:Order notification

                   Item     Details:Order component

                                         Order operation

                                         Order header

                                         Maintenance plan detail

                                                                                            


  • Create an custom RFC


2.7.jpg

  • Create project in T-code 'SEGW'
  • Create 6 entity types and entity sets

 

        Entity Type 1 -  notif

        Entity Type 2 -  ord_hdr

        Entity Type 3 -  ord_comp

        Entity Type 4 -  ord_opr

        Entity Type 5 -  mpos

        Entity Type 6 -  return(created to hold multiple messages of type BAPIRET2)

 

 

        Entity Set 1 - notifSet

       Entity Set 2 - ord_hdrSet

        Entity Set 3 - ord_compSet

        Entity Set 4 - ord_oprSet

        Entity Set 5 - mposSet

        Entity Set 6 - returnSet

1.1.jpg

 

 

  • Create Association and Navigation

       Association 1  notif_comp   (entity notif to ord_comp with cardinality 1:N)

       Association 2  notif_hdr      (entity notif to ord_hdr     with cardinality 1:1)

       Association 3  notif_mpos   (entity notif to ord_mpos with cardinality 1:1)

       Association 4  notif_opr     (entity notif to ord_opr     with cardinality 1:N)

       Association 5  notif_return  (entity notif to ord_return with cardinality 1:N)

2.5.jpg

  • Navigation property

       Navigation  1     notif_return_nav

       Navigation  2     notif_mpos_nav

       Navigation  3     notif_comp_nav

       Navigation  4     notif_opr_nav

       Navigation  5     notif_hdr_nav

 

 

1.3.jpg

 

Now let’s generate runtime artifacts. Click on generate runtime object button. It will display

popup . Keep the default class names as-is and click on enter button.

 

Once generation is successful, you will get 4 classes. 2 for Data provider and 2 for Model provider.


2.3.jpg

 

Once registration done successfully .Goto Gateway Client ( call transaction /IWFND/GW_CLIENT to open SAP NW Gateway client)

Append $metadata to base service URL and press execute button. If everything is fine then you will get HTTP

Response as below. Metadata provides information such as Entity type, key property, properties and Entity Set

name and also check service document append ?$format=xml.

 

1.5.jpg

  • DATA CLASS PROVIDER

 

      Select ***_MPC_EXT class

      Go to “Types” tab and add new type which is similar to your deep entity structure

2.4.jpg

     Type decleration :

   

types:

    BEGIN OF ts_deep_entity ,

      qmnum               TYPE qmnum,

      code                   TYPE char4,

      fllogid                  TYPE diacl_logid,

      tllogid                  TYPE diacl_logid ,

      sectref                TYPE zsectref,

      func_location       TYPE tplnr,

      erdat                   TYPE char15,

      qmnam               TYPE qmnam,

      faultcode            TYPE  zfaultcode,

      asr_gfor              TYPE  boolean,

      nildef                  TYPE  boolean,

      defcode              TYPE  zdefcode,

      def_pos              TYPE  zdef_pos,

      techlog               TYPE ztechlog,

      dd_ltext              TYPE  char200,

      dd_ltext1            TYPE  char200,

      action_text         TYPE  char200,

      action_text1       TYPE  char200,

      structural           TYPE  boolean,

      comp_replacement    TYPE  boolean,

      material_request       TYPE  boolean,

      source                      TYPE  qmgrp,

      dd_order                    TYPE  aufnr,

      dd_notif                     TYPE  qmnum,

      due_date                  TYPE  char15,

      mech                       TYPE  char10,

      inspection                TYPE  char10,

      license                    TYPE  char10,

      add_auth                 TYPE  zadd_auth,

      cross_ref                 TYPE  char25,

        werks                    TYPE werks_d,

      msg_type                TYPE   char1,

      msg_text                 TYPE   char80,

      order_no                  TYPE   aufnr ,

      notif_comp_nav      TYPE STANDARD TABLE OF ts_ord_comp

                                   WITH DEFAULT KEY,

      notif_opr_nav       TYPE STANDARD TABLE OF ts_ord_opr

                                    WITH DEFAULT KEY,

      notif_hdr_nav       TYPE  ts_ord_hdr,

      notif_mpos_nav      TYPE  ts_mpos,

      notif_return_nav TYPE STANDARD TABLE OF  ts_return

                               WITH DEFAULT KEY,

      END OF ts_deep_entity .

 

 

      Redefine the method “DEFINE” in the class.

2.2.jpg

 

Include the following code in Define method.

 

super->define( ).

DATA:

lo_annotation     TYPE REF TO /iwbep/if_mgw_odata_annotation,

lo_entity_type    TYPE REF TO /iwbep/if_mgw_odata_entity_typ,

lo_complex_type   TYPE REF TO /iwbep/if_mgw_odata_cmplx_type,

lo_property       TYPE REF TO /iwbep/if_mgw_odata_property,

lo_entity_set     TYPE REF TO /iwbep/if_mgw_odata_entity_set.

 

********************************************************************

***********************************************************

*   ENTITY - Deep Entity

********************************************************************

***********************************************************

lo_entity_type = model->get_entity_type( iv_entity_name = 'notif' ).

"#EC NOTEXT

 

 

lo_entity_type->bind_structure( iv_structure_name  =

'***CL_LI5_LBK__14_MPC_EXT=>TS_DEEP_ENTITY' ).

 

 

  • Create a custom method 'CUSTOM_CREATE_DEEP_ENTITY' in class '***CL_LI5_LBK__14_DPC_EXT'' whose parameters is similar to

       parameters of method '/IWBEP/IF_MGW_APPL_SRV_RUNTIME~CREATE_DEEP_ENTITY'.The only change we need to make is replace the type of                  'ER_DEEP_ENTITY' to the type which we specified in class '***CL_LI5_LBK__14_MPC_EXT=>TS_DEEP_ENTITY'.

 

CODE SNIPPET

 

METHOD custom_create_deep_entity.

 

 

 

 

  CONSTANTS:c_crt        TYPE    char4         VALUE    'CRT'    ,

                      c_crad      TYPE    char4         VALUE    'CRAD'   ,

                      c_u          TYPE    char1         VALUE    'U'      ,

                      c_read      TYPE    char4         VALUE    'READ'   ,

                      c_clrd       TYPE    char4         VALUE    'CLRD'   ,

                      c_clsd      TYPE    char4         VALUE    'CLSD'   ,

                      c_upd       TYPE    char3         VALUE    'UPD'    ,

                      c_a          TYPE    char1         VALUE    'A'      .

 

 

  TYPES:BEGIN OF return_msg,

        type    TYPE bapi_mtype,

        message TYPE bapi_msg,

        END   OF return_msg.

 

 

  DATA:lt_ret_msg TYPE

                 ***cl_li5_lbk__14_mpc_ext=>tt_return,

       ls_ret_msg TYPE  /tcsmro/cl_li5_lbk__14_mpc_ext=>ts_return.

 

 

  DATA:

lr_deep_entity TYPE ***cl_li5_lbk__14_mpc_ext=>ts_deep_entity,

lt_ord_comp   TYPE ***cl_li5_lbk__14_mpc_ext=>tt_ord_comp,

ls_ord_comp  TYPE ***cl_li5_lbk__14_mpc_ext=>ts_ord_comp,

lt_ord_opr      TYPE ***cl_li5_lbk__14_mpc_ext=>tt_ord_opr,

ls_ord_opr     TYPE ***cl_li5_lbk__14_mpc_ext=>ts_ord_opr,

lt_mpos         TYPE ***cl_li5_lbk__14_mpc_ext=>tt_mpos,

ls_mpos        TYPE ***cl_li5_lbk__14_mpc_ext=>ts_mpos,

lt_ord_hdr      TYPE ***cl_li5_lbk__14_mpc_ext=>tt_ord_hdr,

ls_ord_hdr      TYPE ***cl_li5_lbk__14_mpc_ext=>ts_ord_hdr,

e_msg_type   TYPE char1,

e_msg_text    TYPE char40,

lt_return         TYPE  ***cl_li5_lbk__14_mpc_ext=>tt_return,

ls_return         TYPE ***cl_li5_lbk__14_mpc_ext=>ts_return.

 

 

  DATA :

  ls_general_data TYPE ***li5_lbk_notification,

*ls_general_data TYPE ***LI5_LBK_NOTIF,

*ls_general_data TYPE ***LI5_LBK_NOTIFICATION,

  lt_ord_comp_data TYPE STANDARD TABLE OF ***li5_lbk_ord_comp,

  ls_ord_comp_data TYPE  ***li5_lbk_ord_comp,

********component to RFC structure

  lt_ord_comp_data1 TYPE STANDARD TABLE OF bapi_alm_order_component_e,

  ls_ord_comp_data1 TYPE  bapi_alm_order_component_e,

******

  lt_ord_opr_data TYPE STANDARD TABLE OF ***Li5_lbk_ord_opr,

  ls_ord_opr_data TYPE ***/li5_lbk_ord_opr,

********operation to RFC structure

*lt_ord_opr_data1 TYPE STANDARD TABLE OF BAPI_ALM_ORDER_OPERATION_E,

*ls_ord_opr_data1 TYPE  BAPI_ALM_ORDER_OPERATION_E,

  lt_ord_opr_data1 TYPE STANDARD TABLE OF bapi_alm_order_operation_e,

  ls_ord_opr_data1 TYPE  bapi_alm_order_operation_e,

 

 

 

 

******order header

  lt_ord_hdr_data TYPE STANDARD TABLE OF ***li5_lbk_ord_hdr,

  ls_ord_hdr_data TYPE ***li5_lbk_ord_hdr,

********order header to RFC structure

  lt_ord_hdr_data1 TYPE STANDARD TABLE OF bapi_alm_order_header_e,

  ls_ord_hdr_data1 TYPE bapi_alm_order_header_e,

 

 

 

 

********MPOS

  lt_mpos_data TYPE STANDARD TABLE OF ***li5_lbk_mpos,

  ls_mpos_data TYPE ***li5_lbk_mpos,

********MPOS to RFC structure

  lt_mpos_data1 TYPE STANDARD TABLE OF mpos,

  ls_mpos_data1 TYPE  mpos,

 

 

  ls_mposf    TYPE ***cl_li5_lbk__14_mpc_ext=>ts_mpos.

 

 

  FIELD-SYMBOLS:

  <ls_ord_compf> TYPE    ***cl_li5_lbk__14_mpc_ext=>ts_ord_comp,

  <ls_ord_oprf>     TYPE   ***cl_li5_lbk__14_mpc_ext=>ts_ord_opr,

  <ls_ord_hdrf>      TYPE ***cl_li5_lbk__14_mpc_ext=>ts_ord_hdr.

 

 

******message******

  DATA:

        lr_message_container  TYPE REF TO /iwbep/if_message_container,

        lv_message                 TYPE bapi_msg     ,

        lv_msgid                     TYPE symsgid      ,

        lv_msgno                    TYPE symsgno      .

*lr_message_container = me->mo_context

 

*  Transform data into the internal structure

 

  io_data_provider->read_entry_data(

  IMPORTING

  es_data = lr_deep_entity ).

***********Collect the header fields here

  ls_general_data-qmnum             = lr_deep_entity-qmnum.

  ls_general_data-code              =  lr_deep_entity-code.

  ls_general_data-aufnr             =  lr_deep_entity-order_no.

  ls_general_data-fllogid           = lr_deep_entity-fllogid .

  ls_general_data-func_location     =  lr_deep_entity-func_location.

  ls_general_data-strmn             = lr_deep_entity-erdat.

  ls_general_data-qmnam             = lr_deep_entity-qmnam.

  ls_general_data-/smr/faultcode    = lr_deep_entity-faultcode.

  ls_general_data-/smr/asr_gfor     = lr_deep_entity-asr_gfor.

  ls_general_data-/smr/nildef       = lr_deep_entity-nildef.

  ls_general_data-/smr/defcode      = lr_deep_entity-defcode.

  ls_general_data-/smr/def_pos      = lr_deep_entity-def_pos.

  ls_general_data-/smr/techlog      = lr_deep_entity-techlog.

  ls_general_data-dd_ltext          = lr_deep_entity-dd_ltext.

  ls_general_data-dd_ltext1         = lr_deep_entity-dd_ltext1.

  ls_general_data-action_ltext      = lr_deep_entity-action_text.

  ls_general_data-action_ltext1     = lr_deep_entity-action_text1.

  ls_general_data-/smr/structural   = lr_deep_entity-structural.

  ls_general_data-/smr/comp_replacement =

                               lr_deep_entity-comp_replacement.

  ls_general_data-/smr/material_request =

                                lr_deep_entity-material_request.

  ls_general_data-qmcod             = lr_deep_entity-source.

  ls_general_data-dd_order          = lr_deep_entity-dd_order.

  ls_general_data-dd_notif          = lr_deep_entity-dd_notif.

  ls_general_data-due_date          = lr_deep_entity-due_date.

  ls_general_data-/smr/mech         = lr_deep_entity-mech.

  ls_general_data-/smr/inspection   = lr_deep_entity-inspection.

  ls_general_data-/smr/license      = lr_deep_entity-license.

  ls_general_data-/smr/add_auth     = lr_deep_entity-add_auth.

  ls_general_data-/smr/sectref      = lr_deep_entity-sectref   .

  ls_general_data-plant             = lr_deep_entity-werks.

  ls_general_data-qmtxt             = lr_deep_entity-dd_ltext.

********Collect  comp fields

 

 

  LOOP AT lr_deep_entity-notif_comp_nav ASSIGNING <ls_ord_compf>.

 

 

    "WHERE  qmnum = lr_deep_entity-qmnum.

    CLEAR  ls_ord_comp_data.

    ls_ord_comp_data-qmnum          = <ls_ord_compf>-qmnum.

    ls_ord_comp_data-reserv_no      = <ls_ord_compf>-reserv_no.

    ls_ord_comp_data-res_item       = <ls_ord_compf>-res_item.

    ls_ord_comp_data-material       = <ls_ord_compf>-material.

    ls_ord_comp_data-material_desc  = <ls_ord_compf>-material_desc.

    ls_ord_comp_data-req_quan       = <ls_ord_compf>-req_quan.

    ls_ord_comp_data-unit           = <ls_ord_compf>-unit.

    ls_ord_comp_data-order_no       = <ls_ord_compf>-order_no.

    ls_ord_hdr_data1-orderid        = <ls_ord_compf>-order_no.

    APPEND  ls_ord_comp_data TO lt_ord_comp_data.

    ls_ord_comp_data1-reserv_no     = <ls_ord_compf>-reserv_no.

    ls_ord_comp_data1-res_item      = <ls_ord_compf>-res_item.

    IF NOT <ls_ord_compf>-material IS INITIAL.

      CALL FUNCTION 'CONVERSION_EXIT_MATN1_INPUT'

        EXPORTING

          input        = <ls_ord_compf>-material

        IMPORTING

          output       = <ls_ord_compf>-material

        EXCEPTIONS

          length_error = 1

          OTHERS       = 2.

    ELSE.

      CONTINUE.

    ENDIF.

    IF sy-subrc <> 0.

* Implement suitable error handling here

    ENDIF.

 

 

    ls_ord_comp_data1-material          = <ls_ord_compf>-material.

    ls_ord_comp_data1-matl_desc         = <ls_ord_compf>-material_desc.

    ls_ord_comp_data1-requirement_quantity

                                        = <ls_ord_compf>-req_quan.

 

 

    ls_ord_comp_data1-requirement_quantity_unit

                                       = <ls_ord_compf>-unit.

    ls_ord_comp_data1-item_cat         = <ls_ord_compf>-flag.

    ls_ord_hdr_data1-orderid           = <ls_ord_compf>-order_no.

 

 

    APPEND  ls_ord_comp_data1 TO lt_ord_comp_data1.

 

 

  ENDLOOP.

 

 

********Collect  oper fields

  LOOP AT lr_deep_entity-notif_opr_nav  ASSIGNING <ls_ord_oprf>.

    CLEAR  ls_ord_opr_data.

    ls_ord_opr_data-qmnum          = <ls_ord_oprf>-qmnum.

    IF NOT <ls_ord_oprf>-skill IS INITIAL.

      ls_ord_opr_data-skill          = <ls_ord_oprf>-skill.

    ELSE.

      CONTINUE.

    ENDIF.

    ls_ord_opr_data-work_cntr       = <ls_ord_oprf>-work_cntr.

    ls_ord_opr_data-work_activity   = <ls_ord_oprf>-work_activity.

    TRANSLATE <ls_ord_oprf>-unit TO UPPER CASE.

    ls_ord_opr_data-unit            = <ls_ord_oprf>-unit.

    ls_ord_opr_data-order_no        = <ls_ord_oprf>-order_no.

    ls_ord_hdr_data1-orderid        = <ls_ord_oprf>-order_no.

    ls_ord_opr_data-activity        = <ls_ord_oprf>-activity.

    ls_ord_opr_data-sub_activity    = <ls_ord_oprf>-sub_activity.

    ls_ord_opr_data-control_key     = <ls_ord_oprf>-control_key.

    APPEND  ls_ord_opr_data TO lt_ord_opr_data.

    ls_ord_opr_data1-work_cntr      = <ls_ord_oprf>-work_cntr.

    ls_ord_opr_data1-work_activity  = <ls_ord_oprf>-work_activity.

    TRANSLATE <ls_ord_oprf>-unit TO UPPER CASE.

    ls_ord_opr_data1-un_work        = <ls_ord_oprf>-unit.

    ls_ord_hdr_data1-orderid        = <ls_ord_oprf>-order_no.

    ls_ord_opr_data1-activity       = <ls_ord_oprf>-activity.

    ls_ord_opr_data1-sub_activity   = <ls_ord_oprf>-sub_activity.

    ls_ord_opr_data1-control_key    = <ls_ord_oprf>-control_key.

    ls_ord_opr_data1-sub_activity   = <ls_ord_oprf>-flag.

    IF <ls_ord_oprf>-flag = space.

      ls_ord_opr_data1-sub_activity = c_u.

    ENDIF.

    APPEND  ls_ord_opr_data1 TO lt_ord_opr_data1.

 

 

  ENDLOOP.

********Collect  MPOS fields

*  LOOP AT lr_deep_entity-notif_mpos_nav  ASSIGNING <ls_mposf>

*   WHERE  qmnum = lr_deep_entity-qmnum.

  ls_mposf = lr_deep_entity-notif_mpos_nav .

 

 

  ls_mpos_data1-/smr/dd_type    = ls_mposf-def_cat.

  ls_mpos_data1-/smr/mel_ref    = ls_mposf-mel_ref.

  ls_mpos_data1-/smr/mel_part   = ls_mposf-mel_part.

  ls_mpos_data1-/smr/mel_cat    = ls_mposf-mel_cat.

  ls_mpos_data1-/smr/etops_rat  = ls_mposf-etops_rat.

  ls_mpos_data1-/smr/plan_reqs  = ls_mposf-plan_reqs.

  ls_mpos_data1-/smr/plan_reqb  = ls_mposf-plan_reqb.

  ls_mpos_data1-/smr/plan_reqns = ls_mposf-plan_reqns.

  ls_mpos_data1-/smr/plan_dai   = ls_mposf-plan_dai.

  ls_mpos_data1-/smr/plan_reqnm = ls_mposf-plan_reqnm.

  ls_mpos_data1-/smr/plan_reqni = ls_mposf-plan_reqni.

  ls_mpos_data1-/smr/plan_reqti = ls_mposf-plan_reqti.

  ls_mpos_data1-/smr/offset1 = ls_mposf-repair_within.

 

 

  CASE ls_mposf-dd_type.

    WHEN 'P'.

      ls_mpos_data1-/smr/add_typep   = 'X'.

    WHEN 'NP'.

      ls_mpos_data1-/smr/add_typen   = 'X'.

    WHEN 'I'.

      ls_mpos_data1-/smr/add_typei   = 'X'.

    WHEN 'SC'.

      ls_mpos_data1-/smr/add_typecs  = 'X'.

    WHEN 'C'.

      ls_mpos_data1-/smr/add_typec   = 'X'.

      WHEN 'O'.

        ls_mpos_data1-/SMR/ADD_TYPEO = 'X'.

  ENDCASE.

**need to check

  ls_mpos_data1-/smr/offset2 = ls_mposf-rapair_interval.

  ls_mpos_data1-/smr/uom1  = ls_mposf-unit.

  ls_mpos_data1-plnnr = ls_mposf-plnnr.

  ls_mpos_data1-plnal =  ls_mposf-plnal.

  IF NOT ls_mpos_data1-plnnr IS INITIAL.

    ls_mpos_data1-plnty = c_a.

  ELSE.

    ls_mpos_data1-plnty = ' '.

  ENDIF.

***need to check

  ls_mpos_data1-warpl  = ls_mposf-warpl.

  ls_mpos_data1-wapos = ls_mposf-wapos.

  APPEND ls_mpos_data1 TO lt_mpos_data1.

*  ENDLOOP.

**order header

 

 

  APPEND ls_ord_hdr_data1 TO lt_ord_hdr_data1.

 

 

  DATA lv_func_name TYPE string.

  CASE lr_deep_entity-code.

    WHEN c_clrd.

      lv_func_name = '/TCSMRO/LI5_LBK_CLEAR_DEFECT'.

    WHEN c_crad.

      lv_func_name = '/TCSMRO/LI5_LBK_DEFER_DEFECT'.

    WHEN c_read.

      lv_func_name = '/TCSMRO/LI5_LBK_REASS_DEFECT'.

    WHEN c_crt.

      lv_func_name = '/TCSMRO/LI5_LBK_RAISE_DEFECT'.

    WHEN c_upd.

      lv_func_name = '/TCSMRO/LI5_UPDATE_DEFECT'.

  ENDCASE.

 

 

  IF lv_func_name IS NOT INITIAL.

    CALL FUNCTION lv_func_name

      EXPORTING

        is_notification = ls_general_data

        it_ord_comp     = lt_ord_comp_data1

        it_ord_opr      = lt_ord_opr_data1

        is_aufnr_hdr    = ls_ord_hdr_data1

        is_mpos         = ls_mpos_data1

      IMPORTING

        e_msg_type      = e_msg_type

        e_msg_text      = e_msg_text

      TABLES

        et_return       = lt_return.

  ELSEIF lr_deep_entity-code = c_clsd.

    CALL FUNCTION '/TCSMRO/LI5_CLOSE_DEFECT'

      EXPORTING

        is_notification = ls_general_data

      IMPORTING

        e_msg_type      = e_msg_type

        e_msg_text      = e_msg_text

      TABLES

        et_return       = lt_return.

 

 

  ENDIF.

  er_deep_entity-msg_text = e_msg_text.

  er_deep_entity-msg_type = e_msg_type.

 

 

  LOOP AT lt_return INTO ls_return.

    ls_ret_msg-type    = ls_return-type.

    ls_ret_msg-message = ls_return-message.

    APPEND ls_ret_msg TO lt_ret_msg.

    CLEAR:ls_return,

          ls_ret_msg.

  ENDLOOP.

  er_deep_entity-notif_return_nav = lt_ret_msg.

 

 

ENDMETHOD.

 

 

  • REQUEST PAYLOAD

 

<?xml version="1.0" encoding="UTF-8"?>

<atom:entry

xmlns:atom="http://www.w3.org/2005/Atom"

xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"

xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">

<atom:content type="application/xml">

<m:properties>

<d:Qmnum></d:Qmnum>

<d:code>CRT</d:code>

<d:FLLOGID>N102131204</d:FLLOGID>

<d:TLLOGID>TLAA077303</d:TLLOGID>

<d:FUNCLOCATION>N-5679</d:FUNCLOCATION>

<d:SECTREF>GA047101LHR20131204</d:SECTREF>

<d:Erdat>20160425</d:Erdat>

<d:Qmnam>345153</d:Qmnam>

<d:Faultcode></d:Faultcode>

<d:AsrGfor>false</d:AsrGfor>

<d:Nildef>false</d:Nildef>

<d:Defcode>2340</d:Defcode>

<d:DefPos></d:DefPos>

<d:Techlog></d:Techlog>

<d:DdLtext>symptoms</d:DdLtext>

<d:ActionText>action</d:ActionText>

<d:Structural>false</d:Structural>

<d:CompReplacement>false</d:CompReplacement>

<d:MaterialRequest>false</d:MaterialRequest>

<d:Source>MM</d:Source>

<d:DueDate></d:DueDate>

<d:Mech></d:Mech>

<d:Inspection></d:Inspection>

<d:License></d:License>

<d:AddAuth></d:AddAuth>

</m:properties>

</atom:content>

<atom:link

rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/notif_comp_nav"

type="application/atom+xml;type=feed"

title="***LI5_LBK_DEFECT_DET_SRV/ord_compset">

<m:inline>

<atom:feed>

<atom:entry>

<atom:content type="application/xml">

<m:properties>

<d:Qmnum></d:Qmnum>

<d:ReservNo></d:ReservNo>

<d:ResItem></d:ResItem>

<d:Material>622-9252-001:4V792</d:Material>

<d:MaterialDesc></d:MaterialDesc>

<d:ReqQuan>4</d:ReqQuan>

<d:Unit></d:Unit>

<d:OrderNo></d:OrderNo>

</m:properties>

</atom:content>

</atom:entry>

<atom:entry>

<atom:content type="application/xml">

<m:properties>

<d:Qmnum></d:Qmnum>

<d:ReservNo></d:ReservNo>

<d:ResItem></d:ResItem>

<d:Material>622-9252-001:4V792</d:Material>

<d:MaterialDesc></d:MaterialDesc>

<d:ReqQuan>8</d:ReqQuan>

<d:Unit></d:Unit>

<d:OrderNo></d:OrderNo>

</m:properties>

</atom:content>

</atom:entry>

</atom:feed>

</m:inline>

</atom:link>

<atom:link

rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/notif_opr_nav"

type="application/atom+xml;type=feed"

title="***LI5_LBK_DEFECT_DET_SRV/ord_oprset">

<m:inline>

<atom:feed>

<atom:entry>

<atom:content type="application/xml">

<m:properties>

<d:Qmnum></d:Qmnum>

<d:Skill>B1</d:Skill>

<d:WorkCntr>GALY10B1</d:WorkCntr>

<d:WorkActivity>11.0</d:WorkActivity>

<d:Unit>MIN</d:Unit>

<d:OrderNo></d:OrderNo>

<d:Activity></d:Activity>

<d:SubActivity></d:SubActivity>

<d:ControlKey>AM01</d:ControlKey>

<d:FLAG>U</d:FLAG>

</m:properties>

</atom:content>

</atom:entry>

<atom:entry>

<atom:content type="application/xml">

<m:properties>

<d:Qmnum></d:Qmnum>

<d:Skill>B1</d:Skill>

<d:WorkCntr>GALY10M5</d:WorkCntr>

<d:WorkActivity>12.0</d:WorkActivity>

<d:Unit>MIN</d:Unit>

<d:OrderNo></d:OrderNo>

<d:Activity></d:Activity>

<d:SubActivity></d:SubActivity>

<d:ControlKey>AM01</d:ControlKey>

<d:FLAG>U</d:FLAG>

</m:properties>

</atom:content>

</atom:entry>

</atom:feed>

</m:inline>

</atom:link>

</atom:entry>

 

 

 

NOTE:

Please note that the names you use while creating the deep structure inside your DPC_EXT class of Create_Deep_Entity method should be same as the navigation properties defined in your SEGW Model.




2.1.jpg




This is how we need to construct our payload.

Then click on execute button and see the result on right side pane.


OData Service Deployment Options for On-Premise, Mobile and Cloud Scenarios - SAP TechEd 2015 Lecture of the Week

$
0
0

Customers of SAP Business Suite can use SAP Gateway to develop OData services to modernize the user experience with SAP Fiori apps. A question very often raised by customers is where to run the components. Should they use a separate hub for SAP Gateway or is an embedded deployment the better choice where the SAP Gateway server runs on top of the SAP Business Suite application backend? Or should they leverage cloud offering and use OData provisioning with SAP HANA Cloud Platform with SAP Gateway as service?


If you are interested in getting answers to these questions you can watch the replay of this SAP TechEd 2015 lecture of the week:


OData Service Deployment Options for On-Premise, Mobile and Cloud Scenarios


 


To get the latest information about this topic I am inviting you to attend SAP TechEd 2016 and watch the sessionSAP Gateway deployment options in a nutshell

 

Those that want to get their hands dirty and see how SAP Fiori apps are build end-to-end on top of the SAP Business Suite are invited to our 4h hands-on session4h hands-on - Developing SAP Fiori Apps for Customers of SAP Business Suite


If you are more interested how to build SAP Fiori apps based on SAP S/4HANA you should visit the  4h hands-on - Building an End-to-End SAP Fiori App Based on SAP S/4HANA and ABAP or the 2h lecture - Building an End-to-End SAP Fiori App Based on SAP S/4HANA and ABAP

Consume Odata Service in ABAP CL_HTTP_CLIENT->CREATE_BY_DESTINATION (HTTP Post Method)

$
0
0

This can be considered a continuation of the earlier Blog Post i have written Consume Odata Service in ABAP CL_HTTP_CLIENT->CREATE_BY_DESTINATION. In the earlier post it is described how to trigger a GET method of the Odata Service.

 

This post would not have been possible if not for the wonderful Community that is SCN. Thank You Wouter Venhuizen , Thomas SchmidtandAshwin Dutt Rfor your cotributions. i would Award you more than 10 Points if i could. For pointing me in the right Direction. Without this i would still be stumbling, lost in the Wildlands of SCN.

 

Calling https web service POST method from ABAP | SCN.

 

Even though each question and answer I find may not be particular to the issue i am facing, it always help me channel my investigation in the right direction.

 

I hope this blog Post does the same.

 

 

The Requirement:

 

We had a recent requirement in our team where we were required to modify some Data in the HANA system from ECC.

 

There was already a HANA service that we being used by the UI to create the same data, So the solution direction was to use the same service to post Data from ECC to HANA.

 

The Solution:

 

The approach was to call a HTTP Post method from ECC using the CL_HTTP_CLIENT class.

 

the process is the same for instantiating a HTTP Client has already been described in my earlier blog.

 

The Difference between a GET method and the Post Method for Odata is the X-CSRF token handling.

 

Since the HTTP POST method is a modifying call, an X-CSRF Token has to be passed for security purposes.

More information on this Topic in the below Link

Cross-Site Request Forgery Protection - SAP Gateway Foundation (SAP_GWFND) - SAP Library

 

In one of the posts i saw related to this topic on SCN(usage of CSRF token in ABAP report for POST request), the Classes being used were unavailable in ECC because in our landscape the ECC and GATEWAY implementations are on two separate systems.

 

in the Above post once the X-CSRF Token is fetched the REST object and the HTTP Client are refreshed.

 

They are re instantiated and then the Token is used in a HTTP POST request. this for some reason was not working when i was using the Class Methods available for CL_HTTP_CLIENT.

 

the Work around implemented, was to use the same object instantiation for both the GET and POST methods.

 

  1. Instantiate the Client
  2. Fill headers and set the URI for the GET request of the same Entity for which we want to POST.
    1. While filling the headers we have to pass a header Field with name 'X-CSRF-Token' and value 'Fetch'.
  3. Trigger the  HTTP GET request,
    1. The X-CSRF token can be retrieved from the Response attribute of the HTTP Client object.
  4. Fill the headers of the request Attribute once again. but this time we set the X-CSRF token with the value retrieved from step 3, instead of "Fetch".
    1. Set the Request HTTP method to POST
    2. Fill the BODY of the HTTP request with the data that has to be modified
    3. Trigger the HTTP post request

 

Note: In between step 3 and 4 we should not refresh the HTTP Client Object that was used to fetch the X-CSRF token.

          



Detailed Code for above Steps:-

 

 

 

Preparatory Step:- Create RFC destination in SM59


(please refer to Step 1 in my earlier post) Consume Odata Service in ABAP CL_HTTP_CLIENT->CREATE_BY_DESTINATION.

 

Step1:- Instantiate the HTTP Client in ABAP


DATA:  l_query TYPE string,

       l_body TYPE string,

       l_token TYPE string,       

       l_result TYPE string.

 

DATA: lo_http_client TYPE REF TO if_http_client.

 

CONSTANTS:  c_rfchana   TYPE rfcdest VALUE 'RFCHANA', " RFC Destination

            c_query     TYPE string VALUE '/ModifyMaterial'. " Entity name

.

 

* fetch X-CSRF token

 

DATA: l_query TYPE string.    " URI Query


DATA: lo_http_client TYPE REF TO if_http_client.

 

* Create the HTTP CLient

 

  CALL METHOD cl_http_client=>create_by_destination

    EXPORTING

      destination              = C_RFCHANA

    IMPORTING

      client                   = lo_http_client

    EXCEPTIONS

      argument_not_found       = 1

      destination_not_found     = 2

      destination_no_authority = 3

      plugin_not_active            = 4

      internal_error                   = 5

      OTHERS                          = 6.

 

  IF NOT sy-subrc IS INITIAL.

  ENDIF.

 

 

STEP 2:- Fill headers and set URI for GET Method

 

* create the URI for the client.

 

  l_query = im_query.

 

  CALL METHOD cl_http_utility=>set_request_uri

    EXPORTING

      request = lo_http_client->request

      uri     = l_query.

 

 

* update the HTTP Method

  CALL METHOD lo_http_client->request->set_method

    EXPORTING

      method = lo_http_client->request->co_request_method_get.

 

 

* set Content type

  CALL METHOD lo_http_client->request->if_http_entity~set_content_type

    EXPORTING

      content_type =  'application/json'.

 

 

* set header field for fetching X-CSRF token

  CALL METHOD lo_http_client->request->set_header_field

    EXPORTING

      name  = 'X-CSRF-Token'

      value = 'Fetch'.

 

Step 3:- Trigger the GET Method

 

  lo_http_client->send(

      EXCEPTIONS

        http_communication_failure = 1

        http_invalid_state         = 2 ). "Send the HTTP request

 

 

  lo_http_client->receive(

    EXCEPTIONS

      http_communication_failure = 1

      http_invalid_state         = 2

      http_processing_failed     = 3 ). "receive the response

 

 

****GET x-csrf TOKEN from earlier response

 

CALL METHOD lo_http_client->response->get_header_field

  EXPORTING

    name  = 'X-CSRF-Token'

  RECEIVING

    value = l_token.

 

Step 4:- Fill headers and Body for HTTP POST method

* Set X-CSRF- Token in the new request.

 

CALL METHOD lo_http_client->request->set_header_field

  EXPORTING

    name  = 'X-CSRF-Token'

    value = l_token.

 

* update the HTTP Method

CALL METHOD lo_http_client->request->set_method

  EXPORTING

    method = lo_http_client->request->co_request_method_post.

 

 

****content type

CALL METHOD lo_http_client->request->set_content_type

  EXPORTING

    content_type = 'application/json'.

 

* create Body for the HTTP Post request

 

 

CALL METHOD lo_http_client->request->set_cdata

  EXPORTING

    data = l_body.

 

lo_http_client->send(

    EXCEPTIONS

      http_communication_failure = 1

      http_invalid_state         = 2 ). "Send the HTTP request

 

 

 

lo_http_client->receive(

  EXCEPTIONS

    http_communication_failure = 1

    http_invalid_state         = 2

    http_processing_failed     = 3 ). "receive the response

 

l_result = lo_http_client->response->get_cdata( ).

 

This Succesfully posts my data to HANA, and i can read the status code in the response header to check the request status.

 

Do let me know if this was helpful, as usual i am always available in case you need any further clarifications.

 

Kind regards,

Vamsi

 


Unlock your data with OData

$
0
0

Unlock your data with OData

 

Businesses have valuable data and they want to make that data available to other bodies.

However these data is often locked away based on criteria basis and applications, data can be made available to a few of services.

Now for that raises a whole new set of questions...

 

o1.PNG

Should we use SOAP?

Should we follow Rest services?

Or should it be a custom service?

 

Its important to pick the right way to share this data. Because we have to find how widely available the data is.

The data should be easily accessible to both information workers and developers.

The Odata or Open data protocol is a web protocol for querying and updating data.

Odata makes it simple to interact with data from a variety of applications and programming languages.

 

o2.PNG

Odata is built on http to ensure broad reach and sense data using ATOM or JSON

All of them of which are widely accepted web standards.

o3.PNG

Odata also supports a very advanced query language that enables filtering and ordering data based on any criteria,navigating through relations,client side paging and much more.Odata also supports advanced features like asynchronous queries and requesting just the changes since the last query.These features streamline the use of queries over large datasets.

The Protocol also supports publishers to tag and enable data with domain specific vocabularies.o4.PNG

Odata unlocks data and makes it easy to consume by both information workers and developers.

.o5.PNG

 

The value that odata puts on standardizations and query building makes it an ideal solution in exposing data.

Odata was created to provide simple,standardized way to interact with data on the web from any platform or device.

o7.PNG

 

Thank you.....

Developing data driven reusable READ functionality. Part 1

$
0
0

Introduction

If you have been working with SAP Gateway for a while you will begin to notice the pattern of defining Entities, Entitysets, Associations, etc… and then the coding pattern of redefining GET_ENTITY, GET_ENTITYSET, etc… etc...

 

In this series of blogs I’d like to introduce you to a data driven pattern which I have found quite useful for speeding up the creation of the READ functionalities of CRUD. Leaving more time to focus on the CUD of CRUD...

 

Part 2: Reusable READ functionality. Part 2

 

 

Prerequisites

Just to cover off the prerequisites before we jump into the fun stuff. The functionality shown here has been implemented on an ECC6 system running ABAP 7.31 with a centralized Gateway 7.40 system, as below.

 

ECC

ECC Status.png

 

GW

GW Status.png

 

We will be making use of the good old SFLIGHT demo model for this one. So make sure you have access to that also. Though any table / views can be used of course and as you will see quite easily.

 

The Idea

The beginnings of this idea started when defining another model in SEGW, and longing for CDS views, alas still sitting on an ABAP 7.31 system this isn’t going to happen right? It seems CDS is introduced with ABAP 7.40 SP05.

 

So why can we not use the model we have just defined in SEGW to drive the functionality of querying the entities? Good question I thought. Time to jump in and do some research/debugging.

 

By the end of this series of blogs we’ll be in a place to add the following to our redefined GET_ENTITY:

 

get_gw_helper( )->get_entity(     exporting io_tech_request_context = io_tech_request_context     changing  cs_entity = er_entity               cs_response_context = es_response_context ).

 

And this to our redefined GET_ENTITYSET:

 

get_gw_helper( )->get_entityset(      exporting io_tech_request_context = io_tech_request_context      changing  ct_entityset = et_entityset                cs_response_context = es_response_context ).

 

This will be all that’s required apart from defining the model and registering the service to get our basic query operations working in gateway.

The Design

The main component of the design is our helper class, this will handle the processing of the request. In this class we’ll be making use of the gateways model class, /iwbep/cl_mgw_odata_model. Using the model we can gain access to our tables/views, model entities, properties, referral constraints and much more. These will all aid in making a generic helper class.

 

We will also be making use of our trusted SE11 to define views when required, saving us the need to create inner joins within the code.

In a later blog we’ll add the functionality to extend our entities with properties not existing on our tables or views via the use of an interface class, and some help from SEGW.

 

 

Let’s Start

Alright enough talk, let’s start development. We’ll begin by defining the model in SEGW to see how things should be setup for our helper class to work. Then move onto the helper class and how we can interrogate the model we have just created. First we will be setting up a simple entity and its related entity set.

Our model

Open up transaction SEGW and define a new project, ZFLIGHT and save it as a local object:

 

segw_create_project.PNG

 

Next let’s create a new entity for the airline table SCARR by importing the DDIC structure:

segw_import_entity.png

 

We'll call the entity Airline, and create the default entity set:

segw_create_entity_airline.png

 

Select the attributes to include:

segw_import_airline.PNG

 

And finally the key:

segw_set_airline_key.PNG

 

Now let’s save and generate the run time objects:

segw_generate.PNG

segw_airline_struct.PNG

 

Note here we have left the ABAP fieldnames as they exist on the table, also if you take a look at the Airline entity it has a reference to the table we used to import the structure SCARR. Using these two bits of information we can being to create a generic class to handle some of our basic query operations.

 

To reach the service we will have to call our transaction to maintain services, /IWFND/MAINT_SERVICE and add our service to activate it.

addService.PNG

 

Test the service using the gateway client /IWFND/GW_CLIENT and the $metadata operator to see if our newly create service can be reached:

 

/sap/opu/odata/sap/ZFLIGHT_SRV/$metadata

 

If all is good, then you should receive back the XML from the request.

Let’s continue with our generic class.

 

 

Gateway Helper Class

Initially we will be creating a very simple helper. We will extend this over a couple of blogs to be more robust. Be careful at first though as we are implementing no paging at this stage, so be sure not to use any tables with way too many records and no $filter…

 

Open up SE80 and create a new class ZCL_GATEWAY_HELPER.

We will give this class 2 attributes to start off with.

attributes.PNG

 

  1. MR_MODEL, will keep track of the current model we are working on.
  2. MV_DB_TABNAME, will keep track of the database table or view we are working with.

 

Now lets create our methods.

constructor(), public

The constructor will take a parameter to the runtime, IR_RUNTIME. By using this we can gain access to the model via the façade as below. We will store the reference to the model in our attribute created above as it’ll be required often.

constructor.PNG

method constructor.   data: lr_facade type ref to /iwbep/cl_mgw_dp_facade.
* grab a reference to our facade to get our model reference   lr_facade ?= ir_runtime->get_dp_facade( ).
* save our model for reference later   mr_model ?= lr_facade->/iwbep/if_mgw_dp_int_facade~get_model( ).
endmethod.

 

init(), protected

This will be called both from the GET_ENTITY and GET_ENTITYSET methods of this class. Its purpose funnily enough to initialise everything ready for the next GET request. Here we really interrogate the model to get the main database table or view which we are working from and store this in the MV_DB_TABNAME attribute. The method takes one parameter, IV_ENTITY_NAME, being the current entity name being processed, from this we can grab the structure name, being the database table or view we used in SEGW.

 

init.PNG

 

METHOD init.   DATA: lv_entity_name TYPE /iwbep/if_mgw_med_odata_types=>ty_e_med_entity_name,         lr_table_entity TYPE REF TO /iwbep/cl_mgw_odata_entity_typ.
* free last table name used   FREE: mv_db_tabname.
* grab table entity   lv_entity_name = iv_entity_name.   lr_table_entity ?= mr_model->get_entity_type( lv_entity_name ).
* grab DDIC reference   mv_db_tabname = lr_table_entity->/iwbep/if_mgw_odata_re_etype~get_structure( ).
ENDMETHOD.

get_entity(), public

In this tutorial we’ll be calling this method from our gateway data provider class extension. In our case this will be class, ZCL_ZFLIGHT_DPC_EXT and method GET_ENTITY. It will have 3 parameters as below and will be responsible for requesting and returning the entity.

 

It first makes a call to out init() method mention above to get ready for the new request, this will essentially set up our mv_db_tabname attribute to be the table to select from. Next we create a data structure of our passed in entity, so we can get the values of the converted keys, followed by a call to the keys passed in. We then use these two bits of information to loop over the keys and build our where condition for the OSQL statement. Finally making the call to the OSQL statement with the predetermined values.

 

get_entity.PNG

 

method get_entity.   data: lt_keys type /iwbep/t_mgw_tech_pairs,         lv_db_where type string,         lv_db_and type string value '',         lr_data type ref to data.   field-symbols: <ls_key> like line of lt_keys,                  <ls_data> type any,                  <lv_value> type any.
* initialise   init( io_tech_request_context->get_entity_type_name( ) ).   if mv_db_tabname is not initial.
* create data struct to grab converted keys     create data lr_data like cs_entity.     assign lr_data->* to <ls_data>.     io_tech_request_context->get_converted_keys(       importing         es_key_values = <ls_data> ).     lt_keys = io_tech_request_context->get_keys( ).
* loop over keys to build where condition     loop at lt_keys assigning <ls_key>.       assign component <ls_key>-name of structure <ls_data> to <lv_value>.       if sy-subrc = 0.         lv_db_where = lv_db_where && lv_db_and && `( ` && <ls_key>-name && ` = '` && <lv_value> && `' )`.         lv_db_and = ` and `.       endif.     endloop.     if lv_db_where is not initial.       select single *         from (mv_db_tabname)         into corresponding fields of cs_entity         where (lv_db_where).     endif.   endif.
endmethod.

get_entityset(), public

Again this method will be called from our data provider class, ZCL_ZFLIGHT_DPC_EXT and method GET_ENTITYSET. It will take 3 parameters listed below and be responsible for returning the entity set. Again this function makes a call to the init() method to prepare for our new request. Makes a call to the tech request to get the converted filter, from $filter, and then calls the OSQL with the determined values.

 

get_entityset.PNG

 

method get_entityset.   data: lv_db_where type string.
* initialise   init(     exporting iv_entity_name = io_tech_request_context->get_entity_type_name( ) ).   if mv_db_tabname is not initial.
* $filter, grab our converted filter     lv_db_where = io_tech_request_context->get_osql_where_clause_convert( ).
* execute our select     select *       from (mv_db_tabname)       into corresponding fields of table ct_entityset       where (lv_db_where).   endif.
endmethod.

That’s it for our first simple iteration of this class. We’ll be doing more work here in future blogs. But for now let’s see how we can implement this into our data provider class extension, and test it out.

 

 

Data Provider Extension Class

When we hit the generate button in SEGW one of the things that happened was the generation of our data provider extension class, ZCL_ZFLIGHT_DPC_EXT. This class is the one responsible for filling out all of our entities and sets, or at least the entry point to have those entities filled out via other business classes or functions. To implement our generic helper class from here we will create one attribute in this class, as below a reference to our helper class.

 

dpc_ext_attributes.PNG

Now that's created lets create a method to return our helper class.

 

get_gw_helper(), protected

This method with return a reference with lazy instantiation to our gateway helper class.

 

dpc_ext_helper.PNG

 

method get_gw_helper.
* if our helper has not been created yet, then create...   if mr_gw_helper is not bound.     create object mr_gw_helper       exporting         ir_runtime = me.   endif.
* return instance   rr_gw_helper = mr_gw_helper.
endmethod.

 

Now we can redefine our AIRLINESET_GET_ENTITY() method and call the following:

 

method airlineset_get_entity.   get_gw_helper( )->get_entity(     exporting io_tech_request_context = io_tech_request_context     changing  cs_entity = er_entity               cs_response_context = es_response_context ).
endmethod.

 

We can also redefine our AIRLINESET_GET_ENTITYSET() method and call the following:

 

method airlineset_get_entityset.   get_gw_helper( )->get_entityset(      exporting io_tech_request_context = io_tech_request_context      changing  ct_entityset = et_entityset                cs_response_context = es_response_context ).
endmethod.

 

Activate all the changes and let’s give it a test.

 

Testing

Ensure that you have some data in your SFLIGHT tables, if not you can run program SAPBC_DATA_GENERATOR to generate some.

 

Open up the gateway client, /IWFND/GW_CLIENT and try the following:

 

/sap/opu/odata/sap/ZFLIGHT_SRV/AirlineSet()?$format=json

/sap/opu/odata/sap/ZFLIGHT_SRV/AirlineSet('AA')?$format=json

/sap/opu/odata/sap/ZFLIGHT_SRV/AirlineSet()?$filter=AirlineCurrency eq 'AUD'&$format=json

/sap/opu/odata/sap/ZFLIGHT_SRV/AirlineSet()?$filter=startswith(AirlineName, 'A')&$format=json

 

Final words

Well I hope you have enjoyed this introduction. There is still a lot more to cover in the following blogs, as we still have quite a few odata operators to take care of and you will see how these can be implemented as we move on though the series, so stay tuned.

 

I’m also happy for any feedback along the way so please let me know. I'd also be interested in any other methods of how people have been dealing with their gateway implementations.

 

I have read many a great blog,document,article and post on SCN, so thank you to everyone for sharing your knowledge, hopefully you will find mine just as useful as I have found yours.


See you soon, in part 2.

 

Reusable READ functionality. Part 2

$
0
0

Returning to our adventures in SAP Gateway, following on from my previous blog:

 

Developing data driven reusable READ functionality. Part 1

 

Let's add some more of those odata operators to our generic handler.

 

But first... we'll clean up some of those pesky compiler warnings and be good citizens by passing back any exceptions. Add the following missing exception definitions as below:

 

exceptions.png

 

Now that's done, on to the operators.

 

$select

Now let’s add support for the $select operator. This will allow us to return only properties we are interested in instead of the whole entity. We will being by defining two new custom types to store some attributes about the entity properties from the model. At the moment we are interested in storing the ABAP fieldname and if the field is defined as a key.

 

types:    begin of ty_table_field,        abap_name type fieldname,        is_key type abap_bool,      end of ty_table_field .  types:    ty_table_field_tab type hashed table of ty_table_field with unique key abap_name .

types.PNG

 

Now we have defined the types, let’s use the table type to define a class attribute to store these details.

Add a new Class attribute mt_table_fields as below.

 

table_attribute.PNG

 

init(), change

Open up the init() method and add the below changes to the code.

 

Define some new data and a field symbol:

data: lt_table_properties type /iwbep/if_mgw_med_odata_types=>ty_t_mgw_odata_properties,         lr_property type ref to /iwbep/cl_mgw_odata_property,         ls_table_field type ty_table_field.
field-symbols:  <ls_property> like line of lt_table_properties.

 

Add the mt_table_fields class attribute to the free syntax to ensure we are all good for the next run though get_entity() or get_entityset().

 

free: mv_db_tabname,        mt_table_fields.

The final code to add to the end of the method deals with retrieving the property details from the model definition we are interested, these being the abap_name and is_key.


* grab some table properties, we are interested in, currently only the
* abap name and if the property is a key   lt_table_properties = lr_table_entity->/iwbep/if_mgw_odata_entity_typ~get_properties( ).   loop at lt_table_properties assigning <ls_property>.     lr_property ?= <ls_property>-property.     ls_table_field-abap_name = lr_property->/iwbep/if_mgw_odata_re_prop~get_abap_name( ).     ls_table_field-is_key = lr_property->/iwbep/if_mgw_odata_re_prop~is_key( ).     insert ls_table_field into table mt_table_fields.   endloop.
endmethod.

process_select() - new method, protected

 

Define a new protected method process_select(). This will have two parameters one being a table of strings which have been generated from a call to io_tech_request_context->get_select_entity_properties( ) and the other a returned string for our OSQL statement.

 

process_select.PNG

 

 

NOTE: The reason for passing the string table and not io_tech_request_context is due to this method being called from both get_entity() and get_entityset(), which both have io_tech_request_context defined differently, /IWBEP/IF_MGW_REQ_ENTITY and /IWBEP/IF_MGW_REQ_ENTITYSET respectively. These both seem to resolve back to one private method /IWBEP/CL_MGW_REQUEST-> GET_SELECT_ENTITY_PROPERTIES(), though who knows what might change in the future.

 

The method is quite simplistic, if the $select operator is used then the table will be filled, we loop around this and build our select string. If the table is not filled, we simply return the select all components, *.

 

method process_select.     field-symbols: <ls_string> type string.
* if $select has been supplied, then loop over table
* and add list of attributes to our osql select string     if it_select_table is not initial.       loop at it_select_table assigning <ls_string>.         rv_db_select = rv_db_select && <ls_string> && ` `.       endloop.     else.
* else dump all properties of our entity       rv_db_select = `*`.     endif.   endmethod.                    "get_db_select

 

get_entity(), change

 

Now we make a change to get_entity() to call this new method, and make use of the returned string in our select.

 

Create a new variable in our method to store the select string:

 

data: lv_db_select type string.

Make the call to our new method:

 

if mv_db_tabname is not initial.
* $select, grab fields to select if any     lv_db_select = process_select( io_tech_request_context->get_select_entity_properties( ) ).

 

Replace the the select single * with our below line:

 

select single (lv_db_select)

 

get_entityset(), change

 

Perform the same actions as we did for get_entity() above, though this time replace the select * with:

 

select (lv_db_select)

 

Now let’s activate and test the $select operator, try the following:

 

     /sap/opu/odata/sap/ZFLIGHT_SRV/AirlineSet()?$select=AirlineId,AirlineName&$format=json

 

 

$orderby

Now it’s time to implement our ordering/sorting operator. Define some new constant attributes in the class to handle the conversion from the URI $orderby operator to the OSQL orderby syntax, as below:

 

constants.PNG

 

process_orderby(), new method, protected

Define a new method process_orderby(). This method will define two parameters, the first being the io_tech_request_context for the entityset and the second the returned string for the OSQL statement.

 

process_orderby.PNG

 

NOTE: ordering is only implemented with get_entityset() so we can easily pass the io_tech_request_context, as it will always be of type /IWBEP/IF_MGW_REQ_ENTITYSET. Unlike the definition we made for process_select() above.

 

In this method we make a call to io_tech_request_context->get_orderby() to return our ordering properties supplied on the URI. We loop over these and add them to our ordering string, converting the $orderby asc & desc keywords to the OSQL equivalent.

 

The second section of this code checks to see if the $top or $skip operator have been supplied. As per odata V2 standard, if it has we must order our data to return consistent results. So if we have not passed a $orderby with a $top or $skip then let’s default to sorting by the entities key properties, making reference to the mt_table_fields is_key we built in the init() method above.

 

method process_orderby.     data: lt_orderby type /iwbep/t_mgw_tech_order,           lv_order type string.     field-symbols: <ls_orderby> like line of lt_orderby,                    <ls_table_field> like line of mt_table_fields.     lt_orderby = io_tech_request_context->get_orderby( ).
* build order by osql string from order by uri table     loop at lt_orderby assigning <ls_orderby>.       case <ls_orderby>-order.         when mc_ascending.           lv_order = mc_sql_ascending.         when mc_descending.           lv_order = mc_sql_descending.       endcase.       rv_orderby = rv_orderby && <ls_orderby>-property && ` ` && lv_order && ` `.     endloop.
* if $top or $skip supplied and NO $orderby, then order by keys ascending     if ( rv_orderby is initial ) and        ( ( io_tech_request_context->get_top( ) > 0 ) or          ( io_tech_request_context->get_skip( ) > 0 ) ).
* loop over keys and add to ordering       loop at mt_table_fields assigning <ls_table_field> where is_key = abap_true.         rv_orderby = rv_orderby &&  <ls_table_field>-abap_name && ` ` &&  mc_sql_ascending && ` `.       endloop.     endif.   endmethod.                    "process_orderby

 

 

get_entityset(), change

Here we will create a new variable to hold the ordering string returned from our call to process_orderby().

 

data: lv_db_orderby type string.

 

Next insert our call to the new method, after the call to process_select(), passing our io_tech_request_context and receiving our ordering string.

 

* $select, grab fields to select if any     lv_db_select = process_select( io_tech_request_context->get_select_entity_properties( ) ).
* $orderby grab our order     lv_db_orderby = process_orderby( io_tech_request_context ).

 

Lastly let’s add the orderby variable to the OSQL statement after the where condition.

 

where (lv_db_where)         order by (lv_db_orderby).

 

Now active and test.

 

     /sap/opu/odata/sap/ZFLIGHT_SRV/AirlineSet()?$orderby=AirlineId desc&$format=json

 

 

 

$top and $skip

Now we have added our ordering we can add our paging, as mentioned to return consistent results with $top and $skip we need to add some ordering.

 

We’ll start by adding some new attributes to our class, the first being mc_max_top. This is fall back in case no $top has been specified then we will default to mc_max_top. This can be overridden in the call to get_entityset() as we will find out shortly. The second will be the actual maximum value we’ll be limiting to, either mc_max_top or the value passed into get_entityset().

 

page_attributes.PNG

 

 

init(), change

We’ll add a new parameter to the init() method iv_max_top to set our attribute above, just to be consistent and do all our initialization in the one spot.

 

init_param.PNG

 

Add the MV_MAX_TOP to the free statement to ensure we clear it out each time through, and add a line to set mv_max_top as below.

 

* free last table name used   free: mv_db_tabname,         mt_table_fields,         mv_max_top.
* set max top for this entityset.   mv_max_top = iv_max_top.

 

process_paging(), new method, protected

Create a new method process_paging(). We’ll add an importing parameter io_tech_request_context and two exporting parameters ev_skip and ev_top, to be used in our OSQL statement.

 

process_paging.PNG

 

First we simply grab what is being passed on the URI for $top and $skip. In the next section we make sure we have a $top greater than zero and haven’t gone over the maximum allowed. A check for $count is also added here as we do not want to limit the $top any further if someone is simply counting the number of records in a set. So leave $top as is in this case.

 

Funnily enough $count and $top can be used together, I find this strange if anyone knows why I’d love to know… eg try:

 

     http://services.odata.org/V2/Northwind/Northwind.svc/Products/$count?$top=5 

 

 

In the last section, if we are processing a $skip, then just add this value to our $top value so we return enough records during the select to cover the complete query.

 

method process_paging.
* get our $top and $skip     ev_top = io_tech_request_context->get_top( ).     ev_skip = io_tech_request_context->get_skip( ).
* force top to max allowed if not passed in, and not using $count     if ( io_tech_request_context->has_count( ) = abap_false ) and        ( ( ev_top <= 0 ) or ( ev_top > mv_max_top ) ).       ev_top = mv_max_top.     endif.
* check $skip, and add to max rows if required     if ev_skip > 0.       ev_top = ev_top + ev_skip.     endif.   endmethod.                    "process_paging

 

get_entityset(), change

Let’s add a new attribute to the get_entityset() method to allow us to pass the maximum number of records to return, iv_max_top. This allows as to define different maximums for different entities within our services.

 

get_entityset.PNG

 

Create two new variables to return the top and skip values from the call to process_paging().

 

data: lv_top type i,         lv_skip type i.

Next we will make a call to our new method above process_paging(), passing our io_tech_request_context, top and skip.

 

* $orderby grab our order     lv_db_orderby = process_orderby( io_tech_request_context ).
* $top, $skip, process our paging     process_paging(       exporting io_tech_request_context = io_tech_request_context       importing ev_top = lv_top                 ev_skip = lv_skip ).
* $filter, grab our converted filter     lv_db_where = io_tech_request_context->get_osql_where_clause_convert( ).

Finally on our select statement let’s add the syntax UP TO lv_top ROWS to limit our select to the maximum rows determined, and lastly we will add the code to handle the $skip if supplied, deleting the required number of rows from the front of the results table.

 

* execute our select       select (lv_db_select) up to lv_top rows         from (mv_db_tabname)         into corresponding fields of table ct_entityset         where (lv_db_where)         order by (lv_db_orderby).       if lv_skip > 0.         delete ct_entityset from 1 to lv_skip.       endif.

 

Activate all changes and let's test with the following examples:

 

     /sap/opu/odata/sap/ZFLIGHT_SRV/AirlineSet()?$format=json&$top=2

     /sap/opu/odata/sap/ZFLIGHT_SRV/AirlineSet()?$format=json&$top=2&$skip=2

 

 

 

$inlinecount and $count

Lastly lets add support for our counting operators.

 

get_entityset(), change

First we will place a check to see if we are processing this URI with the $count operator. If so then let’s call the OSQL with the COUNT(*) syntax and remove our $orderby and $select processing logic as it’s not needed. We will leave the paging, as we saw in the previous example above, a $top can be used with $count? We can also still supply a $filter with $count so let’s leave that process and keep it attached to the WHERE condition.

 

We finally return the results of the count back into our response context in attribute count.

 

In the second lot of changes we handle the $inlinecount operator, here we simply count the number or rows in our results set and update the inlinecount attribute of our response context.

 

So the complete method now looks as follows:

 

METHOD get_entityset.   DATA: lv_db_where TYPE string,         lv_db_select TYPE string,         lv_db_orderby TYPE string,         lv_top TYPE i,         lv_skip TYPE i.
* initialise   init(     EXPORTING iv_entity_name = io_tech_request_context->get_entity_type_name( )               iv_max_top = iv_max_top ).   IF mv_db_tabname IS NOT INITIAL.
* $top, $skip, process our paging     process_paging(       EXPORTING io_tech_request_context = io_tech_request_context       IMPORTING ev_top = lv_top                 ev_skip = lv_skip ).
* $filter, grab our converted filter     lv_db_where = io_tech_request_context->get_osql_where_clause_convert( ).
* check for $count if present just count the records
* no need to order results, or select fields!     IF io_tech_request_context->has_count( ) = abap_true.
* execute our select       SELECT COUNT(*) UP TO lv_top ROWS         INTO cs_response_context-count         FROM (mv_db_tabname)         WHERE (lv_db_where).     ELSE.
* $select, grab fields to select if any       lv_db_select = process_select( io_tech_request_context->get_select_entity_properties( ) ).
* $orderby grab our order       lv_db_orderby = process_orderby( io_tech_request_context ).
* execute our select       SELECT (lv_db_select) UP TO lv_top ROWS         FROM (mv_db_tabname)         INTO CORRESPONDING FIELDS OF TABLE ct_entityset         WHERE (lv_db_where)         ORDER BY (lv_db_orderby).       IF lv_skip > 0.         DELETE ct_entityset FROM 1 TO lv_skip.       ENDIF.
* $inlinecount, check for inline count and update       IF io_tech_request_context->has_inlinecount( ) = abap_true.         cs_response_context-inlinecount = lines( ct_entityset ).       ENDIF.     ENDIF.   ENDIF.
ENDMETHOD.

 

Let’s activate the changes and try it out:

 

 

  /sap/opu/odata/sap/ZFLIGHT_SRV/AirlineSet/$count

  /sap/opu/odata/sap/ZFLIGHT_SRV/AirlineSet/$count?$filter=startswith(AirlineId,'A')

  /sap/opu/odata/sap/ZFLIGHT_SRV/AirlineSet()?$inlinecount=allpages&$format=json

 

 

Entityset max records

We mentioned the new parameter iv_max_top above in the get_entityset() method. Now we can make use of this new parameter in the get_entityset() method from the data provider class. This will allow as to specify maximums per entityset across our service. As below:

 

zcl_zflight_dpc_ext->airlineset_get_entityset()

 

get_gw_helper( )->get_entityset(      exporting io_tech_request_context = io_tech_request_context                iv_max_top = 15      changing  ct_entityset = et_entityset                cs_response_context = es_response_context ).

 

 

In Closing

We have reach the end of another blog, thank you for following along. I hope it has all made sense so far and has been helpful.

Stay tuned for another blog or two to follow as we continue this adventure along the gateway...

 

 

Keep coding, sharing and staying happy.

Set and Get custom HTTP Header

$
0
0

This blog is to illustrate following solution which might be helpful in certain scenario:

 

     1. Set Custom HTTP Header in sapui5

     2. Get Custom HTTP Header in Gateway

 

So Let's start !

 

 

1. How to set custom HTTP Header in sapui5


This is simple and can be achieved by setHeaders method of either sap.ui.model.odata.v2.ODataModel or sap.ui.model.odata.ODataModel.

 

Code snippet:

UI5 Code.JPG

 

 

Able to see customer header sets in F12:

     F12 in Chrome.JPG

 

 

 

2. How to get custom HTTP Header in GW


Also simple by calling get_dp_facade of interface /iwbep/if_mgw_conv_srv_runtime.

 

Code snippet:

     GW Code Snipet.JPG

 

Able to see in GW code runtime:

     Header in GW.JPG

 

Now we able to set HTTP custom header in UI5 and get in GW.

This is sometimes helpful when you want to pass additional data from UI to GW but found nowhere to place them.

e.g. When implement get_entity/get_stream method, few places we can put parameters. Then maybe header is a place to consider.

 

 

Hope it helps !

Dynamic entity structure in the Odata service

$
0
0

Most of the time you will need a flexible Odata entity structure. For example, for adding customer fields or providing only fields which would be displayed in customer scenario.

 

I found this example from Aron for Media Links: http://scn.sap.com/docs/DOC-44864

But if you need it not for Stream media, but for old good abap tables, please have a look at some additional steps below:

 

1. You have to perform all steps as described by Aron (except redefining Model and Data provider class methods).

In our example, we will create model with two fields (CARRID and PLANETYPE) from SFLIGHT table:

 

2. Redefine method Define of model provider class as shown below:

001.png

3. Redefine method GET_ENTITYSET of Data provider class as shown below:

002.png

 

Now execute your service adding /Flights and you will see flights data:

Flights.PNG

 

With this, you could already build flexible Odata structure. Enjoy!


Reusable READ functionality. Part 3

$
0
0

Welcome Back

 

Welcome back again to our third installment of the series. Today we will cover off navigation and the odata $expand operator. If you haven’t followed along already check out the previous 2 blogs to find out how we ended up here, at part 3.

 

Part 1 - Developing data driven reusable READ functionality.

Part 2 - Reusable READ functionality. $select, $orderby, $top, $skip, $inlinecount, $count

 

 

SEGW

 

Let’s start with defining some new entities to use in our examples. Referring back to part 1 of this series we’ll import from DDIC structures to create our new entities. We’ll create 3 new entities, Flight (using table SFLIGHT), FlightSchedule (using table SPFLI) and Plane (using table SAPLANE). Define them as below with their Name, Key and ABAP fieldname.

 

entities.png

 

Association / Navigation / Referential Constraints

Now let’s create some associations and navigations using the wizard, as below. As part of the creation we will also setup some referential constraints. This is the information we will be using in our navigation routine to determine associations between entities.

 

02_createAssoc_sm.png

 

 

AirlineToSchedule

The first association and navigation we will create is from the Airline entity to the FlightSchedule entity. As in the image below, in the first step specify the association details and navigation property followed by the referential constraints. Use the search help to look the values up to make it a little easier. Although you will need to type the Association Name and Navigation Property (if you wish to change it as below ).

 

airline_schedule.png

 

 

ScheduleToFlight

Next we will create the association and navigation from the FlightSchedule entity to the Flight entity. Follow the same steps as above with the below image as reference.

 

schedule_flight.png

 

FlightToPlane

Finally we will create an association and navigation from the Flight entity to the Plane entity. Again follow the same steps using the image below as reference.

Though note in this last example we are setting the dependent navigation property.

 

flight_plane.png

 

Complete Model

After all the changes made the complete model should look as below in the overview section.

 

09_final_model.png

 

Now save the model and generate our runtime objects as we did in part 1. Make sure you can see your new entities and associations by running the service with the $metadata uri option:

 

     /sap/opu/odata/sap/ZFLIGHT_SRV/$metadata

 

All good? Let’s continue.

 

 

Data Provider Extension Class - ZCL_ZFLIGHT_DPC_EXT

 

Now we will redefine our data provider methods for the newly created entities.  Simply redefine the below methods of class

ZCL_ZFLIGHT_DPC_EXT and add our standard code for get_entity() and get_entityset(), as we did in part 1 of the series for the Airline entity.

 

Redefine the methods:

10_redefine_methods.png

 

 

Code for ...get_entity():

 

get_gw_helper( )->get_entity(  exporting io_tech_request_context = io_tech_request_context  changing  cs_entity = er_entity   cs_response_context = es_response_context ).

 

Code for ...get_entityset():

 

get_gw_helper( )->get_entityset(  exporting io_tech_request_context = io_tech_request_context  changing  ct_entityset = et_entityset   cs_response_context = es_response_context ).

 

 

This stems back to our original goal of adding as little as possible to our data provider extension class in the way of reading entities and allowing the helper class to do most of the work dynamically for us.

 

Finished, that’s it for the data provider class. Save the changes and activate.

 

 

Helper Class - ZCL_GATEWAY_HELPER

Now let’s add the expand and navigation logic to our helper class.

 

First we will create two new class attributes as below.

 

class attrib.PNG

 

Mv_conv_keys, will be used to determine if we should convert our source keys, mv_entity_name will be used to check if we need to run though our init() method due to a new entity, as explained below in the init() change.

 

 

Init(), change

Now we will be running though the gateway frame work executing expands. So we’ll place a check in this method to ensure that we are not loading all the model details again for the same entity. We will check the entity name passed in does not match the entity last processed.

 

Place these new lines of code after the declaration for the field symbols.

 

  field-symbols:  <ls_property> like line of lt_table_properties.
* first check if we already loaded this entity, eg we might be processing
* through a $expand.  if mv_entity_name <> iv_entity_name.

and the following new lines at the end of the method:

 

* save this for comparison on the next round though.
* if we have already computed this then lets not do it again, eg in an $expand!    mv_entity_name = iv_entity_name.  endif.
endmethod.

 

get_entity_nav(), new method, protected

The first new method we will create is a helper method to be used with our next method process_nav_path(). The purpose of this method is select our current navigation entity in readiness for our next navigation. The method will take 3 parameters the entity name, the where condition and the returned entity selected. This entity can then be used as the data for next navigations required keys. Don’t forget to add the exceptions also.

 

Method Signature

11_get_entity_sig.png

 

At the start we just free any data which may be left over from a previous call, as you’ll see in the next method the call is embedded in the navigation loop. Next we get the DDIC structure name from the entity as we have seen in previous examples. We create some data based on this DDIC structure and assign to a field symbol ready for selection. The select is then processed using the where condition passed in. An exception will be raised if no data can be selected as we’ll be unable to continue our navigation.

 

 

method get_entity_nav.  data: lr_entity type ref to /iwbep/if_mgw_med_odata_types=>ty_s_med_entity_type.  field-symbols: <ls_entity> type any.  lr_entity = mr_model->get_entity( iv_entity_name ).  free: er_entity.
*   create our data to select into  create data er_entity type (lr_entity->attribute_struct).  assign er_entity->* to <ls_entity>.
*   select entity, for navigation  select single *         into <ls_entity>         from (lr_entity->attribute_struct)         where (iv_db_where).  if sy-subrc <> 0.    raise exception type /iwbep/cx_mgw_busi_exception      exporting        textid            = /iwbep/cx_mgw_busi_exception=>business_error_unlimited        message_unlimited = 'Navigation select failed on entity, ' && iv_entity_name && '.'.  endif.
endmethod.

process_nav_path(), new method, protected

This is a big one! I’ll try to explain most of what is going on here and justifications, in a way which makes sense!

 

To start the method takes 4 parameters, these being the navigation path, the source keys, a returned parameter to determine if we are navigating and finally the OSQL where condition to be executed in the calling get_entity() or get_entityset() method. Again remember to add the exceptions.

 

 

Method Signature

11_process_nav_sig.png

Initially we check to see if there is any navigation, if not simply exit. Otherwise we continue to loop over the navigation table.

 

First iteration

In the first iteration only (defined by AT FIRST.) we build our OSQL where condition based on the source keys. The source keys will only be converted if this is the first time though a get…() method, for example not an subsequent call via an expand or navigation, as in this case the keys should already be converted.

 

To convert the keys we use the key converter contained in the navigation structure attribute KEY_CONVERTER. We do this by defining some dynamic data based on the DDIC structure and call the EXECUTE() method of the key converter, the results are returned back into our field symbol to be used in our source key loop, again only if this is the first time in a get…() method. Otherwise we simply use the keys passed into parameter it_source_keys.

 

Once the OSQL where condition has been built we call our new method above get_entity_nav() to retrieve our entity details.

 

All iterations

Continuing on from the above first / initial processing, we can either process navigation keys or process our referential constraints.

 

Navigations with keys

Following on from the first lot of processing, we check to see if we have any key fields from navigations. For example:

 

src_nav_keys.png

If we do then we convert the navigation keys using the class: /iwbep/cl_mgw_req_key_convert. We first create the key converter using the target entity type. Next create some dynamic data based on the DDIC structure of the target entity, and finally call execute() method of the key converter to convert our key values. We loop over the key tab first attempting to get the same key value from the entity details we have at hand. This is in an attempt to stop invalid navigations where keys do not match, for example:

 

keys_no_match.png

 

If the key value can be found in the entity and it does not match the value in the navigation key an exception is raised, otherwise the value is added to the OSQL where condition. If the key value cannot be found in the entity then the value is taken directly from our converted navigation keys.

 

Lastly we check if this is the last navigation, if so we want to return this OSQL where condition back to the caller to process. Otherwise we select our next entity, set the source entity to the previous target entity and continue our next loop iteration.

 

 

Referential Constraints

If we had no navigation keys then we will drop into processing the referential constraints. We set these up at the start of this blog during the association and navigation section. We first retrieve the navigation property, from this we can then pick up the association and from the association we then pick up the referential constraints.  We loop over the referential constraints retrieving the target property and matching this up with the source key property value from our entity. Using this information we can build our OSQL where condition, to be passed back to the caller.

 

Finally after the navigation loop we combine the where condition with the one passed in, being the condition from the odata $filter operator.

 

  method process_nav_path.    data: lv_tech_name type /iwbep/if_mgw_med_odata_types=>ty_e_med_technical_name,          lr_entity_type type ref to /iwbep/if_mgw_med_odata_types=>ty_s_med_entity_type,          lr_nav_prop type ref to /iwbep/if_mgw_med_odata_types=>ty_s_med_reference,          lr_assoc type ref to /iwbep/if_mgw_odata_re_assoc,          lr_assoc_ref type ref to /iwbep/if_mgw_med_odata_types=>ty_s_med_reference,          lt_ref_constraints type  /iwbep/if_mgw_odata_re_assoc=>ty_t_mgw_odata_ref_constraints,          lv_value type string,          lv_db_and type string value '',          lv_db_where type string,          lr_entity type ref to data,          lv_max_nav type i,          lv_src_entity type /iwbep/mgw_tech_name,          lr_key_values type ref to data,          lv_ddic type string,          lr_key_conv type ref to /iwbep/if_mgw_req_key_convert,          lr_etype type ref to /iwbep/if_mgw_odata_fw_etype.    field-symbols: <ls_nav_path> like line of it_nav_path,                   <ls_key_values> type any,                   <ls_keytab> type /iwbep/s_mgw_tech_pair,                   <ls_ref_constraints> like line of lt_ref_constraints,                   <ls_key> like line of it_source_keys,                   <lv_any> type any.    ev_navigating = abap_false.
* check if we are navigating    if it_nav_path is not initial.      ev_navigating = abap_true.
* get maximum number of navigations, as we what to process the
* last select from the callers method.      lv_max_nav = lines( it_nav_path ).      loop at it_nav_path assigning <ls_nav_path>.        at first.
* in our initial iteration, read our entity using our source keys
* as this is our starting point if first time through a get...()
* method then convert keys, otherwise keys should already be converted          if mv_conv_keys = abap_true.            lr_key_conv ?= <ls_nav_path>-key_converter.            lv_ddic = lr_key_conv->get_entity_type( )->get_structure( ).            create data lr_key_values type (lv_ddic).            assign lr_key_values->* to <ls_key_values>.            lr_key_conv->execute(              exporting it_tech_pair = it_source_keys              importing es_key_values = <ls_key_values>            ).          endif.          loop at it_source_keys assigning <ls_key>.
* grab key from converted source keys?            if mv_conv_keys = abap_true.              assign component <ls_key>-name of structure <ls_key_values> to <lv_any>.              lv_value = <lv_any>.            else.
* otherwise directly from source keys              lv_value = <ls_key>-value.            endif.            lv_db_where = lv_db_where && lv_db_and && `( ` && <ls_key>-name && ` = '` && lv_value && `' )`.            lv_db_and = ` and `.          endloop.
* select our entity to complete navigation          get_entity_nav(            exporting              iv_entity_name = <ls_nav_path>-source_entity_type              iv_db_where = lv_db_where            importing              er_entity = lr_entity ).          lv_src_entity = <ls_nav_path>-source_entity_type.        endat.        free: lv_db_where,lv_db_and.
* if we have navigation key then lets process them        if <ls_nav_path>-key_tab[] is not initial.
* Create a key converter here based on the target entity type and convert our keys          lr_etype = mr_model->get_entity_type( <ls_nav_path>-target_entity_type ).          free: lr_key_values, lr_key_conv.          create object lr_key_conv            type            /iwbep/cl_mgw_req_key_convert            exporting              io_entity_type = lr_etype.          lv_ddic = lr_etype->get_structure( ).          create data lr_key_values type (lv_ddic).          assign lr_key_values->* to <ls_key_values>.          lr_key_conv->execute(             exporting it_tech_pair = <ls_nav_path>-key_tab[]             importing es_key_values = <ls_key_values>          ).          loop at <ls_nav_path>-key_tab[] assigning <ls_keytab>.
*
* first attempt is to retrieve key from source entity, if it can be found use it,
* otherwise use key from keytab, this is to stop this sort of thing returning
* valid results:
*
* ZFLIGHT_SRV/AirlineSet(AirlineId='AA')/FlightScheduleSet(AirlineId='AZ',ConnectionId='555')
*                                    \------> KEYS NOT EQUAL <------- /
*            assign lr_entity->(<ls_keytab>-name) to <lv_any>.            if sy-subrc = 0.              if <lv_any> = <ls_keytab>-value.                lv_value = <lv_any>.              else.                raise exception type /iwbep/cx_mgw_busi_exception                  exporting                    textid            = /iwbep/cx_mgw_busi_exception=>business_error_unlimited                    message_unlimited = 'Key fields differ during navigation.'.              endif.            else.
* read value from our converted keys              assign component <ls_keytab>-name of structure <ls_key_values> to <lv_any>.              lv_value = <lv_any>.            endif.            lv_db_where = lv_db_where && lv_db_and && `( ` && <ls_keytab>-name && ` = '` && lv_value && `' )`.            lv_db_and = ` and `.          endloop.
* if we are on the last navigation don't process. Pass where condition back
* to our caller, get_entity() or get_entityset() to complete          if sy-tabix <> lv_max_nav.
* select our entity to continue next navigation loop            get_entity_nav(              exporting                iv_entity_name = <ls_nav_path>-target_entity_type                iv_db_where = lv_db_where              importing                er_entity = lr_entity ).            lv_src_entity = <ls_nav_path>-target_entity_type.          endif.        else.
* if no navigation keys, then try and build our navigation from
* our referential constraints          if lv_src_entity is not initial.            lr_entity_type = mr_model->get_entity( lv_src_entity ).
* get navigation property            lv_tech_name = <ls_nav_path>-nav_prop.            lr_nav_prop = mr_model->/iwbep/if_mgw_odata_fw_model~get_nav_property_by_tech(                                        iv_name = lv_tech_name                                        ir_entity_type = lr_entity_type  ).
* get association, and ref. contraints            lr_assoc_ref = mr_model->get_association_by_id( lr_nav_prop->target_entity_id ).            lr_assoc = mr_model->/iwbep/if_mgw_odata_re_model~get_association( lr_assoc_ref->name ).            lt_ref_constraints = lr_assoc->get_ref_constraints( ).
* loop over ref contraints and marry up targets to sources            loop at lt_ref_constraints assigning <ls_ref_constraints>.              assign lr_entity->(<ls_ref_constraints>-target_property-name) to <lv_any>.              if sy-subrc = 0.                lv_db_where = lv_db_where && lv_db_and && `( ` && <ls_ref_constraints>-target_property-name && ` = '` && <lv_any> && `' )`.                lv_db_and = ` and `.              endif.            endloop.          endif.        endif.      endloop.
* return our where condition, either as is, or appended to our $filter      if cv_db_where is not initial.        cv_db_where = cv_db_where && ` and ` && lv_db_where.      else.        cv_db_where = lv_db_where.      endif.    endif.  endmethod.                    "process_nav_path

 

get_entity(), change

Now time to insert our navigation processing into our get_entity() method. First we create a new variable to determine if we are navigating. Next we add the code to call our new method process_nav_path() passing the required parameters. Then we check to see if we are navigating, if so execute the returned OSQL where condition. Otherwise continue as we previously have. Finally we update our class attribute mv_conv_keys to false, check for source key conversion in our process_nav_path() method.

 

New modified method

method get_entity.  data: lt_keys type /iwbep/t_mgw_tech_pairs,        lv_db_where type string,        lv_db_and type string value '',        lv_db_select type string,        lr_data type ref to data,        lv_navigating type abap_bool.  field-symbols: <ls_key> like line of lt_keys,                 <ls_data> type any,                 <lv_value> type any.
* initialise  init( io_tech_request_context->get_entity_type_name( ) ).  if mv_db_tabname is not initial.
* $select, grab fields to select if any    lv_db_select = process_select( io_tech_request_context->get_select_entity_properties( ) ).    lt_keys = io_tech_request_context->get_keys( ).
* read association and keys    process_nav_path(      exporting it_nav_path = io_tech_request_context->get_navigation_path( )                it_source_keys = io_tech_request_context->get_source_keys( )      importing ev_navigating = lv_navigating      changing  cv_db_where = lv_db_where ).    if lv_navigating = abap_false.
* create data struct to grab converted keys      create data lr_data like cs_entity.      assign lr_data->* to <ls_data>.      io_tech_request_context->get_converted_keys(        importing          es_key_values = <ls_data> ).
* loop over keys to build where condition      loop at lt_keys assigning <ls_key>.        assign component <ls_key>-name of structure <ls_data> to <lv_value>.        if sy-subrc = 0.          lv_db_where = lv_db_where && lv_db_and && `( ` && <ls_key>-name && ` = '` && <lv_value> && `' )`.          lv_db_and = ` and `.        endif.      endloop.    endif.    if lv_db_where is not initial.      select single (lv_db_select)        from (mv_db_tabname)        into corresponding fields of cs_entity        where (lv_db_where).    endif.  endif.  mv_conv_keys = abap_false.
endmethod.

 

 

get_entityset(), change

Now let’s put the logic in our get_entityset() method. Again we will create a new variable to determine if we are navigating. Next we call our new method process_nav_path() passing the required parameters. First we leave the check for our has_count(), if we have a count we will execute with our navigated OSQL where condition. Otherwise we check to see if we are navigating, if so execute the returned OSQL where condition, else continue as we have previously. Finally again we update our class attribute mv_conv_keys to false, check for source key conversion in our process_nav_path() method.

 

New modified method

method get_entityset.  data: lv_db_where type string,        lv_db_select type string,        lv_db_orderby type string,        lv_top type i,        lv_skip type i,        lv_navigating type abap_bool.
* initialise  init(    exporting iv_entity_name = io_tech_request_context->get_entity_type_name( )              iv_max_top = iv_max_top ).  if mv_db_tabname is not initial.
* $top, $skip, process our paging    process_paging(      exporting io_tech_request_context = io_tech_request_context      importing ev_top = lv_top                ev_skip = lv_skip ).
* $filter, grab our converted filter    lv_db_where = io_tech_request_context->get_osql_where_clause_convert( ).
* read association and keys    process_nav_path(      exporting it_nav_path = io_tech_request_context->get_navigation_path( )                it_source_keys = io_tech_request_context->get_source_keys( )      importing ev_navigating = lv_navigating      changing  cv_db_where = lv_db_where ).
* check for $count if present just count the records
* no need to order results, or select fields!    if io_tech_request_context->has_count( ) = abap_true.
* execute our select      select count(*) up to lv_top rows        into cs_response_context-count        from (mv_db_tabname)        where (lv_db_where).    else.
* $select, grab fields to select if any      lv_db_select = process_select( io_tech_request_context->get_select_entity_properties( ) ).
* $orderby grab our order      lv_db_orderby = process_orderby( io_tech_request_context ).      if lv_navigating = abap_true.
* we are navigating, execute select with determined lv_db_where
* from navigation!        select (lv_db_select)            from (mv_db_tabname)            into corresponding fields of table ct_entityset            where (lv_db_where)            order by (lv_db_orderby).      else.
* execute our select        select (lv_db_select) up to lv_top rows          from (mv_db_tabname)          into corresponding fields of table ct_entityset          where (lv_db_where)          order by (lv_db_orderby).        if lv_skip > 0.          delete ct_entityset from 1 to lv_skip.        endif.      endif.
* $inlinecount, check for inline count and update      if io_tech_request_context->has_inlinecount( ) = abap_true.        cs_response_context-inlinecount = lines( ct_entityset ).      endif.    endif.  endif.  mv_conv_keys = abap_false.
endmethod.

 

 

Time to test

Activate all changes and try some of the following examples. Note some key fields may be different depending on demo data created:

 

/sap/opu/odata/sap/ZFLIGHT_SRV/AirlineSet('UA')/ToFlightSchedules?$format=json

/sap/opu/odata/sap/ZFLIGHT_SRV/AirlineSet('UA')/ToFlightSchedules?$filter=AirportFrom eq 'FRA'&$format=json

/sap/opu/odata/sap/ZFLIGHT_SRV/AirlineSet('AZ')/ToFlightSchedules(AirlineId='AZ',ConnectionId='555')?$format=json

/sap/opu/odata/sap/ZFLIGHT_SRV/AirlineSet('AZ')/ToFlightSchedules/$count?$filter=CountryFrom eq 'IT'

/sap/opu/odata/sap/ZFLIGHT_SRV/AirlineSet('AZ')?$expand=ToFlightSchedules,ToFlightSchedules/ToFlights,ToFlightSchedules/ToFlights/ToPlane&$format=json

/sap/opu/odata/sap/ZFLIGHT_SRV/FlightSet(AirlineId='AA',ConnectionId='17',FlightDate=datetime'2016-01-13T00:00:00')/ToPlane?$format=json

 

 

Final Words

Here we are at the end of another blog in the series. Hopefully you have been able to follow along and have all the examples working. Either with this set of data or your own set that you have experimented with.

 

Remember to try and use some views as well as transparent tables, this way we can make use of some inner joins with in the view, to include data from related tables.

 

Stay tuned for another blog…

Trouble shooting in HCP OData Provisioning

$
0
0

With SAP Fiori Cloud Edition  we also introduced HCP OData provisioning as a deployment option to connect to SAP Backend

You can refer Elizabeth's blog where she talks more in details about what HCP ODP, how to configure it and how to build Fiori apps on top of it.

In my blog, I shall explain how you can troubleshoot while working with this service, either as an admin or as a service developer trying to publish a new service

 

Scenario:

Register a service in HCP ODP cockpit.

When an odata request is fired there can be exception thrown from HCP ODP and exception thrown from the backend both of which can be seen in Troubleshooting tab of HCP ODP cockpit.

In HCP ODP cockpit, click on the Show Navigation menu, under which click Troubleshooting tab

 

1.PNG

 

The troubleshooting screen shows the list of transactions that failed. Every log entry is uniquely identified by a transaction id. If the backend sends a transaction id for every request, then the same transaction id is shown here. You can also search the log entries specifying the date range.

 

2.PNG

 

On clicking a specific transaction id, you enter the "log entry details" screen.

 

3.PNG

 

The first section ‘Service Details’ shows the detail of the service. Here the Backend System shown is the default destination

The second section ‘Log Entry’ shows a high level detail about the error obtained.

The third section ‘Stacktrace’ gives the detailed stacktrace of the error message

If Backend throws an exception, then an additional tab is seen in "Log Entry Details" screen

 

4.PNG

 

Click on Backend Log Entry to see deatils of backend error.

 

5.PNG

Note the transaction id seen here is the same as that seen in the troubleshooting screen

In case of backend error, the obtained SAP Note can be used to check ABAP troubleshooting note https://service.sap.com/sap/support/notes/1797736


ODATA Service to Create PR in PARK Status with ATTACHMENT at Item Level.

$
0
0


Step 1: Goto SEGW tcode and Create Project as shown below.

 

Provide Project Details.

 

Step 2: Create Two Entity Types. One for PR Header and the other for PR Item.

 

For PR Header: Provide the following details.

 

Double click on Properties and Click on Insert Row to add field.

 

Add PR Number field for PR Header as follows.

 

For PR Item: Import it from DDIC structure.

 

Provide ABAP Structure for PR Item.

 

Select all fields.

 

Maintain PR Item Number as Key field.

 

 

Step 3: Create Association between PR Header and PR Item as shown below.

 

Provide details as below.

 

 

Select Dependent Property as PreqNo.

 

 

Click on Finish.

 

The Association will be displayed as follows.

 

Step 4: Click on Generate Runtime Objects.

 

 

Runtime Classes will be created as follows.

 


Step 5: Right Click on ZCL_ZPR_CREATE_IN_PARK_DPC_EXT class and select Go to ABAP Workbench.

 

 

Step 6: Redefine PRHEADERSET_GET_ENTITY Method

 

Step 7: Redefine PRITEMSET_GET_ENTITYSET Method

 

@Note: After redefining the get_entity and get_entityset methods, then get a single record i.e Purchase requisition header with line items.

             So for that, just pass dummy data as shown in the below methods to get the xml structure.

 

Sample code(Header) :

 

method PRHEADERSET_GET_ENTITY.

 

DATA: ls_header TYPE ZCL_ZPR_CREATE_IN_PARK_MPC=>TS_PRHEADER.

 

    ls_header-preq_no = '1007426'."(Dummy PR)

    ER_ENTITY = ls_header.

 

Endmethod.

 

Sample code(Item) :

 

method PRITEMSET_GET_ENTITYSET.

 

data: ls_item type  ZCL_ZPR_CREATE_IN_PARK_MPC=>TS_PRITEM.

 

      ls_item-doc_cat = 'NB'.

      ls_item-batch = 'N'.

      ls_item-currency = 'EUR'.

 

      APPEND  ls_item to ET_ENTITYSET.

 

  endmethod.

 

Step 8: Redefine CREATE_DEEP_ENTITY Method in /IWBEP/IF_MGW_APPL_SRV_RUNTIME.

 


Step 9: Add following logic in the method.

 

  METHOD /IWBEP/IF_MGW_APPL_SRV_RUNTIME~CREATE_DEEP_ENTITY.

 

  TYPES: ty_t_PrItem TYPE STANDARD TABLE OF   ZCL_ZPR_CREATION_PARK_MPC=>TS_PRITEM WITH DEFAULT KEY.

* Represents full PR structure - header with one of more items

  TYPES: BEGIN OF ty_pr.

                   INCLUDE TYPE ZCL_ZPR_CREATION_PARK_MPC=>TS_PRHEADER.

                   TYPES: PrItemSet TYPE ty_t_PrItem,

                END OF ty_pr.

 

  DATA: ls_pr            TYPE ty_pr,

              t_item          TYPE TABLE  OF bapimereqitemimp,

              wa_item       TYPE bapimereqitemimp,

              t_itemx        TYPE TABLE OF bapimereqitemx,

              wa_itemx     TYPE bapimereqitemx,

              wa_header    TYPE bapimereqheader,

              wa_headerx  TYPE bapimereqheaderx,

              t_return         TYPE TABLE OF bapiret2,

              c_eban          TYPE dokob  VALUE 'EBAN',

              i_dradn         TYPE TABLE OF  drad_n,

              w_dradn       TYPE drad_n,

              lv_objky       TYPE objky.

 

io_data_provider->read_entry_data( IMPORTING es_data = ls_pr ).

 

* Assigns the Header details

  wa_header-pr_type            = 'NB'.

  wa_header-create_ind        = 'R'.

  wa_header-item_intvl        = '10'.

wa_header-memory            = 'X'.

  wa_header-park_complete = 'X'.

  wa_header-memorytype     = 'X'. (pass 'X' to these three values to create PR in PARK status)

 

* Assigns the Header fields to be updated

  wa_headerx-pr_type             = 'X'.

  wa_headerx-create_ind         = 'X'.

  wa_headerx-item_intvl         = 'X'.

  wa_headerx-memory            = 'X'.

  wa_headerx-park_complete = 'X'.

  wa_headerx-memorytype     = 'X'.

 

* Assigns the Item details

  wa_item-preq_item    = '10'.

  wa_item-pur_group    =  'Z00'.

  wa_item-short_text     = '5-SPEED GEARS'.

  wa_item-material        = 'AM3-400-N'.

  wa_item-plant             = '1000' .

  wa_item-matl_group   = '001'.

  wa_item-quantity        = 12.

  wa_item-preq_price    = 20.

  wa_item-price_unit     = 1.

  wa_item-commitment = 'X'.

  APPEND wa_item TO t_item.

 

* Assigns the Item fields to be updated

  wa_itemx-preq_item     = '00010'.

  wa_itemx-pur_group    = 'X'.

  wa_itemx-short_text     = 'X'.

  wa_itemx-material        = 'X'.

  wa_itemx-plant             = 'X' .

  wa_itemx-matl_group   = 'X'.

  wa_itemx-quantity        = 'X'.

  wa_itemx-preq_price    = 'X'.

  wa_itemx-price_unit     = 'X'.

  wa_itemx-commitment = 'X'.

  APPEND wa_itemx TO t_itemx.

 

* Create the PR in PARK status using BAPI

  CALL FUNCTION 'BAPI_PR_CREATE'

    EXPORTING

      prheader      = wa_header

      prheaderx    = wa_headerx

    TABLES

      return          = t_return

      pritem         = t_item

      pritemx       = t_itemx

    EXCEPTIONS

      OTHERS    = 1.

 

* To Commit

CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'.

 

* To Read PR Number

DATA(ts_return) = t_return[ 1 ].

ls_pr-preq_no = ts_return-message.

 

* To Read Item Number of PR

DATA(w_item) = t_item[ 1 ].

 

* Concatenate PR number with Item number

lv_objky = |{ '00' }| && |{ ts_return-MESSAGE_V1 }| && |{ w_item-PREQ_ITEM }|.

 

* To fetch DMS Document number based on Quotation ID

SELECT SINGLE DMS_DOC_ID from ZQUOT_DETAILS INTO @data(lv_dms_no) WHERE QUOT_ID = '1234'.

 

  w_dradn-DOKAR  = 'DOC'.

*  w_dradn-DOKNR = lv_dms_no.

  w_dradn-DOKNR = '10000000212'.

  w_dradn-dokvr      = '00'.

  w_dradn-doktl       = '000'.

  w_dradn-dokob     = c_eban.

  w_dradn-obzae      = '0000'.

  w_dradn-objky      = lv_objky.

  APPEND w_dradn  TO i_dradn.

 

* FM to attach DMS document to the item of created PR

CALL FUNCTION 'DOKUMENTZUORDNUNGEN_BUCHEN'

      IN UPDATE TASK

      EXPORTING

        ob         = c_eban

        objky    = lv_objky

      TABLES

        savdrad = i_dradn.

 

* Commit

     CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'

      EXPORTING

        wait = 'X'.

 

      REFRESH:  i_dradn.

 

* To display the Message

    copy_data_to_ref(

    EXPORTING

     is_data =  ls_pr"t_return

   CHANGING

     cr_data = er_deep_entity

).

  endmethod.

 

***Service Maintainance & Registration******

Step 10: Goto Tcode /n/iwfnd/maint_service and Add Service.

 

 

 

 

Step 11: Provide System Alias and Click on Get Services.

               Select Our Created Service and click on Add Selected Service.

 

 

 

Step 12: Go Back and Click on SAP Gateway Client.

 

 

 

Step 13: Provide the following URL, Select GET and click on EXECUTE to get the sample XML of PR.

 


Step 14: Click on Use as Request and then select POST and EXECUTE it.

 

 

 

PR will be Created Successfully in PARK Status along with an attachment in Item Level.

 


Step 15: Goto ME51N tcode and Provide the Created PR Number.

               So as shown it is created in PARKED status.

 

You can also check it in EBAN table.(MemoryType value will be "P" for the above Purchase Requisition Number).

 

 

Select Item of  PR and click on Documents Icon to display the attached Document.

 

 

Thanks & Regards,

Naveena M.

How to implement custom dynamic system alias calculation in SAP Gateway

$
0
0

Introduction

 

The standard routing used by the destination finder can be based on user role assignments and host names of the OData client calling the service. It has however turned out that some customers have requirements for the routing that cannot be implemented by just assigning roles.

The standard routing based can thus be enhanced by custom rules. These rules can be implemented in a BadI.

 

This option is described in the SAP Online Help Dynamic System Alias Calculation Via /IWFND/ES_MGW_DEST_FINDER - SAP NetWeaver Gateway - SAP Library. What was missing so far was a simple how to guide that showed how to perform the implementation steps.

 

You can find more detailed information about system aliases in the following blog All About System Alias and Routing of Requests in SAP NetWeaver Gateway and about routing and multi origin in my SCN document Support of multiple backend systems - How to use Multi Origin Composition and Routing

Implementation of the enhancement spot

 

For a detailed description how to implement a BadI see the SAP Online Help: How to Implement a BAdI - Enhancement Framework - SAP Library

 

  • Start transaction SE80 and search for the enhancement spot /IWFND/ES_MGW_DEST_FINDER

 

01 show enhancement spot.png

  • Start to create a BadI implementation by pressing the Create Enhancement Implementation pushbutton.

 

02 create badi implementation.png

 

 

  • In the next screen enter the following values for the enhancement implementation

    03 create badi implementation enter values.png

  • and now you can enter the values for the implementing class for your BAdI impelmentation
    03 create badi implementation 03.png
  • Now you can start to implement the class

    implement_class.png

 

 

We will use the following (very simplified) logic to determine system aliases for our users:

 

 

method /iwfnd/if_mgw_dest_finder_badi~get_system_aliases.

 

     if iv_service_id = 'ZGWSAMPLE_BASIC_0001'.

       case iv_user.

         when 'USER1'.

           delete  ct_system_aliases where system_alias <> 'ERP_1'.

         when 'USER2'.

           delete  ct_system_aliases where system_alias <> 'ERP_2'.

         when others.

           delete  ct_system_aliases where system_alias = 'ERP_1'.

           delete  ct_system_aliases where system_alias = 'ERP_2'.

       endcase.

     endif.

 

   endmethod.

 

 

  • For the service GWSAMPLE_BASIC use the following assignments of system aliases using transaction /n/IWFND/MAINT_SERVICE

    MAINT_SERVICE_SETTINGS.png

 

How does the destination finder works ?

 

 

If USER1 calls the service the destination finder will determine just one system alias, namely ERP_1.

If USER2 calls the service the destination finder will determine also just one system alias, namely ERP_2.

If any other user, for example a user USER3 calls the service the destination finder will determine two system alias entries, namely ERP_3 and ERP_4 where ERP_3 is marked as a default system.

 

Please note that we have assigned no roles since we want to determine the system aliases via custom code shown above.

 

How routing works can best be demonstrated by using the multi origin option to see which system aliases are selected by the destination finder. When three different users USER1, USER2 and USER3 are calling the service GWSAMPLE_BASIC  using the following URI:

 

/sap/opu/odata/IWBEP/GWSAMPLE_BASIC;mo/ProductSet?$filter=ProductID eq 'HT-1000'&$select=SAP__Origin,ProductID&$format=json

 

they would retrieve the following data:

 

Userhttp response
USER1

{

  "d" : {

    "results" : [

      {

        "__metadata" : {        },

        "SAP__Origin" : "ERP_1",

        "ProductID" : "HT-1000"

      }

    ]

  }

}

USER2

{

  "d" : {

    "results" : [

      {

        "__metadata" : {

        },

        "SAP__Origin" : "ERP_2",

        "ProductID" : "HT-1000"

      }

    ]

  }

}

USER3

{

  "d" : {

    "results" : [

      {

        "__metadata" : {

        },

        "SAP__Origin" : "ERP_3",

        "ProductID" : "HT-1000"

      },

      {

        "__metadata" : {

        },

        "SAP__Origin" : "ERP_4",

        "ProductID" : "HT-1000"

      }

    ]

  }

}

 

Please note that USER3 would retrieve data from two backend systems (ERP_3 and ERP_4) in case the multi origin option is used.

 

If this user would try to retrieve data from the same entity set without the multi-origin option the routing would use the destination ERP_3 which has been marked as default.

 

For USER1 and USER2 only one system alias was determined so the choice of the system alias is unique anyway.

 

Please also note that if no system alias is flagged as default the behavior is undetermined. You will find entries in the error log such as

 

No System Alias flagged as "Default" for Service 'ZGWSAMPLE_BASIC_0001' and user 'USER9'

 

I hope this will help you if you have to implement a custom dynamic system alias calculation in SAP Gateway.

 

Best Regards,

Andre

SAP NetWeaver Gateway with Microsoft Power BI

$
0
0

If you have “one” time designed aka rigid SAP reporting solution, I am sure you would have come across below requests from your business users.

 

“I need to analyse this new! data quickly..”

“I don’t have time to write spec’s, wait for data to be loaded and wait for developer to build the report..”

“Our business across regions use different data authoring tools..”

“I need to create interactive visuals and our senior management should be able to view in their tablets in the next hour..”

 

A good alternative to head-on such circumstances could be to use SAP NetWeaver Gateway i.e. to expose SAP data as oData. oData being open standard means, the data could then be consumed with multitude of 3rd party visualisation tools. In below scenario I created a NW Gateway Service to expose Warehouse Orders data so the reporting user can consume this oData Service by using Microsoft Power BI for data insight and interactive visualisation.

The solution took less then 45 mins! to build

 

1.JPG

Below are the steps:

 

Build RFC function module to expose the business data. In my case, I exposed Warehouse Orders so simple select on Warehouse Order table was sufficient. (20 mins). Steps to build/test service is outlined in the blog: http://goo.gl/JCTDEX

2.png

Test the service you have built to make sure all the metadata and results are displayed

(2 mins)

3.jpg


After the service is built, the reporting users can consuming the oData Service. Open Microsoft Power BI and access service through oData feed.

Once logon is successful, the available fields with data are shown. (2 mins). For more info. on Power BI: https://powerbi.microsoft.com

4.png


Now reporting users can go-crazy with their data insight ingenuity. Some basic interactive visualisations I have created are shown below (15-20 mins each!)


5.png

Further, these data visualisations can then be published to Web i.e. Office365/SharePoint to be consumed by the user community or using Power BI App users can open the responsive visualisations on their mobile devices.

         

Some points to note:

  • Replace leaden traditions: SAP data download to excel, format in excel and again upload to “a” visualisation tool.
  • Set of records: Not solving big data issues here, we have  a set of records that need to be visualised quickly!
  • Open data: Data is exposed as oData feed, meaning keeping people with different tastes.. visualisation tools happy!
  • TAT (Turn-around-Time): Quick from inception to visualising report and on a mobile device!
  • Licenses: User your SAP user ID to access oData, Power BI comes along with MS/Office 365 (check options & pricing)

Reusable READ functionality. Part 4

$
0
0

Welcome back

This is our fourth and final in this series of developing reusable READ functionality.


Over the past three blogs we have covered off the idea of a data driven design mostly driven from the setup we have made in SEGW. We have added the functionality to handle various odata operators $select, $orderby, $filter, $expand, $top, $skip, $count, $inlinecount.


Part 1 - Developing data driven reusable READ functionality.

Part 2 - Reusable READ functionality. $select, $orderby, $top, $skip, $inlinecount, $count

Part 3 - Reusable READ functionality. $expand and navigation

 

In this final blog I want to cover off the ability to extend our entity types using SEGW and an interface class as mentioned in the first blog post of the series. There will be many times additional information is required on top of data we have in our tables and views. Obtaining this will most likely requiring some additional processing and here is how we can accomplish this.

 

SEGW

To implement the ability to extend our entities with our own custom properties we’ll make use of the "Base Entity" feature of SEGW. This will allow us to create a base entity based on the data dictionary (table/view) and inherit this into our new extended entity. The extended entity will then define our required properties which do not exist on the table or view.

 

For this example I’ve implemented a new project in SEGW, ZFLIGHT_EXTEND. This is mainly to cleanly show what’s required. But of course this can be added to the previous project we have been working with in the previous 3 blogs.

 

Note defining base entities is not required if only using properties which exist on the table/view. We only need to define the base entity and its inheritance if we want to enhance the table / view with additional information. For example in this blog we’ll enhance the Airline entity to obtain the favicon from the domain and the exchange rate, and the FlightSchedule entity with the airline name and URL.

 

Entities

Let’s start by adding our entities. We’ll create 4 entities in this example.

 

  1. AirlineBase,this will be our base entity imported from DDIC table SCARR
  2. Airline, this will be our extended entity, inheriting from AirlineBase
  3. FlightScheduleBase, again our base entity imported from DDIC table SPFLI
  4. FlightSchedule, this will be our extended entity, inheriting from FlightScheduleBase

 

Define the entities as we have in the past, importing the DDIC structure for the base entities and defining their keys. Note only the base entities require keys here as the keys will be inherited into the child entities.

 

Note the base entities are flagged as abstract, and the base type name has been specified where required for Airline and FlightSchedule entities.

 

Filter and Sort flags

You will see in the below screen shot we have also marked the flags for Sorting and Filtering, we'll be adding logic later to ensure these are checked before proceeding with our odata request.

 

entity_props.png

 

ETAG

We have also included an "etag" property in the FlightSchedule entity. This will also need to be mentioned against the entity types as below. Again we will add code to include MD5 hash calculation of the etag property if present.

 

etag_flagged.PNG

 

Requires Filter

Lastly lets check the requires filter for the entityset AirlineSet as below. We will add the code and be able to test this once done.

 

requiresfilter.PNG

 

Complete Model

complete_model.PNG

 

 

Generate and Activate

Now we can generate the runtime objects for the model. Note if you did create a new model refer back to the first blog in the series and make sure you maintain the service with transaction /IWFND/MAINT_SERVICE and activate it.

 

Now ensure the new entities are visible calling the service with the $metadata operator.

 

     /sap/opu/odata/sap/ZFLIGHT_SRV/$metadata

 

 

ZIF_GATEWAY_HELPER_EXTEND, New interface

Here we create a simple interface class to be used when extending our entities with custom ABAP routines when required. The interface will have two methods which will need to be implemented when creating a new class of this interface type. These will be the GET_ENTITY and GET_ENTITYSET, called from the respective processing routines of the helper class during a request for data.

 

Define the Methods as below

interface_methods.png

 

 

get_entity() signature

get_entity.png

 

get_entityset() signature

get_entityset.png

 

 

Exceptions for both get_entity() and get_entityset()

exceptions.png

 

 

ZCL_GATEWAY_HELPER

To facilitate these model changes we will have to enhance our helper class. The main reason for the changes is to check whether the attributes belong to our base entity (AirlineBase) or are coming from the extended entity (Airline). As we are building a dynamic select we need to ensure we are only building it with those properties on our base entity type. The additional properties in our inherited/extended entity will be fulfilled with logic from our extend interface.

 

 

Class types

We’ll begin by adding two new types to our class, used later in our class attributes to keep track of our extended properties. We will also update the old type ty_table_field to keep track of our sort and filter options, as well as its external name.

 

Add the following types and update:

types:    begin of ty_table_field,        abap_name type fieldname,        is_key type abap_bool,        external_name type string,        is_filterable type abap_bool,        is_sortable type abap_bool,      end of ty_table_field .  types:    ty_table_field_tab type hashed table of ty_table_field with unique key abap_name .  types:    begin of ty_extended_field,          abap_name type fieldname,          external_name type string,          is_sortable type abap_bool,        end of ty_extended_field .  types:    ty_extended_field_tab type hashed table of ty_extended_field with unique key abap_name .

 

Class Attributes

Now we can add some required class attributes.

 

1. MV_POST_SORT, will be used to determine if we need to post sort our results. This would be the case if the $orderby operator was used for one of the extended properties.

 

2. MV_FILTER_REQUIRED, we’ll use this one to add a little functionality to check the $filter operator has been passed for required entities, as we set above on the entity set AirlineSet

 

3. MT_EXTENDED_FILEDS, this will be filled with the extended fields of our entity, our base fields will be held in MT_TABLE_FIELDS.

 

4. MV_ETAG, store our etag property if we have one.

 

New Attributes added to class

 

class_attrb.png

 

 

init(), change

The changes we make here will be to load up the extended entities if inheriting from a base entity type.

First we check to see if this request requires a $filter to be passed, as flagged in SEGW. At the moment we just keep track of this in our new class attribute.

 

Second we attempt to get the base type of the entity. If there is one then we set our class attribute MV_DB_TABNAME (table name) to the structure of the base entity and then load up our class attribute MT_EXTENDED_FIELDS with the extended properties. Otherwise we load the class attribute MV_DB_TABNAME with the structure name of this entity and continue as normal, loading up the table attributes into MT_TABLE_FIELDS.

 

We also store the etag property name here if one exists. We will use this in the next method to populate this property with the MD5 hash of the complete entity data.

 

Complete method

 

method init.  data: lv_entity_name type /iwbep/if_mgw_med_odata_types=>ty_e_med_entity_name,        lr_table_entity type ref to /iwbep/cl_mgw_odata_entity_typ,        lt_table_properties type /iwbep/if_mgw_med_odata_types=>ty_t_mgw_odata_properties,        lr_property type ref to /iwbep/cl_mgw_odata_property,        ls_table_field type ty_table_field,        lv_fieldname type  fieldname,        lr_extended_entity type ref to /iwbep/cl_mgw_odata_entity_typ,        ls_extended_field type ty_extended_field.  field-symbols: <ls_property> like line of lt_table_properties,                 <ls_entity> like line of mr_model->mt_entities,                 <ls_props> type /iwbep/if_mgw_med_odata_types=>ty_s_med_property.
* first check if we alread loaded this entity, eg we might be processing
* through a $expand.  if mv_entity_name <> iv_entity_name.
* free last table name used    free: mv_db_tabname,          mt_table_fields,          mv_max_top,          mv_filter_required,          mv_post_sort,          mv_etag.
* set max top for this entityset.    mv_max_top = iv_max_top.
* grab table entity    lv_entity_name = iv_entity_name.    lr_table_entity ?= mr_model->get_entity_type( lv_entity_name ).
* grab the requires filter from the base or inherited entity    read table mr_model->mt_entities assigning <ls_entity> with key name = lv_entity_name.    if sy-subrc = 0.      mv_filter_required = <ls_entity>-filter_required.    endif.
* grab DDIC reference    try .        lv_entity_name = lr_table_entity->/iwbep/if_mgw_odata_entity_typ~get_base_type( ).        lr_extended_entity = lr_table_entity.
* get struct name from entity base        lr_table_entity ?= mr_model->get_entity_type( lv_entity_name ).        mv_db_tabname = lr_table_entity->/iwbep/if_mgw_odata_re_etype~get_structure( ).
* our extended / inheerited properties        read table mr_model->mt_entities assigning <ls_entity> with key name = iv_entity_name.        lt_table_properties = lr_extended_entity->/iwbep/if_mgw_odata_entity_typ~get_properties( ).        loop at lt_table_properties assigning <ls_property>.          lr_property ?= <ls_property>-property.          lv_fieldname = lr_property->/iwbep/if_mgw_odata_re_prop~get_abap_name( ).          read table <ls_entity>-properties assigning <ls_props> with key name = lv_fieldname.          if sy-subrc = 0.            ls_extended_field-abap_name = <ls_props>-name.            ls_extended_field-external_name = <ls_props>-external_name.            ls_table_field-is_sortable = <ls_props>-sortable.            insert ls_extended_field into table mt_extended_fields.
* check for etag fieldname, first option off extended entity            if <ls_props>-is_etag = abap_true.              mv_etag = <ls_props>-name.            endif.          endif.        endloop.      catch /iwbep/cx_mgw_med_exception.
* get database table/view from entity        mv_db_tabname = lr_table_entity->/iwbep/if_mgw_odata_re_etype~get_structure( ).    endtry.
* grab some table properties, we are interested in, currently only the
* abap name and if the property is a key    read table mr_model->mt_entities assigning <ls_entity> with key name = lv_entity_name.    lt_table_properties = lr_table_entity->/iwbep/if_mgw_odata_entity_typ~get_properties( ).    loop at lt_table_properties assigning <ls_property>.      lr_property ?= <ls_property>-property.      lv_fieldname = lr_property->/iwbep/if_mgw_odata_re_prop~get_abap_name( ).      read table <ls_entity>-properties assigning <ls_props> with key name = lv_fieldname.      if sy-subrc = 0.        ls_table_field-abap_name = <ls_props>-name.        ls_table_field-is_key = <ls_props>-is_key.        ls_table_field-external_name = <ls_props>-external_name.        ls_table_field-is_filterable = <ls_props>-filterable.        ls_table_field-is_sortable = <ls_props>-sortable.        insert ls_table_field into table mt_table_fields.
* check for etag fieldname, 2nd option for etag off table/base        if ( mv_etag is initial ) and ( <ls_props>-is_etag = abap_true ).          mv_etag = <ls_props>-name.        endif.      endif.    endloop.
* save this for comparison on the next round though.
* if we have already computed this then lets not do it again, eg in an $expand!    mv_entity_name = iv_entity_name.  endif.
endmethod.

 

process_etag(), new method, protected

This new method will take care of calculating our etag property if mentioned during our entity creation. We added an etag above to our FlightSchedule entity. The method calculates the MD5 for the complete structure by first transforming to XML, and then calling the MD5 calculate method. Thanks to goes to Leigh Mason for the idea of calculating MD5 on the whole structure (implementation may be different). Picked up from the excellent Sydney Tech Nights (SAP Inside Track, thanks Simon Kemp for organizing)

 

Signature as below.

process_etag.PNG

 

 

method process_etag.  data: lv_hash type md5_fields-hash,        lv_xml type string.  field-symbols: <lv_etag> type any.  assign component mv_etag of structure cs_entity to <lv_etag>.  if sy-subrc = 0.    clear <lv_etag>.
* convert to xml string    call transformation id source entity = cs_entity result xml lv_xml.
* create MD5 hash for etag    call function 'MD5_CALCULATE_HASH_FOR_CHAR'      exporting        data           = lv_xml      importing        hash           = lv_hash      exceptions        no_data        = 1        internal_error = 2        others         = 3.    if sy-subrc = 0.      <lv_etag> = lv_hash.    endif.  endif.
endmethod.

 

check_filter_property(), new method, protected

This is a helper function used below in method process_filter() to check if a property/fieldname exists in the filter string passed with the the odata request.

 

Signature as below.


check_filter_property.PNG

 

Code as below

 

method check_filter_property.  data: lv_str type string.  lv_str = `\<` && iv_fieldname && `\>`. " find word  find regex lv_str in iv_filter.  if sy-subrc = 0.    raise exception type /iwbep/cx_mgw_busi_exception      exporting        textid            = /iwbep/cx_mgw_busi_exception=>business_error_unlimited        message_unlimited = `The filter property ` && iv_external_name && ` supplied for entityset of ` && mv_entity_name && ` is not allowed.`.  endif.
endmethod.

 

 

process_filter(), new method, protected

We create a new method here to take care of loading up our $filter operator details.

 

Signature as below.

process_filter.png

First is to check a filter has been passed if required, if not we throw and exception. Next we check to see if a filter has been passed using one of the extended fields. If so we also throw and exception. We are not adding any logic to handle filtering on extended fields. Lastly we check to ensure this property was flagged as filterable during the SEGW setup above, if not an exception will be thrown in the check_filter_property() method.

 

method process_filter.    field-symbols: <ls_ext_field> like line of mt_extended_fields,                   <ls_tab_field> like line of mt_table_fields.
* grab our converted filter    rv_filter = io_tech_request_context->get_osql_where_clause_convert( ).
* check if filter required....    if ( rv_filter is initial ) and ( mv_filter_required = abap_true ).      raise exception type /iwbep/cx_mgw_busi_exception        exporting          textid            = /iwbep/cx_mgw_busi_exception=>business_error_unlimited          message_unlimited = `The entityset of entity ` && mv_entity_name && ` requires a filter. `.    else.      loop at mt_extended_fields assigning <ls_ext_field>.        check_filter_property(          exporting iv_fieldname = <ls_ext_field>-abap_name                    iv_external_name = <ls_ext_field>-external_name                    iv_filter = rv_filter ).      endloop.
* check if non filterable table fields have been supplied, if so error      loop at mt_table_fields assigning <ls_tab_field> where is_filterable <> abap_true.        check_filter_property(          exporting iv_fieldname = <ls_tab_field>-abap_name                    iv_external_name = <ls_tab_field>-external_name                    iv_filter = rv_filter ).      endloop.    endif.  endmethod.

 

orderby_to_sorttab(), new method, protected

In the case where we add the $orderby operator with an extended field we will need to post sort our results set, as this cannot be completed in the OSQL call. The new method takes our order string and converts this into an ABAP_SORTORDER_TAB to be used with the SORT command, for post sorting of results.

 

Signature

sorttab.png

 

method orderby_to_sorttab.    data: lt_strings type string_table.    field-symbols: <ls_string> like line of lt_strings,                   <ls_sortorder> like line of rt_sortorder.    free: rt_sortorder.
* String will be in format
* FIELDNAME ORDER FIELDNAME ORDER
*   eg:   VBELN ASCENDING ENAME ASCENDING    split iv_db_orderby at ` ` into table lt_strings.    loop at lt_strings assigning <ls_string>.      if ( sy-tabix mod 2 ) = 0.        if <ls_string> = mc_sql_descending.          <ls_sortorder>-descending = abap_true.        endif.      else.        append initial line to rt_sortorder assigning <ls_sortorder>.        <ls_sortorder>-name = <ls_string>.      endif.    endloop.  endmethod.

 

 

process_orderby(), change

We change this method to check that the property being ordered is on our table and not one of the extended properties. If the property cannot be found on the table properties then we set the post sorting attribute MV_POST_SORT to true. We also check that the property has been flagged to allow sorting, as setup during our model creation in SEGW.

 

Complete method

 

  method process_orderby.    data: lt_orderby type /iwbep/t_mgw_tech_order,          lv_order type string.    field-symbols: <ls_orderby> like line of lt_orderby,                   <ls_table_field> like line of mt_table_fields,                   <ls_ext_field> like line of mt_extended_fields,                   <ls_tab_field> like line of mt_table_fields.    lt_orderby = io_tech_request_context->get_orderby( ).
* build order by osql string from order by uri table    loop at lt_orderby assigning <ls_orderby>.      read table mt_table_fields assigning <ls_tab_field> with key abap_name = <ls_orderby>-property.      if sy-subrc = 0.        if <ls_tab_field>-is_sortable <> abap_true.
* error, non sortable field          raise exception type /iwbep/cx_mgw_busi_exception            exporting              textid            = /iwbep/cx_mgw_busi_exception=>business_error_unlimited              message_unlimited = `The sort property ` && <ls_tab_field>-external_name && ` supplied for entityset of ` && mv_entity_name && ` is not allowed.`.        endif.      else.        read table mt_extended_fields assigning <ls_ext_field> with key abap_name = <ls_orderby>-property.        if sy-subrc = 0.          if <ls_ext_field>-is_sortable = abap_true.
* field is sortable and in extended entity, then post sort is required            mv_post_sort = abap_true.          else.
* error, non sortable field            raise exception type /iwbep/cx_mgw_busi_exception              exporting                textid            = /iwbep/cx_mgw_busi_exception=>business_error_unlimited                message_unlimited = `The sort property ` && <ls_ext_field>-external_name && ` supplied for entityset of ` && mv_entity_name && ` is not allowed.`.          endif.        endif.      endif.      case <ls_orderby>-order.        when mc_ascending.          lv_order = mc_sql_ascending.        when mc_descending.          lv_order = mc_sql_descending.      endcase.      rv_orderby = rv_orderby && <ls_orderby>-property && ` ` && lv_order && ` `.    endloop.
* if $top or $skip supplied and NO $orderby, then order by keys ascending    if ( rv_orderby is initial ) and       ( ( io_tech_request_context->get_top( ) > 0 ) or         ( io_tech_request_context->get_skip( ) > 0 ) ).
* loop over keys and add to ordering      loop at mt_table_fields assigning <ls_table_field> where is_key = abap_true.        rv_orderby = rv_orderby &&  <ls_table_field>-abap_name && ` ` &&  mc_sql_ascending && ` `.      endloop.    endif.  endmethod.

 

 

process_select(), change

We make a change to this method to ensure that the properties we are selecting with the $select operator belong to our table/view and not to the extended view. If no select has been passed we also just dump the fields of the entity instead of using the all operator ‘*’.

 

 

Complete method

  method process_select.    field-symbols: <ls_string> type string,                   <ls_table_field> like line of mt_table_fields.
* if $select has been supplied, then loop over table
* and add list of attributes to our osql select string    if it_select_table is not initial.      loop at it_select_table assigning <ls_string>.
* ensure that the string is on our table/view,
* not the extended entity inheriting base entity        read table mt_table_fields transporting no fields with key abap_name = <ls_string>.        if sy-subrc = 0.          rv_db_select = rv_db_select && <ls_string> && ` `.        endif.      endloop.    else.
* else dump all properties of our entity      loop at mt_table_fields assigning <ls_table_field>.        rv_db_select = rv_db_select && <ls_table_field>-abap_name && ` `.      endloop.    endif.  endmethod.

 

get_entity(), change

We add a new optional parameter to this method IR_EXTEND. This allows as to pass in an instance of our extend class, as we will see below.

New parameter to extend

 

get_entity_sig.png

 

We check to see IR_EXTEND is bound and if so make a call to our custom extend logic. Also a check for presence of an ETAG property is made and if it exists then the etag processing logic mentioned above is called, process_etag().

 

Complete method

 

method get_entity.  data: lt_keys type /iwbep/t_mgw_tech_pairs,        lv_db_where type string,        lv_db_and type string value '',        lv_db_select type string,        lr_data type ref to data,        lv_navigating type abap_bool.  field-symbols: <ls_key> like line of lt_keys,                 <ls_data> type any,                 <lv_value> type any.
* initialise  init( io_tech_request_context->get_entity_type_name( ) ).  if mv_db_tabname is not initial.
* $select, grab fields to select if any    lv_db_select = process_select( io_tech_request_context->get_select_entity_properties( ) ).    lt_keys = io_tech_request_context->get_keys( ).
* read association and keys    process_nav_path(      exporting it_nav_path = io_tech_request_context->get_navigation_path( )                it_source_keys = io_tech_request_context->get_source_keys( )      importing ev_navigating = lv_navigating      changing  cv_db_where = lv_db_where ).    if lv_navigating = abap_false.
* create data struct to grab converted keys      create data lr_data like cs_entity.      assign lr_data->* to <ls_data>.      io_tech_request_context->get_converted_keys(        importing          es_key_values = <ls_data> ).
* loop over keys to build where condition      loop at lt_keys assigning <ls_key>.        assign component <ls_key>-name of structure <ls_data> to <lv_value>.        if sy-subrc = 0.          lv_db_where = lv_db_where && lv_db_and && `( ` && <ls_key>-name && ` = '` && <lv_value> && `' )`.          lv_db_and = ` and `.        endif.      endloop.    endif.    if lv_db_where is not initial.      select single (lv_db_select)        from (mv_db_tabname)        into corresponding fields of cs_entity        where (lv_db_where).
* post processing to extend our entity data      if ir_extend is bound.        ir_extend->get_entity(           exporting io_tech_request_context = io_tech_request_context           changing cs_entity = cs_entity ).      endif.
* process our etag field      if mv_etag is not initial.        process_etag( changing cs_entity = cs_entity ).      endif.    endif.  endif.  mv_conv_keys = abap_false.
endmethod.

 

 

get_entityset(), change

Again we add a new optional parameter to this method IR_EXTEND, which allows as to pass in an instance of our extend class.

New parameter to extend

 

get_entityset_sig.png

 

We need to make a few code changes here. First is to define a new data type LT_SORTORDER of type ABAP_SORTORDER_TAB. This is require in the case of post sorting as mentioned above. Next we include the call to our new method PROCESS_FILTER(). Then we add the extra logic to handle our post sorting and performing our extend logic. Finally a call to our etag processing is added if required.

 

Complete method

 

method get_entityset.  data: lv_db_where type string,        lv_db_select type string,        lv_db_orderby type string,        lv_top type i,        lv_skip type i,        lv_navigating type abap_bool,        lt_sortorder type abap_sortorder_tab.  field-symbols: <cs_entity> type any.
* initialise  init(    exporting iv_entity_name = io_tech_request_context->get_entity_type_name( )              iv_max_top = iv_max_top ).  if mv_db_tabname is not initial.
* $top, $skip, process our paging    process_paging(      exporting io_tech_request_context = io_tech_request_context      importing ev_top = lv_top                ev_skip = lv_skip ).
* $filter, grab our converted filter    lv_db_where = process_filter( io_tech_request_context ).
* $orderby grab our order
* process up here to jump out early, if a unsupport sort field has been supplied    lv_db_orderby = process_orderby( io_tech_request_context ).
*
* if post sorting, don't sort at the DB level
* create sort table and clear osql sort var    if mv_post_sort = abap_true.      lt_sortorder = orderby_to_sorttab( lv_db_orderby ).      free: lv_db_orderby.    endif.
* read association and keys    process_nav_path(      exporting it_nav_path = io_tech_request_context->get_navigation_path( )                it_source_keys = io_tech_request_context->get_source_keys( )      importing ev_navigating = lv_navigating      changing  cv_db_where = lv_db_where ).
* check for $count if present just count the records
* no need to order results, or select fields!    if io_tech_request_context->has_count( ) = abap_true.
* execute our select      select count(*) up to lv_top rows        into cs_response_context-count        from (mv_db_tabname)        where (lv_db_where).    else.
* $select, grab fields to select if any      lv_db_select = process_select( io_tech_request_context->get_select_entity_properties( ) ).      if lv_navigating = abap_true.
* we are navigating, execute select with determined lv_db_where
* from navigation!        select (lv_db_select)            from (mv_db_tabname)            into corresponding fields of table ct_entityset            where (lv_db_where)            order by (lv_db_orderby).   " empty if post sorting      else.        if mv_post_sort = abap_true.
* if post sorting we cannot use "up to (x) rows"
* paging needs to be done after sorting          select (lv_db_select)          from (mv_db_tabname)          into corresponding fields of table ct_entityset          where (lv_db_where).        else.
* execute our select          select (lv_db_select) up to lv_top rows            from (mv_db_tabname)            into corresponding fields of table ct_entityset            where (lv_db_where)            order by (lv_db_orderby).          if lv_skip > 0.            delete ct_entityset from 1 to lv_skip.          endif.        endif.      endif.      if ir_extend is bound.        ir_extend->get_entityset(           exporting io_tech_request_context = io_tech_request_context           changing ct_entityset = ct_entityset ).      endif.
* post sort if required      if mv_post_sort = abap_true.        sort ct_entityset by (lt_sortorder).
* paging if required, for post sort        if lv_skip > 0.          delete ct_entityset from 1 to lv_skip.        endif.        if lv_top > 0.          delete ct_entityset from ( lv_top + 1 ).        endif.      endif.
* process etag if present      if mv_etag is not initial.        loop at ct_entityset assigning <cs_entity>.          process_etag( changing cs_entity = <cs_entity> ).        endloop.      endif.
* $inlinecount, check for inline count and update      if io_tech_request_context->has_inlinecount( ) = abap_true.        cs_response_context-inlinecount = lines( ct_entityset ).      endif.    endif.  endif.  mv_conv_keys = abap_false.
endmethod.

 

That’s it for our helper class. Save changes and activate.

 

 

ZCL_ZFLIGHT_EXTEND, Extend Class

Now let’s get down to creating our actual logic to extend. We create a class ZCL_ZFLIGHT_EXTEND specifying ZIF_GATEWAY_HELPER_EXTEND as an interface. We will also add three new methods to this class as below, GET_FAVICON, GET_EXRATE, GET_FLIGHTDATA.

 

ext_class.png

 

With the following signatures and code as below.

 

get_exrate()

A simple select to obtain the USD exchange rate, this could have been done with a view, though here to show the concept.

 

Signature

ex_rate.png

 

Complete method

method get_exrate.
* grab exchange rate compared to USD
* simple example, could be done with a view defined in SE11.  select single ukurs    into rv_usd_rate    from scurr    where fcurr = 'USD'      and tcurr = iv_currency_key.
endmethod.

 

get_flightdata()

Another simple select to obtain additional flight information, again could have been done with a select.

 

Signature

get_flightdata.png

 

Complete method

method get_flightdata.
* Simple example for demo purposes
* Could be just as easily done with a view defined in SE11  select single carrname url    into corresponding fields of cs_entity    from scarr    where carrid = cs_entity-carrid.
endmethod.

 

get_favicon()

The final example to obtain the favicon as base64 encoded string for the domain of the URL.

 

Signature

get_favicon.png

 

Complete method

method get_favicon.
* A little more complex example to make a HTTP request to get he FAVICON of the domain
* return the result as base64 encoded image  data:   lr_client type ref to if_http_client,          lv_url type string,          lv_subrc type sysubrc,          lv_string type string,          lv_xstring type xstring.
* just grab the domain and append the google favicon uri  lv_url = `http://www.google.com/s2/favicons?domain=` && iv_url+11.  call method cl_http_client=>create_by_url    exporting      url                = lv_url    importing      client             = lr_client    exceptions      argument_not_found = 1      plugin_not_active  = 2      internal_error     = 3      others             = 4.  check sy-subrc = 0.
* set http method GET  lr_client->request->set_method( if_http_request=>co_request_method_get ).
* send the message  call method lr_client->send    exceptions      http_communication_failure = 1      http_invalid_state         = 2      http_processing_failed     = 3      others                     = 4.  check sy-subrc = 0.
* receive  call method lr_client->receive    exceptions      http_communication_failure = 1      http_invalid_state         = 2      http_processing_failed     = 3      others                     = 4.  check sy-subrc = 0.
* get data  lv_xstring = lr_client->response->get_data( ).
* close connection  call method lr_client->close    exceptions      http_invalid_state = 1      others             = 2.
* convert b64  rv_favicon_base64 = cl_http_utility=>encode_x_base64( lv_xstring ).
endmethod.

 

 

get_entity()

As we are passing IO_TECH_REQUEST_CONTEXT we can check which entity is being worked on and proceed with the required logic, making calls to our above methods. Another way would be to create a class for each extend, and omit this check. I really depends on requirements, and reuse, possibly across services.

 

Complete method

method zif_gateway_helper_extend~get_entity.  data: lv_entity_name type string.  field-symbols: <ls_airline> type zcl_zflight_extend_mpc=>ts_airline,                 <ls_flight> type zcl_zflight_extend_mpc=>ts_flightschedule.  lv_entity_name = io_tech_request_context->get_entity_type_name( ).  case lv_entity_name.    when `Airline`.      assign cs_entity to <ls_airline>.      if sy-subrc = 0.        <ls_airline>-favicon = me->get_favicon( <ls_airline>-url ).        <ls_airline>-usd_exrate = me->get_exrate( <ls_airline>-currcode ).      endif.    when `FlightSchedule`.      assign cs_entity to <ls_flight>.      if sy-subrc = 0.        me->get_flightdata( changing cs_entity = <ls_flight> ).      endif.  endcase.
endmethod.

 

get_entityset()

Again we are using IO_TECH_REQUEST_CONTEXT to check the entity being worked on and proceed with the appropriate logic. We loop over our entities and make a call to our methods.

 

Complete method

method zif_gateway_helper_extend~get_entityset.  data: lv_entity_name type string.  field-symbols: <ls_airline> type zcl_zflight_extend_mpc=>ts_airline,                 <ls_flight> type zcl_zflight_extend_mpc=>ts_flightschedule.  lv_entity_name = io_tech_request_context->get_entity_type_name( ).  case lv_entity_name.    when `Airline`.      loop at ct_entityset assigning <ls_airline>.        <ls_airline>-favicon = me->get_favicon( <ls_airline>-url ).        <ls_airline>-usd_exrate = me->get_exrate( <ls_airline>-currcode ).      endloop.    when `FlightSchedule`.      loop at ct_entityset assigning <ls_flight>.        me->get_flightdata( changing cs_entity = <ls_flight> ).      endloop.  endcase.
endmethod.

 

That completes our extend class, save and activate.

 

 

ZCL_ZFLIGHT_EXTEND_DPC_EXT, data provider class

Now we can add the extended class to the service methods. As we are using the same class for both entities we can create a class attribute on our data provider class for our extend class. Define this as below:

 

dpc_ext.png

 

constructor

We’ll define the constructor as below to instantiate our extend class attribute defined above.

 

method constructor.  super->constructor( ).  create object me->mr_extend_class.
endmethod.

 

…get_entity() methods

For both the airline and the flightschedule get_entity methods, we add the code to call our helper, including the new extend class.

 

get_gw_helper( )->get_entity(    exporting io_tech_request_context = io_tech_request_context              ir_extend = mr_extend_class    changing  cs_entity = er_entity              cs_response_context = es_response_context ).

 

 

…get_entityset() methods

Again for both the airline and the flightschedule get_entityset methods, we add the code to call our helper including the new extend class.

 

get_gw_helper( )->get_entityset(     exporting io_tech_request_context = io_tech_request_context               ir_extend = mr_extend_class               iv_max_top = 15     changing  ct_entityset = et_entityset               cs_response_context = es_response_context ).

 

Save and activate, time for testing.

 

 

Testing

Once all is activated we can test some of our features with the gateway client, /IWFND/GW_CLIENT.

 

1. /sap/opu/odata/sap/ZFLIGHT_EXTEND_SRV/AirlineSet

  • This should throw and exception that a filter is required, as we set the flag in the model “Filter Required”

 

2. /sap/opu/odata/sap/ZFLIGHT_EXTEND_SRV/AirlineSet('QF')?$format=json

  • This should return our airline details including the extended ‘FavoriteIcon’ and the ‘USDExchangeRate’. You can grab the string of the FavoriteIcon and paste it into one of the many online BASE64 image converters to ensure it worked!

 

3. /sap/opu/odata/sap/ZFLIGHT_EXTEND_SRV/FlightScheduleSet()?$format=json

  • This should return our FlightSchedule entities with their related extended properties, AirlineName and Url.

 

4. /sap/opu/odata/sap/ZFLIGHT_EXTEND_SRV/FlightScheduleSet()?$filter=startswith(AirlineName,'A')

  • This should also return and error as the $filter is specifying a property which exists on the extended entity, eg not the base entity.

 

5. /sap/opu/odata/sap/ZFLIGHT_EXTEND_SRV/FlightScheduleSet()?$orderby=CountryFrom

  • This should also throw and error stating that the sort field CountryFrom is not allowed.

 

6. /sap/opu/odata/sap/ZFLIGHT_EXTEND_SRV/FlightScheduleSet()?$orderby=ConnectionId

  • This should order our results by ConnectionId, as this was ticked as a sortable field during our entity set up.

 

 

As we have also set an ETAG property for the FlightSchedule entity, you will notice this is being populated by our etag logic mentioned above.

 

 

Limitations

In my case, associations cannot currently be built with entities inheriting from a Base entity type, error as below. Seems as though the check is not in place to check for key fields in the base type. A check does exist for this when creating an entity from the base type, as we didn’t receive any errors when doing that! I have raised an OSS message with SAP, which they tell me “The case is a gap identified and being worked <on> by the development team”, so currently awaiting a SAP Note, stay tuned for the update… I’ll post here once I have details.

 

error.png

 

$select

If using $select, ensure that key fields for the extended functionality are also included.

 

  1. Eg: ZFLIGHT_EXTEND_SRV/AirlineSet?$select=AirlineCurrency,USDExchangeRate
    • Will work as USDExchangeRate requires AirlineCurrency.
    • Leaving AirlineCurrency out of the select will result in 0 for the USDExchangeRate, as the select is relying on AirlineCurrency.

 

$filter

The filter must exist on the base entity (table/view), no filtering is supported on the extended entity properties.

 

 

Final words

That brings as to the end of this series of blogs. I hope that you have found it interesting and useful.

Thanks for all the comments and feedback so far.

 

See you all in another blog.

Step-By-Step Procedure for creating Purchase Requisition using BOR interface.

$
0
0


This blog shows how to display and create PR using gateway services(BOR interface) in detail.


Business Object Repository(BOR) in Gateway Service:

It generally used to reduce the time required to create entity types in your data model, SAP Gateway Service Builder provideImport RFC/BOR Interface function with which you can import an existing data source and reuse this data to create new entity types with ease.

 

Step 1:Open Tcode SEGW and create project as shown below.1.png

Provide project details.

2.png

 

Step 2:Create entity by importing BOR interface. For this right click on Data Model and choose Import -> RFC/BOR Interface.3.png

Provide the details.

4.png

 

Step 3:Expand and select the following fields for creating the Purchase requisition and choose next.

             Fields selected RequisitionItems(PREQ_ITEM, DOC_TYPE, MATERIAL, PLANT, QUANTITY, UNIT,

             DELIV_DATE),Number,Return(TYPE,MESSAGE,LOGMSGNO).               

5.png

Specify the key field NUMBER and choose finish.

6.png

 

For displaying (QUERY) Purchase Requisition using BOR interface.

Step 4: Now basic definition of data model is done successfully. As a next step we need to map the fields.

                 Navigate to service implementation and create the QUERY to display the Purchase requisition.

7.png

Step 5: In BOR select the GetDetail for displaying the Purchase requisition details.

8.png

Select the type as Business object Repository and choose tick box.

9.png

Step 6: Now map the fields accordingly by selecting the propose mapping button as shown in below screen.

10.png

Step 7:Fields are mapped accordingly. Check the project, save and generate the project.

11.png

For Creating Purchase Requisition using BOR interface.

Step 8:For creating purchase requisition similarly map the fields by importing the BOR interface.

12.png

Step 9: Select the BOR method (CreateFromData) as shown in below screen.

13.png

Select and proceed.

14.png

Step 10:Now similarly click on propose mapping button as shown below.

15.png

Mapping will be done according. Save and generate the project.

16.png

Testing the Service:

Step 11: Display Purchase requisition

Case1:

Goto Tcode /N/IWFND/MANIT_SERVICE register service.

17.png

Provide System Alias and Click on Get Service. Select Our Created Service and click on Add Selected Service.

Case 2:

Click on SAP Gateway Client.

18.png

Case 3:

Provide the following URL, Select GET and click on EXECUTE to get the sample XML of PR.

19.png

Step 12 :Create Purchase requisition

Case:1

Click on Use as Request and then select POST and EXECUTE it.

20.png

Purchase requisition got created.

21.png

 

Case 2:

Goto SE11 table and provide the Created PR number. So below screen shows that Purchase requisition is created successfully.

22.png

 



Upload document into DMS Server using Net Weaver Gateway

$
0
0

Scenario:

 

Why and when we use DMS?


DMS is used to read the content of storage documents (external data server but not SAP content server).Here I am going to explain one of mine client requirement to upload the document after/while creating the Purchase requisition initem level. So in order to achieve the functionality we need to upload the file from non-SAP server into DMS (CV01N) using Gateway.

 

By Using this Blog, you can upload a wide range of documents like Word Documents,Excel Sheets, PDF, Text Files and Images and many more through Gateway service.

 

Procedure:

 

Goto SEGW TCode and Create Project as shown below.

 

1.png

Provide Project Details.

2.png


Create Entity Type with the required fields.

3.png


For Upload_Doc : Provide the following details.

4.png

Created Entity Type as shown below.

Double click on Entity Type created and put tick mark on Media type check box (mandatory).

5.png

6.png


Double click on Properties and Click on Insert Row to add fields.

7.png

Insert field and check as key field (mandatory).

8.png

Click on Save

9.png

Now click on Generate button.

10.png

 

Then we will get below screen, click on continue

11.png

It displays generated OBJECTS SUCCESSFULLY.

12.png

And at that time it generates all classes automatically.

13.png

Then Redefine the DEFINE method in the *MPC_EXT Class and add the below logic.

14.png

Method DEFINE.

 

super->DEFINE( ).
 

DATA: lo_entity     type REF TO  /IWBEP/IF_MGW_ODATA_ENTITY_TYP,

lo_property type REF TO  /IWBEP/IF_MGW_ODATA_PROPERTY.

 

lo_entity = model->GET_ENTITY_TYPE( IV_ENTITY_NAME = 'Upload_Doc' )."Entity Name

 

IF lo_entity is BOUND.

 

lo_property = lo_entity->GET_PROPERTY( IV_PROPERTY_NAME = 'DocName' )."Key Value(SLUG)

lo_property->SET_AS_CONTENT_TYPE( ).


ENDIF.

 

Endmethod.

 

Right Click on ZCL_ZUPLOAD_DOC_DMS_DPC_EXT class and select Go to ABAP Workbench.

15.png

 

Redefine CREATE_STREAM Method and implement the below logic to upload document into the DMS Server from ODATA Service.

16.png

 

Code:

 

method /IWBEP/IF_MGW_APPL_SRV_RUNTIME~CREATE_STREAM.
DATA ls_documentdata    TYPE  bapi_doc_draw2,
       ls_DOCUMENTNUMBER 
TYPE  BAPI_DOC_AUX-DOCNUMBER,
       ls_DOCUMENTTYPE   
TYPE  BAPI_DOC_AUX-DOCTYPE,
       ls_DOCUMENTPART   
TYPE  BAPI_DOC_AUX-DOCPART,
       ls_DOCUMENTVERSION
TYPE  BAPI_DOC_AUX-DOCVERSION,
       ls_RETURN         
TYPE  BAPIRET2,
       lv_filename       
TYPE DBMSGORA-FILENAME,
       lv_PURE_FILENAME  
TYPE SDBAH-ACTID,
       lv_PURE_EXTENSION 
TYPE SDBAD-FUNCT.

DATAlt_files           TYPE TABLE OF BAPI_DOC_FILES2,
       ls_files          
TYPE          BAPI_DOC_FILES2,
* Short Texts
       lt_drat           
TYPE TABLE OF bapi_doc_drat,
* Object links
       lt_drad           
TYPE TABLE OF bapi_doc_drad,
       LS_DOC            
TYPE ZCL_ZUPLOAD_DOC_DMS_MPC=>TS_UPLOAD_DOC.
* Pass SLUG value


lv_filename
= IV_SLUG.


* Function Module to split and get file name and extension


CALL FUNCTION 'SPLIT_FILENAME'
EXPORTING
long_filename       
= lv_filename
IMPORTING
PURE_FILENAME       
lv_PURE_FILENAME
PURE_EXTENSION      
lv_PURE_EXTENSION.

* Allocate document data
ls_documentdata
-documenttype    = 'DOC'.
ls_documentdata
-documentversion = '000'.
ls_documentdata
-documentpart    = '00'.

ls_documentdata
-wsapplication1 = lv_PURE_EXTENSION.
ls_documentdata
-wsapplication2 = lv_PURE_EXTENSION.

* Check original 1 into the SAP data base at the same time
REFRESH lt_files.
CLEAR lt_files.

ls_files
-originaltype       = '1'.
ls_files
-WSAPPLICATION      = lv_PURE_EXTENSION.
ls_files
-SOURCEDATACARRIER  = 'SAP-SYSTEM'.
ls_files
-docfile            = IV_SLUG.
APPEND ls_files to lt_files.

* BAPI to Upload document into DMS Server
CALL FUNCTION 'BAPI_DOCUMENT_CREATE2'
EXPORTING
DOCUMENTDATA              
= ls_documentdata
IMPORTING
DOCUMENTTYPE              
= ls_DOCUMENTTYPE
DOCUMENTNUMBER            
= ls_DOCUMENTNUMBER
DOCUMENTPART              
= ls_DOCUMENTPART
DOCUMENTVERSION           
= ls_DOCUMENTVERSION
RETURN                     = ls_RETURN
TABLES
DOCUMENTDESCRIPTIONS      
= lt_drat
OBJECTLINKS               
= lt_drad
DOCUMENTFILES             
= lt_files.
IF ls_return-type CA 'EA'.
CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.

ELSE.

CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
EXPORTING
wait = 'X'.

LS_DOC
-DOCNAME = ls_DOCUMENTNUMBER.

copy_data_to_ref
(
EXPORTING
is_data
LS_DOC
CHANGING
cr_data
= er_entity ).
ENDIF.

endmethod.


All input Values we have to get into the SLUG parameter from the UI Side. If you have multiple input parameter values then concatenate multiple parameter values with delimiter we have to get the values in SLUG parameter.


Testing Our Service:

 

Now we will test our service in Gateway Client transaction /IWFND/MAINT_SERVICE

 

17.png

Select the entityset and Upload a file through add file button which is on left side corner as shown in the below screen shot.

18.png

Pass SLUG values(mandatory) file name as shown in the below screen shot.

 

19.png

 

 

Now click on POST HTTP Methodand Execute.

20.png

 

Service Response:


It will generate a document number as shown below.

21.png

 

Successfully uploaded document in DMS Server.


Result:

Go to CV03N Transaction and provide document number and document type and click enter as shown below.

22.png

 

To open the file double click on it.

23.png

File got opened successfully as shown.

24.png

 

In the same way we can upload any type of document into DMS.

 

Thanks&Regards,

Prasanna Addula

How to add files to the existing document info data in DMS server using the SAP Net Weaver Gateway

$
0
0

Scenario: When we want to add some additional files to the document info which is already created in the DMS through CV01N .This particular functionality can be achieved by the below mentioned process.


Procedure:

Go to the T.code SEGW, Click on create project to create new Project

1.png

Provide the Description as shown below .Save and Activate

2.png

Right Click on the Entity Types to create the entity type as shown below

3.png

Provide the Entity Name as Add_File

4.png

Click on the Media type Check Box

Mandatory Note:A normal entity type will act as media entity type when you flag it is media by selecting the check box.

5.png

Expand the Entity type Add_Files it will navigate to the Properties double click on the properties, on the right hand side you can find the option insert row Click on the insert row to add the filed names to the Entity type

6.png

Provide the properties as shown below

7.png

Click on the Generate run time Objects

8.png

Default Classes of the service will be generated.

9.png

Save and activate the classes

10.png

Classes are generated Successfully

11.png

 

Setting Entity Type as Media is not sufficient we also need to set manually via coding. To do so, go to the MPC extension class and redefine the method DEFINEand write the below code inside it.

12.png

14.png

 

METHOD define.
super
->define( ).

DATA: lo_entity     TYPE REF TO  /iwbep/if_mgw_odata_entity_typ,
lo_property
TYPE REF TO  /iwbep/if_mgw_odata_property.

lo_entity
= model->get_entity_type( iv_entity_name = 'Add_File' )."Entity Name

IF lo_entity IS BOUND.

lo_property
= lo_entity->get_property( iv_property_name = 'Filename' )."Key Value(SLUG)
lo_property
->set_as_content_type( ).

ENDIF.
ENDMETHOD.

 

  Now go to the DPCextension class

15.png

Click on the CREATE_STREAM method to redefine as we are about the create the document and write the following coding

16.png

17.png


method /IWBEP/IF_MGW_APPL_SRV_RUNTIME~CREATE_STREAM.

METHOD /iwbep/if_mgw_appl_srv_runtime~create_stream.

DATA: lt_files     TYPE TABLE OF bapi_doc_files2,
ls_files    
TYPE bapi_doc_files2,
ls_return   
TYPE bapiret2,
lv_docnum   
TYPE bapi_doc_aux-docnumber,
lv_filename 
TYPE dbmsgora-filename,
lv_filename1
TYPE sdbah-actid,
lv_extension
TYPE sdbad-funct,
ls_file     
TYPE zcl_zadd_fiLes_dms_doc_mpc=>ts_add_file.

*Passing the SLUG value to the Filename

lv_filename
= iv_slug.

* Function Module to split and get file name and extension

CALL FUNCTION 'SPLIT_FILENAME'
EXPORTING
long_filename       
= lv_filename
IMPORTING
pure_filename       
lv_filename1
pure_extension      
lv_extension.


ls_files
-docfile       = lv_filename1.
ls_files
-wsapplication = lv_extension.
APPEND ls_files TO lt_files.

CALL FUNCTION 'BAPI_DOCUMENT_SETCOMMITMODE'
EXPORTING
auto_commit
= 'X'.

*Function Module to add
CALL FUNCTION 'BAPI_DOCUMENT_CHECKIN2'
EXPORTING
documenttype           
= 'DOC'
documentnumber         
= '0000010000000000000000105'
documentpart           
= '000'
documentversion        
= '00'
*   HOSTNAME                = ' '
*   STATUSINTERN            = ' '
*   STATUSEXTERN            = ' '
*    statuslog              = ' '
*   REVLEVEL                = ' '
*   AENNR                   = ' '
*   PF_FTP_DEST             = ' '
*   PF_HTTP_DEST            = ' '
IMPORTING
return                  = ls_return
TABLES
documentfiles          
= lt_files
*   COMPONENTS              =
*   DOCUMENTSTRUCTURE       =.
.
IF ls_return-type CA 'EA'.
ROLLBACK WORK.
MESSAGE ID '26' TYPE 'I' NUMBER '000'
WITH ls_return-message.
ELSE.
COMMIT WORK.
ENDIF.

CONCATENATE lv_filename 'Uploded Sucessfully' INTO ls_file-filename.

copy_data_to_ref
(
EXPORTING
is_data
ls_file
CHANGING
cr_data
= er_entity ).
ENDMETHOD.

 

Double Click on the Service Maintenance on the right hand side you can see the option Register .Select the system and register the service.

18.png

Assign the package and click on continue to register the service.

19.png

Testing the Service

Go to the T.code /IWFND/MAINT_SERVICE

Filter the service which we created just now.

20.png

Add the file

21.png

22.png

To upload the attachment we must maintain SLUG value as shown below

23.png

Click on execute the response will be shown on the right hand side

24.png

 

Before Adding the Files

Go to the T.code CV03N and provide the details as given below

25.png

26.png

 

After Adding the Files

27.png

Viewing all 137 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>