diff --git a/report_print/hooks.py b/report_print/hooks.py index 9966ed4..f023574 100644 --- a/report_print/hooks.py +++ b/report_print/hooks.py @@ -159,9 +159,9 @@ # Overriding Methods # ------------------------------ # -# override_whitelisted_methods = { -# "frappe.desk.doctype.event.event.get_events": "report_print.event.get_events" -# } +override_whitelisted_methods = { + "frappe.desk.query_report.get_script": "report_print.overrides.report.get_script", +} # # each overriding function accepts a `data` argument; # generated from the base implementation of the doctype dashboard, diff --git a/report_print/overrides/report.py b/report_print/overrides/report.py new file mode 100644 index 0000000..c0db0fd --- /dev/null +++ b/report_print/overrides/report.py @@ -0,0 +1,45 @@ + +import frappe +import os +from frappe.desk.query_report import get_report_doc +from frappe.modules import get_module_path, scrub +from frappe.model.utils import render_include + + +@frappe.whitelist() +def get_script(report_name): + report = get_report_doc(report_name) + module = report.module or frappe.db.get_value("DocType", report.ref_doctype, "module") + + is_custom_module = frappe.get_cached_value("Module Def", module, "custom") + + # custom modules are virtual modules those exists in DB but not in disk. + module_path = "" if is_custom_module else get_module_path(module) + report_folder = module_path and os.path.join(module_path, "report", scrub(report.name)) + script_path = report_folder and os.path.join(report_folder, scrub(report.name) + ".js") + + script = None + if os.path.exists(script_path): + with open(script_path) as f: + script = f.read() + script += f"\n\n//# sourceURL={scrub(report.name)}.js" + + html_format = get_html_format() + + if not script and report.javascript: + script = report.javascript + script += f"\n\n//# sourceURL={scrub(report.name)}__custom" + + if not script: + script = "frappe.query_reports['%s']={}" % report_name + + return { + "script": render_include(script), + "html_format": html_format, + "execution_time": 0, + "filters": report.filters, + "custom_report_name": report.name if report.get("is_custom_report") else None, + } + +def get_html_format(): + return frappe.db.get_value("Report Print Format", "", "html") \ No newline at end of file diff --git a/report_print/report_print/doctype/__init__.py b/report_print/report_print/doctype/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/report_print/report_print/doctype/report_print_format/__init__.py b/report_print/report_print/doctype/report_print_format/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/report_print/report_print/doctype/report_print_format/report_print_format.js b/report_print/report_print/doctype/report_print_format/report_print_format.js new file mode 100644 index 0000000..d2ac360 --- /dev/null +++ b/report_print/report_print/doctype/report_print_format/report_print_format.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, Code Venturers and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Report Print Format', { + // refresh: function(frm) { + + // } +}); diff --git a/report_print/report_print/doctype/report_print_format/report_print_format.json b/report_print/report_print/doctype/report_print_format/report_print_format.json new file mode 100644 index 0000000..8a0770e --- /dev/null +++ b/report_print/report_print/doctype/report_print_format/report_print_format.json @@ -0,0 +1,286 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2024-08-29 01:52:40.846934", + "default_view": "List", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "report", + "module", + "default_print_language", + "column_break_3", + "standard", + "custom_format", + "disabled", + "section_break_6", + "print_format_type", + "raw_printing", + "html", + "raw_commands", + "section_break_9", + "margin_top", + "margin_bottom", + "margin_left", + "margin_right", + "align_labels_right", + "show_section_headings", + "line_breaks", + "absolute_value", + "column_break_11", + "font_size", + "font", + "page_number", + "css_section", + "css", + "custom_html_help", + "section_break_13", + "print_format_help", + "format_data", + "print_format_builder", + "print_format_builder_beta" + ], + "fields": [ + { + "fieldname": "report", + "fieldtype": "Link", + "in_filter": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Report", + "options": "Report", + "reqd": 1 + }, + { + "fieldname": "module", + "fieldtype": "Link", + "label": "Module", + "options": "Module Def" + }, + { + "fieldname": "default_print_language", + "fieldtype": "Link", + "label": "Default Print Language", + "options": "Language" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "default": "No", + "fieldname": "standard", + "fieldtype": "Select", + "in_filter": 1, + "label": "Standard", + "no_copy": 1, + "oldfieldname": "standard", + "oldfieldtype": "Select", + "options": "No\nYes", + "reqd": 1, + "search_index": 1 + }, + { + "default": "0", + "fieldname": "custom_format", + "fieldtype": "Check", + "label": "Custom Format" + }, + { + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, + { + "depends_on": "custom_format", + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "default": "Jinja", + "depends_on": "custom_format", + "fieldname": "print_format_type", + "fieldtype": "Select", + "label": "Print Format Type", + "options": "Jinja\nJS" + }, + { + "default": "0", + "fieldname": "raw_printing", + "fieldtype": "Check", + "label": "Raw Printing" + }, + { + "depends_on": "eval:!doc.raw_printing", + "fieldname": "html", + "fieldtype": "Code", + "label": "HTML", + "oldfieldname": "html", + "oldfieldtype": "Text Editor", + "options": "Jinja" + }, + { + "depends_on": "raw_printing", + "description": "Any string-based printer languages can be used. Writing raw commands requires knowledge of the printer's native language provided by the printer manufacturer. Please refer to the developer manual provided by the printer manufacturer on how to write their native commands. These commands are rendered on the server side using the Jinja Templating Language.", + "fieldname": "raw_commands", + "fieldtype": "Code", + "label": "Raw Commands", + "options": "Jinja" + }, + { + "depends_on": "eval:!doc.custom_format", + "fieldname": "section_break_9", + "fieldtype": "Section Break", + "label": "Style Settings" + }, + { + "default": "15", + "fieldname": "margin_top", + "fieldtype": "Float", + "label": "Margin Top" + }, + { + "default": "15", + "fieldname": "margin_bottom", + "fieldtype": "Float", + "label": "Margin Bottom" + }, + { + "default": "15", + "fieldname": "margin_left", + "fieldtype": "Float", + "label": "Margin Left" + }, + { + "default": "15", + "fieldname": "margin_right", + "fieldtype": "Float", + "label": "Margin Right" + }, + { + "default": "0", + "fieldname": "align_labels_right", + "fieldtype": "Check", + "label": "Align Labels to the Right" + }, + { + "default": "0", + "fieldname": "show_section_headings", + "fieldtype": "Check", + "label": "Show Section Headings" + }, + { + "default": "0", + "fieldname": "line_breaks", + "fieldtype": "Check", + "label": "Show Line Breaks after Sections" + }, + { + "default": "0", + "depends_on": "doc_type", + "description": "If checked, negative numeric values of Currency, Quantity or Count would be shown as positive", + "fieldname": "absolute_value", + "fieldtype": "Check", + "label": "Show Absolute Values" + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "default": "14", + "fieldname": "font_size", + "fieldtype": "Int", + "label": "Font Size" + }, + { + "depends_on": "eval:!doc.custom_format", + "fieldname": "font", + "fieldtype": "Data", + "label": "Google Font" + }, + { + "default": "Hide", + "fieldname": "page_number", + "fieldtype": "Select", + "label": "Page Number", + "options": "Hide\nTop Left\nTop Center\nTop Right\nBottom Left\nBottom Center\nBottom Right" + }, + { + "depends_on": "eval:!doc.raw_printing", + "fieldname": "css_section", + "fieldtype": "Section Break" + }, + { + "fieldname": "css", + "fieldtype": "Code", + "label": "Custom CSS", + "options": "CSS" + }, + { + "fieldname": "custom_html_help", + "fieldtype": "HTML", + "label": "Custom HTML Help", + "options": "

Custom CSS Help

\n\n

Notes:

\n\n
    \n
  1. All field groups (label + value) are set attributes data-fieldtype and data-fieldname
  2. \n
  3. All values are given class value
  4. \n
  5. All Section Breaks are given class section-break
  6. \n
  7. All Column Breaks are given class column-break
  8. \n
\n\n

Examples

\n\n

1. Left align integers

\n\n
[data-fieldtype=\"Int\"] .value { text-align: left; }
\n\n

1. Add border to sections except the last section

\n\n
.section-break { padding: 30px 0px; border-bottom: 1px solid #eee; }\n.section-break:last-child { padding-bottom: 0px; border-bottom: 0px;  }
\n" + }, + { + "depends_on": "custom_format", + "fieldname": "section_break_13", + "fieldtype": "Section Break" + }, + { + "depends_on": "custom_format", + "fieldname": "print_format_help", + "fieldtype": "HTML", + "label": "Print Format Help", + "options": "

Print Format Help

\n
\n

Introduction

\n

Print Formats are rendered on the server side using the Jinja Templating Language. All forms have access to the doc object which contains information about the document that is being formatted. You can also access common utilities via the frappe module.

\n

For styling, the Boostrap CSS framework is provided and you can enjoy the full range of classes.

\n
\n

References

\n
    \n\t
  1. Jinja Templating Language
  2. \n\t
  3. Bootstrap CSS Framework
  4. \n
\n
\n

Example

\n
<h3>{{ doc.select_print_heading or \"Invoice\" }}</h3>\n<div class=\"row\">\n\t<div class=\"col-md-3 text-right\">Customer Name</div>\n\t<div class=\"col-md-9\">{{ doc.customer_name }}</div>\n</div>\n<div class=\"row\">\n\t<div class=\"col-md-3 text-right\">Date</div>\n\t<div class=\"col-md-9\">{{ doc.get_formatted(\"invoice_date\") }}</div>\n</div>\n<table class=\"table table-bordered\">\n\t<tbody>\n\t\t<tr>\n\t\t\t<th>Sr</th>\n\t\t\t<th>Item Name</th>\n\t\t\t<th>Description</th>\n\t\t\t<th class=\"text-right\">Qty</th>\n\t\t\t<th class=\"text-right\">Rate</th>\n\t\t\t<th class=\"text-right\">Amount</th>\n\t\t</tr>\n\t\t{%- for row in doc.items -%}\n\t\t<tr>\n\t\t\t<td style=\"width: 3%;\">{{ row.idx }}</td>\n\t\t\t<td style=\"width: 20%;\">\n\t\t\t\t{{ row.item_name }}\n\t\t\t\t{% if row.item_code != row.item_name -%}\n\t\t\t\t<br>Item Code: {{ row.item_code}}\n\t\t\t\t{%- endif %}\n\t\t\t</td>\n\t\t\t<td style=\"width: 37%;\">\n\t\t\t\t<div style=\"border: 0px;\">{{ row.description }}</div></td>\n\t\t\t<td style=\"width: 10%; text-align: right;\">{{ row.qty }} {{ row.uom or row.stock_uom }}</td>\n\t\t\t<td style=\"width: 15%; text-align: right;\">{{\n\t\t\t\trow.get_formatted(\"rate\", doc) }}</td>\n\t\t\t<td style=\"width: 15%; text-align: right;\">{{\n\t\t\t\trow.get_formatted(\"amount\", doc) }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table>
\n
\n

Common Functions

\n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n
doc.get_formatted(\"[fieldname]\", [parent_doc])Get document value formatted as Date, Currency, etc. Pass parent doc for currency type fields.
frappe.db.get_value(\"[doctype]\", \"[name]\", \"fieldname\")Get value from another document.
\n" + }, + { + "fieldname": "format_data", + "fieldtype": "Code", + "hidden": 1, + "label": "Format Data" + }, + { + "default": "0", + "fieldname": "print_format_builder", + "fieldtype": "Check", + "hidden": 1, + "label": "Print Format Builder" + }, + { + "default": "0", + "fieldname": "print_format_builder_beta", + "fieldtype": "Check", + "label": "Print Format Builder Beta" + } + ], + "icon": "fa fa-print", + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-08-29 01:52:40.846934", + "modified_by": "Administrator", + "module": "Report Print", + "name": "Report Print Format", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/report_print/report_print/doctype/report_print_format/report_print_format.py b/report_print/report_print/doctype/report_print_format/report_print_format.py new file mode 100644 index 0000000..f3d89e2 --- /dev/null +++ b/report_print/report_print/doctype/report_print_format/report_print_format.py @@ -0,0 +1,8 @@ +# Copyright (c) 2024, Code Venturers and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class ReportPrintFormat(Document): + pass diff --git a/report_print/report_print/doctype/report_print_format/test_report_print_format.py b/report_print/report_print/doctype/report_print_format/test_report_print_format.py new file mode 100644 index 0000000..f185715 --- /dev/null +++ b/report_print/report_print/doctype/report_print_format/test_report_print_format.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, Code Venturers and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestReportPrintFormat(FrappeTestCase): + pass