##############################################################################
#
# Copyright (c) 2004, 2005 Nexedi SARL and Contributors. All Rights Reserved.
#          Sebastien Robin <seb@nexedi.com>
#          Guillaume Michon <guillaume.michon@e-asc.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################

#
# Skeleton ZopeTestCase
#

from random import randint

import transaction
import os, sys
if __name__ == '__main__':
    execfile(os.path.join(sys.path[0], 'framework.py'))

# Needed in order to have a log file inside the current folder
os.environ['EVENT_LOG_FILE'] = os.path.join(os.getcwd(), 'zLOG.log')
os.environ['EVENT_LOG_SEVERITY'] = '-300'

from Testing import ZopeTestCase
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from AccessControl.SecurityManagement import newSecurityManager, \
                                             noSecurityManager
from DateTime import DateTime
from Acquisition import aq_base, aq_inner
from zLOG import LOG
from Products.ERP5Type.DateUtils import addToDate
from Products.ERP5Type.tests.Sequence import Sequence, SequenceList
import time
import os
from Products.ERP5Type import product_path
from Products.CMFCore.utils import getToolByName
from testOrder import TestOrderMixin

class TestApparelTransformation(TestOrderMixin, ERP5TypeTestCase):
  """
    Test Transformations with erp5_apparel configuration

    This test :
      - is checking so many values
      - is specific to erp5_apparel, so does not allows to reuse it's code

    Therefore, it's better to use testTransformation for future tests
  """
  run_all_test = 1
  transformation_portal_type = 'Apparel Transformation'
  component_portal_type = 'Apparel Component'
  component_variation_portal_type = 'Apparel Component Variation'
  transformed_resource_portal_type = 'Apparel Transformation Transformed Resource'
  operation_portal_type = 'Apparel Transformation Operation'

  def getTitle(self):
    return "Transformation"

  def enableLightInstall(self):
    """
    You can override this. 
    Return if we should do a light install (1) or not (0)
    """
    return 1

  def enableActivityTool(self):
    """
    You can override this.
    Return if we should create (1) or not (0) an activity tool.
    """
    return 1
  
  def stepCreateComponentDict(self, sequence=None, sequence_list=None, \
                                 **kw):
    """
      Create a number of variated component
    """
    portal = self.getPortal()
    component_module = portal.getDefaultModule(self.component_portal_type)

    components = [
      { 'name':'zip', 'quantity': 100., 'prices':[10.] },
      { 'name':'tissu', 'quantity': 50., 'prices':[4.5, 7.] },
      { 'name':'bouton', 'quantity': 1000., 'prices':[150.] }, ]
    
    component_dict = {} 
    for component_info in components:
      component_name = component_info['name']
      component = component_module.newContent(title=component_name)
      component_dict[component_name] = component
      variation1 = component.newContent(
                          portal_type=self.component_variation_portal_type,
                          id='1')
      variation2 = component.newContent(
                          portal_type=self.component_variation_portal_type,
                          id='2')
      variations = [variation1, variation2]

      # Commit and catalog
      transaction.commit()
      self.tic()
      
      component.setVariationBaseCategoryList(['variation'])
      component.setPVariationBaseCategoryList(['variation'])
      # Variation are automatically acquired if they are individual variation.
#       component.setCategoryList( 
#                     ['variation/' + x.getRelativeUrl() for x in variations] )
      # Set the price
      supply_line = component.newContent(portal_type='Supply Line')
      supply_line.edit(mapped_value_property_list=['base_price'],
          priced_quantity=component_info['quantity'])
      component_prices = component_info['prices']
      if len(component_prices) == 1:
        supply_line.edit(
            membership_criterion_base_category = ['variation'],
            membership_criterion_category = ['variation/' + x.getRelativeUrl() for x in variations],
            base_price = component_prices[0])
      else:
#         supply_line.setVariationBaseCategoryList(['variation'])
        supply_line.updateCellRange(base_id = 'path')
        for i in range(2):
          supply_cell = supply_line.newCell(
                   'variation/apparel_component_module/%s/%d' % \
                                      (component.getId(),(i+1)), 
                   portal_type='Supply Cell',
                   base_id='path')
          supply_cell.edit(
            membership_criterion_base_category = ['variation'],
            membership_criterion_category = ['variation/' + variations[i].getRelativeUrl()],
            base_price = component_prices[i],
            mapped_value_property_list = ['base_price'],
            resource = supply_line.getResource() )
    sequence.edit(component_dict=component_dict)
  
      
  def stepCreateOperationDict(self, sequence=None, sequence_list=None, **kw):
    """
      Create a number of operations
    """
    portal = self.getPortal()
    operation_dict = {}
    for operation_name in ('piquage', 'taillage'):
      operation = portal.portal_categories.operation.newContent(id=operation_name)
      operation_dict[operation_name] = operation
    sequence.edit(operation_dict=operation_dict)
    
    
  def stepCreateTransformation(self, sequence=None, sequence_list=None, \
                                 **kw):
    """
      Create a transformation
    """
    portal = self.getPortal()
    resource = sequence.get('resource')
    
    transformation_module = portal.getDefaultModule(self.transformation_portal_type)
    transformation = transformation_module.newContent(portal_type=self.transformation_portal_type)
    sequence.edit(transformation=transformation)
    transformation.setResourceValue(resource)
    transformation.setVariationBaseCategoryList(('size','colour', 'morphology'))

    
  def stepCreateIncludedTransformation(self, sequence=None, sequence_list=None, **kw):
    """
      Create a transformation to be included into the other
    """
    portal = self.getPortal()
    resource = sequence.get('resource')
    transformation_module = portal.getDefaultModule(self.transformation_portal_type)
    transformation = transformation_module.newContent(portal_type = self.transformation_portal_type)
    sequence.edit(included_transformation = transformation)
    transformation.setResourceValue(resource)
    transformation.setVariationBaseCategoryList(('size',))

    
  def stepCreateTransformationLine(self, sequence=None, sequence_list=None, **kw):
    """
      Create transformed resources and operations for transformation
    """
    transformation = sequence.get('transformation')
    component_dict = sequence.get('component_dict')

    # Transformed Resource 1 : permanent consumption
    tr_resource_name = 'bouton'
    transformed_resource = transformation.newContent(portal_type=self.transformed_resource_portal_type)
    transformed_resource.edit(
        title = tr_resource_name,
        quantity = 2.,
        resource_value = component_dict[tr_resource_name],
        )
    transformed_resource.edit(
       categories = transformed_resource.getCategoryList() + ['variation/' + component_dict[tr_resource_name]['1'].getRelativeUrl()])

    # Transformed Resource 2 : 1 variation axis
    line_list =  [ ('size/Baby',     '1'),
                   ('size/Child/32', '2'),
                   ('size/Child/34', '1'),
                   ('size/Man',      '2'),
                   ('size/Woman',    '1') ]
    tr_resource_name = 'zip'
    transformed_resource = transformation.newContent(portal_type=self.transformed_resource_portal_type)
    transformed_resource.edit(
        title = tr_resource_name,
        quantity = 1.,
        resource_value = component_dict[tr_resource_name]
        )
    base_category_list = ['size']
    transformed_resource.setVariationBaseCategoryList(base_category_list)
    range_list = [x[0] for x in line_list]
    transformed_resource.setCellRange(range_list, base_id='variation')
    for line in line_list:
      size, variation = line
      variation = component_dict[tr_resource_name][variation]
      cell = transformed_resource.newCell(size, base_id='variation')
      cell.edit(
        membership_criterion_base_category = base_category_list,
        membership_criterion_category = [size],
        categories = ('variation/' + variation.getRelativeUrl() ) )
    self.stepTic()

    # Transformed Resource 3 : 3 variation axis
    line_list = [ ('size/Baby', 'ColourVariation1', 'MorphologyVariation1', '2', 3.),
                  ('size/Baby', 'ColourVariation1', 'MorphologyVariation2', '1', 3.5),
                  ('size/Baby', 'ColourVariation2', 'MorphologyVariation1', '1', 3.),                 
                  ('size/Baby', 'ColourVariation2', 'MorphologyVariation2', '2', 3.5),
                  ('size/Baby', 'ColourVariation3', 'MorphologyVariation1', '1', 3.5),
                  ('size/Baby', 'ColourVariation3', 'MorphologyVariation2', '1', 3.5),
                  
                  ('size/Child/32', 'ColourVariation1', 'MorphologyVariation1', '1', 6.),
                  ('size/Child/32', 'ColourVariation1', 'MorphologyVariation2', '1', 6.5),
                  ('size/Child/32', 'ColourVariation2', 'MorphologyVariation1', '1', 6.),
                  ('size/Child/32', 'ColourVariation2', 'MorphologyVariation2', '2', 6.5),
                  ('size/Child/32', 'ColourVariation3', 'MorphologyVariation1', '2', 6.),
                  ('size/Child/32', 'ColourVariation3', 'MorphologyVariation2', '1', 6.5),
                  
                  ('size/Child/34', 'ColourVariation1', 'MorphologyVariation1', '1', 9.),
                  ('size/Child/34', 'ColourVariation1', 'MorphologyVariation2', '2', 9.5),
                  ('size/Child/34', 'ColourVariation2', 'MorphologyVariation1', '2', 9.),
                  ('size/Child/34', 'ColourVariation2', 'MorphologyVariation2', '1', 9.5),
                  ('size/Child/34', 'ColourVariation3', 'MorphologyVariation1', '2', 9.),
                  ('size/Child/34', 'ColourVariation3', 'MorphologyVariation2', '2', 9.5),
                  
                  ('size/Man', 'ColourVariation1', 'MorphologyVariation1', '2', 12.),
                  ('size/Man', 'ColourVariation1', 'MorphologyVariation2', '2', 12.5),
                  ('size/Man', 'ColourVariation2', 'MorphologyVariation1', '2', 12.),
                  ('size/Man', 'ColourVariation2', 'MorphologyVariation2', '1', 12.5),
                  ('size/Man', 'ColourVariation3', 'MorphologyVariation1', '1', 12.),
                  ('size/Man', 'ColourVariation3', 'MorphologyVariation2', '2', 12.5),
                  
                  ('size/Woman', 'ColourVariation1', 'MorphologyVariation1', '2', 15.),
                  ('size/Woman', 'ColourVariation1', 'MorphologyVariation2', '1', 15.5),
                  ('size/Woman', 'ColourVariation2', 'MorphologyVariation1', '1', 15.),
                  ('size/Woman', 'ColourVariation2', 'MorphologyVariation2', '2', 15.5),
                  ('size/Woman', 'ColourVariation3', 'MorphologyVariation1', '2', 15.),
                  ('size/Woman', 'ColourVariation3', 'MorphologyVariation2', '2', 15.5),
               ]
    mapping_dict = {'ColourVariation1': 'colour/apparel_model_module/1/1',
                    'ColourVariation2': 'colour/apparel_model_module/1/2',
                    'ColourVariation3': 'colour/apparel_model_module/1/3',
                    'MorphologyVariation1': 'morphology/apparel_model_module/1/4',
                    'MorphologyVariation2': 'morphology/apparel_model_module/1/5',
                    '1': 'apparel_component_module/2/1',
                    '2': 'apparel_component_module/2/2' }
                    
               
    tr_resource_name = 'tissu'
    transformed_resource = transformation.newContent(portal_type=self.transformed_resource_portal_type)
    transformed_resource.edit(
        title = tr_resource_name,
        resource_value = component_dict[tr_resource_name]
        )
    base_category_list = ['size', 'colour', 'morphology']
    transformed_resource.setVariationBaseCategoryList(base_category_list)
    variation_range_list_list = []
    for base_category in base_category_list:
      variation_range_category_list = transformation.getVariationRangeCategoryList(base_category_list=[base_category]) 
      variation_range_list_list.append(variation_range_category_list)

    transformed_resource.setCellRange(base_id='variation', *variation_range_list_list)
    transformed_resource.setCellRange(base_id='quantity', *variation_range_list_list)
    
    # Define the cells
    for line in line_list:
      size, colour, morphology, variation, quantity = line
      colour = mapping_dict[colour]
      morphology = mapping_dict[morphology]
      variation = component_dict[tr_resource_name][variation]
      cell_variation = transformed_resource.newCell(size, colour, morphology, base_id='variation')
      cell_quantity = transformed_resource.newCell(size, colour, morphology, base_id='quantity')
      cell_variation.edit(
        membership_criterion_base_category = base_category_list,
        membership_criterion_category = [size, colour, morphology],
        categories = ('variation/' + variation.getRelativeUrl()) )
      cell_quantity.edit(
        membership_criterion_base_category = base_category_list,
        membership_criterion_category = [size, colour, morphology],
        quantity = quantity,
        mapped_value_property_list = ['quantity'] ) 
    self.stepTic()

    
  def stepCreateIncludedTransformationLine(self, sequence=None, sequence_list=None, **kw):
    """
      Create transformed resources and operations for transformation
    """
    transformation = sequence.get('included_transformation')
    operation_dict = sequence.get('operation_dict')
    component_dict = sequence.get('component_dict')

    # Operation 1 : permanent consumption
    op_name = 'taillage'
    operation = transformation.newContent(portal_type=self.operation_portal_type)
    operation.edit(
        title = op_name,
        quantity = 10.,
        categories = operation.getCategoryList() + [ 'resource/' + operation_dict[op_name].getRelativeUrl(),
                                                     'quantity_unit/time/min' ]
        )

    # Operation 2 : 1 variation axis
    line_list =  [ ('size/Baby',     2.),
                   ('size/Child/32', 2.5),
                   ('size/Child/34', 3.),
                   ('size/Man',      3.5),
                   ('size/Woman',    4.) ]
    op_name = 'piquage'
    operation = transformation.newContent(portal_type=self.operation_portal_type)
    operation.edit(
        title = op_name,
        categories = operation.getCategoryList() + [ 'resource/' + operation_dict[op_name].getRelativeUrl(),
                                                     'quantity_unit/time/min' ]
        )
    base_category_list = ['size']
    operation.setVariationBaseCategoryList(base_category_list)
    range_list = [x[0] for x in line_list]
    operation.setCellRange(range_list, base_id='quantity')
    for line in line_list:
      size, quantity = line
      cell = operation.newCell(size, base_id='quantity')
      cell.edit(
        membership_criterion_base_category = base_category_list,
        membership_criterion_category = [size],
        quantity = quantity,
        mapped_value_property_list = ['quantity'])
    self.stepTic()
    
  # Transformed Resource : 1 variation axis
    line_list =  [ ('size/Baby',     1.),
                   ('size/Child/32', 1.5),
                   ('size/Child/34', 2.),
                   ('size/Man',      3.),
                   ('size/Woman',    2.5) ]
    tr_resource_name = 'tissu'
    transformed_resource = transformation.newContent(portal_type=self.transformed_resource_portal_type)
    transformed_resource.edit(
        title = tr_resource_name,
        resource_value = component_dict[tr_resource_name],
        )
    transformed_resource.edit(
       categories = transformed_resource.getCategoryList() + ['variation/' + component_dict[tr_resource_name]['1'].getRelativeUrl()])
    base_category_list = ['size']
    transformed_resource.setVariationBaseCategoryList(base_category_list)
    range_list = [x[0] for x in line_list]
    transformed_resource.setCellRange(range_list, base_id='quantity')
    for line in line_list:
      size, quantity = line
      cell = transformed_resource.newCell(size, base_id='quantity')
      cell.edit(
        membership_criterion_base_category = base_category_list,
        membership_criterion_category = [size],
        quantity = quantity,
        mapped_value_property_list = ['quantity'])
    self.stepTic()
    
    
  def stepIncludeTransformation(self, sequence=None, sequence_list=None, **kw):
    transformation = sequence.get('transformation')
    included_transformation = sequence.get('included_transformation')
    transformation.edit(specialise_value = included_transformation)
    
    
  def stepVerifyTransformationAggregatedAmountList(self, sequence=None, sequence_list=None, **kw):
    """
      Verify the return of getAggregatedAmountList
    """
    expected_list = [
      {'id':('size/Baby', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,.42], 'total':.82, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Baby', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,.315], 'total':.715, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Baby', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,.27], 'total':.67, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Baby', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,.49], 'total':.89, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Baby', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,.315], 'total':.715, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Baby', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,.315], 'total':.715, 'duration':[None,None,None], 'total_duration':None},
      
      {'id':('size/Child/32', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,.54], 'total':.94, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Child/32', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,.585], 'total':.985, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Child/32', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,.54], 'total':.94, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Child/32', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,.91], 'total':1.31, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Child/32', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,.84], 'total':1.24, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Child/32', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,.585], 'total':.985, 'duration':[None,None,None], 'total_duration':None},
      
      {'id':('size/Child/34', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,.81], 'total':1.21, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Child/34', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,1.33], 'total':1.73, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Child/34', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,1.26], 'total':1.66, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Child/34', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,.855], 'total':1.255, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Child/34', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,1.26], 'total':1.66, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Child/34', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,1.33], 'total':1.73, 'duration':[None,None,None], 'total_duration':None},
         
      {'id':('size/Man', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,1.68], 'total':2.08, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Man', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,1.75], 'total':2.15, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Man', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,1.68], 'total':2.08, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Man', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,1.125], 'total':1.525, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Man', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,1.08], 'total':1.48, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Man', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,1.75], 'total':2.15, 'duration':[None,None,None], 'total_duration':None},
         
      {'id':('size/Woman', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,2.1], 'total':2.5, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Woman', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,1.395], 'total':1.795, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Woman', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,1.35], 'total':1.75, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Woman', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,2.17], 'total':2.57, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Woman', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,2.1], 'total':2.5, 'duration':[None,None,None], 'total_duration':None},
      {'id':('size/Woman', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,2.17], 'total':2.57, 'duration':[None,None,None], 'total_duration':None},
       ]
    transformation = sequence.get('transformation')
    self.verifyAggregatedAmountList(transformation, expected_list)
        
          
  def stepVerifyIncludedTransformationAggregatedAmountList(self, sequence=None, sequence_list=None, **kw):
    """
      Verify the return of getAggregatedAmountList
    """
    expected_list = [
      {'id':('size/Baby', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/4'),
         'amount':[None,None,.09], 'total':.09, 'duration':[10.,2.,None], 'total_duration':12.},
      {'id':('size/Baby', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/5'),
         'amount':[None,None,.09], 'total':.09, 'duration':[10.,2.,None], 'total_duration':12.},
      {'id':('size/Baby', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/4'),
         'amount':[None,None,.09], 'total':.09, 'duration':[10.,2.,None], 'total_duration':12.},
      {'id':('size/Baby', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/5'),
         'amount':[None,None,.09], 'total':.09, 'duration':[10.,2.,None], 'total_duration':12.},
      {'id':('size/Baby', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/4'),
         'amount':[None,None,.09], 'total':.09, 'duration':[10.,2.,None], 'total_duration':12.},
      {'id':('size/Baby', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/5'),
         'amount':[None,None,.09], 'total':.09, 'duration':[10.,2.,None], 'total_duration':12.},
      
      {'id':('size/Child/32', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/4'),
         'amount':[None,None,.135], 'total':.135, 'duration':[10.,2.5,None], 'total_duration':12.5},
      {'id':('size/Child/32', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/5'),
         'amount':[None,None,.135], 'total':.135, 'duration':[10.,2.5,None], 'total_duration':12.5},
      {'id':('size/Child/32', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/4'),
         'amount':[None,None,.135], 'total':.135, 'duration':[10.,2.5,None], 'total_duration':12.5},
      {'id':('size/Child/32', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/5'),
         'amount':[None,None,.135], 'total':.135, 'duration':[10.,2.5,None], 'total_duration':12.5},
      {'id':('size/Child/32', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/4'),
         'amount':[None,None,.135], 'total':.135, 'duration':[10.,2.5,None], 'total_duration':12.5},
      {'id':('size/Child/32', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/5'),
         'amount':[None,None,.135], 'total':.135, 'duration':[10.,2.5,None], 'total_duration':12.5},
      
      {'id':('size/Child/34', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/4'),
         'amount':[None,None,.18], 'total':.18, 'duration':[10.,3.,None], 'total_duration':13.},
      {'id':('size/Child/34', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/5'),
         'amount':[None,None,.18], 'total':.18, 'duration':[10.,3.,None], 'total_duration':13.},
      {'id':('size/Child/34', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/4'),
         'amount':[None,None,.18], 'total':.18, 'duration':[10.,3.,None], 'total_duration':13.},
      {'id':('size/Child/34', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/5'),
         'amount':[None,None,.18], 'total':.18, 'duration':[10.,3.,None], 'total_duration':13.},
      {'id':('size/Child/34', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/4'),
         'amount':[None,None,.18], 'total':.18, 'duration':[10.,3.,None], 'total_duration':13.},
      {'id':('size/Child/34', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/5'),
         'amount':[None,None,.18], 'total':.18, 'duration':[10.,3.,None], 'total_duration':13.},
         
      {'id':('size/Man', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/4'),
         'amount':[None,None,.27], 'total':.27, 'duration':[10.,3.5,None], 'total_duration':13.5},
      {'id':('size/Man', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/5'),
         'amount':[None,None,.27], 'total':.27, 'duration':[10.,3.5,None], 'total_duration':13.5},
      {'id':('size/Man', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/4'),
         'amount':[None,None,.27], 'total':.27, 'duration':[10.,3.5,None], 'total_duration':13.5},
      {'id':('size/Man', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/5'),
         'amount':[None,None,.27], 'total':.27, 'duration':[10.,3.5,None], 'total_duration':13.5},
      {'id':('size/Man', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/4'),
         'amount':[None,None,.27], 'total':.27, 'duration':[10.,3.5,None], 'total_duration':13.5},
      {'id':('size/Man', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/5'),
         'amount':[None,None,.27], 'total':.27, 'duration':[10.,3.5,None], 'total_duration':13.5},
         
      {'id':('size/Woman', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/4'),
         'amount':[None,None,.225], 'total':.225, 'duration':[10.,4.,None], 'total_duration':14.},
      {'id':('size/Woman', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/5'),
         'amount':[None,None,.225], 'total':.225, 'duration':[10.,4.,None], 'total_duration':14.},
      {'id':('size/Woman', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/4'),
         'amount':[None,None,.225], 'total':.225, 'duration':[10.,4.,None], 'total_duration':14.},
      {'id':('size/Woman', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/5'),
         'amount':[None,None,.225], 'total':.225, 'duration':[10.,4.,None], 'total_duration':14.},
      {'id':('size/Woman', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/4'),
         'amount':[None,None,.225], 'total':.225, 'duration':[10.,4.,None], 'total_duration':14.},
      {'id':('size/Woman', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/5'),
         'amount':[None,None,.225], 'total':.225, 'duration':[10.,4.,None], 'total_duration':14.},
       ]     
    transformation = sequence.get('included_transformation')
    self.verifyAggregatedAmountList(transformation, expected_list)
    
  
  def stepVerifySpecialisedTransformationAggregatedAmountList(self, sequence=None, sequence_list=None, **kw):
    """
      Verify the return of AggregatedAmountList for a transformation which includes another one
    """
    expected_list = [
      {'id':('size/Baby', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,.42,None,None,.09], 'total':.91, 'duration':[None,None,None,10.,2.,None], 'total_duration':12.},
      {'id':('size/Baby', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,.315,None,None,.09], 'total':.805, 'duration':[None,None,None,10.,2.,None], 'total_duration':12.},
      {'id':('size/Baby', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,.27,None,None,.09], 'total':.76, 'duration':[None,None,None,10.,2.,None], 'total_duration':12.},
      {'id':('size/Baby', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,.49,None,None,.09], 'total':.98, 'duration':[None,None,None,10.,2.,None], 'total_duration':12.},
      {'id':('size/Baby', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,.315,None,None,.09], 'total':.805, 'duration':[None,None,None,10.,2.,None], 'total_duration':12.},
      {'id':('size/Baby', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,.315,None,None,.09], 'total':.805, 'duration':[None,None,None,10.,2.,None], 'total_duration':12.},
         
      {'id':('size/Child/32', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,.54,None,None,.135], 'total':1.075, 'duration':[None,None,None,10.,2.5,None], 'total_duration':12.5},
      {'id':('size/Child/32', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,.585,None,None,.135], 'total':1.12, 'duration':[None,None,None,10.,2.5,None], 'total_duration':12.5},
      {'id':('size/Child/32', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,.54,None,None,.135], 'total':1.075, 'duration':[None,None,None,10.,2.5,None], 'total_duration':12.5},
      {'id':('size/Child/32', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,.91,None,None,.135], 'total':1.445, 'duration':[None,None,None,10.,2.5,None], 'total_duration':12.5},
      {'id':('size/Child/32', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,.84,None,None,.135], 'total':1.375, 'duration':[None,None,None,10.,2.5,None], 'total_duration':12.5},
      {'id':('size/Child/32', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,.585,None,None,.135], 'total':1.12, 'duration':[None,None,None,10.,2.5,None], 'total_duration':12.5},
          
      {'id':('size/Child/34', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,.81,None,None,.18], 'total':1.39, 'duration':[None,None,None,10.,3.,None], 'total_duration':13.},
      {'id':('size/Child/34', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,1.33,None,None,.18], 'total':1.91, 'duration':[None,None,None,10.,3.,None], 'total_duration':13.},
      {'id':('size/Child/34', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,1.26,None,None,.18], 'total':1.84, 'duration':[None,None,None,10.,3.,None], 'total_duration':13.},
      {'id':('size/Child/34', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,.855,None,None,.18], 'total':1.435, 'duration':[None,None,None,10.,3.,None], 'total_duration':13.},
      {'id':('size/Child/34', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,1.26,None,None,.18], 'total':1.84, 'duration':[None,None,None,10.,3.,None], 'total_duration':13.},
      {'id':('size/Child/34', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,1.33,None,None,.18], 'total':1.91, 'duration':[None,None,None,10.,3.,None], 'total_duration':13.},
         
      {'id':('size/Man', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,1.68,None,None,.27], 'total':2.35, 'duration':[None,None,None,10.,3.5,None], 'total_duration':13.5},
      {'id':('size/Man', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,1.75,None,None,.27], 'total':2.42, 'duration':[None,None,None,10.,3.5,None], 'total_duration':13.5},
      {'id':('size/Man', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,1.68,None,None,.27], 'total':2.35, 'duration':[None,None,None,10.,3.5,None], 'total_duration':13.5},
      {'id':('size/Man', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,1.125,None,None,.27], 'total':1.795, 'duration':[None,None,None,10.,3.5,None], 'total_duration':13.5},
      {'id':('size/Man', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,1.08,None,None,.27], 'total':1.75, 'duration':[None,None,None,10.,3.5,None], 'total_duration':13.5},
      {'id':('size/Man', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,1.75,None,None,.27], 'total':2.42, 'duration':[None,None,None,10.,3.5,None], 'total_duration':13.5},
         
      {'id':('size/Woman', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,2.1,None,None,.225], 'total':2.725, 'duration':[None,None,None,10.,4.,None], 'total_duration':14.},
      {'id':('size/Woman', 'colour/apparel_model_module/1/1', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,1.395,None,None,.225], 'total':2.02, 'duration':[None,None,None,10.,4.,None], 'total_duration':14.},
      {'id':('size/Woman', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,1.35,None,None,.225], 'total':1.975, 'duration':[None,None,None,10.,4.,None], 'total_duration':14.},
      {'id':('size/Woman', 'colour/apparel_model_module/1/2', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,2.17,None,None,.225], 'total':2.795, 'duration':[None,None,None,10.,4.,None], 'total_duration':14.},
      {'id':('size/Woman', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/4'),
         'amount':[.3,.1,2.1,None,None,.225], 'total':2.725, 'duration':[None,None,None,10.,4.,None], 'total_duration':14.},
      {'id':('size/Woman', 'colour/apparel_model_module/1/3', 'morphology/apparel_model_module/1/5'),
         'amount':[.3,.1,2.17,None,None,.225], 'total':2.795, 'duration':[None,None,None,10.,4.,None], 'total_duration':14.},
       ]
    transformation = sequence.get('transformation')
    self.verifyAggregatedAmountList(transformation, expected_list)
    
    
  def verifyAggregatedAmountList(self, transformation, expected_list):
    """
      Verify aggregated data according to an expected structure
    """
    for expected in expected_list:
      aggregated_amount_list = transformation.getAggregatedAmountList(
        context=transformation.asContext(categories = expected['id']) )
      expected_amount_list = expected['amount']
      
      # Check the number of aggregated components
      if len(aggregated_amount_list) != len(expected_amount_list):
        LOG('TEST ERROR :', 0, 'number of Amount differs between expected (%d) and aggregated (%d) for categories %s' %
             (len(expected_amount_list), len(aggregated_amount_list), expected['id']))
        self.failUnless(0)
        
      # Check quantity for each component
      for i in range(len(aggregated_amount_list)):
        a_amount = aggregated_amount_list[i]
        a_price = a_amount.getTotalPrice()
        e_price = expected_amount_list[i]
        error = 0
        if e_price is None and a_price is not None:
          error = 1
        if a_price is None and e_price is not None:
          error = 1
        if e_price is not None and a_price is not None:
          if round(a_price,10) != round(e_price,10):
            error = 1
        if error == 1:
          LOG('TEST ERROR :', 0, 'Total price differs between expected (%s) and aggregated (%s) Amounts (resource : %s, id_categories : %s, amount.categories : %s)' %
            (repr(e_price), repr(a_price), repr(a_amount.getResource()), expected['id'], a_amount.getCategoryList()))
          self.failUnless(0)
          
      # Check duration for each component
        a_duration = a_amount.getDuration()
        e_duration = expected['duration'][i]
        error = 0
        if e_duration is None and a_duration is not None:
          error = 1
        if a_duration is None and e_duration is not None:
          error = 1
        if e_duration is not None and a_duration is not None:
          if round(a_duration,10) != round(e_duration,10):
            error = 1
        if error == 1:
          LOG('TEST ERROR :', 0, 'Duration differs between expected (%s) and aggregated (%s) Amounts (resource : %s, id_categories : %s, amount.categories : %s)' %
            (repr(e_duration), repr(a_duration), repr(a_amount.getResource()), expected['id'], a_amount.getCategoryList()))
          self.failUnless(0)
            
      # Check global quantity
      total_price = aggregated_amount_list.getTotalPrice()
      if round(total_price, 10) != round(expected['total'], 10):
        LOG('TEST ERROR :', 0, 'Total price for AggregatedAmountList differs between expected (%s) and aggregated (%s) (%s)' %
          (repr(total_price), repr(expected['total']), expected['id']))
        self.failUnless(0)
        
      # Check global duration
      total_duration = aggregated_amount_list.getTotalDuration()
      expected_duration = expected['total_duration']
      error = 0
      if total_duration is None and expected_duration is not None:
        error = 1
      if expected_duration is None and total_duration is not None:
        error = 1
      if total_duration is not None and expected_duration is not None:
        if round(total_duration, 10) != round(expected_duration, 10):
          error = 1
      if error == 1:
        LOG('TEST ERROR :', 0, 'Total duration differs between expected (%s) and aggregated (%s) (%s)' %
          (repr(expected_duration), repr(total_duration), expected['id']))
       
               
  def test_01_getAggregatedAmountList(self, quiet=0, run=run_all_test):
    """
      Test the method getAggregatedAmountList
    """
    if not run: return
    sequence_list = SequenceList()

    # Test with a simply order without cell
    sequence_string = '\
                      CreateComponentDict \
                      CreateOperationDict \
                      Tic \
                      CreateVariatedResource \
                      Tic \
                      CreateTransformation \
                      Tic \
                      CreateTransformationLine \
                      Tic \
                      CreateIncludedTransformation \
                      Tic \
                      CreateIncludedTransformationLine \
                      Tic \
                      VerifyTransformationAggregatedAmountList \
                      VerifyIncludedTransformationAggregatedAmountList \
                      IncludeTransformation \
                      Tic \
                      VerifySpecialisedTransformationAggregatedAmountList \
                      '
    sequence_list.addSequenceString(sequence_string)

    sequence_list.play(self)