package com.ruoyi.production.service.impl;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.common.exception.base.BaseException;
|
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.bean.BeanUtils;
|
import com.ruoyi.common.utils.poi.ExcelUtil;
|
import com.ruoyi.production.bean.dto.ProductionPlanDto;
|
import com.ruoyi.production.bean.dto.ProductionPlanImportDto;
|
import com.ruoyi.production.bean.vo.ProductionPlanVo;
|
import com.ruoyi.production.mapper.ProductionOrderMapper;
|
import com.ruoyi.production.mapper.ProductionPlanMapper;
|
import com.ruoyi.production.pojo.ProductionOrder;
|
import com.ruoyi.production.pojo.ProductionPlan;
|
import com.ruoyi.production.service.ProductionPlanService;
|
import jakarta.servlet.http.HttpServletResponse;
|
import lombok.RequiredArgsConstructor;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.web.multipart.MultipartFile;
|
|
import java.time.LocalDateTime;
|
import java.util.ArrayList;
|
import java.util.HashSet;
|
import java.util.List;
|
import java.util.Set;
|
import java.util.stream.Collectors;
|
|
@Service
|
@RequiredArgsConstructor
|
public class ProductionPlanServiceImpl extends ServiceImpl<ProductionPlanMapper, ProductionPlan> implements ProductionPlanService {
|
|
private final ProductionPlanMapper productionPlanMapper;
|
|
private final ProductionOrderMapper productionOrderMapper;
|
|
@Override
|
public IPage<ProductionPlanVo> listPage(Page<ProductionPlanDto> page, ProductionPlanDto productionPlanDto) {
|
return productionPlanMapper.listPage(page, productionPlanDto);
|
}
|
|
/**
|
* 合并生产计划
|
*/
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public boolean combine(ProductionPlanDto productionPlanDto) {
|
if (productionPlanDto.getIds() == null || productionPlanDto.getIds().isEmpty()) {
|
return false;
|
}
|
|
// 查询主生产计划
|
List<ProductionPlanDto> plans = productionPlanMapper.selectWithMaterialByIds(productionPlanDto.getIds());
|
|
if (plans == null || plans.isEmpty()) {
|
throw new ServiceException("下发失败,生产计划不存在");
|
}
|
|
// 校验是否存在不同的产品名称
|
String firstProductName = plans.get(0).getProductName();
|
if (plans.stream().anyMatch(p -> p.getProductName() == null || !p.getProductName().equals(firstProductName))) {
|
throw new BaseException("合并失败,存在不同的产品名称");
|
}
|
|
// 校验是否存在不同的产品规格
|
String firstProductSpec = plans.get(0).getModel();
|
if (plans.stream().anyMatch(p -> p.getModel() == null || !p.getModel().equals(firstProductSpec))) {
|
throw new BaseException("合并失败,存在不同的产品规格");
|
}
|
|
// 创建生产订单
|
ProductionOrder productionOrder = new ProductionOrder();
|
productionOrder.setQuantity(productionPlanDto.getTotalAssignedQuantity());
|
productionOrder.setPlanCompleteTime(productionPlanDto.getPlanCompleteTime());
|
// // 叠加剩余方数
|
// BigDecimal totalRemainingVolume = plans.stream()
|
// .map(ProductionPlan::getRemainingVolume)
|
// .filter(Objects::nonNull)
|
// .reduce(BigDecimal.ZERO, BigDecimal::add);
|
// // 判断下发数量是否大于等于剩余方数
|
// if (productionPlanDto.getTotalAssignedQuantity().compareTo(totalRemainingVolume) > 0) {
|
// throw new BaseException("操作失败,下发数量不能大于剩余方数");
|
// }
|
//
|
// // 创建生产订单
|
// ProductOrder productOrder = new ProductOrder();
|
// productOrder.setQuantity(productionPlanDto.getTotalAssignedQuantity());
|
// productOrder.setPlanCompleteTime(productionPlanDto.getPlanCompleteTime());
|
// productOrder.setStatus(ProductOrderStatusEnum.WAIT.getCode());
|
// productOrder.setStrength(productionPlanDto.getStrength());
|
// productOrder.setProductMaterialSkuId(plans.get(0).getProductMaterialSkuId());
|
//
|
// Long orderId = productOrderService.insertProductOrder(productOrder);
|
//
|
// // 当下发的产品为砌块或板材,就拉取BOM子集与工艺路线子集数据存入到附表中
|
// if ("砌块".equals(productionPlanDto.getProductName())) {
|
// productOrder.setRouteId(productionOrderAppendixService.populateBlocks(orderId, productionPlanDto));
|
// }
|
// if ("板材".equals(productionPlanDto.getProductName())) {
|
// productOrder.setRouteId(productionOrderAppendixService.populatePlates(orderId, productionPlanDto));
|
// }
|
// // 更新绑定的工艺路线
|
// productOrderService.updateById(productOrder);
|
//
|
// // 根据下发数量,从第一个生产计划开始分配方数
|
// BigDecimal assignedVolume = BigDecimal.ZERO;
|
// for (ProductionPlan plan : plans) {
|
// BigDecimal volume = plan.getVolume();
|
// if (volume == null) {
|
// continue;
|
// }
|
// // 计算剩余方数
|
// BigDecimal remainingVolume = plan.getRemainingVolume();
|
// if (remainingVolume.compareTo(BigDecimal.ZERO) <= 0) {
|
// continue;
|
// }
|
//
|
// ProductOrderPlan productOrderPlan = new ProductOrderPlan();
|
// productOrderPlan.setProductOrderId(productOrder.getId());
|
// productOrderPlan.setProductionPlanId(plan.getId());
|
//
|
// if (assignedVolume.add(remainingVolume).compareTo(productionPlanDto.getTotalAssignedQuantity()) >= 0) {
|
// // 最后一个计划,分配剩余方数
|
// BigDecimal lastRemainingVolume = productionPlanDto.getTotalAssignedQuantity().subtract(assignedVolume);
|
// BigDecimal assignedQuantity = Optional.ofNullable(plan.getAssignedQuantity()).orElse(BigDecimal.ZERO).add(lastRemainingVolume);
|
// plan.setAssignedQuantity(assignedQuantity);
|
// plan.setStatus(assignedQuantity.compareTo(plan.getVolume()) >= 0 ? 2 : 1);
|
// productOrderPlan.setAssignedQuantity(lastRemainingVolume);
|
// productionPlanMapper.updateById(plan);
|
// productOrderPlanMapper.insert(productOrderPlan);
|
// break;
|
// }
|
//
|
// // 分配当前计划方数
|
// BigDecimal assignedQuantity = Optional.ofNullable(plan.getAssignedQuantity()).orElse(BigDecimal.ZERO).add(remainingVolume);
|
// plan.setAssignedQuantity(assignedQuantity);
|
// plan.setStatus(assignedQuantity.compareTo(plan.getVolume()) >= 0 ? 2 : 1);
|
// productOrderPlan.setAssignedQuantity(remainingVolume);
|
// // 更新生产计划
|
// productionPlanMapper.updateById(plan);
|
// // 创建关联关系
|
// productOrderPlanMapper.insert(productOrderPlan);
|
// assignedVolume = assignedVolume.add(remainingVolume);
|
// }
|
//
|
// for (ProductionPlan plan : plans) {
|
// BigDecimal assignedQuantity = Optional.ofNullable(plan.getAssignedQuantity()).orElse(BigDecimal.ZERO);
|
// BigDecimal volume = Optional.ofNullable(plan.getVolume()).orElse(BigDecimal.ZERO);
|
// if (assignedQuantity.compareTo(BigDecimal.ZERO) <= 0) {
|
// plan.setStatus(0);
|
// } else if (assignedQuantity.compareTo(volume) >= 0) {
|
// plan.setStatus(2);
|
// } else {
|
// plan.setStatus(1);
|
// }
|
// productionPlanMapper.updateById(plan);
|
// }
|
return true;
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public boolean add(ProductionPlanDto dto) {
|
if (StringUtils.isBlank(dto.getApplyNo())) {
|
throw new ServiceException("新增失败,申请单编号不能为空");
|
}
|
checkApplyNoUnique(dto.getApplyNo(), null);
|
|
dto.setStatus(0);
|
return productionPlanMapper.insert(dto) > 0;
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public boolean update(ProductionPlanDto dto) {
|
if (dto == null || dto.getId() == null) {
|
throw new ServiceException("编辑失败,数据不能为空");
|
}
|
|
ProductionPlan old = getById(dto.getId());
|
if (old == null) {
|
throw new ServiceException("编辑失败,主生产计划不存在");
|
}
|
|
// 状态校验
|
if (old.getStatus() != 0) {
|
throw new BaseException("编辑失败,该生产计划已下发或部分下发状态,禁止编辑");
|
}
|
|
// applyNo变更才校验
|
if (StringUtils.isNotBlank(dto.getApplyNo())
|
&& !dto.getApplyNo().equals(old.getApplyNo())) {
|
checkApplyNoUnique(dto.getApplyNo(), dto.getId());
|
}
|
|
return productionPlanMapper.updateById(dto) > 0;
|
}
|
|
private void checkApplyNoUnique(String applyNo, Long excludeId) {
|
LambdaQueryWrapper<ProductionPlan> wrapper = Wrappers.lambdaQuery();
|
wrapper.eq(ProductionPlan::getApplyNo, applyNo);
|
|
if (excludeId != null) {
|
wrapper.ne(ProductionPlan::getId, excludeId);
|
}
|
|
if (productionPlanMapper.selectCount(wrapper) > 0) {
|
throw new ServiceException("申请单编号 " + applyNo + " 已存在");
|
}
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public boolean delete(List<Long> ids) {
|
// 如果存在已下发的计划,则不能删除
|
if (productionPlanMapper.selectList(Wrappers.<ProductionPlan>lambdaQuery().in(ProductionPlan::getId, ids)).stream().anyMatch(p -> p.getStatus() == 1 || p.getStatus() == 2)) {
|
throw new BaseException("删除失败,存在已下发或部分下发的计划");
|
}
|
// 如果有关联订单,则不能删除
|
if (productionOrderMapper.selectList(Wrappers.<ProductionOrder>lambdaQuery().in(ProductionOrder::getProductionPlanIds, ids)).stream().anyMatch(p -> p.getId() != null)) {
|
throw new BaseException("删除失败,存在关联订单");
|
}
|
|
return productionPlanMapper.deleteBatchIds(ids) > 0;
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void importProdData(MultipartFile file) {
|
if (file == null || file.isEmpty()) {
|
throw new ServiceException("导入数据不能为空");
|
}
|
ExcelUtil<ProductionPlanImportDto> excelUtil = new ExcelUtil<>(ProductionPlanImportDto.class);
|
List<ProductionPlanImportDto> list;
|
try {
|
list = excelUtil.importExcel(file.getInputStream());
|
} catch (Exception e) {
|
log.error("生产需求Excel导入失败", e);
|
throw new ServiceException("Excel解析失败");
|
}
|
|
if (list == null || list.isEmpty()) {
|
throw new ServiceException("Excel没有数据");
|
}
|
|
Set<String> applyNos = new HashSet<>();
|
Set<String> materialCodes = new HashSet<>();
|
for (int i = 0; i < list.size(); i++) {
|
ProductionPlanImportDto dto = list.get(i);
|
String applyNo = dto.getApplyNo();
|
String materialCode = dto.getMaterialCode();
|
|
if (StringUtils.isEmpty(applyNo)) {
|
throw new ServiceException("导入失败:第 " + (i + 2) + " 行申请单编号不能为空");
|
}
|
if (!applyNos.add(applyNo)) {
|
throw new ServiceException("导入失败:Excel 中存在重复的申请单编号: " + applyNo);
|
}
|
if (StringUtils.isEmpty(materialCode)) {
|
throw new ServiceException("导入失败:第 " + (i + 2) + " 行物料编码不能为空");
|
}
|
|
String strength = dto.getStrength();
|
if (StringUtils.isNotEmpty(strength)) {
|
if (!"A3.5".equals(strength) && !"A5.0".equals(strength)) {
|
throw new ServiceException("导入失败:第 " + (i + 2) + " 行强度只能是 A3.5 或 A5.0");
|
}
|
}
|
|
materialCodes.add(materialCode);
|
}
|
|
// 申请单编号是否已存在
|
Long existApplyNoCount = baseMapper.selectCount(Wrappers.<ProductionPlan>lambdaQuery()
|
.in(ProductionPlan::getApplyNo, applyNos));
|
if (existApplyNoCount > 0) {
|
List<String> existApplyNos = baseMapper.selectList(Wrappers.<ProductionPlan>lambdaQuery()
|
.in(ProductionPlan::getApplyNo, applyNos))
|
.stream().map(ProductionPlan::getApplyNo).collect(Collectors.toList());
|
throw new ServiceException("导入失败,申请单编号已存在: " + String.join(", ", existApplyNos));
|
}
|
|
LocalDateTime now = LocalDateTime.now();
|
List<ProductionPlan> entityList = list.stream().map(dto -> {
|
ProductionPlan entity = new ProductionPlan();
|
BeanUtils.copyProperties(dto, entity);
|
entity.setStatus(0);
|
entity.setCreateTime(now);
|
entity.setUpdateTime(now);
|
return entity;
|
}).collect(Collectors.toList());
|
|
this.saveBatch(entityList);
|
}
|
|
@Override
|
public void exportProdData(HttpServletResponse response, List<Long> ids) {
|
List<ProductionPlan> list;
|
if (ids != null && !ids.isEmpty()) {
|
list = baseMapper.selectBatchIds(ids);
|
} else {
|
list = baseMapper.selectList(null);
|
}
|
|
List<ProductionPlanImportDto> exportList = new ArrayList<>();
|
for (ProductionPlan entity : list) {
|
ProductionPlanImportDto dto = new ProductionPlanImportDto();
|
BeanUtils.copyProperties(entity, dto);
|
exportList.add(dto);
|
}
|
ExcelUtil<ProductionPlanImportDto> util = new ExcelUtil<>(ProductionPlanImportDto.class);
|
util.exportExcel(response, exportList, "销售生产需求数据");
|
}
|
}
|