from django.db.models import Count, F, Sum from django.db.models.functions import TruncDate, TruncHour from django.utils import timezone from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView from apps.core.permissions import IsStoreOwner from apps.orders.models import Order, OrderItem from .serializers import SellerAnalyticsSerializer PERIOD_START = { 'today': lambda now: now.replace(hour=0, minute=0, second=0, microsecond=0), 'week': lambda now: now - timezone.timedelta(days=7), 'month': lambda now: now - timezone.timedelta(days=30), } class SellerAnalyticsView(APIView): """Seller sales dashboard (S17): daily chart, stat cards, top-selling products.""" schema_tags = ['Seller ยท Analytics'] permission_classes = [IsAuthenticated, IsStoreOwner] serializer_class = SellerAnalyticsSerializer def get(self, request): period = request.query_params.get('period', 'today') start_fn = PERIOD_START.get(period, PERIOD_START['today']) start = start_fn(timezone.now()) store = request.user.store period_orders = Order.objects.filter(store=store, created_at__gte=start) order_count = period_orders.count() cancelled_count = period_orders.filter(status=Order.Status.CANCELLED).count() cancellation_rate = round(cancelled_count / order_count * 100, 1) if order_count else 0 delivered_orders = period_orders.filter(status=Order.Status.DELIVERED) total_sales = sum(o.total_amount for o in delivered_orders) delivered_count = delivered_orders.count() average_order_value = round(total_sales / delivered_count) if delivered_count else 0 trunc = TruncHour if period == 'today' else TruncDate chart = list( delivered_orders .annotate(bucket=trunc('created_at')) .values('bucket') .annotate(sales=Sum(F('items_subtotal') + F('delivery_fee'))) .order_by('bucket') ) top_products = list( OrderItem.objects.filter(order__in=delivered_orders) .values('product_name_snapshot') .annotate( units_sold=Sum('quantity'), revenue=Sum(F('unit_price_snapshot') * F('quantity')), ) .order_by('-revenue')[:5] ) return Response({ 'period': period, 'order_count': order_count, 'total_sales': total_sales, 'cancellation_rate': cancellation_rate, 'average_order_value': average_order_value, 'chart': [{'bucket': point['bucket'], 'sales': point['sales']} for point in chart], 'top_products': [ { 'name': p['product_name_snapshot'], 'units_sold': p['units_sold'], 'revenue': p['revenue'], } for p in top_products ], })