本文将深入解析SAP BADI(Business Add-In)增强技术,从基础概念到实战应用,帮助SAP开发人员掌握这一强大的扩展工具。
什么是SAP BADI?
SAP BADI(Business Add-In)是SAP系统中的一种面向对象增强技术,它允许开发人员在标准SAP程序中插入自定义代码,而无需修改原始代码。BADI是SAP第三代增强技术,相比传统的User Exit提供了更灵活的扩展机制。
BADI的核心原理
BADI基于面向对象的设计模式,通过接口和实现类的机制来实现增强。其工作原理如下:
graph TD
A[标准SAP程序] --> B[调用BADI接口]
B --> C{BADI过滤器}
C -->|匹配| D[自定义实现类]
C -->|不匹配| E[标准实现类]
D --> F[执行增强逻辑]
E --> G[执行标准逻辑]
BADI的主要类型
1. 经典BADI(Classic BADI)
经典BADI使用CL_EXITHANDLER类进行调用,是SAP早期版本的BADI实现方式。
特点:
- 基于接口的调用机制
- 支持多重实现
- 需要手动创建实现类
2. 新BADI(Kernel BADI)
新BADI是SAP NetWeaver 7.0之后引入的增强技术,性能更优,使用更便捷。
特点:
- 基于内核的调用机制
- 更好的性能表现
- 支持更复杂的过滤器
3. 复合BADI(Composite BADI)
复合BADI允许将多个BADI组合使用,实现更复杂的业务逻辑。
BADI的创建步骤
第一步:查找合适的BADI
使用事务码SE80或SE18查找系统中可用的BADI:
" 通过程序查找BADI
REPORT z_find_badi.
DATA: lt_badi TYPE TABLE OF sxs_attr,
ls_badi TYPE sxs_attr.
SELECT * FROM sxs_attr INTO TABLE lt_badi
WHERE exit_name LIKE 'BADI_%'.
LOOP AT lt_badi INTO ls_badi.
WRITE: / ls_badi-exit_name, ls_badi-description.
ENDLOOP.第二步:创建BADI实现
- 使用事务码
SE19创建BADI实现 - 输入BADI名称,点击"创建"按钮
- 选择实现类型(经典BADI或新BADI)
第三步:实现接口方法
" BADI接口定义示例
INTERFACE if_ex_badi_material_check.
METHODS: check_material
IMPORTING iv_material TYPE matnr
EXPORTING ev_status TYPE char1
CHANGING cv_message TYPE string.
ENDINTERFACE.
" BADI实现类
CLASS zcl_im_material_check DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES: if_ex_badi_material_check.
PRIVATE SECTION.
METHODS: validate_material_data IMPORTING iv_material TYPE matnr
RETURNING VALUE(rv_status) TYPE char1.
ENDCLASS.
CLASS zcl_im_material_check IMPLEMENTATION.
METHOD if_ex_badi_material_check~check_material.
" 实现物料检查逻辑
ev_status = validate_material_data( iv_material ).
IF ev_status = 'E'.
cv_message = '物料检查失败:数据不完整'.
ELSE.
cv_message = '物料检查通过'.
ENDIF.
ENDMETHOD.
METHOD validate_material_data.
" 具体的验证逻辑
SELECT SINGLE * FROM mara INTO @DATA(ls_mara)
WHERE matnr = @iv_material.
IF sy-subrc = 0 AND ls_mara-mtart IS NOT INITIAL.
rv_status = 'S'.
ELSE.
rv_status = 'E'.
ENDIF.
ENDMETHOD.
ENDCLASS.第四步:配置过滤器(可选)
过滤器允许根据特定条件激活不同的BADI实现:
" 过滤器配置示例
DATA: lr_filter TYPE REF to if_ex_badi_material_check_filter.
CREATE OBJECT lr_filter
EXPORTING
iv_plant = '1000' " 工厂代码
iv_mtype = 'FERT'. " 物料类型实战应用场景
场景一:物料主数据增强
在物料创建时进行自定义验证:
METHOD if_ex_badi_material_check~check_material.
DATA: lt_mara TYPE TABLE OF mara.
" 检查物料描述是否包含敏感词汇
IF iv_material_description CS '测试' OR
iv_material_description CS '临时'.
ev_status = 'W'.
cv_message = '警告:物料描述包含测试相关词汇'.
ENDIF.
" 检查物料 组是否符合公司规范
IF iv_material_group IS INITIAL.
ev_status = 'E'.
cv_message = '错误:物料组不能为空'.
ENDIF.
" 调用外部API进行额外验证
CALL FUNCTION 'Z_EXTERNAL_MATERIAL_CHECK'
EXPORTING
iv_material = iv_material
IMPORTING
ev_status = ev_status
ev_message = cv_message.
ENDMETHOD.场景二:采购订单审批增强
在采购订单审批流程中 添加自定义检查:
CLASS zcl_im_po_approval_check DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES: if_ex_badi_po_approval.
PRIVATE SECTION.
CONSTANTS: gc_max_amount TYPE bapicurr-bapiprice VALUE '10000'.
ENDCLASS.
CLASS zcl_im_po_approval_check IMPLEMENTATION.
METHOD if_ex_badi_po_approval~check_approval.
DATA: lv_total_amount TYPE bapicurr-bapiprice.
" 计算采购订单总金额
SELECT SUM( netwr ) INTO @lv_total_amount
FROM ekpo
WHERE ebeln = @iv_po_number.
" 检查是否超过审批权限
IF lv_total_amount > gc_max_amount.
ev_approval_status = 'R'.
cv_rejection_reason = '采购金额超过权限限制'.
ELSE.
ev_approval_status = 'A'.
ENDIF.
" 记录审批日志
CALL FUNCTION 'Z_LOG_PO_APPROVAL'
EXPORTING
iv_po_number = iv_po_number
iv_amount = lv_total_amount
iv_status = ev_approval_status.
ENDMETHOD.
ENDCLASS.场景三:财务凭证增强
在财务凭证过账时添加自定义字段验证:
METHOD if_ex_badi_fi_document~enhance_document.
DATA: lt_extension TYPE TABLE OF bapiextc.
" 检查成本中心是否有效
IF is_document-header-kostl IS NOT INITIAL.
SELECT SINGLE * FROM csks INTO @DATA(ls_csks)
WHERE kostl = @is_document-header-kostl
AND kokrs = @is_document-header-kokrs
AND datbi >= @sy-datum
AND datab <= @sy-datum.
IF sy-subrc <> 0.
cv_error_message = '成本中心无效或已过期'.
RAISE EXCEPTION TYPE cx_fi_document_error.
ENDIF.
ENDIF.
" 添加自定义字段到扩展结构
APPEND VALUE #( fieldname = 'ZZPROJECT' fieldvalue = iv_project_code ) TO lt_extension.
" 调用BAPI更新凭证
CALL FUNCTION 'BAPI_ACC_DOCUMENT_POST'
EXPORTING
documentheader = is_document-header
TABLES
accountgl = it_accountgl
currencyamount = it_currencyamount
extension1 = lt_extension
IMPORTING
obj_key = cv_document_number.
ENDMETHOD.BADI与User Exit的区别
| 特性 | BADI | User Exit |
|---|---|---|
| 技术基础 | 面向对象 | 面向过程 |
| 实现方式 | 接口实现 | 子程序调用 |
| 多重实现 | 支持 | 不支持 |
| 过滤器支持 | 支持 | 不支持 |
| 版本兼容性 | 更好 | 较差 |
| 性能 | 更优 | 一般 |
| 维护性 | 更好 | 较差 |
选择建议
-
使用BADI的场景:
- 需要多重实现
- 需要复杂的过滤条件