From 1291a480aac073f40fc8074234a0494dbafcd13c Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 26 Feb 2013 16:36:40 +0530 Subject: [PATCH 1/9] added Time Log --- public/js/wn/model/perm.js | 13 +++++++++---- public/js/wn/views/calendar.js | 2 +- webnotes/translate.py | 2 +- webnotes/widgets/reportview.py | 2 ++ 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/public/js/wn/model/perm.js b/public/js/wn/model/perm.js index 9068c6bf9b..00cbb23722 100644 --- a/public/js/wn/model/perm.js +++ b/public/js/wn/model/perm.js @@ -89,13 +89,18 @@ $.extend(wn.perm, { get_match_rule: function(doctype) { var match_rules = {}; + var match = true; $.each(wn.model.get("DocPerm", {parent:doctype}), function(i, p) { - if(p.permlevel==0 && p.match && in_list(user_roles, p.role)) { - match_keys = wn.perm.get_match_keys(p.match); - match_rules[match_keys[0]] = wn.defaults.get_user_defaults(match_keys[1]); + if(p.permlevel==0 && in_list(user_roles, p.role)) { + if(p.match) { + match_keys = wn.perm.get_match_keys(p.match); + match_rules[match_keys[0]] = wn.defaults.get_user_defaults(match_keys[1]); + } else { + match = false; + } } }); - return match_rules; + return match ? match_rules : {}; }, get_match_keys: function(match) { diff --git a/public/js/wn/views/calendar.js b/public/js/wn/views/calendar.js index f746af0b55..3ca64584db 100644 --- a/public/js/wn/views/calendar.js +++ b/public/js/wn/views/calendar.js @@ -57,7 +57,7 @@ wn.views.Calendar = Class.extend({ var me = this; this.$wrapper = $(this.page).find(".layout-main"); this.$cal = $("
").appendTo(this.$wrapper); - wn.utils.set_footnote(this, this.$wrapper, wn._("Select or drag across dates to create a new event.")); + wn.utils.set_footnote(this, this.$wrapper, wn._("Select or drag across time slots to create a new event.")); // // $('
') // .html(wn._("Select dates to create a new ") + wn._(me.doctype)) diff --git a/webnotes/translate.py b/webnotes/translate.py index 432ae399a8..b86cd7db92 100644 --- a/webnotes/translate.py +++ b/webnotes/translate.py @@ -52,7 +52,7 @@ def build_message_files(): build_for_framework('lib/public/js/wn', 'js') build_for_framework('app/public/js', 'js', with_doctype_names=True) - build_for_modules() + #build_for_modules() def build_for_pages(path): """make locale files for framework py and js (all)""" diff --git a/webnotes/widgets/reportview.py b/webnotes/widgets/reportview.py index 726fa3c8d7..b656f2adb0 100644 --- a/webnotes/widgets/reportview.py +++ b/webnotes/widgets/reportview.py @@ -220,6 +220,8 @@ def build_match_conditions(doctype, fields=None): if match_conditions and match: return '('+ ' or '.join(match_conditions) +')' + else: + return "" def get_tables(doctype, fields): """extract tables from fields""" From e0ba00325ee788699eaf8ebcf96b4b42777bff77 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 26 Feb 2013 18:03:45 +0530 Subject: [PATCH 2/9] updated restort_database so it restores from the user id --- webnotes/install_lib/install.py | 9 ++++----- webnotes/model/db_schema.py | 6 +++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/webnotes/install_lib/install.py b/webnotes/install_lib/install.py index 5a58a0018e..c59ddb1b15 100755 --- a/webnotes/install_lib/install.py +++ b/webnotes/install_lib/install.py @@ -41,7 +41,7 @@ class Installer: root_password = getpass.getpass("MySQL root password: ") self.root_password = root_password - + self.conn = webnotes.db.Database(user=root_login, password=root_password) webnotes.conn=self.conn webnotes.session= webnotes._dict({'user':'Administrator'}) @@ -55,8 +55,7 @@ class Installer: self.dbman.delete_user(target) # create user and db - self.dbman.create_user(target, - hasattr(conf, 'db_password') and conf.db_password or password) + self.dbman.create_user(target, conf.db_password) if verbose: print "Created user %s" % target @@ -82,7 +81,7 @@ class Installer: source_given = False source_path = os.path.join(os.path.sep.join(os.path.abspath(webnotes.__file__).split(os.path.sep)[:-3]), 'data', 'Framework.sql') - self.dbman.restore_database(target, source_path, self.root_password) + self.dbman.restore_database(target, source_path, target, conf.db_password) if verbose: print "Imported from database %s" % source_path # fresh app @@ -91,8 +90,8 @@ class Installer: self.install_app() # update admin password - self.update_admin_password(password) self.create_auth_table() + self.update_admin_password(password) return target def install_app(self): diff --git a/webnotes/model/db_schema.py b/webnotes/model/db_schema.py index 7ee9ef6497..d532d12fc8 100644 --- a/webnotes/model/db_schema.py +++ b/webnotes/model/db_schema.py @@ -373,13 +373,13 @@ class DbManager: """get list of databases""" return [d[0] for d in self.conn.sql("SHOW DATABASES")] - def restore_database(self,target,source,root_password): + def restore_database(self,target,source,user,password): from webnotes.utils import make_esc esc = make_esc('$ ') try: - ret = os.system("mysql -u root -p%s %s < %s" % \ - (esc(root_password), esc(target), source)) + ret = os.system("mysql -u %s -p%s %s < %s" % \ + (esc(user), esc(password), esc(target), source)) except Exception,e: raise e From 7bf5b66338ceb54b7af07d120fe302c127a4b85d Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 27 Feb 2013 10:51:25 +0530 Subject: [PATCH 3/9] website: fixed social icons and don't take lang from session for demo --- webnotes/sessions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webnotes/sessions.py b/webnotes/sessions.py index 38ff3189d5..f4062daa76 100644 --- a/webnotes/sessions.py +++ b/webnotes/sessions.py @@ -147,7 +147,7 @@ class Session: data = self.get_session_record() if data: # set language - if data.lang: + if data.lang and self.user!="demo@erpnext.com": webnotes.lang = data.lang self.data = webnotes._dict({'data': data, 'user':data.user, 'sid': self.sid}) From b1c5dfaf20fb48a23ed0a7df9bf5c361c235d97d Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 27 Feb 2013 15:39:41 +0530 Subject: [PATCH 4/9] fixes to form (translate), moduleview (don't count cancelled records) --- public/js/legacy/widgets/form/form.js | 6 +- webnotes/tests/modules.py | 105 -------------------------- webnotes/widgets/moduleview.py | 2 +- 3 files changed, 4 insertions(+), 109 deletions(-) delete mode 100644 webnotes/tests/modules.py diff --git a/public/js/legacy/widgets/form/form.js b/public/js/legacy/widgets/form/form.js index 6a2c263bee..b579ef7c3a 100644 --- a/public/js/legacy/widgets/form/form.js +++ b/public/js/legacy/widgets/form/form.js @@ -133,15 +133,15 @@ _f.Frm.prototype.setup_print_layout = function() { }); var appframe = this.print_wrapper.appframe; - appframe.add_button("View Details", function() { + appframe.add_button(wn._("View Details"), function() { me.edit_doc(); }).addClass("btn-success"); - appframe.add_button("Print", function() { + appframe.add_button(wn._("Print"), function() { me.print_doc(); }, 'icon-print'); - this.$print_view_select = appframe.add_select("Select Preview", this.print_formats) + this.$print_view_select = appframe.add_select(wn._("Select Preview"), this.print_formats) .css({"float":"right"}) .val(this.print_formats[0]) .change(function() { diff --git a/webnotes/tests/modules.py b/webnotes/tests/modules.py deleted file mode 100644 index d773179edb..0000000000 --- a/webnotes/tests/modules.py +++ /dev/null @@ -1,105 +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. -# - -from __future__ import unicode_literals -""" - Tests for modules: - - To execute webnotes/tests.py webnotes.tests.modules - - Uses Sandbox DocType for testing -""" -import unittest -import webnotes - -from webnotes.modules import Module - -class ModuleTest(unittest.TestCase): - def setUp(self): - webnotes.conn.begin() - - def update_timestamp(self, path): - """ - Just open a file for write and save it so that the timetamp changes - """ - import os - path = os.path.join(Module('core').get_path(), path) - c = file(path,'r').read() - file(path,'w').write(c) - - def test_module_import(self): - """ - Import a txt file into the database and check if a new column has been added - """ - webnotes.conn.rollback() - webnotes.conn.sql("alter table tabSandbox drop column `to_be_dropped`", ignore_ddl=1) - webnotes.conn.begin() - - # delete from table - webnotes.conn.sql("delete from tabDocField where parent='Sandbox' and fieldname='to_be_dropped'") - - self.update_timestamp('doctype/sandbox/sandbox.txt') - - # reload - Module('core').reload('DocType','Sandbox') - - # commit re-adding - webnotes.conn.commit() - - # check if column created - self.assertTrue('to_be_dropped' in [r[0] for r in webnotes.conn.sql("desc tabSandbox")]) - - # check if imported in table - self.assertTrue(len(webnotes.conn.sql("select name from tabDocField where parent='Sandbox' and fieldname='to_be_dropped'"))==1) - - - def test_read_js_code(self): - """ - Read a js code file - """ - data = Module('core').get_doc_file('DocType','Sandbox','.js').read() - self.assertTrue('//test3456' in data) - self.assertTrue('//import3456' in data) - - def test_sql_file(self): - """ - Test an sql file - """ - webnotes.conn.rollback() - webnotes.conn.sql("drop trigger if exists sandbox_trigger") - self.update_timestamp('doctype/sandbox/my_trigger.sql') - Module('core').get_file('doctype','sandbox','my_trigger.sql').sync() - self.assertTrue(webnotes.conn.sql("show triggers like 'tabSandbox'")[0][0]=='sandbox_trigger') - - def test_sync_all(self): - """ - Test sync all (rerun the sql file test calling sync_all) - """ - - webnotes.conn.rollback() - webnotes.conn.sql("drop trigger if exists sandbox_trigger") - self.update_timestamp('doctype/sandbox/my_trigger.sql') - Module('core').sync_all() - self.assertTrue(webnotes.conn.sql("show triggers like 'tabSandbox'")[0][0]=='sandbox_trigger') - - def tearDown(self): - webnotes.conn.rollback() \ No newline at end of file diff --git a/webnotes/widgets/moduleview.py b/webnotes/widgets/moduleview.py index 01291905d5..77cfb3f370 100644 --- a/webnotes/widgets/moduleview.py +++ b/webnotes/widgets/moduleview.py @@ -41,7 +41,7 @@ def get_open_count(doctypes): if d in queries: condition = queries[d] key = condition.keys()[0] - query = "select count(*) from `tab%s` where `%s`=%s" % (d, key, '%s') + query = "select count(*) from `tab%s` where `%s`=%s and docstatus<2" % (d, key, '%s') count[d] = webnotes.conn.sql(query, condition[key])[0][0] or "" conditions[d] = condition From e686b6ddd84edfa3a4176fd2c1373217299894a8 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 28 Feb 2013 18:42:45 +0530 Subject: [PATCH 5/9] added time log batch --- core/doctype/doctype/doctype.py | 5 +++-- public/js/wn/views/doclistview.js | 20 +++++++++++++------- public/js/wn/views/listview.js | 14 ++++++++------ webnotes/db.py | 21 ++++++++++++++------- 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/core/doctype/doctype/doctype.py b/core/doctype/doctype/doctype.py index eb5123d640..4bb06fc76a 100644 --- a/core/doctype/doctype/doctype.py +++ b/core/doctype/doctype/doctype.py @@ -160,8 +160,9 @@ class DocType: new.label = 'Amended From' new.fieldtype = 'Link' new.fieldname = 'amended_from' - new.options = "Sales Invoice" - new.permlevel = 1 + new.options = self.doc.name + new.permlevel = 0 + new.read_only = 1 new.print_hide = 1 new.no_copy = 1 new.idx = max_idx + 1 diff --git a/public/js/wn/views/doclistview.js b/public/js/wn/views/doclistview.js index f87e89e4d4..78caa7a41b 100644 --- a/public/js/wn/views/doclistview.js +++ b/public/js/wn/views/doclistview.js @@ -78,13 +78,16 @@ wn.views.DocListView = wn.ui.Listing.extend({ me.can_delete = wn.model.can_delete(me.doctype); me.meta = locals.DocType[me.doctype]; me.$page.find('.wnlist-area').empty(), - me.setup_docstatus_filter(); me.setup_listview(); + me.setup_docstatus_filter(); me.init_list(); me.init_stats(); me.add_delete_option(); - me.make_help(); me.show_match_help(); + if(me.listview.settings.onload) { + me.listview.settings.onload(me); + } + me.make_help(); }, show_match_help: function() { var me = this; @@ -213,7 +216,7 @@ wn.views.DocListView = wn.ui.Listing.extend({ }, add_delete_option: function() { var me = this; - if(this.can_delete) { + if(this.can_delete || this.listview.settings.selectable) { this.add_button(wn._('Delete'), function() { me.delete_items(); }, 'icon-remove'); this.add_button(wn._('Select All'), function() { var checks = me.$page.find('.list-delete'); @@ -221,11 +224,14 @@ wn.views.DocListView = wn.ui.Listing.extend({ }, 'icon-ok'); } }, + get_checked_items: function() { + return $.map(this.$page.find('.list-delete:checked'), function(e) { + return $(e).data('data'); + }); + }, delete_items: function() { var me = this; - var dl = $.map(me.$page.find('.list-delete:checked'), function(e) { - return $(e).data('name'); - }); + var dl = this.get_checked_items(); if(!dl.length) return; @@ -235,7 +241,7 @@ wn.views.DocListView = wn.ui.Listing.extend({ wn.call({ method: 'webnotes.widgets.reportview.delete_items', args: { - items: dl, + items: $.map(dl, function(d, i) { return d.name }), doctype: me.doctype }, callback: function() { diff --git a/public/js/wn/views/listview.js b/public/js/wn/views/listview.js index b70d25faab..6512f1b012 100644 --- a/public/js/wn/views/listview.js +++ b/public/js/wn/views/listview.js @@ -64,10 +64,10 @@ wn.views.ListView = Class.extend({ set_columns: function() { this.columns = []; var me = this; - if(wn.model.can_delete(this.doctype)) { - this.columns.push({width: '3%', content:'check'}) + if(wn.model.can_delete(this.doctype) || this.settings.selectable) { + this.columns.push({width: '4%', content:'check'}) } - this.columns.push({width: '4%', content:'avatar'}); + this.columns.push({width: '7%', content:'avatar'}); if(wn.model.is_submittable(this.doctype)) { this.columns.push({width: '3%', content:'docstatus', css: {"text-align": "center"}}); @@ -173,8 +173,10 @@ wn.views.ListView = Class.extend({ + wn.user_info(data.modified_by).fullname)); } else if(opts.content=='check') { - $(parent).append(''); - $(parent).find('input').data('name', data.name); + $('') + .data('name', data.name) + .data('data', data) + .appendTo(parent) } else if(opts.content=='docstatus') { $(parent).append(repl(' \ @@ -266,7 +268,7 @@ wn.views.ListView = Class.extend({ } // title - if(!in_list(["avatar"], opts.content)) { + if(!in_list(["avatar", "_user_tags", "check"], opts.content)) { $(parent).attr("title", (opts.title || opts.content) + ": " + (data[opts.content] || "Not Set")) .tooltip(); diff --git a/webnotes/db.py b/webnotes/db.py index fbdbc72498..c5b79b8365 100644 --- a/webnotes/db.py +++ b/webnotes/db.py @@ -245,11 +245,15 @@ class Database: def get(self, doctype, filters=None, as_dict=True): return self.get_value(doctype, filters, "*", as_dict=as_dict) - - def get_value(self, doctype, filters=None, fieldname="name", ignore=None, as_dict=False): + + def get_value(self, doctype, filters=None, fieldname="name", ignore=None, as_dict=False, debug=False): """Get a single / multiple value from a record. For Single DocType, let filters be = None""" + + ret = self.get_values(doctype, filters, fieldname, ignore, as_dict, debug) + return ret and (len(ret[0]) > 1 and ret[0] or ret[0][0]) or None + def get_values(self, doctype, filters=None, fieldname="name", ignore=None, as_dict=False, debug=False): if filters is not None and (filters!=doctype or filters=='DocType'): if fieldname!="*" and isinstance(fieldname, basestring): fieldname = "`" + fieldname + "`" @@ -260,24 +264,27 @@ class Database: try: r = self.sql("select %s from `tab%s` where %s" % (fl, doctype, - conditions), filters, as_dict) + conditions), filters, as_dict, debug=debug) except Exception, e: if e.args[0]==1054 and ignore: return None else: raise e - return r and (len(r[0]) > 1 and r[0] or r[0][0]) or None + return r else: fieldname = isinstance(fieldname, basestring) and [fieldname] or fieldname r = self.sql("select field, value from tabSingles where field in (%s) and \ - doctype=%s" % (', '.join(['%s']*len(fieldname)), '%s'), tuple(fieldname) + (doctype,), as_dict=False) + doctype=%s" % (', '.join(['%s']*len(fieldname)), '%s'), tuple(fieldname) + (doctype,), as_dict=False, debug=debug) if as_dict: - return r and webnotes._dict(r) or None + return r and [webnotes._dict(r)] or [] else: - return r and (len(r) > 1 and [i[0] for i in r] or r[0][1]) or None + if r: + return [[i[1] for i in r]] + else: + return [] def set_value(self, dt, dn, field, val, modified=None, modified_by=None): from webnotes.utils import now From 71f2f3a1b1efebfe4c99395d6c818ec3fc6d54ac Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 1 Mar 2013 18:24:50 +0530 Subject: [PATCH 6/9] completed Time Log / Time Log Batch --- core/doctype/profile/profile.txt | 4 +- public/js/legacy/widgets/form/form.js | 4 ++ public/js/wn/model/meta.js | 12 ++++ public/js/wn/model/model.js | 83 +++++++++++++++++++++++++-- public/js/wn/views/listview.js | 3 +- public/js/wn/views/reportview.js | 2 +- webnotes/__init__.py | 2 +- webnotes/client.py | 7 ++- webnotes/model/bean.py | 2 + webnotes/model/doctype.py | 1 + webnotes/utils/__init__.py | 8 ++- webnotes/widgets/reportview.py | 3 +- 12 files changed, 116 insertions(+), 15 deletions(-) diff --git a/core/doctype/profile/profile.txt b/core/doctype/profile/profile.txt index 921cb89f28..f4ca204eec 100644 --- a/core/doctype/profile/profile.txt +++ b/core/doctype/profile/profile.txt @@ -2,7 +2,7 @@ { "creation": "2013-02-14 17:37:36", "docstatus": 0, - "modified": "2013-02-26 10:45:10", + "modified": "2013-03-01 10:18:03", "modified_by": "Administrator", "owner": "Administrator" }, @@ -131,7 +131,7 @@ "fieldname": "language", "fieldtype": "Select", "label": "Language", - "options": "\n\u0627\u0644\u0639\u0631\u0628\u064a\u0629\nenglish\nespa\u00f1ol\nfran\u00e7ais\n\u0939\u093f\u0902\u0926\u0940\nHrvatski\nnederlands\nportugu\u00eas\nportugu\u00eas brasileiro\n\u0441\u0440\u043f\u0441\u043a\u0438\n\u0ba4\u0bae\u0bbf\u0bb4\u0bcd\n\u0e44\u0e17\u0e22" + "options": "\n\u0627\u0644\u0639\u0631\u0628\u064a\u0629\nDeutsch\nenglish\nespa\u00f1ol\nfran\u00e7ais\n\u0939\u093f\u0902\u0926\u0940\nHrvatski\nnederlands\nportugu\u00eas\nportugu\u00eas brasileiro\n\u0441\u0440\u043f\u0441\u043a\u0438\n\u0ba4\u0bae\u0bbf\u0bb4\u0bcd\n\u0e44\u0e17\u0e22" }, { "doctype": "DocField", diff --git a/public/js/legacy/widgets/form/form.js b/public/js/legacy/widgets/form/form.js index b579ef7c3a..a27f9e57a9 100644 --- a/public/js/legacy/widgets/form/form.js +++ b/public/js/legacy/widgets/form/form.js @@ -652,6 +652,10 @@ _f.Frm.prototype.cleanup_refresh = function() { var fn = me.meta.autoname.substr(6); cur_frm.toggle_display(fn, false); } + + if(me.meta.autoname=="naming_series:" && !me.doc.__islocal) { + cur_frm.toggle_display("naming_series", false); + } } // Resolve "depends_on" and show / hide accordingly diff --git a/public/js/wn/model/meta.js b/public/js/wn/model/meta.js index a7760d3f88..202a1e30df 100644 --- a/public/js/wn/model/meta.js +++ b/public/js/wn/model/meta.js @@ -64,6 +64,18 @@ $.extend(wn.meta, { } }, + has_field: function(dt, fn) { + return wn.meta.docfield_map[dt][fn]; + }, + + get_parentfield: function(parent_dt, child_dt) { + var df = wn.model.get("DocField", {parent:parent_dt, fieldtype:"Table", + options:child_dt}) + if(!df.length) + throw "parentfield not found for " + parent_dt + ", " + child_dt; + return df[0].fieldname; + }, + get_label: function(dt, fn, dn) { if(fn==="owner") { return "Owner"; diff --git a/public/js/wn/model/model.js b/public/js/wn/model/model.js index 60a26992a1..c64e5ec62f 100644 --- a/public/js/wn/model/model.js +++ b/public/js/wn/model/model.js @@ -21,6 +21,7 @@ // wn.provide('wn.model'); +wn.provide("wn.model.map_info"); $.extend(wn.model, { no_value_type: ['Section Break', 'Column Break', 'HTML', 'Table', @@ -75,6 +76,9 @@ $.extend(wn.model, { if(meta.__calendar_js) { eval(meta.__calendar_js); } + if(meta.__map_js) { + eval(meta.__map_js); + } callback(r); } }); @@ -228,23 +232,90 @@ $.extend(wn.model, { delete locals[doctype][name]; }, - copy_doc: function(dt, dn, from_amend) { + get_no_copy_list: function(doctype) { var no_copy_list = ['name','amended_from','amendment_date','cancel_reason']; + $.each(wn.model.get("DocField", {parent:doctype}), function(i, df) { + if(cint(df.no_copy)) no_copy_list.push(df.fieldname); + }) + return no_copy_list; + }, + + copy_doc: function(dt, dn, from_amend) { + var no_copy_list = wn.model.get_no_copy_list(dt); var newdoc = wn.model.get_new_doc(dt); for(var key in locals[dt][dn]) { - // dont copy name and blank fields - var df = wn.meta.get_docfield(dt, key); - + // dont copy name and blank fields if(key.substr(0,2)!='__' - && !in_list(no_copy_list, key) - && !(df && (!from_amend && cint(df.no_copy)==1))) { + && !in_list(no_copy_list, key)) { newdoc[key] = locals[dt][dn][key]; } } return newdoc; }, + // args: source (doclist), target (doctype), table_map, field_map, callback + map: function(args) { + wn.model.with_doctype(args.target, function() { + var map_info = wn.model.map_info[args.target] + if(map_info) + map_info = map_info[args.source[0].doctype]; + if(!map_info) { + map_info = { + table_map: {}, + field_map: {} + } + } + + // main + var target = wn.model.map_doc(args.source[0], args.target, map_info.field_map[args.target]); + + // children + $.each(map_info.table_map, function(child_target, child_source) { + $.each($.map(args.source, function(d) + { if(d.doctype==child_source) return d; else return null; }), function(i, d) { + var child = wn.model.map_doc(d, child_target, map_info.field_map[child_target]); + $.extend(child, { + parent: target.name, + parenttype: target.doctype, + parentfield: wn.meta.get_parentfield(target.doctype, child.doctype), + idx: i+1 + }); + }); + }); + + if(args.callback) { + args.callback(target); + } else { + wn.set_route("Form", target.doctype, target.name); + } + }); + }, + + // map a single doc to a new doc of given DocType and field_map + map_doc: function(source, doctype, field_map) { + var new_doc = wn.model.get_new_doc(doctype); + var no_copy_list = wn.model.get_no_copy_list(doctype); + if(!field_map) field_map = {}; + delete no_copy_list[no_copy_list.indexOf("name")]; + + for(fieldname in wn.meta.docfield_map[doctype]) { + var df = wn.meta.docfield_map[doctype][fieldname]; + if(!df.no_copy) { + var source_key = field_map[df.fieldname] || df.fieldname; + if(source_key.substr(0,1)=="=") { + var value = source_key.substr(1); + } else { + var value = source[source_key]; + } + if(value!==undefined) { + new_doc[df.fieldname] = value; + } + } + } + return new_doc; + }, + delete_doc: function(doctype, docname, callback) { wn.confirm("Permanently delete "+ docname + "?", function() { wn.call({ diff --git a/public/js/wn/views/listview.js b/public/js/wn/views/listview.js index 6512f1b012..af4b9c3b12 100644 --- a/public/js/wn/views/listview.js +++ b/public/js/wn/views/listview.js @@ -57,7 +57,8 @@ wn.views.ListView = Class.extend({ // additional fields if(this.settings.add_fields) { $.each(this.settings.add_fields, function(i, d) { - me.fields.push(d); + if(me.fields.indexOf(d)==-1) + me.fields.push(d); }); } }, diff --git a/public/js/wn/views/reportview.js b/public/js/wn/views/reportview.js index cb872b436b..71caf4a49f 100644 --- a/public/js/wn/views/reportview.js +++ b/public/js/wn/views/reportview.js @@ -369,7 +369,7 @@ wn.views.ReportView = wn.ui.Listing.extend({ sort_by: me.sort_by_select.val(), sort_order: me.sort_order_select.val(), sort_by_next: me.sort_by_next_select.val(), - sort_order_next: me.sort_order_next_select.val() + sort_order_next: me.sort_order_next_select.val() }) }, callback: function(r) { diff --git a/webnotes/__init__.py b/webnotes/__init__.py index 17d470f2bd..bbb89ec67c 100644 --- a/webnotes/__init__.py +++ b/webnotes/__init__.py @@ -286,7 +286,7 @@ def generate_hash(): import hashlib, time return hashlib.sha224(str(time.time())).hexdigest() -def get_obj(dt = None, dn = None, doc=None, doclist=[], with_children = 0): +def get_obj(dt = None, dn = None, doc=None, doclist=[], with_children = True): from webnotes.model.code import get_obj return get_obj(dt, dn, doc, doclist, with_children) diff --git a/webnotes/client.py b/webnotes/client.py index dfef660a2f..25ac4440b0 100644 --- a/webnotes/client.py +++ b/webnotes/client.py @@ -33,10 +33,13 @@ def get(doctype, name=None, filters=None): return [d.fields for d in webnotes.bean(doctype, name).doclist] @webnotes.whitelist() -def get_value(doctype, fieldname, filters=None): +def get_value(doctype, fieldname, filters=None, as_dict=True): + if not webnotes.has_permission(doctype): + webnotes.msgprint("No Permission", raise_exception=True) + if fieldname and fieldname.startswith("["): fieldname = json.loads(fieldname) - return webnotes.conn.get_value(doctype, json.loads(filters), fieldname) + return webnotes.conn.get_value(doctype, json.loads(filters), fieldname, as_dict=as_dict) @webnotes.whitelist() def insert(doclist): diff --git a/webnotes/model/bean.py b/webnotes/model/bean.py index 9fd67c818d..975e936b01 100644 --- a/webnotes/model/bean.py +++ b/webnotes/model/bean.py @@ -278,6 +278,7 @@ class Bean: webnotes.msgprint("Only submitted can be cancelled", raise_exception=1) self.to_docstatus = 2 self.prepare_for_save(1) + self.run_method('before_cancel') self.save_main() self.save_children() self.run_method('on_cancel') @@ -292,6 +293,7 @@ class Bean: if self.ignore_permissions or webnotes.has_permission(self.doc.doctype, "write", self.doc): self.to_docstatus = 1 self.prepare_for_save(1) + self.run_method('before_update_after_submit') self.save_main() self.save_children() self.run_method('on_update_after_submit') diff --git a/webnotes/model/doctype.py b/webnotes/model/doctype.py index 33daa34b98..e317868ae7 100644 --- a/webnotes/model/doctype.py +++ b/webnotes/model/doctype.py @@ -276,6 +276,7 @@ def add_code(doctype, doclist): _add_code(scrub(doc.name) + '.css', '__css') _add_code('%s_list.js' % scrub(doc.name), '__list_js') _add_code('%s_calendar.js' % scrub(doc.name), '__calendar_js') + _add_code('%s_map.js' % scrub(doc.name), '__map_js') add_embedded_js(doc) def add_embedded_js(doc): diff --git a/webnotes/utils/__init__.py b/webnotes/utils/__init__.py index 047d1a0d19..59bd58c9c9 100644 --- a/webnotes/utils/__init__.py +++ b/webnotes/utils/__init__.py @@ -182,9 +182,15 @@ def add_years(date, years): def date_diff(string_ed_date, string_st_date): return (getdate(string_ed_date) - getdate(string_st_date)).days + +def time_diff(string_ed_date, string_st_date): + return get_datetime(string_ed_date) - get_datetime(string_st_date) def time_diff_in_seconds(string_ed_date, string_st_date): - return (get_datetime(string_ed_date) - get_datetime(string_st_date)).seconds + return time_diff(string_ed_date, string_st_date).seconds + +def time_diff_in_hours(string_ed_date, string_st_date): + return round(float(time_diff(string_ed_date, string_st_date).seconds) / 3600, 6) def now_datetime(): from datetime import datetime diff --git a/webnotes/widgets/reportview.py b/webnotes/widgets/reportview.py index b656f2adb0..059d68fc71 100644 --- a/webnotes/widgets/reportview.py +++ b/webnotes/widgets/reportview.py @@ -255,7 +255,8 @@ def save_report(): d = Document('Report') d.report_name = data['name'] d.ref_doctype = data['doctype'] - + + d.report_type = "Report Builder" d.json = data['json'] webnotes.bean([d]).save() webnotes.msgprint("%s saved." % d.name) From e31115516d1bb27d7ced6e9a21f412e81ef5b7d4 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 1 Mar 2013 19:09:05 +0530 Subject: [PATCH 7/9] merge, update bean validations --- webnotes/model/bean.py | 61 +++++++++++++++++++++++++++-------------- webnotes/model/utils.py | 2 +- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/webnotes/model/bean.py b/webnotes/model/bean.py index 975e936b01..e2b537ca25 100644 --- a/webnotes/model/bean.py +++ b/webnotes/model/bean.py @@ -33,6 +33,8 @@ from webnotes import _ from webnotes.utils import cint from webnotes.model.doc import Document +class DocstatusTransitionError(webnotes.ValidationError): pass + class Bean: """ Collection of Documents with one parent and multiple children @@ -40,7 +42,6 @@ class Bean: def __init__(self, dt=None, dn=None): self.docs = [] self.obj = None - self.to_docstatus = 0 self.ignore_permissions = 0 if isinstance(dt, basestring) and not dn: dn = dt @@ -115,7 +116,7 @@ class Bean: """ return [d.fields for d in self.docs] - def check_if_latest(self): + def check_if_latest(self, method): """ Raises exception if the modified time is not the same as in the database """ @@ -123,17 +124,37 @@ class Bean: if (not is_single(self.doc.doctype)) and (not cint(self.doc.fields.get('__islocal'))): tmp = webnotes.conn.sql(""" - SELECT modified FROM `tab%s` WHERE name="%s" for update""" - % (self.doc.doctype, self.doc.name)) + SELECT modified, docstatus FROM `tab%s` WHERE name="%s" for update""" + % (self.doc.doctype, self.doc.name), as_dict=True) if not tmp: webnotes.msgprint("""This record does not exist. Please refresh.""", raise_exception=1) - if tmp and str(tmp[0][0]) != str(self.doc.modified): + if tmp and str(tmp[0].modified) != str(self.doc.modified): webnotes.msgprint(""" Document has been modified after you have opened it. To maintain the integrity of the data, you will not be able to save your changes. - Please refresh this document. [%s/%s]""" % (tmp[0][0], self.doc.modified), raise_exception=1) + Please refresh this document. [%s/%s]""" % (tmp[0].modified, self.doc.modified), raise_exception=1) + + self.check_docstatus_transition(tmp[0].docstatus, method) + + def check_docstatus_transition(self, db_docstatus, method): + valid = { + "save": [0,0], + "submit": [0,1], + "cancel": [1,2], + "update_after_submit": [1,1] + } + + labels = { + 0: _("Draft"), + 1: _("Submitted"), + 2: _("Cancelled") + } + + if [db_docstatus, self.to_docstatus] != valid[method]: + webnotes.msgprint(_("Cannot change from") + ": " + labels[db_docstatus] + " > " + \ + labels[self.to_docstatus], raise_exception=DocstatusTransitionError) def check_links(self): ref, err_list = {}, [] @@ -162,13 +183,13 @@ class Bean: if d.docstatus != 2 and self.to_docstatus >= d.docstatus: # don't update deleted d.docstatus = self.to_docstatus - def prepare_for_save(self, check_links): - self.check_if_latest() - if check_links: + def prepare_for_save(self, method): + self.check_if_latest(method) + if method != "cancel": self.check_links() self.update_timestamps_and_docstatus() self.update_parent_info() - + def update_parent_info(self): idx_map = {} is_local = cint(self.doc.fields.get("__islocal")) @@ -247,10 +268,8 @@ class Bean: def save(self, check_links=1): if self.ignore_permissions or webnotes.has_permission(self.doc.doctype, "write", self.doc): - if self.doc.docstatus == 1: - self.no_permission_to("Save submitted document") - - self.prepare_for_save(check_links) + self.to_docstatus = 0 + self.prepare_for_save("save") self.run_method('validate') self.save_main() self.save_children() @@ -262,10 +281,12 @@ class Bean: def submit(self): if self.ignore_permissions or webnotes.has_permission(self.doc.doctype, "submit", self.doc): - if self.doc.docstatus != 0: - webnotes.msgprint("Only draft can be submitted", raise_exception=1) self.to_docstatus = 1 - self.save() + self.prepare_for_save("submit") + self.run_method('validate') + self.save_main() + self.save_children() + self.run_method('on_update') self.run_method('on_submit') else: self.no_permission_to(_("Submit")) @@ -274,10 +295,8 @@ class Bean: def cancel(self): if self.ignore_permissions or webnotes.has_permission(self.doc.doctype, "cancel", self.doc): - if self.doc.docstatus != 1: - webnotes.msgprint("Only submitted can be cancelled", raise_exception=1) self.to_docstatus = 2 - self.prepare_for_save(1) + self.prepare_for_save("cancel") self.run_method('before_cancel') self.save_main() self.save_children() @@ -292,7 +311,7 @@ class Bean: webnotes.msgprint("Only to called after submit", raise_exception=1) if self.ignore_permissions or webnotes.has_permission(self.doc.doctype, "write", self.doc): self.to_docstatus = 1 - self.prepare_for_save(1) + self.prepare_for_save("update_after_submit") self.run_method('before_update_after_submit') self.save_main() self.save_children() diff --git a/webnotes/model/utils.py b/webnotes/model/utils.py index cd3d8f0830..78ed86892e 100644 --- a/webnotes/model/utils.py +++ b/webnotes/model/utils.py @@ -205,7 +205,7 @@ def delete_doc(doctype=None, name=None, doclist = None, force=0): return 'okay' -def check_if_doc_is_linked(dt, dn): +def check_if_doc_is_linked(dt, dn, method="Delete"): """ Raises excption if the given doc(dt, dn) is linked in another record. """ From 7b5eedf5c298a1bb5f7b45c3844c2103818dd5fa Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 4 Mar 2013 11:26:49 +0530 Subject: [PATCH 8/9] fix in query report filter --- public/js/wn/views/query_report.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/public/js/wn/views/query_report.js b/public/js/wn/views/query_report.js index 97a8cae8dc..b94c18437f 100644 --- a/public/js/wn/views/query_report.js +++ b/public/js/wn/views/query_report.js @@ -209,6 +209,11 @@ wn.views.QueryReport = Class.extend({ if(df.width) { col.width=parseInt(df.width); } + } else { + col.df = { + label: c, + fieldtype: "Data" + } } col.name = toTitle(col.name.replace(/ /g, " ")) return col @@ -277,8 +282,7 @@ wn.views.QueryReport = Class.extend({ } else if(filter[0]=="<") { filter = filter.substr(1); cond = "<" - } - + } if(in_list(['Float', 'Currency', 'Int', 'Date'], columnDef.df.fieldtype)) { // non strings @@ -291,7 +295,7 @@ wn.views.QueryReport = Class.extend({ value = flt(value); filter = flt(filter); } - + out = eval("value" + cond + "filter"); } else { // range From e89f88eb9045b7d56de330a4b11e2c7aa3a52d6e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 4 Mar 2013 13:24:13 +0530 Subject: [PATCH 9/9] default method is save in check_if_letest function --- webnotes/model/bean.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webnotes/model/bean.py b/webnotes/model/bean.py index e2b537ca25..1c0e983953 100644 --- a/webnotes/model/bean.py +++ b/webnotes/model/bean.py @@ -116,7 +116,7 @@ class Bean: """ return [d.fields for d in self.docs] - def check_if_latest(self, method): + def check_if_latest(self, method="save"): """ Raises exception if the modified time is not the same as in the database """