from decimal import Decimal from django.core.exceptions import ValidationError class DiscountValidator: """ Сервис для валидации скидок и промокодов. """ @staticmethod def validate_promo_code(code, customer=None, order_subtotal=None): """ Валидировать промокод. Args: code: Код промокода customer: Customer для проверки использований order_subtotal: Сумма заказа для проверки min_order_amount Returns: tuple: (is_valid, promo_code_or_none, error_message) """ from discounts.models import PromoCode if not code or not code.strip(): return False, None, "Промокод не указан" try: promo = PromoCode.objects.get( code__iexact=code.strip().upper(), is_active=True ) except PromoCode.DoesNotExist: return False, None, "Промокод не найден" # Проверяем валидность промокода is_valid, error = promo.is_valid(customer) if not is_valid: return False, None, error # Проверяем мин. сумму заказа if order_subtotal is not None and promo.discount.min_order_amount: if Decimal(order_subtotal) < promo.discount.min_order_amount: return False, None, f"Минимальная сумма заказа: {promo.discount.min_order_amount} руб." # Проверяем scope (только заказ, не товары) if promo.discount.scope not in ('order', 'product', 'category'): return False, None, "Этот тип промокода не поддерживается" return True, promo, None @staticmethod def validate_discount_for_order(discount, order): """ Проверить, можно ли применить скидку к заказу. Args: discount: Discount order: Order Returns: tuple: (is_valid, error_message) """ if not discount.is_active: return False, "Скидка неактивна" # Проверяем даты if not discount.is_valid_now(): return False, "Скидка недействительна" # Проверяем мин. сумму заказа if discount.scope == 'order' and discount.min_order_amount: if order.subtotal < discount.min_order_amount: return False, f"Минимальная сумма заказа: {discount.min_order_amount} руб." return True, None @staticmethod def validate_auto_discount_for_cart(discount, cart_subtotal, customer=None): """ Проверить, можно ли применить автоматическую скидку к корзине. Args: discount: Discount cart_subtotal: Десятичное число - сумма корзины customer: Customer (опционально) Returns: bool: True если скидка применима """ if not discount.is_auto: return False if not discount.is_valid_now(): return False if discount.scope == 'order' and discount.min_order_amount: if cart_subtotal < discount.min_order_amount: return False return True