Commit f163e105e6d3ebbb15218c4399968bfe36a09752

Authored by YunaiV
1 parent a0bc813b

feat(mes-pro): 新增生产工单模块,从 ktg-mes 迁移 pro/workorder

- 新增 mes_pro_workorder、mes_pro_workorder_bom 两张表(含菜单、字典数据)
- 新增 MesProWorkorderDO/BomDO 数据对象
- 新增工单状态/类型/来源类型 3 个枚举,ErrorCode 和 DictType 常量
- 新增 Workorder/Bom 的 VO、Mapper、Service、Controller 全套 CRUD
- 创建工单时自动根据产品 BOM 生成工单 BOM 行
- 支持工单完成/取消状态流转,仅草稿状态可删除
- 去除 ktg-mes 冗余字段,varchar 枚举改 tinyint
Showing 19 changed files with 1587 additions and 0 deletions
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkorderBomController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.pro.workorder;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  7 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  8 +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom.MesProWorkorderBomPageReqVO;
  9 +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom.MesProWorkorderBomRespVO;
  10 +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom.MesProWorkorderBomSaveReqVO;
  11 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
  12 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
  13 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkorderBomDO;
  14 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
  15 +import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
  16 +import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkorderBomService;
  17 +import io.swagger.v3.oas.annotations.Operation;
  18 +import io.swagger.v3.oas.annotations.Parameter;
  19 +import io.swagger.v3.oas.annotations.tags.Tag;
  20 +import jakarta.annotation.Resource;
  21 +import jakarta.validation.Valid;
  22 +import org.springframework.security.access.prepost.PreAuthorize;
  23 +import org.springframework.validation.annotation.Validated;
  24 +import org.springframework.web.bind.annotation.*;
  25 +
  26 +import java.util.Collections;
  27 +import java.util.List;
  28 +import java.util.Map;
  29 +
  30 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  31 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  32 +
  33 +@Tag(name = "管理后台 - MES 生产工单 BOM")
  34 +@RestController
  35 +@RequestMapping("/mes/pro/workorder-bom")
  36 +@Validated
  37 +public class MesProWorkorderBomController {
  38 +
  39 + @Resource
  40 + private MesProWorkorderBomService workorderBomService;
  41 +
  42 + @Resource
  43 + private MesMdItemService itemService;
  44 +
  45 + @Resource
  46 + private MesMdUnitMeasureService unitMeasureService;
  47 +
  48 + @PostMapping("/create")
  49 + @Operation(summary = "创建工单 BOM")
  50 + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:update')")
  51 + public CommonResult<Long> createWorkorderBom(@Valid @RequestBody MesProWorkorderBomSaveReqVO createReqVO) {
  52 + return success(workorderBomService.createWorkorderBom(createReqVO));
  53 + }
  54 +
  55 + @PutMapping("/update")
  56 + @Operation(summary = "更新工单 BOM")
  57 + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:update')")
  58 + public CommonResult<Boolean> updateWorkorderBom(@Valid @RequestBody MesProWorkorderBomSaveReqVO updateReqVO) {
  59 + workorderBomService.updateWorkorderBom(updateReqVO);
  60 + return success(true);
  61 + }
  62 +
  63 + @DeleteMapping("/delete")
  64 + @Operation(summary = "删除工单 BOM")
  65 + @Parameter(name = "id", description = "编号", required = true)
  66 + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:update')")
  67 + public CommonResult<Boolean> deleteWorkorderBom(@RequestParam("id") Long id) {
  68 + workorderBomService.deleteWorkorderBom(id);
  69 + return success(true);
  70 + }
  71 +
  72 + @GetMapping("/get")
  73 + @Operation(summary = "获得工单 BOM")
  74 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  75 + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:query')")
  76 + public CommonResult<MesProWorkorderBomRespVO> getWorkorderBom(@RequestParam("id") Long id) {
  77 + MesProWorkorderBomDO workorderBom = workorderBomService.getWorkorderBom(id);
  78 + if (workorderBom == null) {
  79 + return success(null);
  80 + }
  81 + return success(buildWorkorderBomRespVOList(List.of(workorderBom)).get(0));
  82 + }
  83 +
  84 + @GetMapping("/page")
  85 + @Operation(summary = "获得工单 BOM 分页")
  86 + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:query')")
  87 + public CommonResult<PageResult<MesProWorkorderBomRespVO>> getWorkorderBomPage(@Valid MesProWorkorderBomPageReqVO pageReqVO) {
  88 + PageResult<MesProWorkorderBomDO> pageResult = workorderBomService.getWorkorderBomPage(pageReqVO);
  89 + return success(new PageResult<>(buildWorkorderBomRespVOList(pageResult.getList()), pageResult.getTotal()));
  90 + }
  91 +
  92 + // ==================== 拼接 VO ====================
  93 +
  94 + private List<MesProWorkorderBomRespVO> buildWorkorderBomRespVOList(List<MesProWorkorderBomDO> list) {
  95 + if (CollUtil.isEmpty(list)) {
  96 + return Collections.emptyList();
  97 + }
  98 + // 1. 批量获取关联数据
  99 + Map<Long, MesMdItemDO> itemMap = itemService.getItemMap(
  100 + convertSet(list, MesProWorkorderBomDO::getItemId));
  101 + Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap(
  102 + convertSet(list, MesProWorkorderBomDO::getUnitMeasureId));
  103 + // 2. 拼接 VO
  104 + return BeanUtils.toBean(list, MesProWorkorderBomRespVO.class, vo -> {
  105 + MapUtils.findAndThen(itemMap, vo.getItemId(), item -> {
  106 + // TODO @AI:少了 spec 字段?另外,改成链式调用;
  107 + vo.setItemName(item.getName());
  108 + vo.setItemCode(item.getCode());
  109 + vo.setItemSpec(item.getSpec());
  110 + });
  111 + MapUtils.findAndThen(unitMeasureMap, vo.getUnitMeasureId(),
  112 + unitMeasure -> vo.setUnitMeasureName(unitMeasure.getName()));
  113 + });
  114 + }
  115 +
  116 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkorderController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.pro.workorder;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
  5 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  7 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  8 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  9 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  10 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  11 +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkorderPageReqVO;
  12 +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkorderRespVO;
  13 +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkorderSaveReqVO;
  14 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
  15 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
  16 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
  17 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
  18 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkorderDO;
  19 +import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
  20 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
  21 +import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
  22 +import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
  23 +import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkorderService;
  24 +import io.swagger.v3.oas.annotations.Operation;
  25 +import io.swagger.v3.oas.annotations.Parameter;
  26 +import io.swagger.v3.oas.annotations.tags.Tag;
  27 +import jakarta.annotation.Resource;
  28 +import jakarta.servlet.http.HttpServletResponse;
  29 +import jakarta.validation.Valid;
  30 +import org.springframework.security.access.prepost.PreAuthorize;
  31 +import org.springframework.validation.annotation.Validated;
  32 +import org.springframework.web.bind.annotation.*;
  33 +
  34 +import java.io.IOException;
  35 +import java.util.Collections;
  36 +import java.util.List;
  37 +import java.util.Map;
  38 +
  39 +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
  40 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  41 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  42 +
  43 +@Tag(name = "管理后台 - MES 生产工单")
  44 +@RestController
  45 +@RequestMapping("/mes/pro/workorder")
  46 +@Validated
  47 +public class MesProWorkorderController {
  48 +
  49 + @Resource
  50 + private MesProWorkorderService workorderService;
  51 +
  52 + @Resource
  53 + private MesMdItemService itemService;
  54 +
  55 + @Resource
  56 + private MesMdClientService clientService;
  57 +
  58 + @Resource
  59 + private MesMdVendorService vendorService;
  60 +
  61 + @Resource
  62 + private MesMdUnitMeasureService unitMeasureService;
  63 +
  64 + @PostMapping("/create")
  65 + @Operation(summary = "创建生产工单")
  66 + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:create')")
  67 + public CommonResult<Long> createWorkorder(@Valid @RequestBody MesProWorkorderSaveReqVO createReqVO) {
  68 + return success(workorderService.createWorkorder(createReqVO));
  69 + }
  70 +
  71 + @PutMapping("/update")
  72 + @Operation(summary = "更新生产工单")
  73 + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:update')")
  74 + public CommonResult<Boolean> updateWorkorder(@Valid @RequestBody MesProWorkorderSaveReqVO updateReqVO) {
  75 + workorderService.updateWorkorder(updateReqVO);
  76 + return success(true);
  77 + }
  78 +
  79 + @DeleteMapping("/delete")
  80 + @Operation(summary = "删除生产工单")
  81 + @Parameter(name = "id", description = "编号", required = true)
  82 + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:delete')")
  83 + public CommonResult<Boolean> deleteWorkorder(@RequestParam("id") Long id) {
  84 + workorderService.deleteWorkorder(id);
  85 + return success(true);
  86 + }
  87 +
  88 + @GetMapping("/get")
  89 + @Operation(summary = "获得生产工单")
  90 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  91 + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:query')")
  92 + public CommonResult<MesProWorkorderRespVO> getWorkorder(@RequestParam("id") Long id) {
  93 + MesProWorkorderDO workorder = workorderService.getWorkorder(id);
  94 + if (workorder == null) {
  95 + return success(null);
  96 + }
  97 + return success(buildWorkorderRespVOList(List.of(workorder)).get(0));
  98 + }
  99 +
  100 + @GetMapping("/page")
  101 + @Operation(summary = "获得生产工单分页")
  102 + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:query')")
  103 + public CommonResult<PageResult<MesProWorkorderRespVO>> getWorkorderPage(@Valid MesProWorkorderPageReqVO pageReqVO) {
  104 + PageResult<MesProWorkorderDO> pageResult = workorderService.getWorkorderPage(pageReqVO);
  105 + return success(new PageResult<>(buildWorkorderRespVOList(pageResult.getList()), pageResult.getTotal()));
  106 + }
  107 +
  108 + @GetMapping("/export-excel")
  109 + @Operation(summary = "导出生产工单 Excel")
  110 + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:export')")
  111 + @ApiAccessLog(operateType = EXPORT)
  112 + public void exportWorkorderExcel(@Valid MesProWorkorderPageReqVO pageReqVO,
  113 + HttpServletResponse response) throws IOException {
  114 + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
  115 + List<MesProWorkorderDO> list = workorderService.getWorkorderPage(pageReqVO).getList();
  116 + List<MesProWorkorderRespVO> voList = buildWorkorderRespVOList(list);
  117 + ExcelUtils.write(response, "生产工单.xls", "数据", MesProWorkorderRespVO.class, voList);
  118 + }
  119 +
  120 + @PutMapping("/finish")
  121 + @Operation(summary = "完成工单")
  122 + @Parameter(name = "id", description = "编号", required = true)
  123 + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:update')")
  124 + public CommonResult<Boolean> finishWorkorder(@RequestParam("id") Long id) {
  125 + workorderService.finishWorkorder(id);
  126 + return success(true);
  127 + }
  128 +
  129 + @PutMapping("/cancel")
  130 + @Operation(summary = "取消工单")
  131 + @Parameter(name = "id", description = "编号", required = true)
  132 + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:update')")
  133 + public CommonResult<Boolean> cancelWorkorder(@RequestParam("id") Long id) {
  134 + workorderService.cancelWorkorder(id);
  135 + return success(true);
  136 + }
  137 +
  138 + // ==================== 拼接 VO ====================
  139 +
  140 + @SuppressWarnings("CodeBlock2Expr")
  141 + private List<MesProWorkorderRespVO> buildWorkorderRespVOList(List<MesProWorkorderDO> list) {
  142 + if (CollUtil.isEmpty(list)) {
  143 + return Collections.emptyList();
  144 + }
  145 + // 1. 批量获取关联数据
  146 + Map<Long, MesMdItemDO> itemMap = itemService.getItemMap(
  147 + convertSet(list, MesProWorkorderDO::getProductId));
  148 + Map<Long, MesMdClientDO> clientMap = clientService.getClientMap(
  149 + convertSet(list, MesProWorkorderDO::getClientId));
  150 + Map<Long, MesMdVendorDO> vendorMap = vendorService.getVendorMap(
  151 + convertSet(list, MesProWorkorderDO::getVendorId));
  152 + Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap(
  153 + convertSet(list, MesProWorkorderDO::getUnitMeasureId));
  154 + // 2. 拼接 VO
  155 + return BeanUtils.toBean(list, MesProWorkorderRespVO.class, vo -> {
  156 + MapUtils.findAndThen(itemMap, vo.getProductId(), item -> {
  157 + // TODO @AI:少了 spec 字段?另外,改成链式调用;
  158 + vo.setProductName(item.getName());
  159 + vo.setProductCode(item.getCode());
  160 + vo.setProductSpec(item.getSpec());
  161 + });
  162 + MapUtils.findAndThen(clientMap, vo.getClientId(),
  163 + client -> vo.setClientName(client.getName()));
  164 + MapUtils.findAndThen(vendorMap, vo.getVendorId(),
  165 + vendor -> vo.setVendorName(vendor.getName()));
  166 + MapUtils.findAndThen(unitMeasureMap, vo.getUnitMeasureId(),
  167 + unitMeasure -> vo.setUnitMeasureName(unitMeasure.getName()));
  168 + });
  169 + }
  170 +
  171 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/MesProWorkorderPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +
  10 +import java.time.LocalDateTime;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  13 +
  14 +@Schema(description = "管理后台 - MES 生产工单分页 Request VO")
  15 +@Data
  16 +@EqualsAndHashCode(callSuper = true)
  17 +@ToString(callSuper = true)
  18 +public class MesProWorkorderPageReqVO extends PageParam {
  19 +
  20 + @Schema(description = "工单编码", example = "WO-001")
  21 + private String code;
  22 +
  23 + @Schema(description = "工单名称", example = "生产工单-A")
  24 + private String name;
  25 +
  26 + @Schema(description = "工单类型", example = "1")
  27 + private Integer type;
  28 +
  29 + @Schema(description = "来源类型", example = "1")
  30 + private Integer orderSourceType;
  31 +
  32 + @Schema(description = "产品编号", example = "100")
  33 + private Long productId;
  34 +
  35 + @Schema(description = "客户编号", example = "300")
  36 + private Long clientId;
  37 +
  38 + @Schema(description = "工单状态", example = "0")
  39 + private Integer status;
  40 +
  41 + @Schema(description = "需求日期")
  42 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  43 + private LocalDateTime[] requestDate;
  44 +
  45 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/MesProWorkorderRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
  4 +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
  5 +import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
  6 +import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
  7 +import cn.idev.excel.annotation.ExcelProperty;
  8 +import io.swagger.v3.oas.annotations.media.Schema;
  9 +import lombok.Data;
  10 +
  11 +import java.math.BigDecimal;
  12 +import java.time.LocalDateTime;
  13 +
  14 +@Schema(description = "管理后台 - MES 生产工单 Response VO")
  15 +@Data
  16 +@ExcelIgnoreUnannotated
  17 +public class MesProWorkorderRespVO {
  18 +
  19 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  20 + @ExcelProperty("编号")
  21 + private Long id;
  22 +
  23 + @Schema(description = "工单编码", example = "WO-001")
  24 + @ExcelProperty("工单编码")
  25 + private String code;
  26 +
  27 + @Schema(description = "工单名称", example = "生产工单-A")
  28 + @ExcelProperty("工单名称")
  29 + private String name;
  30 +
  31 + @Schema(description = "工单类型", example = "1")
  32 + @ExcelProperty(value = "工单类型", converter = DictConvert.class)
  33 + @DictFormat(DictTypeConstants.MES_PRO_WORKORDER_TYPE)
  34 + private Integer type;
  35 +
  36 + @Schema(description = "来源类型", example = "1")
  37 + @ExcelProperty(value = "来源类型", converter = DictConvert.class)
  38 + @DictFormat(DictTypeConstants.MES_PRO_WORKORDER_SOURCE_TYPE)
  39 + private Integer orderSourceType;
  40 +
  41 + @Schema(description = "来源单据编号", example = "SO-001")
  42 + @ExcelProperty("来源单据编号")
  43 + private String orderSourceCode;
  44 +
  45 + @Schema(description = "产品编号", example = "100")
  46 + private Long productId;
  47 +
  48 + @Schema(description = "产品名称", example = "电路板 A")
  49 + @ExcelProperty("产品名称")
  50 + private String productName;
  51 +
  52 + @Schema(description = "产品编码", example = "P-001")
  53 + @ExcelProperty("产品编码")
  54 + private String productCode;
  55 +
  56 + @Schema(description = "规格型号", example = "100x200mm")
  57 + @ExcelProperty("规格型号")
  58 + private String productSpec;
  59 +
  60 + @Schema(description = "单位编号", example = "200")
  61 + private Long unitMeasureId;
  62 +
  63 + @Schema(description = "单位名称", example = "个")
  64 + @ExcelProperty("单位")
  65 + private String unitMeasureName;
  66 +
  67 + @Schema(description = "生产数量", example = "100.00")
  68 + @ExcelProperty("生产数量")
  69 + private BigDecimal quantity;
  70 +
  71 + @Schema(description = "已生产数量", example = "50.00")
  72 + @ExcelProperty("已生产数量")
  73 + private BigDecimal quantityProduced;
  74 +
  75 + @Schema(description = "调整数量", example = "0")
  76 + @ExcelProperty("调整数量")
  77 + private BigDecimal quantityChanged;
  78 +
  79 + @Schema(description = "已排产数量", example = "80.00")
  80 + @ExcelProperty("已排产数量")
  81 + private BigDecimal quantityScheduled;
  82 +
  83 + @Schema(description = "客户编号", example = "300")
  84 + private Long clientId;
  85 +
  86 + @Schema(description = "客户名称", example = "客户 A")
  87 + @ExcelProperty("客户名称")
  88 + private String clientName;
  89 +
  90 + @Schema(description = "供应商编号", example = "400")
  91 + private Long vendorId;
  92 +
  93 + @Schema(description = "供应商名称", example = "供应商 A")
  94 + @ExcelProperty("供应商名称")
  95 + private String vendorName;
  96 +
  97 + @Schema(description = "批次号", example = "BATCH-001")
  98 + @ExcelProperty("批次号")
  99 + private String batchCode;
  100 +
  101 + @Schema(description = "需求日期")
  102 + @ExcelProperty("需求日期")
  103 + private LocalDateTime requestDate;
  104 +
  105 + @Schema(description = "父工单编号", example = "0")
  106 + private Long parentId;
  107 +
  108 + @Schema(description = "所有父节点编号", example = "0")
  109 + private String ancestors;
  110 +
  111 + @Schema(description = "完成时间")
  112 + @ExcelProperty("完成时间")
  113 + private LocalDateTime finishDate;
  114 +
  115 + @Schema(description = "取消时间")
  116 + @ExcelProperty("取消时间")
  117 + private LocalDateTime cancelDate;
  118 +
  119 + @Schema(description = "工单状态", example = "0")
  120 + @ExcelProperty(value = "工单状态", converter = DictConvert.class)
  121 + @DictFormat(DictTypeConstants.MES_PRO_WORKORDER_STATUS)
  122 + private Integer status;
  123 +
  124 + @Schema(description = "备注", example = "备注")
  125 + @ExcelProperty("备注")
  126 + private String remark;
  127 +
  128 + @Schema(description = "预留字段1")
  129 + private String attribute1;
  130 +
  131 + @Schema(description = "预留字段2")
  132 + private String attribute2;
  133 +
  134 + @Schema(description = "预留字段3")
  135 + private Integer attribute3;
  136 +
  137 + @Schema(description = "预留字段4")
  138 + private Integer attribute4;
  139 +
  140 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  141 + @ExcelProperty("创建时间")
  142 + private LocalDateTime createTime;
  143 +
  144 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/MesProWorkorderSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
  7 +
  8 +import java.math.BigDecimal;
  9 +import java.time.LocalDateTime;
  10 +
  11 +@Schema(description = "管理后台 - MES 生产工单新增/修改 Request VO")
  12 +@Data
  13 +public class MesProWorkorderSaveReqVO {
  14 +
  15 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  16 + private Long id;
  17 +
  18 + @Schema(description = "工单编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "WO-001")
  19 + @NotEmpty(message = "工单编码不能为空")
  20 + private String code;
  21 +
  22 + @Schema(description = "工单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "生产工单-A")
  23 + @NotEmpty(message = "工单名称不能为空")
  24 + private String name;
  25 +
  26 + @Schema(description = "工单类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  27 + @NotNull(message = "工单类型不能为空")
  28 + private Integer type;
  29 +
  30 + @Schema(description = "来源类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  31 + @NotNull(message = "来源类型不能为空")
  32 + private Integer orderSourceType;
  33 +
  34 + @Schema(description = "来源单据编号", example = "SO-001")
  35 + private String orderSourceCode;
  36 +
  37 + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
  38 + @NotNull(message = "产品不能为空")
  39 + private Long productId;
  40 +
  41 + @Schema(description = "单位编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
  42 + @NotNull(message = "单位不能为空")
  43 + private Long unitMeasureId;
  44 +
  45 + @Schema(description = "生产数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
  46 + @NotNull(message = "生产数量不能为空")
  47 + private BigDecimal quantity;
  48 +
  49 + @Schema(description = "已生产数量", example = "0")
  50 + private BigDecimal quantityProduced;
  51 +
  52 + @Schema(description = "调整数量", example = "0")
  53 + private BigDecimal quantityChanged;
  54 +
  55 + @Schema(description = "已排产数量", example = "0")
  56 + private BigDecimal quantityScheduled;
  57 +
  58 + @Schema(description = "客户编号", example = "300")
  59 + private Long clientId;
  60 +
  61 + @Schema(description = "供应商编号", example = "400")
  62 + private Long vendorId;
  63 +
  64 + @Schema(description = "批次号", example = "BATCH-001")
  65 + private String batchCode;
  66 +
  67 + @Schema(description = "需求日期", requiredMode = Schema.RequiredMode.REQUIRED)
  68 + @NotNull(message = "需求日期不能为空")
  69 + private LocalDateTime requestDate;
  70 +
  71 + @Schema(description = "父工单编号", example = "0")
  72 + private Long parentId;
  73 +
  74 + @Schema(description = "所有父节点编号", example = "0")
  75 + private String ancestors;
  76 +
  77 + @Schema(description = "备注", example = "备注")
  78 + private String remark;
  79 +
  80 + @Schema(description = "预留字段1")
  81 + private String attribute1;
  82 +
  83 + @Schema(description = "预留字段2")
  84 + private String attribute2;
  85 +
  86 + @Schema(description = "预留字段3")
  87 + private Integer attribute3;
  88 +
  89 + @Schema(description = "预留字段4")
  90 + private Integer attribute4;
  91 +
  92 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/bom/MesProWorkorderBomPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
  7 +import lombok.EqualsAndHashCode;
  8 +import lombok.ToString;
  9 +
  10 +@Schema(description = "管理后台 - MES 生产工单 BOM 分页 Request VO")
  11 +@Data
  12 +@EqualsAndHashCode(callSuper = true)
  13 +@ToString(callSuper = true)
  14 +public class MesProWorkorderBomPageReqVO extends PageParam {
  15 +
  16 + @Schema(description = "生产工单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
  17 + @NotNull(message = "生产工单编号不能为空")
  18 + private Long workorderId;
  19 +
  20 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/bom/MesProWorkorderBomRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom;
  2 +
  3 +import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
  4 +import cn.idev.excel.annotation.ExcelProperty;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import lombok.Data;
  7 +
  8 +import java.math.BigDecimal;
  9 +import java.time.LocalDateTime;
  10 +
  11 +@Schema(description = "管理后台 - MES 生产工单 BOM Response VO")
  12 +@Data
  13 +@ExcelIgnoreUnannotated
  14 +public class MesProWorkorderBomRespVO {
  15 +
  16 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  17 + @ExcelProperty("编号")
  18 + private Long id;
  19 +
  20 + @Schema(description = "生产工单编号", example = "100")
  21 + private Long workorderId;
  22 +
  23 + @Schema(description = "BOM 物料编号", example = "200")
  24 + private Long itemId;
  25 +
  26 + @Schema(description = "物料名称", example = "电阻 R1")
  27 + @ExcelProperty("物料名称")
  28 + private String itemName;
  29 +
  30 + @Schema(description = "物料编码", example = "M-001")
  31 + @ExcelProperty("物料编码")
  32 + private String itemCode;
  33 +
  34 + @Schema(description = "规格型号", example = "10K 0603")
  35 + @ExcelProperty("规格型号")
  36 + private String itemSpec;
  37 +
  38 + @Schema(description = "单位编号", example = "300")
  39 + private Long unitMeasureId;
  40 +
  41 + @Schema(description = "单位名称", example = "个")
  42 + @ExcelProperty("单位")
  43 + private String unitMeasureName;
  44 +
  45 + @Schema(description = "物料产品标识", example = "ITEM")
  46 + @ExcelProperty("物料产品标识")
  47 + private String itemOrProduct;
  48 +
  49 + @Schema(description = "预计使用量", example = "10.00")
  50 + @ExcelProperty("预计使用量")
  51 + private BigDecimal quantity;
  52 +
  53 + @Schema(description = "备注", example = "备注")
  54 + @ExcelProperty("备注")
  55 + private String remark;
  56 +
  57 + @Schema(description = "预留字段1")
  58 + private String attribute1;
  59 +
  60 + @Schema(description = "预留字段2")
  61 + private String attribute2;
  62 +
  63 + @Schema(description = "预留字段3")
  64 + private Integer attribute3;
  65 +
  66 + @Schema(description = "预留字段4")
  67 + private Integer attribute4;
  68 +
  69 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  70 + @ExcelProperty("创建时间")
  71 + private LocalDateTime createTime;
  72 +
  73 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/bom/MesProWorkorderBomSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
  7 +
  8 +import java.math.BigDecimal;
  9 +
  10 +@Schema(description = "管理后台 - MES 生产工单 BOM 新增/修改 Request VO")
  11 +@Data
  12 +public class MesProWorkorderBomSaveReqVO {
  13 +
  14 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  15 + private Long id;
  16 +
  17 + @Schema(description = "生产工单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
  18 + @NotNull(message = "生产工单编号不能为空")
  19 + private Long workorderId;
  20 +
  21 + @Schema(description = "BOM 物料编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
  22 + @NotNull(message = "BOM 物料不能为空")
  23 + private Long itemId;
  24 +
  25 + @Schema(description = "单位编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "300")
  26 + @NotNull(message = "单位不能为空")
  27 + private Long unitMeasureId;
  28 +
  29 + @Schema(description = "物料产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "ITEM")
  30 + @NotEmpty(message = "物料产品标识不能为空")
  31 + private String itemOrProduct;
  32 +
  33 + @Schema(description = "预计使用量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10.00")
  34 + @NotNull(message = "预计使用量不能为空")
  35 + private BigDecimal quantity;
  36 +
  37 + @Schema(description = "备注", example = "备注")
  38 + private String remark;
  39 +
  40 + @Schema(description = "预留字段1")
  41 + private String attribute1;
  42 +
  43 + @Schema(description = "预留字段2")
  44 + private String attribute2;
  45 +
  46 + @Schema(description = "预留字段3")
  47 + private Integer attribute3;
  48 +
  49 + @Schema(description = "预留字段4")
  50 + private Integer attribute4;
  51 +
  52 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkorderBomDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
  5 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
  6 +import com.baomidou.mybatisplus.annotation.KeySequence;
  7 +import com.baomidou.mybatisplus.annotation.TableId;
  8 +import com.baomidou.mybatisplus.annotation.TableName;
  9 +import lombok.*;
  10 +
  11 +import java.math.BigDecimal;
  12 +
  13 +/**
  14 + * MES 生产工单 BOM DO
  15 + *
  16 + * @author 芋道源码
  17 + */
  18 +@TableName("mes_pro_workorder_bom")
  19 +@KeySequence("mes_pro_workorder_bom_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  20 +@Data
  21 +@EqualsAndHashCode(callSuper = true)
  22 +@ToString(callSuper = true)
  23 +@Builder
  24 +@NoArgsConstructor
  25 +@AllArgsConstructor
  26 +public class MesProWorkorderBomDO extends BaseDO {
  27 +
  28 + /**
  29 + * 编号
  30 + */
  31 + @TableId
  32 + private Long id;
  33 + /**
  34 + * 生产工单编号
  35 + *
  36 + * 关联 {@link MesProWorkorderDO#getId()}
  37 + */
  38 + private Long workorderId;
  39 + /**
  40 + * BOM 物料编号
  41 + *
  42 + * 关联 {@link MesMdItemDO#getId()}
  43 + */
  44 + private Long itemId;
  45 + /**
  46 + * 单位编号
  47 + *
  48 + * 关联 {@link MesMdUnitMeasureDO#getId()}
  49 + */
  50 + private Long unitMeasureId;
  51 + // TODO @AI:需要存储么?还是使用 itemId 的冗余?
  52 + /**
  53 + * 物料产品标识
  54 + */
  55 + private String itemOrProduct;
  56 + /**
  57 + * 预计使用量
  58 + */
  59 + private BigDecimal quantity;
  60 + /**
  61 + * 备注
  62 + */
  63 + private String remark;
  64 + /**
  65 + * 预留字段1
  66 + */
  67 + private String attribute1;
  68 + /**
  69 + * 预留字段2
  70 + */
  71 + private String attribute2;
  72 + /**
  73 + * 预留字段3
  74 + */
  75 + private Integer attribute3;
  76 + /**
  77 + * 预留字段4
  78 + */
  79 + private Integer attribute4;
  80 +
  81 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkorderDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
  5 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
  8 +import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkorderSourceTypeEnum;
  9 +import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkorderStatusEnum;
  10 +import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkorderTypeEnum;
  11 +import com.baomidou.mybatisplus.annotation.KeySequence;
  12 +import com.baomidou.mybatisplus.annotation.TableId;
  13 +import com.baomidou.mybatisplus.annotation.TableName;
  14 +import lombok.*;
  15 +
  16 +import java.math.BigDecimal;
  17 +import java.time.LocalDateTime;
  18 +
  19 +/**
  20 + * MES 生产工单 DO
  21 + *
  22 + * @author 芋道源码
  23 + */
  24 +@TableName("mes_pro_workorder")
  25 +@KeySequence("mes_pro_workorder_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  26 +@Data
  27 +@EqualsAndHashCode(callSuper = true)
  28 +@ToString(callSuper = true)
  29 +@Builder
  30 +@NoArgsConstructor
  31 +@AllArgsConstructor
  32 +public class MesProWorkorderDO extends BaseDO {
  33 +
  34 + /**
  35 + * 编号
  36 + */
  37 + @TableId
  38 + private Long id;
  39 + /**
  40 + * 工单编码
  41 + */
  42 + private String code;
  43 + /**
  44 + * 工单名称
  45 + */
  46 + private String name;
  47 + /**
  48 + * 工单类型
  49 + *
  50 + * 枚举 {@link MesProWorkorderTypeEnum}
  51 + */
  52 + private Integer type;
  53 + /**
  54 + * 来源类型
  55 + *
  56 + * 枚举 {@link MesProWorkorderSourceTypeEnum}
  57 + */
  58 + private Integer orderSourceType;
  59 + /**
  60 + * 来源单据编号
  61 + */
  62 + private String orderSourceCode;
  63 + /**
  64 + * 产品编号
  65 + *
  66 + * 关联 {@link MesMdItemDO#getId()}
  67 + */
  68 + private Long productId;
  69 + /**
  70 + * 单位编号
  71 + *
  72 + * 关联 {@link MesMdUnitMeasureDO#getId()}
  73 + */
  74 + private Long unitMeasureId;
  75 + /**
  76 + * 生产数量
  77 + */
  78 + private BigDecimal quantity;
  79 + /**
  80 + * 已生产数量
  81 + */
  82 + private BigDecimal quantityProduced;
  83 + /**
  84 + * 调整数量
  85 + */
  86 + private BigDecimal quantityChanged;
  87 + /**
  88 + * 已排产数量
  89 + */
  90 + private BigDecimal quantityScheduled;
  91 + /**
  92 + * 客户编号
  93 + *
  94 + * 关联 {@link MesMdClientDO#getId()}
  95 + */
  96 + private Long clientId;
  97 + /**
  98 + * 供应商编号
  99 + *
  100 + * 关联 {@link MesMdVendorDO#getId()}
  101 + */
  102 + private Long vendorId;
  103 + /**
  104 + * 批次号
  105 + */
  106 + private String batchCode;
  107 + /**
  108 + * 需求日期
  109 + */
  110 + private LocalDateTime requestDate;
  111 + /**
  112 + * 父工单编号
  113 + *
  114 + * 关联 {@link MesProWorkorderDO#getId()}
  115 + */
  116 + private Long parentId;
  117 + // TODO @AI:panrentId?不用父节点编号;
  118 + /**
  119 + * 所有父节点编号
  120 + */
  121 + private String ancestors;
  122 + /**
  123 + * 完成时间
  124 + */
  125 + private LocalDateTime finishDate;
  126 + /**
  127 + * 取消时间
  128 + */
  129 + private LocalDateTime cancelDate;
  130 + /**
  131 + * 工单状态
  132 + *
  133 + * 枚举 {@link MesProWorkorderStatusEnum}
  134 + */
  135 + private Integer status;
  136 + /**
  137 + * 备注
  138 + */
  139 + private String remark;
  140 + /**
  141 + * 预留字段1
  142 + */
  143 + private String attribute1;
  144 + /**
  145 + * 预留字段2
  146 + */
  147 + private String attribute2;
  148 + /**
  149 + * 预留字段3
  150 + */
  151 + private Integer attribute3;
  152 + /**
  153 + * 预留字段4
  154 + */
  155 + private Integer attribute4;
  156 +
  157 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkorderBomMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom.MesProWorkorderBomPageReqVO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkorderBomDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * MES 生产工单 BOM Mapper
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Mapper
  18 +public interface MesProWorkorderBomMapper extends BaseMapperX<MesProWorkorderBomDO> {
  19 +
  20 + default PageResult<MesProWorkorderBomDO> selectPage(MesProWorkorderBomPageReqVO reqVO) {
  21 + return selectPage(reqVO, new LambdaQueryWrapperX<MesProWorkorderBomDO>()
  22 + .eqIfPresent(MesProWorkorderBomDO::getWorkorderId, reqVO.getWorkorderId())
  23 + .orderByDesc(MesProWorkorderBomDO::getId));
  24 + }
  25 +
  26 + default List<MesProWorkorderBomDO> selectListByWorkorderId(Long workorderId) {
  27 + return selectList(MesProWorkorderBomDO::getWorkorderId, workorderId);
  28 + }
  29 +
  30 + default void deleteByWorkorderId(Long workorderId) {
  31 + delete(MesProWorkorderBomDO::getWorkorderId, workorderId);
  32 + }
  33 +
  34 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkorderMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkorderPageReqVO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkorderDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +/**
  11 + * MES 生产工单 Mapper
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@Mapper
  16 +public interface MesProWorkorderMapper extends BaseMapperX<MesProWorkorderDO> {
  17 +
  18 + default PageResult<MesProWorkorderDO> selectPage(MesProWorkorderPageReqVO reqVO) {
  19 + return selectPage(reqVO, new LambdaQueryWrapperX<MesProWorkorderDO>()
  20 + .likeIfPresent(MesProWorkorderDO::getCode, reqVO.getCode())
  21 + .likeIfPresent(MesProWorkorderDO::getName, reqVO.getName())
  22 + .eqIfPresent(MesProWorkorderDO::getType, reqVO.getType())
  23 + .eqIfPresent(MesProWorkorderDO::getOrderSourceType, reqVO.getOrderSourceType())
  24 + .eqIfPresent(MesProWorkorderDO::getProductId, reqVO.getProductId())
  25 + .eqIfPresent(MesProWorkorderDO::getClientId, reqVO.getClientId())
  26 + .eqIfPresent(MesProWorkorderDO::getStatus, reqVO.getStatus())
  27 + .betweenIfPresent(MesProWorkorderDO::getRequestDate, reqVO.getRequestDate())
  28 + .orderByDesc(MesProWorkorderDO::getId));
  29 + }
  30 +
  31 + default MesProWorkorderDO selectByCode(String code) {
  32 + return selectOne(MesProWorkorderDO::getCode, code);
  33 + }
  34 +
  35 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProWorkorderSourceTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.enums.pro;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.ArrayValuable;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Getter;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +/**
  10 + * MES 工单来源类型枚举
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +@Getter
  15 +@AllArgsConstructor
  16 +public enum MesProWorkorderSourceTypeEnum implements ArrayValuable<Integer> {
  17 +
  18 + ORDER(1, "客户订单"),
  19 + STORE(2, "库存备货");
  20 +
  21 + public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesProWorkorderSourceTypeEnum::getType).toArray(Integer[]::new);
  22 +
  23 + /**
  24 + * 类型值
  25 + */
  26 + private final Integer type;
  27 + /**
  28 + * 类型名
  29 + */
  30 + private final String name;
  31 +
  32 + @Override
  33 + public Integer[] array() {
  34 + return ARRAYS;
  35 + }
  36 +
  37 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProWorkorderStatusEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.enums.pro;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.ArrayValuable;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Getter;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +/**
  10 + * MES 生产工单状态枚举
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +@Getter
  15 +@AllArgsConstructor
  16 +public enum MesProWorkorderStatusEnum implements ArrayValuable<Integer> {
  17 +
  18 + PREPARE(0, "草稿"),
  19 + CONFIRMED(1, "已确认"),
  20 + FINISHED(2, "已完成"),
  21 + CANCELED(3, "已取消");
  22 +
  23 + public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesProWorkorderStatusEnum::getStatus).toArray(Integer[]::new);
  24 +
  25 + /**
  26 + * 状态值
  27 + */
  28 + private final Integer status;
  29 + /**
  30 + * 状态名
  31 + */
  32 + private final String name;
  33 +
  34 + @Override
  35 + public Integer[] array() {
  36 + return ARRAYS;
  37 + }
  38 +
  39 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProWorkorderTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.enums.pro;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.ArrayValuable;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Getter;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +/**
  10 + * MES 工单类型枚举
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +@Getter
  15 +@AllArgsConstructor
  16 +public enum MesProWorkorderTypeEnum implements ArrayValuable<Integer> {
  17 +
  18 + SELF(1, "自行生产"),
  19 + OUTSOURCE(2, "代工"),
  20 + PURCHASE(3, "采购");
  21 +
  22 + public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesProWorkorderTypeEnum::getType).toArray(Integer[]::new);
  23 +
  24 + /**
  25 + * 类型值
  26 + */
  27 + private final Integer type;
  28 + /**
  29 + * 类型名
  30 + */
  31 + private final String name;
  32 +
  33 + @Override
  34 + public Integer[] array() {
  35 + return ARRAYS;
  36 + }
  37 +
  38 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkorderBomService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.pro.workorder;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom.MesProWorkorderBomPageReqVO;
  5 +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom.MesProWorkorderBomSaveReqVO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkorderBomDO;
  7 +import jakarta.validation.Valid;
  8 +
  9 +import java.util.List;
  10 +
  11 +/**
  12 + * MES 生产工单 BOM Service 接口
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +public interface MesProWorkorderBomService {
  17 +
  18 + /**
  19 + * 创建工单 BOM
  20 + *
  21 + * @param createReqVO 创建信息
  22 + * @return 编号
  23 + */
  24 + Long createWorkorderBom(@Valid MesProWorkorderBomSaveReqVO createReqVO);
  25 +
  26 + /**
  27 + * 更新工单 BOM
  28 + *
  29 + * @param updateReqVO 更新信息
  30 + */
  31 + void updateWorkorderBom(@Valid MesProWorkorderBomSaveReqVO updateReqVO);
  32 +
  33 + /**
  34 + * 删除工单 BOM
  35 + *
  36 + * @param id 编号
  37 + */
  38 + void deleteWorkorderBom(Long id);
  39 +
  40 + /**
  41 + * 获得工单 BOM
  42 + *
  43 + * @param id 编号
  44 + * @return 工单 BOM
  45 + */
  46 + MesProWorkorderBomDO getWorkorderBom(Long id);
  47 +
  48 + /**
  49 + * 获得工单 BOM 分页
  50 + *
  51 + * @param pageReqVO 分页查询
  52 + * @return 工单 BOM 分页
  53 + */
  54 + PageResult<MesProWorkorderBomDO> getWorkorderBomPage(MesProWorkorderBomPageReqVO pageReqVO);
  55 +
  56 + /**
  57 + * 根据工单编号获得 BOM 列表
  58 + *
  59 + * @param workorderId 工单编号
  60 + * @return BOM 列表
  61 + */
  62 + List<MesProWorkorderBomDO> getWorkorderBomListByWorkorderId(Long workorderId);
  63 +
  64 + /**
  65 + * 根据工单编号删除 BOM
  66 + *
  67 + * @param workorderId 工单编号
  68 + */
  69 + void deleteWorkorderBomByWorkorderId(Long workorderId);
  70 +
  71 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkorderBomServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.pro.workorder;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom.MesProWorkorderBomPageReqVO;
  6 +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom.MesProWorkorderBomSaveReqVO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkorderBomDO;
  8 +import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkorderBomMapper;
  9 +import jakarta.annotation.Resource;
  10 +import org.springframework.stereotype.Service;
  11 +import org.springframework.validation.annotation.Validated;
  12 +
  13 +import java.util.List;
  14 +
  15 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  16 +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_WORKORDER_BOM_NOT_EXISTS;
  17 +
  18 +/**
  19 + * MES 生产工单 BOM Service 实现类
  20 + *
  21 + * @author 芋道源码
  22 + */
  23 +@Service
  24 +@Validated
  25 +public class MesProWorkorderBomServiceImpl implements MesProWorkorderBomService {
  26 +
  27 + @Resource
  28 + private MesProWorkorderBomMapper workorderBomMapper;
  29 +
  30 + @Override
  31 + public Long createWorkorderBom(MesProWorkorderBomSaveReqVO createReqVO) {
  32 + // TODO @AI:是不是要校验 workorder 存在?另外,大小写应该是 workOrder?
  33 + MesProWorkorderBomDO workorderBom = BeanUtils.toBean(createReqVO, MesProWorkorderBomDO.class);
  34 + workorderBomMapper.insert(workorderBom);
  35 + return workorderBom.getId();
  36 + }
  37 +
  38 + @Override
  39 + public void updateWorkorderBom(MesProWorkorderBomSaveReqVO updateReqVO) {
  40 + // 校验存在
  41 + validateWorkorderBomExists(updateReqVO.getId());
  42 + // 更新
  43 + MesProWorkorderBomDO updateObj = BeanUtils.toBean(updateReqVO, MesProWorkorderBomDO.class);
  44 + workorderBomMapper.updateById(updateObj);
  45 + }
  46 +
  47 + @Override
  48 + public void deleteWorkorderBom(Long id) {
  49 + // 校验存在
  50 + validateWorkorderBomExists(id);
  51 + // 删除
  52 + workorderBomMapper.deleteById(id);
  53 + }
  54 +
  55 + private void validateWorkorderBomExists(Long id) {
  56 + if (workorderBomMapper.selectById(id) == null) {
  57 + throw exception(PRO_WORKORDER_BOM_NOT_EXISTS);
  58 + }
  59 + }
  60 +
  61 + @Override
  62 + public MesProWorkorderBomDO getWorkorderBom(Long id) {
  63 + return workorderBomMapper.selectById(id);
  64 + }
  65 +
  66 + @Override
  67 + public PageResult<MesProWorkorderBomDO> getWorkorderBomPage(MesProWorkorderBomPageReqVO pageReqVO) {
  68 + return workorderBomMapper.selectPage(pageReqVO);
  69 + }
  70 +
  71 + @Override
  72 + public List<MesProWorkorderBomDO> getWorkorderBomListByWorkorderId(Long workorderId) {
  73 + return workorderBomMapper.selectListByWorkorderId(workorderId);
  74 + }
  75 +
  76 + @Override
  77 + public void deleteWorkorderBomByWorkorderId(Long workorderId) {
  78 + workorderBomMapper.deleteByWorkorderId(workorderId);
  79 + }
  80 +
  81 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkorderService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.pro.workorder;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkorderPageReqVO;
  5 +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkorderSaveReqVO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkorderDO;
  7 +import jakarta.validation.Valid;
  8 +
  9 +/**
  10 + * MES 生产工单 Service 接口
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +public interface MesProWorkorderService {
  15 +
  16 + /**
  17 + * 创建生产工单
  18 + *
  19 + * @param createReqVO 创建信息
  20 + * @return 编号
  21 + */
  22 + Long createWorkorder(@Valid MesProWorkorderSaveReqVO createReqVO);
  23 +
  24 + /**
  25 + * 更新生产工单
  26 + *
  27 + * @param updateReqVO 更新信息
  28 + */
  29 + void updateWorkorder(@Valid MesProWorkorderSaveReqVO updateReqVO);
  30 +
  31 + /**
  32 + * 删除生产工单
  33 + *
  34 + * @param id 编号
  35 + */
  36 + void deleteWorkorder(Long id);
  37 +
  38 + /**
  39 + * 获得生产工单
  40 + *
  41 + * @param id 编号
  42 + * @return 生产工单
  43 + */
  44 + MesProWorkorderDO getWorkorder(Long id);
  45 +
  46 + /**
  47 + * 获得生产工单分页
  48 + *
  49 + * @param pageReqVO 分页查询
  50 + * @return 生产工单分页
  51 + */
  52 + PageResult<MesProWorkorderDO> getWorkorderPage(MesProWorkorderPageReqVO pageReqVO);
  53 +
  54 + /**
  55 + * 完成工单
  56 + *
  57 + * @param id 编号
  58 + */
  59 + void finishWorkorder(Long id);
  60 +
  61 + /**
  62 + * 取消工单
  63 + *
  64 + * @param id 编号
  65 + */
  66 + void cancelWorkorder(Long id);
  67 +
  68 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkorderServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.pro.workorder;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.ObjUtil;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkorderSaveReqVO;
  8 +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkorderPageReqVO;
  9 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemBatchConfigDO;
  10 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
  11 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdProductBomDO;
  12 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkorderBomDO;
  13 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkorderDO;
  14 +import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkorderBomMapper;
  15 +import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkorderMapper;
  16 +import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkorderStatusEnum;
  17 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemBatchConfigService;
  18 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
  19 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdProductBomService;
  20 +import jakarta.annotation.Resource;
  21 +import org.springframework.stereotype.Service;
  22 +import org.springframework.transaction.annotation.Transactional;
  23 +import org.springframework.validation.annotation.Validated;
  24 +
  25 +import java.math.BigDecimal;
  26 +import java.time.LocalDateTime;
  27 +import java.util.List;
  28 +import java.util.Objects;
  29 +
  30 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  31 +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
  32 +
  33 +/**
  34 + * MES 生产工单 Service 实现类
  35 + *
  36 + * @author 芋道源码
  37 + */
  38 +@Service
  39 +@Validated
  40 +public class MesProWorkorderServiceImpl implements MesProWorkorderService {
  41 +
  42 + @Resource
  43 + private MesProWorkorderMapper workorderMapper;
  44 +
  45 + @Resource
  46 + private MesProWorkorderBomMapper workorderBomMapper;
  47 +
  48 + @Resource
  49 + private MesMdItemService itemService;
  50 +
  51 + @Resource
  52 + private MesMdProductBomService productBomService;
  53 +
  54 + @Resource
  55 + private MesMdItemBatchConfigService itemBatchConfigService;
  56 +
  57 + @Override
  58 + @Transactional(rollbackFor = Exception.class)
  59 + public Long createWorkorder(MesProWorkorderSaveReqVO createReqVO) {
  60 + // 1.1 校验编码唯一
  61 + validateWorkorderCodeUnique(null, createReqVO.getCode());
  62 + // 1.2 校验产品存在
  63 + // TODO @AI:getItem 里面要有个校验方法,不然每次都要写 if (item == null) throw exception(MD_ITEM_NOT_EXISTS);
  64 + MesMdItemDO product = itemService.getItem(createReqVO.getProductId());
  65 + if (product == null) {
  66 + throw exception(MD_ITEM_NOT_EXISTS);
  67 + }
  68 + // 1.3 校验批次配置:如果产品有 clientFlag=true,则 clientId 必填
  69 + validateBatchConfig(createReqVO.getProductId(), createReqVO.getClientId());
  70 +
  71 + // 2.1 设置默认值
  72 + if (createReqVO.getParentId() == null) {
  73 + createReqVO.setParentId(0L);
  74 + }
  75 + if (createReqVO.getAncestors() == null) {
  76 + createReqVO.setAncestors("0");
  77 + }
  78 + // 2.2 插入工单
  79 + MesProWorkorderDO workorder = BeanUtils.toBean(createReqVO, MesProWorkorderDO.class);
  80 + if (workorder.getStatus() == null) {
  81 + workorder.setStatus(MesProWorkorderStatusEnum.PREPARE.getStatus());
  82 + }
  83 + workorderMapper.insert(workorder);
  84 +
  85 + // 3. 自动生成 BOM:根据产品 BOM 生成工单 BOM
  86 + generateWorkorderBom(workorder.getId(), createReqVO.getProductId(), createReqVO.getQuantity());
  87 + return workorder.getId();
  88 + }
  89 +
  90 + @Override
  91 + @Transactional(rollbackFor = Exception.class)
  92 + public void updateWorkorder(MesProWorkorderSaveReqVO updateReqVO) {
  93 + // 1.1 校验存在
  94 + MesProWorkorderDO oldWorkorder = validateWorkorderExists(updateReqVO.getId());
  95 + // 1.2 校验编码唯一
  96 + validateWorkorderCodeUnique(updateReqVO.getId(), updateReqVO.getCode());
  97 + // 1.3 校验批次配置
  98 + validateBatchConfig(updateReqVO.getProductId(), updateReqVO.getClientId());
  99 +
  100 + // 2. 判断产品或数量是否变更,如果变更则重新生成 BOM
  101 + boolean productChanged = !Objects.equals(oldWorkorder.getProductId(), updateReqVO.getProductId());
  102 + boolean quantityChanged = oldWorkorder.getQuantity().compareTo(updateReqVO.getQuantity()) != 0;
  103 + if (productChanged || quantityChanged) {
  104 + // 删除旧 BOM 并重新生成
  105 + workorderBomMapper.deleteByWorkorderId(updateReqVO.getId());
  106 + generateWorkorderBom(updateReqVO.getId(), updateReqVO.getProductId(), updateReqVO.getQuantity());
  107 + }
  108 +
  109 + // 3. 更新
  110 + MesProWorkorderDO updateObj = BeanUtils.toBean(updateReqVO, MesProWorkorderDO.class);
  111 + workorderMapper.updateById(updateObj);
  112 + }
  113 +
  114 + @Override
  115 + @Transactional(rollbackFor = Exception.class)
  116 + public void deleteWorkorder(Long id) {
  117 + // 1.1 校验存在
  118 + MesProWorkorderDO workorder = validateWorkorderExists(id);
  119 + // 1.2 只能删除草稿状态的工单
  120 + // TODO @AI:notEquals;ObjUtil
  121 + if (!Objects.equals(workorder.getStatus(), MesProWorkorderStatusEnum.PREPARE.getStatus())) {
  122 + throw exception(PRO_WORKORDER_NOT_PREPARE);
  123 + }
  124 +
  125 + // 2. 删除工单 + BOM
  126 + workorderMapper.deleteById(id);
  127 + workorderBomMapper.deleteByWorkorderId(id);
  128 + }
  129 +
  130 + @Override
  131 + public MesProWorkorderDO getWorkorder(Long id) {
  132 + return workorderMapper.selectById(id);
  133 + }
  134 +
  135 + @Override
  136 + public PageResult<MesProWorkorderDO> getWorkorderPage(MesProWorkorderPageReqVO pageReqVO) {
  137 + return workorderMapper.selectPage(pageReqVO);
  138 + }
  139 +
  140 + @Override
  141 + public void finishWorkorder(Long id) {
  142 + // 1. 校验存在
  143 + validateWorkorderExists(id);
  144 +
  145 + // 2. 更新状态为已完成
  146 + // TODO @AI:链式调用,不用逐个设置;
  147 + MesProWorkorderDO updateObj = new MesProWorkorderDO();
  148 + updateObj.setId(id);
  149 + updateObj.setStatus(MesProWorkorderStatusEnum.FINISHED.getStatus());
  150 + updateObj.setFinishDate(LocalDateTime.now());
  151 + workorderMapper.updateById(updateObj);
  152 + // TODO @芋艿:pro_task 未迁移,暂不级联更新任务状态
  153 + }
  154 +
  155 + @Override
  156 + public void cancelWorkorder(Long id) {
  157 + // 1. 校验存在
  158 + validateWorkorderExists(id);
  159 +
  160 + // 2. 更新状态为已取消
  161 + // TODO @AI:链式调用,不用逐个设置;
  162 + MesProWorkorderDO updateObj = new MesProWorkorderDO();
  163 + updateObj.setId(id);
  164 + updateObj.setStatus(MesProWorkorderStatusEnum.CANCELED.getStatus());
  165 + updateObj.setCancelDate(LocalDateTime.now());
  166 + workorderMapper.updateById(updateObj);
  167 + // TODO @芋艿:pro_task 未迁移,暂不级联更新任务状态
  168 + }
  169 +
  170 + // ==================== 校验方法 ====================
  171 +
  172 + private MesProWorkorderDO validateWorkorderExists(Long id) {
  173 + MesProWorkorderDO workorder = workorderMapper.selectById(id);
  174 + if (workorder == null) {
  175 + throw exception(PRO_WORKORDER_NOT_EXISTS);
  176 + }
  177 + return workorder;
  178 + }
  179 +
  180 + private void validateWorkorderCodeUnique(Long id, String code) {
  181 + if (code == null) {
  182 + return;
  183 + }
  184 + MesProWorkorderDO workorder = workorderMapper.selectByCode(code);
  185 + if (workorder == null) {
  186 + return;
  187 + }
  188 + if (ObjUtil.notEqual(workorder.getId(), id)) {
  189 + throw exception(PRO_WORKORDER_CODE_DUPLICATE);
  190 + }
  191 + }
  192 +
  193 + private void validateBatchConfig(Long productId, Long clientId) {
  194 + MesMdItemBatchConfigDO batchConfig = itemBatchConfigService.getItemBatchConfigByItemId(productId);
  195 + if (batchConfig != null && Boolean.TRUE.equals(batchConfig.getClientFlag()) && clientId == null) {
  196 + // 产品要求批次管理中必须填写客户
  197 + throw exception(MD_CLIENT_NOT_EXISTS);
  198 + }
  199 + }
  200 +
  201 + // ==================== BOM 自动生成 ====================
  202 +
  203 + /**
  204 + * 根据产品 BOM 自动生成工单 BOM 行
  205 + *
  206 + * @param workorderId 工单编号
  207 + * @param productId 产品编号
  208 + * @param quantity 工单生产数量
  209 + */
  210 + private void generateWorkorderBom(Long workorderId, Long productId, BigDecimal quantity) {
  211 + List<MesMdProductBomDO> productBomList = productBomService.getProductBomListByItemId(productId);
  212 + if (CollUtil.isEmpty(productBomList)) {
  213 + return;
  214 + }
  215 + for (MesMdProductBomDO productBom : productBomList) {
  216 + MesMdItemDO bomItem = itemService.getItem(productBom.getBomItemId());
  217 + // TODO @AI:bomItem 校验必须非空吧?!
  218 + // TODO @AI:联会调用;
  219 + MesProWorkorderBomDO workorderBom = new MesProWorkorderBomDO();
  220 + workorderBom.setWorkorderId(workorderId);
  221 + workorderBom.setItemId(productBom.getBomItemId());
  222 + workorderBom.setItemOrProduct("ITEM");
  223 + // 预计使用量 = 工单数量 × BOM 用量
  224 + workorderBom.setQuantity(quantity.multiply(productBom.getQuantity()));
  225 + // 设置单位:使用物料自身的单位
  226 + if (bomItem != null) {
  227 + workorderBom.setUnitMeasureId(bomItem.getUnitMeasureId());
  228 + }
  229 + workorderBomMapper.insert(workorderBom);
  230 + }
  231 + }
  232 +
  233 +}
... ...