Commit 571d32632ebf23cf56f162fd341fa38a8ce62203
1 parent
fce023ad
✨ feat(mes): 新增批次生成逻辑及相关字段处理
实现批次的生成或获取逻辑,自动关联外协工单和物料信息。更新了相关数据对象和服务实现,确保在创建和更新外协工单行时能够正确处理批次信息。
Showing
4 changed files
with
120 additions
and
16 deletions
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 | } | ... | ... |