提交
This commit is contained in:
+8
@@ -28,6 +28,14 @@ import java.util.List;
|
||||
public class MerchantWarehouseTransferController {
|
||||
@Resource private WarehouseTransferService service;
|
||||
|
||||
@GetMapping("/inventory-source-cartons")
|
||||
@PreAuthorize("hasAuthority('" + OverseasPermissions.MERCHANT_TRANSFER_CREATE + "')")
|
||||
public ApiResponse<List<WarehouseTransferResponses.SourceCarton>> sourceCartons(
|
||||
@AuthenticationPrincipal AuthenticatedUser user, @RequestParam String operatingWarehouseId,
|
||||
@RequestParam String inboundCode) {
|
||||
return ApiResponse.success(service.sourceCartons(customer(user), operatingWarehouseId, inboundCode));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorize("hasAuthority('" + OverseasPermissions.MERCHANT_TRANSFER_CREATE + "')")
|
||||
public ApiResponse<WarehouseTransferResponses.Detail> create(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
|
||||
+11
-3
@@ -29,13 +29,13 @@ public final class WarehouseTransferRequests {
|
||||
@Size(max = 32) String privateDestinationPostalCode,
|
||||
@Size(max = 128) String privateDestinationContactName,
|
||||
@Size(max = 64) String privateDestinationContactPhone,
|
||||
@NotBlank @Size(max = 1000) String deliveryAddress,
|
||||
@Size(max = 1000) String deliveryAddress,
|
||||
@Size(max = 128) String pickupContactName,
|
||||
@Size(max = 64) String pickupContactPhone,
|
||||
@Positive int packageCount,
|
||||
@NotNull @DecimalMin(value = "0.0") BigDecimal volumeCbm,
|
||||
@NotBlank @Size(max = 64) String deliveryMethod,
|
||||
@NotNull LocalDateTime estimatedArrivalAt,
|
||||
@Size(max = 64) String deliveryMethod,
|
||||
LocalDateTime estimatedArrivalAt,
|
||||
LocalDateTime estimatedShipAt,
|
||||
@Size(max = 128) String fbaShipmentNo,
|
||||
@Size(max = 255) String trackingNo,
|
||||
@@ -51,6 +51,14 @@ public final class WarehouseTransferRequests {
|
||||
@NotEmpty @Size(max = 500) List<@Valid Item> items,
|
||||
@Size(max = 1000) List<@Valid RelabelItem> relabelItems
|
||||
) {
|
||||
@jakarta.validation.constraints.AssertTrue(message="外来货物须填写入仓方式、地址和预计入仓时间;库存转运须选择FBA或私人仓")
|
||||
public boolean isInboundModeValid() {
|
||||
if (inboundCode != null && !inboundCode.isBlank())
|
||||
return "FBA".equals(destinationType) || "PRIVATE".equals(destinationType)
|
||||
|| destinationType == null && fbaWarehouseCode != null && !fbaWarehouseCode.isBlank();
|
||||
return deliveryMethod != null && !deliveryMethod.isBlank() && deliveryAddress != null
|
||||
&& !deliveryAddress.isBlank() && estimatedArrivalAt != null;
|
||||
}
|
||||
public Create(String inboundCode, List<Long> sourceLastlegOrderIds, String operatingWarehouseId,
|
||||
String fbaWarehouseCode, String deliveryAddress, int packageCount, BigDecimal volumeCbm,
|
||||
String deliveryMethod, LocalDateTime estimatedArrivalAt, String fbaShipmentNo,
|
||||
|
||||
+3
@@ -6,6 +6,9 @@ import java.util.List;
|
||||
|
||||
public final class WarehouseTransferResponses {
|
||||
private WarehouseTransferResponses() {}
|
||||
public record SourceCarton(String cartonMark, String productNameZh, Long quantity,
|
||||
String destinationWarehouseCode, String destinationWarehouseName, List<SourceCartonSku> items) {}
|
||||
public record SourceCartonSku(String skuCode, Long quantity) {}
|
||||
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,
|
||||
|
||||
+19
@@ -3,6 +3,25 @@ import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
@Mapper public interface InboundSnapshotMapper {
|
||||
record SourceCarton(String cartonMark, String skuCode, String productNameZh, Long quantity,
|
||||
String destinationWarehouseCode, String destinationWarehouseName, String skuItemsJson) {}
|
||||
@Select("""
|
||||
SELECT c.carton_mark cartonMark,c.sku_code skuCode,NULL productNameZh,NULL quantity,
|
||||
c.destination_warehouse_code destinationWarehouseCode,c.destination_warehouse_name destinationWarehouseName,NULL skuItemsJson
|
||||
FROM ll_inbound_plan_carton c JOIN ll_order o ON o.id=c.order_id AND o.customer_id=c.customer_id
|
||||
WHERE c.customer_id=#{customerId} AND o.inbound_code=#{inboundCode} AND o.overseas_warehouse_id=#{warehouseId}
|
||||
UNION ALL
|
||||
SELECT c.carton_mark,i.sku_code,i.product_name_zh,c.counted_quantity,
|
||||
t.destination_warehouse_code,t.destination_warehouse_name,c.sku_items_json
|
||||
FROM ll_warehouse_transfer_carton c
|
||||
JOIN ll_warehouse_transfer_order t ON t.id=c.transfer_order_id AND t.customer_id=c.customer_id
|
||||
JOIN ll_order o ON o.id=t.lastleg_order_id AND o.customer_id=t.customer_id
|
||||
JOIN ll_warehouse_transfer_item i ON i.id=c.transfer_item_id AND i.customer_id=c.customer_id AND i.transfer_order_id=t.id
|
||||
WHERE c.customer_id=#{customerId} AND o.inbound_code=#{inboundCode} AND t.operating_warehouse_id=#{warehouseId} AND c.status='COUNTED'
|
||||
ORDER BY cartonMark
|
||||
""")
|
||||
java.util.List<SourceCarton> sourceCartons(@Param("customerId") String customerId,
|
||||
@Param("warehouseId") String warehouseId, @Param("inboundCode") String inboundCode);
|
||||
@Select("SELECT id FROM ll_inbound_order WHERE customer_id=#{customerId} AND (inbound_code=#{code} OR source_inbound_id=#{code}) LIMIT 1")
|
||||
Long findId(@Param("customerId") String customerId, @Param("code") String code);
|
||||
@Select("SELECT id FROM ll_inbound_order WHERE customer_id=#{customerId} AND source_inbound_id=#{sourceInboundId} LIMIT 1")
|
||||
|
||||
+62
-17
@@ -88,6 +88,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class WarehouseTransferService {
|
||||
@Resource private com.baiyue.lastleg.service.CompactBusinessNumberService compactNumbers;
|
||||
@Resource private com.baiyue.lastleg.fleet.customer.FleetCustomerDirectory customerDirectory;
|
||||
@Resource private com.baiyue.lastleg.fleet.business.FleetPoolOrderBridge fleetPool;
|
||||
@Resource private com.baiyue.lastleg.fleet.business.FleetPoolOrderQueries fleetPoolQueries;
|
||||
@@ -140,14 +141,35 @@ public class WarehouseTransferService {
|
||||
@Resource private InventoryCommandMapper inventoryCommands;
|
||||
@Resource private ObjectMapper objectMapper;
|
||||
|
||||
public List<WarehouseTransferResponses.SourceCarton> sourceCartons(String customerId, String warehouseId, String inboundCode) {
|
||||
if (!StringUtils.hasText(warehouseId) || !StringUtils.hasText(inboundCode)) throw badRequest("请选择海外仓和库存来源单据");
|
||||
return inboundSnapshots.sourceCartons(customerId, warehouseId.trim(), inboundCode.trim()).stream().map(row -> {
|
||||
var carton = new WarehouseTransferCarton(); carton.setSkuItemsJson(row.skuItemsJson());
|
||||
var contents = cartonContents(carton).stream().map(item -> new WarehouseTransferResponses.SourceCartonSku(item.skuCode(),item.quantity())).toList();
|
||||
if (contents.isEmpty()) contents = List.of(new WarehouseTransferResponses.SourceCartonSku(row.skuCode(),row.quantity()));
|
||||
return new WarehouseTransferResponses.SourceCarton(row.cartonMark(),row.productNameZh(),row.quantity(),
|
||||
row.destinationWarehouseCode(),row.destinationWarehouseName(),contents);
|
||||
}).toList();
|
||||
}
|
||||
private static LocalDateTime arrivalTime(WarehouseTransferRequests.Create request) {
|
||||
return StringUtils.hasText(request.inboundCode()) ? null : request.estimatedArrivalAt();
|
||||
}
|
||||
private static String inboundMethod(WarehouseTransferRequests.Create request) {
|
||||
return StringUtils.hasText(request.inboundCode()) ? "INVENTORY_TRANSFER" : request.deliveryMethod().trim();
|
||||
}
|
||||
private static String inboundAddress(WarehouseTransferRequests.Create request) {
|
||||
return StringUtils.hasText(request.inboundCode()) ? "" : request.deliveryAddress().trim();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public WarehouseTransferResponses.Detail create(String customerId, String operatorId,
|
||||
WarehouseTransferRequests.Create request) {
|
||||
if (!request.isInboundModeValid()) throw badRequest("外来货物须填写入仓信息,库存转运须选择FBA仓或私人仓");
|
||||
int itemPackages = request.items().stream().mapToInt(WarehouseTransferRequests.Item::packageCount).sum();
|
||||
if (itemPackages != request.packageCount()) {
|
||||
throw badRequest("Cargo item package total must equal transfer package count");
|
||||
}
|
||||
validateRelabelRequest(request);
|
||||
validateRelabelRequest(customerId, request);
|
||||
validateDestination(request.destinationType(), request.destinationWarehouseCode(), request.fbaWarehouseCode(),
|
||||
request.privateDestinationAddress(), request.privateDestinationPostalCode(),
|
||||
request.privateDestinationContactName(), request.privateDestinationContactPhone());
|
||||
@@ -167,7 +189,7 @@ public class WarehouseTransferService {
|
||||
}
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
long transferId = IdWorker.getId();
|
||||
String transferNo = orderNoGenerator.next("TRANSFER", request.operatingWarehouseId(), transferId);
|
||||
String transferNo = compactNumbers.next("ZY", customerId);
|
||||
String requestedInboundCode = trim(request.inboundCode());
|
||||
List<Long> selectedIds = request.sourceLastlegOrderIds() == null
|
||||
? List.of() : request.sourceLastlegOrderIds().stream().distinct().toList();
|
||||
@@ -210,13 +232,13 @@ public class WarehouseTransferService {
|
||||
transfer.setOperatingWarehouseId(request.operatingWarehouseId().trim());
|
||||
transfer.setFbaWarehouseCode(destinationCode(request));
|
||||
applyDestination(transfer, request);
|
||||
transfer.setDeliveryAddress(request.deliveryAddress().trim()); transfer.setPackageCount(request.packageCount());
|
||||
transfer.setDeliveryAddress(inboundAddress(request)); transfer.setPackageCount(request.packageCount());
|
||||
transfer.setPickupContactName(trim(request.pickupContactName()));
|
||||
transfer.setPickupContactPhone(trim(request.pickupContactPhone()));
|
||||
transfer.setDeliveryMethod(request.deliveryMethod().trim()); transfer.setEstimatedArrivalAt(request.estimatedArrivalAt());
|
||||
transfer.setDeliveryMethod(inboundMethod(request)); transfer.setEstimatedArrivalAt(arrivalTime(request));
|
||||
transfer.setEstimatedShipAt(request.estimatedShipAt());
|
||||
transfer.setFbaShipmentNo(trim(request.fbaShipmentNo()));
|
||||
transfer.setTrackingNo("CUSTOMER_DELIVERY".equals(request.deliveryMethod().trim()) ? trim(request.trackingNo()) : null);
|
||||
transfer.setTrackingNo("CUSTOMER_DELIVERY".equals(inboundMethod(request)) ? trim(request.trackingNo()) : null);
|
||||
transfer.setFbaBoxNos(trim(request.fbaBoxNos())); transfer.setRemark(trim(request.remark()));
|
||||
transfer.setNeedRelabel(request.needRelabel()); transfer.setStatus(DRAFT);
|
||||
transfer.setLabelDeadline(request.labelDeadline());
|
||||
@@ -278,11 +300,12 @@ public class WarehouseTransferService {
|
||||
if (!DRAFT.equals(transfer.getStatus())) {
|
||||
throw conflict("Only a draft warehouse transfer order can be edited");
|
||||
}
|
||||
if (!request.isInboundModeValid()) throw badRequest("外来货物须填写入仓信息,库存转运须选择FBA仓或私人仓");
|
||||
int itemPackages = request.items().stream().mapToInt(WarehouseTransferRequests.Item::packageCount).sum();
|
||||
if (itemPackages != request.packageCount()) {
|
||||
throw badRequest("Cargo item package total must equal transfer package count");
|
||||
}
|
||||
validateRelabelRequest(request);
|
||||
validateRelabelRequest(customerId, request);
|
||||
validateDestination(request.destinationType(), request.destinationWarehouseCode(), request.fbaWarehouseCode(),
|
||||
request.privateDestinationAddress(), request.privateDestinationPostalCode(),
|
||||
request.privateDestinationContactName(), request.privateDestinationContactPhone());
|
||||
@@ -293,15 +316,15 @@ public class WarehouseTransferService {
|
||||
transfer.setOperatingWarehouseId(request.operatingWarehouseId().trim());
|
||||
transfer.setFbaWarehouseCode(destinationCode(request));
|
||||
applyDestination(transfer, request);
|
||||
transfer.setDeliveryAddress(request.deliveryAddress().trim());
|
||||
transfer.setDeliveryAddress(inboundAddress(request));
|
||||
transfer.setPackageCount(request.packageCount());
|
||||
transfer.setPickupContactName(trim(request.pickupContactName()));
|
||||
transfer.setPickupContactPhone(trim(request.pickupContactPhone()));
|
||||
transfer.setDeliveryMethod(request.deliveryMethod().trim());
|
||||
transfer.setEstimatedArrivalAt(request.estimatedArrivalAt());
|
||||
transfer.setDeliveryMethod(inboundMethod(request));
|
||||
transfer.setEstimatedArrivalAt(arrivalTime(request));
|
||||
transfer.setEstimatedShipAt(request.estimatedShipAt());
|
||||
transfer.setFbaShipmentNo(trim(request.fbaShipmentNo()));
|
||||
transfer.setTrackingNo("CUSTOMER_DELIVERY".equals(request.deliveryMethod().trim())
|
||||
transfer.setTrackingNo("CUSTOMER_DELIVERY".equals(inboundMethod(request))
|
||||
? trim(request.trackingNo()) : null);
|
||||
transfer.setFbaBoxNos(trim(request.fbaBoxNos()));
|
||||
transfer.setRemark(trim(request.remark()));
|
||||
@@ -319,7 +342,7 @@ public class WarehouseTransferService {
|
||||
order.setFbaWarehouseCode(destinationCode(request));
|
||||
order.setPackageCount(request.packageCount());
|
||||
order.setVolumeCbm(request.volumeCbm());
|
||||
order.setEstimatedWarehouseAt(request.estimatedArrivalAt());
|
||||
order.setEstimatedWarehouseAt(arrivalTime(request));
|
||||
order.setRelabelStatus(request.needRelabel() ? "PENDING_BINDING" : "NOT_REQUIRED");
|
||||
order.setSubOrderStatus("WAREHOUSE_TRANSFER:" + DRAFT + ";RELABEL:" + order.getRelabelStatus());
|
||||
order.setCustomerRemark(trim(request.remark()));
|
||||
@@ -376,6 +399,19 @@ public class WarehouseTransferService {
|
||||
if (PENDING_REVIEW.equals(transfer.getStatus())) return detail(customerId, transferNo);
|
||||
if (!DRAFT.equals(transfer.getStatus())) throw conflict("Only a draft warehouse transfer order can be submitted");
|
||||
|
||||
if (Boolean.TRUE.equals(transfer.getNeedRelabel())) {
|
||||
Set<String> allowedMarks = !transfer.getTransferNo().equals(transfer.getInboundCode())
|
||||
? inboundSnapshots.sourceCartons(customerId, transfer.getOperatingWarehouseId(), transfer.getInboundCode())
|
||||
.stream().map(InboundSnapshotMapper.SourceCarton::cartonMark).collect(Collectors.toSet())
|
||||
: transferCartons.listOrder(customerId, transfer.getId()).stream()
|
||||
.flatMap(carton -> java.util.stream.Stream.of(carton.getCartonMark(), carton.getCustomerCartonMark()))
|
||||
.filter(StringUtils::hasText).collect(Collectors.toSet());
|
||||
for (var binding : relabelBindings.listOrder(customerId, transfer.getId())) {
|
||||
if (!allowedMarks.contains(binding.getCustomerCartonMark()))
|
||||
throw badRequest("当前箱子不存在或不属于当前订单");
|
||||
}
|
||||
}
|
||||
|
||||
LastlegOrder order = requireTailOrder(customerId, transfer);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
transfer.setStatus(PENDING_REVIEW);
|
||||
@@ -859,7 +895,7 @@ public class WarehouseTransferService {
|
||||
carton = new WarehouseTransferCarton(); carton.setId(IdWorker.getId());
|
||||
carton.setTransferOrderId(transfer.getId()); carton.setTransferItemId(input.transferItemId()); carton.setCustomerId(customerId);
|
||||
carton.setCartonIndex(++nextIndex); carton.setTotalCartons(transfer.getPackageCount());
|
||||
carton.setCartonMark(transferNo + "#EX-" + nextIndex); carton.setCreatedAt(now);
|
||||
carton.setCartonMark(LastlegOrderNoGenerator.compactCartonMark(carton.getId())); carton.setCreatedAt(now);
|
||||
}
|
||||
carton.setTransferPalletId(input.transferPalletId());
|
||||
carton.setLengthCm(input.length()); carton.setWidthCm(input.width()); carton.setHeightCm(input.height());
|
||||
@@ -1712,7 +1748,7 @@ public class WarehouseTransferService {
|
||||
}
|
||||
}
|
||||
|
||||
private void validateRelabelRequest(WarehouseTransferRequests.Create request) {
|
||||
private void validateRelabelRequest(String customerId, WarehouseTransferRequests.Create request) {
|
||||
if (!request.needRelabel()) {
|
||||
if (request.relabelItems() != null && !request.relabelItems().isEmpty()) {
|
||||
throw badRequest("Relabel items require needRelabel=true");
|
||||
@@ -1720,6 +1756,14 @@ public class WarehouseTransferService {
|
||||
return;
|
||||
}
|
||||
if (request.relabelItems() == null || request.relabelItems().isEmpty()) return;
|
||||
Set<String> orderCartonMarks = StringUtils.hasText(request.inboundCode())
|
||||
? inboundSnapshots.sourceCartons(customerId, request.operatingWarehouseId().trim(), request.inboundCode().trim())
|
||||
.stream().map(InboundSnapshotMapper.SourceCarton::cartonMark)
|
||||
.filter(StringUtils::hasText).map(String::trim).collect(Collectors.toSet())
|
||||
: request.items().stream().flatMap(item -> splitMarks(item.shippingMark()).stream()).collect(Collectors.toSet());
|
||||
if (!StringUtils.hasText(request.inboundCode()) && orderCartonMarks.isEmpty()) {
|
||||
orderCartonMarks.addAll(splitMarks(request.fbaBoxNos()));
|
||||
}
|
||||
if (request.labelDeadline() == null || !StringUtils.hasText(request.relabelContactName())
|
||||
|| !StringUtils.hasText(request.relabelContactPhone())) {
|
||||
throw badRequest("Relabel deadline and contact are required");
|
||||
@@ -1754,6 +1798,7 @@ public class WarehouseTransferService {
|
||||
throw badRequest("SKU relabel requires a new SKU");
|
||||
}
|
||||
for (String mark : row.customerCartonMarks()) {
|
||||
if (!orderCartonMarks.contains(mark.trim())) throw badRequest("当前箱子不存在或不属于当前订单");
|
||||
if (!marks.add(mark.trim().toUpperCase())) throw badRequest("Customer carton marks must be unique");
|
||||
}
|
||||
}
|
||||
@@ -1969,7 +2014,7 @@ public class WarehouseTransferService {
|
||||
private LastlegOrder createTailOrder(String customerId, String customerName, Long inboundOrderId, String inboundCode, String transferNo,
|
||||
WarehouseTransferRequests.Create request, String requestHash, LocalDateTime now) {
|
||||
LastlegOrder order = new LastlegOrder();
|
||||
order.setId(IdWorker.getId()); order.setOrderNo(orderNoGenerator.next("TRANSFER", request.operatingWarehouseId(), order.getId())); order.setCustomerId(customerId);
|
||||
order.setId(IdWorker.getId()); order.setOrderNo(transferNo); order.setCustomerId(customerId);
|
||||
order.setCustomerName(customerName);
|
||||
order.setOrderType(ORDER_TYPE);
|
||||
order.setInboundCode(inboundCode); order.setSourceRegion("CUSTOMER");
|
||||
@@ -1979,7 +2024,7 @@ public class WarehouseTransferService {
|
||||
order.setVolumeCbm(request.volumeCbm());
|
||||
order.setContainerNo(transferNo);
|
||||
order.setPalletCount(0); order.setPickedPackageCount(0); order.setShippedPackageCount(0);
|
||||
order.setEstimatedWarehouseAt(request.estimatedArrivalAt());
|
||||
order.setEstimatedWarehouseAt(arrivalTime(request));
|
||||
order.setCurrentStatus(DRAFT);
|
||||
order.setRelabelStatus(request.needRelabel() ? "PENDING_BINDING" : "NOT_REQUIRED");
|
||||
order.setSubOrderStatus("WAREHOUSE_TRANSFER:" + DRAFT + ";RELABEL:" + order.getRelabelStatus());
|
||||
@@ -2007,7 +2052,7 @@ public class WarehouseTransferService {
|
||||
String customerCartonMark, LocalDateTime now) {
|
||||
WarehouseTransferCarton carton = new WarehouseTransferCarton();
|
||||
carton.setId(IdWorker.getId()); carton.setTransferOrderId(transferId); carton.setTransferItemId(transferItemId);
|
||||
carton.setCustomerId(customerId); carton.setCartonMark(transferNo + "#" + totalCartons + "-" + cartonIndex);
|
||||
carton.setCustomerId(customerId); carton.setCartonMark(LastlegOrderNoGenerator.compactCartonMark(carton.getId()));
|
||||
carton.setCustomerCartonMark(trim(customerCartonMark));
|
||||
carton.setCartonIndex(cartonIndex); carton.setTotalCartons(totalCartons); carton.setStatus("EXPECTED");
|
||||
carton.setCreatedAt(now); carton.setUpdatedAt(now); transferCartons.insert(carton);
|
||||
@@ -2020,7 +2065,7 @@ public class WarehouseTransferService {
|
||||
detail.setOrderId(orderId); detail.setSourceInboundDetailId(transferNo + ":" + index);
|
||||
detail.setSkuCode(trim(source.skuCode())); detail.setProductNameZh(trim(source.productNameZh()));
|
||||
detail.setPackageCount(source.packageCount()); detail.setDestinationWarehouseCode(destinationCode(request));
|
||||
detail.setFbaTrackingNo("CUSTOMER_DELIVERY".equals(request.deliveryMethod().trim())
|
||||
detail.setFbaTrackingNo("CUSTOMER_DELIVERY".equals(inboundMethod(request))
|
||||
? trim(request.trackingNo()) : null);
|
||||
detail.setShipmentNo(trim(request.fbaShipmentNo()));
|
||||
detail.setShippingMark(trim(source.shippingMark())); orderDetails.insert(detail);
|
||||
|
||||
Reference in New Issue
Block a user