Files
octopus/myproject/discounts/admin.py
Andrey Smakotin 241625eba7 feat(discounts): добавлено приложение скидок
Создано новое Django приложение для управления скидками:

Модели:
- BaseDiscount: абстрактный базовый класс с общими полями
- Discount: основная модель скидки (процент/фикс, на заказ/товар/категорию)
- PromoCode: промокоды для активации скидок
- DiscountApplication: история применения скидок

Сервисы:
- DiscountCalculator: расчёт скидок для корзины и заказов
- DiscountApplier: применение скидок к заказам (атомарно)
- DiscountValidator: валидация промокодов и условий

Админ-панель:
- DiscountAdmin: управление скидками
- PromoCodeAdmin: управление промокодами
- DiscountApplicationAdmin: история применения (только чтение)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 00:30:14 +03:00

204 lines
5.5 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.
# -*- coding: utf-8 -*-
from django.contrib import admin
from .models import Discount, PromoCode, DiscountApplication
@admin.register(Discount)
class DiscountAdmin(admin.ModelAdmin):
"""Админ-панель для управления скидками."""
list_display = [
'name',
'discount_type',
'value_display',
'scope',
'is_auto',
'is_active',
'current_usage_count',
'validity_period',
]
list_filter = [
'discount_type',
'scope',
'is_auto',
'is_active',
]
search_fields = [
'name',
'description',
]
readonly_fields = [
'current_usage_count',
'created_at',
]
fieldsets = (
('Основная информация', {
'fields': ('name', 'description', 'is_active', 'priority')
}),
('Параметры скидки', {
'fields': ('discount_type', 'value', 'scope')
}),
('Ограничения', {
'fields': (
'start_date',
'end_date',
'max_usage_count',
'current_usage_count',
'is_auto'
)
}),
('Условия применения', {
'fields': (
'min_order_amount',
'products',
'categories',
'excluded_products'
)
}),
('Метаданные', {
'fields': ('created_at', 'created_by'),
'classes': ('collapse',)
}),
)
def value_display(self, obj):
if obj.discount_type == 'percentage':
return f"{obj.value}%"
return f"{obj.value} руб."
value_display.short_description = "Значение"
def validity_period(self, obj):
if obj.start_date and obj.end_date:
return f"{obj.start_date.date()} - {obj.end_date.date()}"
elif obj.start_date:
return f"с {obj.start_date.date()}"
elif obj.end_date:
return f"до {obj.end_date.date()}"
return "Бессрочная"
validity_period.short_description = "Период действия"
@admin.register(PromoCode)
class PromoCodeAdmin(admin.ModelAdmin):
"""Админ-панель для управления промокодами."""
list_display = [
'code',
'discount_name',
'is_active',
'current_uses',
'usage_limit',
'validity_period',
]
list_filter = [
'is_active',
'discount__scope',
]
search_fields = [
'code',
'discount__name',
]
readonly_fields = [
'current_uses',
'created_at',
]
fieldsets = (
('Основная информация', {
'fields': ('code', 'discount', 'is_active')
}),
('Ограничения', {
'fields': (
'max_uses_per_user',
'max_total_uses',
'current_uses',
'start_date',
'end_date',
)
}),
('Метаданные', {
'fields': ('created_at', 'created_by'),
'classes': ('collapse',)
}),
)
def discount_name(self, obj):
return obj.discount.name
discount_name.short_description = "Скидка"
def usage_limit(self, obj):
if obj.max_total_uses:
return f"{obj.current_uses} / {obj.max_total_uses}"
return str(obj.current_uses)
usage_limit.short_description = "Использования"
def validity_period(self, obj):
if obj.start_date and obj.end_date:
return f"{obj.start_date.date()} - {obj.end_date.date()}"
elif obj.start_date:
return f"с {obj.start_date.date()}"
elif obj.end_date:
return f"до {obj.end_date.date()}"
return "Бессрочный"
validity_period.short_description = "Период действия"
@admin.register(DiscountApplication)
class DiscountApplicationAdmin(admin.ModelAdmin):
"""Админ-панель для истории применения скидок."""
list_display = [
'order_link',
'discount_name',
'promo_code_display',
'target',
'discount_amount',
'customer',
'applied_at',
]
list_filter = [
'target',
'applied_at',
'discount__discount_type',
]
readonly_fields = [
'order',
'order_item',
'discount',
'promo_code',
'target',
'base_amount',
'discount_amount',
'final_amount',
'customer',
'applied_at',
'applied_by',
]
def has_add_permission(self, request):
return False # Только чтение
def has_change_permission(self, request, obj=None):
return False # Только чтение
def order_link(self, obj):
from django.urls import reverse
url = reverse('admin:orders_order_change', args=[obj.order.id])
return f'<a href="{url}">#{obj.order.order_number}</a>'
order_link.short_description = "Заказ"
order_link.allow_tags = True
def discount_name(self, obj):
return obj.discount.name if obj.discount else '-'
discount_name.short_description = "Скидка"
def promo_code_display(self, obj):
return obj.promo_code.code if obj.promo_code else '-'
promo_code_display.short_description = "Промокод"