diff --git a/product/ERP5/Document/Transformation.py b/product/ERP5/Document/Transformation.py
index 14e38f64645fbe43c7c2260da7d60978f6bf2c39..a4a80bcd3f784ff6b63d206783b9cb89e2867b5b 100755
--- a/product/ERP5/Document/Transformation.py
+++ b/product/ERP5/Document/Transformation.py
@@ -2,6 +2,8 @@
 #
 # Copyright (c) 2002 Coramy SAS and Contributors. All Rights Reserved.
 #                    Thierry_Faucher <Thierry_Faucher@coramy.com>
+# Copyright (c) 2004 Nexedi SARL and Contributors. All Rights Reserved.
+#                    Romain Courteaud <romain@nexedi.com>
 #
 # WARNING: This program as such is intended to be used by professional
 # programmers who take the whole responsability of assessing all potential
@@ -37,6 +39,7 @@ from Products.ERP5.Variated import Variated
 
 from Products.ERP5.Document.Domain import Domain
 
+import string
 from zLOG import LOG
 
 class Transformation(XMLObject, Domain, Variated):
@@ -46,26 +49,13 @@ class Transformation(XMLObject, Domain, Variated):
       Use of default_resource... (to define the variation range,
       to ...)
 
-
-      About ranges:
-
-      getDomainBaseCategoryList -> base categories which allow to select state...
-
-      getDomainRangeBaseCategoryList -> base categories which allow to select state...
-
-      getDomainCategoryList -> bonne id?.... = DomainValue...
-                               base category which defines the state
-                               (and other things)
-
-      getDomainRangeCategoryList -> bonne id?.... = DomainValue...
+      XXX Transformation works only for a miximum of 3 variation base category...
+      Matrixbox must be rewrite for a clean implementation of n base category
 
     """
 
     meta_type = 'ERP5 Transformation'
     portal_type = 'Transformation'
-    add_permission = Permissions.AddPortalContent
-    isPortalContent = 1
-    isRADContent = 1
 
     # Declarative security
     security = ClassSecurityInfo()
@@ -78,139 +68,82 @@ class Transformation(XMLObject, Domain, Variated):
                       , PropertySheet.DublinCore
                       , PropertySheet.VariationRange
                       , PropertySheet.Domain
+                      #, PropertySheet.Resource
+                      , PropertySheet.TransformedResource
+                      , PropertySheet.Path
                       , PropertySheet.Transformation
                       )
 
     # Declarative interfaces
     __implements__ = ( Interface.Variated, )
 
-    # Factory Type Information
-    factory_type_information = \
-      {    'id'             : portal_type
-         , 'meta_type'      : meta_type
-         , 'description'    : """\
-une gamme..."""
-         , 'icon'           : 'transformation_icon.gif'
-         , 'product'        : 'ERP5'
-         , 'factory'        : 'addTransformation'
-         , 'immediate_view' : 'transformation_view'
-         , 'allow_discussion'     : 1
-         , 'allowed_content_types': ('Transformed Resource',
-                                      )
-         , 'filter_content_types' : 1
-         , 'global_allow'   : 1
-         , 'actions'        :
-        ( { 'id'            : 'view'
-          , 'name'          : 'View'
-          , 'category'      : 'object_view'
-          , 'action'        : 'transformation_view'
-          , 'permissions'   : (
-              Permissions.View, )
-          }
-        , { 'id'            : 'list'
-          , 'name'          : 'Object Contents'
-          , 'category'      : 'object_action'
-          , 'action'        : 'folder_contents'
-          , 'permissions'   : (
-              Permissions.View, )
-          }
-        , { 'id'            : 'print'
-          , 'name'          : 'Print'
-          , 'category'      : 'object_print'
-          , 'action'        : 'transformation_print'
-          , 'permissions'   : (
-              Permissions.View, )
-          }
-        , { 'id'            : 'metadata'
-          , 'name'          : 'Metadata'
-          , 'category'      : 'object_view'
-          , 'action'        : 'metadata_edit'
-          , 'permissions'   : (
-              Permissions.View, )
-          }
-        , { 'id'            : 'translate'
-          , 'name'          : 'Translate'
-          , 'category'      : 'object_action'
-          , 'action'        : 'translation_template_view'
-          , 'permissions'   : (
-              Permissions.TranslateContent, )
-          }
-        )
-      }
-
-    security.declareProtected(Permissions.AccessContentsInformation,
-                                          'getVariationRangeBaseCategoryList')
+
+    security.declareProtected(Permissions.AccessContentsInformation, 'updateVariationCategoryList')
+    def updateVariationCategoryList(self):
+      """
+        Check if variation category list of the resource changed and update transformation
+        and transformation line
+      """
+      self.setVariationBaseCategoryList( self.getVariationBaseCategoryList() )
+      transformation_line_list = self.contentValues()
+      for transformation_line in transformation_line_list:
+        transformation_line.updateVariationCategoryList()
+
+    security.declareProtected(Permissions.AccessContentsInformation, 'getVariationRangeBaseCategoryList')
     def getVariationRangeBaseCategoryList(self):
         """
           Returns possible variation base_category ids of the
-          default resource of this transformation
+          default resource which can be used a variation axis
+          in the transformation. 
         """
-        resource = self.getDefaultResourceValue()
+        #resource = self.getDefaultResourceValue()
+        resource = self.getResourceValue()
         if resource is not None:
+          #result = [''] + resource.getVariationBaseCategoryList()
           result = resource.getVariationBaseCategoryList()
         else:
-          result = self.getBaseCategoryIds()
+          # XXX result = self.getBaseCategoryIds()
+          # Why calling this method ?
+          # Get a global variable which define a list of variation base category
+          # XXX I don' t like this approch, maybe can we define this variable 
+          # on Transformation property sheet ? (Romain)
+          result = self.getPortalVariationBaseCategoryList()
         return result
 
-    security.declareProtected(Permissions.AccessContentsInformation,
-                            'getTransformationVariationRangeCategoryList')
-    def getTransformationVariationRangeCategoryList(self):
-        """
-          Possible categories (ie. of the default resource)
-        """
-        return self.getResourceValue().getVariationCategoryList(base_category_list=
-                              self.getTransformationVariationBaseCategoryList())
-
-    security.declareProtected(Permissions.AccessContentsInformation,
-                            'getTransformationVariationCategoryList')
-    def getTransformationVariationCategoryList(self):
-        """
-          Possible categories (ie. of the default resource)
-        """
-        return self.getVariationCategoryList(base_category_list=
-                              self.getTransformationVariationBaseCategoryList())
-
-    security.declareProtected(Permissions.AccessContentsInformation,
-                            'getTransformationVariationRangeBaseCategoryList')
-    def getTransformationVariationRangeBaseCategoryList(self):
-        """
-          Returns possible variation base_category ids of the
-          default resource which can be used a variation axis
-          in the transformation (ie. all ids of
-          getVariationRangeBaseCategoryList except ids which
-          are used as a transformation state as defined in
-          domain_base_category)
-        """
-        result = ['',] + list(self.getVariationRangeBaseCategoryList())
-        forbidden_id = self.getDomainBaseCategoryList()
-        forbidden_id = asList(forbidden_id) # This will soon be useless
-        return rejectIn(result, forbidden_id)
-
-    security.declareProtected(Permissions.AccessContentsInformation,
-                                      'getVariationRangeBaseCategoryItemList')
+    security.declareProtected(Permissions.AccessContentsInformation, 'getVariationRangeBaseCategoryItemList')
     def getVariationRangeBaseCategoryItemList(self):
         """
-          Returns possible variations of the resource
+          Returns possible variations of the transformation
           as a list of tuples (id, title). This is mostly
           useful in ERP5Form instances to generate selection
           menus.
         """
-        return self.portal_categories.getItemList(self.getVariationRangeBaseCategoryList())
+        return self.portal_categories.getItemList( self.getVariationRangeBaseCategoryList() )
+
 
-    security.declareProtected(Permissions.AccessContentsInformation,
-                          'getTransformationVariationRangeBaseCategoryItemList')
-    def getTransformationVariationRangeBaseCategoryItemList(self):
+    security.declareProtected(Permissions.AccessContentsInformation,'getVariationRangeCategoryList')
+    def getVariationRangeCategoryList(self, base_category_list = ()):
         """
-          Returns possible variations of the transformation
-          as a list of tuples (id, title). This is mostly
-          useful in ERP5Form instances to generate selection
-          menus.
+          Returns possible variation category values for the
+          transformation according to the default resource.
+          Possible category values is provided as a list of
+          id.
+          User may want to define generic transformation without
+          any resource define.
+          Result is left display.
         """
-        return self.portal_categories.getItemList(
-                    self.getTransformationVariationRangeBaseCategoryList())
+        if base_category_list is ():
+          base_category_list = self.getVariationBaseCategoryList()
+
+        resource = self.getResourceValue()
+        if resource != None:
+          result = resource.getVariationRangeCategoryList(base_category_list)
+        else:
+          # No resource is define on transformation. We want to display content of base categories
+          result = self.portal_categories.getCategoryChildList(base_category_list, base=1)
+        return result
 
-    security.declareProtected(Permissions.AccessContentsInformation,
-                                        'getVariationRangeCategoryItemList')
+    security.declareProtected(Permissions.AccessContentsInformation,'getVariationRangeCategoryItemList')
     def getVariationRangeCategoryItemList(self, base_category_list = ()):
         """
           Returns possible variation category values for the
@@ -219,33 +152,105 @@ une gamme..."""
           tuples (id, title). This is mostly
           useful in ERP5Form instances to generate selection
           menus.
+          User may want to define generic transformation without
+          any resource define.
         """
         if base_category_list is ():
           base_category_list = self.getVariationBaseCategoryList()
-        try:
-          result = self.getDefaultResourceValue(
-                    ).getVariationRangeCategoryItemList(base_category_list)
-        except:
-          result = self.portal_categories.getCategoryChildItemList(base_category_list,
-                                               base=1)
+
+        resource = self.getResourceValue()
+        if resource != None:
+          result = resource.getVariationRangeCategoryItemList(base_category_list)
+        else:
+          # No resource is define on transformation. We want to display content of base categories
+          result = self.portal_categories.getCategoryChildTitleItemList(base_category_list, base=1, display_none_category=0)
         return result
 
-    # Aliases to simplify access to range information for TransformedResources
-    security.declareProtected(Permissions.AccessContentsInformation,
-                            'getTransformationVariationBaseCategoryList')
-    def getTransformationVariationBaseCategoryList(self):
+    security.declareProtected(Permissions.AccessContentsInformation, 'getVariationBaseCategoryItemList')
+    def getVariationBaseCategoryItemList(self):
       """
-        Returns a list of base_category ids for this tranformation
+        Returns a list of base_category tuples for this tranformation
       """
-      return self.getVariationBaseCategoryList()
+      return self.portal_categories.getItemList(self.getVariationBaseCategoryList())
 
-    security.declareProtected(Permissions.AccessContentsInformation,
-                            'getTransformationVariationBaseCategoryItemList')
-    def getTransformationVariationBaseCategoryItemList(self):
+
+    security.declareProtected(Permissions.AccessContentsInformation, '_setVariationBaseCategoryList')
+    def _setVariationBaseCategoryList(self, value):
       """
-        Returns a list of base_category tuples for this tranformation
+        Define the possible base categories
       """
-      return self.portal_categories.getItemList(self.getVariationBaseCategoryList())
+#      XXX TransformedResource works only for a maximum of 3 variation base category...
+#      Matrixbox must be rewrite for a clean implementation of n base category
+      if len(value) <= 3:
+        self._baseSetVariationBaseCategoryList(value)
+      else:
+        raise MoreThan3VariationBaseCategory
+
+      # create relations between resource variation and transformation
+      self._setVariationCategoryList( self.getVariationRangeCategoryList() )
+
+    security.declareProtected(Permissions.AccessContentsInformation, 'setVariationBaseCategoryList')
+    def setVariationBaseCategoryList(self, value):
+      """
+        Define the possible base categories and reindex object
+      """
+      self._setVariationBaseCategoryList(value)
+      self.reindexObject()
+
+
+    security.declareProtected(Permissions.AccessContentsInformation, 'getVariationCategoryItemList')
+    def getVariationCategoryItemList(self, base_category_list=()):
+      """
+        Returns a list of variation tuples for this tranformation
+        Result is left display.
+      """
+      if base_category_list == ():
+        base_category_list = self.getVariationBaseCategoryList()
+      
+      variation_category_item_list = map(lambda x: (x,x), self.getVariationCategoryList( base_category_list=base_category_list ))
+
+      return variation_category_item_list
+
+    security.declareProtected(Permissions.AccessContentsInformation, 'getVariationCategoryItemList')
+    def getVariationCategoryItemList(self, base_category_list = (), base=1, display_id='getTitleOrId', current_category=None):
+      """
+        Returns the list of possible variations
+        XXX Copied and modified from Variated
+        Result is left display.
+      """
+      variation_category_item_list = []
+
+# What is this parameter ?
+#    if current_category is not None:
+#      variation_category_item_list.append((current_category,current_category))
+
+      if base_category_list == ():
+        base_category_list = self.getVariationBaseCategoryList()
+
+      variation_category_list = self.getVariationCategoryList(base_category_list=base_category_list)
+
+      for variation_category in variation_category_list:
+        resource = self.portal_categories.resolveCategory(variation_category)
+
+        if resource.getPortalType() == 'Category':
+          # Category is unusable if only Title is displayed...
+          value = getattr(resource, 'getLogicalPath')()
+        else:
+          # And displaying LogicalPath for variation is unusable for user...
+          value = getattr(resource, display_id)()
+
+        if base:
+          index = variation_category.find('/') 
+          base_category = variation_category[:index]
+          label = base_category+'/'+value
+        else:
+          label = value
+
+        # Result is left display
+        variation_category_item_list.append((label,  variation_category ))  
+
+      return variation_category_item_list
+
 
     # This is the main method to do any BOM related calculation
     security.declareProtected(Permissions.AccessContentsInformation, 'getAggregatedAmountList')
@@ -260,95 +265,79 @@ une gamme..."""
       # LOG('getAggregatedAmountList',0,str((context, REQUEST, kw)))
       REQUEST = self.asContext(context=context, REQUEST=REQUEST, **kw)
       # First we need to get the list of transformations which this transformation depends on
-      # At this moment, we only consider 1 dependency
-      transformation_list = [self] + self.getSpecialiseValueList()
+      # XXX At this moment, we only consider 1 dependency
+      template_transformation_list = self.getSpecialiseValueList()
+
       # We consider that the specific parameters to take into account
       # are acquired through the REQUEST parameter
       # The REQUEST can either be a Zope REQUEST or a dictionnary provided by the use
-      if REQUEST is None:
-        # At this moment XXXXXXXXXXXXXXXXXXXXXXXX
-        # we initialize the request to a default value in order
-        # to make sure we have something to test MappedValues on
-        REQUEST = {'categories': ('taille/enfant/08 ans','coloris/modele/701C402/2')}
-      # We define some initial values
-      # result holds a list of dictionaries
-      # price holds a list of dictionaries
-      summary_list = []
-      grand_total_variated_source_base_price = 0.0
-      grand_total_source_base_price = 0.0
-      grand_total_base_price = 0.0
-      grand_total_variated_base_price = 0.0
-      grand_total_variated_source_base_price = 0.0
-      grand_total_duration = 0.0
+#      if REQUEST is None:
+#        # At this moment XXX
+#        # we initialize the request to a default value in order
+#        # to make sure we have something to test MappedValues on
+#        #REQUEST = {'categories': ('taille/enfant/08 ans','coloris/modele/701C402/2')}
+#        REQUEST = {}
+
+      result = None
 
       # Browse all involved transformations and create one line per line of transformation
       # Currently, we do not consider abstractions, we just add whatever we find in all
       # transformations
-      for transformation in transformation_list:
+      for transformation in [self] + transformation_list:
         # Browse each transformed or assorted resource of the current transformation
         for transformed_resource in transformation.objectValues():
-          #LOG("for transformed_resource in transformation",0,transformed_resource.getId())
-          line_item_list, total_base_price, total_source_base_price, \
-            total_variated_base_price, total_variated_source_base_price, duration \
-            = transformed_resource.getAggregatedAmountList(REQUEST)
-          summary_list += line_item_list
-          grand_total_base_price += total_base_price
-          grand_total_source_base_price += total_source_base_price
-          grand_total_variated_base_price += total_variated_base_price
-          grand_total_variated_source_base_price += total_variated_source_base_price
-          grand_total_duration += duration
-
-      #LOG("Transformation Agg summary",0,
-      #        str(map(lambda o: (o.resource_id, o.quantity_defined_by), summary_list)))
-
-      # Return values as a tuple
-      return map(lambda x: x.__dict__, summary_list) , grand_total_base_price, \
-         grand_total_source_base_price, grand_total_duration, \
-         grand_total_variated_base_price, grand_total_variated_source_base_price
-
-    # UPDATED BY JPS
-
-    # XXX This should not be there, but in Document/TransformedResource.py or something like
-    # this, but actually this does not work, we should find why.
-    security.declareProtected(Permissions.ModifyPortalContent, '_setVariationBaseCategoryList')
-    def _setVariationBaseCategoryList(self, new_base_category_list):
-      """
-        We override the default behaviour generated by Utils.py in order
-        to update all TransformedResource contained in this transformation
-      """
-      # Get the list of previous base_category that have been removed or kept
-      removed_base_category = []
-      kept_base_category = []
-      for cat in self.getVariationBaseCategoryList():
-        if cat in new_base_category_list:
-          kept_base_category += [cat]
-        else:
-          removed_base_category += [cat]
-
-      # Update variation_base_category_list
-      self.variation_base_category_list = new_base_category_list
-
-      # Make sure there is no reference to categories
-      # of removed_base_category
-      # in categories
-      if len(removed_base_category) > 0:
-        self._setCategoryMembership(removed_base_category, [], base=1)
-
-      # Filter all fields which are based on base_category
-      if self.getVariationBaseCategoryLine() not in new_base_category_list:
-        self._setVariationBaseCategoryLine(None)
-      if self.getVariationBaseCategoryColumn() not in new_base_category_list:
-        self._setVariationBaseCategoryColumn(None)
-      self._setVariationBaseCategoryTabList(keepIn(self.getVariationBaseCategoryTabList(),
-                                                      new_base_category_list))
-
-      # Make sure that all sub-objects use a valid range
-      # We simply call range functions on each object to force
-      # range update in XMLMatrix
-      for o in self.objectValues():
-        if hasattr(o,'v_variation_base_category_list'):
-          o.setVVariationBaseCategoryList(keepIn(o.getVVariationBaseCategoryList(),
-                                                              new_base_category_list))
-        if hasattr(o,'q_variation_base_category_list'):
-          o.setQVariationBaseCategoryList(keepIn(o.getQVariationBaseCategoryList(),
-                                                              new_base_category_list))
+
+          if result==None:
+            result = transformed_resource.getAggregatedAmountList(REQUEST)
+          else:
+            result += transformed_resource.getAggregatedAmountList(REQUEST)
+
+      return result
+
+
+#    # UPDATED BY JPS
+#
+#    # XXX This should not be there, but in Document/TransformedResource.py or something like
+#    # this, but actually this does not work, we should find why.
+#    security.declareProtected(Permissions.ModifyPortalContent, '_setVariationBaseCategoryList')
+#    def _setVariationBaseCategoryList(self, new_base_category_list):
+#      """
+#        We override the default behaviour generated by Utils.py in order
+#        to update all TransformedResource contained in this transformation
+#      """
+#      # Get the list of previous base_category that have been removed or kept
+#      removed_base_category = []
+#      kept_base_category = []
+#      for cat in self.getVariationBaseCategoryList():
+#        if cat in new_base_category_list:
+#          kept_base_category += [cat]
+#        else:
+#          removed_base_category += [cat]
+#
+#      # Update variation_base_category_list
+#      self.variation_base_category_list = new_base_category_list
+#
+#      # Make sure there is no reference to categories
+#      # of removed_base_category
+#      # in categories
+#      if len(removed_base_category) > 0:
+#        self._setCategoryMembership(removed_base_category, [], base=1)
+#
+#      # Filter all fields which are based on base_category
+#      if self.getVariationBaseCategoryLine() not in new_base_category_list:
+#        self._setVariationBaseCategoryLine(None)
+#      if self.getVariationBaseCategoryColumn() not in new_base_category_list:
+#        self._setVariationBaseCategoryColumn(None)
+#      self._setVariationBaseCategoryTabList(keepIn(self.getVariationBaseCategoryTabList(),
+#                                                      new_base_category_list))
+#
+#      # Make sure that all sub-objects use a valid range
+#      # We simply call range functions on each object to force
+#      # range update in XMLMatrix
+#      for o in self.objectValues():
+#        if hasattr(o,'v_variation_base_category_list'):
+#          o.setVVariationBaseCategoryList(keepIn(o.getVVariationBaseCategoryList(),
+#                                                              new_base_category_list))
+#        if hasattr(o,'q_variation_base_category_list'):
+#          o.setQVariationBaseCategoryList(keepIn(o.getQVariationBaseCategoryList(),
+#                                                              new_base_category_list))