From 33d1da9d431a30ba9ac37c7c0d5b67e5f0d6af14 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 18 Jun 2014 12:48:25 +0530 Subject: [PATCH 01/13] dont_update_if_missing property, lets you skip fields for which you don't want missing values to be updated --- frappe/model/base_document.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 667fe88df6..ef4e420798 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -13,6 +13,7 @@ class BaseDocument(object): def __init__(self, d): self.update(d) + self.dont_update_if_missing = [] @property def meta(self): @@ -42,7 +43,8 @@ class BaseDocument(object): if "doctype" in d: self.set("doctype", d.get("doctype")) for key, value in d.iteritems(): - if self.get(key) is None: + # dont_update_if_missing is a list of fieldnames, for which, you don't want to set default value + if (self.get(key) is None) and (value is not None) and (key not in self.dont_update_if_missing): self.set(key, value) def get_db_value(self, key): From eb9c558cf0f08986fb3261b2f47cb0ef35acd9d9 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 18 Jun 2014 15:26:40 +0530 Subject: [PATCH 02/13] hotfix: dont_update_if_missing --- frappe/model/document.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/model/document.py b/frappe/model/document.py index 537b200fcb..fa7add577d 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -48,6 +48,7 @@ def get_controller(doctype): class Document(BaseDocument): def __init__(self, arg1, arg2=None): self.doctype = self.name = None + if arg1 and isinstance(arg1, basestring): if not arg2: # single @@ -72,6 +73,8 @@ class Document(BaseDocument): # incorrect arguments. let's not proceed. raise frappe.DataError("Document({0}, {1})".format(arg1, arg2)) + self.dont_update_if_missing = [] + def load_from_db(self): if not getattr(self, "_metaclass", False) and self.meta.issingle: self.update(frappe.db.get_singles_dict(self.doctype)) From 91f8338849ea80dd99fceea18b8fe6b0bd112ab0 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 18 Jun 2014 16:49:46 +0530 Subject: [PATCH 03/13] Fixes frappe/erpnext#1811 --- frappe/templates/includes/navbar.html | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/frappe/templates/includes/navbar.html b/frappe/templates/includes/navbar.html index a49af7a409..b8097711bf 100644 --- a/frappe/templates/includes/navbar.html +++ b/frappe/templates/includes/navbar.html @@ -55,9 +55,8 @@ {%- endfor -%} - {% if not disable_signup %} - - {% endif %} + From 587da3d06099c912e71a87f4f027117e5814b282 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 18 Jun 2014 17:33:23 +0530 Subject: [PATCH 04/13] Update session into database every 10 mins --- frappe/sessions.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/frappe/sessions.py b/frappe/sessions.py index faa57b02e6..b6a8861591 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -250,28 +250,27 @@ class Session: def update(self, force=False): """extend session expiry""" - self.data['data']['last_updated'] = frappe.utils.now() - self.data['data']['lang'] = unicode(frappe.lang) + now = frappe.utils.now() + self.data['data']['last_updated'] = now + self.data['data']['lang'] = unicode(frappe.lang) # update session in db time_diff = None last_updated = frappe.cache().get_value("last_db_session_update:" + self.sid) if last_updated: - time_diff = frappe.utils.time_diff_in_seconds(frappe.utils.now(), - last_updated) + time_diff = frappe.utils.time_diff_in_seconds(now, last_updated) if force or (frappe.session['user'] != 'Guest' and \ - ((time_diff==None) or (time_diff > 1800))): + ((time_diff==None) or (time_diff > 600))): # database persistence is secondary, don't update it too often frappe.db.sql("""update tabSessions set sessiondata=%s, lastupdate=NOW() where sid=%s""" , (str(self.data['data']), self.data['sid'])) if frappe.form_dict.cmd not in ("frappe.sessions.clear", "logout"): - frappe.cache().set_value("last_db_session_update:" + self.sid, - frappe.utils.now()) + frappe.cache().set_value("last_db_session_update:" + self.sid, now) frappe.cache().set_value("session:" + self.sid, self.data) def get_expiry_period(): From 65ae2fbd29dc99421958d6d69c1a81d59a71195d Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 18 Jun 2014 18:02:02 +0530 Subject: [PATCH 05/13] Remove script tags, once evaled --- frappe/public/js/legacy/print_format.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/legacy/print_format.js b/frappe/public/js/legacy/print_format.js index bf1a486999..82a29f02f6 100644 --- a/frappe/public/js/legacy/print_format.js +++ b/frappe/public/js/legacy/print_format.js @@ -285,8 +285,9 @@ $.extend(_p, { // This is used to calculate and substitude values in the HTML run_embedded_js: function(container, doc) { - script_list = $(container).find("script"); - for(var i=0; i Date: Wed, 18 Jun 2014 20:53:40 +0530 Subject: [PATCH 06/13] Validate Selects and Fixed Number Formats --- .../system_settings/system_settings.json | 144 +++++++++--------- .../page/data_import_tool/data_import_tool.js | 3 +- frappe/core/page/data_import_tool/importer.py | 2 +- frappe/model/base_document.py | 26 ++++ frappe/model/document.py | 2 + frappe/model/meta.py | 3 + frappe/public/js/frappe/misc/number_format.js | 4 +- frappe/utils/__init__.py | 9 +- 8 files changed, 115 insertions(+), 78 deletions(-) diff --git a/frappe/core/doctype/system_settings/system_settings.json b/frappe/core/doctype/system_settings/system_settings.json index 50cffa5454..dbc9eaead8 100644 --- a/frappe/core/doctype/system_settings/system_settings.json +++ b/frappe/core/doctype/system_settings/system_settings.json @@ -1,100 +1,100 @@ { - "creation": "2014-04-17 16:53:52.640856", - "docstatus": 0, - "doctype": "DocType", - "document_type": "System", + "creation": "2014-04-17 16:53:52.640856", + "docstatus": 0, + "doctype": "DocType", + "document_type": "System", "fields": [ { - "fieldname": "localization", - "fieldtype": "Section Break", - "label": "Localization", + "fieldname": "localization", + "fieldtype": "Section Break", + "label": "Localization", "permlevel": 0 - }, + }, { - "fieldname": "language", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Language", - "options": "Loading...", - "permlevel": 0, - "reqd": 1, + "fieldname": "language", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Language", + "options": "Loading...", + "permlevel": 0, + "reqd": 1, "search_index": 0 - }, + }, { - "fieldname": "time_zone", - "fieldtype": "Select", - "label": "Time Zone", - "permlevel": 0, + "fieldname": "time_zone", + "fieldtype": "Select", + "label": "Time Zone", + "permlevel": 0, "reqd": 1 - }, + }, { - "fieldname": "date_and_number_format", - "fieldtype": "Section Break", - "label": "Date and Number Format", + "fieldname": "date_and_number_format", + "fieldtype": "Section Break", + "label": "Date and Number Format", "permlevel": 0 - }, + }, { - "fieldname": "date_format", - "fieldtype": "Select", - "label": "Date Format", - "options": "yyyy-mm-dd\ndd-mm-yyyy\ndd/mm/yyyy\ndd.mm.yyyy\nmm/dd/yyyy\nmm-dd-yyyy", - "permlevel": 0, + "fieldname": "date_format", + "fieldtype": "Select", + "label": "Date Format", + "options": "yyyy-mm-dd\ndd-mm-yyyy\ndd/mm/yyyy\ndd.mm.yyyy\nmm/dd/yyyy\nmm-dd-yyyy", + "permlevel": 0, "reqd": 1 - }, + }, { - "fieldname": "number_format", - "fieldtype": "Select", - "label": "Number Format", - "options": "#,###.##\n#.###,##\n# ###.##\n# ###,##\n#,###.###\n#,##,###.##\n#.###\n#,###", - "permlevel": 0, + "fieldname": "number_format", + "fieldtype": "Select", + "label": "Number Format", + "options": "#,###.##\n#.###,##\n# ###.##\n# ###,##\n#'###.##\n#, ###.##\n#,##,###.##\n#,###.###\n#.###\n#,###", + "permlevel": 0, "reqd": 1 - }, + }, { - "fieldname": "float_precision", - "fieldtype": "Select", - "label": "Float Precision", - "options": "\n2\n3\n4\n5\n6", + "fieldname": "float_precision", + "fieldtype": "Select", + "label": "Float Precision", + "options": "\n2\n3\n4\n5\n6", "permlevel": 0 - }, + }, { - "fieldname": "security", - "fieldtype": "Section Break", - "label": "Security", + "fieldname": "security", + "fieldtype": "Section Break", + "label": "Security", "permlevel": 0 - }, + }, { - "default": "06:00", - "description": "Session Expiry in Hours e.g. 06:00", - "fieldname": "session_expiry", - "fieldtype": "Data", - "label": "Session Expiry", - "options": "", + "default": "06:00", + "description": "Session Expiry in Hours e.g. 06:00", + "fieldname": "session_expiry", + "fieldtype": "Data", + "label": "Session Expiry", + "options": "", "permlevel": 0 - }, + }, { - "description": "Run scheduled jobs only if checked", - "fieldname": "enable_scheduler", - "fieldtype": "Check", - "in_list_view": 0, - "label": "Enable Scheduled Jobs", + "description": "Run scheduled jobs only if checked", + "fieldname": "enable_scheduler", + "fieldtype": "Check", + "in_list_view": 0, + "label": "Enable Scheduled Jobs", "permlevel": 0 } - ], - "icon": "icon-cog", - "issingle": 1, - "modified": "2014-06-02 02:09:03.623094", - "modified_by": "Administrator", - "module": "Core", - "name": "System Settings", - "name_case": "", - "owner": "Administrator", + ], + "icon": "icon-cog", + "issingle": 1, + "modified": "2014-06-18 02:09:03.623094", + "modified_by": "Administrator", + "module": "Core", + "name": "System Settings", + "name_case": "", + "owner": "Administrator", "permissions": [ { - "create": 1, - "permlevel": 0, - "read": 1, - "role": "System Manager", + "create": 1, + "permlevel": 0, + "read": 1, + "role": "System Manager", "write": 1 } ] -} \ No newline at end of file +} diff --git a/frappe/core/page/data_import_tool/data_import_tool.js b/frappe/core/page/data_import_tool/data_import_tool.js index 6dd953ad17..64196121e7 100644 --- a/frappe/core/page/data_import_tool/data_import_tool.js +++ b/frappe/core/page/data_import_tool/data_import_tool.js @@ -161,7 +161,8 @@ frappe.pages['data-import-tool'].onload = function(wrapper) { $("#dit-output").empty(); $.each(r.messages, function(i, v) { - var $p = $('

').html(v).appendTo('#dit-output'); + var $p = $('

').html(frappe.markdown(v)).appendTo('#dit-output'); + $("
").appendTo('#dit-output'); if(v.substr(0,5)=='Error') { $p.css('color', 'red'); } else if(v.substr(0,8)=='Inserted') { diff --git a/frappe/core/page/data_import_tool/importer.py b/frappe/core/page/data_import_tool/importer.py index 2e8e0609e0..d481407e1c 100644 --- a/frappe/core/page/data_import_tool/importer.py +++ b/frappe/core/page/data_import_tool/importer.py @@ -217,7 +217,7 @@ def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False, error = True if doc: frappe.errprint(doc if isinstance(doc, dict) else doc.as_dict()) - err_msg = frappe.local.message_log and "
".join(frappe.local.message_log) or cstr(e) + err_msg = frappe.local.message_log and "\n\n".join(frappe.local.message_log) or cstr(e) ret.append('Error for row (#%d) %s : %s' % (row_idx + 1, len(row)>1 and row[1] or "", err_msg)) frappe.errprint(frappe.get_traceback()) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index ef4e420798..39b9ad7a96 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -277,6 +277,32 @@ class BaseDocument(object): return invalid_links + def _validate_selects(self): + if frappe.flags.in_import: + return + + for df in self.meta.get_select_fields(): + if not (self.get(df.fieldname) and df.options): + continue + + options = (df.options or "").split("\n") + + # if only empty options + if not filter(None, options): + continue + + # strip and set + self.set(df.fieldname, self.get(df.fieldname).strip()) + + if self.get(df.fieldname) not in options: + # show an elaborate message + prefix = _("Row #{0}:").format(self.idx) if self.get("parentfield") else "" + label = _(self.meta.get_label(df.fieldname)) + comma_options = '", "'.join(_(each) for each in options) + + frappe.throw(_('{0} {1} cannot be "{2}". It should be one of "{3}"').format(prefix, label, + self.get(df.fieldname), comma_options)) + def _validate_constants(self): if frappe.flags.in_import: return diff --git a/frappe/model/document.py b/frappe/model/document.py index fa7add577d..1ccc9d2aa9 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -228,8 +228,10 @@ class Document(BaseDocument): def _validate(self): self._validate_mandatory() self._validate_links() + self._validate_selects() self._validate_constants() for d in self.get_all_children(): + d._validate_selects() d._validate_constants() self._extract_images_from_text_editor() diff --git a/frappe/model/meta.py b/frappe/model/meta.py index bc69eb72e0..4531c85545 100644 --- a/frappe/model/meta.py +++ b/frappe/model/meta.py @@ -61,6 +61,9 @@ class Meta(Document): def get_link_fields(self): return self.get("fields", {"fieldtype": "Link", "options":["!=", "[Select]"]}) + def get_select_fields(self): + return self.get("fields", {"fieldtype": "Select", "options":["not in", ["[Select]", "Loading..."]]}) + def get_table_fields(self): if not hasattr(self, "_table_fields"): if self.name!="DocType": diff --git a/frappe/public/js/frappe/misc/number_format.js b/frappe/public/js/frappe/misc/number_format.js index 9f85a8d68a..e26c96cffc 100644 --- a/frappe/public/js/frappe/misc/number_format.js +++ b/frappe/public/js/frappe/misc/number_format.js @@ -60,8 +60,10 @@ frappe.number_format_info = { "#.###,##": {decimal_str:",", group_sep:".", precision:2}, "# ###.##": {decimal_str:".", group_sep:" ", precision:2}, "# ###,##": {decimal_str:",", group_sep:" ", precision:2}, - "#,###.###": {decimal_str:".", group_sep:",", precision:3}, + "#'###.##": {decimal_str:".", group_sep:"'", precision:2}, + "#, ###.##": {decimal_str:".", group_sep:", ", precision:2}, "#,##,###.##": {decimal_str:".", group_sep:",", precision:2}, + "#,###.###": {decimal_str:".", group_sep:",", precision:3}, "#.###": {decimal_str:"", group_sep:".", precision:0}, "#,###": {decimal_str:"", group_sep:",", precision:0}, } diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index 715ede6a5b..5826ae36fb 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -393,13 +393,16 @@ def fmt_money(amount, precision=None, currency=None): return amount number_format_info = { - "#.###": ("", ".", 0), - "#,###": ("", ",", 0), "#,###.##": (".", ",", 2), - "#,##,###.##": (".", ",", 2), "#.###,##": (",", ".", 2), "# ###.##": (".", " ", 2), + "# ###,##": (",", " ", 2), + "#'###.##": (".", "'", 2), + "#, ###.##": (".", ", ", 2), + "#,##,###.##": (".", ",", 2), "#,###.###": (".", ",", 3), + "#.###": ("", ".", 0), + "#,###": ("", ",", 0) } def get_number_format_info(format): From ec659131825ce169c0d623338a8dc4c6fcf20e1b Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 19 Jun 2014 11:30:45 +0530 Subject: [PATCH 07/13] fixes for tags in form and report --- frappe/model/base_document.py | 3 +++ frappe/public/js/frappe/desk.js | 8 ------ frappe/public/js/frappe/ui/filters.js | 2 +- frappe/public/js/frappe/ui/toolbar/toolbar.js | 2 +- frappe/public/js/frappe/views/doclistview.js | 2 +- frappe/public/js/frappe/views/reportview.js | 2 ++ .../public/js/frappe/views/sidebar_stats.js | 27 ++++++++++--------- 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index e723579bce..2f39e45beb 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -160,6 +160,9 @@ class BaseDocument(object): if doc[k] is None: del doc[k] + if self.get("_user_tags"): + doc["_user_tags"] = self.get("_user_tags") + if self.get("__islocal"): doc["__islocal"] = 1 diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index 5198032471..b3e53eefe9 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -1,14 +1,6 @@ // Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors // MIT License. See license.txt -if(!console) { - var console = { - log: function(txt) { - // suppress - } - } -} - $(document).ready(function() { frappe.assets.check(); frappe.provide('frappe.app'); diff --git a/frappe/public/js/frappe/ui/filters.js b/frappe/public/js/frappe/ui/filters.js index 1da081c540..0e1b90752e 100644 --- a/frappe/public/js/frappe/ui/filters.js +++ b/frappe/public/js/frappe/ui/filters.js @@ -255,7 +255,7 @@ frappe.ui.Filter = Class.extend({ if(df.fieldtype=='Check') { df.fieldtype='Select'; df.options='No\nYes'; - } else if(['Text','Small Text','Text Editor','Code','Tags','Comments'].indexOf(df.fieldtype)!=-1) { + } else if(['Text','Small Text','Text Editor','Code','Tag','Comments'].indexOf(df.fieldtype)!=-1) { df.fieldtype = 'Data'; } else if(df.fieldtype=='Link' && this.$w.find('.condition').val()!="=") { df.fieldtype = 'Data'; diff --git a/frappe/public/js/frappe/ui/toolbar/toolbar.js b/frappe/public/js/frappe/ui/toolbar/toolbar.js index 77eaaa8f6f..720307f16d 100644 --- a/frappe/public/js/frappe/ui/toolbar/toolbar.js +++ b/frappe/public/js/frappe/ui/toolbar/toolbar.js @@ -45,7 +45,7 @@ frappe.ui.toolbar.Toolbar = Class.extend({ placeholder="' + __("Search or type a command") + '" \ style="padding: 2px 6px; height: 24px; margin-top: 5px; \ margin-left: 10px; background-color: #ddd; \ - min-width: 230px; \ + min-width: 230px; font-size: 85%;\ border-radius: 10px;">\ \ \ diff --git a/frappe/public/js/frappe/views/doclistview.js b/frappe/public/js/frappe/views/doclistview.js index fc952d789b..fd86d48585 100644 --- a/frappe/public/js/frappe/views/doclistview.js +++ b/frappe/public/js/frappe/views/doclistview.js @@ -241,7 +241,7 @@ frappe.views.DocListView = frappe.ui.Listing.extend({ init_minbar: function() { var me = this; this.appframe.add_icon_btn("2", 'icon-tag', __('Show Tags'), function() { me.toggle_tags(); }); - this.wrapper.on("click", ".list-tag-preview", function() { me.toggle_tags(); }); + this.$page.on("click", ".list-tag-preview", function() { me.toggle_tags(); }); if(this.can_delete || this.listview.settings.selectable) { this.appframe.add_icon_btn("2", 'icon-remove', __('Delete'), function() { me.delete_items(); }); this.appframe.add_icon_btn("2", 'icon-ok', __('Select All'), function() { diff --git a/frappe/public/js/frappe/views/reportview.js b/frappe/public/js/frappe/views/reportview.js index b4bbad8b75..3a8da65884 100644 --- a/frappe/public/js/frappe/views/reportview.js +++ b/frappe/public/js/frappe/views/reportview.js @@ -397,12 +397,14 @@ frappe.views.ReportView = frappe.ui.Listing.extend({ this.$w.find('.result-list').on("click", ".label-info", function() { if($(this).attr("data-label")) { me.set_filter("_user_tags", $(this).attr("data-label")); + me.refresh(); } }); this.$w.find('.result-list').on("click", "[data-workflow-state]", function() { if($(this).attr("data-workflow-state")) { me.set_filter(me.state_fieldname, $(this).attr("data-workflow-state")); + me.refresh(); } }); }, diff --git a/frappe/public/js/frappe/views/sidebar_stats.js b/frappe/public/js/frappe/views/sidebar_stats.js index eaf32ca54a..e704658422 100644 --- a/frappe/public/js/frappe/views/sidebar_stats.js +++ b/frappe/public/js/frappe/views/sidebar_stats.js @@ -3,7 +3,7 @@ frappe.provide('frappe.views'); -// opts: +// opts: // stats = list of fields // doctype // parent @@ -29,7 +29,7 @@ frappe.views.SidebarStats = Class.extend({ $.each(me.stats, function(i, v) { me.render_stat(v, (r.message || {})[v]); }); - + // reload button at the end if(me.stats.length) { $(' '+__('Refresh')+'') @@ -46,11 +46,14 @@ frappe.views.SidebarStats = Class.extend({ }, render_stat: function(field, stat) { var me = this; + var show_tags = '' + +__("Show tags") +''; if(!stat || !stat.length) { if(field==='_user_tags') { $('
\ -
'+__('Tags')+'
\ +
\ + '+__('Tags')+show_tags+'
\
\
'+__('No records tagged.')+'
' +'
\ @@ -59,9 +62,9 @@ frappe.views.SidebarStats = Class.extend({ return; } - var label = frappe.meta.docfield_map[this.doctype][field] ? + var label = frappe.meta.docfield_map[this.doctype][field] ? frappe.meta.docfield_map[this.doctype][field].label : field; - if(label==='_user_tags') label = 'Tags'; + if(label==='_user_tags') label = 'Tags' + show_tags; // grid var $w = $('
\ @@ -76,12 +79,12 @@ frappe.views.SidebarStats = Class.extend({ $.each(stat, function(i,v) { sum = sum + v[1]; }) // render items - $.each(stat, function(i, v) { + $.each(stat, function(i, v) { me.render_stat_item(i, v, sum, field).appendTo($w.find('.side-panel-body')); }); $w.appendTo(this.wrapper); - }, + }, render_stat_item: function(i, v, max, field) { var me = this; var args = {} @@ -91,8 +94,8 @@ frappe.views.SidebarStats = Class.extend({ args.count = v[1]; args.field = field; args.bar_style = ""; - - $item = $(repl('
\ + + $item = $(repl('
\
\
\
\ %(_label)s (%(count)s)\
', args)); - + this.setup_stat_item_click($item); return $item; }, @@ -116,5 +119,5 @@ frappe.views.SidebarStats = Class.extend({ me.set_filter(fieldname, label); return false; }); - }, -}); \ No newline at end of file + }, +}); From 8885b6b4dc34ee573ba4df8613eda1e052681033 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 19 Jun 2014 14:31:10 +0530 Subject: [PATCH 08/13] vietnam --- frappe/country_info.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/country_info.json b/frappe/country_info.json index 1b90e4d5f4..2929532ea0 100644 --- a/frappe/country_info.json +++ b/frappe/country_info.json @@ -2556,7 +2556,7 @@ "code": "ve", "number_format": "#,###.##" }, - "Viet Nam": { + "Vietnam": { "code": "vn", "currency": "VND", "currency_name": "Dong", From 6083999f8a998fad314929d5298187eed9a4200f Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 19 Jun 2014 15:16:56 +0530 Subject: [PATCH 09/13] added standard reply fixes frappe/erpnext#1819 --- frappe/config/setup.py | 5 ++ frappe/core/doctype/doctype/doctype.py | 2 +- .../core/doctype/standard_reply/__init__.py | 0 .../standard_reply/standard_reply.json | 71 +++++++++++++++++++ .../doctype/standard_reply/standard_reply.py | 9 +++ .../public/js/frappe/views/communication.js | 33 +++++++++ 6 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 frappe/core/doctype/standard_reply/__init__.py create mode 100644 frappe/core/doctype/standard_reply/standard_reply.json create mode 100644 frappe/core/doctype/standard_reply/standard_reply.py diff --git a/frappe/config/setup.py b/frappe/config/setup.py index 318a0aa8d7..ce0d4e08ab 100644 --- a/frappe/config/setup.py +++ b/frappe/config/setup.py @@ -121,6 +121,11 @@ def get_data(): "name": "Outgoing Email Settings", "description": _("Set outgoing mail server.") }, + { + "type": "doctype", + "name": "Standard Reply", + "description": _("Standard replies to common queries.") + }, ] }, { diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index eace9366f3..963c7dbb93 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -41,7 +41,7 @@ class DocType(Document): frappe.db.sql('UPDATE tabDocType SET modified=%s WHERE `name`=%s', (now(), p[0])) def scrub_field_names(self): - restricted = ('name','parent','idx','owner','creation','modified','modified_by', + restricted = ('name','parent','creation','modified','modified_by', 'parentfield','parenttype',"file_list") for d in self.get("fields"): if d.fieldtype: diff --git a/frappe/core/doctype/standard_reply/__init__.py b/frappe/core/doctype/standard_reply/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/core/doctype/standard_reply/standard_reply.json b/frappe/core/doctype/standard_reply/standard_reply.json new file mode 100644 index 0000000000..bbb7c45546 --- /dev/null +++ b/frappe/core/doctype/standard_reply/standard_reply.json @@ -0,0 +1,71 @@ +{ + "allow_import": 1, + "autoname": "field:subject", + "creation": "2014-06-19 05:20:26.331041", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Transaction", + "fields": [ + { + "fieldname": "subject", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Subject", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "response", + "fieldtype": "Text Editor", + "in_list_view": 1, + "label": "Response", + "permlevel": 0, + "reqd": 1 + }, + { + "default": "user", + "fieldname": "owner", + "fieldtype": "Link", + "hidden": 1, + "label": "Owner", + "options": "User", + "permlevel": 0 + } + ], + "icon": "icon-comment", + "modified": "2014-06-19 05:45:09.855045", + "modified_by": "Administrator", + "module": "Core", + "name": "Standard Reply", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "permlevel": 0, + "read": 1, + "role": "All" + }, + { + "apply_user_permissions": 1, + "create": 1, + "permlevel": 0, + "read": 1, + "role": "All", + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "permlevel": 0, + "read": 1, + "report": 1, + "role": "System Manager", + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/frappe/core/doctype/standard_reply/standard_reply.py b/frappe/core/doctype/standard_reply/standard_reply.py new file mode 100644 index 0000000000..54937b3a58 --- /dev/null +++ b/frappe/core/doctype/standard_reply/standard_reply.py @@ -0,0 +1,9 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class StandardReply(Document): + pass \ No newline at end of file diff --git a/frappe/public/js/frappe/views/communication.js b/frappe/public/js/frappe/views/communication.js index 388409a48b..990e539aff 100644 --- a/frappe/public/js/frappe/views/communication.js +++ b/frappe/public/js/frappe/views/communication.js @@ -113,6 +113,7 @@ frappe.views.CommunicationList = Class.extend({ }); frappe.last_edited_communication = {}; +frappe.standard_replies = {}; frappe.views.CommunicationComposer = Class.extend({ init: function(opts) { @@ -129,6 +130,8 @@ frappe.views.CommunicationComposer = Class.extend({ description:__("Email addresses, separted by commas")}, {label:__("Subject"), fieldtype:"Data", reqd: 1, fieldname:"subject"}, + {label:__("Standard Reply"), fieldtype:"Link", options:"Standard Reply", + fieldname:"standard_reply"}, {label:__("Message"), fieldtype:"Text Editor", reqd: 1, fieldname:"content"}, {label:__("Send As Email"), fieldtype:"Check", @@ -155,6 +158,7 @@ frappe.views.CommunicationComposer = Class.extend({ this.dialog.$wrapper.find("[data-edit='outdent']").remove(); this.dialog.get_input("send").addClass("btn-primary"); + $(document).on("upload_complete", function(event, attachment) { if(me.dialog.display) { var wrapper = $(me.dialog.fields_dict.select_attachments.wrapper); @@ -185,10 +189,39 @@ frappe.views.CommunicationComposer = Class.extend({ this.setup_email(); this.setup_autosuggest(); this.setup_last_edited_communication(); + this.setup_standard_reply(); $(this.dialog.fields_dict.recipients.input).val(this.recipients || "").change(); $(this.dialog.fields_dict.subject.input).val(this.subject || "").change(); this.setup_earlier_reply(); }, + + setup_standard_reply: function() { + var me = this; + this.dialog.get_input("standard_reply").on("change", function() { + var standard_reply = $(this).val(); + var prepend_reply = function() { + var content_field = me.dialog.fields_dict.content; + var content = content_field.get_value() || ""; + content_field.set_input( + frappe.standard_replies[standard_reply] + + "

" + content); + } + if(frappe.standard_replies[standard_reply]) { + prepend_reply(); + } else { + $.ajax({ + url:"/api/resource/Standard Reply/" + standard_reply, + statusCode: { + 200: function(data) { + frappe.standard_replies[standard_reply] = data.data.response; + prepend_reply(); + } + } + }); + } + }); + }, + setup_last_edited_communication: function() { var me = this; this.dialog.onhide = function() { From 53f04414cf938986afdee65c598d57eb457370bc Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 19 Jun 2014 15:55:47 +0530 Subject: [PATCH 10/13] Validate Select field values Conflicts: frappe/model/base_document.py frappe/model/meta.py --- frappe/model/base_document.py | 7 ++++--- frappe/model/meta.py | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 39b9ad7a96..b2dbd6332b 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -292,16 +292,17 @@ class BaseDocument(object): continue # strip and set - self.set(df.fieldname, self.get(df.fieldname).strip()) + self.set(df.fieldname, cstr(self.get(df.fieldname)).strip()) + value = self.get(df.fieldname) - if self.get(df.fieldname) not in options: + if value not in options and not (frappe.flags.in_test and value.startswith("_T-")): # show an elaborate message prefix = _("Row #{0}:").format(self.idx) if self.get("parentfield") else "" label = _(self.meta.get_label(df.fieldname)) comma_options = '", "'.join(_(each) for each in options) frappe.throw(_('{0} {1} cannot be "{2}". It should be one of "{3}"').format(prefix, label, - self.get(df.fieldname), comma_options)) + value, comma_options)) def _validate_constants(self): if frappe.flags.in_import: diff --git a/frappe/model/meta.py b/frappe/model/meta.py index 4531c85545..c0986a8952 100644 --- a/frappe/model/meta.py +++ b/frappe/model/meta.py @@ -62,7 +62,8 @@ class Meta(Document): return self.get("fields", {"fieldtype": "Link", "options":["!=", "[Select]"]}) def get_select_fields(self): - return self.get("fields", {"fieldtype": "Select", "options":["not in", ["[Select]", "Loading..."]]}) + return self.get("fields", {"fieldtype": "Select", "options":["not in", + ["[Select]", "Loading...", "attach_files:"]]}) def get_table_fields(self): if not hasattr(self, "_table_fields"): From 8b97787f4014ab8ae509a1ff722f884cf3e0d51f Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 19 Jun 2014 16:04:38 +0530 Subject: [PATCH 11/13] Remove user_owner Custom Field --- frappe/patches.txt | 1 + frappe/patches/v4_0/remove_user_owner_custom_field.py | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 frappe/patches/v4_0/remove_user_owner_custom_field.py diff --git a/frappe/patches.txt b/frappe/patches.txt index 940a9d0981..5387aafbc2 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -40,3 +40,4 @@ execute:import frappe.website.render; frappe.website.render.clear_cache("login") frappe.patches.v4_0.fix_attach_field_file_url execute:frappe.reset_perms("User") #2014-06-13 execute:frappe.db.sql("""delete from `tabUserRole` where ifnull(parentfield, '')=''""") #2014-06-17 +frappe.patches.v4_0.remove_user_owner_custom_field diff --git a/frappe/patches/v4_0/remove_user_owner_custom_field.py b/frappe/patches/v4_0/remove_user_owner_custom_field.py new file mode 100644 index 0000000000..4f3e478f98 --- /dev/null +++ b/frappe/patches/v4_0/remove_user_owner_custom_field.py @@ -0,0 +1,10 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + user_owner = frappe.db.get_value("Custom Field", {"fieldname": "user_owner"}) + if user_owner: + frappe.delete_doc("Custom Field", user_owner) From 412fab3e645717eeb7666b5e937315488591cc0c Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 19 Jun 2014 15:58:10 +0530 Subject: [PATCH 12/13] Fixed update sessions --- frappe/app.py | 6 ++++++ frappe/handler.py | 5 ----- frappe/sessions.py | 24 ++++++++++++++---------- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/frappe/app.py b/frappe/app.py index c356741423..8f836138c5 100644 --- a/frappe/app.py +++ b/frappe/app.py @@ -81,6 +81,12 @@ def application(request): frappe.db.commit() rollback = False + # update session + if getattr(frappe.local, "session_obj", None): + updated_in_db = frappe.local.session_obj.update() + if updated_in_db: + frappe.db.commit() + finally: if frappe.local.request.method in ("POST", "PUT") and frappe.db and rollback: frappe.db.rollback() diff --git a/frappe/handler.py b/frappe/handler.py index 2762e51b9d..d2c59483dd 100755 --- a/frappe/handler.py +++ b/frappe/handler.py @@ -87,11 +87,6 @@ def execute_cmd(cmd): if ret: frappe.response['message'] = ret - # update session - if "session_obj" in frappe.local: - frappe.local.session_obj.update() - - def get_attr(cmd): """get method object from cmd""" if '.' in cmd: diff --git a/frappe/sessions.py b/frappe/sessions.py index b6a8861591..312c525ed4 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -250,28 +250,32 @@ class Session: def update(self, force=False): """extend session expiry""" + if (frappe.session['user'] == "Guest" or frappe.form_dict.cmd=="logout"): + return + now = frappe.utils.now() self.data['data']['last_updated'] = now self.data['data']['lang'] = unicode(frappe.lang) # update session in db - time_diff = None last_updated = frappe.cache().get_value("last_db_session_update:" + self.sid) + time_diff = frappe.utils.time_diff_in_seconds(now, last_updated) if last_updated else None - if last_updated: - time_diff = frappe.utils.time_diff_in_seconds(now, last_updated) - - if force or (frappe.session['user'] != 'Guest' and \ - ((time_diff==None) or (time_diff > 600))): - # database persistence is secondary, don't update it too often + # database persistence is secondary, don't update it too often + updated_in_db = False + if force or (time_diff==None) or (time_diff > 600): frappe.db.sql("""update tabSessions set sessiondata=%s, lastupdate=NOW() where sid=%s""" , (str(self.data['data']), self.data['sid'])) - if frappe.form_dict.cmd not in ("frappe.sessions.clear", "logout"): - frappe.cache().set_value("last_db_session_update:" + self.sid, now) - frappe.cache().set_value("session:" + self.sid, self.data) + updated_in_db = True + + # set in memcache + frappe.cache().set_value("last_db_session_update:" + self.sid, now) + frappe.cache().set_value("session:" + self.sid, self.data) + + return updated_in_db def get_expiry_period(): exp_sec = frappe.defaults.get_global_default("session_expiry") or "06:00:00" From 833913e9e089341237185a45ff28fa948a627270 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 19 Jun 2014 16:30:21 +0530 Subject: [PATCH 13/13] added help in custom script --- .../core/doctype/custom_script/custom_script.json | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/custom_script/custom_script.json b/frappe/core/doctype/custom_script/custom_script.json index 95c302f1f3..8ad35d8c86 100644 --- a/frappe/core/doctype/custom_script/custom_script.json +++ b/frappe/core/doctype/custom_script/custom_script.json @@ -1,6 +1,6 @@ { "autoname": "CustomScript.####", - "creation": "2013-01-10 16:34:01.000000", + "creation": "2013-01-10 16:34:01", "description": "Adds a custom script (client or server) to a DocType", "docstatus": 0, "doctype": "DocType", @@ -8,6 +8,7 @@ { "fieldname": "dt", "fieldtype": "Link", + "in_list_view": 1, "label": "DocType", "oldfieldname": "dt", "oldfieldtype": "Link", @@ -19,6 +20,7 @@ "fieldname": "script_type", "fieldtype": "Select", "hidden": 1, + "in_list_view": 1, "label": "Script Type", "oldfieldname": "script_type", "oldfieldtype": "Select", @@ -29,16 +31,24 @@ { "fieldname": "script", "fieldtype": "Code", + "in_list_view": 1, "label": "Script", "oldfieldname": "script", "oldfieldtype": "Code", "options": "Script", "permlevel": 0 + }, + { + "fieldname": "sample", + "fieldtype": "HTML", + "label": "Sample", + "options": "

Custom Script Help

\n

Custom Scripts are executed only on the client-side (i.e. in Forms). Here are some examples to get you started

\n
\n// additional validation on dates\ncur_frm.cscript.custom_validate = function(doc) {\n    if (doc.from_date < get_today()) {\n        msgprint(\"You can not select past date in From Date\");\n        validated = false;\n    }\n}\n\n// make a field read-only after saving\ncur_frm.cscript.custom_refresh = function(doc) {\n    // use the __islocal value of doc, to check if the doc is saved or not\n    cur_frm.set_df_property(\"myfield\", \"read_only\", doc.__islocal ? 0 : 1);\n}\n\n// addtional permission checking\ncur_frm.cscript.custom_validate = function(doc) {\n    if(user==\"user1@example.com\" && doc.purpose!=\"Material Receipt\") {\n        msgprint(\"You are only allowed Material Receipt\");\n        validated = false;\n    }\n}\n\n// calculate sales incentive\ncur_frm.cscript.custom_validate = function(doc) {\n    // calculate incentives for each person on the deal\n    total_incentive = 0\n    $.each(wn.model.get(\"Sales Team\", {parent:doc.name}), function(i, d) {\n\n        // calculate incentive\n        var incentive_percent = 2;\n        if(doc.grand_total > 400) incentive_percent = 4;\n\n        // actual incentive\n        d.incentives = flt(doc.grand_total) * incentive_percent / 100;\n        total_incentive += flt(d.incentives)\n    });\n\n    doc.total_incentive = total_incentive;\n}\n\n
", + "permlevel": 0 } ], "icon": "icon-glass", "idx": 1, - "modified": "2014-01-20 17:48:31.000000", + "modified": "2014-06-19 06:55:02.522204", "modified_by": "Administrator", "module": "Core", "name": "Custom Script",