fix: isolate invalid receiving candidates
This commit is contained in:
+9
-7
@@ -443,13 +443,13 @@ public class ReceivingContainerService {
|
|||||||
try { assignedWarehouses.add(Long.valueOf(value)); }
|
try { assignedWarehouses.add(Long.valueOf(value)); }
|
||||||
catch (NumberFormatException ignored) { assignedWarehouses.add(Long.MIN_VALUE); }
|
catch (NumberFormatException ignored) { assignedWarehouses.add(Long.MIN_VALUE); }
|
||||||
});
|
});
|
||||||
if (assignedWarehouses.size() > 1) throw conflict("柜内接柜仓分配不一致");
|
if (assignedWarehouses.size() > 1) return null;
|
||||||
if (assignedWarehouses.size() != 1 || !assignedWarehouses.contains(overseasWarehouseId)) return null;
|
if (assignedWarehouses.size() != 1 || !assignedWarehouses.contains(overseasWarehouseId)) return null;
|
||||||
Set<String> warehouseCodes = orderFacts.stream().map(LastlegOrder::getOverseasWarehouseCode)
|
Set<String> warehouseCodes = orderFacts.stream().map(LastlegOrder::getOverseasWarehouseCode)
|
||||||
.map(ReceivingContainerService::trim).filter(Objects::nonNull).collect(Collectors.toSet());
|
.map(ReceivingContainerService::trim).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||||
Set<String> warehouseNames = orderFacts.stream().map(LastlegOrder::getOverseasWarehouseName)
|
Set<String> warehouseNames = orderFacts.stream().map(LastlegOrder::getOverseasWarehouseName)
|
||||||
.map(ReceivingContainerService::trim).filter(Objects::nonNull).collect(Collectors.toSet());
|
.map(ReceivingContainerService::trim).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||||
if (warehouseCodes.size() != 1 || warehouseNames.size() != 1) throw conflict("柜内接柜仓业务快照不一致");
|
boolean warehouseSnapshotConsistent = warehouseCodes.size() == 1 && warehouseNames.size() == 1;
|
||||||
Set<String> customers = orderFacts.stream().map(LastlegOrder::getCustomerId).collect(Collectors.toSet());
|
Set<String> customers = orderFacts.stream().map(LastlegOrder::getCustomerId).collect(Collectors.toSet());
|
||||||
String containerNo = orderFacts.stream().map(LastlegOrder::getContainerNo).filter(Objects::nonNull).findFirst().orElse(null);
|
String containerNo = orderFacts.stream().map(LastlegOrder::getContainerNo).filter(Objects::nonNull).findFirst().orElse(null);
|
||||||
int totalOrders = orderFacts.size();
|
int totalOrders = orderFacts.size();
|
||||||
@@ -468,10 +468,11 @@ public class ReceivingContainerService {
|
|||||||
&& containerPlans.stream().allMatch(plan -> "COMPLETED".equals(plan.getStatus()));
|
&& containerPlans.stream().allMatch(plan -> "COMPLETED".equals(plan.getStatus()));
|
||||||
String customerId = customers.size() == 1 ? customers.iterator().next() : null;
|
String customerId = customers.size() == 1 ? customers.iterator().next() : null;
|
||||||
boolean shippingReady = customerId != null && shippingGates.receivingAvailable(customerId, containerNo) == 1;
|
boolean shippingReady = customerId != null && shippingGates.receivingAvailable(customerId, containerNo) == 1;
|
||||||
boolean operable = allPlansReady && shippingReady;
|
boolean operable = allPlansReady && shippingReady && warehouseSnapshotConsistent;
|
||||||
int pending = Math.max(0, totalOrders - plannedOrders);
|
int pending = Math.max(0, totalOrders - plannedOrders);
|
||||||
String blockReason = operable ? null : (!allPlansReady
|
String blockReason = operable ? null : (!warehouseSnapshotConsistent
|
||||||
? plannedOrders + "个入仓码已生成计划," + pending + "个订单未操作/缺入仓码"
|
? "柜内接柜仓业务快照不一致"
|
||||||
|
: !allPlansReady ? plannedOrders + "个入仓码已生成计划," + pending + "个订单未操作/缺入仓码"
|
||||||
: "柜号尚未达到海外收货可操作节点");
|
: "柜号尚未达到海外收货可操作节点");
|
||||||
LocalDateTime arrival = orderFacts.stream().map(LastlegOrder::getEstimatedWarehouseAt)
|
LocalDateTime arrival = orderFacts.stream().map(LastlegOrder::getEstimatedWarehouseAt)
|
||||||
.filter(Objects::nonNull).min(LocalDateTime::compareTo).orElse(null);
|
.filter(Objects::nonNull).min(LocalDateTime::compareTo).orElse(null);
|
||||||
@@ -492,8 +493,9 @@ public class ReceivingContainerService {
|
|||||||
inboundValues.stream().mapToInt(ReceivingContainerResponses.Inbound::plannedPalletCount).sum(),
|
inboundValues.stream().mapToInt(ReceivingContainerResponses.Inbound::plannedPalletCount).sum(),
|
||||||
inboundValues.stream().mapToInt(ReceivingContainerResponses.Inbound::totalPackages).sum(),
|
inboundValues.stream().mapToInt(ReceivingContainerResponses.Inbound::totalPackages).sum(),
|
||||||
inboundValues.stream().mapToInt(ReceivingContainerResponses.Inbound::receivedPackages).sum());
|
inboundValues.stream().mapToInt(ReceivingContainerResponses.Inbound::receivedPackages).sum());
|
||||||
return new ContainerReadiness(sourceContainerBookingId, containerNo, warehouseCodes.iterator().next(),
|
return new ContainerReadiness(sourceContainerBookingId, containerNo,
|
||||||
warehouseNames.iterator().next(), totalOrders, inboundCodes,
|
warehouseCodes.size() == 1 ? warehouseCodes.iterator().next() : null,
|
||||||
|
warehouseNames.size() == 1 ? warehouseNames.iterator().next() : null, totalOrders, inboundCodes,
|
||||||
totalOrders - inboundCodes, planCount, plannedOrders, pending, allPlansReady, allPlansCompleted, shippingReady, operable,
|
totalOrders - inboundCodes, planCount, plannedOrders, pending, allPlansReady, allPlansCompleted, shippingReady, operable,
|
||||||
blockReason, arrival, factHash, totals);
|
blockReason, arrival, factHash, totals);
|
||||||
}
|
}
|
||||||
|
|||||||
+21
@@ -176,6 +176,27 @@ class ReceivingContainerServiceBehaviorTest {
|
|||||||
.containsExactly("CONT-EARLY", "CONT-LATE", "CONT-NULL", "CONT-BLOCKED");
|
.containsExactly("CONT-EARLY", "CONT-LATE", "CONT-NULL", "CONT-BLOCKED");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void invalidWarehouseSnapshotOnlyBlocksItsContainerWithoutFailingHealthyCandidates() {
|
||||||
|
InboundPlan brokenPlan = plan("CONT-BROKEN");
|
||||||
|
InboundPlan healthyPlan = plan("CONT-HEALTHY"); healthyPlan.setId(3L);
|
||||||
|
InboundPlanLine brokenLine = line("FBA");
|
||||||
|
InboundPlanLine healthyLine = line("FBA"); healthyLine.setId(11L); healthyLine.setOrderId(2L); healthyLine.setPlanId(3L);
|
||||||
|
LastlegOrder broken = containerOrder(1L, "100", "CONT-BROKEN", "BOOKING-A", "IN-1");
|
||||||
|
broken.setOverseasWarehouseCode(null);
|
||||||
|
LastlegOrder healthy = containerOrder(2L, "100", "CONT-HEALTHY", "BOOKING-B", "IN-2");
|
||||||
|
when(plans.selectList(any())).thenReturn(List.of(brokenPlan, healthyPlan));
|
||||||
|
when(lines.selectList(any())).thenReturn(List.of(brokenLine, healthyLine));
|
||||||
|
when(orders.selectList(any())).thenReturn(List.of(broken, healthy));
|
||||||
|
|
||||||
|
var result = service.candidates("7", 100L);
|
||||||
|
|
||||||
|
assertThat(result).extracting("containerNo").containsExactly("CONT-HEALTHY", "CONT-BROKEN");
|
||||||
|
assertThat(result.get(0).operable()).isTrue();
|
||||||
|
assertThat(result.get(1).operable()).isFalse();
|
||||||
|
assertThat(result.get(1).blockReason()).isEqualTo("柜内接柜仓业务快照不一致");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void candidatesIncludeUnassignedAndSameWarehouseButHideOtherWarehouse() {
|
void candidatesIncludeUnassignedAndSameWarehouseButHideOtherWarehouse() {
|
||||||
InboundPlan unassigned = plan("CONT-A");
|
InboundPlan unassigned = plan("CONT-A");
|
||||||
|
|||||||
Reference in New Issue
Block a user