django-cfg 1.3.5__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.
- 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 +258 -0
- django_cfg/apps/payments/admin/payments_admin.py +171 -461
- 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 +105 -34
- django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +12 -16
- django_cfg/apps/payments/admin_interface/views/__init__.py +2 -0
- django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +13 -18
- 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/0002_currency_usd_rate_currency_usd_rate_updated_at.py +26 -0
- django_cfg/apps/payments/migrations/0003_remove_provider_currency_fields.py +28 -0
- django_cfg/apps/payments/migrations/0004_add_reserved_usd_field.py +30 -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/services/core/currency_service.py +35 -28
- django_cfg/apps/payments/services/core/payment_service.py +1 -1
- django_cfg/apps/payments/services/providers/__init__.py +3 -0
- django_cfg/apps/payments/services/providers/base.py +95 -39
- django_cfg/apps/payments/services/providers/models/__init__.py +40 -0
- django_cfg/apps/payments/services/providers/models/base.py +122 -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.py â nowpayments/provider.py} +240 -209
- django_cfg/apps/payments/services/providers/nowpayments/sync.py +196 -0
- django_cfg/apps/payments/services/providers/registry.py +4 -32
- django_cfg/apps/payments/services/providers/sync_service.py +277 -0
- django_cfg/apps/payments/static/payments/js/api-client.js +23 -5
- django_cfg/apps/payments/static/payments/js/payment-form.js +65 -8
- 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/urls_admin.py +1 -1
- django_cfg/apps/payments/views/api/currencies.py +5 -5
- django_cfg/apps/payments/views/overview/services.py +2 -2
- django_cfg/apps/payments/views/serializers/currencies.py +4 -3
- 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/urls.py +1 -2
- django_cfg/config.py +1 -1
- django_cfg/core/config.py +10 -5
- django_cfg/core/generation.py +1 -1
- django_cfg/management/commands/__init__.py +13 -1
- django_cfg/management/commands/app_agent_diagnose.py +470 -0
- django_cfg/management/commands/app_agent_generate.py +342 -0
- django_cfg/management/commands/app_agent_info.py +308 -0
- 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/middleware/__init__.py +0 -2
- django_cfg/models/api_keys.py +115 -0
- 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_app_agent/__init__.py +87 -0
- django_cfg/modules/django_app_agent/agents/__init__.py +40 -0
- django_cfg/modules/django_app_agent/agents/base/__init__.py +24 -0
- django_cfg/modules/django_app_agent/agents/base/agent.py +354 -0
- django_cfg/modules/django_app_agent/agents/base/context.py +236 -0
- django_cfg/modules/django_app_agent/agents/base/executor.py +430 -0
- django_cfg/modules/django_app_agent/agents/generation/__init__.py +12 -0
- django_cfg/modules/django_app_agent/agents/generation/app_generator/__init__.py +15 -0
- django_cfg/modules/django_app_agent/agents/generation/app_generator/config_validator.py +147 -0
- django_cfg/modules/django_app_agent/agents/generation/app_generator/main.py +99 -0
- django_cfg/modules/django_app_agent/agents/generation/app_generator/models.py +32 -0
- django_cfg/modules/django_app_agent/agents/generation/app_generator/prompt_manager.py +290 -0
- django_cfg/modules/django_app_agent/agents/interfaces.py +376 -0
- django_cfg/modules/django_app_agent/core/__init__.py +33 -0
- django_cfg/modules/django_app_agent/core/config.py +300 -0
- django_cfg/modules/django_app_agent/core/exceptions.py +359 -0
- django_cfg/modules/django_app_agent/models/__init__.py +71 -0
- django_cfg/modules/django_app_agent/models/base.py +283 -0
- django_cfg/modules/django_app_agent/models/context.py +496 -0
- django_cfg/modules/django_app_agent/models/enums.py +481 -0
- django_cfg/modules/django_app_agent/models/requests.py +500 -0
- django_cfg/modules/django_app_agent/models/responses.py +585 -0
- django_cfg/modules/django_app_agent/pytest.ini +6 -0
- django_cfg/modules/django_app_agent/services/__init__.py +42 -0
- django_cfg/modules/django_app_agent/services/app_generator/__init__.py +30 -0
- django_cfg/modules/django_app_agent/services/app_generator/ai_integration.py +133 -0
- django_cfg/modules/django_app_agent/services/app_generator/context.py +40 -0
- django_cfg/modules/django_app_agent/services/app_generator/main.py +202 -0
- django_cfg/modules/django_app_agent/services/app_generator/structure.py +316 -0
- django_cfg/modules/django_app_agent/services/app_generator/validation.py +125 -0
- django_cfg/modules/django_app_agent/services/base.py +437 -0
- django_cfg/modules/django_app_agent/services/context_builder/__init__.py +34 -0
- django_cfg/modules/django_app_agent/services/context_builder/code_extractor.py +141 -0
- django_cfg/modules/django_app_agent/services/context_builder/context_generator.py +276 -0
- django_cfg/modules/django_app_agent/services/context_builder/main.py +272 -0
- django_cfg/modules/django_app_agent/services/context_builder/models.py +40 -0
- django_cfg/modules/django_app_agent/services/context_builder/pattern_analyzer.py +85 -0
- django_cfg/modules/django_app_agent/services/project_scanner/__init__.py +31 -0
- django_cfg/modules/django_app_agent/services/project_scanner/app_discovery.py +311 -0
- django_cfg/modules/django_app_agent/services/project_scanner/main.py +221 -0
- django_cfg/modules/django_app_agent/services/project_scanner/models.py +59 -0
- django_cfg/modules/django_app_agent/services/project_scanner/pattern_detection.py +94 -0
- django_cfg/modules/django_app_agent/services/questioning_service/__init__.py +28 -0
- django_cfg/modules/django_app_agent/services/questioning_service/main.py +273 -0
- django_cfg/modules/django_app_agent/services/questioning_service/models.py +111 -0
- django_cfg/modules/django_app_agent/services/questioning_service/question_generator.py +251 -0
- django_cfg/modules/django_app_agent/services/questioning_service/response_processor.py +347 -0
- django_cfg/modules/django_app_agent/services/questioning_service/session_manager.py +356 -0
- django_cfg/modules/django_app_agent/services/report_service.py +332 -0
- django_cfg/modules/django_app_agent/services/template_manager/__init__.py +18 -0
- django_cfg/modules/django_app_agent/services/template_manager/jinja_engine.py +236 -0
- django_cfg/modules/django_app_agent/services/template_manager/main.py +159 -0
- django_cfg/modules/django_app_agent/services/template_manager/models.py +36 -0
- django_cfg/modules/django_app_agent/services/template_manager/template_loader.py +100 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/admin.py.j2 +105 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/apps.py.j2 +31 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_config.py.j2 +44 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_module.py.j2 +81 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/forms.py.j2 +107 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/models.py.j2 +139 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/serializers.py.j2 +91 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/tests.py.j2 +195 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/urls.py.j2 +35 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/views.py.j2 +211 -0
- django_cfg/modules/django_app_agent/services/template_manager/variable_processor.py +200 -0
- django_cfg/modules/django_app_agent/services/validation_service/__init__.py +25 -0
- django_cfg/modules/django_app_agent/services/validation_service/django_validator.py +333 -0
- django_cfg/modules/django_app_agent/services/validation_service/main.py +242 -0
- django_cfg/modules/django_app_agent/services/validation_service/models.py +66 -0
- django_cfg/modules/django_app_agent/services/validation_service/quality_validator.py +352 -0
- django_cfg/modules/django_app_agent/services/validation_service/security_validator.py +272 -0
- django_cfg/modules/django_app_agent/services/validation_service/syntax_validator.py +203 -0
- django_cfg/modules/django_app_agent/ui/__init__.py +25 -0
- django_cfg/modules/django_app_agent/ui/cli.py +419 -0
- django_cfg/modules/django_app_agent/ui/rich_components.py +622 -0
- django_cfg/modules/django_app_agent/utils/__init__.py +38 -0
- django_cfg/modules/django_app_agent/utils/logging.py +360 -0
- django_cfg/modules/django_app_agent/utils/validation.py +417 -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_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 +3 -0
- django_cfg/registry/third_party.py +2 -2
- django_cfg/template_archive/django_sample.zip +0 -0
- {django_cfg-1.3.5.dist-info â django_cfg-1.3.9.dist-info}/METADATA +2 -1
- {django_cfg-1.3.5.dist-info â django_cfg-1.3.9.dist-info}/RECORD +224 -118
- 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/tasks/admin.py +0 -320
- 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.5.dist-info â django_cfg-1.3.9.dist-info}/WHEEL +0 -0
- {django_cfg-1.3.5.dist-info â django_cfg-1.3.9.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.3.5.dist-info â django_cfg-1.3.9.dist-info}/licenses/LICENSE +0 -0
@@ -1,548 +1,258 @@
|
|
1
1
|
"""
|
2
|
-
Payment Admin interface
|
2
|
+
Payment Admin interface using Django Admin Utilities.
|
3
3
|
|
4
|
-
|
4
|
+
Clean, modern payment management with no HTML duplication.
|
5
5
|
"""
|
6
6
|
|
7
7
|
from django.contrib import admin
|
8
|
-
from django.utils.html import format_html
|
9
|
-
from django.contrib.humanize.templatetags.humanize import naturaltime, intcomma
|
10
|
-
from django.contrib import messages
|
11
|
-
from django.shortcuts import redirect
|
12
|
-
from django.utils.safestring import mark_safe
|
13
8
|
from django.db.models import Count, Sum, Q
|
14
9
|
from django.utils import timezone
|
15
10
|
from datetime import timedelta
|
16
|
-
from typing import Optional
|
17
11
|
|
18
12
|
from unfold.admin import ModelAdmin
|
19
|
-
from unfold.decorators import display, action
|
20
|
-
from unfold.enums import ActionVariant
|
21
13
|
|
22
|
-
from
|
23
|
-
|
14
|
+
from django_cfg.modules.django_admin import (
|
15
|
+
OptimizedModelAdmin,
|
16
|
+
DisplayMixin,
|
17
|
+
UserDisplayConfig,
|
18
|
+
MoneyDisplayConfig,
|
19
|
+
StatusBadgeConfig,
|
20
|
+
DateTimeDisplayConfig,
|
21
|
+
Icons,
|
22
|
+
display,
|
23
|
+
action,
|
24
|
+
ActionVariant
|
25
|
+
)
|
26
|
+
from django_cfg.modules.django_admin.utils.badges import StatusBadge
|
24
27
|
from django_cfg.modules.django_logger import get_logger
|
25
28
|
|
29
|
+
from ..models import UniversalPayment
|
30
|
+
|
26
31
|
logger = get_logger("payments_admin")
|
27
32
|
|
28
33
|
|
29
34
|
@admin.register(UniversalPayment)
|
30
|
-
class UniversalPaymentAdmin(ModelAdmin):
|
35
|
+
class UniversalPaymentAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin):
|
31
36
|
"""
|
32
|
-
|
37
|
+
UniversalPayment admin using Django Admin Utilities.
|
33
38
|
|
34
39
|
Features:
|
35
|
-
-
|
36
|
-
-
|
37
|
-
-
|
38
|
-
-
|
39
|
-
- Financial statistics and monitoring
|
40
|
+
- Clean display utilities with no HTML duplication
|
41
|
+
- Automatic query optimization
|
42
|
+
- Type-safe configuration
|
43
|
+
- Payment-specific status mapping
|
40
44
|
"""
|
41
45
|
|
42
|
-
#
|
43
|
-
|
46
|
+
# Performance optimization
|
47
|
+
select_related_fields = ['user', 'currency']
|
48
|
+
annotations = {}
|
49
|
+
# Note: Annotations should use Django expressions like F(), Case(), etc.
|
50
|
+
# Example: 'age_days': timezone.now() - F('created_at')
|
44
51
|
|
52
|
+
# List configuration
|
45
53
|
list_display = [
|
46
54
|
'payment_id_display',
|
47
55
|
'user_display',
|
48
56
|
'amount_display',
|
49
57
|
'status_display',
|
50
58
|
'provider_display',
|
51
|
-
'
|
52
|
-
'progress_display',
|
53
|
-
'created_at_display'
|
54
|
-
]
|
55
|
-
|
56
|
-
list_display_links = ['payment_id_display']
|
57
|
-
|
58
|
-
search_fields = [
|
59
|
-
'id',
|
60
|
-
'provider_payment_id',
|
61
|
-
'user__email',
|
62
|
-
'user__first_name',
|
63
|
-
'user__last_name',
|
64
|
-
'user__username',
|
65
|
-
'description'
|
59
|
+
'created_display'
|
66
60
|
]
|
67
61
|
|
68
62
|
list_filter = [
|
69
|
-
|
70
|
-
PaymentAmountFilter,
|
71
|
-
UserEmailFilter,
|
72
|
-
RecentActivityFilter,
|
63
|
+
'status',
|
73
64
|
'provider',
|
74
65
|
'currency',
|
75
66
|
'created_at'
|
76
67
|
]
|
77
68
|
|
69
|
+
search_fields = [
|
70
|
+
'internal_payment_id',
|
71
|
+
'transaction_hash',
|
72
|
+
'user__username',
|
73
|
+
'user__email',
|
74
|
+
'pay_address'
|
75
|
+
]
|
76
|
+
|
78
77
|
readonly_fields = [
|
79
|
-
'
|
80
|
-
'provider_payment_id',
|
81
|
-
'payment_url',
|
78
|
+
'internal_payment_id',
|
82
79
|
'created_at',
|
83
80
|
'updated_at',
|
84
|
-
'
|
85
|
-
]
|
86
|
-
|
87
|
-
# Unfold actions
|
88
|
-
actions_list = [
|
89
|
-
'check_payment_status',
|
90
|
-
'cancel_selected_payments',
|
91
|
-
'mark_as_completed',
|
92
|
-
'export_payment_data'
|
93
|
-
]
|
94
|
-
|
95
|
-
fieldsets = [
|
96
|
-
('Payment Information', {
|
97
|
-
'fields': [
|
98
|
-
'id',
|
99
|
-
'user',
|
100
|
-
'amount_usd',
|
101
|
-
'currency',
|
102
|
-
'crypto_amount',
|
103
|
-
'description'
|
104
|
-
]
|
105
|
-
}),
|
106
|
-
('Provider Details', {
|
107
|
-
'fields': [
|
108
|
-
'provider',
|
109
|
-
'provider_payment_id',
|
110
|
-
'payment_url'
|
111
|
-
]
|
112
|
-
}),
|
113
|
-
('Status & Tracking', {
|
114
|
-
'fields': [
|
115
|
-
'status',
|
116
|
-
'error_code',
|
117
|
-
'error_message',
|
118
|
-
'expires_at'
|
119
|
-
]
|
120
|
-
}),
|
121
|
-
('URLs & Callbacks', {
|
122
|
-
'fields': [
|
123
|
-
'callback_url',
|
124
|
-
'cancel_url'
|
125
|
-
],
|
126
|
-
'classes': ['collapse']
|
127
|
-
}),
|
128
|
-
('Metadata', {
|
129
|
-
'fields': [
|
130
|
-
'metadata'
|
131
|
-
],
|
132
|
-
'classes': ['collapse']
|
133
|
-
}),
|
134
|
-
('Timestamps', {
|
135
|
-
'fields': [
|
136
|
-
'created_at',
|
137
|
-
'updated_at',
|
138
|
-
'completed_at'
|
139
|
-
],
|
140
|
-
'classes': ['collapse']
|
141
|
-
})
|
81
|
+
'payment_details_display'
|
142
82
|
]
|
143
83
|
|
144
|
-
|
145
|
-
|
146
|
-
return super().get_queryset(request).select_related('user')
|
84
|
+
# Register actions
|
85
|
+
actions = ['mark_as_completed', 'mark_as_failed', 'cancel_payments']
|
147
86
|
|
148
|
-
|
87
|
+
# Display methods using utilities
|
88
|
+
@display(description="Payment ID")
|
149
89
|
def payment_id_display(self, obj):
|
150
|
-
"""
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
'title="Click to copy full ID: {}">{}</span>',
|
155
|
-
obj.id,
|
156
|
-
short_id
|
90
|
+
"""Payment ID display with badge."""
|
91
|
+
return StatusBadge.create(
|
92
|
+
text=obj.internal_payment_id[:12] + "...",
|
93
|
+
variant="info"
|
157
94
|
)
|
158
95
|
|
159
|
-
@display(description="User",
|
96
|
+
@display(description="User", header=True)
|
160
97
|
def user_display(self, obj):
|
161
|
-
"""
|
162
|
-
|
163
|
-
display_name = obj.user.get_full_name() or obj.user.username
|
164
|
-
return format_html(
|
165
|
-
'<div class="flex items-center space-x-2">'
|
166
|
-
'<div class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center text-white text-xs font-bold">'
|
167
|
-
'{}'
|
168
|
-
'</div>'
|
169
|
-
'<div>'
|
170
|
-
'<div class="font-medium text-gray-900 dark:text-gray-100">{}</div>'
|
171
|
-
'<div class="text-xs text-gray-500">{}</div>'
|
172
|
-
'</div>'
|
173
|
-
'</div>',
|
174
|
-
display_name[0].upper() if display_name else 'U',
|
175
|
-
display_name,
|
176
|
-
obj.user.email
|
177
|
-
)
|
178
|
-
return format_html('<span class="text-gray-500">No user</span>')
|
98
|
+
"""User display with avatar."""
|
99
|
+
return self.display_user_with_avatar(obj, 'user')
|
179
100
|
|
180
|
-
@display(description="Amount"
|
101
|
+
@display(description="Amount")
|
181
102
|
def amount_display(self, obj):
|
182
|
-
"""
|
183
|
-
|
184
|
-
|
185
|
-
if obj.
|
186
|
-
|
187
|
-
return format_html(
|
188
|
-
'<div class="text-right">'
|
189
|
-
'<div class="font-bold text-green-600 dark:text-green-400">{}</div>'
|
190
|
-
'<div class="text-xs text-gray-500">{} {}</div>'
|
191
|
-
'</div>',
|
192
|
-
usd_amount,
|
193
|
-
crypto_display,
|
194
|
-
obj.currency.code
|
195
|
-
)
|
103
|
+
"""Amount display with currency."""
|
104
|
+
# Get currency code from currency relation or default to USD
|
105
|
+
currency_code = "USD"
|
106
|
+
if obj.currency:
|
107
|
+
currency_code = getattr(obj.currency, 'code', 'USD')
|
196
108
|
|
197
|
-
|
198
|
-
|
199
|
-
|
109
|
+
config = MoneyDisplayConfig(
|
110
|
+
currency=currency_code,
|
111
|
+
show_sign=False,
|
112
|
+
thousand_separator=True
|
200
113
|
)
|
114
|
+
|
115
|
+
return self.display_money_amount(obj, 'amount_usd', config)
|
201
116
|
|
202
|
-
@display(description="Status",
|
117
|
+
@display(description="Status", label=True)
|
203
118
|
def status_display(self, obj):
|
204
|
-
"""
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
119
|
+
"""Status display with payment-specific colors."""
|
120
|
+
# Payment-specific status mappings
|
121
|
+
payment_status_mappings = {
|
122
|
+
'pending': 'warning',
|
123
|
+
'processing': 'info',
|
124
|
+
'completed': 'success',
|
125
|
+
'failed': 'danger',
|
126
|
+
'cancelled': 'secondary',
|
127
|
+
'expired': 'danger',
|
128
|
+
'refunded': 'warning'
|
214
129
|
}
|
215
130
|
|
216
|
-
|
217
|
-
|
218
|
-
|
131
|
+
config = StatusBadgeConfig(
|
132
|
+
custom_mappings=payment_status_mappings,
|
133
|
+
show_icons=True
|
219
134
|
)
|
220
135
|
|
221
|
-
return
|
222
|
-
'<span class="inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium {}">'
|
223
|
-
'{} {}'
|
224
|
-
'</span>',
|
225
|
-
color_class,
|
226
|
-
icon,
|
227
|
-
label
|
228
|
-
)
|
136
|
+
return self.display_status_auto(obj, 'status', config)
|
229
137
|
|
230
|
-
@display(description="Provider"
|
138
|
+
@display(description="Provider")
|
231
139
|
def provider_display(self, obj):
|
232
|
-
"""
|
140
|
+
"""Provider display with badge and icons."""
|
141
|
+
# Provider-specific styling and icons
|
233
142
|
provider_config = {
|
234
|
-
'
|
235
|
-
'
|
236
|
-
'
|
143
|
+
'stripe': {'variant': 'primary', 'icon': Icons.CREDIT_CARD},
|
144
|
+
'paypal': {'variant': 'info', 'icon': Icons.ACCOUNT_BALANCE_WALLET},
|
145
|
+
'crypto': {'variant': 'warning', 'icon': Icons.CURRENCY_BITCOIN},
|
146
|
+
'bank': {'variant': 'success', 'icon': Icons.ACCOUNT_BALANCE},
|
237
147
|
}
|
238
148
|
|
239
|
-
|
149
|
+
config = provider_config.get(obj.provider.lower(), {'variant': 'secondary', 'icon': Icons.PAYMENT})
|
240
150
|
|
241
|
-
|
242
|
-
|
243
|
-
'
|
244
|
-
'<span class="text-sm font-medium">{}</span>'
|
245
|
-
'</span>',
|
246
|
-
icon,
|
247
|
-
name
|
151
|
+
badge_config = StatusBadgeConfig(
|
152
|
+
show_icons=True,
|
153
|
+
icon=config['icon']
|
248
154
|
)
|
249
|
-
|
250
|
-
@display(description="Currency", ordering='currency__code')
|
251
|
-
def currency_display(self, obj):
|
252
|
-
"""Display currency with type indicator."""
|
253
|
-
if obj.currency:
|
254
|
-
# Use currency type from model
|
255
|
-
is_crypto = obj.currency.currency_type == 'crypto'
|
256
|
-
|
257
|
-
icon = 'âŋ' if is_crypto else 'đ°'
|
258
|
-
|
259
|
-
return format_html(
|
260
|
-
'<span class="inline-flex items-center space-x-1">'
|
261
|
-
'<span>{}</span>'
|
262
|
-
'<span class="font-mono font-bold">{}</span>'
|
263
|
-
'</span>',
|
264
|
-
icon,
|
265
|
-
obj.currency.code
|
266
|
-
)
|
267
|
-
|
268
|
-
return format_html('<span class="text-gray-500">â</span>')
|
269
|
-
|
270
|
-
@display(description="Progress")
|
271
|
-
def progress_display(self, obj):
|
272
|
-
"""Display payment progress with time information."""
|
273
|
-
now = timezone.now()
|
274
|
-
|
275
|
-
# Calculate time since creation
|
276
|
-
time_elapsed = now - obj.created_at
|
277
155
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
'â Expired<br>'
|
283
|
-
'<span class="text-gray-400">{}</span>'
|
284
|
-
'</div>',
|
285
|
-
naturaltime(obj.expires_at)
|
286
|
-
)
|
287
|
-
|
288
|
-
# Show time remaining if has expiry
|
289
|
-
if obj.expires_at:
|
290
|
-
time_remaining = obj.expires_at - now
|
291
|
-
if time_remaining.total_seconds() > 0:
|
292
|
-
return format_html(
|
293
|
-
'<div class="text-orange-500 text-xs">'
|
294
|
-
'â° {} left<br>'
|
295
|
-
'<span class="text-gray-400">Created {}</span>'
|
296
|
-
'</div>',
|
297
|
-
naturaltime(now + time_remaining),
|
298
|
-
naturaltime(obj.created_at)
|
299
|
-
)
|
300
|
-
|
301
|
-
# Default: show creation time
|
302
|
-
return format_html(
|
303
|
-
'<div class="text-gray-500 text-xs">'
|
304
|
-
'Created<br>'
|
305
|
-
'<span>{}</span>'
|
306
|
-
'</div>',
|
307
|
-
naturaltime(obj.created_at)
|
156
|
+
return StatusBadge.create(
|
157
|
+
text=obj.provider.title(),
|
158
|
+
variant=config['variant'],
|
159
|
+
config=badge_config
|
308
160
|
)
|
309
161
|
|
310
|
-
@display(description="Created"
|
311
|
-
def
|
312
|
-
"""
|
313
|
-
return
|
314
|
-
|
315
|
-
'
|
316
|
-
|
317
|
-
'</div>',
|
318
|
-
obj.created_at.strftime('%Y-%m-%d %H:%M'),
|
319
|
-
naturaltime(obj.created_at)
|
162
|
+
@display(description="Created")
|
163
|
+
def created_display(self, obj):
|
164
|
+
"""Created time display."""
|
165
|
+
return self.display_datetime_relative(
|
166
|
+
obj,
|
167
|
+
'created_at',
|
168
|
+
DateTimeDisplayConfig(show_relative=True, show_seconds=False)
|
320
169
|
)
|
321
170
|
|
322
|
-
|
323
|
-
|
324
|
-
|
171
|
+
# Readonly field displays
|
172
|
+
def payment_details_display(self, obj):
|
173
|
+
"""Detailed payment information for detail view."""
|
174
|
+
if not obj.pk:
|
175
|
+
return "Save to see details"
|
325
176
|
|
326
|
-
|
327
|
-
|
328
|
-
total_payments = UniversalPayment.objects.count()
|
329
|
-
|
330
|
-
# Status distribution
|
331
|
-
status_stats = {}
|
332
|
-
for status in UniversalPayment.PaymentStatus:
|
333
|
-
count = UniversalPayment.objects.filter(status=status).count()
|
334
|
-
status_stats[status] = count
|
335
|
-
|
336
|
-
# Financial statistics
|
337
|
-
total_amount = UniversalPayment.objects.aggregate(
|
338
|
-
total=Sum('amount_usd')
|
339
|
-
)['total'] or 0
|
340
|
-
|
341
|
-
completed_amount = UniversalPayment.objects.filter(
|
342
|
-
status=UniversalPayment.PaymentStatus.COMPLETED
|
343
|
-
).aggregate(total=Sum('amount_usd'))['total'] or 0
|
344
|
-
|
345
|
-
# Recent activity (24 hours)
|
346
|
-
recent_threshold = timezone.now() - timedelta(hours=24)
|
347
|
-
recent_payments = UniversalPayment.objects.filter(
|
348
|
-
created_at__gte=recent_threshold
|
349
|
-
).count()
|
350
|
-
|
351
|
-
recent_amount = UniversalPayment.objects.filter(
|
352
|
-
created_at__gte=recent_threshold
|
353
|
-
).aggregate(total=Sum('amount_usd'))['total'] or 0
|
354
|
-
|
355
|
-
# Provider statistics
|
356
|
-
provider_stats = UniversalPayment.objects.values('provider').annotate(
|
357
|
-
count=Count('id'),
|
358
|
-
amount=Sum('amount_usd')
|
359
|
-
).order_by('-count')
|
360
|
-
|
361
|
-
# Success rate
|
362
|
-
completed_count = status_stats.get(UniversalPayment.PaymentStatus.COMPLETED, 0)
|
363
|
-
success_rate = (completed_count / total_payments * 100) if total_payments > 0 else 0
|
364
|
-
|
365
|
-
extra_context.update({
|
366
|
-
'payment_stats': {
|
367
|
-
'total_payments': total_payments,
|
368
|
-
'status_stats': status_stats,
|
369
|
-
'total_amount': total_amount,
|
370
|
-
'completed_amount': completed_amount,
|
371
|
-
'recent_payments': recent_payments,
|
372
|
-
'recent_amount': recent_amount,
|
373
|
-
'provider_stats': provider_stats,
|
374
|
-
'success_rate': success_rate,
|
375
|
-
}
|
376
|
-
})
|
377
|
-
|
378
|
-
except Exception as e:
|
379
|
-
logger.warning(f"Failed to generate payment statistics: {e}")
|
380
|
-
extra_context['payment_stats'] = None
|
177
|
+
from django.utils.html import format_html
|
178
|
+
from django_cfg.modules.django_admin.utils.displays import MoneyDisplay
|
381
179
|
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
@action(
|
387
|
-
description="đ Check Payment Status",
|
388
|
-
icon="refresh",
|
389
|
-
variant=ActionVariant.INFO
|
390
|
-
)
|
391
|
-
def check_payment_status(self, request, queryset):
|
392
|
-
"""Check payment status with providers."""
|
180
|
+
# Calculate age
|
181
|
+
age = timezone.now() - obj.created_at
|
182
|
+
age_text = f"{age.days} days, {age.seconds // 3600} hours"
|
393
183
|
|
394
|
-
|
395
|
-
|
184
|
+
# Build details HTML
|
185
|
+
details = []
|
396
186
|
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
from ..services.core.payment_service import PaymentService
|
401
|
-
|
402
|
-
service = PaymentService()
|
403
|
-
result = service.check_payment_status(payment.id)
|
404
|
-
|
405
|
-
if result.success:
|
406
|
-
updated_count += 1
|
407
|
-
else:
|
408
|
-
error_count += 1
|
409
|
-
|
410
|
-
except Exception as e:
|
411
|
-
error_count += 1
|
412
|
-
logger.error(f"Failed to check payment status for {payment.id}: {e}")
|
187
|
+
# Basic info
|
188
|
+
details.append(f"<strong>Internal ID:</strong> {obj.internal_payment_id}")
|
189
|
+
details.append(f"<strong>Age:</strong> {age_text}")
|
413
190
|
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
f"â
Checked status for {updated_count} payments"
|
418
|
-
)
|
191
|
+
# Transaction details
|
192
|
+
if obj.transaction_hash:
|
193
|
+
details.append(f"<strong>Transaction Hash:</strong> {obj.transaction_hash}")
|
419
194
|
|
420
|
-
if
|
421
|
-
|
422
|
-
|
423
|
-
|
195
|
+
if obj.pay_address:
|
196
|
+
details.append(f"<strong>Pay Address:</strong> {obj.pay_address}")
|
197
|
+
|
198
|
+
if obj.pay_amount:
|
199
|
+
pay_amount_html = MoneyDisplay.amount(
|
200
|
+
obj.pay_amount,
|
201
|
+
MoneyDisplayConfig(currency="USD")
|
424
202
|
)
|
425
|
-
|
426
|
-
@action(
|
427
|
-
description="đĢ Cancel Selected Payments",
|
428
|
-
icon="cancel",
|
429
|
-
variant=ActionVariant.WARNING
|
430
|
-
)
|
431
|
-
def cancel_selected_payments(self, request, queryset):
|
432
|
-
"""Cancel selected payments."""
|
203
|
+
details.append(f"<strong>Pay Amount:</strong> {pay_amount_html}")
|
433
204
|
|
434
|
-
#
|
435
|
-
|
436
|
-
|
437
|
-
UniversalPayment.PaymentStatus.PENDING,
|
438
|
-
UniversalPayment.PaymentStatus.WAITING_FOR_PAYMENT
|
439
|
-
]
|
440
|
-
)
|
205
|
+
# URLs
|
206
|
+
if obj.callback_url:
|
207
|
+
details.append(f"<strong>Callback URL:</strong> {obj.callback_url}")
|
441
208
|
|
442
|
-
|
209
|
+
if obj.cancel_url:
|
210
|
+
details.append(f"<strong>Cancel URL:</strong> {obj.cancel_url}")
|
443
211
|
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
cancelled_count += 1
|
448
|
-
|
449
|
-
except Exception as e:
|
450
|
-
logger.error(f"Failed to cancel payment {payment.id}: {e}")
|
212
|
+
# Description
|
213
|
+
if obj.description:
|
214
|
+
details.append(f"<strong>Description:</strong> {obj.description}")
|
451
215
|
|
452
|
-
|
453
|
-
messages.success(
|
454
|
-
request,
|
455
|
-
f"đĢ Cancelled {cancelled_count} payments"
|
456
|
-
)
|
457
|
-
|
458
|
-
skipped_count = queryset.count() - cancelled_count
|
459
|
-
if skipped_count > 0:
|
460
|
-
messages.info(
|
461
|
-
request,
|
462
|
-
f"âšī¸ Skipped {skipped_count} payments (not cancelable)"
|
463
|
-
)
|
216
|
+
return format_html("<br>".join(details))
|
464
217
|
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
)
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
]
|
218
|
+
payment_details_display.short_description = "Payment Details"
|
219
|
+
|
220
|
+
# Actions
|
221
|
+
@action(description="Mark as completed", variant=ActionVariant.SUCCESS)
|
222
|
+
def mark_completed(self, request, queryset):
|
223
|
+
"""Mark selected payments as completed."""
|
224
|
+
updated = queryset.filter(
|
225
|
+
status__in=['pending', 'processing']
|
226
|
+
).update(status='completed')
|
227
|
+
|
228
|
+
self.message_user(
|
229
|
+
request,
|
230
|
+
f"Successfully marked {updated} payment(s) as completed.",
|
231
|
+
level='SUCCESS'
|
480
232
|
)
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
logger.error(f"Failed to complete payment {payment.id}: {e}")
|
491
|
-
|
492
|
-
if completed_count > 0:
|
493
|
-
messages.success(
|
494
|
-
request,
|
495
|
-
f"â
Marked {completed_count} payments as completed"
|
496
|
-
)
|
497
|
-
messages.warning(
|
498
|
-
request,
|
499
|
-
"â ī¸ Admin override used - ensure payments were actually received!"
|
500
|
-
)
|
501
|
-
|
502
|
-
skipped_count = queryset.count() - completed_count
|
503
|
-
if skipped_count > 0:
|
504
|
-
messages.info(
|
233
|
+
|
234
|
+
@action(description="Mark as failed", variant=ActionVariant.DANGER)
|
235
|
+
def mark_failed(self, request, queryset):
|
236
|
+
"""Mark selected payments as failed."""
|
237
|
+
updated = queryset.filter(
|
238
|
+
status__in=['pending', 'processing']
|
239
|
+
).update(status='failed')
|
240
|
+
|
241
|
+
self.message_user(
|
505
242
|
request,
|
506
|
-
|
507
|
-
|
243
|
+
f"Successfully marked {updated} payment(s) as failed.",
|
244
|
+
level='WARNING'
|
245
|
+
)
|
508
246
|
|
509
|
-
@action(
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
"""Export selected payments to CSV."""
|
516
|
-
|
517
|
-
import csv
|
518
|
-
from django.http import HttpResponse
|
519
|
-
|
520
|
-
response = HttpResponse(content_type='text/csv')
|
521
|
-
response['Content-Disposition'] = f'attachment; filename="payments_{timezone.now().strftime("%Y%m%d_%H%M%S")}.csv"'
|
522
|
-
|
523
|
-
writer = csv.writer(response)
|
524
|
-
writer.writerow([
|
525
|
-
'ID', 'User Email', 'Amount USD', 'Currency', 'Crypto Amount',
|
526
|
-
'Status', 'Provider', 'Created', 'Completed', 'Description'
|
527
|
-
])
|
528
|
-
|
529
|
-
for payment in queryset:
|
530
|
-
writer.writerow([
|
531
|
-
str(payment.id),
|
532
|
-
payment.user.email if payment.user else '',
|
533
|
-
payment.amount_usd,
|
534
|
-
payment.currency.code if payment.currency else '',
|
535
|
-
payment.amount_crypto or '',
|
536
|
-
payment.status,
|
537
|
-
payment.provider,
|
538
|
-
payment.created_at.isoformat(),
|
539
|
-
payment.completed_at.isoformat() if payment.completed_at else '',
|
540
|
-
payment.description or ''
|
541
|
-
])
|
247
|
+
@action(description="Cancel payments", variant=ActionVariant.WARNING)
|
248
|
+
def cancel_payments(self, request, queryset):
|
249
|
+
"""Cancel selected payments."""
|
250
|
+
updated = queryset.filter(
|
251
|
+
status__in=['pending', 'processing']
|
252
|
+
).update(status='cancelled')
|
542
253
|
|
543
|
-
|
254
|
+
self.message_user(
|
544
255
|
request,
|
545
|
-
f"
|
256
|
+
f"Successfully cancelled {updated} payment(s).",
|
257
|
+
level='WARNING'
|
546
258
|
)
|
547
|
-
|
548
|
-
return response
|