mirror of
https://github.com/pretix/pretix.git
synced 2026-05-09 15:54:03 +00:00
Rename flavors to variations
Add new event permissions
This commit is contained in:
@@ -19,26 +19,26 @@ Tixl is used by **users**, of which it knows two types:
|
|||||||
|
|
||||||
For more information about this user concept and reasons behind it, see the docstring of the ``tixlbase.models.User`` class.
|
For more information about this user concept and reasons behind it, see the docstring of the ``tixlbase.models.User`` class.
|
||||||
|
|
||||||
Items and flavors
|
Items and variations
|
||||||
^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
The purpose of tixl is to sell **items** (which belong to **events**) to **users**. An **item** is a abstract thing, popular examples being event tickets or a piece of merchandise, like 'T-Shirt'. An **item** can have multiple **properties** with multiple **values** each. For example, the **item** 'T-Shirt' could have the **property** 'Size' with **values** 'S', 'M' and 'L' and the **property** 'Color' with **values** 'black' and 'blue'.
|
The purpose of tixl is to sell **items** (which belong to **events**) to **users**. An **item** is a abstract thing, popular examples being event tickets or a piece of merchandise, like 'T-Shirt'. An **item** can have multiple **properties** with multiple **values** each. For example, the **item** 'T-Shirt' could have the **property** 'Size' with **values** 'S', 'M' and 'L' and the **property** 'Color' with **values** 'black' and 'blue'.
|
||||||
|
|
||||||
Any combination of those **values** is called a **flavor**. Using the examples from above, a possible **flavor** would be 'T-Shirt S blue'.
|
Any combination of those **values** is called a **variation**. Using the examples from above, a possible **variation** would be 'T-Shirt S blue'.
|
||||||
|
|
||||||
Restrictions
|
Restrictions
|
||||||
^^^^^^^^^^^^
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
The probably most powerful concepts of tixl is the very abstract concept of **restricitons**. We already know that **items** can come in very different **flavors**, but a **restriction** decides whether an item is available for sale and assign **prices** to **flavors**. There are **restriction types**, which are pieces of code implementing the restrictions and **restriction instances**, which are configurations made by the **organzier**. Although **restrictions** are a very abstract concept which can be used to do nearly anything, there are a few obvious examples:
|
The probably most powerful concepts of tixl is the very abstract concept of **restricitons**. We already know that **items** can come in very different **variations**, but a **restriction** decides whether an item is available for sale and assign **prices** to **variations**. There are **restriction types**, which are pieces of code implementing the restrictions and **restriction instances**, which are configurations made by the **organzier**. Although **restrictions** are a very abstract concept which can be used to do nearly anything, there are a few obvious examples:
|
||||||
|
|
||||||
* One easy example is the time restriction, which allows the sale of certain item flavors only within a certain time frame. As restrictions can also assign a price to a flavor, this can also be used to implement something like 'early-bird prices' for your tickets by using multiple time restrictions with different prices.
|
* One easy example is the time restriction, which allows the sale of certain item variations only within a certain time frame. As restrictions can also assign a price to a variation, this can also be used to implement something like 'early-bird prices' for your tickets by using multiple time restrictions with different prices.
|
||||||
* The most obvious example is the number restriction, which limits the sale of the tickets to a maximum number. You can use this either to stop selling tickets completely when your house is full or for creating limited 'VIP tickets'.
|
* The most obvious example is the number restriction, which limits the sale of the tickets to a maximum number. You can use this either to stop selling tickets completely when your house is full or for creating limited 'VIP tickets'.
|
||||||
* A more advanced example is a restriction by user, for example reduced ticket prices for members who are members of a special group.
|
* A more advanced example is a restriction by user, for example reduced ticket prices for members who are members of a special group.
|
||||||
* Arbitrary sophisticated features like coupon codes are also possible to be implemented using this feature.
|
* Arbitrary sophisticated features like coupon codes are also possible to be implemented using this feature.
|
||||||
|
|
||||||
Any number of **restrictions** can be applied to the whole of a **item** or to a specific **flavor**. The processing of the restriction follows the following set of rules:
|
Any number of **restrictions** can be applied to the whole of a **item** or to a specific **variation**. The processing of the restriction follows the following set of rules:
|
||||||
|
|
||||||
* **Flavor**-specific rules have precedence over **item**-specific rules.
|
* **Variation**-specific rules have precedence over **item**-specific rules.
|
||||||
* The restrictions are being processed in random order (there may not be any assumptions about the evaluation order).
|
* The restrictions are being processed in random order (there may not be any assumptions about the evaluation order).
|
||||||
* Multiple restriction instances of **different restriction types** are linked with *and*, so if both a time frame and a number restriction are applied to an item, the item is only avaliable for sale within the given time frame *and* only as long as items are available.
|
* Multiple restriction instances of **different restriction types** are linked with *and*, so if both a time frame and a number restriction are applied to an item, the item is only avaliable for sale within the given time frame *and* only as long as items are available.
|
||||||
* Multiple restriction instances of the **same restriction type** are linked with *or*, so if two time frames are applied to an item, the item is available for sale in both of the time frames. (This behaviour is actually a decision of the restriction type itself, so this rule is not enforced but rather a general rule of thumb).
|
* Multiple restriction instances of the **same restriction type** are linked with *or*, so if two time frames are applied to an item, the item is available for sale in both of the time frames. (This behaviour is actually a decision of the restriction type itself, so this rule is not enforced but rather a general rule of thumb).
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from django import forms
|
|||||||
|
|
||||||
from tixlbase.models import (
|
from tixlbase.models import (
|
||||||
User, Organizer, OrganizerPermission, Event, EventPermission,
|
User, Organizer, OrganizerPermission, Event, EventPermission,
|
||||||
Property, PropertyValue, Item, ItemFlavor
|
Property, PropertyValue, Item, ItemVariation
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -105,16 +105,16 @@ class PropertyAdmin(admin.ModelAdmin):
|
|||||||
search_fields = ('name', 'event')
|
search_fields = ('name', 'event')
|
||||||
|
|
||||||
|
|
||||||
class ItemFlavorInline(admin.TabularInline):
|
class ItemVariationInline(admin.TabularInline):
|
||||||
|
|
||||||
model = ItemFlavor
|
model = ItemVariation
|
||||||
extra = 4
|
extra = 4
|
||||||
|
|
||||||
|
|
||||||
class ItemAdmin(admin.ModelAdmin):
|
class ItemAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
model = Item
|
model = Item
|
||||||
inlines = [ItemFlavorInline]
|
inlines = [ItemVariationInline]
|
||||||
list_display = ('name', 'event', 'category')
|
list_display = ('name', 'event', 'category')
|
||||||
search_fields = ('name', 'event', 'category', 'short_description')
|
search_fields = ('name', 'event', 'category', 'short_description')
|
||||||
|
|
||||||
|
|||||||
30
src/tixlbase/migrations/0010_auto_20140927_1006.py
Normal file
30
src/tixlbase/migrations/0010_auto_20140927_1006.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('tixlbase', '0009_auto_20140916_2120'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='itemflavor',
|
||||||
|
name='prop',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='eventpermission',
|
||||||
|
name='can_change_items',
|
||||||
|
field=models.BooleanField(verbose_name='Can change item settings', default=True),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='itemflavor',
|
||||||
|
name='values',
|
||||||
|
field=models.ManyToManyField(to='tixlbase.PropertyValue', related_name='flavors'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
]
|
||||||
49
src/tixlbase/migrations/0011_auto_20140927_1013.py
Normal file
49
src/tixlbase/migrations/0011_auto_20140927_1013.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('tixlbase', '0010_auto_20140927_1006'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ItemVariation',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(serialize=False, primary_key=True, verbose_name='ID', auto_created=True)),
|
||||||
|
('active', models.BooleanField(default=True)),
|
||||||
|
('default_price', models.DecimalField(max_digits=7, decimal_places=2, blank=True, null=True, verbose_name='Default price')),
|
||||||
|
('item', models.ForeignKey(related_name='variations', to='tixlbase.Item')),
|
||||||
|
('values', models.ManyToManyField(related_name='variations', to='tixlbase.PropertyValue')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
},
|
||||||
|
bases=(models.Model,),
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='itemflavor',
|
||||||
|
name='item',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='itemflavor',
|
||||||
|
name='values',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='ItemFlavor',
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='item',
|
||||||
|
name='category',
|
||||||
|
field=models.ForeignKey(null=True, blank=True, on_delete=django.db.models.deletion.PROTECT, related_name='items', to='tixlbase.ItemCategory'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='item',
|
||||||
|
name='event',
|
||||||
|
field=models.ForeignKey(to='tixlbase.Event', on_delete=django.db.models.deletion.PROTECT, related_name='items'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -309,6 +309,10 @@ class EventPermission(models.Model):
|
|||||||
default=True,
|
default=True,
|
||||||
verbose_name=_("Can change event settings")
|
verbose_name=_("Can change event settings")
|
||||||
)
|
)
|
||||||
|
can_change_items = models.BooleanField(
|
||||||
|
default=True,
|
||||||
|
verbose_name=_("Can change item settings")
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return _("%(name)s on %(object)s") % {
|
return _("%(name)s on %(object)s") % {
|
||||||
@@ -400,11 +404,13 @@ class Item(models.Model):
|
|||||||
"""
|
"""
|
||||||
event = models.ForeignKey(
|
event = models.ForeignKey(
|
||||||
Event,
|
Event,
|
||||||
on_delete=models.PROTECT
|
on_delete=models.PROTECT,
|
||||||
|
related_name="items",
|
||||||
)
|
)
|
||||||
category = models.ForeignKey(
|
category = models.ForeignKey(
|
||||||
ItemCategory,
|
ItemCategory,
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
|
related_name="items",
|
||||||
blank=True, null=True
|
blank=True, null=True
|
||||||
)
|
)
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
@@ -450,32 +456,32 @@ class Item(models.Model):
|
|||||||
verbose_name_plural = _("Items")
|
verbose_name_plural = _("Items")
|
||||||
|
|
||||||
|
|
||||||
class ItemFlavor(models.Model):
|
class ItemVariation(models.Model):
|
||||||
"""
|
"""
|
||||||
A flavor is an item combined with values for all properties
|
A variation is an item combined with values for all properties
|
||||||
associated with the item. For example, if your item is 'T-Shirt'
|
associated with the item. For example, if your item is 'T-Shirt'
|
||||||
and your properties are 'Size' and 'Color', then an example for a
|
and your properties are 'Size' and 'Color', then an example for a
|
||||||
flavor would be 'T-Shirt XL read'.
|
variation would be 'T-Shirt XL read'.
|
||||||
|
|
||||||
Attention: _ALL_ combinations of PropertyValues _ALWAYS_ exist,
|
Attention: _ALL_ combinations of PropertyValues _ALWAYS_ exist,
|
||||||
even if there is no ItemFlavor object for them! ItemFlavor objects
|
even if there is no ItemVariation object for them! ItemVariation objects
|
||||||
do NOT prove existance, they are only available to make it possible
|
do NOT prove existance, they are only available to make it possible
|
||||||
to override default values (like the price) for certain combinations
|
to override default values (like the price) for certain combinations
|
||||||
of property values.
|
of property values.
|
||||||
|
|
||||||
They also allow to explicitly EXCLUDE certain combinations of property
|
They also allow to explicitly EXCLUDE certain combinations of property
|
||||||
values by creating an ItemFlavor object for them with active set to
|
values by creating an ItemVariation object for them with active set to
|
||||||
False.
|
False.
|
||||||
|
|
||||||
Restrictions can be not only set to items but also directly to flavors.
|
Restrictions can be not only set to items but also directly to variation.
|
||||||
"""
|
"""
|
||||||
item = models.ForeignKey(
|
item = models.ForeignKey(
|
||||||
Item,
|
Item,
|
||||||
related_name='flavors'
|
related_name='variations'
|
||||||
)
|
)
|
||||||
values = models.ManyToManyField(
|
values = models.ManyToManyField(
|
||||||
PropertyValue,
|
PropertyValue,
|
||||||
related_name='flavors',
|
related_name='variations',
|
||||||
)
|
)
|
||||||
active = models.BooleanField(
|
active = models.BooleanField(
|
||||||
default=True,
|
default=True,
|
||||||
|
|||||||
Reference in New Issue
Block a user