diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/service/ReceivingContainerService.java b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/service/ReceivingContainerService.java index 38dd06617..3dd5353c4 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/service/ReceivingContainerService.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/service/ReceivingContainerService.java @@ -163,18 +163,19 @@ public class ReceivingContainerService { String activeOperatorKey = activeOperatorKey(operatorKey); ReceivingContainerSession active = sessions.lockActiveByOperator(activeOperatorKey); requireSessionWarehouse(active, overseasWarehouseId); - if (active != null) { - if (!containerNo.equals(active.getContainerNo())) throw conflict("Finish the current container before selecting another one"); - WarehouseResourceGateway.WarehouseReference receivingWarehouse = - warehouse.requireActiveSharedWarehouse(overseasWarehouseId); - ReceivingContainerResponses.Session result = snapshot(active, containerPlans); - save(active, "PREPARE", request.idempotencyKey(), command, result); return result; + if (active != null && !containerNo.equals(active.getContainerNo())) { + throw conflict("Finish the current container before selecting another one"); } - if (sessions.lockActiveByContainer(activeContainerKey(containerNo)) != null) { + if (active == null && sessions.lockActiveByContainer(activeContainerKey(containerNo)) != null) { throw conflict("Container is already signed or prepared by another account"); } WarehouseResourceGateway.WarehouseReference receivingWarehouse = warehouse.requireActiveSharedWarehouse(overseasWarehouseId); + bindReceivingWarehouse(containerPlans, receivingWarehouse.warehouseId()); + if (active != null) { + ReceivingContainerResponses.Session result = snapshot(active, containerPlans); + save(active, "PREPARE", request.idempotencyKey(), command, result); return result; + } LocalDateTime now = LocalDateTime.now(); ReceivingContainerSession session = new ReceivingContainerSession(); session.setId(IdWorker.getId()); session.setCustomerId(customerId); session.setContainerNo(containerNo); @@ -890,7 +891,23 @@ public class ReceivingContainerService { if (assigned.size() > 1) { throw conflict("Container plans have inconsistent receiving warehouse assignments"); } - return assigned.size() == 1 && assigned.contains(overseasWarehouseId); + return assigned.isEmpty() || assigned.size() == 1 && assigned.contains(overseasWarehouseId); + } + + private void bindReceivingWarehouse(List containerPlans, long overseasWarehouseId) { + for (InboundPlan plan : containerPlans) { + if (plan.getOverseasWarehouseId() != null) { + if (!Objects.equals(plan.getOverseasWarehouseId(), overseasWarehouseId)) { + throw conflict("当前柜已分配至其他海外接柜仓,无法在本仓操作"); + } + continue; + } + plan.setOverseasWarehouseId(overseasWarehouseId); + plan.setUpdatedAt(LocalDateTime.now()); + if (plans.updateById(plan) != 1) { + throw conflict("Receiving warehouse assignment changed concurrently"); + } + } } private void requireReceivingAvailable(String customerId, String containerNo) { diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/service/ReceivingContainerServiceBehaviorTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/service/ReceivingContainerServiceBehaviorTest.java index 8452b51d5..0a72f267c 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/service/ReceivingContainerServiceBehaviorTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/service/ReceivingContainerServiceBehaviorTest.java @@ -216,6 +216,47 @@ class ReceivingContainerServiceBehaviorTest { .containsExactly("CONT-A", "CONT-B"); } + @Test + void visibleCandidateWithUnassignedPlanCanOpenDetailInTheSameWarehouse() { + InboundPlan unassigned = plan("TSTU0000204"); + unassigned.setOverseasWarehouseId(null); + InboundPlanLine planLine = line("FBA"); + LastlegOrder order = containerOrder(1L, "100", "TSTU0000204", "BOOKING-204", "IN-204"); + when(plans.selectList(any())).thenReturn(List.of(unassigned)); + when(lines.selectList(any())).thenReturn(List.of(planLine)); + when(orders.selectList(any())).thenReturn(List.of(order)); + when(orders.selectById(1L)).thenReturn(order); + when(warehouse.requireActiveSharedWarehouse(100L)).thenReturn(warehouseReference()); + + var candidate = service.candidates("7", 100L).get(0); + var detail = service.detail("7", 100L, "TSTU0000204"); + + assertThat(candidate.operable()).isTrue(); + assertThat(detail.overseasWarehouseId()).isEqualTo(100L); + assertThat(detail.overseasWarehouseCode()).isEqualTo("SHARED-LAX"); + } + + @Test + void prepareAtomicallyBindsUnassignedPlansToThePickerWarehouse() { + InboundPlan unassigned = plan("TSTU0195472"); + unassigned.setOverseasWarehouseId(null); + InboundPlanLine planLine = line("FBA"); + LastlegOrder order = containerOrder(1L, "100", "TSTU0195472", "BOOKING-472", "IN-472"); + when(plans.selectList(any())).thenReturn(List.of(unassigned)); + when(plans.updateById(unassigned)).thenReturn(1); + when(lines.selectList(any())).thenReturn(List.of(planLine)); + when(orders.selectList(any())).thenReturn(List.of(order)); + when(orders.selectById(1L)).thenReturn(order); + when(warehouse.requireActiveSharedWarehouse(100L)).thenReturn(warehouseReference()); + + var result = service.prepare("7", "receiver", 100L, "TSTU0195472", + new ReceivingContainerRequests.Action("bind-warehouse")); + + assertThat(result.overseasWarehouseId()).isEqualTo(100L); + assertThat(unassigned.getOverseasWarehouseId()).isEqualTo(100L); + verify(plans).updateById(unassigned); + } + @Test void candidatesHideContainersReservedByAnotherOperator() { InboundPlan available = plan("CONT-A");