1. 需求

有时候我们需要为VF01/VF02/VF03增加自定义选项卡来满足用户需求,并且在这个选项卡上维护一些自定义字段的值,在这份文档中我们以金税发票为例。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图1

2. 症状

发票凭证选项卡没有屏幕增强和用户出口,SAP标准只有业务加载项 (BADI_SD_CUST_HEAD, BADI_SD_CUST_ITEM) 可用,但是这两个业务加载项仅供SAP内部使用。

3. 环境

本文基于SAP ERP 6.0 EHP7 IDES,NetWeaver 7.42,并且在SAP S4/HANA 1511(S4Core 100 SP03), NetWeaver 7.49同步验证。
本文使用的SAP GUI是750 Patch 1。

4. 问题重现

当我们尝试创建BADISD_CUST_HEAD或BADI_SD_CUST_ITEM的业务加载项实施时,会看到错误消息“业务加载项定义BADISD_CUST_xxxx仅供SAP内部使用”。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图2
检查业务加载项定义,我们发现这些业务加载项被标记为“SAP内部”,所以我们不能使用这个业务加载项来增加我们自己的数据。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图3

5. 原因和前提

5.1 参考

SAP Note 864944 有详细说明,SAP只打算让这些业务加载项用在单独的SAP行业解决方案中。

5.2 调查分析

本文以发票凭证的抬头选项卡为例,计划增加金税发票号和金税发票生成日期两个字段,并且相关数据应当存储在VBRK的附加结构中。
SE93找到VF03对应的主程序是SAPMV60A(VF01/VF02也是同一个),在资源库浏览器中检查程序SAPMV60A的屏幕,发现屏幕6001/6101可能是相关的。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图4
从所用位置可以看出BADI_SD_CUST_HEAD被用在函数模块GET_HANDLE_SD_CUST_HEAD中,这个函数模块的使用位置有4个命中。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图5
同时我们检查屏幕逻辑流(以6001为例),模块CUST_HEAD_ACTIVATE调用的CUST_HEAD_ACTIVATE中使用到函数模块GET_HANDLE_SD_CUST_HEAD。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图6
从IF_EX_BADI_SD_CUST_HEAD~ACTIVATE_TAB_PAGE的导航目标我们可以看到SAP标准有一个业务加载项实施CL_IM_WB2_IVGUI_LAYOUT_H,我们可以通过SE19查看WB2_IVGUI_LAYOUT_H。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图7
3.为发票凭证VF01/2/3增加自定义选项卡 - 图8
结论就是业务加载项定义BADI_SD_CUST_HEAD的实施WB2_IVGUI_LAYOUT_H是为全球贸易这个行业解决方案实现的,存储在WB2B_CORE这个包中。
照此完整来看,为发票凭证增加自定义选项卡的业务加载项实施正是与屏幕6001和6101有关,既然这个业务加载项不允许我们自己来实施,我们可以尝试在屏幕6001和6101中注入类似的代码来达成目标。

5.3 验证

我们来调试找出所有的关键点。
业务加载项实施WB2IVGUI_LAYOUT_H中有4个方法,我们为每一个方法设置断点,然后找一个发票凭证用VF02来修改。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图9
当我们转到抬头选项卡,会碰到第一个断点ACTIVATE_TAB_PAGE。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图10
当我们转到“
全球贸易管理”选项卡,会碰到第二个断点TRANSFER_DATA_TO_SUBSCREEN,这个选项卡就是有业务加载项WB2_IVGUI_LAYOUT_H增强出来的。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图11
当我们输入数据或点击屏幕上的按钮,会碰到第三个断点PASS_FCODE_TO_SUBSCREEN,然后碰到第四个断点TRANSFER_DATA_FROM_SUBSCREEN。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图12
使用调试器的“
返回 (F7)”我们可以验证这四个断点都是跟屏幕6001和6101有关的,这就验证了之前的结论。
以上是基于SAP ERP 6.0 EHP7 IDES,但是基本上在生产环境我们不会有“
全球贸易管理”这个选项卡,让我们来找出根本原因。
检查WB2_IVGUI_LAYOUT_H->ACTIVATE_TAB_PAGE中的代码并追溯,最终找到控制点来自TWGTAWB2_ADDON_ACTIVE
3.为发票凭证VF01/2/3增加自定义选项卡 - 图13
TWGTA是通过视图V_TWGTA维护的,配置路径是SPRO–>后勤 – 常规–>全球贸易管理–>激活组件,找到配置选项“
增强设置”–>“附加激活”。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图14
当这个选项设置为“_2 所有凭证激活
”,我们可以在发票凭证抬头看到增强的“全球贸易管理”选项卡。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图15
尝试设置成“未激活
3.为发票凭证VF01/2/3增加自定义选项卡 - 图16
这样“全球贸易管理”选项卡就不见了。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图17
这个选项属于“SAP企业扩展全球贸易”这个行业解决方案,包含在独立的EA-GLTRADE组件中,从S4/HANA 1503开始被合并到S4CORE中。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图18
这个选项在不同SAP版本中的默认值不同,具体如下:
3.为发票凭证VF01/2/3增加自定义选项卡 - 图19

6. 解决方案

6.1 副作用

因为SAP只预留了一个屏幕对发票凭证抬头进行增强,所以我们为发票凭证增加我们自定义的选项卡之前需要注意检查“全球贸易管理”的有效性。
如下解决方案基于我们未启用“全球贸易管理”的前提,如果希望启用“全球贸易管理”的情况下增加自定义的数据,可以考虑在业务加载项WB2_IVGUI_LAYOUT_H中处理,本文不再赘述,参考主程序SAPLWB2B_SCREEN_HANDLING。

6.2 附加结构

要显示在自定义选项卡中的数据,我们将定义ZZ字段附加到VBRK中,以便这些数据能够自动存储在VBRK里。为了避免冲突,所有的名称应当以两个Z打头,因为有些标准字段的名称是以一个Z打头的。
创建数据元素ZGTINO,描述、数据类型、字段标签如下:
3.为发票凭证VF01/2/3增加自定义选项卡 - 图20
创建数据元素ZGTIDT,描述、数据类型、字段标签如下:
3.为发票凭证VF01/2/3增加自定义选项卡 - 图21
显示表VBRK,转到附加结构,并点击创建附加,填写附加名称“ZZVBRK_APPEND_GTI”,结构名称应当以“ZZ”打头。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图22
填入组件“ZZGTI_NUM”和“ZZGTI_DAT”,用先前创建的数据元素作为组件类型,确保组件名称也是以“ZZ”打头。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图23

6.3 创建处理程序

主程序SAPMV60A中的所有包含文件都是以MV60AF打头的,所以我们创建一个独立的程序ZMV60AFCUST_HEAD来存放所有的处理代码。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图24

转到资源库浏览器,创建屏幕6001,确保屏幕类型设置为“
子屏幕”。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图25
3.为发票凭证VF01/2/3增加自定义选项卡 - 图26
转到屏幕6001的逻辑流,编辑PBO和PAI代码,双击PBO_6001,在主程序_ZMV60AFZ_CUST_HEAD
中创建输出前处理模块,编写自定义的代码,最后再回到屏幕6001点击格式,进入下一步。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图27
在屏幕绘制器上按F6打开字典/程序字段窗口,在表/字段名称中输入VBRK,然后点击从字典获取,选择要在屏幕上显示的字段。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图28

6.4 屏幕增强

在主程序SAPMV60A的屏幕6001中双击CUSTHEAD_ACTIVATE直到对应的FORM,然后按Shift+F4进行增强
3.为发票凭证VF01/2/3增加自定义选项卡 - 图29
选择菜单编辑–>增强操作->显示隐式增强选项。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图30
在_FORM CUST_HEAD_ACTIVATE.下一行
点击右键,选择增强操作–>创建实施,选择增强模式为声明。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图31
创建新的增强实施ZGTI_SAPMV60A
3.为发票凭证VF01/2/3增加自定义选项卡 - 图32
这里提供两种方案来实施增强,建议使用B方案

  1. PERFORM … IN PROGRAM … IF FOUND
  2. CALL BADI … (自定义业务加载项)

方案 A: PERFORM … IN PROGRAM … IF FOUND
在增强实施中编写自己的代码,在PERFORM … IN PROGRAM …的语法中不要忘记追加IF FOUND,涉及详细逻辑的代码可以在程序ZMV60AFZ_CUST_HEAD中编写,可以参考WB2_IVGUI_LAYOUT_H->ACTIVATE_TAB_PAGE
3.为发票凭证VF01/2/3增加自定义选项卡 - 图33
业务加载项BADI_SD_CUST_HEAD的另外三个方法是用在屏幕6101中,建议不管当前是否有使用到,都应当进行增强到程序ZMV60AFZCUST_HEAD中以预留将来可以方便的修改,所有的增强实施都应当使用同一个名称ZGTI_SAPMV60A,这样我们可以方便地使用SE20查看所有的增强。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图34
3.为发票凭证VF01/2/3增加自定义选项卡 - 图35
3.为发票凭证VF01/2/3增加自定义选项卡 - 图36
3.为发票凭证VF01/2/3增加自定义选项卡 - 图37
3.为发票凭证VF01/2/3增加自定义选项卡 - 图38
方案 B: CALL BADI … (custom BADI)
使用自定义业务加载项是比较好的方式,以创建接口开始,可以参考_IF_EX_BADI_SD_CUST_HEAD
。我们转到SE24来创建ZIFEX_BADI_SD_CUST_HEAD。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图39
参考_IF_EX_BADI_SD_CUST_HEAD
来设置方法和参数,并为ACTIVATE_TAB_PAGE增加FTAB的导出参数作为选项卡的说明文字,再增加一个CHECKBADI_ACTIVATE的方法来检查标准的业务加载项是否已经激活。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图40
接口准备好后,就可以到SE20来创建增强点ZES_GTI_SAPMV60A。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图41
增强点元素定义中,我们创建一个业务加载项定义ZBADI_SD_CUST_HEAD。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图42
将我们先前创建的接口ZIF_EX_BADI_SD_CUST_HEAD分配进来。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图43
取消“
多种用法”的选项。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图44
创建
业务加载项实施ZGTI_BADI_SD_CUST_HEAD,并在弹出的窗口填写实施类ZCL_IM_GTI_BADI_SD_CUST_HEAD。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图45
双击或通过SE19可以查看业务加载项实施_ZGTI_BADI_SD_CUST_HEAD

3.为发票凭证VF01/2/3增加自定义选项卡 - 图46
在方法名称上双击以创建方法实施
3.为发票凭证VF01/2/3增加自定义选项卡 - 图47
3.为发票凭证VF01/2/3增加自定义选项卡 - 图48
为实施类ZCL_IM_GTI_BADI_SD_CUST_HEAD分配别名
3.为发票凭证VF01/2/3增加自定义选项卡 - 图49
为相关的实施方法编写自己的代码,不要忘记检查标准的业务加载项是否激活,可以参考标准业务加载项实施WB2_IVGUI_LAYOUT_H的方法代码。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图50
3.为发票凭证VF01/2/3增加自定义选项卡 - 图51
3.为发票凭证VF01/2/3增加自定义选项卡 - 图52
3.为发票凭证VF01/2/3增加自定义选项卡 - 图53
最后,在主程序SAPMV60A的屏幕60016101对应的增强实施中编写自己的代码,简单使用CALL BADI即可。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图54
3.为发票凭证VF01/2/3增加自定义选项卡 - 图55

6.5 验证

VF02修改发票凭证,转到抬头–>附加数据,填写相关的数据并保存,然后检查表VBRK发现附加数据已经保存成功。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图56
用VF03查看发票凭证,自定义选项卡中的附加数据是只读的。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图57
当我们再次启用“全球贸易管理”,我们的自定义选项卡会被替换,不会影响标准的配置控制。
3.为发票凭证VF01/2/3增加自定义选项卡 - 图58

7. 最佳实践

尽量不要直接在增强实施中写具体逻辑,创建自定义业务加载项是一个比较好的选择,不要忘记预留足够的参数并且需要检查调用的有效性,以避免可能的影响,并且将来如果要再调整增强的逻辑的时候,可以很容易的通过SE20来控制。
由于FORMS是一个过期的ABAP语法,在生产环境不建议使用方案A。

8. 引用

SAP Note 864944, Cannot edit Business Add-In, https://launchpad.support.sap.com/#/notes/864944

)