From cc9a5e172da0f8b8030a442a2dd53be677271cde Mon Sep 17 00:00:00 2001 From: Marc Durepos Date: Wed, 26 Mar 2025 21:24:35 -0400 Subject: [PATCH] new module account_email_to_pdf Since Odoo 18, emails coming in to an alias creating account moves (vendor bills) get rejected if they do not contain an attachment that can be read by the system. This means that sending a plain email receipt with no attachment bounces, when it would be nice to have a vendor bill with the message in the chatter as a minimum. This module checks for attachments and injects one, in the form of a simple pdf containing the email header and contents, if there was no attachment to begin with. This should enable sending of a simple email and not having it bounce due to there being no attachment. --- account_email_to_pdf/__init__.py | 1 + account_email_to_pdf/__manifest__.py | 23 ++++++ account_email_to_pdf/models/__init__.py | 1 + account_email_to_pdf/models/account_move.py | 87 +++++++++++++++++++++ 4 files changed, 112 insertions(+) create mode 100644 account_email_to_pdf/__init__.py create mode 100644 account_email_to_pdf/__manifest__.py create mode 100644 account_email_to_pdf/models/__init__.py create mode 100644 account_email_to_pdf/models/account_move.py diff --git a/account_email_to_pdf/__init__.py b/account_email_to_pdf/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/account_email_to_pdf/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/account_email_to_pdf/__manifest__.py b/account_email_to_pdf/__manifest__.py new file mode 100644 index 0000000..f5c4d75 --- /dev/null +++ b/account_email_to_pdf/__manifest__.py @@ -0,0 +1,23 @@ +{ + 'name': 'Account Email to PDF', + 'version': '18.0.1.0.0', + 'category': 'Accounting', + 'summary': 'Convert email messages to PDF attachments for vendor bills', + 'description': """ +Account Email to PDF +=================== +This module converts email messages without attachments into PDF attachments +when processing incoming emails for vendor bills. + +Instead of rejecting emails without attachments, the system will create a PDF +from the email content and attach it to the message, allowing the vendor bill +creation process to continue. + """, + 'author': 'Bemade', + 'website': 'https://bemade.org', + 'depends': ['account'], + 'data': [], + 'installable': True, + 'auto_install': False, + 'license': 'LGPL-3', +} diff --git a/account_email_to_pdf/models/__init__.py b/account_email_to_pdf/models/__init__.py new file mode 100644 index 0000000..9c0a421 --- /dev/null +++ b/account_email_to_pdf/models/__init__.py @@ -0,0 +1 @@ +from . import account_move diff --git a/account_email_to_pdf/models/account_move.py b/account_email_to_pdf/models/account_move.py new file mode 100644 index 0000000..e9eb647 --- /dev/null +++ b/account_email_to_pdf/models/account_move.py @@ -0,0 +1,87 @@ +import base64 +import logging +from datetime import datetime +from email.utils import formatdate +from html import escape + +from odoo import models, fields +from odoo.tools.pdf import html_to_pdf + +_logger = logging.getLogger(__name__) + + +class AccountMove(models.Model): + _inherit = 'account.move' + + def _check_and_decode_attachment(self, attachments): + """Override to convert email message to PDF if no attachments are present.""" + if not attachments or self.env.context.get('no_new_invoice'): + # Original code would return False here, causing email rejection + # Instead, we'll create a PDF from the email message + message_dict = self.env.context.get('message_dict', {}) + if message_dict: + try: + # Create a PDF from the email content + pdf_attachment = self._create_pdf_from_email(message_dict) + if pdf_attachment: + # Add the PDF to the attachments list + return super()._check_and_decode_attachment([pdf_attachment]) + except Exception as e: + _logger.exception("Error creating PDF from email: %s", e) + + # If we couldn't create a PDF, fall back to original behavior + return False + + # If there are attachments, proceed with the original method + return super()._check_and_decode_attachment(attachments) + + def _create_pdf_from_email(self, message_dict): + """Create a PDF attachment from an email message.""" + # Extract email details + email_from = message_dict.get('email_from', 'Unknown Sender') + email_date = message_dict.get('date', datetime.now()) + subject = message_dict.get('subject', 'No Subject') + body = message_dict.get('body', '') + + # Format the date if it's a datetime object + if isinstance(email_date, datetime): + email_date = email_date.strftime('%Y-%m-%d %H:%M:%S') + + # Create HTML content for the PDF + html_content = f""" + + + + + +
+ + + +
+
+ {body} +
+ + + """ + + # Convert HTML to PDF + pdf_content = html_to_pdf(html_content) + + # Create attachment tuple (filename, content, mime_type) + filename = f"Email_{subject.replace(' ', '_')[:30]}.pdf" + attachment = ( + filename, + base64.b64encode(pdf_content).decode('utf-8'), + 'application/pdf' + ) + + _logger.info("Created PDF attachment from email: %s", filename) + return attachment