This commit is contained in:
2026-10-15 18:25:53 +08:00
parent caff693ad8
commit cda5f3dc32
10 changed files with 109 additions and 16 deletions
@@ -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
@@ -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) {}
@@ -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;
@@ -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)