Commit 63dd6d31 by yubin

导入修改

parent ff9d771e
......@@ -683,6 +683,7 @@ export default {
this.getList()
})
this.handleShip = this.debounce(this.handleShip, 500)
},
methods: {
debounce(fn, delay = 300) {
......
......@@ -719,4 +719,5 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
}
return sb.toString();
}
}
\ No newline at end of file
......@@ -117,5 +117,7 @@ public class OutboundOrderItems extends BaseEntity
@Excel(name = "排序号")
private String updateUserCode;
private String InventoryType;
}
\ No newline at end of file
......@@ -116,4 +116,6 @@ public interface InventoryMapper
public List<java.util.Map<String, String>> selectInventoryTopTenByAmount();
public List<java.util.Map<String, String>> selectInventoryTopTenByQuantity();
public void batchUpdateInventory(List<Inventory> inventoryList);
}
......@@ -4,6 +4,7 @@ import java.util.*;
import java.util.stream.Collectors;
import com.ruoyi.common.annotation.SerialExecution;
import com.ruoyi.common.config.WarehouseConfig;
import com.ruoyi.common.core.domain.entity.Materials;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
......@@ -28,6 +29,10 @@ import org.springframework.util.CollectionUtils;
/**
* 出库单主Service业务层处理
* 核心修正:
* 1. 库存匹配Key统一为 仓库ID_物料ID_库位ID_库存类型
* 2. 确保inventoryType完整参与匹配
* 3. 库存扣减后≤0时强制设置inventory_status=0
*
* @author ruoyi
* @date 2025-12-03
......@@ -54,6 +59,10 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
private MaterialsServiceImpl materialsService;
@Autowired
private StorageLocationsServiceImpl storageLocationsService;
@Autowired
private InventoryMapper inventoryMapper;
/**
* 查询出库单主
*
......@@ -63,7 +72,6 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
@Override
public OutboundOrders selectOutboundOrdersById(String id)
{
return outboundOrdersMapper.selectOutboundOrdersById(id);
}
......@@ -112,7 +120,6 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
outboundOrdersMapper.deleteOutboundOrderItemsByOrderId(outboundOrders.getId());
outboundOrderLogMapper.deleteOutboundOrderLogByOrdersId(outboundOrders.getId());
outboundOrders.setUpdateUserCode(SystemUtils.getUserName());
outboundOrders.setUpdateTime(DateUtils.getNowDate());
insertOutboundOrderItems(outboundOrders);
......@@ -147,42 +154,161 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
outboundOrdersMapper.deleteOutboundOrderItemsByOrderId(id);
return outboundOrdersMapper.deleteOutboundOrdersById(id);
}
@SerialExecution(group = "inventoryRefresh", fair = true)
@Override
public int ship(OutboundOrders outboundOrders) {
OutboundOrderItems outboundOrderItems1 = new OutboundOrderItems();
outboundOrderItems1.setOutboundOrderId(outboundOrders.getId());
outboundOrderItems1.setDivisor(null);
List<OutboundOrderItems> outboundOrderItems = outboundOrderItemsMapper.selectOutboundOrderItemsList(outboundOrderItems1);
List<OutboundOrderItems> outboundOrderItems2 = outboundOrderItems;
// 1. 查询当前出库单的所有明细
OutboundOrderItems query = new OutboundOrderItems();
query.setOutboundOrderId(outboundOrders.getId());
query.setDivisor(null);
List<OutboundOrderItems> outboundOrderItems = outboundOrderItemsMapper.selectOutboundOrderItemsList(query);
// 2. 更新明细和订单状态
OutboundOrderLog outboundOrderLog = new OutboundOrderLog();
String updateUser = SystemUtils.getUserName();
Date updateTime = DateUtils.getNowDate();
for (OutboundOrderItems item : outboundOrderItems) {
item.setItemStatus(3L);
item.setUpdateBy(updateUser);
item.setUpdateTime(updateTime);
outboundOrderItemsMapper.updateOutboundOrderItems(item);
for (OutboundOrderItems outboundOrderItem : outboundOrderItems) {
outboundOrderLog.setId(item.getId());
outboundOrderLog.setItemStatus(item.getItemStatus());
outboundOrderLogMapper.updateOutboundOrderLog(outboundOrderLog);
}
outboundOrderItem.setItemStatus(3l);
outboundOrderItemsMapper.updateOutboundOrderItems(outboundOrderItem);
outboundOrders.setOrderStatus(2L);
outboundOrders.setUpdateTime(updateTime);
outboundOrders.setUpdateUserCode(updateUser);
outboundOrdersMapper.updateOutboundOrders(outboundOrders);
outboundOrderLog.setId(outboundOrderItem.getId());
outboundOrderLog.setItemStatus(outboundOrderItem.getItemStatus());
outboundOrderLogMapper.updateOutboundOrderLog(outboundOrderLog);
// 3. 执行库存扣减(包含inventoryType匹配 + 0值状态更新)
this.deductInventory(outboundOrderItems, updateUser, updateTime);
return 1;
}
/**
* 核心库存扣减逻辑
* 1. 按 仓库ID_物料ID_库位ID_库存类型 匹配库存
* 2. 扣减后数量≤0时设置inventory_status=0
* @param outboundOrderItems 出库明细
* @param updateUser 操作人
* @param updateTime 操作时间
*/
private void deductInventory(List<OutboundOrderItems> outboundOrderItems, String updateUser, Date updateTime) {
if (CollectionUtils.isEmpty(outboundOrderItems)) {
return;
}
// 1. 预加载库存映射:Key=仓库ID_物料ID_库位ID_库存类型 Value=库存对象
Map<String, Inventory> inventoryMap = this.loadInventoryMap();
// 2. 构建扣减数量Map:Key=仓库ID_物料ID_库位ID_库存类型 Value=总扣减数量
Map<String, Long> deductQtyMap = this.buildDeductQtyMap(outboundOrderItems);
outboundOrders.setId(outboundOrders.getId());
outboundOrders.setOrderStatus(2l);
outboundOrders.setUpdateTime(DateUtils.getNowDate());
outboundOrders.setUpdateUserCode(SystemUtils.getUserName());
outboundOrdersMapper.updateOutboundOrders(outboundOrders);
// 3. 遍历扣减Map,执行扣减+状态更新
List<Inventory> needUpdateList = new ArrayList<>();
for (Map.Entry<String, Long> entry : deductQtyMap.entrySet()) {
String key = entry.getKey();
Long deductQty = entry.getValue();
inventoryService.ship(outboundOrderItems2);
return 1;
// 匹配库存
Inventory inventory = inventoryMap.get(key);
if (inventory == null) {
String[] keyParts = key.split("_");
String warehouseId = keyParts.length > 0 ? keyParts[0] : "";
String materialId = keyParts.length > 1 ? keyParts[1] : "";
String locationId = keyParts.length > 2 ? keyParts[2] : "";
String inventoryType = keyParts.length > 3 ? keyParts[3] : "";
throw new ServiceException(String.format(
"仓库[%s]物料[%s]库位[%s]库存类型[%s]的库存不存在,无法扣减",
warehouseId, materialId, locationId, inventoryType));
}
// 执行数量扣减
Long currentQty = Optional.ofNullable(inventory.getQuantity()).orElse(0L);
Long newQty = currentQty - deductQty;
inventory.setQuantity(newQty);
// 核心规则:扣减后数量≤0 → 状态置0
if (newQty <= 0) {
inventory.setInventoryStatus(0L);
}
// 补充审计字段
inventory.setUpdateBy(updateUser);
inventory.setUpdateTime(updateTime);
needUpdateList.add(inventory);
}
// 4. 批量更新库存
if (!needUpdateList.isEmpty()) {
inventoryMapper.batchUpdateInventory(needUpdateList);
// 刷新库存缓存
List<String> inventoryIds = needUpdateList.stream()
.map(Inventory::getId)
.distinct()
.collect(Collectors.toList());
inventoryService.RefreshInventory(inventoryIds);
}
}
/**
* 预加载库存Map
* Key规则:仓库ID_物料ID_库位ID_库存类型
*/
private Map<String, Inventory> loadInventoryMap() {
Inventory query = new Inventory();
query.setInventoryStatus(1L);
query.setIsUsed(1L);
List<Inventory> inventoryList = inventoryService.selectInventoryList(query);
if (CollectionUtils.isEmpty(inventoryList)) {
return Collections.emptyMap();
}
Map<String, Inventory> inventoryMap = new HashMap<>();
for (Inventory inv : inventoryList) {
String key = String.join("_",
Optional.ofNullable(inv.getWarehousesId()).orElse(""),
Optional.ofNullable(inv.getMaterialId()).orElse(""),
Optional.ofNullable(inv.getLocationId()).orElse(""),
Optional.ofNullable(inv.getInventoryType()).map(String::valueOf).orElse("")
);
// 重复key保留第一个(避免覆盖)
if (!inventoryMap.containsKey(key)) {
inventoryMap.put(key, inv);
}
}
return inventoryMap;
}
/**
* 构建扣减数量Map
* Key规则:仓库ID_物料ID_库位ID_库存类型(和库存Map完全对齐)
*/
private Map<String, Long> buildDeductQtyMap(List<OutboundOrderItems> items) {
Map<String, Long> deductQtyMap = new HashMap<>();
for (OutboundOrderItems item : items) {
String key = String.join("_",
Optional.ofNullable(item.getWarehouseId()).orElse(""),
Optional.ofNullable(item.getMaterialId()).orElse(""),
Optional.ofNullable(item.getLocationId()).orElse(""),
Optional.ofNullable(item.getInventoryType()).map(String::valueOf).orElse("")
);
// 累加扣减数量
Long qty = Optional.ofNullable(item.getActualQuantity()).orElse(0L);
deductQtyMap.put(key, deductQtyMap.getOrDefault(key, 0L) + qty);
}
return deductQtyMap;
}
@Override
public List<Map<String,String>> outboundOrdersTopTenByQuantity() {
List<Map<String,String>> o= outboundOrdersMapper.SelectOutboundOrdersMaterialsTopTenByQuantity();
return o;
return outboundOrdersMapper.SelectOutboundOrdersMaterialsTopTenByQuantity();
}
@Override
......@@ -192,7 +318,6 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
@Override
public String outboundOrdersCount() {
return outboundOrdersMapper.outboundOrdersCount();
}
......@@ -204,48 +329,41 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
public void insertOutboundOrderItems(OutboundOrders outboundOrders) {
List<OutboundOrderItems> outboundOrderItemsList = outboundOrders.getOutboundOrderItemsList();
String id = outboundOrders.getId();
// 1. 先做空列表校验(提前返回,避免无效逻辑)
if (outboundOrderItemsList == null || outboundOrderItemsList.isEmpty()) {
if (CollectionUtils.isEmpty(outboundOrderItemsList)) {
return;
}
// 2. 库存校验:失败时抛异常(核心修正:! 取反 + 异常抛出后代码立即终止)
// 库存校验:失败时抛异常
boolean isValid = inventoryService.inventoryLockValidation(outboundOrderItemsList);
if (!isValid) { // 校验失败(返回false)时抛异常
throw new RuntimeException("库存被修改请重新确认"); // 抛异常后,方法立即停止运行
if (!isValid) {
throw new RuntimeException("库存被修改请重新确认");
}
// 2. 为明细设置订单ID和主键ID
// 为明细设置订单ID和主键ID
for (OutboundOrderItems items : outboundOrderItemsList) {
items.setOutboundOrderId(id);
items.setOrderId(outboundOrders.getOrderId());
// 生成无横线的UUID作为主键
items.setId(UUID.randomUUID().toString().replace("-", ""));
}
// 3. 批量插入出库单明细
// 批量插入出库单明细
outboundOrdersMapper.batchOutboundOrderItems(outboundOrderItemsList);
// 4. 正确拷贝明细列表到日志列表(修复核心错误:遍历逐个拷贝)
// 拷贝明细到日志列表
List<String> inventoryIds = new ArrayList<>();
List<OutboundOrderLog> outboundOrderLogs = new ArrayList<>();
for (OutboundOrderItems items : outboundOrderItemsList) {
OutboundOrderLog log = new OutboundOrderLog();
BeanUtils.copyProperties(items, log); // 单个对象属性拷贝
BeanUtils.copyProperties(items, log);
log.setOrderId(items.getOutboundOrderId());
outboundOrderLogs.add(log);
inventoryIds.add(log.getInventoryId());
deleteOutboundOrdersById(items.getId());
}
// 5. 非空校验后插入日志(避免空列表触发SQL语法错误)
// 插入日志 + 刷新库存
if (!outboundOrderLogs.isEmpty()) {
outboundOrderLogMapper.batchOutboundOrderLog(outboundOrderLogs);
}
// 7. 非空校验后刷新库存
if (!inventoryIds.isEmpty()) {
inventoryService.RefreshInventory(inventoryIds);
}
......@@ -254,12 +372,12 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
@Transactional(rollbackFor = Exception.class)
@Override
public String importOutboundOrders(List<OutboundTemplateVO> inboundOrdersList, Boolean isUpdateSupport, String operName, Integer orderType) {
// 1. 基础空值校验(保留原有逻辑)
// 1. 基础空值校验
if (CollectionUtils.isEmpty(inboundOrdersList)) {
throw new ServiceException("导入数据不能为空!");
}
// 2. 初始化变量(保留原有逻辑)
// 2. 初始化变量
int totalMainSuccess = 0;
int totalMainFailure = 0;
int totalItemSuccess = 0;
......@@ -274,18 +392,19 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
Map<String, List<OutboundOrderItems>> validItemMap = new HashMap<>();
boolean hasValidateError = false;
// ========== 新增:预加载映射缓存(核心优化) ==========
Map<String, String> sapToMaterialIdMap = loadSapToMaterialIdMap(); // 物料SAP->ID
Map<String, String> locationNameToIdMap = loadLocationNameToIdMap(); // 库位名称->ID
Map<String, String> warehouseNameToIdMap = loadWarehouseNameToIdMap(); // 仓库名称->ID
Map<String, String> ownerNameToIdMap = loadOwnerNameToIdMap(); // 货主名称->ID
// 3. 预加载映射缓存
Map<String, String> sapToMaterialIdMap = loadSapToMaterialIdMap();
Map<String, String> locationNameToIdMap = loadLocationNameToIdMap();
Map<String, String> warehouseNameToIdMap = loadWarehouseNameToIdMap();
Map<String, String> ownerNameToIdMap = loadOwnerNameToIdMap();
Map<String, AbstractMap.SimpleEntry<String, Long>> inventoryTOIdMap = loadInventoryTOIdMap();
// 3. 按入库单号分组(保留原有逻辑)
// 4. 按入库单号分组
Map<String, List<OutboundTemplateVO>> orderGroupMap = inboundOrdersList.stream()
.filter(vo -> StringUtils.isNotBlank(vo.getOrderId()))
.collect(Collectors.groupingBy(OutboundTemplateVO::getOrderId));
// 4. 第一步:仅验证所有数据(替换为预加载映射校验)
// 5. 数据验证
for (Map.Entry<String, List<OutboundTemplateVO>> entry : orderGroupMap.entrySet()) {
String orderId = entry.getKey();
List<OutboundTemplateVO> voList = entry.getValue();
......@@ -294,8 +413,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
try {
OutboundTemplateVO firstVO = voList.get(0);
// 检查出库单是否已存在(保留原有逻辑)
// 检查出库单是否已存在
OutboundOrders outboundOrdersQuery = new OutboundOrders();
outboundOrdersQuery.setOrderId(orderId);
List<OutboundOrders> existMains = outboundOrdersMapper.selectOutboundOrdersList(outboundOrdersQuery);
......@@ -309,38 +427,33 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
if (!Boolean.TRUE.equals(isUpdateSupport)) {
throw new ServiceException(String.format("入库单号【%s】已存在,且不支持更新", orderId));
}
// 复用已有主表ID(保留原有逻辑)
mainDO = existMain;
BeanUtils.copyProperties(firstVO, mainDO, "id", "createBy", "createTime");
mainDO.setUpdateBy(operId);
mainDO.setWarehouseId("572ba484-199c-45d9-9735-610928ed5c70");
mainDO.setWarehouseId(WarehouseConfig.DEFAULT_WAREHOUSE_ID);
mainDO.setUpdateTime(now);
mainDO.setUpdateUserCode(operId);
} else {
// 新增主表(保留原有结构,替换货主校验为预加载映射)
mainDO = new OutboundOrders();
BeanUtils.copyProperties(firstVO, mainDO,
"sapNo", "materialName", "plannedQuantity", "actualQuantity",
"plannedPackages", "materialUnit", "materialRemark");
// ========== 货主校验(替换为预加载映射) ==========
// 货主校验
String ownerName = firstVO.getOwnerName();
String ownerId = firstVO.getOwnerId();
if (StringUtils.isNotBlank(ownerName)) {
// 从预加载映射获取货主ID,无需重复查询DB
String mappedOwnerId = ownerNameToIdMap.get(ownerName.trim());
if (StringUtils.isBlank(mappedOwnerId)) {
throw new ServiceException(String.format("业主【%s】不存在,无法新增入库单【%s】",
ownerName, orderId));
}
// 货主ID匹配校验
if (ownerId != null && !ownerId.equals(mappedOwnerId)) {
throw new ServiceException(String.format("入库单号【%s】的业主ID【%s】与业主名称【%s】不匹配",
orderId, ownerId, ownerName));
}
mainDO.setOwnerId(mappedOwnerId);
} else if (ownerId != null) {
// 仅货主ID校验(保留原有逻辑)
Owners ownerById = ownersService.selectOwnersById(ownerId);
if (ownerById == null) {
throw new ServiceException(String.format("入库单号【%s】的业主ID【%s】不存在",
......@@ -351,7 +464,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
throw new ServiceException(String.format("入库单号【%s】的业主名称/ID不能为空", orderId));
}
// 填充主表必填字段(保留原有逻辑)
// 填充主表必填字段
mainDO.setId(UUID.randomUUID().toString());
mainDO.setOrderId(orderId);
mainDO.setOrderStatus(2L);
......@@ -365,7 +478,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
mainDO.setSortNo(Optional.ofNullable(mainDO.getSortNo()).orElse(0L));
}
// ========== 明细校验(替换为预加载映射) ==========
// 明细校验
for (int i = 0; i < voList.size(); i++) {
OutboundTemplateVO vo = voList.get(i);
int lineNo = i + 1;
......@@ -374,7 +487,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
BeanUtils.copyProperties(vo, itemDO,
"orderId", "systemNo", "orderTypeId", "batchId");
// 填充明细必填字段(保留原有逻辑)
// 填充明细必填字段
itemDO.setId(UUID.randomUUID().toString());
itemDO.setOrderId(orderId);
itemDO.setBatchCode(Optional.ofNullable(mainDO.getBatchCode()).orElse(""));
......@@ -384,13 +497,12 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
itemDO.setCreateUserCode(operId);
itemDO.setSortNo(0L);
// ========== 物料SAP校验(替换为预加载映射) ==========
// 物料SAP校验
String sapNo = vo.getSapNo() != null ? vo.getSapNo().trim() : "";
if (StringUtils.isBlank(sapNo)) {
throw new ServiceException(String.format("入库单号【%s】第%d条明细的物料SAP号不能为空",
orderId, lineNo));
}
// 从预加载映射获取物料ID,无需重复查询DB
String materialId = sapToMaterialIdMap.get(sapNo);
if (StringUtils.isBlank(materialId)) {
throw new ServiceException(String.format("入库单号【%s】第%d条明细的SAP号【%s】对应的物料不存在",
......@@ -398,13 +510,12 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
itemDO.setMaterialId(materialId);
// ========== 库位校验(替换为预加载映射) ==========
// 库位校验
String locationName = vo.getLocationName() != null ? vo.getLocationName().trim() : "";
if (StringUtils.isBlank(locationName)) {
throw new ServiceException(String.format("入库单号【%s】第%d条明细的库位名称不能为空",
orderId, lineNo));
}
// 从预加载映射获取库位ID,无需重复查询DB
String locationId = locationNameToIdMap.get(locationName);
if (StringUtils.isBlank(locationId)) {
throw new ServiceException(String.format("入库单号【%s】第%d条明细的库位【%s】不存在",
......@@ -412,7 +523,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
itemDO.setLocationId(locationId);
// ========== 仓库校验(替换为预加载映射) ==========
// 仓库校验
String warehouseName = vo.getWarehouseName() != null ? vo.getWarehouseName().trim() : "";
String warehouseId = warehouseNameToIdMap.get(warehouseName);
if (StringUtils.isBlank(warehouseId)) {
......@@ -421,20 +532,17 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
itemDO.setWarehouseId(warehouseId);
// ========== 库存校验(保留原有逻辑,使用预加载的ID) ==========
Inventory itemsList = new Inventory();
itemsList.setWarehousesId(itemDO.getWarehouseId());
itemsList.setMaterialId(itemDO.getMaterialId());
itemsList.setLocationId(itemDO.getLocationId());
itemsList.setInventoryType(Long.valueOf(orderType));
List<Inventory> inventory = inventoryService.selectInventoryList(itemsList);
if (CollectionUtils.isEmpty(inventory)) {
// 库存校验(包含inventoryType)
String inventoryTypeStr = Optional.ofNullable(orderType).map(String::valueOf).orElse("");
String inventoryMatchKey = String.join("_", warehouseId, materialId, locationId, inventoryTypeStr);
AbstractMap.SimpleEntry<String, Long> inventoryEntry = inventoryTOIdMap.get(inventoryMatchKey);
if (inventoryEntry == null) {
throw new ServiceException(String.format(
"入库单号【%s】第%d条明细:仓库【%s】+物料【%s】+库位【%s】组合的库存记录不存在",
orderId, lineNo, vo.getWarehouseName(), vo.getMaterialName(), vo.getLocationName()));
"入库单号【%s】第%d条明细:仓库【%s】+物料【%s】+库位【%s】+库存类型【%s】组合的库存记录不存在",
orderId, lineNo, vo.getWarehouseName(), vo.getMaterialName(), vo.getLocationName(), inventoryTypeStr));
}
itemDO.setInventoryId(inventory.get(0).getId());
itemDO.setInventoryId(inventoryEntry.getKey());
itemDO.setInventoryType(inventoryTypeStr); // 填充库存类型
itemDO.setItemStatus(3L);
itemDOList.add(itemDO);
}
......@@ -450,26 +558,21 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
}
// 5. 有验证失败直接抛异常(保留原有逻辑)
// 6. 有验证失败直接抛异常
if (hasValidateError) {
throw new ServiceException(String.format("验证失败,导入终止!失败详情:%s", failureMsg.toString()));
}
// 6. 验证全通过:统一执行入库/更新操作(保留原有逻辑)
// 7. 执行入库/更新操作
for (Map.Entry<String, OutboundOrders> entry : validMainMap.entrySet()) {
String orderId = entry.getKey();
OutboundOrders mainDO = entry.getValue();
List<OutboundOrderItems> itemDOList = validItemMap.get(orderId);
if (mainDO != null) {
outboundOrdersMapper.insertOutboundOrders(mainDO);
totalMainSuccess++;
successMsg.append(String.format("入库单号【%s】已新增;\n", orderId));
} else {
outboundOrdersMapper.updateOutboundOrders(mainDO);
totalMainSuccess++;
successMsg.append(String.format("入库单号【%s】已更新;\n", orderId));
}
if (!CollectionUtils.isEmpty(itemDOList)) {
int itemSuccess = outboundOrderItemsMapper.batchInsertOutboundOrderItems(itemDOList);
......@@ -484,9 +587,8 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
}
// 事务提交后执行自定义逻辑(保留原有逻辑)
// 8. 事务提交后执行库存扣减
if (TransactionSynchronizationManager.isSynchronizationActive()) {
Map<String, OutboundOrders> finalValidMainMap = new HashMap<>(validMainMap);
Map<String, List<OutboundOrderItems>> finalValidItemMap = new HashMap<>(validItemMap);
String finalOperId = operId;
Date finalNow = now;
......@@ -494,14 +596,18 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
executeCustomLogic(finalValidMainMap, finalValidItemMap, finalOperId, finalNow);
for (List<OutboundOrderItems> itemList : finalValidItemMap.values()) {
deductInventory(itemList, finalOperId, finalNow);
}
}
});
} else {
executeCustomLogic(validMainMap, validItemMap, operId, now);
for (List<OutboundOrderItems> itemList : validItemMap.values()) {
deductInventory(itemList, operId, now);
}
}
// 8. 结果汇总(保留原有逻辑)
// 9. 结果汇总
if (totalMainFailure > 0 || totalItemFailure > 0) {
String finalFailureMsg = String.format(
"导入结果:成功新增/更新%d个入库单,失败%d个;成功导入%d条明细,失败%d条。失败详情:%s",
......@@ -517,10 +623,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
}
// ========== 新增:预加载映射辅助方法(核心) ==========
/**
* 预加载物料SAP号 -> 物料ID映射
*/
// ========== 预加载映射辅助方法 ==========
private Map<String, String> loadSapToMaterialIdMap() {
List<Materials> materialsList = materialsService.selectMaterialsList(new Materials());
if (CollectionUtils.isEmpty(materialsList)) {
......@@ -529,18 +632,15 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
return materialsList.stream()
.filter(m -> StringUtils.isNotBlank(m.getSapNo()))
.collect(Collectors.toMap(
m -> m.getSapNo().trim(), // key:SAP号(去空格)
Materials::getId, // value:物料ID
(k1, k2) -> k1 // 重复SAP号保留第一个
m -> m.getSapNo().trim(),
Materials::getId,
(k1, k2) -> k1
));
}
/**
* 预加载库位名称(locationCode) -> 库位ID映射
*/
private Map<String, String> loadLocationNameToIdMap() {
StorageLocations query = new StorageLocations();
query.setIsUsed(1L); // 只查可用库位
query.setIsUsed(1L);
List<StorageLocations> locationList = storageLocationsService.selectStorageLocationsList(query);
if (CollectionUtils.isEmpty(locationList)) {
return Collections.emptyMap();
......@@ -548,18 +648,15 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
return locationList.stream()
.filter(l -> StringUtils.isNotBlank(l.getLocationCode()))
.collect(Collectors.toMap(
l -> l.getLocationCode().trim(), // key:库位名称(去空格)
StorageLocations::getId, // value:库位ID
(k1, k2) -> k1 // 重复库位名称保留第一个
l -> l.getLocationCode().trim(),
StorageLocations::getId,
(k1, k2) -> k1
));
}
/**
* 预加载仓库名称 -> 仓库ID映射
*/
private Map<String, String> loadWarehouseNameToIdMap() {
Warehouses query = new Warehouses();
query.setIsUsed(1L); // 只查可用仓库
query.setIsUsed(1L);
List<Warehouses> warehouseList = warehousesService.selectWarehousesList(query);
if (CollectionUtils.isEmpty(warehouseList)) {
return Collections.emptyMap();
......@@ -567,15 +664,12 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
return warehouseList.stream()
.filter(w -> StringUtils.isNotBlank(w.getWarehousesName()))
.collect(Collectors.toMap(
w -> w.getWarehousesName().trim(), // key:仓库名称(去空格)
Warehouses::getId, // value:仓库ID
(k1, k2) -> k1 // 重复仓库名称保留第一个
w -> w.getWarehousesName().trim(),
Warehouses::getId,
(k1, k2) -> k1
));
}
/**
* 预加载货主名称 -> 货主ID映射
*/
private Map<String, String> loadOwnerNameToIdMap() {
List<Owners> ownerList = ownersService.selectOwnersList(new Owners());
if (CollectionUtils.isEmpty(ownerList)) {
......@@ -584,96 +678,56 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
return ownerList.stream()
.filter(o -> StringUtils.isNotBlank(o.getOwnerName()))
.collect(Collectors.toMap(
o -> o.getOwnerName().trim(), // key:货主名称(去空格)
Owners::getId, // value:货主ID
(k1, k2) -> k1 // 重复货主名称保留第一个
o -> o.getOwnerName().trim(),
Owners::getId,
(k1, k2) -> k1
));
}
private Map<String, String> loadInventoryTOIdMap() {
private Map<String, AbstractMap.SimpleEntry<String, Long>> loadInventoryTOIdMap() {
Inventory inventory = new Inventory();
inventory.setInventoryStatus(1l);
inventory.setIsUsed(1l);
inventory.setInventoryStatus(1L);
inventory.setIsUsed(1L);
List<Inventory> inventoryList = inventoryService.selectInventoryList(inventory);
if (CollectionUtils.isEmpty(inventoryList)) {
return Collections.emptyMap();
}
return inventoryList.stream()
.filter(inv -> {
// 过滤有效库存:仓库/物料/库位/库存ID都不能为空
return StringUtils.isNotBlank(inv.getWarehousesId());
})
Map<String, AbstractMap.SimpleEntry<String, Long>> emptyLocationMap = inventoryList.stream()
.filter(inv -> StringUtils.isNotBlank(inv.getWarehousesId())
&& StringUtils.isNotBlank(inv.getMaterialId())
&& inv.getInventoryType() != null
&& StringUtils.isNotBlank(inv.getId())
&& StringUtils.isBlank(inv.getLocationId()))
.collect(Collectors.toMap(
// Key:仓库ID+物料ID+库位ID(拼接成唯一标识)
inv -> String.join("_",
inv.getWarehousesId().trim(),
inv.getMaterialId().trim(),
inv.getLocationId().trim()
),
// Value:库存ID(去空格)
inv -> inv.getId().trim(),
// 重复Key处理:保留第一个(避免主键冲突)
"", inv.getInventoryType().toString()),
inv -> new AbstractMap.SimpleEntry<>(inv.getId().trim(), Optional.ofNullable(inv.getQuantity()).orElse(0L)),
(k1, k2) -> k1,
// 指定Map类型(可选,默认HashMap)
HashMap::new
));
}
// 仅修改这个方法中【设置日志ID】的一行代码,其余完全保留
private void executeCustomLogic(Map<String, OutboundOrders> validMainMap,
Map<String, List<OutboundOrderItems>> validItemMap,
String operId, Date now) {
for (String orderId : validMainMap.keySet()) {
OutboundOrders mainDO = validMainMap.get(orderId);
List<OutboundOrderItems> itemList = validItemMap.get(orderId);
System.out.println(String.format("订单【%s】导入成功,主表ID:%s,明细数:%d",
orderId, mainDO.getId(), itemList.size()));
List<OutboundOrderLog> outboundOrderLogs = new ArrayList<>();
for (OutboundOrderItems item : itemList) {
OutboundOrderLog outboundOrderLog = new OutboundOrderLog();
// ========== 唯一改动:设置日志ID = 明细ID ==========
outboundOrderLog.setId(item.getId());
outboundOrderLog.setOrderId(item.getOutboundOrderId());
BeanUtils.copyProperties(item, outboundOrderLog);
outboundOrderLogs.add(outboundOrderLog);
}
outboundOrderLogMapper.batchOutboundOrderLog(outboundOrderLogs);
ship(itemList);
}
}
public int ship(List<OutboundOrderItems> outboundOrderItems)
{
if (!outboundOrderItems.isEmpty()) {
List<String> inventoryIds = new ArrayList<>(); // 手动收集inventoryId
for (OutboundOrderItems outboundOrderItem : outboundOrderItems) {
// 直接用明细的inventoryId,不查日志!
String inventoryId = outboundOrderItem.getInventoryId();
if (StringUtils.isBlank(inventoryId)) {
throw new ServiceException("明细ID【" + outboundOrderItem.getId() + "】的库存ID为空,无法扣减");
}
Inventory inventory = inventoryService.selectInventoryById(inventoryId);
if (inventory == null) {
throw new ServiceException("库存ID【" + inventoryId + "】不存在,无法扣减");
}
// 扣减库存
inventory.setQuantity(inventory.getQuantity() - outboundOrderItem.getActualQuantity());
if (inventory.getQuantity() == 0) {
inventory.setInventoryStatus(0L);
}
inventoryService.updateInventory(inventory);
Map<String, AbstractMap.SimpleEntry<String, Long>> nonEmptyLocationMap = inventoryList.stream()
.filter(inv -> StringUtils.isNotBlank(inv.getWarehousesId())
&& StringUtils.isNotBlank(inv.getMaterialId())
&& StringUtils.isNotBlank(inv.getLocationId())
&& StringUtils.isNotBlank(inv.getId())
&& inv.getInventoryType() != null)
.collect(Collectors.toMap(
inv -> String.join("_",
inv.getWarehousesId().trim(),
inv.getMaterialId().trim(),
inv.getLocationId().trim(),
inv.getInventoryType().toString()),
inv -> new AbstractMap.SimpleEntry<>(inv.getId().trim(), Optional.ofNullable(inv.getQuantity()).orElse(0L)),
(k1, k2) -> k1,
HashMap::new
));
inventoryIds.add(inventoryId); // 收集库存ID用于刷新
}
inventoryService.RefreshInventory(inventoryIds);
}
return 1;
emptyLocationMap.putAll(nonEmptyLocationMap);
return emptyLocationMap;
}
}
\ No newline at end of file
......@@ -344,4 +344,15 @@
select count(*) from outbound_orders where is_used = 1 and order_status=2 and inbound_date &gt;= DATE_FORMAT(CURDATE(), '%Y-%m-01')
and inbound_date &lt; DATE_FORMAT(DATE_ADD(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01')
</select>
<update id="batchUpdateInventory">
<foreach collection="list" item="item" separator=";">
UPDATE inventory
SET quantity = #{item.quantity},
inventory_status = #{item.inventoryStatus},
update_by = #{item.updateBy},
update_time = #{item.updateTime}
WHERE id = #{item.id}
</foreach>
</update>
</mapper>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论