fix(dispatch): expose PDA departure gate
This commit is contained in:
+2
@@ -14,6 +14,8 @@ public interface OutboundOrderMapper extends BaseMapper<OutboundOrder> {
|
|||||||
OutboundOrder findGlobalByNo(@Param("outboundNo") String outboundNo);
|
OutboundOrder findGlobalByNo(@Param("outboundNo") String outboundNo);
|
||||||
@Select("SELECT * FROM ll_outbound_order WHERE customer_id=#{customerId} AND idempotency_key=#{key} LIMIT 1")
|
@Select("SELECT * FROM ll_outbound_order WHERE customer_id=#{customerId} AND idempotency_key=#{key} LIMIT 1")
|
||||||
OutboundOrder findByIdempotency(@Param("customerId") String customerId, @Param("key") String key);
|
OutboundOrder findByIdempotency(@Param("customerId") String customerId, @Param("key") String key);
|
||||||
|
@Select("SELECT * FROM ll_outbound_order WHERE customer_id=#{customerId} AND dispatch_order_id=#{dispatchOrderId} LIMIT 1")
|
||||||
|
OutboundOrder findByDispatch(@Param("customerId") String customerId, @Param("dispatchOrderId") Long dispatchOrderId);
|
||||||
@Select("SELECT * FROM ll_outbound_order WHERE customer_id=#{customerId} AND dispatch_order_id=#{dispatchOrderId} LIMIT 1 FOR UPDATE")
|
@Select("SELECT * FROM ll_outbound_order WHERE customer_id=#{customerId} AND dispatch_order_id=#{dispatchOrderId} LIMIT 1 FOR UPDATE")
|
||||||
OutboundOrder lockByDispatch(@Param("customerId") String customerId, @Param("dispatchOrderId") Long dispatchOrderId);
|
OutboundOrder lockByDispatch(@Param("customerId") String customerId, @Param("dispatchOrderId") Long dispatchOrderId);
|
||||||
@Select("SELECT * FROM ll_outbound_order WHERE customer_id=#{customerId} AND outbound_no=#{outboundNo} LIMIT 1 FOR UPDATE")
|
@Select("SELECT * FROM ll_outbound_order WHERE customer_id=#{customerId} AND outbound_no=#{outboundNo} LIMIT 1 FOR UPDATE")
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ import java.util.List;
|
|||||||
public final class DispatchPdaResponses {
|
public final class DispatchPdaResponses {
|
||||||
private DispatchPdaResponses() {}
|
private DispatchPdaResponses() {}
|
||||||
public record Task(Long id, String taskNo, String dispatchNo, String vehiclePlate, String warehouseId, String status,
|
public record Task(Long id, String taskNo, String dispatchNo, String vehiclePlate, String warehouseId, String status,
|
||||||
List<Item> items) {}
|
boolean canDepart, String departBlockReason, List<Item> items) {}
|
||||||
public record Item(Long dispatchItemId, String palletNo, String destinationWarehouseCode,
|
public record Item(Long dispatchItemId, String palletNo, String destinationWarehouseCode,
|
||||||
String destinationWarehouseName, String fbaWarehouseCode, String warehouseRouteLabel,
|
String destinationWarehouseName, String fbaWarehouseCode, String warehouseRouteLabel,
|
||||||
int scheduledPackages,
|
int scheduledPackages,
|
||||||
|
|||||||
+17
-1
@@ -242,7 +242,23 @@ public class DispatchPdaWorkflowService {
|
|||||||
new DispatchPdaResponses.Position(p.getId(), p.getBalanceId(), n(p.getAvailablePackages()),
|
new DispatchPdaResponses.Position(p.getId(), p.getBalanceId(), n(p.getAvailablePackages()),
|
||||||
n(p.getAllocatedPackages()), n(p.getPickedPackages()))).toList()));
|
n(p.getAllocatedPackages()), n(p.getPickedPackages()))).toList()));
|
||||||
}
|
}
|
||||||
return new DispatchPdaResponses.Task(task.getId(), task.getTaskNo(), order.getDispatchNo(), order.getVehiclePlate(), task.getWarehouseId(), task.getStatus(), values);
|
OutboundOrder outbound = outboundOrders.findByDispatch(customerId, order.getId());
|
||||||
|
String departBlockReason = departBlockReason(order, task, source, outbound);
|
||||||
|
return new DispatchPdaResponses.Task(task.getId(), task.getTaskNo(), order.getDispatchNo(), order.getVehiclePlate(),
|
||||||
|
task.getWarehouseId(), task.getStatus(), departBlockReason == null, departBlockReason, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String departBlockReason(DispatchOrder order, DispatchPdaTask task, List<DispatchItem> source,
|
||||||
|
OutboundOrder outbound) {
|
||||||
|
if ("DEPARTED".equals(order.getStatus())) return "该排车已发车,不可重复提交。";
|
||||||
|
if (trim(order.getVehiclePlate()) == null) return "请先在平台维护车辆信息。";
|
||||||
|
if (!"COMPLETED".equals(task.getStatus()) || !"READY_TO_DEPART".equals(order.getStatus())
|
||||||
|
|| !allPicked(order.getCustomerId(), task.getId(), source)) {
|
||||||
|
return "请先完成排车拣货并核平全部箱数。";
|
||||||
|
}
|
||||||
|
if (outbound == null) return "请先在平台完成“转派送”,生成待出库单后才能发车。";
|
||||||
|
if (!"WAIT_OUTBOUND".equals(outbound.getStatus())) return "关联出库单不是待出库状态,暂不可发车。";
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean allPicked(String customerId, Long taskId, List<DispatchItem> source) {
|
private boolean allPicked(String customerId, Long taskId, List<DispatchItem> source) {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ paths:
|
|||||||
/api/pda/v1/last-leg/dispatches/{dispatchNo}:
|
/api/pda/v1/last-leg/dispatches/{dispatchNo}:
|
||||||
get:
|
get:
|
||||||
summary: 查询绑定仓排车任务详情
|
summary: 查询绑定仓排车任务详情
|
||||||
|
description: 返回 canDepart 与 departBlockReason;PDA 必须以服务端门禁决定是否开放发车,不得仅按本地拣货数量推断。
|
||||||
parameters: [{ name: dispatchNo, in: path, required: true, schema: { type: string } }]
|
parameters: [{ name: dispatchNo, in: path, required: true, schema: { type: string } }]
|
||||||
responses: { '200': { description: 排车托盘、目的仓与库存位置详情 }, '404': { description: 当前绑定仓无此排车任务 } }
|
responses: { '200': { description: 排车托盘、目的仓与库存位置详情 }, '404': { description: 当前绑定仓无此排车任务 } }
|
||||||
/api/pda/v1/last-leg/dispatches/{dispatchNo}/items/{itemId}/pick:
|
/api/pda/v1/last-leg/dispatches/{dispatchNo}/items/{itemId}/pick:
|
||||||
|
|||||||
+37
@@ -107,6 +107,43 @@ class DispatchPdaWorkflowServiceTest {
|
|||||||
verify(tasks, never()).findByDispatch(any(), any());
|
verify(tasks, never()).findByDispatch(any(), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void completedTaskExposesTransferDeliveryGateBeforeDeparture() {
|
||||||
|
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);
|
||||||
|
when(orders.findGlobalByDispatchNo("DSP-1")).thenReturn(order);
|
||||||
|
when(tasks.findByDispatch("100", 1L)).thenReturn(task);
|
||||||
|
when(items.findByDispatchOrder("100", 1L)).thenReturn(List.of(item));
|
||||||
|
when(picks.totalItem("100", 11L, 21L)).thenReturn(6L);
|
||||||
|
|
||||||
|
DispatchPdaResponses.Task result = service.task("WH-1", "DSP-1");
|
||||||
|
|
||||||
|
assertFalse(result.canDepart());
|
||||||
|
assertEquals("请先在平台完成“转派送”,生成待出库单后才能发车。", result.departBlockReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void completedTaskExposesDepartureWhenPendingOutboundExists() {
|
||||||
|
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);
|
||||||
|
OutboundOrder outbound = new OutboundOrder();
|
||||||
|
outbound.setStatus("WAIT_OUTBOUND");
|
||||||
|
when(orders.findGlobalByDispatchNo("DSP-1")).thenReturn(order);
|
||||||
|
when(tasks.findByDispatch("100", 1L)).thenReturn(task);
|
||||||
|
when(items.findByDispatchOrder("100", 1L)).thenReturn(List.of(item));
|
||||||
|
when(picks.totalItem("100", 11L, 21L)).thenReturn(6L);
|
||||||
|
when(outboundOrders.findByDispatch("100", 1L)).thenReturn(outbound);
|
||||||
|
|
||||||
|
DispatchPdaResponses.Task result = service.task("WH-1", "DSP-1");
|
||||||
|
|
||||||
|
assertTrue(result.canDepart());
|
||||||
|
assertNull(result.departBlockReason());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void exactPickRetryReturnsPersistedResultAfterTaskAutomaticallyCompleted() throws Exception {
|
void exactPickRetryReturnsPersistedResultAfterTaskAutomaticallyCompleted() throws Exception {
|
||||||
DispatchOrder order = order("100", "DSP-1", "WH-1", "READY_TO_DEPART");
|
DispatchOrder order = order("100", "DSP-1", "WH-1", "READY_TO_DEPART");
|
||||||
|
|||||||
Reference in New Issue
Block a user