Merge branch 'more_admins'

This commit is contained in:
Christopher Neugebauer 2016-03-31 14:39:54 +11:00
commit 4c7024c9ff
4 changed files with 365 additions and 48 deletions

View file

@ -1,4 +1,5 @@
from django.contrib import admin from django.contrib import admin
from django.core.exceptions import ObjectDoesNotExist
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import nested_admin import nested_admin
@ -6,21 +7,36 @@ import nested_admin
from registrasion import models as rego from registrasion import models as rego
class EffectsDisplayMixin(object):
def effects(self, obj):
return list(obj.effects())
# Inventory admin # Inventory admin
class ProductInline(admin.TabularInline): class ProductInline(admin.TabularInline):
model = rego.Product model = rego.Product
ordering = ("order", )
@admin.register(rego.Category) @admin.register(rego.Category)
class CategoryAdmin(admin.ModelAdmin): class CategoryAdmin(admin.ModelAdmin):
model = rego.Category model = rego.Category
verbose_name_plural = _("Categories") fields = ("name", "description", "required", "render_type",
"limit_per_user", "order",)
list_display = ("name", "description")
ordering = ("order", )
inlines = [ inlines = [
ProductInline, ProductInline,
] ]
admin.site.register(rego.Product)
@admin.register(rego.Product)
class ProductAdmin(admin.ModelAdmin):
model = rego.Product
list_display = ("name", "category", "description")
list_filter = ("category", )
ordering = ("category__order", "order", )
# Discounts # Discounts
@ -37,11 +53,34 @@ class DiscountForCategoryInline(admin.TabularInline):
verbose_name_plural = _("Categories included in discount") verbose_name_plural = _("Categories included in discount")
@admin.register( @admin.register(rego.TimeOrStockLimitDiscount)
rego.TimeOrStockLimitDiscount, class TimeOrStockLimitDiscountAdmin(admin.ModelAdmin, EffectsDisplayMixin):
rego.IncludedProductDiscount, list_display = (
"description",
"start_time",
"end_time",
"limit",
"effects",
) )
class DiscountAdmin(admin.ModelAdmin): ordering = ("start_time", "end_time", "limit")
inlines = [
DiscountForProductInline,
DiscountForCategoryInline,
]
@admin.register(rego.IncludedProductDiscount)
class IncludedProductDiscountAdmin(admin.ModelAdmin):
def enablers(self, obj):
return list(obj.enabling_products.all())
def effects(self, obj):
return list(obj.effects())
list_display = ("description", "enablers", "effects")
inlines = [ inlines = [
DiscountForProductInline, DiscountForProductInline,
DiscountForCategoryInline, DiscountForCategoryInline,
@ -75,7 +114,30 @@ class VoucherEnablingConditionInline(nested_admin.NestedStackedInline):
@admin.register(rego.Voucher) @admin.register(rego.Voucher)
class VoucherAdmin(nested_admin.NestedAdmin): class VoucherAdmin(nested_admin.NestedAdmin):
def effects(self, obj):
''' List the effects of the voucher in the admin. '''
out = []
try:
discount_effects = obj.voucherdiscount.effects()
except ObjectDoesNotExist:
discount_effects = None
try:
enabling_effects = obj.voucherenablingcondition.effects()
except ObjectDoesNotExist:
enabling_effects = None
if discount_effects:
out.append("Discounts: " + str(list(discount_effects)))
if enabling_effects:
out.append("Enables: " + str(list(enabling_effects)))
return "\n".join(out)
model = rego.Voucher model = rego.Voucher
list_display = ("recipient", "code", "effects")
inlines = [ inlines = [
VoucherDiscountInline, VoucherDiscountInline,
VoucherEnablingConditionInline, VoucherEnablingConditionInline,
@ -84,11 +146,46 @@ class VoucherAdmin(nested_admin.NestedAdmin):
# Enabling conditions # Enabling conditions
@admin.register(rego.ProductEnablingCondition) @admin.register(rego.ProductEnablingCondition)
class ProductEnablingConditionAdmin(nested_admin.NestedAdmin): class ProductEnablingConditionAdmin(
nested_admin.NestedAdmin,
EffectsDisplayMixin):
def enablers(self, obj):
return list(obj.enabling_products.all())
model = rego.ProductEnablingCondition model = rego.ProductEnablingCondition
fields = ("description", "enabling_products", "mandatory", "products",
"categories"),
list_display = ("description", "enablers", "effects")
# Enabling conditions # Enabling conditions
@admin.register(rego.CategoryEnablingCondition) @admin.register(rego.CategoryEnablingCondition)
class CategoryEnablingConditionAdmin(nested_admin.NestedAdmin): class CategoryEnablingConditionAdmin(
nested_admin.NestedAdmin,
EffectsDisplayMixin):
model = rego.CategoryEnablingCondition model = rego.CategoryEnablingCondition
fields = ("description", "enabling_category", "mandatory", "products",
"categories"),
list_display = ("description", "enabling_category", "effects")
ordering = ("enabling_category",)
# Enabling conditions
@admin.register(rego.TimeOrStockLimitEnablingCondition)
class TimeOrStockLimitEnablingConditionAdmin(
nested_admin.NestedAdmin,
EffectsDisplayMixin):
model = rego.TimeOrStockLimitEnablingCondition
list_display = (
"description",
"start_time",
"end_time",
"limit",
"effects",
)
ordering = ("start_time", "end_time", "limit")

View file

@ -0,0 +1,113 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import datetime
class Migration(migrations.Migration):
dependencies = [
('registrasion', '0008_cart_released'),
]
operations = [
migrations.AlterModelOptions(
name='category',
options={'verbose_name_plural': 'categories'},
),
migrations.AlterModelOptions(
name='timeorstocklimitenablingcondition',
options={'verbose_name': 'ceiling'},
),
migrations.AlterField(
model_name='category',
name='limit_per_user',
field=models.PositiveIntegerField(help_text='The total number of items from this category one attendee may purchase.', null=True, verbose_name='Limit per user', blank=True),
),
migrations.AlterField(
model_name='category',
name='render_type',
field=models.IntegerField(help_text='The registration form will render this category in this style.', verbose_name='Render type', choices=[(1, 'Radio button'), (2, 'Quantity boxes')]),
),
migrations.AlterField(
model_name='category',
name='required',
field=models.BooleanField(help_text='If enabled, a user must select an item from this category.'),
),
migrations.AlterField(
model_name='categoryenablingcondition',
name='enabling_category',
field=models.ForeignKey(help_text='If a product from this category is purchased, this condition is met.', to='registrasion.Category'),
),
migrations.AlterField(
model_name='discountbase',
name='description',
field=models.CharField(help_text='A description of this discount. This will be included on invoices where this discount is applied.', max_length=255, verbose_name='Description'),
),
migrations.AlterField(
model_name='enablingconditionbase',
name='categories',
field=models.ManyToManyField(help_text='Categories whose products are enabled if this condition is met.', to='registrasion.Category', blank=True),
),
migrations.AlterField(
model_name='enablingconditionbase',
name='mandatory',
field=models.BooleanField(default=False, help_text='If there is at least one mandatory condition defined on a product or category, all such conditions must be met. Otherwise, at least one non-mandatory condition must be met.'),
),
migrations.AlterField(
model_name='enablingconditionbase',
name='products',
field=models.ManyToManyField(help_text='Products that are enabled if this condition is met.', to='registrasion.Product', blank=True),
),
migrations.AlterField(
model_name='includedproductdiscount',
name='enabling_products',
field=models.ManyToManyField(help_text='If one of these products are purchased, the discounts below will be enabled.', to='registrasion.Product', verbose_name='Including product'),
),
migrations.AlterField(
model_name='product',
name='description',
field=models.CharField(max_length=255, null=True, verbose_name='Description', blank=True),
),
migrations.AlterField(
model_name='product',
name='reservation_duration',
field=models.DurationField(default=datetime.timedelta(0, 3600), help_text='The length of time this product will be reserved before it is released for someone else to purchase.', verbose_name='Reservation duration'),
),
migrations.AlterField(
model_name='productenablingcondition',
name='enabling_products',
field=models.ManyToManyField(help_text='If one of these products are purchased, this condition is met.', to='registrasion.Product'),
),
migrations.AlterField(
model_name='timeorstocklimitdiscount',
name='end_time',
field=models.DateTimeField(help_text='This discount will only be available before this time.', null=True, verbose_name='End time', blank=True),
),
migrations.AlterField(
model_name='timeorstocklimitdiscount',
name='limit',
field=models.PositiveIntegerField(help_text='This discount may only be applied this many times.', null=True, verbose_name='Limit', blank=True),
),
migrations.AlterField(
model_name='timeorstocklimitdiscount',
name='start_time',
field=models.DateTimeField(help_text='This discount will only be available after this time.', null=True, verbose_name='Start time', blank=True),
),
migrations.AlterField(
model_name='timeorstocklimitenablingcondition',
name='end_time',
field=models.DateTimeField(help_text='Products included in this condition will only be available before this time.', null=True),
),
migrations.AlterField(
model_name='timeorstocklimitenablingcondition',
name='limit',
field=models.PositiveIntegerField(help_text='The number of items under this grouping that can be purchased.', null=True),
),
migrations.AlterField(
model_name='timeorstocklimitenablingcondition',
name='start_time',
field=models.DateTimeField(help_text='Products included in this condition will only be available after this time.', null=True),
),
]

View file

@ -1,6 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import datetime import datetime
import itertools
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
@ -118,6 +119,9 @@ class BadgeAndProfile(models.Model):
class Category(models.Model): class Category(models.Model):
''' Registration product categories ''' ''' Registration product categories '''
class Meta:
verbose_name_plural = _("categories")
def __str__(self): def __str__(self):
return self.name return self.name
@ -129,17 +133,35 @@ class Category(models.Model):
(RENDER_TYPE_QUANTITY, _("Quantity boxes")), (RENDER_TYPE_QUANTITY, _("Quantity boxes")),
] ]
name = models.CharField(max_length=65, verbose_name=_("Name")) name = models.CharField(
description = models.CharField(max_length=255, max_length=65,
verbose_name=_("Description")) verbose_name=_("Name"),
)
description = models.CharField(
max_length=255,
verbose_name=_("Description"),
)
limit_per_user = models.PositiveIntegerField( limit_per_user = models.PositiveIntegerField(
null=True, null=True,
blank=True, blank=True,
verbose_name=_("Limit per user")) verbose_name=_("Limit per user"),
required = models.BooleanField(blank=True) help_text=_("The total number of items from this category one "
order = models.PositiveIntegerField(verbose_name=("Display order")) "attendee may purchase."),
render_type = models.IntegerField(choices=CATEGORY_RENDER_TYPES, )
verbose_name=_("Render type")) 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"),
)
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 @python_2_unicode_compatible
@ -147,23 +169,41 @@ class Product(models.Model):
''' Registration products ''' ''' Registration products '''
def __str__(self): def __str__(self):
return self.name return "%s - %s" % (self.category.name, self.name)
name = models.CharField(max_length=65, verbose_name=_("Name")) name = models.CharField(
description = models.CharField(max_length=255, max_length=65,
verbose_name=_("Description")) verbose_name=_("Name"),
category = models.ForeignKey(Category, verbose_name=_("Product category")) )
price = models.DecimalField(max_digits=8, 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, decimal_places=2,
verbose_name=_("Price")) verbose_name=_("Price"),
)
limit_per_user = models.PositiveIntegerField( limit_per_user = models.PositiveIntegerField(
null=True, null=True,
blank=True, blank=True,
verbose_name=_("Limit per user")) verbose_name=_("Limit per user"),
)
reservation_duration = models.DurationField( reservation_duration = models.DurationField(
default=datetime.timedelta(hours=1), default=datetime.timedelta(hours=1),
verbose_name=_("Reservation duration")) verbose_name=_("Reservation duration"),
order = models.PositiveIntegerField(verbose_name=("Display order")) 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"),
)
@python_2_unicode_compatible @python_2_unicode_compatible
@ -206,8 +246,18 @@ class DiscountBase(models.Model):
def __str__(self): def __str__(self):
return "Discount: " + self.description return "Discount: " + self.description
description = models.CharField(max_length=255, def effects(self):
verbose_name=_("Description")) ''' 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 @python_2_unicode_compatible
@ -292,11 +342,23 @@ class TimeOrStockLimitDiscount(DiscountBase):
verbose_name = _("Promotional discount") verbose_name = _("Promotional discount")
start_time = models.DateTimeField( start_time = models.DateTimeField(
null=True, blank=True, verbose_name=_("Start time")) null=True,
blank=True,
verbose_name=_("Start time"),
help_text=_("This discount will only be available after this time."),
)
end_time = models.DateTimeField( end_time = models.DateTimeField(
null=True, blank=True, verbose_name=_("End time")) null=True,
blank=True,
verbose_name=_("End time"),
help_text=_("This discount will only be available before this time."),
)
limit = models.PositiveIntegerField( limit = models.PositiveIntegerField(
null=True, blank=True, verbose_name=_("Limit")) null=True,
blank=True,
verbose_name=_("Limit"),
help_text=_("This discount may only be applied this many times."),
)
class VoucherDiscount(DiscountBase): class VoucherDiscount(DiscountBase):
@ -306,7 +368,8 @@ class VoucherDiscount(DiscountBase):
voucher = models.OneToOneField( voucher = models.OneToOneField(
Voucher, Voucher,
on_delete=models.CASCADE, on_delete=models.CASCADE,
verbose_name=_("Voucher")) verbose_name=_("Voucher"),
)
class IncludedProductDiscount(DiscountBase): class IncludedProductDiscount(DiscountBase):
@ -318,7 +381,10 @@ class IncludedProductDiscount(DiscountBase):
enabling_products = models.ManyToManyField( enabling_products = models.ManyToManyField(
Product, Product,
verbose_name=_("Including product")) verbose_name=_("Including product"),
help_text=_("If one of these products are purchased, the discounts "
"below will be enabled."),
)
class RoleDiscount(object): class RoleDiscount(object):
@ -340,20 +406,54 @@ class EnablingConditionBase(models.Model):
objects = InheritanceManager() objects = InheritanceManager()
def __str__(self): def __str__(self):
return self.name return self.description
def effects(self):
''' Returns all of the items enabled by this condition. '''
return itertools.chain(self.products.all(), self.categories.all())
description = models.CharField(max_length=255) description = models.CharField(max_length=255)
mandatory = models.BooleanField(default=False) mandatory = models.BooleanField(
products = models.ManyToManyField(Product, blank=True) default=False,
categories = models.ManyToManyField(Category, blank=True) help_text=_("If there is at least one mandatory condition defined on "
"a product or category, all such conditions must be met. "
"Otherwise, at least one non-mandatory condition must be "
"met."),
)
products = models.ManyToManyField(
Product,
blank=True,
help_text=_("Products that are enabled if this condition is met."),
)
categories = models.ManyToManyField(
Category,
blank=True,
help_text=_("Categories whose products are enabled if this condition "
"is met."),
)
class TimeOrStockLimitEnablingCondition(EnablingConditionBase): class TimeOrStockLimitEnablingCondition(EnablingConditionBase):
''' Registration product ceilings ''' ''' Registration product ceilings '''
start_time = models.DateTimeField(null=True, verbose_name=_("Start time")) class Meta:
end_time = models.DateTimeField(null=True, verbose_name=_("End time")) verbose_name = _("ceiling")
limit = models.PositiveIntegerField(null=True, verbose_name=_("Limit"))
start_time = models.DateTimeField(
null=True,
help_text=_("Products included in this condition will only be "
"available after this time."),
)
end_time = models.DateTimeField(
null=True,
help_text=_("Products included in this condition will only be "
"available before this time."),
)
limit = models.PositiveIntegerField(
null=True,
help_text=_("The number of items under this grouping that can be "
"purchased."),
)
@python_2_unicode_compatible @python_2_unicode_compatible
@ -361,9 +461,13 @@ class ProductEnablingCondition(EnablingConditionBase):
''' The condition is met because a specific product is purchased. ''' ''' The condition is met because a specific product is purchased. '''
def __str__(self): def __str__(self):
return "Enabled by product: " return "Enabled by products: " + str(self.enabling_products.all())
enabling_products = models.ManyToManyField(Product) enabling_products = models.ManyToManyField(
Product,
help_text=_("If one of these products are purchased, this condition "
"is met."),
)
@python_2_unicode_compatible @python_2_unicode_compatible
@ -372,9 +476,13 @@ class CategoryEnablingCondition(EnablingConditionBase):
purchased. ''' purchased. '''
def __str__(self): def __str__(self):
return "Enabled by product in category: " return "Enabled by product in category: " + str(self.enabling_category)
enabling_category = models.ForeignKey(Category) enabling_category = models.ForeignKey(
Category,
help_text=_("If a product from this category is purchased, this "
"condition is met."),
)
@python_2_unicode_compatible @python_2_unicode_compatible

View file

@ -1,3 +1,2 @@
[flake8] [flake8]
exclude = registrasion/migrations/* exclude = registrasion/migrations/*, build/*