Files @ 252697b842c0
Branch filter:

Location: symposion_app/vendor/registrasion/registrasion/models/inventory.py

Joel Addison
Update to Django 2.2

Upgrade site and modules to Django 2.2. Remove and replace obsolete
functionality with current equivalents. Update requirements to latest
versions where possible. Remove unused dependencies.
import datetime

from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _


# Inventory Models

@python_2_unicode_compatible
class Category(models.Model):
    ''' Registration product categories, used as logical groupings for Products
    in registration forms.

    Attributes:
        name (str): The display name for the category.

        description (str): Some explanatory text for the category. This is
            displayed alongside the forms where your attendees choose their
            items.

        required (bool): Requires a user to select an item from this category
            during initial registration. You can use this, e.g., for making
            sure that the user has a ticket before they select whether they
            want a t-shirt.

        render_type (int): This is used to determine what sort of form the
            attendee will be presented with when choosing Products from this
            category. These may be either of the following:

            ``RENDER_TYPE_RADIO`` presents the Products in the Category as a
            list of radio buttons. At most one item can be chosen at a time.
            This works well when setting limit_per_user to 1.

            ``RENDER_TYPE_QUANTITY`` shows each Product next to an input field,
            where the user can specify a quantity of each Product type. This is
            useful for additional extras, like Dinner Tickets.

            ``RENDER_TYPE_ITEM_QUANTITY`` shows a select menu to select a
            Product type, and an input field, where the user can specify the
            quantity for that Product type. This is useful for categories that
            have a lot of options, from which the user is not going to select
            all of the options.

            ``RENDER_TYPE_CHECKBOX`` shows a checkbox beside each product.

        limit_per_user (Optional[int]): This restricts the number of items
            from this Category that each attendee may claim. This extends
            across multiple Invoices.

        order (int): An ascending order for displaying the Categories
            available. By convention, your Category for ticket types should
            have the lowest display order.
    '''

    class Meta:
        app_label = "registrasion"
        verbose_name = _("inventory - category")
        verbose_name_plural = _("inventory - categories")
        ordering = ("order", )

    def __str__(self):
        return self.name

    RENDER_TYPE_RADIO = 1
    RENDER_TYPE_QUANTITY = 2
    RENDER_TYPE_ITEM_QUANTITY = 3
    RENDER_TYPE_CHECKBOX = 4

    CATEGORY_RENDER_TYPES = [
        (RENDER_TYPE_RADIO, _("Radio button")),
        (RENDER_TYPE_QUANTITY, _("Quantity boxes")),
        (RENDER_TYPE_ITEM_QUANTITY, _("Product selector and quantity box")),
        (RENDER_TYPE_CHECKBOX, _("Checkbox button")),
    ]

    name = models.CharField(
        max_length=255,
        verbose_name=_("Name"),
    )
    description = models.TextField(
        verbose_name=_("Description"),
    )
    limit_per_user = models.PositiveIntegerField(
        null=True,
        blank=True,
        verbose_name=_("Limit per user"),
        help_text=_("The total number of items from this category one "
                    "attendee may purchase."),
    )
    required = models.BooleanField(
        blank=True,
        help_text=_("If enabled, a user must select an "
                    "item from this category."),
    )
    order = models.PositiveIntegerField(
        verbose_name=("Display order"),
        db_index=True,
    )
    render_type = models.IntegerField(
        choices=CATEGORY_RENDER_TYPES,
        verbose_name=_("Render type"),
        help_text=_("The registration form will render this category in this "
                    "style."),
    )


@python_2_unicode_compatible
class Product(models.Model):
    ''' Products make up the conference inventory.

    Attributes:
        name (str): The display name for the product.

        description (str): Some descriptive text that will help the user to
            understand the product when they're at the registration form.

        category (Category): The Category that this product will be grouped
            under.

        price (Decimal): The price that 1 unit of this product will sell for.
            Note that this should be the full price, before any discounts are
            applied.

        limit_per_user (Optional[int]): This restricts the number of this
            Product that each attendee may claim. This extends across multiple
            Invoices.

        reservation_duration (datetime): When a Product is added to the user's
            tentative registration, it is marked as unavailable for a period of
            time. This allows the user to build up their registration and then
            pay for it. This reservation duration determines how long an item
            should be allowed to be reserved whilst being unpaid.

        order (int): An ascending order for displaying the Products
            within each Category.

    '''

    class Meta:
        app_label = "registrasion"
        verbose_name = _("inventory - product")
        ordering = ("category__order", "order")

    def __str__(self):
        return "%s - %s" % (self.category.name, self.name)

    name = models.CharField(
        max_length=255,
        verbose_name=_("Name"),
    )
    description = models.TextField(
        verbose_name=_("Description"),
        null=True,
        blank=True,
    )
    category = models.ForeignKey(
        Category,
        verbose_name=_("Product category"),
        on_delete=models.CASCADE,
    )
    price = models.DecimalField(
        max_digits=8,
        decimal_places=2,
        verbose_name=_("Price"),
    )
    limit_per_user = models.PositiveIntegerField(
        null=True,
        blank=True,
        verbose_name=_("Limit per user"),
    )
    reservation_duration = models.DurationField(
        default=datetime.timedelta(hours=1),
        verbose_name=_("Reservation duration"),
        help_text=_("The length of time this product will be reserved before "
                    "it is released for someone else to purchase."),
    )
    order = models.PositiveIntegerField(
        verbose_name=("Display order"),
        db_index=True,
    )


@python_2_unicode_compatible
class Voucher(models.Model):
    ''' Vouchers are used to enable Discounts or Flags for the people who hold
    the voucher code.

    Attributes:
        recipient (str): A display string used to identify the holder of the
            voucher on the admin page.

        code (str): The string that is used to prove that the attendee holds
            this voucher.

        limit (int): The number of attendees who are permitted to hold this
            voucher.

     '''

    class Meta:
        app_label = "registrasion"

    # Vouchers reserve a cart for a fixed amount of time, so that
    # items may be added without the voucher being swiped by someone else
    RESERVATION_DURATION = datetime.timedelta(hours=1)

    def __str__(self):
        return "Voucher for %s" % self.recipient

    @classmethod
    def normalise_code(cls, code):
        return code.upper()

    def save(self, *a, **k):
        ''' Normalise the voucher code to be uppercase '''
        self.code = self.normalise_code(self.code)
        super(Voucher, self).save(*a, **k)

    recipient = models.CharField(max_length=64, verbose_name=_("Recipient"))
    code = models.CharField(max_length=16,
                            unique=True,
                            verbose_name=_("Voucher code"))
    limit = models.PositiveIntegerField(verbose_name=_("Voucher use limit"))