Changeset - 539dbd35252f
registrasion/admin.py
Show inline comments
...
 
@@ -6,3 +6,4 @@ import nested_admin
 

	
 
from registrasion import models as rego
 
from registrasion.models import conditions
 
from registrasion.models import inventory
 

	
...
 
@@ -17,8 +18,8 @@ class EffectsDisplayMixin(object):
 
class ProductInline(admin.TabularInline):
 
    model = rego.Product
 
    model = inventory.Product
 

	
 

	
 
@admin.register(rego.Category)
 
@admin.register(inventory.Category)
 
class CategoryAdmin(admin.ModelAdmin):
 
    model = rego.Category
 
    model = inventory.Category
 
    fields = ("name", "description", "required", "render_type",
...
 
@@ -31,5 +32,5 @@ class CategoryAdmin(admin.ModelAdmin):
 

	
 
@admin.register(rego.Product)
 
@admin.register(inventory.Product)
 
class ProductAdmin(admin.ModelAdmin):
 
    model = rego.Product
 
    model = inventory.Product
 
    list_display = ("name", "category", "description")
...
 
@@ -41,3 +42,3 @@ class ProductAdmin(admin.ModelAdmin):
 
class DiscountForProductInline(admin.TabularInline):
 
    model = rego.DiscountForProduct
 
    model = conditions.DiscountForProduct
 
    verbose_name = _("Product included in discount")
...
 
@@ -47,3 +48,3 @@ class DiscountForProductInline(admin.TabularInline):
 
class DiscountForCategoryInline(admin.TabularInline):
 
    model = rego.DiscountForCategory
 
    model = conditions.DiscountForCategory
 
    verbose_name = _("Category included in discount")
...
 
@@ -52,3 +53,3 @@ class DiscountForCategoryInline(admin.TabularInline):
 

	
 
@admin.register(rego.TimeOrStockLimitDiscount)
 
@admin.register(conditions.TimeOrStockLimitDiscount)
 
class TimeOrStockLimitDiscountAdmin(admin.ModelAdmin, EffectsDisplayMixin):
...
 
@@ -69,3 +70,3 @@ class TimeOrStockLimitDiscountAdmin(admin.ModelAdmin, EffectsDisplayMixin):
 

	
 
@admin.register(rego.IncludedProductDiscount)
 
@admin.register(conditions.IncludedProductDiscount)
 
class IncludedProductDiscountAdmin(admin.ModelAdmin):
...
 
@@ -89,3 +90,3 @@ class IncludedProductDiscountAdmin(admin.ModelAdmin):
 
class VoucherDiscountInline(nested_admin.NestedStackedInline):
 
    model = rego.VoucherDiscount
 
    model = conditions.VoucherDiscount
 
    verbose_name = _("Discount")
...
 
@@ -102,3 +103,3 @@ class VoucherDiscountInline(nested_admin.NestedStackedInline):
 
class VoucherFlagInline(nested_admin.NestedStackedInline):
 
    model = rego.VoucherFlag
 
    model = conditions.VoucherFlag
 
    verbose_name = _("Product and category enabled by voucher")
...
 
@@ -111,3 +112,3 @@ class VoucherFlagInline(nested_admin.NestedStackedInline):
 

	
 
@admin.register(rego.Voucher)
 
@admin.register(inventory.Voucher)
 
class VoucherAdmin(nested_admin.NestedAdmin):
...
 
@@ -135,3 +136,3 @@ class VoucherAdmin(nested_admin.NestedAdmin):
 

	
 
    model = rego.Voucher
 
    model = inventory.Voucher
 
    list_display = ("recipient", "code", "effects")
...
 
@@ -144,3 +145,3 @@ class VoucherAdmin(nested_admin.NestedAdmin):
 
# Enabling conditions
 
@admin.register(rego.ProductFlag)
 
@admin.register(conditions.ProductFlag)
 
class ProductFlagAdmin(
...
 
@@ -152,3 +153,3 @@ class ProductFlagAdmin(
 

	
 
    model = rego.ProductFlag
 
    model = conditions.ProductFlag
 
    fields = ("description", "enabling_products", "condition", "products",
...
 
@@ -160,3 +161,3 @@ class ProductFlagAdmin(
 
# Enabling conditions
 
@admin.register(rego.CategoryFlag)
 
@admin.register(conditions.CategoryFlag)
 
class CategoryFlagAdmin(
...
 
@@ -165,3 +166,3 @@ class CategoryFlagAdmin(
 

	
 
    model = rego.CategoryFlag
 
    model = conditions.CategoryFlag
 
    fields = ("description", "enabling_category", "condition", "products",
...
 
@@ -174,3 +175,3 @@ class CategoryFlagAdmin(
 
# Enabling conditions
 
@admin.register(rego.TimeOrStockLimitFlag)
 
@admin.register(conditions.TimeOrStockLimitFlag)
 
class TimeOrStockLimitFlagAdmin(
...
 
@@ -178,3 +179,3 @@ class TimeOrStockLimitFlagAdmin(
 
        EffectsDisplayMixin):
 
    model = rego.TimeOrStockLimitFlag
 
    model = conditions.TimeOrStockLimitFlag
 

	
registrasion/controllers/cart.py
Show inline comments
...
 
@@ -11,4 +11,6 @@ from django.utils import timezone
 

	
 
from registrasion import models as rego
 
from registrasion.exceptions import CartValidationError
 
from registrasion.models import commerce
 
from registrasion.models import conditions
 
from registrasion.models import inventory
 

	
...
 
@@ -30,5 +32,5 @@ class CartController(object):
 
        try:
 
            existing = rego.Cart.objects.get(user=user, active=True)
 
            existing = commerce.Cart.objects.get(user=user, active=True)
 
        except ObjectDoesNotExist:
 
            existing = rego.Cart.objects.create(
 
            existing = commerce.Cart.objects.create(
 
                user=user,
...
 
@@ -49,6 +51,6 @@ class CartController(object):
 
        if len(self.cart.vouchers.all()) >= 1:
 
            reservations.append(rego.Voucher.RESERVATION_DURATION)
 
            reservations.append(inventory.Voucher.RESERVATION_DURATION)
 

	
 
        # Else, it's the maximum of the included products
 
        items = rego.ProductItem.objects.filter(cart=self.cart)
 
        items = commerce.ProductItem.objects.filter(cart=self.cart)
 
        agg = items.aggregate(Max("product__reservation_duration"))
...
 
@@ -81,3 +83,3 @@ class CartController(object):
 

	
 
        items_in_cart = rego.ProductItem.objects.filter(cart=self.cart)
 
        items_in_cart = commerce.ProductItem.objects.filter(cart=self.cart)
 
        items_in_cart = items_in_cart.select_related(
...
 
@@ -101,3 +103,3 @@ class CartController(object):
 
            try:
 
                product_item = rego.ProductItem.objects.get(
 
                product_item = commerce.ProductItem.objects.get(
 
                    cart=self.cart,
...
 
@@ -108,3 +110,3 @@ class CartController(object):
 
            except ObjectDoesNotExist:
 
                rego.ProductItem.objects.create(
 
                commerce.ProductItem.objects.create(
 
                    cart=self.cart,
...
 
@@ -178,3 +180,3 @@ class CartController(object):
 
        # Try and find the voucher
 
        voucher = rego.Voucher.objects.get(code=voucher_code.upper())
 
        voucher = inventory.Voucher.objects.get(code=voucher_code.upper())
 

	
...
 
@@ -195,3 +197,3 @@ class CartController(object):
 
        # Is voucher exhausted?
 
        active_carts = rego.Cart.reserved_carts()
 
        active_carts = commerce.Cart.reserved_carts()
 

	
...
 
@@ -240,3 +242,3 @@ class CartController(object):
 

	
 
        items = rego.ProductItem.objects.filter(cart=cart)
 
        items = commerce.ProductItem.objects.filter(cart=cart)
 

	
...
 
@@ -250,3 +252,3 @@ class CartController(object):
 
        # Validate the discounts
 
        discount_items = rego.DiscountItem.objects.filter(cart=cart)
 
        discount_items = commerce.DiscountItem.objects.filter(cart=cart)
 
        seen_discounts = set()
...
 
@@ -258,3 +260,3 @@ class CartController(object):
 
            seen_discounts.add(discount)
 
            real_discount = rego.DiscountBase.objects.get_subclass(
 
            real_discount = conditions.DiscountBase.objects.get_subclass(
 
                pk=discount.pk)
...
 
@@ -289,3 +291,3 @@ class CartController(object):
 
        # Fix products and discounts
 
        items = rego.ProductItem.objects.filter(cart=self.cart)
 
        items = commerce.ProductItem.objects.filter(cart=self.cart)
 
        items = items.select_related("product")
...
 
@@ -308,3 +310,3 @@ class CartController(object):
 
        # Delete the existing entries.
 
        rego.DiscountItem.objects.filter(cart=self.cart).delete()
 
        commerce.DiscountItem.objects.filter(cart=self.cart).delete()
 

	
...
 
@@ -333,3 +335,3 @@ class CartController(object):
 
            our product. '''
 
            if isinstance(discount.clause, rego.DiscountForCategory):
 
            if isinstance(discount.clause, conditions.DiscountForCategory):
 
                return discount.clause.category == product.category
...
 
@@ -358,3 +360,3 @@ class CartController(object):
 
            # with the quantity set to as much as we have in the cart
 
            discount_item = rego.DiscountItem.objects.create(
 
            discount_item = commerce.DiscountItem.objects.create(
 
                product=product,
registrasion/controllers/category.py
Show inline comments
 
from registrasion import models as rego
 
from registrasion.models import commerce
 
from registrasion.models import inventory
 

	
...
 
@@ -24,3 +25,5 @@ class CategoryController(object):
 
        if products is AllProducts:
 
            products = rego.Product.objects.all().select_related("category")
 
            products = inventory.Product.objects.all().select_related(
 
                "category",
 
            )
 

	
...
 
@@ -43,3 +46,3 @@ class CategoryController(object):
 

	
 
        carts = rego.Cart.objects.filter(
 
        carts = commerce.Cart.objects.filter(
 
            user=user,
...
 
@@ -49,3 +52,3 @@ class CategoryController(object):
 

	
 
        items = rego.ProductItem.objects.filter(
 
        items = commerce.ProductItem.objects.filter(
 
            cart__in=carts,
registrasion/controllers/conditions.py
Show inline comments
...
 
@@ -9,3 +9,5 @@ from django.utils import timezone
 

	
 
from registrasion import models as rego
 
from registrasion.models import commerce
 
from registrasion.models import conditions
 
from registrasion.models import inventory
 

	
...
 
@@ -31,11 +33,11 @@ class ConditionController(object):
 
        CONTROLLERS = {
 
            rego.CategoryFlag: CategoryConditionController,
 
            rego.IncludedProductDiscount: ProductConditionController,
 
            rego.ProductFlag: ProductConditionController,
 
            rego.TimeOrStockLimitDiscount:
 
            conditions.CategoryFlag: CategoryConditionController,
 
            conditions.IncludedProductDiscount: ProductConditionController,
 
            conditions.ProductFlag: ProductConditionController,
 
            conditions.TimeOrStockLimitDiscount:
 
                TimeOrStockLimitDiscountController,
 
            rego.TimeOrStockLimitFlag:
 
            conditions.TimeOrStockLimitFlag:
 
                TimeOrStockLimitFlagController,
 
            rego.VoucherDiscount: VoucherConditionController,
 
            rego.VoucherFlag: VoucherConditionController,
 
            conditions.VoucherDiscount: VoucherConditionController,
 
            conditions.VoucherFlag: VoucherConditionController,
 
        }
...
 
@@ -123,3 +125,3 @@ class ConditionController(object):
 
            cond_products = condition.products.all()
 
            from_category = rego.Product.objects.filter(
 
            from_category = inventory.Product.objects.filter(
 
                category__in=condition.categories.all(),
...
 
@@ -201,7 +203,7 @@ class CategoryConditionController(ConditionController):
 

	
 
        carts = rego.Cart.objects.filter(user=user, released=False)
 
        enabling_products = rego.Product.objects.filter(
 
        carts = commerce.Cart.objects.filter(user=user, released=False)
 
        enabling_products = inventory.Product.objects.filter(
 
            category=self.condition.enabling_category,
 
        )
 
        products_count = rego.ProductItem.objects.filter(
 
        products_count = commerce.ProductItem.objects.filter(
 
            cart__in=carts,
...
 
@@ -223,4 +225,4 @@ class ProductConditionController(ConditionController):
 

	
 
        carts = rego.Cart.objects.filter(user=user, released=False)
 
        products_count = rego.ProductItem.objects.filter(
 
        carts = commerce.Cart.objects.filter(user=user, released=False)
 
        products_count = commerce.ProductItem.objects.filter(
 
            cart__in=carts,
...
 
@@ -269,3 +271,3 @@ class TimeOrStockLimitConditionController(ConditionController):
 
        # We care about all reserved carts, but not the user's current cart
 
        reserved_carts = rego.Cart.reserved_carts()
 
        reserved_carts = commerce.Cart.reserved_carts()
 
        reserved_carts = reserved_carts.exclude(
...
 
@@ -286,3 +288,3 @@ class TimeOrStockLimitFlagController(
 
    def _items(self):
 
        category_products = rego.Product.objects.filter(
 
        category_products = inventory.Product.objects.filter(
 
            category__in=self.ceiling.categories.all(),
...
 
@@ -291,3 +293,3 @@ class TimeOrStockLimitFlagController(
 

	
 
        product_items = rego.ProductItem.objects.filter(
 
        product_items = commerce.ProductItem.objects.filter(
 
            product__in=products.all(),
...
 
@@ -300,3 +302,3 @@ class TimeOrStockLimitDiscountController(TimeOrStockLimitConditionController):
 
    def _items(self):
 
        discount_items = rego.DiscountItem.objects.filter(
 
        discount_items = commerce.DiscountItem.objects.filter(
 
            discount=self.ceiling,
...
 
@@ -314,3 +316,3 @@ class VoucherConditionController(ConditionController):
 
        ''' returns True if the user has the given voucher attached. '''
 
        carts_count = rego.Cart.objects.filter(
 
        carts_count = commerce.Cart.objects.filter(
 
            user=user,
registrasion/controllers/credit_note.py
Show inline comments
...
 
@@ -2,3 +2,3 @@ from django.db import transaction
 

	
 
from registrasion import models as rego
 
from registrasion.models import commerce
 

	
...
 
@@ -16,3 +16,3 @@ class CreditNoteController(object):
 

	
 
        credit_note = rego.CreditNote.objects.create(
 
        credit_note = commerce.CreditNote.objects.create(
 
            invoice=invoice,
...
 
@@ -41,3 +41,3 @@ class CreditNoteController(object):
 
        # Apply payment to invoice
 
        rego.CreditNoteApplication.objects.create(
 
        commerce.CreditNoteApplication.objects.create(
 
            parent=self.credit_note,
registrasion/controllers/discount.py
Show inline comments
...
 
@@ -3,3 +3,4 @@ import itertools
 
from conditions import ConditionController
 
from registrasion import models as rego
 
from registrasion.models import commerce
 
from registrasion.models import conditions
 

	
...
 
@@ -26,3 +27,3 @@ def available_discounts(user, categories, products):
 
    # discounts that match provided categories
 
    category_discounts = rego.DiscountForCategory.objects.filter(
 
    category_discounts = conditions.DiscountForCategory.objects.filter(
 
        category__in=categories
...
 
@@ -30,3 +31,3 @@ def available_discounts(user, categories, products):
 
    # discounts that match provided products
 
    product_discounts = rego.DiscountForProduct.objects.filter(
 
    product_discounts = conditions.DiscountForProduct.objects.filter(
 
        product__in=products
...
 
@@ -34,3 +35,3 @@ def available_discounts(user, categories, products):
 
    # discounts that match categories for provided products
 
    product_category_discounts = rego.DiscountForCategory.objects.filter(
 
    product_category_discounts = conditions.DiscountForCategory.objects.filter(
 
        category__in=(product.category for product in products)
...
 
@@ -62,3 +63,3 @@ def available_discounts(user, categories, products):
 
    for discount in potential_discounts:
 
        real_discount = rego.DiscountBase.objects.get_subclass(
 
        real_discount = conditions.DiscountBase.objects.get_subclass(
 
            pk=discount.discount.pk,
...
 
@@ -70,3 +71,3 @@ def available_discounts(user, categories, products):
 
        # is not available any more.
 
        past_uses = rego.DiscountItem.objects.filter(
 
        past_uses = commerce.DiscountItem.objects.filter(
 
            cart__user=user,
registrasion/controllers/invoice.py
Show inline comments
...
 
@@ -7,3 +7,5 @@ from django.utils import timezone
 

	
 
from registrasion import models as rego
 
from registrasion.models import commerce
 
from registrasion.models import conditions
 
from registrasion.models import people
 

	
...
 
@@ -27,4 +29,4 @@ class InvoiceController(object):
 
        try:
 
            invoice = rego.Invoice.objects.exclude(
 
                status=rego.Invoice.STATUS_VOID,
 
            invoice = commerce.Invoice.objects.exclude(
 
                status=commerce.Invoice.STATUS_VOID,
 
            ).get(
...
 
@@ -44,3 +46,3 @@ class InvoiceController(object):
 
    def void_all_invoices(cls, cart):
 
        invoices = rego.Invoice.objects.filter(cart=cart).all()
 
        invoices = commerce.Invoice.objects.filter(cart=cart).all()
 
        for invoice in invoices:
...
 
@@ -51,3 +53,3 @@ class InvoiceController(object):
 
        try:
 
            condition = rego.DiscountForProduct.objects.get(
 
            condition = conditions.DiscountForProduct.objects.get(
 
                discount=item.discount,
...
 
@@ -56,3 +58,3 @@ class InvoiceController(object):
 
        except ObjectDoesNotExist:
 
            condition = rego.DiscountForCategory.objects.get(
 
            condition = conditions.DiscountForCategory.objects.get(
 
                discount=item.discount,
...
 
@@ -77,3 +79,3 @@ class InvoiceController(object):
 
        # Get the invoice recipient
 
        profile = rego.AttendeeProfileBase.objects.get_subclass(
 
        profile = people.AttendeeProfileBase.objects.get_subclass(
 
            id=cart.user.attendee.attendeeprofilebase.id,
...
 
@@ -81,3 +83,3 @@ class InvoiceController(object):
 
        recipient = profile.invoice_recipient()
 
        invoice = rego.Invoice.objects.create(
 
        invoice = commerce.Invoice.objects.create(
 
            user=cart.user,
...
 
@@ -85,3 +87,3 @@ class InvoiceController(object):
 
            cart_revision=cart.revision,
 
            status=rego.Invoice.STATUS_UNPAID,
 
            status=commerce.Invoice.STATUS_UNPAID,
 
            value=Decimal(),
...
 
@@ -92,3 +94,3 @@ class InvoiceController(object):
 

	
 
        product_items = rego.ProductItem.objects.filter(cart=cart)
 
        product_items = commerce.ProductItem.objects.filter(cart=cart)
 

	
...
 
@@ -100,3 +102,3 @@ class InvoiceController(object):
 
        )
 
        discount_items = rego.DiscountItem.objects.filter(cart=cart)
 
        discount_items = commerce.DiscountItem.objects.filter(cart=cart)
 
        invoice_value = Decimal()
...
 
@@ -104,3 +106,3 @@ class InvoiceController(object):
 
            product = item.product
 
            line_item = rego.LineItem.objects.create(
 
            line_item = commerce.LineItem.objects.create(
 
                invoice=invoice,
...
 
@@ -114,3 +116,3 @@ class InvoiceController(object):
 
        for item in discount_items:
 
            line_item = rego.LineItem.objects.create(
 
            line_item = commerce.LineItem.objects.create(
 
                invoice=invoice,
...
 
@@ -172,3 +174,3 @@ class InvoiceController(object):
 

	
 
        payments = rego.PaymentBase.objects.filter(invoice=self.invoice)
 
        payments = commerce.PaymentBase.objects.filter(invoice=self.invoice)
 
        total_paid = payments.aggregate(Sum("amount"))["amount__sum"] or 0
...
 
@@ -182,3 +184,3 @@ class InvoiceController(object):
 
        total_paid = self.total_payments()
 
        num_payments = rego.PaymentBase.objects.filter(
 
        num_payments = commerce.PaymentBase.objects.filter(
 
            invoice=self.invoice,
...
 
@@ -187,3 +189,3 @@ class InvoiceController(object):
 

	
 
        if old_status == rego.Invoice.STATUS_UNPAID:
 
        if old_status == commerce.Invoice.STATUS_UNPAID:
 
            # Invoice had an amount owing
...
 
@@ -201,3 +203,3 @@ class InvoiceController(object):
 
                self._mark_void()
 
        elif old_status == rego.Invoice.STATUS_PAID:
 
        elif old_status == commerce.Invoice.STATUS_PAID:
 
            if remainder > 0:
...
 
@@ -206,6 +208,6 @@ class InvoiceController(object):
 
                self._mark_refunded()
 
        elif old_status == rego.Invoice.STATUS_REFUNDED:
 
        elif old_status == commerce.Invoice.STATUS_REFUNDED:
 
            # Should not ever change from here
 
            pass
 
        elif old_status == rego.Invoice.STATUS_VOID:
 
        elif old_status == commerce.Invoice.STATUS_VOID:
 
            # Should not ever change from here
...
 
@@ -220,3 +222,3 @@ class InvoiceController(object):
 
            cart.save()
 
        self.invoice.status = rego.Invoice.STATUS_PAID
 
        self.invoice.status = commerce.Invoice.STATUS_PAID
 
        self.invoice.save()
...
 
@@ -231,3 +233,3 @@ class InvoiceController(object):
 
            cart.save()
 
        self.invoice.status = rego.Invoice.STATUS_REFUNDED
 
        self.invoice.status = commerce.Invoice.STATUS_REFUNDED
 
        self.invoice.save()
...
 
@@ -237,3 +239,3 @@ class InvoiceController(object):
 
        necessary. '''
 
        self.invoice.status = rego.Invoice.STATUS_VOID
 
        self.invoice.status = commerce.Invoice.STATUS_VOID
 
        self.invoice.save()
registrasion/controllers/product.py
Show inline comments
...
 
@@ -3,3 +3,4 @@ import itertools
 
from django.db.models import Sum
 
from registrasion import models as rego
 
from registrasion.models import commerce
 
from registrasion.models import inventory
 

	
...
 
@@ -24,3 +25,3 @@ class ProductController(object):
 
        if category is not None:
 
            all_products = rego.Product.objects.filter(category=category)
 
            all_products = inventory.Product.objects.filter(category=category)
 
            all_products = all_products.select_related("category")
...
 
@@ -67,3 +68,3 @@ class ProductController(object):
 

	
 
        carts = rego.Cart.objects.filter(
 
        carts = commerce.Cart.objects.filter(
 
            user=user,
...
 
@@ -73,3 +74,3 @@ class ProductController(object):
 

	
 
        items = rego.ProductItem.objects.filter(
 
        items = commerce.ProductItem.objects.filter(
 
            cart__in=carts,
registrasion/forms.py
Show inline comments
 
import models as rego
 
from registrasion.models import commerce
 
from registrasion.models import inventory
 

	
...
 
@@ -16,4 +17,4 @@ class ApplyCreditNoteForm(forms.Form):
 
    def _unpaid_invoices_for_user(self):
 
        invoices = rego.Invoice.objects.filter(
 
            status=rego.Invoice.STATUS_UNPAID,
 
        invoices = commerce.Invoice.objects.filter(
 
            status=commerce.Invoice.STATUS_UNPAID,
 
            user=self.user,
...
 
@@ -27,3 +28,2 @@ class ApplyCreditNoteForm(forms.Form):
 
    invoice = forms.ChoiceField(
 
        #choices=_unpaid_invoices_for_user,
 
        required=True,
...
 
@@ -35,3 +35,3 @@ class ManualCreditNoteRefundForm(forms.ModelForm):
 
    class Meta:
 
        model = rego.ManualCreditNoteRefund
 
        model = commerce.ManualCreditNoteRefund
 
        fields = ["reference"]
...
 
@@ -42,3 +42,3 @@ class ManualPaymentForm(forms.ModelForm):
 
    class Meta:
 
        model = rego.ManualPayment
 
        model = commerce.ManualPayment
 
        fields = ["reference", "amount"]
...
 
@@ -170,4 +170,4 @@ def ProductsForm(category, products):
 
    RENDER_TYPES = {
 
        rego.Category.RENDER_TYPE_QUANTITY: _QuantityBoxProductsForm,
 
        rego.Category.RENDER_TYPE_RADIO: _RadioButtonProductsForm,
 
        inventory.Category.RENDER_TYPE_QUANTITY: _QuantityBoxProductsForm,
 
        inventory.Category.RENDER_TYPE_RADIO: _RadioButtonProductsForm,
 
    }
registrasion/models.py
Show inline comments
 
deleted file
registrasion/models/__init__.py
Show inline comments
 
new file 100644
 
from commerce import *  # NOQA
 
from conditions import *  # NOQA
 
from inventory import *  # NOQA
 
from people import *  # NOQA
registrasion/models/commerce.py
Show inline comments
 
new file 100644
 
from . import conditions
 
from . import inventory
 

	
 
from django.contrib.auth.models import User
 
from django.core.exceptions import ValidationError
 
from django.db import models
 
from django.db.models import F, Q
 
from django.utils import timezone
 
from django.utils.encoding import python_2_unicode_compatible
 
from django.utils.translation import ugettext_lazy as _
 
from model_utils.managers import InheritanceManager
 

	
 

	
 
# Commerce Models
 

	
 
@python_2_unicode_compatible
 
class Cart(models.Model):
 
    ''' Represents a set of product items that have been purchased, or are
 
    pending purchase. '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 
        index_together = [
 
            ("active", "time_last_updated"),
 
            ("active", "released"),
 
            ("active", "user"),
 
            ("released", "user"),
 
        ]
 

	
 
    def __str__(self):
 
        return "%d rev #%d" % (self.id, self.revision)
 

	
 
    user = models.ForeignKey(User)
 
    # ProductItems (foreign key)
 
    vouchers = models.ManyToManyField(inventory.Voucher, blank=True)
 
    time_last_updated = models.DateTimeField(
 
        db_index=True,
 
    )
 
    reservation_duration = models.DurationField()
 
    revision = models.PositiveIntegerField(default=1)
 
    active = models.BooleanField(
 
        default=True,
 
        db_index=True,
 
    )
 
    released = models.BooleanField(
 
        default=False,
 
        db_index=True
 
    )  # Refunds etc
 

	
 
    @classmethod
 
    def reserved_carts(cls):
 
        ''' Gets all carts that are 'reserved' '''
 
        return Cart.objects.filter(
 
            (Q(active=True) &
 
                Q(time_last_updated__gt=(
 
                    timezone.now()-F('reservation_duration')
 
                                        ))) |
 
            (Q(active=False) & Q(released=False))
 
        )
 

	
 

	
 
@python_2_unicode_compatible
 
class ProductItem(models.Model):
 
    ''' Represents a product-quantity pair in a Cart. '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 
        ordering = ("product", )
 

	
 
    def __str__(self):
 
        return "product: %s * %d in Cart: %s" % (
 
            self.product, self.quantity, self.cart)
 

	
 
    cart = models.ForeignKey(Cart)
 
    product = models.ForeignKey(inventory.Product)
 
    quantity = models.PositiveIntegerField(db_index=True)
 

	
 

	
 
@python_2_unicode_compatible
 
class DiscountItem(models.Model):
 
    ''' Represents a discount-product-quantity relation in a Cart. '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 
        ordering = ("product", )
 

	
 
    def __str__(self):
 
        return "%s: %s * %d in Cart: %s" % (
 
            self.discount, self.product, self.quantity, self.cart)
 

	
 
    cart = models.ForeignKey(Cart)
 
    product = models.ForeignKey(inventory.Product)
 
    discount = models.ForeignKey(conditions.DiscountBase)
 
    quantity = models.PositiveIntegerField()
 

	
 

	
 
@python_2_unicode_compatible
 
class Invoice(models.Model):
 
    ''' An invoice. Invoices can be automatically generated when checking out
 
    a Cart, in which case, it is attached to a given revision of a Cart. '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 

	
 
    STATUS_UNPAID = 1
 
    STATUS_PAID = 2
 
    STATUS_REFUNDED = 3
 
    STATUS_VOID = 4
 

	
 
    STATUS_TYPES = [
 
        (STATUS_UNPAID, _("Unpaid")),
 
        (STATUS_PAID, _("Paid")),
 
        (STATUS_REFUNDED, _("Refunded")),
 
        (STATUS_VOID, _("VOID")),
 
    ]
 

	
 
    def __str__(self):
 
        return "Invoice #%d" % self.id
 

	
 
    def clean(self):
 
        if self.cart is not None and self.cart_revision is None:
 
            raise ValidationError(
 
                "If this is a cart invoice, it must have a revision")
 

	
 
    @property
 
    def is_unpaid(self):
 
        return self.status == self.STATUS_UNPAID
 

	
 
    @property
 
    def is_void(self):
 
        return self.status == self.STATUS_VOID
 

	
 
    @property
 
    def is_paid(self):
 
        return self.status == self.STATUS_PAID
 

	
 
    @property
 
    def is_refunded(self):
 
        return self.status == self.STATUS_REFUNDED
 

	
 
    # Invoice Number
 
    user = models.ForeignKey(User)
 
    cart = models.ForeignKey(Cart, null=True)
 
    cart_revision = models.IntegerField(
 
        null=True,
 
        db_index=True,
 
    )
 
    # Line Items (foreign key)
 
    status = models.IntegerField(
 
        choices=STATUS_TYPES,
 
        db_index=True,
 
    )
 
    recipient = models.CharField(max_length=1024)
 
    issue_time = models.DateTimeField()
 
    due_time = models.DateTimeField()
 
    value = models.DecimalField(max_digits=8, decimal_places=2)
 

	
 

	
 
@python_2_unicode_compatible
 
class LineItem(models.Model):
 
    ''' Line items for an invoice. These are denormalised from the ProductItems
 
    and DiscountItems that belong to a cart (for consistency), but also allow
 
    for arbitrary line items when required. '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 
        ordering = ("id", )
 

	
 
    def __str__(self):
 
        return "Line: %s * %d @ %s" % (
 
            self.description, self.quantity, self.price)
 

	
 
    invoice = models.ForeignKey(Invoice)
 
    description = models.CharField(max_length=255)
 
    quantity = models.PositiveIntegerField()
 
    price = models.DecimalField(max_digits=8, decimal_places=2)
 
    product = models.ForeignKey(inventory.Product, null=True, blank=True)
 

	
 

	
 
@python_2_unicode_compatible
 
class PaymentBase(models.Model):
 
    ''' The base payment type for invoices. Payment apps should subclass this
 
    class to handle implementation-specific issues. '''
 

	
 
    class Meta:
 
        ordering = ("time", )
 

	
 
    objects = InheritanceManager()
 

	
 
    def __str__(self):
 
        return "Payment: ref=%s amount=%s" % (self.reference, self.amount)
 

	
 
    invoice = models.ForeignKey(Invoice)
 
    time = models.DateTimeField(default=timezone.now)
 
    reference = models.CharField(max_length=255)
 
    amount = models.DecimalField(max_digits=8, decimal_places=2)
 

	
 

	
 
class ManualPayment(PaymentBase):
 
    ''' Payments that are manually entered by staff. '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 

	
 

	
 
class CreditNote(PaymentBase):
 
    ''' Credit notes represent money accounted for in the system that do not
 
    belong to specific invoices. They may be paid into other invoices, or
 
    cashed out as refunds.
 

	
 
    Each CreditNote may either be used to pay towards another Invoice in the
 
    system (by attaching a CreditNoteApplication), or may be marked as
 
    refunded (by attaching a CreditNoteRefund).'''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 

	
 
    @classmethod
 
    def unclaimed(cls):
 
        return cls.objects.filter(
 
            creditnoteapplication=None,
 
            creditnoterefund=None,
 
        )
 

	
 
    @property
 
    def status(self):
 
        if self.is_unclaimed:
 
            return "Unclaimed"
 

	
 
        if hasattr(self, 'creditnoteapplication'):
 
            destination = self.creditnoteapplication.invoice.id
 
            return "Applied to invoice %d" % destination
 

	
 
        elif hasattr(self, 'creditnoterefund'):
 
            reference = self.creditnoterefund.reference
 
            print reference
 
            return "Refunded with reference: %s" % reference
 

	
 
        raise ValueError("This should never happen.")
 

	
 
    @property
 
    def is_unclaimed(self):
 
        return not (
 
            hasattr(self, 'creditnoterefund') or
 
            hasattr(self, 'creditnoteapplication')
 
        )
 

	
 
    @property
 
    def value(self):
 
        ''' Returns the value of the credit note. Because CreditNotes are
 
        implemented as PaymentBase objects internally, the amount is a
 
        negative payment against an invoice. '''
 
        return -self.amount
 

	
 

	
 
class CleanOnSave(object):
 

	
 
    def save(self, *a, **k):
 
        self.full_clean()
 
        super(CleanOnSave, self).save(*a, **k)
 

	
 

	
 
class CreditNoteApplication(CleanOnSave, PaymentBase):
 
    ''' Represents an application of a credit note to an Invoice. '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 

	
 
    def clean(self):
 
        if not hasattr(self, "parent"):
 
            return
 
        if hasattr(self.parent, 'creditnoterefund'):
 
            raise ValidationError(
 
                "Cannot apply a refunded credit note to an invoice"
 
            )
 

	
 
    parent = models.OneToOneField(CreditNote)
 

	
 

	
 
class CreditNoteRefund(CleanOnSave, models.Model):
 
    ''' Represents a refund of a credit note to an external payment.
 
    Credit notes may only be refunded in full. How those refunds are handled
 
    is left as an exercise to the payment app. '''
 

	
 
    def clean(self):
 
        if not hasattr(self, "parent"):
 
            return
 
        if hasattr(self.parent, 'creditnoteapplication'):
 
            raise ValidationError(
 
                "Cannot refund a credit note that has been paid to an invoice"
 
            )
 

	
 
    parent = models.OneToOneField(CreditNote)
 
    time = models.DateTimeField(default=timezone.now)
 
    reference = models.CharField(max_length=255)
 

	
 

	
 
class ManualCreditNoteRefund(CreditNoteRefund):
 
    ''' Credit notes that are entered by a staff member. '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 

	
 
    entered_by = models.ForeignKey(User)
registrasion/models/conditions.py
Show inline comments
 
new file 100644
 
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
 

	
 
@python_2_unicode_compatible
 
class DiscountBase(models.Model):
 
    ''' Base class for discounts. Each subclass has controller code that
 
    determines whether or not the given discount is available to be added to
 
    the current cart. '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 

	
 
    objects = InheritanceManager()
 

	
 
    def __str__(self):
 
        return "Discount: " + self.description
 

	
 
    def effects(self):
 
        ''' Returns all of the effects of this discount. '''
 
        products = self.discountforproduct_set.all()
 
        categories = self.discountforcategory_set.all()
 
        return itertools.chain(products, categories)
 

	
 
    description = models.CharField(
 
        max_length=255,
 
        verbose_name=_("Description"),
 
        help_text=_("A description of this discount. This will be included on "
 
                    "invoices where this discount is applied."),
 
        )
 

	
 

	
 
@python_2_unicode_compatible
 
class DiscountForProduct(models.Model):
 
    ''' Represents a discount on an individual product. Each Discount can
 
    contain multiple products and categories. Discounts can either be a
 
    percentage or a fixed amount, but not both. '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 

	
 
    def __str__(self):
 
        if self.percentage:
 
            return "%s%% off %s" % (self.percentage, self.product)
 
        elif self.price:
 
            return "$%s off %s" % (self.price, self.product)
 

	
 
    def clean(self):
 
        if self.percentage is None and self.price is None:
 
            raise ValidationError(
 
                _("Discount must have a percentage or a price."))
 
        elif self.percentage is not None and self.price is not None:
 
            raise ValidationError(
 
                _("Discount may only have a percentage or only a price."))
 

	
 
        prods = DiscountForProduct.objects.filter(
 
            discount=self.discount,
 
            product=self.product)
 
        cats = DiscountForCategory.objects.filter(
 
            discount=self.discount,
 
            category=self.product.category)
 
        if len(prods) > 1:
 
            raise ValidationError(
 
                _("You may only have one discount line per product"))
 
        if len(cats) != 0:
 
            raise ValidationError(
 
                _("You may only have one discount for "
 
                    "a product or its category"))
 

	
 
    discount = models.ForeignKey(DiscountBase, on_delete=models.CASCADE)
 
    product = models.ForeignKey(inventory.Product, on_delete=models.CASCADE)
 
    percentage = models.DecimalField(
 
        max_digits=4, decimal_places=1, null=True, blank=True)
 
    price = models.DecimalField(
 
        max_digits=8, decimal_places=2, null=True, blank=True)
 
    quantity = models.PositiveIntegerField()
 

	
 

	
 
@python_2_unicode_compatible
 
class DiscountForCategory(models.Model):
 
    ''' Represents a discount for a category of products. Each discount can
 
    contain multiple products. Category discounts can only be a percentage. '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 

	
 
    def __str__(self):
 
        return "%s%% off %s" % (self.percentage, self.category)
 

	
 
    def clean(self):
 
        prods = DiscountForProduct.objects.filter(
 
            discount=self.discount,
 
            product__category=self.category)
 
        cats = DiscountForCategory.objects.filter(
 
            discount=self.discount,
 
            category=self.category)
 
        if len(prods) != 0:
 
            raise ValidationError(
 
                _("You may only have one discount for "
 
                    "a product or its category"))
 
        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):
 
    ''' Discounts that are generally available, but are limited by timespan or
 
    usage count. This is for e.g. Early Bird discounts. '''
 

	
 
    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):
 
    ''' Discounts that are enabled when a voucher code is in the current
 
    cart. '''
 

	
 
    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):
 
    ''' Discounts that are enabled because another product has been purchased.
 
    e.g. A conference ticket includes a free t-shirt. '''
 

	
 
    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.
 

	
 
    The various subclasses of this can define the conditions that enable
 
    or disable products, by the following rules:
 

	
 
    If there is at least one 'disable if false' flag defined on a product or
 
    category, all such flag conditions must be met. If there is at least one
 
    'enable if true' flag, at least one such condition must be met.
 

	
 
    If both types of conditions exist on a product, both of these rules apply.
 
    '''
 

	
 
    class Meta:
 
        # TODO: make concrete once https://code.djangoproject.com/ticket/26488
 
        # is solved.
 
        abstract = True
 

	
 
    DISABLE_IF_FALSE = 1
 
    ENABLE_IF_TRUE = 2
 

	
 
    def __str__(self):
 
        return self.description
 

	
 
    def effects(self):
 
        ''' Returns all of the items affected by this condition. '''
 
        return itertools.chain(self.products.all(), self.categories.all())
 

	
 
    @property
 
    def is_disable_if_false(self):
 
        return self.condition == FlagBase.DISABLE_IF_FALSE
 

	
 
    @property
 
    def is_enable_if_true(self):
 
        return self.condition == FlagBase.ENABLE_IF_TRUE
 

	
 
    description = models.CharField(max_length=255)
 
    condition = models.IntegerField(
 
        default=ENABLE_IF_TRUE,
 
        choices=(
 
            (DISABLE_IF_FALSE, _("Disable if false")),
 
            (ENABLE_IF_TRUE, _("Enable if true")),
 
        ),
 
        help_text=_("If there is at least one 'disable if false' flag "
 
                    "defined on a product or category, all such flag "
 
                    " conditions must be met. If there is at least one "
 
                    "'enable if true' flag, at least one such condition must "
 
                    "be met. If both types of conditions exist on a product, "
 
                    "both of these rules apply."
 
                    ),
 
    )
 
    products = models.ManyToManyField(
 
        inventory.Product,
 
        blank=True,
 
        help_text=_("Products affected by this flag's condition."),
 
        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 EnablingConditionBase(FlagBase):
 
    ''' Reifies the abstract FlagBase. This is necessary because django
 
    prevents renaming base classes in migrations. '''
 
    # TODO: remove this, and make subclasses subclass FlagBase once
 
    # https://code.djangoproject.com/ticket/26488 is solved.
 

	
 
    class Meta:
 
        app_label = "registrasion"
 

	
 
    objects = InheritanceManager()
 

	
 

	
 
class TimeOrStockLimitFlag(EnablingConditionBase):
 
    ''' Registration product ceilings '''
 

	
 
    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(EnablingConditionBase):
 
    ''' The condition is met because a specific product is purchased. '''
 

	
 
    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(EnablingConditionBase):
 
    ''' The condition is met because a product in a particular product is
 
    purchased. '''
 

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

	
 
    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(EnablingConditionBase):
 
    ''' 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
registrasion/models/inventory.py
Show inline comments
 
new file 100644
 
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.
 

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

	
 
        display_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
 

	
 
    CATEGORY_RENDER_TYPES = [
 
        (RENDER_TYPE_RADIO, _("Radio button")),
 
        (RENDER_TYPE_QUANTITY, _("Quantity boxes")),
 
    ]
 

	
 
    name = models.CharField(
 
        max_length=65,
 
        verbose_name=_("Name"),
 
    )
 
    description = models.CharField(
 
        max_length=255,
 
        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):
 
    ''' Registration products '''
 

	
 
    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=65,
 
        verbose_name=_("Name"),
 
    )
 
    description = models.CharField(
 
        max_length=255,
 
        verbose_name=_("Description"),
 
        null=True,
 
        blank=True,
 
    )
 
    category = models.ForeignKey(
 
        Category,
 
        verbose_name=_("Product category")
 
    )
 
    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):
 
    ''' Registration vouchers '''
 

	
 
    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"))
registrasion/models/people.py
Show inline comments
 
new file 100644
 
from registrasion import util
 

	
 
from django.contrib.auth.models import User
 
from django.core.exceptions import ObjectDoesNotExist
 
from django.db import models
 
from django.utils.encoding import python_2_unicode_compatible
 
from model_utils.managers import InheritanceManager
 

	
 

	
 
# User models
 

	
 
@python_2_unicode_compatible
 
class Attendee(models.Model):
 
    ''' Miscellaneous user-related data. '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 

	
 
    def __str__(self):
 
        return "%s" % self.user
 

	
 
    @staticmethod
 
    def get_instance(user):
 
        ''' Returns the instance of attendee for the given user, or creates
 
        a new one. '''
 
        try:
 
            return Attendee.objects.get(user=user)
 
        except ObjectDoesNotExist:
 
            return Attendee.objects.create(user=user)
 

	
 
    def save(self, *a, **k):
 
        while not self.access_code:
 
            access_code = util.generate_access_code()
 
            if Attendee.objects.filter(access_code=access_code).count() == 0:
 
                self.access_code = access_code
 
        return super(Attendee, self).save(*a, **k)
 

	
 
    user = models.OneToOneField(User, on_delete=models.CASCADE)
 
    # Badge/profile is linked
 
    access_code = models.CharField(
 
        max_length=6,
 
        unique=True,
 
        db_index=True,
 
    )
 
    completed_registration = models.BooleanField(default=False)
 
    guided_categories_complete = models.ManyToManyField("category")
 

	
 

	
 
class AttendeeProfileBase(models.Model):
 
    ''' Information for an attendee's badge and related preferences.
 
    Subclass this in your Django site to ask for attendee information in your
 
    registration progess.
 
     '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 

	
 
    objects = InheritanceManager()
 

	
 
    @classmethod
 
    def name_field(cls):
 
        ''' This is used to pre-fill the attendee's name from the
 
        speaker profile. If it's None, that functionality is disabled. '''
 
        return None
 

	
 
    def invoice_recipient(self):
 
        ''' Returns a representation of this attendee profile for the purpose
 
        of rendering to an invoice. Override in subclasses. '''
 

	
 
        # Manual dispatch to subclass. Fleh.
 
        slf = AttendeeProfileBase.objects.get_subclass(id=self.id)
 
        # Actually compare the functions.
 
        if type(slf).invoice_recipient != type(self).invoice_recipient:
 
            return type(slf).invoice_recipient(slf)
 

	
 
        # Return a default
 
        return slf.attendee.user.username
 

	
 
    attendee = models.OneToOneField(Attendee, on_delete=models.CASCADE)
registrasion/templatetags/registrasion_tags.py
Show inline comments
 
from registrasion import models as rego
 
from registrasion.models import commerce
 
from registrasion.models import inventory
 
from registrasion.controllers.category import CategoryController
...
 
@@ -21,3 +22,3 @@ def available_credit(context):
 
    ''' Returns the amount of unclaimed credit available for this user. '''
 
    notes = rego.CreditNote.unclaimed().filter(
 
    notes = commerce.CreditNote.unclaimed().filter(
 
        invoice__user=context.request.user,
...
 
@@ -31,3 +32,3 @@ def invoices(context):
 
    ''' Returns all of the invoices that this user has. '''
 
    return rego.Invoice.objects.filter(cart__user=context.request.user)
 
    return commerce.Invoice.objects.filter(cart__user=context.request.user)
 

	
...
 
@@ -39,3 +40,3 @@ def items_pending(context):
 

	
 
    all_items = rego.ProductItem.objects.filter(
 
    all_items = commerce.ProductItem.objects.filter(
 
        cart__user=context.request.user,
...
 
@@ -57,3 +58,3 @@ def items_purchased(context, category=None):
 

	
 
    all_items = rego.ProductItem.objects.filter(
 
    all_items = commerce.ProductItem.objects.filter(
 
        cart__user=context.request.user,
...
 
@@ -67,3 +68,3 @@ def items_purchased(context, category=None):
 
    pq = all_items.values("product").annotate(quantity=Sum("quantity")).all()
 
    products = rego.Product.objects.all()
 
    products = inventory.Product.objects.all()
 
    out = []
registrasion/tests/controller_helpers.py
Show inline comments
...
 
@@ -3,3 +3,3 @@ from registrasion.controllers.credit_note import CreditNoteController
 
from registrasion.controllers.invoice import InvoiceController
 
from registrasion import models as rego
 
from registrasion.models import commerce
 

	
...
 
@@ -21,3 +21,3 @@ class TestingCartController(CartController):
 
        try:
 
            product_item = rego.ProductItem.objects.get(
 
            product_item = commerce.ProductItem.objects.get(
 
                cart=self.cart,
...
 
@@ -43,3 +43,3 @@ class TestingInvoiceController(InvoiceController):
 
        ''' Adds a payment '''
 
        rego.ManualPayment.objects.create(
 
        commerce.ManualPayment.objects.create(
 
            invoice=self.invoice,
...
 
@@ -55,3 +55,3 @@ class TestingCreditNoteController(CreditNoteController):
 
    def refund(self):
 
        rego.CreditNoteRefund.objects.create(
 
        commerce.CreditNoteRefund.objects.create(
 
            parent=self.credit_note,
registrasion/tests/test_cart.py
Show inline comments
...
 
@@ -9,3 +9,6 @@ from django.test import TestCase
 

	
 
from registrasion import models as rego
 
from registrasion.models import commerce
 
from registrasion.models import conditions
 
from registrasion.models import inventory
 
from registrasion.models import people
 
from registrasion.controllers.product import ProductController
...
 
@@ -38,9 +41,13 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
 

	
 
        attendee1 = rego.Attendee.get_instance(cls.USER_1)
 
        attendee1 = people.Attendee.get_instance(cls.USER_1)
 
        attendee1.save()
 
        profile1 = rego.AttendeeProfileBase.objects.create(attendee=attendee1)
 
        profile1 = people.AttendeeProfileBase.objects.create(
 
            attendee=attendee1,
 
        )
 
        profile1.save()
 
        attendee2 = rego.Attendee.get_instance(cls.USER_2)
 
        attendee2 = people.Attendee.get_instance(cls.USER_2)
 
        attendee2.save()
 
        profile2 = rego.AttendeeProfileBase.objects.create(attendee=attendee2)
 
        profile2 = people.AttendeeProfileBase.objects.create(
 
            attendee=attendee2,
 
        )
 
        profile2.save()
...
 
@@ -51,3 +58,3 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
 
        for i in xrange(2):
 
            cat = rego.Category.objects.create(
 
            cat = inventory.Category.objects.create(
 
                name="Category " + str(i + 1),
...
 
@@ -55,3 +62,3 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
 
                order=i,
 
                render_type=rego.Category.RENDER_TYPE_RADIO,
 
                render_type=inventory.Category.RENDER_TYPE_RADIO,
 
                required=False,
...
 
@@ -66,3 +73,3 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
 
        for i in xrange(4):
 
            prod = rego.Product.objects.create(
 
            prod = inventory.Product.objects.create(
 
                name="Product " + str(i + 1),
...
 
@@ -97,5 +104,5 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
 
    def make_ceiling(cls, name, limit=None, start_time=None, end_time=None):
 
        limit_ceiling = rego.TimeOrStockLimitFlag.objects.create(
 
        limit_ceiling = conditions.TimeOrStockLimitFlag.objects.create(
 
            description=name,
 
            condition=rego.FlagBase.DISABLE_IF_FALSE,
 
            condition=conditions.FlagBase.DISABLE_IF_FALSE,
 
            limit=limit,
...
 
@@ -111,5 +118,5 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
 
            cls, name, limit=None, start_time=None, end_time=None):
 
        limit_ceiling = rego.TimeOrStockLimitFlag.objects.create(
 
        limit_ceiling = conditions.TimeOrStockLimitFlag.objects.create(
 
            description=name,
 
            condition=rego.FlagBase.DISABLE_IF_FALSE,
 
            condition=conditions.FlagBase.DISABLE_IF_FALSE,
 
            limit=limit,
...
 
@@ -126,3 +133,3 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
 
            percentage=100):
 
        limit_ceiling = rego.TimeOrStockLimitDiscount.objects.create(
 
        limit_ceiling = conditions.TimeOrStockLimitDiscount.objects.create(
 
            description=name,
...
 
@@ -133,3 +140,3 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
 
        limit_ceiling.save()
 
        rego.DiscountForProduct.objects.create(
 
        conditions.DiscountForProduct.objects.create(
 
            discount=limit_ceiling,
...
 
@@ -142,3 +149,3 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
 
    def new_voucher(self, code="VOUCHER", limit=1):
 
        voucher = rego.Voucher.objects.create(
 
        voucher = inventory.Voucher.objects.create(
 
            recipient="Voucher recipient",
...
 
@@ -178,3 +185,3 @@ class BasicCartTests(RegistrationCartTestCase):
 
        # Count of products for a given user should be collapsed.
 
        items = rego.ProductItem.objects.filter(
 
        items = commerce.ProductItem.objects.filter(
 
            cart=current_cart.cart,
...
 
@@ -189,3 +196,3 @@ class BasicCartTests(RegistrationCartTestCase):
 
        def get_item():
 
            return rego.ProductItem.objects.get(
 
            return commerce.ProductItem.objects.get(
 
                cart=current_cart.cart,
registrasion/tests/test_ceilings.py
Show inline comments
...
 
@@ -8,3 +8,3 @@ from test_cart import RegistrationCartTestCase
 

	
 
from registrasion import models as rego
 
from registrasion.models import conditions
 

	
...
 
@@ -157,3 +157,3 @@ class CeilingsTestCases(RegistrationCartTestCase):
 

	
 
        discount = rego.VoucherDiscount.objects.create(
 
        discount = conditions.VoucherDiscount.objects.create(
 
            description="VOUCHER RECIPIENT",
...
 
@@ -162,3 +162,3 @@ class CeilingsTestCases(RegistrationCartTestCase):
 
        discount.save()
 
        rego.DiscountForProduct.objects.create(
 
        conditions.DiscountForProduct.objects.create(
 
            discount=discount,
registrasion/tests/test_discount.py
Show inline comments
...
 
@@ -4,3 +4,3 @@ from decimal import Decimal
 

	
 
from registrasion import models as rego
 
from registrasion.models import conditions
 
from registrasion.controllers import discount
...
 
@@ -21,3 +21,3 @@ class DiscountTestCase(RegistrationCartTestCase):
 
            ):
 
        discount = rego.IncludedProductDiscount.objects.create(
 
        discount = conditions.IncludedProductDiscount.objects.create(
 
            description="PROD_1 includes PROD_2 " + str(amount) + "%",
...
 
@@ -27,3 +27,3 @@ class DiscountTestCase(RegistrationCartTestCase):
 
        discount.save()
 
        rego.DiscountForProduct.objects.create(
 
        conditions.DiscountForProduct.objects.create(
 
            discount=discount,
...
 
@@ -41,3 +41,3 @@ class DiscountTestCase(RegistrationCartTestCase):
 
            ):
 
        discount = rego.IncludedProductDiscount.objects.create(
 
        discount = conditions.IncludedProductDiscount.objects.create(
 
            description="PROD_1 includes CAT_2 " + str(amount) + "%",
...
 
@@ -47,3 +47,3 @@ class DiscountTestCase(RegistrationCartTestCase):
 
        discount.save()
 
        rego.DiscountForCategory.objects.create(
 
        conditions.DiscountForCategory.objects.create(
 
            discount=discount,
...
 
@@ -61,3 +61,3 @@ class DiscountTestCase(RegistrationCartTestCase):
 
            ):
 
        discount = rego.IncludedProductDiscount.objects.create(
 
        discount = conditions.IncludedProductDiscount.objects.create(
 
            description="PROD_1 includes PROD_3 and PROD_4 " +
...
 
@@ -68,3 +68,3 @@ class DiscountTestCase(RegistrationCartTestCase):
 
        discount.save()
 
        rego.DiscountForProduct.objects.create(
 
        conditions.DiscountForProduct.objects.create(
 
            discount=discount,
...
 
@@ -74,3 +74,3 @@ class DiscountTestCase(RegistrationCartTestCase):
 
        ).save()
 
        rego.DiscountForProduct.objects.create(
 
        conditions.DiscountForProduct.objects.create(
 
            discount=discount,
registrasion/tests/test_flag.py
Show inline comments
...
 
@@ -4,3 +4,4 @@ from django.core.exceptions import ValidationError
 

	
 
from registrasion import models as rego
 
from registrasion.models import commerce
 
from registrasion.models import conditions
 
from registrasion.controllers.category import CategoryController
...
 
@@ -17,6 +18,6 @@ class FlagTestCases(RegistrationCartTestCase):
 
    @classmethod
 
    def add_product_flag(cls, condition=rego.FlagBase.ENABLE_IF_TRUE):
 
    def add_product_flag(cls, condition=conditions.FlagBase.ENABLE_IF_TRUE):
 
        ''' Adds a product flag condition: adding PROD_1 to a cart is
 
        predicated on adding PROD_2 beforehand. '''
 
        flag = rego.ProductFlag.objects.create(
 
        flag = conditions.ProductFlag.objects.create(
 
            description="Product condition",
...
 
@@ -30,6 +31,9 @@ class FlagTestCases(RegistrationCartTestCase):
 
    @classmethod
 
    def add_product_flag_on_category(cls, condition=rego.FlagBase.ENABLE_IF_TRUE):
 
    def add_product_flag_on_category(
 
            cls,
 
            condition=conditions.FlagBase.ENABLE_IF_TRUE,
 
            ):
 
        ''' Adds a product flag condition that operates on a category:
 
        adding an item from CAT_1 is predicated on adding PROD_3 beforehand '''
 
        flag = rego.ProductFlag.objects.create(
 
        flag = conditions.ProductFlag.objects.create(
 
            description="Product condition",
...
 
@@ -42,6 +46,6 @@ class FlagTestCases(RegistrationCartTestCase):
 

	
 
    def add_category_flag(cls, condition=rego.FlagBase.ENABLE_IF_TRUE):
 
    def add_category_flag(cls, condition=conditions.FlagBase.ENABLE_IF_TRUE):
 
        ''' Adds a category flag condition: adding PROD_1 to a cart is
 
        predicated on adding an item from CAT_2 beforehand.'''
 
        flag = rego.CategoryFlag.objects.create(
 
        flag = conditions.CategoryFlag.objects.create(
 
            description="Category condition",
...
 
@@ -133,4 +137,4 @@ class FlagTestCases(RegistrationCartTestCase):
 
    def test_multiple_dif_conditions(self):
 
        self.add_product_flag(condition=rego.FlagBase.DISABLE_IF_FALSE)
 
        self.add_category_flag(condition=rego.FlagBase.DISABLE_IF_FALSE)
 
        self.add_product_flag(condition=conditions.FlagBase.DISABLE_IF_FALSE)
 
        self.add_category_flag(condition=conditions.FlagBase.DISABLE_IF_FALSE)
 

	
...
 
@@ -147,4 +151,4 @@ class FlagTestCases(RegistrationCartTestCase):
 
    def test_eit_and_dif_conditions_work_together(self):
 
        self.add_product_flag(condition=rego.FlagBase.ENABLE_IF_TRUE)
 
        self.add_category_flag(condition=rego.FlagBase.DISABLE_IF_FALSE)
 
        self.add_product_flag(condition=conditions.FlagBase.ENABLE_IF_TRUE)
 
        self.add_category_flag(condition=conditions.FlagBase.DISABLE_IF_FALSE)
 

	
...
 
@@ -202,3 +206,3 @@ class FlagTestCases(RegistrationCartTestCase):
 
    def test_available_products_on_category_works_when_condition_not_met(self):
 
        self.add_product_flag(condition=rego.FlagBase.ENABLE_IF_TRUE)
 
        self.add_product_flag(condition=conditions.FlagBase.ENABLE_IF_TRUE)
 

	
...
 
@@ -213,3 +217,3 @@ class FlagTestCases(RegistrationCartTestCase):
 
    def test_available_products_on_category_works_when_condition_is_met(self):
 
        self.add_product_flag(condition=rego.FlagBase.ENABLE_IF_TRUE)
 
        self.add_product_flag(condition=conditions.FlagBase.ENABLE_IF_TRUE)
 

	
...
 
@@ -227,3 +231,3 @@ class FlagTestCases(RegistrationCartTestCase):
 
    def test_available_products_on_products_works_when_condition_not_met(self):
 
        self.add_product_flag(condition=rego.FlagBase.ENABLE_IF_TRUE)
 
        self.add_product_flag(condition=conditions.FlagBase.ENABLE_IF_TRUE)
 

	
...
 
@@ -238,3 +242,3 @@ class FlagTestCases(RegistrationCartTestCase):
 
    def test_available_products_on_products_works_when_condition_is_met(self):
 
        self.add_product_flag(condition=rego.FlagBase.ENABLE_IF_TRUE)
 
        self.add_product_flag(condition=conditions.FlagBase.ENABLE_IF_TRUE)
 

	
...
 
@@ -252,3 +256,3 @@ class FlagTestCases(RegistrationCartTestCase):
 
    def test_category_flag_fails_if_cart_refunded(self):
 
        self.add_category_flag(condition=rego.FlagBase.ENABLE_IF_TRUE)
 
        self.add_category_flag(condition=conditions.FlagBase.ENABLE_IF_TRUE)
 

	
...
 
@@ -270,3 +274,3 @@ class FlagTestCases(RegistrationCartTestCase):
 
    def test_product_flag_fails_if_cart_refunded(self):
 
        self.add_product_flag(condition=rego.FlagBase.ENABLE_IF_TRUE)
 
        self.add_product_flag(condition=conditions.FlagBase.ENABLE_IF_TRUE)
 

	
...
 
@@ -288,3 +292,5 @@ class FlagTestCases(RegistrationCartTestCase):
 
    def test_available_categories(self):
 
        self.add_product_flag_on_category(condition=rego.FlagBase.ENABLE_IF_TRUE)
 
        self.add_product_flag_on_category(
 
            condition=conditions.FlagBase.ENABLE_IF_TRUE,
 
        )
 

	
...
 
@@ -309,3 +315,3 @@ class FlagTestCases(RegistrationCartTestCase):
 
    def test_validate_cart_when_flags_become_unmet(self):
 
        self.add_product_flag(condition=rego.FlagBase.ENABLE_IF_TRUE)
 
        self.add_product_flag(condition=conditions.FlagBase.ENABLE_IF_TRUE)
 

	
...
 
@@ -334,3 +340,3 @@ class FlagTestCases(RegistrationCartTestCase):
 
        # Should keep PROD_2 in the cart
 
        items = rego.ProductItem.objects.filter(cart=cart.cart)
 
        items = commerce.ProductItem.objects.filter(cart=cart.cart)
 
        self.assertFalse([i for i in items if i.product == self.PROD_1])
...
 
@@ -350,3 +356,3 @@ class FlagTestCases(RegistrationCartTestCase):
 
        # and also PROD_1, which is now exhausted for user.
 
        items = rego.ProductItem.objects.filter(cart=cart.cart)
 
        items = commerce.ProductItem.objects.filter(cart=cart.cart)
 
        self.assertTrue([i for i in items if i.product == self.PROD_1])
registrasion/tests/test_invoice.py
Show inline comments
...
 
@@ -6,3 +6,5 @@ from django.core.exceptions import ValidationError
 

	
 
from registrasion import models as rego
 
from registrasion.models import commerce
 
from registrasion.models import conditions
 
from registrasion.models import inventory
 
from controller_helpers import TestingCartController
...
 
@@ -25,3 +27,5 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        # That invoice should have a single line item
 
        line_items = rego.LineItem.objects.filter(invoice=invoice_1.invoice)
 
        line_items = commerce.LineItem.objects.filter(
 
            invoice=invoice_1.invoice,
 
        )
 
        self.assertEqual(1, len(line_items))
...
 
@@ -36,4 +40,4 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        # The old invoice should automatically be voided
 
        invoice_1_new = rego.Invoice.objects.get(pk=invoice_1.invoice.id)
 
        invoice_2_new = rego.Invoice.objects.get(pk=invoice_2.invoice.id)
 
        invoice_1_new = commerce.Invoice.objects.get(pk=invoice_1.invoice.id)
 
        invoice_2_new = commerce.Invoice.objects.get(pk=invoice_2.invoice.id)
 
        self.assertTrue(invoice_1_new.is_void)
...
 
@@ -42,3 +46,5 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        # Invoice should have two line items
 
        line_items = rego.LineItem.objects.filter(invoice=invoice_2.invoice)
 
        line_items = commerce.LineItem.objects.filter(
 
            invoice=invoice_2.invoice,
 
        )
 
        self.assertEqual(2, len(line_items))
...
 
@@ -81,3 +87,3 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
    def test_invoice_includes_discounts(self):
 
        voucher = rego.Voucher.objects.create(
 
        voucher = inventory.Voucher.objects.create(
 
            recipient="Voucher recipient",
...
 
@@ -86,3 +92,3 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        )
 
        discount = rego.VoucherDiscount.objects.create(
 
        discount = conditions.VoucherDiscount.objects.create(
 
            description="VOUCHER RECIPIENT",
...
 
@@ -90,3 +96,3 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        )
 
        rego.DiscountForProduct.objects.create(
 
        conditions.DiscountForProduct.objects.create(
 
            discount=discount,
...
 
@@ -105,3 +111,5 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        # That invoice should have two line items
 
        line_items = rego.LineItem.objects.filter(invoice=invoice_1.invoice)
 
        line_items = commerce.LineItem.objects.filter(
 
            invoice=invoice_1.invoice,
 
        )
 
        self.assertEqual(2, len(line_items))
...
 
@@ -113,3 +121,3 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
    def test_zero_value_invoice_is_automatically_paid(self):
 
        voucher = rego.Voucher.objects.create(
 
        voucher = inventory.Voucher.objects.create(
 
            recipient="Voucher recipient",
...
 
@@ -118,3 +126,3 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        )
 
        discount = rego.VoucherDiscount.objects.create(
 
        discount = conditions.VoucherDiscount.objects.create(
 
            description="VOUCHER RECIPIENT",
...
 
@@ -122,3 +130,3 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        )
 
        rego.DiscountForProduct.objects.create(
 
        conditions.DiscountForProduct.objects.create(
 
            discount=discount,
...
 
@@ -241,3 +249,5 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        # There should be a credit note generated out of the invoice.
 
        credit_notes = rego.CreditNote.objects.filter(invoice=invoice.invoice)
 
        credit_notes = commerce.CreditNote.objects.filter(
 
            invoice=invoice.invoice,
 
        )
 
        self.assertEqual(1, credit_notes.count())
...
 
@@ -259,3 +269,5 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        # There should be no credit notes
 
        credit_notes = rego.CreditNote.objects.filter(invoice=invoice.invoice)
 
        credit_notes = commerce.CreditNote.objects.filter(
 
            invoice=invoice.invoice,
 
        )
 
        self.assertEqual(0, credit_notes.count())
...
 
@@ -278,3 +290,5 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        # There should be a credit note generated out of the invoice.
 
        credit_notes = rego.CreditNote.objects.filter(invoice=invoice.invoice)
 
        credit_notes = commerce.CreditNote.objects.filter(
 
            invoice=invoice.invoice,
 
        )
 
        self.assertEqual(1, credit_notes.count())
...
 
@@ -299,3 +313,5 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        # There should be a credit note generated out of the invoice.
 
        credit_notes = rego.CreditNote.objects.filter(invoice=invoice.invoice)
 
        credit_notes = commerce.CreditNote.objects.filter(
 
            invoice=invoice.invoice,
 
        )
 
        self.assertEqual(1, credit_notes.count())
...
 
@@ -316,3 +332,3 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        # There should be one credit note generated out of the invoice.
 
        credit_note = rego.CreditNote.objects.get(invoice=invoice.invoice)
 
        credit_note = commerce.CreditNote.objects.get(invoice=invoice.invoice)
 
        cn = TestingCreditNoteController(credit_note)
...
 
@@ -320,3 +336,3 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        # That credit note should be in the unclaimed pile
 
        self.assertEquals(1, rego.CreditNote.unclaimed().count())
 
        self.assertEquals(1, commerce.CreditNote.unclaimed().count())
 

	
...
 
@@ -332,3 +348,3 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        # That invoice should not show up as unclaimed any more
 
        self.assertEquals(0, rego.CreditNote.unclaimed().count())
 
        self.assertEquals(0, commerce.CreditNote.unclaimed().count())
 

	
...
 
@@ -347,6 +363,6 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        # There should be one credit note generated out of the invoice.
 
        credit_note = rego.CreditNote.objects.get(invoice=invoice.invoice)
 
        credit_note = commerce.CreditNote.objects.get(invoice=invoice.invoice)
 
        cn = TestingCreditNoteController(credit_note)
 

	
 
        self.assertEquals(1, rego.CreditNote.unclaimed().count())
 
        self.assertEquals(1, commerce.CreditNote.unclaimed().count())
 

	
...
 
@@ -363,5 +379,7 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        # unclaimed should still be 1.
 
        self.assertEquals(1, rego.CreditNote.unclaimed().count())
 
        self.assertEquals(1, commerce.CreditNote.unclaimed().count())
 

	
 
        credit_note2 = rego.CreditNote.objects.get(invoice=invoice2.invoice)
 
        credit_note2 = commerce.CreditNote.objects.get(
 
            invoice=invoice2.invoice,
 
        )
 

	
...
 
@@ -387,3 +405,3 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        # There should be one credit note generated out of the invoice.
 
        credit_note = rego.CreditNote.objects.get(invoice=invoice.invoice)
 
        credit_note = commerce.CreditNote.objects.get(invoice=invoice.invoice)
 
        cn = TestingCreditNoteController(credit_note)
...
 
@@ -428,5 +446,5 @@ class InvoiceTestCase(RegistrationCartTestCase):
 

	
 
        self.assertEquals(1, rego.CreditNote.unclaimed().count())
 
        self.assertEquals(1, commerce.CreditNote.unclaimed().count())
 

	
 
        credit_note = rego.CreditNote.objects.get(invoice=invoice.invoice)
 
        credit_note = commerce.CreditNote.objects.get(invoice=invoice.invoice)
 

	
...
 
@@ -436,3 +454,3 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        # Refunding a credit note should mark it as claimed
 
        self.assertEquals(0, rego.CreditNote.unclaimed().count())
 
        self.assertEquals(0, commerce.CreditNote.unclaimed().count())
 

	
...
 
@@ -460,5 +478,5 @@ class InvoiceTestCase(RegistrationCartTestCase):
 

	
 
        self.assertEquals(1, rego.CreditNote.unclaimed().count())
 
        self.assertEquals(1, commerce.CreditNote.unclaimed().count())
 

	
 
        credit_note = rego.CreditNote.objects.get(invoice=invoice.invoice)
 
        credit_note = commerce.CreditNote.objects.get(invoice=invoice.invoice)
 

	
...
 
@@ -473,3 +491,3 @@ class InvoiceTestCase(RegistrationCartTestCase):
 

	
 
        self.assertEquals(0, rego.CreditNote.unclaimed().count())
 
        self.assertEquals(0, commerce.CreditNote.unclaimed().count())
 

	
registrasion/tests/test_voucher.py
Show inline comments
...
 
@@ -7,3 +7,4 @@ from django.db import IntegrityError
 

	
 
from registrasion import models as rego
 
from registrasion.models import conditions
 
from registrasion.models import inventory
 
from controller_helpers import TestingCartController
...
 
@@ -34,3 +35,3 @@ class VoucherTestCases(RegistrationCartTestCase):
 
        # user 2 should be able to apply voucher
 
        self.add_timedelta(rego.Voucher.RESERVATION_DURATION * 2)
 
        self.add_timedelta(inventory.Voucher.RESERVATION_DURATION * 2)
 
        cart_2.apply_voucher(voucher.code)
...
 
@@ -41,3 +42,3 @@ class VoucherTestCases(RegistrationCartTestCase):
 
        # it exceeds the number of vouchers available.
 
        self.add_timedelta(rego.Voucher.RESERVATION_DURATION * 2)
 
        self.add_timedelta(inventory.Voucher.RESERVATION_DURATION * 2)
 
        with self.assertRaises(ValidationError):
...
 
@@ -60,6 +61,6 @@ class VoucherTestCases(RegistrationCartTestCase):
 

	
 
        flag = rego.VoucherFlag.objects.create(
 
        flag = conditions.VoucherFlag.objects.create(
 
            description="Voucher condition",
 
            voucher=voucher,
 
            condition=rego.FlagBase.ENABLE_IF_TRUE,
 
            condition=conditions.FlagBase.ENABLE_IF_TRUE,
 
        )
...
 
@@ -81,3 +82,3 @@ class VoucherTestCases(RegistrationCartTestCase):
 

	
 
        discount = rego.VoucherDiscount.objects.create(
 
        discount = conditions.VoucherDiscount.objects.create(
 
            description="VOUCHER RECIPIENT",
...
 
@@ -86,3 +87,3 @@ class VoucherTestCases(RegistrationCartTestCase):
 
        discount.save()
 
        rego.DiscountForProduct.objects.create(
 
        conditions.DiscountForProduct.objects.create(
 
            discount=discount,
registrasion/views.py
Show inline comments
...
 
@@ -3,3 +3,5 @@ import sys
 
from registrasion import forms
 
from registrasion import models as rego
 
from registrasion.models import commerce
 
from registrasion.models import inventory
 
from registrasion.models import people
 
from registrasion.controllers import discount
...
 
@@ -62,3 +64,3 @@ def guided_registration(request, page_id=0):
 

	
 
    attendee = rego.Attendee.get_instance(request.user)
 
    attendee = people.Attendee.get_instance(request.user)
 

	
...
 
@@ -113,3 +115,3 @@ def guided_registration(request, page_id=0):
 
        # Get the next category
 
        cats = rego.Category.objects
 
        cats = inventory.Category.objects
 
        if SESSION_KEY in request.session:
...
 
@@ -136,3 +138,3 @@ def guided_registration(request, page_id=0):
 

	
 
        all_products = rego.Product.objects.filter(
 
        all_products = inventory.Product.objects.filter(
 
            category__in=cats,
...
 
@@ -219,3 +221,3 @@ def handle_profile(request, prefix):
 
    form was handled. '''
 
    attendee = rego.Attendee.get_instance(request.user)
 
    attendee = people.Attendee.get_instance(request.user)
 

	
...
 
@@ -223,3 +225,5 @@ def handle_profile(request, prefix):
 
        profile = attendee.attendeeprofilebase
 
        profile = rego.AttendeeProfileBase.objects.get_subclass(pk=profile.id)
 
        profile = people.AttendeeProfileBase.objects.get_subclass(
 
            pk=profile.id,
 
        )
 
    except ObjectDoesNotExist:
...
 
@@ -272,3 +276,3 @@ def product_category(request, category_id):
 
    category_id = int(category_id)  # Routing is [0-9]+
 
    category = rego.Category.objects.get(pk=category_id)
 
    category = inventory.Category.objects.get(pk=category_id)
 

	
...
 
@@ -318,3 +322,3 @@ def handle_products(request, category, products, prefix):
 
    # Create initial data for each of products in category
 
    items = rego.ProductItem.objects.filter(
 
    items = commerce.ProductItem.objects.filter(
 
        product__in=products,
...
 
@@ -346,4 +350,4 @@ def handle_products(request, category, products, prefix):
 
        if category.required:
 
            carts = rego.Cart.objects.filter(user=request.user)
 
            items = rego.ProductItem.objects.filter(
 
            carts = commerce.Cart.objects.filter(user=request.user)
 
            items = commerce.ProductItem.objects.filter(
 
                product__category=category,
...
 
@@ -368,3 +372,3 @@ def set_quantities_from_products_form(products_form, current_cart):
 
    pks = [i[0] for i in quantities]
 
    products = rego.Product.objects.filter(
 
    products = inventory.Product.objects.filter(
 
        id__in=pks,
...
 
@@ -386,3 +390,3 @@ def set_quantities_from_products_form(products_form, current_cart):
 
                field = field_names[product]
 
            elif isinstance(product, rego.Product):
 
            elif isinstance(product, inventory.Product):
 
                continue
...
 
@@ -404,3 +408,3 @@ def handle_voucher(request, prefix):
 
        voucher = voucher_form.cleaned_data["voucher"]
 
        voucher = rego.Voucher.normalise_code(voucher)
 
        voucher = inventory.Voucher.normalise_code(voucher)
 

	
...
 
@@ -459,5 +463,5 @@ def invoice_access(request, access_code):
 

	
 
    invoices = rego.Invoice.objects.filter(
 
    invoices = commerce.Invoice.objects.filter(
 
        user__attendee__access_code=access_code,
 
        status=rego.Invoice.STATUS_UNPAID,
 
        status=commerce.Invoice.STATUS_UNPAID,
 
    ).order_by("issue_time")
...
 
@@ -480,3 +484,3 @@ def invoice(request, invoice_id, access_code=None):
 
    invoice_id = int(invoice_id)
 
    inv = rego.Invoice.objects.get(pk=invoice_id)
 
    inv = commerce.Invoice.objects.get(pk=invoice_id)
 

	
...
 
@@ -507,3 +511,3 @@ def manual_payment(request, invoice_id):
 
    invoice_id = int(invoice_id)
 
    inv = get_object_or_404(rego.Invoice, pk=invoice_id)
 
    inv = get_object_or_404(commerce.Invoice, pk=invoice_id)
 
    current_invoice = InvoiceController(inv)
...
 
@@ -538,3 +542,3 @@ def refund(request, invoice_id):
 
    invoice_id = int(invoice_id)
 
    inv = get_object_or_404(rego.Invoice, pk=invoice_id)
 
    inv = get_object_or_404(commerce.Invoice, pk=invoice_id)
 
    current_invoice = InvoiceController(inv)
...
 
@@ -559,3 +563,3 @@ def credit_note(request, note_id, access_code=None):
 
    note_id = int(note_id)
 
    note = rego.CreditNote.objects.get(pk=note_id)
 
    note = commerce.CreditNote.objects.get(pk=note_id)
 

	
...
 
@@ -576,6 +580,7 @@ def credit_note(request, note_id, access_code=None):
 
        inv_id = apply_form.cleaned_data["invoice"]
 
        invoice = rego.Invoice.objects.get(pk=inv_id)
 
        invoice = commerce.Invoice.objects.get(pk=inv_id)
 
        current_note.apply_to_invoice(invoice)
 
        messages.success(request,
 
            "Applied credit note %d to invoice." % note_id
 
        messages.success(
 
            request,
 
            "Applied credit note %d to invoice." % note_id,
 
        )
...
 
@@ -587,3 +592,4 @@ def credit_note(request, note_id, access_code=None):
 
        refund_form.save()
 
        messages.success(request,
 
        messages.success(
 
            request,
 
            "Applied manual refund to credit note."
0 comments (0 inline, 0 general)