Commit db0c0a3a by wangchunyang
parents 6eab21f8 7555bfca
......@@ -151,7 +151,7 @@ export default {
},
/** 下载模板操作 */
importTemplate() {
this.download(this.templateUrl, {}, `${this.templateName}_${new Date().getTime()}.xlsx`)
this.download(this.templateUrl, {orderType: this.upload.orderType}, `${this.templateName}_${new Date().getTime()}.xlsx`)
},
// 文件上传中处理
handleFileUploadProgress(event, file, fileList) {
......
......@@ -94,6 +94,7 @@ export default {
grid: {
left: '3%',
right: '4%',
top: '3%',
bottom: '3%',
containLabel: true
},
......@@ -130,12 +131,12 @@ export default {
series: [{
name: '标准化',
type: 'bar',
barWidth: 16, // 柱子宽度
barWidth: '15', // 柱子宽度
label: {
show: true,
position: 'right', // 位置
color: 'rgba(102, 102, 102, 1)',
fontSize: 14,
fontSize: 12,
fontWeight: 'bold', // 加粗
distance: 5 // 距离
}, // 柱子上方的数值
......
......@@ -63,7 +63,7 @@
<div class="card-container-flex chart-container shadow flex1">
<div class="tip-title">
<div class="title-text">本月入库排名前10物料</div>
<div class="title-text">本月入库排名前5物料</div>
<el-radio-group v-model="rukuRankType" size="mini" @change="rukuRankTypeChange">
<el-radio-button label="count">按数量</el-radio-button>
<el-radio-button label="money">按金额</el-radio-button>
......@@ -75,7 +75,7 @@
</div>
<div class="card-container-flex chart-container shadow flex1 two">
<div class="tip-title">
<div class="title-text">本月出库排名前10物料</div>
<div class="title-text">本月出库排名前5物料</div>
<el-radio-group v-model="cukuRankType" size="mini" @change="cukuRankTypeChange">
<el-radio-button label="count">按数量</el-radio-button>
<el-radio-button label="money">按金额</el-radio-button>
......@@ -186,7 +186,7 @@ export default {
this.rukuList = data.map(item =>({
value:item.totalQuantity,
name:item.materialName
}))
})).splice(0,5).reverse()
})
}
if(this.rukuRankType === 'money'){
......@@ -196,7 +196,7 @@ export default {
this.rukuList = data.map(item =>({
value:item.totalMoney,
name:item.materialName
}))
})).splice(0,5).reverse()
})
}
},
......@@ -234,14 +234,14 @@ export default {
outboundOrdersTopTenByQuantity().then(res=>{
let data = res.rows
if(!data) return
this.cukuList = data
this.cukuList = data.splice(0,5).reverse()
})
}
if(this.cukuRankType === 'money'){
outboundOrdersTopTenByAmount().then(res=>{
let data = res.rows
if(!data) return
this.cukuList = data
this.cukuList = data.splice(0,5).reverse()
})
}
}
......
......@@ -443,7 +443,7 @@
title="导入"
import-url="/inventory/inbound/import"
template-url="/inventory/inbound/importTemplate"
template-name="入库单导入模板"
:template-name="getTemplateName()"
@success="getList"
:show-trdc-checkbox="true"
@orderTypeChange="handleOrderTypeChange"
......@@ -703,14 +703,14 @@ export default {
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
let id = row.id;
if(id === undefined){
id = this.ids[0]
}
getInbound(id).then(response => {
if(response.data.orderStatus === 2){
this.$modal.msgWarning("该入库单已提交,不能修改");
this.$modal.msgWarning("该入库单已提交,不能修改");
return;
}
this.form = response.data || {};
......@@ -1051,6 +1051,15 @@ export default {
handleImport() {
this.$refs.import.show()
},
getTemplateName(){
if(this.form.orderTypeId === '1'){
return "入库单导入模板"
}else if(this.form.orderTypeId === '2'){
return "成品入库单导入模板"
}else if(this.form.orderTypeId === '3'){
return "退料TRDC入库单导入模板"
}
},
/** 处理明细项选择变化 */
handleItemsSelectionChange(selection) {
this.selectedItems = selection
......
......@@ -16,16 +16,15 @@
<!-- 页面容器 -->
<div class="page-container">
<!-- 物料查询表单 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="主订单号" prop="mainOrderId">
<!-- <el-form-item label="主订单号" prop="orderId">
<el-input
v-model="queryParams.mainOrderId"
v-model="queryParams.orderId"
placeholder="请输入主订单号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
</el-form-item> -->
<el-form-item label="时间段" prop="dateRange">
<el-date-picker
v-model="queryParams.dateRange"
......@@ -253,7 +252,7 @@
style="width: 100%"
>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="主订单号" align="center" prop="mainOrderId" width="150" />
<el-table-column label="主订单号" align="center" prop="orderId" width="150" />
<el-table-column label="物料名称" align="center" prop="materialName" min-width="150" show-overflow-tooltip />
<el-table-column label="批次" align="center" prop="batchCode" width="120" />
<el-table-column label="仓库" align="center" prop="warehouseName" width="120" />
......@@ -314,7 +313,7 @@ export default {
queryParams: {
pageNum: 1,
pageSize: 10,
mainOrderId: null,
orderId: null,
startDate: null,
endDate: null,
ownerId: null,
......@@ -339,7 +338,7 @@ export default {
detailQueryParams: {
pageNum: 1,
pageSize: 10,
mainOrderId: null,
orderId: null,
materialId: null,
warehouseId: null,
warehousesCode: null,
......@@ -466,7 +465,7 @@ export default {
this.detailQueryParams = {
pageNum: 1,
pageSize: 10,
mainOrderId: row.mainOrderId,
orderId: row.orderId,
materialId: row.materialId,
warehouseId: null,
warehousesCode: null,
......
......@@ -144,6 +144,7 @@
<el-table-column label="SAP物料号" align="center" prop="sapNo" width="150"/>
<!-- <el-table-column label="物料编码" align="center" prop="materialCode" width="120"/> -->
<el-table-column label="物料名称" align="center" prop="materialName" width="150"/>
<el-table-column label="物料英文名称" align="center" prop="materialEname" width="150"/>
<el-table-column label="TS Code" align="center" prop="tsCode" width="150"/>
<el-table-column label="物料分类" align="center" prop="categoryCode" >
<template slot-scope="scope">
......@@ -298,6 +299,9 @@
<el-form-item label="物料名称" prop="materialName">
<el-input v-model="form.materialName" placeholder="请输入物料名称" />
</el-form-item>
<el-form-item label="物料英文名称" prop="materialEname">
<el-input v-model="form.materialEname" placeholder="请输入物料英文名称" />
</el-form-item>
<el-form-item label="物料分类" prop="categoryCode">
<el-select v-model="form.categoryCode" placeholder="请选择物料分类" clearable>
<el-option
......@@ -444,6 +448,7 @@ export default {
id: null,
materialCode: null,
materialName: null,
materialEname: null,
sapNo: null,
tsCode: null,
categoryCode: null,
......@@ -654,7 +659,7 @@ export default {
.map(item => ({
...item,
// 兜底:如果映射表中没有,显示原始code并标注
displayCategory: this.categoryMap[item.categoryCode] || `${item.categoryCode}(未匹配分类)`
displayCategory: this.categoryMap[item.categoryCode] || `未匹配分类`
}));
this.total = total;
} catch (error) {
......
......@@ -251,7 +251,7 @@ export default {
.filter(item => item.isUsed !== 0 && item.isUsed !== '0')
.map(item => ({
...item,
displayCategory: this.categoryMap[item.categoryCode] || `${item.categoryCode}(未匹配分类)`
displayCategory: this.categoryMap[item.categoryCode] || `未匹配分类`
}));
this.total = response.total;
}).finally(() => {
......
......@@ -121,6 +121,41 @@
</div>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="计划数量" prop="plannedQuantity" required>
<el-input
v-model.number="currentSelectedRow.plannedQuantity"
type="number"
min="1"
:max="(currentSelectedRow.quantity || 0) - (currentSelectedRow.lockedQuantity || 0)"
@input="handleRowPlannedQtyInput(currentSelectedRow); syncDetails(false)"
placeholder="输入计划数量"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="单价" prop="unitPrice" required>
<el-input
v-model.number="currentSelectedRow.unitPrice"
placeholder="请输入单价"
type="number"
min="0"
step="0.01"
@input="syncDetails(false)"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="凭证号" prop="voucherNumber">
<el-input
v-model="currentSelectedRow.voucherNumber"
placeholder="请输入凭证号"
@input="syncDetails(false)"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20" style="margin-top: 10px;">
<el-col :span="8">
<el-form-item label="约数" prop="divisor">
<el-input
v-model.number="currentSelectedRow.divisor"
......@@ -148,32 +183,6 @@
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="计划数量" prop="plannedQuantity">
<el-input
v-model.number="currentSelectedRow.plannedQuantity"
type="number"
min="1"
:max="(currentSelectedRow.quantity || 0) - (currentSelectedRow.lockedQuantity || 0)"
@input="handleRowPlannedQtyInput(currentSelectedRow); syncDetails(false)"
placeholder="输入计划数量"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20" style="margin-top: 10px;">
<el-col :span="8">
<el-form-item label="单价" prop="unitPrice">
<el-input
v-model.number="currentSelectedRow.unitPrice"
placeholder="请输入单价"
type="number"
min="0"
step="0.01"
@input="syncDetails(false)"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="发货方" prop="shippedBy">
<el-input
v-model="currentSelectedRow.shippedBy"
......@@ -182,16 +191,8 @@
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="凭证号" prop="voucherNumber">
<el-input
v-model="currentSelectedRow.voucherNumber"
placeholder="请输入凭证号"
@input="syncDetails(false)"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20" style="margin-top: 10px;">
<el-col :span="16">
<el-form-item label="备注" prop="remark">
......@@ -226,6 +227,7 @@
<el-table-column prop="batchId" label="批次ID" width="150" />
<el-table-column prop="warehousesName" label="仓库名称" width="120" />
<el-table-column prop="locationName" label="库位名称" width="120" />
<el-table-column prop="plannedQuantity" label="计划数量" width="100" />
<el-table-column prop="actualQuantity" label="实际数量" width="100" fixed="right"/>
<el-table-column prop="unitPrice" label="单价" width="100" />
......@@ -637,31 +639,18 @@ syncDetails(strict = true) {
if (row.plannedQuantity > availableQty) {
rowErrors.push(`计划数量不能超过可用库存(${availableQty})`);
}
}else{
rowErrors.push(`请填写计划数量`);
}
// 3. 除数(有值才校验,无值不提示)
if (row.divisor !== null && row.divisor !== undefined) {
if (row.unitPrice !== null && row.unitPrice !== undefined) {
// 若有值但为0/负数,可补充校验(按需)
if (row.divisor <= 0) {
rowErrors.push('换算率(除数)必须大于0');
if (row.unitPrice <= 0) {
rowErrors.push('请填写单价');
}
}
// 4. 标签颜色(有值才校验,空字符串算不合规)
if (row.labelColor !== undefined && row.labelColor === '') {
rowErrors.push('请填写标签颜色');
}
// 5. 凭证号(有值才校验,空字符串算不合规)
if (row.voucherNumber !== undefined && row.voucherNumber === '') {
rowErrors.push('请填写凭证号');
}
// 6. 发货人(有值才校验,空字符串算不合规)
if (row.shippedBy !== undefined && row.shippedBy === '') {
rowErrors.push('请填写发货人');
}
// 收集当前行的错误(仅有值但不合规的字段)
if (rowErrors.length > 0) {
errorMessages.push(`${rowName}${rowErrors.join('、')}`);
......
......@@ -125,6 +125,16 @@
</template>
</el-input>
</el-form-item>
<el-form-item label="出库日期" prop="inboundDate">
<el-date-picker
clearable
v-model="queryParams.inboundDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择出库日期"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="订单状态" prop="orderStatus">
<el-select
v-model="queryParams.orderStatus"
......@@ -140,16 +150,6 @@
/>
</el-select>
</el-form-item>
<el-form-item label="出库日期" prop="inboundDate">
<el-date-picker
clearable
v-model="queryParams.inboundDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择出库日期"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="目的地" prop="destination">
<el-input
v-model="queryParams.destination"
......@@ -255,6 +255,7 @@
v-if="scope.row.orderStatus===1"
@click="handleShip(scope.row)"
v-hasPermi="['inventory:orders:edit']"
:loading="shipLoading[scope.row.id]"
>出货</el-button>
<el-button
size="mini"
......@@ -310,28 +311,7 @@
</el-form-item>
</el-col>
<!-- 新增:订单类型表单项 -->
<el-col :span="12">
<el-form-item label="订单类型" prop="orderType">
<el-select v-model="form.orderType" placeholder="请选择订单类型" :disabled="isViewDetail || formDisabled.orderType" style="width: 100%">
<el-option
v-for="item in dict.type.order_type"
:key="item.value"
:label="item.label"
:value="item.value"
>
<span style="margin-left: 8px;">{{ item.label }}</span>
</el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="批次ID" prop="batchCode">
<el-input v-model="form.batchCode" placeholder="请输入批次ID" :disabled="isViewDetail || formDisabled.batchCode" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-col :span="12">
<el-form-item label="仓库" prop="warehouseId">
<el-input
v-model="form.warehouseName"
......@@ -356,6 +336,26 @@
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="批次ID" prop="batchCode">
<el-input v-model="form.batchCode" placeholder="请输入批次ID" :disabled="isViewDetail || formDisabled.batchCode" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="出库日期" prop="inboundDate">
<el-date-picker
clearable
v-model="form.inboundDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择出库日期"
:disabled="isViewDetail"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="货主" prop="ownerId">
<el-input
v-model="form.ownerName"
......@@ -377,19 +377,22 @@
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="出库日期" prop="inboundDate">
<el-date-picker
clearable
v-model="form.inboundDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择出库日期"
:disabled="isViewDetail"
style="width: 100%"
/>
<el-col :span="12">
<el-form-item label="订单类型" prop="orderType">
<el-select v-model="form.orderType" placeholder="请选择订单类型" :disabled="isViewDetail || formDisabled.orderType" style="width: 100%">
<el-option
v-for="item in dict.type.order_type"
:key="item.value"
:label="item.label"
:value="item.value"
>
<span style="margin-left: 8px;">{{ item.label }}</span>
</el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
......@@ -514,20 +517,19 @@
@selected="handleLocationSelected"
/>
<import-excel
ref="import"
title="导入"
import-url="/inventory/orders/import"
template-url="/inventory/orders/importTemplate"
template-name="出库单导入模板"
@success="getList"
:show-trdc-checkbox="true"
@orderTypeChange="handleOrderTypeChange"
:orderTypeRequired="true"
/>
<import-excel
ref="import"
title="导入"
import-url="/inventory/orders/import"
template-url="/inventory/orders/importTemplate"
template-name="出库单导入模板"
@success="getList"
:show-trdc-checkbox="true"
@orderTypeChange="handleOrderTypeChange"
:orderTypeRequired="true"
/>
</div>
</template>
<script>
import { listOrders, getOrders, delOrders, addOrders, updateOrders, ship } from "@/api/inventory/orders"
import { getMaterialsdicts } from "@/api/inventory/materials"
......@@ -557,6 +559,8 @@ export default {
tableKey: 1,
// 遮罩层
loading: true,
// 出货loading(按订单ID存储,支持多订单同时操作)
shipLoading: {}, // 新增:出货loading状态
// 选中数组
ids: [],
// 非单个禁用
......@@ -657,9 +661,9 @@ export default {
rules: {
orderId: [{ required: true, message: '请输入出库单号', trigger: 'blur' }],
warehouseId: [{ required: true, message: '请选择仓库', trigger: 'blur' }],
ownerId: [{ required: true, message: '请选择货主', trigger: 'blur' }],
inboundDate: [{ required: true, message: '请选择出库日期', trigger: 'change' }],
orderType: [{ required: true, message: '请选择订单类型', trigger: 'change' }], // 新增:订单类型校验
orderTypeId: [{ required: true, message: '请选择出库类型', trigger: 'change' }],
batchCode: [{ required: true, message: '请选输入批次号', trigger: 'change' }],
},
// 货主/仓库选择相关
ownerSelectorVisible: false,
......@@ -678,13 +682,29 @@ export default {
this.$nextTick(() => {
this.getList()
})
this.handleShip = this.debounce(this.handleShip, 500)
},
methods: {
handleOrderTypeChange(selection) {
debounce(fn, delay = 300) {
let timer = null;
let pendingPromise = null;
return async function(...args) {
if (timer) clearTimeout(timer);
pendingPromise = new Promise((resolve) => {
timer = setTimeout(async () => {
const result = await fn.apply(this, args);
resolve(result);
timer = null;
}, delay);
});
return pendingPromise;
};
},
handleOrderTypeChange(selection) {
this.form.orderTypeId = selection
},
/** 打开导入弹窗 */
/** 打开导入弹窗 */
handleImport() {
this.$refs.import.show()
},
......@@ -722,24 +742,24 @@ export default {
this.ownerSelectorVisible = true
},
// 货主选择回调(核心缺失方法)
handleOwnerSelected(owner) {
if (!owner) return
if (this.ownerSelectTarget === 'query') {
this.queryParams.ownerId = owner.ownerId || owner.id
this.queryOwnerName = owner.ownerName || owner.name
this.handleQuery()
} else {
this.form.ownerId = owner.ownerId || owner.id
this.form.ownerName = owner.ownerName || owner.name
// 更新明细中的默认货主(可选)
if (this.currentDetailItem) {
this.currentDetailItem.ownerId = owner.ownerId || owner.id
this.currentDetailItem.ownerName = owner.ownerName || owner.name
}
}
this.ownerSelectorVisible = false
},
handleOwnerSelected(owner) {
if (!owner) return
if (this.ownerSelectTarget === 'query') {
this.queryParams.ownerId = owner.ownerId || owner.id
this.queryOwnerName = owner.ownerName || owner.name
this.handleQuery()
} else {
this.form.ownerId = owner.ownerId || owner.id
this.form.ownerName = owner.ownerName || owner.name
// 更新明细中的默认货主(可选)
if (this.currentDetailItem) {
this.currentDetailItem.ownerId = owner.ownerId || owner.id
this.currentDetailItem.ownerName = owner.ownerName || owner.name
}
}
this.ownerSelectorVisible = false
},
// 打开仓库选择器
openWarehouseSelector(target = 'form') {
this.warehouseSelectTarget = target
......@@ -796,26 +816,35 @@ handleOwnerSelected(owner) {
calcGroupAmount(items) {
return items.reduce((sum, item) => sum + (Number(item.amount) || 0), 0).toFixed(2)
},
// 核心出货方法
// 核心出货方法(添加loading+弹窗确认)
async handleShip(row) {
try {
// 调用ship接口提交数据到后端
console.log("handleShip",row)
await ship({
...row,
orderId: row.orderId, // 用户填写的出货单号
outboundOrderId: row.id // 主表ID作为outboundOrderId
})
// 提示成功并刷新列表
this.$modal.msgSuccess("出货操作成功,数据已提交到后端")
this.getList()
} catch (error) {
// 错误处理
this.$modal.msgError(error.msg || "出货操作失败")
}
},
// 修复:获取要传递给子组件的初始化明细数据(按物料分组)
// 新增:出货前弹窗确认
this.$modal.confirm(`是否确认对出库单【${row.orderId}】执行出货操作?`).then(async () => {
try {
// 设置当前订单出货loading
this.$set(this.shipLoading, row.id, true)
// 原有业务逻辑
console.log("handleShip", row)
await ship({
...row,
orderId: row.orderId, // 用户填写的出货单号
outboundOrderId: row.id // 主表ID作为outboundOrderId
})
// 提示成功并刷新列表
this.$modal.msgSuccess("出货操作成功,数据已提交到后端")
this.getList()
} catch (error) {
// 错误处理
this.$modal.msgError(error.msg || "出货操作失败")
} finally {
// 关闭loading
this.$delete(this.shipLoading, row.id)
}
}).catch(() => {
// 取消确认时不执行任何操作
})
}, // 修复:获取要传递给子组件的初始化明细数据(按物料分组)
getInitDetails() {
// 编辑场景:传递当前物料分组的所有明细(确保子组件反显)
if (this.isEditDetail && this.currentMaterialId) {
......@@ -941,116 +970,116 @@ handleOwnerSelected(owner) {
this.detailDialogOpen = true
})
},
handleDetailSubmit(details) {
// 确保是数组格式
const detailList = Array.isArray(details) ? details : [details];
if (detailList.length === 0) {
this.$message.warning("请填写明细数据");
return;
}
const materialId = this.currentMaterialId || detailList[0]?.materialId;
if (!materialId) {
this.$message.warning("物料ID不能为空");
return;
}
// 初始化物料分组
if (!this.outboundOrderItemsGroup[materialId]) {
this.outboundOrderItemsGroup[materialId] = {
materialId: materialId,
materialName: detailList[0]?.materialName || '', // 【修改9】初始化分组的物料名称
items: []
};
}
const group = this.outboundOrderItemsGroup[materialId];
handleDetailSubmit(details) {
// 确保是数组格式
const detailList = Array.isArray(details) ? details : [details];
if (detailList.length === 0) {
this.$message.warning("请填写明细数据");
return;
}
if (this.isEditDetail) {
// 修复:编辑模式 - 全量替换分组数据(而非追加)
const newDetails = detailList.map((item, idx) => {
const amount = (Number(item.actualQuantity) || 0) * (Number(item.unitPrice) || 0);
return {
...item,
index: idx + 1,
orderId: null, // 明细的orderId设为空
outboundOrderId: this.form.id, // 使用主表ID作为outboundOrderId
materialId: materialId,
materialName: item.materialName || group.materialName || '', // 【修改10】保留物料名称
inventoryId: item.inventoryId || '',
itemStatus: Number(item.itemStatus) || 1,
labelColor: Number(item.labelColor) || 0,
divisor: Number(item.divisor) || 1,
shippedAt: this.form.inboundDate,
plannedQuantity: Number(item.plannedQuantity) || 0,
actualQuantity: Number(item.actualQuantity) || 0,
unitPrice: Number(item.unitPrice) || 0.00,
amount: amount,
isUsed: 1,
sortNo: 0,
remark: item.remark || '',
warehouseName: item.warehouseName || this.form.warehouseName || '',
locationName: item.locationName || '',
batchCode: item.batchId || '' // 新增:将子组件的batchId赋值给batchCode
};
});
const materialId = this.currentMaterialId || detailList[0]?.materialId;
if (!materialId) {
this.$message.warning("物料ID不能为空");
return;
}
// 全量替换分组数据
group.items = newDetails;
// 更新分组的物料名称(取第一条的物料名称)
group.materialName = newDetails[0]?.materialName || group.materialName;
this.$message.success(`成功编辑${materialId}物料组,共${newDetails.length}条明细`);
} else {
// 新增模式:按库存ID去重后追加
const existingInventoryIds = new Set(group.items.map(item => item.inventoryId));
const newDetails = detailList
.filter(item => item.inventoryId && !existingInventoryIds.has(item.inventoryId))
.map((item, idx) => {
const newIndex = group.items.length + idx + 1;
const amount = (Number(item.actualQuantity) || 0) * (Number(item.unitPrice) || 0);
return {
...item,
index: newIndex,
orderId: null, // 明细的orderId设为空
outboundOrderId: this.form.id || null, // 使用主表ID作为outboundOrderId
// 初始化物料分组
if (!this.outboundOrderItemsGroup[materialId]) {
this.outboundOrderItemsGroup[materialId] = {
materialId: materialId,
materialName: item.materialName || '', // 【修改11】保留物料名称
inventoryId: item.inventoryId || '',
itemStatus: Number(item.itemStatus) || 1,
labelColor: Number(item.labelColor) || 0,
divisor: Number(item.divisor) || 1,
plannedQuantity: Number(item.plannedQuantity) || 0,
actualQuantity: Number(item.actualQuantity) || 0,
unitPrice: Number(item.unitPrice) || 0.00,
amount: amount,
isUsed: 1,
sortNo: 0,
remark: item.remark || '',
warehouseName: item.warehouseName || this.form.warehouseName || '',
locationName: item.locationName || '',
batchCode: item.batchId || '' // 新增:将子组件的batchId赋值给batchCode
materialName: detailList[0]?.materialName || '', // 【修改9】初始化分组的物料名称
items: []
};
});
}
const group = this.outboundOrderItemsGroup[materialId];
if (newDetails.length === 0) {
this.$message.warning("该库存明细已存在,无法重复添加");
this.detailDialogOpen = false;
return;
}
if (this.isEditDetail) {
// 修复:编辑模式 - 全量替换分组数据(而非追加)
const newDetails = detailList.map((item, idx) => {
const amount = (Number(item.actualQuantity) || 0) * (Number(item.unitPrice) || 0);
return {
...item,
index: idx + 1,
orderId: null, // 明细的orderId设为空
outboundOrderId: this.form.id, // 使用主表ID作为outboundOrderId
materialId: materialId,
materialName: item.materialName || group.materialName || '', // 【修改10】保留物料名称
inventoryId: item.inventoryId || '',
itemStatus: Number(item.itemStatus) || 1,
labelColor: Number(item.labelColor) || 0,
divisor: Number(item.divisor) || 1,
shippedAt: this.form.inboundDate,
plannedQuantity: Number(item.plannedQuantity) || 0,
actualQuantity: Number(item.actualQuantity) || 0,
unitPrice: Number(item.unitPrice) || 0.00,
amount: amount,
isUsed: 1,
sortNo: 0,
remark: item.remark || '',
warehouseName: item.warehouseName || this.form.warehouseName || '',
locationName: item.locationName || '',
batchCode: item.batchId || '' // 新增:将子组件的batchId赋值给batchCode
};
});
group.items = [...group.items, ...newDetails];
// 更新分组的物料名称(如果为空,取第一条的物料名称)
if (!group.materialName) {
group.materialName = newDetails[0]?.materialName || '';
}
this.$message.success(`成功新增${newDetails.length}条明细`);
}
// 全量替换分组数据
group.items = newDetails;
// 更新分组的物料名称(取第一条的物料名称)
group.materialName = newDetails[0]?.materialName || group.materialName;
this.$message.success(`成功编辑${materialId}物料组,共${newDetails.length}条明细`);
} else {
// 新增模式:按库存ID去重后追加
const existingInventoryIds = new Set(group.items.map(item => item.inventoryId));
const newDetails = detailList
.filter(item => item.inventoryId && !existingInventoryIds.has(item.inventoryId))
.map((item, idx) => {
const newIndex = group.items.length + idx + 1;
const amount = (Number(item.actualQuantity) || 0) * (Number(item.unitPrice) || 0);
return {
...item,
index: newIndex,
orderId: null, // 明细的orderId设为空
outboundOrderId: this.form.id || null, // 使用主表ID作为outboundOrderId
materialId: materialId,
materialName: item.materialName || '', // 【修改11】保留物料名称
inventoryId: item.inventoryId || '',
itemStatus: Number(item.itemStatus) || 1,
labelColor: Number(item.labelColor) || 0,
divisor: Number(item.divisor) || 1,
plannedQuantity: Number(item.plannedQuantity) || 0,
actualQuantity: Number(item.actualQuantity) || 0,
unitPrice: Number(item.unitPrice) || 0.00,
amount: amount,
isUsed: 1,
sortNo: 0,
remark: item.remark || '',
warehouseName: item.warehouseName || this.form.warehouseName || '',
locationName: item.locationName || '',
batchCode: item.batchId || '' // 新增:将子组件的batchId赋值给batchCode
};
});
// 重新计算总数和总金额
this.calcTotalQuantity();
this.detailDialogOpen = false;
// 强制更新视图
this.$forceUpdate();
},
if (newDetails.length === 0) {
this.$message.warning("该库存明细已存在,无法重复添加");
this.detailDialogOpen = false;
return;
}
group.items = [...group.items, ...newDetails];
// 更新分组的物料名称(如果为空,取第一条的物料名称)
if (!group.materialName) {
group.materialName = newDetails[0]?.materialName || '';
}
this.$message.success(`成功新增${newDetails.length}条明细`);
}
// 重新计算总数和总金额
this.calcTotalQuantity();
this.detailDialogOpen = false;
// 强制更新视图
this.$forceUpdate();
},
// 计算主表总数量和总金额
calcTotalQuantity() {
let totalPlanned = 0
......@@ -1213,28 +1242,28 @@ handleDetailSubmit(details) {
resetQuery() {
// 检查ref存在性
if (this.$refs.queryForm) {
this.queryParams= {
pageNum: 1,
pageSize: 10,
orderId: null,
systemNo: null,
orderTypeId: null,
orderType: null, // 新增:订单类型重置
batchCode: null,
warehouseId: null,
ownerId: null,
orderStatus: null,
inboundDate: null,
destination: null,
totalPlannedQuantity: null,
totalActualQuantity: null,
totalPackages: null,
isUsed: null,
sortNo: null,
createUserCode: null,
updateUserCode: null
this.queryParams = {
pageNum: 1,
pageSize: 10,
orderId: null,
systemNo: null,
orderTypeId: null,
orderType: null, // 新增:订单类型重置
batchCode: null,
warehouseId: null,
ownerId: null,
orderStatus: null,
inboundDate: null,
destination: null,
totalPlannedQuantity: null,
totalActualQuantity: null,
totalPackages: null,
isUsed: null,
sortNo: null,
createUserCode: null,
updateUserCode: null
}
}
}
this.queryOwnerName = ''
this.queryWarehouseName = ''
......@@ -1436,82 +1465,88 @@ handleDetailSubmit(details) {
this.$refs["form"].validate(async (valid) => {
if (valid) {
// 检查是否有明细数据
const hasDetails = Object.values(this.outboundOrderItemsGroup).some(group => group.items.length > 0)
if (!hasDetails) {
this.$message.warning('请至少添加一条明细数据')
return
}
// 扁平化分组数据用于提交
const outboundOrderItemsList = []
Object.values(this.outboundOrderItemsGroup).forEach(group => {
outboundOrderItemsList.push(...group.items)
})
// 修复:去重逻辑优化(保留最新数据)
const uniqueDetailsMap = new Map()
outboundOrderItemsList.forEach(item => {
if (item.inventoryId) {
uniqueDetailsMap.set(item.inventoryId, item)
// 新增:提交前弹窗确认
const confirmText = this.form.id ? '修改' : '新增'
this.$modal.confirm(`是否确认${confirmText}出库单【${this.form.orderId || '未命名'}】?`).then(async () => {
// 检查是否有明细数据
const hasDetails = Object.values(this.outboundOrderItemsGroup).some(group => group.items.length > 0)
if (!hasDetails) {
this.$message.warning('请至少添加一条明细数据')
return
}
})
const uniqueDetails = Array.from(uniqueDetailsMap.values())
// 修改点7:构造提交数据,适配新的字段映射规则
const submitData = {
...this.form,
outboundOrderId: this.form.id,
orderId: this.form.orderId,
orderType: this.form.orderType, // 新增:提交订单类型
outboundOrderItemsList: uniqueDetails.map(item => {
const { index, materialUuids, warehouseName, locationName, ...rest } = item
return {
...rest,
orderId: null,
outboundOrderId: this.form.id || null,
materialName: item.materialName || '', // 【修改16】提交物料名称
inventoryId: rest.inventoryId || '',
materialId: rest.materialId || '',
batchCode: rest.batchCode || '',
warehouseId: rest.warehouseId || this.form.warehouseId || '',
locationId: rest.locationId || '',
plannedQuantity: Number(rest.plannedQuantity) || 0,
actualQuantity: Number(rest.actualQuantity) || 0,
divisor: Number(rest.divisor) || 1,
labelColor: Number(rest.labelColor) || 0,
unitPrice: Number(rest.unitPrice) || 0.00,
amount: Number(rest.amount) || 0.00,
voucherNumber: rest.voucherNumber || '',
itemStatus: Number(rest.itemStatus) || 1,
shippedAt: rest.shippedAt || this.form.inboundDate || '',
shippedBy: rest.shippedBy || '',
remark: rest.remark || '',
isUsed: 1,
sortNo: Number(rest.sortNo) || 0
};
// 扁平化分组数据用于提交
const outboundOrderItemsList = []
Object.values(this.outboundOrderItemsGroup).forEach(group => {
outboundOrderItemsList.push(...group.items)
})
}
// 调试:打印最终提交的数据
console.log('最终提交的数据:', submitData)
// 修复:去重逻辑优化(保留最新数据)
const uniqueDetailsMap = new Map()
outboundOrderItemsList.forEach(item => {
if (item.inventoryId) {
uniqueDetailsMap.set(item.inventoryId, item)
}
})
const uniqueDetails = Array.from(uniqueDetailsMap.values())
try {
if (this.form.id != null) {
await updateOrders(submitData)
this.$modal.msgSuccess("修改成功")
} else {
await addOrders(submitData)
this.$modal.msgSuccess("新增成功")
// 修改点7:构造提交数据,适配新的字段映射规则
const submitData = {
...this.form,
outboundOrderId: this.form.id,
orderId: this.form.orderId,
orderType: this.form.orderType, // 新增:提交订单类型
outboundOrderItemsList: uniqueDetails.map(item => {
const { index, materialUuids, warehouseName, locationName, ...rest } = item
return {
...rest,
orderId: null,
outboundOrderId: this.form.id || null,
materialName: item.materialName || '', // 【修改16】提交物料名称
inventoryId: rest.inventoryId || '',
materialId: rest.materialId || '',
batchCode: rest.batchCode || '',
warehouseId: rest.warehouseId || this.form.warehouseId || '',
locationId: rest.locationId || '',
plannedQuantity: Number(rest.plannedQuantity) || 0,
actualQuantity: Number(rest.actualQuantity) || 0,
divisor: Number(rest.divisor) || 1,
labelColor: Number(rest.labelColor) || 0,
unitPrice: Number(rest.unitPrice) || 0.00,
amount: Number(rest.amount) || 0.00,
voucherNumber: rest.voucherNumber || '',
itemStatus: Number(rest.itemStatus) || 1,
shippedAt: rest.shippedAt || this.form.inboundDate || '',
shippedBy: rest.shippedBy || '',
remark: rest.remark || '',
isUsed: 1,
sortNo: Number(rest.sortNo) || 0
};
})
}
this.open = false
this.getList()
} catch (error) {
if (error !== 'cancel') {
const errorMsg = error?.response?.data?.msg || '库存被修改请重新确认'
this.$modal.msgError(errorMsg)
// 调试:打印最终提交的数据
console.log('最终提交的数据:', submitData)
try {
if (this.form.id != null) {
await updateOrders(submitData)
this.$modal.msgSuccess("修改成功")
} else {
await addOrders(submitData)
this.$modal.msgSuccess("新增成功")
}
this.open = false
this.getList()
} catch (error) {
if (error !== 'cancel') {
const errorMsg = error?.response?.data?.msg || '库存被修改请重新确认'
this.$modal.msgError(errorMsg)
}
}
}
}).catch(() => {
// 取消确认时不执行任何操作
})
}
})
},
......@@ -1537,70 +1572,4 @@ handleDetailSubmit(details) {
}
}
}
</script>
<style scoped>
/* 页面容器样式 */
.page-container {
background: #fff;
padding: 16px;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.04);
margin-top: 16px;
}
/* 表格容器样式 */
.table-container {
margin-top: 16px;
height: calc(100vh - 280px);
}
/* 弹窗内表单滚动优化 */
.el-dialog__body {
max-height: 70vh;
overflow-y: auto;
padding-right: 10px;
}
/* 自定义滚动条 */
.el-dialog__body::-webkit-scrollbar {
width: 6px;
}
.el-dialog__body::-webkit-scrollbar-thumb {
background-color: #ddd;
border-radius: 3px;
}
/* 明细表格样式优化 */
.el-table {
--el-table-row-hover-bg-color: #f8f9fa;
}
/* 空数据提示样式 */
.empty-tip {
color: #999;
font-size: 14px;
}
/* 物料分组样式 */
.material-group {
border: 1px solid #e6e6e6;
border-radius: 6px;
padding: 10px;
}
.group-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.mb10 {
margin-bottom: 10px;
}
.mb8 {
margin-bottom: 8px;
}
</style>
\ No newline at end of file
</script>
\ No newline at end of file
......@@ -138,7 +138,15 @@
<el-table-column label="物料名称" align="center" prop="materialName" width="150" />
<el-table-column label="SAP物料号" align="center" prop="sapNo" width="120" />
<el-table-column label="TS Code" align="center" prop="tsCode" width="120" />
<el-table-column label="危险类别" align="center" prop="hazard" width="120" />
<el-table-column label="危险类别" align="center" prop="hazardId" width="120" >
<template slot-scope="scope">
<el-tag
:type="getDictListClass('danger_type',scope.row.hazardId)"
size="small">
{{ getDictLabel('danger_type',scope.row.hazardId) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="规格型号" align="center" prop="specification" width="120" />
<el-table-column label="计量单位" align="center" prop="materialUnit" width="120" />
<el-table-column label="单位重量" align="center" prop="unitWeight" width="120" >
......@@ -232,7 +240,15 @@
<el-table-column label="物料名称" align="center" prop="materialName" width="150" />
<el-table-column label="SAP物料号" align="center" prop="sapNo" width="120" />
<el-table-column label="TS Code" align="center" prop="tsCode" width="120" />
<el-table-column label="危险类别" align="center" prop="hazard" width="120" />
<el-table-column label="危险类别" align="center" prop="hazardId" width="120" >
<template slot-scope="scope">
<el-tag
:type="getDictListClass('danger_type',scope.row.hazardId)"
size="small">
{{ getDictLabel('danger_type',scope.row.hazardId) }}
</el-tag>
</template>
</el-table-column>
<!-- <el-table-column label="规格型号" align="center" prop="specification" width="120" /> -->
<!-- <el-table-column label="入库单号" align="center" prop="orderId" width="150" /> -->
<el-table-column label="批次" align="center" prop="batchId" width="120" />
......@@ -265,6 +281,7 @@ import ImportExcel from "@/components/ImportExcel"
export default {
name: "InventoryDetail",
dicts: ['danger_type'],
components: {
RightToolbar,
PageTitle,
......@@ -333,6 +350,19 @@ export default {
this.getList()
},
methods: {
//从表格中的值当作键获取字典lebel
getDictLabel(dictType, value){
if(!value || !this.dict?.type?.[dictType]) return '-'
const dictItem = this.dict.type[dictType].find(item => item.value === value)
return dictItem?.label || '-'
},
//从表格中的值当作键获取字典listClass
getDictListClass(dictType, value){
if(!value || !this.dict?.type?.[dictType]) return '-'
const dictItem = this.dict.type[dictType].find(item => item.value === value)
return dictItem?.label || '-'
},
/** 查询库存明细列表 */
getList() {
this.loading = true
......
......@@ -36,14 +36,7 @@
@click="handleExport"
v-hasPermi="['inventory:warehouses:export']"
>导出</el-button>
<el-button
type="info"
plain
icon="el-icon-upload2"
size="medium"
@click="handleImport"
v-hasPermi="['inventory:warehouses:add']"
>导入</el-button>
</template>
</PageTitle>
......
package com.ruoyi.web.controller.inventory;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import com.ruoyi.common.core.domain.entity.Materials;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.uuid.UUID;
import javax.servlet.http.HttpServletResponse;
import com.ruoyi.inventory.domain.InboundOrderItems;
import com.ruoyi.inventory.domain.vo.inboundVO.InboundFinishTemplateVO;
import com.ruoyi.inventory.domain.vo.InboundMaterialTotalVO;
import com.ruoyi.inventory.domain.vo.InboundTemplateVO;
import com.ruoyi.inventory.domain.vo.inboundVO.InboundTRDCTemplateVO;
import com.ruoyi.inventory.domain.vo.inboundVO.InboundTemplateVO;
import com.ruoyi.inventory.service.impl.InboundOrdersServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
......@@ -37,7 +51,7 @@ public class InboundOrdersController extends BaseController
{
@Autowired
private IInboundOrdersService inboundOrdersService;
private static final Logger log = LoggerFactory.getLogger(InboundOrdersServiceImpl.class);
/**
* 查询入库单主列表
*/
......@@ -113,10 +127,22 @@ public class InboundOrdersController extends BaseController
@PreAuthorize("@ss.hasPermi('inventory:inbound:importTemplate')")
@Log(title = "入库导入模板", businessType = BusinessType.IMPORT)
@PostMapping("/importTemplate")
public void importTemplate(HttpServletResponse response)
public void importTemplate(HttpServletResponse response,@RequestParam("orderType") Integer orderType)
{
ExcelUtil<InboundTemplateVO> util = new ExcelUtil<InboundTemplateVO>(InboundTemplateVO.class);
util.importTemplateExcel(response, "入库单及入库物料明细信息");
switch(orderType){
case 1:
ExcelUtil<InboundTemplateVO> util = new ExcelUtil<InboundTemplateVO>(InboundTemplateVO.class);
util.importTemplateExcel(response, "入库单及入库物料明细信息");
break;
case 2:
ExcelUtil<InboundFinishTemplateVO> util1 = new ExcelUtil<InboundFinishTemplateVO>(InboundFinishTemplateVO.class);
util1.importTemplateExcel(response, "成品入库单及入库物料明细信息");
break;
case 3:
ExcelUtil<InboundTRDCTemplateVO> util2 = new ExcelUtil<InboundTRDCTemplateVO>(InboundTRDCTemplateVO.class);
util2.importTemplateExcel(response, "TRDC退库入库单及入库物料明细信息");
break;
}
}
/**
......@@ -126,62 +152,117 @@ public class InboundOrdersController extends BaseController
@Log(title = "入库信息导入", businessType = BusinessType.IMPORT)
@PostMapping("/import")
public AjaxResult importTemplate(@RequestParam("file") MultipartFile file,
// 接收 true/false
@RequestParam("updateSupport") Integer updateSupport,
@RequestParam(value = "orderType", required = false) Integer orderType) throws Exception
{
@RequestParam(value = "orderType", required = false) Integer orderType) throws Exception {
// 防护1:校验文件非空
if (file == null || file.isEmpty()) {
return error("导入文件不能为空!");
}
// 防护2:校验文件格式
String fileName = file.getOriginalFilename();
if (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls")) {
if (fileName == null || (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls"))) {
return error("仅支持Excel格式文件(.xlsx/.xls)!");
}
// 第二步:校验Excel列名是否匹配模板(核心!拦截非模板数据)
List<String> templateColumns = Arrays.asList(
"入库日期",
"SAP No",
"物料名称",
"TS Code",
"货主",
"批号",
"计划数量",
"单号",
"系统编号",
"件重",
"约数",
"实际件数",
"实发数量",
"仓库",
"库位",
"标签颜色",
"凭证号",
"单价",
"备注",
"订单类型",
"收货人",
"物料备注"
);
List<String> excelColumns = ExcelUtil.getExcelHeader(file.getInputStream()); // 自定义方法读取表头
if (CollectionUtils.isEmpty(excelColumns) || !excelColumns.containsAll(templateColumns)) {
return AjaxResult.error("导入文件不是标准模板!请下载官方模板后填写(缺失列:"
+ getMissingColumns(templateColumns, excelColumns) + ")");
// 防护3:校验orderType非空且合法
if (orderType == null || !Arrays.asList(1, 2, 3).contains(orderType)) {
return error("导入类型不能为空,仅支持1/2/3!");
}
ExcelUtil<InboundTemplateVO> util = new ExcelUtil<InboundTemplateVO>(InboundTemplateVO.class);
List<InboundTemplateVO> inboundOrders = util.importExcel(file.getInputStream());
// 防护3:拦截空列表,避免 Service 层处理空数据
if (CollectionUtils.isEmpty(inboundOrders)) {
return error("Excel中未解析到有效数据,请检查模板是否正确!");
// 2. 解析Excel表头(适配EasyExcel 2.x,无interrupt、无readRowNumber)
List<String> headerList = new ArrayList<>();
// 标记:是否已解析表头(避免重复处理)
AtomicBoolean headerParsed = new AtomicBoolean(false);
try {
EasyExcel.read(file.getInputStream(), new AnalysisEventListener<Object>() {
// 解析表头(2.x 中invokeHeadMap只会执行一次)
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
// 仅首次执行时解析表头
if (!headerParsed.get()) {
for (String header : headMap.values()) {
headerList.add(header.trim()); // 去空格存入
}
headerParsed.set(true); // 标记表头已解析完成
log.info("Excel表头解析完成,表头列表:{}", headerList);
}
}
// 解析数据行(表头解析完成后,直接返回不处理)
@Override
public void invoke(Object data, AnalysisContext context) {
// 逻辑终止:表头解析完后,数据行直接跳过
return;
}
// 解析完成(空实现即可)
@Override
public void doAfterAllAnalysed(AnalysisContext context) {}
})
.sheet() // 读取第一个sheet
.headRowNumber(1) // 指定表头在第1行(2.x 核心配置)
.doRead(); // 执行解析
} catch (IOException e) {
log.error("解析Excel表头失败", e);
return AjaxResult.error("解析Excel文件失败:" + e.getMessage());
}
// 防护4:表头解析为空的情况
if (CollectionUtils.isEmpty(headerList)) {
return error("未解析到Excel表头,请检查模板是否有表头行!");
}
String operName = getUsername();
String message = inboundOrdersService.importInboundOrders(inboundOrders, updateSupport, operName, orderType);
// 3. 通用导入逻辑(抽取重复代码,避免冗余)
String message = handleImport(getVOClassByOrderType(orderType), file, headerList, updateSupport, getUsername(), orderType);
return success(message);
}
// 辅助方法:获取缺失的列名
/**
* 根据orderType获取对应的VO类
*/
private Class<?> getVOClassByOrderType(Integer orderType) {
switch (orderType) {
case 1: return InboundTemplateVO.class;
case 2: return InboundFinishTemplateVO.class;
case 3: return InboundTRDCTemplateVO.class;
default: throw new ServiceException("不支持的导入类型,请联系管理员" + orderType);
}
}
/**
* 通用导入逻辑(泛型适配不同VO)
*/
private <T> String handleImport(Class<T> clazz, MultipartFile file, List<String> headerList,
Integer updateSupport, String operName, Integer orderType) throws Exception {
// 反射读取VO中@Excel注解的必填表头
List<String> requiredExcelHeads = new ArrayList<>();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Excel.class)) {
requiredExcelHeads.add(field.getAnnotation(Excel.class).name().trim());
}
}
// 校验表头是否包含所有必填项
if (!headerList.containsAll(requiredExcelHeads)) {
List<String> missingHeads = requiredExcelHeads.stream()
.filter(head -> !headerList.contains(head))
.collect(Collectors.toList());
return "导入数据字段与目标模板不一致,请检查!缺失字段:" + String.join("、", missingHeads);
}
// 解析Excel数据(若依ExcelUtil适配2.x,无需修改)
ExcelUtil<T> util = new ExcelUtil<>(clazz);
List<T> dataList = util.importExcel(file.getInputStream());
if (CollectionUtils.isEmpty(dataList)) {
return "Excel中未解析到有效数据,请检查模板是否正确!";
}
// 调用Service导入(需确保Service支持泛型列表,或强转Object)
return inboundOrdersService.importInboundOrders(dataList, updateSupport, operName, orderType);
}
// 辅助方法:获取缺失的列名(备用)
private String getMissingColumns(List<String> template, List<String> excel) {
return template.stream()
.filter(col -> !excel.contains(col))
......
package com.ruoyi.common.config;
/**
* 仓库配置类(存储默认仓库、常用仓库ID等)
*/
public class WarehouseConfig {
/**
* 默认出库仓库ID(核心默认值)
*/
public static final String DEFAULT_WAREHOUSE_ID = "572ba484-199c-45d9-9735-610928ed5c70";
}
\ No newline at end of file
......@@ -11,6 +11,7 @@ import org.apache.commons.lang3.builder.ToStringStyle;
* @author ruoyi
* @date 2025-11-28
*/
public class Materials extends BaseEntity
{
private static final long serialVersionUID = 1L;
......@@ -18,94 +19,115 @@ public class Materials extends BaseEntity
/** 编号 */
private String id;
/** 物料编码 检索条件 */
private String materialCode;
@Excel(name = "NO.")
private Long no;
/** 货主 数据库暂无字段 */
@Excel(name = "货主")
private String ownerId;
/** SAP物料号 检索条件 */
@Excel(name = "SAP物料号")
@Excel(name = "产品代码")
private String sapNo;
/** 是否激活 Y-是 N-否 */
@Excel(name = "激活")
private Long isActive;
/** 物料名称 检索条件 */
@Excel(name = "物料名称")
@Excel(name = "中文描述")
private String materialName;
/** 物料名称 检索条件 */
@Excel(name = "英文描述")
private String materialEname;
/** 总重量 */
@Excel(name = "毛重")
private Long totalWeight;
/** 单位重量 */
@Excel(name = "净重")
private Long unitWeight;
/** 包装重量 */
@Excel(name = "皮重")
private Long packageWeight;
/** 体积 */
@Excel(name = "体积")
private Long volume;
/** 单价 */
@Excel(name = "单价")
private Long unitPrice;
/** 备注 */
@Excel(name = "备注")
private String remark;
/** 物料编码 检索条件 */
private String materialCode;
/** TS Code 检索条件 */
@Excel(name = "TS Code")
// @Excel(name = "TS Code")
private String tsCode;
/** 物料分类 检索条件 */
@Excel(name = "物料分类")
// @Excel(name = "物料分类")
private String categoryCode;
/** 危险类别ID 字典 */
@Excel(name = "危险类别ID")
// @Excel(name = "危险类别ID")
private String hazardId;
/** 规格型号 检索条件 */
@Excel(name = "规格型号")
// @Excel(name = "规格型号")
private String specification;
/** 计量单位 字典 */
@Excel(name = "计量单位")
// @Excel(name = "计量单位")
private String materialUnit;
/** 单位重量 */
@Excel(name = "单位重量")
private Long unitWeight;
/** 包装重量 */
@Excel(name = "包装重量")
private Long packageWeight;
/** 总重量 */
@Excel(name = "总重量")
private Long totalWeight;
/** 体积 */
@Excel(name = "体积")
private Long volume;
/** 保质期天数 */
@Excel(name = "保质期天数")
// @Excel(name = "保质期天数")
private Long shelfLifeDays;
/** 存储温度要求 */
@Excel(name = "存储温度要求")
// @Excel(name = "存储温度要求")
private String storageTemperature;
/** 特殊存储要求 */
@Excel(name = "特殊存储要求")
// @Excel(name = "特殊存储要求")
private String specialRequirements;
/** 是否批次管理 1-是 0-否 */
@Excel(name = "是否批次管理 1-是 0-否")
// @Excel(name = "是否批次管理 1-是 0-否")
private Long isBatchManaged;
/** 是否序列号管理 1-是 0-否 */
@Excel(name = "是否序列号管理 1-是 0-否")
// @Excel(name = "是否序列号管理 1-是 0-否")
private Long isSerialManaged;
/** 最低库存 */
@Excel(name = "最低库存")
// @Excel(name = "最低库存")
private Long minStockLevel;
/** 最高库存 */
@Excel(name = "最高库存")
// @Excel(name = "最高库存")
private Long maxStockLevel;
/** 是否正在使用 1-是 0-否 */
@Excel(name = "是否正在使用 1-是 0-否")
// @Excel(name = "是否正在使用 1-是 0-否")
private Long isUsed;
/** 是否激活 1-是 0-否 */
@Excel(name = "是否激活 1-是 0-否")
private Long isActive;
/** 风险等级 字典 */
@Excel(name = "风险等级")
// @Excel(name = "风险等级")
private String riskLevel;
/** 排序 */
@Excel(name = "排序")
// @Excel(name = "排序")
private Long sortNo;
/** 创建日期 */
......@@ -114,291 +136,290 @@ public class Materials extends BaseEntity
/** 排序号 */
private String updateUserCode;
public void setId(String id)
{
this.id = id;
}
public String getId()
{
public String getId() {
return id;
}
public void setMaterialCode(String materialCode)
{
this.materialCode = materialCode;
public void setId(String id) {
this.id = id;
}
public String getMaterialCode()
{
return materialCode;
public Long getNo() {
return no;
}
public void setMaterialName(String materialName)
{
this.materialName = materialName;
public void setNo(Long no) {
this.no = no;
}
public String getMaterialName()
{
return materialName;
public String getOwnerId() {
return ownerId;
}
public void setSapNo(String sapNo)
{
this.sapNo = sapNo;
public void setOwnerId(String ownerId) {
this.ownerId = ownerId;
}
public String getSapNo()
{
public String getSapNo() {
return sapNo;
}
public void setTsCode(String tsCode)
{
this.tsCode = tsCode;
public void setSapNo(String sapNo) {
this.sapNo = sapNo;
}
public String getTsCode()
{
return tsCode;
public Long getIsActive() {
return isActive;
}
public void setCategoryCode(String categoryCode)
{
this.categoryCode = categoryCode;
public void setIsActive(Long isActive) {
this.isActive = isActive;
}
public String getCategoryCode()
{
return categoryCode;
public String getMaterialName() {
return materialName;
}
public void setHazardId(String hazardId)
{
this.hazardId = hazardId;
public void setMaterialName(String materialName) {
this.materialName = materialName;
}
public String getHazardId()
{
return hazardId;
public String getMaterialEname() {
return materialEname;
}
public void setSpecification(String specification)
{
this.specification = specification;
public void setMaterialEname(String materialEname) {
this.materialEname = materialEname;
}
public String getSpecification()
{
return specification;
public Long getTotalWeight() {
return totalWeight;
}
public void setMaterialUnit(String materialUnit)
{
this.materialUnit = materialUnit;
public void setTotalWeight(Long totalWeight) {
this.totalWeight = totalWeight;
}
public String getMaterialUnit()
{
return materialUnit;
public Long getUnitWeight() {
return unitWeight;
}
public void setUnitWeight(Long unitWeight)
{
public void setUnitWeight(Long unitWeight) {
this.unitWeight = unitWeight;
}
public Long getUnitWeight()
{
return unitWeight;
public Long getPackageWeight() {
return packageWeight;
}
public void setPackageWeight(Long packageWeight)
{
public void setPackageWeight(Long packageWeight) {
this.packageWeight = packageWeight;
}
public Long getPackageWeight()
{
return packageWeight;
public Long getVolume() {
return volume;
}
public void setTotalWeight(Long totalWeight)
{
this.totalWeight = totalWeight;
public void setVolume(Long volume) {
this.volume = volume;
}
public Long getTotalWeight()
{
return totalWeight;
public Long getUnitPrice() {
return unitPrice;
}
public void setVolume(Long volume)
{
this.volume = volume;
public void setUnitPrice(Long unitPrice) {
this.unitPrice = unitPrice;
}
public Long getVolume()
{
return volume;
@Override
public String getRemark() {
return remark;
}
public void setShelfLifeDays(Long shelfLifeDays)
{
this.shelfLifeDays = shelfLifeDays;
@Override
public void setRemark(String remark) {
this.remark = remark;
}
public String getMaterialCode() {
return materialCode;
}
public void setMaterialCode(String materialCode) {
this.materialCode = materialCode;
}
public String getTsCode() {
return tsCode;
}
public void setTsCode(String tsCode) {
this.tsCode = tsCode;
}
public String getCategoryCode() {
return categoryCode;
}
public void setCategoryCode(String categoryCode) {
this.categoryCode = categoryCode;
}
public String getHazardId() {
return hazardId;
}
public void setHazardId(String hazardId) {
this.hazardId = hazardId;
}
public String getSpecification() {
return specification;
}
public Long getShelfLifeDays()
{
public void setSpecification(String specification) {
this.specification = specification;
}
public String getMaterialUnit() {
return materialUnit;
}
public void setMaterialUnit(String materialUnit) {
this.materialUnit = materialUnit;
}
public Long getShelfLifeDays() {
return shelfLifeDays;
}
public void setStorageTemperature(String storageTemperature)
{
this.storageTemperature = storageTemperature;
public void setShelfLifeDays(Long shelfLifeDays) {
this.shelfLifeDays = shelfLifeDays;
}
public String getStorageTemperature()
{
public String getStorageTemperature() {
return storageTemperature;
}
public void setSpecialRequirements(String specialRequirements)
{
this.specialRequirements = specialRequirements;
public void setStorageTemperature(String storageTemperature) {
this.storageTemperature = storageTemperature;
}
public String getSpecialRequirements()
{
public String getSpecialRequirements() {
return specialRequirements;
}
public void setIsBatchManaged(Long isBatchManaged)
{
this.isBatchManaged = isBatchManaged;
public void setSpecialRequirements(String specialRequirements) {
this.specialRequirements = specialRequirements;
}
public Long getIsBatchManaged()
{
public Long getIsBatchManaged() {
return isBatchManaged;
}
public void setIsSerialManaged(Long isSerialManaged)
{
this.isSerialManaged = isSerialManaged;
public void setIsBatchManaged(Long isBatchManaged) {
this.isBatchManaged = isBatchManaged;
}
public Long getIsSerialManaged()
{
public Long getIsSerialManaged() {
return isSerialManaged;
}
public void setMinStockLevel(Long minStockLevel)
{
this.minStockLevel = minStockLevel;
public void setIsSerialManaged(Long isSerialManaged) {
this.isSerialManaged = isSerialManaged;
}
public Long getMinStockLevel()
{
public Long getMinStockLevel() {
return minStockLevel;
}
public void setMaxStockLevel(Long maxStockLevel)
{
this.maxStockLevel = maxStockLevel;
public void setMinStockLevel(Long minStockLevel) {
this.minStockLevel = minStockLevel;
}
public Long getMaxStockLevel()
{
public Long getMaxStockLevel() {
return maxStockLevel;
}
public Long getIsUsed() { return isUsed; }
public void setIsUsed(Long isUsed) { this.isUsed = isUsed; }
public void setIsActive(Long isActive)
{
this.isActive = isActive;
public void setMaxStockLevel(Long maxStockLevel) {
this.maxStockLevel = maxStockLevel;
}
public Long getIsActive()
{
return isActive;
public Long getIsUsed() {
return isUsed;
}
public void setRiskLevel(String riskLevel)
{
this.riskLevel = riskLevel;
public void setIsUsed(Long isUsed) {
this.isUsed = isUsed;
}
public String getRiskLevel()
{
public String getRiskLevel() {
return riskLevel;
}
public void setSortNo(Long sortNo)
{
this.sortNo = sortNo;
public void setRiskLevel(String riskLevel) {
this.riskLevel = riskLevel;
}
public Long getSortNo()
{
public Long getSortNo() {
return sortNo;
}
public void setCreateUserCode(String createUserCode)
{
this.createUserCode = createUserCode;
public void setSortNo(Long sortNo) {
this.sortNo = sortNo;
}
public String getCreateUserCode()
{
public String getCreateUserCode() {
return createUserCode;
}
public void setUpdateUserCode(String updateUserCode)
{
this.updateUserCode = updateUserCode;
public void setCreateUserCode(String createUserCode) {
this.createUserCode = createUserCode;
}
public String getUpdateUserCode()
{
public String getUpdateUserCode() {
return updateUserCode;
}
public void setUpdateUserCode(String updateUserCode) {
this.updateUserCode = updateUserCode;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("materialCode", getMaterialCode())
.append("materialName", getMaterialName())
.append("sapNo", getSapNo())
.append("tsCode", getTsCode())
.append("categoryCode", getCategoryCode())
.append("hazardId", getHazardId())
.append("specification", getSpecification())
.append("materialUnit", getMaterialUnit())
.append("unitWeight", getUnitWeight())
.append("packageWeight", getPackageWeight())
.append("totalWeight", getTotalWeight())
.append("volume", getVolume())
.append("shelfLifeDays", getShelfLifeDays())
.append("storageTemperature", getStorageTemperature())
.append("specialRequirements", getSpecialRequirements())
.append("isBatchManaged", getIsBatchManaged())
.append("isSerialManaged", getIsSerialManaged())
.append("minStockLevel", getMinStockLevel())
.append("maxStockLevel", getMaxStockLevel())
.append("isUsed",getIsUsed())
.append("isActive", getIsActive())
.append("riskLevel", getRiskLevel())
.append("sortNo", getSortNo())
.append("createTime", getCreateTime())
.append("createUserCode", getCreateUserCode())
.append("updateTime", getUpdateTime())
.append("updateUserCode", getUpdateUserCode())
.toString();
return "Materials{" +
"id='" + id + '\'' +
", no=" + no +
", ownerId='" + ownerId + '\'' +
", sapNo='" + sapNo + '\'' +
", isActive=" + isActive +
", materialName='" + materialName + '\'' +
", materialEname='" + materialEname + '\'' +
", totalWeight=" + totalWeight +
", unitWeight=" + unitWeight +
", packageWeight=" + packageWeight +
", volume=" + volume +
", unitPrice=" + unitPrice +
", remark='" + remark + '\'' +
", materialCode='" + materialCode + '\'' +
", tsCode='" + tsCode + '\'' +
", categoryCode='" + categoryCode + '\'' +
", hazardId='" + hazardId + '\'' +
", specification='" + specification + '\'' +
", materialUnit='" + materialUnit + '\'' +
", shelfLifeDays=" + shelfLifeDays +
", storageTemperature='" + storageTemperature + '\'' +
", specialRequirements='" + specialRequirements + '\'' +
", isBatchManaged=" + isBatchManaged +
", isSerialManaged=" + isSerialManaged +
", minStockLevel=" + minStockLevel +
", maxStockLevel=" + maxStockLevel +
", isUsed=" + isUsed +
", riskLevel='" + riskLevel + '\'' +
", sortNo=" + sortNo +
", createUserCode='" + createUserCode + '\'' +
", updateUserCode='" + updateUserCode + '\'' +
'}';
}
}
......@@ -719,4 +719,5 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
}
return sb.toString();
}
}
\ No newline at end of file
......@@ -3,7 +3,7 @@ package com.ruoyi.inventory.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import com.ruoyi.inventory.domain.vo.InboundTemplateVO;
import com.ruoyi.inventory.domain.vo.inboundVO.InboundTemplateVO;
import com.ruoyi.inventory.domain.vo.OutboundOrdersSummaryVO;
import com.ruoyi.inventory.domain.vo.OutboundTemplateVO;
import org.springframework.security.access.prepost.PreAuthorize;
......
......@@ -6,7 +6,7 @@ import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.inventory.domain.Inventory;
import com.ruoyi.inventory.domain.vo.InboundTemplateVO;
import com.ruoyi.inventory.domain.vo.inboundVO.InboundTemplateVO;
import com.ruoyi.inventory.domain.vo.OutboundTemplateVO;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
......
......@@ -2,6 +2,7 @@ package com.ruoyi.inventory.domain;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
......@@ -13,6 +14,7 @@ import com.ruoyi.common.core.domain.BaseEntity;
* @author ruoyi
* @date 2025-12-02
*/
@Data
public class InventoryTransactions extends BaseEntity
{
private static final long serialVersionUID = 1L;
......@@ -91,218 +93,7 @@ public class InventoryTransactions extends BaseEntity
@Excel(name = "排序号")
private String updateUserCode;
public String getInventoryId() {
return inventoryId;
}
@Excel(name = "单价")
private String unitPrice;
public void setInventoryId(String inventoryId) {
this.inventoryId = inventoryId;
}
public void setId(String id)
{
this.id = id;
}
public String getId()
{
return id;
}
public void setTransactionType(Long transactionType)
{
this.transactionType = transactionType;
}
public Long getTransactionType()
{
return transactionType;
}
public void setReferenceId(String referenceId)
{
this.referenceId = referenceId;
}
public String getReferenceId()
{
return referenceId;
}
public void setReferenceItemId(String referenceItemId)
{
this.referenceItemId = referenceItemId;
}
public String getReferenceItemId()
{
return referenceItemId;
}
public void setMaterialId(String materialId)
{
this.materialId = materialId;
}
public String getMaterialId()
{
return materialId;
}
public void setBatchCode(String batchCode)
{
this.batchCode = batchCode;
}
public String getBatchCode()
{
return batchCode;
}
public void setWarehouseId(String warehouseId)
{
this.warehouseId = warehouseId;
}
public String getWarehouseId()
{
return warehouseId;
}
public void setLocationId(String locationId)
{
this.locationId = locationId;
}
public String getLocationId()
{
return locationId;
}
public void setOwnerId(String ownerId)
{
this.ownerId = ownerId;
}
public String getOwnerId()
{
return ownerId;
}
public void setQuantityBefore(Long quantityBefore)
{
this.quantityBefore = quantityBefore;
}
public Long getQuantityBefore()
{
return quantityBefore;
}
public void setQuantityChange(Long quantityChange)
{
this.quantityChange = quantityChange;
}
public Long getQuantityChange()
{
return quantityChange;
}
public void setQuantityAfter(Long quantityAfter)
{
this.quantityAfter = quantityAfter;
}
public Long getQuantityAfter()
{
return quantityAfter;
}
public void setTransactionTime(Date transactionTime)
{
this.transactionTime = transactionTime;
}
public Date getTransactionTime()
{
return transactionTime;
}
public void setOperatedBy(String operatedBy)
{
this.operatedBy = operatedBy;
}
public String getOperatedBy()
{
return operatedBy;
}
public void setIsUsed(Long isUsed)
{
this.isUsed = isUsed;
}
public Long getIsUsed()
{
return isUsed;
}
public void setSortNo(Long sortNo)
{
this.sortNo = sortNo;
}
public Long getSortNo()
{
return sortNo;
}
public void setCreateUserCode(String createUserCode)
{
this.createUserCode = createUserCode;
}
public String getCreateUserCode()
{
return createUserCode;
}
public void setUpdateUserCode(String updateUserCode)
{
this.updateUserCode = updateUserCode;
}
public String getUpdateUserCode()
{
return updateUserCode;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("transactionType", getTransactionType())
.append("referenceId", getReferenceId())
.append("referenceItemId", getReferenceItemId())
.append("materialId", getMaterialId())
.append("batchCode", getBatchCode())
.append("warehouseId", getWarehouseId())
.append("locationId", getLocationId())
.append("ownerId", getOwnerId())
.append("quantityBefore", getQuantityBefore())
.append("quantityChange", getQuantityChange())
.append("quantityAfter", getQuantityAfter())
.append("transactionTime", getTransactionTime())
.append("operatedBy", getOperatedBy())
.append("remark", getRemark())
.append("isUsed", getIsUsed())
.append("sortNo", getSortNo())
.append("createTime", getCreateTime())
.append("createUserCode", getCreateUserCode())
.append("updateTime", getUpdateTime())
.append("updateUserCode", getUpdateUserCode())
.toString();
}
}
......@@ -66,7 +66,7 @@ public class OutboundOrderItems extends BaseEntity
/** 单价 */
@Excel(name = "单价")
private Long unitPrice;
private Double unitPrice;
/** 计划数量 */
@Excel(name = "计划数量")
......@@ -117,5 +117,7 @@ public class OutboundOrderItems extends BaseEntity
@Excel(name = "排序号")
private String updateUserCode;
private String InventoryType;
}
\ No newline at end of file
......@@ -28,7 +28,7 @@ public class Owners extends BaseEntity
@Excel(name = "货主名称")
private String ownerName;
@Excel(name = "货主名称")
@Excel(name = "英文名称")
private String englishName;
/** 货主类型 1-供应商 2-客户 3-内部 检索条件 */
......
package com.ruoyi.inventory.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import java.util.Date;
/**
* 入库单导入对象 inboundTemplate
*
* @author ruoyi
* @date 2025-12-02
*/
public class InboundTemplateVO extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 编号 */
private String id;
/** 入库日期 日期无时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "入库日期", width = 30, dateFormat = "yyyy-MM-dd")
private Date inboundDate;
/** 货物ID 字典,检索条件 */
@Excel(name = "SAP No")
private String sapNo;
/** 货物名称 */
@Excel(name = "物料名称")
private String materialName;
/** 货物名称 */
@Excel(name = "TS Code")
private String tsCode;
/** 货主ID */
@Excel(name = "货主")
private String ownerId;
/** 批次ID 检索条件 */
@Excel(name = "批号")
private String batchId;
/** 计划数量 */
@Excel(name = "计划数量")
private Long plannedQuantity;
/** 入库单号 检索条件 */
@Excel(name = "单号")
private String orderId;
/** 系统编号 检索条件 */
@Excel(name = "系统编号")
private String systemNo;
/** 入库类型 字典,检索条件 */
// @Excel(name = "入库类型")
private String orderTypeId;
@Excel(name = "件重")
private Double unitWeight;
/** 约数 */
@Excel(name = "约数")
private Long divisor;
/** 实际件数 */
@Excel(name = "实际件数")
private Long actualPackages;
/** 实际数量 */
@Excel(name = "实发数量")
private Long actualQuantity;
/** 仓库ID 暂无用 */
@Excel(name = "仓库")
private String warehouseId;
/** 库位ID 检索条件 */
@Excel(name = "库位")
private String locationId;
/** 标签颜色 字典,检索条件 */
@Excel(name = "标签颜色")
private Long labelColor;
/** 凭证号 检索条件 */
@Excel(name = "凭证号")
private String voucherNumber;
/** 单价 */
@Excel(name = "单价")
private Long unitPrice;
/** 备注 */
@Excel(name = "备注")
private String remark;
/** 订单类型 字典,检索条件 */
@Excel(name = "订单类型")
private String orderType;
/** 收货人 */
@Excel(name = "收货人")
private String receivedBy;
/** 物料备注 */
@Excel(name = "物料备注")
private String remark2;
/** 负责人 暂无用 */
// @Excel(name = "负责人 暂无用")
private String opUserName;
/** 计划件数 暂无用 */
// @Excel(name = "件数")
private Long plannedPackages;
/** 排序号 */
private Long sortNo;
/** 创建日期 */
private String createUserCode;
private String updateUserCode;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public String getSystemNo() {
return systemNo;
}
public void setSystemNo(String systemNo) {
this.systemNo = systemNo;
}
public String getOrderTypeId() {
return orderTypeId;
}
public void setOrderTypeId(String orderTypeId) {
this.orderTypeId = orderTypeId;
}
public String getBatchId() {
return batchId;
}
public void setBatchId(String batchId) {
this.batchId = batchId;
}
public Date getInboundDate() {
return inboundDate;
}
public void setInboundDate(Date inboundDate) {
this.inboundDate = inboundDate;
}
public String getOrderType() {
return orderType;
}
public void setOrderType(String orderType) {
this.orderType = orderType;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getOwnerId() {
return ownerId;
}
public void setOwnerId(String ownerId) {
this.ownerId = ownerId;
}
public String getWarehouseId() {
return warehouseId;
}
public void setWarehouseId(String warehouseId) {
this.warehouseId = warehouseId;
}
public String getLocationId() {
return locationId;
}
public void setLocationId(String locationId) {
this.locationId = locationId;
}
public String getRemark2() {
return remark2;
}
public void setRemark2(String remark2) {
this.remark2 = remark2;
}
public String getSapNo() {
return sapNo;
}
public void setSapNo(String sapNo) {
this.sapNo = sapNo;
}
public String getMaterialName() {
return materialName;
}
public void setMaterialName(String materialName) {
this.materialName = materialName;
}
public String getOpUserName() {
return opUserName;
}
public void setOpUserName(String opUserName) {
this.opUserName = opUserName;
}
public Long getPlannedQuantity() {
return plannedQuantity;
}
public void setPlannedQuantity(Long plannedQuantity) {
this.plannedQuantity = plannedQuantity;
}
public Long getActualQuantity() {
return actualQuantity;
}
public void setActualQuantity(Long actualQuantity) {
this.actualQuantity = actualQuantity;
}
public Long getPlannedPackages() {
return plannedPackages;
}
public void setPlannedPackages(Long plannedPackages) {
this.plannedPackages = plannedPackages;
}
public Long getActualPackages() {
return actualPackages;
}
public void setActualPackages(Long actualPackages) {
this.actualPackages = actualPackages;
}
public Long getDivisor() {
return divisor;
}
public void setDivisor(Long divisor) {
this.divisor = divisor;
}
public Long getLabelColor() {
return labelColor;
}
public void setLabelColor(Long labelColor) {
this.labelColor = labelColor;
}
public String getVoucherNumber() {
return voucherNumber;
}
public void setVoucherNumber(String voucherNumber) {
this.voucherNumber = voucherNumber;
}
public Long getUnitPrice() {
return unitPrice;
}
public void setUnitPrice(Long unitPrice) {
this.unitPrice = unitPrice;
}
public String getReceivedBy() {
return receivedBy;
}
public void setReceivedBy(String receivedBy) {
this.receivedBy = receivedBy;
}
public String getCreateUserCode() {
return createUserCode;
}
public void setCreateUserCode(String createUserCode) {
this.createUserCode = createUserCode;
}
public String getUpdateUserCode() {
return updateUserCode;
}
public void setUpdateUserCode(String updateUserCode) {
this.updateUserCode = updateUserCode;
}
public Long getSortNo() {
return sortNo;
}
public void setSortNo(Long sortNo) {
this.sortNo = sortNo;
}
public String getTsCode() {
return tsCode;
}
public void setTsCode(String tsCode) {
this.tsCode = tsCode;
}
public Double getUnitWeight() {
return unitWeight;
}
public void setUnitWeight(Double unitWeight) {
this.unitWeight = unitWeight;
}
@Override
public String toString() {
return "InboundTemplateVO{" +
"id='" + id + '\'' +
", inboundDate=" + inboundDate +
", sapNo='" + sapNo + '\'' +
", materialName='" + materialName + '\'' +
", tsCode='" + tsCode + '\'' +
", batchId='" + batchId + '\'' +
", plannedQuantity=" + plannedQuantity +
", orderId='" + orderId + '\'' +
", systemNo='" + systemNo + '\'' +
", orderTypeId='" + orderTypeId + '\'' +
", unitWeight=" + unitWeight +
", divisor=" + divisor +
", actualPackages=" + actualPackages +
", actualQuantity=" + actualQuantity +
", warehouseId='" + warehouseId + '\'' +
", locationId='" + locationId + '\'' +
", labelColor=" + labelColor +
", voucherNumber='" + voucherNumber + '\'' +
", unitPrice=" + unitPrice +
", remark='" + remark + '\'' +
", orderType='" + orderType + '\'' +
", receivedBy='" + receivedBy + '\'' +
", remark2='" + remark2 + '\'' +
", ownerId='" + ownerId + '\'' +
", opUserName='" + opUserName + '\'' +
", plannedPackages=" + plannedPackages +
", sortNo=" + sortNo +
", createUserCode='" + createUserCode + '\'' +
", updateUserCode='" + updateUserCode + '\'' +
'}';
}
}
package com.ruoyi.inventory.domain.vo.inboundVO;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import lombok.Data;
import java.util.Date;
@Data
public class InboundFinishTemplateVO extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 编号 */
private String id;
/** 入库日期 日期无时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "日期", width = 30, dateFormat = "yyyy-MM-dd")
private Date inboundDate;
/** 货物ID 字典,检索条件 */
@Excel(name = "SAP No")
private String sapNo;
/** 货物名称 */
@Excel(name = "物料名称")
private String materialName;
/** 货物名称 */
@Excel(name = "TS Code")
private String tsCode;
/** 批次ID 检索条件 */
@Excel(name = "批号")
private String batchId;
/** 计划数量 */
@Excel(name = "计划数量")
private Long plannedQuantity;
@Excel(name = "件重")
private Double packageWeight;
/** 约数 */
@Excel(name = "约数")
private Long divisor;
@Excel(name = "件数")
private Long actualPackages;
@Excel(name = "重量")
private Double unitWeight;
/** 实际数量 */
// @Excel(name = "实发数量")
private Long actualQuantity;
/** 库位 */
@Excel(name = "库位")
private String locationId;
/** 仓库ID 暂无用 */
@Excel(name = "仓库")
private String warehouseId;
/** 成品表里的第二个 "库位" */
@Excel(name = "库位")
private String remark2;
/** 库位ID 检索条件 */
// @Excel(name = "收货库位")
private String relocationId;
/** 标签颜色 字典,检索条件 */
@Excel(name = "标签颜色")
private Long labelColor;
/** 凭证号 检索条件 */
@Excel(name = "凭证号")
private String voucherNumber;
@Excel(name = "保温")
private String keepWarm;
@Excel(name = "危险类别")
private String hazardId;
/** 入库单号 检索条件 */
@Excel(name = "智观(客户订单号)")
private String orderId;
/** 系统编号 检索条件 */
@Excel(name = "客户订单号/PO号")
private String systemNo;
/** 货主ID */
@Excel(name = "货主")
private String ownerId;
/** 入库类型 字典,检索条件 */
@Excel(name = "贴标数量")
private String labelQuantity;
/** 订单类型 字典,检索条件 */
@Excel(name = "订单类型")
private String orderType;
@Excel(name = "单个件重")
private Double packageWeight2;
/** 单价 */
// @Excel(name = "单价")
private Long unitPrice;
/** 收货人 */
// @Excel(name = "收货人")
private String receivedBy;
/** 物料备注 */
// @Excel(name = "物料备注")
private String remark1;
/** 负责人 暂无用 */
// @Excel(name = "负责人 暂无用")
private String opUserName;
/** 计划件数 暂无用 */
// @Excel(name = "件数")
private Long plannedPackages;
/** 排序号 */
private Long sortNo;
/** 创建日期 */
private String createUserCode;
private String updateUserCode;
}
package com.ruoyi.inventory.domain.vo.inboundVO;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import lombok.Data;
import java.util.Date;
@Data
public class InboundTRDCTemplateVO extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 编号 */
private String id;
/** 入库日期 日期无时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "日期", width = 30, dateFormat = "yyyy-MM-dd")
private Date inboundDate;
/** 货物ID 字典,检索条件 */
@Excel(name = "SAP No")
private String sapNo;
/** 货物名称 */
@Excel(name = "物料名称")
private String materialName;
/** 货物名称 */
@Excel(name = "TS Code")
private String tsCode;
/** 批次ID 检索条件 */
@Excel(name = "批号")
private String batchId;
/** 计划数量 */
@Excel(name = "计划数量")
private Long plannedQuantity;
@Excel(name = "件重")
private Double unitWeight;
/** 约数 */
@Excel(name = "约数")
private Long divisor;
/** 实际件数 */
@Excel(name = "件数")
private Long actualPackages;
/** 实际数量 */
@Excel(name = "实发数量")
private Long actualQuantity;
/** 库位ID 检索条件 */
@Excel(name = "库位")
private String locationId;
/** 仓库ID 暂无用 */
@Excel(name = "仓库")
private String warehouseId;
/** 备注 */
@Excel(name = "备注")
private String remark;
/** 库位ID 检索条件 */
// @Excel(name = "收货库位")
private String relocationId;
/** 标签颜色 字典,检索条件 */
@Excel(name = "标签颜色")
private Long labelColor;
/** 凭证号 检索条件 */
@Excel(name = "凭证号")
private String voucherNumber;
@Excel(name = "保温")
private String keepWarm;
@Excel(name = "危险类别")
private String hazardId;
/** 入库单号 检索条件 */
@Excel(name = "单号")
private String orderId;
/** 系统编号 检索条件 */
@Excel(name = "系统编号")
private String systemNo;
/** 货主ID */
@Excel(name = "货主")
private String ownerId;
/** 入库类型 字典,检索条件 */
@Excel(name = "贴标数量")
private String labelQuantity;
/** 订单类型 字典,检索条件 */
@Excel(name = "订单类型")
private String orderType;
@Excel(name = "单件重量")
private Double packageWeight;
/** 单价 */
// @Excel(name = "单价")
private Long unitPrice;
/** 收货人 */
// @Excel(name = "收货人")
private String receivedBy;
/** 物料备注 */
// @Excel(name = "物料备注")
private String remark2;
/** 负责人 暂无用 */
// @Excel(name = "负责人 暂无用")
private String opUserName;
/** 计划件数 暂无用 */
// @Excel(name = "件数")
private Long plannedPackages;
/** 排序号 */
private Long sortNo;
/** 创建日期 */
private String createUserCode;
private String updateUserCode;
}
package com.ruoyi.inventory.domain.vo.inboundVO;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import lombok.Data;
import java.util.Date;
/**
* 入库单导入对象 inboundTemplate
*
* @author ZTW
* @date 2025-12-02
*/
@Data
public class InboundTemplateVO extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 编号 */
private String id;
/** 入库日期 日期无时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "日期", width = 30, dateFormat = "yyyy-MM-dd")
private Date inboundDate;
/** 货物ID 字典,检索条件 */
@Excel(name = "SAP No")
private String sapNo;
/** 货物名称 */
@Excel(name = "物料名称")
private String materialName;
/** 货物名称 */
@Excel(name = "TS Code")
private String tsCode;
/** 批次ID 检索条件 */
@Excel(name = "批号")
private String batchId;
/** 计划数量 */
@Excel(name = "计划数量")
private Long plannedQuantity;
@Excel(name = "件重")
private Double unitWeight;
/** 约数 */
@Excel(name = "约数")
private Long divisor;
/** 实际件数 */
@Excel(name = "件数")
private Long actualPackages;
/** 实际数量 */
@Excel(name = "实发数量")
private Long actualQuantity;
/** 库位ID 检索条件 */
@Excel(name = "库位")
private String locationId;
/** 仓库ID 暂无用 */
@Excel(name = "仓库")
private String warehouseId;
/** 备注 */
// @Excel(name = "备注")
private String remark;
/** 库位ID 检索条件 */
@Excel(name = "收货库位")
private String relocationId;
/** 标签颜色 字典,检索条件 */
@Excel(name = "标签颜色")
private Long labelColor;
/** 凭证号 检索条件 */
@Excel(name = "凭证号")
private String voucherNumber;
@Excel(name = "保温")
private String keepWarm;
@Excel(name = "危险类别")
private String hazardId;
/** 入库单号 检索条件 */
@Excel(name = "单号")
private String orderId;
/** 系统编号 检索条件 */
@Excel(name = "系统编号")
private String systemNo;
/** 货主ID */
@Excel(name = "货主")
private String ownerId;
/** 入库类型 字典,检索条件 */
@Excel(name = "贴标数量")
private String labelQuantity;
/** 订单类型 字典,检索条件 */
@Excel(name = "订单类型")
private String orderType;
@Excel(name = "单件重量")
private Double packageWeight;
/** 单价 */
// @Excel(name = "单价")
private Long unitPrice;
/** 收货人 */
// @Excel(name = "收货人")
private String receivedBy;
/** 物料备注 */
// @Excel(name = "物料备注")
private String remark2;
/** 负责人 暂无用 */
// @Excel(name = "负责人 暂无用")
private String opUserName;
/** 计划件数 暂无用 */
// @Excel(name = "件数")
private Long plannedPackages;
/** 排序号 */
private Long sortNo;
/** 创建日期 */
private String createUserCode;
private String updateUserCode;
}
......@@ -116,4 +116,6 @@ public interface InventoryMapper
public List<java.util.Map<String, String>> selectInventoryTopTenByAmount();
public List<java.util.Map<String, String>> selectInventoryTopTenByQuantity();
public void batchUpdateInventory(List<Inventory> inventoryList);
}
......@@ -2,10 +2,9 @@ package com.ruoyi.inventory.service;
import java.util.List;
import com.ruoyi.inventory.domain.InboundOrderItems;
import com.ruoyi.inventory.domain.InboundOrders;
import com.ruoyi.inventory.domain.vo.InboundMaterialTotalVO;
import com.ruoyi.inventory.domain.vo.InboundTemplateVO;
/**
* 入库单主Service接口
......@@ -69,7 +68,7 @@ public interface IInboundOrdersService
* @param inboundOrders,isUpdateSupport,operName 入库单数据信息
* @return 结果
*/
public String importInboundOrders(List<InboundTemplateVO> inboundOrders, Integer isUpdateSupport, String operName, Integer orderType);
public <T> String importInboundOrders(List<T> inboundOrders, Integer isUpdateSupport, String operName, Integer orderType);
/**
......
......@@ -2,10 +2,7 @@ package com.ruoyi.inventory.service;
import java.util.List;
import com.ruoyi.inventory.domain.OutboundOrderItems;
import com.ruoyi.inventory.domain.vo.InboundTemplateVO;
import com.ruoyi.inventory.domain.vo.OutboundOrdersSummaryVO;
import com.ruoyi.inventory.domain.vo.OutboundTemplateVO;
import org.springframework.transaction.annotation.Transactional;
/**
* 出库单明细Service接口
......
package com.ruoyi.inventory.service.impl;
import java.lang.reflect.Method;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import com.ruoyi.common.config.WarehouseConfig;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.inventory.domain.*;
import com.ruoyi.inventory.domain.vo.InboundMaterialTotalVO;
import com.ruoyi.inventory.mapper.*;
import org.apache.commons.lang3.SystemUtils;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.CollectionUtils;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.inventory.domain.vo.InboundTemplateVO;
import com.ruoyi.inventory.domain.vo.inboundVO.InboundTemplateVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -27,12 +30,12 @@ import com.ruoyi.inventory.service.IInboundOrdersService;
/**
* 入库单主Service业务层处理
*
*
* @author ruoyi
* @date 2025-12-02
*/
@Service
public class InboundOrdersServiceImpl implements IInboundOrdersService
public class InboundOrdersServiceImpl implements IInboundOrdersService
{
@Autowired
private InboundOrdersMapper inboundOrdersMapper;
......@@ -49,7 +52,7 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
private static final Logger log = LoggerFactory.getLogger(InboundOrdersServiceImpl.class);
/**
* 查询入库单主
*
*
* @param id 入库单主主键
* @return 入库单主
*/
......@@ -61,7 +64,7 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
/**
* 查询入库单主列表
*
*
* @param inboundOrders 入库单主
* @return 入库单主
*/
......@@ -73,7 +76,7 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
/**
* 新增入库单主
*
*
* @param inboundOrders 入库单主
* @return 结果
*/
......@@ -91,7 +94,7 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
/**
* 修改入库单主
*
*
* @param inboundOrders 入库单主
* @return 结果
*/
......@@ -109,7 +112,7 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
/**
* 批量删除入库单主
*
*
* @param ids 需要删除的入库单主主键
* @return 结果
*/
......@@ -123,7 +126,7 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
/**
* 删除入库单主信息
*
*
* @param id 入库单主主键
* @return 结果
*/
......@@ -137,7 +140,7 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
/**
* 新增入库单明细信息
*
*
* @param inboundOrders 入库单主对象
*/
public void insertInboundOrderItems(InboundOrders inboundOrders)
......@@ -174,7 +177,7 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
*/
@Override
@Transactional(rollbackFor = Exception.class)
public String importInboundOrders(List<InboundTemplateVO> inboundOrdersList, Integer isUpdateSupport, String operName, Integer orderType) {
public <T> String importInboundOrders(List<T> inboundOrdersList, Integer isUpdateSupport, String operName, Integer orderType) {
if (StringUtils.isNull(inboundOrdersList) || inboundOrdersList.size() == 0) {
throw new ServiceException("导入数据不能为空!");
}
......@@ -196,22 +199,53 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
Map<String, String> warehouseNameIdMap = loadWarehouseNameIdMap();
// 预加载库位名称-ID映射
Map<String, String> storageLocationNameIdMap = loadStorageLocationNameIdMap();
// 3. 按入库单号分组(核心:同一入库单的多条明细归为一组)
Map<String, List<InboundTemplateVO>> orderGroupMap = Optional.ofNullable(inboundOrdersList)
// 3. 按入库单号分组(核心:通过反射处理泛型T的orderId字段)
Map<String, List<T>> orderGroupMap = Optional.ofNullable(inboundOrdersList)
.orElse(Collections.emptyList()) // 空列表兜底,避免NPE
.stream()
.filter(Objects::nonNull) // 过滤null的VO对象
.map(vo -> {
// 预处理:入库单号去空格,统一格式
String orderId = Optional.ofNullable(vo.getOrderId())
.map(String::trim) // 去除首尾空格
.orElse("");
vo.setOrderId(orderId); // 把处理后的单号回写,保证后续使用一致
// 预处理:入库单号去空格,统一格式(反射调用get/setOrderId)
try {
// 反射获取getOrderId方法
Method getOrderIdMethod = vo.getClass().getMethod("getOrderId");
String orderId = Optional.ofNullable(getOrderIdMethod.invoke(vo))
.map(Object::toString)
.map(String::trim) // 去除首尾空格
.orElse("");
// 反射调用setOrderId方法回写处理后的单号
Method setOrderIdMethod = vo.getClass().getMethod("setOrderId", String.class);
setOrderIdMethod.invoke(vo, orderId);
} catch (Exception e) {
throw new ServiceException("实体类缺少orderId的getter/setter方法" + e);
}
return vo;
})
.filter(vo -> StringUtils.isNotBlank(vo.getOrderId())) // 过滤无/空白入库单号的无效行
.filter(vo -> {
// 反射过滤无/空白入库单号的无效行
try {
Method getOrderIdMethod = vo.getClass().getMethod("getOrderId");
String orderId = Optional.ofNullable(getOrderIdMethod.invoke(vo))
.map(Object::toString)
.orElse("");
return StringUtils.isNotBlank(orderId);
} catch (Exception e) {
throw new ServiceException("获取orderId失败" + e);
}
})
.collect(Collectors.groupingBy(
InboundTemplateVO::getOrderId, // 分组Key:处理后的入库单号
vo -> {
// 反射获取分组Key:处理后的入库单号
try {
Method getOrderIdMethod = vo.getClass().getMethod("getOrderId");
return Optional.ofNullable(getOrderIdMethod.invoke(vo))
.map(Object::toString)
.orElse("");
} catch (Exception e) {
throw new ServiceException("分组获取orderId失败" + e);
}
},
Collectors.collectingAndThen(
Collectors.toList(),
list -> Collections.unmodifiableList(list) // 生成不可变列表,防止后续篡改
......@@ -219,16 +253,17 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
));
// 兜底:若分组结果为空,返回空的不可变Map(避免后续判空)
orderGroupMap = Optional.ofNullable(orderGroupMap).orElse(Collections.emptyMap());
// 4. 遍历每个入库单分组处理
for (Map.Entry<String, List<InboundTemplateVO>> entry : orderGroupMap.entrySet()) {
for (Map.Entry<String, List<T>> entry : orderGroupMap.entrySet()) {
String orderId = entry.getKey();
List<InboundTemplateVO> voList = entry.getValue();
List<T> voList = entry.getValue();
InboundOrders mainDO = null;
List<InboundOrderItems> itemDOList = new ArrayList<>();
try {
// 4.1 处理主表(每个入库单号只处理一次主表)
InboundTemplateVO firstVO = voList.get(0); // 取第一条VO的主表信息
T firstVO = voList.get(0); // 取第一条VO的主表信息
// 检查入库单是否已存在
InboundOrders existMain = inboundOrdersMapper.selectInboundOrdersByOrderId(orderId);
......@@ -242,7 +277,7 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
}
// 支持更新,复用已有主表ID
mainDO = existMain;
// 复制VO中的主表字段到已有主表(只更新可修改的字段)
// 反射复制VO中的主表字段到已有主表(只更新可修改的字段)
BeanUtils.copyProperties(firstVO, mainDO, "id", "createBy", "createTime"); // 排除不可更新字段
mainDO.setUpdateBy(operId);
mainDO.setUpdateTime(now);
......@@ -255,7 +290,7 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
} else {
// 新增主表
mainDO = new InboundOrders();
// 复制主表字段(只复制主表相关字段,避免物料字段污染)
// 反射复制主表字段(只复制主表相关字段,避免物料字段污染)
BeanUtils.copyProperties(firstVO, mainDO,
"sapNo", "materialName", "plannedQuantity", "actualQuantity",
"plannedPackages", "materialUnit", "materialRemark"); // 排除子表字段
......@@ -274,9 +309,18 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
mainDO.setOrderStatus(1L); // 默认草稿状态
}
// ========== 货主查询 ==========
// ========== 货主查询(反射获取ownerId) ==========
String ownerName = "";
try {
Method getOwnerIdMethod = firstVO.getClass().getMethod("getOwnerId");
ownerName = Optional.ofNullable(getOwnerIdMethod.invoke(firstVO))
.map(Object::toString)
.orElse("");
} catch (Exception e) {
throw new ServiceException("获取货主名称失败" + e);
}
Owners owners = new Owners();
owners.setOwnerName(mainDO.getOwnerId());
owners.setOwnerName(ownerName);
List<Owners> olist = ownersMapper.selectOwnersList(owners);
if (CollectionUtils.isEmpty(olist)) {
// 抛业务异常,携带具体单号/条件,方便排查
......@@ -291,11 +335,10 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
}
// 4.2 处理子表明细(每条VO对应一条明细)
for (InboundTemplateVO vo : voList) {
for (T vo : voList) {
try {
InboundOrderItems itemDO = new InboundOrderItems();
// 复制子表字段(物料相关)
// 反射复制子表字段(物料相关)
BeanUtils.copyProperties(vo, itemDO,
"orderId", "systemNo", "orderTypeId", "batchId"); // 排除主表字段
// 填充明细必填字段
......@@ -304,13 +347,45 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
itemDO.setCreateTime(now);
itemDO.setCreateUserCode(operId);
itemDO.setOrderId(orderId); // 关联入库单号
itemDO.setBatchId(mainDO.getBatchId());
// 反射获取batchId并设置
String batchId = "";
try {
Method getBatchIdMethod = vo.getClass().getMethod("getBatchId");
batchId = Optional.ofNullable(getBatchIdMethod.invoke(vo))
.map(Object::toString)
.orElse("");
} catch (Exception e) {
throw new ServiceException("获取批号失败" + e);
}
itemDO.setBatchId(batchId);
itemDO.setInboundOrderId(mainDO.getId()); // 关联主表ID(核心!)
itemDO.setSortNo(0L);
itemDO.setRemark(vo.getRemark2());
// ========== 物料SAPNO校验 ==========
String sapNo = Optional.ofNullable(vo.getSapNo()).map(String::trim).orElse("");
// 反射获取remark2并设置
String remark2 = "";
try {
Method getRemark2Method = vo.getClass().getMethod("getRemark2");
remark2 = Optional.ofNullable(getRemark2Method.invoke(vo))
.map(Object::toString)
.orElse("");
} catch (Exception e) {
throw new ServiceException("获取明细备注失败" + e);
}
itemDO.setRemark(remark2);
// ========== 物料SAPNO校验(反射获取sapNo) ==========
String sapNo = "";
try {
Method getSapNoMethod = vo.getClass().getMethod("getSapNo");
sapNo = Optional.ofNullable(getSapNoMethod.invoke(vo))
.map(Object::toString)
.map(String::trim)
.orElse("");
} catch (Exception e) {
throw new ServiceException("获取物料SAP号失败" + e);
}
if (StringUtils.isBlank(sapNo)) {
throw new ServiceException("物料SAP号为空");
}
......@@ -320,28 +395,60 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
}
itemDO.setMaterialId(sapAndId.get("id"));
// ========== 仓库/库位查询 ==========
String warehouseName = Optional.ofNullable(itemDO.getWarehouseId()).map(String::trim).orElse("");
// ========== 仓库/库位查询(反射获取) ==========
// 1. 仓库名称转ID
String warehouseName = "";
try {
Method getWarehouseIdMethod = vo.getClass().getMethod("getWarehouseId");
warehouseName = Optional.ofNullable(getWarehouseIdMethod.invoke(vo))
.map(Object::toString)
.map(String::trim)
.orElse("");
} catch (Exception e) {
throw new ServiceException("获取仓库名称失败" + e);
}
String warehouseId = warehouseNameIdMap.get(warehouseName);
if (StringUtils.isBlank(warehouseId)) {
throw new ServiceException("仓库【" + warehouseName + "】不存在");
log.info("仓库【" + warehouseName + "】不存在,可能暂无仓库或为成品入库,已使用默认仓库");
itemDO.setWarehouseId(WarehouseConfig.DEFAULT_WAREHOUSE_ID);
}else{
itemDO.setWarehouseId(warehouseId);
}
// 2. 库位名称转ID
String locationName = "";
try {
Method getLocationIdMethod = vo.getClass().getMethod("getLocationId");
locationName = Optional.ofNullable(getLocationIdMethod.invoke(vo))
.map(Object::toString)
.map(String::trim)
.orElse("");
} catch (Exception e) {
throw new ServiceException("获取库位名称失败" + e);
}
itemDO.setWarehouseId(warehouseId);
String locationName = Optional.ofNullable(itemDO.getLocationId()).map(String::trim).orElse("");
String locationId = storageLocationNameIdMap.get(locationName);
if (StringUtils.isBlank(locationId)) {
throw new ServiceException("库位【" + locationName + "】不存在");
log.info("库位【" + locationName + "】不存在,可能暂无库位或为成品入库");
}else{
itemDO.setLocationId(locationId);
}
itemDO.setLocationId(locationId);
itemDOList.add(itemDO);
} catch (Exception e) {
// 单个明细失败:仅统计,不影响整单
totalItemFailure++;
// 反射获取sapNo用于错误提示
String sapNo = "";
try {
Method getSapNoMethod = vo.getClass().getMethod("getSapNo");
sapNo = Optional.ofNullable(getSapNoMethod.invoke(vo))
.map(Object::toString)
.orElse("未知");
} catch (Exception ex) {
sapNo = "未知";
}
failureMsg.append(String.format("入库单号【%s】的物料明细【%s】处理失败:%s;\n",
orderId, vo.getSapNo(), e.getMessage()));
log.error("导入明细失败-入库单【{}】-SAP【{}】", orderId, vo.getSapNo(), e);
orderId, sapNo, e.getMessage()));
log.error("导入明细失败-入库单【{}】-SAP【{}】", orderId, sapNo, e);
}
}
......
......@@ -159,14 +159,16 @@ public class InventoryServiceImpl implements IInventoryService
// 库存操作表插入数据
InventoryTransactions transactions = new InventoryTransactions();
transactions.setId(IdUtils.simpleUUID());
transactions.setTransactionType(2L);// 事务类型-盘点
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.getWarehouseId());
OutboundOrders outboundOrders = outboundOrderMapper.selectOutboundOrdersById(outboundOrderItem.getOutboundOrderId());
transactions.setOwnerId(outboundOrders.getOwnerId());
transactions.setQuantityBefore(Long.valueOf(quantity));// 变更前数量
transactions.setQuantityAfter(inventory.getQuantity());// 变更后数量
......@@ -175,10 +177,7 @@ public class InventoryServiceImpl implements IInventoryService
transactions.setTransactionTime(nowDate);
transactions.setOperatedBy(SystemUtils.getUserName());
int inventoryTransactions = insertInventoryTransactions.insertInventoryTransactions(transactions);
if (inventoryTransactions < 0) {
return -1;
}
insertInventoryTransactions.insertInventoryTransactions(transactions);
}
RefreshInventory(inventoryIds);
}
......
......@@ -11,7 +11,7 @@ import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.inventory.domain.InboundOrderItems;
import com.ruoyi.inventory.domain.InboundOrders;
import com.ruoyi.inventory.domain.vo.InboundTemplateVO;
import com.ruoyi.inventory.domain.vo.inboundVO.InboundTemplateVO;
import com.ruoyi.inventory.domain.vo.OutboundOrdersSummaryVO;
import com.ruoyi.inventory.domain.vo.OutboundTemplateVO;
import org.springframework.beans.factory.annotation.Autowired;
......
......@@ -4,6 +4,7 @@ import java.util.*;
import java.util.stream.Collectors;
import com.ruoyi.common.annotation.SerialExecution;
import com.ruoyi.common.config.WarehouseConfig;
import com.ruoyi.common.core.domain.entity.Materials;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
......@@ -28,6 +29,10 @@ import org.springframework.util.CollectionUtils;
/**
* 出库单主Service业务层处理
* 核心修正:
* 1. 库存匹配Key统一为 仓库ID_物料ID_库位ID_库存类型
* 2. 确保inventoryType完整参与匹配
* 3. 库存扣减后≤0时强制设置inventory_status=0
*
* @author ruoyi
* @date 2025-12-03
......@@ -54,6 +59,10 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
private MaterialsServiceImpl materialsService;
@Autowired
private StorageLocationsServiceImpl storageLocationsService;
@Autowired
private InventoryMapper inventoryMapper;
/**
* 查询出库单主
*
......@@ -63,7 +72,6 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
@Override
public OutboundOrders selectOutboundOrdersById(String id)
{
return outboundOrdersMapper.selectOutboundOrdersById(id);
}
......@@ -112,7 +120,6 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
outboundOrdersMapper.deleteOutboundOrderItemsByOrderId(outboundOrders.getId());
outboundOrderLogMapper.deleteOutboundOrderLogByOrdersId(outboundOrders.getId());
outboundOrders.setUpdateUserCode(SystemUtils.getUserName());
outboundOrders.setUpdateTime(DateUtils.getNowDate());
insertOutboundOrderItems(outboundOrders);
......@@ -147,42 +154,161 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
outboundOrdersMapper.deleteOutboundOrderItemsByOrderId(id);
return outboundOrdersMapper.deleteOutboundOrdersById(id);
}
@SerialExecution(group = "inventoryRefresh", fair = true)
@Override
public int ship(OutboundOrders outboundOrders) {
OutboundOrderItems outboundOrderItems1 = new OutboundOrderItems();
outboundOrderItems1.setOutboundOrderId(outboundOrders.getId());
outboundOrderItems1.setDivisor(null);
List<OutboundOrderItems> outboundOrderItems = outboundOrderItemsMapper.selectOutboundOrderItemsList(outboundOrderItems1);
List<OutboundOrderItems> outboundOrderItems2 = outboundOrderItems;
// 1. 查询当前出库单的所有明细
OutboundOrderItems query = new OutboundOrderItems();
query.setOutboundOrderId(outboundOrders.getId());
query.setDivisor(null);
List<OutboundOrderItems> outboundOrderItems = outboundOrderItemsMapper.selectOutboundOrderItemsList(query);
// 2. 更新明细和订单状态
OutboundOrderLog outboundOrderLog = new OutboundOrderLog();
String updateUser = SystemUtils.getUserName();
Date updateTime = DateUtils.getNowDate();
for (OutboundOrderItems item : outboundOrderItems) {
item.setItemStatus(3L);
item.setUpdateBy(updateUser);
item.setUpdateTime(updateTime);
outboundOrderItemsMapper.updateOutboundOrderItems(item);
outboundOrderLog.setId(item.getId());
outboundOrderLog.setItemStatus(item.getItemStatus());
outboundOrderLogMapper.updateOutboundOrderLog(outboundOrderLog);
}
for (OutboundOrderItems outboundOrderItem : outboundOrderItems) {
outboundOrders.setOrderStatus(2L);
outboundOrders.setUpdateTime(updateTime);
outboundOrders.setUpdateUserCode(updateUser);
outboundOrdersMapper.updateOutboundOrders(outboundOrders);
outboundOrderItem.setItemStatus(3l);
outboundOrderItemsMapper.updateOutboundOrderItems(outboundOrderItem);
// 3. 执行库存扣减(包含inventoryType匹配 + 0值状态更新)
this.deductInventory(outboundOrderItems, updateUser, updateTime);
outboundOrderLog.setId(outboundOrderItem.getId());
outboundOrderLog.setItemStatus(outboundOrderItem.getItemStatus());
outboundOrderLogMapper.updateOutboundOrderLog(outboundOrderLog);
return 1;
}
/**
* 核心库存扣减逻辑
* 1. 按 仓库ID_物料ID_库位ID_库存类型 匹配库存
* 2. 扣减后数量≤0时设置inventory_status=0
* @param outboundOrderItems 出库明细
* @param updateUser 操作人
* @param updateTime 操作时间
*/
private void deductInventory(List<OutboundOrderItems> outboundOrderItems, String updateUser, Date updateTime) {
if (CollectionUtils.isEmpty(outboundOrderItems)) {
return;
}
// 1. 预加载库存映射:Key=仓库ID_物料ID_库位ID_库存类型 Value=库存对象
Map<String, Inventory> inventoryMap = this.loadInventoryMap();
// 2. 构建扣减数量Map:Key=仓库ID_物料ID_库位ID_库存类型 Value=总扣减数量
Map<String, Long> deductQtyMap = this.buildDeductQtyMap(outboundOrderItems);
// 3. 遍历扣减Map,执行扣减+状态更新
List<Inventory> needUpdateList = new ArrayList<>();
for (Map.Entry<String, Long> entry : deductQtyMap.entrySet()) {
String key = entry.getKey();
Long deductQty = entry.getValue();
// 匹配库存
Inventory inventory = inventoryMap.get(key);
if (inventory == null) {
String[] keyParts = key.split("_");
String warehouseId = keyParts.length > 0 ? keyParts[0] : "";
String materialId = keyParts.length > 1 ? keyParts[1] : "";
String locationId = keyParts.length > 2 ? keyParts[2] : "";
String inventoryType = keyParts.length > 3 ? keyParts[3] : "";
throw new ServiceException(String.format(
"仓库[%s]物料[%s]库位[%s]库存类型[%s]的库存不存在,无法扣减",
warehouseId, materialId, locationId, inventoryType));
}
outboundOrders.setId(outboundOrders.getId());
outboundOrders.setOrderStatus(2l);
outboundOrders.setUpdateTime(DateUtils.getNowDate());
outboundOrders.setUpdateUserCode(SystemUtils.getUserName());
outboundOrdersMapper.updateOutboundOrders(outboundOrders);
// 执行数量扣减
Long currentQty = Optional.ofNullable(inventory.getQuantity()).orElse(0L);
Long newQty = currentQty - deductQty;
inventory.setQuantity(newQty);
inventoryService.ship(outboundOrderItems2);
return 1;
// 核心规则:扣减后数量≤0 → 状态置0
if (newQty <= 0) {
inventory.setInventoryStatus(0L);
}
// 补充审计字段
inventory.setUpdateBy(updateUser);
inventory.setUpdateTime(updateTime);
needUpdateList.add(inventory);
}
// 4. 批量更新库存
if (!needUpdateList.isEmpty()) {
inventoryMapper.batchUpdateInventory(needUpdateList);
// 刷新库存缓存
List<String> inventoryIds = needUpdateList.stream()
.map(Inventory::getId)
.distinct()
.collect(Collectors.toList());
inventoryService.RefreshInventory(inventoryIds);
}
}
/**
* 预加载库存Map
* Key规则:仓库ID_物料ID_库位ID_库存类型
*/
private Map<String, Inventory> loadInventoryMap() {
Inventory query = new Inventory();
query.setInventoryStatus(1L);
query.setIsUsed(1L);
List<Inventory> inventoryList = inventoryService.selectInventoryList(query);
if (CollectionUtils.isEmpty(inventoryList)) {
return Collections.emptyMap();
}
Map<String, Inventory> inventoryMap = new HashMap<>();
for (Inventory inv : inventoryList) {
String key = String.join("_",
Optional.ofNullable(inv.getWarehousesId()).orElse(""),
Optional.ofNullable(inv.getMaterialId()).orElse(""),
Optional.ofNullable(inv.getLocationId()).orElse(""),
Optional.ofNullable(inv.getInventoryType()).map(String::valueOf).orElse("")
);
// 重复key保留第一个(避免覆盖)
if (!inventoryMap.containsKey(key)) {
inventoryMap.put(key, inv);
}
}
return inventoryMap;
}
/**
* 构建扣减数量Map
* Key规则:仓库ID_物料ID_库位ID_库存类型(和库存Map完全对齐)
*/
private Map<String, Long> buildDeductQtyMap(List<OutboundOrderItems> items) {
Map<String, Long> deductQtyMap = new HashMap<>();
for (OutboundOrderItems item : items) {
String key = String.join("_",
Optional.ofNullable(item.getWarehouseId()).orElse(""),
Optional.ofNullable(item.getMaterialId()).orElse(""),
Optional.ofNullable(item.getLocationId()).orElse(""),
Optional.ofNullable(item.getInventoryType()).map(String::valueOf).orElse("")
);
// 累加扣减数量
Long qty = Optional.ofNullable(item.getActualQuantity()).orElse(0L);
deductQtyMap.put(key, deductQtyMap.getOrDefault(key, 0L) + qty);
}
return deductQtyMap;
}
@Override
public List<Map<String,String>> outboundOrdersTopTenByQuantity() {
List<Map<String,String>> o= outboundOrdersMapper.SelectOutboundOrdersMaterialsTopTenByQuantity();
return o;
return outboundOrdersMapper.SelectOutboundOrdersMaterialsTopTenByQuantity();
}
@Override
......@@ -192,7 +318,6 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
@Override
public String outboundOrdersCount() {
return outboundOrdersMapper.outboundOrdersCount();
}
......@@ -204,48 +329,41 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
public void insertOutboundOrderItems(OutboundOrders outboundOrders) {
List<OutboundOrderItems> outboundOrderItemsList = outboundOrders.getOutboundOrderItemsList();
String id = outboundOrders.getId();
// 1. 先做空列表校验(提前返回,避免无效逻辑)
if (outboundOrderItemsList == null || outboundOrderItemsList.isEmpty()) {
if (CollectionUtils.isEmpty(outboundOrderItemsList)) {
return;
}
// 2. 库存校验:失败时抛异常(核心修正:! 取反 + 异常抛出后代码立即终止)
// 库存校验:失败时抛异常
boolean isValid = inventoryService.inventoryLockValidation(outboundOrderItemsList);
if (!isValid) { // 校验失败(返回false)时抛异常
throw new RuntimeException("库存被修改请重新确认"); // 抛异常后,方法立即停止运行
if (!isValid) {
throw new RuntimeException("库存被修改请重新确认");
}
// 2. 为明细设置订单ID和主键ID
// 为明细设置订单ID和主键ID
for (OutboundOrderItems items : outboundOrderItemsList) {
items.setOutboundOrderId(id);
items.setOrderId(outboundOrders.getOrderId());
// 生成无横线的UUID作为主键
items.setId(UUID.randomUUID().toString().replace("-", ""));
}
// 3. 批量插入出库单明细
// 批量插入出库单明细
outboundOrdersMapper.batchOutboundOrderItems(outboundOrderItemsList);
// 4. 正确拷贝明细列表到日志列表(修复核心错误:遍历逐个拷贝)
// 拷贝明细到日志列表
List<String> inventoryIds = new ArrayList<>();
List<OutboundOrderLog> outboundOrderLogs = new ArrayList<>();
for (OutboundOrderItems items : outboundOrderItemsList) {
OutboundOrderLog log = new OutboundOrderLog();
BeanUtils.copyProperties(items, log); // 单个对象属性拷贝
BeanUtils.copyProperties(items, log);
log.setOrderId(items.getOutboundOrderId());
outboundOrderLogs.add(log);
inventoryIds.add(log.getInventoryId());
deleteOutboundOrdersById(items.getId());
}
// 5. 非空校验后插入日志(避免空列表触发SQL语法错误)
// 插入日志 + 刷新库存
if (!outboundOrderLogs.isEmpty()) {
outboundOrderLogMapper.batchOutboundOrderLog(outboundOrderLogs);
}
// 7. 非空校验后刷新库存
if (!inventoryIds.isEmpty()) {
inventoryService.RefreshInventory(inventoryIds);
}
......@@ -253,13 +371,13 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
@Transactional(rollbackFor = Exception.class)
@Override
public String importOutboundOrders(List<OutboundTemplateVO> inboundOrdersList, Boolean isUpdateSupport, String operName,Integer orderType) {
// 1. 基础空值校验(完全保留你的代码)
public String importOutboundOrders(List<OutboundTemplateVO> inboundOrdersList, Boolean isUpdateSupport, String operName, Integer orderType) {
// 1. 基础空值校验
if (CollectionUtils.isEmpty(inboundOrdersList)) {
throw new ServiceException("导入数据不能为空!");
}
// 2. 初始化变量(完全保留你的代码)
// 2. 初始化变量
int totalMainSuccess = 0;
int totalMainFailure = 0;
int totalItemSuccess = 0;
......@@ -274,12 +392,19 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
Map<String, List<OutboundOrderItems>> validItemMap = new HashMap<>();
boolean hasValidateError = false;
// 3. 按入库单号分组(完全保留你的代码)
// 3. 预加载映射缓存
Map<String, String> sapToMaterialIdMap = loadSapToMaterialIdMap();
Map<String, String> locationNameToIdMap = loadLocationNameToIdMap();
Map<String, String> warehouseNameToIdMap = loadWarehouseNameToIdMap();
Map<String, String> ownerNameToIdMap = loadOwnerNameToIdMap();
Map<String, AbstractMap.SimpleEntry<String, Long>> inventoryTOIdMap = loadInventoryTOIdMap();
// 4. 按入库单号分组
Map<String, List<OutboundTemplateVO>> orderGroupMap = inboundOrdersList.stream()
.filter(vo -> StringUtils.isNotBlank(vo.getOrderId()))
.collect(Collectors.groupingBy(OutboundTemplateVO::getOrderId));
// 4. 第一步:仅验证所有数据(完全保留你的代码)
// 5. 数据验证
for (Map.Entry<String, List<OutboundTemplateVO>> entry : orderGroupMap.entrySet()) {
String orderId = entry.getKey();
List<OutboundTemplateVO> voList = entry.getValue();
......@@ -288,12 +413,12 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
try {
OutboundTemplateVO firstVO = voList.get(0);
// 检查出库单是否已存在
OutboundOrders outboundOrdersQuery = new OutboundOrders();
outboundOrdersQuery.setOrderId(orderId);
List<OutboundOrders> existMains = outboundOrdersMapper.selectOutboundOrdersList(outboundOrdersQuery);
if (CollectionUtils.isEmpty(existMains) && existMains.size() > 1) {
if (existMains != null && existMains.size() > 1) {
throw new ServiceException(String.format("入库单号【%s】存在多条重复主表数据", orderId));
}
......@@ -305,45 +430,41 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
mainDO = existMain;
BeanUtils.copyProperties(firstVO, mainDO, "id", "createBy", "createTime");
mainDO.setUpdateBy(operId);
mainDO.setWarehouseId(WarehouseConfig.DEFAULT_WAREHOUSE_ID);
mainDO.setUpdateTime(now);
mainDO.setUpdateUserCode(operId);
} else {
mainDO = new OutboundOrders();
com.ruoyi.common.utils.bean.BeanUtils.copyProperties(firstVO, mainDO,
BeanUtils.copyProperties(firstVO, mainDO,
"sapNo", "materialName", "plannedQuantity", "actualQuantity",
"plannedPackages", "materialUnit", "materialRemark");
if (StringUtils.isNotBlank(firstVO.getOwnerName())) {
Owners ownerQuery = new Owners();
ownerQuery.setOwnerName(firstVO.getOwnerName());
List<Owners> owners = ownersService.selectOwnersList(ownerQuery);
if (CollectionUtils.isEmpty(owners)) {
// 货主校验
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】",
firstVO.getOwnerName(), orderId));
ownerName, orderId));
}
if (firstVO.getOwnerId() != null) {
boolean isMatch = owners.stream().anyMatch(o -> firstVO.getOwnerId().equals(o.getId()));
if (!isMatch) {
throw new ServiceException(String.format("入库单号【%s】的业主ID【%s】与业主名称【%s】不匹配",
orderId, firstVO.getOwnerId(), firstVO.getOwnerName()));
}
mainDO.setOwnerId(firstVO.getOwnerId());
} else {
mainDO.setOwnerId(owners.get(0).getId());
if (ownerId != null && !ownerId.equals(mappedOwnerId)) {
throw new ServiceException(String.format("入库单号【%s】的业主ID【%s】与业主名称【%s】不匹配",
orderId, ownerId, ownerName));
}
} else if (firstVO.getOwnerId() != null) {
Owners ownerById = ownersService.selectOwnersById(firstVO.getOwnerId());
mainDO.setOwnerId(mappedOwnerId);
} else if (ownerId != null) {
Owners ownerById = ownersService.selectOwnersById(ownerId);
if (ownerById == null) {
throw new ServiceException(String.format("入库单号【%s】的业主ID【%s】不存在",
orderId, firstVO.getOwnerId()));
orderId, ownerId));
}
mainDO.setOwnerId(firstVO.getOwnerId());
mainDO.setOwnerId(ownerId);
} else {
throw new ServiceException(String.format("入库单号【%s】的业主名称/ID不能为空", orderId));
}
// 填充主表必填字段
mainDO.setId(UUID.randomUUID().toString());
mainDO.setOrderId(orderId);
mainDO.setOrderStatus(2L);
......@@ -357,14 +478,16 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
mainDO.setSortNo(Optional.ofNullable(mainDO.getSortNo()).orElse(0L));
}
// 明细校验
for (int i = 0; i < voList.size(); i++) {
OutboundTemplateVO vo = voList.get(i);
int lineNo = i + 1;
OutboundOrderItems itemDO = new OutboundOrderItems();
com.ruoyi.common.utils.bean.BeanUtils.copyProperties(vo, itemDO,
BeanUtils.copyProperties(vo, itemDO,
"orderId", "systemNo", "orderTypeId", "batchId");
// 填充明细必填字段
itemDO.setId(UUID.randomUUID().toString());
itemDO.setOrderId(orderId);
itemDO.setBatchCode(Optional.ofNullable(mainDO.getBatchCode()).orElse(""));
......@@ -374,54 +497,53 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
itemDO.setCreateUserCode(operId);
itemDO.setSortNo(0L);
if (StringUtils.isBlank(vo.getSapNo())) {
// 物料SAP校验
String sapNo = vo.getSapNo() != null ? vo.getSapNo().trim() : "";
if (StringUtils.isBlank(sapNo)) {
throw new ServiceException(String.format("入库单号【%s】第%d条明细的物料SAP号不能为空",
orderId, lineNo));
}
Materials materialsQuery = new Materials();
materialsQuery.setSapNo(vo.getSapNo());
List<Materials> materialsList = materialsService.selectMaterialsList(materialsQuery);
if (CollectionUtils.isEmpty(materialsList)) {
String materialId = sapToMaterialIdMap.get(sapNo);
if (StringUtils.isBlank(materialId)) {
throw new ServiceException(String.format("入库单号【%s】第%d条明细的SAP号【%s】对应的物料不存在",
orderId, lineNo, vo.getSapNo()));
orderId, lineNo, sapNo));
}
itemDO.setMaterialId(materialsList.get(0).getId());
itemDO.setMaterialId(materialId);
if (StringUtils.isBlank(vo.getLocationName())) {
// 库位校验
String locationName = vo.getLocationName() != null ? vo.getLocationName().trim() : "";
if (StringUtils.isBlank(locationName)) {
throw new ServiceException(String.format("入库单号【%s】第%d条明细的库位名称不能为空",
orderId, lineNo));
}
StorageLocations storageQuery = new StorageLocations();
storageQuery.setLocationCode(vo.getLocationName());
List<StorageLocations> storageList = storageLocationsService.selectStorageLocationsList(storageQuery);
if (CollectionUtils.isEmpty(storageList)) {
String locationId = locationNameToIdMap.get(locationName);
if (StringUtils.isBlank(locationId)) {
throw new ServiceException(String.format("入库单号【%s】第%d条明细的库位【%s】不存在",
orderId, lineNo, vo.getLocationName()));
orderId, lineNo, locationName));
}
itemDO.setLocationId(storageList.get(0).getId());
Warehouses warehouses = new Warehouses();
warehouses.setWarehousesName(vo.getWarehouseName());
List<Warehouses> warehousesList = warehousesService.selectWarehousesList(warehouses);
if (CollectionUtils.isEmpty(warehousesList)) {
throw new ServiceException(String.format("系统未配置仓库,无法导入入库单号【%s】的第%d条明细",
orderId, lineNo));
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(warehousesList.get(0).getId());
Inventory itemsList = new Inventory();
itemsList.setWarehousesId(itemDO.getWarehouseId());
itemsList.setMaterialId(itemDO.getMaterialId());
itemsList.setLocationId(itemDO.getLocationId());
List<Inventory> inventory = inventoryService.selectInventoryList(itemsList);
if (CollectionUtils.isEmpty(inventory)) {
itemDO.setWarehouseId(warehouseId);
// 库存校验(包含inventoryType)
String inventoryTypeStr = Optional.ofNullable(orderType).map(String::valueOf).orElse("");
String inventoryMatchKey = String.join("_", warehouseId, materialId, locationId, inventoryTypeStr);
AbstractMap.SimpleEntry<String, Long> inventoryEntry = inventoryTOIdMap.get(inventoryMatchKey);
if (inventoryEntry == null) {
throw new ServiceException(String.format(
"入库单号【%s】第%d条明细:仓库【%s】+物料【%s】+库位【%s】组合的库存记录不存在",
orderId, lineNo, itemDO.getWarehouseName(),firstVO.getMaterialName(), firstVO.getLocationName()));
"入库单号【%s】第%d条明细:仓库【%s】+物料【%s】+库位【%s】+库存类型【%s】组合的库存记录不存在",
orderId, lineNo, vo.getWarehouseName(), vo.getMaterialName(), vo.getLocationName(), inventoryTypeStr));
}
itemDO.setInventoryId(inventory.get(0).getId());
itemDO.setItemStatus(3l);
itemDO.setInventoryId(inventoryEntry.getKey());
itemDO.setInventoryType(inventoryTypeStr); // 填充库存类型
itemDO.setItemStatus(3L);
itemDOList.add(itemDO);
}
......@@ -436,26 +558,21 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
}
// 5. 有验证失败直接抛异常(完全保留你的代码)
// 6. 有验证失败直接抛异常
if (hasValidateError) {
throw new ServiceException(String.format("验证失败,导入终止!失败详情:%s", failureMsg.toString()));
}
// 6. 验证全通过:统一执行入库/更新操作(完全保留你的代码)
// 7. 执行入库/更新操作
for (Map.Entry<String, OutboundOrders> entry : validMainMap.entrySet()) {
String orderId = entry.getKey();
OutboundOrders mainDO = entry.getValue();
List<OutboundOrderItems> itemDOList = validItemMap.get(orderId);
if (mainDO != null) {
outboundOrdersMapper.insertOutboundOrders(mainDO);
totalMainSuccess++;
successMsg.append(String.format("入库单号【%s】已新增;\n", orderId));
} else {
outboundOrdersMapper.updateOutboundOrders(mainDO);
totalMainSuccess++;
successMsg.append(String.format("入库单号【%s】已更新;\n", orderId));
}
if (!CollectionUtils.isEmpty(itemDOList)) {
int itemSuccess = outboundOrderItemsMapper.batchInsertOutboundOrderItems(itemDOList);
......@@ -470,10 +587,8 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
}
// ========== 仅新增这一段代码(核心:事务提交后执行自定义逻辑) ==========
// 8. 事务提交后执行库存扣减
if (TransactionSynchronizationManager.isSynchronizationActive()) {
// 保存当前需要的变量,用于事务提交后执行
Map<String, OutboundOrders> finalValidMainMap = new HashMap<>(validMainMap);
Map<String, List<OutboundOrderItems>> finalValidItemMap = new HashMap<>(validItemMap);
String finalOperId = operId;
Date finalNow = now;
......@@ -481,17 +596,18 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
// 事务提交后,调用你原来的executeCustomLogic方法
executeCustomLogic(finalValidMainMap, finalValidItemMap, finalOperId, finalNow);
for (List<OutboundOrderItems> itemList : finalValidItemMap.values()) {
deductInventory(itemList, finalOperId, finalNow);
}
}
});
} else {
// 无事务时,直接执行(和你原有逻辑一致)
executeCustomLogic(validMainMap, validItemMap, operId, now);
for (List<OutboundOrderItems> itemList : validItemMap.values()) {
deductInventory(itemList, operId, now);
}
}
// ========== 新增代码结束 ==========
// 8. 结果汇总(完全保留你的代码)
// 9. 结果汇总
if (totalMainFailure > 0 || totalItemFailure > 0) {
String finalFailureMsg = String.format(
"导入结果:成功新增/更新%d个入库单,失败%d个;成功导入%d条明细,失败%d条。失败详情:%s",
......@@ -507,59 +623,111 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
}
// 仅修改这个方法中【设置日志ID】的一行代码,其余完全保留
private void executeCustomLogic(Map<String, OutboundOrders> validMainMap,
Map<String, List<OutboundOrderItems>> validItemMap,
String operId, Date now) {
for (String orderId : validMainMap.keySet()) {
OutboundOrders mainDO = validMainMap.get(orderId);
List<OutboundOrderItems> itemList = validItemMap.get(orderId);
System.out.println(String.format("订单【%s】导入成功,主表ID:%s,明细数:%d",
orderId, mainDO.getId(), itemList.size()));
List<OutboundOrderLog> outboundOrderLogs = new ArrayList<>();
for (OutboundOrderItems item : itemList) {
OutboundOrderLog outboundOrderLog = new OutboundOrderLog();
// ========== 唯一改动:设置日志ID = 明细ID ==========
outboundOrderLog.setId(item.getId());
outboundOrderLog.setOrderId(item.getOutboundOrderId());
BeanUtils.copyProperties(item, outboundOrderLog);
outboundOrderLogs.add(outboundOrderLog);
}
outboundOrderLogMapper.batchOutboundOrderLog(outboundOrderLogs);
ship(itemList);
// ========== 预加载映射辅助方法 ==========
private Map<String, String> loadSapToMaterialIdMap() {
List<Materials> materialsList = materialsService.selectMaterialsList(new Materials());
if (CollectionUtils.isEmpty(materialsList)) {
return Collections.emptyMap();
}
return materialsList.stream()
.filter(m -> StringUtils.isNotBlank(m.getSapNo()))
.collect(Collectors.toMap(
m -> m.getSapNo().trim(),
Materials::getId,
(k1, k2) -> k1
));
}
public int ship(List<OutboundOrderItems> outboundOrderItems)
{
if (!outboundOrderItems.isEmpty()) {
List<String> inventoryIds = new ArrayList<>(); // 手动收集inventoryId
for (OutboundOrderItems outboundOrderItem : outboundOrderItems) {
// 直接用明细的inventoryId,不查日志!
String inventoryId = outboundOrderItem.getInventoryId();
if (StringUtils.isBlank(inventoryId)) {
throw new ServiceException("明细ID【" + outboundOrderItem.getId() + "】的库存ID为空,无法扣减");
}
Inventory inventory = inventoryService.selectInventoryById(inventoryId);
if (inventory == null) {
throw new ServiceException("库存ID【" + inventoryId + "】不存在,无法扣减");
}
private Map<String, String> loadLocationNameToIdMap() {
StorageLocations query = new StorageLocations();
query.setIsUsed(1L);
List<StorageLocations> locationList = storageLocationsService.selectStorageLocationsList(query);
if (CollectionUtils.isEmpty(locationList)) {
return Collections.emptyMap();
}
return locationList.stream()
.filter(l -> StringUtils.isNotBlank(l.getLocationCode()))
.collect(Collectors.toMap(
l -> l.getLocationCode().trim(),
StorageLocations::getId,
(k1, k2) -> k1
));
}
// 扣减库存
inventory.setQuantity(inventory.getQuantity() - outboundOrderItem.getActualQuantity());
if (inventory.getQuantity() == 0) {
inventory.setInventoryStatus(0L);
}
inventoryService.updateInventory(inventory);
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
));
}
inventoryIds.add(inventoryId); // 收集库存ID用于刷新
}
inventoryService.RefreshInventory(inventoryIds);
private Map<String, String> loadOwnerNameToIdMap() {
List<Owners> ownerList = ownersService.selectOwnersList(new Owners());
if (CollectionUtils.isEmpty(ownerList)) {
return Collections.emptyMap();
}
return 1;
return ownerList.stream()
.filter(o -> StringUtils.isNotBlank(o.getOwnerName()))
.collect(Collectors.toMap(
o -> o.getOwnerName().trim(),
Owners::getId,
(k1, k2) -> k1
));
}
}
private Map<String, AbstractMap.SimpleEntry<String, Long>> loadInventoryTOIdMap() {
Inventory inventory = new Inventory();
inventory.setInventoryStatus(1L);
inventory.setIsUsed(1L);
List<Inventory> inventoryList = inventoryService.selectInventoryList(inventory);
if (CollectionUtils.isEmpty(inventoryList)) {
return Collections.emptyMap();
}
Map<String, AbstractMap.SimpleEntry<String, Long>> emptyLocationMap = inventoryList.stream()
.filter(inv -> StringUtils.isNotBlank(inv.getWarehousesId())
&& StringUtils.isNotBlank(inv.getMaterialId())
&& inv.getInventoryType() != null
&& StringUtils.isNotBlank(inv.getId())
&& StringUtils.isBlank(inv.getLocationId()))
.collect(Collectors.toMap(
inv -> String.join("_",
inv.getWarehousesId().trim(),
inv.getMaterialId().trim(),
"", inv.getInventoryType().toString()),
inv -> new AbstractMap.SimpleEntry<>(inv.getId().trim(), Optional.ofNullable(inv.getQuantity()).orElse(0L)),
(k1, k2) -> k1,
HashMap::new
));
Map<String, AbstractMap.SimpleEntry<String, Long>> nonEmptyLocationMap = inventoryList.stream()
.filter(inv -> StringUtils.isNotBlank(inv.getWarehousesId())
&& StringUtils.isNotBlank(inv.getMaterialId())
&& StringUtils.isNotBlank(inv.getLocationId())
&& StringUtils.isNotBlank(inv.getId())
&& inv.getInventoryType() != null)
.collect(Collectors.toMap(
inv -> String.join("_",
inv.getWarehousesId().trim(),
inv.getMaterialId().trim(),
inv.getLocationId().trim(),
inv.getInventoryType().toString()),
inv -> new AbstractMap.SimpleEntry<>(inv.getId().trim(), Optional.ofNullable(inv.getQuantity()).orElse(0L)),
(k1, k2) -> k1,
HashMap::new
));
emptyLocationMap.putAll(nonEmptyLocationMap);
return emptyLocationMap;
}
}
\ No newline at end of file
......@@ -5,6 +5,7 @@ import java.util.Date;
import java.util.List;
import java.util.Map;
import com.ruoyi.common.config.WarehouseConfig;
import com.ruoyi.common.core.domain.entity.Materials;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
......@@ -50,16 +51,20 @@ public class StorageLocationsServiceImpl implements IStorageLocationsService
public StorageLocations selectStorageLocationsById(String id)
{
StorageLocations storageLocations = storageLocationsMapper.selectStorageLocationsById(id);
String[] AllowedCategoryIds = storageLocations.getAllowedCategoryIds().split(",");
String AllowedCategoryName = "";
for (String AllowedCategoryId : AllowedCategoryIds) {
Materials materials = materialsMapper.selectMaterialsById(AllowedCategoryId);
if (materials != null && materials.getMaterialName() != null) {
String categoryName = materials.getMaterialName().trim(); // 去除首尾空格
if (AllowedCategoryName != "") {
AllowedCategoryName += ",";
if (storageLocations.getAllowedCategoryIds() != null) {
String[] AllowedCategoryIds = storageLocations.getAllowedCategoryIds().split(",");
if (AllowedCategoryIds != null && AllowedCategoryIds.length > 0) {
for (String AllowedCategoryId : AllowedCategoryIds) {
Materials materials = materialsMapper.selectMaterialsById(AllowedCategoryId);
if (materials != null && materials.getMaterialName() != null) {
String categoryName = materials.getMaterialName().trim(); // 去除首尾空格
if (AllowedCategoryName != "") {
AllowedCategoryName += ",";
}
AllowedCategoryName += categoryName;
}
}
AllowedCategoryName += categoryName;
}
}
storageLocations.setAllowedCategoryNames(AllowedCategoryName);
......@@ -260,6 +265,7 @@ public class StorageLocationsServiceImpl implements IStorageLocationsService
StorageLocations storageLocations = new StorageLocations();
// 拷贝基础属性
BeanUtils.copyProperties(templateVO, storageLocations);
storageLocations.setLocationName(templateVO.getLocationCode());
// 字段类型转换与赋值
storageLocations.setId(UUID.randomUUID().toString());
......@@ -274,7 +280,8 @@ public class StorageLocationsServiceImpl implements IStorageLocationsService
storageLocations.setCreateBy(operId);
storageLocations.setCreateTime(now);
storageLocations.setCreateUserCode(operId);
storageLocations.setWarehousesId("572ba484-199c-45d9-9735-610928ed5c70");
storageLocations.setWarehousesCode("固定仓库");
storageLocations.setWarehousesId(WarehouseConfig.DEFAULT_WAREHOUSE_ID);
// 设置默认值
if (storageLocations.getIsUsed() == null) {
......
......@@ -231,8 +231,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</foreach>
</insert>
<!-- 统计入库次数-->
<select id="countInboundOrders" resultType="int" parameterType="String">
select count(id)
......@@ -247,27 +245,36 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result column="total_money" property="totalMoney" jdbcType="DECIMAL"/>
</resultMap>
<select id="countInboundMaterialQuantity" resultMap="InboundMaterialTotalResultMap" parameterType="String">
select m.material_name,
COALESCE(SUM(ioi.actual_quantity), 0) as total_quantity
from materials as m
left join inbound_order_items as ioi
on m.id = ioi.material_id
left join inbound_orders as io
on io.id = ioi.inbound_order_id
and io.order_status = 2
group by m.id, m.material_name
order by total_quantity desc
SELECT t.material_name, t.total_quantity
FROM (
SELECT m.material_name,
COALESCE(SUM(ioi.actual_quantity), 0) as total_quantity
FROM materials as m
LEFT JOIN inbound_order_items as ioi ON m.id = ioi.material_id
LEFT JOIN inbound_orders as io ON io.id = ioi.inbound_order_id AND io.order_status = 2
WHERE m.is_used = 1 AND m.is_active = 1
GROUP BY m.id, m.material_name
ORDER BY total_quantity DESC
LIMIT 10
) t
ORDER BY t.total_quantity ASC;
</select>
<select id="countInboundMaterialMoney" resultMap="InboundMaterialTotalResultMap" parameterType="String">
select m.material_name,
COALESCE(sum(ioi.actual_quantity * ioi.unit_price), 0) as total_money
from materials as m
left join inbound_order_items as ioi
on m.id = ioi.material_id
left join inbound_orders as io
on io.id = ioi.inbound_order_id
and io.order_status = 2
group by m.id, m.material_name
order by total_money desc
select t.material_name,t.total_money
from(
select m.material_name,
COALESCE(sum(ioi.actual_quantity * ioi.unit_price), 0) as total_money
from materials as m
left join inbound_order_items as ioi
on m.id = ioi.material_id
left join inbound_orders as io
on io.id = ioi.inbound_order_id
and io.order_status = 2
where m.is_used = 1 and m.is_active = 1
group by m.id
order by total_money desc
limit 10
) t
order by total_money asc
</select>
</mapper>
\ No newline at end of file
......@@ -10,6 +10,7 @@
<result property="orderId" column="order_id" />
<result property="materialId" column="material_id" />
<result property="materialName" column="material_name"/>
<result property="hazardId" column="hazard_id" />
<result property="batchId" column="batch_id" />
<result property="warehousesCode" column="warehouses_code" />
<result property="warehousesName" column="warehouses_name"/>
......@@ -401,6 +402,7 @@
i.order_id,
i.material_id,
m.material_name,
m.hazard_id,
i.batch_id,
i.location_id,
sl.location_name,
......@@ -563,6 +565,9 @@ and inventory_status = '1'
and i.unit_price > 0
and i.last_inbound_time >= DATE_FORMAT(CURDATE(), '%Y-%m-01')
and i.last_inbound_time &lt; DATE_FORMAT(DATE_ADD(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01')
where
m.is_used =1
and m.is_active =1
group by m.id, m.material_name
order by value desc, m.material_name asc
</select>
......@@ -577,6 +582,9 @@ and inventory_status = '1'
and i.is_used = 1
and i.last_inbound_time >= DATE_FORMAT(CURDATE(), '%Y-%m-01')
and i.last_inbound_time &lt; DATE_FORMAT(DATE_ADD(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01')
where
m.is_used =1
and m.is_active =1
group by m.id, m.material_name
order by value desc, m.material_name asc
</select>
......
......@@ -87,7 +87,8 @@ 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>
<if test="unitPrice != null">unit_price,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">#{id},</if>
<if test="transactionType != null">#{transactionType},</if>
......@@ -111,6 +112,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>
<if test="unitPrice != null">#{unitPrice},</if>
</trim>
</insert>
......
......@@ -8,6 +8,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="id" column="id" />
<result property="materialCode" column="material_code" />
<result property="materialName" column="material_name" />
<result property="materialEname" column="material_ename" />
<result property="sapNo" column="sap_no" />
<result property="tsCode" column="ts_code" />
<result property="categoryCode" column="category_code" />
......@@ -36,7 +37,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap>
<sql id="selectMaterialsVo">
select id, material_code, material_name, sap_no, ts_code, category_code, hazard_id, specification, material_unit, unit_weight, package_weight, total_weight, volume, shelf_life_days, storage_temperature, special_requirements, is_batch_managed, is_serial_managed, min_stock_level, max_stock_level, is_used, is_active, risk_level, sort_no, create_time, create_user_code, update_time, update_user_code from materials
select id, material_code, material_name,material_ename, sap_no, ts_code, category_code, hazard_id, specification, material_unit, unit_weight, package_weight, total_weight, volume, shelf_life_days, storage_temperature, special_requirements, is_batch_managed, is_serial_managed, min_stock_level, max_stock_level, is_used, is_active, risk_level, sort_no, create_time, create_user_code, update_time, update_user_code from materials
</sql>
<select id="selectMaterialsList" parameterType="Materials" resultMap="MaterialsResult">
......@@ -101,6 +102,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="id != null">id,</if>
<if test="materialCode != null">material_code,</if>
<if test="materialName != null">material_name,</if>
<if test="materialEname != null">material_ename,</if>
<if test="sapNo != null">sap_no,</if>
<if test="tsCode != null">ts_code,</if>
<if test="categoryCode != null">category_code,</if>
......@@ -131,6 +133,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="id != null">#{id},</if>
<if test="materialCode != null">#{materialCode},</if>
<if test="materialName != null">#{materialName},</if>
<if test="materialEname != null">#{materialEname},</if>
<if test="sapNo != null">#{sapNo},</if>
<if test="tsCode != null">#{tsCode},</if>
<if test="categoryCode != null">#{categoryCode},</if>
......@@ -164,6 +167,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<trim prefix="SET" suffixOverrides=",">
<if test="materialCode != null">material_code = #{materialCode},</if>
<if test="materialName != null">material_name = #{materialName},</if>
<if test="materialEname != null">material_ename = #{materialEname},</if>
<if test="sapNo != null">sap_no = #{sapNo},</if>
<if test="tsCode != null">ts_code = #{tsCode},</if>
<if test="categoryCode != null">category_code = #{categoryCode},</if>
......
......@@ -315,6 +315,9 @@
and ooi.is_used = 1
and ooi.shipped_at >= DATE_FORMAT(CURDATE(), '%Y-%m-01 00:00:00')
and ooi.shipped_at &lt; DATE_FORMAT(DATE_ADD(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01 00:00:00')
where
m.is_used =1
and m.is_active =1
group by m.id, m.material_name
order by value desc
limit 10;
......@@ -330,6 +333,9 @@
and ooi.is_used = 1
and ooi.shipped_at >= DATE_FORMAT(CURDATE(), '%Y-%m-01 00:00:00')
and ooi.shipped_at &lt; DATE_FORMAT(DATE_ADD(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01 00:00:00')
where
m.is_used =1
and m.is_active =1
group by m.id, m.material_name
order by value desc
limit 10;
......@@ -338,4 +344,15 @@
select count(*) from outbound_orders where is_used = 1 and order_status=2 and inbound_date &gt;= DATE_FORMAT(CURDATE(), '%Y-%m-01')
and inbound_date &lt; DATE_FORMAT(DATE_ADD(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01')
</select>
<update id="batchUpdateInventory">
<foreach collection="list" item="item" separator=";">
UPDATE inventory
SET quantity = #{item.quantity},
inventory_status = #{item.inventoryStatus},
update_by = #{item.updateBy},
update_time = #{item.updateTime}
WHERE id = #{item.id}
</foreach>
</update>
</mapper>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论