diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourcereceipt/vo/line/MesWmOutsourceReceiptLineSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourcereceipt/vo/line/MesWmOutsourceReceiptLineSaveReqVO.java index 01a25d8..33bd341 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourcereceipt/vo/line/MesWmOutsourceReceiptLineSaveReqVO.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourcereceipt/vo/line/MesWmOutsourceReceiptLineSaveReqVO.java @@ -26,12 +26,6 @@ public class MesWmOutsourceReceiptLineSaveReqVO { @NotNull(message = "入库数量不能为空") private BigDecimal quantity; - @Schema(description = "批次编号", example = "1") - private Long batchId; - - @Schema(description = "批次编码", example = "BATCH20260301001") - private String batchCode; - @Schema(description = "生产日期") private LocalDateTime productionDate; diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/outsourcereceipt/MesWmOutsourceReceiptLineDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/outsourcereceipt/MesWmOutsourceReceiptLineDO.java index e52eb13..30dd611 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/outsourcereceipt/MesWmOutsourceReceiptLineDO.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/outsourcereceipt/MesWmOutsourceReceiptLineDO.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourcereceipt; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -48,10 +49,16 @@ public class MesWmOutsourceReceiptLineDO extends BaseDO { /** * 批次编号 * - * DONE @芋艿:保留。待 mes_wm_batch 模块迁移后补充 @link 关联(AI 未修复原因:标注为后续处理,需人工介入) + * 关联 {@link MesWmBatchDO#getId()} */ private Long batchId; /** + * 批次号 + * + * 关联 {@link MesWmBatchDO#getCode()} + */ + private String batchCode; + /** * 生产日期 */ private LocalDateTime productionDate; diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptLineServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptLineServiceImpl.java index 3e792de..2c81a76 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptLineServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptLineServiceImpl.java @@ -2,14 +2,22 @@ package cn.iocoder.yudao.module.mes.service.wm.outsourcereceipt; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchGenerateReqVO; import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourcereceipt.vo.line.MesWmOutsourceReceiptLinePageReqVO; import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourcereceipt.vo.line.MesWmOutsourceReceiptLineSaveReqVO; +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourcereceipt.MesWmOutsourceReceiptDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourcereceipt.MesWmOutsourceReceiptLineDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO; import cn.iocoder.yudao.module.mes.dal.mysql.wm.outsourcereceipt.MesWmOutsourceReceiptLineMapper; import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum; import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; +import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; +import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService; import jakarta.annotation.Resource; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.List; @@ -30,6 +38,21 @@ public class MesWmOutsourceReceiptLineServiceImpl implements MesWmOutsourceRecei @Resource private MesMdItemService itemService; + @Resource + @Lazy + private MesWmOutsourceReceiptDetailService outsourceReceiptDetailService; + + @Resource + @Lazy + private MesWmOutsourceReceiptService outsourceReceiptService; + + @Resource + @Lazy + private MesProWorkOrderService workOrderService; + + @Resource + private MesWmBatchService batchService; + @Override public Long createOutsourceReceiptLine(MesWmOutsourceReceiptLineSaveReqVO createReqVO) { // 校验物料存在 @@ -37,8 +60,13 @@ public class MesWmOutsourceReceiptLineServiceImpl implements MesWmOutsourceRecei // 插入 MesWmOutsourceReceiptLineDO line = BeanUtils.toBean(createReqVO, MesWmOutsourceReceiptLineDO.class); - // 质量状态自动赋值 - line.setQualityStatus(calculateQualityStatus(line.getIqcCheckFlag())); + line.setQualityStatus(calculateQualityStatus(line.getIqcCheckFlag())); // 质量状态自动赋值 + // 生成或获取批次 + MesWmBatchDO batch = generateOrGetBatch(createReqVO); + if (batch != null) { + line.setBatchId(batch.getId()); + line.setBatchCode(batch.getCode()); + } lineMapper.insert(line); return line.getId(); } @@ -52,15 +80,24 @@ public class MesWmOutsourceReceiptLineServiceImpl implements MesWmOutsourceRecei // 更新 MesWmOutsourceReceiptLineDO updateObj = BeanUtils.toBean(updateReqVO, MesWmOutsourceReceiptLineDO.class); - // 质量状态自动赋值 - updateObj.setQualityStatus(calculateQualityStatus(updateObj.getIqcCheckFlag())); + updateObj.setQualityStatus(calculateQualityStatus(updateObj.getIqcCheckFlag())); // 质量状态自动赋值 + // 生成或获取批次 + MesWmBatchDO batch = generateOrGetBatch(updateReqVO); + if (batch != null) { + updateObj.setBatchId(batch.getId()); + updateObj.setBatchCode(batch.getCode()); + } lineMapper.updateById(updateObj); } @Override + @Transactional(rollbackFor = Exception.class) public void deleteOutsourceReceiptLine(Long id) { // 校验存在 validateOutsourceReceiptLineExists(id); + + // 级联删除明细 + outsourceReceiptDetailService.deleteOutsourceReceiptDetailByLineId(id); // 删除 lineMapper.deleteById(id); } @@ -114,4 +151,30 @@ public class MesWmOutsourceReceiptLineServiceImpl implements MesWmOutsourceRecei return MesWmQualityStatusEnum.PASS.getStatus(); // 不需检验,默认合格 } + /** + * 生成或获取批次 + * + * @param reqVO 入库单行请求VO + * @return 批次记录(如果物料未启用批次管理则返回 null) + */ + private MesWmBatchDO generateOrGetBatch(MesWmOutsourceReceiptLineSaveReqVO reqVO) { + MesWmBatchGenerateReqVO batchReqVO = new MesWmBatchGenerateReqVO(); + batchReqVO.setItemId(reqVO.getItemId()).setProduceDate(reqVO.getProductionDate()) + .setExpireDate(reqVO.getExpireDate()).setLotNumber(reqVO.getLotNumber()); + MesWmOutsourceReceiptDO receipt = outsourceReceiptService.getOutsourceReceipt(reqVO.getReceiptId()); + if (receipt != null) { + batchReqVO.setVendorId(receipt.getVendorId()) + .setWorkOrderId(receipt.getWorkOrderId()) + .setReceiptDate(receipt.getReceiptDate()); + if (receipt.getWorkOrderId() != null) { + MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(receipt.getWorkOrderId()); + if (workOrder != null) { + batchReqVO.setClientId(workOrder.getClientId()); + } + } + } + // 调用批次服务生成或获取批次 + return batchService.getOrGenerateBatchCode(batchReqVO); + } + } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptServiceImpl.java index 833c8ce..88270fb 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptServiceImpl.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.mes.service.wm.outsourcereceipt; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; @@ -9,10 +10,10 @@ import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourcereceipt.vo.MesWmOutsourceReceiptPageReqVO; import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourcereceipt.vo.MesWmOutsourceReceiptSaveReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourcereceipt.MesWmOutsourceReceiptDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourcereceipt.MesWmOutsourceReceiptDetailDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourcereceipt.MesWmOutsourceReceiptLineDO; - import cn.iocoder.yudao.module.mes.dal.mysql.wm.outsourcereceipt.MesWmOutsourceReceiptMapper; import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants; import cn.iocoder.yudao.module.mes.enums.wm.MesWmOutsourceReceiptStatusEnum; @@ -20,6 +21,7 @@ import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum; import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum; import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService; +import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService; import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO; import jakarta.annotation.Resource; @@ -55,6 +57,8 @@ public class MesWmOutsourceReceiptServiceImpl implements MesWmOutsourceReceiptSe private MesMdVendorService vendorService; @Resource private MesMdItemService itemService; + @Resource + private MesProWorkOrderService workOrderService; @Override public Long createOutsourceReceipt(MesWmOutsourceReceiptSaveReqVO createReqVO) { @@ -62,6 +66,10 @@ public class MesWmOutsourceReceiptServiceImpl implements MesWmOutsourceReceiptSe validateCodeUnique(null, createReqVO.getCode()); // 校验供应商存在 vendorService.validateVendorExists(createReqVO.getVendorId()); + // 校验外协工单存在 + if (createReqVO.getWorkOrderId() != null) { + workOrderService.validateWorkOrderExists(createReqVO.getWorkOrderId()); + } // 插入 MesWmOutsourceReceiptDO receipt = BeanUtils.toBean(createReqVO, MesWmOutsourceReceiptDO.class); @@ -78,6 +86,10 @@ public class MesWmOutsourceReceiptServiceImpl implements MesWmOutsourceReceiptSe validateCodeUnique(updateReqVO.getId(), updateReqVO.getCode()); // 校验供应商存在 vendorService.validateVendorExists(updateReqVO.getVendorId()); + // 校验外协工单存在 + if (updateReqVO.getWorkOrderId() != null) { + workOrderService.validateWorkOrderExists(updateReqVO.getWorkOrderId()); + } // 更新 MesWmOutsourceReceiptDO updateObj = BeanUtils.toBean(updateReqVO, MesWmOutsourceReceiptDO.class); @@ -104,7 +116,15 @@ public class MesWmOutsourceReceiptServiceImpl implements MesWmOutsourceReceiptSe @Override public PageResult getOutsourceReceiptPage(MesWmOutsourceReceiptPageReqVO pageReqVO) { - return outsourceReceiptMapper.selectPage(pageReqVO); + List workOrderIds = null; + if (StrUtil.isNotBlank(pageReqVO.getWorkOrderCode())) { + MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(pageReqVO.getWorkOrderCode()); + if (workOrder == null) { + return PageResult.empty(); + } + workOrderIds = java.util.Collections.singletonList(workOrder.getId()); + } + return outsourceReceiptMapper.selectPage(pageReqVO, workOrderIds); } @Override @@ -165,16 +185,36 @@ public class MesWmOutsourceReceiptServiceImpl implements MesWmOutsourceReceiptSe @Override @Transactional(rollbackFor = Exception.class) public void finishOutsourceReceipt(Long id) { - // 1. 校验存在 + // 1.1 校验存在 MesWmOutsourceReceiptDO receipt = validateOutsourceReceiptExists(id); if (ObjUtil.notEqual(MesWmOutsourceReceiptStatusEnum.APPROVED.getStatus(), receipt.getStatus())) { throw exception(WM_OUTSOURCE_RECEIPT_STATUS_ERROR); } + // 1.2 校验至少有一条行 + List lines = outsourceReceiptLineService.getOutsourceReceiptLineListByReceiptId(id); + if (CollUtil.isEmpty(lines)) { + throw exception(WM_OUTSOURCE_RECEIPT_NO_LINE); + } - // 2. 遍历所有明细,创建库存事务(增加库存 + 记录流水) + // 3. 遍历所有明细,创建库存事务(增加库存 + 记录流水) createTransactionList(receipt); - // 3. 更新入库单状态 + // 4. 更新外协工单的已生产数量(只统计合格的目标产品) + if (receipt.getWorkOrderId() != null) { + MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(receipt.getWorkOrderId()); + if (workOrder != null) { + BigDecimal totalQuantity = lines.stream() + .filter(line -> ObjUtil.equal(line.getItemId(), workOrder.getProductId())) // 只统计目标产品 + .filter(line -> ObjUtil.notEqual(line.getQualityStatus(), MesWmQualityStatusEnum.FAIL.getStatus())) // 排除不合格品 + .map(MesWmOutsourceReceiptLineDO::getQuantity) + .reduce(BigDecimal.ZERO, BigDecimal::add); + if (totalQuantity.compareTo(BigDecimal.ZERO) > 0) { + workOrderService.updateProducedQuantity(receipt.getWorkOrderId(), totalQuantity); + } + } + } + + // 5. 更新入库单状态 outsourceReceiptMapper.updateById(new MesWmOutsourceReceiptDO() .setId(id).setStatus(MesWmOutsourceReceiptStatusEnum.FINISHED.getStatus())); }