Dynamic language output for structure/internal table - dynamic

I have a selection screen with select-options where I want to enter several information about materials, for example: material number etc.
The user is also able to enter a language which the output should be in.
If the user chooses english the program shall display an internal table with material number, language, material name in english. If the user enters spanish, I want the output to be in spanish.
What do I have to do in order to define a dynamic structure / table which shows the respective columns dependent on the chosen language?
Thanks for your help

It's highly-dependent on the data structure you are going to show to user, but usually you don't need dynamic structure for this, but rather need to populate data dynamically, i.e. depending on current user language.
For example, material texts are stored in MAKT text table, where texts are stored along with language keys by which they are usually retrieved:
SELECT
a~matnr
a~werks
b~maktx FROM ekpo AS a
INNER JOIN makt AS b
ON b~matnr = a~matnr
AND b~spras = sy-langu
INTO CORRESPONDING FIELDS OF TABLE int_out
WHERE
a~matnr IN s_matnr and
a~werks IN s_werks.
Other descriptions in SAP are usually stored in text tables as well.
More about sy-langu and other system fields is here.
UPDATE: If you really want a dynamic structure with all the languages, see this sample:
DATA: lang TYPE SPRAS.
* language selection
SELECT-OPTIONS: s_lang FOR lang.
SELECT a~matnr, a~werks, b~maktx, b~spras UP TO 5000 ROWS
FROM ekpo AS a
JOIN makt AS b
ON b~matnr = a~matnr
INTO TABLE #DATA(int_out)
WHERE a~werks LIKE '3%'
AND a~matnr LIKE '1%'
AND b~spras IN #s_lang.
*finding unique languages
DATA lt_langs TYPE TABLE OF spras.
lt_langs = VALUE #( ( '' ) ).
LOOP AT int_out ASSIGNING FIELD-SYMBOL(<fs_out>)
GROUP BY ( lang = to_upper( val = <fs_out>-spras ) ) ASCENDING
WITHOUT MEMBERS
ASSIGNING FIELD-SYMBOL(<ls_lang>).
APPEND <ls_lang>-lang TO lt_langs.
ENDLOOP.
DATA :
ls_component TYPE cl_abap_structdescr=>component,
gt_components TYPE cl_abap_structdescr=>component_table.
*adding MATNR column
ls_component-name = 'MATNR'.
ls_component-type ?= cl_abap_datadescr=>describe_by_name( 'matnr' ).
APPEND ls_component TO gt_components.
*Creating dynamic structure with column for every lang
LOOP AT lt_langs ASSIGNING FIELD-SYMBOL(<fs_lang>).
CONDENSE <fs_lang>.
IF <fs_lang> IS NOT INITIAL.
ls_component-name = 'makt_' && <fs_lang>.
ls_component-type ?= cl_abap_datadescr=>describe_by_name( 'maktx' ).
APPEND ls_component TO gt_components.
ENDIF.
ENDLOOP.
* constructing dynamic structure
DATA: gr_struct_typ TYPE REF TO cl_abap_datadescr.
gr_struct_typ ?= cl_abap_structdescr=>create( p_components = gt_components ).
* constructing table from structure
DATA: gr_dyntable_typ TYPE REF TO cl_abap_tabledescr.
gr_dyntable_typ = cl_abap_tabledescr=>create( p_line_type = gr_struct_typ ).
DATA: gt_dyn_table TYPE REF TO data,
gw_dyn_line TYPE REF TO data.
FIELD-SYMBOLS: <gfs_line>,<gfs_line1>,<fs1>,
<gfs_dyn_table> TYPE STANDARD TABLE.
CREATE DATA: gt_dyn_table TYPE HANDLE gr_dyntable_typ,
gt_dyn_table TYPE HANDLE gr_dyntable_typ,
gw_dyn_line TYPE HANDLE gr_struct_typ.
ASSIGN gt_dyn_table->* TO <gfs_dyn_table>.
ASSIGN gw_dyn_line->* TO <gfs_line>.
LOOP AT int_out ASSIGNING <fs_out>.
* checking for duplicated
READ TABLE <gfs_dyn_table> ASSIGNING <gfs_line1> WITH KEY ('MATNR') = <fs_out>-matnr.
IF sy-subrc = 0.
CONTINUE.
ENDIF.
* assigning material number
LOOP AT gt_components ASSIGNING FIELD-SYMBOL(<fs_component>).
IF <fs_component>-name = 'MATNR'.
ASSIGN COMPONENT <fs_component>-name OF STRUCTURE <gfs_line> TO <fs1>.
IF <fs1> IS ASSIGNED.
<fs1> = <fs_out>-matnr.
UNASSIGN <fs1>.
ENDIF.
ENDIF.
* assigning languge-dependent names
READ TABLE int_out WITH KEY matnr = <fs_out>-matnr
spras = <fs_component>-name+5
ASSIGNING FIELD-SYMBOL(<fs_spras>).
IF sy-subrc = 0.
ASSIGN COMPONENT <fs_component>-name OF STRUCTURE <gfs_line> TO <fs1>.
IF <fs1> IS ASSIGNED.
<fs1> = <fs_spras>-maktx.
UNASSIGN <fs1>.
ENDIF.
ENDIF.
ENDLOOP.
APPEND <gfs_line> TO <gfs_dyn_table>.
CLEAR: <gfs_line>.
ENDLOOP.
DATA: l_lang TYPE spras VALUE 'E'.
* showing values in proper language depending on user input
LOOP AT <gfs_dyn_table> ASSIGNING <gfs_line>.
ASSIGN COMPONENT 'makt_' && l_lang OF STRUCTURE <gfs_line> TO <fs1>.
IF <fs1> IS ASSIGNED.
WRITE / <fs1>.
UNASSIGN <fs1>.
ENDIF.
ENDLOOP.

Related

Simplify select using hierarchy function

I wonder if it could be possible to simplify the following ABAP-based TDEVC selection using the new hierarchy functions of ABAP SQL.
Table TDEVC is SAP's table of packages (= development classes), with the columns (DEVCLASS,PARENTCL) definig the hierarchy.
The goal is to list each package in the custom namespace, whose name starts with the letter Z, together with its parent package and its root package (the uniquely defined ancestor with PARENTCL = ''.
This would be the selection without hierarchy, performed with plain old ABAP. I didn't succeed to define TDEVC as a hierarchy source in the SELECT statement itself, as described in the ABAP documentation.
types:
begin of ty_node,
devclass type devclass,
parentcl type devclass,
root type devclass,
end of ty_node,
ty_nodes type sorted table of ty_node
with non-unique key parentcl.
data:
lt_nodes type ty_nodes,
lt_root type ty_nodes,
lt_parents type ty_nodes.
field-symbols:
<ls_node> type ty_node,
<ls_parent> type ty_node.
select devclass, parentcl from tdevc
into table #lt_nodes
where devclass like 'Z%'.
loop at lt_nodes assigning <ls_node>
where parentcl eq ''.
insert value #(
devclass = <ls_node>-devclass
root = <ls_node>-devclass )
into table lt_root.
delete lt_nodes.
endloop.
lt_parents = lt_root.
while lt_parents is not initial.
data(lt_parents_new) = value ty_nodes( ).
loop at lt_parents assigning <ls_parent>.
loop at lt_nodes assigning <ls_node>
where parentcl = <ls_parent>-devclass.
<ls_node>-root = <ls_parent>-root.
insert <ls_node> into table lt_parents_new.
endloop.
endloop.
lt_parents = lt_parents_new.
endwhile.
* Result is now in lt_nodes

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.

How to update Z fields with BAPI_OUTB_DELIVERY_CHANGE parameter EXTENSION2?

How to update Z fields with BAPI_OUTB_DELIVERY_CHANGE using EXTENSION2?
I tried to update some Z fields in LIKP table using the BAPI to update deliveries but after call it any changes wasn't made.
Any suggestions?
I found the BAdI - SMOD_V50B0001. I made an enhancement implementaction and in the method EXIT_SAPLV50I_010 (User Exit BAPI for Changes to Outbound Delivery) set the follow code.
METHOD if_ex_smod_v50b0001~exit_saplv50i_010.
CONSTANTS: lc_vbkok TYPE char5 VALUE 'VBKOK', "Enhance the structure for more z fields in LIKP
lc_vbpok TYPE char5 VALUE 'VBPOK', "Enhance the structure for more z fields in LIPS
lc_cs_vbkok TYPE char8 VALUE 'CS_VBKOK'.
DATA: lv_item TYPE posnr_vl.
FIELD-SYMBOLS: <lfs_fieldname> TYPE any.
LOOP AT extension2 INTO DATA(lw_extension2).
CASE lw_extension2-param.
"As a structure in row 0
WHEN lc_vbkok. "Fields for LIKP
ASSIGN (lc_cs_vbkok) TO FIELD-SYMBOL(<lfs_vbkok>).
IF <lfs_vbkok> IS ASSIGNED.
ASSIGN COMPONENT lw_extension2-field OF STRUCTURE <lfs_vbkok> TO <lfs_fieldname>.
IF <lfs_fieldname> IS ASSIGNED.
<lfs_fieldname> = lw_extension2-value.
ENDIF.
ENDIF.
"As a table from row 1 to n
WHEN lc_vbpok. "Fields for LIPS
MOVE lw_extension2-row TO lv_item.
READ TABLE ct_vbpok ASSIGNING FIELD-SYMBOL(<lfs_vbpok>) WITH KEY posnr_vl = lv_item.
IF sy-subrc = 0.
ASSIGN COMPONENT lw_extension2-field OF STRUCTURE <lfs_vbpok> TO <lfs_fieldname>.
IF <lfs_fieldname> IS ASSIGNED.
<lfs_fieldname> = lw_extension2-value.
ENDIF.
ENDIF.
ENDCASE.
ENDLOOP.
ENDMETHOD.
Enhace the structures VBKOK as well LIKP with appen structures to add new z fields and enhance the structure VBPOK as well LIPS for the same purpose.
Also take a look to the program LV50LF01 and would need to implement an additional enhancement point to keep the values.
After this a think that I got it.
METHOD m_update_delivery.
DATA: lw_header_data TYPE bapiobdlvhdrchg,
lw_header_control TYPE bapiobdlvhdrctrlchg,
lw_extension2 TYPE bapiext.
DATA: li_extension2 TYPE TABLE OF bapiext,
li_return TYPE TABLE OF bapiret2.
DATA: lv_delivery TYPE bapiobdlvhdrchg-deliv_numb.
lw_header_data-deliv_numb = p_w_output-delivery.
lw_header_control-deliv_numb = p_w_output-delivery.
lv_delivery = p_w_output-delivery.
lw_extension2-param = 'VBKOK'.
lw_extension2-field = 'ZZEXIDV'.
lw_extension2-value = p_w_output-value.
APPEND lw_extension2 TO li_extension2.
" Call BAPI to update delivery
CALL FUNCTION 'BAPI_OUTB_DELIVERY_CHANGE'
EXPORTING
header_data = lw_header_data
header_control = lw_header_control
delivery = lv_delivery
TABLES
extension2 = li_extension2
return = li_return.
READ TABLE li_return INTO DATA(lw_return)
WITH KEY type = c_e.
IF sy-subrc <> 0.
COMMIT WORK AND WAIT.
ENDIF.
ENDMETHOD.
You need to add BAPI_COMMIT after your BAPI function module.

how can I get values from the table

I concatenated values of select-options and a parameter. The condition of that query is based on the concatenated data. I can get all the data i need.
here's my code:
TABLES: bkpf.
SELECT-OPTIONS: s_belnr FOR bkpf-belnr NO-EXTENSION OBLIGATORY .
PARAMETERS: p_ghjahr LIKE bkpf-gjahr DEFAULT sy-datum(4) OBLIGATORY. "Fiscal
DATA: it_con TYPE TABLE OF BKPF,
ls_con TYPE bkpf-AWKEY,
lv_belnr LIKE bkpf-belnr,
IT TYPE STANDARD TABLE OF BKPF,
WA TYPE BKPF.
IF s_belnr-high IS INITIAL.
CONCATENATE s_belnr-low p_ghjahr INTO ls_con.
APPEND ls_con TO it_con.
ELSE.
lv_belnr = s_belnr-low.
WHILE lv_belnr LE s_belnr-high.
CONCATENATE lv_belnr p_ghjahr INTO ls_con.
APPEND ls_con TO it_con.
ADD 1 TO lv_belnr.
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
EXPORTING
input = lv_belnr
IMPORTING
output = lv_belnr.
ENDWHILE.
ENDIF.
LOOP AT it_concats INTO ls_concats.
SELECT BELNR
FROM BKPF
INTO CORRESPONDING FIELDS OF TABLE IT
FOR ALL ENTRIES IN IT_CONCATS
WHERE AWKEY EQ IT_CONCATS-AWKEY.
ENDLOOP.
LOOP AT IT INTO WA.
WRITE: / WA-BELNR.
ENDLOOP.
Ignoring (because your question is too vague), the type of document you are looking for, I'll suggest something like
(WARNING, I do NOT provide full answers, just code snipets who you must tune to make it work; if someone wants to improve my answer, feel free to do it, and I'll gladly will vote the new one as the good one... if it is)
data: awkey_range type range of bkpf-awkey,
awkey_line like line of awkey_range.
* Fill the awkey_range with something like
awkey_line-sign = 'I'.
awkey_line-option = 'EQ'.
* loop at bkpf_table into bkpf_line.
* concatenate bkpf_line-belnr bkpf_line-ghjahr into awkey_line-low.
* append awkey_line to awkey_range.
* endloop.
* And then a single SQL
select *
from bkpf
into table IT "Ouch, what a name
where awkey in awkey_range.
And it should work, if I'm not missing something.

Create dynamic ABAP internal table

On selection screen, the user needs to insert a table name, and I need to get first 3 fields from that table and display them in an ALV for the output. What I understand from reading tutorials is that I need to call method cl_alv_table_create=>create_dynamic_table, but I don't know how to create the fieldcatalog.
DATA: t_newtable TYPE REF TO data,
t_fldcat TYPE lvc_t_fcat,
CALL METHOD cl_alv_table_create=>create_dynamic_table
EXPORTING
it_fieldcatalog = t_fldcat
IMPORTING
ep_table = t_newtable.
I assume that the table name which user enters is a data dictionary table (like SFLIGHT). If yes, then you can generate the field catalog as follows.
data : it_tabdescr type abap_compdescr_tab,
wa_tabdescr type abap_compdescr.
data : ref_table_descr type ref to cl_abap_structdescr.
ref_table_descr ?= cl_abap_typedescr=>describe_by_name( p_table ).
it_tabdescr[] = ref_table_descr->components[].
loop at it_tabdescr into wa_tabdescr.
clear wa_fieldcat.
wa_fieldcat-fieldname = wa_tabdescr-name .
wa_fieldcat-datatype = wa_tabdescr-type_kind.
wa_fieldcat-inttype = wa_tabdescr-type_kind.
wa_fieldcat-intlen = wa_tabdescr-length.
wa_fieldcat-decimals = wa_tabdescr-decimals.
append wa_fieldcat to it_fieldcat.
endloop.
Here, "p_table" is the selection screen parameter containing the table
name.