Commit 2e2f59f7 authored by Arnaud Fontaine's avatar Arnaud Fontaine

Prevent 'draft' Solver Process from being created when there is already a 'solving' one (#KH-1107).

  1. 'Solve Divergences' action:
     => Display Delivery_viewSolveDivergenceDialog.listbox:
        -> Delivery_getSolverDecisionList
           -> Delivery_getSolverProcess => create Solver Process in 'draft' state and build Solver Decisions.
  2. Clicking 'Update' button updates Solver Decisions.
  3. Click 'Solve Divergences' button after either 'Adopt Prevision' or 'Accept Decision'.
     => SolverProcess.solve():
        Solver Process transit to 'solving' state and 'solve' Activity is created.

Before this Activity is executed, user can still select 'Solve Divergences'
action and this will create another 'Solver Process' as the current one is not
in 'draft' state anymore. Moreover, another user can still 'Adopt Prevision'
(while the first user selected 'Accept Decision') and this will be actually be
applied despite the first user choice.

Instead, create Solver Process when transiting to 'diverged' state and do not
display 'Solve Divergences' button nor allow access to 'Solve Divergences
Dialog' (Delivery_viewSolveDivergenceDialog) if there is a Solver Process in
'solving' state.
parent 66cc5bfa
if context.Delivery_getSolverProcess() is None:
message = context.Base_translateString("Workflow state may have been updated by other user. Please try again.")
return context.Base_redirect('view',
keep_items={'portal_status_message': message})
return context.Delivery_viewSolveDivergenceDialog(*args, **kw)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>*args, **kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Delivery_solveDivergenceAction</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
delivery = state_change['object']
draft_solver_process = None
for solver_process in delivery.getSolverValueList():
validation_state = solver_process.getValidationState()
if validation_state == 'solving':
# Currently solving Divergences through solve() Activity
return
elif solver_process.getValidationState() == 'draft':
draft_solver_process = solver_process
if draft_solver_process is not None:
draft_solver_process.buildSolverDecisionList(delivery)
else:
delivery.getPortalObject().portal_solver_processes.newSolverProcess(delivery)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>state_change</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Delivery_diverge</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -20,7 +20,7 @@
</item>
<item>
<key> <string>after_script_name</string> </key>
<value> <string></string> </value>
<value> <string>Delivery_diverge</string> </value>
</item>
<item>
<key> <string>description</string> </key>
......
......@@ -16,7 +16,7 @@
</item>
<item>
<key> <string>actbox_url</string> </key>
<value> <string>%(content_url)s/Delivery_viewSolveDivergenceDialog?field_your_workflow_action=solve_divergence_action</string> </value>
<value> <string>%(content_url)s/Delivery_solveDivergenceAction?field_your_workflow_action=solve_divergence_action</string> </value>
</item>
<item>
<key> <string>after_script_name</string> </key>
......@@ -88,7 +88,7 @@
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: here.getSimulationState() not in here.getPortalDraftOrderStateList()</string> </value>
<value> <string>python: here.getSimulationState() not in here.getPortalDraftOrderStateList() and here.Delivery_getSolverProcess() is not None</string> </value>
</item>
</dictionary>
</pickle>
......
# XXX currently we create a Solver Process and build Solver Decision in it if missing.
# But for better performance, Solver Process and Solver Decision should be created beforehand
# by causality workflow when a delivery becomes divergent.
for solver_process in context.getSolverValueList():
if solver_process.getValidationState() == 'draft':
solver_process.buildSolverDecisionList(context)
break
else:
solver_process = context.getPortalObject().portal_solver_processes.newSolverProcess(context)
return solver_process
return solver_process
portal = context.getPortalObject()
solver_process = context.getSolverValueList()[-1]
if solver_process.getValidationState() == 'solving':
return
solver_decision_uid = int(solver_decision_uid)
solver_decision = None
for solver_decision in solver_process.objectValues(portal_type="Solver Decision"):
......
......@@ -30,12 +30,17 @@ This test is experimental for new simulation implementation.
"""
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from Products.ERP5Type.tests.SecurityTestCase import SecurityTestCase
from Products.ERP5Type.tests.Sequence import SequenceList
from testPackingList import TestPackingListMixin
from Products.PythonScripts.Utility import allow_class
class DummySolverConfiguration(object):
def as_dict(self):
return {'tested_property_list': ['quantity']}
allow_class(DummySolverConfiguration)
class TestERP5Simulation(TestPackingListMixin, ERP5TypeTestCase):
class TestERP5Simulation(TestPackingListMixin, SecurityTestCase):
run_all_test = 1
quiet = 0
......@@ -329,3 +334,105 @@ class TestERP5Simulation(TestPackingListMixin, ERP5TypeTestCase):
sequence_list.addSequenceString(sequence_string)
sequence_list.play(self, quiet=quiet)
def _getPortalStatusMessage(self, url):
return url.rsplit('=', 1)[-1].replace('%20', ' ')
def stepSolveDivergenceThroughDialog(self,
sequence=None,
sequence_list=None):
packing_list = sequence.get('packing_list')
# 'Solve Divergences' Action (Delivery_viewSolveDivergenceDialog)
self.assertUserCanPassWorkflowTransition('ERP5TypeTestCase',
'solve_divergence_action',
packing_list)
solver_decision_list = packing_list.Delivery_getSolverDecisionList()
self.assertEqual(len(solver_decision_list), 1)
# Choose 'Divergence Resolution' (Adopt Prevision)
solver_decision = solver_decision_list[0]
packing_list.REQUEST['listbox'] = {
solver_decision.getPath():
{'comment': '',
'solver_configuration': DummySolverConfiguration(),
'solver': 'portal_solvers/Adopt Solver'}}
packing_list.Delivery_updateSolveDivergenceDialog()
# Solve Divergences
self.assertEqual(
self._getPortalStatusMessage(packing_list.Delivery_submitSolveDivergenceDialog()),
'Divergence solvers started in background.')
# SolverProcess.solve() called
# => SolverProcess.startSolving()
# => solve() activity created
self.assertEqual(solver_decision.getValidationState(), 'solving')
sequence.edit(solver_decision=solver_decision)
def stepCheckOnlyOneSolverProcessCreated(self,
sequence=None,
sequence_list=None):
packing_list = sequence.get('packing_list')
solver_decision = sequence.get('solver_decision')
# 'Solve Divergences' Action should not be available anymore
self.failIfUserCanPassWorkflowTransition('ERP5TypeTestCase',
'solve_divergence_action',
packing_list)
# Solver Process is in 'solving' state, so no new Solver Process should be
# created and this should return nothing
self.assertEqual(len(packing_list.getSolverValueList()), 1)
self.assertEqual(len(packing_list.Delivery_getSolverDecisionList()), 0)
packing_list.REQUEST['listbox'] = {
solver_decision.getPath():
{'comment': '',
'solver_configuration': DummySolverConfiguration(),
'solver': 'portal_solvers/Adopt Solver'}}
self.assertEqual(
self._getPortalStatusMessage(packing_list.Delivery_updateSolveDivergenceDialog()),
'Workflow state may have been updated by other user. Please try again.')
self.assertEqual(
self._getPortalStatusMessage(packing_list.Delivery_submitSolveDivergenceDialog()),
'Workflow state may have been updated by other user. Please try again.')
self.tic()
self.assertEqual(packing_list.getCausalityState(), 'solved')
def test_03_solverProcessCreatedOnlyOnce(self, quiet=quiet, run=run_all_test):
"""
Solver Process used to be created after selecting 'Solve Divergences' Action:
1. Select 'Solve Divergences' Action.
2. Dialog is displayed and new 'draft' 'Solver Process' is created if it
does not exist yet.
3. Adopt Prevision/Accept Decision is selected: this creates a 'solve'
Activity after changing Solver Process workflow state to 'solving'
Until 'solve' Activity is processed:
=> 2. meant that there was no more 'Solver Process' in 'draft' state and
thus a new one may be created.
=> The 'Solve Divergences' Action was still available.
Now Solver Process is created when causality_state changed to diverged and
'Solve Divergences' Action is not displayed unless its state is 'draft'.
"""
if not run: return
sequence_list = SequenceList()
# Test with a simply order without cell
sequence_string = self.default_sequence + '\
stepIncreasePackingListLineQuantity1000 \
stepCheckPackingListIsCalculating \
stepTic \
stepCheckPackingListIsDiverged \
stepSolveDivergenceThroughDialog \
stepCheckOnlyOneSolverProcessCreated \
'
sequence_list.addSequenceString(sequence_string)
sequence_list.play(self, quiet=quiet)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment