| | |
| | | import com.ruoyi.sales.mapper.*; |
| | | import com.ruoyi.sales.pojo.*; |
| | | import com.ruoyi.sales.service.ISalesLedgerProcessRouteService; |
| | | import com.ruoyi.sales.service.ISalesLedgerProcessRouteRecordService; |
| | | import com.ruoyi.sales.service.ISalesLedgerProductProcessBindService; |
| | | import com.ruoyi.sales.service.ISalesLedgerProductProcessService; |
| | | import com.ruoyi.sales.service.ISalesLedgerService; |
| | |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.io.IOException; |
| | | import java.io.InputStream; |
| | | import java.lang.reflect.Field; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.net.URLEncoder; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Path; |
| | | import java.nio.file.Paths; |
| | |
| | | private final ISalesLedgerProductProcessBindService salesLedgerProductProcessBindService; |
| | | |
| | | private final ISalesLedgerProcessRouteService salesLedgerProcessRouteService; |
| | | private final ISalesLedgerProcessRouteRecordService salesLedgerProcessRouteRecordService; |
| | | |
| | | private final StockInventoryService stockInventoryService; |
| | | private final StockInRecordMapper stockInRecordMapper; |
| | |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void saleProcessBind(SalesLedgerProcessRoute salesLedgerProcessRoute) { |
| | | if (salesLedgerProcessRoute == null) { |
| | | public void saleProcessBind(SalesLedgerProcessRouteDto salesLedgerProcessRouteDto) { |
| | | if (salesLedgerProcessRouteDto == null) { |
| | | throw new ServiceException("绑定失败,数据不能为空"); |
| | | } |
| | | |
| | | SalesLedger salesLedger = baseMapper.selectById(salesLedgerProcessRoute.getSalesLedgerId()); |
| | | SalesLedger salesLedger = baseMapper.selectById(salesLedgerProcessRouteDto.getSalesLedgerId()); |
| | | if (salesLedger == null) { |
| | | throw new ServiceException("绑定失败,销售订单不存在"); |
| | | } |
| | | ProcessRoute processRoute = processRouteMapper.selectById(salesLedgerProcessRoute.getProcessRouteId()); |
| | | ProcessRoute processRoute = processRouteMapper.selectById(salesLedgerProcessRouteDto.getProcessRouteId()); |
| | | if (processRoute == null) { |
| | | throw new ServiceException("绑定失败,工艺路线不存在"); |
| | | } |
| | | // 清除已绑定的数据 |
| | | salesLedgerProcessRouteService.remove(new LambdaQueryWrapper<SalesLedgerProcessRoute>().eq(SalesLedgerProcessRoute::getSalesLedgerId, salesLedger.getId())); |
| | | salesLedgerProcessRouteRecordService.remove(new LambdaQueryWrapper<SalesLedgerProcessRouteRecord>().eq(SalesLedgerProcessRouteRecord::getSalesLedgerId, salesLedger.getId())); |
| | | |
| | | // 将数据迁移到sales_ledger_process_route |
| | | List<ProcessRouteItem> routeItems = processRouteItemMapper.selectList(new LambdaQueryWrapper<ProcessRouteItem>().eq(ProcessRouteItem::getRouteId, processRoute.getId())); |
| | |
| | | salesLedgerProcessRouteList.add(ledgerProcessRoute); |
| | | } |
| | | salesLedgerProcessRouteService.saveBatch(salesLedgerProcessRouteList); |
| | | |
| | | List<SalesLedgerProcessRoute> savedRoutes = salesLedgerProcessRouteService.list(new LambdaQueryWrapper<SalesLedgerProcessRoute>() |
| | | .eq(SalesLedgerProcessRoute::getSalesLedgerId, salesLedger.getId()) |
| | | .eq(SalesLedgerProcessRoute::getProcessRouteId, processRoute.getId())); |
| | | Map<Long, SalesLedgerProcessRoute> routeMap = savedRoutes.stream() |
| | | .filter(item -> item.getProcessRouteItemId() != null) |
| | | .collect(Collectors.toMap(SalesLedgerProcessRoute::getProcessRouteItemId, item -> item, (a, b) -> a)); |
| | | |
| | | Map<Long, SalesLedgerProcessRouteRecord> inputRecordMap = new HashMap<>(); |
| | | if (CollectionUtils.isNotEmpty(salesLedgerProcessRouteDto.getRecordList())) { |
| | | for (SalesLedgerProcessRouteRecord record : salesLedgerProcessRouteDto.getRecordList()) { |
| | | if (record != null && record.getProcessRouteItemId() != null) { |
| | | inputRecordMap.put(record.getProcessRouteItemId(), record); |
| | | } |
| | | } |
| | | } |
| | | |
| | | List<SalesLedgerProcessRouteRecord> routeRecordList = new ArrayList<>(); |
| | | for (ProcessRouteItem routeItem : routeItems) { |
| | | SalesLedgerProcessRoute route = routeMap.get(routeItem.getId()); |
| | | if (route == null || route.getId() == null) { |
| | | continue; |
| | | } |
| | | SalesLedgerProcessRouteRecord inputRecord = inputRecordMap.get(routeItem.getId()); |
| | | SalesLedgerProcessRouteRecord record = new SalesLedgerProcessRouteRecord(); |
| | | record.setSalesLedgerId(salesLedger.getId()); |
| | | record.setSalesLedgerProcessRouteId(route.getId()); |
| | | Integer isCompleted = inputRecord != null && inputRecord.getIsCompleted() != null ? inputRecord.getIsCompleted() : 0; |
| | | record.setIsCompleted(isCompleted); |
| | | record.setCompletedTime(Objects.equals(isCompleted, 1) ? LocalDateTime.now() : null); |
| | | routeRecordList.add(record); |
| | | } |
| | | salesLedgerProcessRouteRecordService.saveBatch(routeRecordList); |
| | | } |
| | | |
| | | /** |
| | |
| | | public SalesLedgerProcessRouteDto salesProcess(Long salesLedgerId) { |
| | | SalesLedgerProcessRouteDto dto = new SalesLedgerProcessRouteDto(); |
| | | List<SalesLedgerProcessRoute> list = baseMapper.selectSalesProcess(salesLedgerId); |
| | | List<SalesLedgerProcessRouteRecord> recordList = salesLedgerProcessRouteRecordService.list( |
| | | new LambdaQueryWrapper<SalesLedgerProcessRouteRecord>() |
| | | .eq(SalesLedgerProcessRouteRecord::getSalesLedgerId, salesLedgerId) |
| | | .orderByAsc(SalesLedgerProcessRouteRecord::getId) |
| | | ); |
| | | if (CollectionUtils.isNotEmpty(list)) { |
| | | Long processRouteId = list.get(0).getProcessRouteId(); |
| | | ProcessRoute processRoute = processRouteMapper.selectById(processRouteId); |
| | |
| | | } |
| | | } |
| | | dto.setList(list); |
| | | if (CollectionUtils.isNotEmpty(list) && CollectionUtils.isNotEmpty(recordList)) { |
| | | Map<Long, Long> routeItemIdMap = list.stream() |
| | | .filter(item -> item.getId() != null && item.getProcessRouteItemId() != null) |
| | | .collect(Collectors.toMap(SalesLedgerProcessRoute::getId, SalesLedgerProcessRoute::getProcessRouteItemId, (a, b) -> a)); |
| | | recordList.forEach(record -> { |
| | | if (record != null && record.getSalesLedgerProcessRouteId() != null) { |
| | | record.setProcessRouteItemId(routeItemIdMap.get(record.getSalesLedgerProcessRouteId())); |
| | | } |
| | | }); |
| | | } |
| | | dto.setRecordList(recordList); |
| | | return dto; |
| | | } |
| | | |
| | |
| | | * 2. 复制原销售台账及产品数据,生成新的台账 |
| | | * 3. 对原台账的库存数据进行反向操作 |
| | | */ |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public List<Long> counterReview(CounterReviewDto dto) { |
| | | if (dto == null || CollectionUtils.isEmpty(dto.getIds())) { |
| | | throw new ServiceException("请选择要反审核的订单"); |
| | | } |
| | | if (dto.getCounterReviewType() == null || (dto.getCounterReviewType() != 1 && dto.getCounterReviewType() != 2)) { |
| | | throw new ServiceException("请选择反审核类型:作废或重新生成"); |
| | | } |
| | | if (dto.getCounterReviewDesc() == null || dto.getCounterReviewDesc().trim().isEmpty()) { |
| | | throw new ServiceException("请输入反审核描述"); |
| | | } |
| | | |
| | | LoginUser loginUser = SecurityUtils.getLoginUser(); |
| | | List<Long> newLedgerIds = new ArrayList<>(); |
| | | |
| | | for (Long id : dto.getIds()) { |
| | | SalesLedger originalLedger = salesLedgerMapper.selectById(id); |
| | | if (originalLedger == null) { |
| | | throw new ServiceException("订单不存在,无法反审核"); |
| | | } |
| | | if (originalLedger.getReviewStatus() == null || originalLedger.getReviewStatus() != 1) { |
| | | throw new ServiceException("订单" + originalLedger.getSalesContractNo() + "不是已审核状态,无法反审核"); |
| | | } |
| | | |
| | | // 1. 标记原订单为已反审 |
| | | originalLedger.setReviewStatus(2); |
| | | originalLedger.setCounterReviewTime(LocalDateTime.now()); |
| | | originalLedger.setCounterReviewPerson(loginUser.getUser().getNickName()); |
| | | originalLedger.setCounterReviewPersonId(loginUser.getUserId()); |
| | | originalLedger.setCounterReviewType(dto.getCounterReviewType()); |
| | | originalLedger.setCounterReviewDesc(dto.getCounterReviewDesc()); |
| | | salesLedgerMapper.updateById(originalLedger); |
| | | |
| | | // 2. 作废库存:入库扣减、出库增加、删除记录 |
| | | processOriginalOrderStock(id); |
| | | |
| | | // 3. 清除质检记录 |
| | | clearQualityInspectRecords(id); |
| | | |
| | | // 4. 清除发货信息和发货审批记录 |
| | | clearShippingAndApprovalRecords(id); |
| | | |
| | | // 5. 取消审批流程 |
| | | cancelApproveProcesses(id, originalLedger.getSalesContractNo()); |
| | | |
| | | // 6. 重新生成:创建新台账副本 |
| | | if (dto.getCounterReviewType() == 2) { |
| | | SalesLedger newLedger = new SalesLedger(); |
| | | BeanUtils.copyProperties(originalLedger, newLedger); |
| | | newLedger.setId(null); |
| | | newLedger.setSalesContractNo(generateSalesContractNo()); |
| | | newLedger.setDeliveryStatus(1); |
| | | newLedger.setStockStatus(0); |
| | | newLedger.setReviewStatus(0); |
| | | newLedger.setCounterReviewTime(null); |
| | | newLedger.setCounterReviewPerson(null); |
| | | newLedger.setCounterReviewPersonId(null); |
| | | newLedger.setCounterReviewType(null); |
| | | newLedger.setCounterReviewDesc(null); |
| | | salesLedgerMapper.insert(newLedger); |
| | | |
| | | // 复制产品到新台账 |
| | | List<SalesLedgerProduct> originalProducts = salesLedgerProductMapper.selectList( |
| | | Wrappers.<SalesLedgerProduct>lambdaQuery() |
| | | .eq(SalesLedgerProduct::getSalesLedgerId, id) |
| | | ); |
| | | for (SalesLedgerProduct originalProduct : originalProducts) { |
| | | SalesLedgerProduct newProduct = new SalesLedgerProduct(); |
| | | BeanUtils.copyProperties(originalProduct, newProduct); |
| | | newProduct.setId(null); |
| | | newProduct.setSalesLedgerId(newLedger.getId()); |
| | | newProduct.setStockedQuantity(BigDecimal.ZERO); |
| | | newProduct.setShippedQuantity(BigDecimal.ZERO); |
| | | newProduct.setUnqualifiedStockedQuantity(BigDecimal.ZERO); |
| | | newProduct.setUnqualifiedShippedQuantity(BigDecimal.ZERO); |
| | | newProduct.setReturnQuality(BigDecimal.ZERO); |
| | | newProduct.setAvailableQuality(newProduct.getQuantity().subtract(newProduct.getReturnQuality())); |
| | | newProduct.setProductStockStatus(0); |
| | | newProduct.fillRemainingQuantity(); |
| | | salesLedgerProductMapper.insert(newProduct); |
| | | } |
| | | newLedgerIds.add(newLedger.getId()); |
| | | } |
| | | } |
| | | return newLedgerIds; |
| | | } |
| | | |
| | | /** |
| | | * 旧版反审处理(兼容 addOrUpdateSalesLedger 中 reviewStatus=2 的调用) |
| | | */ |
| | | private void handleCounterReview(SalesLedger salesLedger) { |
| | | // 1. 设置反审相关信息 |
| | | LoginUser loginUser = SecurityUtils.getLoginUser(); |
| | |
| | | .eq(StockOutRecord::getSalesLedgerId, originalSalesLedgerId) |
| | | ); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void markOrderCompleted(List<Long> ids) { |
| | | if (CollectionUtils.isEmpty(ids)) { |
| | | throw new ServiceException("请选择要标记完成的订单"); |
| | | } |
| | | for (Long id : ids) { |
| | | SalesLedger ledger = salesLedgerMapper.selectById(id); |
| | | if (ledger == null) { |
| | | throw new ServiceException("订单不存在,无法标记完成"); |
| | | } |
| | | if (ledger.getReviewStatus() == null || ledger.getReviewStatus() != 1) { |
| | | throw new ServiceException("订单" + ledger.getSalesContractNo() + "不是已审核状态,无法标记完成"); |
| | | } |
| | | if (ledger.getOrderStatus() != null && ledger.getOrderStatus() == 1) { |
| | | throw new ServiceException("订单" + ledger.getSalesContractNo() + "已完成,无需重复标记"); |
| | | } |
| | | } |
| | | salesLedgerMapper.update(null, |
| | | Wrappers.<SalesLedger>lambdaUpdate() |
| | | .in(SalesLedger::getId, ids) |
| | | .set(SalesLedger::getOrderStatus, 1) |
| | | ); |
| | | } |
| | | |
| | | @Override |
| | | public void incrementPrintCount(Long id, String printType) { |
| | | if (id == null) { |
| | | throw new ServiceException("销售台账ID不能为空"); |
| | | } |
| | | if (printType == null || (!"label".equals(printType) && !"document".equals(printType))) { |
| | | throw new ServiceException("打印类型必须为 label 或 document"); |
| | | } |
| | | SalesLedger ledger = salesLedgerMapper.selectById(id); |
| | | if (ledger == null) { |
| | | throw new ServiceException("销售台账不存在"); |
| | | } |
| | | if ("label".equals(printType)) { |
| | | int currentCount = ledger.getLabelPrintCount() == null ? 0 : ledger.getLabelPrintCount(); |
| | | salesLedgerMapper.update(null, |
| | | Wrappers.<SalesLedger>lambdaUpdate() |
| | | .eq(SalesLedger::getId, id) |
| | | .set(SalesLedger::getLabelPrintCount, currentCount + 1) |
| | | ); |
| | | } else { |
| | | int currentCount = ledger.getDocumentPrintCount() == null ? 0 : ledger.getDocumentPrintCount(); |
| | | salesLedgerMapper.update(null, |
| | | Wrappers.<SalesLedger>lambdaUpdate() |
| | | .eq(SalesLedger::getId, id) |
| | | .set(SalesLedger::getDocumentPrintCount, currentCount + 1) |
| | | ); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void exportWithProducts(HttpServletResponse response, SalesLedgerDto salesLedgerDto) { |
| | | try { |
| | | // 1. 查询销售台账列表(导出使用升序排序) |
| | | Page<SalesLedger> page = new Page<>(-1, -1); |
| | | // 使用 Wrappers 构建升序查询 |
| | | LambdaQueryWrapper<SalesLedger> queryWrapper = Wrappers.<SalesLedger>lambdaQuery() |
| | | .orderByAsc(SalesLedger::getEntryDate) |
| | | .orderByAsc(SalesLedger::getId); |
| | | |
| | | // 添加查询条件 |
| | | if (salesLedgerDto.getCustomerName() != null && !salesLedgerDto.getCustomerName().isEmpty()) { |
| | | queryWrapper.like(SalesLedger::getCustomerName, salesLedgerDto.getCustomerName()); |
| | | } |
| | | if (salesLedgerDto.getSalesContractNo() != null && !salesLedgerDto.getSalesContractNo().isEmpty()) { |
| | | queryWrapper.like(SalesLedger::getSalesContractNo, salesLedgerDto.getSalesContractNo()); |
| | | } |
| | | if (salesLedgerDto.getProjectName() != null && !salesLedgerDto.getProjectName().isEmpty()) { |
| | | queryWrapper.like(SalesLedger::getProjectName, salesLedgerDto.getProjectName()); |
| | | } |
| | | if (salesLedgerDto.getEntryDateStart() != null && !salesLedgerDto.getEntryDateStart().isEmpty()) { |
| | | queryWrapper.ge(SalesLedger::getEntryDate, salesLedgerDto.getEntryDateStart()); |
| | | } |
| | | if (salesLedgerDto.getEntryDateEnd() != null && !salesLedgerDto.getEntryDateEnd().isEmpty()) { |
| | | queryWrapper.le(SalesLedger::getEntryDate, salesLedgerDto.getEntryDateEnd()); |
| | | } |
| | | if (salesLedgerDto.getDeliveryStatus() != null) { |
| | | queryWrapper.eq(SalesLedger::getDeliveryStatus, salesLedgerDto.getDeliveryStatus()); |
| | | } |
| | | if (salesLedgerDto.getStockStatus() != null) { |
| | | queryWrapper.eq(SalesLedger::getStockStatus, salesLedgerDto.getStockStatus()); |
| | | } |
| | | if (salesLedgerDto.getReviewStatus() != null) { |
| | | queryWrapper.eq(SalesLedger::getReviewStatus, salesLedgerDto.getReviewStatus()); |
| | | } |
| | | if (salesLedgerDto.getOrderStatus() != null) { |
| | | queryWrapper.eq(SalesLedger::getOrderStatus, salesLedgerDto.getOrderStatus()); |
| | | } |
| | | if (salesLedgerDto.getReviewStatusList() != null && !salesLedgerDto.getReviewStatusList().isEmpty()) { |
| | | queryWrapper.and(w -> w.in(SalesLedger::getReviewStatus, salesLedgerDto.getReviewStatusList()) |
| | | .or().isNull(SalesLedger::getReviewStatus)); |
| | | } |
| | | |
| | | IPage<SalesLedger> ledgerPage = salesLedgerMapper.selectPage(page, queryWrapper); |
| | | List<SalesLedger> ledgerList = ledgerPage.getRecords(); |
| | | |
| | | // 2. 收集数据 |
| | | List<SalesLedgerExportDto> ledgerExportList = new ArrayList<>(); |
| | | List<SalesLedgerProductExportDto> productExportList = new ArrayList<>(); |
| | | |
| | | for (SalesLedger ledger : ledgerList) { |
| | | // 转换台账数据 |
| | | SalesLedgerExportDto ledgerDto = new SalesLedgerExportDto(); |
| | | ledgerDto.setSalesContractNo(ledger.getSalesContractNo()); |
| | | ledgerDto.setCustomerContractNo(ledger.getCustomerContractNo()); |
| | | ledgerDto.setProjectName(ledger.getProjectName()); |
| | | ledgerDto.setCustomerName(ledger.getCustomerName()); |
| | | ledgerDto.setSalesman(ledger.getSalesman()); |
| | | ledgerDto.setEntryPersonName(ledger.getEntryPersonName()); |
| | | ledgerDto.setEntryDate(ledger.getEntryDate()); |
| | | ledgerDto.setExecutionDate(ledger.getExecutionDate() != null ? |
| | | java.sql.Date.valueOf(ledger.getExecutionDate()) : null); |
| | | ledgerDto.setDeliveryDate(ledger.getDeliveryDate() != null ? |
| | | java.sql.Date.valueOf(ledger.getDeliveryDate()) : null); |
| | | ledgerDto.setContractAmount(ledger.getContractAmount()); |
| | | ledgerDto.setRemarks(ledger.getRemarks()); |
| | | ledgerDto.setCustomerRemarks(ledger.getCustomerRemarks()); |
| | | ledgerDto.setDeliveryStatusText(getDeliveryStatusText(ledger.getDeliveryStatus())); |
| | | ledgerDto.setStockStatusText(getStockStatusText(ledger.getStockStatus())); |
| | | ledgerDto.setReviewStatusText(getReviewStatusText(ledger.getReviewStatus())); |
| | | ledgerDto.setOrderStatusText(getOrderStatusText(ledger.getOrderStatus())); |
| | | ledgerExportList.add(ledgerDto); |
| | | |
| | | // 查询该台账的产品列表 |
| | | List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList( |
| | | Wrappers.<SalesLedgerProduct>lambdaQuery() |
| | | .eq(SalesLedgerProduct::getSalesLedgerId, ledger.getId()) |
| | | .eq(SalesLedgerProduct::getType, 1) |
| | | ); |
| | | |
| | | for (SalesLedgerProduct product : products) { |
| | | SalesLedgerProductExportDto productDto = new SalesLedgerProductExportDto(); |
| | | productDto.setSalesContractNo(ledger.getSalesContractNo()); |
| | | productDto.setProductCategory(product.getProductCategory()); |
| | | productDto.setSpecificationModel(product.getSpecificationModel()); |
| | | productDto.setThickness(product.getThickness()); |
| | | productDto.setFloorCode(product.getFloorCode()); |
| | | productDto.setWidth(product.getWidth()); |
| | | productDto.setHeight(product.getHeight()); |
| | | productDto.setQuantity(product.getQuantity()); |
| | | productDto.setSettlePieceArea(product.getSettlePieceArea()); |
| | | productDto.setSettleTotalArea(product.getSettleTotalArea()); |
| | | productDto.setTaxInclusiveUnitPrice(product.getTaxInclusiveUnitPrice()); |
| | | productDto.setTaxRate(product.getTaxRate()); |
| | | productDto.setTaxInclusiveTotalPrice(product.getTaxInclusiveTotalPrice()); |
| | | productDto.setTaxExclusiveTotalPrice(product.getTaxExclusiveTotalPrice()); |
| | | productDto.setInvoiceType(product.getInvoiceType()); |
| | | productDto.setProcessRequirement(product.getProcessRequirement()); |
| | | productDto.setRemark(product.getRemark()); |
| | | productExportList.add(productDto); |
| | | } |
| | | } |
| | | |
| | | // 3. 使用ExcelUtil导出(内部会创建Workbook并写入响应) |
| | | // 先创建临时文件,再合并两个sheet |
| | | response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); |
| | | response.setCharacterEncoding("utf-8"); |
| | | String fileName = URLEncoder.encode("销售台账.xlsx", "utf-8").replaceAll("\\+", "%20"); |
| | | response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName); |
| | | |
| | | org.apache.poi.xssf.usermodel.XSSFWorkbook workbook = new org.apache.poi.xssf.usermodel.XSSFWorkbook(); |
| | | |
| | | // Sheet1: 销售台账 - 手动填充 |
| | | fillSheetWithData(workbook, "销售台账", SalesLedgerExportDto.class, ledgerExportList); |
| | | |
| | | // Sheet2: 产品明细 - 手动填充 |
| | | fillSheetWithData(workbook, "产品明细", SalesLedgerProductExportDto.class, productExportList); |
| | | |
| | | workbook.write(response.getOutputStream()); |
| | | workbook.close(); |
| | | |
| | | } catch (Exception e) { |
| | | log.error("导出销售台账失败", e); |
| | | throw new ServiceException("导出失败:" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void exportProcessRoute(HttpServletResponse response, List<Long> salesLedgerIds, String completedTimeStart, String completedTimeEnd) { |
| | | try { |
| | | if (CollectionUtils.isEmpty(salesLedgerIds)) { |
| | | throw new ServiceException("请选择要导出的销售台账"); |
| | | } |
| | | LocalDateTime startTime = parseCompletedTime(completedTimeStart); |
| | | LocalDateTime endTime = parseCompletedTime(completedTimeEnd); |
| | | if (startTime == null && endTime == null) { |
| | | startTime = LocalDate.now().atStartOfDay(); |
| | | endTime = LocalDateTime.now(); |
| | | } else { |
| | | if (startTime == null) { |
| | | startTime = LocalDate.now().atStartOfDay(); |
| | | } |
| | | if (endTime == null) { |
| | | endTime = LocalDateTime.now(); |
| | | } |
| | | } |
| | | |
| | | LambdaQueryWrapper<SalesLedgerProcessRouteRecord> queryWrapper = Wrappers.<SalesLedgerProcessRouteRecord>lambdaQuery() |
| | | .eq(SalesLedgerProcessRouteRecord::getIsCompleted, 1) |
| | | .ge(SalesLedgerProcessRouteRecord::getCompletedTime, startTime) |
| | | .le(SalesLedgerProcessRouteRecord::getCompletedTime, endTime) |
| | | .orderByAsc(SalesLedgerProcessRouteRecord::getCompletedTime) |
| | | .orderByAsc(SalesLedgerProcessRouteRecord::getId); |
| | | |
| | | if (CollectionUtils.isNotEmpty(salesLedgerIds)) { |
| | | queryWrapper.in(SalesLedgerProcessRouteRecord::getSalesLedgerId, salesLedgerIds); |
| | | } |
| | | |
| | | List<SalesLedgerProcessRouteRecord> completedRoutes = salesLedgerProcessRouteRecordService.list(queryWrapper); |
| | | Map<Long, SalesLedger> salesLedgerMap = Collections.emptyMap(); |
| | | Map<Long, SalesLedgerProcessRoute> routeMap = Collections.emptyMap(); |
| | | Map<Long, ProcessRouteItem> processRouteItemMap = Collections.emptyMap(); |
| | | Map<Long, List<SalesLedgerProduct>> productMap = Collections.emptyMap(); |
| | | |
| | | if (CollectionUtils.isNotEmpty(completedRoutes)) { |
| | | List<Long> routeSalesLedgerIds = completedRoutes.stream() |
| | | .map(SalesLedgerProcessRouteRecord::getSalesLedgerId) |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | List<Long> salesLedgerProcessRouteIds = completedRoutes.stream() |
| | | .map(SalesLedgerProcessRouteRecord::getSalesLedgerProcessRouteId) |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | |
| | | if (CollectionUtils.isNotEmpty(routeSalesLedgerIds)) { |
| | | salesLedgerMap = salesLedgerMapper.selectBatchIds(routeSalesLedgerIds).stream() |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toMap(SalesLedger::getId, item -> item, (a, b) -> a)); |
| | | List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(new LambdaQueryWrapper<SalesLedgerProduct>() |
| | | .in(SalesLedgerProduct::getSalesLedgerId, routeSalesLedgerIds) |
| | | .eq(SalesLedgerProduct::getType, 1) |
| | | .orderByAsc(SalesLedgerProduct::getSalesLedgerId) |
| | | .orderByAsc(SalesLedgerProduct::getId)); |
| | | productMap = products.stream().collect(Collectors.groupingBy( |
| | | SalesLedgerProduct::getSalesLedgerId, |
| | | LinkedHashMap::new, |
| | | Collectors.toList() |
| | | )); |
| | | } |
| | | |
| | | if (CollectionUtils.isNotEmpty(salesLedgerProcessRouteIds)) { |
| | | routeMap = salesLedgerProcessRouteService.listByIds(salesLedgerProcessRouteIds).stream() |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toMap(SalesLedgerProcessRoute::getId, item -> item, (a, b) -> a)); |
| | | List<Long> processRouteItemIds = routeMap.values().stream() |
| | | .map(SalesLedgerProcessRoute::getProcessRouteItemId) |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | if (CollectionUtils.isNotEmpty(processRouteItemIds)) { |
| | | processRouteItemMap = processRouteItemMapper.selectBatchIds(processRouteItemIds).stream() |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toMap(ProcessRouteItem::getId, item -> item, (a, b) -> a)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | Map<Long, List<SalesLedgerProcessRouteRecord>> routeGroupMap = new LinkedHashMap<>(); |
| | | for (SalesLedgerProcessRouteRecord record : completedRoutes) { |
| | | SalesLedgerProcessRoute route = routeMap.get(record.getSalesLedgerProcessRouteId()); |
| | | if (route == null || route.getProcessRouteItemId() == null) { |
| | | continue; |
| | | } |
| | | routeGroupMap.computeIfAbsent(route.getProcessRouteItemId(), k -> new ArrayList<>()).add(record); |
| | | } |
| | | |
| | | final Map<Long, ProcessRouteItem> finalProcessRouteItemMap = processRouteItemMap; |
| | | LinkedHashMap<String, List<List<Object>>> sheetMap = new LinkedHashMap<>(); |
| | | List<Long> orderedProcessRouteItemIds = routeGroupMap.keySet().stream() |
| | | .sorted((left, right) -> compareProcessRouteItem(left, right, finalProcessRouteItemMap)) |
| | | .collect(Collectors.toList()); |
| | | |
| | | for (Long processRouteItemId : orderedProcessRouteItemIds) { |
| | | ProcessRouteItem processRouteItem = finalProcessRouteItemMap.get(processRouteItemId); |
| | | String sheetName = buildUniqueSheetName(sheetMap, processRouteItem, processRouteItemId); |
| | | List<List<Object>> sheetData = new ArrayList<>(); |
| | | sheetData.add(buildProcessRouteHeader()); |
| | | |
| | | for (SalesLedgerProcessRouteRecord route : routeGroupMap.getOrDefault(processRouteItemId, Collections.emptyList())) { |
| | | SalesLedger salesLedger = salesLedgerMap.get(route.getSalesLedgerId()); |
| | | if (salesLedger == null) { |
| | | continue; |
| | | } |
| | | List<SalesLedgerProduct> products = productMap.getOrDefault(salesLedger.getId(), Collections.emptyList()); |
| | | if (CollectionUtils.isEmpty(products)) { |
| | | sheetData.add(buildProcessRouteRow(salesLedger, null, route)); |
| | | continue; |
| | | } |
| | | for (SalesLedgerProduct product : products) { |
| | | sheetData.add(buildProcessRouteRow(salesLedger, product, route)); |
| | | } |
| | | } |
| | | |
| | | if (sheetData.size() == 1) { |
| | | sheetData.add(Arrays.asList("", "", "", "", "", "", "", "", "")); |
| | | } |
| | | sheetMap.put(sheetName, sheetData); |
| | | } |
| | | |
| | | if (sheetMap.isEmpty()) { |
| | | List<List<Object>> sheetData = new ArrayList<>(); |
| | | sheetData.add(buildProcessRouteHeader()); |
| | | sheetData.add(Arrays.asList("", "", "", "", "", "", "")); |
| | | sheetMap.put("工艺路线", sheetData); |
| | | } |
| | | |
| | | com.ruoyi.common.utils.excel.ExcelUtils.exportManySheet(response, "销售台账工艺路线导出", sheetMap); |
| | | } catch (Exception e) { |
| | | log.error("导出售后台账工艺路线失败", e); |
| | | throw new ServiceException("导出售后台账工艺路线失败:" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | private LocalDateTime parseCompletedTime(String value) { |
| | | if (!StringUtils.hasText(value)) { |
| | | return null; |
| | | } |
| | | String text = value.trim(); |
| | | try { |
| | | return LocalDateTime.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); |
| | | } catch (Exception ex) { |
| | | try { |
| | | return LocalDateTime.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); |
| | | } catch (Exception ignored) { |
| | | return LocalDate.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atStartOfDay(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private int compareProcessRouteItem(Long left, Long right, Map<Long, ProcessRouteItem> processRouteItemMap) { |
| | | ProcessRouteItem leftItem = processRouteItemMap.get(left); |
| | | ProcessRouteItem rightItem = processRouteItemMap.get(right); |
| | | int leftSort = leftItem != null && leftItem.getDragSort() != null ? leftItem.getDragSort() : Integer.MAX_VALUE; |
| | | int rightSort = rightItem != null && rightItem.getDragSort() != null ? rightItem.getDragSort() : Integer.MAX_VALUE; |
| | | if (leftSort != rightSort) { |
| | | return Integer.compare(leftSort, rightSort); |
| | | } |
| | | String leftName = leftItem != null && StringUtils.hasText(leftItem.getProcessName()) ? leftItem.getProcessName() : ""; |
| | | String rightName = rightItem != null && StringUtils.hasText(rightItem.getProcessName()) ? rightItem.getProcessName() : ""; |
| | | int nameCompare = leftName.compareTo(rightName); |
| | | if (nameCompare != 0) { |
| | | return nameCompare; |
| | | } |
| | | return Long.compare(left, right); |
| | | } |
| | | |
| | | private List<Object> buildProcessRouteHeader() { |
| | | return Arrays.asList("日期", "订单编号", "客户名称", "规格", "数量", "面积", "是否是工程"); |
| | | } |
| | | |
| | | private List<Object> buildProcessRouteRow(SalesLedger salesLedger, SalesLedgerProduct product, SalesLedgerProcessRouteRecord route) { |
| | | List<Object> row = new ArrayList<>(); |
| | | row.add(salesLedger.getEntryDate() == null ? "" : DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, salesLedger.getEntryDate())); |
| | | row.add(salesLedger.getSalesContractNo()); |
| | | row.add(salesLedger.getCustomerName()); |
| | | row.add(product == null ? "" : product.getSpecificationModel()); |
| | | row.add(product == null ? "" : product.getQuantity()); |
| | | row.add(product == null ? "" : resolveExportArea(product)); |
| | | row.add(""); |
| | | return row; |
| | | } |
| | | |
| | | private BigDecimal resolveExportArea(SalesLedgerProduct product) { |
| | | if (product == null) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | if (product.getSettleTotalArea() != null) { |
| | | return product.getSettleTotalArea(); |
| | | } |
| | | if (product.getActualTotalArea() != null) { |
| | | return product.getActualTotalArea(); |
| | | } |
| | | BigDecimal qty = product.getQuantity() == null ? BigDecimal.ONE : product.getQuantity(); |
| | | if (product.getSettlePieceArea() != null) { |
| | | return product.getSettlePieceArea().multiply(qty).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | if (product.getActualPieceArea() != null) { |
| | | return product.getActualPieceArea().multiply(qty).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | if (product.getWidth() != null && product.getHeight() != null) { |
| | | BigDecimal area = product.getWidth().multiply(product.getHeight()) |
| | | .divide(new BigDecimal("1000000"), 2, RoundingMode.HALF_UP); |
| | | return area.multiply(qty).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | return BigDecimal.ZERO; |
| | | } |
| | | |
| | | private String buildUniqueSheetName(Map<String, List<List<Object>>> sheetMap, ProcessRouteItem processRouteItem, Long processRouteItemId) { |
| | | String baseName = processRouteItem != null && StringUtils.hasText(processRouteItem.getProcessName()) |
| | | ? processRouteItem.getProcessName() |
| | | : "工序" + processRouteItemId; |
| | | baseName = sanitizeSheetName(baseName); |
| | | String sheetName = baseName; |
| | | int suffix = 2; |
| | | while (sheetMap.containsKey(sheetName)) { |
| | | String suffixText = "_" + suffix++; |
| | | int maxBaseLength = 31 - suffixText.length(); |
| | | String trimmedBase = baseName.length() > maxBaseLength ? baseName.substring(0, maxBaseLength) : baseName; |
| | | sheetName = trimmedBase + suffixText; |
| | | } |
| | | return sheetName; |
| | | } |
| | | |
| | | private String sanitizeSheetName(String sheetName) { |
| | | if (!StringUtils.hasText(sheetName)) { |
| | | return "工艺路线"; |
| | | } |
| | | String sanitized = sheetName.replaceAll("[\\\\/?*\\[\\]:]", "_").trim(); |
| | | if (sanitized.isEmpty()) { |
| | | sanitized = "工艺路线"; |
| | | } |
| | | return sanitized.length() > 31 ? sanitized.substring(0, 31) : sanitized; |
| | | } |
| | | |
| | | /** |
| | | * 手动填充Sheet数据 |
| | | */ |
| | | private <T> void fillSheetWithData(org.apache.poi.xssf.usermodel.XSSFWorkbook workbook, String sheetName, Class<T> clazz, List<T> dataList) throws Exception { |
| | | org.apache.poi.ss.usermodel.Sheet sheet = workbook.createSheet(sheetName); |
| | | |
| | | // 获取字段上的@Excel注解 |
| | | java.lang.reflect.Field[] fields = clazz.getDeclaredFields(); |
| | | List<java.lang.reflect.Field> excelFields = new ArrayList<>(); |
| | | for (java.lang.reflect.Field field : fields) { |
| | | com.ruoyi.framework.aspectj.lang.annotation.Excel excel = field.getAnnotation(com.ruoyi.framework.aspectj.lang.annotation.Excel.class); |
| | | if (excel != null) { |
| | | excelFields.add(field); |
| | | } |
| | | } |
| | | |
| | | // 创建表头 |
| | | org.apache.poi.ss.usermodel.Row headerRow = sheet.createRow(0); |
| | | org.apache.poi.ss.usermodel.CellStyle headerStyle = workbook.createCellStyle(); |
| | | headerStyle.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.GREY_50_PERCENT.getIndex()); |
| | | headerStyle.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND); |
| | | headerStyle.setAlignment(org.apache.poi.ss.usermodel.HorizontalAlignment.CENTER); |
| | | org.apache.poi.ss.usermodel.Font headerFont = workbook.createFont(); |
| | | headerFont.setBold(true); |
| | | headerStyle.setFont(headerFont); |
| | | |
| | | for (int i = 0; i < excelFields.size(); i++) { |
| | | java.lang.reflect.Field field = excelFields.get(i); |
| | | field.setAccessible(true); |
| | | com.ruoyi.framework.aspectj.lang.annotation.Excel excel = field.getAnnotation(com.ruoyi.framework.aspectj.lang.annotation.Excel.class); |
| | | org.apache.poi.ss.usermodel.Cell cell = headerRow.createCell(i); |
| | | cell.setCellValue(excel.name()); |
| | | cell.setCellStyle(headerStyle); |
| | | sheet.setColumnWidth(i, 20 * 256); |
| | | } |
| | | |
| | | // 创建数据行 |
| | | org.apache.poi.ss.usermodel.CellStyle dataStyle = workbook.createCellStyle(); |
| | | dataStyle.setAlignment(org.apache.poi.ss.usermodel.HorizontalAlignment.CENTER); |
| | | |
| | | for (int rowIndex = 0; rowIndex < dataList.size(); rowIndex++) { |
| | | T data = dataList.get(rowIndex); |
| | | org.apache.poi.ss.usermodel.Row row = sheet.createRow(rowIndex + 1); |
| | | for (int colIndex = 0; colIndex < excelFields.size(); colIndex++) { |
| | | java.lang.reflect.Field field = excelFields.get(colIndex); |
| | | field.setAccessible(true); |
| | | Object value = field.get(data); |
| | | org.apache.poi.ss.usermodel.Cell cell = row.createCell(colIndex); |
| | | if (value == null) { |
| | | cell.setCellValue(""); |
| | | } else if (value instanceof Number) { |
| | | cell.setCellValue(((Number) value).doubleValue()); |
| | | } else if (value instanceof Date) { |
| | | cell.setCellValue(new java.text.SimpleDateFormat("yyyy-MM-dd").format((Date) value)); |
| | | } else { |
| | | cell.setCellValue(value.toString()); |
| | | } |
| | | cell.setCellStyle(dataStyle); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private String getDeliveryStatusText(Integer status) { |
| | | if (status == null) return "未知"; |
| | | switch (status) { |
| | | case 1: return "未发货"; |
| | | case 2: return "审批中"; |
| | | case 3: return "审批不通过"; |
| | | case 4: return "审批通过"; |
| | | case 5: return "已发货"; |
| | | case 6: return "部分发货"; |
| | | default: return "未知"; |
| | | } |
| | | } |
| | | |
| | | private String getStockStatusText(Integer status) { |
| | | if (status == null) return "未知"; |
| | | switch (status) { |
| | | case 0: return "未入库"; |
| | | case 1: return "部分入库"; |
| | | case 2: return "已入库"; |
| | | case 3: return "审批中"; |
| | | default: return "未知"; |
| | | } |
| | | } |
| | | |
| | | private String getReviewStatusText(Integer status) { |
| | | if (status == null) return "待审核"; |
| | | switch (status) { |
| | | case 0: return "待审核"; |
| | | case 1: return "已审核"; |
| | | case 2: return "已反审"; |
| | | default: return "待审核"; |
| | | } |
| | | } |
| | | |
| | | private String getOrderStatusText(Integer status) { |
| | | if (status == null || status == 0) return "进行中"; |
| | | switch (status) { |
| | | case 0: return "进行中"; |
| | | case 1: return "已完成"; |
| | | default: return "进行中"; |
| | | } |
| | | } |
| | | } |