package cn.iocoder.yudao.module.mes.service.wm.productissue; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.line.MesWmProductIssueLinePageReqVO; import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.line.MesWmProductIssueLineRespVO; import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.line.MesWmProductIssueLineReturnableRespVO; import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.line.MesWmProductIssueLineSaveReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productissue.MesWmProductIssueDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productissue.MesWmProductIssueLineDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnissue.MesWmReturnIssueLineDO; import cn.iocoder.yudao.module.mes.dal.mysql.wm.productissue.MesWmProductIssueLineMapper; import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductIssueStatusEnum; import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService; import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderBomService; import cn.iocoder.yudao.module.mes.service.wm.productissue.MesWmProductIssueDetailService; import cn.iocoder.yudao.module.mes.service.wm.productissue.MesWmProductIssueLineService; import cn.iocoder.yudao.module.mes.service.wm.productissue.MesWmProductIssueService; import cn.iocoder.yudao.module.mes.service.wm.returnissue.MesWmReturnIssueLineService; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; /** * MES 领料出库单行 Service 实现类 */ @Service @Validated public class MesWmProductIssueLineServiceImpl implements MesWmProductIssueLineService { @Resource private MesWmProductIssueLineMapper issueLineMapper; @Resource @Lazy private MesWmProductIssueService issueService; @Resource private MesMdItemService mdmItemService; @Resource @Lazy private MesWmReturnIssueLineService returnIssueLineService; @Resource private MesMdUnitMeasureService unitMeasureService; @Resource @Lazy private MesWmProductIssueDetailService issueDetailService; @Resource private MesProWorkOrderBomService workOrderBomService; @Override public Long createProductIssueLine(MesWmProductIssueLineSaveReqVO createReqVO) { validateProductIssueLineSaveData(createReqVO); // 插入 MesWmProductIssueLineDO line = BeanUtils.toBean(createReqVO, MesWmProductIssueLineDO.class); issueLineMapper.insert(line); return line.getId(); } @Override public void updateProductIssueLine(MesWmProductIssueLineSaveReqVO updateReqVO) { // 校验存在 validateProductIssueLineExists(updateReqVO.getId()); validateProductIssueLineSaveData(updateReqVO); // 更新 MesWmProductIssueLineDO updateObj = BeanUtils.toBean(updateReqVO, MesWmProductIssueLineDO.class); issueLineMapper.updateById(updateObj); } @Override public void deleteProductIssueLine(Long id) { // 校验存在 MesWmProductIssueLineDO line = validateProductIssueLineExists(id); // 校验主单为草稿状态才允许删除行 issueService.validateProductIssueExistsAndPrepare(line.getIssueId()); // 级联删除该行的明细 issueDetailService.deleteProductIssueDetailByLineId(id); // 删除行 issueLineMapper.deleteById(id); } @Override public MesWmProductIssueLineDO getProductIssueLine(Long id) { return issueLineMapper.selectById(id); } @Override public PageResult getProductIssueLinePage(MesWmProductIssueLinePageReqVO pageReqVO) { return issueLineMapper.selectPage(pageReqVO); } @Override public List getProductIssueLineListByIssueId(Long issueId) { return issueLineMapper.selectListByIssueId(issueId); } @Override public void deleteProductIssueLineByIssueId(Long issueId) { issueLineMapper.deleteByIssueId(issueId); } @Override public MesWmProductIssueLineDO validateProductIssueLineExists(Long id) { MesWmProductIssueLineDO line = issueLineMapper.selectById(id); if (line == null) { throw exception(WM_PRODUCT_ISSUE_LINE_NOT_EXISTS); } return line; } /** * 校验保存时的关联数据 */ private void validateProductIssueLineSaveData(MesWmProductIssueLineSaveReqVO reqVO) { // 校验父数据存在 + 草稿状态才允许新增/修改行 issueService.validateProductIssueExistsAndPrepare(reqVO.getIssueId()); // 校验物料在工单 BOM 中 validateItemInWorkOrderBom(reqVO.getIssueId(), reqVO.getItemId()); // 校验物料存在 mdmItemService.validateItemExists(reqVO.getItemId()); } private void validateItemInWorkOrderBom(Long issueId, Long itemId) { // 校验领料单存在,并获取工单编号 MesWmProductIssueDO issue = issueService.validateProductIssueExists(issueId); // 校验物料是否在工单 BOM 中(防错料) List workOrderBoms = workOrderBomService.getWorkOrderBomListByWorkOrderId(issue.getWorkOrderId()); if (CollUtil.isEmpty(workOrderBoms)) { return; } MesProWorkOrderBomDO workOrderBom = CollUtil.findOne(workOrderBoms, bom -> bom.getItemId().equals(itemId)); if (workOrderBom == null) { throw exception(WM_PRODUCT_ISSUE_LINE_ITEM_NOT_IN_BOM); } } @Override public List getReturnableList(Long workOrderId) { // 1. 查询该工单下已完成的领料单 List finishedIssues = issueService.getFinishedIssuesByWorkOrderId(workOrderId); if (CollUtil.isEmpty(finishedIssues)) { return Collections.emptyList(); } // 2. 获取这些领料单的所有行 List issueIds = CollectionUtils.convertList(finishedIssues, MesWmProductIssueDO::getId); List lines = issueLineMapper.selectListByIssueIds(issueIds); if (CollUtil.isEmpty(lines)) { return Collections.emptyList(); } // 3. 批量查询已退数量(按 issueLineId 汇总) List lineIds = CollectionUtils.convertList(lines, MesWmProductIssueLineDO::getId); List returnLines = returnIssueLineService.getReturnIssueLineListByIssueLineIds(lineIds); // 按 issueLineId 汇总退料数量 Map returnedQtyMap = new java.util.HashMap<>(); for (MesWmReturnIssueLineDO returnLine : returnLines) { if (returnLine.getIssueLineId() != null && returnLine.getQuantity() != null) { returnedQtyMap.merge(returnLine.getIssueLineId(), returnLine.getQuantity(), BigDecimal::add); } } // 4. 批量查询物料和单位信息 Map itemMap = mdmItemService.getItemMap( CollectionUtils.convertSet(lines, MesWmProductIssueLineDO::getItemId)); Map unitMap = unitMeasureService.getUnitMeasureMap( CollectionUtils.convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId)); // 5. 组装结果,过滤可退数量 > 0 的行 List result = new ArrayList<>(); for (MesWmProductIssueLineDO line : lines) { BigDecimal issuedQty = line.getQuantity() != null ? line.getQuantity() : BigDecimal.ZERO; BigDecimal returnedQty = returnedQtyMap.getOrDefault(line.getId(), BigDecimal.ZERO); BigDecimal returnableQty = issuedQty.subtract(returnedQty); if (returnableQty.compareTo(BigDecimal.ZERO) > 0) { MesWmProductIssueLineReturnableRespVO vo = new MesWmProductIssueLineReturnableRespVO(); vo.setId(line.getId()); vo.setItemId(line.getItemId()); vo.setBatchId(line.getBatchId()); vo.setQuantity(returnableQty); // 填充物料信息 MesMdItemDO item = itemMap.get(line.getItemId()); if (item != null) { vo.setItemCode(item.getCode()); vo.setItemName(item.getName()); vo.setSpecification(item.getSpecification()); // 填充单位信息 MesMdUnitMeasureDO unit = unitMap.get(item.getUnitMeasureId()); vo.setUnitMeasureName(unit != null ? unit.getName() : null); } result.add(vo); } } return result; } }