Create a PDF from selected entries in List View

Hello,
I’m trying to accomplish this:

  • In List View select some records with the selection checkbox
  • Call some functionality that displays a PDF containing information for the selected records.

I managed to pass the names of the selected records as parameters to a server method, generate html and transform the html to a PDF.
Unfortunately I don’t know how to handle the the binary data of the PDF in my Client Script.

I have done this:

Here is the Client Script:

frappe.listview_settings['Schluessel'] = {
    hide_name_column: true,
    onload(listview) { 
        listview.page.add_inner_button(__("GenPdf"), () => callGenPdf());
    }
}

function callGenPdf() {
    let checked_items = cur_list.get_checked_items(true);
    frappe.call({
            method: "my_app.my_app.doctype.schluessel.api.GenPdf",
            args: {
              "checked_items" : checked_items
            },
            callback: function(r) {
                // How to handle the returned binary data?
                msgprint(__("Simpler Returnwert: {0}", [r.message]));
            }
        });
}

This is the serverside code:

@frappe.whitelist()
def GenPdf(**args):
    checked_items = args.get('checked_items')

    # prepare checked_items for split(). Does anyone knows a better way?
    checked_items = checked_items.replace("[", "")
    checked_items = checked_items.replace("]", "")
    checked_items = checked_items.replace("\"", "")

    doc = MyDoc(checked_items.split(","))
    template = "{%- for row in doc.checked_items -%} {{row}} <br>{%- endfor -%}"
    html = frappe.render_template(template, {"doc" : doc})
    print(html)

    return get_pdf(html) # How to handle this in Client Script?

class MyDoc:
    def __init__(self, checked_items) -> None:
        self.checked_items = checked_items

And that’s the result:

Currently I have no idea what to do with the returned value. How can I persuade the browser to output it as a PDF?

1 Like

why not use the default print functionality?

maybe with some adaptions of the print format it will work out ok?
maybe downside is that you probably get one page per Schlüssel…

other idea:
make a custom doctype Schlüssellist. With an table like items in a invoice. Then you could print that again using the already available functionality…

2 Likes

Pass the names of the selected records to the this method “/api/method/frappe.utils.print_format.download_multi_pdf”

Here is an example I did to get multiple payment entries to print in a specific print format.

function pop_bank_invoiceprint_dialog(recent_record) {
const print_format = ‘Invoice List’;
var base_url = frappe.urllib.get_base_url();
let json_string = JSON.stringify(recent_record);
const url = window.open(base_url + ‘/api/method/frappe.utils.print_format.download_multi_pdf?’ +
‘doctype=’ + encodeURIComponent(‘Payment Entry’) +
‘&name=’ + encodeURIComponent(json_string) +
‘&format=’ + encodeURIComponent(print_format) +
‘&no_letterhead=’ + ‘1’);
if (!url) {
frappe.msgprint(__(‘Please enable pop-ups’));
return;
}
};

Basically returns a URL which will combine pdfs of payments entries (recent_records)

Hope this helps

Thank you all for your input.
Some snooping around revealed some unknown functions. This Javascript-code uses the template rendering and pdf-creation, no server side code required:

var doc = {};
doc.test = "Mr. Test"
let result = frappe.render_template("<h1>Hello {{ doc.test }}</h1>", {"doc" : doc})
frappe.render_pdf(result, {orientation:"Portrait"}); // orientation takes "Portrait" or "Landscape"

This opens a popup with a PDF:

Used frappe version 13.13.0, don’t know if it’s available in earlier versions.

I can build my code upon this. I can create my own HTML in the client script from the selected records and will benefit from render_template(). Then I use render_pdf() to display it as a PDF.

1 Like