package cn.iocoder.yudao.module.finance.service.statistics;
|
|
import cn.iocoder.yudao.module.erp.api.finance.ErpFinanceStatisticsApi;
|
import cn.iocoder.yudao.module.erp.api.finance.dto.ErpFinanceMonthlyStatisticsRespDTO;
|
import cn.iocoder.yudao.module.finance.controller.admin.statistics.vo.FinStatisticsOverviewRespVO;
|
import cn.iocoder.yudao.module.finance.controller.admin.statistics.vo.FinStatisticsReqVO;
|
import cn.iocoder.yudao.module.finance.controller.admin.statistics.vo.FinStatisticsTrendRespVO;
|
import cn.iocoder.yudao.module.finance.dal.dataobject.asset.FinFixedAssetDO;
|
import cn.iocoder.yudao.module.finance.dal.dataobject.asset.FinIntangibleAssetDO;
|
import cn.iocoder.yudao.module.finance.dal.mysql.subject.FinAccountSubjectMapper;
|
import cn.iocoder.yudao.module.finance.dal.mysql.voucher.FinVoucherMapper;
|
import cn.iocoder.yudao.module.finance.enums.FinVoucherStatusEnum;
|
import cn.iocoder.yudao.module.finance.service.asset.FinFixedAssetService;
|
import cn.iocoder.yudao.module.finance.service.asset.FinIntangibleAssetService;
|
import jakarta.annotation.Resource;
|
import org.springframework.stereotype.Service;
|
import org.springframework.validation.annotation.Validated;
|
|
import java.math.BigDecimal;
|
import java.time.LocalDate;
|
import java.time.LocalDateTime;
|
import java.time.YearMonth;
|
import java.util.ArrayList;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.function.Function;
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
import static cn.iocoder.yudao.module.finance.enums.ErrorCodeConstants.STATISTICS_PERIOD_INVALID;
|
|
/**
|
* 财务报表 / 会计核算看板 Service 实现类
|
*
|
* @author 超级管理员
|
*/
|
@Service
|
@Validated
|
public class FinStatisticsServiceImpl implements FinStatisticsService {
|
|
@Resource
|
private ErpFinanceStatisticsApi erpFinanceStatisticsApi;
|
@Resource
|
private FinVoucherMapper voucherMapper;
|
@Resource
|
private FinAccountSubjectMapper accountSubjectMapper;
|
@Resource
|
private FinFixedAssetService fixedAssetService;
|
@Resource
|
private FinIntangibleAssetService intangibleAssetService;
|
|
@Override
|
public FinStatisticsOverviewRespVO getStatisticsOverview(FinStatisticsReqVO reqVO) {
|
// 1. 期间
|
LocalDate startDate = resolveStartDate(reqVO);
|
LocalDate endDate = resolveEndDate(reqVO);
|
validatePeriod(startDate, endDate);
|
|
// 2. ERP 收支、购销金额
|
List<ErpFinanceMonthlyStatisticsRespDTO> monthlyList = getErpMonthlyStatistics(startDate, endDate);
|
BigDecimal receiptPrice = sumOf(monthlyList, ErpFinanceMonthlyStatisticsRespDTO::getReceiptPrice);
|
BigDecimal paymentPrice = sumOf(monthlyList, ErpFinanceMonthlyStatisticsRespDTO::getPaymentPrice);
|
BigDecimal saleOrderPrice = sumOf(monthlyList, ErpFinanceMonthlyStatisticsRespDTO::getSaleOrderPrice);
|
BigDecimal purchaseOrderPrice = sumOf(monthlyList, ErpFinanceMonthlyStatisticsRespDTO::getPurchaseOrderPrice);
|
|
// 3. 固定资产、无形资产(时点数,取全部资产)
|
BigDecimal fixedAssetOriginalValue = BigDecimal.ZERO;
|
BigDecimal fixedAssetAccumulatedDepreciation = BigDecimal.ZERO;
|
BigDecimal fixedAssetNetValue = BigDecimal.ZERO;
|
for (FinFixedAssetDO asset : fixedAssetService.getFixedAssetList()) {
|
fixedAssetOriginalValue = fixedAssetOriginalValue.add(defaultIfNull(asset.getOriginalValue()));
|
fixedAssetAccumulatedDepreciation = fixedAssetAccumulatedDepreciation
|
.add(defaultIfNull(asset.getAccumulatedDepreciation()));
|
fixedAssetNetValue = fixedAssetNetValue.add(defaultIfNull(asset.getNetValue()));
|
}
|
BigDecimal intangibleAssetOriginalValue = BigDecimal.ZERO;
|
BigDecimal intangibleAssetAccumulatedAmortization = BigDecimal.ZERO;
|
BigDecimal intangibleAssetNetValue = BigDecimal.ZERO;
|
for (FinIntangibleAssetDO asset : intangibleAssetService.getIntangibleAssetList()) {
|
intangibleAssetOriginalValue = intangibleAssetOriginalValue.add(defaultIfNull(asset.getOriginalValue()));
|
intangibleAssetAccumulatedAmortization = intangibleAssetAccumulatedAmortization
|
.add(defaultIfNull(asset.getAccumulatedAmortization()));
|
intangibleAssetNetValue = intangibleAssetNetValue.add(defaultIfNull(asset.getNetValue()));
|
}
|
|
// 4. 凭证统计
|
Long voucherUnpostedCount = defaultIfNull(voucherMapper.selectCountByStatus(FinVoucherStatusEnum.UNPOSTED.getStatus()));
|
Long voucherPostedCount = defaultIfNull(voucherMapper.selectCountByStatus(FinVoucherStatusEnum.POSTED.getStatus()));
|
Long voucherCancelledCount = defaultIfNull(voucherMapper.selectCountByStatus(FinVoucherStatusEnum.CANCELLED.getStatus()));
|
|
// 5. 组装
|
return new FinStatisticsOverviewRespVO()
|
.setStartDate(startDate).setEndDate(endDate)
|
.setReceiptPrice(receiptPrice)
|
.setPaymentPrice(paymentPrice)
|
.setNetProfit(receiptPrice.subtract(paymentPrice))
|
.setSaleOrderPrice(saleOrderPrice)
|
.setPurchaseOrderPrice(purchaseOrderPrice)
|
.setFixedAssetOriginalValue(fixedAssetOriginalValue)
|
.setFixedAssetAccumulatedDepreciation(fixedAssetAccumulatedDepreciation)
|
.setFixedAssetNetValue(fixedAssetNetValue)
|
.setIntangibleAssetOriginalValue(intangibleAssetOriginalValue)
|
.setIntangibleAssetAccumulatedAmortization(intangibleAssetAccumulatedAmortization)
|
.setIntangibleAssetNetValue(intangibleAssetNetValue)
|
.setTotalAssetNetValue(fixedAssetNetValue.add(intangibleAssetNetValue))
|
.setSubjectCount(defaultIfNull(accountSubjectMapper.selectCount()))
|
.setVoucherCount(voucherUnpostedCount + voucherPostedCount + voucherCancelledCount)
|
.setVoucherUnpostedCount(voucherUnpostedCount)
|
.setVoucherPostedCount(voucherPostedCount)
|
.setVoucherCancelledCount(voucherCancelledCount);
|
}
|
|
@Override
|
public List<FinStatisticsTrendRespVO> getStatisticsTrend(FinStatisticsReqVO reqVO) {
|
// 1. 期间
|
LocalDate startDate = resolveStartDate(reqVO);
|
LocalDate endDate = resolveEndDate(reqVO);
|
validatePeriod(startDate, endDate);
|
|
// 2. ERP 月度数据转 Map,便于按月份补 0
|
Map<String, ErpFinanceMonthlyStatisticsRespDTO> monthMap = convertMap(
|
getErpMonthlyStatistics(startDate, endDate), ErpFinanceMonthlyStatisticsRespDTO::getMonth);
|
|
// 3. 期间内逐月输出,没有数据的月份补 0
|
List<FinStatisticsTrendRespVO> list = new ArrayList<>();
|
YearMonth endMonth = YearMonth.from(endDate);
|
for (YearMonth month = YearMonth.from(startDate); !month.isAfter(endMonth); month = month.plusMonths(1)) {
|
ErpFinanceMonthlyStatisticsRespDTO dto = monthMap.get(month.toString());
|
BigDecimal receiptPrice = dto == null ? BigDecimal.ZERO : defaultIfNull(dto.getReceiptPrice());
|
BigDecimal paymentPrice = dto == null ? BigDecimal.ZERO : defaultIfNull(dto.getPaymentPrice());
|
list.add(new FinStatisticsTrendRespVO()
|
.setMonth(month.toString())
|
.setReceiptPrice(receiptPrice)
|
.setPaymentPrice(paymentPrice)
|
.setNetProfit(receiptPrice.subtract(paymentPrice))
|
.setSaleOrderPrice(dto == null ? BigDecimal.ZERO : defaultIfNull(dto.getSaleOrderPrice()))
|
.setPurchaseOrderPrice(dto == null ? BigDecimal.ZERO : defaultIfNull(dto.getPurchaseOrderPrice())));
|
}
|
return list;
|
}
|
|
/**
|
* 调用 ERP 模块 API 获取月度统计,并做兜底解包
|
*/
|
private List<ErpFinanceMonthlyStatisticsRespDTO> getErpMonthlyStatistics(LocalDate startDate, LocalDate endDate) {
|
List<ErpFinanceMonthlyStatisticsRespDTO> list = erpFinanceStatisticsApi
|
.getMonthlyStatistics(startDate.atStartOfDay(), endDate.plusDays(1).atStartOfDay())
|
.getCheckedData();
|
return list == null ? List.of() : list;
|
}
|
|
private LocalDate resolveStartDate(FinStatisticsReqVO reqVO) {
|
return reqVO.getStartMonth() != null ? reqVO.getStartMonth().atDay(1)
|
: LocalDate.now().withDayOfYear(1);
|
}
|
|
private LocalDate resolveEndDate(FinStatisticsReqVO reqVO) {
|
return reqVO.getEndMonth() != null ? reqVO.getEndMonth().atEndOfMonth()
|
: LocalDate.now();
|
}
|
|
private void validatePeriod(LocalDate startDate, LocalDate endDate) {
|
if (startDate.isAfter(endDate)) {
|
throw exception(STATISTICS_PERIOD_INVALID, YearMonth.from(startDate), YearMonth.from(endDate));
|
}
|
}
|
|
private static BigDecimal sumOf(List<ErpFinanceMonthlyStatisticsRespDTO> list,
|
Function<ErpFinanceMonthlyStatisticsRespDTO, BigDecimal> getter) {
|
return list.stream().map(getter).map(FinStatisticsServiceImpl::defaultIfNull)
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
}
|
|
private static BigDecimal defaultIfNull(BigDecimal amount) {
|
return amount == null ? BigDecimal.ZERO : amount;
|
}
|
|
private static Long defaultIfNull(Long value) {
|
return value == null ? 0L : value;
|
}
|
|
}
|