Implementing BAdI’s for SAP BPC - Tips & Tricks

Introduction

SAP Business Planning and Consolidation (SAP BPC) is available in a version for SAP Netweaver and a version for the Microsoft platform. SAP BPC Netweaver has the advantage that ABAP (Advanced Business Application Programming) can be used, the SAP programming language.

The SAP BPC script logic can be converted to ABAP, using the SAP BAdI (Business Add Ins) functionality. BAdI is an enhancement to the standard version of the code of SAP. The performance of BAdIs is significant better than SAP BPC script.

This document combines several tips & tricks that will help you creating BAdI implementations for SAP BPC.

Choose the right BAdI

Open the BAdi Builder via transaction SE18 to display all the available Enhancement Spot entries. Several BAdIs relevant to SAP BPC, are available (eg UJR_WRITE_BACK, UJ_CUSTOM_LOGIC) and can be found by searching (press F4) for the string UJ*. Select the one that best fits your needs.

Figure 1: BAdI Builder

Once you display the selected enhancement spot, right-click Implementations and choose Create BAdI Implementation or select an existing implementation.

Figure 2: Create BAdI Implementation

Several objects need to be created:

  • Class (eg ZCLASS_BADI_JOURNAL); Create a new class per BAdI implementation,
  • Enhancement Implementation (eg ZBPC_ELEMENTARYPACK); One enhancement implementation can contain multiple BAdI implementations,
  • BAdI Implementation (eg ZBPC_BADI_JOURNAL_C_IFRS_DEV). One BAdI implementation can be linked to one or more applications.

Another way to access existing BAdI coding is via transaction SE80 Object Navigator. Enter the desired class and open the method to change it directly.

Figure 3: Object Navigator

Filter the BAdI

Once the Enhancement Implementation is available, you need to filter the BAdI. This guarantees that the code is only executed for the desired application set and application (which corresponds to InfoArea and InfoCube respectively). Multiple filter combinations can be added by clicking Create Filter Combination multiple times.

The filters are specific for each Enhancement Spot. Below is an example of the UJR_WRITE_BACK enhancement Spot.

Figure 4: Filter the BAdI

Module_ID can have several values:

COMMComment Entry
DMData Manager
DOCSDocument Modifications
JRNJournal Entry
MANManual Input

Declare Data

At the beginning of the method, several constants, field symbols, etc are declared. A constant is a fixed value, eg value AST (Account Type Asset). If this value has to be changed, the change has to be done only once.

CONSTANTS: c_acctype_act TYPE c LENGTH 3 VALUE 'AST', "asset

Field symbols and data references allow dynamic access to data objects. No need to specify the name of the object like with static access, field symbols and data references allow you to access and pass data objects whose name and attributes you do not know until runtime.

FIELD-SYMBOLS: 
	       TYPE table, "result itab

The declaration for each custom object used in this document is listed in Annex.

Access Incoming Data

Facilitating access to any dimension requires extensive coding, which is due to the abstractness of the BAdI implementation.

The following code enables access to the dimension account and should to be repeated for each dimension that needs to be consulted or updated.

"Find the account dimension by its type
LOOP AT it_dim_obj ASSIGNING 
	 
   WHERE dim_type = uj00_cs_dim_type-account.
      lo_account ?= 
		-dim_obj.
      ls_account = 
			.
ENDLOOP.
"preparation: create data structure and assign fields
CREATE DATA lr_data LIKE LINE OF ct_array.
ASSIGN lr_data->* TO 
				.
ASSIGN COMPONENT ls_account-dimension OF 
STRUCTURE 
					 TO 
						. "account

The object it_dim_obj contains the metadata of the dimensions. You can find the InfoObject name for each dimension and all of its attributes.

Figure 5: Contents of IT_DIM_OBJ

Figure 6: Contents of IT_DIM_OBJ(1)-T_ATTR

Figure 7: Contents of LS_ACCOUNT

The object uj00_cs_dim_type is defined in the global type pool UJ00. uj00_cs_dim_type-account can thus be replaced by uj00_cs_dim_type-entity etc for other dimensions.

begin of uj00_cs_dim_type,
  account   type uj_dim_type  value 'A',
  entity    type uj_dim_type  value 'E',
  time      type uj_dim_type  value 'T',
  currency  type uj_dim_type  value 'R',
  category  type uj_dim_type  value 'C',
  datasrc   type uj_dim_type  value 'D',
  intco     type uj_dim_type  value 'I',
  subtables type uj_dim_type  value 'S',
  user      type uj_dim_type  value 'U',
  group     type uj_dim_type  value 'G',
end of uj00_cs_dim_type.

Suppose the attributes account type and account group are needed. The list of attributes for a dimension is fetched using the function get_attr_list. The actual members are fetched via function read_mbr_data.

"Get attribute AccountType / Account Group
lo_account->get_attr_list( IMPORTING et_attr_list = lt_attr_list ).
LOOP AT lt_attr_list INTO ls_attr_list
      WHERE attribute_name = ujr0_c_attr_acctype OR
            attribute_name = c_attr_group.
  APPEND ls_attr_list-attribute_name TO lt_attr_name.
ENDLOOP.
 "Get Members of account
 CALL METHOD lo_account->read_mbr_data
    EXPORTING
      if_ret_hashtab = abap_true
      it_attr_list   = lt_attr_name "columns:attributes name list
 *     it_hier_list   = lt_hier_name "columns:hieracies name list
    IMPORTING
      er_data        = lr_data.
  ASSIGN lr_data->* TO 
	.

Figure 8: Contents of LT_ACCOUNT_MBR

The internal table ct_array is a generated object that contains all the records coming from the BPC application. Looping over this table ct_array gives access to each record.

LOOP AT ct_array INTO 
	.

During the first loop several objects are created that will capture the new, calculated records. Object lt_tab
is a temporary object that is created at runtime with the same structure as ct_array. Then the structure of lt_tab is assigned to the field-symbols so the latter has a structure and it is a table. To be able to work with one line of the table, the field-symbols is created similarly. A field-symbols can be assigned to the individual field of one line using the ASSIGN
COMPONENT
statement. Eg, the column account can then be accessed via .

 

IF 
	 IS NOT ASSIGNED.
 CREATE DATA lt_tab LIKE ct_array.
 ASSIGN lt_tab->* TO 
		.
 CREATE DATA lr_data_r LIKE LINE OF ct_array.
 ASSIGN lr_data_r->* TO 
			.
 ASSIGN COMPONENT ls_account-dimension OF 
     STRUCTURE 
				 TO 
					. "account
ENDIF.

Next we search the internal table containing the account member data for the current account and its attributes. These attributes are saved into the field-symbols and
.

"get account members
 READ TABLE 
	     WITH TABLE KEY (ujr0_c_member_id) = 
		     ASSIGNING 
			.
 IF sy-subrc = 0.
     ASSIGN COMPONENT c_attr_acctype OF 
         STRUCTURE 
				 TO 
					.
     ASSIGN COMPONENT c_attr_group OF 
         STRUCTURE 
						 TO 
							.

 

Now all fields are accessible via their corresponding field-symbols. All kinds of calculations and manipulations can be executed using these field-symbols.

Access Existing Data

When new data is sent from BPC towards the BW InfoCube in the backend, sometimes we need to have access to the data that already exists in the BW InfoCube. Data disaggregation is a perfect example for this. In BPC data can be entered at parent level and then this data can be distributed across its children prorated to the existing data. This makes most sense for dimensions like entity and time.

The following code retrieves the hierarchy information for entity and is also repeated for the time component.

lo_entity->get_hier_list( IMPORTING et_hier_info = lt_hier_info ).
  LOOP AT lt_hier_info INTO ls_hier_info.
    APPEND ls_hier_info-hier_name TO lt_hier_name.
  ENDLOOP.
  

Data is disaggregated pro rata to existing data. So we need to look up the existing data that corresponds to the data coming from the BPC application. This is achieved by launching a RSDRI query, which requires a list of dimensions to be returned and the list of filters to be applied. First step is to retrieve the cube metadata.

CLEAR ls_application.
  lo_appl_mgr->get(
    EXPORTING
      if_with_measures = abap_false     
      if_summary       = abap_false     
    IMPORTING
      es_application   = ls_application ).  

If the processed line is a parent, we need to get its children to be able to distribute the data across the children afterwards. This code has to be repeated for the time dimension.

CALL METHOD lo_entity->get_children_mbr
      EXPORTING
        i_parent_mbr     = 
	  
        i_level          = -1 
        if_only_base_mbr = abap_true  
      IMPORTING
        et_member        = lt_ent_children.
    DESCRIBE TABLE lt_ent_children LINES nr_ent_chil.

We prepare a range to filter on entity base members. The same range has to be expanded for the time dimension.

LOOP AT lt_ent_children INTO ls_base_en.
        ls_range-dimension = 'COMPANY'.
        ls_range-low = ls_base_en.
        APPEND ls_range TO lt_range.
      ENDLOOP.

We save the cube dimensions in a list and expand the range to use the same restrictions as in BPC.

REFRESH lt_dim_list.
      LOOP AT ls_application-dimensions INTO ls_dimensions.
        APPEND ls_dimensions-dimension TO lt_dim_list.
        ls_range-dimension = ls_dimensions-dimension.  
        ASSIGN COMPONENT ls_dimensions-dimension 
  OF STRUCTURE 
	 TO 
		.
        ls_range-low = 
			.
        APPEND ls_range TO lt_range.
      ENDLOOP.

We build a structure to capture the result of the RSDRI query

lo_appl_mgr->create_data_ref(
        EXPORTING
          i_data_type   = 'T'
          it_dim_name   = lt_dim_list
          if_tech_name  = abap_false
          if_signeddata = abap_true
        IMPORTING
          er_data       = lr_data ).
      ASSIGN lr_data->* TO 
	.

Now we can execute the RSDRI query which retrieves the required date from the SAP BW InfoCube and stores it in field symbols

TRY.
          lo_query = cl_ujo_query_factory=>get_query_adapter(
              i_appset_id = lv_environment_id
              i_appl_id   = lv_application_id ).
          lo_query->run_rsdri_query(
            EXPORTING
              it_dim_name       = lt_dim_list    
              it_range          = lt_range       
              if_check_security = abap_false     
             IMPORTING
               et_data           = 
	               et_message        = lt_message    
            ).
        CATCH cx_ujo_read.   
      ENDTRY.

Test your application

In order to debug the ABAP coding, set an external breakpoint and (un)post a test journal. Select the line of code where you would like to start debugging and hit the red button Set / Delete external breakpoint (Ctrl+Shift+F9) in the toolbar. Note that an icon appears in front of the selected line. Now you can (un)post a test journal, using the same SAP user and you should enter the debugger. Now you execute the code step by step and check the content of all fields.

Figure 9: Debugging in Class Builder

Conclusion

SAP BPC Netweaver can make use of ABAP, the SAP programming language. BAdI is an enhancement to the standard version of the code of SAP. The performance of BAdIs is significant better than SAP BPC script. These functionalities enable endless possibilities.

Annex

This annex contains the ABAP declarations for all fields contained in this insight.

  DATA:
        ls_record_r      TYPE  REF TO data,
        lt_attr_list     TYPE uja_t_attr, "Attributes info
        ls_attr_list     TYPE uja_s_attr,
        lt_attr_name     TYPE uja_t_attr_name, "Attribute names
        lr_data          TYPE REF TO data,
        lt_tab           TYPE REF TO data,
        ls_account       TYPE ujr_s_dim_handler, "account
        lo_account       TYPE REF TO if_uja_dim_data.
 
  FIELD-SYMBOLS:
        
	     TYPE ujr_s_dim_handler,
        
		 TYPE HASHED TABLE, "All account members
        
			 TYPE any,  
        
				      TYPE uj_dim_member, "account member of current rec        
        
					        TYPE any, "account group
        
						      TYPE any, "account type
        
							     TYPE table, "result itab
        
								    TYPE any, "result record
        
									    TYPE uj_dim_member,"account member of result rec
        
										      TYPE any, "attr GROUP of result record