diff --git a/account_email_to_pdf/models/account_move.py b/account_email_to_pdf/models/account_move.py index 14bed27..df99587 100644 --- a/account_email_to_pdf/models/account_move.py +++ b/account_email_to_pdf/models/account_move.py @@ -28,8 +28,19 @@ class AccountMove(models.Model): # 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]) + _logger.info( + "Successfully created PDF attachment, proceeding with invoice creation" + ) + # We need to return the result of _extend_with_attachments directly + # as that's what the original method would return + # Convert the list to a recordset before passing to _extend_with_attachments + attachment_recordset = self.env["ir.attachment"].browse( + [pdf_attachment.id] + ) + return self._extend_with_attachments( + attachment_recordset, + new=bool(self._context.get("from_alias")), + ) except Exception as e: _logger.exception("Error creating PDF from email: %s", e) @@ -110,7 +121,14 @@ class AccountMove(models.Model): _logger.error("Failed to remove temporary files") def _create_pdf_from_email(self, message_dict): - """Create a PDF attachment from an email message.""" + """Create a PDF attachment from an email message. + + Args: + message_dict (dict): Email message dictionary + + Returns: + ir.attachment: The created attachment record or False if failed + """ # Extract email details email_from = message_dict.get("email_from", "Unknown Sender") email_date = message_dict.get("date", datetime.now()) @@ -151,13 +169,16 @@ class AccountMove(models.Model): if not pdf_content: return False - # Create attachment tuple (filename, content, mime_type) + # Create a proper ir.attachment record filename = f"Email_{subject.replace(' ', '_')[:30]}.pdf" - attachment = ( - filename, - base64.b64encode(pdf_content).decode("utf-8"), - "application/pdf", - ) + attachment_vals = { + "name": filename, + "datas": base64.b64encode(pdf_content), + "mimetype": "application/pdf", + "res_model": "mail.message", + "res_id": message_dict.get("id", 0), + } + attachment = self.env["ir.attachment"].create(attachment_vals) _logger.info("Created PDF attachment from email: %s", filename) return attachment diff --git a/account_email_to_pdf/tests/__init__.py b/account_email_to_pdf/tests/__init__.py index 00d0605..8e19221 100644 --- a/account_email_to_pdf/tests/__init__.py +++ b/account_email_to_pdf/tests/__init__.py @@ -1 +1,2 @@ from . import test_email_to_pdf +from . import test_email_integration diff --git a/account_email_to_pdf/tests/test_email_integration.py b/account_email_to_pdf/tests/test_email_integration.py new file mode 100644 index 0000000..5dbe556 --- /dev/null +++ b/account_email_to_pdf/tests/test_email_integration.py @@ -0,0 +1,88 @@ +import base64 +from odoo.tests.common import TransactionCase +from odoo.tools.misc import find_in_path +from unittest.mock import patch + + +class TestEmailProcessing(TransactionCase): + """Integration tests for email processing with PDF generation.""" + + @classmethod + def setUpClass(cls): + super().setUpClass() + # Check if wkhtmltopdf is available + cls.wkhtmltopdf_available = bool(find_in_path("wkhtmltopdf")) + + # Get the model class to access the methods + cls.account_move = cls.env["account.move"] + + def test_create_pdf_from_email(self): + """Test that an email without attachments can be converted to PDF. + + This test directly verifies that the _create_pdf_from_email method + correctly generates a PDF attachment from an email message without + attachments, allowing the invoice creation process to continue. + """ + if not self.wkhtmltopdf_available: + self.skipTest("wkhtmltopdf not available") + + # Create a sample email message dictionary (similar to what would be parsed from an email) + message_dict = { + "subject": "Test Invoice", + "from": "test@example.com", + "to": "invoices@example.com", + "body": "

Invoice Test

This is a test invoice.

", + "attachments": [], # No attachments + "message_id": "", + } + + # Call the method directly to create a PDF from the email + attachment = self.account_move._create_pdf_from_email(message_dict) + + # Verify that an attachment was created + self.assertTrue(attachment, "An attachment should have been created") + # The actual name format is 'Email_' + subject + '.pdf' with spaces replaced by underscores + self.assertEqual( + attachment.name, + "Email_Test_Invoice.pdf", + "Attachment name should match expected format", + ) + self.assertEqual( + attachment.mimetype, "application/pdf", "Attachment should be a PDF" + ) + + # Verify the content of the PDF attachment + pdf_data = base64.b64decode(attachment.datas) + self.assertTrue(pdf_data.startswith(b"%PDF-"), "Content should be a valid PDF") + self.assertTrue(len(pdf_data) > 100, "PDF should have a reasonable size") + + def test_check_and_decode_attachment_with_empty_attachments(self): + """Test that _check_and_decode_attachment doesn't reject emails with no attachments.""" + if not self.wkhtmltopdf_available: + self.skipTest("wkhtmltopdf not available") + + # Set up a context with a message_dict to simulate email processing + message_dict = { + "subject": "Test Invoice", + "from": "test@example.com", + "to": "invoices@example.com", + "body": "

Invoice Test

This is a test invoice.

", + "attachments": [], # No attachments + "message_id": "", + } + + # Call the method with an empty attachments list + # We need to pass the message_dict in the context so _create_pdf_from_email can access it + result = self.account_move.with_context( + message_dict=message_dict + )._check_and_decode_attachment([]) + + # Verify that the result is not False (which would mean email rejection) + self.assertNotEqual( + result, + False, + "Should not reject the email when no attachments are provided", + ) + + # Verify that the result contains attachment data + self.assertTrue(result, "Should return attachment data") diff --git a/account_email_to_pdf/tests/test_email_to_pdf.py b/account_email_to_pdf/tests/test_email_to_pdf.py index 954cf45..1acddea 100644 --- a/account_email_to_pdf/tests/test_email_to_pdf.py +++ b/account_email_to_pdf/tests/test_email_to_pdf.py @@ -1,4 +1,3 @@ -import base64 from odoo.tests.common import TransactionCase from odoo.tools.misc import find_in_path