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);
}
}
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 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_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 ============
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;
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;
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) {
@@ -13,7 +13,7 @@ import java.util.List;
public class InvoiceRecordsReqVO {
@Schema(description = "购买记录ID")
@Size(max = 300, message = "单次开票选择记录不能超过100条")
@Size(max = 100, message = "单次开票选择记录不能超过100条")
@NotEmpty
private List<Long> purchaseRecordId;
@@ -47,6 +47,8 @@ public class InvoiceRecordsRespVO {
@Schema(description = "发票附件保存路径")
private String invoiceAttachmentPath;
@Schema(description = "发票附件名称")
private String invoiceAttachmentName;
@Schema(description = "开票人")
private String invoicePerson;
@@ -16,15 +16,15 @@ public interface InvoiceRecordsMapper extends BaseMapperX<InvoiceRecordsDO> {
default PageResult<InvoiceRecordsDO> selectInvoiceRecordsPage(InvoiceDetailsPageReqVO pageReqVO) {
return selectPage(pageReqVO, new LambdaQueryWrapperX<InvoiceRecordsDO>()
.eq(InvoiceRecordsDO::getDeleted,false)
.eqIfPresent(InvoiceRecordsDO::getOrganId,pageReqVO.getOrganId())
.likeIfPresent(InvoiceRecordsDO::getInvoiceNo,pageReqVO.getInvoiceNo())
.eqIfPresent(InvoiceRecordsDO::getStatus,pageReqVO.getStatus())
.likeIfPresent(InvoiceRecordsDO::getCreator,pageReqVO.getCreator())
.likeIfPresent(InvoiceRecordsDO::getInvoicePerson,pageReqVO.getInvoicePerson())
.betweenIfPresent(InvoiceRecordsDO::getInvoiceAmount,pageReqVO.getInvoiceAmount())
.betweenIfPresent(InvoiceRecordsDO::getCreateTime,pageReqVO.getCreateTime())
.betweenIfPresent(InvoiceRecordsDO::getInvoiceTime,pageReqVO.getInvoiceTime())
.eq(InvoiceRecordsDO::getDeleted, false)
.eqIfPresent(InvoiceRecordsDO::getOrganId, pageReqVO.getOrganId())
.likeIfPresent(InvoiceRecordsDO::getInvoiceNo, pageReqVO.getInvoiceNo())
.eqIfPresent(InvoiceRecordsDO::getStatus, pageReqVO.getStatus())
.likeIfPresent(InvoiceRecordsDO::getCreator, pageReqVO.getCreator())
.likeIfPresent(InvoiceRecordsDO::getInvoicePerson, pageReqVO.getInvoicePerson())
.betweenIfPresent(InvoiceRecordsDO::getInvoiceAmount, pageReqVO.getInvoiceAmount())
.betweenIfPresent(InvoiceRecordsDO::getCreateTime, pageReqVO.getCreateTime())
.betweenIfPresent(InvoiceRecordsDO::getInvoiceTime, pageReqVO.getInvoiceTime())
.orderByDesc(InvoiceRecordsDO::getId));
}
@@ -1,11 +1,14 @@
package com.cf.imes.module.system.service.funds.invoice;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.pojo.PageResult;
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.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
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.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.unzipString;
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_TITLE_ENTERPRISE_TAXNUMBER_EMPTY_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 organId
*/
private void validTitleOrg(Long id, Long organId) {
private InvoiceTitleInfoDO validTitleOrg(Long id, Long organId) {
InvoiceTitleInfoDO titleInfoDO = invoiceTitleInfoMapper.selectById(id);
// 抬头不存在
AssertUtils.notEmpty(titleInfoDO, INVOICE_TITLE_NOT_EXISTS_ERROR);
// 抬头组织不匹配
AssertUtils.notEquals(titleInfoDO.getOrganId(), organId, INVOICE_TITLE_NOT_EXISTS_ERROR);
return titleInfoDO;
}
@Override
@@ -217,8 +226,15 @@ public class InvoiceServiceImpl implements InvoiceService {
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);
// 可开票金额不能为0
AssertUtils.isTrue(ObjectUtils.isNull(invoicableAmount) || invoicableAmount.compareTo(BigDecimal.ZERO) == 0, INVOICE_AMOUNT_ZERO_ERROR);
// 更新购买记录表的发票状态
int update = purchaseRecordMapper.update(new LambdaUpdateWrapper<PurchaseRecordDO>()
@@ -256,7 +272,7 @@ public class InvoiceServiceImpl implements InvoiceService {
PageResult<InvoiceRecordsDO> pageResult = invoiceRecordsMapper.selectInvoiceRecordsPage(pageReqVO);
List<InvoiceRecordsDO> resultList = pageResult.getList();
if(CollUtil.isEmpty(resultList)){
if (CollUtil.isEmpty(resultList)) {
return new PageResult<>();
}
List<InvoiceRecordsRespVO> recordsRespVOS = BeanUtils.toBean(resultList, InvoiceRecordsRespVO.class);
@@ -266,7 +282,7 @@ public class InvoiceServiceImpl implements InvoiceService {
@Override
public InvoiceDetailsRespVO getInvoiceRecordDetail(Long recordId) {
InvoiceRecordsDO invoiceRecordsDO = invoiceRecordsMapper.selectById(recordId);
AssertUtils.notEmpty(invoiceRecordsDO,INVOICE_NO_EXIST);
AssertUtils.notEmpty(invoiceRecordsDO, INVOICE_NO_EXIST);
// 记录中的购买记录id数组转list
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<PurchaseRecordDO> purchaseRecordDOS = validatePurchaseRecordExists(purchaseIdList);
// 获取对应组织的抬头信息
InvoiceTitleInfoDO invoiceTitleInfoDO = validTitleOrg(invoiceRecordsDO.getInvoiceTitleId(), invoiceRecordsDO.getOrganId());
if (reqVO.isAgree()) {
// 同意开票
// 更新发票申请记录状态
@@ -333,11 +352,13 @@ public class InvoiceServiceImpl implements InvoiceService {
if (ObjectUtil.isNull(file) || file.isEmpty()) {
throw new ServiceException(ORG_INVOICE_AGREE_FILE_NULL_ERROR);
}
String originalFilename = file.getOriginalFilename();
String filePath = fileApi.createFile(IoUtil.readBytes(file.getInputStream()), originalFilename);
LocalDateTime now = LocalDateTime.now();
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.setInvoiceAttachmentName(originalFilename);
invoiceRecordsDO.setInvoiceAttachmentName(invoiceFileName);
invoiceRecordsDO.setInvoiceNo(reqVO.getInvoiceNo());
invoiceRecordsDO.setInvoiceTime(now);
} else {
// 拒绝开票
// 更新发票申请记录状态
@@ -359,6 +380,27 @@ public class InvoiceServiceImpl implements InvoiceService {
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)
.monthAverageAmount(monthAverageAmount)
.accountBalanceSpent(price)
.isInvocing(InvoiceStatusEnum.INVOICABLE.getCode())
.isInvocing(null)
.initial(initialized)
.status(PurchaseRecordStatusEnum.ACTIVE.getStatus())
.build();
@@ -412,6 +412,8 @@ public class PayProductProcessor {
purchaseRecordDO.setPreviousId(previousPurchaseRecord.getId());
}
// 是否只用了赠送金
boolean justPayGift = true;
// 先用赠送金
if (gift != null && gift.compareTo(BigDecimal.ZERO) > 0) {
usedGift = remaining.min(gift);
@@ -424,6 +426,7 @@ public class PayProductProcessor {
usedBalance = remaining.min(balance);
remaining = remaining.subtract(usedBalance);
purchaseRecordDO.setAccountBalanceSpent(usedBalance);
justPayGift = false;
}
// 剩下走第三方
@@ -449,6 +452,11 @@ public class PayProductProcessor {
.giftAmount(newGift)
.build();
// 如果只用赠送金那么不可开票
if (justPayGift) {
purchaseRecordDO.setIsInvocing(null);
}
// 更新余额
organAmountDO.setAmount(newAmounta);
organAmountDO.setGiftAmount(newGift);
@@ -570,7 +578,9 @@ public class PayProductProcessor {
purchaseRecordDO.setPreviousId(previousPurchaseRecord.getId());
}
// 先用余额
// 是否只用赠送金
boolean justPayGift = true;
// 先用赠送金
if (gift != null && gift.compareTo(BigDecimal.ZERO) > 0) {
usedGift = remaining.min(gift);
remaining = remaining.subtract(usedGift);
@@ -605,6 +615,12 @@ public class PayProductProcessor {
purchaseRecordDO.setWechatSpent(remaining);
}
purchaseRecordDO.setStatus(PurchaseRecordStatusEnum.PENDING.getStatus());
justPayGift = false;
}
// 如果只用赠送金那么不可开票
if (justPayGift) {
purchaseRecordDO.setIsInvocing(null);
}
// 更新余额
@@ -651,6 +667,8 @@ public class PayProductProcessor {
purchaseRecordDO.setPreviousId(previousPurchaseRecord.getId());
}
// 是否只用了赠送金
boolean justPayGift = true;
// 先用赠送金
if (gift != null && gift.compareTo(BigDecimal.ZERO) > 0) {
usedGift = remaining.min(gift);
@@ -663,6 +681,7 @@ public class PayProductProcessor {
usedBalance = remaining.min(balance);
remaining = remaining.subtract(usedBalance);
purchaseRecordDO.setAccountBalanceSpent(usedBalance);
justPayGift = false;
}
// 新增收支明细
@@ -694,6 +713,12 @@ public class PayProductProcessor {
purchaseRecordDO.setWechatSpent(remaining);
}
purchaseRecordDO.setStatus(PurchaseRecordStatusEnum.PENDING.getStatus());
justPayGift = false;
}
// 如果只用赠送金那么不可开票
if (justPayGift) {
purchaseRecordDO.setIsInvocing(null);
}
// 更新余额