文章目录
- [SAP 采购订单收货函数实战:基于 BAPI_GOODS_MVT_CREATE 的 AI 驱动开发](#SAP 采购订单收货函数实战:基于 BAPI_GOODS_MVT_CREATE 的 AI 驱动开发)
-
- 一、需求背景--(测试AI功能使用-没有指定具体的业务要求)
- 二、技术栈与环境
- 三、过程实现(核心踩坑与解决)
-
- [1. 第一步:确认对象与类型是否存在](#1. 第一步:确认对象与类型是否存在)
- [2. 使用 ADT 创建函数模块](#2. 使用 ADT 创建函数模块)
- [3. 遇到的"规则雷区"(关键经验)](#3. 遇到的"规则雷区"(关键经验))
- [4. 标准化的成功判断与提交](#4. 标准化的成功判断与提交)
- 四、最终成果
- 五、结尾
SAP 采购订单收货函数实战:基于 BAPI_GOODS_MVT_CREATE 的 AI 驱动开发
本文记录一次使用 AI 编程助手,在 SAP S/4HANA 系统中从零开发采购收货自定义函数的完整过程,涵盖技术选型、开发环境、踩坑过程、最终成果,希望对 SAP 开发 + AI 提效场景有参考价值。
一、需求背景--(测试AI功能使用-没有指定具体的业务要求)

业务需要封装一个可供其他程序复用的采购订单收货 函数模块,底层调用 SAP 标准 BAPI ------ BAPI_GOODS_MVT_CREATE(移动类型默认 101)。
要求:
- 放置在现有的 PO 函数组
ZFG_COPY_PO下; - 参数风格统一(
IV_/EV_前缀); - 业务逻辑完善:必填校验、默认日期/移动类型、成功判断、
RETURN表回传、测试运行控制; - 提交方式标准化:使用 BAPI 事务函数提交/回滚,而非裸
COMMIT WORK。
二、技术栈与环境
开发过程不是"闷头写代码",而是基于 MCP(Model Context Protocol) 打通 AI 与 SAP 的三层通道:
| 通道 | 用途 | 写权限 |
|---|---|---|
| (ABAP Development Tools) | 创建/读取/编辑源代码、激活对象 | ✅ 可写 |
| (Remote Function Call) | 只读查询(查表结构等) | ❌ 只读 |
| OData | 系统登录与会话管理 | 中间层 |
开发对象:自定义函数 Z_MM_GR_PO_GOODSRCPT
- 函数组:
ZFG_COPY_PO - 包:
$TMP - 关键依赖类型:
BAPI2017_GM_HEAD_01抬头BAPI2017_GM_CODE代码BAPI2017_GM_ITEM_CREATE项目BAPIRET2返回消息
三、过程实现(核心踩坑与解决)
1. 第一步:确认对象与类型是否存在
按"字段名先查再写、禁止猜"的原则,用 RFC 读取数据字典 DD03L 确认所有 BAPI 结构真实存在:
BAPIRET2结构存在 ------ 返回消息表行类型;BAPI2017_GM_ITEM_CREATE结构存在 ------ 项目字段(PO_NUMBER/PO_ITEM/MOVE_TYPE/PLANT/STGE_LOC/ENTRY_QNT)。
教训:类型名拼写错误是激活失败的主要来源之一。
2. 使用 ADT 创建函数模块
使用 ADT 的源码写入能力,一次性提交完整的 FUNCTION ... ENDFUNCTION. 块,完成对象创建与激活。
3. 遇到的"规则雷区"(关键经验)
在多次激活失败后,总结出该套件下的三大避坑点:
-
禁止
DATA:冒号批量声明 :ADT 归一化解析器会把DATA: a TYPE x, b TYPE y.误判为函数参数接口,从而生成一堆错误的 EXPORTING 参数。必须一行一条DATA x TYPE y.。 -
TABLES 参数类型写法 :需要一个
ET_RETURN TYPE BAPIRET2(直接给行类型 ),这一行写STANDARD TABLE OF会被解析器拆错。用行类型由系统自动转内表。 -
Type 名称必须以数据字典为准 :曾把
BAPIRET2误写成BAPIRETAP/bapiretr等,触发Type unknown。
4. 标准化的成功判断与提交
abap
" 调用标准 BAPI 执行货物移动
CALL FUNCTION 'BAPI_GOODS_MVT_CREATE'
EXPORTING goodsmvt_header = ls_header
goodsmvt_code = ls_code
testrun = iv_testrun
TABLES goodsmvt_item = lt_item
return = lt_ret.
" 回传 BAPI 返回消息
et_return[] = lt_ret[].
" 检查是否存在错误/中止类型返回
LOOP AT lt_ret INTO ls_ret WHERE type CA 'EAX'.
lv_error = 'X'.
EXIT.
ENDLOOP.
" 存在错误则标准回滚
IF lv_error = 'X'.
IF iv_testrun IS INITIAL.
CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
ENDIF.
ev_success = ''.
RETURN.
ENDIF.
" 处理成功:标准提交(测试运行不提交)
ev_success = 'X'.
IF iv_testrun IS INITIAL.
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
EXPORTING wait = 'X'.
ENDIF.
要点:
- 成功判断:遍历
RETURN表的type,含 E/A/X 视为失败; - 提交:统一走
BAPI_TRANSACTION_COMMIT(WAIT='X'); - 回滚:出错走
BAPI_TRANSACTION_ROLLBACK,不再直接用 `COMMIT WORK》; - 测试运行
IV_TESTRUN时不提交,只做验证。
四、最终成果
生成对象
plaintext
对象名称 : Z_MM_GR_PO_GOODSRCPT
所属函数组: ZFG_COPY_PO
所属包 : $TMP
激活状态 : ✓ 成功激活(activated = true, 无错误)
参数数量 : 14
接口清单
| 方向 | 参数 | 类型 | 说明 |
|---|---|---|---|
| IMPORTING | IV_PO_NUMBER | EBELN | 采购订单号(必输) |
| IV_PO_ITEM | EBELP | 采购订单行项目(必输) | |
| IV_QTY | MENGE_D | 收货数量(必输) | |
| IV_UOM | MEINS | 收货单位 | |
| IV_PLANT | WERKS_D | 工厂 | |
| IV_STGE_LOC | LGORT_D | 存储地点 | |
| IV_MOVE_TYPE | BWART | 移动类型(默认 101) | |
| IV_PSTNG_DATE | BUDAT | 过账日期(默认当天) | |
| IV_HEADER_TXT | BKTXT | 抬头文本 | |
| IV_TESTRUN | XFELD | 测试运行 | |
| EXPORT | EV_SUCCESS | XFELD | 是否成功 |
| EV_MAT_DOC | MBLNR | 物料凭证号 | |
| EV_MAT_YEAR | MJAHR | 物料凭证年度 | |
| TABLES | ET_RETURN | BAPIRET2 | BAPI 返回消息 |
开发亮点
- 多通道协作:ADT 写、RFC 查、OData 登录,三层通道各司其职,全程无人为手工录入;
- 先查后写:所有字段/类型名先经 DDIC 核实,杜绝盲猜;
- 标准化提交:BAPI 事务函数 + 测试运行处理,安全合规;
- 注释与格式:程序头文档、逐段中文注释、变量前缀规范。
五、结尾
这次开发说明了一个判断:即使是一个小功能 BAPI 封装函数,背后也涉及类型名精确性、参数写法兼容、事务提交规范 等细节。而借助统一的 MCP 通道,AI 可以完成「分析 → 查询 → 编码 → 激活 → 校验」的闭环,大幅提升开发效率。

如果你也想用类似方式,提升 SAP 常规功能的开发体验,建议先从「小函数封装 + 类型字典查询」这类低风险场景入手。
最后附上代码
ABAP
function z_mm_gr_po_goodsrcpt.
*"----------------------------------------------------------------------
*"*"本地接口:
*" IMPORTING
*" VALUE(IV_PO_NUMBER) TYPE EBELN
*" VALUE(IV_PO_ITEM) TYPE EBELP
*" VALUE(IV_QTY) TYPE MENGE_D
*" VALUE(IV_UOM) TYPE MEINS OPTIONAL
*" VALUE(IV_PLANT) TYPE WERKS_D OPTIONAL
*" VALUE(IV_STGE_LOC) TYPE LGORT_D OPTIONAL
*" VALUE(IV_MOVE_TYPE) TYPE BWART OPTIONAL
*" VALUE(IV_PSTNG_DATE) TYPE BUDAT OPTIONAL
*" VALUE(IV_HEADER_TXT) TYPE BKTXT OPTIONAL
*" VALUE(IV_TESTRUN) TYPE XFELD OPTIONAL
*" EXPORTING
*" VALUE(EV_SUCCESS) TYPE XFELD
*" VALUE(EV_MAT_DOC) TYPE MBLNR
*" VALUE(EV_MAT_YEAR) TYPE MJAHR
*" TABLES
*" ET_RETURN STRUCTURE BAPIRET2
*"----------------------------------------------------------------------
.
DATA ls_header TYPE bapi2017_gm_head_01. " 货物移动抬头
DATA ls_code TYPE bapi2017_gm_code. " 货物移动代码
DATA lt_item TYPE TABLE OF bapi2017_gm_item_create. " 货物移动项目表
DATA ls_item LIKE LINE OF lt_item. " 货物移动项目行
DATA lt_ret TYPE TABLE OF bapiret2. " BAPI返回消息表
DATA ls_ret LIKE LINE OF lt_ret. " BAPI返回消息行
DATA lv_move TYPE bwart. " 移动类型
DATA lv_error TYPE flag. " 错误标记
CLEAR: ev_success, ev_mat_doc, ev_mat_year, et_return.
" 必填参数校验
IF iv_po_number IS INITIAL OR iv_po_item IS INITIAL OR iv_qty IS INITIAL.
RETURN.
ENDIF.
" 设置货物移动代码(01=收货)
ls_code-gm_code = '01'.
" 设置抬头过账日期(默认当前日期)
ls_header-pstng_date = sy-datum.
IF iv_pstng_date IS NOT INITIAL.
ls_header-pstng_date = iv_pstng_date.
ENDIF.
ls_header-header_txt = iv_header_txt.
" 移动类型默认101(采购订单收货)
lv_move = iv_move_type.
IF lv_move IS INITIAL.
lv_move = '101'.
ENDIF.
ls_item-move_type = lv_move.
" 组装BAPI项目字段
ls_item-po_number = iv_po_number.
ls_item-po_item = iv_po_item.
ls_item-plant = iv_plant.
ls_item-stge_loc = iv_stge_loc.
ls_item-entry_qnt = iv_qty.
ls_item-entry_uom = iv_uom.
APPEND ls_item TO lt_item.
" 调用标准BAPI执行货物移动
CALL FUNCTION 'BAPI_GOODS_MVT_CREATE'
EXPORTING goodsmvt_header = ls_header
goodsmvt_code = ls_code
testrun = iv_testrun
TABLES goodsmvt_item = lt_item
return = lt_ret.
" 回传BAPI返回消息
et_return[] = lt_ret[].
" 检查是否存在错误/中止类型返回
LOOP AT lt_ret INTO ls_ret WHERE type CA 'EAX'.
lv_error = 'X'.
EXIT.
ENDLOOP.
" 存在错误则标准回滚
IF lv_error = 'X'.
IF iv_testrun IS INITIAL.
CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
ENDIF.
ev_success = ''.
RETURN.
ENDIF.
" 处理成功:标准提交(测试运行不提交)
ev_success = 'X'.
IF iv_testrun IS INITIAL.
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
EXPORTING wait = 'X'.
ENDIF.
endfunction.