bemade-addons/account_email_to_pdf/tests/test_email_to_pdf.py
2025-03-27 16:34:15 -04:00

207 lines
7.6 KiB
Python

from odoo.tests.common import TransactionCase
from odoo.tools.misc import find_in_path
import logging
from datetime import datetime
_logger = logging.getLogger(__name__)
class TestHtmlToPdf(TransactionCase):
"""
Test the functionality of converting an email to a PDF when it's received for a supplier invoice.
This test simulates the full flow of an email being received through
the mail alias and verifies that an account move is created with a PDF
attachment generated from the email content.
It's important to also run the tests in account, which can be done using the test tag:
`/account:TestAccountIncomingSupplierInvoice.test_extend_with_attachments_document_formats`
This specific test ensures we are not creating more attachments than necessary.
"""
@classmethod
def setUpClass(cls):
super().setUpClass()
# Set up sender and alias for testing
cls.sender_email = "sender@example.com"
cls.alias_email = "test-invoices@example.com"
# Check if wkhtmltopdf is available
cls.wkhtmltopdf_available = bool(find_in_path("wkhtmltopdf"))
# Get the model class to access the classmethod
cls.account_move = cls.env["account.move"]
# Simple test HTML content
cls.test_html = """
<html>
<head>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
h1 { color: #333; }
</style>
</head>
<body>
<h1>Test HTML Document</h1>
<p>This is a test paragraph with <b>bold text</b> and <i>italic text</i>.</p>
<ul>
<li>List item 1</li>
<li>List item 2</li>
<li>List item 3</li>
</ul>
</body>
</html>
"""
# Set up a supplier for testing
cls.supplier = cls.env["res.partner"].create(
{
"name": "Test Supplier",
"email": cls.sender_email,
}
)
# Set up a journal for incoming invoices
cls.journal = cls.env["account.journal"].search(
[("type", "=", "purchase")], limit=1
)
# Set up the mail alias domain
cls.alias_domain = cls.env["mail.alias.domain"].create({"name": "example.com"})
# Set up a mail alias for the journal
cls.alias = cls.env["mail.alias"].create(
{
"alias_name": cls.alias_email,
"alias_model_id": cls.env["ir.model"]
.search([("model", "=", "account.move")], limit=1)
.id,
"alias_domain_id": cls.alias_domain.id,
"alias_defaults": f'{{"move_type": "in_invoice", "journal_id": {cls.journal.id}}}',
}
)
cls.journal.alias_id = cls.alias
def test_html_to_pdf_conversion(self):
"""Test the direct HTML to PDF conversion."""
if not self.wkhtmltopdf_available:
self.skipTest("wkhtmltopdf not available")
# Call the method to convert HTML to PDF
pdf_content = self.account_move._html_to_pdf(self.test_html)
# Verify the PDF was created
self.assertTrue(pdf_content, "PDF content should be generated")
# Verify it's a valid PDF
self.assertTrue(
pdf_content.startswith(b"%PDF-"), "Content should be a valid PDF"
)
self.assertTrue(len(pdf_content) > 100, "PDF should have reasonable size")
# Integration test
def test_account_email_to_pdf_full_flow(self):
"""Test that an email without attachments is correctly converted to PDF
and an account move is created.
This test simulates the full flow of an email being received through
the mail alias and verifies that an account move is created with a PDF
attachment generated from the email content.
"""
# Get the current timestamp
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
message_id = f"<test123_{timestamp}@example.com>"
subject = f"Invoice from Test Supplier {timestamp}"
date = datetime.now().strftime("%a, %d %b %Y %H:%M:%S +0000")
# HTML body of the email
html_body = "<html><body><h1>Invoice Test</h1><p>This is a test invoice from Test Supplier.</p><p>Amount: $100.00</p><p>Date: 2025-03-27</p></body></html>"
# Construct the raw email with proper headers
# Make sure the From header is correctly formatted for Odoo to parse
raw_email = f"""Return-Path: <{self.sender_email}>
X-Original-To: {self.alias_email}
Delivered-To: {self.alias_email}
Received: from mail.example.com (mail.example.com [192.168.1.1])
From: {self.sender_email}
To: {self.alias_email}
Subject: {subject}
Date: {date}
Message-ID: {message_id}
MIME-Version: 1.0
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: 7bit
{html_body}"""
# Process the email through the mail gateway
# This simulates what happens when fetchmail receives an email
invoice_count_before = self.env["account.move"].search_count(
[("move_type", "=", "in_invoice")]
)
# Process the email through the mail gateway
# We need to specify the model explicitly since we're in a test environment
# In production, the alias would determine this automatically
self.env["mail.thread"].with_context(fetchmail_server_id=1).message_process(
model=None,
message=raw_email,
save_original=True,
strip_attachments=False,
)
# Count account moves after the test
# Note: move_type is the correct field name in Odoo, even if the linter doesn't recognize it
move_count_after = self.env["account.move"].search_count(
[("move_type", "=", "in_invoice")]
)
self.assertEqual(
move_count_after,
invoice_count_before + 1,
"A new account move should be created",
)
# Find the newly created invoice
invoice = self.env["account.move"].search(
[("move_type", "=", "in_invoice")], order="id desc", limit=1
)
self.assertTrue(invoice, "An account move should be created")
messages = invoice.message_ids
self.assertEqual(len(messages), 1, "The account move should have one message")
# Debug message attachments
_logger.info("Message ID: %s", messages.id)
_logger.info("Message attachments: %s", messages.attachment_ids)
_logger.info("Message attachment count: %s", len(messages.attachment_ids))
# Debug all attachments in the system
all_attachments = self.env["ir.attachment"].search(
[("res_model", "=", "mail.message"), ("res_id", "=", messages.id)]
)
_logger.info("All attachments for this message: %s", all_attachments)
_logger.info("All attachment count: %s", len(all_attachments))
attachment = messages.attachment_ids
self.assertTrue(attachment, "The message should have an attachment")
attachment = attachment.filtered(
lambda a: a.mimetype == "application/pdf" or ".pdf" in a.name
)
self.assertTrue(attachment, "The message should have a PDF attachment")
# Verify the invoice is linked to the correct supplier
self.assertEqual(
invoice.partner_id,
self.supplier,
"The invoice should be linked to the correct supplier",
)
# Verify the invoice type
self.assertEqual(
invoice.move_type, "in_invoice", "The invoice should be an incoming invoice"
)