From a378f38df1785cbc8c9f1d55ace6a37952d2f65b Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期一, 10 八月 2026 14:01:56 +0800
Subject: [PATCH] feat(crm): 新增开票功能并完善权限查询
---
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java | 49 ++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 46 insertions(+), 3 deletions(-)
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
index 0c68f1e..0652c25 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
@@ -14,6 +14,7 @@
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
+import cn.iocoder.yudao.module.crm.dal.dataobject.invoice.CrmInvoiceDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivableMapper;
@@ -23,9 +24,12 @@
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
+import cn.iocoder.yudao.module.crm.service.invoice.CrmInvoiceService;
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
+import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.enums.storage.StorageRecordTypeEnum;
import com.mzt.logapi.context.LogRecordContext;
import com.mzt.logapi.service.impl.DiffParseFunction;
import com.mzt.logapi.starter.annotation.LogRecord;
@@ -72,11 +76,15 @@
private CrmReceivablePlanService receivablePlanService;
@Resource
private CrmPermissionService permissionService;
+ @Resource
+ private CrmInvoiceService invoiceService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private BpmProcessInstanceApi bpmProcessInstanceApi;
+ @Resource
+ private StorageAttachmentApi storageAttachmentApi;
@Override
@Transactional(rollbackFor = Exception.class)
@@ -85,9 +93,11 @@
public Long createReceivable(CrmReceivableSaveReqVO createReqVO) {
// 1.1 鏍¢獙鍙洖娆鹃噾棰濊秴杩囦笂闄�
validateReceivablePriceExceedsLimit(createReqVO);
- // 1.2 鏍¢獙鍏宠仈鏁版嵁瀛樺湪
+ // 1.2 鏍¢獙鍙洖娆鹃噾棰濅笉瓒呰繃绱宸插紑绁ㄩ噾棰濓紙鍏堝紑绁ㄥ悗鍥炴锛�
+ validateReceivablePriceExceedsInvoiceLimit(createReqVO);
+ // 1.3 鏍¢獙鍏宠仈鏁版嵁瀛樺湪
validateRelationDataExists(createReqVO);
- // 1.3 鐢熸垚鍥炴缂栧彿
+ // 1.4 鐢熸垚鍥炴缂栧彿
String no = noRedisDAO.generate(CrmNoRedisDAO.RECEIVABLE_PREFIX);
if (receivableMapper.selectByNo(no) != null) {
throw exception(RECEIVABLE_NO_EXISTS);
@@ -131,6 +141,37 @@
}
}
+ /**
+ * 鏍¢獙绱鍥炴閲戦涓嶈秴杩囩疮璁″凡寮�绁ㄤ笖宸蹭笂浼犲彂绁ㄩ檮浠剁殑閲戦锛堝厛寮�绁ㄥ悗鍥炴锛�
+ */
+ private void validateReceivablePriceExceedsInvoiceLimit(CrmReceivableSaveReqVO reqVO) {
+ // 1. 璁$畻鍚堝悓绱鍥炴閲戦锛堜笉鍚湰娆★紝缁熻鍙e緞鍚屽悎鍚岄噾棰濇牎楠岋細瀹℃壒閫氳繃 + 瀹℃壒涓級
+ List<CrmReceivableDO> receivables = receivableMapper.selectListByContractIdAndStatus(reqVO.getContractId(),
+ Arrays.asList(CrmAuditStatusEnum.APPROVE.getStatus(), CrmAuditStatusEnum.PROCESS.getStatus()));
+ if (reqVO.getId() != null) {
+ receivables.removeIf(receivable -> ObjectUtil.equal(receivable.getId(), reqVO.getId()));
+ }
+ BigDecimal receivablePriceSum = CollectionUtils.getSumValue(receivables, CrmReceivableDO::getPrice,
+ BigDecimal::add, BigDecimal.ZERO);
+
+ // 2. 璁$畻鍚堝悓涓嬨�屽鎵归�氳繃涓斿凡涓婁紶鍙戠エ闄勪欢銆嶇殑绱寮�绁ㄩ噾棰�
+ List<CrmInvoiceDO> invoices = invoiceService.getInvoiceListByContractIdAndStatus(reqVO.getContractId(),
+ List.of(CrmAuditStatusEnum.APPROVE.getStatus()));
+ BigDecimal invoicePriceSum = BigDecimal.ZERO;
+ for (CrmInvoiceDO invoice : invoices) {
+ if (CollUtil.isNotEmpty(storageAttachmentApi.listAttachments(
+ StorageRecordTypeEnum.CRM_INVOICE.getType(), invoice.getId()))) {
+ invoicePriceSum = invoicePriceSum.add(invoice.getPrice());
+ }
+ }
+
+ // 3. 鏍¢獙鏈鍥炴閲戦涓嶈秴杩囧墿浣欏彲鍥炴閲戦锛堝凡寮�绁ㄩ噾棰� - 宸插洖娆鹃噾棰濓級
+ BigDecimal availablePrice = invoicePriceSum.subtract(receivablePriceSum);
+ if (reqVO.getPrice().compareTo(availablePrice) > 0) {
+ throw exception(RECEIVABLE_CREATE_FAIL_INVOICE_NOT_ENOUGH, availablePrice);
+ }
+ }
+
private void validateRelationDataExists(CrmReceivableSaveReqVO reqVO) {
if (reqVO.getOwnerUserId() != null) {
adminUserApi.validateUser(reqVO.getOwnerUserId()); // 鏍¢獙璐熻矗浜哄瓨鍦�
@@ -167,8 +208,10 @@
.setContractId(oldReceivable.getContractId()).setPlanId(oldReceivable.getPlanId()); // 璁剧疆宸插瓨鍦ㄧ殑鍊�
// 1.2 鏍¢獙鍙洖娆鹃噾棰濊秴杩囦笂闄�
validateReceivablePriceExceedsLimit(updateReqVO);
+ // 1.3 鏍¢獙鍙洖娆鹃噾棰濅笉瓒呰繃绱宸插紑绁ㄩ噾棰濓紙鍏堝紑绁ㄥ悗鍥炴锛�
+ validateReceivablePriceExceedsInvoiceLimit(updateReqVO);
- // 1.3 鍙湁鑽夌銆佸鎵逛腑锛屽彲浠ョ紪杈戯紱
+ // 1.4 鍙湁鑽夌銆佸鎵逛腑锛屽彲浠ョ紪杈戯紱
if (!ObjectUtils.equalsAny(oldReceivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(),
CrmAuditStatusEnum.PROCESS.getStatus())) {
throw exception(RECEIVABLE_UPDATE_FAIL_EDITING_PROHIBITED);
--
Gitblit v1.9.3