DN的过账一般使用的是都是 Function: WS_DELIVERY_UPDATE ,其实这是一个经常使用到的函数,注意,这不是一个BAPI,只是一个函数,但是这个函数里面有一个update funtion ,所以这个函数并没有返回参数 return ( update 和异步的funtion都没有返回参数 )。
DN的创建/修改 / 以及过账中BAPI,创建利润中心,成本中心:
BAPI_OUTB_DELIVERY_CREATE_SLS
BAPI_OUTB_DELIVERY_CREATE_STO
BAPI_OUTB_DELIVERY_CHANGE
BAPI_PROFITCENTER_CREATE
BAPI_COSTCENTER_CREATEMULTIPLE
都使用的这个函数,但是这个函数里面会调用一个 update funtion,
路径如下:
WS_DELIVERY_UPDATE_2
delivery_save
beleg_sichern_01
beleg_sichern_post
lieferungen_verbuchen_db
这就导致即使我们在外部使用 commit work and wait.
这样此时我们 去读取数据库LIKP获取字段状态的时候,偶尔会获取不到最新值。
此时场景一:交货单过账的时候,由于该函数没有返回参数,所以没有如果直接使用下面的SQL判断,是无法得到最新的结果。
SELECT SINGLE * FROM likp INTO @DATA(ls_likp) WHERE vbeln = @lf_vbeln AND wbstk = 'C'.
IF sy-subrc = 0. " 过账成功
ev_message = ev_message && '过账成功'.
ELSE.
ev_message = ev_message && '过账失败'.
ENDIF.
此时一般方法是:
DO循环中,间隔一秒去读取记录,读到就退出,一般会尝试读个3-5次。
这种方法基本上不会出错,但是太慢了,慢到经常大批量做的时候受不了。
第二种:
本地调用 SET UPDATE TASK LOCAL
当使用 SET UPDATE TASK LOCAL 的时候,会将所有的更新函数( IN UPDATE TASK 函数, IN COMMIM的例程 )的数据注册到SAP MEMORY,当使用 COMMIT WORK 的时候,系统会在当前进程执行更新语句。这样只有在UPDATE 函数的逻辑更新结束才会执行后面的自定义的逻辑。
但是这样会有一个问题,当标准函数发生Dump的时候,后面的自定义程序也会中断,不使用本地调用的话不会这样,而是在SM13中看到错误的日志,自定义部分会正常执行。
la_vbkok-vbeln_vl = lf_vbeln.
la_vbkok-wabuc = 'X'.
la_vbkok-wadat_ist = sy-datum.
LOOP AT lt_item INTO ls_item.
ls_vbpok-vbeln_vl = ls_item-deliv_numb. "delivery
ls_vbpok-posnr_vl = ls_item-deliv_item.
ls_vbpok-vbeln = ls_item-deliv_numb. "delivery
ls_vbpok-posnn = ls_item-deliv_item.
ls_vbpok-matnr = ls_item-material. "delivery
ls_vbpok-pikmg = ls_item-dlv_qty.
ls_vbpok-lgort = 'L001'.
ls_vbpok-kzlgo = 'X'.
ls_vbpok-xwmpp = 'X'.
APPEND ls_vbpok TO lt_vbpok.
ENDLOOP.
SET UPDATE TASK LOCAL.
CALL FUNCTION 'WS_DELIVERY_UPDATE'
EXPORTING
vbkok_wa = la_vbkok
commit = 'X'
delivery = lf_vbeln
TABLES
vbpok_tab = lt_vbpok
prot = lt_prot.
LOOP AT lt_prot INTO ls_prot WHERE msgty CA 'AEX'.
CALL FUNCTION 'MESSAGE_TEXT_BUILD'
EXPORTING
msgid = ls_prot-msgid
msgnr = ls_prot-msgno
msgv1 = ls_prot-msgv1
msgv2 = ls_prot-msgv2
msgv3 = ls_prot-msgv3
msgv4 = ls_prot-msgv4
IMPORTING
message_text_output = lv_message.
ev_msgty = cs_status_e.
CONCATENATE ev_message lv_message ';' INTO ev_message.
ENDLOOP.
IF sy-subrc = 0.
ELSE.
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
EXPORTING
wait = space.
SELECT SINGLE * FROM likp INTO @DATA(ls_likp) WHERE vbeln = @lf_vbeln AND wbstk = 'C'.
IF sy-subrc = 0. " 过账成功
wa_input-dn_post = 'X'.
ev_message = ev_message && '过账成功'.
ELSE.
ev_msgty = cs_status_e.
ev_message = ev_message && '过账失败'.
ENDIF.
ENDIF.
方法二:在funtion 外面再使用一个update function ,
交货单自动过账程序原来的逻辑期望通过这种方式确保交货单过账的更新和自定义表的更新都在更新进程中执行, 如果交货单更新报错,会中断更新过程, 自定义表也就不会更新了.
特殊BAPI函数的问题
但是交货单的过账BAPI使用这种方式时. 系统没有确保二者的更新写入同一个更新进程.而是写到了不同的更新进程中. 如图二(两个更新在不同的更新进程中).
交货单中的底层逻辑有时候会启动另外一个进程执行函数.新启动的进程中的更新就无法和当前进程的更新注册到同一个更新进程中了. 可以通过在标准更新过程中及自定义更新函数中强制报错模拟出这个场景.
一
问题的发现
最近发现自动过账程序表中记录的单据过账状态与单据的实际过账状态不一致.
自动过账程序根据写入ZTA_DN_POST表的交货单, 调用BAPI 执行交货单过账, 成功后,更新表中的处理标记 ( ZTA_DN_POST-VPROC='S' ).
详见链接
无峰,公众号:ABAP 技巧与实战SAP开发框架系列之 自动单据
更新不一致一般都能在SM13中找到响应的报错信息.大部分报错是在程序或表结构修改上传生产系统时产生的.
所以从传输或作业机制中需要为后台作业保留一个空窗期, 程序传输在这个空窗期中执行. 以便减少因为传输导致的后台执行报错.
快速方便的的后台作业调度
无峰,公众号:ABAP 技巧与实战SAP批量后台作业定义和管控
二
自动过账更新逻辑
调用BAPI执行交货单过账. 判断BAPI调用成功,然后更新ZTA_DN_POST. 更新语句放入更新函数,使用IN UPDATE TASK 调用. 最后执行COMMIT WORK. 提交更新.
三
IN UPDATE TASK语句功能
查看IN UPDATE TASK 语句的帮助可以了解到 IN UPDATE TASK 中的代码会注册要更新的数据到更新进程中. COMMIT WORK 语句时才会触发所有更新语句的执行.
如果业务单据执行过程报错, 在事务代码SM13中可以看到更新条目,更新条目中能看到标准更新和自定义表更新在同一个更新进程中. 如图一
交货单自动过账程序原来的逻辑期望通过这种方式确保交货单过账的更新和自定义表的更新都在更新进程中执行, 如果交货单更新报错,会中断更新过程, 自定义表也就不会更新了.
图一
四
特殊BAPI函数的问题
但是交货单的过账BAPI使用这种方式时. 系统没有确保二者的更新写入同一个更新进程.而是写到了不同的更新进程中. 如图二(两个更新在不同的更新进程中).
具体原因的猜想
SPRING
交货单中的底层逻辑有时候会启动另外一个进程执行函数.新启动的进程中的更新就无法和当前进程的更新注册到同一个更新进程中了. 可以通过在标准更新过程中及自定义更新函数中强制报错模拟出这个场景.
图二
五
问题解决的方式
在调用交货单过账BAPI函数之前
执行SET LOCAL UPDATE 语句. 该语句会让所有的更新函数(IN UPDATE TASK 调用)的数据注册到SAP MEMORY 中. 执行COMMIT WORK语句时, 系统在当前进程中执行数据更新的语句. 这样可以确保标准更新和自定义的更新都在当前进程中执行. 如果标准更新报错,进程会中止,自定义表的更新也就会失败.
但是这样设置有一个弊端: 如果更新报错,程序的后续处理会中止. 如果因为某个单据数据的异常导致该单据一直更新异常. 设置本地更新就会导致后续单据都无法处理.
六
总结
尽量使用 IN UPATE TASK 提交自定义表的更新, 应用系统的更新进程机制, 如果存在更新错误,可以通过SM13查看报错的信息.
尽量不使用SET UPDATE TASK LOCAL ?(这个结论可能会有不同的看法)
因为这个语句会让当前进程执行更新过程,更新报错会导致程序中止. 影响其它正确单据的执行.同时无法使用SM13查看报错.
但是对交货单过账等比较变态的BAPI函数(函数中又启动了新的进程执行逻辑). 在发现程序更新不一致时, 建议启用 SET UPDATE TASK LOCAL 以确保更新一致