ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SAP销售订单抬头增强:BADI_SLS_HEAD_SCR_CUT完整实施指南

SAP销售订单抬头增强:BADI_SLS_HEAD_SCR_CUT完整实施指南 去年在项目上接到一个SD增强需求销售订单VA01/VA02抬头要加三个自定义字段客户还特别强调不要再用老一套的USER EXIT要求用BADI_SLS_HEAD_SCR_CUT来扩展抬头屏幕。当时我翻了不少帖子发现很多人对BADI_SLS_HEAD_SCR_CUT要么一句话带过要么只讲SE19创建实施真正能把函数组、子屏幕、PBO/PAI、数据保存这条链路完整说清的极少。这篇文章就把我当时落地这套增强的完整过程、函数组代码、以及踩过的坑全部写出来希望能给正在做SAP SD屏幕增强的ABAP开发和SD顾问省点时间。这个需求本身不复杂核心就是三件事在VA01/VA02抬头页签上显示自定义字段用户输入后能随订单保存再次打开VA03还能看到。但真做到位需要把BADI原理、屏幕增强机制、VBAK表结构、函数组生命周期几个知识点串起来。下面我按实施顺序拆开讲你可以直接照着做。1. 为什么绕过了老式USER EXITBADI_SLS_HEAD_SCR_CUT解决的是什么问题1.1 从销售部门的一个真实需求说起销售订单抬头需要加下单渠道审批编号重点客户标记三个字段这是SD模块非常常见的扩展点。以前遇到这种需求很多顾问第一反应是找USER EXIT比如VA01/VA02的抬头保存出口然后通过CMOD或SMOD挂一段ABAP代码再手动往屏幕上塞字段。这么做不是不行但问题不少出口程序里的逻辑散落在多个FORM里后期换人维护非常痛苦字段在屏幕上的位置要么靠标准屏幕的隐式增强要么靠Customer Exit硬改版本升级时特别容易出幺蛾子而且出口代码一旦多了VA01/VA02操作响应明显变慢用户抱怨很大。BADI_SLS_HEAD_SCR_CUT这个增强点则是SAP专门为销售订单抬头屏幕预留的扩展接口它本质上是一个屏幕增强BADI。你在SE19里创建实施后可以把一个自建子屏幕嵌到标准VA01/VA02的抬头页签区域再通过接口方法在屏幕显示前把VBAK里的值读出来在屏幕输入后把值写回去。相比老式出口它的优势是增强逻辑和标准程序解耦字段维护在独立子屏幕里后续改动只动函数组不动标准程序。1.2 选型对比为什么不直接用其他增强点我见过不少人问抬头字段能不能用行项目的BADI_SLS_ITEM_SCR_CUT一起做掉或者直接改VBAK屏幕。这里必须先分清业务维度抬头字段是整张订单级别的行项目字段是每个订单项级别的。BADI_SLS_HEAD_SCR_CUT管抬头BADI_SLS_ITEM_SCR_CUT管行项目两者触发时机和数据对象完全不同别混着用。还有一个容易混淆的增强点是BADI_SD_SALES_HEAD_MAINTAIN它也能拦截抬头维护逻辑但主要面向业务规则校验和数据处理不是专门做屏幕字段的。如果你只是要加几个可输入字段、还要在VA01/VA02界面直接可见首选就是BADI_SLS_HEAD_SCR_CUT如果你要做的是保存时的复杂校验那可能还需要结合其他保存增强点。最稳妥的判断方法是在SE18里输入BADI_SLS_HEAD_SCR_CUT看看它的接口定义里是否有屏幕增强Screen Enhancement相关说明。选型这块我个人的建议是新增屏幕字段优先用BADI_SLS_HEAD_SCR_CUT不要再用老掉牙的USER EXIT只有字段完全不涉及屏幕展示、只做后台逻辑处理时才考虑别的增强点。2. 先搞懂数据从哪来到哪去BADI接口与屏幕增强的运行链路2.1 BADI的接口方法拆解在写代码之前我建议你先到SE18里把BADI_SLS_HEAD_SCR_CUT的定义完整的看一遍。不同SAP版本ECC 6.0、S/4HANA 1909、2020等的接口方法名和参数可能略有差异但核心通常会有这几个方法EDIT_HEADER在屏幕输出PBO阶段触发用来把当前销售订单抬头数据从VBAK读到你的子屏幕字段里。MODIFY_HEADER在屏幕输入PAI阶段触发用来把用户在子屏幕上录入的值写回抬头数据结构。SAVE在订单保存时触发用来执行自定义的持久化逻辑。INITIALIZE在订单初始创建时触发可以用来给自定义字段赋默认值。ACTIVATE_ALV / DEACTIVATE_ALV有些版本用来控制ALV相关界面元素的激活或隐藏增量字段不涉及ALV时可以不管。这里要特别提醒一句别直接把网上的代码复制粘贴。不同系统里接口参数名经常有差异比如抬头数据结构有的版本叫CS_HEADER有的版本在方法里以CHANGING参数传入参数结构可能是VBAK加自定义附加结构。你必须在SE18里点开方法签名看清楚参数名和参数结构再写实现。2.2 数据如何与VBAK附加结构联动这个增强的核心数据载体是VBAK表销售订单抬头表。你在SE11里给VBAK增加一个附加结构Append Structure里面放自定义字段比如ZORDER_SOURCE、ZAPPROVE_NO、ZKEY_CUSTOMER激活后VBAK这个数据库表就会带上这些字段。BADI的EDIT_HEADER、MODIFY_HEADER等方法在运行时接口参数里通常就包含VBAK的抬头结构以及附加结构字段。那么整个数据流就是用户打开VA01系统进入销售订单抬头屏幕PBO阶段触发BADI的EDIT_HEADER。当前订单抬头数据含VBAK附加字段被传入函数组赋值给函数组全局变量。子屏幕上的字段从全局变量取值显示。用户修改字段点击保存或回车PAI阶段触发。PAI模块把子屏幕字段写回函数组全局变量。随后触发BADI的MODIFY_HEADER函数组全局变量里的值被传回接口的抬头数据结构。标准保存流程启动VBAK带自定义字段一起落库。这里面有个关键点为什么子屏幕字段不能直接和BADI参数打交道因为子屏幕是在函数组里运行的其生命周期和VA01事务一致全局变量可以在多个PBO/PAI模块之间接力传值而BADI方法每次调用都是独立的直接把值塞给方法参数会因为调用顺序问题经常拿不到正确的数据。所以实战中标准做法永远是BADI方法负责和函数组全局变量交换数据屏幕负责和全局变量交换数据全局变量是中间桥梁。2.3 子屏幕是怎么挂上去的子屏幕的挂载原理很多人搞不清楚。BADI_SLS_HEAD_SCR_CUT不是你自己在标准屏幕上硬加一个容器而是SAP在VA01/VA02的抬头页签区域预留了一个客户增强区域。你在SE19里维护增强实施时需要指定一个函数组和子屏幕号SAP标准程序在运行到这个预留区域时会自动调用你的子屏幕。这也是为什么标题里强调函数组代码——因为子屏幕本身必须归属于一个函数组屏幕字段和PBO/PAI模块都编译进这个函数组。没有函数组子屏幕根本没法运行。理解了这个机制你就知道后续创建顺序为什么是函数组 - 子屏幕 - BADI实施。3. 拆开讲落地步骤从VBAK附加结构到SE19实施类3.1 第一步SE11给VBAK加附加结构打开SE11输入VBAK点击附加结构按钮新建一个以Z开头的附加结构比如ZSD_HEAD_APPEND。在这个结构里添加自定义字段注意字段名一般也是Z开头数据元素建议自建可以挂上域、搜索帮助、文本描述。我这里以三个字段为例ZORDER_SOURCE下单渠道字符型20位ZAPPROVE_NO审批编号字符型30位ZKEY_CUSTOMER重点客户标记字符型1位类似勾选框保存并激活。激活VBAK表附加结构时系统会做一次数据库表调整如果线上VBAK数据量很大建议在业务低峰期安排传输激活并且先在测试机确认DDIC激活时间。这一步做完后你可以用SE16N直接查看VBAK会发现表结构里已经能看到这三个字段但所有数据都是空值。接下来要做的就是让它们能通过屏幕输入值。3.2 第二步创建函数组与子屏幕进SE80创建一个函数组我这里是ZSD_VA_HEAD_FG。函数组会自动生成主程序和四个IncludeZSD_VA_HEAD_FGTOP、ZSD_VA_HEAD_FGUXX、ZSD_VA_HEAD_FGO01、ZSD_VA_HEAD_FGI01。其中TOP放全局变量O01放PBO模块I01放PAI模块UXX放私有子程序一般用不到。然后在函数组下创建屏幕1000屏幕类型选择子屏幕。进入屏幕编辑器后放置三个输入字段名称分别为ZORDER_SOURCE、ZAPPROVE_NO、ZKEY_CUSTOMER再放几个文本标签说明字段含义。布局不需要太复杂保持和SAP标准屏幕风格一致即可。这里有个小经验子屏幕的字段名最好和VBAK附加字段名保持一致这样在代码里用MOVE-CORRESPONDING时能省很多事。如果你字段名不一致PBO/PAI里就要逐个赋值代码会显得啰嗦也容易漏。3.3 第三步SE18/SE19创建BADI实施先到SE18输入BADI_SLS_HEAD_SCR_CUT查看接口定义确认当前系统版本里有哪些方法和参数。然后到SE19创建一个BADI实施实施名称一般是ZCL_IM_SLS_HEAD_SCR_CUT类似格式SAP建议以Z开头。创建时系统会让你输入增强点名称填BADI_SLS_HEAD_SCR_CUT。进入实施后界面中通常有一个区域用来维护屏幕增强Screen Enhancement信息。这里要选择刚才创建的函数组ZSD_VA_HEAD_FG和子屏幕1000。这是整个增强能否成功的最关键一步如果这里没维护你的子屏幕永远显示不出来。接下来在实施类里重定义接口方法。一般必须实现的是EDIT_HEADER、MODIFY_HEADER、SAVE、INITIALIZE这几个。保存代码后激活实施。3.4 第四步验证调用激活之后先用SE19的调试或者直接在VA01里用/ H进入调试模式在调用点检查BADI是否被触发。第一次测试大概率会发现要么子屏幕不显示要么显示出来了但字段空白、输入后保存不进去。别急这些都是正常的后面章节我会把排查和代码细节补齐。4. 函数组与屏幕1000的完整写法PBO、PAI和数据交换4.1 函数组全局数据定义TOP Include下面这套代码是我在ECC 6.0 EHP8系统上实际跑通的简化版本。注意再次强调接口方法的参数名要对照SE18里的实际定义我这里用cs_head代表接口传递的抬头结构参数你用的时候要替换成自己系统里的参数名。ZSD_VA_HEAD_FGTOP完整代码FUNCTION-POOL ZSD_VA_HEAD_FG. 函数组主定义 * 全局变量用于子屏幕与BADI方法之间传值 DATA: gv_zorder_source TYPE zorder_source, gv_zapprove_no TYPE zapprove_no, gv_zkey_customer TYPE zkey_customer, gv_vbak_wa TYPE vbak.这段代码里最关键的是gv_vbak_wa它用来暂存当前VA01/VA02正在处理的VBAK抬头记录。BADI的EDIT_HEADER把抬头数据挪到这里屏幕字段从gv_zorder_source这些变量里取值用户输入后PAI模块把屏幕字段的值挪回gv_vbak_wa最后MODIFY_HEADER再把gv_vbak_wa整个传回去。4.2 PBO输出模块O01 Include在Include ZSD_VA_HEAD_FGO01里写PBO模块MODULE pbo_1000 OUTPUT. * 输出前把全局VBAK工作区里的附加字段搬到屏幕字段变量 CLEAR: gv_zorder_source, gv_zapprove_no, gv_zkey_customer. MOVE-CORRESPONDING gv_vbak_wa TO gv_zorder_source. gv_zorder_source gv_vbak_wa-zorder_source. gv_zapprove_no gv_vbak_wa-zapprove_no. gv_zkey_customer gv_vbak_wa-zkey_customer. ENDMODULE.这里为什么要CLEAR再赋值因为子屏幕每次PBO输出时都会重新执行如果不清理旧值用户新建订单时可能残留上一张订单的值。我第一次实现时没注意这个导致VA02修改订单A后直接新建订单抬头字段还带着订单A的内容差点误导业务人员。4.3 PAI输入模块I01 Include在Include ZSD_VA_HEAD_FGI01里写PAI模块MODULE pai_1000 INPUT. * 用户确认输入后把屏幕字段搬运回VBAK工作区 gv_vbak_wa-zorder_source gv_zorder_source. gv_vbak_wa-zapprove_no gv_zapprove_no. gv_vbak_wa-zkey_customer gv_zkey_customer. ENDMODULE.PAI模块的触发时机可以设置在子屏幕的回车或保存等用户操作上。这里不需要写太复杂的逻辑如果字段需要做必填校验或合法性检查可以放在这个模块里通过MESSAGE报错中断用户输入。但注意别在这里报标准字段的错只对自定义字段做校验否则容易干扰SAP标准屏幕的输入顺序。4.4 BADI实现类中的方法代码接下来说实施类里的方法实现。EDID_HEADER和MODIFY_HEADER是重头。EDIT_HEADER方法示例METHOD if_ex_sls_head_scr_cut~edit_header. * 方法触发时接口参数cs_head代表当前订单抬头结构。 * 先把抬头数据存入函数组全局工作区。 gv_vbak_wa cs_head. ENDMETHOD.注意这里cs_head并不是标准的VBAK表结构那么简单在BADI接口定义里它通常是一个包含VBAK字段和自定义附加结构的抬头工作区。你只要把它整个赋值给gv_vbak_wa即可类型如果对不上就做一次MOVE-CORRESPONDING。MODIFY_HEADER方法示例METHOD if_ex_sls_head_scr_cut~modify_header. * 用户输入结束后把函数组全局工作区的值传回抬头结构 cs_head gv_vbak_wa. ENDMETHOD.INITIALIZE方法示例METHOD if_ex_sls_head_scr_cut~initialize. * 新建订单时给自定义字段设置默认值 gv_zorder_source WEB. gv_zkey_customer . gv_vbak_wa-zorder_source gv_zorder_source. ENDMETHOD.SAVE方法示例方案A随VBAK自动保存METHOD if_ex_sls_head_scr_cut~save. * 如果字段已经通过MODIFY_HEADER写回抬头结构标准保存会自动写VBAK * 这里一般不需要额外操作。 * 如果需要做保存前额外处理可以在这里写DETERMINE逻辑。 ENDMETHOD.这里我强烈建议你打开BADI方法在每个方法的第一行打上断点然后在VA01里逐步跑一遍。你会更直观地看到进入抬头屏幕时EDIT_HEADER先触发保存时MODIFY_HEADER和SAVE按顺序触发这个顺序对整个设计至关重要。4.5 关于完整函数组代码的组合说明一个完整的函数组除了TOP、O01、I01还需要一个主程序和一些屏幕定义。SE80创建函数组时主程序会自动生成你只需要重命名和激活即可。屏幕1000的布局在SE51里维护代码在O01和I01里编写全局变量在TOP里定义。严格来说函数组本身就是这么多东西并不像普通报表程序那样把所有代码写在一个文件里。如果你在种子代码里看到函数组包含F01这种Include那是放私有FORM的用于封装一些可复用的内表处理或计算逻辑。我们这个需求里没有特别复杂的子程序所以F01可以留空不写。但如果字段多了建议把赋值逻辑抽成FORM比如FORM fill_screen_fields、FORM fill_vbak_fields这样后续加字段只扩FORM不动PBO/PAI主流程。5. 保存环节的两种玩法随VBAK落库和自定义扩展表5.1 方案A随VBAK附加结构直接保存前文代码走下来你会发现自定义字段最终通过MODIFY_HEADER写回了接口的抬头结构在标准保存流程中VBAK表会整体更新附加字段自然也就保存进去了。这在实际项目里是最推荐的方案原因有三个。第一事务一致性天然得到保证。SAP标准保存逻辑会处理VBAK的UPDATE、INSERT和数据库提交你的字段跟着主人走不会出现标准表保存成功扩展表没保存的尴尬。第二查询方便。订单抬头加字段后报表、查询、接口程序直接读VBAK-ZORDER_SOURCE就能拿到值不用再去JOIN一张自定义表。第三后续增强方便。如果哪天要加字段到输出FORM、IDOC或接口映射直接引用VBAK字段即可。但方案A有一个前提你必须在SE11里真的给VBAK表增加附加结构否则光在BADI方法里操作cs_head某个字段系统会报类型错误。这个前面已经讲过别漏了。5.2 方案B自定义扩展表保存有的项目出于数据库表结构管控要求不允许随意给标准表加附加结构尤其是一些S/4HANA云环境和大型企业集团对标准表的扩展管得非常严。这时候可以改用独立扩展表保存。需要先创建一张自定义表比如ZSD_ORDER_EXT关键字段如下VBELN主键对应VBAK-VBELNZORDER_SOURCEZAPPROVE_NOZKEY_CUSTOMERERNAM创建人ERDAT创建日期在SAVE方法里写METHOD if_ex_sls_head_scr_cut~save. DATA: ls_ext TYPE zsd_order_ext. MOVE-CORRESPONDING gv_vbak_wa TO ls_ext. ls_ext-vbeln gv_vbak_wa-vbeln. ls_ext-ernam sy-uname. ls_ext-erdat sy-datum. MODIFY zsd_order_ext FROM ls_ext. IF sy-subrc 0. MESSAGE e001(zsd_va_head_fg) WITH 扩展表保存失败. ENDIF. ENDMETHOD.这段代码看着简单但有两个问题必须处理好。一是保存时机SAVE方法是在标准数据库提交之前还是之后需要仔细看接口文档或用调试确认否则可能出现扩展表更新了VBAK却没提交或者反过来。二是读值同步进入VA02修改时EDIT_HEADER只能读到VBAK里的标准字段你的自定义字段在扩展表里所以还得在EDIT_HEADER里加一段按VBELN读扩展表的逻辑把值补到gv_vbak_wa里屏幕才能显示出来。5.3 我的推荐和理由如果项目允许我会优先选方案A也就是给VBAK加附加结构。理由很简单SD销售订单的抬头表扩展本来就非常常见SAP的附加结构机制就是为这种场景设计的公开、可持续、好维护。扩展表适合那些字段值不是跟随订单抬头保存而是要单独审计、单独归档、或者做复杂权限控制的特殊场景。不过要提醒一点即使是方案A也别在SAVE方法里再去MODIFY数据库表VBAK那样会导致两次更新而且很容易和标准更新逻辑冲突。你只要确保MODIFY_HEADER把数据正确写回抬头结构保存就交给SAP标准程序。6. 上线前必须验证的6个场景和3个坑6.1 六个必须验证的测试场景我整理成了一张表每次项目上线前我都照着这个清单跑一遍基本能覆盖日常使用场景。场景操作预期结果VA01新建订单新建标准订单在抬头自定义字段输入值保存订单保存成功字段值正常入库VA02修改订单打开已存在订单修改自定义字段值保存修改后值更新到VBAK再次打开显示新值VA03显示订单直接查看订单不进入编辑状态自定义字段正常显示且不能编辑复制订单复制为用复制为创建新订单旧订单自定义字段值复制过来新建订单可修改跨单据类型测试标准订单OR、退货订单RE、借贷项单等显示正常无字段丢失或报错批量保存与回车连续录入多张订单切换页签快速保存字段值不会串单PBO/PAI数据不互相污染这里尤其要重视复制为场景因为复制订单时SAP内部走的是另一套数据拷贝逻辑BADI的EDIT_HEADER可能不会按预期触发自定义字段的值很容易丢。如果客户有这个需求可能需要额外在复制增强点里补拷贝逻辑。6.2 三个必踩的坑坑一GLOBAL变量没清理导致串单。这个前面已经提到PBO模块开头一定要CLEAR字段。新建订单、打开已有订单、切页签都会反复触发PBO不清空上一次的残留值用户很快就会发现数据自己跑了。排查方法很简单在PBO里打断点看每次进入时字段变量是不是已经清干净。坑二BADI方法在VA03和其他非编辑模式下也触发。有些版本里VA03显示订单同样会触发BADI方法但你放在屏幕上的字段可能是可输入状态让用户误以为可以在显示画面改东西。处理方法是在PBO模块里判断当前事务模式比如通过SY-UCOMM、SY-TCODE或者调用系统函数识别主程序调用状态然后设置字段的输入属性。更常见的办法是在屏幕字段属性里直接设置输入属性但如果你想根据订单状态动态控制就在PBO里调用SCREEN-LOOP循环改SCREEN-INPUT。坑三子屏幕字段名和VBAK附加字段名不一致赋值遗漏。如果只用MOVE-CORRESPONDING字段名对不上就会静默失败没有任何报错。我建议在开发初期把字段名完全统一ZORDER_SOURCE在附加结构、屏幕字段、全局变量、数据元素层层保持同名能省掉大量排查时间。真遇到字段名必须不同的情况就别偷懒逐个赋值并在代码里加注释。6.3 传输与激活顺序最后说下传输这个增强涉及的传输对象不少包括SE11里VBAK附加结构ZSD_HEAD_APPEND自定义数据元素和域SE80函数组ZSD_VA_HEAD_FG包含TOP、O01、I01屏幕1000SE19创建的BADI实施类ZCL_IM_SLS_HEAD_SCR_CUT我的建议是创建完所有对象后在一个变更请求里打包传输。激活顺序要注意先激活字典对象数据元素、域、附加结构再激活函数组和屏幕最后激活BADI实施类。如果先激活BADI实施类实施类引用的字段类型或函数组全局变量还不存在激活会报错。这个顺序问题在传输到QA或生产系统时尤其明显很多人上线当天才发现激活不了就是因为对象顺序乱了。传输过去之后别忘了在生产系统SE19里检查BADI实施是否处于激活状态并且用VA01做一个冒烟测试确认子屏幕真的显示出来再放开用户使用。最后再分享一个我实测过的经验这套增强不只是VA01/VA02/VA03能用很多从销售订单抬头复用的业务场景也会触发比如定价过程中的抬头条件、输出确定等里面如果读取自定义字段可能有性能消耗。字段本身别加太多够用就行。我自己做完这个增强后的感受是BADI_SLS_HEAD_SCR_CUT并没有想象中那么难最难的反而是把函数组生命周期和BADI方法调用顺序之间的关系理解透。只要把这条链路跑通了后面再做行项目屏幕增强、采购订单屏幕增强思路基本是一样的。
返回列表