@@ -14,7 +14,7 @@ import os, sys, importlib, inspect, json | |||||
from .exceptions import * | from .exceptions import * | ||||
from .utils.jinja import get_jenv, get_template, render_template | from .utils.jinja import get_jenv, get_template, render_template | ||||
__version__ = '8.1.4' | |||||
__version__ = '8.2.0' | |||||
__title__ = "Frappe Framework" | __title__ = "Frappe Framework" | ||||
local = Local() | local = Local() | ||||
@@ -45,13 +45,13 @@ frappe.PermissionEngine = Class.extend({ | |||||
var me = this; | var me = this; | ||||
this.doctype_select | this.doctype_select | ||||
= this.wrapper.page.add_select(__("Document Types"), | = this.wrapper.page.add_select(__("Document Types"), | ||||
[{value: "", label: __("Select Document Type")+"..."}].concat(this.options.doctypes)) | |||||
[{value: "", label: __("Select Document Type")+"..."}].concat(this.options.doctypes.sort())) | |||||
.change(function() { | .change(function() { | ||||
frappe.set_route("permission-manager", $(this).val()); | frappe.set_route("permission-manager", $(this).val()); | ||||
}); | }); | ||||
this.role_select | this.role_select | ||||
= this.wrapper.page.add_select(__("Roles"), | = this.wrapper.page.add_select(__("Roles"), | ||||
[__("Select Role")+"..."].concat(this.options.roles)) | |||||
[__("Select Role")+"..."].concat(this.options.roles.sort())) | |||||
.change(function() { | .change(function() { | ||||
me.refresh(); | me.refresh(); | ||||
}); | }); | ||||
@@ -184,4 +184,5 @@ frappe.patches.v8_0.rename_print_to_printing | |||||
frappe.patches.v7_1.disabled_print_settings_for_custom_print_format | frappe.patches.v7_1.disabled_print_settings_for_custom_print_format | ||||
frappe.patches.v8_0.update_desktop_icons | frappe.patches.v8_0.update_desktop_icons | ||||
frappe.patches.v8_0.update_gender_and_salutation | frappe.patches.v8_0.update_gender_and_salutation | ||||
execute:frappe.db.sql('update tabReport set module="Desk" where name="ToDo"') | |||||
execute:frappe.db.sql('update tabReport set module="Desk" where name="ToDo"') | |||||
frappe.patches.v8_1.enable_allow_error_traceback_in_system_settings |
@@ -0,0 +1,15 @@ | |||||
# Copyright (c) 2017, Frappe and Contributors | |||||
# License: GNU General Public License v3. See license.txt | |||||
from __future__ import unicode_literals | |||||
import frappe | |||||
def execute(): | |||||
""" enable the allow_enable_traceback property in system settings """ | |||||
frappe.reload_doc("core", "doctype", "system_settings") | |||||
doc = frappe.get_doc("System Settings", "System Settings") | |||||
doc.allow_error_traceback = 1 | |||||
doc.flags.ignore_permissions=True | |||||
doc.flags.ignore_mandatory=True | |||||
doc.save() |
@@ -216,7 +216,6 @@ | |||||
"public/css/list.css", | "public/css/list.css", | ||||
"public/css/calendar.css", | "public/css/calendar.css", | ||||
"public/css/role_editor.css", | "public/css/role_editor.css", | ||||
"public/css/filter_dashboard.css", | |||||
"public/css/gantt.css" | "public/css/gantt.css" | ||||
], | ], | ||||
"js/list.min.js": [ | "js/list.min.js": [ | ||||
@@ -227,9 +226,6 @@ | |||||
"public/js/frappe/model/indicator.js", | "public/js/frappe/model/indicator.js", | ||||
"public/js/frappe/ui/filters/filters.js", | "public/js/frappe/ui/filters/filters.js", | ||||
"public/js/frappe/ui/filters/edit_filter.html", | "public/js/frappe/ui/filters/edit_filter.html", | ||||
"public/js/frappe/ui/filters/filter_dashboard.html", | |||||
"public/js/frappe/ui/filters/filter_dashboard_value.html", | |||||
"public/js/frappe/ui/filters/filter_dashboard_head.html", | |||||
"public/js/frappe/ui/tags.js", | "public/js/frappe/ui/tags.js", | ||||
"public/js/frappe/ui/like.js", | "public/js/frappe/ui/like.js", | ||||
"public/js/frappe/ui/liked_by.html", | "public/js/frappe/ui/liked_by.html", | ||||
@@ -1,106 +0,0 @@ | |||||
/* the element that this class is applied to, should have a max width for this to work*/ | |||||
.date-range-picker { | |||||
font-size: 85%; | |||||
} | |||||
.filter_area { | |||||
margin: 0 -15px; | |||||
} | |||||
.filter-dashboard-wrapper { | |||||
display: none; | |||||
padding-bottom: 0px; | |||||
} | |||||
.list-filter-dashboard { | |||||
border-top: 1px solid #d1d8dd; | |||||
overflow-x: scroll; | |||||
overflow-y: hidden; | |||||
} | |||||
.filter-header { | |||||
border-bottom: 1px solid #d1d8dd; | |||||
background-color: #F7FAFC; | |||||
padding: 8px 15px; | |||||
} | |||||
.filter-header .search-dropdown { | |||||
margin-top: -20px; | |||||
margin-right: 20px; | |||||
} | |||||
.filter-header .search-dropdown .octicon { | |||||
font-size: 14px; | |||||
} | |||||
.filter-header .sort-dropdown { | |||||
margin-top: -16px; | |||||
} | |||||
.filter-header .dropdown-menu { | |||||
min-width: 120px !important; | |||||
} | |||||
.filter-label { | |||||
margin: 0px; | |||||
color: #8D99A6; | |||||
} | |||||
.filter-dashboard-items { | |||||
height: 187px; | |||||
margin-right: -2px; | |||||
display: flex; | |||||
} | |||||
.filter-dash-item { | |||||
flex-grow: 1; | |||||
min-width: 180px; | |||||
float: left; | |||||
height: 187px; | |||||
border-right: 1px solid #d1d8dd; | |||||
} | |||||
.filter-input { | |||||
padding: 10px 15px; | |||||
} | |||||
.filter-input input { | |||||
height: 25px; | |||||
} | |||||
.filter-item-value, | |||||
.stat-no-records { | |||||
padding: 0px 15px; | |||||
} | |||||
.filter-dash-item:after { | |||||
/*top:-10px;*/ | |||||
} | |||||
.filter-sort { | |||||
font-size: 1.5em; | |||||
} | |||||
.filter-sort-item a { | |||||
padding: 8px !important; | |||||
} | |||||
.filter-sort-item:hover { | |||||
color: #212a33; | |||||
text-decoration: none; | |||||
background-color: #F0F4F7; | |||||
} | |||||
.filter-stat { | |||||
overflow-y: auto; | |||||
overflow-x: hidden; | |||||
height: 158px; | |||||
margin: 0px; | |||||
} | |||||
.selected { | |||||
vertical-align: 10%; | |||||
font-size: 1.2em; | |||||
} | |||||
.filter-sort-item > label { | |||||
float: right; | |||||
} | |||||
.filter-stat-link > .badge { | |||||
position: absolute; | |||||
float: right; | |||||
margin-right: 10px; | |||||
margin-top: 2px; | |||||
font-size: 11px; | |||||
} | |||||
.filter-stat-link > .stat { | |||||
width: 130px; | |||||
display: inline-block; | |||||
white-space: nowrap; | |||||
overflow: hidden; | |||||
text-overflow: ellipsis; | |||||
max-width: 100%; | |||||
vertical-align: middle; | |||||
} | |||||
.filter-dash-controls > .filter-label { | |||||
padding-bottom: 5px; | |||||
} |
@@ -194,7 +194,7 @@ h6.uppercase, | |||||
} | } | ||||
.form-section:not(:last-child), | .form-section:not(:last-child), | ||||
.form-inner-toolbar { | .form-inner-toolbar { | ||||
border-bottom: 1px solid #EBEFF2; | |||||
border-bottom: 1px solid #d1d8dd; | |||||
} | } | ||||
.empty-section { | .empty-section { | ||||
display: none !important; | display: none !important; | ||||
@@ -41,7 +41,7 @@ | |||||
color: #8D99A6; | color: #8D99A6; | ||||
} | } | ||||
.filter-box { | .filter-box { | ||||
border-top: 1px solid #d1d8dd; | |||||
border-bottom: 1px solid #d1d8dd; | |||||
padding: 10px 15px 3px; | padding: 10px 15px 3px; | ||||
} | } | ||||
.filter-box .remove-filter { | .filter-box .remove-filter { | ||||
@@ -253,6 +253,9 @@ | |||||
color: #36414C; | color: #36414C; | ||||
font-size: 11px; | font-size: 11px; | ||||
} | } | ||||
.page-form .awesomplete > ul { | |||||
min-width: 300px; | |||||
} | |||||
.taggle_input { | .taggle_input { | ||||
padding: 0; | padding: 0; | ||||
margin-top: 3px; | margin-top: 3px; | ||||
@@ -97,14 +97,21 @@ | |||||
} | } | ||||
.page-form { | .page-form { | ||||
margin: 0px; | margin: 0px; | ||||
padding-top: 15px; | |||||
padding-right: 15px; | |||||
padding-top: 10px; | |||||
border-bottom: 1px solid #d1d8dd; | border-bottom: 1px solid #d1d8dd; | ||||
background-color: #F7FAFC; | |||||
} | } | ||||
.page-form .form-group { | .page-form .form-group { | ||||
margin-bottom: 15px; | |||||
padding-right: 0px; | |||||
margin-bottom: 10px; | |||||
} | } | ||||
.page-form .checkbox { | .page-form .checkbox { | ||||
margin-top: 2px; | |||||
margin-top: 4px; | |||||
margin-bottom: 4px; | |||||
} | |||||
.page-form .checkbox .help-box { | |||||
display: none; | |||||
} | } | ||||
select.input-sm { | select.input-sm { | ||||
line-height: 1.2em !important; | line-height: 1.2em !important; | ||||
@@ -290,6 +290,7 @@ frappe.views.ListView = frappe.ui.BaseList.extend({ | |||||
}, | }, | ||||
init_filters: function () { | init_filters: function () { | ||||
this.make_standard_filters(); | |||||
this.filter_list = new frappe.ui.FilterList({ | this.filter_list = new frappe.ui.FilterList({ | ||||
base_list: this, | base_list: this, | ||||
parent: this.wrapper.find('.list-filters').show(), | parent: this.wrapper.find('.list-filters').show(), | ||||
@@ -412,9 +413,6 @@ frappe.views.ListView = frappe.ui.BaseList.extend({ | |||||
execute_run: function () { | execute_run: function () { | ||||
if (this.dirty) { | if (this.dirty) { | ||||
this.run(); | this.run(); | ||||
if (this.clean_dash != true) { | |||||
this.filter_list.reload_stats(); | |||||
} | |||||
} else { | } else { | ||||
if (new Date() - (this.last_updated_on || 0) > 30000) { | if (new Date() - (this.last_updated_on || 0) > 30000) { | ||||
// older than 5 mins, refresh | // older than 5 mins, refresh | ||||
@@ -504,7 +502,6 @@ frappe.views.ListView = frappe.ui.BaseList.extend({ | |||||
this.last_updated_on = new Date(); | this.last_updated_on = new Date(); | ||||
this.dirty = false; | this.dirty = false; | ||||
this.clean_dash = false; | |||||
// set a fresh so that multiple refreshes do not happen | // set a fresh so that multiple refreshes do not happen | ||||
// at the same time. This is true when deleting. | // at the same time. This is true when deleting. | ||||
// AJAX response will try to refresh and list_update notification | // AJAX response will try to refresh and list_update notification | ||||
@@ -555,7 +552,7 @@ frappe.views.ListView = frappe.ui.BaseList.extend({ | |||||
var order_by = ''; | var order_by = ''; | ||||
if(this.sort_selector) { | if(this.sort_selector) { | ||||
// get order_by from sort_selector | // get order_by from sort_selector | ||||
order_by = $.format('`tab{0}`.`{1}` {2}', | |||||
order_by = $.format('`tab{0}`.`{1}` {2}', | |||||
[this.doctype, this.sort_selector.sort_by, this.sort_selector.sort_order]); | [this.doctype, this.sort_selector.sort_by, this.sort_selector.sort_order]); | ||||
} else { | } else { | ||||
order_by = this.list_renderer.order_by; | order_by = this.list_renderer.order_by; | ||||
@@ -67,9 +67,10 @@ frappe.route = function() { | |||||
frappe.get_route = function(route) { | frappe.get_route = function(route) { | ||||
// for app | // for app | ||||
var route = frappe.get_route_str(route).split('/') | |||||
var route = frappe.get_raw_route_str(route).split('/'); | |||||
route = $.map(route, frappe._decode_str); | |||||
var parts = route[route.length - 1].split("?"); | var parts = route[route.length - 1].split("?"); | ||||
route[route.length - 1] = parts[0]; | |||||
route[route.length - 1] = frappe._decode_str(parts[0]); | |||||
if (parts.length > 1) { | if (parts.length > 1) { | ||||
var query_params = get_query_params(parts[1]); | var query_params = get_query_params(parts[1]); | ||||
frappe.route_options = $.extend(frappe.route_options || {}, query_params); | frappe.route_options = $.extend(frappe.route_options || {}, query_params); | ||||
@@ -92,25 +93,31 @@ frappe.get_prev_route = function() { | |||||
} | } | ||||
} | } | ||||
frappe.get_route_str = function(route) { | |||||
frappe._decode_str = function(r) { | |||||
try { | |||||
return decodeURIComponent(r); | |||||
} catch(e) { | |||||
if (e instanceof URIError) { | |||||
return r; | |||||
} else { | |||||
throw e; | |||||
} | |||||
} | |||||
} | |||||
frappe.get_raw_route_str = function(route) { | |||||
if(!route) | if(!route) | ||||
route = window.location.hash; | route = window.location.hash; | ||||
if(route.substr(0,1)=='#') route = route.substr(1); | if(route.substr(0,1)=='#') route = route.substr(1); | ||||
if(route.substr(0,1)=='!') route = route.substr(1); | if(route.substr(0,1)=='!') route = route.substr(1); | ||||
route = $.map(route.split('/'), function(r) { | |||||
try { | |||||
return decodeURIComponent(r); | |||||
} catch(e) { | |||||
if (e instanceof URIError) { | |||||
return r; | |||||
} else { | |||||
throw e; | |||||
} | |||||
} | |||||
return route; | |||||
} | |||||
}).join('/'); | |||||
frappe.get_route_str = function(route) { | |||||
var rawRoute = frappe.get_raw_route_str() | |||||
route = $.map(rawRoute.split('/'), frappe._decode_str).join('/'); | |||||
return route; | return route; | ||||
} | } | ||||
@@ -163,6 +163,8 @@ frappe.ui.BaseList = Class.extend({ | |||||
}, | }, | ||||
make_filters: function () { | make_filters: function () { | ||||
this.make_standard_filters(); | |||||
this.filter_list = new frappe.ui.FilterList({ | this.filter_list = new frappe.ui.FilterList({ | ||||
base_list: this, | base_list: this, | ||||
parent: this.wrapper.find('.list-filters').show(), | parent: this.wrapper.find('.list-filters').show(), | ||||
@@ -176,6 +178,57 @@ frappe.ui.BaseList = Class.extend({ | |||||
} | } | ||||
}, | }, | ||||
make_standard_filters: function() { | |||||
var me = this; | |||||
if (this.standard_filters_added) { | |||||
return; | |||||
} | |||||
this.page.add_field({ | |||||
fieldtype:'Link', | |||||
options:this.doctype, | |||||
label:'ID', | |||||
fieldname:'name', | |||||
}); | |||||
var has_standard_filters = false; | |||||
this.meta.fields.forEach(function(df) { | |||||
if(df.in_standard_filter) { | |||||
me.page.add_field({ | |||||
fieldtype: df.fieldtype, | |||||
label: __(df.label), | |||||
options: df.options, | |||||
fieldname: df.fieldname | |||||
}); | |||||
} | |||||
}); | |||||
this.page.page_form.on('change', ':input', function() { | |||||
me.refresh(true); | |||||
}); | |||||
this.standard_filters_added = true; | |||||
}, | |||||
update_standard_filters: function(filters) { | |||||
let values = {}; | |||||
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) { | |||||
filters.push([ | |||||
me.doctype, | |||||
field.df.fieldname, | |||||
(['Data', 'Text', 'Small Text', 'Text Editor'] | |||||
.includes(field.df.fieldtype) ? 'like' : '='), | |||||
value | |||||
]) | |||||
} | |||||
} | |||||
}, | |||||
clear: function () { | clear: function () { | ||||
this.data = []; | this.data = []; | ||||
this.wrapper.find('.result-list').empty(); | this.wrapper.find('.result-list').empty(); | ||||
@@ -1,9 +0,0 @@ | |||||
<div class="filter_area"> | |||||
<div class="filter-dashboard-wrapper"> | |||||
<div class="list-filter-dashboard"> | |||||
<div class="filter-dashboard-items"> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> |
@@ -1,41 +0,0 @@ | |||||
<div class="filter-dash-item"> | |||||
<div class="filter-header"> | |||||
<h6 class="h6 filter-label" data-name="{{ field }}">{{ label }}</h6> | |||||
{% if (type!=="Date" && type!=="Datetime" && type!=="DateRange") { %} | |||||
<div class="dropdown search-dropdown hide pull-right"> | |||||
<i class="dropdown-toggle octicon octicon-search text-muted" | |||||
data-toggle="dropdown"></i> | |||||
<div class="dropdown-menu"> | |||||
<form > | |||||
<input class="search-dashboard" type="text" autocomplete="off" class="input-with-feedback form-control" /> | |||||
</form> | |||||
</div> | |||||
</div> | |||||
<div class="dropdown sort-dropdown pull-right"> | |||||
<i class="pull-right dropdown-toggle | |||||
filter-sort-active octicon octicon-gear text-muted" | |||||
data-name="{{ field }}" data-sort-by="number" data-order="desc" | |||||
data-toggle="dropdown"/> | |||||
<ul class="dropdown-menu"> | |||||
<li class="filter-sort-item" data-sort-by="alphabet" data-order="asc"> | |||||
<a>{%= __("Alphabetically Ascending") %}</a></li> | |||||
<li class="filter-sort-item" data-sort-by="alphabet" data-order="desc"> | |||||
<a>{%= __("Alphabetically Descending") %}</a></li> | |||||
<li class="filter-sort-item" data-sort-by="number" data-order="asc"> | |||||
<a>{%= __("Numerically Ascending") %}</a></li> | |||||
<li class="filter-sort-item" data-sort-by="number" data-order="desc"> | |||||
<a>{%= __("Numerically Descending") %}</a></li> | |||||
</ul> | |||||
</div> | |||||
{% } %} | |||||
</div> | |||||
{% if(type==="Date" || type==="Datetime" || type==="DateRange") { %} | |||||
<div class="filter-input filter-input-date" data-name="{{ field }}"> | |||||
</div> | |||||
{% } else { %} | |||||
<ul class="list-unstyled sidebar-menu filter-stat" data-name="{{ field }}"> | |||||
</ul> | |||||
{% } %} | |||||
</div> |
@@ -1,19 +0,0 @@ | |||||
<li class="divider" style="margin-bottom: 10px"></li> | |||||
{% if(!stat.length) { %} | |||||
<li class="stat-no-records text-muted small">{%= __("No records.") %}</li> | |||||
{% } else { | |||||
for (var i=0, l=stat.length; i < l; i++) { | |||||
var stat_label = stat[i][0]; | |||||
var stat_count = stat[i][1]; | |||||
%} | |||||
<li class="filter-item-value"> | |||||
<a class="filter-stat-link badge-hover small" data-label="{%= stat_label %}" data-field="{%= field %}" title="{%= stat_label %} "> | |||||
<span class="stat">{%= __(labels.length>0? labels[i] : stat_label) %}</span> | |||||
<span class="badge">{%= stat_count %}</span> | |||||
</a> | |||||
</li> | |||||
{% } | |||||
} %} | |||||
@@ -18,214 +18,12 @@ frappe.ui.FilterList = Class.extend({ | |||||
<div class="show_filters"> | <div class="show_filters"> | ||||
<div class="set-filters"> | <div class="set-filters"> | ||||
<button | <button | ||||
class="btn btn-default btn-xs show-filters text-muted" | |||||
style="margin-right: 10px;"> | |||||
${__("Show Filters")} | |||||
</button> | |||||
<button style="margin-left: -5px;" | |||||
style="margin-right: 10px;" | |||||
class="btn btn-default btn-xs new-filter text-muted"> | class="btn btn-default btn-xs new-filter text-muted"> | ||||
<i class="octicon octicon-plus"></i></button> | |||||
${__("Add Filter")}</button> | |||||
</div> | </div> | ||||
</div>`); | |||||
$(frappe.render_template("filter_dashboard", {})).appendTo(this.wrapper.find('.show_filters')); | |||||
//show filter dashboard | |||||
this.filters_visible = false; | |||||
this.wrapper.find('.show-filters').click(function() { | |||||
var wrapper = $(me.wrapper).find('.filter-dashboard-wrapper'); | |||||
if(!me.filters_visible) { | |||||
wrapper.toggle(true); | |||||
$(this).text(__("Hide Filters")); | |||||
me.filters_visible = true; | |||||
} else { | |||||
wrapper.toggle(false); | |||||
$(this).text(__("Show Filters")); | |||||
me.filters_visible = false; | |||||
} | |||||
}); | |||||
//add stats | |||||
$.each(frappe.meta.docfield_map[this.doctype], function(i,d) { | |||||
if (d.in_standard_filter && frappe.perm.has_perm(me.doctype, d.permlevel, "read")) { | |||||
if (d.fieldtype != 'Table') { | |||||
me.stats.push({name:d.fieldname,label:d.label,type:d.fieldtype}); | |||||
} | |||||
} | |||||
}); | |||||
me.stats = me.stats.concat([ | |||||
{name:'creation', label:'Created On', type:'Datetime'}, | |||||
{name:'modified', label:'Last Modified On', type:'Datetime'}, | |||||
{name:'owner', label:'Created By', type:'Data'}, | |||||
{name:'modified_by', label:'Last Modified By', type:'Data'}, | |||||
]); | |||||
if(frappe.model.is_submittable(me.doctype)) { | |||||
me.stats.push({name:'docstatus', label:'Document Status', type:'Data'}); | |||||
} | |||||
$.each(me.stats, function (i, v) { | |||||
me.render_dashboard_headers(v); | |||||
}); | |||||
me.reload_stats() | |||||
}, | |||||
render_dashboard_headers: function(field){ | |||||
var me = this; | |||||
var context = { | |||||
field: field.name, | |||||
label: __(field.label), | |||||
type:field.type | |||||
}; | |||||
var sidebar_stat = $(frappe.render_template("filter_dashboard_head", context)) | |||||
.appendTo(this.wrapper.find(".filter-dashboard-items")); | |||||
}, | |||||
reload_stats: function(){ | |||||
if(this.fresh) { | |||||
return; | |||||
} | |||||
// set a fresh so that multiple refreshes do not happen | |||||
// at the same time. | |||||
this.fresh = true; | |||||
setTimeout(function() { | |||||
me.fresh = false; | |||||
}, 1000); | |||||
//get stats | |||||
var me = this | |||||
return frappe.call({ | |||||
type: "GET", | |||||
method: 'frappe.desk.reportview.get_filter_dashboard_data', | |||||
args: { | |||||
stats: me.stats, | |||||
doctype: me.doctype, | |||||
filters: me.default_filters | |||||
}, | |||||
callback: function(r) { | |||||
// This gives a predictable stats order | |||||
me.wrapper.find(".filter-stat").empty(); | |||||
$.each(me.stats, function (i, v) { | |||||
me.render_filters(v, (r.message|| {})[v.name]); | |||||
}); | |||||
} | |||||
}); | |||||
}, | |||||
render_filters: function(field, stat){ | |||||
var me = this; | |||||
var sum = 0; | |||||
if (['Date', 'Datetime'].indexOf(field.type)!=-1) { | |||||
return | |||||
} | |||||
var active = this.wrapper.find(".filter-sort-active[data-name='"+__(field.name)+"']"); | |||||
// sort filters | |||||
if(active.attr('data-sort-by')==='alphabet') { | |||||
stat = (stat || []).sort(function(a, b) {return a[0].toString().toLowerCase().localeCompare(b[0].toString().toLowerCase());}); | |||||
} else { | |||||
stat = (stat || []).sort(function(a, b) { return a[1] - b[1] }); | |||||
} | |||||
if(active.attr('data-order')==='desc') { | |||||
stat = stat.reverse(); | |||||
} | |||||
//check formatting | |||||
var options = [] | |||||
var df = frappe.meta.has_field(me.doctype,field.name) | |||||
var labels =[] | |||||
if(df && df.fieldtype=='Check') { | |||||
options = [ | |||||
{value: 0, label: 'No'}, | |||||
{value: 1, label: 'Yes'} | |||||
]; | |||||
} else if(field.name=="docstatus") { | |||||
labels.length = stat.length; | |||||
options = [ | |||||
{value: 0, label: "Draft"}, | |||||
{value: 1, label: "Submitted"}, | |||||
{value: 2, label: "Cancelled"} | |||||
]; | |||||
} | |||||
if(options.length>0) { | |||||
for (var i in stat) { | |||||
for (var o in options) { | |||||
if (stat[i][0] == options[o].value) { | |||||
if (field.name=="docstatus") { | |||||
labels[i] = options[o].label | |||||
}else{ | |||||
stat[i][0] = options[o].label | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
var context = { | |||||
field: field.name, | |||||
stat: stat, | |||||
sum: sum, | |||||
label: __(field.label), | |||||
labels:labels | |||||
}; | |||||
var dashboard_filter = this.wrapper.find(".filter-stat[data-name='" + __(field.name) + "']") | |||||
dashboard_filter.html(frappe.render_template("filter_dashboard_value", context)) | |||||
.on("click", ".filter-stat-link", function() { | |||||
var fieldname = $(this).attr('data-field'); | |||||
var label = $(this).attr('data-label'); | |||||
if ((df && df.fieldtype=='Check' )|| field.name=="docstatus") { | |||||
var noduplicate = true | |||||
} | |||||
if (label=="No Data"){ | |||||
me.base_list.set_filter(fieldname, '', false, noduplicate); | |||||
}else{ | |||||
me.base_list.set_filter(fieldname, label, false, noduplicate); | |||||
} | |||||
return false; | |||||
}) | |||||
if (stat.length>5) { | |||||
//list for autocomplete | |||||
var autolist = [] | |||||
for (var i = 0; i < stat.length; i++) { | |||||
autolist.push({label: stat[i][0], value: field.name}); | |||||
} | |||||
var search_input = dashboard_filter.parent().find(".search-dashboard"); | |||||
dashboard_filter.parent().find(".search-dropdown") | |||||
.removeClass("hide") | |||||
.on("shown.bs.dropdown", function (event) { | |||||
search_input.focus(); | |||||
search_input.val("") | |||||
}); | |||||
new Awesomplete(search_input.get(0), { | |||||
minChars: 0, | |||||
maxItems: 99, | |||||
autoFirst: true, | |||||
list: autolist, | |||||
item: function(item, input) { | |||||
return $("<li>").text(item.label).get(0); | |||||
}, | |||||
replace: function(text) { | |||||
this.input.value = ''; | |||||
} | |||||
}); | |||||
search_input.on("awesomplete-select", function(e) { | |||||
var item = e.originalEvent.text; | |||||
if (item) { | |||||
if (df && df.fieldtype == 'Check') { | |||||
var noduplicate = true | |||||
} | |||||
if (item.label == "No Data") { | |||||
me.base_list.set_filter(item.value, '', false, noduplicate); | |||||
} else { | |||||
me.base_list.set_filter(item.value, item.label, false, noduplicate); | |||||
} | |||||
} | |||||
}); | |||||
} | |||||
</div> | |||||
<div class="filter_area"></div>`); | |||||
}, | }, | ||||
set_events: function() { | set_events: function() { | ||||
var me = this; | var me = this; | ||||
@@ -240,57 +38,6 @@ frappe.ui.FilterList = Class.extend({ | |||||
me.base_list.run(); | me.base_list.run(); | ||||
$(this).addClass("hide"); | $(this).addClass("hide"); | ||||
}); | }); | ||||
//set sort filters | |||||
this.wrapper.on("click", ".filter-sort-item", function() { | |||||
var active = $(this).closest(".filter-dash-item").find(".filter-sort-active"); | |||||
active.attr('data-sort-by', $(this).attr('data-sort-by')); | |||||
active.attr('data-order', $(this).attr('data-order')); | |||||
me.reload_stats(); | |||||
}); | |||||
//setup date-time range pickers | |||||
this.wrapper.find(".filter-input-date").each(function(i,v) { | |||||
var self = this; | |||||
var date; | |||||
var date_wrapper = $('<div>').appendTo($(this)); | |||||
make_date("range"); | |||||
function make_date(mode) { | |||||
var fieldtype = mode==="range" ? "DateRange" : "Date"; | |||||
var name = $(v).data("name"); | |||||
if(date) { | |||||
//cleanup old datepicker | |||||
date.datepicker.destroy(); | |||||
} | |||||
date = frappe.ui.form.make_control({ | |||||
df: { | |||||
fieldtype: fieldtype, | |||||
fieldname: name, | |||||
}, | |||||
parent: date_wrapper, | |||||
only_input: true | |||||
}); | |||||
date.refresh(); | |||||
date.datepicker.update("onSelect", function(fd, dateObj) { | |||||
var filt = me.get_filter(name); | |||||
filt && filt.remove(true); | |||||
if(!dateObj.length && dateObj && date.datepicker.opts.range===false) { | |||||
me.add_filter(me.doctype, name, '=', moment(dateObj).format('YYYY-MM-DD')); | |||||
me.base_list.run(); | |||||
} else if(dateObj.length===2 && date.datepicker.opts.range===true) { | |||||
var [date1, date2] = [moment(dateObj[0]).format('YYYY-MM-DD'), moment(dateObj[1]).format('YYYY-MM-DD')]; | |||||
if(date1==date2) { | |||||
me.add_filter(me.doctype, name, '=', date1); | |||||
} else { | |||||
me.add_filter(me.doctype, name, 'Between', [date1, date2]); | |||||
} | |||||
me.base_list.run(); | |||||
} | |||||
}); | |||||
} | |||||
}) | |||||
}, | }, | ||||
show_filters: function() { | show_filters: function() { | ||||
@@ -307,6 +54,13 @@ frappe.ui.FilterList = Class.extend({ | |||||
}, | }, | ||||
add_filter: function(doctype, fieldname, condition, value, hidden) { | add_filter: function(doctype, fieldname, condition, value, hidden) { | ||||
if (this.base_list.page.fields_dict[fieldname] | |||||
&& ['=', 'like'].includes(condition)) { | |||||
// if filter exists in base_list, then exit | |||||
this.base_list.page.fields_dict[fieldname].set_input(value); | |||||
return; | |||||
} | |||||
if(doctype && fieldname | if(doctype && fieldname | ||||
&& !frappe.meta.has_field(doctype, fieldname) | && !frappe.meta.has_field(doctype, fieldname) | ||||
&& !in_list(frappe.model.std_fields_list, fieldname)) { | && !in_list(frappe.model.std_fields_list, fieldname)) { | ||||
@@ -318,7 +72,6 @@ frappe.ui.FilterList = Class.extend({ | |||||
return; | return; | ||||
} | } | ||||
this.wrapper.find('.show_filters').toggle(true); | this.wrapper.find('.show_filters').toggle(true); | ||||
var is_new_filter = arguments.length===0; | var is_new_filter = arguments.length===0; | ||||
@@ -384,6 +137,8 @@ frappe.ui.FilterList = Class.extend({ | |||||
values.push(filter.get_value()); | values.push(filter.get_value()); | ||||
} | } | ||||
}); | }); | ||||
this.base_list.update_standard_filters(values); | |||||
return values; | return values; | ||||
}, | }, | ||||
@@ -460,7 +215,8 @@ frappe.ui.Filter = Class.extend({ | |||||
? __("values separated by commas") | ? __("values separated by commas") | ||||
: __("use % as wildcard"))+'</div>'); | : __("use % as wildcard"))+'</div>'); | ||||
} else { | } else { | ||||
me.set_field(me.field.df.parent, me.field.df.fieldname, null, condition); | |||||
//if condition selected after refresh | |||||
me.set_field(me.field.df.parent, me.field.df.fieldname, me.field.df.fieldtype, condition); | |||||
} | } | ||||
}); | }); | ||||
@@ -480,7 +236,6 @@ frappe.ui.Filter = Class.extend({ | |||||
this.flist.update_filters(); | this.flist.update_filters(); | ||||
if(!dont_run) { | if(!dont_run) { | ||||
this.flist.base_list.clean_dash = true; | |||||
this.flist.base_list.refresh(true); | this.flist.base_list.refresh(true); | ||||
} | } | ||||
}, | }, | ||||
@@ -673,7 +428,7 @@ frappe.ui.Filter = Class.extend({ | |||||
title="'+__("Remove Filter")+'">\ | title="'+__("Remove Filter")+'">\ | ||||
<i class="fa fa-remove text-muted"></i>\ | <i class="fa fa-remove text-muted"></i>\ | ||||
</button></div>') | </button></div>') | ||||
.insertAfter(this.flist.wrapper.find(".set-filters .show-filters")); | |||||
.insertAfter(this.flist.wrapper.find(".set-filters .new-filter")); | |||||
this.set_filter_button_text(); | this.set_filter_button_text(); | ||||
@@ -403,6 +403,13 @@ frappe.ui.Page = Class.extend({ | |||||
show_form: function() { | show_form: function() { | ||||
this.page_form.removeClass("hide"); | this.page_form.removeClass("hide"); | ||||
}, | }, | ||||
get_form_values: function() { | |||||
var values = {}; | |||||
this.page_form.fields_dict.forEach(function(field, key) { | |||||
values[key] = field.get_value(); | |||||
}); | |||||
return values; | |||||
}, | |||||
add_view: function(name, html) { | add_view: function(name, html) { | ||||
this.views[name] = $(html).appendTo($(this.wrapper).find(".page-content")); | this.views[name] = $(html).appendTo($(this.wrapper).find(".page-content")); | ||||
if(!this.current_view) { | if(!this.current_view) { | ||||
@@ -76,6 +76,7 @@ frappe.views.ReportView = frappe.ui.BaseList.extend({ | |||||
this.add_totals_row = 0; | this.add_totals_row = 0; | ||||
this.page = this.parent.page; | this.page = this.parent.page; | ||||
this.meta = frappe.get_meta(this.doctype); | |||||
this._body = $('<div>').appendTo(this.page.main); | this._body = $('<div>').appendTo(this.page.main); | ||||
this.page_title = __('Report')+ ': ' + (this.docname ? | this.page_title = __('Report')+ ': ' + (this.docname ? | ||||
__(this.doctype) + ' - ' + __(this.docname) : __(this.doctype)); | __(this.doctype) + ' - ' + __(this.docname) : __(this.doctype)); | ||||
@@ -258,13 +259,15 @@ frappe.views.ReportView = frappe.ui.BaseList.extend({ | |||||
// build args for query | // build args for query | ||||
get_args: function() { | get_args: function() { | ||||
var me = this; | |||||
let me = this; | |||||
let filters = this.filter_list.get_filters(); | |||||
return { | return { | ||||
doctype: this.doctype, | doctype: this.doctype, | ||||
fields: $.map(this.columns, function(v) { return me.get_full_column_name(v) }), | fields: $.map(this.columns, function(v) { return me.get_full_column_name(v) }), | ||||
order_by: this.get_order_by(), | order_by: this.get_order_by(), | ||||
add_total_row: this.add_total_row, | add_total_row: this.add_total_row, | ||||
filters: this.filter_list.get_filters(), | |||||
filters: filters, | |||||
save_user_settings_fields: 1, | save_user_settings_fields: 1, | ||||
with_childnames: 1, | with_childnames: 1, | ||||
file_format_type: this.file_format_type | file_format_type: this.file_format_type | ||||
@@ -1,129 +0,0 @@ | |||||
@import "variables.less"; | |||||
@import "mixins.less"; | |||||
.date-range-picker { | |||||
font-size:85%; | |||||
} | |||||
.filter_area { | |||||
margin: 0 -15px; | |||||
} | |||||
.filter-dashboard-wrapper { | |||||
display: none; | |||||
padding-bottom: 0px; | |||||
} | |||||
.list-filter-dashboard { | |||||
border-top: 1px solid @border-color; | |||||
overflow-x: scroll; | |||||
overflow-y: hidden; | |||||
} | |||||
.filter-header { | |||||
border-bottom: 1px solid @border-color; | |||||
background-color: @panel-bg; | |||||
padding: 8px 15px; | |||||
.search-dropdown { | |||||
margin-top: -20px; | |||||
margin-right: 20px; | |||||
.octicon { | |||||
font-size: 14px; | |||||
} | |||||
} | |||||
.sort-dropdown { | |||||
margin-top: -16px; | |||||
} | |||||
.dropdown-menu { | |||||
min-width: 120px !important; | |||||
} | |||||
} | |||||
.filter-label { | |||||
margin: 0px; | |||||
color: @text-muted; | |||||
} | |||||
.filter-dashboard-items { | |||||
height: 187px; | |||||
margin-right: -2px; | |||||
display: flex; | |||||
} | |||||
.filter-dash-item { | |||||
flex-grow: 1; | |||||
min-width: 180px; | |||||
float: left; | |||||
height: 187px; | |||||
border-right:1px solid @border-color; | |||||
} | |||||
.filter-input { | |||||
padding: 10px 15px; | |||||
input { | |||||
height: 25px; | |||||
} | |||||
} | |||||
.filter-item-value, .stat-no-records { | |||||
padding: 0px 15px; | |||||
} | |||||
.filter-dash-item:after { | |||||
/*top:-10px;*/ | |||||
} | |||||
.filter-sort { | |||||
font-size:1.5em; | |||||
} | |||||
.filter-sort-item { | |||||
a { | |||||
padding: 8px !important; | |||||
} | |||||
} | |||||
.filter-sort-item:hover { | |||||
color: @grey-link-color; | |||||
text-decoration: none; | |||||
background-color: @btn-bg; | |||||
} | |||||
.filter-stat { | |||||
overflow-y: auto; | |||||
overflow-x: hidden; | |||||
height: 158px; | |||||
margin: 0px; | |||||
} | |||||
.selected { | |||||
vertical-align: 10%; | |||||
font-size: 1.2em; | |||||
} | |||||
.filter-sort-item > label { | |||||
float:right; | |||||
} | |||||
.filter-stat-link > .badge { | |||||
position: absolute; | |||||
float: right; | |||||
margin-right: 10px; | |||||
margin-top: 2px; | |||||
font-size: 11px; | |||||
} | |||||
.filter-stat-link > .stat { | |||||
width: 130px; | |||||
display: inline-block; | |||||
.text-ellipsis(); | |||||
} | |||||
.filter-dash-controls >.filter-label { | |||||
padding-bottom: 5px; | |||||
} |
@@ -249,7 +249,7 @@ h6.uppercase, .h6.uppercase { | |||||
.form-section:not(:last-child), | .form-section:not(:last-child), | ||||
.form-inner-toolbar { | .form-inner-toolbar { | ||||
border-bottom: 1px solid @light-border-color; | |||||
border-bottom: 1px solid @border-color; | |||||
} | } | ||||
.empty-section { | .empty-section { | ||||
@@ -54,7 +54,7 @@ | |||||
} | } | ||||
.filter-box { | .filter-box { | ||||
border-top: 1px solid @border-color; | |||||
border-bottom: 1px solid @border-color; | |||||
// margin: 0px -15px; | // margin: 0px -15px; | ||||
padding: 10px 15px 3px; | padding: 10px 15px 3px; | ||||
@@ -319,6 +319,12 @@ | |||||
} | } | ||||
} | } | ||||
.page-form { | |||||
.awesomplete > ul { | |||||
min-width: 300px; | |||||
} | |||||
} | |||||
.taggle_input { | .taggle_input { | ||||
padding: 0; | padding: 0; | ||||
margin-top: 3px; | margin-top: 3px; | ||||
@@ -118,16 +118,23 @@ | |||||
.page-form { | .page-form { | ||||
margin: 0px; | margin: 0px; | ||||
padding-top: 15px; | |||||
padding-right: 15px; | |||||
padding-top: 10px; | |||||
border-bottom: 1px solid @border-color; | border-bottom: 1px solid @border-color; | ||||
} | |||||
background-color: @panel-bg; | |||||
.page-form .form-group { | |||||
margin-bottom: 15px; | |||||
} | |||||
.form-group { | |||||
padding-right: 0px; | |||||
margin-bottom: 10px; | |||||
} | |||||
.checkbox { | |||||
margin-top: 4px; | |||||
margin-bottom: 4px; | |||||
.page-form .checkbox { | |||||
margin-top: 2px; | |||||
.help-box { | |||||
display: none; | |||||
} | |||||
} | |||||
} | } | ||||
select.input-sm { | select.input-sm { | ||||
@@ -25,7 +25,7 @@ ipython | |||||
html2text | html2text | ||||
email_reply_parser | email_reply_parser | ||||
click | click | ||||
num2words | |||||
num2words==0.5.4 | |||||
watchdog==0.8.0 | watchdog==0.8.0 | ||||
bleach | bleach | ||||
bleach-whitelist | bleach-whitelist | ||||