Count order lines by condition and calculate the percentage to total lines - abap

Experts,
Please let me know how to write the code in ABAP to implement the following logic?
From the below screenshot, for each "S_ORD_ITM", I have to determine if Order_Qty = Dlv_Qty. If yes, determine the total count of S_ORD_ITM for which Order_Qty = Dlv_Qty. In this example, for all 6 rows of S_ORD_ITM, Order_Qty = Dlv_Qty. So, this value would be 6. Lets says this as 'X' Next step is to find the total record count of S_ORD_ITM column. It is also 6 in this case. Lets says this as 'Y'.
My result should be [X/Y]*100.
In some cases, there could be total of 18 S_ORD_ITM, out of which only there exists only 6 records of S_ORD_ITM for which Ord_Qty = Dlv_Qty. So, my result would be [6/18]*100 = 33.33%
This logic has to be implemented for delivery numbers which have a first pass indicator as 'X'. Imagine this sales order has many delivery numbers, and the delivery number in this example is a first pass indicator with 'X'. I already have a loop statement in my end routine, that says
LOOP AT RESULT PACKAGE ASSIGNING RESULT FIELDS WHERE /BIC/FIRSTPASS = 'X'.
Please let me know how I can make use of this already available loop statement and implement the above logic.
Thanks a ton,
G.

UPDATE:
Hello Goutham,
You can solve the whole thing a lot easier. You just need to make a data flow from your DSO where the order data is, then you do a lookup. with that you loop through your result data and push just the extracted, aggregated rows in a new DSO. First build the target structure and the DSO and then use an expert routine / end routine with an abap coding like i described.
END UPDATE
so the Structure is like
sales_order, plant, shipping_point, delivery_number, s_ord_itm, ord_qty, dlv_qty
in your result package variable. is that correct? without a screenshot it is very hard to know what you mean, do you mean a SAP BW transformation or just ABAP code?
you could add some helper-variables to your structure or do it in the loop, i prefer doing it in the loop. but first you have to sort your result package!
your coding should be something like this (pseudo code) where your x variable is v_counter_ord_itm and v_counter_ord_dlv is your y:
make some data definitions like
WA_RESULT.../END OF... (build a workarea for sales_order, result)
T_RESULT (make an itab out of workarea)
WA (workarea with sales_order, counter_ord_itm, counter_ord_dlv)
PSEUDO-CODE!!!
SORT RESULT_PACKAGE BY /BIC/SALES_ORDER
WA-SALES_ORDER = 0.
WA-COUNTER_ORD_ITM = 0
WA-COUNTER_ORD_DLV = 0
LOOP AT RESULT PACKAGE ASSIGNING RESULT FIELDS WHERE /BIC/FIRSTPASS = 'X'.
IF WA-SALES_ORDER NE /BIC/SALES_ORDER.
IF WA-SALES_ORDER NE 0.
WA_RESULT-RESULT = WA-COUNTER_ORD_DLV / WA-COUNTER_ORD_ITM * 100.
WA_RESULT-SALES_ORDER = WA-SALES_ORDER.
APPEND WA_RESULT TO T_RESULT.
CLEAR WA, WA_RESULT.
ENDIF.
WA-SALES_ORDER = /BIC/SALES_ORDER.
ENDIF.
WA-COUNTER_ORD_ITM = WA-COUNTER_ORD_ITM + 1.
IF result_fields-ord_qty EQ result_fields-dlv_qty.
WA-COUNTER_ORD_DLV = WA-COUNTER_ORD_DLV + 1.
ENDIF.
ENDLOOP.
then you have the variables in your itab. for usage within data processing in sap bw, do another loop with a lookup to push the result data in a new field "result" (you have to add it in the output structure):
LOOP AT RESULT_PACKAGE ...
LOOP AT IT_RESULT ASSIGNING <z>
WHERE /BIC/SALES_ORDER = <z>-SALES_ORDER.
RESULT_PACKAGE-RESULT = <z>-RESULT.
ENDLOOP

This is the code that I used:
SELECT doc_number plant ship_point dsdel_date s_ord_item deliv_numb /bic/zlord_qty /bic/zldlv_qty
INTO CORRESPONDING FIELDS OF TABLE it_doc_table
FROM /bic/azord_dso00.
SELECT doc_number COUNT( DISTINCT s_ord_item ) AS numr
FROM /bic/azsd_o11000
INTO CORRESPONDING FIELDS OF TABLE it_count_table
GROUP BY doc_number.
READ TABLE lt_min_flag WITH KEY doc_number = source_fields-doc_number
plant = source_fields-plant
ship_point = source_fields-ship_point
deliv_numb = source_fields-deliv_numb
dsdel_date = source_fields-dsdel_date
INTO lt_min_flag_wa
BINARY SEARCH.
CHECK sy-subrc = 0. CLEAR result.
IT_DOC_TABLE = VALUE /bic/azord_dso00( FOR ls_doc IN it_doc_table WHERE ( doc_number = source_fields-doc_number AND plant = source_fields-plant AND ship_point = source_fields-ship_point AND deliv_numb = source_fields-deliv_numb AND dsdel_date = source_fields-dsdel_date AND /bic/zlord_qty = /bic/zldlv_qty ) ( ls_doc ) ).
z_numr = lines( it_doc_table ).
READ TABLE it_count_table INTO wa_count_table WITH KEY doc_number = source_fields-doc_number.
IF sy-subrc = 0 AND wa_count_table-numr <> 0.
result = ( z_numr / wa_count_table-numr ) * 100 .
ENDIF.

Related

Calculate Sum based on condition [duplicate]

This question already has an answer here:
loop with condition ignored by the system in sap abap
(1 answer)
Closed 7 months ago.
How to do summation in the internal table with condition?
For example, my itab have the following data:
itab l_bcode_i :
vgpos[1] :10 , menge[1]:600
vgpos[2] :10 , menge[2]:500
vgpos[3] :20 , menge[3]:400
vgpos[4] :20 , menge[4]:300
vgpos[5] :30 , menge[5]:200
vgpos[6] :30 , menge[6]:10
The requirement request to sum up the data with the same vgpos number by using the menge, and use to display another message.
I try to show
vgpos: 10 menge = 1100
vgpos:20 menge = 500
vgpos:30 menge = 210
I try to use loop and sum, however the result not as the result I need because it will total up all the menge value by ignored the where clause condition that I set. Any recommendation or suggestion is appreciated:
at last, it should menge = 1810
LOOP AT LT_BCODE_I INTO LT_BCODE_I WHERE VGPOS = LW_LIPS2-VGPOS.
AT LAST.
SUM.
cl_demo_output=>display( LT_BCODE_I-MENGE ).
ENDAT.
ENDLOOP.
SUM statement calculates the sum of Numeric Components(Numeric data types). Check and validate the data type for the field MENGE.
Read the documentation about SUM statement,
Also, there is the condition on the LOOP. So, loop executes only once, if VGPOS is considered as key.
Hi I able to fixed my problem by using the following code:
In stead of At Last, I change to use the AT END. After used the AT END, I able to get the result I need.
LOOP AT LT_BCODE_I INTO LT_BCODE_I WHERE VGBEL = LT_LIPS-VGBEL AND VGPOS = LT_LIPS-VGPOS.
AT END OF VGBEL.
SUM.
LT_BCODE_I-MENGE = LT_BCODE_I-MENGE .
ENDAT.
ENDLOOP.
You can find examples in the documentation of SUM.
Here is an excerpt for "Calculation of a sum with SUM at AT END OF. The lines of the respective current group level are evaluated":
DATA:
BEGIN OF wa,
col1 TYPE i,
col2 TYPE i,
END OF wa,
itab LIKE TABLE OF wa WITH EMPTY KEY.
itab = VALUE #( FOR i = 1 UNTIL i > 5
FOR j = 1 UNTIL j > i
( col1 = i col2 = j ) ).
LOOP AT itab INTO wa.
AT END OF col1.
SUM.
cl_demo_output=>write( wa ).
ENDAT.
ENDLOOP.
cl_demo_output=>display( wa ).

I don't understand how READ TABLE inside LOOP works

In my code I need to iterate inside a loop, and for each iteration in the loop I have to fill in the data whose values ​​are different in certain cells.
Reading the sap documentation I have come to the conclusion that what I need is to use a read table to fill for each iteration into a working area that later I'll treat.
I have declarated the following tables:
it_sap with the content of a join from VBRK and VBRP tables
wa_sap as working area
it_ext with content from an another join from an external database
wa_ext as working area.
This is how my loop looks actually like:
LOOP AT it_sap INTO wa_sap.
wa_sap-tipo_documento = wa_sap-xblnr+2(1). " Linea
tiket = wa_sap-xblnr+9(7).
creationyear = wa_sap-fkdat+0(4).
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
EXPORTING
input = tiket
IMPORTING
output = tiket.
CONCATENATE creationyear '/' wa_sap-vkorg wa_sap-xblnr+7(2) '/' tiket INTO wa_sap-codalb.
"CLEAR position.
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_OUTPUT'
EXPORTING
input = wa_sap-posnr
IMPORTING
output = position.
READ TABLE it_ext INTO wa_ext
WITH KEY codalb = wa_ext-codalb
tipo_documento = wa_ext-tipo_documento.
IF sy-subrc = 0.
wa_sap-import_total = wa_ext-import_total.
wa_sap-import = wa_ext-import.
wa_sap-price = wa_ext-price.
wa_sap-price_total = wa_ext-price_total.
wa_sap-disccount = wa_ext-disccount.
wa_sap-quantity = wa_ext-quantity.
MODIFY it_sap FROM wa_sap.
IF wa_sap-importe_total <> wa_sap-netwr. "AND position = 1 .
APPEND wa_sap TO it_results.
ENDIF.
ENDIF.
ENDLOOP.
How does it work? I understand that by using the conditional sy-subrc = 0 I can see if the previous statement gives true or false but I don't quite understand how it works by using READ instead SELECT steament.
Thank you guys!
LOOP AT iterates over all rows of an internal table.
READ TABLE retrieves at most one row from an internal table.
sy-subrc provides you details about how well the previous statement worked. Its values differ with the statement. In case of READ TABLE, it tells you whether a row was found (= 0) or not (<> 0). The IF sy-subrc = 0 after your READ TABLE thus means "if you found such a row".
It's hard to understand what your code is supposed to do and whether it does that correctly without some helpful sample data. As József Szikszai points out in the comments to your question, the READ TABLE's conditions rely on wa_ext, which is never filled in your sample code, so at first glance this looks like it's not working at all or your sample does not include some initialisation code.

Check for duplicated values in the internal table

How can I check the repetitive value in the "Form #" column?
I want to highlight it later as duplicate record.
LOOP AT ZVBELNEXTTAB WHERE werks IN werks.
ZVBELNEXTTAB_COPY-WERKS = ZVBELNEXTTAB-WERKS.
ZVBELNEXTTAB_COPY-MANDT = ZVBELNEXTTAB-MANDT.
ZVBELNEXTTAB_COPY-BUKRS = ZVBELNEXTTAB-BUKRS.
ZVBELNEXTTAB_COPY-VBELN = ZVBELNEXTTAB-VBELN.
ZVBELNEXTTAB_COPY-EVBELN = ZVBELNEXTTAB-EVBELN.
ZVBELNEXTTAB_COPY-FKDAT = ZVBELNEXTTAB-FKDAT.
ZVBELNEXTTAB_COPY-VBLSTAT = ZVBELNEXTTAB-VBLSTAT.
ZVBELNEXTTAB_COPY-ZPRN = ZVBELNEXTTAB-ZPRN.
ZVBELNEXTTAB_COPY-UNAME = ZVBELNEXTTAB-UNAME.
ZVBELNEXTTAB_COPY-TYPE = ZVBELNEXTTAB-TYPE.
curr = ZVBELNEXTTAB-EVBELN.
lv_tab = SY-TABIX + 1.
READ TABLE ZVBELNEXTTAB INDEX lv_tab.
next = ZVBELNEXTTAB-EVBELN.
IF curr GT next.
a = curr - next.
ELSE.
a = next - curr.
ENDIF.
IF a GT 1.
curr = curr + 1.
next = next - 1.
ZVBELNEXTTAB_COPY-MISSINGFROM = curr.
ZVBELNEXTTAB_COPY-MISSINGTO = next.
ELSE.
ZVBELNEXTTAB_COPY-MISSINGFROM = ''.
ZVBELNEXTTAB_COPY-MISSINGTO = ''.
ENDIF.
APPEND ZVBELNEXTTAB_COPY.
SORT ZVBELNEXTTAB_COPY BY EVBELN.
ENDLOOP.
ENDFORM.
I still trying to check the duplicate "Form #" column by using 1 dimensional array by looping them.
Use GROUP BY functionality during looping. You wanna extract duplicates based on comparison fields Company code, Plant, Form #, Sales Doc, Billing date, Username.
So you should write something like this:
TYPES tt_vbeln TYPE STANDARD TABLE OF vbeln WITH DEFAULT KEY.
DATA duplicates TYPE tt_vbeln.
LOOP AT ZVBELNEXTTAB INTO DATA(zvbeln)
GROUP BY ( BUKRS = zvbeln-BUKRS
WERKS = zvbeln-WERKS
VBELN = zvbeln-VBELN
EVBELN = zvbeln-EVBELN
FKDAT = zvbeln-FKDAT
UNAME = zvbeln-UNAME
size = GROUP SIZE )
ASCENDING REFERENCE INTO DATA(group_ref).
CHECK group_ref->*-size > 1. "extracting dups
duplicates = VALUE tt_vbeln( BASE duplicates FOR <form_num> IN GROUP group_ref ( <form_num> ) ).
* setting color
MODIFY duplicates FROM VALUE tt_vbeln( line_color = 'C410' ) TRANSPORTING line_color WHERE line_color IS INITIAL.
ENDLOOP.
That allows you to extract sets of duplicated values like this
By the way, in the above sample rows of blue dataset differ in fields Form # and Username, so my GROUP snippet won't actually work on them. You should adjust grouping fields accordingly, for example leave only VBELN field as grouping field.
Beforehand you should add field line_color to your structure where you will put color codes for duplicates datasets.
Good sample of conditional coloring an ALV resides here.

Error DBIF_RSQL_INVALID_RSQL CX_SY_OPEN_SQL_DB in program

I get the following error: DBIF_RSQL_INVALID_RSQL CX_SY_OPEN_SQL_DB (Open SQL command is too big. Condition WHERE of the Open SQL command contains too many conditions). The error points to the following line:
select * from Z3T_MAILS into table t_full_mail UP TO 250 ROWS where ID in r_mid and (p_dat_clause).
Other parts of code:
DATA: p_dat_clause type STRING,
t_full_mail type Z3TT_MAILS,
r_mid type range of Z3E_MAIL_ID.
<...>
if not NOT_READED is initial.
p_clause = 'ISREAD = '''''.
endif.
if DATETO is initial.
p_dateto = DATEFROM.
else.
p_dateto = DATETO.
endif.
if not DATEFROM is initial or not DATETO is initial.
concatenate 'SEND_DATE >= ''' DATEFROM ''' and SEND_DATE <= ''' p_dateto '''' into p_dat_clause.
endif.
<...>
if MAILS is supplied or BODY is supplied or p_dat_clause ne ''.
if not r_mid[] is initial.
select * from Z3T_MAILS into table t_full_mail UP TO 250 ROWS where ID in r_mid and (p_dat_clause).
endif.
endif.
I am new to ABAP and would appreciate any help!
That error happens when you use a range/select-option that has too many entries for the database to handle. The solution to this is always dependent on the use, but in any case you have to limit the number of entries in the range.
In your case you only want up to 250 rows from the database anyway. So, if R_MID is filled with all rows containing a single ID you can check the number of lines in it (LINES( R_MID ) ) and limit it to 250 if there are more than that. In most systems this would get rid of the error.
I'm just guessing here but your range r_mid probably has hundreds of lines that look like this:
r_mid-sign = 'I'.
r_mid-option = 'EQ'.
r_mid-low = '123123123'.
r_mid-high = ''.
So instead you could store those IDs in an internal table. You even might be able to use the internal table you're looping over to fill r_mid in the first place.
Your date variables on the other hand are actually well suited to be declared as a single range:
r_date-sign = 'I'.
r_date-option = 'BT'.
r_date-low = datefrom.
r_date-high = dateto.
Also note the documentation about ranges.
Finally you can write your query as follows:
SELECT *
FROM z3t_mails
INTO TABLE t_full_mail
FOR ALL ENTRIES IN lt_mid
WHERE id EQ lt_mid-id
AND send_date IN r_date.

How to get rows count of internal table in abap?

How do I get the row count of an internal table? I guess that I can loop on it. But there must be a saner way.
I don't know if it makes a difference but the code should run on 4.6c version.
There is also a built-in function for this task:
variable = lines( itab_name ).
Just like the "pure" ABAP syntax described by IronGoofy, the function "lines( )" writes the number of lines of table itab_name into the variable.
You can use the following function:
DESCRIBE TABLE <itab-Name> LINES <variable>
After the call, variable contains the number of rows of the internal table .
Beside the recommended
DESCRIBE TABLE <itab-Name> LINES <variable>
there is also the system variable SY-TFILL.
From documentation:
After the statements DESCRIBE TABLE, LOOP AT and READ TABLE, the number of rows of the accessed internal table.
Example script:
REPORT ytest.
DATA pf_exclude TYPE TABLE OF sy-ucomm WITH HEADER LINE.
START-OF-SELECTION.
APPEND '1' TO pf_exclude.
APPEND '2' TO pf_exclude.
APPEND '3' TO pf_exclude.
APPEND '4' TO pf_exclude.
WRITE: / 'sy-tfill = ', sy-tfill.
DESCRIBE TABLE pf_exclude.
WRITE: / 'sy-tfill = ', sy-tfill, 'after describe table'.
sy-tfill = 0. "Reset
READ TABLE pf_exclude INDEX 1 TRANSPORTING NO FIELDS.
WRITE: / 'sy-tfill = ', sy-tfill, 'after read table'.
sy-tfill = 0. "Reset
LOOP AT pf_exclude.
WRITE: / 'sy-tfill = ', sy-tfill, 'in loop with', pf_exclude.
sy-tfill = 0. "Reset
ENDLOOP.
The result:
sy-tfill = 0
sy-tfill = 4 after describe tabl
sy-tfill = 4 after read table
sy-tfill = 4 in loop with 1
sy-tfill = 0 in loop with 2
sy-tfill = 0 in loop with 3
sy-tfill = 0 in loop with 4
Please get attention of the value 0 for the 2nd entry: SY-TFILL is not updated with each step, only after the first loop.
I recommend the usage SY-TFILL only, if you need it direct after the READ(1)... If there are other commands between the READ and the usage of SY-TFILL, there is always the danger of a change of the system variable.
(1) or describe table.
DATA : V_LINES TYPE I. "declare variable
DESCRIBE TABLE <ITAB> LINES V_LINES. "get no of rows
WRITE:/ V_LINES. "display no of rows
Refreance:
http://www.sapnuts.com/courses/core-abap/internal-table-work-area.html
The functional module EM_GET_NUMBER_OF_ENTRIES will also provide the row count. It takes 1 parameter - the table name.
you can also use OPEN Sql to find the number of rows using the COUNT Grouping clause and also there is system field SY-LINCT to count the lines(ROWS) of your table.
if I understand your question correctly, you want to know the row number during a conditional loop over an internal table.
You can use the system variable sy-tabix if you work with internal tables. Please refer to the ABAP documentation if you need more information (especially the chapter on internal table processing).
Example:
LOOP AT itab INTO workarea
WHERE tablefield = value.
WRITE: 'This is row number ', sy-tabix.
ENDLOOP.
data: vcnt(4).
clear vcnt.
LOOP at itab WHERE value = '1'.
add 1 to vcnt.
ENDLOOP.
The answer will be 3. (vcnt = 3).
I don't think there is a SAP parameter for that kind of result. Though the code below will deliver.
LOOP AT intTab.
AT END OF value.
result = sy-tabix.
write result.
ENDAT.
ENDLOOP.