package cn.iocoder.yudao.module.erp.service.purchase;
|
|
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.util.ObjectUtil;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.invoice.ErpPurchaseInvoicePageReqVO;
|
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.invoice.ErpPurchaseInvoiceSaveReqVO;
|
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInvoiceDO;
|
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO;
|
import cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpFinancePaymentMapper;
|
import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseInvoiceMapper;
|
import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO;
|
import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
|
import jakarta.annotation.Resource;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.validation.annotation.Validated;
|
|
import java.math.BigDecimal;
|
import java.util.Collection;
|
import java.util.List;
|
import java.util.Map;
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue;
|
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;
|
|
/**
|
* ERP 采购来票 Service 实现类
|
*
|
* @author 超级管理员
|
*/
|
@Service
|
@Validated
|
public class ErpPurchaseInvoiceServiceImpl implements ErpPurchaseInvoiceService {
|
|
/**
|
* 来票附件的业务记录类型,对应 {@link cn.iocoder.yudao.module.system.enums.storage.StorageRecordTypeEnum#ERP_PURCHASE_INVOICE}
|
*/
|
public static final String PURCHASE_INVOICE_RECORD_TYPE = "erp_purchase_invoice";
|
|
@Resource
|
private ErpPurchaseInvoiceMapper invoiceMapper;
|
|
@Resource
|
private ErpFinancePaymentMapper financePaymentMapper;
|
|
@Resource
|
private ErpNoRedisDAO noRedisDAO;
|
|
@Resource
|
private ErpPurchaseOrderService purchaseOrderService;
|
|
@Resource
|
private StorageAttachmentApi storageAttachmentApi;
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public Long createPurchaseInvoice(ErpPurchaseInvoiceSaveReqVO createReqVO) {
|
// 1.1 校验关联数据存在
|
validateRelationDataExists(createReqVO);
|
// 1.2 校验可开票金额超过上限
|
validatePurchaseInvoicePriceExceedsLimit(createReqVO);
|
// 1.3 生成来票编号
|
String no = noRedisDAO.generate(ErpNoRedisDAO.PURCHASE_INVOICE_NO_PREFIX);
|
if (invoiceMapper.selectByNo(no) != null) {
|
throw exception(PURCHASE_INVOICE_NO_EXISTS);
|
}
|
|
// 2.1 插入来票(创建后即为有效状态,无需审核)
|
ErpPurchaseInvoiceDO invoice = BeanUtils.toBean(createReqVO, ErpPurchaseInvoiceDO.class).setNo(no);
|
invoiceMapper.insert(invoice);
|
|
// 2.2 绑定发票附件
|
if (CollUtil.isNotEmpty(createReqVO.getBlobIds())) {
|
storageAttachmentApi.bindAttachments("file", PURCHASE_INVOICE_RECORD_TYPE, invoice.getId(), createReqVO.getBlobIds());
|
}
|
return invoice.getId();
|
}
|
|
private void validateRelationDataExists(ErpPurchaseInvoiceSaveReqVO reqVO) {
|
// 校验采购订单,必须审核通过
|
ErpPurchaseOrderDO purchaseOrder = purchaseOrderService.validatePurchaseOrder(reqVO.getPurchaseOrderId());
|
// 校验供应商与采购订单一致(防御脏数据:来票必须属于该采购订单的供应商)
|
if (reqVO.getSupplierId() != null && ObjectUtil.notEqual(purchaseOrder.getSupplierId(), reqVO.getSupplierId())) {
|
throw exception(PURCHASE_INVOICE_CREATE_FAIL_SUPPLIER_MISMATCH);
|
}
|
reqVO.setSupplierId(purchaseOrder.getSupplierId()); // 供应商以采购订单为准
|
}
|
|
private void validatePurchaseInvoicePriceExceedsLimit(ErpPurchaseInvoiceSaveReqVO reqVO) {
|
// 1. 计算剩余可开票金额,不包括 reqVO 自身(所有有效来票都占用额度,防止累计来票超过采购订单金额)
|
ErpPurchaseOrderDO purchaseOrder = purchaseOrderService.getPurchaseOrder(reqVO.getPurchaseOrderId());
|
List<ErpPurchaseInvoiceDO> invoices = invoiceMapper.selectListByOrderId(reqVO.getPurchaseOrderId());
|
if (reqVO.getId() != null) {
|
invoices.removeIf(invoice -> ObjectUtil.equal(invoice.getId(), reqVO.getId()));
|
}
|
BigDecimal remainPrice = purchaseOrder.getTotalPrice().subtract(
|
getSumValue(invoices, ErpPurchaseInvoiceDO::getPrice, BigDecimal::add, BigDecimal.ZERO));
|
// 2. 校验金额是否超过
|
if (reqVO.getPrice().compareTo(remainPrice) > 0) {
|
throw exception(PURCHASE_INVOICE_CREATE_FAIL_PRICE_EXCEEDS_LIMIT, remainPrice);
|
}
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void updatePurchaseInvoice(ErpPurchaseInvoiceSaveReqVO updateReqVO) {
|
Assert.notNull(updateReqVO.getId(), "来票编号不能为空");
|
// 1.1 校验存在
|
validatePurchaseInvoiceExists(updateReqVO.getId());
|
// 1.2 校验关联数据 + 可开票金额
|
validateRelationDataExists(updateReqVO);
|
validatePurchaseInvoicePriceExceedsLimit(updateReqVO);
|
|
// 2. 更新来票
|
ErpPurchaseInvoiceDO updateObj = BeanUtils.toBean(updateReqVO, ErpPurchaseInvoiceDO.class);
|
invoiceMapper.updateById(updateObj);
|
|
// 3. 增量更新发票附件
|
storageAttachmentApi.updateAttachments("file", PURCHASE_INVOICE_RECORD_TYPE, updateReqVO.getId(), updateReqVO.getBlobIds());
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void deletePurchaseInvoice(Long id) {
|
// 1.1 校验存在
|
validatePurchaseInvoiceExists(id);
|
// 1.2 已有有效付款单关联的来票,不允许删除(保持来票与付款的关联完整性)
|
Map<Long, BigDecimal> paidMap = financePaymentMapper.selectPaymentPriceMapByInvoiceId(List.of(id));
|
if (paidMap.getOrDefault(id, BigDecimal.ZERO).compareTo(BigDecimal.ZERO) > 0) {
|
throw exception(PURCHASE_INVOICE_DELETE_FAIL_HAS_PAYMENT);
|
}
|
|
// 2.1 删除发票附件
|
storageAttachmentApi.deleteAttachmentsByRecord(PURCHASE_INVOICE_RECORD_TYPE, id);
|
// 2.2 删除来票
|
invoiceMapper.deleteById(id);
|
}
|
|
private ErpPurchaseInvoiceDO validatePurchaseInvoiceExists(Long id) {
|
ErpPurchaseInvoiceDO invoice = invoiceMapper.selectById(id);
|
if (invoice == null) {
|
throw exception(PURCHASE_INVOICE_NOT_EXISTS);
|
}
|
return invoice;
|
}
|
|
@Override
|
public ErpPurchaseInvoiceDO getPurchaseInvoice(Long id) {
|
return invoiceMapper.selectById(id);
|
}
|
|
@Override
|
public List<ErpPurchaseInvoiceDO> getPurchaseInvoiceList(Collection<Long> ids) {
|
if (CollUtil.isEmpty(ids)) {
|
return List.of();
|
}
|
return invoiceMapper.selectByIds(ids);
|
}
|
|
@Override
|
public PageResult<ErpPurchaseInvoiceDO> getPurchaseInvoicePage(ErpPurchaseInvoicePageReqVO pageReqVO) {
|
return invoiceMapper.selectPage(pageReqVO);
|
}
|
|
@Override
|
public List<ErpPurchaseInvoiceDO> getPurchaseInvoiceListByOrderId(Long orderId) {
|
return invoiceMapper.selectListByOrderId(orderId);
|
}
|
|
@Override
|
public List<ErpPurchaseInvoiceDO> getPurchaseInvoiceListBySupplierId(Long supplierId) {
|
return invoiceMapper.selectListBySupplierId(supplierId);
|
}
|
|
}
|