Skip to Content

Odoo 19 Email Templates: How the 'Subject' Field Actually Works

The From field resolves through a fallback chain. The To field resolves through a single checkbox. The Subject field doesn't resolve through a decision at all. It's concatenation: a handful of pieces stitched together into one line, no candidates, no default recipient, just text plus text plus text. The one exception is worth pulling apart, because it's the only place in this whole system where the subject line itself runs real logic instead of just echoing a name off the record.

Same Example: Sales: Send Quotation

Here's the subject Odoo 19 ships on the standard "Sales: Send Quotation" template, the same one covered in the From and To posts:

{{ object.company_id.name }} {{ object.state in ('draft', 'sent') and 'Quotation' or 'Order' }} (Ref {{ object.name or 'n/a' }})

Read it in three pieces:

  1. object.company_id.name: the company's name, always, first.
  2. object.state in ('draft', 'sent') and 'Quotation' or 'Order': a live conditional. While the record is still a draft or has just been sent, the subject reads "Quotation." The moment someone confirms it into an order, the exact same template starts producing "Order" instead, with nothing edited by hand.
  3. (Ref {{ object.name or 'n/a' }}): the record's own reference number in parentheses, falling back to "n/a" on the rare record that doesn't have one yet.

Put together, a still-open quote for Acme Corp reads "Acme Corp Quotation (Ref S00042)." Confirm that same record, and the next email about it reads "Acme Corp Order (Ref S00042)," same template, same code, different word, because the state changed underneath it. Everywhere else on this list, the subject is just pulling a name or a reference off the record. Here, it's actually branching on the record's status.

Every Template's Subject Field, Broken Down

Everything below is stock Odoo 19, pulled directly off a fresh install, no 19 Prince customization. Same template order as the From and To breakdowns: customer-facing first, internal/system notifications at the bottom.

TemplateSubjectPlain English
Sales: Send Quotation{{ object.company_id.name }} {{ object.state in ('draft', 'sent') and 'Quotation' or 'Order' }} (Ref {{ object.name or 'n/a' }})Company name + "Quotation" or "Order" depending on quote status + ref.
Sales: Order Confirmation{{ object.company_id.name }} {{ (object.get_portal_last_transaction().state == 'pending') and 'Pending Order' or 'Order' }} (Ref {{ object.name or 'n/a' }})Company name + "Pending Order" or "Order" depending on payment status + order ref.
Sales: Payment Done{{ object.company_id.name }} {{ (object.get_portal_last_transaction().state == 'pending') and 'Pending Order' or 'Order' }} (Ref {{ object.name or 'n/a' }})Company name + "Pending Order" or "Order" depending on payment status + order ref.
Sales: Send Proforma{{ object.company_id.name }} {{ object.state in ('draft', 'sent') and 'Proforma' or 'Order'}} (Ref {{ object.name or 'n/a' }})Company name + "Proforma" or "Order" depending on quote status + ref.
Ecommerce: Cart RecoveryYou left items in your cart!Static text.
Invoice: Sending{{ object.company_id.name }} Invoice (Ref {{ object.name or 'n/a' }})Company name + "Invoice" + invoice number.
Credit Note: Sending{{ object.company_id.name }} Credit Note (Ref {{ object.name or 'n/a' }})Company name + "Credit Note" + the credit note's reference number.
Self-billing invoice: Sending{{ object.company_id.name }} Self-billing invoice (Ref {{ object.name or 'n/a' }})Company name + "Self-billing invoice" + ref.
Self-billing credit note: Sending{{ object.company_id.name }} Self-billing credit note (Ref {{ object.name or 'n/a' }})Company name + "Self-billing credit note" + ref.
Payment: Payment Receipt{{ object.company_id.name }} Payment Receipt (Ref {{ object.name or 'n/a' }})Company name + "Payment Receipt" + payment reference number.
Customer Statement{{ (object.company_id or object._get_followup_responsible().company_id).name }} Statement - {{ object.commercial_company_name }}Company name + "Statement" + the customer's company name.
Follow Up Report{{ (object.company_id or object._get_followup_responsible().company_id).name }} Follow Up - {{ object.commercial_company_name }}Company name + "Follow Up" + the customer's company name.
Payment Reminder{{ (object.company_id or object._get_followup_responsible().company_id).name }} Payment Reminder - {{ object.commercial_company_name }}Company name + "Payment Reminder" + customer's company name.
Second reminder followup{{ (object.company_id or object._get_followup_responsible().company_id).name }} Payment Reminder - {{ object.commercial_company_name }}Company name + "Payment Reminder" + customer's company name.
Settings: New Portal Sign UpWelcome to {{ object.company_id.name }}!Company name welcome message.
Settings: New Portal User InviteYour account at {{ object.company_id.name }}Company name + generic account notice.
New eInvoices NotificationNew Electronic Invoices ReceivedStatic text.
Journal Notification{{ object.company_id.name }} - New invoice in {{ object.journal_id.display_name or 'Invoices' }} journalCompany name + which accounting journal the invoice landed in.
Bank connection expiration reminderYour bank connection is expiring soonStatic text.
Tax Return DeadlineReminder: Upcoming {{ object.name }} Deadline - Action RequiredNames the specific tax return + urgency framing.
Tax paymentTax return payment instructionsStatic text.
Settings: New User Invite{{ object.create_uid.name }} from {{ object.company_id.name }} invites you to connect to OdooNames the specific person who sent the invite + company name.
Settings: 2Fa InvitationInvitation to activate two-factor authentication on your Odoo accountStatic text.
Settings: 2Fa New LoginYour two-factor authentication codeStatic text.
Settings: Unregistered User ReminderReminder for unregistered usersStatic text.
Mail: Install RequestModule Activation Request for "{{ object.module_id.shortdesc }}"Names the specific app/module being requested.
IAP Extract NotificationIAP Extract NotificationStatic text.

Most of these rows are static or a straight name-swap, nothing to debug. The one pattern worth carrying with you: whenever a subject line seems to change wording on its own, on the same record, with no one touching the template, go looking for a conditional like the one on the quotation template. Odoo isn't reacting to an edit. It's just running a check against the record's current state, every single time it renders.

— Darren from 19 Prince