fix(fusion_helpdesk_central): Generate Summary crashed wizard with self-id collision

Repro: open the engagement wizard on a ticket, write findings, click
'Generate Summary from Findings'. Notification: "Ticket N no longer
exists" and the whole dialog closes — even though the ticket clearly
exists in the DB.

Root cause was two compounding bugs:

1. action_generate_summary returned an act_window dict with
   res_id=self.id to "stay open after writing the summary field". The
   web client honoured that by opening a NEW act_window — and the new
   action's context inherited active_id=<wizard_id> (because that's
   the res_id of the action being opened). Wizard ids are not ticket
   ids, but our default_get didn't know the difference.

2. default_get read ctx.get('active_id') unconditionally, without
   first checking ctx.get('active_model') == 'helpdesk.ticket'. So
   when active_id pointed at the wizard's own id, default_get fed
   that to _default_get_single, which raised "Ticket <wizard_id> no
   longer exists" — and the user saw a confusing error about a
   ticket that obviously DID exist (just not with that id).

Two fixes:

(a) action_generate_summary + action_generate_all_summaries now
    return True. The form field write is visible to the client via
    the call response; the wizard re-renders with the new
    ai_summary populated. No spurious navigation, no context
    pollution.

(b) default_get only consults active_id / active_ids when
    active_model is helpdesk.ticket. Explicit
    default_ticket_id[s] context keys still take precedence and
    aren't gated by active_model (they're the caller's strong
    signal).

Verified live: opening the wizard with active_id=99999 and NO
active_model no longer raises 'Ticket 99999 no longer exists' —
just creates the wizard cleanly. The normal flow (default_ticket_id
+ active_model='helpdesk.ticket') still works as before.

Bumps fusion_helpdesk_central to 19.0.2.3.3.
This commit is contained in:
gsinghpal
2026-05-27 14:30:53 -04:00
parent 1f818096db
commit 0104e87750
2 changed files with 27 additions and 19 deletions

View File

@@ -3,7 +3,7 @@
# License OPL-1
{
'name': 'Fusion Helpdesk Central — Client API Keys',
'version': '19.0.2.3.2',
'version': '19.0.2.3.3',
'category': 'Productivity',
'summary': 'Admin UI on the central Odoo for issuing per-client API '
'keys used by fusion_helpdesk client deployments.',

View File

@@ -117,10 +117,23 @@ class FusionHelpdeskEngagementWizard(models.TransientModel):
def default_get(self, fields_list):
vals = super().default_get(fields_list)
ctx = self.env.context or {}
ticket_id = ctx.get('default_ticket_id') or ctx.get('active_id')
ticket_ids = ctx.get('default_ticket_ids') or ctx.get('active_ids') or []
active_model = ctx.get('active_model')
# ONLY trust active_id/active_ids when active_model says they're
# helpdesk tickets. Without this guard, opening the wizard via any
# act_window that left an unrelated active_id in context (e.g. a
# button on the wizard returning a re-open action whose res_id is
# the wizard's OWN id) silently reinterprets that id as a ticket
# — and `_default_get_single` then raises "Ticket N no longer
# exists" against the wizard's own id. The explicit
# `default_ticket_id[s]` context keys are still the strong signal.
ticket_id = ctx.get('default_ticket_id')
if not ticket_id and active_model == 'helpdesk.ticket':
ticket_id = ctx.get('active_id')
ticket_ids = ctx.get('default_ticket_ids') or []
if not ticket_ids and active_model == 'helpdesk.ticket':
ticket_ids = ctx.get('active_ids') or []
# Disambiguate single vs bulk by what the caller actually selected.
# The list-view server action passes active_ids; the form button
# passes a single active_id via a deliberate context key.
@@ -318,8 +331,12 @@ class FusionHelpdeskEngagementWizard(models.TransientModel):
# ------------------------------------------------------------------
def action_generate_summary(self):
"""Single mode: fire OpenAI with the current findings, drop the
result into ai_summary. Returns an action to keep the wizard open
(replaces the current view instead of closing it).
result into ai_summary. Returns True (no navigation) so the
wizard stays open and the form view re-renders with the new
summary populated. DO NOT return an `act_window` with
`res_id=self.id` here — that puts the WIZARD's id into the new
action's `active_id`, which then collides with default_get's
ticket lookup and raises "Ticket <wizard_id> no longer exists".
"""
self.ensure_one()
if self.mode != 'single' or not self.ticket_id:
@@ -332,18 +349,15 @@ class FusionHelpdeskEngagementWizard(models.TransientModel):
)
self.ai_summary = summary
self.ai_unavailable = not bool(summary)
return {
'type': 'ir.actions.act_window',
'res_model': 'fusion.helpdesk.engagement.wizard',
'res_id': self.id,
'view_mode': 'form',
'target': 'new',
}
return True
def action_generate_all_summaries(self):
"""Bulk mode: fire OpenAI per-ticket in parallel using each line's
own findings. Lines that already have a non-empty ai_summary get
regenerated too (the user clicked the button — they meant it).
Returns True for the same reason as action_generate_summary:
keep the dialog open; don't re-navigate.
"""
self.ensure_one()
if self.mode != 'bulk' or not self.line_ids:
@@ -359,13 +373,7 @@ class FusionHelpdeskEngagementWizard(models.TransientModel):
line.ai_summary = results.get(line.ticket_id.id, '')
any_ok = any(results.values())
self.ai_unavailable = not any_ok
return {
'type': 'ir.actions.act_window',
'res_model': 'fusion.helpdesk.engagement.wizard',
'res_id': self.id,
'view_mode': 'form',
'target': 'new',
}
return True
# ------------------------------------------------------------------
# Send: write engagement state + queue mail