package com.ruoyi.account.service.impl.financial;
|
|
import com.ruoyi.account.bean.dto.financial.FinDetailLedgerQueryDto;
|
import com.ruoyi.account.bean.dto.financial.FinLedgerQueryDto;
|
import com.ruoyi.account.bean.vo.financial.FinLedgerEntryRecordVo;
|
import com.ruoyi.account.bean.vo.financial.FinLedgerRowVo;
|
import com.ruoyi.account.mapper.financial.FinVoucherEntryMapper;
|
import com.ruoyi.account.service.financial.FinLedgerService;
|
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.common.utils.StringUtils;
|
import lombok.RequiredArgsConstructor;
|
import org.springframework.stereotype.Service;
|
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.time.LocalDate;
|
import java.time.YearMonth;
|
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeParseException;
|
import java.util.*;
|
|
/**
|
* 科目总账/明细账服务实现。
|
*/
|
@Service
|
@RequiredArgsConstructor
|
public class FinLedgerServiceImpl implements FinLedgerService {
|
|
private static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM");
|
private static final BigDecimal ZERO = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
|
|
private final FinVoucherEntryMapper finVoucherEntryMapper;
|
|
@Override
|
public List<FinLedgerRowVo> queryGeneralLedger(FinLedgerQueryDto queryDto) {
|
if (queryDto == null || StringUtils.isEmpty(queryDto.getSubjectCode())) {
|
return Collections.emptyList();
|
}
|
YearMonth startMonth = parseMonth(queryDto.getStartMonth(), "开始月份");
|
YearMonth endMonth = parseMonth(queryDto.getEndMonth(), "结束月份");
|
if (startMonth.isAfter(endMonth)) {
|
throw new ServiceException("开始月份不能大于结束月份");
|
}
|
return buildLedgerRows(queryDto.getSubjectCode(), startMonth, endMonth, null, null);
|
}
|
|
@Override
|
public List<FinLedgerRowVo> queryDetailLedger(FinDetailLedgerQueryDto queryDto) {
|
if (queryDto == null || StringUtils.isEmpty(queryDto.getSubjectCode())) {
|
return Collections.emptyList();
|
}
|
YearMonth startMonth = parseMonth(queryDto.getStartMonth(), "开始月份");
|
YearMonth endMonth = parseMonth(queryDto.getEndMonth(), "结束月份");
|
if (startMonth.isAfter(endMonth)) {
|
throw new ServiceException("开始月份不能大于结束月份");
|
}
|
return buildLedgerRows(queryDto.getSubjectCode(), startMonth, endMonth, queryDto.getAuxiliaryType(), queryDto.getAuxiliaryId());
|
}
|
|
/**
|
* 构建账簿行数据,输出期初、分录、本月合计、本年累计。
|
*/
|
private List<FinLedgerRowVo> buildLedgerRows(String subjectCode,
|
YearMonth startMonth,
|
YearMonth endMonth,
|
String auxiliaryType,
|
String auxiliaryId) {
|
LocalDate startDate = startMonth.atDay(1);
|
LocalDate endDate = endMonth.atEndOfMonth();
|
|
List<FinLedgerEntryRecordVo> openingEntries = finVoucherEntryMapper.listPostedEntriesBefore(
|
subjectCode, startDate, auxiliaryType, auxiliaryId
|
);
|
BigDecimal openingBalance = calculateBalance(openingEntries);
|
|
List<FinLedgerEntryRecordVo> currentPeriodEntries = finVoucherEntryMapper.listPostedEntries(
|
subjectCode, startDate, endDate, auxiliaryType, auxiliaryId
|
);
|
Map<YearMonth, List<FinLedgerEntryRecordVo>> monthEntriesMap = groupEntriesByMonth(currentPeriodEntries);
|
|
List<FinLedgerRowVo> rows = new ArrayList<>();
|
BigDecimal runningBalance = openingBalance;
|
BigDecimal yearDebit = ZERO;
|
BigDecimal yearCredit = ZERO;
|
|
for (YearMonth month = startMonth; !month.isAfter(endMonth); month = month.plusMonths(1)) {
|
rows.add(buildOpeningRow(month.atDay(1), runningBalance));
|
|
List<FinLedgerEntryRecordVo> monthEntries = monthEntriesMap.getOrDefault(month, Collections.emptyList());
|
BigDecimal monthDebit = ZERO;
|
BigDecimal monthCredit = ZERO;
|
for (FinLedgerEntryRecordVo entry : monthEntries) {
|
BigDecimal debit = money(entry.getDebit());
|
BigDecimal credit = money(entry.getCredit());
|
runningBalance = runningBalance.add(debit).subtract(credit);
|
monthDebit = monthDebit.add(debit);
|
monthCredit = monthCredit.add(credit);
|
|
FinLedgerRowVo row = new FinLedgerRowVo();
|
row.setRowType("entry");
|
row.setDate(entry.getVoucherDate());
|
row.setVoucherNo(entry.getVoucherNo());
|
row.setSummary(StringUtils.isNotEmpty(entry.getSummary()) ? entry.getSummary() : "");
|
row.setDebit(debit);
|
row.setCredit(credit);
|
row.setBalance(money(runningBalance));
|
row.setDirection(resolveDirection(runningBalance));
|
rows.add(row);
|
}
|
|
rows.add(buildMonthlyTotalRow(month.atEndOfMonth(), monthDebit, monthCredit, runningBalance));
|
yearDebit = yearDebit.add(monthDebit);
|
yearCredit = yearCredit.add(monthCredit);
|
}
|
|
rows.add(buildYearlyTotalRow(endMonth.atEndOfMonth(), yearDebit, yearCredit, runningBalance));
|
return rows;
|
}
|
|
private Map<YearMonth, List<FinLedgerEntryRecordVo>> groupEntriesByMonth(List<FinLedgerEntryRecordVo> entries) {
|
Map<YearMonth, List<FinLedgerEntryRecordVo>> map = new LinkedHashMap<>();
|
for (FinLedgerEntryRecordVo entry : entries) {
|
if (entry.getVoucherDate() == null) {
|
continue;
|
}
|
YearMonth month = YearMonth.from(entry.getVoucherDate());
|
map.computeIfAbsent(month, key -> new ArrayList<>()).add(entry);
|
}
|
return map;
|
}
|
|
private FinLedgerRowVo buildOpeningRow(LocalDate date, BigDecimal openingBalance) {
|
FinLedgerRowVo row = new FinLedgerRowVo();
|
row.setRowType("opening");
|
row.setDate(date);
|
row.setVoucherNo("-");
|
row.setSummary("期初余额");
|
row.setDebit(ZERO);
|
row.setCredit(ZERO);
|
row.setBalance(money(openingBalance));
|
row.setDirection(resolveDirection(openingBalance));
|
return row;
|
}
|
|
private FinLedgerRowVo buildMonthlyTotalRow(LocalDate date,
|
BigDecimal monthDebit,
|
BigDecimal monthCredit,
|
BigDecimal monthBalance) {
|
FinLedgerRowVo row = new FinLedgerRowVo();
|
row.setRowType("monthly_total");
|
row.setDate(date);
|
row.setVoucherNo("-");
|
row.setSummary("本月合计");
|
row.setDebit(money(monthDebit));
|
row.setCredit(money(monthCredit));
|
row.setBalance(money(monthBalance));
|
row.setDirection(resolveDirection(monthBalance));
|
return row;
|
}
|
|
private FinLedgerRowVo buildYearlyTotalRow(LocalDate date,
|
BigDecimal yearDebit,
|
BigDecimal yearCredit,
|
BigDecimal yearBalance) {
|
FinLedgerRowVo row = new FinLedgerRowVo();
|
row.setRowType("yearly_total");
|
row.setDate(date);
|
row.setVoucherNo("-");
|
row.setSummary("本年累计");
|
row.setDebit(money(yearDebit));
|
row.setCredit(money(yearCredit));
|
row.setBalance(money(yearBalance));
|
row.setDirection(resolveDirection(yearBalance));
|
return row;
|
}
|
|
private BigDecimal calculateBalance(List<FinLedgerEntryRecordVo> entries) {
|
BigDecimal balance = ZERO;
|
for (FinLedgerEntryRecordVo entry : entries) {
|
balance = balance.add(money(entry.getDebit())).subtract(money(entry.getCredit()));
|
}
|
return money(balance);
|
}
|
|
private String resolveDirection(BigDecimal balance) {
|
return money(balance).compareTo(BigDecimal.ZERO) >= 0 ? "借" : "贷";
|
}
|
|
private YearMonth parseMonth(String value, String fieldLabel) {
|
if (StringUtils.isEmpty(value)) {
|
throw new ServiceException(fieldLabel + "不能为空,格式应为YYYY-MM");
|
}
|
try {
|
return YearMonth.parse(value, MONTH_FORMATTER);
|
} catch (DateTimeParseException ex) {
|
throw new ServiceException(fieldLabel + "格式错误,格式应为YYYY-MM");
|
}
|
}
|
|
private BigDecimal money(BigDecimal value) {
|
if (value == null) {
|
return ZERO;
|
}
|
return value.setScale(2, RoundingMode.HALF_UP);
|
}
|
}
|