diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkorderBomController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkorderBomController.java new file mode 100644 index 0000000..a7f1f8d --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkorderBomController.java @@ -0,0 +1,116 @@ +package cn.iocoder.yudao.module.mes.controller.admin.pro.workorder; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +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.pro.workorder.vo.bom.MesProWorkorderBomPageReqVO; +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom.MesProWorkorderBomRespVO; +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom.MesProWorkorderBomSaveReqVO; +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.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 io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +@Tag(name = "管理后台 - MES 生产工单 BOM") +@RestController +@RequestMapping("/mes/pro/workorder-bom") +@Validated +public class MesProWorkorderBomController { + + @Resource + private MesProWorkorderBomService workorderBomService; + + @Resource + private MesMdItemService itemService; + + @Resource + private MesMdUnitMeasureService unitMeasureService; + + @PostMapping("/create") + @Operation(summary = "创建工单 BOM") + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:update')") + public CommonResult createWorkorderBom(@Valid @RequestBody MesProWorkorderBomSaveReqVO createReqVO) { + return success(workorderBomService.createWorkorderBom(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新工单 BOM") + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:update')") + public CommonResult updateWorkorderBom(@Valid @RequestBody MesProWorkorderBomSaveReqVO updateReqVO) { + workorderBomService.updateWorkorderBom(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除工单 BOM") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:update')") + public CommonResult deleteWorkorderBom(@RequestParam("id") Long id) { + workorderBomService.deleteWorkorderBom(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得工单 BOM") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:query')") + public CommonResult getWorkorderBom(@RequestParam("id") Long id) { + MesProWorkorderBomDO workorderBom = workorderBomService.getWorkorderBom(id); + if (workorderBom == null) { + return success(null); + } + return success(buildWorkorderBomRespVOList(List.of(workorderBom)).get(0)); + } + + @GetMapping("/page") + @Operation(summary = "获得工单 BOM 分页") + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:query')") + public CommonResult> getWorkorderBomPage(@Valid MesProWorkorderBomPageReqVO pageReqVO) { + PageResult pageResult = workorderBomService.getWorkorderBomPage(pageReqVO); + return success(new PageResult<>(buildWorkorderBomRespVOList(pageResult.getList()), pageResult.getTotal())); + } + + // ==================== 拼接 VO ==================== + + private List buildWorkorderBomRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 1. 批量获取关联数据 + Map itemMap = itemService.getItemMap( + convertSet(list, MesProWorkorderBomDO::getItemId)); + Map unitMeasureMap = unitMeasureService.getUnitMeasureMap( + convertSet(list, MesProWorkorderBomDO::getUnitMeasureId)); + // 2. 拼接 VO + return BeanUtils.toBean(list, MesProWorkorderBomRespVO.class, vo -> { + MapUtils.findAndThen(itemMap, vo.getItemId(), item -> { + // TODO @AI:少了 spec 字段?另外,改成链式调用; + vo.setItemName(item.getName()); + vo.setItemCode(item.getCode()); + vo.setItemSpec(item.getSpec()); + }); + MapUtils.findAndThen(unitMeasureMap, vo.getUnitMeasureId(), + unitMeasure -> vo.setUnitMeasureName(unitMeasure.getName())); + }); + } + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkorderController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkorderController.java new file mode 100644 index 0000000..95f7d29 --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkorderController.java @@ -0,0 +1,171 @@ +package cn.iocoder.yudao.module.mes.controller.admin.pro.workorder; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkorderPageReqVO; +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkorderRespVO; +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkorderSaveReqVO; +import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO; +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.md.vendor.MesMdVendorDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkorderDO; +import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService; +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.md.vendor.MesMdVendorService; +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.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +@Tag(name = "管理后台 - MES 生产工单") +@RestController +@RequestMapping("/mes/pro/workorder") +@Validated +public class MesProWorkorderController { + + @Resource + private MesProWorkorderService workorderService; + + @Resource + private MesMdItemService itemService; + + @Resource + private MesMdClientService clientService; + + @Resource + private MesMdVendorService vendorService; + + @Resource + private MesMdUnitMeasureService unitMeasureService; + + @PostMapping("/create") + @Operation(summary = "创建生产工单") + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:create')") + public CommonResult createWorkorder(@Valid @RequestBody MesProWorkorderSaveReqVO createReqVO) { + return success(workorderService.createWorkorder(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新生产工单") + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:update')") + public CommonResult updateWorkorder(@Valid @RequestBody MesProWorkorderSaveReqVO updateReqVO) { + workorderService.updateWorkorder(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除生产工单") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:delete')") + public CommonResult deleteWorkorder(@RequestParam("id") Long id) { + workorderService.deleteWorkorder(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得生产工单") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:query')") + public CommonResult getWorkorder(@RequestParam("id") Long id) { + MesProWorkorderDO workorder = workorderService.getWorkorder(id); + if (workorder == null) { + return success(null); + } + return success(buildWorkorderRespVOList(List.of(workorder)).get(0)); + } + + @GetMapping("/page") + @Operation(summary = "获得生产工单分页") + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:query')") + public CommonResult> getWorkorderPage(@Valid MesProWorkorderPageReqVO pageReqVO) { + PageResult pageResult = workorderService.getWorkorderPage(pageReqVO); + return success(new PageResult<>(buildWorkorderRespVOList(pageResult.getList()), pageResult.getTotal())); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出生产工单 Excel") + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportWorkorderExcel(@Valid MesProWorkorderPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = workorderService.getWorkorderPage(pageReqVO).getList(); + List voList = buildWorkorderRespVOList(list); + ExcelUtils.write(response, "生产工单.xls", "数据", MesProWorkorderRespVO.class, voList); + } + + @PutMapping("/finish") + @Operation(summary = "完成工单") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:update')") + public CommonResult finishWorkorder(@RequestParam("id") Long id) { + workorderService.finishWorkorder(id); + return success(true); + } + + @PutMapping("/cancel") + @Operation(summary = "取消工单") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('mes:pro-workorder:update')") + public CommonResult cancelWorkorder(@RequestParam("id") Long id) { + workorderService.cancelWorkorder(id); + return success(true); + } + + // ==================== 拼接 VO ==================== + + @SuppressWarnings("CodeBlock2Expr") + private List buildWorkorderRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 1. 批量获取关联数据 + Map itemMap = itemService.getItemMap( + convertSet(list, MesProWorkorderDO::getProductId)); + Map clientMap = clientService.getClientMap( + convertSet(list, MesProWorkorderDO::getClientId)); + Map vendorMap = vendorService.getVendorMap( + convertSet(list, MesProWorkorderDO::getVendorId)); + Map unitMeasureMap = unitMeasureService.getUnitMeasureMap( + convertSet(list, MesProWorkorderDO::getUnitMeasureId)); + // 2. 拼接 VO + return BeanUtils.toBean(list, MesProWorkorderRespVO.class, vo -> { + MapUtils.findAndThen(itemMap, vo.getProductId(), item -> { + // TODO @AI:少了 spec 字段?另外,改成链式调用; + vo.setProductName(item.getName()); + vo.setProductCode(item.getCode()); + vo.setProductSpec(item.getSpec()); + }); + MapUtils.findAndThen(clientMap, vo.getClientId(), + client -> vo.setClientName(client.getName())); + MapUtils.findAndThen(vendorMap, vo.getVendorId(), + vendor -> vo.setVendorName(vendor.getName())); + MapUtils.findAndThen(unitMeasureMap, vo.getUnitMeasureId(), + unitMeasure -> vo.setUnitMeasureName(unitMeasure.getName())); + }); + } + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/MesProWorkorderPageReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/MesProWorkorderPageReqVO.java new file mode 100644 index 0000000..5ecd1a3 --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/MesProWorkorderPageReqVO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - MES 生产工单分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MesProWorkorderPageReqVO extends PageParam { + + @Schema(description = "工单编码", example = "WO-001") + private String code; + + @Schema(description = "工单名称", example = "生产工单-A") + private String name; + + @Schema(description = "工单类型", example = "1") + private Integer type; + + @Schema(description = "来源类型", example = "1") + private Integer orderSourceType; + + @Schema(description = "产品编号", example = "100") + private Long productId; + + @Schema(description = "客户编号", example = "300") + private Long clientId; + + @Schema(description = "工单状态", example = "0") + private Integer status; + + @Schema(description = "需求日期") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] requestDate; + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/MesProWorkorderRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/MesProWorkorderRespVO.java new file mode 100644 index 0000000..c4d7b81 --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/MesProWorkorderRespVO.java @@ -0,0 +1,144 @@ +package cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.mes.enums.DictTypeConstants; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - MES 生产工单 Response VO") +@Data +@ExcelIgnoreUnannotated +public class MesProWorkorderRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "工单编码", example = "WO-001") + @ExcelProperty("工单编码") + private String code; + + @Schema(description = "工单名称", example = "生产工单-A") + @ExcelProperty("工单名称") + private String name; + + @Schema(description = "工单类型", example = "1") + @ExcelProperty(value = "工单类型", converter = DictConvert.class) + @DictFormat(DictTypeConstants.MES_PRO_WORKORDER_TYPE) + private Integer type; + + @Schema(description = "来源类型", example = "1") + @ExcelProperty(value = "来源类型", converter = DictConvert.class) + @DictFormat(DictTypeConstants.MES_PRO_WORKORDER_SOURCE_TYPE) + private Integer orderSourceType; + + @Schema(description = "来源单据编号", example = "SO-001") + @ExcelProperty("来源单据编号") + private String orderSourceCode; + + @Schema(description = "产品编号", example = "100") + private Long productId; + + @Schema(description = "产品名称", example = "电路板 A") + @ExcelProperty("产品名称") + private String productName; + + @Schema(description = "产品编码", example = "P-001") + @ExcelProperty("产品编码") + private String productCode; + + @Schema(description = "规格型号", example = "100x200mm") + @ExcelProperty("规格型号") + private String productSpec; + + @Schema(description = "单位编号", example = "200") + private Long unitMeasureId; + + @Schema(description = "单位名称", example = "个") + @ExcelProperty("单位") + private String unitMeasureName; + + @Schema(description = "生产数量", example = "100.00") + @ExcelProperty("生产数量") + private BigDecimal quantity; + + @Schema(description = "已生产数量", example = "50.00") + @ExcelProperty("已生产数量") + private BigDecimal quantityProduced; + + @Schema(description = "调整数量", example = "0") + @ExcelProperty("调整数量") + private BigDecimal quantityChanged; + + @Schema(description = "已排产数量", example = "80.00") + @ExcelProperty("已排产数量") + private BigDecimal quantityScheduled; + + @Schema(description = "客户编号", example = "300") + private Long clientId; + + @Schema(description = "客户名称", example = "客户 A") + @ExcelProperty("客户名称") + private String clientName; + + @Schema(description = "供应商编号", example = "400") + private Long vendorId; + + @Schema(description = "供应商名称", example = "供应商 A") + @ExcelProperty("供应商名称") + private String vendorName; + + @Schema(description = "批次号", example = "BATCH-001") + @ExcelProperty("批次号") + private String batchCode; + + @Schema(description = "需求日期") + @ExcelProperty("需求日期") + private LocalDateTime requestDate; + + @Schema(description = "父工单编号", example = "0") + private Long parentId; + + @Schema(description = "所有父节点编号", example = "0") + private String ancestors; + + @Schema(description = "完成时间") + @ExcelProperty("完成时间") + private LocalDateTime finishDate; + + @Schema(description = "取消时间") + @ExcelProperty("取消时间") + private LocalDateTime cancelDate; + + @Schema(description = "工单状态", example = "0") + @ExcelProperty(value = "工单状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.MES_PRO_WORKORDER_STATUS) + private Integer status; + + @Schema(description = "备注", example = "备注") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "预留字段1") + private String attribute1; + + @Schema(description = "预留字段2") + private String attribute2; + + @Schema(description = "预留字段3") + private Integer attribute3; + + @Schema(description = "预留字段4") + private Integer attribute4; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/MesProWorkorderSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/MesProWorkorderSaveReqVO.java new file mode 100644 index 0000000..48f5389 --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/MesProWorkorderSaveReqVO.java @@ -0,0 +1,92 @@ +package cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - MES 生产工单新增/修改 Request VO") +@Data +public class MesProWorkorderSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "工单编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "WO-001") + @NotEmpty(message = "工单编码不能为空") + private String code; + + @Schema(description = "工单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "生产工单-A") + @NotEmpty(message = "工单名称不能为空") + private String name; + + @Schema(description = "工单类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "工单类型不能为空") + private Integer type; + + @Schema(description = "来源类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "来源类型不能为空") + private Integer orderSourceType; + + @Schema(description = "来源单据编号", example = "SO-001") + private String orderSourceCode; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + @NotNull(message = "产品不能为空") + private Long productId; + + @Schema(description = "单位编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "200") + @NotNull(message = "单位不能为空") + private Long unitMeasureId; + + @Schema(description = "生产数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "生产数量不能为空") + private BigDecimal quantity; + + @Schema(description = "已生产数量", example = "0") + private BigDecimal quantityProduced; + + @Schema(description = "调整数量", example = "0") + private BigDecimal quantityChanged; + + @Schema(description = "已排产数量", example = "0") + private BigDecimal quantityScheduled; + + @Schema(description = "客户编号", example = "300") + private Long clientId; + + @Schema(description = "供应商编号", example = "400") + private Long vendorId; + + @Schema(description = "批次号", example = "BATCH-001") + private String batchCode; + + @Schema(description = "需求日期", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "需求日期不能为空") + private LocalDateTime requestDate; + + @Schema(description = "父工单编号", example = "0") + private Long parentId; + + @Schema(description = "所有父节点编号", example = "0") + private String ancestors; + + @Schema(description = "备注", example = "备注") + private String remark; + + @Schema(description = "预留字段1") + private String attribute1; + + @Schema(description = "预留字段2") + private String attribute2; + + @Schema(description = "预留字段3") + private Integer attribute3; + + @Schema(description = "预留字段4") + private Integer attribute4; + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/bom/MesProWorkorderBomPageReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/bom/MesProWorkorderBomPageReqVO.java new file mode 100644 index 0000000..952f8f7 --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/bom/MesProWorkorderBomPageReqVO.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - MES 生产工单 BOM 分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MesProWorkorderBomPageReqVO extends PageParam { + + @Schema(description = "生产工单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + @NotNull(message = "生产工单编号不能为空") + private Long workorderId; + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/bom/MesProWorkorderBomRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/bom/MesProWorkorderBomRespVO.java new file mode 100644 index 0000000..17d2713 --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/bom/MesProWorkorderBomRespVO.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - MES 生产工单 BOM Response VO") +@Data +@ExcelIgnoreUnannotated +public class MesProWorkorderBomRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "生产工单编号", example = "100") + private Long workorderId; + + @Schema(description = "BOM 物料编号", example = "200") + private Long itemId; + + @Schema(description = "物料名称", example = "电阻 R1") + @ExcelProperty("物料名称") + private String itemName; + + @Schema(description = "物料编码", example = "M-001") + @ExcelProperty("物料编码") + private String itemCode; + + @Schema(description = "规格型号", example = "10K 0603") + @ExcelProperty("规格型号") + private String itemSpec; + + @Schema(description = "单位编号", example = "300") + private Long unitMeasureId; + + @Schema(description = "单位名称", example = "个") + @ExcelProperty("单位") + private String unitMeasureName; + + @Schema(description = "物料产品标识", example = "ITEM") + @ExcelProperty("物料产品标识") + private String itemOrProduct; + + @Schema(description = "预计使用量", example = "10.00") + @ExcelProperty("预计使用量") + private BigDecimal quantity; + + @Schema(description = "备注", example = "备注") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "预留字段1") + private String attribute1; + + @Schema(description = "预留字段2") + private String attribute2; + + @Schema(description = "预留字段3") + private Integer attribute3; + + @Schema(description = "预留字段4") + private Integer attribute4; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/bom/MesProWorkorderBomSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/bom/MesProWorkorderBomSaveReqVO.java new file mode 100644 index 0000000..eb270ee --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/bom/MesProWorkorderBomSaveReqVO.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - MES 生产工单 BOM 新增/修改 Request VO") +@Data +public class MesProWorkorderBomSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "生产工单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + @NotNull(message = "生产工单编号不能为空") + private Long workorderId; + + @Schema(description = "BOM 物料编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "200") + @NotNull(message = "BOM 物料不能为空") + private Long itemId; + + @Schema(description = "单位编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "300") + @NotNull(message = "单位不能为空") + private Long unitMeasureId; + + @Schema(description = "物料产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "ITEM") + @NotEmpty(message = "物料产品标识不能为空") + private String itemOrProduct; + + @Schema(description = "预计使用量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10.00") + @NotNull(message = "预计使用量不能为空") + private BigDecimal quantity; + + @Schema(description = "备注", example = "备注") + private String remark; + + @Schema(description = "预留字段1") + private String attribute1; + + @Schema(description = "预留字段2") + private String attribute2; + + @Schema(description = "预留字段3") + private Integer attribute3; + + @Schema(description = "预留字段4") + private Integer attribute4; + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkorderBomDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkorderBomDO.java new file mode 100644 index 0000000..38318c3 --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkorderBomDO.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * MES 生产工单 BOM DO + * + * @author 芋道源码 + */ +@TableName("mes_pro_workorder_bom") +@KeySequence("mes_pro_workorder_bom_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MesProWorkorderBomDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 生产工单编号 + * + * 关联 {@link MesProWorkorderDO#getId()} + */ + private Long workorderId; + /** + * BOM 物料编号 + * + * 关联 {@link MesMdItemDO#getId()} + */ + private Long itemId; + /** + * 单位编号 + * + * 关联 {@link MesMdUnitMeasureDO#getId()} + */ + private Long unitMeasureId; + // TODO @AI:需要存储么?还是使用 itemId 的冗余? + /** + * 物料产品标识 + */ + private String itemOrProduct; + /** + * 预计使用量 + */ + private BigDecimal quantity; + /** + * 备注 + */ + private String remark; + /** + * 预留字段1 + */ + private String attribute1; + /** + * 预留字段2 + */ + private String attribute2; + /** + * 预留字段3 + */ + private Integer attribute3; + /** + * 预留字段4 + */ + private Integer attribute4; + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkorderDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkorderDO.java new file mode 100644 index 0000000..c8332e1 --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkorderDO.java @@ -0,0 +1,157 @@ +package cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO; +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.md.vendor.MesMdVendorDO; +import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkorderSourceTypeEnum; +import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkorderStatusEnum; +import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkorderTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * MES 生产工单 DO + * + * @author 芋道源码 + */ +@TableName("mes_pro_workorder") +@KeySequence("mes_pro_workorder_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MesProWorkorderDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 工单编码 + */ + private String code; + /** + * 工单名称 + */ + private String name; + /** + * 工单类型 + * + * 枚举 {@link MesProWorkorderTypeEnum} + */ + private Integer type; + /** + * 来源类型 + * + * 枚举 {@link MesProWorkorderSourceTypeEnum} + */ + private Integer orderSourceType; + /** + * 来源单据编号 + */ + private String orderSourceCode; + /** + * 产品编号 + * + * 关联 {@link MesMdItemDO#getId()} + */ + private Long productId; + /** + * 单位编号 + * + * 关联 {@link MesMdUnitMeasureDO#getId()} + */ + private Long unitMeasureId; + /** + * 生产数量 + */ + private BigDecimal quantity; + /** + * 已生产数量 + */ + private BigDecimal quantityProduced; + /** + * 调整数量 + */ + private BigDecimal quantityChanged; + /** + * 已排产数量 + */ + private BigDecimal quantityScheduled; + /** + * 客户编号 + * + * 关联 {@link MesMdClientDO#getId()} + */ + private Long clientId; + /** + * 供应商编号 + * + * 关联 {@link MesMdVendorDO#getId()} + */ + private Long vendorId; + /** + * 批次号 + */ + private String batchCode; + /** + * 需求日期 + */ + private LocalDateTime requestDate; + /** + * 父工单编号 + * + * 关联 {@link MesProWorkorderDO#getId()} + */ + private Long parentId; + // TODO @AI:panrentId?不用父节点编号; + /** + * 所有父节点编号 + */ + private String ancestors; + /** + * 完成时间 + */ + private LocalDateTime finishDate; + /** + * 取消时间 + */ + private LocalDateTime cancelDate; + /** + * 工单状态 + * + * 枚举 {@link MesProWorkorderStatusEnum} + */ + private Integer status; + /** + * 备注 + */ + private String remark; + /** + * 预留字段1 + */ + private String attribute1; + /** + * 预留字段2 + */ + private String attribute2; + /** + * 预留字段3 + */ + private Integer attribute3; + /** + * 预留字段4 + */ + private Integer attribute4; + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkorderBomMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkorderBomMapper.java new file mode 100644 index 0000000..089d6f2 --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkorderBomMapper.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom.MesProWorkorderBomPageReqVO; +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkorderBomDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * MES 生产工单 BOM Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface MesProWorkorderBomMapper extends BaseMapperX { + + default PageResult selectPage(MesProWorkorderBomPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(MesProWorkorderBomDO::getWorkorderId, reqVO.getWorkorderId()) + .orderByDesc(MesProWorkorderBomDO::getId)); + } + + default List selectListByWorkorderId(Long workorderId) { + return selectList(MesProWorkorderBomDO::getWorkorderId, workorderId); + } + + default void deleteByWorkorderId(Long workorderId) { + delete(MesProWorkorderBomDO::getWorkorderId, workorderId); + } + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkorderMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkorderMapper.java new file mode 100644 index 0000000..e76b8b9 --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkorderMapper.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkorderPageReqVO; +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkorderDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * MES 生产工单 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface MesProWorkorderMapper extends BaseMapperX { + + default PageResult selectPage(MesProWorkorderPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(MesProWorkorderDO::getCode, reqVO.getCode()) + .likeIfPresent(MesProWorkorderDO::getName, reqVO.getName()) + .eqIfPresent(MesProWorkorderDO::getType, reqVO.getType()) + .eqIfPresent(MesProWorkorderDO::getOrderSourceType, reqVO.getOrderSourceType()) + .eqIfPresent(MesProWorkorderDO::getProductId, reqVO.getProductId()) + .eqIfPresent(MesProWorkorderDO::getClientId, reqVO.getClientId()) + .eqIfPresent(MesProWorkorderDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(MesProWorkorderDO::getRequestDate, reqVO.getRequestDate()) + .orderByDesc(MesProWorkorderDO::getId)); + } + + default MesProWorkorderDO selectByCode(String code) { + return selectOne(MesProWorkorderDO::getCode, code); + } + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProWorkorderSourceTypeEnum.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProWorkorderSourceTypeEnum.java new file mode 100644 index 0000000..7ee14b9 --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProWorkorderSourceTypeEnum.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.mes.enums.pro; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * MES 工单来源类型枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum MesProWorkorderSourceTypeEnum implements ArrayValuable { + + ORDER(1, "客户订单"), + STORE(2, "库存备货"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesProWorkorderSourceTypeEnum::getType).toArray(Integer[]::new); + + /** + * 类型值 + */ + private final Integer type; + /** + * 类型名 + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProWorkorderStatusEnum.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProWorkorderStatusEnum.java new file mode 100644 index 0000000..31ed0b7 --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProWorkorderStatusEnum.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.mes.enums.pro; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * MES 生产工单状态枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum MesProWorkorderStatusEnum implements ArrayValuable { + + PREPARE(0, "草稿"), + CONFIRMED(1, "已确认"), + FINISHED(2, "已完成"), + CANCELED(3, "已取消"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesProWorkorderStatusEnum::getStatus).toArray(Integer[]::new); + + /** + * 状态值 + */ + private final Integer status; + /** + * 状态名 + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProWorkorderTypeEnum.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProWorkorderTypeEnum.java new file mode 100644 index 0000000..7681445 --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProWorkorderTypeEnum.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.mes.enums.pro; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * MES 工单类型枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum MesProWorkorderTypeEnum implements ArrayValuable { + + SELF(1, "自行生产"), + OUTSOURCE(2, "代工"), + PURCHASE(3, "采购"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesProWorkorderTypeEnum::getType).toArray(Integer[]::new); + + /** + * 类型值 + */ + private final Integer type; + /** + * 类型名 + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkorderBomService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkorderBomService.java new file mode 100644 index 0000000..b101a02 --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkorderBomService.java @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.mes.service.pro.workorder; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom.MesProWorkorderBomPageReqVO; +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom.MesProWorkorderBomSaveReqVO; +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkorderBomDO; +import jakarta.validation.Valid; + +import java.util.List; + +/** + * MES 生产工单 BOM Service 接口 + * + * @author 芋道源码 + */ +public interface MesProWorkorderBomService { + + /** + * 创建工单 BOM + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createWorkorderBom(@Valid MesProWorkorderBomSaveReqVO createReqVO); + + /** + * 更新工单 BOM + * + * @param updateReqVO 更新信息 + */ + void updateWorkorderBom(@Valid MesProWorkorderBomSaveReqVO updateReqVO); + + /** + * 删除工单 BOM + * + * @param id 编号 + */ + void deleteWorkorderBom(Long id); + + /** + * 获得工单 BOM + * + * @param id 编号 + * @return 工单 BOM + */ + MesProWorkorderBomDO getWorkorderBom(Long id); + + /** + * 获得工单 BOM 分页 + * + * @param pageReqVO 分页查询 + * @return 工单 BOM 分页 + */ + PageResult getWorkorderBomPage(MesProWorkorderBomPageReqVO pageReqVO); + + /** + * 根据工单编号获得 BOM 列表 + * + * @param workorderId 工单编号 + * @return BOM 列表 + */ + List getWorkorderBomListByWorkorderId(Long workorderId); + + /** + * 根据工单编号删除 BOM + * + * @param workorderId 工单编号 + */ + void deleteWorkorderBomByWorkorderId(Long workorderId); + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkorderBomServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkorderBomServiceImpl.java new file mode 100644 index 0000000..105ec8e --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkorderBomServiceImpl.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.mes.service.pro.workorder; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom.MesProWorkorderBomPageReqVO; +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom.MesProWorkorderBomSaveReqVO; +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkorderBomDO; +import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkorderBomMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_WORKORDER_BOM_NOT_EXISTS; + +/** + * MES 生产工单 BOM Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class MesProWorkorderBomServiceImpl implements MesProWorkorderBomService { + + @Resource + private MesProWorkorderBomMapper workorderBomMapper; + + @Override + public Long createWorkorderBom(MesProWorkorderBomSaveReqVO createReqVO) { + // TODO @AI:是不是要校验 workorder 存在?另外,大小写应该是 workOrder? + MesProWorkorderBomDO workorderBom = BeanUtils.toBean(createReqVO, MesProWorkorderBomDO.class); + workorderBomMapper.insert(workorderBom); + return workorderBom.getId(); + } + + @Override + public void updateWorkorderBom(MesProWorkorderBomSaveReqVO updateReqVO) { + // 校验存在 + validateWorkorderBomExists(updateReqVO.getId()); + // 更新 + MesProWorkorderBomDO updateObj = BeanUtils.toBean(updateReqVO, MesProWorkorderBomDO.class); + workorderBomMapper.updateById(updateObj); + } + + @Override + public void deleteWorkorderBom(Long id) { + // 校验存在 + validateWorkorderBomExists(id); + // 删除 + workorderBomMapper.deleteById(id); + } + + private void validateWorkorderBomExists(Long id) { + if (workorderBomMapper.selectById(id) == null) { + throw exception(PRO_WORKORDER_BOM_NOT_EXISTS); + } + } + + @Override + public MesProWorkorderBomDO getWorkorderBom(Long id) { + return workorderBomMapper.selectById(id); + } + + @Override + public PageResult getWorkorderBomPage(MesProWorkorderBomPageReqVO pageReqVO) { + return workorderBomMapper.selectPage(pageReqVO); + } + + @Override + public List getWorkorderBomListByWorkorderId(Long workorderId) { + return workorderBomMapper.selectListByWorkorderId(workorderId); + } + + @Override + public void deleteWorkorderBomByWorkorderId(Long workorderId) { + workorderBomMapper.deleteByWorkorderId(workorderId); + } + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkorderService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkorderService.java new file mode 100644 index 0000000..da2e117 --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkorderService.java @@ -0,0 +1,68 @@ +package cn.iocoder.yudao.module.mes.service.pro.workorder; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkorderPageReqVO; +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkorderSaveReqVO; +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkorderDO; +import jakarta.validation.Valid; + +/** + * MES 生产工单 Service 接口 + * + * @author 芋道源码 + */ +public interface MesProWorkorderService { + + /** + * 创建生产工单 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createWorkorder(@Valid MesProWorkorderSaveReqVO createReqVO); + + /** + * 更新生产工单 + * + * @param updateReqVO 更新信息 + */ + void updateWorkorder(@Valid MesProWorkorderSaveReqVO updateReqVO); + + /** + * 删除生产工单 + * + * @param id 编号 + */ + void deleteWorkorder(Long id); + + /** + * 获得生产工单 + * + * @param id 编号 + * @return 生产工单 + */ + MesProWorkorderDO getWorkorder(Long id); + + /** + * 获得生产工单分页 + * + * @param pageReqVO 分页查询 + * @return 生产工单分页 + */ + PageResult getWorkorderPage(MesProWorkorderPageReqVO pageReqVO); + + /** + * 完成工单 + * + * @param id 编号 + */ + void finishWorkorder(Long id); + + /** + * 取消工单 + * + * @param id 编号 + */ + void cancelWorkorder(Long id); + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkorderServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkorderServiceImpl.java new file mode 100644 index 0000000..8b83242 --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkorderServiceImpl.java @@ -0,0 +1,233 @@ +package cn.iocoder.yudao.module.mes.service.pro.workorder; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkorderSaveReqVO; +import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkorderPageReqVO; +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemBatchConfigDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdProductBomDO; +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.mysql.pro.workorder.MesProWorkorderBomMapper; +import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkorderMapper; +import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkorderStatusEnum; +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemBatchConfigService; +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; +import cn.iocoder.yudao.module.mes.service.md.item.MesMdProductBomService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +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.*; + +/** + * MES 生产工单 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class MesProWorkorderServiceImpl implements MesProWorkorderService { + + @Resource + private MesProWorkorderMapper workorderMapper; + + @Resource + private MesProWorkorderBomMapper workorderBomMapper; + + @Resource + private MesMdItemService itemService; + + @Resource + private MesMdProductBomService productBomService; + + @Resource + private MesMdItemBatchConfigService itemBatchConfigService; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createWorkorder(MesProWorkorderSaveReqVO createReqVO) { + // 1.1 校验编码唯一 + validateWorkorderCodeUnique(null, createReqVO.getCode()); + // 1.2 校验产品存在 + // TODO @AI:getItem 里面要有个校验方法,不然每次都要写 if (item == null) throw exception(MD_ITEM_NOT_EXISTS); + MesMdItemDO product = itemService.getItem(createReqVO.getProductId()); + if (product == null) { + throw exception(MD_ITEM_NOT_EXISTS); + } + // 1.3 校验批次配置:如果产品有 clientFlag=true,则 clientId 必填 + validateBatchConfig(createReqVO.getProductId(), createReqVO.getClientId()); + + // 2.1 设置默认值 + if (createReqVO.getParentId() == null) { + createReqVO.setParentId(0L); + } + if (createReqVO.getAncestors() == null) { + createReqVO.setAncestors("0"); + } + // 2.2 插入工单 + MesProWorkorderDO workorder = BeanUtils.toBean(createReqVO, MesProWorkorderDO.class); + if (workorder.getStatus() == null) { + workorder.setStatus(MesProWorkorderStatusEnum.PREPARE.getStatus()); + } + workorderMapper.insert(workorder); + + // 3. 自动生成 BOM:根据产品 BOM 生成工单 BOM + generateWorkorderBom(workorder.getId(), createReqVO.getProductId(), createReqVO.getQuantity()); + return workorder.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateWorkorder(MesProWorkorderSaveReqVO updateReqVO) { + // 1.1 校验存在 + MesProWorkorderDO oldWorkorder = validateWorkorderExists(updateReqVO.getId()); + // 1.2 校验编码唯一 + validateWorkorderCodeUnique(updateReqVO.getId(), updateReqVO.getCode()); + // 1.3 校验批次配置 + validateBatchConfig(updateReqVO.getProductId(), updateReqVO.getClientId()); + + // 2. 判断产品或数量是否变更,如果变更则重新生成 BOM + boolean productChanged = !Objects.equals(oldWorkorder.getProductId(), updateReqVO.getProductId()); + boolean quantityChanged = oldWorkorder.getQuantity().compareTo(updateReqVO.getQuantity()) != 0; + if (productChanged || quantityChanged) { + // 删除旧 BOM 并重新生成 + workorderBomMapper.deleteByWorkorderId(updateReqVO.getId()); + generateWorkorderBom(updateReqVO.getId(), updateReqVO.getProductId(), updateReqVO.getQuantity()); + } + + // 3. 更新 + MesProWorkorderDO updateObj = BeanUtils.toBean(updateReqVO, MesProWorkorderDO.class); + workorderMapper.updateById(updateObj); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteWorkorder(Long id) { + // 1.1 校验存在 + MesProWorkorderDO workorder = validateWorkorderExists(id); + // 1.2 只能删除草稿状态的工单 + // TODO @AI:notEquals;ObjUtil + if (!Objects.equals(workorder.getStatus(), MesProWorkorderStatusEnum.PREPARE.getStatus())) { + throw exception(PRO_WORKORDER_NOT_PREPARE); + } + + // 2. 删除工单 + BOM + workorderMapper.deleteById(id); + workorderBomMapper.deleteByWorkorderId(id); + } + + @Override + public MesProWorkorderDO getWorkorder(Long id) { + return workorderMapper.selectById(id); + } + + @Override + public PageResult getWorkorderPage(MesProWorkorderPageReqVO pageReqVO) { + return workorderMapper.selectPage(pageReqVO); + } + + @Override + public void finishWorkorder(Long id) { + // 1. 校验存在 + validateWorkorderExists(id); + + // 2. 更新状态为已完成 + // TODO @AI:链式调用,不用逐个设置; + MesProWorkorderDO updateObj = new MesProWorkorderDO(); + updateObj.setId(id); + updateObj.setStatus(MesProWorkorderStatusEnum.FINISHED.getStatus()); + updateObj.setFinishDate(LocalDateTime.now()); + workorderMapper.updateById(updateObj); + // TODO @芋艿:pro_task 未迁移,暂不级联更新任务状态 + } + + @Override + public void cancelWorkorder(Long id) { + // 1. 校验存在 + validateWorkorderExists(id); + + // 2. 更新状态为已取消 + // TODO @AI:链式调用,不用逐个设置; + MesProWorkorderDO updateObj = new MesProWorkorderDO(); + updateObj.setId(id); + updateObj.setStatus(MesProWorkorderStatusEnum.CANCELED.getStatus()); + updateObj.setCancelDate(LocalDateTime.now()); + workorderMapper.updateById(updateObj); + // TODO @芋艿:pro_task 未迁移,暂不级联更新任务状态 + } + + // ==================== 校验方法 ==================== + + private MesProWorkorderDO validateWorkorderExists(Long id) { + MesProWorkorderDO workorder = workorderMapper.selectById(id); + if (workorder == null) { + throw exception(PRO_WORKORDER_NOT_EXISTS); + } + return workorder; + } + + private void validateWorkorderCodeUnique(Long id, String code) { + if (code == null) { + return; + } + MesProWorkorderDO workorder = workorderMapper.selectByCode(code); + if (workorder == null) { + return; + } + if (ObjUtil.notEqual(workorder.getId(), id)) { + throw exception(PRO_WORKORDER_CODE_DUPLICATE); + } + } + + private void validateBatchConfig(Long productId, Long clientId) { + MesMdItemBatchConfigDO batchConfig = itemBatchConfigService.getItemBatchConfigByItemId(productId); + if (batchConfig != null && Boolean.TRUE.equals(batchConfig.getClientFlag()) && clientId == null) { + // 产品要求批次管理中必须填写客户 + throw exception(MD_CLIENT_NOT_EXISTS); + } + } + + // ==================== BOM 自动生成 ==================== + + /** + * 根据产品 BOM 自动生成工单 BOM 行 + * + * @param workorderId 工单编号 + * @param productId 产品编号 + * @param quantity 工单生产数量 + */ + private void generateWorkorderBom(Long workorderId, Long productId, BigDecimal quantity) { + List productBomList = productBomService.getProductBomListByItemId(productId); + if (CollUtil.isEmpty(productBomList)) { + return; + } + for (MesMdProductBomDO productBom : productBomList) { + MesMdItemDO bomItem = itemService.getItem(productBom.getBomItemId()); + // TODO @AI:bomItem 校验必须非空吧?! + // TODO @AI:联会调用; + MesProWorkorderBomDO workorderBom = new MesProWorkorderBomDO(); + workorderBom.setWorkorderId(workorderId); + workorderBom.setItemId(productBom.getBomItemId()); + workorderBom.setItemOrProduct("ITEM"); + // 预计使用量 = 工单数量 × BOM 用量 + workorderBom.setQuantity(quantity.multiply(productBom.getQuantity())); + // 设置单位:使用物料自身的单位 + if (bomItem != null) { + workorderBom.setUnitMeasureId(bomItem.getUnitMeasureId()); + } + workorderBomMapper.insert(workorderBom); + } + } + +}