物理库存没有出库,但订单取消后Salable Quantity继续下降。查inventory_reservation会看到下单时的负记录,却没有对应取消的正记录;或者有补偿,但stock_id、sku或metadata中的订单引用不一致,汇总后仍然不为零。MSI的Reservation是不可变流水,正确修复是追加补偿,不是删除旧行。
先针对一张订单重建事件链
SELECT reservation_id,stock_id,sku,quantity,metadata FROM inventory_reservation WHERE metadata LIKE '%"object_id":"100000123"%' ORDER BY reservation_id;
正常情况下,下单产生负数;取消、退款未发货数量或发货扣减Source后会产生相应事件。不同流程事件名不同,但同一SKU最终汇总必须符合实际未履约数量。
确认订单是怎么被“取消”的
如果ERP插件只是 setState/setStatus 然后save,没有调用OrderManagementInterface::cancel,就可能绕过取消事件和库存补偿。后台手动取消正常、ERP取消异常时,几乎可以直接沿这条线查。
$order = $orderRepository->get($orderId);
if ($order->canCancel()) {
$orderManagement->cancel($orderId);
}
不要在Observer里重复调用cancel。服务层应负责一次完整状态转换,外部回调用幂等键避免重复消费。
用官方工具生成补偿前先预览
bin/magento inventory:reservation:list-inconsistencies -r > /tmp/reservation-inconsistencies.txt
逐条核对订单状态、已发货数量、退款数量与Stock。确认无误后,再把输出交给补偿命令。大批量执行前先在预生产复制数据演练,并备份数据库。
cat /tmp/reservation-inconsistencies.txt | bin/magento inventory:reservation:create-compensations
命令参数在不同版本可能有差异,以当前 bin/magento help 输出为准。核心原则是让系统追加带来源说明的补偿流水。
为什么不建议DELETE
- 删除会失去库存审计链
- 并发下单可能在清理期间写入新Reservation
- 删错stock_id会影响另一个销售渠道
- 后续升级和一致性检查无法解释数量来源
修复代码后做四种状态回归
- 下单后直接取消
- 部分发货后取消剩余商品
- 全额退款已发货商品
- 支付回调与取消回调乱序到达
每个用例都记录source quantity、reservation sum和salable quantity。满足近似公式:可售量 = Source可售汇总 + Reservation汇总 - 阈值调整。若启用backorder或自定义Source Selection,还要把相应配置计入。
补偿历史数据只是第一步。若ERP仍用错误方式改订单状态,今晚修好的库存明天还会继续变负。

