Handling function module interfaces in bulk? - abap

Is there a way to access the import parameters passed into a function module without addressing them individually? Does ABAP store them in some internal table, so I can work with them by looping through rows in some table, or fields of a structure?
We can use the PATTERN function, knowing only the function module's name, to have ABAP print out the function module's interface for us, so I'm wondering where this information is stored and if I can work with it once the function group has been loaded into memory.
Thanks in advance!

You can use the function module RPY_FUNCTIONMODULE_READ to obtain information about the parameter structure of a function module and then access the parameters dynamically. This has several drawbacks - most noticeably, the user doing so will need (additional) S_DEVELOP authorizations, and logging this way will usually impose a serious performance impact.
I'd rather add the function module parameters to the logging/tracing function manually once - with a sufficiently generic method call, it's not that difficult. I also tend to group individual parameters into structures to facilitate later enhancements.

PARAMETER-TABLE construct exists in ABAP since ancient times, it allows passing params in batch:
One should create two parameter tables of types abap_func_parmbind_tab and abap_func_excpbind_tab and fill them like this:
DATA: ptab TYPE abap_func_parmbind_tab,
etab TYPE abap_func_excpbind_tab,
itab TYPE TABLE OF string.
ptab = VALUE #(
( name = 'FILENAME' kind = abap_func_exporting value = REF #( 'c:\text.txt' ) )
( name = 'FILETYPE' kind = abap_func_exporting value = REF #( 'ASC' ) )
( name = 'DATA_TAB' kind = abap_func_tables value = REF #( itab ) )
( name = 'FILELENGTH' kind = abap_func_importing value = REF #( space ) ) ).
etab = VALUE #( ( name = 'OTHERS' value = 10 ) ) .
CALL FUNCTION 'GUI_DOWNLOAD'
PARAMETER-TABLE ptab
EXCEPTION-TABLE etab.

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

BAPI_CONTRACT_CREATE error "Enter material number or account assignment category"

I am using the BAPI_CONTRACT_CREATE to upload the contract orders in ME31K.
For Testing purpose I maintained all the parameters which is relevant to above BAPI in SE37.
But system is showing the error even though material code & and all other fields are maintained in respective fields.
Please find the attached screenshot for the reference.
My 2 cents, as this kind of error is frequent in many BAPI functions.
BAPI functions frequently have their parameters doubled, for instance one named ITEM and another one named accordingly and suffixed with X (ITEMX). This additional parameter has the exact same fields, with the same name but with a one-character length which can take the value either ' ' or 'X'.
'X' is to indicate that the corresponding field in ITEM must be changed. The interest of these doubled parameters can be better understood in BAPI used to change an existing object, to force a field to be changed to an empty value. Fields with a empty value mean "no change".
Example code:
DATA: items TYPE TABLE OF BAPIMEOUTITEM,
items_x TYPE TABLE OF BAPIMEOUTITEMX.
items = VALUE #(
( item_no = '00010' material = 'MAT01' )
( item_no = '00020' material = 'MAT02' ) ).
items_x = VALUE #(
( item_no = '00010' material = 'X' )
( item_no = '00020' material = 'X' ) ).
...
CALL FUNCTION 'BAPI_CONTRACT_CREATE'
TABLES
item = items
itemx = items_x
...

Checking against value in a STRING_TABLE in a WHERE clause

I have a procedure with the parameter IT_ATINN:
IMPORTING
REFERENCE(IT_ATINN) TYPE STRING_TABLE
IT_ATINN contains a list of characteristics.
I have the following code:
LOOP AT values_tab INTO DATA(value).
SELECT ( #value-INSTANCE ) AS CUOBJ
FROM IBSYMBOL
WHERE SYMBOL_ID = #value-SYMBOL_ID
AND ATINN ??? "<======== HERE ???
APPENDING TABLE #DATA(ibsymbol_tab).
ENDLOOP.
How can I check if ATINN (in the WHERE clause) is equal to any entry in IT_ATINN?
To achieve what you want (and I assume you want dynamic SELECT fields) you cannot use inline declarations here, both in LOOP and in SELECT:
The structure of the results set must be statically identifiable. The SELECT list and the FROM clause must be specified statically and host variables in the SELECT list must not be generic.
So either you use inline or use dynamics, not both.
Here is the snippet that illustrates Sandra good suggestion:
TYPES: BEGIN OF ty_value_tab,
instance TYPE char18,
symbol_id TYPE id,
END OF ty_value_tab.
DATA: it_atinn TYPE string_table.
DATA: rt_atinn TYPE RANGE OF atinn,
value TYPE ty_value_tab,
values_tab TYPE RANGE OF ty_value_tab,
ibsymbol_tab TYPE TABLE OF ibsymbol.
rt_atinn = VALUE #( FOR value_atinn IN it_atinn ( sign = 'I' option = 'EQ' low = value_atinn ) ).
APPEND VALUE ty_value_tab( instance = 'ATWRT' ) TO values_tab.
LOOP AT values_tab INTO value.
SELECT (value-instance)
FROM ibsymbol
WHERE symbol_id = #value-symbol_id
AND atinn IN #rt_atinn
APPENDING CORRESPONDING FIELDS OF TABLE #ibsymbol_tab.
ENDLOOP.
Overall, it makes no sense select ibsymbol in loop, 'cause it has only 8 fields, so you can easily collect all necessary fields from values_tab and pass them as dynamic fieldstring.
If you wanna use alias CUOBJ for your dynamic field you should add it like this:
LOOP AT values_tab INTO value.
DATA(aliased_value) = value-instance && ` AS cuobj `.
SELECT (aliased_value)
...
Remember, that your alias should exists among ibsymbol fields, otherwise in case of static ibsymbol_tab declaration this statement will throw a short dump.

READ TABLE WITH TABLE KEY does not find record

I'm trying to use the class /ui5/cl_json_parser for parsing a JSON string.
The following code snippet reproduces the problem:
REPORT ztest_json_parse.
DATA: input TYPE string,
output TYPE string,
json_parser TYPE REF TO /ui5/cl_json_parser.
input = '{"address":[{"street":"Road","number":"545"},{"street":"Avenue","number":"15"}]}'.
CREATE OBJECT json_parser.
json_parser->parse( input ).
json_parser->print( ).
output = json_parser->value( path = '/address/1/street' ).
WRITE output.
The print method shows the correct parsed JSON string, but the output variable is always empty.
I have traced the code down to the method VALUE of the class /UI5/CL_JSON_PARSER, at line 15, which contains:
read table m_entries into l_entry with table key parent = l_parent name = l_name.
In the debugger, I can see that l_parent = '/address/1' and l_name = 'street', and that the internal table m_entries contains a record with parent = '/address/1' and name = 'street'. Nevertheless the READ statement always returns sy-subrc = 4 and does not find anything.
Can anyone help?
First: Do not use /ui5/cl_json_parser class, it is intended for internal use ONLY and has no reliable documentation
Secondly, here is the sample how you can fetch street value from the first element of your JSON:
DATA(o_json) = cl_abap_codepage=>convert_to( '{"address":[{"street":"Road","number":"545"},{"street":"Avenue","number":"15"}]' ).
DATA(o_reader) = cl_sxml_string_reader=>create( o_json ).
TRY.
DATA(o_node) = o_reader->read_next_node( ).
WHILE o_node IS BOUND.
DATA(op) = CAST if_sxml_open_element( o_node ).
LOOP AT op->get_attributes( ) ASSIGNING FIELD-SYMBOL(<a>).
DATA(attr) = <a>->get_value( ).
ENDLOOP.
IF attr <> 'street'.
o_node = o_reader->read_next_node( ).
ELSE.
DATA(val) = CAST if_sxml_value_node( o_reader->read_next_node( ) ).
WRITE: '/address/1/street =>', val->get_value( ).
EXIT.
ENDIF.
ENDWHILE.
CATCH cx_root INTO DATA(e_txt).
ENDTRY.
As far as I know, there is no class in ABAP that allows fetching single JSON attributes like XPath.
Certainly agree with Suncatcher on avoid UI5 Json parser.
If you dont control/know the structure of the source data, Suncatchers answer is good.
However,
if you know the basic structure of the source JSON and you must, if you plan to access the first address row, fieldname street .
AND you can have the source provided using uppercase variable names then you can use the so called identity transformation.
types: begin of ty_addr,
street type string,
number type string,
end of ty_addr.
types ty_addr_t type STANDARD TABLE OF ty_addr.
DATA: input TYPE string,
ls_addr TYPE ty_addr,
lt_addr type ty_addr_t.
input = '{"ADDRESS":[{"STREET":"Road","NUMBER":"545"},{"STREET":"Avenue","NUMBER":"15"}]}'.
CALL TRANSFORMATION id SOURCE XML input
RESULT address = lt_addr.
read table lt_addr index 1 into ls_addr.
WRITE ls_addr-street.

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