云
3 天以前 c47c8647f514eaa8c6a7624185b55738e8625c2c
feat(product-issue): 支持按任务工序校验和选择领料物料

- 修改物料不在BOM中的错误提示信息以包含任务工序
- 新增根据任务ID获取工序投入物料的后端接口
- 领料单行保存时增加基于任务工序BOM的物料校验逻辑
- 前端领料单行表单物料选择改为基于当前工序投入物料
- 新增前端获取任务工序投入物料的API请求方法
- 移除排产表单中多余的完成订单按钮及相关逻辑
已修改3个文件
35 ■■■■■ 文件已修改
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderBomController.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueLineServiceImpl.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderBomController.java
@@ -18,6 +18,8 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockMapper;
import cn.iocoder.yudao.module.erp.api.purchase.PurchaseOrderApi;
@@ -51,6 +53,9 @@
@RequestMapping("/mes/pro/work-order-bom")
@Validated
public class MesProWorkOrderBomController {
    @Resource
    private MesProTaskService taskService;
    @Resource
    private MesProWorkOrderBomService workOrderBomService;
@@ -115,6 +120,18 @@
        return success(new PageResult<>(buildWorkOrderBomRespVOList(pageResult.getList()), pageResult.getTotal()));
    }
    @GetMapping("/list-by-task-id")
    @Operation(summary = "获得生产任务工序的投入物料")
    public CommonResult<List<MesProWorkOrderBomRespVO>> getWorkOrderBomListByTaskId(@RequestParam("taskId") Long taskId) {
        MesProTaskDO task = taskService.validateTaskExists(taskId);
        if (task.getWorkOrderId() == null || task.getProcessId() == null) {
            return success(Collections.emptyList());
        }
        List<MesProWorkOrderBomDO> list = workOrderBomService.getWorkOrderBomListByWorkOrderId(task.getWorkOrderId())
                .stream().filter(bom -> Objects.equals(bom.getProcessId(), task.getProcessId())).toList();
        return success(buildWorkOrderBomRespVOList(list));
    }
    @GetMapping("/item-list-by-work-order-id")
    @Operation(summary = "获得工单物料需求列表")
    @Parameter(name = "workOrderId", description = "工单编号", required = true)
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -614,7 +614,7 @@
    ErrorCode WM_PRODUCT_ISSUE_WORKSTATION_NOT_EXISTS = new ErrorCode(1_040_708_005, "工作站不存在");
    ErrorCode WM_PRODUCT_ISSUE_CANCEL_NOT_ALLOWED = new ErrorCode(1_040_708_006, "生产领料出库单已完成或已取消,无法取消");
    ErrorCode WM_PRODUCT_ISSUE_LINE_NOT_EXISTS = new ErrorCode(1_040_708_100, "生产领料出库单行不存在");
    ErrorCode WM_PRODUCT_ISSUE_LINE_ITEM_NOT_IN_BOM = new ErrorCode(1_040_708_101, "当前物料不在生产工单的 BOM 物料清单中");
    ErrorCode WM_PRODUCT_ISSUE_LINE_ITEM_NOT_IN_BOM = new ErrorCode(1_040_708_101, "当前物料不在生产工单或当前任务工序的 BOM 物料清单中");
    ErrorCode WM_PRODUCT_ISSUE_CODE_DUPLICATE = new ErrorCode(1_040_708_102, "领料出库单编码已存在");
    ErrorCode WM_PRODUCT_ISSUE_DETAIL_NOT_EXISTS = new ErrorCode(1_040_708_200, "生产领料出库单明细不存在");
    ErrorCode WM_PRODUCT_ISSUE_DETAIL_ITEM_MISMATCH = new ErrorCode(1_040_708_201, "拣货明细的物料与领料单行的物料不一致");
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueLineServiceImpl.java
@@ -6,6 +6,8 @@
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.MesWmProductIssueLineSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
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.mysql.wm.productissue.MesWmProductIssueLineMapper;
@@ -17,6 +19,7 @@
import org.springframework.validation.annotation.Validated;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_PRODUCT_ISSUE_LINE_ITEM_NOT_IN_BOM;
@@ -39,6 +42,8 @@
    private MesMdmItemService mdmItemService;
    @Resource
    private MesProWorkOrderBomService workOrderBomService;
    @Resource
    private MesProTaskService taskService;
    @Resource
    @Lazy
    private MesWmProductIssueDetailService issueDetailService;
@@ -120,6 +125,17 @@
    private void validateItemInWorkOrderBom(Long issueId, Long itemId) {
        // 校验领料单存在,并获取工单编号
        MesWmProductIssueDO issue = issueService.validateProductIssueExists(issueId);
        if (issue.getTaskId() != null) {
            MesProTaskDO task = taskService.validateTaskExists(issue.getTaskId());
            if (task.getWorkOrderId() == null || task.getProcessId() == null
                    || !Objects.equals(issue.getWorkOrderId(), task.getWorkOrderId())
                    || workOrderBomService.getWorkOrderBomListByWorkOrderId(task.getWorkOrderId()).stream()
                    .noneMatch(bom -> Objects.equals(bom.getProcessId(), task.getProcessId())
                            && Objects.equals(bom.getItemId(), itemId))) {
                throw exception(WM_PRODUCT_ISSUE_LINE_ITEM_NOT_IN_BOM);
            }
            return;
        }
        // 校验物料是否在工单 BOM 中(防错料)
        List<MesProWorkOrderBomDO> workOrderBoms = workOrderBomService.getWorkOrderBomListByWorkOrderId(issue.getWorkOrderId());
        if (CollUtil.isEmpty(workOrderBoms)) {