Commit c9662b1e authored by Alexandre Boeglin's avatar Alexandre Boeglin

* _isTreeDelivered method moved from Rule to AppliedRule and

  SimulationMovement
* added transactional variable based cache to _isTreeDelivered
* activate and flush _isTreeDelivered cache in SimulationMovement.expand and
  AppliedRule.expand (so that it's only active when calling expand, and
  flushed afterwards, to prevent issues if we call expand in the same
  transaction as a builder)
* modified SimulationMovement.expand behaviour, so that it removes applied
  rules that no longer test(), and add new ones, if no movment in the applied
  rule sub tree is linked to a delivery


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@17756 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 20d7370b
......@@ -31,9 +31,13 @@ from Products.CMFCore.utils import getToolByName
from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5Type.PsycoWrapper import psyco
from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
from zLOG import LOG
TREE_DELIVERED_CACHE_KEY = 'AppliedRule._isTreeDelivered_cache'
TREE_DELIVERED_CACHE_ENABLED = 'TREE_DELIVERED_CACHE_ENABLED'
class AppliedRule(XMLObject):
"""
An applied rule holds a list of simulation movements.
......@@ -89,6 +93,14 @@ class AppliedRule(XMLObject):
An applied rule can be expanded only if its parent movement
is expanded.
"""
tv = getTransactionalVariable(self)
cache = tv.setdefault(TREE_DELIVERED_CACHE_KEY, {})
cache_enabled = cache.get(TREE_DELIVERED_CACHE_ENABLED, 0)
# enable cache
if not cache_enabled:
cache[TREE_DELIVERED_CACHE_ENABLED] = 1
rule = self.getSpecialiseValue()
if rule is not None:
if self.isRootAppliedRule():
......@@ -102,6 +114,13 @@ class AppliedRule(XMLObject):
# "recursiveImmediateReindexObject"]).\
# notifySimulationChange(rule._v_notify_dict)
# disable and clear cache
if not cache_enabled:
try:
del tv[TREE_DELIVERED_CACHE_KEY]
except KeyError:
pass
security.declareProtected(Permissions.ModifyPortalContent, 'solve')
def solve(self, solution_list):
"""
......@@ -193,3 +212,33 @@ class AppliedRule(XMLObject):
delivery_url)
else:
delivery_value.notifySimulationChange()
def _isTreeDelivered(self):
"""
Checks if submovements of this applied rule (going down the complete
simulation tree) have a delivery relation.
Returns True if at least one is delivered, False if none of them are.
see SimulationMovement._isTreeDelivered
"""
tv = getTransactionalVariable(self)
cache = tv.setdefault(TREE_DELIVERED_CACHE_KEY, {})
cache_enabled = cache.get(TREE_DELIVERED_CACHE_ENABLED, 0)
def getTreeDelivered(applied_rule):
for movement in applied_rule.objectValues():
if movement._isTreeDelivered():
return True
return False
rule_key = self.getRelativeUrl()
if cache_enabled:
try:
return cache[rule_key]
except:
result = getTreeDelivered(self)
cache[rule_key] = result
return result
else:
return getTreeDelivered(self)
......@@ -85,7 +85,7 @@ class DeliveryRule(Rule):
(delivery.getPortalDraftOrderStateList() + \
delivery.getPortalPlannedOrderStateList()):
movement_delivery = movement.getDeliveryValue()
if not self._isTreeDelivered([movement], ignore_first=1) and \
if not movement._isTreeDelivered(ignore_first=1) and \
movement_delivery not in delivery_movement_list:
applied_rule._delObject(movement.getId())
else:
......@@ -103,7 +103,7 @@ class DeliveryRule(Rule):
# We are on a line
new_id = deliv_mvt.getId()
else:
# Weare on a cell
# We are on a cell
new_id = "%s_%s" % (deliv_mvt.getParentId(), deliv_mvt.getId())
# Generate the simulation deliv_mvt
# XXX Hardcoded value
......
......@@ -86,7 +86,7 @@ class OrderRule(DeliveryRule):
order.getPortalReservedInventoryStateList() and
not movement.getLastExpandSimulationState() in
order.getPortalCurrentInventoryStateList()) and \
not self._isTreeDelivered([movement]):
not movement._isTreeDelivered():
movement_order = movement.getOrderValue()
if movement_order in order_movement_list:
......
......@@ -220,21 +220,6 @@ class Rule(Predicate, XMLObject):
return 1
#### Helpers
def _isTreeDelivered(self, movement_list, ignore_first=0):
"""
returns 1 if the movement or any of its child is linked to a delivery
"""
child_movement_list = []
for movement in movement_list:
if not ignore_first and len(movement.getDeliveryList()) > 0:
return 1
else:
for applied_rule in movement.objectValues():
child_movement_list = applied_rule.objectValues()
if len(child_movement_list) == 0:
return 0
return self._isTreeDelivered(child_movement_list)
def _getCurrentMovementList(self, applied_rule, **kw):
"""
Returns the list of current children of the applied rule, sorted in 3
......@@ -263,7 +248,7 @@ class Rule(Predicate, XMLObject):
if movement.isFrozen():
immutable_movement_list.append(movement)
else:
if self._isTreeDelivered([movement]):
if movement._isTreeDelivered():
mutable_movement_list.append(movement)
else:
deletable_movement_list.append(movement)
......
......@@ -31,6 +31,7 @@ from AccessControl import ClassSecurityInfo
from Products.CMFCore.utils import getToolByName
from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
from Products.ERP5.Document.Movement import Movement
......@@ -38,6 +39,8 @@ from zLOG import LOG
from Acquisition import aq_base
from AppliedRule import TREE_DELIVERED_CACHE_KEY, TREE_DELIVERED_CACHE_ENABLED
# XXX Do we need to create groups ? (ie. confirm group include confirmed, getting_ready and ready
parent_to_movement_simulation_state = {
......@@ -209,36 +212,57 @@ class SimulationMovement(Movement):
security.declareProtected(Permissions.ModifyPortalContent, 'expand')
def expand(self, force=0, **kw):
"""
Parses all existing applied rules and make sure they apply.
Checks other possible rules and starts expansion process
(instanciates rule and calls expand on rule)
Only movements which applied rule parent is expanded can
be expanded.
"""
# XXX Default behaviour is not to expand if it has already been
# expanded, but some rules are configuration rules and need to be
# reexpanded each time, because the rule apply only if predicates
# are true, then this kind of rule must always be tested. Currently,
# we know that invoicing rule acts like this, and that it comes after
# invoice or invoicing_rule, so we if we come from invoince rule or
# invoicing rule, we always expand regardless of the causality state.
if ((self.getParentValue().getSpecialiseReference() not in
('default_invoicing_rule', 'default_invoice_rule')
and self.getCausalityState() == 'expanded' ) or \
len(self.objectIds()) != 0):
# Reexpand
for my_applied_rule in self.objectValues():
my_applied_rule.expand(force=force,**kw)
else:
portal_rules = getToolByName(self, 'portal_rules')
# Parse each rule and test if it applies
for rule in portal_rules.searchRuleList(self):
rule.constructNewAppliedRule(self, **kw)
for my_applied_rule in self.objectValues() :
my_applied_rule.expand(force=force,**kw)
# Set to expanded
self.setCausalityState('expanded')
Checks all existing applied rules and make sure they still apply.
Checks for other possible rules and starts expansion process (instanciates
applied rules and calls expand on them).
First get all applicable rules,
then, delete all applied rules that no longer match and are not linked to
a delivery,
finally, apply new rules if no rule with the same type is already applied.
"""
portal_rules = getToolByName(self, 'portal_rules')
tv = getTransactionalVariable(self)
cache = tv.setdefault(TREE_DELIVERED_CACHE_KEY, {})
cache_enabled = cache.get(TREE_DELIVERED_CACHE_ENABLED, 0)
# enable cache
if not cache_enabled:
cache[TREE_DELIVERED_CACHE_ENABLED] = 1
applied_rule_dict = {}
applicable_rule_dict = {}
for rule in portal_rules.searchRuleList(self, sort_on='version',
sort_order='descending'):
ref = rule.getReference()
if ref and ref not in applicable_rule_dict.iterkeys():
applicable_rule_dict[ref] = rule
for applied_rule in self.objectValues():
rule = applied_rule.getSpecialiseValue()
if not applied_rule._isTreeDelivered() and not rule.test(self):
self._delObject(applied_rule.getId())
else:
applied_rule_dict[rule.getPortalType()] = applied_rule
for rule in applicable_rule_dict.itervalues():
rule_type = rule.getPortalType()
if rule_type not in applied_rule_dict.iterkeys():
applied_rule = rule.constructNewAppliedRule(self, **kw)
applied_rule_dict[rule_type] = applied_rule
self.setCausalityState('expanded')
# expand
for applied_rule in applied_rule_dict.itervalues():
applied_rule.expand(force=force, **kw)
# disable and clear cache
if not cache_enabled:
try:
del tv[TREE_DELIVERED_CACHE_KEY]
except KeyError:
pass
security.declareProtected(Permissions.ModifyPortalContent, 'diverge')
def diverge(self):
......@@ -480,3 +504,38 @@ class SimulationMovement(Movement):
# 'recursiveImmediateReindexObject']))
# activity.edit()
def _isTreeDelivered(self, ignore_first=0):
"""
checks if subapplied rules of this movement (going down the complete
simulation tree) have a child with a delivery relation.
Returns True if at least one is delivered, False if none of them are.
see AppliedRule._isTreeDelivered
"""
tv = getTransactionalVariable(self)
cache = tv.setdefault(TREE_DELIVERED_CACHE_KEY, {})
cache_enabled = cache.get(TREE_DELIVERED_CACHE_ENABLED, 0)
def getTreeDelivered(movement, ignore_first=0):
if ignore_first:
if len(movement.getDeliveryList()) > 0:
return True
for applied_rule in movement.objectValues():
if applied_rule._isTreeDelivered():
return True
return False
if ignore_first:
rule_key = (self.getRelativeUrl(), 1)
else:
rule_key = self.getRelativeUrl()
if cache_enabled:
try:
return cache[rule_key]
except:
result = getTreeDelivered(self, ignore_first=ignore_first)
cache[rule_key] = result
return result
else:
return getTreeDelivered(self, ignore_first=ignore_first)
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