Removed CleanerVersion layer [backwards-incompatible!]

This commit is contained in:
Raphael Michel
2015-12-12 13:08:33 +01:00
parent 0c9c9dd22c
commit d133d2abff
85 changed files with 712 additions and 1089 deletions

View File

@@ -87,13 +87,13 @@ class JSONExporter(BaseExporter):
},
'categories': [
{
'id': category.identity,
'id': category.id,
'name': str(category.name)
} for category in self.event.categories.current.all()
} for category in self.event.categories.all()
],
'items': [
{
'id': item.identity,
'id': item.id,
'name': str(item.name),
'category': item.category_id,
'price': item.default_price,
@@ -101,20 +101,20 @@ class JSONExporter(BaseExporter):
'active': item.active,
'variations': [
{
'id': variation.identity,
'id': variation.id,
'active': variation.active,
'price': variation.default_price if variation.default_price is not None else item.default_price,
'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': [
{
'id': question.identity,
'id': question.id,
'question': str(question.question),
'type': question.type
} for question in self.event.questions.current.all()
} for question in self.event.questions.all()
],
'orders': [
{
@@ -126,7 +126,7 @@ class JSONExporter(BaseExporter):
'total': order.total,
'positions': [
{
'id': position.identity,
'id': position.id,
'item': position.item_id,
'variation': position.variation_id,
'price': position.price,
@@ -137,19 +137,19 @@ class JSONExporter(BaseExporter):
'answer': answer.answer
} for answer in position.answers.all()
]
} for position in order.positions.current.all()
} for position in order.positions.all()
]
} 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')
],
'quotas': [
{
'id': quota.identity,
'id': quota.id,
'size': quota.size,
'items': [item.id for item in quota.items.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')
]
}
}

View File

@@ -5,11 +5,9 @@ from django import forms
from django.core.files import File
from django.core.files.storage import default_storage
from django.core.files.uploadedfile import UploadedFile
from django.db import models
from django.forms.models import BaseModelForm, ModelFormMetaclass
from django.utils import six
from django.utils.translation import ugettext_lazy as _
from versions.models import Versionable
from pretix.base.i18n import I18nFormField
from pretix.base.models import Event
@@ -30,44 +28,6 @@ class BaseI18nModelForm(BaseModelForm):
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)):
"""
This is a modified version of Django's ModelForm which differs from ModelForm in

View File

@@ -5,12 +5,23 @@ import uuid
import django.core.validators
import django.db.models.deletion
import versions.models
from django.conf import settings
from django.contrib.auth.hashers import make_password
from django.db import migrations, models
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):
@@ -23,20 +34,20 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='User',
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)),
('last_login', models.DateTimeField(verbose_name='last login', null=True, blank=True)),
('is_superuser', models.BooleanField(default=False, verbose_name='superuser status', 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)),
('givenname', models.CharField(verbose_name='Given name', null=True, max_length=255, blank=True)),
('familyname', models.CharField(verbose_name='Family name', null=True, max_length=255, blank=True)),
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
('is_staff', models.BooleanField(default=False, verbose_name='Is site admin')),
('last_login', models.DateTimeField(verbose_name='last login', blank=True, null=True)),
('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(max_length=254, blank=True, unique=True, verbose_name='E-mail', null=True, db_index=True)),
('givenname', models.CharField(verbose_name='Given name', max_length=255, blank=True, null=True)),
('familyname', models.CharField(verbose_name='Family name', max_length=255, blank=True, null=True)),
('is_active', models.BooleanField(verbose_name='Is active', default=True)),
('is_staff', models.BooleanField(verbose_name='Is site admin', default=False)),
('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)),
('timezone', models.CharField(default='UTC', verbose_name='Timezone', 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')),
('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')),
('locale', models.CharField(verbose_name='Language', default='en', choices=[('en', 'English'), ('de', 'German'), ('de-informal', 'German (informal)')], max_length=50)),
('timezone', models.CharField(verbose_name='Timezone', default='UTC', max_length=100)),
('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(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={
'verbose_name': 'User',
@@ -47,17 +58,17 @@ class Migration(migrations.Migration):
name='CachedFile',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('expires', models.DateTimeField(null=True, blank=True)),
('date', models.DateTimeField(null=True, blank=True)),
('expires', models.DateTimeField(blank=True, null=True)),
('date', models.DateTimeField(blank=True, null=True)),
('filename', 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(
name='CachedTicket',
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)),
('cachedfile', models.ForeignKey(to='pretixbase.CachedFile')),
],
@@ -65,39 +76,32 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='CartPosition',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('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()),
('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)),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('cart_id', models.CharField(verbose_name='Cart ID (e.g. session key)', max_length=255, blank=True, null=True)),
('price', models.DecimalField(verbose_name='Price', max_digits=10, decimal_places=2)),
('datetime', models.DateTimeField(verbose_name='Date', auto_now_add=True)),
('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={
'verbose_name': 'Cart position',
'verbose_name_plural': 'Cart positions',
},
bases=(pretix.base.models.ObjectWithAnswers, models.Model),
bases=(pretix.base.models.orders.ObjectWithAnswers, models.Model),
),
migrations.CreateModel(
name='Event',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('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()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('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.')])),
('currency', models.CharField(default='EUR', verbose_name='Default currency', max_length=10)),
('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(verbose_name='Default currency', default='EUR', max_length=10)),
('date_from', models.DateTimeField(verbose_name='Event start time')),
('date_to', models.DateTimeField(verbose_name='Event end time', null=True, blank=True)),
('presale_end', models.DateTimeField(verbose_name='End of presale', help_text='No products will be sold after this date.', null=True, blank=True)),
('presale_start', models.DateTimeField(verbose_name='Start of presale', help_text='No products will be sold before this date.', null=True, blank=True)),
('plugins', models.TextField(verbose_name='Plugins', null=True, blank=True)),
('date_to', models.DateTimeField(verbose_name='Event end time', blank=True, null=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_end', models.DateTimeField(verbose_name='End of presale', help_text='No products will be sold after this date.', blank=True, null=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={
'verbose_name': 'Event',
@@ -108,7 +112,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='EventLock',
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)),
('token', models.UUIDField(default=uuid.uuid4)),
],
@@ -116,18 +120,14 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='EventPermission',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('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()),
('can_change_settings', models.BooleanField(default=True, verbose_name='Can change event settings')),
('can_change_items', models.BooleanField(default=True, verbose_name='Can change product settings')),
('can_view_orders', models.BooleanField(default=True, verbose_name='Can view orders')),
('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)),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('can_change_settings', models.BooleanField(verbose_name='Can change event settings', default=True)),
('can_change_items', models.BooleanField(verbose_name='Can change product settings', default=True)),
('can_view_orders', models.BooleanField(verbose_name='Can view orders', default=True)),
('can_change_permissions', models.BooleanField(verbose_name='Can change permissions', default=True)),
('can_change_orders', models.BooleanField(verbose_name='Can change orders', default=True)),
('event', models.ForeignKey(to='pretixbase.Event', related_name='user_perms')),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='event_perms')),
],
options={
'verbose_name': 'Event permission',
@@ -137,35 +137,26 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='EventSetting',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('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()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('key', models.CharField(max_length=255)),
('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(
name='Item',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('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()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('name', pretix.base.i18n.I18nCharField(verbose_name='Item name', max_length=255)),
('active', models.BooleanField(default=True, verbose_name='Active')),
('description', pretix.base.i18n.I18nTextField(verbose_name='Description', help_text='This is shown below the product name in lists.', null=True, blank=True)),
('default_price', models.DecimalField(decimal_places=2, verbose_name='Default price', max_digits=7, null=True)),
('tax_rate', models.DecimalField(decimal_places=2, verbose_name='Taxes included in percent', max_digits=7, null=True, blank=True)),
('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')),
('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.', blank=True, null=True)),
('default_price', models.DecimalField(verbose_name='Default price', max_digits=7, decimal_places=2, null=True)),
('tax_rate', models.DecimalField(verbose_name='Taxes included in percent', max_digits=7, blank=True, null=True, decimal_places=2)),
('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)),
('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={
'verbose_name': 'Product',
@@ -176,32 +167,24 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='ItemCategory',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('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()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('name', pretix.base.i18n.I18nCharField(verbose_name='Category name', max_length=255)),
('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={
'verbose_name': 'Product category',
'verbose_name_plural': 'Product categories',
'ordering': ('position', 'version_birth_date'),
'ordering': ('position', 'id'),
},
),
migrations.CreateModel(
name='ItemVariation',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('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()),
('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')),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('active', models.BooleanField(verbose_name='Active', default=True)),
('default_price', models.DecimalField(verbose_name='Default price', max_digits=7, blank=True, null=True, decimal_places=2)),
('item', models.ForeignKey(to='pretixbase.Item', related_name='variations')),
],
options={
'verbose_name': 'Product variation',
@@ -211,25 +194,21 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='Order',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('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()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('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)),
('email', models.EmailField(verbose_name='E-mail', null=True, max_length=254, blank=True)),
('locale', models.CharField(verbose_name='Locale', null=True, max_length=32, blank=True)),
('secret', models.CharField(default=pretix.base.models.generate_secret, max_length=32)),
('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', max_length=254, blank=True, null=True)),
('locale', models.CharField(verbose_name='Locale', max_length=32, blank=True, null=True)),
('secret', models.CharField(default=pretix.base.models.orders.generate_secret, max_length=32)),
('datetime', models.DateTimeField(verbose_name='Date')),
('expires', models.DateTimeField(verbose_name='Expiration date')),
('payment_date', models.DateTimeField(verbose_name='Payment date', null=True, blank=True)),
('payment_provider', models.CharField(verbose_name='Payment provider', null=True, max_length=255, blank=True)),
('payment_fee', models.DecimalField(decimal_places=2, default=0, verbose_name='Payment method fee', max_digits=10)),
('payment_info', models.TextField(verbose_name='Payment information', null=True, blank=True)),
('payment_manual', models.BooleanField(default=False, verbose_name='Payment state was manually modified')),
('total', models.DecimalField(decimal_places=2, verbose_name='Total amount', max_digits=10)),
('event', versions.models.VersionedForeignKey(verbose_name='Event', related_name='orders', to='pretixbase.Event')),
('payment_date', models.DateTimeField(verbose_name='Payment date', blank=True, null=True)),
('payment_provider', models.CharField(verbose_name='Payment provider', max_length=255, blank=True, null=True)),
('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', blank=True, null=True)),
('payment_manual', models.BooleanField(verbose_name='Payment state was manually modified', default=False)),
('total', models.DecimalField(verbose_name='Total amount', max_digits=10, decimal_places=2)),
('event', models.ForeignKey(to='pretixbase.Event', verbose_name='Event', related_name='orders')),
],
options={
'verbose_name': 'Order',
@@ -240,33 +219,25 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='OrderPosition',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('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()),
('price', models.DecimalField(decimal_places=2, verbose_name='Price', max_digits=10)),
('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')),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('price', models.DecimalField(verbose_name='Price', max_digits=10, decimal_places=2)),
('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')),
('item', models.ForeignKey(to='pretixbase.Item', on_delete=django.db.models.deletion.PROTECT, verbose_name='Item', related_name='positions')),
('order', models.ForeignKey(to='pretixbase.Order', on_delete=django.db.models.deletion.PROTECT, verbose_name='Order', related_name='positions')),
('variation', models.ForeignKey(blank=True, to='pretixbase.ItemVariation', on_delete=django.db.models.deletion.PROTECT, verbose_name='Variation', null=True)),
],
options={
'verbose_name': 'Order position',
'verbose_name_plural': 'Order positions',
},
bases=(pretix.base.models.ObjectWithAnswers, models.Model),
bases=(pretix.base.models.orders.ObjectWithAnswers, models.Model),
),
migrations.CreateModel(
name='Organizer',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('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()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('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={
'verbose_name': 'Organizer',
@@ -277,14 +248,10 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='OrganizerPermission',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('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()),
('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)),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('can_create_events', models.BooleanField(verbose_name='Can create events', default=True)),
('organizer', models.ForeignKey(to='pretixbase.Organizer', related_name='user_perms')),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='organizer_perms')),
],
options={
'verbose_name': 'Organizer permission',
@@ -294,30 +261,19 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='OrganizerSetting',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('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()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('key', models.CharField(max_length=255)),
('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(
name='Property',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('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()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('name', pretix.base.i18n.I18nCharField(verbose_name='Property name', max_length=250)),
('event', versions.models.VersionedForeignKey(related_name='properties', to='pretixbase.Event')),
('item', versions.models.VersionedForeignKey(blank=True, related_name='properties', null=True, to='pretixbase.Item')),
('event', models.ForeignKey(to='pretixbase.Event', related_name='properties')),
('item', models.ForeignKey(blank=True, to='pretixbase.Item', null=True, related_name='properties')),
],
options={
'verbose_name': 'Product property',
@@ -327,34 +283,26 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='PropertyValue',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('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()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('value', pretix.base.i18n.I18nCharField(verbose_name='Value', max_length=250)),
('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={
'verbose_name': 'Property value',
'verbose_name_plural': 'Property values',
'ordering': ('position', 'version_birth_date'),
'ordering': ('position', 'id'),
},
),
migrations.CreateModel(
name='Question',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('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()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('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)),
('required', models.BooleanField(default=False, verbose_name='Required question')),
('event', versions.models.VersionedForeignKey(related_name='questions', to='pretixbase.Event')),
('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)),
('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(verbose_name='Required question', default=False)),
('event', models.ForeignKey(to='pretixbase.Event', related_name='questions')),
('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={
'verbose_name': 'Question',
@@ -364,33 +312,22 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='QuestionAnswer',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('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()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('answer', models.TextField()),
('cartposition', models.ForeignKey(blank=True, related_name='answers', null=True, to='pretixbase.CartPosition')),
('orderposition', models.ForeignKey(blank=True, related_name='answers', null=True, to='pretixbase.OrderPosition')),
('question', versions.models.VersionedForeignKey(related_name='answers', to='pretixbase.Question')),
('cartposition', models.ForeignKey(blank=True, to='pretixbase.CartPosition', null=True, related_name='answers')),
('orderposition', models.ForeignKey(blank=True, to='pretixbase.OrderPosition', null=True, related_name='answers')),
('question', models.ForeignKey(to='pretixbase.Question', related_name='answers')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Quota',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('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()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('name', models.CharField(verbose_name='Name', max_length=200)),
('size', models.PositiveIntegerField(verbose_name='Total capacity')),
('event', versions.models.VersionedForeignKey(verbose_name='Event', related_name='quotas', to='pretixbase.Event')),
('items', versions.models.VersionedManyToManyField(to='pretixbase.Item', verbose_name='Item', related_name='quotas', blank=True)),
('variations', pretix.base.models.VariationsField(to='pretixbase.ItemVariation', verbose_name='Variations', related_name='quotas', blank=True)),
('size', models.PositiveIntegerField(verbose_name='Total capacity', help_text='Leave empty for an unlimited number of tickets.', blank=True, null=True)),
('event', models.ForeignKey(to='pretixbase.Event', verbose_name='Event', related_name='quotas')),
('items', models.ManyToManyField(verbose_name='Item', to='pretixbase.Item', blank=True, related_name='quotas')),
('variations', pretix.base.models.items.VariationsField(verbose_name='Variations', to='pretixbase.ItemVariation', blank=True, related_name='quotas')),
],
options={
'verbose_name': 'Quota',
@@ -400,51 +337,52 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='organizer',
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(
model_name='itemvariation',
name='values',
field=versions.models.VersionedManyToManyField(related_name='variations', to='pretixbase.PropertyValue'),
field=models.ForeignKey(to='pretixbase.PropertyValue', related_name='variations'),
),
migrations.AddField(
model_name='item',
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(
model_name='item',
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(
model_name='event',
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(
model_name='event',
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(
model_name='cartposition',
name='event',
field=versions.models.VersionedForeignKey(verbose_name='Event', to='pretixbase.Event'),
field=models.ForeignKey(verbose_name='Event', to='pretixbase.Event'),
),
migrations.AddField(
model_name='cartposition',
name='item',
field=versions.models.VersionedForeignKey(verbose_name='Item', to='pretixbase.Item'),
field=models.ForeignKey(verbose_name='Item', to='pretixbase.Item'),
),
migrations.AddField(
model_name='cartposition',
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(
model_name='cachedticket',
name='order',
field=versions.models.VersionedForeignKey(to='pretixbase.Order'),
field=models.ForeignKey(to='pretixbase.Order'),
),
migrations.RunPython(initial_user),
]

View File

@@ -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),
),
]

View File

@@ -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'),
),
]

View File

@@ -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'),
),
]

View File

@@ -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),
),
]

View File

@@ -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'),
),
]

View File

@@ -1,5 +1,5 @@
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 .items import (
Item, ItemCategory, ItemVariation, Property, PropertyValue, Question,
@@ -12,7 +12,7 @@ from .orders import (
from .organizer import Organizer, OrganizerPermission, OrganizerSetting
__all__ = [
'Versionable', 'User', 'CachedFile', 'Organizer', 'OrganizerPermission', 'Event', 'EventPermission',
'User', 'CachedFile', 'Organizer', 'OrganizerPermission', 'Event', 'EventPermission',
'ItemCategory', 'Item', 'Property', 'PropertyValue', 'ItemVariation', 'VariationsField', 'Question',
'Quota', 'Order', 'CachedTicket', 'QuestionAnswer', 'ObjectWithAnswers', 'OrderPosition',
'CartPosition', 'EventSetting', 'OrganizerSetting', 'EventLock', 'cachedfile_name', 'itempicture_upload_to',

View File

@@ -1,71 +1,12 @@
import copy
import uuid
from datetime import datetime
import six
from django.db import models
from django.db.models.signals import post_delete
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:
return 'cachedfiles/%s.%s' % (instance.id, filename.split('.')[-1])
return 'cachedfiles/%012d.%s' % (instance.id, filename.split('.')[-1])
class CachedFile(models.Model):

View File

@@ -9,17 +9,15 @@ from django.template.defaultfilters import date as _date
from django.utils.functional import cached_property
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
from versions.models import VersionedForeignKey
from pretix.base.i18n import I18nCharField
from pretix.base.settings import SettingsProxy
from .auth import User
from .base import Versionable
from .organizer import Organizer
class Event(Versionable):
class Event(models.Model):
"""
This model represents an event. An event is anything you can buy
tickets for.
@@ -46,8 +44,7 @@ class Event(Versionable):
:type plugins: str
"""
organizer = VersionedForeignKey(Organizer, related_name="events",
on_delete=models.PROTECT)
organizer = models.ForeignKey(Organizer, related_name="events", on_delete=models.PROTECT)
name = I18nCharField(
max_length=200,
verbose_name=_("Name"),
@@ -184,7 +181,7 @@ class Event(Versionable):
return locking.LockManager(self)
class EventPermission(Versionable):
class EventPermission(models.Model):
"""
The relation between an Event and an User who has permissions to
access an event.
@@ -203,8 +200,8 @@ class EventPermission(Versionable):
:type can_change_orders: bool
"""
event = VersionedForeignKey(Event)
user = models.ForeignKey(User, related_name="event_perms")
event = models.ForeignKey(Event, related_name="user_perms", on_delete=models.CASCADE)
user = models.ForeignKey(User, related_name="event_perms", on_delete=models.CASCADE)
can_change_settings = models.BooleanField(
default=True,
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
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)
value = models.TextField()

View File

@@ -8,16 +8,14 @@ from django.utils.functional import cached_property
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
from typing import List, Tuple
from versions.models import VersionedForeignKey, VersionedManyToManyField
from pretix.base.i18n import I18nCharField, I18nTextField
from ..types import VariationDict
from .base import Versionable
from .event import Event
class ItemCategory(Versionable):
class ItemCategory(models.Model):
"""
Items can be sorted into these categories.
@@ -28,7 +26,7 @@ class ItemCategory(Versionable):
:param position: An integer, used for sorting
:type position: int
"""
event = VersionedForeignKey(
event = models.ForeignKey(
Event,
on_delete=models.CASCADE,
related_name='categories',
@@ -44,7 +42,7 @@ class ItemCategory(Versionable):
class Meta:
verbose_name = _("Product category")
verbose_name_plural = _("Product categories")
ordering = ('position', 'version_birth_date')
ordering = ('position', 'id')
def __str__(self):
return str(self.name)
@@ -61,7 +59,7 @@ class ItemCategory(Versionable):
@property
def sortkey(self):
return self.position, self.version_birth_date
return self.position, self.id
def __lt__(self, other) -> bool:
return self.sortkey < other.sortkey
@@ -69,12 +67,12 @@ class ItemCategory(Versionable):
def itempicture_upload_to(instance, filename: str) -> str:
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]
)
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.
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,
on_delete=models.PROTECT,
related_name="items",
verbose_name=_("Event"),
)
category = VersionedForeignKey(
category = models.ForeignKey(
ItemCategory,
on_delete=models.PROTECT,
related_name="items",
@@ -222,7 +220,7 @@ class Item(Versionable):
for var in all_variations:
key = []
for v in var.values.all():
key.append((v.prop_id, v.identity))
key.append((v.prop_id, v.id))
key = tuple(sorted(key))
variations_cache[key] = var
@@ -234,8 +232,8 @@ class Item(Versionable):
key = []
var = VariationDict()
for v in comb:
key.append((v.prop.identity, v.identity))
var[v.prop.identity] = v
key.append((v.prop.id, v.id))
var[v.prop.id] = v
key = tuple(sorted(key))
if key in variations_cache:
var['variation'] = variations_cache[key]
@@ -245,7 +243,7 @@ class Item(Versionable):
return result
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:
variations = [VariationDict()]
else:
@@ -261,11 +259,11 @@ class Item(Versionable):
values = list(var.values.all())
# Make sure we don't expose stale ItemVariation objects which are
# 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
vardict = VariationDict()
for v in values:
vardict[v.prop.identity] = v
vardict[v.prop.id] = v
vardict['variation'] = var
variations.append(vardict)
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))
class Property(Versionable):
class Property(models.Model):
"""
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'.
@@ -336,11 +334,11 @@ class Property(Versionable):
:type name: str
"""
event = VersionedForeignKey(
event = models.ForeignKey(
Event,
related_name="properties"
)
item = VersionedForeignKey(
item = models.ForeignKey(
Item, related_name='properties', null=True, blank=True
)
name = I18nCharField(
@@ -366,7 +364,7 @@ class Property(Versionable):
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',
this could be 'M' or 'L'.
@@ -379,7 +377,7 @@ class PropertyValue(Versionable):
:type position: int
"""
prop = VersionedForeignKey(
prop = models.ForeignKey(
Property,
on_delete=models.CASCADE,
related_name="values"
@@ -395,7 +393,7 @@ class PropertyValue(Versionable):
class Meta:
verbose_name = _("Property value")
verbose_name_plural = _("Property values")
ordering = ("position", "version_birth_date")
ordering = ("position", "id")
def __str__(self):
return "%s: %s" % (self.prop.name, self.value)
@@ -412,13 +410,13 @@ class PropertyValue(Versionable):
@property
def sortkey(self) -> Tuple[int, datetime]:
return self.position, self.version_birth_date
return self.position, self.id
def __lt__(self, other) -> bool:
return self.sortkey < other.sortkey
class ItemVariation(Versionable):
class ItemVariation(models.Model):
"""
A variation is an item combined with values for all properties
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
:type default_price: decimal.Decimal
"""
item = VersionedForeignKey(
item = models.ForeignKey(
Item,
related_name='variations'
)
values = VersionedManyToManyField(
values = models.ManyToManyField(
PropertyValue,
related_name='variations',
)
@@ -495,7 +493,7 @@ class ItemVariation(Versionable):
"""
vd = VariationDict()
for v in self.values.all():
vd[v.prop.identity] = v
vd[v.prop.id] = v
vd['variation'] = self
return vd
@@ -507,14 +505,14 @@ class ItemVariation(Versionable):
for pair in pk.split(","):
prop, value = pair.split(":")
self.values.add(
PropertyValue.objects.current.get(
identity=value,
PropertyValue.objects.get(
id=value,
prop_id=prop
)
)
class VariationsField(VersionedManyToManyField):
class VariationsField(models.ManyToManyField):
"""
This is a ManyToManyField using the pretixcontrol.views.forms.VariationsField
form field by default.
@@ -536,12 +534,12 @@ class VariationsField(VersionedManyToManyField):
initial = defaults['initial']
if callable(initial):
initial = initial()
defaults['initial'] = [i.identity for i in initial]
defaults['initial'] = [i.id for i in initial]
# Skip ManyToManyField in dependency chain
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
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")),
)
event = VersionedForeignKey(
event = models.ForeignKey(
Event,
related_name="questions"
)
@@ -589,7 +587,7 @@ class Question(Versionable):
default=False,
verbose_name=_("Required question")
)
items = VersionedManyToManyField(
items = models.ManyToManyField(
Item,
related_name='questions',
verbose_name=_("Products"),
@@ -615,7 +613,7 @@ class Question(Versionable):
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
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_OK = 100
event = VersionedForeignKey(
event = models.ForeignKey(
Event,
on_delete=models.CASCADE,
related_name="quotas",
@@ -681,7 +679,7 @@ class Quota(Versionable):
null=True, blank=True,
help_text=_("Leave empty for an unlimited number of tickets.")
)
items = VersionedManyToManyField(
items = models.ManyToManyField(
Item,
verbose_name=_("Item"),
related_name="quotas",
@@ -745,7 +743,7 @@ class Quota(Versionable):
def count_in_cart(self) -> int:
from pretix.base.models import CartPosition
return CartPosition.objects.current.filter(
return CartPosition.objects.filter(
Q(expires__gte=now())
& self._position_lookup
).count()
@@ -753,7 +751,7 @@ class Quota(Versionable):
def count_orders(self) -> dict:
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(
Case(When(order__status=Order.STATUS_PAID, then=1),
output_field=models.IntegerField())

View File

@@ -6,9 +6,8 @@ from django.db import models
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
from typing import List, Union
from versions.models import VersionedForeignKey
from .base import CachedFile, Versionable
from .base import CachedFile
from .event import Event
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))
class Order(Versionable):
class Order(models.Model):
"""
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
@@ -83,7 +82,7 @@ class Order(Versionable):
choices=STATUS_CHOICE,
verbose_name=_("Status")
)
event = VersionedForeignKey(
event = models.ForeignKey(
Event,
verbose_name=_("Event"),
related_name="orders"
@@ -180,13 +179,11 @@ class Order(Versionable):
def mark_refunded(self):
"""
Mark this order as refunded. This clones the order object, sets the payment status and
returns the cloned order object.
Mark this order as refunded. This sets the payment status and returns the order object.
"""
order = self.clone()
order.status = Order.STATUS_REFUNDED
order.save()
return order
self.status = Order.STATUS_REFUNDED
self.save()
return self
def _can_be_paid(self) -> Union[bool, str]:
error_messages = {
@@ -223,12 +220,12 @@ class Order(Versionable):
for quota in quotas:
# Lock the quota, so no other thread is allowed to perform sales covered by this
# quota while we're doing so.
if quota.identity not in quota_cache:
quota_cache[quota.identity] = quota
if quota.id not in quota_cache:
quota_cache[quota.id] = quota
quota.cached_availability = quota.availability()[1]
else:
# Use cached version
quota = quota_cache[quota.identity]
quota = quota_cache[quota.id]
if quota.cached_availability is not None:
quota.cached_availability -= 1
if quota.cached_availability < 0:
@@ -240,12 +237,12 @@ class Order(Versionable):
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)
provider = models.CharField(max_length=255)
class QuestionAnswer(Versionable):
class QuestionAnswer(models.Model):
"""
The answer to a Question, connected to an OrderPosition or CartPosition.
@@ -268,7 +265,7 @@ class QuestionAnswer(Versionable):
'CartPosition', null=True, blank=True,
related_name='answers'
)
question = VersionedForeignKey(
question = models.ForeignKey(
Question, related_name='answers'
)
answer = models.TextField()
@@ -286,14 +283,14 @@ class ObjectWithAnswers:
self.answ[a.question_id] = a.answer
self.questions = []
for q in self.item.questions.all():
if q.identity in self.answ:
q.answer = self.answ[q.identity]
if q.id in self.answ:
q.answer = self.answ[q.id]
else:
q.answer = ""
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
of a specified type (or variation).
@@ -309,20 +306,23 @@ class OrderPosition(ObjectWithAnswers, Versionable):
:param attendee_name: The attendee's name, if entered.
:type attendee_name: str
"""
order = VersionedForeignKey(
order = models.ForeignKey(
Order,
verbose_name=_("Order"),
related_name='positions'
related_name='positions',
on_delete=models.PROTECT
)
item = VersionedForeignKey(
item = models.ForeignKey(
Item,
verbose_name=_("Item"),
related_name='positions'
related_name='positions',
on_delete=models.PROTECT
)
variation = VersionedForeignKey(
variation = models.ForeignKey(
ItemVariation,
null=True, blank=True,
verbose_name=_("Variation")
verbose_name=_("Variation"),
on_delete=models.PROTECT
)
price = models.DecimalField(
decimal_places=2, max_digits=10,
@@ -347,17 +347,16 @@ class OrderPosition(ObjectWithAnswers, Versionable):
order=order, item=cartpos.item, variation=cartpos.variation,
price=cartpos.price, attendee_name=cartpos.attendee_name
)
op.save()
for answ in cartpos.answers.all():
answ = answ.clone()
answ.orderposition = op
answ.cartposition = None
answ.save()
op.save()
cartpos.delete()
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
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.
:type attendee_name: str
"""
event = VersionedForeignKey(
event = models.ForeignKey(
Event,
verbose_name=_("Event")
)
@@ -391,14 +390,16 @@ class CartPosition(ObjectWithAnswers, Versionable):
max_length=255, null=True, blank=True,
verbose_name=_("Cart ID (e.g. session key)")
)
item = VersionedForeignKey(
item = models.ForeignKey(
Item,
verbose_name=_("Item")
verbose_name=_("Item"),
on_delete=models.CASCADE
)
variation = VersionedForeignKey(
variation = models.ForeignKey(
ItemVariation,
null=True, blank=True,
verbose_name=_("Variation")
verbose_name=_("Variation"),
on_delete=models.CASCADE
)
price = models.DecimalField(
decimal_places=2, max_digits=10,
@@ -421,3 +422,8 @@ class CartPosition(ObjectWithAnswers, Versionable):
class Meta:
verbose_name = _("Cart position")
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
)

View File

@@ -2,15 +2,13 @@ from django.core.validators import RegexValidator
from django.db import models
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from versions.models import VersionedForeignKey
from pretix.base.settings import SettingsProxy
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,
charity, person, …
@@ -72,7 +70,7 @@ class Organizer(Versionable):
return ObjectRelatedCache(self)
class OrganizerPermission(Versionable):
class OrganizerPermission(models.Model):
"""
The relation between an Organizer and an User who has permissions to
access an organizer profile.
@@ -86,7 +84,7 @@ class OrganizerPermission(Versionable):
: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")
can_create_events = models.BooleanField(
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
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)
value = models.TextField()

View File

@@ -258,9 +258,7 @@ class BasePaymentProvider:
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
you might want to store for later usage. Please note, that if you want to store
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
you might want to store for later usage. Please note that ``mark_order_paid`` might
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
to display a meaningful error to the user.
@@ -403,7 +401,7 @@ class FreeOrderProvider(BasePaymentProvider):
pass
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)
).aggregate(sum=Sum('price'))['sum'] == 0
@@ -446,7 +444,7 @@ class FreeOrderProvider(BasePaymentProvider):
messages.success(request, _('The order has been marked as refunded.'))
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
).aggregate(sum=Sum('price'))['sum'] == 0

View File

@@ -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
# cart expire at the same time
# 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())
).update(expires=expiry)
@@ -44,7 +44,7 @@ def _re_add_expired_positions(items: List[CartPosition], event: Event, cart_id:
positions = set()
# 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!
expired = CartPosition.objects.current.filter(
expired = CartPosition.objects.filter(
Q(cart_id=cart_id) & Q(event=event) & Q(expires__lte=now())
)
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:
for cp in expired:
if cp.version_end_date is None:
if cp.expires <= now():
cp.delete()
@@ -66,19 +66,19 @@ def _check_date(event: Event) -> None:
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]:
err = None
# 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")
items_cache = {i.identity: i for i in items_query}
variations_query = ItemVariation.objects.current.filter(
items_cache = {i.id: i for i in items_query}
variations_query = ItemVariation.objects.filter(
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")
variations_cache = {v.identity: v for v in variations_query}
variations_cache = {v.id: v for v in variations_query}
for i in items:
# 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):
if len(i) > 3 and i[2] == 1:
# Recreating
cp = i[3].clone()
cp = i[3]
cp.expires = expiry
cp.price = item.default_price if variation is None else (
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
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():
_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):
# TODO: i18n plurals
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)
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.
: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
:raises CartError: On any error that occured
"""
event = Event.objects.current.get(identity=event)
event = Event.objects.get(id=event)
try:
_add_items_to_cart(event, items, cart_id)
except EventLock.LockTimeoutException:
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.
:param event: The event ID in question
:param items: A list of tuple of the form (item id, variation id or None, number)
: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:
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)
else:
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()
@@ -190,8 +190,8 @@ if settings.HAS_CELERY:
from pretix.celery import app
@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):
event = Event.objects.current.get(identity=event)
def add_items_to_cart_task(self, event: int, items: List[Tuple[int, Optional[int], int]], cart_id: str):
event = Event.objects.get(id=event)
try:
_add_items_to_cart(event, items, cart_id)
except EventLock.LockTimeoutException:

View File

@@ -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:
event = Event.objects.current.get(identity=event)
event = Event.objects.get(id=event)
file = CachedFile.objects.get(id=fileid)
responses = register_data_exporters.send(event)
for receiver, response in responses:

View File

@@ -64,13 +64,13 @@ def lock_event_db(event):
for i in range(retries):
with transaction.atomic():
dt = now()
l, created = EventLock.objects.get_or_create(event=event.identity)
l, created = EventLock.objects.get_or_create(event=event.id)
if created:
event._lock = l
return True
elif l.date < now() - timedelta(seconds=LOCK_TIMEOUT):
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:
l.token = newtoken
event._lock = l
@@ -84,7 +84,7 @@ def release_event_db(event):
if not hasattr(event, '_lock') or not event._lock:
raise EventLock.LockReleaseException('Lock is not owned by this thread')
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()
event._lock = None
except EventLock.DoesNotExist:
@@ -97,7 +97,7 @@ def redis_lock_from_event(event):
if not hasattr(event, '_lock') or not event._lock:
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

View File

@@ -33,8 +33,8 @@ error_messages = {
def mark_order_paid(order: Order, provider: str=None, info: str=None, date: datetime=None, manual: bool=None,
force: bool=False) -> Order:
"""
Marks an order as paid. This clones the order object, sets the payment provider,
info and date and returns the cloned order object.
Marks an order as paid. This sets the payment provider, info and date and returns
the order object.
:param provider: The payment provider that marked this as paid
: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()
if not force and can_be_paid is not True:
raise Quota.QuotaExceededException(can_be_paid)
order = order.clone()
order.payment_provider = provider or order.payment_provider
order.payment_info = info or order.payment_info
order.payment_date = date or now()
@@ -110,7 +109,6 @@ def _check_positions(event: Event, dt: datetime, positions: List[CartPosition]):
cp.delete()
continue
if price != cp.price:
cp = cp.clone()
positions[i] = cp
cp.price = price
cp.save()
@@ -125,7 +123,6 @@ def _check_positions(event: Event, dt: datetime, positions: List[CartPosition]):
quota_ok = False
break
if quota_ok:
cp = cp.clone()
positions[i] = cp
cp.expires = now() + timedelta(
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],
email: str, locale: str):
event = Event.objects.current.get(identity=event)
event = Event.objects.get(id=event)
responses = register_payment_providers.send(event)
pprov = None
for receiver, response in responses:
@@ -175,8 +172,8 @@ def _perform_order(event: str, payment_provider: str, position_ids: List[str],
dt = now()
with event.lock():
positions = list(CartPosition.objects.current.filter(
identity__in=position_ids).select_related('item', 'variation'))
positions = list(CartPosition.objects.filter(
id__in=position_ids).select_related('item', 'variation'))
if len(position_ids) != len(positions):
raise OrderError(error_messages['internal'])
_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
)
return order.identity
return order.id
def perform_order(event: str, payment_provider: str, positions: List[str],

View File

@@ -37,28 +37,28 @@ def order_overview(event: Event) -> Tuple[List[Tuple[ItemCategory, List[Item]]],
num_total = {
(p['item'], p['variation']): (p['cnt'], p['price'])
for p in (OrderPosition.objects.current
for p in (OrderPosition.objects
.filter(order__event=event)
.values('item', 'variation')
.annotate(cnt=Count('id'), price=Sum('price')).order_by())
}
num_cancelled = {
(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)
.values('item', 'variation')
.annotate(cnt=Count('id'), price=Sum('price')).order_by())
}
num_refunded = {
(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)
.values('item', 'variation')
.annotate(cnt=Count('id'), price=Sum('price')).order_by())
}
num_pending = {
(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__in=(Order.STATUS_PENDING, Order.STATUS_EXPIRED))
.values('item', 'variation')
@@ -66,7 +66,7 @@ def order_overview(event: Event) -> Tuple[List[Tuple[ItemCategory, List[Item]]],
}
num_paid = {
(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)
.values('item', 'variation')
.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(),
key=lambda vd: vd.ordered_values())
for var in item.all_variations:
variid = var['variation'].identity if 'variation' in var else None
var.num_total = num_total.get((item.identity, variid), (0, 0))
var.num_pending = num_pending.get((item.identity, variid), (0, 0))
var.num_cancelled = num_cancelled.get((item.identity, variid), (0, 0))
var.num_refunded = num_refunded.get((item.identity, variid), (0, 0))
var.num_paid = num_paid.get((item.identity, variid), (0, 0))
variid = var['variation'].id if 'variation' in var else None
var.num_total = num_total.get((item.id, variid), (0, 0))
var.num_pending = num_pending.get((item.id, variid), (0, 0))
var.num_cancelled = num_cancelled.get((item.id, variid), (0, 0))
var.num_refunded = num_refunded.get((item.id, variid), (0, 0))
var.num_paid = num_paid.get((item.id, variid), (0, 0))
item.has_variations = (len(item.all_variations) != 1
or not item.all_variations[0].empty())
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
# 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:
@@ -116,35 +117,35 @@ def order_overview(event: Event) -> Tuple[List[Tuple[ItemCategory, List[Item]]],
payment_items = []
num_total = {
o['payment_provider']: (o['cnt'], o['payment_fee'])
for o in (Order.objects.current
for o in (Order.objects
.filter(event=event)
.values('payment_provider')
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
}
num_cancelled = {
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)
.values('payment_provider')
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
}
num_refunded = {
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)
.values('payment_provider')
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
}
num_pending = {
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))
.values('payment_provider')
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
}
num_paid = {
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)
.values('payment_provider')
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())

View File

@@ -9,7 +9,7 @@ from pretix.base.signals import register_ticket_outputs
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]
if not ct.cachedfile:
cf = CachedFile()

View File

@@ -8,7 +8,6 @@ from django.core.files import File
from django.core.files.storage import default_storage
from django.db.models import Model
from typing import Any, Callable, Dict, Optional, TypeVar, Union
from versions.models import Versionable
DEFAULTS = {
'max_items_per_order': {
@@ -115,7 +114,7 @@ class SettingsProxy:
def _cache(self) -> Dict[str, Any]:
if self._cached_obj is None:
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
return self._cached_obj
@@ -146,8 +145,6 @@ class SettingsProxy:
return dateutil.parser.parse(value).date()
elif as_type == 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):
return as_type.objects.get(pk=value)
return value
@@ -162,8 +159,6 @@ class SettingsProxy:
return json.dumps(value)
elif isinstance(value, datetime) or isinstance(value, date) or isinstance(value, time):
return value.isoformat()
elif isinstance(value, Versionable):
return value.identity
elif isinstance(value, Model):
return value.pk
elif isinstance(value, File):
@@ -213,7 +208,6 @@ class SettingsProxy:
def set(self, key: str, value: Any) -> None:
if key in self._cache():
s = self._cache()[key]
s = s.clone()
else:
s = self._type(object=self._obj, key=key)
s.value = self._serialize(value)
@@ -237,7 +231,7 @@ class SettingsSandbox:
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._type = type
self._key = key

View File

@@ -14,7 +14,7 @@ from django.utils.html import format_html
from django.utils.safestring import mark_safe
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
@@ -59,9 +59,9 @@ class I18nFormSet(BaseModelFormSet):
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
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
@@ -105,15 +105,15 @@ def selector(values, prop):
# properties they belong to EXCEPT the value for the property prop2.
# We'll see later why we need this.
return [
v.identity for v in sorted(values, key=lambda v: v.prop.identity)
if v.prop.identity != prop.identity
v.id for v in sorted(values, key=lambda v: v.prop.id)
if v.prop.id != prop.id
]
def sort(v, prop):
# Given a list of variations, this will sort them by their position
# on the x-axis
return v[prop.identity].sortkey
return v[prop.id].sortkey
class VariationsFieldRenderer(forms.widgets.CheckboxFieldRenderer):
@@ -175,7 +175,7 @@ class VariationsFieldRenderer(forms.widgets.CheckboxFieldRenderer):
final_attrs['checked'] = 'checked'
w = self.choice_input_class(
self.name, self.value, self.attrs.copy(),
(variation['key'], variation[properties[0].identity].value),
(variation['key'], variation[properties[0].id].value),
i
)
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):
# prop1 is the property on all the grid's y-axes
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 = properties[1]
prop2v = list(prop2.values.current.all())
prop2v = list(prop2.values.all())
# We now iterate over the cartesian product of all the other
# properties which are NOT on the axes of the grid because we
# 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:
output.append('<strong>')
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)
return (
(
v['variation'].identity if 'variation' in v else v.key(),
v['variation'].id if 'variation' in v else v.key(),
v
) for v in variations
)
@@ -312,10 +312,10 @@ class VariationsField(forms.ModelMultipleChoiceField):
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
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:
if force_text(val) not in pks:
raise ValidationError(
@@ -335,7 +335,7 @@ class VariationsField(forms.ModelMultipleChoiceField):
# which uses a very similar method
all_variations = self.item.variations.all().prefetch_related("values")
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 = []
@@ -360,7 +360,7 @@ class VariationsField(forms.ModelMultipleChoiceField):
# No ItemVariation present, create one!
var = ItemVariation()
var.item_id = self.item.identity
var.item_id = self.item.id
var.save()
# Add the values to the ItemVariation object
try:
@@ -371,8 +371,8 @@ class VariationsField(forms.ModelMultipleChoiceField):
code='invalid_pk_value',
params={'pk': value},
)
variations_cache[key] = var.identity
cleaned_value.append(str(var.identity))
variations_cache[key] = var.id
cleaned_value.append(str(var.id))
else:
# An ItemVariation id was given
cleaned_value.append(pk)

View File

@@ -4,11 +4,11 @@ from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
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
class EventCreateForm(VersionedModelForm):
class EventCreateForm(I18nModelForm):
error_messages = {
'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
class EventUpdateForm(VersionedModelForm):
class EventUpdateForm(I18nModelForm):
def clean_slug(self):
return self.instance.slug

View File

@@ -1,19 +1,18 @@
import copy
from django import forms
from django.db import models
from django.forms import BooleanField
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 (
Item, ItemCategory, ItemVariation, Property, PropertyValue, Question,
Quota, Versionable,
Quota,
)
from pretix.control.forms import TolerantFormsetModelForm, VariationsField
class CategoryForm(VersionedModelForm):
class CategoryForm(I18nModelForm):
class Meta:
model = ItemCategory
localized_fields = '__all__'
@@ -22,7 +21,7 @@ class CategoryForm(VersionedModelForm):
]
class PropertyForm(VersionedModelForm):
class PropertyForm(I18nModelForm):
class Meta:
model = Property
localized_fields = '__all__'
@@ -40,10 +39,10 @@ class PropertyValueForm(TolerantFormsetModelForm):
]
class QuestionForm(VersionedModelForm):
class QuestionForm(I18nModelForm):
def __init__(self, *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:
model = Question
@@ -60,10 +59,6 @@ class QuestionForm(VersionedModelForm):
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):
items = kwargs['items']
@@ -72,7 +67,7 @@ class QuotaForm(I18nModelForm):
self.original_instance = copy.copy(instance) if instance else None
super().__init__(**kwargs)
if hasattr(self, 'instance'):
if hasattr(self, 'instance') and self.instance.pk:
active_items = set(self.instance.items.all())
active_variations = set(self.instance.variations.all())
else:
@@ -81,36 +76,19 @@ class QuotaForm(I18nModelForm):
for item in items:
if len(item.properties.all()) > 0:
self.fields['item_%s' % item.identity] = VariationsField(
self.fields['item_%s' % item.id] = VariationsField(
item, label=_("Activate for"),
required=False,
initial=active_variations
)
self.fields['item_%s' % item.identity].set_item(item)
self.fields['item_%s' % item.id].set_item(item)
else:
self.fields['item_%s' % item.identity] = BooleanField(
self.fields['item_%s' % item.id] = BooleanField(
label=_("Activate"),
required=False,
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:
model = Quota
localized_fields = '__all__'
@@ -120,10 +98,10 @@ class QuotaForm(I18nModelForm):
]
class ItemFormGeneral(VersionedModelForm):
class ItemFormGeneral(I18nModelForm):
def __init__(self, *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:
model = Item
@@ -142,7 +120,7 @@ class ItemFormGeneral(VersionedModelForm):
]
class ItemVariationForm(VersionedModelForm):
class ItemVariationForm(I18nModelForm):
class Meta:
model = ItemVariation
localized_fields = '__all__'

View File

@@ -1,8 +1,8 @@
from pretix.base.forms import VersionedModelForm
from pretix.base.forms import I18nModelForm
from pretix.base.models import Order
class ExtendForm(VersionedModelForm):
class ExtendForm(I18nModelForm):
class Meta:
model = Order
fields = ['expires']

View File

@@ -1,11 +1,11 @@
from django import forms
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
class OrganizerForm(VersionedModelForm):
class OrganizerForm(I18nModelForm):
error_messages = {
'duplicate_slug': _("This slug is already in use. Please choose a different one."),
}

View File

@@ -54,16 +54,16 @@ class PermissionMiddleware:
return redirect_to_login(
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")
if 'event' in url.kwargs and 'organizer' in url.kwargs:
try:
request.event = Event.objects.current.filter(
request.event = Event.objects.filter(
slug=url.kwargs['event'],
permitted__id__exact=request.user.id,
organizer__slug=url.kwargs['organizer'],
).select_related('organizer')[0]
request.eventperm = EventPermission.objects.current.get(
request.eventperm = EventPermission.objects.get(
event=request.event,
user=request.user
)
@@ -73,7 +73,7 @@ class PermissionMiddleware:
"have no permission to administrate it."))
elif 'organizer' in url.kwargs:
try:
request.organizer = Organizer.objects.current.filter(
request.organizer = Organizer.objects.filter(
slug=url.kwargs['organizer'],
permitted__id__exact=request.user.id,
)[0]

View File

@@ -15,7 +15,7 @@ def event_permission_required(permission):
# just a double check, should not ever happen
raise PermissionDenied()
try:
perm = EventPermission.objects.current.get(
perm = EventPermission.objects.get(
event=request.event,
user=request.user
)
@@ -59,7 +59,7 @@ def organizer_permission_required(permission):
# just a double check, should not ever happen
raise PermissionDenied()
try:
perm = OrganizerPermission.objects.current.get(
perm = OrganizerPermission.objects.get(
organizer=request.organizer,
user=request.user
)

View File

@@ -2,12 +2,12 @@
{% load i18n %}
{% block title %}{{ object.name }} :: {% trans "Product" %}{% endblock %}
{% block content %}
{% if object.identity %}
{% if object.id %}
<h1>{% trans "Modify product:" %} {{ object.name }}</h1>
<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.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.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" == 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.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.id %}">{% trans "Variations" %}</a></li>
</ul>
{% else %}
<h1>{% trans "Create product" %}</h1>
@@ -15,7 +15,7 @@
You will be able to adjust further settings in the next step.
{% endblocktrans %}</p>
{% endif %}
{% if object.identity and not object.quotas.exists %}
{% if object.id and not object.quotas.exists %}
<div class="alert alert-warning">
{% blocktrans trimmed %}
Please note, that your product will <strong>not</strong> be available for sale until you added your

View File

@@ -18,12 +18,12 @@
<tbody>
{% for c in categories %}
<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>
<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.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.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.id %}" class="btn btn-default btn-sm {% if forloop.revcounter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-down"></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.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>
{% endfor %}
</tbody>

View File

@@ -32,15 +32,15 @@
<td><strong>
{% if not i.active %}<strike>{% endif %}
<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 %}
</strong></td>
<td>{% if i.category %}{{ i.category.name }}{% endif %}</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.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.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.id %}" class="btn btn-default btn-sm {% if forloop.revcounter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-down"></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.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>
{% endfor %}
{% endfor %}

View File

@@ -19,10 +19,10 @@
{% for q in questions %}
<tr>
<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 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>
{% endfor %}
</tbody>

View File

@@ -25,12 +25,12 @@
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion"
href="#collapse{{ item.identity }}">
href="#collapse{{ item.id }}">
{{ item.name }}
</a>
</h4>
</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="form-horizontal">
{% bootstrap_field item.field layout="horizontal" %}

View File

@@ -20,19 +20,19 @@
<tbody>
{% for q in quotas %}
<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>
<ul>
{% for item in q.items.all %}
<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>
{% endfor %}
</ul>
</td>
<td>{{ q.size }}</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>
{% endfor %}
</tbody>

View File

@@ -25,8 +25,8 @@
<select name="item" class="form-control">
<option value="">{% trans "All products" %}</option>
{% for item in items %}
<option value="{{ item.identity }}"
{% if request.GET.item == item.identity %}selected="selected"{% endif %}>
<option value="{{ item.id }}"
{% if request.GET.item == item.id %}selected="selected"{% endif %}>
{{ item.name }}
</option>
{% endfor %}

View File

@@ -40,27 +40,27 @@
<tr class="item {% if tup.0 %}categorized{% endif %}">
<td>{{ item.name }}</td>
<td>
<a href="{{ listurl }}?item={{ item.identity }}">
<a href="{{ listurl }}?item={{ item.id }}">
{{ item.num_total|togglesum }}
</a>
</td>
<td>
<a href="{{ listurl }}?item={{ item.identity }}&amp;status=n">
<a href="{{ listurl }}?item={{ item.id }}&amp;status=n">
{{ item.num_pending|togglesum }}
</a>
</td>
<td>
<a href="{{ listurl }}?item={{ item.identity }}&amp;status=c">
<a href="{{ listurl }}?item={{ item.id }}&amp;status=c">
{{ item.num_cancelled|togglesum }}
</a>
</td>
<td>
<a href="{{ listurl }}?item={{ item.identity }}&amp;status=r">
<a href="{{ listurl }}?item={{ item.id }}&amp;status=r">
{{ item.num_refunded|togglesum }}
</a>
</td>
<td>
<a href="{{ listurl }}?item={{ item.identity }}&amp;status=p">
<a href="{{ listurl }}?item={{ item.id }}&amp;status=p">
{{ item.num_paid|togglesum }}
</a>
</td>

View File

@@ -27,32 +27,32 @@ urlpatterns = [
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/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>[0-9a-f-]+)/variations$', item.ItemVariations.as_view(),
url(r'^items/(?P<item>\d+)/$', item.ItemUpdateGeneral.as_view(), name='event.item'),
url(r'^items/(?P<item>\d+)/variations$', item.ItemVariations.as_view(),
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'),
url(r'^items/(?P<item>[0-9a-f-]+)/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>[0-9a-f-]+)/delete$', item.ItemDelete.as_view(), name='event.items.delete'),
url(r'^items/(?P<item>\d+)/up$', item.item_move_up, name='event.items.up'),
url(r'^items/(?P<item>\d+)/down$', item.item_move_down, name='event.items.down'),
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/(?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'),
url(r'^categories/(?P<category>[0-9a-f-]+)/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+)/up$', item.category_move_up, name='event.items.categories.up'),
url(r'^categories/(?P<category>\d+)/down$', item.category_move_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'),
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/(?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'),
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'),
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/(?P<quota>[0-9a-f-]+)/$', 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+)/$', item.QuotaUpdate.as_view(), name='event.items.quotas.edit'),
url(r'^quotas/(?P<quota>\d+)/delete$', item.QuotaDelete.as_view(),
name='event.items.quotas.delete'),
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(),

View File

@@ -12,7 +12,7 @@ from django.views.generic import FormView
from django.views.generic.base import TemplateView
from django.views.generic.detail import SingleObjectMixin
from pretix.base.forms import VersionedModelForm
from pretix.base.forms import I18nModelForm
from pretix.base.models import (
Event, EventPermission, Item, Order, OrderPosition, User,
)
@@ -248,19 +248,19 @@ class TicketSettings(EventPermissionRequiredMixin, FormView):
def index(request, organizer, event):
ctx = {
'products_active': Item.objects.current.filter(
'products_active': Item.objects.filter(
event=request.event,
active=True,
).count(),
'tickets_total': OrderPosition.objects.current.filter(
'tickets_total': OrderPosition.objects.filter(
order__event=request.event,
item__admission=True
).count(),
'tickets_revenue': Order.objects.current.filter(
'tickets_revenue': Order.objects.filter(
event=request.event,
status=Order.STATUS_PAID,
).aggregate(sum=Sum('total'))['sum'],
'tickets_sold': OrderPosition.objects.current.filter(
'tickets_sold': OrderPosition.objects.filter(
order__event=request.event,
order__status=Order.STATUS_PAID,
item__admission=True
@@ -269,7 +269,7 @@ def index(request, organizer, event):
return render(request, 'pretixcontrol/event/index.html', ctx)
class EventPermissionForm(VersionedModelForm):
class EventPermissionForm(I18nModelForm):
class Meta:
model = EventPermission
fields = (
@@ -297,7 +297,7 @@ class EventPermissions(EventPermissionRequiredMixin, TemplateView):
)
return fs(data=self.request.POST if self.request.method == "POST" else None,
prefix="formset",
queryset=EventPermission.objects.current.filter(event=self.request.event))
queryset=EventPermission.objects.filter(event=self.request.event))
@cached_property
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_id = self.add_form.instance.user.id
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:
messages.error(self.request, _('There is no user with the email address you entered.'))
return self.get(*args, **kwargs)
else:
if EventPermission.objects.current.filter(user=self.add_form.instance.user,
event=self.request.event).exists():
if EventPermission.objects.filter(user=self.add_form.instance.user,
event=self.request.event).exists():
messages.error(self.request, _('This user already has permissions for this event.'))
return self.get(*args, **kwargs)
self.add_form.save()

View File

@@ -18,8 +18,7 @@ from pretix.base.models import (
Quota,
)
from pretix.control.forms import (
I18nInlineFormSet, NestedInnerI18nInlineFormSet, VariationsField,
nestedformset_factory,
NestedInnerI18nInlineFormSet, VariationsField, nestedformset_factory,
)
from pretix.control.forms.item import (
CategoryForm, ItemFormGeneral, ItemVariationForm, PropertyForm,
@@ -28,7 +27,6 @@ from pretix.control.forms.item import (
from pretix.control.permissions import (
EventPermissionRequiredMixin, event_permission_required,
)
from pretix.control.signals import restriction_formset
from . import CreateView, UpdateView
@@ -42,7 +40,7 @@ class ItemList(ListView):
template_name = 'pretixcontrol/items/index.html'
def get_queryset(self):
return Item.objects.current.filter(
return Item.objects.filter(
event=self.request.event
).prefetch_related("category")
@@ -54,12 +52,12 @@ def item_move(request, item, up=True):
all items for this category in a new order.
"""
try:
item = request.event.items.current.get(
identity=item
item = request.event.items.get(
id=item
)
except Item.DoesNotExist:
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)
if index != 0 and up:
@@ -70,7 +68,7 @@ def item_move(request, item, up=True):
for i, item in enumerate(items):
if 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.'))
@@ -99,16 +97,15 @@ class CategoryDelete(EventPermissionRequiredMixin, DeleteView):
def get_object(self, queryset=None) -> ItemCategory:
try:
return self.request.event.categories.current.get(
identity=self.kwargs['category']
return self.request.event.categories.get(
id=self.kwargs['category']
)
except ItemCategory.DoesNotExist:
raise Http404(_("The requested product category does not exist."))
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
for item in self.object.items.current.all():
# TODO: Clone!?
for item in self.object.items.all():
item.category = None
item.save()
success_url = self.get_success_url()
@@ -133,8 +130,8 @@ class CategoryUpdate(EventPermissionRequiredMixin, UpdateView):
def get_object(self, queryset=None) -> ItemCategory:
url = resolve(self.request.path_info)
try:
return self.request.event.categories.current.get(
identity=url.kwargs['category']
return self.request.event.categories.get(
id=url.kwargs['category']
)
except ItemCategory.DoesNotExist:
raise Http404(_("The requested product category does not exist."))
@@ -176,7 +173,7 @@ class CategoryList(ListView):
template_name = 'pretixcontrol/items/categories.html'
def get_queryset(self):
return self.request.event.categories.current.all()
return self.request.event.categories.all()
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.
"""
try:
category = request.event.categories.current.get(
identity=category
category = request.event.categories.get(
id=category
)
except ItemCategory.DoesNotExist:
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)
if index != 0 and up:
@@ -202,7 +199,7 @@ def category_move(request, category, up=True):
for i, cat in enumerate(categories):
if 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.'))
@@ -229,7 +226,7 @@ class QuestionList(ListView):
template_name = 'pretixcontrol/items/questions.html'
def get_queryset(self):
return self.request.event.questions.current.all()
return self.request.event.questions.all()
class QuestionDelete(EventPermissionRequiredMixin, DeleteView):
@@ -240,15 +237,15 @@ class QuestionDelete(EventPermissionRequiredMixin, DeleteView):
def get_object(self, queryset=None) -> Question:
try:
return self.request.event.questions.current.get(
identity=self.kwargs['question']
return self.request.event.questions.get(
id=self.kwargs['question']
)
except Question.DoesNotExist:
raise Http404(_("The requested question does not exist."))
def get_context_data(self, *args, **kwargs) -> dict:
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
def delete(self, request, *args, **kwargs):
@@ -274,8 +271,8 @@ class QuestionUpdate(EventPermissionRequiredMixin, UpdateView):
def get_object(self, queryset=None) -> Question:
try:
return self.request.event.questions.current.get(
identity=self.kwargs['question']
return self.request.event.questions.get(
id=self.kwargs['question']
)
except Question.DoesNotExist:
raise Http404(_("The requested question does not exist."))
@@ -321,7 +318,7 @@ class QuotaList(ListView):
template_name = 'pretixcontrol/items/quotas.html'
def get_queryset(self):
return Quota.objects.current.filter(
return Quota.objects.filter(
event=self.request.event
).prefetch_related("items")
@@ -342,32 +339,29 @@ class QuotaEditorMixin:
context = super().get_context_data(*args, **kwargs)
context['items'] = self.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
@transaction.atomic()
def form_valid(self, form):
res = super().form_valid(form)
# The following commented-out checks are not necessary as both self.object.items
# and self.object.variations can be expected empty due to the performance
# optimization of pretixbase.models.Versionable.clone_shallow()
# items = self.object.items.all()
# variations = self.object.variations.all()
items = self.object.items.all()
variations = self.object.variations.all()
selected_variations = []
self.object = form.instance
for item in self.items:
field = form.fields['item_%s' % item.identity]
data = form.cleaned_data['item_%s' % item.identity]
field = form.fields['item_%s' % item.id]
data = form.cleaned_data['item_%s' % item.id]
if isinstance(field, VariationsField):
for v in data:
selected_variations.append(v)
if data: # and item not in items:
if data and item not in items:
self.object.items.add(item)
# elif not data and item in items:
# self.object.items.remove(item)
elif not data and item in items:
self.object.items.remove(item)
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.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])
return res
@@ -399,8 +393,8 @@ class QuotaUpdate(EventPermissionRequiredMixin, QuotaEditorMixin, UpdateView):
def get_object(self, queryset=None) -> Quota:
try:
return self.request.event.quotas.current.get(
identity=self.kwargs['quota']
return self.request.event.quotas.get(
id=self.kwargs['quota']
)
except Quota.DoesNotExist:
raise Http404(_("The requested quota does not exist."))
@@ -424,15 +418,15 @@ class QuotaDelete(EventPermissionRequiredMixin, DeleteView):
def get_object(self, queryset=None) -> Quota:
try:
return self.request.event.quotas.current.get(
identity=self.kwargs['quota']
return self.request.event.quotas.get(
id=self.kwargs['quota']
)
except Quota.DoesNotExist:
raise Http404(_("The requested quota does not exist."))
def get_context_data(self, *args, **kwargs) -> dict:
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
def delete(self, request, *args, **kwargs):
@@ -456,8 +450,8 @@ class ItemDetailMixin(SingleObjectMixin):
def get_object(self, queryset=None) -> Item:
try:
if not hasattr(self, 'object') or not self.object:
self.item = self.request.event.items.current.get(
identity=self.kwargs['item']
self.item = self.request.event.items.get(
id=self.kwargs['item']
)
self.object = self.item
return self.object
@@ -474,7 +468,7 @@ class ItemCreate(EventPermissionRequiredMixin, CreateView):
return reverse('control:event.item', kwargs={
'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug,
'item': self.object.identity,
'item': self.object.id,
})
def form_valid(self, form):
@@ -500,7 +494,7 @@ class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, UpdateVie
return reverse('control:event.item', kwargs={
'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug,
'item': self.get_object().identity,
'item': self.get_object().id,
})
def form_valid(self, form):
@@ -516,7 +510,7 @@ class ItemProperties(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView
return reverse('control:event.item.properties', kwargs={
'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug,
'item': self.get_object().identity,
'item': self.get_object().id,
})
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
)
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)
return formset
@@ -595,16 +589,16 @@ class ItemVariations(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView
form = ItemVariationForm(
data,
instance=variation['variation'],
prefix=",".join([str(i.identity) for i in values]),
prefix=",".join([str(i.id) for i in values]),
)
else:
inst = ItemVariation(item=self.object)
inst.item_id = self.object.identity
inst.item_id = self.object.id
inst.creation = True
form = ItemVariationForm(
data,
instance=inst,
prefix=",".join([str(i.identity) for i in values]),
prefix=",".join([str(i.id) for i in values]),
)
form.values = values
return form
@@ -649,21 +643,21 @@ class ItemVariations(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView
# properties they belong to EXCEPT the value for the property prop2.
# We'll see later why we need this.
return [
v.identity for v in sorted(values, key=lambda v: v.prop.identity)
if v.prop.identity != prop2.identity
v.id for v in sorted(values, key=lambda v: v.prop.id)
if v.prop.id != prop2.id
]
def sort(v):
# Given a list of variations, this will sort them by their position
# 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
# properties which are NOT on the axes of the grid because we
# 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 = []
for val1 in prop1.values.current.all():
for val1 in prop1.values.all():
formrow = []
# 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
@@ -689,7 +683,7 @@ class ItemVariations(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView
def main(self, request, *args, **kwargs):
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.forms, self.forms_flat = self.get_forms()
@@ -730,7 +724,7 @@ class ItemVariations(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView
return reverse('control:event.item.variations', kwargs={
'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug,
'item': self.get_object().identity,
'item': self.get_object().id,
})
def get_context_data(self, **kwargs) -> dict:
@@ -757,8 +751,8 @@ class ItemDelete(EventPermissionRequiredMixin, DeleteView):
def get_object(self, queryset=None) -> Property:
if not hasattr(self, 'object') or not self.object:
try:
self.object = self.request.event.items.current.get(
identity=self.kwargs['item']
self.object = self.request.event.items.get(
id=self.kwargs['item']
)
except Property.DoesNotExist:
raise Http404(_("The requested product does not exist."))

View File

@@ -17,7 +17,7 @@ class EventList(ListView):
template_name = 'pretixcontrol/events/index.html'
def get_queryset(self):
return Event.objects.current.filter(
return Event.objects.filter(
permitted__id__exact=self.request.user.pk
).prefetch_related(
"organizer",
@@ -34,7 +34,7 @@ class EventCreateStart(TemplateView):
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
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
).select_related("organizer")
]

View File

@@ -35,7 +35,7 @@ class OrderList(EventPermissionRequiredMixin, ListView):
permission = 'can_view_orders'
def get_queryset(self):
qs = Order.objects.current.filter(
qs = Order.objects.filter(
event=self.request.event
)
if self.request.GET.get("user", "") != "":
@@ -53,7 +53,7 @@ class OrderList(EventPermissionRequiredMixin, ListView):
def get_context_data(self, **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
@@ -62,7 +62,7 @@ class OrderView(EventPermissionRequiredMixin, DetailView):
model = Order
def get_object(self, queryset=None):
return Order.objects.current.get(
return Order.objects.get(
event=self.request.event,
code=self.kwargs['code'].upper()
)
@@ -170,15 +170,13 @@ class OrderTransition(OrderView):
else:
messages.success(self.request, _('The order has been marked as paid.'))
elif self.order.status == 'n' and to == 'c':
order = self.order.clone()
order.status = Order.STATUS_CANCELLED
order.save()
self.order.status = Order.STATUS_CANCELLED
self.order.save()
messages.success(self.request, _('The order has been cancelled.'))
elif self.order.status == 'p' and to == 'n':
order = self.order.clone()
order.status = Order.STATUS_PENDING
order.payment_manual = True
order.save()
self.order.status = Order.STATUS_PENDING
self.order.payment_manual = True
self.order.save()
messages.success(self.request, _('The order has been marked as not paid.'))
elif self.order.status == 'p' and to == 'r':
ret = self.payment_provider.order_control_refund_perform(self.request, self.order)
@@ -233,7 +231,7 @@ class OrderDownload(OrderView):
ct.cachedfile = cf
ct.save()
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}))
@@ -305,7 +303,7 @@ class OrderGo(EventPermissionRequiredMixin, View):
try:
if code.startswith(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,
code=order.code)
except Order.DoesNotExist:

View File

@@ -17,9 +17,9 @@ class OrganizerList(ListView):
def get_queryset(self):
if self.request.user.is_superuser:
return Organizer.objects.current.all()
return Organizer.objects.all()
else:
return Organizer.objects.current.filter(
return Organizer.objects.filter(
permitted__id__exact=self.request.user.pk
)

View File

@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import versions.models
from django.db import migrations, models
@@ -15,8 +14,12 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='KnownDomain',
fields=[
('domainname', models.CharField(serialize=False, max_length=255, primary_key=True)),
('organizer', versions.models.VersionedForeignKey(blank=True, to='pretixbase.Organizer', null=True)),
('domainname', models.CharField(primary_key=True, max_length=255, serialize=False)),
('organizer', models.ForeignKey(to='pretixbase.Organizer', null=True, blank=True, related_name='domains')),
],
options={
'verbose_name_plural': 'Known domains',
'verbose_name': 'Known domain',
},
),
]

View File

@@ -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),
),
]

View File

@@ -1,13 +1,12 @@
from django.db import models
from django.utils.translation import ugettext_lazy as _
from versions.models import VersionedForeignKey
from pretix.base.models import Organizer
class KnownDomain(models.Model):
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:
verbose_name = _("Known domain")

View File

@@ -12,7 +12,7 @@ def hbci_transactions(event, conf):
log = []
data = []
accname = event.identity + '_' + str(int(time.time()))
accname = event.id + '_' + str(int(time.time()))
try:
try:
subprocess.call([

View File

@@ -50,8 +50,8 @@ class ImportView(EventPermissionRequiredMixin, TemplateView):
return self.process_mt940()
if 'confirm' in self.request.POST:
orders = Order.objects.current.filter(event=self.request.event,
code__in=self.request.POST.getlist('mark_paid'))
orders = Order.objects.filter(event=self.request.event,
code__in=self.request.POST.getlist('mark_paid'))
some_failed = False
for order in orders:
try:
@@ -210,8 +210,7 @@ class ImportView(EventPermissionRequiredMixin, TemplateView):
code = match.group(1)
try:
order = Order.objects.current.get(event=self.request.event,
code=code)
order = Order.objects.get(event=self.request.event, code=code)
except Order.DoesNotExist:
row['class'] = 'danger'
row['message'] = _('Unknown order code detected')

View File

@@ -168,7 +168,6 @@ class Paypal(BasePaymentProvider):
if payment.state == 'pending':
messages.warning(request, _('PayPal has not yet approved the payment. We will inform you as soon as the '
'payment completed.'))
order = order.clone()
order.payment_info = json.dumps(payment.to_dict())
order.save()
return

View File

@@ -23,7 +23,7 @@ def success(request):
request.session['payment_paypal_token'] = token
request.session['payment_paypal_payer'] = payer
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'}))
except Event.DoesNotExist:
pass # TODO: Handle this
@@ -35,7 +35,7 @@ def success(request):
def abort(request):
messages.error(request, _('It looks like you cancelled the PayPal payment'))
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'}))
except Event.DoesNotExist:
pass # TODO: Handle this
@@ -44,7 +44,7 @@ def abort(request):
@login_required
def retry(request, order):
try:
order = Order.objects.current.get(
order = Order.objects.get(
user=request.user,
code=order,
)

View File

@@ -42,7 +42,7 @@ class ConfigView(EventPermissionRequiredMixin, TemplateView):
class ApiView(View):
def get(self, request, **kwargs):
try:
event = Event.objects.current.get(
event = Event.objects.get(
slug=self.kwargs['event'],
organizer__slug=self.kwargs['organizer']
)
@@ -53,12 +53,12 @@ class ApiView(View):
or event.settings.get('pretixdroid_key') != request.GET.get('key', '')):
return HttpResponseForbidden('Invalid key')
ops = OrderPosition.objects.current.filter(
ops = OrderPosition.objects.filter(
order__event=event, order__status=Order.STATUS_PAID,
).select_related('item', 'variation')
data = [
{
'id': op.identity,
'id': op.id,
'item': str(op.item),
'variation': str(op.variation) if op.variation else None,
'attendee_name': op.attendee_name

View File

@@ -25,7 +25,7 @@ class SenderView(EventPermissionRequiredMixin, FormView):
return kwargs
def form_valid(self, form):
orders = Order.objects.current.filter(
orders = Order.objects.filter(
event=self.request.event, status__in=form.cleaned_data['sendto']
).select_related("user")
users = set([o.user for o in orders])

View File

@@ -27,12 +27,12 @@ class IndexView(EventPermissionRequiredMixin, TemplateView):
ctx['obd_data'] = cache.get('statistics_obd_data')
if not ctx['obd_data']:
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()
ordered_by_day[day] = ordered_by_day.get(day, 0) + 1
paid_by_day = {}
for o in Order.objects.current.filter(event=self.request.event,
payment_date__isnull=False).values('payment_date'):
for o in Order.objects.filter(event=self.request.event,
payment_date__isnull=False).values('payment_date'):
day = o['payment_date'].date()
paid_by_day[day] = paid_by_day.get(day, 0) + 1
@@ -59,21 +59,21 @@ class IndexView(EventPermissionRequiredMixin, TemplateView):
if not ctx['obp_data']:
num_ordered = {
p['item']: p['cnt']
for p in (OrderPosition.objects.current
for p in (OrderPosition.objects
.filter(order__event=self.request.event)
.values('item')
.annotate(cnt=Count('id')))
}
num_paid = {
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)
.values('item')
.annotate(cnt=Count('id')))
}
item_names = {
i.identity: str(i.name)
for i in Item.objects.current.filter(event=self.request.event)
i.id: str(i.name)
for i in Item.objects.filter(event=self.request.event)
}
ctx['obp_data'] = [
{
@@ -87,9 +87,9 @@ class IndexView(EventPermissionRequiredMixin, TemplateView):
ctx['rev_data'] = cache.get('statistics_rev_data')
if not ctx['rev_data']:
rev_by_day = {}
for o in Order.objects.current.filter(event=self.request.event,
status=Order.STATUS_PAID,
payment_date__isnull=False).values('payment_date', 'total'):
for o in Order.objects.filter(event=self.request.event,
status=Order.STATUS_PAID,
payment_date__isnull=False).values('payment_date', 'total'):
day = o['payment_date'].date()
rev_by_day[day] = rev_by_day.get(day, 0) + o['total']

View File

@@ -83,18 +83,17 @@ class Stripe(BasePaymentProvider):
currency=request.event.currency.lower(),
source=request.session['payment_stripe_token'],
metadata={
'order': order.identity,
'event': self.event.identity,
'order': order.id,
'event': self.event.id,
'code': order.code
},
# 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:
err = e.json_body['error']
messages.error(request, _('Stripe reported an error with your card: %s' % err['message']))
logger.info('Stripe card error: %s' % str(err))
order = order.clone()
order.payment_info = json.dumps({
'error': True,
'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 '
'with us if this problem persists.'))
logger.error('Stripe error: %s' % str(err))
order = order.clone()
order.payment_info = json.dumps({
'error': True,
'message': err['message'],
@@ -121,7 +119,6 @@ class Stripe(BasePaymentProvider):
else:
messages.warning(request, _('Stripe reported an error: %s' % charge.failure_message))
logger.info('Charge failed: %s' % str(charge))
order = order.clone()
order.payment_info = str(charge)
order.save()
del request.session['payment_stripe_token']

View File

@@ -30,12 +30,12 @@ def webhook(request):
return HttpResponse('Event not given', status=200)
try:
event = Event.objects.current.get(identity=metadata['event'])
event = Event.objects.get(id=metadata['event'])
except Event.DoesNotExist:
return HttpResponse('Event not found', status=200)
try:
order = Order.objects.current.get(identity=metadata['order'])
order = Order.objects.get(id=metadata['order'])
except Order.DoesNotExist:
return HttpResponse('Order not found', status=200)

View File

@@ -69,7 +69,7 @@ class PdfTicketOutput(BaseTicketOutput):
qr_s = self.settings.get('qr_s', default=80, as_type=float)
if qr_s:
reqs = qr_s * units.mm
qrw = QrCodeWidget(op.identity, barLevel='H')
qrw = QrCodeWidget(op.id, barLevel='H')
b = qrw.getBounds()
w = b[2] - b[0]
h = b[3] - b[1]
@@ -84,7 +84,7 @@ class PdfTicketOutput(BaseTicketOutput):
p.setFont("Helvetica", code_s)
code_x = self.settings.get('code_x', default=15, 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()

View File

@@ -168,7 +168,7 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
aw.question_id: aw.answer for aw in cp.answers.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:
messages.warning(request, _('Please fill in answers to all required questions.'))
return False
@@ -193,7 +193,7 @@ class PaymentStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
@cached_property
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)
).aggregate(sum=Sum('price'))['sum']
@@ -300,15 +300,15 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep):
def post(self, request):
self.request = request
return self.do(self.request.event.identity, self.payment_provider.identifier,
[p.identity for p in self.positions], request.session.get('email'),
return self.do(self.request.event.id, self.payment_provider.identifier,
[p.id for p in self.positions], request.session.get('email'),
translation.get_language())
def get_success_message(self, value):
return None
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)
def get_error_message(self, exception):

View File

@@ -42,7 +42,7 @@ class QuestionsForm(forms.Form):
answers = [
a for a
in (cartpos.answers.all() if cartpos else orderpos.answers.all())
if a.question_id == q.identity
if a.question_id == q.id
]
if answers:
initial = answers[0].answer
@@ -73,4 +73,4 @@ class QuestionsForm(forms.Form):
if answers:
# Cache the answer object for later use
field.answer = answers[0]
self.fields['question_%s' % q.identity] = field
self.fields['question_%s' % q.id] = field

View File

@@ -26,7 +26,7 @@ class EventMiddleware:
path = "/" + request.get_full_path().split("/", 2)[-1]
return redirect(path)
request.event = Event.objects.current.filter(
request.event = Event.objects.filter(
slug=url.kwargs['event'],
organizer=request.organizer,
).select_related('organizer')[0]
@@ -34,13 +34,13 @@ class EventMiddleware:
else:
# We are on our main domain
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'],
organizer__slug=url.kwargs['organizer']
).select_related('organizer')[0]
request.organizer = request.event.organizer
elif 'organizer' in url.kwargs:
request.organizer = Organizer.objects.current.filter(
request.organizer = Organizer.objects.filter(
slug=url.kwargs['organizer']
)[0]
else:

View File

@@ -26,7 +26,7 @@
<div class="panel panel-default">
<div class="panel-heading">
<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">
<strong>{{ form.pos.item.name }}</strong>
{% if form.pos.variation %}
@@ -35,7 +35,7 @@
</a>
</h4>
</div>
<div id="cp{{ form.pos.identity }}"
<div id="cp{{ form.pos.id }}"
class="panel-collapse collapsed in">
<div class="panel-body">
{% bootstrap_form form layout="horizontal" %}

View File

@@ -26,10 +26,10 @@
method="post">
{% csrf_token %}
{% 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" />
{% else %}
<input type="hidden" name="item_{{ line.item.identity }}"
<input type="hidden" name="item_{{ line.item.id }}"
value="1" />
{% endif %}
<button class="btn btn-mini btn-link"><i class="fa fa-minus"></i></button>
@@ -41,10 +41,10 @@
method="post" data-asynctask>
{% csrf_token %}
{% 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" />
{% else %}
<input type="hidden" name="item_{{ line.item.identity }}"
<input type="hidden" name="item_{{ line.item.id }}"
value="1" />
{% endif %}
<button class="btn btn-mini btn-link"><i class="fa fa-plus"></i></button>

View File

@@ -65,7 +65,7 @@
{% if item.picture %}
<a href="{{ item.picture.url }}" class="productpicture"
data-title="{{ item.name }}"
data-lightbox="{{ item.identity }}">
data-lightbox="{{ item.id }}">
<img src="{{ item.picture|thumbnail_url:'productlist' }}"
alt="{{ item.name }}" />
</a>
@@ -109,7 +109,7 @@
<div class="col-md-2 col-xs-6 availability-box available">
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
max="{{ var.cached_availability.1 }}"
name="variation_{{ item.identity }}_{{ var.variation.identity }}">
name="variation_{{ item.id }}_{{ var.variation.id }}">
</div>
{% else %}
{% include "pretixpresale/event/fragment_availability.html" with avail=var.cached_availability.0 %}
@@ -125,7 +125,7 @@
{% if item.picture %}
<a href="{{ item.picture.url }}" class="productpicture"
data-title="{{ item.name }}"
data-lightbox="{{ item.identity }}">
data-lightbox="{{ item.id }}">
<img src="{{ item.picture|thumbnail_url:'productlist' }}"
alt="{{ item.name }}" />
</a>
@@ -144,7 +144,7 @@
{% if item.cached_availability.0 == 100 %}
<div class="col-md-2 col-xs-6 availability-box available">
<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>
{% else %}
{% include "pretixpresale/event/fragment_availability.html" with avail=item.cached_availability.0 %}

View File

@@ -15,7 +15,7 @@
<div class="panel panel-default">
<div class="panel-heading">
<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">
<strong>{{ form.pos.item }}</strong>
{% if form.pos.variation %}
@@ -24,7 +24,7 @@
</a>
</h4>
</div>
<div id="cp{{ form.pos.identity }}"
<div id="cp{{ form.pos.id }}"
class="panel-collapse collapsed {% if forloop.counter0 == 0 %}in{% endif %}">
<div class="panel-body">
{% bootstrap_form form layout="horizontal" %}

View File

@@ -14,7 +14,7 @@ class CartMixin:
"""
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
).order_by(
'item', 'variation'
@@ -26,7 +26,7 @@ class CartMixin:
))
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
)
@@ -49,8 +49,8 @@ class CartMixin:
def keyfunc(pos):
if answers and ((pos.item.admission and self.request.event.settings.attendee_names_asked)
or pos.item.questions.all()):
return pos.id, "", "", ""
return "", pos.item_id, pos.variation_id, pos.price
return pos.id, 0, 0, 0
return 0, pos.item_id, pos.variation_id, pos.price
positions = []
for k, g in groupby(sorted(list(cartpos), key=keyfunc), key=keyfunc):

View File

@@ -37,13 +37,13 @@ class CartActionMixin:
continue
if key.startswith('item_'):
try:
items.append((key.split("_")[1], None, int(value)))
items.append((int(key.split("_")[1]), None, int(value)))
except ValueError:
messages.error(self.request, _('Please enter numbers only.'))
return []
elif key.startswith('variation_'):
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:
messages.error(self.request, _('Please enter numbers only.'))
return []
@@ -60,7 +60,7 @@ class CartRemove(EventViewMixin, CartActionMixin, View):
if not items:
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.'))
return redirect(self.get_success_url())
@@ -81,7 +81,7 @@ class CartAdd(EventViewMixin, CartActionMixin, AsyncAction, View):
def post(self, request, *args, **kwargs):
items = self._items_from_post_data()
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:
if 'ajax' in self.request.GET or 'ajax' in self.request.POST:
return JsonResponse({

View File

@@ -59,7 +59,8 @@ class EventIndex(EventViewMixin, CartMixin, TemplateView):
# insert categories into a set for uniqueness
# 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()

View File

@@ -23,8 +23,8 @@ class OrderDetailMixin:
@cached_property
def order(self):
try:
return Order.objects.current.get(secret=self.kwargs['secret'],
event=self.request.event, code=self.kwargs['order'])
return Order.objects.get(secret=self.kwargs['secret'],
event=self.request.event, code=self.kwargs['order'])
except Order.DoesNotExist:
return None
@@ -80,7 +80,7 @@ class OrderDetails(EventViewMixin, OrderDetailMixin, CartMixin, TemplateView):
ctx['download_buttons'] = self.download_buttons
ctx['cart'] = self.get_cart(
answers=True,
queryset=OrderPosition.objects.current.filter(order=self.order)
queryset=OrderPosition.objects.filter(order=self.order)
)
if self.order.status == Order.STATUS_PENDING:
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)
def post(self, request, *args, **kwargs):
order = self.order.clone()
order.status = Order.STATUS_CANCELLED
order.save()
self.order.status = Order.STATUS_CANCELLED
self.order.save()
return redirect(self.get_order_url())
def get(self, request, *args, **kwargs):
@@ -302,5 +301,5 @@ class OrderDownload(EventViewMixin, OrderDetailMixin, View):
cf.save()
ct.cachedfile = cf
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}))

View File

@@ -20,6 +20,6 @@ class OrganizerIndex(OrganizerViewMixin, ListView):
else:
query &= Q(Q(date_from__gte=now()) | Q(date_to__gte=now()))
order = 'date_from'
return Event.objects.current.filter(
return Event.objects.filter(
Q(organizer=self.request.organizer) & query
).order_by(order)

View File

@@ -17,7 +17,7 @@ class QuestionsViewMixin:
cartpos = cr if isinstance(cr, CartPosition) else None
orderpos = cr if isinstance(cr, OrderPosition) else None
form = QuestionsForm(event=self.request.event,
prefix=cr.identity,
prefix=cr.id,
cartpos=cartpos,
orderpos=orderpos,
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
for k, v in form.cleaned_data.items():
if k == 'attendee_name':
form.pos = form.pos.clone()
form.pos.attendee_name = v if v != '' else None
form.pos.save()
elif k.startswith('question_') and v is not None:
@@ -48,7 +47,6 @@ class QuestionsViewMixin:
if v == '':
field.answer.delete()
else:
field.answer = field.answer.clone()
field.answer.answer = v
field.answer.save()
elif v != '':

View File

@@ -4,8 +4,6 @@ python-dateutil>=2.4,<2.5
pytz
django-bootstrap3>=6.1,<6.2
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
reportlab>=3.1.44,<3.2
git+https://github.com/pretix/PyPDF2.git@pretix#egg=PyPDF2

View File

@@ -21,7 +21,7 @@ def event():
def test_locking_exclusive(event):
with event.lock():
with pytest.raises(EventLock.LockTimeoutException):
ev = Event.objects.current.get(identity=event.identity)
ev = Event.objects.get(id=event.id)
with ev.lock():
pass
@@ -42,7 +42,7 @@ def test_lock_timeout_steal(event):
locking.LOCK_TIMEOUT = 5
locking.lock_event(event)
with pytest.raises(EventLock.LockTimeoutException):
ev = Event.objects.current.get(identity=event.identity)
ev = Event.objects.get(id=event.id)
locking.lock_event(ev)
time.sleep(6)
locking.lock_event(ev)

View File

@@ -146,23 +146,6 @@ class ItemVariationsTest(TestCase):
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):
def test_name(self):
u = User.objects.create_user('test@foo.bar', 'test')
@@ -336,14 +319,14 @@ class OrderTestCase(BaseQuotaTestCase):
self.quota.size = 0
self.quota.save()
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)
def test_paid_expired_available(self):
self.order.expires = now() - timedelta(days=2)
self.order.save()
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)
def test_paid_expired_partial(self):
@@ -356,7 +339,7 @@ class OrderTestCase(BaseQuotaTestCase):
self.assertFalse(True, 'This should have raised an exception.')
except Quota.QuotaExceededException:
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))
def test_paid_expired_unavailable(self):
@@ -369,7 +352,7 @@ class OrderTestCase(BaseQuotaTestCase):
self.assertFalse(True, 'This should have raised an exception.')
except Quota.QuotaExceededException:
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))
def test_paid_expired_unavailable_force(self):
@@ -378,7 +361,7 @@ class OrderTestCase(BaseQuotaTestCase):
self.quota.size = 0
self.quota.save()
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)
def test_can_modify_answers(self):

View File

@@ -29,7 +29,7 @@ class SettingsTestCase(TestCase):
self.assertEqual(self.organizer.settings.test, 'foo')
# 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')
def test_event_set_explicit(self):
@@ -37,7 +37,7 @@ class SettingsTestCase(TestCase):
self.assertEqual(self.event.settings.test, 'foo')
# 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')
def test_event_set_twice(self):
@@ -46,7 +46,7 @@ class SettingsTestCase(TestCase):
self.assertEqual(self.event.settings.test, 'foo')
# 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')
def test_event_set_on_organizer(self):
@@ -55,18 +55,7 @@ class SettingsTestCase(TestCase):
self.assertEqual(self.event.settings.test, 'foo')
# Reload object
self.organizer = Organizer.objects.get(identity=self.organizer.identity)
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'
self.organizer = Organizer.objects.get(id=self.organizer.id)
def test_override_organizer(self):
self.organizer.settings.test = 'foo'
@@ -75,8 +64,8 @@ class SettingsTestCase(TestCase):
self.assertEqual(self.event.settings.test, 'bar')
# Reload object
self.organizer = Organizer.objects.get(identity=self.organizer.identity)
self.event = Event.objects.get(identity=self.event.identity)
self.organizer = Organizer.objects.get(id=self.organizer.id)
self.event = Event.objects.get(id=self.event.id)
self.assertEqual(self.organizer.settings.test, 'foo')
self.assertEqual(self.event.settings.test, 'bar')
@@ -103,13 +92,13 @@ class SettingsTestCase(TestCase):
del self.event.settings.test
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')
del 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)
def test_serialize_str(self):
@@ -196,7 +185,7 @@ class SettingsTestCase(TestCase):
sandbox['bar'] = 'baz'
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)
self.assertEqual(sandbox['bar'], 'baz')
self.assertEqual(sandbox.baz, '42')

View File

@@ -26,16 +26,9 @@ class EventsTest(BrowserTest):
organizer=self.orga2, name='MRMCD14', slug='mrmcd14',
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)
EventPermission.objects.create(event=self.event1, user=self.user, can_change_items=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.get('%s%s' % (self.live_server_url, '/control/login'))
username_input = self.driver.find_element_by_name("email")
@@ -51,7 +44,6 @@ class EventsTest(BrowserTest):
self.assertIn("30C3", tabletext)
self.assertNotIn("31C3", tabletext)
self.assertNotIn("MRMCD14", tabletext)
self.assertNotIn("MRMCD00", tabletext)
def test_settings(self):
self.driver.get('%s/control/event/%s/%s/settings/' % (self.live_server_url, self.orga1.slug,

View File

@@ -49,9 +49,8 @@ class CategoriesTest(ItemFormTest):
def test_update(self):
c = ItemCategory.objects.create(event=self.event1, name="Entry tickets")
t1 = now()
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").send_keys('T-Shirts')
@@ -59,8 +58,7 @@ class CategoriesTest(ItemFormTest):
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.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.current.get(identity=c.identity).name) == 'T-Shirts'
assert str(ItemCategory.objects.get(id=c.id).name) == 'T-Shirts'
@unittest.skipIf('TRAVIS' in os.environ, 'See docstring for details.')
def test_sort(self):
@@ -98,15 +96,13 @@ class CategoriesTest(ItemFormTest):
def test_delete(self):
c = ItemCategory.objects.create(event=self.event1, name="Entry tickets")
t1 = now()
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("alert-success")
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.current.filter(identity=c.identity).exists()
assert not ItemCategory.objects.filter(id=c.id).exists()
class PropertiesTest(ItemFormTest):
@@ -134,9 +130,8 @@ class PropertiesTest(ItemFormTest):
c = Property.objects.create(event=self.event1, name="Size")
p1 = PropertyValue.objects.create(prop=c, position=0, value="S")
p2 = PropertyValue.objects.create(prop=c, position=1, value="M")
t1 = now()
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").send_keys('Color')
@@ -150,25 +145,20 @@ class PropertiesTest(ItemFormTest):
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("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(PropertyValue.objects.as_of(t1).get(identity=p2.identity).value) == 'M'
assert str(PropertyValue.objects.current.get(identity=p2.identity).value) == 'red'
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'
assert str(Property.objects.get(id=c.id).name) == 'Color'
assert str(PropertyValue.objects.get(id=p2.id).value) == 'red'
assert not PropertyValue.objects.filter(id=p1.id).exists()
@unittest.skip
def test_delete(self):
c = Property.objects.create(event=self.event1, name="Size")
t1 = now()
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("alert-success")
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.current.filter(identity=c.identity).exists()
assert not Property.objects.filter(id=c.id).exists()
class QuestionsTest(ItemFormTest):
@@ -185,9 +175,8 @@ class QuestionsTest(ItemFormTest):
def test_update(self):
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.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").send_keys('How old are you?')
@@ -195,22 +184,19 @@ class QuestionsTest(ItemFormTest):
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.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)
assert str(Question.objects.current.get(identity=c.identity).question) == 'How old are you?'
assert str(Question.objects.as_of(t1).get(identity=c.identity).question) == 'What is your shoe size?'
assert str(Question.objects.get(id=c.id).question) == 'How old are you?'
def test_delete(self):
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.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("alert-success")
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.current.filter(identity=c.identity).exists()
assert not Question.objects.filter(id=c.id).exists()
class QuotaTest(ItemFormTest):
@@ -232,36 +218,31 @@ class QuotaTest(ItemFormTest):
prop1 = Property.objects.create(event=self.event1, name="Level", item=item2)
PropertyValue.objects.create(prop=prop1, value="Silver")
PropertyValue.objects.create(prop=prop1, value="Gold")
t1 = now()
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").send_keys('350')
# self.scroll_and_click(self.driver.find_element_by_css_selector('.panel-group .panel:nth-child(1)
# .panel-title a'))
# 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()
# 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.driver.find_element_by_class_name("alert-success")
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)
assert Quota.objects.current.get(identity=c.identity).size == 350
assert Quota.objects.as_of(t1).get(identity=c.identity).size == 500
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()
assert Quota.objects.get(id=c.id).size == 350
assert item1 in Quota.objects.get(id=c.id).items.all()
def test_delete(self):
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.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("alert-success")
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.current.filter(identity=c.identity).exists()
assert not Quota.objects.filter(id=c.id).exists()

View File

@@ -60,7 +60,7 @@ def test_order_list(client, env):
assert 'FOO' in response.rendered_content
response = client.get('/control/event/dummy/dummy/orders/?item=15')
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
@@ -80,13 +80,13 @@ def test_order_transition_to_paid_in_time_success(client, env):
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
'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
@pytest.mark.django_db
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.save()
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', {
'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
@pytest.mark.django_db
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.save()
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', {
'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
@@ -134,14 +134,14 @@ def test_order_transition_to_paid_expired_quota_full(client, env):
(Order.STATUS_REFUNDED, Order.STATUS_PENDING, False)
])
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.save()
client.login(email='dummy@dummy.dummy', password='dummy')
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
'status': process[1]
})
o = Order.objects.current.get(identity=env[2].identity)
o = Order.objects.get(id=env[2].id)
if process[2]:
assert o.status == process[1]
else:
@@ -150,7 +150,7 @@ def test_order_transition(client, env, process):
@pytest.mark.django_db
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.save()
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
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.save()
env[0].settings.set('ticket_download', True)
@@ -176,7 +176,7 @@ def test_order_detail_download_buttons_visible(client, env):
@pytest.mark.django_db
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.save()
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
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.save()
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
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.save()
env[0].settings.set('ticket_download', True)
@@ -225,7 +225,7 @@ def test_order_download_unpaid(client, env):
@pytest.mark.django_db
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.save()
env[0].settings.set('ticket_download', True)
@@ -237,7 +237,7 @@ def test_order_download_unknown_provider(client, env):
@pytest.mark.django_db
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.save()
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):
from pretix.base.services import tickets
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.save()
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')
response = client.get('/control/event/dummy/dummy/orders/FOO/download/testdummy')
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']
dl = response['Location']
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
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.save()
client.login(email='dummy@dummy.dummy', password='dummy')
@@ -296,13 +296,13 @@ def test_order_extend_not_expired(client, env):
'expires': newdate
}, follow=True)
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
@pytest.mark.django_db
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.save()
q = Quota.objects.create(event=env[0], size=3)
@@ -313,13 +313,13 @@ def test_order_extend_expired_quota_left(client, env):
'expires': newdate
}, follow=True)
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
@pytest.mark.django_db
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)
olddate = o.expires
o.save()
@@ -331,7 +331,7 @@ def test_order_extend_expired_quota_empty(client, env):
'expires': newdate
}, follow=True)
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")

View File

@@ -33,22 +33,22 @@ event_urls = [
"settings/tickets",
"items/",
"items/add",
"items/abc/",
"items/abc/variations",
"items/abc/properties",
"items/1/",
"items/1/variations",
"items/1/properties",
"categories/",
"categories/add",
"categories/abc/",
"categories/abc/up",
"categories/abc/down",
"categories/abc/delete",
"categories/2/",
"categories/2/up",
"categories/2/down",
"categories/2/delete",
"questions/",
"questions/abc/delete",
"questions/abc/",
"questions/2/delete",
"questions/2/",
"questions/add",
"quotas/",
"quotas/abc/delete",
"quotas/abc/",
"quotas/2/delete",
"quotas/2/",
"quotas/add",
"orders/ABC/transition",
"orders/ABC/extend",
@@ -56,7 +56,6 @@ event_urls = [
"orders/",
]
organizer_urls = [
'organizer/abc/edit',
'event/abc/add'
@@ -101,16 +100,16 @@ event_permission_urls = [
# We don't have to create categories and similar objects
# for testing this, it is enough to test that a 404 error
# is returned instead of a 403 one.
("can_change_items", "categories/abc/", 404),
("can_change_items", "categories/abc/delete", 404),
("can_change_items", "categories/2/", 404),
("can_change_items", "categories/2/delete", 404),
("can_change_items", "categories/add", 200),
# ("can_change_items", "questions/", 200),
("can_change_items", "questions/abc/", 404),
("can_change_items", "questions/abc/delete", 404),
("can_change_items", "questions/2/", 404),
("can_change_items", "questions/2/delete", 404),
("can_change_items", "questions/add", 200),
# ("can_change_items", "quotas/", 200),
("can_change_items", "quotas/abc/", 404),
("can_change_items", "quotas/abc/delete", 404),
("can_change_items", "quotas/2/", 404),
("can_change_items", "quotas/2/delete", 404),
("can_change_items", "quotas/add", 200),
("can_view_orders", "orders/overview/", 200),
("can_view_orders", "orders/", 200),
@@ -143,7 +142,6 @@ def test_current_permission(client, env):
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.get('/control/event/dummy/dummy/settings/')
assert response.status_code == 200
ep = ep.clone()
setattr(ep, 'can_change_settings', False)
ep.save()
response = client.get('/control/event/dummy/dummy/settings/')

View File

@@ -105,5 +105,5 @@ Buchungstag;Valuta;Buchungstext;Auftraggeber / Empfänger;Verwendungszweck;Betra
data[inp.attrs['name']] = inp.attrs['value']
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.current.get(identity=env[3].identity).status == Order.STATUS_CANCELLED
assert Order.objects.get(id=env[2].id).status == Order.STATUS_PAID
assert Order.objects.get(id=env[3].id).status == Order.STATUS_CANCELLED

View File

@@ -68,5 +68,5 @@ def test_pretixdroid(client, env):
jdata = json.loads(response.content.decode("utf-8"))
assert jdata['version'] == 1
assert len(jdata['data']) == 2
assert {"id": env[3].identity, "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[3].id, "variation": "Red", "attendee_name": None, "item": "T-Shirt"} in jdata['data']
assert {"id": env[4].id, "variation": None, "attendee_name": "Peter", "item": "Ticket"} in jdata['data']

View File

@@ -45,7 +45,7 @@ class CartTestMixin:
class CartTest(CartTestMixin, TestCase):
def test_simple(self):
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)
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
target_status_code=200)
@@ -62,7 +62,7 @@ class CartTest(CartTestMixin, TestCase):
def test_variation(self):
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)
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
target_status_code=200)
@@ -80,7 +80,7 @@ class CartTest(CartTestMixin, TestCase):
def test_count(self):
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)
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
target_status_code=200)
@@ -98,8 +98,8 @@ class CartTest(CartTestMixin, TestCase):
def test_multiple(self):
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'item_' + self.ticket.identity: '2',
'variation_' + self.shirt.identity + '_' + self.shirt_red.identity: '1'
'item_%d' % self.ticket.id: '2',
'variation_%d_%d' % (self.shirt.id, self.shirt_red.id): '1'
}, follow=True)
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
target_status_code=200)
@@ -114,7 +114,7 @@ class CartTest(CartTestMixin, TestCase):
def test_fuzzy_input(self):
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)
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
target_status_code=200)
@@ -137,7 +137,7 @@ class CartTest(CartTestMixin, TestCase):
)
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), {
'item_' + shirt2.identity: '1',
'item_%d' % shirt2.id: '1',
}, follow=True)
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
target_status_code=200)
@@ -148,7 +148,7 @@ class CartTest(CartTestMixin, TestCase):
def test_no_quota(self):
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), {
'item_' + shirt2.identity: '1',
'item_%d' % shirt2.id: '1',
}, follow=True)
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
target_status_code=200)
@@ -161,7 +161,7 @@ class CartTest(CartTestMixin, TestCase):
self.ticket.available_from = now() - timedelta(days=2)
self.ticket.save()
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)
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.save()
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)
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.save()
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)
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
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)
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
target_status_code=200)
@@ -200,7 +200,7 @@ class CartTest(CartTestMixin, TestCase):
self.quota_tickets.size = 0
self.quota_tickets.save()
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)
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
target_status_code=200)
@@ -212,7 +212,7 @@ class CartTest(CartTestMixin, TestCase):
self.quota_tickets.size = 1
self.quota_tickets.save()
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)
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
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), {
}, follow=True)
cp = CartPosition.objects.current.get(identity=cp.identity)
cp = CartPosition.objects.get(id=cp.id)
self.assertGreater(cp.expires, now())
def test_renew_expired_successfully(self):
@@ -244,9 +244,9 @@ class CartTest(CartTestMixin, TestCase):
price=23, expires=now() - timedelta(minutes=10)
)
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)
obj = CartPosition.objects.current.get(identity=cp1.identity)
obj = CartPosition.objects.get(id=cp1.id)
self.assertEqual(obj.item, self.ticket)
self.assertIsNone(obj.variation)
self.assertEqual(obj.price, 23)
@@ -266,9 +266,9 @@ class CartTest(CartTestMixin, TestCase):
cartposition=cr1, question=q1, answer='23'
))
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)
obj = CartPosition.objects.current.get(identity=cr1.identity)
obj = CartPosition.objects.get(id=cr1.id)
self.assertEqual(obj.answers.get(question=q1).answer, '23')
def test_renew_expired_failed(self):
@@ -279,11 +279,11 @@ class CartTest(CartTestMixin, TestCase):
price=23, expires=now() - timedelta(minutes=10)
)
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)
doc = BeautifulSoup(response.rendered_content)
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):
CartPosition.objects.create(
@@ -291,11 +291,11 @@ class CartTest(CartTestMixin, TestCase):
price=23, expires=now() + timedelta(minutes=10)
)
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)
doc = BeautifulSoup(response.rendered_content)
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):
CartPosition.objects.create(
@@ -303,11 +303,11 @@ class CartTest(CartTestMixin, TestCase):
price=14, expires=now() + timedelta(minutes=10)
)
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)
doc = BeautifulSoup(response.rendered_content)
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):
CartPosition.objects.create(
@@ -319,11 +319,11 @@ class CartTest(CartTestMixin, TestCase):
price=23, expires=now() + timedelta(minutes=10)
)
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)
doc = BeautifulSoup(response.rendered_content)
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):
CartPosition.objects.create(
@@ -335,11 +335,11 @@ class CartTest(CartTestMixin, TestCase):
price=23, expires=now() + timedelta(minutes=10)
)
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)
doc = BeautifulSoup(response.rendered_content)
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):
CartPosition.objects.create(
@@ -351,11 +351,11 @@ class CartTest(CartTestMixin, TestCase):
price=20, expires=now() + timedelta(minutes=10)
)
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)
doc = BeautifulSoup(response.rendered_content)
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(objs[0].item, self.ticket)
self.assertIsNone(objs[0].variation)

View File

@@ -60,17 +60,17 @@ class CheckoutTestCase(TestCase):
response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True)
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]' % (cr2.identity, q1.identity))), 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]' % (cr2.identity, q2.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.id, q1.id))), 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.id, q2.id))), 1)
# Not all required fields filled out, expect failure
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' % (cr2.identity, q1.identity): '',
'%s-question_%s' % (cr1.identity, q2.identity): 'Internet',
'%s-question_%s' % (cr2.identity, q2.identity): '',
'%s-question_%s' % (cr1.id, q1.id): '42',
'%s-question_%s' % (cr2.id, q1.id): '',
'%s-question_%s' % (cr1.id, q2.id): 'Internet',
'%s-question_%s' % (cr2.id, q2.id): '',
'email': 'admin@localhost'
}, follow=True)
doc = BeautifulSoup(response.rendered_content)
@@ -78,17 +78,17 @@ class CheckoutTestCase(TestCase):
# Corrected request
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' % (cr2.identity, q1.identity): '23',
'%s-question_%s' % (cr1.identity, q2.identity): 'Internet',
'%s-question_%s' % (cr2.identity, q2.identity): '',
'%s-question_%s' % (cr1.id, q1.id): '42',
'%s-question_%s' % (cr2.id, q1.id): '23',
'%s-question_%s' % (cr1.id, q2.id): 'Internet',
'%s-question_%s' % (cr2.id, q2.id): '',
'email': 'admin@localhost'
}, follow=True)
self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug),
target_status_code=200)
cr1 = CartPosition.objects.current.get(identity=cr1.identity)
cr2 = CartPosition.objects.current.get(identity=cr2.identity)
cr1 = CartPosition.objects.get(id=cr1.id)
cr2 = CartPosition.objects.get(id=cr2.id)
self.assertEqual(cr1.answers.filter(question=q1).count(), 1)
self.assertEqual(cr2.answers.filter(question=q1).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)
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
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'
}, follow=True)
doc = BeautifulSoup(response.rendered_content)
@@ -115,13 +115,13 @@ class CheckoutTestCase(TestCase):
# Corrected request
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'
}, follow=True)
self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug),
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')
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)
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
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'
}, follow=True)
self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug),
target_status_code=200)
cr1 = CartPosition.objects.current.get(identity=cr1.identity)
cr1 = CartPosition.objects.get(id=cr1.id)
self.assertIsNone(cr1.attendee_name)
def test_payment(self):
@@ -188,7 +188,6 @@ class CheckoutTestCase(TestCase):
self.assertRedirects(response, '/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug),
target_status_code=200)
cr1 = cr1.clone()
cr1.attendee_name = 'Peter'
cr1.save()
q1 = Question.objects.create(
@@ -201,7 +200,6 @@ class CheckoutTestCase(TestCase):
self.assertRedirects(response, '/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug),
target_status_code=200)
q1 = q1.clone()
q1.required = False
q1.save()
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)
doc = BeautifulSoup(response.rendered_content)
self.assertEqual(len(doc.select(".thank-you")), 1)
self.assertFalse(CartPosition.objects.current.filter(identity=cr1.identity).exists())
self.assertEqual(Order.objects.current.count(), 1)
self.assertEqual(OrderPosition.objects.current.count(), 1)
self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists())
self.assertEqual(Order.objects.count(), 1)
self.assertEqual(OrderPosition.objects.count(), 1)
def test_confirm_expired_available(self):
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)
doc = BeautifulSoup(response.rendered_content)
self.assertEqual(len(doc.select(".thank-you")), 1)
self.assertFalse(CartPosition.objects.current.filter(identity=cr1.identity).exists())
self.assertEqual(Order.objects.current.count(), 1)
self.assertEqual(OrderPosition.objects.current.count(), 1)
self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists())
self.assertEqual(Order.objects.count(), 1)
self.assertEqual(OrderPosition.objects.count(), 1)
def test_confirm_price_changed(self):
self.ticket = self.ticket.clone()
self.ticket.default_price = 24
self.ticket.save()
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)
doc = BeautifulSoup(response.rendered_content)
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)
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)
doc = BeautifulSoup(response.rendered_content)
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):
self.ticket = self.ticket.clone()
self.ticket.active = False
self.ticket.save()
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)
doc = BeautifulSoup(response.rendered_content)
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):
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)
doc = BeautifulSoup(response.rendered_content)
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):
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)
doc = BeautifulSoup(response.rendered_content)
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())

View File

@@ -194,7 +194,7 @@ class DeadlineTest(EventTestMixin, TestCase):
response = self.client.post(
'/%s/%s/cart/add' % (self.orga.slug, self.event.slug),
{
'item_' + self.item.identity: '1',
'item_%d' % self.item.id: '1',
},
follow=True
)
@@ -213,7 +213,7 @@ class DeadlineTest(EventTestMixin, TestCase):
response = self.client.post(
'/%s/%s/cart/add' % (self.orga.slug, self.event.slug),
{
'item_' + self.item.identity: '1'
'item_%d' % self.item.id: '1'
},
follow=True
)
@@ -233,7 +233,7 @@ class DeadlineTest(EventTestMixin, TestCase):
response = self.client.post(
'/%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)
@@ -251,7 +251,7 @@ class DeadlineTest(EventTestMixin, TestCase):
response = self.client.post(
'/%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)

View File

@@ -129,7 +129,7 @@ class OrdersTest(TestCase):
self.client.get(
'/%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
def test_orders_modify_attendee_optional(self):
@@ -139,18 +139,18 @@ class OrdersTest(TestCase):
response = self.client.get(
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret))
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
response = self.client.post(
'/%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)
self.assertRedirects(response,
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
self.order.secret),
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, '')
def test_orders_modify_attendee_required(self):
@@ -160,24 +160,24 @@ class OrdersTest(TestCase):
response = self.client.get(
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret))
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
response = self.client.post(
'/%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)
doc = BeautifulSoup(response.rendered_content)
self.assertGreaterEqual(len(doc.select('.has-error')), 1)
response = self.client.post(
'/%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)
self.assertRedirects(response, '/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
self.order.secret),
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'
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))
doc = BeautifulSoup(response.rendered_content)
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
response = self.client.post(
'/%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)
self.assertRedirects(response,
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
@@ -211,19 +211,19 @@ class OrdersTest(TestCase):
self.order.secret))
doc = BeautifulSoup(response.rendered_content)
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
response = self.client.post(
'/%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)
doc = BeautifulSoup(response.rendered_content)
self.assertGreaterEqual(len(doc.select('.has-error')), 1)
response = self.client.post(
'/%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)
self.assertRedirects(response,
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
@@ -237,7 +237,7 @@ class OrdersTest(TestCase):
self.client.get(
'/%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
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,
self.order.secret),
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):
self.event.settings.set('ticket_download', True)

View File

@@ -12,4 +12,4 @@ class DummyTicketOutput(BaseTicketOutput):
download_button_icon = 'fa-print'
def generate(self, order):
return 'test.txt', 'text/plain', order.identity
return 'test.txt', 'text/plain', str(order.id)