diff --git a/src/pretix/base/migrations/0036_auto_20160902_0755.py b/src/pretix/base/migrations/0036_auto_20160902_0755.py new file mode 100644 index 0000000000..a611e287c6 --- /dev/null +++ b/src/pretix/base/migrations/0036_auto_20160902_0755.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2016-09-02 07:55 +from __future__ import unicode_literals + +from django.db import migrations, models + + +def preserve_event_settings(apps, schema_editor): + Event = apps.get_model('pretixbase', 'Event') + EventSetting = apps.get_model('pretixbase', 'EventSetting') + for e in Event.objects.all(): + EventSetting.objects.create(object=e, key='mail_days_order_expire_warning', value='0') + + +class Migration(migrations.Migration): + + dependencies = [ + ('pretixbase', '0035_merge'), + ] + + operations = [ + migrations.AddField( + model_name='order', + name='expiry_reminder_sent', + field=models.BooleanField(default=False), + ), + migrations.AlterField( + model_name='item', + name='allow_cancel', + field=models.BooleanField(default=True, help_text='If you deactivate this, an order including this product might not be cancelled by the user. It may still be cancelled by you.', verbose_name='Allow product to be cancelled'), + ), + migrations.RunPython(preserve_event_settings) + ] diff --git a/src/pretix/base/models/orders.py b/src/pretix/base/models/orders.py index c553129cc7..d6ed863276 100644 --- a/src/pretix/base/models/orders.py +++ b/src/pretix/base/models/orders.py @@ -160,6 +160,9 @@ class Order(LoggedModel): help_text=_("The text entered in this field will not be visible to the user and is available for your " "convenience.") ) + expiry_reminder_sent = models.BooleanField( + default=False + ) class Meta: verbose_name = _("Order") diff --git a/src/pretix/base/services/orders.py b/src/pretix/base/services/orders.py index 2c12bef97c..a1ec0d91fa 100644 --- a/src/pretix/base/services/orders.py +++ b/src/pretix/base/services/orders.py @@ -1,3 +1,4 @@ +import logging from collections import Counter, namedtuple from datetime import datetime, timedelta from decimal import Decimal @@ -6,6 +7,7 @@ from typing import List, Optional from django.conf import settings from django.db import transaction from django.dispatch import receiver +from django.utils.formats import date_format from django.utils.timezone import now from django.utils.translation import ugettext as _ @@ -21,7 +23,7 @@ from pretix.base.payment import BasePaymentProvider from pretix.base.services.invoices import ( generate_cancellation, generate_invoice, invoice_qualified, ) -from pretix.base.services.mail import mail +from pretix.base.services.mail import SendMailException, mail from pretix.base.signals import ( order_paid, order_placed, periodic_task, register_payment_providers, ) @@ -48,6 +50,8 @@ error_messages = { 'voucher_required': _('You need a valid voucher code to order one of the products in your cart.'), } +logger = logging.getLogger(__name__) + def mark_order_paid(order: Order, provider: str=None, info: str=None, date: datetime=None, manual: bool=None, force: bool=False, send_mail: bool=True, user: User=None) -> Order: @@ -368,6 +372,41 @@ def expire_orders(sender, **kwargs): o.save() +@receiver(signal=periodic_task) +def send_expiry_warnings(sender, **kwargs): + eventcache = {} + today = now().date() + + for o in Order.objects.filter(expires__gte=today, expiry_reminder_sent=False, status=Order.STATUS_PENDING).select_related('event'): + settings = eventcache.get(o.event.pk, None) + if settings is None: + settings = o.event.settings + eventcache[o.event.pk] = settings + + days = settings.get('mail_days_order_expire_warning', as_type=int) + if days and (o.expires.date() - today).days <= days: + o.expiry_reminder_sent = True + o.save() + try: + mail( + o.email, _('Your is about to expire: %(code)s') % {'code': o.code}, + settings.mail_text_order_expire_warning, + { + 'event': o.event.name, + 'url': build_absolute_uri(o.event, 'presale:event.order', kwargs={ + 'order': o.code, + 'secret': o.secret + }), + 'expire_date': date_format(o.expires, 'SHORT_DATE_FORMAT') + }, + o.event, locale=o.locale + ) + except SendMailException: + logger.exception('Reminder email could not be sent') + else: + o.log_action('pretix.event.order.expire_warning_sent') + + class OrderChangeManager: error_messages = { 'free_to_paid': _('You cannot change a free order to a paid order.'), diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py index 288c5487a8..86e9f58eac 100644 --- a/src/pretix/base/settings.py +++ b/src/pretix/base/settings.py @@ -227,6 +227,24 @@ we successfully received your payment for {event}. Thank you! You can change your order details and view the status of your order at {url} +Best regards, +Your {event} team""")) + }, + 'mail_days_order_expire_warning': { + 'type': int, + 'default': '3' + }, + 'mail_text_order_expire_warning': { + 'type': LazyI18nString, + 'default': LazyI18nString.from_gettext(ugettext_noop("""Hello, + +we did not yet receive a payment for your order for {event}. +Please keep in mind that if we only guarantee your order if we receive +your payment before {expire_date}. + +You can view the payment information and the status of your order at +{url} + Best regards, Your {event} team""")) }, diff --git a/src/pretix/control/forms/event.py b/src/pretix/control/forms/event.py index c7822e01db..4dea0a7fa4 100644 --- a/src/pretix/control/forms/event.py +++ b/src/pretix/control/forms/event.py @@ -312,35 +312,54 @@ class MailSettingsForm(SettingsForm): help_text=_("Sender address for outgoing emails") ) mail_text_order_placed = I18nFormField( - label=_("Placed order"), + label=_("Text"), required=False, widget=I18nTextarea, help_text=_("Available placeholders: {event}, {total}, {currency}, {date}, {paymentinfo}, {url}") ) mail_text_order_paid = I18nFormField( - label=_("Paid order"), + label=_("Text"), required=False, widget=I18nTextarea, help_text=_("Available placeholders: {event}, {url}") ) mail_text_order_free = I18nFormField( - label=_("Free order"), + label=_("Text"), required=False, widget=I18nTextarea, help_text=_("Available placeholders: {event}, {url}") ) mail_text_order_changed = I18nFormField( - label=_("Changed order"), + label=_("Text"), required=False, widget=I18nTextarea, help_text=_("Available placeholders: {event}, {url}") ) mail_text_resend_link = I18nFormField( - label=_("Resend link"), + label=_("Text (sent by admin)"), required=False, widget=I18nTextarea, help_text=_("Available placeholders: {event}, {url}") ) + mail_text_resend_all_links = I18nFormField( + label=_("Text (requested by user)"), + required=False, + widget=I18nTextarea, + help_text=_("Available placeholders: {event}, {orders}") + ) + mail_days_order_expire_warning = forms.IntegerField( + label=_("Number of days"), + required=False, + min_value=0, + help_text=_("This email will be sent out this many days before the order expires. If the " + "value is 0, the mail will never be sent.") + ) + mail_text_order_expire_warning = I18nFormField( + label=_("Text"), + required=False, + widget=I18nTextarea, + help_text=_("Available placeholders: {event}, {url}, {expire_date}") + ) smtp_use_custom = forms.BooleanField( label=_("Use custom SMTP server"), help_text=_("All mail related to your event will be sent over the smtp server specified by you."), diff --git a/src/pretix/control/logdisplay.py b/src/pretix/control/logdisplay.py index ad54d03ce0..0a1bb5165c 100644 --- a/src/pretix/control/logdisplay.py +++ b/src/pretix/control/logdisplay.py @@ -61,6 +61,7 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs): 'pretix.event.order.comment': _('The order\'s internal comment has been updated.'), 'pretix.event.order.contact.changed': _('The email address has been changed.'), 'pretix.event.order.payment.changed': _('The payment method has been changed.'), + 'pretix.event.order.expire_warning_sent': _('An email has been sent with a warning that the order is about to expire.'), } if logentry.action_type in plains: return plains[logentry.action_type] diff --git a/src/pretix/control/templates/pretixcontrol/event/mail.html b/src/pretix/control/templates/pretixcontrol/event/mail.html index 594b8edefb..d435c4f648 100644 --- a/src/pretix/control/templates/pretixcontrol/event/mail.html +++ b/src/pretix/control/templates/pretixcontrol/event/mail.html @@ -12,11 +12,94 @@
{% trans "E-mail content" %} - {% bootstrap_field form.mail_text_order_placed layout="horizontal" %} - {% bootstrap_field form.mail_text_order_paid layout="horizontal" %} - {% bootstrap_field form.mail_text_order_free layout="horizontal" %} - {% bootstrap_field form.mail_text_resend_link layout="horizontal" %} - {% bootstrap_field form.mail_text_order_changed layout="horizontal" %} +
+
+ +
+
+ {% bootstrap_field form.mail_text_order_placed layout="horizontal" %} +
+
+
+
+ +
+
+ {% bootstrap_field form.mail_text_order_paid layout="horizontal" %} +
+
+
+
+ +
+
+ {% bootstrap_field form.mail_text_order_free layout="horizontal" %} +
+
+
+
+ + +
+
+ +
+
+ {% bootstrap_field form.mail_text_order_changed layout="horizontal" %} +
+
+
+
+ +
+
+ {% bootstrap_field form.mail_days_order_expire_warning layout="horizontal" %} + {% bootstrap_field form.mail_text_order_expire_warning layout="horizontal" %} +
+
+
+
{% trans "SMTP settings" %}