Skip to content

Instantly share code, notes, and snippets.

@brian-pond
Last active October 17, 2024 20:01
Show Gist options
  • Save brian-pond/ecf30aafb0a290347148545ea9b5a7ea to your computer and use it in GitHub Desktop.
Save brian-pond/ecf30aafb0a290347148545ea9b5a7ea to your computer and use it in GitHub Desktop.
""" 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
@kalungia
Copy link

kalungia commented Oct 17, 2024

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment