实时发送SAP会计凭证到外部系统
原创 ABAP老白 ABAP老白 2023-01-29 19:17 发表于上海
最近几年做的接口类型的开发比较多,有一类接口是把SAP的凭证类型数据或者主数据发送到外部系统。在对实时性要求不高的时候,通常会通过筛选创建日期、时间等使用JOB程序推送到外部,但是如果对实时性要求比较高这个方法就不再适合了。这个时候可以使用Message以IDOC的方式发出去,或者在增强里面使用自开发程序直接推出去。
本例使用BTE的方式,实时的把SAP会计凭证推送到外部的Oracle数据库。
和物料凭证的POST不同,并没有一个出口/BADI能够包含会计凭证的所有创建/更改,如果使用BTE的话,需要下面三个:
1、事件00001030 直接生成的会计凭证,比如F-02 2、事件00001050 VF01 MIRO等间接生成的凭证数据 3、事件00001110 FB02 更改凭证数据
详细过程: 1、配置BTE,事务码FIBF
2、程序
FUNCTION zbte_send_bseg_to_gb.
*"----------------------------------------------------------------------
*"*"更新函数模块:
*"
*"*"局部接口:
*" IMPORTING
*" VALUE(UPDKZ) TYPE UPDKZ_D OPTIONAL
*" VALUE(I_XBKPF) TYPE BKPF OPTIONAL
*" VALUE(I_YBKPF) TYPE BKPF OPTIONAL
*" TABLES
*" T_BKPF STRUCTURE BKPF OPTIONAL
*" T_BSEG STRUCTURE BSEG OPTIONAL
*" XBSEG STRUCTURE FBSEG OPTIONAL
*"----------------------------------------------------------------------
DATA: lt_bseg TYPE TABLE OF bseg WITH HEADER LINE.
DATA: lt_gb TYPE TABLE OF zbte_send_bseg_to_gb_s1 WITH HEADER LINE.
CHECK s_hkont[] IS NOT INITIAL.
IF updkz = 'I'.
LOOP AT t_bseg WHERE hkont IN s_hkont.
READ TABLE t_bkpf WITH KEY bukrs = t_bseg-bukrs
belnr = t_bseg-belnr
gjahr = t_bseg-gjahr.
MOVE-CORRESPONDING t_bseg TO lt_gb.
MOVE-CORRESPONDING t_bkpf TO lt_gb.
APPEND lt_gb.CLEAR lt_gb.
ENDLOOP.
ELSEIF updkz = 'U'.
IF i_xbkpf-bktxt <> i_ybkpf-bktxt OR
i_xbkpf-xblnr <> i_ybkpf-xblnr OR
i_xbkpf-xref1_hd <> i_ybkpf-xref1_hd OR
i_xbkpf-xref2_hd <> i_ybkpf-xref2_hd OR
xbseg[] IS INITIAL.
SELECT * INTO CORRESPONDING FIELDS OF TABLE lt_bseg
FROM bseg
WHERE bukrs = i_xbkpf-bukrs AND
belnr = i_xbkpf-belnr AND
gjahr = i_xbkpf-gjahr AND
hkont IN s_hkont.
CHECK sy-subrc = 0.
LOOP AT lt_bseg.
READ TABLE xbseg WITH KEY buzei = lt_bseg-buzei.
IF sy-subrc = 0.
MOVE-CORRESPONDING xbseg TO lt_bseg.
ENDIF.
MOVE-CORRESPONDING lt_bseg TO lt_gb.
MOVE-CORRESPONDING i_xbkpf TO lt_gb.
APPEND lt_gb.CLEAR lt_gb.
ENDLOOP.
ELSE.
LOOP AT xbseg WHERE hkont IN s_hkont.
MOVE-CORRESPONDING xbseg TO lt_gb.
MOVE-CORRESPONDING i_xbkpf TO lt_gb.
APPEND lt_gb.CLEAR lt_gb.
ENDLOOP.
ENDIF.
ENDIF.
CHECK lt_gb[] IS NOT INITIAL.
CALL FUNCTION 'ZBTE_PUSH_BSEG_TO_GB' "IN BACKGROUND TASK
EXPORTING
updkz = updkz
commit = ''
TABLES
gbtab = lt_gb.
ENDFUNCTION.
FUNCTION zbte_push_bseg_to_gb.
*"----------------------------------------------------------------------
*"*"Local interface:
*" IMPORTING
*" VALUE(UPDKZ) TYPE UPDKZ_D OPTIONAL
*" VALUE(COMMIT) TYPE CHAR1 OPTIONAL
*" TABLES
*" GBTAB STRUCTURE ZBTE_SEND_BSEG_TO_GB_S1
*"----------------------------------------------------------------------
DATA: connam TYPE dbcon-con_name VALUE 'EDWP'.
DATA: lt_log TYPE TABLE OF zgbt2 WITH HEADER LINE.
DATA: cxserr TYPE REF TO cx_sy_native_sql_error,
excmsg TYPE string,
rtmsg TYPE bapi_msg.
DATA: BEGIN OF lt_error OCCURS 0,
bukrs TYPE zgbt2-bukrs,
belnr TYPE zgbt2-belnr,
gjahr TYPE zgbt2-gjahr,
buzei TYPE zgbt2-buzei,
exmsg TYPE zgbt2-exmsg,
END OF lt_error.
LOOP AT gbtab.
lt_log-bukrs = gbtab-bukrs.
lt_log-belnr = gbtab-belnr.
lt_log-gjahr = gbtab-gjahr.
lt_log-buzei = gbtab-buzei.
lt_log-hkont = gbtab-hkont.
lt_log-aedat = sy-datum.
lt_log-aezet = sy-uzeit.
lt_log-aenam = sy-uname.
APPEND lt_log.
ENDLOOP.
MODIFY zgbt2 FROM TABLE lt_log.
IF commit = 'X'.
COMMIT WORK.
ENDIF.
TRY .
EXEC SQL.
connect to :connam
ENDEXEC.
CATCH cx_sy_native_sql_error INTO cxserr.
excmsg = cxserr->sqlmsg.
CONCATENATE '连接' connam '失败:' excmsg INTO excmsg.
LOOP AT lt_log.
UPDATE zgbt2 SET exmsg = excmsg
WHERE bukrs = lt_log-bukrs AND
belnr = lt_log-belnr AND
gjahr = lt_log-gjahr AND
buzei = lt_log-buzei.
MOVE-CORRESPONDING lt_log TO lt_error.
lt_error-exmsg = excmsg.
APPEND lt_error.
ENDLOOP.
ENDTRY.
IF lt_error[] IS INITIAL.
LOOP AT gbtab.
CLEAR excmsg.
TRY.
IF updkz = 'U'.
EXEC SQL.
delete from zodss700_bseg
where mandt = :gbtab-mandt and
bukrs = :gbtab-bukrs and
belnr = :gbtab-belnr and
gjahr = :gbtab-gjahr and
buzei = :gbtab-buzei
ENDEXEC.
ENDIF.
CLEAR sy-subrc.
EXEC SQL.
insert into zodss700_bseg
( mandt ,bukrs ,belnr ,gjahr ,buzei ,blart ,bldat ,budat ,monat ,cpudt ,
cputm ,aedat ,upddt ,usnam ,tcode ,bvorg ,xblnr ,stjah ,bktxt ,waers ,
kursf ,hwaer ,awsys ,stgrd ,ppnam ,numpg ,bstat ,glvor ,awtyp ,awkey ,
augdt ,augbl ,bschl ,koart ,umskz ,umsks ,shkzg ,gsber ,mwskz ,dmbtr ,
wrbtr ,pswbt ,pswsl ,valut ,zuonr ,sgtxt ,kokrs ,kostl ,aufnr ,vbeln ,
vbel2 ,posn2 ,anln1 ,bzdat ,xkres ,xpanz ,xzahl ,saknr ,hkont ,kunnr ,
lifnr ,zfbdt ,zterm ,matnr ,werks ,menge ,meins ,ebeln ,bwkey ,bwtar ,
rstgr ,prctr ,kstrg ,projk ,lokkt ,xref1 ,xref2 ,fkber ,xnegp ,kkber ,
xref3 ,zzcfi,anln2 ,anbwa ,load_dt )
values
( :gbtab-mandt ,
:gbtab-bukrs ,
:gbtab-belnr ,
:gbtab-gjahr ,
:gbtab-buzei ,
:gbtab-blart ,
:gbtab-bldat ,
:gbtab-budat ,
trim( :gbtab-monat ) ,
:gbtab-cpudt,
:gbtab-cputm ,
:gbtab-aedat ,
:gbtab-upddt ,
trim( :gbtab-usnam ) ,
trim( :gbtab-tcode ) ,
trim( :gbtab-bvorg ) ,
trim( :gbtab-xblnr ) ,
trim( :gbtab-stjah ) ,
trim( :gbtab-bktxt ) ,
trim( :gbtab-waers ) ,
trim( :gbtab-kursf ) ,
trim( :gbtab-hwaer ) ,
trim( :gbtab-awsys ) ,
trim( :gbtab-stgrd ) ,
trim( :gbtab-ppnam ) ,
trim( :gbtab-numpg ) ,
trim( :gbtab-bstat ) ,
trim( :gbtab-glvor ) ,
trim( :gbtab-awtyp ) ,
trim( :gbtab-awkey ) ,
:gbtab-augdt ,
trim( :gbtab-augbl ),
trim( :gbtab-bschl ),
trim( :gbtab-koart ),
trim( :gbtab-umskz ),
trim( :gbtab-umsks ),
trim( :gbtab-shkzg ),
trim( :gbtab-gsber ),
trim( :gbtab-mwskz ),
trim( :gbtab-dmbtr ),
trim( :gbtab-wrbtr ),
trim( :gbtab-pswbt ),
trim( :gbtab-pswsl ),
:gbtab-valut,
trim( :gbtab-zuonr ) ,
trim( :gbtab-sgtxt ) ,
trim( :gbtab-kokrs ) ,
trim( :gbtab-kostl ) ,
trim( :gbtab-aufnr ) ,
trim( :gbtab-vbeln ) ,
trim( :gbtab-vbel2 ) ,
trim( :gbtab-posn2 ) ,
trim( :gbtab-anln1 ) ,
:gbtab-bzdat ,
trim( :gbtab-xkres ) ,
trim( :gbtab-xpanz ) ,
trim( :gbtab-xzahl ) ,
trim( :gbtab-saknr ) ,
trim( :gbtab-hkont ) ,
trim( :gbtab-kunnr ) ,
trim( :gbtab-lifnr ) ,
:gbtab-zfbdt ,
trim( :gbtab-zterm ) ,
trim( :gbtab-matnr ) ,
trim( :gbtab-werks ) ,
trim( :gbtab-menge ) ,
trim( :gbtab-meins ) ,
trim( :gbtab-ebeln ) ,
trim( :gbtab-bwkey ) ,
trim( :gbtab-bwtar ) ,
trim( :gbtab-rstgr ) ,
trim( :gbtab-prctr ) ,
trim( :gbtab-kstrg ) ,
trim( :gbtab-projk ) ,
trim( :gbtab-lokkt ) ,
trim( :gbtab-xref1 ) ,
trim( :gbtab-xref2 ) ,
trim( :gbtab-fkber ) ,
trim( :gbtab-xnegp ) ,
trim( :gbtab-kkber ) ,
trim( :gbtab-xref3 ) ,
trim( :gbtab-zzcfi ) ,
trim( :gbtab-anln2 ) ,
trim( :gbtab-anbwa ) ,
sysdate
)
ENDEXEC.
IF sy-subrc <> 0.
WRITE sy-subrc TO rtmsg LEFT-JUSTIFIED.
UPDATE zgbt2 SET exmsg = rtmsg
WHERE bukrs = gbtab-bukrs AND
belnr = gbtab-belnr AND
gjahr = gbtab-gjahr AND
buzei = gbtab-buzei.
CONTINUE.
ENDIF.
EXEC SQL.
commit
ENDEXEC.
CATCH cx_sy_native_sql_error INTO cxserr.
excmsg = cxserr->sqlmsg.
UPDATE zgbt2 SET exmsg = excmsg
WHERE bukrs = gbtab-bukrs AND
belnr = gbtab-belnr AND
gjahr = gbtab-gjahr AND
buzei = gbtab-buzei.
MOVE-CORRESPONDING gbtab TO lt_error.
lt_error-exmsg = excmsg.
APPEND lt_error.
ENDTRY.
ENDLOOP.
ENDIF.
ENDFUNCTION.
说明: ZBTE_SEND_BSEG_TO_GB为UPDATE Module, ZBTE_SEND_BSEG_TO_GB为RFC
重发程序: 因为网络或者Oracle的原因,发送失败是在所难免的,所以还需要一个重发程序,此程序既能够看到发送日志,也可以进行重发。
*&---------------------------------------------------------------------*
*& Report ZGB_ZGBT2_RESEND
*&
*&---------------------------------------------------------------------*
*&
*& 重新发送管报凭证数据
*&---------------------------------------------------------------------*
REPORT zgb_resend_docs NO STANDARD PAGE HEADING.
TYPE-POOLS: sscr,slis.
DATA: fldct TYPE lvc_t_fcat WITH HEADER LINE,
slayt TYPE lvc_s_layo,
varnt TYPE disvariant,
repid TYPE sy-repid.
DATA: BEGIN OF it_out OCCURS 0,
bukrs TYPE zgbt2-bukrs,
belnr TYPE zgbt2-belnr,
gjahr TYPE zgbt2-gjahr,
buzei TYPE zgbt2-buzei,
hkont TYPE zgbt2-hkont,
aedat TYPE zgbt2-aedat,
aezet TYPE zgbt2-aezet,
aenam TYPE zgbt2-aenam,
exmsg TYPE zgbt2-exmsg,
seled,
END OF it_out.
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE t01.
SELECT-OPTIONS s_bukrs FOR it_out-bukrs.
SELECT-OPTIONS s_belnr FOR it_out-belnr.
SELECT-OPTIONS s_gjahr FOR it_out-gjahr.
SELECT-OPTIONS s_aedat FOR it_out-aedat.
SELECT-OPTIONS s_aenam FOR it_out-aenam.
SELECT-OPTIONS s_exmsg FOR it_out-exmsg.
SELECTION-SCREEN END OF BLOCK b1.
AT SELECTION-SCREEN OUTPUT.
t01 = '选择条件'.
%_s_bukrs_%_app_%-text = '公司代码'.
%_s_belnr_%_app_%-text = '凭证号'.
%_s_gjahr_%_app_%-text = '年度'.
%_s_aedat_%_app_%-text = '发送日期'.
%_s_aenam_%_app_%-text = '发送账号'.
%_s_exmsg_%_app_%-text = '错误文本'.
START-OF-SELECTION.
PERFORM getdata.
PERFORM outdata.
*&---------------------------------------------------------------------*
*& Form getdata
*&---------------------------------------------------------------------*
FORM getdata.
CLEAR it_out[].
SELECT * INTO CORRESPONDING FIELDS OF TABLE it_out
FROM zgbt2
WHERE bukrs IN s_bukrs AND
belnr IN s_belnr AND
gjahr IN s_gjahr AND
aedat IN s_aedat AND
aenam IN s_aenam AND
exmsg IN s_exmsg
ORDER BY PRIMARY KEY .
MESSAGE s000(oo) WITH '数据条数:' sy-dbcnt.
ENDFORM. "getdata
*&---------------------------------------------------------------------*
*& Form outdata
*&---------------------------------------------------------------------*
FORM outdata.
repid = sy-repid.
slayt-zebra = 'X'.
slayt-no_toolbar = 'X'.
slayt-detailinit = 'X'.
slayt-box_fname = 'SELED'.
varnt-report = sy-repid.
varnt-handle = 1.
PERFORM catset TABLES fldct
USING: 'BUKRS' '' '' '公司代码',
'BELNR' '' '' '凭证号',
'GJAHR' '' '' '年度',
'BUZEI' '' '' '项目号',
'HKONT' '' '' '科目',
'AEDAT' '' '' '发送日期',
'AEZET' '' '' '时间',
'AENAM' '' '' '账号',
'EXMSG' '' '' '错误消息'.
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC'
EXPORTING
it_fieldcat_lvc = fldct[]
i_save = 'A'
is_variant = varnt
is_layout_lvc = slayt
i_callback_program = repid
i_callback_user_command = 'USER_COMMAND'
i_callback_pf_status_set = 'SET_STATUS'
TABLES
t_outtab = it_out.
ENDFORM. " outdata
*&---------------------------------------------------------------------*
*& Form SET_STATUS
*&---------------------------------------------------------------------*
FORM set_status USING rt_extab TYPE slis_t_extab.
SET PF-STATUS 'STD_FULL' EXCLUDING rt_extab .
ENDFORM. "SET_STATUS
*---------------------------------------------------------------------*
* FORM catset *
*---------------------------------------------------------------------*
FORM catset TABLES fldcat USING p_field p_reftab p_reffld p_text.
DATA: ls_fldcat TYPE lvc_s_fcat.
ls_fldcat-fieldname = p_field.
ls_fldcat-scrtext_l = p_text.
ls_fldcat-coltext = p_text.
ls_fldcat-ref_table = p_reftab.
ls_fldcat-ref_field = p_reffld.
ls_fldcat-col_opt = 'A'.
CASE ls_fldcat-fieldname.
WHEN 'BELNR'.
ls_fldcat-edit_mask = '==ALPHA'.
WHEN OTHERS.
ENDCASE.
APPEND ls_fldcat TO fldcat .
CLEAR ls_fldcat .
ENDFORM. "catset
*&--------------------------------------------------------------------*
*& Form user_command
*&--------------------------------------------------------------------*
FORM user_command USING r_ucomm TYPE sy-ucomm
r_field TYPE slis_selfield.
DATA: lt_retn TYPE TABLE OF bapiret2.
READ TABLE it_out INDEX r_field-tabindex.
CASE r_ucomm.
WHEN '&IC1'.
SET PARAMETER ID 'BLN' FIELD it_out-belnr .
SET PARAMETER ID 'BUK' FIELD it_out-bukrs.
SET PARAMETER ID 'GJR' FIELD it_out-gjahr.
CALL TRANSACTION 'FB03' AND SKIP FIRST SCREEN .
RETURN.
WHEN 'RESEND'.
PERFORM resend.
RETURN.
WHEN 'REFRESH'.
PERFORM getdata.
WHEN 'TOCLIP'.
PERFORM itabtoclip_alv(zpublicform) TABLES it_out USING 'X'.
RETURN.
ENDCASE.
r_field-row_stable = 'X'.
r_field-col_stable = 'X'.
r_field-refresh = 'X'.
ENDFORM. "user_command
*&---------------------------------------------------------------------*
*& Form re_send
*&---------------------------------------------------------------------*
FORM resend.
DATA: BEGIN OF lt_key OCCURS 0,
bukrs TYPE bseg-bukrs,
belnr TYPE bseg-belnr,
gjahr TYPE bseg-gjahr,
buzei TYPE bseg-buzei,
END OF lt_key.
DATA: lt_gbtab TYPE TABLE OF zbte_send_bseg_to_gb_s1 WITH HEADER LINE.
LOOP AT it_out WHERE seled = 'X'.
MOVE-CORRESPONDING it_out TO lt_key.
APPEND lt_key.
ENDLOOP.
IF sy-subrc <> 0.
MESSAGE e000(oo) WITH '请至少选择一行'.
ENDIF.
SELECT * INTO CORRESPONDING FIELDS OF TABLE lt_gbtab
FROM bseg
FOR ALL ENTRIES IN lt_key
WHERE bukrs = lt_key-bukrs AND
belnr = lt_key-belnr AND
gjahr = lt_key-gjahr AND
buzei = lt_key-buzei.
LOOP AT lt_gbtab.
SELECT SINGLE * INTO CORRESPONDING FIELDS OF lt_gbtab
FROM bkpf
WHERE bukrs = lt_gbtab-bukrs AND
belnr = lt_gbtab-belnr AND
gjahr = lt_gbtab-gjahr.
MODIFY lt_gbtab.
ENDLOOP.
CALL FUNCTION 'ZBTE_PUSH_BSEG_TO_GB' IN BACKGROUND TASK
EXPORTING
updkz = 'U'
commit = 'X'
TABLES
gbtab = lt_gbtab.
COMMIT WORK.
MESSAGE s000(oo) WITH '已在后台重新发送,请稍后刷新查看最新状态'.
ENDFORM. "resend
如果是讲究一点的开发人员,还会再做一个初始化的程序,用来推送历史凭证,以及一个检查程序,用来检查外部Oracle数据库的凭证是不是缺少。
注:在外部Oracle数据库取数的时候,可以不使用NativeSQL,如下:
总结:这个凭证推送的方法代表了一大类SAP数据推送的解决方案,方案简短来说就是在保存的出口/BADI里面调用一个Update Module,然后Update Module里面调用tRFC(最好是bgRFC),然后保存日志。调用外部的方式可以是RFC、Webservice、DB、FTP等等。