Commit 59df6518 by zhangtw

入库导入

parent 02e541a7
......@@ -35,6 +35,15 @@
<el-button
type="warning"
plain
icon="el-icon-upload"
size="medium"
@click="handleImport"
v-hasPermi="['inventory:inbound:import']"
>导入</el-button>
<el-button
type="warning"
plain
icon="el-icon-download"
size="medium"
@click="handleExport"
......@@ -292,7 +301,7 @@
ref="inboundItemsRef"
v-model="form.inboundOrderItemsList"
:embedded="true"
:order-id="orderId"
:inboundOrderId="inboundOrderId"
:show-search-form="false"
:show-pagination="false"
:show-toolbar="true"
......@@ -356,7 +365,7 @@
ref="detailItemsRef"
v-model="detailForm.inboundOrderItemsList"
:embedded="true"
:order-id="detailForm.orderId"
:inboundOrderId="detailForm.inboundOrderId"
:isEditable="isEditable"
:show-search-form="false"
:show-pagination="false"
......@@ -386,6 +395,16 @@
<el-button type="primary" @click="confirmMaterialSelect">确定</el-button>
</div>
</el-dialog>
<!-- 导入弹窗 -->
<import-excel
ref="import"
title="导入"
import-url="/inventory/inbound/import"
template-url="/inventory/inbound/importTemplate"
template-name="入库单导入模板"
@success="getList"
/>
</div>
</template>
......@@ -397,6 +416,7 @@ import InboundItems from "@/views/inventory/inbound_items/index.vue"
import PageTitle from "@/components/PageTitle" // 引入字典页面的标题组件
import PageWrapperSearch from "@/components/Search/PageWrapperSearch" // 引入搜索包装组件
import MaterialSelector from "@/views/inventory/materials/materialsSeletor.vue";
import ImportExcel from "@/components/ImportExcel/index"
export default {
name: "Inbound",
......@@ -404,7 +424,8 @@ export default {
InboundItems,
PageTitle,
PageWrapperSearch,
MaterialSelector
MaterialSelector,
ImportExcel
},
data() {
return {
......@@ -431,7 +452,7 @@ export default {
detailOpen: false,
detailForm: {}, // 新增详情表单数据
// 组件监听修改时的orderId变化
orderId: null,
inboundOrderId: null,
// 选中的明细项
selectedItems: [],
// 遮罩层
......@@ -588,11 +609,10 @@ export default {
const id = row.id
getInbound(id).then(response => {
this.form = response.data
this.orderId = response.data.orderId
// 加载明细数据
if (this.$refs.inboundItemsRef) {
this.$refs.inboundItemsRef.loadRelatedData(this.orderId)
}
this.inboundOrderId = id
this.$nextTick(() => {
})
this.open = true
this.title = "修改入库单"
})
......@@ -600,14 +620,12 @@ export default {
/** 查看详情 */
handleDetail(row) {
this.reset()
const id = row.id || this.ids
const id = row.id
this.isEditable = false
getInbound(id).then(response => {
this.detailForm = response.data
// 加载明细数据到详情
if (this.$refs.detailItemsRef) {
this.$refs.detailItemsRef.loadRelatedData(this.detailForm.orderId)
}
this.detailForm = response.data
this.detailForm.inboundOrderId = id
this.detailOpen = true
})
},
......@@ -651,7 +669,6 @@ export default {
});
}
});
console.log(this.form.inboundOrderItemsList)
this.materialSelectOpen = false;
this.$refs.inboundItemsRef.handleAddItem(this.selectedMaterials)
this.$message.success(`成功添加 ${this.selectedMaterials.length} 个物料`);
......@@ -797,7 +814,10 @@ export default {
this.$refs.form.resetFields()
}
},
/** 导入按钮操作 */
handleImport() {
this.$refs.import.show()
},
/** 处理明细项选择变化 */
handleItemsSelectionChange(selection) {
this.selectedItems = selection
......
......@@ -82,7 +82,7 @@
@click="handleBatchDelete"
>删除</el-button>
</el-col>
<el-col :span="1.5" v-if="isEditable">
<!-- <el-col :span="1.5" v-if="isEditable">
<el-button
type="warning"
plain
......@@ -91,7 +91,7 @@
@click="handleImport"
v-hasPermi="['inventory:inbound_items:import']"
>导入</el-button>
</el-col>
</el-col> -->
<el-col :span="1.5">
<el-button
type="warning"
......@@ -282,7 +282,7 @@ export default {
default: true
},
// 主表ID
orderId: {
inboundOrderId: {
type: [String, Number],
default: null
},
......@@ -329,13 +329,13 @@ export default {
queryParams: {
pageNum: 1,
materialId: null,
inboundOrderId: null,
warehouseId: null,
locationId: null,
labelColor: null,
voucherNumber: null,
receivedBy: null,
pageSize: this.pageSize,
orderId: this.orderId
pageSize: this.pageSize
},
cachedData: []
}
......@@ -386,13 +386,15 @@ export default {
this.syncDataToParent()
}
},
orderId: {
inboundOrderId: {
immediate: true,
handler(newVal) {
this.queryParams.orderId = newVal
console.log('inboundOrderId 变化:', newVal)
// 核心:过滤 null/空值,只在有效时执行逻辑
if (!newVal) return
this.loadRelatedData(newVal)
this.displayData.forEach(item => {
if (!item.orderId) item.orderId = newVal
if (!item.inboundOrderId) item.inboundOrderId = newVal
})
}
},
......@@ -437,10 +439,10 @@ export default {
},
// 加载关联数据
loadRelatedData(orderId) {
if (!orderId) return
loadRelatedData(inboundOrderId) {
if (!inboundOrderId) return
this.loading = true
this.queryParams.inboundOrderId = inboundOrderId
listInbound_itemsAndMname(this.queryParams).then(response => {
this.displayData = response.rows.map(item => ({
...item,
......
......@@ -141,31 +141,31 @@
<!-- 表格列保持不变 -->
<el-table-column type="selection" width="55" align="center" fixed/>
<el-table-column type="index" label="序号" align="center"/>
<el-table-column label="SAP物料号" align="center" prop="sapNo" />
<el-table-column label="SAP物料号" align="center" prop="sapNo" 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="TS Code" align="center" prop="tsCode" />
<el-table-column label="TS Code" align="center" prop="tsCode" width="150"/>
<el-table-column label="物料分类" align="center" prop="categoryCode" >
<template slot-scope="scope">
{{ scope.row.displayCategory || categoryMap[scope.row.categoryCode] || scope.row.categoryCode }}
</template>
</el-table-column>
<el-table-column label="危险类别ID" align="center" prop="hazardId" />
<el-table-column label="危险类别ID" align="center" prop="hazardId" width="150"/>
<el-table-column label="规格型号" align="center" prop="specification" />
<el-table-column label="计量单位" align="center" prop="materialUnit" />
<el-table-column label="单位重量" align="center" prop="unitWeight" />
<el-table-column label="包装重量" align="center" prop="packageWeight" />
<el-table-column label="总重量" align="center" prop="totalWeight" />
<el-table-column label="体积" align="center" prop="volume" />
<el-table-column label="保质期天数" align="center" prop="shelfLifeDays" />
<el-table-column label="存储温度要求" align="center" prop="storageTemperature" />
<el-table-column label="特殊存储要求" align="center" prop="specialRequirements" />
<el-table-column label="是否批次管理" align="center" prop="isBatchManaged">
<el-table-column label="保质期天数" align="center" prop="shelfLifeDays" width="120"/>
<el-table-column label="存储温度要求" align="center" prop="storageTemperature" width="120"/>
<el-table-column label="特殊存储要求" align="center" prop="specialRequirements" width="120"/>
<el-table-column label="是否批次管理" align="center" prop="isBatchManaged" width="120">
<template slot-scope="scope">
<el-tag :type="scope.row.isBatchManaged === 1 ? 'success' : 'info'" size="mini">{{ scope.row.isBatchManaged === 1 ? '是' : '否' }}</el-tag>
</template>
</el-table-column>
<el-table-column label="是否序列号管理" align="center" prop="isSerialManaged">
<el-table-column label="是否序列号管理" align="center" prop="isSerialManaged" width="120">
<template slot-scope="scope">
<el-tag :type="scope.row.isSerialManaged === 1 ? 'success' : 'info'" size="mini">{{ scope.row.isSerialManaged === 1 ? '是' : '否' }}</el-tag>
</template>
......@@ -438,7 +438,7 @@ export default {
// 表单校验
rules: {
materialCode: [{ required: true, message: '请输入物料编码', trigger: 'blur' }],
// materialCode: [{ required: true, message: '请输入物料编码', trigger: 'blur' }],
materialName: [{ required: true, message: '请输入物料名称', trigger: 'blur' }],
sapNo: [{ required: true, message: '请输入SAP物料号', trigger: 'blur' }],
tsCode: [{ required: true, message: '请输入TS Code', trigger: 'blur' }]
......
......@@ -116,6 +116,7 @@ public class InboundOrderItemsController extends BaseController
{
return toAjax(inboundOrderItemsService.deleteInboundOrderItemsByIds(ids));
}
/**
* 导入入库单物料明细
*/
......
package com.ruoyi.web.controller.inventory;
import java.util.List;
import com.ruoyi.common.core.domain.entity.Materials;
import com.ruoyi.common.utils.uuid.UUID;
import javax.servlet.http.HttpServletResponse;
import com.ruoyi.inventory.domain.InboundOrderItems;
import com.ruoyi.inventory.domain.vo.InboundTemplateVO;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
......@@ -21,6 +26,7 @@ import com.ruoyi.inventory.domain.InboundOrders;
import com.ruoyi.inventory.service.IInboundOrdersService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
import org.springframework.web.multipart.MultipartFile;
/**
* 入库单主Controller
......@@ -103,4 +109,31 @@ public class InboundOrdersController extends BaseController
{
return toAjax(inboundOrdersService.deleteInboundOrdersByIds(ids));
}
/**
* 下载入库单导入模板
*/
@PreAuthorize("@ss.hasPermi('inventory:inbound:importTemplate')")
@Log(title = "入库导入模板", businessType = BusinessType.IMPORT)
@PostMapping("/importTemplate")
public void importTemplate(HttpServletResponse response)
{
ExcelUtil<InboundTemplateVO> util = new ExcelUtil<InboundTemplateVO>(InboundTemplateVO.class);
util.importTemplateExcel(response, "入库单及入库物料明细信息");
}
/**
* 导入入库单物料明细
*/
@PreAuthorize("@ss.hasPermi('inventory:inbound:import')")
@Log(title = "入库信息导入", businessType = BusinessType.IMPORT)
@PostMapping("/import")
public AjaxResult importTemplate(MultipartFile file , boolean updateSupport) throws Exception
{
ExcelUtil<InboundTemplateVO> util = new ExcelUtil<InboundTemplateVO>(InboundTemplateVO.class);
List<InboundTemplateVO> inboundOrders = util.importExcel(file.getInputStream());
String operName = getUsername();
String message = inboundOrdersService.importInboundOrders(inboundOrders, updateSupport, operName);
return success(message);
}
}
......@@ -111,12 +111,18 @@ public class MaterialsController extends BaseController
return toAjax(materialsService.deleteMaterialsByIds(ids));
}
/**
* 下载物料导入模板
*/
@PreAuthorize("@ss.hasPermi('inventory:materials:importTemplate')")
@Log(title = "物料", businessType = BusinessType.IMPORT)
@PostMapping("/importTemplate")
public void importTemplate(HttpServletResponse response)
{
ExcelUtil<Materials> util = new ExcelUtil<Materials>(Materials.class);
util.importTemplateExcel(response, "物料信息");
}
/**
* 导入物料
*/
......
......@@ -18,6 +18,9 @@ public class InboundOrderItems extends BaseEntity
/** 编号 */
private String id;
/** 关联主表ID */
private String inboundOrderId;
/** 入库单号 检索条件 */
@Excel(name = "入库单号")
private String orderId;
......@@ -308,6 +311,14 @@ public class InboundOrderItems extends BaseEntity
return updateUserCode;
}
public String getInboundOrderId() {
return inboundOrderId;
}
public void setInboundOrderId(String inboundOrderId) {
this.inboundOrderId = inboundOrderId;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
......@@ -335,6 +346,7 @@ public class InboundOrderItems extends BaseEntity
.append("createUserCode", getCreateUserCode())
.append("updateTime", getUpdateTime())
.append("updateUserCode", getUpdateUserCode())
.append("inboundOrderId", getInboundOrderId())
.toString();
}
}
......@@ -22,6 +22,10 @@ public class InboundItemsAndMaterialName extends BaseEntity
@Excel(name = "入库单号")
private String orderId;
/** 入库单号 检索条件 */
@Excel(name = "主表Id")
private String inboundOrderId;
/** 货物ID 字典,检索条件 */
@Excel(name = "货物ID")
private String materialId;
......@@ -318,6 +322,14 @@ public class InboundItemsAndMaterialName extends BaseEntity
this.materialName = materialName;
}
public String getInboundOrderId() {
return inboundOrderId;
}
public void setInboundOrderId(String inboundOrderId) {
this.inboundOrderId = inboundOrderId;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
......@@ -346,6 +358,7 @@ public class InboundItemsAndMaterialName extends BaseEntity
.append("updateTime", getUpdateTime())
.append("updateUserCode", getUpdateUserCode())
.append("materialName", getMaterialName())
.append("inboundOrderId", getInboundOrderId())
.toString();
}
}
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;
/** 入库单号 检索条件 */
@Excel(name = "入库单号")
private String orderId;
/** 系统编号 检索条件 */
@Excel(name = "系统编号")
private String systemNo;
/** 入库类型 字典,检索条件 */
@Excel(name = "入库类型")
private String orderTypeId;
/** 批次ID 检索条件 */
@Excel(name = "批次ID")
private String batchId;
/** 入库日期 日期无时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "入库日期", width = 30, dateFormat = "yyyy-MM-dd")
private Date inboundDate;
/** 订单类型 字典,检索条件 */
@Excel(name = "订单类型")
private String orderType;
/** 备注 */
@Excel(name = "备注")
private String remark;
/** 货主ID */
@Excel(name = "货主ID")
private String ownerId;
/** 仓库ID 暂无用 */
@Excel(name = "仓库ID")
private String warehouseId;
/** 库位ID 检索条件 */
@Excel(name = "库位ID")
private String locationId;
/** 货物ID 字典,检索条件 */
@Excel(name = "SAP号")
private String sapNo;
/** 货物ID 字典,检索条件 */
@Excel(name = "货物名称")
private String materialName;
/** 负责人 暂无用 */
// @Excel(name = "负责人 暂无用")
private String opUserName;
/** 计划数量 */
@Excel(name = "计划数量")
private Long plannedQuantity;
/** 实际数量 */
@Excel(name = "实际数量")
private Long actualQuantity;
/** 计划件数 暂无用 */
// @Excel(name = "计划件数")
private Long plannedPackages;
/** 实际件数 */
@Excel(name = "实际件数")
private Long actualPackages;
/** 约数 */
@Excel(name = "约数")
private Long divisor;
/** 标签颜色 字典,检索条件 */
@Excel(name = "标签颜色")
private Long labelColor;
/** 凭证号 检索条件 */
@Excel(name = "凭证号")
private String voucherNumber;
/** 单价 */
@Excel(name = "单价")
private Long unitPrice;
/** 收货人 */
@Excel(name = "收货人")
private String receivedBy;
/** 物料备注 */
@Excel(name = "物料备注")
private String remark2;
/** 排序号 */
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;
}
@Override
public String toString() {
return "InboundTemplateVO{" +
"id='" + id + '\'' +
", orderId='" + orderId + '\'' +
", systemNo='" + systemNo + '\'' +
", orderTypeId='" + orderTypeId + '\'' +
", batchId='" + batchId + '\'' +
", inboundDate=" + inboundDate +
", orderType='" + orderType + '\'' +
", remark1='" + remark + '\'' +
", ownerId='" + ownerId + '\'' +
", warehouseId='" + warehouseId + '\'' +
", locationId='" + locationId + '\'' +
", sapNo='" + sapNo + '\'' +
", materialName='" + materialName + '\'' +
", opUserName='" + opUserName + '\'' +
", plannedQuantity=" + plannedQuantity +
", actualQuantity=" + actualQuantity +
", plannedPackages=" + plannedPackages +
", actualPackages=" + actualPackages +
", divisor=" + divisor +
", labelColor=" + labelColor +
", voucherNumber='" + voucherNumber + '\'' +
", unitPrice=" + unitPrice +
", receivedBy='" + receivedBy + '\'' +
", remark2='" + remark2 + '\'' +
'}';
}
}
......@@ -29,6 +29,13 @@ public interface InboundOrdersMapper
public List<InboundOrders> selectInboundOrdersList(InboundOrders inboundOrders);
/**
* 查询入库单号
*
* @param orderId 入库单主
* @return 入库单主集合
*/
public InboundOrders selectInboundOrdersByOrderId(String orderId);
/**
* 新增入库单主
*
* @param inboundOrders 入库单主
......
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.InboundTemplateVO;
/**
* 入库单主Service接口
......@@ -58,4 +61,12 @@ public interface IInboundOrdersService
* @return 结果
*/
public int deleteInboundOrdersById(String id);
/**
* 导入入库单明细信息
*
* @param inboundOrders,isUpdateSupport,operName 入库单数据信息
* @return 结果
*/
public String importInboundOrders(List<InboundTemplateVO> inboundOrders, Boolean isUpdateSupport, String operName);
}
package com.ruoyi.inventory.service.impl;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.UUID;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.inventory.domain.InboundOrderItems;
......@@ -25,7 +31,7 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
{
@Autowired
private InboundOrdersMapper inboundOrdersMapper;
private static final Logger log = LoggerFactory.getLogger(InboundOrdersServiceImpl.class);
/**
* 查询入库单主
*
......@@ -135,4 +141,154 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
}
}
}
/**
* 导入入库单明细信息
*
* @param inboundOrdersList,isUpdateSupport,operName 入库单数据信息
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public String importInboundOrders(List<InboundTemplateVO> inboundOrdersList, Boolean isUpdateSupport, String operName) {
if (StringUtils.isNull(inboundOrdersList) || inboundOrdersList.size() == 0) {
throw new ServiceException("导入数据不能为空!");
}
// 2. 初始化统计变量
int totalMainSuccess = 0; // 成功的主表数量
int totalMainFailure = 0; // 失败的主表数量
int totalItemSuccess = 0; // 成功的明细数量
int totalItemFailure = 0; // 失败的明细数量
StringBuilder successMsg = new StringBuilder();
StringBuilder failureMsg = new StringBuilder();
Date now = DateUtils.getNowDate();
Long userId = SecurityUtils.getUserId();
String operId = userId.toString();
// 3. 按入库单号分组(核心:同一入库单的多条明细归为一组)
Map<String, List<InboundTemplateVO>> orderGroupMap = inboundOrdersList.stream()
.filter(vo -> StringUtils.isNotBlank(vo.getOrderId())) // 过滤无入库单号的无效行
.collect(Collectors.groupingBy(InboundTemplateVO::getOrderId));
// 4. 遍历每个入库单分组处理
for (Map.Entry<String, List<InboundTemplateVO>> entry : orderGroupMap.entrySet()) {
String orderId = entry.getKey();
List<InboundTemplateVO> voList = entry.getValue();
InboundOrders mainDO = null;
List<InboundOrderItems> itemDOList = new ArrayList<>();
try {
// 4.1 处理主表(每个入库单号只处理一次主表)
InboundTemplateVO firstVO = voList.get(0); // 取第一条VO的主表信息
// 检查入库单是否已存在
InboundOrders existMain = inboundOrdersMapper.selectInboundOrdersByOrderId(orderId);
if (existMain != null) {
if (!isUpdateSupport) {
// 不支持更新,跳过该入库单
totalMainFailure++;
failureMsg.append(String.format("入库单号【%s】已存在,且不支持更新,跳过导入;\n", orderId));
totalItemFailure += voList.size(); // 该单的明细全部失败
continue;
}
// 支持更新,复用已有主表ID
mainDO = existMain;
// 复制VO中的主表字段到已有主表(只更新可修改的字段)
BeanUtils.copyProperties(firstVO, mainDO, "id", "createBy", "createTime"); // 排除不可更新字段
mainDO.setUpdateBy(operId);
mainDO.setUpdateTime(now);
mainDO.setUpdateUserCode(operId);
// 更新主表
inboundOrdersMapper.updateInboundOrders(mainDO);
totalMainSuccess++;
successMsg.append(String.format("入库单号【%s】已更新;\n", orderId));
} else {
// 新增主表
mainDO = new InboundOrders();
// 复制主表字段(只复制主表相关字段,避免物料字段污染)
BeanUtils.copyProperties(firstVO, mainDO,
"sapNo", "materialName", "plannedQuantity", "actualQuantity",
"plannedPackages", "materialUnit", "materialRemark"); // 排除子表字段
// 填充主表必填字段
mainDO.setId(UUID.randomUUID().toString());
mainDO.setOrderId(orderId);
mainDO.setCreateBy(operId);
mainDO.setCreateTime(now);
mainDO.setCreateUserCode(operId);
mainDO.setUpdateBy(operId);
mainDO.setUpdateTime(now);
mainDO.setUpdateUserCode(operId);
// 设置默认值
if (mainDO.getSortNo() == null) {
mainDO.setSortNo(0L);
}
if (mainDO.getOrderStatus() == null) {
mainDO.setOrderStatus(1L); // 默认草稿状态
}
// 插入主表
inboundOrdersMapper.insertInboundOrders(mainDO);
totalMainSuccess++;
successMsg.append(String.format("入库单号【%s】已新增;\n", orderId));
}
// 4.2 处理子表明细(每条VO对应一条明细)
for (InboundTemplateVO vo : voList) {
InboundOrderItems itemDO = new InboundOrderItems();
// 复制子表字段(物料相关)
BeanUtils.copyProperties(vo, itemDO,
"orderId", "systemNo", "orderTypeId", "batchId"); // 排除主表字段
// 填充明细必填字段
itemDO.setId(UUID.randomUUID().toString());
itemDO.setMaterialId(vo.getSapNo());
itemDO.setOrderId(orderId); // 关联入库单号
itemDO.setInboundOrderId(mainDO.getId()); // 关联主表ID(核心!)
itemDO.setCreateBy(operId);
itemDO.setCreateTime(now);
itemDO.setCreateUserCode(operId);
itemDO.setSortNo(0L);
// 校验物料字段(示例:必填sapNo)
if (StringUtils.isBlank(vo.getSapNo())) {
throw new ServiceException(String.format("入库单号【%s】的物料SAP号为空,明细导入失败", orderId));
}
System.out.println(itemDO);
itemDOList.add(itemDO);
}
// 4.3 批量插入明细
if (!CollectionUtils.isEmpty(itemDOList)) {
int itemSuccess = inboundOrdersMapper.batchInboundOrderItems(itemDOList);
totalItemSuccess += itemSuccess;
totalItemFailure += (itemDOList.size() - itemSuccess);
successMsg.append(String.format("入库单号【%s】成功导入%d条物料明细;\n", orderId, itemSuccess));
if (itemDOList.size() - itemSuccess > 0) {
failureMsg.append(String.format("入库单号【%s】有%d条物料明细导入失败;\n", orderId, itemDOList.size() - itemSuccess));
}
}
} catch (Exception e) {
// 单个入库单处理失败,统计错误
totalMainFailure++;
totalItemFailure += voList.size();
failureMsg.append(String.format("入库单号【%s】处理失败:%s;\n", orderId, e.getMessage()));
// 打印异常栈,方便调试
log.error("导入入库单【{}】失败", orderId, e);
}
}
// 5. 结果汇总
if (totalMainFailure > 0 || totalItemFailure > 0) {
// 有失败数据,抛出异常提示
String finalFailureMsg = String.format(
"导入结果:成功新增/更新%d个入库单,失败%d个;成功导入%d条明细,失败%d条。失败详情:%s",
totalMainSuccess, totalMainFailure, totalItemSuccess, totalItemFailure, failureMsg.toString()
);
throw new ServiceException(finalFailureMsg);
} else {
// 全部成功
String finalSuccessMsg = String.format(
"恭喜您,数据已全部导入成功!共处理%d个入库单,成功导入%d条物料明细。详情:%s",
totalMainSuccess, totalItemSuccess, successMsg.toString()
);
return finalSuccessMsg;
}
}
}
......@@ -29,17 +29,19 @@
<result property="createUserCode" column="create_user_code" />
<result property="updateTime" column="update_time" />
<result property="updateUserCode" column="update_user_code" />
<result property="inboundOrderId" column="inbound_order_id" />
</resultMap>
<sql id="selectInboundOrderItemsVo">
select id, order_id, material_id, batch_id, warehouse_id, location_id, planned_quantity, actual_quantity, planned_packages, actual_packages, divisor, label_color, voucher_number, unit_price, item_status, received_at, received_by, remark, is_used, sort_no, create_time, create_user_code, update_time, update_user_code from inbound_order_items
select id, order_id, material_id, batch_id, warehouse_id, location_id, planned_quantity, actual_quantity, planned_packages, actual_packages, divisor, label_color, voucher_number, unit_price, item_status, received_at, received_by, remark, is_used, sort_no, create_time, create_user_code, update_time, update_user_code,inbound_order_id from inbound_order_items
</sql>
<select id="selectInboundOrderItemsList" parameterType="InboundOrderItems" resultMap="InboundOrderItemsResult">
<include refid="selectInboundOrderItemsVo"/>
<where>
<if test="inboundOrderId != null and inboundOrderId != ''"> and inbound_order_id = #{inboundOrderId}</if>
<if test="orderId != null and orderId != ''"> and order_id = #{orderId}</if>
<if test="materialId != null and materialId != ''"> and material_id = #{materialId}</if>
<if test="batchId != null and batchId != ''"> and batch_id = #{batchId}</if>
......@@ -96,11 +98,15 @@
ii.create_user_code,
ii.update_time,
ii.update_user_code,
ii.inbound_order_id,
m.material_name
FROM inbound_order_items ii
LEFT JOIN materials m ON ii.material_id = m.sap_no
<where>
<!-- 移除条件前的and,<where>标签会自动处理首个条件的and/or -->
<if test="inboundOrderId != null and inboundOrderId != ''">
and inbound_order_id = #{inboundOrderId}
</if>
<if test="orderId != null and orderId != ''">
order_id = #{orderId}
</if>
......@@ -161,6 +167,7 @@
<if test="updateUserCode != null and updateUserCode != ''">
and update_user_code = #{updateUserCode}
</if>
</where>
</select>
......@@ -191,6 +198,7 @@
<if test="createUserCode != null">create_user_code,</if>
<if test="updateTime != null">update_time,</if>
<if test="updateUserCode != null">update_user_code,</if>
<if test="inboundOrderId != null">inbound_order_id,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">#{id},</if>
......@@ -217,6 +225,7 @@
<if test="createUserCode != null">#{createUserCode},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="updateUserCode != null">#{updateUserCode},</if>
<if test="inboundOrderId != null">#{inboundOrderId},</if>
</trim>
</insert>
......@@ -246,6 +255,7 @@
<if test="createUserCode != null">create_user_code = #{createUserCode},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="updateUserCode != null">update_user_code = #{updateUserCode},</if>
<if test="inboundOrderId != null">inbound_order_id = #{inboundOrderId},</if>
</trim>
where id = #{id}
</update>
......@@ -286,6 +296,7 @@
<result property="createUserCode" column="create_user_code" />
<result property="updateTime" column="update_time" />
<result property="updateUserCode" column="update_user_code" />
<result property="inboundOrderId" column="inbound_order_id" />
<result property="materialName" column="material_name" />
</resultMap>
</mapper>
\ No newline at end of file
......@@ -94,9 +94,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select>
<select id="selectInboundOrderItemsList" resultMap="InboundOrderItemsResult">
select id, order_id, material_id, batch_id, warehouse_id, location_id, planned_quantity, actual_quantity, planned_packages, actual_packages, divisor, label_color, voucher_number, unit_price, item_status, received_at, received_by, remark, is_used, sort_no, create_time, create_user_code, update_time, update_user_code
select id, order_id, material_id, batch_id, warehouse_id, location_id, planned_quantity, actual_quantity, planned_packages, actual_packages, divisor, label_color, voucher_number, unit_price, item_status, received_at, received_by, remark, is_used, sort_no, create_time, create_user_code, update_time, update_user_code, inbound_order_id
from inbound_order_items
where order_id = #{order_id}
where inbound_order_id = #{inboundOrderId}
</select>
<select id="selectInboundOrdersByOrderId" resultType="InboundOrders">
select id, order_id
from inbound_orders
where order_id = #{orderId}
</select>
<insert id="insertInboundOrders" parameterType="InboundOrders">
......@@ -199,9 +205,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</delete>
<insert id="batchInboundOrderItems">
insert into inbound_order_items( id, order_id, material_id, batch_id, warehouse_id, location_id, planned_quantity, actual_quantity, planned_packages, actual_packages, divisor, label_color, voucher_number, unit_price, item_status, received_at, received_by, remark, is_used, sort_no, create_time, create_user_code, update_time, update_user_code) values
insert into inbound_order_items( id, order_id, material_id, batch_id, warehouse_id, location_id, planned_quantity, actual_quantity, planned_packages, actual_packages, divisor, label_color, voucher_number, unit_price, item_status, received_at, received_by, remark, is_used, sort_no, create_time, create_user_code, update_time, update_user_code, inbound_order_id) values
<foreach item="item" index="index" collection="list" separator=",">
( #{item.id}, #{item.orderId}, #{item.materialId}, #{item.batchId}, #{item.warehouseId}, #{item.locationId}, #{item.plannedQuantity}, #{item.actualQuantity}, #{item.plannedPackages}, #{item.actualPackages}, #{item.divisor}, #{item.labelColor}, #{item.voucherNumber}, #{item.unitPrice}, #{item.itemStatus}, #{item.receivedAt}, #{item.receivedBy}, #{item.remark}, #{item.isUsed}, #{item.sortNo}, #{item.createTime}, #{item.createUserCode}, #{item.updateTime}, #{item.updateUserCode})
( #{item.id}, #{item.orderId}, #{item.materialId}, #{item.batchId}, #{item.warehouseId}, #{item.locationId}, #{item.plannedQuantity}, #{item.actualQuantity}, #{item.plannedPackages}, #{item.actualPackages}, #{item.divisor}, #{item.labelColor}, #{item.voucherNumber}, #{item.unitPrice}, #{item.itemStatus}, #{item.receivedAt}, #{item.receivedBy}, #{item.remark}, #{item.isUsed}, #{item.sortNo}, #{item.createTime}, #{item.createUserCode}, #{item.updateTime}, #{item.updateUserCode}, #{item.inboundOrderId})
</foreach>
</insert>
</mapper>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论