Fixed merge conflict

This commit is contained in:
Nabin Hait 2018-07-31 13:15:15 +05:30
commit 854b068ee9
10 changed files with 1531 additions and 8 deletions

View file

@ -17,7 +17,7 @@ from faker import Faker
from .exceptions import *
from .utils.jinja import (get_jenv, get_template, render_template, get_email_from_template, get_jloader)
__version__ = '10.1.42'
__version__ = '10.1.43'
__title__ = "Frappe Framework"
local = Local()

View file

@ -270,6 +270,8 @@ class TestUser(unittest.TestCase):
def test_comment_mentions(self):
user_name = "@test.comment@example.com"
self.assertEqual(extract_mentions(user_name)[0], "test.comment@example.com")
user_name = "@test.comment@test-example.com"
self.assertEqual(extract_mentions(user_name)[0], "test.comment@test-example.com")
user_name = "Testing comment, @test-user please check."
self.assertEqual(extract_mentions(user_name)[0], "test-user")
user_name = "Testing comment, @test.user@example.com please check."

View file

@ -929,6 +929,7 @@ def notify_admin_access_to_system_manager(login_manager=None):
def extract_mentions(txt):
"""Find all instances of @name in the string.
The mentions will be separated by non-word characters or may appear at the start of the string"""
txt = re.sub(r'(<[a-zA-Z\/][^>]*>)', '', txt)
return re.findall(r'(?:[^\w\.\-\@]|^)@([\w\.\-\@]*)', txt)

View file

@ -42,6 +42,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -72,6 +73,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -99,9 +101,10 @@
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -129,9 +132,10 @@
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
@ -145,7 +149,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-11-14 14:14:11.544811",
"modified": "2018-07-20 08:23:23.737254",
"modified_by": "Administrator",
"module": "Desk",
"name": "Calendar View",

View file

@ -312,9 +312,9 @@ frappe.ui.form.Timeline = Class.extend({
// bold @mentions
if(c.comment_type==="Comment" &&
// avoid adding <b> tag a 2nd time
!c.content_html.match(/(^|\W)<b>(@\w+)<\/b>/)
!c.content_html.match(/(^|\W)<b>(@[^\s]+)<\/b>/)
) {
c.content_html = c.content_html.replace(/(^|\W)(@\w+)/g, "$1<b>$2</b>");
c.content_html = c.content_html.replace(/(^|\W)(@[^\s]+)/g, "$1<b>$2</b>");
}
if (this.is_communication_or_comment(c)) {

View file

@ -338,7 +338,6 @@ frappe.views.BaseList = class BaseList {
const args = this.get_args();
return {
method: this.method,
type: 'GET',
args: args,
freeze: this.freeze_on_refresh || false,
freeze_message: this.freeze_message || (__('Loading') + '...')

View file

@ -0,0 +1,531 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt
// new re-re-factored Listing object
// now called BaseList
//
// opts:
// parent
// method (method to call on server)
// args (additional args to method)
// get_args (method to return args as dict)
// show_filters [false]
// doctype
// filter_fields (if given, this list is rendered, else built from doctype)
// query or get_query (will be deprecated)
// query_max
// buttons_in_frame
// no_result_message ("No result")
// page_length (20)
// hide_refresh (False)
// no_toolbar
// new_doctype
// [function] render_row(parent, data)
// [function] onrun
// no_loading (no ajax indicator)
frappe.provide('frappe.ui');
frappe.ui.BaseList = Class.extend({
init: function (opts) {
this.opts = opts || {};
this.set_defaults();
if (opts) {
this.make();
}
},
set_defaults: function () {
this.page_length = 20;
this.start = 0;
this.data = [];
},
make: function (opts) {
if (opts) {
this.opts = opts;
}
this.prepare_opts();
$.extend(this, this.opts);
// make dom
this.wrapper = $(frappe.render_template('listing', this.opts));
this.parent.append(this.wrapper);
this.set_events();
if (this.page) {
this.wrapper.find('.list-toolbar-wrapper').hide();
}
if (this.show_filters) {
this.make_filters();
}
},
prepare_opts: function () {
if (this.opts.new_doctype) {
if (!frappe.boot.user.can_create.includes(this.opts.new_doctype)) {
this.opts.new_doctype = null;
}
}
if (!this.opts.no_result_message) {
this.opts.no_result_message = __('Nothing to show');
}
if (!this.opts.page_length) {
this.opts.page_length = this.user_settings && this.user_settings.limit || 20;
}
this.opts._more = __('More');
},
add_button: function (label, click, icon) {
if (this.page) {
return this.page.add_menu_item(label, click, icon)
} else {
this.wrapper.find('.list-toolbar-wrapper').removeClass('hide');
return $('<button class="btn btn-default"></button>')
.appendTo(this.wrapper.find('.list-toolbar'))
.html((icon ? ('<i class="' + icon + '"></i> ') : '') + label)
.click(click);
}
},
set_events: function () {
var me = this;
// next page
this.wrapper.find('.btn-more').click(function () {
me.run(true);
});
this.wrapper.find(".btn-group-paging").on('click', '.btn', function () {
me.page_length = cint($(this).attr("data-value"));
me.wrapper.find(".btn-group-paging .btn-info").removeClass("btn-info");
$(this).addClass("btn-info");
// always reset when changing list page length
me.run();
});
// select the correct page length
if (this.opts.page_length !== 20) {
this.wrapper.find(".btn-group-paging .btn-info").removeClass("btn-info");
this.wrapper
.find(".btn-group-paging .btn[data-value='" + this.opts.page_length + "']")
.addClass('btn-info');
}
// title
if (this.title) {
this.wrapper.find('h3').html(this.title).show();
}
// new
this.set_primary_action();
if (me.no_toolbar || me.hide_toolbar) {
me.wrapper.find('.list-toolbar-wrapper').hide();
}
},
set_primary_action: function () {
var me = this;
if (this.new_doctype) {
this.page.set_primary_action(
__("New"),
me.make_new_doc.bind(me, me.new_doctype),
"octicon octicon-plus"
);
} else {
this.page.clear_primary_action();
}
},
make_new_doc: function (doctype) {
var me = this;
frappe.model.with_doctype(doctype, function () {
if (me.custom_new_doc) {
me.custom_new_doc(doctype);
} else {
if (me.filter_list) {
frappe.route_options = {};
me.filter_list.get_filters().forEach(function (f, i) {
if (f[2] === "=" && !frappe.model.std_fields_list.includes(f[1])) {
frappe.route_options[f[1]] = f[3];
}
});
}
frappe.new_doc(doctype, true);
}
});
},
make_filters: function () {
this.make_standard_filters();
this.filter_list = new frappe.ui.FilterList({
base_list: this,
parent: this.wrapper.find('.list-filters').show(),
doctype: this.doctype,
filter_fields: this.filter_fields,
default_filters: this.default_filters || []
});
// default filter for submittable doctype
if (frappe.model.is_submittable(this.doctype)) {
this.filter_list.add_filter(this.doctype, "docstatus", "!=", 2);
}
},
make_standard_filters: function() {
var me = this;
if (this.standard_filters_added) {
return;
}
if (this.meta) {
var filter_count = 1;
if(this.is_list_view) {
$(`<span class="octicon octicon-search text-muted small"></span>`)
.prependTo(this.page.page_form);
}
this.page.add_field({
fieldtype: 'Data',
label: 'ID',
condition: 'like',
fieldname: 'name',
onchange: () => { me.refresh(true); }
});
this.meta.fields.forEach(function(df, i) {
if(df.in_standard_filter && !frappe.model.no_value_type.includes(df.fieldtype)) {
let options = df.options;
let condition = '=';
let fieldtype = df.fieldtype;
if (['Text', 'Small Text', 'Text Editor', 'Data'].includes(fieldtype)) {
fieldtype = 'Data';
condition = 'like';
}
if(df.fieldtype == "Select" && df.options) {
options = df.options.split("\n");
if(options.length > 0 && options[0] != "") {
options.unshift("");
options = options.join("\n");
}
}
let f = me.page.add_field({
fieldtype: fieldtype,
label: __(df.label),
options: options,
fieldname: df.fieldname,
condition: condition,
onchange: () => {me.refresh(true);}
});
filter_count ++;
if (filter_count > 3) {
$(f.wrapper).addClass('hidden-sm').addClass('hidden-xs');
}
if (filter_count > 5) {
return false;
}
}
});
}
this.standard_filters_added = true;
},
update_standard_filters: function(filters) {
let me = this;
for(let key in this.page.fields_dict) {
let field = this.page.fields_dict[key];
let value = field.get_value();
if (value) {
if (field.df.condition==='like' && !value.includes('%')) {
value = '%' + value + '%';
}
filters.push([
me.doctype,
field.df.fieldname,
field.df.condition || '=',
value
]);
}
}
},
clear: function () {
this.data = [];
this.wrapper.find('.result-list').empty();
this.wrapper.find('.result').show();
this.wrapper.find('.no-result').hide();
this.start = 0;
this.onreset && this.onreset();
},
set_filters_from_route_options: function ({clear_filters=true} = {}) {
var me = this;
if(this.filter_list && clear_filters) {
this.filter_list.clear_filters();
}
for(var field in frappe.route_options) {
var value = frappe.route_options[field];
var doctype = null;
// if `Child DocType.fieldname`
if (field.includes(".")) {
doctype = field.split(".")[0];
field = field.split(".")[1];
}
// find the table in which the key exists
// for example the filter could be {"item_code": "X"}
// where item_code is in the child table.
// we can search all tables for mapping the doctype
if (!doctype) {
doctype = frappe.meta.get_doctype_for_field(me.doctype, field);
}
if (doctype && me.filter_list) {
if ($.isArray(value)) {
me.filter_list.add_filter(doctype, field, value[0], value[1]);
} else {
me.filter_list.add_filter(doctype, field, "=", value);
}
}
}
frappe.route_options = null;
},
run: function(more) {
setTimeout(() => this._run(more), 100);
},
_run: function (more) {
var me = this;
if (!more) {
this.start = 0;
this.onreset && this.onreset();
}
var args = this.get_call_args();
this.save_user_settings_locally(args);
// user_settings are saved by db_query.py when dirty
$.extend(args, {
user_settings: frappe.model.user_settings[this.doctype]
});
return frappe.call({
method: this.opts.method || 'frappe.desk.query_builder.runquery',
freeze: this.opts.freeze !== undefined ? this.opts.freeze : true,
args: args,
callback: function (r) {
me.dirty = false;
me.render_results(r);
},
no_spinner: this.opts.no_loading
});
},
save_user_settings_locally: function (args) {
if (this.opts.save_user_settings && this.doctype && !this.docname) {
// save list settings locally
var user_settings = frappe.model.user_settings[this.doctype];
var different = false;
if (!user_settings) {
return;
}
if (!frappe.utils.arrays_equal(args.filters, user_settings.filters)) {
// settings are dirty if filters change
user_settings.filters = args.filters;
different = true;
}
if (user_settings.order_by !== args.order_by) {
user_settings.order_by = args.order_by;
different = true;
}
if (user_settings.limit !== args.limit_page_length) {
user_settings.limit = args.limit_page_length || 20
different = true;
}
// save fields in list settings
if (args.save_user_settings_fields) {
user_settings.fields = args.fields;
}
if (different) {
user_settings.updated_on = moment().toString();
}
}
},
get_call_args: function () {
// load query
if (!this.method) {
var query = this.get_query && this.get_query() || this.query;
query = this.add_limits(query);
var args = {
query_max: this.query_max,
as_dict: 1
}
args.simple_query = query;
} else {
var args = {
start: this.start,
page_length: this.page_length
}
}
// append user-defined arguments
if (this.args)
$.extend(args, this.args)
if (this.get_args) {
$.extend(args, this.get_args());
}
return args;
},
render_results: function (r) {
if (this.start === 0)
this.clear();
this.wrapper.find('.btn-more, .list-loading').hide();
var values = [];
if (r.message) {
values = this.get_values_from_response(r.message);
}
var show_results = true;
if(this.show_no_result) {
if($.isFunction(this.show_no_result)) {
show_results = !this.show_no_result()
} else {
show_results = !this.show_no_result;
}
}
// render result view when
// length > 0 OR
// explicitly set by flag
if (values.length || show_results) {
this.data = this.data.concat(values);
this.render_view(values);
this.update_paging(values);
} else if (this.start === 0) {
// show no result message
this.wrapper.find('.result').hide();
var msg = '';
var no_result_message = this.no_result_message;
if(no_result_message && $.isFunction(no_result_message)) {
msg = no_result_message();
} else if(typeof no_result_message === 'string') {
msg = no_result_message;
} else {
msg = __('No Results')
}
this.wrapper.find('.no-result').html(msg).show();
}
this.wrapper.find('.list-paging-area')
.toggle(values.length > 0|| this.start > 0);
// callbacks
if (this.onrun) this.onrun();
if (this.callback) this.callback(r);
this.wrapper.trigger("render-complete");
},
get_values_from_response: function (data) {
// make dictionaries from keys and values
if (data.keys && $.isArray(data.keys)) {
return frappe.utils.dict(data.keys, data.values);
} else {
return data;
}
},
render_view: function (values) {
// override this method in derived class
},
update_paging: function (values) {
if (values.length >= this.page_length) {
this.wrapper.find('.btn-more').show();
this.start += this.page_length;
}
},
refresh: function () {
this.run();
},
add_limits: function (query) {
return query + ' LIMIT ' + this.start + ',' + (this.page_length + 1);
},
set_filter: function (fieldname, label, no_run, no_duplicate) {
var filter = this.filter_list.get_filter(fieldname);
if (filter) {
var value = cstr(filter.field.get_value());
if (value.includes(label)) {
// already set
return false
} else if (no_duplicate) {
filter.set_values(this.doctype, fieldname, "=", label);
} else {
// second filter set for this field
if (fieldname == '_user_tags' || fieldname == "_liked_by") {
// and for tags
this.filter_list.add_filter(this.doctype, fieldname, 'like', '%' + label + '%');
} else {
// or for rest using "in"
filter.set_values(this.doctype, fieldname, 'in', value + ', ' + label);
}
}
} else {
// no filter for this item,
// setup one
if (['_user_tags', '_comments', '_assign', '_liked_by'].includes(fieldname)) {
this.filter_list.add_filter(this.doctype, fieldname, 'like', '%' + label + '%');
} else {
this.filter_list.add_filter(this.doctype, fieldname, '=', label);
}
}
if (!no_run)
this.run();
},
init_user_settings: function () {
this.user_settings = frappe.model.user_settings[this.doctype] || {};
},
call_for_selected_items: function (method, args) {
var me = this;
args.names = this.get_checked_items().map(function (item) {
return item.name;
});
frappe.call({
method: method,
args: args,
freeze: true,
callback: function (r) {
if (!r.exc) {
if (me.list_header) {
me.list_header.find(".list-select-all").prop("checked", false);
}
me.refresh(true);
}
}
});
}
});

View file

@ -0,0 +1,952 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt
frappe.views.ReportFactory = frappe.views.Factory.extend({
make: function(route) {
new frappe.views.ReportViewPage(route[1], route[2]);
}
});
frappe.views.ReportViewPage = Class.extend({
init: function(doctype, docname) {
if(!frappe.model.can_get_report(doctype)) {
frappe.show_not_permitted(frappe.get_route_str());
return;
}
this.doctype = doctype;
this.docname = docname;
this.page_name = frappe.get_route_str();
this.make_page();
var me = this;
frappe.model.with_doctype(this.doctype, function() {
me.make_report_view();
if(me.docname) {
frappe.model.with_doc('Report', me.docname, function(r) {
me.parent.reportview.set_columns_and_filters(
JSON.parse(frappe.get_doc("Report", me.docname).json || '{}'));
me.parent.reportview.set_route_filters();
me.parent.reportview.run();
});
} else {
me.parent.reportview.set_route_filters();
me.parent.reportview.run();
}
});
},
make_page: function() {
var me = this;
this.parent = frappe.container.add_page(this.page_name);
frappe.ui.make_app_page({parent:this.parent, single_column:true});
this.page = this.parent.page;
frappe.container.change_to(this.page_name);
$(this.parent).on('show', function(){
if(me.parent.reportview.set_route_filters()) {
me.parent.reportview.run();
}
});
},
make_report_view: function() {
this.page.set_title(__(this.doctype));
var module = locals.DocType[this.doctype].module;
frappe.breadcrumbs.add(module, this.doctype);
this.parent.reportview = new frappe.views.ReportView({
doctype: this.doctype,
docname: this.docname,
parent: this.parent
});
}
});
frappe.views.ReportView = frappe.ui.BaseList.extend({
init: function(opts) {
var me = this;
$.extend(this, opts);
this.can_delete = frappe.model.can_delete(me.doctype);
this.tab_name = '`tab'+this.doctype+'`';
this.setup();
},
setup: function() {
var me = this;
this.add_totals_row = 0;
this.page = this.parent.page;
this.meta = frappe.get_meta(this.doctype);
this._body = $('<div>').appendTo(this.page.main);
this.page_title = __('Report')+ ': ' + (this.docname ?
__(this.doctype) + ' - ' + __(this.docname) : __(this.doctype));
this.page.set_title(this.page_title);
this.init_user_settings();
this.make({
page: this.parent.page,
method: 'frappe.desk.reportview.get',
save_user_settings: true,
get_args: this.get_args,
parent: this._body,
start: 0,
show_filters: true,
allow_delete: true,
});
this.make_new_and_refresh();
this.make_delete();
this.make_column_picker();
this.make_sorter();
this.make_totals_row_button();
this.setup_print();
this.make_export();
this.setup_auto_email();
this.set_init_columns();
this.make_save();
this.make_user_permissions();
this.set_tag_and_status_filter();
this.setup_listview_settings();
// add to desktop
this.page.add_menu_item(__("Add to Desktop"), function() {
frappe.add_to_desktop(me.docname || __('{0} Report', [me.doctype]), me.doctype, me.docname);
}, true);
},
make_new_and_refresh: function() {
var me = this;
this.page.set_primary_action(__("Refresh"), function() {
me.run();
});
this.page.add_menu_item(__("New {0}", [this.doctype]), function() {
me.make_new_doc(me.doctype);
}, true);
},
setup_auto_email: function() {
var me = this;
this.page.add_menu_item(__("Setup Auto Email"), function() {
if(me.docname) {
frappe.set_route('List', 'Auto Email Report', {'report' : me.docname});
} else {
frappe.msgprint({message:__('Please save the report first'), indicator: 'red'});
}
}, true);
},
set_init_columns: function() {
// pre-select mandatory columns
var me = this;
var columns = [];
if(this.user_settings.fields && !this.docname) {
this.user_settings.fields.forEach(function(field) {
var coldef = me.get_column_info_from_field(field);
if(!in_list(['_seen', '_comments', '_user_tags', '_assign', '_liked_by', 'docstatus'], coldef[0])) {
columns.push(coldef);
}
});
}
if(!columns.length) {
var columns = [['name', this.doctype],];
$.each(frappe.meta.docfield_list[this.doctype], function(i, df) {
if((df.in_standard_filter || df.in_list_view) && df.fieldname!='naming_series'
&& !in_list(frappe.model.no_value_type, df.fieldtype)
&& !df.report_hide) {
columns.push([df.fieldname, df.parent]);
}
});
}
this.set_columns(columns);
this.page.footer.on('click', '.show-all-data', function() {
me.show_all_data = $(this).prop('checked');
me.run();
})
},
set_columns: function(columns) {
this.columns = columns;
this.column_info = this.get_columns();
this.refresh_footer();
},
refresh_footer: function() {
var can_write = frappe.model.can_write(this.doctype);
var has_child_column = this.has_child_column();
this.page.footer.empty();
if(can_write || has_child_column) {
$(frappe.render_template('reportview_footer', {
has_child_column: has_child_column,
can_write: can_write,
show_all_data: this.show_all_data
})).appendTo(this.page.footer);
this.page.footer.removeClass('hide');
} else {
this.page.footer.addClass('hide');
}
},
// preset columns and filters from saved info
set_columns_and_filters: function(opts) {
var me = this;
this.filter_list.clear_filters();
if(opts.columns) {
this.set_columns(opts.columns);
}
if(opts.filters) {
$.each(opts.filters, function(i, f) {
// f = [doctype, fieldname, condition, value]
var df = frappe.meta.get_docfield(f[0], f[1]);
if (df && df.fieldtype == "Check") {
var value = f[3] ? "Yes" : "No";
} else {
var value = f[3];
}
me.filter_list.add_filter(f[0], f[1], f[2], value);
});
}
if(opts.add_total_row) {
this.add_total_row = opts.add_total_row
}
// first sort
if(opts.sort_by) this.sort_by_select.val(opts.sort_by);
if(opts.sort_order) this.sort_order_select.val(opts.sort_order);
// second sort
if(opts.sort_by_next) this.sort_by_next_select.val(opts.sort_by_next);
if(opts.sort_order_next) this.sort_order_next_select.val(opts.sort_order_next);
this.add_totals_row = cint(opts.add_totals_row);
},
set_route_filters: function() {
var me = this;
if(frappe.route_options) {
this.set_filters_from_route_options({clear_filters: this.docname ? false : true});
return true;
} else if(this.user_settings
&& this.user_settings.filters
&& !this.docname
&& (this.user_settings.updated_on != this.user_settings_updated_on)) {
// list settings (previous settings)
this.filter_list.clear_filters();
$.each(this.user_settings.filters, function(i, f) {
me.filter_list.add_filter(f[0], f[1], f[2], f[3]);
});
return true;
}
this.user_settings_updated_on = this.user_settings.updated_on;
},
setup_print: function() {
var me = this;
this.page.add_menu_item(__("Print"), function() {
frappe.ui.get_print_settings(false, function(print_settings) {
var title = __(me.docname || me.doctype);
frappe.render_grid({grid:me.grid, title:title, print_settings:print_settings});
})
}, true);
},
// build args for query
get_args: function() {
let me = this;
let filters = this.filter_list? this.filter_list.get_filters(): [];
return {
doctype: this.doctype,
fields: $.map(this.columns || [], function(v) { return me.get_full_column_name(v); }),
order_by: this.get_order_by(),
add_total_row: this.add_total_row,
filters: filters,
save_user_settings_fields: 1,
with_childnames: 1,
file_format_type: this.file_format_type
}
},
get_order_by: function() {
var order_by = [];
// first
var sort_by_select = this.get_selected_table_and_column(this.sort_by_select);
if (sort_by_select) {
order_by.push(sort_by_select + " " + this.sort_order_select.val());
}
// second
if(this.sort_by_next_select && this.sort_by_next_select.val()) {
order_by.push(this.get_selected_table_and_column(this.sort_by_next_select)
+ ' ' + this.sort_order_next_select.val());
}
return order_by.join(", ");
},
get_selected_table_and_column: function(select) {
if(!select) {
return
}
return select.selected_doctype ?
this.get_full_column_name([select.selected_fieldname, select.selected_doctype]) : "";
},
// get table_name.column_name
get_full_column_name: function(v) {
if(!v) return;
return (v[1] ? ('`tab' + v[1] + '`') : this.tab_name) + '.`' + v[0] + '`';
},
get_column_info_from_field: function(t) {
if(t.indexOf('.')===-1) {
return [strip(t, '`'), this.doctype];
} else {
var parts = t.split('.');
return [strip(parts[1], '`'), strip(parts[0], '`').substr(3)];
}
},
// build columns for slickgrid
build_columns: function() {
var me = this;
return $.map(this.columns, function(c) {
var docfield = frappe.meta.docfield_map[c[1] || me.doctype][c[0]];
if(!docfield) {
var docfield = frappe.model.get_std_field(c[0]);
if(docfield) {
docfield.parent = me.doctype;
if(c[0]=="name") {
docfield.options = me.doctype;
}
}
}
if(!docfield) return;
let coldef = {
id: c[0],
field: c[0],
docfield: docfield,
name: __(docfield ? docfield.label : toTitle(c[0])),
width: (docfield ? cint(docfield.width) : 120) || 120,
formatter: function(row, cell, value, columnDef, dataContext, for_print) {
var docfield = columnDef.docfield;
docfield.fieldtype = {
"_user_tags": "Tag",
"_comments": "Comment",
"_assign": "Assign",
"_liked_by": "LikedBy",
}[docfield.fieldname] || docfield.fieldtype;
if(docfield.fieldtype==="Link" && docfield.fieldname!=="name") {
// make a copy of docfield for reportview
// as it needs to add a link_onclick property
if(!columnDef.report_docfield) {
columnDef.report_docfield = copy_dict(docfield);
}
docfield = columnDef.report_docfield;
docfield.link_onclick =
repl('frappe.container.page.reportview.filter_or_open("%(parent)s", "%(fieldname)s", "%(value)s")',
{parent: docfield.parent, fieldname:docfield.fieldname, value:value});
}
return frappe.format(value, docfield, {for_print: for_print, always_show_decimals: true}, dataContext);
}
}
return coldef;
});
},
filter_or_open: function(parent, fieldname, value) {
// set filter on click, if filter is set, open the document
var filter_set = false;
this.filter_list.get_filters().forEach(function(f) {
if(f[1]===fieldname) {
filter_set = true;
}
});
if(!filter_set) {
this.set_filter(fieldname, value, false, false, parent);
} else {
var df = frappe.meta.get_docfield(parent, fieldname);
if(df.fieldtype==='Link') {
frappe.set_route('Form', df.options, value);
}
}
},
// render data
render_view: function() {
var me = this;
var data = this.get_unique_data(this.column_info);
this.set_totals_row(data, this.column_info);
// add sr in data
$.each(data, function(i, v) {
// add index
v._idx = i+1;
v.id = v._idx;
});
var options = {
enableCellNavigation: true,
enableColumnReorder: false,
};
if(this.slickgrid_options) {
$.extend(options, this.slickgrid_options);
}
this.dataView = new Slick.Data.DataView();
this.set_data(data);
var grid_wrapper = this.wrapper.find('.result-list').addClass("slick-wrapper");
// set height if not auto
if(!options.autoHeight)
grid_wrapper.css('height', '500px');
this.grid = new Slick.Grid(grid_wrapper
.get(0), this.dataView,
this.column_info, options);
if (!frappe.dom.is_touchscreen()) {
this.grid.setSelectionModel(new Slick.CellSelectionModel());
this.grid.registerPlugin(new Slick.CellExternalCopyManager({
dataItemColumnValueExtractor: function(item, columnDef, value) {
return item[columnDef.field];
}
}));
}
frappe.slickgrid_tools.add_property_setter_on_resize(this.grid);
if(this.start!=0 && !options.autoHeight) {
this.grid.scrollRowIntoView(data.length-1);
}
this.grid.onDblClick.subscribe(function(e, args) {
var row = me.dataView.getItem(args.row);
var cell = me.grid.getColumns()[args.cell];
me.edit_cell(row, cell.docfield);
});
this.dataView.onRowsChanged.subscribe(function (e, args) {
me.grid.invalidateRows(args.rows);
me.grid.render();
});
this.grid.onHeaderClick.subscribe(function(e, args) {
if(e.target.className === "slick-resizable-handle")
return;
var df = args.column.docfield,
sort_by = df.parent + "." + df.fieldname;
if(sort_by===me.sort_by_select.val()) {
me.sort_order_select.val(me.sort_order_select.val()==="asc" ? "desc" : "asc");
} else {
me.sort_by_select.val(df.parent + "." + df.fieldname);
me.sort_order_select.val("asc");
}
me.run();
});
},
has_child_column: function() {
var me = this;
return this.column_info.some(function(c) {
return c.docfield && c.docfield.parent !== me.doctype;
});
},
get_unique_data: function(columns) {
// if child columns are selected, show parent data only once
let has_child_column = this.has_child_column();
var data = [], prev_row = null;
this.data.forEach((d) => {
if (this.show_all_data || !has_child_column) {
data.push(d);
} else if (prev_row && d.name == prev_row.name) {
var new_row = {};
columns.forEach((c) => {
if(!c.docfield || c.docfield.parent!==this.doctype) {
var val = d[c.field];
// add child table row name for update
if(c.docfield && c.docfield.parent!==this.doctype) {
new_row[c.docfield.parent+":name"] = d[c.docfield.parent+":name"];
}
} else {
var val = '';
new_row.__is_repeat = true;
}
new_row[c.field] = val;
});
data.push(new_row);
} else {
data.push(d);
}
prev_row = d;
});
return data;
},
edit_cell: function(row, docfield) {
if(!docfield || docfield.fieldname !== "idx"
&& frappe.model.std_fields_list.indexOf(docfield.fieldname)!==-1) {
return;
} else if(frappe.boot.user.can_write.indexOf(this.doctype)===-1) {
frappe.throw({message:__("No permission to edit"), title:__('Not Permitted')});
}
var me = this;
var d = new frappe.ui.Dialog({
title: __("Edit") + " " + __(docfield.label),
fields: [docfield],
primary_action_label: __("Update"),
primary_action: function() {
me.update_value(docfield, d, row);
}
});
d.set_input(docfield.fieldname, row[docfield.fieldname]);
// Show dialog if field is editable and not hidden
if (d.fields_list[0].disp_status != "Write") d.hide();
else d.show();
},
update_value: function(docfield, dialog, row) {
// update value on the serverside
var me = this;
var args = {
doctype: docfield.parent,
name: row[docfield.parent===me.doctype ? "name" : docfield.parent+":name"],
fieldname: docfield.fieldname,
value: dialog.get_value(docfield.fieldname)
};
if (!args.name) {
frappe.throw(__("ID field is required to edit values using Report. Please select the ID field using the Column Picker"));
}
frappe.call({
method: "frappe.client.set_value",
args: args,
callback: function(r) {
if(!r.exc) {
dialog.hide();
var doc = r.message;
$.each(me.dataView.getItems(), function(i, item) {
if (item.name === doc.name) {
var new_item = $.extend({}, item);
$.each(frappe.model.get_all_docs(doc), function(i, d) {
// find the document of the current updated record
// from locals (which is synced in the response)
var name = item[d.doctype + ":name"];
if(!name) name = item.name;
if(name===d.name) {
for(var k in d) {
var v = d[k];
if(frappe.model.std_fields_list.indexOf(k)===-1
&& item[k]!==undefined) {
new_item[k] = v;
}
}
}
});
me.dataView.updateItem(item.id, new_item);
}
});
}
}
});
},
set_data: function(data) {
this.dataView.beginUpdate();
this.dataView.setItems(data);
this.dataView.endUpdate();
},
get_columns: function() {
var std_columns = [{id:'_idx', field:'_idx', name: 'Sr.', width: 40, maxWidth: 40}];
if(this.can_delete) {
std_columns = std_columns.concat([{
id:'_check', field:'_check', name: "", width: 30, maxWidth: 30,
formatter: function(row, cell, value, columnDef, dataContext) {
return repl("<input type='checkbox' \
data-row='%(row)s' %(checked)s>", {
row: row,
checked: (dataContext.selected ? "checked=\"checked\"" : "")
});
}
}]);
}
return std_columns.concat(this.build_columns());
},
// setup column picker
make_column_picker: function() {
var me = this;
this.column_picker = new frappe.ui.ColumnPicker(this);
this.page.add_inner_button(__('Pick Columns'), function() {
me.column_picker.show(me.columns);
});
},
make_totals_row_button: function() {
var me = this;
this.page.add_inner_button(__('Show Totals'), function() {
me.add_totals_row = !!!me.add_totals_row;
me.render_view();
});
},
set_totals_row: function(data, columns) {
const field_map = {};
const numeric_fieldtypes = ['Int', 'Currency', 'Float'];
columns.forEach(function(row) {
if (row.docfield) {
let r = row.docfield;
if (numeric_fieldtypes.includes(r.fieldtype)) {
field_map[r.fieldname] = [r.fieldtype];
}
}
})
if(this.add_totals_row) {
var totals_row = {_totals_row: 1};
if(data.length) {
data.forEach(function(row, ri) {
$.each(row, function(key, value) {
if (key in field_map) {
totals_row[key] = (totals_row[key] || 0) + value;
}
});
});
}
data.push(totals_row);
}
},
set_tag_and_status_filter: function() {
var me = this;
this.wrapper.find('.result-list').on("click", ".label-info", function() {
if($(this).attr("data-label")) {
me.set_filter("_user_tags", $(this).attr("data-label"));
}
});
this.wrapper.find('.result-list').on("click", "[data-workflow-state]", function() {
if($(this).attr("data-workflow-state")) {
me.set_filter(me.state_fieldname,
$(this).attr("data-workflow-state"));
}
});
},
// setup sorter
make_sorter: function() {
var me = this;
this.sort_dialog = new frappe.ui.Dialog({title:__('Sorting Preferences')});
$(this.sort_dialog.body).html('<p class="help">'+__('Sort By')+'</p>\
<div class="sort-column"></div>\
<div><select class="sort-order form-control" style="margin-top: 10px; width: 60%;">\
<option value="asc">'+__('Ascending')+'</option>\
<option value="desc">'+__('Descending')+'</option>\
</select></div>\
<hr><p class="help">'+__('Then By (optional)')+'</p>\
<div class="sort-column-1"></div>\
<div><select class="sort-order-1 form-control" style="margin-top: 10px; width: 60%;">\
<option value="asc">'+__('Ascending')+'</option>\
<option value="desc">'+__('Descending')+'</option>\
</select></div><hr>\
<div><button class="btn btn-primary">'+__('Update')+'</div>');
// first
this.sort_by_select = new frappe.ui.FieldSelect({
parent: $(this.sort_dialog.body).find('.sort-column'),
doctype: this.doctype
});
this.sort_by_select.$select.css('width', '60%');
this.sort_order_select = $(this.sort_dialog.body).find('.sort-order');
// second
this.sort_by_next_select = new frappe.ui.FieldSelect({
parent: $(this.sort_dialog.body).find('.sort-column-1'),
doctype: this.doctype,
with_blank: true
});
this.sort_by_next_select.$select.css('width', '60%');
this.sort_order_next_select = $(this.sort_dialog.body).find('.sort-order-1');
// initial values
this.sort_by_select.set_value(this.doctype, 'modified');
this.sort_order_select.val('desc');
this.sort_by_next_select.clear();
this.sort_order_next_select.val('desc');
// button actions
this.page.add_inner_button(__('Sort Order'), function() {
me.sort_dialog.show();
});
$(this.sort_dialog.body).find('.btn-primary').click(function() {
me.sort_dialog.hide();
me.run();
});
},
// setup export
make_export: function() {
var me = this;
if(!frappe.model.can_export(this.doctype)) {
return;
}
var export_btn = this.page.add_menu_item(__('Export'), function() {
var args = me.get_args();
var selected_items = me.get_checked_items()
frappe.prompt({fieldtype:"Select", label: __("Select File Type"), fieldname:"file_format_type",
options:"Excel\nCSV", default:"Excel", reqd: 1},
function(data) {
args.cmd = 'frappe.desk.reportview.export_query';
args.file_format_type = data.file_format_type;
if(me.add_totals_row) {
args.add_totals_row = 1;
}
if(selected_items.length >= 1) {
args.selected_items = $.map(selected_items, function(d) { return d.name; });
}
open_url_post(frappe.request.url, args);
}, __("Export Report: {0}",[__(me.doctype)]), __("Download"));
}, true);
},
// save
make_save: function() {
var me = this;
if(frappe.user.is_report_manager()) {
this.page.add_menu_item(__('Save'), function() { me.save_report('save') }, true);
this.page.add_menu_item(__('Save As'), function() { me.save_report('save_as') }, true);
}
},
save_report: function(save_type) {
var me = this;
var _save_report = function(name) {
// callback
return frappe.call({
method: 'frappe.desk.reportview.save_report',
args: {
name: name,
doctype: me.doctype,
json: JSON.stringify({
filters: me.filter_list.get_filters(),
columns: me.columns,
sort_by: me.sort_by_select.val(),
sort_order: me.sort_order_select.val(),
sort_by_next: me.sort_by_next_select.val(),
sort_order_next: me.sort_order_next_select.val(),
add_totals_row: me.add_totals_row
})
},
callback: function(r) {
if(r.exc) {
frappe.msgprint(__("Report was not saved (there were errors)"));
return;
}
if(r.message != me.docname)
frappe.set_route('Report', me.doctype, r.message);
}
});
}
if(me.docname && save_type == "save") {
_save_report(me.docname);
} else {
frappe.prompt({fieldname: 'name', label: __('New Report name'), reqd: 1, fieldtype: 'Data'}, function(data) {
_save_report(data.name);
}, __('Save As'));
}
},
make_delete: function() {
var me = this;
if(this.can_delete) {
$(this.parent).on("click", "input[type='checkbox'][data-row]", function() {
me.data[$(this).attr("data-row")].selected
= this.checked ? true : false;
});
this.page.add_menu_item(__("Delete"), function() {
var delete_list = $.map(me.get_checked_items(), function(d) { return d.name; });
if(!delete_list.length)
return;
if(frappe.confirm(__("This is PERMANENT action and you cannot undo. Continue?"),
function() {
return frappe.call({
method: 'frappe.desk.reportview.delete_items',
args: {
items: delete_list,
doctype: me.doctype
},
callback: function() {
me.refresh();
}
});
}));
}, true);
}
},
make_user_permissions: function() {
var me = this;
if(this.docname && frappe.model.can_set_user_permissions("Report")) {
this.page.add_menu_item(__("User Permissions"), function() {
frappe.route_options = {
doctype: "Report",
name: me.docname
};
frappe.set_route('List', 'User Permission');
}, true);
}
},
setup_listview_settings: function() {
if(frappe.listview_settings[this.doctype] && frappe.listview_settings[this.doctype].onload) {
frappe.listview_settings[this.doctype].onload(this);
}
},
get_checked_items: function() {
var me = this;
var selected_records = []
$.each(me.data, function(i, d) {
if(d.selected && d.name) {
selected_records.push(d);
}
});
return selected_records
}
});
frappe.ui.ColumnPicker = Class.extend({
init: function(list) {
this.list = list;
this.doctype = list.doctype;
},
clear: function() {
this.columns = [];
$(this.dialog.body).html('<div class="text-muted">'+__("Drag to sort columns")+'</div>\
<div class="column-list"></div>\
<div><button class="btn btn-default btn-sm btn-add">'+__("Add Column")+'</button></div>');
},
show: function(columns) {
var me = this;
if(!this.dialog) {
this.dialog = new frappe.ui.Dialog({
title: __("Pick Columns"),
width: '400',
primary_action_label: __("Update"),
primary_action: function() {
me.update_column_selection();
}
});
this.dialog.$wrapper.addClass("column-picker-dialog");
}
this.clear();
this.column_list = $(this.dialog.body).find('.column-list');
// show existing
$.each(columns, function(i, c) {
me.add_column(c);
});
new Sortable(this.column_list.get(0), {
//handle: '.sortable-handle',
filter: 'input',
draggable: '.column-list-item',
chosenClass: 'sortable-chosen',
dragClass: 'sortable-chosen',
onUpdate: function(event) {
me.columns = [];
$.each($(me.dialog.body).find('.column-list .column-list-item'),
function(i, ele) {
me.columns.push($(ele).data("fieldselect"))
});
}
});
// add column
$(this.dialog.body).find('.btn-add').click(function() {
me.add_column(['name']);
});
this.dialog.show();
},
add_column: function(c) {
if(!c) return;
var me = this;
var w = $('<div class="column-list-item"><div class="row">\
<div class="col-xs-1">\
<i class="fa fa-sort text-muted"></i></div>\
<div class="col-xs-10"></div>\
<div class="col-xs-1"><a class="close">&times;</a></div>\
</div></div>')
.appendTo(this.column_list);
var fieldselect = new frappe.ui.FieldSelect({parent:w.find('.col-xs-10'), doctype:this.doctype});
fieldselect.val((c[1] || this.doctype) + "." + c[0]);
w.data("fieldselect", fieldselect);
w.find('.close').data("fieldselect", fieldselect)
.click(function() {
delete me.columns[me.columns.indexOf($(this).data('fieldselect'))];
$(this).parents('.column-list-item').remove();
});
this.columns.push(fieldselect);
},
update_column_selection: function() {
this.dialog.hide();
// selected columns as list of [column_name, table_name]
var columns = $.map(this.columns, function(v) {
return (v && v.selected_fieldname && v.selected_doctype)
? [[v.selected_fieldname, v.selected_doctype]]
: null;
});
this.list.set_columns(columns);
this.list.run();
}
});

View file

@ -584,3 +584,37 @@ def cast_fieldtype(fieldtype, value):
value = to_timedelta(value)
return value
def get_db_count(*args):
"""
Pass a doctype or a series of doctypes to get the count of docs in them
Parameters:
*args: Variable length argument list of doctype names whose doc count you need
Returns:
dict: A dict with the count values.
Example:
via terminal:
bench --site erpnext.local execute frappe.utils.get_db_count --args "['DocType', 'Communication']"
"""
db_count = {}
for doctype in args:
db_count[doctype] = frappe.db.count(doctype)
return json.loads(frappe.as_json(db_count))
def call(fn, *args, **kwargs):
"""
Pass a doctype or a series of doctypes to get the count of docs in them
Parameters:
fn: frappe function to be called
Returns:
based on the function you call: output of the function you call
Example:
via terminal:
bench --site erpnext.local execute frappe.utils.call --args '''["frappe.get_all", "Activity Log"]''' --kwargs '''{"fields": ["user", "creation", "full_name"], "filters":{"Operation": "Login", "Status": "Success"}, "limit": "10"}'''
"""
return json.loads(frappe.as_json(frappe.call(fn, *args, **kwargs)))

View file

@ -17,7 +17,7 @@ def get_pdf(html, options=None, output = None):
try:
pdfkit.from_string(html, fname, options=options or {})
if output:
append_pdf(PdfFileReader(file(fname,"rb")),output)
append_pdf(PdfFileReader(fname),output)
else:
with open(fname, "rb") as fileobj:
filedata = fileobj.read()