django-cfg 1.3.1__py3-none-any.whl → 1.3.5__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 (115) 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 +429 -0
  36. django_cfg/apps/payments/management/commands/currency_stats.py +443 -0
  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/management/commands/process_pending_payments.py +357 -0
  40. django_cfg/apps/payments/management/commands/test_providers.py +434 -0
  41. django_cfg/apps/payments/middleware/api_access.py +35 -34
  42. django_cfg/apps/payments/migrations/0001_initial.py +1 -1
  43. django_cfg/apps/payments/models/balance.py +5 -2
  44. django_cfg/apps/payments/models/managers/api_key_managers.py +6 -2
  45. django_cfg/apps/payments/models/managers/balance_managers.py +3 -3
  46. django_cfg/apps/payments/models/managers/payment_managers.py +5 -0
  47. django_cfg/apps/payments/models/managers/subscription_managers.py +3 -3
  48. django_cfg/apps/payments/models/subscriptions.py +0 -24
  49. django_cfg/apps/payments/services/cache/__init__.py +1 -1
  50. django_cfg/apps/payments/services/cache_service/__init__.py +143 -0
  51. django_cfg/apps/payments/services/cache_service/api_key_cache.py +37 -0
  52. django_cfg/apps/payments/services/cache_service/interfaces.py +32 -0
  53. django_cfg/apps/payments/services/cache_service/keys.py +49 -0
  54. django_cfg/apps/payments/services/cache_service/rate_limit_cache.py +47 -0
  55. django_cfg/apps/payments/services/cache_service/simple_cache.py +101 -0
  56. django_cfg/apps/payments/services/core/balance_service.py +13 -2
  57. django_cfg/apps/payments/services/core/payment_service.py +49 -22
  58. django_cfg/apps/payments/services/integrations/ngrok_service.py +3 -3
  59. django_cfg/apps/payments/services/providers/registry.py +20 -0
  60. django_cfg/apps/payments/signals/api_key_signals.py +2 -2
  61. django_cfg/apps/payments/signals/balance_signals.py +8 -5
  62. django_cfg/apps/payments/static/payments/js/api-client.js +385 -0
  63. django_cfg/apps/payments/static/payments/js/ngrok-status.js +58 -0
  64. django_cfg/apps/payments/static/payments/js/payment-dashboard.js +50 -0
  65. django_cfg/apps/payments/static/payments/js/payment-form.js +175 -0
  66. django_cfg/apps/payments/static/payments/js/payment-list.js +95 -0
  67. django_cfg/apps/payments/static/payments/js/webhook-dashboard.js +154 -0
  68. django_cfg/apps/payments/urls.py +4 -0
  69. django_cfg/apps/payments/urls_admin.py +37 -18
  70. django_cfg/apps/payments/views/api/api_keys.py +14 -0
  71. django_cfg/apps/payments/views/api/base.py +1 -0
  72. django_cfg/apps/payments/views/api/currencies.py +2 -2
  73. django_cfg/apps/payments/views/api/payments.py +11 -5
  74. django_cfg/apps/payments/views/api/subscriptions.py +36 -31
  75. django_cfg/apps/payments/views/overview/__init__.py +40 -0
  76. django_cfg/apps/payments/views/overview/serializers.py +205 -0
  77. django_cfg/apps/payments/views/overview/services.py +439 -0
  78. django_cfg/apps/payments/views/overview/urls.py +27 -0
  79. django_cfg/apps/payments/views/overview/views.py +231 -0
  80. django_cfg/apps/payments/views/serializers/api_keys.py +20 -6
  81. django_cfg/apps/payments/views/serializers/balances.py +5 -8
  82. django_cfg/apps/payments/views/serializers/currencies.py +2 -6
  83. django_cfg/apps/payments/views/serializers/payments.py +37 -32
  84. django_cfg/apps/payments/views/serializers/subscriptions.py +4 -26
  85. django_cfg/apps/urls.py +2 -1
  86. django_cfg/core/config.py +25 -15
  87. django_cfg/core/generation.py +12 -12
  88. django_cfg/core/integration/display/startup.py +1 -1
  89. django_cfg/core/validation.py +4 -4
  90. django_cfg/management/commands/show_config.py +2 -2
  91. django_cfg/management/commands/tree.py +1 -3
  92. django_cfg/middleware/__init__.py +2 -0
  93. django_cfg/middleware/static_nocache.py +55 -0
  94. django_cfg/models/payments.py +13 -15
  95. django_cfg/models/security.py +15 -0
  96. django_cfg/modules/django_ngrok.py +6 -0
  97. django_cfg/modules/django_unfold/dashboard.py +1 -3
  98. django_cfg/utils/smart_defaults.py +51 -5
  99. {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/METADATA +1 -1
  100. {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/RECORD +111 -69
  101. django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +0 -38
  102. django_cfg/apps/payments/admin_interface/views/payment_views.py +0 -259
  103. django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +0 -37
  104. django_cfg/apps/payments/services/cache/cache_service.py +0 -235
  105. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/loading_spinner.html +0 -0
  106. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/notification.html +0 -0
  107. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/provider_card.html +0 -0
  108. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/currency_converter.html +0 -0
  109. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/payment_status.html +0 -0
  110. /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/css/dashboard.css +0 -0
  111. /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/js/components.js +0 -0
  112. /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/js/utils.js +0 -0
  113. {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/WHEEL +0 -0
  114. {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/entry_points.txt +0 -0
  115. {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/licenses/LICENSE +0 -0
@@ -40,9 +40,9 @@ class SubscriptionViewSet(PaymentBaseViewSet):
40
40
 
41
41
  queryset = Subscription.objects.all()
42
42
  serializer_class = SubscriptionSerializer
43
- permission_classes = [permissions.IsAdminUser] # Admin only for global access
44
- filterset_fields = ['status', 'tier', 'tariff', 'endpoint_group', 'user']
45
- search_fields = ['user__username', 'user__email', 'tariff__name']
43
+ permission_classes = [permissions.IsAuthenticated] # Allow authenticated users
44
+ filterset_fields = ['status', 'tier', 'user']
45
+ search_fields = ['user__username', 'user__email']
46
46
  ordering_fields = ['created_at', 'updated_at', 'expires_at', 'total_requests']
47
47
 
48
48
  serializer_classes = {
@@ -55,11 +55,17 @@ class SubscriptionViewSet(PaymentBaseViewSet):
55
55
 
56
56
  def get_queryset(self):
57
57
  """Optimized queryset with related objects."""
58
- return super().get_queryset().select_related(
59
- 'user', 'tariff', 'endpoint_group'
58
+ queryset = super().get_queryset().select_related(
59
+ 'user'
60
60
  ).prefetch_related(
61
- 'tariff__endpoint_groups'
61
+ 'endpoint_groups'
62
62
  )
63
+
64
+ # Non-staff users can only see their own subscriptions
65
+ if not self.request.user.is_staff:
66
+ queryset = queryset.filter(user=self.request.user)
67
+
68
+ return queryset
63
69
 
64
70
  @action(detail=True, methods=['post'])
65
71
  def update_status(self, request, pk=None):
@@ -161,43 +167,42 @@ class SubscriptionViewSet(PaymentBaseViewSet):
161
167
  )
162
168
 
163
169
  @action(detail=False, methods=['get'])
164
- def by_tariff(self, request):
170
+ def by_tier(self, request):
165
171
  """
166
- Get subscriptions grouped by tariff.
172
+ Get subscriptions grouped by tier.
167
173
 
168
- GET /api/subscriptions/by_tariff/
174
+ GET /api/subscriptions/by_tier/
169
175
  """
170
176
  try:
171
177
  queryset = self.filter_queryset(self.get_queryset())
172
178
 
173
- tariff_stats = {}
174
- for subscription in queryset.select_related('tariff'):
175
- tariff_name = subscription.tariff.name if subscription.tariff else 'No Tariff'
176
- tariff_id = subscription.tariff.id if subscription.tariff else None
179
+ tier_stats = {}
180
+ for tier_choice in Subscription.SubscriptionTier.choices:
181
+ tier_code = tier_choice[0]
182
+ tier_name = tier_choice[1]
177
183
 
178
- if tariff_name not in tariff_stats:
179
- tariff_stats[tariff_name] = {
180
- 'tariff_id': tariff_id,
181
- 'tariff_name': tariff_name,
182
- 'total_subscriptions': 0,
183
- 'active_subscriptions': 0,
184
- 'total_requests': 0,
185
- }
184
+ tier_subscriptions = queryset.filter(tier=tier_code)
186
185
 
187
- tariff_stats[tariff_name]['total_subscriptions'] += 1
188
- if subscription.is_active():
189
- tariff_stats[tariff_name]['active_subscriptions'] += 1
190
- tariff_stats[tariff_name]['total_requests'] += subscription.total_requests or 0
186
+ tier_stats[tier_code] = {
187
+ 'name': tier_name,
188
+ 'total_subscriptions': tier_subscriptions.count(),
189
+ 'active_subscriptions': tier_subscriptions.filter(
190
+ status=Subscription.SubscriptionStatus.ACTIVE
191
+ ).count(),
192
+ 'total_requests': tier_subscriptions.aggregate(
193
+ total=models.Sum('total_requests')
194
+ )['total'] or 0,
195
+ }
191
196
 
192
197
  return Response({
193
- 'tariff_stats': tariff_stats,
198
+ 'tier_stats': tier_stats,
194
199
  'generated_at': timezone.now().isoformat()
195
200
  })
196
201
 
197
202
  except Exception as e:
198
- logger.error(f"Subscription tariff stats failed: {e}")
203
+ logger.error(f"Subscription tier stats failed: {e}")
199
204
  return Response(
200
- {'error': f'Tariff stats failed: {e}'},
205
+ {'error': f'Tier stats failed: {e}'},
201
206
  status=status.HTTP_500_INTERNAL_SERVER_ERROR
202
207
  )
203
208
 
@@ -212,8 +217,8 @@ class UserSubscriptionViewSet(NestedPaymentViewSet):
212
217
  queryset = Subscription.objects.all()
213
218
  serializer_class = SubscriptionSerializer
214
219
  permission_classes = [permissions.IsAuthenticated]
215
- filterset_fields = ['status', 'tier', 'tariff', 'endpoint_group']
216
- search_fields = ['tariff__name']
220
+ filterset_fields = ['status', 'tier']
221
+ search_fields = []
217
222
  ordering_fields = ['created_at', 'updated_at', 'expires_at']
218
223
 
219
224
  # Nested ViewSet configuration
@@ -236,7 +241,7 @@ class UserSubscriptionViewSet(NestedPaymentViewSet):
236
241
  if str(self.request.user.id) != str(user_id):
237
242
  return queryset.none()
238
243
 
239
- return queryset.select_related('tariff', 'endpoint_group')
244
+ return queryset.select_related('user').prefetch_related('endpoint_groups')
240
245
 
241
246
  @action(detail=True, methods=['post'])
242
247
  def update_status(self, request, user_pk=None, pk=None):
@@ -0,0 +1,40 @@
1
+ """
2
+ 💰 Payments Overview Dashboard
3
+
4
+ Overview dashboard for user payment metrics and analytics.
5
+ """
6
+
7
+ from .views import PaymentsDashboardViewSet
8
+ from .serializers import (
9
+ PaymentsDashboardOverviewSerializer,
10
+ PaymentsMetricsSerializer,
11
+ BalanceOverviewSerializer,
12
+ SubscriptionOverviewSerializer,
13
+ APIKeysOverviewSerializer,
14
+ PaymentsChartResponseSerializer,
15
+ )
16
+ from .services import (
17
+ PaymentsDashboardMetricsService,
18
+ PaymentsUsageChartService,
19
+ RecentPaymentsService,
20
+ PaymentsAnalyticsService,
21
+ )
22
+
23
+ __all__ = [
24
+ # Views
25
+ 'PaymentsDashboardViewSet',
26
+
27
+ # Serializers
28
+ 'PaymentsDashboardOverviewSerializer',
29
+ 'PaymentsMetricsSerializer',
30
+ 'BalanceOverviewSerializer',
31
+ 'SubscriptionOverviewSerializer',
32
+ 'APIKeysOverviewSerializer',
33
+ 'PaymentsChartResponseSerializer',
34
+
35
+ # Services
36
+ 'PaymentsDashboardMetricsService',
37
+ 'PaymentsUsageChartService',
38
+ 'RecentPaymentsService',
39
+ 'PaymentsAnalyticsService',
40
+ ]
@@ -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')