diff --git a/docs/usage.md b/docs/usage.md index 706557602..7673a77dd 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -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: diff --git a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html index c9869bfcb..2155979d6 100644 --- a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html +++ b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html @@ -129,7 +129,7 @@ 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 > diff --git a/src/documents/tasks.py b/src/documents/tasks.py index 2ab5ab1cb..202e4fc82 100644 --- a/src/documents/tasks.py +++ b/src/documents/tasks.py @@ -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})", diff --git a/src/documents/tests/test_workflows.py b/src/documents/tests/test_workflows.py index b577eeeb4..5aada761c 100644 --- a/src/documents/tests/test_workflows.py +++ b/src/documents/tests/test_workflows.py @@ -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), + ) + + 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), ) tasks.check_scheduled_workflows() - doc.refresh_from_db() - self.assertIsNone(doc.owner) # has not triggered yet + doc1.refresh_from_db() + self.assertEqual(doc1.owner, self.user2) - cfi.value_date = timezone.now() - timedelta(days=8) - cfi.save() - - 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: