summaryrefslogtreecommitdiff
path: root/helpdesk_mgmt/models
diff options
context:
space:
mode:
authorRichard Sassine <richardsassine@tutanota.com>2021-05-04 18:00:38 +0300
committerRichard Sassine <richardsassine@tutanota.com>2021-05-04 18:00:38 +0300
commitad97722b1b35aff50d0adf13a19715f7b8373c07 (patch)
treeedf36f1114b896a01602e31dafd74efe45736427 /helpdesk_mgmt/models
initial commit
Diffstat (limited to 'helpdesk_mgmt/models')
-rw-r--r--helpdesk_mgmt/models/__init__.py7
-rw-r--r--helpdesk_mgmt/models/__pycache__/__init__.cpython-36.pycbin0 -> 456 bytes
-rw-r--r--helpdesk_mgmt/models/__pycache__/helpdesk_ticket.cpython-36.pycbin0 -> 7911 bytes
-rw-r--r--helpdesk_mgmt/models/__pycache__/helpdesk_ticket_category.cpython-36.pycbin0 -> 826 bytes
-rw-r--r--helpdesk_mgmt/models/__pycache__/helpdesk_ticket_channel.cpython-36.pycbin0 -> 797 bytes
-rw-r--r--helpdesk_mgmt/models/__pycache__/helpdesk_ticket_stage.cpython-36.pycbin0 -> 1396 bytes
-rw-r--r--helpdesk_mgmt/models/__pycache__/helpdesk_ticket_tag.cpython-36.pycbin0 -> 821 bytes
-rw-r--r--helpdesk_mgmt/models/__pycache__/helpdesk_ticket_team.cpython-36.pycbin0 -> 3205 bytes
-rw-r--r--helpdesk_mgmt/models/__pycache__/res_partner.cpython-36.pycbin0 -> 1676 bytes
-rw-r--r--helpdesk_mgmt/models/helpdesk_ticket.py258
-rw-r--r--helpdesk_mgmt/models/helpdesk_ticket_category.py21
-rw-r--r--helpdesk_mgmt/models/helpdesk_ticket_channel.py15
-rw-r--r--helpdesk_mgmt/models/helpdesk_ticket_stage.py33
-rw-r--r--helpdesk_mgmt/models/helpdesk_ticket_tag.py15
-rw-r--r--helpdesk_mgmt/models/helpdesk_ticket_team.py86
-rw-r--r--helpdesk_mgmt/models/res_partner.py47
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
new file mode 100644
index 0000000..da2923d
--- /dev/null
+++ b/helpdesk_mgmt/models/__pycache__/__init__.cpython-36.pyc
Binary files differ
diff --git a/helpdesk_mgmt/models/__pycache__/helpdesk_ticket.cpython-36.pyc b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket.cpython-36.pyc
new file mode 100644
index 0000000..ca5dec9
--- /dev/null
+++ b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket.cpython-36.pyc
Binary files differ
diff --git a/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_category.cpython-36.pyc b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_category.cpython-36.pyc
new file mode 100644
index 0000000..240c80c
--- /dev/null
+++ b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_category.cpython-36.pyc
Binary files differ
diff --git a/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_channel.cpython-36.pyc b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_channel.cpython-36.pyc
new file mode 100644
index 0000000..24538ff
--- /dev/null
+++ b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_channel.cpython-36.pyc
Binary files differ
diff --git a/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_stage.cpython-36.pyc b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_stage.cpython-36.pyc
new file mode 100644
index 0000000..364ac7c
--- /dev/null
+++ b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_stage.cpython-36.pyc
Binary files differ
diff --git a/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_tag.cpython-36.pyc b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_tag.cpython-36.pyc
new file mode 100644
index 0000000..0575fa9
--- /dev/null
+++ b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_tag.cpython-36.pyc
Binary files differ
diff --git a/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_team.cpython-36.pyc b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_team.cpython-36.pyc
new file mode 100644
index 0000000..5c12bcd
--- /dev/null
+++ b/helpdesk_mgmt/models/__pycache__/helpdesk_ticket_team.cpython-36.pyc
Binary files differ
diff --git a/helpdesk_mgmt/models/__pycache__/res_partner.cpython-36.pyc b/helpdesk_mgmt/models/__pycache__/res_partner.cpython-36.pyc
new file mode 100644
index 0000000..641418c
--- /dev/null
+++ b/helpdesk_mgmt/models/__pycache__/res_partner.cpython-36.pyc
Binary files differ
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,
+ }