| | |
| | | package com.ruoyi.production.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.basic.mapper.ProductModelMapper; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.production.bean.dto.ProductionBomStructureDto; |
| | | import com.ruoyi.production.bean.vo.ProductionBomStructureVo; |
| | | import com.ruoyi.production.mapper.ProductionBomStructureMapper; |
| | | import com.ruoyi.production.mapper.ProductionOperationTaskMapper; |
| | | import com.ruoyi.production.mapper.ProductionOrderBomMapper; |
| | | import com.ruoyi.production.mapper.ProductionOrderMapper; |
| | | import com.ruoyi.production.mapper.ProductionOrderRoutingMapper; |
| | | import com.ruoyi.production.mapper.ProductionOrderRoutingOperationMapper; |
| | | import com.ruoyi.production.mapper.ProductionOrderRoutingOperationParamMapper; |
| | | import com.ruoyi.production.mapper.ProductionProductMainMapper; |
| | | import com.ruoyi.production.pojo.ProductionBomStructure; |
| | | import com.ruoyi.production.pojo.ProductionOperationTask; |
| | | import com.ruoyi.production.pojo.ProductionOrder; |
| | | import com.ruoyi.production.pojo.ProductionOrderBom; |
| | | import com.ruoyi.production.pojo.ProductionOrderRouting; |
| | | import com.ruoyi.production.pojo.ProductionOrderRoutingOperation; |
| | | 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; |
| | | import com.ruoyi.technology.pojo.TechnologyOperation; |
| | | import com.ruoyi.technology.pojo.TechnologyOperationParam; |
| | | import com.ruoyi.technology.pojo.TechnologyParam; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * <p> |
| | |
| | | @RequiredArgsConstructor() |
| | | public class ProductionBomStructureServiceImpl extends ServiceImpl<ProductionBomStructureMapper, ProductionBomStructure> implements ProductionBomStructureService { |
| | | |
| | | private final ProductionBomStructureMapper productionBomStructureMapper; |
| | | private final ProductionBomStructureMapper productionBomStructureMapper; |
| | | private final ProductionOrderBomMapper productionOrderBomMapper; |
| | | private final ProductionOrderMapper productionOrderMapper; |
| | | private final ProductionOrderRoutingMapper productionOrderRoutingMapper; |
| | | private final ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper; |
| | | private final ProductionOrderRoutingOperationParamMapper productionOrderRoutingOperationParamMapper; |
| | | private final ProductionOperationTaskMapper productionOperationTaskMapper; |
| | | private final ProductionProductMainMapper productionProductMainMapper; |
| | | private final ProductModelMapper productModelMapper; |
| | | private final TechnologyOperationMapper technologyOperationMapper; |
| | | private final TechnologyOperationParamMapper technologyOperationParamMapper; |
| | | private final TechnologyParamMapper technologyParamMapper; |
| | | |
| | | /** |
| | | * 根据BOM查询并组装结构树。 |
| | | * 按订单BOM查询扁平物料明细。 |
| | | */ |
| | | @Override |
| | | public List<ProductionBomStructureVo> listByBomId(Long bomId) { |
| | | List<ProductionBomStructureVo> list = productionBomStructureMapper.listByBomId(bomId); |
| | | Map<Long, ProductionBomStructureVo> map = new HashMap<>(); |
| | | for (ProductionBomStructureVo node : list) { |
| | | node.setChildren(new ArrayList<>()); |
| | | map.put(node.getId(), node); |
| | | if (bomId == null) { |
| | | return new ArrayList<>(); |
| | | } |
| | | return productionBomStructureMapper.listByBomId(bomId); |
| | | } |
| | | |
| | | /** |
| | | * 按生产订单查询订单BOM快照的扁平物料明细。 |
| | | */ |
| | | @Override |
| | | public List<ProductionBomStructureVo> listByOrderId(Long productionOrderId) { |
| | | if (productionOrderId == null) { |
| | | return new ArrayList<>(); |
| | | } |
| | | ProductionOrderBom orderBom = productionOrderBomMapper.selectOne( |
| | | Wrappers.<ProductionOrderBom>lambdaQuery() |
| | | .eq(ProductionOrderBom::getProductionOrderId, productionOrderId) |
| | | .orderByDesc(ProductionOrderBom::getId) |
| | | .last("limit 1")); |
| | | if (orderBom == null || orderBom.getId() == null) { |
| | | return new ArrayList<>(); |
| | | } |
| | | return listByBomId(orderBom.getId()); |
| | | } |
| | | |
| | | /** |
| | | * 全量同步订单BOM快照的扁平物料明细,并按其中的工序重建订单工艺路线快照与工单。 |
| | | */ |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean addProductionBomStructure(ProductionBomStructureDto dto) { |
| | | Long orderBomId = dto.getProductionOrderBomId(); |
| | | if (orderBomId == null) { |
| | | throw new ServiceException("订单BOM ID不能为空"); |
| | | } |
| | | ProductionOrderBom orderBom = productionOrderBomMapper.selectById(orderBomId); |
| | | if (orderBom == null) { |
| | | throw new ServiceException("订单BOM不存在"); |
| | | } |
| | | Long productionOrderId = dto.getProductionOrderId() != null |
| | | ? dto.getProductionOrderId() |
| | | : orderBom.getProductionOrderId(); |
| | | |
| | | List<ProductionBomStructureDto> detailList = dto.getDetailList() == null |
| | | ? Collections.emptyList() |
| | | : dto.getDetailList(); |
| | | |
| | | List<ProductionBomStructure> dbList = this.list(new LambdaQueryWrapper<ProductionBomStructure>() |
| | | .eq(ProductionBomStructure::getProductionOrderBomId, orderBomId)); |
| | | Set<Long> dbIds = dbList.stream().map(ProductionBomStructure::getId).collect(Collectors.toSet()); |
| | | |
| | | Set<Long> submittedIds = new HashSet<>(); |
| | | Set<String> relationKeys = new HashSet<>(); |
| | | List<ProductionBomStructure> insertList = new ArrayList<>(); |
| | | List<ProductionBomStructure> updateList = new ArrayList<>(); |
| | | |
| | | for (ProductionBomStructureDto item : detailList) { |
| | | Long productModelId = item.getProductModelId(); |
| | | Long technologyOperationId = item.getTechnologyOperationId(); |
| | | if (productModelId == null) { |
| | | throw new ServiceException("请选择物料规格"); |
| | | } |
| | | if (productModelMapper.selectById(productModelId) == null) { |
| | | throw new ServiceException("物料规格不存在"); |
| | | } |
| | | if (technologyOperationId == null) { |
| | | throw new ServiceException("请选择消耗工序"); |
| | | } |
| | | if (technologyOperationMapper.selectById(technologyOperationId) == null) { |
| | | throw new ServiceException("消耗工序不存在"); |
| | | } |
| | | if (productModelId.equals(orderBom.getProductModelId())) { |
| | | throw new ServiceException("成品规格不能作为自身的物料"); |
| | | } |
| | | if (!relationKeys.add(productModelId + "#" + technologyOperationId)) { |
| | | throw new ServiceException("同一物料规格在同一工序下不能重复配置"); |
| | | } |
| | | |
| | | ProductionBomStructure entity = new ProductionBomStructure(); |
| | | entity.setProductionOrderBomId(orderBomId); |
| | | entity.setProductionOrderId(productionOrderId); |
| | | entity.setProductModelId(productModelId); |
| | | entity.setTechnologyOperationId(technologyOperationId); |
| | | // 扁平快照不再使用层级与数量语义 |
| | | entity.setParentId(null); |
| | | entity.setUnitQuantity(null); |
| | | entity.setDemandedQuantity(null); |
| | | entity.setUnit(null); |
| | | |
| | | if (item.getId() == null) { |
| | | insertList.add(entity); |
| | | } else { |
| | | if (!dbIds.contains(item.getId())) { |
| | | throw new ServiceException("订单BOM物料明细不存在或不属于当前订单BOM"); |
| | | } |
| | | entity.setId(item.getId()); |
| | | submittedIds.add(item.getId()); |
| | | updateList.add(entity); |
| | | } |
| | | } |
| | | |
| | | List<ProductionBomStructureVo> tree = new ArrayList<>(); |
| | | for (ProductionBomStructureVo node : list) { |
| | | Long parentId = node.getParentId(); |
| | | if (parentId == null || parentId == 0L) { |
| | | tree.add(node); |
| | | Set<Long> deleteIds = new HashSet<>(dbIds); |
| | | deleteIds.removeAll(submittedIds); |
| | | if (!deleteIds.isEmpty()) { |
| | | this.removeByIds(deleteIds); |
| | | } |
| | | if (!updateList.isEmpty()) { |
| | | this.updateBatchById(updateList); |
| | | } |
| | | if (!insertList.isEmpty()) { |
| | | this.saveBatch(insertList); |
| | | } |
| | | |
| | | syncRoutingAndTaskByStructure(orderBomId, productionOrderId); |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * 订单BOM物料变化后,按其工序集合重建订单工艺路线工序快照与工单计划数量。 |
| | | */ |
| | | private void syncRoutingAndTaskByStructure(Long orderBomId, Long productionOrderId) { |
| | | if (orderBomId == null) { |
| | | return; |
| | | } |
| | | ProductionOrderBom orderBom = productionOrderBomMapper.selectById(orderBomId); |
| | | if (orderBom == null) { |
| | | return; |
| | | } |
| | | Long currentProductionOrderId = productionOrderId != null ? productionOrderId : orderBom.getProductionOrderId(); |
| | | if (currentProductionOrderId == null) { |
| | | return; |
| | | } |
| | | ProductionOrder productionOrder = productionOrderMapper.selectById(currentProductionOrderId); |
| | | if (productionOrder == null) { |
| | | return; |
| | | } |
| | | |
| | | List<ProductionBomStructure> structureList = this.list( |
| | | Wrappers.<ProductionBomStructure>lambdaQuery() |
| | | .eq(ProductionBomStructure::getProductionOrderBomId, orderBomId) |
| | | .orderByAsc(ProductionBomStructure::getId)); |
| | | Long rootProductModelId = orderBom.getProductModelId() != null |
| | | ? orderBom.getProductModelId() |
| | | : productionOrder.getProductModelId(); |
| | | // 同步生产工艺路线快照 |
| | | syncRoutingOperationsByBom(currentProductionOrderId, productionOrder, orderBom, structureList, rootProductModelId); |
| | | // 同步工单计划数量:扁平BOM无数量配置,计划数量统一取订单数量 |
| | | syncTaskPlanQuantity(currentProductionOrderId, TaskPlanQuantityUtil.resolveTaskPlanQuantity(productionOrder)); |
| | | } |
| | | |
| | | private void syncTaskPlanQuantity(Long productionOrderId, BigDecimal planQuantity) { |
| | | List<ProductionOperationTask> taskList = productionOperationTaskMapper.selectList( |
| | | Wrappers.<ProductionOperationTask>lambdaQuery() |
| | | .eq(ProductionOperationTask::getProductionOrderId, productionOrderId) |
| | | .orderByAsc(ProductionOperationTask::getId)); |
| | | if (taskList == null || taskList.isEmpty()) { |
| | | return; |
| | | } |
| | | for (ProductionOperationTask task : taskList) { |
| | | if (task == null || task.getId() == null) { |
| | | continue; |
| | | } |
| | | ProductionBomStructureVo parent = map.get(parentId); |
| | | if (parent != null) { |
| | | parent.getChildren().add(node); |
| | | if (compareDecimal(task.getPlanQuantity(), planQuantity) == 0) { |
| | | continue; |
| | | } |
| | | ProductionOperationTask update = new ProductionOperationTask(); |
| | | update.setId(task.getId()); |
| | | update.setPlanQuantity(planQuantity); |
| | | productionOperationTaskMapper.updateById(update); |
| | | } |
| | | } |
| | | |
| | | private void syncRoutingOperationsByBom(Long productionOrderId, |
| | | ProductionOrder productionOrder, |
| | | ProductionOrderBom orderBom, |
| | | List<ProductionBomStructure> structureList, |
| | | Long rootProductModelId) { |
| | | ProductionOrderRouting orderRouting = getOrCreateOrderRoutingSnapshot(productionOrderId, productionOrder, orderBom, rootProductModelId); |
| | | List<ProductionOrderRoutingOperation> desiredOperationList = buildDesiredRoutingOperationList(structureList, rootProductModelId); |
| | | List<ProductionOrderRoutingOperation> existingOperationList = productionOrderRoutingOperationMapper.selectList( |
| | | Wrappers.<ProductionOrderRoutingOperation>lambdaQuery() |
| | | .eq(ProductionOrderRoutingOperation::getOrderRoutingId, orderRouting.getId()) |
| | | .eq(ProductionOrderRoutingOperation::getProductionOrderId, productionOrderId) |
| | | .orderByAsc(ProductionOrderRoutingOperation::getDragSort) |
| | | .orderByAsc(ProductionOrderRoutingOperation::getId)); |
| | | Map<String, Deque<ProductionOrderRoutingOperation>> existingBucketMap = buildExistingRoutingOperationBucketMap(existingOperationList); |
| | | List<ProductionOrderRoutingOperation> finalOperationList = new ArrayList<>(); |
| | | for (ProductionOrderRoutingOperation desiredOperation : desiredOperationList) { |
| | | String bucketKey = buildRoutingOperationBucketKey( |
| | | desiredOperation.getTechnologyOperationId(), |
| | | desiredOperation.getProductModelId()); |
| | | Deque<ProductionOrderRoutingOperation> matchedQueue = existingBucketMap.get(bucketKey); |
| | | ProductionOrderRoutingOperation matchedOperation = matchedQueue == null ? null : matchedQueue.pollFirst(); |
| | | if (matchedOperation == null) { |
| | | matchedOperation = insertRoutingOperationSnapshot(orderRouting.getId(), productionOrderId, desiredOperation); |
| | | } else { |
| | | updateRoutingOperationSnapshotIfNecessary(matchedOperation, orderRouting.getId(), productionOrderId, desiredOperation); |
| | | } |
| | | finalOperationList.add(matchedOperation); |
| | | } |
| | | for (Deque<ProductionOrderRoutingOperation> queue : existingBucketMap.values()) { |
| | | while (queue != null && !queue.isEmpty()) { |
| | | removeRoutingOperationSnapshot(queue.pollFirst()); |
| | | } |
| | | } |
| | | return tree; |
| | | syncRoutingOperationTasks(productionOrderId, finalOperationList); |
| | | } |
| | | |
| | | private ProductionOrderRouting getOrCreateOrderRoutingSnapshot(Long productionOrderId, |
| | | ProductionOrder productionOrder, |
| | | ProductionOrderBom orderBom, |
| | | Long rootProductModelId) { |
| | | ProductionOrderRouting orderRouting = productionOrderRoutingMapper.selectOne( |
| | | Wrappers.<ProductionOrderRouting>lambdaQuery() |
| | | .eq(ProductionOrderRouting::getProductionOrderId, productionOrderId) |
| | | .orderByDesc(ProductionOrderRouting::getId) |
| | | .last("limit 1")); |
| | | if (orderRouting == null) { |
| | | orderRouting = new ProductionOrderRouting(); |
| | | orderRouting.setProductionOrderId(productionOrderId); |
| | | orderRouting.setProductModelId(rootProductModelId); |
| | | orderRouting.setTechnologyRoutingId(productionOrder == null ? null : productionOrder.getTechnologyRoutingId()); |
| | | orderRouting.setBomId(orderBom == null ? null : orderBom.getBomId()); |
| | | orderRouting.setOrderBomId(orderBom == null ? null : orderBom.getId()); |
| | | productionOrderRoutingMapper.insert(orderRouting); |
| | | return orderRouting; |
| | | } |
| | | ProductionOrderRouting update = new ProductionOrderRouting(); |
| | | update.setId(orderRouting.getId()); |
| | | boolean changed = false; |
| | | if (!Objects.equals(orderRouting.getProductModelId(), rootProductModelId)) { |
| | | update.setProductModelId(rootProductModelId); |
| | | orderRouting.setProductModelId(rootProductModelId); |
| | | changed = true; |
| | | } |
| | | Long technologyRoutingId = productionOrder == null ? null : productionOrder.getTechnologyRoutingId(); |
| | | if (!Objects.equals(orderRouting.getTechnologyRoutingId(), technologyRoutingId)) { |
| | | update.setTechnologyRoutingId(technologyRoutingId); |
| | | orderRouting.setTechnologyRoutingId(technologyRoutingId); |
| | | changed = true; |
| | | } |
| | | Long bomId = orderBom == null ? null : orderBom.getBomId(); |
| | | if (!Objects.equals(orderRouting.getBomId(), bomId)) { |
| | | update.setBomId(bomId); |
| | | orderRouting.setBomId(bomId); |
| | | changed = true; |
| | | } |
| | | Long orderBomId = orderBom == null ? null : orderBom.getId(); |
| | | if (!Objects.equals(orderRouting.getOrderBomId(), orderBomId)) { |
| | | update.setOrderBomId(orderBomId); |
| | | orderRouting.setOrderBomId(orderBomId); |
| | | changed = true; |
| | | } |
| | | if (changed) { |
| | | productionOrderRoutingMapper.updateById(update); |
| | | } |
| | | return orderRouting; |
| | | } |
| | | |
| | | /** |
| | | * 扁平BOM按工序去重生成期望的订单工序列表, |
| | | * 顺序取物料明细中工序首次出现的顺序,产出规格固定为订单成品规格。 |
| | | */ |
| | | private List<ProductionOrderRoutingOperation> buildDesiredRoutingOperationList(List<ProductionBomStructure> structureList, |
| | | Long rootProductModelId) { |
| | | if (structureList == null || structureList.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | Set<Long> uniqueOperationIdList = new LinkedHashSet<>(); |
| | | for (ProductionBomStructure structure : structureList) { |
| | | if (structure == null || structure.getTechnologyOperationId() == null) { |
| | | continue; |
| | | } |
| | | uniqueOperationIdList.add(structure.getTechnologyOperationId()); |
| | | } |
| | | |
| | | List<ProductionOrderRoutingOperation> desiredOperationList = new ArrayList<>(uniqueOperationIdList.size()); |
| | | int dragSort = 1; |
| | | for (Long operationId : uniqueOperationIdList) { |
| | | TechnologyOperation technologyOperation = getTechnologyOperation(operationId); |
| | | ProductionOrderRoutingOperation routingOperation = new ProductionOrderRoutingOperation(); |
| | | routingOperation.setProductModelId(rootProductModelId); |
| | | routingOperation.setTechnologyOperationId(operationId); |
| | | routingOperation.setOperationName(technologyOperation == null ? null : technologyOperation.getName()); |
| | | routingOperation.setIsQuality(technologyOperation == null ? null : technologyOperation.getIsQuality()); |
| | | routingOperation.setIsProduction(technologyOperation == null ? null : technologyOperation.getIsProduction()); |
| | | routingOperation.setType(technologyOperation == null ? null : technologyOperation.getType()); |
| | | routingOperation.setDragSort(dragSort++); |
| | | desiredOperationList.add(routingOperation); |
| | | } |
| | | return desiredOperationList; |
| | | } |
| | | |
| | | private Map<String, Deque<ProductionOrderRoutingOperation>> buildExistingRoutingOperationBucketMap(List<ProductionOrderRoutingOperation> existingOperationList) { |
| | | Map<String, Deque<ProductionOrderRoutingOperation>> existingBucketMap = new LinkedHashMap<>(); |
| | | if (existingOperationList == null || existingOperationList.isEmpty()) { |
| | | return existingBucketMap; |
| | | } |
| | | for (ProductionOrderRoutingOperation routingOperation : existingOperationList) { |
| | | String bucketKey = buildRoutingOperationBucketKey( |
| | | routingOperation.getTechnologyOperationId(), |
| | | routingOperation.getProductModelId()); |
| | | existingBucketMap.computeIfAbsent(bucketKey, key -> new ArrayDeque<>()).addLast(routingOperation); |
| | | } |
| | | return existingBucketMap; |
| | | } |
| | | |
| | | private ProductionOrderRoutingOperation insertRoutingOperationSnapshot(Long orderRoutingId, |
| | | Long productionOrderId, |
| | | ProductionOrderRoutingOperation desiredOperation) { |
| | | ProductionOrderRoutingOperation insert = new ProductionOrderRoutingOperation(); |
| | | insert.setOrderRoutingId(orderRoutingId); |
| | | insert.setProductionOrderId(productionOrderId); |
| | | insert.setProductModelId(desiredOperation.getProductModelId()); |
| | | insert.setTechnologyOperationId(desiredOperation.getTechnologyOperationId()); |
| | | insert.setOperationName(desiredOperation.getOperationName()); |
| | | insert.setIsQuality(desiredOperation.getIsQuality()); |
| | | insert.setIsProduction(desiredOperation.getIsProduction()); |
| | | insert.setType(desiredOperation.getType()); |
| | | insert.setDragSort(desiredOperation.getDragSort()); |
| | | productionOrderRoutingOperationMapper.insert(insert); |
| | | syncRoutingOperationParams(insert.getId(), productionOrderId, insert.getTechnologyOperationId()); |
| | | return insert; |
| | | } |
| | | |
| | | private void updateRoutingOperationSnapshotIfNecessary(ProductionOrderRoutingOperation currentOperation, |
| | | Long orderRoutingId, |
| | | Long productionOrderId, |
| | | ProductionOrderRoutingOperation desiredOperation) { |
| | | if (currentOperation == null || currentOperation.getId() == null) { |
| | | return; |
| | | } |
| | | ProductionOrderRoutingOperation update = new ProductionOrderRoutingOperation(); |
| | | update.setId(currentOperation.getId()); |
| | | boolean changed = false; |
| | | if (!Objects.equals(currentOperation.getOrderRoutingId(), orderRoutingId)) { |
| | | update.setOrderRoutingId(orderRoutingId); |
| | | currentOperation.setOrderRoutingId(orderRoutingId); |
| | | changed = true; |
| | | } |
| | | if (!Objects.equals(currentOperation.getProductionOrderId(), productionOrderId)) { |
| | | update.setProductionOrderId(productionOrderId); |
| | | currentOperation.setProductionOrderId(productionOrderId); |
| | | changed = true; |
| | | } |
| | | if (!Objects.equals(currentOperation.getProductModelId(), desiredOperation.getProductModelId())) { |
| | | update.setProductModelId(desiredOperation.getProductModelId()); |
| | | currentOperation.setProductModelId(desiredOperation.getProductModelId()); |
| | | changed = true; |
| | | } |
| | | if (!Objects.equals(currentOperation.getTechnologyOperationId(), desiredOperation.getTechnologyOperationId())) { |
| | | update.setTechnologyOperationId(desiredOperation.getTechnologyOperationId()); |
| | | currentOperation.setTechnologyOperationId(desiredOperation.getTechnologyOperationId()); |
| | | changed = true; |
| | | } |
| | | if (!Objects.equals(currentOperation.getOperationName(), desiredOperation.getOperationName())) { |
| | | update.setOperationName(desiredOperation.getOperationName()); |
| | | currentOperation.setOperationName(desiredOperation.getOperationName()); |
| | | changed = true; |
| | | } |
| | | if (!Objects.equals(currentOperation.getIsQuality(), desiredOperation.getIsQuality())) { |
| | | update.setIsQuality(desiredOperation.getIsQuality()); |
| | | currentOperation.setIsQuality(desiredOperation.getIsQuality()); |
| | | changed = true; |
| | | } |
| | | if (!Objects.equals(currentOperation.getIsProduction(), desiredOperation.getIsProduction())) { |
| | | update.setIsProduction(desiredOperation.getIsProduction()); |
| | | currentOperation.setIsProduction(desiredOperation.getIsProduction()); |
| | | changed = true; |
| | | } |
| | | if (!Objects.equals(currentOperation.getType(), desiredOperation.getType())) { |
| | | update.setType(desiredOperation.getType()); |
| | | currentOperation.setType(desiredOperation.getType()); |
| | | changed = true; |
| | | } |
| | | if (!Objects.equals(currentOperation.getDragSort(), desiredOperation.getDragSort())) { |
| | | update.setDragSort(desiredOperation.getDragSort()); |
| | | currentOperation.setDragSort(desiredOperation.getDragSort()); |
| | | changed = true; |
| | | } |
| | | if (changed) { |
| | | productionOrderRoutingOperationMapper.updateById(update); |
| | | } |
| | | } |
| | | |
| | | private void removeRoutingOperationSnapshot(ProductionOrderRoutingOperation routingOperation) { |
| | | if (routingOperation == null || routingOperation.getId() == null) { |
| | | return; |
| | | } |
| | | ProductionOperationTask task = productionOperationTaskMapper.selectOne( |
| | | Wrappers.<ProductionOperationTask>lambdaQuery() |
| | | .eq(ProductionOperationTask::getProductionOrderRoutingOperationId, routingOperation.getId()) |
| | | .last("limit 1")); |
| | | if (task != null) { |
| | | validateTaskCanRemove(task); |
| | | productionOperationTaskMapper.deleteById(task.getId()); |
| | | } |
| | | productionOrderRoutingOperationParamMapper.delete( |
| | | Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery() |
| | | .eq(ProductionOrderRoutingOperationParam::getProductionOrderRoutingOperationId, routingOperation.getId())); |
| | | productionOrderRoutingOperationMapper.deleteById(routingOperation.getId()); |
| | | } |
| | | |
| | | private void syncRoutingOperationTasks(Long productionOrderId, List<ProductionOrderRoutingOperation> routingOperationList) { |
| | | if (routingOperationList == null || routingOperationList.isEmpty()) { |
| | | return; |
| | | } |
| | | List<Long> routingOperationIdList = routingOperationList.stream() |
| | | .map(ProductionOrderRoutingOperation::getId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toList()); |
| | | if (routingOperationIdList.isEmpty()) { |
| | | return; |
| | | } |
| | | Map<Long, ProductionOperationTask> taskByRoutingOperationId = productionOperationTaskMapper.selectList( |
| | | Wrappers.<ProductionOperationTask>lambdaQuery() |
| | | .in(ProductionOperationTask::getProductionOrderRoutingOperationId, routingOperationIdList) |
| | | .orderByAsc(ProductionOperationTask::getId)) |
| | | .stream() |
| | | .filter(item -> item != null && item.getProductionOrderRoutingOperationId() != null) |
| | | .collect(Collectors.toMap( |
| | | ProductionOperationTask::getProductionOrderRoutingOperationId, |
| | | item -> item, |
| | | (left, right) -> left, |
| | | LinkedHashMap::new)); |
| | | for (int i = 0; i < routingOperationList.size(); i++) { |
| | | ProductionOrderRoutingOperation routingOperation = routingOperationList.get(i); |
| | | if (routingOperation == null || routingOperation.getId() == null) { |
| | | continue; |
| | | } |
| | | boolean shouldHaveTask = i == routingOperationList.size() - 1 || Boolean.TRUE.equals(routingOperation.getIsProduction()); |
| | | ProductionOperationTask existingTask = taskByRoutingOperationId.get(routingOperation.getId()); |
| | | if (shouldHaveTask) { |
| | | if (existingTask == null) { |
| | | ProductionOperationTask task = new ProductionOperationTask(); |
| | | task.setProductionOrderId(productionOrderId); |
| | | task.setProductionOrderRoutingOperationId(routingOperation.getId()); |
| | | task.setPlanQuantity(BigDecimal.ZERO); |
| | | task.setCompleteQuantity(BigDecimal.ZERO); |
| | | task.setWorkOrderNo(generateNextTaskNo()); |
| | | task.setStatus(2); |
| | | productionOperationTaskMapper.insert(task); |
| | | } |
| | | continue; |
| | | } |
| | | if (existingTask != null) { |
| | | validateTaskCanRemove(existingTask); |
| | | productionOperationTaskMapper.deleteById(existingTask.getId()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void validateTaskCanRemove(ProductionOperationTask task) { |
| | | if (task == null || task.getId() == null) { |
| | | return; |
| | | } |
| | | if (defaultDecimal(task.getCompleteQuantity()).compareTo(BigDecimal.ZERO) > 0) { |
| | | throw new ServiceException("工序已产生报工记录,无法根据 BOM 变更删除对应工序快照" + task.getWorkOrderNo()); |
| | | } |
| | | long reportCount = productionProductMainMapper.selectCount( |
| | | Wrappers.<ProductionProductMain>lambdaQuery() |
| | | .eq(ProductionProductMain::getProductionOperationTaskId, task.getId())); |
| | | if (reportCount > 0) { |
| | | throw new ServiceException("工序已产生报工记录,无法根据 BOM 变更删除对应工单"); |
| | | } |
| | | } |
| | | |
| | | private void syncRoutingOperationParams(Long routingOperationId, Long productionOrderId, Long technologyOperationId) { |
| | | if (routingOperationId == null || technologyOperationId == null) { |
| | | return; |
| | | } |
| | | List<TechnologyOperationParam> operationParamList = technologyOperationParamMapper.selectList( |
| | | Wrappers.<TechnologyOperationParam>lambdaQuery() |
| | | .eq(TechnologyOperationParam::getTechnologyOperationId, technologyOperationId) |
| | | .orderByAsc(TechnologyOperationParam::getId)); |
| | | for (TechnologyOperationParam operationParam : operationParamList) { |
| | | TechnologyParam technologyParam = technologyParamMapper.selectById(operationParam.getTechnologyParamId()); |
| | | if (technologyParam == null) { |
| | | continue; |
| | | } |
| | | ProductionOrderRoutingOperationParam snapshot = new ProductionOrderRoutingOperationParam(); |
| | | snapshot.setProductionOrderId(productionOrderId); |
| | | snapshot.setProductionOrderRoutingOperationId(routingOperationId); |
| | | snapshot.setTechnologyOperationId(operationParam.getTechnologyOperationId()); |
| | | snapshot.setTechnologyOperationParamId(operationParam.getId()); |
| | | snapshot.setParamId(technologyParam.getId()); |
| | | snapshot.setParamCode(technologyParam.getParamCode()); |
| | | snapshot.setParamName(technologyParam.getParamName()); |
| | | snapshot.setParamType(technologyParam.getParamType()); |
| | | snapshot.setParamFormat(technologyParam.getParamFormat()); |
| | | snapshot.setUnit(technologyParam.getUnit()); |
| | | snapshot.setIsRequired(technologyParam.getIsRequired()); |
| | | snapshot.setRemark(technologyParam.getRemark()); |
| | | snapshot.setStandardValue(operationParam.getStandardValue()); |
| | | productionOrderRoutingOperationParamMapper.insert(snapshot); |
| | | } |
| | | } |
| | | |
| | | private String buildRoutingOperationBucketKey(Long operationId, Long outputProductModelId) { |
| | | return String.valueOf(operationId) + "#" + String.valueOf(outputProductModelId); |
| | | } |
| | | |
| | | private TechnologyOperation getTechnologyOperation(Long technologyOperationId) { |
| | | if (technologyOperationId == null) { |
| | | return null; |
| | | } |
| | | return technologyOperationMapper.selectById(technologyOperationId); |
| | | } |
| | | |
| | | private String generateNextTaskNo() { |
| | | String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); |
| | | ProductionOperationTask latestTask = productionOperationTaskMapper.selectOne( |
| | | Wrappers.<ProductionOperationTask>lambdaQuery() |
| | | .likeRight(ProductionOperationTask::getWorkOrderNo, "GD" + datePrefix) |
| | | .orderByDesc(ProductionOperationTask::getWorkOrderNo) |
| | | .last("limit 1")); |
| | | int sequenceNumber = 1; |
| | | if (latestTask != null && latestTask.getWorkOrderNo() != null && latestTask.getWorkOrderNo().startsWith("GD" + datePrefix)) { |
| | | try { |
| | | sequenceNumber = Integer.parseInt(latestTask.getWorkOrderNo().substring(("GD" + datePrefix).length())) + 1; |
| | | } catch (NumberFormatException ignored) { |
| | | sequenceNumber = 1; |
| | | } |
| | | } |
| | | return "GD" + String.format("%s%03d", datePrefix, sequenceNumber); |
| | | } |
| | | |
| | | private BigDecimal defaultDecimal(BigDecimal value) { |
| | | return value == null ? BigDecimal.ZERO : value; |
| | | } |
| | | |
| | | private int compareDecimal(BigDecimal left, BigDecimal right) { |
| | | return defaultDecimal(left).compareTo(defaultDecimal(right)); |
| | | } |
| | | |
| | | } |