fix: enforce dropship pallet count for inbound plans
This commit is contained in:
+4
@@ -75,6 +75,10 @@ public interface InboundPlanOrderMapper {
|
||||
String findSourceContainerBookingId(@Param("customerId") String customerId,
|
||||
@Param("orderId") Long orderId);
|
||||
|
||||
@Select("SELECT order_type FROM ll_order WHERE customer_id=#{customerId} AND id=#{orderId} LIMIT 1")
|
||||
String findOrderType(@Param("customerId") String customerId,
|
||||
@Param("orderId") Long orderId);
|
||||
|
||||
@Select("SELECT o.id AS orderId,o.customer_id AS customerId,o.container_no AS containerNo," +
|
||||
"o.inbound_code AS inboundCode,o.current_status AS currentStatus,o.inbound_order_no AS inboundOrderNo," +
|
||||
"COALESCE(o.volume_cbm,0) AS volumeCbm,COALESCE(o.package_count,0) AS packageCount," +
|
||||
|
||||
+21
-4
@@ -130,10 +130,23 @@ public class InboundPlanService {
|
||||
WarehouseResourceGateway.WarehouseReference receivingWarehouse =
|
||||
requiredReceivingWarehouse(containerWarehouseId);
|
||||
validateOrders(snapshots);
|
||||
int minimumPalletCount = suggestedPalletCount(order);
|
||||
int sourcePalletCount = Objects.requireNonNullElse(
|
||||
sourcePalletMapper.palletNos(customerId, request.orderId()), List.<String>of()).size();
|
||||
minimumPalletCount = Math.max(minimumPalletCount, sourcePalletCount);
|
||||
String orderType = trimToNull(orderMapper.findOrderType(customerId, request.orderId()));
|
||||
int sourcePalletCount = "ONE_CLICK_DROPSHIP".equals(orderType)
|
||||
? dropshipOrders.palletSnapshots(customerId, request.orderId()).size()
|
||||
: Objects.requireNonNullElse(
|
||||
sourcePalletMapper.palletNos(customerId, request.orderId()), List.<String>of()).size();
|
||||
if ("ONE_CLICK_DROPSHIP".equals(orderType)) {
|
||||
if (sourcePalletCount <= 0) {
|
||||
throw new ResponseStatusException(HttpStatus.CONFLICT,
|
||||
"一件代发订单缺少不可变来源托盘快照,不能生成入库计划");
|
||||
}
|
||||
if (request.palletCount() != sourcePalletCount) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
|
||||
"一件代发人工计划托数必须与头程封托托盘数一致:" + sourcePalletCount);
|
||||
}
|
||||
}
|
||||
int minimumPalletCount = "ONE_CLICK_DROPSHIP".equals(orderType)
|
||||
? sourcePalletCount : Math.max(suggestedPalletCount(order), sourcePalletCount);
|
||||
if (request.palletCount() < minimumPalletCount) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
|
||||
"人工计划托数不能少于柜级预览建议托数 " + minimumPalletCount);
|
||||
@@ -272,6 +285,10 @@ public class InboundPlanService {
|
||||
int targetPalletCount = Objects.requireNonNullElse(row.plannedPalletCount(),
|
||||
Objects.requireNonNullElse(suggested, 0));
|
||||
int supplementPalletCount = Math.max(0, targetPalletCount - sourcePalletCount);
|
||||
if (planned && "ONE_CLICK_DROPSHIP".equals(row.orderType())
|
||||
&& targetPalletCount != sourcePalletCount) {
|
||||
blockers.add("DROPSHIP_PALLET_COUNT_MISMATCH");
|
||||
}
|
||||
if (!planned && (suggested == null || suggested <= 0)) blockers.add("PALLET_COUNT_NOT_READY");
|
||||
String destinationType = destinationType(row);
|
||||
String fbaWarehouseCode = isFbaDestination(row) ? trimToNull(row.fbaWarehouseCode()) : null;
|
||||
|
||||
+19
@@ -120,6 +120,25 @@ class ContainerInboundPlanPreviewTest {
|
||||
assertEquals("RESOURCE_PENDING", result.groups().get(0).orders().get(0).readiness());
|
||||
}
|
||||
|
||||
@Test
|
||||
void blocksExistingDropshipPlanWhosePalletCountDiffersFromFrozenSourcePallets() {
|
||||
when(orderMapper.containerPreviewOrders("BOOKING-DROPSHIP-MISMATCH")).thenReturn(List.of(
|
||||
row(1L, "LM-DROPSHIP", "ONE_CLICK_DROPSHIP", "OVERSEAS", "US-LAX-01", "洛杉矶仓", 2,
|
||||
"IP-DROPSHIP", 2, 1, "PENDING_ASSIGNMENT", 0)
|
||||
));
|
||||
when(dropshipOrders.palletSnapshots("100", 1L)).thenReturn(List.of(
|
||||
new DropshipPalletSnapshot("DP-260914-0001", 10, List.of())));
|
||||
|
||||
var result = service.previewContainer("BOOKING-DROPSHIP-MISMATCH");
|
||||
var order = result.groups().get(0).orders().get(0);
|
||||
|
||||
assertFalse(result.ready());
|
||||
assertEquals("BLOCKED", order.readiness());
|
||||
assertEquals(List.of("DROPSHIP_PALLET_COUNT_MISMATCH"), order.blockers());
|
||||
assertEquals(1, order.sourcePalletCount());
|
||||
assertEquals(2, order.targetPalletCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void exposesHeadLegExistingSupplementAndFinalPalletCountsWithoutBlocking() {
|
||||
when(orderMapper.containerPreviewOrders("BOOKING-SUPPLEMENT")).thenReturn(List.of(
|
||||
|
||||
+37
@@ -183,6 +183,43 @@ class InboundPlanServiceTest {
|
||||
verify(commandMapper, never()).insert(any(com.baiyue.lastleg.entity.LastlegOrderCommandRecord.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void oneClickDropshipManualPlanMustKeepTheFrozenSourcePalletCount() {
|
||||
SourceOrderSnapshot source = order(2L, "RK-2", "3.3650", "WH-A");
|
||||
when(orderMapper.lockOrder("100", 2L)).thenReturn(List.of(source));
|
||||
when(orderMapper.findOrderType("100", 2L)).thenReturn("ONE_CLICK_DROPSHIP");
|
||||
when(dropshipOrders.palletSnapshots("100", 2L)).thenReturn(List.of(
|
||||
new DropshipPalletSnapshot("DP-260914-0001", 10L, List.of())));
|
||||
|
||||
ResponseStatusException error = assertThrows(ResponseStatusException.class, () -> service.convertManual(
|
||||
new ManualInboundPlanRequest("100", 2L, 0, LocalDate.of(2026, 9, 15),
|
||||
2, "WOOD", false, false, false, null), "dropship-pallet-mismatch", "900"));
|
||||
|
||||
assertEquals(400, error.getStatusCode().value());
|
||||
assertEquals("一件代发人工计划托数必须与头程封托托盘数一致:1", error.getReason());
|
||||
verify(planMapper, never()).insert(any(InboundPlan.class));
|
||||
verify(commandMapper, never()).insert(any(com.baiyue.lastleg.entity.LastlegOrderCommandRecord.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void oneClickDropshipUsesFrozenSourcePalletCountInsteadOfVolumeSuggestion() {
|
||||
SourceOrderSnapshot source = order(2L, "RK-2", "3.3650", "WH-A");
|
||||
when(orderMapper.lockOrder("100", 2L)).thenReturn(List.of(source));
|
||||
when(orderMapper.findOrderType("100", 2L)).thenReturn("ONE_CLICK_DROPSHIP");
|
||||
when(dropshipOrders.palletSnapshots("100", 2L)).thenReturn(List.of(
|
||||
new DropshipPalletSnapshot("DP-260914-0001", 10L, List.of())));
|
||||
when(orderMapper.markPlannedManual(eq("100"), eq(2L), any(), eq(1), eq(false), eq(0), any()))
|
||||
.thenReturn(1);
|
||||
|
||||
var result = service.convertManual(new ManualInboundPlanRequest("100", 2L, 0,
|
||||
LocalDate.of(2026, 9, 15), 1, "WOOD", false, false, false, null),
|
||||
"dropship-exact-pallet-count", "900");
|
||||
|
||||
assertEquals(1, result.palletCount());
|
||||
verify(planMapper).insert(any(InboundPlan.class));
|
||||
verify(palletMapper).insert(any(InboundPlanPallet.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void containerConversionBindsRealWarehousePalletsBackToSignedManualPlanBeforeReceiving() {
|
||||
InboundPlan plan = existingPlan("100", 2L, "IP-MANUAL");
|
||||
|
||||
Reference in New Issue
Block a user