* [fix] filters for calendars frappe/erpnext#9850 * [add] testsversion-14
@@ -62,7 +62,6 @@ frappe.ui.form.on('Test Runner', { | |||||
QUnit.done(({ total, failed, passed, runtime }) => { | QUnit.done(({ total, failed, passed, runtime }) => { | ||||
// flag for selenium that test is done | // flag for selenium that test is done | ||||
$('<div id="frappe-qunit-done"></div>').appendTo($('body')); | |||||
console.log( `Total: ${total}, Failed: ${failed}, Passed: ${passed}, Runtime: ${runtime}` ); // eslint-disable-line | console.log( `Total: ${total}, Failed: ${failed}, Passed: ${passed}, Runtime: ${runtime}` ); // eslint-disable-line | ||||
@@ -72,6 +71,9 @@ frappe.ui.form.on('Test Runner', { | |||||
console.log('Tests Passed'); // eslint-disable-line | console.log('Tests Passed'); // eslint-disable-line | ||||
} | } | ||||
frappe.set_route('Form', 'Test Runner', 'Test Runner'); | frappe.set_route('Form', 'Test Runner', 'Test Runner'); | ||||
$('<div id="frappe-qunit-done"></div>').appendTo($('body')); | |||||
}); | }); | ||||
}); | }); | ||||
@@ -19,16 +19,8 @@ def update_event(args, field_map): | |||||
def get_event_conditions(doctype, filters=None): | def get_event_conditions(doctype, filters=None): | ||||
"""Returns SQL conditions with user permissions and filters for event queries""" | """Returns SQL conditions with user permissions and filters for event queries""" | ||||
from frappe.desk.reportview import build_match_conditions | |||||
from frappe.desk.reportview import get_filters_cond | |||||
if not frappe.has_permission(doctype): | if not frappe.has_permission(doctype): | ||||
frappe.throw(_("Not Permitted"), frappe.PermissionError) | frappe.throw(_("Not Permitted"), frappe.PermissionError) | ||||
conditions = build_match_conditions(doctype) | |||||
conditions = conditions and (" and " + conditions) or "" | |||||
if filters: | |||||
filters = json.loads(filters) | |||||
for key in filters: | |||||
if filters[key]: | |||||
conditions += 'and `{0}` = "{1}"'.format(frappe.db.escape(key), frappe.db.escape(filters[key])) | |||||
return conditions | |||||
return get_filters_cond(doctype, filters, [], with_match_conditions = True) |
@@ -334,7 +334,7 @@ def build_match_conditions(doctype, as_condition=True): | |||||
else: | else: | ||||
return match_conditions | return match_conditions | ||||
def get_filters_cond(doctype, filters, conditions, ignore_permissions=None): | |||||
def get_filters_cond(doctype, filters, conditions, ignore_permissions=None, with_match_conditions=False): | |||||
if filters: | if filters: | ||||
flt = filters | flt = filters | ||||
if isinstance(filters, dict): | if isinstance(filters, dict): | ||||
@@ -350,6 +350,10 @@ def get_filters_cond(doctype, filters, conditions, ignore_permissions=None): | |||||
query = DatabaseQuery(doctype) | query = DatabaseQuery(doctype) | ||||
query.filters = flt | query.filters = flt | ||||
query.conditions = conditions | query.conditions = conditions | ||||
if with_match_conditions: | |||||
query.build_match_conditions() | |||||
query.build_filter_conditions(flt, conditions, ignore_permissions) | query.build_filter_conditions(flt, conditions, ignore_permissions) | ||||
cond = ' and ' + ' and '.join(query.conditions) | cond = ' and ' + ' and '.join(query.conditions) | ||||
@@ -113,13 +113,20 @@ frappe.ui.Page = Class.extend({ | |||||
}, | }, | ||||
set_action: function(btn, opts) { | set_action: function(btn, opts) { | ||||
let me = this; | |||||
if (opts.icon) { | if (opts.icon) { | ||||
opts.label = this.get_icon_label(opts.icon, opts.label); | opts.label = this.get_icon_label(opts.icon, opts.label); | ||||
} | } | ||||
this.clear_action_of(btn); | this.clear_action_of(btn); | ||||
btn.removeClass("hide").prop("disabled", false).html(opts.label).on("click", opts.click); | |||||
btn.removeClass("hide") | |||||
.prop("disabled", false) | |||||
.html(opts.label) | |||||
.on("click", function() { | |||||
let response = opts.click.apply(this); | |||||
me.btn_disable_enable(btn, response); | |||||
}); | |||||
if (opts.working_label) { | if (opts.working_label) { | ||||
btn.attr("data-working-label", opts.working_label); | btn.attr("data-working-label", opts.working_label); | ||||
@@ -250,31 +257,35 @@ frappe.ui.Page = Class.extend({ | |||||
this.get_inner_group_button(label).find("button").removeClass("btn-default").addClass("btn-primary"); | this.get_inner_group_button(label).find("button").removeClass("btn-default").addClass("btn-primary"); | ||||
}, | }, | ||||
btn_disable_enable: function(btn, response) { | |||||
if (response && response.then) { | |||||
btn.prop('disabled', true); | |||||
response.then(() => { | |||||
btn.prop('disabled', false); | |||||
}) | |||||
} else if (response && response.always) { | |||||
btn.prop('disabled', true); | |||||
response.always(() => { | |||||
btn.prop('disabled', false); | |||||
}); | |||||
} | |||||
}, | |||||
add_inner_button: function(label, action, group) { | add_inner_button: function(label, action, group) { | ||||
let _action = function() { | let _action = function() { | ||||
let btn = $(this); | let btn = $(this); | ||||
let _ret = action(); | |||||
if (_ret && _ret.then) { | |||||
// returns a promise | |||||
btn.attr('disabled', true); | |||||
_ret.then(() => { | |||||
btn.attr('disabled', false); | |||||
}) | |||||
} | |||||
if (_ret && _ret.always) { | |||||
// returns frappe.call ($.ajax) | |||||
btn.attr('disabled', true); | |||||
_ret.always(() => { | |||||
btn.attr('disabled', false); | |||||
}); | |||||
} | |||||
} | |||||
let response = action(); | |||||
this.btn_disable_enable(btn, response); | |||||
}; | |||||
if(group) { | if(group) { | ||||
var $group = this.get_inner_group_button(group); | var $group = this.get_inner_group_button(group); | ||||
return $('<li><a>'+label+'</a></li>').on('click', _action).appendTo($group.find(".dropdown-menu")); | |||||
return $('<li><a>'+label+'</a></li>') | |||||
.on('click', _action) | |||||
.appendTo($group.find(".dropdown-menu")); | |||||
} else { | } else { | ||||
return $('<button class="btn btn-default btn-xs" style="margin-left: 10px;">'+__(label)+'</btn>') | return $('<button class="btn btn-default btn-xs" style="margin-left: 10px;">'+__(label)+'</btn>') | ||||
.on("click", _action).appendTo(this.inner_toolbar.removeClass("hide")) | |||||
.on("click", _action) | |||||
.appendTo(this.inner_toolbar.removeClass("hide")); | |||||
} | } | ||||
}, | }, | ||||
@@ -12,7 +12,7 @@ frappe.views.CalendarView = frappe.views.ListRenderer.extend({ | |||||
doctype: this.doctype, | doctype: this.doctype, | ||||
parent: this.wrapper, | parent: this.wrapper, | ||||
page: this.list_view.page, | page: this.list_view.page, | ||||
filter_vals: this.list_view.filter_list.get_filters() | |||||
list_view: this.list_view | |||||
} | } | ||||
$.extend(options, frappe.views.calendar[this.doctype]); | $.extend(options, frappe.views.calendar[this.doctype]); | ||||
this.calendar = new frappe.views.Calendar(options); | this.calendar = new frappe.views.Calendar(options); | ||||
@@ -45,6 +45,7 @@ frappe.views.Calendar = Class.extend({ | |||||
var me = this; | var me = this; | ||||
// add links to other calendars | // add links to other calendars | ||||
me.page.clear_user_actions(); | |||||
$.each(frappe.boot.calendars, function(i, doctype) { | $.each(frappe.boot.calendars, function(i, doctype) { | ||||
if(frappe.model.can_read(doctype)) { | if(frappe.model.can_read(doctype)) { | ||||
me.page.add_menu_item(__(doctype), function() { | me.page.add_menu_item(__(doctype), function() { | ||||
@@ -196,19 +197,11 @@ frappe.views.Calendar = Class.extend({ | |||||
} | } | ||||
}, | }, | ||||
get_args: function(start, end) { | get_args: function(start, end) { | ||||
if(this.filter_vals) { | |||||
var filters = {}; | |||||
this.filter_vals.forEach(function(f) { | |||||
if(f[2]==="=") { | |||||
filters[f[1]] = f[3]; | |||||
} | |||||
}); | |||||
} | |||||
var args = { | var args = { | ||||
doctype: this.doctype, | doctype: this.doctype, | ||||
start: this.get_system_datetime(start), | start: this.get_system_datetime(start), | ||||
end: this.get_system_datetime(end), | end: this.get_system_datetime(end), | ||||
filters: filters | |||||
filters: this.list_view.filter_list.get_filters() | |||||
}; | }; | ||||
return args; | return args; | ||||
}, | }, | ||||
@@ -309,50 +302,5 @@ frappe.views.Calendar = Class.extend({ | |||||
event.start = event.start ? $.fullCalendar.moment(event.start).stripTime() : null; | event.start = event.start ? $.fullCalendar.moment(event.start).stripTime() : null; | ||||
event.end = event.end ? $.fullCalendar.moment(event.end).add(1, "day").stripTime() : null; | event.end = event.end ? $.fullCalendar.moment(event.end).add(1, "day").stripTime() : null; | ||||
} | } | ||||
}, | |||||
add_filters: function() { | |||||
var me = this; | |||||
if(this.filters) { | |||||
$.each(this.filters, function(i, df) { | |||||
df.change = function() { | |||||
me.refresh(); | |||||
}; | |||||
me.page.add_field(df); | |||||
}); | |||||
} | |||||
}, | |||||
set_filter: function(doctype, value) { | |||||
var me = this; | |||||
if(this.filters) { | |||||
$.each(this.filters, function(i, df) { | |||||
if(df.options===value) | |||||
me.page.fields_dict[df.fieldname].set_input(value); | |||||
return false; | |||||
}); | |||||
} | |||||
}, | |||||
get_filters: function() { | |||||
var filter_vals = {}, | |||||
me = this; | |||||
if(this.filters) { | |||||
$.each(this.filters, function(i, df) { | |||||
filter_vals[df.fieldname || df.label] = | |||||
me.page.fields_dict[df.fieldname || df.label].get_value(); | |||||
}); | |||||
} | |||||
return filter_vals; | |||||
}, | |||||
set_filters_from_route_options: function() { | |||||
var me = this; | |||||
if(frappe.route_options) { | |||||
$.each(frappe.route_options, function(k, value) { | |||||
if(me.page.fields_dict[k]) { | |||||
me.page.fields_dict[k].set_input(value); | |||||
} | |||||
}) | |||||
frappe.route_options = null; | |||||
me.refresh(); | |||||
return false; | |||||
} | |||||
} | } | ||||
}) | }) |
@@ -1,88 +1,81 @@ | |||||
QUnit.module('views'); | QUnit.module('views'); | ||||
QUnit.test("Calendar View Tests", function(assert) { | QUnit.test("Calendar View Tests", function(assert) { | ||||
assert.expect(6); | |||||
assert.expect(4); | |||||
let done = assert.async(); | let done = assert.async(); | ||||
let random_text = frappe.utils.get_random(10); | |||||
let random_text = frappe.utils.get_random(3); | |||||
let today = frappe.datetime.get_today()+" 16:20:35"; //arbitrary value taken to prevent cases like 12a for 12:00am and 12h to 24h conversion | let today = frappe.datetime.get_today()+" 16:20:35"; //arbitrary value taken to prevent cases like 12a for 12:00am and 12h to 24h conversion | ||||
let visible_time = () => { | let visible_time = () => { | ||||
// Method to return the start-time (hours) of the event visible | |||||
return $('.fc-time').text().split('p')[0]; // 'p' because the arbitrary time is pm | |||||
// Method to return the start-time (hours) of the event visible | |||||
return $('.fc-time').text().split('p')[0]; // 'p' because the arbitrary time is pm | |||||
}; | }; | ||||
let event_title_text = () => { | let event_title_text = () => { | ||||
// Method to return the title of the event visible | |||||
// Method to return the title of the event visible | |||||
return $('.fc-title:visible').text(); | return $('.fc-title:visible').text(); | ||||
}; | }; | ||||
frappe.run_serially([ | frappe.run_serially([ | ||||
// Create an event using the frappe API | |||||
// create 2 events, one private, one public | |||||
() => frappe.tests.make("Event", [ | () => frappe.tests.make("Event", [ | ||||
{subject: random_text}, | |||||
{subject: random_text + ':Pri'}, | |||||
{starts_on: today}, | {starts_on: today}, | ||||
{event_type: 'Private'} | {event_type: 'Private'} | ||||
]), | ]), | ||||
() => frappe.tests.make("Event", [ | |||||
{subject: random_text + ':Pub'}, | |||||
{starts_on: today}, | |||||
{event_type: 'Public'} | |||||
]), | |||||
// Goto Calendar view | // Goto Calendar view | ||||
() => frappe.set_route(["List", "Event", "Calendar"]), | () => frappe.set_route(["List", "Event", "Calendar"]), | ||||
() => frappe.tests.click_page_head_item("Refresh"), | |||||
() => { | |||||
// clear filter | |||||
$('[data-fieldname="event_type"]').val('').trigger('change'); | |||||
}, | |||||
() => frappe.timeout(2), | () => frappe.timeout(2), | ||||
// Check if event is created | // Check if event is created | ||||
() => { | () => { | ||||
// Check if the event exists and if its title matches with the one created | // Check if the event exists and if its title matches with the one created | ||||
assert.ok(event_title_text().includes(random_text), "Event title verified"); | |||||
assert.ok(event_title_text().includes(random_text + ':Pri'), | |||||
"Event title verified"); | |||||
// Check if time of event created is correct | // Check if time of event created is correct | ||||
assert.ok(visible_time().includes("4:20"), "Event start time verified"); | |||||
assert.ok(visible_time().includes("4:20"), | |||||
"Event start time verified"); | |||||
}, | }, | ||||
// Delete event | |||||
// Goto Calendar view | |||||
() => frappe.set_route(["List", "Event", "Calendar"]), | |||||
() => frappe.timeout(1), | |||||
// Open the event to be deleted | |||||
() => frappe.tests.click_generic_text(random_text), | |||||
() => frappe.tests.click_page_head_item('Menu'), | |||||
() => frappe.tests.click_dropdown_item('Delete'), | |||||
() => frappe.tests.click_page_head_item('Yes'), | |||||
() => frappe.timeout(1), | |||||
() => frappe.tests.click_page_head_item("Refresh"), | |||||
() => frappe.timeout(1), | |||||
// Goto Calendar View | |||||
() => frappe.set_route(["List", "Event", "Calendar"]), | |||||
// check filter | |||||
() => { | |||||
$('[data-fieldname="event_type"]').val('Public').trigger('change'); | |||||
}, | |||||
() => frappe.timeout(1), | () => frappe.timeout(1), | ||||
() => { | |||||
// private event should be hidden | |||||
assert.notOk(event_title_text().includes(random_text + ':Pri'), | |||||
"Event title verified"); | |||||
}, | |||||
// Check if all menu items redirect to correct locations | |||||
// Check if clicking on 'Import' redirects you to ["data-import-tool"] | |||||
() => frappe.tests.click_page_head_item('Menu'), | |||||
() => frappe.tests.click_dropdown_item('Import'), | |||||
() => assert.deepEqual(["data-import-tool"], frappe.get_route(), "Routed to 'data-import-tool' by clicking on 'Import'"), | |||||
() => frappe.set_route(["List", "Event", "Calendar"]), | |||||
() => frappe.timeout(1), | |||||
// Check if clicking on 'User Permissions Manager' redirects you to ["user-permissions"] | |||||
() => frappe.tests.click_page_head_item('Menu'), | |||||
() => frappe.tests.click_dropdown_item('User Permissions Manager'), | |||||
() => assert.deepEqual(["user-permissions"], frappe.get_route(), "Routed to 'user-permissions' by clicking on 'User Permissions Manager'"), | |||||
// Delete event | |||||
// Goto Calendar view | |||||
() => frappe.set_route(["List", "Event", "Calendar"]), | () => frappe.set_route(["List", "Event", "Calendar"]), | ||||
() => frappe.timeout(1), | () => frappe.timeout(1), | ||||
// Check if clicking on 'Role Permissions Manager' redirects you to ["permission-manager"] | |||||
() => frappe.tests.click_page_head_item('Menu'), | |||||
() => frappe.tests.click_dropdown_item('Role Permissions Manager'), | |||||
() => assert.deepEqual(["permission-manager"], frappe.get_route(), "Routed to 'permission-manager' by clicking on 'Role Permissions Manager'"), | |||||
// delete event | |||||
() => frappe.tests.click_generic_text(random_text + ':Pub'), | |||||
() => { | |||||
frappe.tests.click_page_head_item('Menu'); | |||||
frappe.tests.click_dropdown_item('Delete'); | |||||
}, | |||||
() => frappe.timeout(0.5), | |||||
() => frappe.tests.click_button('Yes'), | |||||
() => frappe.timeout(2), | |||||
() => frappe.set_route(["List", "Event", "Calendar"]), | () => frappe.set_route(["List", "Event", "Calendar"]), | ||||
() => frappe.tests.click_button("Refresh"), | |||||
() => frappe.timeout(1), | () => frappe.timeout(1), | ||||
// // Check if clicking on 'Customize' redirects you to ["Form", "Customize Form"] | |||||
// *** ERROR HERE IN FRAPPE: UNCOMMENT THIS ONCE THAT IS RESOLVED *** // | |||||
// () => frappe.tests.click_page_head_item('Menu'), | |||||
// () => frappe.tests.click_dropdown_item('Customize'), | |||||
// () => assert.deepEqual(["Form", "Customize Form"], frappe.get_route(), "Routed to 'Form, Customize Form' by clicking on 'Customize'"), | |||||
// () => frappe.set_route(["List", "Event", "Calendar"]), | |||||
// () => frappe.timeout(3), | |||||
// Check if event is deleted | // Check if event is deleted | ||||
() => assert.notOk(event_title_text().includes(random_text), "Event deleted"), | |||||
() => assert.notOk(event_title_text().includes(random_text + ':Pub'), | |||||
"Event deleted"), | |||||
() => done() | () => done() | ||||
]); | ]); | ||||
}); | }); |