diff --git a/crm/api/doc.py b/crm/api/doc.py
index 1369a952..4c326b2f 100644
--- a/crm/api/doc.py
+++ b/crm/api/doc.py
@@ -662,7 +662,7 @@ def get_fields_meta(doctype, restricted_fieldtypes=None, as_array=False, only_re
@frappe.whitelist()
def remove_assignments(doctype, name, assignees, ignore_permissions=False):
- assignees = json.loads(assignees)
+ assignees = frappe.parse_json(assignees)
if not assignees:
return
diff --git a/crm/api/todo.py b/crm/api/todo.py
index dc9dcf28..685e756b 100644
--- a/crm/api/todo.py
+++ b/crm/api/todo.py
@@ -1,90 +1,79 @@
import frappe
from frappe import _
+
from crm.fcrm.doctype.crm_notification.crm_notification import notify_user
def after_insert(doc, method):
- if (
- doc.reference_type in ["CRM Lead", "CRM Deal"]
- and doc.reference_name
- and doc.allocated_to
- ):
- fieldname = "lead_owner" if doc.reference_type == "CRM Lead" else "deal_owner"
- lead_owner = frappe.db.get_value(
- doc.reference_type, doc.reference_name, fieldname
- )
- if not lead_owner:
- frappe.db.set_value(
- doc.reference_type, doc.reference_name, fieldname, doc.allocated_to
- )
+ if doc.reference_type in ["CRM Lead", "CRM Deal"] and doc.reference_name and doc.allocated_to:
+ fieldname = "lead_owner" if doc.reference_type == "CRM Lead" else "deal_owner"
+ owner = frappe.db.get_value(doc.reference_type, doc.reference_name, fieldname)
+ if not owner:
+ frappe.db.set_value(
+ doc.reference_type, doc.reference_name, fieldname, doc.allocated_to, update_modified=False
+ )
- if (
- doc.reference_type in ["CRM Lead", "CRM Deal", "CRM Task"]
- and doc.reference_name
- and doc.allocated_to
- ):
- notify_assigned_user(doc)
+ if doc.reference_type in ["CRM Lead", "CRM Deal", "CRM Task"] and doc.reference_name and doc.allocated_to:
+ notify_assigned_user(doc)
def on_update(doc, method):
- if (
- doc.has_value_changed("status")
- and doc.status == "Cancelled"
- and doc.reference_type in ["CRM Lead", "CRM Deal", "CRM Task"]
- and doc.reference_name
- and doc.allocated_to
- ):
- notify_assigned_user(doc, is_cancelled=True)
+ if (
+ doc.has_value_changed("status")
+ and doc.status == "Cancelled"
+ and doc.reference_type in ["CRM Lead", "CRM Deal", "CRM Task"]
+ and doc.reference_name
+ and doc.allocated_to
+ ):
+ notify_assigned_user(doc, is_cancelled=True)
def notify_assigned_user(doc, is_cancelled=False):
- _doc = frappe.get_doc(doc.reference_type, doc.reference_name)
- owner = frappe.get_cached_value("User", frappe.session.user, "full_name")
- notification_text = get_notification_text(owner, doc, _doc, is_cancelled)
+ _doc = frappe.get_doc(doc.reference_type, doc.reference_name)
+ owner = frappe.get_cached_value("User", frappe.session.user, "full_name")
+ notification_text = get_notification_text(owner, doc, _doc, is_cancelled)
- message = (
- _("Your assignment on {0} {1} has been removed by {2}").format(
- doc.reference_type, doc.reference_name, owner
- )
- if is_cancelled
- else _("{0} assigned a {1} {2} to you").format(
- owner, doc.reference_type, doc.reference_name
- )
- )
+ message = (
+ _("Your assignment on {0} {1} has been removed by {2}").format(
+ doc.reference_type, doc.reference_name, owner
+ )
+ if is_cancelled
+ else _("{0} assigned a {1} {2} to you").format(owner, doc.reference_type, doc.reference_name)
+ )
- redirect_to_doctype, redirect_to_name = get_redirect_to_doc(doc)
+ redirect_to_doctype, redirect_to_name = get_redirect_to_doc(doc)
- notify_user(
- {
- "owner": frappe.session.user,
- "assigned_to": doc.allocated_to,
- "notification_type": "Assignment",
- "message": message,
- "notification_text": notification_text,
- "reference_doctype": doc.reference_type,
- "reference_docname": doc.reference_name,
- "redirect_to_doctype": redirect_to_doctype,
- "redirect_to_docname": redirect_to_name,
- }
- )
+ notify_user(
+ {
+ "owner": frappe.session.user,
+ "assigned_to": doc.allocated_to,
+ "notification_type": "Assignment",
+ "message": message,
+ "notification_text": notification_text,
+ "reference_doctype": doc.reference_type,
+ "reference_docname": doc.reference_name,
+ "redirect_to_doctype": redirect_to_doctype,
+ "redirect_to_docname": redirect_to_name,
+ }
+ )
def get_notification_text(owner, doc, reference_doc, is_cancelled=False):
- name = doc.reference_name
- doctype = doc.reference_type
+ name = doc.reference_name
+ doctype = doc.reference_type
- if doctype.startswith("CRM "):
- doctype = doctype[4:].lower()
+ if doctype.startswith("CRM "):
+ doctype = doctype[4:].lower()
- if doctype in ["lead", "deal"]:
- name = (
- reference_doc.lead_name or name
- if doctype == "lead"
- else reference_doc.organization or reference_doc.lead_name or name
- )
+ if doctype in ["lead", "deal"]:
+ name = (
+ reference_doc.lead_name or name
+ if doctype == "lead"
+ else reference_doc.organization or reference_doc.lead_name or name
+ )
- if is_cancelled:
- return f"""
+ if is_cancelled:
+ return f"""
{ _('Your assignment on {0} {1} has been removed by {2}').format(
doctype,
@@ -94,7 +83,7 @@ def get_notification_text(owner, doc, reference_doc, is_cancelled=False):
"""
- return f"""
+ return f"""
{ owner }
{ _('assigned a {0} {1} to you').format(
@@ -104,9 +93,9 @@ def get_notification_text(owner, doc, reference_doc, is_cancelled=False):
"""
- if doctype == "task":
- if is_cancelled:
- return f"""
+ if doctype == "task":
+ if is_cancelled:
+ return f"""
{ _('Your assignment on task {0} has been removed by {1}').format(
f'{ reference_doc.title }',
@@ -114,7 +103,7 @@ def get_notification_text(owner, doc, reference_doc, is_cancelled=False):
) }
"""
- return f"""
+ return f"""
{ owner }
{ _('assigned a new task {0} to you').format(
@@ -125,8 +114,8 @@ def get_notification_text(owner, doc, reference_doc, is_cancelled=False):
def get_redirect_to_doc(doc):
- if doc.reference_type == "CRM Task":
- reference_doc = frappe.get_doc(doc.reference_type, doc.reference_name)
- return reference_doc.reference_doctype, reference_doc.reference_docname
+ if doc.reference_type == "CRM Task":
+ reference_doc = frappe.get_doc(doc.reference_type, doc.reference_name)
+ return reference_doc.reference_doctype, reference_doc.reference_docname
- return doc.reference_type, doc.reference_name
+ return doc.reference_type, doc.reference_name
diff --git a/frontend/auto-imports.d.ts b/frontend/auto-imports.d.ts
new file mode 100644
index 00000000..9d240079
--- /dev/null
+++ b/frontend/auto-imports.d.ts
@@ -0,0 +1,10 @@
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// noinspection JSUnusedGlobalSymbols
+// Generated by unplugin-auto-import
+// biome-ignore lint: disable
+export {}
+declare global {
+
+}
diff --git a/frontend/components.d.ts b/frontend/components.d.ts
index afedd3ed..8b1b706a 100644
--- a/frontend/components.d.ts
+++ b/frontend/components.d.ts
@@ -25,6 +25,7 @@ declare module 'vue' {
AscendingIcon: typeof import('./src/components/Icons/AscendingIcon.vue')['default']
AssignmentModal: typeof import('./src/components/Modals/AssignmentModal.vue')['default']
AssignTo: typeof import('./src/components/AssignTo.vue')['default']
+ AssignToBody: typeof import('./src/components/AssignToBody.vue')['default']
AttachmentArea: typeof import('./src/components/Activities/AttachmentArea.vue')['default']
AttachmentIcon: typeof import('./src/components/Icons/AttachmentIcon.vue')['default']
AttachmentItem: typeof import('./src/components/AttachmentItem.vue')['default']
@@ -172,6 +173,7 @@ declare module 'vue' {
LucideChevronRight: typeof import('~icons/lucide/chevron-right')['default']
LucidePenLine: typeof import('~icons/lucide/pen-line')['default']
LucideRefreshCcw: typeof import('~icons/lucide/refresh-ccw')['default']
+ LucideX: typeof import('~icons/lucide/x')['default']
MarkAsDoneIcon: typeof import('./src/components/Icons/MarkAsDoneIcon.vue')['default']
MaximizeIcon: typeof import('./src/components/Icons/MaximizeIcon.vue')['default']
MenuIcon: typeof import('./src/components/Icons/MenuIcon.vue')['default']
diff --git a/frontend/src/components/Activities/Activities.vue b/frontend/src/components/Activities/Activities.vue
index 32528416..2c8d0341 100644
--- a/frontend/src/components/Activities/Activities.vue
+++ b/frontend/src/components/Activities/Activities.vue
@@ -50,11 +50,13 @@
class="activity grid grid-cols-[30px_minmax(auto,_1fr)] gap-2 px-3 sm:gap-4 sm:px-10"
>
@@ -72,11 +74,13 @@
class="activity grid grid-cols-[30px_minmax(auto,_1fr)] gap-4 px-3 sm:px-10"
>
+
diff --git a/frontend/src/components/AssignTo.vue b/frontend/src/components/AssignTo.vue
index 1f42df3a..96d29cb6 100644
--- a/frontend/src/components/AssignTo.vue
+++ b/frontend/src/components/AssignTo.vue
@@ -1,31 +1,100 @@
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/components/AssignToBody.vue b/frontend/src/components/AssignToBody.vue
new file mode 100644
index 00000000..e4e226b0
--- /dev/null
+++ b/frontend/src/components/AssignToBody.vue
@@ -0,0 +1,212 @@
+
+
+
{{ __('Assign to') }}
+
addValue(option) && ($refs.input.value = '')"
+ :placeholder="__('John Doe')"
+ :filters="{
+ name: ['in', users.data.crmUsers?.map((user) => user.name)],
+ }"
+ :hideMe="true"
+ >
+
+
+
+
+
+
+
{{ getUser(assignee.name).full_name }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ getUser(option.value).full_name }}
+
+
+
+
+
+
+ {{ __('Assign to me') }}
+
+
+
+
+
+
+
diff --git a/frontend/src/components/Modals/AssignmentModal.vue b/frontend/src/components/Modals/AssignmentModal.vue
index c6fc6dfd..0a0533dd 100644
--- a/frontend/src/components/Modals/AssignmentModal.vue
+++ b/frontend/src/components/Modals/AssignmentModal.vue
@@ -1,30 +1,8 @@