A Real-World Report: SUBMIT, Variants, Background Jobs, Authority Checks, Message Class
1. What makes a report production-ready?
A report that works on your test data is not finished. In production, hundreds of users run it, with different permissions, on large data, sometimes at 3 a.m. with nobody watching.
| Quality | Tool in this blog |
|---|---|
| Secure | Authority checks |
| Friendly and translatable | Text elements and message classes |
| Reusable | Variants and SUBMIT |
| Schedulable | Background jobs and sy-batch |
| Resilient | TRY ... CATCH |
| Easy to start | A transaction code |
2. Text elements
Never hard-code text that users read. Text elements live outside the code, so they can be translated and changed without editing logic. In SE38 choose Goto > Text Elements.
| Kind | Used for | In code |
|---|---|---|
| Selection texts | Labels of parameters and select-options | Automatic (Blog 2) |
| Text symbols | Any other text in the program | TEXT-001 |
| List headings | Titles of classical lists | Set in the editor |
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-001.
...
SELECTION-SCREEN END OF BLOCK b1.
WRITE: / TEXT-002.
3. Message class (SE91)
A message class is a numbered list of messages, kept in SE91. Your code refers to the number, not the text.
- Open
SE91and create message classZSD. - Add messages by number. Use
&for values filled in at runtime:001No sales orders found for your selection002You are not authorized for sales organization &1003Maximum rows must be greater than zero004&1 sales order(s) read - Save and activate.
MESSAGE e002(zsd) WITH p_vkorg. " error, fills &1
MESSAGE s004(zsd) WITH lv_count. " success in the status bar
MESSAGE s001(zsd) DISPLAY LIKE 'E'. " shown as an error, but no stop
MESSAGE s004(zsd) WITH lv_count INTO lv_text. " only build the text
The letter after MESSAGE is the message type:
| Type | Meaning | Behaviour in dialog |
|---|---|---|
S | Success | Status bar message, program continues |
I | Information | Popup, program continues after Enter |
W | Warning | Status bar, user may continue with Enter |
E | Error | Status bar. On a selection screen the input stays open. In START-OF-SELECTION the program stops. |
A | Abort | Popup, then the transaction ends |
X | Exit | Short dump |
The same type behaves differently depending on the event, which is why validation messages belong in AT SELECTION-SCREEN. MESSAGE ... INTO builds the text without showing it, which is useful for logs.
4. Authority checks
Without a check, every user who can start your report sees all data. AUTHORITY-CHECK asks the system whether the user holds a permission.
AUTHORITY-CHECK OBJECT 'V_VBAK_VKO'
ID 'VKORG' FIELD lv_vkorg
ID 'VTWEG' DUMMY
ID 'SPART' DUMMY
ID 'ACTVT' FIELD '03'. " 03 = display
IF sy-subrc <> 0.
MESSAGE e002(zsd) WITH lv_vkorg.
ENDIF.
sy-subrc = 0: allowed.4: not allowed. Any other value is a technical problem (for example a wrong field name), so treat every non-zero value as no access.- List every field of the object. Use
DUMMYfor fields you do not want to check. Look up the fields inSU21. - Use a standard object where one fits. Create your own in
SU21only when none does.
Where and how often
- Before reading data. A check after the
SELECTmeans the data was already read. - Once per value, not per row. Check each sales organization once, then filter, instead of checking inside the loop.
- Validate explicit input in
AT SELECTION-SCREENso the user gets the error right away.
If a user may see no sales organization at all, your “allowed” range is empty, and vkorg IN @lt_allowed would return all rows. Always stop the report when the allowed list is empty.
| Transaction | Use |
|---|---|
SU21 | Display and create authorization objects |
SU53 | Shows the last failed authorization check of a user. Ask the user to run it. |
PFCG | Roles. Where administrators grant the permission. |
5. Report variants
A variant stores the values of a selection screen, so the user does not retype them. A monthly report can have a variant “last month, plant 1000”.
- Run the report and fill in the selection screen.
- Choose Save (the disk icon or Ctrl+S) and give the variant a name and description.
- In the attributes you can protect the variant, hide fields, and use selection variables such as “current date” so the variant never goes out of date.
- Load it any time with Get Variant (Shift+F5), or manage them in
SE38under Goto > Variants.
Variants are also what a background job uses to run the report with fixed inputs.
A report variant stores selection screen values. An ALV variant (Blog 10) stores grid layout. Users often confuse them.
6. SUBMIT: calling one report from another
" Run another report and come back
SUBMIT zsales_report AND RETURN.
" With a saved variant
SUBMIT zsales_report USING SELECTION-SET 'MONTHLY' AND RETURN.
" With explicit values
DATA lt_dates TYPE RANGE OF vbak-erdat.
lt_dates = VALUE #( ( sign = 'I' option = 'BT'
low = sy-datum - 30 high = sy-datum ) ).
SUBMIT zsales_report
WITH s_vkorg = '1000'
WITH s_erdat IN lt_dates
AND RETURN.
" Show the selection screen first, with the values pre-filled
SUBMIT zsales_report VIA SELECTION-SCREEN
WITH s_vkorg = '1000' AND RETURN.
| Addition | Meaning |
|---|---|
AND RETURN | Come back to the calling program afterwards. Without it, the calling program ends. |
WITH p = value | Fills a parameter |
WITH s IN range | Fills a select-option with a range table |
WITH SELECTION-TABLE lt | A prepared table of type rsparams, for dynamic cases |
USING SELECTION-SET 'V' | Uses a saved variant |
VIA SELECTION-SCREEN | Shows the selection screen to the user |
EXPORTING LIST TO MEMORY | Keeps the output list instead of showing it |
Getting data back
A submitted report cannot return values like a function. Use memory:
" In the called report: put data into ABAP memory
EXPORT orders = gt_orders TO MEMORY ID 'ZSALES_ORDERS'.
" In the calling report: read it, then clean up
SUBMIT zsales_report WITH s_vkorg = '1000' AND RETURN.
IMPORT orders = lt_orders FROM MEMORY ID 'ZSALES_ORDERS'.
FREE MEMORY ID 'ZSALES_ORDERS'.
| Memory | Statements | Reaches |
|---|---|---|
| ABAP memory | EXPORT / IMPORT ... MEMORY ID | Programs in the same call chain of one session |
| SAP memory | SET PARAMETER ID / GET PARAMETER ID | All sessions of the same user logon |
The importing variable must have the same type as the exported data, or the import fails.
7. Background processing
In dialog mode, a work process has a time limit, so long reports time out. A background job has no such limit and needs no user at the screen.
- Quick way: on the selection screen choose Program > Execute in Background (F9).
- Scheduled: in
SM36define the job: name, step (program and variant), and start time or repeat period. - Monitor: in
SM37check the job status, the job log, and the spool output.
SM36 schedules jobs. SM35 is for batch input sessions, an older data upload technique. Do not mix them up.
Rules for background-safe reports
- Detect the mode with
sy-batch: it is'X'in a background job. - Do not rely on popups. A type
IorWmessage only goes to the job log, and anEorAmessage cancels the job. - Do not use interactive features such as
GET CURSORorAT LINE-SELECTION. - Output goes to the spool.
REUSE_ALV_GRID_DISPLAYswitches to a list automatically in background. - Do not depend on
OBLIGATORYinputs the user must correct. Use a variant with all values set.
IF sy-batch = abap_true.
WRITE: / lv_text. " ends up in the spool
ELSE.
MESSAGE s004(zsd) WITH lv_count. " status bar for the user
ENDIF.
Scheduling from code
DATA: lv_jobname TYPE tbtcjob-jobname VALUE 'Z_SALES_NIGHTLY',
lv_jobcount TYPE tbtcjob-jobcount.
CALL FUNCTION 'JOB_OPEN'
EXPORTING jobname = lv_jobname
IMPORTING jobcount = lv_jobcount
EXCEPTIONS OTHERS = 1.
IF sy-subrc <> 0. RETURN. ENDIF.
SUBMIT zsales_report USING SELECTION-SET 'MONTHLY'
VIA JOB lv_jobname NUMBER lv_jobcount
AND RETURN.
CALL FUNCTION 'JOB_CLOSE'
EXPORTING jobname = lv_jobname
jobcount = lv_jobcount
strtimmed = abap_true " start immediately
EXCEPTIONS OTHERS = 1.
8. Error handling with TRY / CATCH
Many runtime errors are class-based exceptions. Catch them to keep the report running, or to give a clear message instead of a short dump.
TRY.
DATA(ls_order) = lt_orders[ vbeln = lv_vbeln ].
DATA(lv_avg) = lv_total / lines( lt_orders ).
CATCH cx_sy_itab_line_not_found.
MESSAGE s001(zsd) DISPLAY LIKE 'E'.
CATCH cx_sy_zerodivide INTO DATA(lx_div).
MESSAGE lx_div->get_text( ) TYPE 'S' DISPLAY LIKE 'E'.
ENDTRY.
| Exception | Raised when |
|---|---|
CX_SY_ITAB_LINE_NOT_FOUND | A table expression finds no row |
CX_SY_ZERODIVIDE | Division by zero |
CX_SY_CONVERSION_ERROR (and subclasses) | A value cannot be converted |
lx->get_text( )returns a readable message.- Catch the specific exception. Catching
CX_ROOThides real bugs, so use it only at the top level. - Function modules use classical
EXCEPTIONSandsy-subrc. Class methods use class-based exceptions.
9. Transaction code and transport
- Open
SE93, enter a name such asZSALES_RPT, and click Create. - Choose Program and selection screen (report transaction) and enter the report name.
- Save. Users can now start the report by typing the code. Access is controlled through the role (
S_TCODE) inPFCG, in addition to your own authority check.
Changes move through the landscape by transport request, created and released in SE09 or SE10: development, then quality, then production.
Fix the problem in development, test it in quality, and transport it. Variants, message classes and text elements are separate objects: check that they travel with the report. Ask your Basis team how variants are handled in your landscape.
10. The complete production-ready report
This report brings the series together: selection screen, text elements, message class ZSD, authority check, background-safe messages, error handling, and ALV. It uses the structure ZST_VBAK from Blog 5.
REPORT zsales_report_prod.
*---------------------------------------------------------------------*
* Text symbols: 001 = Selection
* Message class ZSD: 001 no data, 002 not authorized (&1),
* 003 max rows, 004 &1 order(s) read
*---------------------------------------------------------------------*
TYPES: ty_orders_t TYPE STANDARD TABLE OF zst_vbak WITH DEFAULT KEY,
ty_vkorg_r TYPE RANGE OF vbak-vkorg,
ty_erdat_r TYPE RANGE OF vbak-erdat.
DATA: gv_erdat TYPE vbak-erdat,
gv_vkorg TYPE vbak-vkorg.
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-001.
SELECT-OPTIONS: s_erdat FOR gv_erdat OBLIGATORY,
s_vkorg FOR gv_vkorg.
PARAMETERS p_max TYPE i DEFAULT 1000.
SELECTION-SCREEN END OF BLOCK b1.
*---------------------------------------------------------------------*
CLASS lcl_report DEFINITION FINAL.
PUBLIC SECTION.
METHODS:
is_authorized IMPORTING iv_vkorg TYPE vbak-vkorg
RETURNING VALUE(rv_ok) TYPE abap_bool,
get_data IMPORTING it_erdat TYPE ty_erdat_r
it_vkorg TYPE ty_vkorg_r
iv_max TYPE i,
display.
PRIVATE SECTION.
DATA mt_orders TYPE ty_orders_t.
METHODS:
get_allowed_vkorg RETURNING VALUE(rt_range) TYPE ty_vkorg_r,
notify IMPORTING iv_no TYPE symsgno
iv_v1 TYPE string OPTIONAL.
ENDCLASS.
CLASS lcl_report IMPLEMENTATION.
METHOD is_authorized.
AUTHORITY-CHECK OBJECT 'V_VBAK_VKO'
ID 'VKORG' FIELD iv_vkorg
ID 'VTWEG' DUMMY
ID 'SPART' DUMMY
ID 'ACTVT' FIELD '03'.
rv_ok = xsdbool( sy-subrc = 0 ).
ENDMETHOD.
METHOD get_allowed_vkorg.
" Check each sales organization once, not once per order
SELECT vkorg FROM tvko INTO TABLE @DATA(lt_all).
LOOP AT lt_all INTO DATA(ls_org).
IF is_authorized( ls_org-vkorg ) = abap_true.
APPEND VALUE #( sign = 'I' option = 'EQ' low = ls_org-vkorg ) TO rt_range.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD notify.
" Dialog: status bar. Background: write to the spool.
MESSAGE ID 'ZSD' TYPE 'S' NUMBER iv_no WITH iv_v1 INTO DATA(lv_text).
IF sy-batch = abap_true.
WRITE: / lv_text.
ELSE.
MESSAGE ID 'ZSD' TYPE 'S' NUMBER iv_no WITH iv_v1
DISPLAY LIKE 'E'.
ENDIF.
ENDMETHOD.
METHOD get_data.
DATA(lt_allowed) = get_allowed_vkorg( ).
" An empty allowed list would select EVERYTHING: stop instead
IF lt_allowed IS INITIAL.
notify( iv_no = '002' iv_v1 = '*' ).
RETURN.
ENDIF.
SELECT vbeln, erdat, ernam, vbtyp, netwr, waerk
FROM vbak
WHERE erdat IN @it_erdat
AND vkorg IN @it_vkorg
AND vkorg IN @lt_allowed
ORDER BY erdat DESCENDING, vbeln
INTO CORRESPONDING FIELDS OF TABLE @mt_orders
UP TO @iv_max ROWS.
IF sy-subrc <> 0.
notify( '001' ).
ENDIF.
ENDMETHOD.
METHOD display.
IF mt_orders IS INITIAL.
RETURN.
ENDIF.
DATA(lv_repid) = sy-repid.
DATA(ls_layout) = VALUE slis_layout_alv( zebra = 'X' colwidth_optimize = 'X' ).
DATA(ls_variant) = VALUE disvariant( report = lv_repid ).
" In a background job ALV switches to a list for the spool automatically
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY'
EXPORTING
i_callback_program = lv_repid
i_structure_name = 'ZST_VBAK'
is_layout = ls_layout
i_save = 'A'
is_variant = ls_variant
TABLES
t_outtab = mt_orders
EXCEPTIONS
program_error = 1
OTHERS = 2.
IF sy-subrc = 0.
notify( iv_no = '004' iv_v1 = |{ lines( mt_orders ) }| ).
ENDIF.
ENDMETHOD.
ENDCLASS.
*---------------------------------------------------------------------*
DATA go_report TYPE REF TO lcl_report.
INITIALIZATION.
go_report = NEW #( ).
s_erdat-sign = 'I'. s_erdat-option = 'BT'.
s_erdat-low = sy-datum - 30. s_erdat-high = sy-datum.
APPEND s_erdat.
AT SELECTION-SCREEN.
" Validate only on execute, so users can still click and edit
IF sy-ucomm = 'ONLI'.
IF p_max <= 0.
MESSAGE e003(zsd).
ENDIF.
LOOP AT s_vkorg INTO DATA(ls_vkorg) WHERE sign = 'I' AND option = 'EQ'.
IF go_report->is_authorized( ls_vkorg-low ) = abap_false.
MESSAGE e002(zsd) WITH ls_vkorg-low.
ENDIF.
ENDLOOP.
ENDIF.
START-OF-SELECTION.
TRY.
go_report->get_data( it_erdat = s_erdat[]
it_vkorg = s_vkorg[]
iv_max = p_max ).
go_report->display( ).
CATCH cx_sy_open_sql_error INTO DATA(lx_sql).
MESSAGE lx_sql->get_text( ) TYPE 'S' DISPLAY LIKE 'E'.
ENDTRY.
Setup checklist before you run it
- Structure
ZST_VBAKexists and is active (Blog 5). - Message class
ZSDhas messages 001 to 004 (section 3). - Text symbol
001exists. Selection texts are set for the three fields. - Your test user has authorization object
V_VBAK_VKOfor at least one sales organization. Check withSU53if the report stops. - Run it normally, then with F9 in the background and look at the spool in
SM37. - Create transaction
ZSALES_RPTinSE93.
11. Try it yourself
- Create message class
ZSDwith the four messages and replace any hard-coded text. - Test the report with a user who lacks authorization and read the failed check in
SU53. - Save a variant, then call the report with
SUBMIT ... USING SELECTION-SET. - Schedule the report in
SM36and read the spool inSM37. - Create a transaction code in
SE93and start the report from it.
12. Common mistakes
- Hard-coded text instead of text elements or message classes.
- No authority check, so every user sees all data.
- Authority check after the
SELECT. The data was already read. - An empty “allowed” range used in
IN. It selects everything. SUBMITwithoutAND RETURN. The calling program ends.- Relying on popups in a background job. Nobody sees them. Write to the spool or job log.
- An error message that cancels the job where a logged warning would do.
- Not handling
sy-subrcafterCALL FUNCTION. - Forgetting
FREE MEMORY IDafterIMPORT. - Editing variants or code directly in production instead of transporting.
Up next: Blog 15
What’s Next. SALV, CDS views, RAP, ABAP Development Tools, unit testing, and your learning roadmap.
WhatsApp us