| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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("ä¿®æ¹å¤±è´¥ï¼åè¯IDä¸è½ä¸ºç©º"); |
| | | } |
| | | 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); |
| | | } |
| | | } |