Browse Source

✨ feat(mes): 新增批次生成逻辑及相关字段处理

实现批次的生成或获取逻辑,自动关联外协工单和物料信息。更新了相关数据对象和服务实现,确保在创建和更新外协工单行时能够正确处理批次信息。
YunaiV 6 months ago
parent
commit
571d32632e

+ 0 - 6
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;
 

+ 8 - 1
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,9 +49,15 @@ public class MesWmOutsourceReceiptLineDO extends BaseDO {
     /**
      * 批次编号
      *
-     * DONE @芋艿:保留。待 mes_wm_batch 模块迁移后补充 @link 关联(AI 未修复原因:标注为后续处理,需人工介入)
+     * 关联 {@link MesWmBatchDO#getId()}
      */
     private Long batchId;
+    /**
+     * 批次号
+     *
+     * 关联 {@link MesWmBatchDO#getCode()}
+     */
+    private String batchCode;
     /**
      * 生产日期
      */

+ 67 - 4
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);
+    }
+
 }

+ 45 - 5
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<MesWmOutsourceReceiptDO> getOutsourceReceiptPage(MesWmOutsourceReceiptPageReqVO pageReqVO) {
-        return outsourceReceiptMapper.selectPage(pageReqVO);
+        List<Long> 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<MesWmOutsourceReceiptLineDO> 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()));
     }