关于改造维护工单BAPI_ALM_ORDER_MAINTAIN用于生产订单组件批量修改
1、研究背景
1.1、业务背景
由于销售、研发、工艺等需要频繁变更,导致工单中组件需要频繁的进行变更,修改组件的物料,数量,库存地点,工序等内容。
1.2、技术痛点
为了满足要求,使用了函数:CO_XT_COMPONENT_ADD和CO_XT_COMPONENT_CHANGE用于组件的新增和修改,使用CO_XT_COMPONENTS_DELETE用于组件的批量删除。
痛点1:新增和修改函数,每次只能更改一行组件,而且无法按照订单维度,批量修改组件,只能按照组件维度,多次调用函数,每一次都需要提交保存一遍工单,效率很慢。
痛点2:项目中在工单修改保存时,调用了同步接口推送工单到MES,且MES必须返回成功结果,工单修改才允许保存。受接口+网络影响,工单保存效率很慢。
痛点3:受制于同步接口等原因,导致工单保存时耗时较长,且一直保持着对工单的锁定状态。而批导程序紧接着处理第二行同一工单的组件数据,就会导致出现工单锁表报错。
2、改造BAPI
2.1、BAPI现状
维护订单的BAPI:BAPI_ALM_ORDER_MAINTAIN,拥有丰富的工单修改字段,然而对于生产订单限制修改,只允许修改维护订单。
另外,经过测试,还有反冲强制取物料主数据内容导致不能指定;组件分配报错问题;组件分配BOM行号重新被指定问题等,需要逐一进行修改。
本文档,对BAPI的改造是否满足读者项目的要求,还需读者多加测试
2.1.1、反冲被默认
在原逻辑中,有赋默认值的函数,里面根据物料主数据对传入的反冲标识做了更改
调用子例程
在以下代码处修改了传入的反冲标识
2.1.2、组件分配
在工单中,工序可以通过主数据带入工艺路线,也可以直接在工单前台手工创建。
问题在于,当在CO02手工创建时,与CA01创建的工序不一样,缺少了部分字段值,最主要的序列字段为空
导致组件分配给30工序后,前台查看组件出现报错,或组件全部丢失,最终判定都是因为序列PLNFL字段为空导致
2.1.3、BOM行项目
标准程序中,会根据要分配的工序,查找已经存在分配给该工序的组件,获取这些组件的最大BOM号,+10获得最新BOM号。
如果当前分配的工序,没有存在已经分配给该工序的组件,则直接+10得到最新BOM号0010。
例如:将0030行分配给20工序,而现有的两个工序都是10,没有20工序的组件,则BOM号0030行分配完之后,BOM号将变为0010
2.1.4、工单类型限制
该BAPI只能修改维护订单,不能修改其他类型工单
2.2、增强改造(未采用)
第一次改造,使用了代码修改器,修改原有逻辑,如下所示:
跳过反冲默认
跳过订单类型限制
但是最终感觉太多的地方要修改,并且从未来持续优化功能的角度出发,写大量的代码片段不是一件好事情,且四代增强也要花很多时间调试,因此打算放弃改源码,而直接做代码拷贝。参考2.3自定义BAPI
2.3、自定义BAPI
根据多次的源码调试,找到程序运行相关的函数、包含文件等,依次进行代码的复制,过程不做过多说明,结果如下:
1、需要复制的函数组及函数,将IBAPI_ALM_ORDER复制为ZBAPI_ALM_ORDER,其余同理
2、ZBAPI_C_DEFAULT_VALUES_01 修改反冲标识
3、LZBAPI_ALM_ORDER_PROCESSINGF10
在AFVC表中,序列PLNFL为空,但是序列APLFL有值,可以使用
在程序中为PLNFL赋值,解决组件分配报错
4、LZBAPI_CF03
注释掉BOM号递增逻辑
5、ZBAPI_H_ORDER_READ
去掉对生产订单的限制
另外在标准逻辑调用过程中上下文需要做修改的程序可以参考3、修改路径图。
3、修改路径图
4、参考源码
调用BAPI代码示例:
"--------------------@斌将军-------------------- LOOP AT lt_alv INTO DATA(ls_alv) GROUP BY ls_alv-aufnr. REFRESH:lt_methods[],lt_component[],lt_componentup[],lt_return[]. CLEAR:lv_rspos. CLEAR:ls_methods. ls_methods-refnumber = 1. ls_methods-method = 'SAVE'. ls_methods-objectkey = ls_alv-aufnr. APPEND ls_methods TO lt_methods. LOOP AT GROUP ls_alv ASSIGNING FIELD-SYMBOL(<fs_alv>). READ TABLE gt_resb INTO gs_resb WITH KEY aufnr = <fs_alv>-aufnr rspos = <fs_alv>-rspos BINARY SEARCH. IF sy-subrc EQ 0."修改逻辑 IF gs_resb-xloek = 'X'. <fs_alv>-icon = icon_led_red. <fs_alv>-log = <fs_alv>-log && '项目已删除,不允许修改'. MODIFY gt_alv FROM <fs_alv>. CONTINUE. ENDIF. IF gs_resb-enmng > abs( gs_alv-bdmng ). <fs_alv>-icon = icon_led_red. <fs_alv>-log = <fs_alv>-log && '修改的数量小于已提货数,不允许修改'. MODIFY gt_alv FROM <fs_alv>. CONTINUE. ELSE. lv_rspos = lv_rspos + 1. CLEAR:ls_component. * ls_component-item_number = gs_alv-posnr. "BOM ls_component-reserv_no = gs_resb-rsnum. "预留号 ls_component-res_item = gs_resb-rspos. "预留项目 ls_component-material = <fs_alv>-idnrk. "物料编码 ls_component-requirement_quantity = <fs_alv>-bdmng."数量 * ls_component-plant = gs_alv-werks. IF <fs_alv>-lgort IS NOT INITIAL. ls_component-stge_loc = <fs_alv>-lgort."存储地点 ENDIF. ls_component-batch = <fs_alv>-charg."批次号 ls_component-req_date = <fs_alv>-bdter."组件的需求日期 IF <fs_alv>-rgekz IS NOT INITIAL. ls_component-backflush = 'X'."反冲标识 ELSE. ls_component-backflush = ''."反冲标识 ENDIF. ls_component-special_stock = <fs_alv>-sobkz."特殊库存 ls_component-activity = gs_resb-vornr."操作/活动编号 APPEND ls_component TO lt_component. CLEAR:ls_componentup. * ls_componentup-item_number = 'X'. "BOM ls_componentup-material = 'X'. "物料编码 ls_componentup-requirement_quantity = 'X'."数量 * ls_componentup-plant = 'X'. IF <fs_alv>-lgort IS NOT INITIAL. ls_componentup-stge_loc = 'X'."存储地点 ENDIF. ls_componentup-batch = 'X'."批次号 ls_componentup-req_date = 'X'."组件的需求日期 ls_componentup-backflush = 'X'."反冲标识 ls_componentup-special_stock = 'X'."特殊库存 APPEND ls_componentup TO lt_componentup. CLEAR:ls_methods. ls_methods-refnumber = lv_rspos. "参照组件编号 ls_methods-objecttype = 'COMPONENT'. "组件 ls_methods-method = 'CHANGE'. "修改 ls_methods-objectkey = <fs_alv>-aufnr. "订单号 ls_methods-objectkey+12(4) = gs_resb-rspos. "预留行 APPEND ls_methods TO lt_methods. "组件分配 IF <fs_alv>-vornr IS NOT INITIAL. lv_rspos = lv_rspos + 1. CLEAR:ls_component. ls_component-reserv_no = gs_resb-rsnum. "预留号 ls_component-res_item = gs_resb-rspos. "预留项目 ls_component-activity = <fs_alv>-vornr."新分配的工序 APPEND ls_component TO lt_component. CLEAR:ls_componentup. ls_componentup-activity = 'X'. APPEND ls_componentup TO lt_componentup. CLEAR:ls_methods. ls_methods-refnumber = lv_rspos. "参照组件编号 ls_methods-objecttype = 'COMPONENT'. "组件 ls_methods-method = 'REASSIGN'. "重新分配 ls_methods-objectkey = <fs_alv>-aufnr. "订单号 ls_methods-objectkey+12(4) = gs_resb-rspos. "预留行 APPEND ls_methods TO lt_methods. ENDIF. ENDIF. ELSE."新增逻辑 lv_rspos = lv_rspos + 1. CLEAR:ls_component. ls_component-item_number = <fs_alv>-posnr. "BOM ls_component-material = <fs_alv>-idnrk. "物料编码 ls_component-requirement_quantity = <fs_alv>-bdmng."数量 ls_component-plant = <fs_alv>-werks."工厂 ls_component-stge_loc = <fs_alv>-lgort."存储地点 ls_component-batch = <fs_alv>-charg."批次 ls_component-req_date = <fs_alv>-bdter."组件的需求日期 IF <fs_alv>-rgekz IS NOT INITIAL. ls_component-backflush = 'X'."反冲标识 ELSE. ls_component-backflush = ''."反冲标识 ENDIF. ls_component-special_stock = <fs_alv>-sobkz."特殊库存 ls_component-activity = <fs_alv>-vornr."工序 ls_component-item_cat = 'L'."项目类别 APPEND ls_component TO lt_component. CLEAR:ls_componentup. ls_componentup-item_number = 'X'. "BOM ls_componentup-material = 'X'. "物料编码 ls_componentup-requirement_quantity = 'X'."数量 ls_componentup-plant = 'X'."工厂 ls_componentup-stge_loc = 'X'."存储地点 ls_componentup-batch = 'X'."批次 ls_componentup-req_date = 'X'."组件的需求日期 ls_componentup-backflush = 'X'."反冲标识 ls_componentup-special_stock = 'X'."特殊库存 ls_componentup-activity = 'X'."工序 ls_componentup-item_cat = 'X'."项目类别 APPEND ls_componentup TO lt_componentup. CLEAR:ls_methods. ls_methods-refnumber = lv_rspos. "参照组件编号 ls_methods-objecttype = 'COMPONENT'. "组件 ls_methods-method = 'CREATE'. "创建 ls_methods-objectkey = <fs_alv>-aufnr. "订单号 APPEND ls_methods TO lt_methods. ENDIF. ENDLOOP. READ TABLE gt_afko INTO DATA(ls_afko) WITH KEY aufnr = ls_alv-aufnr BINARY SEARCH. IF sy-subrc EQ 0. CALL FUNCTION 'T350_READ' EXPORTING auart = ls_afko-auart EXCEPTIONS no_t350_entry = 1 OTHERS = 2. * override buffered table T350 ASSIGN ('(SAPLINST)T350') TO <t350>. IF sy-subrc = 0. <t350>-auart = ls_afko-auart. ENDIF. ENDIF. "调用ZBAPI CALL FUNCTION 'ZBAPI_ALM_ORDER_MAINTAIN' TABLES it_methods = lt_methods it_component = lt_component[] it_component_up = lt_componentup[] return = lt_return. CLEAR:lv_check,lv_message. LOOP AT lt_return INTO ls_return WHERE type CA 'AEX'. lv_check = 'E'. lv_message = lv_message && ls_return-message. CLEAR:ls_return. ENDLOOP. IF lv_check = 'E'. CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'. ls_alv-icon = icon_led_red. ls_alv-log = lv_message. ELSE. CALL FUNCTION 'BAPI_TRANSACTION_COMMIT' EXPORTING wait = 'X'. ls_alv-icon = icon_led_green. ls_alv-log = '添加成功'. ENDIF. MODIFY gt_alv FROM ls_alv TRANSPORTING icon log WHERE aufnr = ls_alv-aufnr AND xloek = '' AND item = 'X' AND icon <> icon_led_red. CLEAR:ls_alv. ENDLOOP. "--------------------@斌将军--------------------
5、补充说明
5.1、并行工序组件分配研究
对读者提出的该BAPI改造后是否能进行并行工序组件分配进行了研究。
在组件分配过程中,程序调用了函数CO_BT_READ_MASTER_SEQUENCE,通过工单编号,查询对应的序列信息
但是函数中通过常量限制了序列为0,即只查找标准顺序,不查找并行顺序
导致获取的序列值为000000,
导致查询AFVC数据时,获取到标准顺序0的工序,而不是并行顺序1的工序
综上所述,认为直接使用该函数无法进行并行工序的组件分配。
但是源代码逻辑已经梳理很清晰,想必稍加改造,例如直接抛内存传值,查询对应的并行顺序的工序,应该就可以解决问题。
读者有兴趣可自行研究一下。
定期更文,欢迎关注