From 0104e877501b73192be1ee2b45f864994c9d308b Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Wed, 27 May 2026 14:30:53 -0400 Subject: [PATCH] fix(fusion_helpdesk_central): Generate Summary crashed wizard with self-id collision MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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= (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 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. --- fusion_helpdesk_central/__manifest__.py | 2 +- .../models/engagement_wizard.py | 44 +++++++++++-------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/fusion_helpdesk_central/__manifest__.py b/fusion_helpdesk_central/__manifest__.py index 5ac063e2..f116394a 100644 --- a/fusion_helpdesk_central/__manifest__.py +++ b/fusion_helpdesk_central/__manifest__.py @@ -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.', diff --git a/fusion_helpdesk_central/models/engagement_wizard.py b/fusion_helpdesk_central/models/engagement_wizard.py index d6b03d4a..c4e3f6af 100644 --- a/fusion_helpdesk_central/models/engagement_wizard.py +++ b/fusion_helpdesk_central/models/engagement_wizard.py @@ -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 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