fix: expose overseas receiving business facts

This commit is contained in:
2026-09-11 10:32:30 +08:00
parent b2f85501ac
commit a4b7247c57
8 changed files with 68 additions and 6 deletions
@@ -221,6 +221,7 @@ public class DispatchPdaWorkflowService {
outboundOrders.updateById(outbound);
order.setShippedPackageCount((int) shipped);
order.setStatus("DEPARTED");
order.setDepartAt(now);
order.setUpdatedAt(now);
orders.updateById(order);
releasePalletLocks(customerId, order.getId(), now);
@@ -10,6 +10,9 @@ public record InboundPlanResponse(
String planNo,
String customerId,
Long sourceOrderId,
String sourceOrderNo,
String customerName,
String orderType,
String containerNo,
String overseasWarehouseCode,
String overseasWarehouseName,
@@ -29,7 +32,9 @@ public record InboundPlanResponse(
boolean mergeRequired,
boolean urgent,
String creationMode,
String remark
String remark,
LocalDateTime createdAt,
LocalDateTime updatedAt
) {
public record Line(Long id, Long orderId, String inboundCode, String fbaWarehouseCode, String destinationWarehouseId,
String destinationWarehouseCode, String destinationWarehouseName,
@@ -16,6 +16,7 @@ public interface InboundPlanOrderMapper {
String orderType) { }
record ReceivingWarehouseSnapshot(String overseasWarehouseId, String overseasWarehouseCode,
String overseasWarehouseName) { }
record DetailOrderSnapshot(String orderNo, String customerName, String orderType) { }
record ContainerWarehouseRef(String customerId, Long orderId, String sourceContainerBookingId,
String overseasWarehouseId) { }
record ContainerPreviewOrderRow(
@@ -99,6 +100,11 @@ public interface InboundPlanOrderMapper {
ReceivingWarehouseSnapshot findReceivingWarehouse(@Param("customerId") String customerId,
@Param("orderId") Long orderId);
@Select("SELECT order_no AS orderNo,customer_name AS customerName,order_type AS orderType " +
"FROM ll_order WHERE customer_id=#{customerId} AND id=#{orderId} LIMIT 1")
DetailOrderSnapshot findDetailOrder(@Param("customerId") String customerId,
@Param("orderId") Long orderId);
@Update("UPDATE ll_order SET inbound_order_no=#{planNo},current_status='INBOUND_PLANNED'," +
"pallet_merge_flag='MERGED',pallet_count=#{palletCount},updated_at=#{updatedAt} " +
"WHERE customer_id=#{customerId} AND id=#{orderId}")
@@ -1000,6 +1000,11 @@ public class InboundPlanService {
}
WarehouseResourceGateway.WarehouseReference authoritativeWarehouse = resolvedWarehouse == null
? requiredReceivingWarehouse(receivingWarehouse.overseasWarehouseId()) : resolvedWarehouse;
InboundPlanOrderMapper.DetailOrderSnapshot sourceOrder = plan.getSourceOrderId() == null ? null
: orderMapper.findDetailOrder(plan.getCustomerId(), plan.getSourceOrderId());
if (plan.getSourceOrderId() != null && sourceOrder == null) {
throw new ResponseStatusException(HttpStatus.CONFLICT, "入库计划关联订单不存在");
}
List<InboundPlanLine> lines = lineMapper.selectList(new LambdaQueryWrapper<InboundPlanLine>()
.eq(InboundPlanLine::getPlanId, plan.getId()).orderByAsc(InboundPlanLine::getId));
List<InboundPlanPallet> pallets = palletMapper.selectList(new LambdaQueryWrapper<InboundPlanPallet>()
@@ -1009,7 +1014,10 @@ public class InboundPlanService {
.eq(InboundPlanPalletAllocation::getPlanId, plan.getId())
.orderByAsc(InboundPlanPalletAllocation::getId));
return new InboundPlanResponse(plan.getId(), plan.getPlanNo(), plan.getCustomerId(), plan.getSourceOrderId(),
plan.getContainerNo(), authoritativeWarehouse.warehouseCode(), authoritativeWarehouse.warehouseName(),
sourceOrder == null ? null : sourceOrder.orderNo(),
sourceOrder == null ? null : sourceOrder.customerName(),
sourceOrder == null ? null : sourceOrder.orderType(), plan.getContainerNo(),
authoritativeWarehouse.warehouseCode(), authoritativeWarehouse.warehouseName(),
plan.getStatus(), plan.getLineCount(), plan.getPalletCount(), plan.getTotalVolumeCbm(),
lines.stream().mapToInt(InboundPlanLine::getRequiredPackageCount).sum(),
allocations.stream().mapToInt(InboundPlanPalletAllocation::getPickedPackageCount).sum(),
@@ -1027,7 +1035,8 @@ public class InboundPlanService {
item.getPickedPackageCount(), item.getActualVolumeCbm(), item.getStatus(), item.getVersion())).toList(),
plan.getVersion() == null ? 0 : plan.getVersion(), plan.getPlannedArrivalDate(), plan.getPalletType(),
Boolean.TRUE.equals(plan.getAssignedFlag()), Boolean.TRUE.equals(plan.getMergeRequired()),
Boolean.TRUE.equals(plan.getUrgentFlag()), plan.getCreationMode(), plan.getPlanRemark());
Boolean.TRUE.equals(plan.getUrgentFlag()), plan.getCreationMode(), plan.getPlanRemark(),
plan.getCreatedAt(), plan.getUpdatedAt());
}
private WarehouseResourceGateway.WarehouseReference requiredReceivingWarehouse(String warehouseId) {
@@ -2773,12 +2773,15 @@ components:
version: { type: integer }
InboundPlanDetail:
type: object
required: [id, planNo, customerId, overseasWarehouseCode, overseasWarehouseName, status, lineCount, palletCount, totalVolumeCbm, expectedPackageCount, pickedPackageCount, lines, pallets, allocations, version]
required: [id, planNo, customerId, overseasWarehouseCode, overseasWarehouseName, status, lineCount, palletCount, totalVolumeCbm, expectedPackageCount, pickedPackageCount, lines, pallets, allocations, version, createdAt, updatedAt]
properties:
id: { type: string, pattern: '^[0-9]+$' }
planNo: { type: string }
customerId: { type: string }
sourceOrderId: { type: string, pattern: '^[0-9]+$', nullable: true }
sourceOrderNo: { type: string, nullable: true, description: 关联尾程订单业务单号;手工入库计划为空 }
customerName: { type: string, nullable: true, description: 货主名称;手工入库计划为空 }
orderType: { type: string, nullable: true, description: 关联尾程订单类型;手工入库计划为空 }
containerNo: { type: string, nullable: true }
overseasWarehouseCode: { type: string, description: 接柜仓编码,取自尾程订单预分配接收仓事实 }
overseasWarehouseName: { type: string, description: 接柜仓名称,取自尾程订单预分配接收仓事实 }
@@ -2795,6 +2798,8 @@ components:
urgent: { type: boolean }
creationMode: { type: string, enum: [AUTO, MANUAL] }
remark: { type: string, nullable: true }
createdAt: { type: string, format: date-time }
updatedAt: { type: string, format: date-time }
lines: { type: array, items: { $ref: '#/components/schemas/InboundPlanLine' } }
pallets: { type: array, items: { $ref: '#/components/schemas/InboundPlanPallet' } }
allocations: { type: array, items: { $ref: '#/components/schemas/InboundPlanAllocation' } }
@@ -33,6 +33,7 @@ import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -226,6 +227,33 @@ class DispatchPdaWorkflowServiceTest {
verify(outboundOrders, never()).updateById((OutboundOrder) any());
}
@Test
void successfulDeparturePersistsBusinessDepartureTime() {
DispatchOrder order = order("100", "DSP-1", "WH-1", "READY_TO_DEPART");
order.setVehiclePlate("CA-100");
DispatchPdaTask task = task(11L, "100", "WH-1", "COMPLETED");
DispatchItem item = item(21L, "100", 6);
DispatchPdaPick pick = new DispatchPdaPick(); pick.setPackages(6L);
OutboundOrder outbound = new OutboundOrder();
outbound.setOutboundNo("OB-1"); outbound.setStatus("WAIT_OUTBOUND");
when(orders.findGlobalByDispatchNo("DSP-1")).thenReturn(order);
when(orders.lockById("100", 1L)).thenReturn(order);
when(outboundOrders.lockByDispatch("100", 1L)).thenReturn(outbound);
when(tasks.lockByDispatch("100", 1L)).thenReturn(task);
when(items.findByDispatchOrder("100", 1L)).thenReturn(List.of(item));
when(picks.totalItem("100", 11L, 21L)).thenReturn(6L);
when(picks.listTask("100", 11L)).thenReturn(List.of(pick));
when(palletLocks.selectList(any())).thenReturn(List.of());
DispatchPdaResponses.Departed result = service.depart(
"WH-1", "DSP-1", "7", new DispatchPdaRequests.Depart("depart-1", null));
assertEquals("DEPARTED", result.status());
assertNotNull(order.getDepartAt());
assertEquals(order.getDepartAt(), order.getUpdatedAt());
verify(orders).updateById(order);
}
@Test
void exactDepartRetryReturnsPersistedResultAfterStatusChanged() throws Exception {
DispatchOrder order = order("100", "DSP-1", "WH-1", "DEPARTED");
@@ -431,10 +431,11 @@ class ContainerInboundPlanConversionCoordinatorTest {
}
private InboundPlanResponse plan(String planNo, List<InboundPlanResponse.Pallet> pallets) {
return new InboundPlanResponse(21L, planNo, "100", 11L, "MSCU6639870", "US-LAX-01",
return new InboundPlanResponse(21L, planNo, "100", 11L, "FL-WH-1", "验收客户", "ONE_CLICK_DROPSHIP",
"MSCU6639870", "US-LAX-01",
"Los Angeles", "PENDING_ASSIGNMENT", 1, 1, BigDecimal.ONE, 10, 0,
List.of(), pallets, List.of(), 1, LocalDate.of(2026, 9, 10), "WOOD", false,
false, false, "MANUAL", "自动生成");
false, false, "MANUAL", "自动生成", null, null);
}
private ConvertContainerInboundPlanRequest request() {
@@ -84,6 +84,9 @@ class InboundPlanServiceTest {
org.mockito.Mockito.lenient().when(orderMapper.findReceivingWarehouse(any(), any())).thenReturn(
new InboundPlanOrderMapper.ReceivingWarehouseSnapshot(
"193000000000200003", "US-LAX-01", "洛杉矶仓"));
org.mockito.Mockito.lenient().when(orderMapper.findDetailOrder(any(), anyLong())).thenReturn(
new InboundPlanOrderMapper.DetailOrderSnapshot(
"FL-WH-TEST", "测试客户", "ONE_CLICK_DROPSHIP"));
org.mockito.Mockito.lenient().when(warehouseGateway.requireActiveSharedWarehouse(193000000000200003L)).thenReturn(
new WarehouseResourceGateway.WarehouseReference(
193000000000200003L, "US-LAX-01", "洛杉矶仓", "ACTIVE"));
@@ -324,7 +327,11 @@ class InboundPlanServiceTest {
assertEquals("US-NY-01", result.overseasWarehouseCode());
assertEquals("纽约仓", result.overseasWarehouseName());
assertEquals("FL-WH-TEST", result.sourceOrderNo());
assertEquals("测试客户", result.customerName());
assertEquals("ONE_CLICK_DROPSHIP", result.orderType());
verify(orderMapper).findReceivingWarehouse("100", 1L);
verify(orderMapper).findDetailOrder("100", 1L);
}
@Test