fix(firstleg): share container evidence across loading owners
This commit is contained in:
+21
-5
@@ -97,13 +97,19 @@ public class LoadingDeliveryService {
|
|||||||
@Transactional
|
@Transactional
|
||||||
public LoadingActionResponse completeLoading(long tenantId, long inboundId, long operatorId,
|
public LoadingActionResponse completeLoading(long tenantId, long inboundId, long operatorId,
|
||||||
String operatorName, String key, CompleteLoadingRequest request) {
|
String operatorName, String key, CompleteLoadingRequest request) {
|
||||||
|
return completeLoading(tenantId, inboundId, inboundId, operatorId, operatorName, key, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoadingActionResponse completeLoading(long tenantId, long inboundId, long evidenceInboundId,
|
||||||
|
long operatorId, String operatorName, String key,
|
||||||
|
CompleteLoadingRequest request) {
|
||||||
String hash = hash(request);
|
String hash = hash(request);
|
||||||
LoadingActionResponse replay = claimOrReplay(tenantId, inboundId, operatorId, COMPLETE_LOADING, key, hash);
|
LoadingActionResponse replay = claimOrReplay(tenantId, inboundId, operatorId, COMPLETE_LOADING, key, hash);
|
||||||
if (replay != null) return replay;
|
if (replay != null) return replay;
|
||||||
Snapshot inbound = requireInbound(tenantId, inboundId);
|
Snapshot inbound = requireInbound(tenantId, inboundId);
|
||||||
CompletionTarget target = repository.completionTarget(tenantId, inboundId);
|
CompletionTarget target = repository.completionTarget(tenantId, inboundId);
|
||||||
if (target != null) {
|
if (target != null) {
|
||||||
return completeFulfillmentSlice(tenantId, inboundId, operatorId, operatorName, key, request, target);
|
return completeFulfillmentSlice(tenantId, evidenceInboundId, operatorId, operatorName, key, request, target);
|
||||||
}
|
}
|
||||||
if ("LOADED".equals(inbound.processStatus()) || "DELIVERED".equals(inbound.processStatus())) {
|
if ("LOADED".equals(inbound.processStatus()) || "DELIVERED".equals(inbound.processStatus())) {
|
||||||
loadingModelBridge.requireCompleted(tenantId, inboundId);
|
loadingModelBridge.requireCompleted(tenantId, inboundId);
|
||||||
@@ -114,7 +120,7 @@ public class LoadingDeliveryService {
|
|||||||
loadingModelBridge.requireCompleted(tenantId, inboundId);
|
loadingModelBridge.requireCompleted(tenantId, inboundId);
|
||||||
completionGate.requireReleasedBooking(tenantId, inboundId);
|
completionGate.requireReleasedBooking(tenantId, inboundId);
|
||||||
requireCompletedPicking(tenantId, inboundId);
|
requireCompletedPicking(tenantId, inboundId);
|
||||||
requireCompletionEvidence(tenantId, inboundId);
|
requireCompletionEvidence(tenantId, evidenceInboundId);
|
||||||
if (repository.completeLoading(tenantId, inboundId, operatorId, request.expectedVersion()) != 1) throw concurrent();
|
if (repository.completeLoading(tenantId, inboundId, operatorId, request.expectedVersion()) != 1) throw concurrent();
|
||||||
addHistory(tenantId, inboundId, "LOADING", "LOADED", COMPLETE_LOADING, request.remark(), operatorId, operatorName);
|
addHistory(tenantId, inboundId, "LOADING", "LOADED", COMPLETE_LOADING, request.remark(), operatorId, operatorName);
|
||||||
return complete(tenantId, inboundId, operatorId, COMPLETE_LOADING, key,
|
return complete(tenantId, inboundId, operatorId, COMPLETE_LOADING, key,
|
||||||
@@ -129,13 +135,19 @@ public class LoadingDeliveryService {
|
|||||||
public LoadingActionResponse completeAfterPdaSettlement(long tenantId, long inboundId,
|
public LoadingActionResponse completeAfterPdaSettlement(long tenantId, long inboundId,
|
||||||
long operatorId, String operatorName,
|
long operatorId, String operatorName,
|
||||||
String key, String remark) {
|
String key, String remark) {
|
||||||
|
return completeAfterPdaSettlement(tenantId, inboundId, inboundId, operatorId, operatorName, key, remark);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoadingActionResponse completeAfterPdaSettlement(long tenantId, long inboundId,
|
||||||
|
long evidenceInboundId, long operatorId,
|
||||||
|
String operatorName, String key, String remark) {
|
||||||
CompletionTarget target = repository.completionTarget(tenantId, inboundId);
|
CompletionTarget target = repository.completionTarget(tenantId, inboundId);
|
||||||
long completionInboundId = target == null ? inboundId : target.completionInboundId();
|
long completionInboundId = target == null ? inboundId : target.completionInboundId();
|
||||||
Snapshot current = repository.findInboundForUpdate(tenantId, completionInboundId);
|
Snapshot current = repository.findInboundForUpdate(tenantId, completionInboundId);
|
||||||
if (current == null || current.version() == null) {
|
if (current == null || current.version() == null) {
|
||||||
throw new BizException(ResultCode.CONFLICT, "PDA结算后的实装履约分片不存在,请刷新后重试");
|
throw new BizException(ResultCode.CONFLICT, "PDA结算后的实装履约分片不存在,请刷新后重试");
|
||||||
}
|
}
|
||||||
return completeLoading(tenantId, inboundId, operatorId, operatorName, key,
|
return completeLoading(tenantId, inboundId, evidenceInboundId, operatorId, operatorName, key,
|
||||||
new CompleteLoadingRequest(current.version(), remark));
|
new CompleteLoadingRequest(current.version(), remark));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,13 +156,17 @@ public class LoadingDeliveryService {
|
|||||||
* every external/business hard gate must pass before the PDA task is mutated.
|
* every external/business hard gate must pass before the PDA task is mutated.
|
||||||
*/
|
*/
|
||||||
public void requirePdaCompletionReady(long tenantId, long inboundId) {
|
public void requirePdaCompletionReady(long tenantId, long inboundId) {
|
||||||
|
requirePdaCompletionReady(tenantId, inboundId, inboundId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requirePdaCompletionReady(long tenantId, long inboundId, long evidenceInboundId) {
|
||||||
Snapshot inbound = requireInbound(tenantId, inboundId);
|
Snapshot inbound = requireInbound(tenantId, inboundId);
|
||||||
if (!"LOADING".equals(inbound.processStatus())) {
|
if (!"LOADING".equals(inbound.processStatus())) {
|
||||||
throw new BizException(ResultCode.CONFLICT, "PDA确认装柜完成前订单必须处于LOADING");
|
throw new BizException(ResultCode.CONFLICT, "PDA确认装柜完成前订单必须处于LOADING");
|
||||||
}
|
}
|
||||||
loadingModelBridge.requireStarted(tenantId, inboundId);
|
loadingModelBridge.requireStarted(tenantId, inboundId);
|
||||||
completionGate.requireReleasedBooking(tenantId, inboundId);
|
completionGate.requireReleasedBooking(tenantId, inboundId);
|
||||||
requireCompletionEvidence(tenantId, inboundId);
|
requireCompletionEvidence(tenantId, evidenceInboundId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoadingActionResponse completeFulfillmentSlice(long tenantId, long evidenceInboundId,
|
private LoadingActionResponse completeFulfillmentSlice(long tenantId, long evidenceInboundId,
|
||||||
@@ -167,7 +183,7 @@ public class LoadingDeliveryService {
|
|||||||
loadingModelBridge.requireCompleted(tenantId, target.sourceInboundId());
|
loadingModelBridge.requireCompleted(tenantId, target.sourceInboundId());
|
||||||
completionGate.requireReleasedBooking(tenantId, target.sourceInboundId());
|
completionGate.requireReleasedBooking(tenantId, target.sourceInboundId());
|
||||||
requireCompletedPicking(tenantId, target.sourceInboundId());
|
requireCompletedPicking(tenantId, target.sourceInboundId());
|
||||||
requireCompletionEvidence(tenantId, target.sourceInboundId());
|
requireCompletionEvidence(tenantId, evidenceInboundId);
|
||||||
if (repository.completeLoadedChild(tenantId, target.sourceInboundId(), operatorId,
|
if (repository.completeLoadedChild(tenantId, target.sourceInboundId(), operatorId,
|
||||||
request.expectedVersion()) != 1) throw concurrent();
|
request.expectedVersion()) != 1) throw concurrent();
|
||||||
repository.aggregateRootLoaded(tenantId, target.rootInboundId(), operatorId);
|
repository.aggregateRootLoaded(tenantId, target.rootInboundId(), operatorId);
|
||||||
|
|||||||
@@ -107,10 +107,12 @@ public interface LoadingPickItemMapper extends BaseMapper<LoadingPickItemEntity>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Select("""
|
@Select("""
|
||||||
SELECT DISTINCT plan_item.account_id tenantId,plan_item.inbound_id inboundId
|
SELECT plan_item.account_id tenantId,plan_item.inbound_id inboundId
|
||||||
FROM fl_loading_pick_item pick_item
|
FROM fl_loading_pick_item pick_item
|
||||||
JOIN fl_loading_plan_item plan_item ON plan_item.id=pick_item.loading_plan_item_id
|
JOIN fl_loading_plan_item plan_item ON plan_item.id=pick_item.loading_plan_item_id
|
||||||
WHERE pick_item.pick_task_id=#{taskId}
|
WHERE pick_item.pick_task_id=#{taskId}
|
||||||
|
GROUP BY plan_item.account_id,plan_item.inbound_id
|
||||||
|
ORDER BY MIN(pick_item.id)
|
||||||
""")
|
""")
|
||||||
java.util.List<InboundOwner> selectInboundOwners(@Param("taskId") long taskId);
|
java.util.List<InboundOwner> selectInboundOwners(@Param("taskId") long taskId);
|
||||||
|
|
||||||
|
|||||||
@@ -450,16 +450,20 @@ public class LoadingPickService {
|
|||||||
throw conflict("存在尚未处理的PDA拣货明细,不能确认装柜完成");
|
throw conflict("存在尚未处理的PDA拣货明细,不能确认装柜完成");
|
||||||
}
|
}
|
||||||
if (repository.review(taskId) != null) throw conflict("PDA任务已经确认装柜完成");
|
if (repository.review(taskId) != null) throw conflict("PDA任务已经确认装柜完成");
|
||||||
List<LoadingPickItemMapper.InboundOwner> owners = repository.inboundOwners(taskId).stream()
|
List<LoadingPickItemMapper.InboundOwner> discoveredOwners = repository.inboundOwners(taskId).stream()
|
||||||
.filter(owner -> owner.tenantId() != null && owner.inboundId() != null)
|
.filter(owner -> owner.tenantId() != null && owner.inboundId() != null)
|
||||||
|
.toList();
|
||||||
|
if (discoveredOwners.isEmpty()) throw conflict("PDA任务未关联有效入库单");
|
||||||
|
LoadingPickItemMapper.InboundOwner evidenceOwner = discoveredOwners.get(0);
|
||||||
|
List<LoadingPickItemMapper.InboundOwner> owners = discoveredOwners.stream()
|
||||||
.sorted(Comparator.comparing(LoadingPickItemMapper.InboundOwner::tenantId)
|
.sorted(Comparator.comparing(LoadingPickItemMapper.InboundOwner::tenantId)
|
||||||
.thenComparing(LoadingPickItemMapper.InboundOwner::inboundId))
|
.thenComparing(LoadingPickItemMapper.InboundOwner::inboundId))
|
||||||
.toList();
|
.toList();
|
||||||
if (owners.isEmpty()) throw conflict("PDA任务未关联有效入库单");
|
|
||||||
for (LoadingPickItemMapper.InboundOwner owner : owners) {
|
for (LoadingPickItemMapper.InboundOwner owner : owners) {
|
||||||
LoadingInboundMapper.Snapshot snapshot = repository.inboundForUpdate(owner.tenantId(), owner.inboundId());
|
LoadingInboundMapper.Snapshot snapshot = repository.inboundForUpdate(owner.tenantId(), owner.inboundId());
|
||||||
if (snapshot == null) throw conflict("PDA任务关联入库单不存在");
|
if (snapshot == null) throw conflict("PDA任务关联入库单不存在");
|
||||||
loadingDeliveryService.requirePdaCompletionReady(owner.tenantId(), owner.inboundId());
|
loadingDeliveryService.requirePdaCompletionReady(owner.tenantId(), owner.inboundId(),
|
||||||
|
evidenceOwner.inboundId());
|
||||||
}
|
}
|
||||||
settlementService.settle(taskId, operatorId, operatorName);
|
settlementService.settle(taskId, operatorId, operatorName);
|
||||||
if (!repository.allPlanItemsPicked(task.getLoadingPlanId())) {
|
if (!repository.allPlanItemsPicked(task.getLoadingPlanId())) {
|
||||||
@@ -472,8 +476,9 @@ public class LoadingPickService {
|
|||||||
loadingListService.finishFromPda(task.getLoadingPlanId(), taskId, operatorId, operatorName);
|
loadingListService.finishFromPda(task.getLoadingPlanId(), taskId, operatorId, operatorName);
|
||||||
repository.syncInboundProjections(taskId, operatorId, operatorName);
|
repository.syncInboundProjections(taskId, operatorId, operatorName);
|
||||||
for (LoadingPickItemMapper.InboundOwner owner : owners) {
|
for (LoadingPickItemMapper.InboundOwner owner : owners) {
|
||||||
loadingDeliveryService.completeAfterPdaSettlement(owner.tenantId(), owner.inboundId(), operatorId,
|
loadingDeliveryService.completeAfterPdaSettlement(owner.tenantId(), owner.inboundId(),
|
||||||
operatorName, "PDA-" + operationId + '-' + owner.inboundId(), remark);
|
evidenceOwner.inboundId(), operatorId, operatorName,
|
||||||
|
"PDA-" + operationId + '-' + owner.inboundId(), remark);
|
||||||
}
|
}
|
||||||
loadingListService.publishCompletedEvent(task.getLoadingPlanId());
|
loadingListService.publishCompletedEvent(task.getLoadingPlanId());
|
||||||
completeTruckAfterCommit(task.getContainerBookingId(), taskId, operatorId, operatorName);
|
completeTruckAfterCommit(task.getContainerBookingId(), taskId, operatorId, operatorName);
|
||||||
|
|||||||
@@ -508,9 +508,12 @@ class LoadingPickServiceTest {
|
|||||||
when(repository.allPlanItemsPicked(10L)).thenReturn(true);
|
when(repository.allPlanItemsPicked(10L)).thenReturn(true);
|
||||||
when(repository.approve(20L, 9L, "operator", "checked")).thenReturn(1);
|
when(repository.approve(20L, 9L, "operator", "checked")).thenReturn(1);
|
||||||
var owner = new LoadingPickItemMapper.InboundOwner(101L, 50L);
|
var owner = new LoadingPickItemMapper.InboundOwner(101L, 50L);
|
||||||
when(repository.inboundOwners(20L)).thenReturn(List.of(owner));
|
var incrementalOwner = new LoadingPickItemMapper.InboundOwner(101L, 60L);
|
||||||
|
when(repository.inboundOwners(20L)).thenReturn(List.of(owner, incrementalOwner));
|
||||||
when(repository.inboundForUpdate(101L, 50L)).thenReturn(
|
when(repository.inboundForUpdate(101L, 50L)).thenReturn(
|
||||||
new LoadingInboundMapper.Snapshot(50L, 101L, "LOADING", 7));
|
new LoadingInboundMapper.Snapshot(50L, 101L, "LOADING", 7));
|
||||||
|
when(repository.inboundForUpdate(101L, 60L)).thenReturn(
|
||||||
|
new LoadingInboundMapper.Snapshot(60L, 101L, "LOADING", 3));
|
||||||
|
|
||||||
service.complete(20L, new CompleteLoadingRequest(0, "complete-1", "checked"), 9L, "operator");
|
service.complete(20L, new CompleteLoadingRequest(0, "complete-1", "checked"), 9L, "operator");
|
||||||
service.complete(20L, new CompleteLoadingRequest(0, "complete-1", "checked"), 9L, "operator");
|
service.complete(20L, new CompleteLoadingRequest(0, "complete-1", "checked"), 9L, "operator");
|
||||||
@@ -519,14 +522,17 @@ class LoadingPickServiceTest {
|
|||||||
verify(loadingListService).finishFromPda(10L, 20L, 9L, "operator");
|
verify(loadingListService).finishFromPda(10L, 20L, 9L, "operator");
|
||||||
verify(settlementService).settle(20L, 9L, "operator");
|
verify(settlementService).settle(20L, 9L, "operator");
|
||||||
verify(repository).syncInboundProjections(20L, 9L, "operator");
|
verify(repository).syncInboundProjections(20L, 9L, "operator");
|
||||||
verify(loadingDeliveryService).requirePdaCompletionReady(101L, 50L);
|
verify(loadingDeliveryService).requirePdaCompletionReady(101L, 50L, 50L);
|
||||||
|
verify(loadingDeliveryService).requirePdaCompletionReady(101L, 60L, 50L);
|
||||||
verify(truckAppointmentService).completeLoadingFromPda(99L, 20L, 9L, "operator");
|
verify(truckAppointmentService).completeLoadingFromPda(99L, 20L, 9L, "operator");
|
||||||
verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 9L, "operator",
|
verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 50L, 9L, "operator",
|
||||||
"PDA-complete-1-50", "checked");
|
"PDA-complete-1-50", "checked");
|
||||||
|
verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 60L, 50L, 9L, "operator",
|
||||||
|
"PDA-complete-1-60", "checked");
|
||||||
verify(loadingListService).publishCompletedEvent(10L);
|
verify(loadingListService).publishCompletedEvent(10L);
|
||||||
InOrder finalization = inOrder(loadingListService, loadingDeliveryService);
|
InOrder finalization = inOrder(loadingListService, loadingDeliveryService);
|
||||||
finalization.verify(loadingListService).finishFromPda(10L, 20L, 9L, "operator");
|
finalization.verify(loadingListService).finishFromPda(10L, 20L, 9L, "operator");
|
||||||
finalization.verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 9L, "operator",
|
finalization.verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 50L, 9L, "operator",
|
||||||
"PDA-complete-1-50", "checked");
|
"PDA-complete-1-50", "checked");
|
||||||
finalization.verify(loadingListService).publishCompletedEvent(10L);
|
finalization.verify(loadingListService).publishCompletedEvent(10L);
|
||||||
}
|
}
|
||||||
@@ -556,7 +562,7 @@ class LoadingPickServiceTest {
|
|||||||
verify(settlementService).settle(20L, 9L, "operator");
|
verify(settlementService).settle(20L, 9L, "operator");
|
||||||
verify(repository).approve(20L, 9L, "operator", "checked");
|
verify(repository).approve(20L, 9L, "operator", "checked");
|
||||||
verify(loadingListService).finishFromPda(10L, 20L, 9L, "operator");
|
verify(loadingListService).finishFromPda(10L, 20L, 9L, "operator");
|
||||||
verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 9L, "operator",
|
verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 50L, 9L, "operator",
|
||||||
"PDA-complete-not-blocked-50", "checked");
|
"PDA-complete-not-blocked-50", "checked");
|
||||||
verify(loadingListService).publishCompletedEvent(10L);
|
verify(loadingListService).publishCompletedEvent(10L);
|
||||||
}
|
}
|
||||||
@@ -574,7 +580,7 @@ class LoadingPickServiceTest {
|
|||||||
new LoadingInboundMapper.Snapshot(50L, 101L, "LOADING", 7));
|
new LoadingInboundMapper.Snapshot(50L, 101L, "LOADING", 7));
|
||||||
doThrow(new BizException(com.baiyue.common.core.api.ResultCode.CONFLICT,
|
doThrow(new BizException(com.baiyue.common.core.api.ResultCode.CONFLICT,
|
||||||
"入库单并发修改"))
|
"入库单并发修改"))
|
||||||
.when(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 9L, "operator",
|
.when(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 50L, 9L, "operator",
|
||||||
"PDA-complete-finalize-fails-50", "checked");
|
"PDA-complete-finalize-fails-50", "checked");
|
||||||
|
|
||||||
assertThatThrownBy(() -> service.complete(20L,
|
assertThatThrownBy(() -> service.complete(20L,
|
||||||
|
|||||||
Reference in New Issue
Block a user