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.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/accounts/admin/__init__.py +24 -8
- django_cfg/apps/accounts/admin/activity_admin.py +146 -0
- django_cfg/apps/accounts/admin/filters.py +98 -22
- django_cfg/apps/accounts/admin/group_admin.py +86 -0
- django_cfg/apps/accounts/admin/inlines.py +42 -13
- django_cfg/apps/accounts/admin/otp_admin.py +115 -0
- django_cfg/apps/accounts/admin/registration_admin.py +173 -0
- django_cfg/apps/accounts/admin/resources.py +123 -19
- django_cfg/apps/accounts/admin/twilio_admin.py +327 -0
- django_cfg/apps/accounts/admin/user_admin.py +362 -0
- django_cfg/apps/agents/admin/__init__.py +17 -4
- django_cfg/apps/agents/admin/execution_admin.py +204 -183
- django_cfg/apps/agents/admin/registry_admin.py +230 -255
- django_cfg/apps/agents/admin/toolsets_admin.py +274 -321
- django_cfg/apps/agents/core/__init__.py +1 -1
- django_cfg/apps/agents/core/django_agent.py +221 -0
- django_cfg/apps/agents/core/exceptions.py +14 -0
- django_cfg/apps/agents/core/orchestrator.py +18 -3
- django_cfg/apps/knowbase/admin/__init__.py +1 -1
- django_cfg/apps/knowbase/admin/archive_admin.py +352 -640
- django_cfg/apps/knowbase/admin/chat_admin.py +258 -192
- django_cfg/apps/knowbase/admin/document_admin.py +269 -262
- django_cfg/apps/knowbase/admin/external_data_admin.py +271 -489
- django_cfg/apps/knowbase/config/settings.py +21 -4
- django_cfg/apps/knowbase/views/chat_views.py +3 -0
- django_cfg/apps/leads/admin/__init__.py +3 -1
- django_cfg/apps/leads/admin/leads_admin.py +235 -35
- django_cfg/apps/maintenance/admin/__init__.py +2 -2
- django_cfg/apps/maintenance/admin/api_key_admin.py +125 -63
- django_cfg/apps/maintenance/admin/log_admin.py +143 -61
- django_cfg/apps/maintenance/admin/scheduled_admin.py +212 -301
- django_cfg/apps/maintenance/admin/site_admin.py +213 -352
- django_cfg/apps/newsletter/admin/__init__.py +29 -2
- django_cfg/apps/newsletter/admin/newsletter_admin.py +531 -193
- django_cfg/apps/payments/admin/__init__.py +18 -27
- django_cfg/apps/payments/admin/api_keys_admin.py +179 -546
- django_cfg/apps/payments/admin/balance_admin.py +166 -632
- django_cfg/apps/payments/admin/currencies_admin.py +235 -607
- django_cfg/apps/payments/admin/endpoint_groups_admin.py +127 -0
- django_cfg/apps/payments/admin/filters.py +83 -3
- django_cfg/apps/payments/admin/networks_admin.py +269 -0
- django_cfg/apps/payments/admin/payments_admin.py +183 -460
- django_cfg/apps/payments/admin/subscriptions_admin.py +119 -636
- django_cfg/apps/payments/admin/tariffs_admin.py +248 -0
- django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +153 -34
- django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +43 -17
- django_cfg/apps/payments/admin_interface/views/__init__.py +2 -0
- django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
- django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +109 -63
- django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
- django_cfg/apps/payments/config/__init__.py +14 -15
- django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
- django_cfg/apps/payments/config/helpers.py +8 -13
- django_cfg/apps/payments/management/commands/manage_currencies.py +236 -274
- django_cfg/apps/payments/management/commands/manage_providers.py +4 -1
- django_cfg/apps/payments/middleware/api_access.py +32 -6
- django_cfg/apps/payments/migrations/0001_initial.py +33 -46
- django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
- django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
- django_cfg/apps/payments/models/balance.py +12 -0
- django_cfg/apps/payments/models/currencies.py +106 -32
- django_cfg/apps/payments/models/managers/currency_managers.py +65 -0
- django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
- django_cfg/apps/payments/models/payments.py +94 -0
- django_cfg/apps/payments/services/core/base.py +4 -4
- django_cfg/apps/payments/services/core/currency_service.py +35 -28
- django_cfg/apps/payments/services/core/payment_service.py +266 -39
- django_cfg/apps/payments/services/providers/__init__.py +3 -0
- django_cfg/apps/payments/services/providers/base.py +303 -41
- django_cfg/apps/payments/services/providers/models/__init__.py +42 -0
- django_cfg/apps/payments/services/providers/models/base.py +145 -0
- django_cfg/apps/payments/services/providers/models/providers.py +87 -0
- django_cfg/apps/payments/services/providers/models/universal.py +48 -0
- django_cfg/apps/payments/services/providers/nowpayments/__init__.py +31 -0
- django_cfg/apps/payments/services/providers/nowpayments/config.py +70 -0
- django_cfg/apps/payments/services/providers/nowpayments/models.py +150 -0
- django_cfg/apps/payments/services/providers/nowpayments/parsers.py +879 -0
- django_cfg/apps/payments/services/providers/nowpayments/provider.py +557 -0
- django_cfg/apps/payments/services/providers/nowpayments/sync.py +196 -0
- django_cfg/apps/payments/services/providers/registry.py +9 -37
- django_cfg/apps/payments/services/providers/sync_service.py +277 -0
- django_cfg/apps/payments/services/types/requests.py +19 -7
- django_cfg/apps/payments/signals/payment_signals.py +31 -2
- django_cfg/apps/payments/static/payments/js/api-client.js +29 -6
- django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
- django_cfg/apps/payments/static/payments/js/payment-form.js +98 -32
- django_cfg/apps/payments/tasks/__init__.py +39 -0
- django_cfg/apps/payments/tasks/types.py +73 -0
- django_cfg/apps/payments/tasks/usage_tracking.py +308 -0
- django_cfg/apps/payments/templates/admin/payments/_components/dashboard_header.html +23 -0
- django_cfg/apps/payments/templates/admin/payments/_components/stats_card.html +25 -0
- django_cfg/apps/payments/templates/admin/payments/_components/stats_grid.html +16 -0
- django_cfg/apps/payments/templates/admin/payments/apikey/change_list.html +39 -0
- django_cfg/apps/payments/templates/admin/payments/balance/change_list.html +50 -0
- django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +40 -0
- django_cfg/apps/payments/templates/admin/payments/payment/change_list.html +48 -0
- django_cfg/apps/payments/templates/admin/payments/subscription/change_list.html +48 -0
- django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
- django_cfg/apps/payments/urls.py +3 -2
- django_cfg/apps/payments/urls_admin.py +1 -1
- django_cfg/apps/payments/views/api/currencies.py +8 -5
- django_cfg/apps/payments/views/overview/services.py +2 -2
- django_cfg/apps/payments/views/serializers/currencies.py +22 -8
- django_cfg/apps/support/admin/__init__.py +10 -1
- django_cfg/apps/support/admin/support_admin.py +338 -141
- django_cfg/apps/tasks/admin/__init__.py +11 -0
- django_cfg/apps/tasks/admin/tasks_admin.py +430 -0
- django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
- django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
- django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
- django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
- django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
- django_cfg/apps/tasks/tasks/__init__.py +10 -0
- django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
- django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
- django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
- django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
- django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
- django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
- django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
- django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
- django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
- django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
- django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
- django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
- django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
- django_cfg/apps/tasks/urls.py +2 -2
- django_cfg/apps/tasks/urls_admin.py +2 -2
- django_cfg/apps/tasks/utils/__init__.py +1 -0
- django_cfg/apps/tasks/utils/simulator.py +356 -0
- django_cfg/apps/tasks/views/__init__.py +16 -0
- django_cfg/apps/tasks/views/api.py +569 -0
- django_cfg/apps/tasks/views/dashboard.py +58 -0
- django_cfg/config.py +1 -1
- django_cfg/core/config.py +10 -5
- django_cfg/core/generation.py +1 -1
- django_cfg/core/integration/__init__.py +21 -0
- django_cfg/management/commands/__init__.py +13 -1
- django_cfg/management/commands/migrate_all.py +9 -3
- django_cfg/management/commands/migrator.py +11 -6
- django_cfg/management/commands/rundramatiq.py +3 -2
- django_cfg/management/commands/rundramatiq_simulator.py +430 -0
- django_cfg/middleware/__init__.py +0 -2
- django_cfg/models/api_keys.py +115 -0
- django_cfg/models/constance.py +0 -11
- django_cfg/models/payments.py +137 -3
- django_cfg/modules/django_admin/__init__.py +64 -0
- django_cfg/modules/django_admin/decorators/__init__.py +13 -0
- django_cfg/modules/django_admin/decorators/actions.py +106 -0
- django_cfg/modules/django_admin/decorators/display.py +106 -0
- django_cfg/modules/django_admin/mixins/__init__.py +14 -0
- django_cfg/modules/django_admin/mixins/display_mixin.py +81 -0
- django_cfg/modules/django_admin/mixins/optimization_mixin.py +41 -0
- django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +202 -0
- django_cfg/modules/django_admin/models/__init__.py +20 -0
- django_cfg/modules/django_admin/models/action_models.py +33 -0
- django_cfg/modules/django_admin/models/badge_models.py +20 -0
- django_cfg/modules/django_admin/models/base.py +26 -0
- django_cfg/modules/django_admin/models/display_models.py +31 -0
- django_cfg/modules/django_admin/utils/badges.py +159 -0
- django_cfg/modules/django_admin/utils/displays.py +247 -0
- django_cfg/modules/django_currency/__init__.py +2 -2
- django_cfg/modules/django_currency/clients/__init__.py +2 -2
- django_cfg/modules/django_currency/clients/hybrid_client.py +587 -0
- django_cfg/modules/django_currency/core/converter.py +12 -12
- django_cfg/modules/django_currency/database/__init__.py +2 -2
- django_cfg/modules/django_currency/database/database_loader.py +93 -42
- django_cfg/modules/django_llm/llm/client.py +10 -2
- django_cfg/modules/django_tasks.py +54 -21
- django_cfg/modules/django_unfold/callbacks/actions.py +1 -1
- django_cfg/modules/django_unfold/callbacks/statistics.py +1 -1
- django_cfg/modules/django_unfold/dashboard.py +14 -13
- django_cfg/modules/django_unfold/models/config.py +1 -1
- django_cfg/registry/core.py +7 -9
- django_cfg/registry/third_party.py +2 -2
- django_cfg/template_archive/django_sample.zip +0 -0
- {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/METADATA +2 -1
- {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/RECORD +198 -160
- django_cfg/apps/accounts/admin/activity.py +0 -96
- django_cfg/apps/accounts/admin/group.py +0 -17
- django_cfg/apps/accounts/admin/otp.py +0 -59
- django_cfg/apps/accounts/admin/registration_source.py +0 -97
- django_cfg/apps/accounts/admin/twilio_response.py +0 -227
- django_cfg/apps/accounts/admin/user.py +0 -300
- django_cfg/apps/agents/core/agent.py +0 -281
- django_cfg/apps/payments/admin_interface/old/payments/base.html +0 -175
- django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +0 -125
- django_cfg/apps/payments/admin_interface/old/payments/components/loading_spinner.html +0 -16
- django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +0 -113
- django_cfg/apps/payments/admin_interface/old/payments/components/notification.html +0 -27
- django_cfg/apps/payments/admin_interface/old/payments/components/provider_card.html +0 -86
- django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +0 -35
- django_cfg/apps/payments/admin_interface/old/payments/currency_converter.html +0 -382
- django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +0 -309
- django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +0 -303
- django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +0 -382
- django_cfg/apps/payments/admin_interface/old/payments/payment_status.html +0 -500
- django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +0 -518
- django_cfg/apps/payments/admin_interface/old/static/payments/css/components.css +0 -619
- django_cfg/apps/payments/admin_interface/old/static/payments/css/dashboard.css +0 -188
- django_cfg/apps/payments/admin_interface/old/static/payments/js/components.js +0 -545
- django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +0 -163
- django_cfg/apps/payments/admin_interface/old/static/payments/js/utils.js +0 -412
- django_cfg/apps/payments/config/constance/__init__.py +0 -22
- django_cfg/apps/payments/config/constance/config_service.py +0 -123
- django_cfg/apps/payments/config/constance/fields.py +0 -69
- django_cfg/apps/payments/config/constance/settings.py +0 -160
- django_cfg/apps/payments/services/providers/nowpayments.py +0 -478
- django_cfg/apps/tasks/admin.py +0 -320
- django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
- django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
- django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
- django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
- django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
- django_cfg/apps/tasks/templates/tasks/base.html +0 -96
- django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
- django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
- django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
- django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
- django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
- django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
- django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
- django_cfg/apps/tasks/views.py +0 -461
- django_cfg/management/commands/auto_generate.py +0 -486
- django_cfg/middleware/static_nocache.py +0 -55
- django_cfg/modules/django_currency/clients/yahoo_client.py +0 -157
- /django_cfg/modules/{django_unfold → django_admin}/icons/README.md +0 -0
- /django_cfg/modules/{django_unfold → django_admin}/icons/__init__.py +0 -0
- /django_cfg/modules/{django_unfold → django_admin}/icons/constants.py +0 -0
- /django_cfg/modules/{django_unfold → django_admin}/icons/generate_icons.py +0 -0
- {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/WHEEL +0 -0
- {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/entry_points.txt +0 -0
- {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.
|
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
|
-
|
30
|
-
|
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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
66
|
+
from django.contrib.humanize.templatetags.humanize import naturaltime
|
67
|
+
return naturaltime(obj.created_at)
|
45
68
|
|
46
69
|
|
47
|
-
class AdminPaymentDetailSerializer(serializers.
|
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
|
-
|
53
|
-
|
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
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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.
|
123
|
+
return naturaltime(obj.created_at)
|
71
124
|
|
72
125
|
|
73
|
-
class AdminPaymentCreateSerializer(serializers.
|
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
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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>
|