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:
COMM | Comment Entry |
DM | Data Manager |
DOCS | Document Modifications |
JRN | Journal Entry |
MAN | Manual 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 ASSIGNINGWHERE 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
COMPONENT
statement. Eg, the column account can then be accessed via
IFIS 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
"get account members READ TABLEWITH 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 STRUCTURETO . 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