From 93b9ca86f78423339ae5830eb81dba39c04a40b5 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Tue, 29 Oct 2019 15:02:10 +0530 Subject: [PATCH 01/56] perf: optimise globals search --- .../global_search_settings.py | 11 +++- frappe/utils/global_search.py | 53 +++++++++---------- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/frappe/desk/doctype/global_search_settings/global_search_settings.py b/frappe/desk/doctype/global_search_settings/global_search_settings.py index 0729fca5cb..42ac5db8d2 100644 --- a/frappe/desk/doctype/global_search_settings/global_search_settings.py +++ b/frappe/desk/doctype/global_search_settings/global_search_settings.py @@ -29,12 +29,21 @@ class GlobalSearchSettings(Document): repeated_dts = (", ".join([frappe.bold(dt) for dt in repeated_dts])) frappe.throw(_("Document Type {0} has been repeated.").format(repeated_dts)) + def on_update(self): + get_doctypes_for_global_search() + def get_doctypes_for_global_search(): doctypes = frappe.get_list("Global Search DocType", fields=["document_type"], order_by="idx ASC") if not doctypes: return [] - return [d.document_type for d in doctypes] + priorities = [d.document_type for d in doctypes] + allowed_doctypes = ",".join(["'{0}'".format(dt) for dt in priorities]) + + frappe.cache().hset("global_search", "search_priorities", priorities) + frappe.cache().hset("global_search", "allowed_doctypes", allowed_doctypes) + + return priorities, allowed_doctypes @frappe.whitelist() def reset_global_search_settings_doctypes(): diff --git a/frappe/utils/global_search.py b/frappe/utils/global_search.py index 447466770d..7c38298dc3 100644 --- a/frappe/utils/global_search.py +++ b/frappe/utils/global_search.py @@ -418,10 +418,19 @@ def search(text, start=0, limit=20, doctype=""): from frappe.desk.doctype.global_search_settings.global_search_settings import get_doctypes_for_global_search results = [] - texts = [t.strip() for t in text.split('&') if t] - priorities = get_doctypes_for_global_search() - allowed_doctypes = ",".join(["'{0}'".format(dt) for dt in priorities]) - for text in texts: + sorted_results = [] + + priorities = frappe.cache().hget("global_search", "search_priorities") + allowed_doctypes = frappe.cache().hget("global_search", "allowed_doctypes") + + if not priorities or not allowed_doctypes: + priorities, allowed_doctypes = get_doctypes_for_global_search() + + for text in set(text.split('&')): + text = text.strip() + if not text: + continue + mariadb_conditions = '' postgres_conditions = '' offset = '' @@ -455,36 +464,22 @@ def search(text, start=0, limit=20, doctype=""): 'postgres': common_query.format(fields=postgres_fields, conditions=postgres_conditions, limit=limit, offset=offset) }, as_dict=True) - tmp_result=[] - for i in result: - if i.rank > 0.0: - if i in results or not results: - tmp_result.extend([i]) - results.extend(tmp_result) - - for r in results: - try: - if frappe.get_meta(r.doctype).image_field: - r.image = frappe.db.get_value(r.doctype, r.name, frappe.get_meta(r.doctype).image_field) - except Exception: - frappe.clear_messages() - - sorted_results = [] + results.extend(result) for priority in priorities: - tmp_result = [] - if not results: - break - for index, r in enumerate(results): - if r.doctype == priority: - tmp_result.extend([r]) + if r.doctype == priority and r.rank > 0.0: + try: + meta = frappe.get_meta(r.doctype) + if meta.image_field: + r.image = frappe.db.get_value(r.doctype, r.name, meta.image_field) + except Exception: + frappe.clear_messages() + + sorted_results.extend([r]) results.pop(index) - sorted_results.extend(tmp_result) - - return sorted_results - + return sorted_results or results @frappe.whitelist(allow_guest=True) def web_search(text, scope=None, start=0, limit=20): From da2d1d7bb1483a947b865a7613783d9c8a69d32f Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Sun, 10 Nov 2019 10:22:16 +0530 Subject: [PATCH 02/56] fix: add index on child table --- .../doctype/global_search_doctype/global_search_doctype.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frappe/desk/doctype/global_search_doctype/global_search_doctype.py b/frappe/desk/doctype/global_search_doctype/global_search_doctype.py index 4c9a948278..bf3b4d3040 100644 --- a/frappe/desk/doctype/global_search_doctype/global_search_doctype.py +++ b/frappe/desk/doctype/global_search_doctype/global_search_doctype.py @@ -3,8 +3,10 @@ # For license information, please see license.txt from __future__ import unicode_literals -# import frappe +import frappe from frappe.model.document import Document class GlobalSearchDocType(Document): - pass + + def on_doctype_update(): + frappe.db.add_index("Global Search DocType", ["document_type"]) From 633e2b546cb9c16f72e4ac216e4cb65ef2be0b16 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Wed, 27 Nov 2019 12:59:50 +0530 Subject: [PATCH 03/56] fix: do not pop item from list --- frappe/utils/global_search.py | 1 - 1 file changed, 1 deletion(-) diff --git a/frappe/utils/global_search.py b/frappe/utils/global_search.py index 7c38298dc3..c127a55619 100644 --- a/frappe/utils/global_search.py +++ b/frappe/utils/global_search.py @@ -477,7 +477,6 @@ def search(text, start=0, limit=20, doctype=""): frappe.clear_messages() sorted_results.extend([r]) - results.pop(index) return sorted_results or results From 7f82ee873ef5c4e62c033a2b37d0bef99dc47f04 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Wed, 27 Nov 2019 14:08:45 +0530 Subject: [PATCH 04/56] fix: dont add index for global search doctype --- .../doctype/global_search_doctype/global_search_doctype.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/frappe/desk/doctype/global_search_doctype/global_search_doctype.py b/frappe/desk/doctype/global_search_doctype/global_search_doctype.py index bf3b4d3040..4c9a948278 100644 --- a/frappe/desk/doctype/global_search_doctype/global_search_doctype.py +++ b/frappe/desk/doctype/global_search_doctype/global_search_doctype.py @@ -3,10 +3,8 @@ # For license information, please see license.txt from __future__ import unicode_literals -import frappe +# import frappe from frappe.model.document import Document class GlobalSearchDocType(Document): - - def on_doctype_update(): - frappe.db.add_index("Global Search DocType", ["document_type"]) + pass From 86ff0a635412beb5f9e3e52db5047ff5265cf4cc Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 27 Nov 2019 14:12:44 +0530 Subject: [PATCH 05/56] fix: list view group by filter ambiguous column name --- frappe/desk/listview.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/desk/listview.py b/frappe/desk/listview.py index fd0833eb51..e7f56d313e 100644 --- a/frappe/desk/listview.py +++ b/frappe/desk/listview.py @@ -48,7 +48,7 @@ def get_group_by_count(doctype, current_filters, field): else: return frappe.db.get_list(doctype, filters=current_filters, - group_by=field, + group_by='`tab{0}`.{1}'.format(doctype, field), fields=['count(*) as count', '`{}` as name'.format(field)], order_by='count desc', limit=50, From 649759fc3f92fba93bfb2e743084144dd723f201 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Fri, 29 Nov 2019 14:56:37 +0530 Subject: [PATCH 06/56] fix: Default and company address fixes --- frappe/contacts/doctype/address/address.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frappe/contacts/doctype/address/address.py b/frappe/contacts/doctype/address/address.py index 21ed88addb..40b5f55fa6 100644 --- a/frappe/contacts/doctype/address/address.py +++ b/frappe/contacts/doctype/address/address.py @@ -60,10 +60,6 @@ class Address(Document): if not [row for row in self.links if row.link_doctype == "Company"]: frappe.throw(_("Company is mandatory, as it is your company address")) - # removing other links - to_remove = [row for row in self.links if row.link_doctype != "Company"] - [ self.remove(row) for row in to_remove ] - def get_display(self): return get_address_display(self.as_dict()) @@ -81,7 +77,7 @@ class Address(Document): return False @frappe.whitelist() -def get_default_address(doctype, name, sort_key='is_primary_address'): +def get_default_address(doctype, name, sort_key='is_primary_address', existing_address=None): '''Returns default Address name for the given doctype, name''' if sort_key not in ['is_shipping_address', 'is_primary_address']: return None @@ -95,6 +91,10 @@ def get_default_address(doctype, name, sort_key='is_primary_address'): dl.link_name = %s and ifnull(addr.disabled, 0) = 0 """ %(sort_key, '%s', '%s'), (doctype, name)) + if existing_address: + if existing_address in [d[0] for d in out]: + return existing_address + if out: return sorted(out, key = functools.cmp_to_key(lambda x,y: cmp(y[1], x[1])))[0][0] else: From a8b16f1176ffad2755251744e75818e8e57fd5a2 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Sat, 30 Nov 2019 13:58:59 +0530 Subject: [PATCH 07/56] fix: multiple ui fixes --- .../setup_wizard_slide/setup_wizard_slide.json | 2 +- .../company_letter_head.json | 8 ++++---- .../public/js/frappe/ui/onboarding_dialog.js | 14 +++----------- frappe/public/js/frappe/ui/slides.js | 18 ++++++++++++++---- frappe/public/less/desk.less | 14 +++++++++++--- 5 files changed, 33 insertions(+), 23 deletions(-) diff --git a/frappe/desk/doctype/setup_wizard_slide/setup_wizard_slide.json b/frappe/desk/doctype/setup_wizard_slide/setup_wizard_slide.json index 94d94851c3..31d03253c4 100644 --- a/frappe/desk/doctype/setup_wizard_slide/setup_wizard_slide.json +++ b/frappe/desk/doctype/setup_wizard_slide/setup_wizard_slide.json @@ -157,7 +157,7 @@ "label": "Fields" } ], - "modified": "2019-11-26 17:33:34.553367", + "modified": "2019-11-30 13:58:05.688981", "modified_by": "Administrator", "module": "Desk", "name": "Setup Wizard Slide", diff --git a/frappe/printing/setup_wizard_slide/company_letter_head/company_letter_head.json b/frappe/printing/setup_wizard_slide/company_letter_head/company_letter_head.json index 3644a54089..eb01c619a4 100644 --- a/frappe/printing/setup_wizard_slide/company_letter_head/company_letter_head.json +++ b/frappe/printing/setup_wizard_slide/company_letter_head/company_letter_head.json @@ -7,19 +7,19 @@ "domains": [], "help_links": [ { - "label": "Know more about printing and branding through letterhead", + "label": "Need Help?", "video_id": "cKZHcx1znMc" } ], "idx": 0, - "image_src": "/assets/erpnext/images/illustrations/letterhead.png", + "image_src": "/assets/erpnext/images/illustrations/letterhead-onboard-2.png", "max_count": 0, - "modified": "2019-11-27 11:39:56.213373", + "modified": "2019-11-30 13:06:59.910747", "modified_by": "Administrator", "name": "Company Letter Head", "owner": "Administrator", "ref_doctype": "Letter Head", - "slide_desc": "Attach Letterhead: (Keep it web friendly as 1024px by 128px)", + "slide_desc": "

The letter head will appear across all print formats and PDFs

\n

Keep it web friendly as 1024px by 128px

", "slide_fields": [ { "align": "center", diff --git a/frappe/public/js/frappe/ui/onboarding_dialog.js b/frappe/public/js/frappe/ui/onboarding_dialog.js index 1798d0fe68..cee5216085 100644 --- a/frappe/public/js/frappe/ui/onboarding_dialog.js +++ b/frappe/public/js/frappe/ui/onboarding_dialog.js @@ -24,7 +24,7 @@ frappe.setup.OnboardingSlide = class OnboardingSlide extends frappe.ui.Slide { before_show() { (this.id === 0) ? - this.$next_btn.text(__('Start')) : this.$next_btn.text(__('Next')); + this.$next_btn.text(__('Let\'s Start')) : this.$next_btn.text(__('Next')); //last slide if (this.id === this.parent[0].children.length-1) { this.$complete_btn.removeClass('hide').addClass('action primary'); @@ -74,10 +74,7 @@ frappe.setup.OnboardingSlide = class OnboardingSlide extends frappe.ui.Slide { setup_help_links() { this.help_links.map(link => { let $link = $( - `${link.label} - - - ` + `${link.label || __("Need Help?")}` ); if (link.video_id) { $link.on('click', () => { @@ -109,7 +106,6 @@ frappe.setup.OnboardingDialog = class OnboardingDialog { this.dialog = new frappe.ui.Dialog({ static: true, minimizable: false, - title: __("Let's Onboard!") }); this.$wrapper = $(this.dialog.$wrapper).addClass('onboarding-dialog'); this.slide_container = new frappe.ui.Slides({ @@ -126,11 +122,7 @@ frappe.setup.OnboardingDialog = class OnboardingDialog { } }); - this.$wrapper.find('.modal-title').prepend( - ` - - ` - ); + this.$wrapper.find('.modal-header').remove(); } show() { diff --git a/frappe/public/js/frappe/ui/slides.js b/frappe/public/js/frappe/ui/slides.js index d2e504337c..d3b44bbb12 100644 --- a/frappe/public/js/frappe/ui/slides.js +++ b/frappe/public/js/frappe/ui/slides.js @@ -27,7 +27,7 @@ frappe.ui.Slide = class Slide {
@@ -36,6 +36,7 @@ frappe.ui.Slide = class Slide { this.$content = this.$body.find(".content"); this.$form = this.$body.find(".form"); this.$primary_btn = this.slides_footer.find('.primary'); + this.$form_wrapper = this.$body.find(".form-wrapper"); if(this.image_src) this.$content.append( $(``)); @@ -57,18 +58,25 @@ frappe.ui.Slide = class Slide { } setup_form() { + const fields = this.get_atomic_fields() this.form = new frappe.ui.FieldGroup({ - fields: this.get_atomic_fields(), + fields: fields, body: this.$form[0], no_submit_on_enter: true }); this.form.make(); + if (fields.length == 1 ) { + this.$form_wrapper.addClass("text-center") + } else { + this.$form_wrapper.removeClass("text-center") + } if(this.add_more) this.bind_more_button(); this.set_reqd_fields(); if(this.onload) { this.onload(this); } this.set_reqd_fields(); + window.foorm = this.form } setup_done_state() {} @@ -86,7 +94,7 @@ frappe.ui.Slide = class Slide { field.reqd = 1; } if(!field.static) { - if(field.label) field.label += ' 1'; + if(field.label) field.label; } return field; }); @@ -121,14 +129,16 @@ frappe.ui.Slide = class Slide { .on('click', () => { this.count++; var fields = JSON.parse(JSON.stringify(this.fields)); + this.form.add_fields(fields.map(field => { if(field.fieldname) field.fieldname += '_' + this.count; if(!field.static) { - if(field.label) field.label += ' ' + this.count; + if(field.label) field.label; } field.reqd = 0; return field; })); + if(this.count === this.max_count) { this.$more.addClass('hide'); } diff --git a/frappe/public/less/desk.less b/frappe/public/less/desk.less index d8f5a141f4..cbc63886ec 100644 --- a/frappe/public/less/desk.less +++ b/frappe/public/less/desk.less @@ -897,6 +897,13 @@ input[type="checkbox"] { &:focus { outline: none; } + + .slide-body { + width: 65%; + margin-right: auto; + margin-left: auto; + } + .fa-circle { font-size: 10px; margin: 0px 2px; @@ -928,6 +935,7 @@ input[type="checkbox"] { } .lead { margin-top: 20px; + font-weight: 500; } .success-state { margin-bottom: 20px; @@ -969,7 +977,7 @@ input[type="checkbox"] { } .onboarding-icon { - color: #3246F5; + color: @text-muted; margin-right: 5px; } @@ -980,8 +988,8 @@ input[type="checkbox"] { } img { - max-width: 128px; - max-height: 128px; + max-height: 175px; + width: auto; } .slides-progress { From 9ffd909c6d3bab4411e439b5f3b894453bf4dfe7 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Sat, 30 Nov 2019 14:38:28 +0530 Subject: [PATCH 08/56] refactor: change letterhead image --- .../company_letter_head/company_letter_head.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/printing/setup_wizard_slide/company_letter_head/company_letter_head.json b/frappe/printing/setup_wizard_slide/company_letter_head/company_letter_head.json index eb01c619a4..27f8024c40 100644 --- a/frappe/printing/setup_wizard_slide/company_letter_head/company_letter_head.json +++ b/frappe/printing/setup_wizard_slide/company_letter_head/company_letter_head.json @@ -12,9 +12,9 @@ } ], "idx": 0, - "image_src": "/assets/erpnext/images/illustrations/letterhead-onboard-2.png", + "image_src": "/assets/erpnext/images/illustrations/letterhead-onboard.png", "max_count": 0, - "modified": "2019-11-30 13:06:59.910747", + "modified": "2019-11-30 14:28:49.857639", "modified_by": "Administrator", "name": "Company Letter Head", "owner": "Administrator", From b6418ca059129949cbf72252d647cba1638d44f1 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 2 Dec 2019 13:03:00 +0530 Subject: [PATCH 09/56] style: remove stray code and added semicolon --- frappe/public/js/frappe/ui/slides.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/ui/slides.js b/frappe/public/js/frappe/ui/slides.js index d3b44bbb12..ceeadb09fa 100644 --- a/frappe/public/js/frappe/ui/slides.js +++ b/frappe/public/js/frappe/ui/slides.js @@ -66,9 +66,9 @@ frappe.ui.Slide = class Slide { }); this.form.make(); if (fields.length == 1 ) { - this.$form_wrapper.addClass("text-center") + this.$form_wrapper.addClass("text-center"); } else { - this.$form_wrapper.removeClass("text-center") + this.$form_wrapper.removeClass("text-center"); } if(this.add_more) this.bind_more_button(); @@ -76,7 +76,6 @@ frappe.ui.Slide = class Slide { if(this.onload) { this.onload(this); } this.set_reqd_fields(); - window.foorm = this.form } setup_done_state() {} From d648c4e1b9b4aecd55b9ae7c19993b9be13f2157 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 2 Dec 2019 13:03:39 +0530 Subject: [PATCH 10/56] refactor (breaking): rename Setup Wizard slide to Onboarding Slide --- .../__init__.py | 0 .../onboarding_slide.js} | 4 +- .../onboarding_slide.json} | 4 +- .../onboarding_slide.py} | 8 ++-- .../test_onboarding_slide.py} | 2 +- .../setup_wizard_slide_field.json | 2 +- .../company_letter_head.json | 37 +++++++++++++++++++ .../company_letter_head.json | 2 +- frappe/public/js/frappe/desk.js | 2 +- 9 files changed, 49 insertions(+), 12 deletions(-) rename frappe/desk/doctype/{setup_wizard_slide => onboarding_slide}/__init__.py (100%) rename frappe/desk/doctype/{setup_wizard_slide/setup_wizard_slide.js => onboarding_slide/onboarding_slide.js} (89%) rename frappe/desk/doctype/{setup_wizard_slide/setup_wizard_slide.json => onboarding_slide/onboarding_slide.json} (98%) rename frappe/desk/doctype/{setup_wizard_slide/setup_wizard_slide.py => onboarding_slide/onboarding_slide.py} (91%) rename frappe/desk/doctype/{setup_wizard_slide/test_setup_wizard_slide.py => onboarding_slide/test_onboarding_slide.py} (79%) create mode 100644 frappe/printing/onboarding_slide/company_letter_head/company_letter_head.json diff --git a/frappe/desk/doctype/setup_wizard_slide/__init__.py b/frappe/desk/doctype/onboarding_slide/__init__.py similarity index 100% rename from frappe/desk/doctype/setup_wizard_slide/__init__.py rename to frappe/desk/doctype/onboarding_slide/__init__.py diff --git a/frappe/desk/doctype/setup_wizard_slide/setup_wizard_slide.js b/frappe/desk/doctype/onboarding_slide/onboarding_slide.js similarity index 89% rename from frappe/desk/doctype/setup_wizard_slide/setup_wizard_slide.js rename to frappe/desk/doctype/onboarding_slide/onboarding_slide.js index 7a802ad2b6..d9c210f249 100644 --- a/frappe/desk/doctype/setup_wizard_slide/setup_wizard_slide.js +++ b/frappe/desk/doctype/onboarding_slide/onboarding_slide.js @@ -1,7 +1,7 @@ // Copyright (c) 2019, Frappe Technologies and contributors // For license information, please see license.txt -frappe.ui.form.on('Setup Wizard Slide', { +frappe.ui.form.on('Onboarding Slide', { refresh: function(frm) { frm.toggle_reqd('ref_doctype', frm.doc.slide_type!='Information'); frm.toggle_reqd('slide_module', frm.doc.slide_type=='Information'); @@ -33,7 +33,7 @@ frappe.ui.form.on('Setup Wizard Slide', { reqd: 1 }); $.each(fields, function(_i, data) { - let row = frappe.model.add_child(frm.doc, 'Setup Wizard Slide', 'slide_fields'); + let row = frappe.model.add_child(frm.doc, 'Onboarding Slide', 'slide_fields'); row.label = data.label; row.fieldtype = data.fieldtype; row.fieldname = data.fieldname; diff --git a/frappe/desk/doctype/setup_wizard_slide/setup_wizard_slide.json b/frappe/desk/doctype/onboarding_slide/onboarding_slide.json similarity index 98% rename from frappe/desk/doctype/setup_wizard_slide/setup_wizard_slide.json rename to frappe/desk/doctype/onboarding_slide/onboarding_slide.json index 31d03253c4..b68cdced69 100644 --- a/frappe/desk/doctype/setup_wizard_slide/setup_wizard_slide.json +++ b/frappe/desk/doctype/onboarding_slide/onboarding_slide.json @@ -68,7 +68,7 @@ "fieldname": "slide_fields", "fieldtype": "Table", "label": "Slide Fields", - "options": "Setup Wizard Slide Field" + "options": "Onboarding Slide Field" }, { "fieldname": "section_break_10", @@ -160,7 +160,7 @@ "modified": "2019-11-30 13:58:05.688981", "modified_by": "Administrator", "module": "Desk", - "name": "Setup Wizard Slide", + "name": "Onboarding Slide", "owner": "Administrator", "permissions": [ { diff --git a/frappe/desk/doctype/setup_wizard_slide/setup_wizard_slide.py b/frappe/desk/doctype/onboarding_slide/onboarding_slide.py similarity index 91% rename from frappe/desk/doctype/setup_wizard_slide/setup_wizard_slide.py rename to frappe/desk/doctype/onboarding_slide/onboarding_slide.py index 9109d52a85..1aacab2391 100644 --- a/frappe/desk/doctype/setup_wizard_slide/setup_wizard_slide.py +++ b/frappe/desk/doctype/onboarding_slide/onboarding_slide.py @@ -8,22 +8,22 @@ import json from frappe.model.document import Document from frappe.modules.export_file import export_to_files -class SetupWizardSlide(Document): +class OnboardingSlide(Document): def on_update(self): if self.ref_doctype: module = frappe.db.get_value('DocType', self.ref_doctype, 'module') else: module = self.slide_module - export_to_files(record_list=[['Setup Wizard Slide', self.name]], record_module=module) + export_to_files(record_list=[['Onboarding Slide', self.name]], record_module=module) def get_onboarding_slides_as_list(): slides = [] - slide_docs = frappe.get_all('Setup Wizard Slide', + slide_docs = frappe.get_all('Onboarding Slide', filters={'slide_order': ('!=', 0)}, order_by='slide_order') for entry in slide_docs: # using get_doc because child table fields are not fetched in get_all - slide_doc = frappe.get_doc('Setup Wizard Slide', entry.name) + slide_doc = frappe.get_doc('Onboarding Slide', entry.name) if frappe.scrub(slide_doc.app) in frappe.get_installed_apps(): slides.append(frappe._dict( slide_type=slide_doc.slide_type, diff --git a/frappe/desk/doctype/setup_wizard_slide/test_setup_wizard_slide.py b/frappe/desk/doctype/onboarding_slide/test_onboarding_slide.py similarity index 79% rename from frappe/desk/doctype/setup_wizard_slide/test_setup_wizard_slide.py rename to frappe/desk/doctype/onboarding_slide/test_onboarding_slide.py index 58652c4ec2..d78b9b6158 100644 --- a/frappe/desk/doctype/setup_wizard_slide/test_setup_wizard_slide.py +++ b/frappe/desk/doctype/onboarding_slide/test_onboarding_slide.py @@ -6,5 +6,5 @@ from __future__ import unicode_literals # import frappe import unittest -class TestSetupWizardSlide(unittest.TestCase): +class TestOnboardingSlide(unittest.TestCase): pass diff --git a/frappe/desk/doctype/setup_wizard_slide_field/setup_wizard_slide_field.json b/frappe/desk/doctype/setup_wizard_slide_field/setup_wizard_slide_field.json index 03d58d842b..3ae91912da 100644 --- a/frappe/desk/doctype/setup_wizard_slide_field/setup_wizard_slide_field.json +++ b/frappe/desk/doctype/setup_wizard_slide_field/setup_wizard_slide_field.json @@ -66,7 +66,7 @@ "modified": "2019-11-25 16:50:53.994656", "modified_by": "Administrator", "module": "Desk", - "name": "Setup Wizard Slide Field", + "name": "Onboarding Slide Field", "owner": "Administrator", "permissions": [], "sort_field": "modified", diff --git a/frappe/printing/onboarding_slide/company_letter_head/company_letter_head.json b/frappe/printing/onboarding_slide/company_letter_head/company_letter_head.json new file mode 100644 index 0000000000..6b4ad815e4 --- /dev/null +++ b/frappe/printing/onboarding_slide/company_letter_head/company_letter_head.json @@ -0,0 +1,37 @@ +{ + "add_more_button": 0, + "app": "ERPNext", + "creation": "2019-11-22 13:25:42.892593", + "docstatus": 0, + "doctype": "Onboarding Slide", + "domains": [], + "help_links": [ + { + "label": "Need Help?", + "video_id": "cKZHcx1znMc" + } + ], + "idx": 0, + "image_src": "/assets/erpnext/images/illustrations/letterhead-onboard.png", + "max_count": 0, + "modified": "2019-12-02 12:57:41.353913", + "modified_by": "Administrator", + "name": "Company Letter Head", + "owner": "Administrator", + "ref_doctype": "Letter Head", + "slide_desc": "

The letter head will appear across all print formats and PDFs

\n

Keep it web friendly as 1024px by 128px

", + "slide_fields": [ + { + "align": "center", + "fieldname": "letterhead", + "fieldtype": "Attach Image", + "label": "Attach Letterhead", + "options": "image", + "reqd": 0 + } + ], + "slide_order": 20, + "slide_title": "Company Letter Head", + "slide_type": "Create", + "submit_method": "" +} \ No newline at end of file diff --git a/frappe/printing/setup_wizard_slide/company_letter_head/company_letter_head.json b/frappe/printing/setup_wizard_slide/company_letter_head/company_letter_head.json index 27f8024c40..183a9fd09c 100644 --- a/frappe/printing/setup_wizard_slide/company_letter_head/company_letter_head.json +++ b/frappe/printing/setup_wizard_slide/company_letter_head/company_letter_head.json @@ -3,7 +3,7 @@ "app": "ERPNext", "creation": "2019-11-22 13:25:42.892593", "docstatus": 0, - "doctype": "Setup Wizard Slide", + "doctype": "Onboarding Slide", "domains": [], "help_links": [ { diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index 206094dd71..6c7a748822 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -88,7 +88,7 @@ frappe.Application = Class.extend({ } this.show_update_available(); - if (frappe.boot.is_first_startup) { + if (frappe.boot.is_first_startup || true) { this.setup_onboarding_wizard(); } From 9c57d8076934b0b4a077eb0c572e7c5de2acdb43 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Mon, 2 Dec 2019 13:05:06 +0530 Subject: [PATCH 11/56] fix: rename function to set --- .../doctype/global_search_settings/global_search_settings.py | 4 ++-- frappe/utils/global_search.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frappe/desk/doctype/global_search_settings/global_search_settings.py b/frappe/desk/doctype/global_search_settings/global_search_settings.py index 42ac5db8d2..e39bf18efe 100644 --- a/frappe/desk/doctype/global_search_settings/global_search_settings.py +++ b/frappe/desk/doctype/global_search_settings/global_search_settings.py @@ -30,9 +30,9 @@ class GlobalSearchSettings(Document): frappe.throw(_("Document Type {0} has been repeated.").format(repeated_dts)) def on_update(self): - get_doctypes_for_global_search() + set_doctypes_for_global_search() -def get_doctypes_for_global_search(): +def set_doctypes_for_global_search(): doctypes = frappe.get_list("Global Search DocType", fields=["document_type"], order_by="idx ASC") if not doctypes: return [] diff --git a/frappe/utils/global_search.py b/frappe/utils/global_search.py index c127a55619..32f2f09326 100644 --- a/frappe/utils/global_search.py +++ b/frappe/utils/global_search.py @@ -415,7 +415,7 @@ def search(text, start=0, limit=20, doctype=""): :param limit: number of results to return, default 20 :return: Array of result objects """ - from frappe.desk.doctype.global_search_settings.global_search_settings import get_doctypes_for_global_search + from frappe.desk.doctype.global_search_settings.global_search_settings import set_doctypes_for_global_search results = [] sorted_results = [] @@ -424,7 +424,7 @@ def search(text, start=0, limit=20, doctype=""): allowed_doctypes = frappe.cache().hget("global_search", "allowed_doctypes") if not priorities or not allowed_doctypes: - priorities, allowed_doctypes = get_doctypes_for_global_search() + priorities, allowed_doctypes = set_doctypes_for_global_search() for text in set(text.split('&')): text = text.strip() From 5b16efd1e734157949f7fb1af6c3923862a37fac Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 2 Dec 2019 17:23:13 +0530 Subject: [PATCH 12/56] chore: code cleanup for the old doctype Setup Wizard Slide --- .../onboarding_slide/onboarding_slide.json | 4 +- .../__init__.py | 0 .../onboarding_slide_field.json} | 14 +++---- .../onboarding_slide_field.py} | 2 +- .../__init__.py | 0 .../onboarding_slide_help_link.json} | 2 +- .../onboarding_slide_help_link.py} | 2 +- frappe/model/sync.py | 8 ++-- .../company_letter_head.json | 37 ------------------- frappe/public/js/frappe/desk.js | 4 +- .../public/js/frappe/ui/onboarding_dialog.js | 2 +- 11 files changed, 19 insertions(+), 56 deletions(-) rename frappe/desk/doctype/{setup_wizard_help_link => onboarding_slide_field}/__init__.py (100%) rename frappe/desk/doctype/{setup_wizard_slide_field/setup_wizard_slide_field.json => onboarding_slide_field/onboarding_slide_field.json} (97%) rename frappe/desk/doctype/{setup_wizard_help_link/setup_wizard_help_link.py => onboarding_slide_field/onboarding_slide_field.py} (86%) rename frappe/desk/doctype/{setup_wizard_slide_field => onboarding_slide_help_link}/__init__.py (100%) rename frappe/desk/doctype/{setup_wizard_help_link/setup_wizard_help_link.json => onboarding_slide_help_link/onboarding_slide_help_link.json} (94%) rename frappe/desk/doctype/{setup_wizard_slide_field/setup_wizard_slide_field.py => onboarding_slide_help_link/onboarding_slide_help_link.py} (85%) delete mode 100644 frappe/printing/setup_wizard_slide/company_letter_head/company_letter_head.json diff --git a/frappe/desk/doctype/onboarding_slide/onboarding_slide.json b/frappe/desk/doctype/onboarding_slide/onboarding_slide.json index b68cdced69..c5497070ed 100644 --- a/frappe/desk/doctype/onboarding_slide/onboarding_slide.json +++ b/frappe/desk/doctype/onboarding_slide/onboarding_slide.json @@ -90,7 +90,7 @@ "fieldname": "help_links", "fieldtype": "Table", "label": "Help Links", - "options": "Setup Wizard Help Link" + "options": "Onboarding Slide Help Link" }, { "fieldname": "action_section_break", @@ -157,7 +157,7 @@ "label": "Fields" } ], - "modified": "2019-11-30 13:58:05.688981", + "modified": "2019-12-02 16:17:17.368741", "modified_by": "Administrator", "module": "Desk", "name": "Onboarding Slide", diff --git a/frappe/desk/doctype/setup_wizard_help_link/__init__.py b/frappe/desk/doctype/onboarding_slide_field/__init__.py similarity index 100% rename from frappe/desk/doctype/setup_wizard_help_link/__init__.py rename to frappe/desk/doctype/onboarding_slide_field/__init__.py diff --git a/frappe/desk/doctype/setup_wizard_slide_field/setup_wizard_slide_field.json b/frappe/desk/doctype/onboarding_slide_field/onboarding_slide_field.json similarity index 97% rename from frappe/desk/doctype/setup_wizard_slide_field/setup_wizard_slide_field.json rename to frappe/desk/doctype/onboarding_slide_field/onboarding_slide_field.json index 3ae91912da..0992aed092 100644 --- a/frappe/desk/doctype/setup_wizard_slide_field/setup_wizard_slide_field.json +++ b/frappe/desk/doctype/onboarding_slide_field/onboarding_slide_field.json @@ -33,12 +33,6 @@ "in_list_view": 1, "label": "Fieldname" }, - { - "fieldname": "options", - "fieldtype": "Text", - "in_list_view": 1, - "label": "Options" - }, { "fieldname": "align", "fieldtype": "Select", @@ -60,10 +54,16 @@ { "fieldname": "column_break_4", "fieldtype": "Column Break" + }, + { + "fieldname": "options", + "fieldtype": "Text", + "in_list_view": 1, + "label": "Options" } ], "istable": 1, - "modified": "2019-11-25 16:50:53.994656", + "modified": "2019-12-02 16:43:51.930018", "modified_by": "Administrator", "module": "Desk", "name": "Onboarding Slide Field", diff --git a/frappe/desk/doctype/setup_wizard_help_link/setup_wizard_help_link.py b/frappe/desk/doctype/onboarding_slide_field/onboarding_slide_field.py similarity index 86% rename from frappe/desk/doctype/setup_wizard_help_link/setup_wizard_help_link.py rename to frappe/desk/doctype/onboarding_slide_field/onboarding_slide_field.py index fbb96bbd91..74b6782ff8 100644 --- a/frappe/desk/doctype/setup_wizard_help_link/setup_wizard_help_link.py +++ b/frappe/desk/doctype/onboarding_slide_field/onboarding_slide_field.py @@ -6,5 +6,5 @@ from __future__ import unicode_literals # import frappe from frappe.model.document import Document -class SetupWizardHelpLink(Document): +class OnboardingSlideField(Document): pass diff --git a/frappe/desk/doctype/setup_wizard_slide_field/__init__.py b/frappe/desk/doctype/onboarding_slide_help_link/__init__.py similarity index 100% rename from frappe/desk/doctype/setup_wizard_slide_field/__init__.py rename to frappe/desk/doctype/onboarding_slide_help_link/__init__.py diff --git a/frappe/desk/doctype/setup_wizard_help_link/setup_wizard_help_link.json b/frappe/desk/doctype/onboarding_slide_help_link/onboarding_slide_help_link.json similarity index 94% rename from frappe/desk/doctype/setup_wizard_help_link/setup_wizard_help_link.json rename to frappe/desk/doctype/onboarding_slide_help_link/onboarding_slide_help_link.json index b97b482cac..a09ba50553 100644 --- a/frappe/desk/doctype/setup_wizard_help_link/setup_wizard_help_link.json +++ b/frappe/desk/doctype/onboarding_slide_help_link/onboarding_slide_help_link.json @@ -25,7 +25,7 @@ "modified": "2019-11-19 13:39:57.716248", "modified_by": "Administrator", "module": "Desk", - "name": "Setup Wizard Help Link", + "name": "Onboarding Slide Help Link", "owner": "Administrator", "permissions": [], "quick_entry": 1, diff --git a/frappe/desk/doctype/setup_wizard_slide_field/setup_wizard_slide_field.py b/frappe/desk/doctype/onboarding_slide_help_link/onboarding_slide_help_link.py similarity index 85% rename from frappe/desk/doctype/setup_wizard_slide_field/setup_wizard_slide_field.py rename to frappe/desk/doctype/onboarding_slide_help_link/onboarding_slide_help_link.py index 1b880ef916..6ee5e4f7d7 100644 --- a/frappe/desk/doctype/setup_wizard_slide_field/setup_wizard_slide_field.py +++ b/frappe/desk/doctype/onboarding_slide_help_link/onboarding_slide_help_link.py @@ -6,5 +6,5 @@ from __future__ import unicode_literals # import frappe from frappe.model.document import Document -class SetupWizardSlideField(Document): +class OnboardingSlideHelpLink(Document): pass diff --git a/frappe/model/sync.py b/frappe/model/sync.py index b17c7db5c7..0c9c464695 100644 --- a/frappe/model/sync.py +++ b/frappe/model/sync.py @@ -42,9 +42,9 @@ def sync_for(app_name, force=0, sync_everything = False, verbose=False, reset_pe ("data_migration", "data_migration_mapping"), ("data_migration", "data_migration_plan_mapping"), ("data_migration", "data_migration_plan"), - ("desk", "setup_wizard_slide_field"), - ("desk", "setup_wizard_help_link"), - ("desk", "setup_wizard_slide")): + ("desk", "onboarding_slide_field"), + ("desk", "onboarding_slide_help_link"), + ("desk", "onboarding_slide")): files.append(os.path.join(frappe.get_app_path("frappe"), d[0], "doctype", d[1], d[1] + ".json")) @@ -73,7 +73,7 @@ def get_doc_files(files, start_path, force=0, sync_everything = False, verbose=F # load in sequence - warning for devs document_types = ['doctype', 'page', 'report', 'dashboard_chart_source', 'print_format', 'website_theme', 'web_form', 'notification', 'print_style', - 'data_migration_mapping', 'data_migration_plan', 'setup_wizard_slide'] + 'data_migration_mapping', 'data_migration_plan', 'onboarding_slide'] for doctype in document_types: doctype_path = os.path.join(start_path, doctype) if os.path.exists(doctype_path): diff --git a/frappe/printing/setup_wizard_slide/company_letter_head/company_letter_head.json b/frappe/printing/setup_wizard_slide/company_letter_head/company_letter_head.json deleted file mode 100644 index 183a9fd09c..0000000000 --- a/frappe/printing/setup_wizard_slide/company_letter_head/company_letter_head.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "add_more_button": 0, - "app": "ERPNext", - "creation": "2019-11-22 13:25:42.892593", - "docstatus": 0, - "doctype": "Onboarding Slide", - "domains": [], - "help_links": [ - { - "label": "Need Help?", - "video_id": "cKZHcx1znMc" - } - ], - "idx": 0, - "image_src": "/assets/erpnext/images/illustrations/letterhead-onboard.png", - "max_count": 0, - "modified": "2019-11-30 14:28:49.857639", - "modified_by": "Administrator", - "name": "Company Letter Head", - "owner": "Administrator", - "ref_doctype": "Letter Head", - "slide_desc": "

The letter head will appear across all print formats and PDFs

\n

Keep it web friendly as 1024px by 128px

", - "slide_fields": [ - { - "align": "center", - "fieldname": "letterhead", - "fieldtype": "Attach Image", - "label": "Attach Letterhead", - "options": "image", - "reqd": 0 - } - ], - "slide_order": 20, - "slide_title": "Company Letter Head", - "slide_type": "Create", - "submit_method": "" -} \ No newline at end of file diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index 6c7a748822..ebc5973c73 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -88,7 +88,7 @@ frappe.Application = Class.extend({ } this.show_update_available(); - if (frappe.boot.is_first_startup || true) { + if (frappe.boot.is_first_startup) { this.setup_onboarding_wizard(); } @@ -487,7 +487,7 @@ frappe.Application = Class.extend({ setup_onboarding_wizard: () => { var me = this; - frappe.call('frappe.desk.doctype.setup_wizard_slide.setup_wizard_slide.get_onboarding_slides').then(res => { + frappe.call('frappe.desk.doctype.onboarding_slide.onboarding_slide.get_onboarding_slides').then(res => { if (res.message) { let slides = res.message; if (slides.length) { diff --git a/frappe/public/js/frappe/ui/onboarding_dialog.js b/frappe/public/js/frappe/ui/onboarding_dialog.js index cee5216085..c5299e745a 100644 --- a/frappe/public/js/frappe/ui/onboarding_dialog.js +++ b/frappe/public/js/frappe/ui/onboarding_dialog.js @@ -40,7 +40,7 @@ frappe.setup.OnboardingSlide = class OnboardingSlide extends frappe.ui.Slide { this.$action_button.addClass('disabled'); if (me.add_more) me.values.max_count = me.max_count; frappe.call({ - method: 'frappe.desk.doctype.setup_wizard_slide.setup_wizard_slide.create_onboarding_docs', + method: 'frappe.desk.doctype.onboarding_slide.onboarding_slide.create_onboarding_docs', args: { values: me.values, doctype: me.ref_doctype, From 65402bc2fa8413387e3427bf883fe39d8ebb5027 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Tue, 3 Dec 2019 15:27:10 +0530 Subject: [PATCH 13/56] tests: fix results being return --- frappe/tests/test_global_search.py | 2 +- frappe/utils/global_search.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/tests/test_global_search.py b/frappe/tests/test_global_search.py index 3fcf1fe9ed..01067c85dd 100644 --- a/frappe/tests/test_global_search.py +++ b/frappe/tests/test_global_search.py @@ -80,7 +80,7 @@ class TestGlobalSearch(unittest.TestCase): make_property_setter(doctype, "repeat_on", "in_global_search", 1, "Int") global_search.rebuild_for_doctype(doctype) results = global_search.search('Monthly') - self.assertEqual(len(results), 2) + self.assertEqual(len(results), 3) def test_delete_doc(self): self.insert_test_events() diff --git a/frappe/utils/global_search.py b/frappe/utils/global_search.py index 48fd719f3b..b934195863 100644 --- a/frappe/utils/global_search.py +++ b/frappe/utils/global_search.py @@ -478,7 +478,7 @@ def search(text, start=0, limit=20, doctype=""): sorted_results.extend([r]) - return sorted_results or results + return sorted_results @frappe.whitelist(allow_guest=True) def web_search(text, scope=None, start=0, limit=20): From 8b746287700098d95960a1dca21e34aa5174c704 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 4 Dec 2019 11:27:30 +0530 Subject: [PATCH 14/56] fix: Color not working in the calendar --- frappe/public/js/frappe/views/calendar/calendar.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/views/calendar/calendar.js b/frappe/public/js/frappe/views/calendar/calendar.js index b84d37cff4..71ac175ba0 100644 --- a/frappe/public/js/frappe/views/calendar/calendar.js +++ b/frappe/public/js/frappe/views/calendar/calendar.js @@ -308,6 +308,7 @@ frappe.views.Calendar = Class.extend({ doctype: this.doctype, start: this.get_system_datetime(start), end: this.get_system_datetime(end), + fields: this.fields, filters: this.list_view.filter_area.get(), field_map: this.field_map }; @@ -356,9 +357,13 @@ frappe.views.Calendar = Class.extend({ prepare_colors: function(d) { let color, color_name; if(this.get_css_class) { - color_name = this.color_map[this.get_css_class(d)]; - color_name = frappe.ui.color.validate_hex(color_name) ? - color_name : 'blue'; + color_name = this.color_map[this.get_css_class(d)] || 'blue'; + + if (color_name.startsWith("#")) { + color_name = frappe.ui.color.validate_hex(color_name) ? + color_name : 'blue'; + } + d.backgroundColor = frappe.ui.color.get(color_name, 'extra-light'); d.textColor = frappe.ui.color.get(color_name, 'dark'); } else { From 5414f1897f5c756da8e7191300d19ed07387f976 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 4 Dec 2019 12:41:42 +0530 Subject: [PATCH 15/56] fix: query report chart options --- frappe/public/js/frappe/views/reports/query_report.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index a7505ff8f3..431b482173 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -507,6 +507,9 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { }) }; } + options.axisOptions = { + shortenYAxisNumbers: 1 + } return options; } @@ -561,7 +564,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { get_possible_chart_options() { const columns = this.columns; const rows = this.raw_data.result.filter(value => Object.keys(value).length); - const first_row = Array.isArray(rows[0]) ? rows[0] : Object.values(rows[0]); + const first_row = Array.isArray(rows[0]) ? rows[0] : columns.map(col => rows[0][col.fieldname]); const me = this const indices = first_row.reduce((accumulator, current_value, current_index) => { From 71b398c7c6e496b5e028bea461f807660bd25555 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 4 Dec 2019 13:11:03 +0530 Subject: [PATCH 16/56] style: aded semi colon --- frappe/public/js/frappe/views/reports/query_report.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index 431b482173..0771306298 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -509,7 +509,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { } options.axisOptions = { shortenYAxisNumbers: 1 - } + }; return options; } From d9bbd103822bc45d4a3b257e955cd39a4c9fe0bd Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 4 Dec 2019 15:59:38 +0530 Subject: [PATCH 17/56] fix: added a 'Continue' type slide --- .../onboarding_slide/onboarding_slide.json | 34 +++++----- .../onboarding_slide/onboarding_slide.py | 68 ++++++++++++++----- .../company_letter_head.json | 5 +- frappe/public/js/frappe/desk.js | 5 -- .../public/js/frappe/ui/onboarding_dialog.js | 27 +++++++- 5 files changed, 95 insertions(+), 44 deletions(-) diff --git a/frappe/desk/doctype/onboarding_slide/onboarding_slide.json b/frappe/desk/doctype/onboarding_slide/onboarding_slide.json index c5497070ed..3f6f0d719f 100644 --- a/frappe/desk/doctype/onboarding_slide/onboarding_slide.json +++ b/frappe/desk/doctype/onboarding_slide/onboarding_slide.json @@ -15,7 +15,6 @@ "slide_desc", "action_section_break", "slide_type", - "submit_method", "column_break_6", "max_count", "add_more_button", @@ -25,7 +24,8 @@ "section_break_10", "domains", "column_break_12", - "help_links" + "help_links", + "is_completed" ], "fields": [ { @@ -36,13 +36,6 @@ "reqd": 1, "unique": 1 }, - { - "depends_on": "eval:doc.slide_type!='Information'", - "description": "By default the code inside `create_onboarding_docs` method of the `Reference Document Type` is executed. If your method is not on the doctype level, place this method in {app_name}.utilities.onboarding_utils.{method_name} and specify the method name here", - "fieldname": "submit_method", - "fieldtype": "Data", - "label": "Submit Method" - }, { "fieldname": "slide_desc", "fieldtype": "HTML Editor", @@ -58,13 +51,13 @@ }, { "default": "0", - "depends_on": "eval:doc.slide_type!='Information'", + "depends_on": "eval:doc.slide_type=='Create' || doc.slide_type=='Settings'", "fieldname": "add_more_button", "fieldtype": "Check", "label": "Add More Button" }, { - "depends_on": "eval:doc.slide_type!='Information'", + "depends_on": "eval:doc.slide_type=='Create' || doc.slide_type=='Settings'", "fieldname": "slide_fields", "fieldtype": "Table", "label": "Slide Fields", @@ -98,11 +91,11 @@ "label": "Action Settings" }, { - "description": "If slide type is Action there should be a submit method bound to be executed after the slide is completed.", + "description": "If Slide Type is Create or Settings there should be a 'create_onboarding_docs' method in the {ref_doctype}.py file bound to be executed after the slide is completed.", "fieldname": "slide_type", "fieldtype": "Select", "label": "Slide Type", - "options": "Information\nCreate\nSettings", + "options": "Information\nCreate\nSettings\nContinue", "reqd": 1 }, { @@ -131,7 +124,7 @@ "label": "Description" }, { - "depends_on": "eval:doc.slide_type!='Information'", + "depends_on": "eval:doc.slide_type=='Create' || doc.slide_type=='Settings'", "fieldname": "ref_doctype", "fieldtype": "Link", "label": "Reference Document Type", @@ -145,19 +138,28 @@ "label": "Slide Order" }, { - "depends_on": "eval:doc.slide_type=='Information'", + "depends_on": "eval:doc.slide_type=='Information' || doc.slide_type=='Continue'", "fieldname": "slide_module", "fieldtype": "Link", "label": "Module", "options": "Module Def" }, { + "collapsible_depends_on": "eval:doc.slide_type=='Create' || doc.slide_type=='Settings'", "fieldname": "section_break_18", "fieldtype": "Section Break", "label": "Fields" + }, + { + "default": "0", + "fieldname": "is_completed", + "fieldtype": "Check", + "hidden": 1, + "label": "Is Completed", + "print_hide": 1 } ], - "modified": "2019-12-02 16:17:17.368741", + "modified": "2019-12-04 10:50:43.528901", "modified_by": "Administrator", "module": "Desk", "name": "Onboarding Slide", diff --git a/frappe/desk/doctype/onboarding_slide/onboarding_slide.py b/frappe/desk/doctype/onboarding_slide/onboarding_slide.py index 1aacab2391..7f1f3e9e7b 100644 --- a/frappe/desk/doctype/onboarding_slide/onboarding_slide.py +++ b/frappe/desk/doctype/onboarding_slide/onboarding_slide.py @@ -5,10 +5,15 @@ from __future__ import unicode_literals import frappe import json +from frappe import _ from frappe.model.document import Document from frappe.modules.export_file import export_to_files class OnboardingSlide(Document): + def validate(self): + if frappe.db.exists('Onboarding Slide', {'slide_type': 'Continue', 'name': ('!=', self.name)}): + frappe.throw(_("An Onboarding Slide of Slide Type Continue already exists.")) + def on_update(self): if self.ref_doctype: module = frappe.db.get_value('DocType', self.ref_doctype, 'module') @@ -18,14 +23,19 @@ class OnboardingSlide(Document): def get_onboarding_slides_as_list(): slides = [] - slide_docs = frappe.get_all('Onboarding Slide', - filters={'slide_order': ('!=', 0)}, + slide_docs = frappe.db.get_all('Onboarding Slide', + filters={'is_completed': 0}, + or_filters={'slide_order': ('!=', 0), 'slide_type': 'Continue'}, order_by='slide_order') + + # to check if continue slide is required + first_slide = get_first_slide() + for entry in slide_docs: # using get_doc because child table fields are not fetched in get_all slide_doc = frappe.get_doc('Onboarding Slide', entry.name) if frappe.scrub(slide_doc.app) in frappe.get_installed_apps(): - slides.append(frappe._dict( + slide = frappe._dict( slide_type=slide_doc.slide_type, title=slide_doc.slide_title, help=slide_doc.slide_desc, @@ -33,11 +43,16 @@ def get_onboarding_slides_as_list(): help_links=get_help_links(slide_doc), add_more=slide_doc.add_more_button, max_count=slide_doc.max_count, - submit_method=slide_doc.submit_method, image_src=get_slide_image(slide_doc), ref_doctype=slide_doc.ref_doctype, app=slide_doc.app - )) + ) + if slide.slide_type == 'Continue': + if is_continue_slide_required(first_slide): + slides.insert(0, slide) + else: + slides.append(slide) + return slides @frappe.whitelist() @@ -65,21 +80,26 @@ def get_slide_image(slide_doc): return slide_doc.image_src return None +def is_continue_slide_required(first_slide): + # check if first slide itself is not completed + if not first_slide.is_completed: + return False + + # check if there is any active slide which is not completed + return frappe.db.exists('Onboarding Slide', { + 'is_completed': 0, + 'slide_order': ('!=', 0), + 'slide_type': ('!=', 'Continue') + }) + @frappe.whitelist() -def create_onboarding_docs(values, doctype=None, submit_method=None, app=None, slide_type=None): +def create_onboarding_docs(values, doctype=None, app=None, slide_type=None): data = json.loads(values) - if submit_method: - try: - method = frappe.scrub(app) + '.utilities.onboarding_utils.' + submit_method - frappe.call(method, data) - except AttributeError: - create_generic_onboarding_doc(data, doctype, slide_type) + doc = frappe.new_doc(doctype) + if hasattr(doc, 'create_onboarding_docs'): + doc.create_onboarding_docs(data) else: - doc = frappe.new_doc(doctype) - if hasattr(doc, 'create_onboarding_docs'): - doc.create_onboarding_docs(data) - else: - create_generic_onboarding_doc(data, doctype, slide_type) + create_generic_onboarding_doc(data, doctype, slide_type) def create_generic_onboarding_doc(data, doctype, slide_type): if slide_type == 'Settings': @@ -94,4 +114,16 @@ def create_generic_onboarding_doc(data, doctype, slide_type): doc.set(entry, data.get(entry)) doc.flags.ignore_mandatory = True doc.flags.ignore_links = True - doc.insert() \ No newline at end of file + doc.insert() + +@frappe.whitelist() +def mark_slide_as_completed(slide_title): + frappe.db.set_value('Onboarding Slide', slide_title, 'is_completed', 1) + +def get_first_slide(): + slides = frappe.db.get_all('Onboarding Slide', + filters={'slide_order': ('!=', 0), 'slide_type': ('!=', 'Continue')}, + order_by='slide_order', + fields=['name', 'is_completed'] + ) + return slides[0] \ No newline at end of file diff --git a/frappe/printing/onboarding_slide/company_letter_head/company_letter_head.json b/frappe/printing/onboarding_slide/company_letter_head/company_letter_head.json index 6b4ad815e4..2f51d2e18a 100644 --- a/frappe/printing/onboarding_slide/company_letter_head/company_letter_head.json +++ b/frappe/printing/onboarding_slide/company_letter_head/company_letter_head.json @@ -14,7 +14,7 @@ "idx": 0, "image_src": "/assets/erpnext/images/illustrations/letterhead-onboard.png", "max_count": 0, - "modified": "2019-12-02 12:57:41.353913", + "modified": "2019-12-03 22:54:57.618989", "modified_by": "Administrator", "name": "Company Letter Head", "owner": "Administrator", @@ -32,6 +32,5 @@ ], "slide_order": 20, "slide_title": "Company Letter Head", - "slide_type": "Create", - "submit_method": "" + "slide_type": "Create" } \ No newline at end of file diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index ebc5973c73..dbdb698ebb 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -496,11 +496,6 @@ frappe.Application = Class.extend({ slides: slides }); me.progress_dialog.show(); - frappe.call({ - method: "frappe.desk.page.setup_wizard.setup_wizard.reset_is_first_startup", - args: {}, - callback: () => {} - }); }); } } diff --git a/frappe/public/js/frappe/ui/onboarding_dialog.js b/frappe/public/js/frappe/ui/onboarding_dialog.js index c5299e745a..be6f0fc91b 100644 --- a/frappe/public/js/frappe/ui/onboarding_dialog.js +++ b/frappe/public/js/frappe/ui/onboarding_dialog.js @@ -44,12 +44,12 @@ frappe.setup.OnboardingSlide = class OnboardingSlide extends frappe.ui.Slide { args: { values: me.values, doctype: me.ref_doctype, - submit_method: me.submit_method, app: me.app, slide_type: me.slide_type }, callback: function() { if (me.id === me.parent[0].children.length-1) { + me.reset_is_first_startup(); $('.onboarding-dialog').modal('toggle'); frappe.msgprint({ message: __('You are all set up!'), @@ -86,11 +86,34 @@ frappe.setup.OnboardingSlide = class OnboardingSlide extends frappe.ui.Slide { } setup_action_button() { - if (this.slide_type !== 'Information') { + if (this.slide_type === 'Create' || this.slide_type == 'Settings' || this.id === this.parent[0].children.length-1) { this.$action_button.addClass('primary'); } else { this.$action_button.removeClass('primary'); } + + this.$action_button.on('click', () => { + if (this.slide_type != 'Continue') { + this.mark_as_completed(); + } + }); + } + + mark_as_completed() { + frappe.call({ + method: 'frappe.desk.doctype.onboarding_slide.onboarding_slide.mark_slide_as_completed', + args: {slide_title: this.title}, + callback: () => {}, + freeze: true + }); + } + + reset_is_first_startup() { + frappe.call({ + method: "frappe.desk.page.setup_wizard.setup_wizard.reset_is_first_startup", + args: {}, + callback: () => {} + }); } }; From 36dd074dcc8f74ba5de1710401d402e457cd595b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 4 Dec 2019 16:21:18 +0530 Subject: [PATCH 18/56] fix: added is_last_slide method in slides --- frappe/desk/doctype/onboarding_slide/onboarding_slide.js | 4 ++-- frappe/public/js/frappe/ui/onboarding_dialog.js | 6 +++--- frappe/public/js/frappe/ui/slides.js | 7 +++++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/frappe/desk/doctype/onboarding_slide/onboarding_slide.js b/frappe/desk/doctype/onboarding_slide/onboarding_slide.js index d9c210f249..dc91f42913 100644 --- a/frappe/desk/doctype/onboarding_slide/onboarding_slide.js +++ b/frappe/desk/doctype/onboarding_slide/onboarding_slide.js @@ -3,8 +3,8 @@ frappe.ui.form.on('Onboarding Slide', { refresh: function(frm) { - frm.toggle_reqd('ref_doctype', frm.doc.slide_type!='Information'); - frm.toggle_reqd('slide_module', frm.doc.slide_type=='Information'); + frm.toggle_reqd('ref_doctype', (frm.doc.slide_type=='Create' || frm.doc.slide_type=='Settings')); + frm.toggle_reqd('slide_module', (frm.doc.slide_type=='Information' || frm.doc.slide_type=='Continue')); }, ref_doctype: function(frm) { diff --git a/frappe/public/js/frappe/ui/onboarding_dialog.js b/frappe/public/js/frappe/ui/onboarding_dialog.js index be6f0fc91b..a007784f58 100644 --- a/frappe/public/js/frappe/ui/onboarding_dialog.js +++ b/frappe/public/js/frappe/ui/onboarding_dialog.js @@ -26,7 +26,7 @@ frappe.setup.OnboardingSlide = class OnboardingSlide extends frappe.ui.Slide { (this.id === 0) ? this.$next_btn.text(__('Let\'s Start')) : this.$next_btn.text(__('Next')); //last slide - if (this.id === this.parent[0].children.length-1) { + if (this.is_last_slide()) { this.$complete_btn.removeClass('hide').addClass('action primary'); this.$next_btn.removeClass('action primary'); this.$action_button = this.$complete_btn; @@ -48,7 +48,7 @@ frappe.setup.OnboardingSlide = class OnboardingSlide extends frappe.ui.Slide { slide_type: me.slide_type }, callback: function() { - if (me.id === me.parent[0].children.length-1) { + if (me.is_last_slide()) { me.reset_is_first_startup(); $('.onboarding-dialog').modal('toggle'); frappe.msgprint({ @@ -86,7 +86,7 @@ frappe.setup.OnboardingSlide = class OnboardingSlide extends frappe.ui.Slide { } setup_action_button() { - if (this.slide_type === 'Create' || this.slide_type == 'Settings' || this.id === this.parent[0].children.length-1) { + if (this.slide_type === 'Create' || this.slide_type == 'Settings' || this.is_last_slide()) { this.$action_button.addClass('primary'); } else { this.$action_button.removeClass('primary'); diff --git a/frappe/public/js/frappe/ui/slides.js b/frappe/public/js/frappe/ui/slides.js index ceeadb09fa..d1c34faf6c 100644 --- a/frappe/public/js/frappe/ui/slides.js +++ b/frappe/public/js/frappe/ui/slides.js @@ -182,6 +182,13 @@ frappe.ui.Slide = class Slide { }); } + is_last_slide() { + if (this.id === this.parent[0].children.length-1) { + return true; + } + return false; + } + before_show() { } show_slide() { From a8076cfefeeef51e622e9c9888538a124da3bdbc Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 4 Dec 2019 17:11:29 +0530 Subject: [PATCH 19/56] feat: added validation for slide with same order --- frappe/desk/doctype/onboarding_slide/onboarding_slide.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frappe/desk/doctype/onboarding_slide/onboarding_slide.py b/frappe/desk/doctype/onboarding_slide/onboarding_slide.py index 7f1f3e9e7b..8c75d10b9a 100644 --- a/frappe/desk/doctype/onboarding_slide/onboarding_slide.py +++ b/frappe/desk/doctype/onboarding_slide/onboarding_slide.py @@ -11,8 +11,13 @@ from frappe.modules.export_file import export_to_files class OnboardingSlide(Document): def validate(self): - if frappe.db.exists('Onboarding Slide', {'slide_type': 'Continue', 'name': ('!=', self.name)}): - frappe.throw(_("An Onboarding Slide of Slide Type Continue already exists.")) + if self.slide_type == 'Continue' and frappe.db.exists('Onboarding Slide', {'slide_type': 'Continue', 'name': ('!=', self.name)}): + frappe.throw(_('An Onboarding Slide of Slide Type Continue already exists.')) + + if self.slide_order: + same_order_slide = frappe.db.exists('Onboarding Slide', {'slide_order': self.slide_order, 'name': ('!=', self.name)}) + if same_order_slide: + frappe.throw(_('An Onboarding Slide {0} with the same slide order already exists').format(same_order_slide)) def on_update(self): if self.ref_doctype: From 74fe3c71f719c81d1589567abdeb69622aa6cbdc Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 4 Dec 2019 17:20:22 +0530 Subject: [PATCH 20/56] feat: style only onboarding wizard --- frappe/public/js/frappe/ui/onboarding_dialog.js | 10 ++++++++++ frappe/public/js/frappe/ui/slides.js | 8 +------- frappe/public/less/desk.less | 12 ++++++------ 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/frappe/public/js/frappe/ui/onboarding_dialog.js b/frappe/public/js/frappe/ui/onboarding_dialog.js index a007784f58..b8e0db893c 100644 --- a/frappe/public/js/frappe/ui/onboarding_dialog.js +++ b/frappe/public/js/frappe/ui/onboarding_dialog.js @@ -22,6 +22,16 @@ frappe.setup.OnboardingSlide = class OnboardingSlide extends frappe.ui.Slide { } } + setup_form() { + super.setup_form(); + const fields = this.get_atomic_fields() + if (fields.length == 1) { + this.$form_wrapper.addClass("text-center"); + } else { + this.$form_wrapper.removeClass("text-center"); + } + } + before_show() { (this.id === 0) ? this.$next_btn.text(__('Let\'s Start')) : this.$next_btn.text(__('Next')); diff --git a/frappe/public/js/frappe/ui/slides.js b/frappe/public/js/frappe/ui/slides.js index d1c34faf6c..3212b1af48 100644 --- a/frappe/public/js/frappe/ui/slides.js +++ b/frappe/public/js/frappe/ui/slides.js @@ -58,18 +58,12 @@ frappe.ui.Slide = class Slide { } setup_form() { - const fields = this.get_atomic_fields() this.form = new frappe.ui.FieldGroup({ - fields: fields, + fields: this.get_atomic_fields(), body: this.$form[0], no_submit_on_enter: true }); this.form.make(); - if (fields.length == 1 ) { - this.$form_wrapper.addClass("text-center"); - } else { - this.$form_wrapper.removeClass("text-center"); - } if(this.add_more) this.bind_more_button(); this.set_reqd_fields(); diff --git a/frappe/public/less/desk.less b/frappe/public/less/desk.less index cbc63886ec..0a8062efa2 100644 --- a/frappe/public/less/desk.less +++ b/frappe/public/less/desk.less @@ -898,12 +898,6 @@ input[type="checkbox"] { outline: none; } - .slide-body { - width: 65%; - margin-right: auto; - margin-left: auto; - } - .fa-circle { font-size: 10px; margin: 0px 2px; @@ -970,6 +964,12 @@ input[type="checkbox"] { // Onboarding Dialog .onboarding-dialog { + .slide-body { + width: 65%; + margin-right: auto; + margin-left: auto; + } + .modal-dialog { width: 50%; height: 80%; From 0b19bc3790c90799d60a56c8c71071379503b808 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 4 Dec 2019 17:56:05 +0530 Subject: [PATCH 21/56] refactor: simplified calls --- frappe/public/js/frappe/desk.js | 10 ++--- .../public/js/frappe/ui/onboarding_dialog.js | 44 ++++++++----------- 2 files changed, 23 insertions(+), 31 deletions(-) diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index dbdb698ebb..c67325181a 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -486,17 +486,15 @@ frappe.Application = Class.extend({ }, setup_onboarding_wizard: () => { - var me = this; frappe.call('frappe.desk.doctype.onboarding_slide.onboarding_slide.get_onboarding_slides').then(res => { if (res.message) { let slides = res.message; if (slides.length) { - frappe.require("assets/frappe/js/frappe/ui/onboarding_dialog.js", () => { - me.progress_dialog = new frappe.setup.OnboardingDialog({ - slides: slides - }); - me.progress_dialog.show(); + this.progress_dialog = new frappe.setup.OnboardingDialog({ + slides: slides }); + this.progress_dialog.show(); + frappe.call('frappe.desk.page.setup_wizard.setup_wizard.reset_is_first_startup'); } } }); diff --git a/frappe/public/js/frappe/ui/onboarding_dialog.js b/frappe/public/js/frappe/ui/onboarding_dialog.js index b8e0db893c..d37882a8f0 100644 --- a/frappe/public/js/frappe/ui/onboarding_dialog.js +++ b/frappe/public/js/frappe/ui/onboarding_dialog.js @@ -45,33 +45,27 @@ frappe.setup.OnboardingSlide = class OnboardingSlide extends frappe.ui.Slide { } primary_action() { - let me = this; if (this.set_values()) { this.$action_button.addClass('disabled'); - if (me.add_more) me.values.max_count = me.max_count; - frappe.call({ - method: 'frappe.desk.doctype.onboarding_slide.onboarding_slide.create_onboarding_docs', - args: { - values: me.values, - doctype: me.ref_doctype, - app: me.app, - slide_type: me.slide_type - }, - callback: function() { - if (me.is_last_slide()) { - me.reset_is_first_startup(); - $('.onboarding-dialog').modal('toggle'); - frappe.msgprint({ - message: __('You are all set up!'), - indicator: 'green', - title: __('Success') - }); - } - }, - onerror: function() { - me.slides_footer.find('.primary').removeClass('disabled'); - }, - freeze: true + const primary_method = 'frappe.desk.doctype.onboarding_slide.onboarding_slide.create_onboarding_docs'; + if (this.add_more) { + this.values.max_count = this.max_count; + } + frappe.call(primary_method, { + values: this.values, + doctype: this.ref_doctype, + app: this.app, + slide_type: this.slide_type + }).then(() => { + if (this.is_last_slide()) { + this.reset_is_first_startup(); + $('.onboarding-dialog').modal('toggle'); + frappe.msgprint({ + message: __('You are all set up!'), + indicator: 'green', + title: __('Success') + }); + } }); } } From e1454c20ecb207345829c26788e3cd05da559477 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 4 Dec 2019 18:19:15 +0530 Subject: [PATCH 22/56] fix: codacy fixes --- .../public/js/frappe/ui/onboarding_dialog.js | 2 +- frappe/public/js/frappe/ui/slides.js | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/frappe/public/js/frappe/ui/onboarding_dialog.js b/frappe/public/js/frappe/ui/onboarding_dialog.js index d37882a8f0..962cf988e8 100644 --- a/frappe/public/js/frappe/ui/onboarding_dialog.js +++ b/frappe/public/js/frappe/ui/onboarding_dialog.js @@ -24,7 +24,7 @@ frappe.setup.OnboardingSlide = class OnboardingSlide extends frappe.ui.Slide { setup_form() { super.setup_form(); - const fields = this.get_atomic_fields() + const fields = this.get_atomic_fields(); if (fields.length == 1) { this.$form_wrapper.addClass("text-center"); } else { diff --git a/frappe/public/js/frappe/ui/slides.js b/frappe/public/js/frappe/ui/slides.js index 3212b1af48..19a6cf9263 100644 --- a/frappe/public/js/frappe/ui/slides.js +++ b/frappe/public/js/frappe/ui/slides.js @@ -80,14 +80,14 @@ frappe.ui.Slide = class Slide { if(this.add_more) { this.count = 1; fields = fields.map((field, i) => { - if(field.fieldname) { + if (field.fieldname) { field.fieldname += '_1'; } - if(i === 1 && this.mandatory_entry) { + if (i === 1 && this.mandatory_entry) { field.reqd = 1; } - if(!field.static) { - if(field.label) field.label; + if (!field.static) { + if (field.label) field.label; } return field; }); @@ -107,10 +107,10 @@ frappe.ui.Slide = class Slide { set_values() { this.values = this.form.get_values(); - if(this.values===null) { + if (this.values===null) { return false; } - if(this.validate && !this.validate()) { + if (this.validate && !this.validate()) { return false; } return true; @@ -124,9 +124,9 @@ frappe.ui.Slide = class Slide { var fields = JSON.parse(JSON.stringify(this.fields)); this.form.add_fields(fields.map(field => { - if(field.fieldname) field.fieldname += '_' + this.count; - if(!field.static) { - if(field.label) field.label; + if (field.fieldname) field.fieldname += '_' + this.count; + if (!field.static) { + if (field.label) field.label; } field.reqd = 0; return field; @@ -159,7 +159,7 @@ frappe.ui.Slide = class Slide { var empty_fields = this.reqd_fields.filter((field) => { return !field.get_value(); }); - if(empty_fields.length) { + if (empty_fields.length) { this.slides_footer.find('.action').addClass('disabled'); } else { this.slides_footer.find('.action').removeClass('disabled'); From 65ac0f3982fd26593943ea234f594ae71257e3d3 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 4 Dec 2019 19:30:14 +0530 Subject: [PATCH 23/56] fix: resume after reload broken --- frappe/public/js/frappe/desk.js | 1 - 1 file changed, 1 deletion(-) diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index c67325181a..92194acdca 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -494,7 +494,6 @@ frappe.Application = Class.extend({ slides: slides }); this.progress_dialog.show(); - frappe.call('frappe.desk.page.setup_wizard.setup_wizard.reset_is_first_startup'); } } }); From c336424fa4731522ccb9b26ff7e3a9df21f3330b Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Wed, 4 Dec 2019 23:13:43 +0530 Subject: [PATCH 24/56] fix: codacy fixes --- .../doctype/global_search_settings/global_search_settings.py | 1 - 1 file changed, 1 deletion(-) diff --git a/frappe/desk/doctype/global_search_settings/global_search_settings.py b/frappe/desk/doctype/global_search_settings/global_search_settings.py index e39bf18efe..bce41b072b 100644 --- a/frappe/desk/doctype/global_search_settings/global_search_settings.py +++ b/frappe/desk/doctype/global_search_settings/global_search_settings.py @@ -29,7 +29,6 @@ class GlobalSearchSettings(Document): repeated_dts = (", ".join([frappe.bold(dt) for dt in repeated_dts])) frappe.throw(_("Document Type {0} has been repeated.").format(repeated_dts)) - def on_update(self): set_doctypes_for_global_search() def set_doctypes_for_global_search(): From 3b61a6cb28f393ab968ad313277946f2ba1dc61b Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 6 Dec 2019 07:46:49 +0530 Subject: [PATCH 25/56] fix: Code cleanup - Pass values so that db cursor can handle escaping --- .../global_search_settings.py | 18 ++++------ frappe/utils/global_search.py | 36 +++++++++---------- 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/frappe/desk/doctype/global_search_settings/global_search_settings.py b/frappe/desk/doctype/global_search_settings/global_search_settings.py index bce41b072b..1cdbf11224 100644 --- a/frappe/desk/doctype/global_search_settings/global_search_settings.py +++ b/frappe/desk/doctype/global_search_settings/global_search_settings.py @@ -29,20 +29,16 @@ class GlobalSearchSettings(Document): repeated_dts = (", ".join([frappe.bold(dt) for dt in repeated_dts])) frappe.throw(_("Document Type {0} has been repeated.").format(repeated_dts)) - set_doctypes_for_global_search() + # reset cache + frappe.cache().hdel('global_search', 'search_priorities') -def set_doctypes_for_global_search(): - doctypes = frappe.get_list("Global Search DocType", fields=["document_type"], order_by="idx ASC") - if not doctypes: - return [] +def get_doctypes_for_global_search(): + def get_from_db(): + doctypes = frappe.get_list("Global Search DocType", fields=["document_type"], order_by="idx ASC") + return [d.document_type for d in doctypes] or [] - priorities = [d.document_type for d in doctypes] - allowed_doctypes = ",".join(["'{0}'".format(dt) for dt in priorities]) + return frappe.cache().hget("global_search", "search_priorities", get_from_db) - frappe.cache().hset("global_search", "search_priorities", priorities) - frappe.cache().hset("global_search", "allowed_doctypes", allowed_doctypes) - - return priorities, allowed_doctypes @frappe.whitelist() def reset_global_search_settings_doctypes(): diff --git a/frappe/utils/global_search.py b/frappe/utils/global_search.py index b934195863..3150295ccc 100644 --- a/frappe/utils/global_search.py +++ b/frappe/utils/global_search.py @@ -415,37 +415,34 @@ def search(text, start=0, limit=20, doctype=""): :param limit: number of results to return, default 20 :return: Array of result objects """ - from frappe.desk.doctype.global_search_settings.global_search_settings import set_doctypes_for_global_search + from frappe.desk.doctype.global_search_settings.global_search_settings import get_doctypes_for_global_search results = [] sorted_results = [] - priorities = frappe.cache().hget("global_search", "search_priorities") - allowed_doctypes = frappe.cache().hget("global_search", "allowed_doctypes") - - if not priorities or not allowed_doctypes: - priorities, allowed_doctypes = set_doctypes_for_global_search() + allowed_doctypes = get_doctypes_for_global_search() for text in set(text.split('&')): text = text.strip() if not text: continue - mariadb_conditions = '' - postgres_conditions = '' + conditions = '1=1' offset = '' - if doctype: - mariadb_conditions = postgres_conditions = '`doctype` = {} AND '.format(frappe.db.escape(doctype)) - mariadb_text = frappe.db.escape('+' + text + '*') mariadb_fields = '`doctype`, `name`, `content`, MATCH (`content`) AGAINST ({} IN BOOLEAN MODE) AS rank'.format(mariadb_text) postgres_fields = '`doctype`, `name`, `content`, TO_TSVECTOR("content") @@ PLAINTO_TSQUERY({}) AS rank'.format(frappe.db.escape(text)) - if allowed_doctypes: - mariadb_conditions += '`doctype` IN ({})'.format(allowed_doctypes) - postgres_conditions += '`doctype` IN ({})'.format(allowed_doctypes) + values = {} + + if doctype: + conditions = '`doctype` = %(doctype)s' + values['doctype'] = doctype + elif allowed_doctypes: + conditions = '`doctype` IN %(allowed_doctypes)s' + values['allowed_doctypes'] = allowed_doctypes if int(start) > 0: offset = 'OFFSET {}'.format(start) @@ -460,15 +457,16 @@ def search(text, start=0, limit=20, doctype=""): """ result = frappe.db.multisql({ - 'mariadb': common_query.format(fields=mariadb_fields, conditions=mariadb_conditions, limit=limit, offset=offset), - 'postgres': common_query.format(fields=postgres_fields, conditions=postgres_conditions, limit=limit, offset=offset) - }, as_dict=True) + 'mariadb': common_query.format(fields=mariadb_fields, conditions=conditions, limit=limit, offset=offset), + 'postgres': common_query.format(fields=postgres_fields, conditions=conditions, limit=limit, offset=offset) + }, values=values, as_dict=True) results.extend(result) - for priority in priorities: + # sort results based on allowed_doctype's priority + for doctype in allowed_doctypes: for index, r in enumerate(results): - if r.doctype == priority and r.rank > 0.0: + if r.doctype == doctype and r.rank > 0.0: try: meta = frappe.get_meta(r.doctype) if meta.image_field: From 10701738d85d1a1d9bd4d79f2f2bac58d51567b1 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Fri, 6 Dec 2019 08:29:02 +0530 Subject: [PATCH 26/56] fix: Convert list to tuple --- frappe/utils/global_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/utils/global_search.py b/frappe/utils/global_search.py index 3150295ccc..7012b737c0 100644 --- a/frappe/utils/global_search.py +++ b/frappe/utils/global_search.py @@ -442,7 +442,7 @@ def search(text, start=0, limit=20, doctype=""): values['doctype'] = doctype elif allowed_doctypes: conditions = '`doctype` IN %(allowed_doctypes)s' - values['allowed_doctypes'] = allowed_doctypes + values['allowed_doctypes'] = tuple(allowed_doctypes) if int(start) > 0: offset = 'OFFSET {}'.format(start) From 59d6be6446d7a9ea572e45b8d78b40ace37ff632 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Fri, 6 Dec 2019 09:21:24 +0530 Subject: [PATCH 27/56] fix: revert changes made to get_default_address function --- frappe/contacts/doctype/address/address.py | 5 +---- frappe/public/js/frappe/ui/notifications/notifications.js | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/frappe/contacts/doctype/address/address.py b/frappe/contacts/doctype/address/address.py index 40b5f55fa6..dfe02edee4 100644 --- a/frappe/contacts/doctype/address/address.py +++ b/frappe/contacts/doctype/address/address.py @@ -77,7 +77,7 @@ class Address(Document): return False @frappe.whitelist() -def get_default_address(doctype, name, sort_key='is_primary_address', existing_address=None): +def get_default_address(doctype, name, sort_key='is_primary_address'): '''Returns default Address name for the given doctype, name''' if sort_key not in ['is_shipping_address', 'is_primary_address']: return None @@ -91,9 +91,6 @@ def get_default_address(doctype, name, sort_key='is_primary_address', existing_a dl.link_name = %s and ifnull(addr.disabled, 0) = 0 """ %(sort_key, '%s', '%s'), (doctype, name)) - if existing_address: - if existing_address in [d[0] for d in out]: - return existing_address if out: return sorted(out, key = functools.cmp_to_key(lambda x,y: cmp(y[1], x[1])))[0][0] diff --git a/frappe/public/js/frappe/ui/notifications/notifications.js b/frappe/public/js/frappe/ui/notifications/notifications.js index 2420d6772e..76da0593a3 100644 --- a/frappe/public/js/frappe/ui/notifications/notifications.js +++ b/frappe/public/js/frappe/ui/notifications/notifications.js @@ -49,7 +49,7 @@ frappe.ui.Notifications = class Notifications { this.get_notifications_list(this.max_length).then(list => { this.dropdown_items = list; this.render_notifications_dropdown(); - if (this.notifications_settings.seen == 0) { + if (this.notification_settings && this.notifications_settings.seen == 0) { this.$notification_indicator.show(); } }); From b4bff1d0096166037f672cdd086b370e9b995225 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Fri, 6 Dec 2019 09:23:48 +0530 Subject: [PATCH 28/56] fix: Remove changes in notifications --- frappe/contacts/doctype/address/address.py | 1 - frappe/public/js/frappe/ui/notifications/notifications.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/contacts/doctype/address/address.py b/frappe/contacts/doctype/address/address.py index dfe02edee4..75fd0ad8c6 100644 --- a/frappe/contacts/doctype/address/address.py +++ b/frappe/contacts/doctype/address/address.py @@ -91,7 +91,6 @@ def get_default_address(doctype, name, sort_key='is_primary_address'): dl.link_name = %s and ifnull(addr.disabled, 0) = 0 """ %(sort_key, '%s', '%s'), (doctype, name)) - if out: return sorted(out, key = functools.cmp_to_key(lambda x,y: cmp(y[1], x[1])))[0][0] else: diff --git a/frappe/public/js/frappe/ui/notifications/notifications.js b/frappe/public/js/frappe/ui/notifications/notifications.js index 76da0593a3..2420d6772e 100644 --- a/frappe/public/js/frappe/ui/notifications/notifications.js +++ b/frappe/public/js/frappe/ui/notifications/notifications.js @@ -49,7 +49,7 @@ frappe.ui.Notifications = class Notifications { this.get_notifications_list(this.max_length).then(list => { this.dropdown_items = list; this.render_notifications_dropdown(); - if (this.notification_settings && this.notifications_settings.seen == 0) { + if (this.notifications_settings.seen == 0) { this.$notification_indicator.show(); } }); From 84d8db2dffcc6990e8fb37781d112ce96e6eb0a7 Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 6 Dec 2019 11:43:02 +0530 Subject: [PATCH 29/56] fix: fix ambiguous column error when going to next doc --- frappe/desk/form/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/desk/form/utils.py b/frappe/desk/form/utils.py index 6676bd1908..cf2a8f0879 100644 --- a/frappe/desk/form/utils.py +++ b/frappe/desk/form/utils.py @@ -105,7 +105,7 @@ def get_next(doctype, value, prev, filters, sort_order, sort_field): res = frappe.get_list(doctype, fields = ["name"], filters = filters, - order_by = sort_field + " " + sort_order, + order_by = "`tab{0}`.{1}".format(doctype, sort_field) + " " + sort_order, limit_start=0, limit_page_length=1, as_list=True) if not res: From e07f5ffafe32d98ce14bf4b461e1144896961411 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 6 Dec 2019 12:29:09 +0530 Subject: [PATCH 30/56] fix: spacing Co-Authored-By: Shivam Mishra --- frappe/model/sync.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/model/sync.py b/frappe/model/sync.py index 0c9c464695..c489f2dde8 100644 --- a/frappe/model/sync.py +++ b/frappe/model/sync.py @@ -73,7 +73,7 @@ def get_doc_files(files, start_path, force=0, sync_everything = False, verbose=F # load in sequence - warning for devs document_types = ['doctype', 'page', 'report', 'dashboard_chart_source', 'print_format', 'website_theme', 'web_form', 'notification', 'print_style', - 'data_migration_mapping', 'data_migration_plan', 'onboarding_slide'] + 'data_migration_mapping', 'data_migration_plan', 'onboarding_slide'] for doctype in document_types: doctype_path = os.path.join(start_path, doctype) if os.path.exists(doctype_path): From 54e55380657a97540fcdd12041d6621881fc87e0 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 6 Dec 2019 15:18:54 +0530 Subject: [PATCH 31/56] fix: patch --- frappe/patches/v11_0/drop_column_apply_user_permissions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/patches/v11_0/drop_column_apply_user_permissions.py b/frappe/patches/v11_0/drop_column_apply_user_permissions.py index 4f46bc0907..fd1a975eaf 100644 --- a/frappe/patches/v11_0/drop_column_apply_user_permissions.py +++ b/frappe/patches/v11_0/drop_column_apply_user_permissions.py @@ -10,6 +10,7 @@ def execute(): if column in frappe.db.get_table_columns(doctype): frappe.db.sql("alter table `tab{0}` drop column {1}".format(doctype, column)) + frappe.reload_doc('core', 'doctype', 'doctype_link', force=True) frappe.reload_doc('core', 'doctype', 'docperm', force=True) frappe.reload_doc('core', 'doctype', 'custom_docperm', force=True) From 8590ff04770b12e2b581a49866ea99c92791dff9 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Fri, 6 Dec 2019 15:20:10 +0530 Subject: [PATCH 32/56] fix: add default role on sign up via SSO --- frappe/utils/oauth.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frappe/utils/oauth.py b/frappe/utils/oauth.py index 79bcee7a74..969623c369 100644 --- a/frappe/utils/oauth.py +++ b/frappe/utils/oauth.py @@ -283,6 +283,12 @@ def update_oauth_user(user, data, provider): if save: user.flags.ignore_permissions = True user.flags.no_welcome_mail = True + + # set default signup role as per Portal Settings + default_role = frappe.db.get_single_value("Portal Settings", "default_role") + if default_role: + user.add_roles(default_role) + user.save() def get_first_name(data): From e6664bfba9e5e8c248106dfb065457eef7cabf89 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 6 Dec 2019 16:13:10 +0530 Subject: [PATCH 33/56] fix: scheduler log cold start --- .../scheduled_job_type/scheduled_job_type.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py index 38123cea33..5d5bd35eec 100644 --- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py @@ -23,7 +23,6 @@ class ScheduledJobType(Document): def enqueue(self): # enqueue event if last execution is done if self.is_event_due(): - self.update_last_execution() if frappe.flags.enqueued_jobs: frappe.flags.enqueued_jobs.append(self.method) @@ -39,11 +38,8 @@ class ScheduledJobType(Document): def is_event_due(self, current_time = None): '''Return true if event is due based on time lapsed since last execution''' - # save last execution in expected execution time as per cron - self.last_execution = self.get_next_execution() - # if the next scheduled event is before NOW, then its due! - return self.last_execution <= (current_time or now_datetime()) + return self.get_next_execution() <= (current_time or now_datetime()) def is_job_in_queue(self): queued_jobs = get_jobs(site=frappe.local.site, key='job_type')[frappe.local.site] @@ -68,7 +64,7 @@ class ScheduledJobType(Document): self.cron_format = CRON_MAP[self.frequency] return croniter(self.cron_format, - get_datetime(self.last_execution)).get_next(datetime) + get_datetime(self.last_execution or datetime(2000, 1, 1))).get_next(datetime) def execute(self): self.scheduler_log = None @@ -94,15 +90,16 @@ class ScheduledJobType(Document): self.scheduler_log.db_set('status', status) if status == 'Failed': self.scheduler_log.db_set('details', frappe.get_traceback()) - frappe.db.commit() - - def update_last_execution(self): - self.db_set('last_execution', self.last_execution, update_modified=False) + if status == 'Start': + self.db_set('last_execution', now_datetime(), update_modified=False) frappe.db.commit() def get_queue_name(self): return 'long' if ('Long' in self.frequency) else 'default' + def on_trash(self): + frappe.db.sql('delete from `tabScheduled Job Log` where scheduled_job_type=%s', self.name) + @frappe.whitelist() def execute_event(doc): frappe.only_for('System Manager') From 2d4d8538880562b0039f2a081db0a74cb9a902df Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 6 Dec 2019 17:09:31 +0530 Subject: [PATCH 34/56] test: list view child table filter with created by filter --- frappe/tests/test_listview.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/frappe/tests/test_listview.py b/frappe/tests/test_listview.py index 5c0657e9ee..3a73301608 100644 --- a/frappe/tests/test_listview.py +++ b/frappe/tests/test_listview.py @@ -6,7 +6,7 @@ import unittest import frappe import json -from frappe.desk.listview import get_list_settings, set_list_settings +from frappe.desk.listview import get_list_settings, set_list_settings, get_group_by_count class TestListView(unittest.TestCase): def setUp(self): @@ -51,3 +51,13 @@ class TestListView(unittest.TestCase): self.assertEqual(settings.disable_count, 0) self.assertEqual(settings.disable_sidebar_stats, 0) + def test_list_view_child_table_filter_with_created_by_filter(self): + if frappe.db.exists("Note", "Test created by filter with child table filter"): + frappe.delete_doc("Note", "Test created by filter with child table filter") + + doc = frappe.get_doc({"doctype": "Note", "title": "Test created by filter with child table filter", "public": 1}) + doc.append("seen_by", {"user": "Administrator"}) + doc.insert() + + data = {d.name: d.count for d in get_group_by_count('Note', '[["Note Seen By","user","=","Administrator"]]', 'owner')} + self.assertEqual(data['Administrator'], 1) \ No newline at end of file From 86ecf4fbc1522afe4a29eb42420b0f4cd370924d Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 6 Dec 2019 18:06:52 +0530 Subject: [PATCH 35/56] fix: patch --- frappe/patches.txt | 2 +- frappe/patches/v11_0/drop_column_apply_user_permissions.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/patches.txt b/frappe/patches.txt index 603df8d778..df704a1c4f 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -6,11 +6,11 @@ frappe.patches.v8_0.update_global_search_table frappe.patches.v7_0.update_auth frappe.patches.v8_0.drop_in_dialog #2017-09-22 frappe.patches.v7_2.remove_in_filter -frappe.patches.v11_0.drop_column_apply_user_permissions execute:frappe.reload_doc('core', 'doctype', 'doctype', force=True) #2017-09-22 execute:frappe.reload_doc('core', 'doctype', 'docfield', force=True) #2018-02-20 execute:frappe.reload_doc('core', 'doctype', 'doctype_action', force=True) #2019-09-23 execute:frappe.reload_doc('core', 'doctype', 'doctype_link', force=True) #2019-09-23 +frappe.patches.v11_0.drop_column_apply_user_permissions execute:frappe.reload_doc('core', 'doctype', 'custom_docperm') execute:frappe.reload_doc('core', 'doctype', 'docperm') #2018-05-29 execute:frappe.reload_doc('core', 'doctype', 'comment') diff --git a/frappe/patches/v11_0/drop_column_apply_user_permissions.py b/frappe/patches/v11_0/drop_column_apply_user_permissions.py index fd1a975eaf..4f46bc0907 100644 --- a/frappe/patches/v11_0/drop_column_apply_user_permissions.py +++ b/frappe/patches/v11_0/drop_column_apply_user_permissions.py @@ -10,7 +10,6 @@ def execute(): if column in frappe.db.get_table_columns(doctype): frappe.db.sql("alter table `tab{0}` drop column {1}".format(doctype, column)) - frappe.reload_doc('core', 'doctype', 'doctype_link', force=True) frappe.reload_doc('core', 'doctype', 'docperm', force=True) frappe.reload_doc('core', 'doctype', 'custom_docperm', force=True) From f772e6ab224325bbc7a74f3341018fb7284c2c69 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 6 Dec 2019 18:22:46 +0530 Subject: [PATCH 36/56] fix: patch --- frappe/patches.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/patches.txt b/frappe/patches.txt index df704a1c4f..90abc8c31d 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -6,10 +6,10 @@ frappe.patches.v8_0.update_global_search_table frappe.patches.v7_0.update_auth frappe.patches.v8_0.drop_in_dialog #2017-09-22 frappe.patches.v7_2.remove_in_filter -execute:frappe.reload_doc('core', 'doctype', 'doctype', force=True) #2017-09-22 -execute:frappe.reload_doc('core', 'doctype', 'docfield', force=True) #2018-02-20 execute:frappe.reload_doc('core', 'doctype', 'doctype_action', force=True) #2019-09-23 execute:frappe.reload_doc('core', 'doctype', 'doctype_link', force=True) #2019-09-23 +execute:frappe.reload_doc('core', 'doctype', 'doctype', force=True) #2017-09-22 +execute:frappe.reload_doc('core', 'doctype', 'docfield', force=True) #2018-02-20 frappe.patches.v11_0.drop_column_apply_user_permissions execute:frappe.reload_doc('core', 'doctype', 'custom_docperm') execute:frappe.reload_doc('core', 'doctype', 'docperm') #2018-05-29 From 196b2ad2549ba65dfeb5e72ac8e6cb3f09639373 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Fri, 6 Dec 2019 22:43:49 +0530 Subject: [PATCH 37/56] fix: use get_status for rq job status job.status has been replaced with job.get_status() in the latest release (1.0) https://github.com/rq/rq/releases/tag/v1.0 Signed-off-by: Chinmay D. Pai --- frappe/core/page/background_jobs/background_jobs.html | 2 +- frappe/core/page/background_jobs/background_jobs.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/core/page/background_jobs/background_jobs.html b/frappe/core/page/background_jobs/background_jobs.html index c5d598ccd3..08177ecf8a 100644 --- a/frappe/core/page/background_jobs/background_jobs.html +++ b/frappe/core/page/background_jobs/background_jobs.html @@ -11,7 +11,7 @@ {% for j in jobs %} - {{ j.queue.split(".").slice(-1)[0] }} + {{ j.queue.split(".").slice(-1)[0] }}
{{ frappe.utils.encode_tags(j.job_name) }} diff --git a/frappe/core/page/background_jobs/background_jobs.py b/frappe/core/page/background_jobs/background_jobs.py index 10cb7b97ac..c8a2352968 100644 --- a/frappe/core/page/background_jobs/background_jobs.py +++ b/frappe/core/page/background_jobs/background_jobs.py @@ -29,9 +29,9 @@ def get_info(show_failed=False): jobs.append({ 'job_name': j.kwargs.get('kwargs', {}).get('playbook_method') \ or str(j.kwargs.get('job_name')), - 'status': j.status, 'queue': name, + 'status': j.get_status(), 'queue': name, 'creation': format_datetime(convert_utc_to_user_timezone(j.created_at)), - 'color': colors[j.status] + 'color': colors[j.get_status()] }) if j.exc_info: jobs[-1]['exc_info'] = j.exc_info From 80f87a37fb5d97d06c8c73287cb952f4dc0b185c Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sat, 7 Dec 2019 12:50:23 +0530 Subject: [PATCH 38/56] fix: not able to download XML file --- frappe/utils/response.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/utils/response.py b/frappe/utils/response.py index 8169986e44..1dfbbe5516 100644 --- a/frappe/utils/response.py +++ b/frappe/utils/response.py @@ -210,7 +210,7 @@ def send_private_file(path): blacklist = ['.svg', '.html', '.htm', '.xml'] if extension.lower() in blacklist: - response.headers.add(b'Content-Disposition', b'attachment', filename=filename.encode("utf-8")) + response.headers.add('Content-Disposition', 'attachment', filename=filename.encode("utf-8")) response.mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream' From 35024c18fc86d51e0e414068c3494510d2ad14e3 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Sat, 7 Dec 2019 19:00:54 +0530 Subject: [PATCH 39/56] fix(security): invalidate reset_password_key on password reset currently there is no way to invalidate reset_password_key on updating password through the user settings. so whenever the user sets a new password we'll invalidate the reset_password_key, so that existing links to reset user passwords cannot be used. Signed-off-by: Chinmay D. Pai --- frappe/core/doctype/user/user.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 7de2bb20e5..35495954b4 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -97,7 +97,9 @@ class User(Document): self.share_with_self() clear_notifications(user=self.name) frappe.clear_cache(user=self.name) - self.send_password_notification(self.__new_password) + if self.__new_password: + self.send_password_notification(self.__new_password) + self.reset_password_key = '' create_contact(self, ignore_mandatory=True) if self.name not in ('Administrator', 'Guest') and not self.user_image: frappe.enqueue('frappe.core.doctype.user.user.update_gravatar', name=self.name) @@ -1071,4 +1073,4 @@ def generate_keys(user): user_details.save() return {"api_secret": api_secret} - frappe.throw(frappe._("Not Permitted"), frappe.PermissionError) \ No newline at end of file + frappe.throw(frappe._("Not Permitted"), frappe.PermissionError) From ae418b90c543a05c728c4e8739c832921747a47e Mon Sep 17 00:00:00 2001 From: Saurabh Date: Mon, 9 Dec 2019 11:13:45 +0530 Subject: [PATCH 40/56] fix: add hourly long frequency to scheduled job type --- .../core/doctype/scheduled_job_type/scheduled_job_type.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.json b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.json index 1aafdb47b0..e2ec921679 100644 --- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.json +++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.json @@ -60,7 +60,7 @@ "in_list_view": 1, "in_standard_filter": 1, "label": "Frequency", - "options": "All\nHourly\nDaily\nDaily Long\nWeekly\nWeekly Long\nMonthly\nMonthly Long\nCron\nYearly\nAnnual", + "options": "All\nHourly\nHourly Long\nDaily\nDaily Long\nWeekly\nWeekly Long\nMonthly\nMonthly Long\nCron\nYearly\nAnnual", "read_only": 1, "reqd": 1 } @@ -72,7 +72,7 @@ "link_fieldname": "scheduled_job_type" } ], - "modified": "2019-09-27 12:19:23.259989", + "modified": "2019-12-09 11:10:21.259929", "modified_by": "Administrator", "module": "Core", "name": "Scheduled Job Type", From d3fecebd833535fdea1fa21245cbaf84e3b08e39 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 9 Dec 2019 11:28:44 +0530 Subject: [PATCH 41/56] feat: summary tag should have cursor pointer --- frappe/public/less/common.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frappe/public/less/common.less b/frappe/public/less/common.less index 0f49c43de1..d6dc56cf6c 100644 --- a/frappe/public/less/common.less +++ b/frappe/public/less/common.less @@ -28,6 +28,10 @@ p { margin: 10px 0; } +details > summary { + cursor: pointer; +} + .text-color { color: @text-color !important; } From 2477d5eb373ea52b2018b1e64fbf7f3d5f8ec9bc Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 6 Dec 2019 17:35:13 +0530 Subject: [PATCH 42/56] fix: padding for progress bar in dashboard --- frappe/public/less/form.less | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/frappe/public/less/form.less b/frappe/public/less/form.less index 9c40131067..37570a0073 100644 --- a/frappe/public/less/form.less +++ b/frappe/public/less/form.less @@ -239,6 +239,18 @@ .progress-area { padding-top: 15px; padding-bottom: 15px; + + .progress-chart { + padding-top: 20px; + } + + .progress { + margin-bottom: 5px; + } + + .progress-message { + margin-top: 0px; + } } .form-links { From 7328abe30d7e6bec6e4c5fd694922d649eea2dc2 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 9 Dec 2019 18:19:02 +0530 Subject: [PATCH 43/56] fix: make padding a bit more consistent --- frappe/public/less/form.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/less/form.less b/frappe/public/less/form.less index 37570a0073..9a0eec46ea 100644 --- a/frappe/public/less/form.less +++ b/frappe/public/less/form.less @@ -241,7 +241,7 @@ padding-bottom: 15px; .progress-chart { - padding-top: 20px; + padding-top: 15px; } .progress { From 3bd310b2e1f02dffdbef4eb42819148287b75205 Mon Sep 17 00:00:00 2001 From: prssanna Date: Mon, 9 Dec 2019 18:56:40 +0530 Subject: [PATCH 44/56] fix(tests): add UI test for next previous document navigation --- cypress/integration/form.js | 27 +++++++++++++++++++++++++-- frappe/tests/ui_test_helpers.py | 18 ++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/cypress/integration/form.js b/cypress/integration/form.js index b7ddd6ecb7..5a8b85d19e 100644 --- a/cypress/integration/form.js +++ b/cypress/integration/form.js @@ -1,9 +1,15 @@ context('Form', () => { - before(() => { + beforeEach(() => { cy.login(); cy.visit('/desk'); }); - + before(() => { + cy.login(); + cy.visit('/desk'); + cy.window().its('frappe').then(frappe => { + frappe.call("frappe.tests.ui_test_helpers.create_contact_records"); + }); + }); it('create a new form', () => { cy.visit('/desk#Form/ToDo/New ToDo 1'); cy.fill_field('description', 'this is a test todo', 'Text Editor').blur(); @@ -13,4 +19,21 @@ context('Form', () => { cy.location('hash').should('eq', '#List/ToDo/List'); cy.get('.list-row').should('contain', 'this is a test todo'); }); + it('navigates between documents with child table list filters applied', () => { + cy.visit('/desk#List/Contact'); + cy.get('.tag-filters-area .btn:contains("Add Filter")').click(); + cy.get('.fieldname-select-area').should('exist'); + cy.get('.fieldname-select-area input').type('Number{enter}', { force: true }); + cy.get('.filter-field .input-with-feedback.form-control').type('123', { force: true }); + cy.get('.filter-box .btn:contains("Apply")').click({ force: true }); + cy.visit('/desk#Form/Contact/Test Form Contact 3'); + cy.get('.prev-doc').click(); + cy.get('.msgprint-dialog .modal-body').contains('No further records').should('be.visible'); + cy.get('.modal-backdrop').click(); + cy.get('.next-doc').click(); + cy.contains('Test Form Contact 2').should('not.exist'); + cy.get('.page-title .title-text').should('contain', 'Test Form Contact 1'); + cy.visit('/desk#List/Contact'); + cy.get('.clear-filters.btn').click(); + }); }); diff --git a/frappe/tests/ui_test_helpers.py b/frappe/tests/ui_test_helpers.py index 8eec644115..3a58fbc379 100644 --- a/frappe/tests/ui_test_helpers.py +++ b/frappe/tests/ui_test_helpers.py @@ -73,4 +73,22 @@ def create_contact_phone_nos_records(): doc.first_name = 'Test Contact' for index in range(1000): doc.append('phone_nos', {'phone': '123456{}'.format(index)}) + doc.insert() + +@frappe.whitelist() +def create_contact_records(): + if frappe.db.get_all('Contact', {'first_name': 'Test Form Contact 1'}): + return + + insert_contact('Test Form Contact 1', '12345') + insert_contact('Test Form Contact 2', '54321') + insert_contact('Test Form Contact 3', '12345') + + +def insert_contact(first_name, phone_number): + doc = frappe.get_doc({ + 'doctype': 'Contact', + 'first_name': first_name + }) + doc.append('phone_nos', {'phone': phone_number}) doc.insert() \ No newline at end of file From d169f41a54520ab71d9fe07d17a4f6949fe957db Mon Sep 17 00:00:00 2001 From: Chinmay Pai Date: Mon, 9 Dec 2019 19:25:43 +0530 Subject: [PATCH 45/56] fix(email): try to encode email part to utf-8 (#8965) fixes issues with class objects other than str Traceback (most recent call last): File "/home/frappe/frappe-bench/apps/frappe/frappe/email/doctype/email_account/email_account.py", line 281, in receive communication = self.insert_communication(msg, args=args) File "/home/frappe/frappe-bench/apps/frappe/frappe/email/doctype/email_account/email_account.py", line 338, in insert_communication email = Email(raw) File "/home/frappe/frappe-bench/apps/frappe/frappe/email/receive.py", line 372, in __init__ self.parse() File "/home/frappe/frappe-bench/apps/frappe/frappe/email/receive.py", line 393, in parse self.process_part(part) File "/home/frappe/frappe-bench/apps/frappe/frappe/email/receive.py", line 445, in process_part self.text_content += self.get_payload(part) File "/home/frappe/frappe-bench/apps/frappe/frappe/email/receive.py", line 489, in get_payload charset = self.get_charset(part) File "/home/frappe/frappe-bench/apps/frappe/frappe/email/receive.py", line 484, in get_charset charset = chardet.detect(frappe.safe_encode(part))['encoding'] File "/home/frappe/frappe-bench/env/lib/python3.6/site-packages/chardet/__init__.py", line 34, in detect '{0}'.format(type(byte_str))) TypeError: Expected object of type bytes or bytearray, got: Signed-off-by: Chinmay D. Pai --- frappe/email/receive.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frappe/email/receive.py b/frappe/email/receive.py index bccad1fec5..e5c8457b4e 100644 --- a/frappe/email/receive.py +++ b/frappe/email/receive.py @@ -456,9 +456,9 @@ class Email: def show_attached_email_headers_in_content(self, part): # get the multipart/alternative message try: - from html import escape # python 3.x + from html import escape # python 3.x except ImportError: - from cgi import escape # python 2.x + from cgi import escape # python 2.x message = list(part.walk())[1] headers = [] @@ -480,7 +480,7 @@ class Email: """Detect chartset.""" charset = part.get_content_charset() if not charset: - charset = chardet.detect(frappe.safe_encode(part))['encoding'] + charset = chardet.detect(cstr(part))['encoding'] return charset @@ -514,7 +514,7 @@ class Email: 'fcontent': fcontent, }) - cid = (part.get("Content-Id") or "").strip("><") + cid = (cstr(part.get("Content-Id")) or "").strip("><") if cid: self.cid_map[fname] = cid From 710947c66c65ffa1aef35783acab137f861ea01f Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 9 Dec 2019 20:47:06 +0530 Subject: [PATCH 46/56] fix(autocomplete): parse options (#8998) * fix(autocomplete): parse options * fix: add ignore validation flag * fix: minor change --- .../js/frappe/form/controls/autocomplete.js | 22 +++++++++++++------ .../public/js/frappe/form/sidebar/review.js | 1 + 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/frappe/public/js/frappe/form/controls/autocomplete.js b/frappe/public/js/frappe/form/controls/autocomplete.js index 026cdbec25..0b31776caf 100644 --- a/frappe/public/js/frappe/form/controls/autocomplete.js +++ b/frappe/public/js/frappe/form/controls/autocomplete.js @@ -10,13 +10,7 @@ frappe.ui.form.ControlAutocomplete = frappe.ui.form.ControlData.extend({ set_options() { if (this.df.options) { let options = this.df.options || []; - if (typeof options === 'string') { - options = options.split('\n'); - } - if (typeof options[0] === 'string') { - options = options.map(o => ({ label: o, value: o })); - } - this._data = options; + this._data = this.parse_options(options); } }, @@ -100,6 +94,9 @@ frappe.ui.form.ControlAutocomplete = frappe.ui.form.ControlData.extend({ }, validate(value) { + if (this.df.ignore_validation) { + return value || ''; + } let valid_values = this.awesomplete._list.map(d => d.value); if (!valid_values.length) { return value; @@ -111,11 +108,22 @@ frappe.ui.form.ControlAutocomplete = frappe.ui.form.ControlData.extend({ } }, + parse_options(options) { + if (typeof options === 'string') { + options = options.split('\n'); + } + if (typeof options[0] === 'string') { + options = options.map(o => ({ label: o, value: o })); + } + return options; + }, + get_data() { return this._data || []; }, set_data(data) { + data = this.parse_options(data); if (this.awesomplete) { this.awesomplete.list = data; } diff --git a/frappe/public/js/frappe/form/sidebar/review.js b/frappe/public/js/frappe/form/sidebar/review.js index f4b8069a6e..e187ca4693 100644 --- a/frappe/public/js/frappe/form/sidebar/review.js +++ b/frappe/public/js/frappe/form/sidebar/review.js @@ -81,6 +81,7 @@ frappe.ui.form.Review = class Review { label: __('To User'), reqd: 1, options: user_options, + ignore_validation: 1, description: __('Only users involved in the document are listed') }, { fieldname: 'review_type', From deaa865fa89f0cfcf835e77bc2bfe449a1c5ac01 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Tue, 10 Dec 2019 09:14:40 +0530 Subject: [PATCH 47/56] fix(patch): auto commit on more than 10000 writes fixes issue where patch fails with: frappe.exceptions.ValidationError: Too many writes in one request. Please send smaller requests Signed-off-by: Chinmay D. Pai --- .../v11_0/make_all_prepared_report_attachments_private.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frappe/patches/v11_0/make_all_prepared_report_attachments_private.py b/frappe/patches/v11_0/make_all_prepared_report_attachments_private.py index dd212d157e..f7b9e476a9 100644 --- a/frappe/patches/v11_0/make_all_prepared_report_attachments_private.py +++ b/frappe/patches/v11_0/make_all_prepared_report_attachments_private.py @@ -3,6 +3,9 @@ import frappe def execute(): + if frappe.db.count("File", filters={"attached_to_doctype": "Prepared Report", "is_private": 0}) > 10000: + frappe.db.auto_commit_on_many_writes = True + files = frappe.get_all("File", fields=["name", "attached_to_name"], filters={"attached_to_doctype": "Prepared Report", "is_private": 0}) for file_dict in files: # For some reason Prepared Report doc might not exist, check if it exists first @@ -17,3 +20,7 @@ def execute(): else: # If Prepared Report doc doesn't exist then the file doc is useless. Delete it. frappe.delete_doc("File", file_dict.name) + + if frappe.db.auto_commit_on_many_writes: + frappe.db.auto_commit_on_many_writes = False + From 37dba69db09171fde3e697a3557084af5b422e4e Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 10 Dec 2019 11:42:25 +0530 Subject: [PATCH 48/56] fix: permission error --- .../doctype/global_search_settings/global_search_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/desk/doctype/global_search_settings/global_search_settings.py b/frappe/desk/doctype/global_search_settings/global_search_settings.py index 1cdbf11224..85c9687ab3 100644 --- a/frappe/desk/doctype/global_search_settings/global_search_settings.py +++ b/frappe/desk/doctype/global_search_settings/global_search_settings.py @@ -61,7 +61,7 @@ def update_global_search_doctypes(): if search_doctypes.get(domain): global_search_doctypes.extend(search_doctypes.get(domain)) - doctype_list = set([dt.name for dt in frappe.get_list("DocType")]) + doctype_list = set([dt.name for dt in frappe.get_all("DocType")]) allowed_in_global_search = [] for dt in global_search_doctypes: From a87ca0a6a97e376688de4c7cd423f8d21c7ed7f3 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 10 Dec 2019 15:19:46 +0530 Subject: [PATCH 49/56] fix(scheduler): import update site config --- frappe/utils/scheduler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/utils/scheduler.py b/frappe/utils/scheduler.py index 7c0a642cb3..708bec887f 100755 --- a/frappe/utils/scheduler.py +++ b/frappe/utils/scheduler.py @@ -14,6 +14,7 @@ import frappe, os, time import schedule from frappe.utils import now_datetime, get_datetime from frappe.utils import get_sites +from frappe.installer import update_site_config from frappe.core.doctype.user.user import STANDARD_USERS from frappe.utils.background_jobs import get_jobs From b0622255d43a97500e0088d583159ae30e01e210 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 10 Dec 2019 15:50:26 +0530 Subject: [PATCH 50/56] revert: changes in currency formatting (#8996) * fix: reverted currency formatting * ux: added description for currency's number format * It doesn't have any effect on how the currency is formatted. * fix: revert json changes in currency master --- frappe/geo/doctype/currency/currency.js | 5 ----- frappe/public/js/frappe/form/controls/currency.js | 5 ----- frappe/public/js/frappe/form/controls/float.js | 9 +++------ frappe/public/js/frappe/utils/number_format.js | 5 +---- 4 files changed, 4 insertions(+), 20 deletions(-) diff --git a/frappe/geo/doctype/currency/currency.js b/frappe/geo/doctype/currency/currency.js index 1bc9865999..af2d6ebc4e 100644 --- a/frappe/geo/doctype/currency/currency.js +++ b/frappe/geo/doctype/currency/currency.js @@ -7,10 +7,5 @@ frappe.ui.form.on('Currency', { if(!frm.doc.enabled) { frm.set_intro(__("This Currency is disabled. Enable to use in transactions")); } - }, - - after_save(frm) { - if (frm.doc.enabled) - locals[':Currency'][frm.doc.name] = Object.assign(frm.doc, { doctype: ':Currency' }); } }); diff --git a/frappe/public/js/frappe/form/controls/currency.js b/frappe/public/js/frappe/form/controls/currency.js index 4d6b37aede..5a50ad9de6 100644 --- a/frappe/public/js/frappe/form/controls/currency.js +++ b/frappe/public/js/frappe/form/controls/currency.js @@ -4,11 +4,6 @@ frappe.ui.form.ControlCurrency = frappe.ui.form.ControlFloat.extend({ return isNaN(parseFloat(value)) ? "" : formatted_value; }, - get_number_format: function() { - var currency = frappe.meta.get_field_currency(this.df, this.get_doc()); - return get_number_format(currency); - }, - get_precision: function() { // always round based on field precision or currency's precision // this method is also called in this.parse() diff --git a/frappe/public/js/frappe/form/controls/float.js b/frappe/public/js/frappe/form/controls/float.js index 308d970f6e..027cfebc2a 100644 --- a/frappe/public/js/frappe/form/controls/float.js +++ b/frappe/public/js/frappe/form/controls/float.js @@ -1,10 +1,7 @@ frappe.ui.form.ControlFloat = frappe.ui.form.ControlInt.extend({ parse: function(value) { value = this.eval_expression(value); - return isNaN(parseFloat(value)) ? null : flt(value, this.get_precision(), - // While parsing currency, get_number_format passes currency's number_format - // In case of parsing float, it passes global number_format - this.get_number_format()); + return isNaN(parseFloat(value)) ? null : flt(value, this.get_precision()); }, format_for_input: function(value) { @@ -17,8 +14,8 @@ frappe.ui.form.ControlFloat = frappe.ui.form.ControlInt.extend({ }, get_number_format: function() { - // In case of 'Float' field currency's number_format shouldn't be used for formatting - return get_number_format(); + var currency = frappe.meta.get_field_currency(this.df, this.get_doc()); + return get_number_format(currency); }, get_precision: function() { diff --git a/frappe/public/js/frappe/utils/number_format.js b/frappe/public/js/frappe/utils/number_format.js index b8a18610bd..57022b22c5 100644 --- a/frappe/public/js/frappe/utils/number_format.js +++ b/frappe/public/js/frappe/utils/number_format.js @@ -144,10 +144,7 @@ function get_currency_symbol(currency) { } function get_number_format(currency) { - let format = null; - if (currency) format = frappe.model.get_value(":Currency", currency, "number_format"); - - return format || (frappe.boot && frappe.boot.sysdefaults && frappe.boot.sysdefaults.number_format) || "#,###.##"; + return (frappe.boot && frappe.boot.sysdefaults && frappe.boot.sysdefaults.number_format) || "#,###.##"; } function get_number_format_info(format) { From b3b70f3b7fcec0e7ac4e17c79569441c8fed3a50 Mon Sep 17 00:00:00 2001 From: Shridhar Date: Tue, 10 Dec 2019 16:51:41 +0530 Subject: [PATCH 51/56] Bug fixed ID value should not be set to 0 by default --- frappe/model/db_query.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 64e242d6af..f9016d7fcf 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -501,6 +501,10 @@ class DatabaseQuery(object): value = f.value or "''" fallback = "''" + elif f.fieldname == 'name': + value = f.value or "''" + fallback = "''" + else: value = flt(f.value) fallback = 0 From 90f89a754a9bdcd4782d5b09b717a4c6211d25cc Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Wed, 11 Dec 2019 21:28:59 +0530 Subject: [PATCH 52/56] fix(Leaderboard): Leaderboard timespan options (#8953) * fix: change leaderboard timespan options and add select from date option * fix: leaderboard timespan options grouping and add quarter_start and quarter_end to datetime.js * fix: codacy * fix: add last month --- frappe/desk/page/leaderboard/leaderboard.css | 8 +++ frappe/desk/page/leaderboard/leaderboard.js | 62 +++++++++++++++----- frappe/public/js/frappe/utils/datetime.js | 8 +++ 3 files changed, 64 insertions(+), 14 deletions(-) diff --git a/frappe/desk/page/leaderboard/leaderboard.css b/frappe/desk/page/leaderboard/leaderboard.css index dbe9cca5b8..a3cb4d09c4 100644 --- a/frappe/desk/page/leaderboard/leaderboard.css +++ b/frappe/desk/page/leaderboard/leaderboard.css @@ -19,6 +19,14 @@ background: #f0f4f7; } +.from-date-field .clearfix{ + display: none; +} + +.from-date-field { + margin-left: 10px; +} + .select-time:focus, .select-doctype:focus, .select-filter:focus, .select-sort:focus { background: #f0f4f7; } diff --git a/frappe/desk/page/leaderboard/leaderboard.js b/frappe/desk/page/leaderboard/leaderboard.js index 3e4c36add0..c64d2dcb4f 100644 --- a/frappe/desk/page/leaderboard/leaderboard.js +++ b/frappe/desk/page/leaderboard/leaderboard.js @@ -41,7 +41,11 @@ class Leaderboard { return field; }); } - this.timespans = ["Week", "Month", "Quarter", "Year", "All Time"]; + this.timespans = [ + "This Week", "This Month", "This Quarter", "This Year", + "Last Week", "Last Month", "Last Quarter", "Last Year", + "All Time", "Select From Date" + ]; // for saving current selected filters const _initial_doctype = frappe.get_route()[1] || this.doctypes[0]; @@ -103,7 +107,8 @@ class Leaderboard { this.timespans.map(d => { return {"label": __(d), value: d }; }) - ); + ); + this.create_from_date_field(); this.type_select = this.page.add_select(__("Field"), this.options.selected_filter.map(d => { @@ -113,7 +118,12 @@ class Leaderboard { this.timespan_select.on("change", (e) => { this.options.selected_timespan = e.currentTarget.value; - this.make_request(); + if (this.options.selected_timespan === 'Select From Date') { + this.from_date_field.show(); + } else { + this.from_date_field.hide(); + this.make_request(); + } }); this.type_select.on("change", (e) => { @@ -122,6 +132,28 @@ class Leaderboard { }); } + create_from_date_field() { + let timespan_field = $(this.parent).find(`.frappe-control[data-original-title='Timespan']`); + this.from_date_field = $(`
`).insertAfter(timespan_field).hide(); + + let date_field = frappe.ui.form.make_control({ + df: { + fieldtype: 'Date', + fieldname: 'selected_from_date', + placeholder: frappe.datetime.month_start(), + default: frappe.datetime.month_start(), + input_class: 'input-sm', + reqd: 1, + change: () => { + this.selected_from_date = date_field.get_value(); + if (this.selected_from_date) this.make_request(); + } + }, + parent: $(this.parent).find('.from-date-field'), + render_input: 1 + }); + } + render_selected_doctype() { this.$sidebar_list.on("click", "li", (e)=> { @@ -207,7 +239,6 @@ class Leaderboard { this.leaderboard_config[this.options.selected_doctype].method, { 'from_date': this.get_from_date(), - 'timespan': this.options.selected_timespan, 'company': this.options.selected_company, 'field': this.options.selected_filter_item, 'limit': this.leaderboard_limit, @@ -360,17 +391,20 @@ class Leaderboard { get_from_date() { let timespan = this.options.selected_timespan.toLowerCase(); let current_date = frappe.datetime.now_date(); - let date = ''; - if (timespan === "month") { - date = frappe.datetime.add_months(current_date, -1); - } else if (timespan === "quarter") { - date = frappe.datetime.add_months(current_date, -3); - } else if (timespan === "year") { - date = frappe.datetime.add_months(current_date, -12); - } else if (timespan === "week") { - date = frappe.datetime.add_days(current_date, -7); + let get_from_date = { + "this week": frappe.datetime.week_start(), + "this month": frappe.datetime.month_start(), + "this quarter": frappe.datetime.quarter_start(), + "this year": frappe.datetime.year_start(), + "last week": frappe.datetime.add_days(current_date, -7), + "last month": frappe.datetime.add_months(current_date, -1), + "last quarter": frappe.datetime.add_months(current_date, -3), + "last year": frappe.datetime.add_months(current_date, -12), + "all time": "", + "select from date": this.selected_from_date || frappe.datetime.month_start() } - return date; + + return get_from_date[timespan]; } } diff --git a/frappe/public/js/frappe/utils/datetime.js b/frappe/public/js/frappe/utils/datetime.js index 8481fec5c5..12badc5353 100644 --- a/frappe/public/js/frappe/utils/datetime.js +++ b/frappe/public/js/frappe/utils/datetime.js @@ -90,6 +90,14 @@ $.extend(frappe.datetime, { return moment().endOf("month").format(); }, + quarter_start: function() { + return moment().startOf("quarter").format(); + }, + + quarter_end: function() { + return moment().endOf("quarter").format(); + }, + year_start: function(){ return moment().startOf("year").format(); }, From eeabcb815501270733c52b343e0ac03b2b05c0ad Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 12 Dec 2019 11:51:20 +0530 Subject: [PATCH 53/56] fix: height for null state on list view (#8997) * fix: height for null state on list view * Update list.less --- frappe/public/less/list.less | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frappe/public/less/list.less b/frappe/public/less/list.less index bd6ea7e9d0..ea292daca2 100644 --- a/frappe/public/less/list.less +++ b/frappe/public/less/list.less @@ -10,7 +10,8 @@ // To compensate for percieved centering .null-state { - height: 12em !important; + height: 15rem !important; + max-height: 150px; width: auto; } @@ -616,4 +617,4 @@ input.list-check-all, input.list-row-checkbox { .file-title { margin-top: 5px; -} \ No newline at end of file +} From 8a11fb57cd2a56c9ff28ddcc38dfeb5de019c7dd Mon Sep 17 00:00:00 2001 From: Oshosanya Michael Date: Thu, 12 Dec 2019 08:47:07 +0100 Subject: [PATCH 54/56] feat: Allow full database config on creating site (#8874) * Allow setting db port on install * Allow setting db port on install * Allow full db config on creating site * bug fix * bug fix * Escape character * Update frappe/commands/site.py allow only integer argument Co-Authored-By: Chinmay Pai * Update frappe/database/db_manager.py No need to escape integer variable Co-Authored-By: Chinmay Pai * cast port to integer to surpress pymysql error * cast port to string to surpress pymysql error * Removed type casting --- frappe/commands/site.py | 20 +++++++++++--------- frappe/database/db_manager.py | 6 ++++-- frappe/installer.py | 18 ++++++++++++------ 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/frappe/commands/site.py b/frappe/commands/site.py index e28fd36346..89e9ab7f34 100755 --- a/frappe/commands/site.py +++ b/frappe/commands/site.py @@ -13,6 +13,8 @@ from six import text_type @click.argument('site') @click.option('--db-name', help='Database name') @click.option('--db-type', default='mariadb', type=click.Choice(['mariadb', 'postgres']), help='Optional "postgres" or "mariadb". Default is "mariadb"') +@click.option('--db-host', help='Database Host') +@click.option('--db-port', type=int, help='Database Port') @click.option('--mariadb-root-username', default='root', help='Root username for MariaDB') @click.option('--mariadb-root-password', help='Root password for MariaDB') @click.option('--admin-password', help='Administrator password for new site', default=None) @@ -21,22 +23,22 @@ from six import text_type @click.option('--source_sql', help='Initiate database with a SQL file') @click.option('--install-app', multiple=True, help='Install app after installation') def new_site(site, mariadb_root_username=None, mariadb_root_password=None, admin_password=None, - verbose=False, install_apps=None, source_sql=None, force=None, install_app=None, - db_name=None, db_type=None): + verbose=False, install_apps=None, source_sql=None, force=None, install_app=None, + db_name=None, db_type=None, db_host=None, db_port=None): "Create a new site" frappe.init(site=site, new_site=True) _new_site(db_name, site, mariadb_root_username=mariadb_root_username, - mariadb_root_password=mariadb_root_password, admin_password=admin_password, - verbose=verbose, install_apps=install_app, source_sql=source_sql, force=force, - db_type=db_type) + mariadb_root_password=mariadb_root_password, admin_password=admin_password, + verbose=verbose, install_apps=install_app, source_sql=source_sql, force=force, + db_type=db_type, db_host=db_host, db_port=db_port) if len(frappe.utils.get_sites()) == 1: use(site) def _new_site(db_name, site, mariadb_root_username=None, mariadb_root_password=None, - admin_password=None, verbose=False, install_apps=None, source_sql=None, force=False, - reinstall=False, db_type=None): + admin_password=None, verbose=False, install_apps=None, source_sql=None, force=False, + reinstall=False, db_type=None, db_host=None, db_port=None): """Install a new Frappe site""" if not force and os.path.exists(site): @@ -65,8 +67,8 @@ def _new_site(db_name, site, mariadb_root_username=None, mariadb_root_password=N installing = touch_file(get_site_path('locks', 'installing.lock')) install_db(root_login=mariadb_root_username, root_password=mariadb_root_password, - db_name=db_name, admin_password=admin_password, verbose=verbose, - source_sql=source_sql, force=force, reinstall=reinstall, db_type=db_type) + db_name=db_name, admin_password=admin_password, verbose=verbose, + source_sql=source_sql, force=force, reinstall=reinstall, db_type=db_type, db_host=db_host, db_port=db_port) apps_to_install = ['frappe'] + (frappe.conf.get("install_apps") or []) + (list(install_apps) or []) for app in apps_to_install: diff --git a/frappe/database/db_manager.py b/frappe/database/db_manager.py index 0447f97273..80236b2dc2 100644 --- a/frappe/database/db_manager.py +++ b/frappe/database/db_manager.py @@ -80,12 +80,14 @@ class DbManager: if pipe: print('Creating Database...') - command = '{pipe} mysql -u {user} -p{password} -h{host} {target} {source}'.format( + command = '{pipe} mysql -u {user} -p{password} -h{host} ' + ('-P{port}' if frappe.db.port else '') + ' {target} {source}' + command = command.format( pipe=pipe, user=esc(user), password=esc(password), host=esc(frappe.db.host), target=esc(target), - source=source + source=source, + port=frappe.db.port ) os.system(command) diff --git a/frappe/installer.py b/frappe/installer.py index f691a6cb22..4b07ab8ce8 100755 --- a/frappe/installer.py +++ b/frappe/installer.py @@ -21,13 +21,13 @@ from frappe.database import setup_database from frappe.core.doctype.scheduled_job_type.scheduled_job_type import sync_jobs def install_db(root_login="root", root_password=None, db_name=None, source_sql=None, - admin_password=None, verbose=True, force=0, site_config=None, reinstall=False, - db_type=None): + admin_password=None, verbose=True, force=0, site_config=None, reinstall=False, + db_type=None, db_host=None, db_port=None): if not db_type: db_type = frappe.conf.db_type or 'mariadb' - make_conf(db_name, site_config=site_config, db_type=db_type) + make_conf(db_name, site_config=site_config, db_type=db_type, db_host=db_host, db_port=db_port) frappe.flags.in_install_db = True frappe.flags.root_login = root_login @@ -191,14 +191,14 @@ def init_singles(): doc.flags.ignore_validate=True doc.save() -def make_conf(db_name=None, db_password=None, site_config=None, db_type=None): +def make_conf(db_name=None, db_password=None, site_config=None, db_type=None, db_host=None, db_port=None): site = frappe.local.site - make_site_config(db_name, db_password, site_config, db_type=db_type) + make_site_config(db_name, db_password, site_config, db_type=db_type, db_host=db_host, db_port=db_port) sites_path = frappe.local.sites_path frappe.destroy() frappe.init(site, sites_path=sites_path) -def make_site_config(db_name=None, db_password=None, site_config=None, db_type=None): +def make_site_config(db_name=None, db_password=None, site_config=None, db_type=None, db_host=None, db_port=None): frappe.create_folder(os.path.join(frappe.local.site_path)) site_file = get_site_config_path() @@ -209,6 +209,12 @@ def make_site_config(db_name=None, db_password=None, site_config=None, db_type=N if db_type: site_config['db_type'] = db_type + if db_host: + site_config['db_host'] = db_host + + if db_port: + site_config['db_port'] = db_port + with open(site_file, "w") as f: f.write(json.dumps(site_config, indent=1, sort_keys=True)) From 6ab38c2af580cd4e67272b5b7827875abc8ed841 Mon Sep 17 00:00:00 2001 From: Khushal Trivedi Date: Thu, 12 Dec 2019 19:24:17 +0530 Subject: [PATCH 55/56] fix: link field search taking trailing and leading spaces (#9031) --- frappe/desk/search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/desk/search.py b/frappe/desk/search.py index 7bcfe646ab..c70b650945 100644 --- a/frappe/desk/search.py +++ b/frappe/desk/search.py @@ -50,7 +50,7 @@ def sanitize_searchfield(searchfield): # this is called by the Link Field @frappe.whitelist() def search_link(doctype, txt, query=None, filters=None, page_length=20, searchfield=None, reference_doctype=None, ignore_user_permissions=False): - search_widget(doctype, txt, query, searchfield=searchfield, page_length=page_length, filters=filters, reference_doctype=reference_doctype, ignore_user_permissions=ignore_user_permissions) + search_widget(doctype, txt.strip(), query, searchfield=searchfield, page_length=page_length, filters=filters, reference_doctype=reference_doctype, ignore_user_permissions=ignore_user_permissions) frappe.response['results'] = build_for_autosuggest(frappe.response["values"]) del frappe.response["values"] From d1c0579c705b9d8713fc1237db49baf7d4b787bb Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Thu, 12 Dec 2019 20:00:30 +0530 Subject: [PATCH 56/56] fix(file): Public-private issue (#9029) * fix: Pass file's is_private for doc creation * fix: File is_private and file_url mismatch * fix: Check if file_url exists * fix: Validation check * fix(postgres): Convert is_private to int * fix: File path for content_hash --- frappe/core/doctype/communication/email.py | 6 ++-- frappe/core/doctype/file/file.py | 32 ++++++++++++++-------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/frappe/core/doctype/communication/email.py b/frappe/core/doctype/communication/email.py index 361030d07a..1848136bee 100755 --- a/frappe/core/doctype/communication/email.py +++ b/frappe/core/doctype/communication/email.py @@ -398,7 +398,7 @@ def get_bcc(doc, recipients=None, fetched_from_email_account=False): return bcc def add_attachments(name, attachments): - '''Add attachments to the given Communiction''' + '''Add attachments to the given Communication''' # loop through attachments for a in attachments: if isinstance(a, string_types): @@ -411,7 +411,9 @@ def add_attachments(name, attachments): "file_url": attach.file_url, "attached_to_doctype": "Communication", "attached_to_name": name, - "folder": "Home/Attachments"}) + "folder": "Home/Attachments", + "is_private": attach.is_private + }) _file.save(ignore_permissions=True) def filter_email_list(doc, email_list, exclude, is_cc=False, is_bcc=False): diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index ee139000e1..041b8c3011 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -89,8 +89,9 @@ class File(Document): def validate(self): if self.is_new(): + self.set_is_private() + self.set_file_name() self.validate_duplicate_entry() - self.validate_file_name() self.validate_folder() if not self.file_url and not self.flags.ignore_file_validate: @@ -133,6 +134,9 @@ class File(Document): frappe.db.set_value(self.attached_to_doctype, self.attached_to_name, self.attached_to_field, self.file_url) + if self.file_url and (self.is_private != self.file_url.startswith('/private')): + frappe.throw(_('Invalid file URL. Please contact System Administrator.')) + def set_folder_name(self): """Make parent folders if not exists based on reference doctype and name""" if self.attached_to_doctype and not self.folder: @@ -157,9 +161,11 @@ class File(Document): def validate_duplicate_entry(self): if not self.flags.ignore_duplicate_entry_error and not self.is_folder: - # check duplicate name + if not self.content_hash: + self.generate_content_hash() - # check duplicate assignement + # check duplicate name + # check duplicate assignment filters = { 'content_hash': self.content_hash, 'is_private': self.is_private, @@ -184,21 +190,20 @@ class File(Document): else: self.file_url = duplicate_file.file_url - def validate_file_name(self): + def set_file_name(self): if not self.file_name and self.file_url: self.file_name = self.file_url.split('/')[-1] def generate_content_hash(self): - if self.content_hash or not self.file_url: + if self.content_hash or not self.file_url or self.file_url.startswith('http'): return - if self.file_url.startswith("/files/"): - try: - with open(get_files_path(self.file_name.lstrip("/")), "rb") as f: - self.content_hash = get_content_hash(f.read()) - except IOError: - frappe.msgprint(_("File {0} does not exist").format(self.file_url)) - raise + try: + with open(get_files_path(self.file_name.lstrip("/"), is_private=self.is_private), "rb") as f: + self.content_hash = get_content_hash(f.read()) + except IOError: + frappe.msgprint(_("File {0} does not exist").format(self.file_url)) + raise def on_trash(self): if self.is_home_folder or self.is_attachments_folder: @@ -563,6 +568,9 @@ class File(Document): except frappe.DoesNotExistError: frappe.clear_messages() + def set_is_private(self): + if self.file_url: + self.is_private = cint(self.file_url.startswith('/private')) def on_doctype_update(): frappe.db.add_index("File", ["attached_to_doctype", "attached_to_name"])