Wednesday, August 25, 2010

Create web service from SAP function modules (tcode SE37) and consume it in .NET (C#)


This wiki explains how to create web service from function modules (tcode SE37) and consume it in .NET (C#). This method usually is used when a non-SAP system (such as .NET) wants to interact with SAP. For example : for posting to FI. Let's follow the steps :


1. Create Function modules in SE 37. Go to tcode SE37. Enter the name of function modules and click button Create



2. Enter description for this function module and Set the attributes to "Remote Enabled Module". if you want to generate web service, you must set the processing type to "Remote Enabled Module"


3. Go to the next tab. Click "Import" Tab. Enter Import parameter needed. You must tick "Pass by value" check box in function modules that will be used as web service. In this case the associated type for Header is ZUST_AGRI_POST_CH_VAL_HEADER.


here is the structure of ZUST_AGRI_POST_CH_VAL_HEADER.



4. Go to next tab. Click "Export" tab. Don't forget to tick "Pass by value" checkbox like step 3


5. "Changing" tab is empty. Go to next tab. Click "Tables" tab.


Here is the structure of ZUST_AGRI_POST_CH_VAL_ITEM


6. Go to the last tab. Click "Source code" tab. Copy these codes.



 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
FUNCTION ZUAGRICH_POST_AMOUNT.
*"----------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" VALUE(HEADER) TYPE ZUST_AGRI_POST_CH_VAL_HEADER
*" EXPORTING
*" VALUE(COMMIT_RETURN) LIKE BAPIRET2 STRUCTURE BAPIRET2
*" TABLES
*" GL_ACCOUNT STRUCTURE ZUST_AGRI_POST_CH_VAL_ITEM
*" AMOUNT TYPE RE_T_BAPIACCR09
*" RETURN STRUCTURE BAPIRET2
*" ACC_PAYABLE TYPE RE_T_BAPIACAP09
*"----------------------------------------------------------------------
DATA : lt_gl_account TYPE re_t_bapiacgl09,
ld_gl_account TYPE bapiacgl09,
ld_gl_account2 TYPE bapiacgl09,
lt_accountreceivable TYPE re_t_bapiacar09,
lt_accountpayable TYPE re_t_bapiacap09,
lt_currencyamount TYPE re_t_bapiaccr09,
ld_currencyamount TYPE bapiaccr09,
ld_currencyamount2 TYPE bapiaccr09,

* ld_currencyamount3 TYPE bapiaccr09,
* lt_return TYPE /eacc/t_bapiret2,
* lt_criteria TYPE bapiackec9_tab,
ld_account TYPE ZUST_AGRI_POST_CH_VAL_ITEM,"zust_agri_post_bkm_val_item,
* ld_account2 TYPE zust_agri_post_bkm_val_item,
ld_amount TYPE bapiaccr09,
ld_header TYPE bapiache09,
ld_ap TYPE bapiacap09,
intLine type POSNR_ACC.

intLine = 1.

MOVE-CORRESPONDING header TO ld_header.
ld_header-bus_act = 'RFBU'.
LOOP AT gl_account INTO ld_account.
MOVE-CORRESPONDING ld_account TO ld_gl_account.
APPEND ld_gl_account TO lt_gl_account.
intLine = intLine + 1.
ENDLOOP.
*
LOOP AT acc_payable INTO ld_ap.
APPEND ld_ap TO lt_accountpayable.
ENDLOOP.

* intLine = 1.

loop at amount into ld_currencyamount.
* ld_currencyamount-ITEMNO_ACC = intLine.
APPEND ld_currencyamount TO lt_currencyamount.
* intLine = intLine + 1.
endloop.



refresh return.
clear commit_return.
CALL FUNCTION 'BAPI_ACC_DOCUMENT_POST'
EXPORTING
documentheader = ld_header
TABLES
accountgl = lt_gl_account
accountreceivable = lt_accountreceivable
accountpayable = lt_accountpayable
currencyamount = lt_currencyamount
return = return.

CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
EXPORTING
wait = 'X'
IMPORTING
return = commit_return.


ENDFUNCTION.


7. Save the codes, assign to package and new Transport request number, then activate the function modules.


8. Next steps are generating the web service. Click "Utilities" -> "More Utilities" -> "Create web service" -> "From Function module"




9. Then "Provide Service Definition details" shows up. Enter web service name and its description, then click Continue. Usually I add "WS" to the prefix to differentiate the web service name and function module name.

wiki09
10. On step "Choose Endpoint", just leave as it is. Click continue.

wiki10


11. Next, on step "Configure service", don't forget to tick "Deploy Service", then click continue

wiki11


12. On step "Enter package/request", Enter your package and transport request no, then click continue

wiki12


13. Done. Your web service is activated.



14. Next step, create endpoint via tcode SOAMANAGER. This tcode will open web configuration tools via your web browser. Remember your browser must be Internet Explorer , otherwise the system won't run well

wiki13

14b. If you face any issues regarding cannot open the web configuration tools, read this step. If you have don't have any issues opening the web configuration tools, you can skip this step and please read step 15. The URL will be like this (http://devecc.indofood.co.id:8080/sap/bc/webdynpro/sap/appl_soap_management?sap-client=530&sap-language=EN) :

wiki14


Usually the problem is your computer cannot recognize the domain such as devecc.indofood.co.id , to resolve this issue, you must open your hosts file on C:\Windows\System32\drivers\etc\hosts , Enter your [SAP IP][space][domainname] under the last line of the hosts file (example : 172.30.52.102 devecc.indofood.co.id) like this picture and don't forget to save

wiki15




You can get your SAP IP via SAP GUI :

wiki20 

To test the connection , open command prompt, press [Windows]+R , type cmd


wiki16
Enter this command to the command prompt : ping[space][yourdomainname] ,example : ping devecc.indofood.co.id . The result will return result similar like this if your computer successfully recognize the SAP server IP:


wiki17


Refresh your page in internet explorer.


15. The web configuration tools will be shown like this.


wiki18



16. Enter your SAP username and password

wiki19


17. The web configuration tools will be shown. Click "Business Administration" tab, then click "Web service administration" link

wiki21


18. Web Administration menu shows up. First, click dropdown "Search by", choose "Service". Second, enter your search pattern, example : ZUAGRICHWS*POST*. Last, click button "Go"



wiki22

19. The result will be shown on the Search results table below. Usually only 5 results are shown. If the results is more that that, you can click up or down arrow on the bottom of the search results table (marked by blue circle). Choose your web service by clicking the left column of chosen row. The row will be highlighted like image below. Then click "Apply selection" button.

wiki23



20. After that, "Detail of service definition" window will be shown. Usually there are two conditions:


- if the dropdown in the right most is empty, then it means the service endpoint hasn't been created. you have to create service first. It will be explain in the next steps.


- if the dropdown in the right most isn't empty, then it means the service endpoint has been created. Just click "Open WSDL document for selected binding" link to open your WSDL document. (or skip to step 25)

wiki24


21. To create service endpoint, click "Configuration" tab, then click "Create service"

wiki25


22. Enter new service name (I give the same name as web service name, ex: ZUAGRICHWS_POST_AMOUNT), enter description , and enter new binding name (ex : ZUAGRICHWS_POST_AMOUNT). Then click apply settings


wiki26


 
23. "Configuration of Web service" window shows up below the "Detail of service definition" window. Tick "User ID/Password" on HTTP Authentication box until "Collected Authentication method" have one row in its table. Then click Save button.


wiki27 

24. Notice that one new row appear on the "Detail of Service definition" table. Click "Overview" tab


wiki28 

25. Notice that the right most dropdown is not empty anymore. You have successfully created your endpoint. Click "Open WSDL for selected binding" link to open your WSDL document



wiki29 




26. The WSDL document URL will be similar like this : http://devecc.indofood.co.id:8080/sap/bc/srt/wsdl/bndg_4C73AADA27CC02CCE1008000AC1E3466/wsdl11/allinone/ws_policy/document?sap-client=530 , the content will be similar like image below.

wiki30 

27. Because there is some bugs from SAP , we cannot directly consume this web service. We have to save this WSDL document to local file (with *.wsdl extension) and fix it. Click "File" - "Save as" in IE Menu Bar, enter file name D530_ZUAGRICHWS_POST_AMOUNT.wsdl. Usually I use naming convention like this [SAPType]_[WebServiceName].wsdl to indicate that this is web service on Development server client 530.

wiki31
28. Change some parts of the wsdl file to fix the bugs before it's consumed by .NET (by using text editors like notepad/ Textpad / Visual Studio).


- First, change "parameters" to "parameter".
wiki32

result:


wiki35


- Second, if you are using BAPI, change "System" to "SYSTEM" (we have to change it to uppercase because in .NET will conflict with .NET System namespace).
wiki33

result:

wiki36


- Third we have to change domain name to IP and fix its corresponding port, if you haven't applied SAP note 1263112, whatever your SAP port settings, it will always show 443 instead of correct port, in this case 8080. There are some people have issues with this problem in http://forums.sdn.sap.com/thread.jspa?threadID=1419721&start=0&tstart=0 (example : devecc.indofood.co.id:443 -> change it to 172.30.52.102:8080).

wiki34
result:
wiki37 

Why you should change domain to IP ? becase not all web service consumer recognize the domain (devecc.indofood.co.id) as 172.30.52.102. (except you have a good DNS server). If the consumer don't know this domain, when they call this web service, they will throw exception / error.


Don't forget to save the document.

 


29. Next step is to place this WSDL document to virtual direcory in order to be able to be accessed by other computers. You can place this document at your computer (http://localhost/services) or on server (ex: http://10.126.140.46/services) . The steps to create virtual directory is not covered this tutorial. You can create it in Internet Information Services (IIS) Manager (command : inetmgr)




30. Assume that the web service URL is http://10.126.140.46/services/D530_ZUAGRICHWS_POST_AMOUNT.wsdl

You can test the web service by entering the URL on your browser. If successful, it will return result similar like this


wiki38 
31. Your web service has been successfully deployed :D Now Open your visual studio 2008. Add web reference from this web service



wiki39
Enter your web service URL, click Go, then enter your web reference name that will be used in the code (ex : SAPWSD530POST), Click "Add reference"
wiki40


32. Use this code










  1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
SAPWSD530POST.ZUAGRICHWS_POST_AMOUNT4 s = new SAPWSD530POST.ZUAGRICHWS_POST_AMOUNT4();
s.Credentials = nc;

SAPWSD530POST.ZuagrichPostAmount param = new SAPWSD530POST.ZuagrichPostAmount();
#region header
SAPWSD530POST.ZustAgriPostChValHeader h = new SAPWSD530POST.ZustAgriPostChValHeader();
h.Username = data.Header.Username;
h.HeaderTxt = data.Header.HeaderText;
h.CompCode = data.Header.CompanyCode;
h.DocDate = string.Format("{0}-{1}-{2}", data.Header.DocumentDate.Year, data.Header.DocumentDate.Month.ToString("00"), data.Header.DocumentDate.Day.ToString("00"));
h.PstngDate = string.Format("{0}-{1}-{2}", data.Header.PostingDate.Year, data.Header.PostingDate.Month.ToString("00"), data.Header.PostingDate.Day.ToString("00"));
//h.TransDate = string.Format("{0}-{1}-{2}", data.Header.TranslationDate.Year, data.Header.TranslationDate.Month, data.Header.TranslationDate.Day);
h.TransDate = "";
h.FiscYear = data.Header.FiscalYear.ToString();
h.FisPeriod = data.Header.FiscalPeriod.ToString("00");
h.DocType = data.Header.DocumentType;
#endregion
//assign header to param
param.Header = h;

//GL Account table
//SAPWSQ615POST4.ZustAgriPostChValItem[] g = new SAPWSQ615POST4.ZustAgriPostChValItem[data.Details.Count];
List<SAPWSD530POST.ZustAgriPostChValItem> glaccs = new List<SAPWSD530POST.ZustAgriPostChValItem>();
//Amount table
//SAPWSQ615POST4.Bapiaccr09[] a = new SAPWSQ615POST4.Bapiaccr09[data.Details.Count];
List<SAPWSD530POST.Bapiaccr09> amounts = new List<SAPWSD530POST.Bapiaccr09>();
//Account payable table
List<SAPWSD530POST.Bapiacap09> aps = new List<SAPWSD530POST.Bapiacap09>();

// int idx = 0;

foreach (PostingJournal detail in data.Details)
{
if (string.IsNullOrEmpty(detail.VendorNo))
{
SAPWSD530POST.ZustAgriPostChValItem obj = new SAPWSD530POST.ZustAgriPostChValItem();
//g[idx] = new SAPWSQ615POST4.ZustAgriPostChValItem();
obj.ItemnoAcc = detail.AccountingDocLineItemNo.ToString("0000000000");
obj.GlAccount = detail.GLAccount;
obj.ItemText = detail.ItemText;
obj.DocType = detail.DocumentType;
obj.CompCode = detail.CompanyCode;
obj.BusArea = detail.BusinessArea;
obj.FisPeriod = detail.FiscalPeriod.ToString("00");
obj.FiscYear = detail.FiscalYear.ToString();
obj.PstngDate = string.Format("{0}-{1}-{2}", detail.PostingDate.Year, detail.PostingDate.Month.ToString("00"), detail.PostingDate.Day.ToString("00"));
obj.AllocNmbr = detail.AssignmentNo;
//g[idx].ProfitCtr = detail.ProfitCenter;
obj.ProfitCtr = "";
obj.Costcenter = detail.CostCenter;
obj.WbsElement = detail.WbsElement;
//added to accommodate jamsostek posting
//g[idx].VendorNo = detail.VendorNo;
glaccs.Add(obj);
}
else
{
SAPWSD530POST.Bapiacap09 obj3 = new SAPWSD530POST.Bapiacap09();
obj3.ItemnoAcc = detail.AccountingDocLineItemNo.ToString("0000000000");
obj3.VendorNo = detail.VendorNo;
obj3.CompCode = detail.CompanyCode;
obj3.BusArea = detail.BusinessArea;
obj3.AllocNmbr = detail.AssignmentNo;
obj3.ItemText = detail.ItemText;
aps.Add(obj3);

}
SAPWSD530POST.Bapiaccr09 obj2 = new SAPWSD530POST.Bapiaccr09();
//a[idx] = new SAPWSQ615POST4.Bapiaccr09();
obj2.ItemnoAcc = detail.AccountingDocLineItemNo.ToString("0000000000");
obj2.Currency = "IDR";
obj2.AmtDoccur = detail.AmountDocumentCurrency;
amounts.Add(obj2);
//idx++;
}
//assign amount and GL account to param
param.GlAccount = glaccs.ToArray();
param.Amount = amounts.ToArray();
param.AccPayable = aps.ToArray();
//result table
//just fill with zero, even though no parameters is needed
//assign to param's inner variable
param.Return = new SAPWSD530POST.Bapiret2[0];
SAPWSD530POST.ZuagrichPostAmountResponse result;
try
{
result = s.ZuagrichPostAmount(param);
foreach (SAPWSD530POST.Bapiret2 item in result.Return)
{
if (item.Type == "S") //success
{
List<SapPostingError> errors = new List<SapPostingError>();
foreach (SAPWSD530POST.Bapiret2 item2 in result.Return)
{
errors.Add(new SapPostingError(item2.Type, item2.Message));
}
//lblStrSuccess.Text = "Posting sukses. No dokumen : " + item.MessageV2.Substring(0, 10);
return new PostingResult(PostingResultType.Success, item.MessageV2.Substring(0, 10), errors);
//break;
}
else if (item.Type == "E") //error
{
List<SapPostingError> errors = new List<SapPostingError>();
foreach (SAPWSD530POST.Bapiret2 item2 in result.Return)
{
errors.Add(new SapPostingError(item2.Type, item2.Message));
}
//ltStrErr.Text = ltStrErr.Text + "Posting error. Error message : " + item.Message + "<br />";
return new PostingResult(PostingResultType.Error, item.Message, errors);
}
}

}
catch (Exception ex)
{
throw ex;
}



33. That's it. Just test using above codes. Customize the codes according to your needs. It works well in my system. Hope this tutorial helps.