Skip to content

物料(RAP)

RAP框架下,各种事务操作都应该在Behavior Definition里,因此需要注意,不能有Commit或Rollback操作。

经研究,以下两种适用于RAP框架下的物料操作

仅作展示用,下面代码只维护基础视图和物料描述

本地调用公共接口

使用 /iwbep/cl_cp_client_proxy_fact=>create_v2_local_proxy 创建本地代理对象,模拟公共接口调用。

目前来说,这种方式仅支持V2版本的公共接口,V4版本由于会触发Commit/Rollback,无法使用

示例代码
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
*&---------------------------------------------------------------------*
*& 模拟公共接口(A2X/V2)调用
*&---------------------------------------------------------------------*
    " 演示用,假设这是程序的入参和出参
    TYPES:
      BEGIN OF ty_datalake,
        matnr TYPE mara-matnr,
        maktx TYPE makt-maktx,
      END OF ty_datalake.
    TYPES tt_datalake TYPE STANDARD TABLE OF ty_datalake.

    " 定义参数格式
    TYPES:
      BEGIN OF ty_entity.
        INCLUDE TYPE a_product.
    TYPES:
        to_description TYPE STANDARD TABLE OF a_productdescription WITH EMPTY KEY,
      END OF ty_entity.
    " 暂时没找到批量处理的方法,不过为了保持和V4格式一致,这里还是保留批处理结构
    TYPES:
      BEGIN OF ty_batch,
        data_ref      TYPE REF TO data,
        request_body  TYPE ty_entity,
        request       TYPE REF TO /iwbep/if_cp_request_create,
        response_body TYPE ty_entity,
        response      TYPE REF TO /iwbep/if_cp_response_create,
      END OF ty_batch.

    DATA:
      lt_batch TYPE STANDARD TABLE OF ty_batch WITH EMPTY KEY,
      ls_batch TYPE ty_batch,
      lv_mtype TYPE bapi_mtype,
      lv_msg   TYPE bapi_msg.

    DATA:
      lo_client_proxy             TYPE REF TO /iwbep/if_cp_client_proxy,
      lo_create_request_changeset TYPE REF TO /iwbep/if_cp_request_changeset,
      lo_create_request           TYPE REF TO /iwbep/if_cp_request_create,
      lo_create_response          TYPE REF TO /iwbep/if_cp_response_create,
      lo_data_desc_node_root      TYPE REF TO /iwbep/if_cp_data_desc_node,
      lo_data_desc_node_child     TYPE REF TO /iwbep/if_cp_data_desc_node,
      lo_entity_list_resource     TYPE REF TO /iwbep/if_cp_resource_list.

    TRY.
        " OData代理
        " ID和Version可以从S4HANA_OP_API包中查找
        DATA: ls_service_key TYPE /iwbep/if_cp_client_proxy=>ty_s_service_key_v2.
        ls_service_key = VALUE #( service_id      = 'API_PRODUCT_SRV'
                                  service_version = '0001' ).
        lo_client_proxy = /iwbep/cl_cp_client_proxy_fact=>create_v2_local_proxy( is_service_key     = ls_service_key
                                                                                 iv_do_write_traces = abap_true ).

        " 创建资源对象
        lo_entity_list_resource = lo_client_proxy->create_resource_for_entity_set( 'A_Product' ).

        LOOP AT ct_datalake REFERENCE INTO DATA(lr_datalake).
          " 还要回写消息,统一记录起来
          CLEAR ls_batch.
          ls_batch-data_ref = lr_datalake.
          INSERT ls_batch INTO TABLE lt_batch REFERENCE INTO DATA(lr_batch).

          " 基础视图
          lr_batch->request_body-producttype = 'FERT'.
          lr_batch->request_body-industrysector = 'M'.
          lr_batch->request_body-baseunit = 'EA'.
          " 物料描述
          lr_batch->request_body-to_description = VALUE #( ( language = '1' productdescription = lr_datalake->productname ) ).

          "
          lr_batch->request =
          lo_create_request = lo_entity_list_resource->create_request_for_create( ).
          " 节点描述
          lo_data_desc_node_root = lo_create_request->create_data_descripton_node( ).
          lo_data_desc_node_child = lo_data_desc_node_root->add_child( 'TO_DESCRIPTION' ).

          lo_create_request->set_deep_business_data( is_business_data    = lr_batch->request_body
                                                     io_data_description = lo_data_desc_node_root ).
          lo_create_request->execute( ).
        ENDLOOP.

        " 回写执行消息
        LOOP AT lt_batch REFERENCE INTO lr_batch.
          TRY.
              lo_create_request = lr_batch->request->check_execution( ).
              IF lo_create_request IS BOUND.
                lo_create_response = lo_create_request->get_response(  ).
                lo_create_response->get_business_data( IMPORTING es_business_data = lr_batch->response_body ).
              ENDIF.
            CATCH /iwbep/cx_cp_remote INTO DATA(lx_cp_remote).
              " HTTP Status Code - {@link /IWBEP/CX_CP_REMOTE.data:HTTP_STATUS_CODE}
              " HTTP Status Reason Message - {@link /IWBEP/CX_CP_REMOTE.data:HTTP_STATUS_MESSAGE}
              " OData Error Details - {@link /IWBEP/CX_CP_REMOTE.data:S_ODATA_ERROR}
              " 还要判断HTTP状态等,挺麻烦的,先简单弄下
              lv_mtype = 'E'.
              lv_msg = lx_cp_remote->s_odata_error-message.
          ENDTRY.

          IF lr_batch->response_body-product IS NOT INITIAL.
            lr_datalake ?= lr_batch->data_ref.
            lr_datalake->product = lr_batch->response_body-product.
            lr_datalake->mtype = 'S'.
          ELSE.
            lr_datalake ?= lr_batch->data_ref.
            lr_datalake->mtype = lv_mtype.
            lr_datalake->msg = lv_msg.
          ENDIF.
        ENDLOOP.

      CATCH /iwbep/cx_gateway INTO DATA(lx_gateway).
        lv_msg = lx_gateway->get_text( ).
        _write_error( EXPORTING iv_msg = lv_msg CHANGING ct_datalake = ct_datalake ).
        RETURN.
    ENDTRY.

内部API

内部API基本上找不到资料,都是看代码找到的。上面公共接口本质上也是调用了这些API,所以也一并写出来

示例代码
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
    " 演示用,假设这是程序的入参和出参
    TYPES:
      BEGIN OF ty_datalake,
        matnr TYPE mara-matnr,
        maktx TYPE makt-maktx,
      END OF ty_datalake.
    TYPES tt_datalake TYPE STANDARD TABLE OF ty_datalake.

*&---------------------------------------------------------------------*
*& 使用内部API(cl_cmd_prod_data_api)创建物料,代码类似以前的BAPI
*&---------------------------------------------------------------------*
    DATA: lt_unified_api_data TYPE cmd_prd_t_unified_prod_data,
          ls_unified_api_data TYPE cmd_prd_s_unified_prod_data,
          lt_prodwise_output  TYPE cl_cmd_prod_data_api=>if_cmd_product_maint_api~tt_pmd_output,
          lv_has_error        TYPE boole_d VALUE abap_false,
          lv_product          TYPE productnumber,
          lv_mtype            TYPE bapi_mtype,
          lv_msg              TYPE bapi_msg.

*&---------------------------------------------------------------------*
*& 数据填充
*&---------------------------------------------------------------------*
    LOOP AT ct_datalake REFERENCE INTO DATA(lr_datalake).
      CLEAR ls_unified_api_data.

      " 对于非外部给号物料,需设置S_TEMP_NUM=X
      IF lr_datalake->product IS INITIAL.
        lr_datalake->product = |%{ sy-tabix }|.
        ls_unified_api_data-s_temp_num = abap_true.
      ENDIF.

*&---------------------------------------------------------------------*
*& 物料基础视图
*&---------------------------------------------------------------------*
      ls_unified_api_data-product-product = lr_datalake->product.
      ls_unified_api_data-product-productdescription = lr_datalake->productname.
      ls_unified_api_data-product-industrysector = 'M'.
      ls_unified_api_data-product-producttype = 'FERT'.
      ls_unified_api_data-product-baseunit = '11'. " 因为不想在测试中创建物料,这里随便给个错误值

*&---------------------------------------------------------------------*
*& 物料描述
*&---------------------------------------------------------------------*
      DATA ls_prd_descr LIKE LINE OF ls_unified_api_data-prd_descr.
      CLEAR ls_prd_descr.
      ls_prd_descr-product = lr_datalake->product.
      ls_prd_descr-language = '1'.
      ls_prd_descr-productdescription = lr_datalake->productname.
      INSERT ls_prd_descr INTO TABLE ls_unified_api_data-prd_descr.

      INSERT ls_unified_api_data INTO TABLE lt_unified_api_data.
    ENDLOOP.

*&---------------------------------------------------------------------*
*& 复制标准代码,价格优先根据科目价格
*&---------------------------------------------------------------------*
    LOOP AT lt_unified_api_data ASSIGNING FIELD-SYMBOL(<ls_unified_api_data>).
      LOOP AT <ls_unified_api_data>-ml_prices ASSIGNING FIELD-SYMBOL(<ls_ml_prices>).
        TRY .
            DATA(lr_ml_account) = REF #( <ls_unified_api_data>-ml_account[ product = <ls_ml_prices>-product
                                                                           valuationarea = <ls_ml_prices>-valuationarea
                                                                           valuationtype = <ls_ml_prices>-valuationtype
                                                                           currencyrole = <ls_ml_prices>-currencyrole ] ).
            <ls_ml_prices>-priceunitqty = lr_ml_account->priceunitqty.
          CATCH cx_sy_itab_line_not_found.
        ENDTRY.
      ENDLOOP.
    ENDLOOP.

*&---------------------------------------------------------------------*
*& API调用
*&---------------------------------------------------------------------*
    cl_cmd_prod_data_api=>get_instance(
      IMPORTING
        eo_cmd_prod_data_api = DATA(lo_unified_prod_data_api)
    ).

    TRY .
        lo_unified_prod_data_api->if_cmd_product_maint_api~check(
          IMPORTING
            et_prodwise_output = lt_prodwise_output
          CHANGING
            ct_data            = lt_unified_api_data
        ).
      CATCH cx_cmd_product_maint_api INTO DATA(lx_cmd_product_maint_api).
        lv_msg = lx_cmd_product_maint_api->if_message~get_text( ).
        _write_error( EXPORTING iv_msg = lv_msg CHANGING ct_datalake = ct_datalake ).
        RETURN.
    ENDTRY.

    LOOP AT lt_prodwise_output ASSIGNING FIELD-SYMBOL(<ls_prodwise_output>).
      IF <ls_prodwise_output>-enqueue_failed = abap_true OR <ls_prodwise_output>-update_failed = abap_true.
        lv_has_error = abap_true.
        READ TABLE ct_datalake REFERENCE INTO lr_datalake WITH KEY product = <ls_prodwise_output>-product.
        IF sy-subrc = 0.
          LOOP AT <ls_prodwise_output>-t_message ASSIGNING FIELD-SYMBOL(<ls_message>) WHERE msgty CA 'EAX'.
            MESSAGE ID <ls_message>-msgid TYPE <ls_message>-msgty NUMBER <ls_message>-msgno
            WITH <ls_message>-msgv1 <ls_message>-msgv2 <ls_message>-msgv3 <ls_message>-msgv4
            INTO lv_msg.
            lr_datalake->mtype = 'E'.
            lr_datalake->msg = |{ lr_datalake->msg }{ lv_msg };|.
          ENDLOOP.
        ENDIF.
      ELSEIF <ls_prodwise_output>-is_new_number = abap_true.
        READ TABLE ct_datalake REFERENCE INTO lr_datalake WITH KEY product = <ls_prodwise_output>-product_temp.
        IF sy-subrc = 0.
          lr_datalake->product = <ls_prodwise_output>-product.
          lr_datalake->mtype = 'S'.
          lr_datalake->msg = |物料{ <ls_prodwise_output>-product }创建成功|.
        ENDIF.
      ENDIF.
    ENDLOOP.

    IF lv_has_error = abap_false.
      TRY .
          lo_unified_prod_data_api->if_cmd_product_maint_api~save( ).
        CATCH cx_cmd_product_maint_api INTO DATA(lx_cmd_product_maint_api_save).
          lv_msg = lx_cmd_product_maint_api_save->if_message~get_text( ).
          _write_error( EXPORTING iv_msg = lv_msg CHANGING ct_datalake = ct_datalake ).
      ENDTRY.
    ENDIF.

测试

RAP开发通常在Eclipse上进行,因此测试也应使用类进行测试,通过基础接口if_oo_adt_classrun,类可直接运行。

另外为了模拟用户操作,使用EML方式触发Behavior:

测试用例
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
CLASS ycl_clean_core_demo IMPLEMENTATION.

  METHOD if_oo_adt_classrun~main.

    " 模拟界面点击按钮创建物料
    MODIFY ENTITIES OF zr_ytproduct
        ENTITY datalake
        EXECUTE CreateErpProduct
        FROM VALUE #( ( uuid = '8F0EA20832B71FE08CAE1FF329BDD452' ) ).

    " 执行
    COMMIT ENTITIES
        RESPONSE OF zr_ytproduct
        FAILED DATA(commit_failed)
        REPORTED DATA(commit_reported).
    IF commit_failed IS NOT INITIAL.
      out->write( commit_failed ).
      RETURN.
    ENDIF.

    " 读取处理结果
    READ ENTITY zr_ytproduct
        ALL FIELDS WITH VALUE #( ( %key-uuid = '8F0EA20832B71FE08CAE1FF329BDD452' ) )
        RESULT FINAL(result).

    " 打印结果到控制台
    out->write( result ).

  ENDMETHOD.

ENDCLASS.

参考:RAP开发流程示例