From a5d7ef82a926d72651bb35779a59883528a9d641 Mon Sep 17 00:00:00 2001
From: liyong <18434998025@163.com>
Date: 星期一, 18 五月 2026 11:58:24 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_New_pro' into dev_New_pro
---
src/main/java/com/ruoyi/account/service/impl/financial/FinVoucherServiceImpl.java | 307 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 307 insertions(+), 0 deletions(-)
diff --git a/src/main/java/com/ruoyi/account/service/impl/financial/FinVoucherServiceImpl.java b/src/main/java/com/ruoyi/account/service/impl/financial/FinVoucherServiceImpl.java
new file mode 100644
index 0000000..b7548ef
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/impl/financial/FinVoucherServiceImpl.java
@@ -0,0 +1,307 @@
+package com.ruoyi.account.service.impl.financial;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.account.bean.dto.financial.FinVoucherDto;
+import com.ruoyi.account.bean.dto.financial.FinVoucherEntryDto;
+import com.ruoyi.account.bean.dto.financial.FinVoucherPageDto;
+import com.ruoyi.account.bean.vo.financial.FinVoucherDetailVo;
+import com.ruoyi.account.mapper.AccountSubjectMapper;
+import com.ruoyi.account.mapper.financial.FinVoucherEntryMapper;
+import com.ruoyi.account.mapper.financial.FinVoucherMapper;
+import com.ruoyi.account.pojo.AccountSubject;
+import com.ruoyi.account.pojo.financial.FinVoucher;
+import com.ruoyi.account.pojo.financial.FinVoucherEntry;
+import com.ruoyi.account.service.financial.FinVoucherService;
+import com.ruoyi.basic.enums.ApplicationTypeEnum;
+import com.ruoyi.basic.enums.RecordTypeEnum;
+import com.ruoyi.basic.utils.FileUtil;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 鍑瘉鏈嶅姟瀹炵幇銆�
+ */
+@Service
+@RequiredArgsConstructor
+public class FinVoucherServiceImpl extends ServiceImpl<FinVoucherMapper, FinVoucher> implements FinVoucherService {
+
+ private static final BigDecimal ZERO = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
+
+ private final FinVoucherEntryMapper finVoucherEntryMapper;
+ private final AccountSubjectMapper accountSubjectMapper;
+ private final FileUtil fileUtil;
+
+ @Override
+ public IPage<FinVoucher> pageList(Page<FinVoucher> page, FinVoucherPageDto queryDto) {
+ LambdaQueryWrapper<FinVoucher> wrapper = new LambdaQueryWrapper<>();
+ if (queryDto != null && StringUtils.isNotEmpty(queryDto.getVoucherNo())) {
+ wrapper.like(FinVoucher::getVoucherNo, queryDto.getVoucherNo());
+ }
+ if (queryDto != null && StringUtils.isNotEmpty(queryDto.getCreator())) {
+ wrapper.eq(FinVoucher::getCreator, queryDto.getCreator());
+ }
+ if (queryDto != null && StringUtils.isNotEmpty(queryDto.getStatus())) {
+ wrapper.eq(FinVoucher::getStatus, queryDto.getStatus());
+ }
+ if (queryDto != null && queryDto.getStartDate() != null) {
+ wrapper.ge(FinVoucher::getVoucherDate, queryDto.getStartDate());
+ }
+ if (queryDto != null && queryDto.getEndDate() != null) {
+ wrapper.le(FinVoucher::getVoucherDate, queryDto.getEndDate());
+ }
+ wrapper.orderByDesc(FinVoucher::getVoucherDate).orderByDesc(FinVoucher::getId);
+ return page(page, wrapper);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean addVoucher(FinVoucherDto dto) {
+ validateVoucherBasicInfo(dto, false);
+ List<FinVoucherEntry> validEntries = buildAndValidateEntries(dto);
+
+ FinVoucher voucher = new FinVoucher();
+ BeanUtils.copyProperties(dto, voucher);
+ voucher.setStatus("unposted");
+ voucher.setAttachmentCount(voucher.getAttachmentCount() == null ? 0 : voucher.getAttachmentCount());
+ BigDecimal totalDebit = calculateTotalDebit(validEntries);
+ BigDecimal totalCredit = calculateTotalCredit(validEntries);
+ voucher.setDebit(totalDebit);
+ voucher.setCredit(totalCredit);
+ if (StringUtils.isEmpty(voucher.getSummary())) {
+ voucher.setSummary(findDefaultSummary(validEntries));
+ }
+ save(voucher);
+ saveEntries(voucher.getId(), validEntries);
+ // 5. 淇濆瓨閿�鍞彴璐﹂檮浠�
+ fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.FIN_VOUCHER, voucher.getId(), dto.getStorageBlobDTOs());
+ return true;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean updateVoucher(FinVoucherDto dto) {
+ validateVoucherBasicInfo(dto, true);
+ FinVoucher existed = getById(dto.getId());
+ if (existed == null) {
+ throw new ServiceException("淇敼澶辫触锛屽嚟璇佷笉瀛樺湪");
+ }
+ if (!"unposted".equals(existed.getStatus())) {
+ throw new ServiceException("浠呮湭杩囪处鍑瘉鍏佽淇敼");
+ }
+ List<FinVoucherEntry> validEntries = buildAndValidateEntries(dto);
+
+ FinVoucher voucher = new FinVoucher();
+ BeanUtils.copyProperties(dto, voucher);
+ voucher.setStatus(existed.getStatus());
+ voucher.setAttachmentCount(voucher.getAttachmentCount() == null ? 0 : voucher.getAttachmentCount());
+ BigDecimal totalDebit = calculateTotalDebit(validEntries);
+ BigDecimal totalCredit = calculateTotalCredit(validEntries);
+ voucher.setDebit(totalDebit);
+ voucher.setCredit(totalCredit);
+ if (StringUtils.isEmpty(voucher.getSummary())) {
+ voucher.setSummary(findDefaultSummary(validEntries));
+ }
+ updateById(voucher);
+
+ LambdaQueryWrapper<FinVoucherEntry> deleteWrapper = new LambdaQueryWrapper<>();
+ deleteWrapper.eq(FinVoucherEntry::getVoucherId, voucher.getId());
+ finVoucherEntryMapper.delete(deleteWrapper);
+ saveEntries(voucher.getId(), validEntries);
+ fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.FIN_VOUCHER, voucher.getId(), dto.getStorageBlobDTOs());
+ return true;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean postVoucher(Long id) {
+ FinVoucher voucher = getById(id);
+ if (voucher == null) {
+ throw new ServiceException("杩囪处澶辫触锛屽嚟璇佷笉瀛樺湪");
+ }
+ if (!"unposted".equals(voucher.getStatus())) {
+ throw new ServiceException("浠呮湭杩囪处鍑瘉鍏佽杩囪处");
+ }
+ voucher.setStatus("posted");
+ return updateById(voucher);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean cancelVoucher(Long id) {
+ FinVoucher voucher = getById(id);
+ if (voucher == null) {
+ throw new ServiceException("浣滃簾澶辫触锛屽嚟璇佷笉瀛樺湪");
+ }
+ if (!"unposted".equals(voucher.getStatus())) {
+ throw new ServiceException("浠呮湭杩囪处鍑瘉鍏佽浣滃簾");
+ }
+ voucher.setStatus("cancelled");
+ return updateById(voucher);
+ }
+
+ @Override
+ public FinVoucherDetailVo detail(Long id) {
+ FinVoucher voucher = getById(id);
+ if (voucher == null) {
+ throw new ServiceException("鏌ヨ澶辫触锛屽嚟璇佷笉瀛樺湪");
+ }
+ LambdaQueryWrapper<FinVoucherEntry> wrapper = new LambdaQueryWrapper<>();
+ wrapper.eq(FinVoucherEntry::getVoucherId, id)
+ .orderByAsc(FinVoucherEntry::getRowNo)
+ .orderByAsc(FinVoucherEntry::getId);
+ List<FinVoucherEntry> entries = finVoucherEntryMapper.selectList(wrapper);
+
+ FinVoucherDetailVo vo = new FinVoucherDetailVo();
+ BeanUtils.copyProperties(voucher, vo);
+ vo.setEntries(entries);
+ vo.setStorageBlobVOList(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.FIN_VOUCHER, id));
+ return vo;
+ }
+
+ /**
+ * 鏍¢獙鍑瘉涓昏〃瀛楁銆佺姸鎬佸瓧娈典笌鍞竴鎬с��
+ */
+ private void validateVoucherBasicInfo(FinVoucherDto dto, boolean isUpdate) {
+ if (dto == null) {
+ throw new ServiceException("鍑瘉鏁版嵁涓嶈兘涓虹┖");
+ }
+ if (isUpdate && dto.getId() == null) {
+ throw new ServiceException("淇敼澶辫触锛屽嚟璇両D涓嶈兘涓虹┖");
+ }
+ if (StringUtils.isEmpty(dto.getVoucherNo())) {
+ throw new ServiceException("鍑瘉瀛楀彿涓嶈兘涓虹┖");
+ }
+ if (dto.getVoucherDate() == null) {
+ throw new ServiceException("鍑瘉鏃ユ湡涓嶈兘涓虹┖");
+ }
+ LambdaQueryWrapper<FinVoucher> uniqueWrapper = new LambdaQueryWrapper<>();
+ uniqueWrapper.eq(FinVoucher::getVoucherNo, dto.getVoucherNo());
+ if (isUpdate) {
+ uniqueWrapper.ne(FinVoucher::getId, dto.getId());
+ }
+ if (count(uniqueWrapper) > 0) {
+ throw new ServiceException("鍑瘉瀛楀彿宸插瓨鍦紝璇峰嬁閲嶅鎻愪氦");
+ }
+ }
+
+ /**
+ * 杩囨护鏈夋晥鍒嗗綍骞舵墽琛屽�熻捶骞宠 鏍¢獙銆�
+ */
+ private List<FinVoucherEntry> buildAndValidateEntries(FinVoucherDto dto) {
+ List<FinVoucherEntryDto> rawEntries = dto.getEntries();
+ if (rawEntries == null || rawEntries.isEmpty()) {
+ throw new ServiceException("鍒嗗綍涓嶈兘涓虹┖锛岃嚦灏戦渶瑕佷竴鏉℃湁鏁堝垎褰�");
+ }
+ List<FinVoucherEntry> validEntries = new ArrayList<>();
+ int rowNo = 1;
+ for (FinVoucherEntryDto entryDto : rawEntries) {
+ if (entryDto == null || StringUtils.isEmpty(entryDto.getSubjectCode())) {
+ continue;
+ }
+ BigDecimal debit = defaultMoney(entryDto.getDebit());
+ BigDecimal credit = defaultMoney(entryDto.getCredit());
+ if (debit.compareTo(BigDecimal.ZERO) <= 0 && credit.compareTo(BigDecimal.ZERO) <= 0) {
+ continue;
+ }
+ if (debit.compareTo(BigDecimal.ZERO) > 0 && credit.compareTo(BigDecimal.ZERO) > 0) {
+ throw new ServiceException("鍒嗗綍鍊熸柟鍜岃捶鏂逛笉鑳藉悓鏃跺ぇ浜�0");
+ }
+ FinVoucherEntry entry = new FinVoucherEntry();
+ BeanUtils.copyProperties(entryDto, entry);
+ entry.setDebit(debit);
+ entry.setCredit(credit);
+ entry.setRowNo(rowNo++);
+ validEntries.add(entry);
+ }
+ if (validEntries.isEmpty()) {
+ throw new ServiceException("鍒嗗綍鑷冲皯闇�瑕佷竴鏉℃湁鏁堣锛堢鐩笉绌猴紝涓斿�熸柟鎴栬捶鏂瑰ぇ浜�0锛�");
+ }
+
+ // 鍒嗗綍绉戠洰蹇呴』瀛樺湪锛岄伩鍏嶈剰绉戠洰缂栫爜鍏ヨ处銆�
+ Set<String> subjectCodes = validEntries.stream()
+ .map(FinVoucherEntry::getSubjectCode)
+ .filter(StringUtils::isNotEmpty)
+ .collect(Collectors.toSet());
+ if (subjectCodes.isEmpty()) {
+ throw new ServiceException("鍒嗗綍绉戠洰涓嶈兘涓虹┖");
+ }
+ LambdaQueryWrapper<AccountSubject> subjectWrapper = new LambdaQueryWrapper<>();
+ subjectWrapper.in(AccountSubject::getSubjectCode, subjectCodes);
+ List<AccountSubject> subjects = accountSubjectMapper.selectList(subjectWrapper);
+ Map<String, AccountSubject> subjectMap = subjects.stream()
+ .collect(Collectors.toMap(AccountSubject::getSubjectCode, it -> it, (a, b) -> a));
+ for (FinVoucherEntry entry : validEntries) {
+ AccountSubject accountSubject = subjectMap.get(entry.getSubjectCode());
+ if (accountSubject == null) {
+ throw new ServiceException("绉戠洰缂栫爜涓嶅瓨鍦細" + entry.getSubjectCode());
+ }
+ if (StringUtils.isEmpty(entry.getSubjectName())) {
+ entry.setSubjectName(accountSubject.getSubjectName());
+ }
+ }
+
+ BigDecimal totalDebit = calculateTotalDebit(validEntries);
+ BigDecimal totalCredit = calculateTotalCredit(validEntries);
+ if (totalDebit.compareTo(BigDecimal.ZERO) <= 0 || totalCredit.compareTo(BigDecimal.ZERO) <= 0) {
+ throw new ServiceException("鍊熻捶閲戦蹇呴』澶т簬0");
+ }
+ if (totalDebit.compareTo(totalCredit) != 0) {
+ throw new ServiceException("鍊熻捶涓嶅钩琛★紝绂佹淇濆瓨");
+ }
+ return validEntries;
+ }
+
+ private void saveEntries(Long voucherId, List<FinVoucherEntry> entries) {
+ if (voucherId == null) {
+ throw new ServiceException("鍑瘉ID涓嶈兘涓虹┖");
+ }
+ for (FinVoucherEntry entry : entries) {
+ entry.setVoucherId(voucherId);
+ finVoucherEntryMapper.insert(entry);
+ }
+ }
+
+ private String findDefaultSummary(List<FinVoucherEntry> entries) {
+ for (FinVoucherEntry entry : entries) {
+ if (StringUtils.isNotEmpty(entry.getSummary())) {
+ return entry.getSummary();
+ }
+ }
+ return "";
+ }
+
+ private BigDecimal calculateTotalDebit(List<FinVoucherEntry> entries) {
+ BigDecimal total = BigDecimal.ZERO;
+ for (FinVoucherEntry entry : entries) {
+ total = total.add(defaultMoney(entry.getDebit()));
+ }
+ return total.setScale(2, RoundingMode.HALF_UP);
+ }
+
+ private BigDecimal calculateTotalCredit(List<FinVoucherEntry> entries) {
+ BigDecimal total = BigDecimal.ZERO;
+ for (FinVoucherEntry entry : entries) {
+ total = total.add(defaultMoney(entry.getCredit()));
+ }
+ return total.setScale(2, RoundingMode.HALF_UP);
+ }
+
+ private BigDecimal defaultMoney(BigDecimal value) {
+ if (value == null) {
+ return ZERO;
+ }
+ return value.setScale(2, RoundingMode.HALF_UP);
+ }
+}
--
Gitblit v1.9.3