yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOrderDO.java
@@ -127,7 +127,9 @@ // ========== çäº§å ³è ========== /** * çäº§ç¶æï¼0-æªå®æï¼1-已宿 * çäº§ç¶æï¼0-æªå®æï¼1-已宿ï¼2-已忶 * * æä¸¾ {@link cn.iocoder.yudao.module.erp.enums.ErpSaleOrderProductionStatusEnum} */ private Integer productionStatus; yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpSaleOrderProductionStatusEnum.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,22 @@ package cn.iocoder.yudao.module.erp.enums; import lombok.Getter; import lombok.RequiredArgsConstructor; /** * ERP éå®è®¢åçäº§ç¶ææä¸¾ * * @author è¶ çº§ç®¡çå */ @RequiredArgsConstructor @Getter public enum ErpSaleOrderProductionStatusEnum { NOT_FINISHED(0, "æªå®æ"), FINISHED(1, "已宿"), CANCELED(2, "已忶"); // ç产计åè¢«åæ¶ private final Integer status; private final String name; } yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java
@@ -23,6 +23,7 @@ import cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleOrderMapper; import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO; import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; import cn.iocoder.yudao.module.erp.enums.ErpSaleOrderProductionStatusEnum; import cn.iocoder.yudao.module.erp.api.sale.enums.ErpSaleOrderOutStatusEnum; import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService; import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi; @@ -35,6 +36,7 @@ import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -248,6 +250,13 @@ .setMpsId(mpsId)); createdCount++; } // éæ°çæç产计ååï¼è®¢åçäº§ç¶æåå°æªå®æï¼è¦çæ¤åçâ已忶âï¼ if (createdCount > 0) { saleOrderMapper.update(null, new LambdaUpdateWrapper<ErpSaleOrderDO>() .eq(ErpSaleOrderDO::getId, orderId) .set(ErpSaleOrderDO::getProductionStatus, ErpSaleOrderProductionStatusEnum.NOT_FINISHED.getStatus()) .set(ErpSaleOrderDO::getProductionFinishTime, null)); } log.info("[createMpsForSaleOrder] éå®è®¢å({}) å®¡æ ¸å®æï¼å建 MPS {} æ¡ï¼å¤ç¨ MPS {} æ¡", orderId, createdCount, reusedCount); } yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/listener/ProductionFinishedListener.java
@@ -2,6 +2,7 @@ import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO; import cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleOrderMapper; import cn.iocoder.yudao.module.erp.enums.ErpSaleOrderProductionStatusEnum; import cn.iocoder.yudao.module.mes.api.event.ProductionFinishedEvent; import cn.iocoder.yudao.module.mes.api.event.ProductionCanceledEvent; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; @@ -46,9 +47,9 @@ // ä½¿ç¨æ¾å¼ update wrapper æè½æå·²åå¨ç宿æ¶é´çæ£æ´æ°ä¸º NULL saleOrderMapper.update(null, new LambdaUpdateWrapper<ErpSaleOrderDO>() .eq(ErpSaleOrderDO::getId, event.getSaleOrderId()) .set(ErpSaleOrderDO::getProductionStatus, 0) .set(ErpSaleOrderDO::getProductionStatus, ErpSaleOrderProductionStatusEnum.CANCELED.getStatus()) .set(ErpSaleOrderDO::getProductionFinishTime, null)); log.info("[onProductionCanceled] éå®è®¢å {} çäº§ç¶æå·²æ¢å¤ä¸ºæªå®æ", event.getSaleOrderId()); log.info("[onProductionCanceled] éå®è®¢å {} çäº§ç¶æå·²æ´æ°ä¸ºå·²åæ¶", event.getSaleOrderId()); } } yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProcessController.java
@@ -6,15 +6,25 @@ import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProProcessBomItemVO; import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProRouteProcessOutputVO; import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProRouteProcessRespVO; import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProRouteProcessSaveReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessOutputDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO; import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService; import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService; import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService; import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService; import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; @@ -43,6 +53,14 @@ private MesProProcessService processService; @Resource private MesMdItemService mdItemService; @Resource private MesProRouteProductService routeProductService; @Resource private MesWmWarehouseService warehouseService; @Resource private MesWmWarehouseLocationService warehouseLocationService; @Resource private MesWmWarehouseAreaService warehouseAreaService; @PostMapping("/create") @Operation(summary = "å建工èºè·¯çº¿å·¥åº") @@ -111,7 +129,35 @@ } } } // å¡«å å·¥èºè·¯çº¿äº§åä¸é ç½®çé»è®¤æ¥å·¥ä»åºï¼ä¸åæ¶ä½ä¸ºå·¥åºé»è®¤å¼ï¼ fillDefaultOutputWarehouse(result, productId); return success(result); } private void fillDefaultOutputWarehouse(List<MesProRouteProcessRespVO> list, Long productId) { if (CollUtil.isEmpty(list)) { return; } MesProRouteProductDO routeProduct = routeProductService.getRouteProductByItemId(productId); if (routeProduct == null || routeProduct.getWarehouseId() == null) { return; } MesWmWarehouseDO warehouse = warehouseService.getWarehouse(routeProduct.getWarehouseId()); MesWmWarehouseLocationDO location = routeProduct.getLocationId() != null ? warehouseLocationService.getWarehouseLocation(routeProduct.getLocationId()) : null; MesWmWarehouseAreaDO area = routeProduct.getAreaId() != null ? warehouseAreaService.getWarehouseArea(routeProduct.getAreaId()) : null; String warehouseName = warehouse != null ? warehouse.getName() : null; String locationName = location != null ? location.getName() : null; String areaName = area != null ? area.getName() : null; for (MesProRouteProcessRespVO vo : list) { vo.setWarehouseId(routeProduct.getWarehouseId()); vo.setWarehouseName(warehouseName); vo.setLocationId(routeProduct.getLocationId()); vo.setLocationName(locationName); vo.setAreaId(routeProduct.getAreaId()); vo.setAreaName(areaName); } } @GetMapping("/list-by-route") @@ -161,6 +207,41 @@ } Map<Long, MesMdItemDO> outputItemMap = allOutputItemIds.isEmpty() ? Collections.emptyMap() : mdItemService.getItemMap(allOutputItemIds); // 2.1 产åºå½å±é ç½®ï¼productId 为空表示å¤äº§åéç¨ï¼ Map<Long, List<MesProRouteProcessOutputVO>> outputsByRouteProcessId = new HashMap<>(); Set<Long> routeProcessIds = convertSet(list, MesProRouteProcessDO::getId); routeProcessIds.removeAll(Collections.singleton(null)); if (CollUtil.isNotEmpty(routeProcessIds)) { List<MesProRouteProcessOutputDO> outputList = routeProcessService .getRouteProcessOutputListByRouteProcessIds(routeProcessIds); if (CollUtil.isNotEmpty(outputList)) { Set<Long> ownerItemIds = new HashSet<>(); outputList.forEach(output -> { if (output.getProductId() != null) { ownerItemIds.add(output.getProductId()); } if (output.getItemId() != null) { ownerItemIds.add(output.getItemId()); } }); Map<Long, MesMdItemDO> ownerItemMap = mdItemService.getItemMap(ownerItemIds); for (MesProRouteProcessOutputDO output : outputList) { MesProRouteProcessOutputVO outputVO = new MesProRouteProcessOutputVO(); outputVO.setId(output.getId()); outputVO.setRouteProcessId(output.getRouteProcessId()); outputVO.setProductId(output.getProductId()); outputVO.setItemId(output.getItemId()); outputVO.setSort(output.getSort()); outputVO.setRemark(output.getRemark()); MapUtils.findAndThen(ownerItemMap, output.getItemId(), item -> outputVO.setItemCode(item.getCode()).setItemName(item.getName())); MapUtils.findAndThen(ownerItemMap, output.getProductId(), product -> outputVO.setProductName(product.getName())); outputsByRouteProcessId.computeIfAbsent(output.getRouteProcessId(), key -> new ArrayList<>()).add(outputVO); } } } // 3. æ¼æ¥ VO List<MesProRouteProcessRespVO> result = BeanUtils.toBean(list, MesProRouteProcessRespVO.class); for (int i = 0; i < result.size(); i++) { @@ -188,6 +269,7 @@ vo.setOutputItemName(firstItem.getName()); } } vo.setOutputs(outputsByRouteProcessId.getOrDefault(vo.getId(), Collections.emptyList())); } return result; } yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessOutputVO.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,37 @@ package cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @Schema(description = "管çåå° - MES å·¥èºè·¯çº¿å·¥åºäº§åºå½å± Response VO") @Data public class MesProRouteProcessOutputVO { @Schema(description = "ç¼å·", example = "1") private Long id; @Schema(description = "å·¥èºè·¯çº¿å·¥åºç¼å·", example = "1") private Long routeProcessId; @Schema(description = "å½å±äº§åç¼å·ï¼ä¸ºç©ºè¡¨ç¤ºå¤äº§åéç¨") private Long productId; @Schema(description = "å½å±äº§ååç§°") private String productName; @Schema(description = "产åºç©æç¼å·", example = "100") private Long itemId; @Schema(description = "产åºç©æç¼ç ") private String itemCode; @Schema(description = "产åºç©æåç§°") private String itemName; @Schema(description = "æåº", example = "1") private Integer sort; @Schema(description = "夿³¨") private String remark; } yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessRespVO.java
@@ -70,6 +70,27 @@ @Schema(description = "产åºäº§åç©æåç§°ï¼éå·æ¼æ¥ï¼", example = "产åA, 产åB") private String outputItemNames; @Schema(description = "产åºç©æå½å±é ç½®ï¼productId 为空表示å¤äº§åéç¨ï¼") private List<MesProRouteProcessOutputVO> outputs; @Schema(description = "é»è®¤æ¥å·¥ä»åºç¼å·ï¼åèªå·¥èºè·¯çº¿äº§åçæ¥å·¥ä»åºé ç½®ï¼", example = "10") private Long warehouseId; @Schema(description = "é»è®¤æ¥å·¥ä»åºåç§°") private String warehouseName; @Schema(description = "é»è®¤æ¥å·¥åºåºç¼å·", example = "110") private Long locationId; @Schema(description = "é»è®¤æ¥å·¥åºåºåç§°") private String locationName; @Schema(description = "é»è®¤æ¥å·¥åºä½ç¼å·", example = "210") private Long areaId; @Schema(description = "é»è®¤æ¥å·¥åºä½åç§°") private String areaName; @Schema(description = "夿³¨") private String remark; yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessSaveReqVO.java
@@ -49,7 +49,28 @@ @Schema(description = "产åºäº§åç©æç¼å·å表ï¼å¤éï¼ä¸ºç©ºæ¶é»è®¤ç¨å·¥åæåï¼", example = "[100, 101]") private List<Long> outputItemIds; @Schema(description = "产åºç©æå½å±é ç½®ï¼ä¼å äº outputItemIdsï¼productId 为空表示å¤äº§åéç¨ï¼") private List<OutputItem> outputs; @Schema(description = "夿³¨") private String remark; @Schema(description = "产åºç©æå½å±") @Data public static class OutputItem { @Schema(description = "å½å±äº§åç¼å·ï¼ä¸ºç©ºè¡¨ç¤ºå¤äº§åéç¨") private Long productId; @Schema(description = "产åºç©æç¼å·", requiredMode = Schema.RequiredMode.REQUIRED) private Long itemId; @Schema(description = "æåº") private Integer sort; @Schema(description = "夿³¨") private String remark; } } yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/MesProTaskController.java
@@ -222,6 +222,13 @@ return success(new PageResult<>(respVOList, pageResult.getTotal())); } @GetMapping("/pending-count-by-workstation") @Operation(summary = "æå·¥ä½ç«è·å¾å¾ æ¥å·¥ä»»å¡æ°", description = "å·¥ä½å°éæ©å·¥ä½ç«å¡çå±ç¤ºï¼å£å¾åå¾ æ¥å·¥å°è´¦") @PreAuthorize("@ss.hasPermission('mes:pro-task:query')") public CommonResult<Map<Long, Long>> getPendingFeedbackCountByWorkstation() { return success(taskService.getPendingFeedbackCountByWorkstation()); } @GetMapping("/gantt-list") @Operation(summary = "è·å¾çç¹å¾ä»»å¡å表", description = "å端ç»è£ å·¥å=project + ä»»å¡=task å表") @PreAuthorize("@ss.hasPermission('mes:pro-task:query')") yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderProcessController.java
@@ -14,10 +14,16 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO; import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService; import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderBomService; import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService; import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService; import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; @@ -47,6 +53,12 @@ private MesProProcessService processService; @Resource private MesMdItemService itemService; @Resource private MesWmWarehouseService warehouseService; @Resource private MesWmWarehouseLocationService warehouseLocationService; @Resource private MesWmWarehouseAreaService warehouseAreaService; @PostMapping("/create") @Operation(summary = "å建工åå·¥åº") @@ -150,6 +162,19 @@ } Map<Long, MesMdItemDO> itemMap = CollUtil.isNotEmpty(allOutputItemIds) ? itemService.getItemMap(allOutputItemIds) : Collections.emptyMap(); // è·åæ¥å·¥ä»åº/åºåº/åºä½ Set<Long> warehouseIds = convertSet(list, MesProWorkOrderProcessDO::getWarehouseId); warehouseIds.removeAll(Collections.singleton(null)); Set<Long> locationIds = convertSet(list, MesProWorkOrderProcessDO::getLocationId); locationIds.removeAll(Collections.singleton(null)); Set<Long> areaIds = convertSet(list, MesProWorkOrderProcessDO::getAreaId); areaIds.removeAll(Collections.singleton(null)); Map<Long, MesWmWarehouseDO> warehouseMap = CollUtil.isNotEmpty(warehouseIds) ? warehouseService.getWarehouseMap(warehouseIds) : Collections.emptyMap(); Map<Long, MesWmWarehouseLocationDO> locationMap = CollUtil.isNotEmpty(locationIds) ? warehouseLocationService.getWarehouseLocationMap(locationIds) : Collections.emptyMap(); Map<Long, MesWmWarehouseAreaDO> areaMap = CollUtil.isNotEmpty(areaIds) ? warehouseAreaService.getWarehouseAreaMap(areaIds) : Collections.emptyMap(); // 2. æ¥è¯¢ææBOMï¼æå·¥åºåç»ï¼ Set<Long> workOrderIds = convertSet(list, MesProWorkOrderProcessDO::getWorkOrderId); @@ -185,6 +210,13 @@ MapUtils.findAndThen(processMap, vo.getNextProcessId(), process -> { vo.setNextProcessName(process.getName()); }); // æ¥å·¥ä»åº/åºåº/åºä½åç§° MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName())); MapUtils.findAndThen(locationMap, vo.getLocationId(), location -> vo.setLocationName(location.getName())); MapUtils.findAndThen(areaMap, vo.getAreaId(), area -> vo.setAreaName(area.getName())); // 产åºç©æï¼æ¯æå¤éï¼ List<Long> ids = voOutputItemIdsMap.getOrDefault((long) i, Collections.emptyList()); if (!ids.isEmpty()) { yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessRespVO.java
@@ -83,6 +83,24 @@ @Schema(description = "产åºäº§åç©æåç§°ï¼éå·æ¼æ¥ï¼", example = "产åA, 产åB") private String outputItemNames; @Schema(description = "æ¥å·¥ä»åºç¼å·", example = "10") private Long warehouseId; @Schema(description = "æ¥å·¥ä»åºåç§°", example = "æåä»") private String warehouseName; @Schema(description = "æ¥å·¥åºåºç¼å·", example = "110") private Long locationId; @Schema(description = "æ¥å·¥åºåºåç§°", example = "æåä»éç¨åºåº") private String locationName; @Schema(description = "æ¥å·¥åºä½ç¼å·", example = "210") private Long areaId; @Schema(description = "æ¥å·¥åºä½åç§°", example = "æåä»é»è®¤åºä½") private String areaName; @Schema(description = "夿³¨", example = "夿³¨") @ExcelProperty("夿³¨") private String remark; yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessSaveReqVO.java
@@ -52,6 +52,15 @@ @Schema(description = "产åºäº§åç©æç¼å·å表ï¼å¤éï¼ä¸ºç©ºæ¶é»è®¤ç¨å·¥åæåï¼", example = "[100, 101]") private List<Long> outputItemIds; @Schema(description = "æ¥å·¥ä»åºç¼å·", example = "10") private Long warehouseId; @Schema(description = "æ¥å·¥åºåºç¼å·", example = "110") private Long locationId; @Schema(description = "æ¥å·¥åºä½ç¼å·", example = "210") private Long areaId; @Schema(description = "夿³¨") private String remark; yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/route/MesProRouteProcessOutputDO.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,72 @@ package cn.iocoder.yudao.module.mes.dal.dataobject.pro.route; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; /** * MES å·¥èºè·¯çº¿å·¥åºäº§åºå½å± DO * * åä¸éå·¥åºç产åºå¯ä»¥æäº§ååºåï¼å½å±äº§åä¸ºç©ºè¡¨ç¤ºè¯¥è·¯çº¿ä¸ææäº§åéç¨ã * * @author è¶ çº§ç®¡çå */ @TableName("mes_pro_route_process_output") @KeySequence("mes_pro_route_process_output_seq") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor public class MesProRouteProcessOutputDO extends BaseDO { /** * ç¼å· */ @TableId private Long id; /** * å·¥èºè·¯çº¿å·¥åºç¼å· * * å ³è {@link MesProRouteProcessDO#getId()} */ private Long routeProcessId; /** * å·¥èºè·¯çº¿ç¼å· * * å ³è {@link MesProRouteDO#getId()} */ private Long routeId; /** * å·¥åºç¼å· * * å ³è {@link MesProProcessDO#getId()} */ private Long processId; /** * å½å±äº§åç¼å·ï¼ä¸ºç©ºè¡¨ç¤ºå¤äº§åéç¨ * * å ³è {@link MesMdItemDO#getId()} */ private Long productId; /** * 产åºç©æç¼å· * * å ³è {@link MesMdItemDO#getId()} */ private Long itemId; /** * æåº */ private Integer sort; /** * 夿³¨ */ private String remark; } yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderProcessDO.java
@@ -90,6 +90,25 @@ */ private String outputItemId; /** * æ¥å·¥ä»åºç¼å· * * ä¸åæ¶ä»å·¥èºè·¯çº¿äº§åå¸¦å ¥ï¼å¯æå·¥è°æ´ï¼æ¥å·¥äº§åºææ¤ä»åºå ¥åº * å ³è {@link cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO#getId()} */ private Long warehouseId; /** * æ¥å·¥åºåºç¼å· * * å ³è {@link cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO#getId()} */ private Long locationId; /** * æ¥å·¥åºä½ç¼å· * * å ³è {@link cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO#getId()} */ private Long areaId; /** * 夿³¨ */ private String remark; yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/item/MesMdItemMapper.java
@@ -41,10 +41,6 @@ return selectOne(MesMdItemDO::getCode, code); } default MesMdItemDO selectByName(String name) { return selectOne(MesMdItemDO::getName, name); } default Long selectCountByItemTypeId(Long itemTypeId) { return selectCount(MesMdItemDO::getItemTypeId, itemTypeId); } yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/route/MesProRouteProcessOutputMapper.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,47 @@ package cn.iocoder.yudao.module.mes.dal.mysql.pro.route; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessOutputDO; import org.apache.ibatis.annotations.Mapper; import java.util.Collection; import java.util.List; /** * MES å·¥èºè·¯çº¿å·¥åºäº§åºå½å± Mapper * * @author è¶ çº§ç®¡çå */ @Mapper public interface MesProRouteProcessOutputMapper extends BaseMapperX<MesProRouteProcessOutputDO> { default List<MesProRouteProcessOutputDO> selectListByRouteProcessId(Long routeProcessId) { return selectList(new LambdaQueryWrapperX<MesProRouteProcessOutputDO>() .eq(MesProRouteProcessOutputDO::getRouteProcessId, routeProcessId) .orderByAsc(MesProRouteProcessOutputDO::getSort)); } default List<MesProRouteProcessOutputDO> selectListByRouteProcessIds(Collection<Long> routeProcessIds) { return selectList(new LambdaQueryWrapperX<MesProRouteProcessOutputDO>() .in(MesProRouteProcessOutputDO::getRouteProcessId, routeProcessIds) .orderByAsc(MesProRouteProcessOutputDO::getSort)); } default List<MesProRouteProcessOutputDO> selectListByRouteId(Long routeId) { return selectList(new LambdaQueryWrapperX<MesProRouteProcessOutputDO>() .eq(MesProRouteProcessOutputDO::getRouteId, routeId) .orderByAsc(MesProRouteProcessOutputDO::getSort)); } default void deleteByRouteProcessId(Long routeProcessId) { delete(new LambdaQueryWrapperX<MesProRouteProcessOutputDO>() .eq(MesProRouteProcessOutputDO::getRouteProcessId, routeProcessId)); } default void deleteByRouteId(Long routeId) { delete(new LambdaQueryWrapperX<MesProRouteProcessOutputDO>() .eq(MesProRouteProcessOutputDO::getRouteId, routeId)); } } yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/task/MesProTaskMapper.java
@@ -67,6 +67,22 @@ return selectPage(reqVO, query); } /** * æå·¥ä½ç«ç»è®¡å¾ æ¥å·¥ä»»å¡æ°ï¼å£å¾ä¸ {@link #selectPendingFeedbackPage} ä¸è´ * * @return æ¯è¡å å« workstation_idãpending_count */ default List<Map<String, Object>> selectPendingFeedbackCountByWorkstation() { return selectMaps(new QueryWrapper<MesProTaskDO>() .select("workstation_id", "COUNT(*) AS pending_count") .in("status", MesProTaskStatusEnum.PREPARE.getStatus(), MesProTaskStatusEnum.PRODUCING.getStatus(), MesProTaskStatusEnum.PARTIAL_FINISHED.getStatus(), MesProTaskStatusEnum.PAUSED.getStatus()) .apply("(quantity - IFNULL(produced_quantity, 0)) > 0") .isNotNull("workstation_id") .groupBy("workstation_id")); } default List<MesProTaskDO> selectListByWorkOrderId(Long workOrderId) { return selectList(new LambdaQueryWrapperX<MesProTaskDO>() .eqIfPresent(MesProTaskDO::getWorkOrderId, workOrderId) yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkOrderProcessMapper.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.process.MesProWorkOrderProcessPageReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.apache.ibatis.annotations.Mapper; import java.util.List; @@ -29,6 +30,14 @@ .orderByAsc(MesProWorkOrderProcessDO::getSort)); } default MesProWorkOrderProcessDO selectByWorkOrderIdAndProcessId(Long workOrderId, Long processId) { return selectOne(new LambdaQueryWrapperX<MesProWorkOrderProcessDO>() .eq(MesProWorkOrderProcessDO::getWorkOrderId, workOrderId) .eq(MesProWorkOrderProcessDO::getProcessId, processId) .orderByAsc(MesProWorkOrderProcessDO::getSort) .last("LIMIT 1")); } default MesProWorkOrderProcessDO selectLastByWorkOrderId(Long workOrderId) { return selectOne(new LambdaQueryWrapperX<MesProWorkOrderProcessDO>() .eq(MesProWorkOrderProcessDO::getWorkOrderId, workOrderId) @@ -36,6 +45,17 @@ .last("LIMIT 1")); } /** * æ´æ°æ¥å·¥ä»åºï¼å 许置空ï¼updateById ä¼å¿½ç¥ null åæ®µï¼æ åç¬å¤çï¼ */ default void updateWarehouseById(Long id, Long warehouseId, Long locationId, Long areaId) { update(null, new LambdaUpdateWrapper<MesProWorkOrderProcessDO>() .set(MesProWorkOrderProcessDO::getWarehouseId, warehouseId) .set(MesProWorkOrderProcessDO::getLocationId, locationId) .set(MesProWorkOrderProcessDO::getAreaId, areaId) .eq(MesProWorkOrderProcessDO::getId, id)); } default void deleteByWorkOrderId(Long workOrderId) { delete(new LambdaQueryWrapperX<MesProWorkOrderProcessDO>() .eq(MesProWorkOrderProcessDO::getWorkOrderId, workOrderId)); yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -294,6 +294,7 @@ ErrorCode PRO_ROUTE_PROCESS_OUTPUT_NOT_BOUND = new ErrorCode(1_040_501_205, "å·¥åºäº§åºäº§åã{}ãæªç»å®å°å½åå·¥èºè·¯çº¿"); ErrorCode PRO_ROUTE_MIDDLE_OUTPUT_INVALID = new ErrorCode(1_040_501_206, "ä¸é´å·¥åºã{}ãåªè½äº§åºåæåå类产å"); ErrorCode PRO_ROUTE_LAST_OUTPUT_INVALID = new ErrorCode(1_040_501_207, "æ«éå·¥åºã{}ãåªè½äº§åºæåå类产åï¼ä¸å¿ é¡»å å«å ¨é¨ç»å®æå"); ErrorCode PRO_ROUTE_LAST_OUTPUT_ATTRIBUTION_REQUIRED = new ErrorCode(1_040_501_208, "æ«éå·¥åºã{}ãçæå产åºå¿ é¡»æäº§åå½å±é ç½®ï¼æ¯ä¸ªæåå½å±å°èªèº«ï¼ä¸è½ä½¿ç¨âéç¨â"); // ========== MES ç产管ç-å·¥èºè·¯çº¿äº§åBOMï¼1-040-501-300ï¼ ========== ErrorCode PRO_ROUTE_PRODUCT_BOM_NOT_EXISTS = new ErrorCode(1_040_501_300, "å·¥èºè·¯çº¿äº§å BOM ä¸åå¨"); ErrorCode PRO_ROUTE_PRODUCT_BOM_DUPLICATE = new ErrorCode(1_040_501_301, "å½å BOM ç©æå¨æ¤å·¥åºå·²ç»é ç½®è¿"); yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdItemServiceImpl.java
@@ -135,8 +135,10 @@ private void validateItemSaveData(MesMdItemSaveReqVO reqVO) { // æ ¡éªç©æç¼ç çå¯ä¸æ§ validateItemCodeUnique(reqVO.getId(), reqVO.getCode()); // æ ¡éªç©æåç§°çå¯ä¸æ§ validateItemNameUnique(reqVO.getId(), reqVO.getName()); // æ ¡éªç©æåç§°çå¯ä¸æ§ï¼MDM ä¸»æ°æ®ä»¥ç¼ç 为å¯ä¸æ è¯ï¼å 许ååä¸åè§æ ¼ if (reqVO.getMdmItemId() == null) { validateItemNameUnique(reqVO.getId(), reqVO.getName()); } // æ ¡éªç©æåç±»åå¨ validateItemTypeExists(reqVO.getItemTypeId()); // æ ¡éªè®¡éåä½åå¨ @@ -212,11 +214,9 @@ } private void validateItemNameUnique(Long id, String name) { MesMdItemDO item = itemMapper.selectByName(name); if (item == null) { return; } if (ObjUtil.notEqual(item.getId(), id)) { // 使ç¨å表æ¥è¯¢ï¼åéæ°æ®å¯è½åå¨ååç©æï¼é¿å selectOne ç´æ¥æå¼å¸¸ List<MesMdItemDO> items = itemMapper.selectList(MesMdItemDO::getName, name); if (items.stream().anyMatch(item -> ObjUtil.notEqual(item.getId(), id))) { throw exception(MD_ITEM_NAME_DUPLICATE); } } yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java
@@ -114,8 +114,9 @@ // 1. æ ¡éª MesProTaskDO task = validateFeedbackData(createReqVO); // 2. æå ¥ï¼èªå¨å¡«å itemIdï¼ // 2. æå ¥ï¼ä¸»é®ç±æ°æ®åºèªå¢ï¼å¿½ç¥è¯·æ±ä¸å¯è½æ®çç idï¼èªå¨å¡«å itemIdï¼ MesProFeedbackDO feedback = BeanUtils.toBean(createReqVO, MesProFeedbackDO.class) .setId(null) .setStatus(MesProFeedbackStatusEnum.PREPARE.getStatus()) .setItemId(task.getItemId()); feedbackMapper.insert(feedback); yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/mps/MesProMpsServiceImpl.java
@@ -617,8 +617,9 @@ } Long routeId = routeProduct.getRouteId(); // 2. æ ¡éªå·¥èºè·¯çº¿æ¯å¦æå·¥åº List<MesProRouteProcessDO> routeProcesses = routeProcessService.getRouteProcessListByRouteId(routeId); // 2. æ ¡éªå·¥èºè·¯çº¿æ¯å¦æå·¥åºï¼æäº§åè¿æ»¤å ¶å®äº§åç产åºç©æï¼ List<MesProRouteProcessDO> routeProcesses = routeProcessService .getRouteProcessListByProductId(mps.getProductId()); if (CollUtil.isEmpty(routeProcesses)) { throw exception(PRO_ROUTE_ENABLE_NO_PROCESS); } @@ -658,6 +659,10 @@ .map(Long::valueOf).collect(Collectors.toList())); } process.setColorCode(rp.getColorCode()); // å¸¦å ¥å·¥èºè·¯çº¿äº§åé ç½®çæ¥å·¥ä»åº process.setWarehouseId(routeProduct.getWarehouseId()); process.setLocationId(routeProduct.getLocationId()); process.setAreaId(routeProduct.getAreaId()); // å¸¦å ¥å·¥åºææBOM List<MesProRouteProductBomDO> routeBomItems = bomMap != null ? bomMap.get(rp.getProcessId()) : null; if (CollUtil.isNotEmpty(routeBomItems)) { yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessService.java
@@ -67,6 +67,27 @@ List<MesProRouteProcessDO> getRouteProcessListByProductId(Long productId); /** * è·å¾å·¥åºå¨æå®äº§åä¸ç产åºç©æç¼å· * * æäº§åºå½å±è¿æ»¤ï¼å½å±ä¸ºç©ºï¼å¤äº§åéç¨ï¼æå½å±çäºæå®äº§åç产åºã * æ å½å±æ°æ®æ¶è¿å {@code null}ï¼è°ç¨æ¹éèªè¡åéï¼æå½å±æ°æ®ä½è¯¥äº§åæ äº§åºæ¶è¿å空å表ã * * @param routeProcessId å·¥èºè·¯çº¿å·¥åºç¼å· * @param productId 产åç¼å· * @return 产åºç©æç¼å·åè¡¨ï¼ææåºï¼ï¼æ å½å±æ°æ®æ¶ä¸º null */ List<Long> getOutputItemIdsByRouteProcessId(Long routeProcessId, Long productId); /** * æå·¥èºè·¯çº¿å·¥åºç¼å·æ¹éè·å¾äº§åºå½å±é ç½® * * @param routeProcessIds å·¥èºè·¯çº¿å·¥åºç¼å·æ°ç» * @return 产åºå½å±å表 */ List<cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessOutputDO> getRouteProcessOutputListByRouteProcessIds(Collection<Long> routeProcessIds); /** * æäº§åè·å¾å·¥åºææBOM Map * * key = å·¥åºIDï¼value = 该工åºä¸çææBOMæç»å表 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessServiceImpl.java
@@ -1,12 +1,15 @@ package cn.iocoder.yudao.module.mes.service.pro.route; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.NumberUtil; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProRouteProcessSaveReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessOutputDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO; import cn.iocoder.yudao.module.mes.dal.mysql.pro.route.MesProRouteProcessMapper; import cn.iocoder.yudao.module.mes.dal.mysql.pro.route.MesProRouteProcessOutputMapper; import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; @@ -14,11 +17,16 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -35,6 +43,8 @@ @Resource private MesProRouteProcessMapper routeProcessMapper; @Resource private MesProRouteProcessOutputMapper routeProcessOutputMapper; @Resource @Lazy @@ -63,13 +73,10 @@ // 2. æå ¥ MesProRouteProcessDO routeProcess = BeanUtils.toBean(createReqVO, MesProRouteProcessDO.class); if (CollUtil.isNotEmpty(createReqVO.getOutputItemIds())) { routeProcess.setOutputItemId( createReqVO.getOutputItemIds().stream() .map(String::valueOf) .collect(Collectors.joining(","))); } routeProcess.setOutputItemId(resolveOutputItemId(createReqVO)); routeProcessMapper.insert(routeProcess); // 2.1 ä¿å产åºå½å±é ç½® saveRouteProcessOutputs(routeProcess, createReqVO); // 3. æ´æ°å·¥åºé¾ rebuildProcessChain(createReqVO.getRouteId()); @@ -93,11 +100,10 @@ // 2. æ´æ° MesProRouteProcessDO updateObj = BeanUtils.toBean(updateReqVO, MesProRouteProcessDO.class); // 空æ°ç»ä¹è¦æ¾å¼åå ¥ç©ºä¸²ï¼å¦å MyBatis-Plus é»è®¤å¿½ç¥ nullï¼ç¨æ·æ¸ 空产åºååæ§é ç½®ä»ä¼æ®çã updateObj.setOutputItemId(CollUtil.isEmpty(updateReqVO.getOutputItemIds()) ? "" : updateReqVO.getOutputItemIds().stream() .map(String::valueOf) .collect(Collectors.joining(","))); updateObj.setOutputItemId(resolveOutputItemId(updateReqVO)); routeProcessMapper.updateById(updateObj); // 2.1 è¦ç产åºå½å±é ç½® saveRouteProcessOutputs(updateObj, updateReqVO); // 3. é建工åºé¾ rebuildProcessChain(updateReqVO.getRouteId()); @@ -111,8 +117,9 @@ // 1.2 å·²å¯ç¨çå·¥èºè·¯çº¿ï¼ä¸å 许æä½ routeService.validateRouteNotEnable(routeProcess.getRouteId()); // 2. å é¤ // 2. å é¤ï¼å«äº§åºå½å±é ç½®ï¼ routeProcessMapper.deleteById(id); routeProcessOutputMapper.deleteByRouteProcessId(id); // 3. é建工åºé¾ rebuildProcessChain(routeProcess.getRouteId()); @@ -211,7 +218,165 @@ return Collections.emptyList(); } // 2. è¿å该工èºè·¯çº¿çå·¥åºå表 return routeProcessMapper.selectListByRouteId(routeProduct.getRouteId()); List<MesProRouteProcessDO> list = routeProcessMapper.selectListByRouteId(routeProduct.getRouteId()); // 3. æäº§åºå½å±è¿æ»¤ï¼éç¨äº§åº + å½å产åä¸å±äº§åº Map<Long, List<MesProRouteProcessOutputDO>> outputMap = routeProcessOutputMapper .selectListByRouteId(routeProduct.getRouteId()).stream() .collect(Collectors.groupingBy(MesProRouteProcessOutputDO::getRouteProcessId)); for (MesProRouteProcessDO process : list) { List<MesProRouteProcessOutputDO> outputs = outputMap.get(process.getId()); if (CollUtil.isEmpty(outputs)) { // åéæ°æ®æªæåå½å±æ¶ï¼éåæ§çâåé¤åè·¯çº¿å ¶å®äº§åâé»è¾ filterOtherProductOutputs(Collections.singletonList(process), routeProduct); continue; } String filtered = outputs.stream() .filter(output -> output.getProductId() == null || output.getProductId().equals(productId)) .map(MesProRouteProcessOutputDO::getItemId) .filter(Objects::nonNull) .distinct() .map(String::valueOf) .collect(Collectors.joining(",")); process.setOutputItemId(filtered.isEmpty() ? null : filtered); } return list; } @Override public List<Long> getOutputItemIdsByRouteProcessId(Long routeProcessId, Long productId) { List<MesProRouteProcessOutputDO> outputs = routeProcessOutputMapper.selectListByRouteProcessId(routeProcessId); if (CollUtil.isEmpty(outputs)) { // æªé 置产åºå½å±ï¼åéæ°æ®ï¼è¿å nullï¼ä¾¿äºè°ç¨æ¹åéèåäº§åº return null; } return outputs.stream() .filter(output -> output.getProductId() == null || output.getProductId().equals(productId)) .map(MesProRouteProcessOutputDO::getItemId) .filter(Objects::nonNull) .distinct() .collect(Collectors.toList()); } @Override public List<MesProRouteProcessOutputDO> getRouteProcessOutputListByRouteProcessIds(Collection<Long> routeProcessIds) { if (CollUtil.isEmpty(routeProcessIds)) { return Collections.emptyList(); } return routeProcessOutputMapper.selectListByRouteProcessIds(routeProcessIds); } /** * 计ç®å·¥åºäº§åºç©æçèå串ï¼éå·åéï¼å»éï¼ * * ä¼å 使ç¨äº§åºå½å±é ç½®ï¼outputsï¼ï¼æªä¼ æ¶å ¼å®¹ outputItemIdsã */ private String resolveOutputItemId(MesProRouteProcessSaveReqVO reqVO) { List<Long> itemIds; if (CollUtil.isNotEmpty(reqVO.getOutputs())) { itemIds = reqVO.getOutputs().stream() .filter(Objects::nonNull) .sorted(Comparator.comparing(MesProRouteProcessSaveReqVO.OutputItem::getSort, Comparator.nullsLast(Comparator.naturalOrder()))) .map(MesProRouteProcessSaveReqVO.OutputItem::getItemId) .filter(Objects::nonNull) .distinct() .collect(Collectors.toList()); } else if (CollUtil.isNotEmpty(reqVO.getOutputItemIds())) { itemIds = reqVO.getOutputItemIds().stream() .filter(Objects::nonNull) .distinct() .collect(Collectors.toList()); } else { itemIds = Collections.emptyList(); } return itemIds.stream().map(String::valueOf).collect(Collectors.joining(",")); } /** * è¦çä¿åå·¥åºäº§åºå½å±é ç½® */ private void saveRouteProcessOutputs(MesProRouteProcessDO routeProcess, MesProRouteProcessSaveReqVO reqVO) { routeProcessOutputMapper.deleteByRouteProcessId(routeProcess.getId()); if (CollUtil.isEmpty(reqVO.getOutputs()) && CollUtil.isEmpty(reqVO.getOutputItemIds())) { return; } Set<Long> routeProductIds = routeProductService.getRouteProductListByRouteId(routeProcess.getRouteId()) .stream().map(MesProRouteProductDO::getItemId).filter(Objects::nonNull).collect(Collectors.toSet()); List<MesProRouteProcessOutputDO> outputs = new ArrayList<>(); Set<String> keys = new HashSet<>(); int index = 0; if (CollUtil.isNotEmpty(reqVO.getOutputs())) { for (MesProRouteProcessSaveReqVO.OutputItem output : reqVO.getOutputs()) { if (output == null || output.getItemId() == null) { continue; } index++; if (!keys.add(output.getProductId() + "_" + output.getItemId())) { continue; } outputs.add(buildOutput(routeProcess, output.getProductId(), output.getItemId(), output.getSort() != null ? output.getSort() : index, output.getRemark())); } } else { // å ¼å®¹åªä¼ outputItemIds çè°ç¨æ¹ï¼äº§åºæ¯è·¯çº¿äº§åæ¶å½å±å°èªèº«ï¼å¦åè§ä¸ºéç¨ for (Long itemId : reqVO.getOutputItemIds()) { if (itemId == null) { continue; } index++; Long productId = routeProductIds.contains(itemId) ? itemId : null; if (!keys.add(productId + "_" + itemId)) { continue; } outputs.add(buildOutput(routeProcess, productId, itemId, index, null)); } } if (CollUtil.isNotEmpty(outputs)) { routeProcessOutputMapper.insertBatch(outputs); } } private MesProRouteProcessOutputDO buildOutput(MesProRouteProcessDO routeProcess, Long productId, Long itemId, Integer sort, String remark) { return MesProRouteProcessOutputDO.builder() .routeProcessId(routeProcess.getId()) .routeId(routeProcess.getRouteId()) .processId(routeProcess.getProcessId()) .productId(productId) .itemId(itemId) .sort(sort) .remark(remark) .build(); } /** * è¿æ»¤äº§åºç©æï¼åé¤åä¸å·¥èºè·¯çº¿ä¸å ¶å®äº§åç产åºï¼åªä¿çå½å产åç产åºååæåäº§åº */ private void filterOtherProductOutputs(List<MesProRouteProcessDO> list, MesProRouteProductDO routeProduct) { if (CollUtil.isEmpty(list) || routeProduct.getItemId() == null) { return; } Set<Long> otherProductIds = routeProductService.getRouteProductListByRouteId(routeProduct.getRouteId()) .stream() .map(MesProRouteProductDO::getItemId) .filter(itemId -> itemId != null && !itemId.equals(routeProduct.getItemId())) .collect(Collectors.toSet()); if (otherProductIds.isEmpty()) { return; } for (MesProRouteProcessDO process : list) { String outputItemId = process.getOutputItemId(); if (outputItemId == null || outputItemId.isBlank()) { continue; } String filtered = Arrays.stream(outputItemId.split(",")) .map(String::trim) .filter(s -> !s.isEmpty()) .filter(s -> !otherProductIds.contains(NumberUtil.parseLong(s, -1L))) .collect(Collectors.joining(",")); process.setOutputItemId(filtered.isEmpty() ? null : filtered); } } @Override @@ -236,6 +401,7 @@ @Override public void deleteRouteProcessByRouteId(Long routeId) { routeProcessMapper.deleteByRouteId(routeId); routeProcessOutputMapper.deleteByRouteId(routeId); } } yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteServiceImpl.java
@@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteSaveReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessOutputDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO; import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; @@ -35,6 +36,7 @@ import java.util.Set; import java.util.Comparator; import java.util.Arrays; import java.util.Objects; import java.util.stream.Collectors; import java.util.concurrent.atomic.AtomicInteger; @@ -209,6 +211,20 @@ throw exception(PRO_ROUTE_MIDDLE_OUTPUT_INVALID, process.getProcessId()); } } // 4. æ«éå·¥åºæå产åºå¿ é¡»æäº§åå½å±ï¼é¿å âéç¨â产åºä¸²å°å ¶å®äº§åçå·¥å List<MesProRouteProcessOutputDO> lastOutputs = routeProcessService .getRouteProcessOutputListByRouteProcessIds(Collections.singletonList(lastProcess.getId())); if (CollUtil.isNotEmpty(lastOutputs)) { boolean attributionInvalid = lastOutputs.stream().anyMatch(output -> output.getProductId() == null || !output.getProductId().equals(output.getItemId())); Set<Long> attributedProducts = lastOutputs.stream() .map(MesProRouteProcessOutputDO::getProductId) .filter(Objects::nonNull) .collect(Collectors.toSet()); if (attributionInvalid || !attributedProducts.containsAll(finishedItemIds)) { throw exception(PRO_ROUTE_LAST_OUTPUT_ATTRIBUTION_REQUIRED, lastProcess.getProcessId()); } } } private Set<Long> parseOutputItemIds(String value) { yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskService.java
@@ -75,6 +75,13 @@ PageResult<MesProTaskDO> getPendingFeedbackPage(MesProTaskPageReqVO pageReqVO); /** * æå·¥ä½ç«ç»è®¡å¾ æ¥å·¥ä»»å¡æ° * * @return å·¥ä½ç«ç¼å· -> å¾ æ¥å·¥ä»»å¡æ° */ Map<Long, Long> getPendingFeedbackCountByWorkstation(); /** * æ ¹æ®å·¥åç¼å·åè¡¨ï¼æ¹éè·å¾ç产任å¡å表 * * @param workOrderIds å·¥åç¼å·å表 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java
@@ -45,7 +45,9 @@ import java.time.LocalDateTime; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -127,11 +129,7 @@ if (itemId == null) { MesProRouteProcessDO routeProcess = routeProcessService .getRouteProcessByRouteIdAndProcessId(createReqVO.getRouteId(), createReqVO.getProcessId()); if (routeProcess != null && routeProcess.getOutputItemId() != null) { itemId = Long.valueOf(routeProcess.getOutputItemId().split(",")[0].trim()); } else { itemId = workOrder.getProductId(); } itemId = resolveTaskItemId(routeProcess, workOrder.getProductId()); } MesMdItemDO item = itemService.validateItemExists(itemId); @@ -256,6 +254,30 @@ return routeProcesses; } /** * ä»»å¡äº§åºç©æï¼ä¼å åå·¥åºå¨å½å产åä¸ç产åºï¼æå½å±è¿æ»¤ï¼ï¼æ²¡æäº§åºæ¶åå·¥åæå */ private Long resolveTaskItemId(MesProRouteProcessDO routeProcess, Long productId) { if (routeProcess == null) { return productId; } if (routeProcess.getId() != null) { // å·¥èºè·¯çº¿æ¨¡æ¿ï¼æäº§åºå½å±è¿æ»¤åºå½å产åçäº§åº List<Long> outputIds = routeProcessService .getOutputItemIdsByRouteProcessId(routeProcess.getId(), productId); if (outputIds != null) { // æå½å±é ç½®ï¼è¯¥äº§åæ äº§åºæ¶åå·¥åæåï¼null 表示åéæ å½å±æ°æ®ï¼ç»§ç»èµ°èåå åº return CollUtil.isNotEmpty(outputIds) ? outputIds.get(0) : productId; } } // å·¥åå·¥åºå¿«ç § / åéæ å½å±æ°æ®ï¼äº§åºå·²æäº§åè¿æ»¤ï¼å第ä¸ä¸ª String outputItemId = routeProcess.getOutputItemId(); if (outputItemId != null && !outputItemId.isBlank()) { return Long.valueOf(outputItemId.split(",")[0].trim()); } return productId; } @Override public void deleteTask(Long id) { // 1. æ ¡éªåå¨ä¸éç»æ @@ -286,6 +308,20 @@ @Override public PageResult<MesProTaskDO> getPendingFeedbackPage(MesProTaskPageReqVO pageReqVO) { return taskMapper.selectPendingFeedbackPage(pageReqVO); } @Override public Map<Long, Long> getPendingFeedbackCountByWorkstation() { List<Map<String, Object>> rows = taskMapper.selectPendingFeedbackCountByWorkstation(); Map<Long, Long> result = new HashMap<>(rows.size()); for (Map<String, Object> row : rows) { Object workstationId = row.get("workstation_id"); Object count = row.get("pending_count"); if (workstationId instanceof Number && count instanceof Number) { result.put(((Number) workstationId).longValue(), ((Number) count).longValue()); } } return result; } @Override @@ -443,7 +479,7 @@ List<Long> taskIds = new java.util.ArrayList<>(); for (MesProRouteProcessDO rp : routeProcesses) { cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO proc = procMap.get(rp.getProcessId()); Long itemId = rp.getOutputItemId() != null ? Long.valueOf(rp.getOutputItemId().split(",")[0].trim()) : workOrder.getProductId(); Long itemId = resolveTaskItemId(rp, workOrder.getProductId()); cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO item = itemService.validateItemExists(itemId); MesProTaskDO task = new MesProTaskDO() .setWorkOrderId(workOrder.getId()) yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessService.java
@@ -62,6 +62,15 @@ List<MesProWorkOrderProcessDO> getWorkOrderProcessListByWorkOrderId(Long workOrderId); /** * æ ¹æ®å·¥åç¼å· + å·¥åºç¼å·è·å¾å·¥åå·¥åº * * @param workOrderId å·¥åç¼å· * @param processId å·¥åºç¼å· * @return å·¥åå·¥åº */ MesProWorkOrderProcessDO getWorkOrderProcessByWorkOrderIdAndProcessId(Long workOrderId, Long processId); /** * æ ¹æ®å·¥åç¼å·è·å¾æåä¸éå·¥åº * * @param workOrderId å·¥åç¼å· yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessServiceImpl.java
@@ -7,12 +7,15 @@ import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.process.MesProWorkOrderProcessSaveReqVO; import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.process.MesProWorkOrderProcessSaveReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO; import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderBomMapper; import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderProcessMapper; import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService; import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService; import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService; import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; @@ -48,6 +51,10 @@ private MesProProcessService processService; @Resource private MesProRouteProcessService routeProcessService; @Resource private MesProRouteProductService routeProductService; @Resource private MesWmWarehouseAreaService warehouseAreaService; @Resource @Lazy private MesProWorkOrderBomService workOrderBomService; @@ -89,6 +96,9 @@ .collect(Collectors.joining(","))); } workOrderProcessMapper.updateById(updateObj); // æ¥å·¥ä»åºå 许置空ï¼åç¬æ´æ° workOrderProcessMapper.updateWarehouseById(updateReqVO.getId(), updateReqVO.getWarehouseId(), updateReqVO.getLocationId(), updateReqVO.getAreaId()); // 3. é建工åºé¾ rebuildProcessChain(updateReqVO.getWorkOrderId()); @@ -122,6 +132,11 @@ } @Override public MesProWorkOrderProcessDO getWorkOrderProcessByWorkOrderIdAndProcessId(Long workOrderId, Long processId) { return workOrderProcessMapper.selectByWorkOrderIdAndProcessId(workOrderId, processId); } @Override public MesProWorkOrderProcessDO getLastWorkOrderProcessByWorkOrderId(Long workOrderId) { return workOrderProcessMapper.selectLastByWorkOrderId(workOrderId); } @@ -146,6 +161,8 @@ if (CollUtil.isEmpty(routeProcessList)) { return Collections.emptyList(); } // å·¥èºè·¯çº¿äº§åä¸é ç½®çæ¥å·¥ä»åºä½ä¸ºå·¥åºé»è®¤å¼ï¼ä¸åæ¶å¯åä¿®æ¹ MesProRouteProductDO routeProduct = routeProductService.getRouteProductByItemId(productId); // 转æ¢ä¸ºå·¥åå·¥åº DOï¼ä¸è®¾ç½® workOrderIdï¼å端ä¿åæ¶åè®¾ç½®ï¼ List<MesProWorkOrderProcessDO> processList = new ArrayList<>(routeProcessList.size()); for (MesProRouteProcessDO routeProcess : routeProcessList) { @@ -161,6 +178,9 @@ .checkFlag(routeProcess.getCheckFlag()) .backflushFlag(routeProcess.getBackflushFlag()) .outputItemId(routeProcess.getOutputItemId()) .warehouseId(routeProduct != null ? routeProduct.getWarehouseId() : null) .locationId(routeProduct != null ? routeProduct.getLocationId() : null) .areaId(routeProduct != null ? routeProduct.getAreaId() : null) .remark(routeProcess.getRemark()) .build()); } @@ -212,6 +232,9 @@ updateObj.setOutputItemId(item.getOutputItemIds().stream().map(String::valueOf).collect(Collectors.joining(","))); } workOrderProcessMapper.updateById(updateObj); // æ¥å·¥ä»åºå 许置空ï¼åç¬æ´æ° workOrderProcessMapper.updateWarehouseById(item.getId(), item.getWarehouseId(), item.getLocationId(), item.getAreaId()); } } @@ -277,7 +300,12 @@ throw exception(PRO_WORK_ORDER_PROCESS_DUPLICATE); } } // 4. æ ¡éªå ³é®å·¥åºå¯ä¸ // 4. æ ¡éªæ¥å·¥ä»åºå±çº§ï¼ä»åº/åºåº/åºä½åæ¶å¡«åæ¶æ ¡éªå½å±å ³ç³»ï¼ if (reqVO.getWarehouseId() != null && reqVO.getLocationId() != null && reqVO.getAreaId() != null) { warehouseAreaService.validateWarehouseAreaExists(reqVO.getWarehouseId(), reqVO.getLocationId(), reqVO.getAreaId()); } // 5. æ ¡éªå ³é®å·¥åºå¯ä¸ if (Boolean.TRUE.equals(reqVO.getKeyFlag())) { for (MesProWorkOrderProcessDO process : list) { if (reqVO.getId() != null && reqVO.getId().equals(process.getId())) { yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceServiceImpl.java
@@ -182,8 +182,9 @@ Long batchId = batch != null ? batch.getId() : null; String batchCode = batch != null ? batch.getCode() : null; // 1.5 ä¼å 使ç¨å·¥èºè·¯çº¿äº§åç»å®çæ¥å·¥ä»åºï¼å岿°æ®æªé ç½®æ¶åéèæçº¿è¾¹åºã WarehouseTarget outputTarget = resolveOutputTarget(feedback.getItemId()); // 1.5 ä¼å 使ç¨å·¥åå·¥åºé ç½®çæ¥å·¥ä»åºï¼ä¸åæ¶å¯ç¼è¾ï¼ï¼å ¶æ¬¡å·¥èºè·¯çº¿äº§åï¼æååéèæçº¿è¾¹åº WarehouseTarget outputTarget = resolveOutputTarget(feedback.getWorkOrderId(), feedback.getProcessId(), feedback.getItemId()); // 3. æ ¹æ®æ¯å¦éè¦æ£éªåæ¯å¤ç if (checkFlag) { @@ -275,7 +276,8 @@ } // 2. éè¿ sourceLineId ç´æ¥å®ä½å¾ æ£éªè¡ MesWmProductProduceLineDO pendingLine = productProduceLineService.validateProductProduceLineExists(sourceLineId); WarehouseTarget outputTarget = resolveOutputTarget(pendingLine.getItemId()); WarehouseTarget outputTarget = resolveOutputTarget(produce.getWorkOrderId(), produce.getProcessId(), pendingLine.getItemId()); // 3A. æ åµä¸ï¼åå¨ä¸åæ ¼åæ°éï¼éè¦æåè¡ if (unqualifiedQty != null && unqualifiedQty.compareTo(BigDecimal.ZERO) > 0) { @@ -322,7 +324,20 @@ finishProductProduce(produce.getId()); } private WarehouseTarget resolveOutputTarget(Long itemId) { private WarehouseTarget resolveOutputTarget(Long workOrderId, Long processId, Long itemId) { // 1. å·¥åå·¥åºé ç½®ï¼ä¸åæ¶å¯ç¼è¾ï¼ if (workOrderId != null && processId != null) { MesProWorkOrderProcessDO workOrderProcess = workOrderProcessService .getWorkOrderProcessByWorkOrderIdAndProcessId(workOrderId, processId); if (workOrderProcess != null && workOrderProcess.getWarehouseId() != null && workOrderProcess.getLocationId() != null && workOrderProcess.getAreaId() != null) { areaService.validateWarehouseAreaExists(workOrderProcess.getWarehouseId(), workOrderProcess.getLocationId(), workOrderProcess.getAreaId()); return new WarehouseTarget(workOrderProcess.getWarehouseId(), workOrderProcess.getLocationId(), workOrderProcess.getAreaId()); } } // 2. å·¥èºè·¯çº¿äº§åé ç½® MesProRouteProductDO routeProduct = itemId != null ? routeProductService.getRouteProductByItemId(itemId) : null; if (routeProduct != null && routeProduct.getWarehouseId() != null