You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中基于ExpectedPayment模型计算未来12个月预期付款总额

预期付款月度总额计算方案

四类付款计入规则

先明确每类付款是否计入当前统计月份的判断标准,所有规则均默认排除date_start为空的无效数据:

  • 一次性付款(period=4):date_start落在当前统计月份的自然月范围内,即计入当月
  • 月度付款(period=1):当前统计月首日 >= date_start,且(当前统计月首日 <= date_end 或 date_end为空),即计入当月
  • 季度付款(period=2):
    1. 先满足上述月度付款的时间范围要求
    2. 计算当前统计月与date_start所在月的总月份差:(统计年 - 起始年)*12 + (统计月 - 起始月),差值模3等于0即属于付款月,计入当月
      该计算方式避开了不同月份天数、闰年的影响,完全符合「从date_start开始每3个月付一次」的要求,比如date_start为2月,2、5、8、11月均会被判定为付款月
  • 年度付款(period=3):
    1. 先满足上述月度付款的时间范围要求
    2. 总月份差模12等于0即属于付款月,计入当月

代码实现

依赖导入

from datetime import datetime
from dateutil.relativedelta import relativedelta
from django.db.models import Sum, Q

生成未来12个月的统计基准日期

# 生成未来12个月每个月的首日,作为统计基准
month_starts = []
current_month_start = datetime.today().replace(day=1, hour=0, minute=0, second=0, microsecond=0).date()
for i in range(12):
    month_starts.append(current_month_start + relativedelta(months=i))

单月总额计算函数

def calculate_single_month_total(stat_month_start):
    total_amount = 0
    # 统计当月月度付款总额
    monthly_total = ExpectedPayment.objects.filter(
        period=1,
        date_start__isnull=False,
        date_start__lte=stat_month_start,
        Q(date_end__gte=stat_month_start) | Q(date_end__isnull=True)
    ).aggregate(total=Sum("amount"))["total"] or 0
    total_amount += monthly_total

    # 统计当月一次性付款总额
    stat_month_end = stat_month_start + relativedelta(months=1, days=-1)
    onetime_total = ExpectedPayment.objects.filter(
        period=4,
        date_start__isnull=False,
        date_start__range=(stat_month_start, stat_month_end)
    ).aggregate(total=Sum("amount"))["total"] or 0
    total_amount += onetime_total

    # 统计当月季度付款总额
    quarter_payments = ExpectedPayment.objects.filter(
        period=2,
        date_start__isnull=False,
        date_start__lte=stat_month_start,
        Q(date_end__gte=stat_month_start) | Q(date_end__isnull=True)
    )
    quarter_total = 0
    for payment in quarter_payments:
        start_y = payment.date_start.year
        start_m = payment.date_start.month
        month_diff = (stat_month_start.year - start_y) * 12 + (stat_month_start.month - start_m)
        if month_diff % 3 == 0:
            quarter_total += payment.amount
    total_amount += quarter_total

    # 统计当年度付款总额
    year_payments = ExpectedPayment.objects.filter(
        period=3,
        date_start__isnull=False,
        date_start__lte=stat_month_start,
        Q(date_end__gte=stat_month_start) | Q(date_end__isnull=True)
    )
    year_total = 0
    for payment in year_payments:
        start_y = payment.date_start.year
        start_m = payment.date_start.month
        month_diff = (stat_month_start.year - start_y) * 12 + (stat_month_start.month - start_m)
        if month_diff % 12 == 0:
            year_total += payment.amount
    total_amount += year_total

    return total_amount

批量计算未来12个月总额

# 返回结构为 年月字符串: 总额 的字典
monthly_payment_stats = {}
for month_start in month_starts:
    month_key = month_start.strftime("%Y-%m")
    monthly_payment_stats[month_key] = calculate_single_month_total(month_start)

性能优化建议

如果数据量较大(万级以上),可以将季度、年度的月份差判断逻辑迁移到数据库层,通过annotate结合ExtractYear、ExtractMonth数据库函数计算差值,无需拉取全量数据到内存循环判断,可大幅提升查询效率。

内容的提问来源于stack exchange,提问作者Spoontech

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 23:36:01