| | |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.production.bean.dto.ProductionOrderDto; |
| | | import com.ruoyi.production.bean.vo.ProductionOrderVo; |
| | | import com.ruoyi.production.bean.vo.ProductionPlanVo; |
| | | import com.ruoyi.production.enums.ProductOrderStatusEnum; |
| | | import com.ruoyi.production.mapper.*; |
| | | import com.ruoyi.production.pojo.*; |
| | | import com.ruoyi.production.service.ProductionOrderService; |
| | | import com.ruoyi.sales.mapper.SalesLedgerMapper; |
| | | import com.ruoyi.sales.mapper.SalesLedgerProductMapper; |
| | | import com.ruoyi.sales.pojo.SalesLedger; |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | | import com.ruoyi.technology.mapper.*; |
| | | import com.ruoyi.technology.pojo.*; |
| | | import lombok.RequiredArgsConstructor; |
| | |
| | | if (!saved) { |
| | | return false; |
| | | } |
| | | syncProductionPlanIssueStatus(oldOrder, productionOrder); |
| | | boolean needSync = productionOrder.getTechnologyRoutingId() != null |
| | | && (oldOrder == null |
| | | || !Objects.equals(oldOrder.getTechnologyRoutingId(), productionOrder.getTechnologyRoutingId()) |
| | |
| | | } |
| | | |
| | | @Override |
| | | public List<ProductionPlanVo> getSource(Long id) { |
| | | ProductionOrder productionOrder = baseMapper.selectById(id); |
| | | if (productionOrder != null && productionOrder.getProductionPlanIds() != null) { |
| | | List<Long> planIds = parsePlanIds(productionOrder.getProductionPlanIds()); |
| | | return productionPlanMapper.getSource(planIds); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public int syncProductionOrderSnapshot(Long productionOrderId) { |
| | | ProductionOrder productionOrder = this.getById(productionOrderId); |
| | | if (productionOrder == null) { |
| | |
| | | ProductionOrderRoutingOperation targetOperation = new ProductionOrderRoutingOperation(); |
| | | targetOperation.setProductionOrderId(productionOrder.getId()); |
| | | targetOperation.setTechnologyRoutingOperationId(sourceOperation.getId()); |
| | | targetOperation.setTechnologyRoutingId(orderRouting.getId()); |
| | | targetOperation.setOrderRoutingId(orderRouting.getId()); |
| | | targetOperation.setProductModelId(sourceOperation.getProductModelId()); |
| | | targetOperation.setDragSort(sourceOperation.getDragSort()); |
| | | targetOperation.setIsQuality(sourceOperation.getIsQuality()); |
| | | targetOperation.setOperationName(operationNameMap.get(sourceOperation.getTechnologyOperationId())); |
| | | targetOperation.setTechnologyOperationId(sourceOperation.getTechnologyOperationId()); |
| | | productionOrderRoutingOperationMapper.insert(targetOperation); |
| | | |
| | | ProductionOperationTask task = new ProductionOperationTask(); |
| | |
| | | // 工序执行参数同样做快照,避免工艺参数调整影响已下达订单。 |
| | | ProductionOrderRoutingOperationParam targetParam = new ProductionOrderRoutingOperationParam(); |
| | | targetParam.setProductionOrderId(productionOrder.getId()); |
| | | targetParam.setTechnologyRoutingOperationId(targetOperation.getId()); |
| | | targetParam.setProductionOrderRoutingOperationId(targetOperation.getId()); |
| | | targetParam.setTechnologyRoutingOperationParamId(sourceParam.getId()); |
| | | targetParam.setParamId(sourceParam.getParamId()); |
| | | targetParam.setTechnologyOperationId(sourceParam.getTechnologyOperationId()); |
| | |
| | | orderBom.setProductionOrderId(productionOrder.getId()); |
| | | orderBom.setBomId(Long.valueOf(technologyBom.getId())); |
| | | orderBom.setProductModelId(root != null ? root.getProductModelId() : productionOrder.getProductModelId()); |
| | | orderBom.setTechnologyOperationId(root == null ? null : root.getOperationId()); |
| | | orderBom.setUnitQuantity(root != null && root.getUnitQuantity() != null ? root.getUnitQuantity() : BigDecimal.ONE); |
| | | orderBom.setDemandedQuantity(orderQuantity); |
| | | orderBom.setUnit(root == null ? null : root.getUnit()); |
| | | orderBom.setRemark(technologyBom.getRemark()); |
| | | orderBom.setBomNo(technologyBom.getBomNo()); |
| | | orderBom.setVersion(technologyBom.getVersion()); |
| | | productionOrderBomMapper.insert(orderBom); |
| | | |
| | | Map<Long, Long> idMap = new HashMap<>(); |
| | |
| | | target.setProductModelId(source.getProductModelId()); |
| | | target.setTechnologyOperationId(source.getOperationId()); |
| | | target.setUnitQuantity(source.getUnitQuantity()); |
| | | target.setDemandedQuantity(resolveBomDemandQuantity(source, orderQuantity)); |
| | | target.setDemandedQuantity(source.getUnitQuantity().multiply(orderQuantity)); |
| | | target.setUnit(source.getUnit()); |
| | | productionBomStructureMapper.insert(target); |
| | | idMap.put(source.getId(), target.getId()); |
| | |
| | | ProductionOrder query = dto == null ? new ProductionOrder() : dto; |
| | | return Wrappers.<ProductionOrder>lambdaQuery() |
| | | .eq(query.getId() != null, ProductionOrder::getId, query.getId()) |
| | | .eq(query.getSalesLedgerId() != null, ProductionOrder::getSalesLedgerId, query.getSalesLedgerId()) |
| | | .eq(query.getProductModelId() != null, ProductionOrder::getProductModelId, query.getProductModelId()) |
| | | .eq(query.getTechnologyRoutingId() != null, ProductionOrder::getTechnologyRoutingId, query.getTechnologyRoutingId()) |
| | | .like(query.getNpsNo() != null && !query.getNpsNo().trim().isEmpty(), ProductionOrder::getNpsNo, query.getNpsNo()) |
| | |
| | | if (productionOrder == null) { |
| | | throw new ServiceException("生产订单不能为空"); |
| | | } |
| | | fillFromSalesLedgerProduct(productionOrder); |
| | | fillFromProductionPlans(productionOrder); |
| | | if (productionOrder.getProductModelId() == null) { |
| | | throw new ServiceException("产品规格ID不能为空"); |
| | |
| | | if (defaultDecimal(productionOrder.getQuantity()).compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw new ServiceException("下单数量必须大于0"); |
| | | } |
| | | // if (productionOrder.getTechnologyRoutingId() == null) { |
| | | // // 未显式指定工艺路线时,按产品规格选最新一条工艺作为默认路线。 |
| | | // TechnologyRouting technologyRouting = technologyRoutingMapper.selectOne( |
| | | // Wrappers.<TechnologyRouting>lambdaQuery() |
| | | // .eq(TechnologyRouting::getProductModelId, productionOrder.getProductModelId()) |
| | | // .orderByDesc(TechnologyRouting::getId) |
| | | // .last("limit 1")); |
| | | // if (technologyRouting == null) { |
| | | // throw new ServiceException("未找到该产品规格对应的工艺路线"); |
| | | // } |
| | | // productionOrder.setTechnologyRoutingId(technologyRouting.getId()); |
| | | // } |
| | | if (productionOrder.getTechnologyRoutingId() == null) { |
| | | // 未显式指定工艺路线时,按产品规格选最新一条工艺作为默认路线。 |
| | | TechnologyRouting technologyRouting = technologyRoutingMapper.selectOne( |
| | | Wrappers.<TechnologyRouting>lambdaQuery() |
| | | .eq(TechnologyRouting::getProductModelId, productionOrder.getProductModelId()) |
| | | .orderByDesc(TechnologyRouting::getId) |
| | | .last("limit 1")); |
| | | if (technologyRouting != null) { |
| | | productionOrder.setTechnologyRoutingId(technologyRouting.getId()); |
| | | } |
| | | } |
| | | if (oldOrder != null && ProductOrderStatusEnum.isStarted(oldOrder.getStatus())) { |
| | | // 开工后只允许修正非核心字段,核心生产依据锁定。 |
| | | if (!Objects.equals(oldOrder.getProductModelId(), productionOrder.getProductModelId()) |
| | | || !Objects.equals(oldOrder.getTechnologyRoutingId(), productionOrder.getTechnologyRoutingId()) |
| | | || compareDecimal(oldOrder.getQuantity(), productionOrder.getQuantity()) != 0) { |
| | | throw new ServiceException("生产订单已开工,不能修改产品、工艺路线或数量"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void fillFromSalesLedgerProduct(ProductionOrder productionOrder) { |
| | | if (productionOrder.getSalesLedgerProductId() == null) { |
| | | return; |
| | | } |
| | | // 销售明细是订单来源时,以销售明细为准回填销售台账、产品规格和默认数量。 |
| | | SalesLedgerProduct salesLedgerProduct = salesLedgerProductMapper.selectById(productionOrder.getSalesLedgerProductId().longValue()); |
| | | if (salesLedgerProduct == null) { |
| | | throw new ServiceException("销售台账产品不存在"); |
| | | } |
| | | if (productionOrder.getSalesLedgerId() == null) { |
| | | productionOrder.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId()); |
| | | } else if (!Objects.equals(productionOrder.getSalesLedgerId(), salesLedgerProduct.getSalesLedgerId())) { |
| | | throw new ServiceException("销售台账ID与销售台账产品不一致"); |
| | | } |
| | | if (productionOrder.getProductModelId() == null) { |
| | | productionOrder.setProductModelId(salesLedgerProduct.getProductModelId()); |
| | | } else if (!Objects.equals(productionOrder.getProductModelId(), salesLedgerProduct.getProductModelId())) { |
| | | throw new ServiceException("产品规格ID与销售台账产品不一致"); |
| | | } |
| | | if (productionOrder.getQuantity() == null || productionOrder.getQuantity().compareTo(BigDecimal.ZERO) <= 0) { |
| | | productionOrder.setQuantity(salesLedgerProduct.getQuantity()); |
| | | } |
| | | if (productionOrder.getPlanCompleteTime() == null && productionOrder.getSalesLedgerId() != null) { |
| | | SalesLedger salesLedger = salesLedgerMapper.selectById(productionOrder.getSalesLedgerId()); |
| | | if (salesLedger != null && salesLedger.getDeliveryDate() != null) { |
| | | productionOrder.setPlanCompleteTime(salesLedger.getDeliveryDate()); |
| | | } |
| | | } |
| | | } |
| | |
| | | productionOrder.setProductionPlanIds(formatPlanIds(planIds)); |
| | | } |
| | | |
| | | private void syncProductionPlanIssueStatus(ProductionOrder oldOrder, ProductionOrder newOrder) { |
| | | // 只处理本次增量变化,避免无关计划被重复写状态。 |
| | | Set<Long> oldIds = new LinkedHashSet<>(parsePlanIds(oldOrder == null ? null : oldOrder.getProductionPlanIds())); |
| | | Set<Long> newIds = new LinkedHashSet<>(parsePlanIds(newOrder == null ? null : newOrder.getProductionPlanIds())); |
| | | Set<Long> toRelease = new LinkedHashSet<>(oldIds); |
| | | toRelease.removeAll(newIds); |
| | | Set<Long> toIssue = new LinkedHashSet<>(newIds); |
| | | toIssue.removeAll(oldIds); |
| | | if (!toRelease.isEmpty()) { |
| | | updatePlanIssuedFlag(new ArrayList<>(toRelease), false); |
| | | } |
| | | if (!toIssue.isEmpty()) { |
| | | updatePlanIssuedFlag(new ArrayList<>(toIssue), true); |
| | | } |
| | | } |
| | | |
| | | private void releaseProductionPlanIssueStatus(ProductionOrder productionOrder) { |
| | | if (productionOrder == null) { |
| | | return; |
| | | } |
| | | List<Long> planIds = parsePlanIds(productionOrder.getProductionPlanIds()); |
| | | if (!planIds.isEmpty()) { |
| | | updatePlanIssuedFlag(planIds, false); |
| | | // 生产订单删除--对应的生产计划的已下发数量要减去 |
| | | updatePlanIssuedFlag(planIds, productionOrder.getQuantity()); |
| | | } |
| | | } |
| | | |
| | | private void updatePlanIssuedFlag(List<Long> planIds, boolean issued) { |
| | | //生产订单删除,生产计划的已下发数量对应变更 |
| | | private void updatePlanIssuedFlag(List<Long> planIds, BigDecimal remainingAssignedQuantity) { |
| | | if (planIds == null || planIds.isEmpty()) { |
| | | return; |
| | | } |
| | | List<ProductionPlan> plans = productionPlanMapper.selectBatchIds(planIds); |
| | | //下发数量减去 |
| | | List<ProductionPlan> updates = new ArrayList<>(); |
| | | for (ProductionPlan plan : plans) { |
| | | BigDecimal requiredQuantity = Optional.ofNullable(plan.getQtyRequired()).orElse(BigDecimal.ZERO); |
| | | if (requiredQuantity.compareTo(BigDecimal.ZERO) < 0) { |
| | | requiredQuantity = BigDecimal.ZERO; |
| | | } |
| | | BigDecimal remainingQuantity = resolveRemainingQuantity(plan); |
| | | BigDecimal historicalIssuedQuantity = requiredQuantity.subtract(remainingQuantity); |
| | | BigDecimal issuedQuantity = remainingAssignedQuantity.min(historicalIssuedQuantity); |
| | | remainingAssignedQuantity = remainingAssignedQuantity.subtract(issuedQuantity); |
| | | BigDecimal totalIssuedQuantity = historicalIssuedQuantity.subtract(issuedQuantity); |
| | | int planStatus = resolvePlanStatus(requiredQuantity, totalIssuedQuantity); |
| | | ProductionPlan update = new ProductionPlan(); |
| | | update.setId(plan.getId()); |
| | | update.setIssued(issued); |
| | | productionPlanMapper.updateById(update); |
| | | update.setStatus(planStatus); |
| | | update.setQuantityIssued(totalIssuedQuantity); |
| | | updates.add(update); |
| | | } |
| | | if (!updates.isEmpty()) { |
| | | productionPlanMapper.updateById(updates); |
| | | } |
| | | } |
| | | |
| | | private BigDecimal resolveRemainingQuantity(ProductionPlan plan) { |
| | | if (plan == null) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | BigDecimal requiredQuantity = Optional.ofNullable(plan.getQtyRequired()).orElse(BigDecimal.ZERO); |
| | | if (requiredQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | BigDecimal issuedQuantity = Optional.ofNullable(plan.getQuantityIssued()).orElse(BigDecimal.ZERO); |
| | | if (issuedQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return requiredQuantity; |
| | | } |
| | | if (issuedQuantity.compareTo(requiredQuantity) >= 0) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | return requiredQuantity.subtract(issuedQuantity); |
| | | } |
| | | |
| | | private int resolvePlanStatus(BigDecimal requiredQuantity, BigDecimal issuedQuantity) { |
| | | if (requiredQuantity == null || requiredQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return 0; |
| | | } |
| | | if (issuedQuantity == null || issuedQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return 0; |
| | | } |
| | | return issuedQuantity.compareTo(requiredQuantity) < 0 ? 1 : 2; |
| | | } |
| | | |
| | | private void upsertOrderPick(ProductionOrder productionOrder) { |
| | |
| | | } else { |
| | | productionOrderPickMapper.updateById(orderPick); |
| | | } |
| | | } |
| | | |
| | | private BigDecimal resolveBomDemandQuantity(TechnologyBomStructure source, BigDecimal orderQuantity) { |
| | | // 工艺 BOM 中的需求量按“单件需求 * 订单数量”展开成订单级需求。 |
| | | BigDecimal baseQuantity = source.getDemandedQuantity() != null ? source.getDemandedQuantity() : source.getUnitQuantity(); |
| | | baseQuantity = baseQuantity == null ? BigDecimal.ZERO : baseQuantity; |
| | | if (baseQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | return baseQuantity.multiply(orderQuantity); |
| | | } |
| | | |
| | | private List<Long> parsePlanIds(String productionPlanIds) { |