The SAP Internet Programming Model Part II: Writing An Internet Application With ITS
The SAP Internet Programming Model Part II: Writing An Internet Application With ITS
Parameter Name
BAPIEKKOC-VENDOR
BAPIEKET-DELIV_DATE
BAPIEKET-QUANTITY
BAPIEKPOC-UNIT
BAPIEKPOC-PUR_MAT
BAPIEKPOC-NET_PRICE
BAPIEKPOC-PRICE_UNIT
BAPIEKPOC-DOC_TYPE
APIEKPOC-PURCH_ORG
BAPIEKPOC-PUR_GROUP
BAPIEKPOC-STORE_LOC
BAPIEKPOC-CO_CODE
BAPIEKPOC-DOC_DATE
Table 1. Parameters for BAPI_PO_CREATE
Meaning
Vendor delivering goods
Delivery date
Quantity
Unit of measure
Material number
Net price
Price unit
Document type
Purchasing organization
Purchasing group
Storage location
Company Code
Document date
Provided By
User
User
User
User
User
User
User
User Default BSA
User Default EKO
User Default EKG
User Default LAG
Calculated
Calculated
To continue writing the transaction, we need to provide ABAP variables that will hold the values
the user enters (see Figure 2). An element on the R/3 screen holds a link to an ABAP variable
with the same name, which means that it displays the current value and updates it whenever the
user enters something.
We should name the ABAP variables exactly like the BAPI parameter so that the R/3
Screenpainter will recognize the name as an ABAP Dictionary field and copy the attributes from
the Dictionary. Naming the variables this way has several advantages:
The Screenpainter will place the item description from the ABAP Dictionary next to the entry
fields. If table controls are used, the item description will appear in the table header. Because the
ABAP Dictionary stores a translation of all item descriptions, you wont have to translate the item
descriptions yourself; users logging on in English will see English descriptions, users logging on
in French will see French descriptions. You only have to display these descriptions on the HTML
page, which is very easy with ITS.
The format description from the ABAP Dictionary will be used automatically. This means date
fields will be displayed and can only be entered with the date format that is set in the user
masteryou will not have to program your input format checking and display formatting
yourself.
If the ABAP Dictionary stores a value table for the data field, users will be allowed to enter only
values that are also in the value table. This is helpful when entering a material number, because
the ABAP Dictionary will immediately check whether the material number the user entered is
valid.
program
zzpocreate.
* BAPI structures
tables:
bapiekpoc,
bapieket,
bapiekkoc,
For the table control, we need two internal tables that will hold
the data entered by the user. Heres how to create them and to
declare the table control:
* Table controls
controls po_item_ctrl type tableview using screen 1000.
If everything goes well, the BAPI will return the purchase order
number. If there is a problem, a detailed error log will be
contained in the variable BAPIRETURN. Lets now create these two
variables:
* BAPI results
data: po_number
data: bapireturn
like bapiekkoc-po_number.
like bapireturn occurs 0 with header line.
The first module is simple: It only creates 40 records in the tables PO_ITEMS and
PO_ITEM_SCHEDULES. PO_ITEMS contains item data such as the material number, and
PO_ITEM_SCHEDULES contains information such as when the user would like to have the
material shipped. These 40 records will initially display 40 empty lines in the table control.
module initialize_app output.
if app_initialized = 0.
app_initialized = 1.
do 40 times.
po_items-po_item = sy-index.
po_item_schedules-po_item = sy-index.
append po_items.
append po_item_schedules.
enddo.
endif.
endmodule.
The Process After Input (PAI) modules process the data entered by the user. PAI module
CTRL_TO_ITEM is the reverse operation of ITEM_TO_CTRL; it copies the data the user
entered into the table control back to the internal tables. Again, the code is simple:
module ctrl_to_item input.
* ----------------- Update Item Table ------------------read table po_items
index po_item_ctrl-current_line.
move bapiekpoc-unit
to po_items-unit.
move bapiekpoc-pur_mat
to po_items-pur_mat.
move bapiekpoc-net_price to po_items-net_price.
move bapiekpoc-price_unit to po_items-price_unit.
modify po_items
index po_item_ctrl-current_line.
* ----------------- Update Schedule Table --------------read table po_item_schedules index po_item_ctrl-current_line.
move bapieket-deliv_date to po_item_schedules-deliv_date.
move bapieket-quantity
to po_item_schedules-quantity.
modify po_item_schedules
index po_item_ctrl-current_line.
endmodule.
" CTRL_TO_ITEM INPUT
Finally, here comes the real stuff: Lets create a pushbutton on the screen with the function code
SAVE. Once thats created, you need to put the variable FCODE into the screen field OK-CODE
so that the function code of the button is automatically copied into the FCODE variable. Listing 1
shows the code that processes the SAVE function code in the module PROCESS_FCODE.
This code contains a few items that require more explanation. First, notice that todays date and
the user creating the purchase order are automatically determined from the ABAP variables SYDATUM and SY-UNAME. Furthermore, parameter IDs are retrieved from the R/3 user
management with the ABAP command GET PARAMETER ID. Also, we determine the company
code from plant based on the customizing table T001K.
I should also explain why the item and schedule tables are copied into two other internal tables.
The answer is simple: Remember that we had set up the item and schedule table with 40 empty
records? Because the user has not entered data into all these records, we filter out records where
data is present and copy only those records into the tables PO_ITEMS2 and
PO_ITEM_SCHEDULES2, which are then passed to the BAPI. You could have created the
purchase order from the original tables as well, but you would have received warnings for every
empty line item. To avoid this, filter out the empty records.
The last step is to create a transaction code ZZPO, then we are ready to test our application. We
start the transaction in SAPGUI and use the ABAP debugger if we discover any problems.
= bapiekkoc
= po_number
= po_number
= po_items2
= po_item_schedules2
= bapireturn
= 1.
endif.
clear fcode.
endmodule.
" PROCESS_FCODE
Use the File->New menu to create a project; lets call our example MyProject. Next, use
File->New again and go to the File Wizard tab. There, choose the Service Wizard
and follow all the steps. First, you are asked for a service name. Since the transaction is called
ZZPO, lets also call our service ZZPO. Next, specify the R/3 system you want to connect to. You
can also use the global settings if you specified a specific R/3 system in the ITS setup. The same
rule applies to the R/3 login and password; you can either use the login that you specified in the
ITS setup or give a service-specific login. For this example, however, we wont give a username
or password because we want users to log on interactively once they start our electronic
purchasing application (see Figure 6).
Finally, specify a timeout (the period of inactivity after which ITS will log a user off the system)
and the transaction code ZZPO. Once confirmed, the service is created.
Now we have to create a template for screen 1000. A template is basically an HTML page into
which ITS merges the data fields from the R/3 screen. ITS makes it possible for a static HTML
page to contain live R/3 data. If the page contains an HTML form with input fields, ITS can also
update the R/3 screen values from the user input in the HTML page.
Use SAP@Web Studios File->New option again and choose the HTML Business
Template option under the Files tab. Pick the service name ZZPO; enter program name
ZZPOCREATE, screen number 1000, and theme 99but no language. (The theme is an ITS
concept that lets you keep several HTML user interfaces for the same transaction; theme 99 is the
default.) SAP@Web Studio will create a file called ZZPOCREATE_ 1000.html, which is formed
by concatenating the program name and the screen number.
Merging live R/3 data into the page is simple: Suppose you have a field DATAFIELD on your
R/3 screen; all you have to do is enclose your field name in back apostrophe symbols. A very
simple HTML page would look like this:
The function wgateURL() creates a valid Web address (URL); you dont have to worry about
the technical details. Also notice that the SUBMIT button sends the function code UPDA along
with the data back to R/3.
Displaying a table control can be more complicated. Suppose table control T has multiple lines, T
has a datafield X , and ITS needs to display every single line of T. Because we dont know what
part of the data set is displayed by T while writing the HTML template, ITS makes this
information available to the HTML template at execution time. T.firstVisible contains the
first visible record number. T.lastVisible contains the last visible record number.
Listing 2 shows how to create a template for screen 1000. The template contains some elements
we havent talked about yet:
The variable ~messageLine contains a value if the R/3 screen displays a message.
The table navigation buttons work a little differently than buttons that only send a function code;
you need to specify the table control with the ~control parameter and the desired navigation in
the ~event parameter.
The column headers of table control T are contained in the variables T-COLUMN[i].title.
The number of columns contained in table control T is in the variable T.columnCount.
The width of an entry field F on the SAP screen is contained in the variable F.maxsize.
<HTML>
<FORM ACTION="`wgateURL()`" METHOD="POST">
`~MessageLine`
<INPUT TYPE="SUBMIT" NAME="~OkCode(/0)" VALUE="Update">
<INPUT TYPE="SUBMIT" NAME="~Control=PO_ITEM_CTRL,~Event=TopPage"
VALUE="Top Page">
<INPUT TYPE="SUBMIT" NAME="~Control=PO_ITEM_CTRL,~Event=PrevPage"
VALUE="Previous Page">
<INPUT TYPE="SUBMIT" NAME="~Control=PO_ITEM_CTRL,~Event=NextPage"
VALUE="Next Page">
<INPUT TYPE="SUBMIT" NAME="~Control=PO_ITEM_CTRL,~Event=LastPage"
VALUE="Last Page">
<TABLE BORDER=1>
<TR>
`repeat with i from 1 to PO_ITEM_CTRL.columnCount`
<TD><B>`PO_ITEM_CTRL-COLUMN[i].title`</B></TD>
`end`
</TR>
`repeat with i from PO_ITEM_CTRL.firstVisible to
PO_ITEM_CTRL.lastVisible`
<TR>
<TD><INPUT TYPE="TEXT"
NAME="BAPIEKET-DELIV_DATE[`i`]"
VALUE="`BAPIEKET-DELIV_DATE[i]`"
SIZE="`BAPIEKET-DELIV_DATE.maxsize`"></TD>
<TD><INPUT TYPE="TEXT"
NAME="BAPIEKET-QUANTITY[`i`]"
VALUE="`BAPIEKET-QUANTITY[i]`"
SIZE="`BAPIEKET-QUANTITY.maxsize`"></TD>
<TD><INPUT TYPE="TEXT"
NAME="BAPIEKPOC-UNIT[`i`]"
VALUE="`BAPIEKPOC-UNIT[i]`"
SIZE="`BAPIEKPOC-UNIT.maxsize`"></TD>
<TD><INPUT TYPE="TEXT"
NAME="BAPIEKPOC-MATERIAL[`i`]"
VALUE="`BAPIEKPOC-MATERIAL[i]`"
SIZE="`BAPIEKPOC-MATERIAL.maxsize`"></TD>
<TD><INPUT TYPE="TEXT"
NAME="BAPIEKPOC-NET_PRICE[`i`]"
VALUE="`BAPIEKPOC-NET_PRICE[i]`"
SIZE="`BAPIEKPOC-NET_PRICE.maxsize`"></TD>
<TD><INPUT TYPE="TEXT"
NAME="BAPIEKPOC-PRICE_UNIT[`i`]"
VALUE="`BAPIEKPOC-PRICE_UNIT[i]`"
SIZE="`BAPIEKPOC-PRICE_UNIT.maxsize`"></TD>
</TR>
One of the side effects of entering an invalid date is that all other lines are blocked from input to
alert the user to correct the input error before entering further data. Although SAPGUI blocks the
data input automatically, the HTML page does notall fields remain ready for input. You can
make the HTML page reflect the SAPGUI screen change in one simple step, though. For any
input field, the attribute .disabled signals whether or not an input field is ready.
With a little HTML template magic, we can also block the HTML input fields when the screen
input fields are blocked by putting an if statement around the HTML input fields:
`if (BAPIEKET-DELIV_DATE[i].disabled )`
<!-- only display the value because
field is not ready for input -->
`BAPIEKET-DELIV_DATE[i]`
`else`
<!-- field is ready for input -->
<INPUT TYPE="TEXT"
NAME="BAPIEKET-DELIV_DATE[`i`]"
VALUE="`BAPIEKET-DELIV_DATE[i]`"
SIZE="`BAPIEKET-DELIV_DATE.maxsize`">
`endif`
Also, you may have noticed that the table control navigation buttons currently display only
English text. To make the purchasing application a multilanguage application, display the button
labels in the appropriate language. You can create buttons on the SAP screen and let SAP do the
translation, but this is a bit cumbersome.
ITS uses a concept called language resources for translating Internet applications. A language
resource (which you can create with SAP@Web Studio) is basically a short text that can be
identified with an abbreviation in the HTML template. Use the menu item File->New to bring
up the dialog for creating new objects, choose the File Wizards tab, then choose Resource
Wizard. SAP@Web Studio will bring up a dialog where you can enter your service ZZPO, theme
99, and the language key (for example, De). (See Figure 9.) Once you confirm the dialog,
SAP@Web Studio will create an empty language resource file that you can fill with the necessary
short texts.
Using language resources in the HTML template is easy; just refer to the text by prefixing the
identifier with a # mark:
And thats it! Weve created a scalable multilingual intranet purchasing application that is ready
for use.
Michael Bechauf is development manager in the SAP ITS group. He has worked for SAP in
various development positions and as a specialist for R/3 performance since 1992. You can reach
him at michael.bechauf@sap-ag.de.