ABAP BADI: ME_PROCESS_PO_CUST~PROCESS_ITEM 报错消息异常处理
场景:
最近业务端反馈创建采购订单时,有报错消息,修正之后,报错消息还在,且会重复,但是能正常保存。
听上去很神奇,经过确认,此类报错消息都是PROCESS_ITEM中的E类型消息,业务顾问也说有过此类情况,但是不影响正常业务,所以没关注。
(可能有人会想,校验逻辑为何不放在CHECK方法中?原因就在于PROCESS_ITEM方法在编辑时可以实时反馈报错消息,而CHECK方法仅在点击CHECK按钮或保存按钮时触发,相对来说不符合中国用户的习惯,这里也不准备将校验逻辑转移到CHECK方法中)
问题排查及处理:
PROCESS_ITEM中报错方式如下:
MESSAGE e060(zmsg) WITH ls_mepoitem-ebelp.
(他山之石)百度&必应一下,发现大神们在方法PROCESS_ITEM 中报错都是通过宏完成消息处理。
例如:ABAP ME_PROCESS_PO_CUST消息异常-CSDN博客
INCLUDE mm_messages_mac. "useful macros for message handling
DATA: lt_events TYPE mmpur_event_list.
DATA: ls_event TYPE mmpur_event_entry.
DATA: lo_business_object TYPE REF TO if_message_obj_mm.
满足校验逻辑,报错消息处理:
mmpur_business_obj_id ls_mepoitem-id."确定错误消息显示到哪个行项目上
mmpur_context MMCNT_USER_EXIT."设置识别编号
MESSAGE e060(zmsg) WITH ls_mepoitem-ebelp INTO DATA(lv_dummy).
mmpur_message_forced sy-msgty sy-msgid sy-msgno sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
不满足校验逻辑,需清除报错消息:
*---清空消息先找消息ID
CALL METHOD cl_message_handler_mm=>get_handler
IMPORTING
ex_handler = gl_message_handler.CALL METHOD gl_message_handler->getlist
IMPORTING
ex_events = lt_events.
LOOP AT lt_events INTO ls_event.*--- check context
IF ls_event-context = mmcnt_user_exit.*--- remove message
lo_business_object = ls_event-business_obj.
CALL METHOD gl_message_handler->remove_by_bo
EXPORTING
im_business_obj = lo_business_object
im_context = ls_event-context.
ENDIF.
ENDLOOP.
通过这个方式确实可以解决当前问题,但是报错后依旧能成功保存采购订单,所以继续研究,发现原本直接报错方式在标准逻辑中相当于抛异常,上一层堆栈中SY-SUBRC = 1 ,而通过宏处理的方式,上一层堆栈中SY-SUBRC = 0, 无法中断标准逻辑。所以需要强行中断标准逻辑才行,继续研究,发现IF_PURCHASE_ORDER_ITEM_MM~INVALIDATE有类似功能,在校验成功后,报错消息处理时调用invalidate方法,可以阻止保存过账。
CALL METHOD im_item->invalidate( ).
到此,理论上已经解决之前的所有问题了。
然而,我们系统中PROCESS_ITEM方法已经15个校验报错逻辑,其中几处检验逻辑蛮复杂的,即便本人有心整改,但业务顾问测试起来也很麻烦。
那么有没有更好的方案呢?(既然都写文章了,必须有)
虽然原本报错方式有点缺陷,但是功能完整,这里只需要考虑及时清除历史消息就行。
那么新思路来了,在 PROCESS_ITEM方法最开始的地方,先清除本方法涉及的所有报错消息,后面走原逻辑,该报还是报错,不会重复,理论上可以完美解决现有问题。
基于这个思路,逻辑如下:
***************************"采购订单自定义检查报错消息统一清除 ***************************
"清空消息先找消息ID
IF sy-tcode = 'ME21N' OR sy-tcode = 'ME22N' OR sy-tcode = 'ME23N'.
CLEAR:lt_events.
CALL METHOD cl_message_handler_mm=>get_handler
IMPORTING
ex_handler = gl_message_handler.
CALL METHOD gl_message_handler->getlist
IMPORTING
ex_events = lt_events.
LOOP AT lt_events INTO ls_event.
IF strlen( ls_event-signature ) >= 4.
IF ls_event-signature(4) = 'ZMSG'.
"Remove Message
lo_business_object = ls_event-business_obj.
CALL METHOD gl_message_handler->remove_by_bo
EXPORTING
im_business_obj = lo_business_object
im_context = ls_event-context.
ENDIF.
ENDIF.
ENDLOOP.
ENDIF.
***************************"采购订单自定义检查报错消息统一清除 ***************************
看到这里,你可能会疑惑?清除消息时怎么没有用CONTEXT判断呢?我也想用,可是非自定义的编码,无法直接使用。我这边直接使用自定义的消息类判断,条条大道通罗马,能达到效果就行。
有报错,只能持有。
修正后,可以保存。
Ps. 标准方案宏方案中,CONTEXT赋值可以使用自定义编码,只要不和标准的冲突就行。
本文只是记录自己的处理思路,如果有更好的方法,欢迎交流。