django-cfg 1.3.7__py3-none-any.whl → 1.3.9__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 (251) 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 +258 -0
  43. django_cfg/apps/payments/admin/payments_admin.py +171 -461
  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 +105 -34
  47. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +12 -16
  48. django_cfg/apps/payments/admin_interface/views/__init__.py +2 -0
  49. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +13 -18
  50. django_cfg/apps/payments/management/commands/manage_currencies.py +236 -274
  51. django_cfg/apps/payments/management/commands/manage_providers.py +4 -1
  52. django_cfg/apps/payments/middleware/api_access.py +32 -6
  53. django_cfg/apps/payments/migrations/0002_currency_usd_rate_currency_usd_rate_updated_at.py +26 -0
  54. django_cfg/apps/payments/migrations/0003_remove_provider_currency_fields.py +28 -0
  55. django_cfg/apps/payments/migrations/0004_add_reserved_usd_field.py +30 -0
  56. django_cfg/apps/payments/models/balance.py +12 -0
  57. django_cfg/apps/payments/models/currencies.py +106 -32
  58. django_cfg/apps/payments/models/managers/currency_managers.py +65 -0
  59. django_cfg/apps/payments/services/core/currency_service.py +35 -28
  60. django_cfg/apps/payments/services/core/payment_service.py +1 -1
  61. django_cfg/apps/payments/services/providers/__init__.py +3 -0
  62. django_cfg/apps/payments/services/providers/base.py +95 -39
  63. django_cfg/apps/payments/services/providers/models/__init__.py +40 -0
  64. django_cfg/apps/payments/services/providers/models/base.py +122 -0
  65. django_cfg/apps/payments/services/providers/models/providers.py +87 -0
  66. django_cfg/apps/payments/services/providers/models/universal.py +48 -0
  67. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +31 -0
  68. django_cfg/apps/payments/services/providers/nowpayments/config.py +70 -0
  69. django_cfg/apps/payments/services/providers/nowpayments/models.py +150 -0
  70. django_cfg/apps/payments/services/providers/nowpayments/parsers.py +879 -0
  71. django_cfg/apps/payments/services/providers/{nowpayments.py → nowpayments/provider.py} +240 -209
  72. django_cfg/apps/payments/services/providers/nowpayments/sync.py +196 -0
  73. django_cfg/apps/payments/services/providers/registry.py +4 -32
  74. django_cfg/apps/payments/services/providers/sync_service.py +277 -0
  75. django_cfg/apps/payments/static/payments/js/api-client.js +23 -5
  76. django_cfg/apps/payments/static/payments/js/payment-form.js +65 -8
  77. django_cfg/apps/payments/tasks/__init__.py +39 -0
  78. django_cfg/apps/payments/tasks/types.py +73 -0
  79. django_cfg/apps/payments/tasks/usage_tracking.py +308 -0
  80. django_cfg/apps/payments/templates/admin/payments/_components/dashboard_header.html +23 -0
  81. django_cfg/apps/payments/templates/admin/payments/_components/stats_card.html +25 -0
  82. django_cfg/apps/payments/templates/admin/payments/_components/stats_grid.html +16 -0
  83. django_cfg/apps/payments/templates/admin/payments/apikey/change_list.html +39 -0
  84. django_cfg/apps/payments/templates/admin/payments/balance/change_list.html +50 -0
  85. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +40 -0
  86. django_cfg/apps/payments/templates/admin/payments/payment/change_list.html +48 -0
  87. django_cfg/apps/payments/templates/admin/payments/subscription/change_list.html +48 -0
  88. django_cfg/apps/payments/urls_admin.py +1 -1
  89. django_cfg/apps/payments/views/api/currencies.py +5 -5
  90. django_cfg/apps/payments/views/overview/services.py +2 -2
  91. django_cfg/apps/payments/views/serializers/currencies.py +4 -3
  92. django_cfg/apps/support/admin/__init__.py +10 -1
  93. django_cfg/apps/support/admin/support_admin.py +338 -141
  94. django_cfg/apps/tasks/admin/__init__.py +11 -0
  95. django_cfg/apps/tasks/admin/tasks_admin.py +430 -0
  96. django_cfg/config.py +1 -1
  97. django_cfg/core/config.py +10 -5
  98. django_cfg/core/generation.py +1 -1
  99. django_cfg/management/commands/__init__.py +13 -1
  100. django_cfg/management/commands/app_agent_diagnose.py +470 -0
  101. django_cfg/management/commands/app_agent_generate.py +342 -0
  102. django_cfg/management/commands/app_agent_info.py +308 -0
  103. django_cfg/management/commands/migrate_all.py +9 -3
  104. django_cfg/management/commands/migrator.py +11 -6
  105. django_cfg/management/commands/rundramatiq.py +3 -2
  106. django_cfg/middleware/__init__.py +0 -2
  107. django_cfg/models/api_keys.py +115 -0
  108. django_cfg/modules/django_admin/__init__.py +64 -0
  109. django_cfg/modules/django_admin/decorators/__init__.py +13 -0
  110. django_cfg/modules/django_admin/decorators/actions.py +106 -0
  111. django_cfg/modules/django_admin/decorators/display.py +106 -0
  112. django_cfg/modules/django_admin/mixins/__init__.py +14 -0
  113. django_cfg/modules/django_admin/mixins/display_mixin.py +81 -0
  114. django_cfg/modules/django_admin/mixins/optimization_mixin.py +41 -0
  115. django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +202 -0
  116. django_cfg/modules/django_admin/models/__init__.py +20 -0
  117. django_cfg/modules/django_admin/models/action_models.py +33 -0
  118. django_cfg/modules/django_admin/models/badge_models.py +20 -0
  119. django_cfg/modules/django_admin/models/base.py +26 -0
  120. django_cfg/modules/django_admin/models/display_models.py +31 -0
  121. django_cfg/modules/django_admin/utils/badges.py +159 -0
  122. django_cfg/modules/django_admin/utils/displays.py +247 -0
  123. django_cfg/modules/django_app_agent/__init__.py +87 -0
  124. django_cfg/modules/django_app_agent/agents/__init__.py +40 -0
  125. django_cfg/modules/django_app_agent/agents/base/__init__.py +24 -0
  126. django_cfg/modules/django_app_agent/agents/base/agent.py +354 -0
  127. django_cfg/modules/django_app_agent/agents/base/context.py +236 -0
  128. django_cfg/modules/django_app_agent/agents/base/executor.py +430 -0
  129. django_cfg/modules/django_app_agent/agents/generation/__init__.py +12 -0
  130. django_cfg/modules/django_app_agent/agents/generation/app_generator/__init__.py +15 -0
  131. django_cfg/modules/django_app_agent/agents/generation/app_generator/config_validator.py +147 -0
  132. django_cfg/modules/django_app_agent/agents/generation/app_generator/main.py +99 -0
  133. django_cfg/modules/django_app_agent/agents/generation/app_generator/models.py +32 -0
  134. django_cfg/modules/django_app_agent/agents/generation/app_generator/prompt_manager.py +290 -0
  135. django_cfg/modules/django_app_agent/agents/interfaces.py +376 -0
  136. django_cfg/modules/django_app_agent/core/__init__.py +33 -0
  137. django_cfg/modules/django_app_agent/core/config.py +300 -0
  138. django_cfg/modules/django_app_agent/core/exceptions.py +359 -0
  139. django_cfg/modules/django_app_agent/models/__init__.py +71 -0
  140. django_cfg/modules/django_app_agent/models/base.py +283 -0
  141. django_cfg/modules/django_app_agent/models/context.py +496 -0
  142. django_cfg/modules/django_app_agent/models/enums.py +481 -0
  143. django_cfg/modules/django_app_agent/models/requests.py +500 -0
  144. django_cfg/modules/django_app_agent/models/responses.py +585 -0
  145. django_cfg/modules/django_app_agent/pytest.ini +6 -0
  146. django_cfg/modules/django_app_agent/services/__init__.py +42 -0
  147. django_cfg/modules/django_app_agent/services/app_generator/__init__.py +30 -0
  148. django_cfg/modules/django_app_agent/services/app_generator/ai_integration.py +133 -0
  149. django_cfg/modules/django_app_agent/services/app_generator/context.py +40 -0
  150. django_cfg/modules/django_app_agent/services/app_generator/main.py +202 -0
  151. django_cfg/modules/django_app_agent/services/app_generator/structure.py +316 -0
  152. django_cfg/modules/django_app_agent/services/app_generator/validation.py +125 -0
  153. django_cfg/modules/django_app_agent/services/base.py +437 -0
  154. django_cfg/modules/django_app_agent/services/context_builder/__init__.py +34 -0
  155. django_cfg/modules/django_app_agent/services/context_builder/code_extractor.py +141 -0
  156. django_cfg/modules/django_app_agent/services/context_builder/context_generator.py +276 -0
  157. django_cfg/modules/django_app_agent/services/context_builder/main.py +272 -0
  158. django_cfg/modules/django_app_agent/services/context_builder/models.py +40 -0
  159. django_cfg/modules/django_app_agent/services/context_builder/pattern_analyzer.py +85 -0
  160. django_cfg/modules/django_app_agent/services/project_scanner/__init__.py +31 -0
  161. django_cfg/modules/django_app_agent/services/project_scanner/app_discovery.py +311 -0
  162. django_cfg/modules/django_app_agent/services/project_scanner/main.py +221 -0
  163. django_cfg/modules/django_app_agent/services/project_scanner/models.py +59 -0
  164. django_cfg/modules/django_app_agent/services/project_scanner/pattern_detection.py +94 -0
  165. django_cfg/modules/django_app_agent/services/questioning_service/__init__.py +28 -0
  166. django_cfg/modules/django_app_agent/services/questioning_service/main.py +273 -0
  167. django_cfg/modules/django_app_agent/services/questioning_service/models.py +111 -0
  168. django_cfg/modules/django_app_agent/services/questioning_service/question_generator.py +251 -0
  169. django_cfg/modules/django_app_agent/services/questioning_service/response_processor.py +347 -0
  170. django_cfg/modules/django_app_agent/services/questioning_service/session_manager.py +356 -0
  171. django_cfg/modules/django_app_agent/services/report_service.py +332 -0
  172. django_cfg/modules/django_app_agent/services/template_manager/__init__.py +18 -0
  173. django_cfg/modules/django_app_agent/services/template_manager/jinja_engine.py +236 -0
  174. django_cfg/modules/django_app_agent/services/template_manager/main.py +159 -0
  175. django_cfg/modules/django_app_agent/services/template_manager/models.py +36 -0
  176. django_cfg/modules/django_app_agent/services/template_manager/template_loader.py +100 -0
  177. django_cfg/modules/django_app_agent/services/template_manager/templates/admin.py.j2 +105 -0
  178. django_cfg/modules/django_app_agent/services/template_manager/templates/apps.py.j2 +31 -0
  179. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_config.py.j2 +44 -0
  180. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_module.py.j2 +81 -0
  181. django_cfg/modules/django_app_agent/services/template_manager/templates/forms.py.j2 +107 -0
  182. django_cfg/modules/django_app_agent/services/template_manager/templates/models.py.j2 +139 -0
  183. django_cfg/modules/django_app_agent/services/template_manager/templates/serializers.py.j2 +91 -0
  184. django_cfg/modules/django_app_agent/services/template_manager/templates/tests.py.j2 +195 -0
  185. django_cfg/modules/django_app_agent/services/template_manager/templates/urls.py.j2 +35 -0
  186. django_cfg/modules/django_app_agent/services/template_manager/templates/views.py.j2 +211 -0
  187. django_cfg/modules/django_app_agent/services/template_manager/variable_processor.py +200 -0
  188. django_cfg/modules/django_app_agent/services/validation_service/__init__.py +25 -0
  189. django_cfg/modules/django_app_agent/services/validation_service/django_validator.py +333 -0
  190. django_cfg/modules/django_app_agent/services/validation_service/main.py +242 -0
  191. django_cfg/modules/django_app_agent/services/validation_service/models.py +66 -0
  192. django_cfg/modules/django_app_agent/services/validation_service/quality_validator.py +352 -0
  193. django_cfg/modules/django_app_agent/services/validation_service/security_validator.py +272 -0
  194. django_cfg/modules/django_app_agent/services/validation_service/syntax_validator.py +203 -0
  195. django_cfg/modules/django_app_agent/ui/__init__.py +25 -0
  196. django_cfg/modules/django_app_agent/ui/cli.py +419 -0
  197. django_cfg/modules/django_app_agent/ui/rich_components.py +622 -0
  198. django_cfg/modules/django_app_agent/utils/__init__.py +38 -0
  199. django_cfg/modules/django_app_agent/utils/logging.py +360 -0
  200. django_cfg/modules/django_app_agent/utils/validation.py +417 -0
  201. django_cfg/modules/django_currency/__init__.py +2 -2
  202. django_cfg/modules/django_currency/clients/__init__.py +2 -2
  203. django_cfg/modules/django_currency/clients/hybrid_client.py +587 -0
  204. django_cfg/modules/django_currency/core/converter.py +12 -12
  205. django_cfg/modules/django_currency/database/__init__.py +2 -2
  206. django_cfg/modules/django_currency/database/database_loader.py +93 -42
  207. django_cfg/modules/django_llm/llm/client.py +10 -2
  208. django_cfg/modules/django_unfold/callbacks/actions.py +1 -1
  209. django_cfg/modules/django_unfold/callbacks/statistics.py +1 -1
  210. django_cfg/modules/django_unfold/dashboard.py +14 -13
  211. django_cfg/modules/django_unfold/models/config.py +1 -1
  212. django_cfg/registry/core.py +3 -0
  213. django_cfg/registry/third_party.py +2 -2
  214. django_cfg/template_archive/django_sample.zip +0 -0
  215. {django_cfg-1.3.7.dist-info → django_cfg-1.3.9.dist-info}/METADATA +2 -1
  216. {django_cfg-1.3.7.dist-info → django_cfg-1.3.9.dist-info}/RECORD +223 -117
  217. django_cfg/apps/accounts/admin/activity.py +0 -96
  218. django_cfg/apps/accounts/admin/group.py +0 -17
  219. django_cfg/apps/accounts/admin/otp.py +0 -59
  220. django_cfg/apps/accounts/admin/registration_source.py +0 -97
  221. django_cfg/apps/accounts/admin/twilio_response.py +0 -227
  222. django_cfg/apps/accounts/admin/user.py +0 -300
  223. django_cfg/apps/agents/core/agent.py +0 -281
  224. django_cfg/apps/payments/admin_interface/old/payments/base.html +0 -175
  225. django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +0 -125
  226. django_cfg/apps/payments/admin_interface/old/payments/components/loading_spinner.html +0 -16
  227. django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +0 -113
  228. django_cfg/apps/payments/admin_interface/old/payments/components/notification.html +0 -27
  229. django_cfg/apps/payments/admin_interface/old/payments/components/provider_card.html +0 -86
  230. django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +0 -35
  231. django_cfg/apps/payments/admin_interface/old/payments/currency_converter.html +0 -382
  232. django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +0 -309
  233. django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +0 -303
  234. django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +0 -382
  235. django_cfg/apps/payments/admin_interface/old/payments/payment_status.html +0 -500
  236. django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +0 -518
  237. django_cfg/apps/payments/admin_interface/old/static/payments/css/components.css +0 -619
  238. django_cfg/apps/payments/admin_interface/old/static/payments/css/dashboard.css +0 -188
  239. django_cfg/apps/payments/admin_interface/old/static/payments/js/components.js +0 -545
  240. django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +0 -163
  241. django_cfg/apps/payments/admin_interface/old/static/payments/js/utils.js +0 -412
  242. django_cfg/apps/tasks/admin.py +0 -320
  243. django_cfg/middleware/static_nocache.py +0 -55
  244. django_cfg/modules/django_currency/clients/yahoo_client.py +0 -157
  245. /django_cfg/modules/{django_unfold → django_admin}/icons/README.md +0 -0
  246. /django_cfg/modules/{django_unfold → django_admin}/icons/__init__.py +0 -0
  247. /django_cfg/modules/{django_unfold → django_admin}/icons/constants.py +0 -0
  248. /django_cfg/modules/{django_unfold → django_admin}/icons/generate_icons.py +0 -0
  249. {django_cfg-1.3.7.dist-info → django_cfg-1.3.9.dist-info}/WHEEL +0 -0
  250. {django_cfg-1.3.7.dist-info → django_cfg-1.3.9.dist-info}/entry_points.txt +0 -0
  251. {django_cfg-1.3.7.dist-info → django_cfg-1.3.9.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)
@@ -21,65 +21,118 @@ class AdminUserSerializer(serializers.ModelSerializer):
21
21
  read_only_fields = fields
22
22
 
23
23
 
24
- class AdminPaymentListSerializer(serializers.ModelSerializer):
24
+ class AdminPaymentListSerializer(serializers.Serializer):
25
25
  """
26
26
  Serializer for payment list in admin interface.
27
+ Uses UniversalPayment only for data extraction.
27
28
  """
29
+ id = serializers.UUIDField(read_only=True)
28
30
  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)
31
+ amount_usd = serializers.FloatField(read_only=True)
32
+ currency_code = serializers.SerializerMethodField()
33
+ currency_name = serializers.SerializerMethodField()
34
+ provider = serializers.CharField(read_only=True)
35
+ provider_display = serializers.SerializerMethodField()
36
+ status = serializers.CharField(read_only=True)
37
+ status_display = serializers.SerializerMethodField()
38
+ pay_amount = serializers.DecimalField(max_digits=20, decimal_places=8, read_only=True)
39
+ pay_address = serializers.CharField(read_only=True)
40
+ transaction_hash = serializers.CharField(read_only=True)
41
+ created_at = serializers.DateTimeField(read_only=True)
42
+ updated_at = serializers.DateTimeField(read_only=True)
43
+ description = serializers.CharField(read_only=True)
31
44
  age = serializers.SerializerMethodField()
32
45
 
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
46
+ def get_currency_code(self, obj):
47
+ """Get currency code from related Currency model."""
48
+ return obj.currency.code if obj.currency else None
49
+
50
+ def get_currency_name(self, obj):
51
+ """Get currency name from related Currency model."""
52
+ return obj.currency.name if obj.currency else None
53
+
54
+ def get_provider_display(self, obj):
55
+ """Get human-readable provider name."""
56
+ return obj.get_provider_display()
57
+
58
+ def get_status_display(self, obj):
59
+ """Get human-readable status."""
60
+ return obj.get_status_display()
41
61
 
42
62
  def get_age(self, obj):
43
63
  """Get human-readable age of payment."""
44
- return obj.age_display
64
+ from django.contrib.humanize.templatetags.humanize import naturaltime
65
+ return naturaltime(obj.created_at)
45
66
 
46
67
 
47
- class AdminPaymentDetailSerializer(serializers.ModelSerializer):
68
+ class AdminPaymentDetailSerializer(serializers.Serializer):
48
69
  """
49
70
  Detailed serializer for individual payment in admin interface.
71
+ Uses UniversalPayment only for data extraction.
50
72
  """
73
+ id = serializers.UUIDField(read_only=True)
51
74
  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)
75
+ internal_payment_id = serializers.CharField(read_only=True)
76
+ amount_usd = serializers.FloatField(read_only=True)
77
+ actual_amount_usd = serializers.FloatField(read_only=True)
78
+ fee_amount_usd = serializers.FloatField(read_only=True)
79
+ currency_code = serializers.SerializerMethodField()
80
+ currency_name = serializers.SerializerMethodField()
81
+ provider = serializers.CharField(read_only=True)
82
+ provider_display = serializers.SerializerMethodField()
83
+ status = serializers.CharField(read_only=True)
84
+ status_display = serializers.SerializerMethodField()
85
+ pay_amount = serializers.DecimalField(max_digits=20, decimal_places=8, read_only=True)
86
+ pay_address = serializers.CharField(read_only=True)
87
+ payment_url = serializers.URLField(read_only=True)
88
+ transaction_hash = serializers.CharField(read_only=True)
89
+ confirmations_count = serializers.IntegerField(read_only=True)
90
+ security_nonce = serializers.CharField(read_only=True)
91
+ expires_at = serializers.DateTimeField(read_only=True)
92
+ completed_at = serializers.DateTimeField(read_only=True)
93
+ description = serializers.CharField(read_only=True)
94
+ callback_url = serializers.URLField(read_only=True)
95
+ cancel_url = serializers.URLField(read_only=True)
96
+ provider_data = serializers.JSONField(read_only=True)
97
+ webhook_data = serializers.JSONField(read_only=True)
98
+ created_at = serializers.DateTimeField(read_only=True)
99
+ updated_at = serializers.DateTimeField(read_only=True)
54
100
  age = serializers.SerializerMethodField()
55
101
 
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
102
+ def get_currency_code(self, obj):
103
+ """Get currency code from related Currency model."""
104
+ return obj.currency.code if obj.currency else None
105
+
106
+ def get_currency_name(self, obj):
107
+ """Get currency name from related Currency model."""
108
+ return obj.currency.name if obj.currency else None
109
+
110
+ def get_provider_display(self, obj):
111
+ """Get human-readable provider name."""
112
+ return obj.get_provider_display()
113
+
114
+ def get_status_display(self, obj):
115
+ """Get human-readable status."""
116
+ return obj.get_status_display()
67
117
 
68
118
  def get_age(self, obj):
69
119
  """Get human-readable age of payment."""
70
- return obj.age_display
120
+ from django.contrib.humanize.templatetags.humanize import naturaltime
121
+ return naturaltime(obj.created_at)
71
122
 
72
123
 
73
- class AdminPaymentCreateSerializer(serializers.ModelSerializer):
124
+ class AdminPaymentCreateSerializer(serializers.Serializer):
74
125
  """
75
126
  Serializer for creating payments in admin interface.
127
+ Uses UniversalPayment only for data creation.
76
128
  """
77
- class Meta:
78
- model = UniversalPayment
79
- fields = [
80
- 'user', 'amount_usd', 'currency_code', 'provider',
81
- 'description', 'callback_url', 'cancel_url'
82
- ]
129
+ user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
130
+ amount_usd = serializers.FloatField(min_value=1.0, max_value=100000.0)
131
+ currency_code = serializers.CharField(max_length=10, help_text="Currency code (e.g., BTC, ETH)")
132
+ provider = serializers.CharField(max_length=50)
133
+ description = serializers.CharField(required=False, allow_blank=True)
134
+ callback_url = serializers.URLField(required=False, allow_blank=True)
135
+ cancel_url = serializers.URLField(required=False, allow_blank=True)
83
136
 
84
137
  def validate_amount_usd(self, value):
85
138
  """Validate USD amount."""
@@ -88,6 +141,24 @@ class AdminPaymentCreateSerializer(serializers.ModelSerializer):
88
141
  if value > 100000: # Max $100k per payment
89
142
  raise serializers.ValidationError("Amount exceeds maximum limit")
90
143
  return value
144
+
145
+ def create(self, validated_data):
146
+ """Create payment with currency lookup."""
147
+ from django_cfg.apps.payments.models.currencies import Currency
148
+ import uuid
149
+
150
+ # Extract currency_code and find Currency object
151
+ currency_code = validated_data.pop('currency_code')
152
+ try:
153
+ currency = Currency.objects.get(code=currency_code)
154
+ except Currency.DoesNotExist:
155
+ raise serializers.ValidationError(f"Currency {currency_code} not found")
156
+
157
+ # Generate internal payment ID and create payment
158
+ validated_data['currency'] = currency
159
+ validated_data['internal_payment_id'] = f"PAY-{uuid.uuid4().hex[:12].upper()}"
160
+
161
+ return UniversalPayment.objects.create(**validated_data)
91
162
 
92
163
 
93
164
  class AdminPaymentUpdateSerializer(serializers.ModelSerializer):
@@ -96,12 +96,8 @@
96
96
  class="block w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white disabled:opacity-50"
97
97
  required>
98
98
  <option value="">Select currency...</option>
99
- <template x-for="currency in currencies" :key="currency.code">
100
- <option :value="currency.code">
101
- <span x-text="currency.code"></span> -
102
- <span x-text="currency.name"></span>
103
- <span x-show="currency.type === 'crypto'" class="text-blue-600"> (Crypto)</span>
104
- <span x-show="currency.network_name" class="text-gray-500" x-text="`[${currency.network_name}]`"></span>
99
+ <template x-for="currency in currencies" :key="`${currency.code}-${currency.provider_code || currency.network || Math.random()}`">
100
+ <option :value="currency.code" x-text="`${currency.code} - ${currency.name}${currency.type === 'crypto' ? ' (Crypto)' : ''}${currency.network_name ? ' [' + currency.network_name + ']' : ''}`">
105
101
  </option>
106
102
  </template>
107
103
  </select>
@@ -122,22 +118,22 @@
122
118
  <span class="text-gray-500">Network:</span>
123
119
  <span x-text="getCurrencyInfo(form.currency_code)?.network_name"></span>
124
120
  </div>
125
- <div x-show="getCurrencyInfo(form.currency_code)?.min_amount || getCurrencyInfo(form.currency_code)?.max_amount" class="mt-1">
121
+ <div x-show="getCurrencyInfo(form.currency_code)?.provider_min_amount_usd || getCurrencyInfo(form.currency_code)?.provider_max_amount_usd" class="mt-1">
126
122
  <span class="text-gray-500">Limits:</span>
127
- <span x-show="getCurrencyInfo(form.currency_code)?.min_amount">
128
- Min: $<span x-text="getCurrencyInfo(form.currency_code)?.min_amount"></span>
123
+ <span x-show="getCurrencyInfo(form.currency_code)?.provider_min_amount_usd">
124
+ Min: $<span x-text="getCurrencyInfo(form.currency_code)?.provider_min_amount_usd"></span>
129
125
  </span>
130
- <span x-show="getCurrencyInfo(form.currency_code)?.max_amount">
131
- Max: $<span x-text="getCurrencyInfo(form.currency_code)?.max_amount"></span>
126
+ <span x-show="getCurrencyInfo(form.currency_code)?.provider_max_amount_usd">
127
+ Max: $<span x-text="getCurrencyInfo(form.currency_code)?.provider_max_amount_usd"></span>
132
128
  </span>
133
129
  </div>
134
- <div x-show="getCurrencyInfo(form.currency_code)?.fee_percentage > 0 || getCurrencyInfo(form.currency_code)?.fixed_fee > 0" class="mt-1">
130
+ <div x-show="getCurrencyInfo(form.currency_code)?.provider_fee_percentage > 0 || getCurrencyInfo(form.currency_code)?.provider_fixed_fee_usd > 0" class="mt-1">
135
131
  <span class="text-gray-500">Fees:</span>
136
- <span x-show="getCurrencyInfo(form.currency_code)?.fee_percentage > 0">
137
- <span x-text="(getCurrencyInfo(form.currency_code)?.fee_percentage * 100).toFixed(2)"></span>%
132
+ <span x-show="getCurrencyInfo(form.currency_code)?.provider_fee_percentage > 0">
133
+ <span x-text="(getCurrencyInfo(form.currency_code)?.provider_fee_percentage * 100).toFixed(2)"></span>%
138
134
  </span>
139
- <span x-show="getCurrencyInfo(form.currency_code)?.fixed_fee > 0">
140
- + $<span x-text="getCurrencyInfo(form.currency_code)?.fixed_fee"></span>
135
+ <span x-show="getCurrencyInfo(form.currency_code)?.provider_fixed_fee_usd > 0">
136
+ + $<span x-text="getCurrencyInfo(form.currency_code)?.provider_fixed_fee_usd"></span>
141
137
  </span>
142
138
  </div>
143
139
  </div>
@@ -13,6 +13,7 @@ from .api import (
13
13
  AdminPaymentViewSet,
14
14
  AdminWebhookViewSet,
15
15
  AdminWebhookEventViewSet,
16
+ WebhookTestViewSet,
16
17
  AdminStatsViewSet,
17
18
  AdminUserViewSet,
18
19
  )
@@ -29,6 +30,7 @@ __all__ = [
29
30
  'AdminPaymentViewSet',
30
31
  'AdminWebhookViewSet',
31
32
  'AdminWebhookEventViewSet',
33
+ 'WebhookTestViewSet',
32
34
  'AdminStatsViewSet',
33
35
  'AdminUserViewSet',
34
36
  ]
@@ -79,39 +79,34 @@ class AdminWebhookViewSet(AdminReadOnlyViewSet):
79
79
 
80
80
  # Mock webhook stats based on real payment data
81
81
  stats_data = {
82
- 'total_events': total_payments * 2, # Assume 2 events per payment on average
83
- 'successful_events': int(total_payments * 1.8), # 90% success rate
84
- 'failed_events': int(total_payments * 0.2), # 10% failure rate
82
+ 'total': total_payments * 2, # Assume 2 events per payment on average
83
+ 'successful': int(total_payments * 1.8), # 90% success rate
84
+ 'failed': int(total_payments * 0.2), # 10% failure rate
85
+ 'pending': 0, # No pending events for now
85
86
  'success_rate': 90.0,
86
- 'recent_events': recent_payments * 2,
87
87
  'providers': {
88
88
  'nowpayments': {
89
89
  'total': int(total_payments * 0.7),
90
90
  'successful': int(total_payments * 0.65),
91
91
  'failed': int(total_payments * 0.05),
92
+ 'pending': 0,
92
93
  'success_rate': 92.8
93
94
  },
94
95
  'stripe': {
95
96
  'total': int(total_payments * 0.3),
96
97
  'successful': int(total_payments * 0.28),
97
98
  'failed': int(total_payments * 0.02),
99
+ 'pending': 0,
98
100
  'success_rate': 93.3
99
101
  }
100
102
  },
101
- 'events_by_type': {
102
- 'payment.created': int(total_payments * 1.0),
103
- 'payment.completed': int(total_payments * 0.8),
104
- 'payment.failed': int(total_payments * 0.2),
103
+ 'last_24h': {
104
+ 'total': recent_payments * 2,
105
+ 'successful': int(recent_payments * 1.8),
106
+ 'failed': int(recent_payments * 0.2),
105
107
  },
106
- 'recent_activity': [
107
- {
108
- 'timestamp': timezone.now() - timedelta(minutes=i*5),
109
- 'event_type': 'payment.created' if i % 3 == 0 else 'payment.completed',
110
- 'provider': 'nowpayments' if i % 2 == 0 else 'stripe',
111
- 'status': 'success' if i % 4 != 0 else 'failed'
112
- }
113
- for i in range(10)
114
- ]
108
+ 'avg_response_time': 150.5, # milliseconds
109
+ 'max_response_time': 2500, # milliseconds
115
110
  }
116
111
 
117
112
  serializer = self.get_serializer(stats_data)
@@ -140,7 +135,7 @@ class AdminWebhookEventViewSet(AdminReadOnlyViewSet):
140
135
  # In real implementation, this would return WebhookEvent.objects.all()
141
136
  return UniversalPayment.objects.none()
142
137
 
143
- def list(self, request):
138
+ def list(self, request, webhook_pk=None):
144
139
  """List webhook events with filtering and pagination."""
145
140
  # Get filter parameters
146
141
  event_type = request.query_params.get('event_type')