|
|
@@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.system.api.bpm.vo.CheckInContractReqVO;
|
|
|
import cn.iocoder.yudao.module.system.api.bpm.vo.CheckInExpenseSaveReqVO;
|
|
|
import cn.iocoder.yudao.module.bpm.api.business.BpmBusinessFormApi;
|
|
|
import cn.iocoder.yudao.module.bpm.api.business.dto.BpmBusinessFormCreateReqDTO;
|
|
|
+import cn.iocoder.yudao.module.system.controller.admin.biz.vo.ElderlyFixedExpenseAppendReqVO;
|
|
|
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.ExpenseRespVO;
|
|
|
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.ExpenseSaveReqVO;
|
|
|
import cn.iocoder.yudao.module.system.dal.dataobject.biz.*;
|
|
|
@@ -41,6 +42,7 @@ import java.time.LocalDate;
|
|
|
import java.time.LocalDateTime;
|
|
|
import java.time.YearMonth;
|
|
|
import java.time.ZoneId;
|
|
|
+import java.time.ZoneOffset;
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
import java.time.temporal.ChronoUnit;
|
|
|
import java.time.temporal.TemporalAdjusters;
|
|
|
@@ -523,6 +525,134 @@ public class ExpenseServiceImpl implements ExpenseService {
|
|
|
addCheckInApply(createReqVO);
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ @Transactional
|
|
|
+ public void appendFixedExpense(ElderlyFixedExpenseAppendReqVO reqVO) {
|
|
|
+ Long tenantId = reqVO.getTenantId() == null ? TenantContextHolder.getTenantId() : reqVO.getTenantId();
|
|
|
+
|
|
|
+ ExpenseDO expense = expenseMapper.selectOne(new LambdaQueryWrapperX<ExpenseDO>()
|
|
|
+ .eq(ExpenseDO::getElderId, reqVO.getElderId())
|
|
|
+ .eq(tenantId != null, ExpenseDO::getTenantId, tenantId)
|
|
|
+ .orderByDesc(ExpenseDO::getCreatedTime)
|
|
|
+ .last("LIMIT 1"));
|
|
|
+
|
|
|
+ if (expense == null) {
|
|
|
+ expense = new ExpenseDO();
|
|
|
+ expense.setElderId(reqVO.getElderId());
|
|
|
+ expense.setTenantId(tenantId);
|
|
|
+ expense.setRemark("手工追加固定费项创建");
|
|
|
+ expense.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
|
|
|
+ expense.setCreatedTime(new Date());
|
|
|
+ expense.setTotalAmount(BigDecimal.ZERO);
|
|
|
+ expenseMapper.insert(expense);
|
|
|
+ }
|
|
|
+
|
|
|
+ int count = Optional.ofNullable(reqVO.getCount()).orElse(1);
|
|
|
+ if (count <= 0) {
|
|
|
+ throw exceptionCustomMsg(ErrorCodeConstants.COMMON_ERROR, "数量必须大于0");
|
|
|
+ }
|
|
|
+
|
|
|
+ LocalDate startDate = reqVO.getStartDate();
|
|
|
+ LocalDate endDate = reqVO.getEndDate() == null ? startDate : reqVO.getEndDate();
|
|
|
+ if (endDate.isBefore(startDate)) {
|
|
|
+ throw exceptionCustomMsg(ErrorCodeConstants.COMMON_ERROR, "结束时间不能早于开始时间");
|
|
|
+ }
|
|
|
+
|
|
|
+ BigDecimal actualAmount = reqVO.getActualAmount() == null ? reqVO.getAmount() : reqVO.getActualAmount();
|
|
|
+ BigDecimal totalAmount = actualAmount.multiply(BigDecimal.valueOf(count)).setScale(2, RoundingMode.HALF_UP);
|
|
|
+
|
|
|
+ ExpenseItemDO item = new ExpenseItemDO();
|
|
|
+ item.setExpenseId(expense.getId());
|
|
|
+ item.setItemId(reqVO.getItemId());
|
|
|
+ item.setItemCategoryName(reqVO.getItemCategoryName());
|
|
|
+ item.setItemName(reqVO.getItemName());
|
|
|
+ item.setAmount(reqVO.getAmount());
|
|
|
+ item.setActualAmount(actualAmount);
|
|
|
+ item.setCount(count);
|
|
|
+ item.setTotalAmount(totalAmount);
|
|
|
+ item.setType(reqVO.getType());
|
|
|
+ item.setIsMonthlyExpense(BooleanEnum.TRUE.getValue());
|
|
|
+ item.setIsOneTimeFee(BooleanEnum.FALSE.getValue());
|
|
|
+ item.setTenantId(tenantId);
|
|
|
+ item.setCreateTime(new Date());
|
|
|
+
|
|
|
+ item.setChangeStartDate(startDate);
|
|
|
+ item.setChangeEndDate(endDate);
|
|
|
+
|
|
|
+ String targetBillingMonth = YearMonth.from(startDate).format(DateTimeFormatter.ofPattern("yyyy-MM"));
|
|
|
+
|
|
|
+ expenseItemMapper.insert(item);
|
|
|
+
|
|
|
+ // 追加费项后,立即触发目标月份账单补差
|
|
|
+ reconcileCurrentMonthBillAfterAppend(reqVO.getElderId(), tenantId, item, targetBillingMonth, startDate, endDate);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 追加固定费项后,对当月账单进行补差(新增一条账单明细)
|
|
|
+ */
|
|
|
+ private void reconcileCurrentMonthBillAfterAppend(Long elderId, Long tenantId, ExpenseItemDO item,
|
|
|
+ String targetBillingMonth, LocalDate startDate, LocalDate endDate) {
|
|
|
+ ExpenseOrderDO currentOrder = expenseOrderMapper.selectOne(new LambdaQueryWrapperX<ExpenseOrderDO>()
|
|
|
+ .eq(ExpenseOrderDO::getElderId, elderId)
|
|
|
+ .eq(ExpenseOrderDO::getBillingMonth, targetBillingMonth)
|
|
|
+ .in(ExpenseOrderDO::getType, 2, 3)
|
|
|
+ .eq(tenantId != null, ExpenseOrderDO::getTenantId, tenantId)
|
|
|
+ .orderByDesc(ExpenseOrderDO::getCreatedTime)
|
|
|
+ .last("LIMIT 1"));
|
|
|
+
|
|
|
+ if (currentOrder == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (Boolean.TRUE.equals(currentOrder.getConfirmStatus()) || Boolean.TRUE.equals(currentOrder.getIsLock())) {
|
|
|
+ throw exceptionCustomMsg(ErrorCodeConstants.COMMON_ERROR, "当月账单已确认或已锁定,无法自动补差");
|
|
|
+ }
|
|
|
+
|
|
|
+ YearMonth billingYm = YearMonth.parse(targetBillingMonth, DateTimeFormatter.ofPattern("yyyy-MM"));
|
|
|
+ LocalDate billStart = billingYm.atDay(1);
|
|
|
+ LocalDate billEnd = billingYm.atEndOfMonth();
|
|
|
+
|
|
|
+ LocalDate actualStart = startDate.isBefore(billStart) ? billStart : startDate;
|
|
|
+ LocalDate actualEnd = endDate.isAfter(billEnd) ? billEnd : endDate;
|
|
|
+ if (actualEnd.isBefore(actualStart)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ long overlapDays = ChronoUnit.DAYS.between(actualStart, actualEnd) + 1;
|
|
|
+ int monthDays = billingYm.lengthOfMonth();
|
|
|
+ BigDecimal dayPrice = item.getActualAmount().divide(BigDecimal.valueOf(monthDays), 8, RoundingMode.DOWN);
|
|
|
+ BigDecimal billDeltaAmount = dayPrice.multiply(BigDecimal.valueOf(overlapDays)).setScale(2, RoundingMode.HALF_UP);
|
|
|
+ if (billDeltaAmount.compareTo(BigDecimal.ZERO) == 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ ExpenseOrderItemDO orderItem = new ExpenseOrderItemDO();
|
|
|
+ orderItem.setExpenseOrderId(currentOrder.getId());
|
|
|
+ orderItem.setSourceExpenseItemId(item.getId());
|
|
|
+ orderItem.setExpenseSource(BusinessConstants.EXPENSE_ITEM);
|
|
|
+ orderItem.setCount((int) overlapDays);
|
|
|
+ orderItem.setItemName(item.getItemName());
|
|
|
+ orderItem.setItemCategoryName(item.getItemCategoryName());
|
|
|
+ orderItem.setPrice(item.getAmount());
|
|
|
+ orderItem.setActualPrice(dayPrice);
|
|
|
+ orderItem.setTotalAmount(billDeltaAmount);
|
|
|
+ orderItem.setRoundAmount(billDeltaAmount.setScale(0, RoundingMode.HALF_UP));
|
|
|
+ orderItem.setRoundTwoDecimalAmount(billDeltaAmount.setScale(2, RoundingMode.HALF_UP));
|
|
|
+ orderItem.setCreatedTime(new Date());
|
|
|
+ orderItem.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
|
|
|
+ orderItem.setType(item.getType());
|
|
|
+ orderItem.setTenantId(tenantId);
|
|
|
+ orderItem.setStartDate(actualStart);
|
|
|
+ orderItem.setEndDate(actualEnd);
|
|
|
+ orderItem.setDescription("固定费项补差:" + actualStart + "至" + actualEnd + ",共" + overlapDays + "天," + item.getItemName());
|
|
|
+ expenseOrderItemMapper.insert(orderItem);
|
|
|
+
|
|
|
+ BigDecimal billAmount = currentOrder.getActualAmount() == null ? BigDecimal.ZERO : currentOrder.getActualAmount();
|
|
|
+ currentOrder.setActualAmount(billAmount.add(billDeltaAmount));
|
|
|
+ currentOrder.setPayStatus(BooleanEnum.FALSE.getValue());
|
|
|
+ currentOrder.setPayType(null);
|
|
|
+ expenseOrderMapper.updateById(currentOrder);
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 更新长者床位信息
|
|
|
*/
|