Today one of the member of our telegram group aked how to display the negative sign of a value at the beginning instead of at the end and keeping the possibility to make get total and subtotal of the column. I never had such kind of request, so I needed to investigate on how to solve this quest.
Request for the test:
- Find a way to display the negative sign in front of the value
- Make sure that decimal notation of the user will still work
Question and decision how to implement:
- Can it be solved using standard functions?
- No, neather fieldcatalog nor ALV layout can handle this request.
- Must be developed here?
- Yes, we need a conversion routine. This means we have to create a functiongroup and two functionmodules
Index of implementation steps
- Thinking about the requirement and the difficulties while implementing.
- Creating a conversion routine.
- Creating functiongroup
- Creating functionmodule internal -> display
- Creating functionmodule display -> internal
- Activating new functiongroup and functionmodules
- Implementing the conversion routine
- Demo report
- Result
Implementation steps
Info: I haven’t included popup with warning and package assignment. I ignored all warnings that happened in this sample and I assigned all objects to the local package $TMP.
- Thinking about the requirement and the difficulties while implementing.
- Creating a conversion routine.
- CONVERSION_EXIT_xxxxx_INPUT
- CONVERSION_EXIT_xxxxx_OUTPUT
- Creating functiongroup
- Creating functionmodule internal -> display
- Creating functionmodule display -> internal
- Activating new functiongroup and functionmodules
- Implementing the conversion routine
- Implementing conversion routine to domain
- Implementing at runtime to ALV
- Demo report
- Result
- The fourth column “Unrestricted” doesn’t have conversion routine set, so the negative sign is at the end
- The fifth column “Unrestricted” has conversion routine set, so the negative sign is at the beginning as expected
- The sixth column “Assembly level” is an integer field and has conversion routine set, so the negative sign is at the beginning as expected
When implementing a conversion routine we have to think about on how to keep the number notation that the user have in the settings (TAC:SU01/SU3). This means that we have to create flexible EDIT MASKS in the output conversion routine
A conversion routine is identified by its five-place name (custom conversion routine should start with Y or Z) and is stored as a group of two function modules. The function modules have a fixed naming convention. The following function modules are assigned to conversion routine xxxxx:
To create the functiongroup, I use transaction SE37. Leave the Funtion Module field blank and navigate to menu->Goto->Function Groups->Create Group
Enter a name and the description for the group and Save.
Naviagte to the topinclude of the functiongroup and paste the code:
1 2 3 4 |
FUNCTION-POOL zstkoes_sign_conversion. "MESSAGE-ID.. DATA: gv_decimal_sign TYPE char1, gv_separator TYPE char1. |
In the initial screen of SE37 enter a name for the functionmodule “CONVERSION_EXIT_XXXXX_OUTPUT” replace XXXXX with the routine name you wish.
Assign the functionmodule to the new functiongroup, enter a Short text and Save.
It is needed to name one importing parameter INPUT.
It is needed to name one exporting parameter OUTPUT.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
" Get the Decimal Format from the user settings (SU01/SU3) " Dezimaldarstellung aus den Benutzereinstellungen holen (SU01/SU3) IF gv_decimal_sign IS INITIAL. CALL FUNCTION 'CLSE_SELECT_USR01' EXPORTING username = sy-uname iv_delete_buffer = abap_true IMPORTING decimal_sign = gv_decimal_sign separator = gv_separator. ENDIF. DATA(lv_input) = CONV string( input ). DATA(lv_negativ_number) = boolc( lv_input CS '-' ). REPLACE ALL OCCURRENCES OF '-' IN lv_input WITH ''. SPLIT lv_input AT '.' INTO DATA(lv_number) DATA(lv_decimals). REPLACE ALL OCCURRENCES OF '.' IN lv_input WITH ''. DO strlen( lv_number ) TIMES. IF sy-index EQ 1. DATA(lv_mask) = |_|. CONTINUE. ENDIF. lv_mask = |_{ COND #( WHEN sy-index MOD 3 EQ 1 THEN gv_separator ) }{ lv_mask }|. ENDDO. DO strlen( lv_decimals ) TIMES. IF sy-index EQ 1. lv_mask = |{ lv_mask }{ gv_decimal_sign }_|. CONTINUE. ENDIF. lv_mask = |{ lv_mask }_|. ENDDO. lv_mask = |{ COND #( WHEN lv_negativ_number EQ abap_true THEN |-| ) }{ lv_mask }|. WRITE lv_input TO output USING EDIT MASK lv_mask. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
DATA: lv_input TYPE string, lv_negativ_number TYPE abap_bool, lv_number TYPE string, lv_decimals TYPE string, lv_mask TYPE string, lv_string_len TYPE i. " Get the Decimal Format from the user settings (SU01/SU3) " Dezimaldarstellung aus den Benutzereinstellungen holen (SU01/SU3) IF gv_decimal_sign IS INITIAL. CALL FUNCTION 'CLSE_SELECT_USR01' EXPORTING username = sy-uname iv_delete_buffer = abap_true IMPORTING decimal_sign = gv_decimal_sign separator = gv_separator. ENDIF. lv_input = input. IF lv_input CS '-'. lv_negativ_number = boolc( lv_input CS '-' ). ENDIF. REPLACE ALL OCCURRENCES OF '-' IN lv_input WITH ''. SPLIT lv_input AT '.' INTO lv_number lv_decimals. REPLACE ALL OCCURRENCES OF '.' IN lv_input WITH ''. DO strlen( lv_number ) TIMES. IF sy-index EQ 1. lv_mask = '_'. CONTINUE. ENDIF. IF sy-index MOD 3 EQ 1. CONCATENATE gv_separator lv_mask INTO lv_mask. ENDIF. CONCATENATE '_' lv_mask INTO lv_mask. ENDDO. lv_string_len = strlen( lv_decimals ). DO lv_string_len TIMES. IF sy-index EQ 1. CONCATENATE lv_mask gv_decimal_sign '_' INTO lv_mask. CONTINUE. ENDIF. CONCATENATE lv_mask '_' INTO lv_mask. ENDDO. IF lv_negativ_number EQ abap_true. CONCATENATE '-' lv_mask INTO lv_mask. ENDIF. WRITE lv_input TO output USING EDIT MASK lv_mask. |
In the initial screen of SE37 enter a name for the functionmodule “CONVERSION_EXIT_XXXXX_INPUT” replace XXXXX with the routine name you wish.
Assign the functionmodule to the new functiongroup, enter a Short text and Save.
It is needed to name one importing parameter INPUT.
It is needed to name one exporting parameter OUTPUT.
For error handling if user enters a non numeric value it is needed to name the exception parameter ERROR_MESSAGE.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
" Get the Decimal Format from the user settings (SU01/SU3) " Dezimaldarstellung aus den Benutzereinstellungen holen (SU01/SU3) IF gv_decimal_sign IS INITIAL. CALL FUNCTION 'CLSE_SELECT_USR01' EXPORTING username = sy-uname iv_delete_buffer = abap_true IMPORTING decimal_sign = gv_decimal_sign separator = gv_separator. ENDIF. DATA(lv_input) = CONV string( input ). REPLACE ALL OCCURRENCES OF gv_separator IN lv_input WITH ''. REPLACE gv_decimal_sign IN lv_input WITH '.'. CONDENSE lv_input NO-GAPS. TRY. output = lv_input. CATCH cx_sy_conversion_no_number. " Clear message fields of sy structure, so that 'Conversion error' will be raised " Alle Message Felder der sy Struktur löschen, damit 'Fehler bei der Konvertierung.' geworfen wird CLEAR: sy-msgty, sy-msgno, sy-msgv1, sy-msgv2, sy-msgv3, sy-msgv4. RAISE error_message. ENDTRY. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
DATA: lv_input TYPE string. " Get the Decimal Format from the user settings (SU01/SU3) " Dezimaldarstellung aus den Benutzereinstellungen holen (SU01/SU3) IF gv_decimal_sign IS INITIAL. CALL FUNCTION 'CLSE_SELECT_USR01' EXPORTING username = sy-uname iv_delete_buffer = abap_true IMPORTING decimal_sign = gv_decimal_sign separator = gv_separator. ENDIF. lv_input = input. REPLACE ALL OCCURRENCES OF gv_separator IN lv_input WITH ''. REPLACE gv_decimal_sign IN lv_input WITH '.'. CONDENSE lv_input NO-GAPS. TRY. output = lv_input. CATCH cx_sy_conversion_no_number. " Clear message fields of sy structure, so that 'Conversion error' will be raised " Alle Message Felder der sy Struktur löschen, damit 'Fehler bei der Konvertierung.' geworfen wird CLEAR: sy-msgty, sy-msgno, sy-msgv1, sy-msgv2, sy-msgv3, sy-msgv4. RAISE error_message. ENDTRY. |
Now we need to activate the functiongroup and the functionmodules.
Create a new domain or copy an existing domain and assign the new conversion routine to it and activate it.
In the demo report you can find how you can implement the conversion routine at runtine to ALV.
Here is a small demo report on how you can use it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
REPORT zstkoes_alv_sort. TYPES: BEGIN OF gty_mard, matnr TYPE matnr, werks TYPE werks_d, lgort TYPE lgort_d, labst TYPE labst, zlabst TYPE zlabst, h_level TYPE h_level, END OF gty_mard, gtty_mard TYPE gty_mard. DATA: gt_mard TYPE TABLE OF gtty_mard. gt_mard = VALUE #( ( matnr = CONV numc4( 1 ) werks = '0001' lgort = '0001' labst = '-9.999' zlabst = '-9.999' h_level = -99999999 ) ( matnr = CONV numc4( 1 ) werks = '0001' lgort = '0002' labst = '-99.999' zlabst = '-99.999' h_level = -9999999 ) ( matnr = CONV numc4( 1 ) werks = '0002' lgort = '0001' labst = '-999.999' zlabst = '-999.999' h_level = -999999 ) ( matnr = CONV numc4( 1 ) werks = '0002' lgort = '0002' labst = '-9999.999' zlabst = '-9999.999' h_level = -99999 ) ( matnr = CONV numc4( 2 ) werks = '0001' lgort = '0001' labst = '-99999.999' zlabst = '-99999.999' h_level = -9999 ) ( matnr = CONV numc4( 2 ) werks = '0001' lgort = '0002' labst = '-999999.999' zlabst = '-999999.999' h_level = -999 ) ( matnr = CONV numc4( 2 ) werks = '0002' lgort = '0001' labst = '-9999999.999' zlabst = '-9999999.999' h_level = -99 ) ( matnr = CONV numc4( 2 ) werks = '0002' lgort = '0002' labst = '-99999999.999' zlabst = '-99999999.999' h_level = -9 ) ). cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_table) " Basisklasse einfache ALV Tabellen CHANGING t_table = gt_mard ). lo_table->get_functions( )->set_all( ). DATA(it_column_ref) = lo_table->get_columns( )->get( ). LOOP AT it_column_ref ASSIGNING FIELD-SYMBOL(<st_column_ref>). CASE <st_column_ref>-columnname. WHEN 'H_LEVEL'. <st_column_ref>-r_column->set_edit_mask( value = '==ZSIGN' ). ENDCASE. ENDLOOP. lo_table->display( ). |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
REPORT zstkoes_alv_sort. TYPES: BEGIN OF gty_mard, matnr TYPE matnr, werks TYPE werks_d, lgort TYPE lgort_d, labst TYPE labst, zlabst TYPE zlabst, h_level TYPE h_level, END OF gty_mard, gtty_mard TYPE gty_mard. DATA: gt_mard TYPE TABLE OF gtty_mard, gt_column_ref TYPE SALV_T_COLUMN_REF, go_salv TYPE REF TO cl_salv_table. FIELD-SYMBOLS: <gs_mard> TYPE gty_mard, <gs_column_ref> TYPE salv_s_column_ref. APPEND INITIAL LINE TO gt_mard ASSIGNING <gs_mard>. <gs_mard>-matnr = '0001'. <gs_mard>-werks = '0001'. <gs_mard>-lgort = '0001'. <gs_mard>-labst = '-9.999'. <gs_mard>-zlabst = '-9.999'. <gs_mard>-h_level = -99999999. APPEND INITIAL LINE TO gt_mard ASSIGNING <gs_mard>. <gs_mard>-matnr = '0001'. <gs_mard>-werks = '0001'. <gs_mard>-lgort = '0002'. <gs_mard>-labst = '-99.999'. <gs_mard>-zlabst = '-99.999'. <gs_mard>-h_level = -9999999. APPEND INITIAL LINE TO gt_mard ASSIGNING <gs_mard>. <gs_mard>-matnr = '0001'. <gs_mard>-werks = '0002'. <gs_mard>-lgort = '0001'. <gs_mard>-labst = '-999.999'. <gs_mard>-zlabst = '-999.999'. <gs_mard>-h_level = -999999. APPEND INITIAL LINE TO gt_mard ASSIGNING <gs_mard>. <gs_mard>-matnr = '0001'. <gs_mard>-werks = '0002'. <gs_mard>-lgort = '0002'. <gs_mard>-labst = '-9999.999'. <gs_mard>-zlabst = '-9999.999'. <gs_mard>-h_level = -99999. APPEND INITIAL LINE TO gt_mard ASSIGNING <gs_mard>. <gs_mard>-matnr = '0002'. <gs_mard>-werks = '0001'. <gs_mard>-lgort = '0001'. <gs_mard>-labst = '-99999.999'. <gs_mard>-zlabst = '-99999.999'. <gs_mard>-h_level = -9999. APPEND INITIAL LINE TO gt_mard ASSIGNING <gs_mard>. <gs_mard>-matnr = '0002'. <gs_mard>-werks = '0001'. <gs_mard>-lgort = '0002'. <gs_mard>-labst = '-999999.999'. <gs_mard>-zlabst = '-999999.999'. <gs_mard>-h_level = -999. APPEND INITIAL LINE TO gt_mard ASSIGNING <gs_mard>. <gs_mard>-matnr = '0002'. <gs_mard>-werks = '0002'. <gs_mard>-lgort = '0001'. <gs_mard>-labst = '-9999999.999'. <gs_mard>-zlabst = '-9999999.999'. <gs_mard>-h_level = -99. APPEND INITIAL LINE TO gt_mard ASSIGNING <gs_mard>. <gs_mard>-matnr = '0002'. <gs_mard>-werks = '0002'. <gs_mard>-lgort = '0002'. <gs_mard>-labst = '-99999999.999'. <gs_mard>-zlabst = '-99999999.999'. <gs_mard>-h_level = -9. cl_salv_table=>factory( IMPORTING r_salv_table = go_salv CHANGING t_table = gt_mard ). go_salv->get_functions( )->set_all( ). gt_column_ref = go_salv->get_columns( )->get( ). LOOP AT gt_column_ref ASSIGNING <gs_column_ref>. CASE <gs_column_ref>-columnname. WHEN 'H_LEVEL'. <gs_column_ref>-r_column->set_edit_mask( value = '==ZSIGN' ). ENDCASE. ENDLOOP. go_salv->display( ). |
Here you can see the result of the development.