package cn.iocoder.yudao.module.crm.service.receivable;
|
|
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.ListUtil;
|
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.util.ObjUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
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.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;
|
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;
|
import jakarta.annotation.Resource;
|
import org.springframework.context.annotation.Lazy;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.validation.annotation.Validated;
|
|
import java.math.BigDecimal;
|
import java.util.*;
|
|
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.*;
|
|
/**
|
* CRM 回款 Service 实现类
|
*
|
* @author 赤焰
|
*/
|
@Service
|
@Validated
|
public class CrmReceivableServiceImpl implements CrmReceivableService {
|
|
@Resource
|
private CrmReceivableMapper receivableMapper;
|
@Resource
|
private CrmReceivableItemMapper receivableItemMapper;
|
|
@Resource
|
private ErpSaleOrderApi saleOrderApi;
|
@Resource
|
private MesWmProductSalesApi productSalesApi;
|
|
@Resource
|
private CrmNoRedisDAO noRedisDAO;
|
|
@Resource
|
private CrmContractService contractService;
|
@Resource
|
@Lazy // 延迟加载,避免循环依赖
|
private CrmReceivablePlanService receivablePlanService;
|
@Resource
|
private CrmPermissionService permissionService;
|
@Resource
|
private CrmInvoiceService invoiceService;
|
|
@Resource
|
private AdminUserApi adminUserApi;
|
@Resource
|
private StorageAttachmentApi storageAttachmentApi;
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
@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 校验关联数据存在
|
validateRelationDataExists(createReqVO);
|
// 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 插入回款(创建后即为未审核状态)
|
CrmReceivableDO receivable = BeanUtils.toBean(createReqVO, CrmReceivableDO.class)
|
.setNo(no).setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus());
|
receivableMapper.insert(receivable);
|
// 2.2 保存核销明细(未传时按关联开票自动生成一条)
|
syncReceivableItems(receivable, createReqVO.getItems());
|
|
// 3. 创建数据权限
|
permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_RECEIVABLE.getType())
|
.setBizId(receivable.getId()).setUserId(createReqVO.getOwnerUserId())
|
.setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
|
|
// 4. 更新关联的回款计划
|
if (createReqVO.getPlanId() != null) {
|
receivablePlanService.updateReceivablePlanReceivableId(receivable.getPlanId(), receivable.getId());
|
}
|
|
// 5. 记录操作日志上下文
|
LogRecordContext.putVariable("receivable", receivable);
|
LogRecordContext.putVariable("period", getReceivablePeriod(receivable.getPlanId()));
|
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());
|
// 统计口径:草稿 + 审批中 + 审批通过(与开票级校验、统计方法 selectReceivablePriceMapByContractId 保持一致)
|
List<CrmReceivableDO> receivables = receivableMapper.selectListByContractIdAndStatus(reqVO.getContractId(),
|
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 notReceivablePrice = contract.getTotalPrice().subtract(
|
CollectionUtils.getSumValue(receivables, CrmReceivableDO::getPrice, BigDecimal::add, BigDecimal.ZERO));
|
// 2. 校验金额是否超过
|
if (reqVO.getPrice().compareTo(notReceivablePrice) > 0) {
|
throw exception(RECEIVABLE_CREATE_FAIL_PRICE_EXCEEDS_LIMIT, notReceivablePrice);
|
}
|
}
|
|
/**
|
* 校验关联开票合格:存在、审批通过、已上传发票附件、与所选合同一致
|
*/
|
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. 校验本次回款金额不超过该开票剩余可回款金额(开票金额 - 已回款金额)
|
BigDecimal availablePrice = invoice.getPrice().subtract(receivablePriceSum);
|
if (reqVO.getPrice().compareTo(availablePrice) > 0) {
|
throw exception(RECEIVABLE_CREATE_FAIL_INVOICE_PRICE_EXCEEDS, availablePrice);
|
}
|
}
|
|
private void validateRelationDataExists(CrmReceivableSaveReqVO reqVO) {
|
if (reqVO.getOwnerUserId() != null) {
|
adminUserApi.validateUser(reqVO.getOwnerUserId()); // 校验负责人存在
|
}
|
if (reqVO.getContractId() != null) {
|
CrmContractDO contract = contractService.validateContract(reqVO.getContractId());
|
if (ObjectUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.APPROVE.getStatus())) {
|
throw exception(RECEIVABLE_CREATE_FAIL_CONTRACT_NOT_APPROVE);
|
}
|
reqVO.setCustomerId(contract.getCustomerId()); // 设置客户编号
|
}
|
if (reqVO.getPlanId() != null) {
|
CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(reqVO.getPlanId());
|
if (receivablePlan == null) {
|
throw exception(RECEIVABLE_PLAN_NOT_EXISTS);
|
}
|
if (receivablePlan.getReceivableId() != null) {
|
throw exception(RECEIVABLE_PLAN_EXISTS_RECEIVABLE);
|
}
|
}
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
@LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
|
success = CRM_RECEIVABLE_UPDATE_SUCCESS)
|
@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); // 不允许修改的字段(invoiceId 允许修改)
|
// 1.1 校验存在
|
CrmReceivableDO oldReceivable = validateReceivableExists(updateReqVO.getId());
|
updateReqVO.setOwnerUserId(oldReceivable.getOwnerUserId()).setCustomerId(oldReceivable.getCustomerId())
|
.setContractId(oldReceivable.getContractId()).setPlanId(oldReceivable.getPlanId()); // 设置已存在的值
|
// 1.2 若未传递关联开票,沿用旧值
|
if (updateReqVO.getInvoiceId() == null) {
|
updateReqVO.setInvoiceId(oldReceivable.getInvoiceId());
|
}
|
// 1.3 校验可回款金额不超过合同剩余可回款金额
|
validateReceivablePriceExceedsLimit(updateReqVO);
|
// 1.4 校验关联开票合格
|
validateInvoiceForReceivable(updateReqVO);
|
// 1.5 校验可回款金额不超过该开票剩余可回款金额
|
validateReceivablePriceExceedsInvoiceRemaining(updateReqVO);
|
|
// 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()); // 避免操作日志出现“删除负责人”的情况
|
LogRecordContext.putVariable("oldReceivable", oldReceivable);
|
LogRecordContext.putVariable("period", getReceivablePeriod(oldReceivable.getPlanId()));
|
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldReceivable, CrmReceivableSaveReqVO.class));
|
}
|
|
private Integer getReceivablePeriod(Long planId) {
|
if (Objects.isNull(planId)) {
|
return null;
|
}
|
CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(planId);
|
return receivablePlan.getPeriod();
|
}
|
|
@Override
|
@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 (receivable.getAuditStatus().equals(status)) {
|
throw exception(approve ? RECEIVABLE_APPROVE_FAIL : RECEIVABLE_PROCESS_FAIL);
|
}
|
|
// 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
|
@Transactional(rollbackFor = Exception.class)
|
@LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_DELETE_SUB_TYPE, bizNo = "{{#id}}",
|
success = CRM_RECEIVABLE_DELETE_SUCCESS)
|
@CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
|
public void deleteReceivable(Long id) {
|
// 1.1 校验存在
|
CrmReceivableDO receivable = validateReceivableExists(id);
|
// 1.2 如果被 CrmReceivablePlanDO 所使用,则不允许删除
|
if (receivable.getPlanId() != null && receivablePlanService.getReceivablePlan(receivable.getPlanId()) != null) {
|
throw exception(RECEIVABLE_DELETE_FAIL);
|
}
|
// 1.3 审批通过时,不允许删除
|
if (ObjUtil.equal(receivable.getAuditStatus(), CrmAuditStatusEnum.APPROVE.getStatus())) {
|
throw exception(RECEIVABLE_DELETE_FAIL_IS_APPROVE);
|
}
|
|
// 2.1 删除回款
|
receivableMapper.deleteById(id);
|
// 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()));
|
}
|
|
private CrmReceivableDO validateReceivableExists(Long id) {
|
CrmReceivableDO receivable = receivableMapper.selectById(id);
|
if (receivable == null) {
|
throw exception(RECEIVABLE_NOT_EXISTS);
|
}
|
return receivable;
|
}
|
|
@Override
|
@CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#id", level = CrmPermissionLevelEnum.READ)
|
public CrmReceivableDO getReceivable(Long id) {
|
return receivableMapper.selectById(id);
|
}
|
|
@Override
|
public List<CrmReceivableDO> getReceivableList(Collection<Long> ids) {
|
if (CollUtil.isEmpty(ids)) {
|
return ListUtil.empty();
|
}
|
return receivableMapper.selectByIds(ids);
|
}
|
|
@Override
|
public PageResult<CrmReceivableDO> getReceivablePage(CrmReceivablePageReqVO pageReqVO, Long userId) {
|
return receivableMapper.selectPage(pageReqVO, userId);
|
}
|
|
@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);
|
}
|
|
@Override
|
public Long getAuditReceivableCount(Long userId) {
|
return receivableMapper.selectCountByAudit(userId);
|
}
|
|
@Override
|
public Map<Long, BigDecimal> getReceivablePriceMapByContractId(Collection<Long> contractIds) {
|
return receivableMapper.selectReceivablePriceMapByContractId(contractIds);
|
}
|
|
@Override
|
public Map<Long, BigDecimal> getReceivablePriceMapByInvoiceId(Collection<Long> invoiceIds) {
|
return receivableMapper.selectReceivablePriceMapByInvoiceId(invoiceIds);
|
}
|
|
@Override
|
public Long getReceivableCountByContractId(Long contractId) {
|
return receivableMapper.selectCountByContractId(contractId);
|
}
|
|
@Override
|
public BigDecimal getReceivablePriceByContractId(Long contractId) {
|
Map<Long, BigDecimal> priceMap = receivableMapper.selectReceivablePriceMapByContractId(
|
java.util.Collections.singleton(contractId));
|
return priceMap.getOrDefault(contractId, BigDecimal.ZERO);
|
}
|
|
@Override
|
public BigDecimal getReceivablePriceSum() {
|
return receivableMapper.selectReceivablePriceSum();
|
}
|
|
@Override
|
public BigDecimal getReceivablePriceByOwnerUserId(Long ownerUserId) {
|
return receivableMapper.selectReceivablePriceSumByOwnerUserId(ownerUserId);
|
}
|
|
}
|