Commit 0b69655e authored by Jean-Paul Smets's avatar Jean-Paul Smets

Finished pseudo-code

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@30636 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent fb692cc0
...@@ -30,6 +30,12 @@ import zope.interface ...@@ -30,6 +30,12 @@ import zope.interface
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, interfaces from Products.ERP5Type import Permissions, interfaces
def _compare(tester_list, prevision_movement, decision_movement):
for tester in tester_list:
if not tester.compare(prevision_movement, decision_movement):
return False
return True
class RuleMixin: class RuleMixin:
""" """
Provides generic methods and helper methods to implement Provides generic methods and helper methods to implement
...@@ -135,10 +141,46 @@ class RuleMixin: ...@@ -135,10 +141,46 @@ class RuleMixin:
tester_key = tuple(tester_key) tester_key = tuple(tester_key)
prevision_movement_dict.setdefaults(tester_key, []).append(movement) prevision_movement_dict.setdefaults(tester_key, []).append(movement)
# Build the diff # Prepare a mapping between prevision and decision
movement_collection_diff = self._buildMovementCollectionDiff(tester_list, # The prevision_to_decision_map is a list of tuples
decision_movement_dict, # of the form (prevision_movement_dict, list of decision_movement)
prevision_movement_dict,) prevision_to_decision_map = []
# First find out all existing (decision) movements which belong to no group
no_group_list = []
for tester_key in decision_movement_dict.keys():
if prevision_movement_dict.has_key(tester_key):
for decision_movement in decision_movement_dict[tester_key]:
no_match = True
for prevision_movement in prevision_movement_dict[tester_key]:
# Check if this movement belongs to an existing group
if _compare(tester_list, prevision_movement, decision_movement):
no_match = False
break
if no_match:
# There is no matching.
# So, let us add the decision movements to no_group_list
no_group_list.append(decision_movement)
else:
# The tester key does not even exist.
# So, let us add all decision movements to no_group_list
no_group_list.extend(decision_movement_dict[tester_key])
prevision_to_decision_map.append((None, no_group_list))
# Second, let us create small groups of movements
for tester_key in prevision_movement_dict.keys():
for prevision_movement in prevision_movement_dict[tester_key]:
map_list = []
for decision_movement in decision_movement_dict.get(tester_key, ()):
if _compare(tester_list, prevision_movement, decision_movement):
map_list.append(decision_movement)
prevision_to_decision_map.append((prevision_movement, map_list))
# Third, time to create the diff
movement_collection_diff = MovementCollectionDiff()
for (prevision_movement, decision_movement_list) in prevision_to_decision_map:
self._extendMovementCollectionDiff(movement_collection_diff, prevision_movement,
decision_movement_list)
# Return result # Return result
return movement_collection_diff return movement_collection_diff
...@@ -189,6 +231,16 @@ class RuleMixin: ...@@ -189,6 +231,16 @@ class RuleMixin:
""" """
raise NotImplementedError raise NotImplementedError
def _getDivergenceTesterList(self, exclude_quantity=True):
"""
Return the applicable divergence testers which must
be used to test movement divergence.
exclude_quantity -- if set to true, do not consider
quantity divergence testers
"""
raise NotImplementedError
def _getMatchingTesterList(self): def _getMatchingTesterList(self):
""" """
Return the applicable divergence testers which must Return the applicable divergence testers which must
...@@ -199,77 +251,110 @@ class RuleMixin: ...@@ -199,77 +251,110 @@ class RuleMixin:
def _getQuantityTesterList(self): def _getQuantityTesterList(self):
""" """
Return the applicable divergence testers which must Return the applicable quantity divergence testers.
be used to match movements and build the diff (ie.
not all divergence testers of the Rule)
""" """
raise NotImplementedError raise NotImplementedError
def _buildMovementCollectionDiff(self, tester_list, def _newProfitAndLossMovement(self, prevision_movement):
decision_movement_dict, prevision_movement_dict):
""" """
Build the diff of movements, based on the rule policy Returns a new temp simulation movement which can
which must take into account for example the risk be used to represent a profit or loss in relation
of theft. This is the most important method to override. with prevision_movement
NOTE: XXX-JPS there is probably a way to make parts of this generic prevision_movement -- a simulation movement
and make implementation even easier and simpler.
""" """
raise NotImplementedError
# Sample implementation bellow
def _compare(tester_list, prevision_movement, decision_movement):
for tester in tester_list:
if not tester.compare(prevision_movement, decision_movement):
return False
return True
# Find movements to add or compensate (all updates go through compensate) def _extendMovementCollectionDiff(self, movement_collection_diff,
new_movement_list = [] prevision_movement, decision_movement_list):
compensation_movement_list = [] """
for tester_key in prevision_movement_dict.keys(): Compares a prevision_movement to decision_movement_list which
if decision_movement_dict.has_key(tester_key): are part of the matching group and updates movement_collection_diff
# We have found now a small group of movements with same key accordingly
# we can now start comparing """
for prevision_movement in prevision_movement_dict[tester_key]: raise NotImplementedError
prevision_quantity = prevision_movement.getQuantity() # Sample implementation - but it actually looks very generic
decision_quantity = 0.0 # Case 1: movements which are not needed
compare_movement = None if prevision_movement is None:
for decision_movement in decision_movement_dict[tester_key]: # decision_movement_list contains simulation movements which must
if _compare(tester_list, prevision_movement, decision_movement): # be deleted
decision_quantity += decision_movement.getQuantity() for decision_movement in decision_movement_list:
compare_movement = decision_movement if decision_movement.isDeletable(): # If not frozen and all children are deletable
# Test globaly # Delete deletable
if compare_movement is None: movement_collection_diff.addDeletableMovement(decision_movement)
new_movement_list.append(prevision_movement) else:
else: # Compensate non deletable
# Build a fake movement with total quantity new_movement = decision_movement.asContext(quantity=-decision_movement.getQuantity())
compare_movement = compare_movement.asContext(quantity=decision_quantity) movement_collection_diff.addNewMovement(new_movement)
# And compare it to the expected quantity return
if not _compare(self._getQuantityTesterList(), prevision_movement, compare_movement): # Case 2: movements which are needed but may need update or compensation_movement_list
# Find any movement which is no frozen and update # let us imagine the case of a forward rule
updated_movement = None # ie. what comes in must either go out or has been lost
for decision_movement in decision_movement_dict[tester_key]: divergence_tester_list = self._getDivergenceTesterList()
if not decision_movement.isFrozen(): profit_tester_list = self._getDivergenceTesterList()
updated_movement = decision_movement quantity_tester_list = self._getQuantityTesterList()
decision_movement.setQuantity(prevision_quantity-decision_quantity) compensated_quantity = 0.0
break updatable_movement = None
if updated_movement is not None: not_completed_movement = None
updatable_list.append(decision_movement, {}) updatable_compensation_movement = None
else: prevision_quantity = prevision_movement.getQuantity()
# Else compensate decision_quantity = 0.0
compensation_movement_list.append(compare_movement.asContext( # First, we update all properties (exc. quantity) which could be divergent
quantity=prevision_quantity-decision_quantity)) # and if we can not, we compensate them
else; for decision_movement in decision_movement_list:
new_movement_list.extend(prevision_movement_dict[tester_key]) decision_quantity += decision_movement.getQuantity()
if self._isProfitAndLossMovement(decision_movement):
# Find movements to delete or cancel by compensating if decision_movement.isFrozen():
for tester_key in decision_movement_dict.keys(): # Record not completed movements
if prevision_movement_dict.has_key(tester_key): if not_completed_movement is None and not decision_movement.isCompleted():
for movement in decision_movement_dict[tester_key]: not_completed_movement = decision_movement
quantity = 0 # Frozen must be compensated
for decision_movement in decision_movement_dict[tester_key]: if not _compare(profit_tester_list, prevision_movement, decision_movement):
pass new_movement = decision_movement.asContext(quantity=-decision_movement.getQuantity())
movement_collection_diff.addNewMovement(new_movement)
compensated_quantity += decision_movement.getQuantity()
else:
updatable_compensation_movement = decision_movement
# Not Frozen can be updated
kw = {}
for tester in profit_tester_list:
if tester.compare(prevision_movement, decision_movement):
kw.update(tester.getUpdatablePropertyDict(prevision_movement, decision_movement))
if kw:
movement_collection_diff.addUpdatableMovement(decision_movement, kw)
else:
if decision_movement.isFrozen():
# Frozen must be compensated
if not _compare(divergence_tester_list, prevision_movement, decision_movement):
new_movement = decision_movement.asContext(quantity=-decision_movement.getQuantity())
movement_collection_diff.addNewMovement(new_movement)
compensated_quantity += decision_movement.getQuantity()
else:
updatable_movement = decision_movement
# Not Frozen can be updated
kw = {}
for tester in divergence_tester_list:
if tester.compare(prevision_movement, decision_movement):
kw.update(tester.getUpdatablePropertyDict(prevision_movement, decision_movement))
if kw:
movement_collection_diff.addUpdatableMovement(decision_movement, kw)
# Second, we calculate if the total quantity is the same on both sides
# after compensation
quantity_movement = prevision_movement.asContext(quantity=decision_quantity-compensated_quantity)
if not _compare(quantity_tester_list, prevision_movement, quantity_movement):
missing_quantity = prevision_quantity - decision_quantity + compensated_quantity
if updatable_movement is not None:
# If an updatable movement still exists, we update it
updatable_movement.setQuantity(updatable_movement.getQuantity() + missing_quantity)
elif not_completed_movement is not None:
# It is still possible to add a new movement some movements are not completed
new_movement = prevision_movement.asContext(quantity=missing_quantity)
movement_collection_diff.addNewMovement(new_movement)
elif updatable_compensation_movement is not None:
# If not, it means that all movements are completed
# but we can still update a profit and loss movement_collection_diff
updatable_compensation_movement.setQuantity(updatable_compensation_movement.getQuantity()
+ missing_quantity)
else: else:
# The movement is no longer present # We must create a profit and loss movement
for movement in decision_movement_dict[tester_key]: new_movement = self._newProfitAndLossMovement(prevision_movement)
movement_collection_diff.addNewMovement(new_movement)
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