Test #1
+22
@@ -38,6 +38,28 @@ public final class InventoryCommandRequests {
|
||||
) {
|
||||
}
|
||||
|
||||
public record InTransit(
|
||||
String customerName,
|
||||
@NotBlank String overseasWarehouseId,
|
||||
String overseasWarehouseCode,
|
||||
String overseasWarehouseName,
|
||||
String destinationWarehouseId,
|
||||
@NotBlank String destinationWarehouseCode,
|
||||
String destinationWarehouseName,
|
||||
@NotNull Long inboundOrderId,
|
||||
@NotBlank String inboundOrderNo,
|
||||
@NotBlank String inboundCode,
|
||||
String skuCode,
|
||||
String productNameZh,
|
||||
String productNameEn,
|
||||
@Positive long packages,
|
||||
String businessOrderNo,
|
||||
String sourceEventId,
|
||||
@NotBlank @Size(max = 255) String idempotencyKey,
|
||||
@Size(max = 1000) String remark
|
||||
) {
|
||||
}
|
||||
|
||||
public record Move(
|
||||
@NotNull Long positionId,
|
||||
@Positive long packages,
|
||||
|
||||
+66
-24
@@ -38,6 +38,24 @@ public class InventoryCommandService {
|
||||
@Resource private WarehouseResourceGateway warehouseGateway;
|
||||
@Resource private ObjectMapper objectMapper;
|
||||
|
||||
@Transactional
|
||||
public InventoryCommandResponse registerInTransit(String customerId, String operatorId,
|
||||
InventoryCommandRequests.InTransit request) {
|
||||
if (request.packages() <= 0) throw badRequest("In-transit packages must be positive");
|
||||
return execute(customerId, operatorId, "IN_TRANSIT", request.idempotencyKey(), request, operation -> {
|
||||
InventoryBalance balance = balance(customerId, request);
|
||||
long before = value(balance.getInTransitPackages());
|
||||
balance.setInTransitPackages(before + request.packages());
|
||||
touch(balance);
|
||||
balances.updateById(balance);
|
||||
ledger(customerId, operatorId, operation.getId(), balance, null, "IN_TRANSIT",
|
||||
"OUTSIDE", "IN_TRANSIT", before, request.packages(), before + request.packages(),
|
||||
request.businessOrderNo(), request.inboundOrderNo(), null, request.sourceEventId(),
|
||||
request.idempotencyKey(), request.remark());
|
||||
return response(operation, balance, null, false);
|
||||
});
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public InventoryCommandResponse stockIn(String customerId, String operatorId,
|
||||
InventoryCommandRequests.StockIn request) {
|
||||
@@ -51,11 +69,14 @@ public class InventoryCommandService {
|
||||
position.setUpdatedAt(LocalDateTime.now());
|
||||
positions.updateById(position);
|
||||
|
||||
long transferred = Math.min(value(balance.getInTransitPackages()), request.packages());
|
||||
balance.setInTransitPackages(value(balance.getInTransitPackages()) - transferred);
|
||||
balance.setAvailablePackages(value(balance.getAvailablePackages()) + request.packages());
|
||||
touch(balance);
|
||||
balances.updateById(balance);
|
||||
ledger(customerId, operatorId, operation.getId(), balance, position, "STOCK_IN",
|
||||
"OUTSIDE", "AVAILABLE", before, request.packages(), before + request.packages(),
|
||||
transferred == request.packages() ? "IN_TRANSIT" : "OUTSIDE", "AVAILABLE",
|
||||
before, request.packages(), before + request.packages(),
|
||||
request.businessOrderNo(), request.inboundOrderNo(), null, request.sourceEventId(),
|
||||
request.idempotencyKey(), request.remark());
|
||||
return response(operation, balance, position, false);
|
||||
@@ -190,26 +211,47 @@ public class InventoryCommandService {
|
||||
}
|
||||
|
||||
private InventoryBalance balance(String customerId, InventoryCommandRequests.StockIn request) {
|
||||
String skuCode = sku(request.skuCode());
|
||||
InventoryBalance balance = balances.lockDimension(customerId, request.overseasWarehouseId(),
|
||||
request.destinationWarehouseCode(), request.inboundOrderId(), request.inboundCode(), skuCode);
|
||||
return balance(customerId, request.customerName(), request.overseasWarehouseId(),
|
||||
request.overseasWarehouseCode(), request.overseasWarehouseName(), request.destinationWarehouseId(),
|
||||
request.destinationWarehouseCode(), request.destinationWarehouseName(), request.inboundOrderId(),
|
||||
request.inboundOrderNo(), request.inboundCode(), request.skuCode(), request.productNameZh(),
|
||||
request.productNameEn());
|
||||
}
|
||||
|
||||
private InventoryBalance balance(String customerId, InventoryCommandRequests.InTransit request) {
|
||||
return balance(customerId, request.customerName(), request.overseasWarehouseId(),
|
||||
request.overseasWarehouseCode(), request.overseasWarehouseName(), request.destinationWarehouseId(),
|
||||
request.destinationWarehouseCode(), request.destinationWarehouseName(), request.inboundOrderId(),
|
||||
request.inboundOrderNo(), request.inboundCode(), request.skuCode(), request.productNameZh(),
|
||||
request.productNameEn());
|
||||
}
|
||||
|
||||
private InventoryBalance balance(String customerId, String customerName, String overseasWarehouseId,
|
||||
String overseasWarehouseCode, String overseasWarehouseName,
|
||||
String destinationWarehouseId, String destinationWarehouseCode,
|
||||
String destinationWarehouseName, Long inboundOrderId, String inboundOrderNo,
|
||||
String inboundCode, String requestedSkuCode, String productNameZh,
|
||||
String productNameEn) {
|
||||
String skuCode = sku(requestedSkuCode);
|
||||
InventoryBalance balance = balances.lockDimension(customerId, overseasWarehouseId,
|
||||
destinationWarehouseCode, inboundOrderId, inboundCode, skuCode);
|
||||
if (balance != null) return balance;
|
||||
balance = new InventoryBalance();
|
||||
balance.setId(IdWorker.getId());
|
||||
balance.setCustomerId(customerId);
|
||||
balance.setCustomerName(trim(request.customerName()));
|
||||
balance.setOverseasWarehouseId(request.overseasWarehouseId().trim());
|
||||
balance.setOverseasWarehouseCode(trim(request.overseasWarehouseCode()));
|
||||
balance.setOverseasWarehouseName(trim(request.overseasWarehouseName()));
|
||||
balance.setDestinationWarehouseId(trim(request.destinationWarehouseId()));
|
||||
balance.setDestinationWarehouseCode(request.destinationWarehouseCode().trim());
|
||||
balance.setDestinationWarehouseName(trim(request.destinationWarehouseName()));
|
||||
balance.setInboundOrderId(request.inboundOrderId());
|
||||
balance.setInboundOrderNo(request.inboundOrderNo().trim());
|
||||
balance.setInboundCode(request.inboundCode().trim());
|
||||
balance.setCustomerName(trim(customerName));
|
||||
balance.setOverseasWarehouseId(overseasWarehouseId.trim());
|
||||
balance.setOverseasWarehouseCode(trim(overseasWarehouseCode));
|
||||
balance.setOverseasWarehouseName(trim(overseasWarehouseName));
|
||||
balance.setDestinationWarehouseId(trim(destinationWarehouseId));
|
||||
balance.setDestinationWarehouseCode(destinationWarehouseCode.trim());
|
||||
balance.setDestinationWarehouseName(trim(destinationWarehouseName));
|
||||
balance.setInboundOrderId(inboundOrderId);
|
||||
balance.setInboundOrderNo(inboundOrderNo.trim());
|
||||
balance.setInboundCode(inboundCode.trim());
|
||||
balance.setSkuCode(skuCode);
|
||||
balance.setProductNameZh(trim(request.productNameZh()));
|
||||
balance.setProductNameEn(trim(request.productNameEn()));
|
||||
balance.setProductNameZh(trim(productNameZh));
|
||||
balance.setProductNameEn(trim(productNameEn));
|
||||
zero(balance);
|
||||
balance.setVersion(0L);
|
||||
balance.setCreatedAt(LocalDateTime.now());
|
||||
@@ -218,8 +260,8 @@ public class InventoryCommandService {
|
||||
balances.insert(balance);
|
||||
return balance;
|
||||
} catch (DuplicateKeyException exception) {
|
||||
InventoryBalance concurrent = balances.lockDimension(customerId, request.overseasWarehouseId(),
|
||||
request.destinationWarehouseCode(), request.inboundOrderId(), request.inboundCode(), skuCode);
|
||||
InventoryBalance concurrent = balances.lockDimension(customerId, overseasWarehouseId,
|
||||
destinationWarehouseCode, inboundOrderId, inboundCode, skuCode);
|
||||
if (concurrent != null) return concurrent;
|
||||
throw exception;
|
||||
}
|
||||
@@ -331,14 +373,14 @@ public class InventoryCommandService {
|
||||
ledger.setId(IdWorker.getId());
|
||||
ledger.setCustomerId(customerId);
|
||||
ledger.setBalanceId(balance.getId());
|
||||
ledger.setPositionId(position.getId());
|
||||
ledger.setPositionId(position == null ? null : position.getId());
|
||||
ledger.setOverseasWarehouseId(balance.getOverseasWarehouseId());
|
||||
ledger.setDestinationWarehouseCode(balance.getDestinationWarehouseCode());
|
||||
ledger.setInboundOrderId(balance.getInboundOrderId());
|
||||
ledger.setPalletId(position.getPalletId());
|
||||
ledger.setPalletNo(position.getPalletNo());
|
||||
ledger.setBatchNo(position.getBatchNo());
|
||||
ledger.setWarehouseLocationCode(position.getWarehouseLocationCode());
|
||||
ledger.setPalletId(position == null ? null : position.getPalletId());
|
||||
ledger.setPalletNo(position == null ? null : position.getPalletNo());
|
||||
ledger.setBatchNo(position == null ? null : position.getBatchNo());
|
||||
ledger.setWarehouseLocationCode(position == null ? null : position.getWarehouseLocationCode());
|
||||
ledger.setSkuCode(balance.getSkuCode());
|
||||
ledger.setBusinessType(businessType);
|
||||
ledger.setBusinessOrderId(balance.getInboundOrderId());
|
||||
@@ -367,7 +409,7 @@ public class InventoryCommandService {
|
||||
private InventoryCommandResponse response(InventoryOperation operation, InventoryBalance balance,
|
||||
InventoryPosition position, boolean palletEmpty) {
|
||||
return new InventoryCommandResponse(operation.getId(), operation.getOperationType(), balance.getId(),
|
||||
position.getId(), value(balance.getAvailablePackages()), value(balance.getHoldPackages()),
|
||||
position == null ? null : position.getId(), value(balance.getAvailablePackages()), value(balance.getHoldPackages()),
|
||||
value(balance.getAllocatedPackages()), value(balance.getPickedPackages()), palletEmpty, false);
|
||||
}
|
||||
|
||||
|
||||
+9
-8
@@ -41,10 +41,10 @@ public class InventoryQueryService {
|
||||
long inTransitPackages = number(value.getInTransitPackages());
|
||||
long receivingPackages = number(value.getReceivingPackages());
|
||||
long procurementPackages = number(value.getProcurementPackages());
|
||||
long totalPackages = availablePackages + holdPackages + allocatedPackages + pickedPackages + receivingPackages;
|
||||
return new InventoryDashboardResponse(number(value.getDimensionCount()), mapper.countPallets(customerId, query),
|
||||
availablePackages + holdPackages + allocatedPackages + pickedPackages + inTransitPackages
|
||||
+ receivingPackages + procurementPackages,
|
||||
availablePackages, holdPackages, allocatedPackages, pickedPackages, inTransitPackages,
|
||||
totalPackages, Math.max(0, totalPackages - holdPackages), holdPackages, allocatedPackages,
|
||||
pickedPackages, inTransitPackages,
|
||||
receivingPackages, procurementPackages);
|
||||
}
|
||||
|
||||
@@ -117,13 +117,13 @@ public class InventoryQueryService {
|
||||
long inTransitPackages = number(value.getInTransitPackages());
|
||||
long receivingPackages = number(value.getReceivingPackages());
|
||||
long procurementPackages = number(value.getProcurementPackages());
|
||||
long totalPackages = availablePackages + holdPackages + allocatedPackages + pickedPackages + receivingPackages;
|
||||
return new InventoryBalanceView(value.getId(), value.getCustomerId(), value.getCustomerName(), value.getOverseasWarehouseId(),
|
||||
value.getOverseasWarehouseCode(), value.getOverseasWarehouseName(), value.getDestinationWarehouseId(),
|
||||
value.getDestinationWarehouseCode(), value.getDestinationWarehouseName(), value.getInboundOrderId(),
|
||||
value.getInboundOrderNo(), value.getInboundCode(), value.getSkuCode(), value.getProductNameZh(), value.getProductNameEn(),
|
||||
availablePackages + holdPackages + allocatedPackages + pickedPackages
|
||||
+ inTransitPackages + receivingPackages + procurementPackages,
|
||||
availablePackages, holdPackages, allocatedPackages, pickedPackages, inTransitPackages,
|
||||
totalPackages, Math.max(0, totalPackages - holdPackages), holdPackages, allocatedPackages,
|
||||
pickedPackages, inTransitPackages,
|
||||
receivingPackages, procurementPackages, value.getUpdatedAt());
|
||||
}
|
||||
|
||||
@@ -133,12 +133,13 @@ public class InventoryQueryService {
|
||||
long allocated = number(value.getAllocatedPackages());
|
||||
long picked = number(value.getPickedPackages());
|
||||
long receiving = number(value.getReceivingPackages());
|
||||
long total = available + hold + allocated + picked + receiving;
|
||||
return new InventoryPositionView(value.getId(), value.getBalanceId(), value.getCustomerId(),
|
||||
value.getOverseasWarehouseId(), value.getDestinationWarehouseCode(), balance.getInboundOrderId(),
|
||||
balance.getInboundOrderNo(), balance.getInboundCode(),
|
||||
value.getPalletId(), value.getPalletNo(), value.getBatchNo(), value.getWarehouseLocationId(),
|
||||
value.getWarehouseLocationCode(), available + hold + allocated + picked + receiving,
|
||||
available, hold, allocated, picked, receiving, value.getStatus(),
|
||||
value.getWarehouseLocationCode(), total, Math.max(0, total - hold), hold, allocated, picked,
|
||||
receiving, value.getStatus(),
|
||||
value.getUpdatedAt());
|
||||
}
|
||||
|
||||
|
||||
+25
-1
@@ -8,6 +8,9 @@ import com.baiyue.lastleg.dropship.service.DropshipOrderService;
|
||||
import com.baiyue.lastleg.inboundplan.entity.InboundPlanCarton;
|
||||
import com.baiyue.lastleg.inboundplan.mapper.InboundPlanCartonMapper;
|
||||
import com.baiyue.lastleg.inboundplan.service.InboundPlanService;
|
||||
import com.baiyue.lastleg.inboundplan.dto.InboundPlanResponse;
|
||||
import com.baiyue.lastleg.inventory.command.dto.InventoryCommandRequests;
|
||||
import com.baiyue.lastleg.inventory.command.service.InventoryCommandService;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -35,6 +38,7 @@ public class LoadingCompletedConsumer {
|
||||
@Resource private ObjectMapper objectMapper;
|
||||
@Resource private LastlegOrderNoGenerator orderNoGenerator;
|
||||
@Resource private DropshipOrderService dropshipOrders;
|
||||
@Resource private InventoryCommandService inventory;
|
||||
|
||||
@Transactional
|
||||
public int consume(EventEnvelope envelope) {
|
||||
@@ -184,7 +188,10 @@ public class LoadingCompletedConsumer {
|
||||
LastlegOrderStatusHistory history=new LastlegOrderStatusHistory();
|
||||
history.setOrderId(order.getId()); history.setToStatus("PENDING_ASSIGNMENT"); history.setEventId(event.eventId());
|
||||
history.setRemark("头程装柜完成自动生成"); history.setCreatedAt(LocalDateTime.now()); historyMapper.insert(history);
|
||||
if ("FIRST_LEG".equals(orderType)) inboundPlans.convert(customerId, order.getId());
|
||||
if ("FIRST_LEG".equals(orderType)) {
|
||||
InboundPlanResponse plan = inboundPlans.convert(customerId, order.getId());
|
||||
registerInTransit(event, payload, source, order, plan.planNo());
|
||||
}
|
||||
if ("ONE_CLICK_DROPSHIP".equals(orderType)) {
|
||||
long warehouseId;
|
||||
try { warehouseId = Long.parseLong(payload.warehouseId()); }
|
||||
@@ -194,6 +201,23 @@ public class LoadingCompletedConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
private void registerInTransit(EventEnvelope event, FirstLegLoadingPlanCompletedV1 payload,
|
||||
FirstLegLoadingPlanCompletedV1.Order source, LastlegOrder order,
|
||||
String inboundOrderNo) {
|
||||
for (int index = 0; index < source.items().size(); index++) {
|
||||
FirstLegLoadingPlanCompletedV1.Item item = source.items().get(index);
|
||||
if (item.packageCount() <= 0) continue;
|
||||
inventory.registerInTransit(order.getCustomerId(), "SYSTEM",
|
||||
new InventoryCommandRequests.InTransit(order.getCustomerName(), payload.warehouseId(), null,
|
||||
payload.warehouseName(), item.destinationWarehouseId(), item.destinationWarehouseCode(),
|
||||
item.destinationWarehouseName(), order.getId(), inboundOrderNo, source.inboundCode(),
|
||||
item.skuCode(), item.productNameZh(), item.productNameEn(), item.packageCount(),
|
||||
order.getOrderNo(), event.eventId(),
|
||||
"TAIL_IN_TRANSIT:" + event.eventId() + ":" + order.getId() + ":" + index,
|
||||
"Tail-leg order created from completed loading plan"));
|
||||
}
|
||||
}
|
||||
|
||||
private void createCartonLabelPool(String customerId, FirstLegLoadingPlanCompletedV1.Item item) {
|
||||
List<String> marks = item.cartonMarks() == null ? List.of() : item.cartonMarks();
|
||||
if (marks.isEmpty()) return; // 历史 V1 事件保持原有按箱数入库路径。
|
||||
|
||||
@@ -971,6 +971,18 @@ CREATE TABLE IF NOT EXISTS ll_warehouse_transfer_order (
|
||||
KEY idx_ll_wh_transfer_list (customer_id,status,created_at), KEY idx_ll_wh_transfer_inbound (customer_id,inbound_code)
|
||||
) ENGINE=InnoDB COMMENT='客户转仓单';
|
||||
|
||||
-- 兼容已存在的旧版转仓单表:CREATE TABLE IF NOT EXISTS 不会补充新增字段。
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='allocation_idempotency_key'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN allocation_idempotency_key VARCHAR(255) NULL COMMENT ''库存分配幂等键'' AFTER status'));
|
||||
PREPARE transfer_allocation_key_stmt FROM @ddl; EXECUTE transfer_allocation_key_stmt; DEALLOCATE PREPARE transfer_allocation_key_stmt;
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='allocation_request_hash'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN allocation_request_hash CHAR(64) NULL COMMENT ''库存分配请求哈希'' AFTER allocation_idempotency_key'));
|
||||
PREPARE transfer_allocation_hash_stmt FROM @ddl; EXECUTE transfer_allocation_hash_stmt; DEALLOCATE PREPARE transfer_allocation_hash_stmt;
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='outbound_idempotency_key'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN outbound_idempotency_key VARCHAR(255) NULL COMMENT ''出库幂等键'' AFTER allocation_request_hash'));
|
||||
PREPARE transfer_outbound_key_stmt FROM @ddl; EXECUTE transfer_outbound_key_stmt; DEALLOCATE PREPARE transfer_outbound_key_stmt;
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='outbound_request_hash'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN outbound_request_hash CHAR(64) NULL COMMENT ''出库请求哈希'' AFTER outbound_idempotency_key'));
|
||||
PREPARE transfer_outbound_hash_stmt FROM @ddl; EXECUTE transfer_outbound_hash_stmt; DEALLOCATE PREPARE transfer_outbound_hash_stmt;
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='outbound_at'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN outbound_at DATETIME(3) NULL COMMENT ''出库时间'' AFTER outbound_request_hash'));
|
||||
PREPARE transfer_outbound_at_stmt FROM @ddl; EXECUTE transfer_outbound_at_stmt; DEALLOCATE PREPARE transfer_outbound_at_stmt;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ll_warehouse_transfer_source_order (
|
||||
id BIGINT NOT NULL COMMENT '主键ID', transfer_order_id BIGINT NOT NULL COMMENT '转仓单ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID',
|
||||
source_lastleg_order_id BIGINT NOT NULL COMMENT '来源尾程订单ID', source_lastleg_order_no VARCHAR(32) NOT NULL COMMENT '来源尾程订单号',
|
||||
|
||||
Reference in New Issue
Block a user