diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 98dbe76e0..fb164fb1c 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -3288,17 +3288,32 @@ class WorkflowSerializer(serializers.ModelSerializer[Workflow]): def validate(self, attrs): attrs = super().validate(attrs) - triggers = attrs.get("triggers") or [] - actions = attrs.get("actions") or [] + if "actions" in attrs: + has_remote_ocr_action = any( + action.get("type") == WorkflowAction.WorkflowActionType.REMOTE_OCR + for action in attrs["actions"] + ) + else: + has_remote_ocr_action = self.instance is not None and ( + self.instance.actions.filter( + type=WorkflowAction.WorkflowActionType.REMOTE_OCR, + ).exists() + ) + + if "triggers" in attrs: + has_consumption_trigger = any( + trigger.get("type") == WorkflowTrigger.WorkflowTriggerType.CONSUMPTION + for trigger in attrs["triggers"] + ) + else: + has_consumption_trigger = self.instance is not None and ( + self.instance.triggers.filter( + type=WorkflowTrigger.WorkflowTriggerType.CONSUMPTION, + ).exists() + ) # Remote OCR can only work with consumption triggers - if any( - action.get("type") == WorkflowAction.WorkflowActionType.REMOTE_OCR - for action in actions - ) and not any( - trigger.get("type") == WorkflowTrigger.WorkflowTriggerType.CONSUMPTION - for trigger in triggers - ): + if has_remote_ocr_action and not has_consumption_trigger: raise serializers.ValidationError( "Remote OCR actions require a consumption started trigger", ) diff --git a/src/documents/tests/test_api_workflows.py b/src/documents/tests/test_api_workflows.py index 1055e884e..8c328f047 100644 --- a/src/documents/tests/test_api_workflows.py +++ b/src/documents/tests/test_api_workflows.py @@ -466,6 +466,65 @@ class TestApiWorkflows(DirectoriesMixin, APITestCase): self.assertEqual(response.status_code, status.HTTP_201_CREATED) + def test_api_partial_update_adds_remote_ocr_action(self) -> None: + """ + GIVEN: + - An existing workflow with a consumption started trigger + WHEN: + - A partial update adds a remote OCR action without resubmitting triggers + THEN: + - The existing trigger is considered and the update succeeds + """ + response = self.client.patch( + f"{self.ENDPOINT}{self.workflow.id}/", + json.dumps( + { + "actions": [ + { + "type": WorkflowAction.WorkflowActionType.REMOTE_OCR, + }, + ], + }, + ), + content_type="application/json", + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual( + self.workflow.actions.get().type, + WorkflowAction.WorkflowActionType.REMOTE_OCR, + ) + + def test_api_partial_update_cannot_remove_remote_ocr_trigger(self) -> None: + """ + GIVEN: + - An existing workflow with a remote OCR action + - An existing consumption started trigger + WHEN: + - A partial update replaces the trigger without resubmitting actions + THEN: + - The existing action is considered and the update is rejected + """ + self.action.type = WorkflowAction.WorkflowActionType.REMOTE_OCR + self.action.save() + + response = self.client.patch( + f"{self.ENDPOINT}{self.workflow.id}/", + json.dumps( + { + "triggers": [ + { + "type": WorkflowTrigger.WorkflowTriggerType.DOCUMENT_UPDATED, + }, + ], + }, + ), + content_type="application/json", + ) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(self.workflow.triggers.get(), self.trigger) + def test_api_create_workflow_trigger_action_empty_fields(self) -> None: """ GIVEN: