From ce9f08c7560c3f9777cc9881ec85f04a7fef18ca Mon Sep 17 00:00:00 2001 From: liuyihe <2513762445@qq.com> Date: Mon, 24 Aug 2026 14:52:50 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/InboundPdaWorkflowService.java | 6 +++++ .../service/InboundStockReceiptService.java | 23 +++++++++++-------- .../InboundPdaWorkflowServiceTest.java | 18 +++++++++++++++ .../InboundStockReceiptServiceTest.java | 18 +++++++++++++++ 4 files changed, 55 insertions(+), 10 deletions(-) diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/service/InboundPdaWorkflowService.java b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/service/InboundPdaWorkflowService.java index 3719e5f51..96a4dd50f 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/service/InboundPdaWorkflowService.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/service/InboundPdaWorkflowService.java @@ -451,6 +451,11 @@ public class InboundPdaWorkflowService { InboundPdaOperation replay = replay(customerId, "PARTIAL_COMPLETE", request.idempotencyKey(), request); if (replay != null) return read(replay, PdaResponses.Task.class); InboundPdaTask task = requireActiveTask(customerId, plan); + List unloadPhotoFileIds = unloadPhotoFileIds(task); + if (unloadPhotoFileIds.isEmpty() || task.getUnloadPhotoUploadedAt() == null + || task.getEstimatedEmptyReturnAt() == null) { + throw conflict("Upload unload photos before partially completing the PDA inbound task"); + } List lines = repository.lines(plan.getId()); List allocations = repository.allocations(plan.getId()); int expected = lines.stream().mapToInt(line -> value(line.getRequiredPackageCount())).sum(); @@ -468,6 +473,7 @@ public class InboundPdaWorkflowService { task.setStatus("PARTIAL_INBOUND"); task.setCompletedAt(now); task.setUpdatedAt(now); task.setVersion(value(task.getVersion()) + 1); repository.update(task); plan.setStatus("PARTIAL_INBOUND"); plan.setUpdatedAt(now); repository.update(plan); + unloadCompletedOutbox.publish(plan, task, unloadPhotoFileIds); PdaResponses.Task result = taskResponse(task, plan, planNo); saveOperation(customerId, task.getId(), null, "PARTIAL_COMPLETE", request.idempotencyKey(), request, result); return result; diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/service/InboundStockReceiptService.java b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/service/InboundStockReceiptService.java index cef5764b8..c428261aa 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/service/InboundStockReceiptService.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/service/InboundStockReceiptService.java @@ -72,7 +72,7 @@ public class InboundStockReceiptService { .filter(Objects::nonNull).collect(Collectors.toSet()); if (receivedAllocationIds.equals(expectedAllocationIds)) { if (!allowPartial) activateDropship(customerId, task.getId(), orderById, existing); - if (!allowPartial) completeOrders(customerId, plan, orderById, receivedAt); + completeOrders(customerId, plan, orderById, receivedAt, allowPartial); return; } throw conflict("PDA stock-in receipts are incomplete; repair the task receipts before retrying"); @@ -109,29 +109,32 @@ public class InboundStockReceiptService { throw conflict("Inbound SKU package snapshots were not fully received"); } } - if (!allowPartial) { - activateDropship(customerId, task.getId(), orderById, created); - completeOrders(customerId, plan, orderById, receivedAt); - } + if (!allowPartial) activateDropship(customerId, task.getId(), orderById, created); + completeOrders(customerId, plan, orderById, receivedAt, allowPartial); } private void completeOrders(String customerId, InboundPlan plan, Map orderById, - LocalDateTime completedAt) { + LocalDateTime completedAt, boolean partial) { + String subOrderStatus = partial ? "INBOUND_PLAN:PARTIAL_INBOUND" : "INBOUND_PLAN:COMPLETED"; for (LastlegOrder snapshot : orderById.values()) { LastlegOrder order = orders.lockById(customerId, snapshot.getId()); if (order == null) throw conflict("Inbound tail-leg order disappeared while completing stock-in"); - if ("INBOUND_COMPLETED".equals(order.getCurrentStatus())) continue; + if ("INBOUND_COMPLETED".equals(order.getCurrentStatus()) + && subOrderStatus.equals(order.getSubOrderStatus())) continue; String before = order.getCurrentStatus(); order.setCurrentStatus("INBOUND_COMPLETED"); - order.setSubOrderStatus("INBOUND_PLAN:COMPLETED"); + order.setSubOrderStatus(subOrderStatus); order.setUpdatedAt(completedAt); orders.updateById(order); LastlegOrderStatusHistory history = new LastlegOrderStatusHistory(); history.setOrderId(order.getId()); history.setFromStatus(before); history.setToStatus("INBOUND_COMPLETED"); - history.setEventId("PDA_INBOUND_COMPLETE:" + plan.getPlanNo()); - history.setRemark("PDA inbound completed and inventory stocked in"); + history.setEventId((partial ? "PDA_PARTIAL_INBOUND_COMPLETE:" : "PDA_INBOUND_COMPLETE:") + + plan.getPlanNo()); + history.setRemark(partial + ? "PDA inbound partially completed and actual inventory stocked in" + : "PDA inbound completed and inventory stocked in"); history.setCreatedAt(completedAt); orderHistories.insert(history); } diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/service/InboundPdaWorkflowServiceTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/service/InboundPdaWorkflowServiceTest.java index 7f38e5550..d009229fe 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/service/InboundPdaWorkflowServiceTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/service/InboundPdaWorkflowServiceTest.java @@ -472,6 +472,24 @@ class InboundPdaWorkflowServiceTest { assertEquals("PARTIAL_INBOUND", plan.getStatus()); verify(stockReceipts).stockIn(eq("100"), eq(plan), eq(task), eq(List.of(targetLine)), eq(List.of(target)), eq(List.of(targetPallet)), any(), eq(true)); + verify(unloadCompletedOutbox).publish(eq(plan), eq(task), eq(List.of(101L))); + } + + @Test + void partialCompleteRequiresUnloadPhotosBeforeStockIn() { + target.setPickedPackageCount(2); + task.setUnloadPhotoFileIds(null); + task.setUnloadPhotoUploadedAt(null); + task.setEstimatedEmptyReturnAt(null); + activeTask(); + + ResponseStatusException exception = assertThrows(ResponseStatusException.class, + () -> service.partialComplete("100", "LIP1", + new PdaRequests.ForceComplete("partial-no-photo", "7", "worker"))); + + assertEquals(409, exception.getStatusCode().value()); + verify(stockReceipts, never()).stockIn(any(), any(), any(), any(), any(), any(), any(), eq(true)); + verify(unloadCompletedOutbox, never()).publish(any(), any(), any()); } @Test diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/service/InboundStockReceiptServiceTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/service/InboundStockReceiptServiceTest.java index 1199e5409..75ba697ee 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/service/InboundStockReceiptServiceTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/service/InboundStockReceiptServiceTest.java @@ -179,6 +179,24 @@ class InboundStockReceiptServiceTest { assertEquals(10L, activationReceipts.getValue().get(0).getInventoryPositionId()); } + @Test + void partialStockInSynchronizesOrderWithoutActivatingDropship() { + Fixture fixture = fixture(5, 2, 2, 10); + fixture.order.setOrderType("ONE_CLICK_DROPSHIP"); + when(orders.selectBatchIds(any())).thenReturn(List.of(fixture.order)); + when(inventory.stockIn(eq("100"), eq("operator"), any(InventoryCommandRequests.StockIn.class))) + .thenReturn(new InventoryCommandResponse(8L, "STOCK_IN", 9L, 10L, 2, 0, 0, 0, false, false)); + + service.stockIn("100", fixture.plan, fixture.task, List.of(fixture.line), + List.of(fixture.allocation), List.of(fixture.pallet), LocalDateTime.now(), true); + + assertEquals("INBOUND_COMPLETED", fixture.order.getCurrentStatus()); + assertEquals("INBOUND_PLAN:PARTIAL_INBOUND", fixture.order.getSubOrderStatus()); + verify(orders).updateById(fixture.order); + verify(orderHistories).insert(any(LastlegOrderStatusHistory.class)); + verify(dropshipOrders, never()).activateAfterStockIn(any(), any(), any(), any()); + } + private Fixture fixture(int required, int planned, int picked, int quantity) { InboundPlan plan = new InboundPlan(); plan.setId(1L); plan.setPlanNo("IP-1"); InboundPdaTask task = new InboundPdaTask(); task.setId(2L); task.setOperatorId("operator");