Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
M
mini-wms
概览
Overview
Details
Activity
Cycle Analytics
版本库
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
问题
0
Issues
0
列表
Board
标记
里程碑
合并请求
0
Merge Requests
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
Snippets
成员
Members
Collapse sidebar
Close sidebar
活动
图像
聊天
创建新问题
作业
提交
Issue Boards
Open sidebar
周海峰
mini-wms
Commits
c7cf6243
Commit
c7cf6243
authored
Dec 16, 2025
by
yubin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
组件双击 去选框 弹窗修改高度 物料根据库存排序反显 列表id修改
parent
278338ea
隐藏空白字符变更
内嵌
并排
正在显示
12 个修改的文件
包含
617 行增加
和
300 行删除
+617
-300
ruoyi-admin-vue/src/components/materialsSeletor.vue
+85
-5
ruoyi-admin-vue/src/views/inventory/locations/index.vue
+12
-12
ruoyi-admin-vue/src/views/inventory/orders/OutboundOrderFormWithItems.vue
+43
-48
ruoyi-admin/src/main/resources/application-druid.yml
+1
-1
ruoyi-common/src/main/java/com/ruoyi/common/config/WarehouseConfig.java
+2
-1
ruoyi-inventory/src/main/java/com/ruoyi/inventory/domain/OutboundOrderItems.java
+2
-1
ruoyi-inventory/src/main/java/com/ruoyi/inventory/mapper/OutboundOrderItemsMapper.java
+6
-0
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/impl/InventoryServiceImpl.java
+34
-22
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/impl/OutboundOrdersServiceImpl.java
+378
-210
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/impl/StorageLocationsServiceImpl.java
+1
-0
ruoyi-inventory/src/main/resources/mapper/inventory/InventoryMapper.xml
+14
-0
ruoyi-inventory/src/main/resources/mapper/inventory/OutboundOrderItemsMapper.xml
+39
-0
没有找到文件。
ruoyi-admin-vue/src/components/materialsSeletor.vue
View file @
c7cf6243
...
@@ -55,7 +55,8 @@
...
@@ -55,7 +55,8 @@
<el-button
icon=
"el-icon-refresh"
size=
"mini"
@
click=
"resetQuery"
>
重置
</el-button>
<el-button
icon=
"el-icon-refresh"
size=
"mini"
@
click=
"resetQuery"
>
重置
</el-button>
</el-form-item>
</el-form-item>
</el-form>
</el-form>
<div
class=
"table-container"
style=
"flex: 1; min-height: 400px; max-height: 600px; overflow: auto; margin: 10px 0;"
>
<!-- 物料表格(恢复所有字段显示) -->
<!-- 物料表格(恢复所有字段显示) -->
<el-table
<el-table
ref=
"materialTable"
ref=
"materialTable"
...
@@ -66,12 +67,15 @@
...
@@ -66,12 +67,15 @@
:row-key=
"row => row.id"
:row-key=
"row => row.id"
@
row-click=
"handleRowClick"
@
row-click=
"handleRowClick"
:select-on-indeterminate=
"false"
:select-on-indeterminate=
"false"
@
select=
"handleTableSelect"
@
select=
"handleTableSelect"
@
row-dblclick=
"handleRowDblClick"
>
>
<!-- 单选模式下隐藏选择框 -->
<el-table-column
<el-table-column
type=
"selection"
type=
"selection"
width=
"55"
width=
"55"
align=
"center"
align=
"center"
v-if=
"multiple"
/>
/>
<el-table-column
type=
"index"
label=
"序号"
align=
"center"
/>
<el-table-column
type=
"index"
label=
"序号"
align=
"center"
/>
<el-table-column
label=
"SAP物料号"
align=
"center"
prop=
"sapNo"
/>
<el-table-column
label=
"SAP物料号"
align=
"center"
prop=
"sapNo"
/>
...
@@ -79,7 +83,7 @@
...
@@ -79,7 +83,7 @@
<el-table-column
label=
"TS Code"
align=
"center"
prop=
"tsCode"
/>
<el-table-column
label=
"TS Code"
align=
"center"
prop=
"tsCode"
/>
<el-table-column
label=
"物料分类"
align=
"center"
prop=
"categoryCode"
>
<el-table-column
label=
"物料分类"
align=
"center"
prop=
"categoryCode"
>
<
template
slot-scope=
"scope"
>
<
template
slot-scope=
"scope"
>
{{
scope
.
row
.
displayCategory
||
categoryMap
[
scope
.
row
.
categoryCode
]
||
scope
.
row
.
categoryCode
||
'-'
}}
{{
scope
.
row
.
displayCategory
}}
</
template
>
</
template
>
</el-table-column>
</el-table-column>
<el-table-column
label=
"规格型号"
align=
"center"
prop=
"specification"
/>
<el-table-column
label=
"规格型号"
align=
"center"
prop=
"specification"
/>
...
@@ -92,7 +96,8 @@
...
@@ -92,7 +96,8 @@
</
template
>
</
template
>
</el-table-column>
</el-table-column>
</el-table>
</el-table>
</div>
<!-- 分页 -->
<!-- 分页 -->
<pagination
<pagination
v-show=
"total>0"
v-show=
"total>0"
...
@@ -221,6 +226,55 @@ export default {
...
@@ -221,6 +226,55 @@ export default {
this
.
getList
()
this
.
getList
()
},
},
methods
:
{
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
()
{
async
getCategoryList
()
{
try
{
try
{
...
@@ -246,6 +300,7 @@ export default {
...
@@ -246,6 +300,7 @@ export default {
console
.
error
(
'获取分类列表失败:'
,
error
)
console
.
error
(
'获取分类列表失败:'
,
error
)
}
}
},
},
handleValueChange
(
val
)
{
handleValueChange
(
val
)
{
if
(
this
.
isSelecting
)
return
if
(
this
.
isSelecting
)
return
...
@@ -276,6 +331,7 @@ export default {
...
@@ -276,6 +331,7 @@ export default {
this
.
handleValueSync
()
this
.
handleValueSync
()
}
}
},
},
async
getCategoryTreeData
()
{
async
getCategoryTreeData
()
{
this
.
loadingTree
=
true
this
.
loadingTree
=
true
try
{
try
{
...
@@ -291,6 +347,7 @@ export default {
...
@@ -291,6 +347,7 @@ export default {
this
.
loadingTree
=
false
this
.
loadingTree
=
false
}
}
},
},
buildTreeData
(
list
,
parentId
=
null
)
{
buildTreeData
(
list
,
parentId
=
null
)
{
return
list
return
list
.
filter
(
item
=>
parentId
===
null
.
filter
(
item
=>
parentId
===
null
...
@@ -306,6 +363,7 @@ export default {
...
@@ -306,6 +363,7 @@ export default {
:
undefined
:
undefined
}))
}))
},
},
buildCategoryCodeToSidMap
(
treeData
)
{
buildCategoryCodeToSidMap
(
treeData
)
{
treeData
.
forEach
(
node
=>
{
treeData
.
forEach
(
node
=>
{
if
(
node
.
categoryCode
)
{
if
(
node
.
categoryCode
)
{
...
@@ -320,6 +378,7 @@ export default {
...
@@ -320,6 +378,7 @@ export default {
}
}
})
})
},
},
selectCategoryNodes
(
categoryCodes
)
{
selectCategoryNodes
(
categoryCodes
)
{
if
(
!
this
.
$refs
.
treeComponent
||
!
this
.
$refs
.
treeComponent
.
$refs
.
tree
)
return
if
(
!
this
.
$refs
.
treeComponent
||
!
this
.
$refs
.
treeComponent
.
$refs
.
tree
)
return
const
tree
=
this
.
$refs
.
treeComponent
.
$refs
.
tree
const
tree
=
this
.
$refs
.
treeComponent
.
$refs
.
tree
...
@@ -334,6 +393,7 @@ export default {
...
@@ -334,6 +393,7 @@ export default {
}
}
})
})
},
},
handleTreeClick
(
data
)
{
handleTreeClick
(
data
)
{
this
.
currentNodeId
=
data
.
sid
this
.
currentNodeId
=
data
.
sid
this
.
queryParams
.
categoryCode
=
data
.
categoryCode
this
.
queryParams
.
categoryCode
=
data
.
categoryCode
...
@@ -341,13 +401,14 @@ export default {
...
@@ -341,13 +401,14 @@ export default {
this
.
queryParams
.
pageNum
=
1
this
.
queryParams
.
pageNum
=
1
this
.
getList
()
this
.
getList
()
},
},
getList
()
{
getList
()
{
this
.
loading
=
true
this
.
loading
=
true
listMaterials
(
this
.
queryParams
).
then
(
response
=>
{
listMaterials
(
this
.
queryParams
).
then
(
response
=>
{
// 恢复所有字段映射(显示用)
// 恢复所有字段映射(显示用)
this
.
materialsList
=
(
response
.
rows
||
[]).
filter
(
item
=>
item
.
isUsed
!==
0
&&
item
.
isUsed
!==
'0'
).
map
(
item
=>
({
this
.
materialsList
=
(
response
.
rows
||
[]).
filter
(
item
=>
item
.
isUsed
!==
0
&&
item
.
isUsed
!==
'0'
).
map
(
item
=>
({
...
item
,
...
item
,
displayCategory
:
this
.
categoryMap
[
item
.
categoryCode
]
||
`
${
item
.
categoryCode
}
(未匹配分类)`
displayCategory
:
this
.
categoryMap
[
item
.
categoryCode
]
||
'未匹配分类'
}))
}))
this
.
total
=
response
.
total
||
0
this
.
total
=
response
.
total
||
0
this
.
$nextTick
(()
=>
{
this
.
$nextTick
(()
=>
{
...
@@ -361,6 +422,7 @@ export default {
...
@@ -361,6 +422,7 @@ export default {
this
.
loading
=
false
this
.
loading
=
false
})
})
},
},
handleQuery
()
{
handleQuery
()
{
// 恢复分类名称查询逻辑
// 恢复分类名称查询逻辑
const
inputName
=
this
.
queryParams
.
categoryNameInput
const
inputName
=
this
.
queryParams
.
categoryNameInput
...
@@ -380,6 +442,7 @@ export default {
...
@@ -380,6 +442,7 @@ export default {
this
.
queryParams
.
pageNum
=
1
this
.
queryParams
.
pageNum
=
1
this
.
getList
()
this
.
getList
()
},
},
resetQuery
()
{
resetQuery
()
{
// 恢复所有查询参数重置
// 恢复所有查询参数重置
this
.
queryParams
=
{
this
.
queryParams
=
{
...
@@ -399,6 +462,7 @@ export default {
...
@@ -399,6 +462,7 @@ export default {
this
.
clearSelection
()
this
.
clearSelection
()
this
.
getList
()
this
.
getList
()
},
},
// 核心:基于 ID 的选择事件(保留所有字段返回)
// 核心:基于 ID 的选择事件(保留所有字段返回)
handleSelectionChange
(
selection
)
{
handleSelectionChange
(
selection
)
{
if
(
this
.
isSelecting
||
!
this
.
$refs
.
materialTable
)
return
if
(
this
.
isSelecting
||
!
this
.
$refs
.
materialTable
)
return
...
@@ -454,6 +518,7 @@ export default {
...
@@ -454,6 +518,7 @@ export default {
this
.
isSelecting
=
false
this
.
isSelecting
=
false
}
}
},
},
// 单选模式下的选择事件(纯 ID 逻辑,保留字段返回)
// 单选模式下的选择事件(纯 ID 逻辑,保留字段返回)
handleTableSelect
(
selection
,
row
)
{
handleTableSelect
(
selection
,
row
)
{
if
(
this
.
isSelecting
||
this
.
multiple
)
return
if
(
this
.
isSelecting
||
this
.
multiple
)
return
...
@@ -502,6 +567,7 @@ export default {
...
@@ -502,6 +567,7 @@ export default {
this
.
isSelecting
=
false
this
.
isSelecting
=
false
}
}
},
},
// 行点击事件(纯 ID 逻辑,保留字段返回)
// 行点击事件(纯 ID 逻辑,保留字段返回)
handleRowClick
(
row
)
{
handleRowClick
(
row
)
{
if
(
this
.
isSelecting
||
!
this
.
$refs
.
materialTable
)
return
if
(
this
.
isSelecting
||
!
this
.
$refs
.
materialTable
)
return
...
@@ -555,6 +621,7 @@ export default {
...
@@ -555,6 +621,7 @@ export default {
this
.
isSelecting
=
false
this
.
isSelecting
=
false
}
}
},
},
// 清空选择(纯 ID 逻辑)
// 清空选择(纯 ID 逻辑)
clearSelection
()
{
clearSelection
()
{
if
(
this
.
isSelecting
||
!
this
.
$refs
.
materialTable
)
return
if
(
this
.
isSelecting
||
!
this
.
$refs
.
materialTable
)
return
...
@@ -576,6 +643,7 @@ export default {
...
@@ -576,6 +643,7 @@ export default {
this
.
isSelecting
=
false
this
.
isSelecting
=
false
}
}
},
},
// 核心:基于 ID 的反显逻辑(保留所有字段显示)
// 核心:基于 ID 的反显逻辑(保留所有字段显示)
handleValueSync
(
isRetry
=
false
)
{
handleValueSync
(
isRetry
=
false
)
{
if
(
this
.
loading
||
this
.
isSelecting
||
!
this
.
$refs
.
materialTable
)
return
if
(
this
.
loading
||
this
.
isSelecting
||
!
this
.
$refs
.
materialTable
)
return
...
@@ -646,6 +714,7 @@ export default {
...
@@ -646,6 +714,7 @@ export default {
this
.
isRetrySync
=
false
this
.
isRetrySync
=
false
}
}
},
},
// 外部设置选中 ID 的方法
// 外部设置选中 ID 的方法
setSelectedIds
(
ids
)
{
setSelectedIds
(
ids
)
{
if
(
this
.
isSelecting
)
return
if
(
this
.
isSelecting
)
return
...
@@ -665,6 +734,7 @@ export default {
...
@@ -665,6 +734,7 @@ export default {
this
.
isSelecting
=
false
this
.
isSelecting
=
false
}
}
},
},
// 获取选中物料(返回完整字段,核心为ID)
// 获取选中物料(返回完整字段,核心为ID)
getSelectedMaterials
()
{
getSelectedMaterials
()
{
if
(
this
.
multiple
)
{
if
(
this
.
multiple
)
{
...
@@ -693,6 +763,7 @@ export default {
...
@@ -693,6 +763,7 @@ export default {
}
:
null
}
:
null
}
}
},
},
// 单选模式下设置选中 ID
// 单选模式下设置选中 ID
setSingleSelection
(
id
)
{
setSingleSelection
(
id
)
{
if
(
this
.
isSelecting
||
this
.
multiple
)
return
if
(
this
.
isSelecting
||
this
.
multiple
)
return
...
@@ -758,9 +829,17 @@ export default {
...
@@ -758,9 +829,17 @@ export default {
}
}
/
deep
/
.el-table--enable-row-hover
.el-table__body
tr
:hover
>
td
{
/
deep
/
.el-table--enable-row-hover
.el-table__body
tr
:hover
>
td
{
background-color
:
#f5f7fa
;
background-color
:
#f5f7fa
;
cursor
:
pointer
;
/* 新增:鼠标悬停显示指针 */
}
}
/
deep
/
.el-table__fixed-right
,
/
deep
/
.el-table__fixed-right
,
/
deep
/
.el-table__fixed-left
{
/
deep
/
.el-table__fixed-left
{
pointer-events
:
auto
!important
;
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
>
</
style
>
\ No newline at end of file
ruoyi-admin-vue/src/views/inventory/locations/index.vue
View file @
c7cf6243
...
@@ -71,7 +71,7 @@
...
@@ -71,7 +71,7 @@
@
keyup
.
enter
.
native=
"handleQuery"
@
keyup
.
enter
.
native=
"handleQuery"
/>
/>
</el-form-item>
</el-form-item>
<el-form-item
label=
"仓库"
prop=
"warehouseId"
>
<
!-- <
el-form-item label="仓库" prop="warehouseId">
<el-input
<el-input
v-model="queryWarehouseName"
v-model="queryWarehouseName"
placeholder="请选择仓库"
placeholder="请选择仓库"
...
@@ -89,7 +89,7 @@
...
@@ -89,7 +89,7 @@
></i>
></i>
</template>
</template>
</el-input>
</el-input>
</el-form-item>
</el-form-item>
-->
<el-form-item
label=
"层"
prop=
"layerCode"
>
<el-form-item
label=
"层"
prop=
"layerCode"
>
<el-input
<el-input
v-model=
"queryParams.layerCode"
v-model=
"queryParams.layerCode"
...
@@ -230,11 +230,11 @@
...
@@ -230,11 +230,11 @@
<el-table-column
type=
"selection"
width=
"55"
align=
"center"
fixed
/>
<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=
"locationCode"
width=
"120"
fixed
/>
<el-table-column
label=
"库位名称"
align=
"center"
prop=
"locationName"
width=
"150"
/>
<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">
<template slot-scope="scope">
{{ scope.row.warehousesName }}
{{ scope.row.warehousesName }}
</template>
</template>
</el-table-column>
</el-table-column>
-->
<el-table-column
label=
"库位类型"
align=
"center"
prop=
"locationType"
width=
"100"
>
<el-table-column
label=
"库位类型"
align=
"center"
prop=
"locationType"
width=
"100"
>
<
template
slot-scope=
"scope"
>
<
template
slot-scope=
"scope"
>
<dict-tag
:options=
"dict.type.location_type"
:value=
"scope.row.locationType"
/>
<dict-tag
:options=
"dict.type.location_type"
:value=
"scope.row.locationType"
/>
...
@@ -357,7 +357,7 @@
...
@@ -357,7 +357,7 @@
</el-form-item>
</el-form-item>
</el-col>
</el-col>
</el-row>
</el-row>
<el-form-item
label=
"仓库"
prop=
"warehouseId"
>
<
!-- <
el-form-item label="仓库" prop="warehouseId">
<el-input
<el-input
v-model="form.warehouseName"
v-model="form.warehouseName"
placeholder="请选择仓库"
placeholder="请选择仓库"
...
@@ -375,7 +375,7 @@
...
@@ -375,7 +375,7 @@
></i>
></i>
</template>
</template>
</el-input>
</el-input>
</el-form-item>
</el-form-item>
-->
<el-row
:gutter=
"20"
>
<el-row
:gutter=
"20"
>
<el-col
:span=
"12"
>
<el-col
:span=
"12"
>
<el-form-item
label=
"库位类型"
prop=
"locationType"
>
<el-form-item
label=
"库位类型"
prop=
"locationType"
>
...
@@ -583,10 +583,10 @@
...
@@ -583,10 +583,10 @@
/>
/>
<!-- 仓库选择器组件 -->
<!-- 仓库选择器组件 -->
<WarehouseSelector
<
!-- <
WarehouseSelector
v-model="warehouseSelectorVisible"
v-model="warehouseSelectorVisible"
@selected="handleWarehouseSelected"
@selected="handleWarehouseSelected"
/>
/>
-->
</div>
</div>
</template>
</template>
...
@@ -594,7 +594,7 @@
...
@@ -594,7 +594,7 @@
import
{
listLocations
,
getLocations
,
delLocations
,
addLocations
,
updateLocations
}
from
"@/api/inventory/locations"
import
{
listLocations
,
getLocations
,
delLocations
,
addLocations
,
updateLocations
}
from
"@/api/inventory/locations"
import
{
listWarehouses
}
from
"@/api/inventory/warehouses"
import
{
listWarehouses
}
from
"@/api/inventory/warehouses"
import
materialsSeletor
from
"../../../components/materialsSeletor.vue"
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
ImportExcel
from
"@/components/ImportExcel/index"
import
{
listMaterials
}
from
"@/api/inventory/materials"
import
{
listMaterials
}
from
"@/api/inventory/materials"
...
@@ -670,9 +670,9 @@ export default {
...
@@ -670,9 +670,9 @@ export default {
{
required
:
true
,
message
:
'库位名称不能为空'
,
trigger
:
'blur'
},
{
required
:
true
,
message
:
'库位名称不能为空'
,
trigger
:
'blur'
},
{
min
:
1
,
max
:
100
,
message
:
'库位名称长度不能超过100个字符'
,
trigger
:
'blur'
}
{
min
:
1
,
max
:
100
,
message
:
'库位名称长度不能超过100个字符'
,
trigger
:
'blur'
}
],
],
warehouseId
:
[
//
warehouseId: [
{
required
:
true
,
message
:
'仓库不能为空'
,
trigger
:
'change'
}
//
{ required: true, message: '仓库不能为空', trigger: 'change' }
],
//
],
locationType
:
[
locationType
:
[
{
required
:
true
,
message
:
'库位类型不能为空'
,
trigger
:
'change'
}
{
required
:
true
,
message
:
'库位类型不能为空'
,
trigger
:
'change'
}
],
],
...
...
ruoyi-admin-vue/src/views/inventory/orders/OutboundOrderFormWithItems.vue
View file @
c7cf6243
...
@@ -49,15 +49,12 @@
...
@@ -49,15 +49,12 @@
<div
style=
"height: 70vh; overflow: auto; padding: 0 10px;"
>
<div
style=
"height: 70vh; overflow: auto; padding: 0 10px;"
>
<MaterialSelector
<MaterialSelector
ref=
"materialsSeletor"
ref=
"materialsSeletor"
@
selection-change=
"
handleMaterialSelectionChange
"
@
selection-change=
"
confirmMaterialSelect
"
:selected-material-codes=
"form.materialUuids ? [form.materialUuids] : []"
:selected-material-codes=
"form.materialUuids ? [form.materialUuids] : []"
:multiple=
"false"
:multiple=
"false"
/>
/>
</div>
</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>
</el-dialog>
<!-- 库存信息列表 -->
<!-- 库存信息列表 -->
...
@@ -335,7 +332,8 @@ export default {
...
@@ -335,7 +332,8 @@ export default {
selectedMaterialId
:
''
,
selectedMaterialId
:
''
,
selectedMaterialInfo
:
null
,
selectedMaterialInfo
:
null
,
currentSelectedRowId
:
null
,
currentSelectedRowId
:
null
,
isInitEcho
:
false
isInitEcho
:
false
,
openMaterialSelector
:
false
,
// 确保初始为关闭状态
};
};
},
},
computed
:
{
computed
:
{
...
@@ -798,53 +796,50 @@ handleSubmit() {
...
@@ -798,53 +796,50 @@ handleSubmit() {
}
}
},
},
// 修复:增强校验逻辑,增加组件存在性检查和异步同步
// 修复:增强校验逻辑,增加组件存在性检查和异步同步
confirmMaterialSelect
()
{
confirmMaterialSelect
()
{
// 1. 检查选择器组件是否存在
// 标记为“用户主动操作”
if
(
!
this
.
$refs
.
materialsSeletor
)
{
this
.
isUserInitiatedSelect
=
true
;
this
.
$message
.
error
(
'物料选择器组件加载失败,请重试'
);
return
;
}
// 2. 异步等待组件数据同步(避免取值过早)
// 1. 检查选择器组件是否存在
this
.
$nextTick
(()
=>
{
if
(
!
this
.
$refs
.
materialsSeletor
)
{
// 3. 强制触发选中数据同步
this
.
$message
.
error
(
'物料选择器组件加载失败,请重试'
);
const
selector
=
this
.
$refs
.
materialsSeletor
;
return
;
let
selectedData
=
[];
}
// 兼容组件的多种取值方式
if
(
selector
.
getSelectedMaterials
)
{
selectedData
=
selector
.
getSelectedMaterials
();
}
else
if
(
selector
.
selectedList
)
{
selectedData
=
selector
.
selectedList
;
}
else
if
(
selector
.
value
)
{
selectedData
=
selector
.
value
;
}
// 触发数据同步(覆盖事件未触发的场景)
this
.
handleMaterialSelectionChange
(
selectedData
);
// 4. 增强空值校验
// 2. 异步等待组件数据同步(避免取值过早)
if
(
!
this
.
selectedMaterialInfo
||
!
this
.
selectedMaterialId
)
{
this
.
$nextTick
(()
=>
{
this
.
$message
.
warning
(
'请选择物料后再确认'
);
// 3. 强制触发选中数据同步
return
;
const
selector
=
this
.
$refs
.
materialsSeletor
;
}
let
selectedData
=
[];
// 兼容组件的多种取值方式
if
(
selector
.
getSelectedMaterials
)
{
selectedData
=
selector
.
getSelectedMaterials
();
}
else
if
(
selector
.
selectedList
)
{
selectedData
=
selector
.
selectedList
;
}
else
if
(
selector
.
value
)
{
selectedData
=
selector
.
value
;
}
// 触发数据同步(覆盖事件未触发的场景)
this
.
handleMaterialSelectionChange
(
selectedData
);
// 5. 容错处理:确保物料ID有效
const
materialId
=
this
.
selectedMaterialInfo
.
id
||
this
.
selectedMaterialInfo
.
materialId
||
this
.
selectedMaterialInfo
.
uuid
||
''
;
if
(
!
materialId
)
{
this
.
$message
.
error
(
'选中的物料缺少有效ID,请重新选择'
);
return
;
}
// 6. 赋值并关闭弹窗
// 5. 容错处理:确保物料ID有效
this
.
$set
(
this
.
form
,
'materialId'
,
materialId
);
const
materialId
=
this
.
selectedMaterialInfo
.
id
||
this
.
selectedMaterialInfo
.
materialId
||
this
.
selectedMaterialInfo
.
uuid
||
''
;
this
.
$set
(
this
.
form
,
'materialUuids'
,
materialId
);
this
.
openMaterialSelector
=
false
;
// 7. 查询库存并校验字段
// 6. 赋值并关闭弹窗
this
.
handleMaterialIdChange
();
this
.
$set
(
this
.
form
,
'materialId'
,
materialId
);
this
.
$nextTick
(()
=>
{
this
.
$set
(
this
.
form
,
'materialUuids'
,
materialId
);
this
.
$refs
.
detailForm
?.
validateField
(
'materialId'
);
this
.
openMaterialSelector
=
false
;
});
});
// 7. 查询库存并校验字段
this
.
handleMaterialIdChange
();
this
.
$nextTick
(()
=>
{
this
.
$refs
.
detailForm
?.
validateField
(
'materialId'
);
});
// 重置标记
this
.
isUserInitiatedSelect
=
false
;
});
}
}
},
},
beforeDestroy
()
{
beforeDestroy
()
{
...
...
ruoyi-admin/src/main/resources/application-druid.yml
View file @
c7cf6243
...
@@ -13,7 +13,7 @@ spring:
...
@@ -13,7 +13,7 @@ spring:
#username: root # 数据库用户名
#username: root # 数据库用户名
#password: 'Aa123456'
#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
username
:
root
password
:
'
!QAZ2wsx#EDC2022'
password
:
'
!QAZ2wsx#EDC2022'
# 从库数据源
# 从库数据源
...
...
ruoyi-common/src/main/java/com/ruoyi/common/config/WarehouseConfig.java
View file @
c7cf6243
...
@@ -7,6 +7,6 @@ public class WarehouseConfig {
...
@@ -7,6 +7,6 @@ public class WarehouseConfig {
/**
/**
* 默认出库仓库ID(核心默认值)
* 默认出库仓库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
ruoyi-inventory/src/main/java/com/ruoyi/inventory/domain/OutboundOrderItems.java
View file @
c7cf6243
...
@@ -117,7 +117,7 @@ public class OutboundOrderItems extends BaseEntity
...
@@ -117,7 +117,7 @@ public class OutboundOrderItems extends BaseEntity
@Excel
(
name
=
"排序号"
)
@Excel
(
name
=
"排序号"
)
private
String
updateUserCode
;
private
String
updateUserCode
;
private
String
InventoryType
;
private
int
InventoryType
;
}
}
\ No newline at end of file
ruoyi-inventory/src/main/java/com/ruoyi/inventory/mapper/OutboundOrderItemsMapper.java
View file @
c7cf6243
...
@@ -74,5 +74,11 @@ public interface OutboundOrderItemsMapper
...
@@ -74,5 +74,11 @@ public interface OutboundOrderItemsMapper
public
int
batchInsertOutboundOrderItems
(
List
<
OutboundOrderItems
>
inboundOrderItems
);
public
int
batchInsertOutboundOrderItems
(
List
<
OutboundOrderItems
>
inboundOrderItems
);
public
int
batchUpdateOutboundOrderItems
(
List
<
OutboundOrderItems
>
inboundOrderItems
);
int
batchDeleteOutboundOrderItems
(
String
[]
ids
);
public
int
deleteOutboundOrderItemsByOrderId
(
String
orderId
);
}
}
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/impl/InventoryServiceImpl.java
View file @
c7cf6243
...
@@ -156,34 +156,46 @@ public class InventoryServiceImpl implements IInventoryService
...
@@ -156,34 +156,46 @@ public class InventoryServiceImpl implements IInventoryService
}
}
updateInventory
(
inventory
);
updateInventory
(
inventory
);
// 库存操作表插入数据
createInventoryOutboundLog
(
inventory
,
outboundOrderItem
,
Long
.
valueOf
(
quantity
),
SystemUtils
.
getUserName
(),
new
Date
());
InventoryTransactions
transactions
=
new
InventoryTransactions
();
transactions
.
setId
(
IdUtils
.
simpleUUID
());
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
.
setMaterialId
(
outboundOrderItem
.
getMaterialId
());
transactions
.
setWarehouseId
(
outboundOrderItem
.
getWarehouseId
());
transactions
.
setLocationId
(
outboundOrderItem
.
getLocationId
());
OutboundOrders
outboundOrders
=
outboundOrderMapper
.
selectOutboundOrdersById
(
outboundOrderItem
.
getOutboundOrderId
());
transactions
.
setOwnerId
(
outboundOrders
.
getOwnerId
());
transactions
.
setQuantityBefore
(
Long
.
valueOf
(
quantity
));
// 变更前数量
transactions
.
setQuantityAfter
(
inventory
.
getQuantity
());
// 变更后数量
transactions
.
setQuantityChange
(
outboundOrderItem
.
getActualQuantity
());
Date
nowDate
=
new
Date
();
transactions
.
setTransactionTime
(
nowDate
);
transactions
.
setOperatedBy
(
SystemUtils
.
getUserName
());
insertInventoryTransactions
.
insertInventoryTransactions
(
transactions
);
}
}
RefreshInventory
(
inventoryIds
);
RefreshInventory
(
inventoryIds
);
}
}
return
1
;
return
1
;
}
}
private
void
createInventoryOutboundLog
(
Inventory
inventory
,
OutboundOrderItems
outboundOrderItem
,
Long
deductQty
,
String
updateUser
,
Date
updateTime
)
{
InventoryTransactions
transactions
=
new
InventoryTransactions
();
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
());
// 关联出库单主表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
());
}
// 变更前后数量
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
);
// 插入日志
insertInventoryTransactions
.
insertInventoryTransactions
(
transactions
);
}
@SerialExecution
(
group
=
"inventoryRefresh"
,
fair
=
true
)
@SerialExecution
(
group
=
"inventoryRefresh"
,
fair
=
true
)
@Override
@Override
public
boolean
inventoryLockValidation
(
List
<
OutboundOrderItems
>
outboundOrderItems
)
public
boolean
inventoryLockValidation
(
List
<
OutboundOrderItems
>
outboundOrderItems
)
...
...
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/impl/OutboundOrdersServiceImpl.java
View file @
c7cf6243
...
@@ -30,9 +30,12 @@ import org.springframework.util.CollectionUtils;
...
@@ -30,9 +30,12 @@ import org.springframework.util.CollectionUtils;
/**
/**
* 出库单主Service业务层处理
* 出库单主Service业务层处理
* 核心修正:
* 核心修正:
* 1. 库存匹配Key统一为
仓库ID_物料ID_库位ID_库存类型
* 1. 库存匹配Key统一为
物料ID_库位ID_库存类型(移除仓库维度)
* 2. 确保inventoryType完整参与匹配
* 2. 确保inventoryType完整参与匹配
* 3. 库存扣减后≤0时强制设置inventory_status=0
* 3. 库存扣减后≤0时强制设置inventory_status=0
* 4. 无库位库存不足时,自动扣减同物料同库存类型的有库位库存
* 5. 导入场景下仅保留新增逻辑,移除更新相关处理
* 6. 移除所有仓库相关逻辑,仅保留物料+库位维度匹配
*
*
* @author ruoyi
* @author ruoyi
* @date 2025-12-03
* @date 2025-12-03
...
@@ -52,8 +55,6 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
...
@@ -52,8 +55,6 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
private
OwnersServiceImpl
ownersService
;
private
OwnersServiceImpl
ownersService
;
@Autowired
@Autowired
private
WarehousesServiceImpl
warehousesService
;
@Autowired
private
InventoryServiceImpl
inventoryService
;
private
InventoryServiceImpl
inventoryService
;
@Autowired
@Autowired
private
MaterialsServiceImpl
materialsService
;
private
MaterialsServiceImpl
materialsService
;
...
@@ -108,7 +109,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
...
@@ -108,7 +109,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
}
/**
/**
* 修改出库单主
* 修改出库单主
(仅保留方法定义,移除更新逻辑)
*
*
* @param outboundOrders 出库单主
* @param outboundOrders 出库单主
* @return 结果
* @return 结果
...
@@ -117,13 +118,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
...
@@ -117,13 +118,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
@Override
@Override
public
int
updateOutboundOrders
(
OutboundOrders
outboundOrders
)
public
int
updateOutboundOrders
(
OutboundOrders
outboundOrders
)
{
{
outboundOrdersMapper
.
deleteOutboundOrderItemsByOrderId
(
outboundOrders
.
getId
());
throw
new
ServiceException
(
"当前系统仅支持新增出库单,不支持修改操作"
);
outboundOrderLogMapper
.
deleteOutboundOrderLogByOrdersId
(
outboundOrders
.
getId
());
outboundOrders
.
setUpdateUserCode
(
SystemUtils
.
getUserName
());
outboundOrders
.
setUpdateTime
(
DateUtils
.
getNowDate
());
insertOutboundOrderItems
(
outboundOrders
);
return
outboundOrdersMapper
.
updateOutboundOrders
(
outboundOrders
);
}
}
/**
/**
...
@@ -184,7 +179,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
...
@@ -184,7 +179,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
outboundOrders
.
setUpdateUserCode
(
updateUser
);
outboundOrders
.
setUpdateUserCode
(
updateUser
);
outboundOrdersMapper
.
updateOutboundOrders
(
outboundOrders
);
outboundOrdersMapper
.
updateOutboundOrders
(
outboundOrders
);
// 3. 执行库存扣减(
包含inventoryType匹配 + 0值状态更新
)
// 3. 执行库存扣减(
无库位不足时扣有库位
)
this
.
deductInventory
(
outboundOrderItems
,
updateUser
,
updateTime
);
this
.
deductInventory
(
outboundOrderItems
,
updateUser
,
updateTime
);
return
1
;
return
1
;
...
@@ -192,75 +187,204 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
...
@@ -192,75 +187,204 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
/**
/**
* 核心库存扣减逻辑
* 核心库存扣减逻辑
* 1. 按 仓库ID_物料ID_库位ID_库存类型 匹配库存
* 1. 有库位:直接扣减指定库位库存
* 2. 扣减后数量≤0时设置inventory_status=0
* 2. 无库位:先扣无库位库存,不足则扣同物料同库存类型的有库位库存
* 3. 扣减后数量≤0时设置inventory_status=0
* 4. 无库位扣减有库位时,记录扣减的库位和数量(用于生成明细)
* 5. 移除仓库维度,仅按物料+库位+库存类型匹配
* @param outboundOrderItems 出库明细
* @param outboundOrderItems 出库明细
* @param updateUser 操作人
* @param updateUser 操作人
* @param updateTime 操作时间
* @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
))
{
if
(
CollectionUtils
.
isEmpty
(
outboundOrderItems
))
{
return
;
return
Collections
.
emptyMap
()
;
}
}
// 1. 预加载库存映射:Key=仓库ID_物料ID_库位ID_库存类型 Value=库存对象
Map
<
String
,
List
<
Map
<
String
,
Object
>>>
deductRecordMap
=
new
HashMap
<>();
Map
<
String
,
Inventory
>
inventoryMap
=
this
.
loadInventoryMap
();
Map
<
String
,
List
<
Inventory
>>
inventoryGroupMap
=
this
.
loadInventoryGroupMap
();
// 2. 构建扣减数量Map:Key=仓库ID_物料ID_库位ID_库存类型 Value=总扣减数量
Map
<
String
,
Long
>
deductQtyMap
=
this
.
buildDeductQtyMap
(
outboundOrderItems
);
Map
<
String
,
Long
>
deductQtyMap
=
this
.
buildDeductQtyMap
(
outboundOrderItems
);
// 3. 遍历扣减Map,执行扣减+状态更新
List
<
Inventory
>
needUpdateList
=
new
ArrayList
<>();
List
<
Inventory
>
needUpdateList
=
new
ArrayList
<>();
for
(
Map
.
Entry
<
String
,
Long
>
entry
:
deductQtyMap
.
entrySet
())
{
for
(
Map
.
Entry
<
String
,
Long
>
entry
:
deductQtyMap
.
entrySet
())
{
String
key
=
entry
.
getKey
();
String
key
=
entry
.
getKey
();
Long
d
eductQty
=
entry
.
getValue
();
Long
totalD
eductQty
=
entry
.
getValue
();
if
(
totalDeductQty
<=
0
)
continue
;
// 匹配库存
Inventory
inventory
=
inventoryMap
.
get
(
key
);
String
[]
keyParts
=
key
.
split
(
"_"
);
if
(
inventory
==
null
)
{
String
materialId
=
keyParts
.
length
>
0
?
keyParts
[
0
]
:
""
;
String
[]
keyParts
=
key
.
split
(
"_"
)
;
String
locationId
=
keyParts
.
length
>
1
?
keyParts
[
1
]
:
""
;
String
warehouseId
=
keyParts
.
length
>
0
?
keyParts
[
0
]
:
""
;
String
inventoryType
=
keyParts
.
length
>
2
?
keyParts
[
2
]
:
""
;
String
materialId
=
keyParts
.
length
>
1
?
keyParts
[
1
]
:
""
;
String
groupKey
=
String
.
join
(
"_"
,
materialId
,
inventoryType
)
;
String
locationId
=
keyParts
.
length
>
2
?
keyParts
[
2
]
:
""
;
List
<
Inventory
>
inventoryList
=
inventoryGroupMap
.
get
(
groupKey
)
;
String
inventoryType
=
keyParts
.
length
>
3
?
keyParts
[
3
]
:
""
;
if
(
CollectionUtils
.
isEmpty
(
inventoryList
))
{
throw
new
ServiceException
(
String
.
format
(
throw
new
ServiceException
(
String
.
format
(
"
仓库[%s]物料[%s]库位[%s]库存类型[%s]的库存不存在
,无法扣减"
,
"
物料[%s]库存类型[%s]无可用库存
,无法扣减"
,
warehouseId
,
materialId
,
location
Id
,
inventoryType
));
material
Id
,
inventoryType
));
}
}
// 执行数量扣减
Long
remainDeductQty
=
totalDeductQty
;
Long
currentQty
=
Optional
.
ofNullable
(
inventory
.
getQuantity
()).
orElse
(
0L
);
List
<
Map
<
String
,
Object
>>
deductRecords
=
new
ArrayList
<>();
Long
newQty
=
currentQty
-
deductQty
;
String
itemId
=
outboundOrderItems
.
stream
()
inventory
.
setQuantity
(
newQty
);
.
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
);
// 核心规则:扣减后数量≤0 → 状态置0
// 记录无库位扣减
if
(
newQty
<=
0
)
{
deductRecords
.
add
(
buildDeductRecord
(
inv
,
inventoryType
,
deductQty
));
inventory
.
setInventoryStatus
(
0L
);
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
);
// 补充审计字段
// 记录有库位扣减
inventory
.
setUpdateBy
(
updateUser
);
deductRecords
.
add
(
buildDeductRecord
(
inv
,
inventoryType
,
deductQty
));
inventory
.
setUpdateTime
(
updateTime
);
remainDeductQty
-=
deductQty
;
}
}
needUpdateList
.
add
(
inventory
);
// 最后仍有剩余(所有库存耗尽),允许扣最后一个库存为负数
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
);
}
}
}
//
4.
批量更新库存
// 批量更新库存
if
(!
needUpdateList
.
isEmpty
())
{
if
(!
needUpdateList
.
isEmpty
())
{
inventoryMapper
.
batchUpdateInventory
(
needUpdateList
);
inventoryMapper
.
batchUpdateInventory
(
needUpdateList
);
// 刷新库存缓存
inventoryService
.
RefreshInventory
(
needUpdateList
.
stream
().
map
(
Inventory:
:
getId
).
distinct
().
collect
(
Collectors
.
toList
()));
List
<
String
>
inventoryIds
=
needUpdateList
.
stream
()
.
map
(
Inventory:
:
getId
)
.
distinct
()
.
collect
(
Collectors
.
toList
());
inventoryService
.
RefreshInventory
(
inventoryIds
);
}
}
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
;
}
}
/**
/**
* 预加载库存Map
* 扣减单个库存的数量
* Key规则:仓库ID_物料ID_库位ID_库存类型
* @param inventory 库存对象
* @param deductQty 待扣减数量
* @param updateUser 操作人
* @param updateTime 操作时间
* @return 剩余未扣减数量
*/
*/
private
Map
<
String
,
Inventory
>
loadInventoryMap
()
{
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
);
}
// 补充审计字段
inventory
.
setUpdateBy
(
updateUser
);
inventory
.
setUpdateTime
(
updateTime
);
// 返回剩余未扣减数量(扣减全部,剩余为0)
return
0L
;
// 原逻辑:deductQty - canDeductQty
}
/**
* 预加载库存分组Map
* Key=物料ID_库存类型 Value=该维度下所有库位的库存列表(含无库位)
* 移除仓库维度
*/
private
Map
<
String
,
List
<
Inventory
>>
loadInventoryGroupMap
()
{
Inventory
query
=
new
Inventory
();
Inventory
query
=
new
Inventory
();
query
.
setInventoryStatus
(
1L
);
query
.
setInventoryStatus
(
1L
);
query
.
setIsUsed
(
1L
);
query
.
setIsUsed
(
1L
);
...
@@ -270,35 +394,26 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
...
@@ -270,35 +394,26 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
return
Collections
.
emptyMap
();
return
Collections
.
emptyMap
();
}
}
Map
<
String
,
Inventory
>
inventoryMap
=
new
HashMap
<>();
// 按「物料ID_库存类型」分组(移除仓库维度)
for
(
Inventory
inv
:
inventoryList
)
{
return
inventoryList
.
stream
()
String
key
=
String
.
join
(
"_"
,
.
collect
(
Collectors
.
groupingBy
(
Optional
.
ofNullable
(
inv
.
getWarehousesId
()).
orElse
(
""
),
inv
->
String
.
join
(
"_"
,
Optional
.
ofNullable
(
inv
.
getMaterialId
()).
orElse
(
""
),
Optional
.
ofNullable
(
inv
.
getMaterialId
()).
orElse
(
""
),
Optional
.
ofNullable
(
inv
.
getLocationId
()).
orElse
(
""
),
Optional
.
ofNullable
(
inv
.
getInventoryType
()).
map
(
String:
:
valueOf
).
orElse
(
""
)
Optional
.
ofNullable
(
inv
.
getInventoryType
()).
map
(
String:
:
valueOf
).
orElse
(
""
)
),
);
HashMap:
:
new
,
// 重复key保留第一个(避免覆盖)
Collectors
.
toList
()
if
(!
inventoryMap
.
containsKey
(
key
))
{
));
inventoryMap
.
put
(
key
,
inv
);
}
}
return
inventoryMap
;
}
}
/**
/**
* 构建扣减数量Map
* 构建扣减数量Map
* Key规则:
仓库ID_物料ID_库位ID_库存类型(和库存Map完全对齐
)
* Key规则:
物料ID_库位ID_库存类型(移除仓库维度
)
*/
*/
private
Map
<
String
,
Long
>
buildDeductQtyMap
(
List
<
OutboundOrderItems
>
items
)
{
private
Map
<
String
,
Long
>
buildDeductQtyMap
(
List
<
OutboundOrderItems
>
items
)
{
Map
<
String
,
Long
>
deductQtyMap
=
new
HashMap
<>();
Map
<
String
,
Long
>
deductQtyMap
=
new
HashMap
<>();
for
(
OutboundOrderItems
item
:
items
)
{
for
(
OutboundOrderItems
item
:
items
)
{
String
key
=
String
.
join
(
"_"
,
String
key
=
buildDeductKey
(
item
);
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
);
Long
qty
=
Optional
.
ofNullable
(
item
.
getActualQuantity
()).
orElse
(
0L
);
deductQtyMap
.
put
(
key
,
deductQtyMap
.
getOrDefault
(
key
,
0L
)
+
qty
);
deductQtyMap
.
put
(
key
,
deductQtyMap
.
getOrDefault
(
key
,
0L
)
+
qty
);
...
@@ -306,6 +421,18 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
...
@@ -306,6 +421,18 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
return
deductQtyMap
;
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
@Override
public
List
<
Map
<
String
,
String
>>
outboundOrdersTopTenByQuantity
()
{
public
List
<
Map
<
String
,
String
>>
outboundOrdersTopTenByQuantity
()
{
return
outboundOrdersMapper
.
SelectOutboundOrdersMaterialsTopTenByQuantity
();
return
outboundOrdersMapper
.
SelectOutboundOrdersMaterialsTopTenByQuantity
();
...
@@ -392,10 +519,9 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
...
@@ -392,10 +519,9 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
Map
<
String
,
List
<
OutboundOrderItems
>>
validItemMap
=
new
HashMap
<>();
Map
<
String
,
List
<
OutboundOrderItems
>>
validItemMap
=
new
HashMap
<>();
boolean
hasValidateError
=
false
;
boolean
hasValidateError
=
false
;
// 3. 预加载映射缓存
// 3. 预加载映射缓存
(移除仓库相关映射)
Map
<
String
,
String
>
sapToMaterialIdMap
=
loadSapToMaterialIdMap
();
Map
<
String
,
String
>
sapToMaterialIdMap
=
loadSapToMaterialIdMap
();
Map
<
String
,
String
>
locationNameToIdMap
=
loadLocationNameToIdMap
();
Map
<
String
,
String
>
locationNameToIdMap
=
loadLocationNameToIdMap
();
Map
<
String
,
String
>
warehouseNameToIdMap
=
loadWarehouseNameToIdMap
();
Map
<
String
,
String
>
ownerNameToIdMap
=
loadOwnerNameToIdMap
();
Map
<
String
,
String
>
ownerNameToIdMap
=
loadOwnerNameToIdMap
();
Map
<
String
,
AbstractMap
.
SimpleEntry
<
String
,
Long
>>
inventoryTOIdMap
=
loadInventoryTOIdMap
();
Map
<
String
,
AbstractMap
.
SimpleEntry
<
String
,
Long
>>
inventoryTOIdMap
=
loadInventoryTOIdMap
();
...
@@ -404,7 +530,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
...
@@ -404,7 +530,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
.
filter
(
vo
->
StringUtils
.
isNotBlank
(
vo
.
getOrderId
()))
.
filter
(
vo
->
StringUtils
.
isNotBlank
(
vo
.
getOrderId
()))
.
collect
(
Collectors
.
groupingBy
(
OutboundTemplateVO:
:
getOrderId
));
.
collect
(
Collectors
.
groupingBy
(
OutboundTemplateVO:
:
getOrderId
));
// 5. 数据验证
// 5. 数据验证
(仅保留新增逻辑,检测到已存在则抛异常)
for
(
Map
.
Entry
<
String
,
List
<
OutboundTemplateVO
>>
entry
:
orderGroupMap
.
entrySet
())
{
for
(
Map
.
Entry
<
String
,
List
<
OutboundTemplateVO
>>
entry
:
orderGroupMap
.
entrySet
())
{
String
orderId
=
entry
.
getKey
();
String
orderId
=
entry
.
getKey
();
List
<
OutboundTemplateVO
>
voList
=
entry
.
getValue
();
List
<
OutboundTemplateVO
>
voList
=
entry
.
getValue
();
...
@@ -413,71 +539,59 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
...
@@ -413,71 +539,59 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
try
{
try
{
OutboundTemplateVO
firstVO
=
voList
.
get
(
0
);
OutboundTemplateVO
firstVO
=
voList
.
get
(
0
);
// 检查出库单是否已存在
// 检查出库单是否已存在
(仅新增,存在则抛异常)
OutboundOrders
outboundOrdersQuery
=
new
OutboundOrders
();
OutboundOrders
outboundOrdersQuery
=
new
OutboundOrders
();
outboundOrdersQuery
.
setOrderId
(
orderId
);
outboundOrdersQuery
.
setOrderId
(
orderId
);
List
<
OutboundOrders
>
existMains
=
outboundOrdersMapper
.
selectOutboundOrdersList
(
outboundOrdersQuery
);
List
<
OutboundOrders
>
existMains
=
outboundOrdersMapper
.
selectOutboundOrdersList
(
outboundOrdersQuery
);
if
(
existMains
!=
null
&&
existMains
.
size
()
>
1
)
{
if
(
existMains
!=
null
&&
!
existMains
.
isEmpty
()
)
{
throw
new
ServiceException
(
String
.
format
(
"入库单号【%s】
存在多条重复主表数据
"
,
orderId
));
throw
new
ServiceException
(
String
.
format
(
"入库单号【%s】
已存在,当前系统仅支持新增,不支持更新
"
,
orderId
));
}
}
OutboundOrders
existMain
=
CollectionUtils
.
isEmpty
(
existMains
)
?
null
:
existMains
.
get
(
0
);
// 仅新增逻辑:构建新出库单主数据
if
(
existMain
!=
null
)
{
mainDO
=
new
OutboundOrders
();
if
(!
Boolean
.
TRUE
.
equals
(
isUpdateSupport
))
{
BeanUtils
.
copyProperties
(
firstVO
,
mainDO
,
throw
new
ServiceException
(
String
.
format
(
"入库单号【%s】已存在,且不支持更新"
,
orderId
));
"sapNo"
,
"materialName"
,
"plannedQuantity"
,
"actualQuantity"
,
"plannedPackages"
,
"materialUnit"
,
"materialRemark"
,
"warehouseName"
,
"warehouseId"
);
// 货主校验
String
ownerName
=
firstVO
.
getOwnerName
();
String
ownerId
=
firstVO
.
getOwnerId
();
if
(
StringUtils
.
isNotBlank
(
ownerName
))
{
String
mappedOwnerId
=
ownerNameToIdMap
.
get
(
ownerName
.
trim
());
if
(
StringUtils
.
isBlank
(
mappedOwnerId
))
{
throw
new
ServiceException
(
String
.
format
(
"业主【%s】不存在,无法新增入库单【%s】"
,
ownerName
,
orderId
));
}
}
mainDO
=
existMain
;
if
(
ownerId
!=
null
&&
!
ownerId
.
equals
(
mappedOwnerId
))
{
BeanUtils
.
copyProperties
(
firstVO
,
mainDO
,
"id"
,
"createBy"
,
"createTime"
);
throw
new
ServiceException
(
String
.
format
(
"入库单号【%s】的业主ID【%s】与业主名称【%s】不匹配"
,
mainDO
.
setUpdateBy
(
operId
);
orderId
,
ownerId
,
ownerName
));
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
))
{
String
mappedOwnerId
=
ownerNameToIdMap
.
get
(
ownerName
.
trim
());
if
(
StringUtils
.
isBlank
(
mappedOwnerId
))
{
throw
new
ServiceException
(
String
.
format
(
"业主【%s】不存在,无法新增入库单【%s】"
,
ownerName
,
orderId
));
}
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
)
{
Owners
ownerById
=
ownersService
.
selectOwnersById
(
ownerId
);
if
(
ownerById
==
null
)
{
throw
new
ServiceException
(
String
.
format
(
"入库单号【%s】的业主ID【%s】不存在"
,
orderId
,
ownerId
));
}
mainDO
.
setOwnerId
(
ownerId
);
}
else
{
throw
new
ServiceException
(
String
.
format
(
"入库单号【%s】的业主名称/ID不能为空"
,
orderId
));
}
}
mainDO
.
setOwnerId
(
mappedOwnerId
);
// 填充主表必填字段
}
else
if
(
ownerId
!=
null
)
{
mainDO
.
setId
(
UUID
.
randomUUID
().
toString
());
Owners
ownerById
=
ownersService
.
selectOwnersById
(
ownerId
);
mainDO
.
setOrderId
(
orderId
);
if
(
ownerById
==
null
)
{
mainDO
.
setOrderStatus
(
2L
);
throw
new
ServiceException
(
String
.
format
(
"入库单号【%s】的业主ID【%s】不存在"
,
mainDO
.
setCreateBy
(
operId
);
orderId
,
ownerId
));
mainDO
.
setCreateTime
(
now
);
}
mainDO
.
setOrderTypeId
(
String
.
valueOf
(
orderType
));
mainDO
.
setOwnerId
(
ownerId
);
mainDO
.
setCreateUserCode
(
operId
);
}
else
{
mainDO
.
setUpdateBy
(
operId
);
throw
new
ServiceException
(
String
.
format
(
"入库单号【%s】的业主名称/ID不能为空"
,
orderId
));
mainDO
.
setUpdateTime
(
now
);
mainDO
.
setUpdateUserCode
(
operId
);
mainDO
.
setSortNo
(
Optional
.
ofNullable
(
mainDO
.
getSortNo
()).
orElse
(
0L
));
}
}
// 填充主表必填字段
mainDO
.
setId
(
UUID
.
randomUUID
().
toString
());
mainDO
.
setOrderId
(
orderId
);
mainDO
.
setOrderStatus
(
2L
);
mainDO
.
setCreateBy
(
operId
);
mainDO
.
setCreateTime
(
now
);
mainDO
.
setOrderTypeId
(
String
.
valueOf
(
orderType
));
mainDO
.
setCreateUserCode
(
operId
);
mainDO
.
setUpdateBy
(
operId
);
mainDO
.
setUpdateTime
(
now
);
mainDO
.
setUpdateUserCode
(
operId
);
mainDO
.
setSortNo
(
Optional
.
ofNullable
(
mainDO
.
getSortNo
()).
orElse
(
0L
));
// 明细校验
// 明细校验
for
(
int
i
=
0
;
i
<
voList
.
size
();
i
++)
{
for
(
int
i
=
0
;
i
<
voList
.
size
();
i
++)
{
OutboundTemplateVO
vo
=
voList
.
get
(
i
);
OutboundTemplateVO
vo
=
voList
.
get
(
i
);
...
@@ -485,7 +599,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
...
@@ -485,7 +599,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
OutboundOrderItems
itemDO
=
new
OutboundOrderItems
();
OutboundOrderItems
itemDO
=
new
OutboundOrderItems
();
BeanUtils
.
copyProperties
(
vo
,
itemDO
,
BeanUtils
.
copyProperties
(
vo
,
itemDO
,
"orderId"
,
"systemNo"
,
"orderTypeId"
,
"batchId"
);
"orderId"
,
"systemNo"
,
"orderTypeId"
,
"batchId"
,
"warehouseName"
,
"warehouseId"
);
// 填充明细必填字段
// 填充明细必填字段
itemDO
.
setId
(
UUID
.
randomUUID
().
toString
());
itemDO
.
setId
(
UUID
.
randomUUID
().
toString
());
...
@@ -496,6 +610,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
...
@@ -496,6 +610,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
itemDO
.
setCreateTime
(
now
);
itemDO
.
setCreateTime
(
now
);
itemDO
.
setCreateUserCode
(
operId
);
itemDO
.
setCreateUserCode
(
operId
);
itemDO
.
setSortNo
(
0L
);
itemDO
.
setSortNo
(
0L
);
itemDO
.
setItemStatus
(
3L
);
// 设置为已出库状态
// 物料SAP校验
// 物料SAP校验
String
sapNo
=
vo
.
getSapNo
()
!=
null
?
vo
.
getSapNo
().
trim
()
:
""
;
String
sapNo
=
vo
.
getSapNo
()
!=
null
?
vo
.
getSapNo
().
trim
()
:
""
;
...
@@ -512,38 +627,35 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
...
@@ -512,38 +627,35 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
// 库位校验
// 库位校验
String
locationName
=
vo
.
getLocationName
()
!=
null
?
vo
.
getLocationName
().
trim
()
:
""
;
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
);
String
locationId
=
locationNameToIdMap
.
get
(
locationName
);
if
(
StringUtils
.
isBlank
(
locationId
))
{
if
(
StringUtils
.
isNotBlank
(
locationName
))
{
throw
new
ServiceException
(
String
.
format
(
"入库单号【%s】第%d条明细的库位【%s】不存在"
,
if
(
StringUtils
.
isBlank
(
locationId
))
{
orderId
,
lineNo
,
locationName
));
throw
new
ServiceException
(
String
.
format
(
"入库单号【%s】第%d条明细的库位【%s】不存在"
,
}
orderId
,
lineNo
,
locationName
));
itemDO
.
setLocationId
(
locationId
);
}
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
);
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
);
itemDO
.
setActualQuantity
(
Optional
.
ofNullable
(
vo
.
getActualQuantity
()).
orElse
(
0L
));
if
(
inventoryEntry
==
null
)
{
throw
new
ServiceException
(
String
.
format
(
// 库存校验(包含inventoryType)- 有库位才校验,无库位直接跳过
"入库单号【%s】第%d条明细:仓库【%s】+物料【%s】+库位【%s】+库存类型【%s】组合的库存记录不存在"
,
if
(
StringUtils
.
isNotBlank
(
locationName
))
{
orderId
,
lineNo
,
vo
.
getWarehouseName
(),
vo
.
getMaterialName
(),
vo
.
getLocationName
(),
inventoryTypeStr
));
String
inventoryTypeStr
=
Optional
.
ofNullable
(
orderType
).
map
(
String:
:
valueOf
).
orElse
(
""
);
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】组合的库存记录不存在"
,
orderId
,
lineNo
,
vo
.
getMaterialName
(),
vo
.
getLocationName
(),
inventoryTypeStr
));
}
itemDO
.
setInventoryId
(
inventoryEntry
.
getKey
());
}
}
itemDO
.
setInventoryId
(
inventoryEntry
.
getKey
());
// 无库位时不校验库存,也不设置inventoryId
itemDO
.
setInventoryType
(
inventoryTypeStr
);
// 填充库存类型
itemDO
.
setItemStatus
(
3L
);
itemDOList
.
add
(
itemDO
);
itemDOList
.
add
(
itemDO
);
}
}
...
@@ -563,18 +675,21 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
...
@@ -563,18 +675,21 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
throw
new
ServiceException
(
String
.
format
(
"验证失败,导入终止!失败详情:%s"
,
failureMsg
.
toString
()));
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
())
{
for
(
Map
.
Entry
<
String
,
OutboundOrders
>
entry
:
validMainMap
.
entrySet
())
{
String
orderId
=
entry
.
getKey
();
String
orderId
=
entry
.
getKey
();
OutboundOrders
mainDO
=
entry
.
getValue
();
OutboundOrders
mainDO
=
entry
.
getValue
();
List
<
OutboundOrderItems
>
itemDOList
=
validItemMap
.
get
(
orderId
);
List
<
OutboundOrderItems
>
itemDOList
=
validItemMap
.
get
(
orderId
);
outboundOrdersMapper
.
insertOutboundOrders
(
mainDO
);
// 仅新增主单
totalMainSuccess
++
;
outboundOrdersMapper
.
insertOutboundOrders
(
mainDO
)
;
successMsg
.
append
(
String
.
format
(
"入库单号【%s】已新增;\n"
,
orderId
))
;
totalMainSuccess
++
;
successMsg
.
append
(
String
.
format
(
"入库单号【%s】已新增;\n"
,
orderId
));
// 插入明细(仅新增,无删除操作)
if
(!
CollectionUtils
.
isEmpty
(
itemDOList
))
{
if
(!
CollectionUtils
.
isEmpty
(
itemDOList
))
{
// 批量插入新明细
int
itemSuccess
=
outboundOrderItemsMapper
.
batchInsertOutboundOrderItems
(
itemDOList
);
int
itemSuccess
=
outboundOrderItemsMapper
.
batchInsertOutboundOrderItems
(
itemDOList
);
totalItemSuccess
+=
itemSuccess
;
totalItemSuccess
+=
itemSuccess
;
int
itemFail
=
itemDOList
.
size
()
-
itemSuccess
;
int
itemFail
=
itemDOList
.
size
()
-
itemSuccess
;
...
@@ -584,46 +699,115 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
...
@@ -584,46 +699,115 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
if
(
itemFail
>
0
)
{
if
(
itemFail
>
0
)
{
failureMsg
.
append
(
String
.
format
(
"入库单号【%s】有%d条物料明细导入失败;\n"
,
orderId
,
itemFail
));
failureMsg
.
append
(
String
.
format
(
"入库单号【%s】有%d条物料明细导入失败;\n"
,
orderId
,
itemFail
));
}
}
allItemListMap
.
put
(
orderId
,
itemDOList
);
}
}
}
}
// 8. 事务提交后执行库存扣减
// 8. 执行库存扣减并处理无库位明细拆分
if
(
TransactionSynchronizationManager
.
isSynchronizationActive
())
{
for
(
Map
.
Entry
<
String
,
List
<
OutboundOrderItems
>>
entry
:
allItemListMap
.
entrySet
())
{
Map
<
String
,
List
<
OutboundOrderItems
>>
finalValidItemMap
=
new
HashMap
<>(
validItemMap
);
List
<
OutboundOrderItems
>
itemList
=
entry
.
getValue
();
String
finalOperId
=
operId
;
// 执行库存扣减,获取无库位明细的扣减记录
Date
finalNow
=
now
;
Map
<
String
,
List
<
Map
<
String
,
Object
>>>
deductRecordMap
=
deductInventory
(
itemList
,
operId
,
now
);
TransactionSynchronizationManager
.
registerSynchronization
(
new
TransactionSynchronization
()
{
// 处理无库位明细拆分:整理有效明细后统一插入
@Override
if
(!
deductRecordMap
.
isEmpty
())
{
public
void
afterCommit
()
{
handleNoLocationItemSplit
(
itemList
,
deductRecordMap
,
operId
,
now
);
for
(
List
<
OutboundOrderItems
>
itemList
:
finalValidItemMap
.
values
())
{
deductInventory
(
itemList
,
finalOperId
,
finalNow
);
}
}
});
}
else
{
for
(
List
<
OutboundOrderItems
>
itemList
:
validItemMap
.
values
())
{
deductInventory
(
itemList
,
operId
,
now
);
}
}
}
}
// 9. 结果汇总
// 9. 结果汇总
if
(
totalMainFailure
>
0
||
totalItemFailure
>
0
)
{
if
(
totalMainFailure
>
0
||
totalItemFailure
>
0
)
{
String
finalFailureMsg
=
String
.
format
(
String
finalFailureMsg
=
String
.
format
(
"导入结果:成功新增
/更新
%d个入库单,失败%d个;成功导入%d条明细,失败%d条。失败详情:%s"
,
"导入结果:成功新增%d个入库单,失败%d个;成功导入%d条明细,失败%d条。失败详情:%s"
,
totalMainSuccess
,
totalMainFailure
,
totalItemSuccess
,
totalItemFailure
,
failureMsg
.
toString
()
totalMainSuccess
,
totalMainFailure
,
totalItemSuccess
,
totalItemFailure
,
failureMsg
.
toString
()
);
);
throw
new
ServiceException
(
finalFailureMsg
);
throw
new
ServiceException
(
finalFailureMsg
);
}
else
{
}
else
{
String
finalSuccessMsg
=
String
.
format
(
String
finalSuccessMsg
=
String
.
format
(
"恭喜您,数据已全部导入成功!共
处理
%d个入库单,成功导入%d条物料明细。详情:%s"
,
"恭喜您,数据已全部导入成功!共
新增
%d个入库单,成功导入%d条物料明细。详情:%s"
,
totalMainSuccess
,
totalItemSuccess
,
successMsg
.
toString
()
totalMainSuccess
,
totalItemSuccess
,
successMsg
.
toString
()
);
);
return
finalSuccessMsg
;
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
()
{
private
Map
<
String
,
String
>
loadSapToMaterialIdMap
()
{
List
<
Materials
>
materialsList
=
materialsService
.
selectMaterialsList
(
new
Materials
());
List
<
Materials
>
materialsList
=
materialsService
.
selectMaterialsList
(
new
Materials
());
if
(
CollectionUtils
.
isEmpty
(
materialsList
))
{
if
(
CollectionUtils
.
isEmpty
(
materialsList
))
{
...
@@ -654,22 +838,6 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
...
@@ -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
()
{
private
Map
<
String
,
String
>
loadOwnerNameToIdMap
()
{
List
<
Owners
>
ownerList
=
ownersService
.
selectOwnersList
(
new
Owners
());
List
<
Owners
>
ownerList
=
ownersService
.
selectOwnersList
(
new
Owners
());
if
(
CollectionUtils
.
isEmpty
(
ownerList
))
{
if
(
CollectionUtils
.
isEmpty
(
ownerList
))
{
...
@@ -684,6 +852,10 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
...
@@ -684,6 +852,10 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
));
));
}
}
/**
* 加载库存映射Map(移除仓库维度)
* Key=物料ID_库位ID_库存类型
*/
private
Map
<
String
,
AbstractMap
.
SimpleEntry
<
String
,
Long
>>
loadInventoryTOIdMap
()
{
private
Map
<
String
,
AbstractMap
.
SimpleEntry
<
String
,
Long
>>
loadInventoryTOIdMap
()
{
Inventory
inventory
=
new
Inventory
();
Inventory
inventory
=
new
Inventory
();
inventory
.
setInventoryStatus
(
1L
);
inventory
.
setInventoryStatus
(
1L
);
...
@@ -695,14 +867,12 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
...
@@ -695,14 +867,12 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
}
Map
<
String
,
AbstractMap
.
SimpleEntry
<
String
,
Long
>>
emptyLocationMap
=
inventoryList
.
stream
()
Map
<
String
,
AbstractMap
.
SimpleEntry
<
String
,
Long
>>
emptyLocationMap
=
inventoryList
.
stream
()
.
filter
(
inv
->
StringUtils
.
isNotBlank
(
inv
.
getWarehousesId
())
.
filter
(
inv
->
StringUtils
.
isNotBlank
(
inv
.
getMaterialId
())
&&
StringUtils
.
isNotBlank
(
inv
.
getMaterialId
())
&&
inv
.
getInventoryType
()
!=
null
&&
inv
.
getInventoryType
()
!=
null
&&
StringUtils
.
isNotBlank
(
inv
.
getId
())
&&
StringUtils
.
isNotBlank
(
inv
.
getId
())
&&
StringUtils
.
isBlank
(
inv
.
getLocationId
()))
&&
StringUtils
.
isBlank
(
inv
.
getLocationId
()))
.
collect
(
Collectors
.
toMap
(
.
collect
(
Collectors
.
toMap
(
inv
->
String
.
join
(
"_"
,
inv
->
String
.
join
(
"_"
,
inv
.
getWarehousesId
().
trim
(),
inv
.
getMaterialId
().
trim
(),
inv
.
getMaterialId
().
trim
(),
""
,
inv
.
getInventoryType
().
toString
()),
""
,
inv
.
getInventoryType
().
toString
()),
inv
->
new
AbstractMap
.
SimpleEntry
<>(
inv
.
getId
().
trim
(),
Optional
.
ofNullable
(
inv
.
getQuantity
()).
orElse
(
0L
)),
inv
->
new
AbstractMap
.
SimpleEntry
<>(
inv
.
getId
().
trim
(),
Optional
.
ofNullable
(
inv
.
getQuantity
()).
orElse
(
0L
)),
...
@@ -711,14 +881,12 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
...
@@ -711,14 +881,12 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
));
));
Map
<
String
,
AbstractMap
.
SimpleEntry
<
String
,
Long
>>
nonEmptyLocationMap
=
inventoryList
.
stream
()
Map
<
String
,
AbstractMap
.
SimpleEntry
<
String
,
Long
>>
nonEmptyLocationMap
=
inventoryList
.
stream
()
.
filter
(
inv
->
StringUtils
.
isNotBlank
(
inv
.
getWarehousesId
())
.
filter
(
inv
->
StringUtils
.
isNotBlank
(
inv
.
getMaterialId
())
&&
StringUtils
.
isNotBlank
(
inv
.
getMaterialId
())
&&
StringUtils
.
isNotBlank
(
inv
.
getLocationId
())
&&
StringUtils
.
isNotBlank
(
inv
.
getLocationId
())
&&
StringUtils
.
isNotBlank
(
inv
.
getId
())
&&
StringUtils
.
isNotBlank
(
inv
.
getId
())
&&
inv
.
getInventoryType
()
!=
null
)
&&
inv
.
getInventoryType
()
!=
null
)
.
collect
(
Collectors
.
toMap
(
.
collect
(
Collectors
.
toMap
(
inv
->
String
.
join
(
"_"
,
inv
->
String
.
join
(
"_"
,
inv
.
getWarehousesId
().
trim
(),
inv
.
getMaterialId
().
trim
(),
inv
.
getMaterialId
().
trim
(),
inv
.
getLocationId
().
trim
(),
inv
.
getLocationId
().
trim
(),
inv
.
getInventoryType
().
toString
()),
inv
.
getInventoryType
().
toString
()),
...
...
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/impl/StorageLocationsServiceImpl.java
View file @
c7cf6243
...
@@ -128,6 +128,7 @@ public class StorageLocationsServiceImpl implements IStorageLocationsService
...
@@ -128,6 +128,7 @@ public class StorageLocationsServiceImpl implements IStorageLocationsService
storageLocations
.
setId
(
LocationsID
);
storageLocations
.
setId
(
LocationsID
);
storageLocations
.
setCreateTime
(
DateUtils
.
getNowDate
());
storageLocations
.
setCreateTime
(
DateUtils
.
getNowDate
());
storageLocations
.
setCreateUserCode
(
String
.
valueOf
(
SecurityUtils
.
getUserId
()));
storageLocations
.
setCreateUserCode
(
String
.
valueOf
(
SecurityUtils
.
getUserId
()));
storageLocations
.
setWarehousesId
(
WarehouseConfig
.
DEFAULT_WAREHOUSE_ID
);
storageLocationsCategory
.
setLocationCode
(
LocationsID
);
storageLocationsCategory
.
setLocationCode
(
LocationsID
);
...
...
ruoyi-inventory/src/main/resources/mapper/inventory/InventoryMapper.xml
View file @
c7cf6243
...
@@ -586,4 +586,17 @@ and inventory_status = '1'
...
@@ -586,4 +586,17 @@ and inventory_status = '1'
group by m.id, m.material_name
group by m.id, m.material_name
order by value desc, m.material_name asc
order by value desc, m.material_name asc
</select>
</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>
</mapper>
\ No newline at end of file
ruoyi-inventory/src/main/resources/mapper/inventory/OutboundOrderItemsMapper.xml
View file @
c7cf6243
...
@@ -367,6 +367,9 @@
...
@@ -367,6 +367,9 @@
</foreach>
</foreach>
</update>
</update>
<delete
id=
"deleteOutboundOrderItemsByOrderId"
parameterType=
"String"
>
delete from outbound_order_items where outbound_order_id = #{orderId}
</delete>
<!-- 单条删除:逻辑删除 -->
<!-- 单条删除:逻辑删除 -->
<update
id=
"deleteOutboundOrderItemsById"
parameterType=
"String"
>
<update
id=
"deleteOutboundOrderItemsById"
parameterType=
"String"
>
update outbound_order_items
update outbound_order_items
...
@@ -432,4 +435,39 @@
...
@@ -432,4 +435,39 @@
)
)
</foreach>
</foreach>
</insert>
</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>
</mapper>
\ No newline at end of file
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论