feat: add Consolidated Print action to list view

Wires the new /consolidated_printview backend route into the List View
bulk-actions menu so users can print selected records as one continuous
layout without page breaks between them.

- BulkOperations.consolidated_print() validates docs against the same
  draft/cancelled print-settings rules as the existing print(), enforces
  a 100-document limit, and opens a dialog to pick Letter Head and Print
  Format before launching the consolidated print window
- bulk_consolidated_printing() action added to list_view.js and
  registered immediately after the existing "Print" entry, gated by the
  same frappe.model.can_print() permission check

Made-with: Cursor
This commit is contained in:
dataCenter430 2026-03-02 21:45:14 +01:00
parent 101983c7af
commit 59f12a1ac4
2 changed files with 103 additions and 0 deletions

View file

@ -166,6 +166,100 @@ export default class BulkOperations {
dialog.show();
}
consolidated_print(docs) {
const print_settings = frappe.model.get_doc(":Print Settings", "Print Settings");
const allow_print_for_draft = cint(print_settings.allow_print_for_draft);
const is_submittable = frappe.model.is_submittable(this.doctype);
const allow_print_for_cancelled = cint(print_settings.allow_print_for_cancelled);
const letterheads = this.get_letterhead_options();
const MAX_CONSOLIDATED_LIMIT = 100;
const valid_docs = docs
.filter((doc) => {
return (
!is_submittable ||
doc.docstatus === 1 ||
(allow_print_for_cancelled && doc.docstatus == 2) ||
(allow_print_for_draft && doc.docstatus == 0) ||
frappe.user.has_role("Administrator")
);
})
.map((doc) => doc.name);
const invalid_docs = docs.filter((doc) => !valid_docs.includes(doc.name));
if (invalid_docs.length > 0) {
frappe.msgprint(__("You selected Draft or Cancelled documents"));
return;
}
if (valid_docs.length === 0) {
frappe.msgprint(__("Select atleast 1 record for printing"));
return;
}
if (valid_docs.length > MAX_CONSOLIDATED_LIMIT) {
frappe.msgprint(
__("You can only consolidate up to {0} documents at a time", [MAX_CONSOLIDATED_LIMIT])
);
return;
}
const dialog = new frappe.ui.Dialog({
title: __("Consolidated Print"),
fields: [
{
fieldtype: "HTML",
options: `<p class="text-muted small">${__("All selected documents will be rendered in a single continuous layout without page breaks between them.")}</p>`,
},
{
fieldtype: "Select",
label: __("Letter Head"),
fieldname: "letter_sel",
options: letterheads,
default: letterheads[0],
},
{
fieldtype: "Select",
label: __("Print Format"),
fieldname: "print_sel",
options: frappe.meta.get_print_formats(this.doctype),
default: frappe.get_meta(this.doctype).default_print_format,
},
],
});
dialog.set_primary_action(__("Print"), (args) => {
if (!args) return;
const default_print_format = frappe.get_meta(this.doctype).default_print_format;
const with_letterhead = args.letter_sel == __("No Letterhead") ? 0 : 1;
const print_format = args.print_sel ? args.print_sel : default_print_format;
const names_json = JSON.stringify(valid_docs);
const letterhead = args.letter_sel;
const w = window.open(
"/consolidated_printview?" +
"doctype=" +
encodeURIComponent(this.doctype) +
"&names=" +
encodeURIComponent(names_json) +
"&format=" +
encodeURIComponent(print_format) +
"&no_letterhead=" +
(with_letterhead ? "0" : "1") +
"&letterhead=" +
encodeURIComponent(letterhead) +
"&trigger_print=1"
);
if (!w) {
frappe.msgprint(__("Please enable pop-ups"));
}
dialog.hide();
});
dialog.show();
}
get_letterhead_options() {
const letterhead_options = [__("No Letterhead")];
frappe.call({

View file

@ -2264,6 +2264,14 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
};
};
const bulk_consolidated_printing = () => {
return {
label: __("Consolidated Print", null, "Button in list view actions menu"),
action: () => bulk_operations.consolidated_print(this.get_checked_items()),
standard: true,
};
};
const bulk_delete = () => {
return {
label: __("Delete", null, "Button in list view actions menu"),
@ -2552,6 +2560,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
// bulk printing
if (frappe.model.can_print(doctype)) {
actions_menu_items.push(bulk_printing());
actions_menu_items.push(bulk_consolidated_printing());
}
// bulk submit