diff --git a/frappe/public/js/frappe/list/bulk_operations.js b/frappe/public/js/frappe/list/bulk_operations.js index 69ccd596006..ce0fec922dd 100644 --- a/frappe/public/js/frappe/list/bulk_operations.js +++ b/frappe/public/js/frappe/list/bulk_operations.js @@ -35,11 +35,6 @@ export default class BulkOperations { return; } - if (valid_docs.length > 50) { - frappe.msgprint(__("You can only print upto 50 documents at a time")); - return; - } - const dialog = new frappe.ui.Dialog({ title: __("Print Documents"), fields: [ @@ -102,28 +97,25 @@ export default class BulkOperations { pdf_options = JSON.stringify({ "page-size": args.page_size }); } - const w = window.open( - "/api/method/frappe.utils.print_format.download_multi_pdf?" + - "doctype=" + - encodeURIComponent(this.doctype) + - "&name=" + - encodeURIComponent(json_string) + - "&format=" + - encodeURIComponent(print_format) + - "&no_letterhead=" + - (with_letterhead ? "0" : "1") + - "&letterhead=" + - encodeURIComponent(letterhead) + - "&options=" + - encodeURIComponent(pdf_options) - ); + const task_id = Math.random().toString(36).slice(-5); + frappe.realtime.task_subscribe(task_id); + frappe.realtime.on(`task_progress:${task_id}`, (data) => { + frappe.msgprint( + `Please click here to download the PDF` + ); + }); - if (!w) { - frappe.msgprint(__("Please enable pop-ups")); - return; - } + frappe.call("frappe.utils.print_format.download_multi_pdf", { + doctype: this.doctype, + name: json_string, + format: print_format, + no_letterhead: with_letterhead ? "0" : "1", + letterhead: letterhead, + options: pdf_options, + task_id: task_id, + }); + dialog.hide(); }); - dialog.show(); } diff --git a/frappe/utils/print_format.py b/frappe/utils/print_format.py index 29c76d7615e..294c64aa2c9 100644 --- a/frappe/utils/print_format.py +++ b/frappe/utils/print_format.py @@ -19,7 +19,35 @@ @frappe.whitelist() -def download_multi_pdf(doctype, name, format=None, no_letterhead=False, letterhead=None, options=None): +def download_multi_pdf( + doctype, + name, + format=None, + no_letterhead=False, + letterhead=None, + options=None, + task_id=None, +): + """ + Calls _download_multi_pdf with the given parameters and returns the URL of the generated PDF + + """ + frappe.enqueue( + _download_multi_pdf, + doctype=doctype, + name=name, + format=format, + no_letterhead=no_letterhead, + letterhead=letterhead, + options=options, + task_id=task_id, + ) + frappe.local.response["http_status_code"] = 201 + + +def _download_multi_pdf( + doctype, name, format=None, no_letterhead=False, letterhead=None, options=None, task_id=None +): """Return a PDF compiled by concatenating multiple documents. The documents can be from a single DocType or multiple DocTypes. @@ -52,6 +80,7 @@ def download_multi_pdf(doctype, name, format=None, no_letterhead=False, letterhe Returns: PDF: A PDF generated by the concatenation of the mentioned input docs + """ import json @@ -63,23 +92,35 @@ def download_multi_pdf(doctype, name, format=None, no_letterhead=False, letterhe if not isinstance(doctype, dict): result = json.loads(name) + total_docs = len(result) # Concatenating pdf files - for ss in result: - pdf_writer = frappe.get_print( - doctype, - ss, - format, - as_pdf=True, - output=pdf_writer, - no_letterhead=no_letterhead, - letterhead=letterhead, - pdf_options=options, + for idx, ss in enumerate(result): + try: + pdf_writer = frappe.get_print( + doctype, + ss, + format, + as_pdf=True, + output=pdf_writer, + no_letterhead=no_letterhead, + letterhead=letterhead, + pdf_options=options, + ) + except Exception: + frappe.publish_realtime(task_id=task_id, message={"message": "Failed"}) + + # Publish progress + frappe.publish_progress( + percent=(idx + 1) / total_docs * 100, + title="Creating PDF", + description=f"{idx+1} out of {total_docs}", + task_id=task_id, ) - frappe.local.response.filename = "{doctype}.pdf".format( - doctype=doctype.replace(" ", "-").replace("/", "-") - ) + else: + total_docs = sum([len(doctype[dt]) for dt in doctype]) + count = 1 for doctype_name in doctype: for doc_name in doctype[doctype_name]: try: @@ -94,19 +135,33 @@ def download_multi_pdf(doctype, name, format=None, no_letterhead=False, letterhe pdf_options=options, ) except Exception: + frappe.publish_realtime(task_id=task_id, message="Failed") frappe.log_error( title="Error in Multi PDF download", message=f"Permission Error on doc {doc_name} of doctype {doctype_name}", reference_doctype=doctype_name, reference_name=doc_name, ) - frappe.local.response.filename = f"{name}.pdf" + count += 1 + frappe.publish_progress( + percent=count / total_docs * 100, + title="Creating PDF", + description=f"{count} out of {total_docs}", + task_id=task_id, + ) with BytesIO() as merged_pdf: pdf_writer.write(merged_pdf) - frappe.local.response.filecontent = merged_pdf.getvalue() - - frappe.local.response.type = "pdf" + _file = frappe.get_doc( + { + "doctype": "File", + "file_name": f"{task_id}.pdf", + "content": merged_pdf.getvalue(), + "is_private": 1, + } + ) + _file.save() + frappe.publish_realtime(f"task_progress:{task_id}", message={"file_url": _file.unique_url}) @deprecated