fix(dispatch): cancel planned cargo without loading readiness

This commit is contained in:
2026-10-22 09:08:26 +08:00
parent 5783610154
commit 0c05c15e5a
4 changed files with 88 additions and 1 deletions
@@ -373,7 +373,8 @@ public class DispatchAggregateService implements DispatchExecutionPort,DispatchA
var replay=commands.replay(actor,number,identity);if(replay.isPresent())return replay.get();requireEditing(row,request.recordVersion());
if(trim(request.reason())==null)throw bad("取消原因不能为空");
if(repository.realPicked(row.getId())!=0 || repository.outbounds(row.getId()).stream().anyMatch(o->!"CANCELLED".equals(o.status())))throw conflict("本车已有真实执行或关联出库,不能取消");
var items=repository.cargo(row.getId());lockExecutionSources(row,items);appointments.lock(repository.batches(row.getId()),List.of());appointments.remove(row.getId(),items);sourceRules.releaseWholeRelabel(items);
// 撤销的是本车运输占用,不重新要求货物已实收或满足装车条件;真实执行仍由前面的零扫码及出库门禁保护。
var items=repository.cargo(row.getId());sourceRules.lockCancellationSources(row.getOverseasWarehouseId(),items);appointments.lock(repository.batches(row.getId()),List.of());appointments.remove(row.getId(),items);sourceRules.releaseWholeRelabel(items);
releaseSources(row,actor,items,now());
fleet.cancelDispatchShell(number,actor.operatorId(),request.reason());row.setStatus(Status.CANCELLED.name());invalidate(row,"排车已取消");
return saveCommand(row,actor,identity,request.reason());
@@ -382,6 +382,18 @@ public class DispatchService {
});
}
/** 取消只锁本车待释放的申请与原实托,不以当前库存、实收或组托完整性作为撤销安排的资格。 */
@Transactional(propagation=org.springframework.transaction.annotation.Propagation.MANDATORY)
public void lockCancellationSources(String warehouse,List<DispatchItem> items) {
if(warehouse==null || warehouse.isBlank())throw new IllegalArgumentException("取消排车仍须明确起运仓库");
var applications=items.stream().filter(i->"RELABEL_ORDER".equals(i.getSourceBusinessType()))
.map(DispatchItem::getSourceBusinessNo).collect(Collectors.toCollection(TreeSet::new));
// 先锁实际需要恢复预占的换标申请,再取得共同实托串行点;计划来源不伪造实托或查询库存。
if(!applications.isEmpty())lockRelabelApplications(applications);
var pallets=items.stream().map(DispatchItem::getWarehousePalletId).filter(Objects::nonNull).distinct().sorted().toList();
if(!pallets.isEmpty())palletSerial.lock(pallets);
}
/** 执行沿用全车申请/订单→共同实托串行顺序;库存变化不重新解释原冻结贡献。 */
@Transactional(propagation=org.springframework.transaction.annotation.Propagation.MANDATORY)
public RelabelDispatchSourcePort.Snapshot lockExecutionSources(String warehouse,List<DispatchItem> items) {