From 6456f669ef67cee8afa2b1809cfb157e121b17cd Mon Sep 17 00:00:00 2001
From: Romain Courteaud <romain@nexedi.com>
Date: Tue, 12 Jul 2005 12:22:11 +0000
Subject: [PATCH] Split DeliveryBuilder in order to generate "auto-planned"
 Order.

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@3449 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5/Document/DeliveryBuilder.py | 373 +----------------------
 1 file changed, 6 insertions(+), 367 deletions(-)

diff --git a/product/ERP5/Document/DeliveryBuilder.py b/product/ERP5/Document/DeliveryBuilder.py
index 1deb7075ac..4cd4f0f920 100755
--- a/product/ERP5/Document/DeliveryBuilder.py
+++ b/product/ERP5/Document/DeliveryBuilder.py
@@ -29,16 +29,14 @@
 from AccessControl import ClassSecurityInfo
 from Products.CMFCore.utils import getToolByName
 from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
-from Products.ERP5Type.XMLObject import XMLObject
-from Products.ERP5.Document.Predicate import Predicate
-from Products.ERP5.Document.Amount import Amount
 from Acquisition import aq_base, aq_parent, aq_inner, aq_acquire
 from Products.ERP5 import MovementGroup
 from Products.ERP5Type.Utils import convertToUpperCase
+from Products.ERP5.Document.OrderBuilder import OrderBuilder
 
 from zLOG import LOG
 
-class DeliveryBuilder(XMLObject, Amount, Predicate):
+class DeliveryBuilder(OrderBuilder):
   """
     Delivery Builder objects allow to gather multiple Simulation Movements
     into a single Delivery. 
@@ -95,349 +93,6 @@ class DeliveryBuilder(XMLObject, Amount, Predicate):
                     , PropertySheet.DeliveryBuilder
                     )
 
-  security.declareProtected(Permissions.ModifyPortalContent, 'build')
-  def build(self, applied_rule_uid=None, movement_relative_url_list=[],
-            delivery_relative_url_list=[]):
-    """
-      Build deliveries from a list of movements
-
-      Delivery Builders can also be provided with optional parameters to
-      restrict selection to a given root Applied Rule caused by a single Order
-      or to Simulation Movements related to a limited set of existing
-    """
-    # Select
-    if movement_relative_url_list == []:
-      movement_list = self.searchMovementList(
-                                      applied_rule_uid=applied_rule_uid)
-    else:
-      movement_list = [self.restrictedTraverse(relative_url) for relative_url\
-                       in movement_relative_url_list]
-    # Collect
-    root_group = self.collectMovement(movement_list)
-    # Build
-    delivery_list = self.buildDeliveryList(
-                       root_group,
-                       delivery_relative_url_list=delivery_relative_url_list)
-    # Call script on each delivery built
-    delivery_after_generation_script_id =\
-                              self.getDeliveryAfterGenerationScriptId()
-    if delivery_after_generation_script_id not in ["", None]:
-      for delivery in delivery_list:
-        getattr(delivery, delivery_after_generation_script_id)()
-    return delivery_list
-
-  def searchMovementList(self, applied_rule_uid=None):
-    """
-      defines how to query all Simulation Movements which meet certain criteria
-      (including the above path path definition).
-
-      First, select movement matching to criteria define on DeliveryBuilder
-      Then, call script simulation_select_method to restrict movement_list
-    """
-    movement_list = []
-    kw = {}
-    # We only search Simulation Movement
-    kw['portal_type'] = 'Simulation Movement'
-    # Search only child movement from this applied rule
-    if applied_rule_uid is not None:
-      kw['parent_uid'] = applied_rule_uid
-    # XXX Add profile query
-    # Add resource query
-    if self.resource_portal_type not in ('', None):
-      kw['resourceType'] = self.resource_portal_type
-
-    if self.simulation_select_method_id in ['', None]:
-      kw.update(self.portal_catalog.buildSQLQuery(**kw))
-      movement_list = [x.getObject() for x in self.portal_catalog(**kw)]
-    else:
-      select_method = getattr(self, self.simulation_select_method_id)
-      movement_list = select_method(**kw)
-
-    # XXX Use buildSQLQuery will be better
-    movement_list = filter(lambda x: x.getDeliveryRelatedValueList()==[],
-                           movement_list)
-
-    # XXX  Add predicate test
-    return movement_list
-
-  def getCollectOrderList(self):
-    """
-      Simply method to get the 3 collect order lists define on a
-      DeliveryBuilder
-    """
-    return self.getDeliveryCollectOrderList()+\
-           self.getDeliveryLineCollectOrderList()+\
-           self.getDeliveryCellCollectOrderList()
-
-  def collectMovement(self, movement_list):
-    """
-      group movements in the way we want. Thanks to this method, we are able 
-      to retrieve movement classed by order, resource, criterion,....
-
-      movement_list : the list of movement wich we want to group
-
-      check_list : the list of classes used to group movements. The order
-                   of the list is important and determines by what we will
-                   group movement first
-                   Typically, check_list is :
-                   [DateMovementGroup,PathMovementGroup,...]
-    """
-    class_list = []
-    for class_name in self.getCollectOrderList():
-      class_list.append(getattr(MovementGroup, class_name))
-
-    last_line_class_name = self.getDeliveryLineCollectOrderList()[-1]
-    separate_method_name_list = self.getDeliveryCellSeparateOrderList()
-
-    my_root_group = MovementGroup.RootMovementGroup(
-                           class_list,
-                           last_line_class_name=last_line_class_name,
-                           separate_method_name_list=separate_method_name_list)
-    for movement in movement_list:
-      my_root_group.append(movement)
-
-    return my_root_group
-
-  def testObjectProperties(self, object, property_dict):
-    """
-      Test object properties.
-    """
-    result = 1
-    for key in property_dict:
-      getter_name = 'get%s' % convertToUpperCase(key)
-      if hasattr(object, getter_name):
-        value = getattr(object, getter_name)()
-        if value != property_dict[key]:
-          result = 0
-          break
-      else:
-        result = 0
-        break
-    return result
-
-  def buildDeliveryList(self, movement_group, delivery_relative_url_list=[]):
-    """
-      Build deliveries from a list of movements
-    """
-    # Module where we can create new deliveries
-    delivery_module = getattr(self, self.getDeliveryModule())
-    
-    delivery_to_update_list = [self.restrictedTraverse(relative_url) for\
-                               relative_url in delivery_relative_url_list]
-    # Deliveries we are trying to update
-    delivery_select_method_id = self.getDeliverySelectMethodId()
-    if delivery_select_method_id not in ["", None]:
-      movement_list = []
-      for movement in movement_group.getMovementList() :
-        if movement.__class__.__name__ == "FakeMovement":
-          movement_list += movement.getMovementList()
-	else:
-	  movement_list += [movement]
-      to_update_delivery_sql_list = getattr(self, delivery_select_method_id)\
-                                      (movement_list=movement_list)
-      delivery_to_update_list.extend([x.getObject() for x\
-                                     in to_update_delivery_sql_list])
-
-    delivery_list = self._deliveryGroupProcessing(
-                          delivery_module,
-                          movement_group,
-                          self.getDeliveryCollectOrderList(),
-                          {},
-                          delivery_to_update_list=delivery_to_update_list)
-
-    return delivery_list
-
-  def _deliveryGroupProcessing(self, delivery_module, movement_group, 
-                               collect_order_list, property_dict,
-                               delivery_to_update_list=[]):
-    """
-      Build empty delivery from a list of movement
-    """
-    delivery_list = []
-
-    # Get current properties from current movement group
-    # And fill property_dict
-    property_dict.update(movement_group.getGroupEditDict())
-
-    if collect_order_list != []:
-      # Get sorted movement for each delivery
-      for group in movement_group.getGroupList():
-        new_delivery_list = self._deliveryGroupProcessing(
-                              delivery_module,
-                              group,
-                              collect_order_list[1:],
-                              property_dict.copy(),
-                              delivery_to_update_list=delivery_to_update_list)
-
-        delivery_list.extend(new_delivery_list)
-
-    else:
-      # Test if we can update a existing delivery, or if we need to create 
-      # a new one
-      delivery = None
-      for delivery_to_update in delivery_to_update_list:
-        if self.testObjectProperties(delivery_to_update, property_dict):
-          # Check if delivery has the correct portal_type
-          if delivery_to_update.getPortalType() ==\
-                                        self.getDeliveryPortalType():
-            delivery = delivery_to_update
-            break
-
-      if delivery == None:
-        # Create delivery
-        new_delivery_id = str(delivery_module.generateNewId())
-        delivery = delivery_module.newContent(
-                                  portal_type=self.getDeliveryPortalType(),
-                                  id=new_delivery_id)
-        # Put properties on delivery
-        delivery.edit(**property_dict)
-
-      # Then, create delivery line
-      for group in movement_group.getGroupList():
-        self._deliveryLineGroupProcessing(
-                                delivery,
-                                group,
-                                self.getDeliveryLineCollectOrderList()[1:],
-                                {})
-
-      delivery_list.append(delivery)
-
-    return delivery_list
-      
-  def _deliveryLineGroupProcessing(self, delivery, movement_group,
-                                   collect_order_list, property_dict):
-    """
-      Build delivery line from a list of movement on a delivery
-    """
-    # Get current properties from current movement group
-    # And fill property_dict
-    property_dict.update(movement_group.getGroupEditDict())
-
-    if collect_order_list != []:
-      # Get sorted movement for each delivery line
-      for group in movement_group.getGroupList():
-        self._deliveryLineGroupProcessing(
-          delivery, group, collect_order_list[1:], property_dict.copy())
-    else:
-      # Test if we can update an existing line, or if we need to create a new
-      # one
-      delivery_line = None
-      update_existing_line=0
-      for delivery_line_to_update in delivery.contentValues(
-               filter={'portal_type':self.getDeliveryLinePortalType()}):
-        if self.testObjectProperties(delivery_line_to_update, property_dict):
-          delivery_line = delivery_line_to_update
-          update_existing_line=1
-          break
-
-      if delivery_line == None:
-        # Create delivery line
-        new_delivery_line_id = str(delivery.generateNewId())
-        delivery_line = delivery.newContent(
-                                  portal_type=self.getDeliveryLinePortalType(),
-                                  id=new_delivery_line_id,
-                                  variation_category_list=[])
-        # Put properties on delivery line
-        delivery_line.edit(**property_dict)
-
-      # Update variation category list on line
-      line_variation_category_list = delivery_line.getVariationCategoryList()
-      for movement in movement_group.getMovementList():
-        line_variation_category_list.extend(
-                                      movement.getVariationCategoryList())
-      # erase double
-      line_variation_category_list = dict([(x, 1) for x in\
-                                          line_variation_category_list]).keys()
-      delivery_line.setVariationCategoryList(line_variation_category_list)
-
-      # Then, create delivery movement (delivery cell or complete delivery
-      # line)
-      for group in movement_group.getGroupList():
-        self._deliveryCellGroupProcessing(
-                                    delivery_line,
-                                    group,
-                                    self.getDeliveryCellCollectOrderList()[1:],
-                                    {},
-                                    update_existing_line=update_existing_line)
-
-  def _deliveryCellGroupProcessing(self, delivery_line, movement_group,
-                                   collect_order_list, property_dict,
-                                   update_existing_line=0):
-    """
-      Build delivery cell from a list of movement on a delivery line
-      or complete delivery line
-    """
-    # Get current properties from current movement group
-    # And fill property_dict
-    property_dict.update(movement_group.getGroupEditDict())
-
-    if collect_order_list != []:
-      # Get sorted movement for each delivery line
-      for group in movement_group.getGroupList():
-        self._deliveryCellGroupProcessing(
-                                    delivery_line, 
-                                    group, 
-                                    collect_order_list[1:], 
-                                    property_dict.copy(),
-                                    update_existing_line=update_existing_line)
-    else:
-      movement_list = movement_group.getMovementList()
-      if len(movement_list) != 1:
-        raise "CollectError", "DeliveryBuilder: %s unable to distinct those\
-              movements: %s" % (self.getId(), str(movement_list))
-      else:
-        # XXX Hardcoded value
-        base_id = 'movement'
-        object_to_update = None
-        # We need to initialize the cell
-        update_existing_movement=0
-        movement = movement_list[0]
-        # decide if we create a cell or if we update the line
-        # Decision can only be made with line matrix range:
-        # because matrix range can be empty even if line variation category
-        # list is not empty
-        if list(delivery_line.getCellKeyList(base_id=base_id)) == []:
-          # update line
-          object_to_update = delivery_line
-          if self.testObjectProperties(delivery_line, property_dict):
-            if update_existing_line == 1:
-              # We update a initialized line
-              update_existing_movement=1
-
-        else:
-          for cell_key in delivery_line.getCellKeyList(base_id=base_id):
-            if delivery_line.hasCell(base_id=base_id, *cell_key):
-              cell = delivery_line.getCell(base_id=base_id, *cell_key)
-              if self.testObjectProperties(cell, property_dict):
-                # We update a existing cell
-                # delivery_ratio of new related movement to this cell 
-                # must be updated to 0.
-                update_existing_movement=1
-                object_to_update = cell
-                break
-
-        if object_to_update is None:
-          # create a new cell
-          cell_key = movement.getVariationCategoryList()
-          if not delivery_line.hasCell(base_id=base_id, *cell_key):
-            cell = delivery_line.newCell(base_id=base_id,\
-                       portal_type=self.getDeliveryCellPortalType(), *cell_key)
-            cell._edit(category_list=cell_key,
-                      # XXX hardcoded value
-                      mapped_value_property_list=['quantity', 'price'],
-                      membership_criterion_category_list=cell_key,
-                      membership_criterion_base_category_list=movement.\
-                                             getVariationBaseCategoryList())
-            object_to_update = cell
-
-          else:
-            raise 'MatrixError', 'Cell: %s already exists on %s' %\
-                  (str(cell_key), str(delivery_line))
-                
-        self._setDeliveryMovementProperties(
-                            object_to_update, movement, property_dict,
-                            update_existing_movement=update_existing_movement)
-
   def _setDeliveryMovementProperties(self, delivery_movement,
                                      simulation_movement, property_dict,
                                      update_existing_movement=0):
@@ -447,26 +102,10 @@ class DeliveryBuilder(XMLObject, Amount, Predicate):
       Create the relation between simulation movement
       and delivery movement.
     """
-    if update_existing_movement == 1:
-      # Important.
-      # Attributes of object_to_update must not be modified here.
-      # Because we can not change values that user modified.
-      # Delivery will probably diverge now, but this is not the job of
-      # DeliveryBuilder to resolve such problem.
-      # Use Solver instead.
-      #simulation_movement.setDeliveryRatio(0)
-      simulation_movement.edit(delivery_ratio=0)
-    else:
-      # Now, only 1 movement is possible, so copy from this movement
-      # XXX hardcoded value
-      property_dict['quantity'] = simulation_movement.getQuantity()
-      property_dict['price'] = simulation_movement.getPrice()
-                
-      # Update properties on object (quantity, price...)
-      delivery_movement.edit(**property_dict)
-      #simulation_movement.setDeliveryRatio(1)
-      simulation_movement.edit(delivery_ratio=1)
-
+    OrderBuilder._setDeliveryMovementProperties(
+                            self, delivery_movement, 
+                            simulation_movement, property_dict,
+                            update_existing_movement=update_existing_movement)
     # Update simulation movement
     #simulation_movement.setDeliveryValue(delivery_movement)
     simulation_movement.edit(delivery_value=delivery_movement)
-- 
2.30.9