Commit 571d32632ebf23cf56f162fd341fa38a8ce62203

Authored by YunaiV
1 parent fce023ad

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

实现批次的生成或获取逻辑,自动关联外协工单和物料信息。更新了相关数据对象和服务实现,确保在创建和更新外协工单行时能够正确处理批次信息。
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 {
26 26 @NotNull(message = "入库数量不能为空")
27 27 private BigDecimal quantity;
28 28  
29   - @Schema(description = "批次编号", example = "1")
30   - private Long batchId;
31   -
32   - @Schema(description = "批次编码", example = "BATCH20260301001")
33   - private String batchCode;
34   -
35 29 @Schema(description = "生产日期")
36 30 private LocalDateTime productionDate;
37 31  
... ...
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;
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 4 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
5 5 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcDO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
6 7 import com.baomidou.mybatisplus.annotation.KeySequence;
7 8 import com.baomidou.mybatisplus.annotation.TableId;
8 9 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -48,10 +49,16 @@ public class MesWmOutsourceReceiptLineDO extends BaseDO {
48 49 /**
49 50 * 批次编号
50 51 *
51   - * DONE @芋艿:保留。待 mes_wm_batch 模块迁移后补充 @link 关联(AI 未修复原因:标注为后续处理,需人工介入)
  52 + * 关联 {@link MesWmBatchDO#getId()}
52 53 */
53 54 private Long batchId;
54 55 /**
  56 + * 批次号
  57 + *
  58 + * 关联 {@link MesWmBatchDO#getCode()}
  59 + */
  60 + private String batchCode;
  61 + /**
55 62 * 生产日期
56 63 */
57 64 private LocalDateTime productionDate;
... ...
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;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchGenerateReqVO;
5 6 import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourcereceipt.vo.line.MesWmOutsourceReceiptLinePageReqVO;
6 7 import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourcereceipt.vo.line.MesWmOutsourceReceiptLineSaveReqVO;
  8 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
  9 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourcereceipt.MesWmOutsourceReceiptDO;
7 10 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourcereceipt.MesWmOutsourceReceiptLineDO;
  11 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
8 12 import cn.iocoder.yudao.module.mes.dal.mysql.wm.outsourcereceipt.MesWmOutsourceReceiptLineMapper;
9 13 import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
10 14 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
  15 +import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
  16 +import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService;
11 17 import jakarta.annotation.Resource;
  18 +import org.springframework.context.annotation.Lazy;
12 19 import org.springframework.stereotype.Service;
  20 +import org.springframework.transaction.annotation.Transactional;
13 21 import org.springframework.validation.annotation.Validated;
14 22  
15 23 import java.util.List;
... ... @@ -30,6 +38,21 @@ public class MesWmOutsourceReceiptLineServiceImpl implements MesWmOutsourceRecei
30 38 @Resource
31 39 private MesMdItemService itemService;
32 40  
  41 + @Resource
  42 + @Lazy
  43 + private MesWmOutsourceReceiptDetailService outsourceReceiptDetailService;
  44 +
  45 + @Resource
  46 + @Lazy
  47 + private MesWmOutsourceReceiptService outsourceReceiptService;
  48 +
  49 + @Resource
  50 + @Lazy
  51 + private MesProWorkOrderService workOrderService;
  52 +
  53 + @Resource
  54 + private MesWmBatchService batchService;
  55 +
33 56 @Override
34 57 public Long createOutsourceReceiptLine(MesWmOutsourceReceiptLineSaveReqVO createReqVO) {
35 58 // 校验物料存在
... ... @@ -37,8 +60,13 @@ public class MesWmOutsourceReceiptLineServiceImpl implements MesWmOutsourceRecei
37 60  
38 61 // 插入
39 62 MesWmOutsourceReceiptLineDO line = BeanUtils.toBean(createReqVO, MesWmOutsourceReceiptLineDO.class);
40   - // 质量状态自动赋值
41   - line.setQualityStatus(calculateQualityStatus(line.getIqcCheckFlag()));
  63 + line.setQualityStatus(calculateQualityStatus(line.getIqcCheckFlag())); // 质量状态自动赋值
  64 + // 生成或获取批次
  65 + MesWmBatchDO batch = generateOrGetBatch(createReqVO);
  66 + if (batch != null) {
  67 + line.setBatchId(batch.getId());
  68 + line.setBatchCode(batch.getCode());
  69 + }
42 70 lineMapper.insert(line);
43 71 return line.getId();
44 72 }
... ... @@ -52,15 +80,24 @@ public class MesWmOutsourceReceiptLineServiceImpl implements MesWmOutsourceRecei
52 80  
53 81 // 更新
54 82 MesWmOutsourceReceiptLineDO updateObj = BeanUtils.toBean(updateReqVO, MesWmOutsourceReceiptLineDO.class);
55   - // 质量状态自动赋值
56   - updateObj.setQualityStatus(calculateQualityStatus(updateObj.getIqcCheckFlag()));
  83 + updateObj.setQualityStatus(calculateQualityStatus(updateObj.getIqcCheckFlag())); // 质量状态自动赋值
  84 + // 生成或获取批次
  85 + MesWmBatchDO batch = generateOrGetBatch(updateReqVO);
  86 + if (batch != null) {
  87 + updateObj.setBatchId(batch.getId());
  88 + updateObj.setBatchCode(batch.getCode());
  89 + }
57 90 lineMapper.updateById(updateObj);
58 91 }
59 92  
60 93 @Override
  94 + @Transactional(rollbackFor = Exception.class)
61 95 public void deleteOutsourceReceiptLine(Long id) {
62 96 // 校验存在
63 97 validateOutsourceReceiptLineExists(id);
  98 +
  99 + // 级联删除明细
  100 + outsourceReceiptDetailService.deleteOutsourceReceiptDetailByLineId(id);
64 101 // 删除
65 102 lineMapper.deleteById(id);
66 103 }
... ... @@ -114,4 +151,30 @@ public class MesWmOutsourceReceiptLineServiceImpl implements MesWmOutsourceRecei
114 151 return MesWmQualityStatusEnum.PASS.getStatus(); // 不需检验,默认合格
115 152 }
116 153  
  154 + /**
  155 + * 生成或获取批次
  156 + *
  157 + * @param reqVO 入库单行请求VO
  158 + * @return 批次记录(如果物料未启用批次管理则返回 null)
  159 + */
  160 + private MesWmBatchDO generateOrGetBatch(MesWmOutsourceReceiptLineSaveReqVO reqVO) {
  161 + MesWmBatchGenerateReqVO batchReqVO = new MesWmBatchGenerateReqVO();
  162 + batchReqVO.setItemId(reqVO.getItemId()).setProduceDate(reqVO.getProductionDate())
  163 + .setExpireDate(reqVO.getExpireDate()).setLotNumber(reqVO.getLotNumber());
  164 + MesWmOutsourceReceiptDO receipt = outsourceReceiptService.getOutsourceReceipt(reqVO.getReceiptId());
  165 + if (receipt != null) {
  166 + batchReqVO.setVendorId(receipt.getVendorId())
  167 + .setWorkOrderId(receipt.getWorkOrderId())
  168 + .setReceiptDate(receipt.getReceiptDate());
  169 + if (receipt.getWorkOrderId() != null) {
  170 + MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(receipt.getWorkOrderId());
  171 + if (workOrder != null) {
  172 + batchReqVO.setClientId(workOrder.getClientId());
  173 + }
  174 + }
  175 + }
  176 + // 调用批次服务生成或获取批次
  177 + return batchService.getOrGenerateBatchCode(batchReqVO);
  178 + }
  179 +
117 180 }
... ...
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;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.util.ObjUtil;
  5 +import cn.hutool.core.util.StrUtil;
5 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 7 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
7 8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
... ... @@ -9,10 +10,10 @@ import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
9 10 import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourcereceipt.vo.MesWmOutsourceReceiptPageReqVO;
10 11 import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourcereceipt.vo.MesWmOutsourceReceiptSaveReqVO;
11 12 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
  13 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
12 14 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourcereceipt.MesWmOutsourceReceiptDO;
13 15 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourcereceipt.MesWmOutsourceReceiptDetailDO;
14 16 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourcereceipt.MesWmOutsourceReceiptLineDO;
15   -
16 17 import cn.iocoder.yudao.module.mes.dal.mysql.wm.outsourcereceipt.MesWmOutsourceReceiptMapper;
17 18 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
18 19 import cn.iocoder.yudao.module.mes.enums.wm.MesWmOutsourceReceiptStatusEnum;
... ... @@ -20,6 +21,7 @@ import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
20 21 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
21 22 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
22 23 import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
  24 +import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
23 25 import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
24 26 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
25 27 import jakarta.annotation.Resource;
... ... @@ -55,6 +57,8 @@ public class MesWmOutsourceReceiptServiceImpl implements MesWmOutsourceReceiptSe
55 57 private MesMdVendorService vendorService;
56 58 @Resource
57 59 private MesMdItemService itemService;
  60 + @Resource
  61 + private MesProWorkOrderService workOrderService;
58 62  
59 63 @Override
60 64 public Long createOutsourceReceipt(MesWmOutsourceReceiptSaveReqVO createReqVO) {
... ... @@ -62,6 +66,10 @@ public class MesWmOutsourceReceiptServiceImpl implements MesWmOutsourceReceiptSe
62 66 validateCodeUnique(null, createReqVO.getCode());
63 67 // 校验供应商存在
64 68 vendorService.validateVendorExists(createReqVO.getVendorId());
  69 + // 校验外协工单存在
  70 + if (createReqVO.getWorkOrderId() != null) {
  71 + workOrderService.validateWorkOrderExists(createReqVO.getWorkOrderId());
  72 + }
65 73  
66 74 // 插入
67 75 MesWmOutsourceReceiptDO receipt = BeanUtils.toBean(createReqVO, MesWmOutsourceReceiptDO.class);
... ... @@ -78,6 +86,10 @@ public class MesWmOutsourceReceiptServiceImpl implements MesWmOutsourceReceiptSe
78 86 validateCodeUnique(updateReqVO.getId(), updateReqVO.getCode());
79 87 // 校验供应商存在
80 88 vendorService.validateVendorExists(updateReqVO.getVendorId());
  89 + // 校验外协工单存在
  90 + if (updateReqVO.getWorkOrderId() != null) {
  91 + workOrderService.validateWorkOrderExists(updateReqVO.getWorkOrderId());
  92 + }
81 93  
82 94 // 更新
83 95 MesWmOutsourceReceiptDO updateObj = BeanUtils.toBean(updateReqVO, MesWmOutsourceReceiptDO.class);
... ... @@ -104,7 +116,15 @@ public class MesWmOutsourceReceiptServiceImpl implements MesWmOutsourceReceiptSe
104 116  
105 117 @Override
106 118 public PageResult<MesWmOutsourceReceiptDO> getOutsourceReceiptPage(MesWmOutsourceReceiptPageReqVO pageReqVO) {
107   - return outsourceReceiptMapper.selectPage(pageReqVO);
  119 + List<Long> workOrderIds = null;
  120 + if (StrUtil.isNotBlank(pageReqVO.getWorkOrderCode())) {
  121 + MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(pageReqVO.getWorkOrderCode());
  122 + if (workOrder == null) {
  123 + return PageResult.empty();
  124 + }
  125 + workOrderIds = java.util.Collections.singletonList(workOrder.getId());
  126 + }
  127 + return outsourceReceiptMapper.selectPage(pageReqVO, workOrderIds);
108 128 }
109 129  
110 130 @Override
... ... @@ -165,16 +185,36 @@ public class MesWmOutsourceReceiptServiceImpl implements MesWmOutsourceReceiptSe
165 185 @Override
166 186 @Transactional(rollbackFor = Exception.class)
167 187 public void finishOutsourceReceipt(Long id) {
168   - // 1. 校验存在
  188 + // 1.1 校验存在
169 189 MesWmOutsourceReceiptDO receipt = validateOutsourceReceiptExists(id);
170 190 if (ObjUtil.notEqual(MesWmOutsourceReceiptStatusEnum.APPROVED.getStatus(), receipt.getStatus())) {
171 191 throw exception(WM_OUTSOURCE_RECEIPT_STATUS_ERROR);
172 192 }
  193 + // 1.2 校验至少有一条行
  194 + List<MesWmOutsourceReceiptLineDO> lines = outsourceReceiptLineService.getOutsourceReceiptLineListByReceiptId(id);
  195 + if (CollUtil.isEmpty(lines)) {
  196 + throw exception(WM_OUTSOURCE_RECEIPT_NO_LINE);
  197 + }
173 198  
174   - // 2. 遍历所有明细,创建库存事务(增加库存 + 记录流水)
  199 + // 3. 遍历所有明细,创建库存事务(增加库存 + 记录流水)
175 200 createTransactionList(receipt);
176 201  
177   - // 3. 更新入库单状态
  202 + // 4. 更新外协工单的已生产数量(只统计合格的目标产品)
  203 + if (receipt.getWorkOrderId() != null) {
  204 + MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(receipt.getWorkOrderId());
  205 + if (workOrder != null) {
  206 + BigDecimal totalQuantity = lines.stream()
  207 + .filter(line -> ObjUtil.equal(line.getItemId(), workOrder.getProductId())) // 只统计目标产品
  208 + .filter(line -> ObjUtil.notEqual(line.getQualityStatus(), MesWmQualityStatusEnum.FAIL.getStatus())) // 排除不合格品
  209 + .map(MesWmOutsourceReceiptLineDO::getQuantity)
  210 + .reduce(BigDecimal.ZERO, BigDecimal::add);
  211 + if (totalQuantity.compareTo(BigDecimal.ZERO) > 0) {
  212 + workOrderService.updateProducedQuantity(receipt.getWorkOrderId(), totalQuantity);
  213 + }
  214 + }
  215 + }
  216 +
  217 + // 5. 更新入库单状态
178 218 outsourceReceiptMapper.updateById(new MesWmOutsourceReceiptDO()
179 219 .setId(id).setStatus(MesWmOutsourceReceiptStatusEnum.FINISHED.getStatus()));
180 220 }
... ...