]> git.ipfire.org Git - thirdparty/paperless-ngx.git/commitdiff
Fix: restore expected pre-2.16 scheduled workflow offset behavior (#10218)
authorshamoon <4887959+shamoon@users.noreply.github.com>
Thu, 19 Jun 2025 14:47:54 +0000 (07:47 -0700)
committerGitHub <noreply@github.com>
Thu, 19 Jun 2025 14:47:54 +0000 (14:47 +0000)
docs/usage.md
src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html
src/documents/tasks.py
src/documents/tests/test_workflows.py

index 7065576027f22d502998d4dccf979acbd1e4029b..7673a77dd56f7077b451ae11467a9cbd4b144ad3 100644 (file)
@@ -408,7 +408,7 @@ Currently, there are three events that correspond to workflow trigger 'types':
    tags, doc type, or correspondent.
 4. **Scheduled**: a scheduled trigger that can be used to run workflows at a specific time. The date used can be either the document
    added, created, updated date or you can specify a (date) custom field. You can also specify a day offset from the date (positive
-   offsets will trigger before the date, negative offsets will trigger after).
+   offsets will trigger after the date, negative offsets will trigger before).
 
 The following flow diagram illustrates the three document trigger types:
 
index c9869bfcbcbd7ddc2472c0fd88b4e38c63f53781..2155979d6266d9556d0521ea0f172b0f08e82d1f 100644 (file)
             formControlName="schedule_offset_days"
             [showAdd]="false"
             [error]="error?.schedule_offset_days"
-            hint="Positive values will trigger the workflow before the date, negative values after."
+            hint="Positive values will trigger after the date, negative values before."
             i18n-hint
           ></pngx-input-number>
         </div>
index 2ab5ab1cb6b4c01aeebde05032a823da1683a0eb..202e4fc823627e36a4faea2938bc6c1010f23c40 100644 (file)
@@ -420,7 +420,7 @@ def check_scheduled_workflows():
             trigger: WorkflowTrigger
             for trigger in schedule_triggers:
                 documents = Document.objects.none()
-                offset_td = datetime.timedelta(days=-trigger.schedule_offset_days)
+                offset_td = datetime.timedelta(days=trigger.schedule_offset_days)
                 threshold = now - offset_td
                 logger.debug(
                     f"Trigger {trigger.id}: checking if (date + {offset_td}) <= now ({now})",
index b577eeeb4ee41767ef83999ed83f793fd428f143..5aada761c684330e44ec062a9ff267ca1f472732 100644 (file)
@@ -1614,13 +1614,14 @@ class TestWorkflows(
     def test_workflow_scheduled_trigger_negative_offset_customfield(self):
         """
         GIVEN:
-            - Existing workflow with SCHEDULED trigger and negative offset of -7 days (so 7 days after date)
-            - Custom field date initially set to 5 days ago → trigger time = 2 days in future
-            - Then updated to 8 days ago → trigger time = 1 day ago
+            - Workflow with offset -7 (i.e., 7 days *before* the date)
+            - doc1: value_date = 5 days ago → trigger time = 12 days ago → triggers
+            - doc2: value_date = 9 days in future → trigger time = 2 days in future → does NOT trigger
         WHEN:
-            - Scheduled workflows are checked for document with custom field date 8 days in the past
+            - Scheduled workflows are checked
         THEN:
-            - Workflow runs and document owner is updated
+            - doc1 has owner assigned
+            - doc2 remains untouched
         """
         trigger = WorkflowTrigger.objects.create(
             type=WorkflowTrigger.WorkflowTriggerType.SCHEDULED,
@@ -1640,38 +1641,49 @@ class TestWorkflows(
         w.actions.add(action)
         w.save()
 
-        doc = Document.objects.create(
-            title="sample test",
+        doc1 = Document.objects.create(
+            title="doc1",
             correspondent=self.c,
-            original_filename="sample.pdf",
+            original_filename="doc1.pdf",
+            checksum="doc1-checksum",
         )
-        cfi = CustomFieldInstance.objects.create(
-            document=doc,
+        CustomFieldInstance.objects.create(
+            document=doc1,
             field=self.cf1,
-            value_date=timezone.now() - timedelta(days=5),
+            value_date=timezone.now().date() - timedelta(days=5),
         )
 
-        tasks.check_scheduled_workflows()
+        doc2 = Document.objects.create(
+            title="doc2",
+            correspondent=self.c,
+            original_filename="doc2.pdf",
+            checksum="doc2-checksum",
+        )
+        CustomFieldInstance.objects.create(
+            document=doc2,
+            field=self.cf1,
+            value_date=timezone.now().date() + timedelta(days=9),
+        )
 
-        doc.refresh_from_db()
-        self.assertIsNone(doc.owner)  # has not triggered yet
+        tasks.check_scheduled_workflows()
 
-        cfi.value_date = timezone.now() - timedelta(days=8)
-        cfi.save()
+        doc1.refresh_from_db()
+        self.assertEqual(doc1.owner, self.user2)
 
-        tasks.check_scheduled_workflows()
-        doc.refresh_from_db()
-        self.assertEqual(doc.owner, self.user2)
+        doc2.refresh_from_db()
+        self.assertIsNone(doc2.owner)
 
     def test_workflow_scheduled_trigger_negative_offset_created(self):
         """
         GIVEN:
-            - Existing workflow with SCHEDULED trigger and negative offset of -7 days (so 7 days after date)
-            - Created date set to 8 days ago → trigger time = 1 day ago and 5 days ago
+            - Existing workflow with SCHEDULED trigger and negative offset of -7 days (so 7 days before date)
+            - doc created 8 days ago → trigger time = 15 days ago → triggers
+            - doc2 created 8 days *in the future* → trigger time = 1 day in future → does NOT trigger
         WHEN:
             - Scheduled workflows are checked for document
         THEN:
-            - Workflow runs and document owner is updated for the first document, not the second
+            - doc is matched and owner updated
+            - doc2 is untouched
         """
         trigger = WorkflowTrigger.objects.create(
             type=WorkflowTrigger.WorkflowTriggerType.SCHEDULED,
@@ -1703,7 +1715,7 @@ class TestWorkflows(
             correspondent=self.c,
             original_filename="sample2.pdf",
             checksum="2",
-            created=timezone.now().date() - timedelta(days=5),
+            created=timezone.now().date() + timedelta(days=8),
         )
 
         tasks.check_scheduled_workflows()
@@ -1712,6 +1724,40 @@ class TestWorkflows(
         doc2.refresh_from_db()
         self.assertIsNone(doc2.owner)  # has not triggered yet
 
+    def test_offset_positive_means_after(self):
+        """
+        GIVEN:
+            - Document created 30 days ago
+            - Workflow with offset +10
+        EXPECT:
+            - It triggers now, because created + 10 = 20 days ago < now
+        """
+        doc = Document.objects.create(
+            title="Test doc",
+            created=timezone.now() - timedelta(days=30),
+            correspondent=self.c,
+            original_filename="test.pdf",
+        )
+        trigger = WorkflowTrigger.objects.create(
+            type=WorkflowTrigger.WorkflowTriggerType.SCHEDULED,
+            schedule_date_field=WorkflowTrigger.ScheduleDateField.CREATED,
+            schedule_offset_days=10,
+        )
+        action = WorkflowAction.objects.create(
+            assign_title="Doc assign owner",
+            assign_owner=self.user2,
+        )
+        w = Workflow.objects.create(
+            name="Workflow 1",
+            order=0,
+        )
+        w.triggers.add(trigger)
+        w.actions.add(action)
+        w.save()
+        tasks.check_scheduled_workflows()
+        doc.refresh_from_db()
+        self.assertEqual(doc.owner, self.user2)
+
     def test_workflow_scheduled_filters_queryset(self):
         """
         GIVEN: