yuan
5 天以前 15f7555b9f9d3e3ae2cdaa82d89bc7ea5160f62c
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
@@ -20,27 +20,24 @@
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.common.enums.FileNameType;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.AmountUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.other.mapper.TempFileMapper;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysDeptMapper;
import com.ruoyi.project.system.mapper.SysUserMapper;
import com.ruoyi.purchase.dto.PurchaseLedgerDto;
import com.ruoyi.purchase.dto.PurchaseLedgerImportDto;
import com.ruoyi.purchase.dto.PurchaseLedgerProductImportDto;
import com.ruoyi.purchase.mapper.PaymentRegistrationMapper;
import com.ruoyi.purchase.mapper.ProductRecordMapper;
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
import com.ruoyi.purchase.mapper.TicketRegistrationMapper;
import com.ruoyi.purchase.pojo.PaymentRegistration;
import com.ruoyi.purchase.pojo.ProductRecord;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.purchase.pojo.TicketRegistration;
import com.ruoyi.purchase.service.IPurchaseLedgerService;
import com.ruoyi.quality.mapper.QualityInspectMapper;
import com.ruoyi.quality.mapper.QualityInspectParamMapper;
@@ -51,11 +48,9 @@
import com.ruoyi.quality.pojo.QualityTestStandard;
import com.ruoyi.quality.pojo.QualityTestStandardParam;
import com.ruoyi.sales.mapper.CommonFileMapper;
import com.ruoyi.sales.mapper.InvoiceRegistrationProductMapper;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.sales.pojo.CommonFile;
import com.ruoyi.sales.pojo.InvoiceRegistrationProduct;
import com.ruoyi.sales.pojo.SalesLedger;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.sales.service.impl.CommonFileServiceImpl;
@@ -64,12 +59,14 @@
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
@@ -95,14 +92,11 @@
    private final SysUserMapper userMapper;
    private final TempFileMapper tempFileMapper;
    private final CommonFileMapper commonFileMapper;
    private final SysDeptMapper sysDeptMapper;
    private final SupplierManageMapper supplierManageMapper;
    private final ProductMapper productMapper;
    private final ProductModelMapper productModelMapper;
    private final SysUserMapper sysUserMapper;
    private final TicketRegistrationMapper ticketRegistrationMapper;
    private final ProductRecordMapper productRecordMapper;
    private final PaymentRegistrationMapper paymentRegistrationMapper;
    private final InvoiceRegistrationProductMapper invoiceRegistrationProductMapper;
    private final StringRedisTemplate redisTemplate;
    private final QualityInspectMapper qualityInspectMapper;
    private final CommonFileServiceImpl commonFileService;
@@ -111,6 +105,7 @@
    private final QualityInspectParamMapper qualityInspectParamMapper;
    private final ApproveProcessServiceImpl approveProcessService;
    private final ProcurementRecordMapper procurementRecordStorageMapper;
    private final PlatformTransactionManager transactionManager;
    private final FileUtil fileUtil;
    @Override
@@ -134,6 +129,7 @@
        PurchaseLedger purchaseLedger = new PurchaseLedger();
        // DTO转Entity
        BeanUtils.copyProperties(purchaseLedgerDto, purchaseLedger);
        AmountUtils.normalizeContractAmount(purchaseLedger);
        SalesLedger salesLedger = salesLedgerMapper.selectById(purchaseLedgerDto.getSalesLedgerId());
        //录入人
        SysUser sysUser = userMapper.selectUserById(purchaseLedgerDto.getRecorderId());
@@ -247,6 +243,7 @@
        // 设置字段
        for (SalesLedgerProduct product : products) {
            AmountUtils.normalizeSalesLedgerProduct(product);
            product.setSalesLedgerId(salesLedgerId);
            Long productId = product.getProductId();
@@ -283,18 +280,15 @@
                LocalDateTime localDateTime = entryDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
                salesLedgerProduct.setRegisterDate(localDateTime);
                salesLedgerProduct.setFutureTickets(salesLedgerProduct.getQuantity());
                salesLedgerProduct.setFutureTicketsAmount(salesLedgerProduct.getTaxInclusiveTotalPrice());
                salesLedgerProduct.setPendingTicketsTotal(salesLedgerProduct.getTaxInclusiveTotalPrice());
                salesLedgerProductMapper.insert(salesLedgerProduct);
            }
        }
        // 计算总含税金额
        BigDecimal totalTaxInclusiveAmount = products.stream()
        BigDecimal totalTaxInclusiveAmount = AmountUtils.scaleAmount(products.stream()
                .map(SalesLedgerProduct::getTaxInclusiveTotalPrice)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
                .reduce(BigDecimal.ZERO, BigDecimal::add));
        // 更新主表的总金额字段
        if (salesLedgerId != null) {
@@ -333,18 +327,6 @@
        queryWrapper.in(SalesLedgerProduct::getSalesLedgerId, ids)
                .eq(SalesLedgerProduct::getType, 2);
        salesLedgerProductMapper.delete(queryWrapper);
        // 批量删除关联的采购台账的来票登记
        LambdaQueryWrapper<TicketRegistration> ticketRegistrationLambdaQueryWrapper = new LambdaQueryWrapper<>();
        ticketRegistrationLambdaQueryWrapper.in(TicketRegistration::getPurchaseLedgerId,ids);
        ticketRegistrationMapper.delete(ticketRegistrationLambdaQueryWrapper);
        // 批量删除关联的采购台账的来票登记记录
        LambdaQueryWrapper<ProductRecord> productRecordLambdaQueryWrapper = new LambdaQueryWrapper<>();
        productRecordLambdaQueryWrapper.in(ProductRecord::getPurchaseLedgerId,ids);
        productRecordMapper.delete(productRecordLambdaQueryWrapper);
        // 批量删除付款登记
        LambdaQueryWrapper<PaymentRegistration> paymentRegistrationLambdaQueryWrapper = new LambdaQueryWrapper<>();
        paymentRegistrationLambdaQueryWrapper.in(PaymentRegistration::getPurchaseLedgerId, ids);
        paymentRegistrationMapper.delete(paymentRegistrationLambdaQueryWrapper);
        //批量删除检验标准
        LambdaQueryWrapper<QualityInspect> materialInspectLambdaQueryWrapper = new LambdaQueryWrapper<>();
        materialInspectLambdaQueryWrapper.in(QualityInspect::getPurchaseLedgerId, ids);
@@ -404,7 +386,9 @@
        // 4. 转换 DTO
        PurchaseLedgerDto resultDto = new PurchaseLedgerDto();
        BeanUtils.copyProperties(purchaseLedger, resultDto);
        AmountUtils.normalizeContractAmount(resultDto);
        if (!products.isEmpty()) {
            AmountUtils.normalizeSalesLedgerProducts(products);
            resultDto.setHasChildren(true);
            resultDto.setProductData(products);
            resultDto.setStorageBlobVOS(fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.FILE, RecordTypeEnum.PURCHASE_LEDGER, purchaseLedger.getId()));
@@ -447,14 +431,7 @@
        queryWrapper.eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId())
                .eq(SalesLedgerProduct::getType, 2);
        List<SalesLedgerProduct> productList = salesLedgerProductMapper.selectList(queryWrapper);
        productList.forEach(product -> {
            product.setFutureTickets(product.getFutureTickets() != null ? product.getFutureTickets() : product.getQuantity());
            product.setFutureTicketsAmount(product.getFutureTicketsAmount() != null ? product.getFutureTicketsAmount() : product.getTaxInclusiveTotalPrice());
            product.setTicketsNum(null);
            product.setTicketsAmount(null);
            product.setTempFutureTickets(product.getFutureTickets());
            product.setTempFutureTicketsAmount(product.getFutureTicketsAmount());
        });
        AmountUtils.normalizeSalesLedgerProducts(productList);
        resultDto.setProductData(productList);
        return resultDto;
    }
@@ -481,35 +458,19 @@
        PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(id);
        BeanUtils.copyProperties(purchaseLedger, purchaseLedgerDto);
//        TicketRegistration ticketRegistration = ticketRegistrationMapper.selectOne(new LambdaQueryWrapper<TicketRegistration>().eq(TicketRegistration::getPurchaseLedgerId, id));
//        if (ticketRegistration != null) {
//            purchaseLedgerDto.setInvoiceNumber(ticketRegistration.getInvoiceNumber());
//            purchaseLedgerDto.setInvoiceAmount(ticketRegistration.getInvoiceAmount());
//            purchaseLedgerDto.setTicketRegistrationId(ticketRegistration.getId());
//        }
        return purchaseLedgerDto;
    }
    @Override
    public IPage<PurchaseLedgerDto> selectPurchaseLedgerListPage(IPage ipage, PurchaseLedgerDto purchaseLedger) {
        IPage<PurchaseLedgerDto> purchaseLedgerDtoIPage = purchaseLedgerMapper.selectPurchaseLedgerListPage(ipage, purchaseLedger);
        purchaseLedgerDtoIPage.getRecords().forEach(purchaseLedgerDto -> {
            List<CommonFile> commonFiles = commonFileMapper.selectList(new LambdaQueryWrapper<CommonFile>().eq(CommonFile::getCommonId, purchaseLedgerDto.getId()).eq(CommonFile::getType, FileNameType.PURCHASE.getValue()));
            purchaseLedgerDto.setSalesLedgerFiles(commonFiles);
        });
        return purchaseLedgerDtoIPage;
    }
    @Override
    public List<InvoiceRegistrationProduct> getProductBySalesNo(Long id) {
        List<InvoiceRegistrationProduct> invoiceRegistrationProducts = invoiceRegistrationProductMapper.selectList(new LambdaQueryWrapper<InvoiceRegistrationProduct>()
                .select(InvoiceRegistrationProduct::getId, InvoiceRegistrationProduct::getProductCategory, InvoiceRegistrationProduct::getSpecificationModel,
                        InvoiceRegistrationProduct::getUnit, InvoiceRegistrationProduct::getQuantity)
                .eq(InvoiceRegistrationProduct::getSalesLedgerId, id));
        if (invoiceRegistrationProducts.isEmpty()) {
            return new ArrayList<>();
        IPage<PurchaseLedgerDto> page = purchaseLedgerMapper.selectPurchaseLedgerListPage(ipage, purchaseLedger);
        if (!CollectionUtils.isEmpty(page.getRecords())) {
            page.getRecords().forEach(record -> {
                AmountUtils.normalizeContractAmount(record);
                AmountUtils.normalizeSalesLedgerProducts(record.getProductData());
            });
        }
        return invoiceRegistrationProducts;
        return page;
    }
    @Override
@@ -533,118 +494,190 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<?> importData(MultipartFile file) {
    public AjaxResult importData(MultipartFile file) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        int successCount = 0;
        int skipCount = 0;
        int failCount = 0;
        List<String> failList = new ArrayList<>();
        List<String> skipList = new ArrayList<>();
        try {
            InputStream inputStream = file.getInputStream();
            ExcelUtil<PurchaseLedgerImportDto> salesLedgerImportDtoExcelUtil = new ExcelUtil<>(PurchaseLedgerImportDto.class);
            Map<String, List<PurchaseLedgerImportDto>> stringListMap = salesLedgerImportDtoExcelUtil.importExcelMultiSheet(Arrays.asList("采购台账数据", "采购产品数据"), inputStream, 0);
            if (CollectionUtils.isEmpty(stringListMap)) return R.fail("采购表格为空!");
            // 业务层合并
            List<PurchaseLedgerImportDto> salesLedgerImportDtoList = stringListMap.get("采购台账数据");
            if (CollectionUtils.isEmpty(salesLedgerImportDtoList)) return R.fail("采购台账数据为空!");
            List<PurchaseLedgerImportDto> salesLedgerProductImportDtoList = stringListMap.get("采购产品数据");
            if (CollectionUtils.isEmpty(salesLedgerProductImportDtoList)) return R.fail("采购产品数据为空!");
            // 供应商数据
            List<SupplierManage> customers = supplierManageMapper.selectList(new LambdaQueryWrapper<SupplierManage>().in(SupplierManage::getSupplierName,
                    salesLedgerImportDtoList.stream().map(PurchaseLedgerImportDto::getSupplierName).collect(Collectors.toList())));
            List<Map<String,Object>> list = productModelMapper.getProductAndModelList();
            // 录入人数据
            List<SysUser> sysUsers = sysUserMapper.selectList(new LambdaQueryWrapper<SysUser>().in(SysUser::getNickName,
                    salesLedgerImportDtoList.stream().map(PurchaseLedgerImportDto::getRecorderName).collect(Collectors.toList())));
            for (PurchaseLedgerImportDto salesLedgerImportDto : salesLedgerImportDtoList) {
                PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectOne(new LambdaQueryWrapper<PurchaseLedger>()
                        .eq(PurchaseLedger::getPurchaseContractNumber, salesLedgerImportDto.getPurchaseContractNumber())
            Map<String, List<PurchaseLedgerImportDto>> stringListMap = salesLedgerImportDtoExcelUtil.importExcelMultiSheet(
                    Arrays.asList("采购台账数据", "采购产品数据"), inputStream, 0);
            if (CollectionUtils.isEmpty(stringListMap)) return AjaxResult.error("采购表格为空!");
            List<PurchaseLedgerImportDto> ledgerList = stringListMap.get("采购台账数据");
            if (CollectionUtils.isEmpty(ledgerList)) return AjaxResult.error("采购台账数据为空!");
            List<PurchaseLedgerImportDto> productList = stringListMap.get("采购产品数据");
            if (CollectionUtils.isEmpty(productList)) return AjaxResult.error("采购产品数据为空!");
            // 预加载供应商、产品、录入人数据(全量,避免每条都查)
            List<SupplierManage> customers = supplierManageMapper.selectList(Wrappers.lambdaQuery());
            List<Map<String, Object>> productModelList = productModelMapper.getProductAndModelList();
            List<SysUser> sysUsers = sysUserMapper.selectList(Wrappers.lambdaQuery());
            int rowNum = 0;
            for (PurchaseLedgerImportDto dto : ledgerList) {
                rowNum++;
                String contractNo = dto.getPurchaseContractNumber();
                String rowLabel = "第" + rowNum + "行[" + (contractNo != null ? contractNo : "无采购单号") + "]";
                // 已存在则跳过
                PurchaseLedger exist = purchaseLedgerMapper.selectOne(Wrappers.<PurchaseLedger>lambdaQuery()
                        .eq(PurchaseLedger::getPurchaseContractNumber, contractNo)
                        .last("limit 1"));
                if(purchaseLedger != null){
                if (exist != null) {
                    skipCount++;
                    skipList.add(rowLabel + " 采购单号已存在,跳过");
                    continue;
                }
                PurchaseLedger salesLedger = new PurchaseLedger();
                BeanUtils.copyProperties(salesLedgerImportDto, salesLedger);
                // 通过供应商名称查询ID
                salesLedger.setSupplierId(customers.stream()
                        .filter(customer -> customer.getSupplierName().equals(salesLedger.getSupplierName()))
                        .findFirst()
                        .map(SupplierManage::getId)
                        .orElse(null));
                Long aLong = sysUsers.stream()
                        .filter(sysUser -> sysUser.getNickName().equals(salesLedger.getRecorderName()))
                        .findFirst()
                        .map(SysUser::getUserId)
                        .orElse(null);
                if (aLong == null)
                    throw new RuntimeException("录入人:" + salesLedger.getRecorderName() + ",无对应用户!");
                salesLedger.setRecorderId(aLong);
                // 采购产品数据绑定,通过采购单号获取对应采购产品数据
                List<PurchaseLedgerProductImportDto> salesLedgerProductImportDtos = salesLedgerProductImportDtoList.stream()
                        .filter(salesLedgerProductImportDto -> salesLedgerProductImportDto.getPurchaseContractNumber().equals(salesLedger.getPurchaseContractNumber()))
                        .collect(Collectors.toList());
                if (CollectionUtils.isEmpty(salesLedgerProductImportDtos))
                    throw new RuntimeException("采购单号:" + salesLedgerImportDto.getPurchaseContractNumber() + ",无对应产品数据!");
                salesLedger.setContractAmount(salesLedgerProductImportDtos.stream()
                        .map(PurchaseLedgerProductImportDto::getTaxInclusiveTotalPrice)
                        .reduce(BigDecimal.ZERO,BigDecimal::add));
                // 通过销售单号绑定销售
                SalesLedger salesLedger1 = salesLedgerMapper.selectOne(new LambdaQueryWrapper<SalesLedger>()
                        .eq(SalesLedger::getSalesContractNo, salesLedger.getSalesContractNo())
                        .last("LIMIT 1"));
                if(salesLedger1 != null){
                    salesLedger.setSalesLedgerId(salesLedger1.getId());
                }
                if (StringUtils.hasText(salesLedger.getApproveUserIds())) {
                    // 采购审核:历史导入模板传审批人姓名时,继续兼容转换为用户ID。
                    String[] split = salesLedger.getApproveUserIds().split(",");
                    List<Long> ids = new ArrayList<>();
                    for (int i = 0; i < split.length; i++) {
                        SysUser sysUser = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getNickName, split[i])
                                .last("LIMIT 1"));
                        if (sysUser != null) {
                            ids.add(sysUser.getUserId());
                        }
                    }
                    // 将集合转为字符串,隔开
                    String collect = ids.stream().map(Object::toString).collect(Collectors.joining(","));
                    salesLedger.setApproveUserIds(collect);
                }
                purchaseLedgerMapper.insert(salesLedger);
                for (PurchaseLedgerProductImportDto salesLedgerProductImportDto : salesLedgerProductImportDtos) {
                    SalesLedgerProduct salesLedgerProduct = new SalesLedgerProduct();
                    BeanUtils.copyProperties(salesLedgerProductImportDto, salesLedgerProduct);
                    salesLedgerProduct.setSalesLedgerId(salesLedger.getId());
                    salesLedgerProduct.setType(2);
                    // 计算不含税总价
                    salesLedgerProduct.setTaxExclusiveTotalPrice(salesLedgerProduct.getTaxInclusiveTotalPrice().divide(new BigDecimal(1).add(salesLedgerProduct.getTaxRate().divide(new BigDecimal(100))), 2, RoundingMode.HALF_UP));
                    salesLedgerProduct.setFutureTickets(salesLedgerProduct.getQuantity());
                    salesLedgerProduct.setFutureTicketsAmount(salesLedgerProduct.getTaxExclusiveTotalPrice());
                    list.stream()
                            .filter(map -> map.get("productName").equals(salesLedgerProduct.getProductCategory()) && map.get("model").equals(salesLedgerProduct.getSpecificationModel()))
                            .findFirst()
                            .ifPresent(map -> {
                                salesLedgerProduct.setProductModelId(Long.parseLong(map.get("modelId").toString()));
                                salesLedgerProduct.setProductId(Long.parseLong(map.get("id").toString()));
                            });
                    salesLedgerProduct.setRegister(loginUser.getNickName());
                    salesLedgerProduct.setRegisterDate(LocalDateTime.now());
                    salesLedgerProduct.setApproveStatus(0);
                    salesLedgerProduct.setPendingTicketsTotal(salesLedgerProductImportDto.getTaxInclusiveTotalPrice());
                    // 是否质检判断
                    salesLedgerProduct.setIsChecked(salesLedgerProductImportDto.getIsChecked() == 1);
                    if(salesLedgerProductImportDto.getIsChecked() == 1){
                        addQualityInspect(salesLedger, salesLedgerProduct);
                    }
                    salesLedgerProductMapper.insert(salesLedgerProduct);
                // 每条一个独立事务,失败只回滚当前条
                TransactionStatus txStatus = transactionManager.getTransaction(new DefaultTransactionDefinition());
                try {
                    importOneLedger(dto, productList, customers, productModelList, sysUsers, loginUser);
                    transactionManager.commit(txStatus);
                    successCount++;
                } catch (Exception e) {
                    transactionManager.rollback(txStatus);
                    failCount++;
                    failList.add(rowLabel + " " + e.getMessage());
                    log.error("导入失败 - {}", rowLabel, e);
                }
                // 采购审核
                addApproveByPurchase(loginUser,salesLedger);
            }
            return R.ok(null, "导入成功");
            // 构建返回结果
            StringBuilder msg = new StringBuilder();
            msg.append("导入完成:成功").append(successCount).append("条");
            if (skipCount > 0) msg.append(",跳过").append(skipCount).append("条");
            if (failCount > 0) msg.append(",失败").append(failCount).append("条");
            Map<String, Object> result = new HashMap<>();
            result.put("successCount", successCount);
            result.put("skipCount", skipCount);
            result.put("failCount", failCount);
            result.put("failList", failList);
            result.put("skipList", skipList);
            result.put("msg", msg.toString());
            AjaxResult ajaxResult = AjaxResult.success(msg.toString());
            ajaxResult.putAll(result);
            return ajaxResult;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("采购台账导入异常", e);
            return AjaxResult.error("导入异常:" + e.getMessage());
        }
        return R.ok(null, "导入失败");
    }
    /**
     * 导入单条采购台账(含产品明细+审批流程)
     */
    private void importOneLedger(PurchaseLedgerImportDto dto,
                                 List<PurchaseLedgerImportDto> productList,
                                 List<SupplierManage> customers,
                                 List<Map<String, Object>> productModelList,
                                 List<SysUser> sysUsers,
                                 LoginUser loginUser) throws Exception {
        PurchaseLedger ledger = new PurchaseLedger();
        BeanUtils.copyProperties(dto, ledger);
        // 供应商
        ledger.setSupplierId(customers.stream()
                .filter(c -> c.getSupplierName() != null && c.getSupplierName().equals(dto.getSupplierName()))
                .findFirst()
                .map(SupplierManage::getId)
                .orElse(null));
        if (ledger.getSupplierId() == null) {
            throw new RuntimeException("供应商不存在:" + dto.getSupplierName());
        }
        // 录入人
        Long recorderId = sysUsers.stream()
                .filter(u -> u.getNickName() != null && u.getNickName().equals(dto.getRecorderName()))
                .findFirst()
                .map(SysUser::getUserId)
                .orElse(null);
        if (recorderId == null) {
            throw new RuntimeException("录入人不存在:" + dto.getRecorderName());
        }
        ledger.setRecorderId(recorderId);
        // 产品明细
        List<PurchaseLedgerProductImportDto> productDtos = productList.stream()
                .filter(p -> p.getPurchaseContractNumber() != null
                        && p.getPurchaseContractNumber().equals(dto.getPurchaseContractNumber()))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(productDtos)) {
            throw new RuntimeException("无对应产品数据");
        }
        ledger.setContractAmount(productDtos.stream()
                .map(PurchaseLedgerProductImportDto::getTaxInclusiveTotalPrice)
                .reduce(BigDecimal.ZERO, BigDecimal::add));
        // 关联销售单号
        SalesLedger salesLedger = salesLedgerMapper.selectOne(Wrappers.<SalesLedger>lambdaQuery()
                .eq(SalesLedger::getSalesContractNo, dto.getSalesContractNo())
                .last("LIMIT 1"));
        if (salesLedger != null) {
            ledger.setSalesLedgerId(salesLedger.getId());
        }
        // 审核人(中文逗号分隔的姓名转ID)
        if (StringUtils.hasText(ledger.getApproveUserIds())) {
            String[] split = ledger.getApproveUserIds().split(",");
            List<Long> ids = new ArrayList<>();
            for (String name : split) {
                String trimmed = name.trim();
                if (trimmed.isEmpty()) continue;
                sysUsers.stream()
                        .filter(u -> trimmed.equals(u.getNickName()))
                        .findFirst()
                        .ifPresent(u -> ids.add(u.getUserId()));
            }
            ledger.setApproveUserIds(ids.stream().map(String::valueOf).collect(Collectors.joining(",")));
        }
        purchaseLedgerMapper.insert(ledger);
        // 产品明细入库
        for (PurchaseLedgerProductImportDto prodDto : productDtos) {
            SalesLedgerProduct product = new SalesLedgerProduct();
            BeanUtils.copyProperties(prodDto, product);
            product.setSalesLedgerId(ledger.getId());
            product.setType(2);
            AmountUtils.normalizeSalesLedgerProduct(product);
            product.setTaxExclusiveTotalPrice(
                    AmountUtils.calcTaxExclusiveTotalPrice(
                            product.getTaxInclusiveTotalPrice(),
                            product.getTaxRate()
                    )
            );
            productModelList.stream()
                    .filter(map -> prodDto.getProductCategory() != null
                            && prodDto.getSpecificationModel() != null
                            && prodDto.getProductCategory().equals(map.get("productName"))
                            && prodDto.getSpecificationModel().equals(map.get("model")))
                    .findFirst()
                    .ifPresent(map -> {
                        product.setProductModelId(Long.parseLong(map.get("modelId").toString()));
                        product.setProductId(Long.parseLong(map.get("id").toString()));
                    });
            product.setRegister(loginUser.getNickName());
            product.setRegisterDate(LocalDateTime.now());
            product.setApproveStatus(0);
            product.setIsChecked(prodDto.getIsChecked() != null && prodDto.getIsChecked() == 1);
            //if (prodDto.getIsChecked() != null && prodDto.getIsChecked() == 1) {
            //    addQualityInspect(ledger, product);
            //}
            salesLedgerProductMapper.insert(product);
        }
        // 采购审核
        addApproveByPurchase(loginUser, ledger);
    }
    @Override
@@ -666,7 +699,9 @@
        // 4. 转换 DTO
        PurchaseLedgerDto resultDto = new PurchaseLedgerDto();
        BeanUtils.copyProperties(purchaseLedger, resultDto);
        AmountUtils.normalizeContractAmount(resultDto);
        if (!products.isEmpty()) {
            AmountUtils.normalizeSalesLedgerProducts(products);
            resultDto.setHasChildren(true);
            resultDto.setProductData(products);
        }
@@ -684,6 +719,7 @@
        approveProcessVO.setApproveUserIds(purchaseLedger.getApproveUserIds());
        approveProcessVO.setApproveUser(loginUser.getUserId());
        approveProcessVO.setApproveTime(LocalDate.now().toString());
        approveProcessVO.setPrice(AmountUtils.scaleAmount(purchaseLedger.getContractAmount()));
        approveProcessService.addApprove(approveProcessVO);
    }
@@ -708,4 +744,23 @@
        }
        return sb.toString();
    }
    @Override
    public Map<String, Object> getPrintData(Long id) {
        PurchaseLedger purchaseLedger = getById(id);
        if (purchaseLedger == null) {
            return null;
        }
        List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(
            new LambdaQueryWrapper<SalesLedgerProduct>()
                .eq(SalesLedgerProduct::getSalesLedgerId, id)
                .eq(SalesLedgerProduct::getType, 2)
        );
        SysDept dept = sysDeptMapper.selectDeptById(100L);
        Map<String, Object> result = new HashMap<>();
        result.put("companyName", dept != null ? dept.getDeptName() : "");
        result.put("ledger", purchaseLedger);
        result.put("products", products);
        return result;
    }
}