django-cfg 1.3.7__py3-none-any.whl → 1.3.11__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 (246) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/admin/__init__.py +24 -8
  3. django_cfg/apps/accounts/admin/activity_admin.py +146 -0
  4. django_cfg/apps/accounts/admin/filters.py +98 -22
  5. django_cfg/apps/accounts/admin/group_admin.py +86 -0
  6. django_cfg/apps/accounts/admin/inlines.py +42 -13
  7. django_cfg/apps/accounts/admin/otp_admin.py +115 -0
  8. django_cfg/apps/accounts/admin/registration_admin.py +173 -0
  9. django_cfg/apps/accounts/admin/resources.py +123 -19
  10. django_cfg/apps/accounts/admin/twilio_admin.py +327 -0
  11. django_cfg/apps/accounts/admin/user_admin.py +362 -0
  12. django_cfg/apps/agents/admin/__init__.py +17 -4
  13. django_cfg/apps/agents/admin/execution_admin.py +204 -183
  14. django_cfg/apps/agents/admin/registry_admin.py +230 -255
  15. django_cfg/apps/agents/admin/toolsets_admin.py +274 -321
  16. django_cfg/apps/agents/core/__init__.py +1 -1
  17. django_cfg/apps/agents/core/django_agent.py +221 -0
  18. django_cfg/apps/agents/core/exceptions.py +14 -0
  19. django_cfg/apps/agents/core/orchestrator.py +18 -3
  20. django_cfg/apps/knowbase/admin/__init__.py +1 -1
  21. django_cfg/apps/knowbase/admin/archive_admin.py +352 -640
  22. django_cfg/apps/knowbase/admin/chat_admin.py +258 -192
  23. django_cfg/apps/knowbase/admin/document_admin.py +269 -262
  24. django_cfg/apps/knowbase/admin/external_data_admin.py +271 -489
  25. django_cfg/apps/knowbase/config/settings.py +21 -4
  26. django_cfg/apps/knowbase/views/chat_views.py +3 -0
  27. django_cfg/apps/leads/admin/__init__.py +3 -1
  28. django_cfg/apps/leads/admin/leads_admin.py +235 -35
  29. django_cfg/apps/maintenance/admin/__init__.py +2 -2
  30. django_cfg/apps/maintenance/admin/api_key_admin.py +125 -63
  31. django_cfg/apps/maintenance/admin/log_admin.py +143 -61
  32. django_cfg/apps/maintenance/admin/scheduled_admin.py +212 -301
  33. django_cfg/apps/maintenance/admin/site_admin.py +213 -352
  34. django_cfg/apps/newsletter/admin/__init__.py +29 -2
  35. django_cfg/apps/newsletter/admin/newsletter_admin.py +531 -193
  36. django_cfg/apps/payments/admin/__init__.py +18 -27
  37. django_cfg/apps/payments/admin/api_keys_admin.py +179 -546
  38. django_cfg/apps/payments/admin/balance_admin.py +166 -632
  39. django_cfg/apps/payments/admin/currencies_admin.py +235 -607
  40. django_cfg/apps/payments/admin/endpoint_groups_admin.py +127 -0
  41. django_cfg/apps/payments/admin/filters.py +83 -3
  42. django_cfg/apps/payments/admin/networks_admin.py +269 -0
  43. django_cfg/apps/payments/admin/payments_admin.py +183 -460
  44. django_cfg/apps/payments/admin/subscriptions_admin.py +119 -636
  45. django_cfg/apps/payments/admin/tariffs_admin.py +248 -0
  46. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +153 -34
  47. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
  48. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
  49. django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
  50. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
  51. django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
  52. django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
  53. django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
  54. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +43 -17
  55. django_cfg/apps/payments/admin_interface/views/__init__.py +2 -0
  56. django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
  57. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +109 -63
  58. django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
  59. django_cfg/apps/payments/config/__init__.py +14 -15
  60. django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
  61. django_cfg/apps/payments/config/helpers.py +8 -13
  62. django_cfg/apps/payments/management/commands/manage_currencies.py +236 -274
  63. django_cfg/apps/payments/management/commands/manage_providers.py +4 -1
  64. django_cfg/apps/payments/middleware/api_access.py +32 -6
  65. django_cfg/apps/payments/migrations/0001_initial.py +33 -46
  66. django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
  67. django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
  68. django_cfg/apps/payments/models/balance.py +12 -0
  69. django_cfg/apps/payments/models/currencies.py +106 -32
  70. django_cfg/apps/payments/models/managers/currency_managers.py +65 -0
  71. django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
  72. django_cfg/apps/payments/models/payments.py +94 -0
  73. django_cfg/apps/payments/services/core/base.py +4 -4
  74. django_cfg/apps/payments/services/core/currency_service.py +35 -28
  75. django_cfg/apps/payments/services/core/payment_service.py +266 -39
  76. django_cfg/apps/payments/services/providers/__init__.py +3 -0
  77. django_cfg/apps/payments/services/providers/base.py +303 -41
  78. django_cfg/apps/payments/services/providers/models/__init__.py +42 -0
  79. django_cfg/apps/payments/services/providers/models/base.py +145 -0
  80. django_cfg/apps/payments/services/providers/models/providers.py +87 -0
  81. django_cfg/apps/payments/services/providers/models/universal.py +48 -0
  82. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +31 -0
  83. django_cfg/apps/payments/services/providers/nowpayments/config.py +70 -0
  84. django_cfg/apps/payments/services/providers/nowpayments/models.py +150 -0
  85. django_cfg/apps/payments/services/providers/nowpayments/parsers.py +879 -0
  86. django_cfg/apps/payments/services/providers/nowpayments/provider.py +557 -0
  87. django_cfg/apps/payments/services/providers/nowpayments/sync.py +196 -0
  88. django_cfg/apps/payments/services/providers/registry.py +9 -37
  89. django_cfg/apps/payments/services/providers/sync_service.py +277 -0
  90. django_cfg/apps/payments/services/types/requests.py +19 -7
  91. django_cfg/apps/payments/signals/payment_signals.py +31 -2
  92. django_cfg/apps/payments/static/payments/js/api-client.js +29 -6
  93. django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
  94. django_cfg/apps/payments/static/payments/js/payment-form.js +98 -32
  95. django_cfg/apps/payments/tasks/__init__.py +39 -0
  96. django_cfg/apps/payments/tasks/types.py +73 -0
  97. django_cfg/apps/payments/tasks/usage_tracking.py +308 -0
  98. django_cfg/apps/payments/templates/admin/payments/_components/dashboard_header.html +23 -0
  99. django_cfg/apps/payments/templates/admin/payments/_components/stats_card.html +25 -0
  100. django_cfg/apps/payments/templates/admin/payments/_components/stats_grid.html +16 -0
  101. django_cfg/apps/payments/templates/admin/payments/apikey/change_list.html +39 -0
  102. django_cfg/apps/payments/templates/admin/payments/balance/change_list.html +50 -0
  103. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +40 -0
  104. django_cfg/apps/payments/templates/admin/payments/payment/change_list.html +48 -0
  105. django_cfg/apps/payments/templates/admin/payments/subscription/change_list.html +48 -0
  106. django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
  107. django_cfg/apps/payments/urls.py +3 -2
  108. django_cfg/apps/payments/urls_admin.py +1 -1
  109. django_cfg/apps/payments/views/api/currencies.py +8 -5
  110. django_cfg/apps/payments/views/overview/services.py +2 -2
  111. django_cfg/apps/payments/views/serializers/currencies.py +22 -8
  112. django_cfg/apps/support/admin/__init__.py +10 -1
  113. django_cfg/apps/support/admin/support_admin.py +338 -141
  114. django_cfg/apps/tasks/admin/__init__.py +11 -0
  115. django_cfg/apps/tasks/admin/tasks_admin.py +430 -0
  116. django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
  117. django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
  118. django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
  119. django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
  120. django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
  121. django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
  122. django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
  123. django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
  124. django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
  125. django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
  126. django_cfg/apps/tasks/tasks/__init__.py +10 -0
  127. django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
  128. django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
  129. django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
  130. django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
  131. django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
  132. django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
  133. django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
  134. django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
  135. django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
  136. django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
  137. django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
  138. django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
  139. django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
  140. django_cfg/apps/tasks/urls.py +2 -2
  141. django_cfg/apps/tasks/urls_admin.py +2 -2
  142. django_cfg/apps/tasks/utils/__init__.py +1 -0
  143. django_cfg/apps/tasks/utils/simulator.py +356 -0
  144. django_cfg/apps/tasks/views/__init__.py +16 -0
  145. django_cfg/apps/tasks/views/api.py +569 -0
  146. django_cfg/apps/tasks/views/dashboard.py +58 -0
  147. django_cfg/config.py +1 -1
  148. django_cfg/core/config.py +10 -5
  149. django_cfg/core/generation.py +1 -1
  150. django_cfg/core/integration/__init__.py +21 -0
  151. django_cfg/management/commands/__init__.py +13 -1
  152. django_cfg/management/commands/migrate_all.py +9 -3
  153. django_cfg/management/commands/migrator.py +11 -6
  154. django_cfg/management/commands/rundramatiq.py +3 -2
  155. django_cfg/management/commands/rundramatiq_simulator.py +430 -0
  156. django_cfg/middleware/__init__.py +0 -2
  157. django_cfg/models/api_keys.py +115 -0
  158. django_cfg/models/constance.py +0 -11
  159. django_cfg/models/payments.py +137 -3
  160. django_cfg/modules/django_admin/__init__.py +64 -0
  161. django_cfg/modules/django_admin/decorators/__init__.py +13 -0
  162. django_cfg/modules/django_admin/decorators/actions.py +106 -0
  163. django_cfg/modules/django_admin/decorators/display.py +106 -0
  164. django_cfg/modules/django_admin/mixins/__init__.py +14 -0
  165. django_cfg/modules/django_admin/mixins/display_mixin.py +81 -0
  166. django_cfg/modules/django_admin/mixins/optimization_mixin.py +41 -0
  167. django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +202 -0
  168. django_cfg/modules/django_admin/models/__init__.py +20 -0
  169. django_cfg/modules/django_admin/models/action_models.py +33 -0
  170. django_cfg/modules/django_admin/models/badge_models.py +20 -0
  171. django_cfg/modules/django_admin/models/base.py +26 -0
  172. django_cfg/modules/django_admin/models/display_models.py +31 -0
  173. django_cfg/modules/django_admin/utils/badges.py +159 -0
  174. django_cfg/modules/django_admin/utils/displays.py +247 -0
  175. django_cfg/modules/django_currency/__init__.py +2 -2
  176. django_cfg/modules/django_currency/clients/__init__.py +2 -2
  177. django_cfg/modules/django_currency/clients/hybrid_client.py +587 -0
  178. django_cfg/modules/django_currency/core/converter.py +12 -12
  179. django_cfg/modules/django_currency/database/__init__.py +2 -2
  180. django_cfg/modules/django_currency/database/database_loader.py +93 -42
  181. django_cfg/modules/django_llm/llm/client.py +10 -2
  182. django_cfg/modules/django_tasks.py +54 -21
  183. django_cfg/modules/django_unfold/callbacks/actions.py +1 -1
  184. django_cfg/modules/django_unfold/callbacks/statistics.py +1 -1
  185. django_cfg/modules/django_unfold/dashboard.py +14 -13
  186. django_cfg/modules/django_unfold/models/config.py +1 -1
  187. django_cfg/registry/core.py +7 -9
  188. django_cfg/registry/third_party.py +2 -2
  189. django_cfg/template_archive/django_sample.zip +0 -0
  190. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/METADATA +2 -1
  191. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/RECORD +198 -160
  192. django_cfg/apps/accounts/admin/activity.py +0 -96
  193. django_cfg/apps/accounts/admin/group.py +0 -17
  194. django_cfg/apps/accounts/admin/otp.py +0 -59
  195. django_cfg/apps/accounts/admin/registration_source.py +0 -97
  196. django_cfg/apps/accounts/admin/twilio_response.py +0 -227
  197. django_cfg/apps/accounts/admin/user.py +0 -300
  198. django_cfg/apps/agents/core/agent.py +0 -281
  199. django_cfg/apps/payments/admin_interface/old/payments/base.html +0 -175
  200. django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +0 -125
  201. django_cfg/apps/payments/admin_interface/old/payments/components/loading_spinner.html +0 -16
  202. django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +0 -113
  203. django_cfg/apps/payments/admin_interface/old/payments/components/notification.html +0 -27
  204. django_cfg/apps/payments/admin_interface/old/payments/components/provider_card.html +0 -86
  205. django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +0 -35
  206. django_cfg/apps/payments/admin_interface/old/payments/currency_converter.html +0 -382
  207. django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +0 -309
  208. django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +0 -303
  209. django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +0 -382
  210. django_cfg/apps/payments/admin_interface/old/payments/payment_status.html +0 -500
  211. django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +0 -518
  212. django_cfg/apps/payments/admin_interface/old/static/payments/css/components.css +0 -619
  213. django_cfg/apps/payments/admin_interface/old/static/payments/css/dashboard.css +0 -188
  214. django_cfg/apps/payments/admin_interface/old/static/payments/js/components.js +0 -545
  215. django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +0 -163
  216. django_cfg/apps/payments/admin_interface/old/static/payments/js/utils.js +0 -412
  217. django_cfg/apps/payments/config/constance/__init__.py +0 -22
  218. django_cfg/apps/payments/config/constance/config_service.py +0 -123
  219. django_cfg/apps/payments/config/constance/fields.py +0 -69
  220. django_cfg/apps/payments/config/constance/settings.py +0 -160
  221. django_cfg/apps/payments/services/providers/nowpayments.py +0 -478
  222. django_cfg/apps/tasks/admin.py +0 -320
  223. django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
  224. django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
  225. django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
  226. django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
  227. django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
  228. django_cfg/apps/tasks/templates/tasks/base.html +0 -96
  229. django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
  230. django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
  231. django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
  232. django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
  233. django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
  234. django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
  235. django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
  236. django_cfg/apps/tasks/views.py +0 -461
  237. django_cfg/management/commands/auto_generate.py +0 -486
  238. django_cfg/middleware/static_nocache.py +0 -55
  239. django_cfg/modules/django_currency/clients/yahoo_client.py +0 -157
  240. /django_cfg/modules/{django_unfold → django_admin}/icons/README.md +0 -0
  241. /django_cfg/modules/{django_unfold → django_admin}/icons/__init__.py +0 -0
  242. /django_cfg/modules/{django_unfold → django_admin}/icons/constants.py +0 -0
  243. /django_cfg/modules/{django_unfold → django_admin}/icons/generate_icons.py +0 -0
  244. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/WHEEL +0 -0
  245. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/entry_points.txt +0 -0
  246. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,248 @@
1
+ """
2
+ Tariffs Admin interface using Django Admin Utilities.
3
+
4
+ Clean tariff management with pricing and limits display.
5
+ """
6
+
7
+ from django.contrib import admin
8
+ from django.db.models import Count, Q
9
+ from django.utils import timezone
10
+ from django.utils.html import format_html
11
+ from datetime import timedelta
12
+
13
+ from unfold.admin import ModelAdmin
14
+
15
+ from django_cfg.modules.django_admin import (
16
+ OptimizedModelAdmin,
17
+ DisplayMixin,
18
+ MoneyDisplayConfig,
19
+ DateTimeDisplayConfig,
20
+ StatusBadgeConfig,
21
+ Icons,
22
+ ActionVariant,
23
+ display,
24
+ action
25
+ )
26
+ from django_cfg.modules.django_admin.utils.badges import StatusBadge
27
+
28
+ from ..models.tariffs import Tariff, TariffEndpointGroup
29
+
30
+
31
+ @admin.register(Tariff)
32
+ class TariffAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin):
33
+ """
34
+ Admin interface for Tariff model.
35
+
36
+ Features:
37
+ - Clean pricing display
38
+ - Rate limits visualization
39
+ - Feature flags display
40
+ - Automatic query optimization
41
+ - Type-safe configuration
42
+ """
43
+
44
+ # Performance optimization
45
+ select_related_fields = []
46
+ annotations = {}
47
+
48
+ # List configuration
49
+ list_display = [
50
+ 'name_display',
51
+ 'code_display',
52
+ 'pricing_display',
53
+ 'rate_limits_display',
54
+ 'features_display',
55
+ 'status_display',
56
+ 'created_at_display'
57
+ ]
58
+
59
+ list_filter = [
60
+ 'is_active',
61
+ 'is_public',
62
+ 'supports_webhooks',
63
+ 'priority_support',
64
+ 'created_at'
65
+ ]
66
+
67
+ search_fields = ['name', 'code', 'description']
68
+
69
+ readonly_fields = [
70
+ 'created_at',
71
+ 'updated_at',
72
+ 'yearly_discount_percentage'
73
+ ]
74
+
75
+ # Display methods using Unfold features
76
+ @display(description="Name", ordering="name")
77
+ def name_display(self, obj):
78
+ """Tariff name display."""
79
+ return obj.name
80
+
81
+ @display(description="Code", ordering="code")
82
+ def code_display(self, obj):
83
+ """Tariff code display."""
84
+ return obj.code
85
+
86
+ @display(description="Pricing", ordering="monthly_price_usd")
87
+ def pricing_display(self, obj):
88
+ """Pricing display using utilities."""
89
+ monthly = self.display_money_amount(
90
+ obj,
91
+ 'monthly_price_usd',
92
+ MoneyDisplayConfig(currency="USD", show_sign=False)
93
+ )
94
+
95
+ if obj.yearly_price_usd:
96
+ yearly = self.display_money_amount(
97
+ obj,
98
+ 'yearly_price_usd',
99
+ MoneyDisplayConfig(currency="USD", show_sign=False)
100
+ )
101
+ discount = f"{obj.yearly_discount_percentage:.0f}% off"
102
+ return format_html("{}/mo • {}/yr ({})", monthly, yearly, discount)
103
+
104
+ return format_html("{}/mo", monthly)
105
+
106
+ @display(description="Rate Limits")
107
+ def rate_limits_display(self, obj):
108
+ """Rate limits display."""
109
+ return f"{obj.requests_per_hour:,}/hr • {obj.requests_per_day:,}/day • {obj.requests_per_month:,}/mo"
110
+
111
+ @display(description="Features")
112
+ def features_display(self, obj):
113
+ """Features display."""
114
+ features = []
115
+
116
+ features.append(f"{obj.max_api_keys} API keys")
117
+
118
+ if obj.supports_webhooks:
119
+ features.append("Webhooks")
120
+
121
+ if obj.priority_support:
122
+ features.append("Priority Support")
123
+
124
+ return " • ".join(features)
125
+
126
+ @display(description="Status", label={
127
+ "Active": "success",
128
+ "Private": "warning",
129
+ "Inactive": "danger"
130
+ })
131
+ def status_display(self, obj):
132
+ """Status display using Unfold label feature."""
133
+ if obj.is_active and obj.is_public:
134
+ return "Active"
135
+ elif obj.is_active:
136
+ return "Private"
137
+ else:
138
+ return "Inactive"
139
+
140
+ @display(description="Created")
141
+ def created_at_display(self, obj):
142
+ """Created at display."""
143
+ config = DateTimeDisplayConfig(
144
+ show_relative=True,
145
+ show_seconds=False
146
+ )
147
+ return self.display_datetime_relative(obj, 'created_at', config)
148
+
149
+
150
+ @admin.register(TariffEndpointGroup)
151
+ class TariffEndpointGroupAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin):
152
+ """
153
+ Admin interface for TariffEndpointGroup model.
154
+
155
+ Features:
156
+ - Tariff and endpoint group relationships
157
+ - Custom rate limit display
158
+ - Clean utilities integration
159
+ """
160
+
161
+ # Performance optimization
162
+ select_related_fields = ['tariff', 'endpoint_group']
163
+ annotations = {}
164
+
165
+ # List configuration
166
+ list_display = [
167
+ 'tariff_display',
168
+ 'endpoint_group_display',
169
+ 'rate_limit_display',
170
+ 'status_display',
171
+ 'created_at_display'
172
+ ]
173
+
174
+ list_filter = [
175
+ 'is_enabled',
176
+ 'tariff__is_active',
177
+ 'created_at'
178
+ ]
179
+
180
+ search_fields = [
181
+ 'tariff__name',
182
+ 'endpoint_group__name'
183
+ ]
184
+
185
+ readonly_fields = [
186
+ 'created_at',
187
+ 'updated_at',
188
+ 'effective_rate_limit'
189
+ ]
190
+
191
+ # Display methods
192
+ @display(description="Tariff")
193
+ def tariff_display(self, obj):
194
+ """Tariff display."""
195
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.PRICE_CHANGE)
196
+ return StatusBadge.create(
197
+ text=obj.tariff.name,
198
+ variant="primary",
199
+ config=config
200
+ )
201
+
202
+ @display(description="Endpoint Group")
203
+ def endpoint_group_display(self, obj):
204
+ """Endpoint group display."""
205
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.GROUP)
206
+ return StatusBadge.create(
207
+ text=obj.endpoint_group.name,
208
+ variant="info",
209
+ config=config
210
+ )
211
+
212
+ @display(description="Rate Limit")
213
+ def rate_limit_display(self, obj):
214
+ """Rate limit display."""
215
+ effective = obj.effective_rate_limit
216
+
217
+ if obj.custom_rate_limit:
218
+ return f"{effective:,}/hr (custom)"
219
+ else:
220
+ return f"{effective:,}/hr (default)"
221
+
222
+ @display(description="Status")
223
+ def status_display(self, obj):
224
+ """Status display."""
225
+ if obj.is_enabled:
226
+ status = "Enabled"
227
+ variant = "success"
228
+ icon = Icons.CHECK_CIRCLE
229
+ else:
230
+ status = "Disabled"
231
+ variant = "danger"
232
+ icon = Icons.CANCEL
233
+
234
+ config = StatusBadgeConfig(show_icons=True, icon=icon)
235
+ return StatusBadge.create(
236
+ text=status,
237
+ variant=variant,
238
+ config=config
239
+ )
240
+
241
+ @display(description="Created")
242
+ def created_at_display(self, obj):
243
+ """Created at display."""
244
+ config = DateTimeDisplayConfig(
245
+ show_relative=True,
246
+ show_seconds=False
247
+ )
248
+ return self.display_datetime_relative(obj, 'created_at', config)
@@ -6,6 +6,8 @@ DRF serializers for payment management in admin dashboard.
6
6
 
7
7
  from rest_framework import serializers
8
8
  from django.contrib.auth import get_user_model
9
+ from django.contrib.humanize.templatetags.humanize import naturaltime
10
+
9
11
  from ...models import UniversalPayment
10
12
 
11
13
  User = get_user_model()
@@ -21,65 +23,118 @@ class AdminUserSerializer(serializers.ModelSerializer):
21
23
  read_only_fields = fields
22
24
 
23
25
 
24
- class AdminPaymentListSerializer(serializers.ModelSerializer):
26
+ class AdminPaymentListSerializer(serializers.Serializer):
25
27
  """
26
28
  Serializer for payment list in admin interface.
29
+ Uses UniversalPayment only for data extraction.
27
30
  """
31
+ id = serializers.UUIDField(read_only=True)
28
32
  user = AdminUserSerializer(read_only=True)
29
- status_display = serializers.CharField(source='get_status_display', read_only=True)
30
- provider_display = serializers.CharField(source='get_provider_display', read_only=True)
33
+ amount_usd = serializers.FloatField(read_only=True)
34
+ currency_code = serializers.SerializerMethodField()
35
+ currency_name = serializers.SerializerMethodField()
36
+ provider = serializers.CharField(read_only=True)
37
+ provider_display = serializers.SerializerMethodField()
38
+ status = serializers.CharField(read_only=True)
39
+ status_display = serializers.SerializerMethodField()
40
+ pay_amount = serializers.DecimalField(max_digits=20, decimal_places=8, read_only=True)
41
+ pay_address = serializers.CharField(read_only=True)
42
+ transaction_hash = serializers.CharField(read_only=True)
43
+ created_at = serializers.DateTimeField(read_only=True)
44
+ updated_at = serializers.DateTimeField(read_only=True)
45
+ description = serializers.CharField(read_only=True)
31
46
  age = serializers.SerializerMethodField()
32
47
 
33
- class Meta:
34
- model = UniversalPayment
35
- fields = [
36
- 'id', 'user', 'amount_usd', 'currency_code', 'provider', 'provider_display',
37
- 'status', 'status_display', 'pay_amount', 'pay_address', 'transaction_hash',
38
- 'created_at', 'updated_at', 'age', 'description'
39
- ]
40
- read_only_fields = fields
48
+ def get_currency_code(self, obj):
49
+ """Get currency code from related Currency model."""
50
+ return obj.currency.code if obj.currency else None
51
+
52
+ def get_currency_name(self, obj):
53
+ """Get currency name from related Currency model."""
54
+ return obj.currency.name if obj.currency else None
55
+
56
+ def get_provider_display(self, obj):
57
+ """Get human-readable provider name."""
58
+ return obj.get_provider_display()
59
+
60
+ def get_status_display(self, obj):
61
+ """Get human-readable status."""
62
+ return obj.get_status_display()
41
63
 
42
64
  def get_age(self, obj):
43
65
  """Get human-readable age of payment."""
44
- return obj.age_display
66
+ from django.contrib.humanize.templatetags.humanize import naturaltime
67
+ return naturaltime(obj.created_at)
45
68
 
46
69
 
47
- class AdminPaymentDetailSerializer(serializers.ModelSerializer):
70
+ class AdminPaymentDetailSerializer(serializers.Serializer):
48
71
  """
49
72
  Detailed serializer for individual payment in admin interface.
73
+ Uses UniversalPayment only for data extraction.
50
74
  """
75
+ id = serializers.UUIDField(read_only=True)
51
76
  user = AdminUserSerializer(read_only=True)
52
- status_display = serializers.CharField(source='get_status_display', read_only=True)
53
- provider_display = serializers.CharField(source='get_provider_display', read_only=True)
77
+ internal_payment_id = serializers.CharField(read_only=True)
78
+ amount_usd = serializers.FloatField(read_only=True)
79
+ actual_amount_usd = serializers.FloatField(read_only=True)
80
+ fee_amount_usd = serializers.FloatField(read_only=True)
81
+ currency_code = serializers.SerializerMethodField()
82
+ currency_name = serializers.SerializerMethodField()
83
+ provider = serializers.CharField(read_only=True)
84
+ provider_display = serializers.SerializerMethodField()
85
+ status = serializers.CharField(read_only=True)
86
+ status_display = serializers.SerializerMethodField()
87
+ pay_amount = serializers.DecimalField(max_digits=20, decimal_places=8, read_only=True)
88
+ pay_address = serializers.CharField(read_only=True)
89
+ payment_url = serializers.URLField(read_only=True)
90
+ transaction_hash = serializers.CharField(read_only=True)
91
+ confirmations_count = serializers.IntegerField(read_only=True)
92
+ security_nonce = serializers.CharField(read_only=True)
93
+ expires_at = serializers.DateTimeField(read_only=True)
94
+ completed_at = serializers.DateTimeField(read_only=True)
95
+ status_changed_at = serializers.DateTimeField(read_only=True)
96
+ description = serializers.CharField(read_only=True)
97
+ callback_url = serializers.URLField(read_only=True)
98
+ cancel_url = serializers.URLField(read_only=True)
99
+ provider_data = serializers.JSONField(read_only=True)
100
+ webhook_data = serializers.JSONField(read_only=True)
101
+ created_at = serializers.DateTimeField(read_only=True)
102
+ updated_at = serializers.DateTimeField(read_only=True)
54
103
  age = serializers.SerializerMethodField()
55
104
 
56
- class Meta:
57
- model = UniversalPayment
58
- fields = [
59
- 'id', 'user', 'internal_payment_id', 'amount_usd', 'actual_amount_usd',
60
- 'fee_amount_usd', 'currency_code', 'provider', 'provider_display',
61
- 'status', 'status_display', 'pay_amount', 'pay_address', 'payment_url',
62
- 'transaction_hash', 'confirmations_count', 'security_nonce',
63
- 'expires_at', 'completed_at', 'description', 'callback_url', 'cancel_url',
64
- 'provider_data', 'webhook_data', 'created_at', 'updated_at', 'age'
65
- ]
66
- read_only_fields = fields
105
+ def get_currency_code(self, obj):
106
+ """Get currency code from related Currency model."""
107
+ return obj.currency.code if obj.currency else None
108
+
109
+ def get_currency_name(self, obj):
110
+ """Get currency name from related Currency model."""
111
+ return obj.currency.name if obj.currency else None
112
+
113
+ def get_provider_display(self, obj):
114
+ """Get human-readable provider name."""
115
+ return obj.get_provider_display()
116
+
117
+ def get_status_display(self, obj):
118
+ """Get human-readable status."""
119
+ return obj.get_status_display()
67
120
 
68
121
  def get_age(self, obj):
69
122
  """Get human-readable age of payment."""
70
- return obj.age_display
123
+ return naturaltime(obj.created_at)
71
124
 
72
125
 
73
- class AdminPaymentCreateSerializer(serializers.ModelSerializer):
126
+ class AdminPaymentCreateSerializer(serializers.Serializer):
74
127
  """
75
128
  Serializer for creating payments in admin interface.
129
+ Uses UniversalPayment only for data creation.
76
130
  """
77
- class Meta:
78
- model = UniversalPayment
79
- fields = [
80
- 'user', 'amount_usd', 'currency_code', 'provider',
81
- 'description', 'callback_url', 'cancel_url'
82
- ]
131
+ user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
132
+ amount_usd = serializers.FloatField(min_value=1.0, max_value=100000.0)
133
+ currency_code = serializers.CharField(max_length=20, help_text="Provider currency code (e.g., BTC, ZROERC20)", write_only=True)
134
+ provider = serializers.CharField(max_length=50)
135
+ description = serializers.CharField(required=False, allow_blank=True)
136
+ callback_url = serializers.URLField(required=False, allow_blank=True)
137
+ cancel_url = serializers.URLField(required=False, allow_blank=True)
83
138
 
84
139
  def validate_amount_usd(self, value):
85
140
  """Validate USD amount."""
@@ -88,6 +143,69 @@ class AdminPaymentCreateSerializer(serializers.ModelSerializer):
88
143
  if value > 100000: # Max $100k per payment
89
144
  raise serializers.ValidationError("Amount exceeds maximum limit")
90
145
  return value
146
+
147
+ def create(self, validated_data):
148
+ """Create payment using PaymentService for proper provider integration."""
149
+ from django_cfg.apps.payments.services.core.payment_service import PaymentService
150
+ from django_cfg.apps.payments.services.types.requests import PaymentCreateRequest
151
+ from django_cfg.apps.payments.models import ProviderCurrency
152
+
153
+ # Extract provider_currency_code and find original currency
154
+ provider_currency_code = validated_data.pop('currency_code')
155
+
156
+ # Find the ProviderCurrency to get original currency code
157
+ try:
158
+ provider_currency = ProviderCurrency.objects.select_related('currency').get(
159
+ provider_currency_code=provider_currency_code,
160
+ provider=validated_data['provider'],
161
+ is_enabled=True
162
+ )
163
+ original_currency_code = provider_currency.currency.code
164
+ except ProviderCurrency.DoesNotExist:
165
+ raise serializers.ValidationError(f"Provider currency {provider_currency_code} not found for {validated_data['provider']}")
166
+
167
+ # Create PaymentCreateRequest for the service
168
+ payment_request = PaymentCreateRequest(
169
+ user_id=validated_data['user'].id,
170
+ amount_usd=validated_data['amount_usd'],
171
+ currency_code=original_currency_code, # Use original currency code
172
+ provider=validated_data['provider'],
173
+ description=validated_data.get('description', ''),
174
+ callback_url=validated_data.get('callback_url', ''),
175
+ cancel_url=validated_data.get('cancel_url', ''),
176
+ metadata={'provider_currency_code': provider_currency_code} # Store provider code in metadata
177
+ )
178
+
179
+ # Use PaymentService to create payment with provider integration
180
+ payment_service = PaymentService()
181
+ result = payment_service.create_payment(payment_request)
182
+
183
+ if result.success:
184
+ # Get the created payment object from database using payment_id
185
+ from django_cfg.apps.payments.models import UniversalPayment
186
+ payment = UniversalPayment.objects.get(id=result.payment_id)
187
+ return payment
188
+ else:
189
+ raise serializers.ValidationError(f"Payment creation failed: {result.message}")
190
+
191
+ def to_representation(self, instance):
192
+ """Return created payment with ID and currency info."""
193
+ if instance:
194
+ return {
195
+ 'id': str(instance.id),
196
+ 'user': instance.user.id,
197
+ 'amount_usd': instance.amount_usd,
198
+ 'currency_code': instance.currency.code if instance.currency else None,
199
+ 'currency_name': instance.currency.name if instance.currency else None,
200
+ 'provider': instance.provider,
201
+ 'description': instance.description,
202
+ 'callback_url': instance.callback_url,
203
+ 'cancel_url': instance.cancel_url,
204
+ 'internal_payment_id': instance.internal_payment_id,
205
+ 'status': instance.status,
206
+ 'created_at': instance.created_at.isoformat() if instance.created_at else None
207
+ }
208
+ return super().to_representation(instance)
91
209
 
92
210
 
93
211
  class AdminPaymentUpdateSerializer(serializers.ModelSerializer):
@@ -107,6 +225,7 @@ class AdminPaymentUpdateSerializer(serializers.ModelSerializer):
107
225
  if value != UniversalPayment.PaymentStatus.COMPLETED:
108
226
  raise serializers.ValidationError("Cannot change status of completed payment")
109
227
  return value
228
+
110
229
 
111
230
 
112
231
  class AdminPaymentStatsSerializer(serializers.Serializer):
@@ -0,0 +1,121 @@
1
+ {% comment %}
2
+ Payment Card Component
3
+
4
+ Usage:
5
+ {% include 'payments/components/payment_card.html' with payment=payment show_actions=True %}
6
+
7
+ Parameters:
8
+ - payment: UniversalPayment object
9
+ - show_actions: Boolean to show/hide action buttons (default: False)
10
+ {% endcomment %}
11
+
12
+ <div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6"
13
+ data-payment-id="{{ payment.id }}">
14
+
15
+ <!-- Header -->
16
+ <div class="flex items-center justify-between mb-4">
17
+ <div class="payment-id">
18
+ <span class="text-sm text-gray-500 dark:text-gray-400">Payment</span>
19
+ <p class="text-lg font-semibold text-gray-900 dark:text-white">
20
+ #{{ payment.internal_payment_id|default:payment.id|truncatechars:8 }}
21
+ </p>
22
+ </div>
23
+ {% include 'payments/components/status_badge.html' with payment=payment %}
24
+ </div>
25
+
26
+ <!-- Amount -->
27
+ <div class="payment-amount mb-4">
28
+ <div class="flex items-baseline">
29
+ <span class="text-3xl font-bold text-gray-900 dark:text-white">
30
+ ${{ payment.amount_usd|floatformat:2 }}
31
+ </span>
32
+ <span class="ml-2 text-lg text-gray-500 dark:text-gray-400">USD</span>
33
+ </div>
34
+ {% if payment.currency.code != 'USD' and payment.pay_amount %}
35
+ <div class="text-sm text-gray-500 dark:text-gray-400">
36
+ {{ payment.pay_amount|floatformat:8 }} {{ payment.currency.code }}
37
+ </div>
38
+ {% endif %}
39
+ </div>
40
+
41
+ <!-- Payment Details -->
42
+ <div class="payment-details space-y-2 mb-4">
43
+ <div class="flex justify-between">
44
+ <span class="text-sm text-gray-500 dark:text-gray-400">Provider</span>
45
+ <div class="flex items-center">
46
+ <div class="w-2 h-2 rounded-full mr-2
47
+ {% if payment.provider == 'cryptapi' %}bg-orange-500
48
+ {% elif payment.provider == 'cryptomus' %}bg-blue-500
49
+ {% elif payment.provider == 'stripe' %}bg-purple-500
50
+ {% elif payment.provider == 'nowpayments' %}bg-green-500
51
+ {% else %}bg-gray-500{% endif %}">
52
+ </div>
53
+ <span class="text-sm font-medium text-gray-900 dark:text-white capitalize">
54
+ {{ payment.provider }}
55
+ </span>
56
+ </div>
57
+ </div>
58
+ <div class="flex justify-between">
59
+ <span class="text-sm text-gray-500 dark:text-gray-400">Created</span>
60
+ <span class="text-sm text-gray-900 dark:text-white">
61
+ {{ payment.created_at|date:"M d, H:i" }}
62
+ </span>
63
+ </div>
64
+ {% if payment.status_changed_at %}
65
+ <div class="flex justify-between">
66
+ <span class="text-sm text-gray-500 dark:text-gray-400">Status Changed</span>
67
+ <span class="text-sm text-gray-900 dark:text-white">
68
+ {{ payment.status_changed_at|date:"M d, H:i" }}
69
+ </span>
70
+ </div>
71
+ {% endif %}
72
+ {% if payment.processed_at %}
73
+ <div class="flex justify-between">
74
+ <span class="text-sm text-gray-500 dark:text-gray-400">Processed</span>
75
+ <span class="text-sm text-gray-900 dark:text-white">
76
+ {{ payment.processed_at|date:"M d, H:i" }}
77
+ </span>
78
+ </div>
79
+ {% endif %}
80
+ {% if payment.pay_address %}
81
+ <div class="flex justify-between">
82
+ <span class="text-sm text-gray-500 dark:text-gray-400">Address</span>
83
+ <span class="text-sm font-mono text-gray-900 dark:text-white">
84
+ {{ payment.pay_address|truncatechars:16 }}
85
+ <button onclick="navigator.clipboard.writeText('{{ payment.pay_address }}')"
86
+ class="ml-1 text-blue-500 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300">
87
+ <span class="material-icons-outlined text-xs">content_copy</span>
88
+ </button>
89
+ </span>
90
+ </div>
91
+ {% endif %}
92
+ </div>
93
+
94
+ <!-- Progress Bar -->
95
+ {% include 'payments/components/progress_bar.html' with payment=payment %}
96
+
97
+ <!-- Actions -->
98
+ {% if show_actions %}
99
+ <div class="payment-actions mt-4 flex space-x-2">
100
+ <a href="{% url 'cfg_payments_admin:payment-detail' payment.id %}"
101
+ class="flex-1 px-3 py-1.5 text-xs border border-gray-300 dark:border-gray-500 text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 rounded-md transition-colors duration-200 text-center">
102
+ <span class="material-icons-outlined text-sm mr-1">visibility</span>
103
+ View Details
104
+ </a>
105
+ {% if payment.status == 'pending' or payment.status == 'confirming' %}
106
+ <button class="px-3 py-1.5 text-xs bg-red-600 text-white hover:bg-red-700 dark:bg-red-500 dark:hover:bg-red-600 rounded-md transition-colors duration-200"
107
+ onclick="cancelPayment('{{ payment.id }}')">
108
+ <span class="material-icons-outlined text-sm mr-1">cancel</span>
109
+ Cancel
110
+ </button>
111
+ {% endif %}
112
+ {% if payment.pay_address %}
113
+ <button class="px-3 py-1.5 text-xs bg-blue-600 text-white hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 rounded-md transition-colors duration-200"
114
+ onclick="showQRCode('{{ payment.id }}')">
115
+ <span class="material-icons-outlined text-sm mr-1">qr_code</span>
116
+ QR Code
117
+ </button>
118
+ {% endif %}
119
+ </div>
120
+ {% endif %}
121
+ </div>