一件代发拣货

This commit is contained in:
2026-09-01 13:41:00 +08:00
parent ffed9bf898
commit 8b0c99593e
29 changed files with 910 additions and 29 deletions
@@ -34,7 +34,7 @@ public interface PrintPdfClient {
}
record Label(String topCode, String warehouseText, String cartonText,
String routeText, String qrContent) {
String routeText, String qrContent, String skuCode) {
}
record FreightA4PdfRequest(String templateCode, PageSpec pageSpec,
@@ -0,0 +1,117 @@
package com.baiyue.firstleg.controller;
import com.baiyue.common.core.api.ApiResponse;
import com.baiyue.common.security.AuthenticatedUser;
import com.baiyue.firstleg.dto.DropshipPalletizationDtos;
import com.baiyue.firstleg.dto.DropshipPalletizationDtos.Pallet;
import com.baiyue.firstleg.dto.DropshipPalletizationDtos.Task;
import com.baiyue.firstleg.service.DropshipPalletizationService;
import com.baiyue.firstleg.service.FirstlegScopeGuard;
import com.baiyue.firstleg.service.InboundCartonLabelPrintService;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.Size;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestHeader;
import java.util.List;
/** 国内 PDA 一件代发合托;与装柜 PDA 分离。 */
@Validated
@RestController
@RequestMapping("/api/platform/v1/first-leg/pda-dropship")
public class PlatformDropshipPalletizationController {
@Resource private DropshipPalletizationService service;
@Resource private InboundCartonLabelPrintService printService;
@GetMapping("/tasks/pending")
@PreAuthorize("hasAuthority('platform:firstleg:pda:view')")
public ApiResponse<List<DropshipPalletizationDtos.PendingTask>> pending(
@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam(required = false) @Size(max = 64) String keyword) {
FirstlegScopeGuard.platform(user);
return ApiResponse.success(service.pending(user.userId(), keyword));
}
@GetMapping("/tasks/{taskId}")
@PreAuthorize("hasAuthority('platform:firstleg:pda:view')")
public ApiResponse<Task> task(@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable @Positive long taskId,
@RequestParam @Positive long warehouseId) {
FirstlegScopeGuard.platform(user);
return ApiResponse.success(service.task(taskId, warehouseId, user.userId()));
}
@PostMapping("/tasks/{taskId}/claim")
@PreAuthorize("hasAuthority('platform:firstleg:pda:work')")
public ApiResponse<Task> claim(@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable @Positive long taskId,
@Valid @RequestBody DropshipPalletizationDtos.ClaimRequest request) {
FirstlegScopeGuard.platform(user);
return ApiResponse.success(service.claim(taskId, request.warehouseId(), user.userId(), user.username()));
}
@PostMapping("/tasks/{taskId}/pallets/open")
@PreAuthorize("hasAuthority('platform:firstleg:pda:work')")
public ApiResponse<Pallet> open(@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable @Positive long taskId,
@Valid @RequestBody DropshipPalletizationDtos.OpenPalletRequest request) {
FirstlegScopeGuard.platform(user);
return ApiResponse.success(service.open(taskId, request, user.userId(), user.username()));
}
@PostMapping("/tasks/{taskId}/pallets/{palletId}/scan")
@PreAuthorize("hasAuthority('platform:firstleg:pda:work')")
public ApiResponse<DropshipPalletizationDtos.ScanResult> scan(
@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable @Positive long taskId, @PathVariable @Positive long palletId,
@Valid @RequestBody DropshipPalletizationDtos.ScanRequest request) {
FirstlegScopeGuard.platform(user);
return ApiResponse.success(service.scan(taskId, palletId, request, user.userId(), user.username()));
}
@PostMapping("/tasks/{taskId}/pallets/{palletId}/seal")
@PreAuthorize("hasAuthority('platform:firstleg:pda:work')")
public ApiResponse<Task> seal(@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable @Positive long taskId,
@PathVariable @Positive long palletId,
@Valid @RequestBody DropshipPalletizationDtos.SealPalletRequest request) {
FirstlegScopeGuard.platform(user);
return ApiResponse.success(service.seal(taskId, palletId, request, user.userId(), user.username()));
}
@GetMapping("/inbound-orders/{inboundId}/pallets")
@PreAuthorize("hasAuthority('firstleg:inbound:list')")
public ApiResponse<List<Pallet>> inboundPallets(@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable @Positive long inboundId,
@RequestParam @Positive long accountId) {
FirstlegScopeGuard.platform(user, accountId);
return ApiResponse.success(service.inboundPallets(accountId, inboundId));
}
@GetMapping("/inbound-orders/{inboundId}/pallets/{palletId}/print")
@PreAuthorize("hasAuthority('firstleg:inbound:list')")
public ResponseEntity<byte[]> printPallet(@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable @Positive long inboundId,
@PathVariable @Positive long palletId,
@RequestParam @Positive long accountId,
@RequestHeader(value = "X-Request-Id", required = false) String requestId) {
FirstlegScopeGuard.platform(user, accountId);
Pallet pallet = service.inboundPallets(accountId, inboundId).stream()
.filter(item -> item.palletId() == palletId).findFirst()
.orElseThrow(() -> new com.baiyue.common.core.exception.BizException(
com.baiyue.common.core.api.ResultCode.NOT_FOUND, "托盘不存在"));
return printService.generateDropshipPallet(pallet, requestId);
}
}
@@ -20,6 +20,8 @@ public final class InboundProcessStatus {
public static final String FEE_CONFIRMING = "FEE_CONFIRMING";
/** 费用已确认,正在执行托盘、库位占用和入库收口。 */
public static final String INBOUNDING = "INBOUNDING";
/** 一件代发已完成测量但未真实入库,等待 PDA 合托。 */
public static final String WAIT_PALLETIZE = "WAIT_PALLETIZE";
/** 入库和费用发布完成,等待分柜。 */
public static final String WAIT_SPLIT = "WAIT_SPLIT";
/** 已进入装柜作业,PDA 可执行拣货。 */
@@ -33,7 +35,7 @@ public final class InboundProcessStatus {
public static final Set<String> VALUES = Set.of(
PREPARING, WAIT_MEASURE, MEASURING, MEASURE_PENDING, MEASURE_REJECTED,
WAIT_PRICING, FEE_CONFIRMING, INBOUNDING, WAIT_SPLIT,
WAIT_PRICING, FEE_CONFIRMING, INBOUNDING, WAIT_PALLETIZE, WAIT_SPLIT,
LOADING, LOADED, DELIVERED, VOIDED
);
@@ -0,0 +1,51 @@
package com.baiyue.firstleg.dto;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
import java.util.List;
/** 一件代发测量完成后的独立 PDA 合托契约。 */
public final class DropshipPalletizationDtos {
private DropshipPalletizationDtos() {}
public record ClaimRequest(@Positive long warehouseId) {}
public record OpenPalletRequest(@Positive long warehouseId,
@NotBlank @Size(max = 128) String operationId) {}
public record ScanRequest(@Positive long warehouseId,
@NotBlank @Size(max = 64) String boxCode,
@Positive @Max(2000) int cartonCount,
@NotBlank @Size(max = 128) String operationId) {}
public record SealPalletRequest(@Positive long warehouseId,
@NotBlank @Size(max = 128) String operationId,
@Positive int expectedVersion) {}
public record PendingTask(Long taskId, Long inboundId, String inboundCode, Long tenantId,
String customerName, Long warehouseId, String warehouseName,
Integer totalCartons, Integer allocatedCartons, String taskStatus,
LocalDateTime createdAt) {}
public record Task(Long taskId, Long inboundId, String inboundCode, Long tenantId,
String customerName, Long warehouseId, String warehouseName,
Integer totalCartons, Integer allocatedCartons, String taskStatus,
Long assigneeId, String assigneeName, Integer version,
List<Pallet> pallets) {}
public record Pallet(Long palletId, String palletNo, String palletStatus,
Integer cartonCount, Integer skuCount, Integer version,
List<SkuItem> items, LocalDateTime completedAt) {}
public record SkuItem(Long inboundId, String inboundCode, Long inboundDetailId,
String skuCode, String cargoName, Integer cartonCount,
List<String> cartonMarks) {}
public record ScanResult(String skuCode, String cargoName, String inboundCode,
int addedCartons, int allocatedCartons, int remainingCartons,
Pallet pallet) {}
}
@@ -15,8 +15,17 @@ public final class InboundCartonLabels {
public record Label(Long id, Long inboundDetailId, Integer cargoItemNo, String cargoName,
Integer detailPackageCount, String boxCode, Integer totalCount,
Integer cartonIndex, String boxSpecification, BigDecimal lengthCm,
BigDecimal widthCm, BigDecimal heightCm, String topCode,
BigDecimal widthCm, BigDecimal heightCm, String skuCode, String topCode,
String warehouseText, String cartonText, String routeText) {
public Label(Long id, Long inboundDetailId, Integer cargoItemNo, String cargoName,
Integer detailPackageCount, String boxCode, Integer totalCount,
Integer cartonIndex, String boxSpecification, BigDecimal lengthCm,
BigDecimal widthCm, BigDecimal heightCm, String topCode,
String warehouseText, String cartonText, String routeText) {
this(id, inboundDetailId, cargoItemNo, cargoName, detailPackageCount, boxCode,
totalCount, cartonIndex, boxSpecification, lengthCm, widthCm, heightCm,
null, topCode, warehouseText, cartonText, routeText);
}
}
public record Resolution(Long inboundId, Long orderId, Long inboundDetailId, String boxCode,
@@ -8,11 +8,18 @@ import java.util.List;
public record InboundOrderDetail(
InboundOrderSummary summary,
String publicRemark,
List<DropshipPalletizationDtos.Pallet> dropshipPallets,
List<PalletLocationSummary> palletLocations,
List<FileSummary> files,
List<InternalNoteSummary> internalNotes,
List<StatusHistorySummary> statusHistory
) {
public InboundOrderDetail(InboundOrderSummary summary, String publicRemark,
List<PalletLocationSummary> palletLocations, List<FileSummary> files,
List<InternalNoteSummary> internalNotes,
List<StatusHistorySummary> statusHistory) {
this(summary, publicRemark, List.of(), palletLocations, files, internalNotes, statusHistory);
}
public record PalletLocationSummary(
Integer allocationNo, Long warehouseId, Long palletId, String palletCode,
BigDecimal palletCapacityCbm, BigDecimal allocatedVolumeCbm, Integer allocatedPackageCount,
@@ -14,7 +14,7 @@ public record UnifiedInboundQuery(
Long tenantId,
@Size(max = 64) String keyword,
@Size(max = 64) String orderNo,
@Schema(allowableValues = {"PENDING_REVIEW", "WAIT_MEASURE", "WAIT_SPLIT",
@Schema(allowableValues = {"PENDING_REVIEW", "WAIT_MEASURE", "WAIT_PALLETIZE", "WAIT_SPLIT",
"LOADING", "LOADED", "DELIVERED"})
@Size(max = 32) String mainStatus,
Long customerId,
@@ -0,0 +1,203 @@
package com.baiyue.firstleg.mapper;
import com.baiyue.firstleg.dto.DropshipPalletizationDtos.PendingTask;
import lombok.Getter;
import lombok.Setter;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.time.LocalDateTime;
import java.util.List;
/** 一件代发合托持久化;任务、托盘和逐箱绑定均独立于装柜拣货。 */
@Mapper
public interface DropshipPalletizationMapper {
@Insert("""
INSERT IGNORE INTO fl_dropship_pallet_task
(id,tenant_id,inbound_id,warehouse_id,task_status,total_cartons,allocated_cartons,version,created_at,updated_at)
VALUES(#{id},#{tenantId},#{inboundId},#{warehouseId},'PENDING',#{totalCartons},0,1,CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3))
""")
int insertTask(@Param("id") long id, @Param("tenantId") long tenantId,
@Param("inboundId") long inboundId, @Param("warehouseId") long warehouseId,
@Param("totalCartons") int totalCartons);
@Select("""
SELECT t.id task_id,t.inbound_id,o.inbound_code,t.tenant_id,o.customer_name,t.warehouse_id,
o.warehouse_name,t.total_cartons,t.allocated_cartons,t.task_status,t.created_at
FROM fl_dropship_pallet_task t JOIN fl_inbound_order o ON o.id=t.inbound_id AND o.tenant_id=t.tenant_id
WHERE t.task_status IN ('PENDING','CLAIMED')
AND (t.assignee_id IS NULL OR t.assignee_id=#{operatorId})
AND (#{keyword} IS NULL OR o.inbound_code LIKE CONCAT('%',#{keyword},'%')
OR o.customer_name LIKE CONCAT('%',#{keyword},'%'))
ORDER BY t.created_at,t.id
""")
List<PendingTask> pending(@Param("operatorId") long operatorId, @Param("keyword") String keyword);
@Select("""
SELECT t.*,o.inbound_code,o.customer_name,o.warehouse_name,o.process_status,o.fulfillment_type
FROM fl_dropship_pallet_task t JOIN fl_inbound_order o ON o.id=t.inbound_id AND o.tenant_id=t.tenant_id
WHERE t.id=#{taskId} AND t.warehouse_id=#{warehouseId}
""")
TaskRow task(@Param("taskId") long taskId, @Param("warehouseId") long warehouseId);
@Update("""
UPDATE fl_dropship_pallet_task
SET task_status='CLAIMED',assignee_id=#{operatorId},assignee_name=#{operatorName},
claimed_at=COALESCE(claimed_at,CURRENT_TIMESTAMP(3)),version=version+1,updated_at=CURRENT_TIMESTAMP(3)
WHERE id=#{taskId} AND warehouse_id=#{warehouseId} AND task_status IN ('PENDING','CLAIMED')
AND (assignee_id IS NULL OR assignee_id=#{operatorId})
""")
int claim(@Param("taskId") long taskId, @Param("warehouseId") long warehouseId,
@Param("operatorId") long operatorId, @Param("operatorName") String operatorName);
@Insert("""
INSERT INTO fl_dropship_pallet
(id,task_id,tenant_id,inbound_id,warehouse_id,pallet_status,open_operation_id,version,created_by,created_by_name,created_at,updated_at)
VALUES(#{id},#{taskId},#{tenantId},#{inboundId},#{warehouseId},'OPEN',#{operationId},1,
#{operatorId},#{operatorName},CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3))
""")
int insertPallet(@Param("id") long id, @Param("taskId") long taskId,
@Param("tenantId") long tenantId, @Param("inboundId") long inboundId,
@Param("warehouseId") long warehouseId, @Param("operationId") String operationId,
@Param("operatorId") long operatorId, @Param("operatorName") String operatorName);
@Select("SELECT * FROM fl_dropship_pallet WHERE open_operation_id=#{operationId}")
PalletRow palletByOpenOperation(@Param("operationId") String operationId);
@Select("SELECT * FROM fl_dropship_pallet WHERE seal_operation_id=#{operationId}")
PalletRow palletBySealOperation(@Param("operationId") String operationId);
@Select("SELECT * FROM fl_dropship_pallet WHERE id=#{palletId} AND task_id=#{taskId} AND warehouse_id=#{warehouseId}")
PalletRow pallet(@Param("taskId") long taskId, @Param("palletId") long palletId,
@Param("warehouseId") long warehouseId);
@Select("SELECT * FROM fl_dropship_pallet WHERE task_id=#{taskId} ORDER BY id")
List<PalletRow> pallets(@Param("taskId") long taskId);
@Select("""
SELECT p.* FROM fl_dropship_pallet p JOIN fl_dropship_pallet_task t ON t.id=p.task_id
WHERE t.tenant_id=#{tenantId} AND t.inbound_id=#{inboundId} ORDER BY p.id
""")
List<PalletRow> palletsByInbound(@Param("tenantId") long tenantId, @Param("inboundId") long inboundId);
@Select("SELECT inbound_code FROM fl_inbound_order WHERE tenant_id=#{tenantId} AND id=#{inboundId} AND deleted=0")
String inboundCode(@Param("tenantId") long tenantId, @Param("inboundId") long inboundId);
@Select("""
SELECT l.id carton_label_id,l.inbound_detail_id,l.sku_code,l.cargo_name,l.detail_package_count,
l.inbound_code,l.box_code
FROM fl_inbound_carton_label l
JOIN fl_dropship_pallet_task t ON t.inbound_id=l.inbound_id AND t.tenant_id=l.tenant_id
WHERE t.id=#{taskId} AND l.box_code=#{boxCode} AND l.batch_status='ACTIVE'
""")
LabelRow label(@Param("taskId") long taskId, @Param("boxCode") String boxCode);
@Select("""
SELECT l.id carton_label_id,l.inbound_detail_id,l.sku_code,l.cargo_name,l.detail_package_count,
l.inbound_code,l.box_code
FROM fl_inbound_carton_label l
WHERE l.tenant_id=#{tenantId} AND l.inbound_id=#{inboundId}
AND l.inbound_detail_id=#{detailId} AND l.batch_status='ACTIVE'
AND NOT EXISTS (SELECT 1 FROM fl_dropship_pallet_carton c
WHERE c.task_id=#{taskId} AND c.carton_label_id=l.id)
ORDER BY CASE WHEN l.box_code=#{preferredBoxCode} THEN 0 ELSE 1 END,l.carton_index,l.id
LIMIT #{count}
""")
List<LabelRow> unboundLabels(@Param("taskId") long taskId, @Param("tenantId") long tenantId,
@Param("inboundId") long inboundId, @Param("detailId") long detailId,
@Param("preferredBoxCode") String preferredBoxCode,
@Param("count") int count);
@Select("SELECT COUNT(*) FROM fl_dropship_pallet_carton WHERE operation_id=#{operationId}")
int operationCount(@Param("operationId") String operationId);
@Insert("""
INSERT INTO fl_dropship_pallet_carton
(id,task_id,pallet_id,tenant_id,inbound_id,inbound_detail_id,carton_label_id,sku_code,cargo_name,
box_code,operation_id,bound_by,bound_by_name,bound_at)
VALUES(#{id},#{taskId},#{palletId},#{tenantId},#{inboundId},#{detailId},#{labelId},#{skuCode},#{cargoName},
#{boxCode},#{operationId},#{operatorId},#{operatorName},CURRENT_TIMESTAMP(3))
""")
int insertCarton(@Param("id") long id, @Param("taskId") long taskId,
@Param("palletId") long palletId, @Param("tenantId") long tenantId,
@Param("inboundId") long inboundId, @Param("detailId") long detailId,
@Param("labelId") long labelId, @Param("skuCode") String skuCode,
@Param("cargoName") String cargoName, @Param("boxCode") String boxCode,
@Param("operationId") String operationId, @Param("operatorId") long operatorId,
@Param("operatorName") String operatorName);
@Update("""
UPDATE fl_dropship_pallet_task
SET allocated_cartons=allocated_cartons+#{count},version=version+1,updated_at=CURRENT_TIMESTAMP(3)
WHERE id=#{taskId} AND task_status='CLAIMED' AND assignee_id=#{operatorId}
AND allocated_cartons+#{count}<=total_cartons
""")
int addAllocated(@Param("taskId") long taskId, @Param("operatorId") long operatorId,
@Param("count") int count);
@Update("""
UPDATE fl_dropship_pallet
SET pallet_no=#{palletNo},pallet_status='COMPLETED',seal_operation_id=#{operationId},
completed_by=#{operatorId},completed_by_name=#{operatorName},completed_at=CURRENT_TIMESTAMP(3),
version=version+1,updated_at=CURRENT_TIMESTAMP(3)
WHERE id=#{palletId} AND task_id=#{taskId} AND pallet_status='OPEN' AND version=#{expectedVersion}
AND EXISTS(SELECT 1 FROM fl_dropship_pallet_carton c WHERE c.pallet_id=#{palletId})
""")
int seal(@Param("taskId") long taskId, @Param("palletId") long palletId,
@Param("expectedVersion") int expectedVersion, @Param("palletNo") String palletNo,
@Param("operationId") String operationId, @Param("operatorId") long operatorId,
@Param("operatorName") String operatorName);
@Select("SELECT COUNT(*) FROM fl_dropship_pallet WHERE task_id=#{taskId} AND pallet_status='OPEN'")
int openPalletCount(@Param("taskId") long taskId);
@Update("""
UPDATE fl_dropship_pallet_task
SET task_status='COMPLETED',completed_at=CURRENT_TIMESTAMP(3),version=version+1,updated_at=CURRENT_TIMESTAMP(3)
WHERE id=#{taskId} AND task_status='CLAIMED' AND allocated_cartons=total_cartons
AND NOT EXISTS(SELECT 1 FROM fl_dropship_pallet p WHERE p.task_id=#{taskId} AND p.pallet_status='OPEN')
""")
int completeTask(@Param("taskId") long taskId);
@Update("""
UPDATE fl_inbound_order o
SET o.process_status='WAIT_SPLIT',o.pallet_count=(SELECT COUNT(*) FROM fl_dropship_pallet p
WHERE p.inbound_id=o.id AND p.pallet_status='COMPLETED'),o.updated_by=#{operatorId},o.version=o.version+1
WHERE o.id=#{inboundId} AND o.tenant_id=#{tenantId} AND o.process_status='WAIT_PALLETIZE'
AND EXISTS(SELECT 1 FROM fl_dropship_pallet_task t WHERE t.id=#{taskId} AND t.task_status='COMPLETED')
""")
int completeInbound(@Param("taskId") long taskId, @Param("tenantId") long tenantId,
@Param("inboundId") long inboundId, @Param("operatorId") long operatorId);
@Select("""
SELECT c.* FROM fl_dropship_pallet_carton c WHERE c.pallet_id=#{palletId}
ORDER BY c.sku_code,c.inbound_detail_id,c.id
""")
List<CartonRow> cartons(@Param("palletId") long palletId);
@Getter @Setter class TaskRow {
private Long id; private Long tenantId; private Long inboundId; private Long warehouseId;
private String inboundCode; private String customerName; private String warehouseName;
private String taskStatus; private Integer totalCartons; private Integer allocatedCartons;
private Long assigneeId; private String assigneeName; private Integer version;
private String processStatus; private String fulfillmentType;
}
@Getter @Setter class PalletRow {
private Long id; private Long taskId; private Long tenantId; private Long inboundId;
private Long warehouseId; private String palletNo; private String palletStatus;
private String openOperationId; private String sealOperationId; private Integer version;
private LocalDateTime completedAt;
}
@Getter @Setter class LabelRow {
private Long cartonLabelId; private Long inboundDetailId; private String skuCode;
private String cargoName; private Integer detailPackageCount; private String inboundCode;
private String boxCode;
}
@Getter @Setter class CartonRow {
private Long inboundId; private Long inboundDetailId; private String skuCode;
private String cargoName; private String boxCode;
}
}
@@ -26,7 +26,9 @@ public interface InboundOrderMapper extends BaseMapper<InboundOrderEntity> {
@Update("""
UPDATE fl_inbound_order
SET process_status = 'WAIT_SPLIT', measure_review_status = 'APPROVED',
SET process_status = CASE WHEN fulfillment_type='ONE_CLICK_DROPSHIP'
THEN 'WAIT_PALLETIZE' ELSE 'WAIT_SPLIT' END,
measure_review_status = 'APPROVED',
actual_gross_weight_kg = (SELECT SUM(weight.gross_weight_kg)
FROM fl_inbound_weight_result weight
WHERE weight.tenant_id = #{tenantId} AND weight.inbound_id = #{inboundId}
@@ -41,7 +43,9 @@ public interface InboundOrderMapper extends BaseMapper<InboundOrderEntity> {
volume_weight_kg = (SELECT COALESCE(SUM(item.volume_weight_kg), 0)
FROM fl_inbound_measurement_item item
WHERE item.tenant_id = #{tenantId} AND item.measurement_id = #{measurementId}),
inbounded_at = CURRENT_TIMESTAMP(3), updated_by = #{operatorId}, version = version + 1
inbounded_at = CASE WHEN fulfillment_type='ONE_CLICK_DROPSHIP'
THEN NULL ELSE CURRENT_TIMESTAMP(3) END,
updated_by = #{operatorId}, version = version + 1
WHERE tenant_id = #{tenantId} AND id = #{inboundId} AND deleted = 0
AND process_status = 'MEASURE_PENDING' AND measure_review_status = 'PENDING'
AND version = #{expectedVersion}
@@ -65,7 +69,10 @@ public interface InboundOrderMapper extends BaseMapper<InboundOrderEntity> {
@Update("""
UPDATE fl_inbound_order
SET process_status = 'WAIT_SPLIT', inbounded_at = CURRENT_TIMESTAMP(3),
SET process_status = CASE WHEN fulfillment_type='ONE_CLICK_DROPSHIP'
THEN 'WAIT_PALLETIZE' ELSE 'WAIT_SPLIT' END,
inbounded_at = CASE WHEN fulfillment_type='ONE_CLICK_DROPSHIP'
THEN NULL ELSE CURRENT_TIMESTAMP(3) END,
updated_by = #{operatorId}, version = version + 1
WHERE tenant_id = #{tenantId} AND id = #{inboundId} AND deleted = 0
AND process_status = 'MEASURE_REJECTED' AND measure_review_status = 'REJECTED'
@@ -80,7 +80,7 @@ public interface LoadingCompletedEventMapper {
""")
List<PalletRow> palletRows(@Param("planId") long planId);
/** 一件代发现场逐箱扫描结果;同一实物托盘可以包含多个客户,但行仍保留客户与入仓单归属。 */
/** 一件代发测量后合托的逐箱结果;装柜只读取已冻结事实,不再现场重新打托。 */
@Select("""
SELECT carton.tenant_id AS account_id,carton.inbound_id AS source_inbound_id,
pallet.id AS pallet_id,1 AS actual_binding,
@@ -88,20 +88,28 @@ public interface LoadingCompletedEventMapper {
COUNT(*) OVER (PARTITION BY pallet.id,carton.tenant_id,carton.inbound_id) AS allocated_package_count,
carton.inbound_detail_id AS source_inbound_detail_id,carton.sku_code,
item.product_name_zh,item.package_count,item.product_quantity,carton.box_code
FROM fl_loading_work_pallet pallet
JOIN fl_loading_work_pallet_carton carton ON carton.work_pallet_id=pallet.id
JOIN fl_loading_plan_item item ON item.id=carton.loading_plan_item_id AND item.deleted=0
JOIN fl_loading_pick_task task ON task.id=pallet.pick_task_id
AND task.loading_plan_id=pallet.loading_plan_id
WHERE pallet.loading_plan_id=#{planId} AND pallet.pallet_status='SEALED'
FROM fl_dropship_pallet pallet
JOIN fl_dropship_pallet_carton carton ON carton.pallet_id=pallet.id
JOIN fl_loading_plan_item item ON item.loading_plan_id=#{planId}
AND item.account_id=carton.tenant_id AND item.inbound_id=carton.inbound_id
AND item.inbound_detail_id=carton.inbound_detail_id AND item.deleted=0
WHERE pallet.pallet_status='COMPLETED'
ORDER BY carton.tenant_id,carton.inbound_id,pallet.id,item.inbound_detail_id,carton.id
""")
List<PalletRow> actualPalletRows(@Param("planId") long planId);
@Select("SELECT COUNT(*) FROM fl_loading_work_pallet WHERE loading_plan_id=#{planId} AND pallet_status<>'CANCELLED'")
@Select("""
SELECT COUNT(DISTINCT p.id) FROM fl_dropship_pallet p
JOIN fl_loading_plan_item i ON i.loading_plan_id=#{planId} AND i.account_id=p.tenant_id
AND i.inbound_id=p.inbound_id AND i.deleted=0 WHERE p.pallet_status='COMPLETED'
""")
int workPalletCount(@Param("planId") long planId);
@Select("SELECT COUNT(*) FROM fl_loading_work_pallet WHERE loading_plan_id=#{planId} AND pallet_status='OPEN'")
@Select("""
SELECT COUNT(DISTINCT p.id) FROM fl_dropship_pallet p
JOIN fl_loading_plan_item i ON i.loading_plan_id=#{planId} AND i.account_id=p.tenant_id
AND i.inbound_id=p.inbound_id AND i.deleted=0 WHERE p.pallet_status='OPEN'
""")
int openWorkPalletCount(@Param("planId") long planId);
@Getter @Setter
@@ -244,7 +244,7 @@ public class InboundOrderRepository {
.filter(StringUtils::hasText)
.collect(Collectors.joining("、"));
return new InboundOrderDetail(InboundOrderSummary.from(order, transport, pickup, locationCodes),
order.getPublicRemark(),
order.getPublicRemark(), List.of(),
palletSummaries, fileSummaries, noteSummaries, historySummaries);
}
@@ -0,0 +1,197 @@
package com.baiyue.firstleg.service;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.firstleg.dto.DropshipPalletizationDtos;
import com.baiyue.firstleg.dto.DropshipPalletizationDtos.Pallet;
import com.baiyue.firstleg.dto.DropshipPalletizationDtos.ScanResult;
import com.baiyue.firstleg.dto.DropshipPalletizationDtos.SkuItem;
import com.baiyue.firstleg.dto.DropshipPalletizationDtos.Task;
import com.baiyue.firstleg.entity.InboundOrderEntity;
import com.baiyue.firstleg.mapper.DropshipPalletizationMapper;
import com.baiyue.firstleg.mapper.DropshipPalletizationMapper.CartonRow;
import com.baiyue.firstleg.mapper.DropshipPalletizationMapper.LabelRow;
import com.baiyue.firstleg.mapper.DropshipPalletizationMapper.PalletRow;
import com.baiyue.firstleg.mapper.DropshipPalletizationMapper.TaskRow;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/** 测量完成即下发的一件代发合托,不创建国内仓库托盘、库位或库存。 */
@Service
public class DropshipPalletizationService {
@Resource private DropshipPalletizationMapper mapper;
@Resource private FirstlegDocumentNumberGenerator numbers;
public void createTask(InboundOrderEntity inbound) {
if (!"ONE_CLICK_DROPSHIP".equals(inbound.getFulfillmentType())) return;
if (inbound.getTotalPackageCount() == null || inbound.getTotalPackageCount() < 1) {
throw invalid("一件代发测量完成箱数无效,不能下发合托任务");
}
mapper.insertTask(IdWorker.getId(), inbound.getTenantId(), inbound.getId(),
inbound.getWarehouseId(), inbound.getTotalPackageCount());
}
public List<DropshipPalletizationDtos.PendingTask> pending(long operatorId, String keyword) {
return mapper.pending(operatorId, normalize(keyword));
}
@Transactional
public Task claim(long taskId, long warehouseId, long operatorId, String operatorName) {
if (mapper.claim(taskId, warehouseId, operatorId, operatorName) != 1) {
throw conflict("任务已被其他人员领取或状态已变化");
}
return task(taskId, warehouseId, operatorId);
}
public Task task(long taskId, long warehouseId, long operatorId) {
TaskRow row = requireTask(taskId, warehouseId);
if (row.getAssigneeId() != null && row.getAssigneeId() != operatorId) {
throw conflict("任务已由其他人员领取");
}
return view(row);
}
@Transactional
public Pallet open(long taskId, DropshipPalletizationDtos.OpenPalletRequest request,
long operatorId, String operatorName) {
TaskRow task = requireWorkingTask(taskId, request.warehouseId(), operatorId);
String operationId = request.operationId().trim();
PalletRow replay = mapper.palletByOpenOperation(operationId);
if (replay != null) {
if (replay.getTaskId() != taskId) throw conflict("开托幂等号已用于其他任务");
return palletView(replay, task.getInboundCode());
}
long id = IdWorker.getId();
mapper.insertPallet(id, taskId, task.getTenantId(), task.getInboundId(), task.getWarehouseId(),
operationId, operatorId, operatorName);
return palletView(mapper.pallet(taskId, id, request.warehouseId()), task.getInboundCode());
}
@Transactional
public ScanResult scan(long taskId, long palletId, DropshipPalletizationDtos.ScanRequest request,
long operatorId, String operatorName) {
TaskRow task = requireWorkingTask(taskId, request.warehouseId(), operatorId);
PalletRow pallet = requireOpenPallet(taskId, palletId, request.warehouseId());
String operationId = request.operationId().trim();
LabelRow scanned = mapper.label(taskId, request.boxCode().trim());
if (scanned == null) throw new BizException(ResultCode.NOT_FOUND, "箱唛不属于当前一件代发入仓单");
if (!StringUtils.hasText(scanned.getSkuCode())) throw conflict("箱唛缺少SKU,不能合托");
if (mapper.operationCount(operationId) > 0) {
return scanResult(scanned, request.cartonCount(), task, pallet);
}
List<LabelRow> labels = mapper.unboundLabels(taskId, task.getTenantId(), task.getInboundId(),
scanned.getInboundDetailId(), scanned.getBoxCode(), request.cartonCount());
if (labels.size() != request.cartonCount()) {
throw conflict("本SKU剩余未上托箱数不足,请刷新任务后重试");
}
for (LabelRow label : labels) {
mapper.insertCarton(IdWorker.getId(), taskId, palletId, task.getTenantId(), task.getInboundId(),
label.getInboundDetailId(), label.getCartonLabelId(), label.getSkuCode(), label.getCargoName(),
label.getBoxCode(), operationId, operatorId, operatorName);
}
if (mapper.addAllocated(taskId, operatorId, labels.size()) != 1) {
throw conflict("任务箱数已变化,请刷新后重试");
}
TaskRow refreshed = requireTask(taskId, request.warehouseId());
return scanResult(scanned, labels.size(), refreshed,
mapper.pallet(taskId, palletId, request.warehouseId()));
}
@Transactional
public Task seal(long taskId, long palletId, DropshipPalletizationDtos.SealPalletRequest request,
long operatorId, String operatorName) {
TaskRow task = requireWorkingTask(taskId, request.warehouseId(), operatorId);
PalletRow replay = mapper.palletBySealOperation(request.operationId().trim());
if (replay != null) {
if (replay.getTaskId() != taskId || replay.getId() != palletId) {
throw conflict("封托幂等号已用于其他托盘");
}
return view(requireTask(taskId, request.warehouseId()));
}
requireOpenPallet(taskId, palletId, request.warehouseId());
String palletNo = numbers.next("DP");
if (mapper.seal(taskId, palletId, request.expectedVersion(), palletNo, request.operationId().trim(),
operatorId, operatorName) != 1) {
throw conflict("空托盘、托盘状态或版本已变化,请刷新后重试");
}
TaskRow refreshed = requireTask(taskId, request.warehouseId());
if (refreshed.getAllocatedCartons().equals(refreshed.getTotalCartons())
&& mapper.openPalletCount(taskId) == 0) {
if (mapper.completeTask(taskId) != 1
|| mapper.completeInbound(taskId, refreshed.getTenantId(), refreshed.getInboundId(), operatorId) != 1) {
throw conflict("合托完成状态已变化,请刷新后重试");
}
}
return view(requireTask(taskId, request.warehouseId()));
}
public List<Pallet> inboundPallets(long tenantId, long inboundId) {
String inboundCode = mapper.inboundCode(tenantId, inboundId);
if (inboundCode == null) throw new BizException(ResultCode.NOT_FOUND, "入库单不存在");
return mapper.palletsByInbound(tenantId, inboundId).stream()
.map(row -> palletView(row, inboundCode)).toList();
}
private ScanResult scanResult(LabelRow label, int added, TaskRow task, PalletRow pallet) {
return new ScanResult(label.getSkuCode(), label.getCargoName(), label.getInboundCode(), added,
task.getAllocatedCartons(), task.getTotalCartons() - task.getAllocatedCartons(),
palletView(pallet, task.getInboundCode()));
}
private Task view(TaskRow row) {
return new Task(row.getId(), row.getInboundId(), row.getInboundCode(), row.getTenantId(),
row.getCustomerName(), row.getWarehouseId(), row.getWarehouseName(), row.getTotalCartons(),
row.getAllocatedCartons(), row.getTaskStatus(), row.getAssigneeId(), row.getAssigneeName(),
row.getVersion(), mapper.pallets(row.getId()).stream()
.map(pallet -> palletView(pallet, row.getInboundCode())).toList());
}
private Pallet palletView(PalletRow row, String inboundCode) {
List<CartonRow> cartons = mapper.cartons(row.getId());
Map<String, List<CartonRow>> groups = new LinkedHashMap<>();
for (CartonRow carton : cartons) {
groups.computeIfAbsent(carton.getInboundDetailId() + "|" + carton.getSkuCode(), ignored -> new ArrayList<>())
.add(carton);
}
List<SkuItem> items = groups.values().stream().map(values -> {
CartonRow first = values.get(0);
return new SkuItem(first.getInboundId(), inboundCode, first.getInboundDetailId(), first.getSkuCode(),
first.getCargoName(), values.size(), values.stream().map(CartonRow::getBoxCode).toList());
}).toList();
return new Pallet(row.getId(), row.getPalletNo(), row.getPalletStatus(), cartons.size(), items.size(),
row.getVersion(), items, row.getCompletedAt());
}
private TaskRow requireWorkingTask(long taskId, long warehouseId, long operatorId) {
TaskRow task = requireTask(taskId, warehouseId);
if (!"CLAIMED".equals(task.getTaskStatus())) throw conflict("请先领取待合托任务");
if (task.getAssigneeId() == null || task.getAssigneeId() != operatorId) throw conflict("任务不属于当前操作员");
if (!"WAIT_PALLETIZE".equals(task.getProcessStatus())) throw conflict("入仓单已不在待合托状态");
return task;
}
private TaskRow requireTask(long taskId, long warehouseId) {
TaskRow task = mapper.task(taskId, warehouseId);
if (task == null) throw new BizException(ResultCode.NOT_FOUND, "未找到当前仓库的一件代发合托任务");
return task;
}
private PalletRow requireOpenPallet(long taskId, long palletId, long warehouseId) {
PalletRow pallet = mapper.pallet(taskId, palletId, warehouseId);
if (pallet == null) throw new BizException(ResultCode.NOT_FOUND, "托盘不存在");
if (!"OPEN".equals(pallet.getPalletStatus())) throw conflict("托盘已完成,不能继续上托");
return pallet;
}
private static String normalize(String value) { return StringUtils.hasText(value) ? value.trim() : null; }
private static BizException conflict(String message) { return new BizException(ResultCode.CONFLICT, message); }
private static BizException invalid(String message) { return new BizException(ResultCode.VALIDATION_FAILED, message); }
}
@@ -19,7 +19,7 @@ import java.util.Set;
public class FirstlegDocumentNumberGenerator {
private static final ZoneId BUSINESS_ZONE = ZoneId.of("Asia/Shanghai");
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyMMdd", Locale.ROOT);
private static final Set<String> SUPPORTED_PREFIXES = Set.of("OB", "TA", "CB", "CAB", "LP");
private static final Set<String> SUPPORTED_PREFIXES = Set.of("OB", "TA", "CB", "CAB", "LP", "DP");
private static final long MAX_DAILY_SEQUENCE = 9_999L;
private final JdbcTemplate jdbc;
@@ -19,6 +19,7 @@ import org.springframework.util.StringUtils;
import java.math.BigDecimal;
import java.util.List;
import java.util.ArrayList;
import com.baiyue.firstleg.dto.DropshipPalletizationDtos.Pallet;
/** Allocates stable carton codes and forwards immutable label facts to Print. */
@Service
@@ -91,6 +92,28 @@ public class InboundCartonLabelPrintService {
return ResponseEntity.status(response.getStatusCode()).headers(headers).body(response.getBody());
}
/** 一件代发托盘标签:托盘号、入仓码、SKU箱数和托盘二维码。 */
public ResponseEntity<byte[]> generateDropshipPallet(Pallet pallet, String requestId) {
if (pallet == null || !StringUtils.hasText(pallet.palletNo()) || pallet.items().isEmpty()) {
throw new BizException(ResultCode.CONFLICT, "只有已完成且包含货物的一件代发托盘可以打印");
}
String inboundCodes = pallet.items().stream().map(item -> item.inboundCode())
.filter(StringUtils::hasText).distinct().collect(java.util.stream.Collectors.joining("/"));
String skuSummary = pallet.items().stream()
.map(item -> item.skuCode() + "×" + item.cartonCount())
.collect(java.util.stream.Collectors.joining(" / "));
PrintPdfClient.Label label = new PrintPdfClient.Label(pallet.palletNo(), inboundCodes,
"CTNS:" + pallet.cartonCount(), skuSummary, pallet.palletNo(), null);
PrintPdfClient.InboundMarkPdfRequest request = new PrintPdfClient.InboundMarkPdfRequest(
"INBOUND_MARK_V1", new PrintPdfClient.PageSpec(PAGE_MM, PAGE_MM),
pallet.palletNo() + "-托盘唛.pdf", List.of(label));
try {
return requirePdf(printClient.generateInboundMarks(internalToken, requestId, request));
} catch (FeignException | NoFallbackAvailableException exception) {
throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "打印服务暂不可用,请稍后重试");
}
}
private ResponseEntity<byte[]> generatePdf(String fileName, List<InboundCartonLabels.Label> labels,
String requestId) {
PrintPdfClient.InboundMarkPdfRequest request = new PrintPdfClient.InboundMarkPdfRequest(
@@ -105,7 +128,7 @@ public class InboundCartonLabelPrintService {
private PrintPdfClient.Label toPrintLabel(InboundCartonLabels.Label label) {
return new PrintPdfClient.Label(label.topCode(), label.warehouseText(), label.cartonText(),
label.routeText(), label.boxCode());
label.routeText(), label.boxCode(), label.skuCode());
}
private ResponseEntity<byte[]> requirePdf(ResponseEntity<byte[]> response) {
@@ -291,7 +291,7 @@ public class InboundCartonLabelService implements CartonLabelBindingResolver {
new InboundCartonLabels.Label(entity.getId(), entity.getInboundDetailId(), entity.getCargoItemNo(),
entity.getCargoName(), entity.getDetailPackageCount(), entity.getBoxCode(),
entity.getTotalCartonCount(), entity.getCartonIndex(), entity.getBoxSpecification(),
entity.getLengthCm(), entity.getWidthCm(), entity.getHeightCm(), inbound.getInboundCode(),
entity.getLengthCm(), entity.getWidthCm(), entity.getHeightCm(), entity.getSkuCode(), inbound.getInboundCode(),
fallback(inbound.getWarehouseName(), "WAREHOUSE"),
"CTNS:" + entity.getTotalCartonCount() + "-" + entity.getCartonIndex(),
fallback(inbound.getChannelName(), fallback(inbound.getChannelCode(), "ROUTE"))))
@@ -19,6 +19,8 @@ import org.springframework.util.StringUtils;
public class InboundOrderQueryService {
@Resource
private InboundOrderRepository repository;
@Resource
private DropshipPalletizationService dropshipPalletization;
public PageResult<InboundOrderSummary> page(Long tenantId, InboundOrderQuery query) {
validateQuery(query);
@@ -30,7 +32,11 @@ public class InboundOrderQueryService {
if (order == null) {
throw new BizException(ResultCode.NOT_FOUND, "入库单不存在");
}
return repository.detail(tenantId, order);
InboundOrderDetail detail = repository.detail(tenantId, order);
if (!"ONE_CLICK_DROPSHIP".equals(order.getFulfillmentType())) return detail;
return new InboundOrderDetail(detail.summary(), detail.publicRemark(),
dropshipPalletization.inboundPallets(tenantId, inboundId), detail.palletLocations(),
detail.files(), detail.internalNotes(), detail.statusHistory());
}
private void validateQuery(InboundOrderQuery query) {
@@ -33,6 +33,8 @@ public class MeasurementReviewService {
private InboundIdempotencyService idempotency;
@Resource
private InboundProperties properties;
@Resource
private DropshipPalletizationService dropshipPalletization;
@Transactional
public void approve(long tenantId, long operatorId, String operatorName, long inboundId,
@@ -117,8 +119,13 @@ public class MeasurementReviewService {
}
repository.insertReview(reviewRecord(tenantId, inboundId, request.measurementId(), result,
request.reason(), false, null, false, operatorId, operatorName));
if (approved && "ONE_CLICK_DROPSHIP".equals(order.getFulfillmentType())) {
dropshipPalletization.createTask(order);
}
String approvedStatus = "ONE_CLICK_DROPSHIP".equals(order.getFulfillmentType())
? "WAIT_PALLETIZE" : "WAIT_SPLIT";
repository.insertHistory(history(tenantId, inboundId, "MEASURE_PENDING",
approved ? "WAIT_SPLIT" : "MEASURE_REJECTED",
approved ? approvedStatus : "MEASURE_REJECTED",
approved ? "MEASURE_APPROVE" : "MEASURE_REJECT", request.reason(), operatorId, operatorName));
idempotency.complete(context, inboundId);
}
@@ -152,9 +159,13 @@ public class MeasurementReviewService {
if (repository.releaseRejected(tenantId, inboundId, operatorId, request.expectedVersion()) != 1) {
throw concurrentConflict();
}
if ("ONE_CLICK_DROPSHIP".equals(order.getFulfillmentType())) {
dropshipPalletization.createTask(order);
}
repository.insertReview(reviewRecord(tenantId, inboundId, request.measurementId(), "REJECTED",
null, true, request.reason(), true, operatorId, operatorName));
repository.insertHistory(history(tenantId, inboundId, "MEASURE_REJECTED", "WAIT_SPLIT",
repository.insertHistory(history(tenantId, inboundId, "MEASURE_REJECTED",
"ONE_CLICK_DROPSHIP".equals(order.getFulfillmentType()) ? "WAIT_PALLETIZE" : "WAIT_SPLIT",
"MEASURE_EXCEPTION_RELEASE", request.reason(), operatorId, operatorName));
idempotency.complete(context, inboundId);
}
@@ -28,7 +28,7 @@ import java.util.stream.Collectors;
@Service
public class UnifiedInboundQueryService {
private static final Set<String> MAIN_STATUSES = Set.of(
"PENDING_REVIEW", "WAIT_MEASURE", "WAIT_SPLIT", "LOADING", "LOADED", "DELIVERED");
"PENDING_REVIEW", "WAIT_MEASURE", "WAIT_PALLETIZE", "WAIT_SPLIT", "LOADING", "LOADED", "DELIVERED");
private static final Set<String> REVIEW_STATUSES = Set.of("UNRECORDED", "PENDING", "APPROVED", "REJECTED");
@Resource private InboundOrderRepository inbounds;
@@ -165,6 +165,7 @@ public class UnifiedInboundQueryService {
case "WAIT_MEASURE", "MEASURING", "MEASURE_PENDING", "MEASURE_REJECTED",
"WAIT_PRICING", "FEE_CONFIRMING", "INBOUNDING" -> "WAIT_MEASURE";
case "WAIT_SPLIT" -> "WAIT_SPLIT";
case "WAIT_PALLETIZE" -> "WAIT_PALLETIZE";
case "LOADING", "LOADED", "DELIVERED" -> status;
default -> null;
};
@@ -175,6 +176,7 @@ public class UnifiedInboundQueryService {
return switch (mainStatus) {
case "PENDING_REVIEW" -> "APPROVE_ORDER";
case "WAIT_MEASURE" -> "MEASURE_PENDING".equals(processStatus) ? "CONFIRM_MEASUREMENT" : null;
case "WAIT_PALLETIZE" -> "PALLETIZE_DROPSHIP";
case "WAIT_SPLIT" -> !"WAIT_SPLIT".equals(processStatus) ? null
: "CONFIRMED".equals(preloadStatus) ? null
: preloadStatus == null ? "SMART_SPLIT" : "CONFIRM_PRELOAD";
@@ -195,6 +197,7 @@ public class UnifiedInboundQueryService {
case "WAIT_MEASURE" -> List.of("WAIT_MEASURE", "MEASURING", "MEASURE_PENDING", "MEASURE_REJECTED",
"WAIT_PRICING", "FEE_CONFIRMING", "INBOUNDING");
case "WAIT_SPLIT" -> List.of("WAIT_SPLIT");
case "WAIT_PALLETIZE" -> List.of("WAIT_PALLETIZE");
case "LOADING", "LOADED", "DELIVERED" -> List.of(mainStatus);
default -> List.of("__NO_MATCH__");
};