Files
octopus/myproject/discounts/admin.py
Andrey Smakotin f57e639dbe feat(discounts): добавлено комбинирование скидок по режимам
Добавлено поле combine_mode с тремя режимами:
- stack - складывать с другими скидками
- max_only - применять только максимальную
- exclusive - отменяет все остальные скидки

Изменения:
- Модель Discount: добавлено поле combine_mode
- Calculator: новый класс DiscountCombiner, методы возвращают списки скидок
- Applier: создание нескольких DiscountApplication записей
- Admin: отображение combine_mode с иконками
- POS API: возвращает списки применённых скидок
- POS UI: отображение нескольких скидок с иконками режимов

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

223 lines
6.2 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',
'combine_mode_display',
'is_auto',
'is_active',
'current_usage_count',
'validity_period',
]
list_filter = [
'discount_type',
'scope',
'combine_mode',
'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', 'combine_mode')
}),
('Ограничения', {
'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 combine_mode_display(self, obj):
"""Отображение режима объединения с иконкой."""
icons = {
'stack': '📚', # слои
'max_only': '🏆', # максимум
'exclusive': '🚫', # запрет
}
labels = {
'stack': 'Склад.',
'max_only': 'Макс.',
'exclusive': 'Исключ.',
}
icon = icons.get(obj.combine_mode, '')
label = labels.get(obj.combine_mode, obj.combine_mode)
return f'{icon} {label}' if icon else label
combine_mode_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 = "Промокод"