From 4aec95809f7ed372048e4bc1f9e38327847c0cf4 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Sat, 30 Nov 2019 19:07:27 +0530 Subject: [PATCH 01/61] fix: ignore args if Tag Link --- frappe/public/js/frappe/ui/filters/filter_list.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frappe/public/js/frappe/ui/filters/filter_list.js b/frappe/public/js/frappe/ui/filters/filter_list.js index d629bd5be9..18ae1d1819 100644 --- a/frappe/public/js/frappe/ui/filters/filter_list.js +++ b/frappe/public/js/frappe/ui/filters/filter_list.js @@ -63,6 +63,10 @@ frappe.ui.FilterGroup = class { } validate_args(doctype, fieldname) { + if (doctype === "Tag Link" && fieldname === "tag") { + return true; + } + if(doctype && fieldname && !frappe.meta.has_field(doctype, fieldname) && !frappe.model.std_fields_list.includes(fieldname)) { From d7f4f865cbfc9890d50f32307f0744baf48b4c4d Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 6 Dec 2019 15:15:29 +0530 Subject: [PATCH 02/61] fix: wider msg dialog for changelog --- frappe/public/js/frappe/desk.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index 92194acdca..6d51342d82 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -466,10 +466,19 @@ frappe.Application = Class.extend({ show_change_log: function() { var me = this; - var d = frappe.msgprint( - frappe.render_template("change_log", {"change_log": frappe.boot.change_log}), - __("Updated To New Version") - ); + let change_log = frappe.boot.change_log; + + change_log.forEach(log => { + log.change_log.forEach(version_info => { + version_info[1] = version_info[1].replace(/#/, '##') + }) + }) + + var d = frappe.msgprint({ + message: frappe.render_template("change_log", {"change_log": change_log}), + title: __("Updated To New Version 🎉"), + wide: true + }); d.keep_open = true; d.custom_onhide = function() { frappe.call({ From e1754d78ef19955e465905e5bd7a46a7cab0400e Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 6 Dec 2019 15:26:33 +0530 Subject: [PATCH 03/61] feat: allow overflow scroll for msgprint body --- frappe/public/js/frappe/ui/messages.js | 9 +++++++++ frappe/public/less/common.less | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/frappe/public/js/frappe/ui/messages.js b/frappe/public/js/frappe/ui/messages.js index 08711a8237..6070acfe6e 100644 --- a/frappe/public/js/frappe/ui/messages.js +++ b/frappe/public/js/frappe/ui/messages.js @@ -207,6 +207,15 @@ frappe.msgprint = function(msg, title) { frappe.msg_dialog.wrapper.classList.add('msgprint-dialog'); } + if (data.scroll) { + frappe.msg_dialog.body.classList.add('msgprint-scroll'); + } else { + // limit modal height and allow scrolling instead + if (frappe.msg_dialog.body.classList.contains('msgprint-scroll')) { + frappe.msg_dialog.body.classList.remove('msgprint-scroll'); + } + } + if(msg_exists) { frappe.msg_dialog.msg_area.append("
"); diff --git a/frappe/public/less/common.less b/frappe/public/less/common.less index 0f49c43de1..785327e35c 100644 --- a/frappe/public/less/common.less +++ b/frappe/public/less/common.less @@ -153,6 +153,11 @@ a.badge-hover& { } } +.msgprint-scroll { + max-height: 36em; + overflow: scroll; +} + .msgprint { // margin: 15px 0px; // text-align: center; From 0ac077f43131455141156ebc09d2528bac777018 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 6 Dec 2019 15:26:52 +0530 Subject: [PATCH 04/61] feat: enable scroll for changelog dialog --- frappe/public/js/frappe/desk.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index 6d51342d82..148871e6b7 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -477,7 +477,8 @@ frappe.Application = Class.extend({ var d = frappe.msgprint({ message: frappe.render_template("change_log", {"change_log": change_log}), title: __("Updated To New Version 🎉"), - wide: true + wide: true, + scroll: true }); d.keep_open = true; d.custom_onhide = function() { From 7b07df513c71e43e01bf81d80f68e2010b8825b8 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 6 Dec 2019 15:28:34 +0530 Subject: [PATCH 05/61] style: better variable names --- frappe/public/js/frappe/desk.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index 148871e6b7..d66105b401 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -474,14 +474,14 @@ frappe.Application = Class.extend({ }) }) - var d = frappe.msgprint({ + var change_log_dialog = frappe.msgprint({ message: frappe.render_template("change_log", {"change_log": change_log}), title: __("Updated To New Version 🎉"), wide: true, scroll: true }); - d.keep_open = true; - d.custom_onhide = function() { + change_log_dialog.keep_open = true; + change_log_dialog.custom_onhide = function() { frappe.call({ "method": "frappe.utils.change_log.update_last_known_versions" }); From 6a719502144d3319768135889c7ecc1ad9fc9b26 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 6 Dec 2019 15:39:28 +0530 Subject: [PATCH 06/61] style: added comments --- frappe/public/js/frappe/desk.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index d66105b401..314a032985 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -468,9 +468,21 @@ frappe.Application = Class.extend({ var me = this; let change_log = frappe.boot.change_log; + // frappe.boot.change_log = [{ + // "change_log": [ + // [, ], + // [, ], + // ], + // "description": "ERP made simple", + // "title": "ERPNext", + // "version": "12.2.0" + // }]; + + // Iterate over changelog change_log.forEach(log => { log.change_log.forEach(version_info => { version_info[1] = version_info[1].replace(/#/, '##') + // replace all # with ## for rendering them as

}) }) From eab6cb76b851da5b581f26e9b29593f440b51290 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 6 Dec 2019 15:47:49 +0530 Subject: [PATCH 07/61] style: better comments --- frappe/public/js/frappe/ui/messages.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/ui/messages.js b/frappe/public/js/frappe/ui/messages.js index 6070acfe6e..2265203ef9 100644 --- a/frappe/public/js/frappe/ui/messages.js +++ b/frappe/public/js/frappe/ui/messages.js @@ -208,9 +208,9 @@ frappe.msgprint = function(msg, title) { } if (data.scroll) { + // limit modal height and allow scrolling instead frappe.msg_dialog.body.classList.add('msgprint-scroll'); } else { - // limit modal height and allow scrolling instead if (frappe.msg_dialog.body.classList.contains('msgprint-scroll')) { frappe.msg_dialog.body.classList.remove('msgprint-scroll'); } From f149a789f4bd7c08d752621ba5d2699b61579715 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 9 Dec 2019 10:29:45 +0530 Subject: [PATCH 08/61] style: added semi-colon --- frappe/public/js/frappe/desk.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index 314a032985..cb52406b1e 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -481,10 +481,10 @@ frappe.Application = Class.extend({ // Iterate over changelog change_log.forEach(log => { log.change_log.forEach(version_info => { - version_info[1] = version_info[1].replace(/#/, '##') + version_info[1] = version_info[1].replace(/#/, '##'); // replace all # with ## for rendering them as

- }) - }) + }); + }); var change_log_dialog = frappe.msgprint({ message: frappe.render_template("change_log", {"change_log": change_log}), From 7a98b8dca83cbf0d0272371bff27c3b8b6b40a02 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 9 Dec 2019 16:12:02 +0530 Subject: [PATCH 09/61] fix: remove illustrations from onboarding slides --- .../company_letter_head/company_letter_head.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 2f51d2e18a..e0125bad3c 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 @@ -12,9 +12,10 @@ } ], "idx": 0, - "image_src": "/assets/erpnext/images/illustrations/letterhead-onboard.png", + "image_src": "", + "is_completed": 1, "max_count": 0, - "modified": "2019-12-03 22:54:57.618989", + "modified": "2019-12-09 15:12:45.588567", "modified_by": "Administrator", "name": "Company Letter Head", "owner": "Administrator", From a7600f527ae40d6d28e2d369a2fdda8fce5e0c30 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 9 Dec 2019 16:14:23 +0530 Subject: [PATCH 10/61] fix: set the modal backdrop opacity to 1 --- frappe/public/less/desk.less | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/frappe/public/less/desk.less b/frappe/public/less/desk.less index 0a8062efa2..529272a3bc 100644 --- a/frappe/public/less/desk.less +++ b/frappe/public/less/desk.less @@ -970,8 +970,15 @@ input[type="checkbox"] { margin-left: auto; } + .modal-backdrop { + background-color: #ffffff; + } + + .modal-backdrop.in { + opacity: 1; + } + .modal-dialog { - width: 50%; height: 80%; max-width: none; } From f64604cd586b72872d3133ed97060693b3abf4d0 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 9 Dec 2019 18:52:45 +0530 Subject: [PATCH 11/61] fix: reduced modal's box-shadow and added a border --- frappe/public/less/desk.less | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frappe/public/less/desk.less b/frappe/public/less/desk.less index 529272a3bc..3c066c5d9c 100644 --- a/frappe/public/less/desk.less +++ b/frappe/public/less/desk.less @@ -970,6 +970,11 @@ input[type="checkbox"] { margin-left: auto; } + .modal-content { + border: 1px solid #d1d8dd; + box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.1); + } + .modal-backdrop { background-color: #ffffff; } From 4f477cbc944aa11b39c533bd882aa2ebebbe8085 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 10 Dec 2019 10:55:36 +0530 Subject: [PATCH 12/61] fix: Allow field of submitted doc to edit if field has allow_on_submit enabled --- .../js/frappe/views/reports/report_view.js | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js index 94c8e122c8..ff4ea56404 100644 --- a/frappe/public/js/frappe/views/reports/report_view.js +++ b/frappe/public/js/frappe/views/reports/report_view.js @@ -615,15 +615,18 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { } is_editable(df, data) { - if (!df || data.docstatus !== 0) return false; - const is_standard_field = frappe.model.std_fields_list.includes(df.fieldname); - const can_edit = !( - is_standard_field - || df.read_only - || df.hidden - || !frappe.model.can_write(this.doctype) - ); - return can_edit; + if (df + && frappe.model.can_write(this.doctype) + // not a submitted doc or field is allowed to edit after submit + && (data.docstatus !== 1 || df.allow_on_submit) + // not a cancelled doc + && data.docstatus !== 2 + && !df.read_only + && !df.hidden + // not a standard field i.e., owner, modified_by, etc. + && !frappe.model.std_fields_list.includes(df.fieldname)) + return true; + return false; } get_data(values) { From aa8f0d5c416b4736889d6cab6b5a472ecf701b61 Mon Sep 17 00:00:00 2001 From: prssanna Date: Tue, 10 Dec 2019 14:39:15 +0530 Subject: [PATCH 13/61] fix: reopen todo only when assignment rule condition is satisfied --- .../assignment_rule/assignment_rule.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/frappe/automation/doctype/assignment_rule/assignment_rule.py b/frappe/automation/doctype/assignment_rule/assignment_rule.py index b431c7c473..5286757e8a 100644 --- a/frappe/automation/doctype/assignment_rule/assignment_rule.py +++ b/frappe/automation/doctype/assignment_rule/assignment_rule.py @@ -31,12 +31,6 @@ class AssignmentRule(Document): return False - def apply_close(self, doc, assignments): - if (self.close_assignments and - self.name in [d.assignment_rule for d in assignments]): - return self.close_assignments(doc) - - return False def apply_assign(self, doc): if self.safe_eval('assign_condition', doc): @@ -225,13 +219,12 @@ def apply(doc, method=None, doctype=None, name=None): continue if not new_apply: - reopen = reopen_closed_assignment(doc) - if reopen: - break - close = assignment_rule.apply_close(doc, assignments) - if close: - break - + # only reopen if assignment rule condition is satisfied + if assignment_rule.safe_eval('assign_condition', doc): + reopen = reopen_closed_assignment(doc) + if reopen: + break + assignment_rule.close_assignments(doc) def get_assignment_rules(): return [d.document_type for d in frappe.db.get_all('Assignment Rule', fields=['document_type'], filters=dict(disabled = 0))] From 1555ef360b921dd6948d40a0807d0c046e6fcce0 Mon Sep 17 00:00:00 2001 From: prssanna Date: Tue, 10 Dec 2019 15:47:47 +0530 Subject: [PATCH 14/61] fix: reopen all todos if close condition is not satisfed --- .../doctype/assignment_rule/assignment_rule.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/frappe/automation/doctype/assignment_rule/assignment_rule.py b/frappe/automation/doctype/assignment_rule/assignment_rule.py index 5286757e8a..55792b2648 100644 --- a/frappe/automation/doctype/assignment_rule/assignment_rule.py +++ b/frappe/automation/doctype/assignment_rule/assignment_rule.py @@ -151,16 +151,17 @@ def bulk_apply(doctype, docnames): apply(None, doctype=doctype, name=name) def reopen_closed_assignment(doc): - todo = frappe.db.exists('ToDo', dict( + todo_list = frappe.db.get_all('ToDo', filters = dict( reference_type = doc.doctype, reference_name = doc.name, status = 'Closed' )) - if not todo: + if not todo_list: return False - todo = frappe.get_doc("ToDo", todo) - todo.status = 'Open' - todo.save(ignore_permissions=True) + for todo in todo_list: + todo_doc = frappe.get_doc('ToDo', todo.name) + todo_doc.status = 'Open' + todo_doc.save(ignore_permissions=True) return True def apply(doc, method=None, doctype=None, name=None): @@ -219,8 +220,8 @@ def apply(doc, method=None, doctype=None, name=None): continue if not new_apply: - # only reopen if assignment rule condition is satisfied - if assignment_rule.safe_eval('assign_condition', doc): + # only reopen if close condition is not satisfied + if not assignment_rule.safe_eval('close_condition', doc): reopen = reopen_closed_assignment(doc) if reopen: break From d6313057b39a4e11a77228f4d5baa8acadc51a42 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 11 Dec 2019 09:44:59 +0530 Subject: [PATCH 15/61] fix: Check if the type of row data is tuple --- frappe/desk/query_report.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py index 21a69f5111..7dc561193f 100644 --- a/frappe/desk/query_report.py +++ b/frappe/desk/query_report.py @@ -510,7 +510,7 @@ def has_match(row, linked_doctypes, doctype_match_filters, ref_doctype, if_owner cell_value = None if isinstance(row, dict): cell_value = row.get(idx) - elif isinstance(row, list): + elif isinstance(row, (list, tuple)): cell_value = row[idx] if dt in match_filters and cell_value not in match_filters.get(dt) and frappe.db.exists(dt, cell_value): From a8b3a6b20bed84dec5da94dccf12d0f39bc6bba7 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 11 Dec 2019 11:22:09 +0530 Subject: [PATCH 16/61] test: Add test to check if field is editable --- .../fixtures/custom_submittable_doctype.js | 51 +++++++++++++++++++ cypress/integration/report_view.js | 29 +++++++++++ cypress/support/commands.js | 28 ++++++++++ 3 files changed, 108 insertions(+) create mode 100644 cypress/fixtures/custom_submittable_doctype.js create mode 100644 cypress/integration/report_view.js diff --git a/cypress/fixtures/custom_submittable_doctype.js b/cypress/fixtures/custom_submittable_doctype.js new file mode 100644 index 0000000000..22b14db890 --- /dev/null +++ b/cypress/fixtures/custom_submittable_doctype.js @@ -0,0 +1,51 @@ +export default { + name: 'Custom Submittable Doctype', + custom: 1, + actions: [], + is_submittable: 1, + creation: '2019-12-10 06:29:07.215072', + doctype: 'DocType', + editable_grid: 1, + engine: 'InnoDB', + fields: [ + { + fieldname: 'enabled', + fieldtype: 'Check', + label: 'Enabled', + allow_on_submit: 1, + reqd: 1 + }, + { + fieldname: 'title', + fieldtype: 'Data', + label: 'title', + reqd: 1 + }, + { + fieldname: 'description', + fieldtype: 'Text Editor', + label: 'Description' + } + ], + links: [], + modified: '2019-12-10 14:40:53.127615', + modified_by: 'Administrator', + module: 'Custom', + owner: 'Administrator', + permissions: [ + { + create: 1, + delete: 1, + email: 1, + print: 1, + read: 1, + role: 'System Manager', + share: 1, + write: 1 + } + ], + quick_entry: 1, + sort_field: 'modified', + sort_order: 'ASC', + track_changes: 1 +}; \ No newline at end of file diff --git a/cypress/integration/report_view.js b/cypress/integration/report_view.js new file mode 100644 index 0000000000..d139f586f0 --- /dev/null +++ b/cypress/integration/report_view.js @@ -0,0 +1,29 @@ +import custom_submittable_doctype from '../fixtures/custom_submittable_doctype'; +const doctype_name = custom_submittable_doctype.name; + +context('Report View', () => { + before(() => { + cy.login(); + cy.visit('/desk'); + cy.insert_doc('DocType', custom_submittable_doctype, true); + cy.insert_doc(doctype_name, { + 'title': 'Doc 1', + 'description': 'Random Text', + 'enabled': 0, + // submit document + 'docstatus': 1 + }, true); + + }); + it('Field with enabled allow_on_edit should be editable.', () => { + cy.server(); + cy.route('POST', 'api/method/frappe.client.set_value').as('value-update'); + cy.visit(`/desk#List/${doctype_name}/Report`); + let cell = cy.get('.dt-row-0 > .dt-cell--col-3'); + // select the cell + cell.dblclick(); + cell.find('input[data-fieldname="enabled"]').check({force: true}); + cy.get('.dt-row-0 > .dt-cell--col-4').click(); + cy.wait('@value-update'); + }); +}); \ No newline at end of file diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 464cbbe1d5..00c7d7f8f4 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -183,3 +183,31 @@ Cypress.Commands.add('hide_dialog', () => { cy.get_open_dialog().find('.btn-modal-close').click(); cy.get('.modal:visible').should('not.exist'); }); + +Cypress.Commands.add('insert_doc', (doctype, args, ignore_duplicate) => { + return cy + .window() + .its('frappe.csrf_token') + .then(csrf_token => { + return cy + .request({ + method: 'POST', + url: `/api/resource/${doctype}`, + body: args, + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'X-Frappe-CSRF-Token': csrf_token + }, + failOnStatusCode: !ignore_duplicate + }) + .then(res => { + let status_codes = [200]; + if (ignore_duplicate) { + status_codes.push(409); + } + expect(res.status).to.be.oneOf(status_codes); + return res.body; + }); + }); +}); \ No newline at end of file From 9ce7f6c6ab28105b4cc1d5c7d5d2459f38b718a8 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 11 Dec 2019 11:32:01 +0530 Subject: [PATCH 17/61] fix: Typo --- cypress/integration/report_view.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/integration/report_view.js b/cypress/integration/report_view.js index d139f586f0..fe33d5c128 100644 --- a/cypress/integration/report_view.js +++ b/cypress/integration/report_view.js @@ -15,7 +15,7 @@ context('Report View', () => { }, true); }); - it('Field with enabled allow_on_edit should be editable.', () => { + it('Field with enabled allow_on_submit should be editable.', () => { cy.server(); cy.route('POST', 'api/method/frappe.client.set_value').as('value-update'); cy.visit(`/desk#List/${doctype_name}/Report`); From 80fd42aba29bd965bd711c4639dd74e13b0b67ff Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 12 Dec 2019 11:35:22 +0530 Subject: [PATCH 18/61] fix: Hidden columns should be hidden even after refresh --- frappe/public/js/frappe/views/reports/query_report.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index 0771306298..c7e976306d 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -452,6 +452,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { render_datatable() { let data = this.data; + let columns = this.columns.filter((col) => !col.hidden); if (this.raw_data.add_total_row) { data = data.slice(); @@ -460,10 +461,10 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { if (this.datatable) { this.datatable.options.treeView = this.tree_report; - this.datatable.refresh(data, this.columns); + this.datatable.refresh(data, columns); } else { let datatable_options = { - columns: this.columns.filter((col) => !col.hidden), + columns: columns, data: data, inlineFilters: true, treeView: this.tree_report, From f9777268b844b07bb42d727aeccd06307197c377 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Thu, 12 Dec 2019 21:40:56 +0530 Subject: [PATCH 19/61] feat: add default email account for email linking (#8833) * feat: add default email account for email linking * fix: remove unused import * fix: remove try-catch --- .../desk/page/setup_wizard/install_fixtures.py | 16 +++++++--------- frappe/patches.txt | 3 ++- frappe/patches/v12_0/setup_email_linking.py | 6 ++++++ 3 files changed, 15 insertions(+), 10 deletions(-) create mode 100644 frappe/patches/v12_0/setup_email_linking.py diff --git a/frappe/desk/page/setup_wizard/install_fixtures.py b/frappe/desk/page/setup_wizard/install_fixtures.py index bb598ab180..e93b71291f 100644 --- a/frappe/desk/page/setup_wizard/install_fixtures.py +++ b/frappe/desk/page/setup_wizard/install_fixtures.py @@ -10,6 +10,7 @@ from frappe.desk.doctype.global_search_settings.global_search_settings import up def install(): update_genders_and_salutations() update_global_search_doctypes() + setup_email_linking() @frappe.whitelist() def update_genders_and_salutations(): @@ -20,13 +21,10 @@ def update_genders_and_salutations(): for record in records: doc = frappe.new_doc(record.get("doctype")) doc.update(record) + doc.insert(ignore_permissions=True, ignore_if_duplicate=True) - try: - doc.insert(ignore_permissions=True) - except frappe.DuplicateEntryError as e: - # pass DuplicateEntryError and continue - if e.args and e.args[0]==doc.doctype and e.args[1]==doc.name: - # make sure DuplicateEntryError is for the exact same doc and not a related doc - pass - else: - raise \ No newline at end of file +def setup_email_linking(): + doc = frappe.get_doc({ + "doctype": "Email Account", + "email_id": "email_linking@example.com", + }).insert(ignore_permissions=True, ignore_if_duplicate=True) \ No newline at end of file diff --git a/frappe/patches.txt b/frappe/patches.txt index 90abc8c31d..0ca7d8b048 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -260,4 +260,5 @@ frappe.patches.v12_0.update_auto_repeat_status_and_not_submittable frappe.patches.v12_0.copy_to_parent_for_tags frappe.patches.v12_0.create_notification_settings_for_user frappe.patches.v11_0.make_all_prepared_report_attachments_private #2019-11-26 -execute:frappe.delete_doc("Test Runner") \ No newline at end of file +execute:frappe.delete_doc("Test Runner") +frappe.patches.v12_0.setup_email_linking diff --git a/frappe/patches/v12_0/setup_email_linking.py b/frappe/patches/v12_0/setup_email_linking.py new file mode 100644 index 0000000000..08f57ca5e4 --- /dev/null +++ b/frappe/patches/v12_0/setup_email_linking.py @@ -0,0 +1,6 @@ +from __future__ import unicode_literals + +from frappe.desk.page.setup_wizard.install_fixtures import setup_email_linking + +def execute(): + setup_email_linking() \ No newline at end of file From 129bc14495dc6b500b30cee282f2c522e8c10c77 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 13 Dec 2019 14:09:23 +0530 Subject: [PATCH 20/61] fix: Add a patch to fix all user's home settings --- frappe/patches.txt | 1 + .../v12_0/fix_home_settings_for_all_users.py | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 frappe/patches/v12_0/fix_home_settings_for_all_users.py diff --git a/frappe/patches.txt b/frappe/patches.txt index 0ca7d8b048..9612994168 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -262,3 +262,4 @@ frappe.patches.v12_0.create_notification_settings_for_user frappe.patches.v11_0.make_all_prepared_report_attachments_private #2019-11-26 execute:frappe.delete_doc("Test Runner") frappe.patches.v12_0.setup_email_linking +frappe.patches.v12_0.fix_home_settings_for_all_users diff --git a/frappe/patches/v12_0/fix_home_settings_for_all_users.py b/frappe/patches/v12_0/fix_home_settings_for_all_users.py new file mode 100644 index 0000000000..9bc05f8e2c --- /dev/null +++ b/frappe/patches/v12_0/fix_home_settings_for_all_users.py @@ -0,0 +1,41 @@ +import frappe +from frappe.config import get_modules_from_all_apps_for_user +import json +def execute(): + users = frappe.get_all('User', fields=['name', 'home_settings'], limit=2) + + for user in users: + + if not user.home_settings: + continue + + home_settings = json.loads(user.home_settings) + + modules_by_category = home_settings.get('modules_by_category') + if not modules_by_category: + continue + visible_modules = [] + category_to_check = [] + + for category, modules in modules_by_category.items(): + visible_modules += modules + category_to_check.append(category) + + all_modules = get_modules_from_all_apps_for_user(user.name) + all_modules = set([m.get('name') or m.get('module_name') or m.get('label') \ + for m in all_modules if m.get('category') in category_to_check]) + + hidden_modules = home_settings['hidden_modules'] + + modules_in_home_settings = set(visible_modules + hidden_modules) + + all_modules = all_modules.union(modules_in_home_settings) + + missing_modules = all_modules - modules_in_home_settings + + if missing_modules: + home_settings['hidden_modules'] += missing_modules + home_settings = json.dumps(home_settings) + frappe.set_value('User', user.name, 'home_settings', home_settings) + + frappe.cache().delete_key('home_settings') From ce7929e1e23bcd76c10b80f06ea25fc92c84b3c5 Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 13 Dec 2019 15:56:59 +0530 Subject: [PATCH 21/61] fix: grid file upload fix --- frappe/public/js/frappe/form/grid.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js index 38818b18aa..fa65e77afe 100644 --- a/frappe/public/js/frappe/form/grid.js +++ b/frappe/public/js/frappe/form/grid.js @@ -755,6 +755,7 @@ export default class Grid { } setup_allow_bulk_edit() { + let me = this; if (this.frm && this.frm.get_docfield(this.df.fieldname).allow_bulk_edit) { // download this.setup_download(); @@ -769,8 +770,7 @@ export default class Grid { var data = frappe.utils.csv_to_array(frappe.utils.get_decoded_string(file.dataurl)); // row #2 contains fieldnames; var fieldnames = data[2]; - - this.frm.clear_table(this.df.fieldname); + me.frm.clear_table(me.df.fieldname); $.each(data, (i, row) => { if (i > 6) { var blank_row = true; @@ -782,10 +782,10 @@ export default class Grid { }); if (!blank_row) { - var d = this.frm.add_child(this.df.fieldname); + var d = me.frm.add_child(me.df.fieldname); $.each(row, (ci, value) => { var fieldname = fieldnames[ci]; - var df = frappe.meta.get_docfield(this.df.options, fieldname); + var df = frappe.meta.get_docfield(me.df.options, fieldname); // convert date formatting if (df.fieldtype==="Date" && value) { @@ -802,7 +802,7 @@ export default class Grid { } }); - this.frm.refresh_field(this.df.fieldname); + me.frm.refresh_field(me.df.fieldname); frappe.msgprint({message: __('Table updated'), title: __('Success'), indicator: 'green'}); } }); From e5c1f800718a90bda34587011a3eb1dcd1830494 Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Fri, 13 Dec 2019 22:35:29 +0530 Subject: [PATCH 22/61] fix(Grid): Child table pagination fixes (#9034) * fix: don't render result if grid_rows is undefined and fix truncate_rows * fix: change page length to 50 * fix: grid pagination ui test * fix: confirm before deleting all child table rows --- cypress/integration/grid_pagination.js | 15 +++++++------ frappe/public/js/frappe/form/grid.js | 22 +++++++++++-------- .../public/js/frappe/form/grid_pagination.js | 2 +- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/cypress/integration/grid_pagination.js b/cypress/integration/grid_pagination.js index cf41e31ee6..67fdb8acf0 100644 --- a/cypress/integration/grid_pagination.js +++ b/cypress/integration/grid_pagination.js @@ -14,15 +14,15 @@ context('Grid Pagination', () => { cy.visit('/desk#Form/Contact/Test Contact'); cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table'); cy.get('@table').find('.current-page-number').should('contain', '1'); - cy.get('@table').find('.total-page-number').should('contain', '50'); - cy.get('@table').find('.grid-body .grid-row').should('have.length', 20); + cy.get('@table').find('.total-page-number').should('contain', '20'); + cy.get('@table').find('.grid-body .grid-row').should('have.length', 50); }); it('goes to the next and previous page', () => { cy.visit('/desk#Form/Contact/Test Contact'); cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table'); cy.get('@table').find('.next-page').click(); cy.get('@table').find('.current-page-number').should('contain', '2'); - cy.get('@table').find('.grid-body .grid-row').first().should('have.attr', 'data-idx', '21'); + cy.get('@table').find('.grid-body .grid-row').first().should('have.attr', 'data-idx', '51'); cy.get('@table').find('.prev-page').click(); cy.get('@table').find('.current-page-number').should('contain', '1'); cy.get('@table').find('.grid-body .grid-row').first().should('have.attr', 'data-idx', '1'); @@ -32,19 +32,20 @@ context('Grid Pagination', () => { cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table'); cy.get('@table').find('button.grid-add-row').click(); cy.get('@table').find('.grid-body .row-index').should('contain', 1001); - cy.get('@table').find('.current-page-number').should('contain', '51'); - cy.get('@table').find('.total-page-number').should('contain', '51'); + cy.get('@table').find('.current-page-number').should('contain', '21'); + cy.get('@table').find('.total-page-number').should('contain', '21'); cy.get('@table').find('.grid-body .grid-row .grid-row-check').click({force: true}); cy.get('@table').find('button.grid-remove-rows').click(); cy.get('@table').find('.grid-body .row-index').last().should('contain', 1000); - cy.get('@table').find('.current-page-number').should('contain', '50'); - cy.get('@table').find('.total-page-number').should('contain', '50'); + cy.get('@table').find('.current-page-number').should('contain', '20'); + cy.get('@table').find('.total-page-number').should('contain', '20'); }); it('deletes all rows', ()=> { cy.visit('/desk#Form/Contact/Test Contact'); cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table'); cy.get('@table').find('.grid-heading-row .grid-row-check').click({force: true}); cy.get('@table').find('button.grid-remove-all-rows').click(); + cy.get('.modal-dialog .btn-primary').contains('Yes').click(); cy.get('@table').find('.grid-body .grid-row').should('have.length', 0); }); }); \ No newline at end of file diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js index fa65e77afe..6da742638a 100644 --- a/frappe/public/js/frappe/form/grid.js +++ b/frappe/public/js/frappe/form/grid.js @@ -195,11 +195,14 @@ export default class Grid { } delete_all_rows() { - this.frm.doc[this.df.fieldname] = []; - $(this.parent).find('.rows').empty(); - this.grid_rows = []; - this.refresh(); - frappe.utils.scroll_to(this.wrapper); + frappe.confirm(__("Are you sure you want to delete all rows?"), () => { + this.frm.doc[this.df.fieldname] = []; + $(this.parent).find('.rows').empty(); + this.grid_rows = []; + this.refresh(); + frappe.utils.scroll_to(this.wrapper); + }); + } select_row(name) { @@ -304,11 +307,12 @@ export default class Grid { render_result_rows($rows, append_row) { - let result_length = this.grid_pagination.get_result_length(); let page_index = this.grid_pagination.page_index; let page_length = this.grid_pagination.page_length; - + if (!this.grid_rows) { + return; + } for (var ri = (page_index-1)*page_length; ri < result_length; ri++) { var d = this.data[ri]; if (!d) { @@ -364,9 +368,9 @@ export default class Grid { truncate_rows() { if (this.grid_rows.length > this.data.length) { // remove extra rows - for (var i=this.data.length; i < this.grid_rows.length; i++) { + for (var i = this.data.length; i < this.grid_rows.length; i++) { var grid_row = this.grid_rows[i]; - grid_row.wrapper.remove(); + if (grid_row) grid_row.wrapper.remove(); } this.grid_rows.splice(this.data.length); } diff --git a/frappe/public/js/frappe/form/grid_pagination.js b/frappe/public/js/frappe/form/grid_pagination.js index 8884c772ab..190a58c5fe 100644 --- a/frappe/public/js/frappe/form/grid_pagination.js +++ b/frappe/public/js/frappe/form/grid_pagination.js @@ -6,7 +6,7 @@ export default class GridPagination { } setup_pagination() { - this.page_length = 20; + this.page_length = 50; this.page_index = 1; this.total_pages = Math.ceil(this.grid.data.length/this.page_length); From 310233c56a2955c6a4bb82dfbbb8128d0dae6e02 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Sat, 14 Dec 2019 17:34:04 +0530 Subject: [PATCH 23/61] fix: Show default currency as a fallback for currency fieldtype (#8944) * fix: Show default currency as a fallback for currency fieldtype * test: Add a test for currency formatting --- frappe/tests/test_formatter.py | 25 +++++++++++++++++++++++++ frappe/utils/formatters.py | 11 +++++++---- 2 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 frappe/tests/test_formatter.py diff --git a/frappe/tests/test_formatter.py b/frappe/tests/test_formatter.py new file mode 100644 index 0000000000..5257e1c717 --- /dev/null +++ b/frappe/tests/test_formatter.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +import frappe +from frappe import format +import unittest + +class TestFormatter(unittest.TestCase): + def test_currency_formatting(self): + df = frappe._dict({ + 'fieldname': 'amount', + 'fieldtype': 'Currency', + 'options': 'currency' + }) + + doc = frappe._dict({ + 'amount': 5 + }) + frappe.db.set_default("currency", 'INR') + + # if currency field is not passed then default currency should be used. + self.assertEqual(format(100, df, doc), '₹ 100.00') + + doc.currency = 'USD' + self.assertEqual(format(100, df, doc), "$ 100.00") + + frappe.db.set_default("currency", None) \ No newline at end of file diff --git a/frappe/utils/formatters.py b/frappe/utils/formatters.py index 1574ae9ace..46e3694e6f 100644 --- a/frappe/utils/formatters.py +++ b/frappe/utils/formatters.py @@ -55,12 +55,15 @@ def format_value(value, df=None, doc=None, currency=None, translated=False): # this is required to show 0 as blank in table columns return "" - elif df.get("fieldtype") == "Currency" or (df.get("fieldtype")=="Float" and (df.options or "").strip()): - return fmt_money(value, precision=get_field_precision(df, doc), - currency=currency if currency else (get_field_currency(df, doc) if doc else None)) + elif df.get("fieldtype") == "Currency": + default_currency = frappe.db.get_default("currency") + currency = currency or get_field_currency(df, doc) or default_currency + return fmt_money(value, precision=get_field_precision(df, doc), currency=currency) elif df.get("fieldtype") == "Float": precision = get_field_precision(df, doc) + # I don't know why we support currency option for float + currency = currency or get_field_currency(df, doc) # show 1.000000 as 1 # options should not specified @@ -69,7 +72,7 @@ def format_value(value, df=None, doc=None, currency=None, translated=False): if len(temp)==1 or cint(temp[1])==0: precision = 0 - return fmt_money(value, precision=precision) + return fmt_money(value, precision=precision, currency=currency) elif df.get("fieldtype") == "Percent": return "{}%".format(flt(value, 2)) From 09ec275c2bfb0a1f528ce05ccbc7d029a7ab0fc7 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sat, 14 Dec 2019 17:44:17 +0530 Subject: [PATCH 24/61] fix: Remove limit --- frappe/patches/v12_0/fix_home_settings_for_all_users.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/patches/v12_0/fix_home_settings_for_all_users.py b/frappe/patches/v12_0/fix_home_settings_for_all_users.py index 9bc05f8e2c..8a6bcca9a0 100644 --- a/frappe/patches/v12_0/fix_home_settings_for_all_users.py +++ b/frappe/patches/v12_0/fix_home_settings_for_all_users.py @@ -2,7 +2,7 @@ import frappe from frappe.config import get_modules_from_all_apps_for_user import json def execute(): - users = frappe.get_all('User', fields=['name', 'home_settings'], limit=2) + users = frappe.get_all('User', fields=['name', 'home_settings']) for user in users: From 7cf5dee09703fca69dca2629bf72128598dfdf9f Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sat, 14 Dec 2019 18:27:58 +0530 Subject: [PATCH 25/61] test: Add assert statement for value --- cypress/fixtures/custom_submittable_doctype.js | 6 ++++-- cypress/integration/report_view.js | 13 ++++++++++++- cypress/support/commands.js | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/cypress/fixtures/custom_submittable_doctype.js b/cypress/fixtures/custom_submittable_doctype.js index 22b14db890..c88d37b373 100644 --- a/cypress/fixtures/custom_submittable_doctype.js +++ b/cypress/fixtures/custom_submittable_doctype.js @@ -1,5 +1,5 @@ export default { - name: 'Custom Submittable Doctype', + name: 'Custom Submittable DocType', custom: 1, actions: [], is_submittable: 1, @@ -41,7 +41,9 @@ export default { read: 1, role: 'System Manager', share: 1, - write: 1 + write: 1, + submit: 1, + cancel: 1 } ], quick_entry: 1, diff --git a/cypress/integration/report_view.js b/cypress/integration/report_view.js index fe33d5c128..47e6cd6150 100644 --- a/cypress/integration/report_view.js +++ b/cypress/integration/report_view.js @@ -12,7 +12,7 @@ context('Report View', () => { 'enabled': 0, // submit document 'docstatus': 1 - }, true); + }, true).as('doc'); }); it('Field with enabled allow_on_submit should be editable.', () => { @@ -25,5 +25,16 @@ context('Report View', () => { cell.find('input[data-fieldname="enabled"]').check({force: true}); cy.get('.dt-row-0 > .dt-cell--col-4').click(); cy.wait('@value-update'); + cy.get('@doc').then(doc => { + cy.call('frappe.client.get_value', { + doctype: doc.doctype, + filters: { + name: doc.name, + }, + fieldname: 'enabled' + }).then(r => { + expect(r.message.enabled).to.equals(1); + }); + }); }); }); \ No newline at end of file diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 00c7d7f8f4..41d9c16d7b 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -207,7 +207,7 @@ Cypress.Commands.add('insert_doc', (doctype, args, ignore_duplicate) => { status_codes.push(409); } expect(res.status).to.be.oneOf(status_codes); - return res.body; + return res.body.data; }); }); }); \ No newline at end of file From 5bd9023f8c70259444c3d9b16fa8e4b906a7ff24 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sat, 14 Dec 2019 23:29:21 +0530 Subject: [PATCH 26/61] test: Clear cache after creating doctype --- cypress/integration/report_view.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/integration/report_view.js b/cypress/integration/report_view.js index 47e6cd6150..c7aeaa92de 100644 --- a/cypress/integration/report_view.js +++ b/cypress/integration/report_view.js @@ -6,6 +6,7 @@ context('Report View', () => { cy.login(); cy.visit('/desk'); cy.insert_doc('DocType', custom_submittable_doctype, true); + cy.clear_cache(); cy.insert_doc(doctype_name, { 'title': 'Doc 1', 'description': 'Random Text', @@ -13,7 +14,6 @@ context('Report View', () => { // submit document 'docstatus': 1 }, true).as('doc'); - }); it('Field with enabled allow_on_submit should be editable.', () => { cy.server(); From 65854bf3efb6ddaec342e9ceb51ea129e5a2ae80 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 16 Dec 2019 12:40:55 +0530 Subject: [PATCH 27/61] fix: Empty row validation --- frappe/public/js/frappe/form/save.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frappe/public/js/frappe/form/save.js b/frappe/public/js/frappe/form/save.js index 1483fc0a65..28da7a9a63 100644 --- a/frappe/public/js/frappe/form/save.js +++ b/frappe/public/js/frappe/form/save.js @@ -53,6 +53,8 @@ frappe.ui.form.save = function (frm, action, callback, btn) { return frappe.model.is_table(d.doctype); }); + let modified_table_fields = []; + tables.map( function(doc){ const cells = frappe.meta.docfield_list[doc.doctype] || []; @@ -72,9 +74,14 @@ frappe.ui.form.save = function (frm, action, callback, btn) { if (is_empty_row(in_list_view_cells)) { frappe.model.clear_doc(doc.doctype, doc.name); + modified_table_fields.push(doc.parentfield); } } ); + + modified_table_fields.forEach(field => { + frm.refresh_field(field); + }); }; var cancel = function () { From 13bda7118869c000e9837841d726cf1458d78899 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 16 Dec 2019 12:43:38 +0530 Subject: [PATCH 28/61] style: Fix code formatting --- frappe/public/js/frappe/form/save.js | 44 +++++++++++++--------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/frappe/public/js/frappe/form/save.js b/frappe/public/js/frappe/form/save.js index 28da7a9a63..fbc35634f4 100644 --- a/frappe/public/js/frappe/form/save.js +++ b/frappe/public/js/frappe/form/save.js @@ -41,43 +41,41 @@ frappe.ui.form.save = function (frm, action, callback, btn) { }; var remove_empty_rows = function() { - /** - This function removes empty rows. Note that in this function, a row is considered - empty if the fields with `in_list_view: 1` are undefined or falsy because that's - what users also consider to be an empty row - */ + /* + This function removes empty rows. Note that in this function, a row is considered + empty if the fields with `in_list_view: 1` are undefined or falsy because that's + what users also consider to be an empty row + */ const docs = frappe.model.get_all_docs(frm.doc); // we should only worry about table data - const tables = docs.filter(function(d){ + const tables = docs.filter(d => { return frappe.model.is_table(d.doctype); }); let modified_table_fields = []; - tables.map( - function(doc){ - const cells = frappe.meta.docfield_list[doc.doctype] || []; + tables.map(doc => { + const cells = frappe.meta.docfield_list[doc.doctype] || []; - const in_list_view_cells = cells.filter(function(df) { - return cint(df.in_list_view) === 1; - }); + const in_list_view_cells = cells.filter((df) => { + return cint(df.in_list_view) === 1; + }); - var is_empty_row = function(cells) { - for (var i=0; i < cells.length; i++){ - if(locals[doc.doctype][doc.name][cells[i].fieldname]){ - return false; - } + const is_empty_row = function(cells) { + for (let i = 0; i < cells.length; i++) { + if (locals[doc.doctype][doc.name][cells[i].fieldname]) { + return false; } - return true; } + return true; + }; - if (is_empty_row(in_list_view_cells)) { - frappe.model.clear_doc(doc.doctype, doc.name); - modified_table_fields.push(doc.parentfield); - } + if (is_empty_row(in_list_view_cells)) { + frappe.model.clear_doc(doc.doctype, doc.name); + modified_table_fields.push(doc.parentfield); } - ); + }); modified_table_fields.forEach(field => { frm.refresh_field(field); From c83ff2679f5353cdd56173e9aac9083129005d53 Mon Sep 17 00:00:00 2001 From: prssanna Date: Mon, 16 Dec 2019 14:06:18 +0530 Subject: [PATCH 29/61] fix: before setting user default value, if fieldtype is Link check if doc exists --- frappe/model/create_new.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frappe/model/create_new.py b/frappe/model/create_new.py index 6dd0ff3eec..36991f5787 100644 --- a/frappe/model/create_new.py +++ b/frappe/model/create_new.py @@ -61,6 +61,10 @@ def set_user_and_static_default_values(doc): user_default_value = get_user_default_value(df, defaults, doctype_user_permissions, allowed_records, default_doc) if user_default_value is not None: + # if fieldtype is link check if doc exists + if df.fieldtype == "Link": + if not frappe.db.exists(df.options, user_default_value, cache=True): + return doc.set(df.fieldname, user_default_value) else: From e7d9e6edf9a95c6339110c7b559ddb25b134556c Mon Sep 17 00:00:00 2001 From: prssanna Date: Mon, 16 Dec 2019 16:48:18 +0530 Subject: [PATCH 30/61] fix: don't validate email for standard filters --- frappe/public/js/frappe/list/base_list.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/list/base_list.js b/frappe/public/js/frappe/list/base_list.js index 2c5e1df1df..c5084f7d6d 100644 --- a/frappe/public/js/frappe/list/base_list.js +++ b/frappe/public/js/frappe/list/base_list.js @@ -637,7 +637,8 @@ class FilterArea { condition: condition, default: default_value, onchange: () => this.refresh_list_view(), - ignore_link_validation: fieldtype === 'Dynamic Link' + ignore_link_validation: fieldtype === 'Dynamic Link', + is_filter: 1, }; })); From f34af5bc257278a9ff1ffb086dfc2f58c6835d14 Mon Sep 17 00:00:00 2001 From: cjpit Date: Tue, 17 Dec 2019 16:31:52 +0800 Subject: [PATCH 31/61] fix: rfc5322 compliance for python3 (#8912) * start of tests * rfc compliant emails for frappe. * fix for deepsource * fix length of strings. * fix bug that I'm not sure where it came from? * codacy and deepsource issues trying to keep them happy * take a punt in the dark * fix: use SMTPUTF8 instead of SMTP and other minor fixes Signed-off-by: Chinmay D. Pai --- .../doctype/email_account/email_account.py | 10 +-- frappe/email/queue.py | 73 ++++++++++-------- frappe/email/test_email_body.py | 60 +++++++++++++-- frappe/tests/test_email.py | 76 +++++++++++-------- 4 files changed, 146 insertions(+), 73 deletions(-) diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index c05a0f3fe4..495644f652 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -322,16 +322,16 @@ class EmailAccount(Document): unhandled_email.insert(ignore_permissions=True) frappe.db.commit() - def insert_communication(self, msg, args={}): + def insert_communication(self, msg, args=None): if isinstance(msg, list): raw, uid, seen = msg else: raw = msg uid = -1 seen = 0 - - if args.get("uid", -1): uid = args.get("uid", -1) - if args.get("seen", 0): seen = args.get("seen", 0) + if isinstance(args, dict): + if args.get("uid", -1): uid = args.get("uid", -1) + if args.get("seen", 0): seen = args.get("seen", 0) email = Email(raw) @@ -355,7 +355,7 @@ class EmailAccount(Document): name = names[0].get("name") # email is already available update communication uid instead frappe.db.set_value("Communication", name, "uid", uid, update_modified=False) - return + return frappe.get_doc("Communication", name) if email.content_type == 'text/html': email.content = clean_email_html(email.content) diff --git a/frappe/email/queue.py b/frappe/email/queue.py index 792b47296a..1c9a2fd3de 100755 --- a/frappe/email/queue.py +++ b/frappe/email/queue.py @@ -3,22 +3,25 @@ from __future__ import unicode_literals import frappe +import sys from six.moves import html_parser as HTMLParser import smtplib, quopri, json -from frappe import msgprint, throw, _, safe_decode +from frappe import msgprint, _, safe_decode from frappe.email.smtp import SMTPServer, get_outgoing_email_account from frappe.email.email_body import get_email, get_formatted_html, add_attachment from frappe.utils.verified_command import get_signed_params, verify_request from html2text import html2text -from frappe.utils import get_url, nowdate, encode, now_datetime, add_days, split_emails, cstr, cint +from frappe.utils import get_url, nowdate, now_datetime, add_days, split_emails, cstr, cint from rq.timeouts import JobTimeoutException -from six import text_type, string_types +from six import text_type, string_types, PY3 +from email.parser import Parser + class EmailLimitCrossedError(frappe.ValidationError): pass def send(recipients=None, sender=None, subject=None, message=None, text_content=None, reference_doctype=None, reference_name=None, unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None, - attachments=None, reply_to=None, cc=[], bcc=[], message_id=None, in_reply_to=None, send_after=None, + attachments=None, reply_to=None, cc=None, bcc=None, message_id=None, in_reply_to=None, send_after=None, expose_recipients=None, send_priority=1, communication=None, now=False, read_receipt=None, queue_separately=False, is_notification=False, add_unsubscribe_link=1, inline_images=None, header=None, print_letterhead=False): @@ -52,6 +55,11 @@ def send(recipients=None, sender=None, subject=None, message=None, text_content= if not recipients and not cc: return + if not cc: + cc = [] + if not bcc: + bcc = [] + if isinstance(recipients, string_types): recipients = split_emails(recipients) @@ -68,7 +76,6 @@ def send(recipients=None, sender=None, subject=None, message=None, text_content= if not sender or sender == "Administrator": sender = email_account.default_sender - if not text_content: try: text_content = html2text(message) @@ -404,7 +411,7 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False, from_test=Fals message = prepare_message(email, recipient.recipient, recipients_list) if not frappe.flags.in_test: - smtpserver.sess.sendmail(email.sender, recipient.recipient, encode(message)) + smtpserver.sess.sendmail(email.sender, recipient.recipient, message) recipient.status = "Sent" frappe.db.sql("""update `tabEmail Queue Recipient` set status='Sent', modified=%s where name=%s""", @@ -509,37 +516,41 @@ def prepare_message(email, recipient, recipients_list): message = (message and message.encode('utf8')) or '' message = safe_decode(message) - if not email.attachments: - return message - # On-demand attachments - from email.parser import Parser + if PY3: + from email.policy import SMTPUTF8 + message = Parser(policy=SMTPUTF8).parsestr(message) + else: + message = Parser().parsestr(message) - msg_obj = Parser().parsestr(message) - attachments = json.loads(email.attachments) + if email.attachments: + # On-demand attachments - for attachment in attachments: - if attachment.get('fcontent'): continue + attachments = json.loads(email.attachments) - fid = attachment.get("fid") - if fid: - _file = frappe.get_doc("File", fid) - fcontent = _file.get_content() - attachment.update({ - 'fname': _file.file_name, - 'fcontent': fcontent, - 'parent': msg_obj - }) - attachment.pop("fid", None) - add_attachment(**attachment) + for attachment in attachments: + if attachment.get('fcontent'): + continue - elif attachment.get("print_format_attachment") == 1: - attachment.pop("print_format_attachment", None) - print_format_file = frappe.attach_print(**attachment) - print_format_file.update({"parent": msg_obj}) - add_attachment(**print_format_file) + fid = attachment.get("fid") + if fid: + _file = frappe.get_doc("File", fid) + fcontent = _file.get_content() + attachment.update({ + 'fname': _file.file_name, + 'fcontent': fcontent, + 'parent': message + }) + attachment.pop("fid", None) + add_attachment(**attachment) - return msg_obj.as_string() + elif attachment.get("print_format_attachment") == 1: + attachment.pop("print_format_attachment", None) + print_format_file = frappe.attach_print(**attachment) + print_format_file.update({"parent": message}) + add_attachment(**print_format_file) + + return message.as_string() def clear_outbox(): """Remove low priority older than 31 days in Outbox and expire mails not sent for 7 days. diff --git a/frappe/email/test_email_body.py b/frappe/email/test_email_body.py index feb8e80007..26c4e5ba5d 100644 --- a/frappe/email/test_email_body.py +++ b/frappe/email/test_email_body.py @@ -5,7 +5,10 @@ from __future__ import unicode_literals import unittest, os, base64 from frappe.email.receive import Email from frappe.email.email_body import (replace_filename_with_cid, - get_email, inline_style_in_html, get_header) + get_email, inline_style_in_html, get_header) +from frappe.email.queue import prepare_message, get_email_queue +from six import PY3 + class TestEmailBody(unittest.TestCase): def setUp(self): @@ -37,6 +40,53 @@ This is the text version of this email text_content=email_text ).as_string() + def test_prepare_message_returns_already_encoded_string(self): + + if PY3: + uni_chr1 = chr(40960) + uni_chr2 = chr(1972) + else: + uni_chr1 = unichr(40960) + uni_chr2 = unichr(1972) + + email = get_email_queue( + recipients=['test@example.com'], + sender='me@example.com', + subject='Test Subject', + content='

' + uni_chr1 + 'abcd' + uni_chr2 + '

', + formatted='

' + uni_chr1 + 'abcd' + uni_chr2 + '

', + text_content='whatever') + result = prepare_message(email=email, recipient='test@test.com', recipients_list=[]) + self.assertTrue("

=EA=80=80abcd=DE=B4

" in result) + + def test_prepare_message_returns_cr_lf(self): + email = get_email_queue( + recipients=['test@example.com'], + sender='me@example.com', + subject='Test Subject', + content='

\n this is a test of newlines\n' + '

', + formatted='

\n this is a test of newlines\n' + '

', + text_content='whatever') + result = prepare_message(email=email, recipient='test@test.com', recipients_list=[]) + if PY3: + self.assertTrue(result.count('\n') == result.count("\r")) + else: + self.assertTrue(True) + + def test_rfc_5322_header_is_wrapped_at_998_chars(self): + # unfortunately the db can only hold 140 chars so this can't be tested properly. test at max chars anyway. + email = get_email_queue( + recipients=['test@example.com'], + sender='me@example.com', + subject='Test Subject', + content='

Whatever

', + text_content='whatever', + message_id= "a.really.long.message.id.that.should.not.wrap.until.998.if.it.does.then.exchange.will.break" + + ".really.long.message.id.that.should.not.wrap.unti") + result = prepare_message(email=email, recipient='test@test.com', recipients_list=[]) + self.assertTrue( + "a.really.long.message.id.that.should.not.wrap.until.998.if.it.does.then.exchange.will.break" + + ".really.long.message.id.that.should.not.wrap.unti" in result) def test_image(self): img_signature = ''' @@ -49,7 +99,6 @@ Content-Disposition: inline; filename="favicon.png" self.assertTrue(img_signature in self.email_string) self.assertTrue(self.img_base64 in self.email_string) - def test_text_content(self): text_content = ''' Content-Type: text/plain; charset="utf-8" @@ -62,7 +111,6 @@ This is the text version of this email ''' self.assertTrue(text_content in self.email_string) - def test_email_content(self): html_head = ''' Content-Type: text/html; charset="utf-8" @@ -79,7 +127,6 @@ w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> self.assertTrue(html_head in self.email_string) self.assertTrue(html in self.email_string) - def test_replace_filename_with_cid(self): original_message = '''
@@ -152,6 +199,7 @@ Reply-To: test2_@erpnext.com mail = Email(content_bytes) self.assertEqual(mail.text_content, text_content) + def fixed_column_width(string, chunk_size): - parts = [string[0+i:chunk_size+i] for i in range(0, len(string), chunk_size)] - return '\n'.join(parts) \ No newline at end of file + parts = [string[0 + i:chunk_size + i] for i in range(0, len(string), chunk_size)] + return '\n'.join(parts) diff --git a/frappe/tests/test_email.py b/frappe/tests/test_email.py index ff92dd3a32..0efcb47948 100644 --- a/frappe/tests/test_email.py +++ b/frappe/tests/test_email.py @@ -4,11 +4,14 @@ from __future__ import unicode_literals import unittest, frappe, re, email +from six import PY3 from frappe.test_runner import make_test_records + make_test_records("User") make_test_records("Email Account") + class TestEmail(unittest.TestCase): def setUp(self): frappe.db.sql("""delete from `tabEmail Unsubscribe`""") @@ -16,11 +19,11 @@ class TestEmail(unittest.TestCase): frappe.db.sql("""delete from `tabEmail Queue Recipient`""") def test_email_queue(self, send_after=None): - frappe.sendmail(recipients = ['test@example.com', 'test1@example.com'], - sender="admin@example.com", - reference_doctype='User', reference_name='Administrator', - subject='Testing Queue', message='This mail is queued!', - unsubscribe_message="Unsubscribe", send_after=send_after) + frappe.sendmail(recipients=['test@example.com', 'test1@example.com'], + sender="admin@example.com", + reference_doctype='User', reference_name='Administrator', + subject='Testing Queue', message='This mail is queued!', + unsubscribe_message="Unsubscribe", send_after=send_after) email_queue = frappe.db.sql("""select name,message from `tabEmail Queue` where status='Not Sent'""", as_dict=1) self.assertEqual(len(email_queue), 1) @@ -32,7 +35,7 @@ class TestEmail(unittest.TestCase): self.assertTrue('' in email_queue[0]['message']) def test_send_after(self): - self.test_email_queue(send_after = 1) + self.test_email_queue(send_after=1) from frappe.email.queue import flush flush(from_test=True) email_queue = frappe.db.sql("""select name from `tabEmail Queue` where status='Sent'""", as_dict=1) @@ -52,12 +55,13 @@ class TestEmail(unittest.TestCase): self.assertTrue('Unsubscribe' in frappe.safe_decode(frappe.flags.sent_mail)) def test_cc_header(self): - #test if sending with cc's makes it into header + # test if sending with cc's makes it into header frappe.sendmail(recipients=['test@example.com'], - cc=['test1@example.com'], - sender="admin@example.com", - reference_doctype='User', reference_name="Administrator", - subject='Testing Email Queue', message='This is mail is queued!', unsubscribe_message="Unsubscribe", expose_recipients="header") + cc=['test1@example.com'], + sender="admin@example.com", + reference_doctype='User', reference_name="Administrator", + subject='Testing Email Queue', message='This is mail is queued!', + unsubscribe_message="Unsubscribe", expose_recipients="header") email_queue = frappe.db.sql("""select name from `tabEmail Queue` where status='Not Sent'""", as_dict=1) self.assertEqual(len(email_queue), 1) queue_recipients = [r.recipient for r in frappe.db.sql("""select recipient from `tabEmail Queue Recipient` @@ -71,12 +75,13 @@ class TestEmail(unittest.TestCase): self.assertTrue('CC: test1@example.com' in message) def test_cc_footer(self): - #test if sending with cc's makes it into header + # test if sending with cc's makes it into header frappe.sendmail(recipients=['test@example.com'], - cc=['test1@example.com'], - sender="admin@example.com", - reference_doctype='User', reference_name="Administrator", - subject='Testing Email Queue', message='This is mail is queued!', unsubscribe_message="Unsubscribe", expose_recipients="footer", now=True) + cc=['test1@example.com'], + sender="admin@example.com", + reference_doctype='User', reference_name="Administrator", + subject='Testing Email Queue', message='This is mail is queued!', + unsubscribe_message="Unsubscribe", expose_recipients="footer", now=True) email_queue = frappe.db.sql("""select name from `tabEmail Queue` where status='Sent'""", as_dict=1) self.assertEqual(len(email_queue), 1) queue_recipients = [r.recipient for r in frappe.db.sql("""select recipient from `tabEmail Queue Recipient` @@ -84,15 +89,17 @@ class TestEmail(unittest.TestCase): self.assertTrue('test@example.com' in queue_recipients) self.assertTrue('test1@example.com' in queue_recipients) - self.assertTrue('This email was sent to test@example.com and copied to test1@example.com' in frappe.safe_decode(frappe.flags.sent_mail)) + self.assertTrue('This email was sent to test@example.com and copied to test1@example.com' in frappe.safe_decode( + frappe.flags.sent_mail)) def test_expose(self): from frappe.utils.verified_command import verify_request frappe.sendmail(recipients=['test@example.com'], - cc=['test1@example.com'], - sender="admin@example.com", - reference_doctype='User', reference_name="Administrator", - subject='Testing Email Queue', message='This is mail is queued!', unsubscribe_message="Unsubscribe", now=True) + cc=['test1@example.com'], + sender="admin@example.com", + reference_doctype='User', reference_name="Administrator", + subject='Testing Email Queue', message='This is mail is queued!', + unsubscribe_message="Unsubscribe", now=True) email_queue = frappe.db.sql("""select name from `tabEmail Queue` where status='Sent'""", as_dict=1) self.assertEqual(len(email_queue), 1) queue_recipients = [r.recipient for r in frappe.db.sql("""select recipient from `tabEmail Queue Recipient` @@ -109,7 +116,14 @@ class TestEmail(unittest.TestCase): content = part.get_payload(decode=True) if content: - frappe.local.flags.signed_query_string = re.search(r'(?<=/api/method/frappe.email.queue.unsubscribe\?).*(?=\n)', content.decode()).group(0) + if PY3: + eol = "\r\n" + else: + eol = "\n" + + frappe.local.flags.signed_query_string = \ + re.search(r'(?<=/api/method/frappe.email.queue.unsubscribe\?).*(?=' + eol + ')', + content.decode()).group(0) self.assertTrue(verify_request()) break @@ -121,7 +135,7 @@ class TestEmail(unittest.TestCase): email_queue = frappe.db.sql("""select name from `tabEmail Queue` where status='Expired'""", as_dict=1) self.assertEqual(len(email_queue), 1) queue_recipients = [r.recipient for r in frappe.db.sql("""select recipient from `tabEmail Queue Recipient` - where parent = %s""",email_queue[0].name, as_dict=1)] + where parent = %s""", email_queue[0].name, as_dict=1)] self.assertTrue('test@example.com' in queue_recipients) self.assertTrue('test1@example.com' in queue_recipients) self.assertEqual(len(queue_recipients), 2) @@ -131,19 +145,20 @@ class TestEmail(unittest.TestCase): unsubscribe(doctype="User", name="Administrator", email="test@example.com") self.assertTrue(frappe.db.get_value("Email Unsubscribe", - {"reference_doctype": "User", "reference_name": "Administrator", "email": "test@example.com"})) + {"reference_doctype": "User", "reference_name": "Administrator", + "email": "test@example.com"})) before = frappe.db.sql("""select count(name) from `tabEmail Queue` where status='Not Sent'""")[0][0] - send(recipients = ['test@example.com', 'test1@example.com'], - sender="admin@example.com", - reference_doctype='User', reference_name= "Administrator", - subject='Testing Email Queue', message='This is mail is queued!', unsubscribe_message="Unsubscribe") + send(recipients=['test@example.com', 'test1@example.com'], + sender="admin@example.com", + reference_doctype='User', reference_name="Administrator", + subject='Testing Email Queue', message='This is mail is queued!', unsubscribe_message="Unsubscribe") # this is sent async (?) email_queue = frappe.db.sql("""select name from `tabEmail Queue` where status='Not Sent'""", - as_dict=1) + as_dict=1) self.assertEqual(len(email_queue), before + 1) queue_recipients = [r.recipient for r in frappe.db.sql("""select recipient from `tabEmail Queue Recipient` where status='Not Sent'""", as_dict=1)] @@ -152,7 +167,6 @@ class TestEmail(unittest.TestCase): self.assertEqual(len(queue_recipients), 1) self.assertTrue('Unsubscribe' in frappe.safe_decode(frappe.flags.sent_mail)) - def test_image_parsing(self): import re email_account = frappe.get_doc('Email Account', '_Test Email Account 1') @@ -166,6 +180,6 @@ class TestEmail(unittest.TestCase): self.assertTrue(re.search(''']*src=["']/private/files/rtco2.png[^>]*>''', communication.content)) -if __name__=='__main__': +if __name__ == '__main__': frappe.connect() unittest.main() From cf3aa5489848a909bfcb8cee74cab0b561370510 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Tue, 17 Dec 2019 17:48:46 +0530 Subject: [PATCH 32/61] feat (#9052) (server-scripts): Support Before/After Save for submitted document --- frappe/core/doctype/server_script/server_script.json | 6 ++++-- frappe/core/doctype/server_script/server_script_utils.py | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/server_script/server_script.json b/frappe/core/doctype/server_script/server_script.json index 82fff31394..36c297cc26 100644 --- a/frappe/core/doctype/server_script/server_script.json +++ b/frappe/core/doctype/server_script/server_script.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "Prompt", "creation": "2019-09-30 11:56:57.943241", "doctype": "DocType", @@ -43,7 +44,7 @@ "fieldname": "doctype_event", "fieldtype": "Select", "label": "DocType Event", - "options": "Before Insert\nBefore Save\nAfter Save\nBefore Submit\nAfter Submit\nBefore Cancel\nAfter Cancel\nBefore Delete\nAfter Delete" + "options": "Before Insert\nBefore Save\nAfter Save\nBefore Submit\nAfter Submit\nBefore Cancel\nAfter Cancel\nBefore Delete\nAfter Delete\nBefore Save (Submitted Document)\nAfter Save (Submitted Document)" }, { "depends_on": "eval:doc.script_type==='API'", @@ -73,7 +74,8 @@ "fieldtype": "Section Break" } ], - "modified": "2019-10-09 15:08:40.085059", + "links": [], + "modified": "2019-12-17 12:55:07.389775", "modified_by": "Administrator", "module": "Core", "name": "Server Script", diff --git a/frappe/core/doctype/server_script/server_script_utils.py b/frappe/core/doctype/server_script/server_script_utils.py index 878810f459..2e1a5ae8bb 100644 --- a/frappe/core/doctype/server_script/server_script_utils.py +++ b/frappe/core/doctype/server_script/server_script_utils.py @@ -14,6 +14,8 @@ EVENT_MAP = { 'on_cancel': 'After Cancel', 'on_trash': 'Before Delete', 'after_delete': 'After Delete', + 'before_update_after_submit': 'Before Save (Submitted Document)', + 'on_update_after_submit': 'After Save (Submitted Document)' } def run_server_script_api(method): From cda63fa8678621f077e30e7af6104c4d10f6e18f Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Tue, 17 Dec 2019 18:19:07 +0530 Subject: [PATCH 33/61] Revert "fix: user default value for Link fields" --- frappe/model/create_new.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/frappe/model/create_new.py b/frappe/model/create_new.py index 36991f5787..6dd0ff3eec 100644 --- a/frappe/model/create_new.py +++ b/frappe/model/create_new.py @@ -61,10 +61,6 @@ def set_user_and_static_default_values(doc): user_default_value = get_user_default_value(df, defaults, doctype_user_permissions, allowed_records, default_doc) if user_default_value is not None: - # if fieldtype is link check if doc exists - if df.fieldtype == "Link": - if not frappe.db.exists(df.options, user_default_value, cache=True): - return doc.set(df.fieldname, user_default_value) else: From 3f002e6320fbd35dc77eeb761da396116feea4b2 Mon Sep 17 00:00:00 2001 From: prssanna Date: Tue, 17 Dec 2019 18:23:13 +0530 Subject: [PATCH 34/61] before setting user default value, if fieldtype is Link check if doc exists --- frappe/model/create_new.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/model/create_new.py b/frappe/model/create_new.py index 6dd0ff3eec..f697d8051a 100644 --- a/frappe/model/create_new.py +++ b/frappe/model/create_new.py @@ -61,7 +61,9 @@ def set_user_and_static_default_values(doc): user_default_value = get_user_default_value(df, defaults, doctype_user_permissions, allowed_records, default_doc) if user_default_value is not None: - doc.set(df.fieldname, user_default_value) + # if fieldtype is link check if doc exists + if not df.fieldtype == "Link" or frappe.db.exists(df.options, user_default_value): + doc.set(df.fieldname, user_default_value) else: if df.fieldname != doc.meta.title_field: From 954ca6175a765ba5d5dbef6224f0fcb1ab99e823 Mon Sep 17 00:00:00 2001 From: vishal Date: Mon, 16 Dec 2019 11:49:29 +0530 Subject: [PATCH 35/61] clear attachment on image remove attach file --- frappe/public/js/frappe/form/controls/attach.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/controls/attach.js b/frappe/public/js/frappe/form/controls/attach.js index 15cbd3b043..cb435c64bd 100644 --- a/frappe/public/js/frappe/form/controls/attach.js +++ b/frappe/public/js/frappe/form/controls/attach.js @@ -32,7 +32,7 @@ frappe.ui.form.ControlAttach = frappe.ui.form.ControlData.extend({ if(this.frm) { me.parse_validate_and_set_in_model(null); me.refresh(); - me.frm.attachments.remove_attachment_by_filename(me.value, function() { + me.frm.attachments.remove_attachment_by_filename(me.value || me.frm.doc[me.frm.meta.image_field], function() { me.parse_validate_and_set_in_model(null); me.refresh(); me.frm.doc.docstatus == 1 ? me.frm.save('Update') : me.frm.save(); From a25a0b46e67b1792cb2fb740f7287cc2f888ac8c Mon Sep 17 00:00:00 2001 From: vishal Date: Wed, 18 Dec 2019 11:11:07 +0530 Subject: [PATCH 36/61] fix: Remove Attachment on remove --- frappe/public/js/frappe/form/controls/attach.js | 2 +- frappe/public/js/frappe/form/sidebar/user_image.js | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/form/controls/attach.js b/frappe/public/js/frappe/form/controls/attach.js index cb435c64bd..15cbd3b043 100644 --- a/frappe/public/js/frappe/form/controls/attach.js +++ b/frappe/public/js/frappe/form/controls/attach.js @@ -32,7 +32,7 @@ frappe.ui.form.ControlAttach = frappe.ui.form.ControlData.extend({ if(this.frm) { me.parse_validate_and_set_in_model(null); me.refresh(); - me.frm.attachments.remove_attachment_by_filename(me.value || me.frm.doc[me.frm.meta.image_field], function() { + me.frm.attachments.remove_attachment_by_filename(me.value, function() { me.parse_validate_and_set_in_model(null); me.refresh(); me.frm.doc.docstatus == 1 ? me.frm.save('Update') : me.frm.save(); diff --git a/frappe/public/js/frappe/form/sidebar/user_image.js b/frappe/public/js/frappe/form/sidebar/user_image.js index 6c8099db89..056e5f9427 100644 --- a/frappe/public/js/frappe/form/sidebar/user_image.js +++ b/frappe/public/js/frappe/form/sidebar/user_image.js @@ -88,7 +88,9 @@ frappe.ui.form.setup_user_image_event = function(frm) { } field.$input.trigger('click'); } else { - field.set_value('').then(() => frm.save()); + frm.attachments.remove_attachment_by_filename(frm.doc[frm.meta.image_field] , function() { + field.set_value('').then(() => frm.save()); + }); } }); } \ No newline at end of file From b302437d5c2a902d6f3b8b7405b21f7477c35fa4 Mon Sep 17 00:00:00 2001 From: vishal Date: Wed, 18 Dec 2019 11:18:39 +0530 Subject: [PATCH 37/61] fix: minor changes --- frappe/public/js/frappe/form/sidebar/user_image.js | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/public/js/frappe/form/sidebar/user_image.js b/frappe/public/js/frappe/form/sidebar/user_image.js index 056e5f9427..9fe6a4017a 100644 --- a/frappe/public/js/frappe/form/sidebar/user_image.js +++ b/frappe/public/js/frappe/form/sidebar/user_image.js @@ -88,6 +88,7 @@ frappe.ui.form.setup_user_image_event = function(frm) { } field.$input.trigger('click'); } else { + /// on remove event for a sidebar image wrapper remove attach file. frm.attachments.remove_attachment_by_filename(frm.doc[frm.meta.image_field] , function() { field.set_value('').then(() => frm.save()); }); From 918cd70161f20ca631ce020400338d10983352a7 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Wed, 18 Dec 2019 11:40:06 +0530 Subject: [PATCH 38/61] style: fix Codacy issue There should be no space before ','. (comma-spacing) --- frappe/public/js/frappe/form/sidebar/user_image.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/form/sidebar/user_image.js b/frappe/public/js/frappe/form/sidebar/user_image.js index 9fe6a4017a..cd0993207b 100644 --- a/frappe/public/js/frappe/form/sidebar/user_image.js +++ b/frappe/public/js/frappe/form/sidebar/user_image.js @@ -89,9 +89,9 @@ frappe.ui.form.setup_user_image_event = function(frm) { field.$input.trigger('click'); } else { /// on remove event for a sidebar image wrapper remove attach file. - frm.attachments.remove_attachment_by_filename(frm.doc[frm.meta.image_field] , function() { + frm.attachments.remove_attachment_by_filename(frm.doc[frm.meta.image_field], function() { field.set_value('').then(() => frm.save()); }); } }); -} \ No newline at end of file +} From 8dfacdecaca8ff5915b16f3004dcff371ae0e096 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Wed, 18 Dec 2019 17:04:12 +0530 Subject: [PATCH 39/61] chore: Add comment to explain the reason for ignoring validation --- frappe/public/js/frappe/ui/filters/filter_list.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frappe/public/js/frappe/ui/filters/filter_list.js b/frappe/public/js/frappe/ui/filters/filter_list.js index 18ae1d1819..b9a131d4c7 100644 --- a/frappe/public/js/frappe/ui/filters/filter_list.js +++ b/frappe/public/js/frappe/ui/filters/filter_list.js @@ -63,6 +63,8 @@ frappe.ui.FilterGroup = class { } validate_args(doctype, fieldname) { + // Tags attached to the document are maintained seperately in Tag Link + // and is not the part of doctype meta therefore tag fieldname validation is ignored. if (doctype === "Tag Link" && fieldname === "tag") { return true; } From 1a69ba6a84c901bb3cf5027126a92e3e5ecb433f Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Thu, 19 Dec 2019 00:13:35 +0530 Subject: [PATCH 40/61] fix: fix grid ui for child tables in column break (#9050) --- frappe/public/less/form_grid.less | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frappe/public/less/form_grid.less b/frappe/public/less/form_grid.less index f41b60d4cb..28f08635ba 100644 --- a/frappe/public/less/form_grid.less +++ b/frappe/public/less/form_grid.less @@ -262,6 +262,14 @@ border-bottom: 1px solid @border-color; } +.grid-header-toolbar { + display: flow-root; +} + +.grid-buttons { + display: inline-flex; +} + .grid-footer { background-color: #fff; border: 1px solid @border-color; From 5c738ade2eb4e0557a5ecf3f2fdb19045ca63c81 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 19 Dec 2019 12:55:33 +0530 Subject: [PATCH 41/61] fix: Make "Restriction" text translatable --- frappe/public/js/frappe/list/list_view.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 777030c923..e8abb9d490 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -137,8 +137,8 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { show_restricted_list_indicator_if_applicable() { const match_rules_list = frappe.perm.get_match_rules(this.doctype); - if(match_rules_list.length) { - this.restricted_list = $('') + if (match_rules_list.length) { + this.restricted_list = $(``) .prepend('') .click(() => this.show_restrictions(match_rules_list)) .appendTo(this.page.page_form); @@ -148,7 +148,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { show_restrictions(match_rules_list=[]) { frappe.msgprint(frappe.render_template('list_view_permission_restrictions', { condition_list: match_rules_list - }), 'Restrictions'); + }), __('Restrictions')); } set_fields() { From bd6d2b16bc3e37bba9dde10dd62353dc74146027 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 19 Dec 2019 12:56:00 +0530 Subject: [PATCH 42/61] fix: Modal indicator CSS --- frappe/public/js/frappe/dom.js | 2 +- frappe/public/less/indicator.less | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/dom.js b/frappe/public/js/frappe/dom.js index 6411c2fb2b..819ecb526e 100644 --- a/frappe/public/js/frappe/dom.js +++ b/frappe/public/js/frappe/dom.js @@ -291,7 +291,7 @@ frappe.get_modal = function(title, content) {

+
{% for (var x=0, y=app_info.change_log.length; x < y; x++) { var version_info = app_info.change_log[x]; if(version_info) { %}

{{ frappe.markdown(version_info[1]) }}

{% } } %} +
{% } %} diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index cb52406b1e..23d7bffec2 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -479,13 +479,6 @@ frappe.Application = Class.extend({ // }]; // Iterate over changelog - change_log.forEach(log => { - log.change_log.forEach(version_info => { - version_info[1] = version_info[1].replace(/#/, '##'); - // replace all # with ## for rendering them as

- }); - }); - var change_log_dialog = frappe.msgprint({ message: frappe.render_template("change_log", {"change_log": change_log}), title: __("Updated To New Version 🎉"), diff --git a/frappe/public/less/desk.less b/frappe/public/less/desk.less index 0a8062efa2..fa29d7d788 100644 --- a/frappe/public/less/desk.less +++ b/frappe/public/less/desk.less @@ -1137,3 +1137,9 @@ body.no-sidebar { .alt-pressed .alt-underline { text-decoration: underline; } + +.app-change-log-body { + h1 { + font-size: 20px; + } +} From bdaaf75c96af26fd69903754e1ce9d464ada893f Mon Sep 17 00:00:00 2001 From: prssanna Date: Wed, 18 Dec 2019 13:13:04 +0530 Subject: [PATCH 50/61] fix: list sidebar tags dropdown doesn't get updated --- frappe/desk/reportview.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 5db6ae18bf..f7f2867238 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -267,11 +267,9 @@ def get_sidebar_stats(stats, doctype, filters=[]): data = frappe._dict(frappe.local.form_dict) filters = json.loads(data["filters"]) - if not frappe.cache().hget("Tags", doctype): - tags = set([tag.tag for tag in frappe.get_list("Tag Link", filters={"document_type": doctype}, fields=["tag"])]) - frappe.cache().hset("Tags", doctype, tags) - - for tag in list(frappe.cache().hget("Tags", doctype)): + tags = set([tag.tag for tag in frappe.get_list("Tag Link", filters={"document_type": doctype}, fields=["tag"])]) + _user_tags = [] + for tag in tags: tag_filters = [] tag_filters.extend(filters) tag_filters.extend([['Tag Link', 'tag', '=', tag]]) From 8d99e2303ca9198eb73bf50e4697ca419a076883 Mon Sep 17 00:00:00 2001 From: prssanna Date: Wed, 18 Dec 2019 13:29:41 +0530 Subject: [PATCH 51/61] fix: fix tag dropdown search --- frappe/public/js/frappe/list/list_sidebar.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/list/list_sidebar.js b/frappe/public/js/frappe/list/list_sidebar.js index 4767e02f43..d1bf4ca2d4 100644 --- a/frappe/public/js/frappe/list/list_sidebar.js +++ b/frappe/public/js/frappe/list/list_sidebar.js @@ -253,13 +253,15 @@ frappe.views.ListSidebar = class ListSidebar { let text_filter = $search_input.val().toLowerCase(); // Replace trailing and leading spaces text_filter = text_filter.replace(/^\s+|\s+$/g, ''); - let text; for (var i = 0; i < $elements.length; i++) { let text_element = $elements.eq(i).find(text_class); let text = text_element.text().toLowerCase(); // Search data-name since label for current user is 'Me' - let name = text_element.data('name').toLowerCase(); + let name = ''; + if (text_element.data('name')) { + name = text_element.data('name').toLowerCase(); + } if (text.includes(text_filter) || name.includes(text_filter)) { $elements.eq(i).css('display',''); } else { From 0728529b6efabfc217de9462db2bca52f16c74dc Mon Sep 17 00:00:00 2001 From: prssanna Date: Tue, 17 Dec 2019 16:58:52 +0530 Subject: [PATCH 52/61] fix: no records tagged getting rendered multiple times --- frappe/public/js/frappe/list/list_sidebar.js | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/public/js/frappe/list/list_sidebar.js b/frappe/public/js/frappe/list/list_sidebar.js index 4767e02f43..1b6930bfdb 100644 --- a/frappe/public/js/frappe/list/list_sidebar.js +++ b/frappe/public/js/frappe/list/list_sidebar.js @@ -41,6 +41,7 @@ frappe.views.ListSidebar = class ListSidebar { } else { this.sidebar.find('.list-stats').on('click', (e) => { $(e.currentTarget).find('.stat-link').remove(); + $(e.currentTarget).find('.stat-no-records').remove(); this.get_stats(); }); } From 1c126415874abf61499d7bc6a0185cd712fe58de Mon Sep 17 00:00:00 2001 From: prssanna Date: Tue, 17 Dec 2019 17:00:23 +0530 Subject: [PATCH 53/61] fix: add padding to 'No Records Tagged' --- frappe/public/less/sidebar.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frappe/public/less/sidebar.less b/frappe/public/less/sidebar.less index 61d47712f3..ac5a5c33d5 100644 --- a/frappe/public/less/sidebar.less +++ b/frappe/public/less/sidebar.less @@ -406,6 +406,10 @@ body[data-route^="Module"] .main-menu { .dropdown-search { padding: 8px; } + + .stat-no-records { + margin: 5px 10px; + } } // module sidebar From 28f99551f51cdff031004fa2c12d6f31035a9120 Mon Sep 17 00:00:00 2001 From: prssanna Date: Tue, 17 Dec 2019 17:17:23 +0530 Subject: [PATCH 54/61] fix: use reload_stats function --- frappe/public/js/frappe/list/list_sidebar.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/list/list_sidebar.js b/frappe/public/js/frappe/list/list_sidebar.js index 1b6930bfdb..b390d59732 100644 --- a/frappe/public/js/frappe/list/list_sidebar.js +++ b/frappe/public/js/frappe/list/list_sidebar.js @@ -40,9 +40,7 @@ frappe.views.ListSidebar = class ListSidebar { this.sidebar.find('.sidebar-stat').remove(); } else { this.sidebar.find('.list-stats').on('click', (e) => { - $(e.currentTarget).find('.stat-link').remove(); - $(e.currentTarget).find('.stat-no-records').remove(); - this.get_stats(); + this.reload_stats(); }); } From 4e79b7994e89e2a7da4824a4c0208412a06f9df8 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Thu, 19 Dec 2019 12:06:50 +0530 Subject: [PATCH 55/61] fix: miscellaneous fixes --- frappe/desk/reportview.py | 10 +++------- frappe/public/js/frappe/list/base_list.js | 2 +- frappe/public/less/list.less | 13 +++++++++++++ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 5db6ae18bf..9caf72d3bd 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -267,18 +267,14 @@ def get_sidebar_stats(stats, doctype, filters=[]): data = frappe._dict(frappe.local.form_dict) filters = json.loads(data["filters"]) - if not frappe.cache().hget("Tags", doctype): - tags = set([tag.tag for tag in frappe.get_list("Tag Link", filters={"document_type": doctype}, fields=["tag"])]) - frappe.cache().hset("Tags", doctype, tags) - - for tag in list(frappe.cache().hget("Tags", doctype)): + for tag in frappe.get_all("Tag Link", filters={"document_type": doctype}, fields=["tag"]): tag_filters = [] tag_filters.extend(filters) - tag_filters.extend([['Tag Link', 'tag', '=', tag]]) + tag_filters.extend([['Tag Link', 'tag', '=', tag.tag]]) count = frappe.get_all(doctype, filters=tag_filters, fields=["count(*)"]) if count[0].get("count(*)") > 0: - _user_tags.append([tag, count[0].get("count(*)")]) + _user_tags.append([tag.tag, count[0].get("count(*)")]) return {"stats": {"_user_tags": _user_tags}} diff --git a/frappe/public/js/frappe/list/base_list.js b/frappe/public/js/frappe/list/base_list.js index c5084f7d6d..5e1b717720 100644 --- a/frappe/public/js/frappe/list/base_list.js +++ b/frappe/public/js/frappe/list/base_list.js @@ -207,7 +207,7 @@ frappe.views.BaseList = class BaseList { show_or_hide_sidebar() { let show_sidebar = JSON.parse(localStorage.show_sidebar || 'true'); - $(document.body).toggleClass('no-sidebar', !show_sidebar); + $(document.body).toggleClass('no-list-sidebar', !show_sidebar); } setup_main_section() { diff --git a/frappe/public/less/list.less b/frappe/public/less/list.less index 8336f13f11..c22184406c 100644 --- a/frappe/public/less/list.less +++ b/frappe/public/less/list.less @@ -50,6 +50,19 @@ } } +body.no-list-sidebar { + [data-page-route^="List/"] { + @media (min-width: @screen-md) { + .layout-side-section { + display: none; + } + + .layout-main-section-wrapper { + width: 100% !important; + } + } + } +} .filter-list { position: relative; From da09ef5fe53b12fe030b51a257a2ca4628419b91 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 17 Dec 2019 21:45:43 +0530 Subject: [PATCH 56/61] fix: Return empty dict if session is not created --- frappe/desk/notifications.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/desk/notifications.py b/frappe/desk/notifications.py index b142047059..32d5e281a7 100644 --- a/frappe/desk/notifications.py +++ b/frappe/desk/notifications.py @@ -182,6 +182,8 @@ def get_notification_info(): return out def get_notification_config(): + if not frappe.session.user: + return frappe._dict() def _get(): subscribed_documents = get_subscribed_documents() config = frappe._dict() @@ -204,7 +206,7 @@ def get_notification_config(): else: config[key].update(nc.get(key, {})) return config - + print(frappe.cache().hget("notification_config", frappe.session.user)) return frappe.cache().hget("notification_config", frappe.session.user, _get) def get_filters_for(doctype): From 6b91ed22e4707f5d1c0848e42840a09a3bbc4a8d Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Tue, 17 Dec 2019 22:15:06 +0530 Subject: [PATCH 57/61] fix: Remove unwanted print statement --- frappe/desk/notifications.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/desk/notifications.py b/frappe/desk/notifications.py index 32d5e281a7..31aae834b5 100644 --- a/frappe/desk/notifications.py +++ b/frappe/desk/notifications.py @@ -206,7 +206,7 @@ def get_notification_config(): else: config[key].update(nc.get(key, {})) return config - print(frappe.cache().hget("notification_config", frappe.session.user)) + return frappe.cache().hget("notification_config", frappe.session.user, _get) def get_filters_for(doctype): From e3dfbf4562bd31e6431d08c8900c197d5537997c Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 18 Dec 2019 09:16:35 +0530 Subject: [PATCH 58/61] fix: Get guest's config if session.user is None --- frappe/desk/notifications.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/desk/notifications.py b/frappe/desk/notifications.py index 31aae834b5..3a8815ca71 100644 --- a/frappe/desk/notifications.py +++ b/frappe/desk/notifications.py @@ -182,8 +182,8 @@ def get_notification_info(): return out def get_notification_config(): - if not frappe.session.user: - return frappe._dict() + user = frappe.session.user or 'Guest' + def _get(): subscribed_documents = get_subscribed_documents() config = frappe._dict() @@ -207,7 +207,7 @@ def get_notification_config(): config[key].update(nc.get(key, {})) return config - return frappe.cache().hget("notification_config", frappe.session.user, _get) + return frappe.cache().hget("notification_config", user, _get) def get_filters_for(doctype): '''get open filters for doctype''' From 147dfb12e8f64ad107ccb1b39a392824958331f4 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 23 Dec 2019 08:53:32 +0530 Subject: [PATCH 59/61] test: Fix form's randomly failing test --- cypress/integration/form.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cypress/integration/form.js b/cypress/integration/form.js index 5a8b85d19e..faa8005a9d 100644 --- a/cypress/integration/form.js +++ b/cypress/integration/form.js @@ -1,8 +1,4 @@ context('Form', () => { - beforeEach(() => { - cy.login(); - cy.visit('/desk'); - }); before(() => { cy.login(); cy.visit('/desk'); @@ -10,6 +6,9 @@ context('Form', () => { frappe.call("frappe.tests.ui_test_helpers.create_contact_records"); }); }); + beforeEach(() => { + cy.visit('/desk'); + }); 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(); @@ -30,7 +29,8 @@ context('Form', () => { 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.get('.next-doc').click({ force: true }); + cy.wait(200); 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'); From ecf0755911a7ed297b503765f0e849b8cb53cc86 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Mon, 23 Dec 2019 10:08:55 +0530 Subject: [PATCH 60/61] test: Fix failing test in form.js (#9103) --- cypress/integration/form.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cypress/integration/form.js b/cypress/integration/form.js index faa8005a9d..ed5ff21ff1 100644 --- a/cypress/integration/form.js +++ b/cypress/integration/form.js @@ -26,9 +26,9 @@ context('Form', () => { 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('.prev-doc').click({ force: true }); cy.get('.msgprint-dialog .modal-body').contains('No further records').should('be.visible'); - cy.get('.modal-backdrop').click(); + cy.get('.btn-modal-close:visible').click(); cy.get('.next-doc').click({ force: true }); cy.wait(200); cy.contains('Test Form Contact 2').should('not.exist'); From 71074095317c327bddbf58a05458c34dffb8c425 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Mon, 23 Dec 2019 12:24:52 +0530 Subject: [PATCH 61/61] Merge version-12-hotfix to develop (#9095) * fix: REST API utf-8 decoding on creates/updates Creating or updating a document via the REST API would generate an error of: `TypeError: the JSON object must be str, not 'bytes'` Because get_data() returns bytes which must be explicitly converted to a string before parsing as JSON. Defect introduced by efe94886a and a71a92341e * fix(patch): Handle failures in while making Prepared Report attachments private * case insensitive search for postgres * convert operator to lowercase while checking * fix: Pass prepared_report_name as filter if exists * fix: Add input to multiselect_list control * fix(reportview): convert to unicode conditionally fixes TypeError: decoding str is not supported Traceback (most recent call last): File "/home/frappe/frappe-bench/apps/frappe/frappe/app.py", line 57, in application response = frappe.handler.handle() File "/home/frappe/frappe-bench/apps/frappe/frappe/handler.py", line 22, in handle data = execute_cmd(cmd) File "/home/frappe/frappe-bench/apps/frappe/frappe/handler.py", line 61, in execute_cmd return frappe.call(method, **frappe.form_dict) File "/home/frappe/frappe-bench/apps/frappe/frappe/__init__.py", line 1038, in call return fn(*args, **newargs) File "/home/frappe/frappe-bench/apps/frappe/frappe/__init__.py", line 511, in wrapper_fn retval = fn(*args, **get_newargs(fn, kwargs)) File "/home/frappe/frappe-bench/apps/frappe/frappe/desk/reportview.py", line 177, in export_query frappe.response['result'] = text_type(f.read(), 'utf-8') TypeError: decoding str is not supported Signed-off-by: Chinmay D. Pai * chore: remove useless encode Signed-off-by: Chinmay D. Pai * fix: Allow Rename in Website Route Meta * fix: Add Visit Web Page button in form * fix: next schedule date should be on or after current date * test: next schedule date * fix: Change modified by * fix: do not allow bulk update for core, single doctypes and doctypes from inactive domains * fix: switch to safe_decode * test: Fix list_view test by removing clear-cache code (#8941) * test: Fix listview test by waiting for clear-cache * test: Move wait to list_view.js * test: Try removing clear-cache * fix(integration): Social Login Key (#8940) add missing field * fix: don't set filter for restrict_to_domain in Bulk Update Tool * fix: query report chart options * fix: Default and company address fixes * fix: revert changes made to get_default_address function * fix: Remove changes in notifications * fix: list view group by filter ambiguous column name * fix: changelog modal ui * test: list view child table filter with created by filter * fix: not able to download XML file * 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 * fix: Pin faker version to avoid test failures * perf: optimise globals search * fix: add index on child table * fix: do not pop item from list * fix: dont add index for global search doctype * fix: rename function to set * tests: fix results being return * fix: codacy fixes * fix: Code cleanup - Pass values so that db cursor can handle escaping * fix: Convert list to tuple * style: added semi-colon * fix: add default role on sign up via SSO * fix(autocomplete): parse options (#8999) * fix(autocomplete): parse options * fix: add ignore validation flag * fix: minor change * fix(email): try to encode email part to utf-8 (#8964) 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 * fix: Ambiguous column error when going to next doc (#9005) fix: Ambiguous column error when going to next doc Co-authored-by: Prssanna Desai * 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 * revert: changes in currency formatting (#9003) * 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 * fix(filters): set default id to empty string instead of 0 (#9014) fixes issue where "undefined" value gets set to 0 in postgres, which causes ProgrammingError since the expected value is of type str and not int * revert: changes in currency formatting (#9003) * 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 * feat: add doc for email linking * fix: missing commits while reverting currency changes * fix(file): Public-private issue (#9032) * 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 * fix: Show default currency as a fallback for currency fieldtype (#8992) * fix: Show default currency as a fallback for currency fieldtype * test: Add a test for currency formatting * fix: Add a patch to fix all user's home settings (#9040) * fix: Add a patch to fix all user's home settings * fix: Remove limit * fix: Allow field of submitted doc to edit if field has allow_on_submit enabled * test: Add test to check if field is editable * fix: Typo * test: Add assert statement for value * test: Clear cache after creating doctype * fix: Codacy * fix checkbox behavior in rtl view when clicking on label to check (#8898) * fix checkbox behavior in rtl view when clicking on label to check * fix: Undo changes in desk.css * fix: Empty row validation * style: Fix code formatting * fix: Terminate regex * fix: don't validate email for standard filters * fix: before setting user default value, if fieldtype is Link check if doc exists * Revert "fix: user default value for Link fields" * before setting user default value, if fieldtype is Link check if doc exists * fix: Remove Attachment on remove * fix: minor changes * style: fix Codacy issue There should be no space before ','. (comma-spacing) * fix: rfc5322 compliance for python3 (#9056) * start of tests * rfc compliant emails for frappe. * fix for deepsource * fix length of strings. * fix bug that I'm not sure where it came from? * codacy and deepsource issues trying to keep them happy * take a punt in the dark * fix: use SMTPUTF8 instead of SMTP and other minor fixes Signed-off-by: Chinmay D. Pai * fix: patch home_settings_for_all_users * style: Fix Codacy * fix: KeyError: 'hidden_modules' * fix: TypeError in home_settings patch Convert set to list * refactor: override style for changelog modal * Merge V12-pre-release to version-12-hotfix (#9094) Merge V12-pre-release to version-12-hotfix Co-authored-by: null Co-authored-by: Prssanna Desai Co-authored-by: Chinmay Pai Co-authored-by: Vishal Dhayagude Co-authored-by: Himanshu Co-authored-by: null <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: null * Revert queue.py Co-authored-by: Ben Knowles Co-authored-by: Aditya Hase Co-authored-by: Shridhar Patil Co-authored-by: Faris Ansari Co-authored-by: Chinmay Pai Co-authored-by: Rucha Mahabal Co-authored-by: Revant Nandgaonkar Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Shivam Mishra Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Co-authored-by: rohitwaghchaure Co-authored-by: Himanshu Co-authored-by: Rohan Co-authored-by: Mangesh-Khairnar Co-authored-by: Prssanna Desai Co-authored-by: Saqib Co-authored-by: Nabin Hait Co-authored-by: Ammar Hararah Co-authored-by: Vishal Dhayagude Co-authored-by: sahil28297 <37302950+sahil28297@users.noreply.github.com> --- frappe/__init__.py | 2 +- frappe/automation/doctype/auto_repeat/auto_repeat.py | 7 +++---- frappe/desk/page/setup_wizard/install_fixtures.py | 4 +++- .../doctype/social_login_key/social_login_key.json | 2 +- frappe/patches.txt | 2 +- frappe/public/js/frappe/form/controls/int.js | 2 +- frappe/public/js/frappe/views/reports/query_report.js | 6 ++++-- frappe/public/less/desk.less | 4 ++-- frappe/tests/ui_test_helpers.py | 2 +- 9 files changed, 17 insertions(+), 14 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index cb1b6e5358..b383ae958e 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -23,7 +23,7 @@ if sys.version[0] == '2': reload(sys) sys.setdefaultencoding("utf-8") -__version__ = '12.0.20' +__version__ = '12.1.0' __title__ = "Frappe Framework" local = Local() diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.py b/frappe/automation/doctype/auto_repeat/auto_repeat.py index 27f17a1a62..e618c7d63e 100644 --- a/frappe/automation/doctype/auto_repeat/auto_repeat.py +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.py @@ -117,8 +117,8 @@ class AutoRepeat(Document): start_date = get_next_schedule_date(start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day) if self.end_date: - start_date = start_date = get_next_schedule_date( - start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day, end_date, for_full_schedule=True) + start_date = get_next_schedule_date( + start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day, for_full_schedule=True) while (getdate(start_date) < getdate(end_date)): row = { "reference_document" : self.reference_document, @@ -126,10 +126,9 @@ class AutoRepeat(Document): "next_scheduled_date" : start_date } schedule_details.append(row) - start_date = start_date = get_next_schedule_date( + start_date = get_next_schedule_date( start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day, end_date, for_full_schedule=True) - return schedule_details def create_documents(self): diff --git a/frappe/desk/page/setup_wizard/install_fixtures.py b/frappe/desk/page/setup_wizard/install_fixtures.py index e93b71291f..e7e147fb7d 100644 --- a/frappe/desk/page/setup_wizard/install_fixtures.py +++ b/frappe/desk/page/setup_wizard/install_fixtures.py @@ -27,4 +27,6 @@ def setup_email_linking(): doc = frappe.get_doc({ "doctype": "Email Account", "email_id": "email_linking@example.com", - }).insert(ignore_permissions=True, ignore_if_duplicate=True) \ No newline at end of file + }) + doc.insert(ignore_permissions=True, ignore_if_duplicate=True) + \ No newline at end of file diff --git a/frappe/integrations/doctype/social_login_key/social_login_key.json b/frappe/integrations/doctype/social_login_key/social_login_key.json index 290aae0e4e..6c0fbdb26c 100644 --- a/frappe/integrations/doctype/social_login_key/social_login_key.json +++ b/frappe/integrations/doctype/social_login_key/social_login_key.json @@ -157,7 +157,7 @@ "label": "User ID Property" } ], - "modified": "2019-12-03 12:35:55.115260", + "modified": "2019-12-03 13:13:46.989099", "modified_by": "Administrator", "module": "Integrations", "name": "Social Login Key", diff --git a/frappe/patches.txt b/frappe/patches.txt index 9612994168..1599fd50ac 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -260,6 +260,6 @@ frappe.patches.v12_0.update_auto_repeat_status_and_not_submittable frappe.patches.v12_0.copy_to_parent_for_tags frappe.patches.v12_0.create_notification_settings_for_user frappe.patches.v11_0.make_all_prepared_report_attachments_private #2019-11-26 -execute:frappe.delete_doc("Test Runner") frappe.patches.v12_0.setup_email_linking frappe.patches.v12_0.fix_home_settings_for_all_users +execute:frappe.delete_doc("Test Runner") \ No newline at end of file diff --git a/frappe/public/js/frappe/form/controls/int.js b/frappe/public/js/frappe/form/controls/int.js index 5639e5f132..91dee45838 100644 --- a/frappe/public/js/frappe/form/controls/int.js +++ b/frappe/public/js/frappe/form/controls/int.js @@ -21,7 +21,7 @@ frappe.ui.form.ControlInt = frappe.ui.form.ControlData.extend({ }, eval_expression: function(value) { if (typeof value === 'string') { - if (value.match(/^[0-9\+\-\/\* ]+$/)) { + if (value.match(/^[0-9+\-/* ]+$/)) { // If it is a string containing operators try { return eval(value); diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index c7e976306d..1316e3c403 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -966,8 +966,10 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { } get_data_for_csv(include_indentation) { - const indices = this.datatable.bodyRenderer.visibleRowIndices; - const rows = indices.map(i => this.datatable.datamanager.getRow(i)); + const rows = this.datatable.bodyRenderer.visibleRows; + if (this.raw_data.add_total_row) { + rows.push(this.datatable.bodyRenderer.getTotalRow()); + } return rows.map(row => { const standard_column_count = this.datatable.datamanager.getStandardColumnCount(); return row diff --git a/frappe/public/less/desk.less b/frappe/public/less/desk.less index b4482b6d7d..6232f7feae 100644 --- a/frappe/public/less/desk.less +++ b/frappe/public/less/desk.less @@ -771,7 +771,7 @@ li.user-progress { // custom font awesome checkbox input[type="checkbox"] { position: relative; - left: -999999px; + visibility: hidden; &:before { position: absolute; @@ -787,7 +787,7 @@ input[type="checkbox"] { font-size: 14px; color: @text-extra-muted; .transition(150ms color); - left: 999999px; + left: 0px; } &:focus:before { diff --git a/frappe/tests/ui_test_helpers.py b/frappe/tests/ui_test_helpers.py index 3a58fbc379..7442bf3d46 100644 --- a/frappe/tests/ui_test_helpers.py +++ b/frappe/tests/ui_test_helpers.py @@ -91,4 +91,4 @@ def insert_contact(first_name, phone_number): 'first_name': first_name }) doc.append('phone_nos', {'phone': phone_number}) - doc.insert() \ No newline at end of file + doc.insert()