SAP PP 生产订单创建添加定制组件的ABAP实现方法

背景

此文适合对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来添加定制组件和加工数量(负数)。

方案细节

  1. 先调用BAPI_PRODORD_CREATE创建生产订单,这一步会自动生成加工组件,不能定制。

这一步会根据表头的物料,工厂,订单类型,数量,计划起始/截止日期生成生产订单,同时会根据订单物料对应的生产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不推荐,我也不推荐,可能出一些奇怪的问题。

  • 8
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是 SAP 采购订单过账的 ABAP 代码实现: 1. 首先需要定义一些变量,并且获取采购订单的相关信息。 ``` DATA: ls_header TYPE bapi_po_header, lt_poitem TYPE TABLE OF bapi_po_item, ls_poitem TYPE bapi_po_item, lt_account TYPE TABLE OF bapi_po_account, ls_account TYPE bapi_po_account. ls_header-po_number = '采购订单号'. ls_header-doc_date = sy-datum. ls_header-posting_date = sy-datum. ls_header-username = sy-uname. CALL FUNCTION 'BAPI_PO_GETDETAIL' EXPORTING purchaseorder = ls_header-po_number IMPORTING po_header = ls_header po_items = lt_poitem. LOOP AT lt_poitem INTO ls_poitem. ls_poitem-po_number = ls_header-po_number. ls_poitem-doc_date = sy-datum. ls_poitem-posting_date = sy-datum. CALL FUNCTION 'BAPI_PO_GETACCOUNT' EXPORTING purchaseorder = ls_header-po_number po_item = ls_poitem-po_item TABLES po_account = lt_account. LOOP AT lt_account INTO ls_account. ls_account-po_number = ls_header-po_number. ls_account-po_item = ls_poitem-po_item. ls_account-doc_date = sy-datum. ls_account-posting_date = sy-datum. ENDLOOP. ENDLOOP. ``` 2. 之后需要调用 `BAPI_PO_CHANGE` 函数进行采购订单的过账。 ``` CALL FUNCTION 'BAPI_PO_CHANGE' EXPORTING purchaseorder = ls_header-po_number headerdata = ls_header TABLES return = lt_return poitem = lt_poitem poaccount = lt_account. ``` 3. 最后需要判断过账是否成功。 ``` IF lt_return[] IS INITIAL. COMMIT WORK. WRITE: / '采购订单 ', ls_header-po_number, ' 过账成功。'. ELSE. ROLLBACK WORK. WRITE: / '采购订单 ', ls_header-po_number, ' 过账失败。'. ENDIF. ``` 以上就是 SAP 采购订单过账的 ABAP 代码实现。需要注意的是,实现过程中需要根据实际情况进行相应的修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ChampaignWolf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值