[feature] checkboxes in grid #2451
This commit is contained in:
parent
b9fd3ea5aa
commit
ac69247325
8 changed files with 131 additions and 22 deletions
|
|
@ -7,6 +7,22 @@ from frappe import _
|
|||
from frappe.utils import cstr
|
||||
from frappe.model import default_fields
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_mapped_doc(method, source_name, selected_children=None):
|
||||
'''Returns the mapped document calling the given mapper method.
|
||||
Sets selected_children as flags for the `get_mapped_doc` method.
|
||||
|
||||
Called from `open_mapped_doc` from create_new.js'''
|
||||
method = frappe.get_attr(method)
|
||||
|
||||
if method not in frappe.whitelisted:
|
||||
raise frappe.PermissionError
|
||||
|
||||
frappe.flags.selected_children = selected_children
|
||||
|
||||
return method(source_name)
|
||||
|
||||
|
||||
def get_mapped_doc(from_doctype, from_docname, table_maps, target_doc=None,
|
||||
postprocess=None, ignore_permissions=False, ignore_child_tables=False):
|
||||
|
||||
|
|
@ -51,6 +67,13 @@ def get_mapped_doc(from_doctype, from_docname, table_maps, target_doc=None,
|
|||
if not table_map["condition"](source_d):
|
||||
continue
|
||||
|
||||
# if children are selected (checked from UI) for this table type,
|
||||
# and this record is not in the selected children, then continue
|
||||
if (frappe.flags.selected_children
|
||||
and (df.fieldname in frappe.flags.selected_children)
|
||||
and source_d.name not in frappe.flags.selected_children):
|
||||
continue
|
||||
|
||||
target_child_doctype = table_map["doctype"]
|
||||
target_parentfield = target_doc.get_parentfield_of_doctype(target_child_doctype)
|
||||
|
||||
|
|
|
|||
|
|
@ -58,10 +58,45 @@ frappe.ui.form.Grid = Class.extend({
|
|||
|
||||
this.custom_buttons = {};
|
||||
this.grid_buttons = this.wrapper.find('.grid-buttons');
|
||||
this.remove_rows_button = this.grid_buttons.find('.grid-remove-rows')
|
||||
|
||||
this.setup_allow_bulk_edit();
|
||||
this.setup_check();
|
||||
|
||||
},
|
||||
setup_check: function() {
|
||||
var me = this;
|
||||
this.wrapper.on('click', '.grid-row-check', function(e) {
|
||||
$check = $(this);
|
||||
if($check.parents('.grid-heading-row:first').length!==0) {
|
||||
$check.parents('.form-grid:first').find('.grid-row-check').prop('checked', $check.prop('checked'));
|
||||
}
|
||||
me.refresh_remove_rows_button();
|
||||
});
|
||||
|
||||
this.remove_rows_button.on('click', function() {
|
||||
me.get_selected().forEach(function(docname) {
|
||||
me.grid_rows_by_docname[docname].remove();
|
||||
});
|
||||
setTimeout(function() { me.refresh_remove_rows_button(); }, 100);
|
||||
});
|
||||
},
|
||||
refresh_remove_rows_button: function() {
|
||||
this.remove_rows_button.toggleClass('hide',
|
||||
this.wrapper.find('.grid-body .grid-row-check:checked:first').length ? false : true);
|
||||
},
|
||||
get_selected: function() {
|
||||
var selected = [];
|
||||
var me = this;
|
||||
this.wrapper.find('.grid-body .grid-row-check:checked').each(function() {
|
||||
selected.push($(this).parents('.grid-row:first').attr('data-name'));
|
||||
});
|
||||
return selected;
|
||||
},
|
||||
refresh_checks: function() {
|
||||
var show = this.is_editable() || this.frm.has_mapper();
|
||||
this.wrapper.find('.grid-row-check').toggle(show);
|
||||
},
|
||||
make_head: function() {
|
||||
// labels
|
||||
if(!this.header_row) {
|
||||
|
|
@ -132,6 +167,7 @@ frappe.ui.form.Grid = Class.extend({
|
|||
|
||||
// toolbar
|
||||
this.setup_toolbar();
|
||||
this.refresh_checks();
|
||||
|
||||
// sortable
|
||||
if(this.is_sortable() && !this.sortable_setup_done) {
|
||||
|
|
@ -143,6 +179,8 @@ frappe.ui.form.Grid = Class.extend({
|
|||
this.last_docname = this.frm.docname;
|
||||
frappe.utils.scroll_to(_scroll_y);
|
||||
}
|
||||
|
||||
this.refresh_remove_rows_button();
|
||||
},
|
||||
setup_toolbar: function() {
|
||||
if(this.is_editable()) {
|
||||
|
|
@ -216,7 +254,6 @@ frappe.ui.form.Grid = Class.extend({
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
new Sortable($rows.get(0), {
|
||||
group: {name: 'row'},
|
||||
handle: ".sortable-handle",
|
||||
|
|
@ -545,6 +582,7 @@ frappe.ui.form.GridRow = Class.extend({
|
|||
init: function(opts) {
|
||||
this.on_grid_fields_dict = {};
|
||||
this.on_grid_fields = [];
|
||||
this.row_check_html = '<input type="checkbox" class="grid-row-check pull-left">';
|
||||
this.columns = {};
|
||||
this.columns_list = [];
|
||||
$.extend(this, opts);
|
||||
|
|
@ -552,9 +590,13 @@ frappe.ui.form.GridRow = Class.extend({
|
|||
},
|
||||
make: function() {
|
||||
var me = this;
|
||||
|
||||
this.wrapper = $('<div class="grid-row"></div>').appendTo(this.parent).data("grid_row", this);
|
||||
this.row = $('<div class="data-row row sortable-handle"></div>').appendTo(this.wrapper)
|
||||
.on("click", function() {
|
||||
.on("click", function(e) {
|
||||
if($(e.target).hasClass('grid-row-check')) {
|
||||
return;
|
||||
}
|
||||
if(me.grid.allow_on_grid_editing() && me.grid.is_editable()) {
|
||||
// pass
|
||||
} else {
|
||||
|
|
@ -563,6 +605,11 @@ frappe.ui.form.GridRow = Class.extend({
|
|||
}
|
||||
});
|
||||
|
||||
// no checkboxes if too small
|
||||
if(this.is_too_small()) {
|
||||
this.row_check_html = '';
|
||||
}
|
||||
|
||||
if(this.grid.template && !this.grid.meta.editable_grid) {
|
||||
this.render_template();
|
||||
} else {
|
||||
|
|
@ -582,7 +629,7 @@ frappe.ui.form.GridRow = Class.extend({
|
|||
this.wrapper
|
||||
.attr('data-name', this.doc.name)
|
||||
.attr("data-idx", this.doc.idx)
|
||||
.find(".row-index, .grid-form-row-index").html(this.doc.idx)
|
||||
.find(".row-index span, .grid-form-row-index").html(this.doc.idx)
|
||||
|
||||
}
|
||||
},
|
||||
|
|
@ -638,9 +685,9 @@ frappe.ui.form.GridRow = Class.extend({
|
|||
if(this.doc) {
|
||||
if(!this.row_index) {
|
||||
this.row_index = $('<div style="float: left; margin-left: 15px; margin-top: 8px; \
|
||||
margin-right: -20px;"></div>').appendTo(this.row);
|
||||
margin-right: -20px;">'+this.row_check_html+' <span></span></div>').appendTo(this.row);
|
||||
}
|
||||
this.row_index.html(this.doc.idx);
|
||||
this.row_index.find('span').html(this.doc.idx);
|
||||
}
|
||||
|
||||
this.row_display = $('<div class="row-data template-row">'+
|
||||
|
|
@ -657,11 +704,18 @@ frappe.ui.form.GridRow = Class.extend({
|
|||
|
||||
// index (1, 2, 3 etc)
|
||||
if(!this.row_index) {
|
||||
this.row_index = $('<div class="row-index col col-xs-1">' + (this.doc ? this.doc.idx : " ")+ '</div>')
|
||||
var txt = (this.doc ? this.doc.idx : " ");
|
||||
this.row_index = $('<div class="row-index col col-xs-1">' +
|
||||
this.row_check_html +
|
||||
' <span>' + txt + '</span></div>')
|
||||
.appendTo(this.row)
|
||||
.on('click', function() { me.toggle_view(); });
|
||||
.on('click', function(e) {
|
||||
if(!$(e.target).hasClass('grid-row-check')) {
|
||||
me.toggle_view();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.row_index.html(this.doc ? this.doc.idx : " ");
|
||||
this.row_index.find('span').html(txt);
|
||||
}
|
||||
|
||||
this.setup_columns();
|
||||
|
|
@ -676,6 +730,10 @@ frappe.ui.form.GridRow = Class.extend({
|
|||
this.row.toggleClass('editable-row', this.grid.is_editable());
|
||||
},
|
||||
|
||||
is_too_small: function() {
|
||||
return this.row.width() < 400;
|
||||
},
|
||||
|
||||
add_open_form_button: function() {
|
||||
var me = this;
|
||||
if(this.doc) {
|
||||
|
|
@ -686,7 +744,7 @@ frappe.ui.form.GridRow = Class.extend({
|
|||
.appendTo($('<div class="col col-xs-1"></div>').appendTo(this.row))
|
||||
.on('click', function() { me.toggle_view(); return false; });
|
||||
|
||||
if(this.row.width() < 400) {
|
||||
if(this.is_too_small()) {
|
||||
// narrow
|
||||
this.open_form_button.css({'margin-right': '-2px'});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,11 +99,11 @@ frappe.ui.form.LinkSelector = Class.extend({
|
|||
})
|
||||
})
|
||||
} else {
|
||||
$('<div class="alert alert-info">' + __("No Results")
|
||||
+ (frappe.model.can_read(me.doctype) ?
|
||||
('. <a class="new-doc">'
|
||||
+ __("Make a new") + " " + __(me.doctype) + "</a>") : '')
|
||||
+ '</div>').appendTo(parent).find(".new-doc").click(function() {
|
||||
$('<p><br><span class="text-muted">' + __("No Results") + '</span>'
|
||||
+ (frappe.model.can_create(me.doctype) ?
|
||||
('<br><br><a class="new-doc btn btn-default btn-sm">'
|
||||
+ __("Make a new {0}", [__(me.doctype)]) + "</a>") : '')
|
||||
+ '</p>').appendTo(parent).find(".new-doc").click(function() {
|
||||
me.target.new_doc();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
<div class="small form-clickable-section grid-footer">
|
||||
<div class="row">
|
||||
<div class="col-sm-6 grid-buttons">
|
||||
<button type="reset" class="btn btn-xs btn-danger grid-remove-rows hide">
|
||||
{%= __("Delete") %}</button>
|
||||
<button type="reset"
|
||||
class="grid-add-multiple-rows btn btn-xs btn-default hide"
|
||||
style="margin-right: 10px;">
|
||||
|
|
|
|||
|
|
@ -275,9 +275,11 @@ $.extend(frappe.model, {
|
|||
|
||||
return frappe.call({
|
||||
type: "POST",
|
||||
method: opts.method,
|
||||
method: 'frappe.model.mapper.make_mapped_doc',
|
||||
args: {
|
||||
"source_name": opts.source_name
|
||||
method: opts.method,
|
||||
source_name: opts.source_name,
|
||||
selected_children: opts.frm.get_selected()
|
||||
},
|
||||
freeze: true,
|
||||
callback: function(r) {
|
||||
|
|
|
|||
|
|
@ -398,6 +398,28 @@ _f.Frm.prototype.get_title = function() {
|
|||
}
|
||||
}
|
||||
|
||||
_f.Frm.prototype.get_selected = function() {
|
||||
// returns list of children that are selected. returns [parentfield, name] for each
|
||||
var selected = {}, me = this;
|
||||
frappe.meta.get_table_fields(this.doctype).forEach(function(df) {
|
||||
var _selected = me.fields_dict[df.fieldname].grid.get_selected();
|
||||
if(_selected.length) {
|
||||
selected[df.fieldname] = _selected;
|
||||
}
|
||||
});
|
||||
return selected;
|
||||
}
|
||||
|
||||
_f.Frm.prototype.has_mapper = function() {
|
||||
// hackalert!
|
||||
// if open_mapped_doc is mentioned in the custom script, then mapper exists
|
||||
if(this._has_mapper === undefined) {
|
||||
this._has_mapper = (this.meta.__js && this.meta.__js.search('open_mapped_doc')!==-1) ?
|
||||
true: false;
|
||||
}
|
||||
return this._has_mapper;
|
||||
}
|
||||
|
||||
_f.Frm.prototype.set_indicator_formatter = function(fieldname, get_color, get_text) {
|
||||
// get doctype from parent
|
||||
if(frappe.meta.docfield_map[this.doctype][fieldname]) {
|
||||
|
|
|
|||
|
|
@ -26,8 +26,7 @@ class TestScheduler(TestCase):
|
|||
self.assertTrue("all" in frappe.flags.ran_schedulers)
|
||||
|
||||
def test_enabled_events(self):
|
||||
val = json.dumps(["hourly", "hourly_long", "daily", "daily_long", "weekly", "weekly_long", "monthly", "monthly_long"])
|
||||
frappe.db.set_global('enabled_scheduler_events', val)
|
||||
frappe.flags.enabled_events = ["hourly", "hourly_long", "daily", "daily_long", "weekly", "weekly_long", "monthly", "monthly_long"]
|
||||
|
||||
# maintain last_event and next_event on the same day
|
||||
last_event = now_datetime().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
|
|
@ -44,8 +43,7 @@ class TestScheduler(TestCase):
|
|||
self.assertTrue("all" in frappe.flags.ran_schedulers)
|
||||
self.assertTrue("hourly" in frappe.flags.ran_schedulers)
|
||||
|
||||
frappe.db.set_global('enabled_scheduler_events', "")
|
||||
|
||||
del frappe.flags['enabled_events']
|
||||
|
||||
def test_enabled_events_day_change(self):
|
||||
val = json.dumps(["daily", "daily_long", "weekly", "weekly_long", "monthly", "monthly_long"])
|
||||
|
|
|
|||
|
|
@ -144,6 +144,9 @@ def trigger(site, event, queued_jobs=(), now=False):
|
|||
if not queued_jobs and not now:
|
||||
queued_jobs = get_jobs(site=site, queue=queue)
|
||||
|
||||
if frappe.flags.in_test:
|
||||
frappe.flags.ran_schedulers.append(event)
|
||||
|
||||
events = get_scheduler_events(event)
|
||||
if not events:
|
||||
return
|
||||
|
|
@ -155,8 +158,6 @@ def trigger(site, event, queued_jobs=(), now=False):
|
|||
else:
|
||||
scheduler_task(site=site, event=event, handler=handler, now=True)
|
||||
|
||||
if frappe.flags.in_test:
|
||||
frappe.flags.ran_schedulers.append(event)
|
||||
|
||||
def get_scheduler_events(event):
|
||||
'''Get scheduler events from hooks and integrations'''
|
||||
|
|
@ -193,6 +194,9 @@ def log(method, message=None):
|
|||
return message
|
||||
|
||||
def get_enabled_scheduler_events():
|
||||
if 'enabled_events' in frappe.flags:
|
||||
return frappe.flags.enabled_events
|
||||
|
||||
enabled_events = frappe.db.get_global("enabled_scheduler_events")
|
||||
if enabled_events:
|
||||
if isinstance(enabled_events, basestring):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue