From 4e35c1287d1be80d21ba48ab75ea20f5e3c568fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= <fpeters@entrouvert.com>
Date: Mon, 27 Jun 2022 14:38:28 +0200
Subject: [PATCH] misc: add support for actions on cards linked via items
 fields (#66648)

---
 tests/workflow/test_all.py | 90 ++++++++++++++++++++++++++++++++++++++
 wcs/formdata.py            | 17 ++++---
 2 files changed, 101 insertions(+), 6 deletions(-)

diff --git a/tests/workflow/test_all.py b/tests/workflow/test_all.py
index 07c9337e0..2266d1d56 100644
--- a/tests/workflow/test_all.py
+++ b/tests/workflow/test_all.py
@@ -6267,6 +6267,96 @@ def test_call_external_workflow_with_data_sourced_object(two_pubs, admin_user):
     perform_items([delete_action], formdata)
 
 
+def test_call_external_workflow_with_items_data_sourced_object(two_pubs, admin_user):
+    FormDef.wipe()
+    CardDef.wipe()
+
+    carddef_wf = Workflow(name='Carddef Workflow')
+    carddef_wf.add_status('status')
+    carddef_wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(carddef_wf)
+    carddef_wf.backoffice_fields_formdef.fields = [
+        StringField(id='bo0', varname='bo', type='string', label='bo variable'),
+    ]
+    update_action = carddef_wf.add_global_action('Update', 'ac1')
+    update_action.add_action('set-backoffice-fields')
+    setbo = update_action.items[0]
+    setbo.fields = [{'field_id': 'bo0', 'value': '{{ form_var_bo|default:"0"|add:1 }}'}]
+    trigger = update_action.append_trigger('webservice')
+    trigger.identifier = 'update'
+    carddef_wf.store()
+
+    carddef = CardDef()
+    carddef.name = 'Data'
+    carddef.fields = [
+        StringField(id='0', label='string', varname='card_string'),
+    ]
+    carddef.digest_templates = {'default': '{{ form_var_card_string }}'}
+    carddef.workflow = carddef_wf
+    carddef.store()
+
+    carddata = carddef.data_class()()
+    carddata.data = {'0': 'Text1'}
+    carddata.just_created()
+    carddata.store()
+
+    carddata2 = carddef.data_class()()
+    carddata2.data = {'0': 'Text2'}
+    carddata2.just_created()
+    carddata2.store()
+
+    carddata3 = carddef.data_class()()
+    carddata3.data = {'0': 'Text3'}
+    carddata3.just_created()
+    carddata3.store()
+
+    wf = Workflow(name='External actions')
+    wf.add_status('Action')
+
+    update_global_action = wf.add_global_action('Update linked object data')
+    update_action = update_global_action.add_action('external_workflow_global_action')
+    update_action.slug = 'carddef:%s' % carddef.url_name
+    update_action.trigger_id = 'action:update'
+
+    wf.store()
+
+    datasource = {'type': 'carddef:%s' % carddef.url_name}
+    formdef = FormDef()
+    formdef.name = 'External action form'
+    formdef.fields = [
+        ItemsField(id='0', label='Cards', type='items', varname='cards', data_source=datasource),
+        EmailField(id='1', label='Email', varname='email'),
+    ]
+    formdef.workflow = wf
+    formdef.store()
+
+    assert carddef.data_class().count() == 3
+
+    formdata = formdef.data_class()()
+    formdata.data = {'0': [str(carddata.id), str(carddata3.id)], '1': 'foo@example.com'}
+    formdata.data['0_display'] = formdef.fields[0].store_display_value(formdata.data, '1')
+    formdata.data['0_structured'] = formdef.fields[0].store_structured_value(formdata.data, '1')
+    formdata.store()
+    formdata.just_created()
+    formdata.perform_workflow()
+
+    perform_items([update_action], formdata)
+    assert carddef.data_class().count() == 3
+    carddata.refresh_from_storage()
+    assert carddata.data['bo0'] == '1'
+    carddata2.refresh_from_storage()
+    assert not carddata2.data.get('bo0')
+    carddata3.refresh_from_storage()
+    assert carddata3.data['bo0'] == '1'
+
+    perform_items([update_action], formdata)
+    carddata.refresh_from_storage()
+    assert carddata.data['bo0'] == '2'
+    carddata2.refresh_from_storage()
+    assert not carddata2.data.get('bo0')
+    carddata3.refresh_from_storage()
+    assert carddata3.data['bo0'] == '2'
+
+
 def test_call_external_workflow_with_parent_object(pub):
     FormDef.wipe()
     CardDef.wipe()
diff --git a/wcs/formdata.py b/wcs/formdata.py
index fcb2d36ec..4fd96ab59 100644
--- a/wcs/formdata.py
+++ b/wcs/formdata.py
@@ -1513,6 +1513,10 @@ class FormData(StorableObject):
             data_source = getattr(field, 'data_source', None)
             if not data_source:
                 continue
+            if field.type == 'items':
+                linked_ids = linked_id
+            else:
+                linked_ids = [linked_id]
             data_source_type = data_source['type']
             if data_source_type.count(':') == 2:
                 # custom view, only keep object type and object slug
@@ -1520,13 +1524,14 @@ class FormData(StorableObject):
             origin = _('Data Source')
             if field.varname:
                 origin = '%s - varname "%s"' % (origin, field.varname)
-            if object_type:
-                # looking for a data_source of a specific type (workflow action)
-                if data_source_type == object_type:
+            for linked_id in linked_ids:
+                if object_type:
+                    # looking for a data_source of a specific type (workflow action)
+                    if data_source_type == object_type:
+                        data_ids.append((data_source_type, linked_id, origin))
+                else:
+                    # looking for any data_source (inspect page)
                     data_ids.append((data_source_type, linked_id, origin))
-            else:
-                # looking for any data_source (inspect page)
-                data_ids.append((data_source_type, linked_id, origin))
 
         # search in evolution
         for part in self.iter_evolution_parts():
-- 
2.36.1

