django-cfg 1.3.3__py3-none-any.whl → 1.3.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/payments/admin_interface/old/payments/base.html +175 -0
  3. django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +125 -0
  4. django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +113 -0
  5. django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +35 -0
  6. django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +309 -0
  7. django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +303 -0
  8. django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +382 -0
  9. django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +518 -0
  10. django_cfg/apps/payments/{static → admin_interface/old/static}/payments/css/components.css +248 -9
  11. django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +163 -0
  12. django_cfg/apps/payments/admin_interface/serializers/__init__.py +39 -0
  13. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +149 -0
  14. django_cfg/apps/payments/admin_interface/serializers/webhook_serializers.py +114 -0
  15. django_cfg/apps/payments/admin_interface/templates/payments/base.html +55 -90
  16. django_cfg/apps/payments/admin_interface/templates/payments/components/dialog.html +81 -0
  17. django_cfg/apps/payments/admin_interface/templates/payments/components/ngrok_help_dialog.html +112 -0
  18. django_cfg/apps/payments/admin_interface/templates/payments/components/ngrok_status.html +175 -0
  19. django_cfg/apps/payments/admin_interface/templates/payments/components/status_card.html +21 -17
  20. django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +123 -250
  21. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +170 -269
  22. django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +152 -355
  23. django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +202 -551
  24. django_cfg/apps/payments/admin_interface/views/__init__.py +25 -14
  25. django_cfg/apps/payments/admin_interface/views/api/__init__.py +20 -0
  26. django_cfg/apps/payments/admin_interface/views/api/payments.py +191 -0
  27. django_cfg/apps/payments/admin_interface/views/api/stats.py +206 -0
  28. django_cfg/apps/payments/admin_interface/views/api/users.py +60 -0
  29. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +257 -0
  30. django_cfg/apps/payments/admin_interface/views/api/webhook_public.py +70 -0
  31. django_cfg/apps/payments/admin_interface/views/base.py +114 -0
  32. django_cfg/apps/payments/admin_interface/views/dashboard.py +60 -0
  33. django_cfg/apps/payments/admin_interface/views/forms.py +94 -0
  34. django_cfg/apps/payments/config/helpers.py +2 -2
  35. django_cfg/apps/payments/management/commands/cleanup_expired_data.py +16 -6
  36. django_cfg/apps/payments/management/commands/currency_stats.py +72 -5
  37. django_cfg/apps/payments/management/commands/manage_currencies.py +9 -20
  38. django_cfg/apps/payments/management/commands/manage_providers.py +5 -5
  39. django_cfg/apps/payments/middleware/api_access.py +35 -34
  40. django_cfg/apps/payments/migrations/0001_initial.py +1 -1
  41. django_cfg/apps/payments/models/managers/api_key_managers.py +4 -0
  42. django_cfg/apps/payments/models/managers/payment_managers.py +5 -0
  43. django_cfg/apps/payments/models/subscriptions.py +0 -24
  44. django_cfg/apps/payments/services/cache/__init__.py +1 -1
  45. django_cfg/apps/payments/services/core/balance_service.py +13 -2
  46. django_cfg/apps/payments/services/integrations/ngrok_service.py +3 -3
  47. django_cfg/apps/payments/services/providers/registry.py +20 -0
  48. django_cfg/apps/payments/signals/balance_signals.py +7 -4
  49. django_cfg/apps/payments/static/payments/js/api-client.js +385 -0
  50. django_cfg/apps/payments/static/payments/js/ngrok-status.js +58 -0
  51. django_cfg/apps/payments/static/payments/js/payment-dashboard.js +50 -0
  52. django_cfg/apps/payments/static/payments/js/payment-form.js +175 -0
  53. django_cfg/apps/payments/static/payments/js/payment-list.js +95 -0
  54. django_cfg/apps/payments/static/payments/js/webhook-dashboard.js +154 -0
  55. django_cfg/apps/payments/urls.py +4 -0
  56. django_cfg/apps/payments/urls_admin.py +37 -18
  57. django_cfg/apps/payments/views/api/api_keys.py +14 -0
  58. django_cfg/apps/payments/views/api/base.py +1 -0
  59. django_cfg/apps/payments/views/api/currencies.py +2 -2
  60. django_cfg/apps/payments/views/api/payments.py +11 -5
  61. django_cfg/apps/payments/views/api/subscriptions.py +36 -31
  62. django_cfg/apps/payments/views/overview/__init__.py +40 -0
  63. django_cfg/apps/payments/views/overview/serializers.py +205 -0
  64. django_cfg/apps/payments/views/overview/services.py +439 -0
  65. django_cfg/apps/payments/views/overview/urls.py +27 -0
  66. django_cfg/apps/payments/views/overview/views.py +231 -0
  67. django_cfg/apps/payments/views/serializers/api_keys.py +20 -6
  68. django_cfg/apps/payments/views/serializers/balances.py +5 -8
  69. django_cfg/apps/payments/views/serializers/currencies.py +2 -6
  70. django_cfg/apps/payments/views/serializers/payments.py +37 -32
  71. django_cfg/apps/payments/views/serializers/subscriptions.py +4 -26
  72. django_cfg/core/config.py +25 -15
  73. django_cfg/core/generation.py +12 -12
  74. django_cfg/core/integration/display/startup.py +1 -1
  75. django_cfg/core/validation.py +4 -4
  76. django_cfg/management/commands/show_config.py +2 -2
  77. django_cfg/management/commands/tree.py +1 -3
  78. django_cfg/middleware/__init__.py +2 -0
  79. django_cfg/middleware/static_nocache.py +55 -0
  80. django_cfg/models/payments.py +13 -15
  81. django_cfg/models/security.py +15 -0
  82. django_cfg/modules/django_ngrok.py +6 -0
  83. django_cfg/modules/django_unfold/dashboard.py +1 -3
  84. django_cfg/utils/smart_defaults.py +41 -1
  85. {django_cfg-1.3.3.dist-info → django_cfg-1.3.7.dist-info}/METADATA +1 -1
  86. {django_cfg-1.3.3.dist-info → django_cfg-1.3.7.dist-info}/RECORD +97 -64
  87. django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +0 -38
  88. django_cfg/apps/payments/admin_interface/views/payment_views.py +0 -259
  89. django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +0 -37
  90. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/loading_spinner.html +0 -0
  91. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/notification.html +0 -0
  92. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/provider_card.html +0 -0
  93. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/currency_converter.html +0 -0
  94. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/payment_status.html +0 -0
  95. /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/css/dashboard.css +0 -0
  96. /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/js/components.js +0 -0
  97. /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/js/utils.js +0 -0
  98. {django_cfg-1.3.3.dist-info → django_cfg-1.3.7.dist-info}/WHEEL +0 -0
  99. {django_cfg-1.3.3.dist-info → django_cfg-1.3.7.dist-info}/entry_points.txt +0 -0
  100. {django_cfg-1.3.3.dist-info → django_cfg-1.3.7.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,205 @@
1
+ """
2
+ 💰 Payments Overview Dashboard Serializers
3
+
4
+ DRF serializers for payments dashboard data with drf-spectacular integration.
5
+ """
6
+ from rest_framework import serializers
7
+ from drf_spectacular.utils import extend_schema_field
8
+ from typing import Dict, Any
9
+
10
+
11
+ class BalanceOverviewSerializer(serializers.Serializer):
12
+ """
13
+ User balance overview metrics
14
+ """
15
+ current_balance = serializers.FloatField(help_text="Current balance in USD")
16
+ balance_display = serializers.CharField(help_text="Formatted balance display")
17
+ total_deposited = serializers.FloatField(help_text="Total amount deposited (lifetime)")
18
+ total_spent = serializers.FloatField(help_text="Total amount spent (lifetime)")
19
+ last_transaction_at = serializers.DateTimeField(allow_null=True, help_text="Last transaction timestamp")
20
+ has_transactions = serializers.BooleanField(help_text="Whether user has any transactions")
21
+ is_empty = serializers.BooleanField(help_text="Whether balance is zero")
22
+
23
+
24
+ class SubscriptionOverviewSerializer(serializers.Serializer):
25
+ """
26
+ Current subscription overview
27
+ """
28
+ tier = serializers.CharField(help_text="Subscription tier")
29
+ tier_display = serializers.CharField(help_text="Human-readable tier name")
30
+ status = serializers.CharField(help_text="Subscription status")
31
+ status_display = serializers.CharField(help_text="Human-readable status")
32
+ status_color = serializers.CharField(help_text="Color for status display")
33
+ is_active = serializers.BooleanField(help_text="Whether subscription is active")
34
+ is_expired = serializers.BooleanField(help_text="Whether subscription is expired")
35
+ days_remaining = serializers.IntegerField(help_text="Days until expiration")
36
+
37
+ # Limits and usage
38
+ requests_per_hour = serializers.IntegerField(help_text="Hourly request limit")
39
+ requests_per_day = serializers.IntegerField(help_text="Daily request limit")
40
+ total_requests = serializers.IntegerField(help_text="Total requests made")
41
+ usage_percentage = serializers.FloatField(help_text="Usage percentage for current period")
42
+
43
+ # Billing
44
+ monthly_cost_usd = serializers.FloatField(help_text="Monthly cost in USD")
45
+ cost_display = serializers.CharField(help_text="Formatted cost display")
46
+
47
+ # Dates
48
+ starts_at = serializers.DateTimeField(help_text="Subscription start date")
49
+ expires_at = serializers.DateTimeField(help_text="Subscription expiration date")
50
+ last_request_at = serializers.DateTimeField(allow_null=True, help_text="Last API request timestamp")
51
+
52
+ # Access
53
+ endpoint_groups_count = serializers.IntegerField(help_text="Number of accessible endpoint groups")
54
+ endpoint_groups = serializers.ListField(
55
+ child=serializers.CharField(),
56
+ help_text="List of accessible endpoint group names"
57
+ )
58
+
59
+
60
+ class APIKeysOverviewSerializer(serializers.Serializer):
61
+ """
62
+ API keys overview metrics
63
+ """
64
+ total_keys = serializers.IntegerField(help_text="Total number of API keys")
65
+ active_keys = serializers.IntegerField(help_text="Number of active API keys")
66
+ expired_keys = serializers.IntegerField(help_text="Number of expired API keys")
67
+ total_requests = serializers.IntegerField(help_text="Total requests across all keys")
68
+ last_used_at = serializers.DateTimeField(allow_null=True, help_text="When any key was last used")
69
+
70
+ # Recent keys info
71
+ most_used_key_name = serializers.CharField(allow_null=True, help_text="Name of most used API key")
72
+ most_used_key_requests = serializers.IntegerField(help_text="Requests count for most used key")
73
+
74
+ # Expiring keys warning
75
+ expiring_soon_count = serializers.IntegerField(help_text="Number of keys expiring within 7 days")
76
+
77
+
78
+ class PaymentOverviewSerializer(serializers.Serializer):
79
+ """
80
+ Payments overview metrics
81
+ """
82
+ total_payments = serializers.IntegerField(help_text="Total number of payments")
83
+ completed_payments = serializers.IntegerField(help_text="Number of completed payments")
84
+ pending_payments = serializers.IntegerField(help_text="Number of pending payments")
85
+ failed_payments = serializers.IntegerField(help_text="Number of failed payments")
86
+
87
+ # Amounts
88
+ total_amount_usd = serializers.FloatField(help_text="Total payment amount in USD")
89
+ completed_amount_usd = serializers.FloatField(help_text="Total completed amount in USD")
90
+ average_payment_usd = serializers.FloatField(help_text="Average payment amount in USD")
91
+
92
+ # Success rate
93
+ success_rate = serializers.FloatField(help_text="Payment success rate percentage")
94
+
95
+ # Recent activity
96
+ last_payment_at = serializers.DateTimeField(allow_null=True, help_text="Last payment timestamp")
97
+ payments_this_month = serializers.IntegerField(help_text="Number of payments this month")
98
+ amount_this_month = serializers.FloatField(help_text="Total amount this month")
99
+
100
+ # Popular currencies
101
+ top_currency = serializers.CharField(allow_null=True, help_text="Most used currency")
102
+ top_currency_count = serializers.IntegerField(help_text="Usage count for top currency")
103
+
104
+
105
+ class PaymentsMetricsSerializer(serializers.Serializer):
106
+ """
107
+ Complete payments dashboard metrics
108
+ """
109
+ balance = BalanceOverviewSerializer(help_text="Balance overview")
110
+ subscription = SubscriptionOverviewSerializer(allow_null=True, help_text="Subscription overview")
111
+ api_keys = APIKeysOverviewSerializer(help_text="API keys overview")
112
+ payments = PaymentOverviewSerializer(help_text="Payments overview")
113
+
114
+
115
+ class RecentPaymentSerializer(serializers.Serializer):
116
+ """
117
+ Recent payment item
118
+ """
119
+ id = serializers.UUIDField(help_text="Payment ID")
120
+ internal_payment_id = serializers.CharField(help_text="Internal payment ID")
121
+ amount_usd = serializers.FloatField(help_text="Payment amount in USD")
122
+ amount_display = serializers.CharField(help_text="Formatted amount display")
123
+ currency_code = serializers.CharField(help_text="Currency code")
124
+ status = serializers.CharField(help_text="Payment status")
125
+ status_display = serializers.CharField(help_text="Human-readable status")
126
+ status_color = serializers.CharField(help_text="Color for status display")
127
+ provider = serializers.CharField(help_text="Payment provider")
128
+ created_at = serializers.DateTimeField(help_text="Payment creation timestamp")
129
+ completed_at = serializers.DateTimeField(allow_null=True, help_text="Payment completion timestamp")
130
+
131
+ # Status flags
132
+ is_pending = serializers.BooleanField(help_text="Whether payment is pending")
133
+ is_completed = serializers.BooleanField(help_text="Whether payment is completed")
134
+ is_failed = serializers.BooleanField(help_text="Whether payment failed")
135
+
136
+
137
+ class RecentTransactionSerializer(serializers.Serializer):
138
+ """
139
+ Recent transaction item
140
+ """
141
+ id = serializers.UUIDField(help_text="Transaction ID")
142
+ transaction_type = serializers.CharField(help_text="Transaction type")
143
+ amount_usd = serializers.FloatField(help_text="Transaction amount in USD")
144
+ amount_display = serializers.CharField(help_text="Formatted amount display")
145
+ balance_after = serializers.FloatField(help_text="Balance after transaction")
146
+ description = serializers.CharField(help_text="Transaction description")
147
+ created_at = serializers.DateTimeField(help_text="Transaction timestamp")
148
+ payment_id = serializers.CharField(allow_null=True, help_text="Related payment ID")
149
+
150
+ # Type info
151
+ is_credit = serializers.BooleanField(help_text="Whether this is a credit transaction")
152
+ is_debit = serializers.BooleanField(help_text="Whether this is a debit transaction")
153
+ type_color = serializers.CharField(help_text="Color for transaction type display")
154
+
155
+
156
+ class ChartDataPointSerializer(serializers.Serializer):
157
+ """
158
+ Chart data point for payments analytics
159
+ """
160
+ x = serializers.CharField(help_text="X-axis value (date)")
161
+ y = serializers.FloatField(help_text="Y-axis value (amount or count)")
162
+
163
+
164
+ class ChartSeriesSerializer(serializers.Serializer):
165
+ """
166
+ Chart series data for payments visualization
167
+ """
168
+ name = serializers.CharField(help_text="Series name")
169
+ data = ChartDataPointSerializer(many=True, help_text="Data points")
170
+ color = serializers.CharField(help_text="Series color")
171
+
172
+
173
+ class PaymentsChartResponseSerializer(serializers.Serializer):
174
+ """
175
+ Complete chart response for payments analytics
176
+ """
177
+ series = ChartSeriesSerializer(many=True, help_text="Chart series data")
178
+ period = serializers.CharField(help_text="Time period")
179
+ total_amount = serializers.FloatField(help_text="Total amount for period")
180
+ total_payments = serializers.IntegerField(help_text="Total payments for period")
181
+ success_rate = serializers.FloatField(help_text="Success rate for period")
182
+
183
+
184
+ class PaymentsDashboardOverviewSerializer(serializers.Serializer):
185
+ """
186
+ Complete payments dashboard overview response
187
+ """
188
+ metrics = PaymentsMetricsSerializer(help_text="Dashboard metrics")
189
+ recent_payments = RecentPaymentSerializer(many=True, help_text="Recent payments")
190
+ recent_transactions = RecentTransactionSerializer(many=True, help_text="Recent transactions")
191
+ chart_data = PaymentsChartResponseSerializer(help_text="Chart data for analytics")
192
+
193
+
194
+ class TimePeriodSerializer(serializers.Serializer):
195
+ """
196
+ Time period filter for charts and analytics
197
+ """
198
+ PERIOD_CHOICES = [
199
+ ('7d', 'Last 7 days'),
200
+ ('30d', 'Last 30 days'),
201
+ ('90d', 'Last 90 days'),
202
+ ('1y', 'Last year'),
203
+ ]
204
+
205
+ period = serializers.ChoiceField(choices=PERIOD_CHOICES, default='30d')
@@ -0,0 +1,439 @@
1
+ """
2
+ 💰 Payments Overview Dashboard Services
3
+
4
+ Services for aggregating payments dashboard data from existing models.
5
+ """
6
+ from django.db.models import Count, Sum, Avg, Q, Max
7
+ from django.utils import timezone
8
+ from datetime import timedelta, date
9
+ from decimal import Decimal
10
+ from typing import Dict, List, Any, Optional
11
+
12
+ from ...models import (
13
+ UniversalPayment,
14
+ UserBalance,
15
+ Transaction,
16
+ Subscription,
17
+ EndpointGroup,
18
+ APIKey,
19
+ )
20
+
21
+
22
+ class PaymentsDashboardMetricsService:
23
+ """
24
+ Service for calculating payments dashboard metrics
25
+ """
26
+
27
+ def __init__(self, user):
28
+ self.user = user
29
+ self.now = timezone.now()
30
+ self.today = self.now.date()
31
+ self.month_start = self.today.replace(day=1)
32
+ self.last_month_start = (self.month_start - timedelta(days=1)).replace(day=1)
33
+ self.last_month_end = self.month_start - timedelta(days=1)
34
+
35
+ def get_dashboard_metrics(self) -> Dict[str, Any]:
36
+ """
37
+ Get complete payments dashboard metrics
38
+ """
39
+ return {
40
+ 'balance': self.get_balance_overview(),
41
+ 'subscription': self.get_subscription_overview(),
42
+ 'api_keys': self.get_api_keys_overview(),
43
+ 'payments': self.get_payments_overview(),
44
+ }
45
+
46
+ def get_balance_overview(self) -> Dict[str, Any]:
47
+ """
48
+ Get user balance overview using manager methods
49
+ """
50
+ balance = UserBalance.objects.get_or_create_for_user(self.user)
51
+
52
+ return {
53
+ 'current_balance': balance.balance_usd,
54
+ 'balance_display': balance.balance_display,
55
+ 'total_deposited': balance.total_deposited,
56
+ 'total_spent': balance.total_spent,
57
+ 'last_transaction_at': balance.last_transaction_at,
58
+ 'has_transactions': balance.has_transactions,
59
+ 'is_empty': balance.is_empty,
60
+ }
61
+
62
+ def get_subscription_overview(self) -> Optional[Dict[str, Any]]:
63
+ """
64
+ Get current subscription overview using manager methods
65
+ """
66
+ subscription = Subscription.objects.get_active_for_user(self.user)
67
+
68
+ if not subscription:
69
+ return None
70
+
71
+ # Get endpoint groups info
72
+ endpoint_groups = subscription.endpoint_groups.filter(is_enabled=True)
73
+ endpoint_groups_names = list(endpoint_groups.values_list('name', flat=True))
74
+
75
+ return {
76
+ 'tier': subscription.tier,
77
+ 'tier_display': subscription.tier_display,
78
+ 'status': subscription.status,
79
+ 'status_display': subscription.get_status_display(),
80
+ 'status_color': subscription.status_color,
81
+ 'is_active': subscription.is_active,
82
+ 'is_expired': subscription.is_expired,
83
+ 'days_remaining': subscription.days_remaining,
84
+
85
+ # Limits and usage
86
+ 'requests_per_hour': subscription.requests_per_hour,
87
+ 'requests_per_day': subscription.requests_per_day,
88
+ 'total_requests': subscription.total_requests,
89
+ 'usage_percentage': subscription.usage_percentage,
90
+
91
+ # Billing
92
+ 'monthly_cost_usd': subscription.monthly_cost_usd,
93
+ 'cost_display': f"${subscription.monthly_cost_usd:.2f}/month",
94
+
95
+ # Dates
96
+ 'starts_at': subscription.starts_at,
97
+ 'expires_at': subscription.expires_at,
98
+ 'last_request_at': subscription.last_request_at,
99
+
100
+ # Access
101
+ 'endpoint_groups_count': endpoint_groups.count(),
102
+ 'endpoint_groups': endpoint_groups_names,
103
+ }
104
+
105
+ def get_api_keys_overview(self) -> Dict[str, Any]:
106
+ """
107
+ Get API keys overview using manager methods
108
+ """
109
+ api_keys = APIKey.objects.by_user(self.user)
110
+
111
+ total_keys = api_keys.count()
112
+ active_keys = api_keys.active().count()
113
+ expired_keys = api_keys.expired().count()
114
+
115
+ # Total requests across all keys
116
+ total_requests = api_keys.aggregate(
117
+ total=Sum('total_requests')
118
+ )['total'] or 0
119
+
120
+ # Last used timestamp
121
+ last_used_at = api_keys.aggregate(
122
+ last_used=Max('last_used_at')
123
+ )['last_used']
124
+
125
+ # Most used key
126
+ most_used_key = api_keys.order_by('-total_requests').first()
127
+ most_used_key_name = most_used_key.name if most_used_key else None
128
+ most_used_key_requests = most_used_key.total_requests if most_used_key else 0
129
+
130
+ # Keys expiring soon (within 7 days)
131
+ expiring_soon = api_keys.expiring_soon(7).count()
132
+
133
+ return {
134
+ 'total_keys': total_keys,
135
+ 'active_keys': active_keys,
136
+ 'expired_keys': expired_keys,
137
+ 'total_requests': total_requests,
138
+ 'last_used_at': last_used_at,
139
+ 'most_used_key_name': most_used_key_name,
140
+ 'most_used_key_requests': most_used_key_requests,
141
+ 'expiring_soon_count': expiring_soon,
142
+ }
143
+
144
+ def get_payments_overview(self) -> Dict[str, Any]:
145
+ """
146
+ Get payments overview using manager methods
147
+ """
148
+ payments = UniversalPayment.objects.by_user(self.user)
149
+
150
+ # Basic counts using manager methods
151
+ total_payments = payments.count()
152
+ completed_payments = payments.completed().count()
153
+ pending_payments = payments.pending().count()
154
+ failed_payments = payments.failed().count()
155
+
156
+ # Amount calculations
157
+ total_amount_usd = payments.total_amount()
158
+ completed_amount_usd = payments.completed().total_amount()
159
+ average_payment_usd = payments.average_amount()
160
+
161
+ # Success rate
162
+ success_rate = (completed_payments / total_payments * 100) if total_payments > 0 else 0.0
163
+
164
+ # Recent activity
165
+ last_payment = payments.order_by('-created_at').first()
166
+ last_payment_at = last_payment.created_at if last_payment else None
167
+
168
+ # This month stats using manager methods
169
+ payments_this_month = payments.this_month().count()
170
+ amount_this_month = payments.this_month().total_amount()
171
+
172
+ # Top currency
173
+ top_currency_data = payments.values('currency__code').annotate(
174
+ count=Count('id')
175
+ ).order_by('-count').first()
176
+
177
+ top_currency = top_currency_data['currency__code'] if top_currency_data else None
178
+ top_currency_count = top_currency_data['count'] if top_currency_data else 0
179
+
180
+ return {
181
+ 'total_payments': total_payments,
182
+ 'completed_payments': completed_payments,
183
+ 'pending_payments': pending_payments,
184
+ 'failed_payments': failed_payments,
185
+
186
+ 'total_amount_usd': total_amount_usd,
187
+ 'completed_amount_usd': completed_amount_usd,
188
+ 'average_payment_usd': average_payment_usd,
189
+
190
+ 'success_rate': round(success_rate, 2),
191
+
192
+ 'last_payment_at': last_payment_at,
193
+ 'payments_this_month': payments_this_month,
194
+ 'amount_this_month': amount_this_month,
195
+
196
+ 'top_currency': top_currency,
197
+ 'top_currency_count': top_currency_count,
198
+ }
199
+
200
+
201
+ class PaymentsUsageChartService:
202
+ """
203
+ Service for generating payments usage chart data
204
+ """
205
+
206
+ def __init__(self, user):
207
+ self.user = user
208
+
209
+ def get_chart_data(self, period: str = '30d') -> Dict[str, Any]:
210
+ """
211
+ Get chart data for payments analytics
212
+ """
213
+ days_map = {
214
+ '7d': 7,
215
+ '30d': 30,
216
+ '90d': 90,
217
+ '1y': 365
218
+ }
219
+
220
+ days = days_map.get(period, 30)
221
+ start_date = timezone.now().date() - timedelta(days=days)
222
+
223
+ # Get payments for the period using manager methods
224
+ payments = UniversalPayment.objects.by_user(self.user).filter(
225
+ created_at__date__gte=start_date
226
+ ).values('created_at__date').annotate(
227
+ total_amount=Sum('amount_usd'),
228
+ completed_amount=Sum(
229
+ 'actual_amount_usd',
230
+ filter=Q(status=UniversalPayment.PaymentStatus.COMPLETED)
231
+ ),
232
+ payment_count=Count('id'),
233
+ completed_count=Count(
234
+ 'id',
235
+ filter=Q(status=UniversalPayment.PaymentStatus.COMPLETED)
236
+ ),
237
+ failed_count=Count(
238
+ 'id',
239
+ filter=Q(status__in=[
240
+ UniversalPayment.PaymentStatus.FAILED,
241
+ UniversalPayment.PaymentStatus.EXPIRED,
242
+ UniversalPayment.PaymentStatus.CANCELLED
243
+ ])
244
+ )
245
+ ).order_by('created_at__date')
246
+
247
+ # Generate chart series data
248
+ amounts_data = []
249
+ completed_data = []
250
+ failed_data = []
251
+
252
+ # Create data points for each day
253
+ for i in range(days):
254
+ current_date = start_date + timedelta(days=i)
255
+ date_str = current_date.strftime('%Y-%m-%d')
256
+
257
+ # Find payments for this date
258
+ day_data = next(
259
+ (p for p in payments if p['created_at__date'] == current_date),
260
+ None
261
+ )
262
+
263
+ total_amount = day_data['total_amount'] if day_data else 0.0
264
+ completed_amount = day_data['completed_amount'] if day_data else 0.0
265
+ failed_count = day_data['failed_count'] if day_data else 0
266
+
267
+ amounts_data.append({'x': date_str, 'y': total_amount})
268
+ completed_data.append({'x': date_str, 'y': completed_amount})
269
+ failed_data.append({'x': date_str, 'y': failed_count})
270
+
271
+ # Calculate totals
272
+ total_amount = sum(p['y'] for p in amounts_data)
273
+ total_payments = sum(1 for p in amounts_data if p['y'] > 0)
274
+ completed_amount = sum(p['y'] for p in completed_data)
275
+ success_rate = (completed_amount / total_amount * 100) if total_amount > 0 else 0.0
276
+
277
+ return {
278
+ 'series': [
279
+ {
280
+ 'name': 'Total Amount',
281
+ 'data': amounts_data,
282
+ 'color': '#3B82F6'
283
+ },
284
+ {
285
+ 'name': 'Completed Amount',
286
+ 'data': completed_data,
287
+ 'color': '#10B981'
288
+ },
289
+ {
290
+ 'name': 'Failed Payments',
291
+ 'data': failed_data,
292
+ 'color': '#EF4444'
293
+ }
294
+ ],
295
+ 'period': period,
296
+ 'total_amount': total_amount,
297
+ 'total_payments': total_payments,
298
+ 'success_rate': round(success_rate, 2)
299
+ }
300
+
301
+
302
+ class RecentPaymentsService:
303
+ """
304
+ Service for getting recent payments and transactions
305
+ """
306
+
307
+ def __init__(self, user):
308
+ self.user = user
309
+
310
+ def get_recent_payments(self, limit: int = 10) -> List[Dict[str, Any]]:
311
+ """
312
+ Get recent payments for the user using manager methods
313
+ """
314
+ payments = UniversalPayment.objects.by_user(self.user).optimized().order_by('-created_at')[:limit]
315
+
316
+ result = []
317
+ for payment in payments:
318
+ result.append({
319
+ 'id': payment.id,
320
+ 'internal_payment_id': payment.internal_payment_id,
321
+ 'amount_usd': payment.amount_usd,
322
+ 'amount_display': payment.amount_display,
323
+ 'currency_code': payment.currency.code,
324
+ 'status': payment.status,
325
+ 'status_display': payment.get_status_display(),
326
+ 'status_color': payment.status_color,
327
+ 'provider': payment.provider,
328
+ 'created_at': payment.created_at,
329
+ 'completed_at': payment.completed_at,
330
+
331
+ # Status flags
332
+ 'is_pending': payment.is_pending,
333
+ 'is_completed': payment.is_completed,
334
+ 'is_failed': payment.is_failed,
335
+ })
336
+
337
+ return result
338
+
339
+ def get_recent_transactions(self, limit: int = 10) -> List[Dict[str, Any]]:
340
+ """
341
+ Get recent transactions for the user using manager methods
342
+ """
343
+ transactions = Transaction.objects.by_user(self.user).optimized().order_by('-created_at')[:limit]
344
+
345
+ result = []
346
+ for transaction in transactions:
347
+ result.append({
348
+ 'id': transaction.id,
349
+ 'transaction_type': transaction.transaction_type,
350
+ 'amount_usd': transaction.amount_usd,
351
+ 'amount_display': transaction.amount_display,
352
+ 'balance_after': transaction.balance_after,
353
+ 'description': transaction.description,
354
+ 'created_at': transaction.created_at,
355
+ 'payment_id': transaction.payment_id,
356
+
357
+ # Type info
358
+ 'is_credit': transaction.is_credit,
359
+ 'is_debit': transaction.is_debit,
360
+ 'type_color': transaction.type_color,
361
+ })
362
+
363
+ return result
364
+
365
+
366
+ class PaymentsAnalyticsService:
367
+ """
368
+ Service for payments analytics and insights
369
+ """
370
+
371
+ def __init__(self, user):
372
+ self.user = user
373
+
374
+ def get_payment_analytics(self, limit: int = 10) -> List[Dict[str, Any]]:
375
+ """
376
+ Get payment analytics by currency, provider, etc.
377
+ """
378
+ # Analytics by currency using manager methods
379
+ currency_stats = UniversalPayment.objects.by_user(self.user).values(
380
+ 'currency__code', 'currency__name'
381
+ ).annotate(
382
+ total_payments=Count('id'),
383
+ total_amount=Sum('amount_usd'),
384
+ completed_payments=Count(
385
+ 'id',
386
+ filter=Q(status=UniversalPayment.PaymentStatus.COMPLETED)
387
+ ),
388
+ avg_amount=Avg('amount_usd'),
389
+ success_rate=Count(
390
+ 'id',
391
+ filter=Q(status=UniversalPayment.PaymentStatus.COMPLETED)
392
+ ) * 100.0 / Count('id')
393
+ ).order_by('-total_amount')[:limit]
394
+
395
+ result = []
396
+ for stat in currency_stats:
397
+ result.append({
398
+ 'currency_code': stat['currency__code'],
399
+ 'currency_name': stat['currency__name'],
400
+ 'total_payments': stat['total_payments'],
401
+ 'total_amount': stat['total_amount'] or 0.0,
402
+ 'completed_payments': stat['completed_payments'],
403
+ 'average_amount': stat['avg_amount'] or 0.0,
404
+ 'success_rate': round(stat['success_rate'] or 0.0, 2),
405
+ })
406
+
407
+ return result
408
+
409
+ def get_provider_analytics(self) -> List[Dict[str, Any]]:
410
+ """
411
+ Get analytics by payment provider
412
+ """
413
+ provider_stats = UniversalPayment.objects.by_user(self.user).values('provider').annotate(
414
+ total_payments=Count('id'),
415
+ total_amount=Sum('amount_usd'),
416
+ completed_payments=Count(
417
+ 'id',
418
+ filter=Q(status=UniversalPayment.PaymentStatus.COMPLETED)
419
+ ),
420
+ success_rate=Count(
421
+ 'id',
422
+ filter=Q(status=UniversalPayment.PaymentStatus.COMPLETED)
423
+ ) * 100.0 / Count('id')
424
+ ).order_by('-total_payments')
425
+
426
+ result = []
427
+ for stat in provider_stats:
428
+ result.append({
429
+ 'provider': stat['provider'],
430
+ 'provider_display': dict(UniversalPayment.PaymentProvider.choices).get(
431
+ stat['provider'], stat['provider']
432
+ ),
433
+ 'total_payments': stat['total_payments'],
434
+ 'total_amount': stat['total_amount'] or 0.0,
435
+ 'completed_payments': stat['completed_payments'],
436
+ 'success_rate': round(stat['success_rate'] or 0.0, 2),
437
+ })
438
+
439
+ return result
@@ -0,0 +1,27 @@
1
+ """
2
+ 💰 Payments Overview Dashboard URLs
3
+
4
+ Nested router configuration for payments dashboard API endpoints.
5
+ """
6
+ from rest_framework.routers import DefaultRouter
7
+ from .views import PaymentsDashboardViewSet
8
+
9
+ # Create router
10
+ router = DefaultRouter()
11
+
12
+ # Register payments dashboard viewset
13
+ router.register(r'dashboard', PaymentsDashboardViewSet, basename='payments-dashboard')
14
+
15
+ # URL patterns
16
+ urlpatterns = router.urls
17
+
18
+ # Available endpoints:
19
+ # GET /api/payments/overview/dashboard/overview/ - Complete payments dashboard overview
20
+ # GET /api/payments/overview/dashboard/metrics/ - Payments dashboard metrics only
21
+ # GET /api/payments/overview/dashboard/chart_data/?period=30d - Payments chart data
22
+ # GET /api/payments/overview/dashboard/recent_payments/?limit=10 - Recent payments
23
+ # GET /api/payments/overview/dashboard/recent_transactions/?limit=10 - Recent transactions
24
+ # GET /api/payments/overview/dashboard/payment_analytics/?limit=10 - Payment analytics
25
+ # GET /api/payments/overview/dashboard/balance_overview/ - Balance overview only
26
+ # GET /api/payments/overview/dashboard/subscription_overview/ - Subscription overview only
27
+ # GET /api/payments/overview/dashboard/api_keys_overview/ - API keys overview only