ABAP report programming has its advantages. It’s quick and simple, but it comes with some limitations. If you strive for sophistication and flexible reuse, you should consider some of the advanced programming techniques I discuss in this weblog.
Suppose you have a simple report that consist of a selection screen and an ALV grid (hopefully programmed with the new SALV service classes). It will probably look something like this:
REPORT zalv.
TABLES:
mytable.
DATA:
gt_mydata TYPE STANDARD TABLE OF mytable.
SELECT-OPTIONS:
s_this FOR mytable-this,
s_that FOR mytable-that.
START-OF-SELECTION.
PERFORM select_data.
PERFORM display_alv.
FORM select_data.
…
ENDFORM.
FORM display_alv.
…
ENDFORM.
Depending on how cleanly you like to program, you might eliminate the obsolete TABLES statement, pass the select-options and table as parameters (to avoid accessing global data in form routines), and use a local application class with (mostly private) methods instead of form routines.
Once your report is fully functional and neatly polished, you still have serious limitations left:
The only way to programmatically call your report or transaction, say, from a different screen or function module, would would be the SUBMIT … AND RETURN or CALL TRANSACTION statements.
But these two come with serious drawbacks: Both SUBMIT … AND RETURN and CALL TRANSACTION
Causing the creation of a new internal mode means that the states of every function group, class, and object instance you have touched and changed during the course of your main program is reset. If you have, say, initialized the Business Application Log function group and opened a log to which you want to add entries, you’re out of luck – the log lives in the internal mode of the caller but not the called program.
Same with any ABAP classes that you may have touched: All of their class constructors will have to be executed again in the new internal mode, different instances will be created for singleton, and the state of the internal mode will generally behave like a remotely called system (CALL FUNCTION … DESTINATION), except that you won’t be able to return to it with repeated function calls and that no call-back (as in CALL FUNCTION … DESTINATION ‘BACK’) is possible.
Calling a program with SUBMIT or CALL TRANSACTION may cause a database commit that affects your transaction if any of the triggers listed here in the SAP Library is processed (e.g. a selection screen, messages of certain types, RFC calls, list output). This means that all pending database transactions will be closed, irrevocably persisting database changes made with the OpenSQL commands INSERT, UPDATE, or MODIFY. Calling ROLLBACK WORK afterwards will have no effect.
(By the way, a database commit is not the same as the end of an LUW as triggered with the COMMIT WORK statement: COMMIT WORK will not only cause a database commit, but also execute any routine registered with PERFORM … ON COMMIT and any function module invoked with CALL FUNCTION … IN UPDATE TASK. It does a lot of highly interesting and well-documented stuff and is very much worth an in-depth exploration.
Thanks to Sandra Rossi for pointing out that the DB_COMMIT occurs not always but under certain circumstances.)
When I create ABAP Dynpros, I usually don’t place them in reports but in function groups. This allows me to create function modules that can
Being able to conveniently pass parameters into a screen and receive result parameters is very useful when you want to reuse your screen in many meaningful scenarios.
You can:
Needless to say, being able to call a screen as a function module is also helpful because it allows for dynamic calls across programs, packages, and software components, and is a vital programming technique for creating frameworks.
By the way, I would like to be able to embed my dynpros into classes, but ABAP doesn’t support that. I’m forced to place my dynpro in a function group and create a function module in which to put the CALL SCREEN command (it has to be in a modularization unit of the same main program), but I try to put as much of the application code as possible into classes. If I see no potential for reuse, these may be local classes in the same function group. If I do see some reuse potential for the future, e.g. creating subclasses with specialized or enhanced functionality, I create global classes which have better visibility and can be reused outside the function group.
This is especially useful because it allows me to expose the class, and not the function module, as the interface for usage by anyone who wants to use my screen. This permits better type safety than function calls while allowing for sophisticated features like polymorphism at screen level, which is a basic feature in modern environments like Web Dynpro but typically not available in ABAP Dynpro.
Moving normal dynpros from a report into a function group is one thing – but the selection screen of a report is a different thing. Why would we want to create a selection screen (as opposed to a normal dynpro)?
The answer:
However, there are a number of differences:
So in order to create a selection screen in your function module, you have to do the following. In your global data include or a new include in the direct vincinity, define the selection screen as follows:
TABLES:
mytable.
SELECTION-SCREEN BEGIN OF SCREEN 0100 WITH TITLE TEXT-100.
SELECT-OPTIONS:
s_this FOR mytable-this,
s_that FOR mytable-that.
SELECTION-SCREEN END OF SCREEN 0100.
Call the selection screen from somewhere in the same function group. I usually create a method in the local application class inside that function group:
METHOD start_dialog.
CALL SELECTION-SCREEN 0100.
ENDMETHOD.
When working with a global class, you cannot use CALL SELECTION-SCREEN or CALL SCREEN in its methods. You have to implement it somewhere inside your function group and have the global class delegate the call there, most easily by means of a function module.
Now you need to handle user commands. Create a method in your application class to handle all function codes:
CLASS lcl_application DEFINITION.
PUBLIC-SECTION.
CLASS-METHODS:
handle_function_code IMPORTING VALUE(iv_fcode) TYPE sy-ucomm.
…
ENDCLASS.
CLASS lcl_application IMPLEMENTATION.
METHOD handle_function_code.
CASE iv_fcode.
WHEN 'CRET'. " execute
select_data( ).
display_alv( ).
WHEN 'CBAC' or 'CEND' or 'CCAN'.
LEAVE PROGRAM.
ENDCASE.
ENDMETHOD.
…
ENDCLASS.
(Whatever code was previously located in the START-OF-SELECTION processing block should be moved to the branch for the CRET function code.)
In order for the handle_function_code( ) method to be called, we need to implement the corresponding selection screen events right below the definition of the selection screen:
AT SELECTION-SCREEN.
lcl_application=>handle_function_code( sy-ucomm ).
AT SELECTION-SCREEN ON EXIT-COMMAND.
lcl_application=>handle_function_code( sy-ucomm ).
Now only one problem remains: After the execution of our former START-OF-SELECTION processing block, we will not return to the selection screen but to the position immediately after the CALL SELECTION-SCREEN command, which typically brings us right back to the caller.
To modify this behaviour so that our application behaves more like a standard report, you can change the start_dialog( ) method as follows:
METHOD start_dialog.
DO.
CALL SELECTION-SCREEN 0100.
ENDDO.
ENDMETHOD.
The only way out of this look is through the LEAVE PROGRAM command in our handle_function_code( ) method. But of course you can implement other ways and more explicit checks and flow controls to navigate back or elsewhere.
If want to learn more about object-oriented ABAP dynpro programming, you might want to explore the BUS_SCREEN framework in package BUS_TOOLS, package interface BUS_SCREEN. It provides abstract base classes for main screens, subscreens, tabstrips, and tabs – and if you feel very explorative, you can do what I did and find out how to use it by analyzing the BUS_LOCATOR framework (which is the foundation of the Business Partner search bar in transaction BP).
While I don’t want to turn this blog into a shameless plug, it feels appropriate to mention that you can also read up on it in the SAP Press book on ABAP programming I wrote with my esteemed colleague and fellow SAP Mentor Tobias Trapp, "ABAP Objects: Application Development from Scratch" (German edition: "Anwendungsentwicklung mit ABAP Objects").
By taking normal dynpros and selection-screens out of standard reports and encapsulating them with function groups and global classes, we gain many advantages:
It’s a bit more design-intensive and requires more lines of code but especially the increased reusability is worth the effort in many cases.
P.S.: If you're interested in ABAP Dynpro programming, you might want to read my recent blog post Kiss of Life for ABAP Dynpro – It’s going to stay, so let’s improve the integration.