1
0
forked from erp-dev/erp

feat: settlement first api beta

This commit is contained in:
2026-02-27 18:26:12 +08:00
parent c564b1af32
commit 86f5dab32b
16 changed files with 1854 additions and 10 deletions

View File

@@ -1,10 +1,217 @@
"""日结模块统计服务函数"""
import logging
from datetime import datetime
from datetime import datetime, date
from django.db.models import Q, Sum, Case, When, Value, CharField, IntegerField
from django.db.models.functions import Concat
from printing.models import PlateOrder
logger = logging.getLogger(__name__)
def get_plate_order_summary_by_customer(
merchant_id: int,
settlement_date: date,
user=None
) -> list[dict]:
"""
获取按客户分组的开版订单统计
Args:
merchant_id: 商户ID
settlement_date: 统计日期
user: 当前用户(用于客户可见性过滤)
Returns:
list[dict]: 客户统计列表
"""
logger.info(
f'[settlement.services] 获取开版订单统计: '
f'merchant_id={merchant_id}, date={settlement_date}'
)
settlement_date = _validate_and_normalize_summary_args(
merchant_id=merchant_id,
settlement_date=settlement_date,
)
queryset = _get_plate_order_queryset(merchant_id, user)
aggregated_data = _aggregate_plate_orders_by_customer_and_type(
queryset, settlement_date
)
return _format_plate_order_summary(aggregated_data)
def _validate_and_normalize_summary_args(
merchant_id: int,
settlement_date: date,
) -> date:
"""校验并标准化统计参数。"""
if not isinstance(merchant_id, int) or merchant_id <= 0:
raise ValueError('merchant_id 必须为正整数')
if isinstance(settlement_date, datetime):
settlement_date = settlement_date.date()
if not isinstance(settlement_date, date):
raise ValueError('settlement_date 必须为 date 或 datetime 类型')
return settlement_date
def _get_plate_order_queryset(merchant_id: int, user=None):
"""
获取开版订单的基础查询集
Args:
merchant_id: 商户ID
user: 当前用户(用于客户可见性过滤)
Returns:
QuerySet[PlateOrder]: 过滤后的查询集
"""
queryset = PlateOrder.objects.filter(
merchant_id=merchant_id,
plate_type__isnull=False,
production_method__isnull=False
).select_related('customer')
if user and not user.is_superuser:
emp = getattr(user, 'employee', None)
if emp:
visible_customer_filter = (
Q(customer__created_by=emp) |
Q(customer__visible_employees=emp)
)
no_customer_filter = Q(customer__isnull=True)
queryset = queryset.filter(
visible_customer_filter | no_customer_filter
).distinct()
return queryset
def _get_month_date_range(settlement_date: date) -> tuple[date, date]:
"""
获取从月初到指定日期的日期范围
Args:
settlement_date: 统计日期
Returns:
tuple[date, date]: (月初日期, 统计日期)
"""
month_start = settlement_date.replace(day=1)
return month_start, settlement_date
def _aggregate_plate_orders_by_customer_and_type(queryset, settlement_date: date):
"""
按客户和类型分组聚合订单数据
Args:
queryset: 基础查询集
settlement_date: 统计日期
Returns:
QuerySet[PlateOrder]: 添加了聚合标注的查询集
"""
month_start, _ = _get_month_date_range(settlement_date)
queryset = queryset.annotate(
type=Concat(
'plate_type',
Value('-'),
'production_method',
output_field=CharField()
)
).values(
'customer__id',
'customer__name',
'type'
).annotate(
today=Sum(
Case(
When(
plate_date__date=settlement_date,
then=1
),
default=0,
output_field=IntegerField()
)
),
current_month=Sum(
Case(
When(
plate_date__date__gte=month_start,
plate_date__date__lte=settlement_date,
then=1
),
default=0,
output_field=IntegerField()
)
)
).order_by('customer__name', 'customer__id', 'type')
return queryset
def _format_plate_order_summary(aggregated_data):
"""
格式化聚合结果为 API 返回格式
Args:
aggregated_data: 聚合后的查询集
Returns:
list[dict]: 格式化后的数据
"""
result = {}
for item in aggregated_data:
client_id = item['customer__id']
client_name = item['customer__name']
client_key = (client_id, client_name)
if client_key not in result:
result[client_key] = {
'client_id': client_id,
'client_name': client_name,
'plate_order_count': []
}
plate_order_count = {
'type': item['type'],
'today': item['today'],
'current_month': item['current_month']
}
result[client_key]['plate_order_count'].append(plate_order_count)
for client_key in result:
result[client_key]['plate_order_count'] = _filter_zero_data(
result[client_key]['plate_order_count']
)
return [v for k, v in result.items() if v['plate_order_count']]
def _filter_zero_data(plate_order_counts):
"""
过滤全0数据
Args:
plate_order_counts: 订单统计列表
Returns:
list[dict]: 过滤后的列表
"""
return [
item for item in plate_order_counts
if item['today'] > 0 or item['current_month'] > 0
]
def calculate_plate_order_daily_summary(
merchant_id: int,
settlement_date: datetime.date