修改提交

This commit is contained in:
2026-09-08 20:25:05 +08:00
parent 2a27e28f26
commit 249bf0ab4b
10 changed files with 134 additions and 22 deletions
@@ -126,7 +126,7 @@ public class RelabelApplicationService implements WalletResultHandler {
|| !Objects.equals(balance.getInboundOrderId(), item.sourceInboundOrderId())) {
throw conflict("Selected inventory does not belong to this customer, operation warehouse and inbound order");
}
if (value(position.getAvailablePackages()) < item.totalPackageCount()) {
if (value(position.getAvailablePackages()) < item.notifiedPackageCount()) {
throw conflict("Available inventory changed for inbound " + balance.getInboundOrderNo());
}
sources.add(new SourceInventory(item, balance));
@@ -137,7 +137,7 @@ public class RelabelApplicationService implements WalletResultHandler {
String relabelNo = orderNoGenerator.next("RELABEL", operationWarehouseId, applicationId);
for (SourceInventory source : sources) {
inventoryCommands.hold(customerId, operatorId, new InventoryCommandRequests.Move(
source.request().sourceInventoryPositionId(), source.request().totalPackageCount(), relabelNo,
source.request().sourceInventoryPositionId(), source.request().notifiedPackageCount(), relabelNo,
null, "RELABEL:" + relabelNo, request.idempotencyKey().trim() + ":HOLD:"
+ source.request().sourceInventoryPositionId(), "贴标申请冻结原入仓单库存"));
}
@@ -398,9 +398,9 @@ public class RelabelApplicationService implements WalletResultHandler {
private void releaseInventory(String customerId, String operatorId, RelabelApplication application) {
for (RelabelItem item : itemList(application.getId())) {
if (item.getSourceInventoryPositionId() == null || item.getTotalPackageCount() == null) continue;
if (item.getSourceInventoryPositionId() == null || item.getNotifiedPackageCount() == null) continue;
inventoryCommands.release(customerId, operatorId, new InventoryCommandRequests.Move(
item.getSourceInventoryPositionId(), item.getTotalPackageCount(), application.getRelabelNo(), null,
item.getSourceInventoryPositionId(), item.getNotifiedPackageCount(), application.getRelabelNo(), null,
"RELABEL:" + application.getRelabelNo(), application.getIdempotencyKey() + ":RELEASE:"
+ item.getSourceInventoryPositionId(), "贴标结束释放库存至原入仓单"));
}
@@ -30,6 +30,12 @@ public class PlatformReturnInventoryController {
return ApiResponse.success(service.platformCartonPage(request));
}
@GetMapping("/movements")
@PreAuthorize("hasAuthority('platform:overseas-warehouse:return-inventory:list')")
public ApiResponse<PageResult<ReturnInventoryResponses.PlatformMovement>> movements(@Valid ReturnInventoryRequests.PlatformQuery request) {
return ApiResponse.success(service.platformMovementPage(request));
}
@GetMapping("/cartons/{cartonMark}")
@PreAuthorize("hasAuthority('platform:overseas-warehouse:return-cartons:list')")
public ApiResponse<ReturnInventoryResponses.CartonDetail> carton(@PathVariable String cartonMark) {
@@ -11,8 +11,8 @@ public final class ReturnInventoryResponses {
String productNameZh, long quantity, int cartonSkuTypes, long cartonTotalQuantity,
LocalDateTime receivedAt) { }
public record SummaryItem(String overseasWarehouseId, String skuCode, String productNameZh,
long availableQuantity, long cartonQuantity, LocalDateTime updatedAt) { }
public record CartonDetail(String cartonMark, String overseasWarehouseId, String sourceBatchNo, String sourceReturnNo, String status,
long totalQuantity, long availableQuantity, long cartonQuantity, LocalDateTime updatedAt) { }
public record CartonDetail(String cartonMark, String customerId, String overseasWarehouseId, String sourceBatchNo, String sourceReturnNo, String status,
int skuTypes, long totalQuantity, BigDecimal length, BigDecimal width, BigDecimal height,
BigDecimal weight, LocalDateTime receivedAt, List<CartonItem> items) { }
public record CartonItem(String skuCode, String productNameZh, long quantity) { }
@@ -24,9 +24,12 @@ public final class ReturnInventoryResponses {
public record PackingSku(String skuCode, String productNameZh, long availableQuantity) { }
public record PackingPreview(List<PackingOrder> orders, String customerId, List<PackingSku> skus) { }
public record PlatformStock(String customerId, String overseasWarehouseId, String skuCode, String productNameZh,
long availableQuantity, long cartonQuantity, LocalDateTime updatedAt) { }
long totalQuantity, long availableQuantity, long cartonQuantity, LocalDateTime updatedAt) { }
public record PlatformCarton(String customerId, String overseasWarehouseId, String cartonMark, String sourceReturnNo,
String sourceBatchNo, String status, int skuTypes, long totalQuantity,
BigDecimal length, BigDecimal width, BigDecimal height, BigDecimal weight,
LocalDateTime createdAt) { }
public record PlatformMovement(String customerId, String overseasWarehouseId, String skuCode, String productNameZh,
String movementType, String referenceNo, long totalDelta, long availableDelta,
LocalDateTime occurredAt) { }
}
@@ -16,6 +16,8 @@ public class ReturnInventoryBalance {
private String overseasWarehouseId;
private String skuCode;
private String productNameZh;
/** 实物总库存;只有实际出库才会减少。 */
private Long totalQuantity;
private Long availableQuantity;
private Long cartonQuantity;
private LocalDateTime createdAt;
@@ -11,13 +11,14 @@ import java.util.List;
@Mapper
public interface ReturnInventoryBalanceMapper extends BaseMapper<ReturnInventoryBalance> {
@Insert("INSERT INTO ll_return_inventory_balance (id,customer_id,overseas_warehouse_id,sku_code,product_name_zh,available_quantity,carton_quantity,created_at,updated_at) " +
"VALUES (#{id},#{customerId},#{warehouseId},#{skuCode},#{productNameZh},#{quantity},#{cartons},NOW(3),NOW(3)) " +
"ON DUPLICATE KEY UPDATE available_quantity=available_quantity+VALUES(available_quantity),carton_quantity=carton_quantity+VALUES(carton_quantity), " +
@Insert("INSERT INTO ll_return_inventory_balance (id,customer_id,overseas_warehouse_id,sku_code,product_name_zh,total_quantity,available_quantity,carton_quantity,created_at,updated_at) " +
"VALUES (#{id},#{customerId},#{warehouseId},#{skuCode},#{productNameZh},#{totalQuantity},#{availableQuantity},#{cartons},NOW(3),NOW(3)) " +
"ON DUPLICATE KEY UPDATE total_quantity=total_quantity+VALUES(total_quantity),available_quantity=available_quantity+VALUES(available_quantity),carton_quantity=carton_quantity+VALUES(carton_quantity), " +
"product_name_zh=COALESCE(NULLIF(VALUES(product_name_zh),''),product_name_zh),updated_at=NOW(3)")
int adjust(@Param("id") long id, @Param("customerId") String customerId, @Param("warehouseId") String warehouseId,
@Param("skuCode") String skuCode, @Param("productNameZh") String productNameZh,
@Param("quantity") long quantity, @Param("cartons") long cartons);
@Param("totalQuantity") long totalQuantity, @Param("availableQuantity") long availableQuantity,
@Param("cartons") long cartons);
@Select("<script>SELECT COUNT(*) FROM ll_return_inventory_balance WHERE customer_id=#{customerId} " +
"<if test='warehouseId != null and warehouseId != \"\"'>AND overseas_warehouse_id=#{warehouseId} </if>" +
@@ -0,0 +1,33 @@
package com.baiyue.lastleg.returnreceipt.mapper;
import com.baiyue.lastleg.returnreceipt.dto.ReturnInventoryResponses;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/** Read-only return inventory movement ledger reconstructed from signed receipts and merged cartons. */
@Mapper
public interface ReturnInventoryMovementMapper {
String MOVEMENTS = "SELECT o.customer_id customerId,o.overseas_warehouse_id overseasWarehouseId,ri.sku_code skuCode,oi.product_name_zh productNameZh," +
"'RECEIPT' movementType,r.receipt_no referenceNo,ri.received_quantity totalDelta,ri.received_quantity availableDelta,r.signed_at occurredAt " +
"FROM ll_return_receipt r JOIN ll_return_order o ON o.id=r.return_order_id JOIN ll_return_receipt_item ri ON ri.receipt_id=r.id " +
"JOIN ll_return_order_item oi ON oi.id=ri.return_order_item_id " +
"UNION ALL SELECT c.customer_id,c.overseas_warehouse_id,i.sku_code,i.product_name_zh,'MERGE',c.carton_mark,0,-i.quantity,c.created_at " +
"FROM ll_return_inventory_carton c JOIN ll_return_inventory_carton_item i ON i.carton_id=c.id WHERE c.source_batch_no='MERGED'";
@Select("<script>SELECT COUNT(*) FROM (" + MOVEMENTS + ") m WHERE 1=1 " +
"<if test='customerId != null and customerId != \"\"'>AND m.customerId=#{customerId} </if>" +
"<if test='warehouseId != null and warehouseId != \"\"'>AND m.overseasWarehouseId=#{warehouseId} </if>" +
"<if test='skuCode != null and skuCode != \"\"'>AND m.skuCode LIKE CONCAT('%',#{skuCode},'%') </if></script>")
long platformCount(@Param("customerId") String customerId, @Param("warehouseId") String warehouseId, @Param("skuCode") String skuCode);
@Select("<script>SELECT * FROM (" + MOVEMENTS + ") m WHERE 1=1 " +
"<if test='customerId != null and customerId != \"\"'>AND m.customerId=#{customerId} </if>" +
"<if test='warehouseId != null and warehouseId != \"\"'>AND m.overseasWarehouseId=#{warehouseId} </if>" +
"<if test='skuCode != null and skuCode != \"\"'>AND m.skuCode LIKE CONCAT('%',#{skuCode},'%') </if>" +
"ORDER BY occurredAt DESC LIMIT #{offset},#{size}</script>")
List<ReturnInventoryResponses.PlatformMovement> platformPage(@Param("customerId") String customerId, @Param("warehouseId") String warehouseId,
@Param("skuCode") String skuCode, @Param("offset") long offset, @Param("size") long size);
}
@@ -15,6 +15,7 @@ import com.baiyue.lastleg.returnreceipt.mapper.ReturnInventoryCartonItemMapper;
import com.baiyue.lastleg.returnreceipt.mapper.ReturnInventoryCartonMapper;
import com.baiyue.lastleg.returnreceipt.mapper.ReturnInventoryOperationMapper;
import com.baiyue.lastleg.returnreceipt.mapper.ReturnInventoryQueryMapper;
import com.baiyue.lastleg.returnreceipt.mapper.ReturnInventoryMovementMapper;
import com.baiyue.lastleg.returnreceipt.model.ReturnInventoryRow;
import com.baiyue.lastleg.service.LastlegOrderNoGenerator;
import jakarta.annotation.Resource;
@@ -47,6 +48,7 @@ public class ReturnInventoryService {
@Resource private ReturnInventoryBalanceMapper balances;
@Resource private ReturnInventoryOperationMapper operations;
@Resource private ReturnInventoryQueryMapper queries;
@Resource private ReturnInventoryMovementMapper movements;
@Resource private LastlegOrderNoGenerator numbers;
@Resource private com.baiyue.lastleg.returnreceipt.mapper.ReturnOrderMapper orders;
@Resource private com.baiyue.lastleg.returnreceipt.mapper.ReturnReceiptMapper receiptMapper;
@@ -72,7 +74,7 @@ public class ReturnInventoryService {
item.setSkuCode(line.skuCode()); item.setProductNameZh(text(line.productNameZh())); item.setQuantity(line.quantity());
cartonItems.insert(item);
balances.adjust(IdWorker.getId(), order.getCustomerId(), order.getOverseasWarehouseId(), line.skuCode(),
text(line.productNameZh()), line.quantity(), 1);
text(line.productNameZh()), line.quantity(), line.quantity(), 1);
}
return carton;
}
@@ -105,7 +107,16 @@ public class ReturnInventoryService {
List<ReturnInventoryResponses.PlatformStock> records = total == 0 ? List.of() : balances.platformPage(customerId, warehouseId, sku,
(request.page() - 1) * request.size(), request.size()).stream().map(balance -> new ReturnInventoryResponses.PlatformStock(
balance.getCustomerId(), balance.getOverseasWarehouseId(), balance.getSkuCode(), balance.getProductNameZh(),
value(balance.getAvailableQuantity()), value(balance.getCartonQuantity()), balance.getUpdatedAt())).toList();
value(balance.getTotalQuantity()), value(balance.getAvailableQuantity()), value(balance.getCartonQuantity()), balance.getUpdatedAt())).toList();
return new PageResult<>(records, request.page(), request.size(), total, (total + request.size() - 1) / request.size());
}
@Transactional(readOnly = true)
public PageResult<ReturnInventoryResponses.PlatformMovement> platformMovementPage(ReturnInventoryRequests.PlatformQuery request) {
String customerId = text(request.customerId()); String warehouseId = text(request.overseasWarehouseId()); String sku = text(request.skuCode());
long total = movements.platformCount(customerId, warehouseId, sku);
List<ReturnInventoryResponses.PlatformMovement> records = total == 0 ? List.of() : movements.platformPage(customerId, warehouseId, sku,
(request.page() - 1) * request.size(), request.size());
return new PageResult<>(records, request.page(), request.size(), total, (total + request.size() - 1) / request.size());
}
@@ -251,7 +262,7 @@ public class ReturnInventoryService {
targetItem.setId(IdWorker.getId()); targetItem.setCartonId(targetId); targetItem.setCustomerId(customerId); targetItem.setSkuCode(entry.getKey());
targetItem.setProductNameZh(productNames.get(entry.getKey())); targetItem.setQuantity(entry.getValue()); cartonItems.insert(targetItem);
balances.adjust(IdWorker.getId(), customerId, target.getOverseasWarehouseId(), entry.getKey(), productNames.get(entry.getKey()), 0,
1L - emptiedBySku.getOrDefault(entry.getKey(), 0));
-entry.getValue(), 1L - emptiedBySku.getOrDefault(entry.getKey(), 0));
}
operation.setResultCartonId(targetId); operation.setUpdatedAt(now); operations.updateById(operation);
return new ReturnInventoryResponses.MergeResult(target.getCartonMark(), requested.keySet().iterator().next(), total, label(target));
@@ -281,13 +292,13 @@ public class ReturnInventoryService {
private ReturnInventoryResponses.SummaryItem summary(com.baiyue.lastleg.returnreceipt.entity.ReturnInventoryBalance balance) {
return new ReturnInventoryResponses.SummaryItem(balance.getOverseasWarehouseId(), balance.getSkuCode(), balance.getProductNameZh(),
value(balance.getAvailableQuantity()), value(balance.getCartonQuantity()), balance.getUpdatedAt());
value(balance.getTotalQuantity()), value(balance.getAvailableQuantity()), value(balance.getCartonQuantity()), balance.getUpdatedAt());
}
private ReturnInventoryResponses.CartonDetail cartonDetail(ReturnInventoryCarton carton) {
List<ReturnInventoryResponses.CartonItem> items = cartonItems.listCarton(carton.getId()).stream()
.map(item -> new ReturnInventoryResponses.CartonItem(item.getSkuCode(), item.getProductNameZh(), item.getQuantity())).toList();
return new ReturnInventoryResponses.CartonDetail(carton.getCartonMark(), carton.getOverseasWarehouseId(), carton.getSourceBatchNo(), carton.getSourceReturnNo(),
return new ReturnInventoryResponses.CartonDetail(carton.getCartonMark(), carton.getCustomerId(), carton.getOverseasWarehouseId(), carton.getSourceBatchNo(), carton.getSourceReturnNo(),
carton.getStatus(), carton.getSkuTypes() == null ? 0 : carton.getSkuTypes(), value(carton.getTotalQuantity()), carton.getLength(), carton.getWidth(), carton.getHeight(), carton.getWeight(), carton.getCreatedAt(), items);
}