parameters: (self, movement_collection_diff,
                                      prevision_movement, decision_movement_list)
docstring: Compares a prevision_movement to decision_movement_list which
      are part of the matching group and updates movement_collection_diff
      accordingly
  
      NOTE: this method API implicitely considers that each group of matching
      movements has 1 prevision_movement (aggregated) for N decision_movement
      It implies that prevision_movement are "more" aggregated than
      decision_movement.
  
      TODO:
         - is this assumption appropriate ?
docstring_summary: Compares a prevision_movement to decision_movement_list which
      are part of the matching group and updates movement_collection_diff
      accordingly
docstring_tokens: Compares a prevision_movement to decision_movement_list which are part of the matching group and updates movement_collection_diff accordingly
function: def _extendMovementCollectionDiff(self, movement_collection_diff,
                                      prevision_movement, decision_movement_list):
      """
      Compares a prevision_movement to decision_movement_list which
      are part of the matching group and updates movement_collection_diff
      accordingly
  
      NOTE: this method API implicitely considers that each group of matching
      movements has 1 prevision_movement (aggregated) for N decision_movement
      It implies that prevision_movement are "more" aggregated than
      decision_movement.
  
      TODO:
         - is this assumption appropriate ?
      """
      # Sample implementation - but it actually looks very generic
  
      # Case 1: movements which are not needed
      if prevision_movement is None:
        # decision_movement_list contains simulation movements which must
        # be deleted
        for decision_movement in decision_movement_list:
          # If not frozen and all children are deletable
          if decision_movement.isDeletable():
            # Delete deletable
            movement_collection_diff.addDeletableMovement(decision_movement)
            continue
          quantity = decision_movement.getQuantity()
          if quantity:
            if decision_movement.isFrozen():
              # Compensate
              new_movement = decision_movement.asContext(quantity=-quantity)
              new_movement.setDelivery(None)
              movement_collection_diff.addNewMovement(new_movement)
            else:
              movement_collection_diff.addUpdatableMovement(decision_movement,
                                                            {'quantity': 0})
        return
  
      # Case 2: movements which should be added
      elif len(decision_movement_list) == 0:
        # if decision_movement_list is empty, we can just create a new one.
        movement_collection_diff.addNewMovement(prevision_movement)
        return
  
      # Case 3: movements which are needed but may need update or
      # compensation_movement_list.
      #  let us imagine the case of a forward rule
      #  ie. what comes in must either go out or has been lost
      divergence_tester_list = self._getDivergenceTesterList()
      profit_tester_list = divergence_tester_list
      updating_tester_list = self._getUpdatingTesterList(exclude_quantity=True)
      profit_updating_tester_list = updating_tester_list
      quantity_tester_list = self._getQuantityTesterList()
      compensated_quantity = 0.0
      updatable_movement = None
      not_completed_movement = None
      updatable_compensation_movement = None
      prevision_quantity = prevision_movement.getQuantity()
      decision_quantity = 0.0
      real_quantity = 0.0
      # First, we update all properties (exc. quantity) which could be divergent
      # and if we can not, we compensate them
      for decision_movement in decision_movement_list:
        real_movement_quantity = decision_movement.getQuantity()
        if decision_movement.isPropertyRecorded('quantity'):
          decision_movement_quantity = decision_movement.getRecordedProperty('quantity')
        else:
          decision_movement_quantity = real_movement_quantity
        decision_quantity += decision_movement_quantity
        real_quantity += real_movement_quantity
        if self._isProfitAndLossMovement(decision_movement):
          if decision_movement.isFrozen():
            # Record not completed movements
            if not_completed_movement is None and not decision_movement.isCompleted():
              not_completed_movement = decision_movement
            # Frozen must be compensated
            if not _compare(profit_tester_list, prevision_movement, decision_movement):
              new_movement = decision_movement.asContext(
                                  quantity=-decision_movement_quantity)
              new_movement.setDelivery(None)
              movement_collection_diff.addNewMovement(new_movement)
              compensated_quantity += decision_movement_quantity
          else:
            updatable_compensation_movement = decision_movement
            # Not Frozen can be updated
            kw = {}
            for tester in profit_updating_tester_list:
              if not tester.compare(prevision_movement, decision_movement):
                kw.update(tester.getUpdatablePropertyDict(prevision_movement, decision_movement))
            if kw:
              movement_collection_diff.addUpdatableMovement(decision_movement, kw)
        else:
          if decision_movement.isFrozen():
            # Frozen must be compensated
            if not _compare(divergence_tester_list, prevision_movement, decision_movement):
              new_movement = decision_movement.asContext(
                                    quantity=-decision_movement_quantity)
              new_movement.setDelivery(None)
              movement_collection_diff.addNewMovement(new_movement)
              compensated_quantity += decision_movement_quantity
          else:
            updatable_movement = decision_movement
            # Not Frozen can be updated
            kw = {}
            for tester in updating_tester_list:
              if not tester.compare(prevision_movement, decision_movement):
                kw.update(tester.getUpdatablePropertyDict(prevision_movement, decision_movement))
                # XXX-JPS - there is a risk here that quantity is wrongly updated
            if kw:
              movement_collection_diff.addUpdatableMovement(decision_movement, kw)
      # Second, we calculate if the total quantity is the same on both sides
      # after compensation
      quantity_movement = prevision_movement.asContext(
                              quantity=decision_quantity-compensated_quantity)
      if not _compare(quantity_tester_list, prevision_movement, quantity_movement):
        missing_quantity = ( prevision_quantity
                             - real_quantity
                             + compensated_quantity )
        if updatable_movement is not None:
          # If an updatable movement still exists, we update it
          updatable_movement.setQuantity(
              updatable_movement.getQuantity() + missing_quantity)
          updatable_movement.clearRecordedProperty('quantity')
        elif not_completed_movement is not None:
          # It is still possible to add a new movement some movements are not
          # completed
          new_movement = prevision_movement.asContext(quantity=missing_quantity)
          new_movement.setDelivery(None)
          movement_collection_diff.addNewMovement(new_movement)
        elif updatable_compensation_movement is not None:
          # If not, it means that all movements are completed
          # but we can still update a profit and loss movement_collection_diff
          updatable_compensation_movement.setQuantity(
              updatable_compensation_movement.getQuantity() + missing_quantity)
          updatable_compensation_movement.clearRecordedProperty('quantity')
        elif missing_quantity:
          # We must create a profit and loss movement
          new_movement = self._newProfitAndLossMovement(prevision_movement)
          if new_movement is not None:
            movement_collection_diff.addNewMovement(new_movement)
function_tokens: def _extendMovementCollectionDiff ( self , movement_collection_diff , prevision_movement , decision_movement_list ) : # Sample implementation - but it actually looks very generic # Case 1: movements which are not needed if prevision_movement is None : # decision_movement_list contains simulation movements which must # be deleted for decision_movement in decision_movement_list : # If not frozen and all children are deletable if decision_movement . isDeletable ( ) : # Delete deletable movement_collection_diff . addDeletableMovement ( decision_movement ) continue quantity = decision_movement . getQuantity ( ) if quantity : if decision_movement . isFrozen ( ) : # Compensate new_movement = decision_movement . asContext ( quantity = - quantity ) new_movement . setDelivery ( None ) movement_collection_diff . addNewMovement ( new_movement ) else : movement_collection_diff . addUpdatableMovement ( decision_movement , { 'quantity' : 0 } ) return # Case 2: movements which should be added elif len ( decision_movement_list ) == 0 : # if decision_movement_list is empty, we can just create a new one. movement_collection_diff . addNewMovement ( prevision_movement ) return # Case 3: movements which are needed but may need update or # compensation_movement_list. #  let us imagine the case of a forward rule #  ie. what comes in must either go out or has been lost divergence_tester_list = self . _getDivergenceTesterList ( ) profit_tester_list = divergence_tester_list updating_tester_list = self . _getUpdatingTesterList ( exclude_quantity = True ) profit_updating_tester_list = updating_tester_list quantity_tester_list = self . _getQuantityTesterList ( ) compensated_quantity = 0.0 updatable_movement = None not_completed_movement = None updatable_compensation_movement = None prevision_quantity = prevision_movement . getQuantity ( ) decision_quantity = 0.0 real_quantity = 0.0 # First, we update all properties (exc. quantity) which could be divergent # and if we can not, we compensate them for decision_movement in decision_movement_list : real_movement_quantity = decision_movement . getQuantity ( ) if decision_movement . isPropertyRecorded ( 'quantity' ) : decision_movement_quantity = decision_movement . getRecordedProperty ( 'quantity' ) else : decision_movement_quantity = real_movement_quantity decision_quantity += decision_movement_quantity real_quantity += real_movement_quantity if self . _isProfitAndLossMovement ( decision_movement ) : if decision_movement . isFrozen ( ) : # Record not completed movements if not_completed_movement is None and not decision_movement . isCompleted ( ) : not_completed_movement = decision_movement # Frozen must be compensated if not _compare ( profit_tester_list , prevision_movement , decision_movement ) : new_movement = decision_movement . asContext ( quantity = - decision_movement_quantity ) new_movement . setDelivery ( None ) movement_collection_diff . addNewMovement ( new_movement ) compensated_quantity += decision_movement_quantity else : updatable_compensation_movement = decision_movement # Not Frozen can be updated kw = { } for tester in profit_updating_tester_list : if not tester . compare ( prevision_movement , decision_movement ) : kw . update ( tester . getUpdatablePropertyDict ( prevision_movement , decision_movement ) ) if kw : movement_collection_diff . addUpdatableMovement ( decision_movement , kw ) else : if decision_movement . isFrozen ( ) : # Frozen must be compensated if not _compare ( divergence_tester_list , prevision_movement , decision_movement ) : new_movement = decision_movement . asContext ( quantity = - decision_movement_quantity ) new_movement . setDelivery ( None ) movement_collection_diff . addNewMovement ( new_movement ) compensated_quantity += decision_movement_quantity else : updatable_movement = decision_movement # Not Frozen can be updated kw = { } for tester in updating_tester_list : if not tester . compare ( prevision_movement , decision_movement ) : kw . update ( tester . getUpdatablePropertyDict ( prevision_movement , decision_movement ) ) # XXX-JPS - there is a risk here that quantity is wrongly updated if kw : movement_collection_diff . addUpdatableMovement ( decision_movement , kw ) # Second, we calculate if the total quantity is the same on both sides # after compensation quantity_movement = prevision_movement . asContext ( quantity = decision_quantity - compensated_quantity ) if not _compare ( quantity_tester_list , prevision_movement , quantity_movement ) : missing_quantity = ( prevision_quantity - real_quantity + compensated_quantity ) if updatable_movement is not None : # If an updatable movement still exists, we update it updatable_movement . setQuantity ( updatable_movement . getQuantity ( ) + missing_quantity ) updatable_movement . clearRecordedProperty ( 'quantity' ) elif not_completed_movement is not None : # It is still possible to add a new movement some movements are not # completed new_movement = prevision_movement . asContext ( quantity = missing_quantity ) new_movement . setDelivery ( None ) movement_collection_diff . addNewMovement ( new_movement ) elif updatable_compensation_movement is not None : # If not, it means that all movements are completed # but we can still update a profit and loss movement_collection_diff updatable_compensation_movement . setQuantity ( updatable_compensation_movement . getQuantity ( ) + missing_quantity ) updatable_compensation_movement . clearRecordedProperty ( 'quantity' ) elif missing_quantity : # We must create a profit and loss movement new_movement = self . _newProfitAndLossMovement ( prevision_movement ) if new_movement is not None : movement_collection_diff . addNewMovement ( new_movement )