Last active
October 17, 2024 20:01
-
-
Save brian-pond/ecf30aafb0a290347148545ea9b5a7ea to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" ftp/utilities/pdf.py """ | |
import os | |
from PyPDF2 import PdfFileWriter | |
import frappe | |
NoneType = type(None) | |
''' | |
USAGE EXAMPLE: | |
order_dict = { 'Sales Order': [ 'SO-0001', 'SO-0002', 'SO-0003', 'SO-0004' ] } | |
file_document_key, file_name = create_multi_pdf(document_dict=order_dict, | |
file_name_no_ext="my_new_pdf_file", | |
print_format="My Print Format Name", | |
email_recipient="[email protected]") | |
print(f"I just created a PDF file named {file_name}") | |
print(f"I can find this in the File DocType, with ID = {file_document_key}") | |
''' | |
@frappe.whitelist() | |
def create_multi_pdf(document_dict: dict, | |
file_name_no_ext: str, | |
folder="Home", | |
print_format=None, | |
pdf_options=None, | |
email_recipient=None) -> tuple: | |
""" | |
Alternative version of Frappe's "download_multi_pdf". | |
* This version does not alter HTTP responses. | |
* Returns a PDF consisting of multiple, concatenated documents. | |
* The documents can be from a single DocType or multiple DocTypes | |
* Documents are saved on the web server, and also in the Files document. | |
Arguments: | |
document_dict (dict): | |
key (string): DocType name | |
value (list): of strings of doc names which need to be concatenated and printed | |
folder: | |
name of the folder in File DocType to store the PDF file. | |
file_name_no_ext (string): | |
name of the pdf which is generated. Do not add the suffix '.pdf' | |
print_format: | |
Print Format to be used | |
Returns: | |
A tuple of the File DocType 'name' (ID), and the actual filename. | |
""" | |
pdf_writer = PdfFileWriter() | |
number_of_docs_added = 0 | |
for doctype_name in document_dict: | |
for doc_name in document_dict[doctype_name]: | |
try: | |
html = create_html_from_print_format(doctype_name=doctype_name, | |
document_name=doc_name, | |
print_format=print_format) | |
pdf_writer = append_html_to_pdfwriter(html, pdf_writer, pdf_options) | |
number_of_docs_added += 1 | |
except Exception as ex: | |
raise ex | |
if not number_of_docs_added: | |
frappe.msgprint("No orders were processed into a PDF. Please check your criteria, and the Error Log.") | |
return None, None | |
file_name = append_suffix_to_filename(file_name_no_ext) # This adds a _1.pdf, _2.pdf, etc | |
# Write to a Temporary Directory: | |
temp_filename = os.path.join("/tmp", f"frappe-pdf-{frappe.generate_hash()}.pdf") | |
with open(temp_filename,"wb") as fileobj: | |
pdf_writer.write(fileobj) | |
# Read back the file content: | |
with open(temp_filename, "rb") as fileobj: | |
file_content = fileobj.read() | |
doc_file = frappe.get_doc({ | |
"doctype": "File", | |
"file_name": file_name, | |
"attached_to_doctype": None, | |
"attached_to_name": None, | |
"content": file_content, | |
"folder": folder | |
}) | |
doc_file.save() | |
if email_recipient: | |
recipient = frappe.get_doc('User', email_recipient).email | |
sender = frappe.get_doc('User', email_recipient).email | |
frappe.sendmail( | |
recipients = recipient, | |
sender = sender, | |
subject = "New PDF File is ready", | |
message = f"Created a new PDF for {number_of_docs_added} documents.", | |
add_unsubscribe_link=0, | |
delayed=False, # send it right away | |
attachments=None) | |
return doc_file.name, doc_file.file_name # return a tuple to the caller | |
def append_suffix_to_filename(filename_base): | |
filters = { | |
"file_name": ("like", f"{filename_base}%") | |
} | |
fields = fields=["name", "file_name"] | |
other_files = frappe.get_list("File", filters=filters, fields=fields, debug=False) | |
if not other_files or len(other_files) == 0: | |
return f"{filename_base}.pdf" | |
new_suffix = str(len(other_files) + 1) | |
return f"{filename_base}_{new_suffix}.pdf" | |
def create_html_from_print_format(doctype_name, document_name, print_format): | |
""" | |
Given a Document and Print Format, creates some HTML. | |
Note that CSS is linked in the <head>, not embedded. This HTML will not render correctly as-is. | |
""" | |
html = frappe.get_print(doctype=doctype_name, name=document_name, print_format=print_format) | |
return html | |
def append_html_to_pdfwriter(html, pdf_writer, pdf_options=None): | |
""" | |
Accepts HTML and PDF Writer, and returns an updated PDF Writer. | |
""" | |
from frappe.utils.pdf import get_pdf | |
ret = get_pdf(html, output=pdf_writer, options=pdf_options) | |
print(f"Result of 'append_html_to_pdfwriter()' = {ret}") | |
return ret |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello, i do get error
"exception": "ValueError: key must be PdfObject",
File "apps/module_name/module_name/api/utilities.pdf.py", line 61, in create_multi_pdf raise ex File "apps/module_name/module_name/api/utilities.pdf.py", line 58, in create_multi_pdf pdf_writer = append_html_to_pdfwriter(html, pdf_writer, pdf_options) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "apps/module_name/module_name/api/utilities.pdf.py", line 133, in append_html_to_pdfwriter ret = get_pdf(html, output=pdf_writer, options=pdf_options)