src/main/java/com/ruoyi/production/bean/dto/ProductionOperationTaskDto.java
@@ -44,4 +44,7 @@ @Schema(description = "ç»ææ¥æ") @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate endDate; @Schema(description = "æ¯å¦ç产") private Integer isProduction; } src/main/java/com/ruoyi/production/controller/ProductionOrderRoutingController.java
@@ -39,7 +39,7 @@ @PostMapping("/updateRouteItem") @Operation(summary = "ä¿®æ¹ç产订åçå·¥èºè·¯çº¿è¯¦æ ") public R updateRouteItem(@RequestBody ProductionOrderRoutingOperation productionOrderRoutingOperation) { return R.ok(productionOrderRoutingOperationService.updateById(productionOrderRoutingOperation)); return R.ok(productionOrderRoutingOperationService.updateRouteItem(productionOrderRoutingOperation)); } @DeleteMapping("/deleteRouteItem/{id}") src/main/java/com/ruoyi/production/service/ProductionOrderRoutingOperationService.java
@@ -8,6 +8,8 @@ R addRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation); R updateRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation); R deleteRouteItem(Long id); int sortRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation); src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
@@ -23,6 +23,7 @@ import com.ruoyi.production.pojo.ProductionOrderRoutingOperationParam; import com.ruoyi.production.pojo.ProductionProductMain; import com.ruoyi.production.service.ProductionBomStructureService; import com.ruoyi.production.util.TaskPlanQuantityUtil; import com.ruoyi.technology.mapper.TechnologyOperationMapper; import com.ruoyi.technology.mapper.TechnologyOperationParamMapper; import com.ruoyi.technology.mapper.TechnologyParamMapper; @@ -262,7 +263,7 @@ .filter(item -> item != null && item.getId() != null) .collect(Collectors.toMap(ProductionOrderRoutingOperation::getId, item -> item, (left, right) -> left)); // Keep task plan quantities aligned with the same order BOM snapshot demand used during snapshot creation. Map<String, BigDecimal> demandedQuantityMap = buildOperationDemandedQuantityMap(structureList, rootProductModelId); Map<String, BigDecimal> demandedQuantityMap = TaskPlanQuantityUtil.buildOperationDemandedQuantityMap(structureList, rootProductModelId); for (ProductionOperationTask task : taskList) { if (task == null || task.getId() == null || task.getProductionOrderRoutingOperationId() == null) { continue; src/main/java/com/ruoyi/production/service/impl/ProductionOrderRoutingOperationServiceImpl.java
@@ -4,21 +4,15 @@ import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.framework.web.domain.R; import com.ruoyi.production.mapper.ProductionOperationTaskMapper; import com.ruoyi.production.mapper.ProductionOrderRoutingOperationMapper; import com.ruoyi.production.mapper.ProductionOrderRoutingOperationParamMapper; import com.ruoyi.production.mapper.ProductionProductMainMapper; import com.ruoyi.production.pojo.ProductionOperationTask; import com.ruoyi.production.pojo.ProductionOrderRoutingOperation; import com.ruoyi.production.pojo.ProductionOrderRoutingOperationParam; import com.ruoyi.production.pojo.ProductionProductMain; import com.ruoyi.production.mapper.*; import com.ruoyi.production.util.TaskPlanQuantityUtil; import com.ruoyi.technology.mapper.*; import com.ruoyi.production.pojo.*; import com.ruoyi.production.service.ProductionOrderRoutingOperationService; import com.ruoyi.production.service.ProductionProductMainService; import com.ruoyi.technology.mapper.TechnologyOperationParamMapper; import com.ruoyi.technology.mapper.TechnologyParamMapper; import com.ruoyi.technology.pojo.TechnologyOperationParam; import com.ruoyi.technology.pojo.TechnologyParam; import com.ruoyi.technology.pojo.*; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -26,8 +20,7 @@ import java.math.BigDecimal; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import java.util.*; @Service @Transactional(rollbackFor = Exception.class) @@ -42,6 +35,11 @@ private final TechnologyOperationParamMapper technologyOperationParamMapper; private final TechnologyParamMapper technologyParamMapper; private final ProductionOrderRoutingOperationParamMapper productionOrderRoutingOperationParamMapper; private final ProductionOrderMapper productionOrderMapper; private final ProductionOrderRoutingMapper productionOrderRoutingMapper; private final ProductionOrderBomMapper productionOrderBomMapper; private final ProductionBomStructureMapper productionBomStructureMapper; private final TechnologyRoutingOperationMapper technologyRoutingOperationMapper; @Override public R addRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation) { @@ -102,6 +100,109 @@ productionOperationTaskMapper.insert(productionOperationTask); } return R.ok(); } @Override @Transactional(rollbackFor = Exception.class) public R updateRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation) { Long operationId = productionOrderRoutingOperation.getId(); // æ´æ°å·¥èºè·¯çº¿å·¥åº productionOrderRoutingOperationMapper.updateById(productionOrderRoutingOperation); // éæ°æ¥è¯¢å®æ´è®°å½ï¼å端å¯è½æ²¡æä¼ éææåæ®µï¼å¦ productionOrderIdï¼ ProductionOrderRoutingOperation updatedOperation = productionOrderRoutingOperationMapper.selectById(operationId); if (updatedOperation == null) { throw new ServiceException("å·¥èºè·¯çº¿å·¥åºä¸åå¨"); } // æ¥è¯¢æ¯å¦åå¨å·¥å ProductionOperationTask productionOperationTask = productionOperationTaskMapper.selectOne( new LambdaQueryWrapper<ProductionOperationTask>() .eq(ProductionOperationTask::getProductionOrderRoutingOperationId, operationId) .last("limit 1")); // æ ¹æ®æ¯å¦éè¦ç产è¿è¡å¤ç Boolean isProduction = updatedOperation.getIsProduction(); if (Boolean.TRUE.equals(isProduction)) { // éè¦çäº§ï¼æ£æ¥å·¥åæ¯å¦åå¨ï¼ä¸åå¨åçæ if (productionOperationTask == null) { ProductionOperationTask task = new ProductionOperationTask(); task.setProductionOrderRoutingOperationId(updatedOperation.getId()); task.setProductionOrderId(updatedOperation.getProductionOrderId()); // è·åç产订å ProductionOrder productionOrder = productionOrderMapper.selectById(updatedOperation.getProductionOrderId()); if (productionOrder == null) { throw new ServiceException("ç产订åä¸åå¨"); } // è·å订åBOM ProductionOrderBom orderBom = productionOrderBomMapper.selectOne( Wrappers.<ProductionOrderBom>lambdaQuery() .eq(ProductionOrderBom::getProductionOrderId, productionOrder.getId())); // ç¡®å®æ ¹äº§åè§æ ¼ID Long rootProductModelId = orderBom != null && orderBom.getProductModelId() != null ? orderBom.getProductModelId() : productionOrder.getProductModelId(); // è·åBOMç»æå表 List<ProductionBomStructure> orderBomStructureList = orderBom == null || orderBom.getId() == null ? Collections.emptyList() : productionBomStructureMapper.selectList( Wrappers.<ProductionBomStructure>lambdaQuery() .eq(ProductionBomStructure::getProductionOrderBomId, orderBom.getId()) .orderByAsc(ProductionBomStructure::getId)); // æå»ºå·¥åºéæ±éæ å° Map<String, BigDecimal> operationDemandedQuantityMap = TaskPlanQuantityUtil.buildOperationDemandedQuantityMap(orderBomStructureList, rootProductModelId); // è·åå·¥èºè·¯çº¿å·¥åºï¼ç¨äºè®¡ç®è®¡åæ°éï¼ TechnologyRoutingOperation sourceOperation = technologyRoutingOperationMapper.selectById( updatedOperation.getTechnologyRoutingOperationId()); // å°åæ¥çç§ææ¹æ³æ¿æ¢ä¸ºè°ç¨å·¥å ·ç±» BigDecimal planQuantity = TaskPlanQuantityUtil.resolveTaskPlanQuantity( sourceOperation, operationDemandedQuantityMap, productionOrder, rootProductModelId); task.setPlanQuantity(planQuantity); task.setCompleteQuantity(BigDecimal.ZERO); task.setWorkOrderNo(generateNextTaskNo()); task.setStatus(2); productionOperationTaskMapper.insert(task); } } else { // ä¸éè¦çäº§ï¼æ£æ¥å·¥åæ¯å¦åå¨ if (productionOperationTask != null) { validateTaskCanRemove(productionOperationTask); // æ²¡ææ¥å·¥ï¼åå é¤å·¥å productionOperationTaskMapper.deleteById(productionOperationTask.getId()); } } return R.ok(); } private void validateTaskCanRemove(ProductionOperationTask task) { if (task == null || task.getId() == null) { return; } if (defaultDecimal(task.getCompleteQuantity()).compareTo(BigDecimal.ZERO) > 0) { throw new ServiceException("å·¥åºå·²äº§çæ¥å·¥è®°å½ï¼æ æ³æ ¹æ® BOM åæ´å é¤å¯¹åºå·¥åºå¿«ç §"); } long reportCount = productionProductMainMapper.selectCount( Wrappers.<ProductionProductMain>lambdaQuery() .eq(ProductionProductMain::getProductionOperationTaskId, task.getId())); if (reportCount > 0) { throw new ServiceException("å·¥åºå·²äº§çæ¥å·¥è®°å½ï¼æ æ³æ ¹æ® BOM åæ´å é¤å¯¹åºå·¥å"); } } private BigDecimal defaultDecimal(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; } @Override @@ -182,4 +283,24 @@ } return 0; } private String generateNextTaskNo() { // çæä¸ä¸ä¸ªç产工åå· String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); String prefix = "GD" + datePrefix; ProductionOperationTask lastTask = productionOperationTaskMapper.selectOne( Wrappers.<ProductionOperationTask>lambdaQuery() .likeRight(ProductionOperationTask::getWorkOrderNo, prefix) .orderByDesc(ProductionOperationTask::getWorkOrderNo) .last("limit 1")); int sequence = 1; if (lastTask != null && lastTask.getWorkOrderNo() != null && lastTask.getWorkOrderNo().startsWith(prefix)) { try { sequence = Integer.parseInt(lastTask.getWorkOrderNo().substring(prefix.length())) + 1; } catch (NumberFormatException ignored) { sequence = 1; } } return prefix + String.format("%03d", sequence); } } src/main/java/com/ruoyi/production/util/TaskPlanQuantityUtil.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,137 @@ package com.ruoyi.production.util; import com.ruoyi.production.pojo.ProductionBomStructure; import com.ruoyi.production.pojo.ProductionOrder; import com.ruoyi.production.pojo.ProductionOrderRoutingOperation; import com.ruoyi.technology.pojo.TechnologyRoutingOperation; import lombok.experimental.UtilityClass; import java.math.BigDecimal; import java.util.*; import java.util.stream.Collectors; /** * å·¥åè®¡åæ°é计ç®å·¥å ·ç±» */ @UtilityClass public class TaskPlanQuantityUtil { /** * 计ç®å·¥åè®¡åæ°éï¼ä½¿ç¨ TechnologyRoutingOperationï¼ */ public BigDecimal resolveTaskPlanQuantity(TechnologyRoutingOperation sourceOperation, Map<String, BigDecimal> operationDemandedQuantityMap, ProductionOrder productionOrder, Long rootProductModelId) { if (sourceOperation == null || operationDemandedQuantityMap == null || operationDemandedQuantityMap.isEmpty()) { return defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity()); } Long outputProductModelId = sourceOperation.getProductModelId() != null ? sourceOperation.getProductModelId() : rootProductModelId; String key = buildOperationDemandedQuantityKey(sourceOperation.getTechnologyOperationId(), outputProductModelId); BigDecimal planQuantity = operationDemandedQuantityMap.get(key); return planQuantity != null ? planQuantity : defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity()); } /** * 计ç®å·¥åè®¡åæ°éï¼ä½¿ç¨ ProductionOrderRoutingOperationï¼ */ public BigDecimal resolveTaskPlanQuantity(ProductionOrderRoutingOperation routingOperation, Map<String, BigDecimal> demandedQuantityMap, BigDecimal orderQuantity, Long rootProductModelId) { if (routingOperation == null || demandedQuantityMap == null || demandedQuantityMap.isEmpty()) { return orderQuantity; } Long outputProductModelId = routingOperation.getProductModelId() != null ? routingOperation.getProductModelId() : rootProductModelId; String key = buildOperationDemandedQuantityKey(routingOperation.getTechnologyOperationId(), outputProductModelId); BigDecimal planQuantity = demandedQuantityMap.get(key); return planQuantity != null ? planQuantity : orderQuantity; } /** * æå»ºå·¥åºéæ±éæ å°è¡¨ */ public Map<String, BigDecimal> buildOperationDemandedQuantityMap(List<ProductionBomStructure> bomStructures, Long rootProductModelId) { if (bomStructures == null || bomStructures.isEmpty()) { return Collections.emptyMap(); } Map<Long, ProductionBomStructure> structureById = new HashMap<>(); for (ProductionBomStructure item : bomStructures) { if (item != null && item.getId() != null) { structureById.put(item.getId(), item); } } Map<String, BigDecimal> demandedQuantityMap = new HashMap<>(); Set<String> mergedOutputNodeKeySet = new HashSet<>(); for (ProductionBomStructure bomStructure : bomStructures) { if (bomStructure == null || bomStructure.getTechnologyOperationId() == null) { continue; } ProductionBomStructure outputNode = resolveOperationOutputNode(bomStructure, structureById); Long outputProductModelId = resolveOutputProductModelId(outputNode, rootProductModelId); if (outputProductModelId == null) { continue; } String mergedOutputNodeKey = buildOperationOutputNodeKey(bomStructure.getTechnologyOperationId(), outputNode == null ? null : outputNode.getId(), outputProductModelId); if (!mergedOutputNodeKeySet.add(mergedOutputNodeKey)) { continue; } BigDecimal demandedQuantity = defaultDecimal(outputNode == null ? null : outputNode.getDemandedQuantity()); String key = buildOperationDemandedQuantityKey(bomStructure.getTechnologyOperationId(), outputProductModelId); demandedQuantityMap.merge(key, demandedQuantity, BigDecimal::add); } return demandedQuantityMap; } /** * æå»ºå·¥åºéæ±ékey */ public String buildOperationDemandedQuantityKey(Long operationId, Long outputProductModelId) { return String.valueOf(operationId) + "#" + String.valueOf(outputProductModelId); } /** * æå»ºè¾åºèç¹key */ public String buildOperationOutputNodeKey(Long operationId, Long outputNodeId, Long outputProductModelId) { return String.valueOf(operationId) + "#" + String.valueOf(outputNodeId) + "#" + String.valueOf(outputProductModelId); } /** * è§£æå·¥åºè¾åºèç¹ */ public ProductionBomStructure resolveOperationOutputNode(ProductionBomStructure bomStructure, Map<Long, ProductionBomStructure> structureById) { if (bomStructure == null) { return null; } if (bomStructure.getParentId() == null) { return bomStructure; } ProductionBomStructure parent = structureById.get(bomStructure.getParentId()); return parent != null ? parent : bomStructure; } /** * è§£æè¾åºäº§åè§æ ¼ID */ public Long resolveOutputProductModelId(ProductionBomStructure outputNode, Long rootProductModelId) { if (outputNode == null) { return rootProductModelId; } return outputNode.getProductModelId() != null ? outputNode.getProductModelId() : rootProductModelId; } /** * é»è®¤BigDecimalå¼ */ public BigDecimal defaultDecimal(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; } } src/main/resources/mapper/production/ProductionOperationTaskMapper.xml
@@ -64,6 +64,9 @@ <if test="c != null and c.status != null"> and pot.status = #{c.status} </if> <if test="c != null and c.isProduction != null"> and poro.is_production = #{c.isProduction} </if> <if test="c != null and c.workOrderNo != null and c.workOrderNo != ''"> and pot.work_order_no like concat('%', #{c.workOrderNo}, '%') </if>