Add comprehensive analytics and VAT rate management
Introduced a full-featured analytics module for e-commerce business intelligence, including sales, product, customer, shipping, and review analytics, with API endpoints for dashboard and custom reports. Added VAT rate management: new VATRate model, admin interface, serializer, and API endpoints, and integrated VAT logic into Product and pricing calculations. Refactored configuration and admin code to support VAT rates, improved email notification tasks, and updated related serializers, views, and URLs for unified configuration and VAT management.
This commit is contained in:
@@ -3,9 +3,16 @@ from rest_framework.permissions import AllowAny
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
import base64
|
||||
from datetime import datetime
|
||||
from django.utils.dateparse import parse_datetime
|
||||
|
||||
from .models import Refund, Review
|
||||
from .serializers import RefundCreatePublicSerializer, ReviewSerializerPublic
|
||||
from .analytics import (
|
||||
SalesAnalytics, ProductAnalytics, CustomerAnalytics,
|
||||
ShippingAnalytics, ReviewAnalytics, AnalyticsAggregator,
|
||||
get_predefined_date_ranges
|
||||
)
|
||||
|
||||
|
||||
from django.db import transaction
|
||||
@@ -854,66 +861,237 @@ class AdminWishlistViewSet(viewsets.ModelViewSet):
|
||||
ordering = ["-updated_at"]
|
||||
|
||||
|
||||
# ---------- ANALYTICS PLACEHOLDER ----------
|
||||
# ---------- ANALYTICS SYSTEM ----------
|
||||
|
||||
# TODO: Implement comprehensive analytics system
|
||||
# Ideas:
|
||||
# - Sales analytics (daily/monthly/yearly revenue)
|
||||
# - Product performance (most sold, most viewed, most wishlisted)
|
||||
# - User behavior analytics (cart abandonment, conversion rates)
|
||||
# - Inventory reports (low stock alerts, bestsellers)
|
||||
# - Regional sales data
|
||||
# - Seasonal trends analysis
|
||||
# - Customer lifetime value
|
||||
# - Refund/return analytics
|
||||
|
||||
@extend_schema_view(
|
||||
list=extend_schema(tags=["commerce", "analytics"], summary="Analytics dashboard (admin)"),
|
||||
)
|
||||
class AnalyticsViewSet(viewsets.GenericViewSet):
|
||||
class AnalyticsView(APIView):
|
||||
"""
|
||||
Analytics and reporting endpoints for business intelligence
|
||||
TODO: Implement comprehensive analytics
|
||||
Comprehensive analytics and reporting API for business intelligence.
|
||||
Supports configurable date ranges and various analytics modules.
|
||||
|
||||
GET /analytics/ - Complete dashboard overview
|
||||
GET /analytics/?type=sales - Sales analytics
|
||||
GET /analytics/?type=products - Product analytics
|
||||
GET /analytics/?type=customers - Customer analytics
|
||||
GET /analytics/?type=shipping - Shipping analytics
|
||||
GET /analytics/?type=reviews - Review analytics
|
||||
GET /analytics/?type=inventory - Inventory analytics
|
||||
GET /analytics/?type=ranges - Available date ranges
|
||||
POST /analytics/ - Generate custom analytics reports
|
||||
"""
|
||||
permission_classes = [permissions.IsAdminUser]
|
||||
|
||||
@action(detail=False, methods=['get'], url_path='sales-overview')
|
||||
@extend_schema(
|
||||
tags=["commerce", "analytics"],
|
||||
summary="Sales overview (TODO)",
|
||||
responses={200: {"type": "object", "properties": {"message": {"type": "string"}}}},
|
||||
)
|
||||
def sales_overview(self, request):
|
||||
"""TODO: Implement sales analytics"""
|
||||
return Response({"message": "Sales analytics coming soon!"})
|
||||
def _parse_date_params(self, request):
|
||||
"""Parse date parameters from request"""
|
||||
start_date = request.query_params.get('start_date')
|
||||
end_date = request.query_params.get('end_date')
|
||||
period = request.query_params.get('period', 'last_30_days')
|
||||
|
||||
if start_date:
|
||||
start_date = parse_datetime(start_date)
|
||||
if end_date:
|
||||
end_date = parse_datetime(end_date)
|
||||
|
||||
# If no dates provided, use predefined period
|
||||
if not start_date or not end_date:
|
||||
predefined_ranges = get_predefined_date_ranges()
|
||||
if period in predefined_ranges:
|
||||
date_range = predefined_ranges[period]
|
||||
start_date = start_date or date_range['start']
|
||||
end_date = end_date or date_range['end']
|
||||
|
||||
return start_date, end_date, period
|
||||
|
||||
@action(detail=False, methods=['get'], url_path='product-performance')
|
||||
@extend_schema(
|
||||
tags=["commerce", "analytics"],
|
||||
summary="Product performance analytics (TODO)",
|
||||
responses={200: {"type": "object", "properties": {"message": {"type": "string"}}}},
|
||||
summary="Get analytics data",
|
||||
description="Get various analytics based on type parameter",
|
||||
parameters=[
|
||||
{"name": "type", "in": "query", "schema": {"type": "string", "enum": ["dashboard", "sales", "products", "customers", "shipping", "reviews", "inventory", "ranges"]}, "description": "Type of analytics to retrieve"},
|
||||
{"name": "start_date", "in": "query", "schema": {"type": "string", "format": "date-time"}},
|
||||
{"name": "end_date", "in": "query", "schema": {"type": "string", "format": "date-time"}},
|
||||
{"name": "period", "in": "query", "schema": {"type": "string", "enum": ["today", "yesterday", "last_7_days", "last_30_days", "last_90_days", "this_month", "last_month", "this_year", "last_year"]}},
|
||||
{"name": "limit", "in": "query", "schema": {"type": "integer", "minimum": 1, "maximum": 100}, "description": "Limit for product analytics"}
|
||||
]
|
||||
)
|
||||
def product_performance(self, request):
|
||||
"""TODO: Implement product performance analytics"""
|
||||
return Response({"message": "Product analytics coming soon!"})
|
||||
def get(self, request):
|
||||
"""Get analytics data based on type parameter"""
|
||||
analytics_type = request.query_params.get('type', 'dashboard')
|
||||
start_date, end_date, period = self._parse_date_params(request)
|
||||
|
||||
if analytics_type == 'dashboard':
|
||||
return self._get_dashboard_analytics(start_date, end_date, period)
|
||||
elif analytics_type == 'sales':
|
||||
return self._get_sales_analytics(start_date, end_date, period)
|
||||
elif analytics_type == 'products':
|
||||
return self._get_product_analytics(request, start_date, end_date)
|
||||
elif analytics_type == 'customers':
|
||||
return self._get_customer_analytics(start_date, end_date)
|
||||
elif analytics_type == 'shipping':
|
||||
return self._get_shipping_analytics(start_date, end_date)
|
||||
elif analytics_type == 'reviews':
|
||||
return self._get_review_analytics(start_date, end_date)
|
||||
elif analytics_type == 'inventory':
|
||||
return self._get_inventory_analytics()
|
||||
elif analytics_type == 'ranges':
|
||||
return self._get_date_ranges()
|
||||
else:
|
||||
return Response({'error': 'Invalid analytics type'}, status=400)
|
||||
|
||||
def _get_dashboard_analytics(self, start_date, end_date, period):
|
||||
"""Get complete analytics dashboard overview"""
|
||||
dashboard_data = AnalyticsAggregator.dashboard_overview(start_date, end_date)
|
||||
dashboard_data['query_params'] = {
|
||||
'start_date': start_date.isoformat() if start_date else None,
|
||||
'end_date': end_date.isoformat() if end_date else None,
|
||||
'period': period
|
||||
}
|
||||
return Response(dashboard_data)
|
||||
|
||||
def _get_sales_analytics(self, start_date, end_date, period):
|
||||
"""Get detailed sales and revenue analytics"""
|
||||
sales_data = SalesAnalytics.revenue_overview(start_date, end_date, period)
|
||||
payment_data = SalesAnalytics.payment_methods_breakdown(start_date, end_date)
|
||||
|
||||
# Calculate percentages for payment methods
|
||||
total_revenue = sum(item['revenue'] for item in payment_data)
|
||||
for item in payment_data:
|
||||
item['percentage'] = round(
|
||||
(item['revenue'] / total_revenue * 100) if total_revenue > 0 else 0, 2
|
||||
)
|
||||
|
||||
return Response({
|
||||
'sales': sales_data,
|
||||
'payment_methods': payment_data
|
||||
})
|
||||
|
||||
def _get_product_analytics(self, request, start_date, end_date):
|
||||
"""Get product performance analytics"""
|
||||
limit = int(request.query_params.get('limit', 20))
|
||||
|
||||
top_products = ProductAnalytics.top_selling_products(start_date, end_date, limit)
|
||||
category_performance = ProductAnalytics.category_performance(start_date, end_date)
|
||||
|
||||
return Response({
|
||||
'top_products': top_products,
|
||||
'category_performance': category_performance
|
||||
})
|
||||
|
||||
def _get_customer_analytics(self, start_date, end_date):
|
||||
"""Get customer behavior analytics"""
|
||||
customer_overview = CustomerAnalytics.customer_overview(start_date, end_date)
|
||||
cart_analysis = CustomerAnalytics.cart_abandonment_analysis()
|
||||
|
||||
return Response({
|
||||
'customer_overview': customer_overview,
|
||||
'cart_abandonment': cart_analysis
|
||||
})
|
||||
|
||||
def _get_shipping_analytics(self, start_date, end_date):
|
||||
"""Get shipping methods and Deutsche Post analytics"""
|
||||
shipping_methods = ShippingAnalytics.shipping_methods_breakdown(start_date, end_date)
|
||||
deutsche_post_data = ShippingAnalytics.deutsche_post_analytics()
|
||||
|
||||
return Response({
|
||||
'shipping_methods': shipping_methods,
|
||||
'deutsche_post': deutsche_post_data
|
||||
})
|
||||
|
||||
def _get_review_analytics(self, start_date, end_date):
|
||||
"""Get review and rating analytics"""
|
||||
review_data = ReviewAnalytics.review_overview(start_date, end_date)
|
||||
return Response(review_data)
|
||||
|
||||
def _get_inventory_analytics(self):
|
||||
"""Get comprehensive inventory analytics"""
|
||||
inventory_data = ProductAnalytics.inventory_analysis()
|
||||
return Response(inventory_data)
|
||||
|
||||
def _get_date_ranges(self):
|
||||
"""Get available predefined date ranges"""
|
||||
ranges = get_predefined_date_ranges()
|
||||
return Response({
|
||||
'available_ranges': list(ranges.keys()),
|
||||
'ranges': {
|
||||
key: {
|
||||
'start': value['start'].isoformat(),
|
||||
'end': value['end'].isoformat(),
|
||||
'display_name': key.replace('_', ' ').title()
|
||||
}
|
||||
for key, value in ranges.items()
|
||||
}
|
||||
})
|
||||
|
||||
@action(detail=False, methods=['get'], url_path='inventory-report')
|
||||
@extend_schema(
|
||||
tags=["commerce", "analytics"],
|
||||
summary="Inventory status report (TODO)",
|
||||
responses={200: {"type": "object", "properties": {"message": {"type": "string"}}}},
|
||||
summary="Generate custom analytics report",
|
||||
description="Generate custom analytics based on specified modules and parameters",
|
||||
request={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"modules": {"type": "array", "items": {"type": "string", "enum": ["sales", "products", "customers", "shipping", "reviews"]}},
|
||||
"start_date": {"type": "string", "format": "date-time"},
|
||||
"end_date": {"type": "string", "format": "date-time"},
|
||||
"period": {"type": "string", "enum": ["daily", "weekly", "monthly"]},
|
||||
"options": {"type": "object"}
|
||||
}
|
||||
}
|
||||
)
|
||||
def inventory_report(self, request):
|
||||
"""TODO: Implement inventory management features"""
|
||||
# TODO Ideas:
|
||||
# - Low stock alerts (products with stock < threshold)
|
||||
# - Out of stock products list
|
||||
# - Bestsellers vs slow movers
|
||||
# - Stock value calculation
|
||||
# - Automated reorder suggestions
|
||||
# - Stock movement history
|
||||
# - Bulk stock updates
|
||||
# - Supplier management integration
|
||||
return Response({"message": "Inventory analytics coming soon!"})
|
||||
def post(self, request):
|
||||
"""Generate custom analytics report based on specified modules"""
|
||||
modules = request.data.get('modules', ['sales', 'products'])
|
||||
start_date = request.data.get('start_date')
|
||||
end_date = request.data.get('end_date')
|
||||
period = request.data.get('period', 'daily')
|
||||
options = request.data.get('options', {})
|
||||
|
||||
if start_date:
|
||||
start_date = parse_datetime(start_date)
|
||||
if end_date:
|
||||
end_date = parse_datetime(end_date)
|
||||
|
||||
# If no dates provided, default to last 30 days
|
||||
if not start_date or not end_date:
|
||||
predefined_ranges = get_predefined_date_ranges()
|
||||
date_range = predefined_ranges.get('last_30_days')
|
||||
start_date = start_date or date_range['start']
|
||||
end_date = end_date or date_range['end']
|
||||
|
||||
report_data = {}
|
||||
|
||||
if 'sales' in modules:
|
||||
report_data['sales'] = {
|
||||
'revenue': SalesAnalytics.revenue_overview(start_date, end_date, period),
|
||||
'payment_methods': SalesAnalytics.payment_methods_breakdown(start_date, end_date)
|
||||
}
|
||||
|
||||
if 'products' in modules:
|
||||
limit = options.get('product_limit', 20)
|
||||
report_data['products'] = {
|
||||
'top_selling': ProductAnalytics.top_selling_products(start_date, end_date, limit),
|
||||
'categories': ProductAnalytics.category_performance(start_date, end_date)
|
||||
}
|
||||
|
||||
if 'customers' in modules:
|
||||
report_data['customers'] = CustomerAnalytics.customer_overview(start_date, end_date)
|
||||
|
||||
if 'shipping' in modules:
|
||||
report_data['shipping'] = {
|
||||
'methods': ShippingAnalytics.shipping_methods_breakdown(start_date, end_date),
|
||||
'deutsche_post': ShippingAnalytics.deutsche_post_analytics()
|
||||
}
|
||||
|
||||
if 'reviews' in modules:
|
||||
report_data['reviews'] = ReviewAnalytics.review_overview(start_date, end_date)
|
||||
|
||||
return Response({
|
||||
'report': report_data,
|
||||
'parameters': {
|
||||
'modules': modules,
|
||||
'start_date': start_date.isoformat(),
|
||||
'end_date': end_date.isoformat(),
|
||||
'period': period,
|
||||
'generated_at': datetime.now().isoformat()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user