Odoo 18 is the newest version of the versatile, open-source platform that brings together ERP, CRM, sales, accounting, inventory, and many other business applications in one seamless system. Alongside its core business tools, it also offers strong communication features, such as sending emails with attached documents like quotations, invoices, or reports. By default, however, Odoo’s email system is designed to include only one main attachment per template, which can be a limitation in situations where several related documents need to be sent at once. For example, you might want to send a quotation along with product brochures, terms and conditions, or technical details. To make this possible in Odoo 18, a small customization is often needed by enhancing the mail composer or automating the process of generating and attaching multiple files.
Sometimes, when working in Odoo, you may need to send more than one document to your customer in the same email. For example, after confirming a Sale Order, you might want to send them both the quotation and the related invoice together. In this example, we will make a button on the Sale Order form that will prepare an email with both documents already attached.
Python Code - Model
from odoo import models, _
from odoo.exceptions import ValidationError
import base64
class SaleOrder(models.Model):
_inherit = 'sale.order'
def action_send_so_invoice_email(self):
"""Generate and send an email for the Sale Order with its quotation
PDF and the first linked invoice PDF (any state) attached.
Steps:
1. Fetch the email template `multi_attachment_email_sale.email_template_so_invoice`.
2. Generate and attach the Sale Order PDF.
3. Generate and attach the first linked invoice PDF (draft or posted).
4. Open the email composer preloaded with these attachments.
"""
for order in self:
template = self.env.ref('custom_module_name.email_template_so_invoice', raise_if_not_found=False)
if not template:
raise ValidationError(_("Email template not found."))
attachments = []
# Add Sale Order PDF
so_pdf, _ = self.env['ir.actions.report']._render_qweb_pdf(
'sale.action_report_saleorder', order.id
)
so_attachment = self.env['ir.attachment'].create({
'name': f"Quotation_{order.name}.pdf",
'type': 'binary',
'datas': base64.b64encode(so_pdf),
'res_model': 'sale.order',
'res_id': order.id,
'mimetype': 'application/pdf',
})
attachments.append(so_attachment.id)
# Add related Invoice PDF (first invoice linked to SO, any state)
invoice = order.invoice_ids[:1]
if invoice:
inv_pdf, _ = self.env['ir.actions.report']._render_qweb_pdf(
'account.report_invoice_with_payments', invoice.id
)
inv_attachment = self.env['ir.attachment'].create({
'name': f"Invoice_{invoice.name}.pdf",
'type': 'binary',
'datas': base64.b64encode(inv_pdf),
'res_model': 'sale.order',
'res_id': order.id,
'mimetype': 'application/pdf',
})
attachments.append(inv_attachment.id)
# Open email composer preloaded with attachments
return {
'type': 'ir.actions.act_window',
'res_model': 'mail.compose.message',
'view_mode': 'form',
'target': 'new',
'context': {
'default_model': 'sale.order',
'default_res_ids': [order.id],
'default_use_template': bool(template),
'default_template_id': template.id if template else False,
'default_attachment_ids': [(6, 0, attachments)],
},
}
This code extends the Sale Order model by adding a new method that automates the process of preparing an email with multiple attachments. When the button is clicked, Odoo first generates the PDF for the Sale Order (quotation) itself. Then it looks for the very first invoice linked to that Sale Order, no matter what state the invoice is in, and generates a PDF for that as well. Both of these PDFs are stored as attachments in Odoo, and their IDs are collected. Finally, the method opens Odoo’s standard email composer, pre-filling it with the right recipient, the email template, and both attachments already in place. The user can edit the template content if needed and then send the email directly.
XML Code - View and Email Template
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Button on Sale Order form -->
<record id="view_order_form" model="ir.ui.view">
<field name="name">sale.order.form.so.invoice.button</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<header position="inside">
<button name="action_send_so_invoice_email"
type="object"
string="Send SO + Invoice Email"
class="btn-primary"
invisible="state != 'sale'"/>
</header>
</field>
</record>
<!-- Email template -->
<record id="email_template_so_invoice" model="mail.template">
<field name="name">Sale Order + Invoice Email</field>
<field name="model_id" ref="sale.model_sale_order"/>
<field name="subject">Documents for {{ object.name }}</field>
<field name="email_from">{{ object.user_id.email or '' }}</field>
<field name="email_to">{{ object.partner_id.email }}</field>
<field name="body_html">
<![CDATA[
<p>Dear <t t-out="object.partner_id.name"/>,</p>
<p>Please find attached your quotation and the related invoice for reference.</p>
<p>Best regards,<br/><t t-out="object.company_id.name"/></p>
]]>
</field>
</record>
</odoo>
The first part of the XML adds a new button on the Sale Order form. When clicked, it calls our Python method. The second part creates an email template with placeholders for the customer name, company name, and other details. When the button is pressed, Odoo will use this template and automatically attach both the quotation and invoice PDFs.
Result
After adding these files to a custom module and installing it, the Sale Order form will display a new button labeled "Send SO + Invoice Email". This button appears only when the Sale Order is in the Sale order state-as shown in the figure below.

Clicking it runs the Python method discussed earlier, which generates the quotation PDF and the first related invoice PDF, attaches them to an email, and opens Odoo’s email composer. The user can then review or edit the email template content before sending it, exactly as shown below.

Then clicking the Send button will send it to the corresponding partner email, and it will also appear in the log note of the Sale Order form view, as shown below.

In conclusion, sending emails with multiple attachments in Odoo 18 is a simple yet powerful way to speed up your workflow. By letting the system gather and attach the necessary documents automatically, you remove the need for repetitive manual steps and reduce the risk of missing important files. This approach keeps communication clear and complete, while still giving you the chance to adjust the email content before it goes out. Whether you’re sharing quotes, invoices, reports, or any other business documents, this method helps you deliver them quickly, accurately, and professionally.
To read more about How to Create Email Template in Odoo 18, refer to our blog How to Create Email Template in Odoo 18.