Two user-reported bugs on S00056 (and visible on any SO/invoice):
Bug 1: Cancelling the Send dialog still sends the email.
action_quotation_send is a button handler that returns a
compose-dialog action synchronously. Our override was calling
_dispatch('quote_sent', ...) AFTER super(), which immediately
sends the email via template.send_mail() before the user ever
sees the dialog. Clicking Cancel at that point only dismisses
the already-sent email's compose window.
Fix: removed the _dispatch call entirely from action_quotation_send.
The Send button IS the manual-send path; Odoo's compose dialog
(pre-populated with our FP: Quotation Sent template thanks to
_find_mail_template) handles the send-or-cancel choice correctly.
If an auto-quote-sent notification is ever wanted, it should be
wired from a cron that scans SOs that just transitioned
draft -> sent, not from the button handler. Noted in a code
comment.
Bug 2: Two copies of the same PDF attached to every email.
The mail.template records now carry report_template_ids (set by
the post_init hook from the previous refactor) which Odoo uses to
auto-attach PDFs on send_mail(). But the fp.notification.template
records ALSO had attach_quotation / attach_sale_order / attach_invoice
flags set, which cause _collect_attachments() to render the same
PDF a second time and pass it in email_values.
Fix: turned those three flags off in the XML data file. Other
flags (attach_coc, attach_bol, attach_receipt, attach_thickness_report,
attach_packing_list, attach_pod) stay on — those are genuinely
different documents, not dupes.
Because the records are noupdate="1", updated the post_init_hook
to also backfill the flag changes onto existing DB rows so
production instances get cleaned up on -u, not just fresh installs.
Smoke:
attach_quotation=False attach_sale_order=False attach_invoice=False on all three records
1 report per template (no dupes)
action_quotation_send no longer contains _dispatch("quote_sent", ...)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
67 lines
2.6 KiB
Python
67 lines
2.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright 2026 Nexa Systems Inc.
|
|
# License OPL-1 (Odoo Proprietary License v1.0)
|
|
# Part of the Fusion Plating product family.
|
|
|
|
from odoo import models
|
|
|
|
|
|
class SaleOrder(models.Model):
|
|
_inherit = 'sale.order'
|
|
|
|
def _find_mail_template(self):
|
|
"""Prefer Fusion Plating-branded templates over Odoo defaults.
|
|
|
|
Called by sale.order.action_quotation_send (the "Send" button on
|
|
both quotations and confirmed orders) to resolve the template
|
|
the composer pre-selects.
|
|
|
|
Override returns:
|
|
- state in ('draft', 'sent') -> fp_mail_template_quote_sent
|
|
(attaches Quotation PDF)
|
|
- state == 'sale' or 'done' -> fp_mail_template_so_confirmed
|
|
(attaches Acknowledgement PDF)
|
|
|
|
Falls back to Odoo's default template if ours can't be resolved
|
|
(e.g. fusion_plating_reports not installed, or template record
|
|
missing).
|
|
"""
|
|
self.ensure_one()
|
|
ref = self.env.ref
|
|
if self.env.context.get('proforma'):
|
|
return super()._find_mail_template()
|
|
fp_tpl = False
|
|
if self.state in ('draft', 'sent'):
|
|
fp_tpl = ref(
|
|
'fusion_plating_notifications.fp_mail_template_quote_sent',
|
|
raise_if_not_found=False,
|
|
)
|
|
elif self.state in ('sale', 'done'):
|
|
fp_tpl = ref(
|
|
'fusion_plating_notifications.fp_mail_template_so_confirmed',
|
|
raise_if_not_found=False,
|
|
)
|
|
return fp_tpl or super()._find_mail_template()
|
|
|
|
# NOTE: we intentionally do NOT override action_quotation_send to
|
|
# fire the quote_sent trigger. action_quotation_send returns a
|
|
# compose-dialog action synchronously — if we dispatch here the
|
|
# email is already sent by the time the user sees the dialog and
|
|
# decides to Cancel.
|
|
#
|
|
# The Send button itself IS the manual-send path; Odoo's compose
|
|
# dialog (populated with our FP: Quotation Sent template via the
|
|
# _find_mail_template override) handles the send-or-cancel choice
|
|
# cleanly. If an auto-quote-sent notification is ever desired,
|
|
# wire it from a cron job that scans SOs that just transitioned
|
|
# from draft → sent, not from the button handler.
|
|
|
|
def action_confirm(self):
|
|
res = super().action_confirm()
|
|
Dispatch = self.env['fp.notification.template']
|
|
for order in self:
|
|
Dispatch._dispatch(
|
|
'so_confirmed', order, order.partner_id, sale_order=order,
|
|
)
|
|
return res
|