From 057a9f3dac84aff00b800d3837f081f7c7d1c016 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Thu, 9 Sep 2021 11:00:33 +0200
Subject: [PATCH 4/4] wf: LazyFormDataLinksItems object for links substitution
 (#56611)

---
 tests/workflow/test_carddata.py | 96 +++++++++++++++++++++++++++++++++
 wcs/wf/create_formdata.py       | 28 +++++++++-
 2 files changed, 123 insertions(+), 1 deletion(-)

diff --git a/tests/workflow/test_carddata.py b/tests/workflow/test_carddata.py
index 692289e8..d9ac92fd 100644
--- a/tests/workflow/test_carddata.py
+++ b/tests/workflow/test_carddata.py
@@ -150,6 +150,102 @@ def test_create_carddata(two_pubs):
     assert carddef.data_class().count() == 0
 
 
+def test_create_carddata_with_links(pub):
+    CardDef.wipe()
+    FormDef.wipe()
+
+    carddef1 = CardDef()
+    carddef1.name = 'Card 1'
+    carddef1.fields = [
+        StringField(id='1', label='string', varname='foo'),
+    ]
+    carddef1.store()
+    carddef1.data_class().wipe()
+    carddef2 = CardDef()
+    carddef2.name = 'Card 2'
+    carddef2.fields = [
+        StringField(id='1', label='string', varname='bar'),
+        ItemField(
+            id='2', label='card', varname='card1', data_source={'type': 'carddef:%s' % carddef1.url_name}
+        ),
+    ]
+    carddef2.store()
+    carddef2.data_class().wipe()
+
+    wf = Workflow(name='create-carddata')
+    st1 = wf.add_status('Create cards', 'st1')
+    create1 = CreateCarddataWorkflowStatusItem()
+    create1.label = 'Create CardDef1'
+    create1.varname = 'mycard1'
+    create1.id = '_create1'
+    create1.formdef_slug = carddef1.url_name
+    create1.mappings = [
+        Mapping(field_id='1', expression='{{ form_var_card1_foo }}'),
+    ]
+    create1.parent = st1
+    st1.items.append(create1)
+    create2 = CreateCarddataWorkflowStatusItem()
+    create2.label = 'Create CardDef2'
+    create2.varname = 'mycard2'
+    create2.id = '_create2'
+    create2.formdef_slug = carddef2.url_name
+    create2.mappings = [
+        Mapping(field_id='1', expression='{{ form_var_card2_bar }}'),
+        Mapping(field_id='2', expression='{{ form_links_mycard1 }}'),
+    ]
+    create2.parent = st1
+    st1.items.append(create2)
+    wf.store()
+
+    formdef = FormDef()
+    formdef.name = 'form'
+    formdef.fields = [
+        StringField(id='1', label='foo', varname='card1_foo'),
+        StringField(id='2', label='bar', varname='card2_bar'),
+    ]
+    formdef.workflow_id = wf.id
+    formdef.store()
+
+    formdata = formdef.data_class()()
+    formdata.data = {
+        '1': 'card1 foo',
+        '2': 'card2 bar',
+    }
+    formdata.just_created()
+    formdata.perform_workflow()
+
+    assert carddef1.data_class().count() == 1
+    assert carddef2.data_class().count() == 1
+    carddata1 = carddef1.data_class().get(id=1)
+    carddata2 = carddef2.data_class().get(id=1)
+    assert carddata1.data['1'] == 'card1 foo'
+    assert carddata2.data['1'] == 'card2 bar'
+    assert carddata2.data['2'] == str(carddata1.id)
+
+    create2.mappings[1] = Mapping(field_id='2', expression='{{ form_links_mycard1_form_number_raw }}')
+    wf.store()
+    formdef = FormDef.get(formdef.id)
+
+    carddef1.data_class().wipe()
+    carddef2.data_class().wipe()
+    formdef.data_class().wipe()
+    formdata = formdef.data_class()()
+    formdata.data = {
+        '1': 'card1 fooo',
+        '2': 'card2 barr',
+    }
+    formdata.just_created()
+    formdata.perform_workflow()
+
+    assert carddef1.data_class().count() == 1
+    assert carddef2.data_class().count() == 1
+    carddata1 = carddef1.data_class().get(id=1)
+    carddata2 = carddef2.data_class().get(id=1)
+    assert carddata1.data['1'] == 'card1 fooo'
+    assert carddata2.data['1'] == 'card2 barr'
+    assert carddata2.data['2'] == str(carddata1.id)
+
+
 def test_create_carddata_with_map_field(two_pubs):
     CardDef.wipe()
     FormDef.wipe()
diff --git a/wcs/wf/create_formdata.py b/wcs/wf/create_formdata.py
index 00ee3952..73aeb772 100644
--- a/wcs/wf/create_formdata.py
+++ b/wcs/wf/create_formdata.py
@@ -205,11 +205,14 @@ class LazyFormDataLinks:
         self._formdata = formdata
 
     def __getattr__(self, varname):
+        linked_formdatas = []
         for part in self._formdata.iter_evolution_parts():
             if not isinstance(part, LinkedFormdataEvolutionPart):
                 continue
             if part.varname == varname and part.formdata:
-                return CompatibilityNamesDict({'form': part.formdata.get_as_lazy()})
+                linked_formdatas.append(CompatibilityNamesDict({'form': part.formdata.get_as_lazy()}))
+        if linked_formdatas:
+            return LazyFormDataLinksItems(linked_formdatas)
         raise AttributeError(varname)
 
     def inspect_keys(self):
@@ -218,6 +221,29 @@ class LazyFormDataLinks:
                 yield part.varname
 
 
+class LazyFormDataLinksItems:
+    def __init__(self, linked_formdatas):
+        self._linked_formdatas = linked_formdatas
+
+    def inspect_keys(self):
+        return ['form']
+
+    def __str__(self):
+        return str(self._linked_formdatas[0]['form'].internal_id)
+
+    @property
+    def form(self):
+        # alias when there's a single linked formdata
+        return self._linked_formdatas[0]['form']
+
+    def __getattr__(self, varname):
+        formdata = self._linked_formdatas[0]['form']
+        return getattr(formdata, varname)
+
+    def __getitem__(self, key):
+        return getattr(self, key)
+
+
 class CreateFormdataWorkflowStatusItem(WorkflowStatusItem):
     description = _('New Form Creation')
     key = 'create_formdata'
-- 
2.30.2

