Adding the values of a field using FOR loop - abap

How do I add the values in a field based on the same values in another field using FOR loop?
Types:
Begin of ty_final,
doctype type char5,
posnr type char5,
total type quan5,
End of ty_final.
DATA(lt_final) = VALUE ty_final(
FOR line IN lt_history
         WHERE ( hist_type = 'U' )
         ( doctype = line-hist_type
total  = line-quantity
           posnr   = line-po_item  ) ).
What I have in LT_HISTORY:
HIST_TYPE POSNR QUANTITY
U 10 5
U 10 2
U 20 3
U 20 -3
What I need in LT_FINAL:
DOCTYPE POSNR QUANTITY
U 10 7
U 20 0
I am trying to use GROUP BY to get the sum of the values in TOTAL field based on POSNR and DOCTYPE fields. It's just that I am not sure where exactly I need to add GROUP BY in my FOR loop. REDUCE makes my head spin. So I was trying out as simple as possible.

Below is minimal reproducible example, which uses ABAP Unit (Ctrl+Shift+F10 to run it). I commented your code and replaced it with the solution:
CLASS ltc_test DEFINITION FOR TESTING
DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
METHODS test FOR TESTING.
ENDCLASS.
CLASS ltc_test IMPLEMENTATION.
METHOD test.
TYPES: BEGIN OF ty_line,
doctype TYPE c LENGTH 1,
posnr TYPE i,
quantity TYPE i,
END OF ty_line,
ty_table TYPE STANDARD TABLE OF ty_line WITH DEFAULT KEY.
DATA(lt_history) = VALUE ty_table(
( doctype = 'U' posnr = 10 quantity = 5 )
( doctype = 'U' posnr = 10 quantity = 2 )
( doctype = 'U' posnr = 20 quantity = 3 )
( doctype = 'U' posnr = 20 quantity = -3 ) ).
DATA(lt_final) = VALUE ty_table(
* FOR line IN lt_history
* WHERE ( doctype = 'U' )
* ( doctype = line-doctype
* quantity = line-quantity
* posnr = line-posnr ) ).
FOR GROUPS grp_line OF line IN lt_history
WHERE ( doctype = 'U' )
GROUP BY ( doctype = line-doctype posnr = line-posnr )
( doctype = grp_line-doctype
quantity = REDUCE #( INIT t = 0 FOR <line> IN GROUP grp_line
NEXT t = t + <line>-quantity )
posnr = grp_line-posnr ) ).
cl_abap_unit_assert=>assert_equals( act = lt_final exp = VALUE ty_table(
( doctype = 'U' posnr = 10 quantity = 7 )
( doctype = 'U' posnr = 20 quantity = 0 ) ) ).
ENDMETHOD.
ENDCLASS.

Related

Why values are not displayed for some columns of an ALV List?

I am trying to create an ALV report with list display but some of the contents are not displaying in the output list. I created a classical report and an ALV report with grid display and I was successful. But this list display is creating a problem.
I've included the REUSE_ALV_LIST_DISPLAY function, with the right internal table name. I've debugged and all my data is coming in the final internal table correctly but it is not displaying in output list:
Here is my code (note that the flight demo data is to be generated via the program SAPBC_DATA_GENERATOR, once):
REPORT ztest.
SELECT scarr~carrid, spfli~connid
FROM scarr INNER JOIN spfli ON scarr~carrid = spfli~carrid
INTO TABLE #DATA(it_f).
DATA(it_fcat) = VALUE slis_t_fieldcat_alv(
( tabname = 'SCARR'
fieldname = 'CARRID'
seltext_l = 'Carrier code'
col_pos = 1
outputlen = 20 )
( tabname = 'SPFLI'
fieldname = 'CONNID'
seltext_l = 'Connection ID'
col_pos = 2
outputlen = 20 ) ).
CALL FUNCTION 'REUSE_ALV_LIST_DISPLAY'
EXPORTING
it_fieldcat = it_fcat
TABLES
t_outtab = it_f.
In a simple ALV table, you don't have to fill the component TABNAME of the field catalog. TABNAME is only needed for the hierarchical-sequential lists (function module REUSE_ALV_HIERSEQ_LIST_DISPLAY for instance) which are an output of two tables.
If you omit it, or if you give the same value (any value) for all columns, you will get a correct output:
Code with the correction:
SELECT scarr~carrid, spfli~connid
FROM scarr INNER JOIN spfli ON scarr~carrid = spfli~carrid
INTO TABLE #DATA(it_f).
DATA(it_fcat) = VALUE slis_t_fieldcat_alv(
( " do not fill TABNAME // tabname = 'SCARR'
fieldname = 'CARRID'
seltext_l = 'Carrier code'
col_pos = 1
outputlen = 20 )
( " do not fill TABNAME // tabname = 'SPFLI'
fieldname = 'CONNID'
seltext_l = 'Connection ID'
col_pos = 2
outputlen = 20 ) ).
CALL FUNCTION 'REUSE_ALV_LIST_DISPLAY'
EXPORTING
it_fieldcat = it_fcat
TABLES
t_outtab = it_f.
EDIT: I see that you two already solved the problem in comments.
As Sandra wrote, you could try using cl_salv_table. It should look like this:
cl_salv_table=>factory(
* EXPORTING
* list_display = if_salv_c_bool_sap=>true
* r_container =
* container_name =
IMPORTING
r_salv_table = DATA(lr_alv)
CHANGING
t_table = it_f
).
lr_alv->display( ).

Assign values to dynamic structure

Need idea on the below codes, on how to simplify. Codes below works good but is there a way I could enhance or shorten the codes making it dynamic?
TYPES: BEGIN OF lty_dates,
yesterday TYPE string,
today TYPE string,
tomorrow TYPE string,
END OF lty_dates.
DATA: it_table TYPE TABLE OF lty_dates.
DO 3 TIMES.
CASE lv_count.
WHEN 1.
it_table[ 1 ]-zyesterday = 'Result Yesterday'.
it_table[ 2 ]-zyesterday = 'Result Yesterday'.
it_table[ 3 ]-zyesterday = 'Result Yesterday'.
WHEN 2.
it_table[ 1 ]-ztoday = 'Result Today'.
it_table[ 2 ]-ztoday = 'Result today'.
it_table[ 3 ]-ztoday = 'Result Today'.
WHEN 3.
it_table[ 1 ]-ztommorrow = 'Result Tomorrow'.
it_table[ 2 ]-ztommorrow = 'Result Tomorrow'.
it_table[ 3 ]-ztommorrow = 'Result Tomorrow'.
ENDCASE.
lv_count = lv_count + 1.
ENDDO.
My idea is something like the below pseudocodes. I would not want to perform CASE multiple times specially instances if fields for it_table would reach 100 (fields).
DO 3 TIMES.
ASSIGN 'ZTODAY' TO <dynamic_fieldname>.
it_table[ 1 ]-<dynamic_fieldname> = <dynamic_result>.
it_table[ 2 ]-<dynamic_fieldname> = <dynamic_result>.
it_table[ 3 ]-<dynamic_fieldname> = <dynamic_result>.
ENDDO.
Please help or light me up on this.
You could use the command ASSIGN COMPONENT compname OF STRUCTURE structure TO <field_symbol>.
TYPES: BEGIN OF lty_dates,
yesterday TYPE string,
today TYPE string,
tomorrow TYPE string,
END OF lty_dates.
TYPES: BEGIN OF lty_result ,
fieldname TYPE fieldname,
result TYPE string,
END OF lty_result .
FIELD-SYMBOLS <data> TYPE any .
DATA: lt_table TYPE TABLE OF lty_dates,
lt_result TYPE TABLE OF lty_result.
lt_result = VALUE #( ( fieldname = 'yesterday'
result = 'Result Yesterday' )
( fieldname = 'today'
result = 'Result Today' )
( fieldname = 'tomorrow'
result = 'Result Tomorrow' ) ).
lt_table = VALUE #( ( ) ( ) ( ) ). " 3 empty lines
LOOP AT lt_table ASSIGNING FIELD-SYMBOL(<table>) .
LOOP AT lt_result ASSIGNING FIELD-SYMBOL(<result>) .
ASSIGN COMPONENT <result>-fieldname OF STRUCTURE <table> TO <data> .
<data> = <result>-result.
ENDLOOP .
ENDLOOP .
Actually, your code creates this result table:
YESTERDAY TODAY TOMORROW
Result Yesterday Result Today Result Tomorrow
Result Yesterday Result today Result Tomorrow
Result Yesterday Result Today Result Tomorrow
Why not to use macro for this? Macro can perfectly fulfill your needs:
FIELD-SYMBOLS: <fvalue> TYPE ANY.
DEFINE put_values.
ASSIGN COMPONENT &1 OF STRUCTURE &2 TO <fvalue>.
<fvalue> = &3.
END-OF-DEFINITION.
it_table = VALUE #( ( ) ( ) ( ) ).
LOOP AT it_table ASSIGNING FIELD-SYMBOL(<fs_tab>).
put_values 'yesterday' <fs_tab> 'Result Yesterday'.
put_values 'today' <fs_tab> 'Result Today'.
put_values 'tomorrow' <fs_tab> 'Result Tomorrow'.
ENDLOOP.
This will work if the number if the loop iterations are equal to lines of itab (as in your code).

How to read the position value given the cost center

I want to read the position using this FM HRWPC_RPT_COSTCENTER_EVALPATH where the cost center is given.
There are 3 result tables. from which table I can read the position value ?
here how I call the FM:
DATA i_hrrootob TYPE TABLE OF hrrootob.
DATA w_hrrootob LIKE LINE OF i_hrrootob.
DATA i_object_tab TYPE TABLE OF objec.
DATA w_object_tab LIKE LINE OF i_object_tab.
data i_STRUC TYPE TABLE OF STRUC.
w_hrrootob-otype = 'K'.
w_hrrootob-objid = w_orgdata-costcenter_key-costcenter.
APPEND w_hrrootob TO i_hrrootob.
CALL FUNCTION 'HRWPC_RPT_COSTCENTER_EVALPATH'
EXPORTING
depth = 0
evpath = 'KOSTDIUP'
* PLVAR = 01
* BEGDA = SY-DATUM
* ENDDA = SY-DATUM
* LEVEL = 1
TABLES
root_objects = i_hrrootob
result_objec = i_object_tab
result_struc = i_STRUC
EXCEPTIONS
NO_OBJECTS_FOUND = 1
OTHERS = 2
.
I got it by myself.
The result table result_objec has the value in the field stext, where the obtype ='S'

Convert WHERE logic to SQL statement

Can someone help me transform the following code to raw SQL statement?
(NOT to Dynamic SQL)
Dim blnAllow as Boolean = True
Dim intType as Int32 = 35
.Append("SELECT * FROM TABLE1 WHERE NAME='AAA' ")
Select Case intType
Case 35
.Append("AND (Type IN (2,4) OR type=8) ")
.Append("AND [use]=1 ")
Case 34
If blnAllow = True Then
.Append("AND (Type IN (2,4) OR (type=8 and Col1 > 0 )) ")
Else
.Append("AND (Type IN (2,4)) ")
End If
.Append(" AND [use]=1 ")
Case Else
.Append("AND Type=1")
End Select
Well since intType is defined as 35, only the Case 35 section applies...
select * from TABLE1 where [NAME]='AAA'
and [Type] in (2,4,8)
and [use] = 1
If you want to encapsulate those other cases, you'll have to explain where intType fits in.. or do you just want 3 separate queries?
How about something like this
SELECT *
FROM TABLE1 WHERE NAME='AAA'
AND (
(
intType = 35
AND (Type IN (2,4) OR type=8)
AND [use]=1
)
OR
(
intType = 34
AND (
(
blnAllow = 'true'
AND (Type IN (2,4) OR (type=8 and Col1 > 0 ))
)
OR
(
blnAllow = 'false'
AND (Type IN (2,4))
)
)
AND [use]=1
)
OR
(
intType NOT IN (35, 34)
AND Type=1
)
)
The general converting pattern is
If condition Then ands1
Else ands2 End If
becomes
( (condition AND ands1) OR ((NOT condition) AND ands2) )
Please try this most optimized query.
select * from table where name = 'AAA' AND
(
(
((Type IN (2,4) OR type=8) OR // Case 35
(
(Type IN (2,4) OR (type=8 and Col1 > 0 )) // Case 34 and blnAllow checking
)
)
AND [use]=1 // Case 35 && 34
) OR
(Type=1) // Else
)
If the string "Type" and "type" indicates the same field, You just modifify the
//case 35 section to (Type IN (2,4) OR type=8) => (Type IN (2,4,8))
In MS SQL, this would look like this:
DECLARE #blnAllow BIT
SET #blnAllow = 1
DECLARE #intType INT
SET #intType = 35
SELECT *
FROM TABLE1
WHERE
NAME = 'AAA' AND
(
(#intType = 35 AND (Type IN (2,4) OR type = 8) AND [use] = 1) OR
(#intType = 34 AND [use] = 1 AND
(
(#blnAllow = 1 AND (Type IN (2,4) OR (type = 8 and Col1 > 0 ))) OR
(#blnAllow = 0 AND (Type IN (2,4)))
)) OR
(#intType not in (34, 35) AND Type = 1)
)
Don't expect query optimizer to optimize it :).
…
WHERE NAME = 'AAA'
AND (#intType NOT IN (34, 35) AND Type = 1
OR #intType IN (34, 35) AND [use] = 1 AND (
Type IN (2, 4)
OR #intType = 35 AND Type = 8
OR #intType = 34 AND (#blnAllow = 0 OR Type = 8 AND Col1 > 0)
)
)
It is assumed that #intType is an int parameter and #blnAllow is a bit parameter.

How to define a 2-Column Array A = 1, B = 2... ZZZ =?

I need to create a 2 column array in ABAP so that a program can look up a record item (defined by the letters A - ZZZ) and then return the number associated with it.
For example:
A = 1
B = 2
C = 3
...
Z = 26
AA = 27
AB = 28
...
AZ =
BA =
...
BZ =
CA =
...
...
ZZZ =
Please can you suggest how I can code this.
Is there a better option than writing an array?
Thanks.
you don't need to lookup the value in a table. this can be calculated:
parameters: p_input(3) type c value 'AAA'.
data: len type i value 0,
multiplier type i value 1,
result type i value 0,
idx type i.
* how many characters are there?
len = strlen( p_input ).
idx = len.
* compute the value for every char starting at the end
* in 'ABC' the C is multiplied with 1, the B with 26 and the A with 26^2
do len times.
* p_input+idx(1) should be the actual character and we look it up in sy-abcde
search p_input+idx(1) in SY-ABCDE.
* if p_input+idx(1) was A then sy-fdpos should now be set to 0 that is s why we add 1
compute result = result + ( sy-fdpos + 1 ) * multiplier.
idx = idx - 1.
multiplier = multiplier * 26.
enddo.
write: / result.
i didn't test the program and it has pretty sure some syntax errors. but the algorithm behind it should work.
perhaps I'm misunderstanding, but don't you want something like this?
type: begin of t_lookup,
rec_key type string,
value type i,
end of t_lookup.
data: it_lookup type hashed table of t_lookup with unique key rec_key.
then once it's populated, read it back
read table it_lookup with key rec_key = [value] assigning <s>.
if sy-subrc eq 0.
" got something
else.
" didn't
endif.
unfortunately, arrays don't exist in ABAP, but a hashed table is designed for this kind of lookup (fast access, unique keys).
DATA: STR TYPE STRING, I TYPE I, J TYPE I, K TYPE I, CH TYPE C, RES
TYPE INT2, FLAG TYPE I.
PARAMETERS: S(3).
START-OF-SELECTION.
I = STRLEN( S ).
STR = S.
DO I TIMES.
I = I - 1.
CH = S.
IF CH CO '1234567890.' OR CH CN SY-ABCDE.
FLAG = 0.
EXIT.
ELSE.
FLAG = 1.
ENDIF.
SEARCH SY-ABCDE FOR CH.
J = I.
K = 1.
WHILE J > 0.
K = K * 26.
J = J - 1.
ENDWHILE.
K = K * ( SY-FDPOS + 1 ).
RES = RES + K.
REPLACE SUBSTRING CH IN S WITH ''.
ENDDO.
* RES = RES + SY-FDPOS.
IF FLAG = 0.
MESSAGE 'String is not valid.' TYPE 'S'.
ELSE.
WRITE: /, RES .
ENDIF.
Use this code after executing.
I did a similar implementation some time back.
Check this it it works for you.
DATA:
lv_char TYPE char1,
lv_len TYPE i,
lv_len_minus_1 TYPE i,
lv_partial_index1 TYPE i,
lv_partial_index2 TYPE i,
lv_number TYPE i,
result_tab TYPE match_result_tab,
lv_col_index_substr TYPE string,
lv_result TYPE i.
FIELD-SYMBOLS:
<match> LIKE LINE OF result_tab.
lv_len = strlen( iv_col_index ) .
lv_char = iv_col_index(1).
FIND FIRST OCCURRENCE OF lv_char IN co_char RESULTS result_tab.
READ TABLE result_tab ASSIGNING <match> INDEX 1.
lv_number = <match>-offset .
lv_number = lv_number + 1 .
IF lv_len EQ 1.
ev_col = ( ( 26 ** ( lv_len - 1 ) ) * lv_number ) .
ELSE.
lv_len_minus_1 = lv_len - 1.
lv_col_index_substr = iv_col_index+1(lv_len_minus_1) .
CALL METHOD get_col_index
EXPORTING
iv_col_index = lv_col_index_substr
IMPORTING
ev_col = lv_partial_index2.
lv_partial_index1 = ( ( 26 ** ( lv_len - 1 ) ) * lv_number ) + lv_partial_index2 .
ev_col = lv_partial_index1 .
ENDIF.
Here The algorithm uses a recursive logic to determine the column index in numbers.
This is not my algorithm but have adapted to be used in ABAP.
The original algorithm is used in Open Excel, cant find any links right now.