I Bet They Make Great Wine
Jun/29/06 12:28 Filed in: Personal
Australia presents... Saint Derycke's Wood Vineyards and Winery. This can only be good.
|
A Generic ALV Grid Report Class
Jun/28/06 12:34 Filed in: SAP
If you’ve ever used ABAP Objects to create ALV Grid reports, you will have noticed how the same code keeps coming up from one report to another. It doesn’t have to be this way. Here’s a report class that reduces your report to simply gathering the data and instantiating an object.
Let’s build the class, and then show a little example report.
In transaction se24 (the Class Builder), create a class zcl_report. Give it the following attributes:
These are all private instance variables that’ll be used later on. The g_title1 attribute is used in the handler for the top-of-page event. In the example, we’ll use a customer-specific function module to write the top of page, which uses this attribute for the report title. Obviously, in your environment, you should use as many or as few title fields as needed.
Next up, the constructor. It should have the following interface:
Import parameters:
Exceptions:
The code of the constructor looks like this:
That’s all there is to it. We need another method to send the data table to the grid. Let’s call it display. It’s a public instance method.
Changing parameters:
Exceptions:
The code is one simple method call:
The last method we need is the handler for the top-of-page event. It gets called when you print the report. Create a method called handle_print_top_of_page, and in the detail view, set it to be the event handler for event print_top_of_page of class cl_gui_alv_grid. You can make the method a private instance method. The code is simple:
As I mentioned before, this is a customer-specific function that does nothing but write a few lines with such gems as user name, date and time stamp, program name, system and client, name of the report, etc. You can leave it out, or replace it with your own.
That concludes the creation of the class. Go ahead and activate it. We’ll do the example next.
For the example, we’ll select some fields from table bseg for the first 100 rows, and display those.
The first step is to go to the data dictionary and create a structure with the fields we want to display. In our case, we could just use bseg, but what’s the fun in that? So off we go to transaction se11, and we create a structure called yyexample. It’s generally a good idea to create the structure with the same name as you’ll use for your program, but you can’t have an underscore in the second or third position of a structure. For our purposes, add fields bukrs, belnr, gjahr, buzei, and buzid from table bseg into the structure, and activate it.
Step two is to create the program. Go to transaction se38 (or se80, if you’re so inclined), and create a new program called y_example. I’ll go over the code bit by bit.
First, select your data:
Obviously, in a real report, the whole data selection would be more complicated, but this’ll do for our purposes.
Second, create an object:
Now we can tell the object what our data table is, and call a screen to display it:
So we end up calling screen 9000, but we don’t have a screen 9000, do we? Let’s double-click the screen number to create it. In the flow logic of the created screen, un-comment the two modules. The flow logic should look like this:
Create the modules by double-clicking on them. Their contents should be:
Double-click the word status, and enter the three codes back, exit, and cancel for the three navigation buttons (arrow on green circle, arrow on yellow circle, and x on red circle, respectively). Finally, double-click the word titlebar to create a title. This title will appear at the top of the screen in the title bar. Activate the status, the screen, and the program. That’s it. You can now run the example report and admire it in all its glory.
What we’ve ended up with is a generic ALV Grid report. Programs that use it become much simpler, and need less testing. They also use a common look, with common functionality, and use common headers when printed. Another advantage is that as the class gets refined and further developed, all the programs that use it immediately use the updated version.
What improvements could be made to the class? For one thing, we could add some more event handlers. Events such as print_top_of_list, print_end_of_page, and print_end_of_list could hold common code for displaying program parameters or whatever else is the standard in your company.
There are times when you need a report with functionality that you can’t abstract out so that every report has it. Drill-downs, for example. You want drill-down capability in one report, but not in all. The path for that is to create a subclass of zcl_report. That way, you can have your custom functions, but still reap the benefit of the common framework of the class.
One advantage of creating a structure in the data dictionary is that the presentation is further removed from the data selection. In the data dictionary, you can create data elements with the exact descriptive labels you need. As the user resizes the columns, the right length label is inserted in the column header.
Finally, I know, the sample report has no selection screen. That’s hardly a representative situation. I can’t imagine a report in a production environment where you would not want to restrict your data selection. But the example program isn’t meant to be a production program.
Enjoy.
Let’s build the class, and then show a little example report.
Building The Class
In transaction se24 (the Class Builder), create a class zcl_report. Give it the following attributes:
g_title1 type sytitle
g_grid type ref to cl_gui_alv_grid
gt_fieldcat type lvc_t_fcat
g_variant type disvariant
g_layout type lvc_s_layo
g_print type lvc_s_prnt
These are all private instance variables that’ll be used later on. The g_title1 attribute is used in the handler for the top-of-page event. In the example, we’ll use a customer-specific function module to write the top of page, which uses this attribute for the report title. Obviously, in your environment, you should use as many or as few title fields as needed.
Next up, the constructor. It should have the following interface:
Import parameters:
i_title1 type c optional
i_structure type dd02l-tabname
i_repid type syrepid
i_dynnr type sydynnr
Exceptions:
container_error
grid_error
field_catalog_error
The code of the constructor looks like this:
data: l_container type ref to cl_gui_docking_container.
* Store the report title
g_title1 = i_title1.
* Store the program name
g_variant-report = i_repid.
* Set some lay-out and print-related flags
g_layout-smalltitle = 'X'. "Default grid title to small font
g_layout-zebra = 'X'. "Output rows with alternating colors
g_layout-no_author = 'X'. "Allow users to enter global layouts
g_layout-cwidth_opt = 'X'. "Optimize column width
g_print-prnt_title = 'X'. "Do not print out grid title
* Create a docking container covering 95% of the screen.
* This container will hold the grid, and 95% is the maximum
* allowed size.
IF cl_gui_alv_grid=>offline( ) IS INITIAL.
CREATE OBJECT l_container
EXPORTING
repid = i_repid
dynnr = i_dynnr
ratio = 95
EXCEPTIONS
cntl_error = 1
cntl_system_error = 2
create_error = 3
lifetime_error = 4
lifetime_dynpro_dynpro_link = 5
OTHERS = 6
.
IF sy-subrc <> 0.
RAISE container_error.
ENDIF.
ENDIF.
* Create the ALV Grid object
CREATE OBJECT g_grid
EXPORTING
i_parent = l_container
EXCEPTIONS
error_cntl_create = 1
error_cntl_init = 2
error_cntl_link = 3
error_dp_create = 4
OTHERS = 5
.
IF sy-subrc <> 0.
RAISE grid_error.
ENDIF.
* Handle events
SET HANDLER me->handle_print_top_of_page FOR g_grid.
* Retrieve field specifications and descriptions for ALV Grid
CALL FUNCTION 'LVC_FIELDCATALOG_MERGE'
EXPORTING
i_structure_name = i_structure
CHANGING
ct_fieldcat = gt_fieldcat
EXCEPTIONS
inconsistent_interface = 1
program_error = 2
OTHERS = 3
.
IF sy-subrc <> 0.
RAISE field_catalog_error.
ENDIF.
That’s all there is to it. We need another method to send the data table to the grid. Let’s call it display. It’s a public instance method.
Changing parameters:
et_datatable type standard table
Exceptions:
display_error
The code is one simple method call:
call method g_grid->set_table_for_first_display
exporting
i_save = 'A'
is_layout = g_layout
is_variant = g_variant
is_print = g_print
changing
it_outtab = et_datatable
it_fieldcatalog = gt_fieldcat
exceptions
invalid_parameter_combination = 1
program_error = 2
too_many_lines = 3
others = 4
.
if sy-subrc ne 0.
raise display_error.
endif.
The last method we need is the handler for the top-of-page event. It gets called when you print the report. Create a method called handle_print_top_of_page, and in the detail view, set it to be the event handler for event print_top_of_page of class cl_gui_alv_grid. You can make the method a private instance method. The code is simple:
CALL FUNCTION 'Z_STANDARD_HEADING'
EXPORTING
i_title = g_title1
.
As I mentioned before, this is a customer-specific function that does nothing but write a few lines with such gems as user name, date and time stamp, program name, system and client, name of the report, etc. You can leave it out, or replace it with your own.
That concludes the creation of the class. Go ahead and activate it. We’ll do the example next.
An Example of The Class in Action
For the example, we’ll select some fields from table bseg for the first 100 rows, and display those.
The first step is to go to the data dictionary and create a structure with the fields we want to display. In our case, we could just use bseg, but what’s the fun in that? So off we go to transaction se11, and we create a structure called yyexample. It’s generally a good idea to create the structure with the same name as you’ll use for your program, but you can’t have an underscore in the second or third position of a structure. For our purposes, add fields bukrs, belnr, gjahr, buzei, and buzid from table bseg into the structure, and activate it.
Step two is to create the program. Go to transaction se38 (or se80, if you’re so inclined), and create a new program called y_example. I’ll go over the code bit by bit.
First, select your data:
data: gt_datatable type standard table of yyexample.
select bukrs belnr gjahr buzei buzid from bseg
into table gt_datatable up to 100 rows.
Obviously, in a real report, the whole data selection would be more complicated, but this’ll do for our purposes.
Second, create an object:
data: g_report type ref to zcl_report,
g_repid type syrepid.
* We need a helper field, because the value of sy-repid changes
* as you enter the constructor of the class.
g_repid = sy-repid.
CREATE OBJECT g_report
EXPORTING
I_TITLE1 = 'Sample Report'
i_structure = 'YYEXAMPLE' “Name of the DDIC structure
i_repid = g_repid
i_dynnr = '9000'
EXCEPTIONS
CONTAINER_ERROR = 1
GRID_ERROR = 2
FIELD_CATALOG_ERROR = 3
others = 4
.
IF sy-subrc <> 0.
* Do some error handling here
ENDIF.
Now we can tell the object what our data table is, and call a screen to display it:
CALL METHOD g_report->display
CHANGING
et_datatable = gt_datatable
EXCEPTIONS
DISPLAY_ERROR = 1
others = 2
.
IF sy-subrc <> 0.
* Do some error handling here
ENDIF.
call screen 9000.
So we end up calling screen 9000, but we don’t have a screen 9000, do we? Let’s double-click the screen number to create it. In the flow logic of the created screen, un-comment the two modules. The flow logic should look like this:
PROCESS BEFORE OUTPUT.
MODULE STATUS_9000.
*
PROCESS AFTER INPUT.
MODULE USER_COMMAND_9000.
Create the modules by double-clicking on them. Their contents should be:
module STATUS_9000 output.
set pf-status 'STATUS'.
set titlebar 'TITLEBAR'.
endmodule. " STATUS_9000 OUTPUT
module USER_COMMAND_9000 input.
case sy-ucomm.
when 'BACK'.
set screen 0.
leave screen.
when 'EXIT'.
set screen 0.
leave screen.
when 'CANCEL'.
set screen 0.
leave screen.
endcase.
endmodule. " USER_COMMAND_9000 INPUT
Double-click the word status, and enter the three codes back, exit, and cancel for the three navigation buttons (arrow on green circle, arrow on yellow circle, and x on red circle, respectively). Finally, double-click the word titlebar to create a title. This title will appear at the top of the screen in the title bar. Activate the status, the screen, and the program. That’s it. You can now run the example report and admire it in all its glory.
Some Final Thoughts
What we’ve ended up with is a generic ALV Grid report. Programs that use it become much simpler, and need less testing. They also use a common look, with common functionality, and use common headers when printed. Another advantage is that as the class gets refined and further developed, all the programs that use it immediately use the updated version.
What improvements could be made to the class? For one thing, we could add some more event handlers. Events such as print_top_of_list, print_end_of_page, and print_end_of_list could hold common code for displaying program parameters or whatever else is the standard in your company.
There are times when you need a report with functionality that you can’t abstract out so that every report has it. Drill-downs, for example. You want drill-down capability in one report, but not in all. The path for that is to create a subclass of zcl_report. That way, you can have your custom functions, but still reap the benefit of the common framework of the class.
One advantage of creating a structure in the data dictionary is that the presentation is further removed from the data selection. In the data dictionary, you can create data elements with the exact descriptive labels you need. As the user resizes the columns, the right length label is inserted in the column header.
Finally, I know, the sample report has no selection screen. That’s hardly a representative situation. I can’t imagine a report in a production environment where you would not want to restrict your data selection. But the example program isn’t meant to be a production program.
Enjoy.