| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |