src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -19,6 +19,7 @@
import com.ruoyi.basic.mapper.ProductMapper;
import com.ruoyi.basic.mapper.ProductModelMapper;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.basic.pojo.ProductModel;
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.common.enums.FileNameType;
import com.ruoyi.common.enums.SaleEnum;
@@ -361,13 +362,34 @@
            // 录入人数据
            List<SysUser> sysUsers = sysUserMapper.selectList(new LambdaQueryWrapper<SysUser>().in(SysUser::getNickName,
                    salesLedgerImportDtoList.stream().map(SalesLedgerImportDto::getEntryPerson).collect(Collectors.toList())));
            for (SalesLedgerImportDto salesLedgerImportDto : salesLedgerImportDtoList) {
                SalesLedger salesLedger1 = salesLedgerMapper.selectOne(new LambdaQueryWrapper<SalesLedger>()
                        .eq(SalesLedger::getSalesContractNo, salesLedgerImportDto.getSalesContractNo())
                        .last("LIMIT 1"));
                if (salesLedger1 != null) {
            // 销售单号重复校验:文件内重复或库内已存在的,直接失败并回显原有销售单号
            List<String> duplicateContractNos = new ArrayList<>();
            Set<String> importContractNos = new HashSet<>();
            for (SalesLedgerImportDto item : salesLedgerImportDtoList) {
                String contractNo = StringUtils.trim(item.getSalesContractNo());
                if (StringUtils.isEmpty(contractNo)) {
                    continue;
                }
                if (!importContractNos.add(contractNo)) {
                    duplicateContractNos.add(contractNo);
                }
            }
            if (CollectionUtils.isNotEmpty(duplicateContractNos)) {
                return AjaxResult.error("导入文件中存在重复的销售单号:" + String.join("、", duplicateContractNos));
            }
            if (CollectionUtils.isNotEmpty(importContractNos)) {
                List<SalesLedger> existLedgers = salesLedgerMapper.selectList(new LambdaQueryWrapper<SalesLedger>()
                        .in(SalesLedger::getSalesContractNo, importContractNos)
                        .select(SalesLedger::getId, SalesLedger::getSalesContractNo));
                if (CollectionUtils.isNotEmpty(existLedgers)) {
                    String existNos = existLedgers.stream()
                            .map(SalesLedger::getSalesContractNo)
                            .distinct()
                            .collect(Collectors.joining("、"));
                    return AjaxResult.error("以下销售单号已存在,不能重复新增:" + existNos);
                }
            }
            for (SalesLedgerImportDto salesLedgerImportDto : salesLedgerImportDtoList) {
                SalesLedger salesLedger = new SalesLedger();
                BeanUtils.copyProperties(salesLedgerImportDto, salesLedger);
                salesLedger.setExecutionDate(DateUtils.toLocalDate(salesLedgerImportDto.getExecutionDate()));
@@ -408,6 +430,11 @@
                    BeanUtils.copyProperties(salesLedgerProductImportDto, salesLedgerProduct);
                    salesLedgerProduct.setSalesLedgerId(salesLedger.getId());
                    salesLedgerProduct.setType(1);
                    // 物料编码 -> 产品编码
                    String materialCode = StringUtils.trim(salesLedgerProductImportDto.getMaterialCode());
                    if (StringUtils.isNotEmpty(materialCode)) {
                        salesLedgerProduct.setProductCode(materialCode);
                    }
                    AmountUtils.normalizeSalesLedgerProduct(salesLedgerProduct);
                    // 计算不含税总价
                    salesLedgerProduct.setTaxExclusiveTotalPrice(
@@ -422,6 +449,10 @@
                            .ifPresent(map -> {
                                salesLedgerProduct.setProductModelId(Long.parseLong(map.get("modelId").toString()));
                                salesLedgerProduct.setProductId(Long.parseLong(map.get("id").toString()));
                                // 未填物料编码时,按产品型号回填产品编码
                                if (StringUtils.isEmpty(salesLedgerProduct.getProductCode()) && map.get("productCode") != null) {
                                    salesLedgerProduct.setProductCode(map.get("productCode").toString());
                                }
                            });
//                    salesLedgerProduct.setProductId(productList.stream()
//                            .filter(product -> product.getProductName().equals(salesLedgerProduct.getProductCategory()))
@@ -436,7 +467,7 @@
                    salesLedgerProduct.setRegister(loginUser.getNickName());
                    salesLedgerProduct.setRegisterDate(LocalDateTime.now());
                    salesLedgerProduct.setApproveStatus(0);
                    salesLedgerProduct.setIsProduction(salesLedgerProductImportDto.getIsProduction() == 1);
                    salesLedgerProduct.setIsProduction(parseProductionFlag(salesLedgerProductImportDto.getIsProduction()));
                    salesLedgerProductMapper.insert(salesLedgerProduct);
                    // 添加生产数据
                    salesLedgerProductServiceImpl.addProductionData(salesLedgerProduct);
@@ -582,9 +613,14 @@
        AmountUtils.normalizeContractAmount(salesLedger);
        salesLedger.setCustomerName(customer.getCustomerName());
        salesLedger.setTenantId(customer.getTenantId());
        // 3. 新增或更新主表
        // 3. 销售合同号唯一校验(支持字母、数字及特殊字符,不做字符集限制)
        String contractNo = StringUtils.trim(salesLedger.getSalesContractNo());
        if (StringUtils.isNotEmpty(contractNo)) {
            checkSalesContractNoUnique(contractNo, salesLedger.getId());
            salesLedger.setSalesContractNo(contractNo);
        }
        // 4. 新增或更新主表
        if (salesLedger.getId() == null) {
            String contractNo = salesLedger.getSalesContractNo();
            if (StringUtils.isEmpty(contractNo)) {
                contractNo = generateSalesContractNo();
            }
@@ -595,7 +631,7 @@
        }
        // 4. 处理子表数据
        // 5. 处理子表数据
        List<SalesLedgerProduct> productList = salesLedgerDto.getProductData();
        if (productList != null && !productList.isEmpty()) {
            AmountUtils.normalizeSalesLedgerProducts(productList);
@@ -615,8 +651,31 @@
        return 1;
    }
    /**
     * 销售合同号唯一校验(编辑时排除自身)
     */
    private void checkSalesContractNoUnique(String salesContractNo, Long currentId) {
        LambdaQueryWrapper<SalesLedger> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SalesLedger::getSalesContractNo, salesContractNo)
                .ne(currentId != null, SalesLedger::getId, currentId)
                .last("limit 1");
        SalesLedger duplicate = salesLedgerMapper.selectOne(queryWrapper);
        if (duplicate != null) {
            throw new BaseException("销售合同号" + salesContractNo + "已存在(销售单号:" + duplicate.getSalesContractNo() + "),请重新填写");
        }
    }
    @Override
    public void handleSalesLedgerProducts(Long salesLedgerId, List<SalesLedgerProduct> products, SaleEnum type) {
        // 产品编码兜底:前端未传时按产品型号回填
        products.forEach(p -> {
            if (StringUtils.isEmpty(p.getProductCode()) && p.getProductModelId() != null) {
                ProductModel productModel = productModelMapper.selectById(p.getProductModelId());
                if (productModel != null) {
                    p.setProductCode(productModel.getProductCode());
                }
            }
        });
        // 按ID分组,区分新增和更新的记录
        Map<Boolean, List<SalesLedgerProduct>> partitionedProducts = products.stream()
                .peek(p -> p.setSalesLedgerId(salesLedgerId))
@@ -643,6 +702,17 @@
                salesLedgerProductServiceImpl.addProductionData(salesLedgerProduct);
            }
        }
    }
    /**
     * 解析导入的“是否生产”列:支持 1/0、是/否
     */
    private static boolean parseProductionFlag(String value) {
        if (StringUtils.isEmpty(value)) {
            return false;
        }
        String v = value.trim();
        return "1".equals(v) || "是".equals(v) || "true".equalsIgnoreCase(v) || "yes".equalsIgnoreCase(v);
    }
    private SalesLedger convertToEntity(SalesLedgerDto dto) {
@@ -800,4 +870,16 @@
        result.put("ledger", ledger);
        return result;
    }
    @Override
    public Map<Long, SalesLedgerEditFlagDto> getEditFlags(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return new HashMap<>();
        }
        List<SalesLedgerEditFlagDto> flags = salesLedgerMapper.selectEditFlags(ids);
        return flags.stream().collect(Collectors.toMap(
                SalesLedgerEditFlagDto::getSalesLedgerId,
                Function.identity(),
                (existing, replacement) -> existing));
    }
}