贴标申请,仓储转运
This commit is contained in:
+1
@@ -514,6 +514,7 @@ public class DeliveryTransferService {
|
||||
existing = outboundOrders.findByIdempotency(customerId, request.idempotencyKey().trim());
|
||||
if (existing != null) return replayReturnOutbound(customerId, existing, requestHash);
|
||||
for (ReturnInventoryCarton carton : locked) {
|
||||
com.baiyue.lastleg.returnreceipt.service.ReturnCartonCountGuard.requireConfirmed(carton);
|
||||
if (!"AVAILABLE".equals(carton.getStatus())) throw conflict("退货箱已被其他作业占用:" + carton.getCartonMark());
|
||||
var contents = returnCartonItems.lockCarton(carton.getId());
|
||||
if (contents.isEmpty() || contents.stream().anyMatch(line -> value(line.getPackingReservedQuantity()) > 0))
|
||||
|
||||
+6
-1
@@ -13,8 +13,13 @@ public record LastlegOrderDetailResponse(
|
||||
List<LastlegOrderDetail> cargo,
|
||||
List<History> statusHistory,
|
||||
List<OverseasFeeSource> fees,
|
||||
List<Operation> operationHistory
|
||||
List<Operation> operationHistory,
|
||||
com.baiyue.lastleg.returnreceipt.dto.ReturnOrderResponses.Detail returnReceipt
|
||||
) {
|
||||
public LastlegOrderDetailResponse(LastlegOrderSummary order, List<LastlegOrderCommandDtos.ManualNodeView> manualNodes,
|
||||
List<LastlegOrderDetail> cargo, List<History> statusHistory, List<OverseasFeeSource> fees, List<Operation> operationHistory) {
|
||||
this(order, manualNodes, cargo, statusHistory, fees, operationHistory, null);
|
||||
}
|
||||
public record History(Long id, String fromStatus, String toStatus, String remark, LocalDateTime createdAt) {}
|
||||
public record Operation(Long id, String operationNode, String operatorName, String result, LocalDateTime createdAt) {}
|
||||
}
|
||||
|
||||
+7
@@ -615,6 +615,13 @@ public class PdaRelabelService {
|
||||
}
|
||||
|
||||
private PdaRelabelTask requireOwnedTask(RelabelApplication application, String operatorKey, boolean lock) {
|
||||
for (RelabelItem source : items.selectList(new LambdaQueryWrapper<RelabelItem>()
|
||||
.eq(RelabelItem::getApplicationId, application.getId()).eq(RelabelItem::getCustomerId, application.getCustomerId()))) {
|
||||
if (source.getReturnCartonId() == null) continue;
|
||||
ReturnInventoryCarton carton = returnCartons.selectById(source.getReturnCartonId());
|
||||
if (carton == null) throw conflict("退货来源箱不存在");
|
||||
com.baiyue.lastleg.returnreceipt.service.ReturnCartonCountGuard.requireConfirmed(carton);
|
||||
}
|
||||
if (!PROCESSING.equals(application.getStatus())) {
|
||||
throw conflict("Relabel application is no longer executable");
|
||||
}
|
||||
|
||||
+4
@@ -334,6 +334,8 @@ public class RelabelApplicationService implements WalletResultHandler {
|
||||
private RelabelResponses.Detail create(String customerId, String operatorId, RelabelRequests.Create request,
|
||||
Long existingTailOrderId) {
|
||||
if (!StringUtils.hasText(request.operationWarehouseId())) throw badRequest("Operation warehouse is required");
|
||||
if (request.items().stream().map(RelabelApplicationService::operationType).distinct().count() != 1)
|
||||
throw badRequest("一张贴标申请只能选择一种换标类型,不能混合提交只换箱唛、只换 SKU 或 SKU 和箱唛都换");
|
||||
LastlegOrder linkedSourceOrder = existingTailOrderId == null ? null : orders.selectById(existingTailOrderId);
|
||||
boolean warehouseTransferRelabel = linkedSourceOrder != null
|
||||
&& "WAREHOUSE_TRANSFER".equals(linkedSourceOrder.getOrderType());
|
||||
@@ -393,6 +395,7 @@ public class RelabelApplicationService implements WalletResultHandler {
|
||||
}
|
||||
if (!carton.getCartonMark().equals(item.cartonMark().trim()))
|
||||
throw conflict("来源退货箱号与选择的库存不一致");
|
||||
com.baiyue.lastleg.returnreceipt.service.ReturnCartonCountGuard.requireConfirmed(carton);
|
||||
if ("PARCEL".equals(carton.getSourceUnitType()) && !"SKU_LABEL".equals(item.operationType()))
|
||||
throw conflict("散件退货库存只能贴 SKU,请先合箱后申请箱唛或混贴");
|
||||
final long sourceReturnCartonId = carton.getId();
|
||||
@@ -1037,6 +1040,7 @@ public class RelabelApplicationService implements WalletResultHandler {
|
||||
|| !"RELABEL_PENDING".equals(carton.getStatus())
|
||||
|| intValue(source.getNotifiedPackageCount()) != 1)
|
||||
throw conflict("退货整箱换标出库必须覆盖该箱全部 SKU 数量:" + source.getCartonMark());
|
||||
com.baiyue.lastleg.returnreceipt.service.ReturnCartonCountGuard.requireConfirmed(carton);
|
||||
}
|
||||
OutboundOrder outbound = new OutboundOrder();
|
||||
outbound.setId(IdWorker.getId()); outbound.setCustomerId(application.getCustomerId());
|
||||
|
||||
+32
@@ -22,6 +22,38 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@PreAuthorize("hasAuthority('lastleg:pda:operate') and hasAuthority('ROLE_OVERSEAS_WAREHOUSE_PDA_OPERATOR')")
|
||||
public class PdaReturnInventoryController {
|
||||
@Resource private ReturnInventoryService service;
|
||||
@Resource private com.baiyue.lastleg.returnreceipt.service.ReturnCartonCountService counting;
|
||||
|
||||
@GetMapping("/counting/tasks")
|
||||
public ApiResponse<com.baiyue.common.core.api.PageResult<com.baiyue.lastleg.returnreceipt.dto.ReturnCartonCountDtos.Task>> countingTasks(
|
||||
@AuthenticationPrincipal AuthenticatedUser user, @Valid com.baiyue.lastleg.returnreceipt.dto.ReturnCartonCountDtos.Query query) {
|
||||
return ApiResponse.success(counting.page(PdaWarehouseIdentity.require(user), query));
|
||||
}
|
||||
@GetMapping("/counting/cartons/{cartonMark}")
|
||||
public ApiResponse<com.baiyue.lastleg.returnreceipt.dto.ReturnCartonCountDtos.Task> countingCarton(
|
||||
@AuthenticationPrincipal AuthenticatedUser user, @PathVariable String cartonMark) {
|
||||
return ApiResponse.success(counting.scan(PdaWarehouseIdentity.require(user), cartonMark));
|
||||
}
|
||||
@GetMapping("/counting/scan/{code}")
|
||||
public ApiResponse<java.util.List<com.baiyue.lastleg.returnreceipt.dto.ReturnCartonCountDtos.Task>> scanCounting(
|
||||
@AuthenticationPrincipal AuthenticatedUser user, @PathVariable String code) {
|
||||
return ApiResponse.success(counting.scanTasks(PdaWarehouseIdentity.require(user), code));
|
||||
}
|
||||
@GetMapping("/counting/current")
|
||||
public ApiResponse<com.baiyue.lastleg.returnreceipt.dto.ReturnCartonCountDtos.Task> currentCounting(@AuthenticationPrincipal AuthenticatedUser user) {
|
||||
return ApiResponse.success(counting.current(PdaWarehouseIdentity.require(user)));
|
||||
}
|
||||
@PostMapping("/counting/receipts/{receiptNo}/begin")
|
||||
public ApiResponse<com.baiyue.lastleg.returnreceipt.dto.ReturnCartonCountDtos.Task> beginCounting(
|
||||
@AuthenticationPrincipal AuthenticatedUser user, @PathVariable String receiptNo) {
|
||||
return ApiResponse.success(counting.begin(PdaWarehouseIdentity.require(user), receiptNo));
|
||||
}
|
||||
@PostMapping("/counting/receipts/{receiptNo}")
|
||||
public ApiResponse<com.baiyue.lastleg.returnreceipt.dto.ReturnCartonCountDtos.Task> confirmCounting(
|
||||
@AuthenticationPrincipal AuthenticatedUser user, @PathVariable String receiptNo,
|
||||
@Valid @RequestBody com.baiyue.lastleg.returnreceipt.dto.ReturnCartonCountDtos.Confirm request) {
|
||||
return ApiResponse.success(counting.confirm(PdaWarehouseIdentity.require(user), receiptNo, request));
|
||||
}
|
||||
|
||||
@GetMapping("/packing/orders")
|
||||
public ApiResponse<ReturnInventoryResponses.PackingPreview> packingOrders(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.baiyue.lastleg.returnreceipt.dto;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.util.List;
|
||||
|
||||
public final class ReturnCartonCountDtos {
|
||||
private ReturnCartonCountDtos() { }
|
||||
public record Query(@Min(1) long page, @Min(1) @Max(50) long size, @Size(max=128) String keyword) { }
|
||||
public record Sku(String skuCode, String productNameZh, long quantity) { }
|
||||
public record Box(String cartonMark, List<Sku> items) { }
|
||||
public record Task(String receiptNo, String returnNo, String palletCode, String status, List<Sku> expectedItems, List<Box> boxes) { }
|
||||
public record CountItem(@NotBlank @Size(max=128) String skuCode, @Min(1) long quantity) { }
|
||||
public record CountBox(@NotBlank @Size(max=128) String cartonMark, @NotEmpty @Size(max=100) List<@NotNull @Valid CountItem> items) { }
|
||||
public record Confirm(@NotBlank @Size(max=128) String idempotencyKey, @NotEmpty @Size(max=500) List<@NotNull @Valid CountBox> boxes) { }
|
||||
}
|
||||
+6
-1
@@ -14,11 +14,16 @@ public final class ReturnOrderResponses {
|
||||
public record Item(String skuCode, String productNameZh, long expectedQuantity,
|
||||
long receivedQuantity) { }
|
||||
public record Detail(Summary order, String remark, List<Item> items, List<Receipt> receipts, List<PalletDetail> pallets, List<Photo> photos,
|
||||
List<PackingDetail> packingDetails) {
|
||||
List<PackingDetail> packingDetails, List<ReceiptCarton> cartons) {
|
||||
public Detail(Summary order, String remark, List<Item> items, List<Receipt> receipts, List<PalletDetail> pallets, List<Photo> photos, List<PackingDetail> packingDetails) {
|
||||
this(order, remark, items, receipts, pallets, photos, packingDetails, List.of());
|
||||
}
|
||||
public Detail(Summary order, String remark, List<Item> items, List<Receipt> receipts, List<PalletDetail> pallets, List<Photo> photos) {
|
||||
this(order, remark, items, receipts, pallets, photos, List.of());
|
||||
}
|
||||
}
|
||||
public record ReceiptCarton(String receiptNo, String palletCode, String cartonMark, String countStatus,
|
||||
LocalDateTime countedAt, List<ReceiptItem> items) { }
|
||||
public record PackingDetail(String sourceCartonMark, String targetCartonMark, String returnNo,
|
||||
String skuCode, long quantity, LocalDateTime createdAt) { }
|
||||
public record Photo(long fileId, String fileName, String contentType, long sizeBytes) { }
|
||||
|
||||
+3
@@ -30,6 +30,9 @@ public class ReturnInventoryCarton {
|
||||
private String relabelStatus;
|
||||
private LocalDateTime relabelCompletedAt;
|
||||
private String status;
|
||||
private String countStatus = "CONFIRMED";
|
||||
private LocalDateTime countedAt;
|
||||
private String countedBy;
|
||||
private Integer skuTypes;
|
||||
private Long totalQuantity;
|
||||
private BigDecimal length;
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.baiyue.lastleg.returnreceipt.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/** Locks one durable work session per operator, including when no task is active. */
|
||||
@Mapper
|
||||
public interface ReturnCartonCountSessionMapper {
|
||||
record Session(String operatorId, String warehouseId, Long receiptId, LocalDateTime startedAt) { }
|
||||
|
||||
@Insert("INSERT INTO ll_return_carton_count_session(operator_id,warehouse_id,created_at,updated_at) " +
|
||||
"VALUES(#{operator},#{warehouse},NOW(3),NOW(3)) ON DUPLICATE KEY UPDATE operator_id=operator_id")
|
||||
int ensure(@Param("operator") String operator, @Param("warehouse") String warehouse);
|
||||
@Select("SELECT operator_id,warehouse_id,receipt_id,started_at FROM ll_return_carton_count_session WHERE operator_id=#{operator} FOR UPDATE")
|
||||
Session lockOperator(@Param("operator") String operator);
|
||||
@Select("SELECT operator_id,warehouse_id,receipt_id,started_at FROM ll_return_carton_count_session WHERE operator_id=#{operator}")
|
||||
Session findOperator(@Param("operator") String operator);
|
||||
@Select("SELECT operator_id,warehouse_id,receipt_id,started_at FROM ll_return_carton_count_session WHERE receipt_id=#{receiptId}")
|
||||
Session findReceipt(@Param("receiptId") long receiptId);
|
||||
@Update("UPDATE ll_return_carton_count_session SET receipt_id=#{receiptId},warehouse_id=#{warehouse},started_at=NOW(3),updated_at=NOW(3) WHERE operator_id=#{operator} AND receipt_id IS NULL")
|
||||
int claim(@Param("operator") String operator, @Param("warehouse") String warehouse, @Param("receiptId") long receiptId);
|
||||
@Update("UPDATE ll_return_carton_count_session SET receipt_id=NULL,started_at=NULL,updated_at=NOW(3) WHERE operator_id=#{operator} AND receipt_id=#{receiptId}")
|
||||
int release(@Param("operator") String operator, @Param("receiptId") long receiptId);
|
||||
}
|
||||
+1
-1
@@ -16,7 +16,7 @@ public interface ReturnInventoryBalanceMapper extends BaseMapper<ReturnInventory
|
||||
"LEAST(b.available_quantity,COALESCE((SELECT SUM(GREATEST(0,i.quantity-COALESCE(i.packing_reserved_quantity,0))) " +
|
||||
"FROM ll_return_inventory_carton c JOIN ll_return_inventory_carton_item i ON i.carton_id=c.id AND i.customer_id=c.customer_id " +
|
||||
"WHERE c.customer_id=b.customer_id AND c.overseas_warehouse_id=b.overseas_warehouse_id AND i.sku_code=b.sku_code " +
|
||||
"AND c.status='AVAILABLE'),0)) AS available_quantity";
|
||||
"AND c.status='AVAILABLE' AND c.count_status='CONFIRMED'),0)) AS available_quantity";
|
||||
@Select("SELECT * FROM ll_return_inventory_balance WHERE customer_id=#{customerId} " +
|
||||
"AND overseas_warehouse_id=#{warehouseId} AND sku_code=#{skuCode} FOR UPDATE")
|
||||
ReturnInventoryBalance lockSku(@Param("customerId") String customerId, @Param("warehouseId") String warehouseId,
|
||||
|
||||
+2
@@ -11,6 +11,8 @@ import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ReturnInventoryCartonItemMapper extends BaseMapper<ReturnInventoryCartonItem> {
|
||||
@org.apache.ibatis.annotations.Delete("DELETE FROM ll_return_inventory_carton_item WHERE carton_id=#{cartonId}")
|
||||
int deleteCarton(@Param("cartonId") long cartonId);
|
||||
@Select("SELECT COALESCE(SUM(GREATEST(0,i.quantity-COALESCE(i.packing_reserved_quantity,0))),0) " +
|
||||
"FROM ll_return_inventory_carton_item i JOIN ll_return_inventory_carton c ON c.id=i.carton_id " +
|
||||
"WHERE c.customer_id=#{customerId} AND c.overseas_warehouse_id=#{warehouseId} " +
|
||||
|
||||
+12
-9
@@ -10,7 +10,9 @@ import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ReturnInventoryCartonMapper extends BaseMapper<ReturnInventoryCarton> {
|
||||
@Select("SELECT * FROM ll_return_inventory_carton WHERE customer_id=#{customerId} AND overseas_warehouse_id=#{warehouseId} AND status='AVAILABLE' ORDER BY carton_mark")
|
||||
@Select("SELECT * FROM ll_return_inventory_carton WHERE source_receipt_id=#{receiptId} ORDER BY id FOR UPDATE")
|
||||
List<ReturnInventoryCarton> lockReceipt(@Param("receiptId") long receiptId);
|
||||
@Select("SELECT * FROM ll_return_inventory_carton WHERE customer_id=#{customerId} AND overseas_warehouse_id=#{warehouseId} AND status='AVAILABLE' AND count_status='CONFIRMED' ORDER BY carton_mark")
|
||||
List<ReturnInventoryCarton> packingAvailable(@Param("customerId") String customerId, @Param("warehouseId") String warehouseId);
|
||||
|
||||
@Update("UPDATE ll_return_inventory_carton SET location_code=NULL,putaway_at=NULL WHERE id=#{id}")
|
||||
@@ -20,7 +22,7 @@ public interface ReturnInventoryCartonMapper extends BaseMapper<ReturnInventoryC
|
||||
"<if test='query.overseasWarehouseId != null and query.overseasWarehouseId != \"\"'>AND c.overseas_warehouse_id=#{query.overseasWarehouseId} </if>" +
|
||||
"<if test='query.cartonMark != null and query.cartonMark != \"\"'>AND c.carton_mark LIKE CONCAT('%',#{query.cartonMark},'%') </if>" +
|
||||
"<if test='query.returnNo != null and query.returnNo != \"\"'>AND c.source_return_no LIKE CONCAT('%',#{query.returnNo},'%') </if>" +
|
||||
"<if test='query.status != null and query.status != \"\"'>AND c.status=#{query.status} </if>" +
|
||||
"<if test='query.status != null and query.status != \"\"'>AND (CASE WHEN c.count_status='PENDING' AND c.status='AVAILABLE' THEN 'PENDING_COUNT' ELSE c.status END)=#{query.status} </if>" +
|
||||
"<if test='query.skuCode != null and query.skuCode != \"\"'>AND EXISTS(SELECT 1 FROM ll_return_inventory_carton_item i WHERE i.carton_id=c.id AND i.sku_code LIKE CONCAT('%',#{query.skuCode},'%')) </if>";
|
||||
|
||||
@Select("<script>SELECT COUNT(*)" + MERCHANT_FILTER + "</script>")
|
||||
@@ -31,7 +33,7 @@ public interface ReturnInventoryCartonMapper extends BaseMapper<ReturnInventoryC
|
||||
List<ReturnInventoryCarton> merchantPage(@Param("customerId") String customerId,
|
||||
@Param("query") com.baiyue.lastleg.returnreceipt.dto.ReturnInventoryRequests.Query query, @Param("offset") long offset);
|
||||
|
||||
@Select("<script>SELECT * FROM ll_return_inventory_carton WHERE customer_id=#{customerId} AND status='AVAILABLE' " +
|
||||
@Select("<script>SELECT * FROM ll_return_inventory_carton WHERE customer_id=#{customerId} AND status='AVAILABLE' AND count_status='CONFIRMED' " +
|
||||
"<if test='warehouseId != null and warehouseId != \"\"'>AND overseas_warehouse_id=#{warehouseId} </if>" +
|
||||
"ORDER BY created_at DESC,id DESC LIMIT 500</script>")
|
||||
List<ReturnInventoryCarton> findAvailable(@Param("customerId") String customerId, @Param("warehouseId") String warehouseId);
|
||||
@@ -44,7 +46,7 @@ public interface ReturnInventoryCartonMapper extends BaseMapper<ReturnInventoryC
|
||||
@Select("SELECT * FROM ll_return_inventory_carton WHERE id=#{id} FOR UPDATE")
|
||||
ReturnInventoryCarton lockId(@Param("id") long id);
|
||||
|
||||
@Update("UPDATE ll_return_inventory_carton SET status='DISCARD_PENDING',updated_at=NOW(3) WHERE id=#{id} AND customer_id=#{customerId} AND status='AVAILABLE'")
|
||||
@Update("UPDATE ll_return_inventory_carton SET status='DISCARD_PENDING',updated_at=NOW(3) WHERE id=#{id} AND customer_id=#{customerId} AND status='AVAILABLE' AND count_status='CONFIRMED'")
|
||||
int markDiscardPending(@Param("id") long id, @Param("customerId") String customerId);
|
||||
|
||||
@Update("UPDATE ll_return_inventory_carton SET status=#{status},total_quantity=#{totalQuantity},sku_types=#{skuTypes},updated_at=NOW(3) WHERE id=#{id} AND customer_id=#{customerId} AND status='DISCARD_PENDING'")
|
||||
@@ -71,7 +73,7 @@ public interface ReturnInventoryCartonMapper extends BaseMapper<ReturnInventoryC
|
||||
|
||||
@Update("UPDATE ll_return_inventory_carton SET relabel_application_id=#{applicationId},relabel_no=#{relabelNo}," +
|
||||
"relabel_status='PENDING',status='RELABEL_PENDING',updated_at=#{updatedAt} " +
|
||||
"WHERE id=#{id} AND status='AVAILABLE' AND sku_types>0 AND total_quantity>0 AND relabel_application_id IS NULL")
|
||||
"WHERE id=#{id} AND status='AVAILABLE' AND count_status='CONFIRMED' AND sku_types>0 AND total_quantity>0 AND relabel_application_id IS NULL")
|
||||
int markRelabelPending(@Param("id") long id, @Param("applicationId") long applicationId,
|
||||
@Param("relabelNo") String relabelNo, @Param("updatedAt") java.time.LocalDateTime updatedAt);
|
||||
|
||||
@@ -81,12 +83,13 @@ public interface ReturnInventoryCartonMapper extends BaseMapper<ReturnInventoryC
|
||||
@Select("SELECT * FROM ll_return_inventory_carton WHERE source_receipt_id=#{receiptId} ORDER BY id")
|
||||
List<ReturnInventoryCarton> listReceipt(@Param("receiptId") long receiptId);
|
||||
|
||||
@Select("SELECT c.* FROM ll_return_inventory_carton c JOIN ll_return_inventory_carton_item i ON i.carton_id=c.id WHERE c.customer_id=#{customerId} AND c.overseas_warehouse_id=#{warehouseId} AND c.pallet_code=#{palletCode} AND c.status='AVAILABLE' ORDER BY c.id LIMIT 1 FOR UPDATE")
|
||||
@Select("SELECT c.* FROM ll_return_inventory_carton c JOIN ll_return_inventory_carton_item i ON i.carton_id=c.id WHERE c.customer_id=#{customerId} AND c.overseas_warehouse_id=#{warehouseId} AND c.pallet_code=#{palletCode} AND c.status='AVAILABLE' AND c.count_status='CONFIRMED' ORDER BY c.id LIMIT 1 FOR UPDATE")
|
||||
ReturnInventoryCarton findPalletSku(@Param("customerId") String customerId, @Param("warehouseId") String warehouseId, @Param("palletCode") String palletCode);
|
||||
|
||||
@Select("SELECT * FROM ll_return_inventory_carton WHERE overseas_warehouse_id=#{warehouseId} AND pallet_code=#{palletCode} AND status='AVAILABLE' ORDER BY id LIMIT 1")
|
||||
@Select("SELECT * FROM ll_return_inventory_carton WHERE overseas_warehouse_id=#{warehouseId} AND pallet_code=#{palletCode} AND status='AVAILABLE' AND count_status='CONFIRMED' ORDER BY id LIMIT 1")
|
||||
ReturnInventoryCarton findPalletSkuForCheck(@Param("customerId") String ignoredCustomerId, @Param("warehouseId") String warehouseId, @Param("palletCode") String palletCode);
|
||||
|
||||
// Carrier ownership checks must include boxes still waiting for contents counting.
|
||||
@Select("SELECT * FROM ll_return_inventory_carton WHERE overseas_warehouse_id=#{warehouseId} AND pallet_code=#{palletCode} AND status='AVAILABLE' ORDER BY id")
|
||||
List<ReturnInventoryCarton> listPalletInventory(@Param("warehouseId") String warehouseId, @Param("palletCode") String palletCode);
|
||||
|
||||
@@ -103,7 +106,7 @@ public interface ReturnInventoryCartonMapper extends BaseMapper<ReturnInventoryC
|
||||
"<if test='warehouseId != null and warehouseId != \"\"'>AND c.overseas_warehouse_id=#{warehouseId} </if>" +
|
||||
"<if test='cartonMark != null and cartonMark != \"\"'>AND c.carton_mark LIKE CONCAT('%',#{cartonMark},'%') </if>" +
|
||||
"<if test='returnNo != null and returnNo != \"\"'>AND c.source_return_no LIKE CONCAT('%',#{returnNo},'%') </if>" +
|
||||
"<if test='status != null and status != \"\"'>AND c.status=#{status} </if>" +
|
||||
"<if test='status != null and status != \"\"'>AND (CASE WHEN c.count_status='PENDING' AND c.status='AVAILABLE' THEN 'PENDING_COUNT' ELSE c.status END)=#{status} </if>" +
|
||||
"<if test='skuCode != null and skuCode != \"\"'>AND EXISTS(SELECT 1 FROM ll_return_inventory_carton_item i WHERE i.carton_id=c.id AND i.sku_code LIKE CONCAT('%',#{skuCode},'%')) </if></script>")
|
||||
long platformCount(@Param("customerId") String customerId, @Param("warehouseId") String warehouseId,
|
||||
@Param("cartonMark") String cartonMark, @Param("returnNo") String returnNo, @Param("status") String status, @Param("skuCode") String skuCode);
|
||||
@@ -113,7 +116,7 @@ public interface ReturnInventoryCartonMapper extends BaseMapper<ReturnInventoryC
|
||||
"<if test='warehouseId != null and warehouseId != \"\"'>AND c.overseas_warehouse_id=#{warehouseId} </if>" +
|
||||
"<if test='cartonMark != null and cartonMark != \"\"'>AND c.carton_mark LIKE CONCAT('%',#{cartonMark},'%') </if>" +
|
||||
"<if test='returnNo != null and returnNo != \"\"'>AND c.source_return_no LIKE CONCAT('%',#{returnNo},'%') </if>" +
|
||||
"<if test='status != null and status != \"\"'>AND c.status=#{status} </if>" +
|
||||
"<if test='status != null and status != \"\"'>AND (CASE WHEN c.count_status='PENDING' AND c.status='AVAILABLE' THEN 'PENDING_COUNT' ELSE c.status END)=#{status} </if>" +
|
||||
"<if test='skuCode != null and skuCode != \"\"'>AND EXISTS(SELECT 1 FROM ll_return_inventory_carton_item i WHERE i.carton_id=c.id AND i.sku_code LIKE CONCAT('%',#{skuCode},'%')) </if>" +
|
||||
"ORDER BY c.created_at DESC,c.id DESC LIMIT #{offset},#{size}</script>")
|
||||
List<ReturnInventoryCarton> platformPage(@Param("customerId") String customerId, @Param("warehouseId") String warehouseId,
|
||||
|
||||
+5
@@ -8,6 +8,11 @@ import org.apache.ibatis.annotations.Select;
|
||||
|
||||
@Mapper
|
||||
public interface ReturnInventoryOperationMapper extends BaseMapper<ReturnInventoryOperation> {
|
||||
@Select("SELECT o.* FROM ll_return_inventory_operation o JOIN ll_return_inventory_carton c ON c.id=o.result_carton_id " +
|
||||
"WHERE c.source_receipt_id=#{receiptId} AND c.customer_id=#{customerId} AND o.operation_type IN ('COUNT_CONTENTS','SIGN_CONTENTS') " +
|
||||
"ORDER BY CASE WHEN o.operation_type='COUNT_CONTENTS' THEN 0 ELSE 1 END, o.created_at DESC, o.id DESC LIMIT 1")
|
||||
ReturnInventoryOperation receiptContents(@Param("customerId") String customerId, @Param("receiptId") long receiptId);
|
||||
|
||||
@Select("SELECT * FROM ll_return_inventory_operation WHERE operator_id=#{operatorId} AND idempotency_key=#{key} LIMIT 1")
|
||||
ReturnInventoryOperation findKey(@Param("operatorId") String operatorId, @Param("key") String key);
|
||||
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@ import java.util.List;
|
||||
public interface ReturnInventoryQueryMapper {
|
||||
@Select("<script>SELECT COUNT(*) FROM ll_return_inventory_carton c JOIN ll_return_inventory_carton_item i ON i.carton_id=c.id " +
|
||||
"WHERE c.customer_id=#{customerId} " +
|
||||
"<if test='status != null and status != \"\"'>AND c.status=#{status} </if>" +
|
||||
"<if test='status != null and status != \"\"'>AND (CASE WHEN c.count_status='PENDING' AND c.status='AVAILABLE' THEN 'PENDING_COUNT' ELSE c.status END)=#{status} </if>" +
|
||||
"<if test='returnNo != null and returnNo != \"\"'>AND (c.source_return_no LIKE CONCAT('%',#{returnNo},'%') OR EXISTS (SELECT 1 FROM ll_return_receipt r JOIN ll_return_order o ON o.id=r.return_order_id WHERE r.id=c.source_receipt_id AND o.return_no LIKE CONCAT('%',#{returnNo},'%'))) </if>" +
|
||||
"<if test='warehouseId != null and warehouseId != \"\"'>AND c.overseas_warehouse_id=#{warehouseId} </if>" +
|
||||
"<if test='skuCode != null and skuCode != \"\"'>AND i.sku_code LIKE CONCAT('%',#{skuCode},'%') </if>" +
|
||||
@@ -20,10 +20,10 @@ public interface ReturnInventoryQueryMapper {
|
||||
@Param("skuCode") String skuCode, @Param("cartonMark") String cartonMark,
|
||||
@Param("status") String status, @Param("returnNo") String returnNo);
|
||||
|
||||
@Select("<script>SELECT c.id carton_id,c.overseas_warehouse_id,c.carton_mark,c.source_batch_no,c.status,c.sku_types,c.total_quantity,c.created_at, " +
|
||||
@Select("<script>SELECT c.id carton_id,c.overseas_warehouse_id,c.carton_mark,c.source_batch_no,(CASE WHEN c.count_status='PENDING' AND c.status='AVAILABLE' THEN 'PENDING_COUNT' ELSE c.status END) AS status,c.sku_types,c.total_quantity,c.created_at, " +
|
||||
"i.sku_code,i.product_name_zh,i.quantity FROM ll_return_inventory_carton c JOIN ll_return_inventory_carton_item i ON i.carton_id=c.id " +
|
||||
"WHERE c.customer_id=#{customerId} " +
|
||||
"<if test='status != null and status != \"\"'>AND c.status=#{status} </if>" +
|
||||
"<if test='status != null and status != \"\"'>AND (CASE WHEN c.count_status='PENDING' AND c.status='AVAILABLE' THEN 'PENDING_COUNT' ELSE c.status END)=#{status} </if>" +
|
||||
"<if test='returnNo != null and returnNo != \"\"'>AND (c.source_return_no LIKE CONCAT('%',#{returnNo},'%') OR EXISTS (SELECT 1 FROM ll_return_receipt r JOIN ll_return_order o ON o.id=r.return_order_id WHERE r.id=c.source_receipt_id AND o.return_no LIKE CONCAT('%',#{returnNo},'%'))) </if>" +
|
||||
"<if test='warehouseId != null and warehouseId != \"\"'>AND c.overseas_warehouse_id=#{warehouseId} </if>" +
|
||||
"<if test='skuCode != null and skuCode != \"\"'>AND i.sku_code LIKE CONCAT('%',#{skuCode},'%') </if>" +
|
||||
|
||||
+17
@@ -10,6 +10,23 @@ import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ReturnReceiptMapper extends BaseMapper<ReturnReceipt> {
|
||||
@Select("SELECT r.* FROM ll_return_receipt r WHERE r.unit_type='PALLET' AND EXISTS(" +
|
||||
"SELECT 1 FROM ll_return_inventory_carton c WHERE c.source_receipt_id=r.id " +
|
||||
"AND c.overseas_warehouse_id=#{warehouseId} AND c.count_status='PENDING' AND c.status='AVAILABLE' " +
|
||||
"AND (c.carton_mark=#{code} OR c.pallet_code=#{code})) ORDER BY r.signed_at,r.id")
|
||||
List<ReturnReceipt> scanPendingContents(@Param("warehouseId") String warehouseId, @Param("code") String code);
|
||||
|
||||
@Select("SELECT * FROM ll_return_receipt WHERE receipt_no=#{receiptNo} AND unit_type='PALLET' FOR UPDATE")
|
||||
ReturnReceipt lockCountReceipt(@Param("receiptNo") String receiptNo);
|
||||
|
||||
String COUNT_TASK_FILTER = " FROM ll_return_receipt r WHERE r.unit_type='PALLET' AND EXISTS(SELECT 1 FROM ll_return_inventory_carton c " +
|
||||
"WHERE c.source_receipt_id=r.id AND c.overseas_warehouse_id=#{warehouseId} AND c.count_status='PENDING' AND c.status='AVAILABLE' " +
|
||||
"<if test='keyword != null and keyword != \"\"'>AND (c.carton_mark LIKE CONCAT('%',#{keyword},'%') OR c.pallet_code LIKE CONCAT('%',#{keyword},'%') OR c.source_return_no LIKE CONCAT('%',#{keyword},'%'))</if>) ";
|
||||
@Select("<script>SELECT COUNT(*)" + COUNT_TASK_FILTER + "</script>")
|
||||
long countPendingContents(@Param("warehouseId") String warehouseId, @Param("keyword") String keyword);
|
||||
@Select("<script>SELECT r.*" + COUNT_TASK_FILTER + "ORDER BY r.signed_at,r.id LIMIT #{offset},#{size}</script>")
|
||||
List<ReturnReceipt> pendingContents(@Param("warehouseId") String warehouseId, @Param("keyword") String keyword,
|
||||
@Param("offset") long offset, @Param("size") long size);
|
||||
@Select("SELECT * FROM ll_return_receipt WHERE operator_id=#{operatorId} AND idempotency_key=#{key} LIMIT 1")
|
||||
ReturnReceipt findKey(@Param("operatorId") String operatorId, @Param("key") String key);
|
||||
@Select("SELECT * FROM ll_return_receipt WHERE operator_id=#{operatorId} AND idempotency_key=#{key} LIMIT 1 FOR UPDATE")
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.baiyue.lastleg.returnreceipt.service;
|
||||
|
||||
import com.baiyue.lastleg.returnreceipt.entity.ReturnInventoryCarton;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
public final class ReturnCartonCountGuard {
|
||||
private ReturnCartonCountGuard() { }
|
||||
public static void requireConfirmed(ReturnInventoryCarton carton) {
|
||||
if (!"CONFIRMED".equals(carton.getCountStatus())) {
|
||||
throw new ResponseStatusException(HttpStatus.CONFLICT, "退货箱尚未完成箱内清点,请先到 PDA 退货箱内清点作业:" + carton.getCartonMark());
|
||||
}
|
||||
}
|
||||
}
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
package com.baiyue.lastleg.returnreceipt.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import com.baiyue.common.core.api.PageResult;
|
||||
import com.baiyue.lastleg.pda.security.PdaWarehouseIdentity;
|
||||
import com.baiyue.lastleg.returnreceipt.dto.ReturnCartonCountDtos.*;
|
||||
import com.baiyue.lastleg.returnreceipt.entity.*;
|
||||
import com.baiyue.lastleg.returnreceipt.mapper.*;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
/** Box quantities are released together after reconciliation to the immutable pallet receipt totals. */
|
||||
@Service
|
||||
public class ReturnCartonCountService {
|
||||
@Resource private ReturnReceiptMapper receipts;
|
||||
@Resource private ReturnReceiptItemMapper receiptItems;
|
||||
@Resource private ReturnInventoryCartonMapper cartons;
|
||||
@Resource private ReturnInventoryCartonItemMapper items;
|
||||
@Resource private ReturnInventoryBalanceMapper balances;
|
||||
@Resource private ReturnInventoryOperationMapper operations;
|
||||
@Resource private ReturnCartonCountSessionMapper sessions;
|
||||
@Resource private ObjectMapper objectMapper;
|
||||
|
||||
@Transactional(readOnly=true)
|
||||
public PageResult<Task> page(PdaWarehouseIdentity identity, Query query) {
|
||||
String warehouse = Long.toString(identity.overseasWarehouseId());
|
||||
long total = receipts.countPendingContents(warehouse, query.keyword());
|
||||
List<Task> tasks = receipts.pendingContents(warehouse, query.keyword(), (query.page()-1)*query.size(), query.size()).stream()
|
||||
.map(receipt -> task(receipt, cartons.listReceipt(receipt.getId()))).toList();
|
||||
return new PageResult<>(tasks, query.page(), query.size(), total, (total+query.size()-1)/query.size());
|
||||
}
|
||||
|
||||
@Transactional(readOnly=true)
|
||||
public List<Task> scanTasks(PdaWarehouseIdentity identity, String code) {
|
||||
if (code == null || code.isBlank() || code.trim().length() > 128) throw error(HttpStatus.BAD_REQUEST, "请扫描有效的箱号或托盘号");
|
||||
return receipts.scanPendingContents(Long.toString(identity.overseasWarehouseId()), code.trim()).stream()
|
||||
.map(receipt -> task(receipt, cartons.listReceipt(receipt.getId()))).toList();
|
||||
}
|
||||
|
||||
@Transactional(readOnly=true)
|
||||
public Task scan(PdaWarehouseIdentity identity, String mark) {
|
||||
ReturnInventoryCarton carton = cartons.findMark(mark.trim());
|
||||
if (carton == null || !Long.toString(identity.overseasWarehouseId()).equals(carton.getOverseasWarehouseId())) throw error(HttpStatus.NOT_FOUND, "当前仓没有该退货箱");
|
||||
if (!"PALLET".equals(carton.getSourceUnitType()) || carton.getSourceReceiptId() == null) throw error(HttpStatus.CONFLICT, "该箱不属于托盘签收清点任务");
|
||||
ReturnReceipt receipt = receipts.selectById(carton.getSourceReceiptId());
|
||||
if (receipt == null) throw error(HttpStatus.NOT_FOUND, "签收批次不存在");
|
||||
return task(receipt, cartons.listReceipt(receipt.getId()));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Task begin(PdaWarehouseIdentity identity, String receiptNo) {
|
||||
var session = lockSession(identity);
|
||||
ReturnReceipt receipt = receipts.lockCountReceipt(receiptNo);
|
||||
if (receipt == null) throw error(HttpStatus.NOT_FOUND, "清点任务不存在");
|
||||
if (session.receiptId() != null && !session.receiptId().equals(receipt.getId()))
|
||||
throw error(HttpStatus.CONFLICT, "您有未清点完成的任务,请先继续当前清点;一次只能清点一单");
|
||||
List<ReturnInventoryCarton> boxes = cartons.lockReceipt(receipt.getId());
|
||||
requireWarehouse(identity, receipt, boxes);
|
||||
if (boxes.stream().anyMatch(box -> !"PENDING".equals(box.getCountStatus()) || !"AVAILABLE".equals(box.getStatus())))
|
||||
throw error(HttpStatus.CONFLICT, "该任务无需清点或箱子已被其他作业占用,请刷新任务");
|
||||
var owner = sessions.findReceipt(receipt.getId());
|
||||
if (owner != null && !identity.operatorKey().equals(owner.operatorId()))
|
||||
throw error(HttpStatus.CONFLICT, "该任务正在由其他人员清点");
|
||||
if (session.receiptId() == null && sessions.claim(identity.operatorKey(), Long.toString(identity.overseasWarehouseId()), receipt.getId()) != 1)
|
||||
throw error(HttpStatus.CONFLICT, "当前清点任务已变化,请重试");
|
||||
return task(receipt, boxes);
|
||||
}
|
||||
|
||||
@Transactional(readOnly=true)
|
||||
public Task current(PdaWarehouseIdentity identity) {
|
||||
var session = sessions.findOperator(identity.operatorKey());
|
||||
if (session == null || session.receiptId() == null) return null;
|
||||
if (!Long.toString(identity.overseasWarehouseId()).equals(session.warehouseId()))
|
||||
throw error(HttpStatus.CONFLICT, "您在其他仓有未完成的清点任务,请先完成原任务");
|
||||
ReturnReceipt receipt = receipts.selectById(session.receiptId());
|
||||
if (receipt == null) throw error(HttpStatus.CONFLICT, "当前清点任务已异常,请核实原任务");
|
||||
var boxes = cartons.listReceipt(receipt.getId());
|
||||
requireWarehouse(identity, receipt, boxes);
|
||||
return task(receipt, boxes);
|
||||
}
|
||||
|
||||
private ReturnCartonCountSessionMapper.Session lockSession(PdaWarehouseIdentity identity) {
|
||||
sessions.ensure(identity.operatorKey(), Long.toString(identity.overseasWarehouseId()));
|
||||
var session = sessions.lockOperator(identity.operatorKey());
|
||||
if (session == null) throw error(HttpStatus.CONFLICT, "清点会话未建立,请重试");
|
||||
return session;
|
||||
}
|
||||
|
||||
private void requireWarehouse(PdaWarehouseIdentity identity, ReturnReceipt receipt, List<ReturnInventoryCarton> boxes) {
|
||||
if (boxes.isEmpty() || boxes.stream().anyMatch(box -> !Long.toString(identity.overseasWarehouseId()).equals(box.getOverseasWarehouseId()) || !receipt.getCustomerId().equals(box.getCustomerId())))
|
||||
throw error(HttpStatus.NOT_FOUND, "当前仓没有该托盘签收任务");
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Task confirm(PdaWarehouseIdentity identity, String receiptNo, Confirm request) {
|
||||
var session = lockSession(identity);
|
||||
ReturnReceipt receipt = receipts.lockCountReceipt(receiptNo);
|
||||
if (receipt == null) throw error(HttpStatus.NOT_FOUND, "托盘签收任务不存在");
|
||||
List<ReturnInventoryCarton> boxes = cartons.lockReceipt(receipt.getId());
|
||||
requireWarehouse(identity, receipt, boxes);
|
||||
String hash = payload(List.of(receiptNo, request.boxes()));
|
||||
ReturnInventoryOperation replay = operations.lockKey(identity.operatorKey(), request.idempotencyKey());
|
||||
if (replay != null) {
|
||||
if (!"COUNT_CONTENTS".equals(replay.getOperationType()) || !hash.equals(replay.getRequestHash())) throw error(HttpStatus.CONFLICT, "该幂等键已用于不同的清点内容");
|
||||
try { return objectMapper.readValue(replay.getResultJson(), Task.class); }
|
||||
catch (Exception error) { throw new IllegalStateException("清点结果读取失败", error); }
|
||||
}
|
||||
if (!Objects.equals(session.receiptId(), receipt.getId()) || !Long.toString(identity.overseasWarehouseId()).equals(session.warehouseId()))
|
||||
throw error(HttpStatus.CONFLICT, "请先开始本单清点;未完成当前单前不能清点其他单");
|
||||
if (boxes.stream().anyMatch(box -> !"PENDING".equals(box.getCountStatus()) || !"AVAILABLE".equals(box.getStatus()) || box.getRelabelApplicationId() != null)) throw error(HttpStatus.CONFLICT, "箱子已清点或被其他作业占用,请刷新任务");
|
||||
Map<String, ReturnInventoryCarton> byMark = new LinkedHashMap<>(); boxes.forEach(box -> byMark.put(box.getCartonMark(), box));
|
||||
if (request.boxes().size() != boxes.size()) throw error(HttpStatus.BAD_REQUEST, "必须清点本次托盘签收的全部箱子");
|
||||
Map<String, Long> expected = new TreeMap<>(), actual = new TreeMap<>(), oldCounts = new TreeMap<>(), newCounts = new TreeMap<>();
|
||||
Map<String, String> names = new HashMap<>();
|
||||
for (ReturnReceiptItem line : receiptItems.listReceipt(receipt.getId())) expected.merge(line.getSkuCode(), line.getReceivedQuantity(), Math::addExact);
|
||||
for (ReturnInventoryCarton box : boxes) for (ReturnInventoryCartonItem line : items.lockCarton(box.getId())) {
|
||||
if (line.getPackingReservedQuantity() != null && line.getPackingReservedQuantity() > 0) throw error(HttpStatus.CONFLICT, "来源箱已被合箱计划占用");
|
||||
oldCounts.merge(line.getSkuCode(), 1L, Long::sum); names.put(line.getSkuCode(), line.getProductNameZh());
|
||||
}
|
||||
Set<String> marks = new HashSet<>();
|
||||
for (CountBox box : request.boxes()) {
|
||||
if (!byMark.containsKey(box.cartonMark()) || !marks.add(box.cartonMark())) throw error(HttpStatus.BAD_REQUEST, "箱号不属于本次签收或重复填写");
|
||||
Set<String> skus = new HashSet<>();
|
||||
for (CountItem line : box.items()) {
|
||||
if (!expected.containsKey(line.skuCode()) || !skus.add(line.skuCode()) || line.quantity() <= 0) throw error(HttpStatus.BAD_REQUEST, "SKU 不属于本次签收、数量无效或同箱重复");
|
||||
actual.merge(line.skuCode(), line.quantity(), Math::addExact); newCounts.merge(line.skuCode(), 1L, Long::sum);
|
||||
}
|
||||
}
|
||||
if (!expected.equals(actual)) throw error(HttpStatus.CONFLICT, "各箱 SKU 数量汇总必须与本托签收数量一致;签收=" + expected + ",清点=" + actual);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
for (CountBox input : request.boxes()) {
|
||||
ReturnInventoryCarton box = byMark.get(input.cartonMark()); items.deleteCarton(box.getId());
|
||||
for (CountItem line : input.items()) {
|
||||
ReturnInventoryCartonItem item = new ReturnInventoryCartonItem(); item.setId(IdWorker.getId()); item.setCartonId(box.getId());
|
||||
item.setCustomerId(box.getCustomerId()); item.setSkuCode(line.skuCode()); item.setProductNameZh(names.get(line.skuCode())); item.setQuantity(line.quantity());
|
||||
item.setPackingReservedQuantity(0L); items.insert(item);
|
||||
}
|
||||
box.setSkuTypes(input.items().size()); box.setTotalQuantity(input.items().stream().mapToLong(CountItem::quantity).sum());
|
||||
box.setCountStatus("CONFIRMED"); box.setCountedAt(now); box.setCountedBy(identity.operatorKey()); box.setUpdatedAt(now); cartons.updateById(box);
|
||||
}
|
||||
for (String sku : expected.keySet()) {
|
||||
long delta = newCounts.getOrDefault(sku, 0L) - oldCounts.getOrDefault(sku, 0L);
|
||||
if (delta != 0 && balances.adjustCartonQuantity(receipt.getCustomerId(), boxes.get(0).getOverseasWarehouseId(), sku, delta) != 1) throw error(HttpStatus.CONFLICT, "SKU 箱数余额已变化,请重新核对");
|
||||
}
|
||||
if (sessions.release(identity.operatorKey(), receipt.getId()) != 1) throw error(HttpStatus.CONFLICT, "清点会话已变化,请重新核对");
|
||||
Task result = task(receipt, boxes);
|
||||
ReturnInventoryOperation operation = new ReturnInventoryOperation(); operation.setId(IdWorker.getId()); operation.setOperatorId(identity.operatorKey());
|
||||
operation.setIdempotencyKey(request.idempotencyKey()); operation.setRequestHash(hash); operation.setOperationType("COUNT_CONTENTS");
|
||||
operation.setResultCartonId(boxes.get(0).getId()); operation.setResultJson(json(result)); operation.setCreatedAt(now); operation.setUpdatedAt(now); operations.insert(operation);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Task task(ReturnReceipt receipt, List<ReturnInventoryCarton> boxes) {
|
||||
List<Sku> expected = receiptItems.listReceipt(receipt.getId()).stream().map(line -> new Sku(line.getSkuCode(), null, line.getReceivedQuantity())).toList();
|
||||
List<Box> contents = boxes.stream().map(box -> new Box(box.getCartonMark(), items.listCarton(box.getId()).stream()
|
||||
.map(line -> new Sku(line.getSkuCode(), line.getProductNameZh(), "PENDING".equals(box.getCountStatus()) ? 0 : line.getQuantity())).toList())).toList();
|
||||
return new Task(receipt.getReceiptNo(), boxes.isEmpty() ? null : boxes.get(0).getSourceReturnNo(), receipt.getPalletCode(),
|
||||
boxes.stream().allMatch(box -> "CONFIRMED".equals(box.getCountStatus())) ? "CONFIRMED" : sessions.findReceipt(receipt.getId()) != null ? "COUNTING" : "PENDING", expected, contents);
|
||||
}
|
||||
private String json(Object value) { try { return objectMapper.writeValueAsString(value); } catch(Exception error) { throw new IllegalStateException(error); } }
|
||||
private String payload(Object value) {
|
||||
try { return HexFormat.of().formatHex(java.security.MessageDigest.getInstance("SHA-256").digest(json(value).getBytes(java.nio.charset.StandardCharsets.UTF_8))); }
|
||||
catch(Exception error) { throw new IllegalStateException(error); }
|
||||
}
|
||||
private ResponseStatusException error(HttpStatus status, String message) { return new ResponseStatusException(status, message); }
|
||||
}
|
||||
+43
-14
@@ -142,6 +142,7 @@ public class ReturnInventoryService {
|
||||
|| !first.getOverseasWarehouseId().equals(carton.getOverseasWarehouseId())
|
||||
|| !AVAILABLE.equals(carton.getStatus())
|
||||
|| !sourceMatches(carton, returnNos)) throw conflict("来源箱不属于所选退货单或已被占用");
|
||||
ReturnCartonCountGuard.requireConfirmed(carton);
|
||||
if ("BOX".equals(mode) && "PARCEL".equals(carton.getSourceUnitType())) throw badRequest("包裹散 SKU 不能作为整箱选择");
|
||||
for (ReturnInventoryCartonItem item : cartonItems.lockCarton(carton.getId())) {
|
||||
long free = Math.max(0, value(item.getQuantity()) - value(item.getPackingReservedQuantity()));
|
||||
@@ -385,6 +386,7 @@ public class ReturnInventoryService {
|
||||
if (carton == null || !customerId.equals(carton.getCustomerId())
|
||||
|| !warehouseId.equals(carton.getOverseasWarehouseId()) || !matchesReturnSource(carton, returnNo)
|
||||
|| !"OUTBOUND_PENDING".equals(carton.getStatus())) throw conflict("退货箱状态或客户、仓库、来源不匹配");
|
||||
ReturnCartonCountGuard.requireConfirmed(carton);
|
||||
List<ReturnInventoryCartonItem> contents = cartonItems.lockCarton(carton.getId());
|
||||
long total = 0;
|
||||
var skus = new java.util.HashSet<String>();
|
||||
@@ -449,6 +451,7 @@ public class ReturnInventoryService {
|
||||
carton.setSourceReturnNo(order.getReturnNo()); carton.setCartonMark(cartonMark);
|
||||
carton.setPalletCode(text(receipt.getPalletCode()));
|
||||
carton.setSourceUnitType(receipt.getUnitType());
|
||||
carton.setCountStatus("PALLET".equals(receipt.getUnitType()) ? "PENDING" : "CONFIRMED");
|
||||
carton.setSourceUnitCode(normalizedUnitCode);
|
||||
// Pallet measurements describe the carrier, never each child carton.
|
||||
if ("BOX".equals(receipt.getUnitType())) {
|
||||
@@ -464,6 +467,15 @@ public class ReturnInventoryService {
|
||||
balances.adjust(IdWorker.getId(), order.getCustomerId(), order.getOverseasWarehouseId(), line.skuCode(),
|
||||
text(line.productNameZh()), line.quantity(), line.quantity(), 1);
|
||||
}
|
||||
if ("BOX".equals(receipt.getUnitType())) {
|
||||
var contents = lines.stream().map(line -> new com.baiyue.lastleg.returnreceipt.dto.ReturnCartonCountDtos.Sku(line.skuCode(), line.productNameZh(), line.quantity())).toList();
|
||||
var snapshot = new com.baiyue.lastleg.returnreceipt.dto.ReturnCartonCountDtos.Task(receipt.getReceiptNo(), order.getReturnNo(), receipt.getPalletCode(), "CONFIRMED", contents,
|
||||
List.of(new com.baiyue.lastleg.returnreceipt.dto.ReturnCartonCountDtos.Box(cartonMark, contents)));
|
||||
ReturnInventoryOperation operation = new ReturnInventoryOperation(); operation.setId(IdWorker.getId());
|
||||
operation.setOperatorId(receipt.getOperatorId()); operation.setIdempotencyKey("sign-contents-" + receipt.getId() + "-" + cartonId);
|
||||
operation.setRequestHash(hash(snapshot)); operation.setOperationType("SIGN_CONTENTS"); operation.setResultCartonId(cartonId);
|
||||
operation.setResultJson(json(snapshot)); operation.setCreatedAt(now); operation.setUpdatedAt(now); operations.insert(operation);
|
||||
}
|
||||
return carton;
|
||||
}
|
||||
|
||||
@@ -512,7 +524,7 @@ public class ReturnInventoryService {
|
||||
List<ReturnInventoryResponses.CartonSummary> records = total == 0 ? List.of() :
|
||||
cartons.merchantPage(customerId, request, (request.page() - 1) * request.size()).stream()
|
||||
.map(carton -> new ReturnInventoryResponses.CartonSummary(carton.getCartonMark(), carton.getOverseasWarehouseId(),
|
||||
carton.getSourceBatchNo(), carton.getSourceReturnNo(), carton.getStatus(),
|
||||
carton.getSourceBatchNo(), carton.getSourceReturnNo(), displayStatus(carton),
|
||||
carton.getSkuTypes() == null ? 0 : carton.getSkuTypes(), value(carton.getTotalQuantity()),
|
||||
carton.getLength(), carton.getWidth(), carton.getHeight(), carton.getWeight(), carton.getCreatedAt())).toList();
|
||||
return new PageResult<>(records, request.page(), request.size(), total, (total + request.size() - 1) / request.size());
|
||||
@@ -557,7 +569,7 @@ public class ReturnInventoryService {
|
||||
List<ReturnInventoryResponses.PlatformCarton> records = total == 0 ? List.of() : cartons.platformPage(customerId, warehouseId, cartonMark,
|
||||
returnNo, status, sku, (request.page() - 1) * request.size(), request.size()).stream().map(carton -> new ReturnInventoryResponses.PlatformCarton(
|
||||
carton.getCustomerId(), carton.getOverseasWarehouseId(), carton.getCartonMark(), carton.getSourceReturnNo(), carton.getSourceUnitType(), carton.getSourceBatchNo(),
|
||||
carton.getStatus(), carton.getSkuTypes() == null ? 0 : carton.getSkuTypes(), value(carton.getTotalQuantity()), carton.getFbaCartonMark(), carton.getDestinationWarehouseCode(), carton.getDestinationWarehouseName(), carton.getRelabelNo(), carton.getRelabelStatus(), carton.getLength(),
|
||||
displayStatus(carton), carton.getSkuTypes() == null ? 0 : carton.getSkuTypes(), value(carton.getTotalQuantity()), carton.getFbaCartonMark(), carton.getDestinationWarehouseCode(), carton.getDestinationWarehouseName(), carton.getRelabelNo(), carton.getRelabelStatus(), carton.getLength(),
|
||||
carton.getWidth(), carton.getHeight(), carton.getWeight(), carton.getCreatedAt(), carton.getLocationCode(), carton.getPalletCode(), carton.getPutawayAt())).toList();
|
||||
return new PageResult<>(records, request.page(), request.size(), total, (total + request.size() - 1) / request.size());
|
||||
}
|
||||
@@ -616,6 +628,21 @@ public class ReturnInventoryService {
|
||||
}
|
||||
|
||||
/** Print only persisted physical cartons; a parcel's SKU rows are not cartons. */
|
||||
@Transactional(readOnly = true)
|
||||
public List<ReturnOrderResponses.ReceiptCarton> receiptContents(ReturnReceipt receipt) {
|
||||
if ("PARCEL".equals(receipt.getUnitType())) return List.of();
|
||||
var snapshot = operations.receiptContents(receipt.getCustomerId(), receipt.getId());
|
||||
if (snapshot != null) {
|
||||
var task = read(snapshot.getResultJson(), new TypeReference<com.baiyue.lastleg.returnreceipt.dto.ReturnCartonCountDtos.Task>() { });
|
||||
if (!receipt.getReceiptNo().equals(task.receiptNo())) throw new IllegalStateException("箱内清点快照与签收单不一致");
|
||||
return task.boxes().stream().map(box -> new ReturnOrderResponses.ReceiptCarton(receipt.getReceiptNo(), receipt.getPalletCode(), box.cartonMark(), "CONFIRMED", snapshot.getCreatedAt(),
|
||||
box.items().stream().map(line -> new ReturnOrderResponses.ReceiptItem(line.skuCode(), line.productNameZh(), line.quantity(), 1)).toList())).toList();
|
||||
}
|
||||
// Never present provisional averages or subsequently changed inventory as receipt facts.
|
||||
return cartons.listReceipt(receipt.getId()).stream().map(box -> new ReturnOrderResponses.ReceiptCarton(receipt.getReceiptNo(), receipt.getPalletCode(), box.getCartonMark(),
|
||||
"PENDING".equals(box.getCountStatus()) ? "PENDING" : "UNRECORDED", box.getCountedAt(), List.of())).toList();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<ReturnOrderResponses.CartonLabel> receiptLabels(ReturnOrder order, ReturnReceipt receipt,
|
||||
List<ReturnOrderLine> lines) {
|
||||
@@ -780,6 +807,7 @@ public class ReturnInventoryService {
|
||||
ReturnInventoryCarton carton = cartons.findMark(required(mark, "箱唛不能为空"));
|
||||
if (carton == null || !String.valueOf(identity.overseasWarehouseId()).equals(carton.getOverseasWarehouseId())) throw notFound("当前仓没有该退货箱");
|
||||
if (!AVAILABLE.equals(carton.getStatus()) || "PARCEL".equals(carton.getSourceUnitType())) throw conflict("该箱不是可用实物箱");
|
||||
ReturnCartonCountGuard.requireConfirmed(carton);
|
||||
return cartonDetail(carton);
|
||||
}
|
||||
|
||||
@@ -801,6 +829,7 @@ public class ReturnInventoryService {
|
||||
if (carton == null || !AVAILABLE.equals(carton.getStatus()) || "PARCEL".equals(carton.getSourceUnitType())
|
||||
|| !String.valueOf(identity.overseasWarehouseId()).equals(carton.getOverseasWarehouseId())) throw conflict("来源箱不存在或不可合并");
|
||||
if (customer != null && !customer.equals(carton.getCustomerId())) throw conflict("不同客户的箱不能合并");
|
||||
ReturnCartonCountGuard.requireConfirmed(carton);
|
||||
customer = carton.getCustomerId(); returnNos.addAll(java.util.Arrays.asList(required(carton.getSourceReturnNo(), "来源退货单缺失").split(",")));
|
||||
Map<String, Long> items = new LinkedHashMap<>();
|
||||
for (var item : cartonItems.lockCarton(carton.getId())) {
|
||||
@@ -865,6 +894,7 @@ public class ReturnInventoryService {
|
||||
|| limits.containsKey(targetMark))) throw conflict("目标箱不存在、被占用、跨客户或与来源箱相同");
|
||||
if (existingTarget != null && cartonItems.lockCarton(existingTarget.getId()).stream().anyMatch(i -> value(i.getPackingReservedQuantity()) > 0))
|
||||
throw conflict("目标箱已有合箱预占,不能并入");
|
||||
if (existingTarget != null) ReturnCartonCountGuard.requireConfirmed(existingTarget);
|
||||
if (existingTarget != null) for (var item : cartonItems.lockCarton(existingTarget.getId())) {
|
||||
long tracked = packingTrace.ownership(customerId, targetMark, item.getSkuCode()).stream()
|
||||
.mapToLong(com.baiyue.lastleg.returnreceipt.mapper.ReturnPackingTraceMapper.Ownership::quantity).sum();
|
||||
@@ -894,6 +924,7 @@ public class ReturnInventoryService {
|
||||
&& String.valueOf(identity.overseasWarehouseId()).equals(locked.getOverseasWarehouseId())
|
||||
&& sourceMatches(locked, returnNos) && (limits != null || "PARCEL".equals(locked.getSourceUnitType()))) {
|
||||
sources.add(locked); sourceOwners.put(locked.getId(), candidate.getValue());
|
||||
ReturnCartonCountGuard.requireConfirmed(locked);
|
||||
}
|
||||
}
|
||||
if (sources.isEmpty()) throw conflict("所选退货单没有可合箱的库存箱");
|
||||
@@ -1031,7 +1062,7 @@ public class ReturnInventoryService {
|
||||
.map(item -> new ReturnInventoryResponses.CartonItem(item.getSkuCode(), item.getProductNameZh(), item.getQuantity(),
|
||||
Math.max(0, value(item.getQuantity()) - value(item.getPackingReservedQuantity())))).toList();
|
||||
return new ReturnInventoryResponses.Label(carton.getCartonMark(), carton.getOverseasWarehouseId(), carton.getCustomerId(),
|
||||
carton.getSourceBatchNo(), carton.getSourceReturnNo(), value(carton.getTotalQuantity()), carton.getLength(), carton.getWidth(), carton.getHeight(), carton.getWeight(), items, LocalDateTime.now(), printableHtml(carton, items));
|
||||
carton.getSourceBatchNo(), carton.getSourceReturnNo(), value(carton.getTotalQuantity()), carton.getLength(), carton.getWidth(), carton.getHeight(), carton.getWeight(), items, LocalDateTime.now(), printableHtml(carton));
|
||||
}
|
||||
|
||||
private ReturnInventoryResponses.InventoryItem item(ReturnInventoryRow row) {
|
||||
@@ -1045,26 +1076,24 @@ public class ReturnInventoryService {
|
||||
value(balance.getTotalQuantity()), value(balance.getAvailableQuantity()), value(balance.getCartonQuantity()), balance.getUpdatedAt());
|
||||
}
|
||||
|
||||
private static String displayStatus(ReturnInventoryCarton carton) {
|
||||
return "PENDING".equals(carton.getCountStatus()) && AVAILABLE.equals(carton.getStatus()) ? "PENDING_COUNT" : carton.getStatus();
|
||||
}
|
||||
|
||||
private ReturnInventoryResponses.CartonDetail cartonDetail(ReturnInventoryCarton carton) {
|
||||
List<ReturnInventoryResponses.CartonItem> items = cartonItems.listCarton(carton.getId()).stream()
|
||||
.map(item -> new ReturnInventoryResponses.CartonItem(item.getSkuCode(), item.getProductNameZh(), item.getQuantity(),
|
||||
"AVAILABLE".equals(carton.getStatus())
|
||||
"AVAILABLE".equals(carton.getStatus()) && "CONFIRMED".equals(carton.getCountStatus())
|
||||
? Math.max(0, value(item.getQuantity()) - value(item.getPackingReservedQuantity())) : 0)).toList();
|
||||
return new ReturnInventoryResponses.CartonDetail(carton.getCartonMark(), carton.getCustomerId(), carton.getOverseasWarehouseId(), carton.getSourceBatchNo(), carton.getSourceReturnNo(),
|
||||
carton.getStatus(), carton.getFbaCartonMark(), carton.getDestinationWarehouseCode(), carton.getDestinationWarehouseName(), carton.getRelabelNo(), carton.getRelabelStatus(), carton.getSkuTypes() == null ? 0 : carton.getSkuTypes(), value(carton.getTotalQuantity()), carton.getLength(), carton.getWidth(), carton.getHeight(), carton.getWeight(), carton.getCreatedAt(), items, carton.getLocationCode(), carton.getPalletCode(), carton.getPutawayAt(), packingTrace.listCartonHistory(carton.getCustomerId(), carton.getCartonMark()));
|
||||
displayStatus(carton), carton.getFbaCartonMark(), carton.getDestinationWarehouseCode(), carton.getDestinationWarehouseName(), carton.getRelabelNo(), carton.getRelabelStatus(), carton.getSkuTypes() == null ? 0 : carton.getSkuTypes(), value(carton.getTotalQuantity()), carton.getLength(), carton.getWidth(), carton.getHeight(), carton.getWeight(), carton.getCreatedAt(), items, carton.getLocationCode(), carton.getPalletCode(), carton.getPutawayAt(), packingTrace.listCartonHistory(carton.getCustomerId(), carton.getCartonMark()));
|
||||
}
|
||||
|
||||
private static String printableHtml(ReturnInventoryCarton carton, List<ReturnInventoryResponses.CartonItem> items) {
|
||||
StringBuilder rows = new StringBuilder();
|
||||
for (ReturnInventoryResponses.CartonItem item : items) {
|
||||
rows.append("<div class=\"line\"><span class=\"sku\">")
|
||||
.append(escape(item.skuCode()))
|
||||
.append("</span></div>");
|
||||
}
|
||||
private static String printableHtml(ReturnInventoryCarton carton) {
|
||||
return "<!doctype html><html><head><meta charset=\"utf-8\"><title>退货箱唛</title><style>"
|
||||
+ "@page{size:100mm 100mm;margin:8mm}*{box-sizing:border-box}body{font-family:Arial,'Microsoft YaHei',sans-serif;color:#111;margin:0}.label{width:84mm;height:84mm;padding:4mm;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;page-break-after:always}.label:last-child{page-break-after:auto}.mark{font:700 14px Arial,sans-serif;line-height:1.25;overflow-wrap:anywhere;margin:0 0 5mm}.barcode{width:100%;max-width:76mm;margin:0 0 4mm}.barcode svg{display:block;width:100%;height:auto}.line{display:flex;justify-content:center;align-items:baseline;width:100%;padding:1mm 0;font-size:12px}.sku{max-width:76mm;overflow-wrap:anywhere;font-weight:700}</style></head><body><div class=\"label\"><div class=\"mark\">"
|
||||
+ "@page{size:100mm 100mm;margin:8mm}*{box-sizing:border-box}body{font-family:Arial,'Microsoft YaHei',sans-serif;color:#111;margin:0}.label{width:84mm;height:84mm;padding:4mm;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;page-break-after:always}.label:last-child{page-break-after:auto}.mark{font:700 14px Arial,sans-serif;line-height:1.25;overflow-wrap:anywhere;margin:0 0 5mm}.barcode{width:100%;max-width:76mm;margin:0}.barcode svg{display:block;width:100%;height:auto}</style></head><body><div class=\"label\"><div class=\"mark\">"
|
||||
+ escape(carton.getCartonMark()) + "</div><div class=\"barcode\">"
|
||||
+ ReturnCartonBarcode.svg(carton.getCartonMark()) + "</div>" + rows
|
||||
+ ReturnCartonBarcode.svg(carton.getCartonMark()) + "</div>"
|
||||
+ "</div></body></html>";
|
||||
}
|
||||
private static String dimension(BigDecimal value) { return value == null ? "—" : value.stripTrailingZeros().toPlainString(); }
|
||||
|
||||
+5
-3
@@ -124,7 +124,7 @@ public class ReturnReceiptService {
|
||||
private void createTailOrder(ReturnOrder returnOrder, String operatorId, LocalDateTime now) {
|
||||
LastlegOrder tail = new LastlegOrder();
|
||||
tail.setId(IdWorker.getId());
|
||||
tail.setOrderNo(numbers.next("RETURN_RELABEL", returnOrder.getOverseasWarehouseId(), tail.getId()));
|
||||
tail.setOrderNo(returnOrder.getReturnNo());
|
||||
tail.setCustomerId(returnOrder.getCustomerId()); tail.setOrderType("RETURN_RELABEL");
|
||||
tail.setSourceRegion("CUSTOMER"); tail.setSourceInboundId(returnOrder.getReturnNo());
|
||||
tail.setSourceOrderId(String.valueOf(returnOrder.getId()));
|
||||
@@ -301,7 +301,8 @@ public class ReturnReceiptService {
|
||||
List<ReturnOrderResponses.Photo> photos = photoList(order).stream()
|
||||
.map(file -> new ReturnOrderResponses.Photo(file.fileId(), file.fileName(), file.contentType(), file.sizeBytes())).toList();
|
||||
return new ReturnOrderResponses.Detail(summary(order, relabelSubOrders(List.of(order)).getOrDefault(order.getLastlegOrderId(), List.of())), order.getRemark(), items, receiptViews, palletViews, photos,
|
||||
inventory.packingHistory(order.getCustomerId(), order.getReturnNo()));
|
||||
inventory.packingHistory(order.getCustomerId(), order.getReturnNo()),
|
||||
orderReceipts.stream().flatMap(receipt -> inventory.receiptContents(receipt).stream()).toList());
|
||||
}
|
||||
|
||||
private String combineLabels(List<ReturnOrderResponses.CartonLabel> labels) {
|
||||
@@ -364,7 +365,7 @@ public class ReturnReceiptService {
|
||||
List<ReturnOrderResponses.RelabelCandidate> result = new ArrayList<>();
|
||||
for (ReturnReceipt receipt : receipts.listOrder(order.getId())) {
|
||||
for (ReturnInventoryCarton carton : cartons.listReceipt(receipt.getId())) {
|
||||
if (!ReturnInventoryService.AVAILABLE.equals(carton.getStatus()) || carton.getRelabelApplicationId() != null) continue;
|
||||
if (!ReturnInventoryService.AVAILABLE.equals(carton.getStatus()) || !"CONFIRMED".equals(carton.getCountStatus()) || carton.getRelabelApplicationId() != null) continue;
|
||||
List<ReturnOrderResponses.Item> items = cartonItems.listCarton(carton.getId()).stream()
|
||||
.map(i -> new ReturnOrderResponses.Item(i.getSkuCode(), i.getProductNameZh(), value(i.getQuantity()), value(i.getQuantity()))).toList();
|
||||
result.add(new ReturnOrderResponses.RelabelCandidate(carton.getId(), carton.getCartonMark(), carton.getSourceReturnNo(), carton.getStatus(), value(carton.getTotalQuantity()), items, carton.getRelabelStatus()));
|
||||
@@ -457,6 +458,7 @@ public class ReturnReceiptService {
|
||||
if (cartonItems.lockCarton(carton.getId()).stream().anyMatch(line -> value(line.getPackingReservedQuantity()) > 0))
|
||||
throw conflict("退货 SKU 已被合箱计划锁定,请先完成或取消合箱计划");
|
||||
long allocated = Math.min(remaining, skuQuantity);
|
||||
ReturnCartonCountGuard.requireConfirmed(carton);
|
||||
RelabelItem item = new RelabelItem(); item.setId(IdWorker.getId()); item.setApplicationId(appId); item.setCustomerId(customerId);
|
||||
item.setSkuCode(source.getSkuCode());
|
||||
item.setProductNameZh(text(source.getProductNameZh()) == null ? source.getSkuCode() : text(source.getProductNameZh()));
|
||||
|
||||
@@ -72,6 +72,7 @@ public class LastlegOrderService {
|
||||
"OVERSEAS_CUSTOMS_COMPLETED", "ACTUAL_ARRIVAL_WAITING_UNLOAD", "PICKUP_APPOINTED", "UNLOADED");
|
||||
|
||||
@Resource private LastlegOrderMapper orderMapper;
|
||||
@Resource private com.baiyue.lastleg.returnreceipt.service.ReturnReceiptService returnReceipts;
|
||||
@Resource private LastlegOrderDetailMapper detailMapper;
|
||||
@Resource private LastlegOrderStatusHistoryMapper historyMapper;
|
||||
@Resource private OverseasFeeSourceService feeSources;
|
||||
@@ -364,7 +365,9 @@ public class LastlegOrderService {
|
||||
.map(value -> new LastlegOrderDetailResponse.History(value.getId(), value.getFromStatus(),
|
||||
value.getToStatus(), value.getRemark(), value.getCreatedAt())).toList(), fees, operations.stream()
|
||||
.map(value -> new LastlegOrderDetailResponse.Operation(value.getId(), value.getOperationNode(),
|
||||
value.getOperatorName(), value.getResult(), value.getCreatedAt())).toList());
|
||||
value.getOperatorName(), value.getResult(), value.getCreatedAt())).toList(),
|
||||
"RETURN_RELABEL".equals(order.getOrderType()) && StringUtils.hasText(order.getSourceOrderNo())
|
||||
? returnReceipts.detail(order.getCustomerId(), order.getSourceOrderNo()) : null);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -653,7 +656,7 @@ public class LastlegOrderService {
|
||||
}
|
||||
|
||||
static String displayOrderNo(LastlegOrder order) {
|
||||
return "WAREHOUSE_TRANSFER".equals(order.getOrderType()) && StringUtils.hasText(order.getSourceOrderNo())
|
||||
return ("WAREHOUSE_TRANSFER".equals(order.getOrderType()) || "RETURN_RELABEL".equals(order.getOrderType())) && StringUtils.hasText(order.getSourceOrderNo())
|
||||
? order.getSourceOrderNo() : order.getOrderNo();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user