提交
This commit is contained in:
+4
-2
@@ -111,10 +111,12 @@ public final class WarehouseTransferRequests {
|
||||
@NotNull @DecimalMin("0.01") BigDecimal width,
|
||||
@NotNull @DecimalMin("0.01") BigDecimal height,
|
||||
@NotNull @DecimalMin("0.01") BigDecimal weight,
|
||||
@NotBlank @Size(max = 255) String skuCode,
|
||||
@Positive @Max(2147483647) long quantity
|
||||
@NotEmpty @Size(max = 200) List<@Valid @NotNull CountSku> items
|
||||
) {}
|
||||
|
||||
public record CountSku(@NotBlank @Size(max = 255) String skuCode,
|
||||
@Positive @Max(2147483647) long quantity) {}
|
||||
|
||||
public record Palletize(
|
||||
@NotBlank @Size(max = 255) String idempotencyKey,
|
||||
@NotEmpty @Size(max = 20) List<@NotNull Long> photoFileIds
|
||||
|
||||
+2
-1
@@ -9,7 +9,8 @@ public final class WarehouseTransferResponses {
|
||||
public record Item(Long id, String skuCode, String productNameZh, int packageCount, String shippingMark, Long quantity) {}
|
||||
public record Carton(String cartonMark, String customerCartonMark, int cartonIndex, int totalCartons, String status,
|
||||
Long transferItemId, Long transferPalletId, BigDecimal length, BigDecimal width, BigDecimal height,
|
||||
BigDecimal weight, Long quantity, String discrepancyReason, String countedBy, LocalDateTime countedAt) {}
|
||||
BigDecimal weight, Long quantity, String discrepancyReason, String countedBy, LocalDateTime countedAt, List<CartonItem> items) {}
|
||||
public record CartonItem(Long transferItemId, String skuCode, long quantity) {}
|
||||
public record Label(String transferNo, List<Carton> cartons, String printableHtml) {}
|
||||
public record DestinationResult(String customerId, String transferNo) {}
|
||||
public record PalletItem(Long transferItemId, String skuCode, int packageCount) {}
|
||||
|
||||
+1
@@ -25,6 +25,7 @@ public class WarehouseTransferCarton {
|
||||
private java.math.BigDecimal heightCm;
|
||||
private java.math.BigDecimal grossWeightKg;
|
||||
private Long countedQuantity;
|
||||
private String skuItemsJson;
|
||||
private String countIdempotencyKey;
|
||||
private String countRequestHash;
|
||||
private String discrepancyReason;
|
||||
|
||||
+32
-5
@@ -535,7 +535,7 @@ public class WarehouseTransferService {
|
||||
value.getSourceLastlegOrderNo(), value.getInboundOrderId(), value.getInboundCode())).toList();
|
||||
List<WarehouseTransferResponses.Carton> cartons = transferCartons.listOrder(customerId, transfer.getId())
|
||||
.stream().map(value -> new WarehouseTransferResponses.Carton(value.getCartonMark(), value.getCustomerCartonMark(),
|
||||
value.getCartonIndex(), value.getTotalCartons(), value.getStatus(), value.getTransferItemId(), value.getTransferPalletId(), value.getLengthCm(), value.getWidthCm(), value.getHeightCm(), value.getGrossWeightKg(), value.getCountedQuantity(), value.getDiscrepancyReason(), value.getCountedBy(), value.getCountedAt())).toList();
|
||||
value.getCartonIndex(), value.getTotalCartons(), value.getStatus(), value.getTransferItemId(), value.getTransferPalletId(), value.getLengthCm(), value.getWidthCm(), value.getHeightCm(), value.getGrossWeightKg(), value.getCountedQuantity(), value.getDiscrepancyReason(), value.getCountedBy(), value.getCountedAt(), cartonContents(value))).toList();
|
||||
List<WarehouseTransferPalletItem> palletItemRows = transferPalletItems == null
|
||||
? List.of() : transferPalletItems.listOrder(customerId, transfer.getId());
|
||||
Map<Long, List<WarehouseTransferPalletItem>> palletItemsByPallet = palletItemRows.stream()
|
||||
@@ -621,7 +621,7 @@ public class WarehouseTransferService {
|
||||
if (DRAFT.equals(transfer.getStatus())) throw conflict("Submit the warehouse transfer order before printing carton labels");
|
||||
List<WarehouseTransferResponses.Carton> cartons = transferCartons.listOrder(customerId, transfer.getId()).stream()
|
||||
.filter(value -> !"NOT_RECEIVED".equals(value.getStatus()))
|
||||
.map(value -> new WarehouseTransferResponses.Carton(value.getCartonMark(), value.getCustomerCartonMark(), value.getCartonIndex(), value.getTotalCartons(), value.getStatus(), value.getTransferItemId(), value.getTransferPalletId(), value.getLengthCm(), value.getWidthCm(), value.getHeightCm(), value.getGrossWeightKg(), value.getCountedQuantity(), value.getDiscrepancyReason(), value.getCountedBy(), value.getCountedAt())).toList();
|
||||
.map(value -> new WarehouseTransferResponses.Carton(value.getCartonMark(), value.getCustomerCartonMark(), value.getCartonIndex(), value.getTotalCartons(), value.getStatus(), value.getTransferItemId(), value.getTransferPalletId(), value.getLengthCm(), value.getWidthCm(), value.getHeightCm(), value.getGrossWeightKg(), value.getCountedQuantity(), value.getDiscrepancyReason(), value.getCountedBy(), value.getCountedAt(), cartonContents(value))).toList();
|
||||
StringBuilder labels = new StringBuilder();
|
||||
for (WarehouseTransferResponses.Carton carton : cartons) {
|
||||
labels.append("<section class=label><div class=title>仓储转运箱唛</div><div class=mark>")
|
||||
@@ -829,8 +829,18 @@ public class WarehouseTransferService {
|
||||
throw badRequest("箱号不属于本单或重复填写");
|
||||
if (carton == null && input.transferItemId() == null) throw badRequest("新增实收箱必须选择本单申报明细");
|
||||
var item = byItem.get(carton == null ? input.transferItemId() : carton.getTransferItemId());
|
||||
if (item == null || !item.getSkuCode().equals(input.skuCode())) throw badRequest("箱内 SKU 必须与该箱申报明细一致");
|
||||
if (input.quantity() <= 0 || input.quantity() > Integer.MAX_VALUE || input.length() == null || input.width() == null
|
||||
if (item == null) throw badRequest("箱子申报明细不属于本单");
|
||||
if (input.items() == null || input.items().isEmpty() || input.items().size() > 200)
|
||||
throw badRequest("每箱必须填写 1 至 200 行 SKU");
|
||||
Set<String> skuCodes = new java.util.HashSet<>();
|
||||
for (var line : input.items()) {
|
||||
if (line == null || !StringUtils.hasText(line.skuCode())
|
||||
|| byItem.values().stream().noneMatch(declared -> declared.getSkuCode().equals(line.skuCode()))
|
||||
|| !skuCodes.add(line.skuCode())) throw badRequest("箱内 SKU 必须来自本单预报,同箱不能重复");
|
||||
if (line.quantity() <= 0 || line.quantity() > Integer.MAX_VALUE) throw badRequest("每行 SKU 必须填写有效实点件数");
|
||||
}
|
||||
if (!skuCodes.contains(item.getSkuCode())) throw badRequest("请保留箱子原申报 SKU,可添加其他本单 SKU");
|
||||
if (input.length() == null || input.width() == null
|
||||
|| input.height() == null || input.weight() == null || input.length().signum() <= 0 || input.width().signum() <= 0
|
||||
|| input.height().signum() <= 0 || input.weight().signum() <= 0) throw badRequest("每箱必须填写有效尺寸、重量和实点件数");
|
||||
}
|
||||
@@ -853,7 +863,14 @@ public class WarehouseTransferService {
|
||||
}
|
||||
carton.setTransferPalletId(input.transferPalletId());
|
||||
carton.setLengthCm(input.length()); carton.setWidthCm(input.width()); carton.setHeightCm(input.height());
|
||||
carton.setGrossWeightKg(input.weight()); carton.setCountedQuantity(input.quantity());
|
||||
carton.setGrossWeightKg(input.weight());
|
||||
carton.setCountedQuantity(input.items().stream().mapToLong(WarehouseTransferRequests.CountSku::quantity).sum());
|
||||
List<WarehouseTransferResponses.CartonItem> contents = input.items().stream().map(line -> {
|
||||
var declared = byItem.values().stream().filter(value -> value.getSkuCode().equals(line.skuCode())).findFirst().orElseThrow();
|
||||
return new WarehouseTransferResponses.CartonItem(declared.getId(), line.skuCode(), line.quantity());
|
||||
}).toList();
|
||||
try { carton.setSkuItemsJson(objectMapper.writeValueAsString(contents)); }
|
||||
catch (com.fasterxml.jackson.core.JsonProcessingException e) { throw new IllegalStateException("Cannot serialize carton contents", e); }
|
||||
carton.setCountedBy(operatorId); carton.setCountedAt(now); carton.setStatus("COUNTED");
|
||||
carton.setCountIdempotencyKey(request.idempotencyKey()); carton.setCountRequestHash(requestHash); carton.setUpdatedAt(now);
|
||||
carton.setDiscrepancyReason(trim(request.discrepancyReason()));
|
||||
@@ -871,6 +888,16 @@ public class WarehouseTransferService {
|
||||
return detail(customerId, transferNo);
|
||||
}
|
||||
|
||||
private List<WarehouseTransferResponses.CartonItem> cartonContents(WarehouseTransferCarton carton) {
|
||||
if (!StringUtils.hasText(carton.getSkuItemsJson())) return List.of();
|
||||
try {
|
||||
return objectMapper.readValue(carton.getSkuItemsJson(),
|
||||
new com.fasterxml.jackson.core.type.TypeReference<List<WarehouseTransferResponses.CartonItem>>() {});
|
||||
} catch (com.fasterxml.jackson.core.JsonProcessingException e) {
|
||||
throw new IllegalStateException("Invalid persisted carton contents", e);
|
||||
}
|
||||
}
|
||||
|
||||
private List<WarehouseTransferCarton> requireCountedCartons(WarehouseTransferOrder transfer) {
|
||||
var cartons = transferCartons.listOrder(transfer.getCustomerId(), transfer.getId());
|
||||
if (cartons.isEmpty() || cartons.stream().anyMatch(box -> box.getCountedAt() == null)
|
||||
|
||||
@@ -4145,6 +4145,11 @@ CREATE TABLE IF NOT EXISTS ll_selfop_inventory_daily (
|
||||
CONSTRAINT chk_selfop_daily_quantity CHECK ((quality_status='VALID' AND physical_quantity IS NOT NULL AND physical_quantity>=0) OR (quality_status='UNKNOWN' AND physical_quantity IS NULL))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='历史日末分货主商品品质实物库存';
|
||||
|
||||
-- 仓储转运一箱多SKU清点;仅增加结构,不回填历史业务数据。
|
||||
USE baiyue_lastleg;
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_carton' AND column_name='sku_items_json'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_carton ADD COLUMN sku_items_json JSON NULL COMMENT ''实收箱内SKU及逐项件数明细'''));
|
||||
PREPARE transfer_sku_stmt FROM @ddl; EXECUTE transfer_sku_stmt; DEALLOCATE PREPARE transfer_sku_stmt;
|
||||
|
||||
|
||||
-- Appointment work scope, audit and physical batch allocation.
|
||||
USE baiyue_lastleg;
|
||||
|
||||
@@ -33,7 +33,7 @@ paths:
|
||||
/api/pda/v1/last-leg/warehouse-transfers/{transferNo}/cartons/count:
|
||||
post:
|
||||
summary: 转运逐箱测量与清点
|
||||
description: 仅限 lastleg:pda:operate 与 OVERSEAS_WAREHOUSE_PDA_OPERATOR,绑定仓由统一Token确定。先创建本单托盘号,每箱选择所属托盘并按申报SKU记录实点件数,托盘体积重量由分配箱子汇总,全部实收箱子测量清点后才可完成打托入库,打托箱数与已清点实收箱数核平;短收箱保留未收到事实,超收生成系统箱号;差异需原因。事务锁定转运单;原键原载荷重试不重复写入,同键不同载荷拒绝,已确认事实不可覆盖。
|
||||
description: 仅限 lastleg:pda:operate 与 OVERSEAS_WAREHOUSE_PDA_OPERATOR,绑定仓由统一Token确定。先创建本单托盘号,每箱选择所属托盘,可包含多个本单预报SKU,逐项记录实点件数;同箱SKU不可重复,尺寸重量和实收箱数按实物箱计算一次,托盘体积重量由分配箱子汇总,全部实收箱子测量清点后才可完成打托入库,打托箱数与已清点实收箱数核平;短收箱保留未收到事实,超收生成系统箱号;差异需原因。事务锁定转运单;原键原载荷重试不重复写入,同键不同载荷拒绝,已确认事实不可覆盖。
|
||||
parameters:
|
||||
- {name: transferNo, in: path, required: true, schema: {type: string}}
|
||||
requestBody:
|
||||
@@ -52,7 +52,7 @@ paths:
|
||||
maxItems: 5000
|
||||
items:
|
||||
type: object
|
||||
required: [transferPalletId, length, width, height, weight, skuCode, quantity]
|
||||
required: [transferPalletId, length, width, height, weight, items]
|
||||
properties:
|
||||
cartonMark: {type: string, maxLength: 255, nullable: true, description: 已有箱号;新增超收箱不传,由服务端生成}
|
||||
transferItemId: {type: integer, format: int64, nullable: true, description: 新增超收箱所属本单申报明细}
|
||||
@@ -61,10 +61,19 @@ paths:
|
||||
width: {type: number, minimum: 0.01, description: 箱宽厘米}
|
||||
height: {type: number, minimum: 0.01, description: 箱高厘米}
|
||||
weight: {type: number, minimum: 0.01, description: 箱重千克}
|
||||
skuCode: {type: string, maxLength: 255}
|
||||
quantity: {type: integer, minimum: 1, maximum: 2147483647, description: 箱内实点件数}
|
||||
items:
|
||||
type: array
|
||||
minItems: 1
|
||||
maxItems: 200
|
||||
description: 本箱实际SKU明细,仅限本单预报SKU,同箱不可重复
|
||||
items:
|
||||
type: object
|
||||
required: [skuCode, quantity]
|
||||
properties:
|
||||
skuCode: {type: string, maxLength: 255}
|
||||
quantity: {type: integer, minimum: 1, maximum: 2147483647, description: 本SKU箱内实点件数}
|
||||
responses:
|
||||
'200': {description: 统一业务响应,成功data为转运详情,cartons含逐箱尺寸重量件数及清点操作人时间}
|
||||
'200': {description: 统一业务响应,成功data为转运详情,cartons含逐箱尺寸重量、quantity合计件数、items逐SKU明细(transferItemId/skuCode/quantity)及清点操作人时间}
|
||||
/api/platform/v1/last-leg/inventory/balances/{balanceId}:
|
||||
get:
|
||||
summary: 查询单条库存余额详情
|
||||
|
||||
Reference in New Issue
Block a user