feat(fusion_helpdesk_central): owner email shows 3 sections — Request / Reply / Summary

The owner only saw the AI summary, which was a paraphrase of the user
report — they couldn't see the actual request OR what we said back.
Restructure the engagement email into three sections so the owner can
read the conversation and not just the AI's take:

  1. Original Request (from the reporter) — ticket.description, no
     longer buried in a <details> collapsible at the bottom
  2. Our Reply — the wizard's "Your Findings" text, now persisted on
     the ticket so the email template can render it directly. This is
     the engineer's analysis / response to the request.
  3. Summary for the Decision — the AI-generated brief

Approve / Reject buttons stay below all three. Bulk email mirrors the
same per-card structure.

New ticket field x_fc_engagement_findings (Text, copy=False) stores
the findings at send-time so they survive as audit history. Wizard's
_action_send_single / _action_send_bulk pass findings into
_fc_reset_engagement; bulk uses per-line findings + per-line summary.

Mail templates are in <data noupdate="1"> so a plain -u doesn't
re-import them. Pre-migration in migrations/19.0.2.4.0/pre-migration.py
deletes the existing template records + ir_model_data so the upgrade's
data load re-creates them with the new body_html. Pre- (not post-)
because data load happens between the two phases.

Smoke-tested live on nexa: rendered template HTML contains all three
section headers at the expected positions with their expected content
markers (ORIGINAL FROM RIYA in Original Request, REPLY-FROM-GURPREET
in Our Reply, the summary text in Summary for the Decision).

Bumps fusion_helpdesk_central to 19.0.2.4.0.
This commit is contained in:
gsinghpal
2026-05-27 15:26:26 -04:00
parent 76866a7c76
commit 32c7026558
5 changed files with 154 additions and 26 deletions

View File

@@ -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',

View File

@@ -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):