From b8fbed0f667af953c5ead931e444416d751bdaed Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Sun, 11 Jul 2021 17:19:34 +0530 Subject: [PATCH 001/138] feat: New Print Format Builder - Print Format Builder Beta page - Add margin fields in Print Format - Using vuedraggable for drag and drop --- .../doctype/print_format/print_format.json | 39 +++- .../doctype/print_format/print_format.py | 4 + frappe/printing/page/print/print.js | 6 + .../print_format_builder.js | 18 +- .../print_format_builder.py | 9 +- .../print_format_builder_beta/__init__.py | 0 .../print_format_builder_beta.css | 3 + .../print_format_builder_beta.js | 31 +++ .../print_format_builder_beta.json | 22 ++ .../js/print_format_builder/PrintFormat.vue | 73 +++++++ .../PrintFormatBuilder.vue | 110 ++++++++++ .../PrintFormatControls.vue | 163 ++++++++++++++ .../PrintFormatSection.vue | 203 ++++++++++++++++++ .../print_format_builder.bundle.js | 31 +++ .../public/js/print_format_builder/utils.js | 100 +++++++++ package.json | 3 +- yarn.lock | 12 ++ 17 files changed, 815 insertions(+), 12 deletions(-) create mode 100644 frappe/printing/page/print_format_builder_beta/__init__.py create mode 100644 frappe/printing/page/print_format_builder_beta/print_format_builder_beta.css create mode 100644 frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js create mode 100644 frappe/printing/page/print_format_builder_beta/print_format_builder_beta.json create mode 100644 frappe/public/js/print_format_builder/PrintFormat.vue create mode 100644 frappe/public/js/print_format_builder/PrintFormatBuilder.vue create mode 100644 frappe/public/js/print_format_builder/PrintFormatControls.vue create mode 100644 frappe/public/js/print_format_builder/PrintFormatSection.vue create mode 100644 frappe/public/js/print_format_builder/print_format_builder.bundle.js create mode 100644 frappe/public/js/print_format_builder/utils.js diff --git a/frappe/printing/doctype/print_format/print_format.json b/frappe/printing/doctype/print_format/print_format.json index 4032cef209..52220ed67c 100644 --- a/frappe/printing/doctype/print_format/print_format.json +++ b/frappe/printing/doctype/print_format/print_format.json @@ -19,6 +19,10 @@ "html", "raw_commands", "section_break_9", + "margin_top", + "margin_bottom", + "margin_left", + "margin_right", "align_labels_right", "show_section_headings", "line_breaks", @@ -31,7 +35,8 @@ "section_break_13", "print_format_help", "format_data", - "print_format_builder" + "print_format_builder", + "print_format_builder_beta" ], "fields": [ { @@ -205,13 +210,43 @@ "fieldname": "absolute_value", "fieldtype": "Check", "label": "Show Absolute Values" + }, + { + "default": "0", + "fieldname": "print_format_builder_beta", + "fieldtype": "Check", + "label": "Print Format Builder Beta" + }, + { + "default": "15", + "fieldname": "margin_top", + "fieldtype": "Float", + "label": "Margin Top" + }, + { + "default": "15", + "fieldname": "margin_bottom", + "fieldtype": "Float", + "label": "Margin Bottom" + }, + { + "default": "15", + "fieldname": "margin_left", + "fieldtype": "Float", + "label": "Margin Left" + }, + { + "default": "15", + "fieldname": "margin_right", + "fieldtype": "Float", + "label": "Margin Right" } ], "icon": "fa fa-print", "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2021-03-01 15:25:46.578863", + "modified": "2021-07-11 11:53:52.028982", "modified_by": "Administrator", "module": "Printing", "name": "Print Format", diff --git a/frappe/printing/doctype/print_format/print_format.py b/frappe/printing/doctype/print_format/print_format.py index 878a864b38..2049c4105c 100644 --- a/frappe/printing/doctype/print_format/print_format.py +++ b/frappe/printing/doctype/print_format/print_format.py @@ -38,6 +38,10 @@ class PrintFormat(Document): def extract_images(self): from frappe.core.doctype.file.file import extract_images_from_html + + if self.print_format_builder_beta: + return + if self.format_data: data = json.loads(self.format_data) for df in data: diff --git a/frappe/printing/page/print/print.js b/frappe/printing/page/print/print.js index ca2a340661..225c06980e 100644 --- a/frappe/printing/page/print/print.js +++ b/frappe/printing/page/print/print.js @@ -258,6 +258,11 @@ frappe.ui.form.PrintView = class { fieldtype: 'Read Only', default: print_format.name || 'Standard', }, + { + label: __('Use the new Print Format Builder Beta'), + fieldname: 'beta', + fieldtype: 'Check' + }, ], (data) => { frappe.route_options = { @@ -265,6 +270,7 @@ frappe.ui.form.PrintView = class { doctype: this.frm.doctype, name: data.print_format_name, based_on: data.based_on, + beta: data.beta }; frappe.set_route('print-format-builder'); this.print_sel.val(data.print_format_name); diff --git a/frappe/printing/page/print_format_builder/print_format_builder.js b/frappe/printing/page/print_format_builder/print_format_builder.js index ca2a8bc378..e2d00bd53d 100644 --- a/frappe/printing/page/print_format_builder/print_format_builder.js +++ b/frappe/printing/page/print_format_builder/print_format_builder.js @@ -12,9 +12,9 @@ frappe.pages['print-format-builder'].on_page_show = function(wrapper) { }); } else if(frappe.route_options) { if(frappe.route_options.make_new) { - let { doctype, name, based_on } = frappe.route_options; + let { doctype, name, based_on, beta } = frappe.route_options; frappe.route_options = null; - frappe.print_format_builder.setup_new_print_format(doctype, name, based_on); + frappe.print_format_builder.setup_new_print_format(doctype, name, based_on, beta); } else { frappe.print_format_builder.print_format = frappe.route_options.doc; frappe.route_options = null; @@ -126,18 +126,22 @@ frappe.PrintFormatBuilder = class PrintFormatBuilder { }); } - setup_new_print_format(doctype, name, based_on) { + setup_new_print_format(doctype, name, based_on, beta) { frappe.call({ method: 'frappe.printing.page.print_format_builder.print_format_builder.create_custom_format', args: { doctype: doctype, name: name, - based_on: based_on + based_on: based_on, + beta: Boolean(beta) }, callback: (r) => { - if(!r.exc) { - if(r.message) { - this.print_format = r.message; + if(r.message) { + let print_format = r.message; + if (print_format.print_format_builder_beta) { + frappe.set_route('print-format-builder-beta', print_format.name); + } else { + this.print_format = print_format; this.refresh(); } } diff --git a/frappe/printing/page/print_format_builder/print_format_builder.py b/frappe/printing/page/print_format_builder/print_format_builder.py index d9f57762b0..fae564d3c3 100644 --- a/frappe/printing/page/print_format_builder/print_format_builder.py +++ b/frappe/printing/page/print_format_builder/print_format_builder.py @@ -1,11 +1,16 @@ import frappe @frappe.whitelist() -def create_custom_format(doctype, name, based_on='Standard'): +def create_custom_format(doctype, name, based_on='Standard', beta=False): doc = frappe.new_doc('Print Format') doc.doc_type = doctype doc.name = name - doc.print_format_builder = 1 + beta = frappe.parse_json(beta) + + if beta: + doc.print_format_builder_beta = 1 + else: + doc.print_format_builder = 1 doc.format_data = frappe.db.get_value('Print Format', based_on, 'format_data') \ if based_on != 'Standard' else None doc.insert() diff --git a/frappe/printing/page/print_format_builder_beta/__init__.py b/frappe/printing/page/print_format_builder_beta/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.css b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.css new file mode 100644 index 0000000000..0bd8d9c0f3 --- /dev/null +++ b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.css @@ -0,0 +1,3 @@ +.layout-main-section-wrapper { + margin-bottom: 0; +} diff --git a/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js new file mode 100644 index 0000000000..9004dd8b98 --- /dev/null +++ b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js @@ -0,0 +1,31 @@ +frappe.pages["print-format-builder-beta"].on_page_load = function(wrapper) { + var page = frappe.ui.make_app_page({ + parent: wrapper, + title: __("Print Format Builder"), + single_column: true + }); + + function load_print_format_builder_beta() { + let route = frappe.get_route(); + let $parent = $(wrapper).find(".layout-main-section"); + $parent.empty(); + + if (route.length > 1) { + frappe.require("print_format_builder.bundle.js").then(() => { + frappe.print_format_builder = new frappe.ui.PrintFormatBuilder({ + wrapper: $parent, + page, + print_format: route[1] + }); + }); + } + } + + load_print_format_builder_beta(); + + // hot reload in development + if (frappe.boot.developer_mode) { + frappe.hot_update = frappe.hot_update || []; + frappe.hot_update.push(load_print_format_builder_beta); + } +}; diff --git a/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.json b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.json new file mode 100644 index 0000000000..a5b1288bc0 --- /dev/null +++ b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.json @@ -0,0 +1,22 @@ +{ + "content": null, + "creation": "2021-07-10 12:22:16.138485", + "docstatus": 0, + "doctype": "Page", + "idx": 0, + "modified": "2021-07-10 12:22:16.138485", + "modified_by": "Administrator", + "module": "Printing", + "name": "print-format-builder-beta", + "owner": "Administrator", + "page_name": "Print Format Builder Beta", + "roles": [ + { + "role": "System Manager" + } + ], + "script": null, + "standard": "Yes", + "style": null, + "system_page": 0 +} \ No newline at end of file diff --git a/frappe/public/js/print_format_builder/PrintFormat.vue b/frappe/public/js/print_format_builder/PrintFormat.vue new file mode 100644 index 0000000000..9e8e2033a4 --- /dev/null +++ b/frappe/public/js/print_format_builder/PrintFormat.vue @@ -0,0 +1,73 @@ + + + + + \ No newline at end of file diff --git a/frappe/public/js/print_format_builder/PrintFormatBuilder.vue b/frappe/public/js/print_format_builder/PrintFormatBuilder.vue new file mode 100644 index 0000000000..88b70f8b1b --- /dev/null +++ b/frappe/public/js/print_format_builder/PrintFormatBuilder.vue @@ -0,0 +1,110 @@ + + + + + \ No newline at end of file diff --git a/frappe/public/js/print_format_builder/PrintFormatControls.vue b/frappe/public/js/print_format_builder/PrintFormatControls.vue new file mode 100644 index 0000000000..b4f452751a --- /dev/null +++ b/frappe/public/js/print_format_builder/PrintFormatControls.vue @@ -0,0 +1,163 @@ + + + + + \ No newline at end of file diff --git a/frappe/public/js/print_format_builder/PrintFormatSection.vue b/frappe/public/js/print_format_builder/PrintFormatSection.vue new file mode 100644 index 0000000000..e204c2053f --- /dev/null +++ b/frappe/public/js/print_format_builder/PrintFormatSection.vue @@ -0,0 +1,203 @@ + + + + + \ No newline at end of file diff --git a/frappe/public/js/print_format_builder/print_format_builder.bundle.js b/frappe/public/js/print_format_builder/print_format_builder.bundle.js new file mode 100644 index 0000000000..e364bfa29e --- /dev/null +++ b/frappe/public/js/print_format_builder/print_format_builder.bundle.js @@ -0,0 +1,31 @@ +import PrintFormatBuilderComponent from "./PrintFormatBuilder.vue"; + +class PrintFormatBuilder { + constructor({ wrapper, page, print_format }) { + this.$wrapper = $(wrapper); + this.page = page; + this.print_format = print_format; + this.page.set_title(__("Editing {0}", [this.print_format])); + this.page.set_primary_action(__("Save changes"), () => { + this.$component.save_changes(); + }); + this.page.set_secondary_action(__("Reset changes"), () => { + this.$component.reset_changes(); + }); + + let $vm = new Vue({ + el: this.$wrapper.get(0), + render: h => + h(PrintFormatBuilderComponent, { + props: { + print_format_name: print_format + } + }) + }); + this.$component = $vm.$children[0]; + } +} + +frappe.provide("frappe.ui"); +frappe.ui.PrintFormatBuilder = PrintFormatBuilder; +export default PrintFormatBuilder; diff --git a/frappe/public/js/print_format_builder/utils.js b/frappe/public/js/print_format_builder/utils.js new file mode 100644 index 0000000000..9428ca61f9 --- /dev/null +++ b/frappe/public/js/print_format_builder/utils.js @@ -0,0 +1,100 @@ +export function create_default_layout(meta) { + let layout = { + sections: [] + }; + + let section = null, + column = null; + + function set_column(df) { + if (!section) { + set_section(); + } + column = get_new_column(df); + section.columns.push(column); + } + + function set_section(df) { + section = get_new_section(df); + column = null; + layout.sections.push(section); + } + + function get_new_section(df) { + if (!df) { + df = { label: "" }; + } + return { + label: df.label || "", + columns: [] + }; + } + + function get_new_column(df) { + if (!df) { + df = { label: "" }; + } + return { + label: df.label || "", + fields: [] + }; + } + + for (let df of meta.fields) { + if (df.fieldname) { + // make a copy to avoid mutation bugs + df = JSON.parse(JSON.stringify(df)); + } else { + continue; + } + + if (df.fieldtype === "Section Break") { + set_section(df); + } else if (df.fieldtype === "Column Break") { + set_column(df); + } else if (df.label) { + if (!column) set_column(); + + if (!df.print_hide) { + let field = { + label: df.label, + fieldname: df.fieldname, + options: df.options + }; + + if (df.fieldtype === "Table") { + field.table_columns = get_table_columns(df); + } + + column.fields.push(field); + section.has_fields = true; + } + } + } + + // remove empty sections + layout.sections = layout.sections.filter(section => section.has_fields); + + return layout; +} + +export function get_table_columns(df) { + let table_columns = []; + let table_fields = frappe.get_meta(df.options).fields; + + for (let tf of table_fields) { + if ( + !in_list(["Section Break", "Column Break"], tf.fieldtype) && + !tf.print_hide && + df.label + ) { + table_columns.push({ + label: tf.label, + fieldname: tf.fieldname, + options: tf.options, + width: tf.width || 0 + }); + } + } + return table_columns; +} diff --git a/package.json b/package.json index 2283a44533..82a335c2ba 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,8 @@ "superagent": "^3.8.2", "touch": "^3.1.0", "vue": "2.6.12", - "vue-router": "^2.0.0" + "vue-router": "^2.0.0", + "vuedraggable": "^2.24.3" }, "devDependencies": { "chalk": "^2.3.2", diff --git a/yarn.lock b/yarn.lock index ee530d747b..cdabdb6565 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6911,6 +6911,11 @@ socket.io@^2.4.0: socket.io-client "2.4.0" socket.io-parser "~3.4.0" +sortablejs@1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.10.2.tgz#6e40364d913f98b85a14f6678f92b5c1221f5290" + integrity sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A== + sortablejs@^1.7.0: version "1.8.3" resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.8.3.tgz#5ae908ef96300966e95440a143340f5dd565a0df" @@ -7790,6 +7795,13 @@ vue@2.6.12: resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.12.tgz#f5ebd4fa6bd2869403e29a896aed4904456c9123" integrity sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg== +vuedraggable@^2.24.3: + version "2.24.3" + resolved "https://registry.yarnpkg.com/vuedraggable/-/vuedraggable-2.24.3.tgz#43c93849b746a24ce503e123d5b259c701ba0d19" + integrity sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g== + dependencies: + sortablejs "1.10.2" + wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" From f58254db7870d0d95f85636a3b81508c4ee37e09 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Sun, 11 Jul 2021 17:21:06 +0530 Subject: [PATCH 002/138] fix: poor man's hot reload --- esbuild/esbuild.js | 41 ++++++++++--------- .../build_events/build_events.bundle.js | 28 +++++++++++++ frappe/sessions.py | 4 ++ 3 files changed, 54 insertions(+), 19 deletions(-) diff --git a/esbuild/esbuild.js b/esbuild/esbuild.js index 9074beae06..442846c73f 100644 --- a/esbuild/esbuild.js +++ b/esbuild/esbuild.js @@ -280,10 +280,24 @@ function get_watch_config() { assets_json, prev_assets_json } = await write_assets_json(result.metafile); + + let changed_files; if (prev_assets_json) { - log_rebuilt_assets(prev_assets_json, assets_json); + changed_files = get_rebuilt_assets( + prev_assets_json, + assets_json + ); + + let timestamp = new Date().toLocaleTimeString(); + let message = `${timestamp}: Compiled ${changed_files.length} files...`; + log(chalk.yellow(message)); + for (let filepath of changed_files) { + let filename = path.basename(filepath); + log(" " + filename); + } + log(); } - notify_redis({ success: true }); + notify_redis({ success: true, changed_files }); } } }; @@ -453,7 +467,7 @@ function run_build_command_for_apps(apps) { process.chdir(cwd); } -async function notify_redis({ error, success }) { +async function notify_redis({ error, success, changed_files }) { // notify redis which in turns tells socketio to publish this to browser let subscriber = get_redis_subscriber("redis_socketio"); subscriber.on("error", _ => { @@ -475,7 +489,8 @@ async function notify_redis({ error, success }) { } if (success) { payload = { - success: true + success: true, + changed_files }; } @@ -505,7 +520,7 @@ function open_in_editor() { subscriber.subscribe("open_in_editor"); } -function log_rebuilt_assets(prev_assets, new_assets) { +function get_rebuilt_assets(prev_assets, new_assets) { let added_files = []; let old_files = Object.values(prev_assets); let new_files = Object.values(new_assets); @@ -515,17 +530,5 @@ function log_rebuilt_assets(prev_assets, new_assets) { added_files.push(filepath); } } - - log( - chalk.yellow( - `${new Date().toLocaleTimeString()}: Compiled ${ - added_files.length - } files...` - ) - ); - for (let filepath of added_files) { - let filename = path.basename(filepath); - log(" " + filename); - } - log(); -} \ No newline at end of file + return added_files; +} diff --git a/frappe/public/js/frappe/build_events/build_events.bundle.js b/frappe/public/js/frappe/build_events/build_events.bundle.js index 6c8986af3f..6180ccbd66 100644 --- a/frappe/public/js/frappe/build_events/build_events.bundle.js +++ b/frappe/public/js/frappe/build_events/build_events.bundle.js @@ -7,6 +7,34 @@ let error = null; frappe.realtime.on("build_event", data => { if (data.success) { + // remove executed cache for rebuilt files + let changed_files = data.changed_files; + if (Array.isArray(changed_files)) { + for (let file of changed_files) { + if (file.includes(".bundle.")) { + let parts = file.split(".bundle."); + if (parts.length === 2) { + let filename = parts[0].split("/").slice(-1)[0]; + + frappe.assets.executed_ = frappe.assets.executed_.filter( + asset => !asset.includes(`${filename}.bundle`) + ); + } + } + } + } + // update assets json + frappe.call("frappe.sessions.get_boot_assets_json").then(r => { + if (r.message) { + frappe.boot.assets_json = r.message; + + if (frappe.hot_update) { + frappe.hot_update.forEach(callback => { + callback(); + }); + } + } + }); show_build_success(data); } else if (data.error) { show_build_error(data); diff --git a/frappe/sessions.py b/frappe/sessions.py index ce104968ad..8a8355ec75 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -159,6 +159,10 @@ def get(): return bootinfo +@frappe.whitelist() +def get_boot_assets_json(): + return get_assets_json() + def get_csrf_token(): if not frappe.local.session.data.csrf_token: generate_csrf_token() From f536a1ff91db91397ff09d2e996014c09ff80aba Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Sun, 15 Aug 2021 16:10:04 +0530 Subject: [PATCH 003/138] fix: More features - Show selection dialog if Print Format not selected - Field component - Common store - Edit Field label inline - Configure table columns - Edit Custom HTML - Preview --- .../print_format_builder_beta.js | 20 + .../print_format_builder/ConfigureColumns.vue | 86 +++++ .../public/js/print_format_builder/Field.vue | 345 ++++++++++++++++++ .../js/print_format_builder/PrintFormat.vue | 21 +- .../PrintFormatBuilder.vue | 94 +---- .../PrintFormatControls.vue | 61 +++- .../PrintFormatSection.vue | 62 +--- .../print_format_builder.bundle.js | 55 ++- .../public/js/print_format_builder/store.js | 125 +++++++ .../public/js/print_format_builder/utils.js | 22 +- 10 files changed, 732 insertions(+), 159 deletions(-) create mode 100644 frappe/public/js/print_format_builder/ConfigureColumns.vue create mode 100644 frappe/public/js/print_format_builder/Field.vue create mode 100644 frappe/public/js/print_format_builder/store.js diff --git a/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js index 9004dd8b98..b40e8c6f97 100644 --- a/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js +++ b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js @@ -18,6 +18,26 @@ frappe.pages["print-format-builder-beta"].on_page_load = function(wrapper) { print_format: route[1] }); }); + } else { + let d = new frappe.ui.Dialog({ + title: __("Select Print Format to edit"), + fields: [ + { + label: __("Print Format"), + fieldname: "print_format", + fieldtype: "Link", + options: "Print Format", + filters: { + print_format_builder_beta: 1 + } + } + ], + primary_action({ print_format }) { + if (!print_format) return; + frappe.set_route("print-format-builder-beta", print_format); + } + }); + d.show(); } } diff --git a/frappe/public/js/print_format_builder/ConfigureColumns.vue b/frappe/public/js/print_format_builder/ConfigureColumns.vue new file mode 100644 index 0000000000..8966595216 --- /dev/null +++ b/frappe/public/js/print_format_builder/ConfigureColumns.vue @@ -0,0 +1,86 @@ + + + diff --git a/frappe/public/js/print_format_builder/Field.vue b/frappe/public/js/print_format_builder/Field.vue new file mode 100644 index 0000000000..d82015f51c --- /dev/null +++ b/frappe/public/js/print_format_builder/Field.vue @@ -0,0 +1,345 @@ + + + diff --git a/frappe/public/js/print_format_builder/PrintFormat.vue b/frappe/public/js/print_format_builder/PrintFormat.vue index 9e8e2033a4..d10eb88a6f 100644 --- a/frappe/public/js/print_format_builder/PrintFormat.vue +++ b/frappe/public/js/print_format_builder/PrintFormat.vue @@ -19,13 +19,14 @@ @@ -70,4 +71,4 @@ export default { box-shadow: var(--shadow-lg); border-radius: var(--border-radius); } - \ No newline at end of file + diff --git a/frappe/public/js/print_format_builder/PrintFormatBuilder.vue b/frappe/public/js/print_format_builder/PrintFormatBuilder.vue index 88b70f8b1b..78205406e3 100644 --- a/frappe/public/js/print_format_builder/PrintFormatBuilder.vue +++ b/frappe/public/js/print_format_builder/PrintFormatBuilder.vue @@ -1,14 +1,10 @@ @@ -16,87 +12,33 @@ @@ -107,4 +49,4 @@ export default { padding-top: 0.5rem; padding-bottom: 4rem; } - \ No newline at end of file + diff --git a/frappe/public/js/print_format_builder/PrintFormatControls.vue b/frappe/public/js/print_format_builder/PrintFormatControls.vue index b4f452751a..dd54f8f55e 100644 --- a/frappe/public/js/print_format_builder/PrintFormatControls.vue +++ b/frappe/public/js/print_format_builder/PrintFormatControls.vue @@ -14,7 +14,7 @@ type="number" class="form-control form-control-sm" :value="print_format[df.fieldname]" - @change="(e) => update_margin(df.fieldname, e.target.value)" + @change="e => update_margin(df.fieldname, e.target.value)" /> @@ -24,7 +24,7 @@ @@ -74,19 +62,23 @@ @@ -166,38 +158,8 @@ export default { padding-right: 8px; } -.field { - display: flex; - justify-content: space-between; - align-items: center; - width: 100%; - background-color: var(--bg-light-gray); - border-radius: var(--border-radius); - border: 1px dashed var(--gray-400); - padding: 0.5rem 0.75rem; - font-size: var(--text-sm); -} - -.field:not(:first-child) { - margin-top: 0.5rem; -} - -.btn-remove-field { - opacity: 0; - padding: 2px; - box-shadow: none; -} - -.btn-remove-field:hover { - background-color: white; -} - -.field:hover .btn-remove-field { - opacity: 1; -} - .drag-container { height: 100%; min-height: 2rem; } - \ No newline at end of file + diff --git a/frappe/public/js/print_format_builder/print_format_builder.bundle.js b/frappe/public/js/print_format_builder/print_format_builder.bundle.js index e364bfa29e..63cc098380 100644 --- a/frappe/public/js/print_format_builder/print_format_builder.bundle.js +++ b/frappe/public/js/print_format_builder/print_format_builder.bundle.js @@ -5,13 +5,24 @@ class PrintFormatBuilder { this.$wrapper = $(wrapper); this.page = page; this.print_format = print_format; + + this.page.clear_actions() + this.page.clear_custom_actions() + this.page.set_title(__("Editing {0}", [this.print_format])); this.page.set_primary_action(__("Save changes"), () => { - this.$component.save_changes(); + this.$component.$store.save_changes(); }); this.page.set_secondary_action(__("Reset changes"), () => { - this.$component.reset_changes(); + this.$component.$store.reset_changes(); }); + this.page.add_button( + __("Preview"), + () => { + this.preview(); + }, + { icon: "small-file" } + ); let $vm = new Vue({ el: this.$wrapper.get(0), @@ -24,6 +35,46 @@ class PrintFormatBuilder { }); this.$component = $vm.$children[0]; } + + async preview() { + let doctype = this.$component.$store.print_format.doc_type; + let default_doc = await frappe.db.get_list(doctype, { + limit: 1 + }); + + let d = new frappe.ui.Dialog({ + title: __("Preview Print Format"), + fields: [ + { + label: __("Type"), + fieldname: "type", + fieldtype: "Select", + options: ["PDF", "HTML"], + default: "PDF" + }, + { + label: __("Select Document"), + fieldname: "docname", + fieldtype: "Link", + options: doctype, + reqd: 1, + default: default_doc.length > 0 ? default_doc[0].name : null + } + ], + primary_action: ({ docname, type }) => { + let params = new URLSearchParams(); + params.append("doctype", doctype); + params.append("name", docname); + params.append("print_format", this.print_format); + let url = + type == "PDF" + ? `/api/method/frappe.utils.weasyprint.download_pdf` + : "/printpreview"; + window.open(`${url}?${params.toString()}`, "_blank"); + } + }); + d.show(); + } } frappe.provide("frappe.ui"); diff --git a/frappe/public/js/print_format_builder/store.js b/frappe/public/js/print_format_builder/store.js new file mode 100644 index 0000000000..3af4a21dec --- /dev/null +++ b/frappe/public/js/print_format_builder/store.js @@ -0,0 +1,125 @@ +import { create_default_layout, pluck } from "./utils"; + +let stores = {}; + +export function getStore(print_format_name) { + if (stores[print_format_name]) { + return stores[print_format_name]; + } + + let options = { + data() { + return { + print_format_name, + print_format: null, + doctype: null, + meta: null, + layout: null + }; + }, + methods: { + fetch() { + frappe.model.clear_doc("Print Format", this.print_format_name); + frappe.model.with_doc( + "Print Format", + this.print_format_name, + () => { + this.print_format = frappe.get_doc( + "Print Format", + this.print_format_name + ); + frappe.model.with_doctype( + this.print_format.doc_type, + () => { + this.meta = frappe.get_meta( + this.print_format.doc_type + ); + this.layout = this.get_layout(); + } + ); + } + ); + }, + update({ fieldname, value }) { + this.$set(this.print_format, fieldname, value); + }, + save_changes() { + frappe.dom.freeze(__("Saving...")); + + this.layout.sections = this.layout.sections + .filter(section => !section.remove) + .map(section => { + section.columns = section.columns.map(column => { + column.fields = column.fields + .filter(df => !df.remove) + .map(df => { + if (df.table_columns) { + df.table_columns = df.table_columns.map( + tf => { + return pluck(tf, [ + "label", + "fieldname", + "fieldtype", + "options", + "width" + ]); + } + ); + } + return pluck(df, [ + "label", + "fieldname", + "fieldtype", + "options", + "table_columns" + ]); + }); + return column; + }); + return section; + }); + + this.print_format.format_data = JSON.stringify(this.layout); + + frappe + .call("frappe.client.save", { + doc: this.print_format + }) + .then(() => this.fetch()) + .always(() => frappe.dom.unfreeze()); + }, + reset_changes() { + this.fetch(); + }, + get_layout() { + if (this.print_format) { + if (!this.print_format.format_data) { + return create_default_layout(this.meta); + } + if (typeof this.print_format.format_data == "string") { + return JSON.parse(this.print_format.format_data); + } + return this.print_format.format_data; + } + return null; + } + } + }; + stores[print_format_name] = new Vue(options); + return stores[print_format_name]; +} + +export let storeMixin = { + inject: ["$store"], + computed: { + print_format() { + return this.$store.print_format; + }, + layout() { + return this.$store.layout; + }, + meta() { + return this.$store.meta; + } + } +}; diff --git a/frappe/public/js/print_format_builder/utils.js b/frappe/public/js/print_format_builder/utils.js index 9428ca61f9..7bdd78a04a 100644 --- a/frappe/public/js/print_format_builder/utils.js +++ b/frappe/public/js/print_format_builder/utils.js @@ -59,6 +59,7 @@ export function create_default_layout(meta) { let field = { label: df.label, fieldname: df.fieldname, + fieldtype: df.fieldtype, options: df.options }; @@ -81,20 +82,35 @@ export function create_default_layout(meta) { export function get_table_columns(df) { let table_columns = []; let table_fields = frappe.get_meta(df.options).fields; - + let total_columns = 0; for (let tf of table_fields) { if ( !in_list(["Section Break", "Column Break"], tf.fieldtype) && !tf.print_hide && - df.label + df.label && + total_columns < 12 ) { + let columns = + typeof tf.width == "number" && tf.width < 12 ? tf.width : 2; table_columns.push({ label: tf.label, fieldname: tf.fieldname, + fieldtype: tf.fieldtype, options: tf.options, - width: tf.width || 0 + width: columns }); + total_columns += columns; } } return table_columns; } + +export function pluck(object, keys) { + let out = {}; + for (let key of keys) { + if (key in object) { + out[key] = object[key]; + } + } + return out; +} From a25817b64ce9d3d2434f2057899ce68fffb26037 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Sat, 21 Aug 2021 18:03:36 +0530 Subject: [PATCH 004/138] fix: Set table column width in percentages --- .../print_format_builder/ConfigureColumns.vue | 45 ++++++++++++++----- .../public/js/print_format_builder/Field.vue | 20 +++++---- .../public/js/print_format_builder/utils.js | 16 ++++--- 3 files changed, 56 insertions(+), 25 deletions(-) diff --git a/frappe/public/js/print_format_builder/ConfigureColumns.vue b/frappe/public/js/print_format_builder/ConfigureColumns.vue index 8966595216..da10f99e40 100644 --- a/frappe/public/js/print_format_builder/ConfigureColumns.vue +++ b/frappe/public/js/print_format_builder/ConfigureColumns.vue @@ -1,6 +1,6 @@ + diff --git a/frappe/public/js/print_format_builder/LetterHeadEditor.vue b/frappe/public/js/print_format_builder/LetterHeadEditor.vue new file mode 100644 index 0000000000..d958a6e67a --- /dev/null +++ b/frappe/public/js/print_format_builder/LetterHeadEditor.vue @@ -0,0 +1,108 @@ + + + diff --git a/frappe/public/js/print_format_builder/MarginText.vue b/frappe/public/js/print_format_builder/MarginText.vue new file mode 100644 index 0000000000..85229d2d42 --- /dev/null +++ b/frappe/public/js/print_format_builder/MarginText.vue @@ -0,0 +1,114 @@ + + + diff --git a/frappe/public/js/print_format_builder/Preview.vue b/frappe/public/js/print_format_builder/Preview.vue index 1ce89a87aa..1d795f27d7 100644 --- a/frappe/public/js/print_format_builder/Preview.vue +++ b/frappe/public/js/print_format_builder/Preview.vue @@ -97,6 +97,9 @@ export default { params.append("doctype", this.doctype); params.append("name", this.docname); params.append("print_format", this.print_format.name); + if (this.$store.letterhead) { + params.append("letterhead", this.$store.letterhead.name); + } let url = this.type == "PDF" ? `/api/method/frappe.utils.weasyprint.download_pdf` diff --git a/frappe/public/js/print_format_builder/PrintFormat.vue b/frappe/public/js/print_format_builder/PrintFormat.vue index f0f7f713ea..74cfb84133 100644 --- a/frappe/public/js/print_format_builder/PrintFormat.vue +++ b/frappe/public/js/print_format_builder/PrintFormat.vue @@ -1,6 +1,20 @@ - diff --git a/frappe/public/js/print_format_builder/PrintFormat.vue b/frappe/public/js/print_format_builder/PrintFormat.vue index 74cfb84133..7163282067 100644 --- a/frappe/public/js/print_format_builder/PrintFormat.vue +++ b/frappe/public/js/print_format_builder/PrintFormat.vue @@ -32,7 +32,12 @@ @change="$set(layout, 'footer', $event)" :button-label="__('Edit Footer')" /> - + @@ -85,6 +90,10 @@ export default { sections.push(_section); } this.$set(this.layout, "sections", sections); + }, + update_letterhead_footer(val) { + this.letterhead.footer = val; + this.letterhead._dirty = true; } } }; diff --git a/frappe/public/js/print_format_builder/store.js b/frappe/public/js/print_format_builder/store.js index f72cf0c450..57c48632c8 100644 --- a/frappe/public/js/print_format_builder/store.js +++ b/frappe/public/js/print_format_builder/store.js @@ -115,9 +115,11 @@ export function getStore(print_format_name) { }) .then(() => { if (this.letterhead && this.letterhead._dirty) { - return frappe.call("frappe.client.save", { - doc: this.letterhead - }); + return frappe + .call("frappe.client.save", { + doc: this.letterhead + }) + .then(r => (this.letterhead = r.message)); } }) .then(() => this.fetch()) @@ -125,7 +127,6 @@ export function getStore(print_format_name) { }, reset_changes() { this.fetch(); - }, get_layout() { if (this.print_format) { @@ -143,7 +144,7 @@ export function getStore(print_format_name) { return create_default_layout(this.meta); }, change_letterhead(letterhead) { - frappe.db.get_doc("Letter Head", letterhead).then(doc => { + return frappe.db.get_doc("Letter Head", letterhead).then(doc => { this.letterhead = doc; }); } diff --git a/frappe/public/js/print_format_builder/utils.js b/frappe/public/js/print_format_builder/utils.js index 77d4e2a36d..6c52b2e4a4 100644 --- a/frappe/public/js/print_format_builder/utils.js +++ b/frappe/public/js/print_format_builder/utils.js @@ -94,8 +94,8 @@ export function get_table_columns(df) { typeof tf.width == "number" && tf.width < 100 ? tf.width : tf.width - ? 20 - : 10; + ? 20 + : 10; table_columns.push({ label: tf.label, fieldname: tf.fieldname, @@ -118,3 +118,13 @@ export function pluck(object, keys) { } return out; } + +export function get_image_dimensions(src) { + return new Promise(resolve => { + let img = new Image(); + img.onload = function() { + resolve({ width: this.width, height: this.height }); + }; + img.src = src; + }); +} diff --git a/frappe/templates/print_format/print_format.html b/frappe/templates/print_format/print_format.html index 7e710e8c3f..69fb50d959 100644 --- a/frappe/templates/print_format/print_format.html +++ b/frappe/templates/print_format/print_format.html @@ -13,7 +13,7 @@ - {{ header }} + {{ header or '' }} {% for section in layout.sections %}
{% if section.label %} @@ -31,6 +31,6 @@
{% endfor %} - {{ footer }} + {{ footer or '' }} diff --git a/frappe/templates/print_format/print_header.html b/frappe/templates/print_format/print_header.html index 69a5360d6f..9b1357e08c 100644 --- a/frappe/templates/print_format/print_header.html +++ b/frappe/templates/print_format/print_header.html @@ -6,7 +6,7 @@ position: fixed; top: 0; left: 0; - width: 100%; + width: {{ body_width | int }}mm; padding-top: {{ print_format.margin_top | int }}mm; padding-left: {{ print_format.margin_left | int }}mm; padding-right: {{ print_format.margin_right | int }}mm; From a8347c0b27c7ad254cbd82f5a427eb62dfd10af8 Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Mon, 4 Oct 2021 14:15:54 -0400 Subject: [PATCH 022/138] feat: allow bulk rename with merge --- frappe/model/rename_doc.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py index 9b8ac2574d..465358e557 100644 --- a/frappe/model/rename_doc.py +++ b/frappe/model/rename_doc.py @@ -458,7 +458,7 @@ def bulk_rename(doctype, rows=None, via_console = False): """Bulk rename documents :param doctype: DocType to be renamed - :param rows: list of documents as `((oldname, newname), ..)`""" + :param rows: list of documents as `((oldname, newname, merge(optional)), ..)`""" if not rows: frappe.throw(_("Please select a valid csv file with data")) @@ -471,8 +471,9 @@ def bulk_rename(doctype, rows=None, via_console = False): for row in rows: # if row has some content if len(row) > 1 and row[0] and row[1]: + merge = True if len(row) > 2 and (row[2] == "1" or row[2].lower() == "true") else False try: - if rename_doc(doctype, row[0], row[1]): + if rename_doc(doctype, row[0], row[1], merge=merge): msg = _("Successful: {0} to {1}").format(row[0], row[1]) frappe.db.commit() else: From 67acd5d22e5865b40e76fd8b57d458301707dbeb Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 5 Oct 2021 14:21:12 +0530 Subject: [PATCH 023/138] fix: Set route_options from links --- frappe/public/js/frappe/router.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index 484f1ac911..7b26832522 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -58,6 +58,12 @@ $('body').on('click', 'a', function(e) { if (frappe.router.is_app_route(e.currentTarget.pathname)) { // target has "/app, this is a v2 style route. + + frappe.route_options = {}; + let params = new URLSearchParams(e.currentTarget.search); + for (const [key, value] of params) { + frappe.route_options[key] = value; + } return override(e.currentTarget.pathname + e.currentTarget.hash); } }); From d8457a3c71bdc2f0e73ba648f83537427faa4222 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 5 Oct 2021 14:21:57 +0530 Subject: [PATCH 024/138] fix: Show print preview on print page --- frappe/printing/page/print/print.js | 41 ++++++++- .../print_format_builder_beta.js | 83 +++++++++++++++++-- frappe/public/scss/desk/print_preview.scss | 5 ++ .../templates/print_format/print_format.css | 2 +- 4 files changed, 120 insertions(+), 11 deletions(-) diff --git a/frappe/printing/page/print/print.js b/frappe/printing/page/print/print.js index 27fc4be802..30d9fedeae 100644 --- a/frappe/printing/page/print/print.js +++ b/frappe/printing/page/print/print.js @@ -41,7 +41,11 @@ frappe.ui.form.PrintView = class {
- ` + +
+ +
+ ` ); this.print_settings = frappe.model.get_doc( @@ -134,7 +138,7 @@ frappe.ui.form.PrintView = class { add_sidebar_item(df, is_dynamic) { if (df.fieldtype == 'Select') { - df.input_class = 'btn btn-default btn-sm'; + df.input_class = 'btn btn-default btn-sm text-left'; } let field = frappe.ui.form.make_control({ @@ -187,6 +191,13 @@ frappe.ui.form.PrintView = class { this.set_breadcrumbs(); this.setup_customize_dialog(); + // print format builder beta + this.page.add_inner_message(` + + ${__('Try the new Print Format Builder')} + + `); + let tasks = [ this.refresh_print_options, this.set_default_print_language, @@ -383,6 +394,17 @@ frappe.ui.form.PrintView = class { } preview() { + let print_format = this.get_print_format(); + if (print_format.print_format_builder_beta) { + this.print_wrapper.find('.print-preview-wrapper').hide(); + this.print_wrapper.find('.preview-beta-wrapper').show(); + this.preview_beta(); + return; + } + + this.print_wrapper.find('.preview-beta-wrapper').hide(); + this.print_wrapper.find('.print-preview-wrapper').show(); + const $print_format = this.print_wrapper.find('iframe'); this.$print_format_body = $print_format.contents(); this.get_print_html((out) => { @@ -406,6 +428,21 @@ frappe.ui.form.PrintView = class { }); } + preview_beta() { + let print_format = this.get_print_format(); + const iframe = this.print_wrapper.find('.preview-beta-wrapper iframe'); + let params = new URLSearchParams({ + doctype: this.frm.doc.doctype, + name: this.frm.doc.name, + print_format: print_format.name + }); + let letterhead = this.get_letterhead(); + if (letterhead) { + params.append("letterhead", letterhead); + } + iframe.prop('src', `/printpreview?${params.toString()}`); + } + setup_print_format_dom(out, $print_format) { this.print_wrapper.find('.print-format-skeleton').remove(); let base_url = frappe.urllib.get_base_url(); diff --git a/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js index d53266d0d5..7185d17832 100644 --- a/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js +++ b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js @@ -31,23 +31,90 @@ function load_print_format_builder_beta(wrapper) { }); } else { let d = new frappe.ui.Dialog({ - title: __("Select Print Format to edit"), + title: __("Create or Edit Print Format"), fields: [ { - label: __("Print Format"), + label: __("Action"), + fieldname: "action", + fieldtype: "Select", + options: [ + { label: __("Create New"), value: "Create" }, + { label: __("Edit Existing"), value: "Edit" } + ], + change() { + let action = d.get_value("action"); + d.get_primary_btn().text( + action === "Create" ? __("Create") : __("Edit") + ); + } + }, + { + label: __("Select Document Type"), + fieldname: "doctype", + fieldtype: "Link", + options: "DocType", + filters: { + istable: 0 + }, + reqd: 1, + default: frappe.route_options + ? frappe.route_options.doctype + : null + }, + { + label: __("Print Format Name"), + fieldname: "print_format_name", + fieldtype: "Data", + depends_on: doc => doc.action === "Create", + mandatory_depends_on: doc => doc.action === "Create" + }, + { + label: __("Select Print Format"), fieldname: "print_format", fieldtype: "Link", options: "Print Format", - filters: { - print_format_builder_beta: 1 - } + only_select: 1, + depends_on: doc => doc.action === "Edit", + get_query() { + return { + filters: { + doc_type: d.get_value("doctype"), + print_format_builder_beta: 1 + } + }; + }, + mandatory_depends_on: doc => doc.action === "Edit" } ], - primary_action({ print_format }) { - if (!print_format) return; - frappe.set_route("print-format-builder-beta", print_format); + primary_action_label: __("Edit"), + primary_action({ + action, + doctype, + print_format, + print_format_name + }) { + if (action === "Edit") { + frappe.set_route("print-format-builder-beta", print_format); + } else if (action === "Create") { + d.get_primary_btn().prop("disabled", true); + frappe.db + .insert({ + doctype: "Print Format", + name: print_format_name, + doc_type: doctype, + print_format_builder_beta: 1 + }) + .then(doc => { + d.get_primary_btn().prop("disabled", false); + frappe.set_route( + "print-format-builder-beta", + doc.name + ); + }); + } } }); + d.set_value("action", "Create"); d.show(); } } diff --git a/frappe/public/scss/desk/print_preview.scss b/frappe/public/scss/desk/print_preview.scss index 3c0acc68b8..468b37fe5a 100644 --- a/frappe/public/scss/desk/print_preview.scss +++ b/frappe/public/scss/desk/print_preview.scss @@ -14,6 +14,11 @@ } } +.preview-beta-wrapper { + border-radius: var(--border-radius); + overflow: hidden; +} + .print-toolbar { margin: 0px; padding: var(--padding-md) 0; diff --git a/frappe/templates/print_format/print_format.css b/frappe/templates/print_format/print_format.css index a52dcf49aa..fd0c0be1b6 100644 --- a/frappe/templates/print_format/print_format.css +++ b/frappe/templates/print_format/print_format.css @@ -34,7 +34,7 @@ body { @media screen { html { - background-color: var(--gray-300); + background-color: var(--gray-200); } body { background-color: white; From 75a9b9c8367d45250913efc6f6a5b9f37d0a617d Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 7 Oct 2021 14:22:14 +0530 Subject: [PATCH 025/138] fix: Redirect to private home if it exist --- frappe/public/js/frappe/desk.js | 2 +- frappe/public/js/frappe/router.js | 3 ++- frappe/public/js/frappe/views/workspace/workspace.js | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index a4dc1a6709..4a4c757cc3 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -283,7 +283,7 @@ frappe.Application = class Application { frappe.workspaces = {}; for (let page of frappe.boot.allowed_workspaces || []) { frappe.modules[page.module]=page; - frappe.workspaces[frappe.router.slug(page.title)] = page; + frappe.workspaces[frappe.router.slug(page.label)] = page; } } diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index 484f1ac911..8ca9e6b0ad 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -354,7 +354,8 @@ frappe.router = { return a; } }).join('/'); - let default_page = frappe.workspaces['home'] ? 'home' : Object.keys(frappe.workspaces)[0]; + let private_home = frappe.workspaces[`home-${frappe.user.name.toLowerCase()}`]; + let default_page = private_home ? 'private/home' : frappe.workspaces['home'] ? 'home' : Object.keys(frappe.workspaces)[0]; return '/app/' + (path_string || default_page); }, diff --git a/frappe/public/js/frappe/views/workspace/workspace.js b/frappe/public/js/frappe/views/workspace/workspace.js index 41f73c8639..052f4cf159 100644 --- a/frappe/public/js/frappe/views/workspace/workspace.js +++ b/frappe/public/js/frappe/views/workspace/workspace.js @@ -66,7 +66,7 @@ frappe.views.Workspace = class Workspace { if (this.all_pages) { frappe.workspaces = {}; for (let page of this.all_pages) { - frappe.workspaces[frappe.router.slug(page.title)] = {title: page.title}; + frappe.workspaces[frappe.router.slug(page.label)] = {title: page.title}; } if (this.new_page && this.new_page.name) { if (!frappe.workspaces[frappe.router.slug(this.new_page.name)]) { From 1133f625863adb46d9fa155bec90abed03a7f2a6 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 7 Oct 2021 15:55:29 +0530 Subject: [PATCH 026/138] fix: Added translation to blocks and sidebar items --- frappe/public/js/frappe/desk.js | 2 +- frappe/public/js/frappe/views/workspace/blocks/card.js | 2 +- frappe/public/js/frappe/views/workspace/blocks/chart.js | 2 +- frappe/public/js/frappe/views/workspace/blocks/header.js | 2 +- frappe/public/js/frappe/views/workspace/blocks/paragraph.js | 2 +- frappe/public/js/frappe/views/workspace/blocks/shortcut.js | 2 +- frappe/public/js/frappe/views/workspace/workspace.js | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index 4a4c757cc3..a53368d67a 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -283,7 +283,7 @@ frappe.Application = class Application { frappe.workspaces = {}; for (let page of frappe.boot.allowed_workspaces || []) { frappe.modules[page.module]=page; - frappe.workspaces[frappe.router.slug(page.label)] = page; + frappe.workspaces[frappe.router.slug(page.name)] = page; } } diff --git a/frappe/public/js/frappe/views/workspace/blocks/card.js b/frappe/public/js/frappe/views/workspace/blocks/card.js index 15e27fed40..9b4a2ed14f 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/card.js +++ b/frappe/public/js/frappe/views/workspace/blocks/card.js @@ -30,7 +30,7 @@ export default class Card extends Block { this.new('card', 'links'); if (this.data && this.data.card_name) { - let has_data = this.make('card', this.data.card_name, 'links'); + let has_data = this.make('card', __(this.data.card_name), 'links'); if (!has_data) return; } diff --git a/frappe/public/js/frappe/views/workspace/blocks/chart.js b/frappe/public/js/frappe/views/workspace/blocks/chart.js index e41063e6fc..02e6a66e6f 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/chart.js +++ b/frappe/public/js/frappe/views/workspace/blocks/chart.js @@ -30,7 +30,7 @@ export default class Chart extends Block { this.new('chart'); if (this.data && this.data.chart_name) { - let has_data = this.make('chart', this.data.chart_name); + let has_data = this.make('chart', __(this.data.chart_name)); if (!has_data) return; } diff --git a/frappe/public/js/frappe/views/workspace/blocks/header.js b/frappe/public/js/frappe/views/workspace/blocks/header.js index 356f9c3244..a87d52fc95 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/header.js +++ b/frappe/public/js/frappe/views/workspace/blocks/header.js @@ -27,7 +27,7 @@ export default class Header extends Block { data = {}; } - newData.text = data.text || ''; + newData.text = __(data.text.replace(/(\n|\t)/gm,"")) || ''; newData.level = parseInt(data.level) || this.defaultLevel.number; newData.col = parseInt(data.col) || 12; diff --git a/frappe/public/js/frappe/views/workspace/blocks/paragraph.js b/frappe/public/js/frappe/views/workspace/blocks/paragraph.js index 26afa65d51..9e5dfb68ff 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/paragraph.js +++ b/frappe/public/js/frappe/views/workspace/blocks/paragraph.js @@ -177,7 +177,7 @@ export default class Paragraph extends Block { set data(data) { this._data = data || {}; - this._element.innerHTML = this._data.text || ''; + this._element.innerHTML = __(this._data.text) || ''; } static get pasteConfig() { diff --git a/frappe/public/js/frappe/views/workspace/blocks/shortcut.js b/frappe/public/js/frappe/views/workspace/blocks/shortcut.js index f7482a06f3..96b8f47484 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/shortcut.js +++ b/frappe/public/js/frappe/views/workspace/blocks/shortcut.js @@ -29,7 +29,7 @@ export default class Shortcut extends Block { this.new('shortcut'); if (this.data && this.data.shortcut_name) { - let has_data = this.make('shortcut', this.data.shortcut_name); + let has_data = this.make('shortcut', __(this.data.shortcut_name)); if (!has_data) return; } diff --git a/frappe/public/js/frappe/views/workspace/workspace.js b/frappe/public/js/frappe/views/workspace/workspace.js index 052f4cf159..a97d6d293f 100644 --- a/frappe/public/js/frappe/views/workspace/workspace.js +++ b/frappe/public/js/frappe/views/workspace/workspace.js @@ -66,7 +66,7 @@ frappe.views.Workspace = class Workspace { if (this.all_pages) { frappe.workspaces = {}; for (let page of this.all_pages) { - frappe.workspaces[frappe.router.slug(page.label)] = {title: page.title}; + frappe.workspaces[frappe.router.slug(page.name)] = {title: page.title}; } if (this.new_page && this.new_page.name) { if (!frappe.workspaces[frappe.router.slug(this.new_page.name)]) { @@ -94,10 +94,10 @@ frappe.views.Workspace = class Workspace { From 7230b7e8e4065e9b4114d9d4c4c9219e93d90542 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 7 Oct 2021 16:06:27 +0530 Subject: [PATCH 027/138] fix: sider fix --- frappe/public/js/frappe/views/workspace/blocks/header.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/views/workspace/blocks/header.js b/frappe/public/js/frappe/views/workspace/blocks/header.js index a87d52fc95..219ee3ebd3 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/header.js +++ b/frappe/public/js/frappe/views/workspace/blocks/header.js @@ -27,7 +27,7 @@ export default class Header extends Block { data = {}; } - newData.text = __(data.text.replace(/(\n|\t)/gm,"")) || ''; + newData.text = __(data.text.replace(/(\n|\t)/gm, "")) || ''; newData.level = parseInt(data.level) || this.defaultLevel.number; newData.col = parseInt(data.col) || 12; From f7ad01cb64812b99f97866d9010aa216b5610a49 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 7 Oct 2021 19:38:42 +0530 Subject: [PATCH 028/138] fix: Update check all checkbox based on checked rows in List View --- frappe/public/js/frappe/list/list_view.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 1cf4b4c6ac..56c47d0d8a 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -302,9 +302,20 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { refresh(refresh_header=false) { super.refresh().then(() => { this.render_header(refresh_header); + this.update_checkbox(); }); } + update_checkbox(target) { + let $check_all_checkbox = this.$checkbox_actions.find(".list-check-all"); + + if ($check_all_checkbox.prop("checked") && target && !target.prop("checked")) { + $check_all_checkbox.prop("checked", false); + } + + $check_all_checkbox.prop("checked", this.$checks.length === this.data.length); + } + setup_freeze_area() { this.$freeze = $( `
${__( @@ -1253,6 +1264,8 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { } this.$checkbox_cursor = $target; + + this.update_checkbox($target); }); } @@ -1398,6 +1411,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { this.$checkbox_actions.show(); this.$list_head_subject.hide(); } + this.update_checkbox(); this.toggle_actions_menu_button(this.$checks.length > 0); } From f08584c06dbb7750a6ddf9542b51140a2dc544c6 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Fri, 8 Oct 2021 11:04:43 +0530 Subject: [PATCH 029/138] fix: Fixed redirect to newly created workspace --- frappe/public/js/frappe/router.js | 6 ++++-- frappe/public/js/frappe/views/workspace/blocks/header.js | 2 +- frappe/public/js/frappe/views/workspace/workspace.js | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index 8ca9e6b0ad..7ecc3f5010 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -126,12 +126,14 @@ frappe.router = { // /app/user/user-001 = ["Form", "User", "user-001"] // /app/event/view/calendar/default = ["List", "Event", "Calendar", "Default"] + let private_wspace = route[1] && `${route[1]}-${frappe.user.name.toLowerCase()}`; + if (frappe.workspaces[route[0]]) { // public workspace route = ['Workspaces', frappe.workspaces[route[0]].title]; - } else if (route[0] == 'private' && frappe.workspaces[route[1]]) { + } else if (route[0] == 'private' && frappe.workspaces[private_wspace]) { // private workspace - route = ['Workspaces', 'private', frappe.workspaces[route[1]].title]; + route = ['Workspaces', 'private', frappe.workspaces[private_wspace].title]; } else if (this.routes[route[0]]) { // route route = this.set_doctype_route(route); diff --git a/frappe/public/js/frappe/views/workspace/blocks/header.js b/frappe/public/js/frappe/views/workspace/blocks/header.js index 219ee3ebd3..d88bc42af9 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/header.js +++ b/frappe/public/js/frappe/views/workspace/blocks/header.js @@ -27,7 +27,7 @@ export default class Header extends Block { data = {}; } - newData.text = __(data.text.replace(/(\n|\t)/gm, "")) || ''; + newData.text = (data.text && __(data.text.replace(/(\n|\t)/gm, ""))) || ''; newData.level = parseInt(data.level) || this.defaultLevel.number; newData.col = parseInt(data.col) || 12; diff --git a/frappe/public/js/frappe/views/workspace/workspace.js b/frappe/public/js/frappe/views/workspace/workspace.js index a97d6d293f..e6248f66cf 100644 --- a/frappe/public/js/frappe/views/workspace/workspace.js +++ b/frappe/public/js/frappe/views/workspace/workspace.js @@ -69,7 +69,7 @@ frappe.views.Workspace = class Workspace { frappe.workspaces[frappe.router.slug(page.name)] = {title: page.title}; } if (this.new_page && this.new_page.name) { - if (!frappe.workspaces[frappe.router.slug(this.new_page.name)]) { + if (!frappe.workspaces[frappe.router.slug(this.new_page.label)]) { this.new_page = { name: this.all_pages[0].title, public: this.all_pages[0].public }; } if (this.new_page.public) { From 9ac748dc300e44298cb083e185adb6f6c9c38c6c Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 11 Oct 2021 19:45:06 +0200 Subject: [PATCH 030/138] feat: allow tuple of doctypes as key --- .../doctype/custom_field/custom_field.py | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/frappe/custom/doctype/custom_field/custom_field.py b/frappe/custom/doctype/custom_field/custom_field.py index bf606701da..8c22d3c45c 100644 --- a/frappe/custom/doctype/custom_field/custom_field.py +++ b/frappe/custom/doctype/custom_field/custom_field.py @@ -131,7 +131,7 @@ def create_custom_field(doctype, df, ignore_validate=False): "permlevel": 0, "fieldtype": 'Data', "hidden": 0, - # Looks like we always use this programatically? + # Looks like we always use this programatically? # "is_standard": 1 }) custom_field.update(df) @@ -146,24 +146,29 @@ def create_custom_fields(custom_fields, ignore_validate = False, update=True): if not ignore_validate and frappe.flags.in_setup_wizard: ignore_validate = True - for doctype, fields in custom_fields.items(): + for doctypes, fields in custom_fields.items(): if isinstance(fields, dict): # only one field fields = [fields] - for df in fields: - field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": df["fieldname"]}) - if not field: - try: - df["owner"] = "Administrator" - create_custom_field(doctype, df, ignore_validate=ignore_validate) - except frappe.exceptions.DuplicateEntryError: - pass - elif update: - custom_field = frappe.get_doc("Custom Field", field) - custom_field.flags.ignore_validate = ignore_validate - custom_field.update(df) - custom_field.save() + if isinstance(doctypes, str): + # only one doctype + doctypes = (doctypes,) + + for doctype in doctypes: + for df in fields: + field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": df["fieldname"]}) + if not field: + try: + df["owner"] = "Administrator" + create_custom_field(doctype, df, ignore_validate=ignore_validate) + except frappe.exceptions.DuplicateEntryError: + pass + elif update: + custom_field = frappe.get_doc("Custom Field", field) + custom_field.flags.ignore_validate = ignore_validate + custom_field.update(df) + custom_field.save() frappe.clear_cache(doctype=doctype) frappe.db.updatedb(doctype) From 096700a77aaf74e66c7e795a42ba69869d41bf96 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 12 Oct 2021 19:11:25 +0530 Subject: [PATCH 031/138] fix: Loading state for query reports --- .../js/frappe/views/reports/query_report.js | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index 7d68919821..8a50a516af 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -107,7 +107,6 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { } if (this.report_name !== frappe.get_route()[1]) { - // this.toggle_loading(true); // different report this.load_report(); } @@ -556,6 +555,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { refresh() { this.toggle_message(true); this.toggle_report(false); + this.show_loading_indicator(); let filters = this.get_filter_values(true); // only one refresh at a time @@ -645,6 +645,8 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { this.show_footer_message(); frappe.hide_progress(); + }).finally(() => { + this.hide_loading_indicator(); }); } @@ -869,6 +871,22 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { } } + show_loading_indicator() { + this.$loading.find('div').html(`
+
+ Generic Empty State +
+

${__('Loading')}...

+
`); + this.$report.hide(); + this.$loading.show(); + } + + hide_loading_indicator() { + this.$loading.hide(); + this.$report.show(); + } + get_chart_options(data) { let options = this.report_settings.get_chart_data ? this.report_settings.get_chart_data(data.columns, data.result) @@ -1679,6 +1697,9 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { .hide().appendTo(this.page.main); this.$chart = $('
').hide().appendTo(this.page.main); + + this.$loading = $(this.message_div('')).hide().appendTo(this.page.main); + this.$report = $('
').appendTo(this.page.main); this.$message = $(this.message_div('')).hide().appendTo(this.page.main); } @@ -1738,11 +1759,6 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { this.refresh(); } - toggle_loading(flag) { - this.toggle_message(flag, __('Loading') + '...'); - } - - toggle_nothing_to_show(flag) { let message = this.prepared_report ? __('This is a background report. Please set the appropriate filters and then generate a new one.') From 7315076038fea37f8368ee691b56835ff3b64ab7 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Wed, 13 Oct 2021 14:06:34 +0530 Subject: [PATCH 032/138] refactor: converted queries --- frappe/defaults.py | 18 ++++++++---------- frappe/permissions.py | 17 +++++++++-------- frappe/sessions.py | 18 +++++++++--------- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/frappe/defaults.py b/frappe/defaults.py index 75feabc332..796965e597 100644 --- a/frappe/defaults.py +++ b/frappe/defaults.py @@ -116,14 +116,11 @@ def set_default(key, value, parent, parenttype="__default"): :param value: Default value. :param parent: Usually, **User** to whom the default belongs. :param parenttype: [optional] default is `__default`.""" - if frappe.db.sql(''' - select - defkey - from - `tabDefaultValue` - where - defkey=%s and parent=%s - for update''', (key, parent)): + table = frappe.qb.DocType("DefaultValue") + result = frappe.qb.from_(table).where(table.defkey == key) \ + .where(table.parent == parent) \ + .select(table.defkey).for_update().run() + if result: frappe.db.delete("DefaultValue", { "defkey": key, "parent": parent @@ -191,8 +188,9 @@ def get_defaults_for(parent="__default"): if defaults==None: # sort descending because first default must get precedence - res = frappe.db.sql("""select defkey, defvalue from `tabDefaultValue` - where parent = %s order by creation""", (parent,), as_dict=1) + table = frappe.qb.DocType("DefaultValue") + res = frappe.qb.from_(table).where(table.parent == parent) \ + .select(table.defkey, table.defvalue).orderby("creation").run(as_dict=True) defaults = frappe._dict({}) for d in res: diff --git a/frappe/permissions.py b/frappe/permissions.py index 7ee1119ebb..34fcbee1a8 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -333,8 +333,8 @@ def get_all_perms(role): '''Returns valid permissions for a given role''' perms = frappe.get_all('DocPerm', fields='*', filters=dict(role=role)) custom_perms = frappe.get_all('Custom DocPerm', fields='*', filters=dict(role=role)) - doctypes_with_custom_perms = frappe.db.sql_list("""select distinct parent - from `tabCustom DocPerm`""") + query = frappe.qb.from_("Custom DocPerm").select("parent").distinct() + doctypes_with_custom_perms = frappe.db.sql_list(query) for p in perms: if p.parent not in doctypes_with_custom_perms: @@ -351,10 +351,12 @@ def get_roles(user=None, with_standard=True): def get(): if user == 'Administrator': - return [r[0] for r in frappe.db.sql("select name from `tabRole`")] # return all available roles + return [r[0] for r in frappe.qb.from_("Role").select("name").run()] # return all available roles else: - return [r[0] for r in frappe.db.sql("""select role from `tabHas Role` - where parent=%s and role not in ('All', 'Guest')""", (user,))] + ['All', 'Guest'] + table = frappe.qb.DocType("Has Role") + result = frappe.qb.form_(table).where(table.parent == user) \ + .where(table.role.notin(["All", "Guest"])).select(table.role).run() + return [r[0] for r in result] + ['All', 'Guest'] roles = frappe.cache().hget("roles", user, get) @@ -463,10 +465,9 @@ def update_permission_property(doctype, role, permlevel, ptype, value=None, vali name = frappe.get_value('Custom DocPerm', dict(parent=doctype, role=role, permlevel=permlevel)) + table = frappe.qb.DocType("Custom DocPerm") + frappe.qb.update(table).set(ptype, value).where(table.name == name).run() - frappe.db.sql(""" - update `tabCustom DocPerm` - set `{0}`=%s where name=%s""".format(ptype), (value, name)) if validate: validate_permissions_for_doctype(doctype) diff --git a/frappe/sessions.py b/frappe/sessions.py index ce104968ad..efa2779a23 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -16,6 +16,7 @@ import frappe.translate import redis from urllib.parse import unquote from frappe.cache_manager import clear_user_cache +from frappe.query_builder import Order @frappe.whitelist(allow_guest=True) def clear(user=None): @@ -61,18 +62,17 @@ def get_sessions_to_clear(user=None, keep_current=False, device=None): simultaneous_sessions = frappe.db.get_value('User', user, 'simultaneous_sessions') or 1 offset = simultaneous_sessions - 1 + table = frappe.qb.DocType("Sessions") + criterion = frappe.qb.from_(table).where(table.user == user) \ + .where(table.device.isin(device)) condition = '' if keep_current: - condition = ' AND sid != {0}'.format(frappe.db.escape(frappe.session.sid)) + criterion = criterion.where(table.sid != frappe.db.escape(frappe.session.sid)) - return frappe.db.sql_list(""" - SELECT `sid` FROM `tabSessions` - WHERE `tabSessions`.user=%(user)s - AND device in %(device)s - {condition} - ORDER BY `lastupdate` DESC - LIMIT 100 OFFSET {offset}""".format(condition=condition, offset=offset), - {"user": user, "device": device}) + query = criterion.select(table.sid).offset(offset).limit(100) \ + .orderby(table.lastupdate, order=Order.desc) + + return frappe.db.sql_list(query) def delete_session(sid=None, user=None, reason="Session Expired"): from frappe.core.doctype.activity_log.feed import logout_feed From a621c4178cb20ffc19296226c6851ec5b8cbf6e7 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Wed, 13 Oct 2021 14:28:13 +0530 Subject: [PATCH 033/138] fix: fixing erroneous query conversions --- frappe/defaults.py | 2 +- frappe/permissions.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/defaults.py b/frappe/defaults.py index 796965e597..3672bed511 100644 --- a/frappe/defaults.py +++ b/frappe/defaults.py @@ -190,7 +190,7 @@ def get_defaults_for(parent="__default"): # sort descending because first default must get precedence table = frappe.qb.DocType("DefaultValue") res = frappe.qb.from_(table).where(table.parent == parent) \ - .select(table.defkey, table.defvalue).orderby("creation").run(as_dict=True) + .select(table.defkey, table.defvalue).orderby("creation").run(as_dict=True) defaults = frappe._dict({}) for d in res: diff --git a/frappe/permissions.py b/frappe/permissions.py index 34fcbee1a8..494a0f1d14 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -354,7 +354,7 @@ def get_roles(user=None, with_standard=True): return [r[0] for r in frappe.qb.from_("Role").select("name").run()] # return all available roles else: table = frappe.qb.DocType("Has Role") - result = frappe.qb.form_(table).where(table.parent == user) \ + result = frappe.qb.from_(table).where(table.parent == user) \ .where(table.role.notin(["All", "Guest"])).select(table.role).run() return [r[0] for r in result] + ['All', 'Guest'] From e0a3e4efe3283ae08e88979262723b9e460a1aeb Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Wed, 13 Oct 2021 15:13:13 +0530 Subject: [PATCH 034/138] refactor: converted queries in share & translate --- frappe/share.py | 6 ++++-- frappe/translate.py | 18 ++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/frappe/share.py b/frappe/share.py index 030feea8fa..cee1120066 100644 --- a/frappe/share.py +++ b/frappe/share.py @@ -128,8 +128,10 @@ def get_shared_doctypes(user=None): """Return list of doctypes in which documents are shared for the given user.""" if not user: user = frappe.session.user - - return frappe.db.sql_list("select distinct share_doctype from tabDocShare where (user=%s or everyone=1)", user) + table = frappe.qb.DocType("DocShare") + query = frappe.qb.from_(table).where(table.user == user | table.everyone == 1) \ + .select(table.share_doctype).distinct() + return frappe.db.sql_list(query) def get_share_name(doctype, name, user, everyone): if cint(everyone): diff --git a/frappe/translate.py b/frappe/translate.py index 6f3ed81dc2..3fc9fa826e 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -119,7 +119,8 @@ def set_default_language(lang): def get_lang_dict(): """Returns all languages in dict format, full name is the key e.g. `{"english":"en"}`""" - return dict(frappe.db.sql('select language_name, name from tabLanguage')) + result = dict(frappe.qb.from_("Language").select("language_name", "name").run()) + return result def get_dict(fortype, name=None): """Returns translation dict for a type of object. @@ -151,12 +152,12 @@ def get_dict(fortype, name=None): messages += get_messages_from_navbar() messages += get_messages_from_include_files() - messages += frappe.db.sql("select 'Print Format:', name from `tabPrint Format`") - messages += frappe.db.sql("select 'DocType:', name from tabDocType") - messages += frappe.db.sql("select 'Role:', name from tabRole") - messages += frappe.db.sql("select 'Module:', name from `tabModule Def`") - messages += frappe.db.sql("select '', format from `tabWorkspace Shortcut` where format is not null") - messages += frappe.db.sql("select '', title from `tabOnboarding Step`") + messages += frappe.qb.from_("Print Format").select("Print Format:", "name").run() + messages += frappe.qb.from_("DocType").select("DocType:", "name").run() + messages += frappe.qb.from_("Role").select("Role:", "name").run() + messages += frappe.qb.from_("Module Def").select("Module:", "name").run() + messages += frappe.qb.from_("Workspace Shortcut").where(frappe.qb.Field("format" != None)).select("").run() + messages += frappe.qb.from_("Onboarding Step").select("", "title").run() messages = deduplicate_messages(messages) message_dict = make_dict_from_messages(messages, load_user_translation=False) @@ -898,7 +899,8 @@ def get_translator_url(): def get_all_languages(with_language_name=False): """Returns all language codes ar, ch etc""" def get_language_codes(): - return frappe.db.sql_list('select name from tabLanguage') + query = frappe.qb.from_("Language").select("name") + return frappe.db.sql_list(query) def get_all_language_with_name(): return frappe.db.get_all('Language', ['language_code', 'language_name']) From cb4b9868d3b49fa260a14d53c9b71f7cd838df7a Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Tue, 12 Oct 2021 13:31:09 +0530 Subject: [PATCH 035/138] chore: Try on FCloud button --- .github/try-on-f-cloud-button.svg | 32 +++++++++++++++++++++++++++++++ README.md | 7 +++++++ 2 files changed, 39 insertions(+) create mode 100644 .github/try-on-f-cloud-button.svg diff --git a/.github/try-on-f-cloud-button.svg b/.github/try-on-f-cloud-button.svg new file mode 100644 index 0000000000..fe0bb2c52d --- /dev/null +++ b/.github/try-on-f-cloud-button.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README.md b/README.md index f8a1907da2..ef471aa05a 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,12 @@ Full-stack web application framework that uses Python and MariaDB on the server side and a tightly integrated client side library. Built for [ERPNext](https://erpnext.com) + + ## Table of Contents * [Installation](#installation) * [Contributing](#contributing) @@ -46,6 +52,7 @@ Full-stack web application framework that uses Python and MariaDB on the server * [Install via Docker](https://github.com/frappe/frappe_docker) * [Install via Frappe Bench](https://github.com/frappe/bench) * [Offical Documentation](https://frappeframework.com/docs/user/en/installation) +* [Managed Hosting on Frappe Cloud](https://frappecloud.com/deploy?apps=frappe&source=frappe_readme) ## Contributing From 12e4b33a1f8a9e66091f0531d19f50fff7dc649c Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Wed, 13 Oct 2021 11:26:24 -0400 Subject: [PATCH 036/138] fix: simplify merge condition statement Co-authored-by: gavin --- frappe/model/rename_doc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py index 9377ad74f8..de83b24cd8 100644 --- a/frappe/model/rename_doc.py +++ b/frappe/model/rename_doc.py @@ -471,7 +471,7 @@ def bulk_rename(doctype, rows=None, via_console = False): for row in rows: # if row has some content if len(row) > 1 and row[0] and row[1]: - merge = True if len(row) > 2 and (row[2] == "1" or row[2].lower() == "true") else False + merge = len(row) > 2 and (row[2] == "1" or row[2].lower() == "true") try: if rename_doc(doctype, row[0], row[1], merge=merge): msg = _("Successful: {0} to {1}").format(row[0], row[1]) From e01d97b8df979e59a318cd3e1b03c5838f3b8e83 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Thu, 14 Oct 2021 01:16:46 +0530 Subject: [PATCH 037/138] refactor: replacing queries with frappe ORM --- frappe/defaults.py | 20 ++++++++++++-------- frappe/permissions.py | 16 ++++++++-------- frappe/translate.py | 15 ++++++++++----- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/frappe/defaults.py b/frappe/defaults.py index 3672bed511..eb98db449f 100644 --- a/frappe/defaults.py +++ b/frappe/defaults.py @@ -4,6 +4,7 @@ import frappe from frappe.desk.notifications import clear_notifications from frappe.cache_manager import clear_defaults_cache, common_default_keys +from frappe.query_builder import DocType # Note: DefaultValue records are identified by parenttype # __default, __global or 'User Permission' @@ -116,11 +117,11 @@ def set_default(key, value, parent, parenttype="__default"): :param value: Default value. :param parent: Usually, **User** to whom the default belongs. :param parenttype: [optional] default is `__default`.""" - table = frappe.qb.DocType("DefaultValue") - result = frappe.qb.from_(table).where(table.defkey == key) \ - .where(table.parent == parent) \ - .select(table.defkey).for_update().run() - if result: + table = DocType("DefaultValue") + key_exists = frappe.qb.from_(table).where( + (table.defkey == key) & (table.parent == parent) + ).select(table.defkey).for_update().run() + if key_exists: frappe.db.delete("DefaultValue", { "defkey": key, "parent": parent @@ -188,9 +189,12 @@ def get_defaults_for(parent="__default"): if defaults==None: # sort descending because first default must get precedence - table = frappe.qb.DocType("DefaultValue") - res = frappe.qb.from_(table).where(table.parent == parent) \ - .select(table.defkey, table.defvalue).orderby("creation").run(as_dict=True) + table = DocType("DefaultValue") + res = frappe.qb.from_(table).where( + table.parent == parent + ).select( + table.defkey, table.defvalue + ).orderby("creation").run(as_dict=True) defaults = frappe._dict({}) for d in res: diff --git a/frappe/permissions.py b/frappe/permissions.py index 494a0f1d14..0585b1e220 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -6,7 +6,7 @@ import frappe import frappe.share from frappe import _, msgprint from frappe.utils import cint - +from frappe.query_builder import DocType rights = ("select", "read", "write", "create", "delete", "submit", "cancel", "amend", "print", "email", "report", "import", "export", "set_user_permissions", "share") @@ -333,8 +333,7 @@ def get_all_perms(role): '''Returns valid permissions for a given role''' perms = frappe.get_all('DocPerm', fields='*', filters=dict(role=role)) custom_perms = frappe.get_all('Custom DocPerm', fields='*', filters=dict(role=role)) - query = frappe.qb.from_("Custom DocPerm").select("parent").distinct() - doctypes_with_custom_perms = frappe.db.sql_list(query) + doctypes_with_custom_perms = frappe.get_all("Custom DocPerm", pluck="parent", distinct=True) for p in perms: if p.parent not in doctypes_with_custom_perms: @@ -351,11 +350,12 @@ def get_roles(user=None, with_standard=True): def get(): if user == 'Administrator': - return [r[0] for r in frappe.qb.from_("Role").select("name").run()] # return all available roles + return frappe.get_all("Role", pluck="name") # return all available roles else: - table = frappe.qb.DocType("Has Role") - result = frappe.qb.from_(table).where(table.parent == user) \ - .where(table.role.notin(["All", "Guest"])).select(table.role).run() + table = DocType("Has Role") + result = frappe.qb.from_(table).where( + (table.parent == user) & (table.role.notin(["All", "Guest"])) + ).select(table.role).run() return [r[0] for r in result] + ['All', 'Guest'] roles = frappe.cache().hget("roles", user, get) @@ -465,7 +465,7 @@ def update_permission_property(doctype, role, permlevel, ptype, value=None, vali name = frappe.get_value('Custom DocPerm', dict(parent=doctype, role=role, permlevel=permlevel)) - table = frappe.qb.DocType("Custom DocPerm") + table = DocType("Custom DocPerm") frappe.qb.update(table).set(ptype, value).where(table.name == name).run() if validate: diff --git a/frappe/translate.py b/frappe/translate.py index 3fc9fa826e..3abffd3215 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -20,6 +20,7 @@ from typing import List, Union, Tuple import frappe from frappe.model.utils import InvalidIncludePath, render_include from frappe.utils import get_bench_path, is_html, strip, strip_html_tags +from frappe.query_builder import Field def get_language(lang_list: List = None) -> str: @@ -156,7 +157,7 @@ def get_dict(fortype, name=None): messages += frappe.qb.from_("DocType").select("DocType:", "name").run() messages += frappe.qb.from_("Role").select("Role:", "name").run() messages += frappe.qb.from_("Module Def").select("Module:", "name").run() - messages += frappe.qb.from_("Workspace Shortcut").where(frappe.qb.Field("format" != None)).select("").run() + messages += frappe.qb.from_("Workspace Shortcut").where(Field("format").isnotnull()).select("").run() messages += frappe.qb.from_("Onboarding Step").select("", "title").run() messages = deduplicate_messages(messages) @@ -324,13 +325,17 @@ def get_messages_for_app(app, deduplicate=True): # doctypes if modules: - for name in frappe.db.sql_list("""select name from tabDocType - where module in ({})""".format(modules)): + names = frappe.qb.from_("DocType").where( + Field("module").isin(modules) + ).select("name").run() + for name in names: messages.extend(get_messages_from_doctype(name)) # pages - for name, title in frappe.db.sql("""select name, title from tabPage - where module in ({})""".format(modules)): + result = frappe.qb.from_("Page").where( + Field("module").isin(modules) + ).select("name", "title").run() + for name, title in result: messages.append((None, title or name)) messages.extend(get_messages_from_page(name)) From d75aab4b3edf219471169b12487b3c3dcfd92be5 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Thu, 14 Oct 2021 03:28:53 +0530 Subject: [PATCH 038/138] fix: fixed erroneous query conversion --- frappe/share.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frappe/share.py b/frappe/share.py index cee1120066..4d43990c54 100644 --- a/frappe/share.py +++ b/frappe/share.py @@ -129,8 +129,9 @@ def get_shared_doctypes(user=None): if not user: user = frappe.session.user table = frappe.qb.DocType("DocShare") - query = frappe.qb.from_(table).where(table.user == user | table.everyone == 1) \ - .select(table.share_doctype).distinct() + query = frappe.qb.from_(table).where( + (table.user == user) | (table.everyone == 1) + ).select(table.share_doctype).distinct() return frappe.db.sql_list(query) def get_share_name(doctype, name, user, everyone): From 5186347219759bc51ffe34df394c6506aefcd916 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 14 Oct 2021 11:08:21 +0530 Subject: [PATCH 039/138] revert: Reverting single vertical scrollbar fix --- .../js/frappe/views/reports/query_report.js | 4 ---- .../js/frappe/views/reports/report_view.js | 2 -- frappe/public/scss/desk/report.scss | 22 ------------------- 3 files changed, 28 deletions(-) diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index 7d68919821..a4b3564e37 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -518,9 +518,6 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { } else { this.page.show_form(); } - - this.page.body[0].style.setProperty('--report-filter-height', this.page.page_form.css('height')); - this.page.body.parent().css('margin-bottom', 'unset'); } set_filters(filters) { @@ -832,7 +829,6 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { if (this.raw_data.add_total_row) { data = data.slice(); data.splice(-1, 1); - this.$page.find('.layout-main-section')[0].style.setProperty('--report-total-height', '310px'); } this.$report.show(); diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js index 2a92d93e30..8866a4b2af 100644 --- a/frappe/public/js/frappe/views/reports/report_view.js +++ b/frappe/public/js/frappe/views/reports/report_view.js @@ -50,8 +50,6 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { this.setup_columns(); super.setup_new_doc_event(); this.page.main.addClass('report-view'); - this.page.body[0].style.setProperty('--report-filter-height', this.page.page_form.css('height')); - this.page.body.parent().css('margin-bottom', 'unset'); } toggle_side_bar() { diff --git a/frappe/public/scss/desk/report.scss b/frappe/public/scss/desk/report.scss index 2389a4f8f6..f8666602ff 100644 --- a/frappe/public/scss/desk/report.scss +++ b/frappe/public/scss/desk/report.scss @@ -84,39 +84,17 @@ margin-bottom: 10px; } -.layout-main-section { - --report-filter-height: 0px; - --report-total-height: 275px; -} - .report-wrapper { overflow: auto; - - .datatable { - height: calc(100vh - var(--report-filter-height) - 205px); - - .dt-scrollable { - height: calc(100vh - var(--report-filter-height) - var(--report-total-height)); - } - } } .report-view { .result { - min-height: 50vh !important; .dt-row:last-child:not(.dt-row-filter) { .dt-cell { border-bottom: 1px solid var(--border-color); } } - - .datatable { - height: calc(100vh - var(--report-filter-height) - 225px); - - .dt-scrollable { - height: calc(100vh - var(--report-filter-height) - 295px); - } - } } } From 2bebf4b2c0e73f117664e7673d63928cac9bff35 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 1 Sep 2021 15:10:21 +0530 Subject: [PATCH 040/138] refactor: convert core communication db.sql calls --- .../doctype/communication/communication.py | 2 +- frappe/core/doctype/communication/mixins.py | 24 ++----------------- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index 66bb3909da..bd33189d58 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -255,7 +255,7 @@ class Communication(Document, CommunicationEmailMixin): def set_delivery_status(self, commit=False): '''Look into the status of Email Queue linked to this Communication and set the Delivery Status of this Communication''' delivery_status = None - status_counts = Counter(frappe.db.sql_list('''select status from `tabEmail Queue` where communication=%s''', self.name)) + status_counts = Counter(frappe.get_all("Email Queue", pluck="status", filters={"communication": self.name})) if self.sent_or_received == "Received": return diff --git a/frappe/core/doctype/communication/mixins.py b/frappe/core/doctype/communication/mixins.py index 52cd370890..b6d8070d00 100644 --- a/frappe/core/doctype/communication/mixins.py +++ b/frappe/core/doctype/communication/mixins.py @@ -217,17 +217,7 @@ class CommunicationEmailMixin: if not emails: return [] - disabled_users = frappe.db.sql_list(""" - SELECT - email - FROM - `tabUser` - where - email in %(emails)s - and - thread_notify=0 - """, {'emails': tuple(emails)}) - return disabled_users + return frappe.get_all("User", pluck="email", filters={"email": ["in", emails], "thread_notify": 0}) @staticmethod def filter_disabled_users(emails): @@ -236,17 +226,7 @@ class CommunicationEmailMixin: if not emails: return [] - disabled_users = frappe.db.sql_list(""" - SELECT - email - FROM - `tabUser` - where - email in %(emails)s - and - enabled=0 - """, {'emails': tuple(emails)}) - return disabled_users + return frappe.get_all("User", pluck="email", filters={"email": ["in", emails], "enabled": 0}) def sendmail_input_dict(self, print_html=None, print_format=None, send_me_a_copy=None, print_letterhead=None, is_inbound_mail_communcation=None): From f5a5f975936390039672012b0225b79a1d714db9 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Thu, 14 Oct 2021 03:29:20 +0530 Subject: [PATCH 041/138] refactor: converted quries in sessions.py --- frappe/sessions.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/frappe/sessions.py b/frappe/sessions.py index efa2779a23..508badbd8f 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -17,6 +17,7 @@ import redis from urllib.parse import unquote from frappe.cache_manager import clear_user_cache from frappe.query_builder import Order +from frappe.query_builder import DocType @frappe.whitelist(allow_guest=True) def clear(user=None): @@ -65,12 +66,10 @@ def get_sessions_to_clear(user=None, keep_current=False, device=None): table = frappe.qb.DocType("Sessions") criterion = frappe.qb.from_(table).where(table.user == user) \ .where(table.device.isin(device)) - condition = '' if keep_current: criterion = criterion.where(table.sid != frappe.db.escape(frappe.session.sid)) - query = criterion.select(table.sid).offset(offset).limit(100) \ - .orderby(table.lastupdate, order=Order.desc) + query = criterion.select(table.sid).offset(offset).limit(100).orderby(table.lastupdate, order=Order.desc) return frappe.db.sql_list(query) @@ -80,7 +79,10 @@ def delete_session(sid=None, user=None, reason="Session Expired"): frappe.cache().hdel("session", sid) frappe.cache().hdel("last_db_session_update", sid) if sid and not user: - user_details = frappe.db.sql("""select user from tabSessions where sid=%s""", sid, as_dict=True) + table = DocType("Sessions") + user_details = frappe.qb.from_(table).where( + table.sid == sid + ).select(table.user).run(as_dict=True) if user_details: user = user_details[0].get("user") logout_feed(user, reason) @@ -91,7 +93,7 @@ def clear_all_sessions(reason=None): """This effectively logs out all users""" frappe.only_for("Administrator") if not reason: reason = "Deleted All Active Session" - for sid in frappe.db.sql_list("select sid from `tabSessions`"): + for sid in [r[0] for r in frappe.qb.from_("Sessions").select("sid").run()]: delete_session(sid, reason=reason) def get_expired_sessions(): From a81a0d16c27db7fcb9276cfa925a25867e75beb1 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 1 Sep 2021 15:11:17 +0530 Subject: [PATCH 042/138] refactor: convert doctype data_export db.sql calls --- frappe/core/doctype/data_export/exporter.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/frappe/core/doctype/data_export/exporter.py b/frappe/core/doctype/data_export/exporter.py index 7c660c7180..c5cf67ba57 100644 --- a/frappe/core/doctype/data_export/exporter.py +++ b/frappe/core/doctype/data_export/exporter.py @@ -261,6 +261,7 @@ class DataExporter: self.writer.writerow([self.data_keys.data_separator]) def add_data(self): + from frappe.query_builder import DocType if self.template and not self.with_data: return @@ -305,9 +306,15 @@ class DataExporter: if self.all_doctypes: # add child tables for c in self.child_doctypes: - for ci, child in enumerate(frappe.db.sql("""select * from `tab{0}` - where parent=%s and parentfield=%s order by idx""".format(c['doctype']), - (doc.name, c['parentfield']), as_dict=1)): + child_doctype_table = DocType(c["doctype"]) + data_row = ( + frappe.qb.from_(child_doctype_table) + .select("*") + .where(child_doctype_table.parent == doc.name) + .where(child_doctype_table.parentfield == c["parentfield"]) + .orderby(child_doctype_table.idx) + ) + for ci, child in enumerate(data_row.run()): self.add_data_row(rows, c['doctype'], c['parentfield'], child, ci) for row in rows: From b05cd732f53dc3e4197b0819958bc86d9ea2965d Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Thu, 14 Oct 2021 14:45:15 +0530 Subject: [PATCH 043/138] ci: trigger build From 58de6515e7bd7a54f03cfd35267f85860343350f Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 14 Oct 2021 11:23:56 +0200 Subject: [PATCH 044/138] test: test_create_custom_fields --- .../doctype/custom_field/test_custom_field.py | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/frappe/custom/doctype/custom_field/test_custom_field.py b/frappe/custom/doctype/custom_field/test_custom_field.py index 9633f0eb8a..ad3cf27eea 100644 --- a/frappe/custom/doctype/custom_field/test_custom_field.py +++ b/frappe/custom/doctype/custom_field/test_custom_field.py @@ -6,7 +6,42 @@ import frappe import unittest -test_records = frappe.get_test_records('Custom Field') +test_records = frappe.get_test_records("Custom Field") + class TestCustomField(unittest.TestCase): - pass + def test_create_custom_fields(self): + from .custom_field import create_custom_fields + + create_custom_fields( + { + "Address": [ + { + "fieldname": "_test_custom_field_1", + "label": "_Test Custom Field 1", + "fieldtype": "Data", + "insert_after": "phone", + }, + ], + ("Address", "Contact"): [ + { + "fieldname": "_test_custom_field_2", + "label": "_Test Custom Field 2", + "fieldtype": "Data", + "insert_after": "phone", + }, + ], + } + ) + + frappe.db.commit() + + self.assertTrue( + frappe.db.exists("Custom Field", "Address-_test_custom_field_1") + ) + self.assertTrue( + frappe.db.exists("Custom Field", "Address-_test_custom_field_2") + ) + self.assertTrue( + frappe.db.exists("Custom Field", "Contact-_test_custom_field_2") + ) From ac54e24fc4e8339c350cda3073ab4c03ee11d9e9 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 1 Sep 2021 15:12:06 +0530 Subject: [PATCH 045/138] refactor: convert doctype db.sql calls --- frappe/core/doctype/doctype/doctype.py | 38 ++++++++++++++------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 8f8a8ed287..5a91016e32 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -23,6 +23,7 @@ from frappe.modules.import_file import get_file_path from frappe.model.meta import Meta from frappe.desk.utils import validate_route_conflict from frappe.website.utils import clear_cache +from frappe.query_builder.functions import Concat class InvalidFieldNameError(frappe.ValidationError): pass class UniqueFieldnameError(frappe.ValidationError): pass @@ -465,7 +466,7 @@ class DocType(Document): return # check if atleast 1 record exists - if not (frappe.db.table_exists(self.name) and frappe.db.sql("select name from `tab{}` limit 1".format(self.name))): + if not (frappe.db.table_exists(self.name) and frappe.get_all(self.name, fields=["name"], limit=1, as_list=True)): return existing_property_setter = frappe.db.get_value("Property Setter", {"doc_type": self.name, @@ -571,17 +572,17 @@ class DocType(Document): def make_amendable(self): """If is_submittable is set, add amended_from docfields.""" if self.is_submittable: - if not frappe.db.sql("""select name from tabDocField - where fieldname = 'amended_from' and parent = %s""", self.name): - self.append("fields", { - "label": "Amended From", - "fieldtype": "Link", - "fieldname": "amended_from", - "options": self.name, - "read_only": 1, - "print_hide": 1, - "no_copy": 1 - }) + docfield_exists = frappe.get_all("DocField", filters={"fieldname": "amended_from", "parent": self.name}, pluck="name", limit=1) + if not docfield_exists: + self.append("fields", { + "label": "Amended From", + "fieldtype": "Link", + "fieldname": "amended_from", + "options": self.name, + "read_only": 1, + "print_hide": 1, + "no_copy": 1 + }) def make_repeatable(self): """If allow_auto_repeat is set, add auto_repeat custom field.""" @@ -706,12 +707,13 @@ def validate_series(dt, autoname=None, name=None): and (not autoname.startswith('format:')): prefix = autoname.split('.')[0] - used_in = frappe.db.sql(""" - SELECT `name` - FROM `tabDocType` - WHERE `autoname` LIKE CONCAT(%s, '.%%') - AND `name`!=%s - """, (prefix, name)) + doctype = frappe.qb.DocType("DocType") + used_in = (frappe.qb + .from_(doctype) + .select(doctype.name) + .where(doctype.autoname.like(Concat(prefix,".%"))) + .where(doctype.name != name) + ).run() if used_in: frappe.throw(_("Series {0} already used in {1}").format(prefix, used_in[0][0])) From 668051cfe9a7b3a6a30485a81b2c7fca380cb646 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Thu, 14 Oct 2021 22:54:53 +0530 Subject: [PATCH 046/138] refactor: refactored query using frappe.get_all --- frappe/translate.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/frappe/translate.py b/frappe/translate.py index 3abffd3215..f6a3880774 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -120,7 +120,7 @@ def set_default_language(lang): def get_lang_dict(): """Returns all languages in dict format, full name is the key e.g. `{"english":"en"}`""" - result = dict(frappe.qb.from_("Language").select("language_name", "name").run()) + result = frappe.get_all("Language", fields=["language_name", "name"], order_by="modified", as_list=True) return result def get_dict(fortype, name=None): @@ -904,8 +904,7 @@ def get_translator_url(): def get_all_languages(with_language_name=False): """Returns all language codes ar, ch etc""" def get_language_codes(): - query = frappe.qb.from_("Language").select("name") - return frappe.db.sql_list(query) + return frappe.get_all("Language", pluck="name", order_by="modified") def get_all_language_with_name(): return frappe.db.get_all('Language', ['language_code', 'language_name']) From 4ed10de918bfa05d9e38675868e42e92cede805f Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Thu, 14 Oct 2021 23:25:43 +0530 Subject: [PATCH 047/138] fix: fixed sider issues --- frappe/core/doctype/activity_log/activity_log.py | 9 ++++++--- frappe/sessions.py | 3 +-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/frappe/core/doctype/activity_log/activity_log.py b/frappe/core/doctype/activity_log/activity_log.py index 183a1c264c..9c27e84740 100644 --- a/frappe/core/doctype/activity_log/activity_log.py +++ b/frappe/core/doctype/activity_log/activity_log.py @@ -7,6 +7,8 @@ from frappe.utils import get_fullname, now from frappe.model.document import Document from frappe.core.utils import set_timeline_doc import frappe +from frappe.query_builder import DocType, Interval +from frappe.query_builder.functions import Now class ActivityLog(Document): def before_insert(self): @@ -44,6 +46,7 @@ def clear_activity_logs(days=None): if not days: days = 90 - - frappe.db.sql("""delete from `tabActivity Log` where \ - creation< (NOW() - INTERVAL '{0}' DAY)""".format(days)) \ No newline at end of file + doctype = DocType("Activity Log") + frappe.qb.from_(doctype).where( + doctype.creation < (Now() - Interval(days=days)) + ).delete().run() \ No newline at end of file diff --git a/frappe/sessions.py b/frappe/sessions.py index 508badbd8f..05f3bba3cf 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -64,8 +64,7 @@ def get_sessions_to_clear(user=None, keep_current=False, device=None): offset = simultaneous_sessions - 1 table = frappe.qb.DocType("Sessions") - criterion = frappe.qb.from_(table).where(table.user == user) \ - .where(table.device.isin(device)) + criterion = frappe.qb.from_(table).where((table.user == user) & (table.device.isin(device))) if keep_current: criterion = criterion.where(table.sid != frappe.db.escape(frappe.session.sid)) From 2c088c81ba3f05cd9a9500cf3ef197e028f89c14 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Fri, 15 Oct 2021 05:00:46 +0530 Subject: [PATCH 048/138] refactor: Converted more queries --- .../core/doctype/log_settings/log_settings.py | 10 +++++-- frappe/core/doctype/user/user.py | 30 ++++++++++++------- .../patches/v11_0/remove_skip_for_doctype.py | 17 +++++------ 3 files changed, 33 insertions(+), 24 deletions(-) diff --git a/frappe/core/doctype/log_settings/log_settings.py b/frappe/core/doctype/log_settings/log_settings.py index 8a471b9173..5b361c9a13 100644 --- a/frappe/core/doctype/log_settings/log_settings.py +++ b/frappe/core/doctype/log_settings/log_settings.py @@ -5,6 +5,9 @@ import frappe from frappe import _ from frappe.model.document import Document +from frappe.query_builder import DocType, Interval +from frappe.query_builder.functions import Now + class LogSettings(Document): def clear_logs(self): @@ -13,9 +16,10 @@ class LogSettings(Document): self.clear_email_queue() def clear_error_logs(self): - frappe.db.sql(""" DELETE FROM `tabError Log` - WHERE `creation` < (NOW() - INTERVAL '{0}' DAY) - """.format(self.clear_error_log_after)) + table = DocType("Error Log") + frappe.db.delete(table, filters=( + table.creation < Now() - Interval(days=self.clear_error_log_after) + )) def clear_activity_logs(self): from frappe.core.doctype.activity_log.activity_log import clear_activity_logs diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index e4b94cdbb6..45f7d47a27 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -16,6 +16,7 @@ from frappe.utils.user import get_system_managers from frappe.website.utils import is_signup_disabled from frappe.rate_limiter import rate_limit from frappe.core.doctype.user_type.user_type import user_linked_with_permission_on_doctype +from frappe.query_builder import DocType STANDARD_USERS = ("Guest", "Administrator") @@ -366,15 +367,21 @@ class User(Document): # delete shares frappe.db.delete("DocShare", {"user": self.name}) # delete messages - frappe.db.sql("""delete from `tabCommunication` - where communication_type in ('Chat', 'Notification') - and reference_doctype='User' - and (reference_name=%s or owner=%s)""", (self.name, self.name)) - + table = DocType("Communication") + frappe.db.delete( + table, + filters=( + (table.communication_type.isin(["Chat", "Notification"])) + & (table.reference_doctype == "User") + & ((table.reference_name == self.name) | table.owner == self.name) + ), + run=False, + ) # unlink contact - frappe.db.sql("""update `tabContact` - set `user`=null - where `user`=%s""", (self.name)) + table = DocType("Contact") + frappe.qb.update(table).where( + table.user == self.name + ).set(table.user, None).run() # delete notification settings frappe.delete_doc("Notification Settings", self.name, ignore_permissions=True) @@ -421,9 +428,10 @@ class User(Document): frappe.rename_doc("Notification Settings", old_name, new_name, force=True, show_alert=False) # set email - frappe.db.sql("""UPDATE `tabUser` - SET email = %s - WHERE name = %s""", (new_name, new_name)) + table = DocType("User") + frappe.qb.update(table).where( + table.name == new_name + ).set("email", new_name).run() def append_roles(self, *roles): """Add roles to user""" diff --git a/frappe/patches/v11_0/remove_skip_for_doctype.py b/frappe/patches/v11_0/remove_skip_for_doctype.py index 638a5a0fd7..1063dca3ff 100644 --- a/frappe/patches/v11_0/remove_skip_for_doctype.py +++ b/frappe/patches/v11_0/remove_skip_for_doctype.py @@ -2,6 +2,7 @@ import frappe from frappe.desk.form.linked_with import get_linked_doctypes from frappe.patches.v11_0.replicate_old_user_permissions import get_doctypes_to_skip +from frappe.query_builder import Field # `skip_for_doctype` was a un-normalized way of storing for which # doctypes the user permission was applicable. @@ -72,16 +73,12 @@ def execute(): frappe.db.set_value('User Permission', user_permission.name, 'apply_to_all_doctypes', 1) if new_user_permissions_list: - frappe.db.sql(''' - INSERT INTO `tabUser Permission` - (`name`, `user`, `allow`, `for_value`, `applicable_for`, `apply_to_all_doctypes`, `creation`, `modified`) - VALUES {} - '''.format( # nosec - ', '.join(['%s'] * len(new_user_permissions_list)) - ), tuple(new_user_permissions_list)) + frappe.qb.into("User Permission").columns( + "name", "user", "allow", "for_value", "applicable_for", "apply_to_all_doctypes", "creation", "modified" + ).insert(tuple(new_user_permissions_list)).run() if user_permissions_to_delete: - frappe.db.sql('DELETE FROM `tabUser Permission` WHERE `name` in ({})' # nosec - .format(','.join(['%s'] * len(user_permissions_to_delete))), - tuple(user_permissions_to_delete) + frappe.db.delete( + "User Permission", + filters=(Field("name").isin(tuple(user_permissions_to_delete))) ) From ab976aaaae255e3606c0eebde8e217846acb265b Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 1 Sep 2021 15:13:10 +0530 Subject: [PATCH 049/138] refactor: convert test_file db.sql calls --- frappe/core/doctype/file/test_file.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/frappe/core/doctype/file/test_file.py b/frappe/core/doctype/file/test_file.py index 4538ffb6bb..9a758b53f5 100644 --- a/frappe/core/doctype/file/test_file.py +++ b/frappe/core/doctype/file/test_file.py @@ -204,10 +204,14 @@ class TestFile(unittest.TestCase): def delete_test_data(self): - for f in frappe.db.sql('''select name, file_name from tabFile where - is_home_folder = 0 and is_attachments_folder = 0 order by creation desc'''): - frappe.delete_doc("File", f[0]) - + test_file_data = frappe.db.get_all( + "File", + pluck="name", + filters={"is_home_folder": 0, "is_attachments_folder": 0}, + order_by="creation desc", + ) + for f in test_file_data: + frappe.delete_doc("File", f) def upload_file(self): _file = frappe.get_doc({ From b0421ab7cca69862bcacf7922cd1d37276dfdbe3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 1 Sep 2021 15:16:46 +0530 Subject: [PATCH 050/138] refactor: convert doctype log settings db.sql calls --- frappe/core/doctype/log_settings/log_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/core/doctype/log_settings/log_settings.py b/frappe/core/doctype/log_settings/log_settings.py index 8a471b9173..c505302c52 100644 --- a/frappe/core/doctype/log_settings/log_settings.py +++ b/frappe/core/doctype/log_settings/log_settings.py @@ -38,7 +38,7 @@ def has_unseen_error_log(user): 'message': _("You have unseen {0}").format(' Error Logs ') } - if frappe.db.sql_list("select name from `tabError Log` where seen = 0 limit 1"): + if frappe.get_all("Error Log", filters={"seen": 0}, limit=1): log_settings = frappe.get_cached_doc('Log Settings') if log_settings.users_to_notify: From 3cc2202ce9bd82cc04d04b839c8e85b76bf4a7f2 Mon Sep 17 00:00:00 2001 From: abhishek Date: Wed, 13 Oct 2021 11:59:01 +0530 Subject: [PATCH 051/138] refactor: convert doctype transaction_log db.sql calls --- frappe/core/doctype/transaction_log/transaction_log.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/frappe/core/doctype/transaction_log/transaction_log.py b/frappe/core/doctype/transaction_log/transaction_log.py index bb94642f48..e2e75b130c 100644 --- a/frappe/core/doctype/transaction_log/transaction_log.py +++ b/frappe/core/doctype/transaction_log/transaction_log.py @@ -14,10 +14,9 @@ class TransactionLog(Document): self.row_index = index self.timestamp = now_datetime() if index != 1: - prev_hash = frappe.db.sql( - "SELECT `chaining_hash` FROM `tabTransaction Log` WHERE `row_index` = '{0}'".format(index - 1)) + prev_hash = frappe.get_all("Transaction Log", filters={"row_index":str(index-1)}, pluck="chaining_hash", limit=1) if prev_hash: - self.previous_hash = prev_hash[0][0] + self.previous_hash = prev_hash[0] else: self.previous_hash = "Indexing broken" else: From 0cce6e2af87d26f9844e4ace38418f190cf10f2f Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Sat, 16 Oct 2021 09:21:38 +0530 Subject: [PATCH 052/138] fix: fixed erroneous queries in translate --- .../core/doctype/activity_log/activity_log.py | 7 ++-- .../core/doctype/log_settings/log_settings.py | 3 +- frappe/translate.py | 36 +++++++++++++++---- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/frappe/core/doctype/activity_log/activity_log.py b/frappe/core/doctype/activity_log/activity_log.py index 9c27e84740..69565a2c2a 100644 --- a/frappe/core/doctype/activity_log/activity_log.py +++ b/frappe/core/doctype/activity_log/activity_log.py @@ -9,6 +9,7 @@ from frappe.core.utils import set_timeline_doc import frappe from frappe.query_builder import DocType, Interval from frappe.query_builder.functions import Now +from pypika.terms import PseudoColumn class ActivityLog(Document): def before_insert(self): @@ -47,6 +48,6 @@ def clear_activity_logs(days=None): if not days: days = 90 doctype = DocType("Activity Log") - frappe.qb.from_(doctype).where( - doctype.creation < (Now() - Interval(days=days)) - ).delete().run() \ No newline at end of file + frappe.db.delete(doctype, filters=( + doctype.creation < PseudoColumn(f"({Now() - Interval(days=days)})") + )) \ No newline at end of file diff --git a/frappe/core/doctype/log_settings/log_settings.py b/frappe/core/doctype/log_settings/log_settings.py index 5b361c9a13..008656f0f6 100644 --- a/frappe/core/doctype/log_settings/log_settings.py +++ b/frappe/core/doctype/log_settings/log_settings.py @@ -7,6 +7,7 @@ from frappe import _ from frappe.model.document import Document from frappe.query_builder import DocType, Interval from frappe.query_builder.functions import Now +from pypika.terms import PseudoColumn class LogSettings(Document): @@ -18,7 +19,7 @@ class LogSettings(Document): def clear_error_logs(self): table = DocType("Error Log") frappe.db.delete(table, filters=( - table.creation < Now() - Interval(days=self.clear_error_log_after) + table.creation < PseudoColumn(f"({Now() - Interval(days=self.clear_error_log_after)})") )) def clear_activity_logs(self): diff --git a/frappe/translate.py b/frappe/translate.py index f6a3880774..d0208bd379 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -21,6 +21,7 @@ import frappe from frappe.model.utils import InvalidIncludePath, render_include from frappe.utils import get_bench_path, is_html, strip, strip_html_tags from frappe.query_builder import Field +from pypika.terms import PseudoColumn def get_language(lang_list: List = None) -> str: @@ -153,12 +154,35 @@ def get_dict(fortype, name=None): messages += get_messages_from_navbar() messages += get_messages_from_include_files() - messages += frappe.qb.from_("Print Format").select("Print Format:", "name").run() - messages += frappe.qb.from_("DocType").select("DocType:", "name").run() - messages += frappe.qb.from_("Role").select("Role:", "name").run() - messages += frappe.qb.from_("Module Def").select("Module:", "name").run() - messages += frappe.qb.from_("Workspace Shortcut").where(Field("format").isnotnull()).select("").run() - messages += frappe.qb.from_("Onboarding Step").select("", "title").run() + messages += ( + frappe.qb.from_("Print Format") + .select(PseudoColumn("'Print Format:'"), "name") + .run() + ) + messages += ( + frappe.qb.from_("DocType") + .select(PseudoColumn("'DocType:'"), "name") + .run() + ) + messages += ( + frappe.qb.from_("Role").select(PseudoColumn("'Role:'"), "name").run() + ) + messages += ( + frappe.qb.from_("Module Def") + .select(PseudoColumn("'Module:'"), "name") + .run() + ) + messages += ( + frappe.qb.from_("Workspace Shortcut") + .where(Field("format").isnotnull()) + .select(PseudoColumn("''"), "format") + .run() + ) + messages += ( + frappe.qb.from_("Onboarding Step") + .select(PseudoColumn("''"), "title") + .run() + ) messages = deduplicate_messages(messages) message_dict = make_dict_from_messages(messages, load_user_translation=False) From d257adc1302b720016cc3ed830ede379aee4403e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 16 Oct 2021 16:48:33 +0530 Subject: [PATCH 053/138] fix: Rename loader functions --- .../js/frappe/views/reports/query_report.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index 8a50a516af..4a3a953a01 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -555,7 +555,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { refresh() { this.toggle_message(true); this.toggle_report(false); - this.show_loading_indicator(); + this.show_loading_screen(); let filters = this.get_filter_values(true); // only one refresh at a time @@ -646,7 +646,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { this.show_footer_message(); frappe.hide_progress(); }).finally(() => { - this.hide_loading_indicator(); + this.hide_loading_screen(); }); } @@ -871,18 +871,20 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { } } - show_loading_indicator() { - this.$loading.find('div').html(`
+ show_loading_screen() { + const loading_state = `
Generic Empty State

${__('Loading')}...

-
`); +
`; + + this.$loading.find('div').html(loading_state); this.$report.hide(); this.$loading.show(); } - hide_loading_indicator() { + hide_loading_screen() { this.$loading.hide(); this.$report.show(); } @@ -1699,7 +1701,6 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { this.$chart = $('
').hide().appendTo(this.page.main); this.$loading = $(this.message_div('')).hide().appendTo(this.page.main); - this.$report = $('
').appendTo(this.page.main); this.$message = $(this.message_div('')).hide().appendTo(this.page.main); } From f51b1cc73997fba7df1b356c4f82a877797d4868 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 18 Oct 2021 11:51:33 +0530 Subject: [PATCH 054/138] fix: Remove unnecessary missing_ok kwarg from unlink missing_ok added in PY38, this change breaks installs for PY37. ref: https://docs.python.org/3/library/pathlib.html#pathlib.Path.unlink --- frappe/installer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/installer.py b/frappe/installer.py index 1fe891c852..70dbbb61e8 100755 --- a/frappe/installer.py +++ b/frappe/installer.py @@ -507,14 +507,14 @@ def convert_archive_content(sql_file_path): sql_file_path = Path(sql_file_path) os.rename(sql_file_path, old_sql_file_path) - sql_file_path.unlink(missing_ok=True) + sql_file_path.unlink() sql_file_path.touch() with open(old_sql_file_path) as r, open(sql_file_path, "a") as w: for line in r: w.write(line.replace("ROW_FORMAT=COMPRESSED", "ROW_FORMAT=DYNAMIC")) - old_sql_file_path.unlink(missing_ok=True) + old_sql_file_path.unlink() def extract_sql_gzip(sql_gz_path): From 30c39acabe43de97a7fbe4a72cc6d0bcb933473c Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 18 Oct 2021 12:07:42 +0530 Subject: [PATCH 055/138] fix: Don't unlink file blindly This made sense with the missing_ok. But now, a try-except seems unnecessary too. Also, possibly destructive. Best to stray away from these things. --- frappe/installer.py | 1 - 1 file changed, 1 deletion(-) diff --git a/frappe/installer.py b/frappe/installer.py index 70dbbb61e8..d1a13fdaab 100755 --- a/frappe/installer.py +++ b/frappe/installer.py @@ -507,7 +507,6 @@ def convert_archive_content(sql_file_path): sql_file_path = Path(sql_file_path) os.rename(sql_file_path, old_sql_file_path) - sql_file_path.unlink() sql_file_path.touch() with open(old_sql_file_path) as r, open(sql_file_path, "a") as w: From 5c5c17687284006e7a12dee69cd810e9f8be5fbc Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 18 Oct 2021 12:20:06 +0530 Subject: [PATCH 056/138] fix: Check if parent exist in col before getting doc This fixes following error while downloading auto email report ``` Traceback (most recent call last): File "/Users/sps/benches/develop/apps/frappe/frappe/app.py", line 66, in application response = frappe.api.handle() File "/Users/sps/benches/develop/apps/frappe/frappe/api.py", line 54, in handle return frappe.handler.handle() File "/Users/sps/benches/develop/apps/frappe/frappe/handler.py", line 31, in handle data = execute_cmd(cmd) File "/Users/sps/benches/develop/apps/frappe/frappe/handler.py", line 67, in execute_cmd return frappe.call(method, **frappe.form_dict) File "/Users/sps/benches/develop/apps/frappe/frappe/__init__.py", line 1205, in call return fn(*args, **newargs) File "/Users/sps/benches/develop/apps/frappe/frappe/email/doctype/auto_email_report/auto_email_report.py", line 196, in download data = auto_email_report.get_report_content() File "/Users/sps/benches/develop/apps/frappe/frappe/email/doctype/auto_email_report/auto_email_report.py", line 98, in get_report_content columns, data = make_links(columns, data) File "/Users/sps/benches/develop/apps/frappe/frappe/email/doctype/auto_email_report/auto_email_report.py", line 253, in make_links doc = frappe.get_doc(col.parent, doc_name) if doc_name else None File "/Users/sps/benches/develop/apps/frappe/frappe/__init__.py", line 883, in get_doc doc = frappe.model.document.get_doc(*args, **kwargs) File "/Users/sps/benches/develop/apps/frappe/frappe/model/document.py", line 62, in get_doc raise ValueError('First non keyword argument must be a string or dict') ValueError: First non keyword argument must be a string or dict ``` The issue was introduced via https://github.com/frappe/frappe/pull/13340 --- frappe/email/doctype/auto_email_report/auto_email_report.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/email/doctype/auto_email_report/auto_email_report.py b/frappe/email/doctype/auto_email_report/auto_email_report.py index 37089d58df..7081a84e7a 100644 --- a/frappe/email/doctype/auto_email_report/auto_email_report.py +++ b/frappe/email/doctype/auto_email_report/auto_email_report.py @@ -249,7 +249,7 @@ def make_links(columns, data): if col.options and row.get(col.fieldname) and row.get(col.options): row[col.fieldname] = get_link_to_form(row[col.options], row[col.fieldname]) elif col.fieldtype == "Currency" and row.get(col.fieldname): - doc = frappe.get_doc(col.parent, doc_name) if doc_name else None + doc = frappe.get_doc(col.parent, doc_name) if doc_name and col.parent else None # Pass the Document to get the currency based on docfield option row[col.fieldname] = frappe.format_value(row[col.fieldname], col, doc=doc) return columns, data From 20d7a9df78bb227a8f938558b52235162923ce4e Mon Sep 17 00:00:00 2001 From: Summayya Hashmani <58825865+sumaiya2908@users.noreply.github.com> Date: Mon, 18 Oct 2021 13:32:35 +0530 Subject: [PATCH 057/138] feat: Add confirm password field (#14281) * fix: remove commented code * refactor: add confirm password field * fix: remove dialog and add inline warning * fix: capitalize words and text function * fix: capitalize words * style: Fix formatting * refactor:update class name Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * refactor: remove indicator class and add margin Co-authored-by: Summayya Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- frappe/www/update-password.html | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/frappe/www/update-password.html b/frappe/www/update-password.html index 0d66fe5ab5..cacbce35b3 100644 --- a/frappe/www/update-password.html +++ b/frappe/www/update-password.html @@ -10,7 +10,7 @@

{{ _("Reset Password") if frappe.db.get_default('company') else _("Set Password")}}

- +
+ + + +
@@ -38,7 +44,7 @@ - diff --git a/frappe/public/js/print_format_builder/PrintFormat.vue b/frappe/public/js/print_format_builder/PrintFormat.vue index 7163282067..5138a54a38 100644 --- a/frappe/public/js/print_format_builder/PrintFormat.vue +++ b/frappe/public/js/print_format_builder/PrintFormat.vue @@ -1,11 +1,6 @@