gaoluyang
6 天以前 7bd10d34e8a8dc2437ec8551eb511e37634d7306
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderBomController.java
@@ -25,6 +25,7 @@
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService;
import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
import cn.iocoder.yudao.module.mes.service.pro.material.MesProMaterialRequirementService;
import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductBomService;
import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderBomService;
@@ -32,6 +33,7 @@
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
@@ -72,6 +74,8 @@
    private MesWmMaterialStockMapper materialStockMapper;
    @Resource
    private ErpPurchaseOrderApi purchaseOrderApi;
    @Resource
    private MesProMaterialRequirementService materialRequirementService;
    @PostMapping("/create")
    @Operation(summary = "创建工单 BOM")
@@ -100,7 +104,6 @@
    @GetMapping("/get")
    @Operation(summary = "获得工单 BOM")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:pro-work-order:query')")
    public CommonResult<MesProWorkOrderBomRespVO> getWorkOrderBom(@RequestParam("id") Long id) {
        MesProWorkOrderBomDO workOrderBom = workOrderBomService.getWorkOrderBom(id);
        if (workOrderBom == null) {
@@ -111,16 +114,27 @@
    @GetMapping("/page")
    @Operation(summary = "获得工单 BOM 分页")
    @PreAuthorize("@ss.hasPermission('mes:pro-work-order:query')")
    public CommonResult<PageResult<MesProWorkOrderBomRespVO>> getWorkOrderBomPage(@Valid MesProWorkOrderBomPageReqVO pageReqVO) {
        PageResult<MesProWorkOrderBomDO> pageResult = workOrderBomService.getWorkOrderBomPage(pageReqVO);
        return success(new PageResult<>(buildWorkOrderBomRespVOList(pageResult.getList()), pageResult.getTotal()));
    }
    @GetMapping("/item-list-preview")
    @Operation(summary = "按产品和数量预览辅料需求(新增工单时判断库存是否充足)")
    @Parameters({
            @Parameter(name = "productId", description = "产品编号", required = true),
            @Parameter(name = "quantity", description = "生产数量", required = true)
    })
    @PreAuthorize("@ss.hasPermission('mes:pro-work-order:query')")
    public CommonResult<List<MesProWorkOrderItemRespVO>> previewItemList(
            @RequestParam("productId") Long productId,
            @RequestParam("quantity") BigDecimal quantity) {
        return success(materialRequirementService.getItemListByProduct(productId, quantity));
    }
    @GetMapping("/item-list-by-work-order-id")
    @Operation(summary = "获得工单物料需求列表")
    @Parameter(name = "workOrderId", description = "工单编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pro-work-order:query')")
    public CommonResult<List<MesProWorkOrderItemRespVO>> getWorkOrderBomItemListByWorkOrderId(
            @RequestParam("workOrderId") Long workOrderId) {
        // 1. 获取工单
@@ -129,19 +143,15 @@
            return success(Collections.emptyList());
        }
        // 2. 获取工单工序列表(含各工序的投料明细与产出品)
        List<MesProWorkOrderProcessDO> processList = workOrderProcessService
                .getWorkOrderProcessListByWorkOrderId(workOrderId);
        if (CollUtil.isEmpty(processList)) {
            return success(Collections.emptyList());
        }
        // 3. 获取工单 BOM 行(各工序的投料记录,按 processId 分组使用)
        // 2. 获取工单 BOM 行(各工序的投料记录,单耗口径)
        List<MesProWorkOrderBomDO> bomList = workOrderBomService.getWorkOrderBomListByWorkOrderId(workOrderId);
        if (CollUtil.isEmpty(bomList)) {
            return success(Collections.emptyList());
        }
        // 3. 获取工单工序列表(含各工序的投料明细与产出品)
        List<MesProWorkOrderProcessDO> processList = workOrderProcessService
                .getWorkOrderProcessListByWorkOrderId(workOrderId);
        // 4. 通过产品ID反查工艺路线(工单 routeId 可能为空)
        Long routeId = null;
@@ -153,7 +163,22 @@
        }
        // 5. 按工序产出链逐层展开 BOM,得到叶子物料(每单位成品的用量)
        Map<Long, BigDecimal> leafItems = buildWorkOrderItems(processList, bomList, routeId);
        Map<Long, BigDecimal> leafItems = CollUtil.isEmpty(processList)
                ? Collections.emptyMap()
                : buildWorkOrderItems(processList, bomList, routeId);
        // 5.1 回退:工单没有工序,或投料未挂在最后一道工序上(例如投料配置在首道配料工序)时,
        //     逐层展开会得到空结果。此时按全部投料行汇总单耗,与「按工单生成领料单」的取数口径保持一致,
        //     否则工单明明有投料、领料也领过了,打印领料单却显示「暂无辅料需求」。
        if (CollUtil.isEmpty(leafItems)) {
            Map<Long, BigDecimal> allBomItems = new LinkedHashMap<>();
            for (MesProWorkOrderBomDO bom : bomList) {
                if (bom.getItemId() == null || bom.getQuantity() == null) {
                    continue;
                }
                allBomItems.merge(bom.getItemId(), bom.getQuantity(), BigDecimal::add);
            }
            leafItems = allBomItems;
        }
        if (CollUtil.isEmpty(leafItems)) {
            return success(Collections.emptyList());
        }