1、软件购买是否纯赠送金判断购买记录是否可开票状态修复;2、组织过期判断需要包含当天完善;3、申请开票增加购买记录开票状态校验;

This commit is contained in:
gaoqr
2025-10-15 15:34:54 +08:00
parent 480fa25afa
commit 6e3f5db169
8 changed files with 106 additions and 20 deletions
@@ -90,4 +90,16 @@ public class AssertUtils {
throw exception(errorCode); throw exception(errorCode);
} }
} }
public static void isFalse(boolean expression, ErrorCode errorCode) {
if (!expression) {
throw exception(errorCode);
}
}
public static void isTrue(boolean expression, ErrorCode errorCode) {
if (expression) {
throw exception(errorCode);
}
}
} }
@@ -446,6 +446,8 @@ public class ErrorCodeConstants {
public static final ErrorCode PRODUCTS_NOT_NOLIST = new ErrorCode(1_002_043_033, "当前产品未上架"); public static final ErrorCode PRODUCTS_NOT_NOLIST = new ErrorCode(1_002_043_033, "当前产品未上架");
public static final ErrorCode ORG_PRODUCT_PURCHASE_LOCK_ERROR = new ErrorCode(1_002_043_034, "组织下存在购买中的产品操作,请稍后再试"); public static final ErrorCode ORG_PRODUCT_PURCHASE_LOCK_ERROR = new ErrorCode(1_002_043_034, "组织下存在购买中的产品操作,请稍后再试");
public static final ErrorCode INVOICE_TITLE_ENTERPRISE_TAXNUMBER_EMPTY_ERROR = new ErrorCode(1_002_043_035, "企业统一信用代码不能为空"); public static final ErrorCode INVOICE_TITLE_ENTERPRISE_TAXNUMBER_EMPTY_ERROR = new ErrorCode(1_002_043_035, "企业统一信用代码不能为空");
public static final ErrorCode INVOICE_AMOUNT_ZERO_ERROR = new ErrorCode(1_002_043_036, "可开票金额为,请检查购买记录");
public static final ErrorCode INVOICE_APPLY_UNINVOICEABLE_EXIST = new ErrorCode(1_002_043_036, "当前提交记录中存在不可开票记录,请刷新列表后重新选择提交");
//=========== 支付相关 1-002-048-000 ============ //=========== 支付相关 1-002-048-000 ============
public static final ErrorCode PAY_CHANNEL_NOT_SUPPORT = new ErrorCode(1_002_048_000, "暂不支持的支付渠道,请使用其他支付渠道"); public static final ErrorCode PAY_CHANNEL_NOT_SUPPORT = new ErrorCode(1_002_048_000, "暂不支持的支付渠道,请使用其他支付渠道");
@@ -63,12 +63,15 @@ public class OrganApiImpl implements OrganApi {
// 当前产品购买记录有效期是否超过当前时间 // 当前产品购买记录有效期是否超过当前时间
boolean productMatch = false; boolean productMatch = false;
if (ObjectUtil.isNotNull(previouPurchaseRecord)) { if (ObjectUtil.isNotNull(previouPurchaseRecord)) {
productMatch = previouPurchaseRecord.getEndTime().isAfter(LocalDate.now()); LocalDate endTime = previouPurchaseRecord.getEndTime();
productMatch = endTime.isAfter(LocalDate.now()) || endTime.isEqual(LocalDate.now());
} }
// 延期记录中是否有超过当前时间的记录 // 延期记录中是否有超过当前时间的记录
boolean delayMatch = false; boolean delayMatch = false;
if (CollUtil.isNotEmpty(delayRespVOList)) { if (CollUtil.isNotEmpty(delayRespVOList)) {
delayMatch = delayRespVOList.stream().anyMatch(item -> item.getDelayTime().isAfter(LocalDate.now())); delayMatch = delayRespVOList.stream()
.anyMatch(item -> item.getDelayTime().isAfter(LocalDate.now())
|| item.getDelayTime().isEqual(LocalDate.now()));
} }
// 产品过期且没有有效的延期记录时,提示产品已过期 // 产品过期且没有有效的延期记录时,提示产品已过期
if (!productMatch && !delayMatch) { if (!productMatch && !delayMatch) {
@@ -13,7 +13,7 @@ import java.util.List;
public class InvoiceRecordsReqVO { public class InvoiceRecordsReqVO {
@Schema(description = "购买记录ID") @Schema(description = "购买记录ID")
@Size(max = 300, message = "单次开票选择记录不能超过100条") @Size(max = 100, message = "单次开票选择记录不能超过100条")
@NotEmpty @NotEmpty
private List<Long> purchaseRecordId; private List<Long> purchaseRecordId;
@@ -47,6 +47,8 @@ public class InvoiceRecordsRespVO {
@Schema(description = "发票附件保存路径") @Schema(description = "发票附件保存路径")
private String invoiceAttachmentPath; private String invoiceAttachmentPath;
@Schema(description = "发票附件名称")
private String invoiceAttachmentName;
@Schema(description = "开票人") @Schema(description = "开票人")
private String invoicePerson; private String invoicePerson;
@@ -16,15 +16,15 @@ public interface InvoiceRecordsMapper extends BaseMapperX<InvoiceRecordsDO> {
default PageResult<InvoiceRecordsDO> selectInvoiceRecordsPage(InvoiceDetailsPageReqVO pageReqVO) { default PageResult<InvoiceRecordsDO> selectInvoiceRecordsPage(InvoiceDetailsPageReqVO pageReqVO) {
return selectPage(pageReqVO, new LambdaQueryWrapperX<InvoiceRecordsDO>() return selectPage(pageReqVO, new LambdaQueryWrapperX<InvoiceRecordsDO>()
.eq(InvoiceRecordsDO::getDeleted,false) .eq(InvoiceRecordsDO::getDeleted, false)
.eqIfPresent(InvoiceRecordsDO::getOrganId,pageReqVO.getOrganId()) .eqIfPresent(InvoiceRecordsDO::getOrganId, pageReqVO.getOrganId())
.likeIfPresent(InvoiceRecordsDO::getInvoiceNo,pageReqVO.getInvoiceNo()) .likeIfPresent(InvoiceRecordsDO::getInvoiceNo, pageReqVO.getInvoiceNo())
.eqIfPresent(InvoiceRecordsDO::getStatus,pageReqVO.getStatus()) .eqIfPresent(InvoiceRecordsDO::getStatus, pageReqVO.getStatus())
.likeIfPresent(InvoiceRecordsDO::getCreator,pageReqVO.getCreator()) .likeIfPresent(InvoiceRecordsDO::getCreator, pageReqVO.getCreator())
.likeIfPresent(InvoiceRecordsDO::getInvoicePerson,pageReqVO.getInvoicePerson()) .likeIfPresent(InvoiceRecordsDO::getInvoicePerson, pageReqVO.getInvoicePerson())
.betweenIfPresent(InvoiceRecordsDO::getInvoiceAmount,pageReqVO.getInvoiceAmount()) .betweenIfPresent(InvoiceRecordsDO::getInvoiceAmount, pageReqVO.getInvoiceAmount())
.betweenIfPresent(InvoiceRecordsDO::getCreateTime,pageReqVO.getCreateTime()) .betweenIfPresent(InvoiceRecordsDO::getCreateTime, pageReqVO.getCreateTime())
.betweenIfPresent(InvoiceRecordsDO::getInvoiceTime,pageReqVO.getInvoiceTime()) .betweenIfPresent(InvoiceRecordsDO::getInvoiceTime, pageReqVO.getInvoiceTime())
.orderByDesc(InvoiceRecordsDO::getId)); .orderByDesc(InvoiceRecordsDO::getId));
} }
@@ -1,11 +1,14 @@
package com.cf.imes.module.system.service.funds.invoice; package com.cf.imes.module.system.service.funds.invoice;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.cf.imes.framework.common.exception.ServiceException; import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.common.pojo.PageResult; import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.Assert.AssertUtils; import com.cf.imes.framework.common.util.Assert.AssertUtils;
@@ -47,15 +50,20 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Optional; import java.util.Optional;
import static cn.hutool.core.date.DatePattern.NORM_DATE_FORMATTER;
import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.cf.imes.framework.common.util.json.JsonUtils.parseObject; import static com.cf.imes.framework.common.util.json.JsonUtils.parseObject;
import static com.cf.imes.framework.common.util.json.JsonUtils.toJsonString; import static com.cf.imes.framework.common.util.json.JsonUtils.toJsonString;
import static com.cf.imes.framework.common.util.json.JsonUtils.unzipString; import static com.cf.imes.framework.common.util.json.JsonUtils.unzipString;
import static com.cf.imes.framework.common.util.json.JsonUtils.zipString; import static com.cf.imes.framework.common.util.json.JsonUtils.zipString;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.INVOICE_AMOUNT_ZERO_ERROR;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.INVOICE_APPLY_UNINVOICEABLE_EXIST;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.INVOICE_NO_EXIST; import static com.cf.imes.module.system.enums.ErrorCodeConstants.INVOICE_NO_EXIST;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.INVOICE_TITLE_ENTERPRISE_TAXNUMBER_EMPTY_ERROR; import static com.cf.imes.module.system.enums.ErrorCodeConstants.INVOICE_TITLE_ENTERPRISE_TAXNUMBER_EMPTY_ERROR;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.INVOICE_TITLE_NOT_EXISTS_ERROR; import static com.cf.imes.module.system.enums.ErrorCodeConstants.INVOICE_TITLE_NOT_EXISTS_ERROR;
@@ -165,12 +173,13 @@ public class InvoiceServiceImpl implements InvoiceService {
* @param id * @param id
* @param organId * @param organId
*/ */
private void validTitleOrg(Long id, Long organId) { private InvoiceTitleInfoDO validTitleOrg(Long id, Long organId) {
InvoiceTitleInfoDO titleInfoDO = invoiceTitleInfoMapper.selectById(id); InvoiceTitleInfoDO titleInfoDO = invoiceTitleInfoMapper.selectById(id);
// 抬头不存在 // 抬头不存在
AssertUtils.notEmpty(titleInfoDO, INVOICE_TITLE_NOT_EXISTS_ERROR); AssertUtils.notEmpty(titleInfoDO, INVOICE_TITLE_NOT_EXISTS_ERROR);
// 抬头组织不匹配 // 抬头组织不匹配
AssertUtils.notEquals(titleInfoDO.getOrganId(), organId, INVOICE_TITLE_NOT_EXISTS_ERROR); AssertUtils.notEquals(titleInfoDO.getOrganId(), organId, INVOICE_TITLE_NOT_EXISTS_ERROR);
return titleInfoDO;
} }
@Override @Override
@@ -217,8 +226,15 @@ public class InvoiceServiceImpl implements InvoiceService {
AssertUtils.notEmpty(invoiceTitleInfoDO, INVOICE_TITLE_NOT_EXISTS_ERROR); AssertUtils.notEmpty(invoiceTitleInfoDO, INVOICE_TITLE_NOT_EXISTS_ERROR);
// 检查关联记录的购买记录,不能有已开票的
List<PurchaseRecordDO> purchaseRecordDOS = purchaseRecordMapper.selectByIds(purchaseRecordId);
boolean uninvoicableExist = purchaseRecordDOS.stream().filter(p -> !InvoiceStatusEnum.INVOICABLE.getCode().equals(p.getIsInvocing())).findAny().isPresent();
AssertUtils.isTrue(uninvoicableExist, INVOICE_APPLY_UNINVOICEABLE_EXIST);
// 查询可开票金额 // 查询可开票金额
BigDecimal invoicableAmount = purchaseRecordMapper.getInvoicableAmount(organId, purchaseRecordId); BigDecimal invoicableAmount = purchaseRecordMapper.getInvoicableAmount(organId, purchaseRecordId);
// 可开票金额不能为0
AssertUtils.isTrue(ObjectUtils.isNull(invoicableAmount) || invoicableAmount.compareTo(BigDecimal.ZERO) == 0, INVOICE_AMOUNT_ZERO_ERROR);
// 更新购买记录表的发票状态 // 更新购买记录表的发票状态
int update = purchaseRecordMapper.update(new LambdaUpdateWrapper<PurchaseRecordDO>() int update = purchaseRecordMapper.update(new LambdaUpdateWrapper<PurchaseRecordDO>()
@@ -256,7 +272,7 @@ public class InvoiceServiceImpl implements InvoiceService {
PageResult<InvoiceRecordsDO> pageResult = invoiceRecordsMapper.selectInvoiceRecordsPage(pageReqVO); PageResult<InvoiceRecordsDO> pageResult = invoiceRecordsMapper.selectInvoiceRecordsPage(pageReqVO);
List<InvoiceRecordsDO> resultList = pageResult.getList(); List<InvoiceRecordsDO> resultList = pageResult.getList();
if(CollUtil.isEmpty(resultList)){ if (CollUtil.isEmpty(resultList)) {
return new PageResult<>(); return new PageResult<>();
} }
List<InvoiceRecordsRespVO> recordsRespVOS = BeanUtils.toBean(resultList, InvoiceRecordsRespVO.class); List<InvoiceRecordsRespVO> recordsRespVOS = BeanUtils.toBean(resultList, InvoiceRecordsRespVO.class);
@@ -266,7 +282,7 @@ public class InvoiceServiceImpl implements InvoiceService {
@Override @Override
public InvoiceDetailsRespVO getInvoiceRecordDetail(Long recordId) { public InvoiceDetailsRespVO getInvoiceRecordDetail(Long recordId) {
InvoiceRecordsDO invoiceRecordsDO = invoiceRecordsMapper.selectById(recordId); InvoiceRecordsDO invoiceRecordsDO = invoiceRecordsMapper.selectById(recordId);
AssertUtils.notEmpty(invoiceRecordsDO,INVOICE_NO_EXIST); AssertUtils.notEmpty(invoiceRecordsDO, INVOICE_NO_EXIST);
// 记录中的购买记录id数组转list // 记录中的购买记录id数组转list
String purchaseIds = Optional.ofNullable(invoiceRecordsDO.getPurchaseIds()).orElse("[]"); String purchaseIds = Optional.ofNullable(invoiceRecordsDO.getPurchaseIds()).orElse("[]");
@@ -321,6 +337,9 @@ public class InvoiceServiceImpl implements InvoiceService {
List<Long> purchaseIdList = JSON.parseArray(purchaseIds).toJavaList(Long.class).stream().distinct().toList(); List<Long> purchaseIdList = JSON.parseArray(purchaseIds).toJavaList(Long.class).stream().distinct().toList();
List<PurchaseRecordDO> purchaseRecordDOS = validatePurchaseRecordExists(purchaseIdList); List<PurchaseRecordDO> purchaseRecordDOS = validatePurchaseRecordExists(purchaseIdList);
// 获取对应组织的抬头信息
InvoiceTitleInfoDO invoiceTitleInfoDO = validTitleOrg(invoiceRecordsDO.getInvoiceTitleId(), invoiceRecordsDO.getOrganId());
if (reqVO.isAgree()) { if (reqVO.isAgree()) {
// 同意开票 // 同意开票
// 更新发票申请记录状态 // 更新发票申请记录状态
@@ -333,11 +352,13 @@ public class InvoiceServiceImpl implements InvoiceService {
if (ObjectUtil.isNull(file) || file.isEmpty()) { if (ObjectUtil.isNull(file) || file.isEmpty()) {
throw new ServiceException(ORG_INVOICE_AGREE_FILE_NULL_ERROR); throw new ServiceException(ORG_INVOICE_AGREE_FILE_NULL_ERROR);
} }
String originalFilename = file.getOriginalFilename(); LocalDateTime now = LocalDateTime.now();
String filePath = fileApi.createFile(IoUtil.readBytes(file.getInputStream()), originalFilename); String invoiceFileName = invoiceTitleInfoDO.getInvoiceTitle() + "-" + LocalDateTimeUtil.format(now, NORM_DATE_FORMATTER) + invoiceRecordsDO.getInvoiceAmount() + getFileExtension(file.getOriginalFilename());
String filePath = fileApi.createFile(IoUtil.readBytes(file.getInputStream()), invoiceFileName);
invoiceRecordsDO.setInvoiceAttachmentPath(filePath); invoiceRecordsDO.setInvoiceAttachmentPath(filePath);
invoiceRecordsDO.setInvoiceAttachmentName(originalFilename); invoiceRecordsDO.setInvoiceAttachmentName(invoiceFileName);
invoiceRecordsDO.setInvoiceNo(reqVO.getInvoiceNo()); invoiceRecordsDO.setInvoiceNo(reqVO.getInvoiceNo());
invoiceRecordsDO.setInvoiceTime(now);
} else { } else {
// 拒绝开票 // 拒绝开票
// 更新发票申请记录状态 // 更新发票申请记录状态
@@ -359,6 +380,27 @@ public class InvoiceServiceImpl implements InvoiceService {
purchaseRecordMapper.updateBatch(purchaseRecordDOS); purchaseRecordMapper.updateBatch(purchaseRecordDOS);
} }
/**
* 截取文件类型
*
* @param fileName
* @return
*/
public static String getFileExtension(String fileName) {
if (StrUtil.isEmpty(fileName)) {
return ""; // 或者返回 null,看你需求
}
int lastDotIndex = fileName.lastIndexOf('.');
if (lastDotIndex < 0 || lastDotIndex == fileName.length() - 1) {
// 没有点号,或者点号在最后一位(例如 "file."
return "";
}
// 截取后缀部分并转小写
return "." + fileName.substring(lastDotIndex + 1).toLowerCase(Locale.ROOT);
}
/** /**
* 校验发票申请记录 * 校验发票申请记录
* *
@@ -268,7 +268,7 @@ public class PayProductProcessor {
.originalAmount(originalPrice) .originalAmount(originalPrice)
.monthAverageAmount(monthAverageAmount) .monthAverageAmount(monthAverageAmount)
.accountBalanceSpent(price) .accountBalanceSpent(price)
.isInvocing(InvoiceStatusEnum.INVOICABLE.getCode()) .isInvocing(null)
.initial(initialized) .initial(initialized)
.status(PurchaseRecordStatusEnum.ACTIVE.getStatus()) .status(PurchaseRecordStatusEnum.ACTIVE.getStatus())
.build(); .build();
@@ -412,6 +412,8 @@ public class PayProductProcessor {
purchaseRecordDO.setPreviousId(previousPurchaseRecord.getId()); purchaseRecordDO.setPreviousId(previousPurchaseRecord.getId());
} }
// 是否只用了赠送金
boolean justPayGift = true;
// 先用赠送金 // 先用赠送金
if (gift != null && gift.compareTo(BigDecimal.ZERO) > 0) { if (gift != null && gift.compareTo(BigDecimal.ZERO) > 0) {
usedGift = remaining.min(gift); usedGift = remaining.min(gift);
@@ -424,6 +426,7 @@ public class PayProductProcessor {
usedBalance = remaining.min(balance); usedBalance = remaining.min(balance);
remaining = remaining.subtract(usedBalance); remaining = remaining.subtract(usedBalance);
purchaseRecordDO.setAccountBalanceSpent(usedBalance); purchaseRecordDO.setAccountBalanceSpent(usedBalance);
justPayGift = false;
} }
// 剩下走第三方 // 剩下走第三方
@@ -449,6 +452,11 @@ public class PayProductProcessor {
.giftAmount(newGift) .giftAmount(newGift)
.build(); .build();
// 如果只用赠送金那么不可开票
if (justPayGift) {
purchaseRecordDO.setIsInvocing(null);
}
// 更新余额 // 更新余额
organAmountDO.setAmount(newAmounta); organAmountDO.setAmount(newAmounta);
organAmountDO.setGiftAmount(newGift); organAmountDO.setGiftAmount(newGift);
@@ -570,7 +578,9 @@ public class PayProductProcessor {
purchaseRecordDO.setPreviousId(previousPurchaseRecord.getId()); purchaseRecordDO.setPreviousId(previousPurchaseRecord.getId());
} }
// 先用余额 // 是否只用赠送金
boolean justPayGift = true;
// 先用赠送金
if (gift != null && gift.compareTo(BigDecimal.ZERO) > 0) { if (gift != null && gift.compareTo(BigDecimal.ZERO) > 0) {
usedGift = remaining.min(gift); usedGift = remaining.min(gift);
remaining = remaining.subtract(usedGift); remaining = remaining.subtract(usedGift);
@@ -605,6 +615,12 @@ public class PayProductProcessor {
purchaseRecordDO.setWechatSpent(remaining); purchaseRecordDO.setWechatSpent(remaining);
} }
purchaseRecordDO.setStatus(PurchaseRecordStatusEnum.PENDING.getStatus()); purchaseRecordDO.setStatus(PurchaseRecordStatusEnum.PENDING.getStatus());
justPayGift = false;
}
// 如果只用赠送金那么不可开票
if (justPayGift) {
purchaseRecordDO.setIsInvocing(null);
} }
// 更新余额 // 更新余额
@@ -651,6 +667,8 @@ public class PayProductProcessor {
purchaseRecordDO.setPreviousId(previousPurchaseRecord.getId()); purchaseRecordDO.setPreviousId(previousPurchaseRecord.getId());
} }
// 是否只用了赠送金
boolean justPayGift = true;
// 先用赠送金 // 先用赠送金
if (gift != null && gift.compareTo(BigDecimal.ZERO) > 0) { if (gift != null && gift.compareTo(BigDecimal.ZERO) > 0) {
usedGift = remaining.min(gift); usedGift = remaining.min(gift);
@@ -663,6 +681,7 @@ public class PayProductProcessor {
usedBalance = remaining.min(balance); usedBalance = remaining.min(balance);
remaining = remaining.subtract(usedBalance); remaining = remaining.subtract(usedBalance);
purchaseRecordDO.setAccountBalanceSpent(usedBalance); purchaseRecordDO.setAccountBalanceSpent(usedBalance);
justPayGift = false;
} }
// 新增收支明细 // 新增收支明细
@@ -694,6 +713,12 @@ public class PayProductProcessor {
purchaseRecordDO.setWechatSpent(remaining); purchaseRecordDO.setWechatSpent(remaining);
} }
purchaseRecordDO.setStatus(PurchaseRecordStatusEnum.PENDING.getStatus()); purchaseRecordDO.setStatus(PurchaseRecordStatusEnum.PENDING.getStatus());
justPayGift = false;
}
// 如果只用赠送金那么不可开票
if (justPayGift) {
purchaseRecordDO.setIsInvocing(null);
} }
// 更新余额 // 更新余额