fix(dropship): preserve carton quantities and shared pallet outbound ownership

This commit is contained in:
2026-09-14 20:55:09 +08:00
parent 6dbcb2128c
commit 4f13ee2ec3
24 changed files with 302 additions and 99 deletions
@@ -265,13 +265,21 @@ public class DeliveryTransferService {
outbound.setPackageCount(request.items().stream().mapToInt(i -> i.packages().intValue()).sum()); outbound.setPalletCount(request.items().size());
outbound.setIdempotencyKey(request.idempotencyKey().trim()); outbound.setCreatedBy(operatorId); outbound.setCreatedAt(now); outbound.setUpdatedAt(now);
outboundOrders.insert(outbound);
Set<Long> physicalPalletIds = new HashSet<>();
for (DeliveryRequests.OneClickItem input : request.items()) {
InventoryPosition position = inventoryPositions.selectById(input.inventoryPositionId());
if (position == null || !customerId.equals(position.getCustomerId())) throw notFound("Inventory position not found");
InventoryBalance balance = inventoryBalances.selectById(position.getBalanceId());
if (balance == null || !customerId.equals(balance.getCustomerId()) || !StringUtils.hasText(balance.getInboundCode())) {
throw conflict("一件代发出库库存缺少所属入仓码");
}
OutboundPalletItem item = new OutboundPalletItem(); item.setId(IdWorker.getId()); item.setCustomerId(customerId); item.setOutboundOrderId(outbound.getId());
item.setInboundCode(balance.getInboundCode());
physicalPalletIds.add(position.getPalletId());
item.setWarehousePalletId(position.getPalletId()); item.setPalletNo(position.getPalletNo()); item.setPackageCount(input.packages().intValue()); item.setPickedPackageCount(0); item.setStatus("WAIT_PICK"); item.setSkuCode(input.sku().trim()); item.setCartonMarks(String.join(",", input.cartonMarks() == null ? List.of() : input.cartonMarks())); item.setCreatedAt(now);
outboundPallets.insert(item);
}
outbound.setPalletCount(physicalPalletIds.size());
outbound.setOverseasWarehouseId(selectedWarehouseId); outboundOrders.updateById(outbound);
OutboundPdaTask task = new OutboundPdaTask(); task.setId(IdWorker.getId()); task.setCustomerId(customerId); task.setOutboundOrderId(outbound.getId());
task.setTaskNo("PDA" + now.format(DateTimeFormatter.BASIC_ISO_DATE) + task.getId()); task.setStatus("PENDING"); task.setPushedAt(now); task.setCreatedAt(now); task.setUpdatedAt(now); pdaTasks.insert(task);
@@ -476,6 +484,11 @@ public class DeliveryTransferService {
if (!warehouseId.equals(selectedPosition.getOverseasWarehouseId())) {
throw conflict("Scanned inventory position does not belong to the bound warehouse");
}
InventoryBalance selectedBalance = inventoryBalances.selectById(selectedPosition.getBalanceId());
if (selectedBalance == null || !customerId.equals(selectedBalance.getCustomerId())
|| !StringUtils.hasText(item.getInboundCode()) || !item.getInboundCode().equals(selectedBalance.getInboundCode())) {
throw conflict("所选库存不属于出库明细的入仓码");
}
DropshipOrderProfile profile = dropshipProfiles.findPosition(customerId, request.inventoryPositionId());
if (profile == null) throw conflict("Scanned pallet is not one-click dropship inventory");
List<DropshipCarton> cartons = StringUtils.hasText(item.getCartonMarks())
@@ -488,7 +501,12 @@ public class DeliveryTransferService {
inventoryCommands.allocate(customerId, operatorId, move);
inventoryCommands.pick(customerId, operatorId, new InventoryCommandRequests.Move(request.inventoryPositionId(), request.packages(), outbound.getOutboundNo(), outbound.getOutboundNo(), null, request.idempotencyKey() + ":PICK", null));
inventoryCommands.ship(customerId, operatorId, new InventoryCommandRequests.Move(request.inventoryPositionId(), request.packages(), outbound.getOutboundNo(), outbound.getOutboundNo(), null, request.idempotencyKey() + ":SHIP", null));
if (dropshipSkuInventory.deduct(customerId, profile.getId(), item.getSkuCode(), request.packages()) != 1) throw conflict("Insufficient one-click dropship SKU inventory");
long productQuantity = 0;
for (DropshipCarton carton : cartons) {
if (carton.getProductQuantity() == null || carton.getProductQuantity() <= 0) throw conflict("出库箱唛缺少逐箱实收件数");
productQuantity = Math.addExact(productQuantity, carton.getProductQuantity());
}
if (dropshipSkuInventory.deduct(customerId, profile.getId(), item.getSkuCode(), request.packages(), productQuantity) != 1) throw conflict("Insufficient one-click dropship SKU inventory");
for (DropshipCarton carton : cartons) { carton.setStatus("SHIPPED"); carton.setOutboundOrderNo(outbound.getOutboundNo()); carton.setVersion((carton.getVersion() == null ? 0 : carton.getVersion()) + 1); carton.setUpdatedAt(LocalDateTime.now()); dropshipCartons.updateById(carton); }
OutboundPdaPick pick = new OutboundPdaPick(); pick.setId(IdWorker.getId()); pick.setCustomerId(customerId); pick.setTaskId(task.getId()); pick.setOutboundPalletItemId(item.getId()); pick.setInventoryPositionId(request.inventoryPositionId()); pick.setPackages(request.packages()); pick.setIdempotencyKey(request.idempotencyKey().trim()); pick.setPickedAt(LocalDateTime.now()); pdaPicks.insert(pick);
item.setPickedPackageCount(value(item.getPickedPackageCount()) + request.packages().intValue()); item.setStatus(item.getPickedPackageCount() >= item.getPackageCount() ? "PICKED" : "PARTIAL_PICKED"); outboundPallets.updateById(item);
@@ -630,6 +648,8 @@ public class DeliveryTransferService {
long available = value(position.getAvailablePackages());
if (available <= 0 || !warehouseId.equals(position.getOverseasWarehouseId())) continue;
InventoryBalance balance = inventoryBalances.selectById(position.getBalanceId());
if (StringUtils.hasText(item.getInboundCode()) && (balance == null
|| !item.getInboundCode().equals(balance.getInboundCode()))) continue;
DropshipOrderProfile profile = dropshipProfiles.findPosition(customerId, position.getId());
List<String> marks = profile == null ? List.of() : dropshipCartons.listProfile(customerId, profile.getId())
.stream().filter(carton -> "AVAILABLE".equals(carton.getStatus()))
@@ -698,10 +718,13 @@ public class DeliveryTransferService {
OutboundOrder outbound = outboundOrders.findGlobalByNo(trim(outboundNo));
if (outbound == null) throw notFound("Outbound order not found");
String customerId = outbound.getCustomerId();
DeliveryOrder delivery = requireDelivery(customerId, outbound.getId());
DeliveryOrder delivery = deliveryOrders.lockByOutbound(customerId, outbound.getId());
if (delivery == null && "DISPATCH_DELIVERY".equals(outbound.getOutboundType())) {
throw new IllegalStateException("Delivery document is missing for dispatch outbound order");
}
List<DeliveryResponses.OutboundPalletRow> pallets = outboundPallets.findByOutbound(customerId, outbound.getId()).stream()
.map(this::outboundPalletRow).toList();
return new DeliveryResponses.OutboundDetail(outboundRow(outbound), deliveryRow(delivery), pallets);
return new DeliveryResponses.OutboundDetail(outboundRow(outbound), delivery == null ? null : deliveryRow(delivery), pallets);
}
public PageResult<DeliveryResponses.DeliveryRow> deliveryPage(String customerId, DeliveryRequests.Query query) {