I have the following piece of code.
REPORT ZZY.
CLASS lcl_main DEFINITION FINAL CREATE PRIVATE.
PUBLIC SECTION.
CLASS-METHODS:
convert_to_xstring
IMPORTING
i_param1 TYPE i
i_param2 TYPE i
RETURNING
VALUE(rv_result) TYPE xstring,
main.
ENDCLASS.
CLASS lcl_main IMPLEMENTATION.
METHOD convert_to_xstring.
ENDMETHOD.
METHOD main.
DATA: lt_binary_tab TYPE STANDARD TABLE OF x.
DATA(lv_result) = convert_to_xstring( i_param1 = 1 i_param2 = 2 ).
CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
EXPORTING
buffer = lcl_main=>convert_to_xstring(
EXPORTING
i_param1 = 1
i_param2 = 2
)
TABLES
binary_tab = lt_binary_tab.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
lcl_main=>main( ).
A functional method call that is not a part of a function module call can be written like that.
DATA(lv_result) = convert_to_xstring( i_param1 = 1 i_param2 = 2 ).
However when I want to use it exactly as written above
CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
EXPORTING
buffer = lcl_main=>convert_to_xstring( i_param1 = 1 i_param2 = 2 )
TABLES
binary_tab = lt_binary_tab.
I get the following syntax error.
Field "CONVERT_TO_XSTRING(" is unknown. It is neither in one of the
specified tables nor defined by a "DATA" statement. "DATA" statement.
It looks like the compiler needs some guidance in this case to distinguish between an attribute and a method. Why would it be ambiguous for the compiler to let such a case without writing EXPORTING?
CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
EXPORTING
buffer = lcl_main=>convert_to_xstring( EXPORTING i_param1 = 1 i_param2 = 2 )
TABLES
binary_tab = lt_binary_tab.
The design of abap is quite bad. There is something like functional method calls, but you can't use it in combination with all commands. For example the WRITE command doesn't work in combination with functional method calls. This seems to be some kind of "partial compatible" with function method calls.
I don't know why(maybe the sap dev folks were drunk), but it is just a fact we have to live with.
Related
I know when I need to pass an internal table to a subroutine, I need to declare a type so I can use it in the FORM statement.
What happens if the internal table is an inline declaration table from a SELECT statement like this:
SELECT * FROM KNA1 INTO TABLE #DATA(LT_KNA1)
Is there any way to pass this table to a subroutine?
Thank you.
The subroutines are obsolete since ABAP 7.02 (2009), so I use a method in my example.
Inline declarations are an easy way of declaring types implicitly, but the limit of this solution is that you can type the parameter of a method only generically (types STANDARD TABLE, INDEX TABLE, ANY TABLE, ANY) which prevents you from stating the component names statically in your code.
But inline declarations of type DATA(...) are always based on a complete "bound" data type, so you can declare the type explicitly with TYPES and use it to type both your parameter and your data object.
If you use the ABAP Development Tools (Eclipse), you may use the Quick Fix "Declare local variable ... explicitly" to simplify the task:
which gives this code:
REPORT.
CLASS lcl_app DEFINITION.
PUBLIC SECTION.
CLASS-METHODS main.
ENDCLASS.
CLASS lcl_app IMPLEMENTATION.
METHOD main.
TYPES: BEGIN OF helper_type, " <=== automatically generated
carrid TYPE scarr-carrid,
carrname TYPE scarr-carrname,
END OF helper_type.
DATA: lt_scarr TYPE STANDARD TABLE OF helper_type. " <=== automatically generated
SELECT carrid, carrname FROM scarr
INTO TABLE #lt_scarr. " <=== automatically changed
ENDMETHOD.
ENDCLASS.
Now, declare manually the table type, use it to type a parameter of a method (a new one here):
REPORT.
CLASS lcl_app DEFINITION.
PUBLIC SECTION.
TYPES: BEGIN OF helper_type,
carrid TYPE scarr-carrid,
carrname TYPE scarr-carrname,
END OF helper_type.
TYPES: tt_scarr TYPE STANDARD TABLE OF helper_type. " <=== declare the type
CLASS-METHODS main.
CLASS-METHODS process_table " <=== new method with this type
IMPORTING table TYPE tt_scarr.
ENDCLASS.
CLASS lcl_app IMPLEMENTATION.
METHOD main.
DATA: lt_scarr TYPE STANDARD TABLE OF helper_type.
SELECT carrid, carrname FROM scarr
INTO TABLE #lt_scarr.
ENDMETHOD.
METHOD process_table. " <=== new method
LOOP AT table REFERENCE INTO DATA(line).
DATA(carrid) = line->carrid.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
Use TYPE ANY or generic table type for parameters, TABLE parameters are obsolete:
FORM fill_table USING tab TYPE any
CHANGING ptab TYPE INDEX TABLE.
APPEND LINES OF tab TO ptab.
ENDFORM.
And yes, subroutines are obsolete themselves, use them only if you absolutely must do this, e.g. in legacy environment.
You don't need to indicate structure type for perform variable table. But you can get error on runtime if any field name change which are used in perform. Two working example below.
SELECT * FROM kna1 INTO TABLE #DATA(lt_kna1).
PERFORM test TABLES lt_kna1.
FORM test TABLES pt_kna1 STRUCTURE kna1.
*
ENDFORM.
Risky one:
FORM test TABLES pt_kna1.
*
ENDFORM.
I want to return record of table from abap method, which is field of class
but nothing is returned to the variable in PBO
definition in class
data MS_ZORK_JG_SETTING type ZORK_JG_SETTING .
methods GET_MS_ZORK_JG_SETTING
returning
value(MS_ZORK_JG_SETTING) type ZORK_JG_SETTING .
MS_ZORK_JG_SETTING is a data type which contains data from ZORK_JG_SETTING transparent table, filled properly using other method, so there is a correct data in this variable
in pbo of the screen i wanted to assign returned value to variable of the same type as returned
MODULE pbo_0102 OUTPUT.
DATA: wa_jg_setting TYPE zork_jg_setting.
wa_jg_setting = go_bukrs_conf->get_ms_zork_jg_setting( ).
MOVE-CORRESPONDING wa_jg_setting TO zork_jg_setting.
ENDMODULE.
But wa_jg_setting is empty. Tell me why and how to repair that?
Place of invoking screen
DATA: go_bukrs_conf TYPE REF TO zork_cl_scr_bukrs_conf.
CREATE OBJECT go_bukrs_conf
EXPORTING
pa_bukrs = '3020'.
CALL SCREEN 102.
And get_ms_zork_jg_settings method. I am assigning a value of field to a formal parameter
method GET_MS_ZORK_JG_SETTING.
ms_zork_jg_setting = ms_zork_jg_setting."zwracana wartosc to pole/ atrybut
endmethod.
The line
ms_zork_jg_setting = ms_zork_jg_setting.
is useless, as you assign the value of the return parameter to itself.
Generally speaking, var = var is always non-sense.
Probably you want to do:
ms_zork_jg_setting = me->ms_zork_jg_setting.
So I need to use the INCLUDES rpcblo00 and rpcbdt00 to get the type of infotype change (create, update, delete). Beforehand I used a subroutine that had no problem with the includes, but I cannot get them into a class for the life of me.
If I try to put the include into the method as described here (it's even about the same HR include), I get the following error (because of the minus in lo-key):
Syntax error: Names may only consist of the characters "A-Z", "0-9"
and "_". In addition, they may not begin with a number.
minimal reproducible example:
CLASS lcl_infotypaenderungen DEFINITION.
PUBLIC SECTION.
TYPES: tty_aenderungs_operationen TYPE STANDARD TABLE OF pc403.
METHODS:
constructor
IMPORTING is_aenderungs_kopf TYPE pldoc_key,
get_aenderungs_operationen
RETURNING value(rt_aenderungs_operationen) TYPE tty_aenderungs_operationen.
PRIVATE SECTION.
DATA: s_aenderungs_kopf TYPE pldoc_key,
t_aenderungs_operationen TYPE tty_aenderungs_operationen.
METHODS:
select_aenderungs_operationen.
ENDCLASS. "lcl_infotypaenderungen DEFINITION
*----------------------------------------------------------------------*
TYPE-POOLS: abap.
DATA: lo_infotypaenderungen TYPE REF TO lcl_infotypaenderungen,
lv_fehler TYPE sy-subrc,
lt_log_kopf TYPE pldoc_key_tab WITH HEADER LINE,
lt_log_felder TYPE TABLE OF hrinftylog_fields,
lt_infotyp_vorher TYPE prelp_tab,
lt_infotyp_nachher TYPE prelp_tab,
lt_aenderungs_operationen TYPE STANDARD TABLE OF pc403.
FIELD-SYMBOLS: <log_kopfzeile> TYPE pldoc_key.
*----------------------------------------------------------------------*
CALL FUNCTION 'HR_INFOTYPE_LOG_GET_LIST'
EXPORTING
tclas = 'A'
begda = '20190315'
endda = '20190315'
IMPORTING
subrc = lv_fehler
TABLES
infty_logg_key_tab = lt_log_kopf.
CLEAR lv_fehler.
SORT lt_log_kopf DESCENDING BY infty bdate btime pernr.
LOOP AT lt_log_kopf ASSIGNING <log_kopfzeile>.
CALL FUNCTION 'HR_INFOTYPE_LOG_GET_DETAIL'
EXPORTING
logged_infotype = <log_kopfzeile>
IMPORTING
subrc = lv_fehler
TABLES
infty_tab_before = lt_infotyp_vorher
infty_tab_after = lt_infotyp_nachher
fields = lt_log_felder.
CREATE OBJECT lo_infotypaenderungen
EXPORTING
is_aenderungs_kopf = <log_kopfzeile>.
REFRESH lt_aenderungs_operationen.
lt_aenderungs_operationen = lo_infotypaenderungen->get_aenderungs_operationen( ).
ENDLOOP.
*----------------------------------------------------------------------*
CLASS lcl_infotypaenderungen IMPLEMENTATION.
METHOD constructor.
me->s_aenderungs_kopf = is_aenderungs_kopf.
me->select_aenderungs_operationen( ).
ENDMETHOD. "constructor
METHOD select_aenderungs_operationen.
INCLUDE rpcblo00. """ <---
INCLUDE rpcbdt00. """ <---
lo-key-tclas = me->s_aenderungs_kopf-tclas.
lo-key-pernr = me->s_aenderungs_kopf-pernr.
lo-key-infty = me->s_aenderungs_kopf-infty.
lo-key-bdate = me->s_aenderungs_kopf-bdate.
lo-key-btime = me->s_aenderungs_kopf-btime.
lo-key-seqnr = me->s_aenderungs_kopf-seqnr.
IMPORT header TO me->t_aenderungs_operationen FROM DATABASE pcl4(la) ID lo-key.
ENDMETHOD. "select_aenderungs_operationen
METHOD get_aenderungs_operationen.
rt_aenderungs_operationen = me->t_aenderungs_operationen.
ENDMETHOD. "get_aenderungs_operationen
ENDCLASS. "lcl_infotypaenderungen IMPLEMENTATION
Anyone know a good solution? Thanks in advance
Edit: The includes have some declarations and a makro reading from a data cluster. Of course I could just put those directly into the method, but I would like to avoid that (for now I did that).
Alternatively, does someone know of a different way to get the change operation per infotype line?
If you use your class as a local one then the only way to use these includes is to put them at the very beginning of the program. The downside is of course that the variables there become global but unfortunately there is no other way to do that and for sure not if you want to use a global class after all (not sure if your minimal working example is just simplified to use a local class instead of global or not).
REPORT ZZZ.
INCLUDE rpcblo00. """ <---
INCLUDE rpcbdt00. """ <---
CLASS lcl_infotypaenderungen DEFINITION.
" ...
Thanks to Jagger I can make it work with a local class, but in case anyone later wonders how you need to change the include code to be able to use it in a global method, you basically just need to get rid of INCLUDE STRUCTURE declarations and exchange tables with a header line.
So
DATA BEGIN OF LO-KEY.
INCLUDE STRUCTURE PC400.
DATA END OF LO-KEY.
becomes
DATA: lo_key TYPE pc400.
And
DATA BEGIN OF BELEGE_00 OCCURS 100.
DATA:
SPLKZ(01) TYPE X,
FIELD(10) TYPE C,
FTYPE(04) TYPE C,
FLENG(03) TYPE N,
DECIM(02) TYPE N,
OLDDT(50) TYPE C,
NEWDT(50) TYPE C.
DATA END OF BELEGE_00.
becomes
TYPES: BEGIN OF ty_belege,
splkz(01) TYPE x,
field(10) TYPE c,
ftype(04) TYPE c,
fleng(03) TYPE n,
decim(02) TYPE n,
olddt(50) TYPE c,
newdt(50) TYPE c,
END OF ty_belege.
DATA: belege_00 TYPE STANDARD TABLE OF ty_belege.
The macro can stay the same (or I guess you could rewrite it).
Can I define an ABAP method where the RETURNING parameter and any IMPORTING parameters have a generic type but that can still be called in a single line as a functional method?
In other words I'd like to replace this:
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
EXPORTING
input = lv_external_value
IMPORTING
output = lv_internal_value.
With:
lv_internal_value= zcl_conversion=>alpha_input( lv_external_value ).
Unfortunately the fact that Class Methods can't have an untyped returning parameter is preventing me from declaring the functional method's return value as type ANY or CLIKE. The accepted standard of creating generic method parameters seems to be to define them as TYPE REF TO DATA and dereference/assign them. But as far as I know that prevents me from calling the method in a single statement as I have to first assign the importing parameter and then dereference the returning parameter, resulting in the same or more lines of code than a simple FM call.
Is there a way around this?
Unfortunately, there is no other way to dereference data than to use the dereference operator, either in the form ->* for the full value segment, or in the form ->comp, if the data object is structured and has a component named comp (and, even worse, there are a lot of places in ABAP code where you would like to use a value from a derefenced data object but can't do it for internal reasons / syntax restrictions).
However, you could simply keep the data reference object retrieved by your method in a variable of the calling code and work with that variable (instead of using a field symbol or a variable for the derefenced value segment itself). Either generically, as a ref to data variable, or typed, using the CAST operator (new ABAP syntax).
Most things that can be done with a field-symbol, can also be done directly with a data reference as well.
Example: Working with a variable result of the expected return type:
data(result) = cast t000( cl=>m( ) ).
write result->mandt.
See here the full example:
report zz_new_syntax.
class cl definition.
public section.
class-methods m returning value(s) type ref to data.
endclass.
start-of-selection.
data(result) = cast t000( cl=>m( ) ).
write: / result->mandt. " Writes '123'.
class cl implementation.
method m.
s = new t000( mandt = '123' ).
endmethod.
endclass.
On ABAP NW Stack 7.4 you could just use parameters type STRING and then use the new CONV Operator to convert your actual input in string. Little ugly but should work.
lv_internal_value = CONV #(zcl_conversion=>alpha_input( CONV #(lv_external_value) )).
i'm new in abap (OO) but developed before in java and wrote a class abap "cl_caretaker" which should handle the operations on database table and the local copy (intern table) of it.
I want to make the following method call:
caretaker->show_table( caretaker->get_users( ) ) .
with:
caretaker = cl_caretaker=>get_instance( ). "singleton instance
METHODS:
"! get a list of all user which registrated for FCP
"!
"! #parameter rt_users | users which are registrated for FCP
get_users
RETURNING value(rt_users) TYPE itty_users,
"! shows the content of a table
"!
"! #parameter it_table | the table we want to visualize
show_table
IMPORTING
value(it_table) TYPE ANY TABLE.
if I split the call in two and store the result of get_users in a tmp variable it works.
DATA:
gt_tmp_users TYPE caretaker->itty_users.
gt_tmp_users = caretaker->get_users( ).
caretaker->show_table( gt_tmp_users ).
So my questions are:
1) is a call like: caretaker->show_table( caretaker->get_users( ) ).
possible and if how?
2) I also tried to create a generic variable, which stores all kind of tables.
Because i don't want to create for each table kind i use a tmp/help variable.
But i got the information that only (german: Formalparameter) dummy parameters of method definitions are allowed to of generic type (eg. TYPE any TABLE ).
Here some stuff I already tried:
DATA:
* tmpanytable TYPE TABLE OF any.
* tmpAnyTable TYPE any.
tmpanytable TYPE REF TO data.
" needed to store a temporal table
FIELD-SYMBOLS: <tmpanytable> TYPE ANY TABLE.
* ASSIGN caretaker->get_users( ) TO <tmpAnyTable>.
* <tmpAnyTable> = caretaker->get_users( ).
* caretaker->get_users( ).
*caretaker->show_table( <tmpAnyTable> ).
*caretaker->show_table( caretaker->get_users( ) ).
*CALL METHOD: caretaker->show_table( IMPORTING it_table = caretaker->get_users ).
*CALL METHOD: caretaker->show_table( it_table = caretaker->get_users( ) ).
*COMPUTE caretaker->show_table( it_table = caretaker->get_users( ) ).
*ASSIGN caretaker->get_users() ->* to <tmpAnyTable>.
*Caretaker->show_table( <tmpAnyTable> ).
*call METHOD caretaker->show_table
* Exporting It_table = caretaker->get_users( ).
* CREATE DATA tmpanytable TYPE STANDARD TABLE OF (dbtab)
* WITH NON-UNIQUE DEFAULT KEY.
* ASSIGN tmpanytable->* TO <tmpanytable>.
* CREATE DATA tmpanytable TYPE tabkind OF any Table .
* ASSIGN tmpanytable->* TO <tmpanytable>.
*GET REFERENCE OF caretaker->get_users() INTO tmpAnyTable.
*caretaker->show_table( tmpAnyTable ) .
Method chaining is possible, and methods in operand positions are possible as well, but you need at least SAP_ABA 702 for that.
You can use generic types to pass a table around without knowing its type at runtime. However, you can't create a table without knowing its type. Comparing it to OO principles, you can handle references to an abstract superclass and pass them along between components, but you can't instantiate the abstract superclass.
The CREATE DATA statement needs a "concrete data type" to work on, not the "abstract super type STANDARD TABLE". The hard part here is deciding who will know about the type and create the data object.
BTW, you may want to take a look at the built-in Object Services - maybe there's no need to reinvent the database access layer wheel yet again.