How to attach a print format to attachments upon saving the Doctype in ERPNext

I am new to ERPNext, I have created a custom print format in ErpNext from the Quotation Doctype.

it gets print and i can download it as pdf as well.

Now what I want is that whenever I save the Quotation Doctype, the pdf of that print format named as Scope of Work to attach in the attachments of Quotations (in the leftside bar of quotation).

Why I am doing this because when I send the quoation in the email, i downlaod the pdf manually when I print it and attach it to the email attachments so that quite and hectic process, everytime I will have to download the pdf and then attach to the email attachments.

Now when that print format will be attached to attachments upon saving the Quotation, and when I will send the email that attachement from quotation will automatically me attached to email attachments and that is what i want.

I was trying custom script but that did not worked as expected so I don’t know may be there is so some good way to achieve this.

My bench version is 14.

Please do this.

Thank you for the reply, but i can’t do this because the “Scope Of Work” is not my default print format, there are other formats I want to attach like “Scope of Work Arabic” another print format.
i need a script or function are whatever, even a custom button will do the job that when I click it, the formats will be attached to the attachments to doctype itlsef in this case the Quotation.

I have solved it by writing a function in quotation.py like below.

def attach_scope_of_work_to_attachments(doc, print_format, pdf_name):
	try:
		pdf_content = frappe.get_print(doc.doctype, doc.name, print_format, as_pdf=True)
			
		file_name = f"{doc.name}_{pdf_name}.pdf"
			
		existing_files = frappe.get_all("File",
            filters={
                "attached_to_doctype": doc.doctype,
                "attached_to_name": doc.name,
                "file_name": file_name
            },fields=["name"])
			
		if existing_files:
			for file_doc in existing_files:
				frappe.delete_doc("File", file_doc.name, force=True)

		file_doc = frappe.get_doc({
       		"doctype": "File",
       		"file_name": file_name,
       		"attached_to_doctype": doc.doctype,
       		"attached_to_name": doc.name,
       		"content": pdf_content,
       		"is_private": 0
   		})


		file_doc.save(ignore_permissions=True)

		doc.add_comment("Attachment", f"{file_doc.file_name} attached.")
			
		return True
	except Exception as e:
		frappe.throw(f"Error generating Scope of Work PDF: {str(e)}")
		return False

and wrote another function after_insert like below

def after_insert(self):
	self.attach_scope_of_work_to_attachments(print_format="Scope Of Work", pdf_name="scope_of_work")
3 Likes

If anyone needs a solution when you can press the button to select print format and language. Then attach it, here is the working code:
you have to change the doc and prefered language.

// Auto Attach PDF on Button Click for Sales Order

function store_pdf_file(frm, pdf_file) {
    let xhr = new XMLHttpRequest();
    xhr.open('POST', '/api/method/upload_file', true);
    xhr.setRequestHeader('Accept', 'application/json');
    xhr.setRequestHeader('X-Frappe-CSRF-Token', frappe.csrf_token);

    let form_data = new FormData();
    form_data.append('file', pdf_file, frm.doc.name + '.pdf');
    form_data.append('is_private', 1);
    form_data.append('doctype', frm.doc.doctype);
    form_data.append('docname', frm.doc.name);

    xhr.onerror = function() {
        frappe.throw(__('An Error occurred while attaching the PDF of the submitted transaction! Please do attach the PDF manually.'));  
    };

    xhr.onload = function() {
        if (xhr.status < 200 || xhr.status > 299) {
            let message = ''
            try {
                let response = JSON.parse(xhr.responseText);
                let server_message = JSON.parse(response._server_messages);

                message = JSON.parse(server_message[0]).message;
                // If we reached the attachment limit for a doctype, the response is an error message which is not clear.
                message += ' ' + __('Please remove old attachments and do <b>attach the PDF of the submitted transaction manually</b>.');
            }
            catch (e) {
                message = 'An Error occurred while attaching the PDF of the submitted transaction! Please do attach the PDF manually.';
            }
            frappe.throw(__(message));
        } else {
            frappe.msgprint(__('PDF attached successfully.'));
            frm.reload_doc();
        }
    };
    xhr.send(form_data);
}

function show_pdf_options_dialog(frm) {
    // Fetch available print formats
    frappe.call({
        method: 'frappe.client.get_list',
        args: {
            doctype: 'Print Format',
            fields: ['name'],
            filters: {
                doc_type: frm.doc.doctype
            }
        },
        callback: function(response) {
            let print_formats = response.message.map(format => format.name);

            // Fetch available languages
            frappe.call({
                method: 'frappe.client.get_list',
                args: {
                    doctype: 'Language',
                    fields: ['name']
                },
                callback: function(response) {
                    let languages = response.message.map(language => language.name);

                    // Create the dialog with fetched options
                    let d = new frappe.ui.Dialog({
                        title: __('Select PDF Options'),
                        fields: [
                            {
                                label: __('Print Format'),
                                fieldname: 'print_format',
                                fieldtype: 'Select',
                                options: print_formats,
                                default: print_formats[0]
                            },
                            {
                                label: __('Language'),
                                fieldname: 'language',
                                fieldtype: 'Select',
                                options: ['en', 'pl', 'cs'], // Add more languages as needed
                                default: 'pl'
                            }
                        ],
                        primary_action_label: __('Generate PDF'),
                        primary_action(values) {
                            generate_and_attach_pdf(frm, values.print_format, values.language);
                            d.hide();
                        }
                    });

                    d.show();
                }
            });
        }
    });
}

function generate_and_attach_pdf(frm, print_format, language) {
    let xhr_pdf = new XMLHttpRequest();
    xhr_pdf.open(
        'GET',
        `/api/method/frappe.utils.print_format.download_pdf?doctype=${frm.doc.doctype}&name=${frm.doc.name}&format=${print_format}&_lang=${language}`,
        true
    );

    xhr_pdf.setRequestHeader('Accept', 'application/pdf');
    xhr_pdf.setRequestHeader('X-Frappe-CSRF-Token', frappe.csrf_token);
    
    xhr_pdf.responseType = 'blob';

    xhr_pdf.onload = function() {
        if (xhr_pdf.status == 200) {
            store_pdf_file(frm, xhr_pdf.response);
        } else {
            frappe.throw(__('Failed to generate PDF.'));
        }
    };
    xhr_pdf.send();
}

frappe.ui.form.on('Sales Order', {
    refresh: function(frm) {
        // Add custom button to generate PDF and attach
        frm.add_custom_button(__('Generate PDF and Attach'), function() {
            console.log('Button clicked: Generate PDF and Attach');
            show_pdf_options_dialog(frm);
        });
    }
});