Commit 6a2a05b32ef9d8a86c052b9664ec579952fd7f60
1 parent
660a8515
✨ feat(mes): 添加生产批号字段及相关逻辑
在多个 VO 和服务中添加生产批号字段,更新相关逻辑以支持批号的生成和管理。此变更旨在增强物料消耗记录的追踪能力,确保生产过程中的批次管理更加清晰和有效。
Showing
22 changed files
with
313 additions
and
51 deletions
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/feedback/vo/MesProFeedbackRespVO.java
| @@ -110,6 +110,9 @@ public class MesProFeedbackRespVO { | @@ -110,6 +110,9 @@ public class MesProFeedbackRespVO { | ||
| 110 | @Schema(description = "过期日期") | 110 | @Schema(description = "过期日期") |
| 111 | private LocalDateTime expireDate; | 111 | private LocalDateTime expireDate; |
| 112 | 112 | ||
| 113 | + @Schema(description = "生产批号", example = "LOT20260321") | ||
| 114 | + private String lotNumber; | ||
| 115 | + | ||
| 113 | @Schema(description = "排产数量", example = "5000.00") | 116 | @Schema(description = "排产数量", example = "5000.00") |
| 114 | @ExcelProperty("排产数量") | 117 | @ExcelProperty("排产数量") |
| 115 | private BigDecimal scheduledQuantity; | 118 | private BigDecimal scheduledQuantity; |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/feedback/vo/MesProFeedbackSaveReqVO.java
| @@ -49,6 +49,9 @@ public class MesProFeedbackSaveReqVO { | @@ -49,6 +49,9 @@ public class MesProFeedbackSaveReqVO { | ||
| 49 | @Schema(description = "过期日期") | 49 | @Schema(description = "过期日期") |
| 50 | private LocalDateTime expireDate; | 50 | private LocalDateTime expireDate; |
| 51 | 51 | ||
| 52 | + @Schema(description = "生产批号", example = "LOT20260321") | ||
| 53 | + private String lotNumber; | ||
| 54 | + | ||
| 52 | @Schema(description = "排产数量", example = "5000.00") | 55 | @Schema(description = "排产数量", example = "5000.00") |
| 53 | private BigDecimal scheduledQuantity; | 56 | private BigDecimal scheduledQuantity; |
| 54 | 57 |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/MesProTaskController.java
| @@ -134,7 +134,7 @@ public class MesProTaskController { | @@ -134,7 +134,7 @@ public class MesProTaskController { | ||
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | @GetMapping("/gantt-list") | 136 | @GetMapping("/gantt-list") |
| 137 | - @Operation(summary = "获得甘特图任务列表", description = "对齐 KTG:后端组装工单=project + 任务=task 列表") | 137 | + @Operation(summary = "获得甘特图任务列表", description = "后端组装工单=project + 任务=task 列表") |
| 138 | @PreAuthorize("@ss.hasPermission('mes:pro-task:query')") | 138 | @PreAuthorize("@ss.hasPermission('mes:pro-task:query')") |
| 139 | public CommonResult<List<GanttDataRespVO>> listGanttTaskList(@Valid MesProWorkOrderPageReqVO reqVO) { | 139 | public CommonResult<List<GanttDataRespVO>> listGanttTaskList(@Valid MesProWorkOrderPageReqVO reqVO) { |
| 140 | // 1.1 查询匹配的工单(不分页) | 140 | // 1.1 查询匹配的工单(不分页) |
| @@ -251,7 +251,7 @@ public class MesProTaskController { | @@ -251,7 +251,7 @@ public class MesProTaskController { | ||
| 251 | } | 251 | } |
| 252 | 252 | ||
| 253 | /** | 253 | /** |
| 254 | - * 拼接甘特图显示文本,对齐 KTG 格式:"[产品名][数量][单位]" | 254 | + * 拼接甘特图显示文本,格式:"[产品名][数量][单位]" |
| 255 | */ | 255 | */ |
| 256 | private String buildGanttText(MesMdItemDO item, BigDecimal quantity, | 256 | private String buildGanttText(MesMdItemDO item, BigDecimal quantity, |
| 257 | Map<Long, MesMdUnitMeasureDO> unitMap) { | 257 | Map<Long, MesMdUnitMeasureDO> unitMap) { |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/GanttDataRespVO.java
| @@ -8,7 +8,7 @@ import java.math.BigDecimal; | @@ -8,7 +8,7 @@ import java.math.BigDecimal; | ||
| 8 | import java.time.LocalDateTime; | 8 | import java.time.LocalDateTime; |
| 9 | 9 | ||
| 10 | /** | 10 | /** |
| 11 | - * 甘特图数据项 VO(对齐 KTG GanttData) | 11 | + * 甘特图数据项 VO |
| 12 | */ | 12 | */ |
| 13 | @Schema(description = "管理后台 - 甘特图数据项 Response VO") | 13 | @Schema(description = "管理后台 - 甘特图数据项 Response VO") |
| 14 | @Data | 14 | @Data |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/feedback/MesProFeedbackDO.java
| @@ -97,6 +97,12 @@ public class MesProFeedbackDO extends BaseDO { | @@ -97,6 +97,12 @@ public class MesProFeedbackDO extends BaseDO { | ||
| 97 | */ | 97 | */ |
| 98 | private LocalDateTime expireDate; | 98 | private LocalDateTime expireDate; |
| 99 | /** | 99 | /** |
| 100 | + * 生产批号 | ||
| 101 | + * | ||
| 102 | + * TODO @芋艿:预留字段,当前未启用 | ||
| 103 | + */ | ||
| 104 | + private String lotNumber; | ||
| 105 | + /** | ||
| 100 | * 排产数量 | 106 | * 排产数量 |
| 101 | */ | 107 | */ |
| 102 | private BigDecimal scheduledQuantity; | 108 | private BigDecimal scheduledQuantity; |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/task/MesProTaskMapper.java
| @@ -5,8 +5,10 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | @@ -5,8 +5,10 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 5 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | 5 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 6 | import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskPageReqVO; | 6 | import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskPageReqVO; |
| 7 | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO; | 7 | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO; |
| 8 | +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | ||
| 8 | import org.apache.ibatis.annotations.Mapper; | 9 | import org.apache.ibatis.annotations.Mapper; |
| 9 | 10 | ||
| 11 | +import java.math.BigDecimal; | ||
| 10 | import java.util.List; | 12 | import java.util.List; |
| 11 | 13 | ||
| 12 | /** | 14 | /** |
| @@ -42,4 +44,15 @@ public interface MesProTaskMapper extends BaseMapperX<MesProTaskDO> { | @@ -42,4 +44,15 @@ public interface MesProTaskMapper extends BaseMapperX<MesProTaskDO> { | ||
| 42 | .orderByDesc(MesProTaskDO::getId)); | 44 | .orderByDesc(MesProTaskDO::getId)); |
| 43 | } | 45 | } |
| 44 | 46 | ||
| 47 | + default void updateProducedQuantity(Long id, | ||
| 48 | + BigDecimal incrProducedQuantity, | ||
| 49 | + BigDecimal incrQualifyQuantity, | ||
| 50 | + BigDecimal incrUnqualifyQuantity) { | ||
| 51 | + update(null, new LambdaUpdateWrapper<MesProTaskDO>() | ||
| 52 | + .eq(MesProTaskDO::getId, id) | ||
| 53 | + .setSql("produced_quantity = IFNULL(produced_quantity, 0) + " + incrProducedQuantity) | ||
| 54 | + .setSql("qualify_quantity = IFNULL(qualify_quantity, 0) + " + incrQualifyQuantity) | ||
| 55 | + .setSql("unqualify_quantity = IFNULL(unqualify_quantity, 0) + " + incrUnqualifyQuantity)); | ||
| 56 | + } | ||
| 57 | + | ||
| 45 | } | 58 | } |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkorderMapper.java
| @@ -5,8 +5,11 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | @@ -5,8 +5,11 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 5 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | 5 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 6 | import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkOrderPageReqVO; | 6 | import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkOrderPageReqVO; |
| 7 | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO; | 7 | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO; |
| 8 | +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | ||
| 8 | import org.apache.ibatis.annotations.Mapper; | 9 | import org.apache.ibatis.annotations.Mapper; |
| 9 | 10 | ||
| 11 | +import java.math.BigDecimal; | ||
| 12 | + | ||
| 10 | /** | 13 | /** |
| 11 | * MES 生产工单 Mapper | 14 | * MES 生产工单 Mapper |
| 12 | * | 15 | * |
| @@ -32,4 +35,10 @@ public interface MesProWorkOrderMapper extends BaseMapperX<MesProWorkOrderDO> { | @@ -32,4 +35,10 @@ public interface MesProWorkOrderMapper extends BaseMapperX<MesProWorkOrderDO> { | ||
| 32 | return selectOne(MesProWorkOrderDO::getCode, code); | 35 | return selectOne(MesProWorkOrderDO::getCode, code); |
| 33 | } | 36 | } |
| 34 | 37 | ||
| 38 | + default void updateProducedQuantity(Long id, BigDecimal incrQuantityProduced) { | ||
| 39 | + update(null, new LambdaUpdateWrapper<MesProWorkOrderDO>() | ||
| 40 | + .eq(MesProWorkOrderDO::getId, id) | ||
| 41 | + .setSql("quantity_produced = IFNULL(quantity_produced, 0) + " + incrQuantityProduced)); | ||
| 42 | + } | ||
| 43 | + | ||
| 35 | } | 44 | } |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/packages/MesWmPackageLineMapper.java
| @@ -17,7 +17,7 @@ public interface MesWmPackageLineMapper extends BaseMapperX<MesWmPackageLineDO> | @@ -17,7 +17,7 @@ public interface MesWmPackageLineMapper extends BaseMapperX<MesWmPackageLineDO> | ||
| 17 | 17 | ||
| 18 | default PageResult<MesWmPackageLineDO> selectPage(MesWmPackageLinePageReqVO reqVO) { | 18 | default PageResult<MesWmPackageLineDO> selectPage(MesWmPackageLinePageReqVO reqVO) { |
| 19 | // TODO @芋艿:需确认“父箱详情 -> 装箱清单”是否需要汇总当前箱及其所有子孙箱的明细; | 19 | // TODO @芋艿:需确认“父箱详情 -> 装箱清单”是否需要汇总当前箱及其所有子孙箱的明细; |
| 20 | - // ktg-mes 当前通过 SQL + ancestors 实现聚合查询,这里现在仅按 packageId 精确查询。 | 20 | + // TODO @芋艿:对齐,当前通过 SQL + ancestors 实现聚合查询,这里现在仅按 packageId 精确查询。 |
| 21 | return selectPage(reqVO, new LambdaQueryWrapperX<MesWmPackageLineDO>() | 21 | return selectPage(reqVO, new LambdaQueryWrapperX<MesWmPackageLineDO>() |
| 22 | .eqIfPresent(MesWmPackageLineDO::getPackageId, reqVO.getPackageId()) | 22 | .eqIfPresent(MesWmPackageLineDO::getPackageId, reqVO.getPackageId()) |
| 23 | .orderByDesc(MesWmPackageLineDO::getId)); | 23 | .orderByDesc(MesWmPackageLineDO::getId)); |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productproduce/MesWmProductProduceLineMapper.java
| @@ -31,4 +31,8 @@ public interface MesWmProductProduceLineMapper extends BaseMapperX<MesWmProductP | @@ -31,4 +31,8 @@ public interface MesWmProductProduceLineMapper extends BaseMapperX<MesWmProductP | ||
| 31 | delete(MesWmProductProduceLineDO::getProduceId, produceId); | 31 | delete(MesWmProductProduceLineDO::getProduceId, produceId); |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | + default List<MesWmProductProduceLineDO> selectListByFeedbackId(Long feedbackId) { | ||
| 35 | + return selectList(MesWmProductProduceLineDO::getFeedbackId, feedbackId); | ||
| 36 | + } | ||
| 37 | + | ||
| 34 | } | 38 | } |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/DictTypeConstants.java
| @@ -47,7 +47,6 @@ public interface DictTypeConstants { | @@ -47,7 +47,6 @@ public interface DictTypeConstants { | ||
| 47 | String MES_PRO_FEEDBACK_CHANNEL = "mes_pro_feedback_channel"; // MES 生产报工途径 | 47 | String MES_PRO_FEEDBACK_CHANNEL = "mes_pro_feedback_channel"; // MES 生产报工途径 |
| 48 | String MES_WM_ARRIVAL_NOTICE_STATUS = "mes_wm_arrival_notice_status"; // MES 到货通知单状态 | 48 | String MES_WM_ARRIVAL_NOTICE_STATUS = "mes_wm_arrival_notice_status"; // MES 到货通知单状态 |
| 49 | String MES_WM_ITEM_RECEIPT_STATUS = "mes_wm_item_receipt_status"; // MES 采购入库单状态 | 49 | String MES_WM_ITEM_RECEIPT_STATUS = "mes_wm_item_receipt_status"; // MES 采购入库单状态 |
| 50 | - String MES_WM_PRODUCT_PRODUCE_STATUS = "mes_product_produce_status"; // MES 生产入库单状态 | ||
| 51 | String MES_WM_QUALITY_STATUS = "mes_wm_quality_status"; // MES 质量状态(待检/合格/不合格) | 50 | String MES_WM_QUALITY_STATUS = "mes_wm_quality_status"; // MES 质量状态(待检/合格/不合格) |
| 52 | String MES_WM_RETURN_ISSUE_STATUS = "mes_return_issue_status"; // MES 生产退料单状态 | 51 | String MES_WM_RETURN_ISSUE_STATUS = "mes_return_issue_status"; // MES 生产退料单状态 |
| 53 | String MES_WM_RETURN_ISSUE_TYPE = "mes_return_issue_type"; // MES 生产退料类型 | 52 | String MES_WM_RETURN_ISSUE_TYPE = "mes_return_issue_type"; // MES 生产退料类型 |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackService.java
| @@ -66,11 +66,24 @@ public interface MesProFeedbackService { | @@ -66,11 +66,24 @@ public interface MesProFeedbackService { | ||
| 66 | void rejectFeedback(Long id); | 66 | void rejectFeedback(Long id); |
| 67 | 67 | ||
| 68 | /** | 68 | /** |
| 69 | - * 审批报工(审批中 -> 已完成 或 待检验) | 69 | + * 审批通过报工单,执行生产入库流程 |
| 70 | * | 70 | * |
| 71 | - * @param id 编号 | ||
| 72 | - * @param userId 当前操作用户编号 | ||
| 73 | - * @return true=已完成, false=待检验 | 71 | + * <p>整体流程如下: |
| 72 | + * <ol> | ||
| 73 | + * <li>校验报工单状态和数量</li> | ||
| 74 | + * <li>生成物料消耗单,扣减线边库物料</li> | ||
| 75 | + * <li>根据工序配置(关键工序 & 是否需要检验)分情况处理: | ||
| 76 | + * <ul> | ||
| 77 | + * <li><b>关键工序 + 需要检验</b>:生成产出单(质量状态=待检验),报工单状态 → 待检验,等待质检完成回调</li> | ||
| 78 | + * <li><b>关键工序 + 无需检验</b>:生成产出单(按合格/不合格拆行),执行产品入库,更新任务/工单已生产数量,报工单状态 → 已完成</li> | ||
| 79 | + * <li><b>非关键工序</b>:不生成产出单,不更新任务/工单数量,报工单状态 → 已完成</li> | ||
| 80 | + * </ul> | ||
| 81 | + * </li> | ||
| 82 | + * </ol> | ||
| 83 | + * | ||
| 84 | + * @param id 报工单编号 | ||
| 85 | + * @param userId 当前操作用户编号(审核人) | ||
| 86 | + * @return true=已完成, false=待检验(需等质检回调) | ||
| 74 | */ | 87 | */ |
| 75 | boolean approveFeedback(Long id, Long userId); | 88 | boolean approveFeedback(Long id, Long userId); |
| 76 | 89 |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java
| @@ -8,15 +8,20 @@ import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedba | @@ -8,15 +8,20 @@ import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedba | ||
| 8 | import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackSaveReqVO; | 8 | import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackSaveReqVO; |
| 9 | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO; | 9 | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO; |
| 10 | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO; | 10 | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO; |
| 11 | -import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDO; | ||
| 12 | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO; | 11 | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO; |
| 12 | +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDO; | ||
| 13 | +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO; | ||
| 14 | +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceLineDO; | ||
| 13 | import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackMapper; | 15 | import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackMapper; |
| 14 | import cn.iocoder.yudao.module.mes.enums.pro.MesProFeedbackStatusEnum; | 16 | import cn.iocoder.yudao.module.mes.enums.pro.MesProFeedbackStatusEnum; |
| 17 | +import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum; | ||
| 15 | import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService; | 18 | import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService; |
| 16 | import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService; | 19 | import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService; |
| 17 | import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService; | 20 | import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService; |
| 18 | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; | 21 | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; |
| 19 | import cn.iocoder.yudao.module.mes.service.wm.itemconsume.MesWmItemConsumeService; | 22 | import cn.iocoder.yudao.module.mes.service.wm.itemconsume.MesWmItemConsumeService; |
| 23 | +import cn.iocoder.yudao.module.mes.service.wm.productproduce.MesWmProductProduceLineService; | ||
| 24 | +import cn.iocoder.yudao.module.mes.service.wm.productproduce.MesWmProductProduceService; | ||
| 20 | import jakarta.annotation.Resource; | 25 | import jakarta.annotation.Resource; |
| 21 | import org.springframework.context.annotation.Lazy; | 26 | import org.springframework.context.annotation.Lazy; |
| 22 | import org.springframework.stereotype.Service; | 27 | import org.springframework.stereotype.Service; |
| @@ -25,6 +30,7 @@ import org.springframework.validation.annotation.Validated; | @@ -25,6 +30,7 @@ import org.springframework.validation.annotation.Validated; | ||
| 25 | 30 | ||
| 26 | import java.math.BigDecimal; | 31 | import java.math.BigDecimal; |
| 27 | import java.time.LocalDateTime; | 32 | import java.time.LocalDateTime; |
| 33 | +import java.util.List; | ||
| 28 | 34 | ||
| 29 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 35 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 30 | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | 36 | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| @@ -44,19 +50,19 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService { | @@ -44,19 +50,19 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService { | ||
| 44 | 50 | ||
| 45 | @Resource | 51 | @Resource |
| 46 | private MesProWorkOrderService workOrderService; | 52 | private MesProWorkOrderService workOrderService; |
| 47 | - | ||
| 48 | @Resource | 53 | @Resource |
| 49 | private MesProRouteProcessService routeProcessService; | 54 | private MesProRouteProcessService routeProcessService; |
| 50 | - | ||
| 51 | @Resource | 55 | @Resource |
| 52 | private MesMdWorkstationService workstationService; | 56 | private MesMdWorkstationService workstationService; |
| 53 | - | ||
| 54 | @Resource | 57 | @Resource |
| 55 | @Lazy // 避免循环依赖 | 58 | @Lazy // 避免循环依赖 |
| 56 | private MesProTaskService taskService; | 59 | private MesProTaskService taskService; |
| 57 | - | ||
| 58 | @Resource | 60 | @Resource |
| 59 | private MesWmItemConsumeService itemConsumeService; | 61 | private MesWmItemConsumeService itemConsumeService; |
| 62 | + @Resource | ||
| 63 | + private MesWmProductProduceService productProduceService; | ||
| 64 | + @Resource | ||
| 65 | + private MesWmProductProduceLineService produceLineService; | ||
| 60 | 66 | ||
| 61 | @Override | 67 | @Override |
| 62 | public Long createFeedback(MesProFeedbackSaveReqVO createReqVO) { | 68 | public Long createFeedback(MesProFeedbackSaveReqVO createReqVO) { |
| @@ -143,39 +149,75 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService { | @@ -143,39 +149,75 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService { | ||
| 143 | throw exception(PRO_FEEDBACK_UNCHECK_QUANTITY_EXISTS, feedback.getUncheckQuantity()); | 149 | throw exception(PRO_FEEDBACK_UNCHECK_QUANTITY_EXISTS, feedback.getUncheckQuantity()); |
| 144 | } | 150 | } |
| 145 | 151 | ||
| 146 | - // 2. 物料消耗:根据工序 BOM 生成消耗记录 | 152 | + // 2. 物料消耗:根据工序 BOM 生成消耗记录并执行扣减 |
| 147 | MesWmItemConsumeDO itemConsume = itemConsumeService.generateItemConsume(feedback); | 153 | MesWmItemConsumeDO itemConsume = itemConsumeService.generateItemConsume(feedback); |
| 148 | if (itemConsume != null) { | 154 | if (itemConsume != null) { |
| 149 | itemConsumeService.finishItemConsume(itemConsume.getId()); | 155 | itemConsumeService.finishItemConsume(itemConsume.getId()); |
| 150 | } | 156 | } |
| 151 | 157 | ||
| 152 | - // 3. 查询工序的 keyFlag + checkFlag | 158 | + // 3. 查询工序的关键工序标识 + 检验标识 |
| 153 | MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId( | 159 | MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId( |
| 154 | feedback.getRouteId(), feedback.getProcessId()); | 160 | feedback.getRouteId(), feedback.getProcessId()); |
| 155 | boolean keyFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getKeyFlag()); | 161 | boolean keyFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getKeyFlag()); |
| 156 | boolean checkFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getCheckFlag()); | 162 | boolean checkFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getCheckFlag()); |
| 157 | 163 | ||
| 158 | - // 4. 情况一:关键工序 + 需要检验 → 待检验状态(等质检完成后入库) | ||
| 159 | - if (keyFlag && checkFlag) { | ||
| 160 | - feedbackMapper.updateById(new MesProFeedbackDO().setId(id) | ||
| 161 | - .setStatus(MesProFeedbackStatusEnum.UNCHECK.getStatus()) | ||
| 162 | - .setApproveUserId(userId)); | ||
| 163 | - // TODO @芋艿:WM 产品产出(WmProductProduceService)待补充 generateProductProduce(质量状态=待检验) | ||
| 164 | - return false; | ||
| 165 | - } | ||
| 166 | - | ||
| 167 | - // 4.1 情况二:关键工序 + 无需检验 → 直接完成 | 164 | + // 4. 关键工序:生成产出单,并根据是否需要检验决定入库方式 |
| 168 | if (keyFlag) { | 165 | if (keyFlag) { |
| 169 | - // TODO @芋艿:WM 产品产出(WmProductProduceService)待补充 generateProductProduce + executeProductProduce | ||
| 170 | - // TODO @芋艿:updateProTaskAndWorkorder 待补充级联更新任务/工单的已生产数量 | 166 | + // 4.1 需要检验:生成产出单(质量状态=待检验),更新报工状态为待检验,等质检完成回调后再入库 |
| 167 | + if (checkFlag) { | ||
| 168 | + // TODO @AI(from codex):当前这里只生成待检产出并把报工置为待检验;后续还需要在 IPQC 完成时继续拆分产出行、完成产出入库、 | ||
| 169 | + // 回写任务/工单进度,并把报工状态改为已完成。 | ||
| 170 | + productProduceService.generateProductProduce(feedback, true); | ||
| 171 | + feedbackMapper.updateById(new MesProFeedbackDO().setId(id) | ||
| 172 | + .setStatus(MesProFeedbackStatusEnum.UNCHECK.getStatus())); | ||
| 173 | + return false; | ||
| 174 | + } | ||
| 175 | + // 4.2 无需检验:生成产出单(按合格/不合格拆行),直接完成入库,并更新任务/工单的已生产数量 | ||
| 176 | + MesWmProductProduceDO produce = productProduceService.generateProductProduce(feedback, false); | ||
| 177 | + productProduceService.finishProductProduce(produce.getId()); | ||
| 178 | + updateTaskAndWorkOrderByFeedback(feedback); | ||
| 171 | } | 179 | } |
| 172 | - // 4.2 更新状态为已完成 + 审核人 | 180 | + |
| 181 | + // 5. 非关键工序:不生成产出单,不更新任务/工单数量,直接完成 | ||
| 173 | feedbackMapper.updateById(new MesProFeedbackDO().setId(id) | 182 | feedbackMapper.updateById(new MesProFeedbackDO().setId(id) |
| 174 | .setStatus(MesProFeedbackStatusEnum.FINISHED.getStatus()) | 183 | .setStatus(MesProFeedbackStatusEnum.FINISHED.getStatus()) |
| 175 | .setApproveUserId(userId)); | 184 | .setApproveUserId(userId)); |
| 176 | return true; // 已完成 | 185 | return true; // 已完成 |
| 177 | } | 186 | } |
| 178 | 187 | ||
| 188 | + /** | ||
| 189 | + * 根据当前报工单的最终结果,更新生产任务和生产工单的进度 | ||
| 190 | + * | ||
| 191 | + * <ul> | ||
| 192 | + * <li>使用报工数量(feedbackQuantity),累加任务和工单的已生产数量</li> | ||
| 193 | + * <li>使用产出单行按 qualityStatus 聚合的合格品/不合格品数量,累加任务的合格品/不合格品数量 | ||
| 194 | + * (不直接用 feedback 上的数量,确保质检回调场景下数量来源正确)</li> | ||
| 195 | + * </ul> | ||
| 196 | + * | ||
| 197 | + * @param feedback 报工单 | ||
| 198 | + */ | ||
| 199 | + private void updateTaskAndWorkOrderByFeedback(MesProFeedbackDO feedback) { | ||
| 200 | + // 1. 查询该报工单关联的所有产出单行,按质量状态聚合数量 | ||
| 201 | + BigDecimal qualifiedQty = BigDecimal.ZERO; | ||
| 202 | + BigDecimal unqualifiedQty = BigDecimal.ZERO; | ||
| 203 | + List<MesWmProductProduceLineDO> lines = produceLineService.getProductProduceLineListByFeedbackId(feedback.getId()); | ||
| 204 | + for (MesWmProductProduceLineDO line : lines) { | ||
| 205 | + if (ObjUtil.equal(line.getQualityStatus(), MesWmQualityStatusEnum.FAIL.getStatus())) { | ||
| 206 | + unqualifiedQty = unqualifiedQty.add(line.getQuantity()); | ||
| 207 | + } | ||
| 208 | + if (ObjUtil.equal(line.getQualityStatus(), MesWmQualityStatusEnum.PASS.getStatus())) { | ||
| 209 | + qualifiedQty = qualifiedQty.add(line.getQuantity()); | ||
| 210 | + } | ||
| 211 | + } | ||
| 212 | + | ||
| 213 | + // 2. 更新任务的已生产/合格/不合格数量 | ||
| 214 | + taskService.updateProducedQuantity(feedback.getTaskId(), | ||
| 215 | + feedback.getFeedbackQuantity(), qualifiedQty, unqualifiedQty); | ||
| 216 | + // 3. 更新工单的已生产数量 | ||
| 217 | + workOrderService.updateProducedQuantity(feedback.getWorkOrderId(), | ||
| 218 | + feedback.getFeedbackQuantity()); | ||
| 219 | + } | ||
| 220 | + | ||
| 179 | // ==================== 校验方法 ==================== | 221 | // ==================== 校验方法 ==================== |
| 180 | 222 | ||
| 181 | private MesProFeedbackDO validateFeedbackExists(Long id) { | 223 | private MesProFeedbackDO validateFeedbackExists(Long id) { |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskService.java
| @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskSaveRe | @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskSaveRe | ||
| 6 | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO; | 6 | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO; |
| 7 | import jakarta.validation.Valid; | 7 | import jakarta.validation.Valid; |
| 8 | 8 | ||
| 9 | +import java.math.BigDecimal; | ||
| 9 | import java.util.Collection; | 10 | import java.util.Collection; |
| 10 | import java.util.List; | 11 | import java.util.List; |
| 11 | import java.util.Map; | 12 | import java.util.Map; |
| @@ -121,4 +122,17 @@ public interface MesProTaskService { | @@ -121,4 +122,17 @@ public interface MesProTaskService { | ||
| 121 | */ | 122 | */ |
| 122 | void cancelTaskByOrderId(Long workOrderId); | 123 | void cancelTaskByOrderId(Long workOrderId); |
| 123 | 124 | ||
| 125 | + /** | ||
| 126 | + * 累加任务的已生产、合格、不合格数量 | ||
| 127 | + * | ||
| 128 | + * @param id 任务编号 | ||
| 129 | + * @param incrProducedQuantity 本次已生产数量增量(不良品也计入) | ||
| 130 | + * @param incrQualifyQuantity 本次合格品数量增量 | ||
| 131 | + * @param incrUnqualifyQuantity 本次不合格品数量增量 | ||
| 132 | + */ | ||
| 133 | + void updateProducedQuantity(Long id, | ||
| 134 | + BigDecimal incrProducedQuantity, | ||
| 135 | + BigDecimal incrQualifyQuantity, | ||
| 136 | + BigDecimal incrUnqualifyQuantity); | ||
| 137 | + | ||
| 124 | } | 138 | } |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java
| @@ -196,4 +196,15 @@ public class MesProTaskServiceImpl implements MesProTaskService { | @@ -196,4 +196,15 @@ public class MesProTaskServiceImpl implements MesProTaskService { | ||
| 196 | } | 196 | } |
| 197 | } | 197 | } |
| 198 | 198 | ||
| 199 | + @Override | ||
| 200 | + public void updateProducedQuantity(Long id, | ||
| 201 | + BigDecimal incrProducedQuantity, | ||
| 202 | + BigDecimal incrQualifyQuantity, | ||
| 203 | + BigDecimal incrUnqualifyQuantity) { | ||
| 204 | + // 校验任务存在 | ||
| 205 | + validateTaskExists(id); | ||
| 206 | + // 更新数量 | ||
| 207 | + taskMapper.updateProducedQuantity(id, incrProducedQuantity, incrQualifyQuantity, incrUnqualifyQuantity); | ||
| 208 | + } | ||
| 209 | + | ||
| 199 | } | 210 | } |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkorderService.java
| @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkO | @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkO | ||
| 6 | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO; | 6 | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO; |
| 7 | import jakarta.validation.Valid; | 7 | import jakarta.validation.Valid; |
| 8 | 8 | ||
| 9 | +import java.math.BigDecimal; | ||
| 9 | import java.util.Collection; | 10 | import java.util.Collection; |
| 10 | import java.util.List; | 11 | import java.util.List; |
| 11 | import java.util.Map; | 12 | import java.util.Map; |
| @@ -119,4 +120,12 @@ public interface MesProWorkOrderService { | @@ -119,4 +120,12 @@ public interface MesProWorkOrderService { | ||
| 119 | */ | 120 | */ |
| 120 | List<MesProWorkOrderDO> getWorkOrderSimpleList(); | 121 | List<MesProWorkOrderDO> getWorkOrderSimpleList(); |
| 121 | 122 | ||
| 123 | + /** | ||
| 124 | + * 累加工单的已生产数量 | ||
| 125 | + * | ||
| 126 | + * @param id 工单编号 | ||
| 127 | + * @param incrQuantityProduced 本次已生产数量增量 | ||
| 128 | + */ | ||
| 129 | + void updateProducedQuantity(Long id, BigDecimal incrQuantityProduced); | ||
| 130 | + | ||
| 122 | } | 131 | } |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkorderServiceImpl.java
| @@ -20,6 +20,7 @@ import org.springframework.stereotype.Service; | @@ -20,6 +20,7 @@ import org.springframework.stereotype.Service; | ||
| 20 | import org.springframework.transaction.annotation.Transactional; | 20 | import org.springframework.transaction.annotation.Transactional; |
| 21 | import org.springframework.validation.annotation.Validated; | 21 | import org.springframework.validation.annotation.Validated; |
| 22 | 22 | ||
| 23 | +import java.math.BigDecimal; | ||
| 23 | import java.time.LocalDateTime; | 24 | import java.time.LocalDateTime; |
| 24 | import java.util.Collection; | 25 | import java.util.Collection; |
| 25 | import java.util.Collections; | 26 | import java.util.Collections; |
| @@ -233,4 +234,12 @@ public class MesProWorkOrderServiceImpl implements MesProWorkOrderService { | @@ -233,4 +234,12 @@ public class MesProWorkOrderServiceImpl implements MesProWorkOrderService { | ||
| 233 | return workOrderMapper.selectList(); | 234 | return workOrderMapper.selectList(); |
| 234 | } | 235 | } |
| 235 | 236 | ||
| 237 | + @Override | ||
| 238 | + public void updateProducedQuantity(Long id, BigDecimal incrQuantityProduced) { | ||
| 239 | + // 校验工单存在 | ||
| 240 | + validateWorkOrderExists(id); | ||
| 241 | + // 更新数量 | ||
| 242 | + workOrderMapper.updateProducedQuantity(id, incrQuantityProduced); | ||
| 243 | + } | ||
| 244 | + | ||
| 236 | } | 245 | } |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcServiceImpl.java
| @@ -124,7 +124,8 @@ public class MesQcIpqcServiceImpl implements MesQcIpqcService { | @@ -124,7 +124,8 @@ public class MesQcIpqcServiceImpl implements MesQcIpqcService { | ||
| 124 | updateObj.setStatus(MesQcStatusEnum.FINISHED.getStatus()); | 124 | updateObj.setStatus(MesQcStatusEnum.FINISHED.getStatus()); |
| 125 | ipqcMapper.updateById(updateObj); | 125 | ipqcMapper.updateById(updateObj); |
| 126 | 126 | ||
| 127 | - // TODO @芋艿:IPQC 完成时的 PRO/WM 联动(报工反馈更新、产品产出单拆分),待 WM 模块迁移后对接 | 127 | + // TODO @AI(from codex):如果检验来源是报工,需要对待检产出按检验结果拆分合格/不合格行、生成产出明细、完成产品产出入库、 |
| 128 | + // 回写任务/工单数量,并将报工状态从待检验改为已完成。 | ||
| 128 | } | 129 | } |
| 129 | 130 | ||
| 130 | @Override | 131 | @Override |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeServiceImpl.java
| @@ -20,8 +20,6 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. | @@ -20,8 +20,6 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. | ||
| 20 | /** | 20 | /** |
| 21 | * MES 物料消耗记录 Service 实现类 | 21 | * MES 物料消耗记录 Service 实现类 |
| 22 | * | 22 | * |
| 23 | - * 参考 KTG {@code WmItemConsumeServiceImpl#generateItemConsume} | ||
| 24 | - * | ||
| 25 | * @author 芋道源码 | 23 | * @author 芋道源码 |
| 26 | */ | 24 | */ |
| 27 | @Service | 25 | @Service |
| @@ -60,12 +58,13 @@ public class MesWmItemConsumeServiceImpl implements MesWmItemConsumeService { | @@ -60,12 +58,13 @@ public class MesWmItemConsumeServiceImpl implements MesWmItemConsumeService { | ||
| 60 | .consumeId(consume.getId()).itemId(bom.getItemId()) | 58 | .consumeId(consume.getId()).itemId(bom.getItemId()) |
| 61 | .quantity(bom.getQuantity().multiply(feedback.getFeedbackQuantity())).build()); | 59 | .quantity(bom.getQuantity().multiply(feedback.getFeedbackQuantity())).build()); |
| 62 | itemConsumeLineService.createItemConsumeLineBatch(lines); | 60 | itemConsumeLineService.createItemConsumeLineBatch(lines); |
| 61 | + // TODO @AI(from codex):对齐,这里还需要按线边库/FIFO 生成消耗明细,并在库存不足时回退为无批次消耗明细。 | ||
| 63 | return consume; | 62 | return consume; |
| 64 | } | 63 | } |
| 65 | 64 | ||
| 66 | @Override | 65 | @Override |
| 67 | public void finishItemConsume(Long consumeId) { | 66 | public void finishItemConsume(Long consumeId) { |
| 68 | - // TODO @芋艿:待 MaterialStockService 补充 decreaseStock 后实现库存扣减 | 67 | + // TODO @AI(from codex):对齐,这里还需要按消耗明细扣减线边库库存,不能只把消耗单状态改为已完成。 |
| 69 | // List<MesWmItemConsumeLineDO> lines = itemConsumeLineService.getItemConsumeLineListByConsumeId(consumeId); | 68 | // List<MesWmItemConsumeLineDO> lines = itemConsumeLineService.getItemConsumeLineListByConsumeId(consumeId); |
| 70 | // for (MesWmItemConsumeLineDO line : lines) { | 69 | // for (MesWmItemConsumeLineDO line : lines) { |
| 71 | // materialStockService.decreaseStock(line.getItemId(), ..., line.getQuantity()); | 70 | // materialStockService.decreaseStock(line.getItemId(), ..., line.getQuantity()); |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceLineService.java
| @@ -74,4 +74,12 @@ public interface MesWmProductProduceLineService { | @@ -74,4 +74,12 @@ public interface MesWmProductProduceLineService { | ||
| 74 | */ | 74 | */ |
| 75 | MesWmProductProduceLineDO validateProductProduceLineExists(Long id); | 75 | MesWmProductProduceLineDO validateProductProduceLineExists(Long id); |
| 76 | 76 | ||
| 77 | + /** | ||
| 78 | + * 根据报工记录ID获取行列表 | ||
| 79 | + * | ||
| 80 | + * @param feedbackId 报工记录ID | ||
| 81 | + * @return 行列表 | ||
| 82 | + */ | ||
| 83 | + List<MesWmProductProduceLineDO> getProductProduceLineListByFeedbackId(Long feedbackId); | ||
| 84 | + | ||
| 77 | } | 85 | } |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceLineServiceImpl.java
| @@ -97,4 +97,9 @@ public class MesWmProductProduceLineServiceImpl implements MesWmProductProduceLi | @@ -97,4 +97,9 @@ public class MesWmProductProduceLineServiceImpl implements MesWmProductProduceLi | ||
| 97 | return line; | 97 | return line; |
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | + @Override | ||
| 101 | + public List<MesWmProductProduceLineDO> getProductProduceLineListByFeedbackId(Long feedbackId) { | ||
| 102 | + return produceLineMapper.selectListByFeedbackId(feedbackId); | ||
| 103 | + } | ||
| 104 | + | ||
| 100 | } | 105 | } |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceService.java
| @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.mes.service.wm.productproduce; | @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.mes.service.wm.productproduce; | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | import cn.iocoder.yudao.module.mes.controller.admin.wm.productproduce.vo.MesWmProductProducePageReqVO; | 4 | import cn.iocoder.yudao.module.mes.controller.admin.wm.productproduce.vo.MesWmProductProducePageReqVO; |
| 5 | import cn.iocoder.yudao.module.mes.controller.admin.wm.productproduce.vo.MesWmProductProduceSaveReqVO; | 5 | import cn.iocoder.yudao.module.mes.controller.admin.wm.productproduce.vo.MesWmProductProduceSaveReqVO; |
| 6 | +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO; | ||
| 6 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO; | 7 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO; |
| 7 | import jakarta.validation.Valid; | 8 | import jakarta.validation.Valid; |
| 8 | 9 | ||
| @@ -79,4 +80,13 @@ public interface MesWmProductProduceService { | @@ -79,4 +80,13 @@ public interface MesWmProductProduceService { | ||
| 79 | */ | 80 | */ |
| 80 | Boolean checkProductProduceQuantity(Long id); | 81 | Boolean checkProductProduceQuantity(Long id); |
| 81 | 82 | ||
| 83 | + /** | ||
| 84 | + * 根据报工记录,自动生成产品产出单(头 + 行 + 明细) | ||
| 85 | + * | ||
| 86 | + * @param feedback 报工记录 | ||
| 87 | + * @param checkFlag 是否需要检验(true=待检验,false=按合格/不合格拆分行) | ||
| 88 | + * @return 生成的产品产出单 | ||
| 89 | + */ | ||
| 90 | + MesWmProductProduceDO generateProductProduce(MesProFeedbackDO feedback, boolean checkFlag); | ||
| 91 | + | ||
| 82 | } | 92 | } |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceServiceImpl.java
| @@ -2,25 +2,36 @@ package cn.iocoder.yudao.module.mes.service.wm.productproduce; | @@ -2,25 +2,36 @@ package cn.iocoder.yudao.module.mes.service.wm.productproduce; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | import cn.hutool.core.util.ObjUtil; | 4 | import cn.hutool.core.util.ObjUtil; |
| 5 | +import cn.hutool.core.util.ObjectUtil; | ||
| 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | 7 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 7 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 8 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 8 | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; | 9 | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; |
| 10 | +import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchGenerateReqVO; | ||
| 9 | import cn.iocoder.yudao.module.mes.controller.admin.wm.productproduce.vo.MesWmProductProducePageReqVO; | 11 | import cn.iocoder.yudao.module.mes.controller.admin.wm.productproduce.vo.MesWmProductProducePageReqVO; |
| 10 | import cn.iocoder.yudao.module.mes.controller.admin.wm.productproduce.vo.MesWmProductProduceSaveReqVO; | 12 | import cn.iocoder.yudao.module.mes.controller.admin.wm.productproduce.vo.MesWmProductProduceSaveReqVO; |
| 13 | +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO; | ||
| 14 | +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO; | ||
| 15 | +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; | ||
| 11 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDetailDO; | 16 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDetailDO; |
| 12 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO; | 17 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO; |
| 13 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceLineDO; | 18 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceLineDO; |
| 19 | +import cn.iocoder.yudao.module.mes.dal.mysql.wm.productproduce.MesWmProductProduceDetailMapper; | ||
| 20 | +import cn.iocoder.yudao.module.mes.dal.mysql.wm.productproduce.MesWmProductProduceLineMapper; | ||
| 14 | import cn.iocoder.yudao.module.mes.dal.mysql.wm.productproduce.MesWmProductProduceMapper; | 21 | import cn.iocoder.yudao.module.mes.dal.mysql.wm.productproduce.MesWmProductProduceMapper; |
| 15 | import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductProduceStatusEnum; | 22 | import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductProduceStatusEnum; |
| 23 | +import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum; | ||
| 16 | import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService; | 24 | import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService; |
| 17 | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; | 25 | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; |
| 26 | +import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService; | ||
| 18 | import jakarta.annotation.Resource; | 27 | import jakarta.annotation.Resource; |
| 19 | import org.springframework.stereotype.Service; | 28 | import org.springframework.stereotype.Service; |
| 20 | import org.springframework.transaction.annotation.Transactional; | 29 | import org.springframework.transaction.annotation.Transactional; |
| 21 | import org.springframework.validation.annotation.Validated; | 30 | import org.springframework.validation.annotation.Validated; |
| 22 | 31 | ||
| 23 | import java.math.BigDecimal; | 32 | import java.math.BigDecimal; |
| 33 | +import java.time.LocalDate; | ||
| 34 | +import java.time.LocalDateTime; | ||
| 24 | import java.util.List; | 35 | import java.util.List; |
| 25 | 36 | ||
| 26 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 37 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| @@ -34,7 +45,13 @@ import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; | @@ -34,7 +45,13 @@ import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; | ||
| 34 | public class MesWmProductProduceServiceImpl implements MesWmProductProduceService { | 45 | public class MesWmProductProduceServiceImpl implements MesWmProductProduceService { |
| 35 | 46 | ||
| 36 | @Resource | 47 | @Resource |
| 37 | - private MesWmProductProduceMapper produceMapper; | 48 | + private MesWmProductProduceMapper productProduceMapper; |
| 49 | + // TODO @芋艿:需要优化,不要直接调用对方的 mapper | ||
| 50 | + @Resource | ||
| 51 | + private MesWmProductProduceLineMapper produceLineMapper; | ||
| 52 | + // TODO @芋艿:需要优化,不要直接调用对方的 mapper | ||
| 53 | + @Resource | ||
| 54 | + private MesWmProductProduceDetailMapper produceDetailMapper; | ||
| 38 | 55 | ||
| 39 | @Resource | 56 | @Resource |
| 40 | private MesWmProductProduceLineService produceLineService; | 57 | private MesWmProductProduceLineService produceLineService; |
| @@ -44,6 +61,8 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic | @@ -44,6 +61,8 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic | ||
| 44 | private MesMdWorkstationService workstationService; | 61 | private MesMdWorkstationService workstationService; |
| 45 | @Resource | 62 | @Resource |
| 46 | private MesProWorkOrderService workOrderService; | 63 | private MesProWorkOrderService workOrderService; |
| 64 | + @Resource | ||
| 65 | + private MesWmBatchService batchService; | ||
| 47 | 66 | ||
| 48 | @Override | 67 | @Override |
| 49 | @Transactional(rollbackFor = Exception.class) | 68 | @Transactional(rollbackFor = Exception.class) |
| @@ -59,7 +78,7 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic | @@ -59,7 +78,7 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic | ||
| 59 | // 2. 插入主表 | 78 | // 2. 插入主表 |
| 60 | MesWmProductProduceDO produce = BeanUtils.toBean(createReqVO, MesWmProductProduceDO.class); | 79 | MesWmProductProduceDO produce = BeanUtils.toBean(createReqVO, MesWmProductProduceDO.class); |
| 61 | produce.setStatus(MesWmProductProduceStatusEnum.PREPARE.getStatus()); | 80 | produce.setStatus(MesWmProductProduceStatusEnum.PREPARE.getStatus()); |
| 62 | - produceMapper.insert(produce); | 81 | + productProduceMapper.insert(produce); |
| 63 | return produce.getId(); | 82 | return produce.getId(); |
| 64 | } | 83 | } |
| 65 | 84 | ||
| @@ -78,7 +97,7 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic | @@ -78,7 +97,7 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic | ||
| 78 | 97 | ||
| 79 | // 2. 更新主表 | 98 | // 2. 更新主表 |
| 80 | MesWmProductProduceDO updateObj = BeanUtils.toBean(updateReqVO, MesWmProductProduceDO.class); | 99 | MesWmProductProduceDO updateObj = BeanUtils.toBean(updateReqVO, MesWmProductProduceDO.class); |
| 81 | - produceMapper.updateById(updateObj); | 100 | + productProduceMapper.updateById(updateObj); |
| 82 | } | 101 | } |
| 83 | 102 | ||
| 84 | @Override | 103 | @Override |
| @@ -92,32 +111,31 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic | @@ -92,32 +111,31 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic | ||
| 92 | // 2.2 级联删除行 | 111 | // 2.2 级联删除行 |
| 93 | produceLineService.deleteProductProduceLineByProduceId(id); | 112 | produceLineService.deleteProductProduceLineByProduceId(id); |
| 94 | // 2.3 删除主表 | 113 | // 2.3 删除主表 |
| 95 | - produceMapper.deleteById(id); | 114 | + productProduceMapper.deleteById(id); |
| 96 | } | 115 | } |
| 97 | 116 | ||
| 98 | @Override | 117 | @Override |
| 99 | public MesWmProductProduceDO getProductProduce(Long id) { | 118 | public MesWmProductProduceDO getProductProduce(Long id) { |
| 100 | - return produceMapper.selectById(id); | 119 | + return productProduceMapper.selectById(id); |
| 101 | } | 120 | } |
| 102 | 121 | ||
| 103 | @Override | 122 | @Override |
| 104 | public PageResult<MesWmProductProduceDO> getProductProducePage(MesWmProductProducePageReqVO pageReqVO) { | 123 | public PageResult<MesWmProductProduceDO> getProductProducePage(MesWmProductProducePageReqVO pageReqVO) { |
| 105 | - return produceMapper.selectPage(pageReqVO); | 124 | + return productProduceMapper.selectPage(pageReqVO); |
| 106 | } | 125 | } |
| 107 | 126 | ||
| 108 | @Override | 127 | @Override |
| 109 | @Transactional(rollbackFor = Exception.class) | 128 | @Transactional(rollbackFor = Exception.class) |
| 110 | public void finishProductProduce(Long id) { | 129 | public void finishProductProduce(Long id) { |
| 111 | - // 1. 校验存在 + 草稿状态 | 130 | + // 1.1 校验存在 + 草稿状态 |
| 112 | validateProductProduceExistsAndPrepare(id); | 131 | validateProductProduceExistsAndPrepare(id); |
| 113 | - | ||
| 114 | - // 2. 校验至少有一条行 | 132 | + // 1.2 校验至少有一条行 |
| 115 | List<MesWmProductProduceLineDO> lines = produceLineService.getProductProduceLineListByProduceId(id); | 133 | List<MesWmProductProduceLineDO> lines = produceLineService.getProductProduceLineListByProduceId(id); |
| 116 | if (CollUtil.isEmpty(lines)) { | 134 | if (CollUtil.isEmpty(lines)) { |
| 117 | throw exception(WM_PRODUCT_PRODUCE_NO_LINE); | 135 | throw exception(WM_PRODUCT_PRODUCE_NO_LINE); |
| 118 | } | 136 | } |
| 119 | 137 | ||
| 120 | - // 3. 校验每行明细数量之和等于行数量 | 138 | + // 2. 校验每行明细数量之和等于行数量 |
| 121 | for (MesWmProductProduceLineDO line : lines) { | 139 | for (MesWmProductProduceLineDO line : lines) { |
| 122 | List<MesWmProductProduceDetailDO> details = produceDetailService.getProductProduceDetailListByLineId(line.getId()); | 140 | List<MesWmProductProduceDetailDO> details = produceDetailService.getProductProduceDetailListByLineId(line.getId()); |
| 123 | BigDecimal totalDetailQty = CollectionUtils.getSumValue(details, | 141 | BigDecimal totalDetailQty = CollectionUtils.getSumValue(details, |
| @@ -127,17 +145,17 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic | @@ -127,17 +145,17 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic | ||
| 127 | } | 145 | } |
| 128 | } | 146 | } |
| 129 | 147 | ||
| 130 | - // TODO @芋艿:完成入库时更新库存(对齐 ktg-mes 的 storageCoreService.processProductProduce),待库存事务模块就绪后实现 | 148 | + // TODO @AI(from codex):对齐,这里还需要按产出明细执行库存入账,而不是只校验明细并更新单据状态。 |
| 131 | 149 | ||
| 132 | - // 4. 更新入库单状态 | ||
| 133 | - produceMapper.updateById(new MesWmProductProduceDO() | 150 | + // 3. 更新入库单状态 |
| 151 | + productProduceMapper.updateById(new MesWmProductProduceDO() | ||
| 134 | .setId(id).setStatus(MesWmProductProduceStatusEnum.FINISHED.getStatus())); | 152 | .setId(id).setStatus(MesWmProductProduceStatusEnum.FINISHED.getStatus())); |
| 135 | } | 153 | } |
| 136 | 154 | ||
| 137 | @Override | 155 | @Override |
| 138 | @Transactional(rollbackFor = Exception.class) | 156 | @Transactional(rollbackFor = Exception.class) |
| 139 | public void cancelProductProduce(Long id) { | 157 | public void cancelProductProduce(Long id) { |
| 140 | - // 校验存在 | 158 | + // 1. 校验存在 |
| 141 | MesWmProductProduceDO produce = validateProductProduceExists(id); | 159 | MesWmProductProduceDO produce = validateProductProduceExists(id); |
| 142 | // 已完成和已取消不允许取消 | 160 | // 已完成和已取消不允许取消 |
| 143 | if (ObjectUtils.equalsAny(produce.getStatus(), | 161 | if (ObjectUtils.equalsAny(produce.getStatus(), |
| @@ -146,8 +164,8 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic | @@ -146,8 +164,8 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic | ||
| 146 | throw exception(WM_PRODUCT_PRODUCE_CANCEL_NOT_ALLOWED); | 164 | throw exception(WM_PRODUCT_PRODUCE_CANCEL_NOT_ALLOWED); |
| 147 | } | 165 | } |
| 148 | 166 | ||
| 149 | - // 取消 | ||
| 150 | - produceMapper.updateById(new MesWmProductProduceDO() | 167 | + // 2. 取消 |
| 168 | + productProduceMapper.updateById(new MesWmProductProduceDO() | ||
| 151 | .setId(id).setStatus(MesWmProductProduceStatusEnum.CANCELED.getStatus())); | 169 | .setId(id).setStatus(MesWmProductProduceStatusEnum.CANCELED.getStatus())); |
| 152 | } | 170 | } |
| 153 | 171 | ||
| @@ -166,8 +184,93 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic | @@ -166,8 +184,93 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic | ||
| 166 | } | 184 | } |
| 167 | 185 | ||
| 168 | @Override | 186 | @Override |
| 187 | + @Transactional(rollbackFor = Exception.class) | ||
| 188 | + public MesWmProductProduceDO generateProductProduce(MesProFeedbackDO feedback, boolean checkFlag) { | ||
| 189 | + // 0. 查询关联的工单(用于获取 clientId 等信息) | ||
| 190 | + MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(feedback.getWorkOrderId()); | ||
| 191 | + | ||
| 192 | + // 1. 创建产出单头 | ||
| 193 | + MesWmProductProduceDO produce = MesWmProductProduceDO.builder() | ||
| 194 | + .workOrderId(feedback.getWorkOrderId()).feedbackId(feedback.getId()).taskId(feedback.getTaskId()) | ||
| 195 | + .workstationId(feedback.getWorkstationId()).processId(feedback.getProcessId()) | ||
| 196 | + .produceDate(LocalDateTime.now()).status(MesWmProductProduceStatusEnum.PREPARE.getStatus()) | ||
| 197 | + .build(); | ||
| 198 | + productProduceMapper.insert(produce); | ||
| 199 | + | ||
| 200 | + // 2. 获取或生成批次 | ||
| 201 | + MesWmBatchGenerateReqVO batchReqVO = new MesWmBatchGenerateReqVO() | ||
| 202 | + .setItemId(feedback.getItemId()) | ||
| 203 | + .setProduceDate(LocalDate.now().atStartOfDay()) // 截断到当天零点,确保同一天的报工生成相同批次号 | ||
| 204 | + .setExpireDate(feedback.getExpireDate()) | ||
| 205 | + .setWorkOrderId(feedback.getWorkOrderId()) | ||
| 206 | + .setClientId(workOrder != null ? workOrder.getClientId() : null) | ||
| 207 | + .setWorkstationId(feedback.getWorkstationId()) | ||
| 208 | + .setLotNumber(feedback.getLotNumber()); | ||
| 209 | + // TODO @AI(from codex):对齐 批次生成还需要带上工单来源单号(salesOrderCode / coCode 对应 orderSourceCode); | ||
| 210 | + // 否则启用该维度的批次规则会生成错误批次或直接缺参失败。 | ||
| 211 | + MesWmBatchDO batch = batchService.getOrGenerateBatchCode(batchReqVO); | ||
| 212 | + Long batchId = batch != null ? batch.getId() : null; | ||
| 213 | + String batchCode = batch != null ? batch.getCode() : null; | ||
| 214 | + | ||
| 215 | + // 3. 根据是否需要检验分支处理 | ||
| 216 | + if (checkFlag) { | ||
| 217 | + // 3.1 需要检验:创建一条行(质量状态=待检验),不生成明细 | ||
| 218 | + MesWmProductProduceLineDO line = buildProduceLine(produce, feedback, batchId, batchCode, | ||
| 219 | + feedback.getFeedbackQuantity(), MesWmQualityStatusEnum.PENDING.getStatus()); | ||
| 220 | + produceLineMapper.insert(line); | ||
| 221 | + // TODO @芋艿:先不生成明细行,等待检验完成时,再根据行的质量状态生成明细行 | ||
| 222 | + } else { | ||
| 223 | + // 3.2 无需检验:按合格品/不合格品各生成一行 + 明细 | ||
| 224 | + BigDecimal qualifiedQty = ObjectUtil.defaultIfNull(feedback.getQualifiedQuantity(), BigDecimal.ZERO); | ||
| 225 | + BigDecimal unqualifiedQty = ObjectUtil.defaultIfNull(feedback.getUnqualifiedQuantity(), BigDecimal.ZERO); | ||
| 226 | + // 3.2.1 不合格品行 + 明细 | ||
| 227 | + if (unqualifiedQty.compareTo(BigDecimal.ZERO) > 0) { | ||
| 228 | + MesWmProductProduceLineDO unqualifiedLine = buildProduceLine(produce, feedback, batchId, batchCode, | ||
| 229 | + unqualifiedQty, MesWmQualityStatusEnum.FAIL.getStatus()); | ||
| 230 | + produceLineMapper.insert(unqualifiedLine); | ||
| 231 | + MesWmProductProduceDetailDO unqualifiedDetail = buildProduceDetail(produce, feedback, batchId, batchCode, | ||
| 232 | + unqualifiedLine.getId(), unqualifiedQty); | ||
| 233 | + produceDetailMapper.insert(unqualifiedDetail); | ||
| 234 | + } | ||
| 235 | + // 3.2.2 合格品行 + 明细 | ||
| 236 | + if (qualifiedQty.compareTo(BigDecimal.ZERO) > 0) { | ||
| 237 | + MesWmProductProduceLineDO qualifiedLine = buildProduceLine(produce, feedback, batchId, batchCode, | ||
| 238 | + qualifiedQty, MesWmQualityStatusEnum.PASS.getStatus()); | ||
| 239 | + produceLineMapper.insert(qualifiedLine); | ||
| 240 | + MesWmProductProduceDetailDO qualifiedDetail = buildProduceDetail(produce, feedback, batchId, batchCode, | ||
| 241 | + qualifiedLine.getId(), qualifiedQty); | ||
| 242 | + produceDetailMapper.insert(qualifiedDetail); | ||
| 243 | + } | ||
| 244 | + } | ||
| 245 | + return produce; | ||
| 246 | + } | ||
| 247 | + | ||
| 248 | + private MesWmProductProduceLineDO buildProduceLine(MesWmProductProduceDO produce, MesProFeedbackDO feedback, | ||
| 249 | + Long batchId, String batchCode, | ||
| 250 | + BigDecimal quantity, Integer qualityStatus) { | ||
| 251 | + return MesWmProductProduceLineDO.builder() | ||
| 252 | + .produceId(produce.getId()).feedbackId(feedback.getId()) | ||
| 253 | + .itemId(feedback.getItemId()).quantity(quantity) | ||
| 254 | + .batchId(batchId).batchCode(batchCode) | ||
| 255 | + .expireDate(feedback.getExpireDate()).lotNumber(feedback.getLotNumber()) | ||
| 256 | + .qualityStatus(qualityStatus) | ||
| 257 | + .build(); | ||
| 258 | + } | ||
| 259 | + | ||
| 260 | + private MesWmProductProduceDetailDO buildProduceDetail(MesWmProductProduceDO produce, MesProFeedbackDO feedback, | ||
| 261 | + Long batchId, String batchCode, | ||
| 262 | + Long lineId, BigDecimal quantity) { | ||
| 263 | + return MesWmProductProduceDetailDO.builder() | ||
| 264 | + .produceId(produce.getId()).lineId(lineId) | ||
| 265 | + .itemId(feedback.getItemId()).quantity(quantity) | ||
| 266 | + .batchId(batchId).batchCode(batchCode) | ||
| 267 | + // TODO @AI(from codex):对齐 产出明细需要补齐虚拟仓/库区/库位(VIRTUAL_WH/VIRTUAL_WS/VIRTUAL_WA),否则后续库存入账定位不完整。 | ||
| 268 | + .build(); | ||
| 269 | + } | ||
| 270 | + | ||
| 271 | + @Override | ||
| 169 | public MesWmProductProduceDO validateProductProduceExists(Long id) { | 272 | public MesWmProductProduceDO validateProductProduceExists(Long id) { |
| 170 | - MesWmProductProduceDO produce = produceMapper.selectById(id); | 273 | + MesWmProductProduceDO produce = productProduceMapper.selectById(id); |
| 171 | if (produce == null) { | 274 | if (produce == null) { |
| 172 | throw exception(WM_PRODUCT_PRODUCE_NOT_EXISTS); | 275 | throw exception(WM_PRODUCT_PRODUCE_NOT_EXISTS); |
| 173 | } | 276 | } |
| @@ -179,10 +282,11 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic | @@ -179,10 +282,11 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic | ||
| 179 | */ | 282 | */ |
| 180 | private MesWmProductProduceDO validateProductProduceExistsAndPrepare(Long id) { | 283 | private MesWmProductProduceDO validateProductProduceExistsAndPrepare(Long id) { |
| 181 | MesWmProductProduceDO produce = validateProductProduceExists(id); | 284 | MesWmProductProduceDO produce = validateProductProduceExists(id); |
| 182 | - if (ObjUtil.notEqual(MesWmProductProduceStatusEnum.PREPARE.getStatus(), produce.getStatus())) { | 285 | + if (ObjUtil.notEqual(produce.getStatus(), MesWmProductProduceStatusEnum.PREPARE.getStatus())) { |
| 183 | throw exception(WM_PRODUCT_PRODUCE_STATUS_INVALID); | 286 | throw exception(WM_PRODUCT_PRODUCE_STATUS_INVALID); |
| 184 | } | 287 | } |
| 185 | return produce; | 288 | return produce; |
| 186 | } | 289 | } |
| 187 | 290 | ||
| 291 | + | ||
| 188 | } | 292 | } |