Losing $127K on Document Chaos? Auto-Attach PDFs to Chatter in Odoo 18
By Braincuber Team
Published on December 22, 2025
Sales team closes $127K deal. Customer asks: "Send me signed contract." Sales rep downloads PDF, attaches to email, sends. Customer replies: "Also need quote and terms sheet." Sales rep searches computer—can't find. Asks colleague. Colleague says: "Check shared drive." Not there. Checks email history—finds quote in email from 3 weeks ago buried in 247 unread. Downloads, sends. Takes 23 minutes. Customer later disputes contract clause—needs proof of what was sent. Sales rep searches emails again—can't find original. Customer relationship damaged. Happens 12 times monthly across sales team.
Your document chaos: PDFs emailed separately (disconnected from Odoo record). Customer asks for document—sales rep can't find (not in Odoo, buried in email). Contracts sent via email (no audit trail in Odoo). When dispute arises, no proof of what document version sent. Team members can't see what documents customer received. Sales order has no attached quote/contract/invoice. Manual attachment process (download PDF, attach to email, send) = wasted time. Compliance issues (regulatory requires document trail, you have email mess). Customer onboarding docs scattered (welcome email, contracts, terms—all separate).
Cost: Document search time = 23 min × 12 monthly × 8 sales reps × $87/hr = $24,012/year. Missed follow-ups (can't find sent documents) = 7 deals delayed monthly × $3,200 avg = $22,400 lost. Compliance audit failures = $47,000 penalty (couldn't prove document delivery). Customer disputes = 4 yearly × 12 hours resolution × $127/hr = $6,096. Duplicate document requests (customer asks again, you resend) = 340 requests yearly × 8 min × $67/hr = $3,029. Team coordination overhead (asking "Did you send them X?") = 2.3 hours weekly × 52 × $87/hr = $10,400.
Odoo 18 PDF Chatter Attachments fix this: Programmatically attach PDFs to records (quote, contract, invoice auto-attached to sales order). Documents live in Chatter (centralized, audit trail). Anyone on team can see what was sent (transparency). Customer disputes? Click Chatter, see exact PDF sent with timestamp. Auto-attach on events (when order confirmed → attach quote PDF). No manual download-attach-email dance. Here's how to code automatic PDF attachments so you stop losing $113K annually to document chaos.
You're Losing Money If:
What PDF Chatter Attachments Do
Programmatically attach PDF documents to Odoo records via Chatter: Generate PDF report → Create attachment → Link to record → Post message in Chatter → Document visible to entire team with audit trail.
| Manual Email Attachments | Odoo Chatter PDF Attachments |
|---|---|
| PDF emailed (not in Odoo record) | PDF attached to record (centralized) |
| 23 min searching scattered emails | Instant access (click Chatter, see all docs) |
| No audit trail (compliance risk) | Timestamped history (audit-ready) |
| Team can't see what was sent | Entire team visibility (transparency) |
| Manual download-attach-send | Automatic on events (order confirmed → auto-attach) |
💡 Auto-Attach PDF Example:
Scenario: When sales order confirmed, auto-attach quote PDF to Chatter
- Sales rep creates quote, sends to customer
- Customer accepts, sales rep clicks "Confirm" in Odoo
- Automatic: System generates quote PDF, attaches to sales order Chatter
- 3 months later: Customer disputes pricing
- Sales rep opens sales order → Scrolls to Chatter → Sees exact quote PDF sent on confirmation date
- Shows customer: "Here's the PDF from June 15, line item #4: $127/unit"
- Dispute resolved in 2 minutes (vs 23 min searching emails)
Understanding the Process
Three steps to programmatically attach PDFs to Chatter:
- Generate PDF: Use Odoo's QWeb report rendering to create PDF from template
- Create Attachment: Store PDF in
ir.attachmentmodel, link to record - Post to Chatter: Use
message_post()to add message with attachment reference
Step 1: Generate PDF Report
Use Odoo's built-in report engine to generate PDF from QWeb template.
pdf_content, _ = self.env['ir.actions.report'].sudo()._render_qweb_pdf(
'your_module_name.report_action_name', # Report external ID
record.id # Record ID to generate report for
)
Parameters Explained:
- 'your_module_name.report_action_name': External ID of report (e.g., 'sale.action_report_saleorder' for sales order report)
- record.id: ID of record to generate report for (sales order, invoice, etc.)
- Returns: Tuple of (pdf_content as bytes, report_format)
- sudo(): Execute with superuser rights (necessary for report generation)
Step 2: Create Attachment Record
Store PDF in ir.attachment and link to specific record.
import base64
attachment = self.env['ir.attachment'].create({
'name': 'My Document.pdf', # Display name
'type': 'binary', # Binary data type
'datas': base64.b64encode(pdf_content), # Base64-encoded PDF
'res_model': record._name, # Model name (e.g., 'sale.order')
'res_id': record.id, # Record ID
'mimetype': 'application/pdf', # MIME type
})
Attachment Fields:
- name: Filename displayed in Chatter (e.g., "SaleOrder_SO123.pdf")
- type: 'binary' for file data, 'url' for external links
- datas: Base64-encoded file content (must encode PDF bytes)
- res_model: Model to attach to (e.g., 'sale.order', 'account.move')
- res_id: Specific record ID
- mimetype: 'application/pdf' for PDFs (important for proper display)
Step 3: Post Message to Chatter
Add message to Chatter with attachment reference.
record.message_post(
body='The document has been attached.', # Message text
attachment_ids=[attachment.id] # List of attachment IDs
)
message_post() Parameters:
- body: HTML message text (displayed in Chatter)
- attachment_ids: List of attachment record IDs to include
- Optional: subject, message_type ('notification', 'comment'), partner_ids, email_from
- Result: Message appears in Chatter with attached PDF, visible to all followers
Complete Example: Auto-Attach on Sales Order Confirmation
Override action_confirm() to auto-attach quote PDF when sales order confirmed.
Create Custom Module
# -*- coding: utf-8 -*-
from odoo import models
import base64
class SaleOrder(models.Model):
_inherit = 'sale.order'
def action_confirm(self):
"""
Override to attach PDF before confirming order.
"""
# Attach PDF to each order
self.action_attach_pdf()
# Call parent method to complete confirmation
return super().action_confirm()
def action_attach_pdf(self):
"""
Generate and attach Sale Order PDF to Chatter.
"""
for order in self:
# Step 1: Generate PDF report
pdf_content, _ = self.env['ir.actions.report'].sudo()._render_qweb_pdf(
'sale.action_report_saleorder', # Built-in sales order report
order.id
)
# Step 2: Create attachment
attachment = self.env['ir.attachment'].create({
'name': f'SaleOrder_{order.name}.pdf',
'type': 'binary',
'datas': base64.b64encode(pdf_content),
'res_model': order._name, # 'sale.order'
'res_id': order.id,
'mimetype': 'application/pdf',
})
# Step 3: Post to Chatter
order.message_post(
body=f'Sale Order PDF attached: {order.name}
',
attachment_ids=[attachment.id]
)
Module Structure
your_module/
├── __init__.py
├── __manifest__.py
└── models/
├── __init__.py
└── sale_order.py
__manifest__.py
{
'name': 'Sale Order PDF Attachment',
'version': '18.0.1.0.0',
'depends': ['sale'],
'data': [],
'installable': True,
'application': False,
}
How It Works
- Sales rep creates quotation, sends to customer
- Customer accepts
- Sales rep clicks Confirm button
- Automatic: System generates PDF, creates attachment, posts to Chatter
- Chatter shows: "Sale Order PDF attached: SO123" with PDF link
- Anyone can download PDF from Chatter (audit trail preserved)
Advanced Examples
1. Attach Multiple PDFs
def attach_multiple_pdfs(self):
attachment_ids = []
# Generate quote PDF
quote_pdf, _ = self.env['ir.actions.report'].sudo()._render_qweb_pdf(
'sale.action_report_saleorder', self.id
)
quote_attachment = self.env['ir.attachment'].create({
'name': f'Quote_{self.name}.pdf',
'type': 'binary',
'datas': base64.b64encode(quote_pdf),
'res_model': self._name,
'res_id': self.id,
'mimetype': 'application/pdf',
})
attachment_ids.append(quote_attachment.id)
# Generate terms PDF (custom report)
terms_pdf, _ = self.env['ir.actions.report'].sudo()._render_qweb_pdf(
'your_module.report_terms_conditions', self.id
)
terms_attachment = self.env['ir.attachment'].create({
'name': 'Terms_and_Conditions.pdf',
'type': 'binary',
'datas': base64.b64encode(terms_pdf),
'res_model': self._name,
'res_id': self.id,
'mimetype': 'application/pdf',
})
attachment_ids.append(terms_attachment.id)
# Post both to Chatter
self.message_post(
body='Quote and Terms attached.
',
attachment_ids=attachment_ids
)
2. Conditional Attachment
def action_attach_pdf(self):
for order in self:
# Only attach for high-value orders
if order.amount_total >= 10000:
pdf_content, _ = self.env['ir.actions.report'].sudo()._render_qweb_pdf(
'sale.action_report_saleorder', order.id
)
attachment = self.env['ir.attachment'].create({
'name': f'HighValue_SaleOrder_{order.name}.pdf',
'type': 'binary',
'datas': base64.b64encode(pdf_content),
'res_model': order._name,
'res_id': order.id,
'mimetype': 'application/pdf',
})
order.message_post(
body=f'High-Value Order Alert: ${order.amount_total:,.2f}
',
attachment_ids=[attachment.id]
)
3. Notify Specific Users
def action_attach_pdf(self):
# Get sales manager
sales_manager = self.env.ref('sales_team.group_sale_manager').users
for order in self:
pdf_content, _ = self.env['ir.actions.report'].sudo()._render_qweb_pdf(
'sale.action_report_saleorder', order.id
)
attachment = self.env['ir.attachment'].create({
'name': f'SaleOrder_{order.name}.pdf',
'type': 'binary',
'datas': base64.b64encode(pdf_content),
'res_model': order._name,
'res_id': order.id,
'mimetype': 'application/pdf',
})
# Post with notification to manager
order.message_post(
body=f'Order confirmed: {order.name} - ${order.amount_total:,.2f}
',
attachment_ids=[attachment.id],
partner_ids=sales_manager.partner_id.ids, # Notify these partners
message_type='notification' # Send as notification
)
Real-World Use Cases
- Sales Orders: Auto-attach quote PDF on confirmation (customer reference)
- Invoices: Attach invoice PDF + payment terms when invoice posted
- Purchase Orders: Attach PO PDF when sent to supplier (proof of order)
- Contracts: Attach signed contract PDF when contract activated
- Manufacturing Orders: Attach work instructions PDF when MO starts
- Quality Checks: Attach inspection report PDF when check completed
- Projects: Attach milestone report PDF at each milestone
- HR: Attach offer letter PDF when offer made
Real-World Impact
Manufacturing Company (8 Sales Reps) Example:
Before PDF Chatter Attachments:
- PDFs emailed separately (disconnected from Odoo)
- Customer requests document: 23 minutes searching emails
- 12 requests monthly × 8 reps × 23 min = 36.8 hours wasted
- Annual search time: 36.8 × 12 × $87/hr = $38,419
- Deals delayed finding documents: 7 monthly × $3,200 = $22,400 lost
- Compliance audit: No document trail = $47,000 penalty
- Customer disputes: No proof of sent docs = 4 yearly × 12 hrs × $127/hr = $6,096
- Duplicate requests: 340 yearly × 8 min × $67/hr = $3,029
- Team coordination: "Did you send them X?" = 2.3 hrs weekly × 52 × $87/hr = $10,400
After Implementing PDF Chatter Attachments:
- Auto-attach on order confirmation (0 manual work)
- Document requests: Open order → Click Chatter → Download (45 seconds)
- Search time eliminated: 36.8 hrs → 0.5 hrs monthly
- Deals proceed immediately (no document delays)
- Compliance audit: Perfect document trail (passed audit)
- Customer disputes: "Here's the PDF from June 15" (resolved in 2 min)
- No duplicate requests (customer can access Chatter if needed)
- Team coordination: Everyone sees attached docs (0 questions)
Financial Impact:
- Search time saved: $38,419/year
- Deals no longer delayed: $22,400 recovered
- Compliance penalty avoided: $47,000
- Dispute resolution: $6,096 saved
- Duplicate request elimination: $3,029
- Coordination overhead: $10,400 saved
- Total Year 1 impact: $127,344
- Implementation: 2 hours development, $0 cost
Best Practices
- Use Descriptive Filenames
- Bad: "Document.pdf"
- Good: "SaleOrder_SO123_2024-12-22.pdf"
- Include order number, date for easy identification
- Add Meaningful Chatter Messages
- Don't: "PDF attached."
- Do: "Quote confirmed. PDF attached for customer reference: SO123 - $47,200"
- Context helps team understand why attachment added
- Use sudo() for Report Generation
- Report generation requires elevated permissions
- Always use
.sudo()when calling_render_qweb_pdf() - Otherwise: Access rights error
- Check User Permissions
- Ensure user has rights to post messages
- Ensure user has rights to create attachments
- Test with non-admin users
- Clean Up Old Attachments (Optional)
- Attachments stored in database/filestore
- Large PDFs accumulate over time
- Consider archival policy for old attachments
Pro Tip: Manufacturing company had compliance audit. Auditor asks: "Prove you sent terms & conditions with every quote in 2024." Before PDF Chatter: Panic. Search 847 emails. Find 83% of quotes (164 missing docs). Audit failed. After implementing auto-attach: Open Orders → Filter 2024 → Click any order → Chatter shows attached Terms PDF with timestamp. Showed auditor 10 random orders in 3 minutes. All had Terms PDF. Auditor: "Perfect documentation." Passed audit. Avoided $47K penalty. Implementation time: 2 hours coding. ROI: Infinite.
FAQs
with open('/path/to/file.pdf', 'rb') as f: pdf_content = f.read(), then 'datas': base64.b64encode(pdf_content). Useful for contracts signed externally, scanned documents, or third-party PDFs.
try: pdf, _ = self.env['ir.actions.report'].sudo()._render_qweb_pdf(...) except Exception as e: _logger.error(f'PDF generation failed: {e}') return. Prevents order confirmation from failing if PDF attachment fails. Log error for debugging.
mail.thread (has Chatter). Examples: Invoices (account.move), Purchase Orders (purchase.order), Projects (project.project), HR Applications (hr.applicant), Manufacturing Orders (mrp.production). Change _inherit and report ID accordingly.
Losing $127K Annually to Document Chaos?
We implement Odoo 18 PDF Chatter automation: Generate reports, create attachments, post to Chatter programmatically. Turn 23-minute document hunts into instant Chatter access with full audit trail.
