from django.db import models from django.core.exceptions import ValidationError from django.utils import timezone class PromoCode(models.Model): """ Промокод для активации скидки. Связывает код с одной скидкой. """ code = models.CharField( max_length=50, unique=True, verbose_name="Код промокода", help_text="Уникальный код (например: SALE2025, WINTER10)" ) discount = models.ForeignKey( 'Discount', on_delete=models.CASCADE, related_name='promo_codes', verbose_name="Скидка" ) # Ограничения использования max_uses_per_user = models.PositiveIntegerField( null=True, blank=True, verbose_name="Макс. использований на клиента", help_text="Оставьте пустым для безлимитного использования" ) max_total_uses = models.PositiveIntegerField( null=True, blank=True, verbose_name="Макс. общее количество использований" ) current_uses = models.PositiveIntegerField( default=0, verbose_name="Текущее количество использований" ) is_active = models.BooleanField( default=True, verbose_name="Активен" ) start_date = models.DateTimeField( null=True, blank=True, verbose_name="Дата начала действия" ) end_date = models.DateTimeField( null=True, blank=True, verbose_name="Дата окончания действия" ) created_at = models.DateTimeField( auto_now_add=True, verbose_name="Дата создания" ) created_by = models.ForeignKey( 'accounts.CustomUser', on_delete=models.SET_NULL, null=True, blank=True, related_name='created_promo_codes', verbose_name="Создал" ) class Meta: verbose_name = "Промокод" verbose_name_plural = "Промокоды" indexes = [ models.Index(fields=['code']), models.Index(fields=['is_active']), ] def __str__(self): return f"{self.code} -> {self.discount.name}" def clean(self): """Валидация промокода""" super().clean() if self.code: self.code = self.code.strip().upper() def save(self, *args, **kwargs): """Приводим код к верхнему регистру при сохранении""" if self.code: self.code = self.code.strip().upper() super().save(*args, **kwargs) def is_valid(self, customer=None): """ Проверить валидность промокода. Args: customer: Customer для проверки использований на пользователя Returns: tuple: (is_valid, error_message) """ now = timezone.now() if not self.is_active: return False, "Промокод неактивен" if self.start_date and now < self.start_date: return False, "Промокод еще не начал действовать" if self.end_date and now > self.end_date: return False, "Промокод истек" if self.max_total_uses and self.current_uses >= self.max_total_uses: return False, "Промокод полностью использован" if customer and self.max_uses_per_user: # Проверяем использования этим клиентом uses = DiscountApplication.objects.filter( promo_code=self, customer=customer ).count() if uses >= self.max_uses_per_user: return False, f"Вы уже использовали этот промокод максимальное количество раз ({self.max_uses_per_user})" return True, None def record_usage(self, customer=None): """ Зарегистрировать использование промокода. Args: customer: Customer (опционально) """ self.current_uses += 1 self.save(update_fields=['current_uses']) # Импортируем здесь, чтобы избежать циклического импорта from .application import DiscountApplication