Show negative sign in front of the value in ALV

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:

  1. Can it be solved using standard functions?
    • No, neather fieldcatalog nor ALV layout can handle this request.
  2. Must be developed here?
    • Yes, we need a conversion routine. This means we have to create a functiongroup and two functionmodules

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.

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

Creating a 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:

  • CONVERSION_EXIT_xxxxx_INPUT
  • CONVERSION_EXIT_xxxxx_OUTPUT

Creating functiongroup

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.

Creating functionmodule internal -> display

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.

Creating functionmodule display -> internal

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.

Activating new functiongroup and functionmodules

Now we need to activate the functiongroup and the functionmodules.

Implementing the conversion routine

Implementing conversion routine to domain

Create a new domain or copy an existing domain and assign the new conversion routine to it and activate it.

Implementing at runtime to ALV

In the demo report you can find how you can implement the conversion routine at runtine to ALV.

Demo report

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( ).

Result

Here you can see the result of the development.
  • 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