From 2839171620545c264c4a2ffcb055469b4651d32a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thomas=20NO=C3=8BL?= <tnoel@entrouvert.com>
Date: Fri, 25 Feb 2022 23:43:43 +0100
Subject: [PATCH] wf: add remove tracking code action (#59033)

---
 tests/admin_pages/test_workflow.py | 23 ++++++++++++
 tests/workflow/test_all.py         | 41 ++++++++++++++++++++
 wcs/wf/remove_tracking_code.py     | 60 ++++++++++++++++++++++++++++++
 wcs/workflows.py                   |  1 +
 4 files changed, 125 insertions(+)
 create mode 100644 wcs/wf/remove_tracking_code.py

diff --git a/tests/admin_pages/test_workflow.py b/tests/admin_pages/test_workflow.py
index 58ebbc2ec..0f8204888 100644
--- a/tests/admin_pages/test_workflow.py
+++ b/tests/admin_pages/test_workflow.py
@@ -27,6 +27,7 @@ from wcs.wf.form import FormWorkflowStatusItem, WorkflowFormFieldsFormDef
 from wcs.wf.jump import JumpWorkflowStatusItem
 from wcs.wf.redirect_to_url import RedirectToUrlWorkflowStatusItem
 from wcs.wf.register_comment import RegisterCommenterWorkflowStatusItem
+from wcs.wf.remove_tracking_code import RemoveTrackingCodeWorkflowStatusItem
 from wcs.wf.roles import AddRoleWorkflowStatusItem, RemoveRoleWorkflowStatusItem
 from wcs.wf.wscall import WebserviceCallStatusItem
 from wcs.workflows import (
@@ -3519,3 +3520,25 @@ def test_workflows_duplicate_keep_ids(pub):
     wf2 = Workflow.get(2)
     assert wf2.possible_status[0].id == 'baz'
     assert wf2.backoffice_fields_formdef.fields[0].id == 'bo1-1x'
+
+
+def test_remove_tracking_code_details(pub):
+    create_superuser(pub)
+
+    Workflow.wipe()
+    workflow = Workflow(name='foo')
+    baz_status = workflow.add_status(name='baz')
+    remove_code = RemoveTrackingCodeWorkflowStatusItem()
+    remove_code.parent = baz_status
+    baz_status.items.append(remove_code)
+    workflow.store()
+
+    app = login(get_app(pub))
+    resp = app.get('/backoffice/workflows/%s/status/%s/' % (workflow.id, baz_status.id))
+    assert 'Remove Tracking Code' in resp.text
+    assert 'Remove Tracking Code (' not in resp.text
+
+    remove_code.replace = True
+    workflow.store()
+    resp = app.get('/backoffice/workflows/%s/status/%s/' % (workflow.id, baz_status.id))
+    assert 'Remove Tracking Code (replace with a new one)' in resp.text
diff --git a/tests/workflow/test_all.py b/tests/workflow/test_all.py
index 20b211e0b..e125038bd 100644
--- a/tests/workflow/test_all.py
+++ b/tests/workflow/test_all.py
@@ -70,6 +70,7 @@ from wcs.wf.profile import UpdateUserProfileStatusItem
 from wcs.wf.redirect_to_url import RedirectToUrlWorkflowStatusItem
 from wcs.wf.register_comment import JournalEvolutionPart, RegisterCommenterWorkflowStatusItem
 from wcs.wf.remove import RemoveWorkflowStatusItem
+from wcs.wf.remove_tracking_code import RemoveTrackingCodeWorkflowStatusItem
 from wcs.wf.roles import AddRoleWorkflowStatusItem, RemoveRoleWorkflowStatusItem
 from wcs.wf.timeout_jump import TimeoutWorkflowStatusItem
 from wcs.wf.wscall import WebserviceCallStatusItem
@@ -6852,3 +6853,43 @@ def test_conditional_jump_vs_tracing(pub):
     formdata.refresh_from_storage()
     assert formdata.evolution[0].parts[-1].actions[0][1:] == ('register-comment', str(comment.id))
     assert formdata.evolution[0].parts[-1].actions[1][1:] == ('jump', str(jump2.id))
+
+
+def test_remove_tracking_code(two_pubs):
+    formdef = FormDef()
+    formdef.name = 'baz'
+    formdef.enable_tracking_codes = True
+    formdef.store()
+
+    formdef.data_class().wipe()
+    formdata = formdef.data_class()()
+    formdata.store()
+    code = get_publisher().tracking_code_class()
+    code.formdata = formdata
+    formdata.refresh_from_storage()
+
+    assert formdata.tracking_code
+    assert get_publisher().tracking_code_class().count() == 1
+
+    item = RemoveTrackingCodeWorkflowStatusItem()
+    item.perform(formdata)
+    assert not formdata.tracking_code
+    assert get_publisher().tracking_code_class().count() == 0
+    item.perform(formdata)  # do not crash if no tracking_code
+
+    item.replace = True
+    item.perform(formdata)
+    assert formdata.tracking_code
+    assert get_publisher().tracking_code_class().count() == 1
+    tracking_code_orig = formdata.tracking_code
+    item.perform(formdata)
+    assert formdata.tracking_code
+    assert formdata.tracking_code != tracking_code_orig
+    assert get_publisher().tracking_code_class().count() == 1
+
+    # cannot replace if formdef not handles tracking code
+    formdef.enable_tracking_codes = False
+    formdef.store()
+    item.perform(formdata)
+    assert not formdata.tracking_code
+    assert get_publisher().tracking_code_class().count() == 0
diff --git a/wcs/wf/remove_tracking_code.py b/wcs/wf/remove_tracking_code.py
new file mode 100644
index 000000000..7f775f0ed
--- /dev/null
+++ b/wcs/wf/remove_tracking_code.py
@@ -0,0 +1,60 @@
+# w.c.s. - web application for online forms
+# Copyright (C) 2005-2022  Entr'ouvert
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+from quixote import get_publisher
+
+from wcs.qommon import _
+from wcs.qommon.form import CheckboxWidget
+from wcs.workflows import WorkflowStatusItem, register_item_class
+
+
+class RemoveTrackingCodeWorkflowStatusItem(WorkflowStatusItem):
+    description = _('Remove Tracking Code')
+    key = 'remove_tracking_code'
+    category = 'formdata-action'
+
+    replace = False
+
+    def add_parameters_widgets(self, form, parameters, prefix='', formdef=None, **kwargs):
+        super().add_parameters_widgets(form, parameters, prefix=prefix, formdef=formdef, **kwargs)
+        if 'replace' in parameters:
+            form.add(
+                CheckboxWidget,
+                '%sreplace' % prefix,
+                title=_('Replace with a new tracking code'),
+                value=self.replace,
+                hint=_('This only works if form supports tracking codes.'),
+            )
+
+    def get_line_details(self):
+        if self.replace:
+            return _('replace with a new one')
+
+    def get_parameters(self):
+        return ('replace', 'condition')
+
+    def perform(self, formdata):
+        if formdata.tracking_code:
+            get_publisher().tracking_code_class.remove_object(formdata.tracking_code)
+        if self.replace and formdata.formdef.enable_tracking_codes:
+            code = get_publisher().tracking_code_class()
+            code.formdata = formdata  # this will .store() code and formdata
+        else:
+            formdata.tracking_code = None
+            formdata.store()
+
+
+register_item_class(RemoveTrackingCodeWorkflowStatusItem)
diff --git a/wcs/workflows.py b/wcs/workflows.py
index 91aeacb9c..90fdaacdd 100644
--- a/wcs/workflows.py
+++ b/wcs/workflows.py
@@ -3453,6 +3453,7 @@ def load_extra():
     from .wf import redirect_to_url  # noqa pylint: disable=unused-import
     from .wf import register_comment  # noqa pylint: disable=unused-import
     from .wf import remove  # noqa pylint: disable=unused-import
+    from .wf import remove_tracking_code  # noqa pylint: disable=unused-import
     from .wf import resubmit  # noqa pylint: disable=unused-import
     from .wf import roles  # noqa pylint: disable=unused-import
     from .wf import sendmail  # noqa pylint: disable=unused-import
-- 
2.34.1

