From ad97722b1b35aff50d0adf13a19715f7b8373c07 Mon Sep 17 00:00:00 2001 From: Richard Sassine Date: Tue, 4 May 2021 18:00:38 +0300 Subject: initial commit --- helpdesk_mgmt/models/helpdesk_ticket.py | 258 ++++++++++++++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 helpdesk_mgmt/models/helpdesk_ticket.py (limited to 'helpdesk_mgmt/models/helpdesk_ticket.py') diff --git a/helpdesk_mgmt/models/helpdesk_ticket.py b/helpdesk_mgmt/models/helpdesk_ticket.py new file mode 100644 index 0000000..f93ba69 --- /dev/null +++ b/helpdesk_mgmt/models/helpdesk_ticket.py @@ -0,0 +1,258 @@ +from odoo import _, api, fields, models, tools +from odoo.exceptions import AccessError + + +class HelpdeskTicket(models.Model): + _name = "helpdesk.ticket" + _description = "Helpdesk Ticket" + _rec_name = "number" + _order = "number desc" + _mail_post_access = "read" + _inherit = ["mail.thread.cc", "mail.activity.mixin"] + + def _get_default_stage_id(self): + return self.env["helpdesk.ticket.stage"].search([], limit=1).id + + @api.model + def _read_group_stage_ids(self, stages, domain, order): + stage_ids = self.env["helpdesk.ticket.stage"].search([]) + return stage_ids + + number = fields.Char(string="Ticket number", default="/", readonly=True) + name = fields.Char(string="Title", required=True) + description = fields.Html(required=True, sanitize_style=True) + user_id = fields.Many2one(comodel_name="res.users", string="Assigned user") + user_ids = fields.Many2many( + comodel_name="res.users", related="team_id.user_ids", string="Users" + ) + stage_id = fields.Many2one( + comodel_name="helpdesk.ticket.stage", + string="Stage", + group_expand="_read_group_stage_ids", + default=_get_default_stage_id, + tracking=True, + ondelete="restrict", + index=True, + copy=False, + ) + partner_id = fields.Many2one(comodel_name="res.partner", string="Contact") + partner_name = fields.Char() + partner_email = fields.Char(string="Email") + + last_stage_update = fields.Datetime( + string="Last Stage Update", default=fields.Datetime.now + ) + assigned_date = fields.Datetime(string="Assigned Date") + closed_date = fields.Datetime(string="Closed Date") + closed = fields.Boolean(related="stage_id.closed") + unattended = fields.Boolean(related="stage_id.unattended") + tag_ids = fields.Many2many(comodel_name="helpdesk.ticket.tag", string="Tags") + company_id = fields.Many2one( + comodel_name="res.company", + string="Company", + required=True, + default=lambda self: self.env.company, + ) + channel_id = fields.Many2one( + comodel_name="helpdesk.ticket.channel", + string="Channel", + help="Channel indicates where the source of a ticket" + "comes from (it could be a phone call, an email...)", + ) + category_id = fields.Many2one( + comodel_name="helpdesk.ticket.category", + string="Category", + ) + team_id = fields.Many2one( + comodel_name="helpdesk.ticket.team", + string="Team", + ) + priority = fields.Selection( + selection=[ + ("0", _("Low")), + ("1", _("Medium")), + ("2", _("High")), + ("3", _("Very High")), + ], + string="Priority", + default="1", + ) + attachment_ids = fields.One2many( + comodel_name="ir.attachment", + inverse_name="res_id", + domain=[("res_model", "=", "helpdesk.ticket")], + string="Media Attachments", + ) + color = fields.Integer(string="Color Index") + kanban_state = fields.Selection( + selection=[ + ("normal", "Default"), + ("done", "Ready for next stage"), + ("blocked", "Blocked"), + ], + string="Kanban State", + ) + active = fields.Boolean(default=True) + + def send_user_mail(self): + self.env.ref("helpdesk_mgmt.assignment_email_template").send_mail(self.id) + + def assign_to_me(self): + self.write({"user_id": self.env.user.id}) + + @api.onchange("partner_id") + def _onchange_partner_id(self): + if self.partner_id: + self.partner_name = self.partner_id.name + self.partner_email = self.partner_id.email + + @api.onchange("team_id", "user_id") + def _onchange_dominion_user_id(self): + if self.user_id and self.user_ids and self.user_id not in self.team_id.user_ids: + self.update({"user_id": False}) + return {"domain": {"user_id": []}} + if self.team_id: + return {"domain": {"user_id": [("id", "in", self.user_ids.ids)]}} + else: + return {"domain": {"user_id": []}} + + # --------------------------------------------------- + # CRUD + # --------------------------------------------------- + + @api.model + def create(self, vals): + if vals.get("number", "/") == "/": + vals["number"] = self._prepare_ticket_number(vals) + res = super().create(vals) + + # Check if mail to the user has to be sent + if vals.get("user_id") and res: + res.send_user_mail() + return res + + def copy(self, default=None): + self.ensure_one() + if default is None: + default = {} + if "number" not in default: + default["number"] = self._prepare_ticket_number(default) + res = super().copy(default) + return res + + def write(self, vals): + for _ticket in self: + now = fields.Datetime.now() + if vals.get("stage_id"): + stage = self.env["helpdesk.ticket.stage"].browse([vals["stage_id"]]) + vals["last_stage_update"] = now + if stage.closed: + vals["closed_date"] = now + if vals.get("user_id"): + vals["assigned_date"] = now + + res = super().write(vals) + + # Check if mail to the user has to be sent + for ticket in self: + if vals.get("user_id"): + ticket.send_user_mail() + return res + + def action_duplicate_tickets(self): + for ticket in self.browse(self.env.context["active_ids"]): + ticket.copy() + + def _prepare_ticket_number(self, values): + seq = self.env["ir.sequence"] + if "company_id" in values: + seq = seq.with_context(force_company=values["company_id"]) + return seq.next_by_code("helpdesk.ticket.sequence") or "/" + + # --------------------------------------------------- + # Mail gateway + # --------------------------------------------------- + + def _track_template(self, tracking): + res = super()._track_template(tracking) + ticket = self[0] + if "stage_id" in tracking and ticket.stage_id.mail_template_id: + res["stage_id"] = ( + ticket.stage_id.mail_template_id, + { + "auto_delete_message": True, + "subtype_id": self.env["ir.model.data"].xmlid_to_res_id( + "mail.mt_note" + ), + "email_layout_xmlid": "mail.mail_notification_light", + }, + ) + return res + + @api.model + def message_new(self, msg, custom_values=None): + """Override message_new from mail gateway so we can set correct + default values. + """ + if custom_values is None: + custom_values = {} + defaults = { + "name": msg.get("subject") or _("No Subject"), + "description": msg.get("body"), + "partner_email": msg.get("from"), + "partner_id": msg.get("author_id"), + } + defaults.update(custom_values) + + # Write default values coming from msg + ticket = super().message_new(msg, custom_values=defaults) + + # Use mail gateway tools to search for partners to subscribe + email_list = tools.email_split( + (msg.get("to") or "") + "," + (msg.get("cc") or "") + ) + partner_ids = [ + p + for p in self.env["mail.thread"]._mail_find_partner_from_emails( + email_list, records=ticket, force_create=False + ) + if p + ] + ticket.message_subscribe(partner_ids) + + return ticket + + def message_update(self, msg, update_vals=None): + """ Override message_update to subscribe partners """ + email_list = tools.email_split( + (msg.get("to") or "") + "," + (msg.get("cc") or "") + ) + partner_ids = [ + p + for p in self.env["mail.thread"]._mail_find_partner_from_emails( + email_list, records=self, force_create=False + ) + if p + ] + self.message_subscribe(partner_ids) + return super().message_update(msg, update_vals=update_vals) + + def _message_get_suggested_recipients(self): + recipients = super()._message_get_suggested_recipients() + try: + for ticket in self: + if ticket.partner_id: + ticket._message_add_suggested_recipient( + recipients, partner=ticket.partner_id, reason=_("Customer") + ) + elif ticket.partner_email: + ticket._message_add_suggested_recipient( + recipients, + email=ticket.partner_email, + reason=_("Customer Email"), + ) + except AccessError: + # no read access rights -> just ignore suggested recipients because this + # imply modifying followers + pass + return recipients -- cgit v1.2.3