Files
octopus/myproject/discounts/models/discount.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

125 lines
4.7 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.db import models
from .base import BaseDiscount
class Discount(BaseDiscount):
"""
Основная модель скидки.
Наследует все поля из BaseDiscount и добавляет специфические параметры.
"""
# Для scope='order' - минимальная сумма заказа
min_order_amount = models.DecimalField(
max_digits=10,
decimal_places=2,
null=True,
blank=True,
verbose_name="Мин. сумма заказа",
help_text="Скидка применяется только если сумма заказа >= этого значения"
)
# Для scope='product' и scope='category' - товары и категории
products = models.ManyToManyField(
'products.Product',
blank=True,
related_name='discounts',
verbose_name="Товары"
)
categories = models.ManyToManyField(
'products.ProductCategory',
blank=True,
related_name='discounts',
verbose_name="Категории"
)
# Исключения (товары, к которым скидка НЕ применяется)
excluded_products = models.ManyToManyField(
'products.Product',
blank=True,
related_name='excluded_from_discounts',
verbose_name="Исключенные товары"
)
# Автоматическая скидка (не требует промокода)
is_auto = models.BooleanField(
default=False,
verbose_name="Автоматическая",
help_text="Применяется автоматически при выполнении условий"
)
# Режим объединения с другими скидками
combine_mode = models.CharField(
max_length=20,
choices=BaseDiscount.COMBINE_MODE_CHOICES,
default='max_only',
verbose_name="Режим объединения",
help_text="stack = суммировать с другими, max_only = применить максимальную, exclusive = отменить остальные"
)
class Meta:
verbose_name = "Скидка"
verbose_name_plural = "Скидки"
indexes = [
models.Index(fields=['is_active']),
models.Index(fields=['scope']),
models.Index(fields=['discount_type']),
models.Index(fields=['is_auto']),
]
def applies_to_product(self, product):
"""
Проверить, применяется ли скидка к товару.
Args:
product: Объект Product
Returns:
bool: True если скидка применяется к товару
"""
# Проверяем исключения
if self.excluded_products.filter(id=product.id).exists():
return False
# Если scope='product', проверяем прямое соответствие
if self.scope == 'product':
return self.products.filter(id=product.id).exists()
# Если scope='category', проверяем категории товара
if self.scope == 'category':
if not self.categories.exists():
return False
product_categories = product.categories.all()
return self.categories.filter(id__in=product_categories).exists()
return False
def get_applicable_products(self):
"""
Получить queryset товаров, к которым применяется эта скидка.
Returns:
QuerySet: Товары, к которым применяется скидка
"""
from products.models import Product
if self.scope == 'product':
qs = self.products.all()
# Исключаем исключенные товары
if self.excluded_products.exists():
qs = qs.exclude(id__in=self.excluded_products.values_list('id', flat=True))
return qs
if self.scope == 'category':
# Товары из указанных категорий
product_ids = Product.objects.filter(
categories__in=self.categories.all()
).values_list('id', flat=True).distinct()
# Исключаем исключенные товары
if self.excluded_products.exists():
excluded_ids = self.excluded_products.values_list('id', flat=True)
product_ids = set(product_ids) - set(excluded_ids)
return Product.objects.filter(id__in=product_ids)
return Product.objects.none()