BAdI 上下文数据获取与交互:标准数据读取与修改逻辑实现
博客简介:讲解 BAdI 接口参数的使用规则,覆盖标准程序传入的业务对象数据读取、返回参数修改影响标准流程的实现方法,梳理 BAdI 中内存参数导入、全局对象引用、增强间数据共享的操作技巧,结合"销售订单创建时修改行项目定价日期"场景给出实操代码,解决 BAdI 开发中标准数据交互的核心痛点。
"BAdI 开发中最头疼的问题是什么?不是找不到 BAdI,也不是不会写实现------而是'数据在哪'。BAdI 的参数就那么几个,拿不到我想要的数据怎么办?修改了参数为什么没生效?本篇把 BAdI 的五种数据获取方式和修改规则一次讲透。"
本篇讲解 BAdI 接口参数的使用规则,覆盖标准程序传入的业务对象数据读取、返回参数修改影响标准流程的实现方法,梳理 BAdI 中内存参数导入、全局对象引用、增强间数据共享的操作技巧,结合"销售订单创建时修改行项目定价日期"场景给出实操代码。
📖 写在前面
本篇定位
这是一篇数据交互深入篇。BAdI 系列能力进阶阶段的第二篇,重点解决"数据在哪"和"怎么改"两个核心问题。如果你曾被 BAdI 的参数限制困扰过------想拿的数据拿不到、改了的参数不生效------本篇就是为你准备的。
本篇适合谁读
- BAdI 开发中遇到"参数不够用"的开发者
- 需要修改标准数据但不确定修改方式的开发者
- 想了解 BAdI 间数据共享机制的开发者
五种数据获取方式速览
| 方式 | 可靠度 | 复杂度 | 适用场景 |
|---|---|---|---|
| 方式一:接口方法参数 | ★★★★★ | 低 | 参数直接包含所需数据 |
| 方式二:对象方法调用 | ★★★★☆ | 中 | 通过对象接口获取更多数据 |
| 方式三:数据库查询 | ★★★☆☆ | 中 | 获取扩展数据、历史数据 |
| 方式四:ABAP 内存共享 | ★★★☆☆ | 中 | 跨 BAdI 实现传递数据 |
| 方式五:全局变量引用 | ★★☆☆☆ | 高 | 获取标准程序的运行时数据 |
#mermaid-svg-6cUF8wlYibzZ8jFs{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6cUF8wlYibzZ8jFs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6cUF8wlYibzZ8jFs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6cUF8wlYibzZ8jFs .error-icon{fill:#552222;}#mermaid-svg-6cUF8wlYibzZ8jFs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6cUF8wlYibzZ8jFs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6cUF8wlYibzZ8jFs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6cUF8wlYibzZ8jFs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6cUF8wlYibzZ8jFs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6cUF8wlYibzZ8jFs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6cUF8wlYibzZ8jFs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6cUF8wlYibzZ8jFs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6cUF8wlYibzZ8jFs .marker.cross{stroke:#333333;}#mermaid-svg-6cUF8wlYibzZ8jFs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6cUF8wlYibzZ8jFs p{margin:0;}#mermaid-svg-6cUF8wlYibzZ8jFs .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6cUF8wlYibzZ8jFs .cluster-label text{fill:#333;}#mermaid-svg-6cUF8wlYibzZ8jFs .cluster-label span{color:#333;}#mermaid-svg-6cUF8wlYibzZ8jFs .cluster-label span p{background-color:transparent;}#mermaid-svg-6cUF8wlYibzZ8jFs .label text,#mermaid-svg-6cUF8wlYibzZ8jFs span{fill:#333;color:#333;}#mermaid-svg-6cUF8wlYibzZ8jFs .node rect,#mermaid-svg-6cUF8wlYibzZ8jFs .node circle,#mermaid-svg-6cUF8wlYibzZ8jFs .node ellipse,#mermaid-svg-6cUF8wlYibzZ8jFs .node polygon,#mermaid-svg-6cUF8wlYibzZ8jFs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6cUF8wlYibzZ8jFs .rough-node .label text,#mermaid-svg-6cUF8wlYibzZ8jFs .node .label text,#mermaid-svg-6cUF8wlYibzZ8jFs .image-shape .label,#mermaid-svg-6cUF8wlYibzZ8jFs .icon-shape .label{text-anchor:middle;}#mermaid-svg-6cUF8wlYibzZ8jFs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6cUF8wlYibzZ8jFs .rough-node .label,#mermaid-svg-6cUF8wlYibzZ8jFs .node .label,#mermaid-svg-6cUF8wlYibzZ8jFs .image-shape .label,#mermaid-svg-6cUF8wlYibzZ8jFs .icon-shape .label{text-align:center;}#mermaid-svg-6cUF8wlYibzZ8jFs .node.clickable{cursor:pointer;}#mermaid-svg-6cUF8wlYibzZ8jFs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6cUF8wlYibzZ8jFs .arrowheadPath{fill:#333333;}#mermaid-svg-6cUF8wlYibzZ8jFs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6cUF8wlYibzZ8jFs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6cUF8wlYibzZ8jFs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6cUF8wlYibzZ8jFs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6cUF8wlYibzZ8jFs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6cUF8wlYibzZ8jFs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6cUF8wlYibzZ8jFs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6cUF8wlYibzZ8jFs .cluster text{fill:#333;}#mermaid-svg-6cUF8wlYibzZ8jFs .cluster span{color:#333;}#mermaid-svg-6cUF8wlYibzZ8jFs div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6cUF8wlYibzZ8jFs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6cUF8wlYibzZ8jFs rect.text{fill:none;stroke-width:0;}#mermaid-svg-6cUF8wlYibzZ8jFs .icon-shape,#mermaid-svg-6cUF8wlYibzZ8jFs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6cUF8wlYibzZ8jFs .icon-shape p,#mermaid-svg-6cUF8wlYibzZ8jFs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6cUF8wlYibzZ8jFs .icon-shape .label rect,#mermaid-svg-6cUF8wlYibzZ8jFs .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6cUF8wlYibzZ8jFs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6cUF8wlYibzZ8jFs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6cUF8wlYibzZ8jFs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
是
否
是
否
需要获取数据
参数中有?
方式一:接口参数
最优先
对象方法能获取?
方式二:对象方法调用
数据库中有?
方式三:数据库查询
其他 BAdI 已计算?
方式四:ABAP 内存共享
方式五:全局变量引用
最后手段
一、方式一:接口方法参数
1.1 三种参数类型详解
BAdI 接口方法的参数是最直接、最可靠的数据来源。每种参数类型有不同的使用规则和修改限制。
| 参数类型 | 方向 | 可否读取 | 可否修改 | 修改是否影响标准流程 |
|---|---|---|---|---|
| IMPORTING | 标准 → BAdI | 是 | 否 | --- |
| EXPORTING | BAdI → 标准 | 否 | 是 | 是 |
| CHANGING | 双向 | 是 | 是 | 是 |
1.2 IMPORTING 参数:只读数据
abap
" IMPORTING 参数:只能读取,不能修改
" 如果尝试修改,编译器会报错
METHOD if_ex_badi_demo~process.
" 正确的用法:读取 IMPORTING 参数
DATA: lv_doc_type TYPE bstyp.
lv_doc_type = im_header->get_doc_type( ). " ← 读取
" 错误的用法:修改 IMPORTING 参数
" im_header = lo_new_header. " ← 编译错误!IMPORTING 不可修改
ENDMETHOD.
1.3 CHANGING 参数:可读可写
abap
" CHANGING 参数:可以读取,也可以修改
" 修改后会影响标准程序的后续流程
METHOD if_ex_badi_po~process_header.
" 读取 CHANGING 参数
DATA: lv_ekgrp TYPE ekgrp.
lv_ekgrp = ch_header-ekgrp. " ← 读取采购组
" 修改 CHANGING 参数
IF lv_ekgrp IS INITIAL.
ch_header-ekgrp = '001'. " ← 自动填充默认采购组
" 注意:修改后,标准程序会使用新的值
ENDIF.
ENDMETHOD.
1.4 EXPORTING 参数:返回结果
abap
" EXPORTING 参数:BAdI 向标准程序返回数据
" 标准程序根据返回值决定后续行为
METHOD if_ex_badi_po~post.
" 设置 EXPORTING 参数
ex_result = 'X'. " ← 返回处理结果
" 标准程序根据 ex_result 决定后续行为
" ex_result = 'X' → 处理成功,继续执行
" ex_result = 'E' → 处理失败,可能需要回滚
ENDMETHOD.
1.5 参数修改影响范围
abap
" 参数修改的影响范围测试
" 场景:在 BAdI 中修改 CHANGING 参数
METHOD if_ex_badi_po~process_header.
" 修改前:ch_header-ekgrp = ''(空)
ch_header-ekgrp = '001'.
ENDMETHOD.
" BAdI 调用返回后,标准程序继续执行
" 此时:
" 标准程序中的 EKKO-EKGRP = '001' ← 修改生效了!
" 后续保存到数据库的也是 '001'
" 关键规则:
" 1. CHANGING 参数修改 → 影响标准流程
" 2. IMPORTING 参数修改 → 编译错误,不允许
" 3. EXPORTING 参数设置 → 标准程序读取后决定行为
二、方式二:对象方法调用
2.1 通过 IMPORTING 对象获取更多数据
BAdI 的 IMPORTING 参数经常是对象引用(TYPE REF TO),通过调用对象的方法可以获取比参数更多的数据。
abap
" 典型场景:IMPORTING 参数只有一个对象引用
" 但通过对象方法,可以获取丰富的数据
METHOD if_ex_badi_me_purchdoc_posted~post.
" im_document 是 IMPORTING 参数,类型是对象引用
" 通过对象方法获取数据:
" 方式1:获取抬头对象
DATA: lo_header TYPE REF TO if_purchase_order.
lo_header = im_document->get_header( ). " ← 获取抬头
" 方式2:从抬头对象获取基本字段
DATA: lv_ebeln TYPE ebeln,
lv_bukrs TYPE bukrs,
lv_ekorg TYPE ekorg.
lv_ebeln = lo_header->get_number( ). " ← 采购订单号
lv_bukrs = lo_header->get_company_code( ). " ← 公司代码
lv_ekorg = lo_header->get_purch_org( ). " ← 采购组织
" 方式3:获取行项目列表
DATA: lt_items TYPE mepoitem_tty.
lt_items = im_document->get_items( ). " ← 获取所有行项目
" 方式4:遍历行项目
LOOP AT lt_items INTO DATA(ls_item).
DATA: lv_ebelp TYPE ebelp.
lv_ebelp = ls_item-ebelp. " ← 行项目号
ENDLOOP.
ENDMETHOD.
2.2 对象方法链式调用
abap
" 对象方法可以链式调用,获取深层数据
METHOD if_ex_badi_sd~process_item.
" 链式调用:从文档 → 抬头 → 字段
DATA: lv_sales_org TYPE vkorg.
lv_sales_org = im_document->get_header( )->get_sales_org( ).
" 链式调用:从文档 → 行项目 → 字段
DATA: lv_material TYPE matnr.
lv_material = im_document->get_item( )->get_material( ).
" 链式调用:从文档 → 行项目 → 定价 → 条件记录
DATA: lt_conditions TYPE tt_conditions.
lt_conditions = im_document->get_item( )->get_pricing( )->get_conditions( ).
ENDMETHOD.
2.3 对象方法 vs 直接参数
| 对比维度 | 直接参数(CHANGING) | 对象方法调用 |
|---|---|---|
| 数据获取方式 | 直接访问结构字段 | 调用对象方法 |
| 数据类型 | 基本类型(CHAR/DATS 等) | 对象引用(TYPE REF TO) |
| 数据丰富度 | 有限(结构中的字段) | 丰富(可调用多个方法) |
| 可修改性 | 可直接修改结构字段 | 通过 SET 方法修改 |
| 代码复杂度 | 低 | 中 |
三、方式三:数据库查询
3.1 当参数不够用时------查数据库
BAdI 的接口参数是有限的,有些数据需要从数据库表中获取。
abap
" 场景:BAdI 中没有供应商名称,需要从 LFA1 表中查询
METHOD if_ex_badi_po~process_header.
" 从 CHANGING 参数获取供应商编码
DATA: lv_lifnr TYPE lifnr.
lv_lifnr = ch_header-lifnr.
" 从数据库查询供应商名称
SELECT SINGLE name1
FROM lfa1
INTO @DATA(lv_name1)
WHERE lifnr = @lv_lifnr.
IF sy-subrc = 0.
" 将供应商名称填入自定义字段
ch_header-zlifnr_name = lv_name1.
ENDIF.
ENDMETHOD.
3.2 数据库查询的注意事项
abap
" 事项1:避免在循环中查询
" 错误做法:
LOOP AT lt_items INTO ls_item.
SELECT SINGLE maktx
FROM makt
INTO ls_item-zmaktx
WHERE matnr = ls_item-matnr. " ← 每条记录一次查询,性能差
MODIFY lt_items FROM ls_item.
ENDLOOP.
" 正确做法:先收集所有物料号,批量查询
DATA: lt_matnr TYPE RANGE OF matnr.
LOOP AT lt_items INTO ls_item.
APPEND VALUE #( sign = 'I' option = 'EQ' low = ls_item-matnr ) TO lt_matnr.
ENDLOOP.
SELECT matnr, maktx
FROM makt
INTO TABLE @DATA(lt_makt)
WHERE matnr IN @lt_matnr
AND spras = @sy-langu. " ← 一次查询,性能好
" 事项2:注意数据库查询的性能影响
" BAdI 在标准流程中执行,查询时间直接影响用户操作体验
" 建议:能用参数获取的不要查数据库,必须查的用批量查询
3.3 数据库查询适用场景
| 场景 | 示例 | 是否推荐 |
|---|---|---|
| 获取扩展数据 | 供应商名称、物料描述 | 推荐 |
| 获取历史数据 | 上次采购价格、历史订单 | 推荐 |
| 获取配置数据 | 校验规则、阈值配置 | 推荐(但建议缓存) |
| 获取已传入的数据 | 重复查询 EKKO/EKPO | 不推荐(用参数即可) |
四、方式四:ABAP 内存共享
4.1 内存共享的使用场景
BAdI 的多个实现之间可以通过 ABAP 内存传递数据,实现跨增强的数据共享。
ABAP 内存共享的典型场景:
用户操作:ME21N → 输入数据 → 点击"保存"
时间线:
┌──────────────────────────────────────────────────────┐
│ 时刻1:BAdI 实现 A 执行(保存前校验) │
│ → 计算了订单总金额 │
│ → EXPORT p_amount = lv_amount TO MEMORY ID 'PO_TOTAL_AMOUNT' │
├──────────────────────────────────────────────────────┤
│ 时刻2:BAdI 实现 B 执行(保存后日志) │
│ → 需要记录订单总金额 │
│ → IMPORT p_amount = lv_amount FROM MEMORY ID 'PO_TOTAL_AMOUNT' │
│ → 不需要重新计算,直接使用 │
└──────────────────────────────────────────────────────┘
4.2 内存共享的代码实现(已修正语法)
abap
"=======================================================================
" BAdI 实现 A:ZIMPL_PO_CALC(保存前校验)
" 功能:计算订单总金额,存入 ABAP 内存
"=======================================================================
METHOD if_ex_me_process_po_cust~process_header.
" 计算订单总金额
DATA: lv_total_amount TYPE netwr VALUE 0.
LOOP AT ch_items INTO DATA(ls_item).
lv_total_amount = lv_total_amount + ls_item-netwr.
ENDLOOP.
" 将总额存入 ABAP 内存(供后续 BAdI 使用)
" 注意:EXPORT 语法应为 p_name = variable
EXPORT p_total_amount = lv_total_amount TO MEMORY ID 'PO_TOTAL_AMOUNT'.
" 校验逻辑
IF lv_total_amount > 100000.
MESSAGE w001(zmm_badi) WITH lv_total_amount.
ENDIF.
ENDMETHOD.
abap
"=======================================================================
" BAdI 实现 B:ZIMPL_PO_LOG(保存后日志)
" 功能:从 ABAP 内存读取总金额,记录日志
"=======================================================================
METHOD if_ex_badi_me_purchdoc_posted~post.
" 从 ABAP 内存读取总金额
DATA: lv_total_amount TYPE netwr.
" 注意:IMPORT 语法应为 p_name = variable
IMPORT p_total_amount = lv_total_amount FROM MEMORY ID 'PO_TOTAL_AMOUNT'.
IF sy-subrc = 0.
" 记录日志(包含总金额)
DATA: ls_log TYPE zt_po_action_log.
ls_log-ebeln = lo_header->get_number( ).
ls_log-amount = lv_total_amount. " ← 使用内存中的数据
" ... 写入日志
ELSE.
" 内存中没有数据,重新计算
" ... 备选逻辑
ENDIF.
" 清理内存(避免内存泄漏)
FREE MEMORY ID 'PO_TOTAL_AMOUNT'.
ENDMETHOD.
4.3 内存共享的注意事项
| 注意事项 | 说明 | 解决方案 |
|---|---|---|
| 内存泄漏 | 数据未清理,占用内存 | 使用后 FREE MEMORY |
| 内存 ID 冲突 | 多个 BAdI 使用相同 ID | 使用唯一 ID 命名规则 |
| 跨会话 | ABAP 内存仅在当前会话有效 | 需要跨会话用 SAP 内存 |
| 执行顺序依赖 | 依赖内存先后顺序 | 检查 sy-subrc,提供备选逻辑 |
五、方式五:全局变量引用(谨慎使用)
5.1 全局变量引用的风险
BAdI 中直接引用标准程序的全局变量是不推荐但有时的确需要的做法。这种方式风险高,需要谨慎使用。
abap
" 警告:全局变量引用风险高,仅作为最后手段
" 风险1:全局变量可能未被赋值
" 风险2:全局变量名可能因版本升级而改变
" 风险3:全局变量类型可能不同
" 风险4:不同 BAdI 调用时机,全局变量状态不同
" 如果一定要用,加以下保护:
METHOD if_ex_badi_custom~process.
" 尝试访问标准程序的全局变量
DATA: lv_vbeln TYPE vbeln.
" 方式1:ASSIGN 动态赋值(需要知道变量名)
FIELD-SYMBOLS: <fs_vbeln> TYPE vbeln.
ASSIGN ('(SAPMV45A)VBAK-VBELN') TO <fs_vbeln>.
IF sy-subrc = 0.
lv_vbeln = <fs_vbeln>.
ENDIF.
" 方式2:直接引用全局变量(更危险)
" lv_vbeln = vbak-vbeln. " ← 可能编译错误或 Dump
ENDMETHOD.
5.2 全局变量引用的替代方案
| 想获取的数据 | 不推荐(全局变量) | 推荐替代方案 |
|---|---|---|
| 销售订单号 | ASSIGN (VBAK-VBELN) | IMPORTING 参数获取 |
| 采购订单号 | ASSIGN (EKKO-EBELN) | CHANGING 参数获取 |
| 当前用户 | 全局变量 | SY-UNAME |
| 当前事务码 | 全局变量 | SY-TCODE |
| 配置参数 | 全局变量 | 数据库查询配置表 |
六、实战:销售订单创建时修改行项目定价日期
6.1 需求分析
业务场景:销售订单(VA01)创建时
需求:
1. 如果物料是促销品(物料组 = 'PROMO')
2. 自动将行项目的定价日期改为当前日期
3. 记录修改日志
6.2 BAdI 选择
目标 BAdI:BADI_SD_SALES_ITEM
→ SE18 查看:接口 IF_EX_BADI_SD_SALES_ITEM
→ 方法:ITEM_PROCESS
→ 参数:
├── IMPORTING:IM_ITEM(销售订单行项目对象)
└── CHANGING:CH_ITEM(行项目数据,可修改)
6.3 完整代码(已修正内存操作语法)
abap
"=======================================================================
" BAdI 实现:ZIMPL_SD_PRICING_DATE
" BAdI 名称:BADI_SD_SALES_ITEM
" 方法:ITEM_PROCESS
" 功能:促销品自动修改定价日期
"=======================================================================
METHOD if_ex_badi_sd_sales_item~item_process.
"-----------------------------------------------------------------------
" 步骤1:获取行项目数据(方式一:参数读取)
"-----------------------------------------------------------------------
DATA: lv_matnr TYPE matnr,
lv_matkl TYPE matkl,
lv_prsdt_old TYPE prsdt.
" 从 CHANGING 参数读取行项目数据
lv_matnr = ch_item-matnr. " 物料号
lv_prsdt_old = ch_item-prsdt. " 原定价日期
"-----------------------------------------------------------------------
" 步骤2:获取物料组(方式三:数据库查询)
"-----------------------------------------------------------------------
" BAdI 参数中没有物料组,需要从 MARA 表查询
SELECT SINGLE matkl
FROM mara
INTO @lv_matkl
WHERE matnr = @lv_matnr.
IF sy-subrc <> 0.
RETURN. " 物料不存在,退出
ENDIF.
"-----------------------------------------------------------------------
" 步骤3:判断是否为促销品
"-----------------------------------------------------------------------
IF lv_matkl <> 'PROMO'.
RETURN. " 不是促销品,退出
ENDIF.
"-----------------------------------------------------------------------
" 步骤4:修改定价日期(方式一:修改 CHANGING 参数)
"-----------------------------------------------------------------------
ch_item-prsdt = sy-datum. " ← 修改 CHANGING 参数,影响标准流程
"-----------------------------------------------------------------------
" 步骤5:记录修改日志(方式二:对象方法获取更多数据)
"-----------------------------------------------------------------------
DATA: lv_vbeln TYPE vbeln,
lv_posnr TYPE posnr,
ls_log TYPE zt_sd_pricing_log.
" 通过 IMPORTING 对象获取更多数据
lv_vbeln = im_item->get_header( )->get_doc_number( ). " 销售订单号
lv_posnr = im_item->get_item_number( ). " 行项目号
" 记录日志
ls_log-vbeln = lv_vbeln.
ls_log-posnr = lv_posnr.
ls_log-matnr = lv_matnr.
ls_log-prsdt_old = lv_prsdt_old.
ls_log-prsdt_new = sy-datum.
ls_log-uname = sy-uname.
ls_log-datum = sy-datum.
ls_log-uzeit = sy-uzeit.
" UPDATE TASK 异步写入
CALL FUNCTION 'Z_SD_PRICING_LOG_INSERT' IN UPDATE TASK
EXPORTING
is_log = ls_log.
"-----------------------------------------------------------------------
" 步骤6:将修改信息存入 ABAP 内存(方式四:供后续 BAdI 使用)
"-----------------------------------------------------------------------
EXPORT p_vbeln = lv_vbeln TO MEMORY ID 'SD_PRICING_MODIFIED'.
ENDMETHOD.
6.4 测试验证
| 测试场景 | 操作 | 预期结果 |
|---|---|---|
| 场景1 | VA01 → 物料组=PROMO → 定价日期=2025.01.01 → 保存 | 定价日期自动改为当前日期,日志记录 |
| 场景2 | VA01 → 物料组≠PROMO → 定价日期=2025.01.01 → 保存 | 定价日期保持不变 |
| 场景3 | VA01 → 物料组=PROMO → 查看日志表 | 日志表有修改记录 |
| 场景4 | VA02 → 修改已有销售订单 | 不触发(只在创建时触发) |
常见问题与排查
-
Q1:修改了 CHANGING 参数,但保存后数据没变?
排查:确认参数是 CHANGING;确认修改在 BAdI 方法结束前完成;确认标准程序后续没有覆盖;确认 BAdI 触发时机正确(保存前)。
-
Q2:BAdI 参数中没有我需要的字段怎么办?
按优先级:方式二(对象方法)→ 方式三(数据库)→ 方式四(ABAP 内存)→ 方式五(全局变量,最后手段)。
-
Q3:IMPORTING 参数和 CHANGING 参数有什么区别?
IMPORTING 只读,CHANGING 可读写,修改影响标准流程。
-
Q4:ABAP 内存和 SAP 内存有什么区别?
ABAP 内存(EXPORT/IMPORT TO MEMORY)仅当前会话有效;SAP 内存(SET/GET PARAMETER)跨会话但需谨慎。
-
Q5:通过对象方法获取数据,如何知道有哪些方法可用?
SE24 查看接口类方法;或调试器中查看对象引用。
-
Q6:BAdI 中查询数据库,对性能有什么影响?
同步执行会影响响应时间,优先用参数,必要时批量查询。
-
Q7:多个 BAdI 实现都修改同一个 CHANGING 参数,最终以哪个为准?
后执行的实现覆盖前面的,通过排序字段控制顺序,建议避免冲突。
总结
五种数据获取方式速查
| 方式 | 操作 | 可靠度 | 典型代码 |
|---|---|---|---|
| 接口参数 | lv_ekgrp = ch_header-ekgrp. |
★★★★★ | 直接读取 |
| 对象方法 | lv_ebeln = lo_header->get_number( ). |
★★★★☆ | 链式调用 |
| 数据库查询 | SELECT SINGLE FROM mara ... |
★★★☆☆ | WHERE 条件 |
| 内存共享 | IMPORT p_data = lv_data FROM MEMORY ID 'X'. |
★★★☆☆ | EXPORT/IMPORT |
| 全局变量 | ASSIGN ('(SAPMV45A)VBAK-VBELN') TO <fs>. |
★★☆☆☆ | 动态 ASSIGN |
数据修改规则速查
| 参数类型 | 可否修改 | 修改影响 | 修改后标准程序的行为 |
|---|---|---|---|
| IMPORTING | 否 | --- | --- |
| CHANGING | 是 | 影响标准流程 | 使用修改后的值继续执行 |
| EXPORTING | 是 | 标准程序读取 | 根据返回值决定后续行为 |
数据获取方式选型决策树
#mermaid-svg-YCfWC5LBMmh7vy0C{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-YCfWC5LBMmh7vy0C .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YCfWC5LBMmh7vy0C .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YCfWC5LBMmh7vy0C .error-icon{fill:#552222;}#mermaid-svg-YCfWC5LBMmh7vy0C .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YCfWC5LBMmh7vy0C .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YCfWC5LBMmh7vy0C .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YCfWC5LBMmh7vy0C .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YCfWC5LBMmh7vy0C .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YCfWC5LBMmh7vy0C .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YCfWC5LBMmh7vy0C .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YCfWC5LBMmh7vy0C .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YCfWC5LBMmh7vy0C .marker.cross{stroke:#333333;}#mermaid-svg-YCfWC5LBMmh7vy0C svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YCfWC5LBMmh7vy0C p{margin:0;}#mermaid-svg-YCfWC5LBMmh7vy0C .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-YCfWC5LBMmh7vy0C .cluster-label text{fill:#333;}#mermaid-svg-YCfWC5LBMmh7vy0C .cluster-label span{color:#333;}#mermaid-svg-YCfWC5LBMmh7vy0C .cluster-label span p{background-color:transparent;}#mermaid-svg-YCfWC5LBMmh7vy0C .label text,#mermaid-svg-YCfWC5LBMmh7vy0C span{fill:#333;color:#333;}#mermaid-svg-YCfWC5LBMmh7vy0C .node rect,#mermaid-svg-YCfWC5LBMmh7vy0C .node circle,#mermaid-svg-YCfWC5LBMmh7vy0C .node ellipse,#mermaid-svg-YCfWC5LBMmh7vy0C .node polygon,#mermaid-svg-YCfWC5LBMmh7vy0C .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-YCfWC5LBMmh7vy0C .rough-node .label text,#mermaid-svg-YCfWC5LBMmh7vy0C .node .label text,#mermaid-svg-YCfWC5LBMmh7vy0C .image-shape .label,#mermaid-svg-YCfWC5LBMmh7vy0C .icon-shape .label{text-anchor:middle;}#mermaid-svg-YCfWC5LBMmh7vy0C .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-YCfWC5LBMmh7vy0C .rough-node .label,#mermaid-svg-YCfWC5LBMmh7vy0C .node .label,#mermaid-svg-YCfWC5LBMmh7vy0C .image-shape .label,#mermaid-svg-YCfWC5LBMmh7vy0C .icon-shape .label{text-align:center;}#mermaid-svg-YCfWC5LBMmh7vy0C .node.clickable{cursor:pointer;}#mermaid-svg-YCfWC5LBMmh7vy0C .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-YCfWC5LBMmh7vy0C .arrowheadPath{fill:#333333;}#mermaid-svg-YCfWC5LBMmh7vy0C .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-YCfWC5LBMmh7vy0C .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-YCfWC5LBMmh7vy0C .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YCfWC5LBMmh7vy0C .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-YCfWC5LBMmh7vy0C .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YCfWC5LBMmh7vy0C .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-YCfWC5LBMmh7vy0C .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-YCfWC5LBMmh7vy0C .cluster text{fill:#333;}#mermaid-svg-YCfWC5LBMmh7vy0C .cluster span{color:#333;}#mermaid-svg-YCfWC5LBMmh7vy0C div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-YCfWC5LBMmh7vy0C .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-YCfWC5LBMmh7vy0C rect.text{fill:none;stroke-width:0;}#mermaid-svg-YCfWC5LBMmh7vy0C .icon-shape,#mermaid-svg-YCfWC5LBMmh7vy0C .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YCfWC5LBMmh7vy0C .icon-shape p,#mermaid-svg-YCfWC5LBMmh7vy0C .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-YCfWC5LBMmh7vy0C .icon-shape .label rect,#mermaid-svg-YCfWC5LBMmh7vy0C .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YCfWC5LBMmh7vy0C .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-YCfWC5LBMmh7vy0C .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-YCfWC5LBMmh7vy0C :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
是
否
是
否
需要获取数据
参数中有?
方式一:接口参数(最优先)
对象方法能获取?
方式二:对象方法调用
数据库中有?
方式三:数据库查询(注意性能)
其他 BAdI 已计算?
方式四:ABAP 内存共享
方式五:全局变量引用(最后手段)
本篇与下一篇的衔接
| 本篇(数据获取与交互) | 下一篇(异常处理与流程干预) |
|---|---|
| 学会获取和修改数据 | 学会处理异常和干预流程 |
| 掌握五种数据获取方式 | 掌握消息抛出和流程终止 |
下一篇预告:《BAdI 异常处理与流程干预:消息抛出、流程终止与回滚逻辑实现》------数据拿到了,但如果校验不通过怎么办?下一篇讲解 BAdI 中异常场景的处理逻辑,覆盖错误/警告消息抛出、标准流程终止、数据修改回滚的实现方法,梳理 BAdI 返回参数与标准流程流转的关联规则,附消息分级提示、异常日志记录的通用代码模板。
作者 :爱喝水的鱼丶
版本记录:2026 年 8 月
💬 你在 BAdI 开发中遇到过"参数不够用"的情况吗?你是用什么方式获取额外数据的?欢迎分享你的数据获取技巧。