Computerservice Wolfooo

 

SAP ITS Flow Logic salesorder creation

o Schulung o Consulting o Programmierung o Links o Kontakt

General information

SAP gives you the possibility of accessing SAP R/3 systems over the World Wide Web through the Internet Transaction Server (ITS). For implementation you can couse from 3 programming models :

  • SAP GUI for HTML:
    Representation of the SAP Frontends over a Internetbrowser. In addition a user identifier is necessary in the SAP system. The complexity of SAP is not hidden.
  • Easy Web Transaction:
    Conversion of dynpros to HTML pages by the ITS. Flow and Buissines logic is programmed completely in ABAB. For use no SAP is user identifier is requiered. Simple operation realizable. High load on the SAP system.
  • ITS Flow Logic:
    Control logic is implemented on the ITS. Data from the SAP system can be read from and written back by BAPI's and RFC's.

Requirements

Company A has a product catalog in the Internet, which is implemented in PHP and MySQL. This catalogue should be extended by the possibility of online order. Since company A uses SAP as their ERP system, and the product catalog is already coupled to SAP, also the orders should be transferred to SAP whithout any media break. SAP ITS can be used to do this.

Preparation

  • Download and installation of the ITS:
    Publicly accessible page: http://www.sapmarkets.com/its/, the Downloads offered here, are unfortunately not up to date. If you can acess sapservX, you find the ITS in the directory /general/its.
  • Installation of the SAP@Web Studio:
    For SAP R/3 < 4.6C the ITS can be programmed only over the SAP@Web Studio. Starting from the 4.6C Release you can use the Web Application builder, which is integrated into the ABAP Workbench. You find the SAP@Web studio under the same address as the ITS server.

Implementation

Data transfer from the product catalog

As interface between the product catalog and the ITS Flow Logic application, I use a HTML form, which is sent by HTTP POST to the ITS server:

<form ACTION="http://its.domain.de/scripts/wgate/z_order_in/!?~language=en"
        METHOD="POST">
  <input type="hidden"
         name="ORDER_ITEMS_IN-REQ_QTY[1]"
         value="2000">
  <input type="hidden"
         name="ORDER_ITEMS_IN-MATERIAL[1]"
         value="539744">
  <input type="submit"
         name="bestellen"
         value="Bestellen">
</form>

Here only the data necessary for to proccess the order are transferd. That are the number of items (REQ_QTY) and materials number (MATERIAL). The table ORDER_ITEMS_IN is used for that. Each position must be provided with an index. It starts at 1.

Definition of the ITS Service

On the opposite side to the form on the catalog page, in the ITS a service called z_order_in was defined. The Service file has the following content:

~xGateway        sapxginet
~language  
~initialTemplate login
~theme           99

~xGateway must be set to sapxginet, because we want to implement a Flow Logic application. The parameter ~language is not set, so that this parameter can be set to the value specified in the requesting URL. With he ~language parameter we can make our application language independent through language resources files. The ~initialTemplate is set to login so this template is called as start page. The customer must login before he can place his order. This start page is situated in the theme 99.

Log-on at the SAP R/3 as an Internet user

The login page asks the customer to enter his customer number and the password. The password of the customer is maintained throu SAP R/3 via the transaction SU05. In addition to the customer number and password we must pass the ordered quantity and material number through hidden fields.

<html>
<head>
   <title>`#order_in_login`</title>
</head>
<body>
<form method=post action=`wgateURL()`> 
<h2>`#Eingabe`</h2>
<table>
  <tr>
    <td>`#Kundennummer`</td>
  <td><input type=text name="CUSTOMERNO" value="`CUSTOMERNO`"></td>
</tr>
<tr>
  <td>`#Passwort`</td>
  <td><input type=password name="PASSWORD"></td>
</tr>
<tr>
  <td>
  <input type="hidden" name="SALES_ORGANIZATION" value="1000">
  <input type="hidden" name="DISTRIBUTION_CHANNEL" value="10">
  <input type="hidden" name="DIVISION" value="10">
  `repeat with i from 1 to ORDER_ITEMS_IN-MATERIAL.dim`
  <input type="hidden" name="ORDER_ITEMS_IN-MATERIAL[`i`]" 
                       value="`ORDER_ITEMS_IN-MATERIAL[i]`">
  <input type="hidden" name="ORDER_ITEMS_IN-REQ_QTY[`i`]" 
                       value="`ORDER_ITEMS_IN-REQ_QTY[i]`">
  `end`
  <input type="submit" name="~event=login" value="`#login`">
  </td>
</tr> 
</table>
</form>
<h2>`#Ausgabe`</h2>
`RETURN-MESSAGE`<br>
</body>
</html>

By sending this form the event "login" is called. The control logic is represented by the following Flow Logic source:

<flow>
  <event name="login" next_state="checkCustomerNumber">
  </event>
  <state name="checkCustomerNumber">
    <module name="BAPI_CUSTOMER_CHECKEXISTENCE" stateful="0" type="RFC">
      <result next_state="checkPassword">
        <!--Kundennummer ist Richtig-->
        <expr>
          RETURN-TYPE=="" 
        </expr>
      </result>
      <persistent name="CUSTOMER_NUMBER_OUT"/>
      <persistent name="SALES_ORGANIZATION"/>
      <persistent name="DISTRIBUTION_CHANNEL"/>
      <persistent name="DIVISION"/>
      <persistent name="lang"/>
      <persistent name="javascript"/>
      <persistent name="session"/>
    </module>
  </state>
  <state name="checkPassword">
    <module name="BAPI_CUSTOMER_CHECKPASSWORD" stateful="0" type="RFC">
      <result next_template="simulate">
        <!--Passwort stimmt-->
        <expr>
          RETURN-TYPE=="" 
        </expr>
      </result>
    </module>
  </state>
</flow>

First the BAPI BAPI_CUSTOMER_CHECKEXISTENCE is called, in order to check whether the customer number exists. If this is true, the password is checked with BAPI_CUSTOMER_CHECKPASSWORD and if this is although true the template "simulate" is called. In all other cases the error message of the system is displayed through the structure RETURN-MESSAGE.

Salesorder simulate

If the template "simulate" is called, the customer order is simulated, and the return iof the table ORDER_ITEMS_OUT is displayed. This inclues the customer specific price. This is the source code of the template:

<html>
<head>
   <title>`#simulate_oder`</title>
</head>
<body>
RETURN-TYPE: `RETURN-TYPE`<br>
RETURN-MESSAGE: `RETURN-MESSAGE`<br>
RETURN-CODE: `RETURN-CODE`<br>
<table border="1">
<tr>
  <td>`#material`</td>
  <td>`#quantity`</td>
  <td>`#price`</td>
</tr>
`repeat with i from 1 to ORDER_ITEMS_OUT-MATERIAL.dim`
<tr>
  <td>`ORDER_ITEMS_OUT-MATERIAL[i]`</td>
  <td>`ORDER_ITEMS_OUT-REQ_QTY[i]`</td>
  <td>`ORDER_ITEMS_OUT-SUBTOTAL_2[i]/1000`</td>
</tr>  
`end`
</table>
<form action="`wgateURL()`" method="POST">
`repeat with i from 1 to ORDER_ITEMS_IN-MATERIAL.dim`
  <input type="hidden" name="ORDER_ITEMS_IN-MATERIAL[`i`]" 
                       value="`ORDER_ITEMS_IN-MATERIAL[i]`">
  <input type="hidden" name="ORDER_ITEMS_IN-REQ_QTY[`i`]" 
                       value="`ORDER_ITEMS_IN-REQ_QTY[i]`">
`end`
  <input type="submit"
		 name="~event=shipto"
		 value= "`#shipto`">
  <input type="submit"
		 name="~event=order"
		 value = "`#order`">
</form>
</body>
</html>

To start the salesorder simulation with BAPI_SALESORDER_SIMULATE, many additional fields in the structure ORDER_HEADER_IN and the tables ORDER_PARTNERS and ORDER_ITEMS_IN must be filled. Some fields must be filled with the current date. To get the current date the RFC Z_P6B_RFC_GET_DATETIME is used:

function z_p6b_rfc_get_datetime.                                        
*"----------------------------------------------------------------------
*"*"Lokale Schnittstelle:                                               
*"       IMPORTING                                                      
*"             VALUE(DAYSPAST) LIKE  MARA-MATNR DEFAULT 0               
*"             VALUE(TIMEPAST) LIKE  MARA-MATNR DEFAULT 0               
*"       EXPORTING                                                      
*"             VALUE(DATE) LIKE  SY-DATUM                               
*"             VALUE(TIME) LIKE  SY-UZEIT                               
*"----------------------------------------------------------------------
* Function: Returns the current system date and time 
*           with the possibility to calculate earlier Dates
*"----------------------------------------------------------------------
                                                                        
  date = sy-datum - dayspast.                                           
  time = sy-uzeit - timepast.                                           
                                                                        
endfunction.

The tables and structures are filled through the HTMLBuissines script init:

`
ORDER_HEADER_IN-DOC_TYPE = "ZTA";
ORDER_HEADER_IN-SALES_ORG = SALES_ORGANIZATION;
ORDER_HEADER_IN-DISTR_CHAN = DISTRIBUTION_CHANNEL;
ORDER_HEADER_IN-DIVISION = DIVISION;
ORDER_HEADER_IN-CD_VALUE1 = "0.00";
ORDER_HEADER_IN-CD_VALUE2 = "0.00";
ORDER_HEADER_IN-CD_VALUE3 = "0.00";
ORDER_HEADER_IN-CD_VALUE4 = "0.00";
ORDER_HEADER_IN-PRICE_DATE = DATE;
ORDER_HEADER_IN-QT_VALID_F = DATE; 
ORDER_HEADER_IN-QT_VALID_T = DATE;
ORDER_HEADER_IN-CT_VALID_F = DATE; 
ORDER_HEADER_IN-CT_VALID_T = DATE;
ORDER_PARTNERS-PARTN_ROLE[1] = "AG"; 
ORDER_PARTNERS-PARTN_NUMB[1] = CUSTOMER_NUMBER_OUT;
ORDER_PARTNERS-ITM_NUMBER[1] = "000000";
if (ORDER_PARTNERS-NAME[2] != "")
  ORDER_PARTNERS-PARTN_ROLE[2]="WE";
  ORDER_PARTNERS-PARTN_NUMB[2]=CUSTOMER_NUMBER_OUT;
  ORDER_PARTNERS-ITM_NUMBER[2]="000000";
end;
repeat with i from 1 to ORDER_ITEMS_IN-MATERIAL.dim
  ORDER_ITEMS_IN-COND_VALUE[i] = "0.00";
  ORDER_ITEMS_IN-COND_VAL1[i] = "0.00";
  ORDER_ITEMS_IN-CD_VALUE1[i] = "0.00";
  ORDER_ITEMS_IN-CD_VALUE2[i] = "0.00"; 
  ORDER_ITEMS_IN-CD_VALUE3[i] = "0.00";
  ORDER_ITEMS_IN-CD_VALUE4[i] = "0.00";
end;
`

After the successful simulation of the salesorder, the customer has the possibility to order either directly or to set a differing ship-to-address. Here is entire flow source code:

<flow>
  <state name="simulate">
    <module name="BAPI_SALESORDER_SIMULATE" stateful="0" type="RFC">
    </module>
  </state>
  <state name="init">
    <module name="init" stateful="0" type="SCRIPT">
      <default next_state="simulate">
      </default>
    </module>
  </state>
  <event name="ontouch" next_state="datum_holen">
  </event>
  <state name="datum_holen">
    <module name="Z_P6B_RFC_GET_DATETIME" stateful="0" type="RFC">
      <default next_state="init">
      </default>
    </module>
  </state>
  <event name="order" next_template="bestellen">
  </event>
  <event name="shipto" next_template="lieferanschrift">
  </event>
</flow>

Set a differing ship-to-address

Through the following form a differing ship-to-address can be set. Again the material number and the order quantity must be transferred in hidden fields.

<html>
<head>
   <title>`#shipto`</title>
</head>
<body>
<form action="`wgateURL()`" method="POST">
`repeat with i from 1 to ORDER_ITEMS_IN-MATERIAL.dim`
  <input type="hidden" name="ORDER_ITEMS_IN-MATERIAL[`i`]" 
                       value="`ORDER_ITEMS_IN-MATERIAL[i]`">
  <input type="hidden" name="ORDER_ITEMS_IN-REQ_QTY[`i`]" 
                       value="`ORDER_ITEMS_IN-REQ_QTY[i]`">
`end`
<table>
<tr>
  <td>Name1:</td>
  <td><input type=text
		name="ORDER_PARTNERS-NAME[2]"
		maxlength=35></td>
</tr>
<tr>
  <td>Name2:</td>
  <td><input type=text
		name="ORDER_PARTNERS-NAME_2[2]"
		maxlength=35></td>
</tr>
<tr>
  <td>Name3:</td>
  <td><input type=text
		name="ORDER_PARTNERS-NAME_3[2]"
		maxlength=35></td>
</tr>
<tr>
  <td>Name4:</td>
  <td><input type=text
		name="ORDER_PARTNERS-NAME_4[2]"
		maxlength=35></td>
</tr>
<tr>
  <td>Straße:</td>
  <td><input type=text
		name="ORDER_PARTNERS-STREET[2]"
		maxlength=35></td>
</tr>
<tr>
  <td>PLZ:</td>
  <td><input type=text
		name="ORDER_PARTNERS-POSTL_CODE[2]"
		maxlength=10></td>
</tr>
<tr>
  <td>Ort:</td>
  <td><input type=text
		name="ORDER_PARTNERS-CITY[2]"
		maxlength=35></td>
</tr>
<tr>
  <td>Land:</td>
  <td><input type=text
		name="ORDER_PARTNERS-COUNTRY[2]"
		value="DE"
		maxlength=3></td>
</tr>
</table>
  <input type="submit" name="~event=order" value="`#order`">
</form>
</body>
</html>

After submitting the form the template "bestellen" is called to place the salesorder:

<flow>
  <event name="order" next_template="bestellen">
    <!--*FlowDesigner bounds (-100, 8, 100, 10)*-->
  </event>
</flow>

Salesorder

After the successful salesorder all order data and also the order number are displayed:

<html>
<head>
   <title>`#order`</title>
</head>
<body>
<pre>
SOLD_TO_PARTY-NAME:       `SOLD_TO_PARTY-NAME`
SOLD_TO_PARTY-STREET:     `SOLD_TO_PARTY-STREET`
SOLD_TO_PARTY-POSTL_CODE: `SOLD_TO_PARTY-POSTL_CODE`
SOLD_TO_PARTY-CITY:       `SOLD_TO_PARTY-CITY`

SHIP_TO_PARTY-NAME:       `SHIP_TO_PARTY-NAME`
SHIP_TO_PARTY-STREET:     `SHIP_TO_PARTY-STREET`
SHIP_TO_PARTY-POSTL_CODE: `SHIP_TO_PARTY-POSTL_CODE`
SHIP_TO_PARTY-CITY:       `SHIP_TO_PARTY-CITY`

<table border="1">
<tr>
  <td>`#role`</td>
  <td>`#Kundennummer`</td>
  <td>`#item_numb`</td>
  <td>`#name`</td>
  <td>`#street`</td>
  <td>`#zip`</td>
  <td>`#city`</td>
</tr>
`repeat with i from 1 to ORDER_PARTNERS-NAME.dim`
<tr>
  <td>`ORDER_PARTNERS-PARTN_ROLE[i]`</td>
  <td>`ORDER_PARTNERS-PARTN_NUMB[i]`</td>
  <td>`ORDER_PARTNERS-ITM_NUMBER[i]`</td>
  <td>`ORDER_PARTNERS-NAME[i]`</td>
  <td>`ORDER_PARTNERS-STREET[i]`</td>
  <td>`ORDER_PARTNERS-POSTL_CODE[i]`</td>
  <td>`ORDER_PARTNERS-CITY[i]`</td>
</tr>  
`end`
</table>

RETURN-TYPE:              `RETURN-TYPE`
RETURN-MESSAGE:           `RETURN-MESSAGE`
RETURN-CODE:              `RETURN-CODE`
`#salesdocument`:         `SALESDOCUMENT`
</pre>
</body>
</html>

To place th salesorder, BAPI_SALESORDER_CREATEFROMDATA is called. This RFC has the same interface as the BAPI_SALESORDER_SIMULATE, so we nead to call the script "init". In the script it is also checked whether a deviating ship-to-address was entered, which must be supplied with additional values accordingly.

<flow>
  <state name="simulate">
    <module name="BAPI_SALESORDER_CREATEFROMDATA" stateful="0" type="RFC">
    </module>
  </state>
  <state name="init">
    <module name="init" stateful="0" type="SCRIPT">
      <default next_state="simulate">
      </default>
    </module>
  </state>
  <event name="ontouch" next_state="datum_holen">
  </event>
  <state name="datum_holen">
    <module name="Z_P6B_RFC_GET_DATETIME" stateful="0" type="RFC">
     <default next_state="init">
      </default>
    </module>
  </state>
</flow>

Unfortunately the funcion BAPI_SALESORDER_CREATEFROMDATA has an error in release 4.0B. The error is that if a deviating ship-to-address is indicated, all data is transferred correctly, but not the zip code. The zip code is takten from the customer placing the order The solution is described in the OSS note No. 0441210


© 2002 Computerservice Wolf - all rights reserved. webmaster@computerservice-wolf.com