|
@@ -9,8 +9,10 @@ import cn.iocoder.yudao.module.bpm.enums.message.BpmMessageEnum;
|
|
|
import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
|
|
import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
|
|
|
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.*;
|
|
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.*;
|
|
|
import cn.iocoder.yudao.module.system.dal.dataobject.biz.*;
|
|
import cn.iocoder.yudao.module.system.dal.dataobject.biz.*;
|
|
|
|
|
+import cn.iocoder.yudao.module.system.dal.dataobject.discount.DiscountRuleDO;
|
|
|
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
|
|
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
|
|
|
import cn.iocoder.yudao.module.system.dal.mysql.biz.*;
|
|
import cn.iocoder.yudao.module.system.dal.mysql.biz.*;
|
|
|
|
|
+import cn.iocoder.yudao.module.system.dal.mysql.discount.DiscountRuleMapper;
|
|
|
import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper;
|
|
import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper;
|
|
|
import cn.iocoder.yudao.module.system.enums.ErrorCodeConstants;
|
|
import cn.iocoder.yudao.module.system.enums.ErrorCodeConstants;
|
|
|
import cn.iocoder.yudao.module.system.enums.change.BusinessConstants;
|
|
import cn.iocoder.yudao.module.system.enums.change.BusinessConstants;
|
|
@@ -110,6 +112,12 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
|
|
|
@Resource
|
|
@Resource
|
|
|
private BizUtil bizUtil;
|
|
private BizUtil bizUtil;
|
|
|
|
|
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private SysOverheadChargeMapper sysOverheadChargeMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private DiscountRuleMapper discountRuleMapper;
|
|
|
|
|
+
|
|
|
@Override
|
|
@Override
|
|
|
public PageResult<RefundSettlementOrderPageRespVO> getRefundSettlementOrderPage(RefundSettlementOrderPageReqVO pageReqVO) {
|
|
public PageResult<RefundSettlementOrderPageRespVO> getRefundSettlementOrderPage(RefundSettlementOrderPageReqVO pageReqVO) {
|
|
|
Page<RefundSettlementOrderDO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
|
|
Page<RefundSettlementOrderDO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
|
|
@@ -1096,7 +1104,7 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
|
|
|
// 根据长者类型处理费用项
|
|
// 根据长者类型处理费用项
|
|
|
if (elderlyInfo.getInStatusType() == 2) {
|
|
if (elderlyInfo.getInStatusType() == 2) {
|
|
|
// 试住类型的长者处理逻辑
|
|
// 试住类型的长者处理逻辑
|
|
|
- processProbationElderlyExpenseItem(item, elderlyInfo, costDeadlineDate, itemVO);
|
|
|
|
|
|
|
+ processProbationElderlyExpenseItem(item, elderlyInfo, order, costDeadlineDate, itemVO);
|
|
|
} else {
|
|
} else {
|
|
|
// 正式入住长者处理逻辑
|
|
// 正式入住长者处理逻辑
|
|
|
processRegularElderlyExpenseItem(item, elderlyInfo, order, costDeadlineDate, itemVO);
|
|
processRegularElderlyExpenseItem(item, elderlyInfo, order, costDeadlineDate, itemVO);
|
|
@@ -1278,8 +1286,13 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
|
|
|
*/
|
|
*/
|
|
|
private void processProbationElderlyExpenseItem(ExpenseOrderItemDO item,
|
|
private void processProbationElderlyExpenseItem(ExpenseOrderItemDO item,
|
|
|
ElderlyInfoDO elderlyInfo,
|
|
ElderlyInfoDO elderlyInfo,
|
|
|
|
|
+ ExpenseOrderDO order,
|
|
|
LocalDate costDeadlineDate,
|
|
LocalDate costDeadlineDate,
|
|
|
ExpenseOrderItemVO itemVO) {
|
|
ExpenseOrderItemVO itemVO) {
|
|
|
|
|
+ // 优惠抵扣费用项单独处理,已处理则无需继续
|
|
|
|
|
+ if (processDiscountDeductionItem(item, order, costDeadlineDate, itemVO)) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
if (item.getExpenseSource().equals(BusinessConstants.EXPENSE_ITEM)) {
|
|
if (item.getExpenseSource().equals(BusinessConstants.EXPENSE_ITEM)) {
|
|
|
ExpenseItemDO expenseItem = expenseItemMapper.selectById(item.getSourceExpenseItemId());
|
|
ExpenseItemDO expenseItem = expenseItemMapper.selectById(item.getSourceExpenseItemId());
|
|
|
if (expenseItem != null && expenseItem.getIsDeposit() != null
|
|
if (expenseItem != null && expenseItem.getIsDeposit() != null
|
|
@@ -1352,6 +1365,10 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
|
|
|
ExpenseOrderDO order,
|
|
ExpenseOrderDO order,
|
|
|
LocalDate costDeadlineDate,
|
|
LocalDate costDeadlineDate,
|
|
|
ExpenseOrderItemVO itemVO) {
|
|
ExpenseOrderItemVO itemVO) {
|
|
|
|
|
+ // 优惠抵扣费用项单独处理,已处理则无需继续
|
|
|
|
|
+ if (processDiscountDeductionItem(item, order, costDeadlineDate, itemVO)) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
if (item.getExpenseSource().equals(BusinessConstants.EXPENSE_ITEM)) {
|
|
if (item.getExpenseSource().equals(BusinessConstants.EXPENSE_ITEM)) {
|
|
|
// 处理费用项
|
|
// 处理费用项
|
|
|
ExpenseItemDO expenseItem = expenseItemMapper.selectById(item.getSourceExpenseItemId());
|
|
ExpenseItemDO expenseItem = expenseItemMapper.selectById(item.getSourceExpenseItemId());
|
|
@@ -1368,8 +1385,14 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
|
|
|
// // 月度账单
|
|
// // 月度账单
|
|
|
// processMonthlyBill(item, expenseItem, order, costDeadlineDate, itemVO);
|
|
// processMonthlyBill(item, expenseItem, order, costDeadlineDate, itemVO);
|
|
|
// }
|
|
// }
|
|
|
|
|
+ // 判断费用项是否为按次(inSequence)收费(固定月金额,不乘天数、不按比例折算)
|
|
|
|
|
+ boolean isInSequence = false;
|
|
|
|
|
+ if (expenseItem.getItemId() != null) {
|
|
|
|
|
+ OverheadChargeDO overheadCharge = sysOverheadChargeMapper.selectById(expenseItem.getItemId());
|
|
|
|
|
+ isInSequence = overheadCharge != null && "inSequence".equals(overheadCharge.getChargeType());
|
|
|
|
|
+ }
|
|
|
// 月度账单
|
|
// 月度账单
|
|
|
- processMonthlyBill(item, expenseItem, order, costDeadlineDate, itemVO);
|
|
|
|
|
|
|
+ processMonthlyBill(item, expenseItem, order, costDeadlineDate, itemVO, isInSequence);
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
|
// 处理长护险和其他费用
|
|
// 处理长护险和其他费用
|
|
@@ -1377,6 +1400,88 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 处理优惠抵扣费用项(expenseSource = discount_deduction)
|
|
|
|
|
+ * 优惠抵扣项的 sourceExpenseItemId 为优惠规则(DiscountRuleDO)的ID,
|
|
|
|
|
+ * 按费用截止日期折算长者实际可享受的优惠金额:
|
|
|
|
|
+ * 未缴费:仅享受计费开始日期到费用截止日期的优惠(负数,抵扣应收)
|
|
|
|
|
+ * 已缴费:收回费用截止日期之后未享受天数对应的优惠(正数,补收)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @return true 表示已按优惠抵扣处理,无需再走其他费用项处理逻辑
|
|
|
|
|
+ */
|
|
|
|
|
+ private boolean processDiscountDeductionItem(ExpenseOrderItemDO item,
|
|
|
|
|
+ ExpenseOrderDO order,
|
|
|
|
|
+ LocalDate costDeadlineDate,
|
|
|
|
|
+ ExpenseOrderItemVO itemVO) {
|
|
|
|
|
+ if (item == null || !BusinessConstants.DISCOUNT_DEDUCTION.equals(item.getExpenseSource())) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ DiscountRuleDO discountRule = discountRuleMapper.selectById(item.getSourceExpenseItemId());
|
|
|
|
|
+ if (discountRule == null) {
|
|
|
|
|
+ itemVO.setSkip(true);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 1. 确定优惠抵扣的计费区间:优先账单明细起止时间,其次优惠规则时间,最后账单月份
|
|
|
|
|
+ LocalDate ruleStartDate = discountRule.getStartTime() == null ? null : discountRule.getStartTime().toLocalDate();
|
|
|
|
|
+ LocalDate ruleEndDate = discountRule.getEndTime() == null ? null : discountRule.getEndTime().toLocalDate();
|
|
|
|
|
+ LocalDate startDate = item.getStartDate() != null ? item.getStartDate() : ruleStartDate;
|
|
|
|
|
+ LocalDate endDate = item.getEndDate() != null ? item.getEndDate() : ruleEndDate;
|
|
|
|
|
+ if (startDate == null && endDate == null && order != null && StringUtils.isNotBlank(order.getBillingMonth())) {
|
|
|
|
|
+ YearMonth billingYearMonth = YearMonth.parse(order.getBillingMonth(), DateTimeFormatter.ofPattern("yyyy-MM"));
|
|
|
|
|
+ startDate = billingYearMonth.atDay(1);
|
|
|
|
|
+ endDate = billingYearMonth.atEndOfMonth();
|
|
|
|
|
+ }
|
|
|
|
|
+ if (startDate == null || endDate == null || endDate.isBefore(startDate)) {
|
|
|
|
|
+ itemVO.setSkip(true);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 2. 优惠总金额(负数):优先取账单明细金额,其次取优惠规则的优惠金额取负
|
|
|
|
|
+ BigDecimal totalDiscount = item.getTotalAmount() != null && item.getTotalAmount().compareTo(BigDecimal.ZERO) != 0
|
|
|
|
|
+ ? item.getTotalAmount()
|
|
|
|
|
+ : (discountRule.getDiscountAmount() == null ? BigDecimal.ZERO : discountRule.getDiscountAmount().negate());
|
|
|
|
|
+ long totalDays = ChronoUnit.DAYS.between(startDate, endDate) + 1;
|
|
|
|
|
+ BigDecimal dayDiscount = totalDiscount.divide(BigDecimal.valueOf(totalDays), 8, RoundingMode.HALF_UP);
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 按费用截止日期折算长者实际可享受的优惠天数与金额
|
|
|
|
|
+ LocalDate calcEndDate = endDate.isAfter(costDeadlineDate) ? costDeadlineDate : endDate;
|
|
|
|
|
+ long actualDays = calcEndDate.isBefore(startDate) ? 0 : ChronoUnit.DAYS.between(startDate, calcEndDate) + 1;
|
|
|
|
|
+ BigDecimal actualDiscount = dayDiscount.multiply(BigDecimal.valueOf(actualDays)).setScale(2, RoundingMode.HALF_UP);
|
|
|
|
|
+
|
|
|
|
|
+ boolean paid = item.getPayStatus() != null && item.getPayStatus().equals(BooleanEnum.TRUE.getValue());
|
|
|
|
|
+ // 未缴费:抵扣截止日期之前的优惠(负数);已缴费:收回截止日期之后的优惠(正数)
|
|
|
|
|
+ BigDecimal amount = paid ? totalDiscount.subtract(actualDiscount).negate().setScale(2, RoundingMode.HALF_UP)
|
|
|
|
|
+ : actualDiscount;
|
|
|
|
|
+
|
|
|
|
|
+ itemVO.setItemName(StringUtils.isNotBlank(item.getItemName()) ? item.getItemName() : discountRule.getRuleName());
|
|
|
|
|
+ itemVO.setItemId(discountRule.getId());
|
|
|
|
|
+ itemVO.setCount(1);
|
|
|
|
|
+ itemVO.setDays((int) actualDays);
|
|
|
|
|
+ itemVO.setIsMonthlyExpense(BooleanEnum.FALSE.getValue());
|
|
|
|
|
+ itemVO.setPrice(item.getPrice() != null ? item.getPrice().setScale(2, RoundingMode.HALF_UP) : actualDiscount);
|
|
|
|
|
+ itemVO.setAmount(amount);
|
|
|
|
|
+ // 4. 组装备注
|
|
|
|
|
+ StringBuilder remarks = new StringBuilder(StringUtils.isNotBlank(discountRule.getRuleName())
|
|
|
|
|
+ ? discountRule.getRuleName() : "优惠抵扣");
|
|
|
|
|
+ if (!paid) {
|
|
|
|
|
+ remarks.append(":").append(startDate).append("至").append(calcEndDate)
|
|
|
|
|
+ .append("共").append(actualDays).append("天,抵扣")
|
|
|
|
|
+ .append(actualDiscount.abs().setScale(2, RoundingMode.HALF_UP)).append("元");
|
|
|
|
|
+ } else if (actualDays >= totalDays) {
|
|
|
|
|
+ remarks.append(":").append(startDate).append("至").append(endDate)
|
|
|
|
|
+ .append("优惠已全部享受,无需退补");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ remarks.append(":").append(startDate).append("至").append(calcEndDate)
|
|
|
|
|
+ .append("共").append(actualDays).append("天享受优惠,收回")
|
|
|
|
|
+ .append(calcEndDate.plusDays(1)).append("至").append(endDate)
|
|
|
|
|
+ .append("共").append(totalDays - actualDays).append("天的优惠")
|
|
|
|
|
+ .append(amount.abs().setScale(2, RoundingMode.HALF_UP)).append("元");
|
|
|
|
|
+ }
|
|
|
|
|
+ itemVO.setRemarks(remarks.toString());
|
|
|
|
|
+ // 金额为0时无需展示
|
|
|
|
|
+ itemVO.setSkip(amount.compareTo(BigDecimal.ZERO) == 0);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 处理入院账单
|
|
* 处理入院账单
|
|
|
*/
|
|
*/
|
|
@@ -1408,14 +1513,15 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
|
|
|
ExpenseItemDO expenseItem,
|
|
ExpenseItemDO expenseItem,
|
|
|
ExpenseOrderDO order,
|
|
ExpenseOrderDO order,
|
|
|
LocalDate costDeadlineDate,
|
|
LocalDate costDeadlineDate,
|
|
|
- ExpenseOrderItemVO itemVO) {
|
|
|
|
|
|
|
+ ExpenseOrderItemVO itemVO,
|
|
|
|
|
+ boolean isInSequence) {
|
|
|
itemVO.setPrice(item.getPrice());
|
|
itemVO.setPrice(item.getPrice());
|
|
|
itemVO.setAmount(item.getTotalAmount().setScale(2, RoundingMode.HALF_UP));
|
|
itemVO.setAmount(item.getTotalAmount().setScale(2, RoundingMode.HALF_UP));
|
|
|
itemVO.setCount(item.getCount());
|
|
itemVO.setCount(item.getCount());
|
|
|
|
|
|
|
|
if (expenseItem.getIsMonthlyExpense().equals(BooleanEnum.TRUE.getValue())) {
|
|
if (expenseItem.getIsMonthlyExpense().equals(BooleanEnum.TRUE.getValue())) {
|
|
|
// 月度费用需要计算实际天数(按当月第一天开始计算)
|
|
// 月度费用需要计算实际天数(按当月第一天开始计算)
|
|
|
- processMonthlyExpenseForMonthlyBill(item, expenseItem, order, costDeadlineDate, itemVO);
|
|
|
|
|
|
|
+ processMonthlyExpenseForMonthlyBill(item, expenseItem, order, costDeadlineDate, itemVO, isInSequence);
|
|
|
} else if (expenseItem.getIsFreeGift() != null && expenseItem.getIsFreeGift().equals(BooleanEnum.TRUE.getValue())) {
|
|
} else if (expenseItem.getIsFreeGift() != null && expenseItem.getIsFreeGift().equals(BooleanEnum.TRUE.getValue())) {
|
|
|
// 赠送费用
|
|
// 赠送费用
|
|
|
processFreeGiftExpense(expenseItem, item, itemVO);
|
|
processFreeGiftExpense(expenseItem, item, itemVO);
|
|
@@ -1459,7 +1565,8 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
|
|
|
ExpenseItemDO expenseItem,
|
|
ExpenseItemDO expenseItem,
|
|
|
ExpenseOrderDO order,
|
|
ExpenseOrderDO order,
|
|
|
LocalDate costDeadlineDate,
|
|
LocalDate costDeadlineDate,
|
|
|
- ExpenseOrderItemVO itemVO) {
|
|
|
|
|
|
|
+ ExpenseOrderItemVO itemVO,
|
|
|
|
|
+ boolean isInSequence) {
|
|
|
String orderMonth = order.getBillingMonth();
|
|
String orderMonth = order.getBillingMonth();
|
|
|
YearMonth orderYearMonthParsed = YearMonth.from(DateTimeFormatter.ofPattern("yyyy-MM").parse(orderMonth));
|
|
YearMonth orderYearMonthParsed = YearMonth.from(DateTimeFormatter.ofPattern("yyyy-MM").parse(orderMonth));
|
|
|
LocalDate firstDayDate = orderYearMonthParsed.atDay(1);
|
|
LocalDate firstDayDate = orderYearMonthParsed.atDay(1);
|
|
@@ -1476,7 +1583,7 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
|
|
|
if (costDeadlineDate.getYear() == orderYearMonthParsed.getYear() &&
|
|
if (costDeadlineDate.getYear() == orderYearMonthParsed.getYear() &&
|
|
|
costDeadlineDate.getMonthValue() == orderYearMonthParsed.getMonthValue()) {
|
|
costDeadlineDate.getMonthValue() == orderYearMonthParsed.getMonthValue()) {
|
|
|
// 当月退住需要计算实际天数
|
|
// 当月退住需要计算实际天数
|
|
|
- processCurrentMonthExpense(item, beginDate,endDayDate, itemVO,costDeadlineDate);
|
|
|
|
|
|
|
+ processCurrentMonthExpense(item, beginDate,endDayDate, itemVO,costDeadlineDate, isInSequence);
|
|
|
} else if (costDeadlineDate.isBefore(beginDate)) {
|
|
} else if (costDeadlineDate.isBefore(beginDate)) {
|
|
|
// 退住日期在账单月之前,不计算费用
|
|
// 退住日期在账单月之前,不计算费用
|
|
|
processPreMonthExpense(item, beginDate, itemVO);
|
|
processPreMonthExpense(item, beginDate, itemVO);
|
|
@@ -1594,7 +1701,7 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
|
|
|
* 处理当前月份费用(退住当月)
|
|
* 处理当前月份费用(退住当月)
|
|
|
*/
|
|
*/
|
|
|
private void processCurrentMonthExpense(ExpenseOrderItemDO item, LocalDate firstDayDate,
|
|
private void processCurrentMonthExpense(ExpenseOrderItemDO item, LocalDate firstDayDate,
|
|
|
- LocalDate endDayDate, ExpenseOrderItemVO itemVO,LocalDate costDeadlineDate) {
|
|
|
|
|
|
|
+ LocalDate endDayDate, ExpenseOrderItemVO itemVO,LocalDate costDeadlineDate, boolean isInSequence) {
|
|
|
LocalDate calEndDate = Collections.min(Arrays.asList(endDayDate, costDeadlineDate));
|
|
LocalDate calEndDate = Collections.min(Arrays.asList(endDayDate, costDeadlineDate));
|
|
|
// 获取计费天数
|
|
// 获取计费天数
|
|
|
long stayDay = firstDayDate.isAfter(calEndDate) ? 0 : ChronoUnit.DAYS.between(firstDayDate, calEndDate) + 1;
|
|
long stayDay = firstDayDate.isAfter(calEndDate) ? 0 : ChronoUnit.DAYS.between(firstDayDate, calEndDate) + 1;
|
|
@@ -1606,6 +1713,17 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
|
|
|
itemVO.setPrice(dayPrice.setScale(2, RoundingMode.HALF_UP));
|
|
itemVO.setPrice(dayPrice.setScale(2, RoundingMode.HALF_UP));
|
|
|
itemVO.setAmount(amount);
|
|
itemVO.setAmount(amount);
|
|
|
itemVO.setRemarks("收" + firstDayDate + "到" + calEndDate + "共" + stayDay + "天的费用");
|
|
itemVO.setRemarks("收" + firstDayDate + "到" + calEndDate + "共" + stayDay + "天的费用");
|
|
|
|
|
+ // 按次(inSequence)收费:固定月金额,不乘天数、不按比例折算
|
|
|
|
|
+ if (isInSequence) {
|
|
|
|
|
+ itemVO.setPrice(item.getPrice());
|
|
|
|
|
+ itemVO.setAmount(item.getTotalAmount().setScale(2, RoundingMode.HALF_UP));
|
|
|
|
|
+ // 已缴清则无退费(固定月金额,不按天数折算)
|
|
|
|
|
+ if (item.getPayStatus().equals(BooleanEnum.TRUE.getValue())) {
|
|
|
|
|
+ itemVO.setAmount(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
|
|
|
|
|
+ }
|
|
|
|
|
+ itemVO.setRemarks("收" + firstDayDate + "到" + calEndDate + "共" + stayDay + "天的费用");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
// 如果账单已支付,需要计算应退金额
|
|
// 如果账单已支付,需要计算应退金额
|
|
|
if (item.getPayStatus().equals(BooleanEnum.TRUE.getValue())) {
|
|
if (item.getPayStatus().equals(BooleanEnum.TRUE.getValue())) {
|
|
|
// 修正退费天数计算逻辑:从退住日期的下一天开始计算
|
|
// 修正退费天数计算逻辑:从退住日期的下一天开始计算
|