fix(firstleg): refresh inbound picking progress after task completion
This commit is contained in:
@@ -690,7 +690,6 @@ public class LoadingPickService {
|
|||||||
}
|
}
|
||||||
runCompletionStage("PLAN_COMPLETE", () -> loadingListService.finishFromPda(task.getLoadingPlanId(), taskId,
|
runCompletionStage("PLAN_COMPLETE", () -> loadingListService.finishFromPda(task.getLoadingPlanId(), taskId,
|
||||||
command.getOperatorId(), command.getOperatorName()));
|
command.getOperatorId(), command.getOperatorName()));
|
||||||
repository.syncInboundProjections(taskId, command.getOperatorId(), command.getOperatorName());
|
|
||||||
runCompletionStage("COMPLETED_EVENT", () -> loadingListService.publishCompletedEvent(task.getLoadingPlanId()));
|
runCompletionStage("COMPLETED_EVENT", () -> loadingListService.publishCompletedEvent(task.getLoadingPlanId()));
|
||||||
for (GroupSnapshot group : taskGroups) {
|
for (GroupSnapshot group : taskGroups) {
|
||||||
runCompletionStage("COLOAD_CONSUME", () -> coloadGroups.consumeWholeGroup(group.groupId(),
|
runCompletionStage("COLOAD_CONSUME", () -> coloadGroups.consumeWholeGroup(group.groupId(),
|
||||||
@@ -703,6 +702,7 @@ public class LoadingPickService {
|
|||||||
&& repository.completeAccepted(taskId, command.getOperatorId(), command.getOperatorName()) != 1) {
|
&& repository.completeAccepted(taskId, command.getOperatorId(), command.getOperatorName()) != 1) {
|
||||||
throw new CompletionProcessingException("TASK_FINALIZE", conflict("PDA任务完成状态已变化"));
|
throw new CompletionProcessingException("TASK_FINALIZE", conflict("PDA任务完成状态已变化"));
|
||||||
}
|
}
|
||||||
|
repository.syncInboundProjections(taskId, command.getOperatorId(), command.getOperatorName());
|
||||||
if (!completionCommands.complete(command.getId(), worker)) {
|
if (!completionCommands.complete(command.getId(), worker)) {
|
||||||
throw new CompletionProcessingException("COMMAND_FINALIZE", conflict("装柜完成命令租约已变化"));
|
throw new CompletionProcessingException("COMMAND_FINALIZE", conflict("装柜完成命令租约已变化"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1060,6 +1060,41 @@ class LoadingPickServiceTest {
|
|||||||
verify(completionCommands).complete(900L, "worker-1");
|
verify(completionCommands).complete(900L, "worker-1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(strings = {"COMPLETING", "COMPLETED"})
|
||||||
|
void workerRefreshesInboundProgressAfterTaskCompletionIncludingRetry(String initialTaskStatus) {
|
||||||
|
LoadingPickTaskEntity task = task();
|
||||||
|
task.setTaskStatus(initialTaskStatus);
|
||||||
|
when(repository.task(20L)).thenReturn(task);
|
||||||
|
when(repository.inboundOwners(20L)).thenReturn(List.of(
|
||||||
|
new LoadingPickItemMapper.InboundOwner(101L, 50L)));
|
||||||
|
when(repository.allPlanItemsPicked(10L)).thenReturn(true);
|
||||||
|
when(repository.approve(20L, 9L, "operator", "checked")).thenReturn(1);
|
||||||
|
when(repository.completeAccepted(20L, 9L, "operator")).thenAnswer(invocation -> {
|
||||||
|
task.setTaskStatus("COMPLETED");
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
|
AtomicReference<String> projectedStatus = new AtomicReference<>();
|
||||||
|
doAnswer(invocation -> {
|
||||||
|
projectedStatus.set("COMPLETED".equals(task.getTaskStatus()) ? "COMPLETED" : "IN_PROGRESS");
|
||||||
|
return null;
|
||||||
|
}).when(repository).syncInboundProjections(20L, 9L, "operator");
|
||||||
|
when(completionCommands.complete(925L, "worker-projection")).thenReturn(true);
|
||||||
|
|
||||||
|
service.processCompletion(completionCommand(925L, "async-projection"), "worker-projection");
|
||||||
|
|
||||||
|
assertThat(projectedStatus.get()).isEqualTo("COMPLETED");
|
||||||
|
verify(repository, times(1)).syncInboundProjections(20L, 9L, "operator");
|
||||||
|
if ("COMPLETING".equals(initialTaskStatus)) {
|
||||||
|
InOrder finalization = inOrder(repository, completionCommands);
|
||||||
|
finalization.verify(repository).completeAccepted(20L, 9L, "operator");
|
||||||
|
finalization.verify(repository).syncInboundProjections(20L, 9L, "operator");
|
||||||
|
finalization.verify(completionCommands).complete(925L, "worker-projection");
|
||||||
|
} else {
|
||||||
|
verify(repository, never()).completeAccepted(anyLong(), anyLong(), anyString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ParameterizedTest(name = "truck state {0} never participates in PDA completion")
|
@ParameterizedTest(name = "truck state {0} never participates in PDA completion")
|
||||||
@ValueSource(strings = {"NO_APPOINTMENT", "DRAFT", "IN_PROGRESS", "COMPLETED"})
|
@ValueSource(strings = {"NO_APPOINTMENT", "DRAFT", "IN_PROGRESS", "COMPLETED"})
|
||||||
void completionWorkerIsIndependentOfEveryTruckAppointmentState(String truckState) {
|
void completionWorkerIsIndependentOfEveryTruckAppointmentState(String truckState) {
|
||||||
|
|||||||
@@ -121,6 +121,8 @@ test('HTTP chain refreshes after stale inventory and keeps one stable PDA identi
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('downstream finalizer uses the frozen loading complete contract without truck coupling', () => {
|
test('downstream finalizer uses the frozen loading complete contract without truck coupling', () => {
|
||||||
|
assert.match(finalize, /unifiedDetail\?\.summary\?\.loading\?\.status === 'COMPLETED'/)
|
||||||
|
assert.match(finalize, /unifiedRow\?\.loading\?\.status === 'COMPLETED'/)
|
||||||
assert.match(convergence, /loadingPlan\?\.status === 'LOADING'/)
|
assert.match(convergence, /loadingPlan\?\.status === 'LOADING'/)
|
||||||
assert.match(convergence, /!operations\.some\(\(item\) => item\.actionType === 'COMPLETE_LOADING'\)/)
|
assert.match(convergence, /!operations\.some\(\(item\) => item\.actionType === 'COMPLETE_LOADING'\)/)
|
||||||
assert.match(customs, /\['AWAITING_REVIEW', 'LOADED'\]/)
|
assert.match(customs, /\['AWAITING_REVIEW', 'LOADED'\]/)
|
||||||
|
|||||||
@@ -429,6 +429,9 @@ export async function run(context) {
|
|||||||
id(unifiedDetail?.summary?.orderId) === orderId && unifiedDetail?.summary?.mainStatus === 'LOADED'
|
id(unifiedDetail?.summary?.orderId) === orderId && unifiedDetail?.summary?.mainStatus === 'LOADED'
|
||||||
&& Boolean(orderNo),
|
&& Boolean(orderNo),
|
||||||
`orderId=${id(unifiedDetail?.summary?.orderId) || 'missing'}, mainStatus=${unifiedDetail?.summary?.mainStatus || 'missing'}, orderNo=${orderNo || 'missing'}`, OPTIONS)
|
`orderId=${id(unifiedDetail?.summary?.orderId) || 'missing'}, mainStatus=${unifiedDetail?.summary?.mainStatus || 'missing'}, orderNo=${orderNo || 'missing'}`, OPTIONS)
|
||||||
|
context.assert(MODULE, '装柜完成后订单详情拣货汇总同步完成', unifiedDetailResponse,
|
||||||
|
unifiedDetail?.summary?.loading?.status === 'COMPLETED',
|
||||||
|
`loadingStatus=${unifiedDetail?.summary?.loading?.status || 'missing'}`, OPTIONS)
|
||||||
if (id(unifiedDetail?.summary?.orderId) !== orderId || unifiedDetail?.summary?.mainStatus !== 'LOADED' || !orderNo) return
|
if (id(unifiedDetail?.summary?.orderId) !== orderId || unifiedDetail?.summary?.mainStatus !== 'LOADED' || !orderNo) return
|
||||||
const unifiedListPath = `/api/platform/v1/orders?page=1&size=100&tenantId=${encodeURIComponent(tenantId)}&orderNo=${encodeURIComponent(orderNo)}`
|
const unifiedListPath = `/api/platform/v1/orders?page=1&size=100&tenantId=${encodeURIComponent(tenantId)}&orderNo=${encodeURIComponent(orderNo)}`
|
||||||
const unifiedListResponse = await context.request('GET', unifiedListPath)
|
const unifiedListResponse = await context.request('GET', unifiedListPath)
|
||||||
@@ -437,6 +440,9 @@ export async function run(context) {
|
|||||||
context.assert(MODULE, '统一订单列表投影同一订单为 LOADED', unifiedListResponse,
|
context.assert(MODULE, '统一订单列表投影同一订单为 LOADED', unifiedListResponse,
|
||||||
Boolean(unifiedRow) && unifiedRow?.mainStatus === 'LOADED',
|
Boolean(unifiedRow) && unifiedRow?.mainStatus === 'LOADED',
|
||||||
`orderId=${orderId}, found=${Boolean(unifiedRow)}, mainStatus=${unifiedRow?.mainStatus || 'missing'}`, OPTIONS)
|
`orderId=${orderId}, found=${Boolean(unifiedRow)}, mainStatus=${unifiedRow?.mainStatus || 'missing'}`, OPTIONS)
|
||||||
|
context.assert(MODULE, '装柜完成后订单列表拣货汇总同步完成', unifiedListResponse,
|
||||||
|
unifiedRow?.loading?.status === 'COMPLETED',
|
||||||
|
`loadingStatus=${unifiedRow?.loading?.status || 'missing'}`, OPTIONS)
|
||||||
|
|
||||||
const evidencePath = `/api/platform/v1/first-leg/pda-loading/tasks/${encodeURIComponent(id(fixture.canonicalPdaTaskId))}/evidence?warehouseId=${encodeURIComponent(id(fixture.warehouseId))}`
|
const evidencePath = `/api/platform/v1/first-leg/pda-loading/tasks/${encodeURIComponent(id(fixture.canonicalPdaTaskId))}/evidence?warehouseId=${encodeURIComponent(id(fixture.warehouseId))}`
|
||||||
const immutableEvidenceResponse = await context.request('GET', evidencePath)
|
const immutableEvidenceResponse = await context.request('GET', evidencePath)
|
||||||
|
|||||||
Reference in New Issue
Block a user