[Erp5-report] r30636 - /erp5/trunk/products/ERP5/mixin/rule.py
nobody at svn.erp5.org
nobody at svn.erp5.org
Sun Nov 15 21:42:56 CET 2009
Author: jp
Date: Sun Nov 15 21:42:56 2009
New Revision: 30636
URL: http://svn.erp5.org?rev=30636&view=rev
Log:
Finished pseudo-code
Modified:
erp5/trunk/products/ERP5/mixin/rule.py
Modified: erp5/trunk/products/ERP5/mixin/rule.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/mixin/rule.py?rev=30636&r1=30635&r2=30636&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/mixin/rule.py [utf8] (original)
+++ erp5/trunk/products/ERP5/mixin/rule.py [utf8] Sun Nov 15 21:42:56 2009
@@ -29,6 +29,12 @@
import zope.interface
from AccessControl import ClassSecurityInfo
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:
"""
@@ -135,10 +141,46 @@
tester_key = tuple(tester_key)
prevision_movement_dict.setdefaults(tester_key, []).append(movement)
- # Build the diff
- movement_collection_diff = self._buildMovementCollectionDiff(tester_list,
- decision_movement_dict,
- prevision_movement_dict,)
+ # Prepare a mapping between prevision and decision
+ # The prevision_to_decision_map is a list of tuples
+ # of the form (prevision_movement_dict, list of decision_movement)
+ 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 movement_collection_diff
@@ -189,6 +231,16 @@
"""
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):
"""
Return the applicable divergence testers which must
@@ -199,77 +251,110 @@
def _getQuantityTesterList(self):
"""
- Return the applicable divergence testers which must
- be used to match movements and build the diff (ie.
- not all divergence testers of the Rule)
- """
- raise NotImplementedError
-
- def _buildMovementCollectionDiff(self, tester_list,
- decision_movement_dict, prevision_movement_dict):
- """
- Build the diff of movements, based on the rule policy
- which must take into account for example the risk
- of theft. This is the most important method to override.
-
- NOTE: XXX-JPS there is probably a way to make parts of this generic
- 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)
- new_movement_list = []
- compensation_movement_list = []
- for tester_key in prevision_movement_dict.keys():
- if decision_movement_dict.has_key(tester_key):
- # We have found now a small group of movements with same key
- # we can now start comparing
- for prevision_movement in prevision_movement_dict[tester_key]:
- prevision_quantity = prevision_movement.getQuantity()
- decision_quantity = 0.0
- compare_movement = None
- for decision_movement in decision_movement_dict[tester_key]:
- if _compare(tester_list, prevision_movement, decision_movement):
- decision_quantity += decision_movement.getQuantity()
- compare_movement = decision_movement
- # Test globaly
- if compare_movement is None:
- new_movement_list.append(prevision_movement)
- else:
- # Build a fake movement with total quantity
- compare_movement = compare_movement.asContext(quantity=decision_quantity)
- # And compare it to the expected quantity
- if not _compare(self._getQuantityTesterList(), prevision_movement, compare_movement):
- # Find any movement which is no frozen and update
- updated_movement = None
- for decision_movement in decision_movement_dict[tester_key]:
- if not decision_movement.isFrozen():
- updated_movement = decision_movement
- decision_movement.setQuantity(prevision_quantity-decision_quantity)
- break
- if updated_movement is not None:
- updatable_list.append(decision_movement, {})
- else:
- # Else compensate
- compensation_movement_list.append(compare_movement.asContext(
- quantity=prevision_quantity-decision_quantity))
- else;
- new_movement_list.extend(prevision_movement_dict[tester_key])
-
- # Find movements to delete or cancel by compensating
- for tester_key in decision_movement_dict.keys():
- if prevision_movement_dict.has_key(tester_key):
- for movement in decision_movement_dict[tester_key]:
- quantity = 0
- for decision_movement in decision_movement_dict[tester_key]:
- pass
+ Return the applicable quantity divergence testers.
+ """
+ raise NotImplementedError
+
+ def _newProfitAndLossMovement(self, prevision_movement):
+ """
+ Returns a new temp simulation movement which can
+ be used to represent a profit or loss in relation
+ with prevision_movement
+
+ prevision_movement -- a simulation movement
+ """
+
+ def _extendMovementCollectionDiff(self, movement_collection_diff,
+ prevision_movement, decision_movement_list):
+ """
+ Compares a prevision_movement to decision_movement_list which
+ are part of the matching group and updates movement_collection_diff
+ accordingly
+ """
+ raise NotImplementedError
+ # Sample implementation - but it actually looks very generic
+ # Case 1: movements which are not needed
+ if prevision_movement is None:
+ # decision_movement_list contains simulation movements which must
+ # be deleted
+ for decision_movement in decision_movement_list:
+ if decision_movement.isDeletable(): # If not frozen and all children are deletable
+ # Delete deletable
+ movement_collection_diff.addDeletableMovement(decision_movement)
+ else:
+ # Compensate non deletable
+ new_movement = decision_movement.asContext(quantity=-decision_movement.getQuantity())
+ movement_collection_diff.addNewMovement(new_movement)
+ return
+ # Case 2: movements which are needed but may need update or compensation_movement_list
+ # let us imagine the case of a forward rule
+ # ie. what comes in must either go out or has been lost
+ divergence_tester_list = self._getDivergenceTesterList()
+ profit_tester_list = self._getDivergenceTesterList()
+ quantity_tester_list = self._getQuantityTesterList()
+ compensated_quantity = 0.0
+ updatable_movement = None
+ not_completed_movement = None
+ updatable_compensation_movement = None
+ prevision_quantity = prevision_movement.getQuantity()
+ decision_quantity = 0.0
+ # First, we update all properties (exc. quantity) which could be divergent
+ # and if we can not, we compensate them
+ for decision_movement in decision_movement_list:
+ decision_quantity += decision_movement.getQuantity()
+ if self._isProfitAndLossMovement(decision_movement):
+ if decision_movement.isFrozen():
+ # Record not completed movements
+ if not_completed_movement is None and not decision_movement.isCompleted():
+ not_completed_movement = decision_movement
+ # Frozen must be compensated
+ if not _compare(profit_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_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:
- # The movement is no longer present
- for movement in decision_movement_dict[tester_key]:
-
+ 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:
+ # We must create a profit and loss movement
+ new_movement = self._newProfitAndLossMovement(prevision_movement)
+ movement_collection_diff.addNewMovement(new_movement)
More information about the Erp5-report
mailing list