瀏覽代碼

Merge pull request #2464 from rmehta/checkbox-in-grid

[feature] checkboxes in grid #2451
version-14
Nabin Hait 8 年之前
committed by GitHub
父節點
當前提交
dfc2095b23
共有 8 個檔案被更改,包括 134 行新增22 行删除
  1. +26
    -0
      frappe/model/mapper.py
  2. +67
    -9
      frappe/public/js/frappe/form/grid.js
  3. +5
    -5
      frappe/public/js/frappe/form/link_selector.js
  4. +2
    -0
      frappe/public/js/frappe/form/templates/grid_body.html
  5. +4
    -2
      frappe/public/js/frappe/model/create_new.js
  6. +22
    -0
      frappe/public/js/legacy/clientscriptAPI.js
  7. +2
    -4
      frappe/tests/test_scheduler.py
  8. +6
    -2
      frappe/utils/scheduler.py

+ 26
- 0
frappe/model/mapper.py 查看文件

@@ -7,6 +7,25 @@ from frappe import _
from frappe.utils import cstr from frappe.utils import cstr
from frappe.model import default_fields 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

if selected_children:
selected_children = json.loads(selected_children)

frappe.flags.selected_children = selected_children or None

return method(source_name)


def get_mapped_doc(from_doctype, from_docname, table_maps, target_doc=None, def get_mapped_doc(from_doctype, from_docname, table_maps, target_doc=None,
postprocess=None, ignore_permissions=False, ignore_child_tables=False): postprocess=None, ignore_permissions=False, ignore_child_tables=False):


@@ -51,6 +70,13 @@ def get_mapped_doc(from_doctype, from_docname, table_maps, target_doc=None,
if not table_map["condition"](source_d): if not table_map["condition"](source_d):
continue 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[df.fieldname]):
continue

target_child_doctype = table_map["doctype"] target_child_doctype = table_map["doctype"]
target_parentfield = target_doc.get_parentfield_of_doctype(target_child_doctype) target_parentfield = target_doc.get_parentfield_of_doctype(target_child_doctype)




+ 67
- 9
frappe/public/js/frappe/form/grid.js 查看文件

@@ -58,10 +58,45 @@ frappe.ui.form.Grid = Class.extend({


this.custom_buttons = {}; this.custom_buttons = {};
this.grid_buttons = this.wrapper.find('.grid-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_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() { make_head: function() {
// labels // labels
if(!this.header_row) { if(!this.header_row) {
@@ -132,6 +167,7 @@ frappe.ui.form.Grid = Class.extend({


// toolbar // toolbar
this.setup_toolbar(); this.setup_toolbar();
this.refresh_checks();


// sortable // sortable
if(this.is_sortable() && !this.sortable_setup_done) { if(this.is_sortable() && !this.sortable_setup_done) {
@@ -143,6 +179,8 @@ frappe.ui.form.Grid = Class.extend({
this.last_docname = this.frm.docname; this.last_docname = this.frm.docname;
frappe.utils.scroll_to(_scroll_y); frappe.utils.scroll_to(_scroll_y);
} }

this.refresh_remove_rows_button();
}, },
setup_toolbar: function() { setup_toolbar: function() {
if(this.is_editable()) { if(this.is_editable()) {
@@ -216,7 +254,6 @@ frappe.ui.form.Grid = Class.extend({
return; return;
} }



new Sortable($rows.get(0), { new Sortable($rows.get(0), {
group: {name: 'row'}, group: {name: 'row'},
handle: ".sortable-handle", handle: ".sortable-handle",
@@ -545,6 +582,7 @@ frappe.ui.form.GridRow = Class.extend({
init: function(opts) { init: function(opts) {
this.on_grid_fields_dict = {}; this.on_grid_fields_dict = {};
this.on_grid_fields = []; this.on_grid_fields = [];
this.row_check_html = '<input type="checkbox" class="grid-row-check pull-left">';
this.columns = {}; this.columns = {};
this.columns_list = []; this.columns_list = [];
$.extend(this, opts); $.extend(this, opts);
@@ -552,9 +590,13 @@ frappe.ui.form.GridRow = Class.extend({
}, },
make: function() { make: function() {
var me = this; var me = this;

this.wrapper = $('<div class="grid-row"></div>').appendTo(this.parent).data("grid_row", 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) 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()) { if(me.grid.allow_on_grid_editing() && me.grid.is_editable()) {
// pass // pass
} else { } 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) { if(this.grid.template && !this.grid.meta.editable_grid) {
this.render_template(); this.render_template();
} else { } else {
@@ -582,7 +629,7 @@ frappe.ui.form.GridRow = Class.extend({
this.wrapper this.wrapper
.attr('data-name', this.doc.name) .attr('data-name', this.doc.name)
.attr("data-idx", this.doc.idx) .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)


} }
}, },
@@ -640,9 +687,9 @@ frappe.ui.form.GridRow = Class.extend({
if(this.doc) { if(this.doc) {
if(!this.row_index) { if(!this.row_index) {
this.row_index = $('<div style="float: left; margin-left: 15px; margin-top: 8px; \ 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">'+ this.row_display = $('<div class="row-data template-row">'+
@@ -659,11 +706,18 @@ frappe.ui.form.GridRow = Class.extend({


// index (1, 2, 3 etc) // index (1, 2, 3 etc)
if(!this.row_index) { if(!this.row_index) {
this.row_index = $('<div class="row-index col col-xs-1">' + (this.doc ? this.doc.idx : "&nbsp;")+ '</div>')
var txt = (this.doc ? this.doc.idx : "&nbsp;");
this.row_index = $('<div class="row-index col col-xs-1">' +
this.row_check_html +
' <span>' + txt + '</span></div>')
.appendTo(this.row) .appendTo(this.row)
.on('click', function() { me.toggle_view(); });
.on('click', function(e) {
if(!$(e.target).hasClass('grid-row-check')) {
me.toggle_view();
}
});
} else { } else {
this.row_index.html(this.doc ? this.doc.idx : "&nbsp;");
this.row_index.find('span').html(txt);
} }


this.setup_columns(); this.setup_columns();
@@ -678,6 +732,10 @@ frappe.ui.form.GridRow = Class.extend({
this.row.toggleClass('editable-row', this.grid.is_editable()); this.row.toggleClass('editable-row', this.grid.is_editable());
}, },


is_too_small: function() {
return this.row.width() < 400;
},

add_open_form_button: function() { add_open_form_button: function() {
var me = this; var me = this;
if(this.doc) { if(this.doc) {
@@ -688,7 +746,7 @@ frappe.ui.form.GridRow = Class.extend({
.appendTo($('<div class="col col-xs-1"></div>').appendTo(this.row)) .appendTo($('<div class="col col-xs-1"></div>').appendTo(this.row))
.on('click', function() { me.toggle_view(); return false; }); .on('click', function() { me.toggle_view(); return false; });


if(this.row.width() < 400) {
if(this.is_too_small()) {
// narrow // narrow
this.open_form_button.css({'margin-right': '-2px'}); this.open_form_button.css({'margin-right': '-2px'});
} }


+ 5
- 5
frappe/public/js/frappe/form/link_selector.js 查看文件

@@ -99,11 +99,11 @@ frappe.ui.form.LinkSelector = Class.extend({
}) })
}) })
} else { } 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(); me.target.new_doc();
}); });
} }


+ 2
- 0
frappe/public/js/frappe/form/templates/grid_body.html 查看文件

@@ -7,6 +7,8 @@
<div class="small form-clickable-section grid-footer"> <div class="small form-clickable-section grid-footer">
<div class="row"> <div class="row">
<div class="col-sm-6 grid-buttons"> <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" <button type="reset"
class="grid-add-multiple-rows btn btn-xs btn-default hide" class="grid-add-multiple-rows btn btn-xs btn-default hide"
style="margin-right: 10px;"> style="margin-right: 10px;">


+ 4
- 2
frappe/public/js/frappe/model/create_new.js 查看文件

@@ -275,9 +275,11 @@ $.extend(frappe.model, {


return frappe.call({ return frappe.call({
type: "POST", type: "POST",
method: opts.method,
method: 'frappe.model.mapper.make_mapped_doc',
args: { args: {
"source_name": opts.source_name
method: opts.method,
source_name: opts.source_name,
selected_children: opts.frm.get_selected()
}, },
freeze: true, freeze: true,
callback: function(r) { callback: function(r) {


+ 22
- 0
frappe/public/js/legacy/clientscriptAPI.js 查看文件

@@ -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) { _f.Frm.prototype.set_indicator_formatter = function(fieldname, get_color, get_text) {
// get doctype from parent // get doctype from parent
if(frappe.meta.docfield_map[this.doctype][fieldname]) { if(frappe.meta.docfield_map[this.doctype][fieldname]) {


+ 2
- 4
frappe/tests/test_scheduler.py 查看文件

@@ -26,8 +26,7 @@ class TestScheduler(TestCase):
self.assertTrue("all" in frappe.flags.ran_schedulers) self.assertTrue("all" in frappe.flags.ran_schedulers)


def test_enabled_events(self): 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 # maintain last_event and next_event on the same day
last_event = now_datetime().replace(hour=0, minute=0, second=0, microsecond=0) 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("all" in frappe.flags.ran_schedulers)
self.assertTrue("hourly" 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): def test_enabled_events_day_change(self):
val = json.dumps(["daily", "daily_long", "weekly", "weekly_long", "monthly", "monthly_long"]) val = json.dumps(["daily", "daily_long", "weekly", "weekly_long", "monthly", "monthly_long"])


+ 6
- 2
frappe/utils/scheduler.py 查看文件

@@ -144,6 +144,9 @@ def trigger(site, event, queued_jobs=(), now=False):
if not queued_jobs and not now: if not queued_jobs and not now:
queued_jobs = get_jobs(site=site, queue=queue) queued_jobs = get_jobs(site=site, queue=queue)


if frappe.flags.in_test:
frappe.flags.ran_schedulers.append(event)

events = get_scheduler_events(event) events = get_scheduler_events(event)
if not events: if not events:
return return
@@ -155,8 +158,6 @@ def trigger(site, event, queued_jobs=(), now=False):
else: else:
scheduler_task(site=site, event=event, handler=handler, now=True) 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): def get_scheduler_events(event):
'''Get scheduler events from hooks and integrations''' '''Get scheduler events from hooks and integrations'''
@@ -193,6 +194,9 @@ def log(method, message=None):
return message return message


def get_enabled_scheduler_events(): 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") enabled_events = frappe.db.get_global("enabled_scheduler_events")
if enabled_events: if enabled_events:
if isinstance(enabled_events, basestring): if isinstance(enabled_events, basestring):


Loading…
取消
儲存