diff --git a/src/documents/migrations/1076_workflowaction_order.py b/src/documents/migrations/1076_workflowaction_order.py new file mode 100644 index 000000000..091a17e5e --- /dev/null +++ b/src/documents/migrations/1076_workflowaction_order.py @@ -0,0 +1,29 @@ +# Generated by Django 5.2.7 on 2026-01-14 16:53 + +from django.db import migrations +from django.db import models + + +def populate_action_order(apps, schema_editor): + WorkflowAction = apps.get_model("documents", "WorkflowAction") + for index, action in enumerate(WorkflowAction.objects.all()): + action.order = index + action.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("documents", "1075_alter_paperlesstask_task_name"), + ] + + operations = [ + migrations.AddField( + model_name="workflowaction", + name="order", + field=models.PositiveIntegerField(default=0, verbose_name="order"), + ), + migrations.RunPython( + populate_action_order, + reverse_code=migrations.RunPython.noop, + ), + ] diff --git a/src/documents/models.py b/src/documents/models.py index 0bf3d48dd..372fafaf2 100644 --- a/src/documents/models.py +++ b/src/documents/models.py @@ -1295,6 +1295,8 @@ class WorkflowAction(models.Model): default=WorkflowActionType.ASSIGNMENT, ) + order = models.PositiveIntegerField(_("order"), default=0) + assign_title = models.TextField( _("assign title"), null=True, diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index b780db815..a265b036b 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -2577,7 +2577,8 @@ class WorkflowSerializer(serializers.ModelSerializer): set_triggers.append(trigger_instance) if actions is not None and actions is not serializers.empty: - for action in actions: + for index, action in enumerate(actions): + action["order"] = index assign_tags = action.pop("assign_tags", None) assign_view_users = action.pop("assign_view_users", None) assign_view_groups = action.pop("assign_view_groups", None) @@ -2704,6 +2705,16 @@ class WorkflowSerializer(serializers.ModelSerializer): return instance + def to_representation(self, instance): + data = super().to_representation(instance) + actions = instance.actions.order_by("order", "pk") + data["actions"] = WorkflowActionSerializer( + actions, + many=True, + context=self.context, + ).data + return data + class TrashSerializer(SerializerWithPerms): documents = serializers.ListField( diff --git a/src/documents/signals/handlers.py b/src/documents/signals/handlers.py index bff68780b..cfd2f185b 100644 --- a/src/documents/signals/handlers.py +++ b/src/documents/signals/handlers.py @@ -781,7 +781,7 @@ def run_workflows( if matching.document_matches_workflow(document, workflow, trigger_type): action: WorkflowAction - for action in workflow.actions.all(): + for action in workflow.actions.order_by("order", "pk"): message = f"Applying {action} from {workflow}" if not use_overrides: logger.info(message, extra={"group": logging_group}) diff --git a/src/documents/workflows/utils.py b/src/documents/workflows/utils.py index 553622252..0a644b0eb 100644 --- a/src/documents/workflows/utils.py +++ b/src/documents/workflows/utils.py @@ -20,9 +20,6 @@ def get_workflows_for_trigger( wrap it in a list; otherwise fetch enabled workflows for the trigger with the prefetches used by the runner. """ - if workflow_to_run is not None: - return [workflow_to_run] - annotated_actions = ( WorkflowAction.objects.select_related( "assign_correspondent", @@ -105,10 +102,25 @@ def get_workflows_for_trigger( ) ) + action_prefetch = Prefetch( + "actions", + queryset=annotated_actions.order_by("order", "pk"), + ) + + if workflow_to_run is not None: + return ( + Workflow.objects.filter(pk=workflow_to_run.pk) + .prefetch_related( + action_prefetch, + "triggers", + ) + .distinct() + ) + return ( Workflow.objects.filter(enabled=True, triggers__type=trigger_type) .prefetch_related( - Prefetch("actions", queryset=annotated_actions), + action_prefetch, "triggers", ) .order_by("order")