Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
antonsikidin
Participant
Macros is a powerful tool that increase boilerplate writing speed. Unfortunately, exploitation of  macros is forbidden in many projects because macros cannot be debugged.

Zmacros is my development to generate a boilerplate in few clicks.

Source: https://github.com/AntonSikidin/zmacros

Clone with abapGit

Transaction – Zmacros





  • F8 – rerun program

  • Refresh – reread template and create variables

  • List – load template from db

  • ListAll – show template with versions

  • Save – save template to db

  • Run – populate template with values

  • Range gen – generate number range


 

FIRST CASE – GENERATION OF SCREENS.


If you want to get something like that:
*----------------------------
*-- top screen 0100
, ok_code TYPE sy-ucomm
, gr_alv_0100 TYPE REF TO cl_gui_alv_grid
, gr_cont_0100 TYPE REF TO cl_gui_custom_container
, gt_fieldcat_0100 TYPE lvc_t_fcat
, gt_sort_0100 TYPE lvc_t_sort
, gs_layout_0100 TYPE lvc_s_layo
, gs_vari_0100 TYPE disvariant



*----------------------------
*screen logic

PROCESS BEFORE OUTPUT.
MODULE STATUS_0100.
*
PROCESS AFTER INPUT.
MODULE USER_COMMAND_0100.


*----------------------------
*modules
MODULE status_0100 OUTPUT.
SET PF-STATUS 'STATUS0100'.
SET TITLEBAR 'TITLEBAR0100'.


IF gr_alv_0100 IS NOT BOUND.
CREATE OBJECT gr_cont_0100
EXPORTING
container_name = 'CONT_0100'.

CREATE OBJECT gr_alv_0100
EXPORTING
i_parent = gr_cont_0100.

gs_layout_0100-no_rowmove = 'X'.
gs_layout_0100-cwidth_opt = 'X'.

gs_layout_0100-zebra = 'X'.
gs_layout_0100-no_rowmark = ''.
gs_layout_0100-sel_mode = 'A'.

gs_vari_0100-report = sy-repid.
gs_vari_0100-handle = '0100'.


CALL FUNCTION 'LVC_FIELDCATALOG_MERGE'
EXPORTING
I_STRUCTURE_NAME = 'but100'
CHANGING
ct_fieldcat = gt_fieldcat_0100.


CALL METHOD gr_alv_0100->set_table_for_first_display
EXPORTING
i_save = 'A'
is_layout = gs_layout_0100
is_variant = gs_vari_0100
CHANGING
it_sort = gt_sort_0100
it_outtab = gt_0100
it_fieldcatalog = gt_fieldcat_0100.

ELSE.

CALL METHOD gr_alv_0100->get_frontend_layout
IMPORTING
es_layout = gs_layout_0100.

gs_layout_0100-cwidth_opt = 'X'.

CALL METHOD gr_alv_0100->set_frontend_layout
EXPORTING
is_layout = gs_layout_0100.


gr_alv_0100->refresh_table_display( ).
ENDIF.

ENDMODULE.




MODULE user_command_0100 INPUT.

CASE ok_code .
WHEN 'BACK'.
SET SCREEN 0.
* when 'XXXXXXX'.

ENDCASE.
clear ok_code.

ENDMODULE.

 

Use this template:
*----------------------------
*-- top screen \screen_number/
, ok_code TYPE sy-ucomm
, gr_alv_\screen_number/ TYPE REF TO cl_gui_alv_grid
, gr_cont_\screen_number/ TYPE REF TO cl_gui_custom_container
, gt_fieldcat_\screen_number/ TYPE lvc_t_fcat
, gt_sort_\screen_number/ TYPE lvc_t_sort
, gs_layout_\screen_number/ TYPE lvc_s_layo
, gs_vari_\screen_number/ TYPE disvariant



*----------------------------
*screen logic

PROCESS BEFORE OUTPUT.
MODULE STATUS_\screen_number/.
*
PROCESS AFTER INPUT.
MODULE USER_COMMAND_\screen_number/.


*----------------------------
*modules
MODULE status_\screen_number/ OUTPUT.
SET PF-STATUS 'STATUS\screen_number/'.
SET TITLEBAR 'TITLEBAR\screen_number/'.


IF gr_alv_\screen_number/ IS NOT BOUND.
CREATE OBJECT gr_cont_\screen_number/
EXPORTING
container_name = 'CONT_\screen_number/'.

CREATE OBJECT gr_alv_\screen_number/
EXPORTING
i_parent = gr_cont_\screen_number/.

gs_layout_\screen_number/-no_rowmove = 'X'.
gs_layout_\screen_number/-cwidth_opt = 'X'.

gs_layout_\screen_number/-zebra = 'X'.
gs_layout_\screen_number/-no_rowmark = ''.
gs_layout_\screen_number/-sel_mode = 'A'.

gs_vari_\screen_number/-report = sy-repid.
gs_vari_\screen_number/-handle = '\screen_number/'.


CALL FUNCTION 'LVC_FIELDCATALOG_MERGE'
EXPORTING
I_STRUCTURE_NAME = '\struct_name/'
CHANGING
ct_fieldcat = gt_fieldcat_\screen_number/.


CALL METHOD gr_alv_\screen_number/->set_table_for_first_display
EXPORTING
i_save = 'A'
is_layout = gs_layout_\screen_number/
is_variant = gs_vari_\screen_number/
CHANGING
it_sort = gt_sort_\screen_number/
it_outtab = gt_\screen_number/
it_fieldcatalog = gt_fieldcat_\screen_number/.

ELSE.

CALL METHOD gr_alv_\screen_number/->get_frontend_layout
IMPORTING
es_layout = gs_layout_\screen_number/.

gs_layout_\screen_number/-cwidth_opt = 'X'.

CALL METHOD gr_alv_\screen_number/->set_frontend_layout
EXPORTING
is_layout = gs_layout_\screen_number/.


gr_alv_\screen_number/->refresh_table_display( ).
ENDIF.

ENDMODULE.

MODULE user_command_\screen_number/ INPUT.

CASE ok_code .
WHEN 'BACK'.
SET SCREEN 0.
* when 'XXXXXXX'.

ENDCASE.
clear ok_code.

ENDMODULE.

 

Copy, past and press “Refresh”.


Fill \screen_number/ and \struct_name/ and press “Run”.

Result:
*----------------------------
*-- top screen 0100
, ok_code TYPE sy-ucomm
, gr_alv_0100 TYPE REF TO cl_gui_alv_grid
, gr_cont_0100 TYPE REF TO cl_gui_custom_container
, gt_fieldcat_0100 TYPE lvc_t_fcat
, gt_sort_0100 TYPE lvc_t_sort
, gs_layout_0100 TYPE lvc_s_layo
, gs_vari_0100 TYPE disvariant



*----------------------------
*screen logic

PROCESS BEFORE OUTPUT.
MODULE STATUS_0100.
*
PROCESS AFTER INPUT.
MODULE USER_COMMAND_0100.


*----------------------------
*modules
MODULE status_0100 OUTPUT.
SET PF-STATUS 'STATUS0100'.
SET TITLEBAR 'TITLEBAR0100'.


IF gr_alv_0100 IS NOT BOUND.
CREATE OBJECT gr_cont_0100
EXPORTING
container_name = 'CONT_0100'.

CREATE OBJECT gr_alv_0100
EXPORTING
i_parent = gr_cont_0100.

gs_layout_0100-no_rowmove = 'X'.
gs_layout_0100-cwidth_opt = 'X'.

gs_layout_0100-zebra = 'X'.
gs_layout_0100-no_rowmark = ''.
gs_layout_0100-sel_mode = 'A'.

gs_vari_0100-report = sy-repid.
gs_vari_0100-handle = '0100'.


CALL FUNCTION 'LVC_FIELDCATALOG_MERGE'
EXPORTING
I_STRUCTURE_NAME = 'pa0298'
CHANGING
ct_fieldcat = gt_fieldcat_0100.


CALL METHOD gr_alv_0100->set_table_for_first_display
EXPORTING
i_save = 'A'
is_layout = gs_layout_0100
is_variant = gs_vari_0100
CHANGING
it_sort = gt_sort_0100
it_outtab = gt_0100
it_fieldcatalog = gt_fieldcat_0100.

ELSE.

CALL METHOD gr_alv_0100->get_frontend_layout
IMPORTING
es_layout = gs_layout_0100.

gs_layout_0100-cwidth_opt = 'X'.

CALL METHOD gr_alv_0100->set_frontend_layout
EXPORTING
is_layout = gs_layout_0100.


gr_alv_0100->refresh_table_display( ).
ENDIF.

ENDMODULE.




MODULE user_command_0100 INPUT.

CASE ok_code .
WHEN 'BACK'.
SET SCREEN 0.
* when 'XXXXXXX'.

ENDCASE.
clear ok_code.

ENDMODULE.

Replace 0100 with 0200 and you get boilerplate for screen 0200.


As you can see ‘\’ and ‘/’ start and stop symbol define variable in template.

You can save template for future use by press “Save”.

A dialog will appear:


This dialog shows templates to update.

If it’s a new template - just press “Escape” and enter new name.




  • List” - load a last version of template

  • List all” - show all versions of all templates


 

SECOND CASE - TYPE DEFINITION.


If you want to get something like that:
    TYPES:
BEGIN OF t_data_row,
LINE TYPE CIFCOUNT,
T_TYPE TYPE CLASSTTYPE,
TABNAME TYPE TABNAME,
JJOIN TYPE TABNAME,
LEFT1 TYPE TABNAME,
RIGHT1 TYPE TABNAME,
LV_KEY_SELECT TYPE TABNAME,
LEFT_61 TYPE ECP_FIELDNAME,
RIGHT_61 TYPE ECP_FIELDNAME,
LV_KEY_JOIN TYPE ECP_FIELDNAME,
SORTORDER TYPE ANZST,
CRITERIAFOR TYPE ADDIFCTCOD,
DISABLE TYPE CLASSTTYPE,
OR1 TYPE AKB_NOTE,
OR2 TYPE AKB_NOTE,
OR3 TYPE AKB_NOTE,
OR4 TYPE AKB_NOTE,
END OF t_data_row .

 

Use this template:
    TYPES:
BEGIN OF t_\type/,
$1-1# TYPE $1-2#,
END OF t_\type/ .

Copy, past and press “Refresh”.


You can see table “Main” with variable \type/ and table1 with two fields.

Go to transaction se11 and copy field name and field type.

Next, copy desired fields.


Past in table1.


Press “Run” and you will get this:


As you can see there is a new kind of variable ‘$1-2#’.

  • $ - start symbol

  • # - end symbol

  • 1 – table 1

  • 2 – column 2


You can define any number of tables with any number of columns.

THIRD CASE - MOVING STRUCTURE FROM OLD TO NEW.


If you want to get something like that:
ls_new-TABNAME = ls_old-TAB .
ls_new-JJOIN = ls_old-JJOIN_old .
ls_new-LEFT1 = ls_old-LEF .
ls_new-RIGHT1 = ls_old-RIG .
ls_new-LV_KEY_SELECT = ls_old-LV_KEY_old .

Template:
ls_new-$1-1# = ls_old-$1-2# .

Copy, past and press “Refresh”.

Result:


FOURTH CASE USING ANY TYPE OF “CASE-WHEN”.


If you want to get something like that:
LOOP AT mt_alv_fieldcat INTO ls_alv_fieldcat WHERE tech EQ space
AND no_out EQ space.
CASE ls_alv_fieldcat-fieldname.
WHEN 'A38'.
lv_string = ls_totals-a38.
set_shift.
WHEN 'A39'.
lv_string = ls_totals-a39.
set_shift.
WHEN 'A40'.
lv_string = ls_totals-a40.
set_shift.
WHEN 'A41'.
lv_string = ls_totals-a41.
set_shift.
WHEN 'A42'.
lv_string = ls_totals-a42.
set_shift.
WHEN 'A43'.
lv_string = ls_totals-a43.
set_shift.
WHEN 'A44'.
lv_string = ls_totals-a44.
set_shift.
WHEN 'A45'.
lv_string = ls_totals-a45.
set_shift.
ENDCASE.
ADD ls_alv_fieldcat-outputlen TO lv_len.
ADD 1 TO lv_len.
ENDLOOP.

Template:
  LOOP AT mt_alv_fieldcat INTO ls_alv_fieldcat WHERE tech EQ space
AND no_out EQ space.
CASE ls_alv_fieldcat-fieldname.
>1
WHEN 'A$1-1#'.
lv_string = ls_totals-a$1-1#.
set_shift.
<1
ENDCASE.
ADD ls_alv_fieldcat-outputlen TO lv_len.
ADD 1 TO lv_len.
ENDLOOP.

Copy, past and press “Refresh”.

As you can see there is new punctuation - ‘>1’ and ‘<1’.

  • >1 – start of template that will be copied n-times, where n is number of lines in table1

  • <1 - end of template that will be copied n-times, where n is number of lines in table1


As in HTML all start and stop tag must be nested.

This is good:
>1
>2
$1-1# $2-1#
<2
<1

This is bad:
>1
>2
$1-1# $2-1#
<1
<2

and you will get error:


Very often you need a sequence of increasing or decreasing numbers is required.
In this case, you haveRange gen”.

Generate range from 2 to 15 and copy to clipboard.


Generate range from 15 to 2 and copy to clipboard.


Generate range from 02 to 15 with leading zeros and copy to clipboard.


Generate range from 015 to 002 with leading zeros and copy to clipboard.


All you need is past values in table.

Press “Run” and you will get this:


Go to more complex example.

With template:
$1-1#  $2-1# $3-1#

We can get something like this:



FIFTH CASE – THIS TOOL IS BEST FOR GENERATING ALL POSSIBLE VARIANTS FOR UNIT TESTING.


With this template you can test logic function.
>1
>2
>3
>4

perform logic_$1-1# using '$2-1#' '$3-1#' '$4-1#' changing lv_result.
write 😕 ' $1-1# on v1=$2-1# v2=$3-1# v3=$4-1# ' , lv_result .
<4
❤️
<2


<1


Result:
 perform logic_or using 'X' 'X'  'X' changing lv_result.
write 😕 ' or on v1=X v2=X v3=X ' , lv_result .

perform logic_or using 'X' 'X' '' changing lv_result.
write 😕 ' or on v1=X v2=X v3= ' , lv_result .

perform logic_or using 'X' '' 'X' changing lv_result.
write 😕 ' or on v1=X v2= v3=X ' , lv_result .

perform logic_or using 'X' '' '' changing lv_result.
write 😕 ' or on v1=X v2= v3= ' , lv_result .

perform logic_or using '' 'X' 'X' changing lv_result.
write 😕 ' or on v1= v2=X v3=X ' , lv_result .

perform logic_or using '' 'X' '' changing lv_result.
write 😕 ' or on v1= v2=X v3= ' , lv_result .

perform logic_or using '' '' 'X' changing lv_result.
write 😕 ' or on v1= v2= v3=X ' , lv_result .

perform logic_or using '' '' '' changing lv_result.
write 😕 ' or on v1= v2= v3= ' , lv_result .



perform logic_and using 'X' 'X' 'X' changing lv_result.
write 😕 ' and on v1=X v2=X v3=X ' , lv_result .

perform logic_and using 'X' 'X' '' changing lv_result.
write 😕 ' and on v1=X v2=X v3= ' , lv_result .

perform logic_and using 'X' '' 'X' changing lv_result.
write 😕 ' and on v1=X v2= v3=X ' , lv_result .

perform logic_and using 'X' '' '' changing lv_result.
write 😕 ' and on v1=X v2= v3= ' , lv_result .

perform logic_and using '' 'X' 'X' changing lv_result.
write 😕 ' and on v1= v2=X v3=X ' , lv_result .

perform logic_and using '' 'X' '' changing lv_result.
write 😕 ' and on v1= v2=X v3= ' , lv_result .

perform logic_and using '' '' 'X' changing lv_result.
write 😕 ' and on v1= v2= v3=X ' , lv_result .

perform logic_and using '' '' '' changing lv_result.
write 😕 ' and on v1= v2= v3= ' , lv_result .



perform logic_xor using 'X' 'X' 'X' changing lv_result.
write 😕 ' xor on v1=X v2=X v3=X ' , lv_result .

perform logic_xor using 'X' 'X' '' changing lv_result.
write 😕 ' xor on v1=X v2=X v3= ' , lv_result .

perform logic_xor using 'X' '' 'X' changing lv_result.
write 😕 ' xor on v1=X v2= v3=X ' , lv_result .

perform logic_xor using 'X' '' '' changing lv_result.
write 😕 ' xor on v1=X v2= v3= ' , lv_result .

perform logic_xor using '' 'X' 'X' changing lv_result.
write 😕 ' xor on v1= v2=X v3=X ' , lv_result .

perform logic_xor using '' 'X' '' changing lv_result.
write 😕 ' xor on v1= v2=X v3= ' , lv_result .

perform logic_xor using '' '' 'X' changing lv_result.
write 😕 ' xor on v1= v2= v3=X ' , lv_result .

perform logic_xor using '' '' '' changing lv_result.
write 😕 ' xor on v1= v2= v3= ' , lv_result .



perform logic_magic using 'X' 'X' 'X' changing lv_result.
write 😕 ' magic on v1=X v2=X v3=X ' , lv_result .

perform logic_magic using 'X' 'X' '' changing lv_result.
write 😕 ' magic on v1=X v2=X v3= ' , lv_result .

perform logic_magic using 'X' '' 'X' changing lv_result.
write 😕 ' magic on v1=X v2= v3=X ' , lv_result .

perform logic_magic using 'X' '' '' changing lv_result.
write 😕 ' magic on v1=X v2= v3= ' , lv_result .

perform logic_magic using '' 'X' 'X' changing lv_result.
write 😕 ' magic on v1= v2=X v3=X ' , lv_result .

perform logic_magic using '' 'X' '' changing lv_result.
write 😕 ' magic on v1= v2=X v3= ' , lv_result .

perform logic_magic using '' '' 'X' changing lv_result.
write 😕 ' magic on v1= v2= v3=X ' , lv_result .

perform logic_magic using '' '' '' changing lv_result.
write 😕 ' magic on v1= v2= v3= ' , lv_result .

Conclusion:

All my code is about increase your speed writing boilerplate and save your time. Hope it will be helpful.
10 Comments
shais
Participant

Interesting.

My 2 cents:

Since at the end of the day you would you like to include the generated code in your code, it would be nicer to include it in one of the implicit editor tools (instead of copy paste).

  1. For simple/static code, you may utilize the code templates, both in SAP GUI editor and in ADT.
  2. For dynamic code (wizard style), you may implement a custom pattern for the SAP GUI editor.
    (I’m not sure if a similar solution is available in ADT).
antonsikidin
Participant
0 Kudos
Hello shais

I know about this 2 options.

Did you try to implement my example in the way you propose?

We talking about large part of code >10 lines of code, how you fill template in way you propose with 3 tables of variable?

 
shais
Participant
0 Kudos
This can be achieved with the second option (custom pattern with dynamic pattern / FM implementation).

This is basically a freestyle ABAP code in which you can write whatever you wish (e.g. a popup ALV).
antonsikidin
Participant
0 Kudos

Hello shais

I assume that you have more experience on the subject. And your solution is more optimal.

Teach us!

Please share your knowledge and show your solution for my examples. If we reduce the development time in this way, I'll be very grateful.

shais
Participant
0 Kudos
Hi,

To be honest, I don't have "my" solution.

This is a simplified implementation of a pattern FM:
FUNCTION ztest_editor_exit.
*"----------------------------------------------------------------------
*"*"Local Interface:
*" TABLES
*" BUFFER TYPE RSWSOURCET
*" EXCEPTIONS
*" CANCELLED
*"----------------------------------------------------------------------

DATA lt_text TYPE STANDARD TABLE OF tdline.

CALL FUNCTION 'ZTEST_TEXT_EDITOR'
IMPORTING
et_new_text = lt_text.

APPEND LINES OF lt_text TO buffer.

ENDFUNCTION.

What can be done, for example, without many modifications, is calling your report and setting the results back into buffer (or converting your report into a modal dialog).
antonsikidin
Participant
0 Kudos

ok, this is better.

But write custom enhancement for any case for 1 use is more complex then use my solution.

Integrete my solution in ide also not good idea because in case of typo you need wipe 119 line of code and repeat.

Best practise fast modify – fast get result. In case code is what you want they can be copypasted to main program.

Did we come to conclusion that may program is the best solution to generate boilerplate?

shais
Participant
I guess it depends on the complexity,

but I agree that you suggestion does make sense in some use cases.
0 Kudos
hi Anton Sikidin,

good job~,i want use the tools in my sap sys, but our abap version only stay in 701, so i can't import your project code with abapGit. can you let me konw which way is do that use your code in my sys.

thx a lot of u!
antonsikidin
Participant
0 Kudos

Yes, i will update this repo in week, i am at vakation.

antonsikidin
Participant
0 Kudos
what error do you see when trying import?
i haven't access to system with sap 701 version, i cannot predict what can go wrong.
Labels in this area