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>
This commit is contained in:
2026-01-11 12:56:38 +03:00
parent 293f3b58cb
commit f57e639dbe
9 changed files with 715 additions and 223 deletions

View File

@@ -11,6 +11,7 @@ class DiscountAdmin(admin.ModelAdmin):
'discount_type',
'value_display',
'scope',
'combine_mode_display',
'is_auto',
'is_active',
'current_usage_count',
@@ -20,6 +21,7 @@ class DiscountAdmin(admin.ModelAdmin):
list_filter = [
'discount_type',
'scope',
'combine_mode',
'is_auto',
'is_active',
]
@@ -39,7 +41,7 @@ class DiscountAdmin(admin.ModelAdmin):
'fields': ('name', 'description', 'is_active', 'priority')
}),
('Параметры скидки', {
'fields': ('discount_type', 'value', 'scope')
'fields': ('discount_type', 'value', 'scope', 'combine_mode')
}),
('Ограничения', {
'fields': (
@@ -70,6 +72,23 @@ class DiscountAdmin(admin.ModelAdmin):
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()}"