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
15102e6e
Commit
15102e6e
authored
Dec 05, 2025
by
yubin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
出库
parent
389f53a1
隐藏空白字符变更
内嵌
并排
正在显示
25 个修改的文件
包含
1031 行增加
和
451 行删除
+1031
-451
ruoyi-admin-vue/src/api/inventory/inventory.js
+9
-0
ruoyi-admin-vue/src/api/inventory/orders.js
+9
-0
ruoyi-admin-vue/src/views/inventory/orders/OutboundOrderFormWithItems.vue
+204
-214
ruoyi-admin-vue/src/views/inventory/orders/index.vue
+279
-112
ruoyi-admin/src/main/java/com/ruoyi/web/controller/inventory/InboundOrderItemsController.java
+1
-2
ruoyi-common/pom.xml
+4
-0
ruoyi-common/src/main/java/com/ruoyi/common/annotation/SerialExecution.java
+25
-0
ruoyi-common/src/main/java/com/ruoyi/common/aspectj/SerialExecutionAspect.java
+72
-0
ruoyi-inventory/src/main/java/com/ruoyi/inventory/controller/OutboundOrderItemsController.java
+21
-2
ruoyi-inventory/src/main/java/com/ruoyi/inventory/controller/OutboundOrdersController.java
+13
-0
ruoyi-inventory/src/main/java/com/ruoyi/inventory/domain/OutboundOrderItems.java
+14
-1
ruoyi-inventory/src/main/java/com/ruoyi/inventory/domain/OutboundOrderLog.java
+13
-1
ruoyi-inventory/src/main/java/com/ruoyi/inventory/mapper/InventoryMapper.java
+3
-1
ruoyi-inventory/src/main/java/com/ruoyi/inventory/mapper/OutboundOrderLogMapper.java
+23
-1
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/IInventoryService.java
+20
-9
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/IOutboundOrderLogService.java
+0
-1
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/IOutboundOrdersService.java
+9
-0
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/impl/InventoryServiceImpl.java
+82
-16
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/impl/OutboundOrderItemsServiceImpl.java
+2
-16
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/impl/OutboundOrderLogServiceImpl.java
+0
-4
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/impl/OutboundOrdersServiceImpl.java
+104
-21
ruoyi-inventory/src/main/resources/mapper/inventory/InventoryMapper.xml
+9
-2
ruoyi-inventory/src/main/resources/mapper/inventory/OutboundOrderItemsMapper.xml
+6
-1
ruoyi-inventory/src/main/resources/mapper/inventory/OutboundOrderLogMapper.xml
+58
-9
ruoyi-inventory/src/main/resources/mapper/inventory/OutboundOrdersMapper.xml
+51
-38
没有找到文件。
ruoyi-admin-vue/src/api/inventory/inventory.js
View file @
15102e6e
...
...
@@ -68,3 +68,12 @@ export function listInventoryByMaterialId(materialId) {
params
:
{
materialId
:
materialId
}
})
}
export
function
Ship
(
data
)
{
return
request
({
url
:
'/inventory/inventory/ship'
,
method
:
'post'
,
data
:
data
})
}
ruoyi-admin-vue/src/api/inventory/orders.js
View file @
15102e6e
...
...
@@ -51,3 +51,11 @@ export function listWarehouseInventory(warehouseId) {
params
:
{
warehouseId
:
warehouseId
}
})
}
export
function
ship
(
data
)
{
return
request
({
url
:
'/inventory/orders/ship'
,
method
:
'post'
,
data
:
data
})
}
\ No newline at end of file
ruoyi-admin-vue/src/views/inventory/orders/OutboundOrderFormWithItems.vue
View file @
15102e6e
...
...
@@ -66,7 +66,7 @@
</el-col>
</el-row>
<!-- 库存信息列表 -->
<!-- 库存信息列表 -
添加库存ID列 -
->
<el-row
v-if=
"form.materialId && form.materialId.trim()"
style=
"margin: 10px 0;"
>
<el-col
:span=
"24"
>
<div
style=
"margin-bottom: 8px; font-weight: 600; color: #1989fa;"
>
...
...
@@ -81,11 +81,10 @@
highlight-current-row
stripe
empty-text=
"暂无库存数据"
@
selection-change=
"handleSelectionChange"
@
row-click=
"handleRowClick"
:row-key=
"item => item.id"
>
<el-table-column
type=
"selection"
width=
"55"
/>
<el-table-column
prop=
"id"
label=
"库存ID"
width=
"100"
/>
<el-table-column
prop=
"id"
label=
"库存ID"
width=
"100"
/>
<el-table-column
prop=
"materialId"
label=
"货物ID"
width=
"100"
/>
<el-table-column
prop=
"batchId"
label=
"批次ID"
width=
"100"
/>
<el-table-column
prop=
"orderId"
label=
"订单ID"
width=
"120"
/>
...
...
@@ -107,31 +106,20 @@
{{
(
scope
.
row
.
quantity
||
0
)
-
(
scope
.
row
.
lockedQuantity
||
0
)
}}
</
template
>
</el-table-column>
<el-table-column
label=
"选择数量"
width=
"120"
>
<!-- 实际数量:填写即视为选中(原选择数量) -->
<el-table-column
label=
"实际数量"
width=
"120"
>
<
template
slot-scope=
"scope"
>
<el-input
v-model
.
number=
"scope.row.
selectedQ
ty"
v-model
.
number=
"scope.row.
actualQuanti
ty"
type=
"number"
size=
"mini"
min=
"1"
:max=
"(scope.row.quantity || 0) - (scope.row.lockedQuantity || 0)"
@
input=
"handleRow
QtyInput(scope.row
)"
@
input=
"handleRow
ActualQtyInput(scope.row); syncDetails(
)"
placeholder=
"输入数量"
/>
</
template
>
</el-table-column>
<el-table-column
label=
"计划件数"
width=
"120"
>
<
template
slot-scope=
"scope"
>
<el-input
v-model
.
number=
"scope.row.plannedPackages"
type=
"number"
size=
"mini"
min=
"0"
@
input=
"handleRowPackagesInput(scope.row)"
placeholder=
"输入件数"
/>
</
template
>
</el-table-column>
</el-table>
<!-- 选中行的扩展字段填写区域 -->
...
...
@@ -140,8 +128,9 @@
style=
"margin-top: 10px; padding: 10px; border: 1px solid #e6e6e6; border-radius: 4px;"
>
<div
style=
"margin-bottom: 8px; font-weight: 600; color: #1989fa;"
>
库存
【ID: {{ currentSelectedRow.id }}】
明细信息
库存明细信息
</div>
<!-- 扩展字段区域保持不变 -->
<el-row
:gutter=
"20"
>
<el-col
:span=
"8"
>
<el-form-item
label=
"约数"
prop=
"divisor"
>
...
...
@@ -150,12 +139,13 @@
placeholder=
"请输入约数"
type=
"number"
min=
"0"
@
input=
"syncDetails()"
/>
</el-form-item>
</el-col>
<el-col
:span=
"8"
>
<el-form-item
label=
"标签颜色"
prop=
"labelColor"
>
<el-select
v-model=
"currentSelectedRow.labelColor"
placeholder=
"请选择标签颜色"
>
<el-select
v-model=
"currentSelectedRow.labelColor"
placeholder=
"请选择标签颜色"
@
change=
"syncDetails()"
>
<el-option
label=
"红色"
value=
"1"
></el-option>
<el-option
label=
"蓝色"
value=
"2"
></el-option>
<el-option
label=
"绿色"
value=
"3"
></el-option>
...
...
@@ -163,35 +153,26 @@
</el-select>
</el-form-item>
</el-col>
<el-col
:span=
"8"
>
<el-form-item
label=
"
单价"
prop=
"unitPrice
"
>
<el-col
:span=
"8"
>
<el-form-item
label=
"
计划数量"
prop=
"plannedQuantity
"
>
<el-input
v-model
.
number=
"currentSelectedRow.unitPrice"
placeholder=
"请输入单价"
v-model
.
number=
"currentSelectedRow.plannedQuantity"
type=
"number"
min=
"0"
step=
"0.01"
min=
"1"
:max=
"(currentSelectedRow.quantity || 0) - (currentSelectedRow.lockedQuantity || 0)"
@
input=
"handleRowPlannedQtyInput(currentSelectedRow); syncDetails()"
placeholder=
"输入计划数量"
/>
</el-form-item>
</el-col>
</el-row>
<el-row
:gutter=
"20"
style=
"margin-top: 10px;"
>
<el-col
:span=
"12"
>
<el-form-item
label=
"收货时间"
prop=
"receivedAt"
>
<el-date-picker
v-model=
"currentSelectedRow.receivedAt"
type=
"datetime"
value-format=
"yyyy-MM-dd HH:mm:ss"
placeholder=
"请选择收货时间"
style=
"width: 100%"
/>
</el-form-item>
</el-col>
<el-col
:span=
"12"
>
<el-form-item
label=
"收货人"
prop=
"receivedBy"
>
<el-form-item
label=
"发货方"
prop=
"shippedBy"
>
<el-input
v-model=
"currentSelectedRow.receivedBy"
placeholder=
"请输入收货人"
v-model=
"currentSelectedRow.shippedBy"
placeholder=
"请输入发货方"
@
input=
"syncDetails()"
/>
</el-form-item>
</el-col>
...
...
@@ -202,6 +183,7 @@
<el-input
v-model=
"currentSelectedRow.voucherNumber"
placeholder=
"请输入凭证号"
@
input=
"syncDetails()"
/>
</el-form-item>
</el-col>
...
...
@@ -214,6 +196,7 @@
placeholder=
"请输入备注"
type=
"textarea"
rows=
"2"
@
input=
"syncDetails()"
/>
</el-form-item>
</el-col>
...
...
@@ -222,7 +205,7 @@
</el-col>
</el-row>
<!-- 已生成的明细预览 -->
<!-- 已生成的明细预览 -
添加库存ID列 -
->
<el-row
v-if=
"details.length > 0"
style=
"margin: 10px 0;"
>
<el-col
:span=
"24"
>
<div
style=
"margin-bottom: 8px; font-weight: 600; color: #1989fa;"
>
...
...
@@ -233,24 +216,22 @@
border
size=
"small"
max-height=
"200"
:row-key=
"item => item.inventoryId"
>
<el-table-column
prop=
"batchId"
label=
"批次ID"
/>
<el-table-column
prop=
"inventoryId"
label=
"库存ID"
width=
"100"
/>
<el-table-column
prop=
"batchCode"
label=
"批次ID"
/>
<el-table-column
prop=
"warehouseId"
label=
"仓库ID"
/>
<el-table-column
prop=
"locationId"
label=
"库位ID"
/>
<el-table-column
prop=
"plannedQuantity"
label=
"计划数量"
/>
<el-table-column
prop=
"actualQuantity"
label=
"实际数量"
/>
<el-table-column
prop=
"plannedPackages"
label=
"计划件数"
/>
<el-table-column
prop=
"actualPackages"
label=
"实际件数"
/>
<el-table-column
prop=
"divisor"
label=
"约数"
/>
<el-table-column
prop=
"unitPrice"
label=
"单价"
/>
<el-table-column
prop=
"labelColor"
label=
"标签颜色"
>
<
template
slot-scope=
"scope"
>
{{
getLabelColorText
(
scope
.
row
.
labelColor
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"receivedAt"
label=
"收货时间"
/>
<el-table-column
prop=
"receivedBy"
label=
"收货人"
/>
<el-table-column
prop=
"voucherNumber"
label=
"凭证号"
/>
<el-table-column
prop=
"shippedBy"
label=
"发货方"
/>
<el-table-column
prop=
"remark"
label=
"备注"
/>
<el-table-column
label=
"操作"
width=
"80"
>
<
template
slot-scope=
"scope"
>
...
...
@@ -267,7 +248,7 @@
</el-form>
<div
slot=
"footer"
class=
"dialog-footer"
>
<el-button
@
click
.
native=
"handleClose"
>
取消
</el-button>
<el-button
type=
"primary"
@
click
.
native=
"handleSubmit"
>
确定
</el-button>
<el-button
type=
"primary"
@
click
.
native=
"handleSubmit"
>
生成明细
</el-button>
</div>
</el-dialog>
</template>
...
...
@@ -276,7 +257,6 @@
import
{
listInventoryByMaterialId
}
from
"@/api/inventory/inventory"
import
MaterialSelector
from
'../../../components/materialsSeletor.vue'
// 防抖函数(适配Vue2 this上下文)
function
debounce
(
fn
,
delay
=
500
)
{
let
timer
=
null
return
function
(...
args
)
{
...
...
@@ -308,14 +288,13 @@ export default {
},
data
()
{
return
{
// 仅保留全局字段:货物ID、计划总数量
form
:
{
materialUuids
:
''
,
materialId
:
''
,
totalPlannedQuantity
:
null
,
itemStatus
:
'1'
// 对应数据库状态:1-待收货
itemStatus
:
1
},
details
:
[],
details
:
[],
inventoryList
:
[],
loadingInstance
:
null
,
rules
:
{
...
...
@@ -324,26 +303,11 @@ export default {
message
:
'请选择货物ID'
,
trigger
:
[
'change'
,
'blur'
]
}]
// totalPlannedQuantity: [{
// required: true,
// message: '请输入计划总数量',
// trigger: 'blur',
// type: 'number'
// }, {
// validator: (rule, value, callback) => {
// if (value && value
<
1
)
{
// callback(new Error('计划总数量不能小于1'))
// } else {
// callback()
// }
// },
// trigger: 'blur'
// }]
},
openMaterialSelector
:
false
,
selectedMaterialId
:
''
,
selectedMaterialInfo
:
null
,
currentSelectedRow
:
null
// 存储当前点击的库存行
currentSelectedRow
:
null
}
},
created
()
{
...
...
@@ -356,7 +320,7 @@ export default {
this
.
form
=
{
...
this
.
$options
.
data
().
form
,
...
this
.
initForm
}
this
.
selectedMaterialId
=
this
.
form
.
materialId
||
''
this
.
selectedMaterialInfo
=
null
this
.
currentSelectedRow
=
null
// 重置选中行
this
.
currentSelectedRow
=
null
if
(
this
.
form
.
materialId
&&
this
.
form
.
materialId
.
trim
())
{
this
.
handleMaterialIdChange
()
}
...
...
@@ -377,7 +341,7 @@ export default {
this
.
form
=
{
...
this
.
$options
.
data
().
form
,
...
val
}
this
.
selectedMaterialId
=
this
.
form
.
materialId
||
''
this
.
selectedMaterialInfo
=
null
this
.
currentSelectedRow
=
null
// 重置选中行
this
.
currentSelectedRow
=
null
if
(
this
.
form
.
materialId
&&
this
.
form
.
materialId
.
trim
())
{
this
.
handleMaterialIdChange
()
}
...
...
@@ -388,7 +352,6 @@ export default {
}
},
methods
:
{
// 标签颜色值转文本
getLabelColorText
(
value
)
{
const
colorMap
=
{
'1'
:
'红色'
,
...
...
@@ -405,165 +368,152 @@ export default {
}
},
async
handleMaterialIdChange
()
{
const
materialId
=
this
.
form
.
materialId
?.
trim
()
||
''
if
(
!
materialId
)
{
this
.
inventoryList
=
[]
this
.
closeLoading
()
return
}
try
{
this
.
loadingInstance
=
this
.
$loading
({
text
:
'查询中...'
,
target
:
this
.
$el
,
lock
:
true
,
background
:
'rgba(0, 0, 0, 0.7)'
})
const
res
=
await
listInventoryByMaterialId
(
materialId
)
if
(
res
.
code
===
200
)
{
// 为每个库存行初始化扩展字段
this
.
inventoryList
=
(
res
.
rows
||
[]).
map
(
item
=>
({
...
item
,
selectedQty
:
null
,
plannedPackages
:
null
,
divisor
:
null
,
// 约数
labelColor
:
''
,
// 标签颜色
unitPrice
:
null
,
// 单价
receivedAt
:
''
,
// 收货时间
receivedBy
:
''
,
// 收货人
voucherNumber
:
''
,
// 凭证号
remark
:
''
,
// 备注
quantity
:
item
.
quantity
||
0
,
lockedQuantity
:
item
.
lockedQuantity
||
0
}))
if
(
this
.
inventoryList
.
length
===
0
)
{
this
.
$message
.
warning
(
'未查询到该物料的库存信息'
)
}
}
else
{
this
.
inventoryList
=
[]
this
.
$message
.
error
(
res
.
msg
||
'查询库存失败'
)
const
materialId
=
this
.
form
.
materialId
?.
trim
()
||
''
;
// 增加分号,避免解析错误
if
(
!
materialId
)
{
this
.
inventoryList
=
[];
this
.
closeLoading
();
return
;
}
try
{
this
.
loadingInstance
=
this
.
$loading
({
text
:
'查询中...'
,
target
:
this
.
$el
,
lock
:
true
,
background
:
'rgba(0, 0, 0, 0.7)'
});
const
res
=
await
listInventoryByMaterialId
(
materialId
);
// 增加分号
if
(
res
.
code
===
200
)
{
// 按库存ID去重(修复语法:确保Map操作正确)
const
uniqueInventoryMap
=
new
Map
();
(
res
.
rows
||
[]).
forEach
(
item
=>
{
if
(
!
uniqueInventoryMap
.
has
(
item
.
id
))
{
uniqueInventoryMap
.
set
(
item
.
id
,
item
);
}
}
catch
(
error
)
{
this
.
$message
.
error
(
'查询库存失败:'
+
(
error
.
message
||
'网络异常'
))
this
.
inventoryList
=
[]
}
finally
{
this
.
closeLoading
()
});
const
uniqueRows
=
Array
.
from
(
uniqueInventoryMap
.
values
());
// 增加分号
this
.
inventoryList
=
uniqueRows
.
map
(
item
=>
({
...
item
,
actualQuantity
:
null
,
plannedQuantity
:
null
,
divisor
:
null
,
labelColor
:
''
,
unitPrice
:
null
,
shippedAt
:
''
,
shippedBy
:
''
,
voucherNumber
:
''
,
remark
:
''
,
quantity
:
item
.
quantity
||
0
,
lockedQuantity
:
item
.
lockedQuantity
||
0
,
batchCode
:
item
.
batchId
||
''
}));
// 增加分号
if
(
this
.
inventoryList
.
length
===
0
)
{
this
.
$message
.
warning
(
'未查询到该物料的库存信息'
);
}
},
// 点击库存行触发 - 显示扩展字段填写区域
}
else
{
this
.
inventoryList
=
[];
this
.
$message
.
error
(
res
.
msg
||
'查询库存失败'
);
}
}
catch
(
error
)
{
this
.
$message
.
error
(
'查询库存失败:'
+
(
error
.
message
||
'网络异常'
));
this
.
inventoryList
=
[];
}
finally
{
this
.
closeLoading
();
}
},
handleRowClick
(
row
)
{
if
(
!
row
)
return
this
.
currentSelectedRow
=
row
// 自动选中当前行
this
.
$refs
.
inventoryTable
.
toggleRowSelection
(
row
,
true
)
},
handleRowQtyInput
(
row
)
{
if
(
isNaN
(
row
.
selectedQty
)
||
row
.
selectedQ
ty
===
''
)
{
row
.
selectedQ
ty
=
null
handleRow
Actual
QtyInput
(
row
)
{
if
(
isNaN
(
row
.
actualQuantity
)
||
row
.
actualQuanti
ty
===
''
)
{
row
.
actualQuanti
ty
=
null
return
}
const
availableQty
=
(
row
.
quantity
||
0
)
-
(
row
.
lockedQuantity
||
0
)
if
(
row
.
selectedQ
ty
>
availableQty
)
{
this
.
$message
.
warning
(
`
选择
数量不能超过可用数量
${
availableQty
}
`
)
row
.
selectedQ
ty
=
availableQty
}
else
if
(
row
.
selectedQ
ty
<
1
)
{
this
.
$message
.
warning
(
'
选择
数量不能小于1'
)
row
.
selectedQ
ty
=
1
if
(
row
.
actualQuanti
ty
>
availableQty
)
{
this
.
$message
.
warning
(
`
实际
数量不能超过可用数量
${
availableQty
}
`
)
row
.
actualQuanti
ty
=
availableQty
}
else
if
(
row
.
actualQuanti
ty
<
1
)
{
this
.
$message
.
warning
(
'
实际
数量不能小于1'
)
row
.
actualQuanti
ty
=
1
}
// 核心改造1:清空其他行的实际数量,确保仅当前行有值
this
.
inventoryList
.
forEach
(
item
=>
{
if
(
item
.
id
!==
row
.
id
)
{
item
.
actualQuantity
=
null
item
.
plannedQuantity
=
null
item
.
divisor
=
null
item
.
labelColor
=
''
item
.
shippedBy
=
''
item
.
voucherNumber
=
''
item
.
remark
=
''
}
})
},
handleRowP
ackages
Input
(
row
)
{
if
(
isNaN
(
row
.
planned
Packages
)
||
row
.
plannedPackages
===
''
)
{
row
.
planned
Packages
=
null
handleRowP
lannedQty
Input
(
row
)
{
if
(
isNaN
(
row
.
planned
Quantity
)
||
row
.
plannedQuantity
===
''
)
{
row
.
planned
Quantity
=
null
return
}
if
(
row
.
plannedPackages
<
0
)
{
this
.
$message
.
warning
(
'计划件数不能小于0'
)
row
.
plannedPackages
=
0
const
availableQty
=
(
row
.
quantity
||
0
)
-
(
row
.
lockedQuantity
||
0
)
if
(
row
.
plannedQuantity
>
availableQty
)
{
this
.
$message
.
warning
(
`计划数量不能超过可用数量
${
availableQty
}
`
)
row
.
plannedQuantity
=
availableQty
}
else
if
(
row
.
plannedQuantity
<
1
)
{
this
.
$message
.
warning
(
'计划数量不能小于1'
)
row
.
plannedQuantity
=
1
}
},
handleSelectionChange
(
rows
)
{
// 过滤已取消选择的行
this
.
details
=
this
.
details
.
filter
(
detail
=>
rows
.
some
(
row
=>
row
.
id
===
detail
.
inventoryId
)
)
// 处理选中行,携带行级扩展字段
rows
.
forEach
(
row
=>
{
// 选择数量为空则跳过
if
(
row
.
selectedQty
===
null
||
row
.
selectedQty
===
''
)
return
syncDetails
()
{
// 核心修复2:先清空明细,避免重复累加
this
.
details
=
[]
// 过滤出仅填写了实际数量的行(此时最多只有一行)
const
validRow
=
this
.
inventoryList
.
find
(
row
=>
{
if
(
row
.
actualQuantity
===
null
||
row
.
actualQuantity
===
''
||
isNaN
(
row
.
actualQuantity
))
return
false
const
availableQty
=
(
row
.
quantity
||
0
)
-
(
row
.
lockedQuantity
||
0
)
if
(
row
.
selectedQty
>
availableQty
||
row
.
selectedQty
<
1
)
{
this
.
$message
.
warning
(
`行
${
row
.
id
}
的选择数量不合法,请重新输入`
)
row
.
selectedQty
=
Math
.
min
(
Math
.
max
(
row
.
selectedQty
,
1
),
availableQty
)
}
// 校验行级必填字段(可根据实际需求调整)
if
(
!
row
.
divisor
&&
row
.
divisor
!==
0
)
{
this
.
$message
.
warning
(
`库存ID:
${
row
.
id
}
请填写约数`
)
return
}
if
(
!
row
.
labelColor
)
{
this
.
$message
.
warning
(
`库存ID:
${
row
.
id
}
请选择标签颜色`
)
return
}
if
(
!
row
.
unitPrice
&&
row
.
unitPrice
!==
0
)
{
this
.
$message
.
warning
(
`库存ID:
${
row
.
id
}
请填写单价`
)
return
}
if
(
!
row
.
receivedAt
)
{
this
.
$message
.
warning
(
`库存ID:
${
row
.
id
}
请选择收货时间`
)
return
}
if
(
!
row
.
receivedBy
)
{
this
.
$message
.
warning
(
`库存ID:
${
row
.
id
}
请填写收货人`
)
return
}
return
row
.
actualQuantity
<=
availableQty
&&
row
.
actualQuantity
>=
1
})
const
existingIndex
=
this
.
details
.
findIndex
(
d
=>
d
.
inventoryId
===
row
.
id
)
if
(
validRow
)
{
const
newDetail
=
{
inventoryId
:
row
.
id
,
inventoryId
:
validRow
.
id
,
// 确保库存ID唯一
materialId
:
this
.
form
.
materialId
,
batch
Id
:
row
.
batchId
||
''
,
warehouseId
:
r
ow
.
warehouseId
||
''
,
locationId
:
r
ow
.
locationId
||
''
,
plannedQuantity
:
row
.
selectedQty
,
actualQuantity
:
row
.
selectedQty
,
plannedPackages
:
row
.
plannedPackages
||
0
,
actualPackages
:
row
.
plannedPackages
||
0
,
// 实际件数默认等于计划件数
divisor
:
row
.
divisor
,
// 行级约数
labelColor
:
row
.
labelColor
,
// 行级标签颜色
unitPrice
:
row
.
unitPrice
,
// 行级单价
itemStatus
:
this
.
form
.
itemStatus
||
'1'
,
receivedAt
:
row
.
receivedAt
,
// 行级收货时间
re
ceivedBy
:
row
.
receivedBy
,
// 行级收货人
voucherNumber
:
row
.
voucherNumber
||
''
,
// 行级凭证号
remark
:
row
.
remark
||
''
// 行级备注
batch
Code
:
validRow
.
batchCode
||
''
,
warehouseId
:
validR
ow
.
warehouseId
||
''
,
locationId
:
validR
ow
.
locationId
||
''
,
plannedQuantity
:
Number
(
validRow
.
plannedQuantity
)
,
actualQuantity
:
Number
(
validRow
.
actualQuantity
)
,
divisor
:
Number
(
validRow
.
divisor
)
||
0
,
labelColor
:
Number
(
validRow
.
labelColor
)
||
0
,
unitPrice
:
Number
(
validRow
.
unitPrice
)
||
0
,
voucherNumber
:
validRow
.
voucherNumber
||
''
,
itemStatus
:
Number
(
this
.
form
.
itemStatus
)
||
1
,
shippedAt
:
validRow
.
shippedAt
,
shippedBy
:
validRow
.
shippedBy
,
re
mark
:
validRow
.
remark
||
''
,
isUsed
:
1
,
sortNo
:
0
}
if
(
existingIndex
>
-
1
)
{
this
.
details
.
splice
(
existingIndex
,
1
,
newDetail
)
}
else
{
this
.
details
.
push
(
newDetail
)
}
})
this
.
details
.
push
(
newDetail
)
}
},
removeDetail
(
row
)
{
this
.
details
=
this
.
details
.
filter
(
d
=>
d
.
inventoryId
!==
row
.
inventoryId
)
if
(
this
.
$refs
.
inventoryTable
&&
row
.
id
)
{
const
inventoryRow
=
this
.
inventoryList
.
find
(
item
=>
item
.
id
===
row
.
inventoryId
)
if
(
inventoryRow
)
{
this
.
$refs
.
inventoryTable
.
toggleRowSelection
(
inventoryRow
,
false
)
// 清空该行的填写内容
inventoryRow
.
selectedQty
=
null
inventoryRow
.
plannedPackages
=
null
inventoryRow
.
divisor
=
null
inventoryRow
.
labelColor
=
''
inventoryRow
.
unitPrice
=
null
inventoryRow
.
receivedAt
=
''
inventoryRow
.
receivedBy
=
''
inventoryRow
.
voucherNumber
=
''
inventoryRow
.
remark
=
''
// 如果删除的是当前选中行,重置currentSelectedRow
if
(
this
.
currentSelectedRow
&&
this
.
currentSelectedRow
.
id
===
row
.
inventoryId
)
{
this
.
currentSelectedRow
=
null
}
const
inventoryRow
=
this
.
inventoryList
.
find
(
item
=>
item
.
id
===
row
.
inventoryId
)
if
(
inventoryRow
)
{
inventoryRow
.
actualQuantity
=
null
inventoryRow
.
plannedQuantity
=
null
inventoryRow
.
divisor
=
null
inventoryRow
.
labelColor
=
''
inventoryRow
.
unitPrice
=
null
inventoryRow
.
shippedAt
=
''
inventoryRow
.
shippedBy
=
''
inventoryRow
.
voucherNumber
=
''
inventoryRow
.
remark
=
''
if
(
this
.
currentSelectedRow
&&
this
.
currentSelectedRow
.
id
===
row
.
inventoryId
)
{
this
.
currentSelectedRow
=
null
}
}
},
...
...
@@ -578,12 +528,53 @@ export default {
this
.
$message
.
error
(
'表单校验失败,请检查必填项'
)
return
}
this
.
syncDetails
()
if
(
this
.
details
.
length
===
0
)
{
this
.
$message
.
error
(
'请选择库存并填写数量生成明细'
)
this
.
$message
.
error
(
'请填写实际数量并完善明细信息'
)
return
}
// 核心改造3:兜底校验 - 检查是否存在重复的inventoryId(防御性编程)
const
inventoryIds
=
this
.
details
.
map
(
d
=>
d
.
inventoryId
)
const
uniqueInventoryIds
=
[...
new
Set
(
inventoryIds
)]
if
(
uniqueInventoryIds
.
length
!==
inventoryIds
.
length
)
{
this
.
$message
.
error
(
'发现重复的库存ID明细,请重新选择!'
)
return
}
// 计算实际总数量
let
hasError
=
false
this
.
details
.
forEach
(
detail
=>
{
const
row
=
this
.
inventoryList
.
find
(
r
=>
r
.
id
===
detail
.
inventoryId
)
if
(
!
row
)
return
if
(
row
.
divisor
===
null
&&
row
.
divisor
!==
0
)
{
this
.
$message
.
warning
(
`库存ID:
${
row
.
id
}
请填写约数`
)
hasError
=
true
}
if
(
!
row
.
labelColor
)
{
this
.
$message
.
warning
(
`库存ID:
${
row
.
id
}
请选择标签颜色`
)
hasError
=
true
}
if
(
!
row
.
voucherNumber
)
{
this
.
$message
.
warning
(
`库存ID:
${
row
.
id
}
请填写凭证号`
)
hasError
=
true
}
if
(
!
row
.
shippedBy
)
{
this
.
$message
.
warning
(
`库存ID:
${
row
.
id
}
请填写发货方`
)
hasError
=
true
}
if
(
!
row
.
plannedQuantity
)
{
this
.
$message
.
warning
(
`库存ID:
${
row
.
id
}
请填写计划数量`
)
hasError
=
true
}
if
(
!
row
.
actualQuantity
)
{
this
.
$message
.
warning
(
`库存ID:
${
row
.
id
}
请填写实际数量`
)
hasError
=
true
}
})
if
(
hasError
)
return
const
totalActual
=
this
.
details
.
reduce
((
sum
,
d
)
=>
sum
+
(
d
.
actualQuantity
||
0
),
0
)
if
(
this
.
form
.
totalPlannedQuantity
&&
this
.
form
.
totalPlannedQuantity
!==
totalActual
)
{
try
{
...
...
@@ -613,7 +604,7 @@ export default {
this
.
selectedMaterialId
=
''
this
.
selectedMaterialInfo
=
null
this
.
openMaterialSelector
=
false
this
.
currentSelectedRow
=
null
// 重置选中行
this
.
currentSelectedRow
=
null
if
(
this
.
$refs
.
detailForm
)
{
this
.
$refs
.
detailForm
.
resetFields
()
}
...
...
@@ -683,7 +674,6 @@ export default {
min-height
:
500px
;
}
/* 修复模态框遮罩层点击穿透问题 */
/
deep
/
.el-dialog__wrapper
{
.el-modal__mask
{
pointer-events
:
auto
!important
;
...
...
ruoyi-admin-vue/src/views/inventory/orders/index.vue
View file @
15102e6e
...
...
@@ -122,13 +122,20 @@
</el-row>
<!-- 主表格 -->
<el-table
v-loading=
"loading"
:data=
"ordersList"
@
selection-change=
"handleSelectionChange"
>
<el-table
ref=
"mainTable"
v-loading=
"loading"
:data=
"ordersList"
@
selection-change=
"handleSelectionChange"
:key=
"tableKey"
>
<el-table-column
type=
"selection"
width=
"55"
align=
"center"
/>
<el-table-column
label=
"出库单号"
align=
"center"
prop=
"orderId"
width=
"150"
/>
<el-table-column
label=
"系统编号"
align=
"center"
prop=
"systemNo"
width=
"150"
/>
<el-table-column
label=
"入库类型"
align=
"center"
prop=
"orderTypeId"
width=
"120"
>
<template
slot-scope=
"scope"
>
<dict-tag
:options=
"dict.type.inbound_order_type"
:value=
"scope.row.orderTypeId"
/>
<dict-tag
v-if=
"dict.type.inbound_order_type"
:options=
"dict.type.inbound_order_type"
:value=
"scope.row.orderTypeId"
/>
<span
v-else
>
-
</span>
</
template
>
</el-table-column>
<el-table-column
label=
"批次ID"
align=
"center"
prop=
"batchCode"
width=
"120"
/>
...
...
@@ -136,7 +143,8 @@
<el-table-column
label=
"货主ID"
align=
"center"
prop=
"ownerId"
width=
"100"
/>
<el-table-column
label=
"订单状态"
align=
"center"
prop=
"orderStatus"
width=
"150"
>
<
template
slot-scope=
"scope"
>
<dict-tag
:options=
"dict.type.inbound_order_status"
:value=
"scope.row.orderStatus"
/>
<dict-tag
v-if=
"dict.type.inbound_order_status"
:options=
"dict.type.inbound_order_status"
:value=
"scope.row.orderStatus"
/>
<span
v-else
>
-
</span>
</
template
>
</el-table-column>
<el-table-column
label=
"出库日期"
align=
"center"
prop=
"inboundDate"
width=
"150"
>
...
...
@@ -149,7 +157,7 @@
<
el
-
table
-
column
label
=
"实际量"
align
=
"center"
prop
=
"totalActualQuantity"
width
=
"100"
/>
<
el
-
table
-
column
label
=
"总件数"
align
=
"center"
prop
=
"totalPackages"
width
=
"100"
/>
<
el
-
table
-
column
label
=
"备注"
align
=
"center"
prop
=
"remark"
width
=
"150"
/>
<
el
-
table
-
column
label
=
"操作"
align
=
"center"
class
-
name
=
"small-padding fixed-width"
width
=
"1
2
0"
>
<
el
-
table
-
column
label
=
"操作"
align
=
"center"
class
-
name
=
"small-padding fixed-width"
width
=
"1
8
0"
>
<
template
slot
-
scope
=
"scope"
>
<
el
-
button
size
=
"mini"
...
...
@@ -161,6 +169,13 @@
<
el
-
button
size
=
"mini"
type
=
"text"
icon
=
"el-icon-send"
@
click
=
"handleShip(scope.row)"
v
-
hasPermi
=
"['inventory:orders:edit']"
>
出货
<
/el-button
>
<
el
-
button
size
=
"mini"
type
=
"text"
icon
=
"el-icon-delete"
@
click
=
"handleDelete(scope.row)"
v
-
hasPermi
=
"['inventory:orders:remove']"
...
...
@@ -266,7 +281,6 @@
<
el
-
divider
content
-
position
=
"center"
>
入库单明细信息
<
/el-divider
>
<
el
-
row
:
gutter
=
"10"
class
=
"mb8"
>
<
el
-
col
:
span
=
"1.5"
>
<!--
点击该按钮打开子组件弹窗
-->
<
el
-
button
type
=
"primary"
icon
=
"el-icon-plus"
size
=
"mini"
@
click
=
"openDetailDialog"
>
添加
<
/el-button
>
<
/el-col
>
<
el
-
col
:
span
=
"1.5"
>
...
...
@@ -274,37 +288,45 @@
<
/el-col
>
<
/el-row
>
<
el
-
table
:
data
=
"
in
boundOrderItemsList"
:
data
=
"
out
boundOrderItemsList"
:
row
-
class
-
name
=
"rowInboundOrderItemsIndex"
@
selection
-
change
=
"handleInboundOrderItemsSelectionChange"
ref
=
"inboundOrderItems"
border
style
=
"width: 100%;"
v
-
if
=
"outboundOrderItemsList.length > 0"
:
row
-
key
=
"item => item.inventoryId"
>
<
el
-
table
-
column
type
=
"selection"
width
=
"50"
align
=
"center"
/>
<
el
-
table
-
column
label
=
"序号"
align
=
"center"
prop
=
"index"
width
=
"50"
/>
<
el
-
table
-
column
prop
=
"inventoryId"
label
=
"库存ID"
width
=
"100"
/>
<
el
-
table
-
column
label
=
"货物ID"
prop
=
"materialId"
width
=
"150"
/>
<
el
-
table
-
column
label
=
"批次ID"
prop
=
"batch
Id
"
width
=
"150"
/>
<
el
-
table
-
column
label
=
"批次ID"
prop
=
"batch
Code
"
width
=
"150"
/>
<
el
-
table
-
column
label
=
"仓库ID"
prop
=
"warehouseId"
width
=
"150"
/>
<
el
-
table
-
column
label
=
"库位ID"
prop
=
"locationId"
width
=
"150"
/>
<
el
-
table
-
column
label
=
"计划数量"
prop
=
"plannedQuantity"
width
=
"150"
/>
<
el
-
table
-
column
label
=
"实际数量"
prop
=
"actualQuantity"
width
=
"150"
/>
<
el
-
table
-
column
label
=
"计划件数"
prop
=
"plannedPackages"
width
=
"150"
/>
<
el
-
table
-
column
label
=
"实际件数"
prop
=
"actualPackages"
width
=
"150"
/>
<
el
-
table
-
column
label
=
"约数"
prop
=
"divisor"
width
=
"150"
/>
<
el
-
table
-
column
label
=
"标签颜色"
prop
=
"labelColor"
width
=
"150"
/>
<
el
-
table
-
column
label
=
"标签颜色"
prop
=
"labelColor"
width
=
"150"
>
<
template
slot
-
scope
=
"scope"
>
{{
getLabelColorText
(
scope
.
row
.
labelColor
)
}}
<
/template
>
<
/el-table-column
>
<
el
-
table
-
column
label
=
"单价"
prop
=
"unitPrice"
width
=
"150"
/>
<
el
-
table
-
column
label
=
"凭证号"
prop
=
"voucherNumber"
width
=
"150"
/>
<
el
-
table
-
column
label
=
"状态"
prop
=
"itemStatus"
width
=
"150"
>
<
template
slot
-
scope
=
"scope"
>
<
dict
-
tag
:
options
=
"dict.type.inbound_order_item_status"
:
value
=
"scope.row.itemStatus"
/>
<
dict
-
tag
v
-
if
=
"dict.type.inbound_order_item_status"
:
options
=
"dict.type.inbound_order_item_status"
:
value
=
"scope.row.itemStatus"
/>
<
span
v
-
else
>-<
/span
>
<
/template
>
<
/el-table-column
>
<
el
-
table
-
column
label
=
"
收货时间"
prop
=
"receiv
edAt"
width
=
"150"
>
<
el
-
table
-
column
label
=
"
发货时间"
prop
=
"shipp
edAt"
width
=
"150"
>
<
template
slot
-
scope
=
"scope"
>
<
span
>
{{
parseTime
(
scope
.
row
.
receiv
edAt
,
'{y
}
-{m
}
-{d
}
{h
}
:{i
}
:{s
}
'
)
}}
<
/span
>
<
span
>
{{
parseTime
(
scope
.
row
.
shipp
edAt
,
'{y
}
-{m
}
-{d
}
{h
}
:{i
}
:{s
}
'
)
}}
<
/span
>
<
/template
>
<
/el-table-column
>
<
el
-
table
-
column
label
=
"收货人"
prop
=
"receivedBy"
width
=
"150"
/>
<
el
-
table
-
column
label
=
"发货方"
prop
=
"shippedBy"
width
=
"150"
/>
<
el
-
table
-
column
label
=
"备注"
prop
=
"remark"
width
=
"150"
/>
<
el
-
table
-
column
label
=
"操作"
align
=
"center"
width
=
"80"
>
<
template
slot
-
scope
=
"scope"
>
<
el
-
button
...
...
@@ -316,6 +338,9 @@
<
/template
>
<
/el-table-column
>
<
/el-table
>
<
div
v
-
else
class
=
"empty-tip"
style
=
"text-align: center; padding: 20px; color: #999;"
>
暂无明细数据,请点击添加按钮添加
<
/div
>
<
/el-form
>
<
div
slot
=
"footer"
class
=
"dialog-footer"
>
<
el
-
button
type
=
"primary"
@
click
=
"submitForm"
>
确
定
<
/el-button
>
...
...
@@ -323,7 +348,7 @@
<
/div
>
<
/el-dialog
>
<!--
明细项添加
/
编辑子组件弹窗
(
OutboundOrderFormWithItems
)
-->
<!--
明细项添加
/
编辑子组件弹窗
-->
<
OutboundOrderFormWithItems
:
title
=
"detailDialogTitle"
:
open
=
"detailDialogOpen"
...
...
@@ -336,18 +361,19 @@
<
/template
>
<
script
>
import
{
listOrders
,
getOrders
,
delOrders
,
addOrders
,
updateOrders
}
from
"@/api/inventory/orders"
// 导入明细子组件
import
{
listOrders
,
getOrders
,
delOrders
,
addOrders
,
updateOrders
,
ship
}
from
"@/api/inventory/orders"
import
OutboundOrderFormWithItems
from
'./OutboundOrderFormWithItems.vue'
export
default
{
name
:
"Orders"
,
dicts
:
[
'inbound_order_status'
,
'inbound_order_type'
,
'inbound_order_item_status'
],
components
:
{
OutboundOrderFormWithItems
// 注册子组件
OutboundOrderFormWithItems
}
,
data
()
{
return
{
// 添加tableKey解决tableId渲染问题
tableKey
:
1
,
// 遮罩层
loading
:
true
,
// 选中数组
...
...
@@ -366,31 +392,34 @@ export default {
title
:
""
,
// 主弹窗是否显示
open
:
false
,
// 明细
表格数据
in
boundOrderItemsList
:
[],
// 明细
列表
out
boundOrderItemsList
:
[],
// 选中的明细行
selectedInboundOrderItems
:
[],
// 明细子弹窗相关
detailDialogOpen
:
false
,
// 子弹窗是否显示
detailDialogTitle
:
"新增明细项"
,
// 子弹窗标题
detailDialogOpen
:
false
,
detailDialogTitle
:
"新增明细项"
,
currentDetailItem
:
{
materialUuids
:
''
,
// 存储物料选择器返回的materialCodes
inventoryId
:
''
,
materialUuids
:
''
,
materialId
:
''
,
batch
Id
:
''
,
batch
Code
:
''
,
warehouseId
:
''
,
locationId
:
''
,
plannedQuantity
:
''
,
actualQuantity
:
''
,
plannedPackages
:
''
,
actualPackages
:
''
,
divisor
:
''
,
labelColor
:
''
,
unitPrice
:
''
,
itemStatus
:
'pending'
,
receivedAt
:
''
,
receivedBy
:
''
}
,
// 当前编辑的明细项
isEditDetail
:
false
,
// 标记是否为编辑明细
voucherNumber
:
''
,
itemStatus
:
1
,
shippedAt
:
''
,
shippedBy
:
''
,
isUsed
:
1
,
sortNo
:
0
,
remark
:
''
}
,
isEditDetail
:
false
,
// 查询参数
queryParams
:
{
pageNum
:
1
,
...
...
@@ -423,30 +452,59 @@ export default {
}
}
,
created
()
{
this
.
getList
()
// 延迟加载避免初始化渲染问题
this
.
$nextTick
(()
=>
{
this
.
getList
()
}
)
}
,
methods
:
{
// ========== 明细子弹窗相关方法 ==========
// 核心出货方法
async
handleShip
(
row
)
{
try
{
// 调用ship接口提交数据到后端
await
ship
(
row
)
// 提示成功并刷新列表
this
.
$modal
.
msgSuccess
(
"出货操作成功,数据已提交到后端"
)
this
.
getList
()
}
catch
(
error
)
{
// 错误处理
this
.
$modal
.
msgError
(
error
.
msg
||
"出货操作失败"
)
}
}
,
// 标签颜色值转文本
getLabelColorText
(
value
)
{
const
colorMap
=
{
'1'
:
'红色'
,
'2'
:
'蓝色'
,
'3'
:
'绿色'
,
'4'
:
'黄色'
}
return
colorMap
[
value
]
||
'-'
}
,
// 打开明细子弹窗(新增)
openDetailDialog
()
{
this
.
isEditDetail
=
false
this
.
detailDialogTitle
=
"新增明细项"
this
.
currentDetailItem
=
{
materialUuids
:
''
,
// 初始化materialUuids(接收materialCodes)
inventoryId
:
''
,
materialUuids
:
''
,
materialId
:
''
,
batch
Id
:
''
,
warehouseId
:
''
,
batch
Code
:
''
,
warehouseId
:
this
.
form
.
warehouseId
||
''
,
locationId
:
''
,
plannedQuantity
:
''
,
actualQuantity
:
''
,
plannedPackages
:
''
,
actualPackages
:
''
,
divisor
:
''
,
labelColor
:
''
,
unitPrice
:
''
,
itemStatus
:
'pending'
,
// 默认状态
receivedAt
:
''
,
receivedBy
:
''
voucherNumber
:
''
,
itemStatus
:
1
,
shippedAt
:
''
,
shippedBy
:
''
,
isUsed
:
1
,
sortNo
:
0
,
remark
:
''
}
this
.
detailDialogOpen
=
true
}
,
...
...
@@ -454,73 +512,108 @@ export default {
editDetailItem
(
row
)
{
this
.
isEditDetail
=
true
this
.
detailDialogTitle
=
"编辑明细项"
// 兼容materialUuids:优先取row.materialUuids,无则用materialId(适配返回的materialCodes)
this
.
currentDetailItem
=
{
...
row
,
materialUuids
:
row
.
materialUuids
||
row
.
materialId
||
''
}
inventoryId
:
row
.
inventoryId
||
''
,
materialUuids
:
row
.
materialId
||
''
,
divisor
:
row
.
divisor
||
0
,
itemStatus
:
row
.
itemStatus
||
1
,
labelColor
:
row
.
labelColor
||
0
,
plannedQuantity
:
row
.
plannedQuantity
||
0
,
actualQuantity
:
row
.
actualQuantity
||
0
,
remark
:
row
.
remark
||
''
}
this
.
detailDialogOpen
=
true
}
,
// 接收子组件提交的明细数据(核心修
改:适配materialCodes返回格式
)
// 接收子组件提交的明细数据(核心修
复:新增去重逻辑
)
handleDetailSubmit
(
details
)
{
// 兼容子组件返回格式:如果是materialCodes结构,提取第一个值赋值给materialId
const
formatDetail
=
(
item
)
=>
{
// 处理物料选择器返回的materialCodes数组
if
(
item
.
materialUuids
&&
Array
.
isArray
(
item
.
materialUuids
))
{
item
.
materialId
=
item
.
materialUuids
[
0
]
||
''
// 单选取第一个值
item
.
materialUuids
=
item
.
materialUuids
[
0
]
||
''
// 同步更新materialUuids
}
// 兼容子组件返回的原始格式
return
item
}
// 统一转为数组处理
const
detailList
=
Array
.
isArray
(
details
)
?
details
:
[
details
]
// 格式化每条明细的物料字段
const
formattedList
=
detailList
.
map
(
item
=>
formatDetail
(
item
))
if
(
this
.
isEditDetail
)
{
// 编辑模式
:替换原有单条数据
const
editIndex
=
this
.
in
boundOrderItemsList
.
findIndex
(
item
=>
item
.
index
===
this
.
currentDetailItem
.
index
)
// 编辑模式
const
editIndex
=
this
.
out
boundOrderItemsList
.
findIndex
(
item
=>
item
.
index
===
this
.
currentDetailItem
.
index
)
if
(
editIndex
>
-
1
)
{
this
.
inboundOrderItemsList
.
splice
(
editIndex
,
1
,
{
...
formattedList
[
0
],
index
:
this
.
currentDetailItem
.
index
this
.
outboundOrderItemsList
.
splice
(
editIndex
,
1
,
{
...
detailList
[
0
],
index
:
this
.
currentDetailItem
.
index
,
orderId
:
this
.
form
.
id
||
this
.
form
.
orderId
,
inventoryId
:
detailList
[
0
].
inventoryId
||
''
,
itemStatus
:
Number
(
detailList
[
0
].
itemStatus
)
||
1
,
labelColor
:
Number
(
detailList
[
0
].
labelColor
)
||
0
,
divisor
:
Number
(
detailList
[
0
].
divisor
)
||
0
,
plannedQuantity
:
Number
(
detailList
[
0
].
plannedQuantity
)
||
0
,
actualQuantity
:
Number
(
detailList
[
0
].
actualQuantity
)
||
0
,
isUsed
:
1
,
sortNo
:
0
,
remark
:
detailList
[
0
].
remark
||
''
}
)
}
this
.
$message
.
success
(
"编辑明细成功"
)
}
else
{
// 新增模式:批量添加多条明细
const
newDetails
=
formattedList
.
map
((
item
,
idx
)
=>
{
const
newIndex
=
this
.
inboundOrderItemsList
.
length
+
idx
+
1
return
{
...
item
,
index
:
newIndex
}
}
)
this
.
inboundOrderItemsList
=
[...
this
.
inboundOrderItemsList
,
...
newDetails
]
// 新增模式:按库存ID去重,避免重复添加
const
existingInventoryIds
=
new
Set
(
this
.
outboundOrderItemsList
.
map
(
item
=>
item
.
inventoryId
))
// 过滤已存在的库存ID
const
newDetails
=
detailList
.
filter
(
item
=>
item
.
inventoryId
&&
!
existingInventoryIds
.
has
(
item
.
inventoryId
))
.
map
((
item
,
idx
)
=>
{
const
newIndex
=
this
.
outboundOrderItemsList
.
length
+
idx
+
1
return
{
...
item
,
index
:
newIndex
,
orderId
:
this
.
form
.
id
||
this
.
form
.
orderId
,
inventoryId
:
item
.
inventoryId
||
''
,
itemStatus
:
Number
(
item
.
itemStatus
)
||
1
,
labelColor
:
Number
(
item
.
labelColor
)
||
0
,
divisor
:
Number
(
item
.
divisor
)
||
0
,
plannedQuantity
:
Number
(
item
.
plannedQuantity
)
||
0
,
actualQuantity
:
Number
(
item
.
actualQuantity
)
||
0
,
isUsed
:
1
,
sortNo
:
0
,
remark
:
item
.
remark
||
''
}
}
)
// 自动计算主表总数量
// 无新数据则提示
if
(
newDetails
.
length
===
0
)
{
this
.
$message
.
warning
(
"该库存明细已存在,无法重复添加"
)
this
.
detailDialogOpen
=
false
return
}
this
.
outboundOrderItemsList
=
[...
this
.
outboundOrderItemsList
,
...
newDetails
]
this
.
calcTotalQuantity
()
this
.
$message
.
success
(
`成功新增${newDetails.length
}
条明细`
)
}
this
.
detailDialogOpen
=
false
// 更新tableKey触发重新渲染
this
.
tableKey
+=
1
}
,
// 自动计算主表的计划总量/实际总量/总件数
// 计算主表总数量
calcTotalQuantity
()
{
const
totalPlanned
=
this
.
inboundOrderItemsList
.
reduce
((
sum
,
item
)
=>
sum
+
(
Number
(
item
.
plannedQuantity
)
||
0
),
0
)
const
totalActual
=
this
.
inboundOrderItemsList
.
reduce
((
sum
,
item
)
=>
sum
+
(
Number
(
item
.
actualQuantity
)
||
0
),
0
)
const
totalPackages
=
this
.
inboundOrderItemsList
.
reduce
((
sum
,
item
)
=>
sum
+
(
Number
(
item
.
actualPackages
)
||
0
),
0
)
const
totalPlanned
=
this
.
outboundOrderItemsList
.
reduce
((
sum
,
item
)
=>
{
const
qty
=
item
.
plannedQuantity
!==
null
?
Number
(
item
.
plannedQuantity
)
:
0
return
sum
+
qty
}
,
0
)
const
totalActual
=
this
.
outboundOrderItemsList
.
reduce
((
sum
,
item
)
=>
{
const
qty
=
item
.
actualQuantity
!==
null
?
Number
(
item
.
actualQuantity
)
:
0
return
sum
+
qty
}
,
0
)
const
totalPackages
=
this
.
outboundOrderItemsList
.
reduce
((
sum
,
item
)
=>
{
const
divisor
=
item
.
divisor
!==
null
?
Number
(
item
.
divisor
)
:
1
const
actualQty
=
item
.
actualQuantity
!==
null
?
Number
(
item
.
actualQuantity
)
:
0
return
sum
+
Math
.
ceil
(
actualQty
/
divisor
)
}
,
0
)
this
.
form
.
totalPlannedQuantity
=
totalPlanned
this
.
form
.
totalActualQuantity
=
totalActual
this
.
form
.
totalPackages
=
totalPackages
}
,
// ========== 明细表格相关方法 ==========
// 生成明细表格行序号
rowInboundOrderItemsIndex
({
row
,
rowIndex
}
)
{
if
(
!
row
.
index
)
row
.
index
=
rowIndex
+
1
if
(
row
.
index
===
undefined
||
row
.
index
===
null
)
{
row
.
index
=
rowIndex
+
1
}
}
,
// 明细表格选择事件
handleInboundOrderItemsSelectionChange
(
val
)
{
...
...
@@ -532,30 +625,32 @@ export default {
this
.
$message
.
warning
(
'请选择要删除的明细行'
)
return
}
// 获取选中行索引并倒序删除
const
selectedIndexes
=
this
.
selectedInboundOrderItems
.
map
(
item
=>
this
.
in
boundOrderItemsList
.
findIndex
(
row
=>
row
.
index
===
item
.
index
)
this
.
out
boundOrderItemsList
.
findIndex
(
row
=>
row
.
index
===
item
.
index
)
)
selectedIndexes
.
sort
((
a
,
b
)
=>
b
-
a
).
forEach
(
index
=>
{
this
.
in
boundOrderItemsList
.
splice
(
index
,
1
)
this
.
out
boundOrderItemsList
.
splice
(
index
,
1
)
}
)
// 重新
更新
序号
this
.
in
boundOrderItemsList
.
forEach
((
row
,
index
)
=>
{
// 重新
排序
序号
this
.
out
boundOrderItemsList
.
forEach
((
row
,
index
)
=>
{
row
.
index
=
index
+
1
}
)
// 重新计算总数量
this
.
calcTotalQuantity
()
this
.
$message
.
success
(
'明细行删除成功'
)
}
,
// ========== 主表格/表单相关方法 ==========
/** 查询出库单主列表 */
getList
()
{
this
.
loading
=
true
listOrders
(
this
.
queryParams
).
then
(
response
=>
{
this
.
ordersList
=
response
.
rows
this
.
total
=
response
.
total
this
.
ordersList
=
response
.
rows
||
[]
this
.
total
=
response
.
total
||
0
this
.
loading
=
false
// 更新tableKey解决渲染问题
this
.
tableKey
+=
1
}
).
catch
(()
=>
{
this
.
loading
=
false
this
.
ordersList
=
[]
this
.
total
=
0
}
)
}
,
// 取消按钮
...
...
@@ -587,10 +682,12 @@ export default {
updateTime
:
null
,
updateUserCode
:
null
}
// 重置明细表格
this
.
inboundOrderItemsList
=
[]
this
.
outboundOrderItemsList
=
[]
this
.
selectedInboundOrderItems
=
[]
this
.
resetForm
(
"form"
)
// 检查ref存在性
if
(
this
.
$refs
.
form
)
{
this
.
resetForm
(
"form"
)
}
}
,
/** 搜索按钮操作 */
handleQuery
()
{
...
...
@@ -599,7 +696,10 @@ export default {
}
,
/** 重置按钮操作 */
resetQuery
()
{
this
.
resetForm
(
"queryForm"
)
// 检查ref存在性
if
(
this
.
$refs
.
queryForm
)
{
this
.
resetForm
(
"queryForm"
)
}
this
.
handleQuery
()
}
,
// 多选框选中数据
...
...
@@ -614,39 +714,95 @@ export default {
this
.
open
=
true
this
.
title
=
"添加出库单"
}
,
/** 修改按钮操作 */
/** 修改按钮操作
(修复:加载明细时去重)
*/
handleUpdate
(
row
)
{
this
.
reset
()
const
id
=
row
.
id
||
this
.
ids
getOrders
(
id
).
then
(
response
=>
{
this
.
form
=
response
.
data
// 如果有明细数据,初始化明细表格
if
(
response
.
data
.
items
)
{
this
.
inboundOrderItemsList
=
response
.
data
.
items
.
map
((
item
,
index
)
=>
({
this
.
form
=
response
.
data
||
{
}
if
(
response
.
data
&&
response
.
data
.
outboundOrderItemsList
&&
Array
.
isArray
(
response
.
data
.
outboundOrderItemsList
))
{
// 按库存ID去重
const
uniqueItemsMap
=
new
Map
()
response
.
data
.
outboundOrderItemsList
.
forEach
(
item
=>
{
if
(
item
.
inventoryId
&&
!
uniqueItemsMap
.
has
(
item
.
inventoryId
))
{
uniqueItemsMap
.
set
(
item
.
inventoryId
,
item
)
}
}
)
const
uniqueItems
=
Array
.
from
(
uniqueItemsMap
.
values
())
this
.
outboundOrderItemsList
=
uniqueItems
.
map
((
item
,
index
)
=>
({
...
item
,
// 兼容materialUuids:适配物料选择器返回的materialCodes格式
materialUuids
:
item
.
materialUuids
||
item
.
materialId
||
''
,
index
:
index
+
1
index
:
index
+
1
,
inventoryId
:
item
.
inventoryId
||
''
,
divisor
:
item
.
divisor
!==
null
?
Number
(
item
.
divisor
)
:
0
,
itemStatus
:
item
.
itemStatus
!==
null
?
Number
(
item
.
itemStatus
)
:
1
,
labelColor
:
item
.
labelColor
!==
null
?
Number
(
item
.
labelColor
)
:
0
,
plannedQuantity
:
item
.
plannedQuantity
!==
null
?
Number
(
item
.
plannedQuantity
)
:
0
,
actualQuantity
:
item
.
actualQuantity
!==
null
?
Number
(
item
.
actualQuantity
)
:
0
,
isUsed
:
item
.
isUsed
!==
null
?
Number
(
item
.
isUsed
)
:
1
,
sortNo
:
item
.
sortNo
!==
null
?
Number
(
item
.
sortNo
)
:
0
,
materialUuids
:
item
.
materialId
||
''
,
warehouseId
:
item
.
warehouseId
||
''
,
locationId
:
item
.
locationId
||
''
,
voucherNumber
:
item
.
voucherNumber
||
''
,
shippedAt
:
item
.
shippedAt
||
''
,
shippedBy
:
item
.
shippedBy
||
''
,
remark
:
item
.
remark
||
''
,
orderId
:
response
.
data
.
id
}
))
// 计算总数量
this
.
calcTotalQuantity
()
}
this
.
open
=
true
this
.
title
=
"修改出库单"
}
)
}
,
/** 提交按钮 */
/** 提交按钮
(修复:提交前最后去重)
*/
submitForm
()
{
// 检查表单ref存在性
if
(
!
this
.
$refs
.
form
)
{
this
.
$message
.
error
(
'表单初始化失败,请重试'
)
return
}
this
.
$refs
[
"form"
].
validate
(
async
(
valid
)
=>
{
if
(
valid
)
{
// 组装完整数据(主表+明细)
if
(
this
.
outboundOrderItemsList
.
length
===
0
)
{
this
.
$message
.
warning
(
'请至少添加一条明细数据'
)
return
}
// 提交前按库存ID去重
const
uniqueDetailsMap
=
new
Map
()
this
.
outboundOrderItemsList
.
forEach
(
item
=>
{
if
(
item
.
inventoryId
&&
!
uniqueDetailsMap
.
has
(
item
.
inventoryId
))
{
uniqueDetailsMap
.
set
(
item
.
inventoryId
,
item
)
}
}
)
const
uniqueDetails
=
Array
.
from
(
uniqueDetailsMap
.
values
())
const
submitData
=
{
...
this
.
form
,
items
:
this
.
inboundOrderItemsList
.
map
(
item
=>
{
const
{
index
,
...
rest
}
=
item
// 剔除序号
// 提交时确保materialId是单选的物料编码(从materialUuids/原materialId取值)
rest
.
materialId
=
rest
.
materialUuids
||
rest
.
materialId
||
''
return
rest
outboundOrderItemsList
:
uniqueDetails
.
map
(
item
=>
{
const
{
index
,
materialUuids
,
...
rest
}
=
item
return
{
...
rest
,
orderId
:
this
.
form
.
id
||
this
.
form
.
orderId
,
inventoryId
:
rest
.
inventoryId
||
''
,
materialId
:
rest
.
materialId
||
''
,
batchCode
:
rest
.
batchCode
||
''
,
warehouseId
:
rest
.
warehouseId
||
''
,
locationId
:
rest
.
locationId
||
''
,
plannedQuantity
:
Number
(
rest
.
plannedQuantity
)
||
0
,
actualQuantity
:
Number
(
rest
.
actualQuantity
)
||
0
,
divisor
:
Number
(
rest
.
divisor
)
||
0
,
labelColor
:
Number
(
rest
.
labelColor
)
||
0
,
voucherNumber
:
rest
.
voucherNumber
||
''
,
itemStatus
:
Number
(
rest
.
itemStatus
)
||
1
,
shippedAt
:
rest
.
shippedAt
||
''
,
shippedBy
:
rest
.
shippedBy
||
''
,
remark
:
rest
.
remark
||
''
,
isUsed
:
1
,
sortNo
:
Number
(
rest
.
sortNo
)
||
0
}
}
)
}
try
{
...
...
@@ -660,8 +816,13 @@ export default {
this
.
open
=
false
this
.
getList
()
}
catch
(
error
)
{
this
.
$modal
.
msgError
(
error
.
msg
||
"操作失败"
)
}
// 【关键】捕获异常时弹窗(覆盖接口报错场景)
if
(
error
!==
'cancel'
)
{
// 排除用户点击取消的情况
// 优先用后端返回的msg,没有则显示默认文案
const
errorMsg
=
error
?.
response
?.
data
?.
msg
||
'库存被修改请重新确认'
;
this
.
$modal
.
msgError
(
errorMsg
);
// 核心弹窗代码(若依风格错误弹窗)
}
}
}
}
)
}
,
...
...
@@ -703,4 +864,9 @@ export default {
.
el
-
table
{
--
el
-
table
-
row
-
hover
-
bg
-
color
:
#
f8f9fa
;
}
/* 空数据提示样式 */
.
empty
-
tip
{
color
:
#
999
;
font
-
size
:
14
px
;
}
<
/style>
\ No newline at end of file
ruoyi-admin/src/main/java/com/ruoyi/web/controller/inventory/InboundOrderItemsController.java
View file @
15102e6e
...
...
@@ -4,7 +4,7 @@ import java.util.List;
import
java.util.UUID
;
import
javax.servlet.http.HttpServletResponse
;
import
com.ruoyi.
common.core.domain.entity.Material
s
;
import
com.ruoyi.
inventory.domain.InboundOrderItem
s
;
import
org.springframework.security.access.prepost.PreAuthorize
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.web.bind.annotation.GetMapping
;
...
...
@@ -19,7 +19,6 @@ import com.ruoyi.common.annotation.Log;
import
com.ruoyi.common.core.controller.BaseController
;
import
com.ruoyi.common.core.domain.AjaxResult
;
import
com.ruoyi.common.enums.BusinessType
;
import
com.ruoyi.inventory.domain.InboundOrderItems
;
import
com.ruoyi.inventory.service.IInboundOrderItemsService
;
import
com.ruoyi.common.utils.poi.ExcelUtil
;
import
com.ruoyi.common.core.page.TableDataInfo
;
...
...
ruoyi-common/pom.xml
View file @
15102e6e
...
...
@@ -118,6 +118,10 @@
<groupId>
javax.servlet
</groupId>
<artifactId>
javax.servlet-api
</artifactId>
</dependency>
<dependency>
<groupId>
org.aspectj
</groupId>
<artifactId>
aspectjweaver
</artifactId>
</dependency>
</dependencies>
...
...
ruoyi-common/src/main/java/com/ruoyi/common/annotation/SerialExecution.java
0 → 100644
View file @
15102e6e
package
com
.
ruoyi
.
common
.
annotation
;
import
java.lang.annotation.*
;
/**
* 若依框架适配 - 方法串行执行注解
* 标记该注解的方法,同一分组内串行执行,不同分组并行
*
* @author RuoYi
*/
@Target
({
ElementType
.
METHOD
})
@Retention
(
RetentionPolicy
.
RUNTIME
)
@Documented
public
@interface
SerialExecution
{
/**
* 串行分组(默认空,全局串行)
*/
String
group
()
default
""
;
/**
* 是否公平锁(按线程等待顺序执行)
*/
boolean
fair
()
default
false
;
}
\ No newline at end of file
ruoyi-common/src/main/java/com/ruoyi/common/aspectj/SerialExecutionAspect.java
0 → 100644
View file @
15102e6e
package
com
.
ruoyi
.
common
.
aspectj
;
import
com.ruoyi.common.annotation.SerialExecution
;
import
com.ruoyi.common.utils.StringUtils
;
import
org.aspectj.lang.ProceedingJoinPoint
;
import
org.aspectj.lang.annotation.Around
;
import
org.aspectj.lang.annotation.Aspect
;
import
org.aspectj.lang.annotation.Pointcut
;
import
org.aspectj.lang.reflect.MethodSignature
;
import
org.springframework.stereotype.Component
;
import
java.lang.reflect.Method
;
import
java.util.Map
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.locks.Lock
;
import
java.util.concurrent.locks.ReentrantLock
;
/**
* 若依框架适配 - 串行执行注解切面
* 参考若依 SysLogAspect 实现风格
*
* @author RuoYi
*/
@Aspect
@Component
public
class
SerialExecutionAspect
{
/**
* 分组锁缓存:key=分组名,value=锁对象
*/
private
final
Map
<
String
,
Lock
>
groupLockCache
=
new
ConcurrentHashMap
<>();
/**
* 切入点:拦截所有标记@SerialExecution的方法
*/
@Pointcut
(
"@annotation(com.ruoyi.common.annotation.SerialExecution)"
)
public
void
serialExecutionPointCut
()
{
}
/**
* 环绕通知:加锁执行,确保串行
*/
@Around
(
"serialExecutionPointCut()"
)
public
Object
around
(
ProceedingJoinPoint
joinPoint
)
throws
Throwable
{
// 1. 获取方法注解信息(参考若依日志切面的参数解析方式)
MethodSignature
signature
=
(
MethodSignature
)
joinPoint
.
getSignature
();
Method
method
=
signature
.
getMethod
();
SerialExecution
serialAnnotation
=
method
.
getAnnotation
(
SerialExecution
.
class
);
if
(
serialAnnotation
==
null
)
{
return
joinPoint
.
proceed
();
}
// 2. 解析分组(默认空=全局分组)
String
group
=
StringUtils
.
trimToEmpty
(
serialAnnotation
.
group
());
boolean
fair
=
serialAnnotation
.
fair
();
// 3. 获取/创建分组锁(复用若依 StringUtils 工具类)
Lock
lock
=
groupLockCache
.
computeIfAbsent
(
group
,
k
->
new
ReentrantLock
(
fair
));
// 4. 加锁执行(try-finally 确保锁释放,避免死锁)
try
{
lock
.
lock
();
// 执行原方法(若依业务方法的执行逻辑)
return
joinPoint
.
proceed
();
}
finally
{
lock
.
unlock
();
// 可选:若依日志记录(可接入若依的日志框架)
// LogUtils.info("串行方法执行完成,分组:{},方法:{}", group, method.getName());
}
}
}
\ No newline at end of file
ruoyi-inventory/src/main/java/com/ruoyi/inventory/controller/OutboundOrderItemsController.java
View file @
15102e6e
...
...
@@ -78,7 +78,16 @@ public class OutboundOrderItemsController extends BaseController
@PostMapping
public
AjaxResult
add
(
@RequestBody
OutboundOrderItems
outboundOrderItems
)
{
return
toAjax
(
outboundOrderItemsService
.
insertOutboundOrderItems
(
outboundOrderItems
));
try
{
// 调用业务层校验+插入逻辑
outboundOrderItemsService
.
insertOutboundOrderItems
(
outboundOrderItems
);
// 校验通过:返回成功(若依标准成功响应)
return
success
(
"新增出库单明细成功"
);
}
catch
(
Exception
e
)
{
// 校验失败:捕获异常,返回"库存被修改请重新确认"(前端弹窗用)
logger
.
error
(
"新增出库单明细失败"
,
e
);
return
error
(
"库存被修改请重新确认"
);
}
}
/**
...
...
@@ -89,9 +98,19 @@ public class OutboundOrderItemsController extends BaseController
@PutMapping
public
AjaxResult
edit
(
@RequestBody
OutboundOrderItems
outboundOrderItems
)
{
return
toAjax
(
outboundOrderItemsService
.
updateOutboundOrderItems
(
outboundOrderItems
));
try
{
// 调用业务层校验+插入逻辑
outboundOrderItemsService
.
updateOutboundOrderItems
(
outboundOrderItems
);
// 校验通过:返回成功(若依标准成功响应)
return
success
(
"新增出库单明细成功"
);
}
catch
(
Exception
e
)
{
// 校验失败:捕获异常,返回"库存被修改请重新确认"(前端弹窗用)
logger
.
error
(
"新增出库单明细失败"
,
e
);
return
error
(
"库存被修改请重新确认"
);
}
}
/**
* 删除出库单明细
*/
...
...
ruoyi-inventory/src/main/java/com/ruoyi/inventory/controller/OutboundOrdersController.java
View file @
15102e6e
...
...
@@ -2,6 +2,8 @@ package com.ruoyi.inventory.controller;
import
java.util.List
;
import
javax.servlet.http.HttpServletResponse
;
import
com.ruoyi.inventory.domain.Inventory
;
import
org.springframework.security.access.prepost.PreAuthorize
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.web.bind.annotation.GetMapping
;
...
...
@@ -70,6 +72,17 @@ public class OutboundOrdersController extends BaseController
}
/**
* 出货
*/
@PreAuthorize
(
"@ss.hasPermi('inventory:inventory:edit')"
)
@Log
(
title
=
"出货"
,
businessType
=
BusinessType
.
UPDATE
)
@PostMapping
(
"/ship"
)
public
AjaxResult
Ship
(
@RequestBody
OutboundOrders
outboundOrders
)
{
return
toAjax
(
outboundOrdersService
.
ship
(
outboundOrders
));
}
/**
* 新增出库单主
*/
@PreAuthorize
(
"@ss.hasPermi('inventory:orders:add')"
)
...
...
ruoyi-inventory/src/main/java/com/ruoyi/inventory/domain/OutboundOrderItems.java
View file @
15102e6e
...
...
@@ -40,6 +40,10 @@ public class OutboundOrderItems extends BaseEntity
@Excel
(
name
=
"库位ID 检索条件"
)
private
String
locationId
;
/** 库存ID */
private
String
inventoryId
;
/** 计划数量 */
@Excel
(
name
=
"计划数量"
)
private
Long
plannedQuantity
;
...
...
@@ -89,7 +93,15 @@ public class OutboundOrderItems extends BaseEntity
@Excel
(
name
=
"排序号"
)
private
String
updateUserCode
;
public
void
setId
(
String
id
)
public
String
getInventoryId
()
{
return
inventoryId
;
}
public
void
setInventoryId
(
String
inventoryId
)
{
this
.
inventoryId
=
inventoryId
;
}
public
void
setId
(
String
id
)
{
this
.
id
=
id
;
}
...
...
@@ -292,6 +304,7 @@ public class OutboundOrderItems extends BaseEntity
.
append
(
"createTime"
,
getCreateTime
())
.
append
(
"createUserCode"
,
getCreateUserCode
())
.
append
(
"updateTime"
,
getUpdateTime
())
.
append
(
"inventoryId"
,
getInventoryId
())
.
append
(
"updateUserCode"
,
getUpdateUserCode
())
.
toString
();
}
...
...
ruoyi-inventory/src/main/java/com/ruoyi/inventory/domain/OutboundOrderLog.java
View file @
15102e6e
...
...
@@ -22,6 +22,9 @@ public class OutboundOrderLog extends BaseEntity
@Excel
(
name
=
"出货单号ID"
)
private
String
orderId
;
/** 库存ID */
private
String
inventoryId
;
/** 货物ID */
@Excel
(
name
=
"货物ID"
)
private
String
materialId
;
...
...
@@ -81,7 +84,15 @@ public class OutboundOrderLog extends BaseEntity
this
.
warehouseId
=
warehouseId
;
}
public
String
getWarehouseId
()
public
String
getInventoryId
()
{
return
inventoryId
;
}
public
void
setInventoryId
(
String
inventoryId
)
{
this
.
inventoryId
=
inventoryId
;
}
public
String
getWarehouseId
()
{
return
warehouseId
;
}
...
...
@@ -137,6 +148,7 @@ public class OutboundOrderLog extends BaseEntity
.
append
(
"materialId"
,
getMaterialId
())
.
append
(
"warehouseId"
,
getWarehouseId
())
.
append
(
"batchCode"
,
getBatchCode
())
.
append
(
"inventoryId"
,
getInventoryId
())
.
append
(
"actualQuantity"
,
getActualQuantity
())
.
append
(
"itemStatus"
,
getItemStatus
())
.
append
(
"isUsed"
,
getIsUsed
())
...
...
ruoyi-inventory/src/main/java/com/ruoyi/inventory/mapper/InventoryMapper.java
View file @
15102e6e
...
...
@@ -53,6 +53,8 @@ public interface InventoryMapper
*/
public
int
updateInventory
(
Inventory
inventory
);
/**
* 删除库存
*
...
...
@@ -69,7 +71,7 @@ public interface InventoryMapper
*/
public
int
deleteInventoryByIds
(
String
[]
ids
);
public
List
<
Inventory
>
listByMat
re
ialId
(
String
materialId
);
public
List
<
Inventory
>
listByMat
er
ialId
(
String
materialId
);
/**
* @description: 获取库存盘点详细信息
...
...
ruoyi-inventory/src/main/java/com/ruoyi/inventory/mapper/OutboundOrderLogMapper.java
View file @
15102e6e
package
com
.
ruoyi
.
inventory
.
mapper
;
import
java.util.List
;
import
com.ruoyi.inventory.domain.OutboundOrderItems
;
import
com.ruoyi.inventory.domain.OutboundOrderLog
;
/**
...
...
@@ -19,6 +21,7 @@ public interface OutboundOrderLogMapper
*/
public
OutboundOrderLog
selectOutboundOrderLogById
(
String
id
);
/**
* 查询出库明细子(仅用于锁定数量统计)列表
*
...
...
@@ -34,7 +37,8 @@ public interface OutboundOrderLogMapper
* @param outboundOrderLog 出库明细子(仅用于锁定数量统计)
* @return 出库明细子(仅用于锁定数量统计)集合
*/
public
Long
selectLockedQuantity
(
OutboundOrderLog
outboundOrderLog
);
public
Long
selectLockedQuantityByInventory
(
String
id
);
/**
* 新增出库明细子(仅用于锁定数量统计)
...
...
@@ -66,6 +70,24 @@ public interface OutboundOrderLogMapper
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public
int
deleteOutboundOrderLogByOrdersId
(
String
id
);
/**
* 批量删除出库明细子(仅用于锁定数量统计)
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public
int
deleteOutboundOrderLogByIds
(
String
[]
ids
);
/**
* 批量新增出库单明细
*
* @param outboundOrderItemsList 出库单明细列表
* @return 结果
*/
public
int
batchOutboundOrderLog
(
List
<
OutboundOrderLog
>
outboundOrderLogs
);
}
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/IInventoryService.java
View file @
15102e6e
...
...
@@ -2,21 +2,23 @@ package com.ruoyi.inventory.service;
import
java.util.List
;
import
com.ruoyi.common.annotation.SerialExecution
;
import
com.ruoyi.common.core.page.TableDataInfo
;
import
com.ruoyi.inventory.domain.Inventory
;
import
com.ruoyi.inventory.domain.OutboundOrderItems
;
import
com.ruoyi.inventory.domain.StocktakeItems
;
/**
* 库存Service接口
*
*
* @author ruoyi
* @date 2025-12-03
*/
public
interface
IInventoryService
public
interface
IInventoryService
{
/**
* 查询库存
*
*
* @param id 库存主键
* @return 库存
*/
...
...
@@ -24,7 +26,7 @@ public interface IInventoryService
/**
* 查询库存列表
*
*
* @param inventory 库存
* @return 库存集合
*/
...
...
@@ -34,7 +36,7 @@ public interface IInventoryService
/**
* 新增库存
*
*
* @param inventory 库存
* @return 结果
*/
...
...
@@ -49,15 +51,23 @@ public interface IInventoryService
public
int
insertInventoryList
(
List
<
Inventory
>
inventoryList
);
/**
* 修改库存
*
*
* @param inventory 库存
* @return 结果
*/
public
int
updateInventory
(
Inventory
inventory
);
int
RefreshInventory
(
List
<
String
>
inventoryIds
);
@SerialExecution
(
group
=
"inventoryRefresh"
,
fair
=
true
)
int
ship
(
List
<
OutboundOrderItems
>
outboundOrderItems
);
@SerialExecution
(
group
=
"inventoryRefresh"
,
fair
=
true
)
boolean
inventoryLockValidation
(
List
<
OutboundOrderItems
>
outboundOrderItems
);
/**
* 批量删除库存
*
*
* @param ids 需要删除的库存主键集合
* @return 结果
*/
...
...
@@ -65,14 +75,13 @@ public interface IInventoryService
/**
* 删除库存信息
*
*
* @param id 库存主键
* @return 结果
*/
public
int
deleteInventoryById
(
String
id
);
public
List
<
Inventory
>
listByMatreialId
(
String
materialId
);
/**
* @description: 获取库存盘点详细信息
* @author cs
...
...
@@ -80,4 +89,6 @@ public interface IInventoryService
* @version 1.0
*/
public
List
<
StocktakeItems
>
selectstocktakeItemsList
();
}
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/IOutboundOrderLogService.java
View file @
15102e6e
...
...
@@ -35,7 +35,6 @@ public interface IOutboundOrderLogService
*/
public
int
insertOutboundOrderLog
(
OutboundOrderLog
outboundOrderLog
);
Long
selectLockedQuantity
(
OutboundOrderLog
outboundOrderLog
);
/**
* 修改出库明细子(仅用于锁定数量统计)
...
...
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/IOutboundOrdersService.java
View file @
15102e6e
...
...
@@ -58,4 +58,13 @@ public interface IOutboundOrdersService
* @return 结果
*/
public
int
deleteOutboundOrdersById
(
String
id
);
/**
* 出货
*
* @param id 出库单主主键
* @return 结果
*/
public
int
ship
(
OutboundOrders
outboundOrders
);
}
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/impl/InventoryServiceImpl.java
View file @
15102e6e
package
com
.
ruoyi
.
inventory
.
service
.
impl
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.UUID
;
import
java.util.stream.Collectors
;
import
com.ruoyi.common.annotation.SerialExecution
;
import
com.ruoyi.common.utils.DateUtils
;
import
com.ruoyi.inventory.domain.OutboundOrderItems
;
import
com.ruoyi.inventory.domain.OutboundOrderLog
;
import
com.ruoyi.inventory.domain.StocktakeItems
;
import
com.ruoyi.inventory.mapper.OutboundOrderLogMapper
;
import
org.springframework.beans.BeanUtils
;
import
org.apache.commons.collections4.CollectionUtils
;
import
org.apache.commons.lang3.SystemUtils
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
com.ruoyi.inventory.mapper.InventoryMapper
;
...
...
@@ -16,7 +21,7 @@ import com.ruoyi.inventory.service.IInventoryService;
/**
* 库存Service业务层处理
*
*
* @author ruoyi
* @date 2025-12-03
*/
...
...
@@ -30,7 +35,7 @@ public class InventoryServiceImpl implements IInventoryService
/**
* 查询库存
*
*
* @param id 库存主键
* @return 库存
*/
...
...
@@ -42,7 +47,7 @@ public class InventoryServiceImpl implements IInventoryService
/**
* 查询库存列表
*
*
* @param inventory 库存
* @return 库存
*/
...
...
@@ -52,19 +57,16 @@ public class InventoryServiceImpl implements IInventoryService
return
inventoryMapper
.
selectInventoryList
(
inventory
);
}
@Override
public
Inventory
selectInventory
(
Inventory
inventory
)
{
Inventory
inventory1
=
inventoryMapper
.
selectInventory
(
inventory
);
OutboundOrderLog
outboundOrderLog
=
new
OutboundOrderLog
();
BeanUtils
.
copyProperties
(
inventory1
,
outboundOrderLog
);
inventory1
.
setLockedQuantity
(
outboundOrderLogMapper
.
selectLockedQuantity
(
outboundOrderLog
));
return
inventory1
;
return
inventoryMapper
.
selectInventory
(
inventory
);
}
/**
* 新增库存
*
*
* @param inventory 库存
* @return 结果
*/
...
...
@@ -72,6 +74,7 @@ public class InventoryServiceImpl implements IInventoryService
public
int
insertInventory
(
Inventory
inventory
)
{
inventory
.
setCreateTime
(
DateUtils
.
getNowDate
());
inventory
.
setCreateBy
(
SystemUtils
.
getUserName
());
return
inventoryMapper
.
insertInventory
(
inventory
);
}
...
...
@@ -85,25 +88,80 @@ public class InventoryServiceImpl implements IInventoryService
}
return
count
;
}
/**
* 修改库存
*
*
* @param inventory 库存
* @return 结果
*/
@Override
public
int
updateInventory
(
Inventory
inventory
)
{
List
<
Inventory
>
inventoryList
=
Collections
.
singletonList
(
inventory
);
inventory
.
setUpdateTime
(
DateUtils
.
getNowDate
());
inventory
.
setUpdateBy
(
SystemUtils
.
getUserName
());
return
inventoryMapper
.
updateInventory
(
inventory
);
}
@SerialExecution
(
group
=
"inventoryRefresh"
,
fair
=
true
)
@Override
public
int
RefreshInventory
(
List
<
String
>
inventoryIds
)
{
for
(
String
inventoryId
:
inventoryIds
)
{
// 1. 空值前置校验:跳过空的循环项
if
(
inventoryId
==
null
)
{
continue
;
}
// 6. 查询锁定数量(优化:只查一次)
Long
lockedQuantity
=
outboundOrderLogMapper
.
selectLockedQuantityByInventory
(
inventoryId
);
Inventory
inventory
=
new
Inventory
();
inventory
.
setLockedQuantity
(
lockedQuantity
);
inventory
.
setId
(
inventoryId
);
inventoryMapper
.
updateInventory
(
inventory
);
}
return
1
;
}
@SerialExecution
(
group
=
"inventoryRefresh"
,
fair
=
true
)
@Override
public
int
ship
(
List
<
OutboundOrderItems
>
outboundOrderItems
)
{
if
(!
outboundOrderItems
.
isEmpty
())
{
for
(
OutboundOrderItems
outboundOrderItem
:
outboundOrderItems
)
{
OutboundOrderLog
outboundOrderLog
=
outboundOrderLogMapper
.
selectOutboundOrderLogById
(
outboundOrderItem
.
getId
());
Inventory
inventory
=
inventoryMapper
.
selectInventoryById
(
outboundOrderLog
.
getInventoryId
());
inventory
.
setQuantity
(
inventory
.
getQuantity
()-
outboundOrderItem
.
getActualQuantity
());
inventory
.
setLockedQuantity
(
inventory
.
getQuantity
()-
outboundOrderItem
.
getActualQuantity
());
if
(
inventory
.
getQuantity
()==
0
){
inventory
.
setInventoryStatus
(
0
l
);
}
updateInventory
(
inventory
);
}
}
return
1
;
}
@SerialExecution
(
group
=
"inventoryRefresh"
,
fair
=
true
)
@Override
public
boolean
inventoryLockValidation
(
List
<
OutboundOrderItems
>
outboundOrderItems
)
{
if
(!
outboundOrderItems
.
isEmpty
())
{
for
(
OutboundOrderItems
outboundOrderItem
:
outboundOrderItems
)
{
Inventory
inventory
=
inventoryMapper
.
selectInventoryById
(
outboundOrderItem
.
getInventoryId
());
if
(
inventory
.
getLockedQuantity
()+
outboundOrderItem
.
getActualQuantity
()>
inventory
.
getQuantity
()){
return
false
;
}
}
return
true
;
}
return
true
;
}
/**
* 批量删除库存
*
*
* @param ids 需要删除的库存主键
* @return 结果
*/
...
...
@@ -115,7 +173,7 @@ public class InventoryServiceImpl implements IInventoryService
/**
* 删除库存信息
*
*
* @param id 库存主键
* @return 结果
*/
...
...
@@ -125,11 +183,19 @@ public class InventoryServiceImpl implements IInventoryService
return
inventoryMapper
.
deleteInventoryById
(
id
);
}
@SerialExecution
(
group
=
"inventoryRefresh"
,
fair
=
true
)
@Override
public
List
<
Inventory
>
listByMatreialId
(
String
materialId
)
{
return
inventoryMapper
.
listByMatreialId
(
materialId
);
Inventory
inventory
=
new
Inventory
();
inventory
.
setMaterialId
(
materialId
);
List
<
String
>
inventoryIds
=
CollectionUtils
.
isEmpty
(
inventoryMapper
.
listByMaterialId
(
materialId
))
?
Collections
.
emptyList
()
// 空时返回空列表,避免后续NPE
:
inventoryMapper
.
listByMaterialId
(
materialId
).
stream
()
.
map
(
inventory2
->
inventory2
.
getId
())
// 提取ID(核心修正)
.
collect
(
Collectors
.
toList
());
RefreshInventory
(
inventoryIds
);
return
inventoryMapper
.
listByMaterialId
(
materialId
);
}
/**
* @description: 获取库存盘点详细信息
* @author cs
...
...
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/impl/OutboundOrderItemsServiceImpl.java
View file @
15102e6e
package
com
.
ruoyi
.
inventory
.
service
.
impl
;
import
java.util.ArrayList
;
import
java.util.List
;
import
com.ruoyi.common.utils.DateUtils
;
import
com.ruoyi.inventory.domain.Inventory
;
import
com.ruoyi.inventory.domain.OutboundOrderLog
;
import
org.springframework.beans.BeanUtils
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
com.ruoyi.inventory.mapper.OutboundOrderItemsMapper
;
...
...
@@ -53,12 +49,7 @@ public class OutboundOrderItemsServiceImpl implements IOutboundOrderItemsService
return
outboundOrderItemsMapper
.
selectOutboundOrderItemsList
(
outboundOrderItems
);
}
/**
* 新增出库单明细
*
* @param outboundOrderItemsInventory 出库单明细库存DTO
* @return 结果
*/
@Override
public
int
insertOutboundOrderItems
(
OutboundOrderItems
outboundOrderItems
)
{
...
...
@@ -66,12 +57,7 @@ public class OutboundOrderItemsServiceImpl implements IOutboundOrderItemsService
return
outboundOrderItemsMapper
.
insertOutboundOrderItems
(
outboundOrderItems
);
}
/**
* 修改出库单明细
*
* @param outboundOrderItemsInventory 出库单明细库存DTO
* @return 结果
*/
@Override
public
int
updateOutboundOrderItems
(
OutboundOrderItems
outboundOrderItems
)
{
...
...
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/impl/OutboundOrderLogServiceImpl.java
View file @
15102e6e
...
...
@@ -54,10 +54,6 @@ public class OutboundOrderLogServiceImpl implements IOutboundOrderLogService
{
return
outboundOrderLogMapper
.
insertOutboundOrderLog
(
outboundOrderLog
);
}
@Override
public
Long
selectLockedQuantity
(
OutboundOrderLog
outboundOrderLog
){
return
outboundOrderLogMapper
.
selectLockedQuantity
(
outboundOrderLog
);
}
/**
* 修改出库明细子(仅用于锁定数量统计)
*
...
...
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/impl/OutboundOrdersServiceImpl.java
View file @
15102e6e
package
com
.
ruoyi
.
inventory
.
service
.
impl
;
import
java.util.List
;
import
com.ruoyi.common.annotation.SerialExecution
;
import
com.ruoyi.common.utils.DateUtils
;
import
com.ruoyi.inventory.domain.Inventory
;
import
com.ruoyi.inventory.domain.OutboundOrderLog
;
import
com.ruoyi.inventory.mapper.InventoryMapper
;
import
com.ruoyi.inventory.mapper.OutboundOrderItemsMapper
;
import
com.ruoyi.inventory.mapper.OutboundOrderLogMapper
;
import
org.apache.commons.lang3.SystemUtils
;
import
org.springframework.beans.BeanUtils
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
java.util.ArrayList
;
import
java.util.UUID
;
import
com.ruoyi.common.utils.StringUtils
;
import
org.springframework.transaction.annotation.Transactional
;
import
com.ruoyi.inventory.domain.OutboundOrderItems
;
...
...
@@ -14,19 +25,26 @@ import com.ruoyi.inventory.service.IOutboundOrdersService;
/**
* 出库单主Service业务层处理
*
*
* @author ruoyi
* @date 2025-12-03
*/
@Service
public
class
OutboundOrdersServiceImpl
implements
IOutboundOrdersService
public
class
OutboundOrdersServiceImpl
implements
IOutboundOrdersService
{
@Autowired
private
OutboundOrdersMapper
outboundOrdersMapper
;
@Autowired
private
OutboundOrderItemsMapper
outboundOrderItemsMapper
;
@Autowired
private
OutboundOrderLogMapper
outboundOrderLogMapper
;
@Autowired
private
InventoryServiceImpl
inventoryService
;
/**
* 查询出库单主
*
*
* @param id 出库单主主键
* @return 出库单主
*/
...
...
@@ -39,7 +57,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
/**
* 查询出库单主列表
*
*
* @param outboundOrders 出库单主
* @return 出库单主
*/
...
...
@@ -51,7 +69,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
/**
* 新增出库单主
*
*
* @param outboundOrders 出库单主
* @return 结果
*/
...
...
@@ -60,6 +78,8 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
public
int
insertOutboundOrders
(
OutboundOrders
outboundOrders
)
{
outboundOrders
.
setCreateTime
(
DateUtils
.
getNowDate
());
outboundOrders
.
setCreateBy
(
SystemUtils
.
getUserName
());
outboundOrders
.
setId
(
UUID
.
randomUUID
().
toString
());
int
rows
=
outboundOrdersMapper
.
insertOutboundOrders
(
outboundOrders
);
insertOutboundOrderItems
(
outboundOrders
);
return
rows
;
...
...
@@ -67,7 +87,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
/**
* 修改出库单主
*
*
* @param outboundOrders 出库单主
* @return 结果
*/
...
...
@@ -77,13 +97,14 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
{
outboundOrders
.
setUpdateTime
(
DateUtils
.
getNowDate
());
outboundOrdersMapper
.
deleteOutboundOrderItemsByOrderId
(
outboundOrders
.
getId
());
outboundOrderLogMapper
.
deleteOutboundOrderLogByOrdersId
(
outboundOrders
.
getId
());
insertOutboundOrderItems
(
outboundOrders
);
return
outboundOrdersMapper
.
updateOutboundOrders
(
outboundOrders
);
}
/**
* 批量删除出库单主
*
*
* @param ids 需要删除的出库单主主键
* @return 结果
*/
...
...
@@ -97,7 +118,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
/**
* 删除出库单主信息
*
*
* @param id 出库单主主键
* @return 结果
*/
...
...
@@ -108,26 +129,88 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
outboundOrdersMapper
.
deleteOutboundOrderItemsByOrderId
(
id
);
return
outboundOrdersMapper
.
deleteOutboundOrdersById
(
id
);
}
@SerialExecution
(
group
=
"inventoryRefresh"
,
fair
=
true
)
@Override
public
int
ship
(
OutboundOrders
outboundOrders
)
{
OutboundOrderItems
outboundOrderItems1
=
new
OutboundOrderItems
();
outboundOrderItems1
.
setOrderId
(
outboundOrders
.
getId
());
List
<
OutboundOrderItems
>
outboundOrderItems
=
outboundOrderItemsMapper
.
selectOutboundOrderItemsList
(
outboundOrderItems1
);
List
<
OutboundOrderItems
>
outboundOrderItems2
=
outboundOrderItems
;
OutboundOrderLog
outboundOrderLog
=
new
OutboundOrderLog
();
for
(
OutboundOrderItems
outboundOrderItem
:
outboundOrderItems
)
{
outboundOrderItem
.
setItemStatus
(
3
l
);
outboundOrderItemsMapper
.
updateOutboundOrderItems
(
outboundOrderItem
);
outboundOrderLog
.
setId
(
outboundOrderItem
.
getId
());
outboundOrderLog
.
setItemStatus
(
outboundOrderItem
.
getItemStatus
());
outboundOrderLogMapper
.
updateOutboundOrderLog
(
outboundOrderLog
);
}
outboundOrders
.
setId
(
outboundOrders
.
getId
());
outboundOrders
.
setOrderStatus
(
3
l
);
outboundOrders
.
setUpdateTime
(
DateUtils
.
getNowDate
());
outboundOrders
.
setUpdateBy
(
SystemUtils
.
getUserName
());
outboundOrdersMapper
.
updateOutboundOrders
(
outboundOrders
);
inventoryService
.
ship
(
outboundOrderItems2
);
return
1
;
}
/**
* 新增出库单明细信息
*
*
* @param outboundOrders 出库单主对象
*/
public
void
insertOutboundOrderItems
(
OutboundOrders
outboundOrders
)
{
public
void
insertOutboundOrderItems
(
OutboundOrders
outboundOrders
)
{
List
<
OutboundOrderItems
>
outboundOrderItemsList
=
outboundOrders
.
getOutboundOrderItemsList
();
String
id
=
outboundOrders
.
getId
();
if
(
StringUtils
.
isNotNull
(
outboundOrderItemsList
))
{
for
(
OutboundOrderItems
outboundOrderItems
:
outboundOrderItemsList
)
{
outboundOrderItems
.
setOrderId
(
id
);
}
if
(
outboundOrderItemsList
.
size
()
>
0
)
{
outboundOrdersMapper
.
batchOutboundOrderItems
(
outboundOrderItemsList
);
}
// 1. 先做空列表校验(提前返回,避免无效逻辑)
if
(
outboundOrderItemsList
==
null
||
outboundOrderItemsList
.
isEmpty
())
{
return
;
}
// 2. 库存校验:失败时抛异常(核心修正:! 取反 + 异常抛出后代码立即终止)
boolean
isValid
=
inventoryService
.
inventoryLockValidation
(
outboundOrderItemsList
);
if
(!
isValid
)
{
// 校验失败(返回false)时抛异常
throw
new
RuntimeException
(
"库存被修改请重新确认"
);
// 抛异常后,方法立即停止运行
}
// 2. 为明细设置订单ID和主键ID
for
(
OutboundOrderItems
items
:
outboundOrderItemsList
)
{
items
.
setOrderId
(
id
);
// 生成无横线的UUID作为主键
items
.
setId
(
UUID
.
randomUUID
().
toString
().
replace
(
"-"
,
""
));
}
// 3. 批量插入出库单明细
outboundOrdersMapper
.
batchOutboundOrderItems
(
outboundOrderItemsList
);
// 4. 正确拷贝明细列表到日志列表(修复核心错误:遍历逐个拷贝)
List
<
String
>
inventoryIds
=
new
ArrayList
<>();
List
<
OutboundOrderLog
>
outboundOrderLogs
=
new
ArrayList
<>();
for
(
OutboundOrderItems
items
:
outboundOrderItemsList
)
{
OutboundOrderLog
log
=
new
OutboundOrderLog
();
BeanUtils
.
copyProperties
(
items
,
log
);
// 单个对象属性拷贝
outboundOrderLogs
.
add
(
log
);
inventoryIds
.
add
(
log
.
getInventoryId
());
deleteOutboundOrdersById
(
items
.
getId
());
}
// 5. 非空校验后插入日志(避免空列表触发SQL语法错误)
if
(!
outboundOrderLogs
.
isEmpty
())
{
outboundOrderLogMapper
.
batchOutboundOrderLog
(
outboundOrderLogs
);
}
// 7. 非空校验后刷新库存
if
(!
inventoryIds
.
isEmpty
())
{
inventoryService
.
RefreshInventory
(
inventoryIds
);
}
}
}
ruoyi-inventory/src/main/resources/mapper/inventory/InventoryMapper.xml
View file @
15102e6e
...
...
@@ -123,9 +123,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<include
refid=
"selectInventoryVo"
/>
where id = #{id}
</select>
<select
id=
"listByMat
re
ialId"
parameterType=
"String"
resultMap=
"InventoryResult"
>
<select
id=
"listByMat
er
ialId"
parameterType=
"String"
resultMap=
"InventoryResult"
>
<include
refid=
"selectInventoryVo"
/>
where material_id = #{materialId}
where 1=1
<if
test=
"materialId != null and materialId.trim() != ''"
>
and material_id = #{materialId}
</if>
<![CDATA[
and inventory_status = '1'
and quantity >
locked_quantity
]]>
</select>
<insert
id=
"insertInventory"
parameterType=
"Inventory"
>
insert into inventory
...
...
ruoyi-inventory/src/main/resources/mapper/inventory/OutboundOrderItemsMapper.xml
View file @
15102e6e
...
...
@@ -11,6 +11,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result
property=
"batchCode"
column=
"batch_code"
/>
<result
property=
"warehouseId"
column=
"warehouse_id"
/>
<result
property=
"locationId"
column=
"location_id"
/>
<result
property=
"inventoryId"
column=
"inventory_id"
/>
<result
property=
"plannedQuantity"
column=
"planned_quantity"
/>
<result
property=
"actualQuantity"
column=
"actual_quantity"
/>
<result
property=
"divisor"
column=
"divisor"
/>
...
...
@@ -29,7 +30,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap>
<sql
id=
"selectOutboundOrderItemsVo"
>
select id, order_id, material_id, batch_code, warehouse_id, location_id, planned_quantity, actual_quantity, divisor, label_color, voucher_number, item_status, shipped_at, shipped_by, remark, is_used, sort_no, create_time, create_user_code, update_time, update_user_code from outbound_order_items
select id, order_id, material_id, batch_code, warehouse_id, location_id,
inventory_id,
planned_quantity, actual_quantity, divisor, label_color, voucher_number, item_status, shipped_at, shipped_by, remark, is_used, sort_no, create_time, create_user_code, update_time, update_user_code from outbound_order_items
</sql>
<select
id=
"selectOutboundOrderItemsList"
parameterType=
"OutboundOrderItems"
resultMap=
"OutboundOrderItemsResult"
>
...
...
@@ -40,6 +41,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if
test=
"batchCode != null and batchCode != ''"
>
and batch_code = #{batchCode}
</if>
<if
test=
"warehouseId != null and warehouseId != ''"
>
and warehouse_id = #{warehouseId}
</if>
<if
test=
"locationId != null and locationId != ''"
>
and location_id = #{locationId}
</if>
<if
test=
"inventoryId != null and inventoryId != ''"
>
and inventory_id = #{inventoryId}
</if>
<if
test=
"plannedQuantity != null "
>
and planned_quantity = #{plannedQuantity}
</if>
<if
test=
"actualQuantity != null "
>
and actual_quantity = #{actualQuantity}
</if>
<if
test=
"divisor != null "
>
and divisor = #{divisor}
</if>
...
...
@@ -69,6 +71,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if
test=
"batchCode != null"
>
batch_code,
</if>
<if
test=
"warehouseId != null"
>
warehouse_id,
</if>
<if
test=
"locationId != null"
>
location_id,
</if>
<if
test=
"inventoryId != null"
>
inventory_id,
</if>
<if
test=
"plannedQuantity != null"
>
planned_quantity,
</if>
<if
test=
"actualQuantity != null"
>
actual_quantity,
</if>
<if
test=
"divisor != null"
>
divisor,
</if>
...
...
@@ -92,6 +95,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if
test=
"batchCode != null"
>
#{batchCode},
</if>
<if
test=
"warehouseId != null"
>
#{warehouseId},
</if>
<if
test=
"locationId != null"
>
#{locationId},
</if>
<if
test=
"inventoryId != null"
>
#{inventoryId},
</if>
<if
test=
"plannedQuantity != null"
>
#{plannedQuantity},
</if>
<if
test=
"actualQuantity != null"
>
#{actualQuantity},
</if>
<if
test=
"divisor != null"
>
#{divisor},
</if>
...
...
@@ -118,6 +122,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if
test=
"batchCode != null"
>
batch_code = #{batchCode},
</if>
<if
test=
"warehouseId != null"
>
warehouse_id = #{warehouseId},
</if>
<if
test=
"locationId != null"
>
location_id = #{locationId},
</if>
<if
test=
"inventoryId != null"
>
inventory_id = #{inventoryId},
</if>
<if
test=
"plannedQuantity != null"
>
planned_quantity = #{plannedQuantity},
</if>
<if
test=
"actualQuantity != null"
>
actual_quantity = #{actualQuantity},
</if>
<if
test=
"divisor != null"
>
divisor = #{divisor},
</if>
...
...
ruoyi-inventory/src/main/resources/mapper/inventory/OutboundOrderLogMapper.xml
View file @
15102e6e
...
...
@@ -7,6 +7,7 @@
<resultMap
type=
"OutboundOrderLog"
id=
"OutboundOrderLogResult"
>
<result
property=
"id"
column=
"id"
/>
<result
property=
"orderId"
column=
"order_id"
/>
<result
property=
"inventoryId"
column=
"inventory_id"
/>
<result
property=
"materialId"
column=
"material_id"
/>
<result
property=
"warehouseId"
column=
"warehouse_id"
/>
<result
property=
"batchCode"
column=
"batch_code"
/>
...
...
@@ -15,8 +16,9 @@
<result
property=
"isUsed"
column=
"is_used"
/>
</resultMap>
<!-- 补充inventory_id到查询字段中 -->
<sql
id=
"selectOutboundOrderLogVo"
>
select id, order_id, material_id, warehouse_id, batch_code, actual_quantity, item_status, is_used
select id, order_id,
inventory_id,
material_id, warehouse_id, batch_code, actual_quantity, item_status, is_used
from outbound_order_log
</sql>
...
...
@@ -24,6 +26,7 @@
<include
refid=
"selectOutboundOrderLogVo"
/>
<where>
<if
test=
"orderId != null and orderId != ''"
>
and order_id = #{orderId}
</if>
<if
test=
"inventoryId != null and inventoryId != ''"
>
and inventory_id = #{inventoryId}
</if>
<if
test=
"materialId != null and materialId != ''"
>
and material_id = #{materialId}
</if>
<if
test=
"warehouseId != null and warehouseId != ''"
>
and warehouse_id = #{warehouseId}
</if>
<if
test=
"batchCode != null and batchCode != ''"
>
and batch_code = #{batchCode}
</if>
...
...
@@ -33,26 +36,39 @@
</where>
</select>
<select
id=
"selectLockedQuantity"
parameterType=
"OutboundOrderLog"
resultType=
"java.lang.Long"
>
select ifnull(sum(actual_quantity), 0)
from outbound_order_log
<select
id=
"deleteLog"
parameterType=
"OutboundOrderLog"
resultMap=
"OutboundOrderLogResult"
>
delete from outbound_order_log
<where>
<if
test=
"materialId != null and materialId != ''"
>
and material_id = #{materialId}
</if>
<if
test=
"warehouseId != null and warehouseId != ''"
>
and warehouse_id = #{warehouseId}
</if>
<if
test=
"batchCode != null and batchCode != ''"
>
and batch_code = #{batchCode}
</if>
<if
test=
"itemStatus != null "
>
and item_status = #{itemStatus}
</if>
<!-- 补充inventory_id条件 -->
<if
test=
"inventoryId != null and inventoryId != ''"
>
and inventory_id = #{inventoryId}
</if>
</where>
</select>
<!-- 修正参数错误:原#{id}改为#{inventoryId} -->
<select
id=
"selectLockedQuantityByInventory"
parameterType=
"OutboundOrderLog"
resultType=
"java.lang.Long"
>
select ifnull(sum(actual_quantity), 0)
from outbound_order_log
where item_status=1
<if
test=
"inventoryId != null and inventoryId != ''"
>
and inventory_id = #{inventoryId}
</if>
</select>
<select
id=
"selectOutboundOrderLogById"
parameterType=
"String"
resultMap=
"OutboundOrderLogResult"
>
<include
refid=
"selectOutboundOrderLogVo"
/>
where id = #{id}
</select>
<!-- 插入语句补充inventory_id -->
<insert
id=
"insertOutboundOrderLog"
parameterType=
"OutboundOrderLog"
>
insert into outbound_order_log
<trim
prefix=
"("
suffix=
")"
suffixOverrides=
","
>
<if
test=
"id != null"
>
id,
</if>
<if
test=
"orderId != null"
>
order_id,
</if>
<if
test=
"inventoryId != null"
>
inventory_id,
</if>
<if
test=
"materialId != null"
>
material_id,
</if>
<if
test=
"warehouseId != null"
>
warehouse_id,
</if>
<if
test=
"batchCode != null"
>
batch_code,
</if>
...
...
@@ -63,6 +79,7 @@
<trim
prefix=
"values ("
suffix=
")"
suffixOverrides=
","
>
<if
test=
"id != null"
>
#{id},
</if>
<if
test=
"orderId != null"
>
#{orderId},
</if>
<if
test=
"inventoryId != null"
>
#{inventoryId},
</if>
<if
test=
"materialId != null"
>
#{materialId},
</if>
<if
test=
"warehouseId != null"
>
#{warehouseId},
</if>
<if
test=
"batchCode != null"
>
#{batchCode},
</if>
...
...
@@ -72,10 +89,12 @@
</trim>
</insert>
<!-- 更新语句补充inventory_id -->
<update
id=
"updateOutboundOrderLog"
parameterType=
"OutboundOrderLog"
>
update outbound_order_log
<trim
prefix=
"SET"
suffixOverrides=
","
>
<if
test=
"orderId != null"
>
order_id = #{orderId},
</if>
<if
test=
"inventoryId != null"
>
inventory_id = #{inventoryId},
</if>
<if
test=
"materialId != null"
>
material_id = #{materialId},
</if>
<if
test=
"warehouseId != null"
>
warehouse_id = #{warehouseId},
</if>
<if
test=
"batchCode != null"
>
batch_code = #{batchCode},
</if>
...
...
@@ -90,10 +109,39 @@
delete from outbound_order_log where id = #{id}
</delete>
<delete
id=
"deleteOutboundOrderLogByIds"
parameterType=
"String"
>
delete from outbound_order_log where id in
<foreach
item=
"id"
collection=
"array"
open=
"("
separator=
","
close=
")"
>
#{id}
</foreach>
<delete
id=
"deleteOutboundOrderLogByOrdersId"
parameterType=
"String"
>
delete from outbound_order_log where order_id = #{id}
</delete>
<!-- 批量插入补充inventory_id -->
<insert
id=
"batchOutboundOrderLog"
>
<!-- 增加非空判断,避免空列表导致SQL语法错误 -->
<if
test=
"list != null and list.size() > 0"
>
insert into outbound_order_log(
id,
order_id,
inventory_id,
<!-- 新增 -->
material_id,
warehouse_id,
batch_code,
<!-- 对应实体类batchCode -->
actual_quantity,
item_status,
is_used
) values
<foreach
item=
"item"
index=
"index"
collection=
"list"
separator=
","
>
(
#{item.id},
#{item.orderId},
#{item.inventoryId},
#{item.materialId},
#{item.warehouseId},
#{item.batchCode},
#{item.actualQuantity},
#{item.itemStatus},
#{item.isUsed}
)
</foreach>
</if>
</insert>
</mapper>
\ No newline at end of file
ruoyi-inventory/src/main/resources/mapper/inventory/OutboundOrdersMapper.xml
View file @
15102e6e
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper
namespace=
"com.ruoyi.inventory.mapper.OutboundOrdersMapper"
>
<resultMap
type=
"OutboundOrders"
id=
"OutboundOrdersResult"
>
<result
property=
"id"
column=
"id"
/>
<result
property=
"orderId"
column=
"order_id"
/>
...
...
@@ -31,40 +31,40 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<collection
property=
"outboundOrderItemsList"
ofType=
"OutboundOrderItems"
column=
"id"
select=
"selectOutboundOrderItemsList"
/>
</resultMap>
<!-- 修复:仅保留子表outbound_order_items的字段映射,删除错误的主表字段 -->
<resultMap
type=
"OutboundOrderItems"
id=
"OutboundOrderItemsResult"
>
<result
property=
"id"
column=
"id"
/>
<result
property=
"orderId"
column=
"order_id"
/>
<result
property=
"materialId"
column=
"material_id"
/>
<result
property=
"batchId"
column=
"batch_id"
/>
<result
property=
"warehouseId"
column=
"warehouse_id"
/>
<result
property=
"locationId"
column=
"location_id"
/>
<result
property=
"plannedQuantity"
column=
"planned_quantity"
/>
<result
property=
"actualQuantity"
column=
"actual_quantity"
/>
<result
property=
"plannedPackages"
column=
"planned_packages"
/>
<result
property=
"actualPackages"
column=
"actual_packages"
/>
<result
property=
"divisor"
column=
"divisor"
/>
<result
property=
"labelColor"
column=
"label_color"
/>
<result
property=
"voucherNumber"
column=
"voucher_number"
/>
<result
property=
"unitPrice"
column=
"unit_price"
/>
<result
property=
"itemStatus"
column=
"item_status"
/>
<result
property=
"receivedAt"
column=
"received_at"
/>
<result
property=
"receivedBy"
column=
"received_by"
/>
<result
property=
"remark"
column=
"remark"
/>
<result
property=
"isUsed"
column=
"is_used"
/>
<result
property=
"sortNo"
column=
"sort_no"
/>
<result
property=
"createTime"
column=
"create_time"
/>
<result
property=
"createUserCode"
column=
"create_user_code"
/>
<result
property=
"updateTime"
column=
"update_time"
/>
<result
property=
"updateUserCode"
column=
"update_user_code"
/>
<result
property=
"id"
column=
"id"
/>
<result
property=
"orderId"
column=
"order_id"
/>
<result
property=
"materialId"
column=
"material_id"
/>
<result
property=
"batchCode"
column=
"batch_code"
/>
<result
property=
"warehouseId"
column=
"warehouse_id"
/>
<result
property=
"locationId"
column=
"location_id"
/>
<result
property=
"plannedQuantity"
column=
"planned_quantity"
/>
<result
property=
"inventoryId"
column=
"inventory_id"
/>
<result
property=
"actualQuantity"
column=
"actual_quantity"
/>
<result
property=
"divisor"
column=
"divisor"
/>
<result
property=
"labelColor"
column=
"label_color"
/>
<result
property=
"voucherNumber"
column=
"voucher_number"
/>
<result
property=
"itemStatus"
column=
"item_status"
/>
<result
property=
"shippedAt"
column=
"shipped_at"
/>
<result
property=
"shippedBy"
column=
"shipped_by"
/>
<result
property=
"remark"
column=
"remark"
/>
<result
property=
"isUsed"
column=
"is_used"
/>
<result
property=
"sortNo"
column=
"sort_no"
/>
<result
property=
"createTime"
column=
"create_time"
/>
<result
property=
"createUserCode"
column=
"create_user_code"
/>
<result
property=
"updateTime"
column=
"update_time"
/>
<result
property=
"updateUserCode"
column=
"update_user_code"
/>
</resultMap>
<sql
id=
"selectOutboundOrdersVo"
>
select id, order_id, system_no, order_type_id, batch_code, warehouse_id, owner_id, order_status, inbound_date, destination, total_planned_quantity, total_actual_quantity, total_packages, remark, is_used, sort_no, create_time, create_user_code, update_time, update_user_code from outbound_orders
</sql>
<select
id=
"selectOutboundOrdersList"
parameterType=
"OutboundOrders"
resultMap=
"OutboundOrdersResult"
>
<include
refid=
"selectOutboundOrdersVo"
/>
<where>
<where>
<if
test=
"orderId != null and orderId != ''"
>
and order_id = #{orderId}
</if>
<if
test=
"systemNo != null and systemNo != ''"
>
and system_no = #{systemNo}
</if>
<if
test=
"orderTypeId != null and orderTypeId != ''"
>
and order_type_id = #{orderTypeId}
</if>
...
...
@@ -83,16 +83,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if
test=
"updateUserCode != null and updateUserCode != ''"
>
and update_user_code = #{updateUserCode}
</if>
</where>
</select>
<select
id=
"selectOutboundOrdersById"
parameterType=
"String"
resultMap=
"OutboundOrdersOutboundOrderItemsResult"
>
select id, order_id, system_no, order_type_id, batch_code, warehouse_id, owner_id, order_status, inbound_date, destination, total_planned_quantity, total_actual_quantity, total_packages, remark, is_used, sort_no, create_time, create_user_code, update_time, update_user_code
from outbound_orders
where id = #{id}
</select>
<!-- 仅保留子表查询逻辑,字段完整且映射正确 -->
<select
id=
"selectOutboundOrderItemsList"
parameterType=
"String"
resultMap=
"OutboundOrderItemsResult"
>
select id, order_id, material_id, batch_code, warehouse_id, location_id, planned_quantity, actual_quantity, divisor, label_color, voucher_number, item_status, shipped_at, shipped_by, remark, is_used, sort_no, create_time, create_user_code, update_time, update_user_code
from outbound_order_items
select id, order_id, material_id, batch_code, warehouse_id, location_id, planned_quantity, actual_quantity, divisor, label_color, voucher_number, item_status, shipped_at, shipped_by, remark, is_used, sort_no, create_time, create_user_code, update_time, update_user_code
, inventory_id
from outbound_order_items
where order_id = #{id}
</select>
...
...
@@ -119,7 +120,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if
test=
"createUserCode != null"
>
create_user_code,
</if>
<if
test=
"updateTime != null"
>
update_time,
</if>
<if
test=
"updateUserCode != null"
>
update_user_code,
</if>
</trim>
</trim>
<trim
prefix=
"values ("
suffix=
")"
suffixOverrides=
","
>
<if
test=
"id != null"
>
#{id},
</if>
<if
test=
"orderId != null"
>
#{orderId},
</if>
...
...
@@ -141,7 +142,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if
test=
"createUserCode != null"
>
#{createUserCode},
</if>
<if
test=
"updateTime != null"
>
#{updateTime},
</if>
<if
test=
"updateUserCode != null"
>
#{updateUserCode},
</if>
</trim>
</trim>
</insert>
<update
id=
"updateOutboundOrders"
parameterType=
"OutboundOrders"
>
...
...
@@ -175,14 +176,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</delete>
<delete
id=
"deleteOutboundOrdersByIds"
parameterType=
"String"
>
delete from outbound_orders where id in
delete from outbound_orders where id in
<foreach
item=
"id"
collection=
"array"
open=
"("
separator=
","
close=
")"
>
#{id}
</foreach>
</delete>
<delete
id=
"deleteOutboundOrderItemsByOrderIds"
parameterType=
"String"
>
delete from outbound_order_items where order_id in
delete from outbound_order_items where order_id in
<foreach
item=
"orderId"
collection=
"array"
open=
"("
separator=
","
close=
")"
>
#{orderId}
</foreach>
...
...
@@ -193,9 +194,20 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</delete>
<insert
id=
"batchOutboundOrderItems"
>
insert into outbound_order_items( id, order_id, material_id, batch_id, warehouse_id, location_id, planned_quantity, actual_quantity, planned_packages, actual_packages, divisor, label_color, voucher_number, unit_price, item_status, received_at, received_by, remark, is_used, sort_no, create_time, create_user_code, update_time, update_user_code) values
insert into outbound_order_items(
id, order_id, material_id, batch_code, warehouse_id, location_id,
planned_quantity, actual_quantity, divisor, label_color, voucher_number,
item_status, shipped_at, shipped_by, remark, is_used, sort_no,
create_time, create_user_code, update_time, update_user_code,inventory_id
) values
<foreach
item=
"item"
index=
"index"
collection=
"list"
separator=
","
>
( #{item.id}, #{item.orderId}, #{item.materialId}, #{item.batchId}, #{item.warehouseId}, #{item.locationId}, #{item.plannedQuantity}, #{item.actualQuantity}, #{item.plannedPackages}, #{item.actualPackages}, #{item.divisor}, #{item.labelColor}, #{item.voucherNumber}, #{item.unitPrice}, #{item.itemStatus}, #{item.receivedAt}, #{item.receivedBy}, #{item.remark}, #{item.isUsed}, #{item.sortNo}, #{item.createTime}, #{item.createUserCode}, #{item.updateTime}, #{item.updateUserCode})
(
#{item.id}, #{item.orderId}, #{item.materialId}, #{item.batchCode}, #{item.warehouseId},
#{item.locationId}, #{item.plannedQuantity}, #{item.actualQuantity}, #{item.divisor},
#{item.labelColor}, #{item.voucherNumber}, #{item.itemStatus}, #{item.shippedAt},
#{item.shippedBy}, #{item.remark}, #{item.isUsed}, #{item.sortNo}, #{item.createTime},
#{item.createUserCode}, #{item.updateTime}, #{item.updateUserCode},#{item.inventoryId}
)
</foreach>
</insert>
</mapper>
\ No newline at end of file
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论