[feature] Added 'Customize' and 'Import' fields in List view, [cleanups] #194, #113

This commit is contained in:
Rushabh Mehta 2013-07-09 16:04:03 +05:30
parent db1b0e0d4e
commit 5ec86a5b42
15 changed files with 73 additions and 1534 deletions

View file

@ -20,6 +20,12 @@
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
$(cur_frm.wrapper).on("grid-row-render", function(e, grid_row) {
if(grid_row.doc && grid_row.doc.fieldtype=="Section Break") {
$(grid_row.row).css({"font-weight": "bold"});
}
})
cur_frm.cscript.doc_type = function(doc, dt, dn) {
cur_frm.call({
method: "get",
@ -81,6 +87,11 @@ cur_frm.cscript.refresh = function(doc, dt, dn) {
}
cur_frm.cscript.hide_allow_attach(doc, dt, dn);
if(wn.route_options) {
wn.model.set_value("Customize Form", null, "doc_type", wn.route_options.doctype)
wn.route_options = null;
}
}
cur_frm.cscript.hide_allow_attach = function(doc, dt, dn) {

View file

@ -108,7 +108,6 @@
"lib/public/js/wn/ui/toolbar/bookmarks.js",
"lib/public/js/wn/ui/toolbar/toolbar.js",
"lib/public/js/legacy/widgets/form/form_dialog.js",
"lib/public/js/legacy/widgets/form/form.js",
"lib/public/js/legacy/widgets/form/print_format.js",
"lib/public/js/legacy/widgets/form/clientscriptAPI.js",

File diff suppressed because it is too large Load diff

View file

@ -780,7 +780,8 @@ _f.Frm.prototype.disable_save = function() {
// IMPORTANT: this function should be called in refresh event
cur_frm.save_disabled = true;
cur_frm.footer.hide_save();
cur_frm.appframe.buttons.Save.remove();
if(cur_frm.appframe.buttons.Save)
cur_frm.appframe.buttons.Save.remove();
delete cur_frm.appframe.buttons.Save
}

View file

@ -1,133 +0,0 @@
// Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
//
// MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// FrmDialog - twin of FrmContainer
// =======================================================================
_f.frm_dialog = null;
_f.calling_doc_stack = [];
_f.temp_access = {};
_f.FrmDialog = function() {
var me = this;
this.last_displayed = null;
var d = new Dialog(640, null, 'Edit Row');
this.body = $a(d.body, 'div', 'dialog_frm');
d.done_btn_area = $a(d.body, 'div', '', {margin:'8px'});
// done button
me.on_complete = function() {
if(me.table_form) {
// table form, just hide the dialog (saving will be done with the parent)
me.dialog.hide();
} else {
// form in dialog, so save it
var callback = function(r) {
var dn = cur_frm.docname;
if(!r.exc) {
// check if there is another dialog open?
me.dialog.hide();
}
// callback
if(me.on_save_callback)
me.on_save_callback(dn);
}
cur_frm.save('Save', callback);
}
}
// set title onshow
// -------------------------------------------
d.onshow = function() {
// set the dialog title
d.done_btn_area.innerHTML = '';
d.done_btn = $btn(d.done_btn_area, 'Save', null, null, 'green');
d.done_btn.onclick = function() { me.on_complete() };
if(me.table_form) {
d.set_title("Editing Row #" + (_f.cur_grid_ridx+1));
d.done_btn.innerHTML = 'Done Editing';
} else {
d.set_title(cur_frm.doctype==cur_frm.doctype ? (cur_frm.doctype) : (cur_frm.doctype + ': ' + cur_frm.docname));
d.done_btn.innerHTML = 'Save';
}
}
// on hide, refresh grid or call onsave
// -------------------------------------------
d.onhide = function() {
// if called from grid, refresh the row
if(_f.cur_grid) {
_f.cur_grid.refresh_row(_f.cur_grid_ridx, me.dn);
}
// set the new global cur_frm (if applicable)
if(wn.container.page.frm) {
cur_frm = wn.container.page.frm;
}
// call onhide
if(me.cur_frm.cscript.hide_dialog) {
me.cur_frm.cscript.hide_dialog();
}
// hide the form
//console.log(me.cur_frm.wrapper);
$(me.cur_frm.wrapper).toggle(false);
}
this.dialog = d;
}
// called from table edit
_f.edit_record = function(dt, dn) {
if(!_f.frm_dialog) {
_f.frm_dialog = new _f.FrmDialog();
}
var d = _f.frm_dialog;
wn.model.with_doctype(dt, function() {
wn.model.with_doc(dt, dn, function(dn) {
// load
if(!_f.frms[dt]) {
_f.frms[dt] = new _f.Frm(dt, d.body);
}
var f = _f.frms[dt];
if(f.meta.istable) {
f.parent_doctype = cur_frm.doctype;
f.parent_docname = cur_frm.docname;
}
d.cur_frm = f;
d.dn = dn;
d.table_form = f.meta.istable;
d.dialog.show();
// show the form
f.refresh(dn);
$(f.wrapper).removeClass('layout-wrapper')
.removeClass('layout-wrapper-background').toggle(true);
})
})
}

View file

@ -1,147 +0,0 @@
// Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
//
// MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Image field definition
// ======================================================================================
_f.ImageField = function() { this.images = {}; }
_f.ImageField.prototype = new Field();
_f.ImageField.prototype.onrefresh = function() {
$(this.label_span).toggle(false);
$(this.wrapper).find("img").remove();
if(this.df.options && this.frm.doc[this.df.options]) {
$("<img src='"+wn.utils.get_file_link(this.frm.doc[this.df.options])+"' style='max-width: 70%;'>")
.appendTo($(this.wrapper).empty());
} else {
$("<div class='missing-image'><i class='icon-camera'></i></div>")
.appendTo($(this.wrapper).empty())
}
}
_f.ImageField.prototype.set_disp = function (val) { }
_f.ImageField.prototype.set = function (val) { }
// Table
// ======================================================================================
_f.TableField = function() { };
_f.TableField.prototype = new Field();
_f.TableField.prototype.with_label = 0;
_f.TableField.prototype.make_body = function() {
if(this.perm[this.df.permlevel] && this.perm[this.df.permlevel][READ]) {
this.wrapper = $("<div>").appendTo(this.parent).get(0);
this.grid = new wn.ui.form.Grid({
frm: this.frm,
df: this.df,
perm: this.perm,
parent: $("<div>").appendTo(this.wrapper)
})
if(this.frm)
this.frm.grids[this.frm.grids.length] = this;
// description
if(this.df.description) {
$('<p class="text-muted small">' + this.df.description + '</p>')
.appendTo(this.wrapper);
}
}
}
_f.TableField.prototype.refresh = function() {
if(!this.grid)return;
// hide / show grid
var st = this.get_status();
if(!this.df['default'])
this.df['default']='';
this.grid.can_add_rows = false;
this.grid.can_edit = false;
if(st=='Write') {
this.grid.can_edit = true;
if(this.df['default'].toLowerCase()!='no toolbar')
this.grid.can_add_rows = true;
if(this.df['default'].toLowerCase()=='no add rows') {
this.grid.can_add_rows = false;
}
}
$(this.wrapper).toggle(st=='Write' || st=="Read");
this.grid.refresh();
}
_f.TableField.prototype.set = function(v) { }; // nothing
_f.TableField.prototype.set_input = function(v) { }; // nothing
_f.CodeField = function() { };
_f.CodeField.prototype = new Field();
_f.CodeField.prototype.make_input = function() {
var me = this;
this.label_span.innerHTML = this.df.label;
$(this.input_area).css({"min-height":"360px"});
if(this.df.fieldtype=='Text Editor') {
this.input = new wn.editors.BootstrapWYSIWYG({
parent: this.input_area,
change: function(value) {
me.set_value_and_run_trigger(value);
},
field: this
});
} else {
this.input = new wn.editors.ACE({
parent: this.input_area,
change: function(value) {
me.set_value_and_run_trigger(value);
},
field: this
});
}
this.get_value = function() {
return this.input.get_value();
}
}
_f.CodeField.prototype.set_value_and_run_trigger = function(value) {
if(locals[cur_frm.doctype][cur_frm.docname][this.df.fieldname] != value) {
this.set(value);
this.changing_value = true;
this.run_trigger();
this.changing_value = false;
}
}
_f.CodeField.prototype.set_disp = function(val) {
$y(this.disp_area, {width:'90%'})
if(this.df.fieldtype=='Text Editor') {
this.disp_area.innerHTML = val;
} else {
this.disp_area.innerHTML = '<textarea class="code_text" readonly=1>'
+val+'</textarea>';
}
}

View file

@ -1,96 +0,0 @@
// Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
//
// MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// features
// --------
// toolbar - standard and custom
// label - saved, submitted etc
// save / submit button toggle based on "saved" or not
// highlight and fade name based on refresh
_f.FrmHeader = Class.extend({
init: function(parent, frm) {
this.appframe = frm.appframe;
this.$w = this.appframe.$w;
this.frm = frm;
this.appframe.add_home_breadcrumb();
this.appframe.add_module_icon(frm.meta.module)
this.appframe.set_views_for(frm.meta.name, "form");
if(!frm.meta.issingle) {
if(frm.cscript.add_list_breadcrumb) {
frm.cscript.add_list_breadcrumb(this.appframe);
} else {
this.appframe.add_list_breadcrumb(frm.meta.name);
}
}
this.appframe.add_breadcrumb("icon-file");
},
refresh: function() {
var me = this;
var title = this.frm.docname;
if(title.length > 30) {
title = title.substr(0,30) + "...";
}
this.appframe.set_title(title, wn._(this.frm.docname));
this.appframe.set_sub_title(this.frm.doc.__islocal ? "Not Saved"
: "Last Updated on " + dateutil.str_to_user(this.frm.doc.modified) + " by " + this.frm.doc.modified_by)
//this.refresh_timestamps();
},
refresh_timestamps: function() {
this.$w.find(".avatar").remove();
var doc = this.frm.doc;
if(doc.__islocal || !doc.owner || !doc.modified_by)
return;
$(repl('<span class="avatar avatar avatar-small">\
<img title="%(created_by)s" src="%(avatar_created)s"/></span>\
<span class="avatar avatar avatar-small">\
<img title="%(modified_by)s" src="%(avatar_modified)s"/></span>', {
created_by: wn.user_info(doc.owner).fullname,
avatar_created: wn.utils.get_file_link(wn.user_info(doc.owner).image),
modified_by: wn.user_info(doc.modified_by).fullname,
avatar_modified: wn.utils.get_file_link(wn.user_info(doc.modified_by).image),
})).insertAfter(this.$w.find(".appframe-title"));
this.$w.find(".avatar:eq(0)").popover({
trigger:"hover",
title: wn._("Created By"),
content: wn.user_info(this.frm.doc.owner).fullname
+" on "+ dateutil.str_to_user(this.frm.doc.creation)
});
this.$w.find(".avatar:eq(1)").popover({
trigger:"hover",
title: wn._("Modified By"),
content: wn.user_info(this.frm.doc.modified_by).fullname
+" on "+ dateutil.str_to_user(this.frm.doc.modified)
});
this.$w.find('.avatar img').centerImage();
},
hide_close: function() {
this.$w.find('.close').toggle(false);
}
})

View file

@ -195,6 +195,7 @@ $.extend(wn.model, {
set_value: function(doctype, name, fieldname, value, fieldtype) {
/* help: Set a value locally (if changed) and execute triggers */
if(!name) name = doctype;
var doc = locals[doctype] && locals[doctype][name] || null;
if(doc && doc[fieldname] !== value) {
doc[fieldname] = value;

View file

@ -65,7 +65,14 @@ wn.get_route_str = function(route) {
}
wn.set_route = function() {
route = $.map(arguments, function(a) { return a ? encodeURIComponent(a) : null; }).join('/');
route = $.map(arguments, function(a) {
if($.isPlainObject(a)) {
wn.route_options = a;
return null;
} else {
return a ? encodeURIComponent(a) : null;
}
}).join('/');
window.location.hash = route;

View file

@ -237,6 +237,21 @@ wn.views.DocListView = wn.ui.Listing.extend({
checks.attr('checked', $(checks.get(0)).attr('checked') ? false : "checked");
}, 'icon-ok');
}
if(in_list(user_roles, "System Manager")) {
var meta = locals.DocType[this.doctype];
if(meta.allow_import || meta.document_type==="Master") {
this.add_button(wn._("Import"), function() {
wn.set_route("data-import-tool", {
doctype: me.doctype
})
}, "icon-upload")
};
this.add_button(wn._("Customize"), function() {
wn.set_route("Form", "Customize Form", {
doctype: me.doctype
})
}, "icon-glass");
}
},
get_checked_items: function() {
return $.map(this.$page.find('.list-delete:checked'), function(e) {

View file

@ -82,6 +82,12 @@ class Bean:
def __iter__(self):
return self.doclist.__iter__()
@property
def meta(self):
if not hasattr(self, "_meta"):
self._meta = webnotes.get_doctype(self.doc.doctype)
return self._meta
def from_compressed(self, data, docname):
from webnotes.model.utils import expand
self.set_doclist(expand(data))
@ -205,9 +211,12 @@ class Bean:
def update_parent_info(self):
idx_map = {}
is_local = cint(self.doc.fields.get("__islocal"))
parentfields = [d.fieldname for d in self.meta.get({"doctype": "DocField", "fieldtype": "Table"})]
for i, d in enumerate(self.doclist[1:]):
if d.parentfield:
if not d.parentfield in parentfields:
webnotes.msgprint("Bad parentfield %s" % parentfield,
raise_exception=True)
d.parenttype = self.doc.doctype
d.parent = self.doc.name
if not d.idx:
@ -277,7 +286,7 @@ class Bean:
self.doc.fields["__islocal"] = 1
if webnotes.in_test:
if webnotes.get_doctype(self.doc.doctype).get_field("naming_series"):
if self.meta.get_field("naming_series"):
self.doc.naming_series = "_T-" + self.doc.doctype + "-"
return self.save()
@ -358,25 +367,25 @@ class Bean:
def check_mandatory(self):
missing = []
from webnotes.model.meta import get_mandatory_fields
for doc in self.doclist:
for fieldname, label, fieldtype in get_mandatory_fields(doc.doctype):
msg = ""
if fieldtype == "Table":
if not self.doclist.get({"parentfield": fieldname}):
msg = _("Error") + ": " + _("Data missing in table") + ": " + _(label)
for df in self.meta:
if df.doctype=="DocField" and df.reqd and df.parent==doc.doctype:
msg = ""
if df.fieldtype == "Table":
if not self.doclist.get({"parentfield": df.fieldname}):
msg = _("Error") + ": " + _("Data missing in table") + ": " + _(label)
elif doc.fields.get(fieldname) is None:
msg = _("Error") + ": "
if doc.parentfield:
msg += _("Row") + (" # %d: " % doc.idx)
elif doc.fields.get(df.fieldname) is None:
msg = _("Error") + ": "
if doc.parentfield:
msg += _("Row") + (" # %d: " % doc.idx)
msg += _("Value missing for") + ": " + _(label)
msg += _("Value missing for") + ": " + _(df.label)
if msg:
missing.append([msg, fieldname])
if msg:
missing.append([msg, df.fieldname])
if missing:
if missing:
for msg, fieldname in missing:
msgprint(msg)

View file

@ -392,7 +392,7 @@ class Document:
# add missing parentinfo (if reqd)
if self.parent and not (self.parenttype and self.parentfield):
self.update_parentinfo()
if self.parent and not self.idx:
self.set_idx()

View file

@ -112,16 +112,4 @@ def get_field_precision(df, doc):
decimal_str, comma_str, precision = get_number_format_info(number_format or \
webnotes.conn.get_default("number_format") or "#,###.##")
return precision
doctype_mandatory_fields = {}
def get_mandatory_fields(doctype, parenttype=None):
if not doctype_mandatory_fields.get(doctype):
doctype_mandatory_fields[doctype] = []
meta = webnotes.get_doctype(parenttype or doctype)
for df in meta.get({"doctype": "DocField", "parent": doctype}):
if cint(df.reqd):
doctype_mandatory_fields[doctype].append((df.fieldname, df.label, df.fieldtype))
return doctype_mandatory_fields[doctype]
return precision

View file

@ -173,7 +173,7 @@ def _run_test(path, filename, verbose):
test_suite.addTest(unittest.TestLoader().loadTestsFromModule(module))
unittest.TextTestRunner(verbosity=1+(verbose and 1 or 0)).run(test_suite)
if __name__=="__main__":
def main():
import argparse
parser = argparse.ArgumentParser(description='Run tests.')
@ -208,4 +208,7 @@ if __name__=="__main__":
make_test_records(doctype, verbose=args.verbose)
test_suite.addTest(unittest.TestLoader().loadTestsFromModule(sys.modules[args.module]))
unittest.TextTestRunner(verbosity=1+(args.verbose and 1 or 0)).run(test_suite)
unittest.TextTestRunner(verbosity=1+(args.verbose and 1 or 0)).run(test_suite)
if __name__=="__main__":
main()

12
wnf.py
View file

@ -245,8 +245,7 @@ def setup_options():
parser.add_option("--append_future_import", default=False, action="store_true",
help="append from __future__ import unicode literals to py files")
parser.add_option("--test", help="Run test", metavar="MODULE",
nargs=1)
parser.add_option("--test", help="Run test", action="store_true", default=False)
parser.add_option("--build_message_files", default=False, action="store_true",
help="Build message files for translation")
@ -461,13 +460,10 @@ def run():
print '\n'.join(webnotes.message_log)
if options.test is not None:
module_name = options.test
import unittest
del sys.argv[1:]
# is there a better way?
exec ('from %s import *' % module_name) in globals()
unittest.main()
import webnotes.test_runner
webnotes.test_runner.main()
elif options.build_message_files:
import webnotes.translate