From b06b1b4cf74780259c6b7ae0a250e272ffe035f9 Mon Sep 17 00:00:00 2001
From: shamoon <4887959+shamoon@users.noreply.github.com>
Date: Mon, 24 Feb 2025 08:53:20 -0800
Subject: [PATCH] Enhancement: option to stop processing further mail rules
---
.../mail-rule-edit-dialog.component.html | 15 ++++++---
.../mail-rule-edit-dialog.component.ts | 1 +
src-ui/src/app/data/mail-rule.ts | 2 ++
.../services/rest/mail-rule.service.spec.ts | 3 ++
src/paperless_mail/mail.py | 5 +++
.../0030_mailrule_stop_processing.py | 22 +++++++++++++
src/paperless_mail/models.py | 8 +++++
src/paperless_mail/serialisers.py | 1 +
src/paperless_mail/tests/test_mail.py | 33 +++++++++++++++++++
9 files changed, 85 insertions(+), 5 deletions(-)
create mode 100644 src/paperless_mail/migrations/0030_mailrule_stop_processing.py
diff --git a/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html b/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html
index afe6c2ab9..0b06616fb 100644
--- a/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html
+++ b/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html
@@ -9,19 +9,24 @@
-
+
-
+
Paperless will only process mails that match all of the criteria specified below.
diff --git a/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts
index dd5630bab..436569a08 100644
--- a/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts
+++ b/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts
@@ -222,6 +222,7 @@ export class MailRuleEditDialogComponent extends EditDialogComponent
{
),
assign_correspondent: new FormControl(null),
assign_owner_from_rule: new FormControl(true),
+ stop_processing: new FormControl(false),
})
}
diff --git a/src-ui/src/app/data/mail-rule.ts b/src-ui/src/app/data/mail-rule.ts
index 4c47b6500..6609810dd 100644
--- a/src-ui/src/app/data/mail-rule.ts
+++ b/src-ui/src/app/data/mail-rule.ts
@@ -84,4 +84,6 @@ export interface MailRule extends ObjectWithPermissions {
assign_correspondent?: number // PaperlessCorrespondent.id
assign_owner_from_rule: boolean
+
+ stop_processing: boolean
}
diff --git a/src-ui/src/app/services/rest/mail-rule.service.spec.ts b/src-ui/src/app/services/rest/mail-rule.service.spec.ts
index f2016d797..f5bef1ec0 100644
--- a/src-ui/src/app/services/rest/mail-rule.service.spec.ts
+++ b/src-ui/src/app/services/rest/mail-rule.service.spec.ts
@@ -33,6 +33,7 @@ const mail_rules = [
action: MailAction.MarkRead,
assign_title_from: MailMetadataTitleOption.FromSubject,
assign_owner_from_rule: true,
+ stop_processing: false,
},
{
name: 'Mail Rule 2',
@@ -52,6 +53,7 @@ const mail_rules = [
action: MailAction.Delete,
assign_title_from: MailMetadataTitleOption.FromSubject,
assign_owner_from_rule: true,
+ stop_processing: false,
},
{
name: 'Mail Rule 3',
@@ -71,6 +73,7 @@ const mail_rules = [
action: MailAction.Flag,
assign_title_from: MailMetadataTitleOption.FromSubject,
assign_owner_from_rule: false,
+ stop_processing: false,
},
]
diff --git a/src/paperless_mail/mail.py b/src/paperless_mail/mail.py
index 8acd5d8ad..f6a46a292 100644
--- a/src/paperless_mail/mail.py
+++ b/src/paperless_mail/mail.py
@@ -575,6 +575,11 @@ class MailAccountHandler(LoggingMixin):
rule,
supports_gmail_labels=supports_gmail_labels,
)
+ if total_processed_files > 0 and rule.stop_processing:
+ self.log.debug(
+ f"Rule {rule}: Stopping processing rules due to stop_processing flag",
+ )
+ break
except Exception as e:
self.log.exception(
f"Rule {rule}: Error while processing rule: {e}",
diff --git a/src/paperless_mail/migrations/0030_mailrule_stop_processing.py b/src/paperless_mail/migrations/0030_mailrule_stop_processing.py
new file mode 100644
index 000000000..323634c8f
--- /dev/null
+++ b/src/paperless_mail/migrations/0030_mailrule_stop_processing.py
@@ -0,0 +1,22 @@
+# Generated by Django 5.1.6 on 2025-02-24 16:07
+
+from django.db import migrations
+from django.db import models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("paperless_mail", "0029_mailrule_pdf_layout"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="mailrule",
+ name="stop_processing",
+ field=models.BooleanField(
+ default=False,
+ help_text="If True, no further rules will be processed after this one if any document is consumed.",
+ verbose_name="Stop processing further rules",
+ ),
+ ),
+ ]
diff --git a/src/paperless_mail/models.py b/src/paperless_mail/models.py
index cf33a056b..1502f3f39 100644
--- a/src/paperless_mail/models.py
+++ b/src/paperless_mail/models.py
@@ -301,6 +301,14 @@ class MailRule(document_models.ModelWithOwner):
default=True,
)
+ stop_processing = models.BooleanField(
+ _("Stop processing further rules"),
+ default=False,
+ help_text=_(
+ "If True, no further rules will be processed after this one if any document is queued.",
+ ),
+ )
+
def __str__(self):
return f"{self.account.name}.{self.name}"
diff --git a/src/paperless_mail/serialisers.py b/src/paperless_mail/serialisers.py
index b38c8e78c..d7354e9e4 100644
--- a/src/paperless_mail/serialisers.py
+++ b/src/paperless_mail/serialisers.py
@@ -102,6 +102,7 @@ class MailRuleSerializer(OwnedObjectSerializer):
"user_can_change",
"permissions",
"set_permissions",
+ "stop_processing",
]
def update(self, instance, validated_data):
diff --git a/src/paperless_mail/tests/test_mail.py b/src/paperless_mail/tests/test_mail.py
index 51a1929ab..910270d88 100644
--- a/src/paperless_mail/tests/test_mail.py
+++ b/src/paperless_mail/tests/test_mail.py
@@ -1692,6 +1692,39 @@ class TestTasks(TestCase):
result = tasks.process_mail_accounts(account_ids=[account_b.id])
self.assertIn("No new", result)
+ @mock.patch("paperless_mail.tasks.MailAccountHandler.handle_mail_account")
+ def test_rule_with_stop_processing(self, m):
+ """
+ GIVEN:
+ - Mail account with a rule with stop_processing=True
+ WHEN:
+ - Mail account is processed
+ THEN:
+ - Should only process the first rule
+ """
+ m.side_effect = lambda account: 6
+
+ account = MailAccount.objects.create(
+ name="A",
+ imap_server="A",
+ username="A",
+ password="A",
+ )
+ MailRule.objects.create(
+ name="A",
+ account=account,
+ stop_processing=True,
+ )
+ MailRule.objects.create(
+ name="B",
+ account=account,
+ )
+
+ result = tasks.process_mail_accounts()
+
+ self.assertEqual(m.call_count, 1)
+ self.assertIn("Added 6", result)
+
class TestMailAccountTestView(APITestCase):
def setUp(self) -> None: