diff options
| author | Richard Sassine <richardsassine@tutanota.com> | 2021-05-04 18:00:38 +0300 |
|---|---|---|
| committer | Richard Sassine <richardsassine@tutanota.com> | 2021-05-04 18:00:38 +0300 |
| commit | ad97722b1b35aff50d0adf13a19715f7b8373c07 (patch) | |
| tree | edf36f1114b896a01602e31dafd74efe45736427 /helpdesk_mgmt/models | |
initial commit
Diffstat (limited to 'helpdesk_mgmt/models')
16 files changed, 482 insertions, 0 deletions
diff --git a/helpdesk_mgmt/models/__init__.py b/helpdesk_mgmt/models/__init__.py new file mode 100644 index 0000000..30482a0 --- /dev/null +++ b/helpdesk_mgmt/models/__init__.py @@ -0,0 +1,7 @@ +from . import helpdesk_ticket +from . import helpdesk_ticket_stage +from . import helpdesk_ticket_tag +from . import helpdesk_ticket_channel +from . import helpdesk_ticket_category +from . import helpdesk_ticket_team +from . import res_partner diff --git a/helpdesk_mgmt/models/__pycache__/__init__.cpython-36.pyc b/helpdesk_mgmt/models/__pycache__/__init__.cpython-36.pyc Binary files differnew file mode 100644 index 0000000..da2923d --- /dev/null +++ b/helpdesk_mgmt/models/__pycache__/__init__.cpython-36.pyc diff --git a/helpdesk_mgmt/models/__pycache__/helpdesk_ticket.cpython-36.pyc b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket.cpython-36.pyc Binary files differnew file mode 100644 index 0000000..ca5dec9 --- /dev/null +++ b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket.cpython-36.pyc diff --git a/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_category.cpython-36.pyc b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_category.cpython-36.pyc Binary files differnew file mode 100644 index 0000000..240c80c --- /dev/null +++ b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_category.cpython-36.pyc diff --git a/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_channel.cpython-36.pyc b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_channel.cpython-36.pyc Binary files differnew file mode 100644 index 0000000..24538ff --- /dev/null +++ b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_channel.cpython-36.pyc diff --git a/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_stage.cpython-36.pyc b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_stage.cpython-36.pyc Binary files differnew file mode 100644 index 0000000..364ac7c --- /dev/null +++ b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_stage.cpython-36.pyc diff --git a/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_tag.cpython-36.pyc b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_tag.cpython-36.pyc Binary files differnew file mode 100644 index 0000000..0575fa9 --- /dev/null +++ b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_tag.cpython-36.pyc diff --git a/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_team.cpython-36.pyc b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_team.cpython-36.pyc Binary files differnew file mode 100644 index 0000000..5c12bcd --- /dev/null +++ b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_team.cpython-36.pyc diff --git a/helpdesk_mgmt/models/__pycache__/res_partner.cpython-36.pyc b/helpdesk_mgmt/models/__pycache__/res_partner.cpython-36.pyc Binary files differnew file mode 100644 index 0000000..641418c --- /dev/null +++ b/helpdesk_mgmt/models/__pycache__/res_partner.cpython-36.pyc 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 diff --git a/helpdesk_mgmt/models/helpdesk_ticket_category.py b/helpdesk_mgmt/models/helpdesk_ticket_category.py new file mode 100644 index 0000000..ba8e661 --- /dev/null +++ b/helpdesk_mgmt/models/helpdesk_ticket_category.py @@ -0,0 +1,21 @@ +from odoo import fields, models + + +class HelpdeskCategory(models.Model): + + _name = "helpdesk.ticket.category" + _description = "Helpdesk Ticket Category" + + active = fields.Boolean( + string="Active", + default=True, + ) + name = fields.Char( + string="Name", + required=True, + ) + company_id = fields.Many2one( + comodel_name="res.company", + string="Company", + default=lambda self: self.env.company, + ) diff --git a/helpdesk_mgmt/models/helpdesk_ticket_channel.py b/helpdesk_mgmt/models/helpdesk_ticket_channel.py new file mode 100644 index 0000000..a9b4c9a --- /dev/null +++ b/helpdesk_mgmt/models/helpdesk_ticket_channel.py @@ -0,0 +1,15 @@ +from odoo import fields, models + + +class HelpdeskTicketChannel(models.Model): + + _name = "helpdesk.ticket.channel" + _description = "Helpdesk Ticket Channel" + + name = fields.Char(required=True) + active = fields.Boolean(default=True) + company_id = fields.Many2one( + comodel_name="res.company", + string="Company", + default=lambda self: self.env.company, + ) diff --git a/helpdesk_mgmt/models/helpdesk_ticket_stage.py b/helpdesk_mgmt/models/helpdesk_ticket_stage.py new file mode 100644 index 0000000..96255c7 --- /dev/null +++ b/helpdesk_mgmt/models/helpdesk_ticket_stage.py @@ -0,0 +1,33 @@ +from odoo import fields, models + + +class HelpdeskTicketStage(models.Model): + _name = "helpdesk.ticket.stage" + _description = "Helpdesk Ticket Stage" + _order = "sequence, id" + + name = fields.Char(string="Stage Name", required=True, translate=True) + description = fields.Html(translate=True, sanitize_style=True) + sequence = fields.Integer(default=1) + active = fields.Boolean(default=True) + unattended = fields.Boolean(string="Unattended") + closed = fields.Boolean(string="Closed") + mail_template_id = fields.Many2one( + comodel_name="mail.template", + string="Email Template", + domain=[("model", "=", "helpdesk.ticket")], + help="If set an email will be sent to the " + "customer when the ticket" + "reaches this step.", + ) + fold = fields.Boolean( + string="Folded in Kanban", + help="This stage is folded in the kanban view " + "when there are no records in that stage " + "to display.", + ) + company_id = fields.Many2one( + comodel_name="res.company", + string="Company", + default=lambda self: self.env.company, + ) diff --git a/helpdesk_mgmt/models/helpdesk_ticket_tag.py b/helpdesk_mgmt/models/helpdesk_ticket_tag.py new file mode 100644 index 0000000..c1431ac --- /dev/null +++ b/helpdesk_mgmt/models/helpdesk_ticket_tag.py @@ -0,0 +1,15 @@ +from odoo import fields, models + + +class HelpdeskTicketTag(models.Model): + _name = "helpdesk.ticket.tag" + _description = "Helpdesk Ticket Tag" + + name = fields.Char(string="Name") + color = fields.Integer(string="Color Index") + active = fields.Boolean(default=True) + company_id = fields.Many2one( + comodel_name="res.company", + string="Company", + default=lambda self: self.env.company, + ) diff --git a/helpdesk_mgmt/models/helpdesk_ticket_team.py b/helpdesk_mgmt/models/helpdesk_ticket_team.py new file mode 100644 index 0000000..f52b03b --- /dev/null +++ b/helpdesk_mgmt/models/helpdesk_ticket_team.py @@ -0,0 +1,86 @@ +from odoo import api, fields, models +from odoo.tools.safe_eval import safe_eval + + +class HelpdeskTeam(models.Model): + + _name = "helpdesk.ticket.team" + _description = "Helpdesk Ticket Team" + _inherit = ["mail.thread", "mail.alias.mixin"] + + name = fields.Char(string="Name", required=True) + user_ids = fields.Many2many(comodel_name="res.users", string="Members") + active = fields.Boolean(default=True) + category_ids = fields.Many2many( + comodel_name="helpdesk.ticket.category", string="Category" + ) + company_id = fields.Many2one( + comodel_name="res.company", + string="Company", + default=lambda self: self.env.company, + ) + user_id = fields.Many2one( + comodel_name="res.users", + string="Team Leader", + check_company=True, + ) + alias_id = fields.Many2one( + comodel_name="mail.alias", + string="Email", + ondelete="restrict", + required=True, + help="The email address associated with \ + this channel. New emails received will \ + automatically create new tickets assigned \ + to the channel.", + ) + color = fields.Integer(string="Color Index", default=0) + ticket_ids = fields.One2many( + comodel_name="helpdesk.ticket", + inverse_name="team_id", + string="Tickets", + ) + todo_ticket_ids = fields.One2many( + related="ticket_ids", + domain="[('closed', '=', False)]", + string="Todo tickets", + readonly=True, + ) + todo_ticket_count = fields.Integer( + string="Number of tickets", compute="_compute_todo_tickets" + ) + todo_ticket_count_unassigned = fields.Integer( + string="Number of tickets unassigned", compute="_compute_todo_tickets" + ) + todo_ticket_count_unattended = fields.Integer( + string="Number of tickets unattended", compute="_compute_todo_tickets" + ) + todo_ticket_count_high_priority = fields.Integer( + string="Number of tickets in high priority", compute="_compute_todo_tickets" + ) + + @api.depends("ticket_ids", "ticket_ids.stage_id") + def _compute_todo_tickets(self): + for record in self: + record.todo_ticket_ids = record.ticket_ids.filtered( + lambda ticket: not ticket.closed + ) + record.todo_ticket_count = len(record.todo_ticket_ids) + record.todo_ticket_count_unassigned = len( + record.todo_ticket_ids.filtered(lambda ticket: not ticket.user_id) + ) + record.todo_ticket_count_unattended = len( + record.todo_ticket_ids.filtered(lambda ticket: ticket.unattended) + ) + record.todo_ticket_count_high_priority = len( + record.todo_ticket_ids.filtered(lambda ticket: ticket.priority == "3") + ) + + def _alias_get_creation_values(self): + values = super()._alias_get_creation_values() + values["alias_model_id"] = self.env.ref( + "helpdesk_mgmt.model_helpdesk_ticket" + ).id + values["alias_defaults"] = defaults = safe_eval(self.alias_defaults or "{}") + defaults["team_id"] = self.id + return values diff --git a/helpdesk_mgmt/models/res_partner.py b/helpdesk_mgmt/models/res_partner.py new file mode 100644 index 0000000..51cb48f --- /dev/null +++ b/helpdesk_mgmt/models/res_partner.py @@ -0,0 +1,47 @@ +from odoo import fields, models + + +class Partner(models.Model): + _inherit = "res.partner" + + helpdesk_ticket_ids = fields.One2many( + comodel_name="helpdesk.ticket", + inverse_name="partner_id", + string="Related tickets", + ) + + helpdesk_ticket_count = fields.Integer( + compute="_compute_helpdesk_ticket_count", string="Ticket count" + ) + + helpdesk_ticket_active_count = fields.Integer( + compute="_compute_helpdesk_ticket_count", string="Ticket active count" + ) + + helpdesk_ticket_count_string = fields.Char( + compute="_compute_helpdesk_ticket_count", string="Tickets" + ) + + def _compute_helpdesk_ticket_count(self): + for record in self: + ticket_ids = self.env["helpdesk.ticket"].search( + [("partner_id", "child_of", record.id)] + ) + record.helpdesk_ticket_count = len(ticket_ids) + record.helpdesk_ticket_active_count = len( + ticket_ids.filtered(lambda ticket: not ticket.stage_id.closed) + ) + count_active = record.helpdesk_ticket_active_count + count = record.helpdesk_ticket_count + record.helpdesk_ticket_count_string = "{} / {}".format(count_active, count) + + def action_view_helpdesk_tickets(self): + return { + "name": self.name, + "view_type": "form", + "view_mode": "tree,form", + "res_model": "helpdesk.ticket", + "type": "ir.actions.act_window", + "domain": [("partner_id", "child_of", self.id)], + "context": self.env.context, + } |
