fix: only show sales user filter to manager

This commit is contained in:
Shariq Ansari 2025-07-08 12:58:46 +05:30
parent 4b12918ba5
commit f747e076ab
8 changed files with 73 additions and 18 deletions

View File

@ -1,8 +1,11 @@
import frappe import frappe
from frappe import _ from frappe import _
from crm.utils import sales_user_only
@frappe.whitelist() @frappe.whitelist()
@sales_user_only
def get_number_card_data(from_date="", to_date="", user="", lead_conds="", deal_conds=""): def get_number_card_data(from_date="", to_date="", user="", lead_conds="", deal_conds=""):
""" """
Get number card data for the dashboard. Get number card data for the dashboard.
@ -11,6 +14,10 @@ def get_number_card_data(from_date="", to_date="", user="", lead_conds="", deal_
from_date = frappe.utils.get_first_day(from_date or frappe.utils.nowdate()) from_date = frappe.utils.get_first_day(from_date or frappe.utils.nowdate())
to_date = frappe.utils.get_last_day(to_date or frappe.utils.nowdate()) to_date = frappe.utils.get_last_day(to_date or frappe.utils.nowdate())
is_sales_user = "Sales User" in frappe.get_roles(frappe.session.user)
if is_sales_user and not user:
user = frappe.session.user
lead_chart_data = get_lead_count(from_date, to_date, user, lead_conds) lead_chart_data = get_lead_count(from_date, to_date, user, lead_conds)
deal_chart_data = get_deal_count(from_date, to_date, user, deal_conds) deal_chart_data = get_deal_count(from_date, to_date, user, deal_conds)
get_won_deal_count_data = get_won_deal_count(from_date, to_date, user, deal_conds) get_won_deal_count_data = get_won_deal_count(from_date, to_date, user, deal_conds)

View File

@ -23,9 +23,6 @@ def get_users():
if frappe.session.user == user.name: if frappe.session.user == user.name:
user.session_user = True user.session_user = True
user.is_manager = "Sales Manager" in frappe.get_roles(user.name)
user.is_admin = user.name == "Administrator"
user.roles = frappe.get_roles(user.name) user.roles = frappe.get_roles(user.name)
user.role = "" user.role = ""
@ -42,7 +39,7 @@ def get_users():
if frappe.session.user == user.name: if frappe.session.user == user.name:
user.session_user = True user.session_user = True
user.is_agent = frappe.db.exists("CRM Telephony Agent", {"user": user.name}) user.is_telephony_agent = frappe.db.exists("CRM Telephony Agent", {"user": user.name})
crm_users = [] crm_users = []

View File

@ -1,10 +1,13 @@
from frappe import frappe import functools
import frappe
import phonenumbers import phonenumbers
from frappe import _
from frappe.model.docstatus import DocStatus
from frappe.model.dynamic_links import get_dynamic_link_map
from frappe.utils import floor from frappe.utils import floor
from phonenumbers import NumberParseException from phonenumbers import NumberParseException
from phonenumbers import PhoneNumberFormat as PNF from phonenumbers import PhoneNumberFormat as PNF
from frappe.model.docstatus import DocStatus
from frappe.model.dynamic_links import get_dynamic_link_map
def parse_phone_number(phone_number, default_country="IN"): def parse_phone_number(phone_number, default_country="IN"):
@ -97,6 +100,7 @@ def seconds_to_duration(seconds):
else: else:
return "0s" return "0s"
# Extracted from frappe core frappe/model/delete_doc.py/check_if_doc_is_linked # Extracted from frappe core frappe/model/delete_doc.py/check_if_doc_is_linked
def get_linked_docs(doc, method="Delete"): def get_linked_docs(doc, method="Delete"):
from frappe.model.rename_doc import get_link_fields from frappe.model.rename_doc import get_link_fields
@ -161,6 +165,7 @@ def get_linked_docs(doc, method="Delete"):
) )
return docs return docs
# Extracted from frappe core frappe/model/delete_doc.py/check_if_doc_is_dynamically_linked # Extracted from frappe core frappe/model/delete_doc.py/check_if_doc_is_dynamically_linked
def get_dynamic_linked_docs(doc, method="Delete"): def get_dynamic_linked_docs(doc, method="Delete"):
docs = [] docs = []
@ -222,3 +227,42 @@ def get_dynamic_linked_docs(doc, method="Delete"):
} }
) )
return docs return docs
def is_admin(user: str | None = None) -> bool:
"""
Check whether `user` is an admin
:param user: User to check against, defaults to current user
:return: Whether `user` is an admin
"""
user = user or frappe.session.user
return user == "Administrator"
def is_sales_user(user: str | None = None) -> bool:
"""
Check whether `user` is an agent
:param user: User to check against, defaults to current user
:return: Whether `user` is an agent
"""
user = user or frappe.session.user
return is_admin() or "Sales Manager" in frappe.get_roles(user) or "Sales User" in frappe.get_roles(user)
def sales_user_only(fn):
"""Decorator to validate if user is an agent."""
@functools.wraps(fn)
def wrapper(*args, **kwargs):
if not is_sales_user():
frappe.throw(
msg=_("You are not permitted to access this resource."),
title=_("Not Allowed"),
exc=frappe.PermissionError,
)
return fn(*args, **kwargs)
return wrapper

View File

@ -163,6 +163,7 @@ declare module 'vue' {
ListRows: typeof import('./src/components/ListViews/ListRows.vue')['default'] ListRows: typeof import('./src/components/ListViews/ListRows.vue')['default']
LoadingIndicator: typeof import('./src/components/Icons/LoadingIndicator.vue')['default'] LoadingIndicator: typeof import('./src/components/Icons/LoadingIndicator.vue')['default']
LostReasonModal: typeof import('./src/components/Modals/LostReasonModal.vue')['default'] LostReasonModal: typeof import('./src/components/Modals/LostReasonModal.vue')['default']
LucideCalendar: typeof import('~icons/lucide/calendar')['default']
LucideInfo: typeof import('~icons/lucide/info')['default'] LucideInfo: typeof import('~icons/lucide/info')['default']
LucideMoreHorizontal: typeof import('~icons/lucide/more-horizontal')['default'] LucideMoreHorizontal: typeof import('~icons/lucide/more-horizontal')['default']
LucidePlus: typeof import('~icons/lucide/plus')['default'] LucidePlus: typeof import('~icons/lucide/plus')['default']

View File

@ -65,7 +65,7 @@ import {
import { Dialog, Avatar } from 'frappe-ui' import { Dialog, Avatar } from 'frappe-ui'
import { ref, markRaw, computed, watch, h } from 'vue' import { ref, markRaw, computed, watch, h } from 'vue'
const { isManager, isAgent, getUser } = usersStore() const { isManager, isTelephonyAgent, getUser } = usersStore()
const user = computed(() => getUser() || {}) const user = computed(() => getUser() || {})
@ -123,7 +123,7 @@ const tabs = computed(() => {
label: __('Telephony'), label: __('Telephony'),
icon: PhoneIcon, icon: PhoneIcon,
component: markRaw(TelephonySettings), component: markRaw(TelephonySettings),
condition: () => isManager() || isAgent(), condition: () => isManager() || isTelephonyAgent(),
}, },
{ {
label: __('WhatsApp'), label: __('WhatsApp'),
@ -138,7 +138,7 @@ const tabs = computed(() => {
condition: () => isManager(), condition: () => isManager(),
}, },
], ],
condition: () => isManager() || isAgent(), condition: () => isManager() || isTelephonyAgent(),
}, },
] ]

View File

@ -93,7 +93,7 @@ import { toast } from 'frappe-ui'
import { getRandom } from '@/utils' import { getRandom } from '@/utils'
import { ref, computed, watch } from 'vue' import { ref, computed, watch } from 'vue'
const { isManager, isAgent } = usersStore() const { isManager, isTelephonyAgent } = usersStore()
const twilioFields = createResource({ const twilioFields = createResource({
url: 'crm.api.doc.get_fields', url: 'crm.api.doc.get_fields',
@ -283,7 +283,7 @@ async function updateMedium() {
const error = ref('') const error = ref('')
function validateIfDefaultMediumIsEnabled() { function validateIfDefaultMediumIsEnabled() {
if (isAgent() && !isManager()) return true if (isTelephonyAgent() && !isManager()) return true
if (defaultCallingMedium.value === 'Twilio' && !twilio.doc.enabled) { if (defaultCallingMedium.value === 'Twilio' && !twilio.doc.enabled) {
error.value = __('Twilio is not enabled') error.value = __('Twilio is not enabled')

View File

@ -52,6 +52,7 @@
</template> </template>
</DateRangePicker> </DateRangePicker>
<Link <Link
v-if="isAdmin() || isManager()"
class="form-control w-48" class="form-control w-48"
variant="outline" variant="outline"
:value="filters.user && getUser(filters.user).full_name" :value="filters.user && getUser(filters.user).full_name"
@ -145,7 +146,7 @@ import {
} from 'frappe-ui' } from 'frappe-ui'
import { ref, reactive, computed } from 'vue' import { ref, reactive, computed } from 'vue'
const { users, getUser } = usersStore() const { users, getUser, isManager, isAdmin } = usersStore()
const showDatePicker = ref(false) const showDatePicker = ref(false)
const datePickerRef = ref(null) const datePickerRef = ref(null)

View File

@ -50,15 +50,19 @@ export const usersStore = defineStore('crm-users', () => {
} }
function isAdmin(email) { function isAdmin(email) {
return getUser(email).role === 'System Manager' || getUser(email).is_admin return getUser(email).role === 'System Manager'
} }
function isManager(email) { function isManager(email) {
return getUser(email).is_manager return getUser(email).role === 'Sales Manager'
} }
function isAgent(email) { function isSalesUser(email) {
return getUser(email).is_agent return getUser(email).role === 'Sales User'
}
function isTelephonyAgent(email) {
return getUser(email).is_telphony_agent
} }
function getUserRole(email) { function getUserRole(email) {
@ -74,7 +78,8 @@ export const usersStore = defineStore('crm-users', () => {
getUser, getUser,
isAdmin, isAdmin,
isManager, isManager,
isAgent, isSalesUser,
isTelephonyAgent,
getUserRole, getUserRole,
} }
}) })