ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

SAP User Exit与Customer Exit增强:从原理到实战,不砸墙也能改标准程序

2026/9/17 7:57:04 拓冰建站 浏览量
SAP User Exit与Customer Exit增强:从原理到实战,不砸墙也能改标准程序 第一次带SAP项目的时候组里有个新人接到一个需求销售订单保存前要检查一个自定义的信用标识不满足就得拦住整单。小伙子很勤快直接SE38打开SAPMV45A准备改标准代码我赶紧喊停——SAP早就给业务需求留了“门”User Exit与Customer Exit就是专门用来干这种事的不砸墙也能进房间而且进得可控、出得合规。这篇东西就是想把这两扇门从原理到实操掰开揉碎讲清楚适合刚接触SAP增强的ABAP开发也适合那些已经写了几个出口但一直没搞明白内部机制的项目顾问。1. 为什么不能直接改标准程序一次升级引发的“事故现场”1.1 标准代码是SAP的地基你动了它就要自己扛注很多人刚学ABAP时都有过这样的冲动看到SAP标准程序不够用第一个念头就是打开源码改几个if分支或者干脆注释掉一段校验。表面上功能立刻实现了但这等于在楼房承重墙上砸了一锤子。SAP标准程序不是某一版定稿就永远不变的Support Package、Feature Package、S/4HANA升级都会持续更新标准对象。只要你的对象列表里出现“修改过”的标志每次导入补丁都会弹出冲突轻则手动调整重则整个对象报错、升级中断而你写的那段代码能不能保留完全要看运气。更要命的是直接改标准程序会导致问题无法定位。上线三个月后业务说某个价格计算不对你检查发现是半年前某人顺手改了标准逻辑这个锅只能自己背。SAP之所以在架构上规划出各种增强方式本质就是给客户一个合法的“自定义空间”让业务逻辑与标准逻辑彼此隔离升级时互不干扰。这个空间有一系列形态旧的User Exit、Customer Exit后来的Business Add-InBAdI再到NetWeaver时期的增强点Enhancement Point和隐式增强Implicit Enhancement技术代代更迭核心思想始终是同一句话标准归标准客户归客户。1.2 SAP早就留了“门”增强机制的演进脉络讲User Exit和Customer Exit之前有必要把它们放进SAP增强演进的大坐标系里看。SAP R/3早期几乎没有增强概念客户要扩展功能只能改码结果就是升级灾难频发。后来SAP开始在标准程序的特定位置预埋一些“空壳”有的是PERFORM USEREXIT_xxx的调用对应的FORM却由客户来填有的是CALL CUSTOMER-FUNCTION xxx对应的函数模块允许客户实现。这就是今天的主角。再往后到了R/3 4.6版本SAP推出了BAdI用面向对象接口替代了散落的FORM和函数模块支持多个实现并存还带过滤器和排序逻辑灵活性远超经典增强。NetWeaver 7.0之后又出现了增强点Enhancement Point和隐式增强可以直接在标准源码里插入代码不需要SAP预埋出口。但这里有个很现实的情况很多核心业务场景里SAP只提供了User Exit/Customer Exit或者业务团队已经基于老增强跑了十几年代码稳定得没人愿意动。所以经典增强到今天依然大量存在尤其是SD、MM模块的订单、采购、交货业务你绕不开它。1.3 为什么到了S/4HANA时代还在讲User Exit有人会问现在都S/4HANA了怎么还在讲老掉牙的User Exit答案很简单SAP把S/4HANA的核心业务进程重构了但大量标准程序里的调用点并没有删掉。比如销售订单处理中的MV45AFZZ在S/4HANA的销售事务里依然能看到USEREXIT_MOVE_FIELD_TO_VBAK、USEREXIT_SAVE_DOCUMENT这些熟悉的面孔。SAP在迁移时为了兼容老客户保留了这些出口的源码调用。当然也要清醒S/4HANA Cloud和ABAP Cloud开发模式下经典增强是被明确限制的SAP推荐用自定义业务对象、BAdI和应用内扩展如Key User Extensibility来替代。这恰恰说明只要还在做传统ECC或S/4HANA On-Premise项目User Exit/Customer Exit就是必备技能而理解它们的工作原理也是将来理解BAdI和隐式增强的基础。你把老门看懂了新门上手就快。2. User Exit与Customer Exit门框和门板的装配关系2.1 User Exit以FORM子程序为把手User Exit在代码层的形态是SAP标准程序里预留的FORM子程序调用。标准代码会写类似这种语句PERFORM USERE_XIT_SAVE_DOCUMENT.注意真实代码里没有下划线中间的那个怪分隔用户出口的命名统一是USEREXIT_开头例如USEREXIT_FIELD_MODIFICATION、USEREXIT_MOVE_FIELD_TO_VBAK。SAP把PERFORM调用的位置放在业务过程的固定时点比如字段移值前、保存前、保存后、打印时。你通过SMOD/CMOD激活对应增强后会进入一个客户预留的INCLUDE程序在里面的USEREXIT_xxx FORM中填入自己的处理逻辑。这里要特别理解一个关键点这些FORM不是凭空出现的SAP已经在客户INCLUDE里给出了空壳骨架你填的是骨架内部不需要也不应该修改PERFORM调用本身。因此就算你写出的代码有问题最坏情况是逻辑报错或结果不对但标准程序的结构不会被破坏升级受影响的范围被压到了最小。这就是“可控”二字的底层保证。常见的User Exit集中在SD销售订单MV45AFZZ包含字段修改、状态更新、保存前校验等十几处出口MM采购订单MM06EFSC包含抬头更新、行项目校验、收货触发等MM物料主数据MGA00001等交货单V50B0001、V50B0002等。每个增强内部有哪些具体的USEREXIT_xxx你打开SMOD组件列表一眼就能看到不用死记硬背。2.2 Customer Exit以函数模块为锁芯Customer Exit是User Exit的“同门师兄弟”但代码载体从FORM子程序变成了函数模块。标准程序中预埋的调用语句是CALL CUSTOMER-FUNCTION例如CALL CUSTOMER-FUNCTION 001 EXPORTING i_vbeln lv_vbeln IMPORTING e_result lv_result.CUSTOMER-FUNCTION后面的编号如001对应一个具体的增强组件。SAP在标准函数库里把这个函数模块的框架提前建好了函数模块名称通常以EXIT_开头例如EXIT_SAPL绝对的一串字符_001。客户通过CMOD激活后进入函数模块的源代码区域把处理逻辑写进去。Customer Exit和User Exit最大的差别在接口规范上。User Exit直接访问标准程序里的全局变量写起来方便但很容易“不知道上下文里那些变量到底被填到哪一步了”Customer Exit则是通过函数模块的导入、导出、表参数来传递数据接口清晰更适合需要传入大量结构化参数的场景逻辑也更容易单元测试。缺点是SAP预留的Customer Exit数量远少于User Exit而且触发点相对固定灵活度略低。2.3 两张门卡适用边界与选型判断我通常用一张简单表格帮团队做判断什么时候用User Exit什么时候用Customer Exit什么时候应该放弃经典增强对比维度User ExitCustomer Exit代码载体FORM子程序INCLUDE函数模块触发语句PERFORM USEREXIT_xxxCALL CUSTOMER-FUNCTION xxx参数传递直接访问程序全局变量显式导入/导出/表参数适合场景需求简单、直接处理业务字段结构化参数、可复用逻辑实现方式SMOD/CMOD进入INCLUDE编写CMOD进入函数模块实现升级影响低客户INCLUDE独立低函数模块独立如果SAP在目标程序里同时提供了User Exit和Customer Exit我优先看业务要访问的数据是全局变量还是结构化参数。只是改一个抬头字段User Exit就够了要做一套独立校验逻辑、需要传多个表时Customer Exit更干净。如果SAP没有提供任何经典出口或者同一需求要多套实现并存那就直接转向BAdI再往后还有增强点/隐式增强可用但那些需要更谨慎地评估升级影响。3. 找门从业务报错到增强点的定位链路3.1 最直接的一步SMOD里按增强名查接到一个增强需求我最先做的事情永远是去SMOD里验证“门是不是真的存在”。SMOD是SAP经典增强的组件管理工具里面按模块和业务场景整理了所有预置增强。如果你已经知道增强名比如销售订单的MV45AFZZ直接输入并回车组件列表会列出增强组件对应的程序名、INCLUDE名双击组件能看到该增强包含哪些USEREXIT_xxx或CUSTOMER-FUNCTION状态列显示该增强是否已经在某个项目中激活。如果不知道增强名SMOD本身也提供按“文本查找”的搜索功能你可以输入“销售订单”“交货”“采购订单”等关键字检索。但这个功能比较弱匹配的是增强描述文本命中率看运气。实战中我很少用它找具体名字更多是用它确认“门存在且未被占用”。3.2 必备技巧从报错消息反查出口这是老顾问最常用的“藏宝图”式定位法思路是从业务报错反查程序再在程序里找到预留出口。举个例子销售订单保存时弹出一个校验消息业务要求改掉这段校验的触发条件。处理步骤如下用SE91按错误文本搜索消息号比如消息类V8、消息号037双击消息查看它在哪些程序中发出SE38打开发消息的程序搜索该消息号定位到抛出消息的代码段在代码段附近往上翻查找PERFORM USEREXIT_xxx或CALL CUSTOMER-FUNCTION一旦找到出口再用SMOD确认它属于哪个增强组件走CMOD去实现。这个方法虽然要多花几分钟但准确率极高而且能让你顺带理清业务发生的上下文避免写出来的增强跑错时机。很多新顾问找不到增强点不是出口不存在而是他们直接搜关键词“USEREXIT”搜错了程序——销售订单的增强在SAPMV45A和它的INCLUDE链里但具体逻辑可能要在系统日志里先定位到实际执行的行号。3.3 怎么确认“这个门是否存在且可用”找到候选出口后别急着高兴至少要验证三件事增强组件是否已经在其他项目中被激活。如果已被占用你在新项目里重复添加会冲突此时要么并入原项目要么把原项目扩展纳入你的传输请求。该出口在S/4HANA版本里是否仍然有效。部分老增强因逻辑重构被废弃但SMOD里不会自动标注最稳妥的办法是在目标系统实际打开INCLUDE看是否有标准的代码骨架。是否有SAP标准代码本身已经填充了该USEREXIT。这种情况较少但一旦存在你加代码前必须先读懂SAP自己的逻辑避免覆盖或重复处理。这块建议在项目启动阶段的增强评估会上统一做一轮把要用到的出口提前在沙盒系统激活并跑通而不是需求上线前一周才开始找门否则很容易踩进“门存在但打不开”的坑。4. 开门实战销售订单增强从创建到激活全程4.1 CMOD新建项目并挂载增强组件确认了要用的增强组件这里用销售订单MV45AFZZ做演示接下来全部操作都在CMOD里完成。CMOD是经典增强的项目管理工具一个项目可以挂多个增强组件统一激活、统一传输。操作流事务码CMOD点击创建输入自定义项目名强烈建议用Z开头比如ZSD_SORDER_VALID填写项目描述说明这个增强解决什么问题切到“配置组件”页签也叫增强组件输入组件名MV45AFZZ回车保存项目生成传输请求。保存后两边状态会发生变化CMOD项目里能看到一个对象组件SMOD里MV45AFZZ的状态变为“已在项目中使用”。此时增强已经被“挂载”但还没有任何实际代码。4.2 编写USEREXIT代码的语法与上下文回到CMOD主界面双击项目切到“对象”页签你会看到增强组件MV45AFZZ下面列出了所有可编辑的出口对象比如ZXV45AU01这类客户INCLUDE。选中后点击“功能模块编辑器”或直接双击系统会打开ABAP编辑器里面预置了若干个USEREXIT_xxx的FORM空壳。以“销售订单保存前检查自定义信用标识”为场景最合适的出口是USEREXIT_SAVE_DOCUMENT。实现示意FORM userexit_save_document. DATA: lv_flag TYPE zzs_credit_flag. IF vbak-vbeln IS NOT INITIAL. SELECT SINGLE flag FROM zz_custom_check INTO lv_flag WHERE vbeln vbak-vbeln. IF sy-subrc 0 AND lv_flag X. MESSAGE e010(zsd_cust_msg). ENDIF. ENDIF. ENDFORM.这段代码有几个值得注意的细节。第一USEREXIT_SAVE_DOCUMENT虽然叫“保存文档”但在MV45AFZZ的上下文里执行时机是保存动作触发前适合做最终校验如果想在字段传递阶段改写屏幕值应该用USEREXIT_MOVE_FIELD_TO_VBAK或USEREXIT_MOVE_FIELD_TO_VBAP。第二直接访问的全局变量VBAK在出口执行时已经包含当前订单数据但如果你是要增强行项目级逻辑需要读取XVBAP内部表而不是直接FOR ALL ENTRIES再查库否则性能会很难看。第三消息类建议用自定义的Z开头的消息类不要混进标准消息类方便运维过滤和追溯。4.3 激活、测试、快速验证代码写完后保存并激活这个INCLUDE。不要忽略最后一步在CMOD界面点击“激活项目”让整个增强项目处于激活状态。激活后建议按以下顺序验证用SE38打开客户INCLUDE检查语法无误在前台事务里触发对应动作如VA01创建销售订单走一遍主流程如果没走到断点用SE38打开INCLUDE在你要调试的FORM里设置外部断点然后回到前台事务执行确认逻辑触发后检查数据是否正确落库以及是否有副作用影响后续标准逻辑。如果你的增强里有MESSAGE E类型的错误消息要特别注意消息弹出后的处理路径。SAP标准逻辑在USEREXIT_SAVE_DOCUMENT报错后通常会回滚当前保存操作但有些外部程序调用BAPI时不受消息对话框控制此时你可能需要额外检查返回参数否则会出现“界面不报错但数据没保存”的诡异现象。4.4 若组件是Customer Exit时怎么填Customer Exit的整个流程框架与User Exit一致只是最后编辑的不是INCLUDE里的FORM而是函数模块。在CMOD对象页签选中Customer Exit组件时双击进入的是函数模块界面系统会在函数模块的“源代码”区域里给出一个空实现FUNCTION EXIT_SAPL????_001. *---------------------------------------------------------------------- **本地接口 * IMPORTING * VALUE(I_VBELN) LIKE VBAK-VBELN * VALUE(I_POSNR) LIKE VBAP-POSNR * EXPORTING * VALUE(E_RESULT) TYPE CHAR1 *---------------------------------------------------------------------- ENDFUNCTION.你只需在注释区下方、ENDFUNCTION之前写业务逻辑。需要注意Customer Exit的函数模块通常没有直接的全局变量可以访问必须依靠导入参数、导出参数以及全局内存如SAP Memory/ABAP Memory来做数据交互。写完后同样要保存激活函数模块、激活整个项目。函数模块的调试比INCLUDE更方便可以单步跟踪导入导出值出问题也更好定位。5. 门后的质量关卡激活、调试、传输与升级避坑5.1 代码不生效的排查链路“我都写好了为什么跑不到”这是我被问得最多的问题。遇到这种情况不要瞎猜按下面这条链路一步步排查第一步确认激活状态。CMOD里项目是否已激活激活日志有没有报错第二步确认系统。当前登录系统是否已传输了项目请求很多项目沙盒到QA之间请求没推过去代码当然不生效第三步确认断点。在INCLUDE或函数模块里设外部断点重新执行前台事务。断点没触发说明该出口在这个路径上根本没被调用要么找错方向要么该需求有另一个更合适的出口第四步确认前置条件。例如销售订单中有些USEREXIT只有特定流程类型才触发检查事务变式、销售范围、项目类别等条件是否满足。这套链路里最容易误导人的是第一步。CMOD项目的激活状态和INCLUDE的激活状态是两回事INCLUDE保存自动激活但项目里如果新增了组件必须额外执行一次“激活项目”才算数。有时候INCLUDE的代码已释放但项目本身是未激活状态前台就可以完全忽略你的增强。5.2 增强代码的调试与变量保护实际调试User Exit时最容易踩的坑是全局变量状态不明。比如你直接读取VBAK-VBELN但如果当前屏幕刚进入创建模式抬头还没有分配到内部号码这个字段就是空的。建议在调试器里把调用栈打开查看该USEREXIT是处于“数据收集”阶段还是“保存”阶段再决定代码逻辑依赖哪些字段。另一个常见的坑是在增强里改动标准全局变量的值导致后续标准逻辑出现连锁反应。EU安全意识强的团队会规定User Exit中尽量只读全局数据要写数据时优先用本地变量再通过消息、表或内存传给后续程序。如果实在要修改标准字段必须有注释说明修改原因和影响范围代码评审时重点审查。5.3 传输与命名空间让门在项目组里可控User Exit和Customer Exit的对象一般落在SB包或登录对象名下客户自己的代码通过CMOD写入后会分散在客户INCLUDE和函数模块里。为了保证整个项目的可控性我习惯立几条规矩所有CMOD项目名以Z开头描述写清楚业务单据和目的增强内所有自定义表、结构、数据元素统一走Z命名空间不允许在增强里直接引用生产系统里的临时表代码注释头必须以“项目、日期、作者、需求描述”为标准格式每个请求独立释放防止对象被别人误拉进无关传输。这些看起来是流程规范但在项目维护阶段帮过大忙。有一次客户上线一年后问某个校验逻辑是谁加的我让他查CMOD项目清单三分钟定位到了人和需求如果当时随手写在标准程序里这个溯源成本会高到难以想象。5.4 升级与S/4HANA这些经典增强会消失吗关于升级最核心的一点是客户写在ZX INCLUDE或Customer Exit函数模块里的代码升级时通常不会丢。因为SAP升级覆盖的是标准对象而客户代码存放在独立对象里不在标准交付物范围内。但这不代表零风险标准程序里PERFORM调用的位置、传入参数的变量名如果被SAP重构你的FORM接口理论上也可能对不上。常见的情况是SAP在升级中引入了新的出口原出口仍然保留但建议迁移到新出口。S/4HANA环境里我建议每个经典增强都过一遍Finely Entry扫描把所有User Exit/Customer Exit按业务使用频度排个清单优先把活跃度高、技术债务重的增强迁移到BAdI或增强点实现。这个动作不用在升级前全部做完但至少要在升级后的两个月内做一轮回归确认人脸识别功能正常、数据一致性和性能没有明显下降。无论技术怎么换代“可控的门”这个原则不会变标准逻辑保持原样客户逻辑放在标准认可的扩展点里升级时SAP管标准你管自己的门。做了这么多年SAP增强我越来越觉得User Exit和Customer Exit不是什么神秘技术它们就是SAP留给顾问和客户的一条体面通道。每次遇到想直接改标准程序的开发我都会讲一遍当年那个新人改SAPMV45A的故事然后提醒他开对门比把墙砸了再补要省太多心。最后分享一个让我印象很深的小教训曾经有个增强在测试环境一切正常上生产后偶尔报错排查到最后是生产系统的函数模块版本没激活运输请求里根本没有包含它。从那以后我每次上线前都会强制检查一遍对象列表绝不再信“代码应该在请求里”这种默认假设。技术会更新但严谨的态度才是这行最大的护城河。