# -*- 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.workflow_template_id', 'team_id.workflow_template_id.field_ids') def _compute_has_template(self): """Compute if team has form fields configured in workflow template""" for ticket in self: ticket.has_template = bool( ticket.team_id and ticket.team_id.workflow_template_id and ticket.team_id.workflow_template_id.field_ids ) @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 form fields for this ticket's team""" self.ensure_one() if not self.team_id or not self.team_id.workflow_template_id: return self.env['helpdesk.workflow.template.field'] return self.team_id.workflow_template_id.field_ids.sorted('sequence') @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