mirror of
https://github.com/pretix/pretix.git
synced 2026-05-05 15:14:04 +00:00
Removed CleanerVersion layer [backwards-incompatible!]
This commit is contained in:
@@ -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')
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
]
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
]
|
||||
23
src/pretix/base/migrations/0002_auto_20151212_1123.py
Normal file
23
src/pretix/base/migrations/0002_auto_20151212_1123.py
Normal 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'),
|
||||
),
|
||||
]
|
||||
@@ -1,19 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0002_auto_20151021_1412'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='is_public',
|
||||
field=models.BooleanField(help_text="If selected, this event may show up on the ticket system's start page or an organization profile.", default=False, verbose_name='Visible in public lists'),
|
||||
),
|
||||
]
|
||||
@@ -1,24 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0003_event_is_public'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='cartposition',
|
||||
old_name='session',
|
||||
new_name='cart_id'
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='cartposition',
|
||||
name='cart_id',
|
||||
field=models.CharField(blank=True, verbose_name='Cart ID (e.g. session key)', max_length=255, null=True),
|
||||
),
|
||||
]
|
||||
@@ -1,24 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0004_auto_20151024_0848'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='item',
|
||||
name='available_from',
|
||||
field=models.DateTimeField(null=True, help_text='This product will not be sold before the given date.', blank=True, verbose_name='Available from'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='item',
|
||||
name='available_until',
|
||||
field=models.DateTimeField(null=True, help_text='This product will not be sold after the given date.', blank=True, verbose_name='Available to'),
|
||||
),
|
||||
]
|
||||
@@ -1,5 +1,5 @@
|
||||
from .auth import User
|
||||
from .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',
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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__'
|
||||
|
||||
@@ -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']
|
||||
|
||||
@@ -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."),
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" %}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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 }}&status=n">
|
||||
<a href="{{ listurl }}?item={{ item.id }}&status=n">
|
||||
{{ item.num_pending|togglesum }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ listurl }}?item={{ item.identity }}&status=c">
|
||||
<a href="{{ listurl }}?item={{ item.id }}&status=c">
|
||||
{{ item.num_cancelled|togglesum }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ listurl }}?item={{ item.identity }}&status=r">
|
||||
<a href="{{ listurl }}?item={{ item.id }}&status=r">
|
||||
{{ item.num_refunded|togglesum }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ listurl }}?item={{ item.identity }}&status=p">
|
||||
<a href="{{ listurl }}?item={{ item.id }}&status=p">
|
||||
{{ item.num_paid|togglesum }}
|
||||
</a>
|
||||
</td>
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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."))
|
||||
|
||||
@@ -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")
|
||||
]
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import versions.models
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixmultidomain', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='knowndomain',
|
||||
options={'verbose_name': 'Known domain', 'verbose_name_plural': 'Known domains'},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='knowndomain',
|
||||
name='organizer',
|
||||
field=versions.models.VersionedForeignKey(to='pretixbase.Organizer', null=True, related_name='domains', blank=True),
|
||||
),
|
||||
]
|
||||
@@ -1,13 +1,12 @@
|
||||
from django.db import models
|
||||
from django.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")
|
||||
|
||||
@@ -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([
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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']
|
||||
|
||||
|
||||
@@ -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']
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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" %}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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" %}
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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}))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 != '':
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
|
||||
@@ -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/')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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']
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user