fix(biz): 修复采购入库单功能的多个问题

This commit is contained in:
zxy 2026-05-22 16:46:04 +08:00
parent 9d32907946
commit 56749b305b
10 changed files with 225 additions and 57 deletions

2
.gitignore vendored
View File

@ -52,3 +52,5 @@ application-my.yaml
/mes-ui-app/unpackage/
### Trae ###
/.trae/

View File

@ -1,18 +1,20 @@
package com.ningxia.yunxi.chemmes.framework.mybatis.core.mapper;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.github.yulichang.base.MPJBaseMapper;
import com.github.yulichang.interfaces.MPJBaseJoin;
import com.ningxia.yunxi.chemmes.framework.common.pojo.PageParam;
import com.ningxia.yunxi.chemmes.framework.common.pojo.PageResult;
import com.ningxia.yunxi.chemmes.framework.mybatis.core.util.MyBatisUtils;
import com.github.yulichang.base.MPJBaseMapper;
import com.github.yulichang.interfaces.MPJBaseJoin;
import org.apache.ibatis.annotations.Param;
import java.math.BigDecimal;

View File

@ -104,4 +104,13 @@ public class PurReceiptDetailRespVO {
@ExcelProperty("已退料数量")
private BigDecimal returnQty;
@Schema(description = "发货数量")
@ExcelProperty("发货数量")
private BigDecimal deliveryQty;
@Schema(description = "采购数量")
@ExcelProperty("采购数量")
private BigDecimal purQty;
}

View File

@ -40,7 +40,7 @@ public class RawStorageLogDO extends BaseDO {
/**
* 状态1为保存2为提交3为作废
*/
private Boolean status;
private String status;
/**
* 仓储id
*/
@ -140,7 +140,7 @@ public class RawStorageLogDO extends BaseDO {
/**
* 操作人id
*/
private Integer operatorId;
private Long operatorId;
/**
* 操作人
*/

View File

@ -1,14 +1,15 @@
package com.ningxia.yunxi.chemmes.module.biz.dal.mysql.purreceipt;
import java.util.*;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import com.ningxia.yunxi.chemmes.framework.common.pojo.PageResult;
import com.ningxia.yunxi.chemmes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.ningxia.yunxi.chemmes.framework.mybatis.core.mapper.BaseMapperX;
import com.ningxia.yunxi.chemmes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.purreceipt.vo.PurReceiptPageReqVO;
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.purreceipt.PurReceiptDO;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.purreceipt.vo.*;
/**
* 采购入库单主 Mapper
@ -20,12 +21,11 @@ public interface PurReceiptMapper extends BaseMapperX<PurReceiptDO> {
default PageResult<PurReceiptDO> selectPage(PurReceiptPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<PurReceiptDO>()
.eqIfPresent(PurReceiptDO::getPurReceiptNo, reqVO.getPurReceiptNo())
.likeIfPresent(PurReceiptDO::getPurReceiptNo, reqVO.getPurReceiptNo())
.betweenIfPresent(PurReceiptDO::getReceiptDate, reqVO.getReceiptDate())
.eqIfPresent(PurReceiptDO::getSupplierId, reqVO.getSupplierId())
.likeIfPresent(PurReceiptDO::getSupplierName, reqVO.getSupplierName())
.eqIfPresent(PurReceiptDO::getPurStatus, reqVO.getPurStatus())
.eqIfPresent(PurReceiptDO::getRemark, reqVO.getRemark())
.eqIfPresent(PurReceiptDO::getPurOrdId, reqVO.getPurOrdId())
.eqIfPresent(PurReceiptDO::getPurOrdNo, reqVO.getPurOrdNo())
.eqIfPresent(PurReceiptDO::getBillType, reqVO.getBillType())
@ -35,4 +35,14 @@ public interface PurReceiptMapper extends BaseMapperX<PurReceiptDO> {
@Select("SELECT MAX(pur_receipt_no) FROM tsc_pur_receipt")
String selectMaxPurReceiptNo();
/**
* 根据ID物理删除采购入库单
*
* @param id 主键ID
* @return 删除的记录数
*/
@Delete("DELETE FROM tsc_pur_receipt WHERE id = #{id}")
@InterceptorIgnore(tenantLine = "true")
int physicalDeleteById(@Param("id") Integer id);
}

View File

@ -1,11 +1,14 @@
package com.ningxia.yunxi.chemmes.module.biz.dal.mysql.purreceiptdetail;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import com.ningxia.yunxi.chemmes.framework.common.pojo.PageResult;
import com.ningxia.yunxi.chemmes.framework.mybatis.core.mapper.BaseMapperX;
import com.ningxia.yunxi.chemmes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.purreceiptdetail.vo.PurReceiptDetailPageReqVO;
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.purreceiptdetail.PurReceiptDetailDO;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@ -48,9 +51,15 @@ public interface PurReceiptDetailMapper extends BaseMapperX<PurReceiptDetailDO>
.eq(PurReceiptDetailDO::getPurReceiptId, purReceiptId));
}
default void deleteByPurReceiptId(Integer purReceiptId) {
delete(new LambdaQueryWrapperX<PurReceiptDetailDO>()
.eq(PurReceiptDetailDO::getPurReceiptId, purReceiptId));
}
/**
* 根据采购入库单ID物理删除明细
* 使用 @Delete 注解直接写 SQL bypass 逻辑删除和多租户插件
*
* @param purReceiptId 采购入库单ID
* @return 删除的记录数
*/
@Delete("DELETE FROM tsc_pur_receipt_detail WHERE pur_receipt_id = #{purReceiptId}")
@InterceptorIgnore(tenantLine = "true")
int physicalDeleteByPurReceiptId(@Param("purReceiptId") Integer purReceiptId);
}

View File

@ -7,9 +7,11 @@ import com.ningxia.yunxi.chemmes.module.biz.controller.admin.purreceipt.vo.PurRe
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.purreceipt.vo.PurReceiptSaveReqVO;
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.purreceiptdetail.vo.PurReceiptDetailRespVO;
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.purreceiptdetail.vo.PurReceiptDetailSaveReqVO;
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.purorderitem.PurOrderItemDO;
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.purreceipt.PurReceiptDO;
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.purreceiptdetail.PurReceiptDetailDO;
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.rawstoragelog.RawStorageLogDO;
import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.purorderitem.PurOrderItemMapper;
import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.purreceipt.PurReceiptMapper;
import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.purreceiptdetail.PurReceiptDetailMapper;
import com.ningxia.yunxi.chemmes.module.biz.service.rawstoragelog.RawStorageLogService;
@ -48,6 +50,9 @@ public class PurReceiptServiceImpl implements PurReceiptService {
@Resource
private RawStorageLogService rawStorageLogService;
@Resource
private PurOrderItemMapper purOrderItemMapper;
@Override
@Transactional(rollbackFor = Exception.class)
@ -65,23 +70,7 @@ public class PurReceiptServiceImpl implements PurReceiptService {
purReceiptMapper.insert(purReceipt);
// 插入子表
if (createReqVO.getItems() != null && !createReqVO.getItems().isEmpty()) {
Integer purReceiptId = purReceipt.getId();
for (PurReceiptDetailSaveReqVO item : createReqVO.getItems()) {
item.setPurReceiptId(purReceiptId);
PurReceiptDetailDO detail = BeanUtils.toBean(item, PurReceiptDetailDO.class);
detail.setTwmStorageDetailId(item.getTwmStorageDetailId());
detail.setInventBillNo(item.getInventBillNo());
// detail.setPurOrdDetailId(item.getPurOrdDetailId());
detail.setOrdQty(item.getOrdQty());
detail.setReceiptQty(item.getReceiptQty());
detail.setId(null);
purReceiptDetailMapper.insert(detail);
if ("2".equals(createReqVO.getPurStatus())) {
saveRwaStorageLog(purReceipt, detail);
}
}
}
updatePurReceiptDetails(purReceipt.getId(), createReqVO.getItems(), purReceipt);
// 返回
return purReceipt.getId();
@ -94,23 +83,62 @@ public class PurReceiptServiceImpl implements PurReceiptService {
validatePurReceiptExists(updateReqVO.getId());
// 更新主表
PurReceiptDO updateObj = BeanUtils.toBean(updateReqVO, PurReceiptDO.class);
Long userId = getLoginUserId();
AdminUserDO adminUserDO = adminUserMapper.selectById(userId);
updateObj.setReceiptEmpName(adminUserDO.getNickname());
updateObj.setReceiptEmpId(adminUserDO.getId());
purReceiptMapper.updateById(updateObj);
// 更新子表先删除旧的再插入新的
Integer purReceiptId = updateReqVO.getId();
purReceiptDetailMapper.deleteByPurReceiptId(purReceiptId);
if (updateReqVO.getItems() != null && !updateReqVO.getItems().isEmpty()) {
for (PurReceiptDetailSaveReqVO item : updateReqVO.getItems()) {
// 更新子表
updatePurReceiptDetails(updateReqVO.getId(), updateReqVO.getItems(), updateObj);
}
/**
* 更新采购入库单明细
* 先删除所有旧明细再插入新明细
*
* @param purReceiptId 采购入库单ID
* @param items 明细列表
* @param purReceipt 采购入库单主表对象
*/
private void updatePurReceiptDetails(Integer purReceiptId, List<PurReceiptDetailSaveReqVO> items, PurReceiptDO purReceipt) {
// 物理删除旧明细
purReceiptDetailMapper.physicalDeleteByPurReceiptId(purReceiptId);
// 插入新明细
if (items != null && !items.isEmpty()) {
for (PurReceiptDetailSaveReqVO item : items) {
item.setPurReceiptId(purReceiptId);
PurReceiptDetailDO detail = BeanUtils.toBean(item, PurReceiptDetailDO.class);
detail.setTwmStorageDetailId(item.getTwmStorageDetailId());
// 补全所有字段
// detail.setTwmStorageDetailId(item.getId());
detail.setInventBillNo(item.getInventBillNo());
// detail.setPurOrdDetailId(item.getPurOrdDetailId());
detail.setPurOrdDetailId(item.getPurOrdDetailId());
detail.setOrdQty(item.getOrdQty());
detail.setReceiptQty(item.getReceiptQty());
detail.setRemark(item.getRemark());
detail.setLotNo(item.getLotNo());
detail.setStoreAreaId(item.getStoreAreaId());
detail.setStoreAreCd(item.getStoreAreCd());
detail.setStoreAreaName(item.getStoreAreaName());
detail.setReqDeliveryDate(item.getReqDeliveryDate());
detail.setTotalPrice(item.getTotalPrice());
detail.setMaterialId(item.getMaterialId());
detail.setMaterialCode(item.getMaterialCode());
detail.setMaterialName(item.getMaterialName());
detail.setSpec(item.getSpec());
detail.setUnit(item.getUnit());
detail.setRemaimQty(item.getRemaimQty());
detail.setReturnQty(item.getReturnQty());
// 新增明细ID设为null
detail.setId(null);
purReceiptDetailMapper.insert(detail);
if ("2".equals(updateReqVO.getPurStatus())) {
saveRwaStorageLog(updateObj, detail);
// 如果状态为已入库生成入库日志
if ("2".equals(purReceipt.getPurStatus())) {
saveRwaStorageLog(purReceipt, detail);
}
}
}
@ -118,8 +146,44 @@ public class PurReceiptServiceImpl implements PurReceiptService {
private void saveRwaStorageLog(PurReceiptDO purReceipt, PurReceiptDetailDO item) {
RawStorageLogDO rawStorageLog = BeanUtils.toBean(item, RawStorageLogDO.class);
// 查询采购订单明细
PurOrderItemDO purOrderItem = purOrderItemMapper.selectById(item.getPurOrdDetailId());
rawStorageLog.setStockId(purReceipt.getId());
rawStorageLog.setStatus("2");
rawStorageLog.setStoreHouseId(purReceipt.getStoreHouseId());
rawStorageLog.setStoreAreaId(item.getStoreAreaId());
rawStorageLog.setStoreHouseCd(purReceipt.getStoreHouseCd());
rawStorageLog.setStoreHouseName(purReceipt.getStoreHouseName());
rawStorageLog.setStoreAreCd(item.getStoreAreCd());
rawStorageLog.setStoreAreaName(item.getStoreAreaName());
rawStorageLog.setOperatorType("1");
rawStorageLog.setOperatorId(purReceipt.getReceiptEmpId());
rawStorageLog.setOperatorName(purReceipt.getReceiptEmpName());
rawStorageLog.setMaterialId(item.getMaterialId());
rawStorageLog.setMatCode(item.getMaterialCode());
rawStorageLog.setMatName(item.getMaterialName());
rawStorageLog.setRelarionId(purReceipt.getId());
rawStorageLog.setRelarionNo(purReceipt.getPurReceiptNo());
rawStorageLog.setRelarionDetailId(item.getId());
// 补全所有字段set
rawStorageLog.setDescription(item.getRemark());
rawStorageLog.setSpec(item.getSpec());
rawStorageLog.setUnit(item.getUnit());
rawStorageLog.setLotNo(item.getLotNo());
rawStorageLog.setOperatorQty(item.getReceiptQty());
rawStorageLog.setBusinessType("10");
rawStorageLog.setStockItemId(item.getTwmStorageDetailId());
rawStorageLog.setDpstNo(purReceipt.getPurReceiptNo());
// rawStorageLog.setSupplierNo(purReceipt.getSupplierNo());
rawStorageLog.setSupplierName(purReceipt.getSupplierName());
rawStorageLog.setSupplierId(purReceipt.getSupplierId());
rawStorageLog.setPurQty(purOrderItem.getPurQty());
rawStorageLog.setBillDate(purReceipt.getReceiptDate());
rawStorageLog.setStockItemId(item.getId());
rawStorageLogService.saveRawStorageLog(rawStorageLog);
}
@ -128,10 +192,10 @@ public class PurReceiptServiceImpl implements PurReceiptService {
public void deletePurReceipt(Integer id) {
// 校验存在
validatePurReceiptExists(id);
// 删除子表
purReceiptDetailMapper.deleteByPurReceiptId(id);
// 删除主表
purReceiptMapper.deleteById(id);
// 物理删除子表
purReceiptDetailMapper.physicalDeleteByPurReceiptId(id);
// 物理删除主表
purReceiptMapper.physicalDeleteById(id);
}
private void validatePurReceiptExists(Integer id) {
@ -155,9 +219,17 @@ public class PurReceiptServiceImpl implements PurReceiptService {
List<PurReceiptDetailDO> detailList = purReceiptDetailMapper.selectListByPurReceiptId(id);
if (detailList != null && !detailList.isEmpty()) {
List<PurReceiptDetailRespVO> detailRespVOList = BeanUtils.toBean(detailList, PurReceiptDetailRespVO.class);
respVO.setItems(detailRespVOList);
}
for (PurReceiptDetailRespVO purReceiptDetailRespVO : detailRespVOList) {
PurOrderItemDO purOrderItem = purOrderItemMapper.selectById(purReceiptDetailRespVO.getPurOrdDetailId());
if (purOrderItem != null) {
purReceiptDetailRespVO.setDeliveryQty(purOrderItem.getDeliveryQty());
purReceiptDetailRespVO.setPurQty(purOrderItem.getPurQty());
}
}
respVO.setItems(detailRespVOList);
}
return respVO;
}

View File

@ -16,7 +16,7 @@
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="单据类型" prop="billType" >
<el-select v-model="formData.billType" placeholder="请选择" style="width: 100%" >
<el-select v-model="formData.billType" placeholder="请选择" style="width: 100%" :disabled="formType === 'update'">
<el-option label="标准采购申请" value="1" />
<el-option label="设备采购申请" value="2" />
</el-select>
@ -24,7 +24,7 @@
</el-col>
<el-col :span="8">
<el-form-item label="申请类型" prop="applyType">
<el-select v-model="formData.applyType" placeholder="请选择" style="width: 100%" >
<el-select v-model="formData.applyType" placeholder="请选择" style="width: 100%" :disabled="formType === 'update'">
<el-option label="采购申请" value="1" />
<el-option label="紧急采购" value="2" />
</el-select>

View File

@ -18,7 +18,7 @@
v-model="formData.billType"
placeholder="请选择"
class="!w-full"
:disabled="formType === 'detail'"
:disabled="formType === 'detail' || formType === 'update'"
>
<el-option label="标准采购" value="1" />
<el-option label="设备采购" value="2" />
@ -71,7 +71,7 @@
v-model="formData.supplierId"
placeholder="请选择"
class="!w-full"
:disabled="formType === 'detail'"
:disabled="formType === 'detail' || formType === 'update'"
@change="handleSupplierChange"
>
<el-option
@ -216,7 +216,7 @@
:disabled="formLoading"
>保存</el-button>
<el-button
v-if="formType === 'update' && formData.purStatus === 1"
v-if="formData.purStatus === '1' && formType !== 'detail'"
@click="handleConfirm"
type="primary"
:disabled="formLoading"
@ -263,6 +263,7 @@ const formData = reactive({
purOrdNo: undefined,
storeHouseId: undefined,
storeHouseName: undefined,
storeHouseCd: undefined,
billType: undefined,
})
@ -314,6 +315,11 @@ const open = async (type: string, id?: number) => {
Object.assign(formData, data)
//
detailList.value = data.items || []
// ID
if (formData.storeHouseId) {
await handleStoreHouseChange(formData.storeHouseId, true)
}
} finally {
formLoading.value = false
}
@ -357,7 +363,7 @@ const openPurOrderDialog = () => {
}
/** 处理采购订单选择 */
const handlePurOrderSelect = (selectedData: any[]) => {
const handlePurOrderSelect = async (selectedData: any[]) => {
if (selectedData && selectedData.length > 0) {
//
const firstItem = selectedData[0]
@ -388,6 +394,11 @@ const handlePurOrderSelect = (selectedData: any[]) => {
deliveryQty: Number(item.deliveryQty) || 0,
}
})
// ID
if (formData.storeHouseId) {
await handleStoreHouseChange(formData.storeHouseId)
}
}
}
@ -408,7 +419,7 @@ const loadOrderDetail = async (orderId: number) => {
const items = await PurOrderApi.getPurOrderDetail(orderId)
if (items && items.length > 0) {
detailList.value = items.map(item => ({
id: item.id,
id: undefined,
materialCode: item.materialCode,
materialName: item.materialName,
spec: item.spec,
@ -423,6 +434,7 @@ const loadOrderDetail = async (orderId: number) => {
materialId: item.materialId,
totalPrice: item.totalPrice,
deliveryQty: item.deliveryQty || 0,
purOrdDetailId: item.id,
}))
}
} catch (error) {
@ -431,15 +443,33 @@ const loadOrderDetail = async (orderId: number) => {
}
/** 仓储变更 */
const handleStoreHouseChange = async (val: number) => {
const handleStoreHouseChange = async (val: number, keepSelection: boolean = false) => {
const storeHouse = storeHouseList.value.find(item => item.id === val)
if (storeHouse) {
formData.storeHouseName = storeHouse.storeHouseName
formData.storeHouseCd = storeHouse.storeHouseCd
//
const areas = await StoreAreaApi.getStoreAreaSelect(val)
storeAreaList.value = areas || []
//
//
if (!keepSelection) {
detailList.value.forEach(item => {
item.storeAreaId = undefined
item.storeAreaName = undefined
})
}
} else {
storeAreaList.value = []
//
if (!keepSelection) {
detailList.value.forEach(item => {
item.storeAreaId = undefined
item.storeAreaName = undefined
})
}
}
}
@ -475,6 +505,7 @@ const addDetailItem = () => {
materialId: undefined,
totalPrice: 0,
deliveryQty: 0,
purOrdDetailId: undefined,
})
}
@ -494,11 +525,31 @@ const handleSave = async () => {
return
}
//
for (let i = 0; i < detailList.value.length; i++) {
const item = detailList.value[i]
if (!item.receiptQty || Number(item.receiptQty) <= 0) {
message.warning(`${i + 1}行物料的收货数量不能为空且必须大于0`)
return
}
if (!item.storeAreaId) {
message.warning(`${i + 1}行物料的库区名称不能为空`)
return
}
}
formLoading.value = true
//
// item.id ID purOrdDetailId
// items purOrdDetailId
const data = {
...formData,
items: detailList.value,
items: detailList.value.map(item => ({
...item,
// purOrdDetailId 使使 item.id
purOrdDetailId: item.purOrdDetailId || item.id
})),
}
if (formType.value === 'create') {
@ -553,6 +604,7 @@ const resetForm = () => {
formData.purOrdNo = undefined
formData.storeHouseId = undefined
formData.storeHouseName = undefined
formData.storeHouseCd = undefined
formData.billType = undefined
detailList.value = []

View File

@ -116,6 +116,7 @@
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['biz:pur-receipt:update']"
v-if="scope.row.purStatus === '1' "
>
编辑
</el-button>
@ -124,6 +125,7 @@
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['biz:pur-receipt:delete']"
v-if="scope.row.purStatus === '1' "
>
删除
</el-button>
@ -196,6 +198,7 @@ const detailLoading = ref(false) // 详情列表的加载中
const list = ref([]) //
const total = ref(0) //
const detailList = ref([]) //
const mainInfo = ref<any>({}) //
const queryParams = reactive({
pageNo: 1,
@ -230,11 +233,20 @@ const getDetailList = async (id: number) => {
try {
// ID
const data = await PurReceiptApi.getPurReceipt(id)
//
mainInfo.value = {
storeHouseName: data.storeHouseName
}
// items
detailList.value = data?.items || data || []
//
detailList.value.forEach(item => {
item.storeHouseName = mainInfo.value.storeHouseName
})
} catch (error) {
console.error('获取入库物料详情失败:', error)
detailList.value = []
mainInfo.value = {}
} finally {
detailLoading.value = false
}