uchill/backend/apps/referrals/admin.py

372 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Админ-панель для реферальной системы.
"""
from django.contrib import admin
from django.utils.html import format_html
from .models import (
ReferralSettings,
ReferralLevel,
UserReferralProfile,
BonusAccount,
ReferralEarning,
PointsTransaction,
BonusTransaction,
PromoCode,
PromoCodeUsage,
ReferralInvitedEmail,
UserActivityDay,
PendingReferralBonus,
)
@admin.register(ReferralSettings)
class ReferralSettingsAdmin(admin.ModelAdmin):
"""Админ для настроек реферальной программы."""
list_display = [
'level1_commission',
'level2_commission',
'points_direct_referral',
'points_indirect_referral',
'updated_at'
]
fieldsets = (
('Комиссии', {
'fields': ('level1_commission', 'level2_commission')
}),
('Очки', {
'fields': ('points_direct_referral', 'points_indirect_referral')
}),
)
@admin.register(ReferralLevel)
class ReferralLevelAdmin(admin.ModelAdmin):
"""Админ для уровней реферальной программы."""
list_display = ['level', 'name', 'points_required', 'bonus_payment_percent', 'icon']
list_editable = ['bonus_payment_percent']
ordering = ['level']
@admin.register(UserReferralProfile)
class UserReferralProfileAdmin(admin.ModelAdmin):
"""Админ для реферальных профилей."""
list_display = [
'user_email',
'referral_code',
'referred_by_email',
'current_level',
'total_points',
'direct_referrals_count',
'total_earned',
'created_at'
]
list_filter = ['current_level', 'created_at']
search_fields = ['user__email', 'referral_code', 'referred_by__email']
readonly_fields = [
'referral_code',
'total_points',
'direct_referrals_count',
'indirect_referrals_count',
'total_earned',
'created_at',
'updated_at',
'referral_link'
]
fieldsets = (
('Основная информация', {
'fields': ('user', 'referral_code', 'referred_by', 'referral_link')
}),
('Уровень и очки', {
'fields': ('current_level', 'total_points')
}),
('Статистика', {
'fields': (
'direct_referrals_count',
'indirect_referrals_count',
'total_earned'
)
}),
('Даты', {
'fields': ('created_at', 'updated_at')
}),
)
def user_email(self, obj):
return obj.user.email
user_email.short_description = 'Email'
def referred_by_email(self, obj):
return obj.referred_by.email if obj.referred_by else '-'
referred_by_email.short_description = 'Пригласил'
def referral_link(self, obj):
link = obj.get_referral_link()
return format_html('<a href="{}" target="_blank">{}</a>', link, link)
referral_link.short_description = 'Реферальная ссылка'
@admin.register(BonusAccount)
class BonusAccountAdmin(admin.ModelAdmin):
"""Админ для бонусных счетов."""
list_display = [
'user_email',
'balance',
'total_earned',
'total_spent',
'updated_at'
]
list_editable = ['balance']
search_fields = ['user__email']
readonly_fields = ['created_at', 'updated_at']
fieldsets = (
('Основная информация', {
'fields': ('user',)
}),
('Баланс и статистика', {
'fields': ('balance', 'total_earned', 'total_spent'),
'description': 'Внимание: изменение баланса напрямую не создает транзакцию в истории. '
'Для корректного учета используйте методы add_bonus() и spend_bonus() модели.'
}),
('Временные метки', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
def user_email(self, obj):
return obj.user.email
user_email.short_description = 'Email'
def save_model(self, request, obj, form, change):
"""
Сохранить модель с логированием изменений.
"""
if change:
# Получаем старые значения для логирования
old_obj = BonusAccount.objects.get(pk=obj.pk)
# Логируем изменения баланса
if old_obj.balance != obj.balance:
from .models import BonusTransaction
from decimal import Decimal
diff = obj.balance - old_obj.balance
if diff != 0:
transaction_type = 'earn' if diff > 0 else 'spend'
BonusTransaction.objects.create(
user=obj.user,
amount=abs(diff),
transaction_type=transaction_type,
reason=f'Ручное изменение баланса администратором {request.user.email}',
balance_after=obj.balance
)
# Обновляем статистику
if diff > 0:
obj.total_earned += diff
else:
obj.total_spent += abs(diff)
# Логируем изменения статистики (если изменены напрямую)
if old_obj.total_earned != obj.total_earned or old_obj.total_spent != obj.total_spent:
# Если статистика изменена напрямую, просто сохраняем
pass
super().save_model(request, obj, form, change)
@admin.register(ReferralEarning)
class ReferralEarningAdmin(admin.ModelAdmin):
"""Админ для заработков с рефералов."""
list_display = [
'referrer_email',
'referral_email',
'level',
'payment_amount',
'commission_percent',
'earned_amount',
'created_at'
]
list_filter = ['level', 'created_at']
search_fields = ['referrer__email', 'referral__email']
readonly_fields = [
'referrer',
'referral',
'payment',
'level',
'payment_amount',
'commission_percent',
'earned_amount',
'created_at'
]
def referrer_email(self, obj):
return obj.referrer.email
referrer_email.short_description = 'Реферер'
def referral_email(self, obj):
return obj.referral.email
referral_email.short_description = 'Реферал'
@admin.register(PointsTransaction)
class PointsTransactionAdmin(admin.ModelAdmin):
"""Админ для транзакций очков."""
list_display = ['user_email', 'points', 'reason', 'balance_after', 'created_at']
list_filter = ['created_at']
search_fields = ['user__email', 'reason']
readonly_fields = ['user', 'points', 'reason', 'balance_after', 'created_at']
def user_email(self, obj):
return obj.user.email
user_email.short_description = 'Email'
@admin.register(BonusTransaction)
class BonusTransactionAdmin(admin.ModelAdmin):
"""Админ для транзакций бонусов."""
list_display = [
'user_email',
'amount',
'transaction_type',
'reason',
'balance_after',
'created_at'
]
list_filter = ['transaction_type', 'created_at']
search_fields = ['user__email', 'reason']
readonly_fields = ['user', 'amount', 'transaction_type', 'reason', 'balance_after', 'created_at']
def user_email(self, obj):
return obj.user.email
user_email.short_description = 'Email'
@admin.register(PromoCode)
class PromoCodeAdmin(admin.ModelAdmin):
"""Админ для промокодов."""
list_display = [
'code',
'name',
'discount_display',
'current_uses',
'max_uses',
'valid_until',
'is_active',
'created_at'
]
list_filter = ['is_active', 'discount_type', 'created_at']
search_fields = ['code', 'name', 'description']
filter_horizontal = ['applicable_plans']
fieldsets = (
('Основная информация', {
'fields': ('code', 'name', 'description')
}),
('Скидка', {
'fields': ('discount_type', 'discount_value')
}),
('Применимость', {
'fields': ('applicable_plans',)
}),
('Ограничения', {
'fields': ('max_uses', 'current_uses', 'valid_until', 'is_active')
}),
('Метаданные', {
'fields': ('created_by', 'created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
readonly_fields = ['current_uses', 'created_at', 'updated_at']
def discount_display(self, obj):
if obj.discount_type == 'percent':
return f'{obj.discount_value}%'
return f'{obj.discount_value}'
discount_display.short_description = 'Скидка'
def save_model(self, request, obj, form, change):
if not change:
obj.created_by = request.user
super().save_model(request, obj, form, change)
@admin.register(PromoCodeUsage)
class PromoCodeUsageAdmin(admin.ModelAdmin):
"""Админ для использований промокодов."""
list_display = [
'user_email',
'promo_code_code',
'original_amount',
'discount_amount',
'final_amount',
'created_at'
]
list_filter = ['created_at', 'promo_code']
search_fields = ['user__email', 'promo_code__code']
readonly_fields = [
'user',
'promo_code',
'payment',
'original_amount',
'discount_amount',
'final_amount',
'created_at'
]
def user_email(self, obj):
return obj.user.email
user_email.short_description = 'Email'
def promo_code_code(self, obj):
return obj.promo_code.code
promo_code_code.short_description = 'Промокод'
@admin.register(ReferralInvitedEmail)
class ReferralInvitedEmailAdmin(admin.ModelAdmin):
"""Бэклог приглашённых email (защита от накрутки)."""
list_display = ['email', 'referrer', 'referred_user', 'created_at']
search_fields = ['email', 'referrer__email', 'referred_user__email']
readonly_fields = ['email', 'referrer', 'referred_user', 'created_at']
list_filter = ['created_at']
@admin.register(UserActivityDay)
class UserActivityDayAdmin(admin.ModelAdmin):
"""Дни активности пользователей (для проверки условий начисления бонусов)."""
list_display = ['user', 'date', 'created_at']
list_filter = ['date']
search_fields = ['user__email']
readonly_fields = ['user', 'date', 'created_at']
@admin.register(PendingReferralBonus)
class PendingReferralBonusAdmin(admin.ModelAdmin):
"""Ожидающие начисления бонусов за рефералов."""
list_display = ['referrer', 'referred_user', 'points', 'level', 'status', 'referred_at', 'paid_at']
list_filter = ['status', 'level']
search_fields = ['referrer__email', 'referred_user__email']
readonly_fields = ['referrer', 'referred_user', 'referred_at', 'points', 'level', 'reason', 'paid_at', 'created_at']