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 | 110 | @Schema(description = "过期日期") |
| 111 | 111 | private LocalDateTime expireDate; |
| 112 | 112 | |
| 113 | + @Schema(description = "生产批号", example = "LOT20260321") | |
| 114 | + private String lotNumber; | |
| 115 | + | |
| 113 | 116 | @Schema(description = "排产数量", example = "5000.00") |
| 114 | 117 | @ExcelProperty("排产数量") |
| 115 | 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 | 49 | @Schema(description = "过期日期") |
| 50 | 50 | private LocalDateTime expireDate; |
| 51 | 51 | |
| 52 | + @Schema(description = "生产批号", example = "LOT20260321") | |
| 53 | + private String lotNumber; | |
| 54 | + | |
| 52 | 55 | @Schema(description = "排产数量", example = "5000.00") |
| 53 | 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 | 134 | } |
| 135 | 135 | |
| 136 | 136 | @GetMapping("/gantt-list") |
| 137 | - @Operation(summary = "获得甘特图任务列表", description = "对齐 KTG:后端组装工单=project + 任务=task 列表") | |
| 137 | + @Operation(summary = "获得甘特图任务列表", description = "后端组装工单=project + 任务=task 列表") | |
| 138 | 138 | @PreAuthorize("@ss.hasPermission('mes:pro-task:query')") |
| 139 | 139 | public CommonResult<List<GanttDataRespVO>> listGanttTaskList(@Valid MesProWorkOrderPageReqVO reqVO) { |
| 140 | 140 | // 1.1 查询匹配的工单(不分页) |
| ... | ... | @@ -251,7 +251,7 @@ public class MesProTaskController { |
| 251 | 251 | } |
| 252 | 252 | |
| 253 | 253 | /** |
| 254 | - * 拼接甘特图显示文本,对齐 KTG 格式:"[产品名][数量][单位]" | |
| 254 | + * 拼接甘特图显示文本,格式:"[产品名][数量][单位]" | |
| 255 | 255 | */ |
| 256 | 256 | private String buildGanttText(MesMdItemDO item, BigDecimal quantity, |
| 257 | 257 | Map<Long, MesMdUnitMeasureDO> unitMap) { | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/GanttDataRespVO.java
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 | 97 | */ |
| 98 | 98 | private LocalDateTime expireDate; |
| 99 | 99 | /** |
| 100 | + * 生产批号 | |
| 101 | + * | |
| 102 | + * TODO @芋艿:预留字段,当前未启用 | |
| 103 | + */ | |
| 104 | + private String lotNumber; | |
| 105 | + /** | |
| 100 | 106 | * 排产数量 |
| 101 | 107 | */ |
| 102 | 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 | 5 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 6 | 6 | import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskPageReqVO; |
| 7 | 7 | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO; |
| 8 | +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | |
| 8 | 9 | import org.apache.ibatis.annotations.Mapper; |
| 9 | 10 | |
| 11 | +import java.math.BigDecimal; | |
| 10 | 12 | import java.util.List; |
| 11 | 13 | |
| 12 | 14 | /** |
| ... | ... | @@ -42,4 +44,15 @@ public interface MesProTaskMapper extends BaseMapperX<MesProTaskDO> { |
| 42 | 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 | 5 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 6 | 6 | import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkOrderPageReqVO; |
| 7 | 7 | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO; |
| 8 | +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | |
| 8 | 9 | import org.apache.ibatis.annotations.Mapper; |
| 9 | 10 | |
| 11 | +import java.math.BigDecimal; | |
| 12 | + | |
| 10 | 13 | /** |
| 11 | 14 | * MES 生产工单 Mapper |
| 12 | 15 | * |
| ... | ... | @@ -32,4 +35,10 @@ public interface MesProWorkOrderMapper extends BaseMapperX<MesProWorkOrderDO> { |
| 32 | 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 | 17 | |
| 18 | 18 | default PageResult<MesWmPackageLineDO> selectPage(MesWmPackageLinePageReqVO reqVO) { |
| 19 | 19 | // TODO @芋艿:需确认“父箱详情 -> 装箱清单”是否需要汇总当前箱及其所有子孙箱的明细; |
| 20 | - // ktg-mes 当前通过 SQL + ancestors 实现聚合查询,这里现在仅按 packageId 精确查询。 | |
| 20 | + // TODO @芋艿:对齐,当前通过 SQL + ancestors 实现聚合查询,这里现在仅按 packageId 精确查询。 | |
| 21 | 21 | return selectPage(reqVO, new LambdaQueryWrapperX<MesWmPackageLineDO>() |
| 22 | 22 | .eqIfPresent(MesWmPackageLineDO::getPackageId, reqVO.getPackageId()) |
| 23 | 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 | 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 | 47 | String MES_PRO_FEEDBACK_CHANNEL = "mes_pro_feedback_channel"; // MES 生产报工途径 |
| 48 | 48 | String MES_WM_ARRIVAL_NOTICE_STATUS = "mes_wm_arrival_notice_status"; // MES 到货通知单状态 |
| 49 | 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 | 50 | String MES_WM_QUALITY_STATUS = "mes_wm_quality_status"; // MES 质量状态(待检/合格/不合格) |
| 52 | 51 | String MES_WM_RETURN_ISSUE_STATUS = "mes_return_issue_status"; // MES 生产退料单状态 |
| 53 | 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 | 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 | 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 | 8 | import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackSaveReqVO; |
| 9 | 9 | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO; |
| 10 | 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 | 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 | 15 | import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackMapper; |
| 14 | 16 | import cn.iocoder.yudao.module.mes.enums.pro.MesProFeedbackStatusEnum; |
| 17 | +import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum; | |
| 15 | 18 | import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService; |
| 16 | 19 | import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService; |
| 17 | 20 | import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService; |
| 18 | 21 | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; |
| 19 | 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 | 25 | import jakarta.annotation.Resource; |
| 21 | 26 | import org.springframework.context.annotation.Lazy; |
| 22 | 27 | import org.springframework.stereotype.Service; |
| ... | ... | @@ -25,6 +30,7 @@ import org.springframework.validation.annotation.Validated; |
| 25 | 30 | |
| 26 | 31 | import java.math.BigDecimal; |
| 27 | 32 | import java.time.LocalDateTime; |
| 33 | +import java.util.List; | |
| 28 | 34 | |
| 29 | 35 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 30 | 36 | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| ... | ... | @@ -44,19 +50,19 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService { |
| 44 | 50 | |
| 45 | 51 | @Resource |
| 46 | 52 | private MesProWorkOrderService workOrderService; |
| 47 | - | |
| 48 | 53 | @Resource |
| 49 | 54 | private MesProRouteProcessService routeProcessService; |
| 50 | - | |
| 51 | 55 | @Resource |
| 52 | 56 | private MesMdWorkstationService workstationService; |
| 53 | - | |
| 54 | 57 | @Resource |
| 55 | 58 | @Lazy // 避免循环依赖 |
| 56 | 59 | private MesProTaskService taskService; |
| 57 | - | |
| 58 | 60 | @Resource |
| 59 | 61 | private MesWmItemConsumeService itemConsumeService; |
| 62 | + @Resource | |
| 63 | + private MesWmProductProduceService productProduceService; | |
| 64 | + @Resource | |
| 65 | + private MesWmProductProduceLineService produceLineService; | |
| 60 | 66 | |
| 61 | 67 | @Override |
| 62 | 68 | public Long createFeedback(MesProFeedbackSaveReqVO createReqVO) { |
| ... | ... | @@ -143,39 +149,75 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService { |
| 143 | 149 | throw exception(PRO_FEEDBACK_UNCHECK_QUANTITY_EXISTS, feedback.getUncheckQuantity()); |
| 144 | 150 | } |
| 145 | 151 | |
| 146 | - // 2. 物料消耗:根据工序 BOM 生成消耗记录 | |
| 152 | + // 2. 物料消耗:根据工序 BOM 生成消耗记录并执行扣减 | |
| 147 | 153 | MesWmItemConsumeDO itemConsume = itemConsumeService.generateItemConsume(feedback); |
| 148 | 154 | if (itemConsume != null) { |
| 149 | 155 | itemConsumeService.finishItemConsume(itemConsume.getId()); |
| 150 | 156 | } |
| 151 | 157 | |
| 152 | - // 3. 查询工序的 keyFlag + checkFlag | |
| 158 | + // 3. 查询工序的关键工序标识 + 检验标识 | |
| 153 | 159 | MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId( |
| 154 | 160 | feedback.getRouteId(), feedback.getProcessId()); |
| 155 | 161 | boolean keyFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getKeyFlag()); |
| 156 | 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 | 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 | 182 | feedbackMapper.updateById(new MesProFeedbackDO().setId(id) |
| 174 | 183 | .setStatus(MesProFeedbackStatusEnum.FINISHED.getStatus()) |
| 175 | 184 | .setApproveUserId(userId)); |
| 176 | 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 | 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 | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO; |
| 7 | 7 | import jakarta.validation.Valid; |
| 8 | 8 | |
| 9 | +import java.math.BigDecimal; | |
| 9 | 10 | import java.util.Collection; |
| 10 | 11 | import java.util.List; |
| 11 | 12 | import java.util.Map; |
| ... | ... | @@ -121,4 +122,17 @@ public interface MesProTaskService { |
| 121 | 122 | */ |
| 122 | 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 | 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 | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO; |
| 7 | 7 | import jakarta.validation.Valid; |
| 8 | 8 | |
| 9 | +import java.math.BigDecimal; | |
| 9 | 10 | import java.util.Collection; |
| 10 | 11 | import java.util.List; |
| 11 | 12 | import java.util.Map; |
| ... | ... | @@ -119,4 +120,12 @@ public interface MesProWorkOrderService { |
| 119 | 120 | */ |
| 120 | 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 | 20 | import org.springframework.transaction.annotation.Transactional; |
| 21 | 21 | import org.springframework.validation.annotation.Validated; |
| 22 | 22 | |
| 23 | +import java.math.BigDecimal; | |
| 23 | 24 | import java.time.LocalDateTime; |
| 24 | 25 | import java.util.Collection; |
| 25 | 26 | import java.util.Collections; |
| ... | ... | @@ -233,4 +234,12 @@ public class MesProWorkOrderServiceImpl implements MesProWorkOrderService { |
| 233 | 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 | 124 | updateObj.setStatus(MesQcStatusEnum.FINISHED.getStatus()); |
| 125 | 125 | ipqcMapper.updateById(updateObj); |
| 126 | 126 | |
| 127 | - // TODO @芋艿:IPQC 完成时的 PRO/WM 联动(报工反馈更新、产品产出单拆分),待 WM 模块迁移后对接 | |
| 127 | + // TODO @AI(from codex):如果检验来源是报工,需要对待检产出按检验结果拆分合格/不合格行、生成产出明细、完成产品产出入库、 | |
| 128 | + // 回写任务/工单数量,并将报工状态从待检验改为已完成。 | |
| 128 | 129 | } |
| 129 | 130 | |
| 130 | 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 | 20 | /** |
| 21 | 21 | * MES 物料消耗记录 Service 实现类 |
| 22 | 22 | * |
| 23 | - * 参考 KTG {@code WmItemConsumeServiceImpl#generateItemConsume} | |
| 24 | - * | |
| 25 | 23 | * @author 芋道源码 |
| 26 | 24 | */ |
| 27 | 25 | @Service |
| ... | ... | @@ -60,12 +58,13 @@ public class MesWmItemConsumeServiceImpl implements MesWmItemConsumeService { |
| 60 | 58 | .consumeId(consume.getId()).itemId(bom.getItemId()) |
| 61 | 59 | .quantity(bom.getQuantity().multiply(feedback.getFeedbackQuantity())).build()); |
| 62 | 60 | itemConsumeLineService.createItemConsumeLineBatch(lines); |
| 61 | + // TODO @AI(from codex):对齐,这里还需要按线边库/FIFO 生成消耗明细,并在库存不足时回退为无批次消耗明细。 | |
| 63 | 62 | return consume; |
| 64 | 63 | } |
| 65 | 64 | |
| 66 | 65 | @Override |
| 67 | 66 | public void finishItemConsume(Long consumeId) { |
| 68 | - // TODO @芋艿:待 MaterialStockService 补充 decreaseStock 后实现库存扣减 | |
| 67 | + // TODO @AI(from codex):对齐,这里还需要按消耗明细扣减线边库库存,不能只把消耗单状态改为已完成。 | |
| 69 | 68 | // List<MesWmItemConsumeLineDO> lines = itemConsumeLineService.getItemConsumeLineListByConsumeId(consumeId); |
| 70 | 69 | // for (MesWmItemConsumeLineDO line : lines) { |
| 71 | 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 | 74 | */ |
| 75 | 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 | 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 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 4 | import cn.iocoder.yudao.module.mes.controller.admin.wm.productproduce.vo.MesWmProductProducePageReqVO; |
| 5 | 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 | 7 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO; |
| 7 | 8 | import jakarta.validation.Valid; |
| 8 | 9 | |
| ... | ... | @@ -79,4 +80,13 @@ public interface MesWmProductProduceService { |
| 79 | 80 | */ |
| 80 | 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 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.util.ObjUtil; |
| 5 | +import cn.hutool.core.util.ObjectUtil; | |
| 5 | 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | 7 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 7 | 8 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 8 | 9 | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; |
| 10 | +import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchGenerateReqVO; | |
| 9 | 11 | import cn.iocoder.yudao.module.mes.controller.admin.wm.productproduce.vo.MesWmProductProducePageReqVO; |
| 10 | 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 | 16 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDetailDO; |
| 12 | 17 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO; |
| 13 | 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 | 21 | import cn.iocoder.yudao.module.mes.dal.mysql.wm.productproduce.MesWmProductProduceMapper; |
| 15 | 22 | import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductProduceStatusEnum; |
| 23 | +import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum; | |
| 16 | 24 | import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService; |
| 17 | 25 | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; |
| 26 | +import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService; | |
| 18 | 27 | import jakarta.annotation.Resource; |
| 19 | 28 | import org.springframework.stereotype.Service; |
| 20 | 29 | import org.springframework.transaction.annotation.Transactional; |
| 21 | 30 | import org.springframework.validation.annotation.Validated; |
| 22 | 31 | |
| 23 | 32 | import java.math.BigDecimal; |
| 33 | +import java.time.LocalDate; | |
| 34 | +import java.time.LocalDateTime; | |
| 24 | 35 | import java.util.List; |
| 25 | 36 | |
| 26 | 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 | 45 | public class MesWmProductProduceServiceImpl implements MesWmProductProduceService { |
| 35 | 46 | |
| 36 | 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 | 56 | @Resource |
| 40 | 57 | private MesWmProductProduceLineService produceLineService; |
| ... | ... | @@ -44,6 +61,8 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic |
| 44 | 61 | private MesMdWorkstationService workstationService; |
| 45 | 62 | @Resource |
| 46 | 63 | private MesProWorkOrderService workOrderService; |
| 64 | + @Resource | |
| 65 | + private MesWmBatchService batchService; | |
| 47 | 66 | |
| 48 | 67 | @Override |
| 49 | 68 | @Transactional(rollbackFor = Exception.class) |
| ... | ... | @@ -59,7 +78,7 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic |
| 59 | 78 | // 2. 插入主表 |
| 60 | 79 | MesWmProductProduceDO produce = BeanUtils.toBean(createReqVO, MesWmProductProduceDO.class); |
| 61 | 80 | produce.setStatus(MesWmProductProduceStatusEnum.PREPARE.getStatus()); |
| 62 | - produceMapper.insert(produce); | |
| 81 | + productProduceMapper.insert(produce); | |
| 63 | 82 | return produce.getId(); |
| 64 | 83 | } |
| 65 | 84 | |
| ... | ... | @@ -78,7 +97,7 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic |
| 78 | 97 | |
| 79 | 98 | // 2. 更新主表 |
| 80 | 99 | MesWmProductProduceDO updateObj = BeanUtils.toBean(updateReqVO, MesWmProductProduceDO.class); |
| 81 | - produceMapper.updateById(updateObj); | |
| 100 | + productProduceMapper.updateById(updateObj); | |
| 82 | 101 | } |
| 83 | 102 | |
| 84 | 103 | @Override |
| ... | ... | @@ -92,32 +111,31 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic |
| 92 | 111 | // 2.2 级联删除行 |
| 93 | 112 | produceLineService.deleteProductProduceLineByProduceId(id); |
| 94 | 113 | // 2.3 删除主表 |
| 95 | - produceMapper.deleteById(id); | |
| 114 | + productProduceMapper.deleteById(id); | |
| 96 | 115 | } |
| 97 | 116 | |
| 98 | 117 | @Override |
| 99 | 118 | public MesWmProductProduceDO getProductProduce(Long id) { |
| 100 | - return produceMapper.selectById(id); | |
| 119 | + return productProduceMapper.selectById(id); | |
| 101 | 120 | } |
| 102 | 121 | |
| 103 | 122 | @Override |
| 104 | 123 | public PageResult<MesWmProductProduceDO> getProductProducePage(MesWmProductProducePageReqVO pageReqVO) { |
| 105 | - return produceMapper.selectPage(pageReqVO); | |
| 124 | + return productProduceMapper.selectPage(pageReqVO); | |
| 106 | 125 | } |
| 107 | 126 | |
| 108 | 127 | @Override |
| 109 | 128 | @Transactional(rollbackFor = Exception.class) |
| 110 | 129 | public void finishProductProduce(Long id) { |
| 111 | - // 1. 校验存在 + 草稿状态 | |
| 130 | + // 1.1 校验存在 + 草稿状态 | |
| 112 | 131 | validateProductProduceExistsAndPrepare(id); |
| 113 | - | |
| 114 | - // 2. 校验至少有一条行 | |
| 132 | + // 1.2 校验至少有一条行 | |
| 115 | 133 | List<MesWmProductProduceLineDO> lines = produceLineService.getProductProduceLineListByProduceId(id); |
| 116 | 134 | if (CollUtil.isEmpty(lines)) { |
| 117 | 135 | throw exception(WM_PRODUCT_PRODUCE_NO_LINE); |
| 118 | 136 | } |
| 119 | 137 | |
| 120 | - // 3. 校验每行明细数量之和等于行数量 | |
| 138 | + // 2. 校验每行明细数量之和等于行数量 | |
| 121 | 139 | for (MesWmProductProduceLineDO line : lines) { |
| 122 | 140 | List<MesWmProductProduceDetailDO> details = produceDetailService.getProductProduceDetailListByLineId(line.getId()); |
| 123 | 141 | BigDecimal totalDetailQty = CollectionUtils.getSumValue(details, |
| ... | ... | @@ -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 | 152 | .setId(id).setStatus(MesWmProductProduceStatusEnum.FINISHED.getStatus())); |
| 135 | 153 | } |
| 136 | 154 | |
| 137 | 155 | @Override |
| 138 | 156 | @Transactional(rollbackFor = Exception.class) |
| 139 | 157 | public void cancelProductProduce(Long id) { |
| 140 | - // 校验存在 | |
| 158 | + // 1. 校验存在 | |
| 141 | 159 | MesWmProductProduceDO produce = validateProductProduceExists(id); |
| 142 | 160 | // 已完成和已取消不允许取消 |
| 143 | 161 | if (ObjectUtils.equalsAny(produce.getStatus(), |
| ... | ... | @@ -146,8 +164,8 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic |
| 146 | 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 | 169 | .setId(id).setStatus(MesWmProductProduceStatusEnum.CANCELED.getStatus())); |
| 152 | 170 | } |
| 153 | 171 | |
| ... | ... | @@ -166,8 +184,93 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic |
| 166 | 184 | } |
| 167 | 185 | |
| 168 | 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 | 272 | public MesWmProductProduceDO validateProductProduceExists(Long id) { |
| 170 | - MesWmProductProduceDO produce = produceMapper.selectById(id); | |
| 273 | + MesWmProductProduceDO produce = productProduceMapper.selectById(id); | |
| 171 | 274 | if (produce == null) { |
| 172 | 275 | throw exception(WM_PRODUCT_PRODUCE_NOT_EXISTS); |
| 173 | 276 | } |
| ... | ... | @@ -179,10 +282,11 @@ public class MesWmProductProduceServiceImpl implements MesWmProductProduceServic |
| 179 | 282 | */ |
| 180 | 283 | private MesWmProductProduceDO validateProductProduceExistsAndPrepare(Long id) { |
| 181 | 284 | MesWmProductProduceDO produce = validateProductProduceExists(id); |
| 182 | - if (ObjUtil.notEqual(MesWmProductProduceStatusEnum.PREPARE.getStatus(), produce.getStatus())) { | |
| 285 | + if (ObjUtil.notEqual(produce.getStatus(), MesWmProductProduceStatusEnum.PREPARE.getStatus())) { | |
| 183 | 286 | throw exception(WM_PRODUCT_PRODUCE_STATUS_INVALID); |
| 184 | 287 | } |
| 185 | 288 | return produce; |
| 186 | 289 | } |
| 187 | 290 | |
| 291 | + | |
| 188 | 292 | } | ... | ... |