diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/PlanController.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/PlanController.java index 046633081..1ecdc1dbc 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/PlanController.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/PlanController.java @@ -697,6 +697,22 @@ public class PlanController { return success(planService.getPlanProductCapacityPage(reqVO)); } + @PutMapping("/change-line") + @Operation(summary = "生产计划换线") + @PreAuthorize("@ss.hasPermission('mes:plan:update')") + public CommonResult changeLine(@Valid @RequestBody PlanLineChangeReqVO reqVO) { + planService.changeLine(reqVO); + return success(true); + } + + @GetMapping("/line-change-record/list") + @Operation(summary = "获得生产计划换线记录列表") + @Parameter(name = "planId", description = "生产计划 ID", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('mes:plan:query')") + public CommonResult> getLineChangeRecordList(@RequestParam("planId") Long planId) { + return success(planService.getLineChangeRecordList(planId)); + } + } diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanLineChangeRecordRespVO.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanLineChangeRecordRespVO.java new file mode 100644 index 000000000..9c589333c --- /dev/null +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanLineChangeRecordRespVO.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.mes.controller.admin.plan.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 生产计划换线记录 Response VO") +@Data +public class PlanLineChangeRecordRespVO { + + @Schema(description = "编号", example = "1") + private Long id; + + @Schema(description = "生产计划编号", example = "1") + private Long planId; + + @Schema(description = "计划工艺流程明细编号", example = "1") + private Long planProcessRouteItemId; + + @Schema(description = "工艺参数编号", example = "100") + private Long processParameterId; + + @Schema(description = "工艺参数名称") + private String processParameterName; + + @Schema(description = "换线前设备 ID", example = "10") + private Long beforeDeviceId; + + @Schema(description = "换线前设备名称") + private String beforeDeviceName; + + @Schema(description = "换线后设备 ID", example = "11") + private Long afterDeviceId; + + @Schema(description = "换线后设备名称") + private String afterDeviceName; + + @Schema(description = "换线时间") + private LocalDateTime changeTime; + + @Schema(description = "操作人 ID", example = "1") + private Long operatorUserId; + + @Schema(description = "当前计划合格数量", example = "100") + private Long passNumber; + + @Schema(description = "备注") + private String remark; + +} diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanLineChangeReqVO.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanLineChangeReqVO.java new file mode 100644 index 000000000..5ccff44db --- /dev/null +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanLineChangeReqVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.mes.controller.admin.plan.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 生产计划换线 Request VO") +@Data +public class PlanLineChangeReqVO { + + @Schema(description = "生产计划 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "生产计划 ID 不能为空") + private Long planId; + + @Schema(description = "计划工艺流程明细 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "计划工艺流程明细 ID 不能为空") + private Long planProcessRouteItemId; + + @Schema(description = "换线后的设备 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @NotNull(message = "换线后的设备 ID 不能为空") + private Long deviceId; + + @Schema(description = "备注") + private String remark; + +} diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanProcessRouteItemRespVO.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanProcessRouteItemRespVO.java new file mode 100644 index 000000000..37d5cf34e --- /dev/null +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanProcessRouteItemRespVO.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.mes.controller.admin.plan.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 生产计划工艺流程 Response VO") +@Data +public class PlanProcessRouteItemRespVO { + + @Schema(description = "编号", example = "1") + private Long id; + + @Schema(description = "生产计划编号", example = "1") + private Long planId; + + @Schema(description = "工艺路线编号", example = "1") + private Long processRouteId; + + @Schema(description = "工艺路线明细编号", example = "1") + private Long processRouteItemId; + + @Schema(description = "排序,从 1 开始", example = "1") + private Integer sort; + + @Schema(description = "工艺参数编号", example = "100") + private Long processParameterId; + + @Schema(description = "工艺参数编码") + private String processParameterCode; + + @Schema(description = "工艺参数名称") + private String processParameterName; + + @Schema(description = "设备 ID", example = "10") + private Long deviceId; + + @Schema(description = "设备名称") + private String deviceName; + + @Schema(description = "备注") + private String remark; + +} diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanProcessRouteItemSaveReqVO.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanProcessRouteItemSaveReqVO.java new file mode 100644 index 000000000..ae3058f28 --- /dev/null +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanProcessRouteItemSaveReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.mes.controller.admin.plan.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 生产计划工艺流程新增/修改 Request VO") +@Data +public class PlanProcessRouteItemSaveReqVO { + + @Schema(description = "工艺路线明细 ID,对应 /mes/process-route 的 items.id", example = "1") + private Long processRouteItemId; + + @Schema(description = "排序,从 1 开始。不传时使用工艺路线明细排序", example = "1") + private Integer sort; + + @Schema(description = "设备 ID,对应 /mes/device-ledger", example = "10") + private Long deviceId; + + @Schema(description = "备注") + private String remark; + +} diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanRespVO.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanRespVO.java index 81be94a3b..358a19efa 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanRespVO.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanRespVO.java @@ -9,6 +9,7 @@ import lombok.Data; import java.math.BigDecimal; import java.time.LocalDateTime; +import java.util.List; @Schema(description = "管理后台 - 生产计划 Response VO") @Data @@ -156,10 +157,19 @@ public class PlanRespVO { @Schema(description = "设备名称") private String deviceName; + @Schema(description = "工艺路线 ID") + private Long processRouteId; + + @Schema(description = "工艺路线名称") + private String processRouteName; + + @Schema(description = "工艺流程明细") + private List processRouteItems; + @Schema(description = "最晚开工时间") private LocalDateTime latestStartTime; @Schema(description = "交货日期") private LocalDateTime deliveryDate; -} \ No newline at end of file +} diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanSaveReqVO.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanSaveReqVO.java index 66fc384ab..390c1c463 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanSaveReqVO.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanSaveReqVO.java @@ -7,6 +7,7 @@ import lombok.Data; import javax.validation.constraints.NotNull; import java.math.BigDecimal; import java.time.LocalDateTime; +import java.util.List; @Schema(description = "管理后台 - 生产计划新增/修改 Request VO") @Data @@ -102,6 +103,12 @@ public class PlanSaveReqVO { @Schema(description = "设备Id") private Long deviceId; + @Schema(description = "工艺路线 ID,对应 /mes/process-route", example = "1") + private Long processRouteId; + + @Schema(description = "工艺流程明细。传入每个流程选择的设备 ID;不传时按工艺路线 items 自动生成空设备明细", implementation = PlanProcessRouteItemSaveReqVO.class) + private List processRouteItems; + @Schema(description = "订单优先级") private Integer orderPriority; diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/plan/PlanDO.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/plan/PlanDO.java index 154596b80..d258bb030 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/plan/PlanDO.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/plan/PlanDO.java @@ -148,6 +148,16 @@ public class PlanDO extends BaseDO { */ private Long deviceId; + /** + * 工艺路线 ID + */ + private Long processRouteId; + + /** + * 工艺路线名称 + */ + private String processRouteName; + /** * 最晚开工时间 @@ -161,4 +171,4 @@ public class PlanDO extends BaseDO { -} \ No newline at end of file +} diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/planlinechange/PlanLineChangeRecordDO.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/planlinechange/PlanLineChangeRecordDO.java new file mode 100644 index 000000000..a1c5e6b85 --- /dev/null +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/planlinechange/PlanLineChangeRecordDO.java @@ -0,0 +1,78 @@ +package cn.iocoder.yudao.module.mes.dal.dataobject.planlinechange; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * 生产计划换线记录 DO + */ +@TableName("mes_plan_line_change_record") +@KeySequence("mes_plan_line_change_record_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PlanLineChangeRecordDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 生产计划编号 + */ + private Long planId; + /** + * 计划工艺流程明细编号 + */ + private Long planProcessRouteItemId; + /** + * 工艺参数编号 + */ + private Long processParameterId; + /** + * 工艺参数名称 + */ + private String processParameterName; + /** + * 换线前设备 ID + */ + private Long beforeDeviceId; + /** + * 换线前设备名称 + */ + private String beforeDeviceName; + /** + * 换线后设备 ID + */ + private Long afterDeviceId; + /** + * 换线后设备名称 + */ + private String afterDeviceName; + /** + * 换线时间 + */ + private LocalDateTime changeTime; + /** + * 操作人 ID + */ + private Long operatorUserId; + /** + * 当前计划合格数量 + */ + private Long passNumber; + /** + * 备注 + */ + private String remark; + +} diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/planroute/PlanProcessRouteItemDO.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/planroute/PlanProcessRouteItemDO.java new file mode 100644 index 000000000..bdfd71822 --- /dev/null +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/planroute/PlanProcessRouteItemDO.java @@ -0,0 +1,68 @@ +package cn.iocoder.yudao.module.mes.dal.dataobject.planroute; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 生产计划工艺路线明细 DO + */ +@TableName("mes_plan_process_route_item") +@KeySequence("mes_plan_process_route_item_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PlanProcessRouteItemDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 生产计划编号 + */ + private Long planId; + /** + * 工艺路线编号 + */ + private Long processRouteId; + /** + * 工艺路线明细编号 + */ + private Long processRouteItemId; + /** + * 排序,从 1 开始 + */ + private Integer sort; + /** + * 工艺参数编号 + */ + private Long processParameterId; + /** + * 工艺参数编码 + */ + private String processParameterCode; + /** + * 工艺参数名称 + */ + private String processParameterName; + /** + * 设备编号 + */ + private Long deviceId; + /** + * 设备名称 + */ + private String deviceName; + /** + * 备注 + */ + private String remark; + +} diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/planlinechange/PlanLineChangeRecordMapper.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/planlinechange/PlanLineChangeRecordMapper.java new file mode 100644 index 000000000..f3abe184d --- /dev/null +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/planlinechange/PlanLineChangeRecordMapper.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.mes.dal.mysql.planlinechange; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.mes.dal.dataobject.planlinechange.PlanLineChangeRecordDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface PlanLineChangeRecordMapper extends BaseMapperX { + + default List selectListByPlanId(Long planId) { + return selectList(new LambdaQueryWrapperX() + .eq(PlanLineChangeRecordDO::getPlanId, planId) + .orderByDesc(PlanLineChangeRecordDO::getChangeTime) + .orderByDesc(PlanLineChangeRecordDO::getId)); + } + +} diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/planroute/PlanProcessRouteItemMapper.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/planroute/PlanProcessRouteItemMapper.java new file mode 100644 index 000000000..61964aded --- /dev/null +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/planroute/PlanProcessRouteItemMapper.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.mes.dal.mysql.planroute; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.mes.dal.dataobject.planroute.PlanProcessRouteItemDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface PlanProcessRouteItemMapper extends BaseMapperX { + + default List selectListByPlanId(Long planId) { + return selectList(new LambdaQueryWrapperX() + .eq(PlanProcessRouteItemDO::getPlanId, planId) + .orderByAsc(PlanProcessRouteItemDO::getSort) + .orderByAsc(PlanProcessRouteItemDO::getId)); + } + + default void deleteByPlanId(Long planId) { + delete(new LambdaQueryWrapperX() + .eq(PlanProcessRouteItemDO::getPlanId, planId)); + } + +} diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/plan/PlanService.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/plan/PlanService.java index 871d7ce23..18e4258c7 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/plan/PlanService.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/plan/PlanService.java @@ -115,5 +115,9 @@ public interface PlanService { PageResult getPlanProductCapacityPage(PlanProductCapacityPageReqVO reqVO); + void changeLine(@Valid PlanLineChangeReqVO reqVO); + + List getLineChangeRecordList(Long planId); + } diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/plan/PlanServiceImpl.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/plan/PlanServiceImpl.java index 7a088cb8a..201f5f9ed 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/plan/PlanServiceImpl.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/plan/PlanServiceImpl.java @@ -23,22 +23,30 @@ import cn.iocoder.yudao.module.mes.controller.admin.itemrequisition.vo.ItemRequi import cn.iocoder.yudao.module.mes.controller.admin.plan.vo.*; import cn.iocoder.yudao.module.mes.controller.admin.planrecord.vo.PlanRecordRespVO; import cn.iocoder.yudao.module.mes.controller.admin.task.vo.TaskStatusEnum; -import cn.iocoder.yudao.module.mes.dal.dataobject.bom.BomDO; -import cn.iocoder.yudao.module.mes.dal.dataobject.bom.BomDetailDO; +// import cn.iocoder.yudao.module.mes.dal.dataobject.bom.BomDO; +// import cn.iocoder.yudao.module.mes.dal.dataobject.bom.BomDetailDO; import cn.iocoder.yudao.module.mes.dal.dataobject.deviceledger.DeviceLedgerDO; import cn.iocoder.yudao.module.mes.dal.dataobject.itemrequisition.ItemRequisitionDetailDO; import cn.iocoder.yudao.module.mes.dal.dataobject.organization.OrganizationDO; import cn.iocoder.yudao.module.mes.dal.dataobject.paigongrecord.PaigongRecordDO; import cn.iocoder.yudao.module.mes.dal.dataobject.plan.PlanDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.planlinechange.PlanLineChangeRecordDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.planroute.PlanProcessRouteItemDO; import cn.iocoder.yudao.module.mes.dal.dataobject.planrecord.PlanRecordDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.processroute.ProcessRouteDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.processroute.ProcessRouteItemDO; import cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDO; import cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDetailDO; import cn.iocoder.yudao.module.mes.dal.mysql.deviceledger.DeviceLedgerMapper; -import cn.iocoder.yudao.module.mes.dal.mysql.bom.BomMapper; -import cn.iocoder.yudao.module.mes.dal.mysql.bom.BomDetailMapper; +// import cn.iocoder.yudao.module.mes.dal.mysql.bom.BomMapper; +// import cn.iocoder.yudao.module.mes.dal.mysql.bom.BomDetailMapper; import cn.iocoder.yudao.module.mes.dal.mysql.paigongrecord.PaigongRecordMapper; import cn.iocoder.yudao.module.mes.dal.mysql.plan.PlanMapper; +import cn.iocoder.yudao.module.mes.dal.mysql.planlinechange.PlanLineChangeRecordMapper; +import cn.iocoder.yudao.module.mes.dal.mysql.planroute.PlanProcessRouteItemMapper; import cn.iocoder.yudao.module.mes.dal.mysql.planrecord.PlanRecordMapper; +import cn.iocoder.yudao.module.mes.dal.mysql.processroute.ProcessRouteItemMapper; +import cn.iocoder.yudao.module.mes.dal.mysql.processroute.ProcessRouteMapper; import cn.iocoder.yudao.module.mes.dal.mysql.stockindetail.StockInDetailMapper; import cn.iocoder.yudao.module.mes.dal.mysql.task.TaskMapper; import cn.iocoder.yudao.module.mes.dal.redis.no.MesNoRedisDAO; @@ -76,6 +84,7 @@ import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PLAN_CODE_EXISTS; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PLAN_NOT_EXISTS; @@ -130,9 +139,17 @@ public class PlanServiceImpl implements PlanService { @Resource private PlanRecordMapper planRecordMapper; @Resource - private BomMapper bomMapper; + private ProcessRouteMapper processRouteMapper; @Resource - private BomDetailMapper bomDetailMapper; + private ProcessRouteItemMapper processRouteItemMapper; + @Resource + private PlanProcessRouteItemMapper planProcessRouteItemMapper; + @Resource + private PlanLineChangeRecordMapper planLineChangeRecordMapper; +// @Resource +// private BomMapper bomMapper; +// @Resource +// private BomDetailMapper bomDetailMapper; @@ -158,9 +175,10 @@ public class PlanServiceImpl implements PlanService { } if (plan.getIsEnable() == null) plan.setIsEnable(true); if(plan.getPriorityNum()==null) plan.setPriorityNum(1L); - if (createReqVO.getIsCalculateLoss() == null) { - createReqVO.setIsCalculateLoss(true); - } + fillProcessRouteSnapshot(plan); +// if (createReqVO.getIsCalculateLoss() == null) { +// createReqVO.setIsCalculateLoss(true); +// } // todo // planMapper.insert(plan); // 返回 @@ -169,34 +187,38 @@ public class PlanServiceImpl implements PlanService { taskDO.setStatus(TaskStatusEnum.已排产.getValue()); taskService.updateTask(taskDO); } - List list = analysisService.getItemAnalysis(plan); // 产品 num - Map bomYieldRateMap = buildBomYieldRateMap(plan.getProductId(), createReqVO.getIsCalculateLoss()); - List itemRequisitionDetails = new ArrayList<>(); - for (ItemRequisitionAndStock item : list) { - ItemRequisitionDetailDO detailDO = new ItemRequisitionDetailDO() - .setProductId(item.getItemId()) - .setUnitId(item.getUnitId()) - .setNumber(calculateLossNumber(item.getItemId(), item.getNumber(), bomYieldRateMap, createReqVO.getIsCalculateLoss())) - .setIsEnable(true); - itemRequisitionDetails.add(detailDO); - } - ItemRequisitionSaveReqVO saveReqVO = new ItemRequisitionSaveReqVO(); - saveReqVO.setFeedingPipeline(createReqVO.getFeedingPipeline()); - saveReqVO.setWorkerId(createReqVO.getWorkerId()); - saveReqVO.setItemRequisitionDetails(itemRequisitionDetails); - saveReqVO.setStatus(ItemRequisitionStatusEnum.下达.getValue()); - if (saveReqVO.getRequisitionDate() == null) - saveReqVO.setRequisitionDate(LocalDateTime.now()); -// // 创建领料单 - Long id = itemRequisitionService.createItemRequisition(saveReqVO); +// 不再根据产品 BOM 自动生成领料单。analysisService.getItemAnalysis(plan) 内部会查询 BomDetailDO, +// 产品没有 BOM 时会抛出“产品BOM不存在”,所以 /mes/plan/create 暂时跳过该链路。 +// List list = analysisService.getItemAnalysis(plan); // 产品 num +//// Map bomYieldRateMap = buildBomYieldRateMap(plan.getProductId(), createReqVO.getIsCalculateLoss()); +// List itemRequisitionDetails = new ArrayList<>(); +// for (ItemRequisitionAndStock item : list) { +// ItemRequisitionDetailDO detailDO = new ItemRequisitionDetailDO().builder() +// .productId(item.getItemId()) +// .unitId(item.getUnitId()) +// .number(item.getNumber()) +// .isEnable(true) +// .build(); +// itemRequisitionDetails.add(detailDO); +// } +// ItemRequisitionSaveReqVO saveReqVO = new ItemRequisitionSaveReqVO(); +// saveReqVO.setFeedingPipeline(createReqVO.getFeedingPipeline()); +// saveReqVO.setWorkerId(createReqVO.getWorkerId()); +// saveReqVO.setItemRequisitionDetails(itemRequisitionDetails); +// saveReqVO.setStatus(ItemRequisitionStatusEnum.下达.getValue()); +// if (saveReqVO.getRequisitionDate() == null) +// saveReqVO.setRequisitionDate(LocalDateTime.now()); +//// // 创建领料单 +// Long id = itemRequisitionService.createItemRequisition(saveReqVO); //更新计划状态 plan.setStatus(PlanStatusEnum.已排产.getValue()); plan.setIsEnable(true); plan.setProductionManagerId(createReqVO.getWorkerId()); plan.setFeedingPipeline(createReqVO.getFeedingPipeline()); plan.setStartTime(LocalDateTime.now()); - plan.setRequisitionId(id); +// plan.setRequisitionId(id); planMapper.insert(plan); + savePlanProcessRouteItems(plan.getId(), plan.getProcessRouteId(), createReqVO.getProcessRouteItems()); //添加计划操作记录 PlanRecordDO planRecordDO = new PlanRecordDO(); planRecordDO.setTaskId(plan.getTaskId()); @@ -256,71 +278,71 @@ public class PlanServiceImpl implements PlanService { - saveReqVO.setPlanId(plan.getId()); - saveReqVO.setId(id); - itemRequisitionService.updateItemRequisition(saveReqVO); - // 创建入领料出库单 - ErpStockOutSaveReqVO stockOut = new ErpStockOutSaveReqVO(); - List itemList = new ArrayList<>(); - stockOut.setOutTime(LocalDateTime.now()); - stockOut.setOutType("领料出库"); - - ErpWarehouseDO warehouse = erpWarehouseMapper.selectOne( - Wrappers.lambdaQuery() - .orderByDesc(ErpWarehouseDO::getCreateTime) - .last("limit 1") - ); - for (ItemRequisitionAndStock requisition : list) { - ErpStockOutSaveReqVOItem item = new ErpStockOutSaveReqVOItem(); - item.setWarehouseId(warehouse.getId()); - item.setProductId(requisition.getItemId()); - item.setCount(calculateLossNumber(requisition.getItemId(), requisition.getNumber(), bomYieldRateMap, createReqVO.getIsCalculateLoss())); - itemList.add(item); - } - stockOut.setItems(itemList); - stockOut.setPlanCode(StringUtils.isEmpty(plan.getCode()) ? code : plan.getCode()); - stockOutService.createStockOut(stockOut); +// saveReqVO.setPlanId(plan.getId()); +// saveReqVO.setId(id); +// itemRequisitionService.updateItemRequisition(saveReqVO); +// // 创建入领料出库单 +// ErpStockOutSaveReqVO stockOut = new ErpStockOutSaveReqVO(); +// List itemList = new ArrayList<>(); +// stockOut.setOutTime(LocalDateTime.now()); +// stockOut.setOutType("领料出库"); +// +// ErpWarehouseDO warehouse = erpWarehouseMapper.selectOne( +// Wrappers.lambdaQuery() +// .orderByDesc(ErpWarehouseDO::getCreateTime) +// .last("limit 1") +// ); +// for (ItemRequisitionAndStock requisition : list) { +// ErpStockOutSaveReqVOItem item = new ErpStockOutSaveReqVOItem(); +// item.setWarehouseId(warehouse.getId()); +// item.setProductId(requisition.getItemId()); +// item.setCount(requisition.getNumber()); +// itemList.add(item); +// } +// stockOut.setItems(itemList); +// stockOut.setPlanCode(StringUtils.isEmpty(plan.getCode()) ? code : plan.getCode()); +// stockOutService.createStockOut(stockOut); return plan.getId(); } - private Map buildBomYieldRateMap(Long productId, Boolean isCalculateLoss) { - if (!Boolean.TRUE.equals(isCalculateLoss) || productId == null) { - return Collections.emptyMap(); - } - BomDO bomDO = bomMapper.selectByProductId(productId); - if (bomDO == null || bomDO.getId() == null) { - return Collections.emptyMap(); - } - List bomDetails = bomDetailMapper.selectList( - Wrappers.lambdaQuery() - .eq(BomDetailDO::getBomId, bomDO.getId()) - .eq(BomDetailDO::getIsEnable, true) - ); - if (CollUtil.isEmpty(bomDetails)) { - return Collections.emptyMap(); - } - return bomDetails.stream() - .filter(detail -> detail.getProductId() != null) - .collect(Collectors.toMap( - BomDetailDO::getProductId, - BomDetailDO::getYieldRate, - (first, second) -> first - )); - } - - private BigDecimal calculateLossNumber(Long itemId, BigDecimal originalNumber, - Map bomYieldRateMap, - Boolean isCalculateLoss) { - if (!Boolean.TRUE.equals(isCalculateLoss) || itemId == null || originalNumber == null) { - return originalNumber; - } - BigDecimal yieldRate = bomYieldRateMap.get(itemId); - if (yieldRate == null) { - return originalNumber; - } - BigDecimal rate = yieldRate.divide(BigDecimal.valueOf(100), 8, RoundingMode.HALF_UP); - return originalNumber.multiply(BigDecimal.ONE.add(rate)).stripTrailingZeros(); - } +// private Map buildBomYieldRateMap(Long productId, Boolean isCalculateLoss) { +// if (!Boolean.TRUE.equals(isCalculateLoss) || productId == null) { +// return Collections.emptyMap(); +// } +// BomDO bomDO = bomMapper.selectByProductId(productId); +// if (bomDO == null || bomDO.getId() == null) { +// return Collections.emptyMap(); +// } +// List bomDetails = bomDetailMapper.selectList( +// Wrappers.lambdaQuery() +// .eq(BomDetailDO::getBomId, bomDO.getId()) +// .eq(BomDetailDO::getIsEnable, true) +// ); +// if (CollUtil.isEmpty(bomDetails)) { +// return Collections.emptyMap(); +// } +// return bomDetails.stream() +// .filter(detail -> detail.getProductId() != null) +// .collect(Collectors.toMap( +// BomDetailDO::getProductId, +// BomDetailDO::getYieldRate, +// (first, second) -> first +// )); +// } + +// private BigDecimal calculateLossNumber(Long itemId, BigDecimal originalNumber, +// Map bomYieldRateMap, +// Boolean isCalculateLoss) { +// if (!Boolean.TRUE.equals(isCalculateLoss) || itemId == null || originalNumber == null) { +// return originalNumber; +// } +// BigDecimal yieldRate = bomYieldRateMap.get(itemId); +// if (yieldRate == null) { +// return originalNumber; +// } +// BigDecimal rate = yieldRate.divide(BigDecimal.valueOf(100), 8, RoundingMode.HALF_UP); +// return originalNumber.multiply(BigDecimal.ONE.add(rate)).stripTrailingZeros(); +// } @Override @Transactional(rollbackFor = Exception.class) @@ -354,7 +376,65 @@ public class PlanServiceImpl implements PlanService { TaskDetailDO taskDetailDO = taskService.getTaskDetail(updateObj.getTaskDetailId()); updateObj.setProductId(taskDetailDO.getProductId()); } + fillProcessRouteSnapshot(updateObj); planMapper.updateById(updateObj); + if (updateReqVO.getProcessRouteItems() != null || updateReqVO.getProcessRouteId() != null) { + planProcessRouteItemMapper.deleteByPlanId(updateReqVO.getId()); + savePlanProcessRouteItems(updateReqVO.getId(), updateObj.getProcessRouteId(), updateReqVO.getProcessRouteItems()); + } + } + + private void fillProcessRouteSnapshot(PlanDO plan) { + if (plan.getProcessRouteId() == null) { + plan.setProcessRouteName(null); + return; + } + ProcessRouteDO processRoute = processRouteMapper.selectById(plan.getProcessRouteId()); + plan.setProcessRouteName(processRoute == null ? null : processRoute.getName()); + } + + private void savePlanProcessRouteItems(Long planId, Long processRouteId, List reqItems) { + if (planId == null || processRouteId == null) { + return; + } + List routeItems = processRouteItemMapper.selectListByRouteId(processRouteId); + if (CollUtil.isEmpty(routeItems)) { + return; + } + + Map reqItemMap = Optional.ofNullable(reqItems) + .orElse(Collections.emptyList()) + .stream() + .filter(item -> item.getProcessRouteItemId() != null) + .collect(Collectors.toMap(PlanProcessRouteItemSaveReqVO::getProcessRouteItemId, item -> item, (first, second) -> second)); + + Set deviceIds = Optional.ofNullable(reqItems) + .orElse(Collections.emptyList()) + .stream() + .map(PlanProcessRouteItemSaveReqVO::getDeviceId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + Map deviceMap = CollUtil.isEmpty(deviceIds) ? Collections.emptyMap() + : deviceLedgerMapper.selectBatchIds(deviceIds).stream() + .collect(Collectors.toMap(DeviceLedgerDO::getId, item -> item, (first, second) -> first)); + + for (ProcessRouteItemDO routeItem : routeItems) { + PlanProcessRouteItemSaveReqVO reqItem = reqItemMap.get(routeItem.getId()); + DeviceLedgerDO device = reqItem == null || reqItem.getDeviceId() == null ? null : deviceMap.get(reqItem.getDeviceId()); + + PlanProcessRouteItemDO planRouteItem = new PlanProcessRouteItemDO(); + planRouteItem.setPlanId(planId); + planRouteItem.setProcessRouteId(processRouteId); + planRouteItem.setProcessRouteItemId(routeItem.getId()); + planRouteItem.setSort(reqItem != null && reqItem.getSort() != null ? reqItem.getSort() : routeItem.getSort()); + planRouteItem.setProcessParameterId(routeItem.getProcessParameterId()); + planRouteItem.setProcessParameterCode(routeItem.getProcessParameterCode()); + planRouteItem.setProcessParameterName(routeItem.getProcessParameterName()); + planRouteItem.setDeviceId(reqItem == null ? null : reqItem.getDeviceId()); + planRouteItem.setDeviceName(device == null ? null : device.getDeviceName()); + planRouteItem.setRemark(reqItem == null ? routeItem.getRemark() : reqItem.getRemark()); + planProcessRouteItemMapper.insert(planRouteItem); + } } @Override @@ -411,6 +491,8 @@ public class PlanServiceImpl implements PlanService { } else { planRespVO.setDeviceName(null); } + planRespVO.setProcessRouteItems(BeanUtils.toBean( + planProcessRouteItemMapper.selectListByPlanId(planDO.getId()), PlanProcessRouteItemRespVO.class)); return planRespVO; } @@ -619,7 +701,7 @@ public class PlanServiceImpl implements PlanService { convertSet(list, PlanDO::getTaskId)); Map organizationMap = organizationService.getMap( convertSet(list, PlanDO::getFeedingPipeline)); - return BeanUtils.toBean(list, PlanRespVO.class, item -> { + List respList = BeanUtils.toBean(list, PlanRespVO.class, item -> { MapUtils.findAndThen(map, item.getProductId(), product -> item.setProductName(product.getName())); @@ -632,6 +714,27 @@ public class PlanServiceImpl implements PlanService { MapUtils.findAndThen(organizationMap, item.getFeedingPipeline(), organization -> item.setFeedingPipelineName(organization.getName())); }); + fillPlanProcessRouteItems(respList); + return respList; + } + + private void fillPlanProcessRouteItems(List list) { + if (CollUtil.isEmpty(list)) { + return; + } + Set planIds = list.stream().map(PlanRespVO::getId).filter(Objects::nonNull).collect(Collectors.toSet()); + if (CollUtil.isEmpty(planIds)) { + return; + } + List routeItems = planProcessRouteItemMapper.selectList( + Wrappers.lambdaQuery() + .in(PlanProcessRouteItemDO::getPlanId, planIds) + .orderByAsc(PlanProcessRouteItemDO::getSort) + .orderByAsc(PlanProcessRouteItemDO::getId)); + Map> routeItemMap = routeItems.stream() + .collect(Collectors.groupingBy(PlanProcessRouteItemDO::getPlanId, LinkedHashMap::new, + Collectors.mapping(item -> BeanUtils.toBean(item, PlanProcessRouteItemRespVO.class), Collectors.toList()))); + list.forEach(item -> item.setProcessRouteItems(routeItemMap.getOrDefault(item.getId(), Collections.emptyList()))); } @Override public List buildProgress(List list){ @@ -672,11 +775,12 @@ public class PlanServiceImpl implements PlanService { List itemRequisitionDetails = new ArrayList<>(); for (ItemRequisitionAndStock item : list) { - ItemRequisitionDetailDO detailDO = new ItemRequisitionDetailDO() - .setProductId(item.getItemId()) - .setUnitId(item.getUnitId()) - .setNumber(item.getNumber()) - .setIsEnable(true); + ItemRequisitionDetailDO detailDO = new ItemRequisitionDetailDO().builder() + .productId(item.getItemId()) + .unitId(item.getUnitId()) + .number(item.getNumber()) + .isEnable(true) + .build(); itemRequisitionDetails.add(detailDO); } saveReqVO.setItemRequisitionDetails(itemRequisitionDetails); @@ -700,12 +804,13 @@ public class PlanServiceImpl implements PlanService { @Transactional(rollbackFor = Exception.class) public Boolean arrangePlanNum(ItemRequisitionSaveReqVO saveReqVO, PlanDO planDO) { // 创建派工记录 - PaigongRecordDO paigongRecordDO = new PaigongRecordDO() - .setPaigongNum(saveReqVO.getPaigongNum()) - .setPipeline(saveReqVO.getPipeline()) - .setIsPreProduction(saveReqVO.getIsPreProduction()) - .setPlanId(planDO.getId()) - .setPaigongTime(LocalDateTime.now()); + PaigongRecordDO paigongRecordDO = new PaigongRecordDO().builder() + .paigongNum(saveReqVO.getPaigongNum()) + .pipeline(saveReqVO.getPipeline()) + .isPreProduction(saveReqVO.getIsPreProduction()) + .planId(planDO.getId()) + .paigongTime(LocalDateTime.now()) + .build(); paigongRecordMapper.insert(paigongRecordDO); // 更新未派工数量 planDO.setNoPaigongNumber(planDO.getPlanNumber()-saveReqVO.getPaigongNum()); @@ -877,6 +982,44 @@ public class PlanServiceImpl implements PlanService { return new PageResult<>(page.getRecords(), page.getTotal()); } + @Override + @Transactional(rollbackFor = Exception.class) + public void changeLine(PlanLineChangeReqVO reqVO) { + PlanDO plan = planMapper.selectById(reqVO.getPlanId()); + if (plan == null) { + throw exception(PLAN_NOT_EXISTS); + } + PlanProcessRouteItemDO planRouteItem = planProcessRouteItemMapper.selectById(reqVO.getPlanProcessRouteItemId()); + if (planRouteItem == null || !Objects.equals(planRouteItem.getPlanId(), reqVO.getPlanId())) { + throw exception(PLAN_NOT_EXISTS); + } + DeviceLedgerDO afterDevice = deviceLedgerMapper.selectById(reqVO.getDeviceId()); + + PlanLineChangeRecordDO record = new PlanLineChangeRecordDO(); + record.setPlanId(plan.getId()); + record.setPlanProcessRouteItemId(planRouteItem.getId()); + record.setProcessParameterId(planRouteItem.getProcessParameterId()); + record.setProcessParameterName(planRouteItem.getProcessParameterName()); + record.setBeforeDeviceId(planRouteItem.getDeviceId()); + record.setBeforeDeviceName(planRouteItem.getDeviceName()); + record.setAfterDeviceId(reqVO.getDeviceId()); + record.setAfterDeviceName(afterDevice == null ? null : afterDevice.getDeviceName()); + record.setChangeTime(LocalDateTime.now()); + record.setOperatorUserId(getLoginUserId()); + record.setPassNumber(plan.getPassNumber()); + record.setRemark(reqVO.getRemark()); + planLineChangeRecordMapper.insert(record); + + planRouteItem.setDeviceId(reqVO.getDeviceId()); + planRouteItem.setDeviceName(afterDevice == null ? null : afterDevice.getDeviceName()); + planProcessRouteItemMapper.updateById(planRouteItem); + } + + @Override + public List getLineChangeRecordList(Long planId) { + return BeanUtils.toBean(planLineChangeRecordMapper.selectListByPlanId(planId), PlanLineChangeRecordRespVO.class); + } + }