diff --git a/.github/workflows/_base-server-tests.yml b/.github/workflows/_base-server-tests.yml index f269e0bde6..81e90599dc 100644 --- a/.github/workflows/_base-server-tests.yml +++ b/.github/workflows/_base-server-tests.yml @@ -55,7 +55,6 @@ jobs: timeout-minutes: 30 env: NODE_ENV: "production" - PYTHONOPTIMIZE: 2 # noisy 3rd party library warnings PYTHONWARNINGS: "module,ignore:::babel.messages.extract" DB_ROOT_PASSWORD: db_root diff --git a/.github/workflows/_base-ui-tests.yml b/.github/workflows/_base-ui-tests.yml index 88c22e28f1..cc9b09bf1f 100644 --- a/.github/workflows/_base-ui-tests.yml +++ b/.github/workflows/_base-ui-tests.yml @@ -44,7 +44,6 @@ jobs: timeout-minutes: 30 env: NODE_ENV: "production" - PYTHONOPTIMIZE: 2 # noisy 3rd party library warnings PYTHONWARNINGS: "ignore" DB_ROOT_PASSWORD: db_root @@ -99,6 +98,10 @@ jobs: bench --site test_site execute frappe.utils.install.complete_setup_wizard bench --site test_site execute frappe.tests.ui_test_helpers.create_test_user + - uses: browser-actions/setup-chrome@latest + - run: | + echo "BROWSER_PATH=$(which chrome)" >> $GITHUB_ENV + - name: Run Tests run: | source ${GITHUB_WORKSPACE}/env/bin/activate @@ -107,6 +110,7 @@ jobs: --with-coverage \ --headless \ --parallel \ + --browser ${{ env.BROWSER_PATH }} \ --ci-build-id $GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT env: CYPRESS_RECORD_KEY: 4a48f41c-11b3-425b-aa88-c58048fa69eb diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 6c0b3d297c..1d3a9076e2 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -95,7 +95,7 @@ jobs: run: | pip install pip-audit cd ${GITHUB_WORKSPACE} - pip-audit --desc on . + pip-audit --desc on --ignore-vuln PYSEC-2023-312 . precommit: name: 'Pre-Commit' diff --git a/README.md b/README.md index 9b5831d627..411a07072b 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@
- +
diff --git a/frappe/__init__.py b/frappe/__init__.py index 39ae573211..25cb85952c 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -516,6 +516,7 @@ def sendmail( with_container=False, email_read_tracker_url=None, x_priority: Literal[1, 3, 5] = 3, + email_headers=None, ) -> Optional["EmailQueue"]: """Send email using user's default **Email Account** or global default **Email Account**. @@ -544,6 +545,7 @@ def sendmail( :param header: Append header in email :param with_container: Wraps email inside a styled container :param x_priority: 1 = HIGHEST, 3 = NORMAL, 5 = LOWEST + :param email_headers: Additional headers to be added in the email, e.g. {"X-Custom-Header": "value"} or {"Custom-Header": "value"}. Automatically prepends "X-" to the header name if not present. """ if recipients is None: @@ -600,6 +602,7 @@ def sendmail( with_container=with_container, email_read_tracker_url=email_read_tracker_url, x_priority=x_priority, + email_headers=email_headers, ) # build email queue and send the email if send_now is True. @@ -977,6 +980,8 @@ def get_document_cache_key(doctype: str, name: str): def clear_document_cache(doctype: str, name: str | None = None) -> None: + frappe.db.value_cache.pop(doctype, None) + def clear_in_redis(): if name is not None: cache.delete_value(get_document_cache_key(doctype, name)) @@ -1015,6 +1020,15 @@ def get_cached_value( return values +def get_single_value(setting: str, fieldname: str, /, *, as_dict: bool = False): + """Return the cached value associated with the given fieldname from single DocType. + + Usage: + telemetry_enabled = frappe.get_single_value("System Settings", "telemetry_enabled") + """ + return get_cached_value(setting, setting, fieldname=fieldname, as_dict=as_dict) + + def get_last_doc( doctype, filters: FilterSignature | None = None, diff --git a/frappe/_optimizations.py b/frappe/_optimizations.py index b4fbb8f47d..2eaad68135 100644 --- a/frappe/_optimizations.py +++ b/frappe/_optimizations.py @@ -93,9 +93,7 @@ def freeze_gc(): def optimize_for_gil_contention(): - from frappe.utils import sbool - - if not bool(sbool(os.environ.get("FRAPPE_PERF_PIN_WORKERS", True))): + if not os.environ.get("FRAPPE_PERF_PIN_WORKERS"): return if "gunicorn" not in str(sys.argv[0]): diff --git a/frappe/api/__init__.py b/frappe/api/__init__.py index 5c504b2512..a8736db67e 100644 --- a/frappe/api/__init__.py +++ b/frappe/api/__init__.py @@ -41,6 +41,17 @@ def handle(request: Request): `DELETE` will delete """ + if frappe.get_system_settings("log_api_requests"): + doc = frappe.get_doc( + { + "doctype": "API Request Log", + "path": request.path, + "user": frappe.session.user, + "method": request.method, + } + ) + doc.deferred_insert() + try: endpoint, arguments = API_URL_MAP.bind_to_environ(request.environ).match() except NotFound: # Wrap 404 - backward compatiblity diff --git a/frappe/automation/doctype/assignment_rule/test_assignment_rule.py b/frappe/automation/doctype/assignment_rule/test_assignment_rule.py index 08a9cdc62b..be8a7621fe 100644 --- a/frappe/automation/doctype/assignment_rule/test_assignment_rule.py +++ b/frappe/automation/doctype/assignment_rule/test_assignment_rule.py @@ -2,21 +2,12 @@ # License: MIT. See LICENSE import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.tests.utils import make_test_records TEST_DOCTYPE = "Assignment Test" -class UnitTestAssignmentRule(UnitTestCase): - """ - Unit tests for AssignmentRule. - Use this class for testing individual functions and methods. - """ - - pass - - class TestAutoAssign(IntegrationTestCase): @classmethod def setUpClass(cls): diff --git a/frappe/automation/doctype/auto_repeat/test_auto_repeat.py b/frappe/automation/doctype/auto_repeat/test_auto_repeat.py index f5b93a8cc7..f110f6019f 100644 --- a/frappe/automation/doctype/auto_repeat/test_auto_repeat.py +++ b/frappe/automation/doctype/auto_repeat/test_auto_repeat.py @@ -9,7 +9,7 @@ from frappe.automation.doctype.auto_repeat.auto_repeat import ( week_map, ) from frappe.custom.doctype.custom_field.custom_field import create_custom_field -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.utils import add_days, add_months, getdate, today if TYPE_CHECKING: @@ -32,15 +32,6 @@ def add_custom_fields() -> "CustomField": ) -class UnitTestAutoRepeat(UnitTestCase): - """ - Unit tests for AutoRepeat. - Use this class for testing individual functions and methods. - """ - - pass - - class TestAutoRepeat(IntegrationTestCase): @classmethod def setUpClass(cls): diff --git a/frappe/automation/doctype/milestone/test_milestone.py b/frappe/automation/doctype/milestone/test_milestone.py index 6249d9082a..77a2abae1e 100644 --- a/frappe/automation/doctype/milestone/test_milestone.py +++ b/frappe/automation/doctype/milestone/test_milestone.py @@ -1,16 +1,7 @@ # Copyright (c) 2019, Frappe Technologies and Contributors # License: MIT. See LICENSE # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestMilestone(UnitTestCase): - """ - Unit tests for Milestone. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestMilestone(IntegrationTestCase): diff --git a/frappe/automation/doctype/milestone_tracker/test_milestone_tracker.py b/frappe/automation/doctype/milestone_tracker/test_milestone_tracker.py index 7c237cf534..8395d108b6 100644 --- a/frappe/automation/doctype/milestone_tracker/test_milestone_tracker.py +++ b/frappe/automation/doctype/milestone_tracker/test_milestone_tracker.py @@ -2,16 +2,7 @@ # License: MIT. See LICENSE import frappe import frappe.cache_manager -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestMilestoneTracker(UnitTestCase): - """ - Unit tests for MilestoneTracker. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestMilestoneTracker(IntegrationTestCase): diff --git a/frappe/automation/doctype/reminder/test_reminder.py b/frappe/automation/doctype/reminder/test_reminder.py index 11211a13df..c4b3c033dd 100644 --- a/frappe/automation/doctype/reminder/test_reminder.py +++ b/frappe/automation/doctype/reminder/test_reminder.py @@ -4,19 +4,10 @@ import frappe from frappe.automation.doctype.reminder.reminder import create_new_reminder, send_reminders from frappe.desk.doctype.notification_log.notification_log import get_notification_logs -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.utils import add_to_date, now_datetime -class UnitTestReminder(UnitTestCase): - """ - Unit tests for Reminder. - Use this class for testing individual functions and methods. - """ - - pass - - class TestReminder(IntegrationTestCase): def test_reminder(self): description = "TEST_REMINDER" diff --git a/frappe/boot.py b/frappe/boot.py index 612e2f75be..cd51ff5464 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -128,7 +128,7 @@ def get_bootinfo(): def remove_apps_with_incomplete_dependencies(bootinfo): - remove_apps = [] + remove_apps = set() for app in bootinfo.setup_wizard_not_required_apps: if app in bootinfo.setup_wizard_completed_apps: @@ -142,7 +142,7 @@ def remove_apps_with_incomplete_dependencies(bootinfo): continue if required_app not in bootinfo.setup_wizard_completed_apps: - remove_apps.append(app) + remove_apps.add(app) for app in remove_apps: bootinfo.setup_wizard_not_required_apps.remove(app) diff --git a/frappe/commands/testing.py b/frappe/commands/testing.py index 4acc05c9f6..2be1d12153 100644 --- a/frappe/commands/testing.py +++ b/frappe/commands/testing.py @@ -2,6 +2,7 @@ import os import subprocess import sys import time +import unittest from typing import TYPE_CHECKING import click @@ -32,8 +33,30 @@ def main( debug: bool = False, debug_exceptions: tuple[Exception] | None = None, selected_categories: list[str] | None = None, + lightmode: bool = False, ) -> None: """Main function to run tests""" + if lightmode: + from frappe.testing.config import TestParameters + + test_params = TestParameters( + site=site, + app=app, + module=module, + doctype=doctype, + module_def=module_def, + verbose=verbose, + tests=tests, + force=force, + profile=profile, + junit_xml_output=junit_xml_output, + doctype_list_path=doctype_list_path, + failfast=failfast, + case=case, + ) + run_tests_in_light_mode(test_params) + return + import logging from frappe.testing import ( @@ -157,6 +180,28 @@ def main( testing_module_logger.debug(f"Total test run time: {end_time - start_time:.3f} seconds") +def run_tests_in_light_mode(test_params): + from frappe.testing.loader import FrappeTestLoader + from frappe.testing.result import FrappeTestResult + + # init environment + frappe.init(test_params.site) + if not frappe.db: + frappe.connect() + + # disable scheduler + global scheduler_disabled_by_user + scheduler_disabled_by_user = frappe.utils.scheduler.is_scheduler_disabled(verbose=False) + if not scheduler_disabled_by_user: + frappe.utils.scheduler.disable_scheduler() + frappe.clear_cache() + + suite = FrappeTestLoader().discover_tests(test_params) + result = unittest.TextTestRunner(failfast=test_params.failfast, resultclass=FrappeTestResult).run(suite) + if not result.wasSuccessful(): + sys.exit(1) + + def _setup_xml_output(junit_xml_output): """Setup XML output for test results if specified""" global unittest_runner @@ -246,6 +291,7 @@ def _get_doctypes_for_module_def(app, module_def): default="all", help="Select test category to run", ) +@click.option("--lightmode", is_flag=True, default=False) @pass_context def run_tests( context: CliCtxObj, @@ -263,6 +309,7 @@ def run_tests( failfast=False, case=None, test_category="all", + lightmode=False, debug=False, ): """Run python unit-tests""" @@ -307,6 +354,7 @@ def run_tests( skip_before_tests=skip_before_tests, debug=debug, selected_categories=[] if test_category == "all" else test_category, + lightmode=lightmode, ) diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index ea17a029e8..ed126dff24 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -175,7 +175,7 @@ def destroy_all_sessions(context: CliCtxObj, reason=None): @click.option("--format", "-f", type=click.Choice(["text", "json"]), default="text") @pass_context def show_config(context: CliCtxObj, format): - "Print configuration file to STDOUT in speified format" + "Print configuration file to STDOUT in specified format" if not context.sites: raise SiteNotSpecifiedError diff --git a/frappe/contacts/doctype/address/test_address.py b/frappe/contacts/doctype/address/test_address.py index 001dd7221f..bd5793ac34 100644 --- a/frappe/contacts/doctype/address/test_address.py +++ b/frappe/contacts/doctype/address/test_address.py @@ -4,16 +4,7 @@ from functools import partial import frappe from frappe.contacts.doctype.address.address import address_query, get_address_display -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestAddress(UnitTestCase): - """ - Unit tests for Address. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestAddress(IntegrationTestCase): diff --git a/frappe/contacts/doctype/address_template/test_address_template.py b/frappe/contacts/doctype/address_template/test_address_template.py index 8aa97b3490..350feff4af 100644 --- a/frappe/contacts/doctype/address_template/test_address_template.py +++ b/frappe/contacts/doctype/address_template/test_address_template.py @@ -2,19 +2,10 @@ # License: MIT. See LICENSE import frappe from frappe.contacts.doctype.address_template.address_template import get_default_address_template -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.utils.jinja import validate_template -class UnitTestAddressTemplate(UnitTestCase): - """ - Unit tests for AddressTemplate. - Use this class for testing individual functions and methods. - """ - - pass - - class TestAddressTemplate(IntegrationTestCase): def setUp(self) -> None: frappe.db.delete("Address Template", {"country": "India"}) diff --git a/frappe/contacts/doctype/contact/test_contact.py b/frappe/contacts/doctype/contact/test_contact.py index 69d7b6e440..b426f653d1 100644 --- a/frappe/contacts/doctype/contact/test_contact.py +++ b/frappe/contacts/doctype/contact/test_contact.py @@ -3,20 +3,11 @@ import frappe from frappe.contacts.doctype.contact.contact import get_full_name from frappe.email import get_contact_list -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase EXTRA_TEST_RECORD_DEPENDENCIES = ["Contact", "Salutation"] -class UnitTestContact(UnitTestCase): - """ - Unit tests for Contact. - Use this class for testing individual functions and methods. - """ - - pass - - class TestContact(IntegrationTestCase): def test_check_default_email(self): emails = [ diff --git a/frappe/contacts/doctype/gender/test_gender.py b/frappe/contacts/doctype/gender/test_gender.py index 6ed4074186..0991cc5758 100644 --- a/frappe/contacts/doctype/gender/test_gender.py +++ b/frappe/contacts/doctype/gender/test_gender.py @@ -1,15 +1,6 @@ # Copyright (c) 2017, Frappe Technologies and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestGender(UnitTestCase): - """ - Unit tests for Gender. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestGender(IntegrationTestCase): diff --git a/frappe/contacts/doctype/salutation/test_salutation.py b/frappe/contacts/doctype/salutation/test_salutation.py index ffc2cb1d62..c373098876 100644 --- a/frappe/contacts/doctype/salutation/test_salutation.py +++ b/frappe/contacts/doctype/salutation/test_salutation.py @@ -1,15 +1,6 @@ # Copyright (c) 2017, Frappe Technologies and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestSalutation(UnitTestCase): - """ - Unit tests for Salutation. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestSalutation(IntegrationTestCase): diff --git a/frappe/core/doctype/access_log/test_access_log.py b/frappe/core/doctype/access_log/test_access_log.py index 118c3d5eca..555cd94935 100644 --- a/frappe/core/doctype/access_log/test_access_log.py +++ b/frappe/core/doctype/access_log/test_access_log.py @@ -14,19 +14,10 @@ from frappe.core.doctype.data_import.data_import import export_csv from frappe.core.doctype.user.user import generate_keys # imports - standard imports -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.utils import cstr, get_site_url -class UnitTestAccessLog(UnitTestCase): - """ - Unit tests for AccessLog. - Use this class for testing individual functions and methods. - """ - - pass - - class TestAccessLog(IntegrationTestCase): def setUp(self): # generate keys for current user to send requests for the following tests diff --git a/frappe/core/doctype/activity_log/test_activity_log.py b/frappe/core/doctype/activity_log/test_activity_log.py index 357cb2ebf1..d78f29449b 100644 --- a/frappe/core/doctype/activity_log/test_activity_log.py +++ b/frappe/core/doctype/activity_log/test_activity_log.py @@ -4,16 +4,7 @@ import time import frappe from frappe.auth import CookieManager, LoginManager -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestActivityLog(UnitTestCase): - """ - Unit tests for ActivityLog. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestActivityLog(IntegrationTestCase): diff --git a/frappe/core/doctype/api_request_log/__init__.py b/frappe/core/doctype/api_request_log/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/core/doctype/api_request_log/api_request_log.js b/frappe/core/doctype/api_request_log/api_request_log.js new file mode 100644 index 0000000000..010cc02679 --- /dev/null +++ b/frappe/core/doctype/api_request_log/api_request_log.js @@ -0,0 +1,8 @@ +// Copyright (c) 2025, Frappe Technologies and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("API Request Log", { +// refresh(frm) { + +// }, +// }); diff --git a/frappe/core/doctype/api_request_log/api_request_log.json b/frappe/core/doctype/api_request_log/api_request_log.json new file mode 100644 index 0000000000..834309a62f --- /dev/null +++ b/frappe/core/doctype/api_request_log/api_request_log.json @@ -0,0 +1,62 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2025-05-21 16:51:56.070193", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "path", + "method", + "user" + ], + "fields": [ + { + "fieldname": "path", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Path" + }, + { + "fieldname": "user", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "User", + "options": "User" + }, + { + "fieldname": "method", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Method" + } + ], + "grid_page_length": 50, + "index_web_pages_for_search": 1, + "links": [], + "modified": "2025-05-21 17:09:55.054044", + "modified_by": "Administrator", + "module": "Core", + "name": "API Request Log", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "row_format": "Dynamic", + "sort_field": "creation", + "sort_order": "DESC", + "states": [] +} diff --git a/frappe/core/doctype/api_request_log/api_request_log.py b/frappe/core/doctype/api_request_log/api_request_log.py new file mode 100644 index 0000000000..50ca6bf318 --- /dev/null +++ b/frappe/core/doctype/api_request_log/api_request_log.py @@ -0,0 +1,28 @@ +# Copyright (c) 2025, Frappe Technologies and contributors +# For license information, please see license.txt + +import frappe +from frappe.model.document import Document + + +class APIRequestLog(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + method: DF.Data | None + path: DF.Data | None + user: DF.Link | None + # end: auto-generated types + + @staticmethod + def clear_old_logs(days: int = 90): + from frappe.query_builder import Interval + from frappe.query_builder.functions import Now + + table = frappe.qb.DocType("API Request Log") + frappe.db.delete(table, filters=(table.creation < (Now() - Interval(days=days)))) diff --git a/frappe/core/doctype/api_request_log/test_api_request_log.py b/frappe/core/doctype/api_request_log/test_api_request_log.py new file mode 100644 index 0000000000..bb3d92c7f8 --- /dev/null +++ b/frappe/core/doctype/api_request_log/test_api_request_log.py @@ -0,0 +1,20 @@ +# Copyright (c) 2025, Frappe Technologies and Contributors +# See license.txt + +# import frappe +from frappe.tests import IntegrationTestCase + +# On IntegrationTestCase, the doctype test records and all +# link-field test record dependencies are recursively loaded +# Use these module variables to add/remove to/from that list +EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] +IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] + + +class IntegrationTestAPIRequestLog(IntegrationTestCase): + """ + Integration tests for APIRequestLog. + Use this class for testing interactions between multiple components. + """ + + pass diff --git a/frappe/core/doctype/audit_trail/test_audit_trail.py b/frappe/core/doctype/audit_trail/test_audit_trail.py index dcdce5a93a..41dadc92b5 100644 --- a/frappe/core/doctype/audit_trail/test_audit_trail.py +++ b/frappe/core/doctype/audit_trail/test_audit_trail.py @@ -2,19 +2,10 @@ # See license.txt import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.utils import today -class UnitTestAuditTrail(UnitTestCase): - """ - Unit tests for AuditTrail. - Use this class for testing individual functions and methods. - """ - - pass - - class TestAuditTrail(IntegrationTestCase): def setUp(self): self.child_doctype = create_custom_child_doctype() diff --git a/frappe/core/doctype/comment/test_comment.py b/frappe/core/doctype/comment/test_comment.py index 9aa9c015cd..b24641e3b6 100644 --- a/frappe/core/doctype/comment/test_comment.py +++ b/frappe/core/doctype/comment/test_comment.py @@ -4,20 +4,11 @@ import json import frappe from frappe.templates.includes.comments.comments import add_comment -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.tests.test_model_utils import set_user from frappe.website.doctype.blog_post.test_blog_post import make_test_blog -class UnitTestComment(UnitTestCase): - """ - Unit tests for Comment. - Use this class for testing individual functions and methods. - """ - - pass - - class TestComment(IntegrationTestCase): def test_comment_creation(self): test_doc = frappe.get_doc(doctype="ToDo", description="test") diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index b571e30440..4d8fd9bb08 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -490,8 +490,8 @@ def get_permission_query_conditions_for_communication(user): return """`tabCommunication`.communication_medium!='Email'""" email_accounts = ['"{}"'.format(account.get("email_account")) for account in accounts] - return """`tabCommunication`.email_account in ({email_accounts})""".format( - email_accounts=",".join(email_accounts) + return """`tabCommunication`.email_account in ({email_accounts}) or `tabCommunication`.recipients LIKE '%{user}%' or `tabCommunication`.sender LIKE '%{user}%' or `tabCommunication`.cc LIKE '%{user}%' or `tabCommunication`.bcc LIKE '%{user}%'""".format( + email_accounts=",".join(email_accounts), user=user ) diff --git a/frappe/core/doctype/communication/test_communication.py b/frappe/core/doctype/communication/test_communication.py index 7dc47a64c6..48a6e9b636 100644 --- a/frappe/core/doctype/communication/test_communication.py +++ b/frappe/core/doctype/communication/test_communication.py @@ -6,22 +6,13 @@ import frappe from frappe.core.doctype.communication.communication import Communication, get_emails, parse_email from frappe.core.doctype.communication.email import add_attachments, make from frappe.email.doctype.email_queue.email_queue import EmailQueue -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase if TYPE_CHECKING: from frappe.contacts.doctype.contact.contact import Contact from frappe.email.doctype.email_account.email_account import EmailAccount -class UnitTestCommunication(UnitTestCase): - """ - Unit tests for Communication. - Use this class for testing individual functions and methods. - """ - - pass - - class TestCommunication(IntegrationTestCase): def test_email(self): valid_email_list = [ diff --git a/frappe/core/doctype/custom_docperm/test_custom_docperm.py b/frappe/core/doctype/custom_docperm/test_custom_docperm.py index 5cec4ca73a..8882028308 100644 --- a/frappe/core/doctype/custom_docperm/test_custom_docperm.py +++ b/frappe/core/doctype/custom_docperm/test_custom_docperm.py @@ -1,15 +1,6 @@ # Copyright (c) 2015, Frappe Technologies and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestCustomDocperm(UnitTestCase): - """ - Unit tests for CustomDocperm. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestCustomDocPerm(IntegrationTestCase): diff --git a/frappe/core/doctype/custom_role/test_custom_role.py b/frappe/core/doctype/custom_role/test_custom_role.py index 8ee12abd5d..4b4d1bbbed 100644 --- a/frappe/core/doctype/custom_role/test_custom_role.py +++ b/frappe/core/doctype/custom_role/test_custom_role.py @@ -1,15 +1,6 @@ # Copyright (c) 2015, Frappe Technologies and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestCustomRole(UnitTestCase): - """ - Unit tests for CustomRole. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestCustomRole(IntegrationTestCase): diff --git a/frappe/core/doctype/data_export/test_data_exporter.py b/frappe/core/doctype/data_export/test_data_exporter.py index 90379cfb2f..e1b0a1eefa 100644 --- a/frappe/core/doctype/data_export/test_data_exporter.py +++ b/frappe/core/doctype/data_export/test_data_exporter.py @@ -2,16 +2,7 @@ # License: MIT. See LICENSE import frappe from frappe.core.doctype.data_export.exporter import DataExporter -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestDataExport(UnitTestCase): - """ - Unit tests for DataExport. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestDataExporter(IntegrationTestCase): diff --git a/frappe/core/doctype/data_import/test_data_import.py b/frappe/core/doctype/data_import/test_data_import.py index 9dade7f4ab..07b5b6fb27 100644 --- a/frappe/core/doctype/data_import/test_data_import.py +++ b/frappe/core/doctype/data_import/test_data_import.py @@ -1,16 +1,7 @@ # Copyright (c) 2020, Frappe Technologies and Contributors # License: MIT. See LICENSE # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestDataImport(UnitTestCase): - """ - Unit tests for DataImport. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestDataImport(IntegrationTestCase): diff --git a/frappe/core/doctype/data_import/test_exporter.py b/frappe/core/doctype/data_import/test_exporter.py index ac782d95df..f62b4df69b 100644 --- a/frappe/core/doctype/data_import/test_exporter.py +++ b/frappe/core/doctype/data_import/test_exporter.py @@ -3,20 +3,11 @@ import frappe from frappe.core.doctype.data_import.exporter import Exporter from frappe.core.doctype.data_import.test_importer import create_doctype_if_not_exists -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase doctype_name = "DocType for Export" -class UnitTestDataImport(UnitTestCase): - """ - Unit tests for DataImport. - Use this class for testing individual functions and methods. - """ - - pass - - class TestExporter(IntegrationTestCase): def setUp(self): create_doctype_if_not_exists(doctype_name) diff --git a/frappe/core/doctype/data_import/test_importer.py b/frappe/core/doctype/data_import/test_importer.py index b38edbb1c8..f15a87554f 100644 --- a/frappe/core/doctype/data_import/test_importer.py +++ b/frappe/core/doctype/data_import/test_importer.py @@ -2,22 +2,13 @@ # License: MIT. See LICENSE import frappe from frappe.core.doctype.data_import.importer import Importer -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.tests.test_query_builder import db_type_is, run_only_if from frappe.utils import format_duration, getdate doctype_name = "DocType for Import" -class UnitTestDataImport(UnitTestCase): - """ - Unit tests for DataImport. - Use this class for testing individual functions and methods. - """ - - pass - - class TestImporter(IntegrationTestCase): @classmethod def setUpClass(cls): diff --git a/frappe/core/doctype/data_import_log/test_data_import_log.py b/frappe/core/doctype/data_import_log/test_data_import_log.py index 0a75c3eb05..4d0a030718 100644 --- a/frappe/core/doctype/data_import_log/test_data_import_log.py +++ b/frappe/core/doctype/data_import_log/test_data_import_log.py @@ -2,16 +2,7 @@ # See license.txt # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestDataImportLog(UnitTestCase): - """ - Unit tests for DataImportLog. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestDataImportLog(IntegrationTestCase): diff --git a/frappe/core/doctype/deleted_document/test_deleted_document.py b/frappe/core/doctype/deleted_document/test_deleted_document.py index 81fa0871cf..522d6aa996 100644 --- a/frappe/core/doctype/deleted_document/test_deleted_document.py +++ b/frappe/core/doctype/deleted_document/test_deleted_document.py @@ -1,15 +1,6 @@ # Copyright (c) 2015, Frappe Technologies and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestDeletedDocument(UnitTestCase): - """ - Unit tests for DeletedDocument. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestDeletedDocument(IntegrationTestCase): diff --git a/frappe/core/doctype/docshare/test_docshare.py b/frappe/core/doctype/docshare/test_docshare.py index c59592a7da..69a03d754b 100644 --- a/frappe/core/doctype/docshare/test_docshare.py +++ b/frappe/core/doctype/docshare/test_docshare.py @@ -4,20 +4,11 @@ import frappe import frappe.share from frappe.automation.doctype.auto_repeat.test_auto_repeat import create_submittable_doctype -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase EXTRA_TEST_RECORD_DEPENDENCIES = ["User"] -class UnitTestDocshare(UnitTestCase): - """ - Unit tests for Docshare. - Use this class for testing individual functions and methods. - """ - - pass - - class TestDocShare(IntegrationTestCase): def setUp(self): self.user = "test@example.com" diff --git a/frappe/core/doctype/doctype/boilerplate/test_controller._py b/frappe/core/doctype/doctype/boilerplate/test_controller._py index 1baf2e1ffa..5b4a381979 100644 --- a/frappe/core/doctype/doctype/boilerplate/test_controller._py +++ b/frappe/core/doctype/doctype/boilerplate/test_controller._py @@ -2,7 +2,7 @@ # See license.txt # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase # On IntegrationTestCase, the doctype test records and all @@ -12,14 +12,6 @@ EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] -class UnitTest{classname}(UnitTestCase): - """ - Unit tests for {classname}. - Use this class for testing individual functions and methods. - """ - - pass - class IntegrationTest{classname}(IntegrationTestCase): """ diff --git a/frappe/core/doctype/doctype/test_doctype.py b/frappe/core/doctype/doctype/test_doctype.py index 6b20d7872c..ce2aeb65e8 100644 --- a/frappe/core/doctype/doctype/test_doctype.py +++ b/frappe/core/doctype/doctype/test_doctype.py @@ -25,19 +25,10 @@ from frappe.custom.doctype.custom_field.custom_field import create_custom_fields from frappe.desk.form.load import getdoc from frappe.model.delete_doc import delete_controllers from frappe.model.sync import remove_orphan_doctypes -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.utils import get_table_name -class UnitTestDoctype(UnitTestCase): - """ - Unit tests for Doctype. - Use this class for testing individual functions and methods. - """ - - pass - - class TestDocType(IntegrationTestCase): def tearDown(self): frappe.db.rollback() diff --git a/frappe/core/doctype/document_naming_rule/test_document_naming_rule.py b/frappe/core/doctype/document_naming_rule/test_document_naming_rule.py index 69717a4eab..c114b69506 100644 --- a/frappe/core/doctype/document_naming_rule/test_document_naming_rule.py +++ b/frappe/core/doctype/document_naming_rule/test_document_naming_rule.py @@ -1,16 +1,7 @@ # Copyright (c) 2020, Frappe Technologies and Contributors # License: MIT. See LICENSE import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestDocumentNamingRule(UnitTestCase): - """ - Unit tests for DocumentNamingRule. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestDocumentNamingRule(IntegrationTestCase): diff --git a/frappe/core/doctype/document_naming_rule_condition/test_document_naming_rule_condition.py b/frappe/core/doctype/document_naming_rule_condition/test_document_naming_rule_condition.py index 2588217cec..612f0f01e1 100644 --- a/frappe/core/doctype/document_naming_rule_condition/test_document_naming_rule_condition.py +++ b/frappe/core/doctype/document_naming_rule_condition/test_document_naming_rule_condition.py @@ -1,16 +1,7 @@ # Copyright (c) 2020, Frappe Technologies and Contributors # License: MIT. See LICENSE # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestDocumentNamingRuleCondition(UnitTestCase): - """ - Unit tests for DocumentNamingRuleCondition. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestDocumentNamingRuleCondition(IntegrationTestCase): diff --git a/frappe/core/doctype/document_naming_settings/test_document_naming_settings.py b/frappe/core/doctype/document_naming_settings/test_document_naming_settings.py index 3192e8f487..11153883cb 100644 --- a/frappe/core/doctype/document_naming_settings/test_document_naming_settings.py +++ b/frappe/core/doctype/document_naming_settings/test_document_naming_settings.py @@ -7,19 +7,10 @@ from frappe.core.doctype.document_naming_settings.document_naming_settings impor DocumentNamingSettings, ) from frappe.model.naming import NamingSeries, get_default_naming_series -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.utils import cint -class UnitTestDocumentNamingSettings(UnitTestCase): - """ - Unit tests for DocumentNamingSettings. - Use this class for testing individual functions and methods. - """ - - pass - - class TestNamingSeries(IntegrationTestCase): @classmethod def setUpClass(cls): diff --git a/frappe/core/doctype/document_share_key/test_document_share_key.py b/frappe/core/doctype/document_share_key/test_document_share_key.py index 0b00b79146..589c65f484 100644 --- a/frappe/core/doctype/document_share_key/test_document_share_key.py +++ b/frappe/core/doctype/document_share_key/test_document_share_key.py @@ -2,16 +2,7 @@ # See license.txt # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestDocumentShareKey(UnitTestCase): - """ - Unit tests for DocumentShareKey. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestDocumentShareKey(IntegrationTestCase): diff --git a/frappe/core/doctype/domain/test_domain.py b/frappe/core/doctype/domain/test_domain.py index 99b7c0e320..53935200ab 100644 --- a/frappe/core/doctype/domain/test_domain.py +++ b/frappe/core/doctype/domain/test_domain.py @@ -1,15 +1,6 @@ # Copyright (c) 2017, Frappe Technologies and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestDomain(UnitTestCase): - """ - Unit tests for Domain. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestDomain(IntegrationTestCase): diff --git a/frappe/core/doctype/error_log/test_error_log.py b/frappe/core/doctype/error_log/test_error_log.py index e23e7a0e05..6b4cd884ba 100644 --- a/frappe/core/doctype/error_log/test_error_log.py +++ b/frappe/core/doctype/error_log/test_error_log.py @@ -5,19 +5,10 @@ from unittest.mock import patch from ldap3.core.exceptions import LDAPException, LDAPInappropriateAuthenticationResult import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.utils.error import _is_ldap_exception, guess_exception_source -class UnitTestErrorLog(UnitTestCase): - """ - Unit tests for ErrorLog. - Use this class for testing individual functions and methods. - """ - - pass - - class TestErrorLog(IntegrationTestCase): def test_error_log(self): """let's do an error log on error log?""" diff --git a/frappe/core/doctype/file/test_file.py b/frappe/core/doctype/file/test_file.py index bb93396717..12e6b751f4 100644 --- a/frappe/core/doctype/file/test_file.py +++ b/frappe/core/doctype/file/test_file.py @@ -20,7 +20,7 @@ from frappe.core.doctype.file.exceptions import FileTypeNotAllowed from frappe.core.doctype.file.utils import get_corrupted_image_msg, get_extension from frappe.desk.form.utils import add_comment from frappe.exceptions import ValidationError -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.utils import get_files_path, set_request if TYPE_CHECKING: @@ -61,15 +61,6 @@ def make_test_image_file(private=False): _test_file.delete() -class UnitTestFile(UnitTestCase): - """ - Unit tests for File. - Use this class for testing individual functions and methods. - """ - - pass - - class TestSimpleFile(IntegrationTestCase): def setUp(self): self.attached_to_doctype, self.attached_to_docname = make_test_doc() diff --git a/frappe/core/doctype/installed_applications/test_installed_applications.py b/frappe/core/doctype/installed_applications/test_installed_applications.py index 13315bc362..7959f2d591 100644 --- a/frappe/core/doctype/installed_applications/test_installed_applications.py +++ b/frappe/core/doctype/installed_applications/test_installed_applications.py @@ -6,16 +6,7 @@ from frappe.core.doctype.installed_applications.installed_applications import ( InvalidAppOrder, update_installed_apps_order, ) -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestInstalledApplications(UnitTestCase): - """ - Unit tests for InstalledApplications. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestInstalledApplications(IntegrationTestCase): diff --git a/frappe/core/doctype/language/test_language.py b/frappe/core/doctype/language/test_language.py index e491447230..4abb60a288 100644 --- a/frappe/core/doctype/language/test_language.py +++ b/frappe/core/doctype/language/test_language.py @@ -1,15 +1,6 @@ # Copyright (c) 2015, Frappe Technologies and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestLanguage(UnitTestCase): - """ - Unit tests for Language. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestLanguage(IntegrationTestCase): diff --git a/frappe/core/doctype/log_setting_user/test_log_setting_user.py b/frappe/core/doctype/log_setting_user/test_log_setting_user.py index 0296baded9..ea44cb3d47 100644 --- a/frappe/core/doctype/log_setting_user/test_log_setting_user.py +++ b/frappe/core/doctype/log_setting_user/test_log_setting_user.py @@ -1,16 +1,7 @@ # Copyright (c) 2020, Frappe Technologies and Contributors # License: MIT. See LICENSE # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestLogSettingUser(UnitTestCase): - """ - Unit tests for LogSettingUser. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestLogSettingUser(IntegrationTestCase): diff --git a/frappe/core/doctype/log_settings/log_settings.py b/frappe/core/doctype/log_settings/log_settings.py index 09bf1fb2a6..157030b7fb 100644 --- a/frappe/core/doctype/log_settings/log_settings.py +++ b/frappe/core/doctype/log_settings/log_settings.py @@ -155,6 +155,7 @@ LOG_DOCTYPES = [ "Email Queue Recipient", "Error Log", "OAuth Bearer Token", + "API Request Log", ] diff --git a/frappe/core/doctype/log_settings/test_log_settings.py b/frappe/core/doctype/log_settings/test_log_settings.py index ed3f9a2a97..964e512d21 100644 --- a/frappe/core/doctype/log_settings/test_log_settings.py +++ b/frappe/core/doctype/log_settings/test_log_settings.py @@ -5,19 +5,10 @@ from datetime import datetime import frappe from frappe.core.doctype.log_settings.log_settings import _supports_log_clearing, run_log_clean_up -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.utils import add_to_date, now_datetime -class UnitTestLogSettings(UnitTestCase): - """ - Unit tests for LogSettings. - Use this class for testing individual functions and methods. - """ - - pass - - class TestLogSettings(IntegrationTestCase): @classmethod def setUpClass(cls): diff --git a/frappe/core/doctype/module_def/test_module_def.py b/frappe/core/doctype/module_def/test_module_def.py index e7c67c699d..7d03f674d9 100644 --- a/frappe/core/doctype/module_def/test_module_def.py +++ b/frappe/core/doctype/module_def/test_module_def.py @@ -1,15 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestModuleDef(UnitTestCase): - """ - Unit tests for ModuleDef. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestModuleDef(IntegrationTestCase): diff --git a/frappe/core/doctype/module_profile/test_module_profile.py b/frappe/core/doctype/module_profile/test_module_profile.py index 8f7a42e200..abfea4af0b 100644 --- a/frappe/core/doctype/module_profile/test_module_profile.py +++ b/frappe/core/doctype/module_profile/test_module_profile.py @@ -1,16 +1,7 @@ # Copyright (c) 2020, Frappe Technologies and Contributors # License: MIT. See LICENSE import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestModuleProfile(UnitTestCase): - """ - Unit tests for ModuleProfile. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestModuleProfile(IntegrationTestCase): diff --git a/frappe/core/doctype/navbar_item/test_navbar_item.py b/frappe/core/doctype/navbar_item/test_navbar_item.py index 162438e8c2..f8efb681be 100644 --- a/frappe/core/doctype/navbar_item/test_navbar_item.py +++ b/frappe/core/doctype/navbar_item/test_navbar_item.py @@ -1,16 +1,7 @@ # Copyright (c) 2020, Frappe Technologies and Contributors # License: MIT. See LICENSE # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestNavbarItem(UnitTestCase): - """ - Unit tests for NavbarItem. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestNavbarItem(IntegrationTestCase): diff --git a/frappe/core/doctype/navbar_settings/test_navbar_settings.py b/frappe/core/doctype/navbar_settings/test_navbar_settings.py index 15bbba386e..64f093d1bb 100644 --- a/frappe/core/doctype/navbar_settings/test_navbar_settings.py +++ b/frappe/core/doctype/navbar_settings/test_navbar_settings.py @@ -1,16 +1,7 @@ # Copyright (c) 2020, Frappe Technologies and Contributors # License: MIT. See LICENSE # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestNavbarSettings(UnitTestCase): - """ - Unit tests for NavbarSettings. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestNavbarSettings(IntegrationTestCase): diff --git a/frappe/core/doctype/package/test_package.py b/frappe/core/doctype/package/test_package.py index 5915fdc6d0..3de6200f15 100644 --- a/frappe/core/doctype/package/test_package.py +++ b/frappe/core/doctype/package/test_package.py @@ -5,16 +5,7 @@ import json import os import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestPackage(UnitTestCase): - """ - Unit tests for Package. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestPackage(IntegrationTestCase): diff --git a/frappe/core/doctype/package_import/test_package_import.py b/frappe/core/doctype/package_import/test_package_import.py index 260116cfcc..4259de64e4 100644 --- a/frappe/core/doctype/package_import/test_package_import.py +++ b/frappe/core/doctype/package_import/test_package_import.py @@ -2,16 +2,7 @@ # See license.txt # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestPackageImport(UnitTestCase): - """ - Unit tests for PackageImport. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestPackageImport(IntegrationTestCase): diff --git a/frappe/core/doctype/package_release/test_package_release.py b/frappe/core/doctype/package_release/test_package_release.py index f40e0c2277..f8cbd7ba5f 100644 --- a/frappe/core/doctype/package_release/test_package_release.py +++ b/frappe/core/doctype/package_release/test_package_release.py @@ -2,16 +2,7 @@ # See license.txt # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestPackageRelease(UnitTestCase): - """ - Unit tests for PackageRelease. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestPackageRelease(IntegrationTestCase): diff --git a/frappe/core/doctype/page/test_page.py b/frappe/core/doctype/page/test_page.py index 5eaa9ee18d..1a7807f3c3 100644 --- a/frappe/core/doctype/page/test_page.py +++ b/frappe/core/doctype/page/test_page.py @@ -5,16 +5,7 @@ import unittest from unittest.mock import patch import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestPage(UnitTestCase): - """ - Unit tests for Page. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestPage(IntegrationTestCase): diff --git a/frappe/core/doctype/patch_log/test_patch_log.py b/frappe/core/doctype/patch_log/test_patch_log.py index 98c9e3deb9..4af2fb554d 100644 --- a/frappe/core/doctype/patch_log/test_patch_log.py +++ b/frappe/core/doctype/patch_log/test_patch_log.py @@ -1,15 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestPatchLog(UnitTestCase): - """ - Unit tests for PatchLog. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestPatchLog(IntegrationTestCase): diff --git a/frappe/core/doctype/permission_inspector/test_permission_inspector.py b/frappe/core/doctype/permission_inspector/test_permission_inspector.py index 68a7107cd3..7e3e8b1a3c 100644 --- a/frappe/core/doctype/permission_inspector/test_permission_inspector.py +++ b/frappe/core/doctype/permission_inspector/test_permission_inspector.py @@ -2,16 +2,7 @@ # See license.txt # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestPermissionInspector(UnitTestCase): - """ - Unit tests for PermissionInspector. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestPermissionInspector(IntegrationTestCase): diff --git a/frappe/core/doctype/permission_log/test_permission_log.py b/frappe/core/doctype/permission_log/test_permission_log.py index 282c0d99df..3003364506 100644 --- a/frappe/core/doctype/permission_log/test_permission_log.py +++ b/frappe/core/doctype/permission_log/test_permission_log.py @@ -2,16 +2,7 @@ # See license.txt # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestPermissionLog(UnitTestCase): - """ - Unit tests for PermissionLog. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestPermissionLog(IntegrationTestCase): diff --git a/frappe/core/doctype/prepared_report/test_prepared_report.py b/frappe/core/doctype/prepared_report/test_prepared_report.py index daaed2fd62..d9336ed0e0 100644 --- a/frappe/core/doctype/prepared_report/test_prepared_report.py +++ b/frappe/core/doctype/prepared_report/test_prepared_report.py @@ -7,19 +7,10 @@ from contextlib import contextmanager import frappe from frappe.desk.query_report import generate_report_result, get_report_doc from frappe.query_builder.utils import db_type_is -from frappe.tests import IntegrationTestCase, UnitTestCase, timeout +from frappe.tests import IntegrationTestCase, timeout from frappe.tests.test_query_builder import run_only_if -class UnitTestPreparedReport(UnitTestCase): - """ - Unit tests for PreparedReport. - Use this class for testing individual functions and methods. - """ - - pass - - class TestPreparedReport(IntegrationTestCase): @classmethod def tearDownClass(cls): diff --git a/frappe/core/doctype/recorder/test_recorder.py b/frappe/core/doctype/recorder/test_recorder.py index 3118014d72..e896e1dca5 100644 --- a/frappe/core/doctype/recorder/test_recorder.py +++ b/frappe/core/doctype/recorder/test_recorder.py @@ -8,20 +8,11 @@ import frappe.recorder from frappe.core.doctype.recorder.recorder import _optimize_query, serialize_request from frappe.query_builder.utils import db_type_is from frappe.recorder import get as get_recorder_data -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.tests.test_query_builder import run_only_if from frappe.utils import set_request -class UnitTestRecorder(UnitTestCase): - """ - Unit tests for Recorder. - Use this class for testing individual functions and methods. - """ - - pass - - class TestRecorder(IntegrationTestCase): def setUp(self): self.start_recoder() diff --git a/frappe/core/doctype/recorder_query/test_recorder_query.py b/frappe/core/doctype/recorder_query/test_recorder_query.py index 223fb2d8cb..c1f6dea096 100644 --- a/frappe/core/doctype/recorder_query/test_recorder_query.py +++ b/frappe/core/doctype/recorder_query/test_recorder_query.py @@ -2,16 +2,7 @@ # See license.txt # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestRecorderQuery(UnitTestCase): - """ - Unit tests for RecorderQuery. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestRecorderQuery(IntegrationTestCase): diff --git a/frappe/core/doctype/report/test_report.py b/frappe/core/doctype/report/test_report.py index c2ca7d02f4..73f67b044d 100644 --- a/frappe/core/doctype/report/test_report.py +++ b/frappe/core/doctype/report/test_report.py @@ -11,20 +11,11 @@ from frappe.custom.doctype.customize_form.customize_form import reset_customizat from frappe.desk.query_report import add_total_row, run, save_report from frappe.desk.reportview import delete_report from frappe.desk.reportview import save_report as _save_report -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase EXTRA_TEST_RECORD_DEPENDENCIES = ["User"] -class UnitTestReport(UnitTestCase): - """ - Unit tests for Report. - Use this class for testing individual functions and methods. - """ - - pass - - class TestReport(IntegrationTestCase): @classmethod def setUpClass(cls) -> None: diff --git a/frappe/core/doctype/role/test_role.py b/frappe/core/doctype/role/test_role.py index 17cbed33bb..9a2e5b0410 100644 --- a/frappe/core/doctype/role/test_role.py +++ b/frappe/core/doctype/role/test_role.py @@ -3,16 +3,7 @@ import frappe from frappe.core.doctype.role.role import get_info_based_on_role -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestRole(UnitTestCase): - """ - Unit tests for Role. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestUser(IntegrationTestCase): diff --git a/frappe/core/doctype/role_permission_for_page_and_report/test_role_permission_for_page_and_report.py b/frappe/core/doctype/role_permission_for_page_and_report/test_role_permission_for_page_and_report.py index 3d0f57a355..3297523192 100644 --- a/frappe/core/doctype/role_permission_for_page_and_report/test_role_permission_for_page_and_report.py +++ b/frappe/core/doctype/role_permission_for_page_and_report/test_role_permission_for_page_and_report.py @@ -2,16 +2,7 @@ # See license.txt # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestRolePermissionForPageAndReport(UnitTestCase): - """ - Unit tests for RolePermissionForPageAndReport. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestRolePermissionforPageandReport(IntegrationTestCase): diff --git a/frappe/core/doctype/role_profile/test_role_profile.py b/frappe/core/doctype/role_profile/test_role_profile.py index a80b4f8e87..cca7c688fa 100644 --- a/frappe/core/doctype/role_profile/test_role_profile.py +++ b/frappe/core/doctype/role_profile/test_role_profile.py @@ -1,20 +1,11 @@ # Copyright (c) 2017, Frappe Technologies and Contributors # License: MIT. See LICENSE import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase EXTRA_TEST_RECORD_DEPENDENCIES = ["Role"] -class UnitTestRoleProfile(UnitTestCase): - """ - Unit tests for RoleProfile. - Use this class for testing individual functions and methods. - """ - - pass - - class TestRoleProfile(IntegrationTestCase): def test_make_new_role_profiles(self): frappe.delete_doc_if_exists("Role Profile", "Test 1", force=1) diff --git a/frappe/core/doctype/role_replication/test_role_replication.py b/frappe/core/doctype/role_replication/test_role_replication.py index 5f21597646..7a376ebf86 100644 --- a/frappe/core/doctype/role_replication/test_role_replication.py +++ b/frappe/core/doctype/role_replication/test_role_replication.py @@ -2,16 +2,7 @@ # See license.txt # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestRoleReplication(UnitTestCase): - """ - Unit tests for RoleReplication. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestRoleReplication(IntegrationTestCase): diff --git a/frappe/core/doctype/rq_job/test_rq_job.py b/frappe/core/doctype/rq_job/test_rq_job.py index d9090c0853..8ae4bc427b 100644 --- a/frappe/core/doctype/rq_job/test_rq_job.py +++ b/frappe/core/doctype/rq_job/test_rq_job.py @@ -10,7 +10,7 @@ from rq.job import Job import frappe from frappe.core.doctype.rq_job.rq_job import RQJob, remove_failed_jobs, stop_job from frappe.installer import update_site_config -from frappe.tests import IntegrationTestCase, UnitTestCase, timeout +from frappe.tests import IntegrationTestCase, timeout from frappe.utils import cstr, execute_in_shell from frappe.utils.background_jobs import get_job_status, is_job_enqueued @@ -23,15 +23,6 @@ def wait_for_completion(job: Job): time.sleep(0.2) -class UnitTestRqJob(UnitTestCase): - """ - Unit tests for RqJob. - Use this class for testing individual functions and methods. - """ - - pass - - class TestRQJob(IntegrationTestCase): BG_JOB = "frappe.core.doctype.rq_job.test_rq_job.test_func" @@ -175,7 +166,7 @@ class TestRQJob(IntegrationTestCase): # If this starts failing analyze memory usage using memray or some equivalent tool to find # offending imports/function calls. # Refer this PR: https://github.com/frappe/frappe/pull/21467 - LAST_MEASURED_USAGE = 42 + LAST_MEASURED_USAGE = 46 if frappe.conf.use_mysqlclient: # TEMP: Add extra allowance for running two connectors, this should be rolled back before v16 LAST_MEASURED_USAGE += 2 diff --git a/frappe/core/doctype/rq_worker/test_rq_worker.py b/frappe/core/doctype/rq_worker/test_rq_worker.py index a4c8fdb4c9..11ff951c75 100644 --- a/frappe/core/doctype/rq_worker/test_rq_worker.py +++ b/frappe/core/doctype/rq_worker/test_rq_worker.py @@ -3,16 +3,7 @@ import frappe from frappe.core.doctype.rq_worker.rq_worker import RQWorker -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestRqWorker(UnitTestCase): - """ - Unit tests for RqWorker. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestRQWorker(IntegrationTestCase): diff --git a/frappe/core/doctype/scheduled_job_log/test_scheduled_job_log.py b/frappe/core/doctype/scheduled_job_log/test_scheduled_job_log.py index 86df558726..f306997b25 100644 --- a/frappe/core/doctype/scheduled_job_log/test_scheduled_job_log.py +++ b/frappe/core/doctype/scheduled_job_log/test_scheduled_job_log.py @@ -1,16 +1,7 @@ # Copyright (c) 2019, Frappe Technologies and Contributors # License: MIT. See LICENSE # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestScheduledJobLog(UnitTestCase): - """ - Unit tests for ScheduledJobLog. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestScheduledJobLog(IntegrationTestCase): diff --git a/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py b/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py index dc6606a5b3..dfad54d649 100644 --- a/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py @@ -4,20 +4,11 @@ from datetime import timedelta import frappe from frappe.core.doctype.scheduled_job_type.scheduled_job_type import sync_jobs -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.utils import get_datetime from frappe.utils.data import add_to_date, now_datetime -class UnitTestScheduledJobType(UnitTestCase): - """ - Unit tests for ScheduledJobType. - Use this class for testing individual functions and methods. - """ - - pass - - class TestScheduledJobType(IntegrationTestCase): def setUp(self): frappe.db.rollback() diff --git a/frappe/core/doctype/scheduler_event/test_scheduler_event.py b/frappe/core/doctype/scheduler_event/test_scheduler_event.py index bd1bc5f411..28960db4c3 100644 --- a/frappe/core/doctype/scheduler_event/test_scheduler_event.py +++ b/frappe/core/doctype/scheduler_event/test_scheduler_event.py @@ -2,7 +2,7 @@ # See license.txt # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase # On IntegrationTestCase, the doctype test records and all # link-field test record dependencies are recursively loaded @@ -11,15 +11,6 @@ EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] -class UnitTestSchedulerEvent(UnitTestCase): - """ - Unit tests for SchedulerEvent. - Use this class for testing individual functions and methods. - """ - - pass - - class IntegrationTestSchedulerEvent(IntegrationTestCase): """ Integration tests for SchedulerEvent. diff --git a/frappe/core/doctype/server_script/test_server_script.py b/frappe/core/doctype/server_script/test_server_script.py index 92008fb89f..aa2cfb8a94 100644 --- a/frappe/core/doctype/server_script/test_server_script.py +++ b/frappe/core/doctype/server_script/test_server_script.py @@ -6,7 +6,7 @@ import frappe from frappe.core.doctype.scheduled_job_type.scheduled_job_type import ScheduledJobType, sync_jobs from frappe.core.doctype.server_script.server_script import ServerScript from frappe.frappeclient import FrappeClient, FrappeException -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.utils import get_site_url scripts = [ @@ -108,15 +108,6 @@ doc.save() ] -class UnitTestServerScript(UnitTestCase): - """ - Unit tests for ServerScript. - Use this class for testing individual functions and methods. - """ - - pass - - class TestServerScript(IntegrationTestCase): @classmethod def setUpClass(cls): diff --git a/frappe/core/doctype/session_default_settings/test_session_default_settings.py b/frappe/core/doctype/session_default_settings/test_session_default_settings.py index 471584ab77..0f260beb43 100644 --- a/frappe/core/doctype/session_default_settings/test_session_default_settings.py +++ b/frappe/core/doctype/session_default_settings/test_session_default_settings.py @@ -5,16 +5,7 @@ from frappe.core.doctype.session_default_settings.session_default_settings impor clear_session_defaults, set_session_default_values, ) -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestSessionDefaultSettings(UnitTestCase): - """ - Unit tests for SessionDefaultSettings. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestSessionDefaultSettings(IntegrationTestCase): diff --git a/frappe/core/doctype/sms_settings/test_sms_settings.py b/frappe/core/doctype/sms_settings/test_sms_settings.py index 43d284acfc..35b80b1661 100644 --- a/frappe/core/doctype/sms_settings/test_sms_settings.py +++ b/frappe/core/doctype/sms_settings/test_sms_settings.py @@ -1,15 +1,6 @@ # Copyright (c) 2017, Frappe Technologies and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestSmsSettings(UnitTestCase): - """ - Unit tests for SmsSettings. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestSMSSettings(IntegrationTestCase): diff --git a/frappe/core/doctype/submission_queue/test_submission_queue.py b/frappe/core/doctype/submission_queue/test_submission_queue.py index 2c89230de5..afecd49709 100644 --- a/frappe/core/doctype/submission_queue/test_submission_queue.py +++ b/frappe/core/doctype/submission_queue/test_submission_queue.py @@ -5,22 +5,13 @@ import time import typing import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase, timeout +from frappe.tests import IntegrationTestCase, timeout from frappe.utils.background_jobs import get_queue if typing.TYPE_CHECKING: from rq.job import Job -class UnitTestSubmissionQueue(UnitTestCase): - """ - Unit tests for SubmissionQueue. - Use this class for testing individual functions and methods. - """ - - pass - - class TestSubmissionQueue(IntegrationTestCase): @classmethod def setUpClass(cls): diff --git a/frappe/core/doctype/system_settings/system_settings.json b/frappe/core/doctype/system_settings/system_settings.json index 2061c27a12..844425b4de 100644 --- a/frappe/core/doctype/system_settings/system_settings.json +++ b/frappe/core/doctype/system_settings/system_settings.json @@ -105,7 +105,9 @@ "allow_error_traceback", "enable_telemetry", "search_section", - "link_field_results_limit" + "link_field_results_limit", + "api_logging_section", + "log_api_requests" ], "fields": [ { @@ -714,6 +716,17 @@ "fieldname": "enable_data_masking", "fieldtype": "Check", "label": "Enable Data Masking" + }, + { + "fieldname": "api_logging_section", + "fieldtype": "Section Break", + "label": "API Logging" + }, + { + "default": "0", + "fieldname": "log_api_requests", + "fieldtype": "Check", + "label": "Log API Requests" } ], "icon": "fa fa-cog", diff --git a/frappe/core/doctype/system_settings/system_settings.py b/frappe/core/doctype/system_settings/system_settings.py index f1fe86b8bd..93adb5cb2a 100644 --- a/frappe/core/doctype/system_settings/system_settings.py +++ b/frappe/core/doctype/system_settings/system_settings.py @@ -67,6 +67,7 @@ class SystemSettings(Document): language: DF.Link lifespan_qrcode_image: DF.Int link_field_results_limit: DF.Int + log_api_requests: DF.Check login_with_email_link: DF.Check login_with_email_link_expiry: DF.Int logout_on_password_reset: DF.Check diff --git a/frappe/core/doctype/system_settings/test_system_settings.py b/frappe/core/doctype/system_settings/test_system_settings.py index 706191fd8a..c280275efd 100644 --- a/frappe/core/doctype/system_settings/test_system_settings.py +++ b/frappe/core/doctype/system_settings/test_system_settings.py @@ -1,15 +1,6 @@ # Copyright (c) 2017, Frappe Technologies and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestSystemSettings(UnitTestCase): - """ - Unit tests for SystemSettings. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestSystemSettings(IntegrationTestCase): diff --git a/frappe/core/doctype/transaction_log/test_transaction_log.py b/frappe/core/doctype/transaction_log/test_transaction_log.py index e52ec39481..0b2ffdd508 100644 --- a/frappe/core/doctype/transaction_log/test_transaction_log.py +++ b/frappe/core/doctype/transaction_log/test_transaction_log.py @@ -3,16 +3,7 @@ import hashlib import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestTransactionLog(UnitTestCase): - """ - Unit tests for TransactionLog. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestTransactionLog(IntegrationTestCase): diff --git a/frappe/core/doctype/translation/test_translation.py b/frappe/core/doctype/translation/test_translation.py index 0777b89bf7..33c3200ada 100644 --- a/frappe/core/doctype/translation/test_translation.py +++ b/frappe/core/doctype/translation/test_translation.py @@ -2,16 +2,7 @@ # License: MIT. See LICENSE import frappe from frappe import _ -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestTranslation(UnitTestCase): - """ - Unit tests for Translation. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestTranslation(IntegrationTestCase): diff --git a/frappe/core/doctype/user/test_user.py b/frappe/core/doctype/user/test_user.py index 83f0aa6237..3cb5991363 100644 --- a/frappe/core/doctype/user/test_user.py +++ b/frappe/core/doctype/user/test_user.py @@ -22,7 +22,7 @@ from frappe.core.doctype.user.user import ( from frappe.desk.notifications import extract_mentions from frappe.frappeclient import FrappeClient from frappe.model.delete_doc import delete_doc -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.tests.classes.context_managers import change_settings from frappe.tests.test_api import FrappeAPITestCase from frappe.utils import get_url @@ -30,15 +30,6 @@ from frappe.utils import get_url user_module = frappe.core.doctype.user.user -class UnitTestUser(UnitTestCase): - """ - Unit tests for User. - Use this class for testing individual functions and methods. - """ - - pass - - class TestUser(IntegrationTestCase): def tearDown(self): # disable password strength test diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 04d7daf970..39f8f0e7b0 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -638,6 +638,7 @@ class User(Document): def add_roles(self, *roles): """Add roles to user and save""" self.append_roles(*roles) + # test_user_permission.create_user depends on this self.save() def remove_roles(self, *roles): @@ -1048,7 +1049,7 @@ def sign_up(email: str, full_name: str, redirect_to: str) -> tuple[int, str]: user.insert() # set default signup role as per Portal Settings - default_role = frappe.db.get_single_value("Portal Settings", "default_role") + default_role = frappe.get_single_value("Portal Settings", "default_role") if default_role: user.add_roles(default_role) diff --git a/frappe/core/doctype/user_group/test_user_group.py b/frappe/core/doctype/user_group/test_user_group.py index e146320957..bd8d2dcce8 100644 --- a/frappe/core/doctype/user_group/test_user_group.py +++ b/frappe/core/doctype/user_group/test_user_group.py @@ -1,16 +1,7 @@ # Copyright (c) 2021, Frappe Technologies and Contributors # License: MIT. See LICENSE # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestUserGroup(UnitTestCase): - """ - Unit tests for UserGroup. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestUserGroup(IntegrationTestCase): diff --git a/frappe/core/doctype/user_group_member/test_user_group_member.py b/frappe/core/doctype/user_group_member/test_user_group_member.py index 71bff265c5..079def9ca9 100644 --- a/frappe/core/doctype/user_group_member/test_user_group_member.py +++ b/frappe/core/doctype/user_group_member/test_user_group_member.py @@ -1,16 +1,7 @@ # Copyright (c) 2021, Frappe Technologies and Contributors # License: MIT. See LICENSE # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestUserGroupMember(UnitTestCase): - """ - Unit tests for UserGroupMember. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestUserGroupMember(IntegrationTestCase): diff --git a/frappe/core/doctype/user_permission/test_user_permission.py b/frappe/core/doctype/user_permission/test_user_permission.py index 1c5862dce5..47534ee5d8 100644 --- a/frappe/core/doctype/user_permission/test_user_permission.py +++ b/frappe/core/doctype/user_permission/test_user_permission.py @@ -7,19 +7,10 @@ from frappe.core.doctype.user_permission.user_permission import ( remove_applicable, ) from frappe.permissions import add_permission, has_user_permission -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.website.doctype.blog_post.test_blog_post import make_test_blog -class UnitTestUserPermission(UnitTestCase): - """ - Unit tests for UserPermission. - Use this class for testing individual functions and methods. - """ - - pass - - class TestUserPermission(IntegrationTestCase): def setUp(self): test_users = ( @@ -306,6 +297,7 @@ def create_user(email, *roles): if not roles: roles = ("System Manager",) + # this triggers doc.save, so explicit save is not needed user.add_roles(*roles) return user diff --git a/frappe/core/doctype/user_type/test_user_type.py b/frappe/core/doctype/user_type/test_user_type.py index bba1c37f60..67934ffcc7 100644 --- a/frappe/core/doctype/user_type/test_user_type.py +++ b/frappe/core/doctype/user_type/test_user_type.py @@ -2,16 +2,7 @@ # License: MIT. See LICENSE import frappe from frappe.installer import update_site_config -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestUserType(UnitTestCase): - """ - Unit tests for UserType. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestUserType(IntegrationTestCase): diff --git a/frappe/core/doctype/version/test_version.py b/frappe/core/doctype/version/test_version.py index f57210f46f..b99dc6f046 100644 --- a/frappe/core/doctype/version/test_version.py +++ b/frappe/core/doctype/version/test_version.py @@ -4,19 +4,10 @@ import copy import frappe from frappe.core.doctype.version.version import get_diff -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.tests.utils import make_test_objects -class UnitTestVersion(UnitTestCase): - """ - Unit tests for Version. - Use this class for testing individual functions and methods. - """ - - pass - - class TestVersion(IntegrationTestCase): def test_get_diff(self): frappe.set_user("Administrator") diff --git a/frappe/core/doctype/view_log/test_view_log.py b/frappe/core/doctype/view_log/test_view_log.py index ad64a1a655..87e6d05a80 100644 --- a/frappe/core/doctype/view_log/test_view_log.py +++ b/frappe/core/doctype/view_log/test_view_log.py @@ -1,16 +1,7 @@ # Copyright (c) 2018, Frappe Technologies and Contributors # License: MIT. See LICENSE import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestViewLog(UnitTestCase): - """ - Unit tests for ViewLog. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestViewLog(IntegrationTestCase): diff --git a/frappe/custom/doctype/client_script/test_client_script.py b/frappe/custom/doctype/client_script/test_client_script.py index 62e9378071..6847c858aa 100644 --- a/frappe/custom/doctype/client_script/test_client_script.py +++ b/frappe/custom/doctype/client_script/test_client_script.py @@ -1,15 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestClientScript(UnitTestCase): - """ - Unit tests for ClientScript. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestClientScript(IntegrationTestCase): diff --git a/frappe/custom/doctype/custom_field/test_custom_field.py b/frappe/custom/doctype/custom_field/test_custom_field.py index 16e309edf6..3f0aee90bf 100644 --- a/frappe/custom/doctype/custom_field/test_custom_field.py +++ b/frappe/custom/doctype/custom_field/test_custom_field.py @@ -7,16 +7,7 @@ from frappe.custom.doctype.custom_field.custom_field import ( create_custom_fields, rename_fieldname, ) -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestCustomField(UnitTestCase): - """ - Unit tests for CustomField. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestCustomField(IntegrationTestCase): diff --git a/frappe/custom/doctype/customize_form/test_customize_form.py b/frappe/custom/doctype/customize_form/test_customize_form.py index 90608fd26f..d4fa734d91 100644 --- a/frappe/custom/doctype/customize_form/test_customize_form.py +++ b/frappe/custom/doctype/customize_form/test_customize_form.py @@ -6,21 +6,12 @@ import json import frappe from frappe.core.doctype.doctype.doctype import InvalidFieldNameError from frappe.core.doctype.doctype.test_doctype import new_doctype -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.tests.utils import make_test_records_for_doctype EXTRA_TEST_RECORD_DEPENDENCIES = ["Custom Field", "Property Setter"] -class UnitTestCustomizeForm(UnitTestCase): - """ - Unit tests for CustomizeForm. - Use this class for testing individual functions and methods. - """ - - pass - - class TestCustomizeForm(IntegrationTestCase): def insert_custom_field(self): frappe.delete_doc_if_exists("Custom Field", "Event-custom_test_field") diff --git a/frappe/custom/doctype/doctype_layout/test_doctype_layout.py b/frappe/custom/doctype/doctype_layout/test_doctype_layout.py index 97a97cd158..1a920f42fa 100644 --- a/frappe/custom/doctype/doctype_layout/test_doctype_layout.py +++ b/frappe/custom/doctype/doctype_layout/test_doctype_layout.py @@ -1,16 +1,7 @@ # Copyright (c) 2020, Frappe Technologies and Contributors # License: MIT. See LICENSE # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestDoctypeLayout(UnitTestCase): - """ - Unit tests for DoctypeLayout. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestDocTypeLayout(IntegrationTestCase): diff --git a/frappe/custom/doctype/property_setter/test_property_setter.py b/frappe/custom/doctype/property_setter/test_property_setter.py index 59b7bd4d85..ea1887cf76 100644 --- a/frappe/custom/doctype/property_setter/test_property_setter.py +++ b/frappe/custom/doctype/property_setter/test_property_setter.py @@ -1,15 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestPropertySetter(UnitTestCase): - """ - Unit tests for PropertySetter. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestPropertySetter(IntegrationTestCase): diff --git a/frappe/database/database.py b/frappe/database/database.py index 1e8950775c..1501edef77 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -34,7 +34,16 @@ from frappe.exceptions import DoesNotExistError, ImplicitCommitError from frappe.monitor import get_trace_id from frappe.query_builder import Case from frappe.query_builder.functions import Count -from frappe.utils import CallbackManager, cint, get_datetime, get_table_name, getdate, now, sbool +from frappe.utils import ( + CallbackManager, + cint, + get_datetime, + get_table_name, + getdate, + now, + recursive_defaultdict, + sbool, +) from frappe.utils import cast as cast_fieldtype if TYPE_CHECKING: @@ -112,7 +121,7 @@ class Database: self.transaction_writes = 0 self.auto_commit_on_many_writes = 0 - self.value_cache = {} + self.value_cache = recursive_defaultdict() self.logger = frappe.logger("database") self.logger.setLevel("WARNING") @@ -615,11 +624,8 @@ class Database: user = frappe.db.get_values("User", "test@example.com", "*")[0] """ out = None - cache_key = None - if cache and isinstance(filters, str): - cache_key = (doctype, filters, fieldname) - if cache_key in self.value_cache: - return self.value_cache[cache_key] + if cache and isinstance(filters, str) and fieldname in self.value_cache[doctype][filters]: + return self.value_cache[doctype][filters][fieldname] if distinct: order_by = None @@ -701,8 +707,8 @@ class Database: distinct=distinct, ) - if cache and cache_key: - self.value_cache[cache_key] = out + if cache and isinstance(filters, str): + self.value_cache[doctype][filters][fieldname] = out return out @@ -858,9 +864,6 @@ class Database: frappe.qb.into("Singles").columns("doctype", "field", "value").insert(*singles_data).run(debug=debug) frappe.clear_document_cache(doctype, doctype) - if doctype in self.value_cache: - del self.value_cache[doctype] - def get_single_value(self, doctype: str, fieldname: str, cache: bool = True): """Get property of Single DocType. Cache locally by default @@ -872,10 +875,6 @@ class Database: # Get the default value of the company from the Global Defaults doctype. company = frappe.db.get_single_value('Global Defaults', 'default_company') """ - - if doctype not in self.value_cache: - self.value_cache[doctype] = {} - if cache and fieldname in self.value_cache[doctype]: return self.value_cache[doctype][fieldname] @@ -975,9 +974,6 @@ class Database: query.run(debug=debug) - if dt in self.value_cache: - del self.value_cache[dt] - def bulk_update( self, doctype: str, @@ -1252,10 +1248,10 @@ class Database: def count(self, dt, filters=None, debug=False, cache=False, distinct: bool = True): """Return `COUNT(*)` for given DocType and filters.""" - if cache and not filters: - cache_count = frappe.cache.get_value(f"doctype:count:{dt}") - if cache_count is not None: - return cache_count + cache_key = "COUNT(*)" + if cache and not filters and cache_key in self.value_cache[dt]: + return self.value_cache[dt][cache_key] + count = frappe.qb.get_query( table=dt, filters=filters, @@ -1263,8 +1259,9 @@ class Database: distinct=distinct, validate_filters=True, ).run(debug=debug)[0][0] + if not filters and cache: - frappe.cache.set_value(f"doctype:count:{dt}", count, expires_in_sec=86400) + self.value_cache[dt][cache_key] = count return count def estimate_count(self, doctype: str) -> int: diff --git a/frappe/database/query.py b/frappe/database/query.py index 3f3d146232..8695204f26 100644 --- a/frappe/database/query.py +++ b/frappe/database/query.py @@ -75,6 +75,7 @@ class Engine: self.query = qb.from_(self.table).delete() else: self.query = qb.from_(self.table) + self.query.immutable = False self.apply_fields(fields) self.apply_filters(filters) @@ -95,6 +96,7 @@ class Engine: if group_by: self.query = self.query.groupby(group_by) + self.query.immutable = True return self.query def validate_doctype(self): @@ -141,6 +143,10 @@ class Engine: self.apply_filters(filter) elif isinstance(filter, list | tuple): self.apply_list_filters(filter) + else: + raise ValueError(f"Unknown filter type: {type(filters)}") + else: + raise ValueError(f"Unknown filter type: {type(filters)}") def apply_list_filters(self, filter: list): if len(filter) == 2: @@ -152,6 +158,8 @@ class Engine: elif len(filter) == 4: doctype, field, operator, value = filter self._apply_filter(field, value, operator, doctype) + else: + raise ValueError(f"Unknown filter format: {filter}") def apply_dict_filters(self, filters: dict[str, FilterValue | list]): for field, value in filters.items(): diff --git a/frappe/database/utils.py b/frappe/database/utils.py index 6a54014423..fd4f0092a4 100644 --- a/frappe/database/utils.py +++ b/frappe/database/utils.py @@ -8,11 +8,10 @@ from functools import cached_property, wraps import frappe from frappe.query_builder.builder import MariaDB, Postgres, SQLite from frappe.query_builder.functions import Function -from frappe.types import DocRef Query = str | MariaDB | Postgres | SQLite QueryValues = tuple | list | dict | None -FilterValue = DocRef | str | int | bool +FilterValue = str | int | bool EmptyQueryValues = object() FallBackDateTimeStr = "0001-01-01 00:00:00.000000" @@ -29,8 +28,6 @@ QUERY_TYPE_PATTERN = re.compile(r"\s*([A-Za-z]*)") def convert_to_value(o: FilterValue): - if hasattr(o, "__value__"): - return o.__value__() if isinstance(o, bool): return int(o) return o diff --git a/frappe/deprecation_dumpster.py b/frappe/deprecation_dumpster.py index 686fbc57c1..91aac5526f 100644 --- a/frappe/deprecation_dumpster.py +++ b/frappe/deprecation_dumpster.py @@ -576,7 +576,7 @@ def get_tests_CompatFrappeTestCase(): traceback.print_stack(limit=10) def _rollback_db(): - frappe.db.value_cache = {} + frappe.db.value_cache.clear() frappe.db.rollback() def _restore_thread_locals(flags): diff --git a/frappe/desk/doctype/bulk_update/test_bulk_update.py b/frappe/desk/doctype/bulk_update/test_bulk_update.py index 75e9994606..cafa662bae 100644 --- a/frappe/desk/doctype/bulk_update/test_bulk_update.py +++ b/frappe/desk/doctype/bulk_update/test_bulk_update.py @@ -6,16 +6,7 @@ import time import frappe from frappe.core.doctype.doctype.test_doctype import new_doctype from frappe.desk.doctype.bulk_update.bulk_update import submit_cancel_or_update_docs -from frappe.tests import IntegrationTestCase, UnitTestCase, timeout - - -class UnitTestBulkUpdate(UnitTestCase): - """ - Unit tests for BulkUpdate. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase, timeout class TestBulkUpdate(IntegrationTestCase): diff --git a/frappe/desk/doctype/changelog_feed/test_changelog_feed.py b/frappe/desk/doctype/changelog_feed/test_changelog_feed.py index 7fb856e1fb..3753b69d01 100644 --- a/frappe/desk/doctype/changelog_feed/test_changelog_feed.py +++ b/frappe/desk/doctype/changelog_feed/test_changelog_feed.py @@ -2,16 +2,7 @@ # See license.txt # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestChangelogFeed(UnitTestCase): - """ - Unit tests for ChangelogFeed. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestChangelogFeed(IntegrationTestCase): diff --git a/frappe/desk/doctype/console_log/test_console_log.py b/frappe/desk/doctype/console_log/test_console_log.py index 7370da48ac..ad829d063c 100644 --- a/frappe/desk/doctype/console_log/test_console_log.py +++ b/frappe/desk/doctype/console_log/test_console_log.py @@ -1,16 +1,7 @@ # Copyright (c) 2020, Frappe Technologies and Contributors # License: MIT. See LICENSE # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestConsoleLog(UnitTestCase): - """ - Unit tests for ConsoleLog. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestConsoleLog(IntegrationTestCase): diff --git a/frappe/desk/doctype/custom_html_block/test_custom_html_block.py b/frappe/desk/doctype/custom_html_block/test_custom_html_block.py index d6d8c0181d..d1689e6ec9 100644 --- a/frappe/desk/doctype/custom_html_block/test_custom_html_block.py +++ b/frappe/desk/doctype/custom_html_block/test_custom_html_block.py @@ -2,16 +2,7 @@ # See license.txt # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestCustomHtmlBlock(UnitTestCase): - """ - Unit tests for CustomHtmlBlock. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestCustomHTMLBlock(IntegrationTestCase): diff --git a/frappe/desk/doctype/dashboard/test_dashboard.py b/frappe/desk/doctype/dashboard/test_dashboard.py index 81d347730b..d4a6e13975 100644 --- a/frappe/desk/doctype/dashboard/test_dashboard.py +++ b/frappe/desk/doctype/dashboard/test_dashboard.py @@ -2,19 +2,10 @@ # License: MIT. See LICENSE import frappe from frappe.core.doctype.user.test_user import test_user -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.utils.modules import get_modules_from_all_apps_for_user -class UnitTestDashboard(UnitTestCase): - """ - Unit tests for Dashboard. - Use this class for testing individual functions and methods. - """ - - pass - - class TestDashboard(IntegrationTestCase): def test_permission_query(self): for user in ["Administrator", "test@example.com"]: diff --git a/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py index 5fda816a99..69005c06e6 100644 --- a/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py +++ b/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py @@ -9,20 +9,11 @@ from dateutil.relativedelta import relativedelta import frappe from frappe.core.doctype.doctype.test_doctype import new_doctype from frappe.desk.doctype.dashboard_chart.dashboard_chart import get -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.utils import formatdate, get_last_day, getdate from frappe.utils.dateutils import get_period, get_period_ending -class UnitTestDashboardChart(UnitTestCase): - """ - Unit tests for DashboardChart. - Use this class for testing individual functions and methods. - """ - - pass - - class TestDashboardChart(IntegrationTestCase): def setUp(self): doc = new_doctype( diff --git a/frappe/desk/doctype/dashboard_chart_source/test_dashboard_chart_source.py b/frappe/desk/doctype/dashboard_chart_source/test_dashboard_chart_source.py index 3512b6ba74..6fed0db8cb 100644 --- a/frappe/desk/doctype/dashboard_chart_source/test_dashboard_chart_source.py +++ b/frappe/desk/doctype/dashboard_chart_source/test_dashboard_chart_source.py @@ -1,15 +1,6 @@ # Copyright (c) 2019, Frappe Technologies and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestDashboardChartSource(UnitTestCase): - """ - Unit tests for DashboardChartSource. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestDashboardChartSource(IntegrationTestCase): diff --git a/frappe/desk/doctype/event/test_event.py b/frappe/desk/doctype/event/test_event.py index 7dcc680c04..01cd92d705 100644 --- a/frappe/desk/doctype/event/test_event.py +++ b/frappe/desk/doctype/event/test_event.py @@ -8,19 +8,10 @@ from datetime import date import frappe from frappe.core.utils import find from frappe.desk.doctype.event.event import get_events -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.tests.utils import make_test_objects -class UnitTestEvent(UnitTestCase): - """ - Unit tests for Event. - Use this class for testing individual functions and methods. - """ - - pass - - class TestEvent(IntegrationTestCase): def setUp(self): frappe.db.delete("Event") diff --git a/frappe/desk/doctype/form_tour/test_form_tour.py b/frappe/desk/doctype/form_tour/test_form_tour.py index 65dfbe9187..a37e9d605a 100644 --- a/frappe/desk/doctype/form_tour/test_form_tour.py +++ b/frappe/desk/doctype/form_tour/test_form_tour.py @@ -2,16 +2,7 @@ # License: MIT. See LICENSE # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestFormTour(UnitTestCase): - """ - Unit tests for FormTour. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestFormTour(IntegrationTestCase): diff --git a/frappe/desk/doctype/kanban_board/test_kanban_board.py b/frappe/desk/doctype/kanban_board/test_kanban_board.py index 9d41fe5988..1b2873bbde 100644 --- a/frappe/desk/doctype/kanban_board/test_kanban_board.py +++ b/frappe/desk/doctype/kanban_board/test_kanban_board.py @@ -1,15 +1,6 @@ # Copyright (c) 2015, Frappe Technologies and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestKanbanBoard(UnitTestCase): - """ - Unit tests for KanbanBoard. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestKanbanBoard(IntegrationTestCase): 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 index bed90fb987..5de5b58385 100644 --- a/frappe/desk/doctype/list_view_settings/test_list_view_settings.py +++ b/frappe/desk/doctype/list_view_settings/test_list_view_settings.py @@ -1,16 +1,7 @@ # Copyright (c) 2019, Frappe Technologies and Contributors # License: MIT. See LICENSE # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestListViewSettings(UnitTestCase): - """ - Unit tests for ListViewSettings. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestListViewSettings(IntegrationTestCase): diff --git a/frappe/desk/doctype/module_onboarding/test_module_onboarding.py b/frappe/desk/doctype/module_onboarding/test_module_onboarding.py index cc27d870ed..fc23187314 100644 --- a/frappe/desk/doctype/module_onboarding/test_module_onboarding.py +++ b/frappe/desk/doctype/module_onboarding/test_module_onboarding.py @@ -1,16 +1,7 @@ # Copyright (c) 2020, Frappe Technologies and Contributors # License: MIT. See LICENSE # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestModuleOnboarding(UnitTestCase): - """ - Unit tests for ModuleOnboarding. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestModuleOnboarding(IntegrationTestCase): diff --git a/frappe/desk/doctype/note/test_note.py b/frappe/desk/doctype/note/test_note.py index 8a7e6f40ca..bc0f8f51e2 100644 --- a/frappe/desk/doctype/note/test_note.py +++ b/frappe/desk/doctype/note/test_note.py @@ -2,16 +2,7 @@ # License: MIT. See LICENSE import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestNote(UnitTestCase): - """ - Unit tests for Note. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestNote(IntegrationTestCase): diff --git a/frappe/desk/doctype/notification_log/test_notification_log.py b/frappe/desk/doctype/notification_log/test_notification_log.py index d75bd59a69..84de189e29 100644 --- a/frappe/desk/doctype/notification_log/test_notification_log.py +++ b/frappe/desk/doctype/notification_log/test_notification_log.py @@ -3,16 +3,7 @@ import frappe from frappe.core.doctype.user.user import get_system_users from frappe.desk.form.assign_to import add as assign_task -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestNotificationLog(UnitTestCase): - """ - Unit tests for NotificationLog. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestNotificationLog(IntegrationTestCase): diff --git a/frappe/desk/doctype/notification_settings/test_notification_settings.py b/frappe/desk/doctype/notification_settings/test_notification_settings.py index acf2d93798..265656d2aa 100644 --- a/frappe/desk/doctype/notification_settings/test_notification_settings.py +++ b/frappe/desk/doctype/notification_settings/test_notification_settings.py @@ -2,16 +2,7 @@ # See license.txt # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestNotificationSettings(UnitTestCase): - """ - Unit tests for NotificationSettings. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestNotificationSettings(IntegrationTestCase): diff --git a/frappe/desk/doctype/number_card/test_number_card.py b/frappe/desk/doctype/number_card/test_number_card.py index dc1a16dc94..ee78aeb7f6 100644 --- a/frappe/desk/doctype/number_card/test_number_card.py +++ b/frappe/desk/doctype/number_card/test_number_card.py @@ -1,16 +1,7 @@ # Copyright (c) 2020, Frappe Technologies and Contributors # License: MIT. See LICENSE # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestNumberCard(UnitTestCase): - """ - Unit tests for NumberCard. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestNumberCard(IntegrationTestCase): diff --git a/frappe/desk/doctype/onboarding_permission/test_onboarding_permission.py b/frappe/desk/doctype/onboarding_permission/test_onboarding_permission.py index 2bac40f723..648a48bac2 100644 --- a/frappe/desk/doctype/onboarding_permission/test_onboarding_permission.py +++ b/frappe/desk/doctype/onboarding_permission/test_onboarding_permission.py @@ -1,16 +1,7 @@ # Copyright (c) 2020, Frappe Technologies and Contributors # License: MIT. See LICENSE # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestOnboardingPermission(UnitTestCase): - """ - Unit tests for OnboardingPermission. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestOnboardingPermission(IntegrationTestCase): diff --git a/frappe/desk/doctype/onboarding_step/test_onboarding_step.py b/frappe/desk/doctype/onboarding_step/test_onboarding_step.py index b6b78bf2ef..c4a8497a67 100644 --- a/frappe/desk/doctype/onboarding_step/test_onboarding_step.py +++ b/frappe/desk/doctype/onboarding_step/test_onboarding_step.py @@ -1,16 +1,7 @@ # Copyright (c) 2020, Frappe Technologies and Contributors # License: MIT. See LICENSE # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestOnboardingStep(UnitTestCase): - """ - Unit tests for OnboardingStep. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestOnboardingStep(IntegrationTestCase): diff --git a/frappe/desk/doctype/system_console/test_system_console.py b/frappe/desk/doctype/system_console/test_system_console.py index c3e187cfc3..7be0a73c28 100644 --- a/frappe/desk/doctype/system_console/test_system_console.py +++ b/frappe/desk/doctype/system_console/test_system_console.py @@ -1,16 +1,7 @@ # Copyright (c) 2020, Frappe Technologies and Contributors # License: MIT. See LICENSE import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestSystemConsole(UnitTestCase): - """ - Unit tests for SystemConsole. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestSystemConsole(IntegrationTestCase): diff --git a/frappe/desk/doctype/system_health_report/test_system_health_report.py b/frappe/desk/doctype/system_health_report/test_system_health_report.py index b4f8ea018c..2bd4f79359 100644 --- a/frappe/desk/doctype/system_health_report/test_system_health_report.py +++ b/frappe/desk/doctype/system_health_report/test_system_health_report.py @@ -2,16 +2,7 @@ # See license.txt import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestSystemHealthReport(UnitTestCase): - """ - Unit tests for SystemHealthReport. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestSystemHealthReport(IntegrationTestCase): diff --git a/frappe/desk/doctype/tag/test_tag.py b/frappe/desk/doctype/tag/test_tag.py index a009730b17..2d4d7d511c 100644 --- a/frappe/desk/doctype/tag/test_tag.py +++ b/frappe/desk/doctype/tag/test_tag.py @@ -1,16 +1,7 @@ import frappe from frappe.desk.doctype.tag.tag import add_tag from frappe.desk.reportview import get_stats -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestTag(UnitTestCase): - """ - Unit tests for Tag. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestTag(IntegrationTestCase): diff --git a/frappe/desk/doctype/tag_link/test_tag_link.py b/frappe/desk/doctype/tag_link/test_tag_link.py index 0a71b780ad..563c0a0322 100644 --- a/frappe/desk/doctype/tag_link/test_tag_link.py +++ b/frappe/desk/doctype/tag_link/test_tag_link.py @@ -1,16 +1,7 @@ # Copyright (c) 2019, Frappe Technologies and Contributors # License: MIT. See LICENSE # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestTagLink(UnitTestCase): - """ - Unit tests for TagLink. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestTagLink(IntegrationTestCase): diff --git a/frappe/desk/doctype/todo/test_todo.py b/frappe/desk/doctype/todo/test_todo.py index 3cf5d2a45c..6fa713a279 100644 --- a/frappe/desk/doctype/todo/test_todo.py +++ b/frappe/desk/doctype/todo/test_todo.py @@ -4,20 +4,11 @@ import frappe from frappe.core.doctype.doctype.doctype import clear_permissions_cache from frappe.model.db_query import DatabaseQuery from frappe.permissions import add_permission, reset_perms -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase EXTRA_TEST_RECORD_DEPENDENCIES = ["User"] -class UnitTestTodo(UnitTestCase): - """ - Unit tests for Todo. - Use this class for testing individual functions and methods. - """ - - pass - - class TestToDo(IntegrationTestCase): def test_delete(self): todo = frappe.get_doc(doctype="ToDo", description="test todo", assigned_by="Administrator").insert() diff --git a/frappe/desk/doctype/workspace/test_workspace.py b/frappe/desk/doctype/workspace/test_workspace.py index ff2bce69f4..3a3e0801b3 100644 --- a/frappe/desk/doctype/workspace/test_workspace.py +++ b/frappe/desk/doctype/workspace/test_workspace.py @@ -1,16 +1,7 @@ # Copyright (c) 2020, Frappe Technologies and Contributors # License: MIT. See LICENSE import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestWorkspace(UnitTestCase): - """ - Unit tests for Workspace. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestWorkspace(IntegrationTestCase): diff --git a/frappe/desk/doctype/workspace_settings/test_workspace_settings.py b/frappe/desk/doctype/workspace_settings/test_workspace_settings.py index a3c25696b4..219ba291c7 100644 --- a/frappe/desk/doctype/workspace_settings/test_workspace_settings.py +++ b/frappe/desk/doctype/workspace_settings/test_workspace_settings.py @@ -2,16 +2,7 @@ # See license.txt # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestWorkspaceSettings(UnitTestCase): - """ - Unit tests for WorkspaceSettings. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestWorkspaceSettings(IntegrationTestCase): diff --git a/frappe/desk/form/linked_with.py b/frappe/desk/form/linked_with.py index 0c58b325a2..7dbea54d95 100644 --- a/frappe/desk/form/linked_with.py +++ b/frappe/desk/form/linked_with.py @@ -42,7 +42,7 @@ def get_submitted_linked_docs(doctype: str, name: str, ignore_doctypes_on_cancel if isinstance(ignore_doctypes_on_cancel_all, str): ignore_doctypes_on_cancel_all = json.loads(ignore_doctypes_on_cancel_all) - frappe.has_permission(doctype, doc=name) + frappe.has_permission(doctype, doc=name, throw=True) tree = SubmittableDocumentTree(doctype, name) visited_documents = tree.get_all_children(ignore_doctypes_on_cancel_all) docs = [] @@ -523,7 +523,7 @@ def get_linked_docs(doctype: str, name: str, linkinfo: dict | None = None) -> di @frappe.whitelist() def get(doctype, docname): - frappe.has_permission(doctype, doc=docname) + frappe.has_permission(doctype, doc=docname, throw=True) linked_doctypes = get_linked_doctypes(doctype=doctype) return get_linked_docs(doctype=doctype, name=docname, linkinfo=linked_doctypes) diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py index 7810bba752..104211759a 100644 --- a/frappe/desk/query_report.py +++ b/frappe/desk/query_report.py @@ -708,13 +708,6 @@ def has_match( # each doctype could have multiple conflicting user permission doctypes, hence using OR # so that even if one of the sets allows a match, it is true - - if match: - if not frappe.has_permission( - doctype=ref_doctype, ptype="read", throw=False, ignore_share_permissions=True - ): - match = False - matched_for_doctype = matched_for_doctype or match if matched_for_doctype: diff --git a/frappe/email/doctype/auto_email_report/test_auto_email_report.py b/frappe/email/doctype/auto_email_report/test_auto_email_report.py index 5bf7db29ff..ed44d72ed0 100644 --- a/frappe/email/doctype/auto_email_report/test_auto_email_report.py +++ b/frappe/email/doctype/auto_email_report/test_auto_email_report.py @@ -3,20 +3,11 @@ import json import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.utils import add_to_date, get_link_to_form, today from frappe.utils.data import is_html -class UnitTestAutoEmailReport(UnitTestCase): - """ - Unit tests for AutoEmailReport. - Use this class for testing individual functions and methods. - """ - - pass - - class TestAutoEmailReport(IntegrationTestCase): def test_auto_email(self): frappe.delete_doc("Auto Email Report", "Permitted Documents For User") diff --git a/frappe/email/doctype/document_follow/test_document_follow.py b/frappe/email/doctype/document_follow/test_document_follow.py index a74b214715..6612e4fdc1 100644 --- a/frappe/email/doctype/document_follow/test_document_follow.py +++ b/frappe/email/doctype/document_follow/test_document_follow.py @@ -11,16 +11,7 @@ from frappe.desk.like import toggle_like from frappe.query_builder import DocType from frappe.query_builder.functions import Cast_ from frappe.share import add as share -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestDocumentFollow(UnitTestCase): - """ - Unit tests for DocumentFollow. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestDocumentFollow(IntegrationTestCase): diff --git a/frappe/email/doctype/email_account/test_email_account.py b/frappe/email/doctype/email_account/test_email_account.py index 18b1637845..af5749b309 100644 --- a/frappe/email/doctype/email_account/test_email_account.py +++ b/frappe/email/doctype/email_account/test_email_account.py @@ -13,16 +13,7 @@ from frappe.desk.form.load import get_attachments from frappe.email.doctype.email_account.email_account import notify_unreplied from frappe.email.email_body import get_message_id from frappe.email.receive import Email, InboundMail, SentEmailInInboxError -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestEmailAccount(UnitTestCase): - """ - Unit tests for EmailAccount. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestEmailAccount(IntegrationTestCase): diff --git a/frappe/email/doctype/email_domain/test_email_domain.py b/frappe/email/doctype/email_domain/test_email_domain.py index 2f70f27e11..314fbcdc87 100644 --- a/frappe/email/doctype/email_domain/test_email_domain.py +++ b/frappe/email/doctype/email_domain/test_email_domain.py @@ -1,19 +1,10 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.tests.utils import make_test_objects -class UnitTestEmailDomain(UnitTestCase): - """ - Unit tests for EmailDomain. - Use this class for testing individual functions and methods. - """ - - pass - - class TestDomain(IntegrationTestCase): def setUp(self): make_test_objects("Email Domain", reset=True) diff --git a/frappe/email/doctype/email_flag_queue/test_email_flag_queue.py b/frappe/email/doctype/email_flag_queue/test_email_flag_queue.py index 7d3effb56a..f4a00cf57c 100644 --- a/frappe/email/doctype/email_flag_queue/test_email_flag_queue.py +++ b/frappe/email/doctype/email_flag_queue/test_email_flag_queue.py @@ -1,15 +1,6 @@ # Copyright (c) 2015, Frappe Technologies and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestEmailFlagQueue(UnitTestCase): - """ - Unit tests for EmailFlagQueue. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestEmailFlagQueue(IntegrationTestCase): diff --git a/frappe/email/doctype/email_group/test_email_group.py b/frappe/email/doctype/email_group/test_email_group.py index 9774eeac9e..781d8b297f 100644 --- a/frappe/email/doctype/email_group/test_email_group.py +++ b/frappe/email/doctype/email_group/test_email_group.py @@ -1,19 +1,10 @@ # Copyright (c) 2015, Frappe Technologies and Contributors # License: MIT. See LICENSE import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.utils import validate_url -class UnitTestEmailGroup(UnitTestCase): - """ - Unit tests for EmailGroup. - Use this class for testing individual functions and methods. - """ - - pass - - class TestEmailGroup(IntegrationTestCase): def test_welcome_url(self): email_group = frappe.new_doc("Email Group") diff --git a/frappe/email/doctype/email_group_member/test_email_group_member.py b/frappe/email/doctype/email_group_member/test_email_group_member.py index dd67ea7df6..8f532d684d 100644 --- a/frappe/email/doctype/email_group_member/test_email_group_member.py +++ b/frappe/email/doctype/email_group_member/test_email_group_member.py @@ -1,15 +1,6 @@ # Copyright (c) 2015, Frappe Technologies and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestEmailGroupMember(UnitTestCase): - """ - Unit tests for EmailGroupMember. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestEmailGroupMember(IntegrationTestCase): diff --git a/frappe/email/doctype/email_queue/email_queue.py b/frappe/email/doctype/email_queue/email_queue.py index 1b42df7b2e..526188f050 100644 --- a/frappe/email/doctype/email_queue/email_queue.py +++ b/frappe/email/doctype/email_queue/email_queue.py @@ -514,6 +514,7 @@ class QueueBuilder: with_container=False, email_read_tracker_url=None, x_priority: Literal[1, 3, 5] = 3, + email_headers=None, ): """Add email to sending queue (Email Queue) @@ -540,6 +541,7 @@ class QueueBuilder: :param with_container: Wraps email inside styled container :param email_read_tracker_url: A URL for tracking whether an email is read by the recipient. :param x_priority: 1 = HIGHEST, 3 = NORMAL, 5 = LOWEST + :param email_headers: Additional headers to be added in the email, e.g. {"X-Custom-Header": "value"} or {"Custom-Header": "value"}. Automatically prepends "X-" to the header name if not present. """ self._unsubscribe_method = unsubscribe_method @@ -576,6 +578,7 @@ class QueueBuilder: self.inline_images = inline_images self.print_letterhead = print_letterhead self.email_read_tracker_url = email_read_tracker_url + self.email_headers = email_headers @property def unsubscribe_method(self): @@ -728,6 +731,10 @@ class QueueBuilder: ) mail.set_message_id(self.message_id, self.is_notification) + + if self.email_headers: + mail.add_headers(self.email_headers) + if self.read_receipt: mail.msg_root["Disposition-Notification-To"] = self.sender if self.in_reply_to: diff --git a/frappe/email/doctype/email_queue/test_email_queue.py b/frappe/email/doctype/email_queue/test_email_queue.py index f2aec558e4..5dc76096f4 100644 --- a/frappe/email/doctype/email_queue/test_email_queue.py +++ b/frappe/email/doctype/email_queue/test_email_queue.py @@ -4,16 +4,7 @@ import textwrap import frappe from frappe.email.doctype.email_queue.email_queue import SendMailContext, get_email_retry_limit -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestEmailQueue(UnitTestCase): - """ - Unit tests for EmailQueue. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestEmailQueue(IntegrationTestCase): diff --git a/frappe/email/doctype/email_rule/test_email_rule.py b/frappe/email/doctype/email_rule/test_email_rule.py index cea1928ae7..4f4c7cdcb8 100644 --- a/frappe/email/doctype/email_rule/test_email_rule.py +++ b/frappe/email/doctype/email_rule/test_email_rule.py @@ -1,15 +1,6 @@ # Copyright (c) 2017, Frappe Technologies and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestEmailRule(UnitTestCase): - """ - Unit tests for EmailRule. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestEmailRule(IntegrationTestCase): diff --git a/frappe/email/doctype/email_template/test_email_template.py b/frappe/email/doctype/email_template/test_email_template.py index 5073e54359..273b7b3c93 100644 --- a/frappe/email/doctype/email_template/test_email_template.py +++ b/frappe/email/doctype/email_template/test_email_template.py @@ -1,15 +1,6 @@ # Copyright (c) 2018, Frappe Technologies and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestEmailTemplate(UnitTestCase): - """ - Unit tests for EmailTemplate. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestEmailTemplate(IntegrationTestCase): diff --git a/frappe/email/doctype/email_unsubscribe/test_email_unsubscribe.py b/frappe/email/doctype/email_unsubscribe/test_email_unsubscribe.py index 0ea9468919..42082a5d69 100644 --- a/frappe/email/doctype/email_unsubscribe/test_email_unsubscribe.py +++ b/frappe/email/doctype/email_unsubscribe/test_email_unsubscribe.py @@ -1,15 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestEmailUnsubscribe(UnitTestCase): - """ - Unit tests for EmailUnsubscribe. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestEmailUnsubscribe(IntegrationTestCase): diff --git a/frappe/email/doctype/notification/test_notification.py b/frappe/email/doctype/notification/test_notification.py index 0ad2e9dac9..6ec5926261 100644 --- a/frappe/email/doctype/notification/test_notification.py +++ b/frappe/email/doctype/notification/test_notification.py @@ -8,7 +8,7 @@ import frappe import frappe.utils import frappe.utils.scheduler from frappe.desk.form import assign_to -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from .notification import trigger_notifications @@ -25,15 +25,6 @@ def get_test_notification(config): frappe.db.commit() -class UnitTestNotification(UnitTestCase): - """ - Unit tests for Notification. - Use this class for testing individual functions and methods. - """ - - pass - - class TestNotification(IntegrationTestCase): def setUp(self): frappe.db.delete("Email Queue") diff --git a/frappe/email/doctype/unhandled_email/test_unhandled_email.py b/frappe/email/doctype/unhandled_email/test_unhandled_email.py index f7c148e8ec..dbafdc5d59 100644 --- a/frappe/email/doctype/unhandled_email/test_unhandled_email.py +++ b/frappe/email/doctype/unhandled_email/test_unhandled_email.py @@ -1,15 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestUnhandledEmail(UnitTestCase): - """ - Unit tests for UnhandledEmail. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestUnhandledEmail(IntegrationTestCase): diff --git a/frappe/email/email_body.py b/frappe/email/email_body.py index afc9890b3d..7a2256fdda 100755 --- a/frappe/email/email_body.py +++ b/frappe/email/email_body.py @@ -12,6 +12,7 @@ from email.mime.multipart import MIMEMultipart from typing import TYPE_CHECKING import frappe +from frappe import _ from frappe.email.doctype.email_account.email_account import EmailAccount from frappe.utils import ( cint, @@ -315,6 +316,16 @@ class EMail: """Used to send the Message-Id of a received email back as In-Reply-To""" self.set_header("In-Reply-To", in_reply_to) + def add_headers(self, headers): + """Add custom headers to the email""" + if not isinstance(headers, dict): + frappe.throw(_("Headers must be a dictionary")) + + for key, value in headers.items(): + if value is not None: + key = "X-" + key if not key.startswith("X-") else key + self.set_header(key, value) + def make(self): """build into msg_root""" headers = { diff --git a/frappe/email/receive.py b/frappe/email/receive.py index f94d7a1c1c..febbdc63ee 100644 --- a/frappe/email/receive.py +++ b/frappe/email/receive.py @@ -67,6 +67,8 @@ class EmailServer: """Wrapper for POP server to pull emails.""" def __init__(self, args=None): + self.retry_limit = 3 + self.retry_count = 0 self.settings = args or frappe._dict() def connect(self): @@ -177,7 +179,7 @@ class EmailServer: for i, uid in enumerate(email_list[:100]): try: - self.retrieve_message(uid, i + 1) + self.retrieve_message(uid, i + 1, folder) except (_socket.timeout, LoginLimitExceeded): # get whatever messages were retrieved break @@ -258,7 +260,7 @@ class EmailServer: return match[0] if match else None - def retrieve_message(self, uid, msg_num): + def retrieve_message(self, uid, msg_num, folder): try: if cint(self.settings.use_imap): status, message = self.imap.uid("fetch", uid, "(BODY.PEEK[] BODY.PEEK[HEADER] FLAGS)") @@ -272,7 +274,11 @@ class EmailServer: except _socket.timeout: # propagate this error to break the loop raise - + except imaplib.IMAP4.abort: + if self.retry_count < self.retry_limit: + self.connect() + self.get_messages(folder) + self.retry_count += 1 except Exception as e: if self.has_login_limit_exceeded(e): raise LoginLimitExceeded(e) from e @@ -633,6 +639,9 @@ class InboundMail(Email): communication = self.is_exist_in_system() if communication: communication.update_db(uid=self.uid) + data = self.as_dict() + if data.get("bcc") and not communication.bcc: + communication.update_db(bcc=data.get("bcc")) communication.reload() return communication @@ -901,6 +910,7 @@ class InboundMail(Email): "sender": self.from_email, "recipients": self.decode_email(self.mail.get("To") or ""), "cc": self.decode_email(self.mail.get("CC") or ""), + "bcc": self.decode_email(self.mail.get("BCC") or ""), "email_account": self.email_account.name, "communication_medium": "Email", "uid": self.uid, diff --git a/frappe/geo/doctype/country/test_country.py b/frappe/geo/doctype/country/test_country.py index a503e49663..d7625e7061 100644 --- a/frappe/geo/doctype/country/test_country.py +++ b/frappe/geo/doctype/country/test_country.py @@ -7,7 +7,7 @@ from frappe.geo.doctype.country.country import ( import_country_and_currency, ) from frappe.geo.doctype.currency.currency import enable_default_currencies -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase def get_table_snapshot(doctype): @@ -20,15 +20,6 @@ def get_table_snapshot(doctype): return data -class UnitTestCountry(UnitTestCase): - """ - Unit tests for Country. - Use this class for testing individual functions and methods. - """ - - pass - - class TestCountry(IntegrationTestCase): def test_bulk_insert_correctness(self): def clear_tables(): diff --git a/frappe/geo/doctype/currency/test_currency.py b/frappe/geo/doctype/currency/test_currency.py index 0b37b451f2..e0a617cd7c 100644 --- a/frappe/geo/doctype/currency/test_currency.py +++ b/frappe/geo/doctype/currency/test_currency.py @@ -4,16 +4,7 @@ # pre loaded import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestCurrency(UnitTestCase): - """ - Unit tests for Currency. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestUser(IntegrationTestCase): diff --git a/frappe/hooks.py b/frappe/hooks.py index 09539b6e47..bf4d8ac744 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -569,6 +569,7 @@ default_log_clearing_doctypes = { "Activity Log": 90, "Route History": 90, "OAuth Bearer Token": 30, + "API Request Log": 90, } # These keys will not be erased when doing frappe.clear_cache() diff --git a/frappe/integrations/doctype/connected_app/test_connected_app.py b/frappe/integrations/doctype/connected_app/test_connected_app.py index 2079b3a521..f063b2e920 100644 --- a/frappe/integrations/doctype/connected_app/test_connected_app.py +++ b/frappe/integrations/doctype/connected_app/test_connected_app.py @@ -8,7 +8,7 @@ import frappe from frappe.integrations.doctype.social_login_key.test_social_login_key import ( create_or_update_social_login_key, ) -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase def get_user(usr, pwd): @@ -48,15 +48,6 @@ def get_oauth_client(): return oauth_client -class UnitTestConnectedApp(UnitTestCase): - """ - Unit tests for ConnectedApp. - Use this class for testing individual functions and methods. - """ - - pass - - class TestConnectedApp(IntegrationTestCase): def setUp(self): """Set up a Connected App that connects to our own oAuth provider. diff --git a/frappe/integrations/doctype/dropbox_settings/test_dropbox_settings.py b/frappe/integrations/doctype/dropbox_settings/test_dropbox_settings.py index 0b56aa98d7..dedaa2d861 100644 --- a/frappe/integrations/doctype/dropbox_settings/test_dropbox_settings.py +++ b/frappe/integrations/doctype/dropbox_settings/test_dropbox_settings.py @@ -1,16 +1,7 @@ # Copyright (c) 2019, Frappe Technologies and Contributors # License: MIT. See LICENSE # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestDropboxSettings(UnitTestCase): - """ - Unit tests for DropboxSettings. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestDropboxSettings(IntegrationTestCase): diff --git a/frappe/integrations/doctype/geolocation_settings/test_geolocation_settings.py b/frappe/integrations/doctype/geolocation_settings/test_geolocation_settings.py index cf0c2f7231..c656852f35 100644 --- a/frappe/integrations/doctype/geolocation_settings/test_geolocation_settings.py +++ b/frappe/integrations/doctype/geolocation_settings/test_geolocation_settings.py @@ -2,16 +2,7 @@ # See license.txt # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestGeolocationSettings(UnitTestCase): - """ - Unit tests for GeolocationSettings. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestGeolocationSettings(IntegrationTestCase): diff --git a/frappe/integrations/doctype/google_contacts/test_google_contacts.py b/frappe/integrations/doctype/google_contacts/test_google_contacts.py index 667b568cd1..3e96984fe8 100644 --- a/frappe/integrations/doctype/google_contacts/test_google_contacts.py +++ b/frappe/integrations/doctype/google_contacts/test_google_contacts.py @@ -2,16 +2,7 @@ # See license.txt # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestGoogleContacts(UnitTestCase): - """ - Unit tests for GoogleContacts. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestGoogleContacts(IntegrationTestCase): diff --git a/frappe/integrations/doctype/google_drive/test_google_drive.py b/frappe/integrations/doctype/google_drive/test_google_drive.py index 5d499da680..90c400ba80 100644 --- a/frappe/integrations/doctype/google_drive/test_google_drive.py +++ b/frappe/integrations/doctype/google_drive/test_google_drive.py @@ -1,16 +1,7 @@ # Copyright (c) 2019, Frappe Technologies and Contributors # License: MIT. See LICENSE # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestGoogleDrive(UnitTestCase): - """ - Unit tests for GoogleDrive. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestGoogleDrive(IntegrationTestCase): diff --git a/frappe/integrations/doctype/google_settings/test_google_settings.py b/frappe/integrations/doctype/google_settings/test_google_settings.py index dccf03c1c3..2a2899c50c 100644 --- a/frappe/integrations/doctype/google_settings/test_google_settings.py +++ b/frappe/integrations/doctype/google_settings/test_google_settings.py @@ -2,20 +2,11 @@ # License: MIT. See LICENSE import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from .google_settings import get_file_picker_settings -class UnitTestGoogleSettings(UnitTestCase): - """ - Unit tests for GoogleSettings. - Use this class for testing individual functions and methods. - """ - - pass - - class TestGoogleSettings(IntegrationTestCase): def setUp(self): settings = frappe.get_single("Google Settings") diff --git a/frappe/integrations/doctype/integration_request/test_integration_request.py b/frappe/integrations/doctype/integration_request/test_integration_request.py index f7266f7563..c38fff6821 100644 --- a/frappe/integrations/doctype/integration_request/test_integration_request.py +++ b/frappe/integrations/doctype/integration_request/test_integration_request.py @@ -1,16 +1,7 @@ # Copyright (c) 2015, Frappe Technologies and Contributors # License: MIT. See LICENSE import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestIntegrationRequest(UnitTestCase): - """ - Unit tests for IntegrationRequest. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestIntegrationRequest(IntegrationTestCase): diff --git a/frappe/integrations/doctype/oauth_authorization_code/test_oauth_authorization_code.py b/frappe/integrations/doctype/oauth_authorization_code/test_oauth_authorization_code.py index ca36fdee95..b342fe4c35 100644 --- a/frappe/integrations/doctype/oauth_authorization_code/test_oauth_authorization_code.py +++ b/frappe/integrations/doctype/oauth_authorization_code/test_oauth_authorization_code.py @@ -1,16 +1,7 @@ # Copyright (c) 2015, Frappe Technologies and Contributors # License: MIT. See LICENSE import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestOauthAuthorizationCode(UnitTestCase): - """ - Unit tests for OauthAuthorizationCode. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestOAuthAuthorizationCode(IntegrationTestCase): diff --git a/frappe/integrations/doctype/oauth_bearer_token/test_oauth_bearer_token.py b/frappe/integrations/doctype/oauth_bearer_token/test_oauth_bearer_token.py index c3c6fff5c4..731352920e 100644 --- a/frappe/integrations/doctype/oauth_bearer_token/test_oauth_bearer_token.py +++ b/frappe/integrations/doctype/oauth_bearer_token/test_oauth_bearer_token.py @@ -1,16 +1,7 @@ # Copyright (c) 2015, Frappe Technologies and Contributors # License: MIT. See LICENSE import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestOauthBearerToken(UnitTestCase): - """ - Unit tests for OauthBearerToken. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestOAuthBearerToken(IntegrationTestCase): diff --git a/frappe/integrations/doctype/oauth_client/test_oauth_client.py b/frappe/integrations/doctype/oauth_client/test_oauth_client.py index 136ce8adb4..f92eebafdc 100644 --- a/frappe/integrations/doctype/oauth_client/test_oauth_client.py +++ b/frappe/integrations/doctype/oauth_client/test_oauth_client.py @@ -1,16 +1,7 @@ # Copyright (c) 2015, Frappe Technologies and Contributors # License: MIT. See LICENSE import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestOauthClient(UnitTestCase): - """ - Unit tests for OauthClient. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestOAuthClient(IntegrationTestCase): diff --git a/frappe/integrations/doctype/push_notification_settings/test_push_notification_settings.py b/frappe/integrations/doctype/push_notification_settings/test_push_notification_settings.py index 43197862c0..b6c96313c3 100644 --- a/frappe/integrations/doctype/push_notification_settings/test_push_notification_settings.py +++ b/frappe/integrations/doctype/push_notification_settings/test_push_notification_settings.py @@ -2,16 +2,7 @@ # See license.txt # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestPushNotificationSettings(UnitTestCase): - """ - Unit tests for PushNotificationSettings. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestPushNotificationSettings(IntegrationTestCase): diff --git a/frappe/integrations/doctype/s3_backup_settings/test_s3_backup_settings.py b/frappe/integrations/doctype/s3_backup_settings/test_s3_backup_settings.py index 8a5688ac8e..ff40ab8a11 100755 --- a/frappe/integrations/doctype/s3_backup_settings/test_s3_backup_settings.py +++ b/frappe/integrations/doctype/s3_backup_settings/test_s3_backup_settings.py @@ -1,15 +1,6 @@ # Copyright (c) 2017, Frappe Technologies and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestS3BackupSettings(UnitTestCase): - """ - Unit tests for S3BackupSettings. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestS3BackupSettings(IntegrationTestCase): diff --git a/frappe/integrations/doctype/slack_webhook_url/test_slack_webhook_url.py b/frappe/integrations/doctype/slack_webhook_url/test_slack_webhook_url.py index 62cfed6827..cb0d11fab0 100644 --- a/frappe/integrations/doctype/slack_webhook_url/test_slack_webhook_url.py +++ b/frappe/integrations/doctype/slack_webhook_url/test_slack_webhook_url.py @@ -1,15 +1,6 @@ # Copyright (c) 2018, Frappe Technologies and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestSlackWebhookUrl(UnitTestCase): - """ - Unit tests for SlackWebhookUrl. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestSlackWebhookURL(IntegrationTestCase): diff --git a/frappe/integrations/doctype/social_login_key/test_social_login_key.py b/frappe/integrations/doctype/social_login_key/test_social_login_key.py index 09e8fbf6c7..64060b3a0e 100644 --- a/frappe/integrations/doctype/social_login_key/test_social_login_key.py +++ b/frappe/integrations/doctype/social_login_key/test_social_login_key.py @@ -7,22 +7,13 @@ from rauth import OAuth2Service import frappe from frappe.auth import CookieManager, LoginManager from frappe.integrations.doctype.social_login_key.social_login_key import BaseUrlNotSetError -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.utils import set_request from frappe.utils.oauth import login_via_oauth2 TEST_GITHUB_USER = "githublogin@example.com" -class UnitTestSocialLoginKey(UnitTestCase): - """ - Unit tests for SocialLoginKey. - Use this class for testing individual functions and methods. - """ - - pass - - class TestSocialLoginKey(IntegrationTestCase): def setUp(self) -> None: frappe.set_user("Administrator") diff --git a/frappe/integrations/doctype/token_cache/test_token_cache.py b/frappe/integrations/doctype/token_cache/test_token_cache.py index 26eb4a287d..e3e890e629 100644 --- a/frappe/integrations/doctype/token_cache/test_token_cache.py +++ b/frappe/integrations/doctype/token_cache/test_token_cache.py @@ -1,20 +1,11 @@ # Copyright (c) 2019, Frappe Technologies and contributors # License: MIT. See LICENSE import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase EXTRA_TEST_RECORD_DEPENDENCIES = ["User", "Connected App", "Token Cache"] -class UnitTestTokenCache(UnitTestCase): - """ - Unit tests for TokenCache. - Use this class for testing individual functions and methods. - """ - - pass - - class TestTokenCache(IntegrationTestCase): def setUp(self): self.token_cache = frappe.get_last_doc("Token Cache") diff --git a/frappe/integrations/doctype/webhook/test_webhook.py b/frappe/integrations/doctype/webhook/test_webhook.py index 7b229fcac5..55e70c4c5a 100644 --- a/frappe/integrations/doctype/webhook/test_webhook.py +++ b/frappe/integrations/doctype/webhook/test_webhook.py @@ -13,7 +13,7 @@ from frappe.integrations.doctype.webhook.webhook import ( get_webhook_data, get_webhook_headers, ) -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase from frappe.tests.classes.context_managers import timeout @@ -30,15 +30,6 @@ def get_test_webhook(config): wh.delete() -class UnitTestWebhook(UnitTestCase): - """ - Unit tests for Webhook. - Use this class for testing individual functions and methods. - """ - - pass - - class TestWebhook(IntegrationTestCase): @classmethod def setUpClass(cls): diff --git a/frappe/integrations/doctype/webhook_request_log/test_webhook_request_log.py b/frappe/integrations/doctype/webhook_request_log/test_webhook_request_log.py index 1a1f44335e..d707dfd68d 100644 --- a/frappe/integrations/doctype/webhook_request_log/test_webhook_request_log.py +++ b/frappe/integrations/doctype/webhook_request_log/test_webhook_request_log.py @@ -2,16 +2,7 @@ # License: MIT. See LICENSE # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestWebhookRequestLog(UnitTestCase): - """ - Unit tests for WebhookRequestLog. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestWebhookRequestLog(IntegrationTestCase): diff --git a/frappe/locale/fa.po b/frappe/locale/fa.po index 91f8593305..c281977d3a 100644 --- a/frappe/locale/fa.po +++ b/frappe/locale/fa.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: frappe\n" "Report-Msgid-Bugs-To: developers@frappe.io\n" "POT-Creation-Date: 2025-05-18 09:33+0000\n" -"PO-Revision-Date: 2025-05-26 10:24\n" +"PO-Revision-Date: 2025-06-01 11:25\n" "Last-Translator: developers@frappe.io\n" "Language-Team: Persian\n" "MIME-Version: 1.0\n" @@ -10817,7 +10817,7 @@ msgstr "صفحه ساز Frappe با استفاده از کامپوننت ها" #: frappe/public/js/frappe/file_uploader/ImageCropper.vue:112 msgctxt "Image Cropper" msgid "Free" -msgstr "رایگان" +msgstr "آزاد" #. Label of the frequency (Select) field in DocType 'Auto Repeat' #. Label of the frequency (Select) field in DocType 'Scheduled Job Type' @@ -17901,7 +17901,7 @@ msgstr "اپراتور باید یکی از {0} باشد" #: frappe/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.js:8 #: frappe/public/js/frappe/file_uploader/FilePreview.vue:28 msgid "Optimize" -msgstr "بهینه سازی کنید" +msgstr "بهینه‌سازی" #: frappe/core/doctype/file/file.js:105 msgid "Optimizing image..." diff --git a/frappe/locale/fr.po b/frappe/locale/fr.po index 68505aba7a..71745de8df 100644 --- a/frappe/locale/fr.po +++ b/frappe/locale/fr.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: frappe\n" "Report-Msgid-Bugs-To: developers@frappe.io\n" "POT-Creation-Date: 2025-05-18 09:33+0000\n" -"PO-Revision-Date: 2025-05-19 07:01\n" +"PO-Revision-Date: 2025-06-05 11:49\n" "Last-Translator: developers@frappe.io\n" "Language-Team: French\n" "MIME-Version: 1.0\n" @@ -592,7 +592,18 @@ msgid "
*  *  *  *  *\n"
 "* - Any value\n"
 "/ - Step values\n"
 "
\n" -msgstr "" +msgstr "
* * * * *\n"
+"┬ ┬ ┬ ┬ ┬\n"
+"│ │ │ │ │\n"
+"│ │ │ │ └ jour de la semaine (0 - 6) (0 est dimanche)\n"
+"│ │ │ └───── mois (1 - 12)\n"
+"│ │ └────────── jour du mois (1 - 31)\n"
+"│ └──────────────── heure (0 - 23)\n"
+"└──────────────────── minute (0 - 59)\n\n"
+"---\n\n"
+"* - Toute valeur\n"
+"/ - Valeurs d'étape\n"
+"
\n" #. Content of the 'Example' (HTML) field in DocType 'Workflow Transition' #: frappe/workflow/doctype/workflow_transition/workflow_transition.json @@ -1153,7 +1164,7 @@ msgstr "" #: frappe/public/js/frappe/form/grid.js:66 msgid "Add Multiple" -msgstr "" +msgstr "Ajout multiple" #: frappe/core/page/permission_manager/permission_manager.js:445 msgid "Add New Permission Rule" @@ -1656,7 +1667,7 @@ msgstr "Tous les champs sont nécessaires pour soumettre le commentaire." #. Description of the 'Document States' (Table) field in DocType 'Workflow' #: frappe/workflow/doctype/workflow/workflow.json msgid "All possible Workflow States and roles of the workflow. Docstatus Options: 0 is \"Saved\", 1 is \"Submitted\" and 2 is \"Cancelled\"" -msgstr "Tous les états de flux de travail et les rôles possibles du flux de travail. Les options d'état de Doc: 0 sont \"Enregistrés\", 1 est \"Envoyé\" et 2 est \"Annulé\"" +msgstr "Tous les états de workflow et les rôles possibles du workflow. Les options d'état de Doc: 0 sont \"Enregistrés\", 1 est \"Envoyé\" et 2 est \"Annulé\"" #: frappe/utils/password_strength.py:183 msgid "All-uppercase is almost as easy to guess as all-lowercase." @@ -1696,7 +1707,7 @@ msgstr "Autoriser la Modification en Masse" #. Label of the allow_edit (Check) field in DocType 'List View Settings' #: frappe/desk/doctype/list_view_settings/list_view_settings.json msgid "Allow Bulk Editing" -msgstr "" +msgstr "Autoriser l'édition en masse" #. Label of the allow_consecutive_login_attempts (Int) field in DocType 'System #. Settings' @@ -1854,7 +1865,8 @@ msgstr "" #: frappe/desk/doctype/list_view_settings/list_view_settings.json msgid "Allow editing even if the doctype has a workflow set up.\n\n" "Does nothing if a workflow isn't set up." -msgstr "" +msgstr "Autoriser l'édition même s'il existe workflow configuré.\n\n" +"Ne fais rien si un flux de travail n'est pas configuré." #. Label of the allow_events_in_timeline (Check) field in DocType 'DocType' #: frappe/core/doctype/doctype/doctype.json @@ -1953,13 +1965,13 @@ msgstr "Autorisé dans les mentions" #. Type' #: frappe/core/doctype/user_type/user_type.json msgid "Allowed Modules" -msgstr "" +msgstr "Modules autorisés" #. Label of the allowed_roles (Table MultiSelect) field in DocType 'OAuth #. Client' #: frappe/integrations/doctype/oauth_client/oauth_client.json msgid "Allowed Roles" -msgstr "" +msgstr "Rôles Autorisés" #. Label of the allowed_embedding_domains (Small Text) field in DocType 'Web #. Form' @@ -2097,7 +2109,7 @@ msgstr "Ancêtres de" #. Settings' #: frappe/core/doctype/navbar_settings/navbar_settings.json msgid "Announcement Widget" -msgstr "" +msgstr "Widget d'annonce" #. Label of the announcements_section (Section Break) field in DocType 'Navbar #. Settings' @@ -2414,11 +2426,11 @@ msgstr "Voulez-vous vraiment réinitialiser toutes les personnalisations?" #: frappe/workflow/doctype/workflow/workflow.js:125 msgid "Are you sure you want to save this document?" -msgstr "" +msgstr "Êtes-vous sûr de vouloir enregistrer ce document ?" #: frappe/email/doctype/newsletter/newsletter.js:60 msgid "Are you sure you want to send this newsletter now?" -msgstr "" +msgstr "Etes-vous sûr de vouloir envoyer cette newsletter maintenant ?" #: frappe/core/doctype/document_naming_rule/document_naming_rule.js:16 #: frappe/core/doctype/user_permission/user_permission_list.js:165 @@ -2463,7 +2475,7 @@ msgstr "Attribuer À" #: frappe/public/js/frappe/form/sidebar/assign_to.js:181 msgid "Assign To User Group" -msgstr "" +msgstr "Affecter au groupe d'utilisateurs" #. Label of the assign_to_users_section (Section Break) field in DocType #. 'Assignment Rule' @@ -2473,7 +2485,7 @@ msgstr "Attribuer aux utilisateurs" #: frappe/public/js/frappe/form/sidebar/assign_to.js:260 msgid "Assign a user" -msgstr "" +msgstr "Affecter un utilisateur" #: frappe/automation/doctype/assignment_rule/assignment_rule.js:52 msgid "Assign one by one, in sequence" @@ -2521,7 +2533,7 @@ msgstr "Assigné À /Responsable" #: frappe/public/js/frappe/form/sidebar/assign_to.js:269 msgid "Assigning..." -msgstr "" +msgstr "Affectation en cours..." #. Option for the 'Type' (Select) field in DocType 'Notification Log' #: frappe/desk/doctype/notification_log/notification_log.json @@ -2561,7 +2573,7 @@ msgstr "Affectation de règle utilisateur" #: frappe/automation/doctype/assignment_rule/assignment_rule.py:55 msgid "Assignment Rule is not allowed on document type {0}" -msgstr "" +msgstr "La règle d'affectation n'est pas autorisée sur le type de document {0}" #. Label of the assignment_rules_section (Section Break) field in DocType #. 'Assignment Rule' @@ -2642,7 +2654,7 @@ msgstr "Joindre l'Impression" #: frappe/public/js/frappe/file_uploader/WebLink.vue:10 msgid "Attach a web link" -msgstr "" +msgstr "Joindre un lien Web" #: frappe/website/doctype/website_slideshow/website_slideshow.js:8 msgid "Attach files / urls and add in table." @@ -2838,11 +2850,11 @@ msgstr "Autorisé" #: frappe/www/attribution.html:20 msgid "Authors" -msgstr "" +msgstr "Auteurs" #: frappe/www/attribution.html:37 msgid "Authors / Maintainers" -msgstr "" +msgstr "Auteurs / Mainteneurs" #. Option for the 'Skip Authorization' (Select) field in DocType 'OAuth #. Provider Settings' @@ -2875,7 +2887,7 @@ msgstr "Répétition automatique" #. Name of a DocType #: frappe/automation/doctype/auto_repeat_day/auto_repeat_day.json msgid "Auto Repeat Day" -msgstr "" +msgstr "Jour de répétition automatique" #: frappe/automation/doctype/auto_repeat/auto_repeat.py:165 msgid "Auto Repeat Day{0} {1} has been repeated." @@ -2887,7 +2899,7 @@ msgstr "La répétition automatique de la création de document a échoué" #: frappe/automation/doctype/auto_repeat/auto_repeat.js:117 msgid "Auto Repeat Schedule" -msgstr "" +msgstr "Planification de répétition automatique" #: frappe/public/js/frappe/utils/common.js:434 msgid "Auto Repeat created for this document" @@ -2915,12 +2927,12 @@ msgstr "L'affectation automatique a échoué: {0}" #. Label of the follow_assigned_documents (Check) field in DocType 'User' #: frappe/core/doctype/user/user.json msgid "Auto follow documents that are assigned to you" -msgstr "" +msgstr "Suivez automatiquement les documents qui vous sont affectés" #. Label of the follow_shared_documents (Check) field in DocType 'User' #: frappe/core/doctype/user/user.json msgid "Auto follow documents that are shared with you" -msgstr "" +msgstr "Suivez automatiquement les documents partagés avec vous" #. Label of the follow_liked_documents (Check) field in DocType 'User' #: frappe/core/doctype/user/user.json @@ -2939,7 +2951,7 @@ msgstr "Suivi automatique des documents que vous créez" #: frappe/automation/doctype/auto_repeat/auto_repeat.py:227 msgid "Auto repeat failed. Please enable auto repeat after fixing the issues." -msgstr "" +msgstr "La répétition automatique a échoué. Veuillez activer la répétition automatique après avoir résolu les problèmes." #. Option for the 'Type' (Select) field in DocType 'DocField' #. Option for the 'Field Type' (Select) field in DocType 'Custom Field' @@ -2953,7 +2965,7 @@ msgstr "Auto-complétion" #. Option for the 'Naming Rule' (Select) field in DocType 'DocType' #: frappe/core/doctype/doctype/doctype.json msgid "Autoincrement" -msgstr "" +msgstr "Auto-incrémentation" #. Description of a Card Break in the Build Workspace #: frappe/core/workspace/build/build.json @@ -2970,7 +2982,7 @@ msgstr "Message automatique" #: frappe/core/doctype/user/user.json #: frappe/public/js/frappe/ui/theme_switcher.js:69 msgid "Automatic" -msgstr "" +msgstr "Automatique" #: frappe/email/doctype/email_account/email_account.py:776 msgid "Automatic Linking can be activated only for one Email Account." @@ -2983,11 +2995,11 @@ msgstr "La liaison automatique ne peut être activée que si l'option Entran #. Description of a DocType #: frappe/automation/doctype/assignment_rule/assignment_rule.json msgid "Automatically Assign Documents to Users" -msgstr "" +msgstr "Affecter automatiquement des documents aux utilisateurs" #: frappe/public/js/frappe/list/list_view.js:128 msgid "Automatically applied a filter for recent data. You can disable this behavior from the list view settings." -msgstr "" +msgstr "Application automatique d'un filtre pour les données récentes. Vous pouvez désactiver ce comportement dans les paramètres de l'affichage de la liste." #. Label of the auto_account_deletion (Int) field in DocType 'Website Settings' #: frappe/website/doctype/website_settings/website_settings.json @@ -3046,7 +3058,7 @@ msgstr "En attente Mot de Passe" #: frappe/public/js/frappe/widgets/onboarding_widget.js:195 msgid "Awesome Work" -msgstr "" +msgstr "Excellent travail" #: frappe/public/js/frappe/widgets/onboarding_widget.js:353 msgid "Awesome, now try making an entry yourself" @@ -3168,16 +3180,16 @@ msgstr "Travaux en Arrière-plan" #. Report' #: frappe/desk/doctype/system_health_report/system_health_report.json msgid "Background Jobs Check" -msgstr "" +msgstr "Vérification des Travaux en Arrière-plan" #. Label of the background_jobs_queue (Autocomplete) field in DocType 'Webhook' #: frappe/integrations/doctype/webhook/webhook.json msgid "Background Jobs Queue" -msgstr "" +msgstr "Fille d'attente des Travaux en Arrière-plan" #: frappe/public/js/frappe/list/bulk_operations.js:87 msgid "Background Print (required for >25 documents)" -msgstr "" +msgstr "Impression en tache d'arrière-plan (obligatoire pour > 25 documents)" #. Label of the background_workers (Section Break) field in DocType 'System #. Settings' @@ -3190,7 +3202,7 @@ msgstr "Exécution d'Opérations en Arrière-Plan" #: frappe/integrations/doctype/google_drive/google_drive.py:170 msgid "Backing up Data." -msgstr "" +msgstr "Sauvegarde des données." #: frappe/integrations/doctype/google_drive/google_drive.js:32 msgid "Backing up to Google Drive." @@ -3236,7 +3248,7 @@ msgstr "Fréquence de Sauvegarde" #. Label of the backup_path (Data) field in DocType 'S3 Backup Settings' #: frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.json msgid "Backup Path" -msgstr "" +msgstr "Chemin de sauvegarde" #: frappe/desk/page/backups/backups.py:98 msgid "Backup job is already queued. You will receive an email with the download link" @@ -3259,11 +3271,11 @@ msgstr "Sauvegardes" #. Label of the backups_size (Float) field in DocType 'System Health Report' #: frappe/desk/doctype/system_health_report/system_health_report.json msgid "Backups (MB)" -msgstr "" +msgstr "Sauvegardes (MB)" #: frappe/core/doctype/scheduled_job_type/scheduled_job_type.py:65 msgid "Bad Cron Expression" -msgstr "" +msgstr "Expression cron incorrecte" #. Option for the 'Rounding Method' (Select) field in DocType 'System Settings' #: frappe/core/doctype/system_settings/system_settings.json @@ -3300,7 +3312,7 @@ msgstr "La Bannière est au-dessus de la Barre de Menu Supérieure." #. Option for the 'Type' (Select) field in DocType 'Dashboard Chart' #: frappe/desk/doctype/dashboard_chart/dashboard_chart.json msgid "Bar" -msgstr "" +msgstr "Bar" #. Option for the 'Type' (Select) field in DocType 'DocField' #. Option for the 'Field Type' (Select) field in DocType 'Custom Field' @@ -3539,7 +3551,7 @@ msgstr "Blogueur" #: frappe/core/doctype/doctype_state/doctype_state.json #: frappe/desk/doctype/kanban_board_column/kanban_board_column.json msgid "Blue" -msgstr "" +msgstr "Bleue" #. Label of the bold (Check) field in DocType 'DocField' #. Label of the bold (Check) field in DocType 'Custom Field' @@ -3962,7 +3974,7 @@ msgstr "Caméra" #: frappe/website/doctype/web_page_view/web_page_view.json #: frappe/website/report/website_analytics/website_analytics.js:39 msgid "Campaign" -msgstr "" +msgstr "Campagne" #. Label of the campaign_description (Small Text) field in DocType 'UTM #. Campaign' @@ -5873,7 +5885,7 @@ msgstr "" #: frappe/core/doctype/scheduled_job_type/scheduled_job_type.json #: frappe/core/doctype/server_script/server_script.json msgid "Cron" -msgstr "" +msgstr "Cron" #. Label of the cron_format (Data) field in DocType 'Scheduled Job Type' #. Label of the cron_format (Data) field in DocType 'Server Script' @@ -5884,7 +5896,7 @@ msgstr "Format Cron" #: frappe/core/doctype/scheduled_job_type/scheduled_job_type.py:59 msgid "Cron format is required for job types with Cron frequency." -msgstr "" +msgstr "Le format Cron est requis pour les types de tâches avec fréquence Cron." #: frappe/public/js/frappe/file_uploader/ImageCropper.vue:34 msgid "Crop" @@ -7101,7 +7113,7 @@ msgstr "" #: frappe/website/doctype/website_slideshow_item/website_slideshow_item.json #: frappe/www/attribution.html:24 msgid "Description" -msgstr "" +msgstr "Description" #. Description of the 'Blog Intro' (Small Text) field in DocType 'Blog Post' #: frappe/website/doctype/blog_post/blog_post.json @@ -7167,7 +7179,7 @@ msgstr "" #: frappe/workflow/doctype/workflow_action/workflow_action.json #: frappe/workflow/doctype/workflow_state/workflow_state.json msgid "Desk User" -msgstr "" +msgstr "Utilisateur du backoffice" #. Name of a DocType #: frappe/desk/doctype/desktop_icon/desktop_icon.json @@ -7236,7 +7248,7 @@ msgstr "Désactiver l'actualisation automatique" #. 'List View Settings' #: frappe/desk/doctype/list_view_settings/list_view_settings.json msgid "Disable Automatic Recency Filters" -msgstr "" +msgstr "Désactiver les filtres automatiques de récurrence" #. Label of the disable_change_log_notification (Check) field in DocType #. 'System Settings' @@ -7248,7 +7260,7 @@ msgstr "" #. Settings' #: frappe/desk/doctype/list_view_settings/list_view_settings.json msgid "Disable Comment Count" -msgstr "" +msgstr "Désactiver le comptage des commentaires" #. Label of the disable_comments (Check) field in DocType 'Blog Post' #: frappe/website/doctype/blog_post/blog_post.json @@ -7619,7 +7631,7 @@ msgstr "" #. Description of the 'Document Type' (Link) field in DocType 'Workflow' #: frappe/workflow/doctype/workflow/workflow.json msgid "DocType on which this Workflow is applicable." -msgstr "DocType pour lequel ce Flux de Travail est applicable." +msgstr "DocType pour lequel ce Workflow est applicable." #: frappe/public/js/frappe/views/kanban/kanban_settings.js:4 msgid "DocType required" @@ -8560,7 +8572,7 @@ msgstr "" #: frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.json #: frappe/www/login.html:8 frappe/www/login.py:104 msgid "Email" -msgstr "" +msgstr "Courriel" #. Label of a Link in the Tools Workspace #. Label of the email_account (Link) field in DocType 'Communication' @@ -12214,7 +12226,7 @@ msgstr "Si \"Appliquer des autorisations d'utilisateur strictes\" est coché et #: frappe/workflow/doctype/workflow/workflow.json #: frappe/workflow/doctype/workflow_document_state/workflow_document_state.json msgid "If Checked workflow status will not override status in list view" -msgstr "Si Cochée le statut du flux de travail ne remplacera pas le statut de la vue en liste" +msgstr "Si coché, le statut du workflow ne remplacera pas le statut de la vue en liste" #: frappe/core/doctype/doctype/doctype.py:1762 #: frappe/core/report/user_doctype_permissions/user_doctype_permissions.py:45 @@ -17029,7 +17041,7 @@ msgstr "Pas de courrier {0}" #: frappe/public/js/frappe/form/grid_row.js:256 msgctxt "Title of the 'row number' column" msgid "No." -msgstr "" +msgstr "N°." #. Option for the 'Provider' (Select) field in DocType 'Geolocation Settings' #: frappe/integrations/doctype/geolocation_settings/geolocation_settings.json @@ -17057,7 +17069,7 @@ msgstr "Aucun" #: frappe/public/js/frappe/form/workflow.js:36 msgid "None: End of Workflow" -msgstr "Aucun: Fin de Flux de Travail" +msgstr "Rien: Fin du Workflow" #. Label of the normalized_copies (Int) field in DocType 'Recorder Query' #: frappe/core/doctype/recorder_query/recorder_query.json @@ -17176,7 +17188,7 @@ msgstr "Image utilisateur non valide." #: frappe/model/workflow.py:114 msgid "Not a valid Workflow Action" -msgstr "Action de flux de travail non valide" +msgstr "Action de Workflow non valide" #: frappe/templates/includes/login/login.js:255 msgid "Not a valid user" @@ -17242,7 +17254,7 @@ msgstr "Non autorisé à afficher {0}" #: frappe/automation/workspace/tools/tools.json #: frappe/desk/doctype/note/note.json msgid "Note" -msgstr "" +msgstr "Note" #. Name of a DocType #: frappe/desk/doctype/note_seen_by/note_seen_by.json @@ -17665,7 +17677,7 @@ msgstr "" #. Request' #: frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.json msgid "On Hold" -msgstr "" +msgstr "En attente" #. Option for the 'DocType Event' (Select) field in DocType 'Server Script' #: frappe/core/doctype/server_script/server_script.json @@ -18168,7 +18180,7 @@ msgstr "Sortie" #: frappe/public/js/frappe/form/templates/form_dashboard.html:5 msgid "Overview" -msgstr "" +msgstr "Vue d'ensemble" #: frappe/core/report/transaction_log_report/transaction_log_report.py:100 msgid "Owner" @@ -21333,7 +21345,7 @@ msgstr "Type de Rapport" #: frappe/public/js/frappe/list/base_list.js:203 msgid "Report View" -msgstr "" +msgstr "Vue rapport" #: frappe/public/js/frappe/form/templates/form_sidebar.html:26 msgid "Report bug" @@ -22072,7 +22084,7 @@ msgstr "" #. Description of the 'Transitions' (Table) field in DocType 'Workflow' #: frappe/workflow/doctype/workflow/workflow.json msgid "Rules defining transition of state in the workflow." -msgstr "Règles définissant la transition d'état dans le flux de travail" +msgstr "Règles définissant la transition d'état dans le Workflow." #. Description of the 'Transition Rules' (Section Break) field in DocType #. 'Workflow' @@ -25025,7 +25037,7 @@ msgstr "" #: frappe/automation/doctype/auto_repeat/auto_repeat.json #: frappe/automation/doctype/auto_repeat/auto_repeat.py:128 msgid "Submit on Creation" -msgstr "" +msgstr "Valider à la création" #: frappe/public/js/frappe/widgets/onboarding_widget.js:395 msgid "Submit this document to complete this step." @@ -29238,25 +29250,25 @@ msgstr "" #: frappe/public/js/workflow_builder/store.js:129 #: frappe/workflow/doctype/workflow/workflow.json msgid "Workflow" -msgstr "Flux de Travail" +msgstr "Workflow" #. Name of a DocType #: frappe/workflow/doctype/workflow_action/workflow_action.json #: frappe/workflow/doctype/workflow_action/workflow_action.py:444 msgid "Workflow Action" -msgstr "Action du Flux de Travail" +msgstr "Action du Workflow" #. Name of a DocType #. Description of a DocType #: frappe/workflow/doctype/workflow_action_master/workflow_action_master.json msgid "Workflow Action Master" -msgstr "Actions de Base du Flux de Travail" +msgstr "Actions de Base du Workflow" #. Label of the workflow_action_name (Data) field in DocType 'Workflow Action #. Master' #: frappe/workflow/doctype/workflow_action_master/workflow_action_master.json msgid "Workflow Action Name" -msgstr "Nom de l'Action du Flux de Travail" +msgstr "Nom de l'Action du Workflow" #. Name of a DocType #: frappe/workflow/doctype/workflow_action_permitted_role/workflow_action_permitted_role.json @@ -29267,13 +29279,13 @@ msgstr "" #. Document State' #: frappe/workflow/doctype/workflow_document_state/workflow_document_state.json msgid "Workflow Action is not created for optional states" -msgstr "L'action de flux de travail n'est pas créée pour les états facultatifs" +msgstr "L’action du workflow n’est pas créée pour les états facultatifs" #: frappe/public/js/workflow_builder/store.js:129 #: frappe/workflow/doctype/workflow/workflow.js:25 #: frappe/workflow/page/workflow_builder/workflow_builder.js:4 msgid "Workflow Builder" -msgstr "" +msgstr "Constructeur de Workflow" #. Label of the workflow_builder_id (Data) field in DocType 'Workflow Document #. State' @@ -29300,24 +29312,24 @@ msgstr "" #. Name of a DocType #: frappe/workflow/doctype/workflow_document_state/workflow_document_state.json msgid "Workflow Document State" -msgstr "État du Document du Flux de Travail" +msgstr "État du Document du Workflow" #. Label of the workflow_name (Data) field in DocType 'Workflow' #: frappe/workflow/doctype/workflow/workflow.json msgid "Workflow Name" -msgstr "Nom du Flux de Travail" +msgstr "Nom du Workflow" #. Label of the workflow_state (Data) field in DocType 'Workflow Action' #. Name of a DocType #: frappe/workflow/doctype/workflow_action/workflow_action.json #: frappe/workflow/doctype/workflow_state/workflow_state.json msgid "Workflow State" -msgstr "État du Flux de Travail" +msgstr "État du Workflow" #. Label of the workflow_state_field (Data) field in DocType 'Workflow' #: frappe/workflow/doctype/workflow/workflow.json msgid "Workflow State Field" -msgstr "Champ de l'État du Flux de Travail" +msgstr "Champ de l'État du Workflow" #: frappe/model/workflow.py:61 msgid "Workflow State not set" @@ -29325,7 +29337,7 @@ msgstr "L'état du workflow n'est pas défini" #: frappe/model/workflow.py:204 frappe/model/workflow.py:212 msgid "Workflow State transition not allowed from {0} to {1}" -msgstr "La transition d'état du flux de travail n'est pas autorisée de {0} à {1}." +msgstr "La transition d'état du workflow n'est pas autorisée de {0} à {1}" #: frappe/workflow/doctype/workflow/workflow.js:140 msgid "Workflow States Don't Exist" @@ -29338,7 +29350,7 @@ msgstr "Statut du workflow" #. Name of a DocType #: frappe/workflow/doctype/workflow_transition/workflow_transition.json msgid "Workflow Transition" -msgstr "Transition du Flux de Travail" +msgstr "Transition du Workflow" #. Description of a DocType #: frappe/workflow/doctype/workflow_state/workflow_state.json @@ -30831,7 +30843,7 @@ msgstr "aaaa-mm-jj" #: frappe/desk/doctype/event/event.js:87 msgid "{0}" -msgstr "" +msgstr "{0}" #: frappe/public/js/frappe/ui/toolbar/search_utils.js:193 msgid "{0} ${skip_list ? \"\" : type}" @@ -31171,7 +31183,7 @@ msgstr "" #: frappe/core/doctype/scheduled_job_type/scheduled_job_type.py:64 msgid "{0} is not a valid Cron expression." -msgstr "" +msgstr "{0} n'est pas une expression Cron valide." #: frappe/public/js/frappe/form/controls/dynamic_link.js:27 msgid "{0} is not a valid DocType for Dynamic Link" diff --git a/frappe/locale/sr_CS.po b/frappe/locale/sr_CS.po index 248ac74bde..2ec6f861f9 100644 --- a/frappe/locale/sr_CS.po +++ b/frappe/locale/sr_CS.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: frappe\n" "Report-Msgid-Bugs-To: developers@frappe.io\n" "POT-Creation-Date: 2025-05-18 09:33+0000\n" -"PO-Revision-Date: 2025-05-25 10:26\n" +"PO-Revision-Date: 2025-06-03 11:42\n" "Last-Translator: developers@frappe.io\n" "Language-Team: Serbian (Latin)\n" "MIME-Version: 1.0\n" @@ -1325,7 +1325,7 @@ msgstr "Dodaj korisničku dozvolu" #. Label of the add_video_conferencing (Check) field in DocType 'Event' #: frappe/desk/doctype/event/event.json msgid "Add Video Conferencing" -msgstr "Dodaj video konferenciju" +msgstr "Dodaj video-konferenciju" #: frappe/public/js/frappe/ui/filters/filter_list.js:298 msgid "Add a Filter" @@ -5922,7 +5922,7 @@ msgstr "Kreiraj svoj prvi {0}" #: frappe/workflow/doctype/workflow/workflow.js:16 msgid "Create your workflow visually using the Workflow Builder." -msgstr "Kreirajte Vaš radni tok vizuelno koristeći uređivač radnog toka." +msgstr "Kreirajte Vaš radni tok vizualno koristeći uređivač radnog toka." #. Option for the 'Comment Type' (Select) field in DocType 'Comment' #: frappe/core/doctype/comment/comment.json @@ -8584,7 +8584,7 @@ msgstr "Uredite Vaš odgovor" #: frappe/workflow/doctype/workflow/workflow.js:18 msgid "Edit your workflow visually using the Workflow Builder." -msgstr "Vizuelno uredite svoj radni tok pomoću uređivača radnog toka." +msgstr "Vizualno uredite svoj radni tok pomoću uređivača radnog toka." #: frappe/public/js/frappe/views/reports/report_view.js:672 #: frappe/public/js/frappe/widgets/widget_dialog.js:52 @@ -13209,7 +13209,7 @@ msgstr "Interna evidencija deljenja dokumenata" #. Label of the intro_video_url (Data) field in DocType 'Onboarding Step' #: frappe/desk/doctype/onboarding_step/onboarding_step.json msgid "Intro Video URL" -msgstr "URL uvodnog video snimka" +msgstr "URL uvodnog video-snimka" #. Description of the 'Company Introduction' (Text Editor) field in DocType #. 'About Us Settings' @@ -13859,7 +13859,7 @@ msgstr "Zadatak nije pokrenut." #: frappe/desk/doctype/event/event.js:55 msgid "Join video conference with {0}" -msgstr "Pridruži se video konferenciji sa {0}" +msgstr "Pridruži se video-konferenciji sa {0}" #: frappe/public/js/frappe/form/toolbar.js:395 #: frappe/public/js/frappe/form/toolbar.js:830 @@ -29431,7 +29431,7 @@ msgstr "ID uređivača radnog toka" #: frappe/workflow/doctype/workflow/workflow.js:11 msgid "Workflow Builder allows you to create workflows visually. You can drag and drop states and link them to create transitions. Also you can update thieir properties from the sidebar." -msgstr "Uređivač radnog toka Vam dozvoljava da vizuelno kreirate radne tokove. Možete prevlačiti i puštati stanja i povezivati ih kako biste napravili tranzicije. Takođe, možete ažurirati njihova svojstva putem bočne trake." +msgstr "Uređivač radnog toka Vam dozvoljava da vizualno kreirate radne tokove. Možete prevlačiti i puštati stanja i povezivati ih kako biste napravili tranzicije. Takođe, možete ažurirati njihova svojstva putem bočne trake." #. Label of the workflow_data (JSON) field in DocType 'Workflow' #: frappe/workflow/doctype/workflow/workflow.json diff --git a/frappe/locale/sv.po b/frappe/locale/sv.po index 378bcbf981..aa13387bf3 100644 --- a/frappe/locale/sv.po +++ b/frappe/locale/sv.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: frappe\n" "Report-Msgid-Bugs-To: developers@frappe.io\n" "POT-Creation-Date: 2025-05-18 09:33+0000\n" -"PO-Revision-Date: 2025-05-26 10:24\n" +"PO-Revision-Date: 2025-06-05 11:49\n" "Last-Translator: developers@frappe.io\n" "Language-Team: Swedish\n" "MIME-Version: 1.0\n" @@ -5178,7 +5178,7 @@ msgstr "Kommentarer" #. Description of the 'Timeline Field' (Data) field in DocType 'DocType' #: frappe/core/doctype/doctype/doctype.json msgid "Comments and Communications will be associated with this linked document" -msgstr "Kommentarer och E-post kommer att kopplas till detta länkade dokument" +msgstr "Kommentarer och Kommunikation kommer att kopplas till detta länkade dokument" #: frappe/templates/includes/comments/comments.py:38 msgid "Comments cannot have links or email addresses" @@ -5213,22 +5213,22 @@ msgstr "Vanliga förnamn och efternamn är lätta att gissa." #: frappe/email/doctype/email_queue/email_queue.json #: frappe/tests/test_translate.py:35 frappe/tests/test_translate.py:119 msgid "Communication" -msgstr "Konversation" +msgstr "Kommunikation" #. Name of a DocType #: frappe/core/doctype/communication_link/communication_link.json msgid "Communication Link" -msgstr "Konversation Länk" +msgstr "Kommunikation Länk" #. Label of a Link in the Build Workspace #: frappe/core/workspace/build/build.json msgid "Communication Logs" -msgstr "Konversation Logg" +msgstr "Kommunikation Logg" #. Label of the communication_type (Select) field in DocType 'Communication' #: frappe/core/doctype/communication/communication.json msgid "Communication Type" -msgstr "Konversation Typ" +msgstr "Kommunikation Typ" #: frappe/integrations/frappe_providers/frappecloud_billing.py:32 msgid "Communication secret not set" @@ -12115,7 +12115,7 @@ msgstr "Regel med högre prioritet tillämpas först" #. Label of the highlight (Text) field in DocType 'Company History' #: frappe/website/doctype/company_history/company_history.json msgid "Highlight" -msgstr "Markera" +msgstr "Höjdpunkt" #: frappe/www/update-password.html:276 msgid "Hint: Include symbols, numbers and capital letters in the password" @@ -13170,7 +13170,7 @@ msgstr "Integration Begäran" #: frappe/integrations/workspace/integrations/integrations.json #: frappe/website/doctype/website_settings/website_settings.json msgid "Integrations" -msgstr "Integrationer" +msgstr "System Integrationer" #. Description of the 'Delivery Status' (Select) field in DocType #. 'Communication' @@ -30014,7 +30014,7 @@ msgstr "Du har inte lagt till några Översiktpanel Diagram eller Nummerkort än #: frappe/public/js/frappe/list/list_view.js:498 msgid "You haven't created a {0} yet" -msgstr "Du har inte skapat {0} än" +msgstr "Ingen {0} skapad än" #: frappe/rate_limiter.py:166 msgid "You hit the rate limit because of too many requests. Please try after sometime." diff --git a/frappe/locale/tr.po b/frappe/locale/tr.po index d9c6cb6d47..d335db9d96 100644 --- a/frappe/locale/tr.po +++ b/frappe/locale/tr.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: frappe\n" "Report-Msgid-Bugs-To: developers@frappe.io\n" "POT-Creation-Date: 2025-05-18 09:33+0000\n" -"PO-Revision-Date: 2025-05-19 07:02\n" +"PO-Revision-Date: 2025-06-04 11:43\n" "Last-Translator: developers@frappe.io\n" "Language-Team: Turkish\n" "MIME-Version: 1.0\n" @@ -3330,7 +3330,7 @@ msgstr "Yedekleme Sıklığı" #. Label of the backup_path (Data) field in DocType 'S3 Backup Settings' #: frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.json msgid "Backup Path" -msgstr "" +msgstr "Yedekleme Yolu" #: frappe/desk/page/backups/backups.py:98 msgid "Backup job is already queued. You will receive an email with the download link" @@ -13912,7 +13912,7 @@ msgstr "Tüm güncelleme akışlarını takip edin" #. Description of a DocType #: frappe/core/doctype/communication/communication.json msgid "Keeps track of all communications" -msgstr "" +msgstr "Tüm iletişimleri takip eder" #. Label of the defkey (Data) field in DocType 'DefaultValue' #. Label of the key (Data) field in DocType 'Document Share Key' @@ -14217,11 +14217,11 @@ msgstr "Son 10 Aktif Kullanıcı" #: frappe/public/js/frappe/ui/filters/filter.js:627 msgid "Last 14 Days" -msgstr "" +msgstr "Son 14 Gün" #: frappe/public/js/frappe/ui/filters/filter.js:631 msgid "Last 30 Days" -msgstr "" +msgstr "Son 30 Gün" #: frappe/public/js/frappe/ui/filters/filter.js:651 msgid "Last 6 Months" @@ -14229,11 +14229,11 @@ msgstr "" #: frappe/public/js/frappe/ui/filters/filter.js:623 msgid "Last 7 Days" -msgstr "" +msgstr "Son 7 Gün" #: frappe/public/js/frappe/ui/filters/filter.js:635 msgid "Last 90 Days" -msgstr "" +msgstr "Son 90 Gün" #. Label of the last_active (Datetime) field in DocType 'User' #: frappe/core/doctype/user/user.json diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 5ccfbf0cce..fac66fa4d8 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -474,9 +474,6 @@ class BaseDocument: else: value = get_not_null_defaults(df.fieldtype) - if hasattr(value, "__value__"): - value = value.__value__() - d[fieldname] = value return d @@ -847,6 +844,8 @@ class BaseDocument: def get_invalid_links(self, is_submittable=False): """Return list of invalid links and also update fetch values if not set.""" + is_submittable = is_submittable or self.meta.is_submittable + def get_msg(df, docname): # check if parentfield exists (only applicable for child table doctype) if self.get("parentfield"): @@ -859,76 +858,81 @@ class BaseDocument: for df in self.meta.get_link_fields() + self.meta.get("fields", {"fieldtype": ("=", "Dynamic Link")}): docname = self.get(df.fieldname) + if not docname: + continue - if docname: - if df.fieldtype == "Link": - doctype = df.options - if not doctype: - frappe.throw(_("Options not set for link field {0}").format(df.fieldname)) - else: - doctype = self.get(df.options) - if not doctype: - frappe.throw(_("{0} must be set first").format(self.meta.get_label(df.options))) - invalidate_distinct_link_doctypes(df.parent, df.options, doctype) + assert isinstance(docname, str | int) or ( + isinstance(docname, list | tuple | set) and len(docname) == 1 + ), f"Unexpected value for field {df.fieldname}: {docname}" + if df.fieldtype == "Link": + doctype = df.options + if not doctype: + frappe.throw(_("Options not set for link field {0}").format(df.fieldname)) + else: + assert df.fieldtype == "Dynamic Link" + doctype = self.get(df.options) + if not doctype: + frappe.throw(_("{0} must be set first").format(self.meta.get_label(df.options))) + invalidate_distinct_link_doctypes(df.parent, df.options, doctype) + + meta = frappe.get_meta(doctype) + if not meta.istable: + notify_link_count(doctype, docname) + + check_docstatus = is_submittable and frappe.get_meta(doctype).is_submittable + + # get a map of values ot fetch along with this link query + # that are mapped as link_fieldname.source_fieldname in Options of + # Readonly or Data or Text type fields + fields_to_fetch = [ + _df + for _df in self.meta.get_fields_to_fetch(df.fieldname) + if not _df.get("fetch_if_empty") + or (_df.get("fetch_if_empty") and not self.get(_df.fieldname)) + ] + values_to_fetch = ( + "name", + *(_df.fetch_from.split(".")[-1] for _df in fields_to_fetch), + ) + if check_docstatus: + values_to_fetch += ("docstatus",) + + if not meta.get("is_virtual"): + values = frappe.db.get_value( + doctype, docname, values_to_fetch, as_dict=True, cache=True, order_by=None + ) + if not values: # NOTE: DB Value cache does negative caching, which is hard to remove now. + values = frappe.db.get_value( + doctype, docname, values_to_fetch, as_dict=True, order_by=None + ) + else: + values = frappe.get_doc(doctype, docname).as_dict() + + # fallback to dict with field_to_fetch=None if link field value is not found + # (for compatibility, `values` must have same data type) + values = values or _dict.fromkeys(values_to_fetch, None) + + if getattr(meta, "issingle", 0): + values.name = doctype + + if not df.get("is_virtual"): # MySQL is case insensitive. Preserve case of the original docname in the Link Field. + setattr(self, df.fieldname, values.name) - # get a map of values ot fetch along with this link query - # that are mapped as link_fieldname.source_fieldname in Options of - # Readonly or Data or Text type fields + for _df in fields_to_fetch: + if self.is_new() or not self.docstatus.is_submitted() or _df.allow_on_submit: + self.set_fetch_from_value(doctype, _df, values) - meta = frappe.get_meta(doctype) - fields_to_fetch = [ - _df - for _df in self.meta.get_fields_to_fetch(df.fieldname) - if not _df.get("fetch_if_empty") - or (_df.get("fetch_if_empty") and not self.get(_df.fieldname)) - ] - if not meta.get("is_virtual"): - if not fields_to_fetch: - # cache a single value type - values = _dict(name=frappe.db.get_value(doctype, docname, "name", cache=True)) - else: - values_to_fetch = ["name"] + [ - _df.fetch_from.split(".")[-1] for _df in fields_to_fetch - ] + if not values.name: + invalid_links.append((df.fieldname, docname, get_msg(df, docname))) - # fallback to dict with field_to_fetch=None if link field value is not found - # (for compatibility, `values` must have same data type) - empty_values = _dict({value: None for value in values_to_fetch}) - # don't cache if fetching other values too - values = ( - frappe.db.get_value(doctype, docname, values_to_fetch, as_dict=True) - or empty_values - ) - - if getattr(meta, "issingle", 0): - values.name = doctype - - if meta.get("is_virtual"): - values = frappe.get_doc(doctype, docname).as_dict() - - if values: - if not df.get("is_virtual"): - setattr(self, df.fieldname, values.name) - - for _df in fields_to_fetch: - if self.is_new() or not self.docstatus.is_submitted() or _df.allow_on_submit: - self.set_fetch_from_value(doctype, _df, values) - - if not meta.istable: - notify_link_count(doctype, docname) - - if not values.name: - invalid_links.append((df.fieldname, docname, get_msg(df, docname))) - - elif ( - df.fieldname != "amended_from" - and (is_submittable or self.meta.is_submittable) - and frappe.get_meta(doctype).is_submittable - and DocStatus(frappe.db.get_value(doctype, docname, "docstatus") or 0).is_cancelled() - ): - cancelled_links.append((df.fieldname, docname, get_msg(df, docname))) + elif ( + df.fieldname != "amended_from" + and check_docstatus + and DocStatus(values.docstatus or 0).is_cancelled() + ): + cancelled_links.append((df.fieldname, docname, get_msg(df, docname))) return invalid_links, cancelled_links diff --git a/frappe/model/create_new.py b/frappe/model/create_new.py index 84fc2e1e5b..973d9fb619 100644 --- a/frappe/model/create_new.py +++ b/frappe/model/create_new.py @@ -41,7 +41,7 @@ def make_new_doc(doctype): doc["doctype"] = doctype doc["__islocal"] = 1 - if not frappe.model.meta.is_single(doctype): + if not getattr(doc.meta, "issingle", False): doc["__unsaved"] = 1 return doc diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index b34c9c5c20..27563323fa 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -116,6 +116,8 @@ class DatabaseQuery: *, parent_doctype=None, ) -> list: + self.user = user or frappe.session.user + if not ignore_permissions: self.check_read_permission(self.doctype, parent_doctype=parent_doctype) @@ -167,7 +169,6 @@ class DatabaseQuery: self.as_list = as_list self.ignore_ifnull = ignore_ifnull self.flags.ignore_permissions = ignore_permissions - self.user = user or frappe.session.user self.update = update self.user_settings_fields = copy.deepcopy(self.fields) self.run = run @@ -929,7 +930,7 @@ from {tables} df and (db_type := cstr(frappe.db.type_map.get(df.fieldtype, " ")[0])) and db_type in ("varchar", "text", "longtext", "smalltext", "json") - ): + ) or f.fieldname in ("owner", "modified_by", "parent", "parentfield", "parenttype"): value = cstr(f.value) fallback = "''" diff --git a/frappe/model/document.py b/frappe/model/document.py index e81524bc84..07dbef551a 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -24,7 +24,7 @@ from frappe.model.docstatus import DocStatus from frappe.model.naming import set_new_name, validate_name from frappe.model.utils import is_virtual_doctype, simple_singledispatch from frappe.model.workflow import set_workflow_state_on_action, validate_workflow -from frappe.types import DF, DocRef +from frappe.types import DF from frappe.utils import compare, cstr, date_diff, file_lock, flt, get_table_name, now from frappe.utils.data import get_absolute_url, get_datetime, get_timedelta, getdate from frappe.utils.global_search import update_global_search @@ -105,7 +105,7 @@ def get_doc(*args, **kwargs) -> "Document": if not args and kwargs: return get_doc_from_dict(kwargs) else: - raise ValueError("First non keyword argument must be a string, dict or DocRef") + raise ValueError("First non keyword argument must be a string or dict") @get_doc.register(BaseDocument) @@ -113,11 +113,6 @@ def _basedoc(doc: BaseDocument, *args, **kwargs) -> "Document": return doc -@get_doc.register(DocRef) -def _docref(doc_ref: DocRef, **kwargs) -> "Document": - return get_doc(doc_ref.doctype, doc_ref.name, **kwargs) - - @get_doc.register(str) def get_doc_str(doctype: str, name: str | None = None, **kwargs) -> "Document": # if no name: it's a single @@ -143,7 +138,7 @@ def get_doc_from_dict(data: dict[str, Any], **kwargs) -> "Document": raise ImportError(data["doctype"]) -class Document(BaseDocument, DocRef): +class Document(BaseDocument): """All controllers inherit from `Document`.""" doctype: DF.Data @@ -197,9 +192,6 @@ class Document(BaseDocument, DocRef): if isinstance(arg, dict): return self._init_from_kwargs(arg) - if isinstance(arg, DocRef): - return self._init_known_doc(arg.doctype, arg.name, **kwargs) - raise ValueError(f"Unsupported argument type: {type(arg)}") @property @@ -774,7 +766,7 @@ class Document(BaseDocument, DocRef): ) if self.doctype in frappe.db.value_cache: - del frappe.db.value_cache[self.doctype] + frappe.db.value_cache.pop(self.doctype, None) def set_user_and_timestamp(self): self._original_modified = self.modified @@ -1464,7 +1456,7 @@ class Document(BaseDocument, DocRef): self.set("modified_by", frappe.session.user) # load but do not reload doc_before_save because before_change or on_change might expect it - if not self.get_doc_before_save(): + if not self.get_doc_before_save() and not self.meta.istable: self.load_doc_before_save() # to trigger notification on value change diff --git a/frappe/model/meta.py b/frappe/model/meta.py index 3943507999..c182a74e22 100644 --- a/frappe/model/meta.py +++ b/frappe/model/meta.py @@ -38,11 +38,11 @@ from frappe.model.base_document import ( BaseDocument, ) from frappe.model.document import Document +from frappe.model.utils import is_single_doctype from frappe.model.workflow import get_workflow_name from frappe.modules import load_doctype_module -from frappe.types import DocRef from frappe.utils import cached_property, cast, cint, cstr -from frappe.utils.caching import request_cache +from frappe.utils.caching import site_cache from frappe.utils.data import add_to_date, get_datetime DEFAULT_FIELD_LABELS = { @@ -837,13 +837,6 @@ class Meta(Document): ####### -def is_single(doctype): - try: - return frappe.db.get_value("DocType", doctype, "issingle") - except IndexError: - raise Exception("Cannot determine whether {} is single".format(doctype)) - - def get_parent_dt(dt): if not frappe.is_table(dt): return "" @@ -1021,3 +1014,7 @@ if typing.TYPE_CHECKING: class _Meta(Meta, DocType): pass + + +# backward compatibility +is_single = is_single_doctype diff --git a/frappe/model/naming.py b/frappe/model/naming.py index ff64d10b0d..44fe56824c 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -538,9 +538,7 @@ def _set_amended_name(doc): "Amended Document Naming Settings", {"document_type": doc.doctype}, "action", cache=True ) if not amend_naming_rule: - amend_naming_rule = frappe.db.get_single_value( - "Document Naming Settings", "default_amend_naming", cache=True - ) + amend_naming_rule = frappe.get_single_value("Document Naming Settings", "default_amend_naming") if amend_naming_rule == "Default Naming": return diff --git a/frappe/printing/doctype/letter_head/test_letter_head.py b/frappe/printing/doctype/letter_head/test_letter_head.py index fe1b5da023..8273515fc7 100644 --- a/frappe/printing/doctype/letter_head/test_letter_head.py +++ b/frappe/printing/doctype/letter_head/test_letter_head.py @@ -1,16 +1,7 @@ # Copyright (c) 2017, Frappe Technologies and Contributors # License: MIT. See LICENSE import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestLetterHead(UnitTestCase): - """ - Unit tests for LetterHead. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestLetterHead(IntegrationTestCase): diff --git a/frappe/printing/doctype/network_printer_settings/test_network_printer_settings.py b/frappe/printing/doctype/network_printer_settings/test_network_printer_settings.py index 79aba2afd4..9671503a2f 100644 --- a/frappe/printing/doctype/network_printer_settings/test_network_printer_settings.py +++ b/frappe/printing/doctype/network_printer_settings/test_network_printer_settings.py @@ -2,16 +2,7 @@ # See license.txt # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestNetworkPrinterSettings(UnitTestCase): - """ - Unit tests for NetworkPrinterSettings. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestNetworkPrinterSettings(IntegrationTestCase): diff --git a/frappe/printing/doctype/print_format/print_format.py b/frappe/printing/doctype/print_format/print_format.py index 4e5029944b..5cf7c1e7e4 100644 --- a/frappe/printing/doctype/print_format/print_format.py +++ b/frappe/printing/doctype/print_format/print_format.py @@ -142,7 +142,7 @@ class PrintFormat(Document): @frappe.whitelist() def make_default(name): """Set print format as default""" - frappe.has_permission("Print Format", "write") + frappe.has_permission("Print Format", "write", throw=True) print_format = frappe.get_doc("Print Format", name) diff --git a/frappe/printing/doctype/print_format/test_print_format.py b/frappe/printing/doctype/print_format/test_print_format.py index 5bc68d0491..ac8ee764a8 100644 --- a/frappe/printing/doctype/print_format/test_print_format.py +++ b/frappe/printing/doctype/print_format/test_print_format.py @@ -6,21 +6,12 @@ import unittest from typing import TYPE_CHECKING import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests import IntegrationTestCase if TYPE_CHECKING: from frappe.printing.doctype.print_format.print_format import PrintFormat -class UnitTestPrintFormat(UnitTestCase): - """ - Unit tests for PrintFormat. - Use this class for testing individual functions and methods. - """ - - pass - - class TestPrintFormat(IntegrationTestCase): def test_print_user(self, style=None): print_html = frappe.get_print("User", "Administrator", style=style) diff --git a/frappe/printing/doctype/print_format_field_template/test_print_format_field_template.py b/frappe/printing/doctype/print_format_field_template/test_print_format_field_template.py index 503cf0774d..40ee2f19af 100644 --- a/frappe/printing/doctype/print_format_field_template/test_print_format_field_template.py +++ b/frappe/printing/doctype/print_format_field_template/test_print_format_field_template.py @@ -2,16 +2,7 @@ # See license.txt # import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestPrintFormatFieldTemplate(UnitTestCase): - """ - Unit tests for PrintFormatFieldTemplate. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestPrintFormatFieldTemplate(IntegrationTestCase): diff --git a/frappe/printing/doctype/print_heading/test_print_heading.py b/frappe/printing/doctype/print_heading/test_print_heading.py index 3011e20591..67b4371681 100644 --- a/frappe/printing/doctype/print_heading/test_print_heading.py +++ b/frappe/printing/doctype/print_heading/test_print_heading.py @@ -1,16 +1,7 @@ # Copyright (c) 2017, Frappe Technologies and Contributors # License: MIT. See LICENSE import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestPrintHeading(UnitTestCase): - """ - Unit tests for PrintHeading. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestPrintHeading(IntegrationTestCase): diff --git a/frappe/printing/doctype/print_settings/print_settings.py b/frappe/printing/doctype/print_settings/print_settings.py index 4ca4b3736e..dbb19519a9 100644 --- a/frappe/printing/doctype/print_settings/print_settings.py +++ b/frappe/printing/doctype/print_settings/print_settings.py @@ -75,9 +75,4 @@ class PrintSettings(Document): @frappe.whitelist() def is_print_server_enabled(): - if not hasattr(frappe.local, "enable_print_server"): - frappe.local.enable_print_server = cint( - frappe.db.get_single_value("Print Settings", "enable_print_server") - ) - - return frappe.local.enable_print_server + return frappe.get_single_value("Print Settings", "enable_print_server") diff --git a/frappe/printing/doctype/print_settings/test_print_settings.py b/frappe/printing/doctype/print_settings/test_print_settings.py index a699c5aaf7..6b75085d6a 100644 --- a/frappe/printing/doctype/print_settings/test_print_settings.py +++ b/frappe/printing/doctype/print_settings/test_print_settings.py @@ -1,15 +1,6 @@ # Copyright (c) 2018, Frappe Technologies and Contributors # License: MIT. See LICENSE -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestPrintSettings(UnitTestCase): - """ - Unit tests for PrintSettings. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestPrintSettings(IntegrationTestCase): diff --git a/frappe/printing/doctype/print_style/test_print_style.py b/frappe/printing/doctype/print_style/test_print_style.py index bfbf172279..d25f7d5768 100644 --- a/frappe/printing/doctype/print_style/test_print_style.py +++ b/frappe/printing/doctype/print_style/test_print_style.py @@ -1,16 +1,7 @@ # Copyright (c) 2017, Frappe Technologies and Contributors # License: MIT. See LICENSE import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase - - -class UnitTestPrintStyle(UnitTestCase): - """ - Unit tests for PrintStyle. - Use this class for testing individual functions and methods. - """ - - pass +from frappe.tests import IntegrationTestCase class TestPrintStyle(IntegrationTestCase): diff --git a/frappe/public/icons/lucide.svg b/frappe/public/icons/lucide.svg index b9bbb03158..0e5eab1451 100644 --- a/frappe/public/icons/lucide.svg +++ b/frappe/public/icons/lucide.svg @@ -8,6 +8,14 @@ + + + + + + + + diff --git a/frappe/public/icons/timeless/icons.svg b/frappe/public/icons/timeless/icons.svg index b8dbde9d94..b8de8f59bc 100644 --- a/frappe/public/icons/timeless/icons.svg +++ b/frappe/public/icons/timeless/icons.svg @@ -60,6 +60,14 @@ Tip: use lucide.svg in /icons for all downloaded icons + + + + + + + + @@ -396,7 +404,11 @@ Tip: use lucide.svg in /icons for all downloaded icons - + + + + + @@ -728,14 +740,6 @@ Tip: use lucide.svg in /icons for all downloaded icons - - - - - - - - @@ -978,10 +982,20 @@ Tip: use lucide.svg in /icons for all downloaded icons + + + + + + + + + + diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index e4fe8d1573..6ff35c91e8 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -182,6 +182,7 @@ frappe.Application = class Application { } frappe.router.on("change", () => { $(".tooltip").hide(); + if (frappe.frappe_toolbar && frappe.is_mobile()) frappe.frappe_toolbar.show_app_logo(); }); } diff --git a/frappe/public/js/frappe/form/controls/text_editor.js b/frappe/public/js/frappe/form/controls/text_editor.js index 61cc658b1f..582c6cace7 100644 --- a/frappe/public/js/frappe/form/controls/text_editor.js +++ b/frappe/public/js/frappe/form/controls/text_editor.js @@ -286,7 +286,7 @@ frappe.ui.form.ControlTextEditor = class ControlTextEditor extends frappe.ui.for get_toolbar_options() { return [ - [{ header: [1, 2, 3, false] }], + [{ header: [1, 2, 3, 4, 5, 6, false] }], [{ size: font_sizes }], ["bold", "italic", "underline", "strike", "clean"], [{ color: [] }, { background: [] }], diff --git a/frappe/public/js/frappe/form/footer/form_timeline.js b/frappe/public/js/frappe/form/footer/form_timeline.js index f541733616..7a398357a4 100644 --- a/frappe/public/js/frappe/form/footer/form_timeline.js +++ b/frappe/public/js/frappe/form/footer/form_timeline.js @@ -529,10 +529,28 @@ class FormTimeline extends BaseTimeline { ).click(() => { this.compose_mail(communication_doc, true); }); - actions.append(reply); - actions.append(reply_all); + if (frappe.is_mobile()) { + this.add_dropdown_item(communication_box, [reply, reply_all]); + } else { + actions.append(reply); + actions.append(reply_all); + } + } + add_dropdown_item(timeline_box, menu_items) { + let more_actions = timeline_box.find(".more-actions > .dropdown-menu > li"); + menu_items.forEach((m) => { + let action_name = m[0].classList[1]; + let formatted_action_name = + String(action_name).charAt(0).toUpperCase() + String(action_name).slice(1); + m.empty(); + m.append( + __("{0}", [frappe.utils.to_title_case(formatted_action_name.replace("-", " "))]) + ); + m.removeClass(); + m.addClass("dropdown-item"); + more_actions.append(m); + }); } - compose_mail(communication_doc = null, reply_all = false) { const args = { doc: this.frm.doc, @@ -646,11 +664,11 @@ class FormTimeline extends BaseTimeline { let edit_button = $(); let current_user = frappe.session.user; if (["Administrator", doc.owner].includes(current_user)) { - edit_button = $(` +
`) + .prependTo(this.$filter_list_wrapper.find(".filter-selector")) + .on("click", function () { + me.toggle_standard_filter(); + }); + let children = list_view.page.page_form.children(); + list_view.page.page_form.append(children.get().reverse()); + } + + toggle_standard_filter() { + if (this.standard_filters_visible) { + this.standard_filters_visible = false; + this.standard_filters_wrapper.hide(); + } else { + this.standard_filters_visible = true; + this.standard_filters_wrapper.show(); + } + let icon_name = !this.standard_filters_visible ? "funnel-plus" : "funnel-x"; + this.$filter_list_wrapper + .find(".filter-toggle") + .find("use") + .attr("href", `#icon-${icon_name}`); } setup() { diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index ab9149f2d2..7ba60a23a9 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -664,6 +664,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { ); $count.tooltip({ delay: { show: 600, hide: 100 }, trigger: "hover" }); $count.css("cursor", "pointer"); + $count.css("white-space", "nowrap"); $count.on("click", () => { me.count_upper_bound = 0; $count.off("click"); @@ -721,7 +722,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { .join(""); const right_html = ` - +