diff --git a/frappe/core/page/desktop/desktop.py b/frappe/core/page/desktop/desktop.py
index f83e37b98b..2824fb7e9b 100644
--- a/frappe/core/page/desktop/desktop.py
+++ b/frappe/core/page/desktop/desktop.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
+import functools
import frappe
@frappe.whitelist()
@@ -20,4 +21,4 @@ def get_help_messages():
for fn in frappe.get_hooks('get_help_messages'):
messages += frappe.get_attr(fn)()
- return sorted(messages, lambda a, b: cmp(a.get('count'), b.get('count')))
+ return sorted(messages, key = functools.cmp_to_key(lambda a, b: cmp(a.get('count'), b.get('count'))))
diff --git a/frappe/desk/page/activity/activity.js b/frappe/desk/page/activity/activity.js
index 1e623a8f58..86915dcf60 100644
--- a/frappe/desk/page/activity/activity.js
+++ b/frappe/desk/page/activity/activity.js
@@ -166,6 +166,10 @@ frappe.activity.render_heatmap = function(page) {
}
frappe.views.Activity = class Activity extends frappe.views.BaseList {
+ constructor(opts) {
+ super(opts);
+ this.show();
+ }
setup_defaults() {
super.setup_defaults();
diff --git a/frappe/hooks.py b/frappe/hooks.py
index ea57229c15..1d02b64ac2 100755
--- a/frappe/hooks.py
+++ b/frappe/hooks.py
@@ -11,7 +11,7 @@ app_color = "orange"
source_link = "https://github.com/frappe/frappe"
app_license = "MIT"
-develop_version = '10.x.x-develop'
+develop_version = '11.x.x-develop'
app_email = "info@frappe.io"
diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js
index e42e9df15c..c4f6e832d0 100644
--- a/frappe/public/js/frappe/form/grid.js
+++ b/frappe/public/js/frappe/form/grid.js
@@ -234,7 +234,7 @@ frappe.ui.form.Grid = Class.extend({
this.wrapper.find(".grid-footer").toggle(true);
// show, hide buttons to add rows
- if(this.cannot_add_rows) {
+ if(this.cannot_add_rows || (this.df && this.df.cannot_add_rows)) {
// add 'hide' to buttons
this.wrapper.find(".grid-add-row, .grid-add-multiple-rows")
.addClass('hide');
diff --git a/frappe/public/js/frappe/list/base_list.js b/frappe/public/js/frappe/list/base_list.js
index d17b17b012..ebce0101c5 100644
--- a/frappe/public/js/frappe/list/base_list.js
+++ b/frappe/public/js/frappe/list/base_list.js
@@ -3,7 +3,6 @@ frappe.provide('frappe.views');
frappe.views.BaseList = class BaseList {
constructor(opts) {
Object.assign(this, opts);
- this.show();
}
show() {
@@ -152,6 +151,7 @@ frappe.views.BaseList = class BaseList {
}
set_menu_items() {
+ this.page.clear_menu();
const $secondary_action = this.page.set_secondary_action(
this.secondary_action.label,
this.secondary_action.action,
diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js
index 926d089907..fc2ea761df 100644
--- a/frappe/public/js/frappe/list/list_view.js
+++ b/frappe/public/js/frappe/list/list_view.js
@@ -15,6 +15,11 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
return false;
}
+ constructor(opts) {
+ super(opts);
+ this.show();
+ }
+
show() {
this.init().then(() => {
if (frappe.route_options) {
diff --git a/frappe/public/js/frappe/model/model.js b/frappe/public/js/frappe/model/model.js
index 0ca8cce86e..039a053fbe 100644
--- a/frappe/public/js/frappe/model/model.js
+++ b/frappe/public/js/frappe/model/model.js
@@ -154,23 +154,27 @@ $.extend(frappe.model, {
},
with_doc: function(doctype, name, callback) {
- if(!name) name = doctype; // single type
- if(locals[doctype] && locals[doctype][name] && frappe.model.get_docinfo(doctype, name)) {
- callback(name);
- } else {
- return frappe.call({
- method: 'frappe.desk.form.load.getdoc',
- type: "GET",
- args: {
- doctype: doctype,
- name: name
- },
- freeze: true,
- callback: function(r) {
- callback && callback(name, r);
- }
- });
- }
+ return new Promise(resolve => {
+ if(!name) name = doctype; // single type
+ if(locals[doctype] && locals[doctype][name] && frappe.model.get_docinfo(doctype, name)) {
+ callback && callback(name);
+ resolve(frappe.get_doc(doctype, name));
+ } else {
+ return frappe.call({
+ method: 'frappe.desk.form.load.getdoc',
+ type: "GET",
+ args: {
+ doctype: doctype,
+ name: name
+ },
+ freeze: true,
+ callback: function(r) {
+ callback && callback(name, r);
+ resolve(frappe.get_doc(doctype, name));
+ }
+ });
+ }
+ });
},
get_docinfo: function(doctype, name) {
diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js
index 91f73bf6ea..1fc5051d8c 100644
--- a/frappe/public/js/frappe/views/reports/query_report.js
+++ b/frappe/public/js/frappe/views/reports/query_report.js
@@ -19,25 +19,24 @@ frappe.standard_pages["query-report"] = function() {
});
$(wrapper).bind("show", function() {
- frappe.query_report.init();
+ frappe.query_report.show();
});
};
frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
show() {
-
+ this.init().then(() => this.load());
}
init() {
- if (this.init_promise && frappe.get_route()[1] === this.report_name) {
+ if (this.init_promise) {
return this.init_promise;
}
let tasks = [
this.setup_defaults,
this.setup_page,
- this.setup_report_wrapper,
- this.setup_report
+ this.setup_report_wrapper
].map(fn => fn.bind(this));
this.init_promise = frappe.run_serially(tasks);
@@ -47,17 +46,6 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
setup_defaults() {
this.route = frappe.get_route();
this.page_name = frappe.get_route_str();
- this.report_name = this.route[1];
- this.page_title = __(this.report_name);
- this.user_settings = frappe.get_user_settings(this.report_name);
-
- this.start = 0;
- this.page_length = 20;
- this.data = [];
-
- this.fields = [];
- this.filters = [];
- this.order_by = 'modified desc';
// Setup buttons
this.primary_action = null;
@@ -66,43 +54,58 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
action: () => this.refresh()
};
+ // throttle refresh for 300ms
+ this.refresh = frappe.utils.throttle(this.refresh, 300);
+
+ this.menu_items = [];
+ }
+
+ load() {
+ this.toggle_freeze(true);
+ if (this.report_name !== frappe.get_route()[1]) {
+ // different report
+ this.load_report();
+ } else {
+ // same report
+ this.refresh_report();
+ }
+ }
+
+ load_report() {
+ this.route = frappe.get_route();
+ this.page_name = frappe.get_route_str();
+ this.report_name = this.route[1];
+ this.page_title = __(this.report_name);
+ this.user_settings = frappe.get_user_settings(this.report_name);
this.menu_items = this.get_menu_items();
+ this.datatable = null;
- // throttle refresh for 500ms
- this.refresh = frappe.utils.throttle(this.refresh, 500);
+ frappe.run_serially([
+ () => this.get_report_doc(),
+ () => this.get_report_settings(),
+ () => this.report_settings.onload && this.report_settings.onload(this),
+ () => this.setup_page_head(),
+ () => this.refresh_report()
+ ]);
+ }
- // Report Doc
- return frappe.db.get_doc('Report', this.report_name)
+ refresh_report() {
+ return frappe.run_serially([
+ () => this.setup_filters(),
+ () => this.set_route_filters(),
+ () => this.refresh()
+ ]);
+ }
+
+ get_report_doc() {
+ return frappe.model.with_doc('Report', this.report_name)
.then(doc => {
this.report_doc = doc;
})
- // Ref DocType
- .then(() => {
- if (this.report_doc.ref_doctype) {
- return frappe.model.with_doctype(this.report_doc.ref_doctype);
- }
- return Promise.resolve();
- });
+ .then(() => frappe.model.with_doc('DocType', this.report_doc.ref_doctype));
}
- setup_report_wrapper() {
- if (this.$report) return;
- this.$report = $('
').appendTo(this.page.main);
- }
-
- setup_page_head() {
- super.setup_page_head();
- this.page.set_title_sub(`
`);
- }
-
- setup_report() {
- this.$report.empty();
- this.datatable = null;
- return this.load_report_script()
- .then(() => this.load_report());
- }
-
- load_report_script() {
+ get_report_settings() {
if (frappe.query_reports[this.report_name]) {
this.report_settings = frappe.query_reports[this.report_name];
return this._load_script;
@@ -126,20 +129,6 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
return this._load_script;
}
- load_report() {
- this.page.clear_inner_toolbar();
- this.setup_filters();
- // this.toggle_expand_collapse_buttons(false);
- // this.is_tree_report = false;
-
- if (this.report_settings.onload) {
- frappe.run_serially([
- () => this.report_settings.onload(this),
- () => this.refresh()
- ]);
- }
- }
-
setup_filters() {
this.clear_filters();
const { filters = [] } = this.report_settings;
@@ -181,8 +170,6 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
// as they can be used in
// setting/triggering the filters
this.set_filters_by_name();
-
- return this.set_route_filters();
}
set_filters_by_name() {
@@ -217,27 +204,23 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
refresh() {
const filters = this.get_filter_values(true);
-
- if (this.report_ajax) {
- // abort previous request
- this.report_ajax.abort();
- }
-
- this.report_ajax = frappe.call({
+ return new Promise(resolve => frappe.call({
method: "frappe.desk.query_report.run",
type: "GET",
args: {
report_name: this.report_name,
filters: filters
- }
- }).then(r => {
- this.report_ajax = undefined;
+ },
+ callback: resolve
+ })).then(r => {
this.render_report(r.message);
});
}
render_report(data) {
+ this.toggle_freeze(false);
this._data = data.result;
+ this._columns = data.columns;
if (this.datatable) {
this.datatable.refresh(data.result);
return;
@@ -339,11 +322,111 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
}
set_breadcrumbs() {
- if (!this.report_doc.ref_doctype) return;
+ if (!this.report_doc || !this.report_doc.ref_doctype) return;
const ref_doctype = frappe.get_meta(this.report_doc.ref_doctype);
frappe.breadcrumbs.add(ref_doctype.module);
}
+ print_report(print_settings) {
+ frappe.render_grid({
+ template: this.html_format || null,
+ title: __(this.report_name),
+ print_settings: print_settings,
+ filters: this.get_filter_values(),
+ data: this.get_data_for_print(),
+ columns: this.get_columns_for_print(),
+ report: this
+ });
+ }
+
+ pdf_report(print_settings) {
+ const base_url = frappe.urllib.get_base_url();
+ const print_css = frappe.boot.print_css;
+ const landscape = print_settings.orientation == "Landscape";
+ const columns = this.columns;
+
+ let html;
+ if (this.html_format) {
+ const content = frappe.render(this.html_format, {
+ data: this.get_data_for_print(),
+ filters: this.get_filter_values(),
+ report: this,
+ });
+
+ //Render Report in HTML
+ html = frappe.render_template("print_template", {
+ title:__(this.report_name),
+ content: content,
+ base_url: base_url,
+ print_css: print_css,
+ print_settings: print_settings,
+ landscape: landscape,
+ columns: columns
+ });
+ } else {
+ const content = frappe.render_template("print_grid", {
+ title: __(this.report_name),
+ data: this.get_data_for_print(),
+ columns: columns
+ })
+
+ //Render Report in HTML
+ html = frappe.render_template("print_template", {
+ content: content,
+ title: __(this.report_name),
+ base_url: base_url,
+ print_css: print_css,
+ print_settings: print_settings,
+ landscape: landscape,
+ columns: columns
+ });
+ }
+
+ frappe.render_pdf(html, print_settings);
+ }
+
+ export_report() {
+ if (this.export_dialog) {
+ this.export_dialog.clear();
+ this.export_dialog.show();
+ return;
+ }
+
+ this.export_dialog = frappe.prompt({
+ label: __('Select File Format'),
+ fieldname: 'file_format',
+ fieldtype: 'Select',
+ options: ['Excel', 'CSV'],
+ default: 'Excel',
+ reqd: 1
+ }, ({ file_format }) => {
+ if (file_format === 'CSV') {
+ frappe.tools.downloadify(this.get_data_for_print(), null, this.report_name);
+ } else {
+ const filters = this.get_filter_values(true);
+
+ const args = {
+ cmd: 'frappe.desk.query_report.export_query',
+ report_name: this.report_name,
+ file_format_type: file_format,
+ filters: filters,
+ visible_idx: this.datatable.datamanager.getFilteredRowIndices(),
+ };
+
+ open_url_post(frappe.request.url, args);
+ }
+ }, __("Export Report: "+ this.report_name), __("Download"));
+ }
+
+ get_data_for_print() {
+ const indices = this.datatable.datamanager.getFilteredRowIndices();
+ return indices.map(i => this._data[i]);
+ }
+
+ get_columns_for_print() {
+ return this._columns || [];
+ }
+
get_menu_items() {
return [
{
@@ -360,24 +443,30 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
{
label: __('Print'),
action: () => {
- frappe.ui.get_print_settings(false, function(print_settings) {
- this.print_report(print_settings);
- }, this.report_doc.letter_head);
+ frappe.ui.get_print_settings(
+ false,
+ print_settings => this.print_report(print_settings),
+ this.report_doc.letter_head
+ );
},
+ condition: () => frappe.model.can_print(this.report_doc.ref_doctype),
standard: true
},
{
label: __('PDF'),
action: () => {
- frappe.ui.get_print_settings(false, function(print_settings) {
- this.pdf_report(print_settings);
- }, this.report_doc.letter_head);
+ frappe.ui.get_print_settings(
+ false,
+ print_settings => this.pdf_report(print_settings),
+ this.report_doc.letter_head
+ );
},
+ condition: () => frappe.model.can_print(this.report_doc.ref_doctype),
standard: true
},
{
label: __('Export'),
- action: () => this.make_export(),
+ action: () => this.export_report(),
standard: true
},
{
@@ -401,4 +490,31 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
},
];
}
+
+ setup_page_head() {
+ super.setup_page_head();
+ this.page.set_title_sub(`
`);
+ }
+
+ setup_report_wrapper() {
+ if (this.$report) return;
+ this.$report = $('
').appendTo(this.page.main);
+ this.$freeze =
+ $(`
`).hide().appendTo(this.page.main);
+ }
+
+ toggle_freeze(flag) {
+ this.$freeze.toggle(flag);
+ this.$report.toggle(!flag);
+ }
+
+ get data() {
+ return this._data;
+ }
+
+ get columns() {
+ return this._columns;
+ }
};
\ No newline at end of file
diff --git a/frappe/public/js/lib/microtemplate.js b/frappe/public/js/lib/microtemplate.js
index 6aa85c1947..9d75ee33fa 100644
--- a/frappe/public/js/lib/microtemplate.js
+++ b/frappe/public/js/lib/microtemplate.js
@@ -104,7 +104,7 @@ frappe.render_grid = function(opts) {
// render content
if(!opts.content) {
- opts.content = frappe.render_template("print_grid", opts);
+ opts.content = frappe.render_template(opts.template || "print_grid", opts);
}
// render HTML wrapper page
@@ -135,3 +135,31 @@ frappe.render_tree = function(opts) {
w.document.write(tree);
w.document.close();
}
+frappe.render_pdf = function(html, opts = {}) {
+ //Create a form to place the HTML content
+ var formData = new FormData();
+
+ //Push the HTML content into an element
+ formData.append("html", html);
+ if (opts.orientation) {
+ formData.append("orientation", opts.orientation);
+ }
+ var blob = new Blob([], { type: "text/xml"});
+ formData.append("blob", blob);
+
+ var xhr = new XMLHttpRequest();
+ xhr.open("POST", '/api/method/frappe.utils.print_format.report_to_pdf');
+ xhr.setRequestHeader("X-Frappe-CSRF-Token", frappe.csrf_token);
+ xhr.responseType = "arraybuffer";
+
+ xhr.onload = function(success) {
+ if (this.status === 200) {
+ var blob = new Blob([success.currentTarget.response], {type: "application/pdf"});
+ var objectUrl = URL.createObjectURL(blob);
+
+ //Open report in a new window
+ window.open(objectUrl);
+ }
+ };
+ xhr.send(formData);
+}
\ No newline at end of file
diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py
index 57c4051be6..e15e4d4e23 100644
--- a/frappe/utils/pdf.py
+++ b/frappe/utils/pdf.py
@@ -128,7 +128,7 @@ def prepare_header_footer(soup):
# create temp file
fname = os.path.join("/tmp", "frappe-pdf-{0}.html".format(frappe.generate_hash()))
- with open(fname, "w") as f:
+ with open(fname, "wb") as f:
f.write(html.encode("utf-8"))
# {"header-html": "/tmp/frappe-pdf-random.html"}
diff --git a/frappe/website/utils.py b/frappe/website/utils.py
index 9388116c6a..b784a0ea3d 100644
--- a/frappe/website/utils.py
+++ b/frappe/website/utils.py
@@ -2,6 +2,7 @@
# MIT License. See license.txt
from __future__ import unicode_literals
+import functools
import frappe, re, os
from six import iteritems
@@ -257,8 +258,8 @@ def get_full_index(route=None, app=None):
added.append(child_route)
# add remaining pages not in index.txt
- _children = sorted(children, lambda a, b: cmp(
- os.path.basename(a.route), os.path.basename(b.route)))
+ _children = sorted(children, key = functools.cmp_to_key(lambda a, b: cmp(
+ os.path.basename(a.route), os.path.basename(b.route))))
for child_route in _children:
if child_route not in new_children:
diff --git a/yarn.lock b/yarn.lock
index 9f9d8c5f7f..a58c5defd4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -612,7 +612,7 @@ forwarded@~0.1.2:
frappe-datatable@frappe/datatable:
version "0.0.2"
- resolved "https://codeload.github.com/frappe/datatable/tar.gz/5b8be4c5b09a243ed8580cb9667d7658ca249f02"
+ resolved "https://codeload.github.com/frappe/datatable/tar.gz/99701f2477b3fb8180ccafaf2c2746886b13ba53"
dependencies:
clusterize.js "^0.18.0"
lodash "^4.17.5"