Attach receipt PDF to "payment successful" email #96850

Closed
Anna Sirota wants to merge 5 commits from attach-pdf-to-email into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
4 changed files with 44 additions and 17 deletions
Showing only changes of commit 6593b9e6a7 - Show all commits

View File

@ -139,12 +139,15 @@ def send_mail_automatic_payment_performed(order_id: int, transaction_id: int):
from_email=None, # just use the configured default From-address. from_email=None, # just use the configured default From-address.
to=[email], to=[email],
) )
file_data = b'TODO'
file_name = f'blender-development-fund-receipt-{order.display_number}.pdf'
print(file_name)
file_data = PDFResponse().rendered_content
msg.attach(file_name, file_data, 'application/pdf')
msg.attach_alternative(email_body_html, 'text/html') msg.attach_alternative(email_body_html, 'text/html')
# Attach the PDF receipt
if order.status == 'paid':
file_name = f'blender-development-fund-receipt-{order.display_number}.pdf'
pdf_context = {'order': order}
pdf_response = PDFResponse(None, 'looper/settings/receipt_pdf.html', context=pdf_context)
file_data = pdf_response.rendered_content
msg.attach(file_name, file_data, 'application/pdf')
msg.send(fail_silently=False) msg.send(fail_silently=False)
logger.info('Sent %r notification to %s', order.status, email) logger.info('Sent %r notification to %s', order.status, email)

View File

@ -14,6 +14,7 @@ from looper.tests import AbstractLooperTestCase
from looper.tests.factories import SubscriptionFactory, create_customer_with_billing_address from looper.tests.factories import SubscriptionFactory, create_customer_with_billing_address
import looper.exceptions import looper.exceptions
from blender_fund_main.tests.utils import extract_text_from_pdf
from blender_fund_main.utils import html_to_text from blender_fund_main.utils import html_to_text
import blender_fund_main.tasks as tasks import blender_fund_main.tasks as tasks
@ -314,6 +315,7 @@ class TestClockEmails(AbstractLooperTestCase):
expected_soft_failed_email_html_stripped, expected_soft_failed_email_html_stripped,
html_to_text(email.alternatives[0][0]), html_to_text(email.alternatives[0][0]),
) )
self.assertEqual(len(email.attachments), 0)
@responses.activate @responses.activate
def test_automated_payment_failed_email_is_sent(self): def test_automated_payment_failed_email_is_sent(self):
@ -378,6 +380,7 @@ class TestClockEmails(AbstractLooperTestCase):
expected_failed_email_html_stripped, expected_failed_email_html_stripped,
html_to_text(email.alternatives[0][0]), html_to_text(email.alternatives[0][0]),
) )
self.assertEqual(len(email.attachments), 0)
@responses.activate @responses.activate
def test_automated_payment_paid_email_is_sent(self): def test_automated_payment_paid_email_is_sent(self):
@ -438,6 +441,21 @@ class TestClockEmails(AbstractLooperTestCase):
expected_payment_received_email_html_stripped, expected_payment_received_email_html_stripped,
html_to_text(email.alternatives[0][0]), html_to_text(email.alternatives[0][0]),
) )
self.assertEqual(len(email.attachments), 1)
self.assertEqual(email.attachments[0][1], 'application/pdf')
pdf_text = extract_text_from_pdf(email.attachments[0][0])
self.assertEqual(
pdf_text,
expected_text_tmpl.format(
order=order,
expected_address=' -',
expected_currency_symbol='$',
expected_payment_method='PayPal account test@example.com',
expected_total='14.80',
),
pdf_text,
)
@override_settings(LOOPER_MANAGER_MAIL='admin@example.com') @override_settings(LOOPER_MANAGER_MAIL='admin@example.com')
def test_managed_subscription_notification_email_is_sent(self): def test_managed_subscription_notification_email_is_sent(self):
@ -469,3 +487,4 @@ class TestClockEmails(AbstractLooperTestCase):
expected_managed_email_html_stripped, expected_managed_email_html_stripped,
html_to_text(email.alternatives[0][0]), html_to_text(email.alternatives[0][0]),
) )
self.assertEqual(len(email.attachments), 0)

View File

@ -1,11 +1,9 @@
from io import BytesIO
from unittest.mock import patch, Mock from unittest.mock import patch, Mock
from django.conf import settings from django.conf import settings
from django.test import override_settings from django.test import override_settings
from django.urls import reverse from django.urls import reverse
from freezegun import freeze_time from freezegun import freeze_time
from pypdf import PdfReader
from looper.tests import AbstractLooperTestCase from looper.tests import AbstractLooperTestCase
from looper.tests.factories import ( from looper.tests.factories import (
@ -16,6 +14,9 @@ from looper.tests.factories import (
create_customer_with_billing_address, create_customer_with_billing_address,
) )
from blender_fund_main.tests.utils import extract_text_from_pdf
production_storage = settings.STATICFILES_STORAGE production_storage = settings.STATICFILES_STORAGE
expected_text_tmpl = '''Stichting Blender Foundation expected_text_tmpl = '''Stichting Blender Foundation
@ -132,12 +133,6 @@ class TestReceiptPDFContent(AbstractLooperTestCase):
self.paid_order.status = 'paid' self.paid_order.status = 'paid'
self.paid_order.save() self.paid_order.save()
def _extract_text_from_pdf(self, response):
pdf = PdfReader(BytesIO(response.content))
self.assertEqual(1, len(pdf.pages))
pdf_page = pdf.pages[0]
return pdf_page.extract_text()
def test_get_pdf_unpaid_order_not_found(self): def test_get_pdf_unpaid_order_not_found(self):
unpaid_order = OrderFactory( unpaid_order = OrderFactory(
customer=self.payment_method.customer, customer=self.payment_method.customer,
@ -177,7 +172,7 @@ class TestReceiptPDFContent(AbstractLooperTestCase):
self.assertEqual(200, response.status_code) self.assertEqual(200, response.status_code)
pdf_text = self._extract_text_from_pdf(response) pdf_text = extract_text_from_pdf(response)
self.assertEqual( self.assertEqual(
pdf_text, pdf_text,
expected_text_tmpl.format( expected_text_tmpl.format(
@ -214,7 +209,7 @@ class TestReceiptPDFContent(AbstractLooperTestCase):
self.assertEqual(200, response.status_code) self.assertEqual(200, response.status_code)
pdf_text = self._extract_text_from_pdf(response) pdf_text = extract_text_from_pdf(response)
self.assertEqual( self.assertEqual(
pdf_text, pdf_text,
expected_text_tmpl.format( expected_text_tmpl.format(
@ -248,7 +243,7 @@ class TestReceiptPDFContent(AbstractLooperTestCase):
self.assertEqual(200, response.status_code) self.assertEqual(200, response.status_code)
pdf_text = self._extract_text_from_pdf(response) pdf_text = extract_text_from_pdf(response)
self.assertEqual( self.assertEqual(
pdf_text, pdf_text,
expected_text_tmpl.format( expected_text_tmpl.format(
@ -280,7 +275,7 @@ class TestReceiptPDFContent(AbstractLooperTestCase):
self.assertEqual(200, response.status_code) self.assertEqual(200, response.status_code)
pdf_text = self._extract_text_from_pdf(response) pdf_text = extract_text_from_pdf(response)
self.assertEqual( self.assertEqual(
pdf_text, pdf_text,
expected_text_tmpl.format( expected_text_tmpl.format(

View File

@ -0,0 +1,10 @@
from io import BytesIO
from pypdf import PdfReader
def extract_text_from_pdf(response):
pdf = PdfReader(BytesIO(response.content))
assert 1 == len(pdf.pages)
pdf_page = pdf.pages[0]
return pdf_page.extract_text()