commit
27d0e1c6be
5 changed files with 188 additions and 13 deletions
|
@ -97,6 +97,19 @@ class SpeakerDiscountAdmin(admin.ModelAdmin, EffectsDisplayMixin):
|
|||
]
|
||||
|
||||
|
||||
@admin.register(conditions.GroupMemberDiscount)
|
||||
class GroupMemberDiscountAdmin(admin.ModelAdmin, EffectsDisplayMixin):
|
||||
|
||||
fields = ("description", "group")
|
||||
|
||||
list_display = ("description", "effects")
|
||||
|
||||
inlines = [
|
||||
DiscountForProductInline,
|
||||
DiscountForCategoryInline,
|
||||
]
|
||||
|
||||
|
||||
# Vouchers
|
||||
|
||||
class VoucherDiscountInline(nested_admin.NestedStackedInline):
|
||||
|
@ -194,3 +207,11 @@ class SpeakerFlagAdmin(nested_admin.NestedAdmin, EffectsDisplayMixin):
|
|||
list_display = ("description", "is_presenter", "is_copresenter", "effects")
|
||||
|
||||
ordering = ("-is_presenter", "-is_copresenter")
|
||||
|
||||
|
||||
@admin.register(conditions.GroupMemberFlag)
|
||||
class GroupMemberFlagAdmin(admin.ModelAdmin, EffectsDisplayMixin):
|
||||
|
||||
fields = ("description", "group")
|
||||
|
||||
list_display = ("description", "effects")
|
||||
|
|
|
@ -23,6 +23,8 @@ class ConditionController(object):
|
|||
def _controllers():
|
||||
return {
|
||||
conditions.CategoryFlag: CategoryConditionController,
|
||||
conditions.GroupMemberDiscount: GroupMemberConditionController,
|
||||
conditions.GroupMemberFlag: GroupMemberConditionController,
|
||||
conditions.IncludedProductDiscount: ProductConditionController,
|
||||
conditions.ProductFlag: ProductConditionController,
|
||||
conditions.SpeakerFlag: SpeakerConditionController,
|
||||
|
@ -319,7 +321,19 @@ class SpeakerConditionController(IsMetByFilter, ConditionController):
|
|||
# User is a copresenter
|
||||
user_is_copresenter = Q(
|
||||
is_copresenter=True,
|
||||
proposal_kind__proposalbase__presentation__additional_speakers__user=u,
|
||||
proposal_kind__proposalbase__presentation__additional_speakers__user=u, # NOQA
|
||||
)
|
||||
|
||||
return queryset.filter(user_is_presenter | user_is_copresenter)
|
||||
|
||||
|
||||
class GroupMemberConditionController(IsMetByFilter, ConditionController):
|
||||
|
||||
@classmethod
|
||||
def pre_filter(self, conditions, user):
|
||||
''' Returns all of the items from conditions which are enabled by a
|
||||
user being member of a Django Auth Group. '''
|
||||
|
||||
return conditions.filter(
|
||||
group=user.groups.all(),
|
||||
)
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.2 on 2016-09-04 23:59
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('auth', '0007_alter_validators_add_error_messages'),
|
||||
('registrasion', '0003_auto_20160904_0235'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='GroupMemberDiscount',
|
||||
fields=[
|
||||
('discountbase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='registrasion.DiscountBase')),
|
||||
('group', models.ManyToManyField(help_text='The groups a user needs to be a member of for thiscondition to be met.', to='auth.Group')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'discount (group member)',
|
||||
'verbose_name_plural': 'discounts (group member)',
|
||||
},
|
||||
bases=('registrasion.discountbase', models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='GroupMemberFlag',
|
||||
fields=[
|
||||
('flagbase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='registrasion.FlagBase')),
|
||||
('group', models.ManyToManyField(help_text='The groups a user needs to be a member of for thiscondition to be met.', to='auth.Group')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'flag (group member)',
|
||||
'verbose_name_plural': 'flags (group member)',
|
||||
},
|
||||
bases=('registrasion.flagbase', models.Model),
|
||||
),
|
||||
]
|
|
@ -2,6 +2,7 @@ import itertools
|
|||
|
||||
from . import inventory
|
||||
|
||||
from django.contrib.auth.models import Group
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
@ -81,7 +82,7 @@ class IncludedProductCondition(models.Model):
|
|||
|
||||
class SpeakerCondition(models.Model):
|
||||
''' Conditions that are met if a user is a presenter, or copresenter,
|
||||
of a specific of presentation. '''
|
||||
of a specific kind of presentation. '''
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
@ -103,6 +104,20 @@ class SpeakerCondition(models.Model):
|
|||
)
|
||||
|
||||
|
||||
class GroupMemberCondition(models.Model):
|
||||
''' Conditions that are met if a user is a member (not declined or
|
||||
rejected) of a specific django auth group. '''
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
group = models.ManyToManyField(
|
||||
Group,
|
||||
help_text=_("The groups a user needs to be a member of for this"
|
||||
"condition to be met."),
|
||||
)
|
||||
|
||||
|
||||
# Discounts
|
||||
|
||||
@python_2_unicode_compatible
|
||||
|
@ -325,12 +340,23 @@ class SpeakerDiscount(SpeakerCondition, DiscountBase):
|
|||
verbose_name_plural = _("discounts (speaker)")
|
||||
|
||||
|
||||
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
|
||||
class GroupMemberDiscount(GroupMemberCondition, DiscountBase):
|
||||
''' Discounts that are enabled because the user is a member of a specific
|
||||
django auth Group.
|
||||
|
||||
Attributes:
|
||||
group ([Group, ...]): The condition should be met if the user is a
|
||||
member of one of these groups.
|
||||
|
||||
'''
|
||||
|
||||
class Meta:
|
||||
app_label = "registrasion"
|
||||
verbose_name = _("discount (group member)")
|
||||
verbose_name_plural = _("discounts (group member)")
|
||||
|
||||
|
||||
# Flags
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class FlagBase(models.Model):
|
||||
|
@ -517,9 +543,17 @@ class SpeakerFlag(SpeakerCondition, FlagBase):
|
|||
verbose_name_plural = _("flags (speaker)")
|
||||
|
||||
|
||||
# @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
|
||||
class GroupMemberFlag(GroupMemberCondition, FlagBase):
|
||||
''' Flag whose conditions are metbecause the user is a member of a specific
|
||||
django auth Group.
|
||||
|
||||
Attributes:
|
||||
group ([Group, ...]): The condition should be met if the user is a
|
||||
member of one of these groups.
|
||||
|
||||
'''
|
||||
|
||||
class Meta:
|
||||
app_label = "registrasion"
|
||||
verbose_name = _("flag (group member)")
|
||||
verbose_name_plural = _("flags (group member)")
|
||||
|
|
65
registrasion/tests/test_group_member.py
Normal file
65
registrasion/tests/test_group_member.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
import pytz
|
||||
|
||||
from django.contrib.auth.models import Group
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from registrasion.models import commerce
|
||||
from registrasion.models import conditions
|
||||
from registrasion.controllers.category import CategoryController
|
||||
from controller_helpers import TestingCartController
|
||||
from controller_helpers import TestingInvoiceController
|
||||
from registrasion.controllers.product import ProductController
|
||||
|
||||
from test_cart import RegistrationCartTestCase
|
||||
|
||||
UTC = pytz.timezone('UTC')
|
||||
|
||||
|
||||
class GroupMemberTestCase(RegistrationCartTestCase):
|
||||
|
||||
@classmethod
|
||||
def _create_group_and_flag(cls):
|
||||
''' Creates cls.GROUP, and restricts cls.PROD_1 only to users who are
|
||||
members of the group. '''
|
||||
|
||||
group = Group.objects.create(
|
||||
name="TEST GROUP",
|
||||
)
|
||||
|
||||
flag = conditions.GroupMemberFlag.objects.create(
|
||||
description="Group member flag",
|
||||
condition=conditions.FlagBase.ENABLE_IF_TRUE,
|
||||
)
|
||||
flag.group.add(group)
|
||||
flag.products.add(cls.PROD_1)
|
||||
|
||||
cls.GROUP = group
|
||||
|
||||
def test_product_not_enabled_until_user_joins_group(self):
|
||||
''' Tests that GroupMemberFlag disables a product for a user until
|
||||
they are a member of a specific group. '''
|
||||
|
||||
self._create_group_and_flag()
|
||||
|
||||
# USER_1 cannot see PROD_1 until they're in GROUP.
|
||||
available = ProductController.available_products(
|
||||
self.USER_1,
|
||||
products=[self.PROD_1],
|
||||
)
|
||||
self.assertNotIn(self.PROD_1, available)
|
||||
|
||||
self.USER_1.groups.add(self.GROUP)
|
||||
|
||||
# USER_1 cannot see PROD_1 until they're in GROUP.
|
||||
available = ProductController.available_products(
|
||||
self.USER_1,
|
||||
products=[self.PROD_1],
|
||||
)
|
||||
self.assertIn(self.PROD_1, available)
|
||||
|
||||
# USER_2 is still locked out
|
||||
available = ProductController.available_products(
|
||||
self.USER_2,
|
||||
products=[self.PROD_1],
|
||||
)
|
||||
self.assertNotIn(self.PROD_1, available)
|
Loading…
Reference in a new issue