helpdesk_ticket.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. # -*- coding: utf-8 -*-
  2. # Part of Odoo. See LICENSE file for full copyright and licensing details.
  3. from odoo import api, fields, models
  4. class HelpdeskTicket(models.Model):
  5. _inherit = 'helpdesk.ticket'
  6. request_type_id = fields.Many2one(
  7. 'helpdesk.request.type',
  8. string='Request Type',
  9. required=True,
  10. tracking=True,
  11. help="Type of ticket (e.g., Incident, Improvement)",
  12. default=lambda self: self._default_request_type_id()
  13. )
  14. request_type_code = fields.Char(
  15. related='request_type_id.code',
  16. string='Request Type Code',
  17. store=True,
  18. readonly=True,
  19. help="Code of the request type for conditional logic"
  20. )
  21. affected_module_id = fields.Many2one(
  22. 'ir.module.module',
  23. string='Affected Module',
  24. required=False, # Not required at DB level to allow null if module is uninstalled
  25. domain=[('state', '=', 'installed'), ('application', '=', True)],
  26. ondelete='set null',
  27. help="Odoo module where the issue or improvement occurs"
  28. )
  29. business_impact = fields.Selection(
  30. [
  31. ('0', 'Critical'),
  32. ('1', 'High'),
  33. ('2', 'Normal'),
  34. ],
  35. string='Business Impact',
  36. default='2',
  37. tracking=True,
  38. help="Urgency reported by the client"
  39. )
  40. reproduce_steps = fields.Html(
  41. string='Steps to Reproduce',
  42. help="Detailed steps to reproduce the issue (only for Incidents)"
  43. )
  44. business_goal = fields.Html(
  45. string='Business Goal',
  46. help="Business objective for this improvement (only for Improvements)"
  47. )
  48. client_authorization = fields.Boolean(
  49. string='Client Authorization',
  50. default=False,
  51. help="Checkbox from web form indicating client authorization"
  52. )
  53. estimated_hours = fields.Float(
  54. string='Estimated Hours',
  55. help="Hours quoted after analysis"
  56. )
  57. approval_status = fields.Selection(
  58. [
  59. ('draft', 'N/A'),
  60. ('waiting', 'Waiting for Approval'),
  61. ('approved', 'Approved'),
  62. ('rejected', 'Rejected'),
  63. ],
  64. string='Approval Status',
  65. default='draft',
  66. tracking=True,
  67. help="Status of the approval workflow"
  68. )
  69. has_template = fields.Boolean(
  70. string='Has Template',
  71. compute='_compute_has_template',
  72. help="Indicates if the team has a template assigned"
  73. )
  74. attachment_ids = fields.One2many(
  75. 'ir.attachment',
  76. 'res_id',
  77. string='Attachments',
  78. domain=[('res_model', '=', 'helpdesk.ticket')],
  79. help="Files attached to this ticket"
  80. )
  81. @api.depends('team_id.template_id')
  82. def _compute_has_template(self):
  83. """Compute if team has a template"""
  84. for ticket in self:
  85. ticket.has_template = bool(ticket.team_id and ticket.team_id.template_id)
  86. @api.model
  87. def _default_request_type_id(self):
  88. """Default to 'Incident' type if available"""
  89. incident_type = self.env.ref(
  90. 'helpdesk_extras.type_incident',
  91. raise_if_not_found=False
  92. )
  93. return incident_type.id if incident_type else False
  94. def _get_template_fields(self):
  95. """Get template fields for this ticket's team"""
  96. self.ensure_one()
  97. if not self.team_id or not self.team_id.template_id:
  98. return self.env['helpdesk.template.field']
  99. return self.team_id.template_id.field_ids.sorted('sequence')
  100. @api.onchange('request_type_id', 'business_impact')
  101. def _onchange_compute_priority(self):
  102. """
  103. Auto-calculate priority based on request type and business impact.
  104. Only applies when both fields have values.
  105. Mapping:
  106. - Incident + Critical = 3 (Urgent)
  107. - Incident + High = 2 (High)
  108. - Incident + Normal = 1 (Normal)
  109. - Improvement + Critical = 2 (High)
  110. - Improvement + High = 1 (Normal)
  111. - Improvement + Normal = 0 (Low)
  112. """
  113. if not self.request_type_id or not self.business_impact:
  114. return
  115. # Determine if it's an incident (code 'INC') or improvement
  116. is_incident = self.request_type_id.code == 'INC'
  117. # Priority mapping based on business_impact
  118. # business_impact: '0' = Critical, '1' = High, '2' = Normal
  119. # priority: '0' = Low, '1' = Normal, '2' = High, '3' = Urgent
  120. priority_map = {
  121. # Incidents get +1 priority boost
  122. ('INC', '0'): '3', # Incident + Critical = Urgent
  123. ('INC', '1'): '2', # Incident + High = High
  124. ('INC', '2'): '1', # Incident + Normal = Normal
  125. # Improvements have standard mapping
  126. ('IMP', '0'): '2', # Improvement + Critical = High
  127. ('IMP', '1'): '1', # Improvement + High = Normal
  128. ('IMP', '2'): '0', # Improvement + Normal = Low
  129. }
  130. type_code = self.request_type_id.code or ''
  131. key = (type_code, self.business_impact)
  132. if key in priority_map:
  133. self.priority = priority_map[key]