diff --git a/spiffworkflow-backend/migrations/versions/49aae41d7992_.py b/spiffworkflow-backend/migrations/versions/2ec4222f0012_.py similarity index 98% rename from spiffworkflow-backend/migrations/versions/49aae41d7992_.py rename to spiffworkflow-backend/migrations/versions/2ec4222f0012_.py index 52b352a2..0cd0195a 100644 --- a/spiffworkflow-backend/migrations/versions/49aae41d7992_.py +++ b/spiffworkflow-backend/migrations/versions/2ec4222f0012_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: 49aae41d7992 +Revision ID: 2ec4222f0012 Revises: -Create Date: 2023-01-23 14:23:17.989042 +Create Date: 2023-01-24 10:31:26.693063 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '49aae41d7992' +revision = '2ec4222f0012' down_revision = None branch_labels = None depends_on = None @@ -206,8 +206,7 @@ def upgrade(): sa.ForeignKeyConstraint(['completed_by_user_id'], ['user.id'], ), sa.ForeignKeyConstraint(['lane_assignment_id'], ['group.id'], ), sa.ForeignKeyConstraint(['process_instance_id'], ['process_instance.id'], ), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('task_id', 'process_instance_id', name='human_task_unique') + sa.PrimaryKeyConstraint('id') ) op.create_index(op.f('ix_human_task_completed'), 'human_task', ['completed'], unique=False) op.create_table('message_correlation', diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/human_task.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/human_task.py index 7e5117a0..3317f773 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/human_task.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/human_task.py @@ -26,9 +26,6 @@ class HumanTaskModel(SpiffworkflowBaseDBModel): """HumanTaskModel.""" __tablename__ = "human_task" - __table_args__ = ( - db.UniqueConstraint("task_id", "process_instance_id", name="human_task_unique"), - ) id: int = db.Column(db.Integer, primary_key=True) process_instance_id: int = db.Column( diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index ec9fa5b1..a46d964b 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -790,7 +790,7 @@ class ProcessInstanceProcessor: db.session.commit() human_tasks = HumanTaskModel.query.filter_by( - process_instance_id=self.process_instance_model.id + process_instance_id=self.process_instance_model.id, completed=False ).all() ready_or_waiting_tasks = self.get_all_ready_or_waiting_tasks() process_model_display_name = "" diff --git a/spiffworkflow-backend/tests/data/loopback_to_manual_task/loopback.bpmn b/spiffworkflow-backend/tests/data/loopback_to_manual_task/loopback.bpmn new file mode 100644 index 00000000..5ee87215 --- /dev/null +++ b/spiffworkflow-backend/tests/data/loopback_to_manual_task/loopback.bpmn @@ -0,0 +1,65 @@ + + + + + Flow_1w7l0lj + + + + Flow_1ouak9p + flow_default + flow_x_equals_one + + + + flow_default + + + + + HEY + x = 1 + + Flow_1w7l0lj + flow_x_equals_one + Flow_1ouak9p + + + x == 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_all_permissions.py b/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_all_permissions.py index 667a6f73..b31c7228 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_all_permissions.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_all_permissions.py @@ -1,4 +1,6 @@ """Test_get_localtime.""" +from operator import itemgetter + from flask.app import Flask from flask.testing import FlaskClient from tests.spiffworkflow_backend.helpers.base_test import BaseTest @@ -57,6 +59,8 @@ class TestGetAllPermissions(BaseTest): ] permissions = GetAllPermissions().run(script_attributes_context) - sorted_permissions = sorted(permissions, key=lambda x: x['uri'] or '') - sorted_expected_permissions = sorted(expected_permissions, key=lambda x: x['uri'] or '') + sorted_permissions = sorted(permissions, key=itemgetter("uri")) + sorted_expected_permissions = sorted( + expected_permissions, key=itemgetter("uri") + ) assert sorted_permissions == sorted_expected_permissions diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py index 690d6ca5..b46ba1e1 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py @@ -177,7 +177,6 @@ class TestProcessInstanceProcessor(BaseTest): ) processor = ProcessInstanceProcessor(process_instance) processor.do_engine_steps(save=True) - processor.save() assert len(process_instance.active_human_tasks) == 1 human_task = process_instance.active_human_tasks[0] @@ -340,3 +339,41 @@ class TestProcessInstanceProcessor(BaseTest): ).first() assert process_instance.locked_by is None assert process_instance.locked_at_in_seconds is None + + def test_it_can_loopback_to_previous_bpmn_task_with_gateway( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + ) -> None: + initiator_user = self.find_or_create_user("initiator_user") + process_model = load_test_spec( + process_model_id="test_group/loopback_to_manual_task", + bpmn_file_name="loopback.bpmn", + process_model_source_directory="loopback_to_manual_task", + ) + process_instance = self.create_process_instance_from_process_model( + process_model=process_model, user=initiator_user + ) + processor = ProcessInstanceProcessor(process_instance) + processor.do_engine_steps(save=True) + + assert len(process_instance.active_human_tasks) == 1 + assert len(process_instance.human_tasks) == 1 + human_task_one = process_instance.active_human_tasks[0] + + spiff_task = processor.__class__.get_task_by_bpmn_identifier( + human_task_one.task_name, processor.bpmn_process_instance + ) + ProcessInstanceService.complete_form_task( + processor, spiff_task, {}, initiator_user, human_task_one + ) + + assert len(process_instance.active_human_tasks) == 1 + assert len(process_instance.human_tasks) == 2 + human_task_two = process_instance.active_human_tasks[0] + + # this is just asserting the way the functionality currently works in spiff. + # we would actually expect this to change one day if we stop reusing the same guid + # when we re-do a task. + assert human_task_two.task_id == human_task_one.task_id