gaoluyang
7 天以前 d39e4f7d839167c16425086dfa2f8747bccfdff9
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentServiceImpl.java
@@ -2,8 +2,11 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.erp.api.finance.dto.ErpFinancePaymentRedFlushCreateReqDTO;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
@@ -20,7 +23,8 @@
import cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpFinancePaymentMapper;
import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO;
import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;
import cn.iocoder.yudao.module.erp.enums.ErpPurchaseInvoiceAuditStatusEnum;
import cn.iocoder.yudao.module.erp.enums.common.ErpBizTypeEnum;
import cn.iocoder.yudao.module.erp.enums.finance.ErpFinancePaymentSourceTypeEnum;
import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseInvoiceService;
import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseInvoiceServiceImpl;
import cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService;
@@ -34,6 +38,7 @@
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -66,8 +71,6 @@
    @Resource
    private ErpSupplierService supplierService;
    @Resource
    private ErpAccountService accountService;
    @Resource
    private ErpPurchaseInvoiceService invoiceService;
    @Resource
@@ -91,17 +94,13 @@
                createReqVO.getSupplierId(), createReqVO.getItems());
        // 1.2 校验供应商
        supplierService.validateSupplier(createReqVO.getSupplierId());
        // 1.3 校验结算账户
        if (createReqVO.getAccountId() != null) {
            accountService.validateAccount(createReqVO.getAccountId());
        }
        // 1.4 校验财务人员
        // 1.3 校验财务人员
        if (createReqVO.getFinanceUserId() != null) {
            adminUserApi.validateUser(createReqVO.getFinanceUserId());
        }
        // 1.5 校验关联来票
        // 1.4 校验关联来票
        validateInvoiceForPayment(createReqVO.getInvoiceId(), createReqVO.getSupplierId());
        // 1.6 校验付款金额不超过该来票剩余可付金额
        // 1.5 校验付款金额不超过该来票剩余可付金额
        validatePaymentPriceExceedsInvoiceRemaining(createReqVO.getInvoiceId(),
                getSumValue(paymentItems, ErpFinancePaymentItemDO::getPaymentPrice, BigDecimal::add, BigDecimal.ZERO)
                        .subtract(createReqVO.getDiscountPrice()), null);
@@ -142,20 +141,16 @@
        }
        // 1.2 校验供应商
        supplierService.validateSupplier(updateReqVO.getSupplierId());
        // 1.3 校验结算账户
        if (updateReqVO.getAccountId() != null) {
            accountService.validateAccount(updateReqVO.getAccountId());
        }
        // 1.4 校验财务人员
        // 1.3 校验财务人员
        if (updateReqVO.getFinanceUserId() != null) {
            adminUserApi.validateUser(updateReqVO.getFinanceUserId());
        }
        // 1.5 校验付款单项的有效性
        // 1.4 校验付款单项的有效性
        List<ErpFinancePaymentItemDO> paymentItems = validateFinancePaymentItems(
                updateReqVO.getSupplierId(), updateReqVO.getItems());
        // 1.6 校验关联来票
        // 1.5 校验关联来票
        validateInvoiceForPayment(updateReqVO.getInvoiceId(), updateReqVO.getSupplierId());
        // 1.7 校验付款金额不超过该来票剩余可付金额(排除该付款单自身当前已付金额)
        // 1.6 校验付款金额不超过该来票剩余可付金额(排除该付款单自身当前已付金额)
        validatePaymentPriceExceedsInvoiceRemaining(updateReqVO.getInvoiceId(),
                getSumValue(paymentItems, ErpFinancePaymentItemDO::getPaymentPrice, BigDecimal::add, BigDecimal.ZERO)
                        .subtract(updateReqVO.getDiscountPrice()), payment.getPaymentPrice());
@@ -185,6 +180,16 @@
        }
        // 2. 创建 BPM 流程实例
        // 全局免审批:业务校验通过后直接执行审批通过逻辑,无需流程模型。
        if (processInstanceApi.isGlobalAutoApprove()) {
            financePaymentMapper.updateById(new ErpFinancePaymentDO()
                    .setId(id)
                    .setStatus(ErpAuditStatus.APPROVING.getStatus())
                    .setProcessInstanceId(""));
            updateFinancePaymentAuditStatus(id, cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum.APPROVE.getStatus());
            return;
        }
        String processInstanceId = processInstanceApi.createProcessInstance(userId,
                new BpmProcessInstanceCreateReqDTO()
                        .setProcessDefinitionKey(processDefinitionKey)
@@ -354,7 +359,7 @@
    }
    /**
     * 校验付款单关联的来票:存在、审批通过、已上传发票附件、供应商与来票一致
     * 校验付款单关联的来票:存在、已上传发票附件、供应商与来票一致
     *
     * @param invoiceId 来票编号
     * @param supplierId 供应商编号
@@ -365,16 +370,12 @@
        if (invoice == null) {
            throw exception(FINANCE_PAYMENT_CREATE_FAIL_INVOICE_NOT_EXISTS);
        }
        // 2. 校验来票审批通过
        if (ObjectUtil.notEqual(invoice.getAuditStatus(), ErpPurchaseInvoiceAuditStatusEnum.APPROVE.getStatus())) {
            throw exception(FINANCE_PAYMENT_CREATE_FAIL_INVOICE_NOT_APPROVE);
        }
        // 3. 校验已上传发票附件
        // 2. 校验已上传发票附件
        if (CollUtil.isEmpty(storageAttachmentApi.listAttachments(
                ErpPurchaseInvoiceServiceImpl.PURCHASE_INVOICE_RECORD_TYPE, invoiceId))) {
            throw exception(FINANCE_PAYMENT_CREATE_FAIL_INVOICE_NOT_ATTACHMENT);
        }
        // 4. 校验供应商与来票一致(防脏数据)
        // 3. 校验供应商与来票一致(防脏数据)
        if (ObjectUtil.notEqual(invoice.getSupplierId(), supplierId)) {
            throw exception(FINANCE_PAYMENT_CREATE_FAIL_INVOICE_SUPPLIER_MISMATCH);
        }
@@ -401,6 +402,73 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createRedFlushPayment(ErpFinancePaymentRedFlushCreateReqDTO reqDTO) {
        // 1.1 校验来源单据:冲红单必须能追溯到退货单
        if (reqDTO.getSourceId() == null || StrUtil.isBlank(reqDTO.getSourceNo())) {
            throw exception(FINANCE_PAYMENT_RED_FLUSH_SOURCE_REQUIRED);
        }
        // 1.2 校验供应商
        if (reqDTO.getSupplierId() == null) {
            throw exception(FINANCE_PAYMENT_RED_FLUSH_SUPPLIER_REQUIRED);
        }
        supplierService.validateSupplier(reqDTO.getSupplierId());
        // 1.3 校验退款金额
        if (reqDTO.getRefundAmount() == null || reqDTO.getRefundAmount().compareTo(BigDecimal.ZERO) <= 0) {
            throw exception(FINANCE_PAYMENT_RED_FLUSH_AMOUNT_INVALID, reqDTO.getRefundAmount());
        }
        // 1.4 校验财务人员
        if (reqDTO.getFinanceUserId() != null) {
            adminUserApi.validateUser(reqDTO.getFinanceUserId());
        }
        // 1.5 幂等:同一来源退货单只允许生成一张冲红单
        ErpFinancePaymentDO existing = financePaymentMapper.selectOne(new LambdaQueryWrapperX<ErpFinancePaymentDO>()
                .eq(ErpFinancePaymentDO::getSourceType,
                        ErpFinancePaymentSourceTypeEnum.PURCHASE_RETURN_RED_FLUSH.getType())
                .eq(ErpFinancePaymentDO::getSourceId, reqDTO.getSourceId()));
        if (existing != null) {
            log.info("[createRedFlushPayment][退货单({}) 已存在冲红单({}),直接复用]",
                    reqDTO.getSourceNo(), existing.getNo());
            return existing.getId();
        }
        // 2.1 生成付款单号
        String no = noRedisDAO.generate(ErpNoRedisDAO.FINANCE_PAYMENT_NO_PREFIX);
        if (financePaymentMapper.selectByNo(no) != null) {
            throw exception(FINANCE_PAYMENT_NO_EXISTS);
        }
        // 2.2 金额按负数入账
        BigDecimal negativeAmount = reqDTO.getRefundAmount().negate();
        ErpFinancePaymentDO payment = new ErpFinancePaymentDO()
                .setNo(no)
                .setStatus(ErpAuditStatus.APPROVE.getStatus())
                .setPaymentTime(LocalDateTime.now())
                .setFinanceUserId(reqDTO.getFinanceUserId())
                .setSupplierId(reqDTO.getSupplierId())
                .setAccountName(reqDTO.getAccountName())
                .setTotalPrice(negativeAmount)
                .setDiscountPrice(BigDecimal.ZERO)
                .setPaymentPrice(negativeAmount)
                .setRemark(StrUtil.blankToDefault(reqDTO.getRemark(), "采购退货冲红"))
                .setSourceType(ErpFinancePaymentSourceTypeEnum.PURCHASE_RETURN_RED_FLUSH.getType())
                .setSourceId(reqDTO.getSourceId())
                .setSourceNo(reqDTO.getSourceNo());
        financePaymentMapper.insert(payment);
        // 3. 插入付款项(业务类型:采购退货)
        financePaymentItemMapper.insert(new ErpFinancePaymentItemDO()
                .setPaymentId(payment.getId())
                .setBizType(ErpBizTypeEnum.PURCHASE_RETURN.getType())
                .setBizId(reqDTO.getSourceId())
                .setBizNo(reqDTO.getSourceNo())
                .setTotalPrice(negativeAmount)
                .setPaidPrice(BigDecimal.ZERO)
                .setPaymentPrice(negativeAmount)
                .setRemark(StrUtil.blankToDefault(reqDTO.getRemark(), "采购退货冲红")));
        return payment.getId();
    }
    @Override
    public ErpFinancePaymentDO getFinancePayment(Long id) {
        return financePaymentMapper.selectById(id);
    }