diff --git a/src/pretix/base/admin.py b/src/pretix/base/admin.py deleted file mode 100644 index 9650075933..0000000000 --- a/src/pretix/base/admin.py +++ /dev/null @@ -1,134 +0,0 @@ -from django.contrib import admin -from django.contrib.auth.admin import UserAdmin -from django.utils.translation import ugettext as _ -from django import forms - -from pretix.base.models import ( - User, Organizer, OrganizerPermission, Event, EventPermission, - Property, PropertyValue, Item, ItemVariation, ItemCategory -) - - -class PretixUserCreationForm(forms.ModelForm): - - """ - A form that creates a user, with no privileges, from the given username and - password. - """ - error_messages = { - 'password_mismatch': _("The two password fields didn't match."), - } - password1 = forms.CharField(label=_("Password"), - widget=forms.PasswordInput) - password2 = forms.CharField(label=_("Password confirmation"), - widget=forms.PasswordInput, - help_text=_("Enter the same password as above, for verification.")) - - class Meta: - model = User - fields = ("email", "username", "event") - - def clean_password2(self): - password1 = self.cleaned_data.get("password1") - password2 = self.cleaned_data.get("password2") - if password1 and password2 and password1 != password2: - raise forms.ValidationError( - self.error_messages['password_mismatch'], - code='password_mismatch', - ) - return password2 - - def save(self, commit=True): - user = super(PretixUserCreationForm, self).save(commit=False) - user.set_password(self.cleaned_data["password1"]) - if commit: - user.save() - return user - - -class PretixUserAdmin(UserAdmin): - - fieldsets = ( - (None, {'fields': ('identifier', 'event', 'username', 'password')}), - (_('Personal info'), {'fields': ('familyname', 'givenname', 'email')}), - (_('Locale'), {'fields': ('locale', 'timezone')}), - (_('Permissions'), {'fields': ('is_active', 'is_staff', - 'groups', 'user_permissions')}), - ) - list_display = ('identifier', 'event', 'username', 'email', 'givenname', 'familyname', 'is_staff') - search_fields = ('identifier', 'username', 'givenname', 'familyname', 'email') - ordering = ('identifier',) - list_filter = ('is_staff', 'is_active', 'groups') - add_form = PretixUserCreationForm - - -class OrganizerPermissionInline(admin.TabularInline): - - model = OrganizerPermission - extra = 2 - - -class OrganizerAdmin(admin.ModelAdmin): - - model = Organizer - inlines = [OrganizerPermissionInline] - list_display = ('name', 'slug') - search_fields = ('name', 'slug') - - -class EventPermissionInline(admin.TabularInline): - - model = EventPermission - extra = 2 - - -class EventAdmin(admin.ModelAdmin): - - model = Event - inlines = [EventPermissionInline] - list_display = ('name', 'slug', 'organizer', 'date_from') - search_fields = ('name', 'slug') - list_filter = ('date_from', 'currency') - - -class PropertyValueInline(admin.StackedInline): - - model = PropertyValue - extra = 4 - - -class PropertyAdmin(admin.ModelAdmin): - - model = Property - inlines = [PropertyValueInline] - list_display = ('name', 'event') - search_fields = ('name', 'event') - - -class ItemCategoryAdmin(admin.ModelAdmin): - - model = ItemCategory - list_display = ('name', 'event') - search_fields = ('name', 'event') - - -class ItemVariationInline(admin.TabularInline): - - model = ItemVariation - extra = 4 - - -class ItemAdmin(admin.ModelAdmin): - - model = Item - inlines = [ItemVariationInline] - list_display = ('name', 'event', 'category') - search_fields = ('name', 'event', 'category', 'short_description') - - -admin.site.register(User, PretixUserAdmin) -admin.site.register(Organizer, OrganizerAdmin) -admin.site.register(Event, EventAdmin) -admin.site.register(Property, PropertyAdmin) -admin.site.register(Item, ItemAdmin) -admin.site.register(ItemCategory, ItemCategoryAdmin) diff --git a/src/pretix/base/models.py b/src/pretix/base/models.py index 59f8f0bbe0..a4f2784a58 100644 --- a/src/pretix/base/models.py +++ b/src/pretix/base/models.py @@ -5,7 +5,6 @@ import uuid import random import time -from django.core.urlresolvers import reverse from django.db import models from django.conf import settings from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin @@ -1660,58 +1659,6 @@ class Order(Versionable): quota.release() return True, quotas_locked - def mark_paid(self, provider=None, info=None, date=None, manual=None, force=False): - """ - Mark this order as paid. This clones the order object, sets the payment provider, - info and date and returns the cloned order object. - - :param provider: The payment provider that marked this as paid - :type provider: str - :param info: The information to store in order.payment_info - :type info: str - :param date: The date the payment was received (if you pass ``None``, the current - time will be used). - :type date: datetime - :param force: Whether this payment should be marked as paid even if no remaining - quota is available (default: ``False``). - :type force: boolean - :raises Quota.QuotaExceededException: if the quota is exceeded and ``force`` is ``False`` - """ - can_be_paid, quotas_locked = self._can_be_paid(keep_locked=True) - if not force and can_be_paid is not True: - raise Quota.QuotaExceededException(can_be_paid) - order = self.clone() - order.payment_provider = provider or order.payment_provider - order.payment_info = info or order.payment_info - order.payment_date = date or now() - if manual is not None: - order.payment_manual = manual - order.status = Order.STATUS_PAID - order.save() - - if quotas_locked: - for quota in quotas_locked: - quota.release() - - from pretix.base.mail import mail - mail( - order.user, _('Payment received for your order: %(code)s') % {'code': order.code}, - 'pretixpresale/email/order_paid.txt', - { - 'user': order.user, - 'order': order, - 'event': order.event, - 'url': settings.SITE_URL + reverse('presale:event.order', kwargs={ - 'event': order.event.slug, - 'organizer': order.event.organizer.slug, - 'order': order.code - }), - 'downloads': order.event.settings.get('ticket_download', as_type=bool) - }, - order.event - ) - return order - class QuestionAnswer(Versionable): """ diff --git a/src/pretix/base/payment.py b/src/pretix/base/payment.py index 40e29f9c40..aa3ff5dbb6 100644 --- a/src/pretix/base/payment.py +++ b/src/pretix/base/payment.py @@ -244,11 +244,11 @@ class BasePaymentProvider: containing an URL the user will be redirected to. If you are done with your process you should return the user to the order's detail page. - If the payment is completed, you should call ``order.mark_paid(provider, info)`` + If the payment is completed, you should call ``pretix.bsae.services.orders.mark_order_paid(order, provider, info)`` with ``provider`` being your :py:attr:`identifier` and ``info`` being any string you might want to store for later usage. Please note, that if you want to store - something inside ``order.payment_info``, please do it after the ``mark_paid`` call, - as this call does a object clone for you. Please also note that ``mark_paid`` might + something inside ``order.payment_info``, please do it after the ``mark_order_paid`` call, + as this call does a object clone for you. Please also note that ``mark_order_paid`` might raise a ``Quota.QuotaExceededException`` if (and only if) the payment term of this order is over and some of the items are sold out. You should use the exception message to display a meaningful error to the user. diff --git a/src/pretix/base/services/orders.py b/src/pretix/base/services/orders.py new file mode 100644 index 0000000000..cbcc93f68c --- /dev/null +++ b/src/pretix/base/services/orders.py @@ -0,0 +1,58 @@ +from django.conf import settings +from django.core.urlresolvers import reverse +from django.utils.timezone import now +from pretix.base.models import Order, Quota +from django.utils.translation import ugettext_lazy as _ + + +def mark_order_paid(order, provider=None, info=None, date=None, manual=None, force=False): + """ + Marks an order as paid. This clones the order object, sets the payment provider, + info and date and returns the cloned order object. + + :param provider: The payment provider that marked this as paid + :type provider: str + :param info: The information to store in order.payment_info + :type info: str + :param date: The date the payment was received (if you pass ``None``, the current + time will be used). + :type date: datetime + :param force: Whether this payment should be marked as paid even if no remaining + quota is available (default: ``False``). + :type force: boolean + :raises Quota.QuotaExceededException: if the quota is exceeded and ``force`` is ``False`` + """ + can_be_paid, quotas_locked = order._can_be_paid(keep_locked=True) + if not force and can_be_paid is not True: + raise Quota.QuotaExceededException(can_be_paid) + order = order.clone() + order.payment_provider = provider or order.payment_provider + order.payment_info = info or order.payment_info + order.payment_date = date or now() + if manual is not None: + order.payment_manual = manual + order.status = Order.STATUS_PAID + order.save() + + if quotas_locked: + for quota in quotas_locked: + quota.release() + + from pretix.base.mail import mail + mail( + order.user, _('Payment received for your order: %(code)s') % {'code': order.code}, + 'pretixpresale/email/order_paid.txt', + { + 'user': order.user, + 'order': order, + 'event': order.event, + 'url': settings.SITE_URL + reverse('presale:event.order', kwargs={ + 'event': order.event.slug, + 'organizer': order.event.organizer.slug, + 'order': order.code + }), + 'downloads': order.event.settings.get('ticket_download', as_type=bool) + }, + order.event + ) + return order diff --git a/src/pretix/control/views/orders.py b/src/pretix/control/views/orders.py index 7b756fcee3..2eae453c98 100644 --- a/src/pretix/control/views/orders.py +++ b/src/pretix/control/views/orders.py @@ -9,6 +9,7 @@ from django.shortcuts import redirect, render from django.utils.functional import cached_property from django.views.generic import ListView, DetailView, TemplateView from pretix.base.models import Order, Quota, OrderPosition +from pretix.base.services.orders import mark_order_paid from pretix.base.signals import register_payment_providers from pretix.control.forms.orders import ExtendForm from pretix.control.permissions import EventPermissionRequiredMixin @@ -107,7 +108,7 @@ class OrderTransition(OrderView): to = self.request.POST.get('status', '') if self.order.status == 'n' and to == 'p': try: - self.order.mark_paid(manual=True) + mark_order_paid(self.order, manual=True) except Quota.QuotaExceededException as e: messages.error(self.request, str(e)) else: diff --git a/src/pretix/plugins/banktransfer/views.py b/src/pretix/plugins/banktransfer/views.py index 56da4da64e..e5b3f892da 100644 --- a/src/pretix/plugins/banktransfer/views.py +++ b/src/pretix/plugins/banktransfer/views.py @@ -9,6 +9,7 @@ from django.shortcuts import redirect, render from django.utils.timezone import now from django.views.generic import TemplateView from pretix.base.models import Order, Quota +from pretix.base.services.orders import mark_order_paid from pretix.control.permissions import EventPermissionRequiredMixin from pretix.plugins.banktransfer import csvimport, mt940import from django.utils.translation import ugettext_lazy as _ @@ -36,7 +37,7 @@ class ImportView(EventPermissionRequiredMixin, TemplateView): some_failed = False for order in orders: try: - order.mark_paid(provider='banktransfer', info=json.dumps({ + mark_order_paid(order, provider='banktransfer', info=json.dumps({ 'reference': self.request.POST.get('reference_%s' % order.code), 'date': self.request.POST.get('date_%s' % order.code), 'payer': self.request.POST.get('payer_%s' % order.code), diff --git a/src/pretix/plugins/paypal/payment.py b/src/pretix/plugins/paypal/payment.py index 5673941aae..33d4d4d1a1 100644 --- a/src/pretix/plugins/paypal/payment.py +++ b/src/pretix/plugins/paypal/payment.py @@ -1,16 +1,16 @@ from collections import OrderedDict import json import logging + from django.contrib import messages from django.core.urlresolvers import reverse from django.template.loader import get_template from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as __ from django import forms - import paypalrestsdk from pretix.base.models import Quota - +from pretix.base.services.orders import mark_order_paid from pretix.base.payment import BasePaymentProvider @@ -177,7 +177,7 @@ class Paypal(BasePaymentProvider): return try: - order.mark_paid('paypal', json.dumps(payment.to_dict())) + mark_order_paid(order, 'paypal', json.dumps(payment.to_dict())) messages.success(request, _('We successfully received your payment. Thank you!')) except Quota.QuotaExceededException as e: messages.error(request, str(e)) diff --git a/src/pretix/plugins/stripe/payment.py b/src/pretix/plugins/stripe/payment.py index 37476fb9cd..eadcfbf45d 100644 --- a/src/pretix/plugins/stripe/payment.py +++ b/src/pretix/plugins/stripe/payment.py @@ -1,16 +1,18 @@ from collections import OrderedDict import json import logging + from django.contrib import messages from django.core.urlresolvers import reverse from django.template.loader import get_template from django.utils.translation import ugettext_lazy as _ from django import forms from pretix.base.models import Quota +from pretix.base.services.orders import mark_order_paid import stripe - from pretix.base.payment import BasePaymentProvider + logger = logging.getLogger('pretix.plugins.stripe') @@ -99,7 +101,7 @@ class Stripe(BasePaymentProvider): else: if charge.status == 'succeeded' and charge.paid: try: - order.mark_paid('paypal', str(charge)) + mark_order_paid(order, 'paypal', str(charge)) messages.success(request, _('We successfully received your payment. Thank you!')) except Quota.QuotaExceededException as e: messages.error(request, str(e)) diff --git a/src/tests/base/test_models.py b/src/tests/base/test_models.py index d348b7eaa0..8c985344cd 100644 --- a/src/tests/base/test_models.py +++ b/src/tests/base/test_models.py @@ -1,11 +1,12 @@ from datetime import timedelta + from django.test import TestCase from django.utils.timezone import now - from pretix.base.models import ( Event, Organizer, Item, ItemVariation, Property, PropertyValue, User, Quota, Order, OrderPosition, CartPosition) +from pretix.base.services.orders import mark_order_paid from pretix.base.types import VariationDict @@ -314,14 +315,14 @@ class OrderTestCase(BaseQuotaTestCase): def test_paid_in_time(self): self.quota.size = 0 self.quota.save() - self.order.mark_paid() + mark_order_paid(self.order) self.order = Order.objects.current.get(identity=self.order.identity) self.assertEqual(self.order.status, Order.STATUS_PAID) def test_paid_expired_available(self): self.order.expires = now() - timedelta(days=2) self.order.save() - self.order.mark_paid() + mark_order_paid(self.order) self.order = Order.objects.current.get(identity=self.order.identity) self.assertEqual(self.order.status, Order.STATUS_PAID) @@ -331,7 +332,7 @@ class OrderTestCase(BaseQuotaTestCase): self.quota.size = 1 self.quota.save() try: - self.order.mark_paid() + mark_order_paid(self.order) self.assertFalse(True, 'This should have raised an exception.') except Quota.QuotaExceededException: pass @@ -344,7 +345,7 @@ class OrderTestCase(BaseQuotaTestCase): self.quota.size = 0 self.quota.save() try: - self.order.mark_paid() + mark_order_paid(self.order) self.assertFalse(True, 'This should have raised an exception.') except Quota.QuotaExceededException: pass @@ -356,6 +357,6 @@ class OrderTestCase(BaseQuotaTestCase): self.order.save() self.quota.size = 0 self.quota.save() - self.order.mark_paid(force=True) + mark_order_paid(self.order, force=True) self.order = Order.objects.current.get(identity=self.order.identity) self.assertEqual(self.order.status, Order.STATUS_PAID)