Skip to Content

Useful ABAP code in BPC 7.X NW version

Applies to:

1) SAP BPC projects migrating from MS version to 7.X NW version.

2) New development on SAP BPC 7.X NW version.

Summary: The article explains few code snippets which interface with the backend SAP BI system to retrieve or write-back the required BPC data.

Prerequisites: Nascent knowledge of SAP BPC 7.X NW tool, in particular the coding conventions applicable in *.LGF file. Understanding of basic Object-oriented (OOPS) ABAP.

Introduction: A LGF level logic which is required to process large volume of application records will in most likeliness perform better when implemented in ABAP rather than employing the same logic through *.LGF file completely. The ABAP level logic is invoked by calling the BAdI. For more information on BAdI, please refer the following link: SAP Library – Enhancement Framework

To save the extra time overhead in read of application data whenever QUERY is set to ON in START_BADI… END_BADI construct, this parameter can be set to OFF and an equivalent ABAP logic can then fetch the application data. This way the code executes much faster. This kind of code snippet and few more are described below.

Code Snippet # 1

The below code reads a certain dimension’s master data

DATA: lo_dim TYPE REF TO cl_uja_dim,
      lr_dim_data
TYPE REF TO if_uja_dim_data,
      lt_attr_name
TYPE uja_t_attr_name,
      lt_sel
TYPE uj0_t_sel,
      ls_sel
TYPE uj0_s_sel,
      lr_data
TYPE REF TO data,
      ls_emp
TYPE REF TO data.
FIELDSYMBOLS: <lt_emp> TYPE STANDARD TABLE,
               <ls_emp>
TYPE ANY.

***Construct to generate the EMPLOYEE data from dimension library
REFRESH: lt_attr_name, lt_sel.

CLEAR: ls_sel.
TRY .

    CREATE OBJECT lo_dim
     
EXPORTING
        i_appset_id
= i_appset_id
        i_dimension
= ‘EMPLOYEE’.

  CATCH cx_uja_admin_error .

ENDTRY.

lr_dim_data = lo_dim.


” Append the list of attribute(s) for which the master data is generated

APPEND: ‘ID’ TO lt_attr_name.

” Bind the condition data to lt_sel table, this will become selection criteria

” analogous to the WHERE clause of a DB SELECT statement

ls_seldimension = ‘EMPLOYEE’.
ls_sel
attribute = ‘CALC’.
ls_sel
sign = ‘I’.
ls_sel
option = ‘EQ’.
ls_sel
low = ‘N’.

APPEND ls_sel TO lt_sel.

” GET DIMENSION MEMBERS
  TRY.
   
CALL METHOD lr_dim_data->read_mbr_data
     
EXPORTING
        it_attr_list
= lt_attr_name    “attribute list
        it_sel      
= lt_sel          “condition data
     
IMPORTING
        er_data     
= lr_data.        “reference of master data table

  CATCH cx_uja_admin_error .

ENDTRY.

  “Assign the referenced memory area to a field-symbol

ASSIGN lr_data->* TO <lt_emp>.

CREATE DATA ls_emp LIKE LINE OF <lt_emp>.

ASSIGN ls_emp->* TO <ls_emp>.
  ***End of construct

Points to note:

read_mbr_data method reads the relevant master data from dimension library. In addition to lt_attr_name and lt_sel, developer has options to give a particular hierarchy, or specify if only base members are required to be pulled.

Code Snippet # 2

The below code reads child members of a parent node from the hierarchy of dimension’s master data

DATA:   lo_dim TYPE REF TO cl_uja_dim,
        lr_dim_data
TYPE REF TO if_uja_dim_data,
        lt_base_en
TYPE uja_t_dim_member.
TRY.
   
CREATE OBJECT lo_dim
     
EXPORTING
        i_appset_id
= i_appset_id
        i_dimension
= ‘ENTITY’.

  CATCH cx_uja_admin_error.

ENDTRY.

lr_dim_data = lo_dim.


“GET THE CHILD NODES
CALL METHOD lr_dim_data->get_children_mbr
 
EXPORTING
    i_parent_mbr    
= ‘ENT_PAR01’
    if_only_base_mbr
= ‘X’
 
IMPORTING
    et_member       
= lt_base_en.

Points to note:

If if_only_base_mbr parameter is left blank then all the intermediate hierarchy level nodes are fetched in lt_base_en.

Code Snippet # 3

The below code reads an application’s transaction data and puts in a dynamic internal table

DATA: lt_sel TYPE uj0_t_sel,
      ls_sel
TYPE uj0_s_sel,
      ls_cv
TYPE ujk_s_cv,
      lo_appl
TYPE REF TO cl_uja_application,
      lt_appl_dim
TYPE uja_t_appl_dim,
      ls_appl_dim
LIKE LINE OF lt_appl_dim,
      lt_dim_name
TYPE ujq_t_dim,
      ls_dim_name
LIKE LINE OF lt_dim_name,
      lo_model
TYPE REF TO if_uj_model,
      lo_dataref
TYPE REF TO data.
FIELDSYMBOLS: <lt_tx_data> TYPE STANDARD TABLE.

*** Get structure of application data table****
CREATE OBJECT lo_appl
 
EXPORTING
    i_appset_id     
= i_appset_id “input parameter of method
    i_application_id
= i_appl_id. “input parameter of method

* Getting all dimensions for the given application and appset
REFRESH lt_appl_dim.
lo_appl
->get_appl_dim(
         
EXPORTING
              i_appl_id  
= i_appl_id
         
IMPORTING
              et_appl_dim
= lt_appl_dim ).

* populate internal table for SQE call
REFRESH lt_dim_name.

LOOP AT lt_appl_dim INTO ls_appl_dim.
  ls_dim_name
= ls_appl_dimdimension.
 
APPEND ls_dim_name TO lt_dim_name.

ENDLOOP.

* Including MEASURES dimension in table structure

ls_dim_name = ‘MEASURES’.

APPEND ls_dim_name TO lt_dim_name.

SORT lt_dim_name.

* Get the structure of the result

TRY.
    lo_model
= cl_uj_model=>get_model( i_appset_id ).
    lo_model
->create_tx_data_ref(
    
EXPORTING
       i_appl_name 
= i_appl_id
       i_type      
= ‘T’
       it_dim_name 
= lt_dim_name
       if_tech_name
= space
    
IMPORTING
       er_data     
= lo_dataref ).
 
CATCH cx_uj_static_check.

ENDTRY.

* Assigning the structure to table

ASSIGN lo_dataref->* TO <lt_tx_data>.
“Generating application data with below filter set


REFRESH lt_sel.

CLEAR ls_sel.

ls_seldimension = ‘RPTCURRENCY’.
ls_sel
low = ‘LC’.

APPEND ls_sel TO lt_sel.
” Reading the time values passed from the LGF script that calls this BAdI

ls_seldimension = ‘TIME’.

READ TABLE it_cv INTO ls_cv WITH KEY dimension = ‘TIME’.

IF sysubrc = 0.
 
LOOP AT ls_cvmember INTO ls_dim_member.
    ls_sel
low = ls_dim_member.
   
APPEND ls_sel TO lt_sel.
   
CLEAR ls_dim_member.
 
ENDLOOP.

ENDIF.

REFRESH <lt_tx_data>.

TRY.
   
“module call to populate the data records
   
CALL FUNCTION ‘UJQ_RUN_RSDRI_QUERY’
     
EXPORTING
        i_appset_id      
= i_appset_id
        i_appl_id        
= i_appl_id
        it_dim_name      
= lt_dim_name
        if_check_security
= abap_false
        it_sel           
= lt_sel      “condition data
     
IMPORTING
        et_data          
= <lt_tx_data>.
 
CATCH cx_ujq_exception.
ENDTRY.

Points to note:

UJQ_RUN_RSDRI_QUERY reads the application data only for the base level values of the dimensions and the function module can’t fetch the aggregated transaction data at parent level

Code Snippet # 4

The below code push (write) the data back to the application

DATA: l_count TYPE i,
      l_incr TYPE i,
      l_index1 TYPE p,
      l_index2 TYPE p.
DATA: rf_context_ro         TYPE REF TO if_uj_context,
      lo_wb_main_int        TYPE REF TO cl_ujr_write_back,
      context_ro            TYPE REF TO if_uj_context,
      wa_cl_uje_user        TYPE REF TO cl_uje_user,
      wa_user               TYPE        uj0_s_user,
      wa_work_status        TYPE        ujr_s_work_status,
      gi_message            TYPE        uj0_t_message,
      log                   TYPE        string,
      lt_dataref            TYPE REF TO data,
      ls_dataref            TYPE REF TO data,
      lo_ref                TYPE REF TO cx_root.


***Logic to process the ct_data***
” The logic processes ct_data, ct_data is  refreshed and the generated output is appended back to ct_data

” If write is set to OFF in the call to BAdI,

” the new ct_data will now be required to be written back explicitly through ABAP
***Logic to process the ct_data ends***


IF ct_data[] IS NOT INITIAL.  ” Check to make sure the buffer is not empty
* Get the current context details
  CALL METHOD cl_uj_context=>get_cur_context
    RECEIVING
      ro_context = context_ro.
  context_ro->switch_to_srvadmin( ).
* Assign the user details
  wa_user = context_ro->ds_user.
  TRY.
      CALL METHOD cl_uj_context=>set_cur_context
        EXPORTING
          i_appset_id   = i_appset_id
          is_user       = wa_user
          i_appl_id     = i_appl_id
          i_module_name = context_ro->d_calling_module.
    CATCH cx_uj_obj_not_found .
  ENDTRY.

  wa_work_status-module_id = cl_ujk_model=>g_module_id.

  DESCRIBE TABLE ct_data LINES l_count.

  l_incr = 0.

  DO .

    “40000 number based on PACKAGE_SIZE parameter of UJR_PARAM table

    l_index1 = 40000 * l_incr + 1.
    l_incr = l_incr + 1.
    l_index2 = 40000 * l_incr.

    IF l_count < l_index2.

      ” lt_tab_1 declared static internal table having identical structure as that of ct_data

      REFRESH lt_tab_1.

      APPEND LINES OF ct_data FROM l_index1 TO l_count TO lt_tab_1.

    ELSE.

      REFRESH lt_tab_1.

      APPEND LINES OF ct_data FROM l_index1 TO l_index2 TO lt_tab_1.

    ENDIF.

    SORT lt_tab_1. ” System takes less time to write to application when the data is sorted

    TRY.
        CREATE OBJECT lo_wb_main_int.

        CALL METHOD lo_wb_main_int->write_back_int
          EXPORTING
            is_work_status    = wa_work_status
            i_default_logic   = abap_false
            i_bypass_badi     = ‘X’
            i_sign_trans      = abap_false
            it_array          = lt_tab_1
            i_appl_id         = i_appl_id
            i_appset_id       = i_appset_id
          IMPORTING
            et_message        = gi_message
            es_status_records = l_status_records.
      CATCH cx_uj_static_check INTO lo_ref.
    ENDTRY.

    IF l_count < l_index2.

      “Exit the do.. enddo, as the last package is written to the application

      EXIT.

    ENDIF.

  ENDDO.
ENDIF. 

Points to note:

Whenever the data is in process of writing back to application or commiting to DB, the system (based on the filter setting in write-back BAdI) triggers the write-back BAdI. Also the default logic is triggered after the entire process of data save is complete. If this trigger of “extra” logic is not required, then the write_back_int method has i_default_logic and i_bypass_badi as input parameters which can be used to specify to system whether the write-back or the default part should be triggered or not.

Code Snippet # 5

The logic in code snippet # 2 pulls all the base members, immaterial of the level of base members, for a specified parent. Likewise, there is often times a need to read all parents at different levels for a certain base level member. The below code reads all the ascendants for a specified base member from the hierarchy of dimension’s master data.

DATA: lo_dim TYPE REF TO cl_uja_dim,
      lr_dim_data
TYPE REF TO if_uja_dim_data,
      lr_hier_en_data
TYPE REF TO if_uja_hier,
      lt_mbr_name
TYPE uja_t_dim_member,
      lt_mbr_node
TYPE uja_t_mbr_node.

***Construct to read all ascendants (parents) of a base member
TRY.
   
CREATE OBJECT lo_dim
     
EXPORTING
        i_appset_id
= i_appset_id
        i_dimension
= ‘ENTITY’.

  CATCH cx_uja_admin_error.

ENDTRY.

lr_dim_data = lo_dim.


“get_hier_of_mbr will generate a reference to (BI backend type information of) ENT_BAS01 base member
CALL METHOD lr_dim_data->get_hier_of_mbr
 
EXPORTING
    i_member
= ‘ENT_BAS01’
  RECEIVING
    ro_hier 
= lr_hier_en_data. “BPC’s interface for ENTITY hierarchy

CALL METHOD lr_hier_en_data->get_parents
 
EXPORTING
    i_member       
= ‘ENT_BAS01’
    if_self        
= ‘X’          
    if_parent_after
= ‘X’
 
IMPORTING
    et_mbr_name    
= lt_mbr_name
    et_mbr_node    
= lt_mbr_node.

***End of construct

Points to note:

lt_mbr_name reads all the hierarchy nodes in an array format. For example: if ENT_BAS01 is at level 4 of the heirarchy with root node being level 1, then lt_mbr_name will have 4 entries containing “ENT_BAS01 and the 3 upper level parents”. lt_mbr_node will consist all the hierarchy like information of lt_mbr_name. lt_mbr_node contains columns as MEMBER, PARENT, TLEVEL describing the linkage of base node to its parent(s) within the hierarchy.

Conclusion:

The above 4 snippets are of most utility in a typical BPC 7.X NW project. One can explore the CL_UJA_DIM, CL_UJA_APPLICATION, and CL_UJR_WRITE_BACK interfaces (SE24) and implement different methods from it to further customize the requirement in addition to the described code snippets. 2 common parameters in all 4 codes are i_appset_id & i_appl_id and they are the input parameters present in the method of invoked BAdI, specifying the appset and application which triggered the logic originally.

Note: All the ABAP mentioned in this article can be directly utilized in any BPC7.X NW project, but it will require a further change and proper testing to suit the client’s requirement.

14 Comments
You must be Logged on to comment or reply to a post.
    • Hi Deepak,

      Do you mean BPC to it’s equivalent BW backend master data heirarchy or BPC to customised BW master data heirarchy?

      Regards,

      Pankaj

      • Customized. I have created FM extractor using Method read_mbr_data and loaded to new BW info object. Now I need same Hierarchy also for that. Quick response will be really appreciable.

        Regards

        Deepak Gera

    • Hi Mark,

      Sorry for the delay in response.

      Do you want to read the data at parent level in logic. If this is the case, then try doing as per below:

      1) Get all base level children for the intended parents via “get_children_mbr” method.

      2) Scope all such base member values and apply that in call of “UJQ_RUN_RSDRI_QUERY” function module to retrieve the transaction data.

      3) Loop the output transaction buffer and use collect to aggregate on parents for the originally intended results.

      If you mean to load the parent level data in the backend cube, then thats not possible, since in cube the data always resides on base level members.

      Regards.

    • Hi,

      The function modules are replaced by relevant ABAP methods. The classes are different. Although I have not worked on it, I understand that all the FM’s used in this article are only restrictive to BPC 7.X version and fail to work in EPM 10 version of the tool. For a view of how the ABAP looks like in EPM10, please scroll to the end of this SCN link. Rich Heilman has posted in it a relevant and useful code snippet.

      Regards.

  • Hi,

    If we use the write_back_int method to write back data to cube we are not getting log as we pass data to ct_data. is there any fix for this?

    Regards,

    Andrew

    • Hi Andrew,

      Can you try ET_MESSAGE parameter in custom logic BAdi method to display the log entries. You can also map es_status_records parameter from write_back_int method to ET_MESSAGE and get displayed in package logs. On logs, you will have the messages displayed under Formula log section.

      Regards.