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 |