Changeset - 1333fcdea179
[Not reviewed]
0 1 0
Christopher Neugebauer - 8 years ago 2016-09-04 02:18:10
chrisjrn@gmail.com
Refactors flags and discount classes to be DRYer.
1 file changed with 75 insertions and 67 deletions:
0 comments (0 inline, 0 general)
registrasion/models/conditions.py
Show inline comments
 
import itertools
 

	
 
from . import inventory
 

	
 
from django.core.exceptions import ValidationError
 
from django.db import models
 
from django.utils.encoding import python_2_unicode_compatible
 
from django.utils.translation import ugettext_lazy as _
 
from model_utils.managers import InheritanceManager
 

	
 

	
 
# Product Modifiers
 
# Condition Types
 

	
 
class TimeOrStockLimitCondition(models.Model):
 
    ''' Attributes for a condition that is limited by timespan or a count of
 
    purchased or reserved items.
 

	
 
    Attributes:
 
        start_time (Optional[datetime]): When the condition should start being
 
            true.
 

	
 
        end_time (Optional[datetime]): When the condition should stop being
 
            true.
 

	
 
        limit (Optional[int]): How many items may fall under the condition
 
            the condition until it stops being false -- for all users.
 
    '''
 

	
 
    class Meta:
 
        abstract = True
 

	
 
    start_time = models.DateTimeField(
 
        null=True,
 
        blank=True,
 
        verbose_name=_("Start time"),
 
        help_text=_("When the condition should start being true"),
 
    )
 
    end_time = models.DateTimeField(
 
        null=True,
 
        blank=True,
 
        verbose_name=_("End time"),
 
        help_text=_("When the condition should stop being true."),
 
    )
 
    limit = models.PositiveIntegerField(
 
        null=True,
 
        blank=True,
 
        verbose_name=_("Limit"),
 
        help_text=_(
 
            "How many times this condition may be applied for all users."
 
        ),
 
    )
 

	
 

	
 
class VoucherCondition(models.Model):
 
    ''' A condition is met when a voucher code is in the current cart. '''
 

	
 
    class Meta:
 
        abstract = True
 

	
 
    voucher = models.OneToOneField(
 
        inventory.Voucher,
 
        on_delete=models.CASCADE,
 
        verbose_name=_("Voucher"),
 
        db_index=True,
 
    )
 

	
 

	
 
class IncludedProductCondition(models.Model):
 
    class Meta:
 
        abstract = True
 

	
 
    enabling_products = models.ManyToManyField(
 
        inventory.Product,
 
        verbose_name=_("Including product"),
 
        help_text=_("If one of these products are purchased, this condition "
 
                    "is met."),
 
    )
 

	
 

	
 
# Discounts
 

	
 
@python_2_unicode_compatible
 
class DiscountBase(models.Model):
 
    ''' Base class for discounts. This class is subclassed with special
 
    attributes which are used to determine whether or not the given discount
 
    is available to be added to the current cart.
 

	
 
    Attributes:
 
        description (str): Display text that appears on the attendee's Invoice
 
            when the discount is applied to a Product on that invoice.
 
    '''
 

	
...
 
@@ -145,110 +213,77 @@ class DiscountForCategory(models.Model):
 
        if len(cats) > 1:
 
            raise ValidationError(
 
                _("You may only have one discount line per category"))
 

	
 
    discount = models.ForeignKey(DiscountBase, on_delete=models.CASCADE)
 
    category = models.ForeignKey(inventory.Category, on_delete=models.CASCADE)
 
    percentage = models.DecimalField(
 
        max_digits=4,
 
        decimal_places=1)
 
    quantity = models.PositiveIntegerField()
 

	
 

	
 
class TimeOrStockLimitDiscount(DiscountBase):
 
class TimeOrStockLimitDiscount(TimeOrStockLimitCondition, DiscountBase):
 
    ''' Discounts that are generally available, but are limited by timespan or
 
    usage count. This is for e.g. Early Bird discounts.
 

	
 
    Attributes:
 
        start_time (Optional[datetime]): When the discount should start being
 
            offered.
 

	
 
        end_time (Optional[datetime]): When the discount should stop being
 
            offered.
 

	
 
        limit (Optional[int]): How many times the discount is allowed to be
 
            applied -- to all users.
 

	
 
    '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 
        verbose_name = _("discount (time/stock limit)")
 
        verbose_name_plural = _("discounts (time/stock limit)")
 

	
 
    start_time = models.DateTimeField(
 
        null=True,
 
        blank=True,
 
        verbose_name=_("Start time"),
 
        help_text=_("This discount will only be available after this time."),
 
    )
 
    end_time = models.DateTimeField(
 
        null=True,
 
        blank=True,
 
        verbose_name=_("End time"),
 
        help_text=_("This discount will only be available before this time."),
 
    )
 
    limit = models.PositiveIntegerField(
 
        null=True,
 
        blank=True,
 
        verbose_name=_("Limit"),
 
        help_text=_("This discount may only be applied this many times."),
 
    )
 

	
 

	
 
class VoucherDiscount(DiscountBase):
 
class VoucherDiscount(VoucherCondition, DiscountBase):
 
    ''' Discounts that are enabled when a voucher code is in the current
 
    cart. These are normally configured in the Admin page at the same time as
 
    creating a Voucher object.
 

	
 
    Attributes:
 
        voucher (inventory.Voucher): The voucher that enables this discount.
 

	
 
    '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 
        verbose_name = _("discount (enabled by voucher)")
 
        verbose_name_plural = _("discounts (enabled by voucher)")
 

	
 
    voucher = models.OneToOneField(
 
        inventory.Voucher,
 
        on_delete=models.CASCADE,
 
        verbose_name=_("Voucher"),
 
        db_index=True,
 
    )
 

	
 

	
 
class IncludedProductDiscount(DiscountBase):
 
class IncludedProductDiscount(IncludedProductCondition, DiscountBase):
 
    ''' Discounts that are enabled because another product has been purchased.
 
    e.g. A conference ticket includes a free t-shirt.
 

	
 
    Attributes:
 
        enabling_products ([inventory.Product, ...]): The products that enable
 
            the discount.
 

	
 
    '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 
        verbose_name = _("discount (product inclusions)")
 
        verbose_name_plural = _("discounts (product inclusions)")
 

	
 
    enabling_products = models.ManyToManyField(
 
        inventory.Product,
 
        verbose_name=_("Including product"),
 
        help_text=_("If one of these products are purchased, the discounts "
 
                    "below will be enabled."),
 
    )
 

	
 

	
 
class RoleDiscount(object):
 
    ''' Discounts that are enabled because the active user has a specific
 
    role. This is for e.g. volunteers who can get a discount ticket. '''
 
    # TODO: implement RoleDiscount
 
    pass
 

	
 

	
 
@python_2_unicode_compatible
 
class FlagBase(models.Model):
 
    ''' This defines a condition which allows products or categories to
 
    be made visible, or be prevented from being visible.
...
 
@@ -321,89 +356,64 @@ class FlagBase(models.Model):
 
        related_name="flagbase_set",
 
    )
 
    categories = models.ManyToManyField(
 
        inventory.Category,
 
        blank=True,
 
        help_text=_("Categories whose products are affected by this flag's "
 
                    "condition."
 
                    ),
 
        related_name="flagbase_set",
 
    )
 

	
 

	
 
class TimeOrStockLimitFlag(FlagBase):
 
class TimeOrStockLimitFlag(TimeOrStockLimitCondition, FlagBase):
 
    ''' Product groupings that can be used to enable a product during a
 
    specific date range, or when fewer than a limit of products have been
 
    sold.
 

	
 
    Attributes:
 
        start_time (Optional[datetime]): This condition is only met after this
 
            time.
 

	
 
        end_time (Optional[datetime]): This condition is only met before this
 
            time.
 

	
 
        limit (Optional[int]): The number of products that *all users* can
 
            purchase under this limit, regardless of their per-user limits.
 

	
 
    '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 
        verbose_name = _("flag (time/stock limit)")
 
        verbose_name_plural = _("flags (time/stock limit)")
 

	
 
    start_time = models.DateTimeField(
 
        null=True,
 
        blank=True,
 
        help_text=_("Products included in this condition will only be "
 
                    "available after this time."),
 
    )
 
    end_time = models.DateTimeField(
 
        null=True,
 
        blank=True,
 
        help_text=_("Products included in this condition will only be "
 
                    "available before this time."),
 
    )
 
    limit = models.PositiveIntegerField(
 
        null=True,
 
        blank=True,
 
        help_text=_("The number of items under this grouping that can be "
 
                    "purchased."),
 
    )
 

	
 

	
 
@python_2_unicode_compatible
 
class ProductFlag(FlagBase):
 
class ProductFlag(IncludedProductCondition, FlagBase):
 
    ''' The condition is met because a specific product is purchased.
 

	
 
    Attributes:
 
        enabling_products ([inventory.Product, ...]): The products that cause
 
            this condition to be met.
 
    '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 
        verbose_name = _("flag (dependency on product)")
 
        verbose_name_plural = _("flags (dependency on product)")
 

	
 
    def __str__(self):
 
        return "Enabled by products: " + str(self.enabling_products.all())
 

	
 
    enabling_products = models.ManyToManyField(
 
        inventory.Product,
 
        help_text=_("If one of these products are purchased, this condition "
 
                    "is met."),
 
    )
 

	
 

	
 
@python_2_unicode_compatible
 
class CategoryFlag(FlagBase):
 
    ''' The condition is met because a product in a particular product is
 
    purchased.
 

	
 
    Attributes:
 
        enabling_category (inventory.Category): The category that causes this
 
            condition to be met.
 
     '''
 

	
 
    class Meta:
...
 
@@ -413,33 +423,31 @@ class CategoryFlag(FlagBase):
 

	
 
    def __str__(self):
 
        return "Enabled by product in category: " + str(self.enabling_category)
 

	
 
    enabling_category = models.ForeignKey(
 
        inventory.Category,
 
        help_text=_("If a product from this category is purchased, this "
 
                    "condition is met."),
 
    )
 

	
 

	
 
@python_2_unicode_compatible
 
class VoucherFlag(FlagBase):
 
class VoucherFlag(VoucherCondition, FlagBase):
 
    ''' The condition is met because a Voucher is present. This is for e.g.
 
    enabling sponsor tickets. '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 
        verbose_name = _("flag (dependency on voucher)")
 
        verbose_name_plural = _("flags (dependency on voucher)")
 

	
 
    def __str__(self):
 
        return "Enabled by voucher: %s" % self.voucher
 

	
 
    voucher = models.OneToOneField(inventory.Voucher)
 

	
 

	
 
# @python_2_unicode_compatible
 
class RoleFlag(object):
 
    ''' The condition is met because the active user has a particular Role.
 
    This is for e.g. enabling Team tickets. '''
 
    # TODO: implement RoleFlag
 
    pass
0 comments (0 inline, 0 general)