2 Ağustos 2013 Cuma

SAP Abap ile MSSQL ve MYSQL bağlantısı ve SAP Proxy

SAP sistemi Windows üzerinde çalışmayan sistemlerde Microsoft SQL ve MYSQL e bağlanmak incelemelerime göre standart yapıda mümkün olmuyordu.

Bu amaçla bende kendi bağlantı ara yüzümü geliştirdim.

Bu anlatımlarda SAP Proxy yapısı başlı başına bir konu olarak ele alınıp web servislere bağlantı için kullanılabilir. Bu yazı bu işi öğrenip hayata geçirmek içinde gerekli bilgileri barındırmaktadır.

Bu yazımda anlatacağım sistemin çalışması için SAP dışında gereken Aspx çalıştıracağınız bir web sunucusuna ihtiyacımız olacak.

Genel mimari olarak:

SAP Abap programından bir sql’i dışarıdaki aspx web servise sap Proxy yapısını kullanarak göndereceğiz. Bu web servis bizim gönderdiğimiz bağlantı bilgilerini kullanarak ilgili sql i çalıştırıp sonuçları geri döndürecek. Dönen verileri yine abap kodunda işleyip dinamik tabloya aktarıp kullanacağız.

Bu sql komutu select, insert, update, delete 4 işlemide kapsamakta. Select komutunda data dönerken diğer komutlarda üzerinde işlem yapılan kayıt sayısı dönecektir.



Yapılması gerekenler:

1. Microsoft Visual Studio 2008 ile hazırladığım web servis dosyalarını iis sunucusunda bir klasöre koyuyoruz. Web Sitesi Dosyalarını İndir

Mysql 5.x ve mssql için dosya yapısı:




Mysql için kullanmanız gereken driver dll’ini ve web servisimizin dll ini bin klasörüne koyuyoruz.




Benim senaryomda iki ayrı mysql sunucusuna bağlanmak var. Bunlardan birinin versiyonu 4.x diğeri 5.x. Doğal olarak kullanacağım driver farklı. Bu iki driveri aynı bin dosyası içine koyamadığım için iis üzerinde bir web sitesi daha oluşturup ikinci bir bin dosyasına sahip oldum. Bu ikinci sitenin portu diğer siteninkinden farklı olduğu için SAP Proxy ayarlarını buna göre yapıyor olacağım. Sizin ikinci bir siteye ihtiyacınız yoksa bu adımı atlayabilirsiniz.

Mysql 4.x için hazırladığım sitenin dosya yapısı:




IIS sunucusunda tüm işlemler bittikten sonra isterseniz sunucunuzda web servisi test edebilirsiniz. (http://localhost/SqlWebService/SqlWs.asmx)



Kodlarda değişiklik yapmak isteyenler için proje dosyalarını da ekliyorum.
Web Servis Proje Dosyalarını İndir

2. SAP Proxy yapılandırması ile işlemlerimi yapmayı uygun gördüm. Bunu kullanmadan direk abap içerisinden de web servisi çağırmamız mümkün. İsteyen olursa Proxy kullanmadan web servis çağıran abap kodlarını da paylaşabilirim.

İşlem kodu SE80 ile Proxy oluşturmaya başlıyoruz. Bazı sistemlerde aşağıdaki menülerde farklılık olabiliyor, Proxy object olarakta menülerde görünebilir.







Servis wsdl kodlarını direk servis url ini yazarak yada bir file dan ekleyerek devam edebiliriz. Ben URL ile devam etmeyi tercih ediyorum. Burada dikkat edilmesi gereken servisin wsdl adresini vermektir. (Örn: http://141.11.11.243/sqlwebservice/sqlws.asmx?wsdl)



Bir paket seçip request oluşturuyoruz. Prefix web servis için oluşturulacak class ların önüne eklenecektir. Örnek kodların çalışması için prefix i ZSQL olarak yazmalısınız.





Sorun yoksa SAP bizim için Proxy altyapısını hazırlayacaktır. Kaydedip aktifleşebiliriz. Tablar arasında gezinerek oluşturulan yapıları ve kullanacağımız alanları görebilirsiniz. Bu aşamadan sonra burada işimiz kalmıyor. Artık bu yapıları Abap programından çağıracağız.



3. Oluşturmuş olduğumuz Proxy için logical port oluşturmamız gerekiyor. Ben SQLTEST ismini veriyorum siz örnek kodlara uygun olsun isterseniz SQL diye isimlendirebilirsiniz. Dilerseniz kodda değiştirmek kaydıyla dilediğiniz şekilde isimlendirebilirsiniz.

Bunun için işlem kodu “soamanager”. Bir web sayfası açılacak ve işlemlerimizi buradan yapıyor olacağız.

Bu web sayfasında da sistem versiyonları arasında menü farklılıkları olabilir.

Application…--> Single… tıklıyoruz.



Aşağıdaki şekilde arayıp servisimizi buluyoruz. Apply Selection butonuna tıklayarak Create Logical Port diyoruz.



Web servis için kullanıcı adı şifre gerekiyorsa yazılabilir.



Konfigrasyon aşağıdaki gibi olacak, sonrasında kaydet diyoruz.









Kaydettikten sonra aşağıdaki şekilde oluşmuş olacaktır.



İşlemimiz bu kadar.

İki ayrı web sitesi yapıp farklı versiyon veri tabanlarına erişmek durumunda olanlar iki siteye de aynı web servis dosyalarını koymalılar. Bu durumda sadece veri tabanı dll leri farklı ise aynı Proxy ye iki ayrı port oluşturabilirler. Aşağıdaki örneklere uygun yapıp abap kodunda port adını yazıp çağırabilirler. Ayrı bir Proxy daha oluşturmalarına böylelikle gerek kalmayacaktır.

Dikkat edilirse sadece URL port bilgisi değişti.






4. SAP de MSSQL veya MYSQL server bağlantı bilgilerinin girileceği parametre tablosunu oluşturuyoruz.






Sadece Z_SQLTYPE domain tipine değer girme ihtiyacı duydum. Burada bağlantı yapacağımız veri tabanı türlerini giriyorum. Mesela iki ayrı mysql veritabanına bağlanmanız gerekiyor. Ancak bunlar farklı versiyon ve ayrı dll lere sahipler. Yukarıdaki anlatımda detaylarını paylaşmıştım.



Tabloya SM30/SM31 işlem kodlarından kayıt girişi yapılabilmesi için ekranımızı oluşturmalıyız.



Yetki grubunu kendinize göre değiştirip herkesin tabloyu değiştirip değiştirmeyeceğini roller ile kontrol edebiliriz. Ekran numarasını 900 ve üzeri kullanmadığınız bir numara verebilirsiniz.



Tabloya veri girilmiş hali:
Connname: Program içerisinden çağırmak için kullanacağımız benzersiz tanım.
Sqlty: Birden çok veri tabanı sitemiz varsa bunun tiplerini burada tanımlıyoruz. Bilgi amaçlıdır.
Srv: Sql server erişim adı veya ip numarası.
Username: Sql server kullanıcı adı.
Pwd: Sql server kullanıcı şifresi
Db: Sql sunucusunda bağlanacağımız veri tabanı adı. Büyük küçük harf duyarlı olurlar.
Portname: SAP Proxy de servise erişim için oluşturduğumuz port adı.
Metodname: Web servis dosyası içinde birçok servis oluşturulmuş olabilir. Biz hangi servisi çağırmak istiyorsak bunu giriyoruz.



5. SE38 işlem kodu ile Abap include hazırlıyoruz.

            *&---------------------------------------------------------------------*
*&  Include           ZSQL_EXECUTE
*&---------------------------------------------------------------------*
field-symbols<t_dyntable> type standard table,
               <fs_dyntable>
,
               <fs_fldval> 
type any.

datat_newtable   type ref to data,
      t_newline    
type ref to data,
      t_fldcat     
type lvc_t_fcat,
      t_fldcat_tmp 
type lvc_t_fcat,
      wa_it_fldcat 
type lvc_s_fcat.

dataio_clientproxy type ref to zsqlco_service1soap.
datap_connname like zsql_srv_conn-connname,
      p_type
(20),
      p_sql
(1000).
*&---------------------------------------------------------------------*
*&      Form  sql_exec
*&---------------------------------------------------------------------*
form sql_exec.

  
typesbegin of ls_line,
    
line(4000),
  
end of ls_line.
  
datags_connname like zsql_srv_conn.
  
data gt_response_body    type table of ls_line with header line,
         gt_response_headers 
type table of line with header line,
         gt_request_body     
type table of ls_line with header line,
         gt_request_headers  
type table of line with header line.

*-- variables for proxy class
  
datags_mssql_input  type zsqlsql_exec_soap_in,
        gs_mssql_output 
type zsqlsql_exec_soap_out.

  
select single into gs_connname
    
from zsql_srv_conn
   
where connname p_connname.
  
if sy-subrc <> .
    
message 'ZSQL_SRV_CONN tablosunda DB bağlantısı bulunamadı!'
       
type 'E'.
    
exit.
  
endif.

  gs_mssql_input
-server   gs_connname-srv  .
  gs_mssql_input
-user     gs_connname-username .
  gs_mssql_input
-pwd      gs_connname-pwd  .
  gs_mssql_input
-database gs_connname-db   .
  gs_mssql_input
-type     p_type .
  gs_mssql_input
-sql      p_sql  .

  
data datalen type  i,
         datalen2 
type i,
         lv_absolute_uri 
type grmg_url_type,
         status_code
(255),
         status_text
(255).


  
data lv_xmlstr  type string.
  
data lv_xmlstr2 type string.
  
data lv_xmlstrx type xstring.


*-- create web service proxy class instance

  
try.
      
create object io_clientproxy
        
exporting
          logical_port_name 
gs_connname-portname.

    
catch cx_ai_system_fault.
      
exit.
  
endtry.

*-- call web service methods
  
try.
      
call method io_clientproxy->(gs_connname-metodname)
        
exporting
          
input  gs_mssql_input
        
importing
          
output gs_mssql_output.
    
catch cx_ai_system_fault.
      
exit.
    
catch cx_ai_application_fault.
      
exit.
  
endtry.

  lv_xmlstr2 
gs_mssql_output-sql_exec_result.

  
data:lt_xml_table type smum_xmltb occurs with header line,
        lt_return   
type bapiret2   occurs with header line,
        lv_len      
type i.

  
if p_type <> 'S'.
    
concatenate '<SqlExecResult>' lv_xmlstr2 '</SqlExecResult>'
           
into lv_xmlstr2 respecting blanks.
  
endif.

  
clearlv_xmlstrx,lv_len.
  
free   lt_xml_table.

  
call function 'HR_KR_STRING_TO_XSTRING'
    
exporting
      codepage_to      
'4110'
      unicode_string   
lv_xmlstr2
      out_len          
lv_len
    
importing
      xstring_stream   
lv_xmlstrx
    
exceptions
      invalid_codepage 
1
      invalid_string   
2
      
others           3.

  
call function 'SMUM_XML_PARSE'
    
exporting
      xml_input 
lv_xmlstrx
    
tables
      xml_table 
lt_xml_table
      
return    lt_return.

  
databegin of gt_field occurs 0,
    
field(20),
  
end of gt_field.

  
datalv_i type i.

  
loop at lt_xml_table where cname <> 'Table'
                         
and cname <> 'Row' .
    gt_field
-field lt_xml_table-cname.
    
translate gt_field-field to upper case.
    
append gt_field.
    
clear  gt_field.
  
endloop.

  
sort gt_field by field.
  
delete adjacent duplicates from gt_field comparing field.

  
freet_fldcat,wa_it_fldcat.

  
clear lv_i.
  
loop at gt_field.
    lv_i 
lv_i + 1.
    wa_it_fldcat
-col_pos   lv_i.
    wa_it_fldcat
-fieldname gt_field-field.
    wa_it_fldcat
-inttype   'C'.
    wa_it_fldcat
-intlen    '255'.
    wa_it_fldcat
-reptext   =
    wa_it_fldcat
-seltext   wa_it_fldcat-fieldname .
    
append wa_it_fldcat to t_fldcat.
  
endloop.

  
clear lv_i.
  
describe table gt_field lines lv_i.
  
if lv_i 0.
    wa_it_fldcat
-col_pos   1.
    wa_it_fldcat
-fieldname 'NULL'.
    wa_it_fldcat
-inttype   'C'.
    wa_it_fldcat
-intlen    '255'.
    wa_it_fldcat
-reptext   =
    wa_it_fldcat
-seltext   wa_it_fldcat-fieldname .
    
append wa_it_fldcat to t_fldcat.
  
endif.

  
if t_fldcat_tmp[] <> t_fldcat[].
    
free:t_fldcat_tmp.
    
free:t_newtable.
    
free:t_newline.
    t_fldcat_tmp[] 
t_fldcat[].

* Create dynamic internal table and assign to FS
    
call method cl_alv_table_create=>create_dynamic_table
      
exporting
        it_fieldcatalog           
t_fldcat
      
importing
        ep_table                  
t_newtable
      
exceptions
        generate_subpool_dir_full 
1
        
others                    2.
    
if sy-subrc <> 0.
      break ikarahan
.
    
endif.

  
else.
*    free:t_newtable.
*    free:t_newline.
  
endif.

  
if <t_dyntable> is assigned.
    unassign <t_dyntable>
.
  
endif.

  
assign t_newtable->to <t_dyntable>.
* Create dynamic work area and assign to FS
  
create data t_newline like line of <t_dyntable>.
  
assign t_newline->to <fs_dyntable>.
* populating dynamic internal table
  
datafieldname(20)  type c.
  
datafieldvalue(10type c.
  
datalv_fieldname   type lvc_fname.

  
clear wa_it_fldcat.

  
loop at lt_xml_table where cname <> 'Table'.
    
if lt_xml_table-cname 'Row'.
      
append <fs_dyntable> to <t_dyntable>.
      
free:  <fs_dyntable> .
    
else.
      
translate lt_xml_table-cname to upper case.
      
assign component lt_xml_table-cname
      
of structure <fs_dyntable> to <fs_fldval>.
      <fs_fldval> 
lt_xml_table-cvalue.
    
endif.
  
endloop.

  
clear lv_i.

  
describe table lt_xml_table lines lv_i.

  
if lv_i > and p_type 'S'.
    
delete <t_dyntable> index 1.
    
append <fs_dyntable> to <t_dyntable>.
    
free:  <fs_dyntable> .
  
endif.

  
if lv_i > and p_type <> 'S'.
    
delete <t_dyntable> index 1.
    
append <fs_dyntable> to <t_dyntable>.
    
free:  <fs_dyntable> .
  
endif.

endform.                    "sql_exec


2.       ÖRNEK KULLANIM:

Select örnek kullanımı:

include 
zsql_execute.

p_connname 
'MSSQL_CHSQL_SA_SUPP'.
p_type  
'S'. “S:Select,I:Insert,D:Delete,U:Update
concatenate 'SELECT SAPkod FROM tbl_Suppliers'
            
'WHERE (SAPkod IS NOT NULL)'
       
into p_sql separated by space.

perform sql_exec.

databegin of gt_tedarikci occurs 0,
  sapkod 
like lfa1-lifnr,
        
end of gt_tedarikci.

loop at <t_dyntable> into <fs_dyntable>.
  
assign component 'SAPKOD'
      
of structure <fs_dyntable> to <fs_fldval>.
  
move-corresponding <fs_dyntable> to gt_tedarikci.
  
append gt_tedarikci.
  
clear  gt_tedarikci.
endloop.

Insert örnek kullanımı:

Delete ve Update işlemide aynı şekildedir. Sadece type değişir.

include zsql_execute.

p_connname 
'MSSQL_CHSQL_SA_SUPP'.
p_type  
'I'.

    concatenate
    
'INSERT INTO bakiye_GunlukBakiye'
    
'(tarih, firmakodu, parcaNo, parcaTanimi,siparisMiktari,'
    
'bakiyeMiktari,sonMalGirisMik, sonMalGirisTar, sonIrsaliyeNo)'
    
' VALUES '
    
'('''
    gs_data2
-kaltestarihi+0(4'.'
    gs_data2
-kaltestarihi+4(2'.'
    gs_data2
-kaltestarihi+6(2)
    
''','''
    gs_data2
-satici ''','''
    gs_data2
-malzeme''','''
    gs_data2
-malmetin ''','
    lv_str2 
','
    lv_str3 
','
    lv_str1 
','''
    gs_data2
-sonmalgiristar+0(4'.'
    gs_data2
-sonmalgiristar+4(2'.'
    gs_data2
-sonmalgiristar+6(2)
    
''','''
    gs_data2
-sonirsaliyeno ''')'
    
into p_sql respecting blanks.

    
perform sql_exec.
    
read table <t_dyntable> into <fs_dyntable> index 1.
    
assign component 'SQLEXECRESULT'
        
of structure <fs_dyntable> to <fs_fldval>.

    if <fs_fldval> 1. “Insert Delete ve Update işleminde kaç adet kayıt etkilenmişse sayısı döner.
      gt_dataalv
-sonuc Kaydedildi.'.
    
else.
      gt_dataalv
-sonuc Kaydedilemedi!'.
    
endif.

7. SAP den web servisi Proxy ile çağırdığınızda tüm alanları doldurmanız gerekebiliyor, boş ise en azından bir karakter boşluk göndermenizi tavsiye ederim. Kullandığınız SAP sisteminde bu tür ihtiyaç olmayabilir, hata almıyorsanız bu uyarıyı dikkate almayabilirsiniz.

Hiç yorum yok:

Yorum Gönder