From 774f0472e8b2e6d0df3b827b3ff60288352014f8 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Fri, 11 Sep 2026 15:39:38 +0800 Subject: [PATCH] feat: explain supplemental pallet counts --- lastleg-service/AGENTS.md | 2 + .../ContainerInboundPlanPreviewResponse.java | 7 ++++ .../mapper/InboundPlanOrderMapper.java | 6 ++- .../service/InboundPlanService.java | 23 +++++++++--- .../openapi/overseas-business-v1.yaml | 21 +++++++---- ...rInboundPlanConversionCoordinatorTest.java | 37 ++++++++++--------- .../ContainerInboundPlanPreviewTest.java | 27 +++++++++++++- 7 files changed, 89 insertions(+), 34 deletions(-) diff --git a/lastleg-service/AGENTS.md b/lastleg-service/AGENTS.md index 202920809..7cf2c759c 100644 --- a/lastleg-service/AGENTS.md +++ b/lastleg-service/AGENTS.md @@ -18,6 +18,8 @@ 人工计划自动生成托盘与资源重试:`inboundplan/controller/InboundPlanController.java` → `inboundplan/service/ContainerInboundPlanConversionCoordinator.java` 的 `convertManualAndAutoMaterialize`;保存单票后重读全柜预览,全员为 `PLANNED/RESOURCE_PENDING`、预览就绪且存在待绑定资源时自动调用柜级 `confirm`。一票柜立即触发,多票柜在最后一票齐全后触发;资源异常沿 `resourcePendingPlanCount` 和原柜级预览/确认入口重试,不新增独立正常人工步骤。回归入口为 `src/test/java/com/baiyue/lastleg/inboundplan/service/ContainerInboundPlanConversionCoordinatorTest.java`。 +柜级预览同时返回 `sourcePalletCount/supplementPalletCount/targetPalletCount`:头程冻结托盘码原样保留,海外仓最终托数较多时按同柜同 FBA 编码序列补足差额,不因头尾装托密度不同阻断;页面必须分别展示头程已有、海外补充和最终托数,不能把最终总数误称为“本次新增”。 + 头程来源托盘快照:`service/LoadingCompletedConsumer.java`、`mapper/LastlegSourcePalletMapper.java`;来源标签随装柜完成事件落入 `ll_order_source_pallet`,后续由 `inboundplan/service/InboundPlanService.java` 与 `ContainerInboundPlanConversionCoordinator.java` 传递给海外资源批量预留。排查标签跨区丢失时沿此链路核对,不能在尾程页面重新生成头程托盘码。 | 功能 | 入口 | diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/dto/ContainerInboundPlanPreviewResponse.java b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/dto/ContainerInboundPlanPreviewResponse.java index 145aacd90..639b9851e 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/dto/ContainerInboundPlanPreviewResponse.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/dto/ContainerInboundPlanPreviewResponse.java @@ -17,6 +17,8 @@ public record ContainerInboundPlanPreviewResponse( int blockedOrderCount, int pendingOrderCount, int pendingPalletCount, + int sourcePalletCount, + int supplementPalletCount, int existingPlannedPalletCount, int totalSuggestedPalletCount, int totalPlannedPalletCount, @@ -31,6 +33,8 @@ public record ContainerInboundPlanPreviewResponse( int orderCount, int pendingOrderCount, int pendingPalletCount, + int sourcePalletCount, + int supplementPalletCount, int existingPlannedPalletCount, int suggestedPalletCount, int plannedPalletCount, @@ -52,6 +56,9 @@ public record ContainerInboundPlanPreviewResponse( int packageCount, BigDecimal volumeCbm, Integer suggestedPalletCount, + int sourcePalletCount, + int targetPalletCount, + int supplementPalletCount, Integer plannedPalletCount, String existingPlanNo, String planStatus, diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/mapper/InboundPlanOrderMapper.java b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/mapper/InboundPlanOrderMapper.java index 19a6cee5f..35c6d97ed 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/mapper/InboundPlanOrderMapper.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/mapper/InboundPlanOrderMapper.java @@ -27,7 +27,7 @@ public interface InboundPlanOrderMapper { String overseasWarehouseName, String fbaWarehouseCode, String destinationWarehouseId, String destinationWarehouseCode, String destinationWarehouseName, int destinationCount, boolean detained, int version, String planNo, String planStatus, - Integer plannedPalletCount, Integer materializedPalletCount + Integer plannedPalletCount, Integer materializedPalletCount, Integer sourcePalletCount ) { } @Select("SELECT o.customer_id AS customerId,o.id AS orderId,o.order_no AS orderNo," + @@ -45,7 +45,9 @@ public interface InboundPlanOrderMapper { "o.detained AS detained,o.version AS version,p.plan_no AS planNo,p.status AS planStatus," + "p.pallet_count AS plannedPalletCount," + "(SELECT COUNT(*) FROM ll_inbound_plan_pallet pp WHERE pp.customer_id=p.customer_id " + - "AND pp.plan_id=p.id AND pp.warehouse_pallet_id IS NOT NULL) AS materializedPalletCount " + + "AND pp.plan_id=p.id AND pp.warehouse_pallet_id IS NOT NULL) AS materializedPalletCount," + + "(SELECT COUNT(*) FROM ll_order_source_pallet sp WHERE sp.customer_id=o.customer_id " + + "AND sp.order_id=o.id) AS sourcePalletCount " + "FROM ll_order o LEFT JOIN ll_inbound_plan p ON p.customer_id=o.customer_id AND p.source_order_id=o.id " + "WHERE o.source_container_booking_id=#{sourceContainerBookingId} ORDER BY o.customer_id,o.id") List containerPreviewOrders( diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/service/InboundPlanService.java b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/service/InboundPlanService.java index 765a95c13..f85c2c700 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/service/InboundPlanService.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/service/InboundPlanService.java @@ -207,6 +207,10 @@ public class InboundPlanService { items.stream().filter(InboundPlanService::requiresContainerConversion) .map(InboundPlanService::pendingPalletCount) .filter(Objects::nonNull).mapToInt(Integer::intValue).sum(), + items.stream().filter(InboundPlanService::requiresContainerConversion) + .mapToInt(ContainerInboundPlanPreviewResponse.Order::sourcePalletCount).sum(), + items.stream().filter(InboundPlanService::requiresContainerConversion) + .mapToInt(ContainerInboundPlanPreviewResponse.Order::supplementPalletCount).sum(), items.stream().filter(item -> "PLANNED".equals(item.readiness())) .map(ContainerInboundPlanPreviewResponse.Order::plannedPalletCount) .filter(Objects::nonNull).mapToInt(Integer::intValue).sum(), @@ -220,6 +224,10 @@ public class InboundPlanService { int pendingPallets = orders.stream().filter(InboundPlanService::requiresContainerConversion) .map(InboundPlanService::pendingPalletCount) .filter(Objects::nonNull).mapToInt(Integer::intValue).sum(); + int sourcePallets = orders.stream().filter(InboundPlanService::requiresContainerConversion) + .mapToInt(ContainerInboundPlanPreviewResponse.Order::sourcePalletCount).sum(); + int supplementPallets = orders.stream().filter(InboundPlanService::requiresContainerConversion) + .mapToInt(ContainerInboundPlanPreviewResponse.Order::supplementPalletCount).sum(); int existingPallets = orders.stream().filter(item -> "PLANNED".equals(item.readiness())) .map(ContainerInboundPlanPreviewResponse.Order::plannedPalletCount) .filter(Objects::nonNull).mapToInt(Integer::intValue).sum(); @@ -228,7 +236,7 @@ public class InboundPlanService { bookingId, containerNo, warehouseId, snapshotVersion, orders)); return new ContainerInboundPlanPreviewResponse(bookingId, containerNo, warehouseId, warehouseCode, warehouseName, snapshotVersion, snapshotHash, orders.size(), orders.size() - blocked, blocked, - pendingOrders, pendingPallets, existingPallets, + pendingOrders, pendingPallets, sourcePallets, supplementPallets, existingPallets, orders.stream().map(ContainerInboundPlanPreviewResponse.Order::suggestedPalletCount) .filter(Objects::nonNull).mapToInt(Integer::intValue).sum(), orders.stream().map(ContainerInboundPlanPreviewResponse.Order::plannedPalletCount) @@ -258,6 +266,12 @@ public class InboundPlanService { || row.packageCount() > 0 && row.volumeCbm().signum() == 0) blockers.add("CARGO_FACT_INVALID"); } Integer suggested = suggestedPalletCount(row, blockers); + int sourcePalletCount = "ONE_CLICK_DROPSHIP".equals(row.orderType()) + ? Objects.requireNonNullElse(suggested, 0) + : Objects.requireNonNullElse(row.sourcePalletCount(), 0); + int targetPalletCount = Objects.requireNonNullElse(row.plannedPalletCount(), + Objects.requireNonNullElse(suggested, 0)); + int supplementPalletCount = Math.max(0, targetPalletCount - sourcePalletCount); if (!planned && (suggested == null || suggested <= 0)) blockers.add("PALLET_COUNT_NOT_READY"); String destinationType = destinationType(row); String fbaWarehouseCode = isFbaDestination(row) ? trimToNull(row.fbaWarehouseCode()) : null; @@ -281,7 +295,8 @@ public class InboundPlanService { String.valueOf(row.orderId()), trimToNull(row.orderNo()) == null ? row.sourceOrderNo() : row.orderNo(), row.inboundCode(), row.orderType(), destinationType, row.destinationWarehouseId(), destinationCode, row.destinationWarehouseName(), fbaWarehouseCode, row.packageCount(), row.volumeCbm(), - suggested, row.plannedPalletCount(), row.planNo(), row.planStatus(), row.version(), readiness, + suggested, sourcePalletCount, targetPalletCount, supplementPalletCount, + row.plannedPalletCount(), row.planNo(), row.planStatus(), row.version(), readiness, List.copyOf(blockers)); } @@ -321,9 +336,7 @@ public class InboundPlanService { } private static Integer pendingPalletCount(ContainerInboundPlanPreviewResponse.Order order) { - return "RESOURCE_PENDING".equals(order.readiness()) - ? Objects.requireNonNullElse(order.plannedPalletCount(), 0) - : Objects.requireNonNullElse(order.suggestedPalletCount(), 0); + return order.targetPalletCount(); } private static String destinationType(InboundPlanOrderMapper.ContainerPreviewOrderRow row) { diff --git a/lastleg-service/src/main/resources/openapi/overseas-business-v1.yaml b/lastleg-service/src/main/resources/openapi/overseas-business-v1.yaml index d5038ae39..973f4d855 100644 --- a/lastleg-service/src/main/resources/openapi/overseas-business-v1.yaml +++ b/lastleg-service/src/main/resources/openapi/overseas-business-v1.yaml @@ -169,7 +169,7 @@ paths: /api/platform/v1/last-leg/orders: get: summary: 分页查询尾程订单 - description: 权限 platform:overseas-warehouse:last-mile-order:list;平台管理员省略 customerId 时查询全部客户。 + description: 权限 platform:overseas-warehouse:last-mile-order:list;平台管理员省略 customerId 时查询全部客户。containerNode 来自稳定 sourceContainerBookingId 对应的柜级船节点门禁。 security: [{ bearerAuth: [] }] parameters: - { name: page, in: query, schema: { type: integer, minimum: 1, default: 1 } } @@ -190,7 +190,7 @@ paths: /api/platform/v1/last-leg/orders/{id}: get: summary: 查询尾程订单详情 - description: 权限 platform:overseas-warehouse:last-mile-order:list;返回订单、货物、状态历史、费用明细和操作记录。 + description: 权限 platform:overseas-warehouse:last-mile-order:list;返回订单、货物、状态历史、费用明细和操作记录。订单中的 containerNode 来自稳定 sourceContainerBookingId 对应的柜级船节点门禁。 security: [{ bearerAuth: [] }] parameters: - { name: id, in: path, required: true, schema: { type: integer, format: int64 } } @@ -2806,7 +2806,7 @@ components: version: { type: integer } ContainerInboundPlanPreview: type: object - required: [sourceContainerBookingId, snapshotVersion, snapshotHash, totalOrderCount, readyOrderCount, blockedOrderCount, pendingOrderCount, pendingPalletCount, existingPlannedPalletCount, totalSuggestedPalletCount, totalPlannedPalletCount, ready, groups] + required: [sourceContainerBookingId, snapshotVersion, snapshotHash, totalOrderCount, readyOrderCount, blockedOrderCount, pendingOrderCount, pendingPalletCount, sourcePalletCount, supplementPalletCount, existingPlannedPalletCount, totalSuggestedPalletCount, totalPlannedPalletCount, ready, groups] properties: sourceContainerBookingId: { type: string } containerNo: { type: string, nullable: true } @@ -2819,7 +2819,9 @@ components: readyOrderCount: { type: integer } blockedOrderCount: { type: integer } pendingOrderCount: { type: integer, description: 本次确认将新建计划的 READY 订单数 } - pendingPalletCount: { type: integer, description: 本次确认将向资源服务批量预留的新托盘数 } + pendingPalletCount: { type: integer, description: 本次确认最终向资源服务批量预留的托盘总数,包含头程已有编码及海外补充编码 } + sourcePalletCount: { type: integer, description: 本次处理订单随头程装柜完成事件冻结并传入的托盘码数 } + supplementPalletCount: { type: integer, description: 最终托数超过头程已有托盘码数时,在海外仓连续补充的托盘码数 } existingPlannedPalletCount: { type: integer, description: 已有有效计划的托盘总数,不计入本次新增 } totalSuggestedPalletCount: { type: integer } totalPlannedPalletCount: { type: integer } @@ -2827,7 +2829,7 @@ components: groups: { type: array, items: { $ref: '#/components/schemas/ContainerInboundPlanPreviewGroup' } } ContainerInboundPlanPreviewGroup: type: object - required: [destinationType, orderCount, pendingOrderCount, pendingPalletCount, existingPlannedPalletCount, suggestedPalletCount, plannedPalletCount, orders] + required: [destinationType, orderCount, pendingOrderCount, pendingPalletCount, sourcePalletCount, supplementPalletCount, existingPlannedPalletCount, suggestedPalletCount, plannedPalletCount, orders] properties: destinationType: { type: string, enum: [FBA, ONE_CLICK_DROPSHIP, PRIVATE_ADDRESS] } destinationWarehouseId: { type: string, nullable: true } @@ -2836,13 +2838,15 @@ components: orderCount: { type: integer } pendingOrderCount: { type: integer } pendingPalletCount: { type: integer } + sourcePalletCount: { type: integer } + supplementPalletCount: { type: integer } existingPlannedPalletCount: { type: integer } suggestedPalletCount: { type: integer } plannedPalletCount: { type: integer } orders: { type: array, items: { $ref: '#/components/schemas/ContainerInboundPlanPreviewOrder' } } ContainerInboundPlanPreviewOrder: type: object - required: [customerId, orderId, businessOrderNo, orderType, destinationType, packageCount, volumeCbm, orderVersion, readiness, blockers] + required: [customerId, orderId, businessOrderNo, orderType, destinationType, packageCount, volumeCbm, sourcePalletCount, targetPalletCount, supplementPalletCount, orderVersion, readiness, blockers] properties: customerId: { type: string } customerName: { type: string, nullable: true } @@ -2858,11 +2862,14 @@ components: packageCount: { type: integer } volumeCbm: { type: number } suggestedPalletCount: { type: integer, nullable: true } + sourcePalletCount: { type: integer, description: 头程已经冻结并传入的托盘码数 } + targetPalletCount: { type: integer, description: 本票最终生成的托盘总数,不少于头程已有托盘码数 } + supplementPalletCount: { type: integer, description: 本票需要在海外仓连续补充的托盘码数 } plannedPalletCount: { type: integer, nullable: true } existingPlanNo: { type: string, nullable: true } planStatus: { type: string, nullable: true } orderVersion: { type: integer, minimum: 0 } - readiness: { type: string, enum: [READY, PLANNED, BLOCKED] } + readiness: { type: string, enum: [READY, RESOURCE_PENDING, PLANNED, BLOCKED] } blockers: { type: array, items: { type: string } } InboundPlanLine: type: object diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/service/ContainerInboundPlanConversionCoordinatorTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/service/ContainerInboundPlanConversionCoordinatorTest.java index a4a843e15..122acef17 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/service/ContainerInboundPlanConversionCoordinatorTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/service/ContainerInboundPlanConversionCoordinatorTest.java @@ -87,7 +87,7 @@ class ContainerInboundPlanConversionCoordinatorTest { var ready = new ContainerInboundPlanPreviewResponse.Order( "200", "Customer 200", "12", "LM-12", "RK-2", "FIRST_LEG", "FBA", null, "ONT8", "Amazon ONT8", "ONT8", 10, BigDecimal.ONE, 1, - null, null, null, 0, "READY", List.of()); + 0, 1, 1, null, null, null, 0, "READY", List.of()); when(plans.convertManual(request, "manual-key", "7")).thenReturn(logical); when(plans.sourceContainerBookingId("100", 11L)).thenReturn(BOOKING_ID); when(plans.previewContainer(BOOKING_ID)).thenReturn(manualPreview(List.of(resourcePendingOrder(), ready))); @@ -272,11 +272,11 @@ class ContainerInboundPlanConversionCoordinatorTest { var fba = previewOrder("100", "11", "RK-FBA", "ABE3", "ABE3"); var overseas = previewOrder("200", "12", "RK-OVERSEAS", "US-LAX-01", null); var preview = new ContainerInboundPlanPreviewResponse(BOOKING_ID, "MSCU6639870", "31", "US-LAX-01", - "Los Angeles", 7L, SNAPSHOT_HASH, 2, 2, 0, 2, 2, 0, 2, 0, true, + "Los Angeles", 7L, SNAPSHOT_HASH, 2, 2, 0, 2, 2, 2, 0, 0, 2, 0, true, List.of(new ContainerInboundPlanPreviewResponse.Group("ONE_CLICK_DROPSHIP", null, "ABE3", "Amazon ABE3", - 1, 1, 1, 0, 1, 0, List.of(fba)), + 1, 1, 1, 1, 0, 0, 1, 0, List.of(fba)), new ContainerInboundPlanPreviewResponse.Group("ONE_CLICK_DROPSHIP", "31", "US-LAX-01", "Los Angeles", - 1, 1, 1, 0, 1, 0, List.of(overseas)))); + 1, 1, 1, 1, 0, 0, 1, 0, List.of(overseas)))); when(plans.previewContainer(BOOKING_ID)).thenReturn(preview); when(numbers.next(eq("INBOUND_PLAN"), any(String.class), any(Long.class))) .thenReturn("IP-FBA", "IP-OVERSEAS"); @@ -305,11 +305,11 @@ class ContainerInboundPlanConversionCoordinatorTest { var overseas = new ContainerInboundPlanPreviewResponse.Order( "200", "Customer 200", "12", "LM-12", "RK-OVERSEAS", "FIRST_LEG", "FIRST_LEG", "31", "US-LAX-01", "Los Angeles", null, 10, BigDecimal.ONE, 2, - null, null, null, 0, "READY", List.of()); + 0, 2, 2, null, null, null, 0, "READY", List.of()); var preview = new ContainerInboundPlanPreviewResponse(BOOKING_ID, "MSCU6639870", "31", "US-LAX-01", - "Los Angeles", 7L, SNAPSHOT_HASH, 1, 1, 0, 2, 2, 0, 1, 0, true, + "Los Angeles", 7L, SNAPSHOT_HASH, 1, 1, 0, 1, 2, 0, 2, 0, 1, 0, true, List.of(new ContainerInboundPlanPreviewResponse.Group("FIRST_LEG", "31", "US-LAX-01", "Los Angeles", - 1, 1, 2, 0, 2, 0, List.of(overseas)))); + 1, 1, 2, 0, 2, 0, 2, 0, List.of(overseas)))); when(plans.previewContainer(BOOKING_ID)).thenReturn(preview); when(numbers.next(eq("INBOUND_PLAN"), any(String.class), any(Long.class))).thenReturn("IP-OVERSEAS"); when(plans.sourcePalletNos("200", 12L, "FIRST_LEG")).thenReturn(List.of()); @@ -337,11 +337,11 @@ class ContainerInboundPlanConversionCoordinatorTest { var order = new ContainerInboundPlanPreviewResponse.Order( "100", "Customer 100", "11", "LM-11", "RK-FBA", "FIRST_LEG", "FBA", null, "ACY2", "Amazon ACY2", "ACY2", 20, new BigDecimal("5.8000"), 3, - null, null, null, 0, "READY", List.of()); + 2, 3, 1, null, null, null, 0, "READY", List.of()); var preview = new ContainerInboundPlanPreviewResponse(BOOKING_ID, "MSCU6639870", "31", "US-LAX-01", - "Los Angeles", 7L, SNAPSHOT_HASH, 1, 1, 0, 1, 3, 0, 3, 0, true, + "Los Angeles", 7L, SNAPSHOT_HASH, 1, 1, 0, 1, 3, 2, 1, 0, 3, 0, true, List.of(new ContainerInboundPlanPreviewResponse.Group("FBA", null, "ACY2", "Amazon ACY2", - 1, 1, 3, 0, 3, 0, List.of(order)))); + 1, 1, 3, 2, 1, 0, 3, 0, List.of(order)))); when(plans.previewContainer(BOOKING_ID)).thenReturn(preview); when(numbers.next(eq("INBOUND_PLAN"), any(String.class), any(Long.class))).thenReturn("IP-ACY2"); when(plans.sourcePalletNos("100", 11L, "FIRST_LEG")).thenReturn(List.of( @@ -369,11 +369,11 @@ class ContainerInboundPlanConversionCoordinatorTest { var order = new ContainerInboundPlanPreviewResponse.Order( "100", "Customer 100", "11", "LM-11", "RK-FBA", "FIRST_LEG", "FBA", null, "ACY2", "Amazon ACY2", "ACY2", 20, new BigDecimal("5.8000"), 3, - 3, "IP-MANUAL", "PENDING_ASSIGNMENT", 1, "RESOURCE_PENDING", List.of()); + 2, 3, 1, 3, "IP-MANUAL", "PENDING_ASSIGNMENT", 1, "RESOURCE_PENDING", List.of()); var preview = new ContainerInboundPlanPreviewResponse(BOOKING_ID, "MSCU6639870", "31", "US-LAX-01", - "Los Angeles", 7L, SNAPSHOT_HASH, 1, 1, 0, 1, 3, 3, 3, 3, true, + "Los Angeles", 7L, SNAPSHOT_HASH, 1, 1, 0, 1, 3, 2, 1, 3, 3, 3, true, List.of(new ContainerInboundPlanPreviewResponse.Group("FBA", null, "ACY2", "Amazon ACY2", - 1, 1, 3, 3, 3, 3, List.of(order)))); + 1, 1, 3, 2, 1, 3, 3, 3, List.of(order)))); when(plans.previewContainer(BOOKING_ID)).thenReturn(preview); when(plans.sourcePalletNos("100", 11L, "FIRST_LEG")).thenReturn(List.of( "MSCU6639870-101-001", "MSCU6639870-101-002")); @@ -404,7 +404,8 @@ class ContainerInboundPlanConversionCoordinatorTest { "LM-" + orderId, inboundCode, "ONE_CLICK_DROPSHIP", "ONE_CLICK_DROPSHIP", fbaWarehouseCode == null ? "31" : null, destinationCode, fbaWarehouseCode == null ? "Los Angeles" : "Amazon " + destinationCode, - fbaWarehouseCode, 10, BigDecimal.ONE, 1, null, null, null, 0, "READY", List.of()); + fbaWarehouseCode, 10, BigDecimal.ONE, 1, 1, 1, 0, + null, null, null, 0, "READY", List.of()); } private ManualInboundPlanRequest manualRequest() { @@ -416,7 +417,7 @@ class ContainerInboundPlanConversionCoordinatorTest { return new ContainerInboundPlanPreviewResponse.Order( "100", "Customer 100", "11", "LM-11", "RK-1", "FIRST_LEG", "FBA", null, "ACY2", "Amazon ACY2", "ACY2", 10, BigDecimal.ONE, 1, - 1, "IP-MANUAL", "PENDING_ASSIGNMENT", 1, "RESOURCE_PENDING", List.of()); + 1, 1, 0, 1, "IP-MANUAL", "PENDING_ASSIGNMENT", 1, "RESOURCE_PENDING", List.of()); } private ContainerInboundPlanPreviewResponse manualPreview( @@ -425,9 +426,9 @@ class ContainerInboundPlanConversionCoordinatorTest { || "READY".equals(order.readiness())).count(); return new ContainerInboundPlanPreviewResponse(BOOKING_ID, "MSCU6639870", "31", "US-LAX-01", "Los Angeles", 7L, SNAPSHOT_HASH, orders.size(), orders.size(), 0, pending, pending, - 0, orders.size(), 1, true, + pending, 0, 0, orders.size(), 1, true, List.of(new ContainerInboundPlanPreviewResponse.Group("FBA", null, "ACY2", "Amazon ACY2", - orders.size(), pending, pending, 0, orders.size(), 1, orders))); + orders.size(), pending, pending, pending, 0, 0, orders.size(), 1, orders))); } private InboundPlanResponse plan(String planNo, List pallets) { @@ -475,6 +476,6 @@ class ContainerInboundPlanConversionCoordinatorTest { private ContainerInboundPlanPreviewResponse preview(long version, String hash) { return new ContainerInboundPlanPreviewResponse(BOOKING_ID, "MSCU6639870", "31", "US-LAX-01", - "Los Angeles", version, hash, 1, 1, 0, 0, 0, 1, 1, 1, true, List.of()); + "Los Angeles", version, hash, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, true, List.of()); } } diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/service/ContainerInboundPlanPreviewTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/service/ContainerInboundPlanPreviewTest.java index e308c5588..409c5f89b 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/service/ContainerInboundPlanPreviewTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/service/ContainerInboundPlanPreviewTest.java @@ -57,6 +57,8 @@ class ContainerInboundPlanPreviewTest { assertFalse(result.ready()); assertEquals(4, result.pendingOrderCount()); assertEquals(7, result.pendingPalletCount()); + assertEquals(7, result.sourcePalletCount()); + assertEquals(0, result.supplementPalletCount()); assertEquals(2, result.existingPlannedPalletCount()); assertEquals(List.of("A-01", "B-02", "C-03", "DROPSHIP", ""), result.groups().stream() .map(group -> group.destinationWarehouseCode() == null ? "" : group.destinationWarehouseCode()).toList()); @@ -74,7 +76,7 @@ class ContainerInboundPlanPreviewTest { new InboundPlanOrderMapper.ContainerPreviewOrderRow("100", 1L, "LM-1", "FO-1", "测试客户", "FIRST_LEG", "OVERSEAS", "IN-1", "PENDING_ASSIGNMENT", null, null, 0, 0, BigDecimal.ZERO, null, null, null, null, null, null, null, 0, false, 1, - null, null, null, null))); + null, null, null, null, null))); var result = service.previewContainer("BOOKING-NULL"); @@ -118,6 +120,27 @@ class ContainerInboundPlanPreviewTest { assertEquals("RESOURCE_PENDING", result.groups().get(0).orders().get(0).readiness()); } + @Test + void exposesHeadLegExistingSupplementAndFinalPalletCountsWithoutBlocking() { + when(orderMapper.containerPreviewOrders("BOOKING-SUPPLEMENT")).thenReturn(List.of( + new InboundPlanOrderMapper.ContainerPreviewOrderRow("100", 1L, "LM-1", "FO-1", + "测试客户", "FIRST_LEG", "FBA", "IN-1", "PENDING_ASSIGNMENT", null, + "MSCU6639870", 2, 250, new BigDecimal("5.3000"), "31", "US-LAX-01", + "洛杉矶仓", "ACY2", null, null, "Amazon ACY2", 0, false, 1, + null, null, null, null, 2))); + + var result = service.previewContainer("BOOKING-SUPPLEMENT"); + var order = result.groups().get(0).orders().get(0); + + assertEquals(2, order.sourcePalletCount()); + assertEquals(3, order.targetPalletCount()); + assertEquals(1, order.supplementPalletCount()); + assertEquals(2, result.sourcePalletCount()); + assertEquals(1, result.supplementPalletCount()); + assertEquals(3, result.pendingPalletCount()); + assertEquals("READY", order.readiness()); + } + private InboundPlanOrderMapper.ContainerPreviewOrderRow row( long id, String orderNo, String orderType, String destinationType, String destinationCode, String destinationName, int recommendedPallets, String planNo, Integer plannedPallets, int version) { @@ -137,6 +160,6 @@ class ContainerInboundPlanPreviewTest { "FBA".equals(destinationType) || destinationCode == null ? null : "WH-" + id, "FBA".equals(destinationType) ? null : destinationCode, destinationName, "FBA".equals(destinationType) || destinationCode == null ? 0 : 1, false, version, planNo, - planStatus, plannedPallets, materializedPallets); + planStatus, plannedPallets, materializedPallets, recommendedPallets); } }