Commit c7cf6243 by yubin

组件双击 去选框 弹窗修改高度 物料根据库存排序反显 列表id修改

parent 278338ea
......@@ -56,6 +56,7 @@
</el-form-item>
</el-form>
<div class="table-container" style="flex: 1; min-height: 400px; max-height: 600px; overflow: auto; margin: 10px 0;">
<!-- 物料表格(恢复所有字段显示) -->
<el-table
ref="materialTable"
......@@ -67,11 +68,14 @@
@row-click="handleRowClick"
:select-on-indeterminate="false"
@select="handleTableSelect"
@row-dblclick="handleRowDblClick"
>
<!-- 单选模式下隐藏选择框 -->
<el-table-column
type="selection"
width="55"
align="center"
v-if="multiple"
/>
<el-table-column type="index" label="序号" align="center"/>
<el-table-column label="SAP物料号" align="center" prop="sapNo" />
......@@ -79,7 +83,7 @@
<el-table-column label="TS Code" align="center" prop="tsCode" />
<el-table-column label="物料分类" align="center" prop="categoryCode">
<template slot-scope="scope">
{{ scope.row.displayCategory || categoryMap[scope.row.categoryCode] || scope.row.categoryCode || '-' }}
{{ scope.row.displayCategory }}
</template>
</el-table-column>
<el-table-column label="规格型号" align="center" prop="specification" />
......@@ -92,6 +96,7 @@
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<pagination
......@@ -221,6 +226,55 @@ export default {
this.getList()
},
methods: {
// 新增:双击行事件处理
handleRowDblClick(row) {
if (this.isSelecting || !this.$refs.materialTable) return
this.isSelecting = true
try {
// 单选模式下双击直接选择并关闭组件
if (!this.multiple) {
// 选中当前行
this.$refs.materialTable.clearSelection()
this.$refs.materialTable.toggleRowSelection(row, true)
this.singleSelectedId = row.id
this.selectedRows = [row]
// 构造返回数据
const selectedData = {
id: row.id,
sapNo: row.sapNo,
materialName: row.materialName,
tsCode: row.tsCode,
categoryCode: row.categoryCode,
categoryName: this.categoryMap[row.categoryCode] || row.categoryCode,
specification: row.specification,
materialUnit: row.materialUnit,
isBatchManaged: row.isBatchManaged
}
// 触发事件
this.$emit('input', row.id)
this.$emit('change', selectedData)
this.$emit('selection-change', {
materialIds: [row.id],
materials: [selectedData],
names: [row.materialName],
categoryIds: [row.categoryCode || '']
})
// 触发关闭事件(需要父组件监听此事件并关闭弹窗)
this.$emit('confirm', selectedData)
this.$emit('close')
} else {
// 多选模式下双击仅切换选择状态
this.$refs.materialTable.toggleRowSelection(row)
}
} finally {
this.isSelecting = false
}
},
// 恢复分类列表加载(分类名称映射)
async getCategoryList() {
try {
......@@ -246,6 +300,7 @@ export default {
console.error('获取分类列表失败:', error)
}
},
handleValueChange(val) {
if (this.isSelecting) return
......@@ -276,6 +331,7 @@ export default {
this.handleValueSync()
}
},
async getCategoryTreeData() {
this.loadingTree = true
try {
......@@ -291,6 +347,7 @@ export default {
this.loadingTree = false
}
},
buildTreeData(list, parentId = null) {
return list
.filter(item => parentId === null
......@@ -306,6 +363,7 @@ export default {
: undefined
}))
},
buildCategoryCodeToSidMap(treeData) {
treeData.forEach(node => {
if (node.categoryCode) {
......@@ -320,6 +378,7 @@ export default {
}
})
},
selectCategoryNodes(categoryCodes) {
if (!this.$refs.treeComponent || !this.$refs.treeComponent.$refs.tree) return
const tree = this.$refs.treeComponent.$refs.tree
......@@ -334,6 +393,7 @@ export default {
}
})
},
handleTreeClick(data) {
this.currentNodeId = data.sid
this.queryParams.categoryCode = data.categoryCode
......@@ -341,13 +401,14 @@ export default {
this.queryParams.pageNum = 1
this.getList()
},
getList() {
this.loading = true
listMaterials(this.queryParams).then(response => {
// 恢复所有字段映射(显示用)
this.materialsList = (response.rows || []).filter(item => item.isUsed !== 0 && item.isUsed !== '0').map(item => ({
...item,
displayCategory: this.categoryMap[item.categoryCode] || `${item.categoryCode}(未匹配分类)`
displayCategory: this.categoryMap[item.categoryCode] || '未匹配分类'
}))
this.total = response.total || 0
this.$nextTick(() => {
......@@ -361,6 +422,7 @@ export default {
this.loading = false
})
},
handleQuery() {
// 恢复分类名称查询逻辑
const inputName = this.queryParams.categoryNameInput
......@@ -380,6 +442,7 @@ export default {
this.queryParams.pageNum = 1
this.getList()
},
resetQuery() {
// 恢复所有查询参数重置
this.queryParams = {
......@@ -399,6 +462,7 @@ export default {
this.clearSelection()
this.getList()
},
// 核心:基于 ID 的选择事件(保留所有字段返回)
handleSelectionChange(selection) {
if (this.isSelecting || !this.$refs.materialTable) return
......@@ -454,6 +518,7 @@ export default {
this.isSelecting = false
}
},
// 单选模式下的选择事件(纯 ID 逻辑,保留字段返回)
handleTableSelect(selection, row) {
if (this.isSelecting || this.multiple) return
......@@ -502,6 +567,7 @@ export default {
this.isSelecting = false
}
},
// 行点击事件(纯 ID 逻辑,保留字段返回)
handleRowClick(row) {
if (this.isSelecting || !this.$refs.materialTable) return
......@@ -555,6 +621,7 @@ export default {
this.isSelecting = false
}
},
// 清空选择(纯 ID 逻辑)
clearSelection() {
if (this.isSelecting || !this.$refs.materialTable) return
......@@ -576,6 +643,7 @@ export default {
this.isSelecting = false
}
},
// 核心:基于 ID 的反显逻辑(保留所有字段显示)
handleValueSync(isRetry = false) {
if (this.loading || this.isSelecting || !this.$refs.materialTable) return
......@@ -646,6 +714,7 @@ export default {
this.isRetrySync = false
}
},
// 外部设置选中 ID 的方法
setSelectedIds(ids) {
if (this.isSelecting) return
......@@ -665,6 +734,7 @@ export default {
this.isSelecting = false
}
},
// 获取选中物料(返回完整字段,核心为ID)
getSelectedMaterials() {
if (this.multiple) {
......@@ -693,6 +763,7 @@ export default {
} : null
}
},
// 单选模式下设置选中 ID
setSingleSelection(id) {
if (this.isSelecting || this.multiple) return
......@@ -758,9 +829,17 @@ export default {
}
/deep/ .el-table--enable-row-hover .el-table__body tr:hover>td {
background-color: #f5f7fa;
cursor: pointer; /* 新增:鼠标悬停显示指针 */
}
/deep/ .el-table__fixed-right,
/deep/ .el-table__fixed-left {
pointer-events: auto !important;
}
/* 单选模式下表格行样式优化 */
/deep/ .el-table__body tr.el-table__row--striped td {
background-color: #fafafa;
}
/deep/ .el-table__body tr.current-row td {
background-color: #e8f4fd !important;
}
</style>
\ No newline at end of file
......@@ -71,7 +71,7 @@
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="仓库" prop="warehouseId">
<!-- <el-form-item label="仓库" prop="warehouseId">
<el-input
v-model="queryWarehouseName"
placeholder="请选择仓库"
......@@ -89,7 +89,7 @@
></i>
</template>
</el-input>
</el-form-item>
</el-form-item> -->
<el-form-item label="层" prop="layerCode">
<el-input
v-model="queryParams.layerCode"
......@@ -230,11 +230,11 @@
<el-table-column type="selection" width="55" align="center" fixed />
<el-table-column label="库位编码" align="center" prop="locationCode" width="120" fixed />
<el-table-column label="库位名称" align="center" prop="locationName" width="150" />
<el-table-column label="仓库" align="center" prop="warehousesName" width="180">
<!-- <el-table-column label="仓库" align="center" prop="warehousesName" width="180">
<template slot-scope="scope">
{{ scope.row.warehousesName }}
</template>
</el-table-column>
</el-table-column> -->
<el-table-column label="库位类型" align="center" prop="locationType" width="100">
<template slot-scope="scope">
<dict-tag :options="dict.type.location_type" :value="scope.row.locationType"/>
......@@ -357,7 +357,7 @@
</el-form-item>
</el-col>
</el-row>
<el-form-item label="仓库" prop="warehouseId">
<!-- <el-form-item label="仓库" prop="warehouseId">
<el-input
v-model="form.warehouseName"
placeholder="请选择仓库"
......@@ -375,7 +375,7 @@
></i>
</template>
</el-input>
</el-form-item>
</el-form-item> -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="库位类型" prop="locationType">
......@@ -583,10 +583,10 @@
/>
<!-- 仓库选择器组件 -->
<WarehouseSelector
<!-- <WarehouseSelector
v-model="warehouseSelectorVisible"
@selected="handleWarehouseSelected"
/>
/> -->
</div>
</template>
......@@ -594,7 +594,7 @@
import { listLocations, getLocations, delLocations, addLocations, updateLocations } from "@/api/inventory/locations"
import { listWarehouses } from "@/api/inventory/warehouses"
import materialsSeletor from "../../../components/materialsSeletor.vue"
import WarehouseSelector from "@/views/compononents/WarehouseSelector.vue"
// import WarehouseSelector from "@/views/compononents/WarehouseSelector.vue"
import ImportExcel from "@/components/ImportExcel/index"
import { listMaterials } from "@/api/inventory/materials"
......@@ -670,9 +670,9 @@ export default {
{ required: true, message: '库位名称不能为空', trigger: 'blur' },
{ min: 1, max: 100, message: '库位名称长度不能超过100个字符', trigger: 'blur' }
],
warehouseId: [
{ required: true, message: '仓库不能为空', trigger: 'change' }
],
// warehouseId: [
// { required: true, message: '仓库不能为空', trigger: 'change' }
// ],
locationType: [
{ required: true, message: '库位类型不能为空', trigger: 'change' }
],
......
......@@ -49,15 +49,12 @@
<div style="height: 70vh; overflow: auto; padding: 0 10px;">
<MaterialSelector
ref="materialsSeletor"
@selection-change="handleMaterialSelectionChange"
@selection-change="confirmMaterialSelect"
:selected-material-codes="form.materialUuids ? [form.materialUuids] : []"
:multiple="false"
/>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click.native="openMaterialSelector = false">取消</el-button>
<el-button type="primary" @click.native="confirmMaterialSelect">确认选择</el-button>
</div>
</el-dialog>
<!-- 库存信息列表 -->
......@@ -335,7 +332,8 @@ export default {
selectedMaterialId: '',
selectedMaterialInfo: null,
currentSelectedRowId: null,
isInitEcho: false
isInitEcho: false,
openMaterialSelector: false, // 确保初始为关闭状态
};
},
computed: {
......@@ -799,6 +797,9 @@ handleSubmit() {
},
// 修复:增强校验逻辑,增加组件存在性检查和异步同步
confirmMaterialSelect() {
// 标记为“用户主动操作”
this.isUserInitiatedSelect = true;
// 1. 检查选择器组件是否存在
if (!this.$refs.materialsSeletor) {
this.$message.error('物料选择器组件加载失败,请重试');
......@@ -821,18 +822,9 @@ handleSubmit() {
// 触发数据同步(覆盖事件未触发的场景)
this.handleMaterialSelectionChange(selectedData);
// 4. 增强空值校验
if (!this.selectedMaterialInfo || !this.selectedMaterialId) {
this.$message.warning('请选择物料后再确认');
return;
}
// 5. 容错处理:确保物料ID有效
const materialId = this.selectedMaterialInfo.id || this.selectedMaterialInfo.materialId || this.selectedMaterialInfo.uuid || '';
if (!materialId) {
this.$message.error('选中的物料缺少有效ID,请重新选择');
return;
}
// 6. 赋值并关闭弹窗
this.$set(this.form, 'materialId', materialId);
......@@ -844,6 +836,9 @@ handleSubmit() {
this.$nextTick(() => {
this.$refs.detailForm?.validateField('materialId');
});
// 重置标记
this.isUserInitiatedSelect = false;
});
}
},
......
......@@ -13,7 +13,7 @@ spring:
#username: root # 数据库用户名
#password: 'Aa123456'
#測試
url: jdbc:mysql://demo.docmis.cn:23500/inventory_manager?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&useSSL=false
url: jdbc:mysql://demo.docmis.cn:23500/inventory_manager?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&useSSL=false&allowMultiQueries=true
username: root
password: '!QAZ2wsx#EDC2022'
# 从库数据源
......
......@@ -7,6 +7,6 @@ public class WarehouseConfig {
/**
* 默认出库仓库ID(核心默认值)
*/
public static final String DEFAULT_WAREHOUSE_ID = "572ba484-199c-45d9-9735-610928ed5c70";
public static final String DEFAULT_WAREHOUSE_ID = "local";
}
\ No newline at end of file
......@@ -117,7 +117,7 @@ public class OutboundOrderItems extends BaseEntity
@Excel(name = "排序号")
private String updateUserCode;
private String InventoryType;
private int InventoryType;
}
\ No newline at end of file
......@@ -74,5 +74,11 @@ public interface OutboundOrderItemsMapper
public int batchInsertOutboundOrderItems(List<OutboundOrderItems> inboundOrderItems);
public int batchUpdateOutboundOrderItems(List<OutboundOrderItems> inboundOrderItems);
int batchDeleteOutboundOrderItems(String[] ids);
public int deleteOutboundOrderItemsByOrderId(String orderId);
}
......@@ -156,33 +156,45 @@ public class InventoryServiceImpl implements IInventoryService
}
updateInventory(inventory);
// 库存操作表插入数据
createInventoryOutboundLog(inventory,outboundOrderItem,Long.valueOf(quantity),SystemUtils.getUserName(),new Date());
}
RefreshInventory(inventoryIds);
}
return 1;
}
private void createInventoryOutboundLog(Inventory inventory, OutboundOrderItems outboundOrderItem,
Long deductQty, String updateUser, Date updateTime) {
InventoryTransactions transactions = new InventoryTransactions();
transactions.setId(IdUtils.simpleUUID());
transactions.setTransactionType(2L);// 事务类型-出库
transactions.setId(IdUtils.simpleUUID()); // 确保IdUtils工具类存在,若无则替换为UUID.randomUUID().toString()
transactions.setTransactionType(2L); // 事务类型-出库
transactions.setBatchCode(outboundOrderItem.getBatchCode());
transactions.setUnitPrice(String.valueOf(outboundOrderItem.getUnitPrice()));
transactions.setInventoryId(inventory.getId()); // 库存表Id
transactions.setReferenceId(outboundOrderItem.getOutboundOrderId()); //关联单号,相当于主记录-盘点主表
transactions.setReferenceItemId(outboundOrderItem.getId()); // 盘点子表id
transactions.setReferenceId(outboundOrderItem.getOutboundOrderId()); // 关联出库单主表ID
transactions.setReferenceItemId(outboundOrderItem.getId()); // 关联出库单明细ID
transactions.setMaterialId(outboundOrderItem.getMaterialId());
transactions.setWarehouseId(outboundOrderItem.getWarehouseId());
transactions.setLocationId(outboundOrderItem.getLocationId());
// 补充货主ID(从出库单主表查询)
OutboundOrders outboundOrders = outboundOrderMapper.selectOutboundOrdersById(outboundOrderItem.getOutboundOrderId());
if (outboundOrders != null) {
transactions.setOwnerId(outboundOrders.getOwnerId());
transactions.setQuantityBefore(Long.valueOf(quantity));// 变更前数量
transactions.setQuantityAfter(inventory.getQuantity());// 变更后数量
transactions.setQuantityChange(outboundOrderItem.getActualQuantity());
Date nowDate = new Date();
transactions.setTransactionTime(nowDate);
}
// 变更前后数量
Long beforeQty = Optional.ofNullable(inventory.getQuantity()).orElse(0L) + deductQty; // 扣减前数量 = 扣减后 + 扣减量
transactions.setQuantityBefore(beforeQty);
transactions.setQuantityAfter(inventory.getQuantity()); // 扣减后数量
transactions.setQuantityChange(deductQty); // 变更量(出库为正数)
transactions.setTransactionTime(updateTime);
transactions.setOperatedBy(updateUser);
transactions.setOperatedBy(SystemUtils.getUserName());
// 插入日志
insertInventoryTransactions.insertInventoryTransactions(transactions);
}
RefreshInventory(inventoryIds);
}
return 1;
}
@SerialExecution(group = "inventoryRefresh", fair = true)
@Override
......
......@@ -30,9 +30,12 @@ import org.springframework.util.CollectionUtils;
/**
* 出库单主Service业务层处理
* 核心修正:
* 1. 库存匹配Key统一为 仓库ID_物料ID_库位ID_库存类型
* 1. 库存匹配Key统一为 物料ID_库位ID_库存类型(移除仓库维度)
* 2. 确保inventoryType完整参与匹配
* 3. 库存扣减后≤0时强制设置inventory_status=0
* 4. 无库位库存不足时,自动扣减同物料同库存类型的有库位库存
* 5. 导入场景下仅保留新增逻辑,移除更新相关处理
* 6. 移除所有仓库相关逻辑,仅保留物料+库位维度匹配
*
* @author ruoyi
* @date 2025-12-03
......@@ -52,8 +55,6 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
private OwnersServiceImpl ownersService;
@Autowired
private WarehousesServiceImpl warehousesService;
@Autowired
private InventoryServiceImpl inventoryService;
@Autowired
private MaterialsServiceImpl materialsService;
......@@ -108,7 +109,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
/**
* 修改出库单主
* 修改出库单主(仅保留方法定义,移除更新逻辑)
*
* @param outboundOrders 出库单主
* @return 结果
......@@ -117,13 +118,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
@Override
public int updateOutboundOrders(OutboundOrders outboundOrders)
{
outboundOrdersMapper.deleteOutboundOrderItemsByOrderId(outboundOrders.getId());
outboundOrderLogMapper.deleteOutboundOrderLogByOrdersId(outboundOrders.getId());
outboundOrders.setUpdateUserCode(SystemUtils.getUserName());
outboundOrders.setUpdateTime(DateUtils.getNowDate());
insertOutboundOrderItems(outboundOrders);
return outboundOrdersMapper.updateOutboundOrders(outboundOrders);
throw new ServiceException("当前系统仅支持新增出库单,不支持修改操作");
}
/**
......@@ -184,7 +179,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
outboundOrders.setUpdateUserCode(updateUser);
outboundOrdersMapper.updateOutboundOrders(outboundOrders);
// 3. 执行库存扣减(包含inventoryType匹配 + 0值状态更新
// 3. 执行库存扣减(无库位不足时扣有库位
this.deductInventory(outboundOrderItems, updateUser, updateTime);
return 1;
......@@ -192,47 +187,186 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
/**
* 核心库存扣减逻辑
* 1. 按 仓库ID_物料ID_库位ID_库存类型 匹配库存
* 2. 扣减后数量≤0时设置inventory_status=0
* 1. 有库位:直接扣减指定库位库存
* 2. 无库位:先扣无库位库存,不足则扣同物料同库存类型的有库位库存
* 3. 扣减后数量≤0时设置inventory_status=0
* 4. 无库位扣减有库位时,记录扣减的库位和数量(用于生成明细)
* 5. 移除仓库维度,仅按物料+库位+库存类型匹配
* @param outboundOrderItems 出库明细
* @param updateUser 操作人
* @param updateTime 操作时间
* @return 无库位明细扣减的有库位库存记录 Map<无库位明细ID, List<扣减的库位库存信息Map>>
*/
private void deductInventory(List<OutboundOrderItems> outboundOrderItems, String updateUser, Date updateTime) {
private Map<String, List<Map<String, Object>>> deductInventory(List<OutboundOrderItems> outboundOrderItems, String updateUser, Date updateTime) {
if (CollectionUtils.isEmpty(outboundOrderItems)) {
return;
return Collections.emptyMap();
}
// 1. 预加载库存映射:Key=仓库ID_物料ID_库位ID_库存类型 Value=库存对象
Map<String, Inventory> inventoryMap = this.loadInventoryMap();
// 2. 构建扣减数量Map:Key=仓库ID_物料ID_库位ID_库存类型 Value=总扣减数量
Map<String, List<Map<String, Object>>> deductRecordMap = new HashMap<>();
Map<String, List<Inventory>> inventoryGroupMap = this.loadInventoryGroupMap();
Map<String, Long> deductQtyMap = this.buildDeductQtyMap(outboundOrderItems);
// 3. 遍历扣减Map,执行扣减+状态更新
List<Inventory> needUpdateList = new ArrayList<>();
for (Map.Entry<String, Long> entry : deductQtyMap.entrySet()) {
String key = entry.getKey();
Long deductQty = entry.getValue();
Long totalDeductQty = entry.getValue();
if (totalDeductQty <= 0) continue;
// 匹配库存
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] : "";
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]库位[%s]库存类型[%s]的库存不存在,无法扣减",
warehouseId, materialId, locationId, inventoryType));
"物料[%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);
// 记录无库位扣减
deductRecords.add(buildDeductRecord(inv, inventoryType, deductQty));
remainDeductQty -= deductQty;
}
// 第二步:无库位不足,扣有库位库存
if (remainDeductQty > 0) {
List<Inventory> hasLocationInvList = inventoryList.stream()
.filter(inv -> StringUtils.isNotBlank(inv.getLocationId()))
.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;
}
}
// 最后仍有剩余(所有库存耗尽),允许扣最后一个库存为负数
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;
// 执行数量扣减
// 剩余部分扣为负数
if (remainDeductQty > 0) {
Long finalDeduct = remainDeductQty;
targetInv.setQuantity(targetInv.getQuantity() - finalDeduct);
targetInv.setInventoryStatus(0L);
deductRecords.add(buildDeductRecord(targetInv, inventoryType, finalDeduct));
remainDeductQty = 0L;
}
}
// 关联明细与扣减记录
if (itemId != null && !deductRecords.isEmpty()) {
deductRecordMap.put(itemId, deductRecords);
}
}
// 批量更新库存
if (!needUpdateList.isEmpty()) {
inventoryMapper.batchUpdateInventory(needUpdateList);
inventoryService.RefreshInventory(needUpdateList.stream().map(Inventory::getId).distinct().collect(Collectors.toList()));
}
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 库存对象
* @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 newQty = currentQty - deductQty;
// 关键修改:不再限制扣减数量,直接扣减(允许库存变为负数)
Long canDeductQty = deductQty; // 原逻辑:Math.min(deductQty, currentQty)
// 扣减数量(允许负数)
Long newQty = currentQty - canDeductQty;
inventory.setQuantity(newQty);
// 核心规则:扣减后数量≤0 → 状态置0
// 扣减后≤0,设置状态为0(即使是负数也设置)
if (newQty <= 0) {
inventory.setInventoryStatus(0L);
}
......@@ -241,26 +375,16 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
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);
}
// 返回剩余未扣减数量(扣减全部,剩余为0)
return 0L; // 原逻辑:deductQty - canDeductQty
}
/**
* 预加载库存Map
* Key规则:仓库ID_物料ID_库位ID_库存类型
* 预加载库存分组Map
* Key=物料ID_库存类型 Value=该维度下所有库位的库存列表(含无库位)
* 移除仓库维度
*/
private Map<String, Inventory> loadInventoryMap() {
private Map<String, List<Inventory>> loadInventoryGroupMap() {
Inventory query = new Inventory();
query.setInventoryStatus(1L);
query.setIsUsed(1L);
......@@ -270,35 +394,26 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
return Collections.emptyMap();
}
Map<String, Inventory> inventoryMap = new HashMap<>();
for (Inventory inv : inventoryList) {
String key = String.join("_",
Optional.ofNullable(inv.getWarehousesId()).orElse(""),
// 按「物料ID_库存类型」分组(移除仓库维度)
return inventoryList.stream()
.collect(Collectors.groupingBy(
inv -> String.join("_",
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;
),
HashMap::new,
Collectors.toList()
));
}
/**
* 构建扣减数量Map
* Key规则:仓库ID_物料ID_库位ID_库存类型(和库存Map完全对齐
* Key规则:物料ID_库位ID_库存类型(移除仓库维度
*/
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("")
);
String key = buildDeductKey(item);
// 累加扣减数量
Long qty = Optional.ofNullable(item.getActualQuantity()).orElse(0L);
deductQtyMap.put(key, deductQtyMap.getOrDefault(key, 0L) + qty);
......@@ -306,6 +421,18 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
return deductQtyMap;
}
/**
* 构建明细的扣减Key
* 移除仓库维度,Key规则:物料ID_库位ID_库存类型
*/
private String buildDeductKey(OutboundOrderItems item) {
return String.join("_",
Optional.ofNullable(item.getMaterialId()).orElse(""),
Optional.ofNullable(item.getLocationId()).orElse(""),
Optional.ofNullable(item.getInventoryType()).map(String::valueOf).orElse("")
);
}
@Override
public List<Map<String,String>> outboundOrdersTopTenByQuantity() {
return outboundOrdersMapper.SelectOutboundOrdersMaterialsTopTenByQuantity();
......@@ -392,10 +519,9 @@ 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> warehouseNameToIdMap = loadWarehouseNameToIdMap();
Map<String, String> ownerNameToIdMap = loadOwnerNameToIdMap();
Map<String, AbstractMap.SimpleEntry<String, Long>> inventoryTOIdMap = loadInventoryTOIdMap();
......@@ -404,7 +530,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();
......@@ -413,31 +539,20 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
try {
OutboundTemplateVO firstVO = voList.get(0);
// 检查出库单是否已存在
// 检查出库单是否已存在(仅新增,存在则抛异常)
OutboundOrders outboundOrdersQuery = new OutboundOrders();
outboundOrdersQuery.setOrderId(orderId);
List<OutboundOrders> existMains = outboundOrdersMapper.selectOutboundOrdersList(outboundOrdersQuery);
if (existMains != null && existMains.size() > 1) {
throw new ServiceException(String.format("入库单号【%s】存在多条重复主表数据", orderId));
if (existMains != null && !existMains.isEmpty()) {
throw new ServiceException(String.format("入库单号【%s】已存在,当前系统仅支持新增,不支持更新", orderId));
}
OutboundOrders existMain = CollectionUtils.isEmpty(existMains) ? null : existMains.get(0);
if (existMain != null) {
if (!Boolean.TRUE.equals(isUpdateSupport)) {
throw new ServiceException(String.format("入库单号【%s】已存在,且不支持更新", orderId));
}
mainDO = existMain;
BeanUtils.copyProperties(firstVO, mainDO, "id", "createBy", "createTime");
mainDO.setUpdateBy(operId);
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");
"plannedPackages", "materialUnit", "materialRemark", "warehouseName", "warehouseId");
// 货主校验
String ownerName = firstVO.getOwnerName();
......@@ -476,7 +591,6 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
mainDO.setUpdateTime(now);
mainDO.setUpdateUserCode(operId);
mainDO.setSortNo(Optional.ofNullable(mainDO.getSortNo()).orElse(0L));
}
// 明细校验
for (int i = 0; i < voList.size(); i++) {
......@@ -485,7 +599,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
OutboundOrderItems itemDO = new OutboundOrderItems();
BeanUtils.copyProperties(vo, itemDO,
"orderId", "systemNo", "orderTypeId", "batchId");
"orderId", "systemNo", "orderTypeId", "batchId", "warehouseName", "warehouseId");
// 填充明细必填字段
itemDO.setId(UUID.randomUUID().toString());
......@@ -496,6 +610,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
itemDO.setCreateTime(now);
itemDO.setCreateUserCode(operId);
itemDO.setSortNo(0L);
itemDO.setItemStatus(3L); // 设置为已出库状态
// 物料SAP校验
String sapNo = vo.getSapNo() != null ? vo.getSapNo().trim() : "";
......@@ -512,38 +627,35 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
// 库位校验
String locationName = vo.getLocationName() != null ? vo.getLocationName().trim() : "";
if (StringUtils.isBlank(locationName)) {
throw new ServiceException(String.format("入库单号【%s】第%d条明细的库位名称不能为空",
orderId, lineNo));
}
String locationId = locationNameToIdMap.get(locationName);
if (StringUtils.isNotBlank(locationName)) {
if (StringUtils.isBlank(locationId)) {
throw new ServiceException(String.format("入库单号【%s】第%d条明细的库位【%s】不存在",
orderId, lineNo, locationName));
}
itemDO.setLocationId(locationId);
// 仓库校验
String warehouseName = vo.getWarehouseName() != null ? vo.getWarehouseName().trim() : "";
String warehouseId = warehouseNameToIdMap.get(warehouseName);
if (StringUtils.isBlank(warehouseId)) {
throw new ServiceException(String.format("系统未配置仓库【%s】,无法导入入库单号【%s】的第%d条明细",
warehouseName, orderId, lineNo));
}
itemDO.setWarehouseId(warehouseId);
// 库存校验(包含inventoryType)
// 库存类型设置
itemDO.setInventoryType(orderType);
// 实际出库数量(使用计划数量)
itemDO.setActualQuantity(Optional.ofNullable(vo.getActualQuantity()).orElse(0L));
// 库存校验(包含inventoryType)- 有库位才校验,无库位直接跳过
if (StringUtils.isNotBlank(locationName)) {
String inventoryTypeStr = Optional.ofNullable(orderType).map(String::valueOf).orElse("");
String inventoryMatchKey = String.join("_", warehouseId, materialId, locationId, inventoryTypeStr);
String inventoryMatchKey = String.join("_", materialId, locationId, inventoryTypeStr);
AbstractMap.SimpleEntry<String, Long> inventoryEntry = inventoryTOIdMap.get(inventoryMatchKey);
if (inventoryEntry == null) {
throw new ServiceException(String.format(
"入库单号【%s】第%d条明细:仓库【%s】+物料【%s】+库位【%s】+库存类型【%s】组合的库存记录不存在",
orderId, lineNo, vo.getWarehouseName(), vo.getMaterialName(), vo.getLocationName(), inventoryTypeStr));
"入库单号【%s】第%d条明细:物料【%s】+库位【%s】+库存类型【%s】组合的库存记录不存在",
orderId, lineNo, vo.getMaterialName(), vo.getLocationName(), inventoryTypeStr));
}
itemDO.setInventoryId(inventoryEntry.getKey());
itemDO.setInventoryType(inventoryTypeStr); // 填充库存类型
itemDO.setItemStatus(3L);
}
// 无库位时不校验库存,也不设置inventoryId
itemDOList.add(itemDO);
}
......@@ -563,18 +675,21 @@ 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;
......@@ -584,46 +699,115 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
if (itemFail > 0) {
failureMsg.append(String.format("入库单号【%s】有%d条物料明细导入失败;\n", orderId, itemFail));
}
allItemListMap.put(orderId, itemDOList);
}
}
// 8. 事务提交后执行库存扣减
if (TransactionSynchronizationManager.isSynchronizationActive()) {
Map<String, List<OutboundOrderItems>> finalValidItemMap = new HashMap<>(validItemMap);
String finalOperId = operId;
Date finalNow = now;
// 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);
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
for (List<OutboundOrderItems> itemList : finalValidItemMap.values()) {
deductInventory(itemList, finalOperId, finalNow);
}
}
});
} else {
for (List<OutboundOrderItems> itemList : validItemMap.values()) {
deductInventory(itemList, operId, now);
// 处理无库位明细拆分:整理有效明细后统一插入
if (!deductRecordMap.isEmpty()) {
handleNoLocationItemSplit(itemList, deductRecordMap, operId, now);
}
}
// 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;
}
}
// ========== 预加载映射辅助方法 ==========
/**
* 处理无库位明细拆分:
* 1. 过滤无效扣减(≤0),按库位合并有效扣减数量
* 2. 仅新增场景:删除临时明细,插入整理后的有效明细
* 3. 严格保留原逻辑,仅适配新增场景
*/
private void handleNoLocationItemSplit(List<OutboundOrderItems> itemList,
Map<String, List<Map<String, Object>>> deductRecordMap,
String operId, Date now) {
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<>();
for (Map<String, Object> rec : deductRecords) {
String locId = (String) rec.get("locationId");
Long deductQty = (Long) rec.get("deductQty");
if (deductQty <= 0) continue; // 过滤无效扣减
locationQtyMap.put(locId, locationQtyMap.getOrDefault(locId, 0L) + deductQty);
}
// 生成有效明细
for (Map.Entry<String, Long> entry : locationQtyMap.entrySet()) {
String locId = entry.getKey();
Long validQty = entry.getValue();
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.setCreateBy(operId);
newItem.setCreateTime(now);
newItem.setUpdateBy(operId);
newItem.setUpdateTime(now);
newValidItemList.add(newItem);
}
}
// 第二步:删除临时明细(导入时插入的原始明细)
for (String orderId : orderIdSet) {
outboundOrderItemsMapper.deleteOutboundOrderItemsByOrderId(orderId);
}
// 第三步:批量插入整理后的有效明细
if (!newValidItemList.isEmpty()) {
outboundOrderItemsMapper.batchInsertOutboundOrderItems(newValidItemList);
// 同步生成日志(保留原日志逻辑)
List<OutboundOrderLog> logList = new ArrayList<>();
for (OutboundOrderItems newItem : newValidItemList) {
OutboundOrderLog log = new OutboundOrderLog();
BeanUtils.copyProperties(newItem, log);
log.setOrderId(newItem.getOutboundOrderId());
log.setItemStatus(3L);
logList.add(log);
}
outboundOrderLogMapper.batchOutboundOrderLog(logList);
}
}
// ========== 预加载映射辅助方法(移除仓库相关) ==========
private Map<String, String> loadSapToMaterialIdMap() {
List<Materials> materialsList = materialsService.selectMaterialsList(new Materials());
if (CollectionUtils.isEmpty(materialsList)) {
......@@ -654,22 +838,6 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
));
}
private Map<String, String> loadWarehouseNameToIdMap() {
Warehouses query = new Warehouses();
query.setIsUsed(1L);
List<Warehouses> warehouseList = warehousesService.selectWarehousesList(query);
if (CollectionUtils.isEmpty(warehouseList)) {
return Collections.emptyMap();
}
return warehouseList.stream()
.filter(w -> StringUtils.isNotBlank(w.getWarehousesName()))
.collect(Collectors.toMap(
w -> w.getWarehousesName().trim(),
Warehouses::getId,
(k1, k2) -> k1
));
}
private Map<String, String> loadOwnerNameToIdMap() {
List<Owners> ownerList = ownersService.selectOwnersList(new Owners());
if (CollectionUtils.isEmpty(ownerList)) {
......@@ -684,6 +852,10 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
));
}
/**
* 加载库存映射Map(移除仓库维度)
* Key=物料ID_库位ID_库存类型
*/
private Map<String, AbstractMap.SimpleEntry<String, Long>> loadInventoryTOIdMap() {
Inventory inventory = new Inventory();
inventory.setInventoryStatus(1L);
......@@ -695,14 +867,12 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
Map<String, AbstractMap.SimpleEntry<String, Long>> emptyLocationMap = inventoryList.stream()
.filter(inv -> StringUtils.isNotBlank(inv.getWarehousesId())
&& StringUtils.isNotBlank(inv.getMaterialId())
.filter(inv -> StringUtils.isNotBlank(inv.getMaterialId())
&& inv.getInventoryType() != null
&& StringUtils.isNotBlank(inv.getId())
&& StringUtils.isBlank(inv.getLocationId()))
.collect(Collectors.toMap(
inv -> String.join("_",
inv.getWarehousesId().trim(),
inv.getMaterialId().trim(),
"", inv.getInventoryType().toString()),
inv -> new AbstractMap.SimpleEntry<>(inv.getId().trim(), Optional.ofNullable(inv.getQuantity()).orElse(0L)),
......@@ -711,14 +881,12 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
));
Map<String, AbstractMap.SimpleEntry<String, Long>> nonEmptyLocationMap = inventoryList.stream()
.filter(inv -> StringUtils.isNotBlank(inv.getWarehousesId())
&& StringUtils.isNotBlank(inv.getMaterialId())
.filter(inv -> 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()),
......
......@@ -128,6 +128,7 @@ public class StorageLocationsServiceImpl implements IStorageLocationsService
storageLocations.setId(LocationsID);
storageLocations.setCreateTime(DateUtils.getNowDate());
storageLocations.setCreateUserCode(String.valueOf(SecurityUtils.getUserId()));
storageLocations.setWarehousesId(WarehouseConfig.DEFAULT_WAREHOUSE_ID);
storageLocationsCategory.setLocationCode(LocationsID);
......
......@@ -586,4 +586,17 @@ and inventory_status = '1'
group by m.id, m.material_name
order by value desc, m.material_name asc
</select>
<update id="batchUpdateInventory">
<foreach collection="list" item="item" separator=";">
UPDATE inventory
<set>
quantity = #{item.quantity},
inventory_status = #{item.inventoryStatus},
update_user_code = #{item.updateBy},
update_time = #{item.updateTime}
</set>
WHERE id = #{item.id}
</foreach>
</update>
</mapper>
\ No newline at end of file
......@@ -367,6 +367,9 @@
</foreach>
</update>
<delete id="deleteOutboundOrderItemsByOrderId" parameterType="String">
delete from outbound_order_items where outbound_order_id = #{orderId}
</delete>
<!-- 单条删除:逻辑删除 -->
<update id="deleteOutboundOrderItemsById" parameterType="String">
update outbound_order_items
......@@ -432,4 +435,39 @@
)
</foreach>
</insert>
<update id="batchUpdateOutboundOrderItems" parameterType="java.util.List">
<foreach collection="list" item="item" separator=";">
UPDATE outbound_order_items
SET
inventory_id = <if test="item.inventoryId != null">#{item.inventoryId}</if><if test="item.inventoryId == null">null</if>,
material_id = <if test="item.materialId != null">#{item.materialId}</if><if test="item.materialId == null">null</if>,
batch_code = <if test="item.batchCode != null">#{item.batchCode}</if><if test="item.batchCode == null">null</if>,
warehouse_id = <if test="item.warehouseId != null">#{item.warehouseId}</if><if test="item.warehouseId == null">null</if>,
location_id = <if test="item.locationId != null">#{item.locationId}</if><if test="item.locationId == null">null</if>,
unit_price = <if test="item.unitPrice != null">#{item.unitPrice}</if><if test="item.unitPrice == null">null</if>,
planned_quantity = <if test="item.plannedQuantity != null">#{item.plannedQuantity}</if><if test="item.plannedQuantity == null">null</if>,
actual_quantity = <if test="item.actualQuantity != null">#{item.actualQuantity}</if><if test="item.actualQuantity == null">null</if>,
divisor = <if test="item.divisor != null">#{item.divisor}</if><if test="item.divisor == null">null</if>,
label_color = <if test="item.labelColor != null">#{item.labelColor}</if><if test="item.labelColor == null">null</if>,
voucher_number = <if test="item.voucherNumber != null">#{item.voucherNumber}</if><if test="item.voucherNumber == null">null</if>,
item_status = <if test="item.itemStatus != null">#{item.itemStatus}</if><if test="item.itemStatus == null">null</if>,
shipped_at = <if test="item.shippedAt != null">#{item.shippedAt}</if><if test="item.shippedAt == null">null</if>,
shipped_by = <if test="item.shippedBy != null">#{item.shippedBy}</if><if test="item.shippedBy == null">null</if>,
remark = <if test="item.remark != null">#{item.remark}</if><if test="item.remark == null">null</if>,
is_used = <if test="item.isUsed != null">#{item.isUsed}</if><if test="item.isUsed == null">1</if>,
sort_no = <if test="item.sortNo != null">#{item.sortNo}</if><if test="item.sortNo == null">null</if>,
update_time = <if test="item.updateTime != null">#{item.updateTime}</if><if test="item.updateTime == null">NOW()</if>,
update_user_code = <if test="item.updateUserCode != null">#{item.updateUserCode}</if><if test="item.updateUserCode == null">null</if>
WHERE id = #{item.id}
</foreach>
</update>
<delete id="batchDeleteOutboundOrderItems">
DELETE FROM outbound_order_items
WHERE id IN
<foreach collection="array" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论