diff --git a/product/ERP5/Document/SimulatedDeliveryBuilder.py b/product/ERP5/Document/SimulatedDeliveryBuilder.py
index 942c81f52bf77ac8a377df5d316c6bf2e05c5037..0aae5d24a8c28ff9c9e13910cb8c65ea4b4214b9 100644
--- a/product/ERP5/Document/SimulatedDeliveryBuilder.py
+++ b/product/ERP5/Document/SimulatedDeliveryBuilder.py
@@ -98,7 +98,7 @@ class SimulatedDeliveryBuilder(BuilderMixin):
     pass
 
   @UnrestrictedMethod
-  def searchMovementList(self, applied_rule_uid=None, business_link_list=None, **kw):
+  def searchMovementList(self, applied_rule_uid=None, **kw):
     """
       defines how to query all Simulation Movements which meet certain criteria
       (including the above path path definition).
@@ -112,24 +112,18 @@ class SimulatedDeliveryBuilder(BuilderMixin):
     # Search only child movement from this applied rule
     if applied_rule_uid is not None:
       kw['parent_uid'] = applied_rule_uid
-    if business_link_list is None:
-      business_link_list = []
     # XXX Add profile query
     # Add resource query
     if self.getResourcePortalType() not in ('', None):
       kw['resourceType'] = self.getResourcePortalType()
     if self.getSimulationSelectMethodId() in ['', None]:
-      prefilter_movement_list = [x.getObject() for x in self.portal_catalog(**kw)]
+      movement_list = [x.getObject() for x in self.portal_catalog(**kw)]
     else:
       select_method = getattr(self.getPortalObject(), self.getSimulationSelectMethodId())
-      prefilter_movement_list = select_method(**kw)
+      movement_list = select_method(**kw)
     # XXX Use buildSQLQuery will be better
-    movement_list = []
-    for movement in prefilter_movement_list:
-      for business_link in business_link_list:
-        if movement.isBuildable(business_link):
-          if movement.getDeliveryValueList()==[]:
-            movement_list.append(movement)
+    movement_list = [x for x in movement_list if \
+                     x.getDeliveryValueList()==[] and x.isBuildable()]
     # XXX  Add predicate test
     # XXX FIXME Check that there is no double in the list
     # Because we can't trust simulation_select_method
diff --git a/product/ERP5/Document/SimulationMovement.py b/product/ERP5/Document/SimulationMovement.py
index a2c0f0815fb7075255f35c711e02e76da7b4a74a..82a5a0ef0b510788e10f0b8ab582ef93872a14b1 100644
--- a/product/ERP5/Document/SimulationMovement.py
+++ b/product/ERP5/Document/SimulationMovement.py
@@ -615,9 +615,19 @@ class SimulationMovement(PropertyRecordableMixin, Movement, ExplainableMixin):
     else:
       return getTreeDelivered(self, ignore_first=ignore_first)
 
+  def _getBusinessLinkByPredicate(self):
+    business_link_list = self.asComposedDocument().getBusinessLinkValueList(
+      context=self)
+    if len(business_link_list) > 1:
+      raise ValueError('Simulation Movement %s composed document matched more '
+        'than one Business Link.' % self.getPath())
+    if len(business_link_list) == 0:
+      return None
+    return business_link_list[0]
+
   security.declareProtected(Permissions.AccessContentsInformation,
                             'isBuildable')
-  def isBuildable(self, business_link):
+  def isBuildable(self):
     """Simulation Movement buildable logic"""
     if self.getDelivery():
       # already delivered
@@ -625,25 +635,27 @@ class SimulationMovement(PropertyRecordableMixin, Movement, ExplainableMixin):
 
     # might be buildable - business path dependent
     explanation_value = self.getExplanationValue()
-    if business_link is None or explanation_value is None:
-      # without Business Link and explanation it is impossible to check
-      # precisely if movement is buildable
-      # Note: It breaks compatilibity with old simulation code which did not
-      #       use Business Process at all, here assumption is inverted
-      return False
+    if explanation_value is None:
+      # Without explanation movements are buildable
+      return True
 
     ## XXX Code below following line has been moved to BusinessPath (cf r37116)
     #return len(business_path.filterBuildableMovementList([self])) == 1
 
-    predecessor_state = business_link.getPredecessor()
+    business_link_value = self.getCausalityValue(portal_type='Business Link')
+    if business_link_value is None:
+      business_link_value = self._getBusinessLinkByPredicate()
+    if business_link_value is None:
+      # No directly on indirectly set Business Link -- old simulation movement
+      return True
+    predecessor_state = business_link_value.getPredecessor()
     if predecessor_state is None:
       # first one, can be built
       return True # XXX-JPS wrong cause root is marked
 
     # movement is not built, and corresponding business path
     # has predecessors: check movements related to those predecessors!
-    composed_document = self.asComposedDocument()
-    predecessor_link_list = composed_document.getBusinessLinkValueList(
+    predecessor_link_list = self.asComposedDocument().getBusinessLinkValueList(
             successor=predecessor_state)
 
     def isBuiltAndCompleted(simulation, path):
@@ -657,20 +669,14 @@ class SimulationMovement(PropertyRecordableMixin, Movement, ExplainableMixin):
     causality_dict = {}
     current = self.getParentValue().getParentValue()
     while current.getPortalType() == "Simulation Movement":
-      business_link = current.getCausality(portal_type='Business Link')
-      if business_link is None:
-        # no direct business link set on movement, use predicate to find one
-        business_link_list = composed_document.getBusinessLinkValueList(
-          context=current)
-        if len(business_link_list) > 1:
-          raise ValueError('Business Link predicate matches too many '
-              'movements.')
-        if len(business_link_list) == 0:
-          raise ValueError('Simulation Movement has no Business Link related '
-            'and no Business Link from current Business Process matches')
-        business_link = business_link_list[0].getRelativeUrl()
-
-      causality_dict[business_link] = current
+      current_business_link = current.getCausality(portal_type='Business Link')
+      if current_business_link is None:
+        current_business_link_value = current._getBusinessLinkByPredicate()
+        if current_business_link_value is None:
+          raise ValueError('Simulation Movement %s has no Business Link '
+            'related and no Business Link from composed document matches'%
+              current.getPath())
+      causality_dict[current_business_link_value.getRelativeUrl()] = current
       current = current.getParentValue().getParentValue()
 
     remaining_path_set = set()
diff --git a/product/ERP5/mixin/builder.py b/product/ERP5/mixin/builder.py
index ac818183b0275e85d1753dca5363485a5cc3a0b0..54c226cd1b7b0851c7b016393768be89af8cd63c 100644
--- a/product/ERP5/mixin/builder.py
+++ b/product/ERP5/mixin/builder.py
@@ -126,7 +126,6 @@ class BuilderMixin(XMLObject, Amount, Predicate):
           kw['path'] = explanation_cache.getSimulationPathPatternList()
         if business_link is not None:
           kw['causality_uid'] = business_link.getUid()
-          business_link_value_list = [business_link]
         elif kw.get('causality_uid') is None:
           business_link_value_list = self.getRelatedBusinessLinkValueList()
           if len(business_link_value_list) > 0:
@@ -135,7 +134,6 @@ class BuilderMixin(XMLObject, Amount, Predicate):
         movement_list = self.searchMovementList(
           delivery_relative_url_list=delivery_relative_url_list,
           applied_rule_uid=applied_rule_uid,
-          business_link_list=business_link_value_list,
           **kw)
         if not movement_list:
           return []
diff --git a/product/ERP5/mixin/movement_collection_updater.py b/product/ERP5/mixin/movement_collection_updater.py
index 4264108c47a4bbe8feb466ed185f7d7a5a42347e..7c316b677fd92bd22736af495ce1a1cd4865d2bc 100644
--- a/product/ERP5/mixin/movement_collection_updater.py
+++ b/product/ERP5/mixin/movement_collection_updater.py
@@ -189,8 +189,7 @@ class MovementCollectionUpdaterMixin:
       modified_movement_list.append(movement)
     for movement in modified_movement_list:
       # for each touched non buildable movement reset causality_list
-      if not movement.isBuildable(movement.getCausalityValue(
-          portal_type='Business Link')):
+      if not movement.isBuildable():
         movement.setCausalityList([q.getRelativeUrl() for q in \
           movement.getCausalityValueList() if q.getPortalType() \
             not in self.getPortalBusinessLinkTypeList()])
diff --git a/product/ERP5/tests/testTradeModelLine.py b/product/ERP5/tests/testTradeModelLine.py
index b1acc8d6279c43caa91b548464f6a46d5ca7c6b5..8108003bd362bb91c7d81215b77f5eac699ba3b4 100644
--- a/product/ERP5/tests/testTradeModelLine.py
+++ b/product/ERP5/tests/testTradeModelLine.py
@@ -437,7 +437,7 @@ class TestTradeModelLine(TestTradeModelLineMixin):
           sm = result_dict.pop(use)
           business_link_list = sm.asComposedDocument().getBusinessLinkValueList(context=sm)
           self.assertEqual(len(business_link_list), 1)
-          is_buildable = sm.isBuildable(business_link_list[0])
+          is_buildable = sm.isBuildable()
           self.assertEqual(str(sm.getTotalPrice() or 0.0), str(total_price))
           if is_buildable:
             self.assertEqual(3, len(sm.getCausalityValueList()))