From 97ae23c2a1f098fc38a408fac8273c3b4e4130c9 Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Wed, 23 Oct 2019 15:42:24 +0530
Subject: [PATCH 001/572] fix: allow overriding columns from settings
---
.../list_view_setting/list_view_setting.json | 160 ------------------
.../test_list_view_setting.py | 9 -
.../__init__.py | 0
.../list_view_settings.js} | 2 +-
.../list_view_settings.json | 59 +++++++
.../list_view_settings.py} | 4 +-
frappe/desk/listview.py | 7 +-
frappe/public/js/frappe/list/list_view.js | 10 +-
frappe/tests/test_listview.py | 14 +-
9 files changed, 77 insertions(+), 188 deletions(-)
delete mode 100644 frappe/desk/doctype/list_view_setting/list_view_setting.json
delete mode 100644 frappe/desk/doctype/list_view_setting/test_list_view_setting.py
rename frappe/desk/doctype/{list_view_setting => list_view_settings}/__init__.py (100%)
rename frappe/desk/doctype/{list_view_setting/list_view_setting.js => list_view_settings/list_view_settings.js} (78%)
create mode 100644 frappe/desk/doctype/list_view_settings/list_view_settings.json
rename frappe/desk/doctype/{list_view_setting/list_view_setting.py => list_view_settings/list_view_settings.py} (85%)
diff --git a/frappe/desk/doctype/list_view_setting/list_view_setting.json b/frappe/desk/doctype/list_view_setting/list_view_setting.json
deleted file mode 100644
index cd18d3f766..0000000000
--- a/frappe/desk/doctype/list_view_setting/list_view_setting.json
+++ /dev/null
@@ -1,160 +0,0 @@
-{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "autoname": "Prompt",
- "beta": 0,
- "creation": "2019-03-06 13:29:21.101860",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
- "fields": [
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "disable_count",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Disable Count",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "disable_sidebar_stats",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Disable Sidebar Stats",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "disable_auto_refresh",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Disable Auto Refresh",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2019-03-06 13:40:59.533586",
- "modified_by": "Administrator",
- "module": "Desk",
- "name": "List View Setting",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [
- {
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 0,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- }
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0,
- "track_views": 0
-}
\ No newline at end of file
diff --git a/frappe/desk/doctype/list_view_setting/test_list_view_setting.py b/frappe/desk/doctype/list_view_setting/test_list_view_setting.py
deleted file mode 100644
index 143fc4cce7..0000000000
--- a/frappe/desk/doctype/list_view_setting/test_list_view_setting.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2019, Frappe Technologies and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import unittest
-
-class TestListViewSetting(unittest.TestCase):
- pass
diff --git a/frappe/desk/doctype/list_view_setting/__init__.py b/frappe/desk/doctype/list_view_settings/__init__.py
similarity index 100%
rename from frappe/desk/doctype/list_view_setting/__init__.py
rename to frappe/desk/doctype/list_view_settings/__init__.py
diff --git a/frappe/desk/doctype/list_view_setting/list_view_setting.js b/frappe/desk/doctype/list_view_settings/list_view_settings.js
similarity index 78%
rename from frappe/desk/doctype/list_view_setting/list_view_setting.js
rename to frappe/desk/doctype/list_view_settings/list_view_settings.js
index 2c70ddf82d..20c11c0215 100644
--- a/frappe/desk/doctype/list_view_setting/list_view_setting.js
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.js
@@ -1,7 +1,7 @@
// Copyright (c) 2019, Frappe Technologies and contributors
// For license information, please see license.txt
-frappe.ui.form.on('List View Setting', {
+frappe.ui.form.on('List View Settings', {
// refresh: function(frm) {
// }
diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.json b/frappe/desk/doctype/list_view_settings/list_view_settings.json
new file mode 100644
index 0000000000..783a65b8aa
--- /dev/null
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.json
@@ -0,0 +1,59 @@
+{
+ "autoname": "Prompt",
+ "creation": "2019-10-23 15:00:48.392374",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "disable_count",
+ "disable_sidebar_stats",
+ "disable_auto_refresh",
+ "column_count"
+ ],
+ "fields": [
+ {
+ "default": "0",
+ "fieldname": "disable_count",
+ "fieldtype": "Check",
+ "label": "Disable Count"
+ },
+ {
+ "default": "0",
+ "fieldname": "disable_sidebar_stats",
+ "fieldtype": "Check",
+ "label": "Disable Sidebar Stats"
+ },
+ {
+ "default": "0",
+ "fieldname": "disable_auto_refresh",
+ "fieldtype": "Check",
+ "label": "Disable Auto Refresh"
+ },
+ {
+ "fieldname": "column_count",
+ "fieldtype": "Select",
+ "label": "Column Count",
+ "options": "\n4\n5\n6\n7\n8\n9\n10"
+ }
+ ],
+ "modified": "2019-10-23 15:11:41.710014",
+ "modified_by": "Administrator",
+ "module": "Desk",
+ "name": "List View Settings",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/frappe/desk/doctype/list_view_setting/list_view_setting.py b/frappe/desk/doctype/list_view_settings/list_view_settings.py
similarity index 85%
rename from frappe/desk/doctype/list_view_setting/list_view_setting.py
rename to frappe/desk/doctype/list_view_settings/list_view_settings.py
index b66dc29a43..b7cafed068 100644
--- a/frappe/desk/doctype/list_view_setting/list_view_setting.py
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.py
@@ -6,5 +6,5 @@ from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
-class ListViewSetting(Document):
- pass
+class ListViewSettings(Document):
+ pass
\ No newline at end of file
diff --git a/frappe/desk/listview.py b/frappe/desk/listview.py
index 3dc795191a..7f1c120d31 100644
--- a/frappe/desk/listview.py
+++ b/frappe/desk/listview.py
@@ -7,17 +7,16 @@ import frappe
@frappe.whitelist()
def get_list_settings(doctype):
try:
- return frappe.get_cached_doc("List View Setting", doctype)
+ return frappe.get_cached_doc("List View Settings", doctype)
except frappe.DoesNotExistError:
frappe.clear_messages()
-
@frappe.whitelist()
def set_list_settings(doctype, values):
try:
- doc = frappe.get_doc("List View Setting", doctype)
+ doc = frappe.get_doc("List View Settings", doctype)
except frappe.DoesNotExistError:
- doc = frappe.new_doc("List View Setting")
+ doc = frappe.new_doc("List View Settings")
doc.name = doctype
frappe.clear_messages()
doc.update(frappe.parse_json(values))
diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js
index bb79b3441f..41a48589aa 100644
--- a/frappe/public/js/frappe/list/list_view.js
+++ b/frappe/public/js/frappe/list/list_view.js
@@ -292,13 +292,13 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
// Screen with high density no of columns 8
let column_count = 6;
- if (window.innerWidth <= 1200) {
+ if (window.innerWidth <= 1366) {
column_count = 4;
- } else if (window.innerWidth > 1440) {
+ } else if (window.innerWidth >= 1920) {
column_count = 8;
}
- this.columns = this.columns.slice(0, column_count);
+ this.columns = this.columns.slice(0, this.list_view_settings.column_count || column_count);
}
get_no_result_message() {
@@ -1239,10 +1239,10 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
}
show_list_settings() {
- frappe.model.with_doctype("List View Setting", () => {
+ frappe.model.with_doctype("List View Settings", () => {
let d = new frappe.ui.Dialog({
title: __("Settings"),
- fields: frappe.get_meta("List View Setting").fields
+ fields: frappe.get_meta("List View Settings").fields
});
d.set_values(this.list_view_settings);
d.show();
diff --git a/frappe/tests/test_listview.py b/frappe/tests/test_listview.py
index 5c0657e9ee..9b4623eca6 100644
--- a/frappe/tests/test_listview.py
+++ b/frappe/tests/test_listview.py
@@ -10,14 +10,14 @@ from frappe.desk.listview import get_list_settings, set_list_settings
class TestListView(unittest.TestCase):
def setUp(self):
- if frappe.db.exists("List View Setting", "DocType"):
- frappe.delete_doc("List View Setting", "DocType")
+ if frappe.db.exists("List View Settings", "DocType"):
+ frappe.delete_doc("List View Settings", "DocType")
def test_get_list_settings_without_settings(self):
self.assertIsNone(get_list_settings("DocType"), None)
def test_get_list_settings_with_default_settings(self):
- frappe.get_doc({"doctype": "List View Setting", "name": "DocType"}).insert()
+ frappe.get_doc({"doctype": "List View Settings", "name": "DocType"}).insert()
settings = get_list_settings("DocType")
self.assertIsNotNone(settings)
@@ -26,7 +26,7 @@ class TestListView(unittest.TestCase):
self.assertEqual(settings.disable_sidebar_stats, 0)
def test_get_list_settings_with_non_default_settings(self):
- frappe.get_doc({"doctype": "List View Setting", "name": "DocType", "disable_count": 1}).insert()
+ frappe.get_doc({"doctype": "List View Settings", "name": "DocType", "disable_count": 1}).insert()
settings = get_list_settings("DocType")
self.assertIsNotNone(settings)
@@ -36,16 +36,16 @@ class TestListView(unittest.TestCase):
def test_set_list_settings_without_settings(self):
set_list_settings("DocType", json.dumps({}))
- settings = frappe.get_doc("List View Setting","DocType")
+ settings = frappe.get_doc("List View Settings","DocType")
self.assertEqual(settings.disable_auto_refresh, 0)
self.assertEqual(settings.disable_count, 0)
self.assertEqual(settings.disable_sidebar_stats, 0)
def test_set_list_settings_with_existing_settings(self):
- frappe.get_doc({"doctype": "List View Setting", "name": "DocType", "disable_count": 1}).insert()
+ frappe.get_doc({"doctype": "List View Settings", "name": "DocType", "disable_count": 1}).insert()
set_list_settings("DocType", json.dumps({"disable_count": 0, "disable_auto_refresh": 1}))
- settings = frappe.get_doc("List View Setting","DocType")
+ settings = frappe.get_doc("List View Settings","DocType")
self.assertEqual(settings.disable_auto_refresh, 1)
self.assertEqual(settings.disable_count, 0)
From 384cbcb38e5266159bdf3030c1d5aa2b016b834d Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Wed, 23 Oct 2019 17:02:06 +0530
Subject: [PATCH 002/572] fix: clear cached doc
---
.../desk/doctype/list_view_settings/list_view_settings.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.py b/frappe/desk/doctype/list_view_settings/list_view_settings.py
index b7cafed068..97803bf6c6 100644
--- a/frappe/desk/doctype/list_view_settings/list_view_settings.py
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-# import frappe
+import frappe
from frappe.model.document import Document
class ListViewSettings(Document):
- pass
\ No newline at end of file
+
+ def validate(self):
+ frappe.clear_document_cache(self.doctype, self.name)
\ No newline at end of file
From b8dc3f125803eae84ff83a9f423a762e8cd9c954 Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Wed, 23 Oct 2019 17:03:50 +0530
Subject: [PATCH 003/572] fix: clear cache on_update
---
frappe/desk/doctype/list_view_settings/list_view_settings.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.py b/frappe/desk/doctype/list_view_settings/list_view_settings.py
index 97803bf6c6..b1d9fb6da8 100644
--- a/frappe/desk/doctype/list_view_settings/list_view_settings.py
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.py
@@ -8,5 +8,5 @@ from frappe.model.document import Document
class ListViewSettings(Document):
- def validate(self):
+ def on_update(self):
frappe.clear_document_cache(self.doctype, self.name)
\ No newline at end of file
From 46b25297db9347e353b7fd3b0a1306559296669f Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Wed, 23 Oct 2019 19:20:26 +0530
Subject: [PATCH 004/572] fix: clear cache on settings change
---
frappe/desk/doctype/list_view_settings/list_view_settings.json | 2 +-
frappe/public/js/frappe/list/list_view.js | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.json b/frappe/desk/doctype/list_view_settings/list_view_settings.json
index 783a65b8aa..aa3bbf464b 100644
--- a/frappe/desk/doctype/list_view_settings/list_view_settings.json
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.json
@@ -36,7 +36,7 @@
"options": "\n4\n5\n6\n7\n8\n9\n10"
}
],
- "modified": "2019-10-23 15:11:41.710014",
+ "modified": "2019-10-23 19:13:41.427000",
"modified_by": "Administrator",
"module": "Desk",
"name": "List View Settings",
diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js
index 41a48589aa..2cd04b50c2 100644
--- a/frappe/public/js/frappe/list/list_view.js
+++ b/frappe/public/js/frappe/list/list_view.js
@@ -1251,6 +1251,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
frappe.call("frappe.desk.listview.set_list_settings", {doctype: this.doctype, values: values});
Object.assign(this.list_view_settings, values);
d.hide();
+ frappe.ui.toolbar.clear_cache();
});
});
}
From b3322520cfb865cd5faa7aab488f5631f260caff Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Thu, 24 Oct 2019 18:12:12 +0530
Subject: [PATCH 005/572] feat: fetch listview columns
---
.../list_view_column_order/__init__.py | 0
.../list_view_column_order.json | 35 +++++++++++++++++++
.../list_view_column_order.py | 10 ++++++
.../list_view_settings/list_view_settings.js | 21 +++++++++--
.../list_view_settings.json | 11 ++++--
.../list_view_settings/list_view_settings.py | 29 ++++++++++++++-
frappe/public/js/frappe/list/list_view.js | 8 ++++-
7 files changed, 107 insertions(+), 7 deletions(-)
create mode 100644 frappe/desk/doctype/list_view_column_order/__init__.py
create mode 100644 frappe/desk/doctype/list_view_column_order/list_view_column_order.json
create mode 100644 frappe/desk/doctype/list_view_column_order/list_view_column_order.py
diff --git a/frappe/desk/doctype/list_view_column_order/__init__.py b/frappe/desk/doctype/list_view_column_order/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/desk/doctype/list_view_column_order/list_view_column_order.json b/frappe/desk/doctype/list_view_column_order/list_view_column_order.json
new file mode 100644
index 0000000000..2db000e706
--- /dev/null
+++ b/frappe/desk/doctype/list_view_column_order/list_view_column_order.json
@@ -0,0 +1,35 @@
+{
+ "creation": "2019-10-24 07:54:42.901997",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "label",
+ "fieldname"
+ ],
+ "fields": [
+ {
+ "fieldname": "label",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Label",
+ "read_only": 1
+ },
+ {
+ "fieldname": "fieldname",
+ "fieldtype": "Data",
+ "label": "Fieldname",
+ "read_only": 1
+ }
+ ],
+ "istable": 1,
+ "modified": "2019-10-24 18:08:40.740953",
+ "modified_by": "Administrator",
+ "module": "Desk",
+ "name": "List View Column Order",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/frappe/desk/doctype/list_view_column_order/list_view_column_order.py b/frappe/desk/doctype/list_view_column_order/list_view_column_order.py
new file mode 100644
index 0000000000..598e6398b4
--- /dev/null
+++ b/frappe/desk/doctype/list_view_column_order/list_view_column_order.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class ListViewColumnOrder(Document):
+ pass
diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.js b/frappe/desk/doctype/list_view_settings/list_view_settings.js
index 20c11c0215..4f30dda86b 100644
--- a/frappe/desk/doctype/list_view_settings/list_view_settings.js
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.js
@@ -2,7 +2,22 @@
// For license information, please see license.txt
frappe.ui.form.on('List View Settings', {
- // refresh: function(frm) {
-
- // }
+ refresh: function(frm) {
+ frm.add_custom_button('Get List View Columns', function () {
+ frappe.call({
+ method: "frappe.desk.doctype.list_view_settings.list_view_settings.get_listview_columns",
+ args: {
+ doctype: frm.doc.name
+ },
+ callback: function (r) {
+ if (r && r.message) {
+ for (let i in r.message) {
+ frm.add_child("column_order", r.message[i]);
+ }
+ frm.refresh();
+ }
+ }
+ });
+ });
+ }
});
diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.json b/frappe/desk/doctype/list_view_settings/list_view_settings.json
index aa3bbf464b..e16893a875 100644
--- a/frappe/desk/doctype/list_view_settings/list_view_settings.json
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.json
@@ -8,7 +8,8 @@
"disable_count",
"disable_sidebar_stats",
"disable_auto_refresh",
- "column_count"
+ "column_count",
+ "column_order"
],
"fields": [
{
@@ -34,9 +35,15 @@
"fieldtype": "Select",
"label": "Column Count",
"options": "\n4\n5\n6\n7\n8\n9\n10"
+ },
+ {
+ "fieldname": "column_order",
+ "fieldtype": "Table",
+ "label": "Column Order",
+ "options": "List View Column Order"
}
],
- "modified": "2019-10-23 19:13:41.427000",
+ "modified": "2019-10-24 14:34:03.358787",
"modified_by": "Administrator",
"module": "Desk",
"name": "List View Settings",
diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.py b/frappe/desk/doctype/list_view_settings/list_view_settings.py
index b1d9fb6da8..f8947c8ee0 100644
--- a/frappe/desk/doctype/list_view_settings/list_view_settings.py
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.py
@@ -9,4 +9,31 @@ from frappe.model.document import Document
class ListViewSettings(Document):
def on_update(self):
- frappe.clear_document_cache(self.doctype, self.name)
\ No newline at end of file
+ frappe.clear_document_cache(self.doctype, self.name)
+
+@frappe.whitelist()
+def get_listview_columns(doctype):
+ meta = frappe.get_meta(doctype)
+ listview_columns = []
+
+ subject_field = {
+ "label": "Name",
+ "fieldname": "name"
+ }
+
+ if meta.title_field:
+ title_field = meta.get_field(meta.title_field)
+
+ subject_field["label"] = title_field.label,
+ subject_field["fieldname"] = title_field.fieldname
+
+ listview_columns.append(subject_field)
+
+ for field in meta.fields:
+ if field.in_list_view and field.label and field.fieldname:
+ listview_columns.append({
+ "label": field.label,
+ "fieldname": field.fieldname
+ })
+
+ return listview_columns
\ No newline at end of file
diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js
index 2cd04b50c2..5f29f3bd9d 100644
--- a/frappe/public/js/frappe/list/list_view.js
+++ b/frappe/public/js/frappe/list/list_view.js
@@ -1240,9 +1240,15 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
show_list_settings() {
frappe.model.with_doctype("List View Settings", () => {
+ let list_view_settings = frappe.get_meta("List View Settings");
let d = new frappe.ui.Dialog({
title: __("Settings"),
- fields: frappe.get_meta("List View Settings").fields
+ fields: [
+ list_view_settings.fields[0],
+ list_view_settings.fields[1],
+ list_view_settings.fields[2],
+ list_view_settings.fields[3],
+ ]
});
d.set_values(this.list_view_settings);
d.show();
From bddef3d20498b22c70846d31fd52cdd8cd76d7f9 Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Thu, 24 Oct 2019 19:15:03 +0530
Subject: [PATCH 006/572] feat: rearrange column order
---
.../list_view_settings/list_view_settings.js | 4 +++-
.../list_view_settings/list_view_settings.py | 4 ++--
frappe/public/js/frappe/list/list_view.js | 23 +++++++++++++++++++
3 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.js b/frappe/desk/doctype/list_view_settings/list_view_settings.js
index 4f30dda86b..72f6c16684 100644
--- a/frappe/desk/doctype/list_view_settings/list_view_settings.js
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.js
@@ -11,8 +11,10 @@ frappe.ui.form.on('List View Settings', {
},
callback: function (r) {
if (r && r.message) {
- for (let i in r.message) {
+ let i = 0;
+ while (i < frm.doc.column_count) {
frm.add_child("column_order", r.message[i]);
+ i++;
}
frm.refresh();
}
diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.py b/frappe/desk/doctype/list_view_settings/list_view_settings.py
index f8947c8ee0..90ba4d2883 100644
--- a/frappe/desk/doctype/list_view_settings/list_view_settings.py
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.py
@@ -24,7 +24,7 @@ def get_listview_columns(doctype):
if meta.title_field:
title_field = meta.get_field(meta.title_field)
- subject_field["label"] = title_field.label,
+ subject_field["label"] = title_field.label
subject_field["fieldname"] = title_field.fieldname
listview_columns.append(subject_field)
@@ -36,4 +36,4 @@ def get_listview_columns(doctype):
"fieldname": field.fieldname
})
- return listview_columns
\ No newline at end of file
+ return listview_columns
diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js
index 5f29f3bd9d..1dac93c296 100644
--- a/frappe/public/js/frappe/list/list_view.js
+++ b/frappe/public/js/frappe/list/list_view.js
@@ -286,6 +286,29 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
}))
);
+ if (this.list_view_settings.column_order) {
+ let custom_column_order = [];
+
+ //title_field or name and status are fixed
+ custom_column_order.push(this.columns[0]);
+ custom_column_order.push(this.columns[1]);
+
+ this.columns.splice(0, 2)
+
+ for (let i in this.list_view_settings.column_order) {
+ let fieldname = this.list_view_settings.column_order[i].fieldname;
+ for (let j in this.columns) {
+ let df = this.columns[j].df.fieldname;
+ if (fieldname === df) {
+ custom_column_order.push(this.columns[j]);
+ break;
+ }
+ }
+ }
+
+ this.columns = custom_column_order;
+ }
+
// limit max to 8 columns
// Screen with low density no of columns 4
// Screen with medium density no of columns 6
From c8ecda58567446e8858ececb701945832bf47658 Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Thu, 24 Oct 2019 19:46:44 +0530
Subject: [PATCH 007/572] fix: refresh cache on update
---
frappe/desk/doctype/list_view_settings/list_view_settings.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.py b/frappe/desk/doctype/list_view_settings/list_view_settings.py
index 90ba4d2883..adf92e8ec6 100644
--- a/frappe/desk/doctype/list_view_settings/list_view_settings.py
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.py
@@ -10,6 +10,7 @@ class ListViewSettings(Document):
def on_update(self):
frappe.clear_document_cache(self.doctype, self.name)
+ frappe.get_cached_doc(self.doctype, self.name)
@frappe.whitelist()
def get_listview_columns(doctype):
From 89801512942bb03385cb43d2c4680591798cef01 Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Fri, 25 Oct 2019 11:58:00 +0530
Subject: [PATCH 008/572] chore: fix codacy
---
frappe/desk/doctype/list_view_settings/list_view_settings.json | 2 +-
frappe/public/js/frappe/list/list_view.js | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.json b/frappe/desk/doctype/list_view_settings/list_view_settings.json
index e16893a875..3911467980 100644
--- a/frappe/desk/doctype/list_view_settings/list_view_settings.json
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.json
@@ -43,7 +43,7 @@
"options": "List View Column Order"
}
],
- "modified": "2019-10-24 14:34:03.358787",
+ "modified": "2019-10-25 11:57:00.314570",
"modified_by": "Administrator",
"module": "Desk",
"name": "List View Settings",
diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js
index 1dac93c296..6a0a8575b8 100644
--- a/frappe/public/js/frappe/list/list_view.js
+++ b/frappe/public/js/frappe/list/list_view.js
@@ -293,7 +293,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
custom_column_order.push(this.columns[0]);
custom_column_order.push(this.columns[1]);
- this.columns.splice(0, 2)
+ this.columns.splice(0, 2);
for (let i in this.list_view_settings.column_order) {
let fieldname = this.list_view_settings.column_order[i].fieldname;
@@ -1264,6 +1264,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
show_list_settings() {
frappe.model.with_doctype("List View Settings", () => {
let list_view_settings = frappe.get_meta("List View Settings");
+
let d = new frappe.ui.Dialog({
title: __("Settings"),
fields: [
From b7bc2d0fa0abd0ceac587e4f818f53210fcc5763 Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Fri, 25 Oct 2019 21:43:00 +0530
Subject: [PATCH 009/572] fix: just fix title or name field in listview
---
frappe/public/js/frappe/list/list_view.js | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js
index 6a0a8575b8..b88e4a4a7a 100644
--- a/frappe/public/js/frappe/list/list_view.js
+++ b/frappe/public/js/frappe/list/list_view.js
@@ -289,11 +289,9 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
if (this.list_view_settings.column_order) {
let custom_column_order = [];
- //title_field or name and status are fixed
+ //title_field is fixed
custom_column_order.push(this.columns[0]);
- custom_column_order.push(this.columns[1]);
-
- this.columns.splice(0, 2);
+ this.columns.splice(0, 1);
for (let i in this.list_view_settings.column_order) {
let fieldname = this.list_view_settings.column_order[i].fieldname;
From b13c9c4408d52dd3616596b26a8ac7d189df59c0 Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Fri, 25 Oct 2019 22:33:48 +0530
Subject: [PATCH 010/572] fix: column order
---
frappe/desk/doctype/list_view_settings/list_view_settings.js | 4 ++++
frappe/public/js/frappe/list/list_view.js | 5 +++--
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.js b/frappe/desk/doctype/list_view_settings/list_view_settings.js
index 72f6c16684..59ad59444b 100644
--- a/frappe/desk/doctype/list_view_settings/list_view_settings.js
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.js
@@ -3,6 +3,10 @@
frappe.ui.form.on('List View Settings', {
refresh: function(frm) {
+ frm.add_custom_button(__('Go to {0} List', [frm.doc.name]), () => {
+ frappe.set_route('List', frm.doc.name, 'List');
+ });
+
frm.add_custom_button('Get List View Columns', function () {
frappe.call({
method: "frappe.desk.doctype.list_view_settings.list_view_settings.get_listview_columns",
diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js
index b88e4a4a7a..b1ab2f88c7 100644
--- a/frappe/public/js/frappe/list/list_view.js
+++ b/frappe/public/js/frappe/list/list_view.js
@@ -286,7 +286,8 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
}))
);
- if (this.list_view_settings.column_order) {
+ if (this.list_view_settings.column_order.length > 0 &&
+ this.list_view_settings.column_order.length === this.list_view_settings.column_count) {
let custom_column_order = [];
//title_field is fixed
@@ -307,7 +308,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
this.columns = custom_column_order;
}
- // limit max to 8 columns
+ // limit max to 8 columns if no column_count is set in List View Settings
// Screen with low density no of columns 4
// Screen with medium density no of columns 6
// Screen with high density no of columns 8
From 0af5d38b41a37e7ce77afbb7d416087343b51680 Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Mon, 28 Oct 2019 17:09:05 +0530
Subject: [PATCH 011/572] fix: check if new doc
---
.../doctype/list_view_settings/list_view_settings.js | 9 ++++++++-
frappe/public/js/frappe/list/list_view.js | 2 +-
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.js b/frappe/desk/doctype/list_view_settings/list_view_settings.js
index 59ad59444b..8109c6d45b 100644
--- a/frappe/desk/doctype/list_view_settings/list_view_settings.js
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.js
@@ -3,6 +3,10 @@
frappe.ui.form.on('List View Settings', {
refresh: function(frm) {
+ if (frm.is_new()) {
+ return;
+ }
+
frm.add_custom_button(__('Go to {0} List', [frm.doc.name]), () => {
frappe.set_route('List', frm.doc.name, 'List');
});
@@ -16,8 +20,11 @@ frappe.ui.form.on('List View Settings', {
callback: function (r) {
if (r && r.message) {
let i = 0;
+ frm.clear_table("column_order");
while (i < frm.doc.column_count) {
- frm.add_child("column_order", r.message[i]);
+ if (r.message[i]) {
+ frm.add_child("column_order", r.message[i]);
+ }
i++;
}
frm.refresh();
diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js
index b1ab2f88c7..13dd08832a 100644
--- a/frappe/public/js/frappe/list/list_view.js
+++ b/frappe/public/js/frappe/list/list_view.js
@@ -286,7 +286,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
}))
);
- if (this.list_view_settings.column_order.length > 0 &&
+ if (this.list_view_settings.column_order && this.list_view_settings.column_order.length > 0 &&
this.list_view_settings.column_order.length === this.list_view_settings.column_count) {
let custom_column_order = [];
From a26675098d172174fc57ee6662fa97db7fb78a30 Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Mon, 18 Nov 2019 10:12:01 +0530
Subject: [PATCH 012/572] fix: remove child table and use html
---
.../list_view_column_order/__init__.py | 0
.../list_view_column_order.json | 35 -------------------
.../list_view_column_order.py | 10 ------
.../list_view_settings/list_view_columns.html | 9 +++++
.../list_view_settings/list_view_settings.js | 19 +++++-----
.../list_view_settings.json | 13 ++++---
.../test_list_view_settings.py | 10 ++++++
7 files changed, 38 insertions(+), 58 deletions(-)
delete mode 100644 frappe/desk/doctype/list_view_column_order/__init__.py
delete mode 100644 frappe/desk/doctype/list_view_column_order/list_view_column_order.json
delete mode 100644 frappe/desk/doctype/list_view_column_order/list_view_column_order.py
create mode 100644 frappe/desk/doctype/list_view_settings/list_view_columns.html
create mode 100644 frappe/desk/doctype/list_view_settings/test_list_view_settings.py
diff --git a/frappe/desk/doctype/list_view_column_order/__init__.py b/frappe/desk/doctype/list_view_column_order/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/desk/doctype/list_view_column_order/list_view_column_order.json b/frappe/desk/doctype/list_view_column_order/list_view_column_order.json
deleted file mode 100644
index 2db000e706..0000000000
--- a/frappe/desk/doctype/list_view_column_order/list_view_column_order.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "creation": "2019-10-24 07:54:42.901997",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "label",
- "fieldname"
- ],
- "fields": [
- {
- "fieldname": "label",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Label",
- "read_only": 1
- },
- {
- "fieldname": "fieldname",
- "fieldtype": "Data",
- "label": "Fieldname",
- "read_only": 1
- }
- ],
- "istable": 1,
- "modified": "2019-10-24 18:08:40.740953",
- "modified_by": "Administrator",
- "module": "Desk",
- "name": "List View Column Order",
- "owner": "Administrator",
- "permissions": [],
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/frappe/desk/doctype/list_view_column_order/list_view_column_order.py b/frappe/desk/doctype/list_view_column_order/list_view_column_order.py
deleted file mode 100644
index 598e6398b4..0000000000
--- a/frappe/desk/doctype/list_view_column_order/list_view_column_order.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2019, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class ListViewColumnOrder(Document):
- pass
diff --git a/frappe/desk/doctype/list_view_settings/list_view_columns.html b/frappe/desk/doctype/list_view_settings/list_view_columns.html
new file mode 100644
index 0000000000..50474c5169
--- /dev/null
+++ b/frappe/desk/doctype/list_view_settings/list_view_columns.html
@@ -0,0 +1,9 @@
+
diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.js b/frappe/desk/doctype/list_view_settings/list_view_settings.js
index 8109c6d45b..3b8654e349 100644
--- a/frappe/desk/doctype/list_view_settings/list_view_settings.js
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.js
@@ -19,18 +19,19 @@ frappe.ui.form.on('List View Settings', {
},
callback: function (r) {
if (r && r.message) {
- let i = 0;
- frm.clear_table("column_order");
- while (i < frm.doc.column_count) {
- if (r.message[i]) {
- frm.add_child("column_order", r.message[i]);
- }
- i++;
- }
- frm.refresh();
+ render_fields(frm, r.message);
}
}
});
});
+
+ if (frm.doc.column_order) {
+ render_fields(frm, frm.doc.column_order);
+ }
}
});
+
+function render_fields(frm, fields) {
+ let columns_html = frm.get_field("column_order");
+ columns_html.html(frappe.render_template("list_view_columns", {fields: fields}));
+}
\ No newline at end of file
diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.json b/frappe/desk/doctype/list_view_settings/list_view_settings.json
index 3911467980..95ef3b2acb 100644
--- a/frappe/desk/doctype/list_view_settings/list_view_settings.json
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.json
@@ -9,6 +9,7 @@
"disable_sidebar_stats",
"disable_auto_refresh",
"column_count",
+ "columns",
"column_order"
],
"fields": [
@@ -38,12 +39,16 @@
},
{
"fieldname": "column_order",
- "fieldtype": "Table",
- "label": "Column Order",
- "options": "List View Column Order"
+ "fieldtype": "Data",
+ "label": "Column Order"
+ },
+ {
+ "fieldname": "columns",
+ "fieldtype": "HTML",
+ "label": "Columns"
}
],
- "modified": "2019-10-25 11:57:00.314570",
+ "modified": "2019-11-18 10:05:11.859157",
"modified_by": "Administrator",
"module": "Desk",
"name": "List View Settings",
diff --git a/frappe/desk/doctype/list_view_settings/test_list_view_settings.py b/frappe/desk/doctype/list_view_settings/test_list_view_settings.py
new file mode 100644
index 0000000000..c1b2f4a0da
--- /dev/null
+++ b/frappe/desk/doctype/list_view_settings/test_list_view_settings.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestListViewSettings(unittest.TestCase):
+ pass
From 4775d1b6ee5761a1ffa4988b463f5cc7854c5f05 Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Tue, 14 Apr 2020 11:41:31 +0530
Subject: [PATCH 013/572] feat: new UI for list view settings
---
.../list_view_settings/list_view_settings.js | 37 +-
.../list_view_settings.json | 41 ++-
.../list_view_settings/list_view_settings.py | 84 +++--
frappe/public/js/frappe/list/list_settings.js | 320 ++++++++++++++++++
frappe/public/js/frappe/list/list_view.js | 64 ++--
5 files changed, 442 insertions(+), 104 deletions(-)
create mode 100644 frappe/public/js/frappe/list/list_settings.js
diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.js b/frappe/desk/doctype/list_view_settings/list_view_settings.js
index 3b8654e349..db33f71675 100644
--- a/frappe/desk/doctype/list_view_settings/list_view_settings.js
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.js
@@ -1,37 +1,8 @@
-// Copyright (c) 2019, Frappe Technologies and contributors
+// Copyright (c) 2020, Frappe Technologies and contributors
// For license information, please see license.txt
frappe.ui.form.on('List View Settings', {
- refresh: function(frm) {
- if (frm.is_new()) {
- return;
- }
+ // refresh: function(frm) {
- frm.add_custom_button(__('Go to {0} List', [frm.doc.name]), () => {
- frappe.set_route('List', frm.doc.name, 'List');
- });
-
- frm.add_custom_button('Get List View Columns', function () {
- frappe.call({
- method: "frappe.desk.doctype.list_view_settings.list_view_settings.get_listview_columns",
- args: {
- doctype: frm.doc.name
- },
- callback: function (r) {
- if (r && r.message) {
- render_fields(frm, r.message);
- }
- }
- });
- });
-
- if (frm.doc.column_order) {
- render_fields(frm, frm.doc.column_order);
- }
- }
-});
-
-function render_fields(frm, fields) {
- let columns_html = frm.get_field("column_order");
- columns_html.html(frappe.render_template("list_view_columns", {fields: fields}));
-}
\ No newline at end of file
+ // }
+});
\ No newline at end of file
diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.json b/frappe/desk/doctype/list_view_settings/list_view_settings.json
index 95ef3b2acb..e59991c52e 100644
--- a/frappe/desk/doctype/list_view_settings/list_view_settings.json
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"autoname": "Prompt",
"creation": "2019-10-23 15:00:48.392374",
"doctype": "DocType",
@@ -8,9 +9,9 @@
"disable_count",
"disable_sidebar_stats",
"disable_auto_refresh",
- "column_count",
- "columns",
- "column_order"
+ "total_fields",
+ "fields_html",
+ "fields"
],
"fields": [
{
@@ -32,23 +33,26 @@
"label": "Disable Auto Refresh"
},
{
- "fieldname": "column_count",
+ "fieldname": "total_fields",
"fieldtype": "Select",
- "label": "Column Count",
+ "label": "Total Fields",
"options": "\n4\n5\n6\n7\n8\n9\n10"
},
{
- "fieldname": "column_order",
- "fieldtype": "Data",
- "label": "Column Order"
+ "fieldname": "fields_html",
+ "fieldtype": "HTML",
+ "label": "Fields"
},
{
- "fieldname": "columns",
- "fieldtype": "HTML",
- "label": "Columns"
+ "fieldname": "fields",
+ "fieldtype": "Code",
+ "hidden": 1,
+ "label": "Fields",
+ "read_only": 1
}
],
- "modified": "2019-11-18 10:05:11.859157",
+ "links": [],
+ "modified": "2020-04-13 23:31:15.411147",
"modified_by": "Administrator",
"module": "Desk",
"name": "List View Settings",
@@ -63,8 +67,21 @@
"role": "System Manager",
"share": 1,
"write": 1
+ },
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "All",
+ "share": 1,
+ "write": 1
}
],
+ "read_only": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.py b/frappe/desk/doctype/list_view_settings/list_view_settings.py
index adf92e8ec6..acd8f8a7b6 100644
--- a/frappe/desk/doctype/list_view_settings/list_view_settings.py
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.py
@@ -1,40 +1,78 @@
# -*- coding: utf-8 -*-
-# Copyright (c) 2019, Frappe Technologies and contributors
+# Copyright (c) 2020, Frappe Technologies and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
+from six import string_types
+from frappe.exceptions import DoesNotExistError
+import json
class ListViewSettings(Document):
def on_update(self):
frappe.clear_document_cache(self.doctype, self.name)
- frappe.get_cached_doc(self.doctype, self.name)
@frappe.whitelist()
-def get_listview_columns(doctype):
- meta = frappe.get_meta(doctype)
- listview_columns = []
+def save_listview_settings(doctype, listview_settings, removed_listview_fields):
- subject_field = {
- "label": "Name",
- "fieldname": "name"
- }
+ if isinstance(listview_settings, string_types):
+ listview_settings = json.loads(listview_settings)
+
+ if isinstance(removed_listview_fields, string_types):
+ removed_listview_fields = json.loads(removed_listview_fields)
+
+ try:
+ doc = frappe.get_doc("List View Settings", doctype)
+ doc.update(listview_settings)
+ doc.save()
+ except DoesNotExistError:
+ doc = frappe.new_doc("List View Settings")
+ doc.name = doctype
+ doc.update(listview_settings)
+ doc.insert()
+
+ set_listview_fields(doctype, listview_settings.get("fields"), removed_listview_fields)
+
+def set_listview_fields(doctype, listview_fields, removed_listview_fields):
+ meta = frappe.get_meta(doctype)
+
+ if isinstance(listview_fields, string_types):
+ listview_fields = [f.get("fieldname") for f in json.loads(listview_fields)]
+
+ for field in listview_fields:
+ set_in_list_view_property(doctype, meta.get_field(field), "1")
+
+ for field in removed_listview_fields:
+ set_in_list_view_property(doctype, meta.get_field(field), "0")
+
+def set_in_list_view_property(doctype, field, value):
+ property_setter = frappe.db.get_value("Property Setter", {"doc_type": doctype, "field_name": field.fieldname, "property": "in_list_view"})
+ if property_setter:
+ doc = frappe.get_doc("Property Setter", property_setter)
+ doc.value = value
+ doc.save()
+ else:
+ frappe.make_property_setter({
+ "doctype": doctype,
+ "doctype_or_field": "DocField",
+ "fieldname": field.fieldname,
+ "property": "in_list_view",
+ "value": value,
+ "property_type": "Check"
+ }, ignore_validate=True)
+
+@frappe.whitelist()
+def get_default_listview_fields(doctype):
+ meta = frappe.get_meta(doctype)
+ path = frappe.get_module_path(frappe.scrub(meta.module), "doctype", frappe.scrub(meta.name), frappe.scrub(meta.name) + ".json")
+ json = frappe.get_file_json(path)
+
+ fields = [f.get("fieldname") for f in json.get("fields") if f.get("in_list_view")]
if meta.title_field:
- title_field = meta.get_field(meta.title_field)
+ if not meta.title_field.strip() in fields:
+ fields.append(meta.title_field.strip())
- subject_field["label"] = title_field.label
- subject_field["fieldname"] = title_field.fieldname
-
- listview_columns.append(subject_field)
-
- for field in meta.fields:
- if field.in_list_view and field.label and field.fieldname:
- listview_columns.append({
- "label": field.label,
- "fieldname": field.fieldname
- })
-
- return listview_columns
+ return fields
diff --git a/frappe/public/js/frappe/list/list_settings.js b/frappe/public/js/frappe/list/list_settings.js
new file mode 100644
index 0000000000..252309dd88
--- /dev/null
+++ b/frappe/public/js/frappe/list/list_settings.js
@@ -0,0 +1,320 @@
+export default class ListSettings {
+ constructor({ doctype, meta, settings }) {
+ if (!doctype) {
+ frappe.throw(__('Doctype required'));
+ }
+
+ this.doctype = doctype;
+ this.meta = meta;
+ this.settings = settings;
+ this.dialog = null;
+ this.fields = [];
+ this.subject_field = null;
+
+ frappe.model.with_doctype("List View Settings", () => {
+ this.make();
+ this.get_listview_fields(meta);
+ this.setup_fields();
+ this.setup_remove_fields();
+ this.add_new_fields();
+ this.show_dialog();
+ });
+ }
+
+ make() {
+ let me = this;
+
+ let list_view_settings = frappe.get_meta("List View Settings");
+
+ me.dialog = new frappe.ui.Dialog({
+ title: __("{0} List View Settings", [__(me.doctype)]),
+ fields: list_view_settings.fields
+ });
+ me.dialog.set_values(me.settings);
+ me.dialog.set_primary_action(__('Save'), () => {
+ let values = me.dialog.get_values();
+ frappe.call("frappe.desk.doctype.list_view_settings.list_view_settings.save_listview_settings", {
+ doctype: me.doctype,
+ listview_settings: values,
+ removed_listview_fields: me.removed_fields || []
+ });
+ me.dialog.hide();
+ frappe.ui.toolbar.clear_cache();
+ });
+
+ me.dialog.fields_dict["total_fields"].df.onchange = () => me.refresh();
+ }
+
+ refresh() {
+ let me = this;
+
+ me.setup_fields();
+ me.update_fields();
+ me.add_new_fields();
+ me.setup_remove_fields();
+ }
+
+ show_dialog() {
+ let me = this;
+ me.dialog.show();
+ }
+
+ setup_fields() {
+ let me = this;
+
+ let fields_html = me.dialog.get_field("fields_html");
+ let $wrapper = fields_html.$wrapper[0];
+ let fields = ``;
+ let total_fields = me.dialog.get_values().total_fields ? me.dialog.get_values().total_fields : me.settings.total_fields;
+
+ for (let idx in me.fields) {
+ if (idx == parseInt(total_fields)) {
+ break;
+ }
+ let is_sortable = (idx == 0) ? `` : `sortable`;
+ let can_remove = (idx == 0) ? `hide` : ``;
+
+ fields += `
+ `;
+ }
+
+ fields_html.html(`
+
+ `);
+
+ new Sortable($wrapper.getElementsByClassName("control-input-wrapper")[0], {
+ handle: '.sortable-handle',
+ draggable: '.sortable',
+ onUpdate: () => {
+ me.refresh();
+ }
+ });
+ }
+
+ add_new_fields() {
+ let me = this
+
+ let fields_html = me.dialog.get_field("fields_html");
+ let add_new_fields = fields_html.$wrapper[0].getElementsByClassName("add-new-fields")[0];
+ add_new_fields.onclick = () => me.column_selector();
+ }
+
+ setup_remove_fields() {
+ let me = this;
+
+ let fields_html = me.dialog.get_field("fields_html");
+ let remove_fields = fields_html.$wrapper[0].getElementsByClassName("remove-field");
+
+ for (let idx = 0; idx < remove_fields.length; idx++) {
+ remove_fields.item(idx).onclick = () => me.remove_fields(remove_fields.item(idx).getAttribute("data-fieldname"));
+ }
+ }
+
+ remove_fields(fieldname) {
+ let me = this;
+
+ for (let idx in me.fields) {
+ let field = me.fields[idx];
+
+ if (field.fieldname == fieldname) {
+ console.log(idx);
+ me.fields.splice(idx, 1);
+ break;
+ }
+ }
+
+ me.refresh();
+ }
+
+ update_fields() {
+ let me = this;
+
+ let fields_html = me.dialog.get_field("fields_html");
+ let $wrapper = fields_html.$wrapper[0];
+
+ let fields_order = $wrapper.getElementsByClassName("fields_order");
+ me.fields = [];
+
+ for (let idx = 0; idx < fields_order.length; idx++) {
+ me.fields.push({
+ fieldname: fields_order.item(idx).getAttribute("data-fieldname"),
+ label: fields_order.item(idx).getAttribute("data-label")
+ });
+ }
+
+ me.dialog.set_value("fields", JSON.stringify(me.fields));
+ }
+
+ column_selector() {
+ let me = this;
+
+ let d = new frappe.ui.Dialog({
+ title: __("{0} Fields", [__(me.doctype)]),
+ fields: [
+ {
+ label: __("Reset Fields"),
+ fieldtype: "Button",
+ fieldname: "reset_fields",
+ click: () => me.reset_listview_fields(d)
+ },
+ {
+ label: __("Select Fields"),
+ fieldtype: "MultiCheck",
+ fieldname: "fields",
+ options: me.get_doctype_fields(me.meta, me.fields.map(f => f.fieldname)),
+ columns: 2
+ }
+ ]
+ });
+ d.set_primary_action(__('Save'), () => {
+ let values = d.get_values().fields;
+ me.removed_fields = me.get_removed_listview_fields(values)
+ me.new_fields = values
+
+ me.fields = [];
+ me.set_subject_field(me.meta);
+
+ for (let idx in values) {
+ let value = values[idx];
+
+ if (me.fields.length === parseInt(me.dialog.get_values().total_fields)) {
+ break;
+ } else if (value != me.subject_field.fieldname) {
+ let field = frappe.meta.get_docfield(me.doctype, value);
+ me.fields.push({
+ label: field.label,
+ fieldname: field.fieldname
+ })
+ }
+ }
+
+ me.refresh();
+ me.dialog.set_value("fields", JSON.stringify(me.fields));
+ d.hide();
+ });
+ d.show();
+ }
+
+ reset_listview_fields(dialog) {
+ let me = this;
+
+ frappe.xcall("frappe.desk.doctype.list_view_settings.list_view_settings.get_default_listview_fields", {
+ doctype: me.doctype
+ }).then((fields) => {
+ let field = dialog.get_field("fields");
+ field.df.options = me.get_doctype_fields(me.meta, fields);
+ dialog.refresh();
+ });
+
+ }
+
+ get_listview_fields(meta) {
+ let me = this;
+
+ if (!me.settings.fields) {
+ me.set_list_view_fields(meta);
+ } else {
+ me.fields = JSON.parse(this.settings.fields);
+ }
+
+ me.fields.uniqBy(f => f.fieldname);
+ }
+
+ set_list_view_fields(meta) {
+ let me = this;
+
+ me.set_subject_field(meta);
+
+ meta.fields.forEach(field => {
+ if (field.in_list_view && !in_list(frappe.model.no_value_type, field.fieldtype) &&
+ me.subject_field.fieldname != field.fieldname) {
+
+ me.fields.push({
+ label: field.label,
+ fieldname: field.fieldname
+ })
+ }
+ })
+ }
+
+ set_subject_field(meta) {
+ let me = this;
+
+ me.subject_field = {
+ label: "Name",
+ fieldname: "name"
+ }
+
+ if (meta.title_field) {
+ let field = frappe.meta.get_docfield(me.doctype, meta.title_field.trim())
+
+ me.subject_field = {
+ label: field.label,
+ fieldname: field.fieldname
+ }
+ }
+
+ me.fields.push(me.subject_field);
+ }
+
+ get_doctype_fields(meta, fields) {
+ let me = this;
+ let multiselect_fields = []
+
+ meta.fields.forEach(field => {
+ if (!in_list(frappe.model.no_value_type, field.fieldtype)) {
+ multiselect_fields.push({
+ label: field.label,
+ value: field.fieldname,
+ checked: in_list(fields, field.fieldname)
+ })
+ }
+ })
+
+ return multiselect_fields
+ }
+
+ get_removed_listview_fields(new_fields) {
+ let me = this;
+
+ let removed_fields = []
+ let existing_fields = me.fields.map(f => f.fieldname);
+
+ existing_fields.forEach(column => {
+ if (!in_list(new_fields, column)) {
+ removed_fields.push(column);
+ }
+ });
+
+ return removed_fields;
+ }
+}
\ No newline at end of file
diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js
index faa6797d43..4913e29d11 100644
--- a/frappe/public/js/frappe/list/list_view.js
+++ b/frappe/public/js/frappe/list/list_view.js
@@ -1,4 +1,5 @@
import BulkOperations from "./bulk_operations";
+import ListSettings from "./list_settings";
frappe.provide('frappe.views');
@@ -287,41 +288,47 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
}))
);
- if (this.list_view_settings.column_order && this.list_view_settings.column_order.length > 0 &&
- this.list_view_settings.column_order.length === this.list_view_settings.column_count) {
- let custom_column_order = [];
+ if (this.list_view_settings.fields) {
+ let fields_order = [];
+ let fields = JSON.parse(this.list_view_settings.fields);
+ let is_status_field_set = false;
//title_field is fixed
- custom_column_order.push(this.columns[0]);
+ fields_order.push(this.columns[0]);
this.columns.splice(0, 1);
- for (let i in this.list_view_settings.column_order) {
- let fieldname = this.list_view_settings.column_order[i].fieldname;
- for (let j in this.columns) {
- let df = this.columns[j].df.fieldname;
- if (fieldname === df) {
- custom_column_order.push(this.columns[j]);
+ for (let fld in fields) {
+ for (let col in this.columns) {
+ let field = fields[fld];
+ let column = this.columns[col];
+
+ if (column.type == "Status" && !is_status_field_set) {
+ fields_order.push(column);
+ is_status_field_set = true;
+ break;
+ } else if (column.type == "Field" && field.fieldname === column.df.fieldname) {
+ fields_order.push(column);
break;
}
}
}
- this.columns = custom_column_order;
+ this.columns = fields_order;
}
- // limit max to 8 columns if no column_count is set in List View Settings
+ // limit max to 8 columns if no total_fields is set in List View Settings
// Screen with low density no of columns 4
// Screen with medium density no of columns 6
// Screen with high density no of columns 8
- let column_count = 6;
+ let total_fields = 6;
if (window.innerWidth <= 1366) {
- column_count = 4;
+ total_fields = 4;
} else if (window.innerWidth >= 1920) {
- column_count = 8;
+ total_fields = 8;
}
- this.columns = this.columns.slice(0, this.list_view_settings.column_count || column_count);
+ this.columns = this.columns.slice(0, this.list_view_settings.total_fields || total_fields);
}
get_documentation_link() {
@@ -1300,26 +1307,11 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
}
show_list_settings() {
- frappe.model.with_doctype("List View Settings", () => {
- let list_view_settings = frappe.get_meta("List View Settings");
-
- let d = new frappe.ui.Dialog({
- title: __("Settings"),
- fields: [
- list_view_settings.fields[0],
- list_view_settings.fields[1],
- list_view_settings.fields[2],
- list_view_settings.fields[3],
- ]
- });
- d.set_values(this.list_view_settings);
- d.show();
- d.set_primary_action(__('Save'), () => {
- let values = d.get_values();
- frappe.call("frappe.desk.listview.set_list_settings", {doctype: this.doctype, values: values});
- Object.assign(this.list_view_settings, values);
- d.hide();
- frappe.ui.toolbar.clear_cache();
+ frappe.model.with_doctype(this.doctype, () => {
+ new ListSettings({
+ doctype: this.doctype,
+ settings: this.list_view_settings,
+ meta: frappe.get_meta(this.doctype)
});
});
}
From ebf87e430448aad9205105a231bbc4a2bcbdcc76 Mon Sep 17 00:00:00 2001
From: Himanshu
Date: Tue, 14 Apr 2020 11:44:20 +0530
Subject: [PATCH 014/572] Delete list_view_columns.html
---
.../doctype/list_view_settings/list_view_columns.html | 9 ---------
1 file changed, 9 deletions(-)
delete mode 100644 frappe/desk/doctype/list_view_settings/list_view_columns.html
diff --git a/frappe/desk/doctype/list_view_settings/list_view_columns.html b/frappe/desk/doctype/list_view_settings/list_view_columns.html
deleted file mode 100644
index 50474c5169..0000000000
--- a/frappe/desk/doctype/list_view_settings/list_view_columns.html
+++ /dev/null
@@ -1,9 +0,0 @@
-
From fca6181d6771235d883bd1c4cc0ce57c3c32065c Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Wed, 15 Apr 2020 21:32:22 +0530
Subject: [PATCH 015/572] feat: multiple assignments
---
frappe/desk/form/assign_to.py | 77 +++----
.../js/frappe/form/sidebar/assign_to.js | 202 +++++++++++-------
2 files changed, 164 insertions(+), 115 deletions(-)
diff --git a/frappe/desk/form/assign_to.py b/frappe/desk/form/assign_to.py
index 76c7caa63d..a0f2f99910 100644
--- a/frappe/desk/form/assign_to.py
+++ b/frappe/desk/form/assign_to.py
@@ -11,6 +11,7 @@ from frappe.desk.doctype.notification_log.notification_log import enqueue_create
get_title, get_title_html
import frappe.utils
import frappe.share
+import json
class DuplicateToDoError(frappe.ValidationError): pass
@@ -19,7 +20,7 @@ def get(args=None):
if not args:
args = frappe.local.form_dict
- return frappe.get_all('ToDo', fields = ['owner', 'description'], filters = dict(
+ return frappe.get_all('ToDo', fields = ['owner', 'name'], filters = dict(
reference_type = args.get('doctype'),
reference_name = args.get('name'),
status = ('!=', 'Cancelled')
@@ -40,49 +41,53 @@ def add(args=None):
if not args:
args = frappe.local.form_dict
- if frappe.db.sql("""SELECT `owner`
- FROM `tabToDo`
- WHERE `reference_type`=%(doctype)s
- AND `reference_name`=%(name)s
- AND `status`='Open'
- AND `owner`=%(assign_to)s""", args):
- frappe.throw(_("Already in user's To Do list"), DuplicateToDoError)
- else:
- from frappe.utils import nowdate
-
- if not args.get('description'):
- args['description'] = _('Assignment for {0} {1}').format(args['doctype'], args['name'])
-
- d = frappe.get_doc({
- "doctype":"ToDo",
- "owner": args['assign_to'],
+ for assign_to in json.loads(args.get("assign_to")):
+ filters = {
"reference_type": args['doctype'],
"reference_name": args['name'],
- "description": args.get('description'),
- "priority": args.get("priority", "Medium"),
"status": "Open",
- "date": args.get('date', nowdate()),
- "assigned_by": args.get('assigned_by', frappe.session.user),
- 'assignment_rule': args.get('assignment_rule')
- }).insert(ignore_permissions=True)
+ "owner": assign_to
+ }
- # set assigned_to if field exists
- if frappe.get_meta(args['doctype']).get_field("assigned_to"):
- frappe.db.set_value(args['doctype'], args['name'], "assigned_to", args['assign_to'])
+ if frappe.get_all("ToDo", filters=filters):
+ if not args.get("bulk_assign"):
+ frappe.throw(_("Already in user's ToDo list"), DuplicateToDoError)
+ else:
+ from frappe.utils import nowdate
- doc = frappe.get_doc(args['doctype'], args['name'])
+ if not args.get('description'):
+ args['description'] = _('Assignment for {0} {1}').format(args['doctype'], args['name'])
- # if assignee does not have permissions, share
- if not frappe.has_permission(doc=doc, user=args['assign_to']):
- frappe.share.add(doc.doctype, doc.name, args['assign_to'])
- frappe.msgprint(_('Shared with user {0} with read access').format(args['assign_to']), alert=True)
+ d = frappe.get_doc({
+ "doctype":"ToDo",
+ "owner": assign_to,
+ "reference_type": args['doctype'],
+ "reference_name": args['name'],
+ "description": args.get('description'),
+ "priority": args.get("priority", "Medium"),
+ "status": "Open",
+ "date": args.get('date', nowdate()),
+ "assigned_by": args.get('assigned_by', frappe.session.user),
+ 'assignment_rule': args.get('assignment_rule')
+ }).insert(ignore_permissions=True)
- # make this document followed by assigned user
- follow_document(args['doctype'], args['name'], args['assign_to'])
+ # set assigned_to if field exists
+ if frappe.get_meta(args['doctype']).get_field("assigned_to"):
+ frappe.db.set_value(args['doctype'], args['name'], "assigned_to", assign_to)
- # notify
- notify_assignment(d.assigned_by, d.owner, d.reference_type, d.reference_name, action='ASSIGN',\
- description=args.get("description"))
+ doc = frappe.get_doc(args['doctype'], args['name'])
+
+ # if assignee does not have permissions, share
+ if not frappe.has_permission(doc=doc, user=assign_to):
+ frappe.share.add(doc.doctype, doc.name, assign_to)
+ frappe.msgprint(_('Shared with user {0} with read access').format(assign_to, alert=True))
+
+ # make this document followed by assigned user
+ follow_document(args['doctype'], args['name'], assign_to)
+
+ # notify
+ notify_assignment(d.assigned_by, d.owner, d.reference_type, d.reference_name, action='ASSIGN',\
+ description=args.get("description"))
return get(args)
diff --git a/frappe/public/js/frappe/form/sidebar/assign_to.js b/frappe/public/js/frappe/form/sidebar/assign_to.js
index 61d1789518..b78540f6a7 100644
--- a/frappe/public/js/frappe/form/sidebar/assign_to.js
+++ b/frappe/public/js/frappe/form/sidebar/assign_to.js
@@ -87,23 +87,17 @@ frappe.ui.form.AssignTo = Class.extend({
if(!me.assign_to) {
me.assign_to = new frappe.ui.form.AssignToDialog({
- obj: me,
- method: 'frappe.desk.form.assign_to.add',
+ method: "frappe.desk.form.assign_to.add",
doctype: me.frm.doctype,
docname: me.frm.docname,
- callback: function(r) {
+ frm: me.frm,
+ callback: function (r) {
me.render(r.message);
}
});
}
me.assign_to.dialog.clear();
-
- if(me.frm.meta.title_field) {
- me.assign_to.dialog.set_value("description", me.frm.doc[me.frm.meta.title_field])
- }
-
me.assign_to.dialog.show();
- me.assign_to = null;
},
remove: function(owner) {
var me = this;
@@ -130,81 +124,131 @@ frappe.ui.form.AssignTo = Class.extend({
frappe.ui.form.AssignToDialog = Class.extend({
init: function(opts){
- var me = this
- var dialog = new frappe.ui.Dialog({
- title: __('Add to To Do'),
- fields: [
- { fieldtype: 'Link', fieldname: 'assign_to', options: 'User', label: __("Assign To"), reqd: true, filters: { 'user_type': 'System User' }},
- { fieldtype: 'Check', fieldname: 'myself', label: __("Assign to me"), "default": 0 },
- { fieldtype: 'Small Text', fieldname: 'description', label: __("Comment") },
- { fieldtype: 'Section Break' },
- { fieldtype: 'Column Break' },
- { fieldtype: 'Date', fieldname: 'date', label: __("Complete By") },
- { fieldtype: 'Column Break' },
- { fieldtype: 'Select', fieldname: 'priority', label: __("Priority"),
- options: [
- { value: 'Low', label: __('Low') },
- { value: 'Medium', label: __('Medium') },
- { value: 'High', label: __('High') }
- ],
- // Pick up priority from the source document, if it exists and is available in ToDo
- 'default': ["Low", "Medium", "High"].includes(opts.obj.frm && opts.obj.frm.doc.priority
- ? opts.obj.frm.doc.priority : 'Medium')
- },
- ],
- primary_action: function() { frappe.ui.add_assignment(opts, this) },
- primary_action_label: __("Add")
- })
- $.extend(me, dialog);
+ $.extend(this, opts)
- me.dialog = dialog;
-
- me.dialog.fields_dict.assign_to.get_query = "frappe.core.doctype.user.user.user_query";
-
- var myself = me.dialog.get_input("myself").on("click", function() {
- me.toggle_myself(this);
- });
- me.toggle_myself(myself);
- },
- toggle_myself: function(myself) {
- var me = this;
- if($(myself).prop("checked")) {
- me.dialog.set_value("assign_to", frappe.session.user);
- me.dialog.get_field("notify").$wrapper.toggle(false);
- me.dialog.get_field("assign_to").$wrapper.toggle(false);
- } else {
- me.dialog.set_value("assign_to", "");
- me.dialog.get_field("assign_to").$wrapper.toggle(true);
- }
+ this.make();
+ this.set_description_from_doc();
},
+ make: function() {
+ let me = this
-});
+ me.dialog = new frappe.ui.Dialog({
+ title: __('Add to ToDo'),
+ fields: me.get_fields(),
+ primary_action_label: __("Add"),
+ primary_action: function() {
+ let args = me.dialog.get_values();
-frappe.ui.add_assignment = function(opts, dialog) {
- var assign_to = dialog.fields_dict.assign_to.get_value();
- var args = dialog.get_values();
- if(args && assign_to) {
- dialog.set_message('Assigning...');
- return frappe.call({
- method: opts.method,
- args: $.extend(args, {
- doctype: opts.doctype,
- name: opts.docname,
- assign_to: assign_to,
- bulk_assign: opts.bulk_assign || false,
- re_assign: opts.re_assign || false
- }),
- btn: dialog.get_primary_btn(),
- callback: function(r) {
- if(!r.exc) {
- if(opts.callback){
- opts.callback(r);
- }
- dialog && dialog.hide();
- } else {
- dialog.clear_message();
+ if (args && args.assign_to) {
+ me.dialog.set_message("Assigning...");
+
+ frappe.call({
+ method: me.method,
+ args: $.extend(args, {
+ doctype: me.doctype,
+ name: me.docname,
+ assign_to: args.assign_to,
+ bulk_assign: me.bulk_assign || false,
+ re_assign: me.re_assign || false
+ }),
+ btn: me.dialog.get_primary_btn(),
+ callback: function(r) {
+ if (!r.exc) {
+ if (me.callback) {
+ me.callback(r);
+ }
+ me.dialog && me.dialog.hide();
+ } else {
+ me.dialog.clear_message();
+ }
+ },
+ });
}
},
});
+ },
+ assign_to_me: function() {
+ let me = this;
+ let assign_to = [];
+
+ if(me.dialog.get_value("assign_to_me")) {
+ assign_to.push(frappe.session.user);
+ }
+
+ me.dialog.set_value("assign_to", assign_to);
+ },
+ set_description_from_doc: function() {
+ let me = this;
+
+ if (me.frm && me.frm.meta.title_field) {
+ me.dialog.set_value("description", me.frm.doc[me.frm.meta.title_field]);
+ }
+ },
+ set_assign_to_field_description: function() {
+ let me = this;
+
+ let assignees = me.dialog.get_value("assign_to");
+ let field = me.dialog.get_field("assign_to");
+ let description = "";
+
+ if (assignees.length > 0) {
+ description = "Assign To: " + assignees.join(", ");
+ }
+
+ field.set_description(description);
+ },
+ get_fields: function() {
+ let me = this;
+
+ return [
+ {
+ fieldtype: 'MultiSelectList',
+ fieldname: 'assign_to',
+ label: __("Assign To"),
+ reqd: true,
+ get_data: function(txt) {
+ return frappe.db.get_link_options("User", txt, {user_type: "System User", enabled: 1});
+ },
+ onchange: () => me.set_assign_to_field_description()
+ },
+ {
+ label: __("Assign to me"),
+ fieldtype: 'Check',
+ fieldname: 'assign_to_me',
+ default: 0,
+ onchange: () => me.assign_to_me()
+ },
+ {
+ label: __("Comment"),
+ fieldtype: 'Small Text',
+ fieldname: 'description'
+ },
+ {
+ fieldtype: 'Section Break'
+ },
+ {
+ fieldtype: 'Column Break'
+ },
+ {
+ label: __("Complete By"),
+ fieldtype: 'Date',
+ fieldname: 'date'
+ },
+ {
+ fieldtype: 'Column Break'
+ },
+ {
+ label: __("Priority"),
+ fieldtype: 'Select',
+ fieldname: 'priority',
+ options: [
+ { value: 'Low', label: __('Low') },
+ { value: 'Medium', label: __('Medium') },
+ { value: 'High', label: __('High') }
+ ],
+ // Pick up priority from the source document, if it exists and is available in ToDo
+ default: ["Low", "Medium", "High"].includes(me.frm && me.frm.doc.priority ? me.frm.doc.priority : 'Medium')
+ }
+ ]
}
-}
+});
From 6b9de21d539d4a64fe82473be994af9bc814eb61 Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Wed, 15 Apr 2020 22:20:23 +0530
Subject: [PATCH 016/572] fix: minor bug fixes for notifying assignees
---
frappe/desk/form/assign_to.py | 4 ++--
frappe/public/js/frappe/form/sidebar/assign_to.js | 8 ++++----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/frappe/desk/form/assign_to.py b/frappe/desk/form/assign_to.py
index a0f2f99910..8a8219eb22 100644
--- a/frappe/desk/form/assign_to.py
+++ b/frappe/desk/form/assign_to.py
@@ -85,8 +85,8 @@ def add(args=None):
# make this document followed by assigned user
follow_document(args['doctype'], args['name'], assign_to)
- # notify
- notify_assignment(d.assigned_by, d.owner, d.reference_type, d.reference_name, action='ASSIGN',\
+ # notify
+ notify_assignment(d.assigned_by, d.owner, d.reference_type, d.reference_name, action='ASSIGN',\
description=args.get("description"))
return get(args)
diff --git a/frappe/public/js/frappe/form/sidebar/assign_to.js b/frappe/public/js/frappe/form/sidebar/assign_to.js
index b78540f6a7..03fba17ec7 100644
--- a/frappe/public/js/frappe/form/sidebar/assign_to.js
+++ b/frappe/public/js/frappe/form/sidebar/assign_to.js
@@ -124,7 +124,7 @@ frappe.ui.form.AssignTo = Class.extend({
frappe.ui.form.AssignToDialog = Class.extend({
init: function(opts){
- $.extend(this, opts)
+ $.extend(this, opts);
this.make();
this.set_description_from_doc();
@@ -148,7 +148,7 @@ frappe.ui.form.AssignToDialog = Class.extend({
doctype: me.doctype,
name: me.docname,
assign_to: args.assign_to,
- bulk_assign: me.bulk_assign || false,
+ bulk_assign: me.bulk_assign || false,
re_assign: me.re_assign || false
}),
btn: me.dialog.get_primary_btn(),
@@ -171,7 +171,7 @@ frappe.ui.form.AssignToDialog = Class.extend({
let me = this;
let assign_to = [];
- if(me.dialog.get_value("assign_to_me")) {
+ if (me.dialog.get_value("assign_to_me")) {
assign_to.push(frappe.session.user);
}
@@ -249,6 +249,6 @@ frappe.ui.form.AssignToDialog = Class.extend({
// Pick up priority from the source document, if it exists and is available in ToDo
default: ["Low", "Medium", "High"].includes(me.frm && me.frm.doc.priority ? me.frm.doc.priority : 'Medium')
}
- ]
+ ];
}
});
From 7886365c090ad1b645b63c0987af83e13366b1e9 Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Wed, 15 Apr 2020 22:48:58 +0530
Subject: [PATCH 017/572] chore: code cleanup
---
frappe/desk/form/assign_to.py | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/frappe/desk/form/assign_to.py b/frappe/desk/form/assign_to.py
index 8a8219eb22..be83dc957e 100644
--- a/frappe/desk/form/assign_to.py
+++ b/frappe/desk/form/assign_to.py
@@ -20,11 +20,11 @@ def get(args=None):
if not args:
args = frappe.local.form_dict
- return frappe.get_all('ToDo', fields = ['owner', 'name'], filters = dict(
+ return frappe.get_all('ToDo', fields=['owner', 'name'], filters=dict(
reference_type = args.get('doctype'),
reference_name = args.get('name'),
status = ('!=', 'Cancelled')
- ), limit = 5)
+ ), limit=5)
@frappe.whitelist()
def add(args=None):
@@ -59,7 +59,7 @@ def add(args=None):
args['description'] = _('Assignment for {0} {1}').format(args['doctype'], args['name'])
d = frappe.get_doc({
- "doctype":"ToDo",
+ "doctype": "ToDo",
"owner": assign_to,
"reference_type": args['doctype'],
"reference_name": args['name'],
@@ -86,15 +86,13 @@ def add(args=None):
follow_document(args['doctype'], args['name'], assign_to)
# notify
- notify_assignment(d.assigned_by, d.owner, d.reference_type, d.reference_name, action='ASSIGN',\
+ notify_assignment(d.assigned_by, d.owner, d.reference_type, d.reference_name, action='ASSIGN',
description=args.get("description"))
return get(args)
@frappe.whitelist()
def add_multiple(args=None):
- import json
-
if not args:
args = frappe.local.form_dict
From 7483bf79683430e54a9ce73390fce75dc5b04b7d Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Thu, 16 Apr 2020 20:20:13 +0530
Subject: [PATCH 018/572] feat: do not refresh when changing columns
---
.../list_view_settings/list_view_settings.py | 11 ++--
frappe/public/js/frappe/list/list_settings.js | 51 +++++++++++++------
frappe/public/js/frappe/list/list_view.js | 34 ++++++++++++-
3 files changed, 77 insertions(+), 19 deletions(-)
diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.py b/frappe/desk/doctype/list_view_settings/list_view_settings.py
index acd8f8a7b6..6cc7d11950 100644
--- a/frappe/desk/doctype/list_view_settings/list_view_settings.py
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.py
@@ -35,18 +35,23 @@ def save_listview_settings(doctype, listview_settings, removed_listview_fields):
set_listview_fields(doctype, listview_settings.get("fields"), removed_listview_fields)
+ return {
+ "meta": frappe.get_meta(doctype, False),
+ "listview_settings": doc
+ }
+
def set_listview_fields(doctype, listview_fields, removed_listview_fields):
meta = frappe.get_meta(doctype)
if isinstance(listview_fields, string_types):
listview_fields = [f.get("fieldname") for f in json.loads(listview_fields)]
- for field in listview_fields:
- set_in_list_view_property(doctype, meta.get_field(field), "1")
-
for field in removed_listview_fields:
set_in_list_view_property(doctype, meta.get_field(field), "0")
+ for field in listview_fields:
+ set_in_list_view_property(doctype, meta.get_field(field), "1")
+
def set_in_list_view_property(doctype, field, value):
property_setter = frappe.db.get_value("Property Setter", {"doc_type": doctype, "field_name": field.fieldname, "property": "in_list_view"})
if property_setter:
diff --git a/frappe/public/js/frappe/list/list_settings.js b/frappe/public/js/frappe/list/list_settings.js
index 252309dd88..e078a106b1 100644
--- a/frappe/public/js/frappe/list/list_settings.js
+++ b/frappe/public/js/frappe/list/list_settings.js
@@ -1,9 +1,10 @@
export default class ListSettings {
- constructor({ doctype, meta, settings }) {
+ constructor({ listview, doctype, meta, settings }) {
if (!doctype) {
frappe.throw(__('Doctype required'));
}
+ this.listview = listview;
this.doctype = doctype;
this.meta = meta;
this.settings = settings;
@@ -33,13 +34,24 @@ export default class ListSettings {
me.dialog.set_values(me.settings);
me.dialog.set_primary_action(__('Save'), () => {
let values = me.dialog.get_values();
- frappe.call("frappe.desk.doctype.list_view_settings.list_view_settings.save_listview_settings", {
- doctype: me.doctype,
- listview_settings: values,
- removed_listview_fields: me.removed_fields || []
+
+ frappe.show_alert({
+ message: __("Saving"),
+ indicator: "green"
+ });
+
+ frappe.call({
+ method: "frappe.desk.doctype.list_view_settings.list_view_settings.save_listview_settings",
+ args: {
+ doctype: me.doctype,
+ listview_settings: values,
+ removed_listview_fields: me.removed_fields || []
+ },
+ callback: function (r) {
+ me.listview.refresh_columns(r.message.meta, r.message.listview_settings);
+ me.dialog.hide();
+ }
});
- me.dialog.hide();
- frappe.ui.toolbar.clear_cache();
});
me.dialog.fields_dict["total_fields"].df.onchange = () => me.refresh();
@@ -49,7 +61,6 @@ export default class ListSettings {
let me = this;
me.setup_fields();
- me.update_fields();
me.add_new_fields();
me.setup_remove_fields();
}
@@ -115,6 +126,7 @@ export default class ListSettings {
handle: '.sortable-handle',
draggable: '.sortable',
onUpdate: () => {
+ me.update_fields();
me.refresh();
}
});
@@ -141,18 +153,19 @@ export default class ListSettings {
remove_fields(fieldname) {
let me = this;
+ let existing_fields = me.fields.map(f => f.fieldname);
for (let idx in me.fields) {
let field = me.fields[idx];
if (field.fieldname == fieldname) {
- console.log(idx);
me.fields.splice(idx, 1);
break;
}
}
-
+ me.set_removed_fields(me.get_removed_listview_fields(me.fields.map(f => f.fieldname), existing_fields));
me.refresh();
+ me.update_fields();
}
update_fields() {
@@ -197,8 +210,8 @@ export default class ListSettings {
});
d.set_primary_action(__('Save'), () => {
let values = d.get_values().fields;
- me.removed_fields = me.get_removed_listview_fields(values)
- me.new_fields = values
+
+ me.set_removed_fields(me.get_removed_listview_fields(values, me.fields.map(f => f.fieldname)));
me.fields = [];
me.set_subject_field(me.meta);
@@ -303,11 +316,9 @@ export default class ListSettings {
return multiselect_fields
}
- get_removed_listview_fields(new_fields) {
+ get_removed_listview_fields(new_fields, existing_fields) {
let me = this;
-
let removed_fields = []
- let existing_fields = me.fields.map(f => f.fieldname);
existing_fields.forEach(column => {
if (!in_list(new_fields, column)) {
@@ -317,4 +328,14 @@ export default class ListSettings {
return removed_fields;
}
+
+ set_removed_fields(fields) {
+ let me = this;
+
+ if (me.removed_fields) {
+ me.removed_fields.concat(fields);
+ } else {
+ me.removed_fields = fields;
+ }
+ }
}
\ No newline at end of file
diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js
index 3d629d3fda..b784e7ba92 100644
--- a/frappe/public/js/frappe/list/list_view.js
+++ b/frappe/public/js/frappe/list/list_view.js
@@ -232,6 +232,32 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
}
+ refresh_columns(meta, list_view_settings) {
+ this.meta = meta;
+ this.list_view_settings = list_view_settings;
+
+ this.setup_columns();
+ this.refresh(true);
+ }
+
+ refresh(refresh_header=false) {
+ this.freeze(true);
+ // fetch data from server
+ return frappe.call(this.get_call_args()).then(r => {
+ // render
+ this.prepare_data(r);
+ this.toggle_result_area();
+ this.before_render();
+ this.render_header(refresh_header);
+ this.render();
+ this.after_render();
+ this.freeze(false);
+ if (this.settings.refresh) {
+ this.settings.refresh(this);
+ }
+ });
+ }
+
setup_freeze_area() {
this.$freeze =
$(`${__('Loading')}...
`)
@@ -329,6 +355,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
}
this.columns = this.columns.slice(0, this.list_view_settings.total_fields || total_fields);
+ console.log(this.columns);
}
get_documentation_link() {
@@ -415,7 +442,11 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
}
}
- render_header() {
+ render_header(refresh_header=false) {
+ if (refresh_header) {
+ this.$result.find('.list-row-head').remove();
+ }
+
if (this.$result.find('.list-row-head').length === 0) {
// append header once
this.$result.prepend(this.get_header_html());
@@ -1315,6 +1346,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
show_list_settings() {
frappe.model.with_doctype(this.doctype, () => {
new ListSettings({
+ listview: this,
doctype: this.doctype,
settings: this.list_view_settings,
meta: frappe.get_meta(this.doctype)
From 7dac9e2a0342809453bdb44d0588606715b5154b Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Thu, 16 Apr 2020 21:55:52 +0530
Subject: [PATCH 019/572] fix: change to multiselect pills
---
.../js/frappe/form/sidebar/assign_to.js | 33 ++++++++-----------
1 file changed, 14 insertions(+), 19 deletions(-)
diff --git a/frappe/public/js/frappe/form/sidebar/assign_to.js b/frappe/public/js/frappe/form/sidebar/assign_to.js
index 03fba17ec7..84fde564c0 100644
--- a/frappe/public/js/frappe/form/sidebar/assign_to.js
+++ b/frappe/public/js/frappe/form/sidebar/assign_to.js
@@ -184,32 +184,18 @@ frappe.ui.form.AssignToDialog = Class.extend({
me.dialog.set_value("description", me.frm.doc[me.frm.meta.title_field]);
}
},
- set_assign_to_field_description: function() {
- let me = this;
-
- let assignees = me.dialog.get_value("assign_to");
- let field = me.dialog.get_field("assign_to");
- let description = "";
-
- if (assignees.length > 0) {
- description = "Assign To: " + assignees.join(", ");
- }
-
- field.set_description(description);
- },
get_fields: function() {
let me = this;
return [
{
- fieldtype: 'MultiSelectList',
+ fieldtype: 'MultiSelectPills',
fieldname: 'assign_to',
label: __("Assign To"),
reqd: true,
get_data: function(txt) {
return frappe.db.get_link_options("User", txt, {user_type: "System User", enabled: 1});
- },
- onchange: () => me.set_assign_to_field_description()
+ }
},
{
label: __("Assign to me"),
@@ -242,9 +228,18 @@ frappe.ui.form.AssignToDialog = Class.extend({
fieldtype: 'Select',
fieldname: 'priority',
options: [
- { value: 'Low', label: __('Low') },
- { value: 'Medium', label: __('Medium') },
- { value: 'High', label: __('High') }
+ {
+ value: 'Low',
+ label: __('Low')
+ },
+ {
+ value: 'Medium',
+ label: __('Medium')
+ },
+ {
+ value: 'High',
+ label: __('High')
+ }
],
// Pick up priority from the source document, if it exists and is available in ToDo
default: ["Low", "Medium", "High"].includes(me.frm && me.frm.doc.priority ? me.frm.doc.priority : 'Medium')
From acc7de7335e28b13c5590b95a42ed972198de83b Mon Sep 17 00:00:00 2001
From: Shivam Mishra
Date: Fri, 17 Apr 2020 18:49:04 +0530
Subject: [PATCH 020/572] feat: added pandas date_range to utils
---
frappe/utils/dateutils.py | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/frappe/utils/dateutils.py b/frappe/utils/dateutils.py
index d5b7a3136b..907cab3d02 100644
--- a/frappe/utils/dateutils.py
+++ b/frappe/utils/dateutils.py
@@ -73,3 +73,9 @@ def datetime_in_user_format(date_time):
date_time = get_datetime(date_time)
from frappe.utils import formatdate
return formatdate(date_time.date()) + " " + date_time.strftime("%H:%M")
+
+def get_date_range(from_date, to_date, frequency="M", normalize=True):
+ from pandas import date_range
+ date_range = date_range(start=from_date, end=to_date, freq=frequency, normalize=normalize).to_pydatetime().tolist()
+
+ return date_range
\ No newline at end of file
From da6024242769e8c0934f8bc283ae8c9d93fbc573 Mon Sep 17 00:00:00 2001
From: Shivam Mishra
Date: Fri, 17 Apr 2020 18:49:12 +0530
Subject: [PATCH 021/572] feat: added website analytics report
---
frappe/website/report/__init__.py | 0
.../report/website_analytics/__init__.py | 0
.../website_analytics/website_analytics.js | 34 ++++
.../website_analytics/website_analytics.json | 27 ++++
.../website_analytics/website_analytics.py | 145 ++++++++++++++++++
5 files changed, 206 insertions(+)
create mode 100644 frappe/website/report/__init__.py
create mode 100644 frappe/website/report/website_analytics/__init__.py
create mode 100644 frappe/website/report/website_analytics/website_analytics.js
create mode 100644 frappe/website/report/website_analytics/website_analytics.json
create mode 100644 frappe/website/report/website_analytics/website_analytics.py
diff --git a/frappe/website/report/__init__.py b/frappe/website/report/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/website/report/website_analytics/__init__.py b/frappe/website/report/website_analytics/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/website/report/website_analytics/website_analytics.js b/frappe/website/report/website_analytics/website_analytics.js
new file mode 100644
index 0000000000..b607a16eb4
--- /dev/null
+++ b/frappe/website/report/website_analytics/website_analytics.js
@@ -0,0 +1,34 @@
+// Copyright (c) 2016, Frappe Technologies and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Website Analytics"] = {
+ "filters": [
+ {
+ fieldname: "from_date",
+ label: __("From Date"),
+ fieldtype: "Date",
+ default: frappe.datetime.add_days(frappe.datetime.now_date(true), -7),
+ reqd: 1
+ },
+ {
+ fieldname:"to_date",
+ label: __("To Date"),
+ fieldtype: "Date",
+ default: frappe.datetime.now_date(true),
+ reqd: 1
+ },
+ {
+ fieldname: "range",
+ label: __("Range"),
+ fieldtype: "Select",
+ options: [
+ { "value": "D", "label": __("Daily") },
+ { "value": "W", "label": __("Weekly") },
+ { "value": "M", "label": __("Monthly") },
+ ],
+ default: "D",
+ reqd: 1
+ }
+ ]
+};
diff --git a/frappe/website/report/website_analytics/website_analytics.json b/frappe/website/report/website_analytics/website_analytics.json
new file mode 100644
index 0000000000..62c5751a5c
--- /dev/null
+++ b/frappe/website/report/website_analytics/website_analytics.json
@@ -0,0 +1,27 @@
+{
+ "add_total_row": 0,
+ "creation": "2020-04-17 13:04:45.770148",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2020-04-17 16:10:30.168312",
+ "modified_by": "Administrator",
+ "module": "Website",
+ "name": "Website Analytics",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Web Page View",
+ "report_name": "Website Analytics",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "System Manager"
+ },
+ {
+ "role": "Website Manager"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/frappe/website/report/website_analytics/website_analytics.py b/frappe/website/report/website_analytics/website_analytics.py
new file mode 100644
index 0000000000..8b2d5b3806
--- /dev/null
+++ b/frappe/website/report/website_analytics/website_analytics.py
@@ -0,0 +1,145 @@
+# Copyright (c) 2013, Frappe Technologies and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.utils.dateutils import get_date_range
+
+def execute(filters=None):
+ return WebsiteAnalytics(filters).run()
+
+class WebsiteAnalytics(object):
+ def __init__(self, filters=None):
+ self.filters = frappe._dict(filters or {})
+ self.filters.to_date = frappe.utils.add_days(self.filters.to_date, 1)
+ self.query_filters = {'creation': ['between', [self.filters.from_date, self.filters.to_date]]}
+
+ def run(self):
+ columns = self.get_columns()
+ data = self.get_data()
+ summary = self.get_report_summary()
+ chart = self.get_chart_data()
+ return columns, data, None, chart, summary
+
+ def get_columns(self):
+ return [
+ {
+ "fieldname": "path",
+ "label": "Page",
+ "fieldtype": "Data",
+ "width": 300
+ },
+ {
+ "fieldname": "count",
+ "label": "Page Views",
+ "fieldtype": "Int",
+ "width": 150
+ }
+ ]
+
+ def get_data(self):
+ data = frappe.get_all("Web Page View", fields=['path', 'count(*) as count'], filters=self.query_filters, group_by="path", order_by='count desc')
+ return data
+
+ def get_chart_data(self):
+ def _get_field_for_chart(filters_range):
+ field = 'creation'
+ date_format = '%Y-%m-%d'
+
+ if filters_range == "W":
+ field = 'ADDDATE(creation, INTERVAL 1-DAYOFWEEK(creation) DAY)'
+
+ elif filters_range == "M":
+ date_format = '%Y-%m-01'
+
+ return field, date_format
+
+ field, date_format = _get_field_for_chart(self.filters.range)
+
+ data = frappe.db.sql("""
+ SELECT
+ DATE_FORMAT({0}, %s) as date,
+ COUNT(*) as count,
+ count(CASE WHEN is_unique = 1 THEN 1 END) as unique_count
+ FROM `tabWeb Page View`
+ WHERE creation BETWEEN %s AND %s
+ GROUP BY DATE_FORMAT({0}, %s)
+ ORDER BY creation
+ """.format(field), (date_format, self.filters.from_date, self.filters.to_date, date_format), as_dict=1, debug=1)
+
+ return self.prepare_chart_data(data)
+
+ def prepare_chart_data(self, data):
+ date_range = get_date_range(self.filters.from_date, self.filters.to_date, self.filters.range)
+ if self.filters.range == "M":
+ date_range = [frappe.utils.add_days(dd, 1) for dd in date_range]
+
+ labels = []
+ total_dataset = []
+ unique_dataset = []
+
+ def get_data_for_date(date):
+ for item in data:
+ item_date = frappe.utils.get_datetime(item.get("date")).date()
+ if item_date == date.date():
+ return item
+ return {'count': 0, 'unique_count': 0}
+
+
+ for date in date_range:
+ labels.append(date.strftime("%b %d %Y"))
+ match = get_data_for_date(date)
+ total_dataset.append(match.get('count', 0))
+ unique_dataset.append(match.get('unique_count', 0))
+
+ chart = {
+ "data": {
+ 'labels': labels,
+ 'datasets': [
+ {
+ 'name': "Total Views",
+ 'type': 'line',
+ 'values': total_dataset
+ },
+ {
+ 'name': "Unique Visits",
+ 'type': 'line',
+ 'values': unique_dataset
+ }
+ ]
+ },
+ "type": "axis-mixed",
+ 'lineOptions': {
+ 'regionFill': 1,
+ },
+ 'axisOptions': {
+ 'xIsSeries': 1
+ }
+ }
+
+ return chart
+
+
+ def get_report_summary(self):
+ summary_data = frappe.get_all("Web Page View", fields=['is_unique', 'count(*) as count'], filters=self.query_filters, group_by="is_unique")
+
+ total_count = 0
+ unique_count = 0
+ for data in summary_data:
+ if data.get('is_unique'):
+ unique_count = data.get('count')
+ total_count += data.get('count')
+ report_summary = [
+ {
+ "value": total_count,
+ "label": "Total Page Views",
+ "datatype": "Int",
+ },
+ {
+ "value": unique_count,
+ "label": "Unique Page Views",
+ "datatype": "Int",
+ },
+
+ ]
+ return report_summary
\ No newline at end of file
From 9b185802aaab09aa84dff1fce76292ea1edcdc54 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Mon, 27 Apr 2020 18:56:12 +0530
Subject: [PATCH 022/572] fix: fix calculation of dates and values for
dashboard charts
---
.../dashboard_chart/dashboard_chart.py | 108 ++++--------------
.../dashboard_chart/test_dashboard_chart.py | 58 +++++++---
2 files changed, 64 insertions(+), 102 deletions(-)
diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py
index 7bed8f4504..4502748504 100644
--- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py
+++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py
@@ -128,7 +128,6 @@ def get_chart_config(chart, filters, timespan, timegrain, from_date, to_date):
to_date = datetime.datetime.now()
doctype = chart.document_type
- unit_function = get_unit_function(doctype, chart.based_on, timegrain)
datefield = chart.based_on
aggregate_function = get_aggregate_function(chart.chart_type)
value_field = chart.value_based_on or '1'
@@ -141,23 +140,18 @@ def get_chart_config(chart, filters, timespan, timegrain, from_date, to_date):
data = frappe.db.get_all(
doctype,
fields = [
- 'extract(year from `tab{doctype}`.{datefield}) as _year'.format(doctype=doctype, datefield=datefield),
- '{} as _unit'.format(unit_function),
+ '{} as _unit'.format(datefield),
'{aggregate_function}({value_field})'.format(aggregate_function=aggregate_function, value_field=value_field),
],
filters = filters,
- group_by = '_year, _unit',
- order_by = '_year asc, _unit asc',
+ group_by = '_unit',
+ order_by = '_unit asc',
as_list = True,
ignore_ifnull = True
)
+ result = get_result(data, timegrain, from_date, to_date)
- # result given as year, unit -> convert it to end of period of that unit
- result = convert_to_dates(data, timegrain)
-
- # add missing data points for periods where there was no result
- result = add_missing_values(result, timegrain, timespan, from_date, to_date)
chart_config = {
"labels": [formatdate(r[0].strftime('%Y-%m-%d')) for r in result],
"datasets": [{
@@ -217,75 +211,22 @@ def get_aggregate_function(chart_type):
}[chart_type]
-def convert_to_dates(data, timegrain):
- """ Converts individual dates within data to the end of period """
- result = []
- for d in data:
- if d[2] != 0:
- if timegrain == 'Daily':
- result.append([add_to_date('{:d}-01-01'.format(int(d[0])), days = d[1] - 1), d[2]])
- elif timegrain == 'Weekly':
- result.append([add_to_date(add_to_date('{:d}-01-01'.format(int(d[0])), weeks = d[1] + 1), days = -1), d[2]])
- elif timegrain == 'Monthly':
- result.append([add_to_date(add_to_date('{:d}-01-01'.format(int(d[0])), months=d[1]), days = -1), d[2]])
- elif timegrain == 'Quarterly':
- result.append([add_to_date(add_to_date('{:d}-01-01'.format(int(d[0])), months=d[1] * 3), days = -1), d[2]])
- elif timegrain == 'Yearly':
- result.append([add_to_date(add_to_date('{:d}-01-01'.format(int(d[0])), months=12), days = -1), d[2]])
- result[-1][0] = getdate(result[-1][0])
-
- return result
-
-def get_unit_function(doctype, datefield, timegrain):
- unit_function = ''
- if timegrain=='Daily':
- if frappe.db.db_type == 'mariadb':
- unit_function = 'dayofyear(`tab{doctype}`.{datefield})'.format(
- doctype=doctype, datefield=datefield)
- else:
- unit_function = 'extract(doy from `tab{doctype}`.{datefield})'.format(
- doctype=doctype, datefield=datefield)
-
- else:
- unit_function = 'extract({unit} from `tab{doctype}`.{datefield})'.format(
- unit = timegrain[:-2].lower(), doctype=doctype, datefield=datefield)
-
- return unit_function
-
-def add_missing_values(data, timegrain, timespan, from_date, to_date):
- # add missing intervals
+def get_result(data, timegrain, from_date, to_date):
+ start_date = getdate(from_date)
+ end_date = getdate(to_date)
result = []
- if timespan != 'All Time':
- first_expected_date = get_period_ending(from_date, timegrain)
- # fill out data before the first data point
- first_data_point_date = data[0][0] if data else getdate(add_to_date(to_date, days=1))
- while first_data_point_date > first_expected_date:
- result.append([first_expected_date, 0.0])
- first_expected_date = get_next_expected_date(first_expected_date, timegrain)
+ while start_date <= end_date:
+ next_date = get_next_expected_date(start_date, timegrain)
+ result.append([next_date, 0.0])
+ start_date = next_date
- # fill data points and missing points
- for i, d in enumerate(data):
- result.append(d)
-
- next_expected_date = get_next_expected_date(d[0], timegrain)
-
- if i < len(data)-1:
- next_date = data[i+1][0]
- else:
- # already reached at end of data, see if we need any more dates
- next_date = getdate(nowdate())
-
- # if next data point is earler than the expected date
- # need to fill out missing data points
- while next_date > next_expected_date:
- # fill missing value
- result.append([next_expected_date, 0.0])
- next_expected_date = get_next_expected_date(next_expected_date, timegrain)
-
- # add date for the last period (if missing)
- if result and get_period_ending(to_date, timegrain) > result[-1][0]:
- result.append([get_period_ending(to_date, timegrain), 0.0])
+ data_index = 0
+ if data:
+ for i, d in enumerate(result):
+ while data_index < len(data) and getdate(data[data_index][0]) <= d[0]:
+ d[1] += data[data_index][1]
+ data_index += 1
return result
@@ -314,17 +255,12 @@ def get_period_ending(date, timegrain):
return getdate(date)
def get_week_ending(date):
- # fun fact: week ends on the day before 1st Jan of the year.
- # for 2019 it is Monday
+ # week starts on monday
+ from datetime import timedelta
+ start = date - timedelta(days = date.weekday())
+ end = start + timedelta(days=6)
- week_of_the_year = int(date.strftime('%U'))
-
- if week_of_the_year == 52:
- date = add_to_date(date, years=1)
- # first day of next week
- date = add_to_date('{}-01-01'.format(date.year), weeks = (week_of_the_year%52) + 1)
- # last day of this week
- return add_to_date(date, days=-1)
+ return end
def get_month_ending(date):
month_of_the_year = int(date.strftime('%m'))
diff --git a/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py
index 4425c4fd45..dfc6edbf58 100644
--- a/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py
+++ b/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py
@@ -17,10 +17,9 @@ class TestDashboardChart(unittest.TestCase):
self.assertEqual(get_period_ending('2019-04-10', 'Daily'),
getdate('2019-04-10'))
- # fun fact: week ends on the day before 1st Jan of the year.
- # for 2019 it is Monday
+ # week starts on monday
self.assertEqual(get_period_ending('2019-04-10', 'Weekly'),
- getdate('2019-04-15'))
+ getdate('2019-04-14'))
self.assertEqual(get_period_ending('2019-04-10', 'Monthly'),
getdate('2019-04-30'))
@@ -133,6 +132,34 @@ class TestDashboardChart(unittest.TestCase):
frappe.db.rollback()
+ def test_weekly_dashboard_chart(self):
+ insert_test_records()
+
+ if frappe.db.exists('Dashboard Chart', 'Test Weekly Dashboard Chart'):
+ frappe.delete_doc('Dashboard Chart', 'Test Weekly Dashboard Chart')
+
+ frappe.get_doc(dict(
+ doctype = 'Dashboard Chart',
+ chart_name = 'Test Weekly Dashboard Chart',
+ chart_type = 'Sum',
+ document_type = 'Communication',
+ based_on = 'communication_date',
+ value_based_on = 'rating',
+ timespan = 'Select Date Range',
+ time_interval = 'Weekly',
+ from_date = datetime(2018, 12, 30),
+ to_date = datetime(2019, 1, 15),
+ filters_json = '[]',
+ timeseries = 1
+ )).insert()
+
+ result = get(chart_name ='Test Weekly Dashboard Chart', refresh = 1)
+
+ self.assertEqual(result.get('datasets')[0].get('values'), [200.0, 400.0, 0.0])
+ self.assertEqual(result.get('labels'), [formatdate('2019-01-06'), formatdate('2019-01-13'), formatdate('2019-01-20')])
+
+ frappe.db.rollback()
+
def test_group_by_chart_type(self):
if frappe.db.exists('Dashboard Chart', 'Test Group By Dashboard Chart'):
frappe.delete_doc('Dashboard Chart', 'Test Group By Dashboard Chart')
@@ -155,17 +182,16 @@ class TestDashboardChart(unittest.TestCase):
frappe.db.rollback()
- def test_dashboard_with_single_doctype(self):
- if frappe.db.exists('Dashboard Chart', 'Test Single DocType In Dashboard Chart'):
- frappe.delete_doc('Dashboard Chart', 'Test Single DocType In Dashboard Chart')
+def insert_test_records():
+ create_new_communication(datetime(2019, 1, 10), 100)
+ create_new_communication(datetime(2019, 1, 6), 200)
+ create_new_communication(datetime(2019, 1, 8), 300)
- chart_doc = frappe.get_doc(dict(
- doctype = 'Dashboard Chart',
- chart_name = 'Test Single DocType In Dashboard Chart',
- chart_type = 'Count',
- document_type = 'System Settings',
- group_by_based_on = 'Created On',
- filters_json = '{}',
- ))
-
- self.assertRaises(frappe.ValidationError, chart_doc.insert)
+def create_new_communication(date, rating):
+ communication = {
+ 'doctype': 'Communication',
+ 'subject': 'Test Communication',
+ 'rating': rating,
+ 'communication_date': date
+ }
+ frappe.get_doc(communication).insert()
From 58b90e72f35a6b004230b3197ca624ea70c5a5dd Mon Sep 17 00:00:00 2001
From: Rucha Mahabal
Date: Mon, 4 May 2020 13:20:27 +0530
Subject: [PATCH 023/572] feat: duration control
---
frappe/core/doctype/docfield/docfield.json | 4 +-
.../doctype/custom_field/custom_field.json | 4 +-
.../customize_form_field.json | 4 +-
frappe/database/mariadb/database.py | 3 +-
frappe/database/postgres/database.py | 3 +-
frappe/model/__init__.py | 3 +-
frappe/public/build.json | 4 +-
.../public/js/frappe/form/controls/control.js | 1 +
.../js/frappe/form/controls/duration.js | 159 ++++++++++++++++++
frappe/public/less/controls.less | 63 +++++++
.../website_theme/standard/standard.json | 2 +-
11 files changed, 239 insertions(+), 11 deletions(-)
create mode 100644 frappe/public/js/frappe/form/controls/duration.js
diff --git a/frappe/core/doctype/docfield/docfield.json b/frappe/core/doctype/docfield/docfield.json
index 6d8ee41a5a..04f31375d6 100644
--- a/frappe/core/doctype/docfield/docfield.json
+++ b/frappe/core/doctype/docfield/docfield.json
@@ -86,7 +86,7 @@
"label": "Type",
"oldfieldname": "fieldtype",
"oldfieldtype": "Select",
- "options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRead Only\nRating\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature",
+ "options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRead Only\nRating\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature",
"reqd": 1,
"search_index": 1
},
@@ -453,7 +453,7 @@
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2020-04-19 21:54:13.783908",
+ "modified": "2020-04-30 09:00:38.012601",
"modified_by": "Administrator",
"module": "Core",
"name": "DocField",
diff --git a/frappe/custom/doctype/custom_field/custom_field.json b/frappe/custom/doctype/custom_field/custom_field.json
index 394f38b56c..6534bfd90e 100644
--- a/frappe/custom/doctype/custom_field/custom_field.json
+++ b/frappe/custom/doctype/custom_field/custom_field.json
@@ -116,7 +116,7 @@
"label": "Field Type",
"oldfieldname": "fieldtype",
"oldfieldtype": "Select",
- "options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nGeolocation\nHTML\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRating\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature",
+ "options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nGeolocation\nHTML\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRating\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature",
"reqd": 1
},
{
@@ -383,7 +383,7 @@
"icon": "fa fa-glass",
"idx": 1,
"links": [],
- "modified": "2020-04-10 11:57:10.392218",
+ "modified": "2020-04-30 09:15:24.394782",
"modified_by": "Administrator",
"module": "Custom",
"name": "Custom Field",
diff --git a/frappe/custom/doctype/customize_form_field/customize_form_field.json b/frappe/custom/doctype/customize_form_field/customize_form_field.json
index d7887cf8bd..d1510e0858 100644
--- a/frappe/custom/doctype/customize_form_field/customize_form_field.json
+++ b/frappe/custom/doctype/customize_form_field/customize_form_field.json
@@ -78,7 +78,7 @@
"label": "Type",
"oldfieldname": "fieldtype",
"oldfieldtype": "Select",
- "options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRating\nRead Only\nSection Break\nSelect\nSignature\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime",
+ "options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRating\nRead Only\nSection Break\nSelect\nSignature\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime",
"reqd": 1,
"search_index": 1
},
@@ -393,7 +393,7 @@
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2020-04-10 11:58:44.573537",
+ "modified": "2020-04-30 09:15:51.094586",
"modified_by": "Administrator",
"module": "Custom",
"name": "Customize Form Field",
diff --git a/frappe/database/mariadb/database.py b/frappe/database/mariadb/database.py
index cd053569f0..7350e0aaef 100644
--- a/frappe/database/mariadb/database.py
+++ b/frappe/database/mariadb/database.py
@@ -55,7 +55,8 @@ class MariaDBDatabase(Database):
'Signature': ('longtext', ''),
'Color': ('varchar', self.VARCHAR_LEN),
'Barcode': ('longtext', ''),
- 'Geolocation': ('longtext', '')
+ 'Geolocation': ('longtext', ''),
+ 'Duration': ('bigint', '20')
}
def get_connection(self):
diff --git a/frappe/database/postgres/database.py b/frappe/database/postgres/database.py
index e30ef3293f..78dc6d42ec 100644
--- a/frappe/database/postgres/database.py
+++ b/frappe/database/postgres/database.py
@@ -60,7 +60,8 @@ class PostgresDatabase(Database):
'Signature': ('text', ''),
'Color': ('varchar', self.VARCHAR_LEN),
'Barcode': ('text', ''),
- 'Geolocation': ('text', '')
+ 'Geolocation': ('text', ''),
+ 'Duration': ('bigint', '20')
}
def get_connection(self):
diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py
index 93ef78df7b..3c5d996439 100644
--- a/frappe/model/__init__.py
+++ b/frappe/model/__init__.py
@@ -34,7 +34,8 @@ data_fieldtypes = (
'Signature',
'Color',
'Barcode',
- 'Geolocation'
+ 'Geolocation',
+ 'Duration'
)
no_value_fields = ('Section Break', 'Column Break', 'HTML', 'Table', 'Table MultiSelect', 'Button', 'Image',
diff --git a/frappe/public/build.json b/frappe/public/build.json
index 7f55924a6b..fe89e3bcc9 100755
--- a/frappe/public/build.json
+++ b/frappe/public/build.json
@@ -69,7 +69,8 @@
"node_modules/air-datepicker/dist/js/i18n/datepicker.sk.js",
"node_modules/air-datepicker/dist/js/i18n/datepicker.zh.js",
"public/js/frappe/ui/capture.js",
- "public/js/frappe/form/controls/control.js"
+ "public/js/frappe/form/controls/control.js",
+ "public/js/frappe/form/controls/duration.js"
],
"js/dialog.min.js": [
"public/js/frappe/dom.js",
@@ -259,6 +260,7 @@
"public/js/frappe/form/templates/timeline.html",
"public/js/frappe/form/templates/timeline_item.html",
"public/js/frappe/form/controls/control.js",
+ "public/js/frappe/form/controls/duration.js",
"public/js/frappe/views/formview.js",
"public/js/frappe/form/form.js",
"public/js/frappe/meta_tag.js"
diff --git a/frappe/public/js/frappe/form/controls/control.js b/frappe/public/js/frappe/form/controls/control.js
index 2bf6292abc..168da2717c 100644
--- a/frappe/public/js/frappe/form/controls/control.js
+++ b/frappe/public/js/frappe/form/controls/control.js
@@ -38,6 +38,7 @@ import './table_multiselect';
import './multiselect_pills';
import './multiselect_list';
import './rating';
+import './duration';
frappe.ui.form.make_control = function (opts) {
var control_class_name = "Control" + opts.df.fieldtype.replace(/ /g, "");
diff --git a/frappe/public/js/frappe/form/controls/duration.js b/frappe/public/js/frappe/form/controls/duration.js
new file mode 100644
index 0000000000..8c9aa68b5b
--- /dev/null
+++ b/frappe/public/js/frappe/form/controls/duration.js
@@ -0,0 +1,159 @@
+frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
+ make_input: function() {
+ this._super();
+ this.make_picker();
+ },
+
+ make_picker: function() {
+ this.inputs = [];
+ this.$picker = $(
+ ``
+ );
+ this.$wrapper.append(this.$picker);
+ this.build_numeric_input('days', false);
+ this.build_numeric_input('hrs', false, 23);
+ this.build_numeric_input('mins', false, 59);
+ this.build_numeric_input('secs', false, 59);
+ this.set_duration_picker();
+ this.$picker.hide();
+ this.bind_events();
+ this.refresh();
+ },
+
+ build_numeric_input: function(label, hidden, max) {
+ let $duration_input = $(`
+
+ `)
+
+ let $input = $(``).prepend($duration_input);
+
+ if (max) {
+ $duration_input.attr('max', max);
+ }
+
+ this.inputs[label] = $duration_input;
+
+ let $control = $(`
+ `
+ )
+
+ if (hidden) {
+ $control.addClass('hidden');
+ }
+ $control.prepend($input);
+ $control.appendTo($('.picker-row'));
+ },
+
+ set_duration_picker() {
+ let total_duration = this.seconds_to_duration(this.value);
+ if (total_duration.days()) {
+ this.$picker.find(`[data-duration='days']`).prop('value', total_duration.days());
+ }
+ if (total_duration.hours()) {
+ this.$picker.find(`[data-duration='hrs']`).prop('value', total_duration.hours());
+ }
+ if (total_duration.minutes()) {
+ this.$picker.find(`[data-duration='mins']`).prop('value', total_duration.minutes());
+ }
+ if (total_duration.seconds()) {
+ this.$picker.find(`[data-duration='secs']`).prop('value', total_duration.seconds());
+ }
+ },
+
+ bind_events: function() {
+ let me = this;
+ let clicked = false;
+
+ this.$picker.on('change', '.duration-input', () => {
+ clicked = false;
+ me.set_value(me.duration_to_seconds());
+ me.set_focus();
+ });
+
+ this.$wrapper.find(".duration-input").mousedown(() => {
+ clicked = true;
+ });
+
+ this.$input.on("focus", () => {
+ this.$picker.show();
+ });
+
+ this.$input.on("blur", () => {
+ if (clicked) {
+ clicked = false;
+ } else {
+ this.$picker.hide();
+ }
+ });
+ },
+
+ refresh_input: function() {
+ this._super();
+ this.set_duration_picker();
+ },
+
+ format_for_input: function(value) {
+ let input_string = '';
+ if (value) {
+ let total_duration = this.seconds_to_duration(value);
+
+ if (total_duration.days()) {
+ input_string += total_duration.days() + 'd';
+ }
+ if (total_duration.hours()) {
+ input_string += (input_string.length ? " " : "");
+ input_string += total_duration.hours() + 'h';
+ }
+ if (total_duration.minutes()) {
+ input_string += (input_string.length ? " " : "");
+ input_string += total_duration.minutes() + 'm';
+ }
+ if (total_duration.seconds()) {
+ input_string += (input_string.length ? " " : "");
+ input_string += total_duration.seconds() + 's';
+ }
+ }
+ return input_string;
+ },
+
+ seconds_to_duration(value) {
+ let secs = value;
+ let total_duration = moment.duration({
+ days: Math.floor(secs / (3600 * 24)),
+ hours: Math.floor(secs % (3600 * 24) / 3600),
+ minutes: Math.floor(secs % 3600 / 60),
+ seconds : Math.floor(secs % 60)
+ });
+ return total_duration;
+ },
+
+ duration_to_seconds() {
+ let value = 0;
+ if (this.inputs) {
+ let total_duration = moment.duration({
+ seconds : parseInt(this.inputs.secs.val()),
+ minutes : parseInt(this.inputs.mins.val()),
+ hours : parseInt(this.inputs.hrs.val()),
+ days : parseInt(this.inputs.days.val())
+ });
+
+ if (total_duration.days()) {
+ value += total_duration.days() * 24 * 60 * 60;
+ }
+ if (total_duration.hours()) {
+ value += total_duration.hours() * 60 * 60;
+ }
+ if (total_duration.minutes()) {
+ value += total_duration.minutes() * 60;
+ }
+ if (total_duration.seconds()) {
+ value += total_duration.seconds();
+ }
+ }
+ return value;
+ }
+});
\ No newline at end of file
diff --git a/frappe/public/less/controls.less b/frappe/public/less/controls.less
index d88e6adaec..9b585a58f3 100644
--- a/frappe/public/less/controls.less
+++ b/frappe/public/less/controls.less
@@ -165,3 +165,66 @@
top: 8px;
}
}
+
+/* duration control */
+
+.duration-picker {
+ position: relative;
+ z-index: 999;
+
+ border-radius: 4px;
+ box-shadow: 0 4px 12px rgba(0,0,0,.15);
+ background: #fff;
+ border: 1px solid @border-color;
+ padding-top: 10px;
+ padding-left: 5px;
+ position: absolute;
+
+ &:after,
+ &:before {
+ border: solid transparent;
+ content: " ";
+ height: 0;
+ width: 0;
+ pointer-events: none;
+ position: absolute;
+ bottom: 100%;
+ left: 30px;
+ }
+ &:after {
+ border-color: rgba(255, 255, 255, 0);
+ border-bottom-color: #fff;
+ border-width: 8px;
+ margin-left: -8px;
+ }
+ &:before {
+ border-color: rgba(221, 221, 221, 0);
+ border-bottom-color: @border-color;
+ border-width: 9px;
+ margin-left: -9px;
+ }
+
+ .duration-row {
+ margin: 7px;
+ display: flex;
+ }
+
+ .duration-col {
+ margin-left: 2px;
+ }
+
+ .duration-input {
+ width: 60px;
+ }
+
+ .duration-label {
+ justify-content: center;
+ }
+
+ .picker-row {
+ display: flex;
+ width: 335px;
+ margin-left: 5px;
+ margin-bottom: 3px;
+ }
+}
\ No newline at end of file
diff --git a/frappe/website/website_theme/standard/standard.json b/frappe/website/website_theme/standard/standard.json
index a799f25425..799eee72ab 100644
--- a/frappe/website/website_theme/standard/standard.json
+++ b/frappe/website/website_theme/standard/standard.json
@@ -9,7 +9,7 @@
"font_properties": "300,600",
"font_size": "",
"idx": 27,
- "modified": "2020-04-21 02:10:31.761219",
+ "modified": "2020-04-24 10:02:44.993836",
"modified_by": "Administrator",
"module": "Website",
"name": "Standard",
From b7408102411e4375aa1a155b3ee2bc809782aebc Mon Sep 17 00:00:00 2001
From: Mangesh-Khairnar
Date: Mon, 4 May 2020 14:00:14 +0530
Subject: [PATCH 024/572] feat(blog settings): social share settings
---
.../doctype/blog_settings/blog_settings.json | 182 ++++++------------
.../doctype/social_link_settings/__init__.py | 0
.../social_link_settings.json | 43 +++++
.../social_link_settings.py | 10 +
4 files changed, 107 insertions(+), 128 deletions(-)
create mode 100644 frappe/website/doctype/social_link_settings/__init__.py
create mode 100644 frappe/website/doctype/social_link_settings/social_link_settings.json
create mode 100644 frappe/website/doctype/social_link_settings/social_link_settings.py
diff --git a/frappe/website/doctype/blog_settings/blog_settings.json b/frappe/website/doctype/blog_settings/blog_settings.json
index 1ee974d850..f0e51de170 100644
--- a/frappe/website/doctype/blog_settings/blog_settings.json
+++ b/frappe/website/doctype/blog_settings/blog_settings.json
@@ -1,139 +1,65 @@
{
- "allow_copy": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2013-03-11 17:48:16",
- "custom": 0,
- "description": "Blog Settings",
- "docstatus": 0,
- "doctype": "DocType",
- "editable_grid": 0,
+ "actions": [],
+ "creation": "2013-03-11 17:48:16",
+ "description": "Blog Settings",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+ "blog_title",
+ "blog_introduction",
+ "writers_introduction",
+ "section_break_4",
+ "social_share_settings"
+ ],
"fields": [
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "blog_title",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Blog Title",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "blog_title",
+ "fieldtype": "Data",
+ "label": "Blog Title"
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "blog_introduction",
- "fieldtype": "Small Text",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Blog Introduction",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "blog_introduction",
+ "fieldtype": "Small Text",
+ "label": "Blog Introduction"
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "writers_introduction",
- "fieldtype": "Small Text",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Writers Introduction",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
+ "fieldname": "writers_introduction",
+ "fieldtype": "Small Text",
+ "label": "Writers Introduction"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "section_break_4",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "social_share_settings",
+ "fieldtype": "Table",
+ "label": "Social Share Settings",
+ "options": "Social Link Settings"
}
- ],
- "hide_heading": 0,
- "hide_toolbar": 0,
- "icon": "fa fa-cog",
- "idx": 1,
- "image_view": 0,
- "in_create": 0,
-
- "is_submittable": 0,
- "issingle": 1,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2016-12-29 14:40:41.629468",
- "modified_by": "Administrator",
- "module": "Website",
- "name": "Blog Settings",
- "owner": "Administrator",
+ ],
+ "icon": "fa fa-cog",
+ "idx": 1,
+ "issingle": 1,
+ "links": [],
+ "modified": "2020-05-04 09:10:41.815238",
+ "modified_by": "Administrator",
+ "module": "Website",
+ "name": "Blog Settings",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 1,
- "delete": 0,
- "email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "is_custom": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 0,
- "role": "Website Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "role": "Website Manager",
+ "share": 1,
"write": 1
}
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "track_changes": 1,
- "track_seen": 0
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/frappe/website/doctype/social_link_settings/__init__.py b/frappe/website/doctype/social_link_settings/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/website/doctype/social_link_settings/social_link_settings.json b/frappe/website/doctype/social_link_settings/social_link_settings.json
new file mode 100644
index 0000000000..459c98eed6
--- /dev/null
+++ b/frappe/website/doctype/social_link_settings/social_link_settings.json
@@ -0,0 +1,43 @@
+{
+ "actions": [],
+ "creation": "2020-04-30 07:39:33.095554",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "social_link_type",
+ "color",
+ "background_color"
+ ],
+ "fields": [
+ {
+ "fieldname": "social_link_type",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Social Link Type",
+ "options": "\nfacebook\nlinkedin\ntwitter\nemail"
+ },
+ {
+ "fieldname": "color",
+ "fieldtype": "Color",
+ "in_list_view": 1,
+ "label": "Color"
+ },
+ {
+ "fieldname": "background_color",
+ "fieldtype": "Color",
+ "in_list_view": 1,
+ "label": "Background Color"
+ }
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2020-05-01 23:55:04.731518",
+ "modified_by": "Administrator",
+ "module": "Website",
+ "name": "Social Link Settings",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/frappe/website/doctype/social_link_settings/social_link_settings.py b/frappe/website/doctype/social_link_settings/social_link_settings.py
new file mode 100644
index 0000000000..b382afac99
--- /dev/null
+++ b/frappe/website/doctype/social_link_settings/social_link_settings.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class SocialLinkSettings(Document):
+ pass
From 2cd39a1bc4aa4eff44b482b9d786b224b7ac0923 Mon Sep 17 00:00:00 2001
From: Rucha Mahabal
Date: Mon, 4 May 2020 14:36:32 +0530
Subject: [PATCH 025/572] feat: add duration options
---
.../js/frappe/form/controls/duration.js | 22 +++++++++++++++----
1 file changed, 18 insertions(+), 4 deletions(-)
diff --git a/frappe/public/js/frappe/form/controls/duration.js b/frappe/public/js/frappe/form/controls/duration.js
index 8c9aa68b5b..c615dd6eb8 100644
--- a/frappe/public/js/frappe/form/controls/duration.js
+++ b/frappe/public/js/frappe/form/controls/duration.js
@@ -6,16 +6,17 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
make_picker: function() {
this.inputs = [];
+ this.set_duration_options();
this.$picker = $(
``
);
this.$wrapper.append(this.$picker);
- this.build_numeric_input('days', false);
- this.build_numeric_input('hrs', false, 23);
- this.build_numeric_input('mins', false, 59);
- this.build_numeric_input('secs', false, 59);
+ this.build_numeric_input('days', !this.duration_options.showDays);
+ this.build_numeric_input('hrs', false);
+ this.build_numeric_input('mins', false);
+ this.build_numeric_input('secs', !this.duration_options.showSeconds);
this.set_duration_picker();
this.$picker.hide();
this.bind_events();
@@ -48,6 +49,19 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
$control.appendTo($('.picker-row'));
},
+ set_duration_options() {
+ let lang = 'en';
+ frappe.boot.user && (lang = frappe.boot.user.language);
+
+ this.duration_options = {
+ lang: lang,
+ max: 59,
+ checkRanges: false,
+ showSeconds: true,
+ showDays: true,
+ };
+ },
+
set_duration_picker() {
let total_duration = this.seconds_to_duration(this.value);
if (total_duration.days()) {
From ef3e3685e90449f3379102833f40d6c7d6a50674 Mon Sep 17 00:00:00 2001
From: Mangesh-Khairnar
Date: Mon, 4 May 2020 18:01:14 +0530
Subject: [PATCH 026/572] feat(about): add social media links
---
frappe/public/js/frappe/ui/toolbar/about.js | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/frappe/public/js/frappe/ui/toolbar/about.js b/frappe/public/js/frappe/ui/toolbar/about.js
index 13ba4836fb..87462916c2 100644
--- a/frappe/public/js/frappe/ui/toolbar/about.js
+++ b/frappe/public/js/frappe/ui/toolbar/about.js
@@ -9,6 +9,12 @@ frappe.ui.misc.about = function() {
Website: https://frappe.io
\
\
Source: https://github.com/frappe
\
+ \
+ Linkedin: https://linkedin.com/company/frappe-tech
\
+ \
+ Facebook: https://facebook.com/erpnext
\
+ \
+ Twitter: https://twitter.com/erpnext
\
\
Installed Apps
\
Loading versions...
\
From 94bfe471a56ea51a11224a68e5f87d0e85c81c15 Mon Sep 17 00:00:00 2001
From: Rucha Mahabal
Date: Mon, 4 May 2020 19:12:19 +0530
Subject: [PATCH 027/572] fix: wrong days setting
---
.../js/frappe/form/controls/duration.js | 56 +++++++++----------
1 file changed, 28 insertions(+), 28 deletions(-)
diff --git a/frappe/public/js/frappe/form/controls/duration.js b/frappe/public/js/frappe/form/controls/duration.js
index c615dd6eb8..ade49e2293 100644
--- a/frappe/public/js/frappe/form/controls/duration.js
+++ b/frappe/public/js/frappe/form/controls/duration.js
@@ -64,17 +64,17 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
set_duration_picker() {
let total_duration = this.seconds_to_duration(this.value);
- if (total_duration.days()) {
- this.$picker.find(`[data-duration='days']`).prop('value', total_duration.days());
+ if (total_duration.days) {
+ this.$picker.find(`[data-duration='days']`).prop('value', total_duration.days);
}
- if (total_duration.hours()) {
- this.$picker.find(`[data-duration='hrs']`).prop('value', total_duration.hours());
+ if (total_duration.hours) {
+ this.$picker.find(`[data-duration='hrs']`).prop('value', total_duration.hours);
}
- if (total_duration.minutes()) {
- this.$picker.find(`[data-duration='mins']`).prop('value', total_duration.minutes());
+ if (total_duration.minutes) {
+ this.$picker.find(`[data-duration='mins']`).prop('value', total_duration.minutes);
}
- if (total_duration.seconds()) {
- this.$picker.find(`[data-duration='secs']`).prop('value', total_duration.seconds());
+ if (total_duration.seconds) {
+ this.$picker.find(`[data-duration='secs']`).prop('value', total_duration.seconds);
}
},
@@ -115,20 +115,20 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
if (value) {
let total_duration = this.seconds_to_duration(value);
- if (total_duration.days()) {
- input_string += total_duration.days() + 'd';
+ if (total_duration.days) {
+ input_string += total_duration.days + 'd';
}
- if (total_duration.hours()) {
+ if (total_duration.hours) {
input_string += (input_string.length ? " " : "");
- input_string += total_duration.hours() + 'h';
+ input_string += total_duration.hours + 'h';
}
- if (total_duration.minutes()) {
+ if (total_duration.minutes) {
input_string += (input_string.length ? " " : "");
- input_string += total_duration.minutes() + 'm';
+ input_string += total_duration.minutes + 'm';
}
- if (total_duration.seconds()) {
+ if (total_duration.seconds) {
input_string += (input_string.length ? " " : "");
- input_string += total_duration.seconds() + 's';
+ input_string += total_duration.seconds + 's';
}
}
return input_string;
@@ -136,36 +136,36 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
seconds_to_duration(value) {
let secs = value;
- let total_duration = moment.duration({
+ let total_duration = {
days: Math.floor(secs / (3600 * 24)),
hours: Math.floor(secs % (3600 * 24) / 3600),
minutes: Math.floor(secs % 3600 / 60),
seconds : Math.floor(secs % 60)
- });
+ };
return total_duration;
},
duration_to_seconds() {
let value = 0;
if (this.inputs) {
- let total_duration = moment.duration({
+ let total_duration = {
seconds : parseInt(this.inputs.secs.val()),
minutes : parseInt(this.inputs.mins.val()),
hours : parseInt(this.inputs.hrs.val()),
days : parseInt(this.inputs.days.val())
- });
+ };
- if (total_duration.days()) {
- value += total_duration.days() * 24 * 60 * 60;
+ if (total_duration.days) {
+ value += total_duration.days * 24 * 60 * 60;
}
- if (total_duration.hours()) {
- value += total_duration.hours() * 60 * 60;
+ if (total_duration.hours) {
+ value += total_duration.hours * 60 * 60;
}
- if (total_duration.minutes()) {
- value += total_duration.minutes() * 60;
+ if (total_duration.minutes) {
+ value += total_duration.minutes * 60;
}
- if (total_duration.seconds()) {
- value += total_duration.seconds();
+ if (total_duration.seconds) {
+ value += total_duration.seconds;
}
}
return value;
From dc555864dd544c168d38b77c1a814991772bb572 Mon Sep 17 00:00:00 2001
From: Rucha Mahabal
Date: Mon, 4 May 2020 21:40:35 +0530
Subject: [PATCH 028/572] fix: add formatter and util methods for duration
fieldtype
---
.../js/frappe/form/controls/duration.js | 35 ++-----------------
frappe/public/js/frappe/form/formatters.js | 7 ++++
frappe/public/js/frappe/utils/utils.js | 34 ++++++++++++++++++
3 files changed, 43 insertions(+), 33 deletions(-)
diff --git a/frappe/public/js/frappe/form/controls/duration.js b/frappe/public/js/frappe/form/controls/duration.js
index ade49e2293..c0ca37dea3 100644
--- a/frappe/public/js/frappe/form/controls/duration.js
+++ b/frappe/public/js/frappe/form/controls/duration.js
@@ -63,7 +63,7 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
},
set_duration_picker() {
- let total_duration = this.seconds_to_duration(this.value);
+ let total_duration = frappe.utils.seconds_to_duration(this.value);
if (total_duration.days) {
this.$picker.find(`[data-duration='days']`).prop('value', total_duration.days);
}
@@ -111,38 +111,7 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
},
format_for_input: function(value) {
- let input_string = '';
- if (value) {
- let total_duration = this.seconds_to_duration(value);
-
- if (total_duration.days) {
- input_string += total_duration.days + 'd';
- }
- if (total_duration.hours) {
- input_string += (input_string.length ? " " : "");
- input_string += total_duration.hours + 'h';
- }
- if (total_duration.minutes) {
- input_string += (input_string.length ? " " : "");
- input_string += total_duration.minutes + 'm';
- }
- if (total_duration.seconds) {
- input_string += (input_string.length ? " " : "");
- input_string += total_duration.seconds + 's';
- }
- }
- return input_string;
- },
-
- seconds_to_duration(value) {
- let secs = value;
- let total_duration = {
- days: Math.floor(secs / (3600 * 24)),
- hours: Math.floor(secs % (3600 * 24) / 3600),
- minutes: Math.floor(secs % 3600 / 60),
- seconds : Math.floor(secs % 60)
- };
- return total_duration;
+ return frappe.utils.get_formatted_duration(value);
},
duration_to_seconds() {
diff --git a/frappe/public/js/frappe/form/formatters.js b/frappe/public/js/frappe/form/formatters.js
index d178c59100..cc24c48a1f 100644
--- a/frappe/public/js/frappe/form/formatters.js
+++ b/frappe/public/js/frappe/form/formatters.js
@@ -188,6 +188,13 @@ frappe.form.formatters = {
return value || "";
},
+ Duration: function(value) {
+ if (value) {
+ value = frappe.utils.get_formatted_duration(value);
+ }
+
+ return value || "";
+ },
LikedBy: function(value) {
var html = "";
$.each(JSON.parse(value || "[]"), function(i, v) {
diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js
index 1afdbfd81c..2584248711 100644
--- a/frappe/public/js/frappe/utils/utils.js
+++ b/frappe/public/js/frappe/utils/utils.js
@@ -778,6 +778,40 @@ Object.assign(frappe.utils, {
version: M[1],
};
},
+
+ get_formatted_duration(value) {
+ let duration = '';
+ if (value) {
+ let total_duration = frappe.utils.seconds_to_duration(value);
+
+ if (total_duration.days) {
+ duration += total_duration.days + 'd';
+ }
+ if (total_duration.hours) {
+ duration += (duration.length ? " " : "");
+ duration += total_duration.hours + 'h';
+ }
+ if (total_duration.minutes) {
+ duration += (duration.length ? " " : "");
+ duration += total_duration.minutes + 'm';
+ }
+ if (total_duration.seconds) {
+ duration += (duration.length ? " " : "");
+ duration += total_duration.seconds + 's';
+ }
+ }
+ return duration;
+ },
+ seconds_to_duration(value) {
+ let secs = value;
+ let total_duration = {
+ days: Math.floor(secs / (3600 * 24)),
+ hours: Math.floor(secs % (3600 * 24) / 3600),
+ minutes: Math.floor(secs % 3600 / 60),
+ seconds : Math.floor(secs % 60)
+ };
+ return total_duration;
+ },
});
// Array de duplicate
From 003225f20c4c776f794cb3935a69c59933675dc1 Mon Sep 17 00:00:00 2001
From: Rucha Mahabal
Date: Mon, 4 May 2020 21:59:46 +0530
Subject: [PATCH 029/572] fix: filters for duration fieldtype
---
frappe/public/js/frappe/form/controls/duration.js | 4 ++++
frappe/public/js/frappe/ui/filters/filters.js | 2 ++
2 files changed, 6 insertions(+)
diff --git a/frappe/public/js/frappe/form/controls/duration.js b/frappe/public/js/frappe/form/controls/duration.js
index c0ca37dea3..791882185c 100644
--- a/frappe/public/js/frappe/form/controls/duration.js
+++ b/frappe/public/js/frappe/form/controls/duration.js
@@ -105,6 +105,10 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
});
},
+ get_value() {
+ return cint(this.value);
+ },
+
refresh_input: function() {
this._super();
this.set_duration_picker();
diff --git a/frappe/public/js/frappe/ui/filters/filters.js b/frappe/public/js/frappe/ui/filters/filters.js
index 3646dc6b6e..94e2b59d22 100644
--- a/frappe/public/js/frappe/ui/filters/filters.js
+++ b/frappe/public/js/frappe/ui/filters/filters.js
@@ -200,6 +200,8 @@ frappe.ui.FilterList = Class.extend({
value = {0:"Draft", 1:"Submitted", 2:"Cancelled"}[value] || value;
} else if(field.df.original_type==="Check") {
value = {0:"No", 1:"Yes"}[cint(value)];
+ } else if (field.df.original_type === "Duration") {
+ value = frappe.utils.get_formatted_duration(value);
}
value = frappe.format(value, field.df, {only_value: 1});
From 58e4ed06037b7ea45a88a087db0e941b1196d9a5 Mon Sep 17 00:00:00 2001
From: Rucha Mahabal
Date: Tue, 5 May 2020 00:59:03 +0530
Subject: [PATCH 030/572] fix: control styling in child table
---
frappe/public/js/frappe/form/controls/duration.js | 4 ++--
frappe/public/less/controls.less | 7 +++++++
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/frappe/public/js/frappe/form/controls/duration.js b/frappe/public/js/frappe/form/controls/duration.js
index 791882185c..86dee3866f 100644
--- a/frappe/public/js/frappe/form/controls/duration.js
+++ b/frappe/public/js/frappe/form/controls/duration.js
@@ -25,7 +25,7 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
build_numeric_input: function(label, hidden, max) {
let $duration_input = $(`
-
+
`)
let $input = $(``).prepend($duration_input);
@@ -46,7 +46,7 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
$control.addClass('hidden');
}
$control.prepend($input);
- $control.appendTo($('.picker-row'));
+ $control.appendTo(this.$picker.find('.picker-row'));
},
set_duration_options() {
diff --git a/frappe/public/less/controls.less b/frappe/public/less/controls.less
index 9b585a58f3..ac2936b149 100644
--- a/frappe/public/less/controls.less
+++ b/frappe/public/less/controls.less
@@ -204,6 +204,12 @@
margin-left: -9px;
}
+ .row .col {
+ // for fixing layout in child table
+ padding-left: 0px !important;
+ padding-right: 0px !important;
+ }
+
.duration-row {
margin: 7px;
display: flex;
@@ -215,6 +221,7 @@
.duration-input {
width: 60px;
+ border: 1px solid rgba(0, 0, 0, 0.25) !important;
}
.duration-label {
From 77d2c3d8cfaaa61aa7c8984f6e5ec3640c6ab020 Mon Sep 17 00:00:00 2001
From: Shivam Mishra
Date: Tue, 5 May 2020 13:43:34 +0530
Subject: [PATCH 031/572] feat: added patch for is unique
---
frappe/patches.txt | 1 +
.../patches/v13_0/set_unique_for_page_view.py | 6 +++++
.../doctype/web_page_view/web_page_view.json | 24 ++++++++++++++-----
3 files changed, 25 insertions(+), 6 deletions(-)
create mode 100644 frappe/patches/v13_0/set_unique_for_page_view.py
diff --git a/frappe/patches.txt b/frappe/patches.txt
index a086fa6f4a..3d8b0732d2 100644
--- a/frappe/patches.txt
+++ b/frappe/patches.txt
@@ -278,3 +278,4 @@ frappe.patches.v13_0.set_path_for_homepage_in_web_page_view
frappe.patches.v13_0.migrate_translation_column_data
frappe.patches.v13_0.set_read_times
frappe.patches.v13_0.remove_web_view
+frappe.patches.v13_0.set_unique_for_page_view
\ No newline at end of file
diff --git a/frappe/patches/v13_0/set_unique_for_page_view.py b/frappe/patches/v13_0/set_unique_for_page_view.py
new file mode 100644
index 0000000000..2a084e52e3
--- /dev/null
+++ b/frappe/patches/v13_0/set_unique_for_page_view.py
@@ -0,0 +1,6 @@
+import frappe
+
+def execute():
+ frappe.reload_doc('website', 'doctype', 'web_page_view', force=True)
+ site_url = frappe.utils.get_site_url(frappe.local.site)
+ frappe.db.sql("""UPDATE `tabWeb Page View` set is_unique=1 where referrer LIKE '%{0}%'""".format(site_url))
diff --git a/frappe/website/doctype/web_page_view/web_page_view.json b/frappe/website/doctype/web_page_view/web_page_view.json
index 7a1a210d62..4243df39b1 100644
--- a/frappe/website/doctype/web_page_view/web_page_view.json
+++ b/frappe/website/doctype/web_page_view/web_page_view.json
@@ -1,5 +1,6 @@
{
"actions": [],
+ "allow_import": 1,
"creation": "2020-04-15 22:54:46.009703",
"doctype": "DocType",
"editable_grid": 1,
@@ -9,7 +10,9 @@
"referrer",
"browser",
"browser_version",
- "date"
+ "is_unique",
+ "time_zone",
+ "user_agent"
],
"fields": [
{
@@ -39,15 +42,24 @@
"set_only_once": 1
},
{
- "fieldname": "date",
- "fieldtype": "Datetime",
- "label": "Date",
- "set_only_once": 1
+ "fieldname": "is_unique",
+ "fieldtype": "Data",
+ "label": "Is Unique"
+ },
+ {
+ "fieldname": "time_zone",
+ "fieldtype": "Data",
+ "label": "Time Zone"
+ },
+ {
+ "fieldname": "user_agent",
+ "fieldtype": "Data",
+ "label": "User Agent"
}
],
"in_create": 1,
"links": [],
- "modified": "2020-04-15 23:31:27.517793",
+ "modified": "2020-05-05 14:11:24.718770",
"modified_by": "Administrator",
"module": "Website",
"name": "Web Page View",
From ab9e596f627dc70dab273d1b8485796559ea23a3 Mon Sep 17 00:00:00 2001
From: Shivam Mishra
Date: Tue, 5 May 2020 15:06:14 +0530
Subject: [PATCH 032/572] feat: do not track 404
---
frappe/www/404.html | 6 ++++--
frappe/www/website_script.js | 2 +-
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/frappe/www/404.html b/frappe/www/404.html
index 47685c45d0..dc178dbdc8 100644
--- a/frappe/www/404.html
+++ b/frappe/www/404.html
@@ -15,7 +15,9 @@ html, body {
}
{% include "templates/styles/card_style.css" %}
-
+
{{_("Page Missing or Moved")}}
@@ -29,4 +31,4 @@ html, body {
background-color: #f5f7fa;
}
-{% endblock %}
+{% endblock %}
\ No newline at end of file
diff --git a/frappe/www/website_script.js b/frappe/www/website_script.js
index 7fdc2e94d6..e31b6812d5 100644
--- a/frappe/www/website_script.js
+++ b/frappe/www/website_script.js
@@ -14,7 +14,7 @@ ga('send', 'pageview');
{%- endif %}
{% if enable_view_tracking %}
- if (navigator.doNotTrack != 1) {
+ if (navigator.doNotTrack != 1 && !window.is_404) {
frappe.ready(() => {
let browser = frappe.utils.get_browser();
frappe.call("frappe.website.doctype.web_page_view.web_page_view.make_view_log", {
From fb66f85d6364f26b4d1cc4281aed6ac80ee8bdb7 Mon Sep 17 00:00:00 2001
From: Shivam Mishra
Date: Tue, 5 May 2020 15:06:48 +0530
Subject: [PATCH 033/572] feat: update website analytics report
---
.../website_analytics/website_analytics.py | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/frappe/website/report/website_analytics/website_analytics.py b/frappe/website/report/website_analytics/website_analytics.py
index 8b2d5b3806..b821cbff1f 100644
--- a/frappe/website/report/website_analytics/website_analytics.py
+++ b/frappe/website/report/website_analytics/website_analytics.py
@@ -17,8 +17,8 @@ class WebsiteAnalytics(object):
def run(self):
columns = self.get_columns()
data = self.get_data()
- summary = self.get_report_summary()
chart = self.get_chart_data()
+ summary = self.get_report_summary()
return columns, data, None, chart, summary
def get_columns(self):
@@ -56,7 +56,7 @@ class WebsiteAnalytics(object):
field, date_format = _get_field_for_chart(self.filters.range)
- data = frappe.db.sql("""
+ self.chart_data = frappe.db.sql("""
SELECT
DATE_FORMAT({0}, %s) as date,
COUNT(*) as count,
@@ -65,9 +65,9 @@ class WebsiteAnalytics(object):
WHERE creation BETWEEN %s AND %s
GROUP BY DATE_FORMAT({0}, %s)
ORDER BY creation
- """.format(field), (date_format, self.filters.from_date, self.filters.to_date, date_format), as_dict=1, debug=1)
+ """.format(field), (date_format, self.filters.from_date, self.filters.to_date, date_format), as_dict=1)
- return self.prepare_chart_data(data)
+ return self.prepare_chart_data(self.chart_data)
def prepare_chart_data(self, data):
date_range = get_date_range(self.filters.from_date, self.filters.to_date, self.filters.range)
@@ -121,14 +121,12 @@ class WebsiteAnalytics(object):
def get_report_summary(self):
- summary_data = frappe.get_all("Web Page View", fields=['is_unique', 'count(*) as count'], filters=self.query_filters, group_by="is_unique")
-
total_count = 0
unique_count = 0
- for data in summary_data:
- if data.get('is_unique'):
- unique_count = data.get('count')
+ for data in self.chart_data:
+ unique_count += data.get('unique_count')
total_count += data.get('count')
+
report_summary = [
{
"value": total_count,
From b9bfc6b2f51e0359f1a4c524d43f13d371d5d8b1 Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Tue, 5 May 2020 15:30:55 +0530
Subject: [PATCH 034/572] fix: review changes
---
.../list_view_settings.json | 4 +-
.../list_view_settings/list_view_settings.py | 17 +++---
frappe/public/js/frappe/list/list_settings.js | 59 ++++++++++++++++---
frappe/public/js/frappe/list/list_view.js | 53 +++++++++--------
4 files changed, 87 insertions(+), 46 deletions(-)
diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.json b/frappe/desk/doctype/list_view_settings/list_view_settings.json
index e59991c52e..486d2c1c44 100644
--- a/frappe/desk/doctype/list_view_settings/list_view_settings.json
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.json
@@ -35,7 +35,7 @@
{
"fieldname": "total_fields",
"fieldtype": "Select",
- "label": "Total Fields",
+ "label": "Maximum Number of Fields",
"options": "\n4\n5\n6\n7\n8\n9\n10"
},
{
@@ -52,7 +52,7 @@
}
],
"links": [],
- "modified": "2020-04-13 23:31:15.411147",
+ "modified": "2020-05-05 15:15:14.306665",
"modified_by": "Administrator",
"module": "Desk",
"name": "List View Settings",
diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.py b/frappe/desk/doctype/list_view_settings/list_view_settings.py
index 6cc7d11950..db3be47402 100644
--- a/frappe/desk/doctype/list_view_settings/list_view_settings.py
+++ b/frappe/desk/doctype/list_view_settings/list_view_settings.py
@@ -17,17 +17,14 @@ class ListViewSettings(Document):
@frappe.whitelist()
def save_listview_settings(doctype, listview_settings, removed_listview_fields):
- if isinstance(listview_settings, string_types):
- listview_settings = json.loads(listview_settings)
+ listview_settings = frappe.parse_json(listview_settings)
+ removed_listview_fields = frappe.parse_json(removed_listview_fields)
- if isinstance(removed_listview_fields, string_types):
- removed_listview_fields = json.loads(removed_listview_fields)
-
- try:
+ if frappe.get_all("List View Settings", filters={"name": doctype}):
doc = frappe.get_doc("List View Settings", doctype)
doc.update(listview_settings)
doc.save()
- except DoesNotExistError:
+ else:
doc = frappe.new_doc("List View Settings")
doc.name = doctype
doc.update(listview_settings)
@@ -43,8 +40,7 @@ def save_listview_settings(doctype, listview_settings, removed_listview_fields):
def set_listview_fields(doctype, listview_fields, removed_listview_fields):
meta = frappe.get_meta(doctype)
- if isinstance(listview_fields, string_types):
- listview_fields = [f.get("fieldname") for f in json.loads(listview_fields)]
+ listview_fields = [f.get("fieldname") for f in frappe.parse_json(listview_fields) if f.get("fieldname")]
for field in removed_listview_fields:
set_in_list_view_property(doctype, meta.get_field(field), "0")
@@ -53,6 +49,9 @@ def set_listview_fields(doctype, listview_fields, removed_listview_fields):
set_in_list_view_property(doctype, meta.get_field(field), "1")
def set_in_list_view_property(doctype, field, value):
+ if not field or field.fieldname == "status_field":
+ return
+
property_setter = frappe.db.get_value("Property Setter", {"doc_type": doctype, "field_name": field.fieldname, "property": "in_list_view"})
if property_setter:
doc = frappe.get_doc("Property Setter", property_setter)
diff --git a/frappe/public/js/frappe/list/list_settings.js b/frappe/public/js/frappe/list/list_settings.js
index e078a106b1..a2eaaa6689 100644
--- a/frappe/public/js/frappe/list/list_settings.js
+++ b/frappe/public/js/frappe/list/list_settings.js
@@ -9,7 +9,7 @@ export default class ListSettings {
this.meta = meta;
this.settings = settings;
this.dialog = null;
- this.fields = [];
+ this.fields = this.settings && this.settings.fields ? JSON.parse(this.settings.fields) : [];
this.subject_field = null;
frappe.model.with_doctype("List View Settings", () => {
@@ -28,7 +28,7 @@ export default class ListSettings {
let list_view_settings = frappe.get_meta("List View Settings");
me.dialog = new frappe.ui.Dialog({
- title: __("{0} List View Settings", [__(me.doctype)]),
+ title: __("{0} Settings", [__(me.doctype)]),
fields: list_view_settings.fields
});
me.dialog.set_values(me.settings);
@@ -67,14 +67,35 @@ export default class ListSettings {
show_dialog() {
let me = this;
+
+ if (!this.settings.fields) {
+ me.update_fields();
+ }
+
+ if (!me.dialog.get_value("total_fields")) {
+ let field_count = me.fields.length;
+
+ if (field_count < 4) {
+ field_count = 4;
+ } else if (field_count > 10) {
+ field_count = 4;
+ }
+
+ me.dialog.set_value("total_fields", field_count);
+ }
+
me.dialog.show();
}
setup_fields() {
+ function is_status_field(field) {
+ return field.fieldname === "status_field";
+ }
+
let me = this;
let fields_html = me.dialog.get_field("fields_html");
- let $wrapper = fields_html.$wrapper[0];
+ let wrapper = fields_html.$wrapper[0];
let fields = ``;
let total_fields = me.dialog.get_values().total_fields ? me.dialog.get_values().total_fields : me.settings.total_fields;
@@ -83,16 +104,17 @@ export default class ListSettings {
break;
}
let is_sortable = (idx == 0) ? `` : `sortable`;
- let can_remove = (idx == 0) ? `hide` : ``;
+ let show_sortable_handle = (idx == 0) ? `hide` : ``;
+ let can_remove = (idx == 0 || is_status_field(me.fields[idx])) ? `hide` : ``;
fields += `
+ data-label="${me.fields[idx].label}" data-type="${me.fields[idx].type}">
-
+
${me.fields[idx].label}
@@ -122,7 +144,7 @@ export default class ListSettings {
`);
- new Sortable($wrapper.getElementsByClassName("control-input-wrapper")[0], {
+ new Sortable(wrapper.getElementsByClassName("control-input-wrapper")[0], {
handle: '.sortable-handle',
draggable: '.sortable',
onUpdate: () => {
@@ -172,9 +194,9 @@ export default class ListSettings {
let me = this;
let fields_html = me.dialog.get_field("fields_html");
- let $wrapper = fields_html.$wrapper[0];
+ let wrapper = fields_html.$wrapper[0];
- let fields_order = $wrapper.getElementsByClassName("fields_order");
+ let fields_order = wrapper.getElementsByClassName("fields_order");
me.fields = [];
for (let idx = 0; idx < fields_order.length; idx++) {
@@ -185,6 +207,7 @@ export default class ListSettings {
}
me.dialog.set_value("fields", JSON.stringify(me.fields));
+ me.dialog.get_value("fields");
}
column_selector() {
@@ -215,6 +238,7 @@ export default class ListSettings {
me.fields = [];
me.set_subject_field(me.meta);
+ me.set_status_field();
for (let idx in values) {
let value = values[idx];
@@ -266,6 +290,7 @@ export default class ListSettings {
let me = this;
me.set_subject_field(meta);
+ me.set_status_field();
meta.fields.forEach(field => {
if (field.in_list_view && !in_list(frappe.model.no_value_type, field.fieldtype) &&
@@ -299,6 +324,18 @@ export default class ListSettings {
me.fields.push(me.subject_field);
}
+ set_status_field() {
+ let me = this;
+
+ if (frappe.has_indicator(me.doctype)) {
+ me.fields.push({
+ type: "Status",
+ label: "Status",
+ fieldname: "status_field"
+ })
+ }
+ }
+
get_doctype_fields(meta, fields) {
let me = this;
let multiselect_fields = []
@@ -320,6 +357,10 @@ export default class ListSettings {
let me = this;
let removed_fields = []
+ if (frappe.has_indicator(me.doctype)) {
+ new_fields.push("status_field");
+ }
+
existing_fields.forEach(column => {
if (!in_list(new_fields, column)) {
removed_fields.push(column);
diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js
index b784e7ba92..98d21f905d 100644
--- a/frappe/public/js/frappe/list/list_view.js
+++ b/frappe/public/js/frappe/list/list_view.js
@@ -315,31 +315,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
);
if (this.list_view_settings.fields) {
- let fields_order = [];
- let fields = JSON.parse(this.list_view_settings.fields);
- let is_status_field_set = false;
-
- //title_field is fixed
- fields_order.push(this.columns[0]);
- this.columns.splice(0, 1);
-
- for (let fld in fields) {
- for (let col in this.columns) {
- let field = fields[fld];
- let column = this.columns[col];
-
- if (column.type == "Status" && !is_status_field_set) {
- fields_order.push(column);
- is_status_field_set = true;
- break;
- } else if (column.type == "Field" && field.fieldname === column.df.fieldname) {
- fields_order.push(column);
- break;
- }
- }
- }
-
- this.columns = fields_order;
+ this.columns = this.reorder_listview_fields();
}
// limit max to 8 columns if no total_fields is set in List View Settings
@@ -355,7 +331,32 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
}
this.columns = this.columns.slice(0, this.list_view_settings.total_fields || total_fields);
- console.log(this.columns);
+ }
+
+ reorder_listview_fields() {
+ let fields_order = [];
+ let fields = JSON.parse(this.list_view_settings.fields);
+
+ //title_field is fixed
+ fields_order.push(this.columns[0]);
+ this.columns.splice(0, 1);
+
+ for (let fld in fields) {
+ for (let col in this.columns) {
+ let field = fields[fld];
+ let column = this.columns[col];
+
+ if (column.type == "Status" && field.fieldname == "status_field") {
+ fields_order.push(column);
+ break;
+ } else if (column.type == "Field" && field.fieldname === column.df.fieldname) {
+ fields_order.push(column);
+ break;
+ }
+ }
+ }
+
+ return fields_order;
}
get_documentation_link() {
From c4320fb22f71e12539d71f0f6374aa3aa9d99e8b Mon Sep 17 00:00:00 2001
From: Himanshu
Date: Tue, 5 May 2020 16:05:23 +0530
Subject: [PATCH 035/572] Update frappe/public/js/frappe/list/list_settings.js
Co-authored-by: Prssanna Desai
---
frappe/public/js/frappe/list/list_settings.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/frappe/public/js/frappe/list/list_settings.js b/frappe/public/js/frappe/list/list_settings.js
index a2eaaa6689..e1978cc721 100644
--- a/frappe/public/js/frappe/list/list_settings.js
+++ b/frappe/public/js/frappe/list/list_settings.js
@@ -97,7 +97,7 @@ export default class ListSettings {
let fields_html = me.dialog.get_field("fields_html");
let wrapper = fields_html.$wrapper[0];
let fields = ``;
- let total_fields = me.dialog.get_values().total_fields ? me.dialog.get_values().total_fields : me.settings.total_fields;
+ let total_fields = me.dialog.get_values().total_fields || me.settings.total_fields;
for (let idx in me.fields) {
if (idx == parseInt(total_fields)) {
@@ -379,4 +379,4 @@ export default class ListSettings {
me.removed_fields = fields;
}
}
-}
\ No newline at end of file
+}
From 606c57be5d849335513025d6ef12e1ab50613c1b Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Tue, 5 May 2020 17:35:26 +0530
Subject: [PATCH 036/572] fix: error message
---
frappe/desk/form/assign_to.py | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/frappe/desk/form/assign_to.py b/frappe/desk/form/assign_to.py
index be83dc957e..6178690b1f 100644
--- a/frappe/desk/form/assign_to.py
+++ b/frappe/desk/form/assign_to.py
@@ -41,6 +41,9 @@ def add(args=None):
if not args:
args = frappe.local.form_dict
+ users_with_duplicate_todo = []
+ shared_with_users = []
+
for assign_to in json.loads(args.get("assign_to")):
filters = {
"reference_type": args['doctype'],
@@ -50,8 +53,7 @@ def add(args=None):
}
if frappe.get_all("ToDo", filters=filters):
- if not args.get("bulk_assign"):
- frappe.throw(_("Already in user's ToDo list"), DuplicateToDoError)
+ users_with_duplicate_todo.append(assign_to)
else:
from frappe.utils import nowdate
@@ -80,7 +82,7 @@ def add(args=None):
# if assignee does not have permissions, share
if not frappe.has_permission(doc=doc, user=assign_to):
frappe.share.add(doc.doctype, doc.name, assign_to)
- frappe.msgprint(_('Shared with user {0} with read access').format(assign_to, alert=True))
+ shared_with_users.append(assign_to)
# make this document followed by assigned user
follow_document(args['doctype'], args['name'], assign_to)
@@ -89,6 +91,14 @@ def add(args=None):
notify_assignment(d.assigned_by, d.owner, d.reference_type, d.reference_name, action='ASSIGN',
description=args.get("description"))
+ if shared_with_users:
+ user_list = format_message_for_assign_to(shared_with_users)
+ frappe.msgprint(_("Shared with the following Users with Read access:{0}").format(user_list, alert=True))
+
+ if users_with_duplicate_todo:
+ user_list = format_message_for_assign_to(users_with_duplicate_todo)
+ frappe.msgprint(_("Already in the following Users ToDo list:{0}").format(user_list, alert=True))
+
return get(args)
@frappe.whitelist()
@@ -186,3 +196,5 @@ def notify_assignment(assigned_by, owner, doc_type, doc_name, action='CLOSE',
enqueue_create_notification(owner, notification_doc)
+def format_message_for_assign_to(users):
+ return "
" + "
".join(users)
\ No newline at end of file
From f684756fba34fcd5ac1099ea7938f64fe08e574d Mon Sep 17 00:00:00 2001
From: Shivam Mishra
Date: Tue, 5 May 2020 18:22:04 +0530
Subject: [PATCH 037/572] feat: added website analytics report
---
.../website_analytics/website_analytics.js | 2 -
.../website_analytics/website_analytics.py | 116 +++++++++++++++---
2 files changed, 98 insertions(+), 20 deletions(-)
diff --git a/frappe/website/report/website_analytics/website_analytics.js b/frappe/website/report/website_analytics/website_analytics.js
index b607a16eb4..7e051afa8c 100644
--- a/frappe/website/report/website_analytics/website_analytics.js
+++ b/frappe/website/report/website_analytics/website_analytics.js
@@ -9,14 +9,12 @@ frappe.query_reports["Website Analytics"] = {
label: __("From Date"),
fieldtype: "Date",
default: frappe.datetime.add_days(frappe.datetime.now_date(true), -7),
- reqd: 1
},
{
fieldname:"to_date",
label: __("To Date"),
fieldtype: "Date",
default: frappe.datetime.now_date(true),
- reqd: 1
},
{
fieldname: "range",
diff --git a/frappe/website/report/website_analytics/website_analytics.py b/frappe/website/report/website_analytics/website_analytics.py
index b821cbff1f..694bc9e797 100644
--- a/frappe/website/report/website_analytics/website_analytics.py
+++ b/frappe/website/report/website_analytics/website_analytics.py
@@ -3,6 +3,7 @@
from __future__ import unicode_literals
import frappe
+from datetime import datetime
from frappe.utils.dateutils import get_date_range
def execute(filters=None):
@@ -11,6 +12,16 @@ def execute(filters=None):
class WebsiteAnalytics(object):
def __init__(self, filters=None):
self.filters = frappe._dict(filters or {})
+
+ if not self.filters.to_date:
+ self.filters.to_date = datetime.now()
+
+ if not self.filters.from_date:
+ self.filters.from_date = frappe.utils.add_days(self.filters.to_date, -7)
+
+ if not self.filters.range:
+ self.filters.range = "D"
+
self.filters.to_date = frappe.utils.add_days(self.filters.to_date, 1)
self.query_filters = {'creation': ['between', [self.filters.from_date, self.filters.to_date]]}
@@ -19,7 +30,8 @@ class WebsiteAnalytics(object):
data = self.get_data()
chart = self.get_chart_data()
summary = self.get_report_summary()
- return columns, data, None, chart, summary
+
+ return columns, data[:250], None, chart, summary
def get_columns(self):
return [
@@ -34,38 +46,105 @@ class WebsiteAnalytics(object):
"label": "Page Views",
"fieldtype": "Int",
"width": 150
+ },
+ {
+ "fieldname": "unique_count",
+ "label": "Unique Visitors",
+ "fieldtype": "Int",
+ "width": 150
}
]
def get_data(self):
- data = frappe.get_all("Web Page View", fields=['path', 'count(*) as count'], filters=self.query_filters, group_by="path", order_by='count desc')
+ pg_query = """
+ SELECT
+ path,
+ COUNT(*) as count,
+ COUNT(CASE WHEN CAST(is_unique as Integer) = 1 THEN 1 END) as unique_count
+ FROM `tabWeb Page View`
+ WHERE coalesce("tabWeb Page View".creation, '0001-01-01') BETWEEN %s AND %s
+ GROUP BY path
+ ORDER BY count desc
+ """
+
+ mariadb_query = """
+ SELECT
+ path,
+ COUNT(*) as count,
+ COUNT(CASE WHEN is_unique = 1 THEN 1 END) as unique_count
+ FROM `tabWeb Page View`
+ WHERE creation BETWEEN %s AND %s
+ GROUP BY path
+ ORDER BY count desc
+ """
+
+ data = frappe.db.multisql({
+ "mariadb": mariadb_query,
+ "postgres": pg_query
+ }, (self.filters.from_date, self.filters.to_date))
return data
- def get_chart_data(self):
- def _get_field_for_chart(filters_range):
- field = 'creation'
- date_format = '%Y-%m-%d'
+ def _get_query_for_mariadb(self):
+ filters_range = self.filters.range
+ field = 'creation'
+ date_format = '%Y-%m-%d'
- if filters_range == "W":
- field = 'ADDDATE(creation, INTERVAL 1-DAYOFWEEK(creation) DAY)'
+ if filters_range == "W":
+ field = 'ADDDATE(creation, INTERVAL 1-DAYOFWEEK(creation) DAY)'
- elif filters_range == "M":
- date_format = '%Y-%m-01'
+ elif filters_range == "M":
+ date_format = '%Y-%m-01'
- return field, date_format
-
- field, date_format = _get_field_for_chart(self.filters.range)
-
- self.chart_data = frappe.db.sql("""
+ query = """
SELECT
DATE_FORMAT({0}, %s) as date,
COUNT(*) as count,
- count(CASE WHEN is_unique = 1 THEN 1 END) as unique_count
+ COUNT(CASE WHEN is_unique = 1 THEN 1 END) as unique_count
FROM `tabWeb Page View`
WHERE creation BETWEEN %s AND %s
GROUP BY DATE_FORMAT({0}, %s)
ORDER BY creation
- """.format(field), (date_format, self.filters.from_date, self.filters.to_date, date_format), as_dict=1)
+ """.format(field)
+
+ values = (date_format, self.filters.from_date, self.filters.to_date, date_format)
+
+ return query, values
+
+ def _get_query_for_postgres(self):
+ filters_range = self.filters.range
+ field = 'creation'
+ granularity = 'day'
+
+ if filters_range == "W":
+ granularity = 'week'
+
+ elif filters_range == "M":
+ granularity = 'day'
+
+ query = """
+ SELECT
+ DATE_TRUNC(%s, {0}) as date,
+ COUNT(*) as count,
+ COUNT(CASE WHEN CAST(is_unique as Integer) = 1 THEN 1 END) as unique_count
+ FROM "tabWeb Page View"
+ WHERE coalesce("tabWeb Page View".{0}, '0001-01-01') BETWEEN %s AND %s
+ GROUP BY date_trunc(%s, {0})
+ ORDER BY date
+ """.format(field)
+
+ values = (granularity, self.filters.from_date, self.filters.to_date, granularity)
+
+ return query, values
+
+ def get_chart_data(self):
+ current_dialect = frappe.db.db_type or 'mariadb'
+
+ if current_dialect == 'mariadb':
+ query, values = self._get_query_for_mariadb()
+ else:
+ query, values = self._get_query_for_postgres()
+
+ self.chart_data = frappe.db.sql(query, values=values, as_dict=1)
return self.prepare_chart_data(self.chart_data)
@@ -114,7 +193,8 @@ class WebsiteAnalytics(object):
},
'axisOptions': {
'xIsSeries': 1
- }
+ },
+ 'colors': ['#7cd6fd', '#5e64ff']
}
return chart
From 83380f25475edfb3bec8d79cad7013d78443b9b4 Mon Sep 17 00:00:00 2001
From: Shivam Mishra
Date: Tue, 5 May 2020 18:22:47 +0530
Subject: [PATCH 038/572] feat: added website analytics chart as JSON fixture
---
frappe/model/sync.py | 5 ++--
.../website_analytics/website_analytics.json | 24 +++++++++++++++++++
2 files changed, 27 insertions(+), 2 deletions(-)
create mode 100644 frappe/website/dashboard_chart/website_analytics/website_analytics.json
diff --git a/frappe/model/sync.py b/frappe/model/sync.py
index c2acb59f63..3dba994134 100644
--- a/frappe/model/sync.py
+++ b/frappe/model/sync.py
@@ -52,7 +52,8 @@ def sync_for(app_name, force=0, sync_everything = False, verbose=False, reset_pe
("desk", "desk_card"),
("desk", "desk_chart"),
("desk", "desk_shortcut"),
- ("desk", "desk_page")):
+ ("desk", "desk_page"),
+ ("desk", "dashboard_chart")):
files.append(os.path.join(frappe.get_app_path("frappe"), d[0],
"doctype", d[1], d[1] + ".json"))
@@ -82,7 +83,7 @@ def get_doc_files(files, start_path, force=0, sync_everything = False, verbose=F
document_types = ['doctype', 'page', 'report', 'dashboard_chart_source', 'print_format',
'website_theme', 'web_form', 'web_template', 'notification', 'print_style',
'data_migration_mapping', 'data_migration_plan', 'desk_page',
- 'onboarding_step', 'onboarding']
+ 'onboarding_step', 'onboarding', 'dashboard_chart']
for doctype in document_types:
doctype_path = os.path.join(start_path, doctype)
diff --git a/frappe/website/dashboard_chart/website_analytics/website_analytics.json b/frappe/website/dashboard_chart/website_analytics/website_analytics.json
new file mode 100644
index 0000000000..eeeb1a11f9
--- /dev/null
+++ b/frappe/website/dashboard_chart/website_analytics/website_analytics.json
@@ -0,0 +1,24 @@
+{
+ "chart_name": "Website Analytics",
+ "chart_type": "Report",
+ "creation": "2020-05-05 18:14:19.369181",
+ "custom_options": "{\"type\": \"line\", \"lineOptions\": {\"regionFill\": 1}, \"axisOptions\": {\"shortenYAxisNumbers\": 1}, \"colors\": [\"#7cd6fd\", \"#5e64ff\"], \"tooltipOptions\": {}}",
+ "docstatus": 0,
+ "doctype": "Dashboard Chart",
+ "filters_json": "{}",
+ "group_by_type": "Count",
+ "idx": 0,
+ "is_custom": 1,
+ "is_public": 1,
+ "modified": "2020-05-05 18:16:47.383649",
+ "modified_by": "Administrator",
+ "name": "Website Analytics",
+ "number_of_groups": 0,
+ "owner": "Administrator",
+ "report_name": "Website Analytics",
+ "time_interval": "Yearly",
+ "timeseries": 0,
+ "timespan": "Last Year",
+ "type": "Line",
+ "y_axis": []
+}
\ No newline at end of file
From 06e4e6a3b933b28e29bcd7e01bf1027939a236a1 Mon Sep 17 00:00:00 2001
From: Shivam Mishra
Date: Tue, 5 May 2020 18:29:42 +0530
Subject: [PATCH 039/572] feat: add analytics chart to desk
---
frappe/website/desk_page/website/website.json | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/frappe/website/desk_page/website/website.json b/frappe/website/desk_page/website/website.json
index 1c6066d21e..c42a17d404 100644
--- a/frappe/website/desk_page/website/website.json
+++ b/frappe/website/desk_page/website/website.json
@@ -27,7 +27,11 @@
}
],
"category": "Modules",
- "charts": [],
+ "charts": [
+ {
+ "chart_name": "Website Analytics"
+ }
+ ],
"creation": "2020-03-02 14:13:51.089373",
"developer_mode_only": 0,
"disable_user_customization": 0,
@@ -37,7 +41,7 @@
"idx": 0,
"is_standard": 1,
"label": "Website",
- "modified": "2020-04-26 13:03:49.094728",
+ "modified": "2020-05-05 18:17:13.232473",
"modified_by": "Administrator",
"module": "Website",
"name": "Website",
From 627940c56b7fa435726d79f355ca8d96fd329d57 Mon Sep 17 00:00:00 2001
From: Gavin D'souza
Date: Tue, 5 May 2020 18:58:38 +0530
Subject: [PATCH 040/572] feat: site wise logs under
sites/{sitename}/logs/frappe.log
---
frappe/commands/site.py | 2 +-
frappe/utils/logger.py | 31 +++++++++++++++++++++++++------
2 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/frappe/commands/site.py b/frappe/commands/site.py
index 52994ccec3..362d02c0d2 100755
--- a/frappe/commands/site.py
+++ b/frappe/commands/site.py
@@ -409,7 +409,7 @@ def _drop_site(site, root_login='root', root_password=None, archived_sites_path=
else:
click.echo("="*80)
click.echo("Error: The operation has stopped because backup of {s}'s database failed.".format(s=site))
- click.echo("Reason: {reason}{sep}".format(reason=err[1], sep="\n"))
+ click.echo("Reason: {reason}{sep}".format(reason=str(err), sep="\n"))
click.echo("Fix the issue and try again.")
click.echo(
"Hint: Use 'bench drop-site {s} --force' to force the removal of {s}".format(sep="\n", tab="\t", s=site)
diff --git a/frappe/utils/logger.py b/frappe/utils/logger.py
index 5a77434cde..e446b39ee0 100755
--- a/frappe/utils/logger.py
+++ b/frappe/utils/logger.py
@@ -1,23 +1,42 @@
+# imports - compatibility imports
from __future__ import unicode_literals
-import frappe
+
+# imports - standard imports
import logging
+import os
from logging.handlers import RotatingFileHandler
+
+# imports - third party imports
from six import text_type
+# imports - module imports
+import frappe
+
+
default_log_level = logging.DEBUG
-LOG_FILENAME = '../logs/{}-frappe.log'.format(frappe.local.site)
+site = getattr(frappe.local, 'site', None)
+LOG_FILENAME = os.path.join('..', 'logs', 'frappe.log')
+
def get_logger(module, with_more_info=True):
if module in frappe.loggers:
return frappe.loggers[module]
formatter = logging.Formatter('[%(levelname)s] %(asctime)s | %(pathname)s:\n%(message)s')
- # handler = logging.StreamHandler()
- handler = RotatingFileHandler(
- LOG_FILENAME, maxBytes=100000, backupCount=20)
+ handler = RotatingFileHandler(LOG_FILENAME, maxBytes=100_000, backupCount=20)
handler.setFormatter(formatter)
+ if site:
+ SITELOG_FOLDER = os.path.join(site, 'logs')
+ SITELOG_FILENAME = os.path.join(SITELOG_FOLDER, 'frappe.log')
+
+ if not os.path.exists(SITELOG_FOLDER):
+ os.mkdir(SITELOG_FOLDER)
+
+ handler = RotatingFileHandler(SITELOG_FILENAME, maxBytes=100_000, backupCount=20)
+ handler.setFormatter(formatter)
+
if with_more_info:
handler.addFilter(SiteContextFilter())
@@ -39,7 +58,7 @@ class SiteContextFilter(logging.Filter):
def get_more_info_for_log():
'''Adds Site, Form Dict into log entry'''
more_info = []
- site = getattr(frappe.local, 'site', None)
+
if site:
more_info.append('Site: {0}'.format(site))
From ca70bd364cc125be533ec63bcdb2f87dc71096e7 Mon Sep 17 00:00:00 2001
From: Gavin D'souza
Date: Tue, 5 May 2020 18:59:38 +0530
Subject: [PATCH 041/572] fix: create sites/{site}/logs folder
---
frappe/installer.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/frappe/installer.py b/frappe/installer.py
index 54402f0087..565fc9d36b 100755
--- a/frappe/installer.py
+++ b/frappe/installer.py
@@ -269,6 +269,7 @@ def make_site_dirs():
os.path.join(site_private_path, 'backups'),
os.path.join(site_public_path, 'files'),
os.path.join(site_private_path, 'files'),
+ os.path.join(frappe.local.site_path, 'logs'),
os.path.join(frappe.local.site_path, 'task-logs')):
if not os.path.exists(dir_path):
os.makedirs(dir_path)
From 87f2c66e0f3b13359b94819017c3252033089a4d Mon Sep 17 00:00:00 2001
From: Rucha Mahabal
Date: Wed, 6 May 2020 00:09:55 +0530
Subject: [PATCH 042/572] feat: options to hide days and seconds for duration
control
---
.../js/frappe/form/controls/duration.js | 21 ++++++++++---------
frappe/public/js/frappe/utils/utils.js | 10 ++++++---
frappe/public/less/controls.less | 2 +-
3 files changed, 19 insertions(+), 14 deletions(-)
diff --git a/frappe/public/js/frappe/form/controls/duration.js b/frappe/public/js/frappe/form/controls/duration.js
index 86dee3866f..49c1e46530 100644
--- a/frappe/public/js/frappe/form/controls/duration.js
+++ b/frappe/public/js/frappe/form/controls/duration.js
@@ -50,20 +50,16 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
},
set_duration_options() {
- let lang = 'en';
- frappe.boot.user && (lang = frappe.boot.user.language);
-
this.duration_options = {
- lang: lang,
- max: 59,
- checkRanges: false,
showSeconds: true,
showDays: true,
};
},
set_duration_picker() {
- let total_duration = frappe.utils.seconds_to_duration(this.value);
+ let total_duration = frappe.utils.seconds_to_duration(this.value,
+ this.duration_options.showDays, this.set_duration_options.showSeconds);
+
if (total_duration.days) {
this.$picker.find(`[data-duration='days']`).prop('value', total_duration.days);
}
@@ -115,18 +111,23 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
},
format_for_input: function(value) {
- return frappe.utils.get_formatted_duration(value);
+ return frappe.utils.get_formatted_duration(value,
+ this.duration_options.showDays, this.duration_options.showSeconds);
},
duration_to_seconds() {
let value = 0;
if (this.inputs) {
let total_duration = {
- seconds : parseInt(this.inputs.secs.val()),
minutes : parseInt(this.inputs.mins.val()),
hours : parseInt(this.inputs.hrs.val()),
- days : parseInt(this.inputs.days.val())
};
+ if (this.duration_options.showDays) {
+ total_duration.days = parseInt(this.inputs.days.val());
+ }
+ if (this.duration_options.showSeconds) {
+ total_duration.seconds = parseInt(this.inputs.secs.val());
+ }
if (total_duration.days) {
value += total_duration.days * 24 * 60 * 60;
diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js
index 2584248711..559598de5b 100644
--- a/frappe/public/js/frappe/utils/utils.js
+++ b/frappe/public/js/frappe/utils/utils.js
@@ -779,10 +779,10 @@ Object.assign(frappe.utils, {
};
},
- get_formatted_duration(value) {
+ get_formatted_duration(value, showDays=true, showSeconds=true) {
let duration = '';
if (value) {
- let total_duration = frappe.utils.seconds_to_duration(value);
+ let total_duration = frappe.utils.seconds_to_duration(value, showDays, showSeconds);
if (total_duration.days) {
duration += total_duration.days + 'd';
@@ -802,7 +802,7 @@ Object.assign(frappe.utils, {
}
return duration;
},
- seconds_to_duration(value) {
+ seconds_to_duration(value, showDays=true, showSeconds=true) {
let secs = value;
let total_duration = {
days: Math.floor(secs / (3600 * 24)),
@@ -810,6 +810,10 @@ Object.assign(frappe.utils, {
minutes: Math.floor(secs % 3600 / 60),
seconds : Math.floor(secs % 60)
};
+ if (!showDays) {
+ total_duration.hours = Math.floor(secs / 3600)
+ total_duration.days = 0
+ }
return total_duration;
},
});
diff --git a/frappe/public/less/controls.less b/frappe/public/less/controls.less
index ac2936b149..b6ac3cf359 100644
--- a/frappe/public/less/controls.less
+++ b/frappe/public/less/controls.less
@@ -230,8 +230,8 @@
.picker-row {
display: flex;
- width: 335px;
margin-left: 5px;
margin-bottom: 3px;
+ margin-right: 12px;
}
}
\ No newline at end of file
From 156a00724fd6adbeb65471e99d4fb0744c6e7c3f Mon Sep 17 00:00:00 2001
From: Rucha Mahabal
Date: Wed, 6 May 2020 09:39:57 +0530
Subject: [PATCH 043/572] feat: add duration options to Duration docfield
---
frappe/core/doctype/docfield/docfield.json | 18 +++++++++++++++++-
.../public/js/frappe/form/controls/duration.js | 11 +++--------
frappe/public/js/frappe/form/formatters.js | 5 +++--
frappe/public/js/frappe/model/meta.js | 8 ++++++++
frappe/public/js/frappe/utils/utils.js | 8 ++++----
5 files changed, 35 insertions(+), 15 deletions(-)
diff --git a/frappe/core/doctype/docfield/docfield.json b/frappe/core/doctype/docfield/docfield.json
index 04f31375d6..8ae2b740b5 100644
--- a/frappe/core/doctype/docfield/docfield.json
+++ b/frappe/core/doctype/docfield/docfield.json
@@ -13,6 +13,8 @@
"fieldname",
"precision",
"length",
+ "show_days",
+ "show_seconds",
"reqd",
"search_index",
"in_list_view",
@@ -448,12 +450,26 @@
{
"fieldname": "column_break_38",
"fieldtype": "Column Break"
+ },
+ {
+ "default": "1",
+ "depends_on": "eval:doc.fieldtype === \"Duration\";",
+ "fieldname": "show_days",
+ "fieldtype": "Check",
+ "label": "Show Days"
+ },
+ {
+ "default": "1",
+ "depends_on": "eval:doc.fieldtype === \"Duration\";",
+ "fieldname": "show_seconds",
+ "fieldtype": "Check",
+ "label": "Show Seconds"
}
],
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2020-04-30 09:00:38.012601",
+ "modified": "2020-05-06 09:06:25.224411",
"modified_by": "Administrator",
"module": "Core",
"name": "DocField",
diff --git a/frappe/public/js/frappe/form/controls/duration.js b/frappe/public/js/frappe/form/controls/duration.js
index 49c1e46530..de8aa96c95 100644
--- a/frappe/public/js/frappe/form/controls/duration.js
+++ b/frappe/public/js/frappe/form/controls/duration.js
@@ -50,15 +50,11 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
},
set_duration_options() {
- this.duration_options = {
- showSeconds: true,
- showDays: true,
- };
+ this.duration_options = frappe.meta.get_duration_options(this.df, this.get_doc());
},
set_duration_picker() {
- let total_duration = frappe.utils.seconds_to_duration(this.value,
- this.duration_options.showDays, this.set_duration_options.showSeconds);
+ let total_duration = frappe.utils.seconds_to_duration(this.value, this.duration_options);
if (total_duration.days) {
this.$picker.find(`[data-duration='days']`).prop('value', total_duration.days);
@@ -111,8 +107,7 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
},
format_for_input: function(value) {
- return frappe.utils.get_formatted_duration(value,
- this.duration_options.showDays, this.duration_options.showSeconds);
+ return frappe.utils.get_formatted_duration(value, this.duration_options);
},
duration_to_seconds() {
diff --git a/frappe/public/js/frappe/form/formatters.js b/frappe/public/js/frappe/form/formatters.js
index cc24c48a1f..a9abd1c601 100644
--- a/frappe/public/js/frappe/form/formatters.js
+++ b/frappe/public/js/frappe/form/formatters.js
@@ -188,9 +188,10 @@ frappe.form.formatters = {
return value || "";
},
- Duration: function(value) {
+ Duration: function(value, docfield, doc) {
if (value) {
- value = frappe.utils.get_formatted_duration(value);
+ let duration_options = frappe.meta.get_duration_options(docfield, doc);
+ value = frappe.utils.get_formatted_duration(value, duration_options);
}
return value || "";
diff --git a/frappe/public/js/frappe/model/meta.js b/frappe/public/js/frappe/model/meta.js
index b7ad52838c..acde2a367f 100644
--- a/frappe/public/js/frappe/model/meta.js
+++ b/frappe/public/js/frappe/model/meta.js
@@ -267,4 +267,12 @@ $.extend(frappe.meta, {
}
return precision;
},
+
+ get_duration_options: function(df, doc) {
+ let duration_options = {
+ showDays: df.show_days,
+ showSeconds: df.show_seconds
+ };
+ return duration_options;
+ }
});
diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js
index 559598de5b..9281ba6c43 100644
--- a/frappe/public/js/frappe/utils/utils.js
+++ b/frappe/public/js/frappe/utils/utils.js
@@ -779,10 +779,10 @@ Object.assign(frappe.utils, {
};
},
- get_formatted_duration(value, showDays=true, showSeconds=true) {
+ get_formatted_duration(value, duration_options) {
let duration = '';
if (value) {
- let total_duration = frappe.utils.seconds_to_duration(value, showDays, showSeconds);
+ let total_duration = frappe.utils.seconds_to_duration(value, duration_options);
if (total_duration.days) {
duration += total_duration.days + 'd';
@@ -802,7 +802,7 @@ Object.assign(frappe.utils, {
}
return duration;
},
- seconds_to_duration(value, showDays=true, showSeconds=true) {
+ seconds_to_duration(value, duration_options) {
let secs = value;
let total_duration = {
days: Math.floor(secs / (3600 * 24)),
@@ -810,7 +810,7 @@ Object.assign(frappe.utils, {
minutes: Math.floor(secs % 3600 / 60),
seconds : Math.floor(secs % 60)
};
- if (!showDays) {
+ if (!duration_options.showDays) {
total_duration.hours = Math.floor(secs / 3600)
total_duration.days = 0
}
From 94116d322784098f2a215ceb134efd1a24e73589 Mon Sep 17 00:00:00 2001
From: Rucha Mahabal
Date: Wed, 6 May 2020 09:51:16 +0530
Subject: [PATCH 044/572] fix: duration options for filters
---
frappe/public/js/frappe/ui/filters/filters.js | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/frappe/public/js/frappe/ui/filters/filters.js b/frappe/public/js/frappe/ui/filters/filters.js
index 94e2b59d22..9a394eade3 100644
--- a/frappe/public/js/frappe/ui/filters/filters.js
+++ b/frappe/public/js/frappe/ui/filters/filters.js
@@ -201,7 +201,11 @@ frappe.ui.FilterList = Class.extend({
} else if(field.df.original_type==="Check") {
value = {0:"No", 1:"Yes"}[cint(value)];
} else if (field.df.original_type === "Duration") {
- value = frappe.utils.get_formatted_duration(value);
+ let duration_options = {
+ showDays: field.df.show_days,
+ showSeconds: field.df.show_seconds
+ };
+ value = frappe.utils.get_formatted_duration(value, duration_options);
}
value = frappe.format(value, field.df, {only_value: 1});
From b696306edc65bb96a4ed450451c26ca61937b6c9 Mon Sep 17 00:00:00 2001
From: Rucha Mahabal
Date: Wed, 6 May 2020 10:15:11 +0530
Subject: [PATCH 045/572] fix: set duration options in refresh_input
---
.../js/frappe/form/controls/duration.js | 25 ++++++++++---------
1 file changed, 13 insertions(+), 12 deletions(-)
diff --git a/frappe/public/js/frappe/form/controls/duration.js b/frappe/public/js/frappe/form/controls/duration.js
index de8aa96c95..c177ae6f17 100644
--- a/frappe/public/js/frappe/form/controls/duration.js
+++ b/frappe/public/js/frappe/form/controls/duration.js
@@ -13,10 +13,10 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
`
);
this.$wrapper.append(this.$picker);
- this.build_numeric_input('days', !this.duration_options.showDays);
- this.build_numeric_input('hrs', false);
- this.build_numeric_input('mins', false);
- this.build_numeric_input('secs', !this.duration_options.showSeconds);
+ this.build_numeric_input("days", !this.duration_options.showDays);
+ this.build_numeric_input("hrs", false);
+ this.build_numeric_input("mins", false);
+ this.build_numeric_input("secs", !this.duration_options.showSeconds);
this.set_duration_picker();
this.$picker.hide();
this.bind_events();
@@ -31,7 +31,7 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
let $input = $(`
`).prepend($duration_input);
if (max) {
- $duration_input.attr('max', max);
+ $duration_input.attr("max", max);
}
this.inputs[label] = $duration_input;
@@ -43,10 +43,10 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
)
if (hidden) {
- $control.addClass('hidden');
+ $control.addClass("hidden");
}
$control.prepend($input);
- $control.appendTo(this.$picker.find('.picker-row'));
+ $control.appendTo(this.$picker.find(".picker-row"));
},
set_duration_options() {
@@ -57,16 +57,16 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
let total_duration = frappe.utils.seconds_to_duration(this.value, this.duration_options);
if (total_duration.days) {
- this.$picker.find(`[data-duration='days']`).prop('value', total_duration.days);
+ this.$picker.find(`[data-duration="days"]`).prop("value", total_duration.days);
}
if (total_duration.hours) {
- this.$picker.find(`[data-duration='hrs']`).prop('value', total_duration.hours);
+ this.$picker.find(`[data-duration="hrs"]`).prop("value", total_duration.hours);
}
if (total_duration.minutes) {
- this.$picker.find(`[data-duration='mins']`).prop('value', total_duration.minutes);
+ this.$picker.find(`[data-duration="mins"]`).prop("value", total_duration.minutes);
}
if (total_duration.seconds) {
- this.$picker.find(`[data-duration='secs']`).prop('value', total_duration.seconds);
+ this.$picker.find(`[data-duration="secs"]`).prop("value", total_duration.seconds);
}
},
@@ -74,7 +74,7 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
let me = this;
let clicked = false;
- this.$picker.on('change', '.duration-input', () => {
+ this.$picker.on("change", ".duration-input", () => {
clicked = false;
me.set_value(me.duration_to_seconds());
me.set_focus();
@@ -103,6 +103,7 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
refresh_input: function() {
this._super();
+ this.set_duration_options();
this.set_duration_picker();
},
From 972833d13fb20deebe95acb47d0495baed655831 Mon Sep 17 00:00:00 2001
From: Rucha Mahabal
Date: Wed, 6 May 2020 12:36:22 +0530
Subject: [PATCH 046/572] fix: change duration fieldtype to decimal
---
frappe/database/mariadb/database.py | 2 +-
frappe/database/postgres/database.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/frappe/database/mariadb/database.py b/frappe/database/mariadb/database.py
index 7350e0aaef..4ec89c126d 100644
--- a/frappe/database/mariadb/database.py
+++ b/frappe/database/mariadb/database.py
@@ -56,7 +56,7 @@ class MariaDBDatabase(Database):
'Color': ('varchar', self.VARCHAR_LEN),
'Barcode': ('longtext', ''),
'Geolocation': ('longtext', ''),
- 'Duration': ('bigint', '20')
+ 'Duration': ('decimal', '18,6')
}
def get_connection(self):
diff --git a/frappe/database/postgres/database.py b/frappe/database/postgres/database.py
index 78dc6d42ec..e348916705 100644
--- a/frappe/database/postgres/database.py
+++ b/frappe/database/postgres/database.py
@@ -61,7 +61,7 @@ class PostgresDatabase(Database):
'Color': ('varchar', self.VARCHAR_LEN),
'Barcode': ('text', ''),
'Geolocation': ('text', ''),
- 'Duration': ('bigint', '20')
+ 'Duration': ('decimal', '18,6')
}
def get_connection(self):
From 18ce053a7475759f378038bb7017099c6db989a1 Mon Sep 17 00:00:00 2001
From: Rucha Mahabal
Date: Wed, 6 May 2020 13:39:40 +0530
Subject: [PATCH 047/572] fix: refresh input
---
.../js/frappe/form/controls/duration.js | 24 ++++++++++---------
1 file changed, 13 insertions(+), 11 deletions(-)
diff --git a/frappe/public/js/frappe/form/controls/duration.js b/frappe/public/js/frappe/form/controls/duration.js
index c177ae6f17..c764c9b041 100644
--- a/frappe/public/js/frappe/form/controls/duration.js
+++ b/frappe/public/js/frappe/form/controls/duration.js
@@ -56,17 +56,19 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({
set_duration_picker() {
let total_duration = frappe.utils.seconds_to_duration(this.value, this.duration_options);
- if (total_duration.days) {
- this.$picker.find(`[data-duration="days"]`).prop("value", total_duration.days);
- }
- if (total_duration.hours) {
- this.$picker.find(`[data-duration="hrs"]`).prop("value", total_duration.hours);
- }
- if (total_duration.minutes) {
- this.$picker.find(`[data-duration="mins"]`).prop("value", total_duration.minutes);
- }
- if (total_duration.seconds) {
- this.$picker.find(`[data-duration="secs"]`).prop("value", total_duration.seconds);
+ if (this.$picker) {
+ if (total_duration.days) {
+ this.$picker.find(`[data-duration="days"]`).prop("value", total_duration.days);
+ }
+ if (total_duration.hours) {
+ this.$picker.find(`[data-duration="hrs"]`).prop("value", total_duration.hours);
+ }
+ if (total_duration.minutes) {
+ this.$picker.find(`[data-duration="mins"]`).prop("value", total_duration.minutes);
+ }
+ if (total_duration.seconds) {
+ this.$picker.find(`[data-duration="secs"]`).prop("value", total_duration.seconds);
+ }
}
},
From 74eba09e526e521e503ac10b0597dfe6677cbd56 Mon Sep 17 00:00:00 2001
From: Rucha Mahabal
Date: Wed, 6 May 2020 14:06:34 +0530
Subject: [PATCH 048/572] fix(style): remove borders on focus for individual
inputs
---
frappe/public/less/controls.less | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/frappe/public/less/controls.less b/frappe/public/less/controls.less
index b6ac3cf359..161293b558 100644
--- a/frappe/public/less/controls.less
+++ b/frappe/public/less/controls.less
@@ -224,6 +224,10 @@
border: 1px solid rgba(0, 0, 0, 0.25) !important;
}
+ .duration-input:focus {
+ outline: None;
+ }
+
.duration-label {
justify-content: center;
}
From 8383b9fc3ac18485d2379b12a0b2d97f34c96f37 Mon Sep 17 00:00:00 2001
From: "Chinmay D. Pai"
Date: Wed, 6 May 2020 14:21:44 +0530
Subject: [PATCH 049/572] fix: clear_sessions should by default include mobile
whenever password was changed, the system only logged out user for the
current device type; which is incorrect.
suppose if the user logs in through mobile and selects "logout all devices"
on password reset on desktop, the system would log the user out of all
desktop instances, leaving the mobile instance active.
Signed-off-by: Chinmay D. Pai
---
frappe/sessions.py | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/frappe/sessions.py b/frappe/sessions.py
index cca40cbc55..d317d6caf3 100644
--- a/frappe/sessions.py
+++ b/frappe/sessions.py
@@ -33,7 +33,7 @@ def clear_sessions(user=None, keep_current=False, device=None, force=False):
:param user: user name (default: current user)
:param keep_current: keep current session (default: false)
- :param device: delete sessions of this device (default: desktop)
+ :param device: delete sessions of this device (default: desktop, mobile)
:param force: triggered by the user (default false)
'''
@@ -49,13 +49,16 @@ def get_sessions_to_clear(user=None, keep_current=False, device=None):
:param user: user name (default: current user)
:param keep_current: keep current session (default: false)
- :param device: delete sessions of this device (default: desktop)
+ :param device: delete sessions of this device (default: desktop, mobile)
'''
if not user:
user = frappe.session.user
if not device:
- device = frappe.session.data.device or "desktop"
+ device = ("desktop", "mobile")
+
+ if not isinstance(device, (tuple, list)):
+ device = (device,)
offset = 0
if user == frappe.session.user:
@@ -68,12 +71,12 @@ def get_sessions_to_clear(user=None, keep_current=False, device=None):
return frappe.db.sql_list("""
SELECT `sid` FROM `tabSessions`
- WHERE user=%s
- AND device=%s
+ WHERE user=%(user)s
+ AND device in %(device)s
{condition}
ORDER BY `lastupdate` DESC
LIMIT 100 OFFSET {offset}""".format(condition=condition, offset=offset),
- (user, device))
+ {"user": user, "device": device})
def delete_session(sid=None, user=None, reason="Session Expired"):
from frappe.core.doctype.activity_log.feed import logout_feed
From 04a59352f7ccaf66758632e15415395844246280 Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Wed, 6 May 2020 17:40:50 +0530
Subject: [PATCH 050/572] fix: test cases
---
frappe/desk/form/assign_to.py | 2 +-
frappe/public/js/frappe/form/sidebar/assign_to.js | 2 +-
frappe/tests/test_assign.py | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/frappe/desk/form/assign_to.py b/frappe/desk/form/assign_to.py
index 6178690b1f..6c8cd5dfbd 100644
--- a/frappe/desk/form/assign_to.py
+++ b/frappe/desk/form/assign_to.py
@@ -44,7 +44,7 @@ def add(args=None):
users_with_duplicate_todo = []
shared_with_users = []
- for assign_to in json.loads(args.get("assign_to")):
+ for assign_to in frappe.parse_json(args.get("assign_to")):
filters = {
"reference_type": args['doctype'],
"reference_name": args['name'],
diff --git a/frappe/public/js/frappe/form/sidebar/assign_to.js b/frappe/public/js/frappe/form/sidebar/assign_to.js
index 84fde564c0..95ceb246e6 100644
--- a/frappe/public/js/frappe/form/sidebar/assign_to.js
+++ b/frappe/public/js/frappe/form/sidebar/assign_to.js
@@ -130,7 +130,7 @@ frappe.ui.form.AssignToDialog = Class.extend({
this.set_description_from_doc();
},
make: function() {
- let me = this
+ let me = this;
me.dialog = new frappe.ui.Dialog({
title: __('Add to ToDo'),
diff --git a/frappe/tests/test_assign.py b/frappe/tests/test_assign.py
index f32e3c9272..439e1546c0 100644
--- a/frappe/tests/test_assign.py
+++ b/frappe/tests/test_assign.py
@@ -60,7 +60,7 @@ class TestAssign(unittest.TestCase):
def assign(doc, user):
return frappe.desk.form.assign_to.add({
- "assign_to": user,
+ "assign_to": [user],
"doctype": doc.doctype,
"name": doc.name,
"description": 'test',
From 2c12af23191518a625b77c7cf6e715826b709db8 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Thu, 7 May 2020 12:12:04 +0530
Subject: [PATCH 051/572] fix: consider permissions for charts and cards
---
frappe/desk/doctype/dashboard_chart/dashboard_chart.py | 4 ++--
frappe/desk/doctype/number_card/number_card.py | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py
index 7ddb3d98f0..c03f6f8156 100644
--- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py
+++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py
@@ -144,7 +144,7 @@ def get_chart_config(chart, filters, timespan, timegrain, from_date, to_date):
filters.append([doctype, datefield, '>=', from_date, False])
filters.append([doctype, datefield, '<=', to_date, False])
- data = frappe.db.get_all(
+ data = frappe.db.get_list(
doctype,
fields = [
'extract(year from `tab{doctype}`.{datefield}) as _year'.format(doctype=doctype, datefield=datefield),
@@ -182,7 +182,7 @@ def get_group_by_chart_config(chart, filters):
group_by_field = chart.group_by_based_on
doctype = chart.document_type
- data = frappe.db.get_all(
+ data = frappe.db.get_list(
doctype,
fields = [
'{} as name'.format(group_by_field),
diff --git a/frappe/desk/doctype/number_card/number_card.py b/frappe/desk/doctype/number_card/number_card.py
index 2c5655beda..2c072f44c4 100644
--- a/frappe/desk/doctype/number_card/number_card.py
+++ b/frappe/desk/doctype/number_card/number_card.py
@@ -65,7 +65,7 @@ def get_result(doc, to_date=None):
if to_date:
filters.append([doc.document_type, 'creation', '<', to_date, False])
- res = frappe.db.get_all(doc.document_type, fields=fields, filters=filters)
+ res = frappe.db.get_list(doc.document_type, fields=fields, filters=filters)
number = res[0]['result'] if res else 0
return cint(number)
From fff07548fd0e05b5e4232cbda25237a5137ff012 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Thu, 7 May 2020 14:00:32 +0530
Subject: [PATCH 052/572] fix: show restricted button to indicate permission
restrictions
---
frappe/core/page/dashboard/dashboard.css | 5 +++++
frappe/core/page/dashboard/dashboard.js | 7 +++++++
frappe/public/js/frappe/list/list_view.js | 2 +-
.../frappe/views/dashboard/dashboard_view.js | 8 +++++++-
frappe/public/less/dashboard_view.less | 12 ++++++++++++
frappe/public/less/desk.less | 17 +++++++++++++++++
frappe/public/less/list.less | 18 +-----------------
7 files changed, 50 insertions(+), 19 deletions(-)
diff --git a/frappe/core/page/dashboard/dashboard.css b/frappe/core/page/dashboard/dashboard.css
index e69de29bb2..e1687a0675 100644
--- a/frappe/core/page/dashboard/dashboard.css
+++ b/frappe/core/page/dashboard/dashboard.css
@@ -0,0 +1,5 @@
+.restricted-button {
+ cursor: default;
+ position: relative;
+ right: -5px;
+}
\ No newline at end of file
diff --git a/frappe/core/page/dashboard/dashboard.js b/frappe/core/page/dashboard/dashboard.js
index 222a31a863..0634d6518c 100644
--- a/frappe/core/page/dashboard/dashboard.js
+++ b/frappe/core/page/dashboard/dashboard.js
@@ -26,6 +26,13 @@ class Dashboard {
`).appendTo(this.wrapper.find(".page-content").empty());
this.container = this.wrapper.find(".dashboard-graph");
this.page = wrapper.page;
+
+ this.page.set_title_sub(
+ $(``)
+ )
}
show() {
diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js
index af9a3c0221..dd9362d664 100644
--- a/frappe/public/js/frappe/list/list_view.js
+++ b/frappe/public/js/frappe/list/list_view.js
@@ -139,7 +139,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
show_restricted_list_indicator_if_applicable() {
const match_rules_list = frappe.perm.get_match_rules(this.doctype);
if (match_rules_list.length) {
- this.restricted_list = $(``)
+ this.restricted_list = $(``)
.prepend('')
.click(() => this.show_restrictions(match_rules_list))
.appendTo(this.page.page_form);
diff --git a/frappe/public/js/frappe/views/dashboard/dashboard_view.js b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
index 13c44d2130..83f45da5be 100644
--- a/frappe/public/js/frappe/views/dashboard/dashboard_view.js
+++ b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
@@ -41,7 +41,13 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
this.$dashboard_page = this.$page.find('.layout-main-section-wrapper').addClass('dashboard-page');
this.$page.find('.page-form').empty().html(
`