From 48d21da13b512f42c8dc0d417720d6da0bc66bb3 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 27 Oct 2025 10:37:57 -0700 Subject: [PATCH] Fix: support ConsumableDocument in email attachments (#11196) --- src/documents/mail.py | 29 ++++++----- src/documents/tests/test_workflows.py | 75 +++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 12 deletions(-) diff --git a/src/documents/mail.py b/src/documents/mail.py index 240b41e18..6af48ca9b 100644 --- a/src/documents/mail.py +++ b/src/documents/mail.py @@ -7,6 +7,8 @@ from django.conf import settings from django.core.mail import EmailMessage from filelock import FileLock +from documents.data_models import ConsumableDocument + if TYPE_CHECKING: from documents.models import Document @@ -15,7 +17,7 @@ def send_email( subject: str, body: str, to: list[str], - attachments: list[Document], + attachments: list[Document | ConsumableDocument], *, use_archive: bool, ) -> int: @@ -45,17 +47,20 @@ def send_email( # Something could be renaming the file concurrently so it can't be attached with FileLock(settings.MEDIA_LOCK): for document in attachments: - attachment_path = ( - document.archive_path - if use_archive and document.has_archive_version - else document.source_path - ) - - friendly_filename = _get_unique_filename( - document, - used_filenames, - archive=use_archive and document.has_archive_version, - ) + if isinstance(document, ConsumableDocument): + attachment_path = document.original_file + friendly_filename = document.original_file.name + else: + attachment_path = ( + document.archive_path + if use_archive and document.has_archive_version + else document.source_path + ) + friendly_filename = _get_unique_filename( + document, + used_filenames, + archive=use_archive and document.has_archive_version, + ) used_filenames.add(friendly_filename) with attachment_path.open("rb") as f: diff --git a/src/documents/tests/test_workflows.py b/src/documents/tests/test_workflows.py index a6da01578..c25565ae6 100644 --- a/src/documents/tests/test_workflows.py +++ b/src/documents/tests/test_workflows.py @@ -30,6 +30,7 @@ from pytest_django.fixtures import SettingsWrapper from documents import tasks from documents.data_models import ConsumableDocument +from documents.data_models import DocumentMetadataOverrides from documents.data_models import DocumentSource from documents.matching import document_matches_workflow from documents.matching import existing_document_matches_workflow @@ -2788,6 +2789,80 @@ class TestWorkflows( self.assertEqual(doc.tags.all().count(), 1) self.assertIn(self.t2, doc.tags.all()) + @override_settings( + PAPERLESS_EMAIL_HOST="localhost", + EMAIL_ENABLED=True, + PAPERLESS_URL="http://localhost:8000", + ) + @mock.patch("django.core.mail.message.EmailMessage.send") + def test_workflow_assignment_then_email_includes_attachment(self, mock_email_send): + """ + GIVEN: + - Workflow with assignment and email actions + - Email action configured to include the document + WHEN: + - Workflow is run on a newly created document + THEN: + - Email action sends the document as an attachment + """ + + storage_path = StoragePath.objects.create( + name="sp2", + path="workflow/{{ document.pk }}", + ) + trigger = WorkflowTrigger.objects.create( + type=WorkflowTrigger.WorkflowTriggerType.CONSUMPTION, + ) + assignment_action = WorkflowAction.objects.create( + type=WorkflowAction.WorkflowActionType.ASSIGNMENT, + assign_storage_path=storage_path, + assign_owner=self.user2, + ) + assignment_action.assign_tags.add(self.t1) + + email_action_config = WorkflowActionEmail.objects.create( + subject="Doc ready {doc_title}", + body="Document URL: {doc_url}", + to="owner@example.com", + include_document=True, + ) + email_action = WorkflowAction.objects.create( + type=WorkflowAction.WorkflowActionType.EMAIL, + email=email_action_config, + ) + + workflow = Workflow.objects.create(name="Assignment then email", order=0) + workflow.triggers.add(trigger) + workflow.actions.set([assignment_action, email_action]) + + temp_working_copy = shutil.copy( + self.SAMPLE_DIR / "simple.pdf", + self.dirs.scratch_dir / "working-copy.pdf", + ) + + Document.objects.create( + title="workflow doc", + correspondent=self.c, + checksum="wf-assignment-email", + mime_type="application/pdf", + ) + + consumable_document = ConsumableDocument( + source=DocumentSource.ConsumeFolder, + original_file=temp_working_copy, + ) + + mock_email_send.return_value = 1 + + with self.assertNoLogs("paperless.handlers", level="ERROR"): + run_workflows( + WorkflowTrigger.WorkflowTriggerType.CONSUMPTION, + consumable_document, + overrides=DocumentMetadataOverrides(), + ) + + mock_email_send.assert_called_once() + @override_settings( PAPERLESS_EMAIL_HOST="localhost", EMAIL_ENABLED=True,