diff --git a/product/ERP5/tests/testInventory.py b/product/ERP5/tests/testInventory.py
new file mode 100755
index 0000000000000000000000000000000000000000..ff88bf2e42a1b7b2a11b607e0b1b732e91dcf3f9
--- /dev/null
+++ b/product/ERP5/tests/testInventory.py
@@ -0,0 +1,1436 @@
+##############################################################################
+#
+# Copyright (c) 2004, 2005 Nexedi SARL and Contributors. All Rights Reserved.
+#          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 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
+from Products.ERP5Type.DateUtils import addToDate
+import time
+import os
+from Products.ERP5Type import product_path
+from Products.CMFCore.utils import getToolByName
+from testOrder import TestOrderMixin
+
+from Products.ERP5Form.ListBox import makeTreeList
+
+class Test(TestOrderMixin,ERP5TypeTestCase):
+  """
+    Test Transformations
+  """
+  run_all_test = 1
+  packing_list_portal_type = 'Sale Packing List'
+  packing_list_line_portal_type = packing_list_portal_type + ' Line'
+  
+  def getTitle(self):
+    return "Inventory"
+
+  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 createCategory(self, parent, id_list):
+      last_category = None
+      for category_id in id_list:
+        if type(category_id) == type('a'):
+          last_category = parent.newContent(portal_type = 'Category', id=category_id)
+        else:
+          self.createCategory(last_category, category_id)
+  
+  def stepCreateOrganisationList(self, sequence=None, sequence_list=None, **kw):
+    """
+      Create some organisations to manipulate during the test
+      Organisations organigram :
+        section 0 :
+          - payment 1
+          - nodes 2, 3
+        section 4 (provider) :
+          - payment 5
+          - node 6
+        section 7 (customer) :
+          - payment  8
+          - node 9
+    """
+    organisation_list = []
+    for i in range(10):
+      self.stepCreateOrganisation(sequence=sequence, sequence_list=sequence_list, **kw)
+      organisation_list.append(sequence.get('organisation'))
+      
+    category_tool = self.getPortal().portal_categories
+    bc = category_tool.site
+    self.createCategory(bc, ['Place1', ['A', 'B'], 'Place2', ['C'], 'Place3', ['D']])
+    organisation_list[2] = bc.Place1.A
+    organisation_list[3] = bc.Place1.B
+    organisation_list[6] = bc.Place2.C
+    organisation_list[9] = bc.Place3.D
+    
+    sequence.edit(
+      organisation = None,
+      organisation_list = organisation_list)
+        
+
+  def stepCreateVariatedResourceList(self, sequence=None, sequence_list=None, **kw):
+    """
+      Create some variated resources to manipulate during the test
+    """
+    resource_list = []
+    for i in range(3):
+      self.stepCreateVariatedResource(sequence=sequence, sequence_list=sequence_list, **kw)
+      resource_list.append(sequence.get('resource'))
+    sequence.edit(
+      resource=None,
+      resource_list = resource_list)
+      
+    
+  def stepCreatePackingListList(self, sequence=None, sequence_list=None, **kw):
+    """
+      Create some packing lists
+    """
+    base_category_list = ['size', 'colour', 'morphology']
+    data_list = [
+      { 'source':6, 'destination':2, 'source_section':4, 'destination_section':0,
+        'source_payment':5, 'destination_payment':1, 'start_date':DateTime()-30, 'lines':[
+           {'resource':0, 'cells': [
+                             #size, colour, morphology
+               {'variation':['size/Baby', '1', '4'], 'quantity':.5},
+               {'variation':['size/Baby', '2', '4'], 'quantity':1.},
+               {'variation':['size/Baby', '3', '4'], 'quantity':1.5},
+               {'variation':['size/Baby', '1', '5'], 'quantity':2.},
+               {'variation':['size/Baby', '2', '5'], 'quantity':2.5},
+               {'variation':['size/Baby', '3', '5'], 'quantity':3.},
+
+               {'variation':['size/Child/32', '1', '4'], 'quantity':3.5},
+               {'variation':['size/Child/32', '2', '4'], 'quantity':4.},
+               {'variation':['size/Child/32', '3', '4'], 'quantity':4.5},
+               {'variation':['size/Child/32', '1', '5'], 'quantity':5.},
+               {'variation':['size/Child/32', '2', '5'], 'quantity':5.5},
+               {'variation':['size/Child/32', '3', '5'], 'quantity':6.},
+
+               {'variation':['size/Child/34', '1', '4'], 'quantity':6.5},
+               {'variation':['size/Child/34', '2', '4'], 'quantity':7.},
+               {'variation':['size/Child/34', '3', '4'], 'quantity':7.5},
+               {'variation':['size/Child/34', '1', '5'], 'quantity':8.},
+               {'variation':['size/Child/34', '2', '5'], 'quantity':8.5},
+               {'variation':['size/Child/34', '3', '5'], 'quantity':9.},
+
+               {'variation':['size/Man', '1', '4'], 'quantity':9.5},
+               {'variation':['size/Man', '2', '4'], 'quantity':10.},
+               {'variation':['size/Man', '3', '4'], 'quantity':10.5},
+               {'variation':['size/Man', '1', '5'], 'quantity':11.},
+               {'variation':['size/Man', '2', '5'], 'quantity':11.5},
+               {'variation':['size/Man', '3', '5'], 'quantity':12.},
+
+               {'variation':['size/Woman', '1', '4'], 'quantity':12.5},
+               {'variation':['size/Woman', '2', '4'], 'quantity':13.},
+               {'variation':['size/Woman', '3', '4'], 'quantity':13.5},
+               {'variation':['size/Woman', '1', '5'], 'quantity':14.},
+               {'variation':['size/Woman', '2', '5'], 'quantity':14.5},
+               {'variation':['size/Woman', '3', '5'], 'quantity':15.},
+             ]
+           }, # line end
+           {'resource':1, 'cells': [
+               {'variation':['size/Man', '2', '4'], 'quantity':15.5},
+               {'variation':['size/Man', '3', '4'], 'quantity':16.},
+             ]
+           }, # line end
+           {'resource':2, 'cells': [
+               {'variation':['size/Baby', '3', '5'], 'quantity':16.5},
+             ]
+           }  # line end
+        ] 
+      }, # packing list end
+      { 'source':6, 'destination':3, 'source_section':4, 'destination_section':0,
+        'source_payment':5, 'destination_payment':1, 'start_date':DateTime()-25, 'lines':[
+           {'resource':0, 'cells': [
+               {'variation':['size/Baby', '1', '4'], 'quantity':16.5},
+               {'variation':['size/Baby', '2', '4'], 'quantity':17.},
+               {'variation':['size/Baby', '3', '4'], 'quantity':17.5},
+               {'variation':['size/Baby', '1', '5'], 'quantity':18.},
+               {'variation':['size/Baby', '2', '5'], 'quantity':18.5},
+               {'variation':['size/Baby', '3', '5'], 'quantity':19.},
+               {'variation':['size/Woman', '2', '4'], 'quantity':19.5},
+             ]
+           }, # line end
+           {'resource':2, 'cells': [
+               {'variation':['size/Baby', '3', '5'], 'quantity':20.},
+             ]
+           } # line end
+        ]
+      }, # packing list end
+      { 'source':2, 'destination':9, 'source_section':0, 'destination_section':7,
+        'source_payment':1, 'destination_payment':8, 'start_date':DateTime()-15, 'lines':[
+           {'resource':0, 'cells': [
+               {'variation':['size/Baby', '2', '4'], 'quantity':.25},
+               {'variation':['size/Baby', '3', '4'], 'quantity':.5},
+               {'variation':['size/Baby', '1', '5'], 'quantity':.75},
+               {'variation':['size/Baby', '2', '5'], 'quantity':1.},
+               {'variation':['size/Baby', '3', '5'], 'quantity':1.25},
+
+               {'variation':['size/Child/32', '1', '4'], 'quantity':1.5},
+               {'variation':['size/Child/32', '2', '4'], 'quantity':1.75},
+               {'variation':['size/Child/32', '3', '4'], 'quantity':2.},
+               {'variation':['size/Child/32', '1', '5'], 'quantity':2.25},
+               {'variation':['size/Child/32', '2', '5'], 'quantity':2.5},
+               {'variation':['size/Child/32', '3', '5'], 'quantity':2.75},
+
+               {'variation':['size/Child/34', '1', '4'], 'quantity':3.},
+               {'variation':['size/Child/34', '2', '4'], 'quantity':3.25},
+               {'variation':['size/Child/34', '3', '4'], 'quantity':3.5},
+               {'variation':['size/Child/34', '1', '5'], 'quantity':3.75},
+               {'variation':['size/Child/34', '2', '5'], 'quantity':4.},
+               {'variation':['size/Child/34', '3', '5'], 'quantity':4.25},
+
+               {'variation':['size/Man', '1', '4'], 'quantity':4.5},
+               {'variation':['size/Man', '2', '4'], 'quantity':4.75},
+               {'variation':['size/Man', '3', '4'], 'quantity':5.},
+               {'variation':['size/Man', '1', '5'], 'quantity':5.25},
+               {'variation':['size/Man', '2', '5'], 'quantity':5.5},
+               {'variation':['size/Man', '3', '5'], 'quantity':5.75},
+
+               {'variation':['size/Woman', '1', '4'], 'quantity':6.},
+               {'variation':['size/Woman', '2', '4'], 'quantity':6.5},
+               {'variation':['size/Woman', '3', '4'], 'quantity':7.},
+               {'variation':['size/Woman', '1', '5'], 'quantity':7.5},
+               {'variation':['size/Woman', '2', '5'], 'quantity':8.},
+               {'variation':['size/Woman', '3', '5'], 'quantity':8.5},
+             ]
+           }, # line end
+           {'resource':1, 'cells': [
+               {'variation':['size/Man', '3', '4'], 'quantity':6.},
+             ]
+           }, # line end
+        ] 
+      }, # packing list end
+      { 'source':3, 'destination':9, 'source_section':0, 'destination_section':7,
+        'source_payment':1, 'destination_payment':8, 'start_date':DateTime()-10, 'lines':[
+           {'resource':0, 'cells': [
+               {'variation':['size/Baby', '1', '5'], 'quantity':7.5},
+               {'variation':['size/Baby', '2', '5'], 'quantity':5.},
+               {'variation':['size/Baby', '3', '5'], 'quantity':3.},
+             ]
+           }, # line end
+           {'resource':2, 'cells': [
+               {'variation':['size/Baby', '3', '5'], 'quantity':18.},
+             ]
+           }, # line end
+        ] 
+      }, # packing list end
+    ]
+    
+    packing_list_list = []
+    delivery_line_list = []
+    organisation_list = sequence.get('organisation_list')
+    resource_list = sequence.get('resource_list')
+    order = sequence.get('order')
+    packing_list_module = self.getPortal().getDefaultModule(self.packing_list_portal_type)
+    
+    for data in data_list:
+      # Create Packing List
+      packing_list = packing_list_module.newContent(portal_type=self.packing_list_portal_type)
+      packing_list_list.append(packing_list)
+      # Add properties
+      property_list = [x for x in data.items() if x[0] not in ('lines','start_date')]
+      property_list = [(x[0], organisation_list[x[1]].getRelativeUrl()) for x in property_list] + \
+                      [x for x in data.items() if x[0] in ('start_date',)]
+      property_dict = {}
+      for (id, value) in property_list: property_dict[id] = value
+      packing_list.edit(**property_dict)
+      for line in data['lines']:
+        # Create Packing List Line
+        packing_list_line = packing_list.newContent(portal_type=self.packing_list_line_portal_type)
+        delivery_line_list.append(packing_list_line)
+        resource_value = resource_list[line['resource']]
+        resource_value.setVariationBaseCategoryList(base_category_list)
+        category_list = packing_list_line.getCategoryList()
+        variation_category_list = resource_value.getVariationRangeCategoryList(base_category_list=['size']) + \
+            ['colour/' + x.getRelativeUrl() for x in resource_value.objectValues(portal_type='Apparel Model Colour Variation')] + \
+            ['morphology/' + x.getRelativeUrl() for x in resource_value.objectValues(portal_type='Apparel Model Morphology Variation')]
+
+        packing_list_line.edit(
+            resource_value = resource_value,
+            default_resource_value = resource_value,
+            categories = category_list + [x for x in variation_category_list if x not in category_list]
+            )
+        
+        # Set cell range
+        packing_list_line.updateCellRange(base_id='quantity')
+        base_category_dict = {}
+        for i in range(len(base_category_list)):
+          base_category_dict[base_category_list[i]] = i
+        
+        # Set cells
+        for cell in line['cells']:
+          variation = cell['variation']
+          for i in range(len(variation)):
+            c = variation[i]
+            if len(c.split('/')) == 1:
+              variation[i] = '%s/%s' % (base_category_list[i], resource_value[c].getRelativeUrl())
+          new_variation = []
+          for bc in packing_list_line.getVariationBaseCategoryList():
+            new_variation.append(variation[base_category_dict[bc]])
+          variation = new_variation
+          packing_list_cell = packing_list_line.newCell(base_id='quantity', *variation)
+          packing_list_cell.edit(
+              quantity = cell['quantity'],
+              predicate_category_list = variation,
+              variation_category_list = variation,
+              mapped_value_property_list = ['quantity'],
+              )
+    sequence.edit(packing_list_list = packing_list_list)
+    mvt = self.getPortal().portal_simulation.newContent(portal_type='Simulation Movement')
+    mvt.edit(delivery_value_list = delivery_line_list)
+    
+    
+  def stepCreateTestingCategories(self, sequence=None, sequence_list=None, **kw):
+    """
+      Create some categories and affect them to resources and organisations
+    """
+    category_tool = self.getPortal().portal_categories
+    organisation_list = sequence.get('organisation_list')
+    resource_list = sequence.get('resource_list')
+    
+    bc = category_tool.newContent(portal_type = 'Base Category', id = 'testing_category')
+    self.createCategory(bc, ['a', ['aa', 'ab'], 'o', 'z', ['za', 'zb', ['zba', 'zbb'], 'zc'] ])
+    self.stepTic()
+    
+    category_org_list = [ ['testing_category/a/aa', 'testing_category/o'], # 0
+                          ['testing_category/a/aa', 'testing_category/z'], # 1
+                          ['testing_category/a/aa', 'testing_category/z/zb/zba'], # 2
+                          ['testing_category/a/aa', 'testing_category/z/zb/zbb'], # 3
+                          ['testing_category/o', 'testing_category/z'], # 4
+                          ['testing_category/z', 'testing_category/z/zc'], # 5
+                          ['testing_category/z', 'testing_category/a/ab'],# 6
+                          ['testing_category/o', 'testing_category/z/zc'], # 7
+                          ['testing_category/z', 'testing_category/a/ab'], # 8
+                          ['testing_category/a', 'testing_category/z/zb'],# 9
+                        ]
+                        
+    category_res_list = [ ['testing_category/a/aa', 'testing_category/z'],
+                          ['testing_category/a/aa', 'testing_category/z/za'],
+                          ['testing_category/a/aa', 'testing_category/o']
+                        ]
+    
+    for i in range(len(category_org_list)):
+      organisation = organisation_list[i]
+      new_categories = category_org_list[i]
+      organisation.edit(categories = organisation.getCategoryList() + new_categories)
+    for i in range(len(category_res_list)):
+      resource = resource_list[i]
+      new_categories = category_res_list[i]
+      resource.edit(categories = resource.getCategoryList() + new_categories)
+      
+
+  def stepTestGetInventoryOnNode(self, sequence=None, sequence_list=None, **kw):
+    """
+      Test getInventory on each node
+    """
+    organisation_list = sequence.get('organisation_list')
+    expected_values_list = [
+      {'id':2, 'values':[{'date':DateTime()-28, 'inventory':280.5},
+                         {'date':DateTime()-20, 'inventory':280.5},
+                         {'date':DateTime()-12, 'inventory':162.},
+                         {'date':DateTime(),    'inventory':162.},]
+      },
+      {'id':3, 'values':[{'date':DateTime()-28, 'inventory':0.},
+                         {'date':DateTime()-20, 'inventory':146.},
+                         {'date':DateTime()-12, 'inventory':146.},
+                         {'date':DateTime(),    'inventory':112.5},]
+      },
+      {'id':6, 'values':[{'date':DateTime()-28, 'inventory':-280.5},
+                         {'date':DateTime()-20, 'inventory':-426.5},
+                         {'date':DateTime()-12, 'inventory':-426.5},
+                         {'date':DateTime(),    'inventory':-426.5},]
+      },
+      {'id':9, 'values':[{'date':DateTime()-28, 'inventory':0.},
+                         {'date':DateTime()-20, 'inventory':0.},
+                         {'date':DateTime()-12, 'inventory':118.5},
+                         {'date':DateTime(),    'inventory':152.},]
+      },
+    ]
+ 
+    for expected_values in expected_values_list:
+      organisation = organisation_list[expected_values['id']]
+      url = organisation.getRelativeUrl()
+      values = expected_values['values']
+      for value in values:
+        date = value['date']
+        e_inventory = value['inventory']
+        self._testGetInventory(expected=e_inventory, at_date=date, node=url)
+  
+
+  def stepTestGetInventoryOnPayment(self, sequence=None, sequence_list=None, **kw):
+    """
+      Test getInventory on each payment
+    """
+    organisation_list = sequence.get('organisation_list')
+    expected_values_list = [
+      {'id':1, 'values':[{'date':DateTime()-28, 'inventory':280.5},
+                         {'date':DateTime()-20, 'inventory':426.5},
+                         {'date':DateTime()-12, 'inventory':308.},
+                         {'date':DateTime(),    'inventory':274.5},]
+      },
+      {'id':5, 'values':[{'date':DateTime()-28, 'inventory':-280.5},
+                         {'date':DateTime()-20, 'inventory':-426.5},
+                         {'date':DateTime()-12, 'inventory':-426.5},
+                         {'date':DateTime(),    'inventory':-426.5},]
+      },
+      {'id':8, 'values':[{'date':DateTime()-28, 'inventory':0.},
+                         {'date':DateTime()-20, 'inventory':0.},
+                         {'date':DateTime()-12, 'inventory':118.5},
+                         {'date':DateTime(),    'inventory':152.},]
+      },
+    ]
+ 
+    for expected_values in expected_values_list:
+      organisation = organisation_list[expected_values['id']]
+      url = organisation.getRelativeUrl()
+      values = expected_values['values']
+      for value in values:
+        date = value['date']
+        e_inventory = value['inventory']
+        self._testGetInventory(expected=e_inventory, at_date=date, payment=url)
+        
+
+  def stepTestGetInventoryOnSection(self, sequence=None, sequence_list=None, **kw):
+    """
+      Test getInventory on each section
+    """
+    organisation_list = sequence.get('organisation_list')
+    expected_values_list = [
+      {'id':0, 'values':[{'date':DateTime()-28, 'inventory':280.5},
+                         {'date':DateTime()-20, 'inventory':426.5},
+                         {'date':DateTime()-12, 'inventory':308.},
+                         {'date':DateTime(),    'inventory':274.5},]
+      },
+      {'id':4, 'values':[{'date':DateTime()-28, 'inventory':-280.5},
+                         {'date':DateTime()-20, 'inventory':-426.5},
+                         {'date':DateTime()-12, 'inventory':-426.5},
+                         {'date':DateTime(),    'inventory':-426.5},]
+      },
+      {'id':7, 'values':[{'date':DateTime()-28, 'inventory':0.},
+                         {'date':DateTime()-20, 'inventory':0.},
+                         {'date':DateTime()-12, 'inventory':118.5},
+                         {'date':DateTime(),    'inventory':152.},]
+      },
+    ]
+ 
+    for expected_values in expected_values_list:
+      organisation = organisation_list[expected_values['id']]
+      url = organisation.getRelativeUrl()
+      values = expected_values['values']
+      for value in values:
+        date = value['date']
+        e_inventory = value['inventory']
+        self._testGetInventory(expected=e_inventory, at_date=date, section=url)
+        
+        
+  def stepTestGetInventoryOnMirrorSection(self, sequence=None, sequence_list=None, **kw):
+    """
+      Test getInventory on each mirror section
+    """
+    organisation_list = sequence.get('organisation_list')
+    expected_values_list = [
+      {'id':0, 'values':[{'date':DateTime()-28, 'inventory':-280.5},
+                         {'date':DateTime()-20, 'inventory':-426.5},
+                         {'date':DateTime()-12, 'inventory':-308.},
+                         {'date':DateTime(),    'inventory':-274.5},]
+      },
+      {'id':4, 'values':[{'date':DateTime()-28, 'inventory':280.5},
+                         {'date':DateTime()-20, 'inventory':426.5},
+                         {'date':DateTime()-12, 'inventory':426.5},
+                         {'date':DateTime(),    'inventory':426.5},]
+      },
+      {'id':7, 'values':[{'date':DateTime()-28, 'inventory':0.},
+                         {'date':DateTime()-20, 'inventory':0.},
+                         {'date':DateTime()-12, 'inventory':-118.5},
+                         {'date':DateTime(),    'inventory':-152.},]
+      },
+    ]
+ 
+    for expected_values in expected_values_list:
+      organisation = organisation_list[expected_values['id']]
+      url = organisation.getRelativeUrl()
+      values = expected_values['values']
+      for value in values:
+        date = value['date']
+        e_inventory = value['inventory']
+        self._testGetInventory(expected=e_inventory, at_date=date, mirror_section=url)
+        
+        
+  def stepTestGetInventoryOnResource(self, sequence=None, sequence_list=None, **kw):
+    """
+      Test getInventory on each resource
+    """
+    resource_list = sequence.get('resource_list')
+    organisation_list = sequence.get('organisation_list')
+    expected_values_list = [
+      {'id':0, 'values':[{'date':DateTime()-28, 'inventory':232.5},
+                         {'date':DateTime()-20, 'inventory':358.5},
+                         {'date':DateTime()-12, 'inventory':246.},
+                         {'date':DateTime(),    'inventory':230.5},]
+      },
+      {'id':1, 'values':[{'date':DateTime()-28, 'inventory':31.5},
+                         {'date':DateTime()-20, 'inventory':31.5},
+                         {'date':DateTime()-12, 'inventory':25.5},
+                         {'date':DateTime(),    'inventory':25.5},]
+      },
+      {'id':2, 'values':[{'date':DateTime()-28, 'inventory':16.5},
+                         {'date':DateTime()-20, 'inventory':36.5},
+                         {'date':DateTime()-12, 'inventory':36.5},
+                         {'date':DateTime(),    'inventory':18.5},]
+      },
+    ]
+ 
+    organisation_url = organisation_list[0].getRelativeUrl()
+    for expected_values in expected_values_list:
+      resource = resource_list[expected_values['id']]
+      url = resource.getRelativeUrl()
+      values = expected_values['values']
+      for value in values:
+        date = value['date']
+        e_inventory = value['inventory']
+        self._testGetInventory(expected=e_inventory, at_date=date, resource=url, section=organisation_url)
+        
+        
+  def stepTestGetInventoryOnVariationText(self, sequence=None, sequence_list=None, **kw):
+    """
+    
+    """
+    simulation = self.getPortal().portal_simulation
+    delivery = sequence.get('packing_list_list')[0]
+    expected_values_list = [
+      {'text':delivery['1']['quantity_0_0_0'],
+              'values':[{'inventory':17.},]
+      },
+    ]
+    
+    organisation_list = sequence.get('organisation_list')
+    organisation_url = organisation_list[0].getRelativeUrl()
+    for expected_values in expected_values_list:
+      variation_text = expected_values['text'].getVariationText()
+      values = expected_values['values']
+      for value in values:
+        e_inventory = value['inventory']
+        self._testGetInventory(expected=e_inventory, variation_text=variation_text, section=organisation_url)
+    
+    
+  def stepTestGetInventoryOnVariationCategory(self, sequence=None, sequence_list=None, **kw):
+    """
+      Test getInventory on some variation categories
+    """
+    resource_list = sequence.get('resource_list')
+    organisation_list = sequence.get('organisation_list')
+    expected_values_list = [
+      {'id':'size/Baby', 'values':[{'date':DateTime()-28, 'inventory':27.},
+                                   {'date':DateTime()-20, 'inventory':153.5},
+                                   {'date':DateTime()-12, 'inventory':149.75},
+                                   {'date':DateTime(),    'inventory':116.25},]
+      },
+      {'id':'size/Child/32', 'values':[{'date':DateTime()-28, 'inventory':28.5},
+                                       {'date':DateTime()-20, 'inventory':28.5},
+                                       {'date':DateTime()-12, 'inventory':15.75},
+                                       {'date':DateTime(),    'inventory':15.75},]
+      },
+      {'id':'size/Child/34', 'values':[{'date':DateTime()-28, 'inventory':46.5},
+                                       {'date':DateTime()-20, 'inventory':46.5},
+                                       {'date':DateTime()-12, 'inventory':24.75},
+                                       {'date':DateTime(),    'inventory':24.75},]
+      },
+      {'id':'size/Man', 'values':[{'date':DateTime()-28, 'inventory':96.},
+                                  {'date':DateTime()-20, 'inventory':96.},
+                                  {'date':DateTime()-12, 'inventory':59.25},
+                                  {'date':DateTime(),    'inventory':59.25},]
+      },
+      {'id':'size/Woman', 'values':[{'date':DateTime()-28, 'inventory':82.5},
+                                    {'date':DateTime()-20, 'inventory':102.},
+                                    {'date':DateTime()-12, 'inventory':58.5},
+                                    {'date':DateTime(),    'inventory':58.5},]
+      },
+      {'id':['size/Baby', 'colour/' + resource_list[0]['3'].getRelativeUrl()],
+                        'values':[{'date':DateTime()-28, 'inventory':105.},
+                                  {'date':DateTime()-20, 'inventory':231.5},
+                                  {'date':DateTime()-12, 'inventory':189.},
+                                  {'date':DateTime(),    'inventory':155.5},]
+      },
+      {'id':['size/Man', 'colour/' + resource_list[0]['3'].getRelativeUrl(), 'morphology/' + resource_list[0]['4'].getRelativeUrl()],
+                        'values':[{'date':DateTime()-28, 'inventory':204.},
+                                  {'date':DateTime()-20, 'inventory':293.5},
+                                  {'date':DateTime()-12, 'inventory':204.75},
+                                  {'date':DateTime(),    'inventory':201.75},]
+      },
+      
+      
+    ]
+ 
+    organisation_url = organisation_list[0].getRelativeUrl()
+    for expected_values in expected_values_list:
+      category_list = expected_values['id']
+      values = expected_values['values']
+      for value in values:
+        date = value['date']
+        e_inventory = value['inventory']
+        self._testGetInventory(expected=e_inventory, at_date=date, variation_category=category_list, section=organisation_url)
+        
+        
+  def stepTestGetInventoryWithOmitOutput(self, sequence=None, sequence_list=None, **kw):
+    """
+      Test getInventory on each node with omit_output
+    """
+    organisation_list = sequence.get('organisation_list')
+    expected_values_list = [
+      {'id':2, 'values':[{'date':DateTime()-28, 'inventory':280.5},
+                         {'date':DateTime()-20, 'inventory':280.5},
+                         {'date':DateTime()-12, 'inventory':280.5},
+                         {'date':DateTime(),    'inventory':280.5},]
+      },
+      {'id':3, 'values':[{'date':DateTime()-28, 'inventory':0.},
+                         {'date':DateTime()-20, 'inventory':146.},
+                         {'date':DateTime()-12, 'inventory':146.},
+                         {'date':DateTime(),    'inventory':146.},]
+      },
+      {'id':6, 'values':[{'date':DateTime()-28, 'inventory':0.},
+                         {'date':DateTime()-20, 'inventory':0.},
+                         {'date':DateTime()-12, 'inventory':0.},
+                         {'date':DateTime(),    'inventory':0.},]
+      },
+      {'id':9, 'values':[{'date':DateTime()-28, 'inventory':0.},
+                         {'date':DateTime()-20, 'inventory':0.},
+                         {'date':DateTime()-12, 'inventory':118.5},
+                         {'date':DateTime(),    'inventory':152.},]
+      },
+    ]
+ 
+    for expected_values in expected_values_list:
+      organisation = organisation_list[expected_values['id']]
+      url = organisation.getRelativeUrl()
+      values = expected_values['values']
+      for value in values:
+        date = value['date']
+        e_inventory = value['inventory']
+        self._testGetInventory(expected=e_inventory, at_date=date, node=url, omit_output=1)
+        
+        
+  def stepTestGetInventoryWithOmitInput(self, sequence=None, sequence_list=None, **kw):
+    """
+      Test getInventory on each node with omit_input
+    """
+    organisation_list = sequence.get('organisation_list')
+    expected_values_list = [
+      {'id':2, 'values':[{'date':DateTime()-28, 'inventory':0.},
+                         {'date':DateTime()-20, 'inventory':0.},
+                         {'date':DateTime()-12, 'inventory':-118.5},
+                         {'date':DateTime(),    'inventory':-118.5},]
+      },
+      {'id':3, 'values':[{'date':DateTime()-28, 'inventory':0.},
+                         {'date':DateTime()-20, 'inventory':0.},
+                         {'date':DateTime()-12, 'inventory':0.},
+                         {'date':DateTime(),    'inventory':-33.5},]
+      },
+      {'id':6, 'values':[{'date':DateTime()-28, 'inventory':-280.5},
+                         {'date':DateTime()-20, 'inventory':-426.5},
+                         {'date':DateTime()-12, 'inventory':-426.5},
+                         {'date':DateTime(),    'inventory':-426.5},]
+      },
+      {'id':9, 'values':[{'date':DateTime()-28, 'inventory':0.},
+                         {'date':DateTime()-20, 'inventory':0.},
+                         {'date':DateTime()-12, 'inventory':0.},
+                         {'date':DateTime(),    'inventory':0.},]
+      },
+    ]
+ 
+    for expected_values in expected_values_list:
+      organisation = organisation_list[expected_values['id']]
+      url = organisation.getRelativeUrl()
+      values = expected_values['values']
+      for value in values:
+        date = value['date']
+        e_inventory = value['inventory']
+        self._testGetInventory(expected=e_inventory, at_date=date, node=url, omit_input=1)
+                
+
+  def stepTestGetInventoryOnSectionCategory(self, sequence=None, sequence_list=None, **kw):
+    """
+      Test getInventory with a section_category argument
+    """
+    organisation_list = sequence.get('organisation_list')
+    expected_values_list = [
+      {'id':'testing_category/a/aa', 'values':[{'inventory':274.5},] },
+      {'id':'testing_category/z/zc', 'values':[{'inventory':152.},] },
+      {'id':'testing_category/z', 'values':[{'inventory':-274.5},] },
+      {'id':'testing_category/o', 'values':[{'inventory':0.},] },
+    ]
+ 
+    for expected_values in expected_values_list:
+      category = expected_values['id']
+      values = expected_values['values']
+      for value in values:
+        e_inventory = value['inventory']
+        self._testGetInventory(expected=e_inventory, section_category=category)
+        
+        
+  def stepTestGetInventoryOnPaymentCategory(self, sequence=None, sequence_list=None, **kw):
+    """
+      Test getInventory with a payment_category argument
+    """
+    organisation_list = sequence.get('organisation_list')
+    expected_values_list = [
+      {'id':'testing_category/a/aa', 'values':[{'inventory':274.5},] },
+      {'id':'testing_category/z/zc', 'values':[{'inventory':-426.5},] },
+      {'id':'testing_category/a/ab', 'values':[{'inventory':152.},] },
+      {'id':'testing_category/a', 'values':[{'inventory':426.5},] },
+    ]
+ 
+    for expected_values in expected_values_list:
+      category = expected_values['id']
+      values = expected_values['values']
+      for value in values:
+        e_inventory = value['inventory']
+        self._testGetInventory(expected=e_inventory, payment_category=category)
+        
+        
+  def stepTestGetInventoryOnNodeCategory(self, sequence=None, sequence_list=None, **kw):
+    """
+      Test getInventory with a node_category argument
+    """
+    organisation_list = sequence.get('organisation_list')
+    expected_values_list = [
+      {'id':'testing_category/z/zb/zba', 'values':[{'inventory':162.},] },
+      {'id':'testing_category/z/zb/zbb', 'values':[{'inventory':112.5},] },
+      {'id':'testing_category/a/ab', 'values':[{'inventory':-426.5},] },
+      {'id':'testing_category/a', 'values':[{'inventory':0.},] },
+      {'id':'testing_category/z', 'values':[{'inventory':0.},] },
+    ]
+       
+    for expected_values in expected_values_list:
+      category = expected_values['id']
+      values = expected_values['values']
+      for value in values:
+        e_inventory = value['inventory']
+        self._testGetInventory(expected=e_inventory, node_category=category)
+        
+        
+  def stepTestGetInventoryOnMirrorSectionCategory(self, sequence=None, sequence_list=None, **kw):
+    """
+      Test getInventory with a section_category argument
+    """
+    organisation_list = sequence.get('organisation_list')
+    expected_values_list = [
+      {'id':'testing_category/a/aa', 'values':[{'inventory':-274.5},] },
+      {'id':'testing_category/z/zc', 'values':[{'inventory':-152.},] },
+      {'id':'testing_category/z', 'values':[{'inventory':274.5},] },
+      {'id':'testing_category/o', 'values':[{'inventory':0.},] },
+    ]
+ 
+    for expected_values in expected_values_list:
+      category = expected_values['id']
+      values = expected_values['values']
+      for value in values:
+        e_inventory = value['inventory']
+        self._testGetInventory(expected=e_inventory, mirror_section_category=category)
+          
+  
+  def stepTestGetInventoryOnResourceCategory(self, sequence=None, sequence_list=None, **kw):
+    """
+      Test getInventory with a resource_category argument
+    """
+    organisation_list = sequence.get('organisation_list')
+    expected_values_list = [
+      {'id':'testing_category/a/aa', 'values':[{'inventory':274.5},] },
+      {'id':'testing_category/o', 'values':[{'inventory':18.5},] },
+      {'id':'testing_category/z/za', 'values':[{'inventory':25.5},] },
+      {'id':'testing_category/z', 'values':[{'inventory':256.},] },
+    ]
+ 
+    for expected_values in expected_values_list:
+      category = expected_values['id']
+      values = expected_values['values']
+      for value in values:
+        e_inventory = value['inventory']
+        self._testGetInventory(expected=e_inventory, resource_category=category, section=organisation_list[0].getRelativeUrl())
+        
+        
+  def _testGetInventory(self, expected, **kw):
+    """
+      Shared testing method
+    """
+    simulation = self.getPortal().portal_simulation
+    e_inventory = expected
+    LOG('Testing inventory with args :', 0, kw)
+    a_inventory = simulation.getInventory(**kw)
+    if e_inventory != a_inventory:
+       LOG('TEST ERROR : Inventory differs between expected (%s) and real (%s) quantities' % (repr(e_inventory), repr(a_inventory)),0,'')
+       LOG('SQL Query was : ', 0, repr(simulation.getInventory(src__=1, **kw)))
+       self.failUnless(0)
+
+               
+  def stepTestGetInventoryOnSimulationState(self, sequence=None, sequence_list=None, **kw):
+    """
+      Test getInventory with some simulation states, by using
+      methods getCurrentInventory, getAvailableInventory and getFutureInventory
+    """
+    packing_list_workflow = 'packing_list_workflow'
+    state_variable = 'simulation_state'
+    packing_list_list = sequence.get('packing_list_list')
+    workflow_tool = self.getPortal().portal_workflow
+    simulation = self.getPortal().portal_simulation
+    transit_simulation_state = ['started']
+    
+    transition_list = [
+       {'id':0, 'action':'confirm_action'},
+       {'id':0, 'action':'set_ready_action'},
+              {'id':1, 'action':'confirm_action'},
+       {'id':0, 'action':'start_action'},
+                     {'id':2, 'action':'confirm_action'},
+       {'id':0, 'action':'stop_action'},
+                     {'id':2, 'action':'set_ready_action'},
+                     {'id':2, 'action':'start_action'},
+                            {'id':3, 'action':'confirm_action'},
+                            {'id':3, 'action':'set_ready_action'},
+                            {'id':3, 'action':'start_action'},
+                            {'id':3, 'action':'stop_action'},
+    ]
+    
+    expected_values_list = [
+     #( without omit_transit, with omit_transit)
+      ({'Current':  0.  , 'Available':  0.  , 'Future':  0.  }, {'Current':  0.  , 'Available':  0.  , 'Future':  0.  }),
+      ({'Current':  0.  , 'Available':280.5 , 'Future':280.5 }, {'Current':  0.  , 'Available':280.5 , 'Future':280.5 }),
+      ({'Current':  0.  , 'Available':280.5 , 'Future':280.5 }, {'Current':  0.  , 'Available':280.5 , 'Future':280.5 }),
+      ({'Current':  0.  , 'Available':426.5 , 'Future':426.5 }, {'Current':  0.  , 'Available':426.5 , 'Future':426.5 }),
+      ({'Current':280.5 , 'Available':426.5 , 'Future':426.5 }, {'Current':  0.  , 'Available':146.  , 'Future':146.  }),
+      ({'Current':280.5 , 'Available':308.  , 'Future':308.  }, {'Current':  0.  , 'Available': 27.5 , 'Future': 27.5 }),
+      ({'Current':280.5 , 'Available':308.  , 'Future':308.  }, {'Current':280.5 , 'Available':308.  , 'Future':308.  }),
+      ({'Current':280.5 , 'Available':308.  , 'Future':308.  }, {'Current':280.5 , 'Available':308.  , 'Future':308.  }),
+      ({'Current':162.  , 'Available':308.  , 'Future':308.  }, {'Current':162.  , 'Available':308.  , 'Future':308.  }),
+      ({'Current':162.  , 'Available':274.5 , 'Future':274.5 }, {'Current':162.  , 'Available':274.5 , 'Future':274.5 }),
+      ({'Current':162.  , 'Available':274.5 , 'Future':274.5 }, {'Current':162.  , 'Available':274.5 , 'Future':274.5 }),
+      ({'Current':128.5 , 'Available':274.5 , 'Future':274.5 }, {'Current':128.5 , 'Available':274.5 , 'Future':274.5 }),
+      ({'Current':128.5 , 'Available':274.5 , 'Future':274.5 }, {'Current':128.5 , 'Available':274.5 , 'Future':274.5 }),
+    ]
+ 
+    organisation_list = sequence.get('organisation_list')
+    organisation_url = organisation_list[0].getRelativeUrl()
+    date = DateTime()
+    
+    def _testWithState(expected_values, omit_transit):
+      # Get current workflow states to add it to the log
+      state_list = []
+      for packing_list in packing_list_list:
+        state_list.append(workflow_tool.getStatusOf(packing_list_workflow, packing_list)[state_variable])
+      
+      LOG('Testing with these workflow states :', 0, state_list)
+      for name, e_inventory in expected_values.items():
+        method_name = 'get%sInventory' % name
+        method = getattr(simulation, method_name, None)
+        if method is None:
+          LOG('TEST ERROR : Simulation Tool has no %s method' % method_name, 0, '')
+          self.failUnless(0)
+        a_inventory = method(section=organisation_url, 
+                             omit_transit=omit_transit,
+                             transit_simulation_state=transit_simulation_state,
+                             at_date=date)
+        if a_inventory != e_inventory:
+          LOG('TEST ERROR : Inventory quantity differs between expected (%s) and real (%s) quantities' % (repr(e_inventory), repr(a_inventory)), 0, 'with method %s and omit_transit=%s' % (method_name, omit_transit))
+          LOG('SQL Query was :', 0, method(section=organisation_url, 
+                             omit_transit=omit_transit,
+                             transit_simulation_state=transit_simulation_state,
+                             at_date=date, src__=1))
+          self.failUnless(0)
+        
+    # First, test with draft state everywhere
+    LOG('Testing Inventory with every Packing List in draft state...', 0, '')
+    for omit_transit in (0,1):
+      _testWithState(expected_values_list[0][omit_transit], omit_transit)
+      
+    i = 0
+    for expected_values in expected_values_list[1:]:
+      transition_step = transition_list[i]
+      transited_pl = packing_list_list[transition_step['id']]
+      action = transition_step['action']
+      LOG("Transiting '%s' on packing list %s" % (action, transition_step['id']), 0, '')
+      workflow_tool.doActionFor(transited_pl, action, packing_list_workflow)
+      transited_pl.recursiveImmediateReindexObject()
+      self.stepTic()
+      get_transaction().commit()
+      
+      for omit_transit in (0,1):
+        values = expected_values[omit_transit]
+        _testWithState(values, omit_transit)
+        
+      i += 1
+
+      
+  def stepTestGetInventoryWithSelectionReport(self, sequence=None, sequence_list=None, **kw):
+    """
+    """
+    expected_values_list = [
+      {'id':2, 'values':[{'date':DateTime()-28, 'inventory':280.5},
+                         {'date':DateTime()-20, 'inventory':280.5},
+                         {'date':DateTime()-12, 'inventory':162.},
+                         {'date':DateTime(),    'inventory':162.},]
+      },
+      {'id':3, 'values':[{'date':DateTime()-28, 'inventory':0.},
+                         {'date':DateTime()-20, 'inventory':146.},
+                         {'date':DateTime()-12, 'inventory':146.},
+                         {'date':DateTime(),    'inventory':112.5},]
+      },
+      {'id':6, 'values':[{'date':DateTime()-28, 'inventory':-280.5},
+                         {'date':DateTime()-20, 'inventory':-426.5},
+                         {'date':DateTime()-12, 'inventory':-426.5},
+                         {'date':DateTime(),    'inventory':-426.5},]
+      },
+      {'id':9, 'values':[{'date':DateTime()-28, 'inventory':0.},
+                         {'date':DateTime()-20, 'inventory':0.},
+                         {'date':DateTime()-12, 'inventory':118.5},
+                         {'date':DateTime(),    'inventory':152.},]
+      },
+    ]
+ 
+#     for expected_values in expected_values_list:
+#       organisation = organisation_list[expected_values['id']]
+#       url = organisation.getRelativeUrl()
+#       values = expected_values['values']
+#       for value in values:
+#         date = value['date']
+#         e_inventory = value['inventory']
+#         self._testGetInventory(expected=e_inventory, at_date=date, node=url)
+        
+    simulation = self.getPortal().portal_simulation
+    resource_list = sequence.get('resource_list')
+    portal = self.getPortal()
+    resource_module = portal.getDefaultModule(self.resource_portal_type)
+    tree_list = makeTreeList(resource_module, simulation, None, "site", None, 0, None, None, "toto_selection", 10)
+    LOG('report_tree :', 0, tree_list)
+    # makeTreeList(here, form, root_dict, report_path, base_category, depth, unfolded_list, form_id, selection_name, report_depth, is_report_opened=1, sort_on = (('id', 'ASC'),))
+    #LOG('getInventory :', 0, simulation.getInventory(node=0, at_date=DateTime() - 28, selection_report=1))
+    for line in tree_list:
+      # {'Place1':portal.portal_categories.site.Place1}
+      LOG('asSqlExpression on line %s :' % line[0], 0, line[4].asSqlExpression())
+    tree_list = makeTreeList(resource_module, simulation, None, "site/Place1", None, 1, None, None, "toto_selection", 10)
+    LOG('report_tree :', 0, tree_list)
+    # makeTreeList(here, form, root_dict, report_path, base_category, depth, unfolded_list, form_id, selection_name, report_depth, is_report_opened=1, sort_on = (('id', 'ASC'),))
+    #LOG('getInventory :', 0, simulation.getInventory(node=0, at_date=DateTime() - 28, selection_report=1))
+    for line in tree_list:
+      #LOG('getInventory on line %s :' % line[0], 0, simulation.getInventory(section=0, 
+      LOG('asSqlExpression on line %s :' % line[0], 0, line[4].asSqlExpression())
+      
+      
+  def stepTestGetInventoryListOnSection(self, sequence=None, sequence_list=None, **kw):
+    """
+      Test getInventoryList on a section
+    """
+    organisation_list = sequence.get('organisation_list')
+    resource_list = sequence.get('resource_list')
+    
+    # Build expected list
+    expected_list = []
+    for i in range(1, 31):
+      quantity = (i + 0.) / 2
+      expected_list.append({ 'node_relative_url': 2, 'section_relative_url':0, 'resource_relative_url':0, 'inventory':quantity })
+    for i in range(31, 33):
+      quantity = (i + 0.) / 2
+      expected_list.append({ 'node_relative_url': 2, 'section_relative_url':0, 'resource_relative_url':1, 'inventory':quantity })
+    for i in range(33, 34):
+      quantity = (i + 0.) / 2
+      expected_list.append({ 'node_relative_url': 2, 'section_relative_url':0, 'resource_relative_url':2, 'inventory':quantity })
+    for i in range(33, 40):
+      quantity = (i + 0.) / 2
+      expected_list.append({ 'node_relative_url': 3, 'section_relative_url':0, 'resource_relative_url':0, 'inventory':quantity })
+    for i in range(40, 41):
+      quantity = (i + 0.) / 2
+      expected_list.append({ 'node_relative_url': 3, 'section_relative_url':0, 'resource_relative_url':2, 'inventory':quantity })
+    for i in range(1, 24):
+      quantity = (i + 0.) / 4
+      expected_list.append({ 'node_relative_url': 2, 'section_relative_url':0, 'resource_relative_url':0, 'inventory':-quantity })
+    for i in range(12, 18):
+      quantity = (i + 0.) / 2
+      expected_list.append({ 'node_relative_url': 2, 'section_relative_url':0, 'resource_relative_url':0, 'inventory':-quantity })
+    for i in range(12, 13):
+      quantity = (i + 0.) / 2
+      expected_list.append({ 'node_relative_url': 2, 'section_relative_url':0, 'resource_relative_url':1, 'inventory':-quantity })
+    for i in [7.5, 5, 3]:
+      quantity = (i + 0.)
+      expected_list.append({ 'node_relative_url': 3, 'section_relative_url':0, 'resource_relative_url':0, 'inventory':-quantity })
+    for i in [18]:
+      quantity = (i + 0.)
+      expected_list.append({ 'node_relative_url': 3, 'section_relative_url':0, 'resource_relative_url':2, 'inventory':-quantity })
+    for i in [ [2,0,0], [2,0,1], [2,0,2], [3,0,0], [3,0,2],
+               [2,0,0], [2,0,1], [3,0,0], [3,0,2] ]:
+      expected_list.append({ 'node_relative_url': i[0], 'section_relative_url':i[1], 'resource_relative_url':i[2], 'inventory':None })
+    
+    item_dict = {'node':organisation_list, 'section':organisation_list, 'resource':resource_list}
+    expected_l = expected_list[:]
+    for expected in expected_l:
+      for attribute in [x for x in expected.keys() if x.endswith('_relative_url')]:
+        attr_name = attribute.split('_')[0]
+        expected[attribute] = item_dict[attr_name][expected[attribute]].getRelativeUrl()
+    self._testGetInventoryList(expected=expected_l, section=organisation_list[0].getRelativeUrl(), omit_simulation=1)
+    
+    
+  def stepTestGetInventoryListOnNode(self, sequence=None, sequence_list=None, **kw):
+    """
+      Test getInventoryList on a Node
+    """
+    organisation_list = sequence.get('organisation_list')
+    resource_list = sequence.get('resource_list')
+    
+    # Build expected list
+    expected_list = []
+    for i in range(1, 31):
+      quantity = (i + 0.) / 2
+      expected_list.append({ 'node_relative_url': 2, 'section_relative_url':0, 'resource_relative_url':0, 'inventory':quantity })
+    for i in range(31, 33):
+      quantity = (i + 0.) / 2
+      expected_list.append({ 'node_relative_url': 2, 'section_relative_url':0, 'resource_relative_url':1, 'inventory':quantity })
+    for i in range(33, 34):
+      quantity = (i + 0.) / 2
+      expected_list.append({ 'node_relative_url': 2, 'section_relative_url':0, 'resource_relative_url':2, 'inventory':quantity })
+    for i in range(1, 24):
+      quantity = (i + 0.) / 4
+      expected_list.append({ 'node_relative_url': 2, 'section_relative_url':0, 'resource_relative_url':0, 'inventory':-quantity })
+    for i in range(12, 18):
+      quantity = (i + 0.) / 2
+      expected_list.append({ 'node_relative_url': 2, 'section_relative_url':0, 'resource_relative_url':0, 'inventory':-quantity })
+    for i in range(12, 13):
+      quantity = (i + 0.) / 2
+      expected_list.append({ 'node_relative_url': 2, 'section_relative_url':0, 'resource_relative_url':1, 'inventory':-quantity })
+    for i in [ [2,0,0], [2,0,1], [2,0,2], [2,0,0], [2,0,1] ]:
+      expected_list.append({ 'node_relative_url': i[0], 'section_relative_url':i[1], 'resource_relative_url':i[2], 'inventory':None })
+    
+    item_dict = {'node':organisation_list, 'section':organisation_list, 'resource':resource_list}
+    expected_l = expected_list[:]
+    for expected in expected_l:
+      for attribute in [x for x in expected.keys() if x.endswith('_relative_url')]:
+        attr_name = attribute.split('_')[0]
+        expected[attribute] = item_dict[attr_name][expected[attribute]].getRelativeUrl()
+    self._testGetInventoryList(expected=expected_l, node=organisation_list[2].getRelativeUrl(), omit_simulation=1)    
+
+    
+  def stepTestGetInventoryListWithOmitInput(self, sequence=None, sequence_list=None, **kw):
+    """
+      Test getInventoryList on a section with omit_input
+    """
+    organisation_list = sequence.get('organisation_list')
+    resource_list = sequence.get('resource_list')
+    
+    # Build expected list
+    expected_list = []
+    for i in range(1, 24):
+      quantity = (i + 0.) / 4
+      expected_list.append({ 'node_relative_url': 2, 'section_relative_url':0, 'resource_relative_url':0, 'inventory':-quantity })
+    for i in range(12, 18):
+      quantity = (i + 0.) / 2
+      expected_list.append({ 'node_relative_url': 2, 'section_relative_url':0, 'resource_relative_url':0, 'inventory':-quantity })
+    for i in range(12, 13):
+      quantity = (i + 0.) / 2
+      expected_list.append({ 'node_relative_url': 2, 'section_relative_url':0, 'resource_relative_url':1, 'inventory':-quantity })
+    for i in [7.5, 5, 3]:
+      quantity = (i + 0.)
+      expected_list.append({ 'node_relative_url': 3, 'section_relative_url':0, 'resource_relative_url':0, 'inventory':-quantity })
+    for i in [18]:
+      quantity = (i + 0.)
+      expected_list.append({ 'node_relative_url': 3, 'section_relative_url':0, 'resource_relative_url':2, 'inventory':-quantity })
+    
+    item_dict = {'node':organisation_list, 'section':organisation_list, 'resource':resource_list}
+    expected_l = expected_list[:]
+    for expected in expected_l:
+      for attribute in [x for x in expected.keys() if x.endswith('_relative_url')]:
+        attr_name = attribute.split('_')[0]
+        expected[attribute] = item_dict[attr_name][expected[attribute]].getRelativeUrl()
+    self._testGetInventoryList(expected=expected_l, section=organisation_list[0].getRelativeUrl(), omit_simulation=1, omit_input=1)
+    
+    
+  def stepTestGetInventoryListWithOmitOutput(self, sequence=None, sequence_list=None, **kw):
+    """
+      Test getInventoryList on a section with omit_output
+    """
+    organisation_list = sequence.get('organisation_list')
+    resource_list = sequence.get('resource_list')
+    
+    # Build expected list
+    expected_list = []
+    for i in range(1, 31):
+      quantity = (i + 0.) / 2
+      expected_list.append({ 'node_relative_url': 2, 'section_relative_url':0, 'resource_relative_url':0, 'inventory':quantity })
+    for i in range(31, 33):
+      quantity = (i + 0.) / 2
+      expected_list.append({ 'node_relative_url': 2, 'section_relative_url':0, 'resource_relative_url':1, 'inventory':quantity })
+    for i in range(33, 34):
+      quantity = (i + 0.) / 2
+      expected_list.append({ 'node_relative_url': 2, 'section_relative_url':0, 'resource_relative_url':2, 'inventory':quantity })
+    for i in range(33, 40):
+      quantity = (i + 0.) / 2
+      expected_list.append({ 'node_relative_url': 3, 'section_relative_url':0, 'resource_relative_url':0, 'inventory':quantity })
+    for i in range(40, 41):
+      quantity = (i + 0.) / 2
+      expected_list.append({ 'node_relative_url': 3, 'section_relative_url':0, 'resource_relative_url':2, 'inventory':quantity })
+    
+    item_dict = {'node':organisation_list, 'section':organisation_list, 'resource':resource_list}
+    expected_l = expected_list[:]
+    for expected in expected_l:
+      for attribute in [x for x in expected.keys() if x.endswith('_relative_url')]:
+        attr_name = attribute.split('_')[0]
+        expected[attribute] = item_dict[attr_name][expected[attribute]].getRelativeUrl()
+    self._testGetInventoryList(expected=expected_l, section=organisation_list[0].getRelativeUrl(), omit_simulation=1, omit_output=1)
+    
+    
+  def stepTestGetInventoryListWithGroupBy(self, sequence=None, sequence_list=None, **kw):
+    """
+      Test getInventoryList by using group_by_*
+    """
+    organisation_list = sequence.get('organisation_list')
+    resource_list = sequence.get('resource_list')
+    
+    # Build expected list
+    expected_list_list = [ 
+      ({'group_by_node':1}, [ 
+        {'node_relative_url': 2, 'section_relative_url':0, 'resource_relative_url':0, 'inventory':120. },
+        {'node_relative_url': 2, 'section_relative_url':0, 'resource_relative_url':1, 'inventory':25.5 },
+        {'node_relative_url': 2, 'section_relative_url':0, 'resource_relative_url':2, 'inventory':16.5 },
+        {'node_relative_url': 3, 'section_relative_url':0, 'resource_relative_url':0, 'inventory':110.5 },
+        {'node_relative_url': 3, 'section_relative_url':0, 'resource_relative_url':2, 'inventory':2. },
+        {'node_relative_url': 6, 'section_relative_url':4, 'resource_relative_url':0, 'inventory':-358.5 },
+        {'node_relative_url': 6, 'section_relative_url':4, 'resource_relative_url':1, 'inventory':-31.5 },
+        {'node_relative_url': 6, 'section_relative_url':4, 'resource_relative_url':2, 'inventory':-36.5 },
+        {'node_relative_url': 9, 'section_relative_url':7, 'resource_relative_url':0, 'inventory':128. },
+        {'node_relative_url': 9, 'section_relative_url':7, 'resource_relative_url':1, 'inventory':6. },
+        {'node_relative_url': 9, 'section_relative_url':7, 'resource_relative_url':2, 'inventory':18. },
+      ]),
+      ({'group_by_variation':1, 'section':organisation_list[0].getRelativeUrl()}, [
+        {'resource_relative_url':0, 'inventory':17. },    # Baby
+        {'resource_relative_url':0, 'inventory':17.75 },
+        {'resource_relative_url':0, 'inventory':18.5 },
+        {'resource_relative_url':0, 'inventory':11.75 },
+        {'resource_relative_url':0, 'inventory':15. },
+        {'resource_relative_url':0, 'inventory':17.75 },
+        {'resource_relative_url':0, 'inventory':2. },     # 32
+        {'resource_relative_url':0, 'inventory':2.25 },
+        {'resource_relative_url':0, 'inventory':2.5 },
+        {'resource_relative_url':0, 'inventory':2.75 },
+        {'resource_relative_url':0, 'inventory':3. },
+        {'resource_relative_url':0, 'inventory':3.25 },
+        {'resource_relative_url':0, 'inventory':3.5 },    # 34
+        {'resource_relative_url':0, 'inventory':3.75 },
+        {'resource_relative_url':0, 'inventory':4. },
+        {'resource_relative_url':0, 'inventory':4.25 },
+        {'resource_relative_url':0, 'inventory':4.5 },
+        {'resource_relative_url':0, 'inventory':4.75 },
+        {'resource_relative_url':0, 'inventory':5. },     # Man
+        {'resource_relative_url':0, 'inventory':5.25 },
+        {'resource_relative_url':0, 'inventory':5.5 },
+        {'resource_relative_url':0, 'inventory':5.75 },
+        {'resource_relative_url':0, 'inventory':6. },
+        {'resource_relative_url':0, 'inventory':6.25 },
+        {'resource_relative_url':0, 'inventory':6.5 },    # Woman
+        {'resource_relative_url':0, 'inventory':26. },
+        {'resource_relative_url':0, 'inventory':6.5 },
+        {'resource_relative_url':0, 'inventory':6.5 },
+        {'resource_relative_url':0, 'inventory':6.5 },
+        {'resource_relative_url':0, 'inventory':6.5 },
+        {'resource_relative_url':0, 'inventory':None }, # Sum of lines (quantity of lines is NULL)
+        
+        {'resource_relative_url':1, 'inventory':15.5 },
+        {'resource_relative_url':1, 'inventory':10. },
+        {'resource_relative_url':1, 'inventory':None }, # Sum of lines (quantity of lines is ULL)
+        
+        {'resource_relative_url':2, 'inventory':18.5 },
+        {'resource_relative_url':2, 'inventory':None }, # Sum of lines (quantity of lines is NULL)
+      ]),
+    ]
+    
+    item_dict = {'node':organisation_list, 'section':organisation_list, 'resource':resource_list}
+    for expected_tuple in expected_list_list:
+      param, expected_list = expected_tuple
+      expected_l = expected_list[:]
+      for expected in expected_l:
+        for attribute in [x for x in expected.keys() if x.endswith('_relative_url')]:
+          attr_name = attribute.split('_')[0]
+          expected[attribute] = item_dict[attr_name][expected[attribute]].getRelativeUrl()
+      LOG('Testing getInventoryList with', 0, param)
+      self._testGetInventoryList(expected=expected_l, omit_simulation=1, **param)
+        
+    
+  def _testGetInventoryList(self, expected, **kw):
+    """
+      Shared testing method
+      expected is a list of dictionaries containing identifying keys and 'inventory'
+    """
+    simulation = self.getPortal().portal_simulation
+    expected = expected[:]
+    if len(expected) > 0:
+      attribute_list = [x for x in expected[0].keys() if x != 'inventory']
+    else:
+      attribute_list = []
+    
+    LOG('Testing getInventoryList with args :', 0, kw)
+    inventory_list = simulation.getInventoryList(**kw)
+    for inventory in inventory_list:
+      a_attributes = {}
+      for attr in attribute_list:
+        if not hasattr(inventory, attr):
+          LOG('TEST ERROR : Result of getInventoryList has no %s attribute' % attr, 0, '')
+          LOG('SQL Query was : ', 0, repr(simulation.getInventoryList(src__=1, **kw)))
+          self.failUnless(0)
+        a_attributes[attr] = getattr(inventory, attr)
+      a_inventory = inventory.inventory
+      # Build a function to filter on attributes
+      def cmpfunction(item):
+        for (key, value) in a_attributes.items():
+          if item[key] != value:
+            return 0
+        return 1
+      # Look for lines with the same attributes in expected list
+      expected_list = []
+      for i in range(len(expected)):
+        exp = expected[i]
+        if cmpfunction(exp):
+          expected_list.append(dict(exp))
+          expected_list[-1].update({'id':i})
+      # Now, look in these lines for one which has the same inventory
+      found_list = filter(lambda x:x['inventory'] == a_inventory, expected_list)
+      if len(found_list) == 0:
+        LOG('TEST ERROR : Found a line with getInventoryList which is not expected.', 0, 'Found line : %s (inventory : %s) ; expected values with these attributes : %s' % (a_attributes, a_inventory, expected_list))
+        LOG('SQL Query was : ', 0, repr(simulation.getInventoryList(src__=1, **kw)))
+        self.failUnless(0)
+      found = found_list[0]
+      del expected[found['id']]
+    # All inventory lines were found. Now check if some expected values remain
+    if len(expected) > 0:
+      LOG('TEST ERROR : Not all expected values were matched. Remaining =', 0, expected)
+      LOG('SQL Query was : ', 0, repr(simulation.getInventoryList(src__=1, **kw)))
+      self.failUnless(0)
+      
+      
+  def stepTestGetNextNegativeInventoryDate(self, sequence=None, sequence_list=None, **kw):
+    """
+      Test getNextNegativeInventoryDate
+    """
+    expected_negative_date = addToDate(DateTime())
+    base_category_list = ['size', 'colour', 'morphology']
+    variation_categories = ['size/Baby', '1', '5'] # size, colour, morphology
+    node = 3
+    # Set some new deliveries to reach a negative inventory
+    # Resource and variation MUST be the same on each new delivery
+    data_list = [
+      { 'source':node, 'destination':9, 'source_section':0, 'destination_section':7,
+        'source_payment':1, 'destination_payment':8, 'start_date':expected_negative_date, 'lines':[
+           {'resource':0, 'cells': [
+               {'variation':variation_categories, 'quantity':100000.},
+             ]
+           }, # line end
+        ] 
+      }, # packing list end
+      { 'source':6, 'destination':node, 'source_section':4, 'destination_section':0,
+        'source_payment':5, 'destination_payment':1, 'start_date':expected_negative_date+5, 'lines':[
+           {'resource':0, 'cells': [
+               {'variation':variation_categories, 'quantity':100000.},
+             ]
+           }, # line end
+        ] 
+      }, # packing list end
+    ]
+    
+    portal = self.getPortal()
+    simulation = portal.portal_simulation
+    packing_list_module = portal.getDefaultModule(self.packing_list_portal_type)
+    packing_list_list = sequence.get('packing_list_list')
+    organisation_list = sequence.get('organisation_list')
+    resource_list = sequence.get('resource_list')
+    delivery_line_list = []
+    
+    for data in data_list:
+      # Create Packing List
+      packing_list = packing_list_module.newContent(portal_type=self.packing_list_portal_type)
+      packing_list_list.append(packing_list)
+      # Add properties
+      property_list = [x for x in data.items() if x[0] not in ('lines','start_date')]
+      property_list = [(x[0], organisation_list[x[1]].getRelativeUrl()) for x in property_list] + \
+                      [x for x in data.items() if x[0] in ('start_date',)]
+      property_dict = {}
+      for (id, value) in property_list: property_dict[id] = value
+      packing_list.edit(**property_dict)
+      for line in data['lines']:
+        # Create Packing List Line
+        packing_list_line = packing_list.newContent(portal_type=self.packing_list_line_portal_type)
+        delivery_line_list.append(packing_list_line)
+        resource_value = resource_list[line['resource']]
+        resource_value.setVariationBaseCategoryList(base_category_list)
+        category_list = packing_list_line.getCategoryList()
+        variation_category_list = resource_value.getVariationRangeCategoryList(base_category_list=['size']) + \
+            ['colour/' + x.getRelativeUrl() for x in resource_value.objectValues(portal_type='Apparel Model Colour Variation')] + \
+            ['morphology/' + x.getRelativeUrl() for x in resource_value.objectValues(portal_type='Apparel Model Morphology Variation')]
+
+        packing_list_line.edit(
+            resource_value = resource_value,
+            default_resource_value = resource_value,
+            categories = category_list + [x for x in variation_category_list if x not in category_list]
+            )
+        
+        # Set cell range
+        packing_list_line.updateCellRange(base_id='quantity')
+        base_category_dict = {}
+        for i in range(len(base_category_list)):
+          base_category_dict[base_category_list[i]] = i
+        
+        # Set cells
+        for cell in line['cells']:
+          variation = cell['variation']
+          for i in range(len(variation)):
+            c = variation[i]
+            if len(c.split('/')) == 1:
+              variation[i] = '%s/%s' % (base_category_list[i], resource_value[c].getRelativeUrl())
+          new_variation = []
+          for bc in packing_list_line.getVariationBaseCategoryList():
+            new_variation.append(variation[base_category_dict[bc]])
+          variation = new_variation
+          packing_list_cell = packing_list_line.newCell(base_id='quantity', *variation)
+          packing_list_cell.edit(
+              quantity = cell['quantity'],
+              predicate_category_list = variation,
+              variation_category_list = variation,
+              mapped_value_property_list = ['quantity'],
+              )
+      sequence.edit(packing_list_list = packing_list_list)
+    
+    get_transaction().commit()
+    self.stepTic()
+    get_transaction().commit()
+    
+    # Then test the next negative date
+    next_date = simulation.getNextNegativeInventoryDate(resource=resource_value.getRelativeUrl(),
+                                                        node = organisation_list[node].getRelativeUrl(),
+                                                        variation_category = variation_categories)
+    expected_negative_date = '%.4d-%.2d-%.2d %.2d:%.2d:%.2d' % (expected_negative_date.year(),
+                                                      expected_negative_date.month(),
+                                                      expected_negative_date.day(),
+                                                      expected_negative_date.hour(),
+                                                      expected_negative_date.minute(),
+                                                      expected_negative_date.second())
+    if next_date != expected_negative_date:
+      LOG('TEST ERROR : Next negative date is not the expected one.', 0, 'calculated : %s, expected : %s' % (repr(next_date), repr(expected_negative_date)))
+      LOG('SQL Query was ', 0, simulation.getNextNegativeInventoryDate(resource=resource_value.getRelativeUrl(),
+                                                        node = organisation_list[node].getRelativeUrl(),
+                                                        variation_category = variation_categories, src__=1))
+      self.failUnless(0)                                                        
+            
+                
+  def test_01_getInventory(self, quiet=0, run=run_all_test):
+    """
+      Test the method getAggregatedAmountList
+    """
+    if not run: return
+    sequence_list = SequenceList()
+
+    get_inventory_test_sequence = 'TestGetInventoryListOnSection \
+                                   TestGetInventoryListOnNode \
+                                   TestGetInventoryListWithOmitInput \
+                                   TestGetInventoryListWithOmitOutput \
+                                   TestGetInventoryListWithGroupBy \
+                                  '
+    get_inventory_test_sequence+= 'TestGetInventoryOnNode \
+                                   TestGetInventoryOnVariationCategory \
+                                   TestGetInventoryOnPayment \
+                                   TestGetInventoryOnSection \
+                                   TestGetInventoryOnMirrorSection \
+                                   TestGetInventoryOnResource \
+                                   TestGetInventoryWithOmitInput \
+                                   TestGetInventoryWithOmitOutput \
+                                   TestGetInventoryOnSimulationState \
+                                   TestGetInventoryOnSectionCategory \
+                                   TestGetInventoryOnPaymentCategory \
+                                   TestGetInventoryOnNodeCategory \
+                                   TestGetInventoryOnMirrorSectionCategory \
+                                   TestGetInventoryOnResourceCategory \
+                                   TestGetInventoryOnVariationText \
+                                   TestGetInventoryWithSelectionReport \
+                                   TestGetNextNegativeInventoryDate \
+                              '
+
+# XXX Methods which fail for now :
+#  - TestGetInventoryWithSelectionReport ->
+#              problem with categories : makes a selection by using table category
+#              problem with makeTreeList : makes a tree using categories in portal_category, but
+#                                          this test uses some variations which are children of the resources
+# => Fix in progress
+
+#  - TestGetInventoryList : uses InventoryBrain.py in /usr/lib/zope/Extensions, which is not up to date
+# => To update in RPMs
+
+
+
+    sequence_string = 'CreateOrganisationList \
+                       CreateOrder \
+                       CreateVariatedResourceList \
+                       CreatePackingListList \
+                       Tic \
+                       CreateTestingCategories \
+                       Tic \
+                      ' + get_inventory_test_sequence
+    sequence_list.addSequenceString(sequence_string)
+
+    sequence_list.play(self)
+
+
+
+if __name__ == '__main__':
+    framework()
+else:
+    import unittest
+    def test_suite():
+        suite = unittest.TestSuite()
+        suite.addTest(unittest.makeSuite(Test))
+        return suite
+