订单只发了部分商品,后台却显示全部已发,或剩余数量无法继续创建 Shipment。可配置、Bundle 与 Downloadable 的父子行计数不同,自定义 ERP 回写若同时更新父行和子行,容易重复累计。
先固定复现条件和影响范围
锁定订单和 Shipment,导出 sales_order_item 的 parent_item_id、product_type、qty_ordered、qty_shipped、qty_canceled、qty_refunded,再与 sales_shipment_item 对照。
SELECT item_id,parent_item_id,sku,product_type,qty_ordered,qty_shipped,qty_canceled,qty_refunded
FROM sales_order_item WHERE order_id=123 ORDER BY item_id;
SELECT s.entity_id,s.increment_id,si.order_item_id,si.sku,si.qty
FROM sales_shipment s JOIN sales_shipment_item si ON si.parent_id=s.entity_id
WHERE s.order_id=123 ORDER BY s.entity_id,si.entity_id;
SELECT source_code,sku,quantity,status FROM inventory_source_item WHERE sku IN ('SKU-A','SKU-B');
根据证据分支定位
shipment_item 数量正确而 order_item 累计错误,查自定义保存流程;父子行同时累计,检查商品类型处理;后台正确但 ERP 显示错误,查接口映射。MSI 环境还要区分 Reservation 补偿与实际 Source deduction,不要把两个动作都当物理扣减。
修复时保留回滚路径
通过 Shipment Repository 或标准订单服务创建发货,按可发的叶子订单项传数量,并用外部 shipment ID 做幂等。不要直接更新 qty_shipped;修历史订单前先重建每张 Shipment 的事实明细并评估库存和邮件副作用。
上线验收
测试可配置、Bundle、简单商品的部分与全部发货,重复提交同一 ERP 事件只能产生一次 Shipment。剩余可发数量、订单状态、库存 Source 和客户邮件应一致。
生产环境操作前的检查
处理“Magento 2 部分发货数量与订单不一致”前,先记录 Magento 版本、部署模式、问题 Store View、复现时间和最近一次发布。所有 SQL 默认先执行 SELECT;任何写操作、目录清理或配置切换都应确认影响范围、保留备份并准备回滚。多节点环境要核对各节点代码版本、app/etc/env.php 配置摘要与流量分布。
php bin/magento --version
php bin/magento deploy:mode:show
php bin/magento maintenance:status
php bin/magento indexer:status
php bin/magento cache:status
命令应由 Magento 文件所有者在项目根目录执行。不要在生产高峰同时运行全量索引、静态部署和数据修复;涉及客户、支付、税费或订单的改动,先在脱敏数据副本验证。
建立可比较的排查记录
每次实验只改变一个变量,记录操作前值、命令、开始与结束时间、结果和回滚方式。准备一个正常对象与一个异常对象对照;若旧对象恢复而新对象仍能复现,说明根因尚未消除。
| 检查层 | 需要保存的证据 | 通过标准 |
|---|---|---|
| 入口层 | URL、状态码、请求 ID、节点 | 路由和协议一致 |
| 应用层 | 异常堆栈、模块、作用域 | 无新异常且可重复 |
| 数据层 | 主键、时间、关联行数 | 关系完整且无重复副作用 |
| 业务层 | 正常、失败、重试路径 | 最终结果一致 |
修复后的观察窗口
上线后至少观察一轮 Cron、队列和索引周期,并在两台节点、无痕浏览器和目标 Store View 重复验证。临时调试日志必须脱敏,确认错误率、响应时间和数据量稳定后及时关闭。

