mirror of
https://github.com/pretix/pretix.git
synced 2026-04-28 00:02:37 +00:00
Allow attaching invoices to emails
This commit is contained in:
@@ -368,7 +368,7 @@ class Order(LoggedModel):
|
||||
|
||||
def send_mail(self, subject: str, template: Union[str, LazyI18nString],
|
||||
context: Dict[str, Any]=None, log_entry_type: str='pretix.event.order.email.sent',
|
||||
user: User=None, headers: dict=None, sender: str=None):
|
||||
user: User=None, headers: dict=None, sender: str=None, invoices: list=None):
|
||||
"""
|
||||
Sends an email to the user that placed this order. Basically, this method does two things:
|
||||
|
||||
@@ -393,7 +393,8 @@ class Order(LoggedModel):
|
||||
email_content = render_mail(template, context)[0]
|
||||
mail(
|
||||
recipient, subject, template, context,
|
||||
self.event, self.locale, self, headers, sender
|
||||
self.event, self.locale, self, headers, sender,
|
||||
invoices=invoices
|
||||
)
|
||||
except SendMailException:
|
||||
raise
|
||||
@@ -404,7 +405,8 @@ class Order(LoggedModel):
|
||||
data={
|
||||
'subject': subject,
|
||||
'message': email_content,
|
||||
'recipient': recipient
|
||||
'recipient': recipient,
|
||||
'invoices': [i.pk for i in invoices] if invoices else []
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ def build_cancellation(invoice: Invoice):
|
||||
return invoice
|
||||
|
||||
|
||||
def generate_cancellation(invoice: Invoice):
|
||||
def generate_cancellation(invoice: Invoice, trigger_pdf=True):
|
||||
cancellation = copy.copy(invoice)
|
||||
cancellation.pk = None
|
||||
cancellation.invoice_no = None
|
||||
@@ -170,7 +170,8 @@ def generate_cancellation(invoice: Invoice):
|
||||
cancellation.save()
|
||||
|
||||
cancellation = build_cancellation(cancellation)
|
||||
invoice_pdf(cancellation.pk)
|
||||
if trigger_pdf:
|
||||
invoice_pdf(cancellation.pk)
|
||||
return cancellation
|
||||
|
||||
|
||||
@@ -183,7 +184,7 @@ def regenerate_invoice(invoice: Invoice):
|
||||
return invoice
|
||||
|
||||
|
||||
def generate_invoice(order: Order):
|
||||
def generate_invoice(order: Order, trigger_pdf=True):
|
||||
locale = order.event.settings.get('invoice_language')
|
||||
if locale:
|
||||
if locale == '__user__':
|
||||
@@ -197,10 +198,11 @@ def generate_invoice(order: Order):
|
||||
locale=locale
|
||||
)
|
||||
invoice = build_invoice(invoice)
|
||||
invoice_pdf(invoice.pk)
|
||||
if trigger_pdf:
|
||||
invoice_pdf(invoice.pk)
|
||||
|
||||
if order.status in (Order.STATUS_CANCELED, Order.STATUS_REFUNDED):
|
||||
generate_cancellation(invoice)
|
||||
generate_cancellation(invoice, trigger_pdf)
|
||||
|
||||
return invoice
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ from typing import Any, Dict, List, Union
|
||||
import bleach
|
||||
import cssutils
|
||||
import markdown
|
||||
from celery import chain
|
||||
from django.conf import settings
|
||||
from django.core.mail import EmailMultiAlternatives, get_connection
|
||||
from django.template.loader import get_template
|
||||
@@ -12,7 +13,8 @@ from i18nfield.strings import LazyI18nString
|
||||
from inlinestyler.utils import inline_css
|
||||
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import Event, InvoiceAddress, Order
|
||||
from pretix.base.models import Event, Invoice, InvoiceAddress, Order
|
||||
from pretix.base.services.invoices import invoice_pdf_task
|
||||
from pretix.celery_app import app
|
||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||
|
||||
@@ -33,7 +35,7 @@ class SendMailException(Exception):
|
||||
|
||||
def mail(email: str, subject: str, template: Union[str, LazyI18nString],
|
||||
context: Dict[str, Any]=None, event: Event=None, locale: str=None,
|
||||
order: Order=None, headers: dict=None, sender: str=None):
|
||||
order: Order=None, headers: dict=None, sender: str=None, invoices: list=None):
|
||||
"""
|
||||
Sends out an email to a user. The mail will be sent synchronously or asynchronously depending on the installation.
|
||||
|
||||
@@ -61,6 +63,8 @@ def mail(email: str, subject: str, template: Union[str, LazyI18nString],
|
||||
:param sender: Set the sender email address. If not set and ``event`` is set, the event's default will be used,
|
||||
otherwise the system default.
|
||||
|
||||
:param invoices: A list of invoices to attach to this email.
|
||||
|
||||
:raises MailOrderException: on obvious, immediate failures. Not raising an exception does not necessarily mean
|
||||
that the email has been sent, just that it has been queued by the email backend.
|
||||
"""
|
||||
@@ -137,15 +141,42 @@ def mail(email: str, subject: str, template: Union[str, LazyI18nString],
|
||||
|
||||
tpl = get_template('pretixbase/email/plainwrapper.html')
|
||||
body_html = tpl.render(htmlctx)
|
||||
return mail_send([email], subject, body_plain, body_html, sender, event.id if event else None, headers)
|
||||
|
||||
send_task = mail_send_task.si(
|
||||
to=[email],
|
||||
subject=subject,
|
||||
body=body_plain,
|
||||
html=body_html,
|
||||
sender=sender,
|
||||
event=event.id if event else None,
|
||||
headers=headers,
|
||||
invoices=[i.pk for i in invoices]
|
||||
)
|
||||
|
||||
if invoices:
|
||||
task_chain = [invoice_pdf_task.si(i.pk).on_error(send_task) for i in invoices if not i.file]
|
||||
else:
|
||||
task_chain = []
|
||||
|
||||
task_chain.append(send_task)
|
||||
chain(*task_chain).apply_async()
|
||||
|
||||
|
||||
@app.task
|
||||
def mail_send_task(to: List[str], subject: str, body: str, html: str, sender: str,
|
||||
event: int=None, headers: dict=None, bcc: List[str]=None) -> bool:
|
||||
def mail_send_task(*args, to: List[str], subject: str, body: str, html: str, sender: str,
|
||||
event: int=None, headers: dict=None, bcc: List[str]=None, invoices: List[int]=None) -> bool:
|
||||
email = EmailMultiAlternatives(subject, body, sender, to=to, bcc=bcc, headers=headers)
|
||||
if html is not None:
|
||||
email.attach_alternative(inline_css(html), "text/html")
|
||||
if invoices:
|
||||
invoices = Invoice.objects.filter(pk__in=invoices)
|
||||
for inv in invoices:
|
||||
if inv.file:
|
||||
email.attach(
|
||||
'{}.pdf'.format(inv.number),
|
||||
inv.file.file.read(),
|
||||
'application/pdf'
|
||||
)
|
||||
if event:
|
||||
event = Event.objects.get(id=event)
|
||||
backend = event.get_mail_backend()
|
||||
|
||||
@@ -127,9 +127,13 @@ def mark_order_paid(order: Order, provider: str=None, info: str=None, date: date
|
||||
}, user=user, api_token=api_token)
|
||||
order_paid.send(order.event, order=order)
|
||||
|
||||
invoice = None
|
||||
if order.event.settings.get('invoice_generate') in ('True', 'paid') and invoice_qualified(order):
|
||||
if not order.invoices.exists():
|
||||
generate_invoice(order)
|
||||
invoice = generate_invoice(
|
||||
order,
|
||||
trigger_pdf=not send_mail or not order.event.settings.invoice_email_attachment
|
||||
)
|
||||
|
||||
if send_mail:
|
||||
with language(order.locale):
|
||||
@@ -155,7 +159,8 @@ def mark_order_paid(order: Order, provider: str=None, info: str=None, date: date
|
||||
try:
|
||||
order.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
'pretix.event.order.email.order_paid', user
|
||||
'pretix.event.order.email.order_paid', user,
|
||||
invoices=[invoice] if invoice and order.event.settings.invoice_email_attachment else []
|
||||
)
|
||||
except SendMailException:
|
||||
logger.exception('Order paid email could not be sent')
|
||||
@@ -502,9 +507,11 @@ def _perform_order(event: str, payment_provider: str, position_ids: List[str],
|
||||
order = _create_order(event, email, positions, now_dt, pprov,
|
||||
locale=locale, address=addr, meta_info=meta_info)
|
||||
|
||||
invoice = order.invoices.last() # Might be generated by plugin already
|
||||
if event.settings.get('invoice_generate') == 'True' and invoice_qualified(order):
|
||||
if not order.invoices.exists():
|
||||
generate_invoice(order)
|
||||
if not invoice:
|
||||
invoice = generate_invoice(order, trigger_pdf=not event.settings.invoice_email_attachment)
|
||||
# send_mail will trigger PDF generation later
|
||||
|
||||
if order.total == Decimal('0.00'):
|
||||
email_template = event.settings.mail_text_order_free
|
||||
@@ -536,7 +543,8 @@ def _perform_order(event: str, payment_provider: str, position_ids: List[str],
|
||||
try:
|
||||
order.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
log_entry
|
||||
log_entry,
|
||||
invoices=[invoice] if invoice and event.settings.invoice_email_attachment else []
|
||||
)
|
||||
except SendMailException:
|
||||
logger.exception('Order received email could not be sent')
|
||||
|
||||
@@ -129,6 +129,10 @@ DEFAULTS = {
|
||||
'default': '__user__',
|
||||
'type': str
|
||||
},
|
||||
'invoice_email_attachment': {
|
||||
'default': 'False',
|
||||
'type': bool
|
||||
},
|
||||
'show_items_outside_presale_period': {
|
||||
'default': 'True',
|
||||
'type': bool
|
||||
|
||||
@@ -544,7 +544,16 @@ class InvoiceSettingsForm(SettingsForm):
|
||||
('user', _('Automatically on user request')),
|
||||
('True', _('Automatically for all created orders')),
|
||||
('paid', _('Automatically on payment')),
|
||||
)
|
||||
),
|
||||
help_text=_("Invoices will never be automatically generated for free orders.")
|
||||
)
|
||||
invoice_email_attachment = forms.BooleanField(
|
||||
label=_("Attach invoices to emails"),
|
||||
help_text=_("If invocies are automatically generated for all orders, they will be attached to the order "
|
||||
"confirmation mail. If they are automatically generated on payment, they will be attached to the "
|
||||
"payment confirmation mail. If they are not automatically generated, they will not be attached "
|
||||
"to emails."),
|
||||
required=False
|
||||
)
|
||||
invoice_renderer = forms.ChoiceField(
|
||||
label=_("Invoice style"),
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
{% bootstrap_field form.invoice_address_required layout="control" %}
|
||||
{% bootstrap_field form.invoice_name_required layout="control" %}
|
||||
{% bootstrap_field form.invoice_generate layout="control" %}
|
||||
{% bootstrap_field form.invoice_email_attachment layout="control" %}
|
||||
{% bootstrap_field form.invoice_address_vatid layout="control" %}
|
||||
{% bootstrap_field form.invoice_numbers_consecutive layout="control" %}
|
||||
{% bootstrap_field form.invoice_numbers_prefix layout="control" %}
|
||||
|
||||
Reference in New Issue
Block a user