|
|
@@ -12,6 +12,7 @@ import cn.hutool.json.JSONUtil;
|
|
|
import com.alibaba.excel.EasyExcel;
|
|
|
import com.alibaba.nacos.common.utils.CollectionUtils;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
@@ -92,6 +93,12 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
|
|
|
@Autowired
|
|
|
private InsPlyIncomeInvoiceMapper insPlyIncomeInvoiceMapper;
|
|
|
|
|
|
+ @Autowired
|
|
|
+ private InsFeeFollowOrderMapper insFeeFollowOrderMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private InsPlyFollowInvoiceLinkMapper insPlyFollowInvoiceLinkMapper;
|
|
|
+
|
|
|
@Autowired
|
|
|
private InsPlyIncomeInvoiceLinkMapper insPlyIncomeInvoiceLinkMapper;
|
|
|
|
|
|
@@ -531,9 +538,10 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
public Boolean deleteInvoiceById(String id) {
|
|
|
log.info("私有协议-开票记录-删除: id=[{}]",id);
|
|
|
- insPlyIncomeInvoiceLinkMapper.deleteByInvoiceId(id);
|
|
|
+ insPlyIncomeInvoiceLinkMapper.deleteById(id);
|
|
|
return insPlyIncomeInvoiceMapper.deleteById(id) > 0;
|
|
|
}
|
|
|
|
|
|
@@ -649,7 +657,7 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
|
|
|
// 2. 数据库校验:是否已存在开票记录
|
|
|
Integer count = baseMapper.invoicingCheck(invoicingVo);
|
|
|
if (count > 0) {
|
|
|
- log.info("数据库已存在开票记录,直接禁止");
|
|
|
+ log.info("数据库已存在开票记录,直接禁止开票");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
@@ -822,7 +830,7 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
|
|
|
log.info("开票异常.invoicingVo=[{}]",JSONUtil.toJsonStr(invoicingVo), e);
|
|
|
// 回滚事务
|
|
|
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
|
|
|
- return null;
|
|
|
+ throw new ServiceException(Objects.toString(e.getMessage(),"开票异常"));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -836,9 +844,9 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
|
|
|
if(invoicingVo == null){
|
|
|
throw new ServiceException("无法开票,开票参数为空");
|
|
|
}
|
|
|
- if(StrUtil.isEmpty(invoicingVo.getInvoiceNo())){
|
|
|
- throw new ServiceException("发票号不能为空");
|
|
|
- }
|
|
|
+// if(StrUtil.isEmpty(invoicingVo.getInvoiceNo())){
|
|
|
+// throw new ServiceException("发票号不能为空");
|
|
|
+// }
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -854,38 +862,38 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public boolean updateIncomeSettlementStatus(String invoiceType, List<String> invoiceIds, LocalDateTime settlementTime) {
|
|
|
- log.info("更新结算状态: invoiceType=[{}],invoiceIds=[{}],settlementTime=[{}]", invoiceType, JSONUtil.toJsonStr(invoiceIds), settlementTime);
|
|
|
- if (CollectionUtils.isEmpty(invoiceIds)) {
|
|
|
+ public boolean updateIncomeSettlementStatus(String invoiceType, List<String> incomeIds, LocalDateTime settlementTime) {
|
|
|
+ log.info("更新结算状态: invoiceType=[{}],incomeIds=[{}],settlementTime=[{}]", invoiceType, JSONUtil.toJsonStr(incomeIds), settlementTime);
|
|
|
+ if (CollectionUtils.isEmpty(incomeIds)) {
|
|
|
return false;
|
|
|
}
|
|
|
long result;
|
|
|
final String number = "1";
|
|
|
result = switch (invoiceType) {
|
|
|
case number -> baseMapper.update(new LambdaUpdateWrapper<InsPlyIncome>()
|
|
|
- .in(InsPlyIncome::getId, invoiceIds)
|
|
|
+ .in(InsPlyIncome::getId, incomeIds)
|
|
|
.set(InsPlyIncome::getSuperviseSettlement, number)
|
|
|
.set(InsPlyIncome::getSettlementTime,settlementTime)
|
|
|
);
|
|
|
case "2" -> baseMapper.update(new LambdaUpdateWrapper<InsPlyIncome>()
|
|
|
- .in(InsPlyIncome::getId, invoiceIds)
|
|
|
+ .in(InsPlyIncome::getId, incomeIds)
|
|
|
.set(InsPlyIncome::getOtherSettlement, number)
|
|
|
.set(InsPlyIncome::getSettlementTime,settlementTime));
|
|
|
case "3" -> baseMapper.update(new LambdaUpdateWrapper<InsPlyIncome>()
|
|
|
- .in(InsPlyIncome::getId, invoiceIds)
|
|
|
+ .in(InsPlyIncome::getId, incomeIds)
|
|
|
.set(InsPlyIncome::getJySuperviseSettlement, number)
|
|
|
.set(InsPlyIncome::getSettlementTime,settlementTime));
|
|
|
case "4" -> baseMapper.update(new LambdaUpdateWrapper<InsPlyIncome>()
|
|
|
- .in(InsPlyIncome::getId, invoiceIds)
|
|
|
+ .in(InsPlyIncome::getId, incomeIds)
|
|
|
.set(InsPlyIncome::getJyOtherSettlement, number)
|
|
|
.set(InsPlyIncome::getSettlementTime,settlementTime));
|
|
|
case "5" -> baseMapper.update(new LambdaUpdateWrapper<InsPlyIncome>()
|
|
|
- .in(InsPlyIncome::getId, invoiceIds)
|
|
|
+ .in(InsPlyIncome::getId, incomeIds)
|
|
|
.set(InsPlyIncome::getPlatformSuperviseSettlement, number)
|
|
|
.set(InsPlyIncome::getSettlementTime,settlementTime)
|
|
|
.set(InsPlyIncome::getSettlementStatus, number));
|
|
|
case "6" -> baseMapper.update(new LambdaUpdateWrapper<InsPlyIncome>()
|
|
|
- .in(InsPlyIncome::getId, invoiceIds)
|
|
|
+ .in(InsPlyIncome::getId, incomeIds)
|
|
|
.set(InsPlyIncome::getPlatformOtherSettlement, number)
|
|
|
.set(InsPlyIncome::getSettlementTime,settlementTime)
|
|
|
.set(InsPlyIncome::getSettlementStatus, number));
|
|
|
@@ -894,6 +902,83 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
|
|
|
return result > 0;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 更新结算状态
|
|
|
+ *
|
|
|
+ * @param invoiceType 发票类型
|
|
|
+ * @param incomeIds 收入ID列表(当invoiceType为2或4时,此参数可为空,改用年月等条件更新)
|
|
|
+ * @param settlementTime 结算时间
|
|
|
+ * @param systemCode 系统编码(invoiceType为2或4时必传)
|
|
|
+ * @param companyId 公司ID(invoiceType为2或4时必传)
|
|
|
+ * @return 是否更新成功
|
|
|
+ */
|
|
|
+ public boolean updateIncomeSettlementStatus(String invoiceType, List<String> incomeIds,
|
|
|
+ LocalDateTime settlementTime,
|
|
|
+ String systemCode, String companyId) {
|
|
|
+ log.info("更新结算状态: invoiceType=[{}], incomeIds=[{}], settlementTime=[{}], systemCode=[{}], companyId=[{}]",
|
|
|
+ invoiceType, JSONUtil.toJsonStr(incomeIds), settlementTime, systemCode, companyId);
|
|
|
+ final String number = "1";
|
|
|
+ long result = 0;
|
|
|
+ // 针对 invoiceType 为 2 或 4 的特殊逻辑:按签约年月、系统编码、公司ID进行批量更新
|
|
|
+ if ("2".equals(invoiceType) || "4".equals(invoiceType)) {
|
|
|
+ // 参数校验
|
|
|
+ if (settlementTime == null || StringUtils.isBlank(systemCode) || StringUtils.isBlank(companyId)) {
|
|
|
+ log.warn("invoiceType为2或4时,settlementTime、systemCode、companyId不能为空");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 提取 settlementTime 的年份和月份
|
|
|
+ int year = settlementTime.getYear();
|
|
|
+ int month = settlementTime.getMonthValue();
|
|
|
+
|
|
|
+ // 构建 LambdaUpdateWrapper
|
|
|
+ LambdaUpdateWrapper<InsPlyIncome> wrapper = new LambdaUpdateWrapper<>();
|
|
|
+ wrapper.eq(InsPlyIncome::getSystemCode, systemCode)
|
|
|
+ .eq(InsPlyIncome::getCompanyId, companyId)
|
|
|
+ // 核心逻辑:匹配 signingTime 的年份和月份
|
|
|
+ .apply("YEAR(signing_time) = {0} AND MONTH(signing_time) = {1}", year, month)
|
|
|
+ .set(InsPlyIncome::getSettlementTime, settlementTime);
|
|
|
+
|
|
|
+ // 根据具体的 invoiceType 设置对应的结算字段
|
|
|
+ if ("2".equals(invoiceType)) {
|
|
|
+ wrapper.set(InsPlyIncome::getOtherSettlement, number);
|
|
|
+ } else {
|
|
|
+ wrapper.set(InsPlyIncome::getJyOtherSettlement, number);
|
|
|
+ }
|
|
|
+
|
|
|
+ result = baseMapper.update(null, wrapper);
|
|
|
+ } else {
|
|
|
+ // 其他类型保持原有的根据 ID 列表更新的逻辑
|
|
|
+ if (CollectionUtils.isEmpty(incomeIds)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ result = switch (invoiceType) {
|
|
|
+ case "1" -> baseMapper.update(null, new LambdaUpdateWrapper<InsPlyIncome>()
|
|
|
+ .in(InsPlyIncome::getId, incomeIds)
|
|
|
+ .set(InsPlyIncome::getSuperviseSettlement, number)
|
|
|
+ .set(InsPlyIncome::getSettlementTime, settlementTime));
|
|
|
+ case "3" -> baseMapper.update(null, new LambdaUpdateWrapper<InsPlyIncome>()
|
|
|
+ .in(InsPlyIncome::getId, incomeIds)
|
|
|
+ .set(InsPlyIncome::getJySuperviseSettlement, number)
|
|
|
+ .set(InsPlyIncome::getSettlementTime, settlementTime));
|
|
|
+ case "5" -> baseMapper.update(null, new LambdaUpdateWrapper<InsPlyIncome>()
|
|
|
+ .in(InsPlyIncome::getId, incomeIds)
|
|
|
+ .set(InsPlyIncome::getPlatformSuperviseSettlement, number)
|
|
|
+ .set(InsPlyIncome::getSettlementTime, settlementTime)
|
|
|
+ .set(InsPlyIncome::getSettlementStatus, number));
|
|
|
+ case "6" -> baseMapper.update(null, new LambdaUpdateWrapper<InsPlyIncome>()
|
|
|
+ .in(InsPlyIncome::getId, incomeIds)
|
|
|
+ .set(InsPlyIncome::getPlatformOtherSettlement, number)
|
|
|
+ .set(InsPlyIncome::getSettlementTime, settlementTime)
|
|
|
+ .set(InsPlyIncome::getSettlementStatus, number));
|
|
|
+ default -> 0L;
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ return result > 0;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 开票并结算
|
|
|
*
|
|
|
@@ -979,7 +1064,7 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
|
|
|
// 处理结算数据
|
|
|
// 未完全结算完成
|
|
|
InsPlyIncomeInvoiceSettlement settlement = new InsPlyIncomeInvoiceSettlement(invoice.getId(), settlementVo.getActualReceivedAmount(),
|
|
|
- DateTime.now(), settlementVo.getPaymentDifference(), settlementVo.getPaymentReason());
|
|
|
+ DateTime.now(), settlementVo.getSettlementPaymentDifference(), settlementVo.getSettlementPaymentReason());
|
|
|
insPlyIncomeInvoiceSettlementMapper.insert(settlement);
|
|
|
|
|
|
if (CollectionUtils.isNotEmpty(settlementVo.getAttachmentIds())) {
|
|
|
@@ -996,7 +1081,7 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
|
|
|
}
|
|
|
|
|
|
// 判断是否已经结算完成, 考虑回款差额
|
|
|
- if (surplusPremium.compareTo(settlementVo.getActualReceivedAmount().add(platformSettlementVo.getSettlementVo().getPaymentDifference())) == 0) {
|
|
|
+ if (surplusPremium.compareTo(settlementVo.getActualReceivedAmount().add(platformSettlementVo.getSettlementVo().getSettlementPaymentDifference())) == 0) {
|
|
|
log.info("序号为[{}]的发票已经结算完成",invoice.getId());
|
|
|
// 更新发票状态
|
|
|
invoice.setStatus("1");
|
|
|
@@ -1402,8 +1487,8 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
|
|
|
// 实际已经收了的金额
|
|
|
BigDecimal actualReceivedAmount = this.getInvoicingAmount(settlementVo.getInvoicingId());
|
|
|
|
|
|
- // 所有需要结算的金额
|
|
|
- BigDecimal allToSettlement = insPlyIncomeInvoice.getReceivableSupervisePremium().add(insPlyIncomeInvoice.getOverinflatedAmount());
|
|
|
+ // 所有需要结算的金额,开票金额
|
|
|
+ BigDecimal allToSettlement = insPlyIncomeInvoice.getReceivableSupervisePremium();
|
|
|
// 剩余的金额
|
|
|
BigDecimal surplusPremium = allToSettlement.subtract(actualReceivedAmount);
|
|
|
log.info("invoiceId=[{}],一共需要结算的金额[{}],实际已经结算的金额[{}],本次计划结算的金额[{}]",
|
|
|
@@ -1414,7 +1499,7 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
|
|
|
// 处理结算数据
|
|
|
// 未完全结算完成
|
|
|
InsPlyIncomeInvoiceSettlement settlement = new InsPlyIncomeInvoiceSettlement(settlementVo.getInvoicingId(), settlementVo.getActualReceivedAmount(),
|
|
|
- DateTime.now(), settlementVo.getPaymentDifference(), settlementVo.getPaymentReason());
|
|
|
+ DateTime.now(), settlementVo.getSettlementPaymentDifference(), settlementVo.getSettlementPaymentReason());
|
|
|
insPlyIncomeInvoiceSettlementMapper.insert(settlement);
|
|
|
|
|
|
if (CollectionUtils.isNotEmpty(settlementVo.getAttachmentIds())) {
|
|
|
@@ -1452,7 +1537,6 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
|
|
|
log.error("结算异常. settlementVo=[{}]",JSONUtil.toJsonStr(settlementVo), e);
|
|
|
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
|
|
|
throw new RuntimeException(e.getMessage());
|
|
|
- // return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -2072,6 +2156,386 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 处理跟单费开票业务
|
|
|
+ * 包含:创建发票主表记录、批量创建跟单订单明细、建立发票与明细的关联关系
|
|
|
+ *
|
|
|
+ * @param vo 跟单开票请求参数
|
|
|
+ * @return 是否成功
|
|
|
+ */
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ @Override
|
|
|
+ public InvoiceingResult followInvoicing(FollowInvoiceVo vo) {
|
|
|
+ // 校验明细列表,防止空列表导致无意义的数据库操作
|
|
|
+ InvoiceingResult result = new InvoiceingResult();
|
|
|
+ List<InsFeeFollowOrderVo> details = vo.getDetails();
|
|
|
+ if (CollUtil.isEmpty(details)) {
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建并保存发票主表信息
|
|
|
+ InsPlyIncomeInvoice invoice = buildInvoice(vo);
|
|
|
+
|
|
|
+ // 批量构建跟单订单明细和关联关系
|
|
|
+ List<InsFeeFollowOrder> orderList = new ArrayList<>(details.size());
|
|
|
+ List<InsPlyFollowInvoiceLink> links = new ArrayList<>(details.size());
|
|
|
+
|
|
|
+ for (InsFeeFollowOrderVo item : details) {
|
|
|
+ // 构建跟单订单明细
|
|
|
+ InsFeeFollowOrder order = new InsFeeFollowOrder();
|
|
|
+ BeanUtils.copyProperties(item, order);
|
|
|
+ order.setId(IdGenerate.nextId());
|
|
|
+ order.setSystemCode(baseController.getSystemCode());
|
|
|
+ // 如果选择了单条,则设置应收金额为开票金额
|
|
|
+ if ("0".equals(vo.getSelectFlag())){
|
|
|
+ order.setUninvoiced(order.getReceivablePremium().subtract(vo.getReceivableSupervisePremium()));
|
|
|
+ order.setInvoicedAmountTime(vo.getReceivableSupervisePremium());
|
|
|
+ order.setInvoiced(vo.getReceivableSupervisePremium());
|
|
|
+ }
|
|
|
+ orderList.add(order);
|
|
|
+
|
|
|
+ // 构建发票与订单的关联关系
|
|
|
+ InsPlyFollowInvoiceLink link = new InsPlyFollowInvoiceLink();
|
|
|
+ link.setId(IdGenerate.nextId());
|
|
|
+ link.setInvoiceId(invoice.getId());
|
|
|
+ link.setFollowId(order.getId());
|
|
|
+ links.add(link);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 插入invoice表
|
|
|
+ insPlyIncomeInvoiceMapper.insert(invoice);
|
|
|
+ // 批量插入订单明细和关联关系,避免循环内单条插入导致的性能问题
|
|
|
+ // 注意:需确保 Mapper 中已配置对应的批量插入方法
|
|
|
+ insFeeFollowOrderMapper.insert(orderList);
|
|
|
+ insPlyFollowInvoiceLinkMapper.insert(links);
|
|
|
+
|
|
|
+ // 返回结果
|
|
|
+ result.setInvoiceId(invoice.getId());
|
|
|
+ result.setSumPrice(null != invoice.getReceivableSupervisePremium() ? invoice.getReceivableSupervisePremium().toString() : null);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建发票主表实体对象
|
|
|
+ */
|
|
|
+ private InsPlyIncomeInvoice buildInvoice(FollowInvoiceVo vo) {
|
|
|
+ InsPlyIncomeInvoice invoice = new InsPlyIncomeInvoice();
|
|
|
+ invoice.setId(IdGenerate.nextId());
|
|
|
+ invoice.setCompanyId(vo.getCompanyId());
|
|
|
+ // 发票类型:2-跟单费
|
|
|
+ invoice.setInvoiceType("2");
|
|
|
+ invoice.setInvoiceNo(vo.getInvoiceNo());
|
|
|
+ // 开票类型 1、手续费 2、跟单费 3、驾意险手续费 4、驾意险跟单费 5、平台应收-车险 6、平台应收-非车险
|
|
|
+ if("1".equals(vo.getInvoiceType()) || "2".equals(vo.getInvoiceType())){
|
|
|
+ invoice.setInvoiceRiskType("10");
|
|
|
+ }else if("5".equals(vo.getInvoiceType()) || "6".equals(vo.getInvoiceType())){
|
|
|
+ invoice.setInvoiceRiskType("30");
|
|
|
+ }else{
|
|
|
+ invoice.setInvoiceRiskType("20");
|
|
|
+ }
|
|
|
+ invoice.setInvoiceParty(vo.getInvoiceParty());
|
|
|
+ invoice.setOverinflatedAmount(vo.getOverinflatedAmount());
|
|
|
+ invoice.setPaymentReason(vo.getPaymentReason());
|
|
|
+ invoice.setReceivableSupervisePremium(vo.getReceivableSupervisePremium());
|
|
|
+ invoice.setTaxPoint(vo.getTaxPoint());
|
|
|
+ // 初始状态:0-待处理
|
|
|
+ invoice.setStatus("0");
|
|
|
+ return invoice;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取发票跟进分页列表数据
|
|
|
+ *
|
|
|
+ * @param followInvoicePageListVo
|
|
|
+ * @return 发票跟进VO列表
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public List<InsFeeFollowOrderVo> followInvoicePageList(FollowInvoicePageListVo followInvoicePageListVo) {
|
|
|
+
|
|
|
+ if (null == followInvoicePageListVo || CollUtil.isEmpty(followInvoicePageListVo.getSearchList())){
|
|
|
+ return CollUtil.newArrayList();
|
|
|
+ }
|
|
|
+ FollowInvoicePageListVo.SearchVO searchYear = followInvoicePageListVo.getSearchList().stream().filter(search -> "signYear".equals(search.getName())).findFirst().orElse(new FollowInvoicePageListVo.SearchVO());
|
|
|
+ Integer startYear = Integer.parseInt(searchYear.getSearch().split(",")[0]);
|
|
|
+ Integer endYear = Integer.parseInt(searchYear.getSearch().split(",")[1]);
|
|
|
+
|
|
|
+ // 获取所有年份
|
|
|
+ List<Integer> yearList = new ArrayList<>();
|
|
|
+ if (startYear <= endYear) {
|
|
|
+ for (int year = startYear; year <= endYear; year++) {
|
|
|
+ yearList.add(year);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取当前用户的系统编码
|
|
|
+ String systemCode = baseController.getUserSystemCode();
|
|
|
+
|
|
|
+ // 查询该年份下的所有跟进收入数据,并按合作方公司ID进行分组
|
|
|
+ List<InsPlyIncomeDto> incomes = baseMapper.queryFollowIncome(startYear, endYear, systemCode);
|
|
|
+
|
|
|
+ // ====================== 增加动态条件过滤 ======================
|
|
|
+ List<InsPlyIncomeDto> filterIncomes = new ArrayList<>();
|
|
|
+ if (CollUtil.isEmpty(followInvoicePageListVo.getSearchList())) {
|
|
|
+ filterIncomes = incomes;
|
|
|
+ } else {
|
|
|
+ List<FollowInvoicePageListVo.SearchVO> searchParams = followInvoicePageListVo.getSearchList();
|
|
|
+ if (CollUtil.isNotEmpty(searchParams)) {
|
|
|
+ Map<String, FollowInvoicePageListVo.SearchVO> searchVOMap = searchParams.stream()
|
|
|
+ .filter(action -> StrUtil.isNotEmpty(action.getName()))
|
|
|
+ .collect(Collectors.toMap(FollowInvoicePageListVo.SearchVO::getName, Function.identity(), (oldItem, newItem) -> oldItem));
|
|
|
+ Set<String> names = searchVOMap.keySet();
|
|
|
+
|
|
|
+ // 保险公司
|
|
|
+ Predicate<InsPlyIncomeDto> companyIdPredicate = insPlyIncome -> {
|
|
|
+ if (!names.contains(FollowInvoicePageListVo.SearchVO.companyId)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ FollowInvoicePageListVo.SearchVO searchVO = searchVOMap.get(FollowInvoicePageListVo.SearchVO.companyId);
|
|
|
+ String type = searchVO.getType();
|
|
|
+ String search = searchVO.getSearch();
|
|
|
+ if ("为空".equals(type) || "不为空".equals(type)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if (StrUtil.isEmpty(search)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if ("等于".equalsIgnoreCase(type) || "包含".equalsIgnoreCase(type)) {
|
|
|
+ List<String> companyIds = Arrays.asList(search.split(","));
|
|
|
+ return companyIds.contains(insPlyIncome.getCompanyId());
|
|
|
+ }
|
|
|
+ if ("不等于".equalsIgnoreCase(type) || "不包含".equalsIgnoreCase(type)) {
|
|
|
+ List<String> companyIds = Arrays.asList(search.split(","));
|
|
|
+ return !companyIds.contains(insPlyIncome.getCompanyId());
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ };
|
|
|
+ // 签单年份
|
|
|
+ Predicate<InsPlyIncomeDto> signYearPredicate = insPlyIncome -> {
|
|
|
+ if (!names.contains(FollowInvoicePageListVo.SearchVO.signYear)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ FollowInvoicePageListVo.SearchVO searchVO = searchVOMap.get(FollowInvoicePageListVo.SearchVO.signYear);
|
|
|
+ String type = searchVO.getType();
|
|
|
+ String search = searchVO.getSearch();
|
|
|
+ if ("为空".equals(type) || "不为空".equals(type)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if (StrUtil.isEmpty(search)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if ("等于".equalsIgnoreCase(type) || "包含".equalsIgnoreCase(type)) {
|
|
|
+ List<String> signYears = Arrays.asList(search.split(","));
|
|
|
+ return signYears.contains(insPlyIncome.getSignYear());
|
|
|
+ }
|
|
|
+ if ("不等于".equalsIgnoreCase(type) || "不包含".equalsIgnoreCase(type)) {
|
|
|
+ List<String> signYears = Arrays.asList(search.split(","));
|
|
|
+ return !signYears.contains(insPlyIncome.getSignYear());
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ };
|
|
|
+
|
|
|
+ // AND / OR 组合逻辑(和代码A完全一致)
|
|
|
+ if ("and".equalsIgnoreCase(followInvoicePageListVo.getLogicJudge())) {
|
|
|
+ Predicate<InsPlyIncomeDto> and = companyIdPredicate.and(signYearPredicate);
|
|
|
+ filterIncomes = incomes.stream().filter(and).toList();
|
|
|
+ } else if ("or".equalsIgnoreCase(followInvoicePageListVo.getLogicJudge())) {
|
|
|
+ Predicate<InsPlyIncomeDto> or = companyIdPredicate.or(signYearPredicate);
|
|
|
+ filterIncomes = incomes.stream().filter(or).toList();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // ======================条件过滤结束======================
|
|
|
+
|
|
|
+ // 按合作方分组(使用过滤后的数据)
|
|
|
+ Map<String, List<InsPlyIncomeDto>> groupMap = filterIncomes.stream()
|
|
|
+ .collect(Collectors.groupingBy(InsPlyIncomeDto::getPartnerCompanyId));
|
|
|
+
|
|
|
+ // 查询该年份下已开票的数据,并聚合为 [合作方ID_月份 -> 已开票金额] 的映射
|
|
|
+ Map<String, BigDecimal> invoicedMap = buildInvoicedMap(startYear, endYear);
|
|
|
+
|
|
|
+ // 遍历分组数据,组装最终的返回结果
|
|
|
+ List<InsFeeFollowOrderVo> result = new ArrayList<>();
|
|
|
+ for (Map.Entry<String, List<InsPlyIncomeDto>> entry : groupMap.entrySet()) {
|
|
|
+ String partnerCompanyId = entry.getKey();
|
|
|
+ List<InsPlyIncomeDto> list = entry.getValue();
|
|
|
+
|
|
|
+ // 过滤掉空列表,防止后续空指针异常
|
|
|
+ if (CollUtil.isEmpty(list)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算该合作方每月的应收保费
|
|
|
+ Map<Integer, BigDecimal> monthReceivableMap = calculateMonthlyReceivable(list);
|
|
|
+
|
|
|
+ // 遍历12个月份,组装VO对象
|
|
|
+ InsPlyIncomeDto firstItem = list.get(0);
|
|
|
+ for (Integer year : yearList) {
|
|
|
+ for (int month = 1; month <= 12; month++) {
|
|
|
+ InsFeeFollowOrderVo vo = buildFollowOrderVo(
|
|
|
+ year, month, firstItem, partnerCompanyId,
|
|
|
+ monthReceivableMap, invoicedMap
|
|
|
+ );
|
|
|
+ result.add(vo);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建已开票金额映射表
|
|
|
+ * 将已开票数据按 "合作方ID_月份" 进行聚合求和
|
|
|
+ */
|
|
|
+ private Map<String, BigDecimal> buildInvoicedMap(Integer startYear, Integer endYear) {
|
|
|
+ QueryWrapper<InsFeeFollowOrder> queryWrapper = new QueryWrapper<>();
|
|
|
+ queryWrapper.between("year", startYear, endYear);
|
|
|
+ List<InsFeeFollowOrder> invoicedList = insFeeFollowOrderMapper.selectList(queryWrapper);
|
|
|
+
|
|
|
+ return invoicedList.stream().collect(Collectors.groupingBy(
|
|
|
+ item -> item.getPartnerCompanyId() + "_" + item.getMonth(),
|
|
|
+ Collectors.reducing(
|
|
|
+ BigDecimal.ZERO,
|
|
|
+ InsFeeFollowOrder::getInvoiced,
|
|
|
+ BigDecimal::add
|
|
|
+ )
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算单个月份的应收保费
|
|
|
+ * 避免在循环中重复创建BigDecimal对象,使用字符串构造或valueOf
|
|
|
+ */
|
|
|
+ private Map<Integer, BigDecimal> calculateMonthlyReceivable(List<InsPlyIncomeDto> list) {
|
|
|
+ return list.stream()
|
|
|
+ .filter(item -> item.getSigningTime() != null) // 防御性编程:过滤掉签约时间为空的数据
|
|
|
+ .collect(Collectors.groupingBy(
|
|
|
+ item -> item.getSigningTime().getMonthValue(),
|
|
|
+ Collectors.reducing(
|
|
|
+ BigDecimal.ZERO,
|
|
|
+ item -> {
|
|
|
+ // 优化点:将字符串转换为BigDecimal,避免在循环中频繁new对象
|
|
|
+ BigDecimal jqPremium = new BigDecimal(item.getJqReceivablePremium());
|
|
|
+ BigDecimal syPremium = new BigDecimal(item.getSyReceivablePremium());
|
|
|
+ return jqPremium.add(syPremium);
|
|
|
+ },
|
|
|
+ BigDecimal::add
|
|
|
+ )
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 组装单个月的发票跟进VO对象
|
|
|
+ */
|
|
|
+ private InsFeeFollowOrderVo buildFollowOrderVo(Integer year, int month, InsPlyIncomeDto firstItem,
|
|
|
+ String partnerCompanyId,
|
|
|
+ Map<Integer, BigDecimal> monthReceivableMap,
|
|
|
+ Map<String, BigDecimal> invoicedMap) {
|
|
|
+ // 获取当月应收保费,默认为0
|
|
|
+ BigDecimal receivable = monthReceivableMap.getOrDefault(month, BigDecimal.ZERO);
|
|
|
+ // 获取当月已开票金额,默认为0
|
|
|
+ BigDecimal invoiced = invoicedMap.getOrDefault(partnerCompanyId + "_" + month, BigDecimal.ZERO);
|
|
|
+
|
|
|
+ InsFeeFollowOrderVo vo = new InsFeeFollowOrderVo();
|
|
|
+ vo.setYear(year);
|
|
|
+ vo.setMonth(month);
|
|
|
+ vo.setCompanyName(firstItem.getCompanyName());
|
|
|
+ vo.setPartnerCompanyName(firstItem.getPartnerCompanyName());
|
|
|
+ vo.setCompanyId(firstItem.getCompanyId());
|
|
|
+ vo.setPartnerCompanyId(partnerCompanyId);
|
|
|
+ vo.setReceivablePremium(receivable);
|
|
|
+ vo.setInvoiced(invoiced);
|
|
|
+ // 未开票金额 = 应收保费 - 已开票金额
|
|
|
+ vo.setUninvoiced(receivable.subtract(invoiced));
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public boolean followSettlement(SettlementVo settlementVo) {
|
|
|
+ log.info("结算:settlementVo=[{}].DateTimeFormatter", JSONUtil.toJsonStr(settlementVo));
|
|
|
+ AssertionUtils.isFail(StrUtil.isEmpty(settlementVo.getReceivePaymentDate()), "结算日期不能为空");
|
|
|
+ try {
|
|
|
+ // 获取应收的金额
|
|
|
+ InsPlyIncomeInvoice insPlyIncomeInvoice = insPlyIncomeInvoiceMapper.selectById(settlementVo.getInvoicingId());
|
|
|
+ AssertionUtils.isFail(Objects.isNull(insPlyIncomeInvoice), "发票数据不存在");
|
|
|
+ // 实际已经收了的金额
|
|
|
+ BigDecimal actualReceivedAmount = this.getInvoicingAmount(settlementVo.getInvoicingId());
|
|
|
+
|
|
|
+ // 所有需要结算的金额,开票金额
|
|
|
+ BigDecimal allToSettlement = insPlyIncomeInvoice.getReceivableSupervisePremium();
|
|
|
+ // 剩余的金额
|
|
|
+ BigDecimal surplusPremium = allToSettlement.subtract(actualReceivedAmount);
|
|
|
+ log.info("invoiceId=[{}],一共需要结算的金额[{}],实际已经结算的金额[{}],本次计划结算的金额[{}]",
|
|
|
+ settlementVo.getInvoicingId(), allToSettlement,actualReceivedAmount,settlementVo.getActualReceivedAmount());
|
|
|
+ // 校验是否超出了剩余的金额,判断 surplusPremium 是否 小于 实际到账金额
|
|
|
+ AssertionUtils.isFail(surplusPremium.compareTo(settlementVo.getActualReceivedAmount()) < 0, "已超出应收金额,请核对!");
|
|
|
+
|
|
|
+ // 处理结算数据
|
|
|
+ // 未完全结算完成
|
|
|
+ InsPlyIncomeInvoiceSettlement settlement = new InsPlyIncomeInvoiceSettlement(settlementVo.getInvoicingId(), settlementVo.getActualReceivedAmount(),
|
|
|
+ DateTime.now(), settlementVo.getSettlementPaymentDifference(), settlementVo.getSettlementPaymentReason());
|
|
|
+ insPlyIncomeInvoiceSettlementMapper.insert(settlement);
|
|
|
+
|
|
|
+ if (CollectionUtils.isNotEmpty(settlementVo.getAttachmentIds())) {
|
|
|
+ log.info("结算的凭证是:settlementVo.getAttachmentIds()=[{}]", JSONUtil.toJsonStr(settlementVo.getAttachmentIds()));
|
|
|
+ // 添加发票附件
|
|
|
+ List<InsPlyIncomeInvoiceSettlementFile> files = new ArrayList<>();
|
|
|
+ for (String fileId : settlementVo.getAttachmentIds()) {
|
|
|
+ InsPlyIncomeInvoiceSettlementFile file = new InsPlyIncomeInvoiceSettlementFile();
|
|
|
+ file.setId(IdGenerate.nextId());
|
|
|
+ file.setSettlementId(settlement.getId());
|
|
|
+ file.setFileId(fileId);
|
|
|
+ files.add(file);
|
|
|
+ }
|
|
|
+ insPlyIncomeInvoiceSettlementFileMapper.insert(files);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 判断是否已经结算完成
|
|
|
+ if (surplusPremium.compareTo(settlementVo.getActualReceivedAmount()) == 0) {
|
|
|
+ log.info("序号为[{}]的发票已经结算完成",insPlyIncomeInvoice.getId());
|
|
|
+ // 更新发票状态
|
|
|
+ insPlyIncomeInvoice.setStatus("1");
|
|
|
+ insPlyIncomeInvoiceMapper.updateById(insPlyIncomeInvoice);
|
|
|
+ // 判断是需要更新哪个字段
|
|
|
+ this.updateIncomeSettlementStatus(insPlyIncomeInvoice.getInvoiceType(), null, LocalDateTime.now(),
|
|
|
+ baseController.getSystemCode(), insPlyIncomeInvoice.getCompanyId());
|
|
|
+ }else{
|
|
|
+ log.info("invoiceId=[{}], 还剩[{}]钱没有结算",settlementVo.getInvoicingId(), surplusPremium.subtract(settlementVo.getActualReceivedAmount()));
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("结算异常. settlementVo=[{}]",JSONUtil.toJsonStr(settlementVo), e);
|
|
|
+ TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
|
|
|
+ throw new RuntimeException(e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 跟单费开票并结算
|
|
|
+ *
|
|
|
+ * @param followInvoiceAndSettlementVo 跟单费开票信息和结算信息
|
|
|
+ * @author jk
|
|
|
+ * @date 2026/5/26 11:44
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public HttpResult<String> followInvoiceAndSettlement(FollowInvoiceAndSettlementVo followInvoiceAndSettlementVo) {
|
|
|
+ log.info("跟单费开票并结算: settlementVo=[{}]", JSONUtil.toJsonStr(followInvoiceAndSettlementVo));
|
|
|
+ InvoiceingResult result = this.followInvoicing(followInvoiceAndSettlementVo.getInvoiceVO());
|
|
|
+ if (Objects.nonNull(result)){
|
|
|
+ SettlementVo settlementVo = new SettlementVo();
|
|
|
+ BeanUtils.copyProperties(followInvoiceAndSettlementVo, settlementVo);
|
|
|
+ settlementVo.setInvoicingId(result.getInvoiceId());
|
|
|
+ boolean settlement = this.followSettlement(settlementVo);
|
|
|
+ if(settlement){
|
|
|
+ return HttpResult.ok("跟单费开票并结算成功");
|
|
|
+ }else{
|
|
|
+ return HttpResult.error("跟单费开票成功但结算失败");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return HttpResult.error("跟单费开票失败");
|
|
|
+ }
|
|
|
|
|
|
|
|
|
@Override
|
|
|
@@ -2117,14 +2581,17 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
|
|
|
.filter(Objects::nonNull)
|
|
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
BigDecimal receivableSupervisePremium = action.getReceivableSupervisePremium() != null ? action.getReceivableSupervisePremium(): BigDecimal.ZERO;
|
|
|
- BigDecimal overinflatedAmount = action.getOverinflatedAmount() != null? action.getOverinflatedAmount(): BigDecimal.ZERO;
|
|
|
- BigDecimal remainSettlementAmount = receivableSupervisePremium.add(overinflatedAmount).subtract(settlementAmount);
|
|
|
+// BigDecimal overinflatedAmount = action.getOverinflatedAmount() != null? action.getOverinflatedAmount(): BigDecimal.ZERO;
|
|
|
+ // 待结算金额 = 开票金额 - 已收金额
|
|
|
+ BigDecimal remainSettlementAmount = receivableSupervisePremium.subtract(settlementAmount);
|
|
|
// 设置已结算的金额
|
|
|
action.setSettlementAmount(settlementAmount);
|
|
|
// 设置待结算金额
|
|
|
action.setRemainSettlementAmount(remainSettlementAmount);
|
|
|
- action.setCompanyALlName(allCompanyHierarchyPaths.get(Objects.toString(action.getCompanyId(),"")));
|
|
|
+ action.setCompanyAllName(allCompanyHierarchyPaths.get(Objects.toString(action.getCompanyId(),"")));
|
|
|
action.setSettlementStatus(settlementStatusName);
|
|
|
+ // 开票金额
|
|
|
+ action.setReceivableAmount(action.getReceivableSupervisePremium().subtract(action.getOverinflatedAmount()).toString());
|
|
|
}).toList();
|
|
|
invoiceRecordList.setRecords(records);
|
|
|
}
|
|
|
@@ -2236,6 +2703,23 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
|
|
|
return exportSuperviseSettlementExcelDtos;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 跟单协议结算记录
|
|
|
+ *
|
|
|
+ * @param settlementQueryVo 查询条件
|
|
|
+ * @return Page<ExportSuperviseSettlementExcelDto>
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public Page<ExportSuperviseSettlementExcelDto> followSettlementExcel(Page page, SettlementQueryVo settlementQueryVo) {
|
|
|
+ log.info("查询平台结算记录:settlementQueryVo=[{}]", JSONUtil.toJsonStr(settlementQueryVo));
|
|
|
+ // 设置空条件
|
|
|
+ setNoneCondition(settlementQueryVo);
|
|
|
+ Page<ExportSuperviseSettlementExcelDto> exportSuperviseSettlementExcelDtos = baseMapper.followSettlementExcelDtos(page, settlementQueryVo);
|
|
|
+ // 补充、转换列表的数据
|
|
|
+ supplementaryListData(exportSuperviseSettlementExcelDtos);
|
|
|
+ return exportSuperviseSettlementExcelDtos;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 设置空条件
|
|
|
* @param settlementQueryVo
|