mirror of
https://github.com/frappe/erpnext.git
synced 2026-10-29 22:57:42 +00:00
fix(telephony): scope voice call settings to their user
Replace the `All` permission rules with `Desk User` and limit non-admin
users to their own Voice Call Settings through permission hooks. Sites
with customised permissions get the same rules through a patch.
(cherry picked from commit 7c88594ba8)
# Conflicts:
# erpnext/hooks.py
# erpnext/patches.txt
# erpnext/telephony/doctype/voice_call_settings/test_voice_call_settings.py
# erpnext/telephony/doctype/voice_call_settings/voice_call_settings.json
# erpnext/telephony/doctype/voice_call_settings/voice_call_settings.py
This commit is contained in:
@@ -288,6 +288,26 @@ sounds = [
|
||||
|
||||
has_upload_permission = {"Employee": "erpnext.setup.doctype.employee.employee.has_upload_permission"}
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
permission_query_conditions = {
|
||||
"Asset Activity": "erpnext.assets.doctype.asset_activity.asset_activity.get_permission_query_conditions",
|
||||
"Item": "erpnext.stock.doctype.company_restriction.company_restriction.get_permission_query_conditions",
|
||||
"Customer": "erpnext.stock.doctype.company_restriction.company_restriction.get_permission_query_conditions",
|
||||
"Supplier": "erpnext.stock.doctype.company_restriction.company_restriction.get_permission_query_conditions",
|
||||
"Voice Call Settings": "erpnext.telephony.doctype.voice_call_settings.voice_call_settings.get_permission_query_conditions",
|
||||
"*": "erpnext.stock.doctype.company_restriction.company_restriction.get_inherited_permission_query_conditions",
|
||||
}
|
||||
|
||||
has_permission = {
|
||||
"Item": "erpnext.stock.doctype.company_restriction.company_restriction.has_permission",
|
||||
"Customer": "erpnext.stock.doctype.company_restriction.company_restriction.has_permission",
|
||||
"Supplier": "erpnext.stock.doctype.company_restriction.company_restriction.has_permission",
|
||||
"Voice Call Settings": "erpnext.telephony.doctype.voice_call_settings.voice_call_settings.has_permission",
|
||||
"*": "erpnext.stock.doctype.company_restriction.company_restriction.has_inherited_permission",
|
||||
}
|
||||
|
||||
>>>>>>> 7c88594 (fix(telephony): scope voice call settings to their user)
|
||||
has_website_permission = {
|
||||
"Sales Order": "erpnext.controllers.website_list_for_contact.has_website_permission",
|
||||
"Quotation": "erpnext.controllers.website_list_for_contact.has_website_permission",
|
||||
|
||||
@@ -460,3 +460,15 @@ erpnext.patches.v16_0.remove_all_role_from_payment_terms_template
|
||||
execute:from erpnext.setup.install import grant_address_read_to_accounts_manager; grant_address_read_to_accounts_manager()
|
||||
erpnext.patches.v16_0.remove_invalid_fetch_from_italy_payment_schedule_swift_number
|
||||
erpnext.patches.v16_0.set_user_on_timesheets
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
erpnext.patches.v16_0.replace_legacy_print_formats_with_classic
|
||||
erpnext.patches.v16_0.set_stock_value_in_stock_closing_balance
|
||||
erpnext.patches.v16_0.reset_fifo_queue_in_stock_closing_balance
|
||||
erpnext.patches.v16_0.remove_invalid_fields_from_copy_fields_to_variants
|
||||
erpnext.patches.v16_0.fix_ifrs_cash_flow_template_formulas
|
||||
erpnext.patches.v16_0.disable_add_to_transit_on_standard_stock_entry_types
|
||||
erpnext.patches.v16_0.unhide_item_barcode_fields
|
||||
execute:frappe.db.set_value("Report", "Negative Batch Report", "prepared_report", 1)
|
||||
erpnext.patches.v16_0.scope_voice_call_settings_to_desk_users
|
||||
>>>>>>> 7c88594 (fix(telephony): scope voice call settings to their user)
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import frappe
|
||||
from frappe.permissions import get_doctypes_with_custom_docperms
|
||||
from frappe.utils import cint
|
||||
|
||||
from erpnext.patches.v16_0.mirror_release_perms_to_custom_docperm import mirror_rules
|
||||
from erpnext.patches.v16_0.remove_all_role_from_payment_terms_template import remove_rule
|
||||
from erpnext.setup.install import get_role_rules
|
||||
|
||||
DOCTYPE = "Voice Call Settings"
|
||||
DESK_USER_PTYPES = ("read", "write", "create", "report", "export", "print", "email")
|
||||
|
||||
|
||||
def execute():
|
||||
"""Move customised sites from the `All` rule to the `Desk User` rule shipped in the DocType."""
|
||||
if DOCTYPE not in get_doctypes_with_custom_docperms():
|
||||
return
|
||||
|
||||
mirror_rules({DOCTYPE: {"Desk User": DESK_USER_PTYPES}})
|
||||
removed = False
|
||||
for rule in get_role_rules(DOCTYPE, "All"):
|
||||
if not cint(rule.permlevel) and remove_rule(DOCTYPE, rule.name):
|
||||
removed = True
|
||||
if removed:
|
||||
frappe.clear_cache(doctype=DOCTYPE)
|
||||
@@ -1,9 +1,78 @@
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
<<<<<<< HEAD
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
|
||||
class TestVoiceCallSettings(unittest.TestCase):
|
||||
pass
|
||||
=======
|
||||
import frappe
|
||||
import frappe.client
|
||||
|
||||
from erpnext.tests.permission_test_utils import as_user, make_fenced_user
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
class TestVoiceCallSettings(ERPNextTestSuite):
|
||||
def test_user_creates_own_settings(self):
|
||||
user = make_user("voice-call-owner@example.com")
|
||||
other_user = make_user("voice-call-other@example.com")
|
||||
|
||||
with self.set_user(user):
|
||||
own = frappe.get_doc({"doctype": "Voice Call Settings", "user": user}).insert()
|
||||
self.assertEqual((own.name, own.user), (user, user))
|
||||
|
||||
frappe.delete_doc("Voice Call Settings", own.name)
|
||||
with self.set_user(user):
|
||||
picked = frappe.get_doc({"doctype": "Voice Call Settings", "user": other_user}).insert()
|
||||
self.assertEqual((picked.name, picked.user), (user, user))
|
||||
|
||||
def test_settings_are_limited_to_their_user(self):
|
||||
owner = make_user("voice-call-owner@example.com")
|
||||
settings = make_settings(owner, greeting_message="Owner greeting")
|
||||
|
||||
other_user = make_user("voice-call-other@example.com")
|
||||
website_user = make_fenced_user("voice-call-website@example.com", [])
|
||||
with as_user(website_user):
|
||||
self.assertRaises(frappe.PermissionError, frappe.get_list, "Voice Call Settings")
|
||||
with as_user(other_user):
|
||||
self.assertNotIn(owner, frappe.get_list("Voice Call Settings", pluck="name"))
|
||||
|
||||
for outsider in (other_user, website_user):
|
||||
with as_user(outsider):
|
||||
self.assertRaises(frappe.PermissionError, frappe.client.get, "Voice Call Settings", owner)
|
||||
self.assertRaises(
|
||||
frappe.PermissionError,
|
||||
frappe.client.set_value,
|
||||
"Voice Call Settings",
|
||||
owner,
|
||||
"greeting_message",
|
||||
"Changed",
|
||||
)
|
||||
self.assertRaises(frappe.PermissionError, frappe.delete_doc, "Voice Call Settings", owner)
|
||||
|
||||
with as_user(owner):
|
||||
self.assertEqual(frappe.get_list("Voice Call Settings", pluck="name"), [owner])
|
||||
frappe.client.set_value("Voice Call Settings", owner, "greeting_message", "Owner changed")
|
||||
|
||||
with as_user(make_fenced_user("voice-call-manager@example.com", ["System Manager"])):
|
||||
self.assertIn(owner, frappe.get_list("Voice Call Settings", pluck="name"))
|
||||
self.assertEqual(
|
||||
frappe.client.get("Voice Call Settings", owner)["greeting_message"], "Owner changed"
|
||||
)
|
||||
self.assertTrue(frappe.db.exists("Voice Call Settings", settings.name))
|
||||
|
||||
|
||||
def make_user(email: str) -> str:
|
||||
return make_fenced_user(email, ["Sales User"])
|
||||
|
||||
|
||||
def make_settings(user: str, **values) -> "frappe.model.document.Document":
|
||||
if not frappe.db.exists("Voice Call Settings", user):
|
||||
frappe.get_doc({"doctype": "Voice Call Settings", "user": user}).insert(ignore_permissions=True)
|
||||
frappe.db.set_value("Voice Call Settings", user, values)
|
||||
return frappe.get_doc("Voice Call Settings", user)
|
||||
>>>>>>> 7c88594 (fix(telephony): scope voice call settings to their user)
|
||||
|
||||
@@ -53,7 +53,11 @@
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
<<<<<<< HEAD
|
||||
"modified": "2020-12-14 18:49:34.600194",
|
||||
=======
|
||||
"modified": "2026-10-15 15:16:45.554169",
|
||||
>>>>>>> 7c88594 (fix(telephony): scope voice call settings to their user)
|
||||
"modified_by": "Administrator",
|
||||
"module": "Telephony",
|
||||
"name": "Voice Call Settings",
|
||||
@@ -61,14 +65,12 @@
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "All",
|
||||
"share": 1,
|
||||
"role": "Desk User",
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
@@ -114,8 +116,7 @@
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "All",
|
||||
"share": 1
|
||||
"role": "Desk User"
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
ADMIN_ROLE = "System Manager"
|
||||
|
||||
|
||||
class VoiceCallSettings(Document):
|
||||
# begin: auto-generated types
|
||||
@@ -22,4 +24,38 @@ class VoiceCallSettings(Document):
|
||||
user: DF.Link
|
||||
# end: auto-generated types
|
||||
|
||||
<<<<<<< HEAD
|
||||
pass
|
||||
=======
|
||||
def before_insert(self):
|
||||
self.set_user_for_restricted_creator()
|
||||
|
||||
def set_user_for_restricted_creator(self):
|
||||
"""Users who may not pick the user create their own settings."""
|
||||
if self.flags.ignore_permissions or self.has_permlevel_access_to("user", permission_type="write"):
|
||||
return
|
||||
|
||||
self.user = frappe.session.user
|
||||
self.flags.ignore_permlevel_for_fields = ["user"]
|
||||
|
||||
|
||||
def has_permission(doc: VoiceCallSettings, ptype: str | None = None, user: str | None = None) -> bool:
|
||||
"""Allow non-admin users to access only their own Voice Call Settings.
|
||||
|
||||
Create is not scoped here because `before_insert` assigns the record to the creator."""
|
||||
user = user or frappe.session.user
|
||||
return ptype == "create" or is_admin(user) or doc.user == user
|
||||
|
||||
|
||||
def get_permission_query_conditions(user: str | None = None) -> str | None:
|
||||
"""Restrict list views and queries to the user's own record for non-admin users."""
|
||||
user = user or frappe.session.user
|
||||
if is_admin(user):
|
||||
return None
|
||||
|
||||
return f"`tabVoice Call Settings`.`user` = {frappe.db.escape(user)}"
|
||||
|
||||
|
||||
def is_admin(user: str) -> bool:
|
||||
return ADMIN_ROLE in frappe.get_roles(user)
|
||||
>>>>>>> 7c88594 (fix(telephony): scope voice call settings to their user)
|
||||
|
||||
Reference in New Issue
Block a user