forked from CGM_Public/pretix_original
Send automatic payment reminder emails, redesign mail settings
This commit is contained in:
33
src/pretix/base/migrations/0036_auto_20160902_0755.py
Normal file
33
src/pretix/base/migrations/0036_auto_20160902_0755.py
Normal file
@@ -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)
|
||||||
|
]
|
||||||
@@ -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 "
|
help_text=_("The text entered in this field will not be visible to the user and is available for your "
|
||||||
"convenience.")
|
"convenience.")
|
||||||
)
|
)
|
||||||
|
expiry_reminder_sent = models.BooleanField(
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Order")
|
verbose_name = _("Order")
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import logging
|
||||||
from collections import Counter, namedtuple
|
from collections import Counter, namedtuple
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
@@ -6,6 +7,7 @@ from typing import List, Optional
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
from django.utils.formats import date_format
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
@@ -21,7 +23,7 @@ from pretix.base.payment import BasePaymentProvider
|
|||||||
from pretix.base.services.invoices import (
|
from pretix.base.services.invoices import (
|
||||||
generate_cancellation, generate_invoice, invoice_qualified,
|
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 (
|
from pretix.base.signals import (
|
||||||
order_paid, order_placed, periodic_task, register_payment_providers,
|
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.'),
|
'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,
|
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:
|
force: bool=False, send_mail: bool=True, user: User=None) -> Order:
|
||||||
@@ -368,6 +372,41 @@ def expire_orders(sender, **kwargs):
|
|||||||
o.save()
|
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:
|
class OrderChangeManager:
|
||||||
error_messages = {
|
error_messages = {
|
||||||
'free_to_paid': _('You cannot change a free order to a paid order.'),
|
'free_to_paid': _('You cannot change a free order to a paid order.'),
|
||||||
|
|||||||
@@ -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
|
You can change your order details and view the status of your order at
|
||||||
{url}
|
{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,
|
Best regards,
|
||||||
Your {event} team"""))
|
Your {event} team"""))
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -312,35 +312,54 @@ class MailSettingsForm(SettingsForm):
|
|||||||
help_text=_("Sender address for outgoing emails")
|
help_text=_("Sender address for outgoing emails")
|
||||||
)
|
)
|
||||||
mail_text_order_placed = I18nFormField(
|
mail_text_order_placed = I18nFormField(
|
||||||
label=_("Placed order"),
|
label=_("Text"),
|
||||||
required=False,
|
required=False,
|
||||||
widget=I18nTextarea,
|
widget=I18nTextarea,
|
||||||
help_text=_("Available placeholders: {event}, {total}, {currency}, {date}, {paymentinfo}, {url}")
|
help_text=_("Available placeholders: {event}, {total}, {currency}, {date}, {paymentinfo}, {url}")
|
||||||
)
|
)
|
||||||
mail_text_order_paid = I18nFormField(
|
mail_text_order_paid = I18nFormField(
|
||||||
label=_("Paid order"),
|
label=_("Text"),
|
||||||
required=False,
|
required=False,
|
||||||
widget=I18nTextarea,
|
widget=I18nTextarea,
|
||||||
help_text=_("Available placeholders: {event}, {url}")
|
help_text=_("Available placeholders: {event}, {url}")
|
||||||
)
|
)
|
||||||
mail_text_order_free = I18nFormField(
|
mail_text_order_free = I18nFormField(
|
||||||
label=_("Free order"),
|
label=_("Text"),
|
||||||
required=False,
|
required=False,
|
||||||
widget=I18nTextarea,
|
widget=I18nTextarea,
|
||||||
help_text=_("Available placeholders: {event}, {url}")
|
help_text=_("Available placeholders: {event}, {url}")
|
||||||
)
|
)
|
||||||
mail_text_order_changed = I18nFormField(
|
mail_text_order_changed = I18nFormField(
|
||||||
label=_("Changed order"),
|
label=_("Text"),
|
||||||
required=False,
|
required=False,
|
||||||
widget=I18nTextarea,
|
widget=I18nTextarea,
|
||||||
help_text=_("Available placeholders: {event}, {url}")
|
help_text=_("Available placeholders: {event}, {url}")
|
||||||
)
|
)
|
||||||
mail_text_resend_link = I18nFormField(
|
mail_text_resend_link = I18nFormField(
|
||||||
label=_("Resend link"),
|
label=_("Text (sent by admin)"),
|
||||||
required=False,
|
required=False,
|
||||||
widget=I18nTextarea,
|
widget=I18nTextarea,
|
||||||
help_text=_("Available placeholders: {event}, {url}")
|
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(
|
smtp_use_custom = forms.BooleanField(
|
||||||
label=_("Use custom SMTP server"),
|
label=_("Use custom SMTP server"),
|
||||||
help_text=_("All mail related to your event will be sent over the smtp server specified by you."),
|
help_text=_("All mail related to your event will be sent over the smtp server specified by you."),
|
||||||
|
|||||||
@@ -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.comment': _('The order\'s internal comment has been updated.'),
|
||||||
'pretix.event.order.contact.changed': _('The email address has been changed.'),
|
'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.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:
|
if logentry.action_type in plains:
|
||||||
return plains[logentry.action_type]
|
return plains[logentry.action_type]
|
||||||
|
|||||||
@@ -12,11 +12,94 @@
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{% trans "E-mail content" %}</legend>
|
<legend>{% trans "E-mail content" %}</legend>
|
||||||
{% bootstrap_field form.mail_text_order_placed layout="horizontal" %}
|
<div class="panel-group" id="questions_group">
|
||||||
{% bootstrap_field form.mail_text_order_paid layout="horizontal" %}
|
<div class="panel panel-default">
|
||||||
{% bootstrap_field form.mail_text_order_free layout="horizontal" %}
|
<div class="panel-heading">
|
||||||
{% bootstrap_field form.mail_text_resend_link layout="horizontal" %}
|
<h4 class="panel-title">
|
||||||
{% bootstrap_field form.mail_text_order_changed layout="horizontal" %}
|
<a data-toggle="collapse" href="#order_placed">
|
||||||
|
<strong>{% trans "Placed order" %}</strong>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id="order_placed" class="panel-collapse collapse">
|
||||||
|
<div class="panel-body">
|
||||||
|
{% bootstrap_field form.mail_text_order_placed layout="horizontal" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a data-toggle="collapse" href="#order_paid">
|
||||||
|
<strong>{% trans "Paid order" %}</strong>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id="order_paid" class="panel-collapse collapse">
|
||||||
|
<div class="panel-body">
|
||||||
|
{% bootstrap_field form.mail_text_order_paid layout="horizontal" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a data-toggle="collapse" href="#order_free">
|
||||||
|
<strong>{% trans "Free order" %}</strong>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id="order_free" class="panel-collapse collapse">
|
||||||
|
<div class="panel-body">
|
||||||
|
{% bootstrap_field form.mail_text_order_free layout="horizontal" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a data-toggle="collapse" href="#resend_link">
|
||||||
|
<strong>{% trans "Resend link" %}</strong>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id="resend_link" class="panel-collapse collapse">
|
||||||
|
<div class="panel-body">
|
||||||
|
{% bootstrap_field form.mail_text_resend_link layout="horizontal" %}
|
||||||
|
{% bootstrap_field form.mail_text_resend_all_links layout="horizontal" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a data-toggle="collapse" href="#order_changed">
|
||||||
|
<strong>{% trans "Order changed" %}</strong>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id="order_changed" class="panel-collapse collapse">
|
||||||
|
<div class="panel-body">
|
||||||
|
{% bootstrap_field form.mail_text_order_changed layout="horizontal" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a data-toggle="collapse" href="#order_expirew">
|
||||||
|
<strong>{% trans "Payment reminder" %}</strong>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id="order_expirew" class="panel-collapse collapse">
|
||||||
|
<div class="panel-body">
|
||||||
|
{% bootstrap_field form.mail_days_order_expire_warning layout="horizontal" %}
|
||||||
|
{% bootstrap_field form.mail_text_order_expire_warning layout="horizontal" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{% trans "SMTP settings" %}</legend>
|
<legend>{% trans "SMTP settings" %}</legend>
|
||||||
|
|||||||
Reference in New Issue
Block a user