mirror of
https://github.com/pretix/pretix.git
synced 2025-12-23 17:02:26 +00:00
Added logging for all basic operations
This commit is contained in:
@@ -50,7 +50,7 @@ class SettingsForm(forms.Form):
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.obj = kwargs.pop('obj')
|
self.obj = kwargs.pop('obj')
|
||||||
kwargs['initial'] = self.obj.settings
|
kwargs['initial'] = self.obj.settings.freeze()
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import json
|
|||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.models import SubfieldBase, TextField
|
from django.core.serializers.json import DjangoJSONEncoder
|
||||||
|
from django.db.models import Model, QuerySet, SubfieldBase, TextField
|
||||||
from django.utils import translation
|
from django.utils import translation
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
@@ -233,3 +234,15 @@ class I18nTextField(I18nFieldMixin, TextField, metaclass=SubfieldBase):
|
|||||||
Like I18nCharField, but for TextFields.
|
Like I18nCharField, but for TextFields.
|
||||||
"""
|
"""
|
||||||
widget = I18nTextarea
|
widget = I18nTextarea
|
||||||
|
|
||||||
|
|
||||||
|
class I18nJSONEncoder(DjangoJSONEncoder):
|
||||||
|
def default(self, obj):
|
||||||
|
if isinstance(obj, LazyI18nString):
|
||||||
|
return obj.data
|
||||||
|
elif isinstance(obj, QuerySet):
|
||||||
|
return list(obj)
|
||||||
|
elif isinstance(obj, Model):
|
||||||
|
return {'type': obj.__class__.__name__, 'id': obj.id}
|
||||||
|
else:
|
||||||
|
return super().default(obj)
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ from django.contrib.auth.models import (
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from .base import LoggingMixin
|
||||||
|
|
||||||
|
|
||||||
class UserManager(BaseUserManager):
|
class UserManager(BaseUserManager):
|
||||||
"""
|
"""
|
||||||
@@ -30,7 +32,7 @@ class UserManager(BaseUserManager):
|
|||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
class User(AbstractBaseUser, PermissionsMixin):
|
class User(AbstractBaseUser, PermissionsMixin, LoggingMixin):
|
||||||
"""
|
"""
|
||||||
This is the user model used by pretix for authentication.
|
This is the user model used by pretix for authentication.
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from django.contrib.contenttypes.fields import GenericRelation
|
from django.contrib.contenttypes.fields import GenericRelation
|
||||||
@@ -5,6 +6,8 @@ from django.db import models
|
|||||||
from django.db.models.signals import post_delete
|
from django.db.models.signals import post_delete
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
|
from pretix.base.i18n import I18nJSONEncoder
|
||||||
|
|
||||||
|
|
||||||
def cachedfile_name(instance, filename: str) -> str:
|
def cachedfile_name(instance, filename: str) -> str:
|
||||||
return 'cachedfiles/%012d.%s' % (instance.id, filename.split('.')[-1])
|
return 'cachedfiles/%012d.%s' % (instance.id, filename.split('.')[-1])
|
||||||
@@ -29,10 +32,18 @@ def cached_file_delete(sender, instance, **kwargs):
|
|||||||
instance.file.delete(False)
|
instance.file.delete(False)
|
||||||
|
|
||||||
|
|
||||||
class LoggedModel(models.Model):
|
class LoggingMixin:
|
||||||
logentries = GenericRelation('LogEntry')
|
logentries = GenericRelation('LogEntry')
|
||||||
|
|
||||||
def log_action(self, user, action, data):
|
def log_action(self, action, data=None, user=None):
|
||||||
|
"""
|
||||||
|
Create a LogEntry object that is related to this object.
|
||||||
|
See the LogEntry documentation for details.
|
||||||
|
|
||||||
|
:param action: The namespaced action code
|
||||||
|
:param data: Any JSON-serializable object
|
||||||
|
:param user: The user performing the action (optional)
|
||||||
|
"""
|
||||||
from .log import LogEntry
|
from .log import LogEntry
|
||||||
from .event import Event
|
from .event import Event
|
||||||
|
|
||||||
@@ -41,7 +52,13 @@ class LoggedModel(models.Model):
|
|||||||
event = self
|
event = self
|
||||||
elif hasattr(self, 'event'):
|
elif hasattr(self, 'event'):
|
||||||
event = self.event
|
event = self.event
|
||||||
LogEntry.objects.create(content_object=self, user=user, action=action, data=data, event=event)
|
l = LogEntry(content_object=self, user=user, action_type=action, event=event)
|
||||||
|
if data:
|
||||||
|
l.data = json.dumps(data, cls=I18nJSONEncoder)
|
||||||
|
l.save()
|
||||||
|
|
||||||
|
|
||||||
|
class LoggedModel(models.Model, LoggingMixin):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import random
|
|||||||
import string
|
import string
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models, transaction
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from typing import List, Union
|
from typing import List, Union
|
||||||
@@ -177,14 +177,6 @@ class Order(LoggedModel):
|
|||||||
return True
|
return True
|
||||||
return False # nothing there to modify
|
return False # nothing there to modify
|
||||||
|
|
||||||
def mark_refunded(self):
|
|
||||||
"""
|
|
||||||
Mark this order as refunded. This sets the payment status and returns the order object.
|
|
||||||
"""
|
|
||||||
self.status = Order.STATUS_REFUNDED
|
|
||||||
self.save()
|
|
||||||
return self
|
|
||||||
|
|
||||||
def _can_be_paid(self) -> Union[bool, str]:
|
def _can_be_paid(self) -> Union[bool, str]:
|
||||||
error_messages = {
|
error_messages = {
|
||||||
'late': _("The payment is too late to be accepted."),
|
'late': _("The payment is too late to be accepted."),
|
||||||
|
|||||||
@@ -359,6 +359,7 @@ class BasePaymentProvider:
|
|||||||
|
|
||||||
:param order: The order object
|
:param order: The order object
|
||||||
"""
|
"""
|
||||||
|
self.order.log_action('pretix.base.order.refunded')
|
||||||
return '<div class="alert alert-warning">%s</div>' % _('The money can not be automatically refunded, '
|
return '<div class="alert alert-warning">%s</div>' % _('The money can not be automatically refunded, '
|
||||||
'please transfer the money back manually.')
|
'please transfer the money back manually.')
|
||||||
|
|
||||||
@@ -379,7 +380,9 @@ class BasePaymentProvider:
|
|||||||
:param request: The HTTP request
|
:param request: The HTTP request
|
||||||
:param order: The order object
|
:param order: The order object
|
||||||
"""
|
"""
|
||||||
order.mark_refunded()
|
from pretix.base.services.orders import mark_order_refunded
|
||||||
|
|
||||||
|
mark_order_refunded(order, user=request.user)
|
||||||
messages.success(request, _('The order has been marked as refunded. Please transfer the money '
|
messages.success(request, _('The order has been marked as refunded. Please transfer the money '
|
||||||
'back to the buyer manually.'))
|
'back to the buyer manually.'))
|
||||||
|
|
||||||
@@ -440,7 +443,9 @@ class FreeOrderProvider(BasePaymentProvider):
|
|||||||
:param request: The HTTP request
|
:param request: The HTTP request
|
||||||
:param order: The order object
|
:param order: The order object
|
||||||
"""
|
"""
|
||||||
order.mark_refunded()
|
from pretix.base.services.orders import mark_order_refunded
|
||||||
|
|
||||||
|
mark_order_refunded(order, user=request.user)
|
||||||
messages.success(request, _('The order has been marked as refunded.'))
|
messages.success(request, _('The order has been marked as refunded.'))
|
||||||
|
|
||||||
def is_allowed(self, request: HttpRequest) -> bool:
|
def is_allowed(self, request: HttpRequest) -> bool:
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from pretix.base.models import (
|
from pretix.base.models import (
|
||||||
CartPosition, Event, EventLock, Order, OrderPosition, Quota,
|
CartPosition, Event, EventLock, Order, OrderPosition, Quota, User,
|
||||||
)
|
)
|
||||||
from pretix.base.payment import BasePaymentProvider
|
from pretix.base.payment import BasePaymentProvider
|
||||||
from pretix.base.services.mail import mail
|
from pretix.base.services.mail import mail
|
||||||
@@ -31,7 +31,7 @@ error_messages = {
|
|||||||
|
|
||||||
|
|
||||||
def mark_order_paid(order: Order, provider: str=None, info: str=None, date: datetime=None, manual: bool=None,
|
def mark_order_paid(order: Order, provider: str=None, info: str=None, date: datetime=None, manual: bool=None,
|
||||||
force: bool=False) -> Order:
|
force: bool=False, user: User=None) -> Order:
|
||||||
"""
|
"""
|
||||||
Marks an order as paid. This sets the payment provider, info and date and returns
|
Marks an order as paid. This sets the payment provider, info and date and returns
|
||||||
the order object.
|
the order object.
|
||||||
@@ -46,6 +46,7 @@ def mark_order_paid(order: Order, provider: str=None, info: str=None, date: date
|
|||||||
:param force: Whether this payment should be marked as paid even if no remaining
|
:param force: Whether this payment should be marked as paid even if no remaining
|
||||||
quota is available (default: ``False``).
|
quota is available (default: ``False``).
|
||||||
:type force: boolean
|
:type force: boolean
|
||||||
|
:param user: The user that performed the change
|
||||||
:raises Quota.QuotaExceededException: if the quota is exceeded and ``force`` is ``False``
|
:raises Quota.QuotaExceededException: if the quota is exceeded and ``force`` is ``False``
|
||||||
"""
|
"""
|
||||||
with order.event.lock():
|
with order.event.lock():
|
||||||
@@ -59,6 +60,13 @@ def mark_order_paid(order: Order, provider: str=None, info: str=None, date: date
|
|||||||
order.payment_manual = manual
|
order.payment_manual = manual
|
||||||
order.status = Order.STATUS_PAID
|
order.status = Order.STATUS_PAID
|
||||||
order.save()
|
order.save()
|
||||||
|
order.log_action('pretix.event.order.paid', {
|
||||||
|
'provider': provider,
|
||||||
|
'info': info,
|
||||||
|
'date': date,
|
||||||
|
'manual': manual,
|
||||||
|
'force': force
|
||||||
|
}, user=user)
|
||||||
order_paid.send(order.event, order=order)
|
order_paid.send(order.event, order=order)
|
||||||
|
|
||||||
mail(
|
mail(
|
||||||
@@ -78,6 +86,32 @@ def mark_order_paid(order: Order, provider: str=None, info: str=None, date: date
|
|||||||
return order
|
return order
|
||||||
|
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def mark_order_refunded(order: Order, user: User=None):
|
||||||
|
"""
|
||||||
|
Mark this order as refunded. This sets the payment status and returns the order object.
|
||||||
|
:param order: The order to change
|
||||||
|
:param user: The user that performed the change
|
||||||
|
"""
|
||||||
|
order.status = Order.STATUS_REFUNDED
|
||||||
|
order.save()
|
||||||
|
order.log_action('pretix.event.order.refunded', user=user)
|
||||||
|
return order
|
||||||
|
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def cancel_order(order: Order, user: User=None):
|
||||||
|
"""
|
||||||
|
Mark this order as canceled
|
||||||
|
:param order: The order to change
|
||||||
|
:param user: The user that performed the change
|
||||||
|
"""
|
||||||
|
order.status = Order.STATUS_CANCELLED
|
||||||
|
order.save()
|
||||||
|
order.log_action('pretix.event.order.cancelled', user=user)
|
||||||
|
return order
|
||||||
|
|
||||||
|
|
||||||
class OrderError(Exception):
|
class OrderError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -154,6 +188,7 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], dt: d
|
|||||||
payment_provider=payment_provider.identifier
|
payment_provider=payment_provider.identifier
|
||||||
)
|
)
|
||||||
OrderPosition.transform_cart_positions(positions, order)
|
OrderPosition.transform_cart_positions(positions, order)
|
||||||
|
order.log_action('pretix.event.order.placed')
|
||||||
order_placed.send(event, order=order)
|
order_placed.send(event, order=order)
|
||||||
return order
|
return order
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,20 @@ class SettingsProxy:
|
|||||||
def _flush(self) -> None:
|
def _flush(self) -> None:
|
||||||
self._cached_obj = None
|
self._cached_obj = None
|
||||||
|
|
||||||
|
def freeze(self):
|
||||||
|
settings = {}
|
||||||
|
for key, v in DEFAULTS.items():
|
||||||
|
settings[key] = self._unserialize(v['default'], v['type'])
|
||||||
|
if self._parent:
|
||||||
|
settings.update(self._parent.settings.freeze())
|
||||||
|
for key, value in self._cache().items():
|
||||||
|
settings[key] = self.get(key)
|
||||||
|
return settings
|
||||||
|
|
||||||
def _unserialize(self, value: str, as_type: type) -> Any:
|
def _unserialize(self, value: str, as_type: type) -> Any:
|
||||||
|
if as_type is None and value is not None and value.startswith('file://'):
|
||||||
|
as_type = File
|
||||||
|
|
||||||
if as_type is not None and isinstance(value, as_type):
|
if as_type is not None and isinstance(value, as_type):
|
||||||
return value
|
return value
|
||||||
elif value is None:
|
elif value is None:
|
||||||
@@ -186,15 +199,14 @@ class SettingsProxy:
|
|||||||
if value is None and default is not None:
|
if value is None and default is not None:
|
||||||
value = default
|
value = default
|
||||||
|
|
||||||
if as_type is None and value is not None and value.startswith('file://'):
|
|
||||||
as_type = File
|
|
||||||
|
|
||||||
return self._unserialize(value, as_type)
|
return self._unserialize(value, as_type)
|
||||||
|
|
||||||
def __getitem__(self, key: str) -> Any:
|
def __getitem__(self, key: str) -> Any:
|
||||||
return self.get(key)
|
return self.get(key)
|
||||||
|
|
||||||
def __getattr__(self, key: str) -> Any:
|
def __getattr__(self, key: str) -> Any:
|
||||||
|
if key.startswith('_'):
|
||||||
|
return super().__getattr__(key)
|
||||||
return self.get(key)
|
return self.get(key)
|
||||||
|
|
||||||
def __setattr__(self, key: str, value: Any) -> None:
|
def __setattr__(self, key: str, value: Any) -> None:
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ def register(request):
|
|||||||
timezone=request.timezone if hasattr(request, 'timezone') else settings.TIME_ZONE
|
timezone=request.timezone if hasattr(request, 'timezone') else settings.TIME_ZONE
|
||||||
)
|
)
|
||||||
user = authenticate(email=user.email, password=form.cleaned_data['password'])
|
user = authenticate(email=user.email, password=form.cleaned_data['password'])
|
||||||
|
user.log_action('pretix.control.auth.user.created', user=user)
|
||||||
auth_login(request, user)
|
auth_login(request, user)
|
||||||
return redirect('control:index')
|
return redirect('control:index')
|
||||||
else:
|
else:
|
||||||
@@ -90,6 +91,7 @@ class Forgot(TemplateView):
|
|||||||
},
|
},
|
||||||
None, locale=user.locale
|
None, locale=user.locale
|
||||||
)
|
)
|
||||||
|
user.log_action('pretix.control.auth.user.forgot_password.mail_sent')
|
||||||
messages.success(request, _('We sent you an e-mail containing further instructions.'))
|
messages.success(request, _('We sent you an e-mail containing further instructions.'))
|
||||||
return redirect('control:auth.forgot')
|
return redirect('control:auth.forgot')
|
||||||
else:
|
else:
|
||||||
@@ -141,6 +143,7 @@ class Recover(TemplateView):
|
|||||||
user.set_password(self.form.cleaned_data['password'])
|
user.set_password(self.form.cleaned_data['password'])
|
||||||
user.save()
|
user.save()
|
||||||
messages.success(request, _('You can now login using your new password.'))
|
messages.success(request, _('You can now login using your new password.'))
|
||||||
|
user.log_action('pretix.control.auth.user.forgot_password.recovered')
|
||||||
return redirect('control:auth.login')
|
return redirect('control:auth.login')
|
||||||
else:
|
else:
|
||||||
return self.get(request, *args, **kwargs)
|
return self.get(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from collections import OrderedDict
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.db import transaction
|
||||||
from django.db.models import Sum
|
from django.db.models import Sum
|
||||||
from django.forms import modelformset_factory
|
from django.forms import modelformset_factory
|
||||||
from django.shortcuts import redirect, render
|
from django.shortcuts import redirect, render
|
||||||
@@ -53,8 +54,17 @@ class EventUpdate(EventPermissionRequiredMixin, UpdateView):
|
|||||||
context['sform'] = self.sform
|
context['sform'] = self.sform
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
@transaction.atomic()
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
self.sform.save()
|
self.sform.save()
|
||||||
|
if self.sform.has_changed():
|
||||||
|
self.request.event.log_action('pretix.event.settings', user=self.request.user, data={
|
||||||
|
k: self.request.event.settings.get(k) for k in self.sform.changed_data
|
||||||
|
})
|
||||||
|
if form.has_changed():
|
||||||
|
self.request.event.log_action('pretix.event.changed', user=self.request.user, data={
|
||||||
|
k: getattr(self.request.event, k) for k in form.changed_data
|
||||||
|
})
|
||||||
messages.success(self.request, _('Your changes have been saved.'))
|
messages.success(self.request, _('Your changes have been saved.'))
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
@@ -97,15 +107,20 @@ class EventPlugins(EventPermissionRequiredMixin, TemplateView, SingleObjectMixin
|
|||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
plugins_active = self.object.get_plugins()
|
plugins_active = self.object.get_plugins()
|
||||||
for key, value in request.POST.items():
|
with transaction.atomic():
|
||||||
if key.startswith("plugin:"):
|
for key, value in request.POST.items():
|
||||||
module = key.split(":")[1]
|
if key.startswith("plugin:"):
|
||||||
if value == "enable":
|
module = key.split(":")[1]
|
||||||
plugins_active.append(module)
|
if value == "enable":
|
||||||
else:
|
self.request.event.log_action('pretix.event.plugins.enabled', user=self.request.user,
|
||||||
plugins_active.remove(module)
|
data={'plugin': module})
|
||||||
self.object.plugins = ",".join(plugins_active)
|
plugins_active.append(module)
|
||||||
self.object.save()
|
else:
|
||||||
|
self.request.event.log_action('pretix.event.plugins.disabled', user=self.request.user,
|
||||||
|
data={'plugin': module})
|
||||||
|
plugins_active.remove(module)
|
||||||
|
self.object.plugins = ",".join(plugins_active)
|
||||||
|
self.object.save()
|
||||||
messages.success(self.request, _('Your changes have been saved.'))
|
messages.success(self.request, _('Your changes have been saved.'))
|
||||||
return redirect(self.get_success_url())
|
return redirect(self.get_success_url())
|
||||||
|
|
||||||
@@ -159,11 +174,18 @@ class PaymentSettings(EventPermissionRequiredMixin, TemplateView, SingleObjectMi
|
|||||||
context['providers'] = self.provider_forms
|
context['providers'] = self.provider_forms
|
||||||
return self.render_to_response(context)
|
return self.render_to_response(context)
|
||||||
|
|
||||||
|
@transaction.atomic()
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
success = True
|
success = True
|
||||||
for provider in self.provider_forms:
|
for provider in self.provider_forms:
|
||||||
if provider.form.is_valid():
|
if provider.form.is_valid():
|
||||||
|
if provider.form.has_changed():
|
||||||
|
self.request.event.log_action(
|
||||||
|
'pretix.event.payment.provider.' + provider.identifier, user=self.request.user, data={
|
||||||
|
k: provider.form.cleaned_data.get(k) for k in provider.form.changed_data
|
||||||
|
}
|
||||||
|
)
|
||||||
provider.form.save()
|
provider.form.save()
|
||||||
else:
|
else:
|
||||||
success = False
|
success = False
|
||||||
@@ -207,16 +229,29 @@ class TicketSettings(EventPermissionRequiredMixin, FormView):
|
|||||||
form.prepare_fields()
|
form.prepare_fields()
|
||||||
return form
|
return form
|
||||||
|
|
||||||
|
@transaction.atomic()
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
success = True
|
success = True
|
||||||
for provider in self.provider_forms:
|
for provider in self.provider_forms:
|
||||||
if provider.form.is_valid():
|
if provider.form.is_valid():
|
||||||
provider.form.save()
|
provider.form.save()
|
||||||
|
if provider.form.has_changed():
|
||||||
|
self.request.event.log_action(
|
||||||
|
'pretix.event.tickets.provider.' + provider.identifier, user=self.request.user, data={
|
||||||
|
k: provider.form.cleaned_data.get(k) for k in provider.form.changed_data
|
||||||
|
}
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
success = False
|
success = False
|
||||||
form = self.get_form(self.get_form_class())
|
form = self.get_form(self.get_form_class())
|
||||||
if success and form.is_valid():
|
if success and form.is_valid():
|
||||||
form.save()
|
form.save()
|
||||||
|
if form.has_changed():
|
||||||
|
self.request.event.log_action(
|
||||||
|
'pretix.event.tickets.settings', user=self.request.user, data={
|
||||||
|
k: form.cleaned_data.get(k) for k in form.changed_data
|
||||||
|
}
|
||||||
|
)
|
||||||
messages.success(self.request, _('Your changes have been saved.'))
|
messages.success(self.request, _('Your changes have been saved.'))
|
||||||
return redirect(self.get_success_url())
|
return redirect(self.get_success_url())
|
||||||
else:
|
else:
|
||||||
@@ -310,6 +345,7 @@ class EventPermissions(EventPermissionRequiredMixin, TemplateView):
|
|||||||
ctx['add_form'] = self.add_form
|
ctx['add_form'] = self.add_form
|
||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
@transaction.atomic()
|
||||||
def post(self, *args, **kwargs):
|
def post(self, *args, **kwargs):
|
||||||
if self.formset.is_valid() and self.add_form.is_valid():
|
if self.formset.is_valid() and self.add_form.is_valid():
|
||||||
if self.add_form.has_changed():
|
if self.add_form.has_changed():
|
||||||
@@ -327,7 +363,22 @@ class EventPermissions(EventPermissionRequiredMixin, TemplateView):
|
|||||||
messages.error(self.request, _('This user already has permissions for this event.'))
|
messages.error(self.request, _('This user already has permissions for this event.'))
|
||||||
return self.get(*args, **kwargs)
|
return self.get(*args, **kwargs)
|
||||||
self.add_form.save()
|
self.add_form.save()
|
||||||
|
logdata = {
|
||||||
|
k: v for k, v in self.add_form.cleaned_data.items()
|
||||||
|
}
|
||||||
|
logdata['user'] = self.add_form.instance.user_id
|
||||||
|
self.request.event.log_action(
|
||||||
|
'pretix.event.permissions.added', user=self.request.user, data=logdata
|
||||||
|
)
|
||||||
for form in self.formset.forms:
|
for form in self.formset.forms:
|
||||||
|
if form.has_changed():
|
||||||
|
changedata = {
|
||||||
|
k: form.cleaned_data.get(k) for k in form.changed_data
|
||||||
|
}
|
||||||
|
changedata['user'] = form.instance.user_id
|
||||||
|
self.request.event.log_action(
|
||||||
|
'pretix.event.permissions.changed', user=self.request.user, data=changedata
|
||||||
|
)
|
||||||
if form.instance.user_id == self.request.user.pk:
|
if form.instance.user_id == self.request.user.pk:
|
||||||
if not form.cleaned_data['can_change_permissions'] or form in self.formset.deleted_forms:
|
if not form.cleaned_data['can_change_permissions'] or form in self.formset.deleted_forms:
|
||||||
messages.error(self.request, _('You cannot remove your own permission to view this page.'))
|
messages.error(self.request, _('You cannot remove your own permission to view this page.'))
|
||||||
|
|||||||
@@ -103,12 +103,14 @@ class CategoryDelete(EventPermissionRequiredMixin, DeleteView):
|
|||||||
except ItemCategory.DoesNotExist:
|
except ItemCategory.DoesNotExist:
|
||||||
raise Http404(_("The requested product category does not exist."))
|
raise Http404(_("The requested product category does not exist."))
|
||||||
|
|
||||||
|
@transaction.atomic()
|
||||||
def delete(self, request, *args, **kwargs):
|
def delete(self, request, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
for item in self.object.items.all():
|
for item in self.object.items.all():
|
||||||
item.category = None
|
item.category = None
|
||||||
item.save()
|
item.save()
|
||||||
success_url = self.get_success_url()
|
success_url = self.get_success_url()
|
||||||
|
self.object.log_action('pretix.event.category.deleted', user=self.request.user)
|
||||||
self.object.delete()
|
self.object.delete()
|
||||||
messages.success(request, _('The selected category has been deleted.'))
|
messages.success(request, _('The selected category has been deleted.'))
|
||||||
return HttpResponseRedirect(success_url)
|
return HttpResponseRedirect(success_url)
|
||||||
@@ -136,8 +138,15 @@ class CategoryUpdate(EventPermissionRequiredMixin, UpdateView):
|
|||||||
except ItemCategory.DoesNotExist:
|
except ItemCategory.DoesNotExist:
|
||||||
raise Http404(_("The requested product category does not exist."))
|
raise Http404(_("The requested product category does not exist."))
|
||||||
|
|
||||||
|
@transaction.atomic()
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
messages.success(self.request, _('Your changes have been saved.'))
|
messages.success(self.request, _('Your changes have been saved.'))
|
||||||
|
if form.has_changed():
|
||||||
|
self.object.log_action(
|
||||||
|
'pretix.event.category.changed', user=self.request.user, data={
|
||||||
|
k: form.cleaned_data.get(k) for k in form.changed_data
|
||||||
|
}
|
||||||
|
)
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get_success_url(self) -> str:
|
def get_success_url(self) -> str:
|
||||||
@@ -160,10 +169,13 @@ class CategoryCreate(EventPermissionRequiredMixin, CreateView):
|
|||||||
'event': self.request.event.slug,
|
'event': self.request.event.slug,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@transaction.atomic()
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.instance.event = self.request.event
|
form.instance.event = self.request.event
|
||||||
messages.success(self.request, _('The new category has been created.'))
|
messages.success(self.request, _('The new category has been created.'))
|
||||||
return super().form_valid(form)
|
ret = super().form_valid(form)
|
||||||
|
form.instance.log_action('pretix.event.category.added', data=dict(form.cleaned_data), user=self.request.user)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class CategoryList(ListView):
|
class CategoryList(ListView):
|
||||||
@@ -248,9 +260,11 @@ class QuestionDelete(EventPermissionRequiredMixin, DeleteView):
|
|||||||
context['dependent'] = list(self.get_object().items.all())
|
context['dependent'] = list(self.get_object().items.all())
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
@transaction.atomic()
|
||||||
def delete(self, request, *args, **kwargs):
|
def delete(self, request, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
success_url = self.get_success_url()
|
success_url = self.get_success_url()
|
||||||
|
self.object.log_action(action='pretix.event.question.deleted', user=request.user)
|
||||||
self.object.delete()
|
self.object.delete()
|
||||||
messages.success(request, _('The selected question has been deleted.'))
|
messages.success(request, _('The selected question has been deleted.'))
|
||||||
return HttpResponseRedirect(success_url)
|
return HttpResponseRedirect(success_url)
|
||||||
@@ -277,7 +291,14 @@ class QuestionUpdate(EventPermissionRequiredMixin, UpdateView):
|
|||||||
except Question.DoesNotExist:
|
except Question.DoesNotExist:
|
||||||
raise Http404(_("The requested question does not exist."))
|
raise Http404(_("The requested question does not exist."))
|
||||||
|
|
||||||
|
@transaction.atomic()
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
|
if form.has_changed():
|
||||||
|
self.object.log_action(
|
||||||
|
'pretix.event.question.changed', user=self.request.user, data={
|
||||||
|
k: form.cleaned_data.get(k) for k in form.changed_data
|
||||||
|
}
|
||||||
|
)
|
||||||
messages.success(self.request, _('Your changes have been saved.'))
|
messages.success(self.request, _('Your changes have been saved.'))
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
@@ -306,9 +327,12 @@ class QuestionCreate(EventPermissionRequiredMixin, CreateView):
|
|||||||
'event': self.request.event.slug,
|
'event': self.request.event.slug,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@transaction.atomic()
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
messages.success(self.request, _('The new question has been created.'))
|
messages.success(self.request, _('The new question has been created.'))
|
||||||
return super().form_valid(form)
|
ret = super().form_valid(form)
|
||||||
|
form.instance.log_action('pretix.event.question.added', user=self.request.user, data=dict(form.cleaned_data))
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class QuotaList(ListView):
|
class QuotaList(ListView):
|
||||||
@@ -378,10 +402,13 @@ class QuotaCreate(EventPermissionRequiredMixin, QuotaEditorMixin, CreateView):
|
|||||||
'event': self.request.event.slug,
|
'event': self.request.event.slug,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@transaction.atomic()
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.instance.event = self.request.event
|
form.instance.event = self.request.event
|
||||||
messages.success(self.request, _('The new quota has been created.'))
|
messages.success(self.request, _('The new quota has been created.'))
|
||||||
return super().form_valid(form)
|
ret = super().form_valid(form)
|
||||||
|
form.instance.log_action('pretix.event.quota.added', user=self.request.user, data=dict(form.cleaned_data))
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class QuotaUpdate(EventPermissionRequiredMixin, QuotaEditorMixin, UpdateView):
|
class QuotaUpdate(EventPermissionRequiredMixin, QuotaEditorMixin, UpdateView):
|
||||||
@@ -399,8 +426,15 @@ class QuotaUpdate(EventPermissionRequiredMixin, QuotaEditorMixin, UpdateView):
|
|||||||
except Quota.DoesNotExist:
|
except Quota.DoesNotExist:
|
||||||
raise Http404(_("The requested quota does not exist."))
|
raise Http404(_("The requested quota does not exist."))
|
||||||
|
|
||||||
|
@transaction.atomic()
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
messages.success(self.request, _('Your changes have been saved.'))
|
messages.success(self.request, _('Your changes have been saved.'))
|
||||||
|
if form.has_changed():
|
||||||
|
self.object.log_action(
|
||||||
|
'pretix.event.quota.changed', user=self.request.user, data={
|
||||||
|
k: form.cleaned_data.get(k) for k in form.changed_data
|
||||||
|
}
|
||||||
|
)
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get_success_url(self) -> str:
|
def get_success_url(self) -> str:
|
||||||
@@ -429,9 +463,11 @@ class QuotaDelete(EventPermissionRequiredMixin, DeleteView):
|
|||||||
context['dependent'] = list(self.get_object().items.all())
|
context['dependent'] = list(self.get_object().items.all())
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
@transaction.atomic()
|
||||||
def delete(self, request, *args, **kwargs):
|
def delete(self, request, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
success_url = self.get_success_url()
|
success_url = self.get_success_url()
|
||||||
|
self.object.log_action(action='pretix.event.quota.deleted', user=request.user)
|
||||||
self.object.delete()
|
self.object.delete()
|
||||||
messages.success(self.request, _('The selected quota has been deleted.'))
|
messages.success(self.request, _('The selected quota has been deleted.'))
|
||||||
return HttpResponseRedirect(success_url)
|
return HttpResponseRedirect(success_url)
|
||||||
@@ -471,9 +507,12 @@ class ItemCreate(EventPermissionRequiredMixin, CreateView):
|
|||||||
'item': self.object.id,
|
'item': self.object.id,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@transaction.atomic()
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
messages.success(self.request, _('Your changes have been saved.'))
|
messages.success(self.request, _('Your changes have been saved.'))
|
||||||
return super().form_valid(form)
|
ret = super().form_valid(form)
|
||||||
|
form.instance.log_action('pretix.event.item.added', user=self.request.user, data=dict(form.cleaned_data))
|
||||||
|
return ret
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
"""
|
"""
|
||||||
@@ -497,8 +536,15 @@ class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, UpdateVie
|
|||||||
'item': self.get_object().id,
|
'item': self.get_object().id,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@transaction.atomic()
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
messages.success(self.request, _('Your changes have been saved.'))
|
messages.success(self.request, _('Your changes have been saved.'))
|
||||||
|
if form.has_changed():
|
||||||
|
self.object.log_action(
|
||||||
|
'pretix.event.item.changed', user=self.request.user, data={
|
||||||
|
k: form.cleaned_data.get(k) for k in form.changed_data
|
||||||
|
}
|
||||||
|
)
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
@@ -543,13 +589,31 @@ class ItemProperties(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView
|
|||||||
for f in formset:
|
for f in formset:
|
||||||
f.instance.event = self.request.event
|
f.instance.event = self.request.event
|
||||||
f.instance.item = self.get_object()
|
f.instance.item = self.get_object()
|
||||||
|
is_created = not f.instance.pk
|
||||||
f.instance.save()
|
f.instance.save()
|
||||||
print(f.instance)
|
if f.has_changed() and not is_created:
|
||||||
|
change_data = {
|
||||||
|
k: f.cleaned_data.get(k) for k in f.changed_data
|
||||||
|
}
|
||||||
|
change_data['id'] = f.instance.pk
|
||||||
|
f.instance.item.log_action(
|
||||||
|
'pretix.event.item.property.changed', user=self.request.user, data=change_data
|
||||||
|
)
|
||||||
|
elif is_created:
|
||||||
|
change_data = dict(f.cleaned_data)
|
||||||
|
change_data['id'] = f.instance.pk
|
||||||
|
f.instance.item.log_action(
|
||||||
|
'pretix.event.item.property.added', user=self.request.user, data=change_data
|
||||||
|
)
|
||||||
|
|
||||||
for n in f.nested:
|
for n in f.nested:
|
||||||
print(n.deleted_forms, n.ordered_forms, n.extra_forms)
|
|
||||||
|
|
||||||
for fn in n.deleted_forms:
|
for fn in n.deleted_forms:
|
||||||
|
f.instance.item.log_action(
|
||||||
|
'pretix.event.item.property.value.deleted', user=self.request.user, data={
|
||||||
|
'id': fn.instance.pk
|
||||||
|
}
|
||||||
|
)
|
||||||
fn.instance.delete()
|
fn.instance.delete()
|
||||||
fn.instance.pk = None
|
fn.instance.pk = None
|
||||||
|
|
||||||
@@ -557,8 +621,24 @@ class ItemProperties(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView
|
|||||||
fn.instance.position = i
|
fn.instance.position = i
|
||||||
fn.instance.prop = f.instance
|
fn.instance.prop = f.instance
|
||||||
fn.save()
|
fn.save()
|
||||||
|
if f.has_changed():
|
||||||
|
change_data = {k: f.cleaned_data.get(k) for k in f.changed_data}
|
||||||
|
change_data['id'] = f.instance.pk
|
||||||
|
f.instance.item.log_action(
|
||||||
|
'pretix.event.item.property.value.changed', user=self.request.user, data=change_data
|
||||||
|
)
|
||||||
|
|
||||||
n.save_new_objects()
|
for form in n.extra_forms:
|
||||||
|
if not form.has_changed():
|
||||||
|
continue
|
||||||
|
if n.can_delete and n._should_delete_form(form):
|
||||||
|
continue
|
||||||
|
change_data = dict(f.cleaned_data)
|
||||||
|
n.save_new(form)
|
||||||
|
change_data['id'] = form.instance.pk
|
||||||
|
f.instance.item.log_action(
|
||||||
|
'pretix.event.item.property.value.added', user=self.request.user, data=change_data
|
||||||
|
)
|
||||||
messages.success(self.request, _('Your changes have been saved.'))
|
messages.success(self.request, _('Your changes have been saved.'))
|
||||||
return redirect(self.get_success_url())
|
return redirect(self.get_success_url())
|
||||||
|
|
||||||
@@ -700,6 +780,13 @@ class ItemVariations(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView
|
|||||||
for form in self.forms_flat:
|
for form in self.forms_flat:
|
||||||
if form.is_valid() and form.has_changed():
|
if form.is_valid() and form.has_changed():
|
||||||
form.save()
|
form.save()
|
||||||
|
change_data = {
|
||||||
|
k: form.cleaned_data.get(k) for k in form.changed_data
|
||||||
|
}
|
||||||
|
change_data['id'] = form.instance.pk
|
||||||
|
self.object.log_action(
|
||||||
|
'pretix.event.item.variation.changed', user=self.request.user, data=change_data
|
||||||
|
)
|
||||||
if hasattr(form.instance, 'creation') and form.instance.creation:
|
if hasattr(form.instance, 'creation') and form.instance.creation:
|
||||||
# We need this special 'creation' field set to true in get_form
|
# We need this special 'creation' field set to true in get_form
|
||||||
# for newly created items as cleanerversion does already set the
|
# for newly created items as cleanerversion does already set the
|
||||||
@@ -758,9 +845,11 @@ class ItemDelete(EventPermissionRequiredMixin, DeleteView):
|
|||||||
raise Http404(_("The requested product does not exist."))
|
raise Http404(_("The requested product does not exist."))
|
||||||
return self.object
|
return self.object
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
def delete(self, request, *args, **kwargs):
|
def delete(self, request, *args, **kwargs):
|
||||||
success_url = self.get_success_url()
|
success_url = self.get_success_url()
|
||||||
if self.is_allowed():
|
if self.is_allowed():
|
||||||
|
self.get_object().log_action('pretix.event.item.deleted', user=self.request.user)
|
||||||
self.get_object().delete()
|
self.get_object().delete()
|
||||||
messages.success(request, _('The selected product has been deleted.'))
|
messages.success(request, _('The selected product has been deleted.'))
|
||||||
return HttpResponseRedirect(success_url)
|
return HttpResponseRedirect(success_url)
|
||||||
@@ -768,6 +857,9 @@ class ItemDelete(EventPermissionRequiredMixin, DeleteView):
|
|||||||
o = self.get_object()
|
o = self.get_object()
|
||||||
o.active = False
|
o.active = False
|
||||||
o.save()
|
o.save()
|
||||||
|
o.log_action('pretix.event.item.changed', user=self.request.user, data={
|
||||||
|
'active': False
|
||||||
|
})
|
||||||
messages.success(request, _('The selected product has been deactivated.'))
|
messages.success(request, _('The selected product has been deactivated.'))
|
||||||
return HttpResponseRedirect(success_url)
|
return HttpResponseRedirect(success_url)
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ from pretix.base.models import (
|
|||||||
)
|
)
|
||||||
from pretix.base.services import tickets
|
from pretix.base.services import tickets
|
||||||
from pretix.base.services.export import export
|
from pretix.base.services.export import export
|
||||||
from pretix.base.services.orders import mark_order_paid
|
from pretix.base.services.orders import cancel_order, mark_order_paid
|
||||||
from pretix.base.services.stats import order_overview
|
from pretix.base.services.stats import order_overview
|
||||||
from pretix.base.signals import (
|
from pretix.base.signals import (
|
||||||
register_data_exporters, register_payment_providers,
|
register_data_exporters, register_payment_providers,
|
||||||
@@ -136,8 +136,8 @@ class OrderDetail(OrderView):
|
|||||||
def keyfunc(pos):
|
def keyfunc(pos):
|
||||||
if (pos.item.admission and self.request.event.settings.attendee_names_asked) \
|
if (pos.item.admission and self.request.event.settings.attendee_names_asked) \
|
||||||
or pos.item.questions.all():
|
or pos.item.questions.all():
|
||||||
return pos.id, "", "", ""
|
return pos.id, 0, 0, 0
|
||||||
return "", pos.item_id, pos.variation_id, pos.price
|
return 0, pos.item_id, pos.variation_id, pos.price
|
||||||
|
|
||||||
positions = []
|
positions = []
|
||||||
for k, g in groupby(sorted(list(cartpos), key=keyfunc), key=keyfunc):
|
for k, g in groupby(sorted(list(cartpos), key=keyfunc), key=keyfunc):
|
||||||
@@ -164,19 +164,19 @@ class OrderTransition(OrderView):
|
|||||||
to = self.request.POST.get('status', '')
|
to = self.request.POST.get('status', '')
|
||||||
if self.order.status == 'n' and to == 'p':
|
if self.order.status == 'n' and to == 'p':
|
||||||
try:
|
try:
|
||||||
mark_order_paid(self.order, manual=True)
|
mark_order_paid(self.order, manual=True, user=self.request.user)
|
||||||
except Quota.QuotaExceededException as e:
|
except Quota.QuotaExceededException as e:
|
||||||
messages.error(self.request, str(e))
|
messages.error(self.request, str(e))
|
||||||
else:
|
else:
|
||||||
messages.success(self.request, _('The order has been marked as paid.'))
|
messages.success(self.request, _('The order has been marked as paid.'))
|
||||||
elif self.order.status == 'n' and to == 'c':
|
elif self.order.status == 'n' and to == 'c':
|
||||||
self.order.status = Order.STATUS_CANCELLED
|
cancel_order(self.order, user=self.request.user)
|
||||||
self.order.save()
|
|
||||||
messages.success(self.request, _('The order has been cancelled.'))
|
messages.success(self.request, _('The order has been cancelled.'))
|
||||||
elif self.order.status == 'p' and to == 'n':
|
elif self.order.status == 'p' and to == 'n':
|
||||||
self.order.status = Order.STATUS_PENDING
|
self.order.status = Order.STATUS_PENDING
|
||||||
self.order.payment_manual = True
|
self.order.payment_manual = True
|
||||||
self.order.save()
|
self.order.save()
|
||||||
|
self.order.log_action('pretix.base.order.unpaid', user=self.request.user)
|
||||||
messages.success(self.request, _('The order has been marked as not paid.'))
|
messages.success(self.request, _('The order has been marked as not paid.'))
|
||||||
elif self.order.status == 'p' and to == 'r':
|
elif self.order.status == 'p' and to == 'r':
|
||||||
ret = self.payment_provider.order_control_refund_perform(self.request, self.order)
|
ret = self.payment_provider.order_control_refund_perform(self.request, self.order)
|
||||||
@@ -247,6 +247,9 @@ class OrderExtend(OrderView):
|
|||||||
if self.form.is_valid():
|
if self.form.is_valid():
|
||||||
if oldvalue > now():
|
if oldvalue > now():
|
||||||
messages.success(self.request, _('The payment term has been changed.'))
|
messages.success(self.request, _('The payment term has been changed.'))
|
||||||
|
self.order.log_action('pretix.order.changed', user=self.request.user, data={
|
||||||
|
'expires': self.order.expires
|
||||||
|
})
|
||||||
self.form.save()
|
self.form.save()
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
@@ -254,6 +257,9 @@ class OrderExtend(OrderView):
|
|||||||
is_available = self.order._is_still_available()
|
is_available = self.order._is_still_available()
|
||||||
if is_available is True:
|
if is_available is True:
|
||||||
self.form.save()
|
self.form.save()
|
||||||
|
self.order.log_action('pretix.order.changed', user=self.request.user, data={
|
||||||
|
'expires': self.order.expires
|
||||||
|
})
|
||||||
messages.success(self.request, _('The payment term has been changed.'))
|
messages.success(self.request, _('The payment term has been changed.'))
|
||||||
else:
|
else:
|
||||||
messages.error(self.request, is_available)
|
messages.error(self.request, is_available)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from django.utils.translation import ugettext as __, ugettext_lazy as _
|
|||||||
|
|
||||||
from pretix.base.models import Quota
|
from pretix.base.models import Quota
|
||||||
from pretix.base.payment import BasePaymentProvider
|
from pretix.base.payment import BasePaymentProvider
|
||||||
from pretix.base.services.orders import mark_order_paid
|
from pretix.base.services.orders import mark_order_paid, mark_order_refunded
|
||||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||||
|
|
||||||
logger = logging.getLogger('pretix.plugins.paypal')
|
logger = logging.getLogger('pretix.plugins.paypal')
|
||||||
@@ -218,7 +218,7 @@ class Paypal(BasePaymentProvider):
|
|||||||
payment_info = None
|
payment_info = None
|
||||||
|
|
||||||
if not payment_info:
|
if not payment_info:
|
||||||
order.mark_refunded()
|
mark_order_refunded(order)
|
||||||
messages.warning(request, _('We were unable to transfer the money back automatically. '
|
messages.warning(request, _('We were unable to transfer the money back automatically. '
|
||||||
'Please get in touch with the customer and transfer it back manually.'))
|
'Please get in touch with the customer and transfer it back manually.'))
|
||||||
return
|
return
|
||||||
@@ -231,11 +231,11 @@ class Paypal(BasePaymentProvider):
|
|||||||
|
|
||||||
refund = sale.refund({})
|
refund = sale.refund({})
|
||||||
if not refund.success():
|
if not refund.success():
|
||||||
order.mark_refunded()
|
mark_order_refunded(order, user=request.user)
|
||||||
messages.warning(request, _('We were unable to transfer the money back automatically. '
|
messages.warning(request, _('We were unable to transfer the money back automatically. '
|
||||||
'Please get in touch with the customer and transfer it back manually.'))
|
'Please get in touch with the customer and transfer it back manually.'))
|
||||||
else:
|
else:
|
||||||
sale = paypalrestsdk.Payment.find(payment_info['id'])
|
sale = paypalrestsdk.Payment.find(payment_info['id'])
|
||||||
order = order.mark_refunded()
|
order = mark_order_refunded(order)
|
||||||
order.payment_info = json.dumps(sale.to_dict())
|
order.payment_info = json.dumps(sale.to_dict())
|
||||||
order.save()
|
order.save()
|
||||||
|
|||||||
@@ -27,12 +27,15 @@ class SenderView(EventPermissionRequiredMixin, FormView):
|
|||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
orders = Order.objects.filter(
|
orders = Order.objects.filter(
|
||||||
event=self.request.event, status__in=form.cleaned_data['sendto']
|
event=self.request.event, status__in=form.cleaned_data['sendto']
|
||||||
).select_related("user")
|
)
|
||||||
users = set([o.user for o in orders])
|
mails = set([o.email for o in orders])
|
||||||
|
|
||||||
for u in users:
|
self.request.event.log_action('pretix.plugins.sendmail.sent', user=self.request.user, data=dict(
|
||||||
mail(u.email, form.cleaned_data['subject'], form.cleaned_data['message'],
|
form.cleaned_data))
|
||||||
None, self.request.event, locale=u.locale)
|
|
||||||
|
for m in mails:
|
||||||
|
mail(m, form.cleaned_data['subject'], form.cleaned_data['message'],
|
||||||
|
None, self.request.event, locale=m.locale)
|
||||||
|
|
||||||
messages.success(self.request, _('Your message will be sent to the selected users.'))
|
messages.success(self.request, _('Your message will be sent to the selected users.'))
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
|
|
||||||
from pretix.base.models import Quota
|
from pretix.base.models import Quota
|
||||||
from pretix.base.payment import BasePaymentProvider
|
from pretix.base.payment import BasePaymentProvider
|
||||||
from pretix.base.services.orders import mark_order_paid
|
from pretix.base.services.orders import mark_order_paid, mark_order_refunded
|
||||||
from pretix.helpers.urls import build_absolute_uri
|
from pretix.helpers.urls import build_absolute_uri
|
||||||
|
|
||||||
logger = logging.getLogger('pretix.plugins.stripe')
|
logger = logging.getLogger('pretix.plugins.stripe')
|
||||||
@@ -157,7 +157,7 @@ class Stripe(BasePaymentProvider):
|
|||||||
payment_info = None
|
payment_info = None
|
||||||
|
|
||||||
if not payment_info:
|
if not payment_info:
|
||||||
order.mark_refunded()
|
mark_order_refunded(order, user=request.user)
|
||||||
messages.warning(request, _('We were unable to transfer the money back automatically. '
|
messages.warning(request, _('We were unable to transfer the money back automatically. '
|
||||||
'Please get in touch with the customer and transfer it back manually.'))
|
'Please get in touch with the customer and transfer it back manually.'))
|
||||||
return
|
return
|
||||||
@@ -173,10 +173,10 @@ class Stripe(BasePaymentProvider):
|
|||||||
'support if the problem persists.'))
|
'support if the problem persists.'))
|
||||||
logger.error('Stripe error: %s' % str(err))
|
logger.error('Stripe error: %s' % str(err))
|
||||||
except stripe.error.StripeError:
|
except stripe.error.StripeError:
|
||||||
order.mark_refunded()
|
mark_order_refunded(order)
|
||||||
messages.warning(request, _('We were unable to transfer the money back automatically. '
|
messages.warning(request, _('We were unable to transfer the money back automatically. '
|
||||||
'Please get in touch with the customer and transfer it back manually.'))
|
'Please get in touch with the customer and transfer it back manually.'))
|
||||||
else:
|
else:
|
||||||
order = order.mark_refunded()
|
order = mark_order_refunded(order)
|
||||||
order.payment_info = str(ch)
|
order.payment_info = str(ch)
|
||||||
order.save()
|
order.save()
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from django.views.decorators.csrf import csrf_exempt
|
|||||||
from django.views.decorators.http import require_POST
|
from django.views.decorators.http import require_POST
|
||||||
|
|
||||||
from pretix.base.models import Event, Order
|
from pretix.base.models import Event, Order
|
||||||
|
from pretix.base.services.orders import mark_order_refunded
|
||||||
from pretix.plugins.stripe.payment import Stripe
|
from pretix.plugins.stripe.payment import Stripe
|
||||||
|
|
||||||
logger = logging.getLogger('pretix.plugins.stripe')
|
logger = logging.getLogger('pretix.plugins.stripe')
|
||||||
@@ -42,6 +43,8 @@ def webhook(request):
|
|||||||
prov = Stripe(event)
|
prov = Stripe(event)
|
||||||
prov._init_api()
|
prov._init_api()
|
||||||
|
|
||||||
|
order.log_action('pretix.plugins.stripe.event', data=event_json)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
charge = stripe.Charge.retrieve(charge['id'])
|
charge = stripe.Charge.retrieve(charge['id'])
|
||||||
except stripe.error.StripeError as err:
|
except stripe.error.StripeError as err:
|
||||||
@@ -49,6 +52,6 @@ def webhook(request):
|
|||||||
return HttpResponse('StripeError', status=500)
|
return HttpResponse('StripeError', status=500)
|
||||||
|
|
||||||
if charge['refunds']['total_count'] > 0 and order.status == Order.STATUS_PAID:
|
if charge['refunds']['total_count'] > 0 and order.status == Order.STATUS_PAID:
|
||||||
order.mark_refunded()
|
mark_order_refunded(order)
|
||||||
|
|
||||||
return HttpResponse(status=200)
|
return HttpResponse(status=200)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from django.views.generic import TemplateView, View
|
from django.views.generic import TemplateView, View
|
||||||
|
|
||||||
from pretix.base.models import CachedFile, CachedTicket, Order, OrderPosition
|
from pretix.base.models import CachedFile, CachedTicket, Order, OrderPosition
|
||||||
|
from pretix.base.services.orders import cancel_order
|
||||||
from pretix.base.services.tickets import generate
|
from pretix.base.services.tickets import generate
|
||||||
from pretix.base.signals import (
|
from pretix.base.signals import (
|
||||||
register_payment_providers, register_ticket_outputs,
|
register_payment_providers, register_ticket_outputs,
|
||||||
@@ -215,6 +216,7 @@ class OrderModify(EventViewMixin, OrderDetailMixin, QuestionsViewMixin, Template
|
|||||||
messages.error(self.request,
|
messages.error(self.request,
|
||||||
_("We had difficulties processing your input. Please review the errors below."))
|
_("We had difficulties processing your input. Please review the errors below."))
|
||||||
return self.get(request, *args, **kwargs)
|
return self.get(request, *args, **kwargs)
|
||||||
|
self.order.log_action('pretix.event.order.modified')
|
||||||
return redirect(self.get_order_url())
|
return redirect(self.get_order_url())
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
@@ -251,8 +253,7 @@ class OrderCancel(EventViewMixin, OrderDetailMixin, TemplateView):
|
|||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
self.order.status = Order.STATUS_CANCELLED
|
cancel_order(self.order)
|
||||||
self.order.save()
|
|
||||||
return redirect(self.get_order_url())
|
return redirect(self.get_order_url())
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ from pretix.base.settings import SettingsSandbox
|
|||||||
|
|
||||||
|
|
||||||
class SettingsTestCase(TestCase):
|
class SettingsTestCase(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
settings.DEFAULTS['test_default'] = {
|
settings.DEFAULTS['test_default'] = {
|
||||||
'default': 'def',
|
'default': 'def',
|
||||||
@@ -149,6 +148,7 @@ class SettingsTestCase(TestCase):
|
|||||||
def test_serialize_unknown(self):
|
def test_serialize_unknown(self):
|
||||||
class Type:
|
class Type:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._test_serialization(Type(), Type)
|
self._test_serialization(Type(), Type)
|
||||||
self.assertTrue(False, 'No exception thrown!')
|
self.assertTrue(False, 'No exception thrown!')
|
||||||
@@ -195,3 +195,23 @@ class SettingsTestCase(TestCase):
|
|||||||
|
|
||||||
self.assertIsNone(sandbox.bar)
|
self.assertIsNone(sandbox.bar)
|
||||||
self.assertIsNone(sandbox['baz'])
|
self.assertIsNone(sandbox['baz'])
|
||||||
|
|
||||||
|
def test_freeze(self):
|
||||||
|
olddef = settings.DEFAULTS
|
||||||
|
settings.DEFAULTS = {
|
||||||
|
'test_default': {
|
||||||
|
'default': 'def',
|
||||||
|
'type': str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.event.organizer.settings.set('bar', 'baz')
|
||||||
|
self.event.organizer.settings.set('foo', 'baz')
|
||||||
|
self.event.settings.set('foo', 'bar')
|
||||||
|
try:
|
||||||
|
self.assertEqual(self.event.settings.freeze(), {
|
||||||
|
'test_default': 'def',
|
||||||
|
'bar': 'baz',
|
||||||
|
'foo': 'bar'
|
||||||
|
})
|
||||||
|
finally:
|
||||||
|
settings.DEFAULTS = olddef
|
||||||
|
|||||||
Reference in New Issue
Block a user