
聊一个我在项目里被反复问过的问题公司间STO流程发货工厂那边外向交货单做完PGI发货过账之后收货工厂能不能自动生成内向交货单很多企业做内部工厂间调拨时都希望把“到货预告”这件事自动化让收货仓库不用手工建单、不用反复电话问物流系统直接按照发货结果把内向交货单带出来。这个需求在SAP里能做而且不止一种做法但每一条路都有它的前提和坑。这篇文章我把实际操作中验证过的方案、配置路径、增强代码和排错经验整理出来给正在做STO项目的顾问和内部IT一个可落地的参考。这个场景适合谁如果你正在做MM/SD集成、公司间STO两步法的项目或者你被业务方问过“为什么PGI之后没有自动生成内向交货单”那这篇文章可以直接解决你的疑问。内容分为方案选型、标准配置、增强实现和问题排查四个部分既有原理也有实操步骤照着做基本不会跑偏。1. 先弄懂STO两步法里“外向/内向交货单”的角色1.1 一步法和两步法的本质差异STOStock Transport Order在SAP里的本质是一张采购订单单据类型通常叫UB但具体的后台配置可以自定义。很多顾问一上来就谈“自动创建内向交货单”结果忽略了最基础的一个问题你用的到底是STO一步法还是两步法这两者在库存转移逻辑上完全不同决定了内向交货单到底有没有存在的意义。一步法的逻辑是发货工厂做PGI的时候收货工厂的库存直接增加系统自动生成一张收货凭证中间不存在“在途库存”这个状态。这种模式下根本没有内向交货单的概念因为物权和实物几乎同步转移收货端不需要预告也不需要库存占用的过程管理。一步法适合距离近、运输周期短、管理颗粒度粗的调拨场景比如同一园区内两个库房互调。两步法则不同发货工厂PGI后库存先进入“在途库存”Stock in Transit收货工厂必须再做一次101收货库存才变成可用的非限制库存。在这个过程里系统是可选项配置内向交货单的。如果配了收货工厂可以基于内向交货单做收货仓库能提前看到货量、物料清单和预计到达时间也能跟WM/EWM联动做上架策略。这才是大家口中“STO自动触发内向交货单”的真正含义。所以做这个需求之前第一件事是去确认STO是两步法。怎么看SPRO路径物料管理 - 采购 - 采购订单 - 设置库存调拨单 - 定义工厂间/公司间采购订单的文档类型。在UB类型里维护“发货工厂/收货工厂”两步法标识。表格整理如下对比维度一步法两步法无内向交货单两步法有内向交货单PGI后库存状态收货方直接可用在途库存在途库存收货操作自动过账MIGO 101手工收货基于内向交货单收货是否需要内向交货单不需要不需要需要库存追溯清晰度一般较清晰最清晰适合场景同园区短途调拨跨工厂常规调拨管理要求高、配合WM1.2 为什么业务非要“PGI后自动触发”理解了上面的流程你就能明白业务方的真实诉求了。他们不是真的关心内向交货单这个单据本身而是关心仓库能不能提前知道“有一车货要从A工厂发到B工厂”。如果没有自动触发机制收货工厂的仓管员每天要在系统里手工查状态或者靠微信群问再手动VL31N建单效率低、易漏单而且一旦漏建收货环节就没法跟WM打通上架任务全部手工补。我遇到过最典型的一个场景客户在两个工厂之间跑STO每天几十车收货仓库用EWM做仓位管理。上线初期没用自动触发结果仓管员每天下午四点开始集中补建内向交货单高峰期一天补几十张错单漏单频发。后来我们做了自动创建之后PGI一过账内向交货单自动出现在收发货台EWM的入库任务直接生成仓管只需要扫码收货效率提升非常明显。总结一句话自动触发内向交货单解决的不是“建单”这个动作而是“从发运到入库”这段信息链路的自动化。它让发货结果直接驱动收货准备减少人为干预这是整个需求的核心价值。2. 方案选型三条实现路径怎么挑2.1 三条路径的对比SAP里实现“PGI后自动触发内向交货单”的常见路径有三条我把它们的本质拆开讲清楚。第一条是运输单Shipment联动。它的逻辑是发货工厂PGI之后运输团队在VT02N里创建一张运输单把外向交货单挂到运输单上再确认“内向交货”标记系统据此生成内向交货单。这条路径的本质是“人确认车次后系统生成单据”自动化程度中等。第二条是输出控制Output Determination。SAP任何业务单据都有输出控制机制外向交货单PGI时会触发输出类型的确定系统找到条件记录后执行对应的处理例程。这个处理例程可以是一个Function Module也可以是发送IDoc。在标准场景里通过配置可以做到PGI后自动把外向交货单信息发给收货方系统收货方系统自动创建内向交货单。这条路径能实现真正的“无人值守”。第三条是BTE或者BADI增强。BTEBusiness Transaction Event是SAP标准的业务事件接口其中事件00001070对应“外向交货单过账后”的时点我们可以挂一个自定义函数在函数里判断单据类型、检查是否已建单、调用BAPI创建内向交货单。这条路径最灵活适合有复杂业务规则或不想买运输模块的场景。对比表格如下实现路径自动化程度配置复杂度是否需要开发适合场景运输单联动中低否有运输团队、按车次管理输出控制高中视情况需要同系统或跨系统自动建单BTE/增强高中高是有定制规则、需完全自主控制2.2 客户场景与方案匹配建议在我做过的项目里选型从来不是技术越牛越好而是看客户的业务形态。如果客户有专门的运输管理岗位每天发车前要安排车次、打印装运单那就用运输单联动业务上顺理成章。如果客户希望发货过账后收货端自动出现单据不想让人为确认卡一道那就选输出控制或BTE。还有一个判断维度SAP系统是一套还是多套。公司间STO经常出现两个公司代码在不同系统的情况这时候跨系统传信息要用IDoc/ALE输出控制方案更标准因为它天然支持通过消息类型比如DESADV把交货数据发到对方系统。如果是同一套系统内部两个工厂调拨则完全不需要IDoc直接建一个处理例程函数调用BAPI就行。我的建议很直白不要一上来就写增强。先看运输单方案能不能满足再看输出控制实在业务规则特殊、标准方案接不住才动BTE。因为增强代码上线后总要有人维护业务一变代码就得跟着变尽量用标准配置解决问题。3. 标准方案一运输单Shipment联动生成内向交货单3.1 前置后台配置运输单联动方案的核心是“外向交货单类型 - 运输单 - 内向交货单”这条链路。既然要生成内向交货单前提是外向交货单类型里已经启用了内向交货相关的参数。配置路径在SPRO里这样找物流执行 - 装运 - 交货 - 定义交货类型。找到你的STO外向交货单类型常见是NLCC或客户自定义的Z开头类型双击进去检查是否允许后续内向交货。然后定义内向交货单类型同样在“定义交货类型”里维护标准通常用NL或者EL。内向交货单类型的参数重点是仓库负责人、卸货点、库存位置确定规则。这些字段决定了一张内向交货单生成后系统能不能正确找到收货工厂的库位。还有一个容易漏的配置位置确定Location Determination。路径物流执行 - 装运 - 基本装运功能 - 位置确定 - 定义位置确定。这里需要维护发货工厂到收货工厂的对应关系系统创建内向交货单的时候要根据这个映射去找卸货点和收货库位。很多项目上线初期建单报错“找不到卸货点”十有八九就是这里没配。3.2 前台操作多个STO合并交货的场景这个方案的前台操作流程我按实际场景走一遍。业务上经常有“多个STO订单凑一辆车”的情况这也是很多项目里被问得最多的问题——多个STO的外向交货单怎么合并生成内向交货单第一步发货工厂对外向交货单做PGI事务代码VL02N。注意PGI这一步不能省运输单联动是基于已过账的交货单来进行的。第二步事务代码VT02N创建或者修改运输单把需要合并的外向交货单一张一张添加进去。第三步进入运输单的“交货”相关界面勾选内向交货标识。保存之后系统会为运输单关联的外向交货单生成对应的内向交货单。这里有一个关键点要跟业务确认清楚合并的运输单生成内向交货单时是每个外向交货单单独生成一张内向交货单还是合并成一张这个取决于后台配置和外向交货单的分组规则。标准行为通常按外向交货单分别生成如果你希望合并成一张就要调整内向交货单的单据类型里面的分组字段。上线前一定要和仓库确认收货习惯他们想一车一单还是一票一单这个决定直接影响上架效率。3.3 为什么这个方案适合“有运输团队”的企业运输单方案最大的好处是业务流程完整有人对车次负责系统里能看到一车货从发货、在途到收货的完整闭环。如果客户本来就要做运输管理比如要打印装运单、跟踪运费、做在途状态管理那这个方案几乎零额外成本顺手把内向交货单也带出来了。但它有一个明显短板依赖人工确认。要是运输团队忘了勾选内向交货标识或者压根忘了建运输单内向交货单就不会生成。这个不是系统Bug是流程断点。所以选择这个方案的话必须在业务流程里把“建运输单并确认内向交货”设为发车前的强制步骤最好是跟装运单打印绑定不确认不让打印人就不会漏了。4. 标准方案二输出控制自动触发同系统和跨系统都通用4.1 输出类型与处理例程配置输出控制方案可以说是SAP里最正统的“自动触发”实现。原理不复杂外向交货单PGI后系统按输出确定逻辑去匹配条件记录找到对应的输出类型然后执行这个输出类型的处理例程。处理例程可以是一个Function Module直接在当前系统里创建内向交货单也可以是一条IDoc发送逻辑把数据传到另一个系统去做处理。先说配置步骤。第一步定义输出类型。事务代码NACE选择“外向交货单”的应用场景创建一个Z开头的输出类型比如ZDLV。在输出类型的“处理例程”字段里维护要调用的函数名。如果同系统自动建单可以写一个自定义的Function Module我们项目里通常叫Z_CREATE_INBOUND_DELIVERY里面封装BAPI调用。第二步配置输出确定过程。路径IMG - 物流执行 - 装运 - 基本装运功能 - 输出控制 - 输出确定 - 使用条件技术维护交货的输出确定。把输出类型放到确定过程里定义访问顺序。访问顺序就是条件表的查找顺序比如先按“发货工厂收货工厂”查再按“交货类型”查最后按通用条件查。第三步维护条件记录。前台事务代码VV31或者VV11/VV12按条件组合创建输出条件记录。比如在“发货工厂1000 - 收货工厂2000”这个组合下建一条ZDLV的输出记录指定处理例程参数。4.2 PGI后系统到底做了什么很多顾问只知道配置不理解系统在PGI那一刻做了什么导致出了问题无从下手。我这里把触发链路拆开讲。当VL02N执行PGI时交货单状态更新系统自动触发输出确定Output Determination。它读取交货单的数据按照访问顺序去条件表里找匹配的输出记录。找到之后把输出行写入输出日志并执行处理例程。处理例程如果是标准发送会把数据打包成IDoc发送给接收方系统如果是自定义函数则直接把外向交货单的关键字段传给函数。所以判断问题出在哪一环核心是查输出日志。事务代码VL06E可以查看外向交货单的输出记录和处理状态。如果输出行是红色、报错那大概率是条件记录没找到或者处理例程里的函数执行失败。如果输出行已经处理成功但内向交货单没建出来那就是接收端的逻辑有问题。4.3 同系统与跨系统的关键差异同系统场景两个工厂在同一个SAP实例里方案比较轻量输出处理例程直接调用BAPI创建内向交货单不需要IDoc和端口配置。跨系统场景不同SAP实例就需要通过ALE/IDoc传递数据比如标准消息类型DESADVDespatch Advice接收方系统收到IDoc后通过配置的流程代码自动创建内向交货单。跨系统要做的配置就多了一层发送方的端口Port、伙伴参数文件Partner Profile、接收方的伙伴参数和消息控制。别小看这一层很多项目死在这上面。你要先确认IDoc能正常发出、能和接收方连通再谈自动建单。否则排错的时候一会儿查输出一会儿查IDoc很容易晕。另外提醒一点客户名下的两个公司可能是不同的SAP系统也可能业务上叫“公司间”但实际在同一套系统。开始动手前先确认系统架构方案规划完全不同。同系统多工厂优先考虑同系统函数处理跨系统尽量走标准IDoc消息。5. 增强方案BTE ABAP 实现完全自主控制5.1 激活BTE事件00001070如果标准方案满足不了复杂业务或者客户希望完全按照自己的规则来创建内向交货单那就用增强。我推荐用BTE而不是直接在标准程序里加隐式增强因为BTE是SAP官方开放的业务事件接口升级相对安全而且有事务代码可以管理。BTE叫Business Transaction Events对应事务代码FIBF。我们关心的事件是00001070事件描述大概意思是“外向交货单过账/发货Delivery: goods issue”。它的触发时点正好是PGI之后非常契合需求。激活步骤是这样的SE38运行程序SXIFIBF或者直接FIBF。在BTE维护界面里找到“Process”标签页查找到00001070事件。事件处于激活状态后把你要调用的函数模块名称填到“模块调用”对应的位置比如Z_STO_CREATE_INBOUND_DELIVERY。保存后系统会在PGI过账结束时自动调用这个函数模块。写函数的时候有两点要注意。第一事件传入的参数是表不是单条结构因为一次PGI可能处理多张交货单第二函数里对数据库的更新操作要控制好提交时机别在标准程序还没提交的时候单独提交容易造成数据不一致。我习惯在BAPI调用成功后再单独COMMIT。5.2 ABAP实现思路与代码示例函数模块的核心逻辑分四步判断单据类型、检查是否已建单、准备BAPI参数、调用BAPI创建内向交货单。下面是一段简化但结构完整的ABAP代码示例实际项目里根据公司代码、工厂和组织结构做微调就能用。FUNCTION z_sto_create_inbound_delivery. *-------------------------------------------------------------------- * 通过BTE 00001070在PGI后自动创建内向交货单 *-------------------------------------------------------------------- TYPES: BEGIN OF ty_xvbrk, vbeln TYPE vbeln_vl, 外向交货单号 vgtyp TYPE vbtyp, 前续单据类型 vgbel TYPE vbeln, 前续单据号 vstel TYPE vstel, 发运点 END OF ty_xvbrk, BEGIN OF ty_xvbrp, vbeln TYPE vbeln_vl, posnr TYPE posnr_vl, matnr TYPE matnr, lfimg TYPE lfimg, vrkme TYPE vrkme, END OF ty_xvbrp. DATA: lt_inbound_header TYPE bapi_inb_delivery_cre_header, lt_inbound_item TYPE TABLE OF bapi_inb_delivery_cre_item, ls_inbound_item LIKE LINE OF lt_inbound_item, lt_return TYPE TABLE OF bapiret2, lv_inbound_delivery TYPE vbeln_vl, lv_count TYPE i. CHECK i_xvbrk[] IS NOT INITIAL. LOOP AT i_xvbrk INTO DATA(ls_header). * 1. 判断是否是公司间/工厂间调拨的外向交货单 * 具体判断条件取决于你的交货单类型设置 SELECT SINGLE lfart FROM likp INTO DATA(lv_lfart) WHERE vbeln ls_header-vbeln. IF lv_lfart NLCC. 这里换实际STO外向交货类型 CONTINUE. ENDIF. * 2. 检查是否已经创建过内向交货单避免重复创建 SELECT COUNT(*) FROM vbfa INTO lv_count WHERE vbelv ls_header-vbeln AND vbtyp_n 7. 7代表内向交货后续单据 IF lv_count 0. CONTINUE. ENDIF. * 3. 准备BAPI抬头参数 CLEAR lt_inbound_header. lt_inbound_header-delivery_type NL. 内向交货单类型 lt_inbound_header-shipment_no ls_header-vbeln. 其他抬头字段按需补充 * 4. 准备行项目参数基于外向交货单行项目 LOOP AT i_xvbrp INTO DATA(ls_item) WHERE vbeln ls_header-vbeln. MOVE-CORRESPONDING ls_item TO ls_inbound_item. ls_inbound_item-delivery_numb ls_header-vbeln. APPEND ls_inbound_item TO lt_inbound_item. ENDLOOP. * 5. 调用BAPI创建内向交货单 CALL FUNCTION BAPI_INBOUND_DELIVERY_CREATE_SAP EXPORTING header lt_inbound_header IMPORTING delivery lv_inbound_delivery TABLES item lt_inbound_item return lt_return. IF lv_inbound_delivery IS NOT INITIAL. CALL FUNCTION BAPI_TRANSACTION_COMMIT. ELSE. 写应用日志方便排查 WRITE: 创建内向交货单失败外向交货单:, ls_header-vbeln. ENDIF. ENDLOOP. ENDFUNCTION.这个函数的重点在第二步——检查是否已建单。这段逻辑看着不起眼但实际项目里太重要了。如果用户PGI后因为某种原因冲销重过账函数再次触发没有检查就会创建两张内向交货单收货仓库马上乱套。加上VBFA检查后已存在后续内向交货单就直接跳过避免重复。5.3 代码里的几个典型坑第一BTE传入的I_XVBRK/I_XVBRP是内部表不是结构。代码里要按内表处理循环逐条判断。很多新手第一次写误以为只有一个单号结果只看第一行后半车货全部漏建。第二内向交货单的卸货点字段很关键。BAPI参数里如果不传卸货点系统会尝试从位置确定配置里自动找。如果配置没维护BAPI返回错误“无法确定卸货点”。别等到生产环境了才去补位置确定配置测试环境就要用真实工厂架构验证。第三别把COMMIT放在标准程序的LUW里太多。BTE事件本身是在标准程序内部触发的我们调用BAPI后立即COMMIT虽然能快速释放锁但如果外层标准程序后续更新出问题需要回滚内向交货单已经生成两边数据就对不上了。稳妥做法是尽量把业务校验都做在前面确认无误后再提交。6. 常见问题排查与避坑实录6.1 问题速查表下面这张表是我做STO项目时总结的现象和排查路径照着检查基本能定位九成问题。现象可能原因优先排查路径PGI后没有生成内向交货单条件记录缺失/输出类型未激活/BTE未挂函数VL06E查输出日志FIBF查BTE激活状态输出日志报错处理例程函数不存在或权限问题NACE检查输出类型处理例程字段创建时报“找不到卸货点”位置确定配置未维护SPRO - 物流执行 - 装运 - 位置确定运输单确认后未生成外向交货单类型未启用内向交货参数检查交货类型配置生成的内向交货单数量不对单位换算或部分行项目被遗漏对比外向交货单和内向交货单的行项目数重复生成两张没有检查VBFA后续单据增强代码里加VBFA计数检查跨系统IDoc收到但没建单接收方流程代码或伙伴参数错误WE20检查伙伴配置BD87检查IDoc状态6.2 上线前必须做的事不管选择哪条方案上线前我强烈建议做好三件事。第一找两三个有代表性的物料做全链路测试物料主数据里要包含序列号、批次、特殊库存标识这些影响交货的属性别只用通用物料测试很多坑只在特殊物料上出现。第二把输出日志和BTE函数日志留全上线前两周每天都检查一遍后台作业或者日志表确保自动创建的成功率接近100%再切换正式流程。第三也是最容易忽略的跟业务确认“冲销再过账”场景下的单据处理规则。PGI冲销后重新过账BTE会再次触发内向交货单会再建一张。如果业务上不允许重复创建要么在增强里做检查要么在流程上禁止冲销后直接再过账而是让IT介入处理。这个问题不提前约定上线后运维会很痛苦。还有一个实用小技巧在做输出控制方案时把输出记录保留在VL06E里不要清理出问题的时候能快速定位是哪一单没触发、为什么报错。很多项目上线几个月后输出日志被定期清理了再回溯问题就非常费劲。建议在后台作业里把输出日志保留周期调长至少半年。最后再分享一个我个人这些年做STO项目的习惯不管选哪种方案先和仓库负责人当面聊一次搞清楚收货端真正的工作模式。他们对“是否真的需要内向交货单”这件事最有发言权。有些仓库没有用WM收货就是MIGO直接输入采购订单号那内向交货单反而是多余的中间环节。SAP方案设计永远跟着业务流程走而不是业务流程硬套SAP标准功能。这个原则比任何技术细节都重要。