gaoluyang
昨天 90b3ad820eea45d021835197eac5bc3ad55b12ed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
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;
    }
 
}