Skip to content

Commit

Permalink
feat: Store printed PDF attachments on communication (#25439) (#25444)
Browse files Browse the repository at this point in the history
(cherry picked from commit e76c29f)

Co-authored-by: Ankush Menat <ankush@frappe.io>
  • Loading branch information
mergify[bot] and ankush committed Mar 14, 2024
1 parent 6f242ca commit bd15abc
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 6 deletions.
4 changes: 4 additions & 0 deletions frappe/core/doctype/communication/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def make(
communication_type=None,
send_after=None,
print_language=None,
now=False,
**kwargs,
) -> dict[str, str]:
"""Make a new communication. Checks for email permissions for specified Document.
Expand Down Expand Up @@ -104,6 +105,7 @@ def make(
add_signature=False,
send_after=send_after,
print_language=print_language,
now=now,
)


Expand Down Expand Up @@ -131,6 +133,7 @@ def _make(
add_signature=True,
send_after=None,
print_language=None,
now=False,
) -> dict[str, str]:
"""Internal method to make a new communication that ignores Permission checks."""

Expand Down Expand Up @@ -185,6 +188,7 @@ def _make(
send_me_a_copy=send_me_a_copy,
print_letterhead=print_letterhead,
print_language=print_language,
now=now,
)

emails_not_sent_to = comm.exclude_emails_list(include_sender=send_me_a_copy)
Expand Down
3 changes: 2 additions & 1 deletion frappe/core/doctype/communication/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ def send_email(
print_letterhead=None,
is_inbound_mail_communcation=None,
print_language=None,
now=False,
):
if input_dict := self.sendmail_input_dict(
print_html=print_html,
Expand All @@ -317,4 +318,4 @@ def send_email(
is_inbound_mail_communcation=is_inbound_mail_communcation,
print_language=print_language,
):
frappe.sendmail(**input_dict)
frappe.sendmail(now=now, **input_dict)
2 changes: 1 addition & 1 deletion frappe/core/doctype/communication/test_communication.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import frappe
from frappe.core.doctype.communication.communication import Communication, get_emails, parse_email
from frappe.core.doctype.communication.email import add_attachments
from frappe.core.doctype.communication.email import add_attachments, make
from frappe.email.doctype.email_queue.email_queue import EmailQueue
from frappe.tests.utils import FrappeTestCase

Expand Down
10 changes: 9 additions & 1 deletion frappe/core/doctype/system_settings/system_settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"disable_standard_email_footer",
"hide_footer_in_auto_email_reports",
"attach_view_link",
"store_attached_pdf_document",
"welcome_email_template",
"reset_password_template",
"files_tab",
Expand Down Expand Up @@ -648,12 +649,19 @@
"fieldtype": "Int",
"label": "Link Field Results Limit",
"non_negative": 1
},
{
"default": "1",
"description": "When sending document using email, store the PDF on Communication. Warning: This can increase your storage usage.",
"fieldname": "store_attached_pdf_document",
"fieldtype": "Check",
"label": "Store Attached PDF Document"
}
],
"icon": "fa fa-cog",
"issingle": 1,
"links": [],
"modified": "2024-01-26 11:29:20.924425",
"modified": "2024-03-14 15:18:01.465057",
"modified_by": "Administrator",
"module": "Core",
"name": "System Settings",
Expand Down
1 change: 1 addition & 0 deletions frappe/core/doctype/system_settings/system_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class SystemSettings(Document):
rounding_method: DF.Literal["Banker's Rounding (legacy)", "Banker's Rounding", "Commercial Rounding"]
session_expiry: DF.Data | None
setup_complete: DF.Check
store_attached_pdf_document: DF.Check
strip_exif_metadata_from_uploaded_images: DF.Check
time_format: DF.Literal["HH:mm:ss", "HH:mm"]
time_zone: DF.Literal[None]
Expand Down
22 changes: 22 additions & 0 deletions frappe/email/doctype/email_queue/email_queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,11 +385,33 @@ def include_attachments(self, message):
elif attachment.get("print_format_attachment") == 1:
attachment.pop("print_format_attachment", None)
print_format_file = frappe.attach_print(**attachment)
self._store_file(print_format_file["fname"], print_format_file["fcontent"])
print_format_file.update({"parent": message_obj})
add_attachment(**print_format_file)

return safe_encode(message_obj.as_string())

def _store_file(self, file_name, content):
if not frappe.get_system_settings("store_attached_pdf_document"):
return

file_data = frappe._dict(file_name=file_name, is_private=1)

# Store on communication if available, else email queue doc
if self.queue_doc.communication:
file_data.attached_to_doctype = "Communication"
file_data.attached_to_name = self.queue_doc.communication
else:
file_data.attached_to_doctype = self.queue_doc.doctype
file_data.attached_to_name = self.queue_doc.name

if frappe.db.exists("File", file_data):
return

file = frappe.new_doc("File", **file_data)
file.content = content
file.insert()


@frappe.whitelist()
def bulk_retry(queues):
Expand Down
58 changes: 55 additions & 3 deletions frappe/tests/test_email.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@
import requests

import frappe
from frappe.core.doctype.communication.email import make
from frappe.desk.form.load import get_attachments
from frappe.email.doctype.email_account.test_email_account import TestEmailAccount
from frappe.email.doctype.email_queue.email_queue import QueueBuilder
from frappe.tests.utils import FrappeTestCase
from frappe.query_builder.utils import db_type_is
from frappe.tests.test_query_builder import run_only_if
from frappe.tests.utils import FrappeTestCase, change_settings

test_dependencies = ["Email Account"]

Expand Down Expand Up @@ -342,11 +346,16 @@ def tearDown(self) -> None:
frappe.flags.testing_email = False
return super().tearDown()

def get_last_sent_emails(self):
@classmethod
def get_last_sent_emails(cls):
return requests.get(
f"{self.SMTP4DEV_WEB}/api/Messages?sortColumn=receivedDate&sortIsDescending=true"
f"{cls.SMTP4DEV_WEB}/api/Messages?sortColumn=receivedDate&sortIsDescending=true"
).json()

@classmethod
def get_message(cls, message_id):
return requests.get(f"{cls.SMTP4DEV_WEB}/api/Messages/{message_id}").json()

def test_send_email(self):
sender = "a@example.io"
recipients = "b@example.io,c@example.io"
Expand All @@ -368,3 +377,46 @@ def test_send_email(self):
self.assertEqual(sent_mail["from"], sender)
self.assertEqual(sent_mail["subject"], subject)
self.assertSetEqual(set(recipients.split(",")), {m["to"] for m in sent_mails})

@run_only_if(db_type_is.MARIADB)
@change_settings("System Settings", store_attached_pdf_document=1)
def test_store_attachments(self):
""" "attach print" feature just tells email queue which document to attach, this is not
actually stored unless system setting says so."""

name = make(
sender="test_sender@example.com",
recipients="test_recipient@example.com,test_recipient2@example.com",
content="test mail 001",
subject="test-mail-002",
doctype="Email Account",
name="_Test Email Account 1",
print_format="Standard",
send_email=True,
now=True,
).get("name")

communication = frappe.get_doc("Communication", name)

attachments = get_attachments(communication.doctype, communication.name)
self.assertEqual(len(attachments), 1)

file = frappe.get_doc("File", attachments[0].name)
self.assertGreater(file.file_size, 1000)
self.assertIn("pdf", file.file_name.lower())
sent_mails = self.get_last_sent_emails()
self.assertEqual(len(sent_mails), 2)

for mail in sent_mails:
email_content = self.get_message(mail["id"])

attachment_found = False
for part in email_content["parts"]:
for child_part in part["childParts"]:
if child_part["isAttachment"]:
attachment_found = True
self.assertIn("pdf", child_part["name"])
break

if not attachment_found:
self.fail("Attachment not found", email_content)

0 comments on commit bd15abc

Please sign in to comment.