+
+
+
+ Our Reply — from Nexa Systems Support
+
+
+
+
+
+
+
+
+
+ Summary for the Decision
+
+
+
+
+
+
+
-
- View original request & full thread
-
-
-
-
-
This Approve / Reject link is single-use and will
stop working once you've clicked it.
@@ -129,10 +146,32 @@
Request of
-
-
-
+
+
+
+
+ Original Request — from
+
+
+
+
+
+
+
+
+
Summary for the Decision
+
+
+
+
+
|
diff --git a/fusion_helpdesk_central/migrations/19.0.2.4.0/pre-migration.py b/fusion_helpdesk_central/migrations/19.0.2.4.0/pre-migration.py
new file mode 100644
index 00000000..fdbac075
--- /dev/null
+++ b/fusion_helpdesk_central/migrations/19.0.2.4.0/pre-migration.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+# Copyright 2026 Nexa Systems Inc.
+# License OPL-1
+"""Force re-import of the engagement mail templates on 19.0.2.4.0.
+
+The mail templates ship in `` so support's later
+tweaks (different reply-to, custom signature, etc.) aren't blown away
+by every routine module upgrade. The flip side: a structural change
+to the template body in our own XML is silently skipped — the next
+`-u` reads the new XML but Odoo refuses to overwrite the existing
+row because of the noupdate flag.
+
+This release introduces the 3-section email layout (Original Request /
+Our Reply / Summary) and we DELIBERATELY want it to land. So this
+pre-migration deletes the existing template records + their
+ir_model_data anchors BEFORE the upgrade's data load runs. The XML
+import then re-creates them with the new body_html.
+
+Pre-migration > post-migration here because data load happens between
+the two. If we deleted in post-migration, the data load would already
+have skipped the update — too late.
+"""
+import logging
+
+_logger = logging.getLogger(__name__)
+
+
+_TEMPLATE_XMLIDS = (
+ 'mail_template_engagement',
+ 'mail_template_engagement_bulk',
+)
+
+
+def migrate(cr, version):
+ cr.execute(
+ """
+ SELECT name, res_id FROM ir_model_data
+ WHERE module = 'fusion_helpdesk_central'
+ AND name IN %s
+ """,
+ (_TEMPLATE_XMLIDS,),
+ )
+ rows = cr.fetchall()
+ if not rows:
+ _logger.info(
+ 'fusion_helpdesk_central 19.0.2.4.0 pre-migration: no '
+ 'existing engagement templates to delete; nothing to do.'
+ )
+ return
+ template_ids = [r[1] for r in rows]
+ cr.execute(
+ "DELETE FROM mail_template WHERE id IN %s",
+ (tuple(template_ids),),
+ )
+ cr.execute(
+ """
+ DELETE FROM ir_model_data
+ WHERE module = 'fusion_helpdesk_central'
+ AND name IN %s
+ """,
+ (_TEMPLATE_XMLIDS,),
+ )
+ _logger.info(
+ 'fusion_helpdesk_central 19.0.2.4.0 pre-migration: dropped %s '
+ 'engagement mail template(s) so the upgrade re-imports them '
+ 'with the new 3-section layout: %s',
+ len(template_ids), ', '.join(r[0] for r in rows),
+ )
diff --git a/fusion_helpdesk_central/models/engagement_wizard.py b/fusion_helpdesk_central/models/engagement_wizard.py
index 1ae5e438..d4aaface 100644
--- a/fusion_helpdesk_central/models/engagement_wizard.py
+++ b/fusion_helpdesk_central/models/engagement_wizard.py
@@ -416,7 +416,10 @@ class FusionHelpdeskEngagementWizard(models.TransientModel):
if not email:
raise UserError(_('Owner contact disappeared since you opened '
'the wizard. Refresh and try again.'))
- ticket._fc_reset_engagement(email, name, self.ai_summary or '')
+ ticket._fc_reset_engagement(
+ email, name, self.ai_summary or '',
+ findings=self.findings or '',
+ )
template = self.env.ref(
'fusion_helpdesk_central.mail_template_engagement',
raise_if_not_found=False,
@@ -442,11 +445,14 @@ class FusionHelpdeskEngagementWizard(models.TransientModel):
if not email:
raise UserError(_('Owner contact disappeared since you opened '
'the wizard. Refresh and try again.'))
- summary_by_id = {line.ticket_id.id: line.ai_summary or ''
- for line in self.line_ids}
+ by_id = {
+ line.ticket_id.id: (line.ai_summary or '', line.findings or '')
+ for line in self.line_ids
+ }
for ticket in self.ticket_ids:
+ summary, findings = by_id.get(ticket.id, ('', ''))
ticket._fc_reset_engagement(
- email, name, summary_by_id.get(ticket.id, ''),
+ email, name, summary, findings=findings,
)
template = self.env.ref(
'fusion_helpdesk_central.mail_template_engagement_bulk',
diff --git a/fusion_helpdesk_central/models/helpdesk_ticket.py b/fusion_helpdesk_central/models/helpdesk_ticket.py
index b1ca7509..e1ce4c72 100644
--- a/fusion_helpdesk_central/models/helpdesk_ticket.py
+++ b/fusion_helpdesk_central/models/helpdesk_ticket.py
@@ -82,6 +82,14 @@ class HelpdeskTicket(models.Model):
help='OpenAI-generated brief shown to the owner in the approval '
'email. Editable in the wizard before sending; frozen after.',
)
+ x_fc_engagement_findings = fields.Text(
+ string='Engagement Findings', copy=False,
+ help='The support engineer\'s reply / analysis (typed in the '
+ 'wizard\'s Findings field). Sent to the owner in the '
+ 'approval email alongside the original request and the AI '
+ 'summary so they see the back-and-forth context, not just '
+ 'a paraphrase. Frozen at send-time.',
+ )
x_fc_engagement_turnaround_hours = fields.Float(
string='Owner Turnaround (h)',
compute='_compute_engagement_turnaround',
@@ -238,7 +246,8 @@ class HelpdeskTicket(models.Model):
monkeypatch it for deterministic assertions."""
return uuid.uuid4().hex
- def _fc_reset_engagement(self, owner_email, owner_name, ai_summary):
+ def _fc_reset_engagement(self, owner_email, owner_name, ai_summary,
+ findings=''):
"""Stamp a fresh pending engagement on this ticket — invalidates any
previous token + clears decided/reminded timestamps so the cron and
the reporting view see a clean slate.
@@ -248,6 +257,11 @@ class HelpdeskTicket(models.Model):
up as the snapshot. If normalisation fails, we still proceed using
the raw value — the email will probably bounce but state is
consistent and re-engaging fixes it.
+
+ `findings` is the support engineer's reply text from the wizard —
+ stored on the ticket so the mail template can show it as the
+ "My Reply" section without context-magic, and so it survives as
+ audit history once the engagement is decided.
"""
self.ensure_one()
normalised = email_normalize(owner_email or '') or (owner_email or '')
@@ -260,6 +274,7 @@ class HelpdeskTicket(models.Model):
'x_fc_engagement_reminded_at': False,
'x_fc_engagement_decided_at': False,
'x_fc_ai_summary': ai_summary or '',
+ 'x_fc_engagement_findings': findings or '',
})
def action_add_owner_as_follower(self):
|