Создано новое Django приложение для управления скидками: Модели: - BaseDiscount: абстрактный базовый класс с общими полями - Discount: основная модель скидки (процент/фикс, на заказ/товар/категорию) - PromoCode: промокоды для активации скидок - DiscountApplication: история применения скидок Сервисы: - DiscountCalculator: расчёт скидок для корзины и заказов - DiscountApplier: применение скидок к заказам (атомарно) - DiscountValidator: валидация промокодов и условий Админ-панель: - DiscountAdmin: управление скидками - PromoCodeAdmin: управление промокодами - DiscountApplicationAdmin: история применения (только чтение) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
111 lines
3.0 KiB
Python
111 lines
3.0 KiB
Python
from django.db import models
|
|
|
|
|
|
class DiscountApplication(models.Model):
|
|
"""
|
|
История применения скидок к заказам и позициям.
|
|
Используется для аналитики и отчётов.
|
|
"""
|
|
|
|
DISCOUNT_TARGET_CHOICES = [
|
|
('order', 'Заказ'),
|
|
('order_item', 'Позиция заказа'),
|
|
]
|
|
|
|
order = models.ForeignKey(
|
|
'orders.Order',
|
|
on_delete=models.CASCADE,
|
|
related_name='discount_applications',
|
|
verbose_name="Заказ"
|
|
)
|
|
|
|
order_item = models.ForeignKey(
|
|
'orders.OrderItem',
|
|
on_delete=models.CASCADE,
|
|
null=True,
|
|
blank=True,
|
|
related_name='discount_applications',
|
|
verbose_name="Позиция заказа"
|
|
)
|
|
|
|
discount = models.ForeignKey(
|
|
'Discount',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
related_name='applications',
|
|
verbose_name="Скидка"
|
|
)
|
|
|
|
promo_code = models.ForeignKey(
|
|
'PromoCode',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='applications',
|
|
verbose_name="Промокод"
|
|
)
|
|
|
|
target = models.CharField(
|
|
max_length=20,
|
|
choices=DISCOUNT_TARGET_CHOICES,
|
|
verbose_name="Объект применения"
|
|
)
|
|
|
|
base_amount = models.DecimalField(
|
|
max_digits=10,
|
|
decimal_places=2,
|
|
verbose_name="Базовая сумма"
|
|
)
|
|
|
|
discount_amount = models.DecimalField(
|
|
max_digits=10,
|
|
decimal_places=2,
|
|
verbose_name="Сумма скидки"
|
|
)
|
|
|
|
final_amount = models.DecimalField(
|
|
max_digits=10,
|
|
decimal_places=2,
|
|
verbose_name="Итоговая сумма"
|
|
)
|
|
|
|
customer = models.ForeignKey(
|
|
'customers.Customer',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='discount_applications',
|
|
verbose_name="Клиент"
|
|
)
|
|
|
|
applied_at = models.DateTimeField(
|
|
auto_now_add=True,
|
|
verbose_name="Дата применения"
|
|
)
|
|
|
|
applied_by = models.ForeignKey(
|
|
'accounts.CustomUser',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='applied_discounts',
|
|
verbose_name="Применен пользователем"
|
|
)
|
|
|
|
class Meta:
|
|
verbose_name = "Применение скидки"
|
|
verbose_name_plural = "Применения скидок"
|
|
indexes = [
|
|
models.Index(fields=['order']),
|
|
models.Index(fields=['discount']),
|
|
models.Index(fields=['promo_code']),
|
|
models.Index(fields=['customer']),
|
|
models.Index(fields=['applied_at']),
|
|
]
|
|
|
|
def __str__(self):
|
|
target_info = f"Заказ #{self.order.order_number}"
|
|
if self.order_item:
|
|
target_info += f", {self.order_item.item_name_snapshot}"
|
|
return f"{self.discount.name} -> {target_info} (-{self.discount_amount})"
|