Return an internal table in ABAP - abap

Is it possible to return an internal table when calling a method of a class?
The idea is the following:
In my method, i am calculating all periods within a certain date-range.
E.g my range is: 01.01.2020 - 31.03.2020, so I want to get an internal table with the following results:
01.01.2020 - 31.01.2020
01.02.2020 - 28.02.2020
01.03.2020 - 31.03.2021
The calculation already works and i can display the results via the WRITE statement, but I am not sure how to return the result.
I created an internal table with the following structure:
TYPES: BEGIN OF periods,
begda TYPE dats,
endda TYPE dats,
END OF periods.
DATA: lt_periods TYPE STANDARD TABLE OF periods.
But I don't understand how to return the data to work with it in another method.
Thank you in advance.

Yes, it is possible to have an internal table as returning parameter of a method.
The type of the returning parameter has to be a table type, so a table type has to be declared:
TYPES tt_periods TYPE STANDARD TABLE OF periods WITH DEFAULT KEY. "As pointed out by Sandra, see below :)
And the method is declared like this:
METHODS method
... "IMPORTING parameters (if exist)
RETURNING
VALUE(rt_periods) TYPE tt_periods.

Just to add a little bit, you can do as follow:
define your data type in the public / protected or private section of your class
return / export / change in your method the specific data type defined above
If you data type definition is done in public section this will become visible for any other ABAP objects (for example you can use it in a function module definition or in other ABAP objects, as: <class_name>=><type_name>)

Related

How to expose the return type of a global class method?

I've written an ABAP Method, which returns me some analyses in a custom table.
Now I want to call this Method from an RFC module.
So far so good - the method works fine, but I'm curious of how to return this table?
Do I have to create a table / structure ... in SE11 to make it work because otherwise I can't refer to this table type or is there an easier way?
I'm quite new to ABAP so I don't know the best practices.
m_analyses = new zcl_manalyses( ).
data(lt_m_analyses) = m_analyses->analyse_m_data(
budat_from = budat_from
budat_to = budat_to
).
The TYPES statement can not only occur inside a method's body, but also inside the class definition, in which case it can be accessed from other places with class_name=>type_name (if it's public):
CLASS cl_user_registry DEFINITION PUBLIC.
PUBLIC SECTION.
TYPES:
BEGIN OF user,
firstname TYPE string,
lastname TYPE string,
END OF user,
users TYPE STANDARD TABLE OF user.
METHODS:
get_current_users
RETURNING users.
ENDCLASS.
DATA current_users TYPE cl_user_registry=>users.
current_users = cl_user_registry=>get_current_users( ).
You first have to create a structure in ABAP Dictionary (SE11), then you create a table type in SE11 as well.
You then reference the structure in the line type of the table type.
Try using the new global table type, it should work. (with typing 'TYPE')

Date DataType Vs. DateTime Structure Vs. DateAndTime

I found that there can be 5 different ways of declaring a date in VB.Net. They are:
Dim date_one As Date = #2021-3-31#: This is one of the allowed formats of date literals in VB
Dim date_two As New Date(2021,3,31) : Initializes an instance of the DateTime structure to the year, month and day
Dim date_three As DateTime : Declaring a DateTime Value to its default value
Dim date_four As New DateTime() : Calling parameterless constructor - sets to default value
Dim date_five As New DateTime(2021, 3, 31) : Supplying parameters to the constructor
My confusion /questions w.r.t to the above are the following:
A. DateTime is a structure in the system namespace. We do not use NEW keyword to declare a structure type variable. So why then in points 2,4,5 are we allowed to use the NEW keyword?
B. In point 2, we declare the type as DATE whereas in 3,4,5 we declare the type as DateTime. Infact if we compare2 & 5 then we see that they are doing exactly the same thing with the difference being tht one is declared as DATE and the other as DateTime. So my question is What is the difference between DATE and DateTime? If there is no difference why do we have two different words for the same thing?
C. Point 1 is a VB.Net language specific syntax for a date literal. This means I cant use this syntax in another .NET language. But the dates defined in points 2,3,4 and 5 make use of the DateTime structure which is in the system namespace and therefore this way of declaring or constructing dates is allowed in other .Net languages. ----> Is this understanding correct?
D. I also came across a class named DateAndTime. This belongs to the Microsoft.VisualBasic namespace and I believe that is reason this class is exclusive to VB.Net. Since it doesnt belong to the system namespace We cant import it in another .Net language --- Is this understanding correct?
E. Finally, how to decide whether to to use DATE or DateTime? Also when should we use DateAndTime Class? What advantage/limitation does each of them provide?
Would be grateful to anyone willing to help,
Regards,
Sougata
A. Class fields (variables at class level) and local variables (i.e., variables declared in methods or property getters and setters) get initialized to their default value if there is no initializer and there is no initialization code in the class constructor (for fields). So, a New is not required if you are happy with the default value.
Note that a Date (or DateTime) is a Structure and therefore a value type. Default values of value types are "real" values in contrast to reference types (Classes) having a default value of Nothing, meaning that no object is assigned to this variable. This is also called a null reference. (In fact you can assign Nothing to a value type; however, this will translate to a value like 0, False, #1/1/0001 12:00:00 AM#, etc.)
You must differentiate between declaring fields or variables and initializing them. To declare means to give them a name and a type. Initialize means assigning them value for the first time.
You can do this with two statements
Dim d As Date 'Declare
d = New Date(2021, 3, 30) 'Initialize
or use an initializer
Dim d As Date = New Date(2021, 3, 30) 'Declare and initialize
These two variants are equivalent. The first one allows you to assign a value later, the second one is more compact.
B. The .NET Framework defines primitive types and gives them a name in the System Namespace. E.g., System.String, System.Int32 or System.DateTime. Visual Basic gives many of them an alias. For the previously listed types those are String, Integer and Date. C# gives them the different aliases, string, int and has no alias for the DateTime.
The framework name and the corresponding Visual Basic (or C#) aliases are totally equivalent. System.Int32 ≡ VB Integer ≡ C# int.
C. Yes, e.g., in C# you must write new System.DateTime(...) (of course you can always import the namespace and omit the System. prefix). The Visual Basic date literal is just a short form for New Date(...) or New System.DateTime(...).
D. The DateAndTime Class is in fact a Visual Basic module. It is called class, because C# has no notion of modules as in VB and instead implements them as static classes.
The documentation says:
The DateAndTime module contains the procedures and properties used in date and time operations.
Languages in the .NET Framework have a Common Type System. Therefore, you can use a type defined in one language in all the other .NET languages. Also, the generated code is compatible, so you can share methods as well.
E. In Visual Basic I would use the aliases. It makes the code more concise.
You are using the DateAndTime automatically when you write for instance
Dim d As Date = Now
Now is declared in this module. DateAndTime does not compete with Date or DateTime, as you cannot use it to declare variables, i.e., DateAndTime is not a type.
See also:
Date Data Type (Visual Basic)
Value Types and Reference Types
Data Type Summary (Visual Basic)
Data type (Wikipedia)
A. DateTime is a structure in the system namespace. We do not use NEW keyword to declare a structure type variable. So why then in points 2,4,5 are we allowed to use the NEW keyword?
Structures may have constructors
B. In point 2, we declare the type as DATE whereas in 3,4,5 we declare the type as DateTime. Infact if we compare2 & 5 then we see that they are doing exactly the same thing with the difference being tht one is declared as DATE and the other as DateTime. So my question is What is the difference between DATE and DateTime? If there is no difference why do we have two different words for the same thing?
see https://stackoverflow.com/questions/5625795/date-instead-of-datetime
C. Point 1 is a VB.Net language specific syntax for a date literal. This means I cant use this syntax in another .NET language. But the dates defined in points 2,3,4 and 5 make use of the DateTime structure which is in the system namespace and therefore this way of declaring or constructing dates is allowed in other .Net languages. ----> Is this understanding correct?
The data type IS the same across .Net regardless of how it comes to be.
D. I also came across a class named DateAndTime. This belongs to the Microsoft.VisualBasic namespace and I believe that is reason this class is exclusive to VB.Net. Since it doesnt belong to the system namespace We cant import it in another .Net language --- Is this understanding correct?
If the .Net language allows you to import the Microsoft.VisualBasic namespace you can probably use it. Would NOT suggest it.
E. Finally, how to decide whether to to use DATE or DateTime? Also when should we use DateAndTime Class? What advantage/limitation does each of them provide?
Hmmmm.... DateAndTime does have some different date calculations that can be useful.
My preference is to use DateTime.
If all I'm interested in is the Date or Time then I reference that.
Dim d1 As Date = Date.Now
Dim d2 As DateTime = DateTime.Now
If d1 = d2 Then
Stop
End If
If d1.Date = d2.Date Then
Stop
End If
If d1.TimeOfDay = d2.TimeOfDay Then
Stop
End If
A lot of these questions can be answered by searching the documentation.

How to call remove_column on SALV table?

I want to execute the method remove_column on an instance of cl_salv_column_table but because of its visibility level, I am not able to do so.
Plan:
I already tried inheriting from cl_salv_columns_list and then perform the call inside the remove-method:
CLASS lcl_columns_list DEFINITION INHERITING FROM CL_SALV_COLUMNS_LIST.
PUBLIC SECTION.
METHODS:
remove IMPORTING iw_colname TYPE string.
ENDCLASS.
But apparently my casting knowledge got rusty as I'm not able to figure out an appropriate solution.
This is my current hierarchy - the red arrows show the way I would have to take:
My approach looks like this:
DATA lo_column_list TYPE REF TO lcl_columns_list.
lo_column_list ?= CAST cl_salv_columns_list( lo_columns ).
But it fails with:
CX_SY_MOVE_CAST_ERROR
Source type: \CLASS=CL_SALV_COLUMNS_TABLE
Target type: "\PROGRAM=XXX\CLASS=LCL_COLUMNS_LIST"
Background:
My task is to select all columns of 3 tables (which would be done like SELECT t1~*, t2~*, t3~* ...) as long as their names don't conflict (e.g. field MANDT should only be displayed once). This would require defining a very big structure and kick the size of the selection list to a maximum.
To avoid this, I wanted to make use of the type generated by my inline-declaration. Hiding the individual columns via set_visible( abap_false ) would still display them in the layout manager - which looks really ugly.
Is there any other way to accomplish my target?
Use set_technical( abap_true ) to hide the columns entirely. As for your approach - sorry, inheritance does not work that way - in no statically typed object oriented language that I know. You can't 'recast' an instantiated object to a different class. You would need to modify the framework extensively to support that.

How do I retrieve all child nodes of a hierarchical structure in ABAP?

Suppose I have a database table representing a hierachical structure, with the following columns:
id
predecessor_id
name
Starting from a given ID, I have to be able to retrieve all child nodes (not only the direct children). Since Common Table Expressions (WITH RECURSIVE) are not available in ABAP, what would be the best way to solve this?
A possible solution I have thought of is iterating through a result set (LOOP or by using a cursor), and recursively call a function that retrieves the direct child nodes. However, I hope there is a more elegant approach.
First of all you need to know that SAP is not a database and OpenSQL is always translated to the SQL dialect of the underlying database. If the underlying database does not support WITH or WITH RECURSIVE and from what I see from the following article not each and every database does so, then adding it to OpenSQL would not make any sense as in many cases there would not be anything to map it to.
So the first solution would be as you proposed, writing a separate recursive function/method/subroutine or if you really want to use underlying database functionality you can use ADBC interface. If you are familiar with JDBC then the concept shouldn't be new to you. If you are doing that for productive purposes however you should make sure that there is a litte or no probability of a database migration in the future.
The solution with ADBC that works for me on an SAP system with an underlying Oracle database.
REPORT Z_ADBC_TEST.
CLASS lcl_test DEFINITION.
PUBLIC SECTION.
CLASS-METHODS:
main.
ENDCLASS.
CLASS lcl_test IMPLEMENTATION.
METHOD main.
DATA lo_sql_connection TYPE REF TO cl_sql_connection.
DATA lo_sql_statement TYPE REF TO cl_sql_statement.
DATA lo_sql_result_set TYPE REF TO cl_sql_result_set.
TYPES BEGIN OF lt_result_struct,
n TYPE i,
fact TYPE i,
END OF lt_result_struct.
DATA lt_result TYPE TABLE OF t_result_struct WITH DEFAULT KEY.
DATA lr_ref_to_data TYPE REF TO data.
FIELD-SYMBOLS <fs_result> LIKE LINE OF lt_result.
lo_sql_connection = cl_sql_connection=>get_connection( ).
lo_sql_statement = lo_sql_connection->create_statement( ).
GET REFERENCE OF lt_result INTO lr_ref_to_data.
lo_sql_result_set = lo_sql_statement->execute_query(
`WITH temp(n, fact) ` &&
`AS (SELECT 0,1 FROM dual UNION ALL ` &&
`SELECT n+1,(n+1)*fact FROM temp ` &&
`WHERE n < 9) ` &&
`SELECT * FROM temp`
).
lo_sql_result_set->set_param_table( lr_ref_to_data ).
WHILE lo_sql_result_set->next_package( ) > 0.
LOOP AT lt_result ASSIGNING <fs_result>.
WRITE: / <fs_result>-n, <fs_result>-fact.
ENDLOOP.
ENDWHILE.
ENDMETHOD.
ENDCLASS.
END-OF-SELECTION.
lcl_test=>main( ).

Which should be used in ABAP : TYPE or LIKE?

So these both seem to work for me:
TABLES:
T001, "Table of Company Codes.
Z_KNA1_VBRK. "View I created..
DATA:
CCNAME TYPE T001-BUTXT,
CCCURR TYPE T001-WAERS,
KNAVBK TYPE Z_KNA1_VBRK,
AMNICC TYPE Z_KNA1_VBRK-NETWR.
and
DATA:
CCNAME LIKE T001-BUTXT,
CCCURR LIKE T001-WAERS,
KNAVBK LIKE Z_KNA1_VBRK,
AMNICC LIKE Z_KNA1_VBRK-NETWR.
PARAMETERS:
COMPCODE LIKE T001-BUKRS.
Is there any difference between them technically? Which is preferred / best practice and why?
To get the difference try to compile the following program.
REPORT zzz.
CLASS lcl_main DEFINITION FINAL CREATE PRIVATE.
PUBLIC SECTION.
CLASS-METHODS:
main.
ENDCLASS.
CLASS lcl_main IMPLEMENTATION.
METHOD main.
DATA: ls_t000t TYPE t000,
ls_t000l LIKE t000.
ENDMETHOD.
ENDCLASS.
The error message you will get is
Within classes and interfaces, you can only use "TYPE" to refer to ABAP Dictionary types, not "LIKE" or "STRUCTURE".
This is because in the OO context you need to write explicitly TYPE when you actually refer to a type. This is the current state of the art.
Now change your program slightly and try to declare global variables with LIKE and TYPE.
REPORT zzz.
DATA: gs_t000t TYPE t000,
gs_t000l LIKE t000.
CLASS lcl_main DEFINITION FINAL CREATE PRIVATE.
PUBLIC SECTION.
CLASS-METHODS:
main.
ENDCLASS.
CLASS lcl_main IMPLEMENTATION.
METHOD main.
DATA: ls_t000t TYPE t000.
* ls_t000l LIKE t000.
ENDMETHOD.
ENDCLASS.
As you can see there are no compilation errors in this case. In this context TYPE and LIKE are interchangeable, they mean the same. This applies also to the "old" parts of ABAP means of modularization like subroutines and function modules.
However I use the following rule of thumb.
Whenever I refer to a DDIC or local type I use TYPE. If I want to create a variable that is exactly of the same type like other variable I use LIKE. Should the type of the original variable change in the future, the change has to be made only in one place then.
Example.
METHOD main.
DATA: ls_t000t TYPE t000. "should the type change from T000 to T002
"in the future, one has to change it only in one place.
DATA: ls_t000l LIKE ls_t000t.
ENDMETHOD.
Yes, there is a difference. You cannot get this difference because you declared your structures via TABLES statement, which is obsolete now and shouldn't be used.
TABLES statement declares interface work area with a name identical to Data Dictionary structure. Therefore both your LIKE and TYPE declarations treat Z_KNA1_VBRK either like data object or DDIC structure correspondingly.
In any other case such declaration won't compile because LIKE and TYPE statements are not interchangable.
You should declare separate structures in your program rather than using such obsolete elements. The only allowed exception is data exchange with classic dynpros.
To further details, switch to ABAP documentation.