赞
踩
此文适合对SAP PP模块生产订单业务有一定了解的PP顾问和ABAP开发人员阅读。
所在项目中遇到一个特殊需求,创建拆解工单时,拆除整机得到半成品和原材料。
这时候不是增加组件预留数量,而是减少预留数量(因为半成品和原材料增多了),业务方的要求是,这种特殊类型工单,组件数量默认是负数。说实话这种需求我见过,这种解决方案我是第一次听说(更别提见)。为什么不用物料凭证移动类型配置,同仓转储之类的成熟解决方案,我不知道,大概是财务科目配不明白吧。
OK,我的任务是实现需求,别的咱也不知道,咱也不敢问。
需求是通过接口创建拆解工单,组件物料是定制的,数量是负的。
我之前(差不多10年前)一直用一个标准BAPI处理PP生产订单的增删改,就是BAPI_ALM_ORDER_MAINTAIN。
实际上这是一个PM模块维修/服务工单使用的BAPI,SAP的PP,PM,PS模块使用了同一套底表来存储业务数据(问就是架构原因,我也不知道为什么),所以这个API也能够用来处理PP生产订单。为什么非得用PM的API来改PP的数据呢,因为PP模块没有相应功能的API,自带的BAPI_PRODORD_CREATE没有什么考虑定制组件之类的功能,SAP就是摆烂,OK?
SAP曾推出过一系列Note补丁,禁止客户开发人员使用这个BAPI改生产订单,都被我司的BASIS同事屏蔽了,所以我们一直这么用。
我在目前项目的系统一看,BAPI_ALM_ORDER_MAINTAIN这个BAPI不能用了。Note打了好几波,摘都摘不掉。
接口使用的账号不是dialog账号,不能支持BDC的CO01方式,只能另想办法。
最终考虑,使用COXT函数组的CO_XT_COMPONENT_ADD来添加定制组件和加工数量(负数)。
这一步会根据表头的物料,工厂,订单类型,数量,计划起始/截止日期生成生产订单,同时会根据订单物料对应的生产BOM自动计算加工组件物料和数量,按常规方式不能改(非常规可以,我们下面聊)。
我们需要做的是,删除不需要的“自动计算”出来的组件,替换成需要的定制组件。
- FORM create_ppo.
- DATA:ls_orderdata TYPE bapi_pp_order_create,
- ls_return TYPE bapiret2,
- lv_order_number TYPE aufnr.
-
- * 创建生产订单
- CLEAR:ls_orderdata.
- ls_orderdata-material = gs_h-matnr.
- ls_orderdata-plant = gs_h-dwerk.
- ls_orderdata-order_type = gs_h-auart.
- ls_orderdata-quantity = gs_h-pgmng.
- ls_orderdata-basic_start_date = gs_h-gstrp.
- ls_orderdata-basic_end_date = gs_h-gltrp.
- CALL FUNCTION 'BAPI_PRODORD_CREATE'
- EXPORTING
- orderdata = ls_orderdata
- IMPORTING
- return = ls_return
- order_number = lv_order_number.
- IF ls_return-type = 'E'.
- CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
- MESSAGE ID ls_return-id
- TYPE ls_return-type
- NUMBER ls_return-number
- WITH ls_return-message_v1
- ls_return-message_v2
- ls_return-message_v3
- ls_return-message_v4.
- ELSE.
- CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
- EXPORTING
- wait = 'X'.
- ENDIF.
- DO 10 TIMES.
- SELECT COUNT(*)
- FROM aufk
- WHERE aufnr = lv_order_number.
- IF sy-subrc = 0.
- EXIT.
- ELSE.
- WAIT UP TO 1 SECONDS.
- ENDIF.
- ENDDO.
- * 由于生产订单创建时自动生成组件,需要先清空组件
- PERFORM delete_components USING lv_order_number.
- * 添加需要的组件
- PERFORM add_components USING lv_order_number.
- * 没有抛出错误消息,证明工单操作成功
- MESSAGE S100(CO) WITH lv_order_number.
- LEAVE TO SCREEN 0.
- ENDFORM.

2. 删除已存在的订单组件
这里需要调用CO_XT_COMPONENTS_DELETE这函数,删除工单对应RESB预留表中所有已存在组件。
COXT函数组的一些函数使用时,需要执行CO_XT_ORDER_PREPARE_COMMIT预提交,正式提交后需要执行CO_XT_ORDER_INITIALIZE再次初始化,这一点和PS模块的一些API类似(毕竟是同一套底表)。
- FORM delete_components USING pv_order_number TYPE aufnr.
- *** Deleting Components from Production Order
-
- DATA: lt_resbkeys TYPE coxt_t_resbdel,
- lt_return TYPE STANDARD TABLE OF bapiret2,
- ls_return TYPE bapiret2,
- lv_error TYPE flag,
- ls_bapireturn TYPE coxt_bapireturn.
-
- * Fetch existing components of given Production Order
- SELECT rsnum, rspos INTO TABLE @DATA(lt_resb)
- FROM resb
- WHERE aufnr = @pv_order_number. " Previously created order
- IF sy-subrc EQ 0.
- lt_resbkeys = CORRESPONDING #( lt_resb ).
- ENDIF.
- IF NOT lt_resbkeys[] IS INITIAL.
- * BAPI to delete the components of Production Order
- CALL FUNCTION 'CO_XT_COMPONENTS_DELETE'
- EXPORTING
- it_resbkeys_to_delete = lt_resbkeys
- IMPORTING
- e_error_occurred = lv_error
- TABLES
- ct_bapireturn = lt_return
- EXCEPTIONS
- delete_failed = 1
- OTHERS = 2.
- IF lv_error = space.
- CALL FUNCTION 'CO_XT_ORDER_PREPARE_COMMIT'
- IMPORTING
- es_bapireturn = ls_bapireturn
- e_error_occurred = lv_error.
- IF ( ls_bapireturn-type = 'S' OR
- ls_bapireturn-type = 'W' OR
- ls_bapireturn-type = 'I' ) OR
- ls_bapireturn IS INITIAL.
- CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'.
- CALL FUNCTION 'CO_XT_ORDER_INITIALIZE'.
- ELSE.
- CLEAR: lv_error.
- CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
- MESSAGE ID ls_bapireturn-id
- TYPE ls_bapireturn-type
- NUMBER ls_bapireturn-number
- WITH ls_bapireturn-message_v1
- ls_bapireturn-message_v2
- ls_bapireturn-message_v3
- ls_bapireturn-message_v4.
- ENDIF.
- ELSE.
- CLEAR lv_error.
- CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
- READ TABLE lt_return INTO ls_return WITH KEY type = 'E'.
- IF sy-subrc = 0.
- MESSAGE ID ls_return-id
- TYPE ls_return-type
- NUMBER ls_return-number
- WITH ls_return-message_v1
- ls_return-message_v2
- ls_return-message_v3
- ls_return-message_v4.
- ENDIF.
- ENDIF.
- ENDIF.
- ENDFORM.

3. 添加我们需要的定制组件
需要注意,CO_XT_COMPONENT_ADD这个函数每次只能处理一条数据。如果添加多条需要循环处理,都成功后统一预提交,再正式提交,清缓存。
这个函数不是标准BAPI,所以需要修改主程序内存中的预留表对应的变量(SAPLCOBC)RESB_BT[],给组件对应预留数据的行项目字段赋值,配合让它生效。这么做非常别扭,但没别的选择余地。
值得一提的是,某些旧版本系统在执行完添加组件后,不会生成组件的状态对象。你会看到组件状态是空的,不是CRTD已创建。这时候你需要根据预留号和预留行项目号,自己创建一个状态对象。新版本不需要了,对这一步有疑问的伙计,我们可以单独再聊。
- FORM add_components USING pv_order_number TYPE aufnr.
- DATA: ls_requ TYPE coxt_s_quantity,
- ls_storage TYPE coxt_s_storage_location,
- ls_storagex TYPE coxt_s_storage_locationx,
- ls_return TYPE coxt_bapireturn,
- lt_return TYPE coxt_t_bapireturn,
- lv_msg TYPE string,
- lv_tabix TYPE sy-tabix,
- lv_postp TYPE postp,
- lv_operation TYPE co_aplzl,
- lv_sequence TYPE plnfolge,
- lv_material TYPE matnr,
- lv_positionno TYPE positionno,
- lv_error TYPE flag.
-
- TYPES: BEGIN OF ty_resb_bt.
- INCLUDE TYPE resbb.
- TYPES: indold LIKE sy-tabix,
- no_req_upd LIKE sy-datar,
- END OF ty_resb_bt.
-
- TYPES: lt_resb_bt TYPE TABLE OF ty_resb_bt.
- FIELD-SYMBOLS: <ft_resb_bt> TYPE lt_resb_bt,
- <fs_resb_bt> TYPE ty_resb_bt.
-
- SELECT SINGLE aufnr, aufpl
- INTO @DATA(ls_afko)
- FROM afko
- WHERE aufnr = @pv_order_number.
- IF sy-subrc EQ 0.
- * Fetch operation to which it has to be assigned
- SELECT SINGLE aufpl, aplzl, plnfl
- INTO @DATA(ls_afvc)
- FROM afvc
- WHERE aufpl = @ls_afko-aufpl.
- IF sy-subrc EQ 0.
- lv_operation = ls_afvc-aplzl.
- lv_sequence = ls_afvc-plnfl.
- ENDIF.
- ENDIF.
- * gt_i为存储定制组件的内表,gs_i为对应的工作区
- LOOP AT gt_i INTO gs_i.
- lv_tabix = sy-tabix.
- CLEAR: ls_requ,ls_storage,ls_storagex.
- ls_requ-quantity = gs_i-menge.
- ls_requ-uom = gs_i-meins.
-
- ls_storage-werks = gs_h-dwerk.
- ls_storage-lgort = gs_i-lgpro.
-
- ls_storagex-werks = 'X'.
- ls_storagex-lgort = 'X'.
- lv_positionno = sy-tabix * 10.
-
- lv_postp = 'L'.
- lv_material = gs_i-idnrk.
- * BAPI to add components to Production Order
- CALL FUNCTION 'CO_XT_COMPONENT_ADD'
- EXPORTING
- is_order_key = pv_order_number
- i_material = lv_material
- is_requ_quan = ls_requ
- i_operation = lv_operation
- i_sequence = lv_sequence
- is_storage_location = ls_storage
- is_storage_locationx = ls_storagex
- i_postp = lv_postp
- i_posno = lv_positionno
- IMPORTING
- es_bapireturn = ls_return
- e_error_occurred = lv_error.
- IF lv_error IS NOT INITIAL.
- EXIT.
- ENDIF.
- ENDLOOP.
- IF lv_error = space.
- CLEAR: lv_tabix,
- ls_return.
-
- * Modify POSNR via ASSIGN before DB update to correct the blank
- * item number in Components due to incompatible types of I_POSNO
- * (type CIF_R3RES-POSITIONNO) and RESB-POSNR
- ASSIGN ('(SAPLCOBC)RESB_BT[]') TO <ft_resb_bt>.
- LOOP AT <ft_resb_bt> ASSIGNING <fs_resb_bt>.
- lv_tabix = sy-tabix * 10.
- <fs_resb_bt>-posnr = lv_tabix.
- CLEAR lv_tabix.
- ENDLOOP.
-
- * Commit transaction
- CALL FUNCTION 'CO_XT_ORDER_PREPARE_COMMIT'
- IMPORTING
- es_bapireturn = ls_return
- e_error_occurred = lv_error.
-
- IF ( ls_return-type = 'S' OR
- ls_return-type = 'W' OR
- ls_return-type = 'I' ) OR
- ls_return IS INITIAL.
- * Commit data
- CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'.
- CALL FUNCTION 'CO_XT_ORDER_INITIALIZE'.
- ELSE.
- CLEAR: lv_error.
- * Data Rollback
- CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
- IF ls_return-type = 'E'.
- MESSAGE ID ls_return-id
- TYPE ls_return-type
- NUMBER ls_return-number
- WITH ls_return-message_v1
- ls_return-message_v2
- ls_return-message_v3
- ls_return-message_v4.
- ENDIF.
- ENDIF.
- ELSE.
- CLEAR: lv_error.
- * Data Rollback
- CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
- IF ls_return-type = 'E'.
- MESSAGE ID ls_return-id
- TYPE ls_return-type
- NUMBER ls_return-number
- WITH ls_return-message_v1
- ls_return-message_v2
- ls_return-message_v3
- ls_return-message_v4.
- ENDIF.
- ENDIF.
- ENDFORM.

既然BAPI_PRODORD_CREATE这个官方主推的BAPI功能这么少,那么想改组件,工序之类的内容有什么办法吗?
答案是,有!但不是SAP官方推荐的方法。
使用SMOD事务码可以查到一个PP的生产订单保存前增强-PPCO0007,这是一个客户出口增强,其中只有一个组件函数EXIT_SAPLCOZV_001。里面只能看到表头数据CAUFVD。SAP本意是让你在里面做定制校验抛异常的,不是让你改东西。
如果非要改组件之类的行不行?用上文那个方法,字段符号直接匹配主程序内存中的更新数据库表变量(SAPLCOBC)RESB_BT[],以及其他工序,文本表之类。
这么做不但SAP不推荐,我也不推荐,可能出一些奇怪的问题。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。