Despite the heavy investment in SAP Analytics Cloud (SAC), SAP Business Planning and Consolidation (BPC) remains one of the main offerings from SAP as a complete planning and consolidation solution. Whilst SAP BPC Embedded seems to be the preferred choice, many customers are still using and developing their standard version. Because BPC Standard can be business (rather than IT) owned, it is still an attractive proposition, and in this blog, I’ll be sharing a few simple tricks to maximise BPC Standard’s user centricity – even for existing implementations.
1. Tell your users what the packages do
Having worked with BPC users closely for almost three and a half years, I can tell you that nothing frustrates finance users more than not knowing what calculations are triggered when they run packages. After all, BPC’s sophisticated audit logging functionality puts their actions on file – who would want to be responsible for changing numbers without understanding the full scope of their changes?
Although it might look simplistic, simply adding ‘Prompts’ into Data Manager Packages can supply users with information that will save them research time looking for documentation and raising tickets:
Figure 1: BPC Standard (AFO 2.5.3) data manager package with title included as prompt.
Figure 2: BPC Standard (AFO 2.5.3) data manager package with information in the prompt.
If you maintain this simple information structure in all of your packages, you’ll give users the opportunity to further their understanding of the system while also simplifying the onboarding process.
Sample code:
PROMPT(SELECTINPUT,,,,"%CATEGORY_DIM%,%CURRENCY_DIM%,%TIME_DIM%")
PROMPT(MESSAGE,"****************************************************************")
PROMPT(MESSAGE,"Calculate Forecast (scroll down for package description)")
PROMPT(MESSAGE,"****************************************************************")
PROMPT(MESSAGE,"")
PROMPT(MESSAGE,"This package ensures financial integrity for the Forecast in LC by executing: ")
PROMPT(MESSAGE,"1. Translate to Local Currency from Transaction Currency")
PROMPT(MESSAGE,"2. Calculate Gross Margin")
PROMPT(MESSAGE,"3. Allocate BTE")
PROMPT(MESSAGE,"4. Calculate Depreciation")
PROMPT(MESSAGE,"5. Transfer BSE")
PROMPT(MESSAGE,"6. Transfer Overheads")
PROMPT(MESSAGE,"7. Calculate P&L")
PROMPT(MESSAGE,"8. Calculate BS")
PROMPT(MESSAGE,"9. Generate Opening & Closing balances")
PROMPT(MESSAGE,"10. Calculate Indirect Cash Flow")
PROMPT(MESSAGE,"11. Calculate Pricing Assumptions")
PROMPT(MESSAGE,"12. Mgmt Eliminations in LC")
PROMPT(MESSAGE,"")
2. Be smart with local members
Taking a step-back to question how you use local members can make a significant difference to how understandable reports and input forms are for both users and for future support. Calling local members ‘variance’ might seem like a natural thing to do, but when you’ve got 20 local members across rows/columns, it can get a little tough to see the wood from the trees, especially if you aren’t the author. Conversely, being too specific with local member names can cause formatting sheet nightmares – we’ve all been there!
Figure 3:BPC Standard’s Report Editor Screen (AFO 2.5.3) – which local member is which again?!
Naming local members after the format they provide, rather than the calculation they perform will simplify the formatting sheet and make local members easily recognisable in the report editor e.g. “LM_BOLD_BLUE” – not so hard to decipher this code!
Bonus trick:
Use local members to act as headers for other local members; this will help you to differentiate those used in the rows and columns:
Figure 4 - BPC Standard - AFO 253 - Local Members acting as headers.
3. Standardise information in reports / input forms
Let’s be honest, when you build a complex report using the SAP EPM add-in, it’s pretty satisfying to hide the rows and columns storing your formulas and logic to give a façade of an effortless data refresh. But is this really helpful for the user?
One of the best parts of BPC Standard and Analysis for Office is that users can create their own reports in seconds. Showing users how the admins have built complex functionality in an understandable and approachable style makes it so much easier for users to pick up new reporting skills.
Figure 5 - BPC Standard - AFO 253 - simple user selections
I recommend providing standardised documentation, configuration and information sections in reports and input forms.
Documentation = Here, keep a log of the changes made to the report at every opportunity. Tell the user the purpose of the report and explain any complex formulas, logic or derivations that are used in plain English.
Configuration = In the config section, detail how each dimension is used in the report. For any dimension in the row, column or page axis, detail how the members are selected. If formulas are required to derive selections then present this information in pre-prepared spaces so that precedents and descendants can be easily traced.
Figure 6 - BPC Standard - AFO 253 - Storing Configuration & Documentation info at the top of the report keeps things tidy and makes inevitable future changes much more manageable.
Information = A standardised information section which provides the simplest information that makes new users feel calm and ready to absorb information. Pointers such as ‘There is no need to edit this report, simply make selections and refresh’ can really make a difference to user confidence and will develop trust in your solution.
Figure 7 - BPC Standard - AFO 253 - Storing curated information like this is simple, and keeping it accessible will ensure your user feels like the report was made for them.
4. Make logging in ABAP as non-technical as possible
The functionality for returning custom errors from ABAP to users in BPC Standard is already pretty good. But, building formatting into error handling will even make those grey dulcet colours look smart; trawling through the data manager package’s formula log looking for something in English will be a thing of the past.
Displaying individual errors as bullet points will make deciphering why an input has been rejected a far simpler task:
Figure 8 - BPC Standard - AFO 253 - Input form results displayed clearly
When building custom writeback logic, think about returning multiple error messages for each input. If your code throws an exception at the first failed hurdle, the user will have to make a change
and then input all of their data again, only to be rejected once more.
If you pass the user all (but not too many to hurt their feelings!) of the causes of failure then the user can take the appropriate course of action all at once, saving time and effort:
Figure 9 - BPC Standard - AFO 253 - Probably stop counting once the user has broken 10 writeback rules....
4b. (But Also, Make them More Technical!)
While you are designing error handling to run simply and output pretty messages – why not throw in something for yourself? When your package logic raises an exception, read the callstack to return valuable information about where the error occurred. You can write this information into SLG1 or even put it into the package log (providing you format it well!). That way, when exceptions are raised, you’ll know exactly which line of code caused it and the support team can find the source of the problem immediately.
Figure 10 -Transaction SLG1 in BW 7.5 – showing the exact line of code which caused an exception. Now you know that Line 36 of the CHECK_ENT_MGMT method caused the error! Hurrah!
Sample Code:
METHOD get_callstack_log.
* Matt Roberts 14.02.2019
* Outputs the current callstack as a table for application logging
*
* Parameters:
* iv_skip - (Optional) Number of callstack lines to skip (from the deepest part of the callstack).
* If iv_skip = 0, this method, GET_CALLSTACK_LOG, would be included in the callstack output
* iv_top - (Optional) Number of lines to output. If zero, callstack is output until the end.
* rt_stack_msg - message table containing callstack details required
" Get callstack
DATA lt_stack TYPE cl_abap_get_call_stack=>formatted_entry_stack.
lt_stack = cl_abap_get_call_stack=>format_call_stack_with_struct( cl_abap_get_call_stack=>get_call_stack( ) ).
" trim and top message as required
CALL METHOD format_callstack
EXPORTING
it_stack = lt_stack
iv_skip = iv_skip
iv_top = iv_top
RECEIVING
rt_stack_msg = rt_stack_msg.
*** Flip Callstack round (so it reads 3rd method, 2nd method, method that called raise exception):
SORT rt_stack_msg BY time_stmp DESCENDING.
ENDMETHOD.
METHOD format_callstack.
* MR FEB 2019
* Formats a callstack into a message log
*
* Parameters:
* it_stack - Callstack from CL_ABAP_GET_CALL_STACK, type CL_ABAP_GET_CALL_STACK=>FORMATTED_ENTRY_STACK
* iv_skip - (Optional) Number of callstack lines to skip (from the deepest part of the callstack).
* If iv_skip = 0, this method, GET_CALLSTACK_LOG, would be included in the callstack output
* iv_top - (Optional) Number of lines to output. If zero, callstack is output until the end.
* rt_stack_msg - message table containing callstack details required
CONSTANTS: lc_max_len TYPE i VALUE 50.
DATA: lv_stop_idx TYPE i,
lv_event_part2 TYPE c LENGTH lc_max_len.
CLEAR: lv_event_part2.
" calculate index for when to stop output
IF iv_top EQ 0.
" Top 0 would output nothing. Instead, output everything
lv_stop_idx = lines( it_stack ).
ELSE.
" Stop after the top lines have been output, accounting for lines that have been skipped
lv_stop_idx = iv_skip + iv_top.
ENDIF.
" Filter for callstack lines required. Skip the first iv_skip lines (+1 adjustment for tabix), continue until the last line required
LOOP AT it_stack INTO DATA(lv_stack) FROM iv_skip + 1 TO lv_stop_idx.
CHECK lv_stack-event IS NOT INITIAL.
" event may be longer than 50 chars (max of msgv1) so split it into two (event has max length of 61 chars so only 2 parts):
IF strlen( lv_stack-event ) > lc_max_len.
" Get the string from chars 51 onwards into part 2.
lv_event_part2 = shift_left( val = lv_stack-event places = lc_max_len ).
ENDIF.
" select only the details of interest, add to output table
APPEND VALUE #(
msgv1 = lv_stack-event
msgv2 = lv_event_part2
msgv3 = lv_stack-line
time_stmp = sy-tabix "addition to enable sorting
) TO rt_stack_msg.
ENDLOOP.
So, with these neat and easily applicable tricks – your next (or current!) BPC implementation will be more user-centric than ever before. Share with me your best EPM tips by connecting with me on LinkedIn:
https://www.linkedin.com/in/mattroberts12/.