Skip to content

Migrating customizations from HD Ticket Comment to Comment

RitvikSardana edited this page Sep 9, 2026 · 1 revision

Helpdesk used to store ticket comments in its own HD Ticket Comment doctype. As of #3700 it stores them in Frappe's core Comment doctype. Ticket activity moved there too, and HD Notification moved to core Notification Log.

If you wrote custom code against HD Ticket Comment, it still runs, but it now reads and writes rows nobody looks at. This guide tells you what to change.

What happened to your data

Nothing was lost. The migration patches copy every row into Comment and keep the same name, so a link, a bookmark, or a stored comment id still resolves.

The old doctypes are still installed and still hold the old rows. They are no longer written to. Treat them as read-only history.

Field mapping

HD Ticket Comment Comment
name name (unchanged)
content content
reference_ticket reference_name, with reference_doctype = "HD Ticket"
commented_by comment_email
(derived) comment_by, the author's full name
is_pinned is_pinned (custom field)
reactions reactions (custom field, still HD Comment Reaction)

Two more columns matter on the new side:

  • comment_type is "Comment" for a real comment and "Info" for a ticket activity row that used to live in HD Ticket Activity.
  • Comment holds every kind of comment in the site, not just ticket ones. Always filter on reference_doctype and comment_type, or you will pick up attachments, edits, likes, and comments from other apps.

comment_email holds a user id, not an email address. For every user except Administrator and Guest those are the same string, because Frappe forces User.email = User.name on save.

Creating a comment

Use the core document method. It sets reference_doctype, reference_name and comment_email for you.

ticket = frappe.get_doc("HD Ticket", "1234")
comment = ticket.add_comment("Comment", "Called the customer, no answer.")

Helpdesk's own wrapper does a little more, so prefer it if you also have file attachments to move onto the comment:

ticket.new_comment(content, attachments=[{"file_url": "/files/x.png"}])

new_comment refuses non-agents and reparents uploaded files from the ticket to the comment, which is what keeps them out of the customer portal.

Do not build the doc by hand unless you need to. If you must:

frappe.get_doc({
	"doctype": "Comment",
	"comment_type": "Comment",
	"reference_doctype": "HD Ticket",
	"reference_name": ticket_name,
	"comment_email": frappe.session.user,
	"content": content,
}).insert(ignore_permissions=True)

ignore_permissions=True is normal here, because core inserts comments that way. Helpdesk still gates the write in a before_insert hook, so a non-agent is rejected either way.

Reading comments

comments = frappe.get_all(
	"Comment",
	filters={
		"reference_doctype": "HD Ticket",
		"reference_name": ticket_name,
		"comment_type": "Comment",
	},
	fields=["name", "content", "comment_email", "creation", "is_pinned"],
	order_by="creation asc",
)

Drop comment_type from the filter to include activity rows, or set it to "Info" to get only those.

Hooks

Move your doc_events from HD Ticket Comment to Comment, and guard the handler, because it will now fire for every comment in the site.

# hooks.py
doc_events = {
	"Comment": {
		"after_insert": "my_app.comments.after_insert",
	},
}
def after_insert(doc, method=None):
	if doc.reference_doctype != "HD Ticket" or doc.comment_type != "Comment":
		return
	...

That guard is not optional. Without it your handler runs on attachment records, edit trails, and comments belonging to other apps.

Permissions

Comments on tickets are internal agent notes, and customers must never read them. Helpdesk enforces this with a has_permission hook, so a customer is refused even though they can read the ticket itself.

If you add your own has_permission for Comment, remember that Frappe runs all of them and any one returning False denies. Do not return True for a ticket comment unless you have checked the user is an agent.

The migration also grants Agent and Agent Manager full create, read, write and delete on Comment. No customer-facing role is granted anything.

Reactions

HD Comment Reaction still exists and still holds the rows. Only the parent changed: parenttype is now "Comment" instead of "HD Ticket Comment", and the field on the parent is still called reactions.

The whitelisted methods kept their names but moved module:

Before Now
helpdesk.helpdesk.doctype.hd_ticket_comment.hd_ticket_comment.toggle_reaction helpdesk.api.comment.toggle_reaction
...hd_ticket_comment.get_reactions helpdesk.api.comment.get_reactions
...hd_ticket_comment.get_preset_emojis helpdesk.api.comment.get_preset_emojis

Write reaction rows through toggle_reaction, not by saving the parent comment. Saving the parent restamps the comment and rewrites the ticket's _comments cache, and it races other people reacting at the same time.

Attachments

Files attached to a comment now carry attached_to_doctype = "Comment". If you query files by attached_to_doctype = "HD Ticket Comment", update it.

This matters for visibility, not just tidiness. A file row attached to the ticket is readable by every customer on that ticket. Attaching it to the comment is what makes it agent-only.

Notifications

HD Notification became core Notification Log.

HD Notification Notification Log
user_to for_user
user_from from_user
notification_type type
reference_ticket document_name, with document_type = "HD Ticket"
reference_comment source_name, with source_doctype = "Comment"
read read
message subject and title

Every helpdesk row also carries app = "helpdesk". Filter on it, or you will pick up notifications from every other app on the site.

Mentions and assignments are now created by Frappe core, not by helpdesk. If you had code creating an HD Notification for either, delete it, or you will get two notifications. To create your own:

from frappe.desk.doctype.notification_log.notification_log import (
	enqueue_create_notification,
)

enqueue_create_notification(
	[agent_email],
	{
		"type": "Mention",
		"document_type": "HD Ticket",
		"document_name": ticket_name,
		"subject": "Something happened",
		"from_user": frappe.session.user,
		"app": "helpdesk",
	},
)

Recipients are resolved by email, and the resolved user id is what lands in for_user. Pass User.email, and read back with User.name. They differ for Administrator.

Checklist

  1. Grep your app for HD Ticket Comment, HD Notification, and hd_ticket_comment module paths.
  2. Move doc_events to Comment and add the reference_doctype guard.
  3. Swap reference_ticket for reference_name, commented_by for comment_email.
  4. Add comment_type to every query.
  5. Update the three reaction method paths.
  6. Update file queries from attached_to_doctype = "HD Ticket Comment".
  7. Update notification queries to Notification Log and add app = "helpdesk".
  8. Delete any code that created a mention or assignment notification by hand.