| | |
| | | package com.ruoyi.production.service.impl; |
| | | |
| | | import cn.hutool.core.bean.BeanUtil; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | 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.ProductionOrderDto; |
| | | import com.ruoyi.production.bean.dto.ProductionPlanImportDto; |
| | | import com.ruoyi.production.bean.vo.ProductionPlanVo; |
| | | import com.ruoyi.production.enums.ProductOrderStatusEnum; |
| | | 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.ProductionOrderService; |
| | | import com.ruoyi.production.service.ProductionPlanService; |
| | | 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 jakarta.servlet.http.HttpServletResponse; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.Comparator; |
| | | import java.util.ArrayList; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public class ProductionPlanServiceImpl extends ServiceImpl<ProductionPlanMapper, ProductionPlan> implements ProductionPlanService { |
| | | |
| | | private final ProductionOrderService productionOrderService; |
| | | private final SalesLedgerMapper salesLedgerMapper; |
| | | private final SalesLedgerProductMapper salesLedgerProductMapper; |
| | | private final ProductionPlanMapper productionPlanMapper; |
| | | |
| | | private final ProductionOrderMapper productionOrderMapper; |
| | | |
| | | @Override |
| | | public IPage<ProductionPlanVo> listPage(Page<ProductionPlanDto> page, ProductionPlanDto productionPlanDto) { |
| | | Page<ProductionPlan> entityPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal()); |
| | | return this.page(entityPage, buildQueryWrapper(productionPlanDto)) |
| | | .convert(item -> BeanUtil.copyProperties(item, ProductionPlanVo.class)); |
| | | |
| | | IPage<ProductionPlanVo> planVoIPage = productionPlanMapper.listPage(page, productionPlanDto) |
| | | .convert(dto -> { |
| | | ProductionPlanVo vo = new ProductionPlanVo(); |
| | | BeanUtils.copyProperties(dto, vo); |
| | | return vo; |
| | | }); |
| | | return planVoIPage; |
| | | } |
| | | |
| | | /** |
| | | * 合并生产计划 |
| | | */ |
| | | @Override |
| | | public void loadProdData() { |
| | | // 用销售明细作为来源同步生产计划,source 字段承担幂等去重作用。 |
| | | List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectList( |
| | | Wrappers.<SalesLedgerProduct>lambdaQuery() |
| | | .isNotNull(SalesLedgerProduct::getProductModelId) |
| | | .gt(SalesLedgerProduct::getQuantity, BigDecimal.ZERO)); |
| | | for (SalesLedgerProduct salesLedgerProduct : salesLedgerProducts) { |
| | | String source = buildSalesSource(salesLedgerProduct.getId()); |
| | | long exists = this.count(Wrappers.<ProductionPlan>lambdaQuery().eq(ProductionPlan::getSource, source)); |
| | | if (exists > 0) { |
| | | continue; |
| | | } |
| | | SalesLedger salesLedger = salesLedgerMapper.selectById(salesLedgerProduct.getSalesLedgerId()); |
| | | ProductionPlan productionPlan = new ProductionPlan(); |
| | | productionPlan.setMpsNo(generateNextPlanNo()); |
| | | productionPlan.setProductModelId(salesLedgerProduct.getProductModelId()); |
| | | productionPlan.setQtyRequired(defaultDecimal(salesLedgerProduct.getQuantity())); |
| | | productionPlan.setRequiredDate(resolveDeliveryTime(salesLedger)); |
| | | productionPlan.setPromisedDeliveryDate(resolveDeliveryTime(salesLedger)); |
| | | productionPlan.setSource(source); |
| | | productionPlan.setIssued(Boolean.FALSE); |
| | | productionPlan.setState("1"); |
| | | this.save(productionPlan); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void syncProdDataJob() { |
| | | loadProdData(); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public boolean combine(ProductionPlanDto productionPlanDto) { |
| | | // 多个计划合并转单后,仍统一走生产订单保存逻辑,避免两套下单流程不一致。 |
| | | if (productionPlanDto == null || productionPlanDto.getIds() == null || productionPlanDto.getIds().isEmpty()) { |
| | | throw new ServiceException("请选择生产计划"); |
| | | } |
| | | List<ProductionPlan> productionPlans = this.listByIds(productionPlanDto.getIds()); |
| | | if (productionPlans.size() != productionPlanDto.getIds().size()) { |
| | | throw new ServiceException("部分生产计划不存在"); |
| | | } |
| | | if (productionPlans.stream().anyMatch(item -> Boolean.TRUE.equals(item.getIssued()))) { |
| | | throw new ServiceException("已下发的生产计划不能重复转单"); |
| | | } |
| | | Long productModelId = productionPlans.stream() |
| | | .map(ProductionPlan::getProductModelId) |
| | | .distinct() |
| | | .reduce((left, right) -> { |
| | | throw new ServiceException("仅支持相同产品规格的生产计划合并转单"); |
| | | }) |
| | | .orElseThrow(() -> new ServiceException("生产计划缺少产品规格")); |
| | | BigDecimal totalRequiredQuantity = productionPlans.stream() |
| | | .map(ProductionPlan::getQtyRequired) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | BigDecimal assignedQuantity = defaultDecimal(productionPlanDto.getTotalAssignedQuantity()); |
| | | if (assignedQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | assignedQuantity = totalRequiredQuantity; |
| | | } |
| | | if (assignedQuantity.compareTo(totalRequiredQuantity) > 0) { |
| | | throw new ServiceException("下发数量不能大于计划总需求数量"); |
| | | } |
| | | |
| | | ProductionOrder productionOrder = new ProductionOrder(); |
| | | productionOrder.setProductModelId(productModelId); |
| | | productionOrder.setQuantity(assignedQuantity); |
| | | productionOrder.setProductionPlanIds(formatPlanIds(productionPlans)); |
| | | productionOrder.setPlanCompleteTime(productionPlanDto.getPlanCompleteTime() != null |
| | | ? productionPlanDto.getPlanCompleteTime() |
| | | : resolveEarliestPlanDate(productionPlans)); |
| | | productionOrder.setStrength(productionPlanDto.getStrength()); |
| | | return productionOrderService.saveProductionOrder(productionOrder); |
| | | } |
| | | |
| | | @Override |
| | | public boolean add(ProductionPlanDto productionPlanDto) { |
| | | // 手工建计划时补齐默认编号和状态,保证后续可以直接下发转单。 |
| | | ProductionPlan productionPlan = BeanUtil.copyProperties(productionPlanDto, ProductionPlan.class); |
| | | validateProductionPlan(productionPlan, false); |
| | | if (productionPlan.getMpsNo() == null || productionPlan.getMpsNo().trim().isEmpty()) { |
| | | productionPlan.setMpsNo(generateNextPlanNo()); |
| | | } |
| | | if (productionPlan.getIssued() == null) { |
| | | productionPlan.setIssued(Boolean.FALSE); |
| | | } |
| | | if (productionPlan.getState() == null || productionPlan.getState().trim().isEmpty()) { |
| | | productionPlan.setState("1"); |
| | | } |
| | | return this.save(productionPlan); |
| | | } |
| | | |
| | | @Override |
| | | public boolean update(ProductionPlanDto productionPlanDto) { |
| | | // 已下发计划的核心字段不能再变更,否则会和已生成订单脱节。 |
| | | if (productionPlanDto == null || productionPlanDto.getId() == null) { |
| | | throw new ServiceException("生产计划ID不能为空"); |
| | | } |
| | | ProductionPlan current = this.getById(productionPlanDto.getId()); |
| | | if (current == null) { |
| | | throw new ServiceException("生产计划不存在"); |
| | | } |
| | | if (Boolean.TRUE.equals(current.getIssued()) && hasPlanCoreChanges(current, productionPlanDto)) { |
| | | throw new ServiceException("已下发的生产计划不允许修改产品、数量和交期"); |
| | | } |
| | | ProductionPlan update = BeanUtil.copyProperties(productionPlanDto, ProductionPlan.class); |
| | | validateProductionPlan(update, true); |
| | | return this.updateById(update); |
| | | } |
| | | |
| | | @Override |
| | | public boolean delete(List<Long> ids) { |
| | | // 删除前校验 issued,避免把已转单计划直接从源头删掉。 |
| | | if (ids == null || ids.isEmpty()) { |
| | | if (productionPlanDto.getIds() == null || productionPlanDto.getIds().isEmpty()) { |
| | | return false; |
| | | } |
| | | List<ProductionPlan> productionPlans = this.listByIds(ids); |
| | | if (productionPlans.stream().anyMatch(item -> Boolean.TRUE.equals(item.getIssued()))) { |
| | | throw new ServiceException("已下发的生产计划不允许删除"); |
| | | |
| | | // 查询主生产计划 |
| | | List<ProductionPlanDto> plans = productionPlanMapper.selectWithMaterialByIds(productionPlanDto.getIds()); |
| | | |
| | | if (plans == null || plans.isEmpty()) { |
| | | throw new ServiceException("下发失败,生产计划不存在"); |
| | | } |
| | | return this.removeByIds(ids); |
| | | |
| | | // 校验是否存在不同的产品名称 |
| | | 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()); |
| | | productionOrder.setStatus(ProductOrderStatusEnum.WAIT.getCode()); |
| | | productionOrder.setStrength(productionPlanDto.getStrength()); |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public void importProdData(MultipartFile file) { |
| | | @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); |
| | | } |
| | | |
| | | private LambdaQueryWrapper<ProductionPlan> buildQueryWrapper(ProductionPlanDto dto) { |
| | | ProductionPlan query = dto == null ? new ProductionPlan() : dto; |
| | | return Wrappers.<ProductionPlan>lambdaQuery() |
| | | .eq(query.getId() != null, ProductionPlan::getId, query.getId()) |
| | | .eq(query.getProductModelId() != null, ProductionPlan::getProductModelId, query.getProductModelId()) |
| | | .eq(query.getIssued() != null, ProductionPlan::getIssued, query.getIssued()) |
| | | .eq(query.getState() != null && !query.getState().trim().isEmpty(), ProductionPlan::getState, query.getState()) |
| | | .eq(query.getIsAudit() != null && !query.getIsAudit().trim().isEmpty(), ProductionPlan::getIsAudit, query.getIsAudit()) |
| | | .like(query.getMpsNo() != null && !query.getMpsNo().trim().isEmpty(), ProductionPlan::getMpsNo, query.getMpsNo()) |
| | | .like(query.getSource() != null && !query.getSource().trim().isEmpty(), ProductionPlan::getSource, query.getSource()) |
| | | .orderByDesc(ProductionPlan::getId); |
| | | List<ProductionPlanImportDto> exportList = new ArrayList<>(); |
| | | for (ProductionPlan entity : list) { |
| | | ProductionPlanImportDto dto = new ProductionPlanImportDto(); |
| | | BeanUtils.copyProperties(entity, dto); |
| | | exportList.add(dto); |
| | | } |
| | | |
| | | private void validateProductionPlan(ProductionPlan productionPlan, boolean update) { |
| | | if (productionPlan == null) { |
| | | throw new ServiceException("生产计划不能为空"); |
| | | } |
| | | if (update && productionPlan.getId() == null) { |
| | | throw new ServiceException("生产计划ID不能为空"); |
| | | } |
| | | if (productionPlan.getProductModelId() == null) { |
| | | throw new ServiceException("productModelId不能为空"); |
| | | } |
| | | if (defaultDecimal(productionPlan.getQtyRequired()).compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw new ServiceException("qtyRequired必须大于0"); |
| | | } |
| | | } |
| | | |
| | | private boolean hasPlanCoreChanges(ProductionPlan current, ProductionPlanDto update) { |
| | | // 这些字段会直接影响转单结果,用来判断是否属于核心变更。 |
| | | return !Objects.equals(current.getProductModelId(), update.getProductModelId()) |
| | | || defaultDecimal(current.getQtyRequired()).compareTo(defaultDecimal(update.getQtyRequired())) != 0 |
| | | || !Objects.equals(toLocalDate(current.getPromisedDeliveryDate()), update.getPlanCompleteTime()) |
| | | || !Objects.equals(toLocalDate(current.getRequiredDate()), toLocalDate(update.getRequiredDate())); |
| | | } |
| | | |
| | | private String generateNextPlanNo() { |
| | | // 编号按日期递增生成,方便和订单、工单统一追踪。 |
| | | String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); |
| | | String prefix = "MPS" + datePrefix; |
| | | ProductionPlan latestPlan = this.getOne(Wrappers.<ProductionPlan>lambdaQuery() |
| | | .likeRight(ProductionPlan::getMpsNo, prefix) |
| | | .orderByDesc(ProductionPlan::getMpsNo) |
| | | .last("limit 1")); |
| | | int sequence = 1; |
| | | if (latestPlan != null && latestPlan.getMpsNo() != null && latestPlan.getMpsNo().startsWith(prefix)) { |
| | | try { |
| | | sequence = Integer.parseInt(latestPlan.getMpsNo().substring(prefix.length())) + 1; |
| | | } catch (NumberFormatException ignored) { |
| | | sequence = 1; |
| | | } |
| | | } |
| | | return prefix + String.format("%04d", sequence); |
| | | } |
| | | |
| | | private String formatPlanIds(List<ProductionPlan> productionPlans) { |
| | | return productionPlans.stream() |
| | | .map(ProductionPlan::getId) |
| | | .distinct() |
| | | .map(String::valueOf) |
| | | .collect(Collectors.joining(",", "[", "]")); |
| | | } |
| | | |
| | | private LocalDate resolveEarliestPlanDate(List<ProductionPlan> productionPlans) { |
| | | return productionPlans.stream() |
| | | .map(this::resolvePlanDate) |
| | | .filter(Objects::nonNull) |
| | | .min(Comparator.naturalOrder()) |
| | | .orElse(null); |
| | | } |
| | | |
| | | private LocalDate resolvePlanDate(ProductionPlan productionPlan) { |
| | | if (productionPlan.getPromisedDeliveryDate() != null) { |
| | | return productionPlan.getPromisedDeliveryDate().toLocalDate(); |
| | | } |
| | | if (productionPlan.getRequiredDate() != null) { |
| | | return productionPlan.getRequiredDate().toLocalDate(); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private LocalDateTime resolveDeliveryTime(SalesLedger salesLedger) { |
| | | if (salesLedger == null || salesLedger.getDeliveryDate() == null) { |
| | | return null; |
| | | } |
| | | return salesLedger.getDeliveryDate().atStartOfDay(); |
| | | } |
| | | |
| | | private String buildSalesSource(Long salesLedgerProductId) { |
| | | return "salesLedgerProduct:" + salesLedgerProductId; |
| | | } |
| | | |
| | | private LocalDate toLocalDate(LocalDateTime value) { |
| | | return value == null ? null : value.toLocalDate(); |
| | | } |
| | | |
| | | private BigDecimal defaultDecimal(BigDecimal value) { |
| | | return value == null ? BigDecimal.ZERO : value; |
| | | ExcelUtil<ProductionPlanImportDto> util = new ExcelUtil<>(ProductionPlanImportDto.class); |
| | | util.exportExcel(response, exportList, "销售生产需求数据"); |
| | | } |
| | | } |