How do I aggregate negative values with AT END OF and SUM? - sum

I´m working on a ABAP program, in which I have to do a validation for a specific field, like the code below:
SORT t_prcd BY knumh kschl.
LOOP AT t_prcd INTO wa_prcd WHERE knumh = wa_wcocoh-knumh AND kschl = wa_wcocoh-kschl.
IF wa_prcd-vbtyp = 'C'.
wa_prcd-netwr = wa_prcd-netwr * ( -1 ).
wa_prcd-kwmeng = wa_prcd-kwmeng * ( -1 ).
ENDIF.
AT END OF knumh.
SUM.
" SUBTRACT wa_prcd-netwr FROM wa_prcd-netwr.
MOVE EXACT wa_prcd-netwr TO wa_talv-val_vendido.
MOVE wa_prcd-kwmeng TO wa_talv-quant_vendido.
ENDAT.
ENDLOOP.
What I need to do is: if wa_prcd-vbtyp is C the value of wa_prcd-netwr must be negative.
In debugging I see the condition is working fine, but when it comes to SUM, it just does that without sign, which means that instead of sum (-A + -B), it does ( A + B )
Can somebody help me please? Thanks.

When you multiply with -1, you only update the local variable wa_prcd, but not the original table t_prcd. SUM however only takes into account what's in t_prcd.
The fix thus is simply to separate the update from the sum step:
SORT t_prcd BY knumh kschl.
" use a reference or field-symbol to update the table in-place
LOOP AT t_prcd REFERENCE INTO DATA(ref_prcd)
WHERE knumh = wa_wcocoh-knumh AND kschl = wa_wcocoh-kschl
AND vbtyp = 'C'.
ref_prcd->netwr = ref_prcd->netwr * ( -1 ).
ref_prcd->kwmeng = ref_prcd->kwmeng * ( -1 ).
ENDLOOP.
LOOP AT t_prcd INTO DATA(wa_prcd)
WHERE knumh = wa_wcocoh-knumh AND kschl = wa_wcocoh-kschl.
AT END OF knumh.
SUM.
MOVE EXACT wa_prcd-netwr TO wa_talv-val_vendido.
MOVE wa_prcd-kwmeng TO wa_talv-quant_vendido.
ENDAT.
ENDLOOP.

Related

How to check if the field TFK is maintained in the table

I am trying to display an error message for certain conditions. The goal is that from table COST, for an object number (that contains TEST + cost center + activity type), gjahr, value type(WRTTP) and version(VERSN), to check if for a certain fixed price per unit measure(TFK001-016) there is data in the table. Thus, if TFKXXX is not maintained in the COST table it will show an error message.
Now what I have done, is using a Call Function, to get the three number period for the TFK field, thus based on the exporting parameter of date/monmit/periv we will get the field of lv_poper which is the period. Then I have done a merge of TFK and lv_poper. Now what I want to do is to check whether a TFK001-016 field is maintained for the key parameters. I cannot do <ls_co_data>-lv_tfkxxx as it does not exist in the table COST. Does anyone have any idea on how can I check if the field TFK001-016 is maintained in the table COST?
CALL FUNCTION 'DATE_TO_PERIOD_CONVERT'
EXPORTING
i_date = lv_date
i_monmit = lv_monmit
i_periv = lv_periv
IMPORTING
e_buper = lv_poper
e_gjahr = lv_gjahr
EXCEPTIONS
input_false = 1
t009_notfound = 2
t009b_notfound = 3
OTHERS = 4.
lv_objnr = 'TEST' + <ls_co_data>-send_cctr + <ls_co_data>-acttype.
lv_tkfxxx = 'TKF' + lv_poper.
LOOP AT lt_cost ASSIGNING FIELD-SYMBOL(<ls_cost>)
WHERE objnr = lv_objnr
AND gjahr = lv_gjahr
AND wrttp = 1
AND versn = 0.
IF lv_tkfxxx IS NOT INITIAL. "The lv_tkfxxx should be checked in the cost table
lv_text = 'Not maintained in ' + lv_objnr + ' for the date ' + <ls_co_data>-postgdate.
ENDIF.
ENDLOOP.
Thank you all in advance!
...
LOOP AT lt_cost ASSIGNING FIELD-SYMBOL(<ls_cost>)
WHERE objnr = lv_objnr
AND gjahr = lv_gjahr
AND wrttp = 1
AND versn = 0.
ASSIGN COMPONENT lv_tkfxxx OF STRUCTURE <ls_cost> TO FIELD-SYMBOL(<v_tkfxxx>).
IF sy-subrc = 0 AND <v_tkfxxx> IS NOT INITIAL.
lv_text = 'Not maintained in ' + lv_objnr + ' for the date ' + <ls_co_data>-postgdate.
ENDIF.
ENDLOOP.

Loop in a Loop with index less than the stopped one

I have the following problem and I need an idea how to overcome?
I have 2 identical ITABs: ITAB1 and ITAB2 with 60 records.
I am looping in the 1st ITAB and when I am finding a record I am looping in the 2nd ITAB with INDEX = sy-tabix of the 1st one:
LOOP at ITAB1 where COL = '001'.
lv_tabix = sy-tabix.
* Do STH.
LOOP at ITAB2 FROM lv_tabix
* do sth
EXIT.
ENDCASE.
ENDCASE.
Lets suppose that I am looping the 2nd ITAB with lv_tabix = 17 and, I am exiting from the 2nd when its tabix=22.
So I am returning in the 1st ITAB do sth and, I am starting the loop of the 2nd ITAB with lv_tabix=21.
I have noticed that the loop of the 2nd ITAB cannot start from a record (21) which is less than the one it was stopped (22).
Am I right?
How can I overcome this problem?
Thanks
Elias
Cannot reproduce your problem.
DATA(table_1) = VALUE string_table( ( `A` ) ( `B` ) ( `C` ) ( `D` ) ).
DATA(table_2) = VALUE string_table( ( `A` ) ( `B` ) ( `C` ) ( `D` ) ).
LOOP AT table_1 INTO DATA(row_1).
DATA(start_index) = sy-tabix.
LOOP AT table_2 INTO DATA(row_2) FROM start_index.
IF row_2 = `C`.
EXIT.
ENDIF.
ENDLOOP.
ENDLOOP.
works fine, although in the first outer loop iteration it exits the inner loop at sy-tabix = 3 and in the second outer loop iteration restarts the inner loop with the lower start_index = 2.

Checkbox multiple selection filter

I want to filter the data into my program depending what checkbox is selected or not.
parameters: p_z1 as checkbox DEFAULT 'X' ,
p_z2 as checkbox.
selection-screen end of block b4.
So if one of these two is selected or if both are selected how can I filter my data?
select single * from mara where matnr = pv_matnr
and "if checkbox one is selected" where matkl = t023-matkl.
"if checkbox two is selected" where matkl = v023-matkl.
You can prepare dynamic where clause -
DATA : lv_query TYPE string.
CONCATENATE 'MATNR = PV_MATNR' lv_query INTO lv_query.
IF p_z1 = 'X'.
CONCATENATE lv_query 'AND MATKL = T023-MATKL' INTO lv_query SEPARATED BY space.
ENDIF.
IF p_z2 = 'X'.
CONCATENATE lv_query 'AND MATKL = V023-MATKL' INTO lv_query SEPARATED BY space.
ENDIF.
SELECT SINGLE * FROM mara WHERE (lv_query).
"Dynamic" queries are to be avoided as far as possible, just to make it easier to check the code against possible SQL injection attacks (with SELECT … WHERE (lv_query), there could be a Denial-of-Service attack with an empty lv_query empty). So I would write the code by divScorp as follows:
parameters: p_z1 as checkbox DEFAULT 'X' ,
p_z2 as checkbox,
pv_matnr TYPE mara-matnr.
DATA: t023 TYPE t023, v023 TYPE v023, mara TYPE mara.
DATA range_matkl TYPE RANGE OF mara-matkl.
CLEAR range_matkl.
IF p_z1 = 'X'.
APPEND VALUE #( sign = 'I' option = 'EQ' low = t023-matkl ) TO range_matkl.
ENDIF.
IF p_z2 = 'X'.
APPEND VALUE #( sign = 'I' option = 'EQ' low = v023-matkl ) TO range_matkl.
ENDIF.
SELECT SINGLE * FROM mara WHERE MATNR = PV_MATNR and matkl IN range_matkl.
PS: my code (and the one of divScorp) is non-sense because pureAbap algorithm is incorrect : in case p_z1 and p_z2 are both 'X', then there's the risk that the SELECT returns nothing if t023-matkl is different from v023-matkl. Maybe p_z1 and p_z2 should be converted into radio buttons? (only one of the two can be selected?)
You can always use:
IF chck1 = 'X' and chck2 = 'X'.
SELECT ... WHERE both.
ELSEIF chck1 = 'X'.
SELECT ... WHERE ...
ELSEIF chck2 = 'X'.
SELECT ... WHERE ...
ENDIF.
But I guess that's not what you wanted to do. Not the most elegant way but hey - it works.

Shortest notation to split ABAP internal table into smaller pieces

In ABAP, I have a pretty large internal table, say 31,000 rows. What's the shortest and most efficient way to split that into multiple smaller tables of fixed size, say 1,000 rows each?
Naive way would be:
DATA lt_next_package TYPE tt_table_type.
LOOP AT it_large_table INTO DATA(ls_row).
INSERT ls_row INTO TABLE lt_next_package.
IF lines( lt_next_package ) >= lc_package_size.
INSERT lt_next_package INTO TABLE rt_result.
CLEAR lt_next_package.
ENDIF.
ENDLOOP.
IF lt_next_package IS NOT INITIAL.
INSERT lt_next_packge INTO TABLE rt_result.
ENDIF.
That works and is rather efficient, but looks cumbersome, esp. the don't-forget-the-last-package section at the very end. I believe there must be a better way to do this with the newer ABAP mesh paths and table expressions, but so far couldn't come up with one.
I am not sure if, there is a right way to do it (sure, there are several ways to do it), but you can try this to overcome the last package problem:
WHILE it_large_table IS NOT INITIAL.
LOOP AT it_large_table ASSIGNING FIELD-SYMBOL(<ls_line>) FROM 1 TO 1000.
INSERT <ls_line> INTO TABLE lt_next_package.
ENDLOOP.
DELETE it_large_table FROM 1 TO 1000.
INSERT lt_next_package INTO TABLE rt_table.
CLEAR: lt_next_package.
ENDWHILE.
Based on JozsefSzikszai's answer, devised another option:
rt_result = VALUE #( FOR i = 1
UNTIL i > round( val = lines( it_large_table) / lc_package_size
dec = 0
mode = cl_abap_math=>round_up )
LET lv_end = i * lc_package_size
lv_start = lv_end - lc_package_size + 1 IN
( VALUE <result-type>(
( LINES OF it_large_table FROM lv_start TO lv_end ) ) ) ).
Somewhat reinvention of both Florian and Jozsef approaches.
Prerequisits:
TYPES:
BEGIN OF line,
rows TYPE string,
slice TYPE bseg_t,
END OF line,
itab TYPE STANDARD TABLE OF line WITH EMPTY KEY,
bseg_t TYPE STANDARD TABLE OF bseg WITH EMPTY KEY.
DATA: result TYPE itab.
Filling large table:
SELECT * UP TO 31000 ROWS
INTO TABLE #DATA(lt_bseg)
FROM bseg.
Here we costruct table of tables which contains slices of the main table by 1000 rows each.
WHILE lt_bseg IS NOT INITIAL.
result = VALUE itab( BASE result
(
rows = | { sy-index * 1000 }-{ sy-index * 1000 + 1000} |
slice = VALUE bseg_t( FOR wa IN lt_bseg INDEX INTO i FROM i + 1 TO i + 1
( LINES OF lt_bseg from i TO i + 999 ) )
)
).
DELETE lt_bseg FROM 1 TO 1000.
ENDWHILE.
Looks somewhat as our requirement, no?
Here are two ways to build a table of subtables AKA pagination:
METHOD prepare_data.
TYPES:
BEGIN OF line,
name TYPE string,
subset TYPE salv_t_row,
END OF line,
itab TYPE STANDARD TABLE OF line WITH EMPTY KEY.
CONSTANTS: lc_package_size TYPE i VALUE 3.
DATA: result TYPE itab,
it_rows TYPE salv_t_row.
DO round( val = lines( it_rows ) / lc_package_size
dec = 0
mode = cl_abap_math=>round_up ) TIMES.
DATA(lv_end) = sy-index * lc_package_size.
DATA(lv_start) = lv_end - lc_package_size + 1.
APPEND INITIAL LINE TO result ASSIGNING FIELD-SYMBOL(<subset>).
<subset>-name = | Subset { sy-index } |.
<subset>-subset = VALUE #( ( LINES OF it_rows FROM lv_start TO lv_end ) ).
ENDDO.
clear result.
result = VALUE itab( FOR i = 1
UNTIL i > round( val = lines( it_rows ) / lc_package_size
dec = 0
mode = cl_abap_math=>round_up )
LET k = 1 IN
(
name = | Subset { i } |
subset = VALUE salv_t_row(
LET
end = i * lc_package_size
start = end - lc_package_size + 1
IN
( LINES OF it_rows from start to end )
)
)
).
Hope this helps. The code here is tested and works. Just copy-paste (and generate some data).

calculated field using if and or in ms-sql

IF(
[dbo.tblx.Category] = 'WS' OR [dbo.tblx.Category] = 'SEM',
0,
[dbo.tblx.Tonnes] * [dbo.tblx.Grade] / 1000
)
this returns the entire text and not the "calculation"
can someone help me...what am I doing wrong?
i tried
CASE WHEN dbo.tblx.Category = 'WS' OR dbo.tblx.Category = 'SEM' THEN 0
ELSE dbo.tblx.Tonnes * dbo.tblx.Grade / 1000
END AS Metal
Do perhaps insert something before "CASE WHEN"? it is not running
As I understood, you are trying to create a computed column, using value from other table.
I think in this case you have to use a User Defined function in the formula for computed column. If you give more information I can try to write some code for you.
Instead, if you just want to add a column in a query you can use:
CASE WHEN dbo.tblx.Category = 'WS' OR dbo.tblPTx.Category = 'SEM' THEN 0
ELSE dbo.tblx.Tonnes * dbo.tblx.Grade / 1000 END AS NAME_OF_YOUR_NEW_COLUMN
Update
DECLARE #myvar INT;
SET #myvar = CASE WHEN ([dbo.tblx.Category] = WS OR [dbo.tblPTx.Category] = SEM) THEN 0
ELSE [dbo.tblx.Tonnes] * [dbo.tblx.Grade] / 1000
END