Commit 9c516160 authored by Sebastien Robin's avatar Sebastien Robin

simulation: add a solver allowing to move quantity to another existing delivery

Usually, the split and defer dialog suggest you to define start and stop date. So if you have
already an existing delivery and you would like it to be updated, you have to set correct start
and stop date and, if all categories are fully the same, you might have some chance to see
this delivery updated.

To improve the case where you would like to move quantity to existing delivery, add option in the
solve divergence dialog of "Move to existing Delivery" that allows you to select similar deliveries. Then
this specific solver would set correct properties for you to make sure the expected delivery would be updated.
parent 40f40381
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Solver Type" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAY=</string> </persistent>
</value>
</item>
<item>
<key> <string>acquire_local_roles</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>automatic_solver</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>delivery_solver/portal_solvers/FIFO Delivery Solver</string>
<string>delivery_solver/portal_solvers/LIFO Delivery Solver</string>
<string>delivery_solver/portal_solvers/Minimise Price Delivery Solver</string>
</tuple>
</value>
</item>
<item>
<key> <string>configuration_form_id</string> </key>
<value> <string>QuantitySplitMoveSolver_viewConfigurationFormBox</string> </value>
</item>
<item>
<key> <string>configuration_groupable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>configuration_property_list</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>content_icon</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>The Quantity Split Solver splits a simulation movement into multiple simulation movements and place them in existing deliveries in order to defer the delivery of remaining quantities.</string> </value>
</item>
<item>
<key> <string>factory</string> </key>
<value> <string>addQuantitySplitSolver</string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<tuple>
<string>target_solver</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Quantity Split Move Solver</string> </value>
</item>
<item>
<key> <string>init_script</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>line_exclusive</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>line_groupable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>permission</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Solver Type</string> </value>
</item>
<item>
<key> <string>process_exclusive</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>solver_action_title</string> </key>
<value> <string>Move to another Delivery</string> </value>
</item>
<item>
<key> <string>type_class</string> </key>
<value> <string>QuantitySplitMoveSolver</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="5" aka="AAAAAAAAAAU=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="6" aka="AAAAAAAAAAY=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Action Information" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>action_permission</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_view</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>float_index</string> </key>
<value> <float>2.0</float> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>1</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>reference</string> </key>
<value> <string>configuration</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Configuration</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<tuple>
<global name="Expression" module="Products.CMFCore.Expression"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/Solver_viewConfiguration</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Action Information" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>action_permission</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_view</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>float_index</string> </key>
<value> <float>1.0</float> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>2</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>reference</string> </key>
<value> <string>view</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>View</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<tuple>
<global name="Expression" module="Products.CMFCore.Expression"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/TargetSolver_view</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -6,6 +6,7 @@
<item>LIFO Delivery Solver</item>
<item>Minimise Price Delivery Solver</item>
<item>Production Reduction Solver</item>
<item>Quantity Split Move Solver</item>
<item>Quantity Split Solver</item>
<item>Trade Model Solver</item>
<item>Unify Solver</item>
......
......@@ -12,6 +12,8 @@ portal_solvers/Production Reduction Solver
portal_solvers/Production Reduction Solver/*
portal_solvers/Quantity Cancel Solver
portal_solvers/Quantity Cancel Solver/*
portal_solvers/Quantity Split Move Solver
portal_solvers/Quantity Split Move Solver/*
portal_solvers/Quantity Split Solver
portal_solvers/Quantity Split Solver/*
portal_solvers/Trade Model Solver
......
......@@ -4,6 +4,7 @@ Solver Process | FIFO Delivery Solver
Solver Process | LIFO Delivery Solver
Solver Process | Minimise Price Delivery Solver
Solver Process | Production Reduction Solver
Solver Process | Quantity Split Move Solver
Solver Process | Quantity Split Solver
Solver Process | Trade Model Solver
Solver Process | Unify Solver
\ No newline at end of file
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2017 Nexedi SA and Contributors. All Rights Reserved.
# Sebastien Robin <seb@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import zope.interface
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5Type.Globals import PersistentMapping
from Products.ERP5.mixin.solver import SolverMixin
from Products.ERP5.Document.QuantitySplitSolver import QuantitySplitSolver
class QuantitySplitMoveSolver(QuantitySplitSolver):
"""Target solver that split the prevision based on quantity.
Similar to QuantitySplitSolver, it creates another prevision movement with the
delta quantity between decision and prevision.
But this time we will move quantities to an existing delivery that would be
passed as parameter to this solver (usually selected directly by user).
"""
meta_type = 'ERP5 Quantity Split And Move Solver'
portal_type = 'Quantity Split And Move Solver'
add_permission = Permissions.AddPortalContent
isIndexable = 0 # We do not want to fill the catalog with objects on which we need no reporting
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative interfaces
zope.interface.implements(interfaces.ISolver,
interfaces.IConfigurable,
)
# ISolver Implementation
def _solve(self, activate_kw=None):
"""
Split simulation movements, and build them into existing delivery
"""
portal = self.getPortalObject()
diverged_simulation_movement = self.getDeliveryValue()
solver_dict = self._solveBySplitting(activate_kw=activate_kw)
configuration_dict = self.getConfigurationPropertyDict()
new_movement_list = solver_dict['new_movement_list']
delivery = diverged_simulation_movement.getDeliveryValue().getParentValue()
assert delivery.isDelivery()
trade_phase = self.getDeliveryValue().getTradePhase()
assert trade_phase is not None, "Unable to solve, no trade phase is defined for %s" % diverged_simulation_movement.getRelativeUrl()
business_link_list = diverged_simulation_movement.asComposedDocument().getBusinessLinkValueList(trade_phase=trade_phase)
assert len(business_link_list) == 1, "Expected to find only one business link for trade_phase %s, but found %r" % (trade_phase,
[x.getRelativeUrl() for x in business_link_list])
business_link, = business_link_list
delivery_builder_list = business_link.getDeliveryBuilderValueList()
assert len(delivery_builder_list) == 1, "Expected to find only one builder on business link, but found %r" % (
business_link.getRelativeUrl(), [x.getRelativeUrl() for x in delivery_builder_list])
delivery_builder, = delivery_builder_list
# Update simulation movements to make sure they match the new delivery
delivery_level_movement_group = [x for x in delivery_builder.objectValues() if x.getCollectOrderGroup() == "delivery"]
delivery_to_move = portal.unrestrictedTraverse(configuration_dict['delivery_url'])
assert delivery_to_move.getDivergenceList() == []
for movement_group in delivery_level_movement_group:
tested_property_list = movement_group.getTestedPropertyList()
for tested_property in tested_property_list:
for new_movement in new_movement_list:
new_movement.setPropertyList(tested_property, delivery_to_move.getPropertyList(tested_property))
new_movement.recordProperty(tested_property)
delivery_builder.build(delivery_relative_url_list=[configuration_dict['delivery_url']],
movement_list=new_movement_list)
# Now the delivery_builder has chance to be divergent. In such case, make sure to accept value
# from simulation
divergence_list = delivery_to_move.getDivergenceList()
if divergence_list:
"""def _solveDivergence(self, document, property, solver, **kw):"""
"""Solve divergence by using solver tool"""
solver_process_tool = portal.portal_solver_processes
solver_process = solver_process_tool.newSolverProcess(delivery_to_move)
solver_decision, = [x for x in solver_process.contentValues()
if x.getCausalityValue().getTestedProperty() == "quantity"]
# use Quantity Accept Solver.
solver_decision.setSolverValue(portal.portal_solvers['Adopt Solver'])
# configure for Accept Solver.
solver_decision.updateConfiguration(tested_property_list=['quantity'])
solver_process.buildTargetSolverList()
solver_process.solve()
......@@ -69,6 +69,15 @@ class QuantitySplitSolver(SolverMixin, ConfigurableMixin, XMLObject):
def _solve(self, activate_kw=None):
"""
"""
self._solveBySplitting(activate_kw=activate_kw)
def _solveBySplitting(self, activate_kw=None):
"""
contains all the logic to split. This method is convenient in case
another solver needs it.
"""
solver_dict = {}
new_movement_list = []
configuration_dict = self.getConfigurationPropertyDict()
delivery_dict = {}
for simulation_movement in self.getDeliveryValueList():
......@@ -98,6 +107,7 @@ class QuantitySplitSolver(SolverMixin, ConfigurableMixin, XMLObject):
new_movement = applied_rule.newContent(
new_id, simulation_movement.getPortalType(),
activate_kw=activate_kw, **kw)
new_movement_list.append(new_movement)
# Dirty code until IPropertyRecordable is revised.
# Merge original simulation movement recorded property to new one.
recorded_property_dict = simulation_movement._getRecordedPropertyDict(None)
......@@ -129,3 +139,5 @@ class QuantitySplitSolver(SolverMixin, ConfigurableMixin, XMLObject):
if self.getPortalObject().portal_workflow.isTransitionPossible(
self, 'succeed'):
self.succeed()
solver_dict["new_movement_list"] = new_movement_list
return solver_dict
\ No newline at end of file
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