forked from CGM_Public/pretix_original
* MKBDIGI-185: Added update/create to events * MKBDIGI-185: Added validation for 'slug, 'live' on event endpoint * MKBDIGI-185: Code formatting * MKBDIGI-185: Added 'plugins' to 'event' endpoint * MKBDIGI-185: Merge migrations * MKBDIGI-185: Cleaned up static methods * EBILL-5: Added delete endpoint for event * EBILL-5: Merge migrations * EBILL-5: Fixed imports * EBILL-5: Changed plugins to only list plugins enabled for the event * EBILL-5: Added clone event endpoint * EBILL-5: Removed permissions check API test for events * EBILL-5: Merged master, updated migrations * EBILL-5: Updated api permissions check for CRUD on events * EBILL-5: Removed 'unique_together' constraint on event model * EBILL-5: Removed call to changed static methods in test * EBILL-5: Changed Event 'has_paid_things' to a property for consistency * EBILL-5: Fixed created response code in documentation * EBILL-6: Documentation fixes * EBILL-6: Fixed typo * EBILL-6: Fixed permissions * EBILL-6: Added note on copying settings to documentation * EBILL-6: Created model method for deleting sub objects on event before delete * EBILL-6: Fixed typo * EBILL-6: Re-added meta_data as read-only * EBILL-6: Fixed permissions test * EBILL-6: Added plugins issues check before live. Moved issues property from form to Event model. * EBILL-6: Upped version number in documentation * Add write support for MetaDataField * EBILL-6: Expanded documentation for the clone endpoint, made behaviour of 'is_public' similar to 'plugins' for consistency * EBILL-6: Re-added EventCRUDPermission * EBILL-16: Updated documentation with permission model for the API * EBILL-16: Added 'has_subevents' validation to ensure it cannot be changed once event is created. * EBILL-16: Fixed event clone not differentiating between "not set" and "deliberately set to False" * EBILL-16: Fixed event live validation * EBILL-16: Added logging of live activated/deactivated * EBILL-16: Fixed create event bug when no 'meta_data' supplied * EBILL-16: Typo fixed * EBILL-16: Added log display for "event created" * EBILL-16: Enabling a plugin now calls 'installed' if applicable and log entries are added * EBILL-16: Updated tests for events * Do not allow enabling restricted plugins via the API * Remove unused code
This commit is contained in:
committed by
Raphael Michel
parent
1a0e2031d2
commit
7bb18f6fad
@@ -56,3 +56,18 @@ class EventPermission(BasePermission):
|
||||
if required_permission and required_permission not in request.orgapermset:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class EventCRUDPermission(EventPermission):
|
||||
def has_permission(self, request, view):
|
||||
if not super(EventCRUDPermission, self).has_permission(request, view):
|
||||
return False
|
||||
elif view.action == 'create' and 'can_create_events' not in request.orgapermset:
|
||||
return False
|
||||
elif view.action == 'destroy' and 'can_change_event_settings' not in request.eventpermset:
|
||||
return False
|
||||
elif view.action in ['retrieve', 'update', 'partial_update'] \
|
||||
and 'can_change_event_settings' not in request.eventpermset:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import transaction
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import ugettext as _
|
||||
from django_countries.serializers import CountryFieldMixin
|
||||
from rest_framework.fields import Field
|
||||
|
||||
@@ -14,15 +18,161 @@ class MetaDataField(Field):
|
||||
v.property.name: v.value for v in value.meta_values.all()
|
||||
}
|
||||
|
||||
def to_internal_value(self, data):
|
||||
return {
|
||||
'meta_data': data
|
||||
}
|
||||
|
||||
|
||||
class PluginsField(Field):
|
||||
|
||||
def to_representation(self, obj):
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
|
||||
return {
|
||||
p.module for p in get_all_plugins()
|
||||
if not p.name.startswith('.') and getattr(p, 'visible', True) and p.module in obj.get_plugins()
|
||||
}
|
||||
|
||||
def to_internal_value(self, data):
|
||||
return {
|
||||
'plugins': data
|
||||
}
|
||||
|
||||
|
||||
class EventSerializer(I18nAwareModelSerializer):
|
||||
meta_data = MetaDataField(source='*')
|
||||
meta_data = MetaDataField(required=False, source='*')
|
||||
plugins = PluginsField(required=False, source='*')
|
||||
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = ('name', 'slug', 'live', 'currency', 'date_from',
|
||||
'date_to', 'date_admission', 'is_public', 'presale_start',
|
||||
'presale_end', 'location', 'has_subevents', 'meta_data')
|
||||
'presale_end', 'location', 'has_subevents', 'meta_data', 'plugins')
|
||||
|
||||
def validate(self, data):
|
||||
data = super().validate(data)
|
||||
|
||||
full_data = self.to_internal_value(self.to_representation(self.instance)) if self.instance else {}
|
||||
full_data.update(data)
|
||||
|
||||
Event.clean_dates(data.get('date_from'), data.get('date_to'))
|
||||
Event.clean_presale(data.get('presale_start'), data.get('presale_end'))
|
||||
|
||||
return data
|
||||
|
||||
def validate_has_subevents(self, value):
|
||||
Event.clean_has_subevents(self.instance, value)
|
||||
return value
|
||||
|
||||
def validate_slug(self, value):
|
||||
Event.clean_slug(self.context['request'].organizer, self.instance, value)
|
||||
return value
|
||||
|
||||
def validate_live(self, value):
|
||||
if value:
|
||||
if self.instance is None:
|
||||
raise ValidationError(_('Events cannot be created as \'live\'. Quotas and payment must be added to the '
|
||||
'event before sales can go live.'))
|
||||
else:
|
||||
self.instance.clean_live()
|
||||
return value
|
||||
|
||||
@cached_property
|
||||
def meta_properties(self):
|
||||
return {
|
||||
p.name: p for p in self.context['request'].organizer.meta_properties.all()
|
||||
}
|
||||
|
||||
def validate_meta_data(self, value):
|
||||
for key in value['meta_data'].keys():
|
||||
if key not in self.meta_properties:
|
||||
raise ValidationError(_('Meta data property \'{name}\' does not exist.').format(name=key))
|
||||
return value
|
||||
|
||||
def validate_plugins(self, value):
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
|
||||
plugins_available = {
|
||||
p.module for p in get_all_plugins()
|
||||
if not p.name.startswith('.') and getattr(p, 'visible', True)
|
||||
}
|
||||
|
||||
for plugin in value.get('plugins'):
|
||||
if plugin not in plugins_available:
|
||||
raise ValidationError(_('Unknown plugin: \'{name}\'.').format(name=plugin))
|
||||
|
||||
return value
|
||||
|
||||
@transaction.atomic
|
||||
def create(self, validated_data):
|
||||
meta_data = validated_data.pop('meta_data', None)
|
||||
plugins = validated_data.pop('plugins', None)
|
||||
event = super().create(validated_data)
|
||||
|
||||
# Meta data
|
||||
if meta_data is not None:
|
||||
for key, value in meta_data.items():
|
||||
event.meta_values.create(
|
||||
property=self.meta_properties.get(key),
|
||||
value=value
|
||||
)
|
||||
|
||||
# Plugins
|
||||
if plugins is not None:
|
||||
event.set_active_plugins(plugins)
|
||||
|
||||
return event
|
||||
|
||||
@transaction.atomic
|
||||
def update(self, instance, validated_data):
|
||||
meta_data = validated_data.pop('meta_data', None)
|
||||
plugins = validated_data.pop('plugins', None)
|
||||
event = super().update(instance, validated_data)
|
||||
|
||||
# Meta data
|
||||
if meta_data is not None:
|
||||
current = {mv.property: mv for mv in event.meta_values.select_related('property')}
|
||||
for key, value in meta_data.items():
|
||||
prop = self.meta_properties.get(key)
|
||||
if prop in current:
|
||||
current[prop].value = value
|
||||
current[prop].save()
|
||||
else:
|
||||
event.meta_values.create(
|
||||
property=self.meta_properties.get(key),
|
||||
value=value
|
||||
)
|
||||
|
||||
for prop, current_object in current.items():
|
||||
if prop.name not in meta_data:
|
||||
current_object.delete()
|
||||
|
||||
# Plugins
|
||||
if plugins is not None:
|
||||
event.set_active_plugins(plugins)
|
||||
event.save()
|
||||
|
||||
return event
|
||||
|
||||
|
||||
class CloneEventSerializer(EventSerializer):
|
||||
@transaction.atomic
|
||||
def create(self, validated_data):
|
||||
plugins = validated_data.pop('plugins', None)
|
||||
is_public = validated_data.pop('is_public', None)
|
||||
new_event = super().create(validated_data)
|
||||
|
||||
event = Event.objects.filter(slug=self.context['event'], organizer=self.context['organizer'].pk).first()
|
||||
new_event.copy_data_from(event)
|
||||
|
||||
if plugins is not None:
|
||||
new_event.set_active_plugins(plugins)
|
||||
if is_public is not None:
|
||||
new_event.is_public = is_public
|
||||
new_event.save()
|
||||
|
||||
return new_event
|
||||
|
||||
|
||||
class SubEventItemSerializer(I18nAwareModelSerializer):
|
||||
|
||||
@@ -14,6 +14,7 @@ orga_router.register(r'events', event.EventViewSet)
|
||||
|
||||
event_router = routers.DefaultRouter()
|
||||
event_router.register(r'subevents', event.SubEventViewSet)
|
||||
event_router.register(r'clone', event.CloneEventViewSet)
|
||||
event_router.register(r'items', item.ItemViewSet)
|
||||
event_router.register(r'categories', item.ItemCategoryViewSet)
|
||||
event_router.register(r'questions', item.QuestionViewSet)
|
||||
|
||||
@@ -1,24 +1,123 @@
|
||||
from django.db import transaction
|
||||
from django.db.models import ProtectedError
|
||||
from django_filters.rest_framework import DjangoFilterBackend, FilterSet
|
||||
from rest_framework import filters, viewsets
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
|
||||
from pretix.api.auth.permission import EventCRUDPermission
|
||||
from pretix.api.serializers.event import (
|
||||
EventSerializer, SubEventSerializer, TaxRuleSerializer,
|
||||
CloneEventSerializer, EventSerializer, SubEventSerializer,
|
||||
TaxRuleSerializer,
|
||||
)
|
||||
from pretix.base.models import Event, ItemCategory, TaxRule
|
||||
from pretix.base.models.event import SubEvent
|
||||
from pretix.base.models.organizer import TeamAPIToken
|
||||
from pretix.helpers.dicts import merge_dicts
|
||||
|
||||
|
||||
class EventViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
class EventViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = EventSerializer
|
||||
queryset = Event.objects.none()
|
||||
lookup_field = 'slug'
|
||||
lookup_url_kwarg = 'event'
|
||||
permission_classes = (EventCRUDPermission,)
|
||||
|
||||
def get_queryset(self):
|
||||
return self.request.organizer.events.prefetch_related('meta_values', 'meta_values__property')
|
||||
|
||||
def perform_update(self, serializer):
|
||||
current_live_value = serializer.instance.live
|
||||
updated_live_value = serializer.validated_data.get('live', None)
|
||||
current_plugins_value = serializer.instance.get_plugins()
|
||||
updated_plugins_value = serializer.validated_data.get('plugins', None)
|
||||
|
||||
super().perform_update(serializer)
|
||||
|
||||
if updated_live_value is not None and updated_live_value != current_live_value:
|
||||
log_action = 'pretix.event.live.activated' if updated_live_value else 'pretix.event.live.deactivated'
|
||||
serializer.instance.log_action(
|
||||
log_action,
|
||||
user=self.request.user,
|
||||
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
|
||||
data=self.request.data
|
||||
)
|
||||
|
||||
if updated_plugins_value is not None and set(updated_plugins_value) != set(current_plugins_value):
|
||||
enabled = {m: 'enabled' for m in updated_plugins_value if m not in current_plugins_value}
|
||||
disabled = {m: 'disabled' for m in current_plugins_value if m not in updated_plugins_value}
|
||||
changed = merge_dicts(enabled, disabled)
|
||||
|
||||
for module, action in changed.items():
|
||||
serializer.instance.log_action(
|
||||
'pretix.event.plugins.' + action,
|
||||
user=self.request.user,
|
||||
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
|
||||
data={'plugin': module}
|
||||
)
|
||||
|
||||
other_keys = {k: v for k, v in serializer.validated_data.items() if k not in ['plugins', 'live']}
|
||||
if other_keys:
|
||||
serializer.instance.log_action(
|
||||
'pretix.event.changed',
|
||||
user=self.request.user,
|
||||
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
|
||||
data=self.request.data
|
||||
)
|
||||
|
||||
def perform_create(self, serializer):
|
||||
serializer.save(organizer=self.request.organizer)
|
||||
serializer.instance.log_action(
|
||||
'pretix.event.added',
|
||||
user=self.request.user,
|
||||
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
|
||||
data=self.request.data
|
||||
)
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
if not instance.allow_delete():
|
||||
raise PermissionDenied('The event can not be deleted as it already contains orders. Please set \'live\''
|
||||
' to false to hide the event and take the shop offline instead.')
|
||||
try:
|
||||
with transaction.atomic():
|
||||
instance.organizer.log_action(
|
||||
'pretix.event.deleted', user=self.request.user,
|
||||
data={
|
||||
'event_id': instance.pk,
|
||||
'name': str(instance.name),
|
||||
'logentries': list(instance.logentry_set.values_list('pk', flat=True))
|
||||
}
|
||||
)
|
||||
instance.delete_sub_objects()
|
||||
super().perform_destroy(instance)
|
||||
except ProtectedError:
|
||||
raise PermissionDenied('The event could not be deleted as some constraints (e.g. data created by plug-ins) '
|
||||
'do not allow it.')
|
||||
|
||||
|
||||
class CloneEventViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = CloneEventSerializer
|
||||
queryset = Event.objects.none()
|
||||
lookup_field = 'slug'
|
||||
lookup_url_kwarg = 'event'
|
||||
http_method_names = ['post']
|
||||
write_permission = 'can_create_events'
|
||||
|
||||
def get_serializer_context(self):
|
||||
ctx = super().get_serializer_context()
|
||||
ctx['event'] = self.kwargs['event']
|
||||
ctx['organizer'] = self.request.organizer
|
||||
return ctx
|
||||
|
||||
def perform_create(self, serializer):
|
||||
serializer.save(organizer=self.request.organizer)
|
||||
|
||||
serializer.instance.log_action(
|
||||
'pretix.event.added',
|
||||
user=self.request.user,
|
||||
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
|
||||
data=self.request.data
|
||||
)
|
||||
|
||||
|
||||
class SubEventFilter(FilterSet):
|
||||
class Meta:
|
||||
|
||||
@@ -525,6 +525,40 @@ class Event(EventMixin, LoggedModel):
|
||||
data.update({v.property.name: v.value for v in self.meta_values.select_related('property').all()})
|
||||
return data
|
||||
|
||||
@property
|
||||
def has_payment_provider(self):
|
||||
result = False
|
||||
for provider in self.get_payment_providers().values():
|
||||
if provider.is_enabled and provider.identifier != 'free':
|
||||
result = True
|
||||
break
|
||||
return result
|
||||
|
||||
@property
|
||||
def has_paid_things(self):
|
||||
from .items import Item, ItemVariation
|
||||
|
||||
return Item.objects.filter(event=self, default_price__gt=0).exists()\
|
||||
or ItemVariation.objects.filter(item__event=self, default_price__gt=0).exists()
|
||||
|
||||
@cached_property
|
||||
def live_issues(self):
|
||||
from pretix.base.signals import event_live_issues
|
||||
issues = []
|
||||
|
||||
if self.has_paid_things and not self.has_payment_provider:
|
||||
issues.append(_('You have configured at least one paid product but have not enabled any payment methods.'))
|
||||
|
||||
if not self.quotas.exists():
|
||||
issues.append(_('You need to configure at least one quota to sell anything.'))
|
||||
|
||||
responses = event_live_issues.send(self)
|
||||
for receiver, response in sorted(responses, key=lambda r: str(r[0])):
|
||||
if response:
|
||||
issues.append(response)
|
||||
|
||||
return issues
|
||||
|
||||
def get_users_with_any_permission(self):
|
||||
"""
|
||||
Returns a queryset of users who have any permission to this event.
|
||||
@@ -556,9 +590,78 @@ class Event(EventMixin, LoggedModel):
|
||||
|
||||
return User.objects.annotate(twp=Exists(team_with_perm)).filter(twp=True)
|
||||
|
||||
def clean_live(self):
|
||||
for issue in self.live_issues:
|
||||
if issue:
|
||||
raise ValidationError(issue)
|
||||
|
||||
def allow_delete(self):
|
||||
return not self.orders.exists() and not self.invoices.exists()
|
||||
|
||||
def delete_sub_objects(self):
|
||||
self.items.all().delete()
|
||||
self.subevents.all().delete()
|
||||
|
||||
def set_active_plugins(self, modules, allow_restricted=False):
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
|
||||
plugins_active = self.get_plugins()
|
||||
plugins_available = {
|
||||
p.module: p for p in get_all_plugins()
|
||||
if not p.name.startswith('.') and getattr(p, 'visible', True)
|
||||
}
|
||||
|
||||
enable = [m for m in modules if m not in plugins_active and m in plugins_available]
|
||||
|
||||
for module in enable:
|
||||
if getattr(plugins_available[module].app, 'restricted', False) and not allow_restricted:
|
||||
modules.remove(module)
|
||||
elif hasattr(plugins_available[module].app, 'installed'):
|
||||
getattr(plugins_available[module].app, 'installed')(self)
|
||||
|
||||
self.plugins = ",".join(modules)
|
||||
|
||||
def enable_plugin(self, module, allow_restricted=False):
|
||||
plugins_active = self.get_plugins()
|
||||
|
||||
if module not in plugins_active:
|
||||
plugins_active.append(module)
|
||||
self.set_active_plugins(plugins_active, allow_restricted=allow_restricted)
|
||||
|
||||
def disable_plugin(self, module):
|
||||
plugins_active = self.get_plugins()
|
||||
|
||||
if module in plugins_active:
|
||||
plugins_active.remove(module)
|
||||
self.set_active_plugins(plugins_active)
|
||||
|
||||
@staticmethod
|
||||
def clean_has_subevents(event, has_subevents):
|
||||
if event is not None and event.has_subevents is not None:
|
||||
if event.has_subevents != has_subevents:
|
||||
raise ValidationError(_('Once created an event cannot change between an series and a single event.'))
|
||||
|
||||
@staticmethod
|
||||
def clean_slug(organizer, event, slug):
|
||||
if event is not None and event.slug is not None:
|
||||
if event.slug != slug:
|
||||
raise ValidationError(_('The event slug cannot be changed.'))
|
||||
else:
|
||||
if Event.objects.filter(slug=slug, organizer=organizer).exists():
|
||||
raise ValidationError(_('This slug has already been used for a different event.'))
|
||||
|
||||
@staticmethod
|
||||
def clean_dates(date_from, date_to):
|
||||
if date_from is not None and date_to is not None:
|
||||
if date_from > date_to:
|
||||
raise ValidationError(_('The event cannot end before it starts.'))
|
||||
|
||||
@staticmethod
|
||||
def clean_presale(presale_start, presale_end):
|
||||
if presale_start is not None and presale_end is not None:
|
||||
if presale_start > presale_end:
|
||||
raise ValidationError(_('The event\'s presale cannot end before it starts.'))
|
||||
|
||||
|
||||
class SubEvent(EventMixin, LoggedModel):
|
||||
"""
|
||||
|
||||
@@ -229,6 +229,7 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
|
||||
'pretix.event.plugins.disabled': _('A plugin has been disabled.'),
|
||||
'pretix.event.live.activated': _('The shop has been taken live.'),
|
||||
'pretix.event.live.deactivated': _('The shop has been taken offline.'),
|
||||
'pretix.event.added': _('The event has been created.'),
|
||||
'pretix.event.changed': _('The event settings have been changed.'),
|
||||
'pretix.event.question.option.added': _('An answer option has been added to the question.'),
|
||||
'pretix.event.question.option.deleted': _('An answer option has been removed from the question.'),
|
||||
|
||||
@@ -30,13 +30,13 @@ from pytz import timezone
|
||||
|
||||
from pretix.base.i18n import LazyCurrencyNumber
|
||||
from pretix.base.models import (
|
||||
CachedCombinedTicket, CachedTicket, Event, Item, ItemVariation, LogEntry,
|
||||
Order, RequiredAction, TaxRule, Voucher,
|
||||
CachedCombinedTicket, CachedTicket, Event, LogEntry, Order, RequiredAction,
|
||||
TaxRule, Voucher,
|
||||
)
|
||||
from pretix.base.models.event import EventMetaValue
|
||||
from pretix.base.services import tickets
|
||||
from pretix.base.services.invoices import build_preview_invoice_pdf
|
||||
from pretix.base.signals import event_live_issues, register_ticket_outputs
|
||||
from pretix.base.signals import register_ticket_outputs
|
||||
from pretix.base.templatetags.money import money_filter
|
||||
from pretix.control.forms.event import (
|
||||
CommentForm, DisplaySettingsForm, EventDeleteForm, EventMetaValueForm,
|
||||
@@ -200,34 +200,29 @@ class EventPlugins(EventSettingsViewMixin, EventPermissionRequiredMixin, Templat
|
||||
|
||||
self.object = self.get_object()
|
||||
|
||||
plugins_active = self.object.get_plugins()
|
||||
plugins_available = {
|
||||
p.module: p for p in get_all_plugins()
|
||||
if not p.name.startswith('.') and getattr(p, 'visible', True)
|
||||
}
|
||||
|
||||
with transaction.atomic():
|
||||
allow_restricted = request.user.has_active_staff_session(request.session.session_key)
|
||||
|
||||
for key, value in request.POST.items():
|
||||
if key.startswith("plugin:"):
|
||||
module = key.split(":")[1]
|
||||
if value == "enable" and module in plugins_available:
|
||||
if getattr(plugins_available[module], 'restricted', False):
|
||||
if not request.user.has_active_staff_session(request.session.session_key):
|
||||
if not allow_restricted:
|
||||
continue
|
||||
|
||||
if hasattr(plugins_available[module].app, 'installed'):
|
||||
getattr(plugins_available[module].app, 'installed')(self.request.event)
|
||||
|
||||
self.request.event.log_action('pretix.event.plugins.enabled', user=self.request.user,
|
||||
data={'plugin': module})
|
||||
if module not in plugins_active:
|
||||
plugins_active.append(module)
|
||||
self.object.enable_plugin(module, allow_restricted=allow_restricted)
|
||||
else:
|
||||
self.request.event.log_action('pretix.event.plugins.disabled', user=self.request.user,
|
||||
data={'plugin': module})
|
||||
if module in plugins_active:
|
||||
plugins_active.remove(module)
|
||||
self.object.plugins = ",".join(plugins_active)
|
||||
self.object.disable_plugin(module)
|
||||
self.object.save()
|
||||
messages.success(self.request, _('Your changes have been saved.'))
|
||||
return redirect(self.get_success_url())
|
||||
@@ -749,38 +744,11 @@ class EventLive(EventPermissionRequiredMixin, TemplateView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['issues'] = self.issues
|
||||
ctx['issues'] = self.request.event.live_issues
|
||||
return ctx
|
||||
|
||||
@cached_property
|
||||
def issues(self):
|
||||
issues = []
|
||||
has_paid_things = (
|
||||
Item.objects.filter(event=self.request.event, default_price__gt=0).exists()
|
||||
or ItemVariation.objects.filter(item__event=self.request.event, default_price__gt=0).exists()
|
||||
)
|
||||
|
||||
has_payment_provider = False
|
||||
for provider in self.request.event.get_payment_providers().values():
|
||||
if provider.is_enabled and provider.identifier != 'free':
|
||||
has_payment_provider = True
|
||||
break
|
||||
|
||||
if has_paid_things and not has_payment_provider:
|
||||
issues.append(_('You have configured at least one paid product but have not enabled any payment methods.'))
|
||||
|
||||
if not self.request.event.quotas.exists():
|
||||
issues.append(_('You need to configure at least one quota to sell anything.'))
|
||||
|
||||
responses = event_live_issues.send(self.request.event)
|
||||
for receiver, response in sorted(responses, key=lambda r: str(r[0])):
|
||||
if response:
|
||||
issues.append(response)
|
||||
|
||||
return issues
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
if request.POST.get("live") == "true" and not self.issues:
|
||||
if request.POST.get("live") == "true" and not self.request.event.live_issues:
|
||||
request.event.live = True
|
||||
request.event.save()
|
||||
self.request.event.log_action(
|
||||
@@ -831,8 +799,7 @@ class EventDelete(EventPermissionRequiredMixin, FormView):
|
||||
'logentries': list(self.request.event.logentry_set.values_list('pk', flat=True))
|
||||
}
|
||||
)
|
||||
self.request.event.items.all().delete()
|
||||
self.request.event.subevents.all().delete()
|
||||
self.request.event.delete_sub_objects()
|
||||
self.request.event.delete()
|
||||
messages.success(self.request, _('The event has been deleted.'))
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
@@ -27,7 +27,8 @@ def event(organizer, meta_prop):
|
||||
e = Event.objects.create(
|
||||
organizer=organizer, name='Dummy', slug='dummy',
|
||||
date_from=datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC),
|
||||
plugins='pretix.plugins.banktransfer,pretix.plugins.ticketoutputpdf'
|
||||
plugins='pretix.plugins.banktransfer,pretix.plugins.ticketoutputpdf',
|
||||
is_public=True
|
||||
)
|
||||
e.meta_values.create(property=meta_prop, value="Conference")
|
||||
return e
|
||||
@@ -60,6 +61,7 @@ def team(organizer):
|
||||
return Team.objects.create(
|
||||
organizer=organizer,
|
||||
can_change_items=True,
|
||||
can_create_events=True,
|
||||
can_change_event_settings=True,
|
||||
can_change_vouchers=True,
|
||||
can_view_vouchers=True,
|
||||
|
||||
@@ -1,4 +1,77 @@
|
||||
from datetime import datetime, timedelta
|
||||
from decimal import Decimal
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
from django_countries.fields import Country
|
||||
from pytz import UTC
|
||||
|
||||
from pretix.base.models import (
|
||||
CartPosition, Event, InvoiceAddress, Order, OrderPosition,
|
||||
)
|
||||
from pretix.base.models.orders import OrderFee
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def variations(item):
|
||||
v = list()
|
||||
v.append(item.variations.create(value="ChildA1"))
|
||||
v.append(item.variations.create(value="ChildA2"))
|
||||
return v
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def order(event, item, taxrule):
|
||||
testtime = datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC)
|
||||
|
||||
with mock.patch('django.utils.timezone.now') as mock_now:
|
||||
mock_now.return_value = testtime
|
||||
o = Order.objects.create(
|
||||
code='FOO', event=event, email='dummy@dummy.test',
|
||||
status=Order.STATUS_PENDING, secret="k24fiuwvu8kxz3y1",
|
||||
datetime=datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC),
|
||||
expires=datetime(2017, 12, 10, 10, 0, 0, tzinfo=UTC),
|
||||
total=23, payment_provider='banktransfer', locale='en'
|
||||
)
|
||||
o.fees.create(fee_type=OrderFee.FEE_TYPE_PAYMENT, value=Decimal('0.25'), tax_rate=Decimal('19.00'),
|
||||
tax_value=Decimal('0.05'), tax_rule=taxrule)
|
||||
InvoiceAddress.objects.create(order=o, company="Sample company", country=Country('NZ'))
|
||||
return o
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def order_position(item, order, taxrule, variations):
|
||||
op = OrderPosition.objects.create(
|
||||
order=order,
|
||||
item=item,
|
||||
variation=variations[0],
|
||||
tax_rule=taxrule,
|
||||
tax_rate=taxrule.rate,
|
||||
tax_value=Decimal("3"),
|
||||
price=Decimal("23"),
|
||||
attendee_name="Peter",
|
||||
secret="z3fsn8jyufm5kpk768q69gkbyr5f4h6w"
|
||||
)
|
||||
return op
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def cart_position(event, item, variations):
|
||||
testtime = datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC)
|
||||
|
||||
with mock.patch('django.utils.timezone.now') as mock_now:
|
||||
mock_now.return_value = testtime
|
||||
c = CartPosition.objects.create(
|
||||
event=event,
|
||||
item=item,
|
||||
datetime=datetime.now(),
|
||||
expires=datetime.now() + timedelta(days=1),
|
||||
variation=variations[0],
|
||||
price=Decimal("23"),
|
||||
cart_id="z3fsn8jyufm5kpk768q69gkbyr5f4h6w"
|
||||
)
|
||||
return c
|
||||
|
||||
|
||||
TEST_EVENT_RES = {
|
||||
"name": {"en": "Dummy"},
|
||||
@@ -7,22 +80,473 @@ TEST_EVENT_RES = {
|
||||
"date_from": "2017-12-27T10:00:00Z",
|
||||
"date_to": None,
|
||||
"date_admission": None,
|
||||
"is_public": False,
|
||||
"is_public": True,
|
||||
"presale_start": None,
|
||||
"presale_end": None,
|
||||
"location": None,
|
||||
"slug": "dummy",
|
||||
"has_subevents": False,
|
||||
"meta_data": {"type": "Conference"}
|
||||
"meta_data": {"type": "Conference"},
|
||||
'plugins': {
|
||||
'pretix.plugins.banktransfer',
|
||||
'pretix.plugins.ticketoutputpdf'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def item(event):
|
||||
return event.items.create(name="Budget Ticket", default_price=23)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def free_item(event):
|
||||
return event.items.create(name="Free Ticket", default_price=0)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def free_quota(event, free_item):
|
||||
q = event.quotas.create(name="Budget Quota", size=200)
|
||||
q.items.add(free_item)
|
||||
return q
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_list(token_client, organizer, event):
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/'.format(organizer.slug))
|
||||
assert resp.status_code == 200
|
||||
print(resp.data)
|
||||
assert TEST_EVENT_RES == dict(resp.data['results'][0])
|
||||
assert TEST_EVENT_RES == resp.data['results'][0]
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_get(token_client, organizer, event):
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug))
|
||||
assert resp.status_code == 200
|
||||
print(resp.data)
|
||||
assert TEST_EVENT_RES == resp.data
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_create(token_client, organizer, event, meta_prop):
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/'.format(organizer.slug),
|
||||
{
|
||||
"name": {
|
||||
"de": "Demo Konference 2020 Test",
|
||||
"en": "Demo Conference 2020 Test"
|
||||
},
|
||||
"live": False,
|
||||
"currency": "EUR",
|
||||
"date_from": "2017-12-27T10:00:00Z",
|
||||
"date_to": "2017-12-28T10:00:00Z",
|
||||
"date_admission": None,
|
||||
"is_public": False,
|
||||
"presale_start": None,
|
||||
"presale_end": None,
|
||||
"location": None,
|
||||
"slug": "2030",
|
||||
"meta_data": {
|
||||
meta_prop.name: "Conference"
|
||||
}
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 201
|
||||
assert organizer.events.get(slug="2030").meta_values.filter(
|
||||
property__name=meta_prop.name, value="Conference"
|
||||
).exists()
|
||||
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/'.format(organizer.slug),
|
||||
{
|
||||
"name": {
|
||||
"de": "Demo Konference 2020 Test",
|
||||
"en": "Demo Conference 2020 Test"
|
||||
},
|
||||
"live": False,
|
||||
"currency": "EUR",
|
||||
"date_from": "2017-12-27T10:00:00Z",
|
||||
"date_to": "2017-12-28T10:00:00Z",
|
||||
"date_admission": None,
|
||||
"is_public": False,
|
||||
"presale_start": None,
|
||||
"presale_end": None,
|
||||
"location": None,
|
||||
"slug": "2020",
|
||||
"meta_data": {
|
||||
"foo": "bar"
|
||||
}
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.content.decode() == '{"meta_data":["Meta data property \'foo\' does not exist."]}'
|
||||
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/'.format(organizer.slug),
|
||||
{
|
||||
"name": {
|
||||
"de": "Demo Konference 2020 Test",
|
||||
"en": "Demo Conference 2020 Test"
|
||||
},
|
||||
"live": False,
|
||||
"currency": "EUR",
|
||||
"date_from": "2017-12-27T10:00:00Z",
|
||||
"date_to": "2017-12-28T10:00:00Z",
|
||||
"date_admission": None,
|
||||
"is_public": False,
|
||||
"presale_start": None,
|
||||
"presale_end": None,
|
||||
"location": None,
|
||||
"slug": event.slug,
|
||||
"meta_data": {
|
||||
"type": "Conference"
|
||||
}
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.content.decode() == '{"slug":["This slug has already been used for a different event."]}'
|
||||
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/'.format(organizer.slug),
|
||||
{
|
||||
"name": {
|
||||
"de": "Demo Konference 2020 Test",
|
||||
"en": "Demo Conference 2020 Test"
|
||||
},
|
||||
"live": True,
|
||||
"currency": "EUR",
|
||||
"date_from": "2017-12-27T10:00:00Z",
|
||||
"date_to": "2017-12-28T10:00:00Z",
|
||||
"date_admission": None,
|
||||
"is_public": False,
|
||||
"presale_start": None,
|
||||
"presale_end": None,
|
||||
"location": None,
|
||||
"slug": "2031",
|
||||
"meta_data": {
|
||||
"type": "Conference"
|
||||
}
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.content.decode() == '{"live":["Events cannot be created as \'live\'. Quotas and payment must be added ' \
|
||||
'to the event before sales can go live."]}'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_create_with_clone(token_client, organizer, event, meta_prop):
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/{}/clone/'.format(organizer.slug, event.slug),
|
||||
{
|
||||
"name": {
|
||||
"de": "Demo Konference 2020 Test",
|
||||
"en": "Demo Conference 2020 Test"
|
||||
},
|
||||
"live": False,
|
||||
"currency": "EUR",
|
||||
"date_from": "2018-12-27T10:00:00Z",
|
||||
"date_to": "2018-12-28T10:00:00Z",
|
||||
"date_admission": None,
|
||||
"is_public": False,
|
||||
"presale_start": None,
|
||||
"presale_end": None,
|
||||
"location": None,
|
||||
"slug": "2030",
|
||||
"meta_data": {
|
||||
"type": "Conference"
|
||||
},
|
||||
"plugins": [
|
||||
"pretix.plugins.ticketoutputpdf"
|
||||
]
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
|
||||
assert resp.status_code == 201
|
||||
cloned_event = Event.objects.get(organizer=organizer.pk, slug='2030')
|
||||
assert cloned_event.plugins == 'pretix.plugins.ticketoutputpdf'
|
||||
assert cloned_event.is_public is False
|
||||
assert organizer.events.get(slug="2030").meta_values.filter(
|
||||
property__name=meta_prop.name, value="Conference"
|
||||
).exists()
|
||||
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/{}/clone/'.format(organizer.slug, event.slug),
|
||||
{
|
||||
"name": {
|
||||
"de": "Demo Konference 2020 Test",
|
||||
"en": "Demo Conference 2020 Test"
|
||||
},
|
||||
"live": False,
|
||||
"currency": "EUR",
|
||||
"date_from": "2018-12-27T10:00:00Z",
|
||||
"date_to": "2018-12-28T10:00:00Z",
|
||||
"date_admission": None,
|
||||
"presale_start": None,
|
||||
"presale_end": None,
|
||||
"location": None,
|
||||
"slug": "2031",
|
||||
"meta_data": {
|
||||
"type": "Conference"
|
||||
}
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
|
||||
assert resp.status_code == 201
|
||||
cloned_event = Event.objects.get(organizer=organizer.pk, slug='2031')
|
||||
assert cloned_event.plugins == "pretix.plugins.banktransfer,pretix.plugins.ticketoutputpdf"
|
||||
assert cloned_event.is_public is True
|
||||
assert organizer.events.get(slug="2031").meta_values.filter(
|
||||
property__name=meta_prop.name, value="Conference"
|
||||
).exists()
|
||||
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/{}/clone/'.format(organizer.slug, event.slug),
|
||||
{
|
||||
"name": {
|
||||
"de": "Demo Konference 2020 Test",
|
||||
"en": "Demo Conference 2020 Test"
|
||||
},
|
||||
"live": False,
|
||||
"currency": "EUR",
|
||||
"date_from": "2018-12-27T10:00:00Z",
|
||||
"date_to": "2018-12-28T10:00:00Z",
|
||||
"date_admission": None,
|
||||
"presale_start": None,
|
||||
"presale_end": None,
|
||||
"location": None,
|
||||
"slug": "2032",
|
||||
"plugins": []
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
|
||||
assert resp.status_code == 201
|
||||
cloned_event = Event.objects.get(organizer=organizer.pk, slug='2032')
|
||||
assert cloned_event.plugins == ""
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_put_with_clone(token_client, organizer, event, meta_prop):
|
||||
resp = token_client.put(
|
||||
'/api/v1/organizers/{}/events/{}/clone/'.format(organizer.slug, event.slug),
|
||||
{},
|
||||
format='json'
|
||||
)
|
||||
|
||||
assert resp.status_code == 405
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_patch_with_clone(token_client, organizer, event, meta_prop):
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/events/{}/clone/'.format(organizer.slug, event.slug),
|
||||
{},
|
||||
format='json'
|
||||
)
|
||||
|
||||
assert resp.status_code == 405
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_delete_with_clone(token_client, organizer, event, meta_prop):
|
||||
resp = token_client.delete(
|
||||
'/api/v1/organizers/{}/events/{}/clone/'.format(organizer.slug, event.slug),
|
||||
{},
|
||||
format='json'
|
||||
)
|
||||
|
||||
assert resp.status_code == 405
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_update(token_client, organizer, event, item, meta_prop):
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
|
||||
{
|
||||
"date_from": "2018-12-27T10:00:00Z",
|
||||
"date_to": "2018-12-28T10:00:00Z",
|
||||
"currency": "DKK",
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
event = Event.objects.get(organizer=organizer.pk, slug=resp.data['slug'])
|
||||
assert event.currency == "DKK"
|
||||
assert organizer.events.get(slug=resp.data['slug']).meta_values.filter(
|
||||
property__name=meta_prop.name, value="Conference"
|
||||
).exists()
|
||||
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
|
||||
{
|
||||
"date_from": "2017-12-27T10:00:00Z",
|
||||
"date_to": "2017-12-26T10:00:00Z"
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.content.decode() == '{"non_field_errors":["The event cannot end before it starts."]}'
|
||||
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
|
||||
{
|
||||
"presale_start": "2017-12-27T10:00:00Z",
|
||||
"presale_end": "2017-12-26T10:00:00Z"
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.content.decode() == '{"non_field_errors":["The event\'s presale cannot end before it starts."]}'
|
||||
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
|
||||
{
|
||||
"slug": "testing"
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.content.decode() == '{"slug":["The event slug cannot be changed."]}'
|
||||
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
|
||||
{
|
||||
"has_subevents": True
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.content.decode() == '{"has_subevents":["Once created an event cannot change between an series and a ' \
|
||||
'single event."]}'
|
||||
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
|
||||
{
|
||||
"meta_data": {
|
||||
meta_prop.name: "Workshop"
|
||||
}
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
assert organizer.events.get(slug=resp.data['slug']).meta_values.filter(
|
||||
property__name=meta_prop.name, value="Workshop"
|
||||
).exists()
|
||||
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
|
||||
{
|
||||
"meta_data": {
|
||||
}
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
assert not organizer.events.get(slug=resp.data['slug']).meta_values.filter(
|
||||
property__name=meta_prop.name
|
||||
).exists()
|
||||
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
|
||||
{
|
||||
"meta_data": {
|
||||
"test": "test"
|
||||
}
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.content.decode() == '{"meta_data":["Meta data property \'test\' does not exist."]}'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_update_live_no_product(token_client, organizer, event):
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
|
||||
{
|
||||
"live": True
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.content.decode() == '{"live":["You need to configure at least one quota to sell anything."]}'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_update_live_no_payment_method(token_client, organizer, event, item):
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
|
||||
{
|
||||
"live": True
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.content.decode() == '{"live":["You have configured at least one paid product but have not enabled any ' \
|
||||
'payment methods."]}'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_update_live_free_product(token_client, organizer, event, free_item, free_quota):
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
|
||||
{
|
||||
"live": True
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_update_plugins(token_client, organizer, event, free_item, free_quota):
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
|
||||
{
|
||||
"plugins": [
|
||||
"pretix.plugins.ticketoutputpdf",
|
||||
"pretix.plugins.pretixdroid"
|
||||
]
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
assert resp.data.get('plugins') == {
|
||||
"pretix.plugins.ticketoutputpdf",
|
||||
"pretix.plugins.pretixdroid"
|
||||
}
|
||||
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
|
||||
{
|
||||
"plugins": {
|
||||
"pretix.plugins.banktransfer"
|
||||
}
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
assert resp.data.get('plugins') == {
|
||||
"pretix.plugins.banktransfer"
|
||||
}
|
||||
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
|
||||
{
|
||||
"plugins": {
|
||||
"pretix.plugins.test"
|
||||
}
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.content.decode() == '{"plugins":["Unknown plugin: \'pretix.plugins.test\'."]}'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -32,3 +556,28 @@ def test_event_detail(token_client, organizer, event, team):
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug))
|
||||
assert resp.status_code == 200
|
||||
assert TEST_EVENT_RES == resp.data
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_delete(token_client, organizer, event):
|
||||
resp = token_client.delete('/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug))
|
||||
assert resp.status_code == 204
|
||||
assert not organizer.events.filter(pk=event.id).exists()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_with_order_position_not_delete(token_client, organizer, event, item, order_position):
|
||||
resp = token_client.delete('/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug))
|
||||
assert resp.status_code == 403
|
||||
assert resp.content.decode() == '{"detail":"The event can not be deleted as it already contains orders. Please ' \
|
||||
'set \'live\' to false to hide the event and take the shop offline instead."}'
|
||||
assert organizer.events.filter(pk=event.id).exists()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_with_cart_position_not_delete(token_client, organizer, event, item, cart_position):
|
||||
resp = token_client.delete('/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug))
|
||||
assert resp.status_code == 403
|
||||
assert resp.content.decode() == '{"detail":"The event could not be deleted as some constraints (e.g. data ' \
|
||||
'created by plug-ins) do not allow it."}'
|
||||
assert organizer.events.filter(pk=event.id).exists()
|
||||
|
||||
@@ -20,7 +20,7 @@ event_urls = [
|
||||
'checkinlists/',
|
||||
]
|
||||
|
||||
event_permission_urls = [
|
||||
event_permission_sub_urls = [
|
||||
('get', 'can_view_orders', 'orders/', 200),
|
||||
('get', 'can_view_orders', 'orderpositions/', 200),
|
||||
('get', 'can_view_vouchers', 'vouchers/', 200),
|
||||
@@ -68,6 +68,15 @@ event_permission_urls = [
|
||||
('put', 'can_change_event_settings', 'checkinlists/1/', 404),
|
||||
('patch', 'can_change_event_settings', 'checkinlists/1/', 404),
|
||||
('delete', 'can_change_event_settings', 'checkinlists/1/', 404),
|
||||
('post', 'can_create_events', 'clone/', 400),
|
||||
]
|
||||
|
||||
|
||||
event_permission_root_urls = [
|
||||
('post', 'can_create_events', 400),
|
||||
('put', 'can_change_event_settings', 400),
|
||||
('patch', 'can_change_event_settings', 200),
|
||||
('delete', 'can_change_event_settings', 204),
|
||||
]
|
||||
|
||||
|
||||
@@ -137,8 +146,8 @@ def test_event_not_existing(token_client, organizer, url, event):
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize("urlset", event_permission_urls)
|
||||
def test_token_event_permission_allowed(token_client, team, organizer, event, urlset):
|
||||
@pytest.mark.parametrize("urlset", event_permission_sub_urls)
|
||||
def test_token_event_subresources_permission_allowed(token_client, team, organizer, event, urlset):
|
||||
team.all_events = True
|
||||
setattr(team, urlset[1], True)
|
||||
team.save()
|
||||
@@ -148,8 +157,8 @@ def test_token_event_permission_allowed(token_client, team, organizer, event, ur
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize("urlset", event_permission_urls)
|
||||
def test_token_event_permission_not_allowed(token_client, team, organizer, event, urlset):
|
||||
@pytest.mark.parametrize("urlset", event_permission_sub_urls)
|
||||
def test_token_event_subresources_permission_not_allowed(token_client, team, organizer, event, urlset):
|
||||
team.all_events = True
|
||||
setattr(team, urlset[1], False)
|
||||
team.save()
|
||||
@@ -161,6 +170,32 @@ def test_token_event_permission_not_allowed(token_client, team, organizer, event
|
||||
assert resp.status_code in (404, 403)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize("urlset", event_permission_root_urls)
|
||||
def test_token_event_permission_allowed(token_client, team, organizer, event, urlset):
|
||||
team.all_events = True
|
||||
setattr(team, urlset[1], True)
|
||||
team.save()
|
||||
if urlset[0] == 'post':
|
||||
resp = getattr(token_client, urlset[0])('/api/v1/organizers/{}/events/'.format(organizer.slug))
|
||||
else:
|
||||
resp = getattr(token_client, urlset[0])('/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug))
|
||||
assert resp.status_code == urlset[2]
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize("urlset", event_permission_root_urls)
|
||||
def test_token_event_permission_not_allowed(token_client, team, organizer, event, urlset):
|
||||
team.all_events = True
|
||||
setattr(team, urlset[1], False)
|
||||
team.save()
|
||||
if urlset[0] == 'post':
|
||||
resp = getattr(token_client, urlset[0])('/api/v1/organizers/{}/events/'.format(organizer.slug))
|
||||
else:
|
||||
resp = getattr(token_client, urlset[0])('/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug))
|
||||
assert resp.status_code == 403
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_log_out_after_absolute_timeout(user_client, team, organizer, event):
|
||||
session = user_client.session
|
||||
|
||||
Reference in New Issue
Block a user