diff --git a/firstleg/src/main/java/com/baiyue/firstleg/loading/bridge/service/LoadingModelBridgeService.java b/firstleg/src/main/java/com/baiyue/firstleg/loading/bridge/service/LoadingModelBridgeService.java index f0989d6d7..c38d4dd6e 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/loading/bridge/service/LoadingModelBridgeService.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/loading/bridge/service/LoadingModelBridgeService.java @@ -193,24 +193,24 @@ public class LoadingModelBridgeService { } public void requireStarted(long tenantId, long inboundId) { - List links = requireLinks(tenantId, inboundId); - if (links.stream().anyMatch(link -> link.allocatedPackageCount() == null || link.allocatedPackageCount() <= 0 - || !Set.of("LOADING", "COMPLETED").contains(link.loadingPlanStatus()))) { + List links = requireAllocatedLinks(tenantId, inboundId); + if (links.stream().anyMatch(link -> !Set.of("LOADING", "COMPLETED").contains(link.loadingPlanStatus()))) { throw conflict("关联柜次尚未全部开始装柜"); } } public void requireCompleted(long tenantId, long inboundId) { - List links = requireLinks(tenantId, inboundId); - if (links.stream().anyMatch(link -> link.allocatedPackageCount() == null || link.allocatedPackageCount() <= 0 - || !"COMPLETED".equals(link.loadingPlanStatus()))) { + List links = requireAllocatedLinks(tenantId, inboundId); + if (links.stream().anyMatch(link -> !"COMPLETED".equals(link.loadingPlanStatus()))) { throw conflict("关联柜次尚未全部完成装柜"); } } - private List requireLinks(long tenantId, long inboundId) { - List links = repository.byInbound(tenantId, inboundId); - if (links.isEmpty()) throw conflict("入库单尚未关联柜次装柜计划"); + private List requireAllocatedLinks(long tenantId, long inboundId) { + List links = repository.byInbound(tenantId, inboundId).stream() + .filter(link -> link.allocatedPackageCount() != null && link.allocatedPackageCount() > 0) + .toList(); + if (links.isEmpty()) throw conflict("入库单尚未关联有效柜次装柜计划"); return links; } diff --git a/firstleg/src/test/java/com/baiyue/firstleg/loading/bridge/mapper/LoadingModelBridgeMapperContractTest.java b/firstleg/src/test/java/com/baiyue/firstleg/loading/bridge/mapper/LoadingModelBridgeMapperContractTest.java new file mode 100644 index 000000000..53864fc75 --- /dev/null +++ b/firstleg/src/test/java/com/baiyue/firstleg/loading/bridge/mapper/LoadingModelBridgeMapperContractTest.java @@ -0,0 +1,18 @@ +package com.baiyue.firstleg.loading.bridge.mapper; + +import org.apache.ibatis.annotations.Select; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class LoadingModelBridgeMapperContractTest { + + @Test + void allocatedPackagesCountsOnlyActiveLoadingPlanItems() throws Exception { + String sql = String.join(" ", LoadingModelBridgeMapper.class + .getMethod("allocatedPackages", long.class, long.class, long.class) + .getAnnotation(Select.class).value()).replaceAll("\\s+", " "); + + assertThat(sql).contains("inbound_id=#{inboundId} AND deleted=0"); + } +} diff --git a/firstleg/src/test/java/com/baiyue/firstleg/loading/bridge/service/LoadingModelBridgeServiceTest.java b/firstleg/src/test/java/com/baiyue/firstleg/loading/bridge/service/LoadingModelBridgeServiceTest.java index 9059491b7..dc2210c10 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/loading/bridge/service/LoadingModelBridgeServiceTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/loading/bridge/service/LoadingModelBridgeServiceTest.java @@ -264,6 +264,42 @@ class LoadingModelBridgeServiceTest { .hasMessageContaining("尚未全部完成装柜"); } + @Test + void completionGatesIgnoreHistoricalLinksWithoutActiveAllocatedPackages() { + Link removedEditingLink = new Link(2L, 10L, 20L, 30L, "CONFIRMED", 4, + 41L, 51L, null, "EDITING", 6, 0, LocalDateTime.now()); + when(repository.byInbound(10, 20)).thenReturn(List.of( + removedEditingLink, link("CONFIRMED", 4, "LOADING", 7, 5))); + + service.requireStarted(10, 20); + + when(repository.byInbound(10, 20)).thenReturn(List.of( + removedEditingLink, link("CONFIRMED", 4, "COMPLETED", 8, 5))); + + service.requireCompleted(10, 20); + } + + @Test + void completionGateStillRejectsAnActivelyAllocatedContainerThatHasNotStarted() { + when(repository.byInbound(10, 20)).thenReturn(List.of( + link("CONFIRMED", 4, "LOADING", 7, 5), + new Link(2L, 10L, 20L, 30L, "CONFIRMED", 4, 41L, 51L, null, + "EDITING", 6, 3, LocalDateTime.now()))); + + assertThatThrownBy(() -> service.requireStarted(10, 20)) + .hasMessageContaining("尚未全部开始装柜"); + } + + @Test + void completionGateRejectsAnInboundWithOnlyHistoricalZeroAllocationLinks() { + when(repository.byInbound(10, 20)).thenReturn(List.of( + new Link(2L, 10L, 20L, 30L, "CONFIRMED", 4, 41L, 51L, null, + "EDITING", 6, 0, LocalDateTime.now()))); + + assertThatThrownBy(() -> service.requireStarted(10, 20)) + .hasMessageContaining("尚未关联有效柜次装柜计划"); + } + @Test void bindsConfirmingPreloadToRunningPlanAndConfirmsIt() { when(repository.byPair(10, 20, 40)).thenReturn(null);