ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

SAP外向交货单拣配取消:BAPI_OUTB_DELIVERY_CHANGE实战指南

SAP外向交货单拣配取消:BAPI_OUTB_DELIVERY_CHANGE实战指南 1. 项目背景与核心痛点最近在做一个SAP SD模块的增强项目时遇到了一个挺典型的业务场景仓库操作员在VL02N里对一张外向交货单执行了拣配但后来因为客户临时变更、包装错误或者质检发现问题需要取消部分或全部的拣配数量。这个需求听起来简单但在标准SAP里你会发现并没有一个像“取消拣配”这样直接的按钮。系统通常只允许你“冲销”整个拣配凭证或者通过后续的“发货过账”反向操作但这往往不是业务部门想要的——他们希望的是在不影响交货单状态、不产生额外移动类型凭证的前提下仅仅把“已拣配数量”这个字段清零让物料回到“可用”状态以便重新安排拣配或修改交货。这个需求背后其实是SAP WM仓库管理或EWM扩展仓库管理模块与SD销售与分销模块集成时的一个控制逻辑。拣配Picking在系统里不是一个简单的状态标记它关联着库存的预留、仓库任务Warehouse Task的创建和确认。直接去改表LIPS-VSPEM拣配数量或者LIKP-VSTEL拣配状态是行不通的会破坏数据一致性轻则报错重则导致库存数据混乱。所以我们需要通过ABAP开发找到一个合规、稳定且能回滚的途径来实现这个“取消拣配数量”的功能。这不仅仅是写几行代码更新字段而是要深入理解SAP标准流程并利用其提供的BAPI或函数模块在系统的安全框架内完成操作。下面我就结合最近一次的实现把完整的思路、步骤和踩过的坑分享出来。2. 理解外向交货单拣配的核心逻辑与数据流在动手写代码之前我们必须先搞清楚系统是怎么处理拣配的。这能帮你避开很多想当然的误区。2.1 拣配在SAP中的含义与数据影响当你通过VL02N或使用函数模块WS_DELIVERY_UPDATE对交货单执行拣配时系统主要做以下几件事更新交货单行项目表LIPS中的VSPEM已拣配数量字段会增加PKSTA拣配状态会从空‘ ’变为‘B’部分拣配或‘C’完全拣配。创建或更新仓库任务对于启用了WM管理的库存地点系统会通过事务码LX01或LX02创建仓库任务存储在LTAK、LTAP等表中将库存从存储类型如001转移到拣配区如902。此时库存的“已拣配”数量会增加但仍在原库存地点只是被“冻结”了。更新库存预留与交货单关联的库存预留RESB表中相关的需求数量会被标记为部分或完全拣配。生成凭证流在表VBFA凭证流中会生成一条从交货单到拣配凭证类型为P的后继凭证记录。关键点在于拣配不是一个孤立操作它创建了新的凭证对象仓库任务并改变了库存的可用性状态。因此所谓的“取消拣配”本质上是要逆向完成上述步骤删除或冲销仓库任务并更新LIPS和RESB表中的相关字段同时维护凭证流的完整性。2.2 为什么不能直接修改数据库表很多刚接触ABAP开发的朋友可能会想最直接的办法不就是用UPDATE语句把LIPS-VSPEM改成0再把LIPS-PKSTA改回空吗我强烈建议你立刻打消这个念头原因有三数据不一致LIPS表只是冰山一角。你手动改了这里但RESB表中的预留拣配数量、LTAP中的仓库任务状态、VBFA中的凭证流都不会自动更新。这会导致后续发货过账VL02N中的PGI时系统检查逻辑混乱很可能报错“数量不一致”或“凭证流错误”。违反SAP业务逻辑SAP是一个高度集成的系统任何业务操作都应该通过其标准的事务码Transaction、BAPIBusiness Application Programming Interface或函数模块Function Module来执行。这些标准程序内部包含了完整的一致性检查Authority Check、凭证更新Document Update和状态管理Status Management。直接跳开它们等于绕过了所有安全阀。难以支持后续操作即使你侥幸把所有相关表都手动改对了这个“取消”操作也不会在系统的变更日志Change Log或审计线索中留下标准记录。未来排查问题会非常困难。正确的做法是找到并调用SAP标准提供的、用于冲销或删除拣配的相关功能模块。3. 方案选型BAPI vs. 函数模块经过对SAP帮助文档和社区经验的梳理实现取消拣配主要有两个技术方向使用BAPI或直接调用底层函数模块。3.1 方案一使用BAPIBAPI_OUTB_DELIVERY_CHANGE这是最“优雅”和推荐的方法。BAPI是SAP为外部系统集成和批量处理设计的标准化接口其优点在于封装性好、有明确的结构化输入输出、自带错误处理机制。核心思路通过BAPI修改交货单在行项目层级将“拣配数量”字段清空。BAPI内部会处理所有关联的仓库任务冲销和状态更新。关键参数结构DELIVERY输入参数要修改的交货单号。HEADER_DATA通常不需要修改保持初始值。HEADER_CONTROL控制参数一般也不需要。ITEM_DATA一个内表用于传递要修改的行项目数据。这里是我们操作的核心。ITEM_CONTROL对应ITEM_DATA中每一行的控制参数内表用于指示BAPI对每一行执行什么操作如修改‘U’。在ITEM_DATA内表中我们需要填充以下关键字段来取消拣配DELIV_NUMB交货单号。DELIV_ITEM行项目号。PICK_QTY将此字段设置为0。这是告诉BAPI“将拣配数量设置为0”。可选PICK_QTY_DEC如果使用小数位对应的小数字段。在ITEM_CONTROL内表中对应行需要设置DELIV_NUMB/DELIV_ITEM对应行项目。PICK_QTY_CHG设置为‘X’。这是最关键的控制字段它指示BAPI“需要更改拣配数量字段”。调用逻辑DATA: lt_item_data TYPE TABLE OF bapiobdlvitemchg, ls_item_data TYPE bapiobdlvitemchg, lt_item_control TYPE TABLE OF bapiobdlvitemchgcrt, ls_item_control TYPE bapiobdlvitemchgcrt, lt_return TYPE TABLE OF bapiret2. ls_item_data-deliv_numb lv_vbeln. ls_item_data-deliv_item lv_posnr. ls_item_data-pick_qty 0. “ 核心设置拣配数量为0 APPEND ls_item_data TO lt_item_data. ls_item_control-deliv_numb lv_vbeln. ls_item_control-deliv_item lv_posnr. ls_item_control-pick_qty_chg ‘X’. “ 核心指示更改此字段 APPEND ls_item_control TO lt_item_control. CALL FUNCTION ‘BAPI_OUTB_DELIVERY_CHANGE’ EXPORTING header_data ls_header_data header_control ls_header_control delivery lv_vbeln TABLES item_data lt_item_data item_control lt_item_control return lt_return. IF line_exists( lt_return[ type ‘E’ ] ) OR line_exists( lt_return[ type ‘A’ ] ). CALL FUNCTION ‘BAPI_TRANSACTION_ROLLBACK’. “ 处理错误信息 ELSE. CALL FUNCTION ‘BAPI_TRANSACTION_COMMIT’ EXPORTING wait ‘X’. “ 提示成功 ENDIF.优点标准化符合SAP最佳实践易于维护和理解。错误处理完善RETURN表会返回详细的成功或错误消息。支持批量处理可以一次性处理多个行项目。潜在缺点与注意事项依赖系统配置BAPI能否成功执行取决于后台的凭证类型配置、状态管理配置等。如果交货单已经处于“部分发货”或“已完成”状态BAPI可能会拒绝更改。需要显式提交必须调用BAPI_TRANSACTION_COMMIT来真正保存更改否则操作会回滚。理解控制字段必须正确设置ITEM_CONTROL中的PICK_QTY_CHG否则BAPI会忽略你对PICK_QTY的修改。3.2 方案二调用函数模块WS_REVERSE_GOODS_ISSUE或L_TO_CREATE_DN在某些更复杂或更底层的场景下你可能需要更直接的控制。这时可以研究用于冲销货物移动或处理交货单的底层函数。WS_REVERSE_GOODS_ISSUE这个函数原本用于冲销发货过账Goods Issue。但在某些逻辑下取消拣配可以被视为冲销一个“虚拟”的货物移动。使用这个方法需要极其小心因为它直接关联物料凭证Material Document如果使用不当可能会产生非预期的物料账务影响。除非你对MM物料管理模块的移动类型有深刻理解否则不建议作为首选。L_TO_CREATE_DN及相关函数这是一组更底层的仓库管理WM函数用于创建和处理交货单相关的仓库任务。要取消拣配理论上你需要找到并冲销由原始拣配创建的仓库任务LTAP。这通常涉及调用L_TO_DELETE删除任务或L_TO_CONFIRM以反向数量确认任务。这个方法非常复杂需要你精确追踪到对应的仓库任务号TANUM并且要处理任务确认的各种状态如C确认D删除。它绕开了SD模块的标准逻辑只建议在纯WM场景且BAPI无法满足时由经验丰富的WM顾问使用。方案对比与选型建议特性BAPIBAPI_OUTB_DELIVERY_CHANGE底层WM函数 (L_TO_*)抽象层级高层业务对象交货单底层业务对象仓库任务易用性高接口清晰低需要深入理解WM表结构安全性高内置完整校验中低容易因操作不当破坏数据适用场景标准SD交货单取消拣配复杂的WM流程定制或BAPI受限时维护性好标准接口稳定差依赖于底层表结构结论对于绝大多数“取消外向交货单拣配数量”的需求应优先选择方案一即使用BAPI_OUTB_DELIVERY_CHANGE。它安全、标准、易于集成到自定义事务码或增强中。只有在BAPI因特定业务规则如特殊状态无法执行并且你完全清楚WM底层数据模型的情况下才考虑方案二。4. 实战开发基于BAPI的完整实现与增强确定了使用BAPI方案后我们来看一个完整的、可投入生产的实现示例。这个示例将包含自定义屏幕、数据校验、BAPI调用和错误处理。4.1 程序设计与数据准备我们创建一个可执行程序ZCANEL_PICKING通过屏幕输入交货单和行项目。REPORT zcancel_picking. TABLES: likp, lips. DATA: gv_vbeln TYPE vbeln_vl, gv_posnr TYPE posnr_vl. SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-001. PARAMETERS: p_vbeln TYPE vbeln_vl OBLIGATORY. SELECT-OPTIONS: s_posnr FOR lips-posnr. SELECTION-SCREEN END OF BLOCK b1. START-OF-SELECTION. PERFORM main. FORM main. DATA: lt_lips TYPE TABLE OF lips, ls_lips TYPE lips. “ 1. 检查输入的交货单是否存在且状态允许取消拣配 PERFORM check_delivery USING p_vbeln CHANGING lt_lips. “ 2. 根据输入的行项目筛选 IF s_posnr IS NOT INITIAL. DELETE lt_lips WHERE posnr NOT IN s_posnr. ENDIF. “ 3. 检查筛选后的行项目是否已拣配 LOOP AT lt_lips INTO ls_lips WHERE vspem 0. DELETE lt_lips. MESSAGE i398(00) WITH ‘行项目’ ls_lips-posnr ‘拣配数量为0无需处理。’ DISPLAY LIKE ‘W’. ENDLOOP. IF lt_lips IS INITIAL. MESSAGE ‘没有符合条件的行项目需要处理。’ TYPE ‘I’. RETURN. ENDIF. “ 4. 调用BAPI执行取消拣配 PERFORM cancel_picking USING p_vbeln lt_lips. ENDFORM.CHECK_DELIVERY子程序用于验证交货单FORM check_delivery USING iv_vbeln TYPE vbeln_vl CHANGING ct_lips TYPE TABLE OF lips. DATA: ls_likp TYPE likp. “ 检查交货单头 SELECT SINGLE * FROM likp INTO ls_likp WHERE vbeln iv_vbeln. IF sy-subrc 0. MESSAGE e398(00) WITH ‘交货单’ iv_vbeln ‘不存在。’. ENDIF. “ 检查交货单状态已发货或已完成的不应再取消拣配 IF ls_likp-wbstk CA ‘C’. “ 例如状态C表示已完成 MESSAGE e398(00) WITH ‘交货单’ iv_vbeln ‘已发货或完成不能取消拣配。’. ENDIF. “ 获取所有行项目 SELECT * FROM lips INTO TABLE ct_lips WHERE vbeln iv_vbeln. ENDFORM.4.2 BAPI调用核心逻辑与错误处理这是最核心的CANCEL_PICKING子程序。FORM cancel_picking USING iv_vbeln TYPE vbeln_vl it_lips TYPE TABLE OF lips. DATA: lt_item_data TYPE TABLE OF bapiobdlvitemchg, ls_item_data TYPE bapiobdlvitemchg, lt_item_control TYPE TABLE OF bapiobdlvitemchgcrt, ls_item_control TYPE bapiobdlvitemchgcrt, lt_return TYPE TABLE OF bapiret2, ls_return TYPE bapiret2, lv_success TYPE abap_bool VALUE abap_true. “ 1. 准备BAPI输入参数 LOOP AT it_lips ASSIGNING FIELD-SYMBOL(fs_lips). CLEAR: ls_item_data, ls_item_control. ls_item_data-deliv_numb iv_vbeln. ls_item_data-deliv_item fs_lips-posnr. ls_item_data-pick_qty 0. “ 目标将拣配数量设为0 APPEND ls_item_data TO lt_item_data. ls_item_control-deliv_numb iv_vbeln. ls_item_control-deliv_item fs_lips-posnr. ls_item_control-pick_qty_chg ‘X’. “ 关键控制字段 APPEND ls_item_control TO lt_item_control. ENDLOOP. “ 2. 调用BAPI CALL FUNCTION ‘BAPI_OUTB_DELIVERY_CHANGE’ EXPORTING header_data VALUE bapiobdlvhdrchg( ) “ 通常留空 header_control VALUE bapiobdlvhdrchgctrl( ) “ 通常留空 delivery iv_vbeln TABLES item_data lt_item_data item_control lt_item_control return lt_return. “ 3. 处理BAPI返回消息 LOOP AT lt_return INTO ls_return WHERE type CA ‘EAX’. “ E:错误, A:终止, X:退出 WRITE: / ls_return-type, ls_return-id, ls_return-number, ls_return-message. lv_success abap_false. ENDLOOP. “ 4. 根据结果提交或回滚 IF lv_success abap_true. CALL FUNCTION ‘BAPI_TRANSACTION_COMMIT’ EXPORTING wait ‘X’. MESSAGE s398(00) WITH ‘交货单’ iv_vbeln ‘拣配取消成功。’. ELSE. CALL FUNCTION ‘BAPI_TRANSACTION_ROLLBACK’. MESSAGE e398(00) WITH ‘交货单’ iv_vbeln ‘拣配取消失败已回滚。’. ENDIF. ENDFORM.4.3 关键增强点与异常处理在实际项目中直接调用BAPI可能还不够我们通常需要加入一些增强逻辑来处理边界情况。增强点1检查仓库任务状态在调用BAPI前可以检查相关行项目是否已生成仓库任务且未被确认。如果任务已确认LTAP-PQUIT ‘X’取消拣配可能会更复杂可能需要先冲销任务确认。FORM check_warehouse_task USING iv_vbeln TYPE vbeln_vl iv_posnr TYPE posnr_vl CHANGING cv_can_cancel TYPE abap_bool cv_message TYPE string. DATA: lv_tanum TYPE tanum. SELECT SINGLE tanum FROM ltap INTO lv_tanum WHERE vbeln iv_vbeln AND posnr iv_posnr AND ( pquit ‘X’ OR nlpla IS NOT INITIAL ). “ 已确认或已有目标库位 IF sy-subrc 0. cv_can_cancel abap_false. cv_message ‘行项目存在已确认的仓库任务请先在WM中处理。’. ELSE. cv_can_cancel abap_true. ENDIF. ENDFORM.增强点2处理部分取消业务可能只想取消部分拣配数量而不是全部清零。这时ls_item_data-pick_qty应该设置为目标值例如原拣配数量是10想取消5则此处设为5。同时你需要从LIPS-VSPEM中读取当前值进行计算。增强点3集成状态管理Status Management某些公司可能为交货单定义了复杂的状态管理。取消拣配可能需要触发一个状态变更。你可以在BAPI调用后检查LIKP或LIPS的状态字段并调用相应的状态更新函数如I_CHANGE_STATUS。增强点4添加自定义权限检查使用AUTHORITY-CHECK对象例如V_VBAK_VKO销售组织权限、V_VBAK_VTW分销渠道权限在程序开始时检查用户是否有权修改特定销售范围的交货单。5. 测试策略与常见问题排查开发完成后 rigorous的测试是保证功能稳定的关键。5.1 分场景测试用例设计你需要设计覆盖以下场景的测试用例正常场景TC1取消单个行项目的全部拣配数量VSPEM从0变为0PKSTA从‘C’/‘B’变为空。TC2取消单个行项目的部分拣配数量VSPEM从10变为5PKSTA保持‘B’。TC3批量取消多个行项目的拣配。异常与边界场景TC4尝试取消一个拣配数量为0的行项目应给出警告不调用BAPI。TC5尝试取消一个已发货过账WBSTK含‘C’的交货单行项目BAPI应返回错误。TC6交货单已被其他用户锁定应处理ENQUEUE错误。TC7网络超时或系统异常在BAPI调用后、COMMIT前中断数据应回滚。TC8与WM深度集成取消拣配后检查对应的仓库任务LTAP是否被删除或冲销库存的“已拣配”数量MARD-INSME是否相应减少。集成测试TC9取消拣配后立即在VL02N中重新执行拣配确保流程正常。TC10取消拣配后检查相关的库存预留RESB状态是否同步更新。5.2 常见错误与排查思路在测试或上线后你可能会遇到以下错误BAPI返回错误“状态XX不允许更改”排查检查交货单和行项目的状态LIKP-WBSTK,LIPS-STATUS。使用事务码V.50查看你的交货单类型LFART的“状态管理”配置确认在哪些状态下允许更改拣配数量。可能需要通过I_CHANGE_STATUS先更改状态。BAPI执行成功但拣配数量未变排查这是最常见的问题。99%的原因是你忘记了在ITEM_CONTROL内表中设置PICK_QTY_CHG ‘X’。没有这个标志BAPI会忽略你对PICK_QTY的修改。务必仔细检查输入内表。错误“仓库任务XXXX已确认无法更改”排查检查LTAP表。如果仓库任务已确认PQUIT ‘X’BAPI可能无法直接处理。需要先通过WM事务码如LU04冲销该仓库任务的确认或者在你的程序中集成调用WM的相关函数如L_TO_CONFIRM进行反向确认来处理。这通常需要WM模块的知识。程序执行后VL02N中仍显示旧数量排查首先确认BAPI调用后是否成功执行了BAPI_TRANSACTION_COMMIT。其次SAP GUI有缓存。尝试退出VL02N再重新进入或者按F5刷新。最后直接查询LIPS-VSPEM数据库字段确认其值是否已更新。如果数据库已更新但界面显示旧值就是缓存问题。性能问题处理大批量交货单时超时优化避免在循环内频繁调用BAPI。虽然BAPI支持批量处理但BAPI_OUTB_DELIVERY_CHANGE一次调用只能处理一张交货单。对于批量取消可以将程序设计为后台作业JOB并考虑使用COMMIT WORK的分批处理例如每处理50张单子提交一次以减少锁的持有时间。6. 进阶思考与EWM和S/4HANA的兼容性随着系统升级很多客户从传统的WM模块迁移到了更强大的EWM扩展仓库管理或者升级到了S/4HANA。我们的解决方案需要具备前瞻性。在SAP EWM环境下的考量 在EWM中拣配流程更加精细和复杂通常通过仓库订单Warehouse Order和仓库任务Warehouse Task来管理。取消外向交货单的拣配在EWM中可能对应着“取消仓库请求”或“冲销仓库任务”。此时单纯使用SD模块的BAPI_OUTB_DELIVERY_CHANGE可能不够因为它可能无法触达EWM深层的对象。建议方案检查集成点首先确认你的SAP ERP与EWM是通过核心接口Core Interface还是嵌入式EWM部署。这决定了你需要调用ERP侧的BAPI还是EWM侧的API。使用EWM BAPI如果取消操作需要EWM端生效应优先考虑使用EWM提供的BAPI例如/SCWM/BAPI_DELIVERY_CHANGE。它的逻辑与ERP的BAPI类似但会确保EWM中的仓库任务和库存状态同步更新。组合调用在混合环境下可能需要先调用ERP BAPI更新交货单状态再通过RFC调用EWM的BAPI来更新仓库任务状态。务必注意事务一致性。在SAP S/4HANA下的变化 S/4HANA引入了新的数据模型如CDS视图和编程模型如ABAP RESTful Application Programming Model, RAP。虽然经典的BAPI在S/4HANA中仍然可用出于兼容性考虑但SAP鼓励在新开发中使用基于Fiori和OData的服务。后台作业对于批量或后台处理经典BAPI依然是可靠的选择。本文介绍的方法在S/4HANA系统中完全适用。Fiori应用集成如果你需要为这个功能开发一个Fiori应用那么后端应该创建一个OData服务。在这个OData服务的实现类ABAP Behavior Implementation中其核心逻辑最终很可能还是会封装调用BAPI_OUTB_DELIVERY_CHANGE。因此理解本文的BAPI调用逻辑是构建现代化服务的基础。一个实用的建议在开发任何与交货单相关的增强时都使用CL_SD_DELIVERY_PROCESSING之类的封装类来获取交货单服务实例。这类封装类能更好地适应不同版本ECC vs S/4HANA的底层变化提高代码的健壮性和可移植性。虽然直接调用BAPI在大多数情况下没问题但在长期维护的项目中使用官方推荐的封装类是一个好习惯。最后无论系统环境如何变化解决问题的核心思路不变理解业务操作在系统中的完整数据流和状态变迁然后寻找标准、安全的接口去实现它永远避免直接操作底层数据库表。这个“取消拣配”的功能就是一个很好的例子它锻炼了你对SD-WM集成、BAPI使用和异常处理的综合能力。在实际操作中一定要和业务顾问、仓库管理员充分沟通明确他们的操作场景和期望这样才能开发出真正好用、耐用的程序。
返回列表