| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- # -*- coding: utf-8 -*-
- # Part of Odoo. See LICENSE file for full copyright and licensing details.
- from odoo import _, api, fields, models
- class HelpdeskTicket(models.Model):
- _inherit = 'helpdesk.ticket'
- request_type_id = fields.Many2one(
- 'helpdesk.request.type',
- string=_('Request Type'),
- required=True,
- tracking=True,
- help=_("Type of ticket (e.g., Incident, Improvement)"),
- default=lambda self: self._default_request_type_id()
- )
- request_type_code = fields.Char(
- related='request_type_id.code',
- string=_('Request Type Code'),
- store=True,
- readonly=True,
- help=_("Code of the request type for conditional logic")
- )
- affected_module_id = fields.Many2one(
- 'helpdesk.affected.module',
- string=_('Affected Module'),
- required=False,
- domain=[('active', '=', True)],
- help=_("Odoo module where the issue or improvement occurs")
- )
- affected_user_email = fields.Char(
- string=_('Affected User Email'),
- tracking=True,
- help=_("Email address of the affected user (from another Odoo instance or external)")
- )
- business_impact = fields.Selection(
- [
- ('0', _('Critical')),
- ('1', _('High')),
- ('2', _('Normal')),
- ],
- string=_('Business Impact'),
- default='2',
- tracking=True,
- help=_("Urgency reported by the client")
- )
- reproduce_steps = fields.Html(
- string=_('Steps to Reproduce'),
- help=_("Detailed steps to reproduce the issue (only for Incidents)")
- )
- business_goal = fields.Html(
- string=_('Business Goal'),
- help=_("Business objective for this improvement (only for Improvements)")
- )
- client_authorization = fields.Boolean(
- string=_('Client Authorization'),
- default=False,
- help=_("Checkbox from web form indicating client authorization")
- )
- estimated_hours = fields.Float(
- string=_('Estimated Hours'),
- help=_("Hours quoted after analysis")
- )
- approval_status = fields.Selection(
- [
- ('draft', _('N/A')),
- ('waiting', _('Waiting for Approval')),
- ('approved', _('Approved')),
- ('rejected', _('Rejected')),
- ],
- string=_('Approval Status'),
- default='draft',
- tracking=True,
- help=_("Status of the approval workflow")
- )
- has_template = fields.Boolean(
- string=_('Has Template'),
- compute='_compute_has_template',
- help=_("Indicates if the team has a template assigned")
- )
- attachment_ids = fields.One2many(
- 'ir.attachment',
- 'res_id',
- string=_('Attachments'),
- domain=[('res_model', '=', 'helpdesk.ticket')],
- help=_("Files attached to this ticket")
- )
- @api.depends('team_id.template_id', 'team_id.workflow_template_id')
- def _compute_has_template(self):
- """Compute if team has a template (supports both legacy template_id and workflow_template_id)"""
- for ticket in self:
- has_legacy_template = bool(ticket.team_id and ticket.team_id.template_id)
- has_workflow_template = bool(ticket.team_id and ticket.team_id.workflow_template_id and ticket.team_id.workflow_template_id.field_ids)
- ticket.has_template = has_legacy_template or has_workflow_template
- @api.model
- def _default_request_type_id(self):
- """Default to 'Incident' type if available"""
- incident_type = self.env.ref(
- 'helpdesk_extras.type_incident',
- raise_if_not_found=False
- )
- return incident_type.id if incident_type else False
- def _get_template_fields(self):
- """Get template fields for this ticket's team (supports both legacy template_id and workflow_template_id)"""
- self.ensure_one()
- if not self.team_id:
- return self.env['helpdesk.template.field']
-
- # Priority: workflow_template_id over template_id (legacy)
- if self.team_id.workflow_template_id and self.team_id.workflow_template_id.field_ids:
- return self.team_id.workflow_template_id.field_ids.sorted('sequence')
- elif self.team_id.template_id and self.team_id.template_id.field_ids:
- return self.team_id.template_id.field_ids.sorted('sequence')
-
- return self.env['helpdesk.template.field']
- @api.onchange('request_type_id', 'business_impact')
- def _onchange_compute_priority(self):
- """
- Auto-calculate priority based on request type and business impact.
- Only applies when both fields have values.
-
- Mapping:
- - Incident + Critical = 3 (Urgent)
- - Incident + High = 2 (High)
- - Incident + Normal = 1 (Normal)
- - Improvement + Critical = 2 (High)
- - Improvement + High = 1 (Normal)
- - Improvement + Normal = 0 (Low)
- """
- priority = self._compute_priority_from_impact()
- if priority is not None:
- self.priority = priority
- def _compute_priority_from_impact(self, request_type_id=None, business_impact=None):
- """
- Helper method to compute priority from request type and business impact.
- Can be used by onchange, create, and write methods.
- Returns the priority string or None if not applicable.
- """
- # Use provided values or instance values
- if request_type_id is None:
- request_type = self.request_type_id
- else:
- request_type = self.env['helpdesk.request.type'].browse(request_type_id) if isinstance(request_type_id, int) else request_type_id
-
- if business_impact is None:
- business_impact = self.business_impact
-
- if not request_type or not business_impact:
- return None
-
- type_code = request_type.code if hasattr(request_type, 'code') else ''
- if not type_code:
- return None
-
- # Priority mapping based on business_impact
- # business_impact: '0' = Critical, '1' = High, '2' = Normal
- # priority: '0' = Low, '1' = Normal, '2' = High, '3' = Urgent
- priority_map = {
- # Incidents get +1 priority boost
- ('incident', '0'): '3', # Incident + Critical = Urgent
- ('incident', '1'): '2', # Incident + High = High
- ('incident', '2'): '1', # Incident + Normal = Normal
- # Improvements have standard mapping
- ('improvement', '0'): '2', # Improvement + Critical = High
- ('improvement', '1'): '1', # Improvement + High = Normal
- ('improvement', '2'): '0', # Improvement + Normal = Low
- }
-
- key = (type_code, business_impact)
- return priority_map.get(key)
- @api.model_create_multi
- def create(self, vals_list):
- """Override create to auto-calculate priority for website form submissions."""
- for vals in vals_list:
- # Only calculate if priority not explicitly set and we have both fields
- if 'priority' not in vals or vals.get('priority') == '0':
- request_type_id = vals.get('request_type_id')
- business_impact = vals.get('business_impact')
- if request_type_id and business_impact:
- priority = self._compute_priority_from_impact(request_type_id, business_impact)
- if priority is not None:
- vals['priority'] = priority
- return super().create(vals_list)
- def write(self, vals):
- """Override write to recalculate priority when request_type or business_impact changes."""
- result = super().write(vals)
- # If either field was updated, recalculate priority for affected records
- if 'request_type_id' in vals or 'business_impact' in vals:
- for record in self:
- priority = record._compute_priority_from_impact()
- if priority is not None and record.priority != priority:
- # Use super().write to avoid recursion
- super(HelpdeskTicket, record).write({'priority': priority})
- return result
|