Add read-only admin functionality and enhance billing models in admin interface
This commit is contained in:
@@ -1,8 +1,43 @@
|
||||
from django.contrib import admin
|
||||
from django.contrib.admin.apps import AdminConfig
|
||||
|
||||
|
||||
class ReadOnlyAdmin(admin.ModelAdmin):
|
||||
"""Generic read-only admin for system tables."""
|
||||
|
||||
def has_add_permission(self, request):
|
||||
return False
|
||||
|
||||
def has_change_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
|
||||
def _safe_register(model, model_admin):
|
||||
try:
|
||||
admin.site.register(model, model_admin)
|
||||
except admin.sites.AlreadyRegistered:
|
||||
pass
|
||||
|
||||
|
||||
class Igny8AdminConfig(AdminConfig):
|
||||
default_site = 'igny8_core.admin.site.Igny8AdminSite'
|
||||
name = 'django.contrib.admin'
|
||||
|
||||
def ready(self):
|
||||
super().ready()
|
||||
# Register Django internals in admin (read-only where appropriate)
|
||||
from django.contrib.admin.models import LogEntry
|
||||
from django.contrib.auth.models import Group, Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.sessions.models import Session
|
||||
|
||||
_safe_register(LogEntry, ReadOnlyAdmin)
|
||||
_safe_register(Permission, admin.ModelAdmin)
|
||||
_safe_register(Group, admin.ModelAdmin)
|
||||
_safe_register(ContentType, ReadOnlyAdmin)
|
||||
_safe_register(Session, ReadOnlyAdmin)
|
||||
|
||||
|
||||
|
||||
@@ -37,6 +37,12 @@ class Igny8AdminSite(admin.AdminSite):
|
||||
('igny8_core_auth', 'Subscription'),
|
||||
('billing', 'CreditTransaction'),
|
||||
('billing', 'CreditUsageLog'),
|
||||
('billing', 'Invoice'),
|
||||
('billing', 'Payment'),
|
||||
('billing', 'CreditPackage'),
|
||||
('billing', 'PaymentMethodConfig'),
|
||||
('billing', 'AccountPaymentMethod'),
|
||||
('billing', 'CreditCostConfig'),
|
||||
],
|
||||
},
|
||||
'Sites & Users': {
|
||||
@@ -45,6 +51,7 @@ class Igny8AdminSite(admin.AdminSite):
|
||||
('igny8_core_auth', 'User'),
|
||||
('igny8_core_auth', 'SiteUserAccess'),
|
||||
('igny8_core_auth', 'PasswordResetToken'),
|
||||
('igny8_core_auth', 'Sector'),
|
||||
],
|
||||
},
|
||||
'Global Reference Data': {
|
||||
@@ -70,6 +77,10 @@ class Igny8AdminSite(admin.AdminSite):
|
||||
('writer', 'Tasks'),
|
||||
('writer', 'Content'),
|
||||
('writer', 'Images'),
|
||||
('writer', 'ContentTaxonomy'),
|
||||
('writer', 'ContentAttribute'),
|
||||
('writer', 'ContentTaxonomyRelation'),
|
||||
('writer', 'ContentClusterMap'),
|
||||
],
|
||||
},
|
||||
'Thinker Module': {
|
||||
@@ -77,6 +88,7 @@ class Igny8AdminSite(admin.AdminSite):
|
||||
('system', 'AIPrompt'),
|
||||
('system', 'AuthorProfile'),
|
||||
('system', 'Strategy'),
|
||||
('ai', 'AITaskLog'),
|
||||
],
|
||||
},
|
||||
'System Configuration': {
|
||||
@@ -89,11 +101,38 @@ class Igny8AdminSite(admin.AdminSite):
|
||||
('system', 'UserSettings'),
|
||||
('system', 'ModuleSettings'),
|
||||
('system', 'AISettings'),
|
||||
('system', 'ModuleEnableSettings'),
|
||||
# Automation config lives under the automation app - include here
|
||||
('automation', 'AutomationConfig'),
|
||||
('automation', 'AutomationRun'),
|
||||
],
|
||||
},
|
||||
'Integrations & Sync': {
|
||||
'models': [
|
||||
('integration', 'SiteIntegration'),
|
||||
('integration', 'SyncEvent'),
|
||||
],
|
||||
},
|
||||
'Publishing': {
|
||||
'models': [
|
||||
('publishing', 'PublishingRecord'),
|
||||
('publishing', 'DeploymentRecord'),
|
||||
],
|
||||
},
|
||||
'Optimization': {
|
||||
'models': [
|
||||
('optimization', 'OptimizationTask'),
|
||||
],
|
||||
},
|
||||
'Django Internals': {
|
||||
'models': [
|
||||
('admin', 'LogEntry'),
|
||||
('auth', 'Group'),
|
||||
('auth', 'Permission'),
|
||||
('contenttypes', 'ContentType'),
|
||||
('sessions', 'Session'),
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
# Build the custom app list
|
||||
@@ -131,6 +170,10 @@ class Igny8AdminSite(admin.AdminSite):
|
||||
'Writer Module',
|
||||
'Thinker Module',
|
||||
'System Configuration',
|
||||
'Integrations & Sync',
|
||||
'Publishing',
|
||||
'Optimization',
|
||||
'Django Internals',
|
||||
]
|
||||
|
||||
app_list.sort(key=lambda x: order.index(x['name']) if x['name'] in order else 999)
|
||||
|
||||
@@ -3,7 +3,15 @@ Billing Business Logic Admin
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.utils.html import format_html
|
||||
from .models import CreditCostConfig, AccountPaymentMethod
|
||||
from igny8_core.admin.base import AccountAdminMixin
|
||||
from .models import (
|
||||
CreditCostConfig,
|
||||
AccountPaymentMethod,
|
||||
Invoice,
|
||||
Payment,
|
||||
CreditPackage,
|
||||
PaymentMethodConfig,
|
||||
)
|
||||
|
||||
|
||||
@admin.register(CreditCostConfig)
|
||||
@@ -81,6 +89,56 @@ class CreditCostConfigAdmin(admin.ModelAdmin):
|
||||
super().save_model(request, obj, form, change)
|
||||
|
||||
|
||||
@admin.register(Invoice)
|
||||
class InvoiceAdmin(AccountAdminMixin, admin.ModelAdmin):
|
||||
list_display = [
|
||||
'invoice_number',
|
||||
'account',
|
||||
'status',
|
||||
'total',
|
||||
'currency',
|
||||
'invoice_date',
|
||||
'due_date',
|
||||
'subscription',
|
||||
]
|
||||
list_filter = ['status', 'currency', 'invoice_date', 'account']
|
||||
search_fields = ['invoice_number', 'account__name', 'subscription__id']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
|
||||
|
||||
@admin.register(Payment)
|
||||
class PaymentAdmin(AccountAdminMixin, admin.ModelAdmin):
|
||||
list_display = [
|
||||
'id',
|
||||
'invoice',
|
||||
'account',
|
||||
'payment_method',
|
||||
'status',
|
||||
'amount',
|
||||
'currency',
|
||||
'processed_at',
|
||||
]
|
||||
list_filter = ['status', 'payment_method', 'currency', 'created_at']
|
||||
search_fields = ['invoice__invoice_number', 'account__name', 'stripe_payment_intent_id', 'paypal_order_id']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
|
||||
|
||||
@admin.register(CreditPackage)
|
||||
class CreditPackageAdmin(admin.ModelAdmin):
|
||||
list_display = ['name', 'slug', 'credits', 'price', 'discount_percentage', 'is_active', 'is_featured', 'sort_order']
|
||||
list_filter = ['is_active', 'is_featured']
|
||||
search_fields = ['name', 'slug']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
|
||||
|
||||
@admin.register(PaymentMethodConfig)
|
||||
class PaymentMethodConfigAdmin(admin.ModelAdmin):
|
||||
list_display = ['country_code', 'payment_method', 'is_enabled', 'display_name', 'sort_order']
|
||||
list_filter = ['payment_method', 'is_enabled', 'country_code']
|
||||
search_fields = ['country_code', 'display_name', 'payment_method']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
|
||||
|
||||
@admin.register(AccountPaymentMethod)
|
||||
class AccountPaymentMethodAdmin(admin.ModelAdmin):
|
||||
list_display = [
|
||||
|
||||
36
backend/igny8_core/business/integration/admin.py
Normal file
36
backend/igny8_core/business/integration/admin.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from django.contrib import admin
|
||||
from igny8_core.admin.base import AccountAdminMixin
|
||||
from .models import SiteIntegration, SyncEvent
|
||||
|
||||
|
||||
@admin.register(SiteIntegration)
|
||||
class SiteIntegrationAdmin(AccountAdminMixin, admin.ModelAdmin):
|
||||
list_display = [
|
||||
'site',
|
||||
'platform',
|
||||
'platform_type',
|
||||
'is_active',
|
||||
'sync_enabled',
|
||||
'sync_status',
|
||||
'last_sync_at',
|
||||
]
|
||||
list_filter = ['platform', 'platform_type', 'is_active', 'sync_enabled', 'sync_status']
|
||||
search_fields = ['site__name', 'site__domain', 'platform']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
|
||||
|
||||
@admin.register(SyncEvent)
|
||||
class SyncEventAdmin(AccountAdminMixin, admin.ModelAdmin):
|
||||
list_display = [
|
||||
'integration',
|
||||
'site',
|
||||
'event_type',
|
||||
'action',
|
||||
'success',
|
||||
'external_id',
|
||||
'created_at',
|
||||
]
|
||||
list_filter = ['event_type', 'action', 'success', 'created_at']
|
||||
search_fields = ['integration__site__name', 'site__name', 'description', 'external_id']
|
||||
readonly_fields = ['created_at']
|
||||
|
||||
12
backend/igny8_core/business/optimization/admin.py
Normal file
12
backend/igny8_core/business/optimization/admin.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from django.contrib import admin
|
||||
from igny8_core.admin.base import AccountAdminMixin
|
||||
from .models import OptimizationTask
|
||||
|
||||
|
||||
@admin.register(OptimizationTask)
|
||||
class OptimizationTaskAdmin(AccountAdminMixin, admin.ModelAdmin):
|
||||
list_display = ['content', 'account', 'status', 'credits_used', 'created_at']
|
||||
list_filter = ['status', 'created_at']
|
||||
search_fields = ['content__title', 'account__name']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
|
||||
36
backend/igny8_core/business/publishing/admin.py
Normal file
36
backend/igny8_core/business/publishing/admin.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from django.contrib import admin
|
||||
from igny8_core.admin.base import SiteSectorAdminMixin
|
||||
from .models import PublishingRecord, DeploymentRecord
|
||||
|
||||
|
||||
@admin.register(PublishingRecord)
|
||||
class PublishingRecordAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
|
||||
list_display = [
|
||||
'content',
|
||||
'site',
|
||||
'sector',
|
||||
'destination',
|
||||
'status',
|
||||
'destination_url',
|
||||
'published_at',
|
||||
]
|
||||
list_filter = ['destination', 'status', 'site']
|
||||
search_fields = ['content__title', 'destination', 'destination_url']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
|
||||
|
||||
@admin.register(DeploymentRecord)
|
||||
class DeploymentRecordAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
|
||||
list_display = [
|
||||
'site',
|
||||
'sector',
|
||||
'version',
|
||||
'deployed_version',
|
||||
'status',
|
||||
'deployment_url',
|
||||
'deployed_at',
|
||||
]
|
||||
list_filter = ['status', 'site']
|
||||
search_fields = ['site__name', 'deployment_url']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
|
||||
@@ -4,7 +4,13 @@ Billing Module Admin
|
||||
from django.contrib import admin
|
||||
from django.utils.html import format_html
|
||||
from igny8_core.admin.base import AccountAdminMixin
|
||||
from igny8_core.business.billing.models import CreditCostConfig
|
||||
from igny8_core.business.billing.models import (
|
||||
CreditCostConfig,
|
||||
Invoice,
|
||||
Payment,
|
||||
CreditPackage,
|
||||
PaymentMethodConfig,
|
||||
)
|
||||
from .models import CreditTransaction, CreditUsageLog, AccountPaymentMethod
|
||||
|
||||
|
||||
@@ -44,6 +50,56 @@ class CreditUsageLogAdmin(AccountAdminMixin, admin.ModelAdmin):
|
||||
get_account_display.short_description = 'Account'
|
||||
|
||||
|
||||
@admin.register(Invoice)
|
||||
class InvoiceAdmin(AccountAdminMixin, admin.ModelAdmin):
|
||||
list_display = [
|
||||
'invoice_number',
|
||||
'account',
|
||||
'status',
|
||||
'total',
|
||||
'currency',
|
||||
'invoice_date',
|
||||
'due_date',
|
||||
'subscription',
|
||||
]
|
||||
list_filter = ['status', 'currency', 'invoice_date', 'account']
|
||||
search_fields = ['invoice_number', 'account__name', 'subscription__id']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
|
||||
|
||||
@admin.register(Payment)
|
||||
class PaymentAdmin(AccountAdminMixin, admin.ModelAdmin):
|
||||
list_display = [
|
||||
'id',
|
||||
'invoice',
|
||||
'account',
|
||||
'payment_method',
|
||||
'status',
|
||||
'amount',
|
||||
'currency',
|
||||
'processed_at',
|
||||
]
|
||||
list_filter = ['status', 'payment_method', 'currency', 'created_at']
|
||||
search_fields = ['invoice__invoice_number', 'account__name', 'stripe_payment_intent_id', 'paypal_order_id']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
|
||||
|
||||
@admin.register(CreditPackage)
|
||||
class CreditPackageAdmin(admin.ModelAdmin):
|
||||
list_display = ['name', 'slug', 'credits', 'price', 'discount_percentage', 'is_active', 'is_featured', 'sort_order']
|
||||
list_filter = ['is_active', 'is_featured']
|
||||
search_fields = ['name', 'slug']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
|
||||
|
||||
@admin.register(PaymentMethodConfig)
|
||||
class PaymentMethodConfigAdmin(admin.ModelAdmin):
|
||||
list_display = ['country_code', 'payment_method', 'is_enabled', 'display_name', 'sort_order']
|
||||
list_filter = ['payment_method', 'is_enabled', 'country_code']
|
||||
search_fields = ['country_code', 'display_name', 'payment_method']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
|
||||
|
||||
@admin.register(AccountPaymentMethod)
|
||||
class AccountPaymentMethodAdmin(AccountAdminMixin, admin.ModelAdmin):
|
||||
list_display = [
|
||||
|
||||
@@ -91,3 +91,20 @@ class AISettingsAdmin(AccountAdminMixin, admin.ModelAdmin):
|
||||
return '-'
|
||||
get_account_display.short_description = 'Account'
|
||||
|
||||
|
||||
@admin.register(ModuleEnableSettings)
|
||||
class ModuleEnableSettingsAdmin(AccountAdminMixin, admin.ModelAdmin):
|
||||
list_display = [
|
||||
'account',
|
||||
'planner_enabled',
|
||||
'writer_enabled',
|
||||
'thinker_enabled',
|
||||
'automation_enabled',
|
||||
'site_builder_enabled',
|
||||
'linker_enabled',
|
||||
'optimizer_enabled',
|
||||
'publisher_enabled',
|
||||
]
|
||||
list_filter = ['planner_enabled', 'writer_enabled', 'thinker_enabled', 'automation_enabled', 'site_builder_enabled', 'linker_enabled', 'optimizer_enabled', 'publisher_enabled']
|
||||
search_fields = ['account__name']
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from django.contrib import admin
|
||||
from igny8_core.admin.base import SiteSectorAdminMixin
|
||||
from .models import Tasks, Images, Content
|
||||
from igny8_core.business.content.models import ContentTaxonomy, ContentAttribute, ContentTaxonomyRelation
|
||||
from igny8_core.business.content.models import ContentTaxonomy, ContentAttribute, ContentTaxonomyRelation, ContentClusterMap
|
||||
|
||||
|
||||
class ContentTaxonomyInline(admin.TabularInline):
|
||||
@@ -223,3 +223,18 @@ class ContentAttributeAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
|
||||
qs = super().get_queryset(request)
|
||||
return qs.select_related('content', 'cluster', 'site', 'sector')
|
||||
|
||||
|
||||
@admin.register(ContentTaxonomyRelation)
|
||||
class ContentTaxonomyRelationAdmin(admin.ModelAdmin):
|
||||
list_display = ['content', 'taxonomy', 'created_at']
|
||||
search_fields = ['content__title', 'taxonomy__name']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
|
||||
|
||||
@admin.register(ContentClusterMap)
|
||||
class ContentClusterMapAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
|
||||
list_display = ['content', 'task', 'cluster', 'role', 'source', 'site', 'sector', 'created_at']
|
||||
list_filter = ['role', 'source', 'site', 'sector']
|
||||
search_fields = ['content__title', 'task__title', 'cluster__name']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
|
||||
|
||||
Reference in New Issue
Block a user