Merge pull request #1774 from rmehta/list-settings
List Settings: Save user filters across sessions and list/report views
This commit is contained in:
commit
fe6de7981d
23 changed files with 366 additions and 119 deletions
|
|
@ -6,7 +6,6 @@ import json
|
|||
import frappe
|
||||
import frappe.handler
|
||||
import frappe.client
|
||||
import frappe.desk.reportview
|
||||
from frappe.utils.response import build_response
|
||||
from frappe import _
|
||||
|
||||
|
|
|
|||
|
|
@ -4,15 +4,14 @@
|
|||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.desk.reportview import execute as runreport
|
||||
from frappe.utils import getdate
|
||||
|
||||
def execute(filters=None):
|
||||
priority_map = {"High": 3, "Medium": 2, "Low": 1}
|
||||
|
||||
todo_list = runreport(doctype="ToDo", fields=["name", "date", "description",
|
||||
todo_list = frappe.get_list('ToDo', fields=["name", "date", "description",
|
||||
"priority", "reference_type", "reference_name", "assigned_by", "owner"],
|
||||
filters=[["ToDo", "status", "=", "Open"]])
|
||||
filters={'status': 'Open'})
|
||||
|
||||
todo_list.sort(key=lambda todo: (priority_map.get(todo.priority, 0),
|
||||
todo.date and getdate(todo.date) or getdate("1900-01-01")), reverse=True)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import frappe.utils
|
|||
import frappe.share
|
||||
import frappe.defaults
|
||||
import frappe.desk.form.meta
|
||||
from frappe.model.utils.list_settings import get_list_settings
|
||||
from frappe.permissions import get_doc_permissions
|
||||
from frappe import _
|
||||
|
||||
|
|
@ -54,6 +55,8 @@ def getdoctype(doctype, with_parent=False, cached_timestamp=None):
|
|||
"""load doctype"""
|
||||
|
||||
docs = []
|
||||
parent_dt = None
|
||||
|
||||
# with parent (called from report builder)
|
||||
if with_parent:
|
||||
parent_dt = frappe.model.meta.get_parent_dt(doctype)
|
||||
|
|
@ -65,6 +68,7 @@ def getdoctype(doctype, with_parent=False, cached_timestamp=None):
|
|||
docs = get_meta_bundle(doctype)
|
||||
|
||||
frappe.response['user_permissions'] = get_user_permissions(docs)
|
||||
frappe.response['list_settings'] = get_list_settings(parent_dt or doctype)
|
||||
|
||||
if cached_timestamp and docs[0].modified==cached_timestamp:
|
||||
return "use_cache"
|
||||
|
|
|
|||
|
|
@ -60,7 +60,6 @@ def add_comment(doc):
|
|||
|
||||
@frappe.whitelist()
|
||||
def get_next(doctype, value, prev, filters=None, order_by="modified desc"):
|
||||
import frappe.desk.reportview
|
||||
|
||||
prev = not int(prev)
|
||||
sort_field, sort_order = order_by.split(" ")
|
||||
|
|
@ -82,7 +81,7 @@ def get_next(doctype, value, prev, filters=None, order_by="modified desc"):
|
|||
if not order_by[0] in [f[1] for f in filters]:
|
||||
filters.append([doctype, sort_field, condition, value])
|
||||
|
||||
res = frappe.desk.reportview.execute(doctype,
|
||||
res = frappe.get_list(doctype,
|
||||
fields = ["name"],
|
||||
filters = filters,
|
||||
order_by = sort_field + " " + sort_order,
|
||||
|
|
|
|||
|
|
@ -7,11 +7,34 @@ from __future__ import unicode_literals
|
|||
import frappe, json
|
||||
import frappe.permissions
|
||||
from frappe.model.db_query import DatabaseQuery
|
||||
from frappe.model.utils.list_settings import update_list_settings
|
||||
from frappe.utils import cint
|
||||
from frappe import _
|
||||
|
||||
@frappe.whitelist()
|
||||
def get():
|
||||
return compress(execute(**get_form_params()))
|
||||
args = get_form_params()
|
||||
save_list_settings_fields = False
|
||||
|
||||
if args.save_list_settings_fields:
|
||||
save_list_settings_fields = True
|
||||
del args['save_list_settings_fields']
|
||||
|
||||
data = compress(execute(**args))
|
||||
|
||||
# update list settings if new search
|
||||
if not cint(args.limit_start) or cint(args.limit or args.limit_page_length) != 20:
|
||||
list_settings = {
|
||||
'filters': args.filters,
|
||||
'limit': args.limit or args.limit_page_length,
|
||||
'order_by': args.order_by
|
||||
}
|
||||
if save_list_settings_fields:
|
||||
list_settings['fields'] = args.fields
|
||||
|
||||
update_list_settings(args.doctype, list_settings)
|
||||
|
||||
return data
|
||||
|
||||
def execute(doctype, *args, **kwargs):
|
||||
return DatabaseQuery(doctype).execute(*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
# Search
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import frappe.desk.reportview
|
||||
from frappe.utils import cstr, unique
|
||||
|
||||
# this is called by the Link Field
|
||||
|
|
@ -83,7 +82,7 @@ def search_widget(doctype, txt, query=None, searchfield=None, start=0,
|
|||
fields.append("""locate("{_txt}", `tab{doctype}`.`name`) as `_relevance`""".format(
|
||||
_txt=frappe.db.escape((txt or "").replace("%", "")), doctype=frappe.db.escape(doctype)))
|
||||
|
||||
values = frappe.desk.reportview.execute(doctype,
|
||||
values = frappe.get_list(doctype,
|
||||
filters=filters, fields=fields,
|
||||
or_filters = or_filters, limit_start = start,
|
||||
limit_page_length=page_len,
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ scheduler_events = {
|
|||
"frappe.email.doctype.email_account.email_account.notify_unreplied",
|
||||
"frappe.utils.error.collect_error_snapshots",
|
||||
"frappe.model.utils.link_count.update_link_count",
|
||||
'frappe.model.utils.list_settings.sync_list_settings'
|
||||
],
|
||||
"daily": [
|
||||
"frappe.email.bulk.clear_outbox",
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ def install_db(root_login="root", root_password=None, db_name=None, source_sql=N
|
|||
remove_missing_apps()
|
||||
|
||||
create_auth_table()
|
||||
create_list_settings_table()
|
||||
|
||||
frappe.flags.in_install_db = False
|
||||
|
||||
def get_current_host():
|
||||
|
|
@ -72,6 +74,14 @@ def create_auth_table():
|
|||
`password` VARCHAR(180) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8""")
|
||||
|
||||
def create_list_settings_table():
|
||||
frappe.db.sql_ddl("""create table if not exists __ListSettings (
|
||||
`user` VARCHAR(180) NOT NULL,
|
||||
`doctype` VARCHAR(180) NOT NULL,
|
||||
`data` TEXT,
|
||||
UNIQUE(user, doctype)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8""")
|
||||
|
||||
def import_db_from_sql(source_sql, verbose):
|
||||
if verbose: print "Starting database import..."
|
||||
db_name = frappe.conf.db_name
|
||||
|
|
@ -342,7 +352,7 @@ def check_if_ready_for_barracuda():
|
|||
def extract_sql_gzip(sql_gz_path):
|
||||
success = -1
|
||||
try:
|
||||
success = subprocess.check_output(['gzip', '-d', '-v', '-f', sql_gz_path])
|
||||
subprocess.check_output(['gzip', '-d', '-v', '-f', sql_gz_path])
|
||||
except Exception as subprocess.CalledProcessError:
|
||||
print subprocess.CalledProcessError.output
|
||||
finally:
|
||||
|
|
|
|||
30
frappe/model/utils/list_settings.py
Normal file
30
frappe/model/utils/list_settings.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import frappe, json
|
||||
|
||||
def get_list_settings(doctype, for_update=False):
|
||||
list_settings = frappe.cache().hget('_list_settings',
|
||||
'{0}::{1}'.format(doctype, frappe.session.user))
|
||||
|
||||
if list_settings is None:
|
||||
list_settings = frappe.db.sql('''select * from __ListSettings
|
||||
where user=%s and doctype=%s''', (frappe.session.user, doctype), as_dict=True)
|
||||
list_settings = list_settings and list_settings[0] or '{}'
|
||||
|
||||
if not for_update:
|
||||
update_list_settings(doctype, list_settings)
|
||||
|
||||
return list_settings
|
||||
|
||||
def update_list_settings(doctype, list_settings):
|
||||
'''update list settings in cache'''
|
||||
current = json.loads(get_list_settings(doctype, for_update = True))
|
||||
current.update(list_settings)
|
||||
|
||||
frappe.cache().hset('_list_settings', '{0}::{1}'.format(doctype, frappe.session.user),
|
||||
json.dumps(current))
|
||||
|
||||
def sync_list_settings():
|
||||
'''Sync from cache to database (called asynchronously via the browser)'''
|
||||
for key, data in frappe.cache().hgetall('_list_settings').iteritems():
|
||||
doctype, user = key.split('::')
|
||||
frappe.db.sql('''insert into __ListSettings (user, doctype, data) values (%s, %s, %s)
|
||||
on duplicate key update data=%s''', (user, doctype, data, data))
|
||||
|
|
@ -126,3 +126,4 @@ frappe.patches.v7_0.set_user_fullname
|
|||
frappe.patches.v7_0.desktop_icons_hidden_by_admin_as_blocked
|
||||
frappe.patches.v7_0.add_communication_in_doc
|
||||
frappe.patches.v7_0.update_send_after_in_bulk_email
|
||||
frappe.patches.v7_0.setup_list_settings
|
||||
|
|
|
|||
16
frappe/patches/v7_0/setup_list_settings.py
Normal file
16
frappe/patches/v7_0/setup_list_settings.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
from frappe.installer import create_list_settings_table
|
||||
from frappe.model.utils.list_settings import update_list_settings
|
||||
import frappe, json
|
||||
|
||||
def execute():
|
||||
create_list_settings_table()
|
||||
|
||||
for user in frappe.db.get_all('User', {'user_type': 'System User'}):
|
||||
defaults = frappe.defaults.get_defaults_for(user.name)
|
||||
for key, value in defaults.iteritems():
|
||||
if key.startswith('_list_settings:'):
|
||||
doctype = key.replace('_list_settings:', '')
|
||||
columns = ['`tab{1}`.`{0}`'.format(*c) for c in json.loads(value)]
|
||||
|
||||
update_list_settings(doctype, {'fields': columns})
|
||||
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
|
||||
}
|
||||
a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
|
||||
}
|
||||
a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
|
||||
}
|
||||
a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -289,3 +289,4 @@ frappe.dom.set_box_shadow = function(ele, spread) {
|
|||
return this;
|
||||
}
|
||||
})(jQuery);
|
||||
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
|
|||
this.can_delete = frappe.model.can_delete(this.doctype);
|
||||
this.meta = locals.DocType[this.doctype];
|
||||
this.$page.find('.frappe-list-area').empty(),
|
||||
this.init_list_settings();
|
||||
this.setup_listview();
|
||||
this.init_list(false);
|
||||
this.init_menu();
|
||||
|
|
@ -194,14 +195,19 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
|
|||
|
||||
init_filters: function() {
|
||||
var me = this;
|
||||
if(this.listview.settings.filters) {
|
||||
$.each(this.listview.settings.filters, function(i, f) {
|
||||
var set_filters = function(filters) {
|
||||
$.each(filters, function(i, f) {
|
||||
if(f.length===3) {
|
||||
f = [me.doctype, f[0], f[1], f[2]]
|
||||
}
|
||||
me.filter_list.add_filter(f[0], f[1], f[2], f[3]);
|
||||
});
|
||||
}
|
||||
if(this.list_settings.filters) {
|
||||
set_filters(this.list_settings.filters);
|
||||
} else if(this.listview.settings.filters) {
|
||||
set_filters(this.listview.settings.filters);
|
||||
}
|
||||
},
|
||||
|
||||
init_sort_selector: function() {
|
||||
|
|
@ -210,6 +216,25 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
|
|||
if(this.listview.sort_selector) {
|
||||
args = this.listview.sort_selector;
|
||||
}
|
||||
|
||||
if(this.list_settings.order_by) {
|
||||
// last saved settings
|
||||
var order_by = this.list_settings.order_by
|
||||
|
||||
if(order_by.indexOf('`.`')!==-1) {
|
||||
// scrub table name (separted by dot), like `tabTime Log`.`modified` desc`
|
||||
order_by = order_by.split('.')[1];
|
||||
}
|
||||
|
||||
parts = order_by.split(' ');
|
||||
if(parts.length===2) {
|
||||
var fieldname = strip(parts[0], '`');
|
||||
args = {
|
||||
sort_by: fieldname,
|
||||
sort_order: parts[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
this.sort_selector = new frappe.ui.SortSelector({
|
||||
parent: this.wrapper.find('.list-filters'),
|
||||
doctype: this.doctype,
|
||||
|
|
@ -234,7 +259,7 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
|
|||
setup_listview: function() {
|
||||
this.listview = frappe.views.get_listview(this.doctype, this);
|
||||
this.wrapper = this.$page.find('.frappe-list-area');
|
||||
this.page_length = 20;
|
||||
this.page_length = this.list_settings.limit || 20;
|
||||
this.allow_delete = true;
|
||||
},
|
||||
init_list: function(auto_run) {
|
||||
|
|
@ -242,6 +267,7 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
|
|||
// init list
|
||||
this.make({
|
||||
method: 'frappe.desk.reportview.get',
|
||||
save_list_settings: true,
|
||||
get_args: this.get_args,
|
||||
parent: this.wrapper,
|
||||
freeze: true,
|
||||
|
|
@ -278,10 +304,8 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
|
|||
this.listview.settings.refresh(this);
|
||||
}
|
||||
|
||||
if(frappe.route_options) {
|
||||
this.set_route_options();
|
||||
this.run();
|
||||
} else if(this.dirty) {
|
||||
this.set_filters_before_run();
|
||||
if(this.dirty) {
|
||||
this.run();
|
||||
} else {
|
||||
if(new Date() - (this.last_updated_on || 0) > 30000) {
|
||||
|
|
@ -291,57 +315,69 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
|
|||
}
|
||||
},
|
||||
|
||||
set_route_options: function() {
|
||||
set_filters_before_run: function() {
|
||||
// set filters from frappe.route_options
|
||||
// before switching pages, frappe.route_options can have pre-set filters
|
||||
// for the list view
|
||||
var me = this;
|
||||
me.filter_list.clear_filters();
|
||||
$.each(frappe.route_options, function(key, value) {
|
||||
var doctype = null;
|
||||
|
||||
// if `Child DocType.fieldname`
|
||||
if (key.indexOf(".")!==-1) {
|
||||
doctype = key.split(".")[0];
|
||||
key = key.split(".")[1];
|
||||
}
|
||||
if(frappe.route_options) {
|
||||
this.filter_list.clear_filters();
|
||||
$.each(frappe.route_options, function(key, value) {
|
||||
var doctype = null;
|
||||
|
||||
// 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.
|
||||
// if `Child DocType.fieldname`
|
||||
if (key.indexOf(".")!==-1) {
|
||||
doctype = key.split(".")[0];
|
||||
key = key.split(".")[1];
|
||||
}
|
||||
|
||||
// we can search all tables for mapping the doctype
|
||||
if(!doctype) {
|
||||
if(in_list(frappe.model.std_fields_list, key)) {
|
||||
// standard
|
||||
doctype = me.doctype;
|
||||
} else if(frappe.meta.has_field(me.doctype, key)) {
|
||||
// found in parent
|
||||
doctype = me.doctype;
|
||||
} else {
|
||||
frappe.meta.get_table_fields(me.doctype).every(function(d) {
|
||||
if(frappe.meta.has_field(d.options, key)) {
|
||||
doctype = d.options;
|
||||
return false;
|
||||
// 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) {
|
||||
if(in_list(frappe.model.std_fields_list, key)) {
|
||||
// standard
|
||||
doctype = me.doctype;
|
||||
} else if(frappe.meta.has_field(me.doctype, key)) {
|
||||
// found in parent
|
||||
doctype = me.doctype;
|
||||
} else {
|
||||
frappe.meta.get_table_fields(me.doctype).every(function(d) {
|
||||
if(frappe.meta.has_field(d.options, key)) {
|
||||
doctype = d.options;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if(!doctype) {
|
||||
frappe.msgprint(__('Warning: Unable to find {0} in any table related to {1}', [
|
||||
key, __(me.doctype)]));
|
||||
}
|
||||
});
|
||||
|
||||
if(!doctype) {
|
||||
frappe.msgprint(__('Warning: Unable to find {0} in any table related to {1}', [
|
||||
key, __(me.doctype)]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(doctype) {
|
||||
if($.isArray(value)) {
|
||||
me.filter_list.add_filter(doctype, key, value[0], value[1]);
|
||||
} else {
|
||||
me.filter_list.add_filter(doctype, key, "=", value);
|
||||
if(doctype) {
|
||||
if($.isArray(value)) {
|
||||
me.filter_list.add_filter(doctype, key, value[0], value[1]);
|
||||
} else {
|
||||
me.filter_list.add_filter(doctype, key, "=", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
frappe.route_options = null;
|
||||
});
|
||||
frappe.route_options = null;
|
||||
this.dirty = true;
|
||||
} else if(this.list_settings && this.list_settings.filters
|
||||
&& this.list_settings.updated_on != this.list_settings_updated_on) {
|
||||
// update remembered list settings
|
||||
this.filter_list.clear_filters();
|
||||
this.list_settings.filters.forEach(function(f) {
|
||||
me.filter_list.add_filter(f[0], f[1], f[2], f[3]);
|
||||
});
|
||||
this.dirty = true;
|
||||
}
|
||||
},
|
||||
|
||||
run: function(more) {
|
||||
|
|
@ -390,6 +426,8 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
|
|||
if(this.listview.settings.post_render) {
|
||||
this.listview.settings.post_render(this);
|
||||
}
|
||||
|
||||
this.list_settings_updated_on = this.list_settings.updated_on;
|
||||
},
|
||||
|
||||
make_no_result: function() {
|
||||
|
|
|
|||
|
|
@ -325,6 +325,26 @@ frappe.utils = {
|
|||
return list.reduce(function(previous_value, current_value) { return flt(previous_value) + flt(current_value); }, 0.0);
|
||||
},
|
||||
|
||||
arrays_equal: function(arr1, arr2) {
|
||||
if (!arr1 || !arr2) {
|
||||
return false;
|
||||
}
|
||||
if (arr1.length != arr2.length) {
|
||||
return false;
|
||||
}
|
||||
for (var i = 0; i < arr1.length; i++) {
|
||||
if ($.isArray(arr1[i])) {
|
||||
if (!frappe.utils.arrays_equal(arr1[i], arr2[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (arr1[i] !== arr2[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
intersection: function(a, b) {
|
||||
// from stackoverflow: http://stackoverflow.com/questions/1885557/simplest-code-for-array-intersection-in-javascript
|
||||
/* finds the intersection of
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ $.extend(frappe.model, {
|
|||
|
||||
new_names: {},
|
||||
events: {},
|
||||
list_settings: {},
|
||||
|
||||
init: function() {
|
||||
// setup refresh if the document is updated somewhere else
|
||||
|
|
@ -113,6 +114,12 @@ $.extend(frappe.model, {
|
|||
}
|
||||
frappe.model.init_doctype(doctype);
|
||||
frappe.defaults.set_user_permissions(r.user_permissions);
|
||||
|
||||
if(r.list_settings) {
|
||||
// remember filters and other settings from last view
|
||||
frappe.model.list_settings[doctype] = JSON.parse(r.list_settings);
|
||||
frappe.model.list_settings[doctype].updated_on = moment().toString();
|
||||
}
|
||||
callback && callback(r);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ frappe.ui.Listing = Class.extend({
|
|||
this.opts.no_result_message = __('Nothing to show');
|
||||
}
|
||||
if(!this.opts.page_length) {
|
||||
this.opts.page_length = 20;
|
||||
this.opts.page_length = this.list_settings ? (this.list_settings.limit || 20) : 20;
|
||||
}
|
||||
this.opts._more = __("More");
|
||||
},
|
||||
|
|
@ -174,14 +174,18 @@ frappe.ui.Listing = Class.extend({
|
|||
if(this.onreset) this.onreset();
|
||||
}
|
||||
|
||||
if(!me.opts.no_loading)
|
||||
if(!me.opts.no_loading) {
|
||||
me.set_working(true);
|
||||
}
|
||||
|
||||
var args = this.get_call_args();
|
||||
this.save_list_settings_locally(args);
|
||||
|
||||
return frappe.call({
|
||||
method: this.opts.method || 'frappe.desk.query_builder.runquery',
|
||||
type: "GET",
|
||||
freeze: (this.opts.freeze != undefined ? this.opts.freeze : true),
|
||||
args: this.get_call_args(),
|
||||
args: args,
|
||||
callback: function(r) {
|
||||
if(!me.opts.no_loading)
|
||||
me.set_working(false);
|
||||
|
|
@ -191,6 +195,39 @@ frappe.ui.Listing = Class.extend({
|
|||
no_spinner: this.opts.no_loading
|
||||
});
|
||||
},
|
||||
save_list_settings_locally: function(args) {
|
||||
if(this.opts.save_list_settings && this.doctype && !this.docname) {
|
||||
// save list settings locally
|
||||
list_settings = frappe.model.list_settings[this.doctype];
|
||||
|
||||
var different = false;
|
||||
|
||||
if(!frappe.utils.arrays_equal(args.filters, list_settings.filters)) {
|
||||
// settings are dirty if filters change
|
||||
list_settings.filters = args.filters || [];
|
||||
different = true;
|
||||
}
|
||||
|
||||
if(list_settings.order_by !== args.order_by) {
|
||||
list_settings.order_by = args.order_by;
|
||||
different = true;
|
||||
}
|
||||
|
||||
if(list_settings.limit != args.limit_page_length) {
|
||||
list_settings.limit = args.limit_page_length || 20
|
||||
different = true;
|
||||
}
|
||||
|
||||
// save fields in list settings
|
||||
if(args.save_list_settings_fields) {
|
||||
list_settings.fields = args.fields;
|
||||
};
|
||||
|
||||
if(different) {
|
||||
list_settings.updated_on = moment().toString();
|
||||
}
|
||||
}
|
||||
},
|
||||
set_working: function(flag) {
|
||||
this.$w.find('.img-load').toggle(flag);
|
||||
},
|
||||
|
|
@ -325,5 +362,12 @@ frappe.ui.Listing = Class.extend({
|
|||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
},
|
||||
init_list_settings: function() {
|
||||
if(frappe.model.list_settings[this.doctype]) {
|
||||
this.list_settings = frappe.model.list_settings[this.doctype];
|
||||
} else {
|
||||
this.list_settings = {};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -42,11 +42,12 @@ frappe.ui.SortSelector = Class.extend({
|
|||
},
|
||||
prepare_args: function() {
|
||||
var me = this;
|
||||
// make from doctype if not given
|
||||
if(!this.args && this.doctype) {
|
||||
this.setup_from_doctype();
|
||||
if(!this.args) {
|
||||
this.args = {};
|
||||
}
|
||||
|
||||
this.setup_from_doctype();
|
||||
|
||||
// if label is missing, add from options
|
||||
if(this.args.sort_by && !this.args.sort_by_label) {
|
||||
this.args.options.every(function(o) {
|
||||
|
|
@ -57,67 +58,73 @@ frappe.ui.SortSelector = Class.extend({
|
|||
}
|
||||
},
|
||||
setup_from_doctype: function() {
|
||||
var args = {};
|
||||
var me = this;
|
||||
var meta = frappe.get_meta(this.doctype);
|
||||
if(meta.sort_field) {
|
||||
if(meta.sort_field.indexOf(',')!==-1) {
|
||||
parts = meta.sort_field.split(',')[0].split(' ');
|
||||
args.sort_by = parts[0];
|
||||
args.sort_order = parts[1];
|
||||
|
||||
if(!this.args.sort_by) {
|
||||
if(meta.sort_field) {
|
||||
if(meta.sort_field.indexOf(',')!==-1) {
|
||||
parts = meta.sort_field.split(',')[0].split(' ');
|
||||
this.args.sort_by = parts[0];
|
||||
this.args.sort_order = parts[1];
|
||||
} else {
|
||||
this.args.sort_by = meta.sort_field;
|
||||
this.args.sort_order = meta.sort_order.toLowerCase();
|
||||
}
|
||||
} else {
|
||||
args.sort_by = meta.sort_field;
|
||||
args.sort_order = meta.sort_order.toLowerCase();
|
||||
// default
|
||||
this.args.sort_by = 'modified';
|
||||
this.args.sort_order = 'desc';
|
||||
}
|
||||
} else {
|
||||
// default
|
||||
args.sort_by = 'modified';
|
||||
args.sort_order = 'desc';
|
||||
}
|
||||
args.sort_by_label = this.get_label(args.sort_by);
|
||||
|
||||
// default options
|
||||
args._options = [
|
||||
{'fieldname': 'modified'},
|
||||
]
|
||||
|
||||
// title field
|
||||
if(meta.title_field) {
|
||||
args._options.push({'fieldname': meta.title_field});
|
||||
}
|
||||
|
||||
// bold or mandatory
|
||||
meta.fields.forEach(function(df) {
|
||||
if(df.mandatory || df.bold) {
|
||||
args._options.push({fieldname: df.fieldname, label: df.label});
|
||||
}
|
||||
});
|
||||
if(!this.args.sort_by_label) {
|
||||
this.args.sort_by_label = this.get_label(this.args.sort_by);
|
||||
}
|
||||
|
||||
args._options.push({'fieldname': 'name'});
|
||||
args._options.push({'fieldname': 'creation'});
|
||||
args._options.push({'fieldname': 'idx'});
|
||||
if(!this.args.options) {
|
||||
// default options
|
||||
var _options = [
|
||||
{'fieldname': 'modified'},
|
||||
]
|
||||
|
||||
// de-duplicate
|
||||
var added = [];
|
||||
args.options = [];
|
||||
args._options.forEach(function(o) {
|
||||
if(added.indexOf(o.fieldname)===-1) {
|
||||
args.options.push(o);
|
||||
added.push(o.fieldname);
|
||||
// title field
|
||||
if(meta.title_field) {
|
||||
_options.push({'fieldname': meta.title_field});
|
||||
}
|
||||
});
|
||||
|
||||
// add missing labels
|
||||
args.options.forEach(function(o) {
|
||||
if(!o.label) {
|
||||
o.label = me.get_label(o.fieldname);
|
||||
}
|
||||
});
|
||||
// bold or mandatory
|
||||
meta.fields.forEach(function(df) {
|
||||
if(df.mandatory || df.bold) {
|
||||
_options.push({fieldname: df.fieldname, label: df.label});
|
||||
}
|
||||
});
|
||||
|
||||
_options.push({'fieldname': 'name'});
|
||||
_options.push({'fieldname': 'creation'});
|
||||
_options.push({'fieldname': 'idx'});
|
||||
|
||||
// de-duplicate
|
||||
var added = [];
|
||||
this.args.options = [];
|
||||
_options.forEach(function(o) {
|
||||
if(added.indexOf(o.fieldname)===-1) {
|
||||
me.args.options.push(o);
|
||||
added.push(o.fieldname);
|
||||
}
|
||||
});
|
||||
|
||||
// add missing labels
|
||||
this.args.options.forEach(function(o) {
|
||||
if(!o.label) {
|
||||
o.label = me.get_label(o.fieldname);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// set default
|
||||
this.args = args;
|
||||
this.sort_by = args.sort_by;
|
||||
this.sort_order = args.sort_order;
|
||||
this.sort_by = this.args.sort_by;
|
||||
this.sort_order = this.args.sort_order;
|
||||
},
|
||||
get_label: function(fieldname) {
|
||||
if(fieldname==='idx') {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ frappe.views.ReportViewPage = Class.extend({
|
|||
me.parent.reportview.run();
|
||||
});
|
||||
} else {
|
||||
me.parent.reportview.set_route_filters();
|
||||
me.parent.reportview.set_route_filters(true);
|
||||
me.parent.reportview.run();
|
||||
}
|
||||
});
|
||||
|
|
@ -89,10 +89,12 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
|
|||
var me = this;
|
||||
this.page = this.parent.page;
|
||||
this.page_title = __('Report')+ ': ' + __(this.docname ? (this.doctype + ' - ' + this.docname) : this.doctype);
|
||||
this.page.set_title(this.page_title)
|
||||
this.page.set_title(this.page_title);
|
||||
this.init_list_settings();
|
||||
this.make({
|
||||
page: this.parent.page,
|
||||
method: 'frappe.desk.reportview.get',
|
||||
save_list_settings: true,
|
||||
get_args: this.get_args,
|
||||
parent: this.page.main,
|
||||
start: 0,
|
||||
|
|
@ -130,8 +132,17 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
|
|||
|
||||
set_init_columns: function() {
|
||||
// pre-select mandatory columns
|
||||
var columns = frappe.defaults.get_default("_list_settings:" + this.doctype);
|
||||
if(!columns) {
|
||||
var me = this;
|
||||
var columns = [];
|
||||
if(this.list_settings.fields) {
|
||||
this.list_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_filter || df.in_list_view) && df.fieldname!='naming_series'
|
||||
|
|
@ -168,7 +179,7 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
|
|||
if(opts.sort_order_next) this.sort_order_next_select.val(opts.sort_order_next);
|
||||
},
|
||||
|
||||
set_route_filters: function() {
|
||||
set_route_filters: function(first_load) {
|
||||
var me = this;
|
||||
if(frappe.route_options) {
|
||||
me.filter_list.clear_filters();
|
||||
|
|
@ -177,7 +188,16 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
|
|||
});
|
||||
frappe.route_options = null;
|
||||
return true;
|
||||
} else if(this.list_settings && this.list_settings.filters &&
|
||||
(this.list_settings.updated_on != this.list_settings_updated_on)) {
|
||||
// list settings (previous settings)
|
||||
this.filter_list.clear_filters();
|
||||
$.each(this.list_settings.filters, function(i, f) {
|
||||
me.filter_list.add_filter(f[0], f[1], f[2], f[3]);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
this.list_settings_updated_on = this.list_settings.updated_on;
|
||||
},
|
||||
|
||||
setup_print: function() {
|
||||
|
|
@ -196,6 +216,7 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
|
|||
fields: $.map(this.columns, function(v) { return me.get_full_column_name(v) }),
|
||||
order_by: this.get_order_by(),
|
||||
filters: this.filter_list.get_filters(),
|
||||
save_list_settings_fields: 1,
|
||||
with_childnames: 1
|
||||
}
|
||||
},
|
||||
|
|
@ -229,6 +250,15 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
|
|||
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;
|
||||
|
|
@ -706,7 +736,6 @@ frappe.ui.ColumnPicker = Class.extend({
|
|||
: null;
|
||||
});
|
||||
|
||||
frappe.defaults.set_default("_list_settings:" + this.doctype, columns);
|
||||
this.list.columns = columns;
|
||||
this.list.run();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,13 @@
|
|||
// font-family: "Open Sans", "Helvetica", Arial, "sans-serif";
|
||||
// }
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont,
|
||||
"Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell",
|
||||
"Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# MIT License. See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import redis, frappe, re, copy
|
||||
import redis, frappe, re
|
||||
import cPickle as pickle
|
||||
from frappe.utils import cstr
|
||||
|
||||
|
|
@ -126,6 +126,10 @@ class RedisWrapper(redis.Redis):
|
|||
except redis.exceptions.ConnectionError:
|
||||
pass
|
||||
|
||||
def hgetall(self, name):
|
||||
return {key: pickle.loads(value) for key, value in
|
||||
super(redis.Redis, self).hgetall(self.make_key(name)).iteritems()}
|
||||
|
||||
def hget(self, name, key, generator=None):
|
||||
if not name in frappe.local.cache:
|
||||
frappe.local.cache[name] = {}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue