Commit 8ce75748 by jianan

分期出账优化、应收款应付款金额精度

parent 6a7d830a
......@@ -398,13 +398,13 @@ public class ApiFortuneController {
}
/**
* 更新保单发佣(检核操作)
* 更新
*
* @param fortuneUpdateRequest
* @return
*/
@PostMapping("/update")
@Operation(summary = "更新保单发佣信息(检核操作)")
@Operation(summary = "更新")
public Result<Boolean> updateFortune(@RequestBody FortuneUpdateRequest fortuneUpdateRequest) {
if (fortuneUpdateRequest == null || fortuneUpdateRequest.getFortuneBizId() == null) {
return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), "fortuneBizId不能为空");
......
......@@ -668,7 +668,7 @@ public class ApiExpectedFortuneServiceImpl implements ApiExpectedFortuneService
//原币种
updateObj.setOriginalCurrency(policyCurrency);
//原币种金额
BigDecimal ruleAmount = expectedFortune.getRuleAmount();
BigDecimal ruleAmount = expectedFortune.getRuleAmount().setScale(2, RoundingMode.HALF_UP);
updateObj.setOriginalAmount(ruleAmount);
//原币种 -> 港币汇率
updateObj.setOriginalToHkdRate(originalToHkdRate);
......@@ -676,22 +676,22 @@ public class ApiExpectedFortuneServiceImpl implements ApiExpectedFortuneService
updateObj.setRuleCurrency(policyCurrency);
//默认保单币种 -> 港币汇率
updateObj.setDefaultExchangeRate(originalToHkdRate);
//应发港币金额(估)(标准发佣金额 * 默认保单币种 -> 港币汇率)
//应发港币金额(估)(标准发佣金额 * 默认保单币种 -> 港币汇率);出的钱保留整数
BigDecimal hkdAmount = ruleAmount.multiply(originalToHkdRate);
updateObj.setHkdAmount(hkdAmount);
updateObj.setHkdAmount(hkdAmount.setScale(0, RoundingMode.HALF_UP));
//发放币种
updateObj.setPayoutCurrency(currency);
//发放币种金额
updateObj.setPayoutAmount(hkdAmount.multiply(hkdToPayoutRate));
//发放币种金额;出的钱保留整数
updateObj.setPayoutAmount(hkdAmount.multiply(hkdToPayoutRate).setScale(0, RoundingMode.HALF_UP));
//港币 -> 发放币种汇率
updateObj.setHkdToPayoutRate(hkdToPayoutRate);
//默认值
updateObj.setPaidRuleAmount(BigDecimal.ZERO);
updateObj.setUnpaidRuleAmount(ruleAmount);
updateObj.setUnpaidRuleAmountHkd(hkdAmount);
updateObj.setUnpaidRuleAmountHkd(hkdAmount.setScale(0, RoundingMode.HALF_UP));
updateObj.setPaidAmount(BigDecimal.ZERO);
updateObj.setPaidRatio(BigDecimal.ZERO);
updateObj.setUnpaidAmount(expectedFortune.getHkdAmount());
updateObj.setUnpaidAmount(hkdAmount.setScale(0, RoundingMode.HALF_UP));
updateObj.setUnpaidRatio(BigDecimal.valueOf(100));
}
......
......@@ -482,7 +482,8 @@ public class CommissionServiceImpl extends ServiceImpl<CommissionMapper, Commiss
// 查询入账币种到港币的汇率
BigDecimal exchangeRate = queryExchangeRateByFeign(currency, "HKD");
return requestAmount.multiply(exchangeRate);
// 来的钱(入账/来佣)保留2位小数
return requestAmount.multiply(exchangeRate).setScale(2, RoundingMode.HALF_UP);
}
private CommissionExpected getCommissionExpectedBizId(Commission commission) {
......@@ -820,11 +821,13 @@ public class CommissionServiceImpl extends ServiceImpl<CommissionMapper, Commiss
if (isAllHkd) {
// 原币种 = HKD 时,exchangeRate = 保单币种 → 港币,可安全使用
fortune.setOriginalToHkdRate(exchangeRate);
BigDecimal hkdAmount = fortune.getRuleAmount().multiply(exchangeRate);
BigDecimal hkdAmount = fortune.getRuleAmount()
.multiply(exchangeRate).setScale(0, RoundingMode.HALF_UP);
fortune.setHkdAmount(hkdAmount);
fortune.setCurrentPaymentAmount(fortune.getRuleAmount());
fortune.setCurrentPaymentHkdAmount(hkdAmount);
BigDecimal payoutAmount = hkdAmount.multiply(expectedFortune.getHkdToPayoutRate());
BigDecimal payoutAmount = hkdAmount
.multiply(expectedFortune.getHkdToPayoutRate()).setScale(0, RoundingMode.HALF_UP);
fortune.setPayoutAmount(payoutAmount);
ExpectedFortune update = new ExpectedFortune();
......
......@@ -500,14 +500,16 @@ public class FortuneAccountServiceImpl extends ServiceImpl<FortuneAccountMapper,
BigDecimal paidRule = paidRuleMap.getOrDefault(ef.getExpectedFortuneBizId(), BigDecimal.ZERO);
BigDecimal unpaidRule = ef.getRuleAmount().subtract(paidRule);
// 剩余发放折合港币金额
BigDecimal unpaidRuleAmountHkd = unpaidRule.multiply(ef.getDefaultExchangeRate());
// 剩余发放折合港币金额;出的钱保留整数
BigDecimal unpaidRuleAmountHkd = unpaidRule.multiply(ef.getDefaultExchangeRate())
.setScale(0, RoundingMode.HALF_UP);
// 应发港币金额(估)
BigDecimal hkdAmount = hkdAmountMap.getOrDefault(ef.getExpectedFortuneBizId(), BigDecimal.ZERO);
BigDecimal expectedHKD = hkdAmount.add(unpaidRuleAmountHkd);
// 发放币种金额 = 港币 × 港币兑发放币种汇率
BigDecimal expectedPayout = expectedHKD.multiply(ef.getHkdToPayoutRate());
// 发放币种金额 = 港币 × 港币兑发放币种汇率;出的钱保留整数
BigDecimal expectedPayout = expectedHKD.multiply(ef.getHkdToPayoutRate())
.setScale(0, RoundingMode.HALF_UP);
// 累计出账港币金额
BigDecimal paid = paidMap.getOrDefault(ef.getExpectedFortuneBizId(), BigDecimal.ZERO);
......
......@@ -11,7 +11,6 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Joiner;
import com.yd.auth.core.dto.AuthUserDto;
import com.yd.auth.core.utils.SecurityUtil;
import com.yd.base.feign.client.exchangerate.ApiExchangeRateFeignClient;
import com.yd.common.enums.CommonEnum;
import com.yd.common.enums.ResultCode;
import com.yd.common.exception.BusinessException;
......@@ -38,7 +37,6 @@ import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
......@@ -70,10 +68,6 @@ public class FortuneServiceImpl extends ServiceImpl<FortuneMapper, Fortune>
@Resource
private ApiSysDictFeignClient apiSysDictFeignClient;
@Resource
private RedisUtil redisUtil;
@Resource
private ApiExchangeRateFeignClient apiExchangeRateFeignClient;
@Resource
private IAgentAccumulatedFycService iAgentAccumulatedFycService;
@Override
......@@ -254,117 +248,9 @@ public class FortuneServiceImpl extends ServiceImpl<FortuneMapper, Fortune>
// 检核人
fortune.setReconciliationOperator(currentLoginUser.getRealName());
// 如果传入了金额
if (currentPaymentHkdAmount != null && currentPaymentHkdAmount.compareTo(BigDecimal.ZERO) != 0) {
splitFortune(fortune, currentPaymentHkdAmount, expectedFortune, loginUserId, fortuneUpdateRequest);
} else {
// 如果未传入金额,全额发放,出账状态为已出帐
totalFortune(fortune, expectedFortune, loginUserId, fortuneUpdateRequest);
}
// 更新 expected_fortune 出账状态
expectedFortuneService.lambdaUpdate()
.set(ExpectedFortune::getStatus, fortuneUpdateRequest.getStatus())
.eq(ExpectedFortune::getId, expectedFortune.getId())
.update();
return true;
}
private void totalFortune(Fortune fortune, ExpectedFortune expectedFortune, String loginUserId, FortuneUpdateRequest fortuneUpdateRequest) {
AuthUserDto currentLoginUser = SecurityUtil.getCurrentLoginUser();
// 1. 更新fortune记录为全额发放状态
fortune.setCurrentPaymentHkdAmount(fortune.getHkdAmount());
fortune.setCurrentPaymentRatio(BigDecimal.valueOf(100));
fortune.setStatus(FortuneStatusEnum.CHECKED.getItemValue());
fortune.setReconciliationOperator(currentLoginUser.getRealName());
fortune.setUpdaterId(loginUserId);
fortune.setUpdateTime(new Date());
this.updateById(fortune);
}
/**
* 保单币种对预计来佣的结算币种的默认汇率
*
* @param policyCurrency 保单币种
* @param currency 预计来佣的结算币种
* @return 汇率
*/
private BigDecimal queryExchangeRateByFeign(String policyCurrency, String currency) {
if (policyCurrency.equalsIgnoreCase(currency)) {
return BigDecimal.ONE;
}
// 调用Feign客户端查询汇率
Result<BigDecimal> result = apiExchangeRateFeignClient.getExchangeRate(policyCurrency, currency, "");
if (result != null && result.getData() != null) {
return result.getData();
}
return BigDecimal.ONE;
}
/**
* 拆分发放(按本次发放金额拆)
*/
@Transactional(rollbackFor = Exception.class)
public void splitFortune(Fortune main, BigDecimal currentPaymentHkdAmount, ExpectedFortune expectedFortune, String loginUserId, FortuneUpdateRequest fortuneUpdateRequest) {
if (!main.getStatus().equals(FortuneStatusEnum.CAN_SEND.getItemValue()))
throw new BusinessException("只有可出账记录才能修改金额");
BigDecimal fullAmount = main.getHkdAmount();
if (currentPaymentHkdAmount.compareTo(main.getCurrentPaymentHkdAmount()) > 0)
throw new BusinessException("本次发放金额不能大于可出账金额");
// 获取当前登录用户的ID
AuthUserDto currentLoginUser = SecurityUtil.getCurrentLoginUser();
// 1. 更新主行 = 本次发放金额
main.setCurrentPaymentAmount(null);
main.setCurrentPaymentHkdAmount(currentPaymentHkdAmount);
main.setCurrentPaymentRatio(currentPaymentHkdAmount
.divide(expectedFortune.getHkdAmount(), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
main.setStatus(FortuneStatusEnum.CHECKED.getItemValue());
main.setReconciliationOperator(currentLoginUser.getRealName());
main.setUpdaterId(loginUserId);
main.setUpdateTime(new Date());
this.updateById(main);
// 2. 剩余金额生成新行
BigDecimal leftAmount = fullAmount.subtract(currentPaymentHkdAmount);
if (leftAmount.compareTo(BigDecimal.ZERO) > 0) {
Fortune part = new Fortune();
BeanUtils.copyProperties(main, part, "id", "fortuneBizId", "currentPaymentAmount", "status", "isPart", "payoutDate");
part.setFortuneBizId(RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_FORTUNE.getCode()));
part.setCurrentPaymentAmount(null);
part.setCurrentPaymentHkdAmount(leftAmount);
part.setCurrentPaymentRatio(leftAmount
.divide(expectedFortune.getHkdAmount(), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
part.setStatus(FortuneStatusEnum.RESERVED.getItemValue());
part.setIsPart(1);
part.setPayoutDate(LocalDate.now().plusMonths(1)); // 次月
part.setReconciliationOperator(currentLoginUser.getRealName());
part.setCreatorId(loginUserId);
part.setCreateTime(new Date());
this.save(part);
}
}
private BigDecimal calculateExpectedFortunePaidAmount(ExpectedFortune expectedFortune) {
// 查询所有已出账的记录
List<Fortune> paidList = this.list(new QueryWrapper<Fortune>()
.eq("expected_fortune_biz_id", expectedFortune.getExpectedFortuneBizId())
.eq("fortune_period", expectedFortune.getFortunePeriod())
.eq("status", FortuneStatusEnum.CHECKED.getItemValue()));
// 计算已出账金额
return paidList.stream()
.map(Fortune::getCurrentPaymentHkdAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
@Override
public void downloadFortune(FortuneDownloadRequest fortuneDownloadRequest, HttpServletResponse response) throws IOException {
List<String> fortuneBizIdList = fortuneDownloadRequest.getFortuneBizIdList();
......@@ -963,29 +849,34 @@ public class FortuneServiceImpl extends ServiceImpl<FortuneMapper, Fortune>
}
List<FortuneSplitDto> splitList = fortuneSplitRequest.getFortuneSplitDtoList();
// 3. 金额验证
BigDecimal totalSplitOriginalAmount = splitList.stream()
.map(FortuneSplitDto::getOriginalAmount)
// 3. 保单币锚守恒:Σ各期保单币份额 严格等于原记录 ruleAmount(来的钱,2位)
BigDecimal totalRuleAmount = splitList.stream()
.map(FortuneSplitDto::getRuleAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
if (totalRuleAmount.compareTo(originalFortune.getRuleAmount()) != 0) {
throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(),
String.format("分期保单币金额总和(%s)与原记录保单币金额(%s)必须一致",
totalRuleAmount, originalFortune.getRuleAmount()));
}
// 4. 每期港币必须为非负整数(元)。各期汇率可能不同,港币合计不卡总额,以Σ为实际发放港币
for (FortuneSplitDto dto : splitList) {
if (dto.getHkdAmount() == null || dto.getHkdAmount().signum() < 0
|| dto.getHkdAmount().stripTrailingZeros().scale() > 0) {
throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "每期港币金额必须为非负整数(元)");
}
}
// 5. 比例总和必须严格等于 100
BigDecimal totalSplitRatio = splitList.stream()
.map(FortuneSplitDto::getSplitRatio)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal tolerance = new BigDecimal("0.01");
BigDecimal originalDifference = originalFortune.getOriginalAmount().subtract(totalSplitOriginalAmount).abs();
if (originalDifference.compareTo(tolerance) > 0) {
throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(),
String.format("分期原始金额总和(%s)与原记录原始金额(%s)不匹配", totalSplitOriginalAmount, originalFortune.getOriginalAmount()));
}
if (totalSplitRatio.compareTo(BigDecimal.ZERO) > 0) {
BigDecimal ratioDifference = new BigDecimal("100").subtract(totalSplitRatio).abs();
if (ratioDifference.compareTo(new BigDecimal("0.1")) > 0) {
if (totalSplitRatio.compareTo(BigDecimal.ZERO) > 0
&& totalSplitRatio.compareTo(new BigDecimal("100")) != 0) {
throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(),
String.format("分期比例总和(%s%%)应为100%%", totalSplitRatio));
}
String.format("分期比例总和(%s%%)必须为100%%", totalSplitRatio));
}
// 4. 生成新记录
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment