功能提交
This commit is contained in:
@@ -61,6 +61,8 @@
|
||||
- 目的仓变更必须写状态/操作历史或审计证据,并使用版本控制防止并发覆盖。
|
||||
- 货物废弃只使用可核对 SKU 和箱唛的一件代发库存。创建申请只能将箱从 `AVAILABLE` 锁定为 `DISCARD_PENDING`,不得扣减 SKU 箱数;实际扣库存节点固定为具备 `lastleg:cargo-discard:approve` 权限的仓库/PDA 审核人确认废弃时,并且必须在审核事务内调用专用 discard adjustment。该 adjustment 必须将箱改为 `DISCARDED`、写逐箱业务流水及唯一幂等键,并校验 `扣减前箱数 - 废弃箱数 = 扣减后箱数` 且逐箱数等于申请总箱数;任一环节失败须整体回滚。拒绝申请只释放锁箱,不得扣库存。
|
||||
- 海外采购客户下单固定进入 `PENDING_REVIEW`;采购表的 `inbound_quantity - outbound_quantity` 只表示采购业务单据余量,不得写入或冒充 `available_packages`。审核/下单只能停留在采购单据(或明确的 `procurement_packages` 桶);只有海外仓 PDA 完成实收后,`InboundStockReceiptService` 才能以 `PDA_STOCK_IN:{taskId}:{allocationId}` 幂等键调用库存 `stockIn` 转入可用库存。禁止通过通用平台接口绕过 PDA 入可用库存。
|
||||
- 派车状态只允许 PDA 拣货、完成和出库动作推进,普通派车编辑接口不得修改状态。PDA 写操作要求 `lastleg:pda:operate`;后台库存表单操作要求 `lastleg:inventory:manage`,盘点调整另要求 `lastleg:inventory:adjust`,转仓库存操作要求 `lastleg:warehouse-transfer:manage`。
|
||||
- `ll_order.current_status` 只保存尾程主流程状态;换标、转仓、出库和派送等子单精确状态保存到各自单据并汇总到 `sub_order_status`,不得再用子单状态覆盖主流程状态。PDA 入仓及库存落账完成后,主流程状态固定推进为 `INBOUND_COMPLETED`。
|
||||
|
||||
## 修改与验证
|
||||
|
||||
|
||||
@@ -185,6 +185,7 @@ public class DeliveryTransferService {
|
||||
.eq(LastlegOrder::getCustomerId, customerId).in(LastlegOrder::getId, orderIds))) {
|
||||
String from = order.getCurrentStatus(); order.setOutboundOrderNo(outboundNo);
|
||||
order.setDeliveryOrderNo(deliveryNo); order.setCurrentStatus("WAIT_OUTBOUND"); order.setUpdatedAt(now);
|
||||
order.setSubOrderStatus("OUTBOUND:WAIT_OUTBOUND;DELIVERY:WAIT_DELIVERY");
|
||||
tailOrders.updateById(order);
|
||||
LastlegOrderStatusHistory history = new LastlegOrderStatusHistory(); history.setOrderId(order.getId());
|
||||
history.setFromStatus(from); history.setToStatus("WAIT_OUTBOUND");
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.baiyue.lastleg.dispatch.model.DispatchPalletCandidate;
|
||||
import com.baiyue.lastleg.dispatch.pda.dto.DispatchPdaRequests;
|
||||
import com.baiyue.lastleg.dispatch.pda.dto.DispatchPdaResponses;
|
||||
import com.baiyue.lastleg.dispatch.pda.service.DispatchPdaWorkflowService;
|
||||
import com.baiyue.lastleg.dispatch.pda.controller.DispatchPdaController;
|
||||
import com.baiyue.lastleg.dispatch.service.DispatchService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@@ -103,6 +104,7 @@ public class DispatchController {
|
||||
@RequestParam(required = false) String customerId,
|
||||
@PathVariable String dispatchNo,
|
||||
@Valid @RequestBody DispatchPdaRequests.Depart request) {
|
||||
requirePdaOperate(user);
|
||||
String scopedTenant = scope(user, customerId);
|
||||
String operatorId = user.userId() == null ? user.username() : String.valueOf(user.userId());
|
||||
return ApiResponse.success(pdaWorkflow.depart(scopedTenant, dispatchNo, operatorId, request));
|
||||
@@ -121,4 +123,12 @@ public class DispatchController {
|
||||
}
|
||||
return String.valueOf(user.tenantId());
|
||||
}
|
||||
|
||||
private void requirePdaOperate(AuthenticatedUser user) {
|
||||
if (user == null || user.tenantId() == null) throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
|
||||
if (user.permissions() == null || !user.permissions().contains(DispatchPdaController.OPERATE_PERMISSION)) {
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN,
|
||||
DispatchPdaController.OPERATE_PERMISSION + " permission is required");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -15,6 +15,7 @@ import org.springframework.web.server.ResponseStatusException;
|
||||
@RestController
|
||||
@RequestMapping("/api/platform/v1/last-leg/dispatches/{dispatchNo}/pda")
|
||||
public class DispatchPdaController {
|
||||
public static final String OPERATE_PERMISSION = "lastleg:pda:operate";
|
||||
@Resource private DispatchPdaWorkflowService service;
|
||||
|
||||
@PostMapping("/push")
|
||||
@@ -22,6 +23,7 @@ public class DispatchPdaController {
|
||||
@PathVariable String dispatchNo,
|
||||
@RequestParam(required = false) String customerId,
|
||||
@Valid @RequestBody DispatchPdaRequests.Push request) {
|
||||
requireOperate(user);
|
||||
return ApiResponse.success(service.push(scope(user, customerId), dispatchNo, request));
|
||||
}
|
||||
@GetMapping
|
||||
@@ -35,6 +37,7 @@ public class DispatchPdaController {
|
||||
@PathVariable String dispatchNo, @PathVariable long itemId,
|
||||
@RequestParam(required = false) String customerId,
|
||||
@Valid @RequestBody DispatchPdaRequests.Pick request) {
|
||||
requireOperate(user);
|
||||
return ApiResponse.success(service.pick(scope(user, customerId), dispatchNo, itemId, operator(user), request));
|
||||
}
|
||||
@PostMapping("/complete")
|
||||
@@ -43,6 +46,7 @@ public class DispatchPdaController {
|
||||
@PathVariable String dispatchNo,
|
||||
@RequestParam(required = false) String customerId,
|
||||
@Valid @RequestBody DispatchPdaRequests.CompletePicking request) {
|
||||
requireOperate(user);
|
||||
return ApiResponse.success(service.completePicking(scope(user, customerId), dispatchNo, request));
|
||||
}
|
||||
private String scope(AuthenticatedUser user, String requestedCustomer) {
|
||||
@@ -56,4 +60,10 @@ public class DispatchPdaController {
|
||||
return String.valueOf(user.tenantId());
|
||||
}
|
||||
private String operator(AuthenticatedUser user) { return user.userId() == null ? user.username() : String.valueOf(user.userId()); }
|
||||
private void requireOperate(AuthenticatedUser user) {
|
||||
if (user == null || user.tenantId() == null) throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
|
||||
if (user.permissions() == null || !user.permissions().contains(OPERATE_PERMISSION)) {
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, OPERATE_PERMISSION + " permission is required");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +35,6 @@ import java.util.stream.Collectors;
|
||||
public class DispatchService {
|
||||
public static final String PENDING_DEPARTURE = "PENDING_DEPARTURE";
|
||||
public static final String CANCELLED = "CANCELLED";
|
||||
private static final Set<String> STATUSES = Set.of(PENDING_DEPARTURE, "PICKING", "READY_TO_DEPART",
|
||||
"PARTIAL_READY_TO_DEPART", "DEPARTED", "PARTIAL_DEPARTED", "COMPLETED", CANCELLED);
|
||||
|
||||
@Resource private DispatchOrderMapper orders;
|
||||
@Resource private DispatchItemMapper items;
|
||||
@@ -185,11 +183,8 @@ public class DispatchService {
|
||||
dispatch.setVehicleAppointmentStatus(defaultStatus(request.vehicleAppointmentStatus()));
|
||||
dispatch.setEstimatedArrivalAt(request.estimatedArrivalAt());
|
||||
if (StringUtils.hasText(request.status())) {
|
||||
String status = request.status().trim().toUpperCase();
|
||||
if (!STATUSES.contains(status) || CANCELLED.equals(status)) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Use cancel endpoint or a valid dispatch status");
|
||||
}
|
||||
dispatch.setStatus(status);
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
|
||||
"Dispatch status is PDA-managed and cannot be changed by the edit endpoint");
|
||||
}
|
||||
dispatch.setUpdatedAt(LocalDateTime.now());
|
||||
orders.updateById(dispatch);
|
||||
|
||||
@@ -42,6 +42,7 @@ public record LastlegOrderSummary(
|
||||
String relabelStatus,
|
||||
String palletMergeFlag,
|
||||
String currentStatus,
|
||||
String subOrderStatus,
|
||||
String privateRemark,
|
||||
String customerRemark,
|
||||
String warehouseRemark,
|
||||
|
||||
@@ -50,6 +50,7 @@ public class LastlegOrder {
|
||||
private String relabelStatus;
|
||||
private String palletMergeFlag;
|
||||
private String currentStatus;
|
||||
private String subOrderStatus;
|
||||
private String privateRemark;
|
||||
private String customerRemark;
|
||||
private String warehouseRemark;
|
||||
|
||||
+13
@@ -23,6 +23,7 @@ import java.util.List;
|
||||
@RestController
|
||||
@RequestMapping("/api/platform/v1/last-leg/inbound-plans/{planNo}/pda")
|
||||
public class InboundPdaController {
|
||||
public static final String OPERATE_PERMISSION = "lastleg:pda:operate";
|
||||
@Resource private InboundPdaWorkflowService service;
|
||||
|
||||
@PostMapping("/push")
|
||||
@@ -30,6 +31,7 @@ public class InboundPdaController {
|
||||
@PathVariable String planNo,
|
||||
@RequestParam(required = false) String customerId,
|
||||
@Valid @RequestBody PdaRequests.Push request) {
|
||||
requireOperate(user);
|
||||
return ApiResponse.success(service.push(scope(user, customerId), planNo, request));
|
||||
}
|
||||
|
||||
@@ -46,6 +48,7 @@ public class InboundPdaController {
|
||||
@PathVariable long allocationId,
|
||||
@RequestParam(required = false) String customerId,
|
||||
@Valid @RequestBody PdaRequests.PackageOperation request) {
|
||||
requireOperate(user);
|
||||
return ApiResponse.success(service.count(scope(user, customerId), planNo, allocationId, request));
|
||||
}
|
||||
|
||||
@@ -55,6 +58,7 @@ public class InboundPdaController {
|
||||
@PathVariable long allocationId,
|
||||
@RequestParam(required = false) String customerId,
|
||||
@Valid @RequestBody PdaRequests.PackageOperation request) {
|
||||
requireOperate(user);
|
||||
return ApiResponse.success(service.pick(scope(user, customerId), planNo, allocationId, request));
|
||||
}
|
||||
|
||||
@@ -71,6 +75,7 @@ public class InboundPdaController {
|
||||
@PathVariable long allocationId,
|
||||
@RequestParam(required = false) String customerId,
|
||||
@Valid @RequestBody PdaRequests.ConfirmMerge request) {
|
||||
requireOperate(user);
|
||||
return ApiResponse.success(service.merge(scope(user, customerId), planNo, allocationId, request));
|
||||
}
|
||||
|
||||
@@ -78,6 +83,7 @@ public class InboundPdaController {
|
||||
public ApiResponse<PdaResponses.Task> complete(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@PathVariable String planNo,
|
||||
@RequestParam(required = false) String customerId) {
|
||||
requireOperate(user);
|
||||
return ApiResponse.success(service.complete(scope(user, customerId), planNo));
|
||||
}
|
||||
|
||||
@@ -91,4 +97,11 @@ public class InboundPdaController {
|
||||
}
|
||||
return String.valueOf(user.tenantId());
|
||||
}
|
||||
|
||||
private void requireOperate(AuthenticatedUser user) {
|
||||
if (user == null || user.tenantId() == null) throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
|
||||
if (user.permissions() == null || !user.permissions().contains(OPERATE_PERMISSION)) {
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, OPERATE_PERMISSION + " permission is required");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+27
@@ -13,6 +13,8 @@ import com.baiyue.lastleg.inventory.command.dto.InventoryCommandRequests;
|
||||
import com.baiyue.lastleg.inventory.command.dto.InventoryCommandResponse;
|
||||
import com.baiyue.lastleg.inventory.command.service.InventoryCommandService;
|
||||
import com.baiyue.lastleg.mapper.LastlegOrderMapper;
|
||||
import com.baiyue.lastleg.mapper.LastlegOrderStatusHistoryMapper;
|
||||
import com.baiyue.lastleg.entity.LastlegOrderStatusHistory;
|
||||
import com.baiyue.lastleg.dropship.service.DropshipOrderService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -35,6 +37,7 @@ public class InboundStockReceiptService {
|
||||
@Resource private InboundStockReceiptMapper receipts;
|
||||
@Resource private InventoryCommandService inventory;
|
||||
@Resource private DropshipOrderService dropshipOrders;
|
||||
@Resource private LastlegOrderStatusHistoryMapper orderHistories;
|
||||
|
||||
@Transactional
|
||||
public void stockIn(String customerId, InboundPlan plan, InboundPdaTask task,
|
||||
@@ -59,6 +62,7 @@ public class InboundStockReceiptService {
|
||||
.filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
if (receivedAllocationIds.equals(expectedAllocationIds)) {
|
||||
activateDropship(customerId, task.getId(), orderById, existing);
|
||||
completeOrders(customerId, plan, orderById, receivedAt);
|
||||
return;
|
||||
}
|
||||
throw conflict("PDA stock-in receipts are incomplete; repair the task receipts before retrying");
|
||||
@@ -81,6 +85,29 @@ public class InboundStockReceiptService {
|
||||
}
|
||||
}
|
||||
activateDropship(customerId, task.getId(), orderById, created);
|
||||
completeOrders(customerId, plan, orderById, receivedAt);
|
||||
}
|
||||
|
||||
private void completeOrders(String customerId, InboundPlan plan, Map<Long, LastlegOrder> orderById,
|
||||
LocalDateTime completedAt) {
|
||||
for (LastlegOrder snapshot : orderById.values()) {
|
||||
LastlegOrder order = orders.lockById(customerId, snapshot.getId());
|
||||
if (order == null) throw conflict("Inbound tail-leg order disappeared while completing stock-in");
|
||||
if ("INBOUND_COMPLETED".equals(order.getCurrentStatus())) continue;
|
||||
String before = order.getCurrentStatus();
|
||||
order.setCurrentStatus("INBOUND_COMPLETED");
|
||||
order.setSubOrderStatus("INBOUND_PLAN:COMPLETED");
|
||||
order.setUpdatedAt(completedAt);
|
||||
orders.updateById(order);
|
||||
LastlegOrderStatusHistory history = new LastlegOrderStatusHistory();
|
||||
history.setOrderId(order.getId());
|
||||
history.setFromStatus(before);
|
||||
history.setToStatus("INBOUND_COMPLETED");
|
||||
history.setEventId("PDA_INBOUND_COMPLETE:" + plan.getPlanNo());
|
||||
history.setRemark("PDA inbound completed and inventory stocked in");
|
||||
history.setCreatedAt(completedAt);
|
||||
orderHistories.insert(history);
|
||||
}
|
||||
}
|
||||
|
||||
private InboundStockReceipt persist(String customerId, InboundPlan plan, InboundPdaTask task, InboundPlanLine line,
|
||||
|
||||
+16
@@ -21,12 +21,15 @@ import org.springframework.web.server.ResponseStatusException;
|
||||
@RequestMapping("/api/platform/v1/last-leg/inventory")
|
||||
@Tag(name = "Tail-leg inbound-order package inventory commands")
|
||||
public class InventoryCommandController {
|
||||
public static final String MANAGE_PERMISSION = "lastleg:inventory:manage";
|
||||
public static final String ADJUST_PERMISSION = "lastleg:inventory:adjust";
|
||||
@Resource private InventoryCommandService service;
|
||||
|
||||
@PostMapping("/hold")
|
||||
@Operation(summary = "Move available inbound-order packages to hold")
|
||||
public ApiResponse<InventoryCommandResponse> hold(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@Valid @RequestBody InventoryCommandRequests.Move request) {
|
||||
requirePermission(user, MANAGE_PERMISSION);
|
||||
return ApiResponse.success(service.hold(scope(user, request.customerId()), operator(user), request));
|
||||
}
|
||||
|
||||
@@ -34,6 +37,7 @@ public class InventoryCommandController {
|
||||
@Operation(summary = "Release held inbound-order packages")
|
||||
public ApiResponse<InventoryCommandResponse> release(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@Valid @RequestBody InventoryCommandRequests.Move request) {
|
||||
requirePermission(user, MANAGE_PERMISSION);
|
||||
return ApiResponse.success(service.release(scope(user, request.customerId()), operator(user), request));
|
||||
}
|
||||
|
||||
@@ -41,6 +45,7 @@ public class InventoryCommandController {
|
||||
@Operation(summary = "Allocate available inbound-order packages to an outbound order")
|
||||
public ApiResponse<InventoryCommandResponse> allocate(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@Valid @RequestBody InventoryCommandRequests.Move request) {
|
||||
requirePermission(user, MANAGE_PERMISSION);
|
||||
return ApiResponse.success(service.allocate(scope(user, request.customerId()), operator(user), request));
|
||||
}
|
||||
|
||||
@@ -48,6 +53,7 @@ public class InventoryCommandController {
|
||||
@Operation(summary = "Cancel an outbound allocation")
|
||||
public ApiResponse<InventoryCommandResponse> unallocate(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@Valid @RequestBody InventoryCommandRequests.Move request) {
|
||||
requirePermission(user, MANAGE_PERMISSION);
|
||||
return ApiResponse.success(service.unallocate(scope(user, request.customerId()), operator(user), request));
|
||||
}
|
||||
|
||||
@@ -55,6 +61,7 @@ public class InventoryCommandController {
|
||||
@Operation(summary = "Confirm picked inbound-order packages")
|
||||
public ApiResponse<InventoryCommandResponse> pick(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@Valid @RequestBody InventoryCommandRequests.Move request) {
|
||||
requirePermission(user, MANAGE_PERMISSION);
|
||||
return ApiResponse.success(service.pick(scope(user, request.customerId()), operator(user), request));
|
||||
}
|
||||
|
||||
@@ -62,6 +69,7 @@ public class InventoryCommandController {
|
||||
@Operation(summary = "Ship picked inbound-order packages and report whether the pallet is empty")
|
||||
public ApiResponse<InventoryCommandResponse> ship(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@Valid @RequestBody InventoryCommandRequests.Move request) {
|
||||
requirePermission(user, MANAGE_PERMISSION);
|
||||
return ApiResponse.success(service.ship(scope(user, request.customerId()), operator(user), request));
|
||||
}
|
||||
|
||||
@@ -69,6 +77,7 @@ public class InventoryCommandController {
|
||||
@Operation(summary = "Adjust inbound-order package inventory after a physical count")
|
||||
public ApiResponse<InventoryCommandResponse> adjust(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@Valid @RequestBody InventoryCommandRequests.Adjustment request) {
|
||||
requirePermission(user, ADJUST_PERMISSION);
|
||||
return ApiResponse.success(service.adjust(scope(user, request.customerId()), operator(user), request));
|
||||
}
|
||||
|
||||
@@ -87,4 +96,11 @@ public class InventoryCommandController {
|
||||
private String operator(AuthenticatedUser user) {
|
||||
return user.userId() == null ? user.username() : String.valueOf(user.userId());
|
||||
}
|
||||
|
||||
private void requirePermission(AuthenticatedUser user, String permission) {
|
||||
if (user == null || user.tenantId() == null) throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
|
||||
if (user.permissions() == null || !user.permissions().contains(permission)) {
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, permission + " permission is required");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-5
@@ -82,7 +82,8 @@ public class RelabelApplicationService {
|
||||
order.setPackageCount(total);
|
||||
order.setPickedPackageCount(0);
|
||||
order.setShippedPackageCount(0);
|
||||
order.setCurrentStatus(PENDING_PROCESSING);
|
||||
order.setCurrentStatus("PENDING_ASSIGNMENT");
|
||||
order.setSubOrderStatus("RELABEL:" + PENDING_PROCESSING);
|
||||
order.setRelabelOrderNo(relabelNo);
|
||||
order.setRelabelStatus(PENDING_PROCESSING);
|
||||
order.setCustomerRemark(trim(request.remark()));
|
||||
@@ -117,7 +118,7 @@ public class RelabelApplicationService {
|
||||
|
||||
for (RelabelRequests.Item source : request.items()) insertItem(customerId, applicationId, source, now);
|
||||
insertHistory(customerId, applicationId, null, PENDING_PROCESSING, operatorId, "Relabel application submitted", now);
|
||||
insertOrderHistory(order.getId(), null, PENDING_PROCESSING, "Relabel application submitted", now);
|
||||
insertOrderHistory(order.getId(), null, order.getCurrentStatus(), "Relabel application submitted", now);
|
||||
return detail(customerId, relabelNo);
|
||||
}
|
||||
|
||||
@@ -174,15 +175,23 @@ public class RelabelApplicationService {
|
||||
LastlegOrder order = orders.selectById(application.getLastlegOrderId());
|
||||
if (order == null || !customerId.equals(order.getCustomerId())) throw conflict("Linked tail-leg order is missing");
|
||||
String orderBefore = order.getCurrentStatus();
|
||||
String mainStatus = STATUSES.contains(orderBefore) ? "PENDING_ASSIGNMENT" : orderBefore;
|
||||
// A return-relabel tail order joins the normal inbound queue after labels are finished.
|
||||
String orderStatus = "COMPLETED".equals(next) && "RETURN_RELABEL".equals(order.getOrderType())
|
||||
? "PENDING_ASSIGNMENT" : next;
|
||||
String orderStatus = switch (next) {
|
||||
case "COMPLETED" -> "RETURN_RELABEL".equals(order.getOrderType())
|
||||
? "PENDING_ASSIGNMENT" : "COMPLETED";
|
||||
case "REJECTED", "CANCELLED" -> "CANCELLED";
|
||||
default -> mainStatus;
|
||||
};
|
||||
order.setCurrentStatus(orderStatus);
|
||||
order.setSubOrderStatus("RELABEL:" + next);
|
||||
order.setRelabelStatus(next);
|
||||
order.setUpdatedAt(now);
|
||||
orders.updateById(order);
|
||||
insertHistory(customerId, application.getId(), before, next, operatorId, request.remark(), now);
|
||||
insertOrderHistory(order.getId(), orderBefore, orderStatus, request.remark(), now);
|
||||
if (!Objects.equals(orderBefore, orderStatus)) {
|
||||
insertOrderHistory(order.getId(), orderBefore, orderStatus, request.remark(), now);
|
||||
}
|
||||
return detail(customerId, relabelNo);
|
||||
}
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ public class LastlegOrderService {
|
||||
List<LastlegOrder> orders = orderMapper.selectList(filter(customerId, query)
|
||||
.orderByDesc(LastlegOrder::getCreatedAt).last("LIMIT 10000"));
|
||||
List<String> lines = new ArrayList<>();
|
||||
lines.add("ID,尾程单号,订单类型,入仓码,头程单号,货主,海外仓,转仓,转仓编码,FBA仓库,集装箱号,货柜状态,是否异常,是否暂扣,托数,箱数,已拣箱数,已出库箱数,剩余箱数,体积(m3),数量,体积重量,预计到港时间,实际到港时间,预计到仓,入库单号,出库单号,派送单号,换标单号,换标状态,合托标记,当前状态,备注(仅自己),备注(客户),备注(仓库)");
|
||||
lines.add("ID,尾程单号,订单类型,入仓码,头程单号,货主,海外仓,转仓,转仓编码,FBA仓库,集装箱号,货柜状态,是否异常,是否暂扣,托数,箱数,已拣箱数,已出库箱数,剩余箱数,体积(m3),数量,体积重量,预计到港时间,实际到港时间,预计到仓,入库单号,出库单号,派送单号,换标单号,换标状态,合托标记,主流程状态,子单状态,备注(仅自己),备注(客户),备注(仓库)");
|
||||
for (LastlegOrder order : orders) {
|
||||
lines.add(String.join(",", List.of(
|
||||
csv(order.getId()), csv(order.getOrderNo()), csv(order.getOrderType()), csv(order.getInboundCode()),
|
||||
@@ -140,7 +140,7 @@ public class LastlegOrderService {
|
||||
csv(order.getVolumeWeightKg()), csv(order.getEstimatedArrivalAt()), csv(order.getActualArrivalAt()),
|
||||
csv(order.getEstimatedWarehouseAt()), csv(order.getInboundOrderNo()), csv(order.getOutboundOrderNo()),
|
||||
csv(order.getDeliveryOrderNo()), csv(order.getRelabelOrderNo()), csv(order.getRelabelStatus()),
|
||||
csv(order.getPalletMergeFlag()), csv(order.getCurrentStatus()), csv(order.getPrivateRemark()),
|
||||
csv(order.getPalletMergeFlag()), csv(order.getCurrentStatus()), csv(order.getSubOrderStatus()), csv(order.getPrivateRemark()),
|
||||
csv(order.getCustomerRemark()), csv(order.getWarehouseRemark()))));
|
||||
}
|
||||
return ("\uFEFF" + String.join("\r\n", lines)).getBytes(StandardCharsets.UTF_8);
|
||||
@@ -189,7 +189,7 @@ public class LastlegOrderService {
|
||||
- Objects.requireNonNullElse(o.getShippedPackageCount(), 0)), o.getVolumeCbm(),
|
||||
o.getProductQuantity(), o.getVolumeWeightKg(), o.getEstimatedArrivalAt(), o.getActualArrivalAt(),
|
||||
o.getEstimatedWarehouseAt(), o.getInboundOrderNo(), o.getOutboundOrderNo(), o.getDeliveryOrderNo(),
|
||||
o.getRelabelOrderNo(), o.getRelabelStatus(), o.getPalletMergeFlag(), o.getCurrentStatus(),
|
||||
o.getRelabelOrderNo(), o.getRelabelStatus(), o.getPalletMergeFlag(), o.getCurrentStatus(), o.getSubOrderStatus(),
|
||||
o.getPrivateRemark(), o.getCustomerRemark(), o.getWarehouseRemark(), o.getCreatedAt());
|
||||
}
|
||||
|
||||
|
||||
+11
@@ -15,6 +15,7 @@ import org.springframework.web.server.ResponseStatusException;
|
||||
@RestController
|
||||
@RequestMapping("/api/platform/v1/last-leg/warehouse-transfers")
|
||||
public class WarehouseTransferController {
|
||||
public static final String MANAGE_PERMISSION = "lastleg:warehouse-transfer:manage";
|
||||
@Resource private WarehouseTransferService service;
|
||||
|
||||
@PostMapping
|
||||
@@ -41,6 +42,7 @@ public class WarehouseTransferController {
|
||||
public ApiResponse<WarehouseTransferResponses.Detail> allocate(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@RequestParam(required = false) String customerId, @PathVariable String transferNo,
|
||||
@Valid @RequestBody WarehouseTransferRequests.Allocate request) {
|
||||
requirePermission(user, MANAGE_PERMISSION);
|
||||
return ApiResponse.success(service.allocate(scope(user, customerId), transferNo, operator(user), request));
|
||||
}
|
||||
|
||||
@@ -48,6 +50,7 @@ public class WarehouseTransferController {
|
||||
public ApiResponse<WarehouseTransferResponses.Detail> outbound(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@RequestParam(required = false) String customerId, @PathVariable String transferNo,
|
||||
@Valid @RequestBody WarehouseTransferRequests.Outbound request) {
|
||||
requirePermission(user, MANAGE_PERMISSION);
|
||||
return ApiResponse.success(service.outbound(scope(user, customerId), transferNo, operator(user), request));
|
||||
}
|
||||
|
||||
@@ -55,6 +58,7 @@ public class WarehouseTransferController {
|
||||
public ApiResponse<WarehouseTransferResponses.Detail> receive(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@RequestParam(required = false) String customerId, @PathVariable String transferNo,
|
||||
@Valid @RequestBody WarehouseTransferRequests.Receipt request) {
|
||||
requirePermission(user, MANAGE_PERMISSION);
|
||||
return ApiResponse.success(service.receive(scope(user, customerId), transferNo, operator(user), request));
|
||||
}
|
||||
|
||||
@@ -65,6 +69,13 @@ public class WarehouseTransferController {
|
||||
}
|
||||
}
|
||||
|
||||
private void requirePermission(AuthenticatedUser user, String permission) {
|
||||
if (user == null || user.tenantId() == null) throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
|
||||
if (user.permissions() == null || !user.permissions().contains(permission)) {
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, permission + " permission is required");
|
||||
}
|
||||
}
|
||||
|
||||
private String scope(AuthenticatedUser user, String requested) {
|
||||
if (user == null || user.tenantId() == null) throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
|
||||
if ("PLATFORM".equals(user.appCode()) && user.tenantId() == 0L) {
|
||||
|
||||
+17
-10
@@ -225,12 +225,14 @@ public class WarehouseTransferService {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
String before = order.getCurrentStatus();
|
||||
transfer.setStatus(next); transfer.setUpdatedAt(now); transfers.updateById(transfer);
|
||||
String orderStatus = APPROVED.equals(next)
|
||||
? (Boolean.TRUE.equals(transfer.getNeedRelabel()) ? RelabelApplicationService.PENDING_PROCESSING : PENDING_ASSIGNMENT)
|
||||
: "CANCELLED";
|
||||
order.setCurrentStatus(orderStatus); order.setUpdatedAt(now); orders.updateById(order);
|
||||
insertOrderHistory(order.getId(), before, orderStatus, transferNo,
|
||||
"Warehouse transfer reviewed: " + next, now);
|
||||
String orderStatus = APPROVED.equals(next) ? PENDING_ASSIGNMENT : "CANCELLED";
|
||||
order.setCurrentStatus(orderStatus);
|
||||
order.setSubOrderStatus("WAREHOUSE_TRANSFER:" + next);
|
||||
order.setUpdatedAt(now); orders.updateById(order);
|
||||
if (!java.util.Objects.equals(before, orderStatus)) {
|
||||
insertOrderHistory(order.getId(), before, orderStatus, transferNo,
|
||||
"Warehouse transfer reviewed: " + next, now);
|
||||
}
|
||||
return detail(customerId, transferNo);
|
||||
}
|
||||
|
||||
@@ -304,7 +306,8 @@ public class WarehouseTransferService {
|
||||
transfer.setStatus(ALLOCATED); transfer.setAllocationIdempotencyKey(key);
|
||||
transfer.setAllocationRequestHash(requestHash); transfer.setUpdatedAt(now); transfers.updateById(transfer);
|
||||
LastlegOrder order = requireTailOrder(customerId, transfer);
|
||||
String before = order.getCurrentStatus(); order.setCurrentStatus("READY_TO_OUTBOUND"); order.setUpdatedAt(now);
|
||||
String before = order.getCurrentStatus(); order.setCurrentStatus("WAIT_OUTBOUND");
|
||||
order.setSubOrderStatus("WAREHOUSE_TRANSFER:ALLOCATED"); order.setUpdatedAt(now);
|
||||
orders.updateById(order);
|
||||
insertOrderHistory(order.getId(), before, order.getCurrentStatus(), transferNo,
|
||||
"Warehouse transfer inventory allocated", now);
|
||||
@@ -354,7 +357,8 @@ public class WarehouseTransferService {
|
||||
transfer.setOutboundRequestHash(requestHash); transfer.setOutboundAt(now); transfer.setUpdatedAt(now);
|
||||
transfers.updateById(transfer);
|
||||
LastlegOrder order = requireTailOrder(customerId, transfer);
|
||||
String before = order.getCurrentStatus(); order.setCurrentStatus("IN_TRANSIT"); order.setUpdatedAt(now);
|
||||
String before = order.getCurrentStatus(); order.setCurrentStatus("IN_TRANSIT");
|
||||
order.setSubOrderStatus("WAREHOUSE_TRANSFER:OUTBOUNDED"); order.setUpdatedAt(now);
|
||||
orders.updateById(order);
|
||||
insertOrderHistory(order.getId(), before, order.getCurrentStatus(), transferNo,
|
||||
"Warehouse transfer left source warehouse", now);
|
||||
@@ -415,7 +419,8 @@ public class WarehouseTransferService {
|
||||
|| "RECEIVED".equals(value.getStatus()));
|
||||
transfer.setStatus(complete ? RECEIVED : PARTIALLY_RECEIVED); transfer.setUpdatedAt(now); transfers.updateById(transfer);
|
||||
LastlegOrder order = requireTailOrder(customerId, transfer);
|
||||
String before = order.getCurrentStatus(); order.setCurrentStatus(complete ? RECEIVED : PARTIALLY_RECEIVED);
|
||||
String before = order.getCurrentStatus(); order.setCurrentStatus(complete ? "COMPLETED" : "IN_TRANSIT");
|
||||
order.setSubOrderStatus("WAREHOUSE_TRANSFER:" + (complete ? RECEIVED : PARTIALLY_RECEIVED));
|
||||
order.setUpdatedAt(now); orders.updateById(order);
|
||||
insertOrderHistory(order.getId(), before, order.getCurrentStatus(), transferNo,
|
||||
"Warehouse transfer inventory received at destination", now);
|
||||
@@ -435,7 +440,8 @@ public class WarehouseTransferService {
|
||||
order.setContainerNo(transferNo);
|
||||
order.setPalletCount(0); order.setPickedPackageCount(0); order.setShippedPackageCount(0);
|
||||
order.setEstimatedWarehouseAt(request.estimatedArrivalAt());
|
||||
order.setCurrentStatus(PENDING_REVIEW); order.setCustomerRemark(trim(request.remark()));
|
||||
order.setCurrentStatus("PENDING_ASSIGNMENT"); order.setSubOrderStatus("WAREHOUSE_TRANSFER:" + PENDING_REVIEW);
|
||||
order.setCustomerRemark(trim(request.remark()));
|
||||
order.setSourceEventId("WAREHOUSE_TRANSFER:" + transferNo); order.setSourceLoadingPlanId(ORDER_TYPE);
|
||||
order.setSourcePayloadHash("sha256:" + requestHash); order.setCreatedAt(now); order.setUpdatedAt(now);
|
||||
orders.insert(order);
|
||||
@@ -494,6 +500,7 @@ public class WarehouseTransferService {
|
||||
transfer.setRelabelApplicationId(applicationId); transfer.setRelabelNo(relabelNo); transfer.setUpdatedAt(now);
|
||||
transfers.updateById(transfer);
|
||||
order.setRelabelOrderNo(relabelNo); order.setRelabelStatus(RelabelApplicationService.PENDING_PROCESSING);
|
||||
order.setSubOrderStatus("WAREHOUSE_TRANSFER:" + PENDING_REVIEW + ";RELABEL:" + RelabelApplicationService.PENDING_PROCESSING);
|
||||
order.setUpdatedAt(now); orders.updateById(order);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@ CREATE TABLE IF NOT EXISTS ll_order (
|
||||
relabel_status VARCHAR(32) COMMENT '换标状态',
|
||||
pallet_merge_flag VARCHAR(32) COMMENT '合托标记',
|
||||
current_status VARCHAR(32) NOT NULL DEFAULT 'PENDING_ASSIGNMENT' COMMENT '当前业务状态',
|
||||
sub_order_status VARCHAR(255) NULL COMMENT '子单据状态汇总',
|
||||
private_remark VARCHAR(1000) COMMENT '备注(仅自己)',
|
||||
customer_remark VARCHAR(1000) COMMENT '备注(客户)',
|
||||
warehouse_remark VARCHAR(1000) COMMENT '备注(仓库)',
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
@@ -85,6 +86,24 @@ class DispatchServiceTest {
|
||||
verify(candidates, never()).available(any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void editCannotBypassPdaManagedDispatchStatus() {
|
||||
DispatchOrder dispatch = new DispatchOrder();
|
||||
dispatch.setId(1L); dispatch.setCustomerId("100"); dispatch.setDispatchNo("PC1");
|
||||
dispatch.setStatus(DispatchService.PENDING_DEPARTURE);
|
||||
when(orders.findByDispatchNo("100", "PC1")).thenReturn(dispatch);
|
||||
when(orders.lockById("100", 1L)).thenReturn(dispatch);
|
||||
DispatchRequests.Update request = new DispatchRequests.Update(null, null, null, null, null,
|
||||
LocalDateTime.now(), null, null, null, null, "COMPLETED");
|
||||
|
||||
ResponseStatusException exception = assertThrows(ResponseStatusException.class,
|
||||
() -> service.update("100", "PC1", request));
|
||||
|
||||
assertEquals(400, exception.getStatusCode().value());
|
||||
assertEquals(DispatchService.PENDING_DEPARTURE, dispatch.getStatus());
|
||||
verify(orders, never()).updateById(any(DispatchOrder.class));
|
||||
}
|
||||
|
||||
private DispatchPalletCandidate candidate(long planPalletId, Long warehousePalletId, String destination) {
|
||||
return new DispatchPalletCandidate(1L, planPalletId, warehousePalletId, 2L, "WH-1", "Overseas warehouse", "LMO1", "LMO1", "RK1",
|
||||
"LIP1-P001", destination, destination, destination, destination, 12,
|
||||
|
||||
+9
@@ -13,6 +13,8 @@ import com.baiyue.lastleg.inventory.command.dto.InventoryCommandRequests;
|
||||
import com.baiyue.lastleg.inventory.command.dto.InventoryCommandResponse;
|
||||
import com.baiyue.lastleg.inventory.command.service.InventoryCommandService;
|
||||
import com.baiyue.lastleg.mapper.LastlegOrderMapper;
|
||||
import com.baiyue.lastleg.mapper.LastlegOrderStatusHistoryMapper;
|
||||
import com.baiyue.lastleg.entity.LastlegOrderStatusHistory;
|
||||
import com.baiyue.lastleg.dropship.service.DropshipOrderService;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -43,6 +45,7 @@ class InboundStockReceiptServiceTest {
|
||||
@Mock InboundStockReceiptMapper receipts;
|
||||
@Mock InventoryCommandService inventory;
|
||||
@Mock DropshipOrderService dropshipOrders;
|
||||
@Mock LastlegOrderStatusHistoryMapper orderHistories;
|
||||
private InboundStockReceiptService service;
|
||||
|
||||
@BeforeEach
|
||||
@@ -52,6 +55,7 @@ class InboundStockReceiptServiceTest {
|
||||
ReflectionTestUtils.setField(service, "receipts", receipts);
|
||||
ReflectionTestUtils.setField(service, "inventory", inventory);
|
||||
ReflectionTestUtils.setField(service, "dropshipOrders", dropshipOrders);
|
||||
ReflectionTestUtils.setField(service, "orderHistories", orderHistories);
|
||||
when(receipts.listTask("100", 2L)).thenReturn(List.of());
|
||||
}
|
||||
|
||||
@@ -74,6 +78,9 @@ class InboundStockReceiptServiceTest {
|
||||
verify(receipts).insert(receipt.capture());
|
||||
assertEquals(5L, receipt.getValue().getReceivedPackages());
|
||||
assertEquals(10L, receipt.getValue().getInventoryPositionId());
|
||||
assertEquals("INBOUND_COMPLETED", fixture.order.getCurrentStatus());
|
||||
assertEquals("INBOUND_PLAN:COMPLETED", fixture.order.getSubOrderStatus());
|
||||
verify(orderHistories).insert(any(LastlegOrderStatusHistory.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -177,6 +184,8 @@ class InboundStockReceiptServiceTest {
|
||||
LastlegOrder order = new LastlegOrder(); order.setId(101L); order.setCustomerId("100");
|
||||
order.setOrderNo("LMO-1"); order.setCustomerName("Customer"); order.setOverseasWarehouseId("OW-1");
|
||||
order.setOverseasWarehouseName("Warehouse"); order.setSourceEventId("evt-1"); order.setFbaWarehouseCode("ONT8");
|
||||
order.setCurrentStatus("INBOUND_PLANNED");
|
||||
org.mockito.Mockito.lenient().when(orders.lockById("100", 101L)).thenReturn(order);
|
||||
LastlegOrderDetail detail = detail(201L, "SKU-1", required, quantity);
|
||||
return new Fixture(plan, task, line, order, detail, allocation(31L, 12L, 21L, picked), pallet(21L, 2001L));
|
||||
}
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package com.baiyue.lastleg.inventory.command.controller;
|
||||
|
||||
import com.baiyue.common.security.AuthenticatedUser;
|
||||
import com.baiyue.lastleg.inventory.command.dto.InventoryCommandRequests;
|
||||
import com.baiyue.lastleg.inventory.command.dto.InventoryCommandResponse;
|
||||
import com.baiyue.lastleg.inventory.command.service.InventoryCommandService;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class InventoryCommandControllerTest {
|
||||
@Mock InventoryCommandService service;
|
||||
private InventoryCommandController controller;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
controller = new InventoryCommandController();
|
||||
ReflectionTestUtils.setField(controller, "service", service);
|
||||
}
|
||||
|
||||
@Test
|
||||
void inventoryMutationRequiresFormPermission() {
|
||||
var request = move();
|
||||
ResponseStatusException exception = assertThrows(ResponseStatusException.class,
|
||||
() -> controller.hold(user(Set.of()), request));
|
||||
assertEquals(403, exception.getStatusCode().value());
|
||||
verify(service, never()).hold(any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void permittedBackOfficeUserCanRunInventoryFormCommand() {
|
||||
var request = move();
|
||||
when(service.hold("100", "7", request)).thenReturn(response());
|
||||
controller.hold(user(Set.of(InventoryCommandController.MANAGE_PERMISSION)), request);
|
||||
verify(service).hold("100", "7", request);
|
||||
}
|
||||
|
||||
@Test
|
||||
void physicalAdjustmentRequiresDedicatedPermission() {
|
||||
var request = new InventoryCommandRequests.Adjustment("100", 1L, 2, "COUNT", "ORD-1", "adj-1", null);
|
||||
ResponseStatusException exception = assertThrows(ResponseStatusException.class,
|
||||
() -> controller.adjust(user(Set.of(InventoryCommandController.MANAGE_PERMISSION)), request));
|
||||
assertEquals(403, exception.getStatusCode().value());
|
||||
verify(service, never()).adjust(any(), any(), any());
|
||||
}
|
||||
|
||||
private InventoryCommandRequests.Move move() {
|
||||
return new InventoryCommandRequests.Move("100", 1L, 2, "ORD-1", null, null, "hold-1", null);
|
||||
}
|
||||
|
||||
private AuthenticatedUser user(Set<String> permissions) {
|
||||
return new AuthenticatedUser(7L, 100L, 100L, 8L, "operator", "MERCHANT", "WEB", permissions);
|
||||
}
|
||||
|
||||
private InventoryCommandResponse response() {
|
||||
return new InventoryCommandResponse(1L, "HOLD", 2L, 3L, 0, 2, 0, 0, false, false);
|
||||
}
|
||||
}
|
||||
+5
-3
@@ -88,7 +88,8 @@ class RelabelApplicationServiceTest {
|
||||
verify(orderHistories).insert(tailHistory.capture());
|
||||
|
||||
assertEquals("RELABEL_APPLICATION", tailOrder.getValue().getOrderType());
|
||||
assertEquals(RelabelApplicationService.PENDING_PROCESSING, tailOrder.getValue().getCurrentStatus());
|
||||
assertEquals("PENDING_ASSIGNMENT", tailOrder.getValue().getCurrentStatus());
|
||||
assertEquals("RELABEL:PENDING_PROCESSING", tailOrder.getValue().getSubOrderStatus());
|
||||
assertEquals("CUSTOMER", tailOrder.getValue().getSourceRegion());
|
||||
assertEquals("LMO-RELABEL-1", tailOrder.getValue().getOrderNo());
|
||||
assertEquals(tailOrder.getValue().getInboundCode(), tailOrder.getValue().getSourceInboundId());
|
||||
@@ -98,7 +99,7 @@ class RelabelApplicationServiceTest {
|
||||
assertEquals(6, application.getValue().getNotifiedPackageCount());
|
||||
assertEquals("SKU-A", relabelItem.getValue().getSkuCode());
|
||||
assertEquals("PENDING_PROCESSING", relabelHistory.getValue().getToStatus());
|
||||
assertEquals("PENDING_PROCESSING", tailHistory.getValue().getToStatus());
|
||||
assertEquals("PENDING_ASSIGNMENT", tailHistory.getValue().getToStatus());
|
||||
assertEquals("FBR-1", result.application().relabelNo());
|
||||
}
|
||||
|
||||
@@ -161,7 +162,8 @@ class RelabelApplicationServiceTest {
|
||||
verify(applications).updateById(application);
|
||||
ArgumentCaptor<LastlegOrder> updatedOrder = ArgumentCaptor.forClass(LastlegOrder.class);
|
||||
verify(orders).updateById(updatedOrder.capture());
|
||||
assertEquals("PROCESSING", updatedOrder.getValue().getCurrentStatus());
|
||||
assertEquals("PENDING_ASSIGNMENT", updatedOrder.getValue().getCurrentStatus());
|
||||
assertEquals("RELABEL:PROCESSING", updatedOrder.getValue().getSubOrderStatus());
|
||||
verify(histories).insert(any(RelabelStatusHistory.class));
|
||||
verify(orderHistories).insert(any(LastlegOrderStatusHistory.class));
|
||||
}
|
||||
|
||||
+3
-2
@@ -114,7 +114,8 @@ class WarehouseTransferServiceTest {
|
||||
ArgumentCaptor<LastlegOrder> tailOrder = ArgumentCaptor.forClass(LastlegOrder.class);
|
||||
verify(orders).insert(tailOrder.capture());
|
||||
assertEquals(WarehouseTransferService.RETURN_RELABEL_ORDER_TYPE, tailOrder.getValue().getOrderType());
|
||||
assertEquals(WarehouseTransferService.PENDING_REVIEW, tailOrder.getValue().getCurrentStatus());
|
||||
assertEquals("PENDING_ASSIGNMENT", tailOrder.getValue().getCurrentStatus());
|
||||
assertEquals("WAREHOUSE_TRANSFER:PENDING_REVIEW;RELABEL:PENDING_PROCESSING", tailOrder.getValue().getSubOrderStatus());
|
||||
assertEquals("LMO-TRANSFER-1", tailOrder.getValue().getOrderNo());
|
||||
assertNull(tailOrder.getValue().getTransferWarehouseId());
|
||||
verify(sourceOrders).insert(any(WarehouseTransferSourceOrder.class));
|
||||
@@ -193,7 +194,7 @@ class WarehouseTransferServiceTest {
|
||||
assertEquals(701L, captured.getValue().getPalletId());
|
||||
assertEquals(5, captured.getValue().getPackageCount());
|
||||
assertEquals(WarehouseTransferService.ALLOCATED, transfer.getStatus());
|
||||
assertEquals("READY_TO_OUTBOUND", result.orderStatus());
|
||||
assertEquals("WAIT_OUTBOUND", result.orderStatus());
|
||||
verifyNoInteractions(inventory);
|
||||
}
|
||||
|
||||
|
||||
@@ -3545,3 +3545,9 @@ SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE tab
|
||||
PREPARE schema_comments_025 FROM @ddl; EXECUTE schema_comments_025; DEALLOCATE PREPARE schema_comments_025;
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='wh_pallet_business_binding' AND column_name='created_at' AND COALESCE(column_comment,'') <> '创建时间'), 'ALTER TABLE wh_pallet_business_binding MODIFY COLUMN created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT ''创建时间''', 'SELECT 1'));
|
||||
PREPARE schema_comments_025 FROM @ddl; EXECUTE schema_comments_025; DEALLOCATE PREPARE schema_comments_025;
|
||||
-- ============================================================================
|
||||
-- 2026-08-19 / 026
|
||||
-- Separate tail-leg main workflow status from child-document status
|
||||
-- ============================================================================
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_order' AND column_name='sub_order_status'), 'SELECT 1', 'ALTER TABLE ll_order ADD COLUMN sub_order_status VARCHAR(255) NULL COMMENT ''子单据状态汇总'' AFTER current_status'));
|
||||
PREPARE ll_order_sub_status_026 FROM @ddl; EXECUTE ll_order_sub_status_026; DEALLOCATE PREPARE ll_order_sub_status_026;
|
||||
|
||||
Reference in New Issue
Block a user