Quantcast
Channel: ABAP Development
Viewing all 948 articles
Browse latest View live

How to check SAP system URL?

$
0
0

This is not something new but i got to work on SAP system in my recent assignment where i wanted to access SAP system URL from SAP mobile platform. Here is my finding on this:

 

Prerequisite: You must be having SAP GUI Logon pad.

 

 

Steps:

 

1. Login to desired SAP system with valid credentials

 

2. Run SICF transaction

 

1.PNG

 

3. Execute or press F8

 

 

2.PNG

 

 

4. Expand default_host

 

 

3.PNG

 

 

5. look for ping under sap>bc

 

4.PNG

 

 

6.      Right click > Test Service

 

          Choose Allow this one time from the drop down

 

5.PNG

 

7. It will open in browser, asking for SAP system user id and password

 

6.PNG

 

After giving right credentials, you will see below message

 

7.PNG

 

URL would like below

 

http://localhost:8000/sap/bc/ping?sap-client=800

 

 

I hope this sharing would be helpful. Your comments and suggestions are most welcome.


Step by step process to restrict vendor payments under certain conditions in F110

$
0
0

Created by Venkat Reddy


Sample Scenario - If the date maintained in the vendor master is less than Payment Date then the payments made to that specific Vendor in the F110 run should not get paid.

 

 

Functional Configuration that needs to be done in transaction code - FIBF

Step-1 Product needs to be created.


Step-2 Under Process Module we have to assign the BTE-1830 and function module in which the logic is maintained to restrict the payments.


 

 

Technical Part

SAP Documentation of this function module -


 

 

Copy function module - SAMPLE_PROCESS_00001830 into Z.

Note: 'Z' Function module name should be the same as maintained in the config of process module in FIBF t-code.

Source Code


 

 

Code

* Local vaiable to hold gbdat
data :
lv_gbdat
type lfa1-gbdat.

loop at t_regup.
* Checking gbdat if it is less than payment date
select single gbdat
into lv_gbdat
from lfa1
where lifnr eq t_regup-lifnr.
if sy-subrc eq 0.
if lv_gbdat is not initial.
if lv_gbdat LT t_regup-LAUFD.
t_regup
-XIGNO = 'X'.
modify t_regup INDEX sy-tabix from t_regup transporting XIGNO.
clear: t_regup, lv_gbdat.
endif.
else.
endif.
else.
endif.
endloop.


Testing

For example I have 2 invoices to be paid for the vendors – 100214 and 102165.


 

I have maintained date(GBDAT) in vendor Master for vendor-100214


 

 

 

F110 RUN


 

Proposal Display

As date in LFA1-GBDAT for vendor 100214 is 22.10.2013 which is less than payment date that is 23.10.2013 so the payment for 100214 is stopped.



Proposal Log


 

Payment Advice in SOST

Payment Advice got generated for only right vendor which got paid that is 102165 only.


 

 

FBL1N Vendor 100214 is not paid but vendor 102165 is paid.


 

This explains how to restrict payments to Vendor under certain conditions. Good Luck..!!!

 

Thanks..!!! VEnk@

Change TEXTs in master data.

$
0
0

This blog explains how to find Text name,Text ID, language and text object from the master data and change it as per our requirement.

 

Finding Text name,Text ID, language and Text object :

 

Let us take an example of a customer from VD03 transaction.

1.JPG

 

Then go to, EXTRAS --> TEXTS.

2.JPG

 

Once the texts appear, double click on the text we want to change. Lets choose "Internal Note" for our case.

3.JPG

Once the "Internal note" is displayed, GOTO --> Header.

4.JPG

 

Now we can see the Text Header, where we can find,Text name,Text ID, language and Text object as below:

5.JPG

 

Changing the TEXTs :

 

We make use of function module "SAVE_TEXT" using Text name,Text ID, language and Text object to change the master data text.

And write a simple report as below:

 

***************************************************************************

REPORT  ZINTERNALNOTE_CHANGE.

 

data:

      lint_header TYPE STANDARD TABLE OF THEAD,

      lws_header TYPE  THEAD,

      lint_lines TYPE STANDARD TABLE OF tline,

      lws_lines TYPE  tline,

      lw_text TYPE thead-tdname.

 

START-OF-SELECTION.

 

lws_header-tdobject = 'KNVV'.                             "text object from above

lws_header-tdname = '0000472421SE100101'.      "text name from above

lws_header-tdid = 'Z008'.                                     "text ID from above

lws_header-tdspras = 'E'.                                    "text language from above

 

  lws_lines-TDFORMAT = '*'.

  lws_lines-TDLINE = 'text'.

 

  APPEND lws_lines to lint_lines.

 

  CALL FUNCTION 'SAVE_TEXT'             

    EXPORTING

      header                = lws_header

    SAVEMODE_DIRECT       = 'X'

    tables

      lines                 = lint_lines .

 

  IF sy-subrc = 0.

      write : 'Internal note text changed'.

  ENDIF.

 

***************************************************************************

 

Once the report is executed, we can see the change reflected as below :

6.JPG

 

Thanks,

Anil Supraj

SDB_ADBC - Power to the people...

$
0
0

Hi all,

 

Recently there was some mentions of package SDB_ADBC in passing.

 

Personally I was not aware of its existence so I ask some "older" ABAP programmers
and all I received was was a big null.....

 

package SDB_ADBC deals with native SQL access . Since it is native ALL the power of your local server is accesible.

 

So using program ADBC_QUERY as a guide I created a program that I hope I will be happy to share with you in the near future.

 

Note the highlighted yellow text

 

Capture.PNG

ABAP static analysis tool SQF

$
0
0

Recently my colleague Zhang, Harry has introduced one useful tool to me and I would like to share it with you. ABAP static analysis tool SQF is a static code analysis tool developed in package SUPPORT_QUERY_FRAMEWORK in software component SAP_BASIS.

It contains lots of handy tool or short cut to other system utility tools. The most attractive function which is worthy to put it into my toolset is the static code analysis.

1. use tcode SQF, double click on "Source code Analysis"

clipboard1.png

2. Maintain the ABAP object which you would like to do static analysis.

In this example it is function module CRM_PRODUCT_GETLIST2. Specify the object type as well, which could be found in table TADIR.

The Analysis Depth 6 means: for example in the implementation of the FM, it calls another FM or subroutine, these delegated calls will also be analyzedby the tool. Say FM calls A and A calls B, B calls C, C calls D, D calles E, E calls F and F calls G, depth = 6 means any further calls starting from F calls G will be ignored.

clipboard2.png

3. Click save button and it is automatically navigated back to SQF main view. Click F8 to execute.

clipboard3.png

The progress will be displayed in the bottom, the bigger size of depth specified, the more time the analysis will take.

clipboard4.png

After execution, the color of icon changes from white to blue, which means the analysis result is available.

clipboard5.png

4. The analyis results are categorized into four groups:

 

a. Call hierarchy, something like the one in SAT.

clipboard6.png

b. the table read access in static call. Those table read access done via dynamic coding will be listed in group d.

clipboard7.png

Compare with DB access list analyzed by runtime trace SAT, there are far more entries than the static one. This is not surprising, as in CRM product, the set type access is implemented in a highly dynamic way.

dy.png

c. interface call:

clipboard9 (1).png

d. Dynamic coding

clipboard10.png

Hiding Indicator Fields in Query Report.

$
0
0

Queries are mostly used by the functional to generate quick reports without using ABAP code. But the there are certain situations where the report cannot be generated directly through queries using joins. ABAP code comes in to picture in such situations.

 

In some queries we might have to display Quantity, Currency and total price etc., in output report. Then some additional indicator fields to display units are also automatically generated. These fields have to be either populated or have to be hidden in the output. This post deals with hiding such additional indicator fields.

 

In the below picture you can see the additional indicator field of Total Price which is not populated and has to be hidden.

 

First.PNG

 

First go to transaction SQ00 or SQ01. Select the query area (In Environment) either Standard or Global. Give the query name and click on change.

 

In the next screen select Basic List from the application tool bar.

 

Basic List.PNG

 

You will be taken to Query layout design screen. Go to the Data Fields windows in the right and expand the fields. Select you the field for which you want to hide the indicator field. In our case we are selecting Condition Base Value.

 

Data Fields.PNG

 

In the right window all the output fields will be displayed sequentially based on their properties. By clicking on them you will be able to see the field name and its properties in the Quick Viewer window in the left below data field’s window.

 

Selection.PNG

 

The properties of the fields can be defined here. Select the required radio button for the indicator field. Here we are disabling the currency indicator field. Similarly you can set the field position to display to the right or left.

 

Properties.PNG

 

Now activate the query and test it you can see that the field is hidden.

 

Field Diabled.PNG

 

Hope this is helpful.

BDC for Recipe Creation

$
0
0

Its a year of BDC for me....

 

this is my first Blog ..

 

In this Blog i'm sharing how to create Recipe using BDC and  issues which I faced while creating BDC for Recipe Creation C201.

 

Before recipe creation we need to know about BOM and its creation

 

In my previous Document I have explained in detail about BOM and its creation

 

http://scn.sap.com/docs/DOC-49994

 

Recipe Creation



Definition :



Recipe is nothing but planning of manufacturing of products and Recipe is a collection resources .



Recipe is a collection of resources with well planned for manufacturing a product .

 

Recipe is basis for Product costing as well as reference for Process Orders.

 

Recipe will be created for a material and Plant for which BOM already created , moreover BOM is like reference for Recipe

 

Initial Screen

1.png

 

Parts of Recipe :

 

1.Recipe Group

 

It is unique Key to identify Recipe Group .

 

2.Recipe

 

It is a key to differentiate Recipe among Master recipe Group.

 

3.Material and Plant

 

Material and Plant are the reference for Recipe Creation .

 

4. Production Version

 

It is a relation between BOM Header and Recipe based on Alt BOM used .

 

Production version is is used to define different methods of Production through which a material can be produced.


Second Screen : Recipe Header


2.png


5. Status :

 

Status is used to identify the processing status of Recipe .

 

3.png

 

In general Released status is preferred for Task list status.

 

For Cost BOM , status 3 is preferred.

 

6. Usage 

 

Usage is used to indicate for what purpose recipe is used, for ex like recipe used for production or review ..,

 

Usage values are Organisation dependent .

 

7.Planner Group

 

Planner Group is used for Recipe maintenance .

 

Planner Group values are changed as per Organisation Requirement .

 

Third Screen : Operations

 

2.png

 

Standard Values

 

Standard values are used in customization of work center

 

3.png

 

8.Operation/Activity Number

 

It is key which identifies the sequence of operations .

 

In general the Operations are in sequence of 0010,0020...,

 

9.Phase Indicator /SUB Operation/Destination

 

In order to maintain relation between Operations we need to create SUB Operations .

 

Three values need to provided to create sub operations :

 

a.Phase Indicator

b.Sub Operation

c. Destination

 

a.Phase indicator


It is a checkbox , which  is used to indicate whether the operation is sub operation or not .

 

b.Sub Operation :

 

It is used to define for which Operation Sub Operation need to be Assigned .

 

2.png

In the above example , 0010 is Operation and 0020 is sub operation .

 

c.Destination

 

Destination is used to identify Recipe Destination with in plant

 

10.Resource

 

It is the center of recipe creation and it is a key to identify resource in a plant.

 

11.control Key

 

It is use to define which transactions should be executed for Recipe like scheduling or costing ..,

 

 

Create Relationship between Operations

 

To create relation between operations No. of Operations Should be Atleast 4 .

 

 

How to create ?

select all Operations by clicking on select all Button 2.png

 

then click on Generate Relation Button at the bottom 2.png to generate relations

 

 

 

Fourth Screen : Materials

 

before entering 4th Screen we need to create Production version and assign Alt BOM to Production Version .

 

On click of Materials Tab new  popup will appear with Production version as shown below :

 

 

2.png

 

  • In the above screen Mandatory fields are Production version Number , Alt BOM and BOM usage .
  • Here Alt BOM plays key role to link Recipe with BOM .
  • Production version number should be in sequence like 0001,0002....
  • After assigning necessary values , Press check button to activate Production Version, then click on back and close the pop up .

 

5.png


 

Assign Phases to BOM Items

 

Based on the requirement assign Phases created in Operation Tab to Particular BOM Items by selecting  Materials and by clicking on the create assignment  button at the Bottom .

 

A new POP up will appear to assign Phases as shown below :

 

5.png

 

After assigning phases to the necessary BOM Components as below , save the recipe which will create recipe .

 

 

2.png

 

In the above screen if we click on BOM Icon , it will enroute to BOM details .

 

 

BDC for Recipe Creation


Let me introduce the major Issues which i have faced with creating BDC for Recipe Creation

 

1. Page Down Issue

 

2. Selection and deselection of Operations

 

BDC code for Recipe Creation Tcode - C201

 

The Major problem which I faced while creating recipe with more than 10 records is Page Down error .

 

what is Page Down Error ?

 

After entering in to second page or more than that suddenly cursor moves to fist page while filling details in second page .

 

when it occurs ?

 

This Issue I faced at two places

 

1. after entering Standard values (1st standard value, 2nd standard value ...)

2.After entering Charge Qty in standard values  screen .

 

 

what is charge Qty , when it is calculated ?

 

  Charge Qty is division of base Qty by operation Qty and it is calculated for Resources with different UOM from Base UOM.

 

3.png


In the current scenario by default system allowing to enter 10 records w/o page down , for morethan 10 records I have decided Divided entire BDC program of recipe creation into 3 sections :


1. Enter all the Resources

2.Assign Standard values if entered  for the Resources

3.Assign Classification Class and Additional resources.

 

What is Classification class ?

 

According to the Requirement we can assign differentiate similar group of recipe's based on Classification class.

 

How to assign Classification class to particular Operation ?

 

step1 : select created operation

step 2 : Click on resource selection criteria as shown below

 

3.png

 

step3 : fill the classification filed with necessary class name and assign the class

 

 

3.png

 

Assign Additional resources :

 

Additional resc. are nothing but Recipe-specific values are assigned  to the characteristics of this class.


After entering classification class , Additional resources Tab will be enabled as shown below.

 

4.png

 

Solution

 

So finally I divided my entire BDC programming into 3 sections as mentioned above

 

sample Scenario :

 

Create Recipe for material BOM with charge QTy and Additional resources

 

Tcode for Recipe Creation C201 .

 

Step 1: create Recording through SHDB t.code and save it

 

 

step 2 : Create a Report in se38 and define Required declarations

 

Step 3 : replace all the Hard coded values with variables .

 

*---------------------------------------------------------------------------------------------*

*                         BDC for Recipe Creation                                                   *

*---------------------------------------------------------------------------------------------*

 

  DATA : lv_matnr TYPE rc27m-matnr,
lv_stlal
TYPE mast-stlal,
lv_verid
TYPE mkal-verid,
lv_phseq
TYPE plpo-phseq,
lv_losbs
TYPE char13,
lv_bstma
TYPE char13,
lv_bstmi
TYPE char13,
gd_msg  
TYPE string,
lv_date 
TYPE rn1datum-datex,
lv_slno 
TYPE zsl_no,
l_msg   
TYPE bapi_msg,

lit_bom_item
TYPE TABLE OF zsbom_items,
wa_bom_item 
TYPE zsbom_items,

lit_mkal
TYPE STANDARD TABLE OF  mkal,
wa_mkal 
TYPE mkal,

lit_plko
TYPE TABLE OF plko,
wa_plko 
TYPE plko,

lit_plpo
TYPE TABLE OF plpo,
wa_plpo
TYPE plpo,

lit_inob
TYPE TABLE OF inob,
wa_inob 
TYPE inob,

lit_ausp
TYPE TABLE OF ausp,
wa_ausp 
TYPE ausp,

lit_return
TYPE TABLE OF bapiret2,
wa_return 
TYPE bapiret2,

lit_hist_msg 
TYPE TABLE OF zthist_msgs,
wa_hist_msg  
TYPE zthist_msgs,

lv_plnnr
TYPE rc271-plnnr,
lv_plnal
TYPE rc271-plnal,
lv_losvn
TYPE rc271-losvn,
lv_stlan
TYPE mast-stlan,
lv_len  
TYPE i,
lv_lines
TYPE i,

lv_phassign 
TYPE zsbom_items-zph_assign,
lv_text     
TYPE char40,
lv_maktx    
TYPE maktx,
lv_prod_desc
TYPE maktx,
lv_plnkn    
TYPE plnkn,
lv_objek    
TYPE inob-objek,
lv_cuobj    
TYPE inob-cuobj,


lv_chrg_qty
TYPE string,
lv_temp    
TYPE n,
lv_res
(2)   TYPE n,
lv_pgdwn   
TYPE i,
lv_pgdwn1  
TYPE i,
lv_index   
TYPE i,
lv_index1  
TYPE i,
lv_index2  
TYPE i,
lv_lst_ind
(4TYPE n,
lv_flg
(1)      TYPE c,
lv_ph_sel
(4)   TYPE n,

lit_recipe_head_rep
TYPE TABLE OF ztrecipe_h_rep,
wa_recipe_head_rep 
TYPE ztrecipe_h_rep,
wa_recipe_head_rep1
TYPE ztrecipe_h_rep,

lit_recipe_itm_rep 
TYPE TABLE OF ztrecipe_i_rep,
wa_recipe_itm_rep  
TYPE ztrecipe_i_rep,
wa_recipe_itm_rep1 
TYPE ztrecipe_i_rep,


gd_num        
TYPE i,             "operation tab
gd_num1       
TYPE i,
gd_num2       
TYPE i,
gd_cnt
(2)      TYPE c,
gd_cnt1
(2)     TYPE c,
gd_arbpl
(25)   TYPE c,
gd_ltxa1
(25)   TYPE c,
gd_phflg
(25)   TYPE c,
gd_pvznr
(25)   TYPE c,
gd_phseq
(25)   TYPE c,
gd_steus
(25)   TYPE c,
gd_ltxa1_p
(25) TYPE c,
gd_vgw01
(25)   TYPE c,
gd_vgw02
(25)   TYPE c,
gd_vgw03
(25)   TYPE c,
gd_vgw04
(25)   TYPE c,
gd_vgw05
(25)   TYPE c,
gd_vgw06
(25)   TYPE c,
gd_vge06
(25)   TYPE c,
gd_vornr
(25)   TYPE c,
gd_flg
(25)     TYPE c,
gd_bsmch
(25)   TYPE c,
gd_qty_tmp
(14) TYPE c,
gd_atwrt
(25)   TYPE c,
gd_meinh
(25)   TYPE c,
gd_vge
(25)     TYPE c,
gd_chrg_qty   
TYPE string,

lr_bom
TYPE REF TO zcl_bom.

DATAld_num       TYPE i,
ld_num1     
TYPE i,
ld_tmp1
(25TYPE c,
ld_cnt
(2)    TYPE c,
ld_idnrk
(25) TYPE c,
ld_vornr
(25) TYPE c,
ld_flg
(25)   TYPE c,
ld_cnt1
(4)   TYPE n,
ld_cnt2
(4)    TYPE n,
ld_cnt3
(4)   TYPE n,
ld_cnt4
(4)   TYPE n,
lv_tabix    
TYPE i,
lv_count
(4TYPE n,
ld_tmp_m    
TYPE i,
ld_tmp_n    
TYPE i.


TYPES : BEGIN OF ty_class,
atinn
TYPE cabn-atinn,
END OF ty_class.

DATA : wa_class TYPE rmslv_char_class.



*-->Processing


lr_bom
= zcl_bom=>get_instance( ).


*------------------------------------------------------*
*                 CONVERSIONS                       *
*------------------------------------------------------*
CALL FUNCTION 'FORMAT_DATE_4_OUTPUT'
EXPORTING
datin 
= sy-datum
format = 'DD.MM.YYYY'
IMPORTING
datex 
= lv_date.

lv_matnr 
= im_recipe_h-matnr.
lv_verid 
= im_recipe_h-verid.   "verid


CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'   "Convert Material
EXPORTING
input  = lv_matnr
IMPORTING
output = lv_matnr
.


*-->get corresponding data

MOVE-CORRESPONDING im_recipe_h TO wa_recipe_h.

*-->get material description

SELECT SINGLE maktx FROM makt INTO wa_recipe_h-maktx WHERE matnr = lv_matnr
.

lv_werks = wa_recipe_h-werks.
lv_prof 
= wa_recipe_h-profidnetz. "pi01
lv_stat 
= wa_recipe_h-statu." wa_recipe_h-statu.
lv_verwe
= wa_recipe_h-verwe."wa_recipe_h-verwe.
lv_vagrp
= wa_recipe_h-vagrp.
lv_bmsch
= wa_recipe_h-bmsch.
lv_ktext
= wa_recipe_h-ktext.
lv_plnme
= wa_recipe_h-plnme.
lv_meinh
= wa_recipe_h-meinh.
lv_maktx
= wa_recipe_h-maktx.
lv_losbs
= zif_d_constant=>c_losbs"to lot size
lv_losvn
= zif_d_constant=>c_losvn.
lv_phseq
= zif_d_constant=>c_phseq.

IF wa_recipe_h-werks CP '10*' .
lv_prod_desc
= wa_recipe_h-maktx.
ELSEIF wa_recipe_h-werks CP '20*'.
lv_prod_desc
= wa_recipe_h-mpr_num.
ENDIF
.

CLEAR wa_ctu_options.
wa_ctu_options
-dismode = 'N'.
wa_ctu_options
-updmode = 'S'.
wa_ctu_options
-defsize = 'X'
.


*-------------------------------------------------------------*
*      FROM LOT SIZE AND TO LOT SIZE           *
*-------------------------------------------------------------*

IF im_recipe_h-werks CP '10*'.

lv_bstmi
= im_recipe_h-bmsch"base quantity
lv_bstma
= im_recipe_h-bmsch.
CONDENSE lv_bstma.
CONDENSE lv_bstmi.
ELSE
.


lv_bstmi
= '1'.
lv_bstma
= zif_d_constant=>c_bstma.
CONDENSE lv_bstma.
CONDENSE lv_bstmi.
ENDIF
.
*----------------------------------------------*
*                VALIDATIONS                *
*----------------------------------------------*


*-->Validate Material number AND Get Alt BOM

SELECT  matnr
werks
stlan
stlal
stlnr
FROM mast
INTO TABLE lit_mast
WHERE matnr = lv_matnr AND werks = lv_werks.

SORT lit_mast[] BY stlal DESCENDING.

READ TABLE lit_mast INTO wa_mast INDEX 1.
CLEAR lv_stlal.

IF wa_mast-stlnr IS NOT INITIAL.
lv_stlal
= wa_mast-stlal.
lv_stlan
= wa_mast-stlan.

ENDIF.

CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
EXPORTING
input  = lv_verid
IMPORTING
output = lv_verid.

REFRESH lit_bdcdata.
REFRESH lit_msgtab
.


*&---------------------------------------------------------------------*
*&                     BDC Recording
*&---------------------------------------------------------------------*
*          TCode : C201 - Create Master Recipe
*----------------------------------------------------------------------*

*-->INITIAL SCREEN


PERFORM bdc_dynpro      USING 'SAPLCPDI' '4000'.
PERFORM bdc_field       USING 'BDC_CURSOR'
'RC271-PROFIDNETZ'.
PERFORM bdc_field       USING 'PLKOD-PLNAL'
lv_plnnr
.
PERFORM bdc_field       USING 'PLKOD-PLNAL'
lv_plnal
.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=TON'.
PERFORM bdc_field       USING 'PLKOD-PLNAL'
' '."lv_plnnr'.
PERFORM bdc_field       USING 'PLKOD-PLNAL'
' '. "lv_plnal.
PERFORM bdc_field       USING 'RC27M-MATNR'
lv_matnr
.
PERFORM bdc_field       USING 'RC27M-WERKS'
lv_werks
.
PERFORM bdc_field       USING 'RC271-PROFIDNETZ'
'PI01'.
PERFORM bdc_field       USING 'RC271-STTAG'
lv_date
.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=TON'.

*-->SECOND SCREEN : Recipe Header


PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'/00'.
PERFORM bdc_field       USING 'PLKOD-PLNAL'
' '.
PERFORM bdc_field       USING 'PLKOD-KTEXT'
wa_recipe_h
-maktx.
PERFORM bdc_field       USING 'PLKOD-WERKS'
lv_werks
.
PERFORM bdc_field       USING 'BDC_CURSOR'
'PLKOD-VAGRP'.
PERFORM bdc_field       USING 'PLKOD-STATU'
lv_stat
.
PERFORM bdc_field       USING 'PLKOD-VERWE'
lv_verwe
.
PERFORM bdc_field       USING 'PLKOD-VAGRP'
lv_vagrp
.
CONDENSE lv_losbs.
PERFORM bdc_field       USING 'PLKOD-LOSBS'
lv_losbs
.
PERFORM bdc_field       USING 'PLKOD-LOSVN'
lv_losvn
.
PERFORM bdc_field       USING 'PLKOD-PLNME'
lv_plnme
.
PERFORM bdc_field       USING 'PLKOD-MEINH'
lv_plnme
.

gd_qty_tmp
= lv_bmsch.

CONDENSE gd_qty_tmp.
PERFORM bdc_field       USING 'PLKOD-BMSCH'
gd_qty_tmp
.


*-----------------------------------------------------------*
*-----------------Operation Tab-----------------------*
*-----------------------------------------------------------*

CLEAR : gd_bsmch,
                wa_recipe_i
.

SORT im_recipe_i BY vornr ASCENDING.

DESCRIBE TABLE im_recipe_i LINES gd_opt1.


*---define no of rows for page down in operation tab------*


LOOP AT im_recipe_i INTO wa_recipe_i .

gd_opt1 = sy-tabix.
gd_opt 
= gd_opt + 1 .

IF  sy-tabix > 10 .
IF gd_opt1 MOD 2 = 0.

PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=P+'.
PERFORM bdc_field       USING 'BDC_CURSOR'
'PLPOD-VORNR(01)'.
ELSE.
DO ( gd_opt1 - 1 DIV 10 TIMES .
PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=P+'.
PERFORM bdc_field       USING 'BDC_CURSOR'
'PLPOD-VORNR(01)'.
ENDDO.
ENDIF
.


*-->Determine Page Down for Phases Selected.


IF wa_recipe_i-phflg EQ 'X'.

IF ( gd_opt1 MOD 10 ) = 0.
gd_opt
= 02.
ELSE.
gd_opt
= 02.
ENDIF.
ELSE.

IF lv_flg NE 'X'.
gd_opt
= 02.
ELSE.
gd_opt
= gd_opt1 MOD 10.

IF  gd_opt = 1" Incase of First Record of Every Page
gd_opt
= 02.
ENDIF.
ENDIF.
ENDIF.

ENDIF
.

*--End of Page Down Determination


*--Section 1 : Enter all Items

*--> Recipe Items

PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
                                                   
'/00'.
CLEAR gd_phflg.


CONCATENATE 'PLPOD-PHFLG(' gd_opt ')' INTO gd_phflg.
CONDENSE gd_phflg.

PERFORM bdc_field       USING    gd_phflg
                                                      wa_recipe_i
-phflg.
CLEAR gd_pvznr.
CONCATENATE 'PLPOD-PVZNR(' gd_opt ')' INTO gd_pvznr.

CONDENSE gd_pvznr.


PERFORM bdc_field    USING gd_pvznr wa_recipe_i-pvznr"Superordinate Operation

CLEAR gd_phseq.
CONCATENATE 'PLPOD-PHSEQ(' gd_opt ')' INTO gd_phseq.
CONDENSE gd_phseq.
PERFORM bdc_field       USING gd_phseq  wa_recipe_i-phseq"Control Recipe Destination

CLEAR gd_vornr.
CONCATENATE 'PLPOD-VORNR(' gd_opt ')' INTO gd_vornr.
CONDENSE gd_vornr.
PERFORM bdc_field       USING gd_vornr  wa_recipe_i-vornr.

CLEAR gd_arbpl.
CONCATENATE 'PLPOD-ARBPL(' gd_opt ')' INTO gd_arbpl.
CONDENSE gd_arbpl.
PERFORM bdc_field       USING gd_arbpl  wa_recipe_i-arbpl.   " Resource

CLEAR gd_steus.
CONCATENATE 'PLPOD-STEUS(' gd_opt ')' INTO gd_steus.
CONDENSE gd_steus.
PERFORM bdc_field       USING gd_steus wa_recipe_i-steus. " Control key

CLEAR gd_ltxa1_p.
CONCATENATE 'PLPOD-LTXA1(' gd_opt ')' INTO gd_ltxa1_p.
CONDENSE gd_ltxa1_p.
PERFORM bdc_field       USING gd_ltxa1_p wa_recipe_i-ltxa1" Operation short text

CONCATENATE 'PLPOD-BMSCH(' gd_opt ')' INTO gd_bsmch.    "Recipe Item  Quantity
CONDENSE gd_bsmch.
CLEAR gd_qty_tmp.
gd_qty_tmp
= wa_recipe_i-bmsch .

CONDENSE gd_qty_tmp.

PERFORM bdc_field       USING  gd_bsmch  gd_qty_tmp.

CLEAR gd_meinh.
CONCATENATE 'PLPOD-MEINH(' gd_opt ')' INTO gd_meinh.    "Item  UOM
CONDENSE gd_meinh.
PERFORM bdc_field       USING 'BDC_CURSOR' gd_meinh.
PERFORM bdc_field       USING  gd_meinh  wa_recipe_i-meinh.

PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'/00'.


*------------------------------------------------------------------------*
**    Get Standard value key based on resource                    *
*    - Determine no of standard key values to be enabled       *
*------------------------------------------------------------------------*

IF wa_recipe_i-phflg = 'X'.

DATA : lit_crhd TYPE TABLE OF crhd,
wa_crhd 
TYPE crhd.

SELECT SINGLE *
FROM crhd INTO wa_crhd WHERE arbpl = wa_recipe_i-arbpl
AND  werks = im_recipe_h-werks.

IF wa_recipe_i-vgw01 IS NOT INITIAL.

CLEAR gd_vgw01.
CONCATENATE 'PLPOD-VGW01(' gd_opt ')' INTO gd_vgw01.
CONDENSE gd_vgw01.
CLEAR gd_qty_tmp.
gd_qty_tmp
= wa_recipe_i-vgw01.

PERFORM bdc_field       USING 'BDC_CURSOR'
gd_vgw01
.
PERFORM bdc_field       USING 'RC27X-ENTRY_ACT'
'1'.
gd_qty_tmp
= wa_recipe_i-vgw01.
CONDENSE gd_qty_tmp.

PERFORM bdc_field       USING  gd_vgw01
gd_qty_tmp
.

CLEAR gd_qty_tmp.

"Standard Key UOM
IF wa_crhd-vge01 IS NOT INITIAL.
CLEAR gd_vge.
CONCATENATE 'PLPOD-VGE01(' gd_opt ')' INTO gd_vge.
PERFORM bdc_field       USING 'BDC_CURSOR'
gd_vge
.

CALL FUNCTION 'CONVERSION_EXIT_CUNIT_OUTPUT'
EXPORTING
input    = wa_crhd-vge01
language = sy-langu
IMPORTING
output   = wa_crhd-vge01.


PERFORM bdc_field       USING  gd_vge
wa_crhd
-vge01.
ENDIF.

ENDIF.

IF wa_recipe_i-vgw02 IS NOT INITIAL.
CLEAR gd_vgw02.
CONCATENATE 'PLPOD-VGW02(' gd_opt ')' INTO gd_vgw02.
CONDENSE gd_vgw02.
CLEAR gd_qty_tmp.
gd_qty_tmp
= wa_recipe_i-vgw02.
CONDENSE gd_qty_tmp.


PERFORM bdc_field       USING gd_vgw02
gd_qty_tmp
.

IF wa_crhd-vge02 IS NOT INITIAL.
CLEAR gd_vge.
CONCATENATE 'PLPOD-VGE02(' gd_opt ')' INTO gd_vge.
PERFORM bdc_field       USING 'BDC_CURSOR'
gd_vge
.

CALL FUNCTION 'CONVERSION_EXIT_CUNIT_OUTPUT'
EXPORTING
input    = wa_crhd-vge02
language = sy-langu
IMPORTING
output   = wa_crhd-vge02.

PERFORM bdc_field       USING  gd_vge
wa_crhd
-vge02.
ENDIF.
ENDIF.

IF wa_recipe_i-vgw03 IS NOT INITIAL.
CLEAR gd_vgw03.
CONCATENATE 'PLPOD-VGW03(' gd_opt ')' INTO gd_vgw03.
CONDENSE gd_vgw03.
CLEAR gd_qty_tmp.
gd_qty_tmp
= wa_recipe_i-vgw03.
CONDENSE gd_qty_tmp.

PERFORM bdc_field       USING  gd_vgw03
gd_qty_tmp
.

IF wa_crhd-vge03 IS NOT INITIAL.
CLEAR gd_vge.
CONCATENATE 'PLPOD-VGE03(' gd_opt ')' INTO gd_vge.
PERFORM bdc_field       USING 'BDC_CURSOR'
gd_vge
.

CALL FUNCTION 'CONVERSION_EXIT_CUNIT_OUTPUT'
EXPORTING
input    = wa_crhd-vge03
language = sy-langu
IMPORTING
output   = wa_crhd-vge03.


PERFORM bdc_field       USING  gd_vge
wa_crhd
-vge03.
ENDIF.
ENDIF.

IF wa_recipe_i-vgw04 IS NOT INITIAL.
CLEAR gd_vgw04.
CONCATENATE 'PLPOD-VGW04(' gd_opt ')' INTO gd_vgw04.
CONDENSE gd_vgw04.
CLEAR gd_qty_tmp.
gd_qty_tmp
= wa_recipe_i-vgw04.
CONDENSE gd_qty_tmp.


PERFORM bdc_field       USING gd_vgw04
gd_qty_tmp
.

"Standard Key UOM
IF wa_crhd IS NOT INITIAL.
CLEAR gd_vge.
CONCATENATE 'PLPOD-VGE04(' gd_opt ')' INTO gd_vge.
PERFORM bdc_field       USING 'BDC_CURSOR'
gd_vge
.

CALL FUNCTION 'CONVERSION_EXIT_CUNIT_OUTPUT'
EXPORTING
input    = wa_crhd-vge04
language = sy-langu
IMPORTING
output   = wa_crhd-vge04.


PERFORM bdc_field       USING  gd_vge
wa_crhd
-vge04.
ENDIF.
ENDIF.


IF wa_recipe_i-vgw05 IS NOT INITIAL.
CLEAR gd_vgw05.
CONCATENATE 'PLPOD-VGW05(' gd_opt ')' INTO gd_vgw05.
CONDENSE gd_vgw05.
CLEAR gd_qty_tmp.
gd_qty_tmp
= wa_recipe_i-vgw05.
CONDENSE gd_qty_tmp.

PERFORM bdc_field       USING gd_vgw05
gd_qty_tmp
.

"Standard Key UOM


IF wa_crhd-vge05 IS NOT INITIAL.
CLEAR gd_vge.
CONCATENATE 'PLPOD-VGE05(' gd_opt ')' INTO gd_vge.
PERFORM bdc_field       USING 'BDC_CURSOR'
                                                     gd_vge
.

CALL FUNCTION 'CONVERSION_EXIT_CUNIT_OUTPUT'
EXPORTING
input    = wa_crhd-vge05
language = sy-langu
IMPORTING
output   = wa_crhd-vge05.


PERFORM bdc_field       USING  gd_vge
                                                    wa_crhd
-vge05.
ENDIF.
ENDIF.

IF wa_recipe_i-vgw06 IS NOT INITIAL.
CLEAR gd_vgw06.
CONCATENATE 'PLPOD-VGW06(' gd_opt ')' INTO gd_vgw06.
CONDENSE gd_vgw06.
CLEAR gd_qty_tmp.
gd_qty_tmp
= wa_recipe_i-vgw06.
CONDENSE gd_qty_tmp.

PERFORM bdc_field       USING gd_vgw06
gd_qty_tmp
.

"Standard Key UOM
CLEAR gd_vge.
CONCATENATE 'PLPOD-VGE06(' gd_opt ')' INTO gd_vge.
PERFORM bdc_field       USING 'BDC_CURSOR'
gd_vge
.

CALL FUNCTION 'CONVERSION_EXIT_CUNIT_OUTPUT'
EXPORTING
input    = wa_crhd-vge06
language = sy-langu
IMPORTING
output   = wa_crhd-vge06.

IF wa_crhd-vge06 IS INITIAL .
wa_crhd
-vge06 = 'H'.                     "Maintained if all Stnd keys are there 6th UOM is not filling
PERFORM bdc_field       USING  gd_vge
wa_crhd
-vge06.
ENDIF.
ENDIF.

IF gd_opt1 > 10 .
IF wa_recipe_i-vgw01 IS NOT INITIAL OR
wa_recipe_i
-vgw02 IS NOT INITIAL OR
wa_recipe_i
-vgw03 IS NOT INITIAL OR
wa_recipe_i
-vgw04 IS NOT INITIAL OR
wa_recipe_i
-vgw05 IS NOT INITIAL OR
wa_recipe_i
-vgw06 IS NOT INITIAL  .

lv_flg
= 'X'.

ELSE.

lv_flg
= ' '.

ENDIF" Set Flag Wether Standard Keys entered or Not
ENDIF.
ENDIF.

CLEAR : lv_index, wa_recipe_i.
ENDLOOP.

*---END OF SECTION 1-----------*


*--SECTION 2 : Charge Qty calculation

 

*----------------------------------------------------------------------*
**       Charge QTY Formula - Only for 20*(FTO) Plants  *
*----------------------------------------------------------------------*


IF im_recipe_h-werks CP '20*'.

CLEAR : gd_opt,
gd_opt1
,
ld_tmp_n
.

PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'/00'.
PERFORM bdc_field       USING 'BDC_CURSOR'
'PLPOD-VORNR(01)'.

DESCRIBE TABLE im_recipe_i LINES lv_lines.

*-->Page UP

DO ( lv_lines  DIV 10 + 1 ) TIMES  .
PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=P-'.
PERFORM bdc_field       USING 'BDC_CURSOR'
'PLPOD-VORNR(01)'.
ENDDO.

LOOP AT im_recipe_i INTO wa_recipe_i.
ld_cnt1 
= ld_cnt1 + 1.
gd_opt1
= sy-tabix.

IF im_recipe_h-plnme NE wa_recipe_i-meinh AND im_recipe_h-werks CP '20*'.

lv_lst_ind
= gd_opt1.

ld_num  
= ld_cnt1 MOD 11.
ld_num1 
= ld_cnt1 MOD 10.

lv_count
= lv_lines - sy-tabix .

IF gd_opt1 > 10.   "If No of records > 10

IF gd_opt1 MOD 10 NE 0.
IF ld_tmp_n < ( gd_opt1 DIV 10 ) .
CLEAR ld_tmp_n.
ENDIF.
ENDIF.

*-->Determine Page Down
*    If No of Records EQ multiple of 10 then decrease no of records by 1.

IF ( gd_opt1 MOD 10 ) = 0.
lv_pgdwn
= ( gd_opt1 DIV 10 ) - 1.
ELSE.
lv_pgdwn
= ( gd_opt1 DIV 10 ) .
ENDIF .

*-->Get the Previous Record Page Number

IF ld_cnt3 > 10 .
IF ld_cnt3 MOD 10 = 0.
lv_index1
= ld_cnt3 DIV 10 .
ELSE.
lv_index1
= ld_cnt3 DIV 10 + 1.   "Get Page Number of Previous Rec
ENDIF.
ELSE.
lv_index1
= 1.
ENDIF.

DO lv_pgdwn  TIMES .

lv_index1
= lv_index1 - 1.

PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=P+'.                     "1.Page Down

IF  lv_index1 = 0.
IF ld_cnt2 IS NOT INITIAL AND ld_tmp_n IS INITIAL.

IF ld_cnt3 IS NOT INITIAL .
IF ld_cnt3 MOD 10 = 0.
ld_cnt3
= 10.
ELSE.
ld_cnt3
= ld_cnt3 MOD 10.
ENDIF.          "Getting Exact Previous Record Index

CLEAR gd_flg.

CONCATENATE 'RC27X-FLG_SEL(' ld_cnt3 ')' INTO gd_flg.

PERFORM bdc_field  USING gd_flg
' '.                           "2.Deselect Previous Page Record
CLEAR gd_flg.

CLEAR gd_vornr.

ENDIF.

CONCATENATE 'PLPOD-VORNR(' ld_cnt3 ')' INTO gd_vornr.
CONDENSE gd_vornr.

PERFORM bdc_field       USING 'BDC_CURSOR'
gd_vornr
.                   "3.Place the Cursor
CLEAR gd_vornr.
CLEAR ld_cnt2.

IF gd_opt1 DIV 10 EQ 0.
ld_tmp_n
= gd_opt1 DIV 10 - 1.
ELSE.
ld_tmp_n
= gd_opt1 DIV 10.
ENDIF.

ENDIF.   "Index Comparision
ELSE.

PERFORM bdc_field       USING 'BDC_CURSOR'
'PLPOD-VORNR(01)' .
ENDIF.
ENDDO.

*          CLEAR : lv_index, lv_index1.

IF lv_count >= 10 AND ld_num1 = 1. "Get the Exact Number for selcting in the 10 rows
ld_cnt1
= 0001.
ELSE.
ld_cnt1
gd_opt1 MOD 10 .    "If the Records is less than 10
IF ld_cnt1 = 0.
ld_cnt1
= 10.
ENDIF.
ENDIF.

lv_index
= gd_opt1"Get Last Page Record  Number.
ENDIF.

*-->Charge Qty Formula

lv_chrg_qty
= im_recipe_h-bmsch / wa_recipe_i-bmsch"Charge Qty Formula
SPLIT lv_chrg_qty AT '.' INTO lv_chrg_qty lv_temp.
lv_temp
= lv_temp+0(1).

IF lv_temp GE 5.
lv_chrg_qty
= lv_chrg_qty + 1.
ENDIF.

IF lv_chrg_qty EQ '0'.
lv_chrg_qty
= 1.
ENDIF.

gd_chrg_qty
= lv_chrg_qty.
CONDENSE gd_chrg_qty.

PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=PICK'.
CLEAR gd_vornr.
CONCATENATE 'PLPOD-VORNR(' ld_cnt1 ')' INTO gd_vornr.
CONDENSE gd_vornr.

PERFORM bdc_field       USING 'BDC_CURSOR'
gd_vornr
.
IF ld_cnt2 IS NOT INITIAL.
CLEAR gd_flg.
CONCATENATE 'RC27X-FLG_SEL(' ld_cnt2 ')' INTO gd_flg.

ENDIF.

PERFORM bdc_field       USING gd_flg
' '.
PERFORM bdc_field       USING 'RC27X-ENTRY_ACT'
'1'.

PERFORM bdc_dynpro      USING 'SAPLC2CU' '0120'.     "Charge QTY
PERFORM bdc_field       USING 'BDC_OKCODE'
'/00'.

PERFORM bdc_field       USING 'BDC_CURSOR'
'PLPOD-UMREZ'.
PERFORM bdc_field       USING 'PLPOD-UMREZ'
gd_chrg_qty
.

PERFORM bdc_dynpro      USING 'SAPLC2CU' '0120'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=BACK'.

ld_cnt2
= ld_cnt1.   "previous Item to unselect
ld_cnt3
= gd_opt1.
CLEAR gd_chrg_qty.
ENDIF. "End of UOM Check
CLEAR wa_recipe_i.
ENDLOOP.

ENDIF.    "End of Charge QTy


SECTION 3 : Classificaion Class


*------------------------------------------------------------------------------------------------------*
*    ASSIGN CLASSIFICATION CLASS AND ALTERNATE RESOURCES
*  -  FOR FOR PARTICULAR RESOURCE AND ONLY FOR 2003 PLANT       *
*-------------------------------------------------------------------------------------------------------*

IF im_recipe_h-werks EQ '2003'.

CLEAR : gd_opt,
gd_opt1
,
lv_tabix
,
ld_cnt1
,
ld_num1
,
ld_cnt2
,
ld_cnt3
,
ld_tmp_n
,
lv_pgdwn
,
lv_index
.

DO  lv_lines  DIV 10 TIMES .
PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=P-'.

PERFORM bdc_field       USING 'BDC_CURSOR'
'PLPOD-VORNR(01)'.
ENDDO.

*-->Deselct the selected last record in Charge Qty and Once Page Up to First Record
IF lv_lst_ind IS NOT INITIAL.
IF lv_lst_ind > 10.

*-->Goto Exact record where the Record is selected

IF ( lv_lst_ind MOD 10 ) = 0.
lv_pgdwn1
= ( lv_lst_ind DIV 10 ) - 1.
ELSE.
lv_pgdwn1
= ( lv_lst_ind DIV 10 ).
ENDIF .

IF lv_lst_ind MOD 10 = 0.
lv_lst_ind
= 10.
ELSE.
lv_lst_ind
= lv_lst_ind MOD 10.
ENDIF.

DO lv_pgdwn1 TIMES .   "Goto Exact Record
PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=P+'.
ENDDO.

DO  lv_pgdwn1 TIMES .
PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=P-'.

CONCATENATE 'RC27X-FLG_SEL(' lv_lst_ind ')' INTO gd_flg.

PERFORM bdc_field  USING gd_flg
' '.
CLEAR gd_flg.

CLEAR gd_vornr.
CONCATENATE 'PLPOD-VORNR(' lv_lst_ind ')' INTO gd_vornr.
CONDENSE gd_vornr.

PERFORM bdc_field       USING 'BDC_CURSOR'
gd_vornr
.
CLEAR gd_vornr.

ENDDO.
CLEAR lv_lst_ind.

ELSE.
PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=P-'.
CONCATENATE 'RC27X-FLG_SEL(' lv_lst_ind ')' INTO gd_flg.

PERFORM bdc_field  USING gd_flg
' '.
CLEAR gd_flg.

CLEAR gd_vornr.
CONCATENATE 'PLPOD-VORNR(' lv_lst_ind ')' INTO gd_vornr.
CONDENSE gd_vornr.

PERFORM bdc_field       USING 'BDC_CURSOR'
gd_vornr
.
CLEAR gd_vornr.

ENDIF.
ENDIF.

*-->End of Page Down for Last selected record in Charge Qty Assignment

LOOP AT im_recipe_i INTO wa_recipe_i .

lv_tabix 
= sy-tabix.
ld_cnt1 
= ld_cnt1 + 1.

IF wa_recipe_i-phflg NE 'X' AND wa_recipe_i-class IS NOT INITIAL .

lv_count
= lv_lines - sy-tabix .

ld_num1 
= ld_cnt1 MOD 11.

IF ld_num1 = 1.
ld_num1
= ld_num1 DIV 10 .
ENDIF.

"Page Down for Records More Than 10 and to deselct the selected Record

IF lv_tabix > 10 .
IF ld_tmp_n < ( lv_tabix DIV 10 ) .
CLEAR ld_tmp_n.
ENDIF.

*-->Determine Page Down for Additional resources

IF ( lv_tabix MOD 10 ) = 0.
lv_pgdwn
= ( lv_tabix DIV 10 ) - 1.
ELSE.
lv_pgdwn
= ( lv_tabix DIV 10 ) .
ENDIF .
**--End of PageDown

*-->Get Page Number and Exact Record Number
IF ld_cnt3 > 10 .
IF ld_cnt3 MOD 10 = 0.
lv_index1
= ld_cnt3 DIV 10 .
ELSE.
lv_index1
= ld_cnt3 DIV 10 + 1.   "Get Page Number of Previous Rec
ENDIF.
ELSE.
lv_index1
= 1.
ENDIF.

DO lv_pgdwn TIMES .

PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=P+'.
lv_index1
= lv_index1 - 1.

IF lv_index1 = 0.
IF ld_cnt2 IS NOT INITIAL AND ld_tmp_n IS INITIAL.

**--Get the Exact Previous Record
IF ld_cnt3 IS NOT INITIAL .
IF ld_cnt3 MOD 10 = 0.
ld_cnt3
= 10.
ELSE.
ld_cnt3
= ld_cnt3 MOD 10.
ENDIF.          "Getting Exact Previous Record Index
ENDIF.

CLEAR gd_flg.
CONCATENATE 'RC27X-FLG_SEL(' ld_cnt3 ')' INTO gd_flg.

PERFORM bdc_field  USING gd_flg
' '.
CLEAR gd_vornr.
CONCATENATE 'PLPOD-VORNR(' ld_cnt3 ')' INTO gd_vornr.
CONDENSE gd_vornr.

PERFORM bdc_field       USING 'BDC_CURSOR'
gd_vornr
.
CLEAR gd_vornr.
CLEAR ld_cnt2.

IF lv_tabix  MOD 10 EQ 0.
ld_tmp_n
= lv_tabix DIV 10 - 1.
ELSE.
ld_tmp_n
= lv_tabix DIV 10.
ENDIF.

ENDIF.
ELSE.
PERFORM bdc_field       USING 'BDC_CURSOR'
'PLPOD-VORNR(01)'.
ENDIF.
ENDDO.

IF lv_count >= 10 AND ld_num1 = 0 .
ld_cnt1
= 0001.
ELSE.
ld_cnt1
= lv_tabix MOD 10 .    "If the Records is less than 10
IF ld_cnt1 = 0.
ld_cnt1
= 10.
ENDIF.
ENDIF.
ENDIF.

PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'/00'.

CLEAR gd_arbpl.
CONCATENATE 'PLPOD-ARBPL(' ld_cnt1 ')' INTO gd_arbpl.
CONDENSE gd_arbpl.
PERFORM bdc_field       USING 'BDC_CURSOR'
gd_arbpl
.

PERFORM bdc_field       USING gd_arbpl
wa_recipe_i
-arbpl.
CLEAR gd_arbpl.

PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=OPCA'.
CLEAR gd_vornr.

CONCATENATE 'PLPOD-VORNR(' ld_cnt1 ')' INTO gd_vornr.

PERFORM bdc_field       USING 'BDC_CURSOR'
gd_vornr
.
PERFORM bdc_field       USING 'RC27X-ENTRY_ACT'
'1'.
IF ld_cnt2 IS NOT INITIAL.
CLEAR gd_phflg.
CONCATENATE 'RC27X-FLG_SEL(' ld_cnt2 ')' INTO gd_phflg.
PERFORM bdc_field       USING gd_phflg
' '.
ENDIF.

CLEAR gd_phflg.
CONCATENATE 'RC27X-FLG_SEL(' ld_cnt1 ')' INTO gd_phflg.
PERFORM bdc_field       USING gd_phflg
'X'.
ld_cnt2
= ld_cnt1"for Deselecting the same record

*-->Assign CLassification Class type 019

PERFORM bdc_dynpro      USING 'SAPLCLCA' '0602'.
PERFORM bdc_field       USING 'BDC_CURSOR'
'RMCLF-KLART'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=ENTE'.
PERFORM bdc_field       USING 'RMCLF-KLART'
zif_d_constant
=>c_klart.

*-->assign Classification CLass

PERFORM bdc_dynpro      USING 'SAPLCLFM' '0500'.
PERFORM bdc_field       USING 'BDC_CURSOR'
'RMCLF-CLASS(01)'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'/00'.
PERFORM bdc_field       USING 'RMCLF-CLASS(01)'
wa_recipe_i
-class.

*-->Assign Additional Resource to Classification class
CLEAR gd_opt1.

SELECT SINGLE * FROM rmslv_char_class INTO CORRESPONDING FIELDS OF wa_class WHERE class = wa_recipe_i-class.

PERFORM bdc_dynpro      USING 'SAPLCTMS' '0109'.
PERFORM bdc_field       USING 'BDC_CURSOR'
'RCTMS-MWERT(01)'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=BACK'.

IF wa_recipe_i-atwrt1 IS NOT INITIAL.
gd_opt1
= gd_opt1 + 1.

CLEAR gd_atwrt.
CONCATENATE 'RCTMS-MNAME(' gd_opt1 ')' INTO gd_atwrt.
CONDENSE gd_atwrt.
PERFORM bdc_field      USING gd_atwrt
wa_class
-atnam.        "CLass Name

CLEAR gd_atwrt.
CONCATENATE 'RCTMS-MWERT(' gd_opt1 ')' INTO gd_atwrt.
CONDENSE gd_atwrt.
PERFORM bdc_field      USING gd_atwrt
wa_recipe_i
-atwrt1"Additional Resource
ENDIF.

IF wa_recipe_i-atwrt2 IS NOT INITIAL.
gd_opt1
= gd_opt1 + 1.


CLEAR gd_atwrt.
CONCATENATE 'RCTMS-MNAME(' gd_opt1 ')' INTO gd_atwrt.
CONDENSE gd_atwrt.
PERFORM bdc_field      USING gd_atwrt
wa_class
-atnam.        "CLass Name

CLEAR gd_atwrt.
CONCATENATE 'RCTMS-MWERT(' gd_opt1 ')' INTO gd_atwrt.
CONDENSE gd_atwrt.
PERFORM bdc_field      USING gd_atwrt
wa_recipe_i
-atwrt2.
ENDIF.


IF wa_recipe_i-atwrt3 IS NOT INITIAL.
gd_opt1
= gd_opt1 + 1.

CLEAR gd_atwrt.
CONCATENATE 'RCTMS-MNAME(' gd_opt1 ')' INTO gd_atwrt.
CONDENSE gd_atwrt.
PERFORM bdc_field      USING gd_atwrt
wa_class
-atnam.        "CLass Name

CLEAR gd_atwrt.
CONCATENATE 'RCTMS-MWERT(' gd_opt1 ')' INTO gd_atwrt.
CONDENSE gd_atwrt.
PERFORM bdc_field      USING gd_atwrt
wa_recipe_i
-atwrt3.
ENDIF.

IF wa_recipe_i-atwrt4 IS NOT INITIAL.
gd_opt1
= gd_opt1 + 1.

CLEAR gd_atwrt.
CONCATENATE 'RCTMS-MNAME(' gd_opt1 ')' INTO gd_atwrt.
CONDENSE gd_atwrt.
PERFORM bdc_field      USING gd_atwrt
wa_class
-atnam.        "CLass Name

CLEAR gd_atwrt.
CONCATENATE 'RCTMS-MWERT(' gd_opt1 ')' INTO gd_atwrt.
CONDENSE gd_atwrt.
PERFORM bdc_field      USING gd_atwrt
wa_recipe_i
-atwrt4.
ENDIF.

IF wa_recipe_i-atwrt5 IS NOT INITIAL.

gd_opt1
= gd_opt1 + 1.

CLEAR gd_atwrt.
CONCATENATE 'RCTMS-MNAME(' gd_opt1 ')' INTO gd_atwrt.
CONDENSE gd_atwrt.
PERFORM bdc_field      USING gd_atwrt
wa_class
-atnam.        "CLass Name

CLEAR gd_atwrt.
CONCATENATE 'RCTMS-MWERT(' gd_opt1 ')' INTO gd_atwrt.
CONDENSE gd_atwrt.
PERFORM bdc_field      USING gd_atwrt
wa_recipe_i
-atwrt5.

ENDIF.

IF wa_recipe_i-atwrt6 IS NOT INITIAL.
gd_opt1
= gd_opt1 + 1.

CLEAR gd_atwrt.
CONCATENATE 'RCTMS-MNAME(' gd_opt1 ')' INTO gd_atwrt.
CONDENSE gd_atwrt.
PERFORM bdc_field      USING gd_atwrt
wa_class
-atnam.        "CLass Name

CLEAR gd_atwrt.
CONCATENATE 'RCTMS-MWERT(' gd_opt1 ')' INTO gd_atwrt.
CONDENSE gd_atwrt.
PERFORM bdc_field      USING gd_atwrt
wa_recipe_i
-atwrt6.

ENDIF.

PERFORM bdc_dynpro      USING 'SAPLCLFM' '0500'.
PERFORM bdc_field       USING 'BDC_CURSOR'
'RMCLF-CLASS(01)'.

PERFORM bdc_field       USING 'BDC_OKCODE'
'=ENDE'.
PERFORM bdc_field       USING 'RMCLF-PAGPOS'
'1'.


PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'/00'.
ld_cnt3
= lv_tabix .
ld_cnt4
= lv_tabix.
ENDIF.
CLEAR gd_opt1.
ENDLOOP.
ENDIF" Additional Resources

CLEAR ld_tmp_n.



*-------------------------------------------------------------------------*
*    GENERATE RELATIONSHIP IF RECIPE ITEMS     *
*  -   >= 4 RECORDS                                                        *
*-------------------------------------------------------------------------*

DESCRIBE TABLE im_recipe_i LINES lv_lines.
IF lv_lines GE 4.

PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.             "GENERATE RELATIONS
PERFORM bdc_field       USING 'BDC_OKCODE'
'=MAAL'.
PERFORM bdc_field       USING 'BDC_CURSOR'
'PLPOD-VORNR(01)'.

PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=AOBC'.                  "All Phases
PERFORM bdc_field       USING 'BDC_CURSOR'
'PLPOD-VORNR(01)'.

PERFORM bdc_dynpro      USING 'SAPLCPDI' '1000'.
PERFORM bdc_field       USING 'BDC_CURSOR'
'AUSWAHL_PHASE2'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=OK'.
PERFORM bdc_field       USING 'AUSWAHL_PHASE1'
'X'.
PERFORM bdc_field       USING 'AUSWAHL_PHASE2'
' '.
PERFORM bdc_field       USING 'AUSWAHL_SICHT1'
'X'.
PERFORM bdc_field       USING 'PLAB-AOBAR'
'FS'.

ENDIF.


*------------------------------------------------------------------------*
*    CREATE PRODUCTION VERSION  TAB                *
*------------------------------------------------------------------------*

PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=OMBO'.
PERFORM bdc_field       USING 'BDC_CURSOR'
'PLPOD-VORNR(01)'.

PERFORM bdc_dynpro      USING 'SAPLCMFV' '0200'.
PERFORM bdc_field       USING 'BDC_CURSOR'
'MKAL-STLAN'.
PERFORM bdc_field       USING 'MKAL-VERID'
lv_verid
.
PERFORM bdc_field       USING 'MKAL-TEXT1'
lv_prod_desc
.
PERFORM bdc_field       USING 'MKAL-BSTMA'
lv_bstma
.
PERFORM bdc_field       USING 'MKAL-BSTMI'
lv_bstmi
.
PERFORM bdc_field       USING 'MKAL-ADATU'
lv_date
.
PERFORM bdc_field       USING 'MKAL-BDATU'
'31.12.9999'.
PERFORM bdc_field       USING 'MKAL-STLAL'
lv_stlal
.
PERFORM bdc_field       USING 'MKAL-STLAN'
wa_mast
-stlan.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=BACK'.



*----------------------------------------------------------------------------------*
*      GET PHASE TO BE ASSIGNED AND                                 *
*      SELECT BOM COMPONENT AND ASSIGN PHASE         *
*----------------------------------------------------------------------------------*

CALL METHOD lr_bom->zif_d_bom~read
EXPORTING
im_reinr
= wa_recipe_h-reinr
IMPORTING
ex_bom_i
= lit_bom_item.

DATA : lv_phase(4) TYPE c,
lv_lines1
(4) TYPE n,
lv_desc
TYPE string.

DESCRIBE TABLE  lit_bom_item LINES lv_lines1.
CLEAR : ld_cnt1,ld_num,ld_num1,lv_count.
LOOP AT  lit_bom_item INTO wa_bom_item.

*---Split Phase to be assigned to get Exact phase value

SPLIT wa_bom_item-zph_assign AT '-' INTO lv_phase lv_desc.
CONDENSE lv_phase.
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
EXPORTING
input  = lv_phase
IMPORTING
output = lv_phase.


*--Begin of Page Down----------*

ld_cnt1
= ld_cnt1 + 1.
ld_num 
= ld_cnt1 MOD 6.
ld_num1
= ld_cnt1 DIV 6.
lv_count
= lv_lines1 - sy-tabix .

IF ld_cnt1 > 6 AND  ld_num = 1 .                               "Page Down

PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=P+'.
CONCATENATE 'STPOB-IDNRK(' '1' ')' INTO ld_idnrk.
PERFORM bdc_field       USING 'BDC_CURSOR' ld_idnrk.

IF lv_count >= 6 .
ld_cnt1
0001 .
ELSE.
ld_cnt1
= ( 6 lv_count ) .   "If the Records are lessthan 6
ENDIF.
ENDIF.


*---End OF Page Down--------------*

PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=ENT1'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=NEW'.

CLEAR ld_idnrk.
CONCATENATE 'STPOB-IDNRK(' ld_cnt1 ')' INTO ld_idnrk.     "CURSOR IDNRK
CONDENSE ld_idnrk.
PERFORM bdc_field       USING 'BDC_CURSOR' ld_idnrk   .


CLEAR ld_flg.
CONCATENATE 'RC27X-FLG_SEL(' ld_cnt1 ')' INTO ld_flg.
CONDENSE ld_flg.
PERFORM bdc_field       USING ld_flg                  "'RC27X-FLG_SEL(01)'
'X'.

PERFORM bdc_dynpro      USING 'SAPLCM01' '5090'.
PERFORM bdc_field       USING 'BDC_CURSOR'
'RCM01-VORNR'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=GOON'.
CONDENSE wa_bom_item-zph_assign.
PERFORM bdc_field       USING 'RCM01-VORNR'
lv_phase
.

PERFORM bdc_field       USING 'BDC_OKCODE'
'=ENT1'.

ENDLOOP.


PERFORM bdc_dynpro      USING 'SAPLC2CU' '0100'.
PERFORM bdc_field       USING 'BDC_OKCODE'
'=BU'.

REFRESH lit_msgtab[].


*------------------------------------------------------------------*
*---Call Transaction C201 - RECIPE CREATION----*
*------------------------------------------------------------------*

CALL TRANSACTION 'C201'  USING  lit_bdcdata
OPTIONS
FROM wa_ctu_options
MESSAGES
INTO lit_msgtab .

IF sy-subrc NE 0.

LOOP AT lit_msgtab INTO wa_msgtab .
*      IF wa_msgtab-msgtyp EQ ''.

CLEAR gd_msg.
CALL FUNCTION 'FORMAT_MESSAGE'
EXPORTING
id        = wa_msgtab-msgid
lang     
= wa_msgtab-msgspra
no        = wa_msgtab-msgnr
v1       
= wa_msgtab-msgv1
v2       
= wa_msgtab-msgv2
v3       
= wa_msgtab-msgv3
v4       
= wa_msgtab-msgv4
IMPORTING
msg      
= gd_msg
EXCEPTIONS
not_found
= 1
OTHERS    = 2.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.

wa_return
-id = wa_msgtab-msgid.
wa_return
-number = wa_msgtab-msgnr.
wa_return
-message = gd_msg.
wa_return
-type = 'E'.
APPEND wa_return TO e_return.

ENDLOOP.
*-----End of BDC for recipe creation -------*

 


Note : we can cross check the create BOM and Recipe using Tcode C223


SDB_ADBC - the program

$
0
0

Hi all,

 

As I wrote here http://scn.sap.com/community/abap/blog/2013/12/23/sdbadbc--power-to-the-people

I have been playing with package SDB_ADBC and its gang members....

 

Based on what I learned from program ADBC_QUERY I wrote a program (Y_R_EITAN_TEST_28_01) that I hope you will find it useful .

 

The program allow the user (A programmer) to query data using the raw native power of the local database.


For example In case of Oracle you can refer to Oracle documentation (http://docs.oracle.com/cd/B19306_01/server.102/b14200/toc.htm).

 

- The program create SQL statment based on the selected fields and functions (FORM get_statment) .
- The program create a dynamic table based on the selected fields and functions (FORM run_statment) .
- Using SDB_ADBC clases the program query the data and fill the dynamic table (FORM run_statment).
- The table is presented using CL_SALV_TABLE .

 

The native SQL is not client dependent so using MANDT in the where clue is required.

 

Limitation:
  The program is not design for joins (This require more development and I do not have the time...)
 
Some screens:

 

capture_20131226_120644.png

 

capture_20131226_115955.png

 

Source:

 

Y_R_EITAN_TEST_28_01.txt - the program.

Y_R_EITAN_TEST_28_01_screen_100.txt - screen source .(it is very simple since I use OO components )

 

 

capture_20131226_123501.png

 

capture_20131226_123526.png

 

STATUS_COMMON

 

Define BACK , EXIT and CANCEL as Exit Command.

 

capture_20131226_124042.png

capture_20131226_123820.png

 

TITLE_COMMON

capture_20131226_124300.png

That is all for now .

 

Have fun.


Screen process with ABAP OO

$
0
0

At last I have find time to write this . If you find anything wrong pls let me know.

 

As all you know screen process in ABAP has two main block PBO ( Process before output ) and PAI ( Process after input ). All screen has this block.

For dialog program I used this.

 

So we can declare a parent screen object for all screen which we use in program.

Definition:

CLASS lcl_screen DEFINITION ABSTRACT.   PUBLIC SECTION.
METHODS : constructor                IMPORTING                  i_dynnr TYPE sy-dynnr.
 * All screen process     METHODS : pbo ABSTRACT,               pai ABSTRACT                IMPORTING                  fcode TYPE sy-ucomm                RETURNING value(r_fcode) TYPE sy-ucomm,               free ABSTRACT.
 * Exit-command     METHODS : exit_command                IMPORTING                  fcode TYPE sy-ucomm.
 * Screen static methods     CLASS-METHODS : get_screen                      IMPORTING                        i_dynnr TYPE sy-dynnr                      RETURNING value(r_screen) TYPE REF TO lcl_screen.     CLASS-METHODS : del_screen                      IMPORTING                        i_dynnr TYPE sy-dynnr.
PRIVATE SECTION.     DATA : screen TYPE sy-dynnr.     TYPES : BEGIN OF lst_screen,              screen TYPE REF TO lcl_screen,             END OF lst_screen.     CLASS-DATA : t_screen TYPE STANDARD TABLE OF lst_screen.     CONSTANTS : c_class_name TYPE string VALUE 'LCL_SCREEN'.
ENDCLASS.                    "lcl_screen DEFINITION

Implementation

CLASS lcl_screen IMPLEMENTATION.   METHOD constructor.     screen = i_dynnr."Which screen   ENDMETHOD.                    "constructor   METHOD exit_command.     CASE fcode.       WHEN 'EXIT'.         LEAVE PROGRAM.     ENDCASE.   ENDMETHOD.                    "exit_command   METHOD get_screen.     DATA : ls_screen TYPE lst_screen,            lv_type TYPE string.     READ TABLE t_screen           INTO ls_screen       WITH KEY screen->screen = i_dynnr.     IF sy-subrc NE 0.       CONCATENATE c_class_name                   i_dynnr              INTO lv_type         SEPARATED BY '_'.       CREATE OBJECT ls_screen-screen TYPE (lv_type)         EXPORTING           i_dynnr = i_dynnr.       APPEND ls_screen           TO t_screen.     ENDIF.     r_screen ?= ls_screen-screen.   ENDMETHOD.                    "get_screen   METHOD del_screen.     DATA : ls_screen TYPE lst_screen,            lv_type TYPE string.     READ TABLE t_screen           INTO ls_screen       WITH KEY screen->screen = i_dynnr.     IF sy-subrc NE 0.
 * Screen doesnt exist       RETURN.     ENDIF.     ls_screen-screen->free( ).     DELETE t_screen      WHERE screen->screen = i_dynnr.   ENDMETHOD.                    "del_screen
ENDCLASS.                    "lcl_screen IMPLEMENTATION

In get_screen method, to decide which screen object will be created, logic is all screen object start with LCL_SCREEN_ and last 4 character is screen number. If screen object is not created, we create object and adding it to object table then returning screen object. If it is created just returning the object.

 

lets create our first screen :

9000.JPG

And screen module is:

 MODULE pbo OUTPUT.   lcl_screen=>get_screen( sy-dynnr )->pbo( ).
 ENDMODULE.                 " PBO  OUTPUT
MODULE pai INPUT.   gv_okcode = lcl_screen=>get_screen( sy-dynnr )->pai( gv_okcode ).
 ENDMODULE.                 " PAI  INPUT
MODULE exit_command INPUT.   lcl_screen=>get_screen( sy-dynnr )->exit_command( gv_okcode ).
 ENDMODULE.                 " EXIT_COMMAND  INPUT

Now lets create screen 9000 object

Definition

CLASS lcl_screen_9000 DEFINITION INHERITING FROM lcl_screen FINAL.   PUBLIC SECTION.     METHODS : pbo REDEFINITION,               pai REDEFINITION,               free REDEFINITION.   PRIVATE SECTION.
 * Screen 9000 methods     METHODS : set_exclude                RETURNING value(rt_ex) TYPE status_excl_fcode_tt.
 ENDCLASS.                    "lcl_screen_9000 DEFINITION

Implementation

CLASS lcl_screen_9000 IMPLEMENTATION.   METHOD pbo.     DATA : lt_ex TYPE status_excl_fcode_tt.     lt_ex = set_exclude( ).     SET PF-STATUS 'STATUS_9000' EXCLUDING lt_ex.     SET TITLEBAR '9000'.   ENDMETHOD.                    "pbo   METHOD set_exclude.     DATA : lv_ex TYPE fcode.     lv_ex = 'SAVE'.     APPEND lv_ex         TO rt_ex.   ENDMETHOD.                    "set_exclude   METHOD pai.     CASE fcode.       WHEN 'BACK'.           LEAVE TO SCREEN 0.     ENDCASE.   ENDMETHOD.                    "pai   METHOD free.
 * If we declare global object data, we can clear or refresh here!   ENDMETHOD.                    "free
 ENDCLASS.                    "lcl_screen_9000 IMPLEMENTATION

As you can see screen releated methods can be declare in screen object.

like set_exclude method.

 

If a method is global for all screen we can add to lcl_screen for example POPUP_TO_CONFIRM function. we can add this method to lcl_screen

 

TYPES : ty_text(400) TYPE c.     METHODS : call_popup                IMPORTING                  i_title TYPE text60                  i_text  TYPE ty_text                RETURNING value(r_answer) TYPE char1.
METHOD call_popup.     CALL FUNCTION 'POPUP_TO_CONFIRM'       EXPORTING         titlebar       = i_title         text_question  = i_text       IMPORTING         answer         = r_answer       EXCEPTIONS         text_not_found = 1         OTHERS         = 2.     IF sy-subrc <> 0.
 * Implement suitable error handling here     ENDIF.   ENDMETHOD.                    "call_popup

Now we can use this method in all screen.

 

METHOD pai.     CASE fcode.       WHEN 'BACK'.
IF call_popup( i_title = text-006                           i_text  = text-007 ) EQ '1'.           LEAVE TO SCREEN 0.         ENDIF.     ENDCASE.   ENDMETHOD.                    "pai

If we need to create ALV object in one screen we can add this to screen like, or picture element. Just we need to do declare screen object inherited from lcl_screen.

 

I hope you like it.

Have a nice day.

A small tip of class cl_system_transaction_state

$
0
0

The class cl_system_transaction_state contains several useful utility methods:

 

get_in_update_task: return the flag whether current code is running with normal work process or in update work process

get_on_commit: return flag whether current code is called because of a previous registration via PERFORM ON COMMIT and triggered by COMMIT WORK

get_sap_luw_key: return current LUW ID

 

I just use a very simple report to test them. First I call the FM ZSQF in a normal way, then call it via update task, then register it with PERFORM ON COMMIT and trigger it via COMMIT WORK.

 

 

WRITE: / 'Direct call ZSQF begin...'.

DATA(lv_luw_key) = cl_system_transaction_state=>get_sap_luw_key( ).

WRITE:/ 'LUW key in main program:', lv_luw_key.

CALL FUNCTION 'ZSQF'.

WRITE: / 'Direct call ZSQF end...'.

CALL FUNCTION 'ZSQF' IN UPDATE TASK.

PERFORM call_fm ON COMMIT.

COMMIT WORK AND WAIT.

lv_luw_key = cl_system_transaction_state=>get_sap_luw_key( ).

WRITE:/ 'LUW key in main program after COMMIT WORK:', lv_luw_key.

FORM call_fm.

   WRITE:/ 'ZSQF is called on COMMIT begin...'.

   CALL FUNCTION 'ZSQF'.

   WRITE:/ 'ZSQF is called on COMMIT end...'.

ENDFORM.

In the function module ZSQF, I just print out the three flags.

 

DATA(lv_in_update) = cl_system_transaction_state=>get_in_update_task( ).

DATA(lv_on_commit) = cl_system_transaction_state=>get_on_commit( ).

DATA(lv_luw_key) = cl_system_transaction_state=>get_sap_luw_key( ).

WRITE: / 'Am I in update task? ' , lv_in_update.

WRITE: / 'Am I triggered via PERFORM ON COMMIT?', lv_on_commit.

WRITE: / 'Current LUW Key' , lv_luw_key.

 

The execution result shows the fact that the normal FM call, the FM registered to COMMIT WORK and the update task all run within the same LUW, and also proves the explanation of COMMIT WORK in ABAP help: "The COMMIT WORK statement closes the current SAP LUW and opens a new one".

clipboard1.png

 

The WRITE keyword executed in update task will not generate any output in SE38 list, and apart from switching on "update debugging" and check the three flags in debugger, there is also another way to log the content of the variable like lv_luw_key:

 

Just create a new checkpoint group via tcode SAAB, specify option "Log" for Logpoints and maximum validity period.

clipboard2.png

 

Then append the following code in the FM implementation:

 

IF lv_in_update = 1.
LOG-POINT ID ZUPDATELOG SUBKEY 'Current LUW KEY' FIELDS lv_luw_key.

ENDIF.

Now after report execution, go to tcode SAAB, click Log tab, and we can find the content of lv_luw_key which is logged by the above ABAP code LOG-POINT ID ZUPDATELOG SUBKEY 'Current LUW KEY' FIELDS lv_luw_key.

clipboard3.png

LUW - Part1

$
0
0

Logical unit of work (LUW) in SAP:

The objective of this document is aimed at explaining the concept of logical unit of work in SAP. I assume that this will be a good read for my fellow readers.

 

Why logical units of work?

The main purpose of the logical unit of work is to ensure data consistency in the R/3 systems. Let us consider that there is a complex R/3 transaction that involves a series of dialog steps; during the execution of one such dialog step at the middle of the transaction the data involved will lack completeness & will therefore be inconsistent. When talking about of logical unit of work we must note the following points initially.

  1. Database LUW also known as DB LUW, each dialog step has its own DB LUW that is used to ensure data consistency.
  2. SAP LUW consists of several dialog steps and all the changes to database are written in a single DB LUW. We must note that the database changes are always written in the final database LUW.
  3. Therefore to summarize a complex R/3 transaction may span across several dialog steps each having its own DB LUW, however all the changes are written to the database in the final DB LUW of the SAP LUW after a commit work is executed by the system or rolled back with no changes written to the database due to errors.


What is a database LUW?

A database LUW is work unit consisting of database operations (INSERT, UPDATE, MODIFY & DELETE) that it used to make the database changes.  The final database LUW of a SAP LUW is used to make the changes to the database, the changes to the database is not made until after the database commit. The database LUW is executed in a special kind of work process called the update work process.


What is a SAP LUW?

A SAP LUW is a logical unit work that spans over several dialog steps. Let us consider a complex R/3 transaction of n screens; where each screen has its own DB LUW involving database operations and are logically related to each other. It is important that the changes to the database for this complex transaction must be made all at once or it must be rolled back all for the data consistency; that is where the SAP LUW becomes critical in maintaining a logical relationship between several DB LUWs (dialog phases) & making all the changes to the database in the final DB LUW. SAP LUW uses the bundling technique to achieve the same. There are several possibilities of bundling technique and one of them is bundling the database changes using a function module call in the UPDATE TASK.

 

Bundling in SAP LUW:

A CALL FUNCTION…IN UPDATE TASK is one of the bundling techniques to achieve all the database changes in the last DB LUW of the SAP LUW. The open SQL statements for database changes are placed in the function module instead of placing them in the code, using the addition UPDATE TASK ensures that the function module is not executed until COMMIT WORK is found in the program. At COMMIT WORK the function module calls with UPDATE TASK are executed in a special work process called update work process and the changes to the database is executed in the final DB LUW of the SAP LUW.


R/3 transactions using update function modules.

Let us consider there is one R/3 transaction calling another R/3 transaction and as well as calling some update function module. There is some typical behavior that can be noted for this particular scenario.

  • (a)    The called transaction begins in a new SAP LUW in comparison to the calling transaction. It implies that there are two different SAP LUWs now.
  • (b)   A new update key is generated for the new SAP LUW that is used to identify all the update function modules.
  • (c)    If the called R/3 transaction does not have a COMMIT WORK then the update function modules will not be executed i.e. both the calling transaction & the called transaction must have their own COMMIT WORK.
  • (d)   The update function modules are called only after the system has executed a COMMIT WORK.
  • (e)   Let us consider there is an importing parameter -IM_X  for the update function module; now the same function module is called at several places in the program like shown below –

DATA: gv_x TYPE i.

gv_x = 1.

CALL FUNCTION UPD_FUNC IN UPDATE TASK

EXPORTING

         IM_X = gv_x.

gv_x = 2.

CALL FUNCTION UPD_FUNC IN UPDATE TASK

EXPORTING

         IM_X = gv_x.

 

gv_x = 3.

COMMIT WORK.

 

What will be the value of gv_x at the COMMIT WORK?

No, the value of gv_x will not be 3; when commit is executed by the system the value of gv_x that will be passed is dependent at the point of function call therefore during the first call the value of gv_x will be 1 & during the second call the value of gv_x will be 2.

ABAP Dependency Injection - An implementation approach

$
0
0

Introduction

In this blog I want to show you some concepts of a solution for a "Dependency Injection"(DI)-approach which is used in one of my recent projects. It consists of a DI-Framework written in ABAP and an integration-pattern to get the framework working in a large component-based ABAP-application. The DI-framework is custom-made to fit the needs of the chosen design-approach. I will focus on the parts which helps you to organize your DI-container in a component based software design.

 

I won't give you an introduction to DI but rather explain an implementation approach to fit into the abap world. If you're looking to understand the basics of DI you should read "The issue with having many small classes" and "Dependency Injection for ABAP" or even the wikipedia page for DI would be a good start.

 

Getting to the requirements

In my recent project I was looking for a flexible solution of managing my OO-instances that build up my applications. While a factory pattern only describes how to design a factory for access by a client, it doesn't describe how the factory should internally manage instances that it produces. Most of the time you will put a lot of "create object" statements into your factory to set up your application. By this your application-"set up" will be fixed.

Also if you follow the SOLID-Principals in your software-design you will soon face problems described in the blog "The issue with having many small classes". You have a lot of small classes and you have to write code for instance-creation and management in your factory-classes. This could become annoying.

Another drawback is that it's hard to reuse your factory since your application-"set up" is fixed and you can't replace certain classes for special purposes.

 

So the solution is to combine the factory pattern with a DI-framework. The DI-framework handles the instance management and the factory-pattern provides a way to access certain instances. The benfit is, that you keep the framework-code inside the factory and out of your business code. Someone who uses your factory doesn't even have to know about the DI-framework.

 

So in the end the framework fulfills the following reqirements

  • Quick/easy to set up
  • Debugging possibilities
  • Support for all DDIC-Datatypes
  • No dependencies in business code (i.e. not forcing to inherit some class)
  • Class creation (with "create object" or factory methods)
  • Class configuration (constructor-based or with setter-methods)
  • Modularization to match a component based architecture
  • Support for "where-used list"
  • Replacing classes in a given setup

 

Quick Example

I will first start to give you a quick introduction into the DI-framework and how the basic usage is.

The following class-diagram shows the setup for the examples ("zif_*" for interfaces; "zcl_*" for classes).

Klassenmodell.gif

 

"zcl_service_a" needs some other classes to get its job done. "zcl_persistence_a" implements some kind of database access and "zcl_calulate_a" provides some kind of calculation.

If you want to use "zif_service_a" you first have to instantiate "Persistence A" and "Calculate A". Then you can create "Service A".

The ABAP code would look like this (In ABAP NW740 syntax)

  DATA(lo_persistence) = NEW lcl_persistence_a( ).  DATA(lo_calculate) = NEW lcl_calculate_a( ).  DATA(lo_service) = NEW lcl_service_a(                                        io_pers = lo_persistence                                        io_calc = lo_calculate ).

This is quite simple. You could put that into your factory and you would be fine. But if your component grows and involves more and more classes the task of putting them together gets annoying. Especially if you tend to create small classes.

On the other hand it gets difficult to reuse your setup (the way you put your classes together). If you want to use your service in a slightly different context and want to replace i.e. the calculate-implementation by your own you have to copy the whole factory and replace the line where you create the calculate-instance. Some nicer approach would be to say: "do everything as is, but instead of your calculate implementation take mine".

 

With the DI framework you would do something like this. In the first step you would create the DI-container:

  " Create DI-Container  DATA(lo_container) = zcl_di_container=>create_instance_default( ).

Then you would register all your classes in the container, so the container has a bulk of classes to work on.

  " Register classes into container  lo_container->register_classname( iv_classname = 'ZCL_SERVICE_A' ).  lo_container->register_classname( iv_classname = 'ZCL_PERSISTENCE_A' ).  lo_container->register_classname( iv_classname = 'ZCL_CALCULATE_A' ).

Now you can query the container to get an instance of your "Service A":

  DATA lo_service TYPE REF TO zif_service_a.  lo_container->get_instance_value(    CHANGING cv_target_value = lo_service ).
" Use your instance
lo_service->do_something( ... ).

The container analyzes the type of the variable "lo_service". Since it is "zif_service_a" the container looks for a registered class that matches this interface. This would be "zcl_service_a". It analyzes the constructor of this class and looks for classes that match the type of the specific parameters. So it will first create instances of "zcl_persistence_a" and "zcl_calculate_a" before it creates an instance of "zcl_service_a". The instance will be copied into the variable "lo_service".

The construction and analyzing process is done recursively and it can detect possible inifinite recursions. You can even use other datatypes on your parameters like int, string, structure or tables, which also have to be registered in the container.

 

Replacing classes

The code above would be placed into some kind of factory. While the registration code would be placed into the constructor, the querying code would be placed into the factory methods.

CLASS zcl_factory_a IMPLEMENTATION.  METHOD constructor.    ao_container = zcl_di_container=>create_instance_default( ).    ao_container->register_classname( iv_classname = 'ZCL_SERVICE_A' ).    ao_container->register_classname( iv_classname = 'ZCL_PERSISTENCE_A' ).    ao_container->register_classname( iv_classname = 'ZCL_CALCULATE_A' ).  ENDMETHOD.  METHOD get_service_a.    " ** RETURNING VALUE(ro_service_a) TYPE REF TO zif_service_a.    ao_container->get_instance_value( CHANGING cv_target_value = ro_service_a ).  ENDMETHOD.
ENDCLASS.

If you want to replace a specific class you can do it by inheriting the factory-class and register your own implementation. I.e. if you want to replace the implementation "ZCL_SERVICE_A" of interface "ZIF_SERVICE_A" by your own implementation "ZCL_SERVICE_A_BETTER" you can do this:

CLASS lcl_factory_a_better IMPLEMENTATION. " INHERITING FROM zcl_factory_a  METHOD constructor.    super->constructor( ).    ao_container->register_classname( i_var_classname = 'ZCL_SERVICE_A_BETTER' ).  ENDMETHOD.
ENDCLASS.

The container acts like a stack and every new registered datatype or class would be put on top of this stack. If you query for a specific type the container checks for matches from top to bottom. The first match will be returned and used. Since your own class ZCL_SERVICE_A_BETTER is registered last it is on top of the stack. So it will be the first matching type for the interface "zif_service_a" and will be selected. By this way you can replace specific classes.

 

Debugging

If your container contains a large amount of classes you may want to check if instances are bound to the correct parameters. The di-framework provides a possibility to trace the querying of the container. The trace is rendered into a graph. The following picture shows the graph from the small example above:

 

ServiceA.gif

 

You can see that both parameters of the constructor from "zcl_service_a" are bound to the correct classes.

 

Namespaces

Another concept realized in this DI-framework are namespaces. In an application that consists of different components each component would need its own container. That's because you can't put every class of every component into a single container. This would result in conflicts between different components which implement shared interfaces.

Another option is to use namespaces. Each component uses the same container-data-core but uses a different namespace within this container. The namespaces are isolated from each other so that conflicts are avoided. If you want to use a class of another namespace you can set an alias to the other namespace.

The following example shows the concept of namespaces and aliases.The following class-diagram shows "Service B":

 

KlassenmodellB.gif

 

You can see that "Service B" uses "Service A". Furthermore it implements "zif_calculate" which is also implemented by "Service A". The code to set up the DI-container for this scenario would be the following:

 

  DATA(lo_ctx_data) = NEW zcl_di_context_data( ).

The instance of "lo_ctx_data" is the core of each container. It contains the registered classes and datatypes. If you have different container-instances which share this instance of "lo_ctx_data"  they will all act on the same container data.

If you create a container you can set up a default namespace. If you register classes they will all be placed into the containers default namespace. "Service A" would be set up in the following way:

 

  DATA(lo_container_a) = /abk/cl_di_container=>create_instance_default(                                       i_var_namespace = 'urn:a'                                       i_obj_context_data = lo_ctx_data ).  " Register classes into container  lo_container_a->register_classname( iv_classname = 'ZCL_SERVICE_A' ).  lo_container_a->register_classname( iv_classname = 'ZCL_PERSISTENCE_A' ).  lo_container_a->register_classname( iv_classname = 'ZCL_CALCULATE_A' ).

 

"Service B" is set up in the following way. It will reuse the previously created instance  of "lo_ctx_data":

  DATA(lo_container_b) = /abk/cl_di_container=>create_instance_default(                                       i_var_namespace = 'urn:b'                                       i_obj_context_data = lo_ctx_data ).  " Register classes into container  lo_container_b->register_classname( iv_classname = 'ZCL_SERVICE_B' ).  lo_container_b->register_classname( iv_classname = 'ZCL_PERSISTENCE_B' ).  lo_container_b->register_classname( iv_classname = 'ZCL_CALCULATE_B' ).  lo_container_b->register_alias(                                  iv_typename = 'ZIF_SERVICE_A'                                  iv_query_namespace = 'urn:a' ).

If you retrieve "Service B" ...

  DATA lo_service TYPE REF TO zif_service_b.  lo_container_b->get_instance_value( CHANGING cv_target_value = lo_service ).

... you will get an instance constructed like in the following picture:

 

ServiceB.gif

(Click for a larger image)

 

You can see two namespaces. "urn:a" for "Service A" and "urn:b" for "Service B". "Service B" has a registered "alias" which connects the two namespaces on the type "zif_service_a". So just the type "zif_service_a" of namespace "urn:a" is visible in namespace "urn:b". So you can see that the namespaces are clearly isolated to each other and the querying and construction is restricted to its namespace unless you set an explicit alias.

 

DI-Modules

The last concept I want to show are modules. Modules are a descriptive way to set up a container. You can describe five different aspects with a module:

  1. Module-Name
  2. Default-Namespace
  3. Import dependent modules
  4. Public module items
  5. Container registration

 

The following code shows an example for a module for "Service A":

 

CLASS zcl_module_a IMPLEMENTATION.  METHOD zif_di_factory_module~get_modulename.    rv_module_name = 'Service A'.  ENDMETHOD.  METHOD zif_di_factory_module~get_default_namespace.    rv_namespace = 'urn:a'.  ENDMETHOD.  METHOD zif_di_factory_module~register_imports.    " nothing  ENDMETHOD.  METHOD zif_di_factory_module~register_interface.    io_container_mng->register_alias(      iv_typename = 'zif_service_a'      iv_query_namespace = 'urn:a' ).  ENDMETHOD.  METHOD zif_di_factory_module~register_classes.    io_container_mng->register_classname( iv_classname = 'ZCL_SERVICE_A' ).    io_container_mng->register_classname( iv_classname = 'ZCL_PERSISTENCE_A' ).    io_container_mng->register_classname( iv_classname = 'ZCL_CALCULATE_A' ).  ENDMETHOD.
ENDCLASS.

Modules can hold several namespaces. But there is always one namespace that acts as default namespace. Every class or datatype is registered with the default-namespace unless a namespace is explicitly provided on registration.

 

A module can build a relationship to another module in order to get access to public services (registered classes) provided by the other module. This is done in the "register_import"-Method. I.e. in the previous section "Service B" imports a service from "Service A". In terms of modules this would be setup by a relationship between these modules. So "Service B" would import module "Service A". In the module declaration of "Service B" you would find this method implementation:

  METHOD zif_di_factory_module~register_imports.    io_registry_imports->register_factory_module( NEW zcl_module_a( ) ).  ENDMETHOD.

The method "register_interface" describes which registered types of the module should be public and imported into another namespace once the module is imported elsewhere. The method receives the DI-container of the foreign module and can register aliases into the foreign container. The aliases should point to the modules own namespace. By this namespaces of two modules get connected and dedicated registered types are accessible in the foreign container. So this method describes some sort of public interface of the module.

 

The final method "register_classes" fills up the own default-namespace with classes and datatypes. Every class and datatype has to be registered there, which should be considered on construction of instances by the DI-framework for this module. Into this method you can also place factory classes which are not DI based. So you don't have to use the feature of importing modules to get access to instances of another components.

 

If you want to use a module you can do it with this line:

  DATA(lo_container) = zcl_di_factory_generic=>get_instance( io_init_module = NEW zcl_module_b( ) )->get_di_container( ).

Now you can retrieve your instance like shown in the previous examples:

  DATA lo_service TYPE REF TO zif_service_b.  lo_container->get_instance_value( CHANGING cv_target_value = lo_service ).

The benefits of using modules are that ...

  • you have a managed way how different components are connected to each other.
  • you don't have to care about creating and managing the DI-container.
  • every module is only imported once. So if n modules import the same module its content is not registered n times but just once.
  • since the module-class is used in a descriptive way, you can dynamicly replace modules.

 

Finally the DI-implementation provides a code generator which makes module creation a bit easier. So there is a configuration for the SAP "Service Implementation Workbench" (SIW) that can create the module-code and wraps it into a nice factory-class to provide a type-safe access to the DI-container and hiding the DI aspects from users. The SIW generates your factory and module-code and you just have to fill the five module-methods. In the end you have a nice working DI-based factory-class.

 

Finally

This blog could not handle every aspect of this DI-framework. But I hope I could show you the intended design approach to fit in a large component based application.

Please let me know what you think!

 

Edit (6.1.2013): Some parts in section DI-Modules

Performance analysis of long running programs in ABAP

$
0
0

Performance analysis of long running programs in ABAP:

 

The objective of this blog is to show how to analyze long running programs in ABAP. Customers often complain about some custom reports in ABAP are taking a hell lot of time during the production execution and as a consequence they frequently request to look into the performance killers and the bottlenecks.

Recently, we had a severe performance issue with one such reports in ABAP – a custom report for purchasing contracts. Customer anticipated that the report is running very slow and hence it could not be moved to the production environment. Our team of developers had the job to optimize it. They used the R/3 transaction SAT to understand the bottlenecks and the performance killers.

 

Starting with SAT:

 

We have the following report that runs for a long time. The screen-shot of the report is given below

 

1.png

 

Now we will analyze the performance of this report using the transaction code SAT. Let us run this R/3 transaction, the initial screen of SAT is displayed. Here we are creating a new variant called PERFO_VAR that will contain all the restrictions for the performance measurement.

 

2.png

 

The variant will have the following characteristics to measure the performance –

1.     Choose Per Call Position radio-button – This implies that it enables call per aggregation. This option is generally used for performance trace.

 

3.png

  The Statements tab will look like the screen-shot below:

 

4.png

 

After the creation of the variant; we will start the measurement of the transaction. Please see the screen-shot.

 

5.png

 

Once the measurement is finished; we will go to the evaluate tab to see the results & analyze. W.r.t performance analysis it is best to use the tools – Hit List & Profile to analyze the performance killers.

 

 

6.png

 

 

7.png

 

 

If we look closely at the Hit List ALV, we can see which entries took most of the time. The entry 1 consumed 79.65% of the net time. The entry 2 consumed 15.93% of the net time.  The entry 3 consumed 4.21% of the net time. Hence the first 3 entries sum up to 99.79% of the net time. We will have a detailed look at these 3 entries to understand why they took so much time. One important thing to note at this point in time is that we are mostly interested in the net time; where net time = (gross time) – (system time).

 

 

Detailed Analysis with SAT:

 

Now in the Hit List, when we double click on the 1st entry; we navigate to the code & oops this what we see –

 

1.png

 

 

Now in the Hit List, when we double click on the 2nd entry; we navigate to the code & oops this what we see –

 

2.png

 

Now in the Hit List, when we double click on the 3rd entry; we navigate to the code & oops this what we see –

 

 

3.png

 

Therefore it is clear that WAIT/SLEEP& WAIT for RFC takes the most of the time in report. They are the major performance killers.

 

Conclusion:

 

SAT is a very useful transaction to do the following things –

1.       Performance Analysis.

2.       ABAP Trace Analysis

3.       Trace Parallel process

4.       User Trace.

It is an extension of commonly used SE30. It is more sophisticated tool & very useful for analyzing production performance issues quickly & neatly.

Delete Internal Projects in SAP Project Systems

$
0
0


Hi

 

Attached Blog describes how to delete the created internal projects in SAP Project System module.

 

Regards,

Sreeram

What is ANST....and why aren't you using it?

$
0
0

ANST – also known as the Automated Notes Search Tool, is a powerful application that searches SAP notes for a specific problem based on the issue in your system.

 

 

The tool is available if you have upgraded your system to one of the following SAP basis support packages (seeKnowledge Base Article 1818192):

 

 

Software Component Release Package name

  ________________________________________________________________________

SAP Basis component

 

 

SAP_BASIS 700 SAPKB70028

 

 

SAP_BASIS 701 SAPKB70113

 

 

SAP_BASIS 702 SAPKB70213

 

 

SAP_BASIS 731 SAPKB73106

 

 

The transaction to start the tool is named ANST_SEARCH_TOOL.

 

 

NEW! By following the instructions in note 1915529, you can utilizethe shorter transaction ANST.  The functionality is exactly the same as ANST_SEARCH_TOOL, but the shorter transaction name makes it more convenient.

 

 

 

 

 

We have been sharing the news about this great tool through multiple channels.

 

 

 

 

 

SCN Blogs

 

 

Our Newest blog shows how ANST can help you solve billing problems.  A special thanks to Tobias Dolgener for sharing.

 

http://scn.sap.com/community/erp/sd/billing/blog/2013/12/06/the-power-of-tools--how-anst-can-help-you-to-solve-problems-yourself

 

 

Have an idea for new features or functionalities? Drop a comment in the following blog!

 

http://scn.sap.com/community/software-support-and-maintenance/blog/2013/09/10/the-future-of-sap-automated-note-search-tool

 

 

 

 

 

Demos/Training

 

 

We have a training system set up for you to learn how to use the tool. 

Access with S-user ID here

 

 

 

 

 

Videos

 

 

Here’s an ECC example, where a note to solve a problem is found in less than 2 minutes

 

 

 

 

 

 

User Groups/Events

 

 

Most recently, we have been presenting to various global user groups to help spread the word about the Automated Notes Search Tool.

 

 

 

 

 

Here are just some of the locations that we have presented. (Photos added if available)

 

 

Christoph Haberl presented ANST to DSAG Vienna.

 

 

Arvin Chiu presented at  the ASUG British Columbia chapter meeting

ANST arvin.png

 

 

 

 

Miguel Alfaro  and Lloyd Goveia presented at the ASUG Ontario chapter meeting.

 

ANSTmiguelandlloyd.png

Mark Richardson blogged about it on SCN.  Thanks Mark!

 

 

 

Felipe Fonseca and Tomas Black presented at the Grand Opening event in Sao Leopaldo, Brazil

ANSTfelipetomas.png

 

 

Carlos Martinez Escribano  presented to the AUSAPE (Spain SAP User Group)

 

 

Preetpal Singh presented at SAP TechEd in Bangalore. 

 

 

 

 

 

 

 

 

Enterprise Support Academy

 

 

For those of you interested in a more personal approach, we have Meet the Expert sessions on the Automated Notes Search Tool scheduled through the SAP Enterprise Support Academy.  You can register for a session (s-user ID login required) here

 

 

Upon entering the site, type in Automated in the search box and you will find the session information.

ANST ESA.png

 

 

 

 

Here is the current schedule:

 

 
22.01.2014 16:00 - 17:00 CET (CET) EMEA English

 

 


22.01.2014 10:00 - 11:00 CET (CET) EMEA German   

 

 

 

 

 

Select the one that’s best for you and register to learn more.  These Meet the Expert sessions are all recorded, so if you can't make the date, look in the replay library!

ANST MTE.png

 

 

 

 

Social Media

 

SAP Product Support is on Twitter and Facebook! 

Stay connected with us via our 2 Twitter channels:

http://twitter.com/SAPSupportCE and http://twitter.com/SAP_Gsupport

 

Like us on Facebook at http://facebook.com/sapproductsupport

 

 

Customers Spreading the Word

 

Nicolas Busson  started using the tool and has been kind enough to share his thoughts on it with the community on SCN.  Thanks Nicolas!

 

 

In the Business Trends 2013 wrap up, Somnath Manna  shares how the ANST is a hidden gem for any SAP functional consultant.  Very true!

 

 

 

Recap

There is a plethora of information available to help you start using the Automated Notes Search Tool, and SAP Product Support has experts that can help you along the way. 

 

 

Are you using ANST? Add a comment below and tell us about your experience with it.

 

 

If not, what’s stopping you?  We want to know!


FSCM: Additional Tab to Business partner through BDT Settings

$
0
0

Introduction to BDT:

The BDT (Business Data Toolset) is a central control tool for maintaining master data and simple transaction data. In addition to dialog maintenance, it also supports maintenance with direct input and/or function modules.


The BDT also provides generic services for consistently recurring requirements such as occur in change document lists, field groupings and the deletion program. It takes control over these objects as well as generic parts and calls the applications using predefined interfaces (control tables and events). The applications themselves introduce application-specific enhancements, such as writing and reading application tables


BDT settings:


Step 1: Create Module Pool Program - SAPLZFG_FSCM with screen 9001.

(Function Group: ZFG_FSCM)

 

 

Step 2: Create New Application for screen

BUPT->Business Partner->Control->Applications / t-code (BUS1)

 

Step 3: Create New Dataset for the application:

BUPT->Business Partner->Control->Data Sets / t-code (BUS23)

 

Step 4: Create field groups as required:

BUPT->Business Partner->Control->Screen Layout->Field Groups / t-code (BUS2)

 

Assign the fields to the field group

 

Step 5: Create view:

BUPT->Business Partner->Control->Screen Layout->Views / t-code (BUS3)

 

Assign the field groups to the views:

 

Step 6: Create section

Go to BUPT->Business Partner->Control->Screen Layout->Section/ t-code (BUS4)

 

Assign View to the section:

 

Step 7: Create Screen:

Go to BUPT->Business Partner->Control->Screen Layout->Screens (BUS5)

 

Assign the section to the screen


 

Step 8: Create the screen sequence:

Go to BUPT->Business Partner->Control->Screen Sequences (BUS6)

 

Select the screen sequence and click on Screen Sequence->Screens to assign the Screen.

 

 

Create a screen sequence category:

 

Selecting the Screen Sequence category, assign the screen sequence to it.


 

Step 9:

Add your screen with the standard screen sequence so that it would appear with the standard TABS.

Select the BUP001 Screen Sequence and add your screen with the required Item number.

Item Number will depict where the tab will come.


 

**************Most Important Step******************

Step 10: Create Divisibility:

BUPT->Business Partner->Control->Divisibility->BP Views (BUSD)


Add Data Set ZFSCM to standard BP View UKM000


 

Add the custom application to standard BP view UKM000

 

Output: Execute t-code“UKM_BP” and enter partner number.

 

 

Incorporating a logic flow for the validations etc. of those custom fields

To incorporate the logic for the validations of the custom fields, the PAI and the PBO of the custom program that is attached to the View can be used.

Here we attached the custom FM “ZZAPP_BUPA_PBO_ZAPP9001” to the view ZFSCM.

 

 

Updating the underlying database tables for the custom fields when the BP transaction is saved

We will need to create FMs and configure the FMs to trigger under specific SAP events.

 

The list of the events can be found from the transaction BUPT using the path:

SAP Menu -> Business Partner -> Control Data -> Events -> Business Data Toolset (BUS7)

 

 

 

In the e.g. described above, 2 FMs were created under the Function Group ‘ZGM_FSCM’.

 

These FMs were:

 

1) FM ‘ZZAPP_BUPA_EVNT_XCHNG’:

The custom FM was attached to the event the event ‘XCHNG’ (which is triggered to check whether Data Has Been changed). The application name used was ‘ZAPP’.

 

2) FM ‘ZZAPP_BUPA_EVNT_DSAVE’:

The custom FM was attached to the event ‘DSAVE’ (which is triggered to Save Data in Database (from Local Memory)). The application name used was ‘ZAPP’.

Classical way to ABAP OO style of coding

$
0
0

Object Oriented ABAP is taking slow phase in adoption for Pure ABAPers(Not working in Webdynpro or other object oriented space); even I took a yearlong to completely do my work in OO.

 

Even I was in a situation when one of my clients questioned, why you code in OO as he was not able to understand. I was dumbstruck and the thought provoked me to write this for guys who are in dilemma on how to move to OO.

 

I have seen many blogs on ABAP OO which will be a good start for learning (explaining concepts and examples) but still faced few challenges in moving to ABAP OO like where and how to start, just putting down my thoughts about the problems which faced little longer and ways I overcame

Few road blocks in using ABAP OO

o    Understanding concepts of OO who has no previous knowledge on OO

o    How to implement the same in our regular work in RICEF objects.

o    How to use major advantages of OO

 

This blog is for experienced developers, who are familiar with OO concepts but lazy in implementing the same in ABAP. Before I talk about any of these stuff, I would like to tell how to get familiarize with classes. (Note: This blog only provides approach and not any examples)

For newbies to ABAP OO, can get familiarize with these links?

 

http://help.sap.com/saphelp_nw2004s/helpdata/en/c3/225b5654f411d194a60000e8353423/frameset.htm

http://wiki.scn.sap.com/wiki/display/ABAP/ABAP+Objects+Getting+Started

 

I will show a small example report getting some entries from MARA and using CL_SALV_TABLE to display the same in ALV in four ways here

·         Traditional way of writing

·         OO way (only using Static methods) – To get the hang of class and method concept

·         OO way (Only Instance methods) – To get the hang of class and method concept

·         My IDEAL way in OO

·         New Trend of and completely moving your report to OO.

 

Traditional way of writing the code:

 

REPORT  ysdnblog_classic.
PARAMETERS : p_rows TYPE count DEFAULT '100'.
START-OF-SELECTION.
  DATA : it_mara TYPE STANDARD TABLE OF mara.
  PERFORM get_data CHANGING it_mara.
  PERFORM display USING it_mara.
*&---------------------------------------------------------------------*
*&      Form  GET_DATA
*&---------------------------------------------------------------------*
FORM get_data  CHANGING ch_mara TYPE mara_tt.
  SELECT * FROM mara INTO TABLE ch_mara  UP TO p_rows ROWS .
ENDFORM.                    " GET_DATA
*&---------------------------------------------------------------------*
*&      Form  DISPLAY
*&---------------------------------------------------------------------*
FORM display  USING    i_mara TYPE mara_tt.
  DATA : lr_table TYPE REF TO cl_salv_table.
  cl_salv_table=>factoryIMPORTING    r_salv_table   = lr_table
                           CHANGING     t_table        =   i_mara )    .
  lr_table->display( ).
ENDFORM.                    " DISPLAY

OO way (only using Static methods) – To get the hang of class and method concept

 

Let’s start with classes and don’t go in to boring part of explaining what is a STATIC or INSTANCE method (Please google or go through about this stuff). Major developers who are new to OO, first question is whether my method should be INSTANCE or STATIC? Let’s save this question to the last.

First to get the hang of the OO ABAP from traditional ABAP using STATIC methods only and above report looks like this: (suggest continuing writing couple of reports/objects)

 

REPORT  ysdnblog_class_static.
PARAMETERS : p_rows TYPE count DEFAULT '100'.
*----------------------------------------------------------------------*
*       CLASS lcl_main DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_main DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS : get_data  ,
                    display.
  PRIVATE SECTION.
    CLASS-DATA it_mara TYPE mara_tt.
ENDCLASS.                    "lcl_main DEFINITION
*----------------------------------------------------------------------*
*       CLASS lcl_main IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_main IMPLEMENTATION.
  METHOD get_data.
    SELECT * FROM mara INTO TABLE lcl_main=>it_mara  UP TO p_rows ROWS .
  ENDMETHOD.                    "GET_DATA
  METHOD display.
    DATA : lr_table TYPE REF TO cl_salv_table.
    cl_salv_table=>factoryIMPORTING    r_salv_table   = lr_table
                             CHANGING     t_table        =   lcl_main=>it_mara  )    .
    lr_table->display( ).
  ENDMETHOD.                    "display
ENDCLASS.                    "lcl_main IMPLEMENTATION

START-OF-SELECTION.

  lcl_main=>get_data( ).

  lcl_main=>display( ).

 

OK... I hope by now you got hang of what a traditional report looks in CLASS/METHODS.

 

 

OO way (Only Instance methods) – To get the hang of class and method concept

 

What’s next? Let’s see the same program with instance methods. Additional steps would be declaration of an object and instantiate it to use in your program.

 

REPORT  ysdnblog_class_instance.
PARAMETERS : p_rows TYPE count DEFAULT '100'.
*----------------------------------------------------------------------*
*       CLASS lcl_main DEFINITION
*----------------------------------------------------------------------*
CLASS lcl_main DEFINITION.
  PUBLIC SECTION.
    METHODS :   get_data  ,
                display.
  PRIVATE SECTION.
    DATA it_mara TYPE mara_tt.
ENDCLASS.                    "lcl_main DEFINITION
*----------------------------------------------------------------------*
*       CLASS lcl_main IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_main IMPLEMENTATION.
  METHOD get_data.
    SELECT * FROM mara INTO TABLE me->it_mara  UP TO P_rows ROWS .
  ENDMETHOD.                    "GET_DATA
  METHOD display.
    DATA : lr_table TYPE REF TO cl_salv_table.
    cl_salv_table=>factoryIMPORTING    r_salv_table   = lr_table
                             CHANGING     t_table        =   me->it_mara  )    .
    lr_table->display( ).
  ENDMETHOD.                    "display
ENDCLASS.                    "lcl_main IMPLEMENTATION

START-OF-SELECTION.

data : lr_main TYPE REF TO lcl_main.

create OBJECT lr_main.
  lr_main->get_data( ).
  lr_main->display( ).

 

In the above example we declare an object reference of type LCL_MAIN and have to command CREATE OBJECT to create a reference for further usage of the same in program. (The same LCL_MAIN can be declared with different names (many references) and initiated based on the requirement needs)

Please do some live programs either using with any of the above ways to really get initial kick start of OO.

 

My IDEAL way in OO

MY IDEAL way of writing the above program would be as below.


REPORT  ysdnblog_class_ideal.
parameters : p_rows type count default '100'.
*----------------------------------------------------------------------*
*       CLASS lcl_main DEFINITION
*----------------------------------------------------------------------*
CLASS lcl_main DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS : start.
  PRIVATE SECTION.
    METHODS : get_data  ,
              display.
    CLASS-DATA : lr_main TYPE REF TO lcl_main.
    DATA it_mara TYPE mara_tt.
ENDCLASS.                    "lcl_main DEFINITION
*----------------------------------------------------------------------*
*       CLASS lcl_main IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_main IMPLEMENTATION.
  METHOD start.
    CREATE OBJECT lr_main.
    lr_main->get_data( ).
    lr_main->display( ).
  ENDMETHOD.                    "start
  METHOD get_data.
    SELECT * FROM mara INTO TABLE me->it_mara  UP TO P_rows ROWS .
  ENDMETHOD.                    "GET_DATA
  METHOD display.
    DATA : lr_table TYPE REF TO cl_salv_table.
    cl_salv_table=>factoryIMPORTING    r_salv_table   = lr_table
    CHANGING     t_table        =   me->it_mara  )    .
    lr_table->display( ).
  ENDMETHOD.                    "display
ENDCLASS.                    "lcl_main IMPLEMENTATION

 

START-OF-SELECTION.

 

  lcl_main=>start( ).

 

 

 

Here we call the START method only once for a program and so I made it as a static method and one static object (LR_MAIN referencing the same class ) for dealing with rest of the business logic.  (There can be many better ways as well.. )

New Trend of and completely moving your report to OO:

 

The new way of writing the reports includes t-code to launch your report. Let’s start with T-code creation as below and select 3rd option METHOD OF A CLASS (OO TRANSACTION).

 

 

Next step navigates to below screen and un check the box OO TRANSACTION MODEL enabling another field LOCAL IN PROGRAM

 

 

 

Now provide your program name and local class name and method for the below program. Program looks like below


REPORT  ysdnblog_class_new.

 


SELECTION-SCREEN : BEGIN OF SCREEN 200.
PARAMETERS p_rows TYPE count DEFAULT '100'.
SELECTION-SCREEN : END OF SCREEN 200.

 

*----------------------------------------------------------------------*
*       CLASS lcl_main DEFINITION
*----------------------------------------------------------------------*
CLASS lcl_main DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS : start.

 

  PRIVATE SECTION.
    METHODS : get_data  ,
              display.
    CLASS-DATA : lr_main TYPE REF TO lcl_main.
    DATA it_mara TYPE mara_tt.
ENDCLASS.                    "lcl_main DEFINITION
*----------------------------------------------------------------------*
*       CLASS lcl_main IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_main IMPLEMENTATION.
  METHOD start.
BREAK-POINT.
    CALL SELECTION-SCREEN 200.
    IF sy-subrc IS INITIAL.

 

      CREATE OBJECT lr_main.
      lr_main->get_data( ).
      lr_main->display( ).
    ENDIF.

 

  ENDMETHOD.                    "start
  METHOD get_data.
    SELECT * FROM mara INTO TABLE me->it_mara  UP TO p_rows ROWS .
  ENDMETHOD.                    "GET_DATA
  METHOD display.
    DATA : lr_table TYPE REF TO cl_salv_table.
    cl_salv_table=>factoryIMPORTING    r_salv_table   = lr_table
    CHANGING     t_table        =   me->it_mara  )    .
    lr_table->display( ).
  ENDMETHOD.                    "display
ENDCLASS.                    "lcl_main IMPLEMENTATION

 

START-OF-SELECTION.

 

  lcl_main=>start( ).

 

Here you are taking control on when your selection screen should trigger. Things you need to observe in above program

·         Your selection screen is defined as  a screen with a different number, which is 200

·         You are explicitly triggering your selection screen 200 in the START method rather than giving control to framework. (Note the other events AT SELECTION SCREEN will work as usual.)

·         When the transaction is executed; first it triggers the method specified in the transaction

 

By slowly adapting to above approached you can change your coding style to OO.

 

There can be many other ways and this is just little knowledge sharing based on my experience. Comments and suggestions are welcome.

Automatic QA and release request - part 02

$
0
0

A not too long time ago...


I had detailed in previous blog about creating a tool to analyze the source code, or the best solution, using the ATC (Abap Test Cockpit).
I mentioned about the quality and wisdom of the team of programmers that grew up.

But what's the point of having an automatic verification code? What are the gains and benefits? And the worst?

Here in the company, we allocated 2 programmers to do this activity. They were checking the code, line by line, following our standard document.
When non conformities were found it was returned by mail to the developer to proceed with the correction: Much work, numerous requests daily, many similar errors, complex routines to analyze... Add to that the worst question of all: dealing with people. Identify errors and point it in source code is like to speak ill of the mother of the programmer. The human factor was the main cause of failure in this process because not all errors will be identified and not all errors identified will be corrected or treated. Yes, It is true!

Automating these validations reduces the human factor. There are no arguments against the machine... is 0 or 1. If not corrected it will stay there. The error has to be, at least, properly argued to be considered a false positive and "accepted"  by an approver.

Important to mention that the static validation does not identify 100% of the logic and performance problems . Rather, it is almost 0. To do this validation, the tests are crucial, better doing automated tests.  But it can identify points that cause crashes, dumps into production, so with praise. And this is the focus: No dumps into production, who is allocated in support can be allocated to to improve the system.



AUTO QA.jpg

The image above shows month by month - from March to November 2013 - how transport request was handled with automatic release.


  • Green   - no problems, all right - released by request owner
  • Yellow  - False positive - released by an approver
  • Red      - Urgent or no time to correct releases, authorized by boss - released by an approver


Approvers had a lot of work but it was decreasingly month by month. We analysed yellow request to create/change our patterns or to create rules to turn it in green requests. But the main focus was to reduce the red requests number.


The developer and support teams did not accept well the solution. The big problem: change past codes. It is impossible to change old codes without test all scenarios and without automated tests. Then we adjusted the solution to skip the verification on old codes when support team creates the request and only check modified code. But the developer team must have to change old codes when they changed an old program and have to test it all. Of course, case by case was analysed to did not delay the delivery of a project.


Attention to yellow requests: it appears only once. When we registered an approval the validation don't show it anymore in the future.


Other good effect was the lower number of dumps in production.


We advanced some steps and for the next year (2014) we will do it better using ATC and others SAP standard tools.


And parallel with this initiative we started the ABAP DOJO meetings, but it will be a topic for another blog...


Automated Note Search Tool (ANST) - Quick and easy how to

$
0
0

Yesterday we had an issue on an SAP implementation. I was sure that it was a standard error, because in other implementation the functionality works perfectly.

 

On Twitter Nicolas Busson reminded me that ANST can save your life quickly and easily

 

Tweet.PNG

This video is the live implementation of the OSS Note using ANST.

 

Enqueues and Update Tasks

$
0
0

What help.sap.com doesn't tell us

I thought, SAP is pretty clear in what they tell us about the _SCOPE-parameter that can be passed to every enqueue-function modul.

The parameter can have 3 values, which means, according to help.sap.com:

 

Meaning of the _SCOPE Values

Value

Description

_SCOPE = 1

The lock belongs only to the dialog owner (owner_1), and therefore only exists in the dialog transaction. The DEQUEUE call or the end of the transaction, notCOMMIT WORKorROLLBACK WORK,cancels the lock.

_SCOPE = 2

The lock belongs to the update owner (owner_2) only. Therefore, the update inherits the lock whenCALL FUNCTION ‘…‘ IN UPDATE TASKandCOMMIT WORKare called. The lock is released when the update transaction is complete. You can release the lock before it is transferred to the update usingROLLBACK WORK.COMMIT WORKhas no effect, unlessCALL FUNCTION ‘…‘ IN UPDATE TASKhas been called.

_SCOPE = 3

The lock belongs to both owners (owner_1 and owner_2). In other words, it combines the behavior of both. This lock is canceled when the last of the two owners has released it.

 

_SCOPE Parameter (SAP Library - Architecture Manual)

 

The most interesting statement is the description for _SCOPE = 2. It says that the lock is released when the update transaction is complete.

So I understood that COMMIT WORK will implicitly release the lock. As I found out, it is not necessarily the case.

 

The test report

Consider the following test report.

The report is going to request a lock for an ABAP report. The lock argument is '1' as a dummy value. Keep in mind, that Z_UPDATE_TASK_DUMMY is an update function module that has no execution logic in my case.

 

DATA lt_enq TYPE TABLE OF seqg3.
FIELD-SYMBOLS <ls_enq> TYPE seqg3.
PARAMETERS pa_upd TYPE abap_bool AS CHECKBOX.
CALL FUNCTION 'ENQUEUE_ES_PROG'   EXPORTING     name           = '1'     _scope         = '2'   EXCEPTIONS     foreign_lock   = 1     system_failure = 2     OTHERS         = 3.
IF sy-subrc <> 0.   MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
IF pa_upd = abap_true.   CALL FUNCTION 'Z_UPDATE_TASK_DUMMY' IN UPDATE TASK.
ENDIF.
COMMIT WORK AND WAIT.
CALL FUNCTION 'ENQUEUE_READ'   EXPORTING     gclient               = sy-mandt     guname                = sy-uname   TABLES     enq                   = lt_enq   EXCEPTIONS     communication_failure = 1     system_failure        = 2     OTHERS                = 3.
IF sy-subrc <> 0.   MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ELSE.   IF lt_enq IS INITIAL.     WRITE 'No locks existing'.   ELSE.     LOOP AT lt_enq ASSIGNING <ls_enq>.       WRITE: <ls_enq>-gname,               <ls_enq>-garg,               <ls_enq>-gmode.       NEW-LINE.     ENDLOOP.   ENDIF.
ENDIF.

The report offers a parameter PA_UPD to the user. If checked, the update function module will be called. If not checked, there is no update task registered. Afterwards, COMMIT WORK AND WAIT is always triggered.

In the end, the report shows any locks that the current user has.

 

The ugly truth

If the report is called with the enabled checkbox, the dummy function module will be called. In the end, all locks are released.

No Locks existent.PNG

 

If the report should not execute any update function module (checkbox is deactivated), the locks are not released at all. COMMIT WORK AND WAIT has no influence here as there is no update task registered. I also included the output of SM12 in the screenshot.

No Locks released.PNG

 

The moral of the story

Locks of the update owner are not necessarily released, when COMMIT WORK has been called. It depends on wether or not an update task has been called at all. help.sap.com is not wrong with the description about _SCOPE = 2, however, someone who reads the description might get a wrong idea of how ABAP works in this very case. It took me some time to figure out what happens here. So I hope this blog post helps other developers saving some time.

Viewing all 948 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>