Commit e135875d by yubin

导入无库存插入-库存 加导入手动标识 导入日志

parent 583b1606
......@@ -109,5 +109,6 @@ public class OutboundOrders extends BaseEntity
/** 出库单明细信息 */
private List<OutboundOrderItems> outboundOrderItemsList;
@Excel(name = "导入标识")
private Long isImport; // 对应数据库字段is_import,驼峰命名转换
}
\ No newline at end of file
package com.ruoyi.inventory.service.impl;
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;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.inventory.utils.InventoryCache;
import com.ruoyi.inventory.domain.*;
import com.ruoyi.inventory.domain.vo.OutboundTemplateVO;
import com.ruoyi.inventory.mapper.InventoryMapper;
import com.ruoyi.inventory.mapper.OutboundOrderItemsMapper;
import com.ruoyi.inventory.mapper.OutboundOrderLogMapper;
import com.ruoyi.inventory.mapper.OutboundOrdersMapper;
import com.ruoyi.inventory.service.IOutboundOrdersService;
import org.apache.commons.lang3.SystemUtils;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.inventory.mapper.OutboundOrdersMapper;
import com.ruoyi.inventory.service.IOutboundOrdersService;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
/**
* 出库单主Service业务层处理
* 核心修正:
* 1. 库存匹配Key统一为 物料ID_库位ID_库存类型(移除仓库维度)
* 2. 确保inventoryType完整参与匹配
* 3. 库存扣减后≤0时强制设置inventory_status=0
* 4. 无库位库存不足时,自动扣减同物料同库存类型的有库位库存
* 5. 导入场景下仅保留新增逻辑,移除更新相关处理
* 6. 移除所有仓库相关逻辑,仅保留物料+库位维度匹配
* 最终修复版:
* 1. 合并维度调整为「物料ID+库存类型+库位ID」,确保库位信息准确
* 2. 扣减时按该维度分组,统一收集记录后合并
* 3. 修复跨场景(无库位→有库位)扣减同一物料+库存类型的合并逻辑
* 4. 新增相同库存维度的明细合并逻辑,插入前合并为一条记录
*
* @author ruoyi
* @date 2025-12-03
*/
@Service
public class OutboundOrdersServiceImpl implements IOutboundOrdersService
{
public class OutboundOrdersServiceImpl implements IOutboundOrdersService {
@Autowired
private OutboundOrdersMapper outboundOrdersMapper;
......@@ -64,6 +63,9 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
@Autowired
private InventoryMapper inventoryMapper;
@Autowired
private SqlSessionTemplate sqlSessionTemplate;
/**
* 查询出库单主
*
......@@ -71,8 +73,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
* @return 出库单主
*/
@Override
public OutboundOrders selectOutboundOrdersById(String id)
{
public OutboundOrders selectOutboundOrdersById(String id) {
return outboundOrdersMapper.selectOutboundOrdersById(id);
}
......@@ -83,8 +84,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
* @return 出库单主
*/
@Override
public List<OutboundOrders> selectOutboundOrdersList(OutboundOrders outboundOrders)
{
public List<OutboundOrders> selectOutboundOrdersList(OutboundOrders outboundOrders) {
List<OutboundOrders> outboundOrders1 = outboundOrdersMapper.selectOutboundOrdersList(outboundOrders);
return outboundOrders1;
}
......@@ -97,11 +97,11 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
*/
@Transactional
@Override
public int insertOutboundOrders(OutboundOrders outboundOrders)
{
public int insertOutboundOrders(OutboundOrders outboundOrders) {
outboundOrders.setCreateTime(DateUtils.getNowDate());
outboundOrders.setCreateUserCode(SystemUtils.getUserName());
outboundOrders.setId(UUID.randomUUID().toString());
outboundOrders.setIsImport(0l);
int rows = outboundOrdersMapper.insertOutboundOrders(outboundOrders);
insertOutboundOrderItems(outboundOrders);
......@@ -116,8 +116,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
*/
@Transactional
@Override
public int updateOutboundOrders(OutboundOrders outboundOrders)
{
public int updateOutboundOrders(OutboundOrders outboundOrders) {
throw new ServiceException("当前系统仅支持新增出库单,不支持修改操作");
}
......@@ -129,8 +128,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
*/
@Transactional
@Override
public int deleteOutboundOrdersByIds(String[] ids)
{
public int deleteOutboundOrdersByIds(String[] ids) {
outboundOrdersMapper.deleteOutboundOrderItemsByOrderIds(ids);
outboundOrderLogMapper.deleteOutboundOrderLogByOrdersIds(ids);
return outboundOrdersMapper.deleteOutboundOrdersByIds(ids);
......@@ -144,8 +142,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
*/
@Transactional
@Override
public int deleteOutboundOrdersById(String id)
{
public int deleteOutboundOrdersById(String id) {
outboundOrdersMapper.deleteOutboundOrderItemsByOrderId(id);
return outboundOrdersMapper.deleteOutboundOrdersById(id);
}
......@@ -186,16 +183,10 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
/**
* 核心库存扣减逻辑
* 1. 有库位:直接扣减指定库位库存
* 2. 无库位:先扣无库位库存,不足则扣同物料同库存类型的有库位库存
* 3. 扣减后数量≤0时设置inventory_status=0
* 4. 无库位扣减有库位时,记录扣减的库位和数量(用于生成明细)
* 5. 移除仓库维度,仅按物料+库位+库存类型匹配
* @param outboundOrderItems 出库明细
* @param updateUser 操作人
* @param updateTime 操作时间
* @return 无库位明细扣减的有库位库存记录 Map<无库位明细ID, List<扣减的库位库存信息Map>>
* 核心库存扣减逻辑(最终修复版)
* 1. 扣减维度:物料ID+库存类型+库位ID(统一维度,跨场景合并)
* 2. 扣减时先按该维度分组,再累计扣减数量
* 3. 收集记录时按该维度统一收集,合并器仅做最终校验
*/
private Map<String, List<Map<String, Object>>> deductInventory(List<OutboundOrderItems> outboundOrderItems, String updateUser, Date updateTime) {
if (CollectionUtils.isEmpty(outboundOrderItems)) {
......@@ -203,10 +194,12 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
Map<String, List<Map<String, Object>>> deductRecordMap = new HashMap<>();
Map<String, List<Inventory>> inventoryGroupMap = this.loadInventoryGroupMap();
// 预加载库存:按「物料ID+库存类型+库位ID」分组(核心调整)
Map<String, Inventory> inventoryFullMap = this.loadInventoryFullMap();
Map<String, Long> deductQtyMap = this.buildDeductQtyMap(outboundOrderItems);
List<Inventory> needUpdateList = new ArrayList<>();
// 库存更新Map(最终去重,保留最终状态)
Map<String, Inventory> toUpdateInventoryMap = new LinkedHashMap<>();
for (Map.Entry<String, Long> entry : deductQtyMap.entrySet()) {
String key = entry.getKey();
......@@ -217,121 +210,83 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
String materialId = keyParts.length > 0 ? keyParts[0] : "";
String locationId = keyParts.length > 1 ? keyParts[1] : "";
String inventoryType = keyParts.length > 2 ? keyParts[2] : "";
String groupKey = String.join("_", materialId, inventoryType);
List<Inventory> inventoryList = inventoryGroupMap.get(groupKey);
if (CollectionUtils.isEmpty(inventoryList)) {
throw new ServiceException(String.format(
"物料[%s]库存类型[%s]无可用库存,无法扣减",
materialId, inventoryType));
}
Long remainDeductQty = totalDeductQty;
List<Map<String, Object>> deductRecords = new ArrayList<>();
String itemId = outboundOrderItems.stream()
.filter(item -> key.equals(buildDeductKey(item)))
.map(OutboundOrderItems::getId)
.findFirst()
.orElse(null);
// 区分无库位/有库位逻辑
if (StringUtils.isBlank(locationId)) {
// 无库位:先扣无库位库存,再扣有库位库存
// 第一步:扣无库位库存
List<Inventory> noLocationInvList = inventoryList.stream()
.filter(inv -> StringUtils.isBlank(inv.getLocationId()))
.collect(Collectors.toList());
for (Inventory inv : noLocationInvList) {
if (remainDeductQty <= 0) break;
Long currentQty = Optional.ofNullable(inv.getQuantity()).orElse(0L);
Long deductQty = Math.min(remainDeductQty, currentQty); // 不扣负数,只扣现有库存
inv.setQuantity(currentQty - deductQty);
if (inv.getQuantity() <= 0) {
inv.setInventoryStatus(0L);
}
inv.setUpdateBy(updateUser);
inv.setUpdateTime(updateTime);
needUpdateList.add(inv);
Long remainDeductQty = totalDeductQty;
// 核心:按「物料ID+库存类型+库位ID」分组扣减(先处理无库位,再处理有库位)
List<Map<String, Object>> tempDeductRecords = new ArrayList<>();
// 记录无库位扣减
deductRecords.add(buildDeductRecord(inv, inventoryType, deductQty));
remainDeductQty -= deductQty;
}
// 步骤1:先扣指定维度的库存(无库位/有库位)
if (StringUtils.isBlank(locationId)) {
// 无库位:先扣「物料+库存类型+空库位」的库存
String noLocKey = buildInventoryKey(materialId, "", inventoryType);
remainDeductQty = deductByInventoryKey(noLocKey, remainDeductQty, updateUser, updateTime, inventoryFullMap, toUpdateInventoryMap, tempDeductRecords);
// 第二步:无库位不足,扣有库位库存
// 无库位不足,扣「物料+库存类型+任意有库位」的库存
if (remainDeductQty > 0) {
List<Inventory> hasLocationInvList = inventoryList.stream()
.filter(inv -> StringUtils.isNotBlank(inv.getLocationId()))
// 筛选该物料+库存类型的所有有库位库存
List<String> hasLocKeys = inventoryFullMap.keySet().stream()
.filter(k -> {
String[] parts = k.split("_");
return parts.length >= 3
&& parts[0].equals(materialId)
&& parts[2].equals(inventoryType)
&& StringUtils.isNotBlank(parts[1]);
})
.collect(Collectors.toList());
for (Inventory inv : hasLocationInvList) {
if (remainDeductQty <= 0) break;
Long currentQty = Optional.ofNullable(inv.getQuantity()).orElse(0L);
Long deductQty = Math.min(remainDeductQty, currentQty); // 不扣负数
inv.setQuantity(currentQty - deductQty);
if (inv.getQuantity() <= 0) {
inv.setInventoryStatus(0L);
}
inv.setUpdateBy(updateUser);
inv.setUpdateTime(updateTime);
needUpdateList.add(inv);
// 记录有库位扣减
deductRecords.add(buildDeductRecord(inv, inventoryType, deductQty));
remainDeductQty -= deductQty;
for (String hasLocKey : hasLocKeys) {
if (remainDeductQty <= 0) break;
remainDeductQty = deductByInventoryKey(hasLocKey, remainDeductQty, updateUser, updateTime, inventoryFullMap, toUpdateInventoryMap, tempDeductRecords);
}
}
// 最后仍有剩余(所有库存耗尽),允许扣最后一个库存为负数
if (remainDeductQty > 0 && !inventoryList.isEmpty()) {
Inventory lastInv = inventoryList.get(inventoryList.size() - 1);
Long deductQty = remainDeductQty;
lastInv.setQuantity(Optional.ofNullable(lastInv.getQuantity()).orElse(0L) - deductQty);
lastInv.setInventoryStatus(0L);
lastInv.setUpdateBy(updateUser);
lastInv.setUpdateTime(updateTime);
needUpdateList.add(lastInv);
deductRecords.add(buildDeductRecord(lastInv, inventoryType, deductQty));
remainDeductQty = 0L;
}
} else {
// 有库位:直接扣指定库位库存
Inventory targetInv = inventoryList.stream()
.filter(inv -> locationId.equals(inv.getLocationId()))
.findFirst()
.orElseThrow(() -> new ServiceException(String.format(
"物料[%s]库位[%s]无库存", materialId, locationId)));
Long currentQty = Optional.ofNullable(targetInv.getQuantity()).orElse(0L);
Long deductQty = Math.min(remainDeductQty, currentQty);
targetInv.setQuantity(currentQty - deductQty);
if (targetInv.getQuantity() <= 0) {
targetInv.setInventoryStatus(0L);
}
targetInv.setUpdateBy(updateUser);
targetInv.setUpdateTime(updateTime);
needUpdateList.add(targetInv);
deductRecords.add(buildDeductRecord(targetInv, inventoryType, deductQty));
remainDeductQty -= deductQty;
// 有库位:扣指定「物料+库存类型+库位ID」的库存
String targetKey = buildInventoryKey(materialId, locationId, inventoryType);
remainDeductQty = deductByInventoryKey(targetKey, remainDeductQty, updateUser, updateTime, inventoryFullMap, toUpdateInventoryMap, tempDeductRecords);
}
// 剩余部分扣为负数
if (remainDeductQty > 0) {
Long finalDeduct = remainDeductQty;
targetInv.setQuantity(targetInv.getQuantity() - finalDeduct);
targetInv.setInventoryStatus(0L);
deductRecords.add(buildDeductRecord(targetInv, inventoryType, finalDeduct));
remainDeductQty = 0L;
// 步骤2:剩余部分扣负数(最后一个库存)
if (remainDeductQty > 0 && !tempDeductRecords.isEmpty()) {
Map<String, Object> lastRecord = tempDeductRecords.get(tempDeductRecords.size() - 1);
String lastInvId = (String) lastRecord.get("inventoryId");
Inventory lastInv = toUpdateInventoryMap.get(lastInvId);
if (lastInv == null) {
throw new ServiceException(String.format("物料[%s]库存类型[%s]扣减负数时未找到目标库存", materialId, inventoryType));
}
// 累计扣减负数数量
Long finalDeduct = remainDeductQty;
lastInv.setQuantity(lastInv.getQuantity() - finalDeduct);
lastInv.setInventoryStatus(0L);
lastInv.setUpdateBy(updateUser);
lastInv.setUpdateTime(updateTime);
toUpdateInventoryMap.put(lastInvId, lastInv);
// 合并到最后一条记录
lastRecord.put("deductQty", (Long) lastRecord.get("deductQty") + finalDeduct);
remainDeductQty = 0L;
}
// 关联明细与扣减记录
if (itemId != null && !deductRecords.isEmpty()) {
deductRecordMap.put(itemId, deductRecords);
// 步骤3:统一合并(按物料+库存类型+库位ID)
if (itemId != null && !tempDeductRecords.isEmpty()) {
List<Map<String, Object>> mergedRecords = mergeDeductRecords(tempDeductRecords);
deductRecordMap.put(itemId, mergedRecords);
}
// 校验是否扣减完成
if (remainDeductQty > 0) {
throw new ServiceException(String.format("物料[%s]库存类型[%s]扣减失败,剩余%d数量未扣减", materialId, inventoryType, remainDeductQty));
}
}
// 批量更新库存
if (!needUpdateList.isEmpty()) {
// 批量更新库存(最终去重)
if (!toUpdateInventoryMap.isEmpty()) {
List<Inventory> needUpdateList = new ArrayList<>(toUpdateInventoryMap.values());
inventoryMapper.batchUpdateInventory(needUpdateList);
inventoryService.RefreshInventory(needUpdateList.stream().map(Inventory::getId).distinct().collect(Collectors.toList()));
}
......@@ -339,82 +294,126 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
return deductRecordMap;
}
private Map<String, Object> buildDeductRecord(Inventory inv, String inventoryType, Long deductQty) {
Map<String, Object> record = new HashMap<>();
record.put("inventoryId", inv.getId());
record.put("locationId", inv.getLocationId());
record.put("materialId", inv.getMaterialId());
record.put("inventoryType", inventoryType);
record.put("deductQty", deductQty);
return record;
}
/**
* 扣减单个库存的数量
* @param inventory 库存对象
* 按「物料ID+库位ID+库存类型」扣减指定库存
* @param inventoryKey 库存Key(物料ID_库位ID_库存类型)
* @param deductQty 待扣减数量
* @param updateUser 操作人
* @param updateTime 操作时间
* @return 剩余未扣减数量
*/
private Long deductSingleInventory(Inventory inventory, Long deductQty, String updateUser, Date updateTime) {
Long currentQty = Optional.ofNullable(inventory.getQuantity()).orElse(0L);
// 关键修改:不再限制扣减数量,直接扣减(允许库存变为负数)
Long canDeductQty = deductQty; // 原逻辑:Math.min(deductQty, currentQty)
// 扣减数量(允许负数)
Long newQty = currentQty - canDeductQty;
inventory.setQuantity(newQty);
// 扣减后≤0,设置状态为0(即使是负数也设置)
if (newQty <= 0) {
inventory.setInventoryStatus(0L);
private Long deductByInventoryKey(String inventoryKey, Long deductQty, String updateUser, Date updateTime,
Map<String, Inventory> inventoryFullMap, Map<String, Inventory> toUpdateInventoryMap,
List<Map<String, Object>> tempDeductRecords) {
Inventory inv = inventoryFullMap.get(inventoryKey);
if (inv == null) {
return deductQty;
}
// 补充审计字段
inventory.setUpdateBy(updateUser);
inventory.setUpdateTime(updateTime);
Long currentQty = Optional.ofNullable(inv.getQuantity()).orElse(0L);
Long canDeduct = Math.min(deductQty, currentQty);
// 更新库存状态
inv.setQuantity(currentQty - canDeduct);
inv.setInventoryStatus(inv.getQuantity() <= 0 ? 0L : 1L);
inv.setUpdateBy(updateUser);
inv.setUpdateTime(updateTime);
toUpdateInventoryMap.put(inv.getId(), inv);
// 返回剩余未扣减数量(扣减全部,剩余为0)
return 0L; // 原逻辑:deductQty - canDeductQty
// 收集扣减记录(按统一维度)
Map<String, Object> record = buildDeductRecord(inv, inv.getInventoryType().toString(), canDeduct);
tempDeductRecords.add(record);
// 返回剩余未扣减数量
return deductQty - canDeduct;
}
/**
* 预加载库存分组Map
* Key=物料ID_库存类型 Value=该维度下所有库位的库存列表(含无库位)
* 移除仓库维度
* 合并同一「物料ID+库存类型+库位ID」的扣减记录(最终版)
* 核心:按「物料ID_库位ID_库存类型」合并,保留库位信息准确性
*/
private Map<String, List<Inventory>> loadInventoryGroupMap() {
Inventory query = new Inventory();
query.setInventoryStatus(1L);
query.setIsUsed(1L);
List<Inventory> inventoryList = inventoryService.selectInventoryList(query);
private List<Map<String, Object>> mergeDeductRecords(List<Map<String, Object>> deductRecords) {
if (CollectionUtils.isEmpty(deductRecords)) {
return Collections.emptyList();
}
if (CollectionUtils.isEmpty(inventoryList)) {
// 合并Key:物料ID_库位ID_库存类型
Map<String, Map<String, Object>> mergeMap = new LinkedHashMap<>();
for (Map<String, Object> record : deductRecords) {
String materialId = (String) record.get("materialId");
String locationId = (String) record.get("locationId");
String inventoryType = (String) record.get("inventoryType");
Long deductQty = (Long) record.get("deductQty");
if (StringUtils.isBlank(materialId) || StringUtils.isBlank(inventoryType) || deductQty <= 0) {
continue;
}
String mergeKey = buildInventoryKey(materialId, locationId, inventoryType);
if (mergeMap.containsKey(mergeKey)) {
Map<String, Object> existRecord = mergeMap.get(mergeKey);
existRecord.put("deductQty", (Long) existRecord.get("deductQty") + deductQty);
} else {
Map<String, Object> newRecord = new HashMap<>(record);
mergeMap.put(mergeKey, newRecord);
}
}
return new ArrayList<>(mergeMap.values());
}
/**
* 构建库存Key:物料ID_库位ID_库存类型
*/
private String buildInventoryKey(String materialId, String locationId, String inventoryType) {
return String.join("_",
Optional.ofNullable(materialId).orElse(""),
Optional.ofNullable(locationId).orElse(""),
Optional.ofNullable(inventoryType).orElse("")
);
}
/**
* 构建扣减记录
*/
private Map<String, Object> buildDeductRecord(Inventory inv, String inventoryType, Long deductQty) {
Map<String, Object> record = new HashMap<>();
record.put("inventoryId", inv.getId());
record.put("locationId", inv.getLocationId());
record.put("materialId", inv.getMaterialId());
record.put("inventoryType", inventoryType);
record.put("deductQty", deductQty);
return record;
}
/**
* 预加载库存全量Map(按「物料ID_库位ID_库存类型」为Key)
*/
private Map<String, Inventory> loadInventoryFullMap() {
Collection<Inventory> allInventory = InventoryCache.getAll().values();
if (CollectionUtils.isEmpty(allInventory)) {
return Collections.emptyMap();
}
// 按「物料ID_库存类型」分组(移除仓库维度)
return inventoryList.stream()
.collect(Collectors.groupingBy(
inv -> String.join("_",
Optional.ofNullable(inv.getMaterialId()).orElse(""),
return allInventory.stream()
.collect(Collectors.toMap(
inv -> buildInventoryKey(
inv.getMaterialId(),
inv.getLocationId(),
Optional.ofNullable(inv.getInventoryType()).map(String::valueOf).orElse("")
),
HashMap::new,
Collectors.toList()
inv -> inv,
(k1, k2) -> k1, // 重复Key保留第一个
LinkedHashMap::new
));
}
/**
* 构建扣减数量Map
* Key规则:物料ID_库位ID_库存类型(移除仓库维度)
* Key规则:物料ID_库位ID_库存类型
*/
private Map<String, Long> buildDeductQtyMap(List<OutboundOrderItems> items) {
Map<String, Long> deductQtyMap = new HashMap<>();
for (OutboundOrderItems item : items) {
String key = buildDeductKey(item);
// 累加扣减数量
Long qty = Optional.ofNullable(item.getActualQuantity()).orElse(0L);
deductQtyMap.put(key, deductQtyMap.getOrDefault(key, 0L) + qty);
}
......@@ -423,18 +422,17 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
/**
* 构建明细的扣减Key
* 移除仓库维度,Key规则:物料ID_库位ID_库存类型
*/
private String buildDeductKey(OutboundOrderItems item) {
return String.join("_",
Optional.ofNullable(item.getMaterialId()).orElse(""),
Optional.ofNullable(item.getLocationId()).orElse(""),
return buildInventoryKey(
item.getMaterialId(),
item.getLocationId(),
Optional.ofNullable(item.getInventoryType()).map(String::valueOf).orElse("")
);
}
@Override
public List<Map<String,String>> outboundOrdersTopTenByQuantity() {
public List<Map<String, String>> outboundOrdersTopTenByQuantity() {
return outboundOrdersMapper.SelectOutboundOrdersMaterialsTopTenByQuantity();
}
......@@ -450,8 +448,6 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
/**
* 新增出库单明细信息
*
* @param outboundOrders 出库单主对象
*/
public void insertOutboundOrderItems(OutboundOrders outboundOrders) {
List<OutboundOrderItems> outboundOrderItemsList = outboundOrders.getOutboundOrderItemsList();
......@@ -466,20 +462,23 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
throw new RuntimeException("库存被修改请重新确认");
}
// 合并相同库存维度的明细记录(简化版)
List<OutboundOrderItems> mergedItemsList = mergeSameInventoryItems(outboundOrderItemsList);
// 为明细设置订单ID和主键ID
for (OutboundOrderItems items : outboundOrderItemsList) {
for (OutboundOrderItems items : mergedItemsList) {
items.setOutboundOrderId(id);
items.setOrderId(outboundOrders.getOrderId());
items.setId(UUID.randomUUID().toString().replace("-", ""));
}
// 批量插入出库单明细
outboundOrdersMapper.batchOutboundOrderItems(outboundOrderItemsList);
outboundOrdersMapper.batchOutboundOrderItems(mergedItemsList);
// 拷贝明细到日志列表
List<String> inventoryIds = new ArrayList<>();
List<OutboundOrderLog> outboundOrderLogs = new ArrayList<>();
for (OutboundOrderItems items : outboundOrderItemsList) {
for (OutboundOrderItems items : mergedItemsList) {
OutboundOrderLog log = new OutboundOrderLog();
BeanUtils.copyProperties(items, log);
log.setOrderId(items.getOutboundOrderId());
......@@ -496,6 +495,40 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
}
/**
* 简化版:合并相同库存维度的明细记录
* 按「物料ID+库存类型+库位ID」维度合并,仅累加实际数量
*/
private List<OutboundOrderItems> mergeSameInventoryItems(List<OutboundOrderItems> itemsList) {
if (CollectionUtils.isEmpty(itemsList)) {
return Collections.emptyList();
}
// 按「物料ID+库存类型+库位ID」分组合并
Map<String, OutboundOrderItems> mergeMap = new LinkedHashMap<>();
for (OutboundOrderItems item : itemsList) {
String mergeKey = buildInventoryKey(
item.getMaterialId(),
item.getLocationId(),
Optional.ofNullable(item.getInventoryType()).map(String::valueOf).orElse("")
);
if (mergeMap.containsKey(mergeKey)) {
// 只合并实际数量
OutboundOrderItems existItem = mergeMap.get(mergeKey);
Long newActualQty = Optional.ofNullable(existItem.getActualQuantity()).orElse(0L)
+ Optional.ofNullable(item.getActualQuantity()).orElse(0L);
existItem.setActualQuantity(newActualQty);
} else {
// 新记录直接放入
mergeMap.put(mergeKey, item);
}
}
return new ArrayList<>(mergeMap.values());
}
@Transactional(rollbackFor = Exception.class)
@Override
public String importOutboundOrders(List<OutboundTemplateVO> inboundOrdersList, Boolean isUpdateSupport, String operName, Integer orderType) {
......@@ -519,7 +552,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
Map<String, List<OutboundOrderItems>> validItemMap = new HashMap<>();
boolean hasValidateError = false;
// 3. 预加载映射缓存(移除仓库相关映射)
// 3. 预加载映射缓存
Map<String, String> sapToMaterialIdMap = loadSapToMaterialIdMap();
Map<String, String> locationNameToIdMap = loadLocationNameToIdMap();
Map<String, String> ownerNameToIdMap = loadOwnerNameToIdMap();
......@@ -530,7 +563,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
.filter(vo -> StringUtils.isNotBlank(vo.getOrderId()))
.collect(Collectors.groupingBy(OutboundTemplateVO::getOrderId));
// 5. 数据验证(仅保留新增逻辑,检测到已存在则抛异常
// 5. 数据验证(仅新增逻辑
for (Map.Entry<String, List<OutboundTemplateVO>> entry : orderGroupMap.entrySet()) {
String orderId = entry.getKey();
List<OutboundTemplateVO> voList = entry.getValue();
......@@ -539,7 +572,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);
......@@ -548,7 +581,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
throw new ServiceException(String.format("入库单号【%s】已存在,当前系统仅支持新增,不支持更新", orderId));
}
// 仅新增逻辑:构建新出库单主数据
// 构建新出库单主数据
mainDO = new OutboundOrders();
BeanUtils.copyProperties(firstVO, mainDO,
"sapNo", "materialName", "plannedQuantity", "actualQuantity",
......@@ -591,6 +624,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
mainDO.setUpdateTime(now);
mainDO.setUpdateUserCode(operId);
mainDO.setSortNo(Optional.ofNullable(mainDO.getSortNo()).orElse(0L));
mainDO.setIsImport(0L);
// 明细校验
for (int i = 0; i < voList.size(); i++) {
......@@ -645,22 +679,45 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
// 库存校验(包含inventoryType)- 有库位才校验,无库位直接跳过
if (StringUtils.isNotBlank(locationName)) {
String inventoryTypeStr = Optional.ofNullable(orderType).map(String::valueOf).orElse("");
String inventoryMatchKey = String.join("_", materialId, locationId, inventoryTypeStr);
String inventoryMatchKey = buildInventoryKey(materialId, locationId, inventoryTypeStr);
AbstractMap.SimpleEntry<String, Long> inventoryEntry = inventoryTOIdMap.get(inventoryMatchKey);
String inventoryId = "";
if (inventoryEntry == null) {
throw new ServiceException(String.format(
"入库单号【%s】第%d条明细:物料【%s】+库位【%s】+库存类型【%s】组合的库存记录不存在",
orderId, lineNo, vo.getMaterialName(), vo.getLocationName(), inventoryTypeStr));
Inventory inventory = new Inventory();
BeanUtils.copyProperties(itemDO, inventory);
inventoryId = UUID.randomUUID().toString();
inventory.setInventoryType(Long.valueOf(orderType));
inventory.setBatchId(itemDO.getBatchCode());
inventory.setWarehousesId("local");
inventory.setQuantity(0L);
inventory.setInventoryStatus(1L);
inventory.setIsUsed(1L);
inventory.setId(inventoryId);
int insertCount = inventoryMapper.insertInventory(inventory);
if (insertCount != 1) {
throw new ServiceException(String.format("入库单号【%s】第%d条明细新增库存失败,插入行数为0", orderId, lineNo));
}
// 插入后直接添加到全局缓存
InventoryCache.addInventory(inventoryMatchKey, inventory);
sqlSessionTemplate.clearCache();
sqlSessionTemplate.flushStatements();
} else {
System.out.println("库存已存在,使用已有库存ID:" + inventoryEntry.getKey());
inventoryId = inventoryEntry.getKey();
}
itemDO.setInventoryId(inventoryEntry.getKey());
itemDO.setInventoryId(inventoryId);
}
// 无库位时不校验库存,也不设置inventoryId
itemDOList.add(itemDO);
}
// 合并相同库存维度的明细
List<OutboundOrderItems> mergedItemList = mergeSameInventoryItems(itemDOList);
validMainMap.put(orderId, mainDO);
validItemMap.put(orderId, itemDOList);
validItemMap.put(orderId, mergedItemList);
} catch (Exception e) {
hasValidateError = true;
......@@ -675,27 +732,26 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
throw new ServiceException(String.format("验证失败,导入终止!失败详情:%s", failureMsg.toString()));
}
// 7. 执行新增操作(仅新增,无更新)
// 7. 执行新增操作
Map<String, List<OutboundOrderItems>> allItemListMap = new HashMap<>();
for (Map.Entry<String, OutboundOrders> entry : validMainMap.entrySet()) {
String orderId = entry.getKey();
OutboundOrders mainDO = entry.getValue();
List<OutboundOrderItems> itemDOList = validItemMap.get(orderId);
// 新增主单
// 新增主单
outboundOrdersMapper.insertOutboundOrders(mainDO);
totalMainSuccess++;
successMsg.append(String.format("入库单号【%s】已新增;\n", orderId));
// 插入明细(仅新增,无删除操作)
// 插入明细
if (!CollectionUtils.isEmpty(itemDOList)) {
// 批量插入新明细
int itemSuccess = outboundOrderItemsMapper.batchInsertOutboundOrderItems(itemDOList);
totalItemSuccess += itemSuccess;
int itemFail = itemDOList.size() - itemSuccess;
totalItemFailure += itemFail;
successMsg.append(String.format("入库单号【%s】成功导入%d条物料明细;\n", orderId, itemSuccess));
successMsg.append(String.format("入库单号【%s】成功导入%d条物料明细(已合并相同库存维度);\n", orderId, itemSuccess));
if (itemFail > 0) {
failureMsg.append(String.format("入库单号【%s】有%d条物料明细导入失败;\n", orderId, itemFail));
}
......@@ -704,28 +760,36 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
}
// 8. 执行库存扣减并处理无库位明细拆分
for (Map.Entry<String, List<OutboundOrderItems>> entry : allItemListMap.entrySet()) {
List<OutboundOrderItems> itemList = entry.getValue();
// 执行库存扣减,获取无库位明细的扣减记录
Map<String, List<Map<String, Object>>> deductRecordMap = deductInventory(itemList, operId, now);
// 处理无库位明细拆分:整理有效明细后统一插入
if (!deductRecordMap.isEmpty()) {
handleNoLocationItemSplit(itemList, deductRecordMap, operId, now);
// 8. 异步执行库存扣减和无库位明细拆分
CompletableFuture.runAsync(() -> {
try {
for (Map.Entry<String, List<OutboundOrderItems>> entry : allItemListMap.entrySet()) {
List<OutboundOrderItems> itemList = entry.getValue();
// 执行库存扣减
Map<String, List<Map<String, Object>>> deductRecordMap = deductInventory(itemList, operId, now);
// 处理无库位明细拆分
boolean hasNoLocationItem = itemList.stream()
.anyMatch(item -> StringUtils.isBlank(item.getLocationId()));
if (hasNoLocationItem && !deductRecordMap.isEmpty()) {
handleNoLocationItemSplit(itemList, deductRecordMap, operId, now);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
// 9. 结果汇总
if (totalMainFailure > 0 || totalItemFailure > 0) {
String finalFailureMsg = String.format(
"导入结果:成功新增%d个入库单,失败%d个;成功导入%d条明细,失败%d条。失败详情:%s",
"导入结果:成功新增%d个入库单,失败%d个;成功导入%d条明细(已合并相同库存维度),失败%d条。失败详情:%s",
totalMainSuccess, totalMainFailure, totalItemSuccess, totalItemFailure, failureMsg.toString()
);
throw new ServiceException(finalFailureMsg);
} else {
String finalSuccessMsg = String.format(
"恭喜您,数据已全部导入成功!共新增%d个入库单,成功导入%d条物料明细。详情:%s",
"恭喜您,数据已全部导入成功!共新增%d个入库单,成功导入%d条物料明细(已合并相同库存维度)。详情:%s",
totalMainSuccess, totalItemSuccess, successMsg.toString()
);
return finalSuccessMsg;
......@@ -733,10 +797,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
/**
* 处理无库位明细拆分:
* 1. 过滤无效扣减(≤0),按库位合并有效扣减数量
* 2. 仅新增场景:删除临时明细,插入整理后的有效明细
* 3. 严格保留原逻辑,仅适配新增场景
* 处理无库位明细拆分(适配新的合并维度)
*/
private void handleNoLocationItemSplit(List<OutboundOrderItems> itemList,
Map<String, List<Map<String, Object>>> deductRecordMap,
......@@ -744,39 +805,27 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
List<OutboundOrderItems> newValidItemList = new ArrayList<>();
Set<String> orderIdSet = new HashSet<>();
// 第一步:遍历扣减记录,整理有效明细(过滤≤0,合并同库位)
for (OutboundOrderItems item : itemList) {
String itemId = item.getId();
List<Map<String, Object>> deductRecords = deductRecordMap.get(itemId);
if (CollectionUtils.isEmpty(deductRecords)) continue;
// 收集订单ID(用于删除临时明细)
orderIdSet.add(item.getOutboundOrderId());
// 按库位合并扣减数量,过滤≤0的无效记录
Map<String, Long> locationQtyMap = new HashMap<>();
// 直接遍历合并后的记录(已按物料+库存类型+库位ID合并)
for (Map<String, Object> rec : deductRecords) {
String inventoryId = (String) rec.get("inventoryId");
String locId = (String) rec.get("locationId");
Long deductQty = (Long) rec.get("deductQty");
if (deductQty <= 0) continue; // 过滤无效扣减
locationQtyMap.put(locId, locationQtyMap.getOrDefault(locId, 0L) + deductQty);
}
Long validQty = (Long) rec.get("deductQty");
// 生成有效明细
for (Map.Entry<String, Long> entry : locationQtyMap.entrySet()) {
String locId = entry.getKey();
Long validQty = entry.getValue();
if (validQty <= 0 || StringUtils.isBlank(inventoryId)) continue;
OutboundOrderItems newItem = new OutboundOrderItems();
BeanUtils.copyProperties(item, newItem);
newItem.setId(UUID.randomUUID().toString().replace("-", ""));
newItem.setLocationId(locId);
newItem.setActualQuantity(validQty);
newItem.setInventoryId(deductRecords.stream()
.filter(r -> locId.equals(r.get("locationId")))
.findFirst()
.map(r -> (String) r.get("inventoryId"))
.orElse(""));
newItem.setInventoryId(inventoryId);
newItem.setCreateBy(operId);
newItem.setCreateTime(now);
newItem.setUpdateBy(operId);
......@@ -785,18 +834,19 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
}
// 第二步:删除临时明细(导入时插入的原始明细)
// 删除临时明细
for (String orderId : orderIdSet) {
outboundOrderItemsMapper.deleteOutboundOrderItemsByOrderId(orderId);
}
// 第三步:批量插入整理后的有效明细
if (!newValidItemList.isEmpty()) {
outboundOrderItemsMapper.batchInsertOutboundOrderItems(newValidItemList);
// 批量插入有效明细(插入前再次合并)
List<OutboundOrderItems> mergedNewItems = mergeSameInventoryItems(newValidItemList);
if (!mergedNewItems.isEmpty()) {
outboundOrderItemsMapper.batchInsertOutboundOrderItems(mergedNewItems);
// 同步生成日志(保留原日志逻辑)
// 生成日志
List<OutboundOrderLog> logList = new ArrayList<>();
for (OutboundOrderItems newItem : newValidItemList) {
for (OutboundOrderItems newItem : mergedNewItems) {
OutboundOrderLog log = new OutboundOrderLog();
BeanUtils.copyProperties(newItem, log);
log.setOrderId(newItem.getOutboundOrderId());
......@@ -807,7 +857,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
}
// ========== 预加载映射辅助方法(移除仓库相关) ==========
// ========== 预加载映射辅助方法 ==========
private Map<String, String> loadSapToMaterialIdMap() {
List<Materials> materialsList = materialsService.selectMaterialsList(new Materials());
if (CollectionUtils.isEmpty(materialsList)) {
......@@ -853,8 +903,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
/**
* 加载库存映射Map(移除仓库维度)
* Key=物料ID_库位ID_库存类型
* 加载库存映射Map(按新维度)+ 同步填充全局缓存
*/
private Map<String, AbstractMap.SimpleEntry<String, Long>> loadInventoryTOIdMap() {
Inventory inventory = new Inventory();
......@@ -862,40 +911,35 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
inventory.setIsUsed(1L);
List<Inventory> inventoryList = inventoryService.selectInventoryList(inventory);
// 清空全局缓存
InventoryCache.clear();
if (CollectionUtils.isEmpty(inventoryList)) {
return Collections.emptyMap();
}
Map<String, AbstractMap.SimpleEntry<String, Long>> emptyLocationMap = inventoryList.stream()
return inventoryList.stream()
.filter(inv -> StringUtils.isNotBlank(inv.getMaterialId())
&& inv.getInventoryType() != null
&& StringUtils.isNotBlank(inv.getId())
&& StringUtils.isBlank(inv.getLocationId()))
.collect(Collectors.toMap(
inv -> String.join("_",
inv.getMaterialId().trim(),
"", inv.getInventoryType().toString()),
inv -> new AbstractMap.SimpleEntry<>(inv.getId().trim(), Optional.ofNullable(inv.getQuantity()).orElse(0L)),
(k1, k2) -> k1,
HashMap::new
));
Map<String, AbstractMap.SimpleEntry<String, Long>> nonEmptyLocationMap = inventoryList.stream()
.filter(inv -> StringUtils.isNotBlank(inv.getMaterialId())
&& StringUtils.isNotBlank(inv.getLocationId())
&& StringUtils.isNotBlank(inv.getId())
&& inv.getInventoryType() != null)
&& StringUtils.isNotBlank(inv.getId()))
.peek(inv -> {
// 同步到全局缓存(按新维度)
String key = buildInventoryKey(
inv.getMaterialId(),
inv.getLocationId(),
inv.getInventoryType().toString()
);
InventoryCache.addInventory(key, inv);
})
.collect(Collectors.toMap(
inv -> String.join("_",
inv.getMaterialId().trim(),
inv.getLocationId().trim(),
inv.getInventoryType().toString()),
inv -> buildInventoryKey(
inv.getMaterialId(),
inv.getLocationId(),
inv.getInventoryType().toString()
),
inv -> new AbstractMap.SimpleEntry<>(inv.getId().trim(), Optional.ofNullable(inv.getQuantity()).orElse(0L)),
(k1, k2) -> k1,
HashMap::new
));
emptyLocationMap.putAll(nonEmptyLocationMap);
return emptyLocationMap;
}
}
\ No newline at end of file
package com.ruoyi.inventory.utils;
import com.ruoyi.inventory.domain.Inventory;
import java.util.AbstractMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 库存映射全局缓存(解决导入新增库存即时可见问题)
*/
public class InventoryCache {
// 并发安全Map,Key=物料ID_库位ID_库存类型,Value=库存对象
private static final Map<String, Inventory> INVENTORY_MAP = new ConcurrentHashMap<>();
// 添加库存(直接存对象,避免参数不匹配)
public static void addInventory(String key, Inventory inventory) {
INVENTORY_MAP.put(key, inventory);
}
// 获取库存
public static Inventory getInventory(String key) {
return INVENTORY_MAP.get(key);
}
// 清空缓存
public static void clear() {
INVENTORY_MAP.clear();
}
// 获取全部缓存(核心:供loadInventoryGroupMap直接读取)
public static Map<String, Inventory> getAll() {
return INVENTORY_MAP;
}
}
\ No newline at end of file
......@@ -435,7 +435,7 @@
and inventory_status = '1'
]]>
</select>
<insert id="insertInventory" parameterType="Inventory">
<insert id="insertInventory" parameterType="Inventory" flushCache="true">
insert into inventory
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">id,</if>
......@@ -563,6 +563,7 @@ and inventory_status = '1'
left join inventory i on i.material_id = m.id
and i.is_used = 1
and i.unit_price > 0
and i.inventory_status=1
and i.last_inbound_time >= DATE_FORMAT(CURDATE(), '%Y-%m-01')
and i.last_inbound_time &lt; DATE_FORMAT(DATE_ADD(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01')
where
......@@ -580,6 +581,7 @@ and inventory_status = '1'
from materials m
left join inventory i on i.material_id = m.id
and i.is_used = 1
and i.inventory_status=1
and i.last_inbound_time >= DATE_FORMAT(CURDATE(), '%Y-%m-01')
and i.last_inbound_time &lt; DATE_FORMAT(DATE_ADD(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01')
where
......
......@@ -16,7 +16,6 @@
<result property="ownerId" column="owner_id" />
<result property="ownerName" column="owner_name" />
<result property="orderStatus" column="order_status" />
<!-- 保留原字段映射,新增startDate/endDate用于查询 -->
<result property="inboundDate" column="inbound_date" />
<result property="destination" column="destination" />
<result property="totalPlannedQuantity" column="total_planned_quantity" />
......@@ -29,6 +28,8 @@
<result property="createUserCode" column="create_user_code" />
<result property="updateTime" column="update_time" />
<result property="updateUserCode" column="update_user_code" />
<!-- 新增 isImport 字段映射 -->
<result property="isImport" column="is_import" />
</resultMap>
<resultMap id="OutboundOrdersOutboundOrderItemsResult" type="com.ruoyi.inventory.domain.OutboundOrders" extends="OutboundOrdersResult">
......@@ -94,7 +95,8 @@
oo.create_time,
oo.create_user_code,
oo.update_time,
oo.update_user_code
oo.update_user_code,
oo.is_import
from outbound_orders oo
left join owners o on oo.owner_id = o.id and o.is_used = 1
left join warehouses w on oo.warehouse_id = w.id and w.is_used = 1 and w.is_enabled = 1
......@@ -111,7 +113,8 @@
<if test="warehouseId != null and warehouseId != ''"> and oo.warehouse_id = #{warehouseId}</if>
<if test="ownerId != null and ownerId != ''"> and oo.owner_id = #{ownerId}</if>
<if test="orderStatus != null "> and oo.order_status = #{orderStatus}</if>
<!-- 替换为时间段查询:startDate和endDate -->
<!-- 新增 isImport 查询条件 -->
<if test="isImport != null "> and oo.is_import = #{isImport}</if>
<if test="startDate != null"> and oo.inbound_date &gt;= #{startDate}</if>
<if test="endDate != null"> and oo.inbound_date &lt;= #{endDate}</if>
<if test="destination != null and destination != ''"> and oo.destination = #{destination}</if>
......@@ -147,7 +150,8 @@
oo.create_time,
oo.create_user_code,
oo.update_time,
oo.update_user_code
oo.update_user_code,
oo.is_import
from outbound_orders oo
left join owners o on oo.owner_id = o.id
left join warehouses w on oo.warehouse_id = w.id
......@@ -202,7 +206,6 @@
<if test="warehouseId != null">warehouse_id,</if>
<if test="ownerId != null">owner_id,</if>
<if test="orderStatus != null">order_status,</if>
<!-- 保留inboundDate字段的插入(业务字段仍需存储) -->
<if test="inboundDate != null">inbound_date,</if>
<if test="destination != null">destination,</if>
<if test="totalPlannedQuantity != null">total_planned_quantity,</if>
......@@ -215,6 +218,8 @@
<if test="createUserCode != null">create_user_code,</if>
<if test="updateTime != null">update_time,</if>
<if test="updateUserCode != null">update_user_code,</if>
<!-- 新增 is_import 字段插入 -->
<if test="isImport != null">is_import,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">#{id},</if>
......@@ -238,6 +243,8 @@
<if test="createUserCode != null">#{createUserCode},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="updateUserCode != null">#{updateUserCode},</if>
<!-- 新增 is_import 字段值 -->
<if test="isImport != null">#{isImport},</if>
</trim>
</insert>
......@@ -264,6 +271,8 @@
<if test="createUserCode != null">create_user_code = #{createUserCode},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="updateUserCode != null">update_user_code = #{updateUserCode},</if>
<!-- 新增 is_import 字段更新 -->
<if test="isImport != null">is_import = #{isImport},</if>
</trim>
where id = #{id}
</update>
......@@ -316,6 +325,7 @@
left join outbound_order_items ooi
on ooi.material_id = m.id
and ooi.is_used = 1
and ooi.item_status=3
and ooi.shipped_at >= DATE_FORMAT(CURDATE(), '%Y-%m-01 00:00:00')
and ooi.shipped_at &lt; DATE_FORMAT(DATE_ADD(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01 00:00:00')
where
......@@ -334,6 +344,7 @@
left join outbound_order_items ooi
on ooi.material_id = m.id
and ooi.is_used = 1
and ooi.item_status=3
and ooi.shipped_at >= DATE_FORMAT(CURDATE(), '%Y-%m-01 00:00:00')
and ooi.shipped_at &lt; DATE_FORMAT(DATE_ADD(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01 00:00:00')
where
......@@ -346,8 +357,8 @@
<select id="outboundOrdersCount" resultType="String">
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')
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">
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论