Source code for shuup.discounts.models._coupon_codes

# This file is part of Shuup.
#
# Copyright (c) 2012-2021, Shuup Commerce Inc. All rights reserved.
#
# This source code is licensed under the OSL-3.0 license found in the
# LICENSE file in the root directory of this source tree.
import random
import string
from django.conf import settings
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _


@python_2_unicode_compatible
[docs]class CouponUsage(models.Model): coupon = models.ForeignKey(on_delete=models.CASCADE, to="discounts.CouponCode", related_name="usages") order = models.ForeignKey(on_delete=models.CASCADE, to="shuup.Order", related_name="discounts_coupon_usages") created_on = models.DateTimeField(auto_now_add=True, editable=False, verbose_name=_("created on")) def __str__(self): return "Code %s for %s" % (self.coupon, self.order) @classmethod
[docs] def add_usage(cls, order, coupon): return cls.objects.create(order=order, coupon=coupon)
@python_2_unicode_compatible
[docs]class CouponCode(models.Model): code = models.CharField(max_length=12) name_field = "code" shops = models.ManyToManyField("shuup.Shop", blank=True, verbose_name=_("shops")) usage_limit_customer = models.PositiveIntegerField( blank=True, null=True, verbose_name=_("usage limit per customer"), help_text=_("Limit the amount of usages per a single customer."), ) usage_limit = models.PositiveIntegerField( blank=True, null=True, verbose_name=_("usage limit"), help_text=_( "Set the absolute limit of usages for this coupon. " "If the limit is zero (0) coupon cannot be used." ), ) active = models.BooleanField(default=True, verbose_name=_("active")) created_by = models.ForeignKey( settings.AUTH_USER_MODEL, blank=True, null=True, related_name="+", on_delete=models.SET_NULL, verbose_name=_("created by"), ) modified_by = models.ForeignKey( settings.AUTH_USER_MODEL, blank=True, null=True, related_name="+", on_delete=models.SET_NULL, verbose_name=_("modified by"), ) created_on = models.DateTimeField(auto_now_add=True, editable=False, verbose_name=_("created on")) modified_on = models.DateTimeField(auto_now=True, editable=False, verbose_name=_("modified on")) def __str__(self): return self.code @classmethod
[docs] def generate_code(cls, length=6): if length > 12: length = 12 return "".join(random.choice(string.ascii_uppercase + string.digits) for _ in range(length))
@property def exhausted(self): val = bool(self.usage_limit and self.usages.count() >= self.usage_limit) return val @property def attached(self): return self.coupon_code_discounts.all().exists() @classmethod
[docs] def is_usable(cls, shop, code, customer): try: code = cls.objects.get(code__iexact=code, active=True, shops=shop) return code.can_use_code(shop, customer) except cls.DoesNotExist: return False
[docs] def can_use_code(self, shop, customer): if not self.shops.filter(id=shop.pk).exists(): return False if not self.active: return False if not self.attached: return False if self.usage_limit_customer: if not customer or customer.is_anonymous: return False if self.usages.filter(order__customer=customer, coupon=self).count() >= self.usage_limit_customer: return False return not self.exhausted
[docs] def use(self, order): return CouponUsage.add_usage(order=order, coupon=self)