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);