From 356bfc19327faffdd62e69849d7d58af57bc6fd1 Mon Sep 17 00:00:00 2001
From: Romain Courteaud <romain@nexedi.com>
Date: Wed, 26 Oct 2005 15:02:27 +0000
Subject: [PATCH] Add a new functionality to the configuration of the
 SupplyChain. Now, if a resource is associated to a Supply Link representing a
 production, no sourcing will be generated for this resource.

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@4135 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5/Document/SupplyChain.py          | 51 ++++++++++++-
 product/ERP5/Document/SupplyLink.py           | 75 ++++++++++++-------
 product/ERP5/Document/TransformationRule.py   | 17 +----
 .../Document/TransformationSourcingRule.py    | 23 ++++++
 4 files changed, 121 insertions(+), 45 deletions(-)

diff --git a/product/ERP5/Document/SupplyChain.py b/product/ERP5/Document/SupplyChain.py
index 1f4ea508ef..504936db8a 100755
--- a/product/ERP5/Document/SupplyChain.py
+++ b/product/ERP5/Document/SupplyChain.py
@@ -88,6 +88,55 @@ class SupplyChain(Path, XMLObject):
             str(self.getRelativeUrl())
     return result
 
+  security.declareProtected(Permissions.View,
+                            'getNextSupplyLinkList')
+  def getNextSupplyLinkList(self, current_supply_link):
+    """
+      Return the previous SupplyLink  list.
+    """
+    supply_link_list = self.objectValues(
+                                 portal_type=self.supply_link_portal_type)
+    # Search next link
+    next_node_value = current_supply_link.getNextNodeValue()
+    next_supply_link_list = [x for x in supply_link_list if \
+                             x.getCurrentNodeValue() == next_node_value]
+    # Prevent infinite loop
+    if current_supply_link in next_supply_link_list:
+      next_supply_link_list.remove(current_supply_link)
+    # Get only production node in the list, or return the entire list
+    next_production_list = [x for x in next_supply_link_list \
+                                if x.isProductionSupplyLink()]
+    if next_production_list != []:
+      next_supply_link_list = next_production_list 
+    return next_supply_link_list
+
+  security.declareProtected(Permissions.View,
+                            'getNextProductionSupplyLinkList')
+  def getNextProductionSupplyLinkList(self, current_supply_link):
+    """
+      Return the next SupplyLink which represents a production,
+      if there is one.
+      No recursion is done.
+    """
+    next_supply_link_list = self.getNextSupplyLinkList(current_supply_link)
+    return [x for x in next_supply_link_list if x.isProductionSupplyLink()]
+    
+  security.declareProtected(Permissions.View,
+                            'getNextProductionIndustrialPhaseList')
+  def getNextProductionIndustrialPhaseList(self, current_supply_link):
+    """
+      Return all next industrial phase representing a production.
+    """
+    ind_phase_dict = {}
+    for link in self.getNextProductionSupplyLinkList(current_supply_link):
+      for ind_phase in link.getIndustrialPhaseValueList():
+        ind_phase_dict[ind_phase] = 1
+    # Remove None value, and generate the list
+    ind_phase_dict.pop(None, None)
+    return ind_phase_dict.keys()
+
+  security.declareProtected(Permissions.View,
+                            'getPreviousSupplyLinkList')
   def getPreviousSupplyLinkList(self, current_supply_link):
     """
       Return the previous SupplyLink  list.
@@ -100,7 +149,7 @@ class SupplyChain(Path, XMLObject):
       current_node_value = current_supply_link.getCurrentNodeValue()
       previous_supply_link_list = [
                                  x for x in supply_link_list if\
-                                 x.getDestinationValue() == current_node_value]
+                                 x.getNextNodeValue() == current_node_value]
       # Prevent infinite loop
       if current_supply_link in previous_supply_link_list:
         previous_supply_link_list.remove(current_supply_link)
diff --git a/product/ERP5/Document/SupplyLink.py b/product/ERP5/Document/SupplyLink.py
index a563cdc5fe..a328f3c26a 100755
--- a/product/ERP5/Document/SupplyLink.py
+++ b/product/ERP5/Document/SupplyLink.py
@@ -97,46 +97,65 @@ class SupplyLink(Path, XMLObject):
         node = self.getSourceValue()
       return node
 
+    security.declareProtected(Permissions.View, 'getNextNodeValue')
+    def getNextNodeValue(self):
+      """
+        Return the node used to find the next SupplyLink
+      """
+      return self.getDestinationValue()
+
     security.declareProtected(Permissions.View, 'test')
     def test(self, movement, concurrent_supply_link_list):
       """
         Test if the current link can expand this movement.
         Futur implementation have to return properties value
         (like quantity) calculated.
+        This method is called only on packing list supply link.
       """
-      # XXX This method has to be rewritten.
-      # Predicate must be used.
-      # Current implementation is enough now for customers.
       result = 0
-      resource = movement.getResource()
-      if resource.find('operation/') == -1:
-        # XXX reject operation
-        if concurrent_supply_link_list == []:
-          result = 1
+      # Test if the movement correspond to the resource to produced
+      ind_phase_list = movement.getIndustrialPhaseValueList()
+      if ind_phase_list != []:
+        # Is this SupplyLink in the route to the previous production node ?
+        supply_chain = self.getParent()
+        previous_ind_phase_list =\
+              supply_chain.getPreviousProductionIndustrialPhaseList(self)
+        for ind_phase in ind_phase_list:
+          if ind_phase in previous_ind_phase_list:
+            result = 1
+            break
+      else:
+        # How to delivered raw materials ?
+        # XXX This method has to be rewritten.
+        # Predicate must be used.
+        if len(concurrent_supply_link_list) > 1:
+          raise "SupplyChainError",\
+                "SupplyChain unable to find route."
         else:
-          # Test if the movement correspond to the resource to produced
-          ind_phase_list = movement.getIndustrialPhaseValueList()
-          if ind_phase_list != []:
-            # Is this SupplyLink in the route to the previous production node ?
+          # Check if raw material is create by a production link or a packing
+          # list link.
+          supply_chain = self.getParent()
+          next_industrial_phase_list = \
+              supply_chain.getNextProductionIndustrialPhaseList(self)
+          ind_phase_id_list = [x.getId() for x in next_industrial_phase_list]
+
+          # Get the transformation to use
+          applied_rule = movement.getParent()
+          rule = applied_rule.getSpecialiseValue()
+          transformation = rule.getTransformation(applied_rule)
+          # Call getAggregatedAmountList
+          amount_list = transformation.getAggregatedAmountList(
+                       movement.getParent().getParent(),
+                       ind_phase_id_list=ind_phase_id_list)
+          resource_list = [x.getResourceValue() for x in amount_list]
+          current_resource = movement.getResourceValue()
+          if current_resource not in resource_list:
+            # We can delivered this resource
             supply_chain = self.getParent()
             previous_ind_phase_list =\
                   supply_chain.getPreviousProductionIndustrialPhaseList(self)
-            for ind_phase in ind_phase_list:
-              if ind_phase in previous_ind_phase_list:
-                result = 1
-                break
-          else:
-            # How to delivered raw materials ?
-            # First dirty implementation...
-            if len(concurrent_supply_link_list) > 1:
-              raise "SupplyChainError",\
-                    "SupplyChain unable to find route."
-            else:
-              supply_chain = self.getParent()
-              previous_ind_phase_list =\
-                    supply_chain.getPreviousProductionIndustrialPhaseList(self)
-              if len(previous_ind_phase_list) == 0:
-                result = 1
+            if len(previous_ind_phase_list) == 0:
+              result = 1
       return result
 
     security.declareProtected(Permissions.View, 'getStartDate')
diff --git a/product/ERP5/Document/TransformationRule.py b/product/ERP5/Document/TransformationRule.py
index f165b6fe5b..7209613fdf 100755
--- a/product/ERP5/Document/TransformationRule.py
+++ b/product/ERP5/Document/TransformationRule.py
@@ -264,22 +264,7 @@ class TransformationRule(Rule):
       category_list = parent_movement.getVariationCategoryList(
                                   base_category_list=base_category_list)
       # Get the transformation to use
-      production_order_movement = applied_rule.getRootSimulationMovement().\
-                                                     getOrderValue()
-      # XXX Acquisition can be use instead
-      parent_uid = production_order_movement.getParent().getUid()
-      explanation_uid = production_order_movement.getExplanationUid()
-      if parent_uid == explanation_uid:
-        production_order_line = production_order_movement
-      else:
-        production_order_line = production_order_movement.getParent()
-      line_transformation = production_order_line.objectValues(
-                portal_type=self.getPortalTransformationTypeList())
-      if len(line_transformation)==1:
-        transformation = line_transformation[0]
-      else:
-        transformation = production_order_line.getSpecialiseValue(
-                           portal_type=self.getPortalTransformationTypeList())
+      transformation = self.getTransformation(applied_rule)
       # Generate the fake context 
       tmp_context = parent_movement.asContext(
                    context=parent_movement, 
diff --git a/product/ERP5/Document/TransformationSourcingRule.py b/product/ERP5/Document/TransformationSourcingRule.py
index 39bd122422..da67c65c35 100755
--- a/product/ERP5/Document/TransformationSourcingRule.py
+++ b/product/ERP5/Document/TransformationSourcingRule.py
@@ -92,6 +92,29 @@ class TransformationSourcingRuleMixin(ExtensionClass.Base):
       # Update movement properties
       movement.edit(**(movement_dict[movement_id]))
 
+  security.declareProtected(Permissions.View, 'getTransformation')
+  def getTransformation(self, applied_rule):
+    """
+    Get transformation related to used by the applied rule.
+    """
+    production_order_movement = applied_rule.getRootSimulationMovement().\
+                                                   getOrderValue()
+    # XXX Acquisition can be use instead
+    parent_uid = production_order_movement.getParent().getUid()
+    explanation_uid = production_order_movement.getExplanationUid()
+    if parent_uid == explanation_uid:
+      production_order_line = production_order_movement
+    else:
+      production_order_line = production_order_movement.getParent()
+    line_transformation = production_order_line.objectValues(
+              portal_type=self.getPortalTransformationTypeList())
+    if len(line_transformation)==1:
+      transformation = line_transformation[0]
+    else:
+      transformation = production_order_line.getSpecialiseValue(
+                         portal_type=self.getPortalTransformationTypeList())
+    return transformation
+
 class TransformationSourcingRule(Rule):
     """
       Transformation Sourcing Rule object make sure
-- 
2.30.9