diff --git a/cypress/integration/control_icon.js b/cypress/integration/control_icon.js
index a965ed0f9e..406e9f1162 100644
--- a/cypress/integration/control_icon.js
+++ b/cypress/integration/control_icon.js
@@ -42,14 +42,14 @@ context("Control Icon", () => {
it("search for icon and clear search input", () => {
let search_text = "ed";
- cy.get(".icon-picker").findByRole("searchbox").click().type(search_text);
+ cy.get(".icon-picker").get(".search-icons > input").click().type(search_text);
cy.get(".icon-section .icon-wrapper:not(.hidden)").then((i) => {
cy.get(`.icon-section .icon-wrapper[id*='${search_text}']`).then((icons) => {
expect(i.length).to.equal(icons.length);
});
});
- cy.get(".icon-picker").findByRole("searchbox").clear().blur();
+ cy.get(".icon-picker").get(".search-icons > input").clear().blur();
cy.get(".icon-section .icon-wrapper").should("not.have.class", "hidden");
});
});
diff --git a/cypress/integration/control_link.js b/cypress/integration/control_link.js
index d3462492f6..0746f4460e 100644
--- a/cypress/integration/control_link.js
+++ b/cypress/integration/control_link.js
@@ -133,8 +133,7 @@ context("Control Link", () => {
true
);
- cy.clear_cache();
- cy.wait(500);
+ cy.reload();
get_dialog_with_link().as("dialog");
cy.window()
@@ -177,7 +176,7 @@ context("Control Link", () => {
cy.intercept("POST", "/api/method/frappe.client.validate_link").as("validate_link");
cy.get(".frappe-control[data-fieldname=assigned_by] input").focus().as("input");
- cy.get("@input").type(cy.config("testUser"), { delay: 100 }).blur();
+ cy.get("@input").clear().type(cy.config("testUser"), { delay: 300 }).blur();
cy.wait("@validate_link");
cy.get(".frappe-control[data-fieldname=assigned_by_full_name] .control-value").should(
"contain",
diff --git a/cypress/integration/control_phone.js b/cypress/integration/control_phone.js
index b56343c2d8..b5b29fe758 100644
--- a/cypress/integration/control_phone.js
+++ b/cypress/integration/control_phone.js
@@ -47,7 +47,7 @@ context("Control Phone", () => {
it("case insensitive search for country and clear search", () => {
let search_text = "india";
cy.get(".selected-phone").click().first();
- cy.get(".phone-picker").findByRole("searchbox").click().type(search_text);
+ cy.get(".phone-picker").get(".search-phones").click().type(search_text);
cy.get(".phone-section .phone-wrapper:not(.hidden)").then((i) => {
cy.get(`.phone-section .phone-wrapper[id*="${search_text.toLowerCase()}"]`).then(
(countries) => {
@@ -56,7 +56,7 @@ context("Control Phone", () => {
);
});
- cy.get(".phone-picker").findByRole("searchbox").clear().blur();
+ cy.get(".phone-picker").get(".search-phones").clear();
cy.get(".phone-section .phone-wrapper").should("not.have.class", "hidden");
});
diff --git a/cypress/integration/folder_navigation.js b/cypress/integration/folder_navigation.js
index ba65454ef6..4a32c16516 100644
--- a/cypress/integration/folder_navigation.js
+++ b/cypress/integration/folder_navigation.js
@@ -11,9 +11,9 @@ context("Folder Navigation", () => {
cy.click_filter_button();
cy.get(".filter-action-buttons > .text-muted").findByText("+ Add a Filter").click();
cy.get(".fieldname-select-area > .awesomplete > .form-control:last").type("Fol{enter}");
- cy.get(
- ".filter-field > .form-group > .link-field > .awesomplete > .input-with-feedback"
- ).type("Home{enter}");
+ cy.get(".filter-field > .form-group > .link-field > .awesomplete > .input-with-feedback")
+ .first()
+ .type("Home{enter}");
cy.get(".filter-action-buttons > div > .btn-primary").findByText("Apply Filters").click();
//Adding folder (Test Folder)
@@ -24,6 +24,7 @@ context("Folder Navigation", () => {
it("Navigating the nested folders, checking if the URL formed is correct, checking if the added content in the child folder is correct", () => {
//Navigating inside the Attachments folder
+ cy.clear_filters();
cy.wait(500);
cy.get('[title="Attachments"] > span').click();
diff --git a/cypress/integration/kanban.js b/cypress/integration/kanban.js
index f14c991c7c..12c5e7e8bd 100644
--- a/cypress/integration/kanban.js
+++ b/cypress/integration/kanban.js
@@ -100,15 +100,15 @@ context("Kanban Board", () => {
it("Checks if Kanban Board edits are blocked for non-System Manager and non-owner of the Board", () => {
cy.switch_to_user("Administrator");
- const noSystemManager = "nosysmanager@example.com";
+ const not_system_manager = "nosysmanager@example.com";
cy.call("frappe.tests.ui_test_helpers.create_test_user", {
- username: noSystemManager,
+ username: not_system_manager,
});
- cy.remove_role(noSystemManager, "System Manager");
+ cy.remove_role(not_system_manager, "System Manager");
cy.call("frappe.tests.ui_test_helpers.create_todo", { description: "Frappe User ToDo" });
cy.call("frappe.tests.ui_test_helpers.create_admin_kanban");
- cy.switch_to_user(noSystemManager);
+ cy.switch_to_user(not_system_manager);
cy.visit("/app/todo/view/kanban/Admin Kanban");
@@ -125,7 +125,7 @@ context("Kanban Board", () => {
cy.get(".kanban .column-options").should("have.length", 0);
cy.switch_to_user("Administrator");
- cy.call("frappe.client.delete", { doctype: "User", name: noSystemManager });
+ cy.call("frappe.client.delete", { doctype: "User", name: not_system_manager });
});
after(() => {
diff --git a/cypress/support/commands.js b/cypress/support/commands.js
index 4b44a24598..23b03549fa 100644
--- a/cypress/support/commands.js
+++ b/cypress/support/commands.js
@@ -34,7 +34,7 @@ Cypress.Commands.add("login", (email, password) => {
if (!password) {
password = Cypress.env("adminPassword");
}
- cy.request({
+ return cy.request({
url: "/api/method/login",
method: "POST",
body: {
@@ -373,7 +373,9 @@ Cypress.Commands.add("update_doc", (doctype, docname, args) => {
Cypress.Commands.add("switch_to_user", (user) => {
cy.call("logout");
+ cy.wait(200);
cy.login(user);
+ cy.reload();
});
Cypress.Commands.add("add_role", (user, role) => {
diff --git a/esbuild/esbuild.js b/esbuild/esbuild.js
index 8c386c86e4..e1594aa651 100644
--- a/esbuild/esbuild.js
+++ b/esbuild/esbuild.js
@@ -89,7 +89,7 @@ execute()
.then(() => RUN_BUILD_COMMAND && run_build_command_for_apps(APPS))
.catch((e) => {
console.error(e);
- throw e;
+ process.exit(1);
});
if (WATCH_MODE) {
diff --git a/frappe/core/doctype/docshare/docshare.json b/frappe/core/doctype/docshare/docshare.json
index ca10b05dac..e3581f516e 100644
--- a/frappe/core/doctype/docshare/docshare.json
+++ b/frappe/core/doctype/docshare/docshare.json
@@ -67,7 +67,8 @@
"default": "0",
"fieldname": "everyone",
"fieldtype": "Check",
- "label": "Everyone"
+ "label": "Everyone",
+ "search_index": 1
},
{
"default": "1",
@@ -85,10 +86,11 @@
],
"in_create": 1,
"links": [],
- "modified": "2021-04-04 11:38:50.813312",
+ "modified": "2023-06-15 18:02:51.877533",
"modified_by": "Administrator",
"module": "Core",
"name": "DocShare",
+ "naming_rule": "Random",
"owner": "Administrator",
"permissions": [
{
@@ -106,5 +108,6 @@
"read_only": 1,
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"track_changes": 1
}
\ No newline at end of file
diff --git a/frappe/core/doctype/translation/translation.py b/frappe/core/doctype/translation/translation.py
index a285806589..c8226db5b0 100644
--- a/frappe/core/doctype/translation/translation.py
+++ b/frappe/core/doctype/translation/translation.py
@@ -23,70 +23,6 @@ class Translation(Document):
def on_trash(self):
clear_user_translation_cache(self.language)
- def contribute(self):
- pass
-
- def get_contribution_status(self):
- pass
-
-
-@frappe.whitelist()
-def create_translations(translation_map, language):
- from frappe.frappeclient import FrappeClient
-
- translation_map = json.loads(translation_map)
- translation_map_to_send = frappe._dict({})
- # first create / update local user translations
- for source_id, translation_dict in translation_map.items():
- translation_dict = frappe._dict(translation_dict)
- existing_doc_name = frappe.get_all(
- "Translation",
- {
- "source_text": translation_dict.source_text,
- "context": translation_dict.context or "",
- "language": language,
- },
- )
- translation_map_to_send[source_id] = translation_dict
- if existing_doc_name:
- frappe.db.set_value(
- "Translation",
- existing_doc_name[0].name,
- {
- "translated_text": translation_dict.translated_text,
- "contributed": 1,
- "contribution_status": "Pending",
- },
- )
- translation_map_to_send[source_id].name = existing_doc_name[0].name
- else:
- doc = frappe.get_doc(
- {
- "doctype": "Translation",
- "source_text": translation_dict.source_text,
- "contributed": 1,
- "contribution_status": "Pending",
- "translated_text": translation_dict.translated_text,
- "context": translation_dict.context,
- "language": language,
- }
- )
- doc.insert()
- translation_map_to_send[source_id].name = doc.name
-
- params = {
- "language": language,
- "contributor_email": frappe.session.user,
- "contributor_name": frappe.utils.get_fullname(frappe.session.user),
- "translation_map": json.dumps(translation_map_to_send),
- }
-
- translator = FrappeClient(get_translator_url())
- added_translations = translator.post_api("translator.api.add_translations", params=params)
-
- for local_docname, remote_docname in added_translations.items():
- frappe.db.set_value("Translation", local_docname, "contribution_docname", remote_docname)
-
def clear_user_translation_cache(lang):
frappe.cache.hdel(USER_TRANSLATION_KEY, lang)
diff --git a/frappe/database/database.py b/frappe/database/database.py
index 6616592fd7..1f2a1eeba9 100644
--- a/frappe/database/database.py
+++ b/frappe/database/database.py
@@ -33,6 +33,7 @@ from frappe.query_builder.functions import Count
from frappe.utils import CallbackManager
from frappe.utils import cast as cast_fieldtype
from frappe.utils import cint, get_datetime, get_table_name, getdate, now, sbool
+from frappe.utils.deprecations import deprecation_warning
IFNULL_PATTERN = re.compile(r"ifnull\(", flags=re.IGNORECASE)
INDEX_PATTERN = re.compile(r"\s*\([^)]+\)\s*")
@@ -888,6 +889,22 @@ class Database:
:param update_modified: default True. Set as false, if you don't want to update the timestamp.
:param debug: Print the query in the developer / js console.
"""
+ from frappe.model.utils import is_single_doctype
+
+ if (dn is None or dt == dn) and is_single_doctype(dt):
+ deprecation_warning(
+ "Calling db.set_value on single doctype is deprecated. This behaviour will be removed in future. Use db.set_single_value instead."
+ )
+ self.set_single_value(
+ doctype=dt,
+ fieldname=field,
+ value=val,
+ debug=debug,
+ update_modified=update_modified,
+ modified=modified,
+ modified_by=modified_by,
+ )
+ return
to_update = self._get_update_dict(
field, val, modified=modified, modified_by=modified_by, update_modified=update_modified
diff --git a/frappe/desk/doctype/notification_log/notification_log.json b/frappe/desk/doctype/notification_log/notification_log.json
index f24a6447b4..bafe28faf8 100644
--- a/frappe/desk/doctype/notification_log/notification_log.json
+++ b/frappe/desk/doctype/notification_log/notification_log.json
@@ -29,7 +29,8 @@
"fieldtype": "Link",
"hidden": 1,
"label": "For User",
- "options": "User"
+ "options": "User",
+ "search_index": 1
},
{
"fieldname": "type",
@@ -64,8 +65,7 @@
"fieldtype": "Link",
"hidden": 1,
"label": "From User",
- "options": "User",
- "search_index": 1
+ "options": "User"
},
{
"default": "0",
@@ -96,7 +96,7 @@
"hide_toolbar": 1,
"in_create": 1,
"links": [],
- "modified": "2022-09-13 16:08:48.153934",
+ "modified": "2023-06-14 21:20:51.197943",
"modified_by": "Administrator",
"module": "Desk",
"name": "Notification Log",
diff --git a/frappe/desk/form/meta.py b/frappe/desk/form/meta.py
index 4f664658ad..3ad65f7b13 100644
--- a/frappe/desk/form/meta.py
+++ b/frappe/desk/form/meta.py
@@ -36,7 +36,7 @@ ASSET_KEYS = (
def get_meta(doctype, cached=True) -> "FormMeta":
# don't cache for developer mode as js files, templates may be edited
- cached = cached and not frappe._dev_server
+ cached = cached and not frappe.conf.developer_mode
if cached:
meta = frappe.cache.hget("doctype_form_meta", doctype)
if not meta:
diff --git a/frappe/model/utils/__init__.py b/frappe/model/utils/__init__.py
index 2935872fc7..f8f5b21de4 100644
--- a/frappe/model/utils/__init__.py
+++ b/frappe/model/utils/__init__.py
@@ -133,3 +133,13 @@ def is_virtual_doctype(doctype: str):
if frappe.db.has_column("DocType", "is_virtual"):
return frappe.db.get_value("DocType", doctype, "is_virtual")
return False
+
+
+@site_cache()
+def is_single_doctype(doctype: str) -> bool:
+ from frappe.model.base_document import DOCTYPES_FOR_DOCTYPE
+
+ if doctype in DOCTYPES_FOR_DOCTYPE:
+ return False
+
+ return frappe.db.get_value("DocType", doctype, "issingle")
diff --git a/frappe/public/icons/timeless/icons.svg b/frappe/public/icons/timeless/icons.svg
index f5a34a14c9..29aa428d99 100644
--- a/frappe/public/icons/timeless/icons.svg
+++ b/frappe/public/icons/timeless/icons.svg
@@ -84,13 +84,15 @@
-
-
+
-
-
+
+
+
+
+
diff --git a/frappe/public/js/frappe/form/controls/link.js b/frappe/public/js/frappe/form/controls/link.js
index b187135181..cb6beadb7a 100644
--- a/frappe/public/js/frappe/form/controls/link.js
+++ b/frappe/public/js/frappe/form/controls/link.js
@@ -267,15 +267,17 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
r.results = me.merge_duplicates(r.results);
// show filter description in awesomplete
- if (args.filters) {
- let filter_string = me.get_filter_description(args.filters);
- if (filter_string) {
- r.results.push({
- html: `${filter_string}`,
- value: "",
- action: () => {},
- });
- }
+ let filter_string = me.df.filter_description
+ ? me.df.filter_description
+ : args.filters
+ ? me.get_filter_description(args.filters)
+ : null;
+ if (filter_string) {
+ r.results.push({
+ html: `${filter_string}`,
+ value: "",
+ action: () => {},
+ });
}
if (!me.df.only_select) {
diff --git a/frappe/public/js/frappe/model/create_new.js b/frappe/public/js/frappe/model/create_new.js
index 523cb3b7a6..5deccaeb9b 100644
--- a/frappe/public/js/frappe/model/create_new.js
+++ b/frappe/public/js/frappe/model/create_new.js
@@ -163,7 +163,8 @@ $.extend(frappe.model, {
if (!user_default) {
user_default = frappe.defaults.get_user_default(df.fieldname);
- } else if (
+ }
+ if (
!user_default &&
df.remember_last_selected_value &&
frappe.boot.user.last_selected_values
diff --git a/frappe/public/js/frappe/widgets/number_card_widget.js b/frappe/public/js/frappe/widgets/number_card_widget.js
index af89f95197..26b1dabd35 100644
--- a/frappe/public/js/frappe/widgets/number_card_widget.js
+++ b/frappe/public/js/frappe/widgets/number_card_widget.js
@@ -238,7 +238,7 @@ export default class NumberCardWidget extends Widget {
color_class = "green-stat";
} else {
caret_html = `
- ${frappe.utils.icon("arrow-down-left", "xs")}
+ ${frappe.utils.icon("arrow-down-right", "xs")}
`;
color_class = "red-stat";
}
diff --git a/frappe/share.py b/frappe/share.py
index adae95ea23..6c2fb356a6 100644
--- a/frappe/share.py
+++ b/frappe/share.py
@@ -161,7 +161,7 @@ def get_shared(doctype, user=None, rights=None):
or_filters += [["everyone", "=", 1]]
shared_docs = frappe.get_all(
- "DocShare", fields=["share_name"], filters=filters, or_filters=or_filters
+ "DocShare", fields=["share_name"], filters=filters, or_filters=or_filters, order_by=None
)
return [doc.share_name for doc in shared_docs]
diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py
index 3d103e3ab5..f026726cfa 100644
--- a/frappe/tests/test_db.py
+++ b/frappe/tests/test_db.py
@@ -718,6 +718,11 @@ class TestDBSetValue(FrappeTestCase):
updated_value = frappe.db.get_value("ToDo", self.todo1.name, "description")
self.assertEqual(updated_value, "test_set_value change 1")
+ @patch("frappe.db.set_single_value")
+ def test_set_single_value_with_set_value(self, single_set):
+ frappe.db.set_value("Contact Us Settings", None, "country", "India")
+ single_set.assert_called_once()
+
def test_update_single_row_multiple_columns(self):
description, status = "Upated by test_update_single_row_multiple_columns", "Closed"