From c29c9d6df298df04bb42b5302724d4442429b8a0 Mon Sep 17 00:00:00 2001
From: Nicolas Dumazet <nicolas.dumazet@nexedi.com>
Date: Tue, 29 Jun 2010 05:36:00 +0000
Subject: [PATCH] add a catalog check to bail early if catalog refers to an
 unbuilt movement Also document, rename unclear variables, and comment / XXX
 improvable areas

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@36657 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5/Document/SimulationMovement.py | 47 ++++++++++++++++++---
 1 file changed, 40 insertions(+), 7 deletions(-)

diff --git a/product/ERP5/Document/SimulationMovement.py b/product/ERP5/Document/SimulationMovement.py
index d5bd6c6d40..a100399dc2 100644
--- a/product/ERP5/Document/SimulationMovement.py
+++ b/product/ERP5/Document/SimulationMovement.py
@@ -556,21 +556,27 @@ class SimulationMovement(Movement, PropertyRecordableMixin):
       # already delivered
       return False
 
-    # might be buildable - business path depended
+    # might be buildable - business path dependent
     business_path = self.getCausalityValue(portal_type='Business Path')
     explanation_value = self.getExplanationValue()
-
     if business_path is None or explanation_value is None:
       return True
-    predecessor = business_path.getPredecessorValue()
-    if predecessor is None:
+
+    predecessor_state = business_path.getPredecessorValue()
+    if predecessor_state is None:
       # first one, can be built
       return True
 
+    # movement is not built, and corresponding business path
+    # has predecessors: check movements related to those predecessors!
+    predecessor_path_list = predecessor_state.getSuccessorRelatedValueList()
+
+    ### Step 1:
+    ## Explore ancestors in ZODB (cheap)
+
     # store a causality -> causality_related_movement_list mapping
     causality_dict = dict()
 
-    # for now only explore ancestors
     current = self.getParentValue()
     while True:
       portal_type = current.getPortalType()
@@ -581,13 +587,17 @@ class SimulationMovement(Movement, PropertyRecordableMixin):
       # XXX or maybe directly go up by two levels?
       current = current.getParentValue()
 
-    parent_path_list = predecessor.getSuccessorRelatedValueList()
     remaining_path_list = []
-    for path in parent_path_list:
+    for path in predecessor_path_list:
       related_simulation = causality_dict.get(path.getRelativeUrl())
       if related_simulation is None:
         remaining_path_list.append(path)
         continue
+      # XXX assumption is made here that if we find ONE completed ancestor
+      # movement of self that is related to a predecessor path, then
+      # that predecessor path is completed. Is it True? (aka when
+      # Business Process goes downwards, is the maximum movements per
+      # predecessor 1 or can we have more?)
       if related_simulation.getDeliveryValue() is None:
         return False # related movement is not delivered yet
       if related_simulation.getSimulationState() not in \
@@ -600,10 +610,33 @@ class SimulationMovement(Movement, PropertyRecordableMixin):
 
     # But sometimes we have to dig deeper
 
+    ### Step 2:
+    ## Try catalog to find descendant movements, knowing
+    # that it can be incomplete
+    uid_list = [p.getUid() for p in remaining_path_list]
+
+    # That list might be incomplete if catalog is lagging behind
+    portal_catalog = self.getPortalObject().portal_catalog
+    catalog_simulation_movement_list = portal_catalog(
+      portal_type='Simulation Movement',
+      causality_uid=uid_list,
+      path='%s/%%' % self.getPath())
+
+    for simulation in catalog_simulation_movement_list:
+      path = simulation.getCausalityValue()
+      if simulation.getDeliveryValue() is None or \
+          simulation.getSimulationState() not in path.getCompletedStateList():
+        return False
+
+    ### Step 3:
+    ## We had no luck, we have to explore descendant movements in ZODB
+
     # reset dict
     causality_dict = dict()
 
     # and explore descendants
+    # XXX: _recurseGetValueList fetches _all_ descendants, but we can bail
+    #      out early if one movement is not built
     for descendant in business_path._recurseGetValueList(self, 'Simulation Movement'):
       causality_dict.setdefault(descendant.getCausality(), []).append(descendant)
     for path in remaining_path_list:
-- 
2.30.9