From d39e4f7d839167c16425086dfa2f8747bccfdff9 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期四, 05 十一月 2026 17:39:01 +0800
Subject: [PATCH] feat: 打通销售采购财务闭环并完成累计业务优化
---
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java | 334 ++++++++++++++++++++++++++++++++++++++++++-------------
1 files changed, 252 insertions(+), 82 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 0652c25..08d647c 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
@@ -8,20 +8,24 @@
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
-import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
-import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
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.CrmReceivableItemDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
+import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivableItemMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivableMapper;
import cn.iocoder.yudao.module.crm.dal.redis.no.CrmNoRedisDAO;
import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
+import cn.iocoder.yudao.module.crm.enums.receivable.CrmReceivableBizTypeEnum;
+import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi;
+import cn.iocoder.yudao.module.erp.api.sale.dto.ErpSaleOrderRespDTO;
+import cn.iocoder.yudao.module.mes.api.productsales.MesWmProductSalesApi;
+import cn.iocoder.yudao.module.mes.api.productsales.dto.MesWmProductSalesRespDTO;
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;
@@ -34,7 +38,6 @@
import com.mzt.logapi.service.impl.DiffParseFunction;
import com.mzt.logapi.starter.annotation.LogRecord;
import jakarta.annotation.Resource;
-import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -46,7 +49,6 @@
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
-import static cn.iocoder.yudao.module.crm.util.CrmAuditStatusUtils.convertBpmResultToAuditStatus;
/**
* CRM 鍥炴 Service 瀹炵幇绫�
@@ -55,16 +57,17 @@
*/
@Service
@Validated
-@Slf4j
public class CrmReceivableServiceImpl implements CrmReceivableService {
-
- /**
- * BPM 鍚堝悓瀹℃壒娴佺▼鏍囪瘑
- */
- public static final String BPM_PROCESS_DEFINITION_KEY = "crm-receivable-audit";
@Resource
private CrmReceivableMapper receivableMapper;
+ @Resource
+ private CrmReceivableItemMapper receivableItemMapper;
+
+ @Resource
+ private ErpSaleOrderApi saleOrderApi;
+ @Resource
+ private MesWmProductSalesApi productSalesApi;
@Resource
private CrmNoRedisDAO noRedisDAO;
@@ -82,8 +85,6 @@
@Resource
private AdminUserApi adminUserApi;
@Resource
- private BpmProcessInstanceApi bpmProcessInstanceApi;
- @Resource
private StorageAttachmentApi storageAttachmentApi;
@Override
@@ -91,23 +92,26 @@
@LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_CREATE_SUB_TYPE, bizNo = "{{#receivable.id}}",
success = CRM_RECEIVABLE_CREATE_SUCCESS)
public Long createReceivable(CrmReceivableSaveReqVO createReqVO) {
- // 1.1 鏍¢獙鍙洖娆鹃噾棰濊秴杩囦笂闄�
- validateReceivablePriceExceedsLimit(createReqVO);
- // 1.2 鏍¢獙鍙洖娆鹃噾棰濅笉瓒呰繃绱宸插紑绁ㄩ噾棰濓紙鍏堝紑绁ㄥ悗鍥炴锛�
- validateReceivablePriceExceedsInvoiceLimit(createReqVO);
- // 1.3 鏍¢獙鍏宠仈鏁版嵁瀛樺湪
+ // 1.1 鏍¢獙鍏宠仈鏁版嵁瀛樺湪
validateRelationDataExists(createReqVO);
- // 1.4 鐢熸垚鍥炴缂栧彿
+ // 1.2 鏍¢獙鍏宠仈寮�绁ㄥ悎鏍硷紙瀹℃壒閫氳繃 + 宸蹭笂浼犲彂绁ㄩ檮浠� + 涓庡悎鍚屽尮閰嶏級
+ validateInvoiceForReceivable(createReqVO);
+ // 1.3 鏍¢獙鍙洖娆鹃噾棰濅笉瓒呰繃鍚堝悓鍓╀綑鍙洖娆鹃噾棰�
+ validateReceivablePriceExceedsLimit(createReqVO);
+ // 1.4 鏍¢獙鍙洖娆鹃噾棰濅笉瓒呰繃璇ュ紑绁ㄥ墿浣欏彲鍥炴閲戦锛堝厛寮�绁ㄥ悗鍥炴锛�
+ validateReceivablePriceExceedsInvoiceRemaining(createReqVO);
+ // 1.5 鐢熸垚鍥炴缂栧彿
String no = noRedisDAO.generate(CrmNoRedisDAO.RECEIVABLE_PREFIX);
if (receivableMapper.selectByNo(no) != null) {
throw exception(RECEIVABLE_NO_EXISTS);
}
- // 2.1 鎻掑叆鍥炴
+ // 2.1 鎻掑叆鍥炴锛堝垱寤哄悗鍗充负鏈鏍哥姸鎬侊級
CrmReceivableDO receivable = BeanUtils.toBean(createReqVO, CrmReceivableDO.class)
- .setNo(no).setAuditStatus(CrmAuditStatusEnum.DRAFT.getStatus());
+ .setNo(no).setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus());
receivableMapper.insert(receivable);
- // 2.2
+ // 2.2 淇濆瓨鏍搁攢鏄庣粏锛堟湭浼犳椂鎸夊叧鑱斿紑绁ㄨ嚜鍔ㄧ敓鎴愪竴鏉★級
+ syncReceivableItems(receivable, createReqVO.getItems());
// 3. 鍒涘缓鏁版嵁鏉冮檺
permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_RECEIVABLE.getType())
@@ -125,11 +129,128 @@
return receivable.getId();
}
+ /**
+ * 淇濆瓨鏍搁攢鏄庣粏
+ *
+ * 浼� null 鏃讹細鑻ュ叧鑱斿紑绁紝鑷姩鐢熸垚涓�鏉°�屽紑绁ㄣ�嶆牳閿�鏄庣粏锛涘惁鍒欎笉鐢熸垚銆�
+ */
+ private void syncReceivableItems(CrmReceivableDO receivable,
+ List<CrmReceivableSaveReqVO.ReceivableItem> items) {
+ if (items == null) {
+ if (receivable.getInvoiceId() == null) {
+ return;
+ }
+ CrmReceivableSaveReqVO.ReceivableItem autoItem = new CrmReceivableSaveReqVO.ReceivableItem();
+ autoItem.setBizType(CrmReceivableBizTypeEnum.SALE_INVOICE.getType());
+ autoItem.setBizId(receivable.getInvoiceId());
+ autoItem.setReceiptPrice(receivable.getPrice());
+ items = List.of(autoItem);
+ }
+ // 1. 鏍¢獙骞惰В鏋愭瘡琛屽叧鑱斿崟鎹�
+ List<ResolvedReceivableItem> resolvedItems = new ArrayList<>(items.size());
+ BigDecimal itemPriceSum = BigDecimal.ZERO;
+ for (int i = 0; i < items.size(); i++) {
+ CrmReceivableSaveReqVO.ReceivableItem item = items.get(i);
+ int row = i + 1;
+ CrmReceivableBizTypeEnum bizType = item == null ? null
+ : CrmReceivableBizTypeEnum.valueOf(item.getBizType());
+ if (bizType == null) {
+ throw exception(RECEIVABLE_ITEM_BIZ_TYPE_INVALID, row,
+ item == null ? "鏈~鍐�" : item.getBizType());
+ }
+ if (item.getReceiptPrice() == null || item.getReceiptPrice().compareTo(BigDecimal.ZERO) <= 0) {
+ throw exception(RECEIVABLE_ITEM_PRICE_INVALID, row, bizType.getName(),
+ item.getBizId(), item.getReceiptPrice() == null ? "鏈~鍐�"
+ : item.getReceiptPrice().stripTrailingZeros().toPlainString());
+ }
+ ResolvedReceivableItem resolved = resolveReceivableItem(row, bizType, item.getBizId());
+ // 1.1 宸叉牳閿�閲戦锛堜笉鍚湰鍥炴鍗曪紝閬垮厤缂栬緫鏃舵妸鑷繁绠楄繘鍘伙級
+ BigDecimal receiptedPrice = receivableItemMapper.selectListByBiz(bizType.getType(), item.getBizId()).stream()
+ .filter(old -> ObjectUtil.notEqual(old.getReceivableId(), receivable.getId()))
+ .map(old -> old.getReceiptPrice() == null ? BigDecimal.ZERO : old.getReceiptPrice())
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
+ BigDecimal remainPrice = resolved.totalPrice.subtract(receiptedPrice);
+ if (item.getReceiptPrice().compareTo(remainPrice) > 0) {
+ throw exception(RECEIVABLE_ITEM_PRICE_EXCEEDS, row, bizType.getName(), resolved.bizNo,
+ item.getReceiptPrice().stripTrailingZeros().toPlainString(),
+ remainPrice.stripTrailingZeros().toPlainString(),
+ resolved.totalPrice.stripTrailingZeros().toPlainString(),
+ receiptedPrice.stripTrailingZeros().toPlainString());
+ }
+ resolved.receiptedPrice = receiptedPrice;
+ resolved.receiptPrice = item.getReceiptPrice();
+ resolved.remark = item.getRemark();
+ resolvedItems.add(resolved);
+ itemPriceSum = itemPriceSum.add(item.getReceiptPrice());
+ }
+ // 2. 鏄庣粏鍚堣蹇呴』绛変簬鍥炴閲戦
+ if (itemPriceSum.compareTo(receivable.getPrice()) != 0) {
+ throw exception(RECEIVABLE_ITEM_SUM_MISMATCH,
+ itemPriceSum.stripTrailingZeros().toPlainString(),
+ receivable.getPrice().stripTrailingZeros().toPlainString());
+ }
+ // 3. 閲嶅缓鏄庣粏
+ receivableItemMapper.deleteByReceivableId(receivable.getId());
+ for (ResolvedReceivableItem resolved : resolvedItems) {
+ receivableItemMapper.insert(CrmReceivableItemDO.builder()
+ .receivableId(receivable.getId()).bizType(resolved.bizType.getType())
+ .bizId(resolved.bizId).bizNo(resolved.bizNo).totalPrice(resolved.totalPrice)
+ .receiptedPrice(resolved.receiptedPrice).receiptPrice(resolved.receiptPrice)
+ .remark(resolved.remark).build());
+ }
+ }
+
+ private ResolvedReceivableItem resolveReceivableItem(int row, CrmReceivableBizTypeEnum bizType, Long bizId) {
+ ResolvedReceivableItem resolved = new ResolvedReceivableItem();
+ resolved.bizType = bizType;
+ resolved.bizId = bizId;
+ switch (bizType) {
+ case SALE_ORDER -> {
+ ErpSaleOrderRespDTO saleOrder = bizId == null ? null : saleOrderApi.getSaleOrder(bizId).getCheckedData();
+ if (saleOrder == null) {
+ throw exception(RECEIVABLE_ITEM_BIZ_NOT_EXISTS, row, bizType.getName(), bizId);
+ }
+ resolved.bizNo = saleOrder.getNo();
+ resolved.totalPrice = saleOrder.getTotalPrice() == null ? BigDecimal.ZERO : saleOrder.getTotalPrice();
+ }
+ case SALE_OUT -> {
+ MesWmProductSalesRespDTO saleOut = bizId == null ? null : productSalesApi.getProductSales(bizId);
+ if (saleOut == null) {
+ throw exception(RECEIVABLE_ITEM_BIZ_NOT_EXISTS, row, bizType.getName(), bizId);
+ }
+ resolved.bizNo = saleOut.getCode();
+ resolved.totalPrice = saleOut.getTotalPrice() == null ? BigDecimal.ZERO : saleOut.getTotalPrice();
+ }
+ case SALE_INVOICE -> {
+ CrmInvoiceDO invoice = bizId == null ? null : invoiceService.getInvoice(bizId);
+ if (invoice == null) {
+ throw exception(RECEIVABLE_ITEM_BIZ_NOT_EXISTS, row, bizType.getName(), bizId);
+ }
+ resolved.bizNo = invoice.getNo();
+ resolved.totalPrice = invoice.getPrice() == null ? BigDecimal.ZERO : invoice.getPrice();
+ }
+ default -> throw exception(RECEIVABLE_ITEM_BIZ_TYPE_INVALID, row, bizType.getType());
+ }
+ return resolved;
+ }
+
+ private static class ResolvedReceivableItem {
+ private CrmReceivableBizTypeEnum bizType;
+ private Long bizId;
+ private String bizNo;
+ private BigDecimal totalPrice;
+ private BigDecimal receiptedPrice;
+ private BigDecimal receiptPrice;
+ private String remark;
+ }
+
private void validateReceivablePriceExceedsLimit(CrmReceivableSaveReqVO reqVO) {
// 1. 璁$畻鍓╀綑鍙��娆鹃噾棰濓紝涓嶅寘鎷� reqVO 鑷韩
CrmContractDO contract = contractService.validateContract(reqVO.getContractId());
+ // 缁熻鍙e緞锛氳崏绋� + 瀹℃壒涓� + 瀹℃壒閫氳繃锛堜笌寮�绁ㄧ骇鏍¢獙銆佺粺璁℃柟娉� selectReceivablePriceMapByContractId 淇濇寔涓�鑷达級
List<CrmReceivableDO> receivables = receivableMapper.selectListByContractIdAndStatus(reqVO.getContractId(),
- Arrays.asList(CrmAuditStatusEnum.APPROVE.getStatus(), CrmAuditStatusEnum.PROCESS.getStatus()));
+ Arrays.asList(CrmAuditStatusEnum.DRAFT.getStatus(), CrmAuditStatusEnum.PROCESS.getStatus(),
+ CrmAuditStatusEnum.APPROVE.getStatus()));
if (reqVO.getId() != null) {
receivables.removeIf(receivable -> ObjectUtil.equal(receivable.getId(), reqVO.getId()));
}
@@ -142,33 +263,60 @@
}
/**
- * 鏍¢獙绱鍥炴閲戦涓嶈秴杩囩疮璁″凡寮�绁ㄤ笖宸蹭笂浼犲彂绁ㄩ檮浠剁殑閲戦锛堝厛寮�绁ㄥ悗鍥炴锛�
+ * 鏍¢獙鍏宠仈寮�绁ㄥ悎鏍硷細瀛樺湪銆佸鎵归�氳繃銆佸凡涓婁紶鍙戠エ闄勪欢銆佷笌鎵�閫夊悎鍚屼竴鑷�
*/
- private void validateReceivablePriceExceedsInvoiceLimit(CrmReceivableSaveReqVO reqVO) {
- // 1. 璁$畻鍚堝悓绱鍥炴閲戦锛堜笉鍚湰娆★紝缁熻鍙e緞鍚屽悎鍚岄噾棰濇牎楠岋細瀹℃壒閫氳繃 + 瀹℃壒涓級
- List<CrmReceivableDO> receivables = receivableMapper.selectListByContractIdAndStatus(reqVO.getContractId(),
- Arrays.asList(CrmAuditStatusEnum.APPROVE.getStatus(), CrmAuditStatusEnum.PROCESS.getStatus()));
+ private void validateInvoiceForReceivable(CrmReceivableSaveReqVO reqVO) {
+ // 鏈叧鑱斿紑绁ㄦ椂鐩存帴璺宠繃锛堝厑璁稿厛鍥炴鍚庡紑绁級
+ if (reqVO.getInvoiceId() == null) {
+ return;
+ }
+ // 1. 鏍¢獙寮�绁ㄥ瓨鍦�
+ CrmInvoiceDO invoice = invoiceService.getInvoice(reqVO.getInvoiceId());
+ if (invoice == null) {
+ throw exception(RECEIVABLE_CREATE_FAIL_INVOICE_NOT_EXISTS);
+ }
+ // 2. 鏍¢獙寮�绁ㄥ鎵归�氳繃
+ if (ObjectUtil.notEqual(invoice.getAuditStatus(), CrmAuditStatusEnum.APPROVE.getStatus())) {
+ throw exception(RECEIVABLE_CREATE_FAIL_INVOICE_NOT_APPROVE);
+ }
+ // 3. 鏍¢獙寮�绁ㄥ凡涓婁紶鍙戠エ闄勪欢
+ if (CollUtil.isEmpty(storageAttachmentApi.listAttachments(
+ StorageRecordTypeEnum.CRM_INVOICE.getType(), invoice.getId()))) {
+ throw exception(RECEIVABLE_CREATE_FAIL_INVOICE_NOT_ATTACHMENT);
+ }
+ // 4. 鏍¢獙寮�绁ㄤ笌鎵�閫夊悎鍚屼竴鑷�
+ if (ObjectUtil.notEqual(invoice.getContractId(), reqVO.getContractId())) {
+ throw exception(RECEIVABLE_CREATE_FAIL_INVOICE_CONTRACT_MISMATCH);
+ }
+ // 5. 鏍¢獙寮�绁ㄤ笌鎵�閫夊悎鍚屽鎴蜂竴鑷达紙闃插尽鑴忔暟鎹細鍚堝悓-寮�绁�-鍥炴涓�瀵瑰瀵瑰锛屽紑绁ㄥ繀椤诲睘浜庤鍚堝悓瀹㈡埛锛�
+ if (reqVO.getCustomerId() != null && ObjectUtil.notEqual(invoice.getCustomerId(), reqVO.getCustomerId())) {
+ throw exception(RECEIVABLE_CREATE_FAIL_INVOICE_CONTRACT_MISMATCH);
+ }
+ }
+
+ /**
+ * 鏍¢獙绱鍥炴閲戦涓嶈秴杩囪寮�绁ㄥ墿浣欏彲鍥炴閲戦锛堝厛寮�绁ㄥ悗鍥炴锛�
+ */
+ private void validateReceivablePriceExceedsInvoiceRemaining(CrmReceivableSaveReqVO reqVO) {
+ // 鏈叧鑱斿紑绁ㄦ椂鐩存帴璺宠繃锛堥噾棰濅笂闄愪粛鐢卞悎鍚岀骇鏍¢獙 validateReceivablePriceExceedsLimit 鍏滃簳锛�
+ if (reqVO.getInvoiceId() == null) {
+ return;
+ }
+ // 1. 璁$畻璇ュ紑绁ㄧ疮璁″洖娆鹃噾棰濓紙涓嶅惈鏈锛岀粺璁″彛寰勫悓鍚堝悓閲戦鏍¢獙锛氳崏绋� + 瀹℃壒涓� + 瀹℃壒閫氳繃锛�
+ CrmInvoiceDO invoice = invoiceService.getInvoice(reqVO.getInvoiceId());
+ List<CrmReceivableDO> receivables = receivableMapper.selectListByInvoiceIdAndStatus(reqVO.getInvoiceId(),
+ Arrays.asList(CrmAuditStatusEnum.DRAFT.getStatus(), CrmAuditStatusEnum.PROCESS.getStatus(),
+ CrmAuditStatusEnum.APPROVE.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);
+ // 2. 鏍¢獙鏈鍥炴閲戦涓嶈秴杩囪寮�绁ㄥ墿浣欏彲鍥炴閲戦锛堝紑绁ㄩ噾棰� - 宸插洖娆鹃噾棰濓級
+ BigDecimal availablePrice = invoice.getPrice().subtract(receivablePriceSum);
if (reqVO.getPrice().compareTo(availablePrice) > 0) {
- throw exception(RECEIVABLE_CREATE_FAIL_INVOICE_NOT_ENOUGH, availablePrice);
+ throw exception(RECEIVABLE_CREATE_FAIL_INVOICE_PRICE_EXCEEDS, availablePrice);
}
}
@@ -201,25 +349,35 @@
@CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
public void updateReceivable(CrmReceivableSaveReqVO updateReqVO) {
Assert.notNull(updateReqVO.getId(), "鍥炴缂栧彿涓嶈兘涓虹┖");
- updateReqVO.setOwnerUserId(null).setCustomerId(null).setContractId(null).setPlanId(null); // 涓嶅厑璁镐慨鏀圭殑瀛楁
+ updateReqVO.setOwnerUserId(null).setCustomerId(null).setContractId(null).setPlanId(null); // 涓嶅厑璁镐慨鏀圭殑瀛楁锛坕nvoiceId 鍏佽淇敼锛�
// 1.1 鏍¢獙瀛樺湪
CrmReceivableDO oldReceivable = validateReceivableExists(updateReqVO.getId());
updateReqVO.setOwnerUserId(oldReceivable.getOwnerUserId()).setCustomerId(oldReceivable.getCustomerId())
.setContractId(oldReceivable.getContractId()).setPlanId(oldReceivable.getPlanId()); // 璁剧疆宸插瓨鍦ㄧ殑鍊�
- // 1.2 鏍¢獙鍙洖娆鹃噾棰濊秴杩囦笂闄�
+ // 1.2 鑻ユ湭浼犻�掑叧鑱斿紑绁紝娌跨敤鏃у��
+ if (updateReqVO.getInvoiceId() == null) {
+ updateReqVO.setInvoiceId(oldReceivable.getInvoiceId());
+ }
+ // 1.3 鏍¢獙鍙洖娆鹃噾棰濅笉瓒呰繃鍚堝悓鍓╀綑鍙洖娆鹃噾棰�
validateReceivablePriceExceedsLimit(updateReqVO);
- // 1.3 鏍¢獙鍙洖娆鹃噾棰濅笉瓒呰繃绱宸插紑绁ㄩ噾棰濓紙鍏堝紑绁ㄥ悗鍥炴锛�
- validateReceivablePriceExceedsInvoiceLimit(updateReqVO);
+ // 1.4 鏍¢獙鍏宠仈寮�绁ㄥ悎鏍�
+ validateInvoiceForReceivable(updateReqVO);
+ // 1.5 鏍¢獙鍙洖娆鹃噾棰濅笉瓒呰繃璇ュ紑绁ㄥ墿浣欏彲鍥炴閲戦
+ validateReceivablePriceExceedsInvoiceRemaining(updateReqVO);
- // 1.4 鍙湁鑽夌銆佸鎵逛腑锛屽彲浠ョ紪杈戯紱
- if (!ObjectUtils.equalsAny(oldReceivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(),
- CrmAuditStatusEnum.PROCESS.getStatus())) {
+ // 1.6 鍙湁鏈鏍革紝鍙互缂栬緫锛�
+ if (ObjectUtil.notEqual(oldReceivable.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) {
throw exception(RECEIVABLE_UPDATE_FAIL_EDITING_PROHIBITED);
}
// 2. 鏇存柊鍥炴
CrmReceivableDO updateObj = BeanUtils.toBean(updateReqVO, CrmReceivableDO.class);
receivableMapper.updateById(updateObj);
+ // 2.1 鏇存柊鏍搁攢鏄庣粏锛氫紶浜� items 浠� items 涓哄噯锛涙湭浼犱笖鍏宠仈寮�绁ㄦ椂鎸夊紑绁ㄩ噸寤猴紱鍚﹀垯淇濇寔涓嶅彉
+ if (updateReqVO.getItems() != null
+ || (updateReqVO.getInvoiceId() != null && ObjectUtil.notEqual(updateReqVO.getInvoiceId(), oldReceivable.getInvoiceId()))) {
+ syncReceivableItems(updateObj, updateReqVO.getItems());
+ }
// 3. 璁板綍鎿嶄綔鏃ュ織涓婁笅鏂�
updateReqVO.setOwnerUserId(oldReceivable.getOwnerUserId()); // 閬垮厤鎿嶄綔鏃ュ織鍑虹幇鈥滃垹闄よ礋璐d汉鈥濈殑鎯呭喌
@@ -237,19 +395,32 @@
}
@Override
- public void updateReceivableAuditStatus(Long id, Integer bpmResult) {
+ @Transactional(rollbackFor = Exception.class)
+ public void updateReceivableStatus(Long id, Integer status) {
+ boolean approve = CrmAuditStatusEnum.APPROVE.getStatus().equals(status);
// 1.1 鏍¢獙瀛樺湪
CrmReceivableDO receivable = validateReceivableExists(id);
- // 1.2 鍙湁瀹℃壒涓紝鍙互鏇存柊瀹℃壒缁撴灉
- if (ObjUtil.notEqual(receivable.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) {
- log.error("[updateReceivableAuditStatus][receivable({}) 涓嶅浜庡鎵逛腑锛屾棤娉曟洿鏂板鎵圭粨鏋�({})]",
- receivable.getId(), bpmResult);
- throw exception(RECEIVABLE_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS);
+ // 1.2 鏍¢獙鐘舵��
+ if (receivable.getAuditStatus().equals(status)) {
+ throw exception(approve ? RECEIVABLE_APPROVE_FAIL : RECEIVABLE_PROCESS_FAIL);
}
- // 2. 鏇存柊鍥炴瀹℃壒鐘舵��
- Integer auditStatus = convertBpmResultToAuditStatus(bpmResult);
- receivableMapper.updateById(new CrmReceivableDO().setId(id).setAuditStatus(auditStatus));
+ // 2. 鏇存柊鐘舵��
+ int updateCount = receivableMapper.updateByIdAndStatus(id, receivable.getAuditStatus(),
+ new CrmReceivableDO().setAuditStatus(status));
+ if (updateCount == 0) {
+ throw exception(approve ? RECEIVABLE_APPROVE_FAIL : RECEIVABLE_PROCESS_FAIL);
+ }
+
+ // 3. 鍥炴璁″垝鍥炲啓瀹為檯鍥炴閲戦涓庣姸鎬�
+ if (receivable.getPlanId() != null) {
+ if (approve) {
+ receivablePlanService.updateReceivablePlanReceipted(receivable.getPlanId(),
+ receivable.getPrice(), receivable.getReturnTime());
+ } else {
+ receivablePlanService.resetReceivablePlanReceipted(receivable.getPlanId());
+ }
+ }
}
@Override
@@ -271,35 +442,14 @@
// 2.1 鍒犻櫎鍥炴
receivableMapper.deleteById(id);
- // 2.2 鍒犻櫎鏁版嵁鏉冮檺
+ // 2.2 鍒犻櫎鏍搁攢鏄庣粏
+ receivableItemMapper.deleteByReceivableId(id);
+ // 2.3 鍒犻櫎鏁版嵁鏉冮檺
permissionService.deletePermission(CrmBizTypeEnum.CRM_RECEIVABLE.getType(), id);
// 3. 璁板綍鎿嶄綔鏃ュ織涓婁笅鏂�
LogRecordContext.putVariable("receivable", receivable);
LogRecordContext.putVariable("period", getReceivablePeriod(receivable.getPlanId()));
- }
-
- @Override
- @Transactional(rollbackFor = Exception.class)
- @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_SUBMIT_SUB_TYPE, bizNo = "{{#id}}",
- success = CRM_RECEIVABLE_SUBMIT_SUCCESS)
- public void submitReceivable(Long id, Long userId) {
- // 1. 鏍¢獙鍥炴鏄惁鍦ㄥ鎵�
- CrmReceivableDO receivable = validateReceivableExists(id);
- if (ObjUtil.notEqual(receivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus())) {
- throw exception(RECEIVABLE_SUBMIT_FAIL_NOT_DRAFT);
- }
-
- // 2. 鍒涘缓鍥炴瀹℃壒娴佺▼瀹炰緥
- String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
- .setProcessDefinitionKey(BPM_PROCESS_DEFINITION_KEY).setBusinessKey(String.valueOf(id)));
-
- // 3. 鏇存柊鍥炴宸ヤ綔娴佺紪鍙�
- receivableMapper.updateById(new CrmReceivableDO().setId(id).setProcessInstanceId(processInstanceId)
- .setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus()));
-
- // 4. 璁板綍鏃ュ織
- LogRecordContext.putVariable("receivableNo", receivable.getNo());
}
private CrmReceivableDO validateReceivableExists(Long id) {
@@ -330,6 +480,21 @@
}
@Override
+ public List<CrmReceivableItemDO> getReceivableItemList(Long receivableId) {
+ return receivableItemMapper.selectListByReceivableId(receivableId);
+ }
+
+ @Override
+ public List<CrmReceivableItemDO> getReceivableItemListByCustomerId(Long customerId) {
+ List<CrmReceivableDO> receivables = receivableMapper.selectListByCustomerId(customerId);
+ if (CollUtil.isEmpty(receivables)) {
+ return ListUtil.empty();
+ }
+ return receivableItemMapper.selectListByReceivableIds(
+ receivables.stream().map(CrmReceivableDO::getId).toList());
+ }
+
+ @Override
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageReqVO.customerId", level = CrmPermissionLevelEnum.READ)
public PageResult<CrmReceivableDO> getReceivablePageByCustomerId(CrmReceivablePageReqVO pageReqVO) {
return receivableMapper.selectPageByCustomerId(pageReqVO);
@@ -346,6 +511,11 @@
}
@Override
+ public Map<Long, BigDecimal> getReceivablePriceMapByInvoiceId(Collection<Long> invoiceIds) {
+ return receivableMapper.selectReceivablePriceMapByInvoiceId(invoiceIds);
+ }
+
+ @Override
public Long getReceivableCountByContractId(Long contractId) {
return receivableMapper.selectCountByContractId(contractId);
}
--
Gitblit v1.9.3