mirror of
https://github.com/pretix/pretix.git
synced 2026-05-03 14:54:04 +00:00
Merge branch 'master' of https://github.com/pretix/pretix
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
[run]
|
||||
source = pretixbase,pretixcontrol,pretixpresale,pretixplugins
|
||||
source = pretix
|
||||
omit = */migrations/*,*/urls.py,*/tests/*,*/testdummy/*,*/admin.py
|
||||
|
||||
[report]
|
||||
|
||||
8
src/pretix/base/__init__.py
Normal file
8
src/pretix/base/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PretixBaseConfig(AppConfig):
|
||||
name = 'pretix.base'
|
||||
label = 'pretixbase'
|
||||
|
||||
default_app_config = 'pretix.base.PretixBaseConfig'
|
||||
@@ -3,13 +3,13 @@ from django.contrib.auth.admin import UserAdmin
|
||||
from django.utils.translation import ugettext as _
|
||||
from django import forms
|
||||
|
||||
from pretixbase.models import (
|
||||
from pretix.base.models import (
|
||||
User, Organizer, OrganizerPermission, Event, EventPermission,
|
||||
Property, PropertyValue, Item, ItemVariation, ItemCategory
|
||||
)
|
||||
|
||||
|
||||
class TixlUserCreationForm(forms.ModelForm):
|
||||
class PretixUserCreationForm(forms.ModelForm):
|
||||
|
||||
"""
|
||||
A form that creates a user, with no privileges, from the given username and
|
||||
@@ -39,14 +39,14 @@ class TixlUserCreationForm(forms.ModelForm):
|
||||
return password2
|
||||
|
||||
def save(self, commit=True):
|
||||
user = super(TixlUserCreationForm, self).save(commit=False)
|
||||
user = super(PretixUserCreationForm, self).save(commit=False)
|
||||
user.set_password(self.cleaned_data["password1"])
|
||||
if commit:
|
||||
user.save()
|
||||
return user
|
||||
|
||||
|
||||
class TixlUserAdmin(UserAdmin):
|
||||
class PretixUserAdmin(UserAdmin):
|
||||
|
||||
fieldsets = (
|
||||
(None, {'fields': ('identifier', 'event', 'username', 'password')}),
|
||||
@@ -59,7 +59,7 @@ class TixlUserAdmin(UserAdmin):
|
||||
search_fields = ('identifier', 'username', 'givenname', 'familyname', 'email')
|
||||
ordering = ('identifier',)
|
||||
list_filter = ('is_staff', 'is_active', 'groups')
|
||||
add_form = TixlUserCreationForm
|
||||
add_form = PretixUserCreationForm
|
||||
|
||||
|
||||
class OrganizerPermissionInline(admin.TabularInline):
|
||||
@@ -126,7 +126,7 @@ class ItemAdmin(admin.ModelAdmin):
|
||||
search_fields = ('name', 'event', 'category', 'short_description')
|
||||
|
||||
|
||||
admin.site.register(User, TixlUserAdmin)
|
||||
admin.site.register(User, PretixUserAdmin)
|
||||
admin.site.register(Organizer, OrganizerAdmin)
|
||||
admin.site.register(Event, EventAdmin)
|
||||
admin.site.register(Property, PropertyAdmin)
|
||||
@@ -3,7 +3,7 @@ import hashlib
|
||||
|
||||
from django.core.cache import caches
|
||||
|
||||
from pretixbase.models import Event
|
||||
from pretix.base.models import Event
|
||||
|
||||
|
||||
class EventRelatedCache:
|
||||
@@ -1,7 +1,6 @@
|
||||
import pytz
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import resolve
|
||||
from django.middleware.locale import LocaleMiddleware as BaseLocaleMiddleware
|
||||
from django.utils.translation.trans_real import (
|
||||
get_supported_language_variant,
|
||||
@@ -14,7 +13,6 @@ from django.utils import translation, timezone
|
||||
from collections import OrderedDict
|
||||
from django.utils.cache import patch_vary_headers
|
||||
|
||||
from pretixbase.models import Event
|
||||
|
||||
_supported = None
|
||||
|
||||
@@ -7,7 +7,7 @@ import django.core.validators
|
||||
from django.conf import settings
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import pretixbase.models
|
||||
import pretix.base.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
@@ -322,7 +322,8 @@ class Migration(migrations.Migration):
|
||||
('items', versions.models.VersionedManyToManyField(blank=True, related_name='quotas', to='pretixbase.Item', verbose_name='Item')),
|
||||
('lock_cache', models.ManyToManyField(blank=True, to='pretixbase.CartPosition')),
|
||||
('order_cache', models.ManyToManyField(blank=True, to='pretixbase.OrderPosition')),
|
||||
('variations', pretixbase.models.VariationsField(blank=True, related_name='quotas', to='pretixbase.ItemVariation', verbose_name='Variations')),
|
||||
('variations', pretix.base.models.VariationsField(blank=True, related_name='quotas',
|
||||
to='pretixbase.ItemVariation', verbose_name='Variations')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Quotas',
|
||||
@@ -2,7 +2,7 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import pretixbase.models
|
||||
import pretix.base.models
|
||||
import versions.models
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import versions.models
|
||||
import pretixbase.models
|
||||
import pretix.base.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
@@ -5,7 +5,7 @@ from django.db import models, migrations
|
||||
from django.utils.timezone import utc
|
||||
import versions.models
|
||||
import datetime
|
||||
import pretixbase.models
|
||||
import pretix.base.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
@@ -2,7 +2,7 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import pretixbase.models
|
||||
import pretix.base.models
|
||||
import versions.models
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import pretixbase.models
|
||||
import pretix.base.models
|
||||
import versions.models
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import versions.models
|
||||
import pretixbase.models
|
||||
import pretix.base.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
@@ -0,0 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import versions.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0008_quota_locked'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='EventSetting',
|
||||
fields=[
|
||||
('id', models.CharField(primary_key=True, serialize=False, max_length=36)),
|
||||
('identity', models.CharField(max_length=36)),
|
||||
('version_start_date', models.DateTimeField()),
|
||||
('version_end_date', models.DateTimeField(blank=True, default=None, null=True)),
|
||||
('version_birth_date', models.DateTimeField()),
|
||||
('key', models.CharField(max_length=255)),
|
||||
('value', models.TextField()),
|
||||
('event', versions.models.VersionedForeignKey(related_name='setting_objects', to='pretixbase.Event')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='OrganizerSetting',
|
||||
fields=[
|
||||
('id', models.CharField(primary_key=True, serialize=False, max_length=36)),
|
||||
('identity', models.CharField(max_length=36)),
|
||||
('version_start_date', models.DateTimeField()),
|
||||
('version_end_date', models.DateTimeField(blank=True, default=None, null=True)),
|
||||
('version_birth_date', models.DateTimeField()),
|
||||
('key', models.CharField(max_length=255)),
|
||||
('value', models.TextField()),
|
||||
('organizer', versions.models.VersionedForeignKey(related_name='setting_objects', to='pretixbase.Organizer')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
]
|
||||
@@ -7,6 +7,7 @@ from django.db import models
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
|
||||
from django.db.models import Q, Count
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.template.defaultfilters import date as _date
|
||||
@@ -227,6 +228,58 @@ class Organizer(Versionable):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class OrganizerSettingsProxy:
|
||||
"""
|
||||
This objects allows convenient access to settings stored in the
|
||||
OrganizerSettings database model. It exposes all settings as properties
|
||||
and it will do all the nasty defaults stuff for
|
||||
you. It will return None for non-existing properties.
|
||||
"""
|
||||
|
||||
def __init__(self, organizer):
|
||||
self._organizer = organizer
|
||||
self._cached_obj = None
|
||||
|
||||
def _cache(self):
|
||||
if self._cached_obj is None:
|
||||
self._cached_obj = {}
|
||||
for setting in self._organizer.setting_objects.current.all():
|
||||
self._cached_obj[setting.key] = setting
|
||||
return self._cached_obj
|
||||
|
||||
def __getattr__(self, key):
|
||||
if key in self._cache():
|
||||
return self._cache()[key].value
|
||||
if key in OrganizerSetting.DEFAULTS:
|
||||
return OrganizerSetting.DEFAULTS[key]
|
||||
return None
|
||||
|
||||
def __delattr__(self, key):
|
||||
if key.startswith('_'):
|
||||
return super().__delattr__(key)
|
||||
if key in self._cache():
|
||||
self._cache()[key].delete()
|
||||
del self._cache()[key]
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
if key.startswith('_'):
|
||||
return super().__setattr__(key, value)
|
||||
if key in self._cache():
|
||||
s = self._cache()[key]
|
||||
s = s.clone()
|
||||
else:
|
||||
s = OrganizerSetting(organizer=self._organizer, key=key)
|
||||
s.value = value
|
||||
s.save()
|
||||
self._cache()[key] = s
|
||||
|
||||
@cached_property
|
||||
def settings(self):
|
||||
"""
|
||||
Returns an object representing this organizer's settings
|
||||
"""
|
||||
return Organizer.OrganizerSettingsProxy(self)
|
||||
|
||||
|
||||
class OrganizerPermission(Versionable):
|
||||
"""
|
||||
@@ -382,10 +435,60 @@ class Event(Versionable):
|
||||
"DATETIME_FORMAT" if self.show_times else "DATE_FORMAT"
|
||||
)
|
||||
|
||||
def get_cache(self) -> "pretixbase.cache.EventRelatedCache":
|
||||
from pretixbase.cache import EventRelatedCache
|
||||
def get_cache(self) -> "pretix.base.cache.EventRelatedCache":
|
||||
from pretix.base.cache import EventRelatedCache
|
||||
return EventRelatedCache(self)
|
||||
|
||||
class EventSettingsProxy:
|
||||
"""
|
||||
This objects allows convenient access to settings stored in the
|
||||
EventSettings database model. It exposes all settings as properties
|
||||
and it will do all the nasty inheritance and defaults stuff for
|
||||
you. It will return None for non-existing properties.
|
||||
"""
|
||||
|
||||
def __init__(self, event):
|
||||
self._event = event
|
||||
self._cached_obj = None
|
||||
|
||||
def _cache(self):
|
||||
if self._cached_obj is None:
|
||||
self._cached_obj = {}
|
||||
for setting in self._event.setting_objects.current.all():
|
||||
self._cached_obj[setting.key] = setting
|
||||
return self._cached_obj
|
||||
|
||||
def __getattr__(self, key):
|
||||
if key in self._cache():
|
||||
return self._cache()[key].value
|
||||
return getattr(self._event.organizer.settings, key)
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
if key.startswith('_'):
|
||||
return super().__setattr__(key, value)
|
||||
if key in self._cache():
|
||||
s = self._cache()[key]
|
||||
s = s.clone()
|
||||
else:
|
||||
s = EventSetting(event=self._event, key=key)
|
||||
s.value = value
|
||||
s.save()
|
||||
self._cache()[key] = s
|
||||
|
||||
def __delattr__(self, key):
|
||||
if key.startswith('_'):
|
||||
return super().__delattr__(key)
|
||||
if key in self._cache():
|
||||
self._cache()[key].delete()
|
||||
del self._cache()[key]
|
||||
|
||||
@cached_property
|
||||
def settings(self):
|
||||
"""
|
||||
Returns an object representing this event's settings
|
||||
"""
|
||||
return Event.EventSettingsProxy(self)
|
||||
|
||||
|
||||
class EventPermission(Versionable):
|
||||
"""
|
||||
@@ -745,14 +848,27 @@ class Item(Versionable):
|
||||
return self._get_all_available_variations_cache
|
||||
|
||||
from .signals import determine_availability
|
||||
if self.properties.count() == 0:
|
||||
|
||||
propids = set([p.identity for p in self.properties.all()])
|
||||
if len(propids) == 0:
|
||||
variations = [VariationDict()]
|
||||
else:
|
||||
all_variations = list(self.variations.annotate(qc=Count('quotas')).filter(qc__gt=0))
|
||||
all_variations = list(
|
||||
self.variations.annotate(
|
||||
qc=Count('quotas')
|
||||
).filter(qc__gt=0).prefetch_related(
|
||||
"values", "values__prop"
|
||||
)
|
||||
)
|
||||
variations = []
|
||||
for var in all_variations:
|
||||
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:
|
||||
continue
|
||||
vardict = VariationDict()
|
||||
for v in var.values.all():
|
||||
for v in values:
|
||||
vardict[v.prop.identity] = v
|
||||
vardict['variation'] = var
|
||||
variations.append(vardict)
|
||||
@@ -782,7 +898,7 @@ class Item(Versionable):
|
||||
self._get_all_available_variations_cache = variations
|
||||
return variations
|
||||
|
||||
def availability(self):
|
||||
def check_quotas(self):
|
||||
"""
|
||||
This method is used to determine whether this Item is currently available
|
||||
for sale. It may return any of the return codes of Quota.availability()
|
||||
@@ -792,7 +908,7 @@ class Item(Versionable):
|
||||
'but call this on their ItemVariation objects')
|
||||
return min([q.availability() for q in self.quotas.all()])
|
||||
|
||||
def execute_restrictions(self):
|
||||
def check_restrictions(self):
|
||||
"""
|
||||
This method is used to determine whether this ItemVariation is restricted
|
||||
in sale by any restriction plugins.
|
||||
@@ -859,6 +975,9 @@ class ItemVariation(Versionable):
|
||||
verbose_name = _("Item variation")
|
||||
verbose_name_plural = _("Item variations")
|
||||
|
||||
def __str__(self):
|
||||
return str(self.to_variation_dict())
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
super().delete(*args, **kwargs)
|
||||
if self.item:
|
||||
@@ -869,7 +988,7 @@ class ItemVariation(Versionable):
|
||||
if self.item:
|
||||
self.item.event.get_cache().clear()
|
||||
|
||||
def availability(self):
|
||||
def check_quotas(self):
|
||||
"""
|
||||
This method is used to determine whether this ItemVariation is currently
|
||||
available for sale in terms of quotas. It may return any of the return codes
|
||||
@@ -884,7 +1003,7 @@ class ItemVariation(Versionable):
|
||||
vd['variation'] = self
|
||||
return vd
|
||||
|
||||
def execute_restrictions(self):
|
||||
def check_restrictions(self):
|
||||
"""
|
||||
This method is used to determine whether this ItemVariation is restricted
|
||||
in sale by any restriction plugins.
|
||||
@@ -913,7 +1032,7 @@ class VariationsField(VersionedManyToManyField):
|
||||
"""
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
from pretixcontrol.views.forms import VariationsField as FVariationsField
|
||||
from pretix.control.views.forms import VariationsField as FVariationsField
|
||||
from django.db.models.fields.related import RelatedField
|
||||
defaults = {
|
||||
'form_class': FVariationsField,
|
||||
@@ -1086,14 +1205,14 @@ class Quota(Versionable):
|
||||
Q(variation__quotas__in=[self])
|
||||
)
|
||||
)
|
||||
paid_orders = OrderPosition.objects.filter(
|
||||
paid_orders = OrderPosition.objects.current.filter(
|
||||
Q(order__status=Order.STATUS_PAID)
|
||||
& quotalookup
|
||||
).count()
|
||||
if paid_orders >= self.size:
|
||||
return Quota.AVAILABILITY_GONE, 0
|
||||
|
||||
pending_valid_orders = OrderPosition.objects.filter(
|
||||
pending_valid_orders = OrderPosition.objects.current.filter(
|
||||
Q(order__status=Order.STATUS_PENDING)
|
||||
& Q(order__expires__gte=now())
|
||||
& quotalookup
|
||||
@@ -1101,7 +1220,7 @@ class Quota(Versionable):
|
||||
if (paid_orders + pending_valid_orders) >= self.size:
|
||||
return Quota.AVAILABILITY_ORDERED, 0
|
||||
|
||||
valid_cart_positions = CartPosition.objects.filter(
|
||||
valid_cart_positions = CartPosition.objects.current.filter(
|
||||
Q(expires__gte=now())
|
||||
& quotalookup
|
||||
).count()
|
||||
@@ -1307,3 +1426,26 @@ class CartPosition(Versionable):
|
||||
class Meta:
|
||||
verbose_name = _("Cart position")
|
||||
verbose_name_plural = _("Cart positions")
|
||||
|
||||
|
||||
class EventSetting(Versionable):
|
||||
"""
|
||||
An event settings is a key-value setting which can be set for a
|
||||
specific event
|
||||
"""
|
||||
event = VersionedForeignKey(Event, related_name='setting_objects')
|
||||
key = models.CharField(max_length=255)
|
||||
value = models.TextField()
|
||||
|
||||
|
||||
class OrganizerSetting(Versionable):
|
||||
"""
|
||||
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
|
||||
"""
|
||||
DEFAULTS = {
|
||||
|
||||
}
|
||||
organizer = VersionedForeignKey(Organizer, related_name='setting_objects')
|
||||
key = models.CharField(max_length=255)
|
||||
value = models.TextField()
|
||||
@@ -12,12 +12,12 @@ class PluginType(Enum):
|
||||
|
||||
def get_all_plugins() -> "List[class]":
|
||||
"""
|
||||
Returns the TixlPluginMeta classes of all plugins found in the installed Django apps.
|
||||
Returns the PretixPluginMeta classes of all plugins found in the installed Django apps.
|
||||
"""
|
||||
plugins = []
|
||||
for app in apps.get_app_configs():
|
||||
if hasattr(app, 'TixlPluginMeta'):
|
||||
meta = app.TixlPluginMeta
|
||||
if hasattr(app, 'PretixPluginMeta'):
|
||||
meta = app.PretixPluginMeta
|
||||
meta.module = app.name
|
||||
plugins.append(meta)
|
||||
return plugins
|
||||
@@ -17,7 +17,7 @@ class EventPluginSignal(django.dispatch.Signal):
|
||||
Send signal from sender to all connected receivers that belong to
|
||||
plugins enabled for the given Event.
|
||||
|
||||
sender is required to be an instance of ``pretixbase.models.Event``.
|
||||
sender is required to be an instance of ``pretix.base.models.Event``.
|
||||
"""
|
||||
assert isinstance(sender, Event)
|
||||
|
||||
1
src/pretix/base/static/bootstrap
Submodule
1
src/pretix/base/static/bootstrap
Submodule
Submodule src/pretix/base/static/bootstrap added at 0d7b85ece7
1
src/pretix/base/static/fontawesome
Submodule
1
src/pretix/base/static/fontawesome
Submodule
Submodule src/pretix/base/static/fontawesome added at c79474df99
@@ -2,7 +2,6 @@ import os
|
||||
import sys
|
||||
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
|
||||
|
||||
from django.test import LiveServerTestCase
|
||||
from django.conf import settings
|
||||
from selenium import webdriver
|
||||
|
||||
@@ -4,7 +4,7 @@ from django.test import TestCase
|
||||
from django.core.cache import cache as django_cache
|
||||
from django.utils.timezone import now
|
||||
|
||||
from pretixbase.models import Event, Organizer
|
||||
from pretix.base.models import Event, Organizer
|
||||
|
||||
|
||||
class CacheTest(TestCase):
|
||||
@@ -2,7 +2,7 @@ from django.test import TestCase, Client
|
||||
from django.utils.timezone import now
|
||||
from django.conf import settings
|
||||
|
||||
from pretixbase.models import Event, Organizer, User
|
||||
from pretix.base.models import Event, Organizer, User
|
||||
|
||||
|
||||
class LocaleDeterminationTest(TestCase):
|
||||
@@ -2,12 +2,12 @@ from datetime import timedelta
|
||||
from django.test import TestCase
|
||||
from django.utils.timezone import now
|
||||
|
||||
from pretixbase.models import (
|
||||
from pretix.base.models import (
|
||||
Event, Organizer, Item, ItemVariation,
|
||||
Property, PropertyValue, User, Quota,
|
||||
Order, OrderPosition, CartPosition
|
||||
)
|
||||
from pretixbase.types import VariationDict
|
||||
Order, OrderPosition, CartPosition,
|
||||
OrganizerSetting)
|
||||
from pretix.base.types import VariationDict
|
||||
|
||||
|
||||
class ItemVariationsTest(TestCase):
|
||||
@@ -202,15 +202,15 @@ class QuotaTestCase(TestCase):
|
||||
|
||||
def test_available(self):
|
||||
self.quota.items.add(self.item1)
|
||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 2))
|
||||
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 2))
|
||||
self.quota.items.add(self.item2)
|
||||
self.quota.variations.add(self.var1)
|
||||
try:
|
||||
self.item2.availability()
|
||||
self.item2.check_quotas()
|
||||
self.assertTrue(False)
|
||||
except:
|
||||
pass
|
||||
self.assertEqual(self.var1.availability(), (Quota.AVAILABILITY_OK, 2))
|
||||
self.assertEqual(self.var1.check_quotas(), (Quota.AVAILABILITY_OK, 2))
|
||||
|
||||
def test_sold_out(self):
|
||||
self.quota.items.add(self.item1)
|
||||
@@ -219,19 +219,19 @@ class QuotaTestCase(TestCase):
|
||||
total=4)
|
||||
OrderPosition.objects.create(order=order, item=self.item1, price=2)
|
||||
OrderPosition.objects.create(order=order, item=self.item1, price=2)
|
||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_GONE, 0))
|
||||
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_GONE, 0))
|
||||
|
||||
self.quota.items.add(self.item2)
|
||||
self.quota.variations.add(self.var1)
|
||||
self.quota.size = 3
|
||||
self.quota.save()
|
||||
self.assertEqual(self.var1.availability(), (Quota.AVAILABILITY_OK, 1))
|
||||
self.assertEqual(self.var1.check_quotas(), (Quota.AVAILABILITY_OK, 1))
|
||||
|
||||
order = Order.objects.create(event=self.event, status=Order.STATUS_PAID,
|
||||
expires=now() + timedelta(days=3),
|
||||
total=4)
|
||||
OrderPosition.objects.create(order=order, item=self.item2, variation=self.var1, price=2)
|
||||
self.assertEqual(self.var1.availability(), (Quota.AVAILABILITY_GONE, 0))
|
||||
self.assertEqual(self.var1.check_quotas(), (Quota.AVAILABILITY_GONE, 0))
|
||||
|
||||
def test_ordered(self):
|
||||
self.quota.items.add(self.item1)
|
||||
@@ -239,17 +239,17 @@ class QuotaTestCase(TestCase):
|
||||
expires=now() + timedelta(days=3),
|
||||
total=4)
|
||||
OrderPosition.objects.create(order=order, item=self.item1, price=2)
|
||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 1))
|
||||
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 1))
|
||||
|
||||
order = Order.objects.create(event=self.event, status=Order.STATUS_PENDING,
|
||||
expires=now() + timedelta(days=3),
|
||||
total=4)
|
||||
OrderPosition.objects.create(order=order, item=self.item1, price=2)
|
||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_ORDERED, 0))
|
||||
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_ORDERED, 0))
|
||||
|
||||
order.expires = now() - timedelta(days=3)
|
||||
order.save()
|
||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 1))
|
||||
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 1))
|
||||
|
||||
def test_reserved(self):
|
||||
self.quota.items.add(self.item1)
|
||||
@@ -259,36 +259,100 @@ class QuotaTestCase(TestCase):
|
||||
expires=now() + timedelta(days=3),
|
||||
total=4)
|
||||
OrderPosition.objects.create(order=order, item=self.item1, price=2)
|
||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 2))
|
||||
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 2))
|
||||
|
||||
order = Order.objects.create(event=self.event, status=Order.STATUS_PENDING,
|
||||
expires=now() + timedelta(days=3),
|
||||
total=4)
|
||||
OrderPosition.objects.create(order=order, item=self.item1, price=2)
|
||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 1))
|
||||
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 1))
|
||||
|
||||
cp = CartPosition.objects.create(event=self.event, item=self.item1, price=2,
|
||||
expires=now() + timedelta(days=3))
|
||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_RESERVED, 0))
|
||||
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_RESERVED, 0))
|
||||
|
||||
cp.expires = now() - timedelta(days=3)
|
||||
cp.save()
|
||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 1))
|
||||
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 1))
|
||||
|
||||
self.quota.items.add(self.item2)
|
||||
self.quota.variations.add(self.var1)
|
||||
cp = CartPosition.objects.create(event=self.event, item=self.item2, variation=self.var1,
|
||||
price=2, expires=now() + timedelta(days=3))
|
||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_RESERVED, 0))
|
||||
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_RESERVED, 0))
|
||||
|
||||
def test_multiple(self):
|
||||
self.quota.items.add(self.item1)
|
||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 2))
|
||||
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 2))
|
||||
|
||||
quota2 = Quota.objects.create(event=self.event, name="Test 2", size=1)
|
||||
quota2.items.add(self.item1)
|
||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 1))
|
||||
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 1))
|
||||
|
||||
quota2.size = 0
|
||||
quota2.save()
|
||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_GONE, 0))
|
||||
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_GONE, 0))
|
||||
|
||||
|
||||
class SettingsTestCase(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
OrganizerSetting.DEFAULTS['test_default'] = 'def'
|
||||
self.organizer = Organizer.objects.create(name='Dummy', slug='dummy')
|
||||
self.event = Event.objects.create(
|
||||
organizer=self.organizer, name='Dummy', slug='dummy',
|
||||
date_from=now(),
|
||||
)
|
||||
|
||||
def test_event_set_explicit(self):
|
||||
self.event.settings.test = 'foo'
|
||||
self.assertEqual(self.event.settings.test, 'foo')
|
||||
|
||||
# Reload object
|
||||
self.event = Event.objects.get(identity=self.event.identity)
|
||||
self.assertEqual(self.event.settings.test, 'foo')
|
||||
|
||||
def test_event_set_on_organizer(self):
|
||||
self.organizer.settings.test = 'foo'
|
||||
self.assertEqual(self.organizer.settings.test, 'foo')
|
||||
self.assertEqual(self.event.settings.test, 'foo')
|
||||
|
||||
# Reload object
|
||||
self.organizer = Organizer.objects.get(identity=self.organizer.identity)
|
||||
self.event = Event.objects.get(identity=self.event.identity)
|
||||
self.assertEqual(self.organizer.settings.test, 'foo')
|
||||
self.assertEqual(self.event.settings.test, 'foo')
|
||||
|
||||
def test_override_organizer(self):
|
||||
self.organizer.settings.test = 'foo'
|
||||
self.event.settings.test = 'bar'
|
||||
self.assertEqual(self.organizer.settings.test, 'foo')
|
||||
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.assertEqual(self.organizer.settings.test, 'foo')
|
||||
self.assertEqual(self.event.settings.test, 'bar')
|
||||
|
||||
def test_default(self):
|
||||
self.assertEqual(self.organizer.settings.test_default, 'def')
|
||||
self.assertEqual(self.event.settings.test_default, 'def')
|
||||
|
||||
def test_delete(self):
|
||||
self.organizer.settings.test = 'foo'
|
||||
self.event.settings.test = 'bar'
|
||||
self.assertEqual(self.organizer.settings.test, 'foo')
|
||||
self.assertEqual(self.event.settings.test, 'bar')
|
||||
|
||||
del self.event.settings.test
|
||||
self.assertEqual(self.event.settings.test, 'foo')
|
||||
|
||||
self.event = Event.objects.get(identity=self.event.identity)
|
||||
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.assertIsNone(self.organizer.settings.test)
|
||||
@@ -2,9 +2,9 @@ from django.test import TestCase
|
||||
from django.utils.timezone import now
|
||||
from django.conf import settings
|
||||
|
||||
from pretixbase.models import Event, Organizer
|
||||
from pretixbase.plugins import get_all_plugins
|
||||
from pretixbase.signals import determine_availability
|
||||
from pretix.base.models import Event, Organizer
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
from pretix.base.signals import determine_availability
|
||||
|
||||
|
||||
class PluginRegistryTest(TestCase):
|
||||
@@ -49,9 +49,9 @@ class PluginSignalTest(TestCase):
|
||||
self.assertEqual(len(responses), 0)
|
||||
|
||||
def test_one_plugin_active(self):
|
||||
self.event.plugins = 'pretixplugins.testdummy'
|
||||
self.event.plugins = 'pretix.plugins.testdummy'
|
||||
self.event.save()
|
||||
payload = {'foo': 'bar'}
|
||||
responses = determine_availability.send(self.event, **payload)
|
||||
self.assertEqual(len(responses), 1)
|
||||
self.assertIn('pretixplugins.testdummy.signals', [r[0].__module__ for r in responses])
|
||||
self.assertIn('pretix.plugins.testdummy.signals', [r[0].__module__ for r in responses])
|
||||
8
src/pretix/control/__init__.py
Normal file
8
src/pretix/control/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PretixControlConfig(AppConfig):
|
||||
name = 'pretix.control'
|
||||
label = 'pretixcontrol'
|
||||
|
||||
default_app_config = 'pretix.control.PretixControlConfig'
|
||||
@@ -7,7 +7,7 @@ from django.contrib.auth import REDIRECT_FIELD_NAME
|
||||
from django.http import HttpResponseNotFound
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from pretixbase.models import Event
|
||||
from pretix.base.models import Event
|
||||
|
||||
|
||||
class PermissionMiddleware:
|
||||
@@ -1,7 +1,7 @@
|
||||
from django.http import HttpResponseForbidden
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from pretixbase.models import EventPermission
|
||||
from pretix.base.models import EventPermission
|
||||
|
||||
|
||||
def event_permission_required(permission):
|
||||
@@ -1,4 +1,4 @@
|
||||
from pretixbase.signals import EventPluginSignal
|
||||
from pretix.base.signals import EventPluginSignal
|
||||
|
||||
|
||||
"""
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../../../../pretixbase/static/bootstrap/less/bootstrap.less";
|
||||
@import "../../../../base/static/bootstrap/less/bootstrap.less";
|
||||
|
||||
body {
|
||||
background: #eee;
|
||||
4
src/pretix/control/static/pretixcontrol/less/main.less
Normal file
4
src/pretix/control/static/pretixcontrol/less/main.less
Normal file
@@ -0,0 +1,4 @@
|
||||
@import "../../../../base/static/bootstrap/less/bootstrap.less";
|
||||
@import "../../../../base/static/fontawesome/less/font-awesome.less";
|
||||
@fa-font-path: "../../fontawesome/fonts";
|
||||
@import "forms.less";
|
||||
@@ -1,7 +1,7 @@
|
||||
from django.test import TestCase, Client
|
||||
|
||||
from pretixbase.models import User
|
||||
from pretixbase.tests import BrowserTest, on_platforms
|
||||
from pretix.base.models import User
|
||||
from pretix.base.tests import BrowserTest, on_platforms
|
||||
|
||||
|
||||
@on_platforms()
|
||||
@@ -1,6 +1,6 @@
|
||||
import datetime
|
||||
from pretixbase.models import User, Organizer, Event, OrganizerPermission, EventPermission
|
||||
from pretixbase.tests import BrowserTest, on_platforms
|
||||
from pretix.base.models import User, Organizer, Event, OrganizerPermission, EventPermission
|
||||
from pretix.base.tests import BrowserTest, on_platforms
|
||||
|
||||
|
||||
@on_platforms()
|
||||
@@ -54,8 +54,8 @@ class EventsTest(BrowserTest):
|
||||
self.driver.get('%s/control/event/%s/%s/settings/plugins' % (self.live_server_url, self.orga1.slug,
|
||||
self.event1.slug))
|
||||
self.assertIn("Restriction by time", self.driver.find_element_by_class_name("form-plugins").text)
|
||||
self.assertIn("Enable", self.driver.find_element_by_name("plugin:pretixplugins.timerestriction").text)
|
||||
self.driver.find_element_by_name("plugin:pretixplugins.timerestriction").click()
|
||||
self.assertIn("Disable", self.driver.find_element_by_name("plugin:pretixplugins.timerestriction").text)
|
||||
self.driver.find_element_by_name("plugin:pretixplugins.timerestriction").click()
|
||||
self.assertIn("Enable", self.driver.find_element_by_name("plugin:pretixplugins.timerestriction").text)
|
||||
self.assertIn("Enable", self.driver.find_element_by_name("plugin:pretix.plugins.timerestriction").text)
|
||||
self.driver.find_element_by_name("plugin:pretix.plugins.timerestriction").click()
|
||||
self.assertIn("Disable", self.driver.find_element_by_name("plugin:pretix.plugins.timerestriction").text)
|
||||
self.driver.find_element_by_name("plugin:pretix.plugins.timerestriction").click()
|
||||
self.assertIn("Enable", self.driver.find_element_by_name("plugin:pretix.plugins.timerestriction").text)
|
||||
@@ -3,9 +3,9 @@ import time
|
||||
import datetime
|
||||
from django.utils import unittest
|
||||
from selenium.webdriver.support.select import Select
|
||||
from pretixbase.models import User, Organizer, Event, OrganizerPermission, EventPermission, ItemCategory, Property, \
|
||||
from pretix.base.models import User, Organizer, Event, OrganizerPermission, EventPermission, ItemCategory, Property, \
|
||||
PropertyValue, Question, Quota, Item
|
||||
from pretixbase.tests import BrowserTest, on_platforms
|
||||
from pretix.base.tests import BrowserTest, on_platforms
|
||||
|
||||
|
||||
class ItemFormTest(BrowserTest):
|
||||
@@ -18,7 +18,7 @@ class ItemFormTest(BrowserTest):
|
||||
self.event1 = Event.objects.create(
|
||||
organizer=self.orga1, name='30C3', slug='30c3',
|
||||
date_from=datetime.datetime(2013, 12, 26, tzinfo=datetime.timezone.utc),
|
||||
)
|
||||
)
|
||||
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)
|
||||
@@ -1,7 +1,7 @@
|
||||
from django.test import TestCase, Client
|
||||
from django.utils.timezone import now
|
||||
|
||||
from pretixbase.models import Event, Organizer, User, EventPermission
|
||||
from pretix.base.models import Event, Organizer, User, EventPermission
|
||||
|
||||
|
||||
class PermissionMiddlewareTest(TestCase):
|
||||
@@ -1,22 +1,22 @@
|
||||
from django.conf.urls import patterns, url, include
|
||||
from pretixcontrol.views import main, event, item
|
||||
from pretix.control.views import main, event, item
|
||||
|
||||
urlpatterns = patterns('',)
|
||||
urlpatterns += patterns(
|
||||
'pretixcontrol.views.auth',
|
||||
'pretix.control.views.auth',
|
||||
url(r'^logout$', 'logout', name='auth.logout'),
|
||||
url(r'^login$', 'login', name='auth.login'),
|
||||
)
|
||||
urlpatterns += patterns(
|
||||
'pretixcontrol.views.main',
|
||||
'pretix.control.views.main',
|
||||
url(r'^$', 'index', name='index'),
|
||||
url(r'^events/$', main.EventList.as_view(), name='events'),
|
||||
)
|
||||
urlpatterns += patterns(
|
||||
'pretixcontrol.views.event',
|
||||
'pretix.control.views.event',
|
||||
url(r'^event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/', include(
|
||||
patterns(
|
||||
'pretixcontrol.views',
|
||||
'pretix.control.views',
|
||||
url(r'^$', 'event.index', name='event.index'),
|
||||
url(r'^settings/$', event.EventUpdate.as_view(), name='event.settings'),
|
||||
url(r'^settings/plugins$', event.EventPlugins.as_view(), name='event.settings.plugins'),
|
||||
@@ -54,5 +54,5 @@ urlpatterns += patterns(
|
||||
name='event.items.quotas.delete'),
|
||||
url(r'^quotas/add$', item.QuotaCreate.as_view(), name='event.items.quotas.add'),
|
||||
)
|
||||
))
|
||||
))
|
||||
)
|
||||
@@ -7,10 +7,10 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from pytz import common_timezones
|
||||
from pretixbase.forms import VersionedModelForm
|
||||
from pretix.base.forms import VersionedModelForm
|
||||
|
||||
from pretixbase.models import Event
|
||||
from pretixcontrol.permissions import EventPermissionRequiredMixin
|
||||
from pretix.base.models import Event
|
||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||
|
||||
|
||||
class EventUpdateForm(VersionedModelForm):
|
||||
@@ -75,7 +75,7 @@ class EventPlugins(EventPermissionRequiredMixin, TemplateView, SingleObjectMixin
|
||||
return self.request.event
|
||||
|
||||
def get_context_data(self, *args, **kwargs) -> dict:
|
||||
from pretixbase.plugins import get_all_plugins
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
context = super().get_context_data(*args, **kwargs)
|
||||
context['plugins'] = [p for p in get_all_plugins() if not p.name.startswith('.')]
|
||||
context['plugins_active'] = self.object.get_plugins()
|
||||
@@ -1,15 +1,15 @@
|
||||
from itertools import product
|
||||
from django import forms
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import transaction, IntegrityError
|
||||
from django.db import transaction
|
||||
from django.forms.widgets import flatatt
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.html import format_html
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from pretixbase.forms import VersionedModelForm
|
||||
from pretix.base.forms import VersionedModelForm
|
||||
|
||||
from pretixbase.models import ItemVariation, PropertyValue, Item
|
||||
from pretix.base.models import ItemVariation, PropertyValue, Item
|
||||
|
||||
|
||||
class TolerantFormsetModelForm(VersionedModelForm):
|
||||
@@ -12,14 +12,14 @@ from django.http import HttpResponseRedirect, HttpResponseForbidden
|
||||
from django.shortcuts import redirect
|
||||
from django.forms.models import inlineformset_factory
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from pretixbase.forms import VersionedModelForm
|
||||
from pretix.base.forms import VersionedModelForm
|
||||
|
||||
from pretixbase.models import (
|
||||
from pretix.base.models import (
|
||||
Item, ItemCategory, Property, ItemVariation, PropertyValue, Question, Quota,
|
||||
Versionable)
|
||||
from pretixcontrol.permissions import EventPermissionRequiredMixin, event_permission_required
|
||||
from pretixcontrol.views.forms import TolerantFormsetModelForm, VariationsField
|
||||
from pretixcontrol.signals import restriction_formset
|
||||
from pretix.control.permissions import EventPermissionRequiredMixin, event_permission_required
|
||||
from pretix.control.views.forms import TolerantFormsetModelForm, VariationsField
|
||||
from pretix.control.signals import restriction_formset
|
||||
|
||||
|
||||
class ItemList(ListView):
|
||||
@@ -649,7 +649,7 @@ class ItemCreate(EventPermissionRequiredMixin, CreateView):
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
'event': self.request.event.slug,
|
||||
'item': self.object.identity,
|
||||
}) + '?success=true'
|
||||
}) + '?success=true'
|
||||
|
||||
def get_form_kwargs(self):
|
||||
"""
|
||||
@@ -1,7 +1,7 @@
|
||||
from django.shortcuts import render
|
||||
from django.views.generic import ListView
|
||||
|
||||
from pretixbase.models import Event
|
||||
from pretix.base.models import Event
|
||||
|
||||
|
||||
class EventList(ListView):
|
||||
@@ -1,12 +1,12 @@
|
||||
from django.apps import AppConfig
|
||||
from pretixbase.plugins import PluginType
|
||||
from pretix.base.plugins import PluginType
|
||||
|
||||
|
||||
class TestDummyApp(AppConfig):
|
||||
name = 'pretixplugins.testdummy'
|
||||
name = 'pretix.plugins.testdummy'
|
||||
verbose_name = '.testdummy'
|
||||
|
||||
class TixlPluginMeta:
|
||||
class PretixPluginMeta:
|
||||
type = PluginType.RESTRICTION
|
||||
name = '.testdummy'
|
||||
version = '1.0.0'
|
||||
@@ -14,4 +14,4 @@ class TestDummyApp(AppConfig):
|
||||
def ready(self):
|
||||
from . import signals # NOQA
|
||||
|
||||
default_app_config = 'pretixplugins.testdummy.TestDummyApp'
|
||||
default_app_config = 'pretix.plugins.testdummy.TestDummyApp'
|
||||
@@ -1,6 +1,6 @@
|
||||
from django.dispatch import receiver
|
||||
|
||||
from pretixbase.signals import determine_availability
|
||||
from pretix.base.signals import determine_availability
|
||||
|
||||
|
||||
@receiver(determine_availability)
|
||||
@@ -1,13 +1,13 @@
|
||||
from django.apps import AppConfig
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from pretixbase.plugins import PluginType
|
||||
from pretix.base.plugins import PluginType
|
||||
|
||||
|
||||
class TimeRestrictionApp(AppConfig):
|
||||
name = 'pretixplugins.timerestriction'
|
||||
name = 'pretix.plugins.timerestriction'
|
||||
verbose_name = _("Time restriction")
|
||||
|
||||
class TixlPluginMeta:
|
||||
class PretixPluginMeta:
|
||||
type = PluginType.RESTRICTION
|
||||
name = _("Restriction by time")
|
||||
author = _("the pretix team")
|
||||
@@ -19,4 +19,4 @@ class TimeRestrictionApp(AppConfig):
|
||||
def ready(self):
|
||||
from . import signals # NOQA
|
||||
|
||||
default_app_config = 'pretixplugins.timerestriction.TimeRestrictionApp'
|
||||
default_app_config = 'pretix.plugins.timerestriction.TimeRestrictionApp'
|
||||
@@ -2,7 +2,7 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import pretixbase.models
|
||||
import pretix.base.models
|
||||
import versions.models
|
||||
|
||||
|
||||
@@ -26,7 +26,8 @@ class Migration(migrations.Migration):
|
||||
('price', models.DecimalField(null=True, blank=True, verbose_name='Price in time frame', max_digits=7, decimal_places=2)),
|
||||
('event', versions.models.VersionedForeignKey(to='pretixbase.Event', related_name='restrictions_timerestriction_timerestriction', verbose_name='Event')),
|
||||
('item', versions.models.VersionedForeignKey(to='pretixbase.Item', blank=True, null=True, related_name='restrictions_timerestriction_timerestriction', verbose_name='Item')),
|
||||
('variations', pretixbase.models.VariationsField(to='pretixbase.ItemVariation', blank=True, verbose_name='Variations', related_name='restrictions_timerestriction_timerestriction')),
|
||||
('variations', pretix.base.models.VariationsField(to='pretixbase.ItemVariation', blank=True,
|
||||
verbose_name='Variations', related_name='restrictions_timerestriction_timerestriction')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Restriction',
|
||||
@@ -1,7 +1,7 @@
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from pretixbase.models import BaseRestriction
|
||||
from pretix.base.models import BaseRestriction
|
||||
|
||||
|
||||
class TimeRestriction(BaseRestriction):
|
||||
@@ -3,10 +3,10 @@ from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.forms.models import inlineformset_factory
|
||||
|
||||
from pretixbase.signals import determine_availability
|
||||
from pretixbase.models import Item
|
||||
from pretixcontrol.views.forms import VariationsField, RestrictionInlineFormset, RestrictionForm
|
||||
from pretixcontrol.signals import restriction_formset
|
||||
from pretix.base.signals import determine_availability
|
||||
from pretix.base.models import Item
|
||||
from pretix.control.views.forms import RestrictionInlineFormset, RestrictionForm
|
||||
from pretix.control.signals import restriction_formset
|
||||
|
||||
from .models import TimeRestriction
|
||||
|
||||
@@ -3,13 +3,13 @@ from datetime import timedelta
|
||||
from django.test import TestCase
|
||||
from django.utils.timezone import now
|
||||
|
||||
from pretixbase.models import (
|
||||
from pretix.base.models import (
|
||||
Event, Organizer, Item, Property, PropertyValue, ItemVariation
|
||||
)
|
||||
|
||||
# Do NOT use relative imports here
|
||||
from pretixplugins.timerestriction import signals
|
||||
from pretixplugins.timerestriction.models import TimeRestriction
|
||||
from pretix.plugins.timerestriction import signals
|
||||
from pretix.plugins.timerestriction.models import TimeRestriction
|
||||
|
||||
|
||||
class TimeRestrictionTest(TestCase):
|
||||
8
src/pretix/presale/__init__.py
Normal file
8
src/pretix/presale/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PretixPresaleConfig(AppConfig):
|
||||
name = 'pretix.presale'
|
||||
label = 'pretixpresale'
|
||||
|
||||
default_app_config = 'pretix.presale.PretixPresaleConfig'
|
||||
@@ -1,13 +1,7 @@
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import resolve
|
||||
from django.utils.encoding import force_str
|
||||
from django.utils.six.moves.urllib.parse import urlparse
|
||||
from django.shortcuts import resolve_url
|
||||
from django.contrib.auth import REDIRECT_FIELD_NAME
|
||||
from django.http import HttpResponseNotFound
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from pretixbase.models import Event
|
||||
from pretix.base.models import Event
|
||||
|
||||
|
||||
class EventMiddleware:
|
||||
@@ -1,5 +1,4 @@
|
||||
.product-row {
|
||||
padding: 10px 0;
|
||||
border-top: 1px solid @table-border-color;
|
||||
|
||||
&.headline, &.simple {
|
||||
@@ -25,14 +24,25 @@
|
||||
color: @alert-warning-text;
|
||||
}
|
||||
}
|
||||
.price {
|
||||
text-align: center;
|
||||
}
|
||||
.cart-row, .product-row {
|
||||
padding: 10px 0;
|
||||
|
||||
.count form {
|
||||
display: inline;
|
||||
}
|
||||
.price, .count {
|
||||
text-align: right;
|
||||
}
|
||||
.price small,
|
||||
.availability-box small {
|
||||
display: block;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
&.total {
|
||||
border-top: 1px solid @table-border-color;
|
||||
}
|
||||
}
|
||||
.checkout-button-row {
|
||||
padding: 15px 0;
|
||||
5
src/pretix/presale/static/pretixpresale/less/main.less
Normal file
5
src/pretix/presale/static/pretixpresale/less/main.less
Normal file
@@ -0,0 +1,5 @@
|
||||
@import "../../../../base/static/bootstrap/less/bootstrap.less";
|
||||
@import "../../../../base/static/fontawesome/less/font-awesome.less";
|
||||
@fa-font-path: "../../fontawesome/fonts";
|
||||
|
||||
@import "event.less";
|
||||
@@ -0,0 +1,63 @@
|
||||
{% load i18n %}
|
||||
{% for line in cart.positions %}
|
||||
<div class="row-fluid cart-row">
|
||||
<div class="col-md-4 col-xs-6">
|
||||
<strong>{{ line.item }}</strong>
|
||||
{% if line.variation %}
|
||||
– {{ line.variation }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6 price">
|
||||
{{ event.currency }} {{ line.price|floatformat:2 }}
|
||||
</div>
|
||||
<div class="col-md-2 col-xs-6 count">
|
||||
{% if editable %}
|
||||
<form action="{% url "presale:event.cart.remove" event=event.slug organizer=event.organizer.slug %}"
|
||||
method="post">
|
||||
{% csrf_token %}
|
||||
{% if line.variation %}
|
||||
<input type="hidden" name="variation_{{ line.item.identity }}_{{ line.variation.identity }}"
|
||||
value="1" />
|
||||
{% else %}
|
||||
<input type="hidden" name="item_{{ line.item.identity }}"
|
||||
value="1" />
|
||||
{% endif %}
|
||||
<button class="btn btn-mini btn-link"><i class="fa fa-minus"></i></button>
|
||||
</form>
|
||||
{% endif %}
|
||||
{{ line.count }}
|
||||
{% if editable %}
|
||||
<form action="{% url "presale:event.cart.add" event=event.slug organizer=event.organizer.slug %}"
|
||||
method="post">
|
||||
{% csrf_token %}
|
||||
{% if line.variation %}
|
||||
<input type="hidden" name="variation_{{ line.item.identity }}_{{ line.variation.identity }}"
|
||||
value="1" />
|
||||
{% else %}
|
||||
<input type="hidden" name="item_{{ line.item.identity }}"
|
||||
value="1" />
|
||||
{% endif %}
|
||||
<button class="btn btn-mini btn-link"><i class="fa fa-plus"></i></button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6 price">
|
||||
<strong>{{ event.currency }} {{ line.total|floatformat:2 }}</strong>
|
||||
{% if line.item.tax_rate %}
|
||||
<br /><small>{% blocktrans trimmed with rate=line.item.tax_rate %}
|
||||
incl. {{ rate }}% taxes
|
||||
{% endblocktrans %}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="row-fluid cart-row total">
|
||||
<div class="col-md-4 col-xs-6">
|
||||
<strong>{% trans "Total" %}</strong>
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6 col-md-offset-5 price">
|
||||
<strong>{{ event.currency }} {{ cart.total|floatformat:2 }}</strong>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
@@ -2,6 +2,34 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
{% if cart.positions %}
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{% trans "Your cart" %}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{% include "pretixpresale/event/fragment_cart.html" with cart=cart event=request.event editable=True %}
|
||||
<div class="row-fluid">
|
||||
<div class="col-md-6 col-xs-12">
|
||||
{% if cart.minutes_left > 0 %}
|
||||
<em>{% blocktrans trimmed with minutes=cart.minutes_left %}
|
||||
The items in your cart are reserved for you for {{ minutes }} minutes.
|
||||
{% endblocktrans %}</em>
|
||||
{% else %}
|
||||
<em>{% trans "The items in your cart are no longer reserved for you." %}</em>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-4 col-md-offset-2 col-xs-12">
|
||||
<a class="btn btn-block btn-primary btn-lg"
|
||||
href="{% url "presale:event.checkout.start" organizer=request.event.organizer.slug event=request.event.slug %}">
|
||||
<i class="fa fa-shopping-cart"></i> {% trans "Proceed with checkout" %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<form method="post"
|
||||
action="{% url "presale:event.cart.add" organizer=request.event.organizer.slug event=request.event.slug %}?next={{ request.path_info|urlencode }}">
|
||||
{% csrf_token %}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user