From cc47f905c54667769c2c07133ea9def54b10d314 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Tue, 11 Oct 2022 11:26:43 +0200
Subject: [PATCH 1/2] workflow: add a trace on update carddata by wf action
 (#70000)

---
 tests/workflow/test_carddata.py |  8 +++++++
 wcs/backoffice/management.py    | 39 +++++++++++++++++++++++++++++----
 wcs/wf/edit_carddata.py         | 19 +++++++++++++++-
 wcs/workflows.py                |  1 +
 4 files changed, 62 insertions(+), 5 deletions(-)

diff --git a/tests/workflow/test_carddata.py b/tests/workflow/test_carddata.py
index c08b825fa..7a97d00a7 100644
--- a/tests/workflow/test_carddata.py
+++ b/tests/workflow/test_carddata.py
@@ -634,6 +634,14 @@ def test_edit_carddata_with_data_sourced_object(pub):
     assert data.data['2'] == 'c'
     assert data.data['2_display'] == 'cook'
     assert data.data['2_structured'] == {'id': 'c', 'text': 'cook', 'extra': 'plop2'}
+    # check evolutions & tracing
+    assert isinstance(data.evolution[0].parts[0], ActionsTracingEvolutionPart)
+    assert data.evolution[0].parts[0].actions[0][1:] == (
+        'edit_carddata',  # item key
+        'edit',  # item id
+        'st1',  # status
+        wf.id,  # workflow id
+    )
 
     formdata = formdef.data_class()()
     formdata.data = {'0': '1', '1': 'b'}
diff --git a/wcs/backoffice/management.py b/wcs/backoffice/management.py
index 9ff70a9f5..b7c34a313 100644
--- a/wcs/backoffice/management.py
+++ b/wcs/backoffice/management.py
@@ -44,7 +44,13 @@ from wcs.forms.backoffice import FormDefUI
 from wcs.forms.common import FormStatusPage
 from wcs.roles import logged_users_role
 from wcs.variables import LazyFieldVar, LazyList
-from wcs.workflows import ActionsTracingEvolutionPart, WorkflowStatusItem, item_classes, template_on_formdata
+from wcs.workflows import (
+    ActionsTracingEvolutionPart,
+    Workflow,
+    WorkflowStatusItem,
+    item_classes,
+    template_on_formdata,
+)
 
 from ..qommon import _, errors, ezt, force_str, get_cfg, misc, ngettext, ods, pgettext_lazy, template
 from ..qommon.afterjobs import AfterJob
@@ -3747,11 +3753,28 @@ class FormBackOfficeStatusPage(FormStatusPage):
                     if part.actions and part != last_event_line:
                         last_event_line = part
                         r += htmltext('<li><span class="event">%s</span></li>') % part.get_event_label()
-                    for action_ts, action_key, action_id in part.actions:
+                    for action_parts in part.actions:
+                        external_status_id, external_wf_id = None, None
+                        if len(action_parts) == 5:
+                            (
+                                action_ts,
+                                action_key,
+                                action_id,
+                                external_status_id,
+                                external_wf_id,
+                            ) = action_parts
+                        else:
+                            action_ts, action_key, action_id = action_parts[:3]
+                        external_wf = None
+                        if external_wf_id:
+                            external_wf = Workflow.get(external_wf_id, ignore_errors=True)
                         action_label = action_classes.get(action_key, action_key)
                         try:
                             url = '%sitems/%s/' % (
-                                part.get_base_url(self.filled.formdef.workflow, wf_status),
+                                part.get_base_url(
+                                    external_wf or self.filled.formdef.workflow,
+                                    external_status_id or wf_status,
+                                ),
                                 action_id,
                             )
                         except KeyError:
@@ -3763,7 +3786,15 @@ class FormBackOfficeStatusPage(FormStatusPage):
                             url,
                             action_label,
                         )
-                        real_action = part.get_real_action(self.filled.formdef.workflow, wf_status, action_id)
+                        if external_wf:
+                            r += htmltext(' <span class="tracing-details">(%s)</span>') % _(
+                                'external workflow'
+                            )
+                        real_action = part.get_real_action(
+                            external_wf or self.filled.formdef.workflow,
+                            external_status_id or wf_status,
+                            action_id,
+                        )
                         if real_action:
                             details = real_action.get_inspect_details()
                             if details:
diff --git a/wcs/wf/edit_carddata.py b/wcs/wf/edit_carddata.py
index 9152d3033..245ad2ac5 100644
--- a/wcs/wf/edit_carddata.py
+++ b/wcs/wf/edit_carddata.py
@@ -14,12 +14,16 @@
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, see <http://www.gnu.org/licenses/>.
 
+import datetime
+import time
+
 from quixote import get_publisher
 
+from wcs.formdata import Evolution
 from wcs.qommon import _
 from wcs.wf.create_carddata import CreateCarddataWorkflowStatusItem
 from wcs.wf.external_workflow import ExternalWorkflowGlobalAction
-from wcs.workflows import register_item_class
+from wcs.workflows import ActionsTracingEvolutionPart, register_item_class
 
 
 class EditCarddataWorkflowStatusItem(CreateCarddataWorkflowStatusItem, ExternalWorkflowGlobalAction):
@@ -51,6 +55,19 @@ class EditCarddataWorkflowStatusItem(CreateCarddataWorkflowStatusItem, ExternalW
         for target_data in self.iter_target_datas(formdata, carddef):
             self.apply_mappings(dest=target_data, src=formdata)
             with get_publisher().substitutions.freeze():
+                # store an ActionsTracingEvolutionPart with a link to current workflow & action
+                evo = Evolution()
+                evo.time = time.localtime()
+                evo.status = target_data.status
+                performed_action = (
+                    datetime.datetime.now(),
+                    self.key,
+                    self.id,
+                    self.parent.id,
+                    self.parent.parent.id,
+                )
+                evo.add_part(ActionsTracingEvolutionPart('workflow-edited', [performed_action]))
+                target_data.evolution.append(evo)
                 target_data.store()
 
         # update local object as it may have modified itself
diff --git a/wcs/workflows.py b/wcs/workflows.py
index 504ffd9fc..186f31e5f 100644
--- a/wcs/workflows.py
+++ b/wcs/workflows.py
@@ -419,6 +419,7 @@ class ActionsTracingEvolutionPart(EvolutionPart):
             'json-import-created': _('Created (by JSON import)'),
             'timeout-jump': _('Timeout jump'),
             'workflow-created': _('Created (by workflow action)'),
+            'workflow-edited': _('Edited (by workflow action)'),
             'workflow-form-submit': _('Action in workflow form'),
         }.get(self.event, self.event)
 
-- 
2.35.1

