| | |
| | | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | @Resource |
| | | private ErpSupplierService supplierService; |
| | | @Resource |
| | | private ErpAccountService accountService; |
| | | @Resource |
| | | private ErpPurchaseInvoiceService invoiceService; |
| | | |
| | | @Resource |
| | |
| | | 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); |
| | |
| | | } |
| | | // 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()); |
| | |
| | | } |
| | | |
| | | // 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) |
| | |
| | | } |
| | | |
| | | /** |
| | | * 校验付款单关联的来票:存在、审批通过、已上传发票附件、供应商与来票一致 |
| | | * 校验付款单关联的来票:存在、已上传发票附件、供应商与来票一致 |
| | | * |
| | | * @param invoiceId 来票编号 |
| | | * @param supplierId 供应商编号 |
| | |
| | | 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); |
| | | } |
| | |
| | | } |
| | | |
| | | @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); |
| | | } |