ARTICLE DETAIL

资讯详情

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

SAP财务凭证增强:VF01/MIRO利润中心自动填充的AC_DOCUMENT实践

SAP财务凭证增强:VF01/MIRO利润中心自动填充的AC_DOCUMENT实践 做SAP财务集成这么多年每次遇到销售发票过账要自动带利润中心采购发票校验要按物料回填成本中心这类需求第一反应都是先去看OBBH配置凭证替代但往往最后还是要绕到AC_DOCUMENT这个BADI上。这篇文章就把我最近帮一个项目处理VF01和MIRO凭证替代的完整过程写出来包含标准替代的配置步骤、AC_DOCUMENT的完整ABAP代码、两个事务码场景下的坑点以及上线前必须检查的事项。内容尽量保姆级照着做基本能跑通。1. 先想明白VF01/MIRO的过账链路上替代和BADI分别卡在哪个环节1.1 两个事务码过账时到底发生了什么先说VF01。VF01创建销售发票保存时系统会对销售凭证做会计凭证生成。这个过程的触发点其实在SD模块系统从VBRK/VBRP这些销售票据表里抽数据然后通过财务会计接口生成BKPF/BSEG。也就是说VF01的过账动作不是简单的F-02手工记账它是带着SD上下文的后面无论是做凭证替代还是做BADI增强你都能拿到销售发票号、客户、销售订单号这些信息。MIRO的过账链路更复杂一些。发票校验时系统要处理采购订单、收货凭证、GR/IR清算科目生成主凭证的同时可能还会生成后续的会计凭证比如差异凭证、税凭证。所以MIRO场景下AC_DOCUMENT可能不是只调用一次你需要格外注意主凭证和后续凭证的区别。问题也出在这里VF01和MIRO的财务凭证最终都会落到BKPF/BSEG但它们生成凭证的上下文完全不一样。标准替代之所以经常搞不定是因为替代规则引擎是基于字段条件值赋值工作的它没有数据库查询能力没有循环能力更没法干先按物料号汇总再回填大批行项目这种活。1.2 标准凭证替代能做什么、不能做什么OBBH配置的标准凭证替代适合做很朴素的规则。比如公司代码1000的凭证利润中心为空时填P1000这种基于固定字段条件的赋值配置起来很快。它也能做一部分查表比如从物料主数据里取利润中心但实现起来很别扭。我之前遇到过一个需求VF01过账时如果行项目没有利润中心要根据客户主数据的销售利润中心字段来填充。这种逻辑在标准替代里写起来很痛苦因为替代条件编辑器里根本不支持你直接JOIN一张表。你只能通过替代里的公式字段或者函数绕上好几个弯才能实现。MIRO场景更麻烦。发票校验的行项目来源是采购订单很多客户希望利润中心从EKPO采购订单行项目带入但替代的执行点在财务凭证生成过程中这个时候你再通过替代规则去读EKPO基本没戏。所以我的经验是标准替代适合做兜底规则复杂动态逻辑一定要交给AC_DOCUMENT BADI。1.3 AC_DOCUMENT在调用链中的精确位置AC_DOCUMENT这个BADI严格说是财务会计凭证生成前的最后一个增强点。它的执行顺序在标准替代之后在凭证落库之前。这意味着两件事第一你在BADI里修改行项目字段改的是真正用来过账的临时数据最终凭证会按照修改后的内容生成。第二就算标准替代没有触发AC_DOCUMENT也能正常工作。它们是两条并行的增强路径互不依赖。这一点很多人容易误解。另外需要注意AC_DOCUMENT的调用点是所有通过财务会计接口生成凭证的入口也就是说不只VF01/MIROFB50、F-02这类手工记账以及CO模块内部过账只要走同一套凭证接口都会触发。所以在写代码时一定要先做来源判断把处理范围锁死在你关心的VF01和MIRO上否则你会影响其他所有过账逻辑。2. 先配一层标准替代兜底OBBHGGB0操作全流程2.1 打开OBBH找到VF01和MIRO对应的应用区域进入事务码OBBH你会看到一个应用区域列表。这里面不是直接列VF01MIRO这样的字眼而是分模块的应用区域代码。VF01对应的通常是VSD销售凭证相关的过账MIRO对应的是MMM物料管理相关的过账。我第一次配的时候在这里愣了很久因为OBBH界面默认不会告诉你哪个区域对应哪个事务码只能靠经验和调试去确认。一个笨办法先在所有区域都挂一个最简单的替代规则然后分别用VF01和MIRO过账测试看哪个区域被触发。实测下来VF01走的是V区域MIRO走的是M区域但不同版本可能存在差异建议你在自己的系统里验证一下。选中应用区域后点工具栏上的替代按钮注意不要点成验证。验证是Validation做的是检查逻辑替代才是Substitution做的是字段赋值两者入口紧挨着非常容易点错。进去之后系统会跳到GGB0的规则维护界面。2.2 在GGB0里创建替代步骤与规则进入GGB0后界面是分步骤的每一步包含条件和动作两部分。操作路径是在步骤维护界面新建一个步骤步骤号通常从10开始给一个清晰描述例如销售发票利润中心兜底。选中步骤双击进入条件维护。这里的条件是指什么情况执行该步骤。设置完条件后在右侧的动作列表里定义赋值例如利润中心PRCTR等于P1000或者从某个公式/参数里取值。保存并激活。有一点要提醒替代规则的条件里判断字段为空例如利润中心 IS INITIAL这种写法不同版本的规则编辑器支持程度不一样。如果编辑器不接受初始值这种条件我通常的做法是在动作里用公式或者写两条互补条件。但更实用的建议是简单条件在GGB0做涉及空值判断和查表回填的都丢给BADI不要在规则引擎里死磕语法。2.3 验证基础字段替代把它当成一个兜底而不是主方案配置完成后一定要先用测试数据过一遍。VF01建一张测试销售发票保存过账到FB03里去查看会计凭证确认标准替代已经生效。MIRO也同理做一笔测试发票校验过账检查凭证行项目。这个验证过程会暴露很多问题。比如你发现替代没生效先检查这几个位置OBBH应用区域里规则名称是否已经正确分配GGB0规则是否处于激活状态新建规则经常忘记激活过账时调用的是不是同一个应用区域我遇到过一种情况规则激活了VF01过账也带入了利润中心但MIRO死活不触发。后来发现MIRO过账时用的是BAPI方式应用区域的调用点跟VF01不一样。这再次说明标准替代在很多复杂场景下不可靠。因此我的建议是标准替代只作为兜底核心逻辑放到AC_DOCUMENT里这样开发可控性最强。3. AC_DOCUMENT BADI完整代码从建实施到能跑通3.1 创建BADI实施SE19还是直接建类都行AC_DOCUMENT这个BADI传统做法是事务码SE19输入BADI名称AC_DOCUMENT然后创建实施。如果你用的是Eclipse/ADT也可以直接创建ABAP类实现接口IF_EX_AC_DOCUMENT然后在SE19里把实施类关联进去。两种方式都行我习惯用SE19因为激活状态一目了然。还有一个关键点创建实施时系统会让你填写一个实施编号这个编号决定了多个BADI实施之间的执行顺序。如果系统里已经有其他开发做过了这个BADI的增强你需要确认自己的实施是在它的前面还是后面顺序不同结果可能不同特别是多个开发都对同一字段赋值时后执行的会覆盖先执行的。3.2 方法分工CHANGE_INITIATOR、CHANGE_ITEM是主力AC_DOCUMENT这个BADI有四个方法CHANGE_INITIATOR处理凭证抬头数据。比如你可以在这里改抬头文本BKTXT、参考凭证号XBLNR这些字段也可以在这里保存一些上下文数据供后面行项目方法使用。CHANGE_ITEM处理行项目数据。这是最常用的方法利润中心、成本中心、自定义字段的填充都写在这里。CHANGE_NUMBER改变凭证编号一般不用。CHANGE_PORTION处理行项目里的部分数据比如税和金额拆分极少数情况才会碰。需要提醒你的是就算你只改行项目数据也必须把接口的所有方法都在实现类里写出来空的也行否则类激活会报错。3.3 完整代码示例按来源自动填充利润中心下面这段代码是我在项目里实际用过的逻辑稍作脱敏和简化。场景是VF01和MIRO过账时如果行项目里没有利润中心则根据物料号从自定义配置表ZFI_MAT_PRCTR里取利润中心回填。CLASS zcl_badi_ac_document DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES if_ex_ac_document . PROTECTED SECTION. PRIVATE SECTION. DATA: gv_awtyp TYPE awtyp, gv_awkey TYPE awkey, gv_tcode TYPE sytcode. METHODS check_source IMPORTING p_initiator TYPE acc_initiator. METHODS fill_profit_center CHANGING ct_items TYPE acc_items RETURNING VALUE(rv_changed) TYPE abap_bool. ENDCLASS. CLASS zcl_badi_ac_document IMPLEMENTATION. METHOD check_source. gv_awtyp p_initiator-awtyp. gv_awkey p_initiator-awkey. gv_tcode p_initiator-tcode. ENDMETHOD. METHOD if_ex_ac_document~change_initiator. check_source( p_initiator ). ENDMETHOD. METHOD if_ex_ac_document~change_item. DATA: lv_changed TYPE abap_bool. 只处理销售发票和发票校验其他来源直接跳过 IF gv_awtyp VBRK AND gv_awtyp RBKP. RETURN. ENDIF. lv_changed fill_profit_center( CHANGING ct_items c_items ). ENDMETHOD. METHOD fill_profit_center. DATA: ls_item TYPE accit, lt_material TYPE SORTED TABLE OF matnr WITH UNIQUE KEY table_line, lt_mapping TYPE TABLE OF zfi_mat_prctr, ls_mapping TYPE zfi_mat_prctr, lv_matnr TYPE matnr, lv_index LIKE sy-tabix. 第一遍收集缺少利润中心的物料号 LOOP AT ct_items INTO ls_item. IF ls_item-prctr IS INITIAL AND ls_item-matnr IS NOT INITIAL. COLLECT ls_item-matnr INTO lt_material. ENDIF. ENDLOOP. IF lt_material IS INITIAL. RETURN. ENDIF. 批量读取配置表避免一行一个SELECT SELECT * FROM zfi_mat_prctr INTO TABLE lt_mapping FOR ALL ENTRIES IN lt_material WHERE matnr lt_material-table_line. SORT lt_mapping BY matnr. 第二遍回填利润中心 LOOP AT ct_items INTO ls_item. IF ls_item-prctr IS INITIAL AND ls_item-matnr IS NOT INITIAL. READ TABLE lt_mapping INTO ls_mapping WITH KEY matnr ls_item-matnr BINARY SEARCH. IF sy-subrc 0. ls_item-prctr ls_mapping-prctr. MODIFY ct_items FROM ls_item INDEX sy-tabix. rv_changed abap_true. ENDIF. ENDIF. ENDLOOP. ENDMETHOD. ENDCLASS.这段代码的核心思路是先收集所有缺利润中心的物料号一次批量查配置表然后再循环覆盖回c_items。这样几千行的发票凭证也不会卡性能。3.4 代码里最容易踩的坑逐个说明第一接口方法里的参数名和类型一定要以你系统里SE18显示为准。不同S4版本ACC_ITEMS的具体结构可能不完全一样但基本都能用accit直接访问字段。我上面写的c_items和p_initiator在ECC和大部分S4版本里都没问题。第二修改c_items后别忘了用MODIFY。很多人写出LOOP AT ct_items INTO ls_item然后改了ls_item最后忘了写MODIFY ct_items结果改了个寂寞。第三改行项目时注意区分税务行、差异行这类特殊行。上面代码用物料号为空来排除了一部分但不够严谨。如果你想做得更稳可以用ls_item-ktosl或者ls_item-shkzg结合来判断。MIRO里税行的物料号一般是空的GR/IR差异行也通常没有物料号所以这个判断在大多数场景下够用。第四如果你在前台的调试器里看到了BADI但修改没生效多半是后面还有别的逻辑覆盖了你的赋值。比如CO模块的利润中心默认规则在你之后又重新算了一遍利润中心。这种情况只能逐步排查调用链必要时把BADI实施的排序调到更靠后。4. VF01和MIRO的差异怎么避免增强只在一个事务里生效4.1 判断从哪来用TCODE还是AWTYP更靠谱你可能会想既然只处理VF01和MIRO那直接判断SY-TCODE不就行了但实际使用中有一个风险VF01过账时SY-TCODE大概率是VF01但如果用户是通过BAPI或者第三方接口创建销售发票SY-TCODE可能为空或者完全不同的值。MIRO也有类似问题。更稳的判断是用p_initiator-awtyp它是参考交易类型从业务单据的类型来区分。VF01生成的财务凭证参考交易类型通常是VBRKMIRO生成的通常是RBKP。你可以先不写代码直接在调试器里把p_initiator展开看看awtyp到底传的什么值再回代码里写CASE。我的习惯是两者结合先用AWTYP做主干区分再用TCODE做辅助判断。这样既覆盖手工过账也覆盖接口过账。4.2 VF01场景利润中心从哪取取决于你项目的业务规则VF01销售发票的行项目很多时候可以从销售凭证本身带出利润中心。比如VBRP销售发票行项目里可能有利润中心字段或者VBAK/VBAP销售订单里有。如果这些地方已经维护了利润中心你的代码逻辑就可以写得很简单从参考销售凭证号反查VBRP取利润中心回填。不过要注意AWKEY字段在不同来源下格式不一样。有时候是公司代码凭证编号年度有时候就直接是销售凭证编号。所以代码里解析AWKEY之前一定要先在调试器里看一次AWKEY的实际内容再决定用哪一段做查询键值。如果客户主数据或者单据里根本没有利润中心那就只能走你自定义的映射规则比如按物料或工厂去配置表里查这就是我上面代码示例的做法。4.3 MIRO场景税行和GR/IR差异行的处理要当心MIRO发票校验最烦的是行项目类型混杂。主凭证里既有物料行也有费用行还有税行。如果你一股脑把所有缺利润中心的行都回填很可能会把税行也塞进一个无关的利润中心导致后续报表对不上。我的处理策略是对税行直接跳过。判断方式可以用ls_item-ktosl也可以在发票校验的数据结构里看税码确保利润中心只补到真正的业务行上。另外MIRO如果有GR/IR差异比如收货和发票金额不一致产生的差异凭证行怎么办这种行可以设置独立的利润中心规则或者允许为空具体要问财务。不要想当然地用一个物料映射去覆盖所有行。4.4 调试技巧如何准确看到BADI是否被调用调试AC_DOCUMENT的方法很简单在CHANGE_ITEM方法里打断点然后用VF01或MIRO做一笔测试过账。VF01保存过账、MIRO保存过账的时候系统就会命中断点。不过要注意一点VF01在创建发票界面点击保存后断点才会触发不要老盯着初始界面。MIRO同理要先把发票校验数据维护完整点保存断点才会出现。如果你发现过了账也没进断点先检查三件事BADI实施是否激活。去SE19查看AC_DOCUMENT的实施状态没激活等于白改。实施是否传到了当前系统。开发机里能激活质量机或生产机如果没传自然不触发。系统是否走的是完全不同的凭证创建逻辑。极少数情况下业务代码会绕过标准凭证接口。也可以用SATABAP运行时分析跟踪一下过账过程中的函数调用确认到底有没有走AC_DOCUMENT这一段。5. 上线前必须盯住的坑从激活状态到性能与传输5.1 替代和BADI都没触发怎么办上线前最怕遇到明明配置了测试也过了生产上就是没生效。这类问题九成出在传输和激活状态。OBBH和GGB0里的配置属于IMG配置要用配置传输请求传送到目标系统。BADI实施属于ABAP开发对象要用工作台请求传输。这两者在传输列表里是分开的别只传了一个。到了目标系统后还要执行激活操作。BADI实施传输后不一定自动激活需要去SE19里重新激活或者用业务配置激活工具。以前我吃过一次亏配置传了开发类传了但生产上SE19里的实施状态是未激活结果业务过账一点反应都没有。从那以后我养成了习惯传完后第一时间去SE19确认实施状态。5.2 BADI改了字段但最终凭证却没变化这种问题排查起来最费劲。最常见的原因有三个改了C_ACCOUNTING_DOC抬头但没同步改行项目或者反向。实际上像利润中心这种字段一定在行项目层抬头改了没用。你在循环里改了ls_item但最后MODIFY时忘了指定INDEX或者FROM ls_item。这是ABAP新手最容易犯的错。你的赋值被后面的逻辑覆盖了。比如CO模块的利润中心派生规则在你之后又跑了一遍。这种情况可以通过调整BADI实施顺序或者增加调试日志来确认。还有一个很隐蔽的问题如果BADI里同时修改了多个字段而其中某个字段触发了后续的校验逻辑系统可能报错或者回滚最终你看到的凭证仍然没有修改。这时候需要检查过账日志和错误消息。5.3 性能问题行项目循环里的SELECT是杀手写AC_DOCUMENT代码时最怕在LOOP里写SELECT。财务凭证过账本身就是一个高频动作如果你每过一张凭证、每个行项目都去查一次表高峰期CPU会很难看。上面的示例已经演示了正确做法先收集物料号再用FOR ALL ENTRIES一次性查表最后循环回填。这是AC_DOCUMENT性能优化里最重要的一条。另外你能使用的数据库表要尽量小。比如自定义配置表ZFI_MAT_PRCTR字段能精简就精简索引一定要建好。否则FOR ALL ENTRIES也救不了你。5.4 传输请求和跨系统一致性清单上线前我建议按这个清单逐项确认GGB0里建的替代规则是否已经在OBBH对应应用区域上分配。替代规则和BADI代码是否在同一个传输请求或关联请求中。SE19中AC_DOCUMENT实施是否处于激活状态。用FB03抽查VF01和MIRO过账后的凭证确认利润中心、成本中心等字段都正确带出。检查是否有多个BADI实施确认执行顺序符合预期。如果还有后续的凭证比如MIRO的差异凭证也要一并验证。特别是最后一条很多人只看主凭证忘记检查关联凭证导致上线后报表里一部分行项目利润中心为空。从我自己的经验看AC_DOCUMENT算是SAP财务增强里比较厚重的一个口子功能全、权限大但责任也大。每次改完它我都会提醒自己多跑几轮真实业务场景的回归测试尤其要注意是否会误伤到FB50这类手工记账的凭证。毕竟这个BADI一旦激活就是个全局的入口宁可代码里多写几个IF判断也不要让风险范围内扩大到不可控的地步。
返回列表