The Chatter System
The chatter is Odoo's built-in communication layer. Any model that inherits mail.thread gets a message thread with comments, internal notes, status changes, and activity tracking. The message_post method is how you interact with it programmatically.
Basic message_post
# Simple comment visible to followers
record.message_post(
body='Order has been shipped.',
message_type='comment',
subtype_xmlid='mail.mt_comment',
)
# Internal note (not sent to followers by email)
record.message_post(
body='Customer called about delivery delay.',
message_type='comment',
subtype_xmlid='mail.mt_note',
)Message Types and Subtypes
| message_type | Description |
|---|---|
| comment | User comment (chatter post) |
| notification | System notification |
| Incoming email | |
| auto_comment | Automated comment (no notification) |
| subtype_xmlid | Effect |
|---|---|
| mail.mt_comment | Visible to all, sends email to followers |
| mail.mt_note | Internal note, no email notification |
| mail.mt_activities | Activity-related message |
Notifying Specific Partners
# Notify specific people (even if not followers)
record.message_post(
body='Approval required for this order.',
message_type='comment',
subtype_xmlid='mail.mt_comment',
partner_ids=[manager.partner_id.id, cfo.partner_id.id],
)HTML-Formatted Messages
body = f"""
<p>Invoice <strong>{invoice.name}</strong> has been created.</p>
<ul>
<li>Amount: {invoice.amount_total:,.2f} {invoice.currency_id.symbol}</li>
<li>Due date: {invoice.invoice_date_due}</li>
<li>Customer: {invoice.partner_id.name}</li>
</ul>
"""
record.message_post(
body=body,
message_type='comment',
subtype_xmlid='mail.mt_note',
)Posting with Attachments
import base64
# Attach a file
attachment = self.env['ir.attachment'].create({
'name': 'report.pdf',
'datas': base64.b64encode(pdf_content),
'res_model': record._name,
'res_id': record.id,
})
record.message_post(
body='Monthly report attached.',
message_type='comment',
subtype_xmlid='mail.mt_comment',
attachment_ids=[attachment.id],
)Field Tracking
Automatic change tracking logs field changes in the chatter. Declare tracked fields:
class SaleOrder(models.Model):
_inherit = 'sale.order'
state = fields.Selection(tracking=True)
user_id = fields.Many2one('res.users', tracking=True)
amount_total = fields.Monetary(tracking=True)When a tracked field changes, Odoo automatically posts a message like: "Stage changed from Draft to Confirmed. Salesperson changed from John to Jane."
Tracking with Custom Labels
priority = fields.Selection([
('0', 'Normal'),
('1', 'Important'),
('2', 'Urgent'),
], tracking=True, string='Priority')Custom Subtypes
Create custom subtypes for filtering messages:
<record id="mt_order_shipped" model="mail.message.subtype">
<field name="name">Order Shipped</field>
<field name="res_model">sale.order</field>
<field name="default">True</field>
<field name="description">Order has been shipped to customer</field>
</record>Use the custom subtype in message_post:
record.message_post(
body='Your order has been shipped!',
message_type='comment',
subtype_xmlid='my_module.mt_order_shipped',
)Followers can subscribe or unsubscribe from specific subtypes.
Managing Followers
# Subscribe partners as followers
record.message_subscribe(partner_ids=[partner1.id, partner2.id])
# Subscribe with specific subtypes
record.message_subscribe(
partner_ids=[partner.id],
subtype_ids=[subtype1.id, subtype2.id]
)
# Unsubscribe
record.message_unsubscribe(partner_ids=[partner.id])
# Get current followers
follower_partners = record.message_follower_ids.mapped('partner_id')Sending Email via message_post
# Post and send email notification
record.message_post(
body='Your order is ready for pickup.',
message_type='comment',
subtype_xmlid='mail.mt_comment',
email_from='[email protected]',
reply_to='[email protected]',
)
# Followers with email notifications enabled will receive the emailUsing Email Templates
# Send using a predefined email template
template = self.env.ref('sale.email_template_edi_sale')
record.message_post_with_source(
template,
subtype_xmlid='mail.mt_comment',
)Logging Without Notification
# Log a message without notifying anyone
record.message_post(
body='System: automatic price adjustment applied.',
message_type='notification',
subtype_xmlid='mail.mt_note',
)
# Or use _message_log for pure logging
record._message_log(body='Background process completed.')Performance Considerations
tracking_disable=Truein context disables all tracking for bulk operationsmail_create_nolog=Trueskips the creation log messagemail_notrack=Trueskips field change trackingmessage_posttriggers mail sending — useauto_delete=Trueon templates to clean up- For bulk notifications, consider
message_notifyinstead ofmessage_post(no message stored)
Common Patterns
- Post status change messages in workflow action methods
- Use internal notes for CRM activity logging
- Attach reports and documents to relevant records
- Use custom subtypes to let followers filter notification types
- Log automated operations with
_message_logfor audit trails