Prevent CL_SALV_TABLE from removing leading zeros? - abap

Please take a look on this following piece of code in which I put zeros in Test2 as value and two zeros in Test3 as value.
i used set_leading_zero but still leading zeros are removed.
CLASS lcl_main DEFINITION FINAL CREATE PRIVATE.
PUBLIC SECTION.
CLASS-METHODS:
main.
ENDCLASS.
CLASS lcl_main IMPLEMENTATION.
METHOD main.
TYPES: BEGIN OF l_tys_test,
name TYPE string,
value TYPE i,
END OF l_tys_test,
l_tyt_test TYPE STANDARD TABLE OF l_tys_test WITH EMPTY KEY.
DATA(lt_test) = VALUE l_tyt_test(
( name = `Test1` value = 1 )
( name = `Test2` value = 02 )
( name = `Test3` value = 003 )
).
cl_salv_table=>factory(
IMPORTING
r_salv_table = DATA(lo_salv_table)
CHANGING
t_table = lt_test
).
lo_salv_table->get_columns( )->get_column( 'VALUE' )->set_leading_zero( abap_true ).
lo_salv_table->display( ).
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
lcl_main=>main( ).

Agree with Jozsef and Sandra, there is no way you can do it with integer data type.
The only way how to implement this by means of ALV grid is to redeclare you VALUE field as numchar
TYPES: BEGIN OF
...
value TYPE n LENGTH 10,
...
END OF.
and utilize EDIT_MASK function, which is available both for WRITE statement and for ALV grid as well.
Put this instead of set_leading_zero( ) call in your method
lo_salv_table->get_columns( )->get_column( 'VALUE' )->set_edit_mask( '___________' ).
LENGTH specification in type definition is the number of leading zeroes to put.

You can use set_zero method to false.
DATA(lo_column) = lo_alv->get_columns( )->get_column( 'PERNR' ). lo_column->set_long_text( 'Staff No' ). lo_column->set_zero( abap_false ).

Related

Creating a range for a field from internal table using RTTS

I want to create a function/custom class method that takes in 2 parameters:
1) IM_ITAB type ANY TABLE
2) IM_COMPONENT type STRING
and returns 1 parameter:
1) EX_RANGE type PIQ_SELOPT_T
So, algorithm is like this:
First of all, we check if the column with a component name at all exists
Then, we check that internal table is not empty.
Then, we loop through internal table assigning component and filling range table. Code is below.
METHODS compose_range_from_itab
IMPORTING
IM_ITAB type ANY TABLE
IM_COMPONENT type STRING
EXPORTING
EX_RANGE type PIQ_SELOPT_T.
...
METHOD compose_range_from_itab.
DATA: lo_obj TYPE REF TO cl_abap_tabledescr,
wa_range TYPE selopt,
lt_range TYPE piq_selopt_t.
FIELD-SYMBOLS: <fs_line> TYPE ANY,
<fs_component> TYPE ANY.
lo_obj ?= cl_abap_typedescr=>describe_by_data( p_data = im_itab ).
READ TABLE lo_obj->key TRANSPORTING NO FIELDS WITH KEY name = im_component.
IF sy-subrc IS INITIAL.
IF LINES( im_itab ) GT 0.
LOOP AT im_itab ASSIGNING <fs_line>.
ASSIGN COMPONENT im_component OF STRUCTURE <fs_line> TO <fs_component>.
wa_range-sign = 'I'.
wa_range-option = 'EQ'.
wa_range-low = <fs_component>.
APPEND wa_range TO lt_range.
ENDLOOP.
SORT lt_range BY low.
DELETE ADJACENT DUPLICATES FROM lt_range COMPARING low.
ex_range[] = lt_range[].
ENDIF.
ENDIF.
ENDMETHOD.
But I want to improve the method further. If the imported internal table has, let's say, 255 columns, then it will take longer to loop through such table. But I need only one column to compose the range.
So I want to get components of internal table, then choose only one component, create a new line type containing only that component, then create internal table with that line type and copy.
Here is the pseudo code corresponding to what I want to achieve:
append corresponding fields of im_itab into new_line_type_internal_table.
How can I "cut out" one component and create a new line type using RTTS?
You are overcomplicating everything, you don't need RTTS for that.
DEFINE make_range.
ex_range = VALUE #( BASE ex_range ( sign = 'I' option = 'EQ' low = &1 ) ).
END-OF-DEFINITION.
LOOP AT im_itab ASSIGNING FIELD-SYMBOL(<fs_line>).
ASSIGN COMPONENT im_component OF STRUCTURE <fs_line> TO FIELD-SYMBOL(<fs_field>).
CHECK sy-subrc = 0 AND <fs_field> IS NOT INITIAL.
make_range <fs_field>.
ENDLOOP.
And yes, as Sandra said, you won't gain any performance with RTTS, just the opposite.
Surprisingly, this variant turned out to be faster:
CLASS-METHODS make_range_variant_2
IMPORTING
sample TYPE table_type
column TYPE string
RETURNING
VALUE(result) TYPE range_type.
METHOD make_range_variant_2.
TYPES:
BEGIN OF narrow_structure_type,
content TYPE char32,
END OF narrow_structure_type.
TYPES narrow_table_type TYPE STANDARD TABLE OF narrow_structure_type WITH EMPTY KEY.
DATA narrow_table TYPE narrow_table_type.
DATA(mapping) =
VALUE cl_abap_corresponding=>mapping_table_value(
( kind = cl_abap_corresponding=>mapping_component srcname = column dstname = 'CONTENT' ) ).
DATA(mover) =
cl_abap_corresponding=>create_with_value(
source = sample
destination = narrow_table
mapping = mapping ).
mover->execute(
EXPORTING
source = sample
CHANGING
destination = narrow_table ).
LOOP AT narrow_table ASSIGNING FIELD-SYMBOL(<row>).
INSERT VALUE #(
sign = 'I'
option = 'EQ'
low = <row>-content )
INTO TABLE result.
ENDLOOP.
ENDMETHOD.
CL_ABAP_CORRESPONDING delegates to a kernel function for the structure-to-structure move, which apparently is faster than the ABAP-native ASSIGN COMPONENT [...] OF STRUCTURE [...] TO FIELD-SYMBOL [...]. The actual loop then seems to be faster because it uses fixed-name assignments.
Maybe somebody could verify.
I would not go for a Macro.
Data:
lr_data type ref to data.
FIELD-SYMBOLS:
<lv_component> TYPE any,
<ls_data> TYPE any.
CREATE DATA lr_data LIKE LINE OF im_itab.
ASSIGN lr_data->* TO <ls_data>.
"Check whether im_component exists
ASSIGN COMPONENT im_component OF STRUCTURE <ls_data> TO <lv_component>.
CHECK sy-subrc EQ 0.
LOOP AT im_itab INTO <ls_data>.
APPEND VALUE #( sign = 'I' option = 'EQ' low = <lv_component> ) TO ex_range.
ENDLOOP.

Conversion exception while working with constructor expressions

I'm working on a routine which moves the lines of a string table (in this case fui_elements) into a structure of unknown type (fcwa_struct).
DATA(li_temp) = ... "fill assignment table here
LOOP AT fui_elements ASSIGNING FIELD-SYMBOL(<lfs_element>).
ASSIGN COMPONENT li_temp[ sy-tabix ] OF STRUCTURE fcwa_struct
TO FIELD-SYMBOL(<lfs_field>).
IF sy-subrc <> 0.
"somethings wrong with the fui_elements data
ENDIF.
<lfs_field> = <lfs_element>.
ENDLOOP.
If the table i_field_customizing (STANDARD TABLE OF string) is not initial, I want to use its values.
Otherwise I want to generate an integer table (so that the loop runs equally often regardless of the table's values). Here lw_max is the amount of fields the imported structure has:
DATA(li_temp) = COND #( WHEN i_field_customizing[] IS INITIAL
THEN VALUE t_integer_tt( FOR X = 1
THEN X + 1
WHILE X <= lw_max
( X ) )
ELSE i_field_customizing ).
But when I run the report with i_field_customizing like that:
DATA(i_field_customizing) = VALUE t_string_tt( ( `KUNNR` ) ( `NAME1` ) ).
I get this exception on the line where I try to construct li_temp:
CX_SY_CONVERSION_NO_NUMBER (KUNNR cannot be interpreted as a number)
My current guess is that COND gets its type statically. Does anybody know how I can get around this?
What you are trying to achieve will not be possible because the type of an inline definition of a variable using COND is decided at compilation time and not at runtime.
Please see my question here. The type that will be taken is always the type of the variable that stands directly after THEN. You can decide what type will be chosen at compilation time by fiddling with negating the condition and switching places of variables after THEN at ELSE but it will be always either or and from what I understand you want to be able to do it dynamically so your ASSIGN COMPONENT statement works as expected with integers.
Even by specifically casting the variable after ELSE one gets the same short dump as you do.
DATA(li_temp) = COND #( WHEN i_field_customizing IS INITIAL
THEN VALUE t_integer_tt( ( 1 ) ( 2 ) )
ELSE CAST t_string_tt( REF #( i_field_customizing ) )->* ).
Alternatively you could cast to REF TO DATA but then you have to dereference it to a field symbol of type STANDARD TABLE.
REPORT zzy.
CLASS lcl_main DEFINITION FINAL CREATE PRIVATE.
PUBLIC SECTION.
CLASS-METHODS:
main.
ENDCLASS.
CLASS lcl_main IMPLEMENTATION.
METHOD main.
TYPES:
t_integer_tt TYPE STANDARD TABLE OF i WITH EMPTY KEY,
t_string_tt TYPE STANDARD TABLE OF string WITH EMPTY KEY.
FIELD-SYMBOLS:
<fs_table> TYPE STANDARD TABLE.
DATA: BEGIN OF l_str,
kunnr TYPE kunnr,
name1 TYPE name1,
END OF l_str.
* DATA(i_field_customizing) = VALUE t_string_tt( ( `KUNNR` ) ( `NAME1` ) ).
DATA(i_field_customizing) = VALUE t_string_tt( ).
DATA(li_temp) = COND #( WHEN i_field_customizing IS INITIAL
THEN CAST data( NEW t_integer_tt( ( 1 ) ( 2 ) ) )
ELSE CAST data( REF #( i_field_customizing ) ) ).
ASSIGN li_temp->* TO <fs_table>.
LOOP AT <fs_table> ASSIGNING FIELD-SYMBOL(<fs_temp>).
ASSIGN COMPONENT <fs_temp> OF STRUCTURE l_str TO FIELD-SYMBOL(<fs_field>).
ENDLOOP.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
lcl_main=>main( ).

Strange behaviour using string templates and new COND syntax

I have spotted a strange behaviour of new COND syntax when used inside a string template. It is about string length defaulting. It looks like the length of the string will be defaulted always to what stands after THEN even if the condition is not met.
Check out the following piece of code!
REPORT zzz.
CLASS lcl_main DEFINITION FINAL CREATE PRIVATE.
PUBLIC SECTION.
CLASS-METHODS:
main.
ENDCLASS.
CLASS lcl_main IMPLEMENTATION.
METHOD main.
DATA(l_bool) = abap_true.
DATA(l_v_line) = |{ COND #( WHEN l_bool IS INITIAL THEN 'AAA' ELSE 'BBBB' ) }|.
DATA(l_v_line2) = |{ COND #( WHEN l_bool IS INITIAL THEN 'AAA' ELSE 'BBBB' ) WIDTH = 4 }|.
DATA(l_v_line3) = |{ COND #( WHEN l_bool IS INITIAL THEN 'AAA ' ELSE 'BBBB' ) }|.
DATA(l_v_line4) = |{ COND #( WHEN l_bool IS NOT INITIAL THEN 'BBBB' ELSE 'AAA' ) }|.
WRITE /: l_v_line, l_v_line2, l_v_line3, l_v_line4.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
lcl_main=>main( ).
The output
BBB
BBB·
BBBB
BBBB
The first two variables l_v_line and l_v_line2 are truncated even if the condition evaluates to false. If I put space after AAA in l_v_line3 then it is OK. Putting BBBB after THEN for l_v_line4 solves the problem.
My question is: is this behaviour documented anywhere in SAP documentation? I could not find any clues that would have led me to it.
from ABAP documentation
The # character as a symbol for the operand type.
...
If the operand type is not fully identifiable, an operand with a
statically identifiable type must be specified after the first
THEN. This type is then used.

How to use REUSE_ALV_FIELDCATALOG_MERGE function module?

I'm trying to use the function module REUSE_ALV_FIELDCATALOG_MERGE to pass the field label in ddic to display in the column header of the alv report.
But, that didn't work.
If I comment the I_STRUCTURE_NAME = 'TY_YNAH_CUS_OBJ_REQ' line, it give me runtime error state
The ABAP program lines are wider than the internal table.
But if I uncomment it ,the program still did not work
REPORT YALV_TEST.
tables sscrfields.
type-pools : slis.
"CREATE STRUCTURE -1
TYPES: BEGIN OF TY_YNAH_CUS_OBJ_REQ,
REQID TYPE YNAH_REQ_ID,
REQUESTOR TYPE YNAH_REQUESTOR,
BUSINESS_OWNER TYPE YNAH_BUS_OWN,
FUNCTIONAL_OWNER TYPE YNAH_FUNC_OWN,
REQNUM TYPE YNAH_SERVICE_REQ_NUM,
PROJECT_ID TYPE YNAH_PRO_ID,
SYSTEM_ID TYPE YNAH_SYS_ID,
FUNCTIONAL_AREA TYPE YNAH_FUNC_AREA,
REQUEST_DATE TYPE YNAH_REQ_DATE,
REQUEST_TIME TYPE YNAH_REQ_TIME,
END OF TY_YNAH_CUS_OBJ_REQ.
"defining internal table -2
DATA: IT_YNAH_CUS_OBJ_REQ type TABLE OF TY_YNAH_CUS_OBJ_REQ
* WA_YNAH_CUS_OBJ_REQ type TY_YNAH_CUS_OBJ_REQ.
DATA: it_fcat TYPE slis_t_fieldcat_alv ,
wa_fcat TYPE slis_fieldcat_alv,
gd_layout TYPE slis_layout_alv.
SELECTION-SCREEN BEGIN OF BLOCK menu WITH FRAME TITLE text-001.
SELECT-OPTIONS: s_proid FOR IT_YNAH_CUS_OBJ_REQ-PROJECT_ID.
PARAMETER p_sysid type TY_YNAH_CUS_OBJ_REQ-SYSTEM_ID.
SELECTION-SCREEN: BEGIN OF LINE,
pushbutton 33(8) BUT user-command search.
SELECTION-SCREEN END OF LINE.
SELECTION-SCREEN END OF BLOCK menu.
initialization.
BUT = 'SEARCH'. END-OF-SELECTION.
"execute search function when user click search button
at selection-screen. "after processing user input
case SSCRFIELDS.
when 'SEARCH'.
SSCRFIELDS-UCOMM = 'ONLI'.
endcase.
"fetch data using select-4 START-OF-SELECTION.
SELECT *
FROM YNAH_CUS_OBJ_REQ "Database
INTO CORRESPONDING FIELDS OF TABLE IT_YNAH_CUS_OBJ_REQ "Into internal table
WHERE
PROJECT_ID in s_proid and
SYSTEM_ID eq p_sysid.
CALL FUNCTION 'REUSE_ALV_FIELDCATALOG_MERGE'
EXPORTING
I_PROGRAM_NAME = sy-repid
I_INTERNAL_TABNAME ='TY_YNAH_CUS_OBJ_REQ'
* I_STRUCTURE_NAME = 'TY_YNAH_CUS_OBJ_REQ'
* I_CLIENT_NEVER_DISPLAY = 'X'
I_INCLNAME = sy-repid
* I_BYPASSING_BUFFER = 'X'
* I_BUFFER_ACTIVE = CHANGING CT_FIELDCAT = it_fcat.
* EXCEPTIONS
* INCONSISTENT_INTERFACE = 1
* PROGRAM_ERROR = 2
* OTHERS = 3
* .
IF SY-SUBRC <> 0.
** Implement suitable error handling here
ENDIF.
The REUSE_*ALV* function modules are unsupported. I'd suggest switching to the CL_SALV_* classes. The documentation is better, there are more sample programs (DEMO_SALV_*) and you get support.
You need a dictionary structure if you want to get dictionary-based field descriptions (duh). If you assemble a structure type on the ABAP level using TYPE ... BEGIN OF ... END OF ..., as far as I know, the dictionary types for the individual fields are converted to ABAP types first and only then assembled into a structure type. Anyway, the dictionary reference of the original fields is lost. Instead of defining the structure of the output table in your code, use a dictionary structure.
You have some mistakes you might have not known (SAP is very confusing sometimes
and not transparent with error-messages). I got for you a working example of mine, have a look on it, especially on the comments.
First, data definition:
TYPE-POOLS slis. "import you need for REUSE_ALV_FIELDCATALOG_MERGE
DATA:
lt_fieldcat TYPE slis_t_fieldcat_alv,
BEGIN OF G_IT_MATERIAL occurs 0,
MATNR LIKE MARA-MATNR,
MTART LIKE MARA-MTART,
MAKTX_DE LIKE MAKT-MAKTX,
MAKTX_FR LIKE MAKT-MAKTX,
MAKTX_IT LIKE MAKT-MAKTX,
ERNAM LIKE MARA-ERNAM,
ERSDA LIKE MARA-ERSDA,
LAEDA LIKE MARA-LAEDA,
END OF G_IT_MATERIAL.
It is absolutely necessary that you define your local structure directly with LIKE, otherwise the parser from REUSE_ALV_FIELDCATALOG_MERGE will not find it.
Select your stuff:
SELECT ma~matnr ma~mtart ma~ernam ma~ersda ma~laeda
de~maktx as maktx_de fr~maktx as maktx_fr it~maktx as maktx_it
FROM mara as ma
LEFT JOIN MAKT as de ON de~matnr = ma~matnr AND de~spras = 'DE'
LEFT JOIN MAKT as fr ON fr~matnr = ma~matnr AND fr~spras = 'FR'
LEFT JOIN MAKT as it ON it~matnr = ma~matnr AND it~spras = 'IT'
INTO CORRESPONDING FIELDS OF TABLE g_it_material
WHERE ...
Create a field catalog dynamically:
CALL FUNCTION 'REUSE_ALV_FIELDCATALOG_MERGE'
EXPORTING
I_PROGRAM_NAME = sy-repid
I_INTERNAL_TABNAME = 'G_IT_MATERIAL'
I_INCLNAME = sy-repid
CHANGING
ct_fieldcat = lt_fieldcat
EXCEPTIONS
inconsistent_interface = 1
program_error = 2
OTHERS = 3.
IF sy-subrc <> 0.
* MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
* WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
ENDIF.
Now display the ALV grid:
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY'
EXPORTING
it_fieldcat = lt_fieldcat "you could also give a structure
"i_structure_name = 'ZMM_SMATERIAL' "here instead of the fieldcat
TABLES
t_outtab = g_it_material
EXCEPTIONS
program_error = 1
OTHERS = 2.
Note that the parser also needs a max linesize of 72 chars.
There are several different text components provided by structure slis_fieldcat_alv that are used as column labels. The chosen text depends on the current column width (which itself usually depends on the length of the data displayed). Make sure that you change them all accordingly!
The usual technique is: By passing the I_STRUCTURE_NAME, you get a field catalog corresponding to this DDIC structure (the changing parameter ct_fieldcat). You then modify this internal table according to your needs and pass that modified table to the REUSE_ALV_GRID_DISPLAY.
In cases where I don't distinguish the different-sized text versions, I use the following macros to set all the text fields to the same value. The macros require a local work area ls_fieldcat (with the linetype of ct_fieldcat) and a local string variablelv_text` in order to work.
define set_field.
* Feld &1 für Anzeigefeld &2 den Wert &3 zuweisen
ls_fieldcat-&1 = &3.
modify ct_fieldcat from ls_fieldcat
transporting &1
where fieldname cp '&2'.
end-of-definition.
define set_text_direct.
lv_text = &2.
set_field seltext_s &1 lv_text.
lv_text = &2.
set_field seltext_m &1 lv_text.
lv_text = &2.
set_field seltext_l &1 lv_text.
end-of-definition.

ABAP Display field symbol dynamic in alv

I am pasting this program for example but i will never know the type of the table (here vbap and vbak).
My goals is to display my field symbol without knowing the types.
Is it possible ?
Here is my code :
REPORT ZTEST_FME_FOL.
type-pools slis .
FIELD-SYMBOLS : <mytable> TYPE ANY TABLE.
DATA : lv_alv_table TYPE REF TO cl_salv_table,
lv_funct TYPE REF TO cl_salv_functions,
lv_columns TYPE REF TO cl_salv_columns_table,
lv_column TYPE REF TO CL_SALV_COLUMN_table.
SELECT * from vbap INNER JOIN VBAK ON vbap~vbeln = vbak~vbeln UP TO 10 ROWS INTO TABLE <mytable>.
TRY.
cl_salv_table=>factory(
IMPORTING
r_salv_table = lv_alv_table
CHANGING
t_table = <mytable> ).
CATCH cx_salv_msg .
ENDTRY.
lv_funct = lv_alv_table->get_functions( ).
lv_funct->set_all( Abap_True ).
lv_columns = lv_alv_table->get_columns( ).
lv_alv_table->display( ).
Thanks in advance !
Depending on what you 're trying to do there's going to be more validation required than what I've done, but in essence this is what you need.
Using (dynamic) joins may be particularly tricky.
report zevw_test_dynamic_alv.
parameters: p_table type string obligatory.
field-symbols: <gt_table> type standard table.
data: gt_data type ref to data.
start-of-selection.
create data gt_data type table of (p_table).
assign gt_data->* to <gt_table>.
select * from (p_table) up to 10 rows
into table <gt_table>.
perform display_results using <gt_table>. "Your ALV stuff will be in here
You may even have to build the fieldcat manually and then use
call method cl_alv_table_create=>create_dynamic_table
exporting
it_fieldcatalog = gt_fieldcat[]
importing
ep_table = gt_data.
to get the data reference