diff --git a/desk/src/components/CommunicationArea.vue b/desk/src/components/CommunicationArea.vue
index 0ef730e5e6..6dd4c2f971 100644
--- a/desk/src/components/CommunicationArea.vue
+++ b/desk/src/components/CommunicationArea.vue
@@ -245,4 +245,4 @@ onClickOutside(
width: 100vw;
}
}
-
+
\ No newline at end of file
diff --git a/desk/src/components/EmailEditor.vue b/desk/src/components/EmailEditor.vue
index c154e74f03..0b27bdf393 100644
--- a/desk/src/components/EmailEditor.vue
+++ b/desk/src/components/EmailEditor.vue
@@ -169,6 +169,8 @@
+
\ No newline at end of file
diff --git a/desk/src/components/Settings/Profile/Profile.vue b/desk/src/components/Settings/Profile/Profile.vue
index dd3280f348..31f440101b 100644
--- a/desk/src/components/Settings/Profile/Profile.vue
+++ b/desk/src/components/Settings/Profile/Profile.vue
@@ -114,6 +114,36 @@
maxlength="40"
v-model="profile.lastName"
/>
+
+
+
+
+
+
@@ -182,6 +212,7 @@ import {
Dropdown,
FileUploader,
LoadingIndicator,
+ TextEditor,
toast,
} from "frappe-ui";
import { Autocomplete } from "@/components";
@@ -194,13 +225,17 @@ import SettingsLayoutBase from "@/components/layouts/SettingsLayoutBase.vue";
import Link from "@/components/frappe-ui/Link.vue";
import { HDAgent } from "@/types/doctypes";
+
const auth = useAuthStore();
const profile = ref({
fullName: auth.userName,
userImage: auth.userImage,
firstName: auth.userFirstName,
lastName: auth.userLastName,
+ availabilityStatus: "Available",
+ signature: "",
});
+
const showChangePasswordModal = ref(false);
const language = ref(auth.language);
const timezone = ref(auth.timezone);
@@ -215,11 +250,15 @@ const isTimezoneChanged = computed(() => {
});
const isAccountInfoDirty = computed(() => {
- const agentName = agentData.data?.agent_name?.split(" ");
- if (!agentName) return false;
+ const data = agentData.data;
+ if (!data) return false;
+ const agentName = data.agent_name?.split(" ") || [];
+
const isDirty =
- profile.value.firstName !== agentName[0] ||
- profile.value.lastName !== (agentName[1] || "");
+ profile.value.firstName !== (agentName[0] || "") ||
+ profile.value.lastName !== (agentName[1] || "") ||
+ profile.value.availabilityStatus !== agentData.data?.availability_status ||
+ (profile.value.signature?.trim() || "") !== (data.signature?.trim() || "")
if (isDirty) {
disableSettingModalOutsideClick.value = true;
} else {
@@ -244,7 +283,10 @@ const agentData = createResource({
firstName: fullName[0],
lastName: fullName[1] || "",
userImage: data.user_image,
+ availabilityStatus: data.availability_status,
+ signature: data.signature || "",
};
+
},
});
@@ -273,6 +315,8 @@ const setAgent = createResource({
fieldname: {
agent_name: `${profile.value.firstName} ${profile.value.lastName}`,
user_image: profile.value.userImage,
+ availability_status: profile.value.availabilityStatus,
+ signature: profile.value.signature || "",
},
};
},
diff --git a/desk/src/stores/auth.ts b/desk/src/stores/auth.ts
index df69620331..fa6bc71e6d 100644
--- a/desk/src/stores/auth.ts
+++ b/desk/src/stores/auth.ts
@@ -47,6 +47,11 @@ export const useAuthStore = defineStore("auth", () => {
);
const language: ComputedRef = computed(() => user__.value.language);
+ const userSignature: ComputedRef = computed(() => {
+ return user__.value.signature || "";
+});
+
+
function sessionUser() {
const cookies = new URLSearchParams(document.cookie.split("; ").join("&"));
let _sessionUser = cookies.get("user_id");
@@ -93,6 +98,7 @@ export const useAuthStore = defineStore("auth", () => {
timezone,
userTeams,
language,
+ userSignature,
user,
logout,
};
diff --git a/desk/src/types/doctypes.ts b/desk/src/types/doctypes.ts
index e8127f420e..06576e05cd 100644
--- a/desk/src/types/doctypes.ts
+++ b/desk/src/types/doctypes.ts
@@ -29,7 +29,6 @@ export interface HDTicketStatus extends DocType {
order?: number;
/** Enabled: Check */
enabled: 0 | 1;
- parsed_color?: string;
}
// Last updated: 2026-01-20 15:18:57.195606
@@ -246,6 +245,7 @@ export interface HDServiceLevelAgreement extends DocType {
default_ticket_status?: string;
}
+// Last updated: 2026-02-09 22:08:22.055831
// Last updated: 2026-02-02 11:15:47.402850
export interface HDAgent extends DocType {
/** User: Link (User) */
@@ -254,6 +254,10 @@ export interface HDAgent extends DocType {
agent_name: string;
/** Is Active: Check */
is_active: 0 | 1;
+ /** Availability Status: Select */
+ availability_status: 'Available' | 'Away';
+ /** Signature: Small Text */
+ signature?: string;
/** Image: Attach Image */
user_image?: string;
}
diff --git a/helpdesk/api/auth.py b/helpdesk/api/auth.py
index 45a31cc0b6..6959e091cf 100644
--- a/helpdesk/api/auth.py
+++ b/helpdesk/api/auth.py
@@ -23,7 +23,7 @@ def get_user():
fieldname=fields,
as_dict=True,
)
-
+
is_agent = _is_agent()
is_admin = ("System Manager" or "Admistrator") in frappe.get_roles(current_user)
has_desk_access = is_agent or is_admin
@@ -38,7 +38,13 @@ def get_user():
language = user.language or frappe.db.get_single_value(
"System Settings", "language"
)
-
+ signature = frappe.db.get_value(
+ "HD Agent",
+ {"user": current_user},
+ "signature"
+ ) or ""
+ frappe.logger().debug(f"Signature for {current_user}: {signature}") # 👈 add this
+ print(f"Signature for {current_user}: {signature}") # 👈 add this
return {
"has_desk_access": has_desk_access,
"is_admin": is_admin,
@@ -52,4 +58,7 @@ def get_user():
"time_zone": user.time_zone,
"user_teams": user_team_names,
"language": language,
+ "signature":signature ,
+
}
+
diff --git a/helpdesk/helpdesk/doctype/hd_agent/hd_agent.json b/helpdesk/helpdesk/doctype/hd_agent/hd_agent.json
index b51d039761..971830417d 100644
--- a/helpdesk/helpdesk/doctype/hd_agent/hd_agent.json
+++ b/helpdesk/helpdesk/doctype/hd_agent/hd_agent.json
@@ -9,7 +9,9 @@
"user",
"agent_name",
"user_image",
- "is_active"
+ "is_active",
+ "availability_status",
+ "signature"
],
"fields": [
{
@@ -34,6 +36,21 @@
"in_list_view": 1,
"label": "Is Active"
},
+ {
+ "default": "Available",
+ "fieldname": "availability_status",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Availability Status",
+ "options": "Available\nAway",
+ "reqd": 1
+ },
+ {
+ "description": "automatically added in reply section",
+ "fieldname": "signature",
+ "fieldtype": "Text Editor",
+ "label": "Signature"
+ },
{
"fieldname": "user_image",
"fieldtype": "Attach Image",
@@ -42,7 +59,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2026-02-02 11:15:47.402850",
+ "modified": "2026-02-09 22:08:22.055831",
"modified_by": "Administrator",
"module": "Helpdesk",
"name": "HD Agent",
diff --git a/helpdesk/helpdesk/doctype/hd_ticket/hd_ticket.js b/helpdesk/helpdesk/doctype/hd_ticket/hd_ticket.js
index 13f1b14eeb..8893a19627 100644
--- a/helpdesk/helpdesk/doctype/hd_ticket/hd_ticket.js
+++ b/helpdesk/helpdesk/doctype/hd_ticket/hd_ticket.js
@@ -6,4 +6,4 @@ frappe.ui.form.on("HD Ticket", {
if (frm.is_new()) return;
frm.call("mark_seen");
},
-});
+});
\ No newline at end of file
diff --git a/helpdesk/helpdesk/doctype/hd_ticket/hd_ticket.py b/helpdesk/helpdesk/doctype/hd_ticket/hd_ticket.py
index 23d7375a4c..65f3abc5c3 100644
--- a/helpdesk/helpdesk/doctype/hd_ticket/hd_ticket.py
+++ b/helpdesk/helpdesk/doctype/hd_ticket/hd_ticket.py
@@ -448,6 +448,8 @@ def assign_agent(self, agent: str):
if frappe.session.user != agent:
self.notify_agent(agent, "Assignment")
+
+
def get_assigned_agents(self):
assignees = get_assignees({"doctype": "HD Ticket", "name": self.name})
if len(assignees) > 0:
@@ -576,6 +578,8 @@ def reply_via_agent(
medium = "" if skip_email_workflow else "Email"
subject = f"Re: {self.subject}"
sender = frappe.session.user
+
+
recipients = to or self.raised_by
sender_email = None if skip_email_workflow else self.sender_email()
diff --git a/helpdesk/hooks.py b/helpdesk/hooks.py
index 2a45582aa3..3251e4cb64 100644
--- a/helpdesk/hooks.py
+++ b/helpdesk/hooks.py
@@ -66,8 +66,19 @@
"on_trash": "helpdesk.extends.assignment_rule.on_assignment_rule_trash",
"validate": "helpdesk.extends.assignment_rule.on_assignment_rule_validate",
},
+ "ToDo": {
+ "before_insert": "helpdesk.overrides.assign.validate_agent_availability",
+ },
+ "HD Ticket": {
+ "validate": "helpdesk.overrides.assign.validate_hd_ticket_agent",
+ },
+ "ToDo": {
+ "before_insert": "helpdesk.overrides.assign.validate_agent_availability",
+ },
+
}
+
has_permission = {
"HD Ticket": "helpdesk.helpdesk.doctype.hd_ticket.hd_ticket.has_permission",
"HD Saved Reply": "helpdesk.helpdesk.doctype.hd_saved_reply.hd_saved_reply.has_permission",
@@ -78,6 +89,8 @@
"HD Saved Reply": "helpdesk.helpdesk.doctype.hd_saved_reply.hd_saved_reply.permission_query",
}
+
+
# DocType Class
# ---------------
# Override standard doctype classes
@@ -101,3 +114,4 @@
before_tests = "helpdesk.test_utils.before_tests"
auth_hooks = ["helpdesk.auth.authenticate"]
+
diff --git a/helpdesk/overrides/__init__.py b/helpdesk/overrides/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/helpdesk/overrides/assign.py b/helpdesk/overrides/assign.py
new file mode 100644
index 0000000000..656a481d46
--- /dev/null
+++ b/helpdesk/overrides/assign.py
@@ -0,0 +1,46 @@
+import frappe
+from frappe import _
+
+
+def validate_agent_availability(doc, method=None):
+
+ if doc.reference_type != "HD Ticket":
+ return
+
+ assigned_to = doc.allocated_to or doc.owner
+
+ if not assigned_to:
+ return
+
+ status = frappe.db.get_value(
+ "HD Agent",
+ {"user": assigned_to},
+ "availability_status",
+ )
+
+ if status == "Away":
+ frappe.throw(
+ msg=_("This agent is marked as Away and cannot be assigned tickets."),
+ title=_("Agent Unavailable"),
+ exc=frappe.ValidationError,
+ )
+
+
+def validate_hd_ticket_agent(doc, method=None):
+ allocated_to = getattr(doc, "allocated_to", None)
+
+ if not allocated_to:
+ return
+
+ status = frappe.db.get_value(
+ "HD Agent",
+ {"user": doc.allocated_to},
+ "availability_status",
+ )
+
+ if status == "Away":
+ frappe.throw(
+ msg=_("This agent is marked as Away and cannot be assigned tickets."),
+ title=_("Agent Unavailable"),
+ exc=frappe.ValidationError,
+ )
\ No newline at end of file