mirror of
https://github.com/pretix/pretix.git
synced 2026-09-14 16:24:43 +00:00
Removed CleanerVersion layer [backwards-incompatible!]
This commit is contained in:
@@ -8,26 +8,6 @@ Pretix provides the following data(base) models. Every model and every model met
|
|||||||
documented here is considered private and should not be used by third-party plugins, as it may change
|
documented here is considered private and should not be used by third-party plugins, as it may change
|
||||||
without advance notice.
|
without advance notice.
|
||||||
|
|
||||||
.. IMPORTANT::
|
|
||||||
pretix's models are built with `cleanerversion`_, which extends the default Django ORM by adding versioning
|
|
||||||
information to the database. There are basically three things you absolutely need to know about cleanerversion:
|
|
||||||
|
|
||||||
* When querying the database, make sure you only get the current versions::
|
|
||||||
|
|
||||||
queryset = Model.objects.current.filter(…)
|
|
||||||
|
|
||||||
* Before you modify an object, clone it::
|
|
||||||
|
|
||||||
obj = Model.objects.current.get(identity=1) # Prefer identities over primary keys
|
|
||||||
obj = obj.clone() # Saves the old version to the database and creates the new one
|
|
||||||
obj.foo = 'bar'
|
|
||||||
obj.save()
|
|
||||||
|
|
||||||
* Beware of batch operations, use ``queryset.update()``, ``queryset.delete()`` etc. only if
|
|
||||||
you know what you're doing.
|
|
||||||
|
|
||||||
There is one exception: The ``User`` model is a classic Django model!
|
|
||||||
|
|
||||||
User model
|
User model
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|||||||
+13
-13
@@ -87,13 +87,13 @@ class JSONExporter(BaseExporter):
|
|||||||
},
|
},
|
||||||
'categories': [
|
'categories': [
|
||||||
{
|
{
|
||||||
'id': category.identity,
|
'id': category.id,
|
||||||
'name': str(category.name)
|
'name': str(category.name)
|
||||||
} for category in self.event.categories.current.all()
|
} for category in self.event.categories.all()
|
||||||
],
|
],
|
||||||
'items': [
|
'items': [
|
||||||
{
|
{
|
||||||
'id': item.identity,
|
'id': item.id,
|
||||||
'name': str(item.name),
|
'name': str(item.name),
|
||||||
'category': item.category_id,
|
'category': item.category_id,
|
||||||
'price': item.default_price,
|
'price': item.default_price,
|
||||||
@@ -101,20 +101,20 @@ class JSONExporter(BaseExporter):
|
|||||||
'active': item.active,
|
'active': item.active,
|
||||||
'variations': [
|
'variations': [
|
||||||
{
|
{
|
||||||
'id': variation.identity,
|
'id': variation.id,
|
||||||
'active': variation.active,
|
'active': variation.active,
|
||||||
'price': variation.default_price if variation.default_price is not None else item.default_price,
|
'price': variation.default_price if variation.default_price is not None else item.default_price,
|
||||||
'name': str(variation)
|
'name': str(variation)
|
||||||
} for variation in item.variations.current.all()
|
} for variation in item.variations.all()
|
||||||
]
|
]
|
||||||
} for item in self.event.items.current.all().prefetch_related('variations')
|
} for item in self.event.items.all().prefetch_related('variations')
|
||||||
],
|
],
|
||||||
'questions': [
|
'questions': [
|
||||||
{
|
{
|
||||||
'id': question.identity,
|
'id': question.id,
|
||||||
'question': str(question.question),
|
'question': str(question.question),
|
||||||
'type': question.type
|
'type': question.type
|
||||||
} for question in self.event.questions.current.all()
|
} for question in self.event.questions.all()
|
||||||
],
|
],
|
||||||
'orders': [
|
'orders': [
|
||||||
{
|
{
|
||||||
@@ -126,7 +126,7 @@ class JSONExporter(BaseExporter):
|
|||||||
'total': order.total,
|
'total': order.total,
|
||||||
'positions': [
|
'positions': [
|
||||||
{
|
{
|
||||||
'id': position.identity,
|
'id': position.id,
|
||||||
'item': position.item_id,
|
'item': position.item_id,
|
||||||
'variation': position.variation_id,
|
'variation': position.variation_id,
|
||||||
'price': position.price,
|
'price': position.price,
|
||||||
@@ -137,19 +137,19 @@ class JSONExporter(BaseExporter):
|
|||||||
'answer': answer.answer
|
'answer': answer.answer
|
||||||
} for answer in position.answers.all()
|
} for answer in position.answers.all()
|
||||||
]
|
]
|
||||||
} for position in order.positions.current.all()
|
} for position in order.positions.all()
|
||||||
]
|
]
|
||||||
} for order in
|
} for order in
|
||||||
self.event.orders.current.all().prefetch_related('positions', 'positions__answers').select_related(
|
self.event.orders.all().prefetch_related('positions', 'positions__answers').select_related(
|
||||||
'user')
|
'user')
|
||||||
],
|
],
|
||||||
'quotas': [
|
'quotas': [
|
||||||
{
|
{
|
||||||
'id': quota.identity,
|
'id': quota.id,
|
||||||
'size': quota.size,
|
'size': quota.size,
|
||||||
'items': [item.id for item in quota.items.all()],
|
'items': [item.id for item in quota.items.all()],
|
||||||
'variations': [variation.id for variation in quota.variations.all()],
|
'variations': [variation.id for variation in quota.variations.all()],
|
||||||
} for quota in self.event.quotas.current.all().prefetch_related('items', 'variations')
|
} for quota in self.event.quotas.all().prefetch_related('items', 'variations')
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,9 @@ from django import forms
|
|||||||
from django.core.files import File
|
from django.core.files import File
|
||||||
from django.core.files.storage import default_storage
|
from django.core.files.storage import default_storage
|
||||||
from django.core.files.uploadedfile import UploadedFile
|
from django.core.files.uploadedfile import UploadedFile
|
||||||
from django.db import models
|
|
||||||
from django.forms.models import BaseModelForm, ModelFormMetaclass
|
from django.forms.models import BaseModelForm, ModelFormMetaclass
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from versions.models import Versionable
|
|
||||||
|
|
||||||
from pretix.base.i18n import I18nFormField
|
from pretix.base.i18n import I18nFormField
|
||||||
from pretix.base.models import Event
|
from pretix.base.models import Event
|
||||||
@@ -30,44 +28,6 @@ class BaseI18nModelForm(BaseModelForm):
|
|||||||
field.widget.enabled_langcodes = event.settings.get('locales')
|
field.widget.enabled_langcodes = event.settings.get('locales')
|
||||||
|
|
||||||
|
|
||||||
class VersionedBaseModelForm(BaseI18nModelForm):
|
|
||||||
"""
|
|
||||||
This is a helperclass to construct VersionedModelForm
|
|
||||||
"""
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
instance = kwargs.get('instance', None)
|
|
||||||
self.original_instance = copy.copy(instance) if instance else None
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def save(self, commit=True):
|
|
||||||
if self.instance.pk is not None and isinstance(self.instance, Versionable):
|
|
||||||
if self.has_changed() and self.original_instance:
|
|
||||||
new = self.instance
|
|
||||||
old = self.original_instance
|
|
||||||
clone = old.clone()
|
|
||||||
for f in type(self.instance)._meta.get_fields():
|
|
||||||
if f.name not in (
|
|
||||||
'id', 'identity', 'version_start_date', 'version_end_date',
|
|
||||||
'version_birth_date'
|
|
||||||
) and not isinstance(f, (
|
|
||||||
models.ManyToOneRel, models.ManyToManyRel, models.ManyToManyField
|
|
||||||
)):
|
|
||||||
setattr(clone, f.name, getattr(new, f.name))
|
|
||||||
self.instance = clone
|
|
||||||
return super().save(commit)
|
|
||||||
|
|
||||||
|
|
||||||
class VersionedModelForm(six.with_metaclass(ModelFormMetaclass, VersionedBaseModelForm)):
|
|
||||||
"""
|
|
||||||
This is a modified version of I18nModelForm which differs from I18nModelForm in
|
|
||||||
only one way: It executes the .clone() method of an object before saving it back to
|
|
||||||
the database, if the model is a sub-class of versions.models.Versionable. You can
|
|
||||||
safely use this as a base class for all your model forms, it will work out correctly
|
|
||||||
with both versioned and non-versioned models.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class I18nModelForm(six.with_metaclass(ModelFormMetaclass, BaseI18nModelForm)):
|
class I18nModelForm(six.with_metaclass(ModelFormMetaclass, BaseI18nModelForm)):
|
||||||
"""
|
"""
|
||||||
This is a modified version of Django's ModelForm which differs from ModelForm in
|
This is a modified version of Django's ModelForm which differs from ModelForm in
|
||||||
|
|||||||
@@ -5,12 +5,23 @@ import uuid
|
|||||||
|
|
||||||
import django.core.validators
|
import django.core.validators
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import versions.models
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.contrib.auth.hashers import make_password
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
import pretix.base.i18n
|
import pretix.base.i18n
|
||||||
import pretix.base.models
|
import pretix.base.models.base
|
||||||
|
import pretix.base.models.items
|
||||||
|
import pretix.base.models.orders
|
||||||
|
|
||||||
|
|
||||||
|
def initial_user(apps, schema_editor):
|
||||||
|
User = apps.get_model("pretixbase", "User")
|
||||||
|
user = User(email='admin@localhost')
|
||||||
|
user.is_staff = True
|
||||||
|
user.is_superuser = True
|
||||||
|
user.password = make_password('admin')
|
||||||
|
user.save()
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
@@ -23,20 +34,20 @@ class Migration(migrations.Migration):
|
|||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='User',
|
name='User',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(serialize=False, verbose_name='ID', primary_key=True, auto_created=True)),
|
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
||||||
('password', models.CharField(verbose_name='password', max_length=128)),
|
('password', models.CharField(verbose_name='password', max_length=128)),
|
||||||
('last_login', models.DateTimeField(verbose_name='last login', null=True, blank=True)),
|
('last_login', models.DateTimeField(verbose_name='last login', blank=True, null=True)),
|
||||||
('is_superuser', models.BooleanField(default=False, verbose_name='superuser status', help_text='Designates that this user has all permissions without explicitly assigning them.')),
|
('is_superuser', models.BooleanField(verbose_name='superuser status', default=False, help_text='Designates that this user has all permissions without explicitly assigning them.')),
|
||||||
('email', models.EmailField(unique=True, verbose_name='E-mail', blank=True, db_index=True, max_length=254, null=True)),
|
('email', models.EmailField(max_length=254, blank=True, unique=True, verbose_name='E-mail', null=True, db_index=True)),
|
||||||
('givenname', models.CharField(verbose_name='Given name', null=True, max_length=255, blank=True)),
|
('givenname', models.CharField(verbose_name='Given name', max_length=255, blank=True, null=True)),
|
||||||
('familyname', models.CharField(verbose_name='Family name', null=True, max_length=255, blank=True)),
|
('familyname', models.CharField(verbose_name='Family name', max_length=255, blank=True, null=True)),
|
||||||
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
|
('is_active', models.BooleanField(verbose_name='Is active', default=True)),
|
||||||
('is_staff', models.BooleanField(default=False, verbose_name='Is site admin')),
|
('is_staff', models.BooleanField(verbose_name='Is site admin', default=False)),
|
||||||
('date_joined', models.DateTimeField(verbose_name='Date joined', auto_now_add=True)),
|
('date_joined', models.DateTimeField(verbose_name='Date joined', auto_now_add=True)),
|
||||||
('locale', models.CharField(default='en', verbose_name='Language', choices=[('en', 'English'), ('de', 'German'), ('de-informal', 'German (informal)')], max_length=50)),
|
('locale', models.CharField(verbose_name='Language', default='en', choices=[('en', 'English'), ('de', 'German'), ('de-informal', 'German (informal)')], max_length=50)),
|
||||||
('timezone', models.CharField(default='UTC', verbose_name='Timezone', max_length=100)),
|
('timezone', models.CharField(verbose_name='Timezone', default='UTC', max_length=100)),
|
||||||
('groups', models.ManyToManyField(related_query_name='user', verbose_name='groups', help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', blank=True, related_name='user_set', to='auth.Group')),
|
('groups', models.ManyToManyField(to='auth.Group', blank=True, related_query_name='user', verbose_name='groups', help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set')),
|
||||||
('user_permissions', models.ManyToManyField(related_query_name='user', verbose_name='user permissions', help_text='Specific permissions for this user.', blank=True, related_name='user_set', to='auth.Permission')),
|
('user_permissions', models.ManyToManyField(to='auth.Permission', blank=True, related_query_name='user', verbose_name='user permissions', help_text='Specific permissions for this user.', related_name='user_set')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'User',
|
'verbose_name': 'User',
|
||||||
@@ -47,17 +58,17 @@ class Migration(migrations.Migration):
|
|||||||
name='CachedFile',
|
name='CachedFile',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||||
('expires', models.DateTimeField(null=True, blank=True)),
|
('expires', models.DateTimeField(blank=True, null=True)),
|
||||||
('date', models.DateTimeField(null=True, blank=True)),
|
('date', models.DateTimeField(blank=True, null=True)),
|
||||||
('filename', models.CharField(max_length=255)),
|
('filename', models.CharField(max_length=255)),
|
||||||
('type', models.CharField(max_length=255)),
|
('type', models.CharField(max_length=255)),
|
||||||
('file', models.FileField(upload_to=pretix.base.models.cachedfile_name, null=True, blank=True)),
|
('file', models.FileField(blank=True, null=True, upload_to=pretix.base.models.base.cachedfile_name)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='CachedTicket',
|
name='CachedTicket',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(serialize=False, verbose_name='ID', primary_key=True, auto_created=True)),
|
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
||||||
('provider', models.CharField(max_length=255)),
|
('provider', models.CharField(max_length=255)),
|
||||||
('cachedfile', models.ForeignKey(to='pretixbase.CachedFile')),
|
('cachedfile', models.ForeignKey(to='pretixbase.CachedFile')),
|
||||||
],
|
],
|
||||||
@@ -65,39 +76,32 @@ class Migration(migrations.Migration):
|
|||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='CartPosition',
|
name='CartPosition',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
||||||
('identity', models.CharField(max_length=36)),
|
('cart_id', models.CharField(verbose_name='Cart ID (e.g. session key)', max_length=255, blank=True, null=True)),
|
||||||
('version_start_date', models.DateTimeField()),
|
('price', models.DecimalField(verbose_name='Price', max_digits=10, decimal_places=2)),
|
||||||
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
|
|
||||||
('version_birth_date', models.DateTimeField()),
|
|
||||||
('session', models.CharField(verbose_name='Session', null=True, max_length=255, blank=True)),
|
|
||||||
('price', models.DecimalField(decimal_places=2, verbose_name='Price', max_digits=10)),
|
|
||||||
('datetime', models.DateTimeField(verbose_name='Date', auto_now_add=True)),
|
('datetime', models.DateTimeField(verbose_name='Date', auto_now_add=True)),
|
||||||
('expires', models.DateTimeField(verbose_name='Expiration date')),
|
('expires', models.DateTimeField(verbose_name='Expiration date')),
|
||||||
('attendee_name', models.CharField(verbose_name='Attendee name', null=True, help_text='Empty, if this product is not an admission ticket', max_length=255, blank=True)),
|
('attendee_name', models.CharField(verbose_name='Attendee name', max_length=255, blank=True, null=True, help_text='Empty, if this product is not an admission ticket')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Cart position',
|
'verbose_name': 'Cart position',
|
||||||
'verbose_name_plural': 'Cart positions',
|
'verbose_name_plural': 'Cart positions',
|
||||||
},
|
},
|
||||||
bases=(pretix.base.models.ObjectWithAnswers, models.Model),
|
bases=(pretix.base.models.orders.ObjectWithAnswers, models.Model),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Event',
|
name='Event',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
||||||
('identity', models.CharField(max_length=36)),
|
|
||||||
('version_start_date', models.DateTimeField()),
|
|
||||||
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
|
|
||||||
('version_birth_date', models.DateTimeField()),
|
|
||||||
('name', pretix.base.i18n.I18nCharField(verbose_name='Name', max_length=200)),
|
('name', pretix.base.i18n.I18nCharField(verbose_name='Name', max_length=200)),
|
||||||
('slug', models.SlugField(verbose_name='Slug', help_text='Should be short, only contain lowercase letters and numbers, and must be unique among your events. This is being used in addresses and bank transfer references.', validators=[django.core.validators.RegexValidator(regex='^[a-zA-Z0-9.-]+$', message='The slug may only contain letters, numbers, dots and dashes.')])),
|
('slug', models.SlugField(verbose_name='Slug', validators=[django.core.validators.RegexValidator(message='The slug may only contain letters, numbers, dots and dashes.', regex='^[a-zA-Z0-9.-]+$')], help_text='Should be short, only contain lowercase letters and numbers, and must be unique among your events. This is being used in addresses and bank transfer references.')),
|
||||||
('currency', models.CharField(default='EUR', verbose_name='Default currency', max_length=10)),
|
('currency', models.CharField(verbose_name='Default currency', default='EUR', max_length=10)),
|
||||||
('date_from', models.DateTimeField(verbose_name='Event start time')),
|
('date_from', models.DateTimeField(verbose_name='Event start time')),
|
||||||
('date_to', models.DateTimeField(verbose_name='Event end time', null=True, blank=True)),
|
('date_to', models.DateTimeField(verbose_name='Event end time', blank=True, null=True)),
|
||||||
('presale_end', models.DateTimeField(verbose_name='End of presale', help_text='No products will be sold after this date.', null=True, blank=True)),
|
('is_public', models.BooleanField(verbose_name='Visible in public lists', default=False, help_text="If selected, this event may show up on the ticket system's start page or an organization profile.")),
|
||||||
('presale_start', models.DateTimeField(verbose_name='Start of presale', help_text='No products will be sold before this date.', null=True, blank=True)),
|
('presale_end', models.DateTimeField(verbose_name='End of presale', help_text='No products will be sold after this date.', blank=True, null=True)),
|
||||||
('plugins', models.TextField(verbose_name='Plugins', null=True, blank=True)),
|
('presale_start', models.DateTimeField(verbose_name='Start of presale', help_text='No products will be sold before this date.', blank=True, null=True)),
|
||||||
|
('plugins', models.TextField(verbose_name='Plugins', blank=True, null=True)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Event',
|
'verbose_name': 'Event',
|
||||||
@@ -108,7 +112,7 @@ class Migration(migrations.Migration):
|
|||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='EventLock',
|
name='EventLock',
|
||||||
fields=[
|
fields=[
|
||||||
('event', models.CharField(serialize=False, max_length=36, primary_key=True)),
|
('event', models.CharField(max_length=36, primary_key=True, serialize=False)),
|
||||||
('date', models.DateTimeField(auto_now=True)),
|
('date', models.DateTimeField(auto_now=True)),
|
||||||
('token', models.UUIDField(default=uuid.uuid4)),
|
('token', models.UUIDField(default=uuid.uuid4)),
|
||||||
],
|
],
|
||||||
@@ -116,18 +120,14 @@ class Migration(migrations.Migration):
|
|||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='EventPermission',
|
name='EventPermission',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
||||||
('identity', models.CharField(max_length=36)),
|
('can_change_settings', models.BooleanField(verbose_name='Can change event settings', default=True)),
|
||||||
('version_start_date', models.DateTimeField()),
|
('can_change_items', models.BooleanField(verbose_name='Can change product settings', default=True)),
|
||||||
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
|
('can_view_orders', models.BooleanField(verbose_name='Can view orders', default=True)),
|
||||||
('version_birth_date', models.DateTimeField()),
|
('can_change_permissions', models.BooleanField(verbose_name='Can change permissions', default=True)),
|
||||||
('can_change_settings', models.BooleanField(default=True, verbose_name='Can change event settings')),
|
('can_change_orders', models.BooleanField(verbose_name='Can change orders', default=True)),
|
||||||
('can_change_items', models.BooleanField(default=True, verbose_name='Can change product settings')),
|
('event', models.ForeignKey(to='pretixbase.Event', related_name='user_perms')),
|
||||||
('can_view_orders', models.BooleanField(default=True, verbose_name='Can view orders')),
|
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='event_perms')),
|
||||||
('can_change_permissions', models.BooleanField(default=True, verbose_name='Can change permissions')),
|
|
||||||
('can_change_orders', models.BooleanField(default=True, verbose_name='Can change orders')),
|
|
||||||
('event', versions.models.VersionedForeignKey(to='pretixbase.Event')),
|
|
||||||
('user', models.ForeignKey(related_name='event_perms', to=settings.AUTH_USER_MODEL)),
|
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Event permission',
|
'verbose_name': 'Event permission',
|
||||||
@@ -137,35 +137,26 @@ class Migration(migrations.Migration):
|
|||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='EventSetting',
|
name='EventSetting',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
||||||
('identity', models.CharField(max_length=36)),
|
|
||||||
('version_start_date', models.DateTimeField()),
|
|
||||||
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
|
|
||||||
('version_birth_date', models.DateTimeField()),
|
|
||||||
('key', models.CharField(max_length=255)),
|
('key', models.CharField(max_length=255)),
|
||||||
('value', models.TextField()),
|
('value', models.TextField()),
|
||||||
('object', versions.models.VersionedForeignKey(related_name='setting_objects', to='pretixbase.Event')),
|
('object', models.ForeignKey(to='pretixbase.Event', related_name='setting_objects')),
|
||||||
],
|
],
|
||||||
options={
|
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Item',
|
name='Item',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
||||||
('identity', models.CharField(max_length=36)),
|
|
||||||
('version_start_date', models.DateTimeField()),
|
|
||||||
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
|
|
||||||
('version_birth_date', models.DateTimeField()),
|
|
||||||
('name', pretix.base.i18n.I18nCharField(verbose_name='Item name', max_length=255)),
|
('name', pretix.base.i18n.I18nCharField(verbose_name='Item name', max_length=255)),
|
||||||
('active', models.BooleanField(default=True, verbose_name='Active')),
|
('active', models.BooleanField(verbose_name='Active', default=True)),
|
||||||
('description', pretix.base.i18n.I18nTextField(verbose_name='Description', help_text='This is shown below the product name in lists.', null=True, blank=True)),
|
('description', pretix.base.i18n.I18nTextField(verbose_name='Description', help_text='This is shown below the product name in lists.', blank=True, null=True)),
|
||||||
('default_price', models.DecimalField(decimal_places=2, verbose_name='Default price', max_digits=7, null=True)),
|
('default_price', models.DecimalField(verbose_name='Default price', max_digits=7, decimal_places=2, null=True)),
|
||||||
('tax_rate', models.DecimalField(decimal_places=2, verbose_name='Taxes included in percent', max_digits=7, null=True, blank=True)),
|
('tax_rate', models.DecimalField(verbose_name='Taxes included in percent', max_digits=7, blank=True, null=True, decimal_places=2)),
|
||||||
('admission', models.BooleanField(default=False, verbose_name='Is an admission ticket', help_text='Whether or not buying this product allows a person to enter your event')),
|
('admission', models.BooleanField(verbose_name='Is an admission ticket', default=False, help_text='Whether or not buying this product allows a person to enter your event')),
|
||||||
('position', models.IntegerField(default=0)),
|
('position', models.IntegerField(default=0)),
|
||||||
('picture', models.ImageField(upload_to=pretix.base.models.itempicture_upload_to, verbose_name='Product picture', null=True, blank=True)),
|
('picture', models.ImageField(verbose_name='Product picture', blank=True, null=True, upload_to=pretix.base.models.items.itempicture_upload_to)),
|
||||||
|
('available_from', models.DateTimeField(verbose_name='Available from', help_text='This product will not be sold before the given date.', blank=True, null=True)),
|
||||||
|
('available_until', models.DateTimeField(verbose_name='Available until', help_text='This product will not be sold after the given date.', blank=True, null=True)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Product',
|
'verbose_name': 'Product',
|
||||||
@@ -176,32 +167,24 @@ class Migration(migrations.Migration):
|
|||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='ItemCategory',
|
name='ItemCategory',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
||||||
('identity', models.CharField(max_length=36)),
|
|
||||||
('version_start_date', models.DateTimeField()),
|
|
||||||
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
|
|
||||||
('version_birth_date', models.DateTimeField()),
|
|
||||||
('name', pretix.base.i18n.I18nCharField(verbose_name='Category name', max_length=255)),
|
('name', pretix.base.i18n.I18nCharField(verbose_name='Category name', max_length=255)),
|
||||||
('position', models.IntegerField(default=0)),
|
('position', models.IntegerField(default=0)),
|
||||||
('event', versions.models.VersionedForeignKey(related_name='categories', to='pretixbase.Event')),
|
('event', models.ForeignKey(to='pretixbase.Event', related_name='categories')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Product category',
|
'verbose_name': 'Product category',
|
||||||
'verbose_name_plural': 'Product categories',
|
'verbose_name_plural': 'Product categories',
|
||||||
'ordering': ('position', 'version_birth_date'),
|
'ordering': ('position', 'id'),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='ItemVariation',
|
name='ItemVariation',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
||||||
('identity', models.CharField(max_length=36)),
|
('active', models.BooleanField(verbose_name='Active', default=True)),
|
||||||
('version_start_date', models.DateTimeField()),
|
('default_price', models.DecimalField(verbose_name='Default price', max_digits=7, blank=True, null=True, decimal_places=2)),
|
||||||
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
|
('item', models.ForeignKey(to='pretixbase.Item', related_name='variations')),
|
||||||
('version_birth_date', models.DateTimeField()),
|
|
||||||
('active', models.BooleanField(default=True, verbose_name='Active')),
|
|
||||||
('default_price', models.DecimalField(decimal_places=2, verbose_name='Default price', max_digits=7, null=True, blank=True)),
|
|
||||||
('item', versions.models.VersionedForeignKey(related_name='variations', to='pretixbase.Item')),
|
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Product variation',
|
'verbose_name': 'Product variation',
|
||||||
@@ -211,25 +194,21 @@ class Migration(migrations.Migration):
|
|||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Order',
|
name='Order',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
||||||
('identity', models.CharField(max_length=36)),
|
|
||||||
('version_start_date', models.DateTimeField()),
|
|
||||||
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
|
|
||||||
('version_birth_date', models.DateTimeField()),
|
|
||||||
('code', models.CharField(verbose_name='Order code', max_length=16)),
|
('code', models.CharField(verbose_name='Order code', max_length=16)),
|
||||||
('status', models.CharField(verbose_name='Status', choices=[('n', 'pending'), ('p', 'paid'), ('e', 'expired'), ('c', 'cancelled'), ('r', 'refunded')], max_length=3)),
|
('status', models.CharField(verbose_name='Status', max_length=3, choices=[('n', 'pending'), ('p', 'paid'), ('e', 'expired'), ('c', 'cancelled'), ('r', 'refunded')])),
|
||||||
('email', models.EmailField(verbose_name='E-mail', null=True, max_length=254, blank=True)),
|
('email', models.EmailField(verbose_name='E-mail', max_length=254, blank=True, null=True)),
|
||||||
('locale', models.CharField(verbose_name='Locale', null=True, max_length=32, blank=True)),
|
('locale', models.CharField(verbose_name='Locale', max_length=32, blank=True, null=True)),
|
||||||
('secret', models.CharField(default=pretix.base.models.generate_secret, max_length=32)),
|
('secret', models.CharField(default=pretix.base.models.orders.generate_secret, max_length=32)),
|
||||||
('datetime', models.DateTimeField(verbose_name='Date')),
|
('datetime', models.DateTimeField(verbose_name='Date')),
|
||||||
('expires', models.DateTimeField(verbose_name='Expiration date')),
|
('expires', models.DateTimeField(verbose_name='Expiration date')),
|
||||||
('payment_date', models.DateTimeField(verbose_name='Payment date', null=True, blank=True)),
|
('payment_date', models.DateTimeField(verbose_name='Payment date', blank=True, null=True)),
|
||||||
('payment_provider', models.CharField(verbose_name='Payment provider', null=True, max_length=255, blank=True)),
|
('payment_provider', models.CharField(verbose_name='Payment provider', max_length=255, blank=True, null=True)),
|
||||||
('payment_fee', models.DecimalField(decimal_places=2, default=0, verbose_name='Payment method fee', max_digits=10)),
|
('payment_fee', models.DecimalField(verbose_name='Payment method fee', default=0, max_digits=10, decimal_places=2)),
|
||||||
('payment_info', models.TextField(verbose_name='Payment information', null=True, blank=True)),
|
('payment_info', models.TextField(verbose_name='Payment information', blank=True, null=True)),
|
||||||
('payment_manual', models.BooleanField(default=False, verbose_name='Payment state was manually modified')),
|
('payment_manual', models.BooleanField(verbose_name='Payment state was manually modified', default=False)),
|
||||||
('total', models.DecimalField(decimal_places=2, verbose_name='Total amount', max_digits=10)),
|
('total', models.DecimalField(verbose_name='Total amount', max_digits=10, decimal_places=2)),
|
||||||
('event', versions.models.VersionedForeignKey(verbose_name='Event', related_name='orders', to='pretixbase.Event')),
|
('event', models.ForeignKey(to='pretixbase.Event', verbose_name='Event', related_name='orders')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Order',
|
'verbose_name': 'Order',
|
||||||
@@ -240,33 +219,25 @@ class Migration(migrations.Migration):
|
|||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='OrderPosition',
|
name='OrderPosition',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
||||||
('identity', models.CharField(max_length=36)),
|
('price', models.DecimalField(verbose_name='Price', max_digits=10, decimal_places=2)),
|
||||||
('version_start_date', models.DateTimeField()),
|
('attendee_name', models.CharField(verbose_name='Attendee name', max_length=255, blank=True, null=True, help_text='Empty, if this product is not an admission ticket')),
|
||||||
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
|
('item', models.ForeignKey(to='pretixbase.Item', on_delete=django.db.models.deletion.PROTECT, verbose_name='Item', related_name='positions')),
|
||||||
('version_birth_date', models.DateTimeField()),
|
('order', models.ForeignKey(to='pretixbase.Order', on_delete=django.db.models.deletion.PROTECT, verbose_name='Order', related_name='positions')),
|
||||||
('price', models.DecimalField(decimal_places=2, verbose_name='Price', max_digits=10)),
|
('variation', models.ForeignKey(blank=True, to='pretixbase.ItemVariation', on_delete=django.db.models.deletion.PROTECT, verbose_name='Variation', null=True)),
|
||||||
('attendee_name', models.CharField(verbose_name='Attendee name', null=True, help_text='Empty, if this product is not an admission ticket', max_length=255, blank=True)),
|
|
||||||
('item', versions.models.VersionedForeignKey(verbose_name='Item', related_name='positions', to='pretixbase.Item')),
|
|
||||||
('order', versions.models.VersionedForeignKey(verbose_name='Order', related_name='positions', to='pretixbase.Order')),
|
|
||||||
('variation', versions.models.VersionedForeignKey(verbose_name='Variation', blank=True, null=True, to='pretixbase.ItemVariation')),
|
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Order position',
|
'verbose_name': 'Order position',
|
||||||
'verbose_name_plural': 'Order positions',
|
'verbose_name_plural': 'Order positions',
|
||||||
},
|
},
|
||||||
bases=(pretix.base.models.ObjectWithAnswers, models.Model),
|
bases=(pretix.base.models.orders.ObjectWithAnswers, models.Model),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Organizer',
|
name='Organizer',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
||||||
('identity', models.CharField(max_length=36)),
|
|
||||||
('version_start_date', models.DateTimeField()),
|
|
||||||
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
|
|
||||||
('version_birth_date', models.DateTimeField()),
|
|
||||||
('name', models.CharField(verbose_name='Name', max_length=200)),
|
('name', models.CharField(verbose_name='Name', max_length=200)),
|
||||||
('slug', models.SlugField(verbose_name='Slug', help_text='Should be short, only contain lowercase letters and numbers, and must be unique among your events. This is being used in addresses and bank transfer references.', validators=[django.core.validators.RegexValidator(regex='^[a-zA-Z0-9.-]+$', message='The slug may only contain letters, numbers, dots and dashes.')])),
|
('slug', models.SlugField(verbose_name='Slug', validators=[django.core.validators.RegexValidator(message='The slug may only contain letters, numbers, dots and dashes.', regex='^[a-zA-Z0-9.-]+$')], help_text='Should be short, only contain lowercase letters and numbers, and must be unique among your events. This is being used in addresses and bank transfer references.')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Organizer',
|
'verbose_name': 'Organizer',
|
||||||
@@ -277,14 +248,10 @@ class Migration(migrations.Migration):
|
|||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='OrganizerPermission',
|
name='OrganizerPermission',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
||||||
('identity', models.CharField(max_length=36)),
|
('can_create_events', models.BooleanField(verbose_name='Can create events', default=True)),
|
||||||
('version_start_date', models.DateTimeField()),
|
('organizer', models.ForeignKey(to='pretixbase.Organizer', related_name='user_perms')),
|
||||||
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
|
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='organizer_perms')),
|
||||||
('version_birth_date', models.DateTimeField()),
|
|
||||||
('can_create_events', models.BooleanField(default=True, verbose_name='Can create events')),
|
|
||||||
('organizer', versions.models.VersionedForeignKey(to='pretixbase.Organizer')),
|
|
||||||
('user', models.ForeignKey(related_name='organizer_perms', to=settings.AUTH_USER_MODEL)),
|
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Organizer permission',
|
'verbose_name': 'Organizer permission',
|
||||||
@@ -294,30 +261,19 @@ class Migration(migrations.Migration):
|
|||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='OrganizerSetting',
|
name='OrganizerSetting',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
||||||
('identity', models.CharField(max_length=36)),
|
|
||||||
('version_start_date', models.DateTimeField()),
|
|
||||||
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
|
|
||||||
('version_birth_date', models.DateTimeField()),
|
|
||||||
('key', models.CharField(max_length=255)),
|
('key', models.CharField(max_length=255)),
|
||||||
('value', models.TextField()),
|
('value', models.TextField()),
|
||||||
('object', versions.models.VersionedForeignKey(related_name='setting_objects', to='pretixbase.Organizer')),
|
('object', models.ForeignKey(to='pretixbase.Organizer', related_name='setting_objects')),
|
||||||
],
|
],
|
||||||
options={
|
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Property',
|
name='Property',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
||||||
('identity', models.CharField(max_length=36)),
|
|
||||||
('version_start_date', models.DateTimeField()),
|
|
||||||
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
|
|
||||||
('version_birth_date', models.DateTimeField()),
|
|
||||||
('name', pretix.base.i18n.I18nCharField(verbose_name='Property name', max_length=250)),
|
('name', pretix.base.i18n.I18nCharField(verbose_name='Property name', max_length=250)),
|
||||||
('event', versions.models.VersionedForeignKey(related_name='properties', to='pretixbase.Event')),
|
('event', models.ForeignKey(to='pretixbase.Event', related_name='properties')),
|
||||||
('item', versions.models.VersionedForeignKey(blank=True, related_name='properties', null=True, to='pretixbase.Item')),
|
('item', models.ForeignKey(blank=True, to='pretixbase.Item', null=True, related_name='properties')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Product property',
|
'verbose_name': 'Product property',
|
||||||
@@ -327,34 +283,26 @@ class Migration(migrations.Migration):
|
|||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='PropertyValue',
|
name='PropertyValue',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
||||||
('identity', models.CharField(max_length=36)),
|
|
||||||
('version_start_date', models.DateTimeField()),
|
|
||||||
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
|
|
||||||
('version_birth_date', models.DateTimeField()),
|
|
||||||
('value', pretix.base.i18n.I18nCharField(verbose_name='Value', max_length=250)),
|
('value', pretix.base.i18n.I18nCharField(verbose_name='Value', max_length=250)),
|
||||||
('position', models.IntegerField(default=0)),
|
('position', models.IntegerField(default=0)),
|
||||||
('prop', versions.models.VersionedForeignKey(related_name='values', to='pretixbase.Property')),
|
('prop', models.ForeignKey(to='pretixbase.Property', related_name='values')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Property value',
|
'verbose_name': 'Property value',
|
||||||
'verbose_name_plural': 'Property values',
|
'verbose_name_plural': 'Property values',
|
||||||
'ordering': ('position', 'version_birth_date'),
|
'ordering': ('position', 'id'),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Question',
|
name='Question',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
||||||
('identity', models.CharField(max_length=36)),
|
|
||||||
('version_start_date', models.DateTimeField()),
|
|
||||||
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
|
|
||||||
('version_birth_date', models.DateTimeField()),
|
|
||||||
('question', pretix.base.i18n.I18nTextField(verbose_name='Question')),
|
('question', pretix.base.i18n.I18nTextField(verbose_name='Question')),
|
||||||
('type', models.CharField(verbose_name='Question type', choices=[('N', 'Number'), ('S', 'Text (one line)'), ('T', 'Multiline text'), ('B', 'Yes/No')], max_length=5)),
|
('type', models.CharField(verbose_name='Question type', max_length=5, choices=[('N', 'Number'), ('S', 'Text (one line)'), ('T', 'Multiline text'), ('B', 'Yes/No')])),
|
||||||
('required', models.BooleanField(default=False, verbose_name='Required question')),
|
('required', models.BooleanField(verbose_name='Required question', default=False)),
|
||||||
('event', versions.models.VersionedForeignKey(related_name='questions', to='pretixbase.Event')),
|
('event', models.ForeignKey(to='pretixbase.Event', related_name='questions')),
|
||||||
('items', versions.models.VersionedManyToManyField(to='pretixbase.Item', verbose_name='Products', help_text='This question will be asked to buyers of the selected products', related_name='questions', blank=True)),
|
('items', models.ManyToManyField(verbose_name='Products', help_text='This question will be asked to buyers of the selected products', blank=True, to='pretixbase.Item', related_name='questions')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Question',
|
'verbose_name': 'Question',
|
||||||
@@ -364,33 +312,22 @@ class Migration(migrations.Migration):
|
|||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='QuestionAnswer',
|
name='QuestionAnswer',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
||||||
('identity', models.CharField(max_length=36)),
|
|
||||||
('version_start_date', models.DateTimeField()),
|
|
||||||
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
|
|
||||||
('version_birth_date', models.DateTimeField()),
|
|
||||||
('answer', models.TextField()),
|
('answer', models.TextField()),
|
||||||
('cartposition', models.ForeignKey(blank=True, related_name='answers', null=True, to='pretixbase.CartPosition')),
|
('cartposition', models.ForeignKey(blank=True, to='pretixbase.CartPosition', null=True, related_name='answers')),
|
||||||
('orderposition', models.ForeignKey(blank=True, related_name='answers', null=True, to='pretixbase.OrderPosition')),
|
('orderposition', models.ForeignKey(blank=True, to='pretixbase.OrderPosition', null=True, related_name='answers')),
|
||||||
('question', versions.models.VersionedForeignKey(related_name='answers', to='pretixbase.Question')),
|
('question', models.ForeignKey(to='pretixbase.Question', related_name='answers')),
|
||||||
],
|
],
|
||||||
options={
|
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Quota',
|
name='Quota',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
||||||
('identity', models.CharField(max_length=36)),
|
|
||||||
('version_start_date', models.DateTimeField()),
|
|
||||||
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
|
|
||||||
('version_birth_date', models.DateTimeField()),
|
|
||||||
('name', models.CharField(verbose_name='Name', max_length=200)),
|
('name', models.CharField(verbose_name='Name', max_length=200)),
|
||||||
('size', models.PositiveIntegerField(verbose_name='Total capacity')),
|
('size', models.PositiveIntegerField(verbose_name='Total capacity', help_text='Leave empty for an unlimited number of tickets.', blank=True, null=True)),
|
||||||
('event', versions.models.VersionedForeignKey(verbose_name='Event', related_name='quotas', to='pretixbase.Event')),
|
('event', models.ForeignKey(to='pretixbase.Event', verbose_name='Event', related_name='quotas')),
|
||||||
('items', versions.models.VersionedManyToManyField(to='pretixbase.Item', verbose_name='Item', related_name='quotas', blank=True)),
|
('items', models.ManyToManyField(verbose_name='Item', to='pretixbase.Item', blank=True, related_name='quotas')),
|
||||||
('variations', pretix.base.models.VariationsField(to='pretixbase.ItemVariation', verbose_name='Variations', related_name='quotas', blank=True)),
|
('variations', pretix.base.models.items.VariationsField(verbose_name='Variations', to='pretixbase.ItemVariation', blank=True, related_name='quotas')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Quota',
|
'verbose_name': 'Quota',
|
||||||
@@ -400,51 +337,52 @@ class Migration(migrations.Migration):
|
|||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='organizer',
|
model_name='organizer',
|
||||||
name='permitted',
|
name='permitted',
|
||||||
field=models.ManyToManyField(related_name='organizers', to=settings.AUTH_USER_MODEL, through='pretixbase.OrganizerPermission'),
|
field=models.ManyToManyField(to=settings.AUTH_USER_MODEL, through='pretixbase.OrganizerPermission', related_name='organizers'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='itemvariation',
|
model_name='itemvariation',
|
||||||
name='values',
|
name='values',
|
||||||
field=versions.models.VersionedManyToManyField(related_name='variations', to='pretixbase.PropertyValue'),
|
field=models.ForeignKey(to='pretixbase.PropertyValue', related_name='variations'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='item',
|
model_name='item',
|
||||||
name='category',
|
name='category',
|
||||||
field=versions.models.VersionedForeignKey(verbose_name='Category', blank=True, to='pretixbase.ItemCategory', related_name='items', null=True, on_delete=django.db.models.deletion.PROTECT),
|
field=models.ForeignKey(blank=True, to='pretixbase.ItemCategory', on_delete=django.db.models.deletion.PROTECT, verbose_name='Category', null=True, related_name='items'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='item',
|
model_name='item',
|
||||||
name='event',
|
name='event',
|
||||||
field=versions.models.VersionedForeignKey(verbose_name='Event', related_name='items', to='pretixbase.Event', on_delete=django.db.models.deletion.PROTECT),
|
field=models.ForeignKey(to='pretixbase.Event', on_delete=django.db.models.deletion.PROTECT, verbose_name='Event', related_name='items'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='event',
|
model_name='event',
|
||||||
name='organizer',
|
name='organizer',
|
||||||
field=versions.models.VersionedForeignKey(related_name='events', to='pretixbase.Organizer', on_delete=django.db.models.deletion.PROTECT),
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pretixbase.Organizer', related_name='events'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='event',
|
model_name='event',
|
||||||
name='permitted',
|
name='permitted',
|
||||||
field=models.ManyToManyField(related_name='events', to=settings.AUTH_USER_MODEL, through='pretixbase.EventPermission'),
|
field=models.ManyToManyField(to=settings.AUTH_USER_MODEL, through='pretixbase.EventPermission', related_name='events'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='cartposition',
|
model_name='cartposition',
|
||||||
name='event',
|
name='event',
|
||||||
field=versions.models.VersionedForeignKey(verbose_name='Event', to='pretixbase.Event'),
|
field=models.ForeignKey(verbose_name='Event', to='pretixbase.Event'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='cartposition',
|
model_name='cartposition',
|
||||||
name='item',
|
name='item',
|
||||||
field=versions.models.VersionedForeignKey(verbose_name='Item', to='pretixbase.Item'),
|
field=models.ForeignKey(verbose_name='Item', to='pretixbase.Item'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='cartposition',
|
model_name='cartposition',
|
||||||
name='variation',
|
name='variation',
|
||||||
field=versions.models.VersionedForeignKey(verbose_name='Variation', blank=True, null=True, to='pretixbase.ItemVariation'),
|
field=models.ForeignKey(blank=True, to='pretixbase.ItemVariation', verbose_name='Variation', null=True),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='cachedticket',
|
model_name='cachedticket',
|
||||||
name='order',
|
name='order',
|
||||||
field=versions.models.VersionedForeignKey(to='pretixbase.Order'),
|
field=models.ForeignKey(to='pretixbase.Order'),
|
||||||
),
|
),
|
||||||
|
migrations.RunPython(initial_user),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('pretixbase', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='quota',
|
|
||||||
name='size',
|
|
||||||
field=models.PositiveIntegerField(help_text='Leave empty for an unlimited number of tickets.', verbose_name='Total capacity', blank=True, null=True),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('pretixbase', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='itemvariation',
|
||||||
|
name='values',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='itemvariation',
|
||||||
|
name='values',
|
||||||
|
field=models.ManyToManyField(related_name='variations', to='pretixbase.PropertyValue'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('pretixbase', '0002_auto_20151021_1412'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='event',
|
|
||||||
name='is_public',
|
|
||||||
field=models.BooleanField(help_text="If selected, this event may show up on the ticket system's start page or an organization profile.", default=False, verbose_name='Visible in public lists'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('pretixbase', '0003_event_is_public'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name='cartposition',
|
|
||||||
old_name='session',
|
|
||||||
new_name='cart_id'
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='cartposition',
|
|
||||||
name='cart_id',
|
|
||||||
field=models.CharField(blank=True, verbose_name='Cart ID (e.g. session key)', max_length=255, null=True),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('pretixbase', '0004_auto_20151024_0848'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='item',
|
|
||||||
name='available_from',
|
|
||||||
field=models.DateTimeField(null=True, help_text='This product will not be sold before the given date.', blank=True, verbose_name='Available from'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='item',
|
|
||||||
name='available_until',
|
|
||||||
field=models.DateTimeField(null=True, help_text='This product will not be sold after the given date.', blank=True, verbose_name='Available to'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
from .auth import User
|
from .auth import User
|
||||||
from .base import CachedFile, Versionable, cachedfile_name
|
from .base import CachedFile, cachedfile_name
|
||||||
from .event import Event, EventLock, EventPermission, EventSetting
|
from .event import Event, EventLock, EventPermission, EventSetting
|
||||||
from .items import (
|
from .items import (
|
||||||
Item, ItemCategory, ItemVariation, Property, PropertyValue, Question,
|
Item, ItemCategory, ItemVariation, Property, PropertyValue, Question,
|
||||||
@@ -12,7 +12,7 @@ from .orders import (
|
|||||||
from .organizer import Organizer, OrganizerPermission, OrganizerSetting
|
from .organizer import Organizer, OrganizerPermission, OrganizerSetting
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'Versionable', 'User', 'CachedFile', 'Organizer', 'OrganizerPermission', 'Event', 'EventPermission',
|
'User', 'CachedFile', 'Organizer', 'OrganizerPermission', 'Event', 'EventPermission',
|
||||||
'ItemCategory', 'Item', 'Property', 'PropertyValue', 'ItemVariation', 'VariationsField', 'Question',
|
'ItemCategory', 'Item', 'Property', 'PropertyValue', 'ItemVariation', 'VariationsField', 'Question',
|
||||||
'Quota', 'Order', 'CachedTicket', 'QuestionAnswer', 'ObjectWithAnswers', 'OrderPosition',
|
'Quota', 'Order', 'CachedTicket', 'QuestionAnswer', 'ObjectWithAnswers', 'OrderPosition',
|
||||||
'CartPosition', 'EventSetting', 'OrganizerSetting', 'EventLock', 'cachedfile_name', 'itempicture_upload_to',
|
'CartPosition', 'EventSetting', 'OrganizerSetting', 'EventLock', 'cachedfile_name', 'itempicture_upload_to',
|
||||||
|
|||||||
@@ -1,71 +1,12 @@
|
|||||||
import copy
|
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
import six
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.signals import post_delete
|
from django.db.models.signals import post_delete
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from versions.models import Versionable as BaseVersionable, get_utc_now
|
|
||||||
|
|
||||||
|
|
||||||
class Versionable(BaseVersionable):
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
def clone_shallow(self, forced_version_date: datetime=None):
|
|
||||||
"""
|
|
||||||
This behaves like clone(), but misses all the Many2Many-relation-handling. This is
|
|
||||||
a performance optimization for cases in which we have to handle the Many2Many relations
|
|
||||||
by hand anyways.
|
|
||||||
"""
|
|
||||||
if not self.pk: # NOQA
|
|
||||||
raise ValueError('Instance must be saved before it can be cloned')
|
|
||||||
|
|
||||||
if self.version_end_date: # NOQA
|
|
||||||
raise ValueError('This is a historical item and can not be cloned.')
|
|
||||||
|
|
||||||
if forced_version_date: # NOQA
|
|
||||||
if not self.version_start_date <= forced_version_date <= get_utc_now():
|
|
||||||
raise ValueError('The clone date must be between the version start date and now.')
|
|
||||||
else:
|
|
||||||
forced_version_date = get_utc_now()
|
|
||||||
|
|
||||||
earlier_version = self
|
|
||||||
|
|
||||||
later_version = copy.copy(earlier_version)
|
|
||||||
later_version.version_end_date = None
|
|
||||||
later_version.version_start_date = forced_version_date
|
|
||||||
|
|
||||||
# set earlier_version's ID to a new UUID so the clone (later_version) can
|
|
||||||
# get the old one -- this allows 'head' to always have the original
|
|
||||||
# id allowing us to get at all historic foreign key relationships
|
|
||||||
earlier_version.id = six.u(str(uuid.uuid4()))
|
|
||||||
earlier_version.version_end_date = forced_version_date
|
|
||||||
earlier_version.save()
|
|
||||||
|
|
||||||
for field in earlier_version._meta.many_to_many:
|
|
||||||
earlier_version.clone_relations_shallow(later_version, field.attname, forced_version_date)
|
|
||||||
|
|
||||||
if hasattr(earlier_version._meta, 'many_to_many_related'):
|
|
||||||
for rel in earlier_version._meta.many_to_many_related:
|
|
||||||
earlier_version.clone_relations_shallow(later_version, rel.via_field_name, forced_version_date)
|
|
||||||
|
|
||||||
later_version.save()
|
|
||||||
|
|
||||||
return later_version
|
|
||||||
|
|
||||||
def clone_relations_shallow(self, clone, manager_field_name, forced_version_date):
|
|
||||||
# Source: the original object, where relations are currently pointing to
|
|
||||||
source = getattr(self, manager_field_name) # returns a VersionedRelatedManager instance
|
|
||||||
# Destination: the clone, where the cloned relations should point to
|
|
||||||
source.through.objects.filter(**{source.source_field.attname: clone.id}).update(**{
|
|
||||||
source.source_field.attname: self.id, 'version_end_date': forced_version_date
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
def cachedfile_name(instance, filename: str) -> str:
|
def cachedfile_name(instance, filename: str) -> str:
|
||||||
return 'cachedfiles/%s.%s' % (instance.id, filename.split('.')[-1])
|
return 'cachedfiles/%012d.%s' % (instance.id, filename.split('.')[-1])
|
||||||
|
|
||||||
|
|
||||||
class CachedFile(models.Model):
|
class CachedFile(models.Model):
|
||||||
|
|||||||
@@ -9,17 +9,15 @@ from django.template.defaultfilters import date as _date
|
|||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from versions.models import VersionedForeignKey
|
|
||||||
|
|
||||||
from pretix.base.i18n import I18nCharField
|
from pretix.base.i18n import I18nCharField
|
||||||
from pretix.base.settings import SettingsProxy
|
from pretix.base.settings import SettingsProxy
|
||||||
|
|
||||||
from .auth import User
|
from .auth import User
|
||||||
from .base import Versionable
|
|
||||||
from .organizer import Organizer
|
from .organizer import Organizer
|
||||||
|
|
||||||
|
|
||||||
class Event(Versionable):
|
class Event(models.Model):
|
||||||
"""
|
"""
|
||||||
This model represents an event. An event is anything you can buy
|
This model represents an event. An event is anything you can buy
|
||||||
tickets for.
|
tickets for.
|
||||||
@@ -46,8 +44,7 @@ class Event(Versionable):
|
|||||||
:type plugins: str
|
:type plugins: str
|
||||||
"""
|
"""
|
||||||
|
|
||||||
organizer = VersionedForeignKey(Organizer, related_name="events",
|
organizer = models.ForeignKey(Organizer, related_name="events", on_delete=models.PROTECT)
|
||||||
on_delete=models.PROTECT)
|
|
||||||
name = I18nCharField(
|
name = I18nCharField(
|
||||||
max_length=200,
|
max_length=200,
|
||||||
verbose_name=_("Name"),
|
verbose_name=_("Name"),
|
||||||
@@ -184,7 +181,7 @@ class Event(Versionable):
|
|||||||
return locking.LockManager(self)
|
return locking.LockManager(self)
|
||||||
|
|
||||||
|
|
||||||
class EventPermission(Versionable):
|
class EventPermission(models.Model):
|
||||||
"""
|
"""
|
||||||
The relation between an Event and an User who has permissions to
|
The relation between an Event and an User who has permissions to
|
||||||
access an event.
|
access an event.
|
||||||
@@ -203,8 +200,8 @@ class EventPermission(Versionable):
|
|||||||
:type can_change_orders: bool
|
:type can_change_orders: bool
|
||||||
"""
|
"""
|
||||||
|
|
||||||
event = VersionedForeignKey(Event)
|
event = models.ForeignKey(Event, related_name="user_perms", on_delete=models.CASCADE)
|
||||||
user = models.ForeignKey(User, related_name="event_perms")
|
user = models.ForeignKey(User, related_name="event_perms", on_delete=models.CASCADE)
|
||||||
can_change_settings = models.BooleanField(
|
can_change_settings = models.BooleanField(
|
||||||
default=True,
|
default=True,
|
||||||
verbose_name=_("Can change event settings")
|
verbose_name=_("Can change event settings")
|
||||||
@@ -237,12 +234,12 @@ class EventPermission(Versionable):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class EventSetting(Versionable):
|
class EventSetting(models.Model):
|
||||||
"""
|
"""
|
||||||
An event settings is a key-value setting which can be set for a
|
An event settings is a key-value setting which can be set for a
|
||||||
specific event
|
specific event
|
||||||
"""
|
"""
|
||||||
object = VersionedForeignKey(Event, related_name='setting_objects')
|
object = models.ForeignKey(Event, related_name='setting_objects', on_delete=models.CASCADE)
|
||||||
key = models.CharField(max_length=255)
|
key = models.CharField(max_length=255)
|
||||||
value = models.TextField()
|
value = models.TextField()
|
||||||
|
|
||||||
|
|||||||
@@ -8,16 +8,14 @@ from django.utils.functional import cached_property
|
|||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from typing import List, Tuple
|
from typing import List, Tuple
|
||||||
from versions.models import VersionedForeignKey, VersionedManyToManyField
|
|
||||||
|
|
||||||
from pretix.base.i18n import I18nCharField, I18nTextField
|
from pretix.base.i18n import I18nCharField, I18nTextField
|
||||||
|
|
||||||
from ..types import VariationDict
|
from ..types import VariationDict
|
||||||
from .base import Versionable
|
|
||||||
from .event import Event
|
from .event import Event
|
||||||
|
|
||||||
|
|
||||||
class ItemCategory(Versionable):
|
class ItemCategory(models.Model):
|
||||||
"""
|
"""
|
||||||
Items can be sorted into these categories.
|
Items can be sorted into these categories.
|
||||||
|
|
||||||
@@ -28,7 +26,7 @@ class ItemCategory(Versionable):
|
|||||||
:param position: An integer, used for sorting
|
:param position: An integer, used for sorting
|
||||||
:type position: int
|
:type position: int
|
||||||
"""
|
"""
|
||||||
event = VersionedForeignKey(
|
event = models.ForeignKey(
|
||||||
Event,
|
Event,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='categories',
|
related_name='categories',
|
||||||
@@ -44,7 +42,7 @@ class ItemCategory(Versionable):
|
|||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Product category")
|
verbose_name = _("Product category")
|
||||||
verbose_name_plural = _("Product categories")
|
verbose_name_plural = _("Product categories")
|
||||||
ordering = ('position', 'version_birth_date')
|
ordering = ('position', 'id')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.name)
|
return str(self.name)
|
||||||
@@ -61,7 +59,7 @@ class ItemCategory(Versionable):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def sortkey(self):
|
def sortkey(self):
|
||||||
return self.position, self.version_birth_date
|
return self.position, self.id
|
||||||
|
|
||||||
def __lt__(self, other) -> bool:
|
def __lt__(self, other) -> bool:
|
||||||
return self.sortkey < other.sortkey
|
return self.sortkey < other.sortkey
|
||||||
@@ -69,12 +67,12 @@ class ItemCategory(Versionable):
|
|||||||
|
|
||||||
def itempicture_upload_to(instance, filename: str) -> str:
|
def itempicture_upload_to(instance, filename: str) -> str:
|
||||||
return '%s/%s/item-%s.%s' % (
|
return '%s/%s/item-%s.%s' % (
|
||||||
instance.event.organizer.slug, instance.event.slug, instance.identity,
|
instance.event.organizer.slug, instance.event.slug, instance.id,
|
||||||
filename.split('.')[-1]
|
filename.split('.')[-1]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Item(Versionable):
|
class Item(models.Model):
|
||||||
"""
|
"""
|
||||||
An item is a thing which can be sold. It belongs to an event and may or may not belong to a category.
|
An item is a thing which can be sold. It belongs to an event and may or may not belong to a category.
|
||||||
Items are often also called 'products' but are named 'items' internally due to historic reasons.
|
Items are often also called 'products' but are named 'items' internally due to historic reasons.
|
||||||
@@ -104,13 +102,13 @@ class Item(Versionable):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
event = VersionedForeignKey(
|
event = models.ForeignKey(
|
||||||
Event,
|
Event,
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name="items",
|
related_name="items",
|
||||||
verbose_name=_("Event"),
|
verbose_name=_("Event"),
|
||||||
)
|
)
|
||||||
category = VersionedForeignKey(
|
category = models.ForeignKey(
|
||||||
ItemCategory,
|
ItemCategory,
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name="items",
|
related_name="items",
|
||||||
@@ -222,7 +220,7 @@ class Item(Versionable):
|
|||||||
for var in all_variations:
|
for var in all_variations:
|
||||||
key = []
|
key = []
|
||||||
for v in var.values.all():
|
for v in var.values.all():
|
||||||
key.append((v.prop_id, v.identity))
|
key.append((v.prop_id, v.id))
|
||||||
key = tuple(sorted(key))
|
key = tuple(sorted(key))
|
||||||
variations_cache[key] = var
|
variations_cache[key] = var
|
||||||
|
|
||||||
@@ -234,8 +232,8 @@ class Item(Versionable):
|
|||||||
key = []
|
key = []
|
||||||
var = VariationDict()
|
var = VariationDict()
|
||||||
for v in comb:
|
for v in comb:
|
||||||
key.append((v.prop.identity, v.identity))
|
key.append((v.prop.id, v.id))
|
||||||
var[v.prop.identity] = v
|
var[v.prop.id] = v
|
||||||
key = tuple(sorted(key))
|
key = tuple(sorted(key))
|
||||||
if key in variations_cache:
|
if key in variations_cache:
|
||||||
var['variation'] = variations_cache[key]
|
var['variation'] = variations_cache[key]
|
||||||
@@ -245,7 +243,7 @@ class Item(Versionable):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def _get_all_generated_variations(self):
|
def _get_all_generated_variations(self):
|
||||||
propids = set([p.identity for p in self.properties.all()])
|
propids = set([p.id for p in self.properties.all()])
|
||||||
if len(propids) == 0:
|
if len(propids) == 0:
|
||||||
variations = [VariationDict()]
|
variations = [VariationDict()]
|
||||||
else:
|
else:
|
||||||
@@ -261,11 +259,11 @@ class Item(Versionable):
|
|||||||
values = list(var.values.all())
|
values = list(var.values.all())
|
||||||
# Make sure we don't expose stale ItemVariation objects which are
|
# Make sure we don't expose stale ItemVariation objects which are
|
||||||
# still around altough they have an old set of properties
|
# still around altough they have an old set of properties
|
||||||
if set([v.prop.identity for v in values]) != propids:
|
if set([v.prop.id for v in values]) != propids:
|
||||||
continue
|
continue
|
||||||
vardict = VariationDict()
|
vardict = VariationDict()
|
||||||
for v in values:
|
for v in values:
|
||||||
vardict[v.prop.identity] = v
|
vardict[v.prop.id] = v
|
||||||
vardict['variation'] = var
|
vardict['variation'] = var
|
||||||
variations.append(vardict)
|
variations.append(vardict)
|
||||||
return variations
|
return variations
|
||||||
@@ -325,7 +323,7 @@ class Item(Versionable):
|
|||||||
key=lambda s: (s[0], s[1] if s[1] is not None else sys.maxsize))
|
key=lambda s: (s[0], s[1] if s[1] is not None else sys.maxsize))
|
||||||
|
|
||||||
|
|
||||||
class Property(Versionable):
|
class Property(models.Model):
|
||||||
"""
|
"""
|
||||||
A property is a modifier which can be applied to an Item. For example
|
A property is a modifier which can be applied to an Item. For example
|
||||||
'Size' would be a property associated with the item 'T-Shirt'.
|
'Size' would be a property associated with the item 'T-Shirt'.
|
||||||
@@ -336,11 +334,11 @@ class Property(Versionable):
|
|||||||
:type name: str
|
:type name: str
|
||||||
"""
|
"""
|
||||||
|
|
||||||
event = VersionedForeignKey(
|
event = models.ForeignKey(
|
||||||
Event,
|
Event,
|
||||||
related_name="properties"
|
related_name="properties"
|
||||||
)
|
)
|
||||||
item = VersionedForeignKey(
|
item = models.ForeignKey(
|
||||||
Item, related_name='properties', null=True, blank=True
|
Item, related_name='properties', null=True, blank=True
|
||||||
)
|
)
|
||||||
name = I18nCharField(
|
name = I18nCharField(
|
||||||
@@ -366,7 +364,7 @@ class Property(Versionable):
|
|||||||
self.event.get_cache().clear()
|
self.event.get_cache().clear()
|
||||||
|
|
||||||
|
|
||||||
class PropertyValue(Versionable):
|
class PropertyValue(models.Model):
|
||||||
"""
|
"""
|
||||||
A value of a property. If the property would be 'T-Shirt size',
|
A value of a property. If the property would be 'T-Shirt size',
|
||||||
this could be 'M' or 'L'.
|
this could be 'M' or 'L'.
|
||||||
@@ -379,7 +377,7 @@ class PropertyValue(Versionable):
|
|||||||
:type position: int
|
:type position: int
|
||||||
"""
|
"""
|
||||||
|
|
||||||
prop = VersionedForeignKey(
|
prop = models.ForeignKey(
|
||||||
Property,
|
Property,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name="values"
|
related_name="values"
|
||||||
@@ -395,7 +393,7 @@ class PropertyValue(Versionable):
|
|||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Property value")
|
verbose_name = _("Property value")
|
||||||
verbose_name_plural = _("Property values")
|
verbose_name_plural = _("Property values")
|
||||||
ordering = ("position", "version_birth_date")
|
ordering = ("position", "id")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s: %s" % (self.prop.name, self.value)
|
return "%s: %s" % (self.prop.name, self.value)
|
||||||
@@ -412,13 +410,13 @@ class PropertyValue(Versionable):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def sortkey(self) -> Tuple[int, datetime]:
|
def sortkey(self) -> Tuple[int, datetime]:
|
||||||
return self.position, self.version_birth_date
|
return self.position, self.id
|
||||||
|
|
||||||
def __lt__(self, other) -> bool:
|
def __lt__(self, other) -> bool:
|
||||||
return self.sortkey < other.sortkey
|
return self.sortkey < other.sortkey
|
||||||
|
|
||||||
|
|
||||||
class ItemVariation(Versionable):
|
class ItemVariation(models.Model):
|
||||||
"""
|
"""
|
||||||
A variation 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'
|
||||||
@@ -444,11 +442,11 @@ class ItemVariation(Versionable):
|
|||||||
:param default_price: This variation's default price
|
:param default_price: This variation's default price
|
||||||
:type default_price: decimal.Decimal
|
:type default_price: decimal.Decimal
|
||||||
"""
|
"""
|
||||||
item = VersionedForeignKey(
|
item = models.ForeignKey(
|
||||||
Item,
|
Item,
|
||||||
related_name='variations'
|
related_name='variations'
|
||||||
)
|
)
|
||||||
values = VersionedManyToManyField(
|
values = models.ManyToManyField(
|
||||||
PropertyValue,
|
PropertyValue,
|
||||||
related_name='variations',
|
related_name='variations',
|
||||||
)
|
)
|
||||||
@@ -495,7 +493,7 @@ class ItemVariation(Versionable):
|
|||||||
"""
|
"""
|
||||||
vd = VariationDict()
|
vd = VariationDict()
|
||||||
for v in self.values.all():
|
for v in self.values.all():
|
||||||
vd[v.prop.identity] = v
|
vd[v.prop.id] = v
|
||||||
vd['variation'] = self
|
vd['variation'] = self
|
||||||
return vd
|
return vd
|
||||||
|
|
||||||
@@ -507,14 +505,14 @@ class ItemVariation(Versionable):
|
|||||||
for pair in pk.split(","):
|
for pair in pk.split(","):
|
||||||
prop, value = pair.split(":")
|
prop, value = pair.split(":")
|
||||||
self.values.add(
|
self.values.add(
|
||||||
PropertyValue.objects.current.get(
|
PropertyValue.objects.get(
|
||||||
identity=value,
|
id=value,
|
||||||
prop_id=prop
|
prop_id=prop
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class VariationsField(VersionedManyToManyField):
|
class VariationsField(models.ManyToManyField):
|
||||||
"""
|
"""
|
||||||
This is a ManyToManyField using the pretixcontrol.views.forms.VariationsField
|
This is a ManyToManyField using the pretixcontrol.views.forms.VariationsField
|
||||||
form field by default.
|
form field by default.
|
||||||
@@ -536,12 +534,12 @@ class VariationsField(VersionedManyToManyField):
|
|||||||
initial = defaults['initial']
|
initial = defaults['initial']
|
||||||
if callable(initial):
|
if callable(initial):
|
||||||
initial = initial()
|
initial = initial()
|
||||||
defaults['initial'] = [i.identity for i in initial]
|
defaults['initial'] = [i.id for i in initial]
|
||||||
# Skip ManyToManyField in dependency chain
|
# Skip ManyToManyField in dependency chain
|
||||||
return super(RelatedField, self).formfield(**defaults)
|
return super(RelatedField, self).formfield(**defaults)
|
||||||
|
|
||||||
|
|
||||||
class Question(Versionable):
|
class Question(models.Model):
|
||||||
"""
|
"""
|
||||||
A question is an input field that can be used to extend a ticket
|
A question is an input field that can be used to extend a ticket
|
||||||
by custom information, e.g. "Attendee age". A question can allow one o several
|
by custom information, e.g. "Attendee age". A question can allow one o several
|
||||||
@@ -573,7 +571,7 @@ class Question(Versionable):
|
|||||||
(TYPE_BOOLEAN, _("Yes/No")),
|
(TYPE_BOOLEAN, _("Yes/No")),
|
||||||
)
|
)
|
||||||
|
|
||||||
event = VersionedForeignKey(
|
event = models.ForeignKey(
|
||||||
Event,
|
Event,
|
||||||
related_name="questions"
|
related_name="questions"
|
||||||
)
|
)
|
||||||
@@ -589,7 +587,7 @@ class Question(Versionable):
|
|||||||
default=False,
|
default=False,
|
||||||
verbose_name=_("Required question")
|
verbose_name=_("Required question")
|
||||||
)
|
)
|
||||||
items = VersionedManyToManyField(
|
items = models.ManyToManyField(
|
||||||
Item,
|
Item,
|
||||||
related_name='questions',
|
related_name='questions',
|
||||||
verbose_name=_("Products"),
|
verbose_name=_("Products"),
|
||||||
@@ -615,7 +613,7 @@ class Question(Versionable):
|
|||||||
self.event.get_cache().clear()
|
self.event.get_cache().clear()
|
||||||
|
|
||||||
|
|
||||||
class Quota(Versionable):
|
class Quota(models.Model):
|
||||||
"""
|
"""
|
||||||
A quota is a "pool of tickets". It is there to limit the number of items
|
A quota is a "pool of tickets". It is there to limit the number of items
|
||||||
of a certain type to be sold. For example, you could have a quota of 500
|
of a certain type to be sold. For example, you could have a quota of 500
|
||||||
@@ -666,7 +664,7 @@ class Quota(Versionable):
|
|||||||
AVAILABILITY_RESERVED = 20
|
AVAILABILITY_RESERVED = 20
|
||||||
AVAILABILITY_OK = 100
|
AVAILABILITY_OK = 100
|
||||||
|
|
||||||
event = VersionedForeignKey(
|
event = models.ForeignKey(
|
||||||
Event,
|
Event,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name="quotas",
|
related_name="quotas",
|
||||||
@@ -681,7 +679,7 @@ class Quota(Versionable):
|
|||||||
null=True, blank=True,
|
null=True, blank=True,
|
||||||
help_text=_("Leave empty for an unlimited number of tickets.")
|
help_text=_("Leave empty for an unlimited number of tickets.")
|
||||||
)
|
)
|
||||||
items = VersionedManyToManyField(
|
items = models.ManyToManyField(
|
||||||
Item,
|
Item,
|
||||||
verbose_name=_("Item"),
|
verbose_name=_("Item"),
|
||||||
related_name="quotas",
|
related_name="quotas",
|
||||||
@@ -745,7 +743,7 @@ class Quota(Versionable):
|
|||||||
def count_in_cart(self) -> int:
|
def count_in_cart(self) -> int:
|
||||||
from pretix.base.models import CartPosition
|
from pretix.base.models import CartPosition
|
||||||
|
|
||||||
return CartPosition.objects.current.filter(
|
return CartPosition.objects.filter(
|
||||||
Q(expires__gte=now())
|
Q(expires__gte=now())
|
||||||
& self._position_lookup
|
& self._position_lookup
|
||||||
).count()
|
).count()
|
||||||
@@ -753,7 +751,7 @@ class Quota(Versionable):
|
|||||||
def count_orders(self) -> dict:
|
def count_orders(self) -> dict:
|
||||||
from pretix.base.models import Order, OrderPosition
|
from pretix.base.models import Order, OrderPosition
|
||||||
|
|
||||||
o = OrderPosition.objects.current.filter(self._position_lookup).aggregate(
|
o = OrderPosition.objects.filter(self._position_lookup).aggregate(
|
||||||
paid=Sum(
|
paid=Sum(
|
||||||
Case(When(order__status=Order.STATUS_PAID, then=1),
|
Case(When(order__status=Order.STATUS_PAID, then=1),
|
||||||
output_field=models.IntegerField())
|
output_field=models.IntegerField())
|
||||||
|
|||||||
@@ -6,9 +6,8 @@ from django.db import models
|
|||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from typing import List, Union
|
from typing import List, Union
|
||||||
from versions.models import VersionedForeignKey
|
|
||||||
|
|
||||||
from .base import CachedFile, Versionable
|
from .base import CachedFile
|
||||||
from .event import Event
|
from .event import Event
|
||||||
from .items import Item, ItemVariation, Question, Quota
|
from .items import Item, ItemVariation, Question, Quota
|
||||||
|
|
||||||
@@ -17,7 +16,7 @@ def generate_secret():
|
|||||||
return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(16))
|
return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(16))
|
||||||
|
|
||||||
|
|
||||||
class Order(Versionable):
|
class Order(models.Model):
|
||||||
"""
|
"""
|
||||||
An order is created when a user clicks 'buy' on his cart. It holds
|
An order is created when a user clicks 'buy' on his cart. It holds
|
||||||
several OrderPositions and is connected to an user. It has an
|
several OrderPositions and is connected to an user. It has an
|
||||||
@@ -83,7 +82,7 @@ class Order(Versionable):
|
|||||||
choices=STATUS_CHOICE,
|
choices=STATUS_CHOICE,
|
||||||
verbose_name=_("Status")
|
verbose_name=_("Status")
|
||||||
)
|
)
|
||||||
event = VersionedForeignKey(
|
event = models.ForeignKey(
|
||||||
Event,
|
Event,
|
||||||
verbose_name=_("Event"),
|
verbose_name=_("Event"),
|
||||||
related_name="orders"
|
related_name="orders"
|
||||||
@@ -180,13 +179,11 @@ class Order(Versionable):
|
|||||||
|
|
||||||
def mark_refunded(self):
|
def mark_refunded(self):
|
||||||
"""
|
"""
|
||||||
Mark this order as refunded. This clones the order object, sets the payment status and
|
Mark this order as refunded. This sets the payment status and returns the order object.
|
||||||
returns the cloned order object.
|
|
||||||
"""
|
"""
|
||||||
order = self.clone()
|
self.status = Order.STATUS_REFUNDED
|
||||||
order.status = Order.STATUS_REFUNDED
|
self.save()
|
||||||
order.save()
|
return self
|
||||||
return order
|
|
||||||
|
|
||||||
def _can_be_paid(self) -> Union[bool, str]:
|
def _can_be_paid(self) -> Union[bool, str]:
|
||||||
error_messages = {
|
error_messages = {
|
||||||
@@ -223,12 +220,12 @@ class Order(Versionable):
|
|||||||
for quota in quotas:
|
for quota in quotas:
|
||||||
# Lock the quota, so no other thread is allowed to perform sales covered by this
|
# Lock the quota, so no other thread is allowed to perform sales covered by this
|
||||||
# quota while we're doing so.
|
# quota while we're doing so.
|
||||||
if quota.identity not in quota_cache:
|
if quota.id not in quota_cache:
|
||||||
quota_cache[quota.identity] = quota
|
quota_cache[quota.id] = quota
|
||||||
quota.cached_availability = quota.availability()[1]
|
quota.cached_availability = quota.availability()[1]
|
||||||
else:
|
else:
|
||||||
# Use cached version
|
# Use cached version
|
||||||
quota = quota_cache[quota.identity]
|
quota = quota_cache[quota.id]
|
||||||
if quota.cached_availability is not None:
|
if quota.cached_availability is not None:
|
||||||
quota.cached_availability -= 1
|
quota.cached_availability -= 1
|
||||||
if quota.cached_availability < 0:
|
if quota.cached_availability < 0:
|
||||||
@@ -240,12 +237,12 @@ class Order(Versionable):
|
|||||||
|
|
||||||
|
|
||||||
class CachedTicket(models.Model):
|
class CachedTicket(models.Model):
|
||||||
order = VersionedForeignKey(Order, on_delete=models.CASCADE)
|
order = models.ForeignKey(Order, on_delete=models.CASCADE)
|
||||||
cachedfile = models.ForeignKey(CachedFile, on_delete=models.CASCADE)
|
cachedfile = models.ForeignKey(CachedFile, on_delete=models.CASCADE)
|
||||||
provider = models.CharField(max_length=255)
|
provider = models.CharField(max_length=255)
|
||||||
|
|
||||||
|
|
||||||
class QuestionAnswer(Versionable):
|
class QuestionAnswer(models.Model):
|
||||||
"""
|
"""
|
||||||
The answer to a Question, connected to an OrderPosition or CartPosition.
|
The answer to a Question, connected to an OrderPosition or CartPosition.
|
||||||
|
|
||||||
@@ -268,7 +265,7 @@ class QuestionAnswer(Versionable):
|
|||||||
'CartPosition', null=True, blank=True,
|
'CartPosition', null=True, blank=True,
|
||||||
related_name='answers'
|
related_name='answers'
|
||||||
)
|
)
|
||||||
question = VersionedForeignKey(
|
question = models.ForeignKey(
|
||||||
Question, related_name='answers'
|
Question, related_name='answers'
|
||||||
)
|
)
|
||||||
answer = models.TextField()
|
answer = models.TextField()
|
||||||
@@ -286,14 +283,14 @@ class ObjectWithAnswers:
|
|||||||
self.answ[a.question_id] = a.answer
|
self.answ[a.question_id] = a.answer
|
||||||
self.questions = []
|
self.questions = []
|
||||||
for q in self.item.questions.all():
|
for q in self.item.questions.all():
|
||||||
if q.identity in self.answ:
|
if q.id in self.answ:
|
||||||
q.answer = self.answ[q.identity]
|
q.answer = self.answ[q.id]
|
||||||
else:
|
else:
|
||||||
q.answer = ""
|
q.answer = ""
|
||||||
self.questions.append(q)
|
self.questions.append(q)
|
||||||
|
|
||||||
|
|
||||||
class OrderPosition(ObjectWithAnswers, Versionable):
|
class OrderPosition(ObjectWithAnswers, models.Model):
|
||||||
"""
|
"""
|
||||||
An OrderPosition is one line of an order, representing one ordered items
|
An OrderPosition is one line of an order, representing one ordered items
|
||||||
of a specified type (or variation).
|
of a specified type (or variation).
|
||||||
@@ -309,20 +306,23 @@ class OrderPosition(ObjectWithAnswers, Versionable):
|
|||||||
:param attendee_name: The attendee's name, if entered.
|
:param attendee_name: The attendee's name, if entered.
|
||||||
:type attendee_name: str
|
:type attendee_name: str
|
||||||
"""
|
"""
|
||||||
order = VersionedForeignKey(
|
order = models.ForeignKey(
|
||||||
Order,
|
Order,
|
||||||
verbose_name=_("Order"),
|
verbose_name=_("Order"),
|
||||||
related_name='positions'
|
related_name='positions',
|
||||||
|
on_delete=models.PROTECT
|
||||||
)
|
)
|
||||||
item = VersionedForeignKey(
|
item = models.ForeignKey(
|
||||||
Item,
|
Item,
|
||||||
verbose_name=_("Item"),
|
verbose_name=_("Item"),
|
||||||
related_name='positions'
|
related_name='positions',
|
||||||
|
on_delete=models.PROTECT
|
||||||
)
|
)
|
||||||
variation = VersionedForeignKey(
|
variation = models.ForeignKey(
|
||||||
ItemVariation,
|
ItemVariation,
|
||||||
null=True, blank=True,
|
null=True, blank=True,
|
||||||
verbose_name=_("Variation")
|
verbose_name=_("Variation"),
|
||||||
|
on_delete=models.PROTECT
|
||||||
)
|
)
|
||||||
price = models.DecimalField(
|
price = models.DecimalField(
|
||||||
decimal_places=2, max_digits=10,
|
decimal_places=2, max_digits=10,
|
||||||
@@ -347,17 +347,16 @@ class OrderPosition(ObjectWithAnswers, Versionable):
|
|||||||
order=order, item=cartpos.item, variation=cartpos.variation,
|
order=order, item=cartpos.item, variation=cartpos.variation,
|
||||||
price=cartpos.price, attendee_name=cartpos.attendee_name
|
price=cartpos.price, attendee_name=cartpos.attendee_name
|
||||||
)
|
)
|
||||||
|
op.save()
|
||||||
for answ in cartpos.answers.all():
|
for answ in cartpos.answers.all():
|
||||||
answ = answ.clone()
|
|
||||||
answ.orderposition = op
|
answ.orderposition = op
|
||||||
answ.cartposition = None
|
answ.cartposition = None
|
||||||
answ.save()
|
answ.save()
|
||||||
op.save()
|
|
||||||
cartpos.delete()
|
cartpos.delete()
|
||||||
ops.append(op)
|
ops.append(op)
|
||||||
|
|
||||||
|
|
||||||
class CartPosition(ObjectWithAnswers, Versionable):
|
class CartPosition(ObjectWithAnswers, models.Model):
|
||||||
"""
|
"""
|
||||||
A cart position is similar to a order line, except that it is not
|
A cart position is similar to a order line, except that it is not
|
||||||
yet part of a binding order but just placed by some user in his or
|
yet part of a binding order but just placed by some user in his or
|
||||||
@@ -383,7 +382,7 @@ class CartPosition(ObjectWithAnswers, Versionable):
|
|||||||
:param attendee_name: The attendee's name, if entered.
|
:param attendee_name: The attendee's name, if entered.
|
||||||
:type attendee_name: str
|
:type attendee_name: str
|
||||||
"""
|
"""
|
||||||
event = VersionedForeignKey(
|
event = models.ForeignKey(
|
||||||
Event,
|
Event,
|
||||||
verbose_name=_("Event")
|
verbose_name=_("Event")
|
||||||
)
|
)
|
||||||
@@ -391,14 +390,16 @@ class CartPosition(ObjectWithAnswers, Versionable):
|
|||||||
max_length=255, null=True, blank=True,
|
max_length=255, null=True, blank=True,
|
||||||
verbose_name=_("Cart ID (e.g. session key)")
|
verbose_name=_("Cart ID (e.g. session key)")
|
||||||
)
|
)
|
||||||
item = VersionedForeignKey(
|
item = models.ForeignKey(
|
||||||
Item,
|
Item,
|
||||||
verbose_name=_("Item")
|
verbose_name=_("Item"),
|
||||||
|
on_delete=models.CASCADE
|
||||||
)
|
)
|
||||||
variation = VersionedForeignKey(
|
variation = models.ForeignKey(
|
||||||
ItemVariation,
|
ItemVariation,
|
||||||
null=True, blank=True,
|
null=True, blank=True,
|
||||||
verbose_name=_("Variation")
|
verbose_name=_("Variation"),
|
||||||
|
on_delete=models.CASCADE
|
||||||
)
|
)
|
||||||
price = models.DecimalField(
|
price = models.DecimalField(
|
||||||
decimal_places=2, max_digits=10,
|
decimal_places=2, max_digits=10,
|
||||||
@@ -421,3 +422,8 @@ class CartPosition(ObjectWithAnswers, Versionable):
|
|||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Cart position")
|
verbose_name = _("Cart position")
|
||||||
verbose_name_plural = _("Cart positions")
|
verbose_name_plural = _("Cart positions")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '<CartPosition: item %d, variation %d for cart %s>' % (
|
||||||
|
self.item.id, self.variation.id if self.variation else 0, self.cart_id
|
||||||
|
)
|
||||||
|
|||||||
@@ -2,15 +2,13 @@ from django.core.validators import RegexValidator
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from versions.models import VersionedForeignKey
|
|
||||||
|
|
||||||
from pretix.base.settings import SettingsProxy
|
from pretix.base.settings import SettingsProxy
|
||||||
|
|
||||||
from .auth import User
|
from .auth import User
|
||||||
from .base import Versionable
|
|
||||||
|
|
||||||
|
|
||||||
class Organizer(Versionable):
|
class Organizer(models.Model):
|
||||||
"""
|
"""
|
||||||
This model represents an entity organizing events, e.g. a company, institution,
|
This model represents an entity organizing events, e.g. a company, institution,
|
||||||
charity, person, …
|
charity, person, …
|
||||||
@@ -72,7 +70,7 @@ class Organizer(Versionable):
|
|||||||
return ObjectRelatedCache(self)
|
return ObjectRelatedCache(self)
|
||||||
|
|
||||||
|
|
||||||
class OrganizerPermission(Versionable):
|
class OrganizerPermission(models.Model):
|
||||||
"""
|
"""
|
||||||
The relation between an Organizer and an User who has permissions to
|
The relation between an Organizer and an User who has permissions to
|
||||||
access an organizer profile.
|
access an organizer profile.
|
||||||
@@ -86,7 +84,7 @@ class OrganizerPermission(Versionable):
|
|||||||
:type can_create_events: bool
|
:type can_create_events: bool
|
||||||
"""
|
"""
|
||||||
|
|
||||||
organizer = VersionedForeignKey(Organizer)
|
organizer = models.ForeignKey(Organizer, related_name="user_perms", on_delete=models.CASCADE)
|
||||||
user = models.ForeignKey(User, related_name="organizer_perms")
|
user = models.ForeignKey(User, related_name="organizer_perms")
|
||||||
can_create_events = models.BooleanField(
|
can_create_events = models.BooleanField(
|
||||||
default=True,
|
default=True,
|
||||||
@@ -104,11 +102,11 @@ class OrganizerPermission(Versionable):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class OrganizerSetting(Versionable):
|
class OrganizerSetting(models.Model):
|
||||||
"""
|
"""
|
||||||
An event option is a key-value setting which can be set for an
|
An event option is a key-value setting which can be set for an
|
||||||
organizer. It will be inherited by the events of this organizer
|
organizer. It will be inherited by the events of this organizer
|
||||||
"""
|
"""
|
||||||
object = VersionedForeignKey(Organizer, related_name='setting_objects')
|
object = models.ForeignKey(Organizer, related_name='setting_objects', on_delete=models.CASCADE)
|
||||||
key = models.CharField(max_length=255)
|
key = models.CharField(max_length=255)
|
||||||
value = models.TextField()
|
value = models.TextField()
|
||||||
|
|||||||
@@ -258,9 +258,7 @@ class BasePaymentProvider:
|
|||||||
|
|
||||||
If the payment is completed, you should call ``pretix.base.services.orders.mark_order_paid(order, provider, info)``
|
If the payment is completed, you should call ``pretix.base.services.orders.mark_order_paid(order, provider, info)``
|
||||||
with ``provider`` being your :py:attr:`identifier` and ``info`` being any string
|
with ``provider`` being your :py:attr:`identifier` and ``info`` being any string
|
||||||
you might want to store for later usage. Please note, that if you want to store
|
you might want to store for later usage. Please note that ``mark_order_paid`` might
|
||||||
something inside ``order.payment_info``, please do it after the ``mark_order_paid`` call,
|
|
||||||
as this call does a object clone for you. Please also note that ``mark_order_paid`` might
|
|
||||||
raise a ``Quota.QuotaExceededException`` if (and only if) the payment term of this
|
raise a ``Quota.QuotaExceededException`` if (and only if) the payment term of this
|
||||||
order is over and some of the items are sold out. You should use the exception message
|
order is over and some of the items are sold out. You should use the exception message
|
||||||
to display a meaningful error to the user.
|
to display a meaningful error to the user.
|
||||||
@@ -403,7 +401,7 @@ class FreeOrderProvider(BasePaymentProvider):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def payment_is_valid_session(self, request: HttpRequest) -> bool:
|
def payment_is_valid_session(self, request: HttpRequest) -> bool:
|
||||||
return CartPosition.objects.current.filter(
|
return CartPosition.objects.filter(
|
||||||
Q(cart_id=request.session.session_key) & Q(event=request.event)
|
Q(cart_id=request.session.session_key) & Q(event=request.event)
|
||||||
).aggregate(sum=Sum('price'))['sum'] == 0
|
).aggregate(sum=Sum('price'))['sum'] == 0
|
||||||
|
|
||||||
@@ -446,7 +444,7 @@ class FreeOrderProvider(BasePaymentProvider):
|
|||||||
messages.success(request, _('The order has been marked as refunded.'))
|
messages.success(request, _('The order has been marked as refunded.'))
|
||||||
|
|
||||||
def is_allowed(self, request: HttpRequest) -> bool:
|
def is_allowed(self, request: HttpRequest) -> bool:
|
||||||
return CartPosition.objects.current.filter(
|
return CartPosition.objects.filter(
|
||||||
cart_id=request.session.session_key, event=request.event
|
cart_id=request.session.session_key, event=request.event
|
||||||
).aggregate(sum=Sum('price'))['sum'] == 0
|
).aggregate(sum=Sum('price'))['sum'] == 0
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ def _extend_existing(event: Event, cart_id: str, expiry: datetime) -> None:
|
|||||||
# Extend this user's cart session to 30 minutes from now to ensure all items in the
|
# Extend this user's cart session to 30 minutes from now to ensure all items in the
|
||||||
# cart expire at the same time
|
# cart expire at the same time
|
||||||
# We can extend the reservation of items which are not yet expired without risk
|
# We can extend the reservation of items which are not yet expired without risk
|
||||||
CartPosition.objects.current.filter(
|
CartPosition.objects.filter(
|
||||||
Q(cart_id=cart_id) & Q(event=event) & Q(expires__gt=now())
|
Q(cart_id=cart_id) & Q(event=event) & Q(expires__gt=now())
|
||||||
).update(expires=expiry)
|
).update(expires=expiry)
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ def _re_add_expired_positions(items: List[CartPosition], event: Event, cart_id:
|
|||||||
positions = set()
|
positions = set()
|
||||||
# For items that are already expired, we have to delete and re-add them, as they might
|
# For items that are already expired, we have to delete and re-add them, as they might
|
||||||
# be no longer available or prices might have changed. Sorry!
|
# be no longer available or prices might have changed. Sorry!
|
||||||
expired = CartPosition.objects.current.filter(
|
expired = CartPosition.objects.filter(
|
||||||
Q(cart_id=cart_id) & Q(event=event) & Q(expires__lte=now())
|
Q(cart_id=cart_id) & Q(event=event) & Q(expires__lte=now())
|
||||||
)
|
)
|
||||||
for cp in expired:
|
for cp in expired:
|
||||||
@@ -55,7 +55,7 @@ def _re_add_expired_positions(items: List[CartPosition], event: Event, cart_id:
|
|||||||
|
|
||||||
def _delete_expired(expired: List[CartPosition]) -> None:
|
def _delete_expired(expired: List[CartPosition]) -> None:
|
||||||
for cp in expired:
|
for cp in expired:
|
||||||
if cp.version_end_date is None:
|
if cp.expires <= now():
|
||||||
cp.delete()
|
cp.delete()
|
||||||
|
|
||||||
|
|
||||||
@@ -66,19 +66,19 @@ def _check_date(event: Event) -> None:
|
|||||||
raise CartError(error_messages['ended'])
|
raise CartError(error_messages['ended'])
|
||||||
|
|
||||||
|
|
||||||
def _add_new_items(event: Event, items: List[Tuple[str, Optional[str], int]],
|
def _add_new_items(event: Event, items: List[Tuple[int, Optional[int], int]],
|
||||||
cart_id: str, expiry: datetime) -> Optional[str]:
|
cart_id: str, expiry: datetime) -> Optional[str]:
|
||||||
err = None
|
err = None
|
||||||
|
|
||||||
# Fetch items from the database
|
# Fetch items from the database
|
||||||
items_query = Item.objects.current.filter(event=event, identity__in=[i[0] for i in items]).prefetch_related(
|
items_query = Item.objects.filter(event=event, id__in=[i[0] for i in items]).prefetch_related(
|
||||||
"quotas")
|
"quotas")
|
||||||
items_cache = {i.identity: i for i in items_query}
|
items_cache = {i.id: i for i in items_query}
|
||||||
variations_query = ItemVariation.objects.current.filter(
|
variations_query = ItemVariation.objects.filter(
|
||||||
item__event=event,
|
item__event=event,
|
||||||
identity__in=[i[1] for i in items if i[1] is not None]
|
id__in=[i[1] for i in items if i[1] is not None]
|
||||||
).select_related("item", "item__event").prefetch_related("quotas", "values", "values__prop")
|
).select_related("item", "item__event").prefetch_related("quotas", "values", "values__prop")
|
||||||
variations_cache = {v.identity: v for v in variations_query}
|
variations_cache = {v.id: v for v in variations_query}
|
||||||
|
|
||||||
for i in items:
|
for i in items:
|
||||||
# Check whether the specified items are part of what we just fetched from the database
|
# Check whether the specified items are part of what we just fetched from the database
|
||||||
@@ -115,7 +115,7 @@ def _add_new_items(event: Event, items: List[Tuple[str, Optional[str], int]],
|
|||||||
for k in range(quota_ok):
|
for k in range(quota_ok):
|
||||||
if len(i) > 3 and i[2] == 1:
|
if len(i) > 3 and i[2] == 1:
|
||||||
# Recreating
|
# Recreating
|
||||||
cp = i[3].clone()
|
cp = i[3]
|
||||||
cp.expires = expiry
|
cp.expires = expiry
|
||||||
cp.price = item.default_price if variation is None else (
|
cp.price = item.default_price if variation is None else (
|
||||||
variation.default_price if variation.default_price is not None else item.default_price)
|
variation.default_price if variation.default_price is not None else item.default_price)
|
||||||
@@ -131,10 +131,10 @@ def _add_new_items(event: Event, items: List[Tuple[str, Optional[str], int]],
|
|||||||
return err
|
return err
|
||||||
|
|
||||||
|
|
||||||
def _add_items_to_cart(event: Event, items: List[Tuple[str, Optional[str], int]], cart_id: str=None) -> None:
|
def _add_items_to_cart(event: Event, items: List[Tuple[int, Optional[int], int]], cart_id: str=None) -> None:
|
||||||
with event.lock():
|
with event.lock():
|
||||||
_check_date(event)
|
_check_date(event)
|
||||||
existing = CartPosition.objects.current.filter(Q(cart_id=cart_id) & Q(event=event)).count()
|
existing = CartPosition.objects.filter(Q(cart_id=cart_id) & Q(event=event)).count()
|
||||||
if sum(i[2] for i in items) + existing > int(event.settings.max_items_per_order):
|
if sum(i[2] for i in items) + existing > int(event.settings.max_items_per_order):
|
||||||
# TODO: i18n plurals
|
# TODO: i18n plurals
|
||||||
raise CartError(error_messages['max_items'] % event.settings.max_items_per_order)
|
raise CartError(error_messages['max_items'] % event.settings.max_items_per_order)
|
||||||
@@ -152,7 +152,7 @@ def _add_items_to_cart(event: Event, items: List[Tuple[str, Optional[str], int]]
|
|||||||
raise CartError(err)
|
raise CartError(err)
|
||||||
|
|
||||||
|
|
||||||
def add_items_to_cart(event: str, items: List[Tuple[str, Optional[str], int]], cart_id: str=None) -> None:
|
def add_items_to_cart(event: int, items: List[Tuple[int, Optional[int], int]], cart_id: str=None) -> None:
|
||||||
"""
|
"""
|
||||||
Adds a list of items to a user's cart.
|
Adds a list of items to a user's cart.
|
||||||
:param event: The event ID in question
|
:param event: The event ID in question
|
||||||
@@ -160,21 +160,21 @@ def add_items_to_cart(event: str, items: List[Tuple[str, Optional[str], int]], c
|
|||||||
:param session: Session ID of a guest
|
:param session: Session ID of a guest
|
||||||
:raises CartError: On any error that occured
|
:raises CartError: On any error that occured
|
||||||
"""
|
"""
|
||||||
event = Event.objects.current.get(identity=event)
|
event = Event.objects.get(id=event)
|
||||||
try:
|
try:
|
||||||
_add_items_to_cart(event, items, cart_id)
|
_add_items_to_cart(event, items, cart_id)
|
||||||
except EventLock.LockTimeoutException:
|
except EventLock.LockTimeoutException:
|
||||||
raise CartError(error_messages['busy'])
|
raise CartError(error_messages['busy'])
|
||||||
|
|
||||||
|
|
||||||
def remove_items_from_cart(event: str, items: List[Tuple[str, Optional[str], int]], cart_id: str=None) -> None:
|
def remove_items_from_cart(event: int, items: List[Tuple[int, Optional[int], int]], cart_id: int=None) -> None:
|
||||||
"""
|
"""
|
||||||
Removes a list of items from a user's cart.
|
Removes a list of items from a user's cart.
|
||||||
:param event: The event ID in question
|
:param event: The event ID in question
|
||||||
:param items: A list of tuple of the form (item id, variation id or None, number)
|
:param items: A list of tuple of the form (item id, variation id or None, number)
|
||||||
:param session: Session ID of a guest
|
:param session: Session ID of a guest
|
||||||
"""
|
"""
|
||||||
event = Event.objects.current.get(identity=event)
|
event = Event.objects.get(id=event)
|
||||||
|
|
||||||
for item, variation, cnt in items:
|
for item, variation, cnt in items:
|
||||||
cw = Q(cart_id=cart_id) & Q(item_id=item) & Q(event=event)
|
cw = Q(cart_id=cart_id) & Q(item_id=item) & Q(event=event)
|
||||||
@@ -182,7 +182,7 @@ def remove_items_from_cart(event: str, items: List[Tuple[str, Optional[str], int
|
|||||||
cw &= Q(variation_id=variation)
|
cw &= Q(variation_id=variation)
|
||||||
else:
|
else:
|
||||||
cw &= Q(variation__isnull=True)
|
cw &= Q(variation__isnull=True)
|
||||||
for cp in CartPosition.objects.current.filter(cw).order_by("-price")[:cnt]:
|
for cp in CartPosition.objects.filter(cw).order_by("-price")[:cnt]:
|
||||||
cp.delete()
|
cp.delete()
|
||||||
|
|
||||||
|
|
||||||
@@ -190,8 +190,8 @@ if settings.HAS_CELERY:
|
|||||||
from pretix.celery import app
|
from pretix.celery import app
|
||||||
|
|
||||||
@app.task(bind=True, max_retries=5, default_retry_delay=2)
|
@app.task(bind=True, max_retries=5, default_retry_delay=2)
|
||||||
def add_items_to_cart_task(self, event: str, items: List[Tuple[str, Optional[str], int]], cart_id: str):
|
def add_items_to_cart_task(self, event: int, items: List[Tuple[int, Optional[int], int]], cart_id: str):
|
||||||
event = Event.objects.current.get(identity=event)
|
event = Event.objects.get(id=event)
|
||||||
try:
|
try:
|
||||||
_add_items_to_cart(event, items, cart_id)
|
_add_items_to_cart(event, items, cart_id)
|
||||||
except EventLock.LockTimeoutException:
|
except EventLock.LockTimeoutException:
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from pretix.base.signals import register_data_exporters
|
|||||||
|
|
||||||
|
|
||||||
def export(event: str, fileid: str, provider: str, form_data: Dict[str, Any]) -> None:
|
def export(event: str, fileid: str, provider: str, form_data: Dict[str, Any]) -> None:
|
||||||
event = Event.objects.current.get(identity=event)
|
event = Event.objects.get(id=event)
|
||||||
file = CachedFile.objects.get(id=fileid)
|
file = CachedFile.objects.get(id=fileid)
|
||||||
responses = register_data_exporters.send(event)
|
responses = register_data_exporters.send(event)
|
||||||
for receiver, response in responses:
|
for receiver, response in responses:
|
||||||
|
|||||||
@@ -64,13 +64,13 @@ def lock_event_db(event):
|
|||||||
for i in range(retries):
|
for i in range(retries):
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
dt = now()
|
dt = now()
|
||||||
l, created = EventLock.objects.get_or_create(event=event.identity)
|
l, created = EventLock.objects.get_or_create(event=event.id)
|
||||||
if created:
|
if created:
|
||||||
event._lock = l
|
event._lock = l
|
||||||
return True
|
return True
|
||||||
elif l.date < now() - timedelta(seconds=LOCK_TIMEOUT):
|
elif l.date < now() - timedelta(seconds=LOCK_TIMEOUT):
|
||||||
newtoken = uuid.uuid4()
|
newtoken = uuid.uuid4()
|
||||||
updated = EventLock.objects.filter(event=event.identity, token=l.token).update(date=dt, token=newtoken)
|
updated = EventLock.objects.filter(event=event.id, token=l.token).update(date=dt, token=newtoken)
|
||||||
if updated:
|
if updated:
|
||||||
l.token = newtoken
|
l.token = newtoken
|
||||||
event._lock = l
|
event._lock = l
|
||||||
@@ -84,7 +84,7 @@ def release_event_db(event):
|
|||||||
if not hasattr(event, '_lock') or not event._lock:
|
if not hasattr(event, '_lock') or not event._lock:
|
||||||
raise EventLock.LockReleaseException('Lock is not owned by this thread')
|
raise EventLock.LockReleaseException('Lock is not owned by this thread')
|
||||||
try:
|
try:
|
||||||
lock = EventLock.objects.get(event=event.identity, token=event._lock.token)
|
lock = EventLock.objects.get(event=event.id, token=event._lock.token)
|
||||||
lock.delete()
|
lock.delete()
|
||||||
event._lock = None
|
event._lock = None
|
||||||
except EventLock.DoesNotExist:
|
except EventLock.DoesNotExist:
|
||||||
@@ -97,7 +97,7 @@ def redis_lock_from_event(event):
|
|||||||
|
|
||||||
if not hasattr(event, '_lock') or not event._lock:
|
if not hasattr(event, '_lock') or not event._lock:
|
||||||
rc = get_redis_connection("redis")
|
rc = get_redis_connection("redis")
|
||||||
event._lock = Lock(redis=rc, name='pretix_event_%s' % event.identity, timeout=LOCK_TIMEOUT)
|
event._lock = Lock(redis=rc, name='pretix_event_%s' % event.id, timeout=LOCK_TIMEOUT)
|
||||||
return event._lock
|
return event._lock
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ error_messages = {
|
|||||||
def mark_order_paid(order: Order, provider: str=None, info: str=None, date: datetime=None, manual: bool=None,
|
def mark_order_paid(order: Order, provider: str=None, info: str=None, date: datetime=None, manual: bool=None,
|
||||||
force: bool=False) -> Order:
|
force: bool=False) -> Order:
|
||||||
"""
|
"""
|
||||||
Marks an order as paid. This clones the order object, sets the payment provider,
|
Marks an order as paid. This sets the payment provider, info and date and returns
|
||||||
info and date and returns the cloned order object.
|
the order object.
|
||||||
|
|
||||||
:param provider: The payment provider that marked this as paid
|
:param provider: The payment provider that marked this as paid
|
||||||
:type provider: str
|
:type provider: str
|
||||||
@@ -52,7 +52,6 @@ def mark_order_paid(order: Order, provider: str=None, info: str=None, date: date
|
|||||||
can_be_paid = order._can_be_paid()
|
can_be_paid = order._can_be_paid()
|
||||||
if not force and can_be_paid is not True:
|
if not force and can_be_paid is not True:
|
||||||
raise Quota.QuotaExceededException(can_be_paid)
|
raise Quota.QuotaExceededException(can_be_paid)
|
||||||
order = order.clone()
|
|
||||||
order.payment_provider = provider or order.payment_provider
|
order.payment_provider = provider or order.payment_provider
|
||||||
order.payment_info = info or order.payment_info
|
order.payment_info = info or order.payment_info
|
||||||
order.payment_date = date or now()
|
order.payment_date = date or now()
|
||||||
@@ -110,7 +109,6 @@ def _check_positions(event: Event, dt: datetime, positions: List[CartPosition]):
|
|||||||
cp.delete()
|
cp.delete()
|
||||||
continue
|
continue
|
||||||
if price != cp.price:
|
if price != cp.price:
|
||||||
cp = cp.clone()
|
|
||||||
positions[i] = cp
|
positions[i] = cp
|
||||||
cp.price = price
|
cp.price = price
|
||||||
cp.save()
|
cp.save()
|
||||||
@@ -125,7 +123,6 @@ def _check_positions(event: Event, dt: datetime, positions: List[CartPosition]):
|
|||||||
quota_ok = False
|
quota_ok = False
|
||||||
break
|
break
|
||||||
if quota_ok:
|
if quota_ok:
|
||||||
cp = cp.clone()
|
|
||||||
positions[i] = cp
|
positions[i] = cp
|
||||||
cp.expires = now() + timedelta(
|
cp.expires = now() + timedelta(
|
||||||
minutes=event.settings.get('reservation_time', as_type=int))
|
minutes=event.settings.get('reservation_time', as_type=int))
|
||||||
@@ -163,7 +160,7 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], dt: d
|
|||||||
|
|
||||||
def _perform_order(event: str, payment_provider: str, position_ids: List[str],
|
def _perform_order(event: str, payment_provider: str, position_ids: List[str],
|
||||||
email: str, locale: str):
|
email: str, locale: str):
|
||||||
event = Event.objects.current.get(identity=event)
|
event = Event.objects.get(id=event)
|
||||||
responses = register_payment_providers.send(event)
|
responses = register_payment_providers.send(event)
|
||||||
pprov = None
|
pprov = None
|
||||||
for receiver, response in responses:
|
for receiver, response in responses:
|
||||||
@@ -175,8 +172,8 @@ def _perform_order(event: str, payment_provider: str, position_ids: List[str],
|
|||||||
|
|
||||||
dt = now()
|
dt = now()
|
||||||
with event.lock():
|
with event.lock():
|
||||||
positions = list(CartPosition.objects.current.filter(
|
positions = list(CartPosition.objects.filter(
|
||||||
identity__in=position_ids).select_related('item', 'variation'))
|
id__in=position_ids).select_related('item', 'variation'))
|
||||||
if len(position_ids) != len(positions):
|
if len(position_ids) != len(positions):
|
||||||
raise OrderError(error_messages['internal'])
|
raise OrderError(error_messages['internal'])
|
||||||
_check_positions(event, dt, positions)
|
_check_positions(event, dt, positions)
|
||||||
@@ -197,7 +194,7 @@ def _perform_order(event: str, payment_provider: str, position_ids: List[str],
|
|||||||
},
|
},
|
||||||
event, locale=order.locale
|
event, locale=order.locale
|
||||||
)
|
)
|
||||||
return order.identity
|
return order.id
|
||||||
|
|
||||||
|
|
||||||
def perform_order(event: str, payment_provider: str, positions: List[str],
|
def perform_order(event: str, payment_provider: str, positions: List[str],
|
||||||
|
|||||||
@@ -37,28 +37,28 @@ def order_overview(event: Event) -> Tuple[List[Tuple[ItemCategory, List[Item]]],
|
|||||||
|
|
||||||
num_total = {
|
num_total = {
|
||||||
(p['item'], p['variation']): (p['cnt'], p['price'])
|
(p['item'], p['variation']): (p['cnt'], p['price'])
|
||||||
for p in (OrderPosition.objects.current
|
for p in (OrderPosition.objects
|
||||||
.filter(order__event=event)
|
.filter(order__event=event)
|
||||||
.values('item', 'variation')
|
.values('item', 'variation')
|
||||||
.annotate(cnt=Count('id'), price=Sum('price')).order_by())
|
.annotate(cnt=Count('id'), price=Sum('price')).order_by())
|
||||||
}
|
}
|
||||||
num_cancelled = {
|
num_cancelled = {
|
||||||
(p['item'], p['variation']): (p['cnt'], p['price'])
|
(p['item'], p['variation']): (p['cnt'], p['price'])
|
||||||
for p in (OrderPosition.objects.current
|
for p in (OrderPosition.objects
|
||||||
.filter(order__event=event, order__status=Order.STATUS_CANCELLED)
|
.filter(order__event=event, order__status=Order.STATUS_CANCELLED)
|
||||||
.values('item', 'variation')
|
.values('item', 'variation')
|
||||||
.annotate(cnt=Count('id'), price=Sum('price')).order_by())
|
.annotate(cnt=Count('id'), price=Sum('price')).order_by())
|
||||||
}
|
}
|
||||||
num_refunded = {
|
num_refunded = {
|
||||||
(p['item'], p['variation']): (p['cnt'], p['price'])
|
(p['item'], p['variation']): (p['cnt'], p['price'])
|
||||||
for p in (OrderPosition.objects.current
|
for p in (OrderPosition.objects
|
||||||
.filter(order__event=event, order__status=Order.STATUS_REFUNDED)
|
.filter(order__event=event, order__status=Order.STATUS_REFUNDED)
|
||||||
.values('item', 'variation')
|
.values('item', 'variation')
|
||||||
.annotate(cnt=Count('id'), price=Sum('price')).order_by())
|
.annotate(cnt=Count('id'), price=Sum('price')).order_by())
|
||||||
}
|
}
|
||||||
num_pending = {
|
num_pending = {
|
||||||
(p['item'], p['variation']): (p['cnt'], p['price'])
|
(p['item'], p['variation']): (p['cnt'], p['price'])
|
||||||
for p in (OrderPosition.objects.current
|
for p in (OrderPosition.objects
|
||||||
.filter(order__event=event,
|
.filter(order__event=event,
|
||||||
order__status__in=(Order.STATUS_PENDING, Order.STATUS_EXPIRED))
|
order__status__in=(Order.STATUS_PENDING, Order.STATUS_EXPIRED))
|
||||||
.values('item', 'variation')
|
.values('item', 'variation')
|
||||||
@@ -66,7 +66,7 @@ def order_overview(event: Event) -> Tuple[List[Tuple[ItemCategory, List[Item]]],
|
|||||||
}
|
}
|
||||||
num_paid = {
|
num_paid = {
|
||||||
(p['item'], p['variation']): (p['cnt'], p['price'])
|
(p['item'], p['variation']): (p['cnt'], p['price'])
|
||||||
for p in (OrderPosition.objects.current
|
for p in (OrderPosition.objects
|
||||||
.filter(order__event=event, order__status=Order.STATUS_PAID)
|
.filter(order__event=event, order__status=Order.STATUS_PAID)
|
||||||
.values('item', 'variation')
|
.values('item', 'variation')
|
||||||
.annotate(cnt=Count('id'), price=Sum('price')).order_by())
|
.annotate(cnt=Count('id'), price=Sum('price')).order_by())
|
||||||
@@ -76,12 +76,12 @@ def order_overview(event: Event) -> Tuple[List[Tuple[ItemCategory, List[Item]]],
|
|||||||
item.all_variations = sorted(item.get_all_variations(),
|
item.all_variations = sorted(item.get_all_variations(),
|
||||||
key=lambda vd: vd.ordered_values())
|
key=lambda vd: vd.ordered_values())
|
||||||
for var in item.all_variations:
|
for var in item.all_variations:
|
||||||
variid = var['variation'].identity if 'variation' in var else None
|
variid = var['variation'].id if 'variation' in var else None
|
||||||
var.num_total = num_total.get((item.identity, variid), (0, 0))
|
var.num_total = num_total.get((item.id, variid), (0, 0))
|
||||||
var.num_pending = num_pending.get((item.identity, variid), (0, 0))
|
var.num_pending = num_pending.get((item.id, variid), (0, 0))
|
||||||
var.num_cancelled = num_cancelled.get((item.identity, variid), (0, 0))
|
var.num_cancelled = num_cancelled.get((item.id, variid), (0, 0))
|
||||||
var.num_refunded = num_refunded.get((item.identity, variid), (0, 0))
|
var.num_refunded = num_refunded.get((item.id, variid), (0, 0))
|
||||||
var.num_paid = num_paid.get((item.identity, variid), (0, 0))
|
var.num_paid = num_paid.get((item.id, variid), (0, 0))
|
||||||
item.has_variations = (len(item.all_variations) != 1
|
item.has_variations = (len(item.all_variations) != 1
|
||||||
or not item.all_variations[0].empty())
|
or not item.all_variations[0].empty())
|
||||||
item.num_total = tuplesum(var.num_total for var in item.all_variations)
|
item.num_total = tuplesum(var.num_total for var in item.all_variations)
|
||||||
@@ -100,7 +100,8 @@ def order_overview(event: Event) -> Tuple[List[Tuple[ItemCategory, List[Item]]],
|
|||||||
# insert categories into a set for uniqueness
|
# insert categories into a set for uniqueness
|
||||||
# a set is unsorted, so sort again by category
|
# a set is unsorted, so sort again by category
|
||||||
],
|
],
|
||||||
key=lambda group: (group[0].position, group[0].identity) if group[0] is not None else (0, "")
|
key=lambda group: (group[0].position, group[0].id) if (
|
||||||
|
group[0] is not None and group[0].id is not None) else (0, 0)
|
||||||
)
|
)
|
||||||
|
|
||||||
for c in items_by_category:
|
for c in items_by_category:
|
||||||
@@ -116,35 +117,35 @@ def order_overview(event: Event) -> Tuple[List[Tuple[ItemCategory, List[Item]]],
|
|||||||
payment_items = []
|
payment_items = []
|
||||||
num_total = {
|
num_total = {
|
||||||
o['payment_provider']: (o['cnt'], o['payment_fee'])
|
o['payment_provider']: (o['cnt'], o['payment_fee'])
|
||||||
for o in (Order.objects.current
|
for o in (Order.objects
|
||||||
.filter(event=event)
|
.filter(event=event)
|
||||||
.values('payment_provider')
|
.values('payment_provider')
|
||||||
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
|
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
|
||||||
}
|
}
|
||||||
num_cancelled = {
|
num_cancelled = {
|
||||||
o['payment_provider']: (o['cnt'], o['payment_fee'])
|
o['payment_provider']: (o['cnt'], o['payment_fee'])
|
||||||
for o in (Order.objects.current
|
for o in (Order.objects
|
||||||
.filter(event=event, status=Order.STATUS_CANCELLED)
|
.filter(event=event, status=Order.STATUS_CANCELLED)
|
||||||
.values('payment_provider')
|
.values('payment_provider')
|
||||||
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
|
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
|
||||||
}
|
}
|
||||||
num_refunded = {
|
num_refunded = {
|
||||||
o['payment_provider']: (o['cnt'], o['payment_fee'])
|
o['payment_provider']: (o['cnt'], o['payment_fee'])
|
||||||
for o in (Order.objects.current
|
for o in (Order.objects
|
||||||
.filter(event=event, status=Order.STATUS_REFUNDED)
|
.filter(event=event, status=Order.STATUS_REFUNDED)
|
||||||
.values('payment_provider')
|
.values('payment_provider')
|
||||||
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
|
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
|
||||||
}
|
}
|
||||||
num_pending = {
|
num_pending = {
|
||||||
o['payment_provider']: (o['cnt'], o['payment_fee'])
|
o['payment_provider']: (o['cnt'], o['payment_fee'])
|
||||||
for o in (Order.objects.current
|
for o in (Order.objects
|
||||||
.filter(event=event, status__in=(Order.STATUS_PENDING, Order.STATUS_EXPIRED))
|
.filter(event=event, status__in=(Order.STATUS_PENDING, Order.STATUS_EXPIRED))
|
||||||
.values('payment_provider')
|
.values('payment_provider')
|
||||||
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
|
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
|
||||||
}
|
}
|
||||||
num_paid = {
|
num_paid = {
|
||||||
o['payment_provider']: (o['cnt'], o['payment_fee'])
|
o['payment_provider']: (o['cnt'], o['payment_fee'])
|
||||||
for o in (Order.objects.current
|
for o in (Order.objects
|
||||||
.filter(event=event, status=Order.STATUS_PAID)
|
.filter(event=event, status=Order.STATUS_PAID)
|
||||||
.values('payment_provider')
|
.values('payment_provider')
|
||||||
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
|
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from pretix.base.signals import register_ticket_outputs
|
|||||||
|
|
||||||
|
|
||||||
def generate(order: str, provider: str):
|
def generate(order: str, provider: str):
|
||||||
order = Order.objects.current.select_related('event').get(identity=order)
|
order = Order.objects.select_related('event').get(id=order)
|
||||||
ct = CachedTicket.objects.get_or_create(order=order, provider=provider)[0]
|
ct = CachedTicket.objects.get_or_create(order=order, provider=provider)[0]
|
||||||
if not ct.cachedfile:
|
if not ct.cachedfile:
|
||||||
cf = CachedFile()
|
cf = CachedFile()
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from django.core.files import File
|
|||||||
from django.core.files.storage import default_storage
|
from django.core.files.storage import default_storage
|
||||||
from django.db.models import Model
|
from django.db.models import Model
|
||||||
from typing import Any, Callable, Dict, Optional, TypeVar, Union
|
from typing import Any, Callable, Dict, Optional, TypeVar, Union
|
||||||
from versions.models import Versionable
|
|
||||||
|
|
||||||
DEFAULTS = {
|
DEFAULTS = {
|
||||||
'max_items_per_order': {
|
'max_items_per_order': {
|
||||||
@@ -115,7 +114,7 @@ class SettingsProxy:
|
|||||||
def _cache(self) -> Dict[str, Any]:
|
def _cache(self) -> Dict[str, Any]:
|
||||||
if self._cached_obj is None:
|
if self._cached_obj is None:
|
||||||
self._cached_obj = {}
|
self._cached_obj = {}
|
||||||
for setting in self._obj.setting_objects.current.all():
|
for setting in self._obj.setting_objects.all():
|
||||||
self._cached_obj[setting.key] = setting
|
self._cached_obj[setting.key] = setting
|
||||||
return self._cached_obj
|
return self._cached_obj
|
||||||
|
|
||||||
@@ -146,8 +145,6 @@ class SettingsProxy:
|
|||||||
return dateutil.parser.parse(value).date()
|
return dateutil.parser.parse(value).date()
|
||||||
elif as_type == time:
|
elif as_type == time:
|
||||||
return dateutil.parser.parse(value).time()
|
return dateutil.parser.parse(value).time()
|
||||||
elif as_type is not None and issubclass(as_type, Versionable):
|
|
||||||
return as_type.objects.current.get(identity=value)
|
|
||||||
elif as_type is not None and issubclass(as_type, Model):
|
elif as_type is not None and issubclass(as_type, Model):
|
||||||
return as_type.objects.get(pk=value)
|
return as_type.objects.get(pk=value)
|
||||||
return value
|
return value
|
||||||
@@ -162,8 +159,6 @@ class SettingsProxy:
|
|||||||
return json.dumps(value)
|
return json.dumps(value)
|
||||||
elif isinstance(value, datetime) or isinstance(value, date) or isinstance(value, time):
|
elif isinstance(value, datetime) or isinstance(value, date) or isinstance(value, time):
|
||||||
return value.isoformat()
|
return value.isoformat()
|
||||||
elif isinstance(value, Versionable):
|
|
||||||
return value.identity
|
|
||||||
elif isinstance(value, Model):
|
elif isinstance(value, Model):
|
||||||
return value.pk
|
return value.pk
|
||||||
elif isinstance(value, File):
|
elif isinstance(value, File):
|
||||||
@@ -213,7 +208,6 @@ class SettingsProxy:
|
|||||||
def set(self, key: str, value: Any) -> None:
|
def set(self, key: str, value: Any) -> None:
|
||||||
if key in self._cache():
|
if key in self._cache():
|
||||||
s = self._cache()[key]
|
s = self._cache()[key]
|
||||||
s = s.clone()
|
|
||||||
else:
|
else:
|
||||||
s = self._type(object=self._obj, key=key)
|
s = self._type(object=self._obj, key=key)
|
||||||
s.value = self._serialize(value)
|
s.value = self._serialize(value)
|
||||||
@@ -237,7 +231,7 @@ class SettingsSandbox:
|
|||||||
prefixes for you.
|
prefixes for you.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, type: str, key: str, event: Versionable):
|
def __init__(self, type: str, key: str, event: Model):
|
||||||
self._event = event
|
self._event = event
|
||||||
self._type = type
|
self._type = type
|
||||||
self._key = key
|
self._key = key
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from django.utils.html import format_html
|
|||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from pretix.base.forms import VersionedModelForm
|
from pretix.base.forms import I18nModelForm
|
||||||
from pretix.base.models import Item, ItemVariation
|
from pretix.base.models import Item, ItemVariation
|
||||||
|
|
||||||
|
|
||||||
@@ -59,9 +59,9 @@ class I18nFormSet(BaseModelFormSet):
|
|||||||
return form
|
return form
|
||||||
|
|
||||||
|
|
||||||
class TolerantFormsetModelForm(VersionedModelForm):
|
class TolerantFormsetModelForm(I18nModelForm):
|
||||||
"""
|
"""
|
||||||
This is equivalent to a normal VersionedModelForm, but works around a problem that
|
This is equivalent to a normal I18nModelForm, but works around a problem that
|
||||||
arises when the form is used inside a FormSet with can_order=True and django-formset-js
|
arises when the form is used inside a FormSet with can_order=True and django-formset-js
|
||||||
enabled. In this configuration, even empty "extra" forms might have an ORDER value
|
enabled. In this configuration, even empty "extra" forms might have an ORDER value
|
||||||
sent and Django marks the form as empty and raises validation errors because the other
|
sent and Django marks the form as empty and raises validation errors because the other
|
||||||
@@ -105,15 +105,15 @@ def selector(values, prop):
|
|||||||
# properties they belong to EXCEPT the value for the property prop2.
|
# properties they belong to EXCEPT the value for the property prop2.
|
||||||
# We'll see later why we need this.
|
# We'll see later why we need this.
|
||||||
return [
|
return [
|
||||||
v.identity for v in sorted(values, key=lambda v: v.prop.identity)
|
v.id for v in sorted(values, key=lambda v: v.prop.id)
|
||||||
if v.prop.identity != prop.identity
|
if v.prop.id != prop.id
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def sort(v, prop):
|
def sort(v, prop):
|
||||||
# Given a list of variations, this will sort them by their position
|
# Given a list of variations, this will sort them by their position
|
||||||
# on the x-axis
|
# on the x-axis
|
||||||
return v[prop.identity].sortkey
|
return v[prop.id].sortkey
|
||||||
|
|
||||||
|
|
||||||
class VariationsFieldRenderer(forms.widgets.CheckboxFieldRenderer):
|
class VariationsFieldRenderer(forms.widgets.CheckboxFieldRenderer):
|
||||||
@@ -175,7 +175,7 @@ class VariationsFieldRenderer(forms.widgets.CheckboxFieldRenderer):
|
|||||||
final_attrs['checked'] = 'checked'
|
final_attrs['checked'] = 'checked'
|
||||||
w = self.choice_input_class(
|
w = self.choice_input_class(
|
||||||
self.name, self.value, self.attrs.copy(),
|
self.name, self.value, self.attrs.copy(),
|
||||||
(variation['key'], variation[properties[0].identity].value),
|
(variation['key'], variation[properties[0].id].value),
|
||||||
i
|
i
|
||||||
)
|
)
|
||||||
output.append(format_html('<li>{0}</li>', force_text(w)))
|
output.append(format_html('<li>{0}</li>', force_text(w)))
|
||||||
@@ -185,15 +185,15 @@ class VariationsFieldRenderer(forms.widgets.CheckboxFieldRenderer):
|
|||||||
def render_nd(self, output, variations, properties):
|
def render_nd(self, output, variations, properties):
|
||||||
# prop1 is the property on all the grid's y-axes
|
# prop1 is the property on all the grid's y-axes
|
||||||
prop1 = properties[0]
|
prop1 = properties[0]
|
||||||
prop1v = list(prop1.values.current.all())
|
prop1v = list(prop1.values.all())
|
||||||
# prop2 is the property on all the grid's x-axes
|
# prop2 is the property on all the grid's x-axes
|
||||||
prop2 = properties[1]
|
prop2 = properties[1]
|
||||||
prop2v = list(prop2.values.current.all())
|
prop2v = list(prop2.values.all())
|
||||||
|
|
||||||
# We now iterate over the cartesian product of all the other
|
# We now iterate over the cartesian product of all the other
|
||||||
# properties which are NOT on the axes of the grid because we
|
# properties which are NOT on the axes of the grid because we
|
||||||
# create one grid for any combination of them.
|
# create one grid for any combination of them.
|
||||||
for gridrow in product(*[prop.values.current.all() for prop in properties[2:]]):
|
for gridrow in product(*[prop.values.all() for prop in properties[2:]]):
|
||||||
if len(gridrow) > 0:
|
if len(gridrow) > 0:
|
||||||
output.append('<strong>')
|
output.append('<strong>')
|
||||||
output.append(", ".join([str(value.value) for value in gridrow]))
|
output.append(", ".join([str(value.value) for value in gridrow]))
|
||||||
@@ -281,7 +281,7 @@ class VariationsField(forms.ModelMultipleChoiceField):
|
|||||||
variations = self.item.get_all_variations(use_cache=True)
|
variations = self.item.get_all_variations(use_cache=True)
|
||||||
return (
|
return (
|
||||||
(
|
(
|
||||||
v['variation'].identity if 'variation' in v else v.key(),
|
v['variation'].id if 'variation' in v else v.key(),
|
||||||
v
|
v
|
||||||
) for v in variations
|
) for v in variations
|
||||||
)
|
)
|
||||||
@@ -312,10 +312,10 @@ class VariationsField(forms.ModelMultipleChoiceField):
|
|||||||
|
|
||||||
cleaned_value = self._clean_value(value)
|
cleaned_value = self._clean_value(value)
|
||||||
|
|
||||||
qs = self.item.variations.current.filter(identity__in=cleaned_value)
|
qs = self.item.variations.filter(id__in=cleaned_value)
|
||||||
|
|
||||||
# Re-check for consistency
|
# Re-check for consistency
|
||||||
pks = set(force_text(getattr(o, "identity")) for o in qs)
|
pks = set(force_text(getattr(o, "id")) for o in qs)
|
||||||
for val in cleaned_value:
|
for val in cleaned_value:
|
||||||
if force_text(val) not in pks:
|
if force_text(val) not in pks:
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
@@ -335,7 +335,7 @@ class VariationsField(forms.ModelMultipleChoiceField):
|
|||||||
# which uses a very similar method
|
# which uses a very similar method
|
||||||
all_variations = self.item.variations.all().prefetch_related("values")
|
all_variations = self.item.variations.all().prefetch_related("values")
|
||||||
variations_cache = {
|
variations_cache = {
|
||||||
var.to_variation_dict().identify(): var.identity for var in all_variations
|
var.to_variation_dict().identify(): var.id for var in all_variations
|
||||||
}
|
}
|
||||||
|
|
||||||
cleaned_value = []
|
cleaned_value = []
|
||||||
@@ -360,7 +360,7 @@ class VariationsField(forms.ModelMultipleChoiceField):
|
|||||||
|
|
||||||
# No ItemVariation present, create one!
|
# No ItemVariation present, create one!
|
||||||
var = ItemVariation()
|
var = ItemVariation()
|
||||||
var.item_id = self.item.identity
|
var.item_id = self.item.id
|
||||||
var.save()
|
var.save()
|
||||||
# Add the values to the ItemVariation object
|
# Add the values to the ItemVariation object
|
||||||
try:
|
try:
|
||||||
@@ -371,8 +371,8 @@ class VariationsField(forms.ModelMultipleChoiceField):
|
|||||||
code='invalid_pk_value',
|
code='invalid_pk_value',
|
||||||
params={'pk': value},
|
params={'pk': value},
|
||||||
)
|
)
|
||||||
variations_cache[key] = var.identity
|
variations_cache[key] = var.id
|
||||||
cleaned_value.append(str(var.identity))
|
cleaned_value.append(str(var.id))
|
||||||
else:
|
else:
|
||||||
# An ItemVariation id was given
|
# An ItemVariation id was given
|
||||||
cleaned_value.append(pk)
|
cleaned_value.append(pk)
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ from django.core.exceptions import ValidationError
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from pytz import common_timezones
|
from pytz import common_timezones
|
||||||
|
|
||||||
from pretix.base.forms import SettingsForm, VersionedModelForm
|
from pretix.base.forms import I18nModelForm, SettingsForm
|
||||||
from pretix.base.models import Event
|
from pretix.base.models import Event
|
||||||
|
|
||||||
|
|
||||||
class EventCreateForm(VersionedModelForm):
|
class EventCreateForm(I18nModelForm):
|
||||||
error_messages = {
|
error_messages = {
|
||||||
'duplicate_slug': _("You already used this slug for a different event. Please choose a new one."),
|
'duplicate_slug': _("You already used this slug for a different event. Please choose a new one."),
|
||||||
}
|
}
|
||||||
@@ -39,7 +39,7 @@ class EventCreateForm(VersionedModelForm):
|
|||||||
return slug
|
return slug
|
||||||
|
|
||||||
|
|
||||||
class EventUpdateForm(VersionedModelForm):
|
class EventUpdateForm(I18nModelForm):
|
||||||
def clean_slug(self):
|
def clean_slug(self):
|
||||||
return self.instance.slug
|
return self.instance.slug
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
import copy
|
import copy
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.db import models
|
|
||||||
from django.forms import BooleanField
|
from django.forms import BooleanField
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from pretix.base.forms import I18nModelForm, VersionedModelForm
|
from pretix.base.forms import I18nModelForm
|
||||||
from pretix.base.models import (
|
from pretix.base.models import (
|
||||||
Item, ItemCategory, ItemVariation, Property, PropertyValue, Question,
|
Item, ItemCategory, ItemVariation, Property, PropertyValue, Question,
|
||||||
Quota, Versionable,
|
Quota,
|
||||||
)
|
)
|
||||||
from pretix.control.forms import TolerantFormsetModelForm, VariationsField
|
from pretix.control.forms import TolerantFormsetModelForm, VariationsField
|
||||||
|
|
||||||
|
|
||||||
class CategoryForm(VersionedModelForm):
|
class CategoryForm(I18nModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ItemCategory
|
model = ItemCategory
|
||||||
localized_fields = '__all__'
|
localized_fields = '__all__'
|
||||||
@@ -22,7 +21,7 @@ class CategoryForm(VersionedModelForm):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PropertyForm(VersionedModelForm):
|
class PropertyForm(I18nModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Property
|
model = Property
|
||||||
localized_fields = '__all__'
|
localized_fields = '__all__'
|
||||||
@@ -40,10 +39,10 @@ class PropertyValueForm(TolerantFormsetModelForm):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class QuestionForm(VersionedModelForm):
|
class QuestionForm(I18nModelForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.fields['items'].queryset = self.instance.event.items.current.all()
|
self.fields['items'].queryset = self.instance.event.items.all()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Question
|
model = Question
|
||||||
@@ -60,10 +59,6 @@ class QuestionForm(VersionedModelForm):
|
|||||||
|
|
||||||
|
|
||||||
class QuotaForm(I18nModelForm):
|
class QuotaForm(I18nModelForm):
|
||||||
"""
|
|
||||||
The form for quotas does not derive from VersionedModelForm as it does not
|
|
||||||
perform a 'full clone' as part of a performance optimization
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
items = kwargs['items']
|
items = kwargs['items']
|
||||||
@@ -72,7 +67,7 @@ class QuotaForm(I18nModelForm):
|
|||||||
self.original_instance = copy.copy(instance) if instance else None
|
self.original_instance = copy.copy(instance) if instance else None
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
if hasattr(self, 'instance'):
|
if hasattr(self, 'instance') and self.instance.pk:
|
||||||
active_items = set(self.instance.items.all())
|
active_items = set(self.instance.items.all())
|
||||||
active_variations = set(self.instance.variations.all())
|
active_variations = set(self.instance.variations.all())
|
||||||
else:
|
else:
|
||||||
@@ -81,36 +76,19 @@ class QuotaForm(I18nModelForm):
|
|||||||
|
|
||||||
for item in items:
|
for item in items:
|
||||||
if len(item.properties.all()) > 0:
|
if len(item.properties.all()) > 0:
|
||||||
self.fields['item_%s' % item.identity] = VariationsField(
|
self.fields['item_%s' % item.id] = VariationsField(
|
||||||
item, label=_("Activate for"),
|
item, label=_("Activate for"),
|
||||||
required=False,
|
required=False,
|
||||||
initial=active_variations
|
initial=active_variations
|
||||||
)
|
)
|
||||||
self.fields['item_%s' % item.identity].set_item(item)
|
self.fields['item_%s' % item.id].set_item(item)
|
||||||
else:
|
else:
|
||||||
self.fields['item_%s' % item.identity] = BooleanField(
|
self.fields['item_%s' % item.id] = BooleanField(
|
||||||
label=_("Activate"),
|
label=_("Activate"),
|
||||||
required=False,
|
required=False,
|
||||||
initial=(item in active_items)
|
initial=(item in active_items)
|
||||||
)
|
)
|
||||||
|
|
||||||
def save(self, commit=True):
|
|
||||||
if self.instance.pk is not None and isinstance(self.instance, Versionable):
|
|
||||||
if self.has_changed() and self.original_instance:
|
|
||||||
new = self.instance
|
|
||||||
old = self.original_instance
|
|
||||||
clone = old.clone_shallow()
|
|
||||||
for f in type(self.instance)._meta.get_fields():
|
|
||||||
if f.name not in (
|
|
||||||
'id', 'identity', 'version_start_date', 'version_end_date',
|
|
||||||
'version_birth_date'
|
|
||||||
) and not isinstance(f, (
|
|
||||||
models.ManyToOneRel, models.ManyToManyRel, models.ManyToManyField
|
|
||||||
)):
|
|
||||||
setattr(clone, f.name, getattr(new, f.name))
|
|
||||||
self.instance = clone
|
|
||||||
return super().save(commit)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Quota
|
model = Quota
|
||||||
localized_fields = '__all__'
|
localized_fields = '__all__'
|
||||||
@@ -120,10 +98,10 @@ class QuotaForm(I18nModelForm):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class ItemFormGeneral(VersionedModelForm):
|
class ItemFormGeneral(I18nModelForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.fields['category'].queryset = self.instance.event.categories.current.all()
|
self.fields['category'].queryset = self.instance.event.categories.all()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Item
|
model = Item
|
||||||
@@ -142,7 +120,7 @@ class ItemFormGeneral(VersionedModelForm):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class ItemVariationForm(VersionedModelForm):
|
class ItemVariationForm(I18nModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ItemVariation
|
model = ItemVariation
|
||||||
localized_fields = '__all__'
|
localized_fields = '__all__'
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
from pretix.base.forms import VersionedModelForm
|
from pretix.base.forms import I18nModelForm
|
||||||
from pretix.base.models import Order
|
from pretix.base.models import Order
|
||||||
|
|
||||||
|
|
||||||
class ExtendForm(VersionedModelForm):
|
class ExtendForm(I18nModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Order
|
model = Order
|
||||||
fields = ['expires']
|
fields = ['expires']
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from pretix.base.forms import VersionedModelForm
|
from pretix.base.forms import I18nModelForm
|
||||||
from pretix.base.models import Organizer
|
from pretix.base.models import Organizer
|
||||||
|
|
||||||
|
|
||||||
class OrganizerForm(VersionedModelForm):
|
class OrganizerForm(I18nModelForm):
|
||||||
error_messages = {
|
error_messages = {
|
||||||
'duplicate_slug': _("This slug is already in use. Please choose a different one."),
|
'duplicate_slug': _("This slug is already in use. Please choose a different one."),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,16 +54,16 @@ class PermissionMiddleware:
|
|||||||
return redirect_to_login(
|
return redirect_to_login(
|
||||||
path, resolved_login_url, REDIRECT_FIELD_NAME)
|
path, resolved_login_url, REDIRECT_FIELD_NAME)
|
||||||
|
|
||||||
request.user.events_cache = request.user.events.current.order_by(
|
request.user.events_cache = request.user.events.order_by(
|
||||||
"organizer", "date_from").prefetch_related("organizer")
|
"organizer", "date_from").prefetch_related("organizer")
|
||||||
if 'event' in url.kwargs and 'organizer' in url.kwargs:
|
if 'event' in url.kwargs and 'organizer' in url.kwargs:
|
||||||
try:
|
try:
|
||||||
request.event = Event.objects.current.filter(
|
request.event = Event.objects.filter(
|
||||||
slug=url.kwargs['event'],
|
slug=url.kwargs['event'],
|
||||||
permitted__id__exact=request.user.id,
|
permitted__id__exact=request.user.id,
|
||||||
organizer__slug=url.kwargs['organizer'],
|
organizer__slug=url.kwargs['organizer'],
|
||||||
).select_related('organizer')[0]
|
).select_related('organizer')[0]
|
||||||
request.eventperm = EventPermission.objects.current.get(
|
request.eventperm = EventPermission.objects.get(
|
||||||
event=request.event,
|
event=request.event,
|
||||||
user=request.user
|
user=request.user
|
||||||
)
|
)
|
||||||
@@ -73,7 +73,7 @@ class PermissionMiddleware:
|
|||||||
"have no permission to administrate it."))
|
"have no permission to administrate it."))
|
||||||
elif 'organizer' in url.kwargs:
|
elif 'organizer' in url.kwargs:
|
||||||
try:
|
try:
|
||||||
request.organizer = Organizer.objects.current.filter(
|
request.organizer = Organizer.objects.filter(
|
||||||
slug=url.kwargs['organizer'],
|
slug=url.kwargs['organizer'],
|
||||||
permitted__id__exact=request.user.id,
|
permitted__id__exact=request.user.id,
|
||||||
)[0]
|
)[0]
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ def event_permission_required(permission):
|
|||||||
# just a double check, should not ever happen
|
# just a double check, should not ever happen
|
||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
try:
|
try:
|
||||||
perm = EventPermission.objects.current.get(
|
perm = EventPermission.objects.get(
|
||||||
event=request.event,
|
event=request.event,
|
||||||
user=request.user
|
user=request.user
|
||||||
)
|
)
|
||||||
@@ -59,7 +59,7 @@ def organizer_permission_required(permission):
|
|||||||
# just a double check, should not ever happen
|
# just a double check, should not ever happen
|
||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
try:
|
try:
|
||||||
perm = OrganizerPermission.objects.current.get(
|
perm = OrganizerPermission.objects.get(
|
||||||
organizer=request.organizer,
|
organizer=request.organizer,
|
||||||
user=request.user
|
user=request.user
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{{ object.name }} :: {% trans "Product" %}{% endblock %}
|
{% block title %}{{ object.name }} :: {% trans "Product" %}{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% if object.identity %}
|
{% if object.id %}
|
||||||
<h1>{% trans "Modify product:" %} {{ object.name }}</h1>
|
<h1>{% trans "Modify product:" %} {{ object.name }}</h1>
|
||||||
<ul class="nav nav-pills">
|
<ul class="nav nav-pills">
|
||||||
<li {% if "event.item" == url_name %}class="active"{% endif %}><a href="{% url 'control:event.item' organizer=request.event.organizer.slug event=request.event.slug item=object.identity %}">{% trans "General information" %}</a></li>
|
<li {% if "event.item" == url_name %}class="active"{% endif %}><a href="{% url 'control:event.item' organizer=request.event.organizer.slug event=request.event.slug item=object.id %}">{% trans "General information" %}</a></li>
|
||||||
<li {% if "event.item.properties" == url_name %}class="active"{% endif %}><a href="{% url 'control:event.item.properties' organizer=request.event.organizer.slug event=request.event.slug item=object.identity %}">{% trans "Properties" %}</a></li>
|
<li {% if "event.item.properties" == url_name %}class="active"{% endif %}><a href="{% url 'control:event.item.properties' organizer=request.event.organizer.slug event=request.event.slug item=object.id %}">{% trans "Properties" %}</a></li>
|
||||||
<li {% if "event.item.variations" == url_name %}class="active"{% endif %}><a href="{% url 'control:event.item.variations' organizer=request.event.organizer.slug event=request.event.slug item=object.identity %}">{% trans "Variations" %}</a></li>
|
<li {% if "event.item.variations" == url_name %}class="active"{% endif %}><a href="{% url 'control:event.item.variations' organizer=request.event.organizer.slug event=request.event.slug item=object.id %}">{% trans "Variations" %}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
{% else %}
|
{% else %}
|
||||||
<h1>{% trans "Create product" %}</h1>
|
<h1>{% trans "Create product" %}</h1>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
You will be able to adjust further settings in the next step.
|
You will be able to adjust further settings in the next step.
|
||||||
{% endblocktrans %}</p>
|
{% endblocktrans %}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if object.identity and not object.quotas.exists %}
|
{% if object.id and not object.quotas.exists %}
|
||||||
<div class="alert alert-warning">
|
<div class="alert alert-warning">
|
||||||
{% blocktrans trimmed %}
|
{% blocktrans trimmed %}
|
||||||
Please note, that your product will <strong>not</strong> be available for sale until you added your
|
Please note, that your product will <strong>not</strong> be available for sale until you added your
|
||||||
|
|||||||
@@ -18,12 +18,12 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for c in categories %}
|
{% for c in categories %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong><a href="{% url "control:event.items.categories.edit" organizer=request.event.organizer.slug event=request.event.slug category=c.identity %}">{{ c.name }}</a></strong></td>
|
<td><strong><a href="{% url "control:event.items.categories.edit" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}">{{ c.name }}</a></strong></td>
|
||||||
<td>
|
<td>
|
||||||
<a href="{% url "control:event.items.categories.up" organizer=request.event.organizer.slug event=request.event.slug category=c.identity %}" class="btn btn-default btn-sm {% if forloop.counter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-up"></i></a>
|
<a href="{% url "control:event.items.categories.up" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-default btn-sm {% if forloop.counter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-up"></i></a>
|
||||||
<a href="{% url "control:event.items.categories.down" organizer=request.event.organizer.slug event=request.event.slug category=c.identity %}" class="btn btn-default btn-sm {% if forloop.revcounter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-down"></i></a>
|
<a href="{% url "control:event.items.categories.down" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-default btn-sm {% if forloop.revcounter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-down"></i></a>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right"><a href="{% url "control:event.items.categories.delete" organizer=request.event.organizer.slug event=request.event.slug category=c.identity %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a></td>
|
<td class="text-right"><a href="{% url "control:event.items.categories.delete" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -32,15 +32,15 @@
|
|||||||
<td><strong>
|
<td><strong>
|
||||||
{% if not i.active %}<strike>{% endif %}
|
{% if not i.active %}<strike>{% endif %}
|
||||||
<a href="
|
<a href="
|
||||||
{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=i.identity %}">{{ i.name }}</a>
|
{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}">{{ i.name }}</a>
|
||||||
{% if not i.active %}</strike>{% endif %}
|
{% if not i.active %}</strike>{% endif %}
|
||||||
</strong></td>
|
</strong></td>
|
||||||
<td>{% if i.category %}{{ i.category.name }}{% endif %}</td>
|
<td>{% if i.category %}{{ i.category.name }}{% endif %}</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="{% url "control:event.items.up" organizer=request.event.organizer.slug event=request.event.slug item=i.identity %}" class="btn btn-default btn-sm {% if forloop.counter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-up"></i></a>
|
<a href="{% url "control:event.items.up" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-default btn-sm {% if forloop.counter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-up"></i></a>
|
||||||
<a href="{% url "control:event.items.down" organizer=request.event.organizer.slug event=request.event.slug item=i.identity %}" class="btn btn-default btn-sm {% if forloop.revcounter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-down"></i></a>
|
<a href="{% url "control:event.items.down" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-default btn-sm {% if forloop.revcounter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-down"></i></a>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right"><a href="{% url "control:event.items.delete" organizer=request.event.organizer.slug event=request.event.slug item=i.identity %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a></td>
|
<td class="text-right"><a href="{% url "control:event.items.delete" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -19,10 +19,10 @@
|
|||||||
{% for q in questions %}
|
{% for q in questions %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong><a href="
|
<td><strong><a href="
|
||||||
{% url "control:event.items.questions.edit" organizer=request.event.organizer.slug event=request.event.slug question=q.identity %}">{{ q.question }}</a></strong></td>
|
{% url "control:event.items.questions.edit" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}">{{ q.question }}</a></strong></td>
|
||||||
<td>{{ q.get_type_display }}</td>
|
<td>{{ q.get_type_display }}</td>
|
||||||
<td class="text-right"><a href="
|
<td class="text-right"><a href="
|
||||||
{% url "control:event.items.questions.delete" organizer=request.event.organizer.slug event=request.event.slug question=q.identity %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a></td>
|
{% url "control:event.items.questions.delete" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -25,12 +25,12 @@
|
|||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<h4 class="panel-title">
|
<h4 class="panel-title">
|
||||||
<a data-toggle="collapse" data-parent="#accordion"
|
<a data-toggle="collapse" data-parent="#accordion"
|
||||||
href="#collapse{{ item.identity }}">
|
href="#collapse{{ item.id }}">
|
||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
</a>
|
</a>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div id="collapse{{ item.identity }}" class="panel-collapse collapse in">
|
<div id="collapse{{ item.id }}" class="panel-collapse collapse in">
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="form-horizontal">
|
<div class="form-horizontal">
|
||||||
{% bootstrap_field item.field layout="horizontal" %}
|
{% bootstrap_field item.field layout="horizontal" %}
|
||||||
|
|||||||
@@ -20,19 +20,19 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for q in quotas %}
|
{% for q in quotas %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong><a href="{% url "control:event.items.quotas.edit" organizer=request.event.organizer.slug event=request.event.slug quota=q.identity %}">{{ q.name }}</a></strong></td>
|
<td><strong><a href="{% url "control:event.items.quotas.edit" organizer=request.event.organizer.slug event=request.event.slug quota=q.id %}">{{ q.name }}</a></strong></td>
|
||||||
<td>
|
<td>
|
||||||
<ul>
|
<ul>
|
||||||
{% for item in q.items.all %}
|
{% for item in q.items.all %}
|
||||||
<li><a href="
|
<li><a href="
|
||||||
{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=item.identity %}"
|
{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=item.id %}"
|
||||||
>{{ item.name }}</a></li>
|
>{{ item.name }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ q.size }}</td>
|
<td>{{ q.size }}</td>
|
||||||
<td>{% include "pretixcontrol/items/fragment_quota_availability.html" with availability=q.availability %}</td>
|
<td>{% include "pretixcontrol/items/fragment_quota_availability.html" with availability=q.availability %}</td>
|
||||||
<td class="text-right"><a href="{% url "control:event.items.quotas.delete" organizer=request.event.organizer.slug event=request.event.slug quota=q.identity %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a></td>
|
<td class="text-right"><a href="{% url "control:event.items.quotas.delete" organizer=request.event.organizer.slug event=request.event.slug quota=q.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -25,8 +25,8 @@
|
|||||||
<select name="item" class="form-control">
|
<select name="item" class="form-control">
|
||||||
<option value="">{% trans "All products" %}</option>
|
<option value="">{% trans "All products" %}</option>
|
||||||
{% for item in items %}
|
{% for item in items %}
|
||||||
<option value="{{ item.identity }}"
|
<option value="{{ item.id }}"
|
||||||
{% if request.GET.item == item.identity %}selected="selected"{% endif %}>
|
{% if request.GET.item == item.id %}selected="selected"{% endif %}>
|
||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
</option>
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -40,27 +40,27 @@
|
|||||||
<tr class="item {% if tup.0 %}categorized{% endif %}">
|
<tr class="item {% if tup.0 %}categorized{% endif %}">
|
||||||
<td>{{ item.name }}</td>
|
<td>{{ item.name }}</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ listurl }}?item={{ item.identity }}">
|
<a href="{{ listurl }}?item={{ item.id }}">
|
||||||
{{ item.num_total|togglesum }}
|
{{ item.num_total|togglesum }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ listurl }}?item={{ item.identity }}&status=n">
|
<a href="{{ listurl }}?item={{ item.id }}&status=n">
|
||||||
{{ item.num_pending|togglesum }}
|
{{ item.num_pending|togglesum }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ listurl }}?item={{ item.identity }}&status=c">
|
<a href="{{ listurl }}?item={{ item.id }}&status=c">
|
||||||
{{ item.num_cancelled|togglesum }}
|
{{ item.num_cancelled|togglesum }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ listurl }}?item={{ item.identity }}&status=r">
|
<a href="{{ listurl }}?item={{ item.id }}&status=r">
|
||||||
{{ item.num_refunded|togglesum }}
|
{{ item.num_refunded|togglesum }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ listurl }}?item={{ item.identity }}&status=p">
|
<a href="{{ listurl }}?item={{ item.id }}&status=p">
|
||||||
{{ item.num_paid|togglesum }}
|
{{ item.num_paid|togglesum }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
+14
-14
@@ -27,32 +27,32 @@ urlpatterns = [
|
|||||||
url(r'^settings/tickets$', event.TicketSettings.as_view(), name='event.settings.tickets'),
|
url(r'^settings/tickets$', event.TicketSettings.as_view(), name='event.settings.tickets'),
|
||||||
url(r'^items/$', item.ItemList.as_view(), name='event.items'),
|
url(r'^items/$', item.ItemList.as_view(), name='event.items'),
|
||||||
url(r'^items/add$', item.ItemCreate.as_view(), name='event.items.add'),
|
url(r'^items/add$', item.ItemCreate.as_view(), name='event.items.add'),
|
||||||
url(r'^items/(?P<item>[0-9a-f-]+)/$', item.ItemUpdateGeneral.as_view(), name='event.item'),
|
url(r'^items/(?P<item>\d+)/$', item.ItemUpdateGeneral.as_view(), name='event.item'),
|
||||||
url(r'^items/(?P<item>[0-9a-f-]+)/variations$', item.ItemVariations.as_view(),
|
url(r'^items/(?P<item>\d+)/variations$', item.ItemVariations.as_view(),
|
||||||
name='event.item.variations'),
|
name='event.item.variations'),
|
||||||
url(r'^items/(?P<item>[0-9a-f-]+)/properties$', item.ItemProperties.as_view(),
|
url(r'^items/(?P<item>\d+)/properties$', item.ItemProperties.as_view(),
|
||||||
name='event.item.properties'),
|
name='event.item.properties'),
|
||||||
url(r'^items/(?P<item>[0-9a-f-]+)/up$', item.item_move_up, name='event.items.up'),
|
url(r'^items/(?P<item>\d+)/up$', item.item_move_up, name='event.items.up'),
|
||||||
url(r'^items/(?P<item>[0-9a-f-]+)/down$', item.item_move_down, name='event.items.down'),
|
url(r'^items/(?P<item>\d+)/down$', item.item_move_down, name='event.items.down'),
|
||||||
url(r'^items/(?P<item>[0-9a-f-]+)/delete$', item.ItemDelete.as_view(), name='event.items.delete'),
|
url(r'^items/(?P<item>\d+)/delete$', item.ItemDelete.as_view(), name='event.items.delete'),
|
||||||
url(r'^categories/$', item.CategoryList.as_view(), name='event.items.categories'),
|
url(r'^categories/$', item.CategoryList.as_view(), name='event.items.categories'),
|
||||||
url(r'^categories/(?P<category>[0-9a-f-]+)/delete$', item.CategoryDelete.as_view(),
|
url(r'^categories/(?P<category>\d+)/delete$', item.CategoryDelete.as_view(),
|
||||||
name='event.items.categories.delete'),
|
name='event.items.categories.delete'),
|
||||||
url(r'^categories/(?P<category>[0-9a-f-]+)/up$', item.category_move_up, name='event.items.categories.up'),
|
url(r'^categories/(?P<category>\d+)/up$', item.category_move_up, name='event.items.categories.up'),
|
||||||
url(r'^categories/(?P<category>[0-9a-f-]+)/down$', item.category_move_down,
|
url(r'^categories/(?P<category>\d+)/down$', item.category_move_down,
|
||||||
name='event.items.categories.down'),
|
name='event.items.categories.down'),
|
||||||
url(r'^categories/(?P<category>[0-9a-f-]+)/$', item.CategoryUpdate.as_view(),
|
url(r'^categories/(?P<category>\d+)/$', item.CategoryUpdate.as_view(),
|
||||||
name='event.items.categories.edit'),
|
name='event.items.categories.edit'),
|
||||||
url(r'^categories/add$', item.CategoryCreate.as_view(), name='event.items.categories.add'),
|
url(r'^categories/add$', item.CategoryCreate.as_view(), name='event.items.categories.add'),
|
||||||
url(r'^questions/$', item.QuestionList.as_view(), name='event.items.questions'),
|
url(r'^questions/$', item.QuestionList.as_view(), name='event.items.questions'),
|
||||||
url(r'^questions/(?P<question>[0-9a-f-]+)/delete$', item.QuestionDelete.as_view(),
|
url(r'^questions/(?P<question>\d+)/delete$', item.QuestionDelete.as_view(),
|
||||||
name='event.items.questions.delete'),
|
name='event.items.questions.delete'),
|
||||||
url(r'^questions/(?P<question>[0-9a-f-]+)/$', item.QuestionUpdate.as_view(),
|
url(r'^questions/(?P<question>\d+)/$', item.QuestionUpdate.as_view(),
|
||||||
name='event.items.questions.edit'),
|
name='event.items.questions.edit'),
|
||||||
url(r'^questions/add$', item.QuestionCreate.as_view(), name='event.items.questions.add'),
|
url(r'^questions/add$', item.QuestionCreate.as_view(), name='event.items.questions.add'),
|
||||||
url(r'^quotas/$', item.QuotaList.as_view(), name='event.items.quotas'),
|
url(r'^quotas/$', item.QuotaList.as_view(), name='event.items.quotas'),
|
||||||
url(r'^quotas/(?P<quota>[0-9a-f-]+)/$', item.QuotaUpdate.as_view(), name='event.items.quotas.edit'),
|
url(r'^quotas/(?P<quota>\d+)/$', item.QuotaUpdate.as_view(), name='event.items.quotas.edit'),
|
||||||
url(r'^quotas/(?P<quota>[0-9a-f-]+)/delete$', item.QuotaDelete.as_view(),
|
url(r'^quotas/(?P<quota>\d+)/delete$', item.QuotaDelete.as_view(),
|
||||||
name='event.items.quotas.delete'),
|
name='event.items.quotas.delete'),
|
||||||
url(r'^quotas/add$', item.QuotaCreate.as_view(), name='event.items.quotas.add'),
|
url(r'^quotas/add$', item.QuotaCreate.as_view(), name='event.items.quotas.add'),
|
||||||
url(r'^orders/(?P<code>[0-9A-Z]+)/transition$', orders.OrderTransition.as_view(),
|
url(r'^orders/(?P<code>[0-9A-Z]+)/transition$', orders.OrderTransition.as_view(),
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from django.views.generic import FormView
|
|||||||
from django.views.generic.base import TemplateView
|
from django.views.generic.base import TemplateView
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
|
|
||||||
from pretix.base.forms import VersionedModelForm
|
from pretix.base.forms import I18nModelForm
|
||||||
from pretix.base.models import (
|
from pretix.base.models import (
|
||||||
Event, EventPermission, Item, Order, OrderPosition, User,
|
Event, EventPermission, Item, Order, OrderPosition, User,
|
||||||
)
|
)
|
||||||
@@ -248,19 +248,19 @@ class TicketSettings(EventPermissionRequiredMixin, FormView):
|
|||||||
|
|
||||||
def index(request, organizer, event):
|
def index(request, organizer, event):
|
||||||
ctx = {
|
ctx = {
|
||||||
'products_active': Item.objects.current.filter(
|
'products_active': Item.objects.filter(
|
||||||
event=request.event,
|
event=request.event,
|
||||||
active=True,
|
active=True,
|
||||||
).count(),
|
).count(),
|
||||||
'tickets_total': OrderPosition.objects.current.filter(
|
'tickets_total': OrderPosition.objects.filter(
|
||||||
order__event=request.event,
|
order__event=request.event,
|
||||||
item__admission=True
|
item__admission=True
|
||||||
).count(),
|
).count(),
|
||||||
'tickets_revenue': Order.objects.current.filter(
|
'tickets_revenue': Order.objects.filter(
|
||||||
event=request.event,
|
event=request.event,
|
||||||
status=Order.STATUS_PAID,
|
status=Order.STATUS_PAID,
|
||||||
).aggregate(sum=Sum('total'))['sum'],
|
).aggregate(sum=Sum('total'))['sum'],
|
||||||
'tickets_sold': OrderPosition.objects.current.filter(
|
'tickets_sold': OrderPosition.objects.filter(
|
||||||
order__event=request.event,
|
order__event=request.event,
|
||||||
order__status=Order.STATUS_PAID,
|
order__status=Order.STATUS_PAID,
|
||||||
item__admission=True
|
item__admission=True
|
||||||
@@ -269,7 +269,7 @@ def index(request, organizer, event):
|
|||||||
return render(request, 'pretixcontrol/event/index.html', ctx)
|
return render(request, 'pretixcontrol/event/index.html', ctx)
|
||||||
|
|
||||||
|
|
||||||
class EventPermissionForm(VersionedModelForm):
|
class EventPermissionForm(I18nModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = EventPermission
|
model = EventPermission
|
||||||
fields = (
|
fields = (
|
||||||
@@ -297,7 +297,7 @@ class EventPermissions(EventPermissionRequiredMixin, TemplateView):
|
|||||||
)
|
)
|
||||||
return fs(data=self.request.POST if self.request.method == "POST" else None,
|
return fs(data=self.request.POST if self.request.method == "POST" else None,
|
||||||
prefix="formset",
|
prefix="formset",
|
||||||
queryset=EventPermission.objects.current.filter(event=self.request.event))
|
queryset=EventPermission.objects.filter(event=self.request.event))
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def add_form(self):
|
def add_form(self):
|
||||||
@@ -317,13 +317,13 @@ class EventPermissions(EventPermissionRequiredMixin, TemplateView):
|
|||||||
self.add_form.instance.user = User.objects.get(email=self.add_form.cleaned_data['user'])
|
self.add_form.instance.user = User.objects.get(email=self.add_form.cleaned_data['user'])
|
||||||
self.add_form.instance.user_id = self.add_form.instance.user.id
|
self.add_form.instance.user_id = self.add_form.instance.user.id
|
||||||
self.add_form.instance.event = self.request.event
|
self.add_form.instance.event = self.request.event
|
||||||
self.add_form.instance.event_id = self.request.event.identity
|
self.add_form.instance.event_id = self.request.event.id
|
||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
messages.error(self.request, _('There is no user with the email address you entered.'))
|
messages.error(self.request, _('There is no user with the email address you entered.'))
|
||||||
return self.get(*args, **kwargs)
|
return self.get(*args, **kwargs)
|
||||||
else:
|
else:
|
||||||
if EventPermission.objects.current.filter(user=self.add_form.instance.user,
|
if EventPermission.objects.filter(user=self.add_form.instance.user,
|
||||||
event=self.request.event).exists():
|
event=self.request.event).exists():
|
||||||
messages.error(self.request, _('This user already has permissions for this event.'))
|
messages.error(self.request, _('This user already has permissions for this event.'))
|
||||||
return self.get(*args, **kwargs)
|
return self.get(*args, **kwargs)
|
||||||
self.add_form.save()
|
self.add_form.save()
|
||||||
|
|||||||
@@ -18,8 +18,7 @@ from pretix.base.models import (
|
|||||||
Quota,
|
Quota,
|
||||||
)
|
)
|
||||||
from pretix.control.forms import (
|
from pretix.control.forms import (
|
||||||
I18nInlineFormSet, NestedInnerI18nInlineFormSet, VariationsField,
|
NestedInnerI18nInlineFormSet, VariationsField, nestedformset_factory,
|
||||||
nestedformset_factory,
|
|
||||||
)
|
)
|
||||||
from pretix.control.forms.item import (
|
from pretix.control.forms.item import (
|
||||||
CategoryForm, ItemFormGeneral, ItemVariationForm, PropertyForm,
|
CategoryForm, ItemFormGeneral, ItemVariationForm, PropertyForm,
|
||||||
@@ -28,7 +27,6 @@ from pretix.control.forms.item import (
|
|||||||
from pretix.control.permissions import (
|
from pretix.control.permissions import (
|
||||||
EventPermissionRequiredMixin, event_permission_required,
|
EventPermissionRequiredMixin, event_permission_required,
|
||||||
)
|
)
|
||||||
from pretix.control.signals import restriction_formset
|
|
||||||
|
|
||||||
from . import CreateView, UpdateView
|
from . import CreateView, UpdateView
|
||||||
|
|
||||||
@@ -42,7 +40,7 @@ class ItemList(ListView):
|
|||||||
template_name = 'pretixcontrol/items/index.html'
|
template_name = 'pretixcontrol/items/index.html'
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return Item.objects.current.filter(
|
return Item.objects.filter(
|
||||||
event=self.request.event
|
event=self.request.event
|
||||||
).prefetch_related("category")
|
).prefetch_related("category")
|
||||||
|
|
||||||
@@ -54,12 +52,12 @@ def item_move(request, item, up=True):
|
|||||||
all items for this category in a new order.
|
all items for this category in a new order.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
item = request.event.items.current.get(
|
item = request.event.items.get(
|
||||||
identity=item
|
id=item
|
||||||
)
|
)
|
||||||
except Item.DoesNotExist:
|
except Item.DoesNotExist:
|
||||||
raise Http404(_("The requested product does not exist."))
|
raise Http404(_("The requested product does not exist."))
|
||||||
items = list(request.event.items.current.filter(category=item.category).order_by("position"))
|
items = list(request.event.items.filter(category=item.category).order_by("position"))
|
||||||
|
|
||||||
index = items.index(item)
|
index = items.index(item)
|
||||||
if index != 0 and up:
|
if index != 0 and up:
|
||||||
@@ -70,7 +68,7 @@ def item_move(request, item, up=True):
|
|||||||
for i, item in enumerate(items):
|
for i, item in enumerate(items):
|
||||||
if item.position != i:
|
if item.position != i:
|
||||||
item.position = i
|
item.position = i
|
||||||
item.save() # TODO: Clone or document sloppiness?
|
item.save()
|
||||||
messages.success(request, _('The order of items as been updated.'))
|
messages.success(request, _('The order of items as been updated.'))
|
||||||
|
|
||||||
|
|
||||||
@@ -99,16 +97,15 @@ class CategoryDelete(EventPermissionRequiredMixin, DeleteView):
|
|||||||
|
|
||||||
def get_object(self, queryset=None) -> ItemCategory:
|
def get_object(self, queryset=None) -> ItemCategory:
|
||||||
try:
|
try:
|
||||||
return self.request.event.categories.current.get(
|
return self.request.event.categories.get(
|
||||||
identity=self.kwargs['category']
|
id=self.kwargs['category']
|
||||||
)
|
)
|
||||||
except ItemCategory.DoesNotExist:
|
except ItemCategory.DoesNotExist:
|
||||||
raise Http404(_("The requested product category does not exist."))
|
raise Http404(_("The requested product category does not exist."))
|
||||||
|
|
||||||
def delete(self, request, *args, **kwargs):
|
def delete(self, request, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
for item in self.object.items.current.all():
|
for item in self.object.items.all():
|
||||||
# TODO: Clone!?
|
|
||||||
item.category = None
|
item.category = None
|
||||||
item.save()
|
item.save()
|
||||||
success_url = self.get_success_url()
|
success_url = self.get_success_url()
|
||||||
@@ -133,8 +130,8 @@ class CategoryUpdate(EventPermissionRequiredMixin, UpdateView):
|
|||||||
def get_object(self, queryset=None) -> ItemCategory:
|
def get_object(self, queryset=None) -> ItemCategory:
|
||||||
url = resolve(self.request.path_info)
|
url = resolve(self.request.path_info)
|
||||||
try:
|
try:
|
||||||
return self.request.event.categories.current.get(
|
return self.request.event.categories.get(
|
||||||
identity=url.kwargs['category']
|
id=url.kwargs['category']
|
||||||
)
|
)
|
||||||
except ItemCategory.DoesNotExist:
|
except ItemCategory.DoesNotExist:
|
||||||
raise Http404(_("The requested product category does not exist."))
|
raise Http404(_("The requested product category does not exist."))
|
||||||
@@ -176,7 +173,7 @@ class CategoryList(ListView):
|
|||||||
template_name = 'pretixcontrol/items/categories.html'
|
template_name = 'pretixcontrol/items/categories.html'
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return self.request.event.categories.current.all()
|
return self.request.event.categories.all()
|
||||||
|
|
||||||
|
|
||||||
def category_move(request, category, up=True):
|
def category_move(request, category, up=True):
|
||||||
@@ -186,12 +183,12 @@ def category_move(request, category, up=True):
|
|||||||
all categories for this event in a new order.
|
all categories for this event in a new order.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
category = request.event.categories.current.get(
|
category = request.event.categories.get(
|
||||||
identity=category
|
id=category
|
||||||
)
|
)
|
||||||
except ItemCategory.DoesNotExist:
|
except ItemCategory.DoesNotExist:
|
||||||
raise Http404(_("The requested product category does not exist."))
|
raise Http404(_("The requested product category does not exist."))
|
||||||
categories = list(request.event.categories.current.order_by("position"))
|
categories = list(request.event.categories.order_by("position"))
|
||||||
|
|
||||||
index = categories.index(category)
|
index = categories.index(category)
|
||||||
if index != 0 and up:
|
if index != 0 and up:
|
||||||
@@ -202,7 +199,7 @@ def category_move(request, category, up=True):
|
|||||||
for i, cat in enumerate(categories):
|
for i, cat in enumerate(categories):
|
||||||
if cat.position != i:
|
if cat.position != i:
|
||||||
cat.position = i
|
cat.position = i
|
||||||
cat.save() # TODO: Clone or document sloppiness?
|
cat.save()
|
||||||
messages.success(request, _('The order of categories as been updated.'))
|
messages.success(request, _('The order of categories as been updated.'))
|
||||||
|
|
||||||
|
|
||||||
@@ -229,7 +226,7 @@ class QuestionList(ListView):
|
|||||||
template_name = 'pretixcontrol/items/questions.html'
|
template_name = 'pretixcontrol/items/questions.html'
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return self.request.event.questions.current.all()
|
return self.request.event.questions.all()
|
||||||
|
|
||||||
|
|
||||||
class QuestionDelete(EventPermissionRequiredMixin, DeleteView):
|
class QuestionDelete(EventPermissionRequiredMixin, DeleteView):
|
||||||
@@ -240,15 +237,15 @@ class QuestionDelete(EventPermissionRequiredMixin, DeleteView):
|
|||||||
|
|
||||||
def get_object(self, queryset=None) -> Question:
|
def get_object(self, queryset=None) -> Question:
|
||||||
try:
|
try:
|
||||||
return self.request.event.questions.current.get(
|
return self.request.event.questions.get(
|
||||||
identity=self.kwargs['question']
|
id=self.kwargs['question']
|
||||||
)
|
)
|
||||||
except Question.DoesNotExist:
|
except Question.DoesNotExist:
|
||||||
raise Http404(_("The requested question does not exist."))
|
raise Http404(_("The requested question does not exist."))
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs) -> dict:
|
def get_context_data(self, *args, **kwargs) -> dict:
|
||||||
context = super().get_context_data(*args, **kwargs)
|
context = super().get_context_data(*args, **kwargs)
|
||||||
context['dependent'] = list(self.get_object().items.current.all())
|
context['dependent'] = list(self.get_object().items.all())
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def delete(self, request, *args, **kwargs):
|
def delete(self, request, *args, **kwargs):
|
||||||
@@ -274,8 +271,8 @@ class QuestionUpdate(EventPermissionRequiredMixin, UpdateView):
|
|||||||
|
|
||||||
def get_object(self, queryset=None) -> Question:
|
def get_object(self, queryset=None) -> Question:
|
||||||
try:
|
try:
|
||||||
return self.request.event.questions.current.get(
|
return self.request.event.questions.get(
|
||||||
identity=self.kwargs['question']
|
id=self.kwargs['question']
|
||||||
)
|
)
|
||||||
except Question.DoesNotExist:
|
except Question.DoesNotExist:
|
||||||
raise Http404(_("The requested question does not exist."))
|
raise Http404(_("The requested question does not exist."))
|
||||||
@@ -321,7 +318,7 @@ class QuotaList(ListView):
|
|||||||
template_name = 'pretixcontrol/items/quotas.html'
|
template_name = 'pretixcontrol/items/quotas.html'
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return Quota.objects.current.filter(
|
return Quota.objects.filter(
|
||||||
event=self.request.event
|
event=self.request.event
|
||||||
).prefetch_related("items")
|
).prefetch_related("items")
|
||||||
|
|
||||||
@@ -342,32 +339,29 @@ class QuotaEditorMixin:
|
|||||||
context = super().get_context_data(*args, **kwargs)
|
context = super().get_context_data(*args, **kwargs)
|
||||||
context['items'] = self.items
|
context['items'] = self.items
|
||||||
for item in context['items']:
|
for item in context['items']:
|
||||||
item.field = self.get_form(QuotaForm)['item_%s' % item.identity]
|
item.field = self.get_form(QuotaForm)['item_%s' % item.id]
|
||||||
return context
|
return context
|
||||||
|
|
||||||
@transaction.atomic()
|
@transaction.atomic()
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
res = super().form_valid(form)
|
res = super().form_valid(form)
|
||||||
# The following commented-out checks are not necessary as both self.object.items
|
items = self.object.items.all()
|
||||||
# and self.object.variations can be expected empty due to the performance
|
variations = self.object.variations.all()
|
||||||
# optimization of pretixbase.models.Versionable.clone_shallow()
|
|
||||||
# items = self.object.items.all()
|
|
||||||
# variations = self.object.variations.all()
|
|
||||||
selected_variations = []
|
selected_variations = []
|
||||||
self.object = form.instance
|
self.object = form.instance
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
field = form.fields['item_%s' % item.identity]
|
field = form.fields['item_%s' % item.id]
|
||||||
data = form.cleaned_data['item_%s' % item.identity]
|
data = form.cleaned_data['item_%s' % item.id]
|
||||||
if isinstance(field, VariationsField):
|
if isinstance(field, VariationsField):
|
||||||
for v in data:
|
for v in data:
|
||||||
selected_variations.append(v)
|
selected_variations.append(v)
|
||||||
if data: # and item not in items:
|
if data and item not in items:
|
||||||
self.object.items.add(item)
|
self.object.items.add(item)
|
||||||
# elif not data and item in items:
|
elif not data and item in items:
|
||||||
# self.object.items.remove(item)
|
self.object.items.remove(item)
|
||||||
|
|
||||||
self.object.variations.add(*[v for v in selected_variations]) # if v not in variations])
|
self.object.variations.add(*[v for v in selected_variations if v not in variations])
|
||||||
# self.object.variations.remove(*[v for v in variations if v not in selected_variations])
|
self.object.variations.remove(*[v for v in variations if v not in selected_variations])
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
@@ -399,8 +393,8 @@ class QuotaUpdate(EventPermissionRequiredMixin, QuotaEditorMixin, UpdateView):
|
|||||||
|
|
||||||
def get_object(self, queryset=None) -> Quota:
|
def get_object(self, queryset=None) -> Quota:
|
||||||
try:
|
try:
|
||||||
return self.request.event.quotas.current.get(
|
return self.request.event.quotas.get(
|
||||||
identity=self.kwargs['quota']
|
id=self.kwargs['quota']
|
||||||
)
|
)
|
||||||
except Quota.DoesNotExist:
|
except Quota.DoesNotExist:
|
||||||
raise Http404(_("The requested quota does not exist."))
|
raise Http404(_("The requested quota does not exist."))
|
||||||
@@ -424,15 +418,15 @@ class QuotaDelete(EventPermissionRequiredMixin, DeleteView):
|
|||||||
|
|
||||||
def get_object(self, queryset=None) -> Quota:
|
def get_object(self, queryset=None) -> Quota:
|
||||||
try:
|
try:
|
||||||
return self.request.event.quotas.current.get(
|
return self.request.event.quotas.get(
|
||||||
identity=self.kwargs['quota']
|
id=self.kwargs['quota']
|
||||||
)
|
)
|
||||||
except Quota.DoesNotExist:
|
except Quota.DoesNotExist:
|
||||||
raise Http404(_("The requested quota does not exist."))
|
raise Http404(_("The requested quota does not exist."))
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs) -> dict:
|
def get_context_data(self, *args, **kwargs) -> dict:
|
||||||
context = super().get_context_data(*args, **kwargs)
|
context = super().get_context_data(*args, **kwargs)
|
||||||
context['dependent'] = list(self.get_object().items.current.all())
|
context['dependent'] = list(self.get_object().items.all())
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def delete(self, request, *args, **kwargs):
|
def delete(self, request, *args, **kwargs):
|
||||||
@@ -456,8 +450,8 @@ class ItemDetailMixin(SingleObjectMixin):
|
|||||||
def get_object(self, queryset=None) -> Item:
|
def get_object(self, queryset=None) -> Item:
|
||||||
try:
|
try:
|
||||||
if not hasattr(self, 'object') or not self.object:
|
if not hasattr(self, 'object') or not self.object:
|
||||||
self.item = self.request.event.items.current.get(
|
self.item = self.request.event.items.get(
|
||||||
identity=self.kwargs['item']
|
id=self.kwargs['item']
|
||||||
)
|
)
|
||||||
self.object = self.item
|
self.object = self.item
|
||||||
return self.object
|
return self.object
|
||||||
@@ -474,7 +468,7 @@ class ItemCreate(EventPermissionRequiredMixin, CreateView):
|
|||||||
return reverse('control:event.item', kwargs={
|
return reverse('control:event.item', kwargs={
|
||||||
'organizer': self.request.event.organizer.slug,
|
'organizer': self.request.event.organizer.slug,
|
||||||
'event': self.request.event.slug,
|
'event': self.request.event.slug,
|
||||||
'item': self.object.identity,
|
'item': self.object.id,
|
||||||
})
|
})
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
@@ -500,7 +494,7 @@ class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, UpdateVie
|
|||||||
return reverse('control:event.item', kwargs={
|
return reverse('control:event.item', kwargs={
|
||||||
'organizer': self.request.event.organizer.slug,
|
'organizer': self.request.event.organizer.slug,
|
||||||
'event': self.request.event.slug,
|
'event': self.request.event.slug,
|
||||||
'item': self.get_object().identity,
|
'item': self.get_object().id,
|
||||||
})
|
})
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
@@ -516,7 +510,7 @@ class ItemProperties(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView
|
|||||||
return reverse('control:event.item.properties', kwargs={
|
return reverse('control:event.item.properties', kwargs={
|
||||||
'organizer': self.request.event.organizer.slug,
|
'organizer': self.request.event.organizer.slug,
|
||||||
'event': self.request.event.slug,
|
'event': self.request.event.slug,
|
||||||
'item': self.get_object().identity,
|
'item': self.get_object().id,
|
||||||
})
|
})
|
||||||
|
|
||||||
def get_inner_formset_class(self):
|
def get_inner_formset_class(self):
|
||||||
@@ -534,7 +528,7 @@ class ItemProperties(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView
|
|||||||
form=PropertyForm, can_order=False, can_delete=True, extra=0
|
form=PropertyForm, can_order=False, can_delete=True, extra=0
|
||||||
)
|
)
|
||||||
formset = formsetclass(self.request.POST if self.request.method == "POST" else None,
|
formset = formsetclass(self.request.POST if self.request.method == "POST" else None,
|
||||||
queryset=Property.objects.current.filter(item=self.object).prefetch_related('values'),
|
queryset=Property.objects.filter(item=self.object).prefetch_related('values'),
|
||||||
event=self.request.event)
|
event=self.request.event)
|
||||||
return formset
|
return formset
|
||||||
|
|
||||||
@@ -595,16 +589,16 @@ class ItemVariations(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView
|
|||||||
form = ItemVariationForm(
|
form = ItemVariationForm(
|
||||||
data,
|
data,
|
||||||
instance=variation['variation'],
|
instance=variation['variation'],
|
||||||
prefix=",".join([str(i.identity) for i in values]),
|
prefix=",".join([str(i.id) for i in values]),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
inst = ItemVariation(item=self.object)
|
inst = ItemVariation(item=self.object)
|
||||||
inst.item_id = self.object.identity
|
inst.item_id = self.object.id
|
||||||
inst.creation = True
|
inst.creation = True
|
||||||
form = ItemVariationForm(
|
form = ItemVariationForm(
|
||||||
data,
|
data,
|
||||||
instance=inst,
|
instance=inst,
|
||||||
prefix=",".join([str(i.identity) for i in values]),
|
prefix=",".join([str(i.id) for i in values]),
|
||||||
)
|
)
|
||||||
form.values = values
|
form.values = values
|
||||||
return form
|
return form
|
||||||
@@ -649,21 +643,21 @@ class ItemVariations(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView
|
|||||||
# properties they belong to EXCEPT the value for the property prop2.
|
# properties they belong to EXCEPT the value for the property prop2.
|
||||||
# We'll see later why we need this.
|
# We'll see later why we need this.
|
||||||
return [
|
return [
|
||||||
v.identity for v in sorted(values, key=lambda v: v.prop.identity)
|
v.id for v in sorted(values, key=lambda v: v.prop.id)
|
||||||
if v.prop.identity != prop2.identity
|
if v.prop.id != prop2.id
|
||||||
]
|
]
|
||||||
|
|
||||||
def sort(v):
|
def sort(v):
|
||||||
# Given a list of variations, this will sort them by their position
|
# Given a list of variations, this will sort them by their position
|
||||||
# on the x-axis
|
# on the x-axis
|
||||||
return v[prop2.identity].sortkey
|
return v[prop2.id].sortkey
|
||||||
|
|
||||||
# We now iterate over the cartesian product of all the other
|
# We now iterate over the cartesian product of all the other
|
||||||
# properties which are NOT on the axes of the grid because we
|
# properties which are NOT on the axes of the grid because we
|
||||||
# create one grid for any combination of them.
|
# create one grid for any combination of them.
|
||||||
for gridrow in product(*[prop.values.current.all() for prop in self.properties[2:]]):
|
for gridrow in product(*[prop.values.all() for prop in self.properties[2:]]):
|
||||||
grids = []
|
grids = []
|
||||||
for val1 in prop1.values.current.all():
|
for val1 in prop1.values.all():
|
||||||
formrow = []
|
formrow = []
|
||||||
# We are now inside one of the rows of the grid and have to
|
# We are now inside one of the rows of the grid and have to
|
||||||
# select the variations to display in this row. In order to
|
# select the variations to display in this row. In order to
|
||||||
@@ -689,7 +683,7 @@ class ItemVariations(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView
|
|||||||
|
|
||||||
def main(self, request, *args, **kwargs):
|
def main(self, request, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
self.properties = list(self.object.properties.current.all().prefetch_related("values"))
|
self.properties = list(self.object.properties.all().prefetch_related("values"))
|
||||||
self.dimension = len(self.properties)
|
self.dimension = len(self.properties)
|
||||||
self.forms, self.forms_flat = self.get_forms()
|
self.forms, self.forms_flat = self.get_forms()
|
||||||
|
|
||||||
@@ -730,7 +724,7 @@ class ItemVariations(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView
|
|||||||
return reverse('control:event.item.variations', kwargs={
|
return reverse('control:event.item.variations', kwargs={
|
||||||
'organizer': self.request.event.organizer.slug,
|
'organizer': self.request.event.organizer.slug,
|
||||||
'event': self.request.event.slug,
|
'event': self.request.event.slug,
|
||||||
'item': self.get_object().identity,
|
'item': self.get_object().id,
|
||||||
})
|
})
|
||||||
|
|
||||||
def get_context_data(self, **kwargs) -> dict:
|
def get_context_data(self, **kwargs) -> dict:
|
||||||
@@ -757,8 +751,8 @@ class ItemDelete(EventPermissionRequiredMixin, DeleteView):
|
|||||||
def get_object(self, queryset=None) -> Property:
|
def get_object(self, queryset=None) -> Property:
|
||||||
if not hasattr(self, 'object') or not self.object:
|
if not hasattr(self, 'object') or not self.object:
|
||||||
try:
|
try:
|
||||||
self.object = self.request.event.items.current.get(
|
self.object = self.request.event.items.get(
|
||||||
identity=self.kwargs['item']
|
id=self.kwargs['item']
|
||||||
)
|
)
|
||||||
except Property.DoesNotExist:
|
except Property.DoesNotExist:
|
||||||
raise Http404(_("The requested product does not exist."))
|
raise Http404(_("The requested product does not exist."))
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class EventList(ListView):
|
|||||||
template_name = 'pretixcontrol/events/index.html'
|
template_name = 'pretixcontrol/events/index.html'
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return Event.objects.current.filter(
|
return Event.objects.filter(
|
||||||
permitted__id__exact=self.request.user.pk
|
permitted__id__exact=self.request.user.pk
|
||||||
).prefetch_related(
|
).prefetch_related(
|
||||||
"organizer",
|
"organizer",
|
||||||
@@ -34,7 +34,7 @@ class EventCreateStart(TemplateView):
|
|||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
ctx = super().get_context_data(**kwargs)
|
ctx = super().get_context_data(**kwargs)
|
||||||
ctx['organizers'] = [
|
ctx['organizers'] = [
|
||||||
p.organizer for p in OrganizerPermission.objects.current.filter(
|
p.organizer for p in OrganizerPermission.objects.filter(
|
||||||
user=self.request.user, can_create_events=True
|
user=self.request.user, can_create_events=True
|
||||||
).select_related("organizer")
|
).select_related("organizer")
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class OrderList(EventPermissionRequiredMixin, ListView):
|
|||||||
permission = 'can_view_orders'
|
permission = 'can_view_orders'
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
qs = Order.objects.current.filter(
|
qs = Order.objects.filter(
|
||||||
event=self.request.event
|
event=self.request.event
|
||||||
)
|
)
|
||||||
if self.request.GET.get("user", "") != "":
|
if self.request.GET.get("user", "") != "":
|
||||||
@@ -53,7 +53,7 @@ class OrderList(EventPermissionRequiredMixin, ListView):
|
|||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
ctx = super().get_context_data(**kwargs)
|
ctx = super().get_context_data(**kwargs)
|
||||||
ctx['items'] = Item.objects.current.filter(event=self.request.event)
|
ctx['items'] = Item.objects.filter(event=self.request.event)
|
||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ class OrderView(EventPermissionRequiredMixin, DetailView):
|
|||||||
model = Order
|
model = Order
|
||||||
|
|
||||||
def get_object(self, queryset=None):
|
def get_object(self, queryset=None):
|
||||||
return Order.objects.current.get(
|
return Order.objects.get(
|
||||||
event=self.request.event,
|
event=self.request.event,
|
||||||
code=self.kwargs['code'].upper()
|
code=self.kwargs['code'].upper()
|
||||||
)
|
)
|
||||||
@@ -170,15 +170,13 @@ class OrderTransition(OrderView):
|
|||||||
else:
|
else:
|
||||||
messages.success(self.request, _('The order has been marked as paid.'))
|
messages.success(self.request, _('The order has been marked as paid.'))
|
||||||
elif self.order.status == 'n' and to == 'c':
|
elif self.order.status == 'n' and to == 'c':
|
||||||
order = self.order.clone()
|
self.order.status = Order.STATUS_CANCELLED
|
||||||
order.status = Order.STATUS_CANCELLED
|
self.order.save()
|
||||||
order.save()
|
|
||||||
messages.success(self.request, _('The order has been cancelled.'))
|
messages.success(self.request, _('The order has been cancelled.'))
|
||||||
elif self.order.status == 'p' and to == 'n':
|
elif self.order.status == 'p' and to == 'n':
|
||||||
order = self.order.clone()
|
self.order.status = Order.STATUS_PENDING
|
||||||
order.status = Order.STATUS_PENDING
|
self.order.payment_manual = True
|
||||||
order.payment_manual = True
|
self.order.save()
|
||||||
order.save()
|
|
||||||
messages.success(self.request, _('The order has been marked as not paid.'))
|
messages.success(self.request, _('The order has been marked as not paid.'))
|
||||||
elif self.order.status == 'p' and to == 'r':
|
elif self.order.status == 'p' and to == 'r':
|
||||||
ret = self.payment_provider.order_control_refund_perform(self.request, self.order)
|
ret = self.payment_provider.order_control_refund_perform(self.request, self.order)
|
||||||
@@ -233,7 +231,7 @@ class OrderDownload(OrderView):
|
|||||||
ct.cachedfile = cf
|
ct.cachedfile = cf
|
||||||
ct.save()
|
ct.save()
|
||||||
if not ct.cachedfile.file.name:
|
if not ct.cachedfile.file.name:
|
||||||
tickets.generate(self.order.identity, self.output.identifier)
|
tickets.generate(self.order.id, self.output.identifier)
|
||||||
return redirect(reverse('cachedfile.download', kwargs={'id': ct.cachedfile.id}))
|
return redirect(reverse('cachedfile.download', kwargs={'id': ct.cachedfile.id}))
|
||||||
|
|
||||||
|
|
||||||
@@ -305,7 +303,7 @@ class OrderGo(EventPermissionRequiredMixin, View):
|
|||||||
try:
|
try:
|
||||||
if code.startswith(request.event.slug.upper()):
|
if code.startswith(request.event.slug.upper()):
|
||||||
code = code[len(request.event.slug.upper()):]
|
code = code[len(request.event.slug.upper()):]
|
||||||
order = Order.objects.current.get(code=code, event=request.event)
|
order = Order.objects.get(code=code, event=request.event)
|
||||||
return redirect('control:event.order', event=request.event.slug, organizer=request.event.organizer.slug,
|
return redirect('control:event.order', event=request.event.slug, organizer=request.event.organizer.slug,
|
||||||
code=order.code)
|
code=order.code)
|
||||||
except Order.DoesNotExist:
|
except Order.DoesNotExist:
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ class OrganizerList(ListView):
|
|||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
if self.request.user.is_superuser:
|
if self.request.user.is_superuser:
|
||||||
return Organizer.objects.current.all()
|
return Organizer.objects.all()
|
||||||
else:
|
else:
|
||||||
return Organizer.objects.current.filter(
|
return Organizer.objects.filter(
|
||||||
permitted__id__exact=self.request.user.pk
|
permitted__id__exact=self.request.user.pk
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import versions.models
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
@@ -15,8 +14,12 @@ class Migration(migrations.Migration):
|
|||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='KnownDomain',
|
name='KnownDomain',
|
||||||
fields=[
|
fields=[
|
||||||
('domainname', models.CharField(serialize=False, max_length=255, primary_key=True)),
|
('domainname', models.CharField(primary_key=True, max_length=255, serialize=False)),
|
||||||
('organizer', versions.models.VersionedForeignKey(blank=True, to='pretixbase.Organizer', null=True)),
|
('organizer', models.ForeignKey(to='pretixbase.Organizer', null=True, blank=True, related_name='domains')),
|
||||||
],
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name_plural': 'Known domains',
|
||||||
|
'verbose_name': 'Known domain',
|
||||||
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import versions.models
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('pretixmultidomain', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='knowndomain',
|
|
||||||
options={'verbose_name': 'Known domain', 'verbose_name_plural': 'Known domains'},
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='knowndomain',
|
|
||||||
name='organizer',
|
|
||||||
field=versions.models.VersionedForeignKey(to='pretixbase.Organizer', null=True, related_name='domains', blank=True),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from versions.models import VersionedForeignKey
|
|
||||||
|
|
||||||
from pretix.base.models import Organizer
|
from pretix.base.models import Organizer
|
||||||
|
|
||||||
|
|
||||||
class KnownDomain(models.Model):
|
class KnownDomain(models.Model):
|
||||||
domainname = models.CharField(max_length=255, primary_key=True)
|
domainname = models.CharField(max_length=255, primary_key=True)
|
||||||
organizer = VersionedForeignKey(Organizer, blank=True, null=True, related_name='domains')
|
organizer = models.ForeignKey(Organizer, blank=True, null=True, related_name='domains')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Known domain")
|
verbose_name = _("Known domain")
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ def hbci_transactions(event, conf):
|
|||||||
|
|
||||||
log = []
|
log = []
|
||||||
data = []
|
data = []
|
||||||
accname = event.identity + '_' + str(int(time.time()))
|
accname = event.id + '_' + str(int(time.time()))
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
subprocess.call([
|
subprocess.call([
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ class ImportView(EventPermissionRequiredMixin, TemplateView):
|
|||||||
return self.process_mt940()
|
return self.process_mt940()
|
||||||
|
|
||||||
if 'confirm' in self.request.POST:
|
if 'confirm' in self.request.POST:
|
||||||
orders = Order.objects.current.filter(event=self.request.event,
|
orders = Order.objects.filter(event=self.request.event,
|
||||||
code__in=self.request.POST.getlist('mark_paid'))
|
code__in=self.request.POST.getlist('mark_paid'))
|
||||||
some_failed = False
|
some_failed = False
|
||||||
for order in orders:
|
for order in orders:
|
||||||
try:
|
try:
|
||||||
@@ -210,8 +210,7 @@ class ImportView(EventPermissionRequiredMixin, TemplateView):
|
|||||||
|
|
||||||
code = match.group(1)
|
code = match.group(1)
|
||||||
try:
|
try:
|
||||||
order = Order.objects.current.get(event=self.request.event,
|
order = Order.objects.get(event=self.request.event, code=code)
|
||||||
code=code)
|
|
||||||
except Order.DoesNotExist:
|
except Order.DoesNotExist:
|
||||||
row['class'] = 'danger'
|
row['class'] = 'danger'
|
||||||
row['message'] = _('Unknown order code detected')
|
row['message'] = _('Unknown order code detected')
|
||||||
|
|||||||
@@ -168,7 +168,6 @@ class Paypal(BasePaymentProvider):
|
|||||||
if payment.state == 'pending':
|
if payment.state == 'pending':
|
||||||
messages.warning(request, _('PayPal has not yet approved the payment. We will inform you as soon as the '
|
messages.warning(request, _('PayPal has not yet approved the payment. We will inform you as soon as the '
|
||||||
'payment completed.'))
|
'payment completed.'))
|
||||||
order = order.clone()
|
|
||||||
order.payment_info = json.dumps(payment.to_dict())
|
order.payment_info = json.dumps(payment.to_dict())
|
||||||
order.save()
|
order.save()
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ def success(request):
|
|||||||
request.session['payment_paypal_token'] = token
|
request.session['payment_paypal_token'] = token
|
||||||
request.session['payment_paypal_payer'] = payer
|
request.session['payment_paypal_payer'] = payer
|
||||||
try:
|
try:
|
||||||
event = Event.objects.current.get(identity=request.session['payment_paypal_event'])
|
event = Event.objects.get(id=request.session['payment_paypal_event'])
|
||||||
return redirect(eventreverse(event, 'presale:event.checkout', kwargs={'step': 'confirm'}))
|
return redirect(eventreverse(event, 'presale:event.checkout', kwargs={'step': 'confirm'}))
|
||||||
except Event.DoesNotExist:
|
except Event.DoesNotExist:
|
||||||
pass # TODO: Handle this
|
pass # TODO: Handle this
|
||||||
@@ -35,7 +35,7 @@ def success(request):
|
|||||||
def abort(request):
|
def abort(request):
|
||||||
messages.error(request, _('It looks like you cancelled the PayPal payment'))
|
messages.error(request, _('It looks like you cancelled the PayPal payment'))
|
||||||
try:
|
try:
|
||||||
event = Event.objects.current.get(identity=request.session['payment_paypal_event'])
|
event = Event.objects.get(id=request.session['payment_paypal_event'])
|
||||||
return redirect(eventreverse(event, 'presale:event.checkout', kwargs={'step': 'payment'}))
|
return redirect(eventreverse(event, 'presale:event.checkout', kwargs={'step': 'payment'}))
|
||||||
except Event.DoesNotExist:
|
except Event.DoesNotExist:
|
||||||
pass # TODO: Handle this
|
pass # TODO: Handle this
|
||||||
@@ -44,7 +44,7 @@ def abort(request):
|
|||||||
@login_required
|
@login_required
|
||||||
def retry(request, order):
|
def retry(request, order):
|
||||||
try:
|
try:
|
||||||
order = Order.objects.current.get(
|
order = Order.objects.get(
|
||||||
user=request.user,
|
user=request.user,
|
||||||
code=order,
|
code=order,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class ConfigView(EventPermissionRequiredMixin, TemplateView):
|
|||||||
class ApiView(View):
|
class ApiView(View):
|
||||||
def get(self, request, **kwargs):
|
def get(self, request, **kwargs):
|
||||||
try:
|
try:
|
||||||
event = Event.objects.current.get(
|
event = Event.objects.get(
|
||||||
slug=self.kwargs['event'],
|
slug=self.kwargs['event'],
|
||||||
organizer__slug=self.kwargs['organizer']
|
organizer__slug=self.kwargs['organizer']
|
||||||
)
|
)
|
||||||
@@ -53,12 +53,12 @@ class ApiView(View):
|
|||||||
or event.settings.get('pretixdroid_key') != request.GET.get('key', '')):
|
or event.settings.get('pretixdroid_key') != request.GET.get('key', '')):
|
||||||
return HttpResponseForbidden('Invalid key')
|
return HttpResponseForbidden('Invalid key')
|
||||||
|
|
||||||
ops = OrderPosition.objects.current.filter(
|
ops = OrderPosition.objects.filter(
|
||||||
order__event=event, order__status=Order.STATUS_PAID,
|
order__event=event, order__status=Order.STATUS_PAID,
|
||||||
).select_related('item', 'variation')
|
).select_related('item', 'variation')
|
||||||
data = [
|
data = [
|
||||||
{
|
{
|
||||||
'id': op.identity,
|
'id': op.id,
|
||||||
'item': str(op.item),
|
'item': str(op.item),
|
||||||
'variation': str(op.variation) if op.variation else None,
|
'variation': str(op.variation) if op.variation else None,
|
||||||
'attendee_name': op.attendee_name
|
'attendee_name': op.attendee_name
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class SenderView(EventPermissionRequiredMixin, FormView):
|
|||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
orders = Order.objects.current.filter(
|
orders = Order.objects.filter(
|
||||||
event=self.request.event, status__in=form.cleaned_data['sendto']
|
event=self.request.event, status__in=form.cleaned_data['sendto']
|
||||||
).select_related("user")
|
).select_related("user")
|
||||||
users = set([o.user for o in orders])
|
users = set([o.user for o in orders])
|
||||||
|
|||||||
@@ -27,12 +27,12 @@ class IndexView(EventPermissionRequiredMixin, TemplateView):
|
|||||||
ctx['obd_data'] = cache.get('statistics_obd_data')
|
ctx['obd_data'] = cache.get('statistics_obd_data')
|
||||||
if not ctx['obd_data']:
|
if not ctx['obd_data']:
|
||||||
ordered_by_day = {}
|
ordered_by_day = {}
|
||||||
for o in Order.objects.current.filter(event=self.request.event).values('datetime'):
|
for o in Order.objects.filter(event=self.request.event).values('datetime'):
|
||||||
day = o['datetime'].date()
|
day = o['datetime'].date()
|
||||||
ordered_by_day[day] = ordered_by_day.get(day, 0) + 1
|
ordered_by_day[day] = ordered_by_day.get(day, 0) + 1
|
||||||
paid_by_day = {}
|
paid_by_day = {}
|
||||||
for o in Order.objects.current.filter(event=self.request.event,
|
for o in Order.objects.filter(event=self.request.event,
|
||||||
payment_date__isnull=False).values('payment_date'):
|
payment_date__isnull=False).values('payment_date'):
|
||||||
day = o['payment_date'].date()
|
day = o['payment_date'].date()
|
||||||
paid_by_day[day] = paid_by_day.get(day, 0) + 1
|
paid_by_day[day] = paid_by_day.get(day, 0) + 1
|
||||||
|
|
||||||
@@ -59,21 +59,21 @@ class IndexView(EventPermissionRequiredMixin, TemplateView):
|
|||||||
if not ctx['obp_data']:
|
if not ctx['obp_data']:
|
||||||
num_ordered = {
|
num_ordered = {
|
||||||
p['item']: p['cnt']
|
p['item']: p['cnt']
|
||||||
for p in (OrderPosition.objects.current
|
for p in (OrderPosition.objects
|
||||||
.filter(order__event=self.request.event)
|
.filter(order__event=self.request.event)
|
||||||
.values('item')
|
.values('item')
|
||||||
.annotate(cnt=Count('id')))
|
.annotate(cnt=Count('id')))
|
||||||
}
|
}
|
||||||
num_paid = {
|
num_paid = {
|
||||||
p['item']: p['cnt']
|
p['item']: p['cnt']
|
||||||
for p in (OrderPosition.objects.current
|
for p in (OrderPosition.objects
|
||||||
.filter(order__event=self.request.event, order__status=Order.STATUS_PAID)
|
.filter(order__event=self.request.event, order__status=Order.STATUS_PAID)
|
||||||
.values('item')
|
.values('item')
|
||||||
.annotate(cnt=Count('id')))
|
.annotate(cnt=Count('id')))
|
||||||
}
|
}
|
||||||
item_names = {
|
item_names = {
|
||||||
i.identity: str(i.name)
|
i.id: str(i.name)
|
||||||
for i in Item.objects.current.filter(event=self.request.event)
|
for i in Item.objects.filter(event=self.request.event)
|
||||||
}
|
}
|
||||||
ctx['obp_data'] = [
|
ctx['obp_data'] = [
|
||||||
{
|
{
|
||||||
@@ -87,9 +87,9 @@ class IndexView(EventPermissionRequiredMixin, TemplateView):
|
|||||||
ctx['rev_data'] = cache.get('statistics_rev_data')
|
ctx['rev_data'] = cache.get('statistics_rev_data')
|
||||||
if not ctx['rev_data']:
|
if not ctx['rev_data']:
|
||||||
rev_by_day = {}
|
rev_by_day = {}
|
||||||
for o in Order.objects.current.filter(event=self.request.event,
|
for o in Order.objects.filter(event=self.request.event,
|
||||||
status=Order.STATUS_PAID,
|
status=Order.STATUS_PAID,
|
||||||
payment_date__isnull=False).values('payment_date', 'total'):
|
payment_date__isnull=False).values('payment_date', 'total'):
|
||||||
day = o['payment_date'].date()
|
day = o['payment_date'].date()
|
||||||
rev_by_day[day] = rev_by_day.get(day, 0) + o['total']
|
rev_by_day[day] = rev_by_day.get(day, 0) + o['total']
|
||||||
|
|
||||||
|
|||||||
@@ -83,18 +83,17 @@ class Stripe(BasePaymentProvider):
|
|||||||
currency=request.event.currency.lower(),
|
currency=request.event.currency.lower(),
|
||||||
source=request.session['payment_stripe_token'],
|
source=request.session['payment_stripe_token'],
|
||||||
metadata={
|
metadata={
|
||||||
'order': order.identity,
|
'order': order.id,
|
||||||
'event': self.event.identity,
|
'event': self.event.id,
|
||||||
'code': order.code
|
'code': order.code
|
||||||
},
|
},
|
||||||
# TODO: Is this sufficient?
|
# TODO: Is this sufficient?
|
||||||
idempotency_key=self.event.identity + order.code + request.session['payment_stripe_token']
|
idempotency_key=self.event.id + order.code + request.session['payment_stripe_token']
|
||||||
)
|
)
|
||||||
except stripe.error.CardError as e:
|
except stripe.error.CardError as e:
|
||||||
err = e.json_body['error']
|
err = e.json_body['error']
|
||||||
messages.error(request, _('Stripe reported an error with your card: %s' % err['message']))
|
messages.error(request, _('Stripe reported an error with your card: %s' % err['message']))
|
||||||
logger.info('Stripe card error: %s' % str(err))
|
logger.info('Stripe card error: %s' % str(err))
|
||||||
order = order.clone()
|
|
||||||
order.payment_info = json.dumps({
|
order.payment_info = json.dumps({
|
||||||
'error': True,
|
'error': True,
|
||||||
'message': err['message'],
|
'message': err['message'],
|
||||||
@@ -106,7 +105,6 @@ class Stripe(BasePaymentProvider):
|
|||||||
messages.error(request, _('We had trouble communicating with Stripe. Please try again and get in touch '
|
messages.error(request, _('We had trouble communicating with Stripe. Please try again and get in touch '
|
||||||
'with us if this problem persists.'))
|
'with us if this problem persists.'))
|
||||||
logger.error('Stripe error: %s' % str(err))
|
logger.error('Stripe error: %s' % str(err))
|
||||||
order = order.clone()
|
|
||||||
order.payment_info = json.dumps({
|
order.payment_info = json.dumps({
|
||||||
'error': True,
|
'error': True,
|
||||||
'message': err['message'],
|
'message': err['message'],
|
||||||
@@ -121,7 +119,6 @@ class Stripe(BasePaymentProvider):
|
|||||||
else:
|
else:
|
||||||
messages.warning(request, _('Stripe reported an error: %s' % charge.failure_message))
|
messages.warning(request, _('Stripe reported an error: %s' % charge.failure_message))
|
||||||
logger.info('Charge failed: %s' % str(charge))
|
logger.info('Charge failed: %s' % str(charge))
|
||||||
order = order.clone()
|
|
||||||
order.payment_info = str(charge)
|
order.payment_info = str(charge)
|
||||||
order.save()
|
order.save()
|
||||||
del request.session['payment_stripe_token']
|
del request.session['payment_stripe_token']
|
||||||
|
|||||||
@@ -30,12 +30,12 @@ def webhook(request):
|
|||||||
return HttpResponse('Event not given', status=200)
|
return HttpResponse('Event not given', status=200)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
event = Event.objects.current.get(identity=metadata['event'])
|
event = Event.objects.get(id=metadata['event'])
|
||||||
except Event.DoesNotExist:
|
except Event.DoesNotExist:
|
||||||
return HttpResponse('Event not found', status=200)
|
return HttpResponse('Event not found', status=200)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
order = Order.objects.current.get(identity=metadata['order'])
|
order = Order.objects.get(id=metadata['order'])
|
||||||
except Order.DoesNotExist:
|
except Order.DoesNotExist:
|
||||||
return HttpResponse('Order not found', status=200)
|
return HttpResponse('Order not found', status=200)
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class PdfTicketOutput(BaseTicketOutput):
|
|||||||
qr_s = self.settings.get('qr_s', default=80, as_type=float)
|
qr_s = self.settings.get('qr_s', default=80, as_type=float)
|
||||||
if qr_s:
|
if qr_s:
|
||||||
reqs = qr_s * units.mm
|
reqs = qr_s * units.mm
|
||||||
qrw = QrCodeWidget(op.identity, barLevel='H')
|
qrw = QrCodeWidget(op.id, barLevel='H')
|
||||||
b = qrw.getBounds()
|
b = qrw.getBounds()
|
||||||
w = b[2] - b[0]
|
w = b[2] - b[0]
|
||||||
h = b[3] - b[1]
|
h = b[3] - b[1]
|
||||||
@@ -84,7 +84,7 @@ class PdfTicketOutput(BaseTicketOutput):
|
|||||||
p.setFont("Helvetica", code_s)
|
p.setFont("Helvetica", code_s)
|
||||||
code_x = self.settings.get('code_x', default=15, as_type=float)
|
code_x = self.settings.get('code_x', default=15, as_type=float)
|
||||||
code_y = self.settings.get('code_y', default=130, as_type=float)
|
code_y = self.settings.get('code_y', default=130, as_type=float)
|
||||||
p.drawString(code_x * units.mm, code_y * units.mm, op.identity)
|
p.drawString(code_x * units.mm, code_y * units.mm, op.id)
|
||||||
|
|
||||||
p.showPage()
|
p.showPage()
|
||||||
|
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
|
|||||||
aw.question_id: aw.answer for aw in cp.answers.all()
|
aw.question_id: aw.answer for aw in cp.answers.all()
|
||||||
}
|
}
|
||||||
for q in cp.item.questions.all():
|
for q in cp.item.questions.all():
|
||||||
if q.required and q.identity not in answ:
|
if q.required and q.id not in answ:
|
||||||
if warn:
|
if warn:
|
||||||
messages.warning(request, _('Please fill in answers to all required questions.'))
|
messages.warning(request, _('Please fill in answers to all required questions.'))
|
||||||
return False
|
return False
|
||||||
@@ -193,7 +193,7 @@ class PaymentStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
|
|||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def _total_order_value(self):
|
def _total_order_value(self):
|
||||||
return CartPosition.objects.current.filter(
|
return CartPosition.objects.filter(
|
||||||
Q(cart_id=self.request.session.session_key) & Q(event=self.request.event)
|
Q(cart_id=self.request.session.session_key) & Q(event=self.request.event)
|
||||||
).aggregate(sum=Sum('price'))['sum']
|
).aggregate(sum=Sum('price'))['sum']
|
||||||
|
|
||||||
@@ -300,15 +300,15 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep):
|
|||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
self.request = request
|
self.request = request
|
||||||
return self.do(self.request.event.identity, self.payment_provider.identifier,
|
return self.do(self.request.event.id, self.payment_provider.identifier,
|
||||||
[p.identity for p in self.positions], request.session.get('email'),
|
[p.id for p in self.positions], request.session.get('email'),
|
||||||
translation.get_language())
|
translation.get_language())
|
||||||
|
|
||||||
def get_success_message(self, value):
|
def get_success_message(self, value):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_success_url(self, value):
|
def get_success_url(self, value):
|
||||||
order = Order.objects.current.get(identity=value)
|
order = Order.objects.get(id=value)
|
||||||
return self.get_order_url(order)
|
return self.get_order_url(order)
|
||||||
|
|
||||||
def get_error_message(self, exception):
|
def get_error_message(self, exception):
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class QuestionsForm(forms.Form):
|
|||||||
answers = [
|
answers = [
|
||||||
a for a
|
a for a
|
||||||
in (cartpos.answers.all() if cartpos else orderpos.answers.all())
|
in (cartpos.answers.all() if cartpos else orderpos.answers.all())
|
||||||
if a.question_id == q.identity
|
if a.question_id == q.id
|
||||||
]
|
]
|
||||||
if answers:
|
if answers:
|
||||||
initial = answers[0].answer
|
initial = answers[0].answer
|
||||||
@@ -73,4 +73,4 @@ class QuestionsForm(forms.Form):
|
|||||||
if answers:
|
if answers:
|
||||||
# Cache the answer object for later use
|
# Cache the answer object for later use
|
||||||
field.answer = answers[0]
|
field.answer = answers[0]
|
||||||
self.fields['question_%s' % q.identity] = field
|
self.fields['question_%s' % q.id] = field
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class EventMiddleware:
|
|||||||
path = "/" + request.get_full_path().split("/", 2)[-1]
|
path = "/" + request.get_full_path().split("/", 2)[-1]
|
||||||
return redirect(path)
|
return redirect(path)
|
||||||
|
|
||||||
request.event = Event.objects.current.filter(
|
request.event = Event.objects.filter(
|
||||||
slug=url.kwargs['event'],
|
slug=url.kwargs['event'],
|
||||||
organizer=request.organizer,
|
organizer=request.organizer,
|
||||||
).select_related('organizer')[0]
|
).select_related('organizer')[0]
|
||||||
@@ -34,13 +34,13 @@ class EventMiddleware:
|
|||||||
else:
|
else:
|
||||||
# We are on our main domain
|
# We are on our main domain
|
||||||
if 'event' in url.kwargs and 'organizer' in url.kwargs:
|
if 'event' in url.kwargs and 'organizer' in url.kwargs:
|
||||||
request.event = Event.objects.current.filter(
|
request.event = Event.objects.filter(
|
||||||
slug=url.kwargs['event'],
|
slug=url.kwargs['event'],
|
||||||
organizer__slug=url.kwargs['organizer']
|
organizer__slug=url.kwargs['organizer']
|
||||||
).select_related('organizer')[0]
|
).select_related('organizer')[0]
|
||||||
request.organizer = request.event.organizer
|
request.organizer = request.event.organizer
|
||||||
elif 'organizer' in url.kwargs:
|
elif 'organizer' in url.kwargs:
|
||||||
request.organizer = Organizer.objects.current.filter(
|
request.organizer = Organizer.objects.filter(
|
||||||
slug=url.kwargs['organizer']
|
slug=url.kwargs['organizer']
|
||||||
)[0]
|
)[0]
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<h4 class="panel-title">
|
<h4 class="panel-title">
|
||||||
<a data-toggle="collapse" href="#cp{{ form.pos.identity }}"
|
<a data-toggle="collapse" href="#cp{{ form.pos.id }}"
|
||||||
data-parent="#questions_accordion">
|
data-parent="#questions_accordion">
|
||||||
<strong>{{ form.pos.item.name }}</strong>
|
<strong>{{ form.pos.item.name }}</strong>
|
||||||
{% if form.pos.variation %}
|
{% if form.pos.variation %}
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div id="cp{{ form.pos.identity }}"
|
<div id="cp{{ form.pos.id }}"
|
||||||
class="panel-collapse collapsed in">
|
class="panel-collapse collapsed in">
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
{% bootstrap_form form layout="horizontal" %}
|
{% bootstrap_form form layout="horizontal" %}
|
||||||
|
|||||||
@@ -26,10 +26,10 @@
|
|||||||
method="post">
|
method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% if line.variation %}
|
{% if line.variation %}
|
||||||
<input type="hidden" name="variation_{{ line.item.identity }}_{{ line.variation.identity }}"
|
<input type="hidden" name="variation_{{ line.item.id }}_{{ line.variation.id }}"
|
||||||
value="1" />
|
value="1" />
|
||||||
{% else %}
|
{% else %}
|
||||||
<input type="hidden" name="item_{{ line.item.identity }}"
|
<input type="hidden" name="item_{{ line.item.id }}"
|
||||||
value="1" />
|
value="1" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button class="btn btn-mini btn-link"><i class="fa fa-minus"></i></button>
|
<button class="btn btn-mini btn-link"><i class="fa fa-minus"></i></button>
|
||||||
@@ -41,10 +41,10 @@
|
|||||||
method="post" data-asynctask>
|
method="post" data-asynctask>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% if line.variation %}
|
{% if line.variation %}
|
||||||
<input type="hidden" name="variation_{{ line.item.identity }}_{{ line.variation.identity }}"
|
<input type="hidden" name="variation_{{ line.item.id }}_{{ line.variation.id }}"
|
||||||
value="1" />
|
value="1" />
|
||||||
{% else %}
|
{% else %}
|
||||||
<input type="hidden" name="item_{{ line.item.identity }}"
|
<input type="hidden" name="item_{{ line.item.id }}"
|
||||||
value="1" />
|
value="1" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button class="btn btn-mini btn-link"><i class="fa fa-plus"></i></button>
|
<button class="btn btn-mini btn-link"><i class="fa fa-plus"></i></button>
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
{% if item.picture %}
|
{% if item.picture %}
|
||||||
<a href="{{ item.picture.url }}" class="productpicture"
|
<a href="{{ item.picture.url }}" class="productpicture"
|
||||||
data-title="{{ item.name }}"
|
data-title="{{ item.name }}"
|
||||||
data-lightbox="{{ item.identity }}">
|
data-lightbox="{{ item.id }}">
|
||||||
<img src="{{ item.picture|thumbnail_url:'productlist' }}"
|
<img src="{{ item.picture|thumbnail_url:'productlist' }}"
|
||||||
alt="{{ item.name }}" />
|
alt="{{ item.name }}" />
|
||||||
</a>
|
</a>
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
<div class="col-md-2 col-xs-6 availability-box available">
|
<div class="col-md-2 col-xs-6 availability-box available">
|
||||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||||
max="{{ var.cached_availability.1 }}"
|
max="{{ var.cached_availability.1 }}"
|
||||||
name="variation_{{ item.identity }}_{{ var.variation.identity }}">
|
name="variation_{{ item.id }}_{{ var.variation.id }}">
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
{% include "pretixpresale/event/fragment_availability.html" with avail=var.cached_availability.0 %}
|
{% include "pretixpresale/event/fragment_availability.html" with avail=var.cached_availability.0 %}
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
{% if item.picture %}
|
{% if item.picture %}
|
||||||
<a href="{{ item.picture.url }}" class="productpicture"
|
<a href="{{ item.picture.url }}" class="productpicture"
|
||||||
data-title="{{ item.name }}"
|
data-title="{{ item.name }}"
|
||||||
data-lightbox="{{ item.identity }}">
|
data-lightbox="{{ item.id }}">
|
||||||
<img src="{{ item.picture|thumbnail_url:'productlist' }}"
|
<img src="{{ item.picture|thumbnail_url:'productlist' }}"
|
||||||
alt="{{ item.name }}" />
|
alt="{{ item.name }}" />
|
||||||
</a>
|
</a>
|
||||||
@@ -144,7 +144,7 @@
|
|||||||
{% if item.cached_availability.0 == 100 %}
|
{% if item.cached_availability.0 == 100 %}
|
||||||
<div class="col-md-2 col-xs-6 availability-box available">
|
<div class="col-md-2 col-xs-6 availability-box available">
|
||||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||||
max="{{ item.cached_availability.1 }}" name="item_{{ item.identity }}">
|
max="{{ item.cached_availability.1 }}" name="item_{{ item.id }}">
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
{% include "pretixpresale/event/fragment_availability.html" with avail=item.cached_availability.0 %}
|
{% include "pretixpresale/event/fragment_availability.html" with avail=item.cached_availability.0 %}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<h4 class="panel-title">
|
<h4 class="panel-title">
|
||||||
<a data-toggle="collapse" href="#cp{{ form.pos.identity }}"
|
<a data-toggle="collapse" href="#cp{{ form.pos.id }}"
|
||||||
data-parent="#questions_accordion">
|
data-parent="#questions_accordion">
|
||||||
<strong>{{ form.pos.item }}</strong>
|
<strong>{{ form.pos.item }}</strong>
|
||||||
{% if form.pos.variation %}
|
{% if form.pos.variation %}
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div id="cp{{ form.pos.identity }}"
|
<div id="cp{{ form.pos.id }}"
|
||||||
class="panel-collapse collapsed {% if forloop.counter0 == 0 %}in{% endif %}">
|
class="panel-collapse collapsed {% if forloop.counter0 == 0 %}in{% endif %}">
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
{% bootstrap_form form layout="horizontal" %}
|
{% bootstrap_form form layout="horizontal" %}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class CartMixin:
|
|||||||
"""
|
"""
|
||||||
A list of this users cart position
|
A list of this users cart position
|
||||||
"""
|
"""
|
||||||
return list(CartPosition.objects.current.filter(
|
return list(CartPosition.objects.filter(
|
||||||
cart_id=self.request.session.session_key, event=self.request.event
|
cart_id=self.request.session.session_key, event=self.request.event
|
||||||
).order_by(
|
).order_by(
|
||||||
'item', 'variation'
|
'item', 'variation'
|
||||||
@@ -26,7 +26,7 @@ class CartMixin:
|
|||||||
))
|
))
|
||||||
|
|
||||||
def get_cart(self, answers=False, queryset=None, payment_fee=None):
|
def get_cart(self, answers=False, queryset=None, payment_fee=None):
|
||||||
queryset = queryset or CartPosition.objects.current.filter(
|
queryset = queryset or CartPosition.objects.filter(
|
||||||
cart_id=self.request.session.session_key, event=self.request.event
|
cart_id=self.request.session.session_key, event=self.request.event
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -49,8 +49,8 @@ class CartMixin:
|
|||||||
def keyfunc(pos):
|
def keyfunc(pos):
|
||||||
if answers and ((pos.item.admission and self.request.event.settings.attendee_names_asked)
|
if answers and ((pos.item.admission and self.request.event.settings.attendee_names_asked)
|
||||||
or pos.item.questions.all()):
|
or pos.item.questions.all()):
|
||||||
return pos.id, "", "", ""
|
return pos.id, 0, 0, 0
|
||||||
return "", pos.item_id, pos.variation_id, pos.price
|
return 0, pos.item_id, pos.variation_id, pos.price
|
||||||
|
|
||||||
positions = []
|
positions = []
|
||||||
for k, g in groupby(sorted(list(cartpos), key=keyfunc), key=keyfunc):
|
for k, g in groupby(sorted(list(cartpos), key=keyfunc), key=keyfunc):
|
||||||
|
|||||||
@@ -37,13 +37,13 @@ class CartActionMixin:
|
|||||||
continue
|
continue
|
||||||
if key.startswith('item_'):
|
if key.startswith('item_'):
|
||||||
try:
|
try:
|
||||||
items.append((key.split("_")[1], None, int(value)))
|
items.append((int(key.split("_")[1]), None, int(value)))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
messages.error(self.request, _('Please enter numbers only.'))
|
messages.error(self.request, _('Please enter numbers only.'))
|
||||||
return []
|
return []
|
||||||
elif key.startswith('variation_'):
|
elif key.startswith('variation_'):
|
||||||
try:
|
try:
|
||||||
items.append((key.split("_")[1], key.split("_")[2], int(value)))
|
items.append((int(key.split("_")[1]), int(key.split("_")[2]), int(value)))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
messages.error(self.request, _('Please enter numbers only.'))
|
messages.error(self.request, _('Please enter numbers only.'))
|
||||||
return []
|
return []
|
||||||
@@ -60,7 +60,7 @@ class CartRemove(EventViewMixin, CartActionMixin, View):
|
|||||||
if not items:
|
if not items:
|
||||||
return redirect(self.get_error_url())
|
return redirect(self.get_error_url())
|
||||||
|
|
||||||
remove_items_from_cart(self.request.event.identity, items, self.request.session.session_key)
|
remove_items_from_cart(self.request.event.id, items, self.request.session.session_key)
|
||||||
messages.success(self.request, _('Your cart has been updated.'))
|
messages.success(self.request, _('Your cart has been updated.'))
|
||||||
return redirect(self.get_success_url())
|
return redirect(self.get_success_url())
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ class CartAdd(EventViewMixin, CartActionMixin, AsyncAction, View):
|
|||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
items = self._items_from_post_data()
|
items = self._items_from_post_data()
|
||||||
if items:
|
if items:
|
||||||
return self.do(self.request.event.identity, items, self.request.session.session_key)
|
return self.do(self.request.event.id, items, self.request.session.session_key)
|
||||||
else:
|
else:
|
||||||
if 'ajax' in self.request.GET or 'ajax' in self.request.POST:
|
if 'ajax' in self.request.GET or 'ajax' in self.request.POST:
|
||||||
return JsonResponse({
|
return JsonResponse({
|
||||||
|
|||||||
@@ -59,7 +59,8 @@ class EventIndex(EventViewMixin, CartMixin, TemplateView):
|
|||||||
# insert categories into a set for uniqueness
|
# insert categories into a set for uniqueness
|
||||||
# a set is unsorted, so sort again by category
|
# a set is unsorted, so sort again by category
|
||||||
],
|
],
|
||||||
key=lambda group: (group[0].position, group[0].identity) if group[0] is not None else (0, "")
|
key=lambda group: (group[0].position, group[0].id) if (
|
||||||
|
group[0] is not None and group[0].id is not None) else (0, 0)
|
||||||
)
|
)
|
||||||
|
|
||||||
context['cart'] = self.get_cart()
|
context['cart'] = self.get_cart()
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ class OrderDetailMixin:
|
|||||||
@cached_property
|
@cached_property
|
||||||
def order(self):
|
def order(self):
|
||||||
try:
|
try:
|
||||||
return Order.objects.current.get(secret=self.kwargs['secret'],
|
return Order.objects.get(secret=self.kwargs['secret'],
|
||||||
event=self.request.event, code=self.kwargs['order'])
|
event=self.request.event, code=self.kwargs['order'])
|
||||||
except Order.DoesNotExist:
|
except Order.DoesNotExist:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ class OrderDetails(EventViewMixin, OrderDetailMixin, CartMixin, TemplateView):
|
|||||||
ctx['download_buttons'] = self.download_buttons
|
ctx['download_buttons'] = self.download_buttons
|
||||||
ctx['cart'] = self.get_cart(
|
ctx['cart'] = self.get_cart(
|
||||||
answers=True,
|
answers=True,
|
||||||
queryset=OrderPosition.objects.current.filter(order=self.order)
|
queryset=OrderPosition.objects.filter(order=self.order)
|
||||||
)
|
)
|
||||||
if self.order.status == Order.STATUS_PENDING:
|
if self.order.status == Order.STATUS_PENDING:
|
||||||
ctx['payment'] = self.payment_provider.order_pending_render(self.request, self.order)
|
ctx['payment'] = self.payment_provider.order_pending_render(self.request, self.order)
|
||||||
@@ -251,9 +251,8 @@ class OrderCancel(EventViewMixin, OrderDetailMixin, TemplateView):
|
|||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
order = self.order.clone()
|
self.order.status = Order.STATUS_CANCELLED
|
||||||
order.status = Order.STATUS_CANCELLED
|
self.order.save()
|
||||||
order.save()
|
|
||||||
return redirect(self.get_order_url())
|
return redirect(self.get_order_url())
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
@@ -302,5 +301,5 @@ class OrderDownload(EventViewMixin, OrderDetailMixin, View):
|
|||||||
cf.save()
|
cf.save()
|
||||||
ct.cachedfile = cf
|
ct.cachedfile = cf
|
||||||
ct.save()
|
ct.save()
|
||||||
generate(self.order.identity, self.output.identifier)
|
generate(self.order.id, self.output.identifier)
|
||||||
return redirect(reverse('cachedfile.download', kwargs={'id': ct.cachedfile.id}))
|
return redirect(reverse('cachedfile.download', kwargs={'id': ct.cachedfile.id}))
|
||||||
|
|||||||
@@ -20,6 +20,6 @@ class OrganizerIndex(OrganizerViewMixin, ListView):
|
|||||||
else:
|
else:
|
||||||
query &= Q(Q(date_from__gte=now()) | Q(date_to__gte=now()))
|
query &= Q(Q(date_from__gte=now()) | Q(date_to__gte=now()))
|
||||||
order = 'date_from'
|
order = 'date_from'
|
||||||
return Event.objects.current.filter(
|
return Event.objects.filter(
|
||||||
Q(organizer=self.request.organizer) & query
|
Q(organizer=self.request.organizer) & query
|
||||||
).order_by(order)
|
).order_by(order)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class QuestionsViewMixin:
|
|||||||
cartpos = cr if isinstance(cr, CartPosition) else None
|
cartpos = cr if isinstance(cr, CartPosition) else None
|
||||||
orderpos = cr if isinstance(cr, OrderPosition) else None
|
orderpos = cr if isinstance(cr, OrderPosition) else None
|
||||||
form = QuestionsForm(event=self.request.event,
|
form = QuestionsForm(event=self.request.event,
|
||||||
prefix=cr.identity,
|
prefix=cr.id,
|
||||||
cartpos=cartpos,
|
cartpos=cartpos,
|
||||||
orderpos=orderpos,
|
orderpos=orderpos,
|
||||||
data=(self.request.POST if self.request.method == 'POST' else None))
|
data=(self.request.POST if self.request.method == 'POST' else None))
|
||||||
@@ -37,7 +37,6 @@ class QuestionsViewMixin:
|
|||||||
# answers to the questions / in the CartPosition object
|
# answers to the questions / in the CartPosition object
|
||||||
for k, v in form.cleaned_data.items():
|
for k, v in form.cleaned_data.items():
|
||||||
if k == 'attendee_name':
|
if k == 'attendee_name':
|
||||||
form.pos = form.pos.clone()
|
|
||||||
form.pos.attendee_name = v if v != '' else None
|
form.pos.attendee_name = v if v != '' else None
|
||||||
form.pos.save()
|
form.pos.save()
|
||||||
elif k.startswith('question_') and v is not None:
|
elif k.startswith('question_') and v is not None:
|
||||||
@@ -48,7 +47,6 @@ class QuestionsViewMixin:
|
|||||||
if v == '':
|
if v == '':
|
||||||
field.answer.delete()
|
field.answer.delete()
|
||||||
else:
|
else:
|
||||||
field.answer = field.answer.clone()
|
|
||||||
field.answer.answer = v
|
field.answer.answer = v
|
||||||
field.answer.save()
|
field.answer.save()
|
||||||
elif v != '':
|
elif v != '':
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ python-dateutil>=2.4,<2.5
|
|||||||
pytz
|
pytz
|
||||||
django-bootstrap3>=6.1,<6.2
|
django-bootstrap3>=6.1,<6.2
|
||||||
git+https://github.com/pretix/django-formset-js.git@master#egg=django-formset-js
|
git+https://github.com/pretix/django-formset-js.git@master#egg=django-formset-js
|
||||||
cleanerversion==1.5.3
|
|
||||||
#git+https://github.com/pretix/cleanerversion.git@pretix#egg=CleanerVersion
|
|
||||||
django-compressor>=1.5,<2.0
|
django-compressor>=1.5,<2.0
|
||||||
reportlab>=3.1.44,<3.2
|
reportlab>=3.1.44,<3.2
|
||||||
git+https://github.com/pretix/PyPDF2.git@pretix#egg=PyPDF2
|
git+https://github.com/pretix/PyPDF2.git@pretix#egg=PyPDF2
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ def event():
|
|||||||
def test_locking_exclusive(event):
|
def test_locking_exclusive(event):
|
||||||
with event.lock():
|
with event.lock():
|
||||||
with pytest.raises(EventLock.LockTimeoutException):
|
with pytest.raises(EventLock.LockTimeoutException):
|
||||||
ev = Event.objects.current.get(identity=event.identity)
|
ev = Event.objects.get(id=event.id)
|
||||||
with ev.lock():
|
with ev.lock():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ def test_lock_timeout_steal(event):
|
|||||||
locking.LOCK_TIMEOUT = 5
|
locking.LOCK_TIMEOUT = 5
|
||||||
locking.lock_event(event)
|
locking.lock_event(event)
|
||||||
with pytest.raises(EventLock.LockTimeoutException):
|
with pytest.raises(EventLock.LockTimeoutException):
|
||||||
ev = Event.objects.current.get(identity=event.identity)
|
ev = Event.objects.get(id=event.id)
|
||||||
locking.lock_event(ev)
|
locking.lock_event(ev)
|
||||||
time.sleep(6)
|
time.sleep(6)
|
||||||
locking.lock_event(ev)
|
locking.lock_event(ev)
|
||||||
|
|||||||
@@ -146,23 +146,6 @@ class ItemVariationsTest(TestCase):
|
|||||||
self.assertEqual(num_variations, 1)
|
self.assertEqual(num_variations, 1)
|
||||||
|
|
||||||
|
|
||||||
class VersionableTestCase(TestCase):
|
|
||||||
def test_shallow_cone(self):
|
|
||||||
o = Organizer.objects.create(name='Dummy', slug='dummy')
|
|
||||||
event = Event.objects.create(
|
|
||||||
organizer=o, name='Dummy', slug='dummy',
|
|
||||||
date_from=now(),
|
|
||||||
)
|
|
||||||
item = Item.objects.create(event=event, name='Dummy', default_price=14)
|
|
||||||
quota_old = Quota.objects.create(event=event, name='All', size=5)
|
|
||||||
quota_old.items.add(item)
|
|
||||||
quota_new = quota_old.clone_shallow()
|
|
||||||
self.assertIsNone(quota_new.version_end_date)
|
|
||||||
self.assertIsNotNone(quota_old.version_end_date)
|
|
||||||
self.assertEqual(quota_new.items.count(), 0)
|
|
||||||
self.assertEqual(quota_old.items.count(), 1)
|
|
||||||
|
|
||||||
|
|
||||||
class UserTestCase(TestCase):
|
class UserTestCase(TestCase):
|
||||||
def test_name(self):
|
def test_name(self):
|
||||||
u = User.objects.create_user('test@foo.bar', 'test')
|
u = User.objects.create_user('test@foo.bar', 'test')
|
||||||
@@ -336,14 +319,14 @@ class OrderTestCase(BaseQuotaTestCase):
|
|||||||
self.quota.size = 0
|
self.quota.size = 0
|
||||||
self.quota.save()
|
self.quota.save()
|
||||||
mark_order_paid(self.order)
|
mark_order_paid(self.order)
|
||||||
self.order = Order.objects.current.get(identity=self.order.identity)
|
self.order = Order.objects.get(id=self.order.id)
|
||||||
self.assertEqual(self.order.status, Order.STATUS_PAID)
|
self.assertEqual(self.order.status, Order.STATUS_PAID)
|
||||||
|
|
||||||
def test_paid_expired_available(self):
|
def test_paid_expired_available(self):
|
||||||
self.order.expires = now() - timedelta(days=2)
|
self.order.expires = now() - timedelta(days=2)
|
||||||
self.order.save()
|
self.order.save()
|
||||||
mark_order_paid(self.order)
|
mark_order_paid(self.order)
|
||||||
self.order = Order.objects.current.get(identity=self.order.identity)
|
self.order = Order.objects.get(id=self.order.id)
|
||||||
self.assertEqual(self.order.status, Order.STATUS_PAID)
|
self.assertEqual(self.order.status, Order.STATUS_PAID)
|
||||||
|
|
||||||
def test_paid_expired_partial(self):
|
def test_paid_expired_partial(self):
|
||||||
@@ -356,7 +339,7 @@ class OrderTestCase(BaseQuotaTestCase):
|
|||||||
self.assertFalse(True, 'This should have raised an exception.')
|
self.assertFalse(True, 'This should have raised an exception.')
|
||||||
except Quota.QuotaExceededException:
|
except Quota.QuotaExceededException:
|
||||||
pass
|
pass
|
||||||
self.order = Order.objects.current.get(identity=self.order.identity)
|
self.order = Order.objects.get(id=self.order.id)
|
||||||
self.assertIn(self.order.status, (Order.STATUS_PENDING, Order.STATUS_EXPIRED))
|
self.assertIn(self.order.status, (Order.STATUS_PENDING, Order.STATUS_EXPIRED))
|
||||||
|
|
||||||
def test_paid_expired_unavailable(self):
|
def test_paid_expired_unavailable(self):
|
||||||
@@ -369,7 +352,7 @@ class OrderTestCase(BaseQuotaTestCase):
|
|||||||
self.assertFalse(True, 'This should have raised an exception.')
|
self.assertFalse(True, 'This should have raised an exception.')
|
||||||
except Quota.QuotaExceededException:
|
except Quota.QuotaExceededException:
|
||||||
pass
|
pass
|
||||||
self.order = Order.objects.current.get(identity=self.order.identity)
|
self.order = Order.objects.get(id=self.order.id)
|
||||||
self.assertIn(self.order.status, (Order.STATUS_PENDING, Order.STATUS_EXPIRED))
|
self.assertIn(self.order.status, (Order.STATUS_PENDING, Order.STATUS_EXPIRED))
|
||||||
|
|
||||||
def test_paid_expired_unavailable_force(self):
|
def test_paid_expired_unavailable_force(self):
|
||||||
@@ -378,7 +361,7 @@ class OrderTestCase(BaseQuotaTestCase):
|
|||||||
self.quota.size = 0
|
self.quota.size = 0
|
||||||
self.quota.save()
|
self.quota.save()
|
||||||
mark_order_paid(self.order, force=True)
|
mark_order_paid(self.order, force=True)
|
||||||
self.order = Order.objects.current.get(identity=self.order.identity)
|
self.order = Order.objects.get(id=self.order.id)
|
||||||
self.assertEqual(self.order.status, Order.STATUS_PAID)
|
self.assertEqual(self.order.status, Order.STATUS_PAID)
|
||||||
|
|
||||||
def test_can_modify_answers(self):
|
def test_can_modify_answers(self):
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class SettingsTestCase(TestCase):
|
|||||||
self.assertEqual(self.organizer.settings.test, 'foo')
|
self.assertEqual(self.organizer.settings.test, 'foo')
|
||||||
|
|
||||||
# Reload object
|
# Reload object
|
||||||
self.organizer = Organizer.objects.get(identity=self.organizer.identity)
|
self.organizer = Organizer.objects.get(id=self.organizer.id)
|
||||||
self.assertEqual(self.organizer.settings.test, 'foo')
|
self.assertEqual(self.organizer.settings.test, 'foo')
|
||||||
|
|
||||||
def test_event_set_explicit(self):
|
def test_event_set_explicit(self):
|
||||||
@@ -37,7 +37,7 @@ class SettingsTestCase(TestCase):
|
|||||||
self.assertEqual(self.event.settings.test, 'foo')
|
self.assertEqual(self.event.settings.test, 'foo')
|
||||||
|
|
||||||
# Reload object
|
# Reload object
|
||||||
self.event = Event.objects.get(identity=self.event.identity)
|
self.event = Event.objects.get(id=self.event.id)
|
||||||
self.assertEqual(self.event.settings.test, 'foo')
|
self.assertEqual(self.event.settings.test, 'foo')
|
||||||
|
|
||||||
def test_event_set_twice(self):
|
def test_event_set_twice(self):
|
||||||
@@ -46,7 +46,7 @@ class SettingsTestCase(TestCase):
|
|||||||
self.assertEqual(self.event.settings.test, 'foo')
|
self.assertEqual(self.event.settings.test, 'foo')
|
||||||
|
|
||||||
# Reload object
|
# Reload object
|
||||||
self.event = Event.objects.get(identity=self.event.identity)
|
self.event = Event.objects.get(id=self.event.id)
|
||||||
self.assertEqual(self.event.settings.test, 'foo')
|
self.assertEqual(self.event.settings.test, 'foo')
|
||||||
|
|
||||||
def test_event_set_on_organizer(self):
|
def test_event_set_on_organizer(self):
|
||||||
@@ -55,18 +55,7 @@ class SettingsTestCase(TestCase):
|
|||||||
self.assertEqual(self.event.settings.test, 'foo')
|
self.assertEqual(self.event.settings.test, 'foo')
|
||||||
|
|
||||||
# Reload object
|
# Reload object
|
||||||
self.organizer = Organizer.objects.get(identity=self.organizer.identity)
|
self.organizer = Organizer.objects.get(id=self.organizer.id)
|
||||||
|
|
||||||
def test_versioning(self):
|
|
||||||
self.organizer.settings.test = 'foo'
|
|
||||||
t1 = now()
|
|
||||||
self.assertEqual(self.organizer.settings.test, 'foo')
|
|
||||||
self.assertEqual(self.event.settings.test, 'foo')
|
|
||||||
|
|
||||||
self.organizer.settings.test = 'bar'
|
|
||||||
|
|
||||||
assert OrganizerSetting.objects.as_of(t1).get(object=self.organizer, key='test').value == 'foo'
|
|
||||||
assert OrganizerSetting.objects.current.get(object=self.organizer, key='test').value == 'bar'
|
|
||||||
|
|
||||||
def test_override_organizer(self):
|
def test_override_organizer(self):
|
||||||
self.organizer.settings.test = 'foo'
|
self.organizer.settings.test = 'foo'
|
||||||
@@ -75,8 +64,8 @@ class SettingsTestCase(TestCase):
|
|||||||
self.assertEqual(self.event.settings.test, 'bar')
|
self.assertEqual(self.event.settings.test, 'bar')
|
||||||
|
|
||||||
# Reload object
|
# Reload object
|
||||||
self.organizer = Organizer.objects.get(identity=self.organizer.identity)
|
self.organizer = Organizer.objects.get(id=self.organizer.id)
|
||||||
self.event = Event.objects.get(identity=self.event.identity)
|
self.event = Event.objects.get(id=self.event.id)
|
||||||
self.assertEqual(self.organizer.settings.test, 'foo')
|
self.assertEqual(self.organizer.settings.test, 'foo')
|
||||||
self.assertEqual(self.event.settings.test, 'bar')
|
self.assertEqual(self.event.settings.test, 'bar')
|
||||||
|
|
||||||
@@ -103,13 +92,13 @@ class SettingsTestCase(TestCase):
|
|||||||
del self.event.settings.test
|
del self.event.settings.test
|
||||||
self.assertEqual(self.event.settings.test, 'foo')
|
self.assertEqual(self.event.settings.test, 'foo')
|
||||||
|
|
||||||
self.event = Event.objects.get(identity=self.event.identity)
|
self.event = Event.objects.get(id=self.event.id)
|
||||||
self.assertEqual(self.event.settings.test, 'foo')
|
self.assertEqual(self.event.settings.test, 'foo')
|
||||||
|
|
||||||
del self.organizer.settings.test
|
del self.organizer.settings.test
|
||||||
self.assertIsNone(self.organizer.settings.test)
|
self.assertIsNone(self.organizer.settings.test)
|
||||||
|
|
||||||
self.organizer = Organizer.objects.get(identity=self.organizer.identity)
|
self.organizer = Organizer.objects.get(id=self.organizer.id)
|
||||||
self.assertIsNone(self.organizer.settings.test)
|
self.assertIsNone(self.organizer.settings.test)
|
||||||
|
|
||||||
def test_serialize_str(self):
|
def test_serialize_str(self):
|
||||||
@@ -196,7 +185,7 @@ class SettingsTestCase(TestCase):
|
|||||||
sandbox['bar'] = 'baz'
|
sandbox['bar'] = 'baz'
|
||||||
sandbox.baz = 42
|
sandbox.baz = 42
|
||||||
|
|
||||||
self.event = Event.objects.get(identity=self.event.identity)
|
self.event = Event.objects.get(id=self.event.id)
|
||||||
sandbox = SettingsSandbox('testing', 'foo', self.event)
|
sandbox = SettingsSandbox('testing', 'foo', self.event)
|
||||||
self.assertEqual(sandbox['bar'], 'baz')
|
self.assertEqual(sandbox['bar'], 'baz')
|
||||||
self.assertEqual(sandbox.baz, '42')
|
self.assertEqual(sandbox.baz, '42')
|
||||||
|
|||||||
@@ -26,16 +26,9 @@ class EventsTest(BrowserTest):
|
|||||||
organizer=self.orga2, name='MRMCD14', slug='mrmcd14',
|
organizer=self.orga2, name='MRMCD14', slug='mrmcd14',
|
||||||
date_from=datetime.datetime(2014, 9, 5, tzinfo=datetime.timezone.utc),
|
date_from=datetime.datetime(2014, 9, 5, tzinfo=datetime.timezone.utc),
|
||||||
)
|
)
|
||||||
self.event4 = Event.objects.create(
|
|
||||||
organizer=self.orga2, name='MRMCD00', slug='mrmcd00',
|
|
||||||
date_from=datetime.datetime(2000, 9, 5, tzinfo=datetime.timezone.utc),
|
|
||||||
)
|
|
||||||
self.event4.delete()
|
|
||||||
OrganizerPermission.objects.create(organizer=self.orga1, user=self.user)
|
OrganizerPermission.objects.create(organizer=self.orga1, user=self.user)
|
||||||
EventPermission.objects.create(event=self.event1, user=self.user, can_change_items=True,
|
EventPermission.objects.create(event=self.event1, user=self.user, can_change_items=True,
|
||||||
can_change_settings=True)
|
can_change_settings=True)
|
||||||
EventPermission.objects.create(event=self.event4, user=self.user, can_change_items=True,
|
|
||||||
can_change_settings=True)
|
|
||||||
self.driver.implicitly_wait(10)
|
self.driver.implicitly_wait(10)
|
||||||
self.driver.get('%s%s' % (self.live_server_url, '/control/login'))
|
self.driver.get('%s%s' % (self.live_server_url, '/control/login'))
|
||||||
username_input = self.driver.find_element_by_name("email")
|
username_input = self.driver.find_element_by_name("email")
|
||||||
@@ -51,7 +44,6 @@ class EventsTest(BrowserTest):
|
|||||||
self.assertIn("30C3", tabletext)
|
self.assertIn("30C3", tabletext)
|
||||||
self.assertNotIn("31C3", tabletext)
|
self.assertNotIn("31C3", tabletext)
|
||||||
self.assertNotIn("MRMCD14", tabletext)
|
self.assertNotIn("MRMCD14", tabletext)
|
||||||
self.assertNotIn("MRMCD00", tabletext)
|
|
||||||
|
|
||||||
def test_settings(self):
|
def test_settings(self):
|
||||||
self.driver.get('%s/control/event/%s/%s/settings/' % (self.live_server_url, self.orga1.slug,
|
self.driver.get('%s/control/event/%s/%s/settings/' % (self.live_server_url, self.orga1.slug,
|
||||||
|
|||||||
@@ -49,9 +49,8 @@ class CategoriesTest(ItemFormTest):
|
|||||||
|
|
||||||
def test_update(self):
|
def test_update(self):
|
||||||
c = ItemCategory.objects.create(event=self.event1, name="Entry tickets")
|
c = ItemCategory.objects.create(event=self.event1, name="Entry tickets")
|
||||||
t1 = now()
|
|
||||||
self.driver.get('%s/control/event/%s/%s/categories/%s/' % (
|
self.driver.get('%s/control/event/%s/%s/categories/%s/' % (
|
||||||
self.live_server_url, self.orga1.slug, self.event1.slug, c.identity
|
self.live_server_url, self.orga1.slug, self.event1.slug, c.id
|
||||||
))
|
))
|
||||||
self.driver.find_element_by_name("name_0").clear()
|
self.driver.find_element_by_name("name_0").clear()
|
||||||
self.driver.find_element_by_name("name_0").send_keys('T-Shirts')
|
self.driver.find_element_by_name("name_0").send_keys('T-Shirts')
|
||||||
@@ -59,8 +58,7 @@ class CategoriesTest(ItemFormTest):
|
|||||||
self.driver.find_element_by_class_name("alert-success")
|
self.driver.find_element_by_class_name("alert-success")
|
||||||
self.assertIn("T-Shirts", self.driver.find_element_by_css_selector("#page-wrapper table").text)
|
self.assertIn("T-Shirts", self.driver.find_element_by_css_selector("#page-wrapper table").text)
|
||||||
self.assertNotIn("Entry tickets", self.driver.find_element_by_css_selector("#page-wrapper table").text)
|
self.assertNotIn("Entry tickets", self.driver.find_element_by_css_selector("#page-wrapper table").text)
|
||||||
assert str(ItemCategory.objects.as_of(t1).get(identity=c.identity).name) == 'Entry tickets'
|
assert str(ItemCategory.objects.get(id=c.id).name) == 'T-Shirts'
|
||||||
assert str(ItemCategory.objects.current.get(identity=c.identity).name) == 'T-Shirts'
|
|
||||||
|
|
||||||
@unittest.skipIf('TRAVIS' in os.environ, 'See docstring for details.')
|
@unittest.skipIf('TRAVIS' in os.environ, 'See docstring for details.')
|
||||||
def test_sort(self):
|
def test_sort(self):
|
||||||
@@ -98,15 +96,13 @@ class CategoriesTest(ItemFormTest):
|
|||||||
|
|
||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
c = ItemCategory.objects.create(event=self.event1, name="Entry tickets")
|
c = ItemCategory.objects.create(event=self.event1, name="Entry tickets")
|
||||||
t1 = now()
|
|
||||||
self.driver.get('%s/control/event/%s/%s/categories/%s/delete' % (
|
self.driver.get('%s/control/event/%s/%s/categories/%s/delete' % (
|
||||||
self.live_server_url, self.orga1.slug, self.event1.slug, c.identity
|
self.live_server_url, self.orga1.slug, self.event1.slug, c.id
|
||||||
))
|
))
|
||||||
self.driver.find_element_by_class_name("btn-danger").click()
|
self.driver.find_element_by_class_name("btn-danger").click()
|
||||||
self.driver.find_element_by_class_name("alert-success")
|
self.driver.find_element_by_class_name("alert-success")
|
||||||
self.assertNotIn("Entry tickets", self.driver.find_element_by_css_selector("#page-wrapper table").text)
|
self.assertNotIn("Entry tickets", self.driver.find_element_by_css_selector("#page-wrapper table").text)
|
||||||
assert ItemCategory.objects.as_of(t1).filter(identity=c.identity).exists()
|
assert not ItemCategory.objects.filter(id=c.id).exists()
|
||||||
assert not ItemCategory.objects.current.filter(identity=c.identity).exists()
|
|
||||||
|
|
||||||
|
|
||||||
class PropertiesTest(ItemFormTest):
|
class PropertiesTest(ItemFormTest):
|
||||||
@@ -134,9 +130,8 @@ class PropertiesTest(ItemFormTest):
|
|||||||
c = Property.objects.create(event=self.event1, name="Size")
|
c = Property.objects.create(event=self.event1, name="Size")
|
||||||
p1 = PropertyValue.objects.create(prop=c, position=0, value="S")
|
p1 = PropertyValue.objects.create(prop=c, position=0, value="S")
|
||||||
p2 = PropertyValue.objects.create(prop=c, position=1, value="M")
|
p2 = PropertyValue.objects.create(prop=c, position=1, value="M")
|
||||||
t1 = now()
|
|
||||||
self.driver.get('%s/control/event/%s/%s/properties/%s/' % (
|
self.driver.get('%s/control/event/%s/%s/properties/%s/' % (
|
||||||
self.live_server_url, self.orga1.slug, self.event1.slug, c.identity
|
self.live_server_url, self.orga1.slug, self.event1.slug, c.id
|
||||||
))
|
))
|
||||||
self.driver.find_element_by_css_selector("#id_name_0").clear()
|
self.driver.find_element_by_css_selector("#id_name_0").clear()
|
||||||
self.driver.find_element_by_css_selector("#id_name_0").send_keys('Color')
|
self.driver.find_element_by_css_selector("#id_name_0").send_keys('Color')
|
||||||
@@ -150,25 +145,20 @@ class PropertiesTest(ItemFormTest):
|
|||||||
self.driver.find_element_by_class_name("alert-success")
|
self.driver.find_element_by_class_name("alert-success")
|
||||||
self.assertEqual("red", self.driver.find_element_by_name("values-0-value_0").get_attribute("value"))
|
self.assertEqual("red", self.driver.find_element_by_name("values-0-value_0").get_attribute("value"))
|
||||||
self.assertEqual("blue", self.driver.find_element_by_name("values-1-value_0").get_attribute("value"))
|
self.assertEqual("blue", self.driver.find_element_by_name("values-1-value_0").get_attribute("value"))
|
||||||
assert str(Property.objects.current.get(identity=c.identity).name) == 'Color'
|
assert str(Property.objects.get(id=c.id).name) == 'Color'
|
||||||
assert str(PropertyValue.objects.as_of(t1).get(identity=p2.identity).value) == 'M'
|
assert str(PropertyValue.objects.get(id=p2.id).value) == 'red'
|
||||||
assert str(PropertyValue.objects.current.get(identity=p2.identity).value) == 'red'
|
assert not PropertyValue.objects.filter(id=p1.id).exists()
|
||||||
assert str(PropertyValue.objects.as_of(t1).get(identity=p1.identity).value) == 'S'
|
|
||||||
assert not PropertyValue.objects.current.filter(identity=p1.identity).exists()
|
|
||||||
assert str(Property.objects.as_of(t1).get(identity=c.identity).name) == 'Size'
|
|
||||||
|
|
||||||
@unittest.skip
|
@unittest.skip
|
||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
c = Property.objects.create(event=self.event1, name="Size")
|
c = Property.objects.create(event=self.event1, name="Size")
|
||||||
t1 = now()
|
|
||||||
self.driver.get('%s/control/event/%s/%s/properties/%s/delete' % (
|
self.driver.get('%s/control/event/%s/%s/properties/%s/delete' % (
|
||||||
self.live_server_url, self.orga1.slug, self.event1.slug, c.identity
|
self.live_server_url, self.orga1.slug, self.event1.slug, c.id
|
||||||
))
|
))
|
||||||
self.driver.find_element_by_class_name("btn-danger").click()
|
self.driver.find_element_by_class_name("btn-danger").click()
|
||||||
self.driver.find_element_by_class_name("alert-success")
|
self.driver.find_element_by_class_name("alert-success")
|
||||||
self.assertNotIn("Size", self.driver.find_element_by_css_selector("#page-wrapper table").text)
|
self.assertNotIn("Size", self.driver.find_element_by_css_selector("#page-wrapper table").text)
|
||||||
assert Property.objects.as_of(t1).filter(identity=c.identity).exists()
|
assert not Property.objects.filter(id=c.id).exists()
|
||||||
assert not Property.objects.current.filter(identity=c.identity).exists()
|
|
||||||
|
|
||||||
|
|
||||||
class QuestionsTest(ItemFormTest):
|
class QuestionsTest(ItemFormTest):
|
||||||
@@ -185,9 +175,8 @@ class QuestionsTest(ItemFormTest):
|
|||||||
|
|
||||||
def test_update(self):
|
def test_update(self):
|
||||||
c = Question.objects.create(event=self.event1, question="What is your shoe size?", type="N", required=True)
|
c = Question.objects.create(event=self.event1, question="What is your shoe size?", type="N", required=True)
|
||||||
t1 = now()
|
|
||||||
self.driver.get('%s/control/event/%s/%s/questions/%s/' % (
|
self.driver.get('%s/control/event/%s/%s/questions/%s/' % (
|
||||||
self.live_server_url, self.orga1.slug, self.event1.slug, c.identity
|
self.live_server_url, self.orga1.slug, self.event1.slug, c.id
|
||||||
))
|
))
|
||||||
self.driver.find_element_by_name("question_0").clear()
|
self.driver.find_element_by_name("question_0").clear()
|
||||||
self.driver.find_element_by_name("question_0").send_keys('How old are you?')
|
self.driver.find_element_by_name("question_0").send_keys('How old are you?')
|
||||||
@@ -195,22 +184,19 @@ class QuestionsTest(ItemFormTest):
|
|||||||
self.driver.find_element_by_class_name("alert-success")
|
self.driver.find_element_by_class_name("alert-success")
|
||||||
self.assertIn("How old", self.driver.find_element_by_css_selector("#page-wrapper table").text)
|
self.assertIn("How old", self.driver.find_element_by_css_selector("#page-wrapper table").text)
|
||||||
self.assertNotIn("shoe size", self.driver.find_element_by_css_selector("#page-wrapper table").text)
|
self.assertNotIn("shoe size", self.driver.find_element_by_css_selector("#page-wrapper table").text)
|
||||||
c = Question.objects.current.get(identity=c.identity)
|
c = Question.objects.get(id=c.id)
|
||||||
self.assertTrue(c.required)
|
self.assertTrue(c.required)
|
||||||
assert str(Question.objects.current.get(identity=c.identity).question) == 'How old are you?'
|
assert str(Question.objects.get(id=c.id).question) == 'How old are you?'
|
||||||
assert str(Question.objects.as_of(t1).get(identity=c.identity).question) == 'What is your shoe size?'
|
|
||||||
|
|
||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
c = Question.objects.create(event=self.event1, question="What is your shoe size?", type="N", required=True)
|
c = Question.objects.create(event=self.event1, question="What is your shoe size?", type="N", required=True)
|
||||||
t1 = now()
|
|
||||||
self.driver.get('%s/control/event/%s/%s/questions/%s/delete' % (
|
self.driver.get('%s/control/event/%s/%s/questions/%s/delete' % (
|
||||||
self.live_server_url, self.orga1.slug, self.event1.slug, c.identity
|
self.live_server_url, self.orga1.slug, self.event1.slug, c.id
|
||||||
))
|
))
|
||||||
self.driver.find_element_by_class_name("btn-danger").click()
|
self.driver.find_element_by_class_name("btn-danger").click()
|
||||||
self.driver.find_element_by_class_name("alert-success")
|
self.driver.find_element_by_class_name("alert-success")
|
||||||
self.assertNotIn("shoe size", self.driver.find_element_by_css_selector("#page-wrapper table").text)
|
self.assertNotIn("shoe size", self.driver.find_element_by_css_selector("#page-wrapper table").text)
|
||||||
assert Question.objects.as_of(t1).filter(identity=c.identity).exists()
|
assert not Question.objects.filter(id=c.id).exists()
|
||||||
assert not Question.objects.current.filter(identity=c.identity).exists()
|
|
||||||
|
|
||||||
|
|
||||||
class QuotaTest(ItemFormTest):
|
class QuotaTest(ItemFormTest):
|
||||||
@@ -232,36 +218,31 @@ class QuotaTest(ItemFormTest):
|
|||||||
prop1 = Property.objects.create(event=self.event1, name="Level", item=item2)
|
prop1 = Property.objects.create(event=self.event1, name="Level", item=item2)
|
||||||
PropertyValue.objects.create(prop=prop1, value="Silver")
|
PropertyValue.objects.create(prop=prop1, value="Silver")
|
||||||
PropertyValue.objects.create(prop=prop1, value="Gold")
|
PropertyValue.objects.create(prop=prop1, value="Gold")
|
||||||
t1 = now()
|
|
||||||
self.driver.get('%s/control/event/%s/%s/quotas/%s/' % (
|
self.driver.get('%s/control/event/%s/%s/quotas/%s/' % (
|
||||||
self.live_server_url, self.orga1.slug, self.event1.slug, c.identity
|
self.live_server_url, self.orga1.slug, self.event1.slug, c.id
|
||||||
))
|
))
|
||||||
self.driver.find_element_by_name("size").clear()
|
self.driver.find_element_by_name("size").clear()
|
||||||
self.driver.find_element_by_name("size").send_keys('350')
|
self.driver.find_element_by_name("size").send_keys('350')
|
||||||
# self.scroll_and_click(self.driver.find_element_by_css_selector('.panel-group .panel:nth-child(1)
|
# self.scroll_and_click(self.driver.find_element_by_css_selector('.panel-group .panel:nth-child(1)
|
||||||
# .panel-title a'))
|
# .panel-title a'))
|
||||||
# time.sleep(1)
|
# time.sleep(1)
|
||||||
self.scroll_and_click(self.driver.find_element_by_name("item_%s" % item1.identity))
|
self.scroll_and_click(self.driver.find_element_by_name("item_%s" % item1.id))
|
||||||
# self.driver.find_element_by_css_selector('.panel-group .panel:nth-child(2) .panel-title a').click()
|
# self.driver.find_element_by_css_selector('.panel-group .panel:nth-child(2) .panel-title a').click()
|
||||||
# time.sleep(1)
|
# time.sleep(1)
|
||||||
self.scroll_and_click(self.driver.find_elements_by_css_selector("input[name=item_%s]" % item2.identity)[1])
|
self.scroll_and_click(self.driver.find_elements_by_css_selector("input[name=item_%s]" % item2.id)[1])
|
||||||
self.scroll_and_click(self.driver.find_element_by_class_name("btn-save"))
|
self.scroll_and_click(self.driver.find_element_by_class_name("btn-save"))
|
||||||
self.driver.find_element_by_class_name("alert-success")
|
self.driver.find_element_by_class_name("alert-success")
|
||||||
self.assertIn("350", self.driver.find_element_by_css_selector("#page-wrapper table").text)
|
self.assertIn("350", self.driver.find_element_by_css_selector("#page-wrapper table").text)
|
||||||
self.assertNotIn("500", self.driver.find_element_by_css_selector("#page-wrapper table").text)
|
self.assertNotIn("500", self.driver.find_element_by_css_selector("#page-wrapper table").text)
|
||||||
assert Quota.objects.current.get(identity=c.identity).size == 350
|
assert Quota.objects.get(id=c.id).size == 350
|
||||||
assert Quota.objects.as_of(t1).get(identity=c.identity).size == 500
|
assert item1 in Quota.objects.get(id=c.id).items.all()
|
||||||
assert item1 in Quota.objects.current.get(identity=c.identity).items.all()
|
|
||||||
assert item1 not in Quota.objects.as_of(t1).get(identity=c.identity).items.all()
|
|
||||||
|
|
||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
c = Quota.objects.create(event=self.event1, name="Full house", size=500)
|
c = Quota.objects.create(event=self.event1, name="Full house", size=500)
|
||||||
t1 = now()
|
|
||||||
self.driver.get('%s/control/event/%s/%s/quotas/%s/delete' % (
|
self.driver.get('%s/control/event/%s/%s/quotas/%s/delete' % (
|
||||||
self.live_server_url, self.orga1.slug, self.event1.slug, c.identity
|
self.live_server_url, self.orga1.slug, self.event1.slug, c.id
|
||||||
))
|
))
|
||||||
self.driver.find_element_by_class_name("btn-danger").click()
|
self.driver.find_element_by_class_name("btn-danger").click()
|
||||||
self.driver.find_element_by_class_name("alert-success")
|
self.driver.find_element_by_class_name("alert-success")
|
||||||
self.assertNotIn("Full house", self.driver.find_element_by_css_selector("#page-wrapper table").text)
|
self.assertNotIn("Full house", self.driver.find_element_by_css_selector("#page-wrapper table").text)
|
||||||
assert Quota.objects.as_of(t1).filter(identity=c.identity).exists()
|
assert not Quota.objects.filter(id=c.id).exists()
|
||||||
assert not Quota.objects.current.filter(identity=c.identity).exists()
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ def test_order_list(client, env):
|
|||||||
assert 'FOO' in response.rendered_content
|
assert 'FOO' in response.rendered_content
|
||||||
response = client.get('/control/event/dummy/dummy/orders/?item=15')
|
response = client.get('/control/event/dummy/dummy/orders/?item=15')
|
||||||
assert 'FOO' not in response.rendered_content
|
assert 'FOO' not in response.rendered_content
|
||||||
response = client.get('/control/event/dummy/dummy/orders/?item=%s' % env[3].identity)
|
response = client.get('/control/event/dummy/dummy/orders/?item=%s' % env[3].id)
|
||||||
assert 'FOO' in response.rendered_content
|
assert 'FOO' in response.rendered_content
|
||||||
|
|
||||||
|
|
||||||
@@ -80,13 +80,13 @@ def test_order_transition_to_paid_in_time_success(client, env):
|
|||||||
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||||
'status': 'p'
|
'status': 'p'
|
||||||
})
|
})
|
||||||
o = Order.objects.current.get(identity=env[2].identity)
|
o = Order.objects.get(id=env[2].id)
|
||||||
assert o.status == Order.STATUS_PAID
|
assert o.status == Order.STATUS_PAID
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_order_transition_to_paid_expired_quota_left(client, env):
|
def test_order_transition_to_paid_expired_quota_left(client, env):
|
||||||
o = Order.objects.current.get(identity=env[2].identity)
|
o = Order.objects.get(id=env[2].id)
|
||||||
o.expires = now() - timedelta(days=2)
|
o.expires = now() - timedelta(days=2)
|
||||||
o.save()
|
o.save()
|
||||||
q = Quota.objects.create(event=env[0], size=10)
|
q = Quota.objects.create(event=env[0], size=10)
|
||||||
@@ -95,13 +95,13 @@ def test_order_transition_to_paid_expired_quota_left(client, env):
|
|||||||
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||||
'status': 'p'
|
'status': 'p'
|
||||||
})
|
})
|
||||||
o = Order.objects.current.get(identity=env[2].identity)
|
o = Order.objects.get(id=env[2].id)
|
||||||
assert o.status == Order.STATUS_PAID
|
assert o.status == Order.STATUS_PAID
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_order_transition_to_paid_expired_quota_full(client, env):
|
def test_order_transition_to_paid_expired_quota_full(client, env):
|
||||||
o = Order.objects.current.get(identity=env[2].identity)
|
o = Order.objects.get(id=env[2].id)
|
||||||
o.expires = now() - timedelta(days=2)
|
o.expires = now() - timedelta(days=2)
|
||||||
o.save()
|
o.save()
|
||||||
q = Quota.objects.create(event=env[0], size=0)
|
q = Quota.objects.create(event=env[0], size=0)
|
||||||
@@ -110,7 +110,7 @@ def test_order_transition_to_paid_expired_quota_full(client, env):
|
|||||||
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||||
'status': 'p'
|
'status': 'p'
|
||||||
})
|
})
|
||||||
o = Order.objects.current.get(identity=env[2].identity)
|
o = Order.objects.get(id=env[2].id)
|
||||||
assert o.status == Order.STATUS_PENDING
|
assert o.status == Order.STATUS_PENDING
|
||||||
|
|
||||||
|
|
||||||
@@ -134,14 +134,14 @@ def test_order_transition_to_paid_expired_quota_full(client, env):
|
|||||||
(Order.STATUS_REFUNDED, Order.STATUS_PENDING, False)
|
(Order.STATUS_REFUNDED, Order.STATUS_PENDING, False)
|
||||||
])
|
])
|
||||||
def test_order_transition(client, env, process):
|
def test_order_transition(client, env, process):
|
||||||
o = Order.objects.current.get(identity=env[2].identity)
|
o = Order.objects.get(id=env[2].id)
|
||||||
o.status = process[0]
|
o.status = process[0]
|
||||||
o.save()
|
o.save()
|
||||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||||
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||||
'status': process[1]
|
'status': process[1]
|
||||||
})
|
})
|
||||||
o = Order.objects.current.get(identity=env[2].identity)
|
o = Order.objects.get(id=env[2].id)
|
||||||
if process[2]:
|
if process[2]:
|
||||||
assert o.status == process[1]
|
assert o.status == process[1]
|
||||||
else:
|
else:
|
||||||
@@ -150,7 +150,7 @@ def test_order_transition(client, env, process):
|
|||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_order_detail_download_buttons_hidden_if_not_paid(client, env):
|
def test_order_detail_download_buttons_hidden_if_not_paid(client, env):
|
||||||
o = Order.objects.current.get(identity=env[2].identity)
|
o = Order.objects.get(id=env[2].id)
|
||||||
o.status = Order.STATUS_PENDING
|
o.status = Order.STATUS_PENDING
|
||||||
o.save()
|
o.save()
|
||||||
env[0].settings.set('ticket_download', True)
|
env[0].settings.set('ticket_download', True)
|
||||||
@@ -163,7 +163,7 @@ def test_order_detail_download_buttons_hidden_if_not_paid(client, env):
|
|||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_order_detail_download_buttons_visible(client, env):
|
def test_order_detail_download_buttons_visible(client, env):
|
||||||
o = Order.objects.current.get(identity=env[2].identity)
|
o = Order.objects.get(id=env[2].id)
|
||||||
o.status = Order.STATUS_PAID
|
o.status = Order.STATUS_PAID
|
||||||
o.save()
|
o.save()
|
||||||
env[0].settings.set('ticket_download', True)
|
env[0].settings.set('ticket_download', True)
|
||||||
@@ -176,7 +176,7 @@ def test_order_detail_download_buttons_visible(client, env):
|
|||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_order_detail_download_buttons_hidden_of(client, env):
|
def test_order_detail_download_buttons_hidden_of(client, env):
|
||||||
o = Order.objects.current.get(identity=env[2].identity)
|
o = Order.objects.get(id=env[2].id)
|
||||||
o.status = Order.STATUS_PAID
|
o.status = Order.STATUS_PAID
|
||||||
o.save()
|
o.save()
|
||||||
env[0].settings.set('ticket_download', False)
|
env[0].settings.set('ticket_download', False)
|
||||||
@@ -189,7 +189,7 @@ def test_order_detail_download_buttons_hidden_of(client, env):
|
|||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_order_detail_download_buttons_visible_before_date(client, env):
|
def test_order_detail_download_buttons_visible_before_date(client, env):
|
||||||
o = Order.objects.current.get(identity=env[2].identity)
|
o = Order.objects.get(id=env[2].id)
|
||||||
o.status = Order.STATUS_PAID
|
o.status = Order.STATUS_PAID
|
||||||
o.save()
|
o.save()
|
||||||
env[0].settings.set('ticket_download', True)
|
env[0].settings.set('ticket_download', True)
|
||||||
@@ -202,7 +202,7 @@ def test_order_detail_download_buttons_visible_before_date(client, env):
|
|||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_order_detail_download_buttons_hidden_if_provider_disabled(client, env):
|
def test_order_detail_download_buttons_hidden_if_provider_disabled(client, env):
|
||||||
o = Order.objects.current.get(identity=env[2].identity)
|
o = Order.objects.get(id=env[2].id)
|
||||||
o.status = Order.STATUS_PAID
|
o.status = Order.STATUS_PAID
|
||||||
o.save()
|
o.save()
|
||||||
env[0].settings.set('ticket_download', True)
|
env[0].settings.set('ticket_download', True)
|
||||||
@@ -225,7 +225,7 @@ def test_order_download_unpaid(client, env):
|
|||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_order_download_unknown_provider(client, env):
|
def test_order_download_unknown_provider(client, env):
|
||||||
o = Order.objects.current.get(identity=env[2].identity)
|
o = Order.objects.get(id=env[2].id)
|
||||||
o.status = Order.STATUS_PAID
|
o.status = Order.STATUS_PAID
|
||||||
o.save()
|
o.save()
|
||||||
env[0].settings.set('ticket_download', True)
|
env[0].settings.set('ticket_download', True)
|
||||||
@@ -237,7 +237,7 @@ def test_order_download_unknown_provider(client, env):
|
|||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_order_download_disabled_provider(client, env):
|
def test_order_download_disabled_provider(client, env):
|
||||||
o = Order.objects.current.get(identity=env[2].identity)
|
o = Order.objects.get(id=env[2].id)
|
||||||
o.status = Order.STATUS_PAID
|
o.status = Order.STATUS_PAID
|
||||||
o.save()
|
o.save()
|
||||||
env[0].settings.set('ticket_download', True)
|
env[0].settings.set('ticket_download', True)
|
||||||
@@ -252,7 +252,7 @@ def test_order_download_disabled_provider(client, env):
|
|||||||
def test_order_download_success(client, env, mocker):
|
def test_order_download_success(client, env, mocker):
|
||||||
from pretix.base.services import tickets
|
from pretix.base.services import tickets
|
||||||
mocker.patch('pretix.base.services.tickets.generate')
|
mocker.patch('pretix.base.services.tickets.generate')
|
||||||
o = Order.objects.current.get(identity=env[2].identity)
|
o = Order.objects.get(id=env[2].id)
|
||||||
o.status = Order.STATUS_PAID
|
o.status = Order.STATUS_PAID
|
||||||
o.save()
|
o.save()
|
||||||
env[0].settings.set('ticket_download', True)
|
env[0].settings.set('ticket_download', True)
|
||||||
@@ -261,7 +261,7 @@ def test_order_download_success(client, env, mocker):
|
|||||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||||
response = client.get('/control/event/dummy/dummy/orders/FOO/download/testdummy')
|
response = client.get('/control/event/dummy/dummy/orders/FOO/download/testdummy')
|
||||||
assert response.status_code == 302
|
assert response.status_code == 302
|
||||||
tickets.generate.assert_any_call(o.identity, 'testdummy')
|
tickets.generate.assert_any_call(o.id, 'testdummy')
|
||||||
assert 'download' in response['Location']
|
assert 'download' in response['Location']
|
||||||
dl = response['Location']
|
dl = response['Location']
|
||||||
assert CachedTicket.objects.filter(order=o, provider='testdummy').exists()
|
assert CachedTicket.objects.filter(order=o, provider='testdummy').exists()
|
||||||
@@ -276,7 +276,7 @@ def test_order_download_success(client, env, mocker):
|
|||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_order_extend_not_pending(client, env):
|
def test_order_extend_not_pending(client, env):
|
||||||
o = Order.objects.current.get(identity=env[2].identity)
|
o = Order.objects.get(id=env[2].id)
|
||||||
o.status = Order.STATUS_PAID
|
o.status = Order.STATUS_PAID
|
||||||
o.save()
|
o.save()
|
||||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||||
@@ -296,13 +296,13 @@ def test_order_extend_not_expired(client, env):
|
|||||||
'expires': newdate
|
'expires': newdate
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
assert 'alert-success' in response.rendered_content
|
assert 'alert-success' in response.rendered_content
|
||||||
o = Order.objects.current.get(identity=env[2].identity)
|
o = Order.objects.get(id=env[2].id)
|
||||||
assert o.expires.strftime("%Y-%m-%d %H:%M:%S") == newdate
|
assert o.expires.strftime("%Y-%m-%d %H:%M:%S") == newdate
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_order_extend_expired_quota_left(client, env):
|
def test_order_extend_expired_quota_left(client, env):
|
||||||
o = Order.objects.current.get(identity=env[2].identity)
|
o = Order.objects.get(id=env[2].id)
|
||||||
o.expires = now() - timedelta(days=5)
|
o.expires = now() - timedelta(days=5)
|
||||||
o.save()
|
o.save()
|
||||||
q = Quota.objects.create(event=env[0], size=3)
|
q = Quota.objects.create(event=env[0], size=3)
|
||||||
@@ -313,13 +313,13 @@ def test_order_extend_expired_quota_left(client, env):
|
|||||||
'expires': newdate
|
'expires': newdate
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
assert 'alert-success' in response.rendered_content
|
assert 'alert-success' in response.rendered_content
|
||||||
o = Order.objects.current.get(identity=env[2].identity)
|
o = Order.objects.get(id=env[2].id)
|
||||||
assert o.expires.strftime("%Y-%m-%d %H:%M:%S") == newdate
|
assert o.expires.strftime("%Y-%m-%d %H:%M:%S") == newdate
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_order_extend_expired_quota_empty(client, env):
|
def test_order_extend_expired_quota_empty(client, env):
|
||||||
o = Order.objects.current.get(identity=env[2].identity)
|
o = Order.objects.get(id=env[2].id)
|
||||||
o.expires = now() - timedelta(days=5)
|
o.expires = now() - timedelta(days=5)
|
||||||
olddate = o.expires
|
olddate = o.expires
|
||||||
o.save()
|
o.save()
|
||||||
@@ -331,7 +331,7 @@ def test_order_extend_expired_quota_empty(client, env):
|
|||||||
'expires': newdate
|
'expires': newdate
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
assert 'alert-danger' in response.rendered_content
|
assert 'alert-danger' in response.rendered_content
|
||||||
o = Order.objects.current.get(identity=env[2].identity)
|
o = Order.objects.get(id=env[2].id)
|
||||||
assert o.expires.strftime("%Y-%m-%d %H:%M:%S") == olddate.strftime("%Y-%m-%d %H:%M:%S")
|
assert o.expires.strftime("%Y-%m-%d %H:%M:%S") == olddate.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -33,22 +33,22 @@ event_urls = [
|
|||||||
"settings/tickets",
|
"settings/tickets",
|
||||||
"items/",
|
"items/",
|
||||||
"items/add",
|
"items/add",
|
||||||
"items/abc/",
|
"items/1/",
|
||||||
"items/abc/variations",
|
"items/1/variations",
|
||||||
"items/abc/properties",
|
"items/1/properties",
|
||||||
"categories/",
|
"categories/",
|
||||||
"categories/add",
|
"categories/add",
|
||||||
"categories/abc/",
|
"categories/2/",
|
||||||
"categories/abc/up",
|
"categories/2/up",
|
||||||
"categories/abc/down",
|
"categories/2/down",
|
||||||
"categories/abc/delete",
|
"categories/2/delete",
|
||||||
"questions/",
|
"questions/",
|
||||||
"questions/abc/delete",
|
"questions/2/delete",
|
||||||
"questions/abc/",
|
"questions/2/",
|
||||||
"questions/add",
|
"questions/add",
|
||||||
"quotas/",
|
"quotas/",
|
||||||
"quotas/abc/delete",
|
"quotas/2/delete",
|
||||||
"quotas/abc/",
|
"quotas/2/",
|
||||||
"quotas/add",
|
"quotas/add",
|
||||||
"orders/ABC/transition",
|
"orders/ABC/transition",
|
||||||
"orders/ABC/extend",
|
"orders/ABC/extend",
|
||||||
@@ -56,7 +56,6 @@ event_urls = [
|
|||||||
"orders/",
|
"orders/",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
organizer_urls = [
|
organizer_urls = [
|
||||||
'organizer/abc/edit',
|
'organizer/abc/edit',
|
||||||
'event/abc/add'
|
'event/abc/add'
|
||||||
@@ -101,16 +100,16 @@ event_permission_urls = [
|
|||||||
# We don't have to create categories and similar objects
|
# We don't have to create categories and similar objects
|
||||||
# for testing this, it is enough to test that a 404 error
|
# for testing this, it is enough to test that a 404 error
|
||||||
# is returned instead of a 403 one.
|
# is returned instead of a 403 one.
|
||||||
("can_change_items", "categories/abc/", 404),
|
("can_change_items", "categories/2/", 404),
|
||||||
("can_change_items", "categories/abc/delete", 404),
|
("can_change_items", "categories/2/delete", 404),
|
||||||
("can_change_items", "categories/add", 200),
|
("can_change_items", "categories/add", 200),
|
||||||
# ("can_change_items", "questions/", 200),
|
# ("can_change_items", "questions/", 200),
|
||||||
("can_change_items", "questions/abc/", 404),
|
("can_change_items", "questions/2/", 404),
|
||||||
("can_change_items", "questions/abc/delete", 404),
|
("can_change_items", "questions/2/delete", 404),
|
||||||
("can_change_items", "questions/add", 200),
|
("can_change_items", "questions/add", 200),
|
||||||
# ("can_change_items", "quotas/", 200),
|
# ("can_change_items", "quotas/", 200),
|
||||||
("can_change_items", "quotas/abc/", 404),
|
("can_change_items", "quotas/2/", 404),
|
||||||
("can_change_items", "quotas/abc/delete", 404),
|
("can_change_items", "quotas/2/delete", 404),
|
||||||
("can_change_items", "quotas/add", 200),
|
("can_change_items", "quotas/add", 200),
|
||||||
("can_view_orders", "orders/overview/", 200),
|
("can_view_orders", "orders/overview/", 200),
|
||||||
("can_view_orders", "orders/", 200),
|
("can_view_orders", "orders/", 200),
|
||||||
@@ -143,7 +142,6 @@ def test_current_permission(client, env):
|
|||||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||||
response = client.get('/control/event/dummy/dummy/settings/')
|
response = client.get('/control/event/dummy/dummy/settings/')
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
ep = ep.clone()
|
|
||||||
setattr(ep, 'can_change_settings', False)
|
setattr(ep, 'can_change_settings', False)
|
||||||
ep.save()
|
ep.save()
|
||||||
response = client.get('/control/event/dummy/dummy/settings/')
|
response = client.get('/control/event/dummy/dummy/settings/')
|
||||||
|
|||||||
@@ -105,5 +105,5 @@ Buchungstag;Valuta;Buchungstext;Auftraggeber / Empfänger;Verwendungszweck;Betra
|
|||||||
data[inp.attrs['name']] = inp.attrs['value']
|
data[inp.attrs['name']] = inp.attrs['value']
|
||||||
client.post('/control/event/dummy/dummy/banktransfer/import/', data)
|
client.post('/control/event/dummy/dummy/banktransfer/import/', data)
|
||||||
|
|
||||||
assert Order.objects.current.get(identity=env[2].identity).status == Order.STATUS_PAID
|
assert Order.objects.get(id=env[2].id).status == Order.STATUS_PAID
|
||||||
assert Order.objects.current.get(identity=env[3].identity).status == Order.STATUS_CANCELLED
|
assert Order.objects.get(id=env[3].id).status == Order.STATUS_CANCELLED
|
||||||
|
|||||||
@@ -68,5 +68,5 @@ def test_pretixdroid(client, env):
|
|||||||
jdata = json.loads(response.content.decode("utf-8"))
|
jdata = json.loads(response.content.decode("utf-8"))
|
||||||
assert jdata['version'] == 1
|
assert jdata['version'] == 1
|
||||||
assert len(jdata['data']) == 2
|
assert len(jdata['data']) == 2
|
||||||
assert {"id": env[3].identity, "variation": "Red", "attendee_name": None, "item": "T-Shirt"} in jdata['data']
|
assert {"id": env[3].id, "variation": "Red", "attendee_name": None, "item": "T-Shirt"} in jdata['data']
|
||||||
assert {"id": env[4].identity, "variation": None, "attendee_name": "Peter", "item": "Ticket"} in jdata['data']
|
assert {"id": env[4].id, "variation": None, "attendee_name": "Peter", "item": "Ticket"} in jdata['data']
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class CartTestMixin:
|
|||||||
class CartTest(CartTestMixin, TestCase):
|
class CartTest(CartTestMixin, TestCase):
|
||||||
def test_simple(self):
|
def test_simple(self):
|
||||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||||
'item_' + self.ticket.identity: '1'
|
'item_%d' % self.ticket.id: '1'
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
||||||
target_status_code=200)
|
target_status_code=200)
|
||||||
@@ -62,7 +62,7 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
|
|
||||||
def test_variation(self):
|
def test_variation(self):
|
||||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||||
'variation_' + self.shirt.identity + '_' + self.shirt_red.identity: '1'
|
'variation_%d_%d' % (self.shirt.id, self.shirt_red.id): '1'
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
||||||
target_status_code=200)
|
target_status_code=200)
|
||||||
@@ -80,7 +80,7 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
|
|
||||||
def test_count(self):
|
def test_count(self):
|
||||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||||
'item_' + self.ticket.identity: '2'
|
'item_%d' % self.ticket.id: '2'
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
||||||
target_status_code=200)
|
target_status_code=200)
|
||||||
@@ -98,8 +98,8 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
|
|
||||||
def test_multiple(self):
|
def test_multiple(self):
|
||||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||||
'item_' + self.ticket.identity: '2',
|
'item_%d' % self.ticket.id: '2',
|
||||||
'variation_' + self.shirt.identity + '_' + self.shirt_red.identity: '1'
|
'variation_%d_%d' % (self.shirt.id, self.shirt_red.id): '1'
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
||||||
target_status_code=200)
|
target_status_code=200)
|
||||||
@@ -114,7 +114,7 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
|
|
||||||
def test_fuzzy_input(self):
|
def test_fuzzy_input(self):
|
||||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||||
'item_' + self.ticket.identity: 'a',
|
'item_%d' % self.ticket.id: 'a',
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
||||||
target_status_code=200)
|
target_status_code=200)
|
||||||
@@ -137,7 +137,7 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
)
|
)
|
||||||
shirt2 = Item.objects.create(event=event2, name='T-Shirt', default_price=12)
|
shirt2 = Item.objects.create(event=event2, name='T-Shirt', default_price=12)
|
||||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||||
'item_' + shirt2.identity: '1',
|
'item_%d' % shirt2.id: '1',
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
||||||
target_status_code=200)
|
target_status_code=200)
|
||||||
@@ -148,7 +148,7 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
def test_no_quota(self):
|
def test_no_quota(self):
|
||||||
shirt2 = Item.objects.create(event=self.event, name='T-Shirt', default_price=12)
|
shirt2 = Item.objects.create(event=self.event, name='T-Shirt', default_price=12)
|
||||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||||
'item_' + shirt2.identity: '1',
|
'item_%d' % shirt2.id: '1',
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
||||||
target_status_code=200)
|
target_status_code=200)
|
||||||
@@ -161,7 +161,7 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
self.ticket.available_from = now() - timedelta(days=2)
|
self.ticket.available_from = now() - timedelta(days=2)
|
||||||
self.ticket.save()
|
self.ticket.save()
|
||||||
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||||
'item_' + self.ticket.identity: '1',
|
'item_%d' % self.ticket.id: '1',
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
self.assertEqual(CartPosition.objects.filter(cart_id=self.session_key, event=self.event).count(), 1)
|
self.assertEqual(CartPosition.objects.filter(cart_id=self.session_key, event=self.event).count(), 1)
|
||||||
|
|
||||||
@@ -169,7 +169,7 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
self.ticket.available_until = now() - timedelta(days=2)
|
self.ticket.available_until = now() - timedelta(days=2)
|
||||||
self.ticket.save()
|
self.ticket.save()
|
||||||
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||||
'item_' + self.ticket.identity: '1',
|
'item_%d' % self.ticket.id: '1',
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
self.assertEqual(CartPosition.objects.filter(cart_id=self.session_key, event=self.event).count(), 0)
|
self.assertEqual(CartPosition.objects.filter(cart_id=self.session_key, event=self.event).count(), 0)
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
self.ticket.available_from = now() + timedelta(days=2)
|
self.ticket.available_from = now() + timedelta(days=2)
|
||||||
self.ticket.save()
|
self.ticket.save()
|
||||||
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||||
'item_' + self.ticket.identity: '1',
|
'item_%d' % self.ticket.id: '1',
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
self.assertEqual(CartPosition.objects.filter(cart_id=self.session_key, event=self.event).count(), 0)
|
self.assertEqual(CartPosition.objects.filter(cart_id=self.session_key, event=self.event).count(), 0)
|
||||||
|
|
||||||
@@ -188,7 +188,7 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
)
|
)
|
||||||
self.event.settings.max_items_per_order = 5
|
self.event.settings.max_items_per_order = 5
|
||||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||||
'item_' + self.ticket.identity: '5',
|
'item_%d' % self.ticket.id: '5',
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
||||||
target_status_code=200)
|
target_status_code=200)
|
||||||
@@ -200,7 +200,7 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
self.quota_tickets.size = 0
|
self.quota_tickets.size = 0
|
||||||
self.quota_tickets.save()
|
self.quota_tickets.save()
|
||||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||||
'item_' + self.ticket.identity: '1',
|
'item_%d' % self.ticket.id: '1',
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
||||||
target_status_code=200)
|
target_status_code=200)
|
||||||
@@ -212,7 +212,7 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
self.quota_tickets.size = 1
|
self.quota_tickets.size = 1
|
||||||
self.quota_tickets.save()
|
self.quota_tickets.save()
|
||||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||||
'item_' + self.ticket.identity: '2'
|
'item_%d' % self.ticket.id: '2'
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
||||||
target_status_code=200)
|
target_status_code=200)
|
||||||
@@ -235,7 +235,7 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
)
|
)
|
||||||
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
cp = CartPosition.objects.current.get(identity=cp.identity)
|
cp = CartPosition.objects.get(id=cp.id)
|
||||||
self.assertGreater(cp.expires, now())
|
self.assertGreater(cp.expires, now())
|
||||||
|
|
||||||
def test_renew_expired_successfully(self):
|
def test_renew_expired_successfully(self):
|
||||||
@@ -244,9 +244,9 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
price=23, expires=now() - timedelta(minutes=10)
|
price=23, expires=now() - timedelta(minutes=10)
|
||||||
)
|
)
|
||||||
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||||
'variation_%s_%s' % (self.shirt.identity, self.shirt_red.identity): '1'
|
'variation_%d_%d' % (self.shirt.id, self.shirt_red.id): '1'
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
obj = CartPosition.objects.current.get(identity=cp1.identity)
|
obj = CartPosition.objects.get(id=cp1.id)
|
||||||
self.assertEqual(obj.item, self.ticket)
|
self.assertEqual(obj.item, self.ticket)
|
||||||
self.assertIsNone(obj.variation)
|
self.assertIsNone(obj.variation)
|
||||||
self.assertEqual(obj.price, 23)
|
self.assertEqual(obj.price, 23)
|
||||||
@@ -266,9 +266,9 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
cartposition=cr1, question=q1, answer='23'
|
cartposition=cr1, question=q1, answer='23'
|
||||||
))
|
))
|
||||||
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||||
'item_' + self.ticket.identity: '1',
|
'item_%d' % self.ticket.id: '1',
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
obj = CartPosition.objects.current.get(identity=cr1.identity)
|
obj = CartPosition.objects.get(id=cr1.id)
|
||||||
self.assertEqual(obj.answers.get(question=q1).answer, '23')
|
self.assertEqual(obj.answers.get(question=q1).answer, '23')
|
||||||
|
|
||||||
def test_renew_expired_failed(self):
|
def test_renew_expired_failed(self):
|
||||||
@@ -279,11 +279,11 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
price=23, expires=now() - timedelta(minutes=10)
|
price=23, expires=now() - timedelta(minutes=10)
|
||||||
)
|
)
|
||||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||||
'item_' + self.ticket.identity: '1',
|
'item_%d' % self.ticket.id: '1',
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
self.assertIn('no longer available', doc.select('.alert-danger')[0].text)
|
self.assertIn('no longer available', doc.select('.alert-danger')[0].text)
|
||||||
self.assertFalse(CartPosition.objects.current.filter(identity=cp1.identity).exists())
|
self.assertFalse(CartPosition.objects.filter(id=cp1.id).exists())
|
||||||
|
|
||||||
def test_remove_simple(self):
|
def test_remove_simple(self):
|
||||||
CartPosition.objects.create(
|
CartPosition.objects.create(
|
||||||
@@ -291,11 +291,11 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
price=23, expires=now() + timedelta(minutes=10)
|
price=23, expires=now() + timedelta(minutes=10)
|
||||||
)
|
)
|
||||||
response = self.client.post('/%s/%s/cart/remove' % (self.orga.slug, self.event.slug), {
|
response = self.client.post('/%s/%s/cart/remove' % (self.orga.slug, self.event.slug), {
|
||||||
'item_' + self.ticket.identity: '1',
|
'item_%d' % self.ticket.id: '1',
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
self.assertIn('updated', doc.select('.alert-success')[0].text)
|
self.assertIn('updated', doc.select('.alert-success')[0].text)
|
||||||
self.assertFalse(CartPosition.objects.current.filter(cart_id=self.session_key, event=self.event).exists())
|
self.assertFalse(CartPosition.objects.filter(cart_id=self.session_key, event=self.event).exists())
|
||||||
|
|
||||||
def test_remove_variation(self):
|
def test_remove_variation(self):
|
||||||
CartPosition.objects.create(
|
CartPosition.objects.create(
|
||||||
@@ -303,11 +303,11 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
price=14, expires=now() + timedelta(minutes=10)
|
price=14, expires=now() + timedelta(minutes=10)
|
||||||
)
|
)
|
||||||
response = self.client.post('/%s/%s/cart/remove' % (self.orga.slug, self.event.slug), {
|
response = self.client.post('/%s/%s/cart/remove' % (self.orga.slug, self.event.slug), {
|
||||||
'variation_' + self.shirt.identity + '_' + self.shirt_red.identity: '1',
|
'variation_%d_%d' % (self.shirt.id, self.shirt_red.id): '1',
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
self.assertIn('updated', doc.select('.alert-success')[0].text)
|
self.assertIn('updated', doc.select('.alert-success')[0].text)
|
||||||
self.assertFalse(CartPosition.objects.current.filter(cart_id=self.session_key, event=self.event).exists())
|
self.assertFalse(CartPosition.objects.filter(cart_id=self.session_key, event=self.event).exists())
|
||||||
|
|
||||||
def test_remove_one_of_multiple(self):
|
def test_remove_one_of_multiple(self):
|
||||||
CartPosition.objects.create(
|
CartPosition.objects.create(
|
||||||
@@ -319,11 +319,11 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
price=23, expires=now() + timedelta(minutes=10)
|
price=23, expires=now() + timedelta(minutes=10)
|
||||||
)
|
)
|
||||||
response = self.client.post('/%s/%s/cart/remove' % (self.orga.slug, self.event.slug), {
|
response = self.client.post('/%s/%s/cart/remove' % (self.orga.slug, self.event.slug), {
|
||||||
'item_' + self.ticket.identity: '1',
|
'item_%d' % self.ticket.id: '1',
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
self.assertIn('updated', doc.select('.alert-success')[0].text)
|
self.assertIn('updated', doc.select('.alert-success')[0].text)
|
||||||
self.assertEqual(CartPosition.objects.current.filter(cart_id=self.session_key, event=self.event).count(), 1)
|
self.assertEqual(CartPosition.objects.filter(cart_id=self.session_key, event=self.event).count(), 1)
|
||||||
|
|
||||||
def test_remove_multiple(self):
|
def test_remove_multiple(self):
|
||||||
CartPosition.objects.create(
|
CartPosition.objects.create(
|
||||||
@@ -335,11 +335,11 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
price=23, expires=now() + timedelta(minutes=10)
|
price=23, expires=now() + timedelta(minutes=10)
|
||||||
)
|
)
|
||||||
response = self.client.post('/%s/%s/cart/remove' % (self.orga.slug, self.event.slug), {
|
response = self.client.post('/%s/%s/cart/remove' % (self.orga.slug, self.event.slug), {
|
||||||
'item_' + self.ticket.identity: '2',
|
'item_%d' % self.ticket.id: '2',
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
self.assertIn('updated', doc.select('.alert-success')[0].text)
|
self.assertIn('updated', doc.select('.alert-success')[0].text)
|
||||||
self.assertFalse(CartPosition.objects.current.filter(cart_id=self.session_key, event=self.event).exists())
|
self.assertFalse(CartPosition.objects.filter(cart_id=self.session_key, event=self.event).exists())
|
||||||
|
|
||||||
def test_remove_most_expensive(self):
|
def test_remove_most_expensive(self):
|
||||||
CartPosition.objects.create(
|
CartPosition.objects.create(
|
||||||
@@ -351,11 +351,11 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
price=20, expires=now() + timedelta(minutes=10)
|
price=20, expires=now() + timedelta(minutes=10)
|
||||||
)
|
)
|
||||||
response = self.client.post('/%s/%s/cart/remove' % (self.orga.slug, self.event.slug), {
|
response = self.client.post('/%s/%s/cart/remove' % (self.orga.slug, self.event.slug), {
|
||||||
'item_' + self.ticket.identity: '1',
|
'item_%d' % self.ticket.id: '1',
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
self.assertIn('updated', doc.select('.alert-success')[0].text)
|
self.assertIn('updated', doc.select('.alert-success')[0].text)
|
||||||
objs = list(CartPosition.objects.current.filter(cart_id=self.session_key, event=self.event))
|
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
|
||||||
self.assertEqual(len(objs), 1)
|
self.assertEqual(len(objs), 1)
|
||||||
self.assertEqual(objs[0].item, self.ticket)
|
self.assertEqual(objs[0].item, self.ticket)
|
||||||
self.assertIsNone(objs[0].variation)
|
self.assertIsNone(objs[0].variation)
|
||||||
|
|||||||
@@ -60,17 +60,17 @@ class CheckoutTestCase(TestCase):
|
|||||||
response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True)
|
response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True)
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
|
|
||||||
self.assertEqual(len(doc.select('input[name=%s-question_%s]' % (cr1.identity, q1.identity))), 1)
|
self.assertEqual(len(doc.select('input[name=%s-question_%s]' % (cr1.id, q1.id))), 1)
|
||||||
self.assertEqual(len(doc.select('input[name=%s-question_%s]' % (cr2.identity, q1.identity))), 1)
|
self.assertEqual(len(doc.select('input[name=%s-question_%s]' % (cr2.id, q1.id))), 1)
|
||||||
self.assertEqual(len(doc.select('input[name=%s-question_%s]' % (cr1.identity, q2.identity))), 1)
|
self.assertEqual(len(doc.select('input[name=%s-question_%s]' % (cr1.id, q2.id))), 1)
|
||||||
self.assertEqual(len(doc.select('input[name=%s-question_%s]' % (cr2.identity, q2.identity))), 1)
|
self.assertEqual(len(doc.select('input[name=%s-question_%s]' % (cr2.id, q2.id))), 1)
|
||||||
|
|
||||||
# Not all required fields filled out, expect failure
|
# Not all required fields filled out, expect failure
|
||||||
response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), {
|
response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), {
|
||||||
'%s-question_%s' % (cr1.identity, q1.identity): '42',
|
'%s-question_%s' % (cr1.id, q1.id): '42',
|
||||||
'%s-question_%s' % (cr2.identity, q1.identity): '',
|
'%s-question_%s' % (cr2.id, q1.id): '',
|
||||||
'%s-question_%s' % (cr1.identity, q2.identity): 'Internet',
|
'%s-question_%s' % (cr1.id, q2.id): 'Internet',
|
||||||
'%s-question_%s' % (cr2.identity, q2.identity): '',
|
'%s-question_%s' % (cr2.id, q2.id): '',
|
||||||
'email': 'admin@localhost'
|
'email': 'admin@localhost'
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
@@ -78,17 +78,17 @@ class CheckoutTestCase(TestCase):
|
|||||||
|
|
||||||
# Corrected request
|
# Corrected request
|
||||||
response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), {
|
response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), {
|
||||||
'%s-question_%s' % (cr1.identity, q1.identity): '42',
|
'%s-question_%s' % (cr1.id, q1.id): '42',
|
||||||
'%s-question_%s' % (cr2.identity, q1.identity): '23',
|
'%s-question_%s' % (cr2.id, q1.id): '23',
|
||||||
'%s-question_%s' % (cr1.identity, q2.identity): 'Internet',
|
'%s-question_%s' % (cr1.id, q2.id): 'Internet',
|
||||||
'%s-question_%s' % (cr2.identity, q2.identity): '',
|
'%s-question_%s' % (cr2.id, q2.id): '',
|
||||||
'email': 'admin@localhost'
|
'email': 'admin@localhost'
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug),
|
self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug),
|
||||||
target_status_code=200)
|
target_status_code=200)
|
||||||
|
|
||||||
cr1 = CartPosition.objects.current.get(identity=cr1.identity)
|
cr1 = CartPosition.objects.get(id=cr1.id)
|
||||||
cr2 = CartPosition.objects.current.get(identity=cr2.identity)
|
cr2 = CartPosition.objects.get(id=cr2.id)
|
||||||
self.assertEqual(cr1.answers.filter(question=q1).count(), 1)
|
self.assertEqual(cr1.answers.filter(question=q1).count(), 1)
|
||||||
self.assertEqual(cr2.answers.filter(question=q1).count(), 1)
|
self.assertEqual(cr2.answers.filter(question=q1).count(), 1)
|
||||||
self.assertEqual(cr1.answers.filter(question=q2).count(), 1)
|
self.assertEqual(cr1.answers.filter(question=q2).count(), 1)
|
||||||
@@ -103,11 +103,11 @@ class CheckoutTestCase(TestCase):
|
|||||||
)
|
)
|
||||||
response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True)
|
response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True)
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
self.assertEqual(len(doc.select('input[name=%s-attendee_name]' % cr1.identity)), 1)
|
self.assertEqual(len(doc.select('input[name=%s-attendee_name]' % cr1.id)), 1)
|
||||||
|
|
||||||
# Not all required fields filled out, expect failure
|
# Not all required fields filled out, expect failure
|
||||||
response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), {
|
response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), {
|
||||||
'%s-attendee_name' % cr1.identity: '',
|
'%s-attendee_name' % cr1.id: '',
|
||||||
'email': 'admin@localhost'
|
'email': 'admin@localhost'
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
@@ -115,13 +115,13 @@ class CheckoutTestCase(TestCase):
|
|||||||
|
|
||||||
# Corrected request
|
# Corrected request
|
||||||
response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), {
|
response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), {
|
||||||
'%s-attendee_name' % cr1.identity: 'Peter',
|
'%s-attendee_name' % cr1.id: 'Peter',
|
||||||
'email': 'admin@localhost'
|
'email': 'admin@localhost'
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug),
|
self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug),
|
||||||
target_status_code=200)
|
target_status_code=200)
|
||||||
|
|
||||||
cr1 = CartPosition.objects.current.get(identity=cr1.identity)
|
cr1 = CartPosition.objects.get(id=cr1.id)
|
||||||
self.assertEqual(cr1.attendee_name, 'Peter')
|
self.assertEqual(cr1.attendee_name, 'Peter')
|
||||||
|
|
||||||
def test_attendee_name_optional(self):
|
def test_attendee_name_optional(self):
|
||||||
@@ -133,17 +133,17 @@ class CheckoutTestCase(TestCase):
|
|||||||
)
|
)
|
||||||
response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True)
|
response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True)
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
self.assertEqual(len(doc.select('input[name=%s-attendee_name]' % cr1.identity)), 1)
|
self.assertEqual(len(doc.select('input[name=%s-attendee_name]' % cr1.id)), 1)
|
||||||
|
|
||||||
# Not all fields filled out, expect success
|
# Not all fields filled out, expect success
|
||||||
response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), {
|
response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), {
|
||||||
'%s-attendee_name' % cr1.identity: '',
|
'%s-attendee_name' % cr1.id: '',
|
||||||
'email': 'admin@localhost'
|
'email': 'admin@localhost'
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug),
|
self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug),
|
||||||
target_status_code=200)
|
target_status_code=200)
|
||||||
|
|
||||||
cr1 = CartPosition.objects.current.get(identity=cr1.identity)
|
cr1 = CartPosition.objects.get(id=cr1.id)
|
||||||
self.assertIsNone(cr1.attendee_name)
|
self.assertIsNone(cr1.attendee_name)
|
||||||
|
|
||||||
def test_payment(self):
|
def test_payment(self):
|
||||||
@@ -188,7 +188,6 @@ class CheckoutTestCase(TestCase):
|
|||||||
self.assertRedirects(response, '/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug),
|
self.assertRedirects(response, '/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug),
|
||||||
target_status_code=200)
|
target_status_code=200)
|
||||||
|
|
||||||
cr1 = cr1.clone()
|
|
||||||
cr1.attendee_name = 'Peter'
|
cr1.attendee_name = 'Peter'
|
||||||
cr1.save()
|
cr1.save()
|
||||||
q1 = Question.objects.create(
|
q1 = Question.objects.create(
|
||||||
@@ -201,7 +200,6 @@ class CheckoutTestCase(TestCase):
|
|||||||
self.assertRedirects(response, '/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug),
|
self.assertRedirects(response, '/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug),
|
||||||
target_status_code=200)
|
target_status_code=200)
|
||||||
|
|
||||||
q1 = q1.clone()
|
|
||||||
q1.required = False
|
q1.required = False
|
||||||
q1.save()
|
q1.save()
|
||||||
response = self.client.get('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
|
response = self.client.get('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
|
||||||
@@ -227,9 +225,9 @@ class CheckoutTestCase(TestCase):
|
|||||||
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
|
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
self.assertEqual(len(doc.select(".thank-you")), 1)
|
self.assertEqual(len(doc.select(".thank-you")), 1)
|
||||||
self.assertFalse(CartPosition.objects.current.filter(identity=cr1.identity).exists())
|
self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists())
|
||||||
self.assertEqual(Order.objects.current.count(), 1)
|
self.assertEqual(Order.objects.count(), 1)
|
||||||
self.assertEqual(OrderPosition.objects.current.count(), 1)
|
self.assertEqual(OrderPosition.objects.count(), 1)
|
||||||
|
|
||||||
def test_confirm_expired_available(self):
|
def test_confirm_expired_available(self):
|
||||||
cr1 = CartPosition.objects.create(
|
cr1 = CartPosition.objects.create(
|
||||||
@@ -241,12 +239,11 @@ class CheckoutTestCase(TestCase):
|
|||||||
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
|
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
self.assertEqual(len(doc.select(".thank-you")), 1)
|
self.assertEqual(len(doc.select(".thank-you")), 1)
|
||||||
self.assertFalse(CartPosition.objects.current.filter(identity=cr1.identity).exists())
|
self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists())
|
||||||
self.assertEqual(Order.objects.current.count(), 1)
|
self.assertEqual(Order.objects.count(), 1)
|
||||||
self.assertEqual(OrderPosition.objects.current.count(), 1)
|
self.assertEqual(OrderPosition.objects.count(), 1)
|
||||||
|
|
||||||
def test_confirm_price_changed(self):
|
def test_confirm_price_changed(self):
|
||||||
self.ticket = self.ticket.clone()
|
|
||||||
self.ticket.default_price = 24
|
self.ticket.default_price = 24
|
||||||
self.ticket.save()
|
self.ticket.save()
|
||||||
cr1 = CartPosition.objects.create(
|
cr1 = CartPosition.objects.create(
|
||||||
@@ -258,7 +255,7 @@ class CheckoutTestCase(TestCase):
|
|||||||
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
|
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
self.assertEqual(len(doc.select(".alert-danger")), 1)
|
self.assertEqual(len(doc.select(".alert-danger")), 1)
|
||||||
cr1 = CartPosition.objects.current.get(identity=cr1.identity)
|
cr1 = CartPosition.objects.get(id=cr1.id)
|
||||||
self.assertEqual(cr1.price, 24)
|
self.assertEqual(cr1.price, 24)
|
||||||
|
|
||||||
def test_confirm_expired_partial(self):
|
def test_confirm_expired_partial(self):
|
||||||
@@ -277,10 +274,9 @@ class CheckoutTestCase(TestCase):
|
|||||||
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
|
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
self.assertEqual(len(doc.select(".alert-danger")), 1)
|
self.assertEqual(len(doc.select(".alert-danger")), 1)
|
||||||
self.assertEqual(CartPosition.objects.current.filter(cart_id=self.session_key).count(), 1)
|
self.assertEqual(CartPosition.objects.filter(cart_id=self.session_key).count(), 1)
|
||||||
|
|
||||||
def test_confirm_inactive(self):
|
def test_confirm_inactive(self):
|
||||||
self.ticket = self.ticket.clone()
|
|
||||||
self.ticket.active = False
|
self.ticket.active = False
|
||||||
self.ticket.save()
|
self.ticket.save()
|
||||||
cr1 = CartPosition.objects.create(
|
cr1 = CartPosition.objects.create(
|
||||||
@@ -292,7 +288,7 @@ class CheckoutTestCase(TestCase):
|
|||||||
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
|
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
self.assertGreaterEqual(len(doc.select(".alert-danger")), 1)
|
self.assertGreaterEqual(len(doc.select(".alert-danger")), 1)
|
||||||
self.assertFalse(CartPosition.objects.current.filter(identity=cr1.identity).exists())
|
self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists())
|
||||||
|
|
||||||
def test_confirm_expired_unavailable(self):
|
def test_confirm_expired_unavailable(self):
|
||||||
self.quota_tickets.size = 0
|
self.quota_tickets.size = 0
|
||||||
@@ -306,7 +302,7 @@ class CheckoutTestCase(TestCase):
|
|||||||
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
|
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
self.assertGreaterEqual(len(doc.select(".alert-danger")), 1)
|
self.assertGreaterEqual(len(doc.select(".alert-danger")), 1)
|
||||||
self.assertFalse(CartPosition.objects.current.filter(identity=cr1.identity).exists())
|
self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists())
|
||||||
|
|
||||||
def test_confirm_completely_unavailable(self):
|
def test_confirm_completely_unavailable(self):
|
||||||
self.quota_tickets.items.remove(self.ticket)
|
self.quota_tickets.items.remove(self.ticket)
|
||||||
@@ -319,4 +315,4 @@ class CheckoutTestCase(TestCase):
|
|||||||
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
|
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
self.assertGreaterEqual(len(doc.select(".alert-danger")), 1)
|
self.assertGreaterEqual(len(doc.select(".alert-danger")), 1)
|
||||||
self.assertFalse(CartPosition.objects.current.filter(identity=cr1.identity).exists())
|
self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists())
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ class DeadlineTest(EventTestMixin, TestCase):
|
|||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/%s/%s/cart/add' % (self.orga.slug, self.event.slug),
|
'/%s/%s/cart/add' % (self.orga.slug, self.event.slug),
|
||||||
{
|
{
|
||||||
'item_' + self.item.identity: '1',
|
'item_%d' % self.item.id: '1',
|
||||||
},
|
},
|
||||||
follow=True
|
follow=True
|
||||||
)
|
)
|
||||||
@@ -213,7 +213,7 @@ class DeadlineTest(EventTestMixin, TestCase):
|
|||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/%s/%s/cart/add' % (self.orga.slug, self.event.slug),
|
'/%s/%s/cart/add' % (self.orga.slug, self.event.slug),
|
||||||
{
|
{
|
||||||
'item_' + self.item.identity: '1'
|
'item_%d' % self.item.id: '1'
|
||||||
},
|
},
|
||||||
follow=True
|
follow=True
|
||||||
)
|
)
|
||||||
@@ -233,7 +233,7 @@ class DeadlineTest(EventTestMixin, TestCase):
|
|||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/%s/%s/cart/add' % (self.orga.slug, self.event.slug),
|
'/%s/%s/cart/add' % (self.orga.slug, self.event.slug),
|
||||||
{
|
{
|
||||||
'item_' + self.item.identity: '1'
|
'item_%d' % self.item.id: '1'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertNotEqual(response.status_code, 403)
|
self.assertNotEqual(response.status_code, 403)
|
||||||
@@ -251,7 +251,7 @@ class DeadlineTest(EventTestMixin, TestCase):
|
|||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/%s/%s/cart/add' % (self.orga.slug, self.event.slug),
|
'/%s/%s/cart/add' % (self.orga.slug, self.event.slug),
|
||||||
{
|
{
|
||||||
'item_' + self.item.identity: '1'
|
'item_%d' % self.item.id: '1'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertNotEqual(response.status_code, 403)
|
self.assertNotEqual(response.status_code, 403)
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ class OrdersTest(TestCase):
|
|||||||
self.client.get(
|
self.client.get(
|
||||||
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
||||||
)
|
)
|
||||||
self.order = Order.objects.current.get(identity=self.order.identity)
|
self.order = Order.objects.get(id=self.order.id)
|
||||||
assert self.order.status == Order.STATUS_REFUNDED
|
assert self.order.status == Order.STATUS_REFUNDED
|
||||||
|
|
||||||
def test_orders_modify_attendee_optional(self):
|
def test_orders_modify_attendee_optional(self):
|
||||||
@@ -139,18 +139,18 @@ class OrdersTest(TestCase):
|
|||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret))
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret))
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
self.assertEqual(len(doc.select('input[name=%s-attendee_name]' % self.ticket_pos.identity)), 1)
|
self.assertEqual(len(doc.select('input[name=%s-attendee_name]' % self.ticket_pos.id)), 1)
|
||||||
|
|
||||||
# Not all fields filled out, expect success
|
# Not all fields filled out, expect success
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
||||||
'%s-attendee_name' % self.ticket_pos.identity: '',
|
'%s-attendee_name' % self.ticket_pos.id: '',
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
self.assertRedirects(response,
|
self.assertRedirects(response,
|
||||||
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
||||||
self.order.secret),
|
self.order.secret),
|
||||||
target_status_code=200)
|
target_status_code=200)
|
||||||
self.ticket_pos = OrderPosition.objects.current.get(identity=self.ticket_pos.identity)
|
self.ticket_pos = OrderPosition.objects.get(id=self.ticket_pos.id)
|
||||||
assert self.ticket_pos.attendee_name in (None, '')
|
assert self.ticket_pos.attendee_name in (None, '')
|
||||||
|
|
||||||
def test_orders_modify_attendee_required(self):
|
def test_orders_modify_attendee_required(self):
|
||||||
@@ -160,24 +160,24 @@ class OrdersTest(TestCase):
|
|||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret))
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret))
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
self.assertEqual(len(doc.select('input[name=%s-attendee_name]' % self.ticket_pos.identity)), 1)
|
self.assertEqual(len(doc.select('input[name=%s-attendee_name]' % self.ticket_pos.id)), 1)
|
||||||
|
|
||||||
# Not all required fields filled out, expect failure
|
# Not all required fields filled out, expect failure
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
||||||
'%s-attendee_name' % self.ticket_pos.identity: '',
|
'%s-attendee_name' % self.ticket_pos.id: '',
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
self.assertGreaterEqual(len(doc.select('.has-error')), 1)
|
self.assertGreaterEqual(len(doc.select('.has-error')), 1)
|
||||||
|
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
||||||
'%s-attendee_name' % self.ticket_pos.identity: 'Peter',
|
'%s-attendee_name' % self.ticket_pos.id: 'Peter',
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
self.assertRedirects(response, '/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
self.assertRedirects(response, '/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
||||||
self.order.secret),
|
self.order.secret),
|
||||||
target_status_code=200)
|
target_status_code=200)
|
||||||
self.ticket_pos = OrderPosition.objects.current.get(identity=self.ticket_pos.identity)
|
self.ticket_pos = OrderPosition.objects.get(id=self.ticket_pos.id)
|
||||||
assert self.ticket_pos.attendee_name == 'Peter'
|
assert self.ticket_pos.attendee_name == 'Peter'
|
||||||
|
|
||||||
def test_orders_questions_optional(self):
|
def test_orders_questions_optional(self):
|
||||||
@@ -188,12 +188,12 @@ class OrdersTest(TestCase):
|
|||||||
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret))
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret))
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
self.assertEqual(len(doc.select('input[name=%s-question_%s]' % (
|
self.assertEqual(len(doc.select('input[name=%s-question_%s]' % (
|
||||||
self.ticket_pos.identity, self.question.identity))), 1)
|
self.ticket_pos.id, self.question.id))), 1)
|
||||||
|
|
||||||
# Not all fields filled out, expect success
|
# Not all fields filled out, expect success
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
||||||
'%s-question_%s' % (self.ticket_pos.identity, self.question.identity): '',
|
'%s-question_%s' % (self.ticket_pos.id, self.question.id): '',
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
self.assertRedirects(response,
|
self.assertRedirects(response,
|
||||||
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
||||||
@@ -211,19 +211,19 @@ class OrdersTest(TestCase):
|
|||||||
self.order.secret))
|
self.order.secret))
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
self.assertEqual(len(doc.select('input[name=%s-question_%s]' % (
|
self.assertEqual(len(doc.select('input[name=%s-question_%s]' % (
|
||||||
self.ticket_pos.identity, self.question.identity))), 1)
|
self.ticket_pos.id, self.question.id))), 1)
|
||||||
|
|
||||||
# Not all required fields filled out, expect failure
|
# Not all required fields filled out, expect failure
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
||||||
'%s-question_%s' % (self.ticket_pos.identity, self.question.identity): '',
|
'%s-question_%s' % (self.ticket_pos.id, self.question.id): '',
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
doc = BeautifulSoup(response.rendered_content)
|
doc = BeautifulSoup(response.rendered_content)
|
||||||
self.assertGreaterEqual(len(doc.select('.has-error')), 1)
|
self.assertGreaterEqual(len(doc.select('.has-error')), 1)
|
||||||
|
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
||||||
'%s-question_%s' % (self.ticket_pos.identity, self.question.identity): 'ABC',
|
'%s-question_%s' % (self.ticket_pos.id, self.question.id): 'ABC',
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
self.assertRedirects(response,
|
self.assertRedirects(response,
|
||||||
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
||||||
@@ -237,7 +237,7 @@ class OrdersTest(TestCase):
|
|||||||
self.client.get(
|
self.client.get(
|
||||||
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
||||||
)
|
)
|
||||||
self.order = Order.objects.current.get(identity=self.order.identity)
|
self.order = Order.objects.get(id=self.order.id)
|
||||||
assert self.order.status == Order.STATUS_PAID
|
assert self.order.status == Order.STATUS_PAID
|
||||||
|
|
||||||
def test_orders_cancel(self):
|
def test_orders_cancel(self):
|
||||||
@@ -252,7 +252,7 @@ class OrdersTest(TestCase):
|
|||||||
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
||||||
self.order.secret),
|
self.order.secret),
|
||||||
target_status_code=200)
|
target_status_code=200)
|
||||||
assert Order.objects.current.get(identity=self.order.identity).status == Order.STATUS_CANCELLED
|
assert Order.objects.get(id=self.order.id).status == Order.STATUS_CANCELLED
|
||||||
|
|
||||||
def test_orders_download(self):
|
def test_orders_download(self):
|
||||||
self.event.settings.set('ticket_download', True)
|
self.event.settings.set('ticket_download', True)
|
||||||
|
|||||||
@@ -12,4 +12,4 @@ class DummyTicketOutput(BaseTicketOutput):
|
|||||||
download_button_icon = 'fa-print'
|
download_button_icon = 'fa-print'
|
||||||
|
|
||||||
def generate(self, order):
|
def generate(self, order):
|
||||||
return 'test.txt', 'text/plain', order.identity
|
return 'test.txt', 'text/plain', str(order.id)
|
||||||
|
|||||||
Reference in New Issue
Block a user