gaoluyang
8 小时以前 5a347be900bf045c947cdbc08167526cd44116db
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/CrmSaleQuotationServiceImpl.java
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.crm.service.quotation;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
@@ -81,11 +82,11 @@
    public Long createSaleQuotation(CrmSaleQuotationSaveReqVO createReqVO, Long userId) {
        // 1.1 校验物料项的有效性
        List<CrmSaleQuotationProductDO> quotationProducts = validateQuotationItems(createReqVO.getItems());
        // 1.2 生成报价单号,并校验唯一性
        String no = noRedisDAO.generate(CrmNoRedisDAO.SALE_QUOTATION_NO_PREFIX);
        if (saleQuotationMapper.selectByNo(no) != null) {
            throw exception(SALE_QUOTATION_NO_EXISTS);
        }
        // 1.2 确定报价单号(前端填写则用自定义单号,留空则自动生成),并校验唯一性
        String customNo = StrUtil.trim(createReqVO.getNo());
        String no = StrUtil.isBlank(customNo)
                ? noRedisDAO.generate(CrmNoRedisDAO.SALE_QUOTATION_NO_PREFIX) : customNo;
        validateQuotationNoUnique(no, null);
        // 1.3 校验关联字段
        validateRelationDataExists(createReqVO);
@@ -123,8 +124,15 @@
        // 1.4 校验关联字段
        validateRelationDataExists(updateReqVO);
        // 2.1 更新报价单
        // 2.1 更新报价单(报价单号留空表示保持原值,填写则校验唯一性后替换)
        CrmSaleQuotationDO updateObj = BeanUtils.toBean(updateReqVO, CrmSaleQuotationDO.class);
        String no = StrUtil.trim(updateReqVO.getNo());
        if (StrUtil.isBlank(no)) {
            updateObj.setNo(saleQuotation.getNo());
        } else {
            validateQuotationNoUnique(no, saleQuotation.getId());
            updateObj.setNo(no);
        }
        calculateTotalPrice(updateObj, quotationProducts);
        saleQuotationMapper.updateById(updateObj);
        // 2.2 更新报价明细
@@ -167,6 +175,19 @@
        return saleQuotation;
    }
    /**
     * 校验报价单号唯一(含已逻辑删除的报价单,因 uk_no 唯一索引覆盖已删除行)
     *
     * @param no       报价单号
     * @param selfId   自身编号(修改时排除自己,新增传 null)
     */
    private void validateQuotationNoUnique(String no, Long selfId) {
        CrmSaleQuotationDO exists = saleQuotationMapper.selectAnyByNo(no);
        if (exists != null && !exists.getId().equals(selfId)) {
            throw exception(SALE_QUOTATION_NO_EXISTS, no);
        }
    }
    private List<CrmSaleQuotationProductDO> validateQuotationItems(List<CrmSaleQuotationSaveReqVO.Item> list) {
        // 1. 校验物料存在且启用
        Set<Long> itemIds = convertSet(list, CrmSaleQuotationSaveReqVO.Item::getItemId);
@@ -181,10 +202,14 @@
            // 物料原价,如果 MDM 中没有则默认为 0
            item.setItemPrice(mdmItem.getSalesPrice() != null ? mdmItem.getSalesPrice() : BigDecimal.ZERO);
            if (item.getQuotationPrice() != null && item.getCount() != null) {
                item.setTotalPrice(MoneyUtils.priceMultiply(item.getQuotationPrice(), item.getCount()));
                // 报价为含税单价:含税金额 = 单价 × 数量;税额 = 含税金额 × 税率;不含税金额 = 含税金额 - 税额
                BigDecimal taxInclusivePrice = MoneyUtils.priceMultiply(item.getQuotationPrice(), item.getCount());
                BigDecimal taxPrice = null;
                if (item.getTaxPercent() != null) {
                    item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent()));
                    taxPrice = MoneyUtils.priceMultiplyPercent(taxInclusivePrice, item.getTaxPercent());
                }
                item.setTaxPrice(taxPrice);
                item.setTotalPrice(taxPrice != null ? taxInclusivePrice.subtract(taxPrice) : taxInclusivePrice);
            }
        }));
    }