feat(discounts): добавлено приложение скидок
Создано новое Django приложение для управления скидками: Модели: - BaseDiscount: абстрактный базовый класс с общими полями - Discount: основная модель скидки (процент/фикс, на заказ/товар/категорию) - PromoCode: промокоды для активации скидок - DiscountApplication: история применения скидок Сервисы: - DiscountCalculator: расчёт скидок для корзины и заказов - DiscountApplier: применение скидок к заказам (атомарно) - DiscountValidator: валидация промокодов и условий Админ-панель: - DiscountAdmin: управление скидками - PromoCodeAdmin: управление промокодами - DiscountApplicationAdmin: история применения (только чтение) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
110
myproject/discounts/models/application.py
Normal file
110
myproject/discounts/models/application.py
Normal file
@@ -0,0 +1,110 @@
|
||||
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})"
|
||||
Reference in New Issue
Block a user