From e7cc8b8b98bb0e11f752179ab54dbaab586e9385 Mon Sep 17 00:00:00 2001
From: Julien Muchembled <jm@nexedi.com>
Date: Fri, 27 Aug 2010 23:58:11 +0000
Subject: [PATCH] Rewrite testTradeModelLine to test new and legacy simulation

git-svn-id: https://svn.erp5.org/repos/public/erp5/sandbox/amount_generator@38055 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 .../DeliveryRootSimulationRule.py             |   16 +
 .../SimulationLegacyPatches.py                |   61 +
 .../bt/template_document_id_list              |    1 +
 product/ERP5/mixin/composition.py             |    6 +
 product/ERP5/tests/testTradeModelLine.py      | 2712 +++++------------
 product/ERP5Legacy/Document/BusinessPath.py   |    4 +-
 .../tests/testLegacyTradeModelLine.py         |  134 +
 product/ERP5Type/Utils.py                     |   19 +-
 8 files changed, 946 insertions(+), 2007 deletions(-)
 create mode 100644 bt5/erp5_simulation_legacy/DocumentTemplateItem/SimulationLegacyPatches.py
 create mode 100644 product/ERP5Legacy/tests/testLegacyTradeModelLine.py

diff --git a/bt5/erp5_simulation_legacy/DocumentTemplateItem/DeliveryRootSimulationRule.py b/bt5/erp5_simulation_legacy/DocumentTemplateItem/DeliveryRootSimulationRule.py
index c01fa0d601..90e11d94e6 100644
--- a/bt5/erp5_simulation_legacy/DocumentTemplateItem/DeliveryRootSimulationRule.py
+++ b/bt5/erp5_simulation_legacy/DocumentTemplateItem/DeliveryRootSimulationRule.py
@@ -50,3 +50,19 @@ class DeliveryRootSimulationRule(DeliveryRule):
     return {
       'delivery': movement.getRelativeUrl(),
     }
+
+# Fix subclasses (ex: AccountingTransactionRootSimulationRule)
+from Products.ERP5.Document import DeliveryRootSimulationRule as original_module
+original_class = original_module.DeliveryRootSimulationRule
+try:
+  original_module.DeliveryRootSimulationRule = DeliveryRootSimulationRule
+  import gc, os, sys
+  from Products.ERP5Type.Utils import importLocalDocument
+  for bases in gc.get_referrers(original_class):
+    if type(bases) is tuple:
+      for subclass in gc.get_referrers(bases):
+        if getattr(subclass, '__bases__', None) is bases:
+          importLocalDocument(subclass.__name__,
+            os.path.dirname(sys.modules[subclass.__module__].__file__))
+finally:
+  original_module.DeliveryRootSimulationRule = original_class
diff --git a/bt5/erp5_simulation_legacy/DocumentTemplateItem/SimulationLegacyPatches.py b/bt5/erp5_simulation_legacy/DocumentTemplateItem/SimulationLegacyPatches.py
new file mode 100644
index 0000000000..56a32ab84a
--- /dev/null
+++ b/bt5/erp5_simulation_legacy/DocumentTemplateItem/SimulationLegacyPatches.py
@@ -0,0 +1,61 @@
+from Products.ERP5.mixin import composition
+from Products.ERP5.ERP5Site import ERP5Site
+
+def patch():
+  from AccessControl.PermissionRole import PermissionRole
+  from Products.ERP5Type import Permissions
+  def declareProtected(cls, permission, *name_list):
+    roles = PermissionRole(permission)
+    for name in name_list:
+      setattr(cls, name + '__roles__', roles)
+
+  ## ERP5Site
+
+  declareProtected(ERP5Site, Permissions.AccessContentsInformation,
+                   'getPortalBusinessStateTypeList',
+                   'getPortalBusinessPathTypeList')
+
+  def getPortalBusinessStateTypeList(self):
+    """
+      Return business state types.
+    """
+    return ('Business State',)
+
+  ERP5Site.getPortalBusinessStateTypeList = getPortalBusinessStateTypeList
+
+  def getPortalBusinessPathTypeList(self):
+    """
+      Return business path types.
+    """
+    return ('Business Path',)
+
+  ERP5Site.getPortalBusinessPathTypeList = getPortalBusinessPathTypeList
+
+  ## CompositionMixin
+
+  composition._LEGACY_SIMULATION = True
+
+  ## SimulationMovement
+
+  def asComposedDocument(self, *args, **kw):
+    # XXX: What delivery should be used to find amount generator lines ?
+    #      With the currently enabled code, entire branches in the simulation
+    #      tree get (temporary) deleted when new delivery lines are being built
+    #      (and don't have yet a specialise value).
+    #      With the commented code, changing the STC on a SIT generated from a
+    #      SPL/SO would have no impact (and would never make the SIT divergent).
+    #return self.getRootSimulationMovement() \
+    #           .getDeliveryValue() \
+    #           .asComposedDocument(*args, **kw)
+    while 1:
+      delivery_value = self.getDeliveryValue()
+      if delivery_value is not None:
+        return delivery_value.asComposedDocument(*args, **kw)
+      self = self.getParentValue().getParentValue()
+
+  from Products.ERP5.Document.SimulationMovement import SimulationMovement
+  SimulationMovement.asComposedDocument = asComposedDocument
+  from Products.ERP5Type.Document.SimulationMovement import SimulationMovement
+  SimulationMovement.asComposedDocument = asComposedDocument
+
+patch()
diff --git a/bt5/erp5_simulation_legacy/bt/template_document_id_list b/bt5/erp5_simulation_legacy/bt/template_document_id_list
index 6d165a4d93..652f3ece39 100644
--- a/bt5/erp5_simulation_legacy/bt/template_document_id_list
+++ b/bt5/erp5_simulation_legacy/bt/template_document_id_list
@@ -7,6 +7,7 @@ InvoiceTransactionSimulationRule
 OrderRootSimulationRule
 PaymentSimulationRule
 RootAppliedRuleCausalityMovementGroup
+SimulationLegacyPatches
 TradeModelSimulationRule
 Transformation
 TransformedResource
\ No newline at end of file
diff --git a/product/ERP5/mixin/composition.py b/product/ERP5/mixin/composition.py
index 37b8f25055..20a47f151d 100644
--- a/product/ERP5/mixin/composition.py
+++ b/product/ERP5/mixin/composition.py
@@ -165,6 +165,9 @@ class asComposedDocument(object):
                            object_list)
     return sortValueList(object_list, sort_on, sort_order, **kw)
 
+# XXX Legacy simulation allows model lines on deliveries.
+#     Enabled if erp5_simulation_legacy BT is installed.
+_LEGACY_SIMULATION = False
 
 class CompositionMixin:
   """
@@ -203,6 +206,9 @@ class CompositionMixin:
       # we don't use getSpecialiseValueList to avoid acquisition on the parent
       model_list = effective_list[effective_index].getValueList('specialise',
                                       portal_type=specialise_type_list or ())
+      if _LEGACY_SIMULATION and not effective_index: # XXX compatibility
+        effective_set.add(self)
+        specialise_value_list = model_list
       if effective_set:
         effective_index += 1
       else: # first iteration
diff --git a/product/ERP5/tests/testTradeModelLine.py b/product/ERP5/tests/testTradeModelLine.py
index 834ececea3..88e984d8e1 100644
--- a/product/ERP5/tests/testTradeModelLine.py
+++ b/product/ERP5/tests/testTradeModelLine.py
@@ -1,8 +1,9 @@
 # -*- coding: utf-8 -*-
 ##############################################################################
-# Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved.
+# Copyright (c) 2009-2010 Nexedi SA and Contributors. All Rights Reserved.
 #          艁ukasz Nowak <luke@nexedi.com>
 #          Fabien Morin <fabien@nexedi.com>
+#          Julien Muchembled <jm@nexedi.com>
 #
 # WARNING: This program as such is intended to be used by professional
 # programmers who take the whole responsibility of assessing all potential
@@ -27,43 +28,161 @@
 #
 ##############################################################################
 
+from UserDict import UserDict
+import random
 import unittest
 import transaction
 
 from Products.ERP5.tests.testBPMCore import TestBPMMixin
+from Products.ERP5Type.Base import Base
+from Products.ERP5Type.Utils import simple_decorator
 from Products.ERP5Type.tests.backportUnittest import expectedFailure
-from Products.ERP5Type.tests.Sequence import SequenceList
 from DateTime import DateTime
-from Products.CMFCore.utils import getToolByName
 from Products.ERP5Type.tests.utils import createZODBPythonScript
 
 
-class TestTradeModelLineMixin(TestBPMMixin):
+def save_result_as(name):
+  @simple_decorator
+  def decorator(function):
+    def wrapper(self, *args, **kw):
+      result = function(self, *args, **kw)
+      self[name] = result
+      return result
+    return wrapper
+  return decorator
+
+
+class TestTradeModelLineMixin(TestBPMMixin, UserDict):
   """Provides methods to implementations sharing similar logic to Trade Model Lines"""
   # Constants and variables shared by tests
   base_unit_quantity = 0.01
 
-  # Helpers
+  def afterSetUp(self):
+    UserDict.__init__(self)
+    return TestBPMMixin.afterSetUp(self)
+
+  def beforeTearDown(self):
+    UserDict.clear(self)
+    return TestBPMMixin.beforeTearDown(self)
+
+  def clone(self, document):
+    parent = document.getParentValue()
+    clone, = parent.manage_pasteObjects(
+      parent.manage_copyObjects(ids=document.getId()))
+    clone = parent[clone['new_id']]
+    try:
+      self[clone.getPath()] = self[document.getPath()]
+    except KeyError:
+      pass
+    return clone
+
+  @save_result_as('node')
+  def createNode(self, **kw):
+    module = self.portal.getDefaultModule(portal_type=self.node_portal_type)
+    return module.newContent(portal_type=self.node_portal_type, **kw)
+
+  @save_result_as('resource')
   def createResource(self, portal_type, **kw):
     module = self.portal.getDefaultModule(portal_type=portal_type)
     return module.newContent(portal_type=portal_type, **kw)
 
-  # Steps
-  def stepCreatePriceCurrency(self, sequence=None):
-    sequence.edit(price_currency = self.createResource('Currency', \
-        title='Currency', base_unit_quantity=self.base_unit_quantity))
-
-  def stepCreateBusinessProcess(self, sequence=None):
-    sequence.edit(business_process=self.createBusinessProcess(
-      title=self.id()))
+  @save_result_as('currency')
+  def createCurrency(self):
+    return self.createResource('Currency', title='Currency',
+                               base_unit_quantity=self.base_unit_quantity)
+
+  @save_result_as('business_process')
+  def createBusinessProcess(self, business_link_list=(), **kw):
+    business_process = super(TestTradeModelLineMixin,
+        self).createBusinessProcess(**kw)
+    for business_link in business_link_list:
+      link = self.createBusinessLink(business_process, **business_link)
+      self['business_link/' + link.getTradePhaseId()] = link
+    return business_process
+
+  @save_result_as('trade_condition')
+  def createTradeCondition(self, specialise_value_list,
+                           trade_model_line_list=(), **kw):
+    module = self.portal.getDefaultModule(
+        portal_type=self.trade_condition_portal_type)
+    if isinstance(specialise_value_list, Base):
+      specialise_value_list = specialise_value_list,
+    trade_condition = module.newContent(
+        portal_type=self.trade_condition_portal_type,
+        title=self.id(),
+        specialise_value_list=specialise_value_list,
+        **kw)
+    for int_index, line_kw in enumerate(trade_model_line_list):
+      kw = dict(int_index=int_index)
+      kw.update(line_kw)
+      self.createTradeModelLine(trade_condition, **kw)
+    return trade_condition
 
-  def stepCreateBusinessLink(self, sequence):
-    business_process = sequence.get('business_process')
-    sequence.edit(business_link=self.createBusinessLink(business_process))
+  def createTradeModelLine(self, document, **kw):
+    line = document.newContent(portal_type='Trade Model Line', **kw)
+    reference = line.getReference()
+    if reference:
+      self['trade_model_line/' + reference] =  line
+    return line
+
+  @save_result_as('order')
+  def createOrder(self, specialise_value_list, order_line_list=(), **kw):
+    module = self.portal.getDefaultModule(portal_type=self.order_portal_type)
+    if isinstance(specialise_value_list, Base):
+      specialise_value_list = specialise_value_list,
+    kw.setdefault('start_date', self.order_date)
+    order = module.newContent(
+        portal_type=self.order_portal_type,
+        title=self.id(),
+        specialise_value_list=specialise_value_list,
+        **kw)
+    for arrow in ('source_value', 'source_section_value',
+                  'destination_value', 'destination_section_value'):
+      if order.getProperty(arrow) is None:
+        order._setProperty(arrow, self.createNode())
+    if not order.getPriceCurrency():
+      self['price_currency'] = price_currency = self.createCurrency()
+      order._setPriceCurrencyValue(price_currency)
+    for line_kw in order_line_list:
+      order.newContent(portal_type=self.order_line_portal_type, **line_kw)
+    return order
+
+  def getAggregatedAmountDict(self, amount_generator, **expected_amount_dict):
+    amount_list = amount_generator.getAggregatedAmountList()
+    amount_dict = {}
+    for amount in amount_list:
+      reference = amount.getReference()
+      expected_amount = expected_amount_dict.pop(reference)
+      for k, v in expected_amount.iteritems():
+        if k == 'causality_value_list':
+          self.assertEqual(v, amount.getValueList('causality'))
+        else:
+          self.assertEqual(v, amount.getProperty(k))
+      amount_dict[reference] = amount
+    self.assertEqual({}, expected_amount_dict)
+    return amount_dict
+
+  def getTradeModelSimulationMovementList(self, delivery_line):
+    result_list = []
+    for simulation_movement in delivery_line.getDeliveryRelatedValueList(
+        portal_type='Simulation Movement'):
+      if delivery_line.getPortalType() == self.order_line_portal_type:
+        applied_rule, = [x
+          for x in simulation_movement.objectValues()
+          if x.getSpecialiseReference() == 'default_delivering_rule']
+        simulation_movement, = applied_rule.objectValues()
+      applied_rule, = [x
+        for x in simulation_movement.objectValues()
+        if x.getSpecialiseReference() == 'default_invoicing_rule']
+      simulation_movement, = applied_rule.objectValues()
+      applied_rule, = [x
+        for x in simulation_movement.objectValues()
+        if x.getSpecialiseReference() == 'default_trade_model_rule']
+      result_list.append(applied_rule.objectValues())
+    return result_list
 
 
 class TestTradeModelLine(TestTradeModelLineMixin):
-  quiet = True
 
   # Constants and variables shared by tests
   default_discount_ratio = -0.05 # -5%
@@ -76,1358 +195,528 @@ class TestTradeModelLine(TestTradeModelLineMixin):
   order_date = DateTime()
 
   modified_order_line_price_ratio = 2.0
+  modified_packing_list_line_quantity_ratio = 0.4
   modified_invoice_line_quantity_ratio = modified_order_line_quantity_ratio \
       = 2.5
 
-  COMMON_DOCUMENTS_CREATION_SEQUENCE_STRING = """
-              CreateServiceTax
-              CreateServiceDiscount
-              CreatePriceCurrency
-              CreateProductDiscounted
-              CreateProductTaxed
-              CreateProductDiscountedTaxed
-              CreateSource
-              CreateSourceSection
-              CreateDestination
-              CreateDestinationSection
-              Tic
-  """
-
-  AGGREGATED_AMOUNT_LIST_CHECK_SEQUENCE_STRING = """
-              CheckOrderComplexTradeConditionAggregatedAmountList
-              CheckOrderLineTaxedAggregatedAmountList
-              CheckOrderLineDiscountedTaxedAggregatedAmountList
-              CheckOrderLineDiscountedAggregatedAmountList
-  """
-
-  AGGREGATED_AMOUNT_LIST_COMMON_SEQUENCE_STRING = \
-      COMMON_DOCUMENTS_CREATION_SEQUENCE_STRING + """
-              CreateBusinessProcess
-              CreateBusinessLink
-              ModifyBusinessLinkTaxing
-              CreateBusinessLink
-              ModifyBusinessLinkDiscounting
-              CreateTradeModelPath
-              CreateTradeCondition
-              SpecialiseTradeConditionWithBusinessProcess
-              CreateTradeModelLine
-              ModifyTradeModelLineTax
-              CreateTradeModelLine
-              ModifyTradeModelLineDiscount
-              Tic
-              CreateOrder
-              SpecialiseOrderTradeCondition
-              FillOrder
-              Tic
-              CreateOrderLine
-              ModifyOrderLineTaxed
-              CreateOrderLine
-              ModifyOrderLineDiscounted
-              CreateOrderLine
-              ModifyOrderLineDiscountedTaxed
-              Tic
-  """ + AGGREGATED_AMOUNT_LIST_CHECK_SEQUENCE_STRING
-
-  # reset some values
-  def afterSetUp(self):
-    TestTradeModelLineMixin.afterSetUp(self)
-    self.modified_packing_list_line_quantity_ratio = 0.5
-    custom = self.portal.portal_skins.custom
-    if custom.hasObject('TradeModelLine_getAmountProperty'):
-      custom._delObject('TradeModelLine_getAmountProperty')
-
-  # Helper methods
-  def _solveDivergence(self, obj, property, decision, group='line'):
-    kw = {'%s_group_listbox' % group:{}}
-    for divergence in obj.getDivergenceList():
-      if divergence.getProperty('tested_property') != property:
-        continue
-      sm_url = divergence.getProperty('simulation_movement').getRelativeUrl()
-      kw['line_group_listbox']['%s&%s' % (sm_url, property)] = {
-        'choice':decision}
-    self.portal.portal_workflow.doActionFor(
-      obj,
-      'solve_divergence_action',
-      **kw)
-
-  def createOrder(self):
-    module = self.portal.getDefaultModule(portal_type=self.order_portal_type)
-    return module.newContent(portal_type=self.order_portal_type,
-        title=self.id())
-
-  def getTradeModelSimulationMovementList(self, order_line):
-    result_list = []
-    for line_simulation_movement in order_line.getOrderRelatedValueList(
-        portal_type='Simulation Movement'):
-      invoicing_applied_rule = [x for x in
-          line_simulation_movement.objectValues()
-          if x.getSpecialiseValue().getPortalType() == 'Invoice Simulation Rule'][0]
-      invoicing_movement = invoicing_applied_rule.objectValues()[0]
-      trade_model_rule = [x for x in invoicing_movement.objectValues()
-          if x.getSpecialiseValue().getPortalType() == 'Trade Model Simulation Rule'][0]
-      result_list.append(trade_model_rule.objectValues())
-    return result_list
-
-  def checkInvoiceTransactionRule(self, trade_model_simulation_movement):
-    invoice_transaction_rule_list = trade_model_simulation_movement\
-        .objectValues()
-    self.assertEquals(1, len(invoice_transaction_rule_list))
-    invoice_transaction_rule = invoice_transaction_rule_list[0]
-    self.assertEqual('Invoice Transaction Simulation Rule',
-        invoice_transaction_rule.getSpecialiseValue().getPortalType())
-
-    invoice_transaction_simulation_movement_list = invoice_transaction_rule \
-        .objectValues()
+  @save_result_as('product/taxed')
+  def createProductTaxed(self):
+    return self.createResource('Product',
+      title='Product Taxed',
+      base_contribution=['base_amount/tax'],
+      use='normal')
 
-    self.assertEqual(2, len(invoice_transaction_simulation_movement_list))
+  @save_result_as('product/discounted')
+  def createProductDiscounted(self):
+    return self.createResource('Product',
+      title='Product Discounted',
+      base_contribution=['base_amount/discount'],
+      use='normal')
 
-    for movement in invoice_transaction_simulation_movement_list:
-      self.assertEqual(abs(movement.getQuantity()),
-          abs(trade_model_simulation_movement.getTotalPrice()))
+  @save_result_as('product/taxed_discounted')
+  def createProductDiscountedTaxed(self):
+    return self.createResource('Product',
+      title='Product Discounted & Taxed',
+      base_contribution=['base_amount/discount', 'base_amount/tax'],
+      use='normal')
 
-  def createTradeCondition(self):
-    module = self.portal.getDefaultModule(
-        portal_type=self.trade_condition_portal_type)
-    trade_condition = module.newContent(
-        portal_type=self.trade_condition_portal_type,
-        title=self.id())
-    return trade_condition
+  @save_result_as('service/tax')
+  def createServiceTax(self):
+    return self.createResource('Service', title='Tax', use='tax')
 
-  def createTradeModelLine(self, document, **kw):
-    return document.newContent(
-        portal_type='Trade Model Line',
-        **kw)
+  @save_result_as('service/discount')
+  def createServiceDiscount(self):
+    return self.createResource('Service', title='Discount', use='discount')
 
-  def stepModifyBusinessLinkDiscounting(self, sequence):
-    category_tool = self.getCategoryTool()
-    predecessor = category_tool.trade_state.invoiced
-    successor = category_tool.trade_state.taxed
-    business_link = sequence.get('business_link')
-    self.assertNotEqual(None, predecessor)
-    self.assertNotEqual(None, successor)
-
-    business_link.edit(
-      predecessor_value = predecessor,
-      successor_value = successor,
-      trade_phase = 'default/discount'
-    )
-    sequence.edit(business_link=None, business_link_discounting=business_link)
-
-  def stepModifyBusinessLinkTaxing(self, sequence):
-    category_tool = self.getCategoryTool()
-    predecessor = category_tool.trade_state.invoiced
-    successor = category_tool.trade_state.taxed
-    business_link = sequence.get('business_link')
-    self.assertNotEqual(None, predecessor)
-    self.assertNotEqual(None, successor)
-
-    business_link.edit(
-      predecessor_value = predecessor,
-      successor_value = successor,
-      trade_phase = 'default/tax'
-    )
-    sequence.edit(business_link=None, business_link_taxing=business_link)
-
-  def stepCreateTradeModelPath(self, sequence):
-    return
-    trade_phase = self.getCategoryTool().trade_phase
-    sequence.set('trade_model_path_default', self.createTradeModelPath(
-      sequence.get('business_process'),
-      reference='default_path',
-      int_index=0,
-      trade_phase_value_list=trade_phase.default.getCategoryChildValueList(),
-      trade_date_value=trade_phase.default.order,
-    ))
-
-  def stepAcceptDecisionQuantityInvoice(self, sequence=None, **kw):
-    invoice = sequence.get('invoice')
+  def packPackingList(self, packing_list):
+    if packing_list.getContainerState() == 'packed':
+      return
+    packing_list.manage_delObjects(ids=[q.getId()
+      for q in packing_list.objectValues(portal_type='Container')])
+    transaction.commit()
+    container = packing_list.newContent(portal_type='Container')
+    for movement in packing_list.getMovementList():
+      container.newContent(portal_type='Container Line',
+                           resource=movement.getResource(),
+                           quantity=movement.getQuantity())
+    transaction.commit()
+    self.tic()
+    self.assertEqual('packed', packing_list.getContainerState())
+
+  def copyExpectedAmountDict(self, delivery, ratio=1):
+    self[delivery.getPath()] = expected_amount_dict = {}
+    causality = delivery.getCausalityValue()
+    for base_amount, amount_dict in self[causality.getPath()].iteritems():
+      expected_amount_dict[base_amount] = new_amount_dict = {}
+      for line in delivery.getMovementList():
+        line_id = line.getCausalityId()
+        if line_id in amount_dict:
+          new_amount_dict[line.getId()] = ratio * amount_dict[line_id]
+
+  def acceptDecisionQuantityInvoice(self, invoice):
     solver_process_tool = self.portal.portal_solver_processes
     solver_process = solver_process_tool.newSolverProcess(invoice)
-    for quantity_solver_decision in filter(
-      lambda x:x.getCausalityValue().getTestedProperty()=='quantity',
-      solver_process.contentValues()):
-      # use Trade Model Solver.
-      quantity_solver_decision.setSolverValue(self.portal.portal_solvers['Trade Model Solver'])
+    for quantity_solver_decision in solver_process.contentValues():
+      if quantity_solver_decision.getCausalityValue().getTestedProperty() \
+        == 'quantity':
+        # use Trade Model Solver.
+        quantity_solver_decision.setSolverValue(
+          self.portal.portal_solvers['Trade Model Solver'])
     solver_process.buildTargetSolverList()
     solver_process.solve()
 
-  def stepAdoptPrevisionQuantityInvoice(self, sequence=None, **kw):
-    invoice = sequence.get('invoice')
-    self._solveDivergence(invoice, 'quantity', 'adopt')
+  def processPackingListBuildInvoice(self, packing_list, build=None):
+    self.packPackingList(packing_list)
+    transaction.commit()
+    self.tic()
 
-  def stepCreateSource(self, sequence=None, **kw):
-    module = self.portal.getDefaultModule(portal_type=self.node_portal_type)
-    node = module.newContent(portal_type=self.node_portal_type)
-    sequence.edit(source = node)
+    packing_list.start()
+    transaction.commit()
+    self.tic()
 
-  def stepCreateSourceSection(self, sequence=None, **kw):
-    module = self.portal.getDefaultModule(portal_type=self.node_portal_type)
-    node = module.newContent(portal_type=self.node_portal_type)
-    sequence.edit(source_section = node)
+    invoice, = packing_list.getCausalityRelatedValueList(
+      portal_type=self.invoice_portal_type)
+    self.assertEqual(5, len(invoice))
 
-  def stepCreateDestination(self, sequence=None, **kw):
-    module = self.portal.getDefaultModule(portal_type=self.node_portal_type)
-    node = module.newContent(portal_type=self.node_portal_type)
-    sequence.edit(destination = node)
+    packing_list.stop()
+    packing_list.deliver()
+    transaction.commit()
+    self.tic()
 
-  def stepCreateDestinationSection(self, sequence=None, **kw):
-    module = self.portal.getDefaultModule(portal_type=self.node_portal_type)
-    node = module.newContent(portal_type=self.node_portal_type)
-    sequence.edit(destination_section = node)
-
-  def stepCreateOrder(self, sequence=None, **kw):
-    sequence.edit(order = self.createOrder())
-
-  def stepSpecialiseOrderTradeCondition(self, sequence=None, **kw):
-    order = sequence.get('order')
-    trade_condition = sequence.get('trade_condition')
-
-    order.edit(specialise_value = trade_condition)
-
-  def stepSpecialiseInvoiceTradeCondition(self, sequence=None, **kw):
-    invoice = sequence.get('invoice')
-    trade_condition = sequence.get('trade_condition')
-
-    invoice.edit(specialise_value = trade_condition)
-
-  def stepPlanOrder(self, sequence=None, **kw):
-    order = sequence.get('order')
-    workflow_tool = getToolByName(self.portal, 'portal_workflow')
-    workflow_tool.doActionFor(order, 'plan_action')
-
-  def stepStartInvoice(self, sequence=None, **kw):
-    invoice = sequence.get('invoice')
-    workflow_tool = getToolByName(self.portal, 'portal_workflow')
-    workflow_tool.doActionFor(invoice, 'start_action')
-
-  def stepStopInvoice(self, sequence=None, **kw):
-    invoice = sequence.get('invoice')
-    workflow_tool = getToolByName(self.portal, 'portal_workflow')
-    workflow_tool.doActionFor(invoice, 'stop_action')
-
-  def stepDeliverInvoice(self, sequence=None, **kw):
-    invoice = sequence.get('invoice')
-    workflow_tool = getToolByName(self.portal, 'portal_workflow')
-    workflow_tool.doActionFor(invoice, 'deliver_action')
-
-  def stepStartPackingList(self, sequence=None, **kw):
-    packing_list = sequence.get('packing_list')
-    workflow_tool = getToolByName(self.portal, 'portal_workflow')
-    workflow_tool.doActionFor(packing_list, 'start_action')
-
-  def stepStopPackingList(self, sequence=None, **kw):
-    packing_list = sequence.get('packing_list')
-    workflow_tool = getToolByName(self.portal, 'portal_workflow')
-    workflow_tool.doActionFor(packing_list, 'stop_action')
-
-  def stepDeliverPackingList(self, sequence=None, **kw):
-    packing_list = sequence.get('packing_list')
-    workflow_tool = getToolByName(self.portal, 'portal_workflow')
-    workflow_tool.doActionFor(packing_list, 'deliver_action')
-
-  def stepCheckPackingListDiverged(self, sequence=None, **kw):
-    packing_list = sequence.get('packing_list')
-    self.assertEqual(
-      'diverged',
-      packing_list.getCausalityState()
-    )
-
-  def stepSplitAndDeferPackingList(self, sequence=None, **kw):
-    packing_list = sequence.get('packing_list')
-    kw = {'listbox':[
-      {'listbox_key':line.getRelativeUrl(),
-       'choice':'SplitAndDefer'} for line in packing_list.getMovementList() \
-      if line.isDivergent()]}
-    self.portal.portal_workflow.doActionFor(
-      packing_list,
-      'split_and_defer_action',
-      start_date=packing_list.getStartDate() + 15,
-      stop_date=packing_list.getStopDate() + 25,
-      **kw)
+    self['invoice'] = invoice
+    if build == 'invoice':
+      return invoice
 
-  def stepDecreasePackingListLineListQuantity(self, sequence=None, **kw):
-    packing_list = sequence.get('packing_list')
-    for movement in packing_list.getMovementList():
-      movement.edit(
-        quantity = movement.getQuantity() * \
-            self.modified_packing_list_line_quantity_ratio
-      )
-
-  def stepPackPackingList(self, sequence=None, **kw):
-    packing_list = sequence.get('packing_list')
-    if getattr(packing_list,'getContainerState', None) is None:
-      return
-    if packing_list.getContainerState() == 'packed':
-      return
+    self.checkCausalityState(invoice, 'solved')
+    self.checkTradeModelRuleSimulationExpand(packing_list)
 
-    packing_list.manage_delObjects(ids=[q.getId() for q in
-      packing_list.objectValues(portal_type='Container')])
-    transaction.commit()
-    cntr = packing_list.newContent(portal_type='Container')
-    for movement in packing_list.getMovementList(
-        portal_type=self.portal.getPortalMovementTypeList()):
-      cntr.newContent(
-        portal_type='Container Line',
-        resource = movement.getResource(),
-        quantity = movement.getQuantity())
+    invoice.start()
     transaction.commit()
     self.tic()
-    self.assertEqual('packed', packing_list.getContainerState() )
-
-  def stepCheckInvoiceNormalMovements(self, sequence=None, **kw):
-    self.logMessage('Assuming, that it is good...')
-
-  def stepCheckInvoiceAccountingMovements(self, sequence=None, **kw):
-    invoice = sequence.get('invoice')
-    currency = sequence.get('price_currency')
-    currency_precision = currency.getQuantityPrecision()
-    aggregated_amount_list_list = [
-      (q.getResourceValue().getUse(), q)
-      for q in invoice.getSpecialiseValue().getAggregatedAmountList(invoice)]
-    invoice_line_tax = [q[1] for q in aggregated_amount_list_list
-        if q[0] == 'tax'][0]
-    invoice_line_discount = [q[1] for q in aggregated_amount_list_list
-        if q[0] == 'discount'][0]
-
-    movement_list = invoice.getMovementList(
-        portal_type=invoice.getPortalAccountingMovementTypeList())
-    self.assertEqual(3, len(movement_list))
-    income_expense_line = [q for q in movement_list if
-        q.getSourceValue().getAccountType() in ['income', 'expense']][0]
-    payable_receivable_line = [q for q in movement_list if
-        q.getSourceValue().getAccountType() in ['asset/receivable',
-          'liability/payable']][0]
-    vat_line = [q for q in movement_list if q.getSourceValue() \
-        .getAccountType() in ['liability/payable/collected_vat',
-          'asset/receivable/refundable_vat']][0]
-
-    # here, the net total price of the invoice is all invoices lines that does
-    # not use a tax or a discount as resource.
-    rounded_total_price = round(sum(
-      [movement.getTotalPrice() for movement in invoice.getMovementList()
-        if movement.getResource() not in (invoice_line_tax.getResource(),
-                                   invoice_line_discount.getResource())]),
-        currency_precision)
-
-    rounded_tax_price = round(invoice_line_tax.getTotalPrice(),
-        currency_precision)
-    rounded_discount_price = round(invoice_line_discount.getTotalPrice(),
-        currency_precision)
-
-    self.assertEqual(abs(payable_receivable_line.getTotalPrice()),
-        rounded_total_price + rounded_tax_price + rounded_discount_price)
 
-    self.assertEqual(abs(vat_line.getTotalPrice()),
+    self.checkInvoiceAccountingMovements(invoice)
+
+  ###
+  ##  Check methods
+  ##
+
+  def checkWithoutBPM(self, order):
+    transaction.commit() # clear transactional cache
+    order.getSpecialiseValue()._setSpecialise(None)
+    self.assertRaises(ValueError, order.expand,
+      applied_rule_id=order.getCausalityRelatedId(portal_type='Applied Rule'))
+    transaction.abort()
+
+  def checkModelLineOnDelivery(self, delivery):
+    for portal_type in (self.business_link_portal_type,
+                        self.trade_model_path_portal_type,
+                        'Trade Model Line'):
+      self.assertRaises(ValueError, delivery.newContent,
+                        portal_type=portal_type)
+
+  def checkComposition(self, movement, specialise_value_list, type_count_dict):
+    composed = movement.asComposedDocument()
+    self.assertFalse(movement in composed._effective_model_list)
+    self.assertSameSet(composed.getSpecialiseValueList(),
+                       specialise_value_list)
+    count = 0
+    for portal_type, n in type_count_dict.iteritems():
+      count += n
+      self.assertEqual(n, len(composed.objectValues(portal_type=portal_type)))
+    self.assertTrue(count, len(composed.objectValues()))
+
+  def checkAggregatedAmountList(self, order):
+    expected_result_dict = self[order.getPath()]
+    def check(movement, movement_id):
+      kw = {}
+      for reference, result in expected_result_dict.iteritems():
+        total_price = result.get(movement_id)
+        if total_price:
+          model_line = self['trade_model_line/' + reference]
+          kw[reference] = dict(total_price=total_price,
+            causality_value_list=[model_line],
+            base_application_list=model_line.getBaseApplicationList(),
+            base_contribution_list=model_line.getBaseContributionList())
+      self.getAggregatedAmountDict(movement, **kw)
+
+    check(order, None)
+    for line in order.getMovementList():
+      check(line, line.getId())
+
+  def checkTradeModelRuleSimulationExpand(self, delivery):
+    expected_result_dict = self[delivery.getPath()]
+    price_currency = self['price_currency']
+
+    for line in delivery.getMovementList():
+      simulation_movement_list, = \
+        self.getTradeModelSimulationMovementList(line)
+      result_dict = dict((sm.getResourceValue().getUse(), sm)
+                         for sm in simulation_movement_list)
+      self.assertEqual(len(simulation_movement_list),
+                       len(result_dict))
+      for use in 'discount', 'tax':
+        total_price = expected_result_dict[use].get(line.getId())
+        if total_price:
+          sm = result_dict.pop(use)
+          self.assertEqual(sm.getTotalPrice(), total_price)
+          self.assertEqual(2, len(sm.getCausalityValueList()))
+          self.assertEqual(0, len(sm.getCausalityValueList(
+            portal_type=self.business_link_portal_type)))
+          self.assertEqual(1, len(sm.getCausalityValueList(
+            portal_type=self.trade_model_path_portal_type)))
+          self.assertEqual(1, len(sm.getCausalityValueList(
+            portal_type='Trade Model Line')))
+          self.assertEqual(sm.getBaseApplicationList(),
+                           ['base_amount/' + use])
+          self.assertEqual(sm.getBaseContributionList(),
+                           dict(discount=['base_amount/tax'], tax=[])[use])
+      self.assertEqual({}, result_dict)
+
+  def checkCausalityState(self, delivery, state):
+    self.assertEqual(state, delivery.getCausalityState(),
+      delivery.getDivergenceList())
+
+  def checkInvoiceAccountingMovements(self, invoice):
+    # Wouldn't it be better to use 'invoice.getAggregatedAmountList()'
+    # instead of looking at invoice lines ? We wouldn't have to clear
+    # base_contribution_list in test_01a_InvoiceNewTradeConditionOrLineSupport
+    line_dict = {}
+    for line in invoice.getMovementList():
+      if line.getPortalType() == self.invoice_line_portal_type:
+        key = line.getResourceValue().getUse()
+      else:
+        key = ('income_expense', 'payable_receivable', 'vat')[
+          ['income', 'expense',
+           'liability/payable', 'asset/receivable',
+           'liability/payable/collected_vat', 'asset/receivable/refundable_vat',
+          ].index(line.getSourceValue().getAccountType()) // 2]
+      line_dict.setdefault(key, 0)
+      line_dict[key] += line.getTotalPrice()
+    self.assertEqual(6, len(line_dict))
+
+    currency_precision = self['price_currency'].getQuantityPrecision()
+    rounded_total_price = round(line_dict['normal'], currency_precision)
+    rounded_tax_price = round(line_dict['tax'], currency_precision)
+    rounded_discount_price = round(line_dict['discount'], currency_precision)
+    self.assertEqual(abs(line_dict['payable_receivable']),
+        rounded_total_price + rounded_tax_price + rounded_discount_price)
+    self.assertEqual(abs(line_dict['vat']),
         rounded_tax_price)
-
-    self.assertEquals(abs(income_expense_line.getTotalPrice()),
+    self.assertEquals(abs(line_dict['income_expense']),
         rounded_total_price + rounded_discount_price)
 
-  def stepSetTradeConditionOld(self, sequence=None, **kw):
-    trade_condition = sequence.get('trade_condition')
-
-    self.assertNotEqual(None, trade_condition)
-
-    sequence.edit(
-      trade_condition = None,
-      old_trade_condition = trade_condition
-    )
-
-  def stepSetTradeConditionNew(self, sequence=None, **kw):
-    trade_condition = sequence.get('trade_condition')
-
-    self.assertNotEqual(None, trade_condition)
-
-    sequence.edit(
-      trade_condition = None,
-      new_trade_condition = trade_condition
-    )
-
-  def stepGetOldTradeCondition(self, sequence=None, **kw):
-    trade_condition = sequence.get('old_trade_condition')
-
-    self.assertNotEqual(None, trade_condition)
-
-    sequence.edit(
-      trade_condition = trade_condition,
-    )
-
-  def stepGetNewTradeCondition(self, sequence=None, **kw):
-    trade_condition = sequence.get('new_trade_condition')
-
-    self.assertNotEqual(None, trade_condition)
-
-    sequence.edit(
-      trade_condition = trade_condition,
-    )
-
-  def stepAcceptDecisionInvoice(self, sequence=None, **kw):
-    invoice = sequence.get('invoice')
-    invoice.portal_workflow.doActionFor(invoice,'accept_decision_action')
-
-  def stepCheckInvoiceCausalityStateSolved(self, sequence=None, **kw):
-    invoice = sequence.get('invoice')
-    self.assertEqual('solved', invoice.getCausalityState(),
-      invoice.getDivergenceList())
-
-  def stepCheckInvoiceCausalityStateDiverged(self, sequence=None, **kw):
-    invoice = sequence.get('invoice')
-    self.assertEqual('diverged', invoice.getCausalityState())
-
-  def stepGetInvoice(self, sequence=None, **kw):
-    packing_list = sequence.get('packing_list')
-    invoice_list = packing_list.getCausalityRelatedValueList(
-        portal_type=self.invoice_portal_type)
-    self.assertEqual(1, len(invoice_list)) # XXX 1 HC
-    sequence.edit(invoice = invoice_list[0])
-
-  def stepSetNewPackingListAsPackingList(self, sequence=None, **kw):
-    packing_list = sequence.get('packing_list')
-    new_packing_list = sequence.get('new_packing_list')
-    sequence.edit(
-      packing_list = new_packing_list,
-      new_packing_list = None
-    )
-
-  def stepGetNewPackingList(self, sequence=None, **kw):
-    order = sequence.get('order')
-    packing_list = sequence.get('packing_list')
-    packing_list_list = order.getCausalityRelatedValueList(
-        portal_type=self.packing_list_portal_type)
-    self.assertEqual(2, len(packing_list_list)) # XXX 2 HC
-    new_packing_list = [q for q in packing_list_list if q != packing_list][0]
-    sequence.edit(new_packing_list = new_packing_list)
-
-  def stepGetPackingList(self, sequence=None, **kw):
-    order = sequence.get('order')
-    packing_list_list = order.getCausalityRelatedValueList(
-        portal_type=self.packing_list_portal_type)
-    self.assertEqual(1, len(packing_list_list)) # XXX 1 HC
-    sequence.edit(packing_list = packing_list_list[0])
-
-  def stepConfirmOrder(self, sequence=None, **kw):
-    order = sequence.get('order')
-    workflow_tool = getToolByName(self.portal, 'portal_workflow')
-    workflow_tool.doActionFor(order, 'confirm_action')
-
-  def stepCheckOrderTaxNoSimulation(self, sequence=None, **kw):
-    order_line_taxed = sequence.get('order_line_taxed')
-    for trade_model_simulation_movement_list in \
-        self.getTradeModelSimulationMovementList(order_line_taxed):
-      self.assertEquals(0, len(trade_model_simulation_movement_list))
-
-  # XXX: Merge: stepCheckOrderLineDiscountedSimulation stepCheckOrderLineTaxedSimulation stepCheckOrderLineDiscountedTaxedSimulation
-
-  def stepCheckOrderLineDiscountedTaxedSimulation(self, sequence=None, **kw):
-    order_line = sequence.get('order_line_discounted_taxed')
-    business_link_discounting = sequence.get('business_link_discounting')
-    business_link_taxing = sequence.get('business_link_taxing')
-    price_currency = sequence.get('price_currency')
-
-    service_tax = sequence.get('service_tax')
-    service_discount = sequence.get('service_discount')
-
-    self.assertNotEqual(None, business_link_discounting)
-    self.assertNotEqual(None, business_link_taxing)
-    self.assertNotEqual(None, price_currency)
-
-    for trade_model_simulation_movement_list in \
-        self.getTradeModelSimulationMovementList(order_line):
-
-      self.assertEquals(2, len(trade_model_simulation_movement_list))
-      trade_model_simulation_movement_discount_complex = [q for q in \
-          trade_model_simulation_movement_list \
-          if q.getResourceValue() == service_discount][0]
-
-      trade_model_simulation_movement_tax_complex = [q for q in \
-          trade_model_simulation_movement_list \
-          if q.getResourceValue() == service_tax][0]
-
-      # discount complex
-      self.assertEqual(
-        trade_model_simulation_movement_discount_complex.getParentValue() \
-            .getParentValue().getTotalPrice() * self.default_discount_ratio,
-        trade_model_simulation_movement_discount_complex.getTotalPrice()
-      )
-
-      self.assertEqual(
-        business_link_discounting,
-        trade_model_simulation_movement_discount_complex.getCausalityValue()
-      )
-
-      self.assertEqual(
-        price_currency,
-        trade_model_simulation_movement_discount_complex \
-            .getPriceCurrencyValue()
-      )
-
-      self.assertSameSet(
-        ['base_amount/tax'],
-        trade_model_simulation_movement_discount_complex \
-            .getBaseContributionList()
-      )
-
-      self.assertSameSet(
-        ['base_amount/discount'],
-        trade_model_simulation_movement_discount_complex \
-            .getBaseApplicationList()
-      )
-
-      self.checkInvoiceTransactionRule(
-          trade_model_simulation_movement_discount_complex)
-
-      # TODO:
-      #  * trade_phase ???
-      #  * arrow
-      #  * dates
-
-      # tax complex
-      self.assertEqual(
-        (trade_model_simulation_movement_tax_complex.getParentValue()\
-            .getParentValue().getTotalPrice() + \
-            trade_model_simulation_movement_tax_complex.getParentValue()\
-            .getParentValue().getTotalPrice() * self.default_discount_ratio) \
-            * self.default_tax_ratio,
-        trade_model_simulation_movement_tax_complex.getTotalPrice()
-      )
-
-      self.assertEqual(
-        business_link_taxing,
-        trade_model_simulation_movement_tax_complex.getCausalityValue()
-      )
-
-      self.assertEqual(
-        price_currency,
-        trade_model_simulation_movement_tax_complex.getPriceCurrencyValue()
-      )
-
-      self.assertSameSet(
-        [],
-        trade_model_simulation_movement_tax_complex.getBaseContributionList()
-      )
-
-      self.assertSameSet(
-        ['base_amount/tax'],
-        trade_model_simulation_movement_tax_complex.getBaseApplicationList()
-      )
-
-      self.checkInvoiceTransactionRule(
-        trade_model_simulation_movement_tax_complex)
-      # TODO:
-      #  * trade_phase ???
-      #  * arrow
-      #  * dates
-
-  def stepCheckOrderLineDiscountedSimulation(self, sequence=None, **kw):
-    order_line = sequence.get('order_line_discounted')
-    business_link_discounting = sequence.get('business_link_discounting')
-    business_link_taxing = sequence.get('business_link_taxing')
-    price_currency = sequence.get('price_currency')
-
-    service_tax = sequence.get('service_tax')
-    service_discount = sequence.get('service_discount')
-
-    self.assertNotEqual(None, business_link_discounting)
-    self.assertNotEqual(None, business_link_taxing)
-    self.assertNotEqual(None, price_currency)
-
-    for trade_model_simulation_movement_list in \
-        self.getTradeModelSimulationMovementList(order_line):
-
-      self.assertEquals(2, len(trade_model_simulation_movement_list))
-      trade_model_simulation_movement_discount_only = [q for q in \
-          trade_model_simulation_movement_list \
-          if q.getResourceValue() == service_discount][0]
-
-      trade_model_simulation_movement_tax_only = [q for q in \
-          trade_model_simulation_movement_list \
-          if q.getResourceValue() == service_tax][0]
-
-      # discount only
-      self.assertEqual(
-        trade_model_simulation_movement_discount_only.getParentValue()\
-            .getParentValue().getTotalPrice() * self.default_discount_ratio,
-        trade_model_simulation_movement_discount_only.getTotalPrice()
-      )
-
-      self.assertEqual(
-        business_link_discounting,
-        trade_model_simulation_movement_discount_only.getCausalityValue()
-      )
-
-      self.assertEqual(
-        price_currency,
-        trade_model_simulation_movement_discount_only.getPriceCurrencyValue()
-      )
-
-      self.assertSameSet(
-        ['base_amount/tax'],
-        trade_model_simulation_movement_discount_only \
-            .getBaseContributionList()
-      )
-
-      self.assertSameSet(
-        ['base_amount/discount'],
-        trade_model_simulation_movement_discount_only.getBaseApplicationList()
-      )
-
-      self.checkInvoiceTransactionRule(
-          trade_model_simulation_movement_discount_only)
-      # TODO:
-      #  * trade_phase ???
-      #  * arrow
-      #  * dates
-
-      # tax only
-      # below tax is applied only to discount part
-      self.assertEqual(trade_model_simulation_movement_discount_only. \
-          getTotalPrice() * self.default_tax_ratio,
-          trade_model_simulation_movement_tax_only.getTotalPrice())
-
-      self.assertEqual(
-        business_link_taxing,
-        trade_model_simulation_movement_tax_only.getCausalityValue()
-      )
-
-      self.assertEqual(
-        price_currency,
-        trade_model_simulation_movement_tax_only.getPriceCurrencyValue()
-      )
-
-      self.assertSameSet(
-        [],
-        trade_model_simulation_movement_tax_only.getBaseContributionList()
-      )
-
-      self.assertSameSet(
-        ['base_amount/tax'],
-        trade_model_simulation_movement_tax_only.getBaseApplicationList()
-      )
-
-      self.checkInvoiceTransactionRule(
-          trade_model_simulation_movement_tax_only)
-
-      # TODO:
-      #  * trade_phase ???
-      #  * arrow
-      #  * dates
-
-  def stepCheckOrderLineTaxedSimulation(self, sequence=None, **kw):
-    order_line = sequence.get('order_line_taxed')
-    business_link = sequence.get('business_link_taxing')
-    price_currency = sequence.get('price_currency')
-    self.assertNotEqual(None, business_link)
-    self.assertNotEqual(None, price_currency)
-    for trade_model_simulation_movement_list in \
-        self.getTradeModelSimulationMovementList(order_line):
-      self.assertEquals(1, len(trade_model_simulation_movement_list))
-      trade_model_simulation_movement = \
-          trade_model_simulation_movement_list[0]
-
-      self.assertEqual(
-        trade_model_simulation_movement.getParentValue().getParentValue() \
-            .getTotalPrice() * self.default_tax_ratio,
-        trade_model_simulation_movement.getTotalPrice()
-      )
-
-      self.assertEqual(
-        business_link,
-        trade_model_simulation_movement.getCausalityValue()
-      )
-
-      self.assertEqual(
-        price_currency,
-        trade_model_simulation_movement.getPriceCurrencyValue()
-      )
-
-      self.assertSameSet(
-        [],
-        trade_model_simulation_movement.getBaseContributionList()
-      )
-
-      self.assertSameSet(
-        ['base_amount/tax'],
-        trade_model_simulation_movement.getBaseApplicationList()
-      )
-      self.checkInvoiceTransactionRule(trade_model_simulation_movement)
-
-      # TODO:
-      #  * trade_phase ???
-      #  * arrow
-      #  * dates
-
-  def stepFillOrder(self, sequence=None, **kw):
-    order = sequence.get('order')
-    price_currency = sequence.get('price_currency')
-    source = sequence.get('source')
-    destination = sequence.get('destination')
-    source_section = sequence.get('source_section')
-    destination_section = sequence.get('destination_section')
-    self.assertNotEqual(None, price_currency)
-    self.assertNotEqual(None, source)
-    self.assertNotEqual(None, destination)
-    self.assertNotEqual(None, source_section)
-    self.assertNotEqual(None, destination_section)
-    order.edit(
-        source_value=source,
-        destination_value=destination,
-        source_section_value=source_section,
-        destination_section_value=destination_section,
-        start_date=self.order_date,
-        price_currency_value = price_currency)
-
-  def stepCreateProductTaxed(self, sequence=None, **kw):
-    sequence.edit(product_taxed = self.createResource('Product',
-      title='Product Taxed',
-      base_contribution=['base_amount/tax'],
-      use='normal',
-    ))
+  ###
+  ##  Test cases
+  ##
 
-  def stepCreateProductDiscounted(self, sequence=None, **kw):
-    sequence.edit(product_discounted = self.createResource('Product',
-      title='Product Discounted',
-      base_contribution=['base_amount/discount'],
-      use='normal',
-    ))
+  def test_01_OrderWithSimpleTaxedAndDiscountedLines(self, build=None):
+    """Full test case with quite simple linear use case
 
-  def stepCreateProductDiscountedTaxed(self, sequence=None, **kw):
-    sequence.edit(product_discounted_taxed = self.createResource('Product',
-      title='Product Discounted & Taxed',
-      base_contribution=['base_amount/discount', 'base_amount/tax'],
-      use='normal',
-    ))
-
-  def stepCreateServiceTax(self, sequence=None, **kw):
-    sequence.edit(service_tax = self.createResource('Service',
-      title='Tax',
-      use='tax',
-    ))
-
-  def stepCreateServiceDiscount(self, sequence=None, **kw):
-    sequence.edit(service_discount = self.createResource('Service',
-      title='Discount',
-      use='discount',
-    ))
-
-  def stepCreateTradeCondition(self, sequence=None, **kw):
-    sequence.edit(trade_condition = self.createTradeCondition())
-
-  def stepCreateInvoiceLine(self, sequence=None, **kw):
-    invoice = sequence.get('invoice')
-    invoice_line = invoice.newContent(portal_type=self.invoice_line_portal_type)
-    sequence.edit(invoice_line = invoice_line)
-
-  def stepCreateOrderLine(self, sequence=None, **kw):
-    order = sequence.get('order')
-    order_line = order.newContent(portal_type=self.order_line_portal_type)
-    sequence.edit(order_line = order_line)
-
-  def stepGetInvoiceLineDiscounted(self, sequence=None, **kw):
-    invoice = sequence.get('invoice')
-    resource = sequence.get('product_discounted')
-    self.assertNotEqual(None, resource)
-    sequence.edit(invoice_line_discounted = [m for m in
-      invoice.getMovementList() if m.getResourceValue() == resource][0])
-
-  def stepGetInvoiceLineDiscountedTaxed(self, sequence=None, **kw):
-    invoice = sequence.get('invoice')
-    resource = sequence.get('product_discounted_taxed')
-    self.assertNotEqual(None, resource)
-    sequence.edit(invoice_line_discounted_taxed = [m for m in
-      invoice.getMovementList() if m.getResourceValue() == resource][0])
-
-  def stepGetInvoiceLineTaxed(self, sequence=None, **kw):
-    invoice = sequence.get('invoice')
-    resource = sequence.get('product_taxed')
-    self.assertNotEqual(None, resource)
-    sequence.edit(invoice_line_taxed = [m for m in
-      invoice.getMovementList() if m.getResourceValue() == resource][0])
-
-  def stepModifyQuantityInvoiceLineTaxed(self, sequence=None, **kw):
-    invoice_line = sequence.get('invoice_line_taxed')
-    invoice_line.edit(
-      quantity=invoice_line.getQuantity() * \
-          self.modified_invoice_line_quantity_ratio,
-    )
-
-  def stepModifyQuantityInvoiceLineDiscounted(self, sequence=None, **kw):
-    invoice_line = sequence.get('invoice_line_discounted')
-    invoice_line.edit(
-      quantity=invoice_line.getQuantity() * \
-          self.modified_invoice_line_quantity_ratio,
-    )
-
-  def stepModifyQuantityInvoiceLineDiscountedTaxed(self, sequence=None, **kw):
-    invoice_line = sequence.get('invoice_line_discounted_taxed')
-    invoice_line.edit(
-      quantity=invoice_line.getQuantity() * \
-          self.modified_invoice_line_quantity_ratio,
-    )
-
-  def stepModifyAgainOrderLineTaxed(self, sequence=None, **kw):
-    order_line = sequence.get('order_line_taxed')
-    order_line.edit(
-      price=order_line.getPrice() * self.modified_order_line_price_ratio,
-      quantity=order_line.getQuantity() * \
-          self.modified_order_line_quantity_ratio,
-    )
-
-  def stepModifyAgainOrderLineDiscounted(self, sequence=None, **kw):
-    order_line = sequence.get('order_line_discounted')
-    order_line.edit(
-      price=order_line.getPrice() * self.modified_order_line_price_ratio,
-      quantity=order_line.getQuantity() * \
-          self.modified_order_line_quantity_ratio,
-    )
-
-  def stepModifyAgainOrderLineDiscountedTaxed(self, sequence=None, **kw):
-    order_line = sequence.get('order_line_discounted_taxed')
-    order_line.edit(
-      price=order_line.getPrice() * self.modified_order_line_price_ratio,
-      quantity=order_line.getQuantity() * \
-          self.modified_order_line_quantity_ratio,
-    )
-
-  def stepModifyOrderLineTaxed(self, sequence=None, **kw):
-    order_line = sequence.get('order_line')
-    resource = sequence.get('product_taxed')
-    self.assertNotEqual(None, resource)
-    order_line.edit(
-      price=1.0,
-      quantity=2.0,
-      resource_value=resource
-    )
-    sequence.edit(
-      order_line = None,
-      order_line_taxed = order_line
-    )
-
-  def stepModifyOrderLineDiscounted(self, sequence=None, **kw):
-    order_line = sequence.get('order_line')
-    resource = sequence.get('product_discounted')
-    self.assertNotEqual(None, resource)
-    order_line.edit(
-      price=3.0,
-      quantity=4.0,
-      resource_value=resource
-    )
-    sequence.edit(
-      order_line = None,
-      order_line_discounted = order_line
-    )
-
-  def stepModifyOrderLineDiscountedTaxed(self, sequence=None, **kw):
-    order_line = sequence.get('order_line')
-    resource = sequence.get('product_discounted_taxed')
-    self.assertNotEqual(None, resource)
-    order_line.edit(
-      price=5.0,
-      quantity=6.0,
-      resource_value=resource
-    )
-    sequence.edit(
-      order_line = None,
-      order_line_discounted_taxed = order_line
-    )
-
-  def stepModifyInvoiceLineTaxed(self, sequence=None, **kw):
-    invoice_line = sequence.get('invoice_line')
-    resource = sequence.get('product_taxed')
-    self.assertNotEqual(None, resource)
-    invoice_line.edit(
-      price=1.0,
-      quantity=2.0,
-      resource_value=resource
-    )
-    sequence.edit(
-      invoice_line = None,
-      invoice_line_taxed = invoice_line
-    )
-
-  def stepModifyInvoiceLineDiscounted(self, sequence=None, **kw):
-    invoice_line = sequence.get('invoice_line')
-    resource = sequence.get('product_discounted')
-    self.assertNotEqual(None, resource)
-    invoice_line.edit(
-      price=3.0,
-      quantity=4.0,
-      resource_value=resource
-    )
-    sequence.edit(
-      invoice_line = None,
-      invoice_line_discounted = invoice_line
-    )
-
-  def stepModifyInvoiceLineDiscountedTaxed(self, sequence=None, **kw):
-    invoice_line = sequence.get('invoice_line')
-    resource = sequence.get('product_discounted_taxed')
-    self.assertNotEqual(None, resource)
-    invoice_line.edit(
-      price=5.0,
-      quantity=6.0,
-      resource_value=resource
-    )
-    sequence.edit(
-      invoice_line = None,
-      invoice_line_discounted_taxed = invoice_line
-    )
-
-  def stepOrderCreateTradeModelLine(self, sequence=None, **kw):
-    order = sequence.get('order')
-    sequence.edit(trade_model_line = self.createTradeModelLine(order))
-
-  def stepCreateTradeModelLine(self, sequence=None, **kw):
-    trade_condition = sequence.get('trade_condition')
-    sequence.edit(
-      trade_model_line = self.createTradeModelLine(trade_condition))
-
-  def stepSpecialiseTradeConditionWithBusinessProcess(self, sequence=None,
-      **kw):
-    business_process = sequence.get('business_process')
-    trade_condition = sequence.get('trade_condition')
-    self.assertNotEqual(None, business_process)
-    trade_condition.setSpecialiseValue(business_process)
-
-  def stepModifyTradeModelLineNewDiscount(self, sequence=None, **kw):
-    trade_model_line = sequence.get('trade_model_line')
-    service_discount = sequence.get('service_discount')
-
-    trade_model_line.edit(
-      price=self.new_discount_ratio,
-      base_application='base_amount/discount',
-      base_contribution='base_amount/tax',
-      trade_phase='default/discount',
-      resource_value=service_discount,
-      reference='service_discount',
-      int_index=10,
-    )
-    sequence.edit(
-      trade_model_line = None,
-      trade_model_line_discount = trade_model_line
-    )
-
-  def stepModifyTradeModelLineDiscount(self, sequence=None, **kw):
-    trade_model_line = sequence.get('trade_model_line')
-    service_discount = sequence.get('service_discount')
-
-    trade_model_line.edit(
-      price=self.default_discount_ratio,
-      base_application='base_amount/discount',
-      base_contribution='base_amount/tax',
-      trade_phase='default/discount',
-      resource_value=service_discount,
-      reference='discount',
-      int_index=10,
-    )
-    sequence.edit(
-      trade_model_line = None,
-      trade_model_line_discount = trade_model_line
-    )
-
-  def stepModifyTradeModelLineTotalDiscount(self, sequence=None, **kw):
-    trade_model_line = sequence.get('trade_model_line')
-    service_discount = sequence.get('service_discount')
-
-    trade_model_line.edit(
-      price=0.8,
-      base_application='base_amount/total_discount',
-      trade_phase='default/discount',
-      resource_value=service_discount,
-      reference='total_discount',
-      int_index=30,
-    )
-    sequence.edit(
-      trade_model_line = None,
-      trade_model_line_discount = trade_model_line
-    )
-
-  def stepModifyTradeModelLineDiscountContributingToTotalDiscount(self,
-      sequence=None, **kw):
-    trade_model_line = sequence.get('trade_model_line')
-    service_discount = sequence.get('service_discount')
-
-    trade_model_line.edit(
-      price=0.32,
-      base_application='base_amount/discount',
-      base_contribution='base_amount/total_discount',
-      trade_phase='default/discount',
-      resource_value=service_discount,
-      reference='total_dicount_2',
-      int_index=10,
-    )
-    sequence.edit(
-      trade_model_line = None,
-      trade_model_line_discount = trade_model_line
-    )
-
-  def stepModifyTradeModelLineNewTax(self, sequence=None, **kw):
-    trade_model_line = sequence.get('trade_model_line')
-    service_tax = sequence.get('service_tax')
-
-    trade_model_line.edit(
-      price=self.new_tax_ratio,
-      base_application='base_amount/tax',
-      trade_phase='default/tax',
-      resource_value=service_tax,
-      reference='tax_2',
-      int_index=20,
-    )
-    sequence.edit(
-      trade_model_line = None,
-      trade_model_line_tax = trade_model_line
-    )
-
-  def stepModifyTradeModelLineTax(self, sequence=None, **kw):
-    trade_model_line = sequence.get('trade_model_line')
-    service_tax = sequence.get('service_tax')
-
-    trade_model_line.edit(
-      price=self.default_tax_ratio,
-      base_application='base_amount/tax',
-      trade_phase='default/tax',
-      resource_value=service_tax,
-      reference='tax',
-      int_index=20,
-    )
-    sequence.edit(
-      trade_model_line = None,
-      trade_model_line_tax = trade_model_line
-    )
-
-  def stepModifyTradeModelLineTotalTax(self, sequence=None, **kw):
-    trade_model_line = sequence.get('trade_model_line')
-    service_tax = sequence.get('service_tax')
-
-    trade_model_line.edit(
-      price=0.12,
-      base_application='base_amount/total_tax',
-      base_contribution='base_amount/total_discount',
-      trade_phase='default/tax',
-      resource_value=service_tax,
-      reference='tax_3',
-      int_index=20,
-    )
-    sequence.edit(
-      trade_model_line = None,
-      trade_model_line_tax = trade_model_line
-    )
-
-  def stepModifyTradeModelLineTaxContributingToTotalTax(self,
-      sequence=None, **kw):
-    trade_model_line = sequence.get('trade_model_line')
-    service_tax = sequence.get('service_tax')
-
-    trade_model_line.edit(
-      price=0.2,
-      base_application='base_amount/tax',
-      base_contribution='base_amount/total_tax',
-      trade_phase='default/tax',
-      resource_value=service_tax,
-      reference='service_tax',
-      int_index=10,
-    )
-    sequence.edit(
-      trade_model_line = None,
-      trade_model_line_tax = trade_model_line
-    )
-
-  def stepModifyTradeModelLineTaxContributingToTotalTax2(self,
-      sequence=None, **kw):
-    trade_model_line = sequence.get('trade_model_line')
-    service_tax = sequence.get('service_tax')
-
-    trade_model_line.edit(
-      price=0.2,
-      base_application='base_amount/tax',
-      base_contribution='base_amount/total_tax',
-      trade_phase='default/tax',
-      resource_value=service_tax,
-      reference='service_tax_2',
-      int_index=10,
-    )
-    sequence.edit(
-      trade_model_line = None,
-      trade_model_line_tax = trade_model_line
-    )
-
-  def stepCheckOrderLineTaxedAggregatedAmountList(self, sequence=None, **kw):
-    order_line = sequence.get('order_line_taxed')
-    trade_condition = sequence.get('trade_condition')
-    trade_model_line_tax = sequence.get('trade_model_line_tax')
-    amount_list = trade_condition.getAggregatedAmountList(order_line)
-
-    self.assertEquals(1, len(amount_list))
-    tax_amount_list = [q for q in amount_list
-        if q.getBaseApplication() == 'base_amount/tax']
-    self.assertEquals(1, len(tax_amount_list))
-
-    tax_amount = tax_amount_list[0]
-
-    #self.assertEqual(tax_amount.getReference(),
-    #    trade_model_line_tax.getReference())
-    self.assertSameSet(['base_amount/tax'],
-        tax_amount.getBaseApplicationList())
-    self.assertSameSet([], tax_amount.getBaseContributionList())
-
-    self.assertEqual(order_line.getTotalPrice() * self.default_tax_ratio,
-        tax_amount.getTotalPrice())
-
-  def stepCheckOrderLineDiscountedTaxedAggregatedAmountList(self,
-      sequence=None, **kw):
-    order_line = sequence.get('order_line_discounted_taxed')
-    trade_condition = sequence.get('trade_condition')
-    trade_model_line_discount = sequence.get('trade_model_line_discount')
-    trade_model_line_tax = sequence.get('trade_model_line_tax')
-    amount_list = trade_condition.getAggregatedAmountList(order_line)
-
-    self.assertEquals(2, len(amount_list))
-    tax_amount_list = [q for q in amount_list
-        if q.getBaseApplication() == 'base_amount/tax']
-    self.assertEquals(1, len(tax_amount_list))
-    tax_amount = tax_amount_list[0]
-
-    discount_amount_list = [q for q in amount_list
-        if q.getBaseApplication() == 'base_amount/discount']
-    self.assertEquals(1, len(discount_amount_list))
-
-    discount_amount = discount_amount_list[0]
-
-    #self.assertEqual(tax_amount.getReference(),
-    #    trade_model_line_tax.getReference())
-    self.assertSameSet(['base_amount/tax'], tax_amount. \
-        getBaseApplicationList())
-    self.assertSameSet([], tax_amount.getBaseContributionList())
-
-    #self.assertEqual(discount_amount.getReference(),
-    #    trade_model_line_discount.getReference())
-    self.assertSameSet(['base_amount/discount'], discount_amount. \
-        getBaseApplicationList())
-    self.assertSameSet(['base_amount/tax'], discount_amount. \
-        getBaseContributionList())
-
-    self.assertEqual(order_line.getTotalPrice() * \
-        self.default_discount_ratio, discount_amount.getTotalPrice())
-
-    self.assertEqual((order_line.getTotalPrice() + discount_amount. \
-        getTotalPrice()) * self.default_tax_ratio,
-        tax_amount.getTotalPrice())
-
-  def stepCheckOrderLineDiscountedAggregatedAmountList(self, sequence=None,
-      **kw):
-    order_line = sequence.get('order_line_discounted')
-    trade_condition = sequence.get('trade_condition')
-    trade_model_line_discount = sequence.get('trade_model_line_discount')
-    amount_list = trade_condition.getAggregatedAmountList(order_line)
-
-    self.assertEquals(2, len(amount_list))
-    tax_amount_list = [q for q in amount_list
-        if q.getBaseApplication() == 'base_amount/tax']
-    self.assertEquals(1, len(tax_amount_list))
-    tax_amount = tax_amount_list[0]
-
-    discount_amount_list = [q for q in amount_list
-        if q.getBaseApplication() == 'base_amount/discount']
-    self.assertEquals(1, len(discount_amount_list))
-
-    discount_amount = discount_amount_list[0]
-
-    #self.assertEqual(discount_amount.getReference(),
-    #    trade_model_line_discount.getReference())
-    self.assertSameSet(['base_amount/tax'], tax_amount. \
-        getBaseApplicationList())
-    self.assertSameSet([], tax_amount.getBaseContributionList())
-
-    self.assertSameSet(['base_amount/discount'], discount_amount. \
-        getBaseApplicationList())
-    self.assertSameSet(['base_amount/tax'], discount_amount. \
-        getBaseContributionList())
-
-    self.assertEqual(order_line.getTotalPrice() * \
-        self.default_discount_ratio, discount_amount.getTotalPrice())
-
-    # below tax is applied only to discount part
-    self.assertEqual(discount_amount.getTotalPrice() * self.default_tax_ratio,
-        tax_amount.getTotalPrice())
-
-  def stepCheckOrderComplexTradeConditionAggregatedAmountList(self,
-      sequence=None, **kw):
-    trade_condition = sequence.get('trade_condition')
-    order = sequence.get('order')
-    order_line_discounted = sequence.get('order_line_discounted')
-    order_line_discounted_taxed = sequence.get('order_line_discounted_taxed')
-    order_line_taxed = sequence.get('order_line_taxed')
-    trade_model_line_tax = sequence.get('trade_model_line_tax')
-    trade_model_line_discount = sequence.get('trade_model_line_discount')
-
-    amount_list = trade_condition.getAggregatedAmountList(order)
-    self.assertEquals(2, len(amount_list))
-    discount_amount_list = [q for q in amount_list
-        if q.getBaseApplication() == 'base_amount/discount']
-    tax_amount_list = [q for q in amount_list
-        if q.getBaseApplication() == 'base_amount/tax']
-
-    self.assertEquals(1, len(discount_amount_list))
-    self.assertEquals(1, len(tax_amount_list))
-
-    discount_amount = discount_amount_list[0]
-    tax_amount = tax_amount_list[0]
-
-    #self.assertEqual(discount_amount.getReference(),
-    #    trade_model_line_discount.getReference())
-    self.assertSameSet(['base_amount/discount'], discount_amount. \
-        getBaseApplicationList())
-
-    self.assertSameSet(['base_amount/tax'], discount_amount. \
-        getBaseContributionList())
-
-    self.assertSameSet(['base_amount/tax'], tax_amount. \
-        getBaseApplicationList())
-
-    self.assertSameSet([], tax_amount.getBaseContributionList())
-    #self.assertEqual(tax_amount.getReference(),
-    #    trade_model_line_tax.getReference())
-
-    self.assertEqual(
-      discount_amount.getTotalPrice(),
-      (order_line_discounted.getTotalPrice()
-        + order_line_discounted_taxed.getTotalPrice() )
-      * self.default_discount_ratio
-    )
-
-    self.assertEqual(
-      tax_amount.getTotalPrice(),
-      (order_line_taxed.getTotalPrice()
-        + order_line_discounted_taxed.getTotalPrice()
-        + discount_amount.getTotalPrice()) * self.default_tax_ratio
-    )
-
-  def stepCheckAggregatedAmountListWithComplexBaseContributionBaseApplication(self,
-      sequence=None, **kw):
-    trade_condition = sequence.get('trade_condition')
-    order = sequence.get('order')
-    order_line_discounted = sequence.get('order_line_discounted')
-    order_line_discounted_taxed = sequence.get('order_line_discounted_taxed')
-    order_line_taxed = sequence.get('order_line_taxed')
-
-    amount_list = trade_condition.getAggregatedAmountList(order)
-    self.assertEquals(5, len(amount_list))
-    tax_amount_list = [q for q in amount_list
-        if q.getBaseApplication() == 'base_amount/tax']
-    total_tax_amount_list = [q for q in amount_list
-        if q.getBaseApplication() == 'base_amount/total_tax']
-    discount_amount_list = [q for q in amount_list
-        if q.getBaseApplication() == 'base_amount/discount']
-    total_discount_amount_list = [q for q in amount_list
-        if q.getBaseApplication() == 'base_amount/total_discount']
-
-    self.assertEquals(2, len(tax_amount_list))
-    self.assertEquals(1, len(total_tax_amount_list))
-    self.assertEquals(1, len(discount_amount_list))
-    self.assertEquals(1, len(total_discount_amount_list))
-
-    total_tax_amount = total_tax_amount_list[0]
-    discount_amount = discount_amount_list[0]
-    total_discount_amount = total_discount_amount_list[0]
-
-    self.assertSameSet(['base_amount/total_tax'], total_tax_amount. \
-        getBaseApplicationList())
-    self.assertSameSet(['base_amount/total_discount'], total_tax_amount. \
-        getBaseContributionList())
-
-    self.assertSameSet(['base_amount/discount'], discount_amount. \
-        getBaseApplicationList())
-    self.assertSameSet(['base_amount/total_discount'], discount_amount. \
-        getBaseContributionList())
-
-    self.assertSameSet(['base_amount/total_discount'], total_discount_amount. \
-        getBaseApplicationList())
-    self.assertSameSet([], total_discount_amount.getBaseContributionList())
-
-    for tax_amount in tax_amount_list:
-      self.assertSameSet(['base_amount/tax'], tax_amount. \
-          getBaseApplicationList())
-      self.assertSameSet(['base_amount/total_tax'], tax_amount. \
-          getBaseContributionList())
-
-    for tax_amount in tax_amount_list:
-      self.assertEqual(
-        tax_amount.getTotalPrice(),
-        order_line_taxed.getTotalPrice() * 0.2
-      )
-
-    self.assertEqual(
-      total_tax_amount.getTotalPrice(),
-      (order_line_taxed.getTotalPrice() * 0.2) * 2 * 0.12
-    )
-
-    self.assertEqual(
-      discount_amount.getTotalPrice(),
-      order_line_discounted.getTotalPrice() * 0.32
-    )
-
-    self.assertEqual(
-      total_discount_amount.getTotalPrice(),
-      ((order_line_taxed.getTotalPrice() * 0.2) * 2 * 0.12 + \
-      order_line_discounted.getTotalPrice() * 0.32) * 0.8
-    )
-
-  def assertSameUidSet(self, a, b, msg=None):
-    self.assertEqual(set(x.uid for x in a), set(x.uid for x in b), msg)
-
-  # Tests
-  def test_TradeConditionCircularCompositionIsSafe(self):
-    order = self.createOrder()
-    trade_condition_1 = self.createTradeCondition()
-    trade_condition_2 = self.createTradeCondition()
-
-    order.setSpecialiseValue(trade_condition_1)
-    trade_condition_1.setSpecialiseValue(trade_condition_2)
-    trade_condition_2.setSpecialiseValue(trade_condition_1)
-
-    self.assertEqual(trade_condition_1.findEffectiveSpecialiseValueList(order),
-        [trade_condition_1, trade_condition_2]
-    )
-
-  def test_findEffectiveSpecialiseValueListWithPortalType(self):
-    '''
-      check that findSpecialiseValueList is able to return all the inheritance
-      model tree using Depth-first search with a specific portal_type asked
-
-                                  trade_condition_1
-                                    /           \
-                                   /             \
-                                  /               \
-                       trade_condition_2       trade_condition_3
-                               |
-                               |
-                               |
-                        trade_condition_4
-                               |
-                               |
-                               |
-                        business_process
-                        
-    As only business_process will be a "Business Process" and we search for business process
-    the result must be [business_process]
-    '''
-    order = self.createOrder()
-    trade_condition_1 = self.createTradeCondition()
-    trade_condition_2 = self.createTradeCondition()
-    trade_condition_3 = self.createTradeCondition()
-    trade_condition_4 = self.createTradeCondition()
+    Data:
+    - 1 SO: 1 taxed, 1 discounted, 1 taxed&discounted
+    - 1 TC: tax, discount
+    - 1 BP (linked to default BP)
+
+    Checks:
+    - composition
+    - getAggregatedAmountList
+    - expand (before and after modifying quantities on order lines)
+    - build of packing list (+ pack) and invoice
+    """
+    taxed = self.createProductTaxed()
+    discounted = self.createProductDiscounted()
+    taxed_discounted = self.createProductDiscountedTaxed()
+    trade_condition = self.createTradeCondition(
+      self.createBusinessProcess(), (
+      dict(price=self.default_discount_ratio,
+           base_application='base_amount/discount',
+           base_contribution='base_amount/tax',
+           trade_phase='default/discount',
+           resource_value=self.createServiceDiscount(),
+           reference='discount',
+           int_index=10),
+      dict(price=self.default_tax_ratio,
+           base_application='base_amount/tax',
+           trade_phase='default/tax',
+           resource_value=self.createServiceTax(),
+           reference='tax',
+           int_index=20),
+      ))
+    order = self.createOrder(trade_condition, (
+      dict(price=1, quantity=2, id='taxed',
+                                resource_value=taxed),
+      dict(price=3, quantity=4, id='discounted',
+                                resource_value=discounted),
+      dict(price=5, quantity=6, id='taxed_discounted',
+                                resource_value=taxed_discounted),
+      ))
+    discount = {None: (3*4 + 5*6) * self.default_discount_ratio,
+                'discounted': (3*4) * self.default_discount_ratio,
+                'taxed_discounted': (5*6) * self.default_discount_ratio}
+    self[order.getPath()] = dict(
+      discount=discount,
+      tax={None: (1*2 + 5*6 + discount[None]) * self.default_tax_ratio,
+           'taxed': (1*2) * self.default_tax_ratio,
+           'discounted': discount['discounted'] * self.default_tax_ratio,
+           'taxed_discounted': (5*6 + discount['taxed_discounted'])
+                               * self.default_tax_ratio})
+
+    transaction.commit()
+    self.tic()
+
+    if not build:
+      for movement in (order, order['taxed'], order['discounted'],
+                       order['taxed_discounted']):
+        self.checkComposition(movement, [trade_condition], {
+          self.trade_model_path_portal_type: 10,
+          self.business_link_portal_type: 5,
+          "Trade Model Line": 2})
+
+      self.checkAggregatedAmountList(order)
+
+      order.plan()
+      transaction.commit()
+      self.tic()
+
+      self.checkTradeModelRuleSimulationExpand(order)
+
+      self.checkWithoutBPM(order)
+
+      order2 = self.clone(order)
+
+      # Multiply prices by 2 and quantities by 2.5
+      order['taxed'].edit(price=2, quantity=5)
+      order['discounted'].edit(price=6, quantity=10)
+      order['taxed_discounted'].edit(price=10, quantity=15)
+      transaction.commit()
+      self.tic()
+
+      discount = {None: (6*10 + 10*15) * self.default_discount_ratio,
+                  'discounted': (6*10) * self.default_discount_ratio,
+                  'taxed_discounted': (10*15) * self.default_discount_ratio}
+      self[order.getPath()] = dict(
+        discount=discount,
+        tax={None: (2*5 + 10*15 + discount[None]) * self.default_tax_ratio,
+             'taxed': (2*5) * self.default_tax_ratio,
+             'discounted': discount['discounted'] * self.default_tax_ratio,
+             'taxed_discounted': (10*15 + discount['taxed_discounted'])
+                                 * self.default_tax_ratio})
+
+      self.checkTradeModelRuleSimulationExpand(order)
+
+      self.checkAggregatedAmountList(order)
+      order = order2
+
+    order.confirm()
+    transaction.commit()
+    self.tic()
+
+    packing_list, = order.getCausalityRelatedValueList(
+      portal_type=self.packing_list_portal_type)
+    self.copyExpectedAmountDict(packing_list)
+
+    self['packing_list'] = packing_list
+    if build == 'packing_list':
+      return packing_list
+
+    return self.processPackingListBuildInvoice(packing_list, build)
+
+  def test_01a_InvoiceNewTradeConditionOrLineSupport(self):
+    invoice = self.test_01_OrderWithSimpleTaxedAndDiscountedLines('invoice')
+
+    # on invoice, make specialise point to a new TC and check it diverged
+    trade_condition = self['trade_condition']
+    new_trade_condition = self.clone(trade_condition)
+    line_dict = dict((line.getReference(), line)
+                     for line in new_trade_condition.objectValues())
+    line_dict['discount'].edit(reference='discount_2',
+                               price=self.new_discount_ratio)
+    line_dict['tax'].edit(reference='tax_2',
+                          price=self.new_tax_ratio)
+
+    self.assertEqual([trade_condition], invoice.getSpecialiseValueList())
+    invoice.setSpecialiseValue(new_trade_condition)
+    transaction.commit()
+    self.tic()
+    self.checkCausalityState(invoice, 'diverged')
+
+    # revert to reuse invoice
+    invoice.setSpecialiseValue(trade_condition)
+    transaction.commit()
+    self.tic()
+    self.checkCausalityState(invoice, 'solved')
+
+    # check how is supported addition of invoice line to invoice
+    for line in self['order'].getMovementList():
+      line = invoice.newContent(portal_type=self.invoice_line_portal_type,
+                                resource=line.getResource(),
+                                quantity=line.getQuantity(),
+                                price=line.getPrice())
+      # XXX base_contribution_list is automatically copied from the resource
+      #     but Invoice Transaction Trade Model Line Builder will not be called
+      #     again (or if we call it, lines would be built in a new invoice)
+      #     and accounting lines would not match invoice lines.
+      #     So we clear the list to make the test simpler.
+      #     See also 'checkInvoiceAccountingMovements' method.
+      self.assertTrue(line.getBaseContributionList())
+      line._setBaseContributionList(())
+    transaction.commit()
+    self.tic()
+    self.checkCausalityState(invoice, 'solved')
+
+    invoice.start()
+    transaction.commit()
+    self.tic()
+
+    self.checkCausalityState(invoice, 'solved')
+    self.checkInvoiceAccountingMovements(invoice)
+
+    invoice.stop()
+    invoice.deliver()
+    transaction.commit()
+    self.tic()
+
+  def test_01b_InvoiceModifyQuantityAndSolveDivergency(self):
+    invoice = self.test_01_OrderWithSimpleTaxedAndDiscountedLines('invoice')
+
+    for line in invoice.getMovementList():
+      if line.getResourceValue().getUse() == 'normal':
+        line.setQuantity(line.getQuantity() *
+          self.modified_invoice_line_quantity_ratio)
+    transaction.commit()
+    self.tic()
+    self.checkCausalityState(invoice, 'diverged')
+
+    self.acceptDecisionQuantityInvoice(invoice)
+    transaction.commit()
+    self.tic()
+    self.checkCausalityState(invoice, 'solved')
+
+  def test_01c_PackingListSplitBuildInvoiceBuild(self):
+    packing_list = \
+      self.test_01_OrderWithSimpleTaxedAndDiscountedLines('packing_list')
+
+    for line in packing_list.getMovementList():
+        line.setQuantity(line.getQuantity() *
+          self.modified_packing_list_line_quantity_ratio)
+    transaction.commit()
+    self.tic()
+    self.checkCausalityState(packing_list, 'diverged')
+
+    order = self['order']
+    self.checkTradeModelRuleSimulationExpand(order)
+    self.copyExpectedAmountDict(packing_list,
+        self.modified_packing_list_line_quantity_ratio)
+
+    listbox = [{'listbox_key':line.getRelativeUrl(),
+                'choice':'SplitAndDefer'}
+               for line in packing_list.getMovementList()
+               if line.isDivergent()]
+    self.assertEqual(len(order), len(listbox))
+    self.portal.portal_workflow.doActionFor(
+      packing_list,
+      'split_and_defer_action',
+      start_date=packing_list.getStartDate() + 15,
+      stop_date=packing_list.getStopDate() + 25,
+      listbox=listbox)
+
+    transaction.commit()
+    self.tic()
+    self.checkCausalityState(packing_list, 'solved')
+    new_packing_list, = [x for x in order.getCausalityRelatedValueList(
+                             portal_type=self.packing_list_portal_type)
+                           if x != packing_list]
+    self.copyExpectedAmountDict(new_packing_list,
+        1 - self.modified_packing_list_line_quantity_ratio)
+
+    invoice_count = len(self.portal
+        .accounting_module.objectValues(portal_type=self.invoice_portal_type))
+    self.processPackingListBuildInvoice(packing_list)
+    # XXX With legacy code, only 1 invoice was built after starting the first
+    #     packing list. Now, all invoice lines generated by trade model are
+    #     built immediately, creating a second invoice before starting the
+    #     second packing list, and we end up with 3 invoices. In other words,
+    #     the new simulation splits the second invoice, and I am not sure it's
+    #     correct.
+    expectedFailure(self.assertEqual)(invoice_count + 1, len(self.portal
+        .accounting_module.objectValues(portal_type=self.invoice_portal_type)))
+    self.processPackingListBuildInvoice(new_packing_list)
+
+  def test_02_OrderWithComplexTaxedAndDiscountedLines(self):
+    service_discount = self.createServiceDiscount()
+    service_tax = self.createServiceTax()
     business_process = self.createBusinessProcess()
+    line_list = [
+      dict(price=0.2,
+           base_application='base_amount/tax',
+           base_contribution='base_amount/total_tax',
+           trade_phase='default/tax',
+           resource_value=service_tax,
+           reference='service_tax',
+           int_index=10),
+      dict(price=0.32,
+           base_application='base_amount/discount',
+           base_contribution='base_amount/total_discount',
+           trade_phase='default/discount',
+           resource_value=service_discount,
+           reference='total_dicount_2',
+           int_index=10),
+      dict(price=0.2,
+           base_application='base_amount/tax',
+           base_contribution='base_amount/total_tax',
+           trade_phase='default/tax',
+           resource_value=service_tax,
+           reference='service_tax_2',
+           int_index=10),
+      dict(price=0.12,
+           base_application='base_amount/total_tax',
+           base_contribution='base_amount/total_discount',
+           trade_phase='default/tax',
+           resource_value=service_tax,
+           reference='tax_3',
+           int_index=20),
+      dict(price=0.8,
+           base_application='base_amount/total_discount',
+           trade_phase='default/discount',
+           resource_value=service_discount,
+           reference='total_discount',
+           int_index=30),
+      ]
+    random.shuffle(line_list)
+    trade_condition = self.createTradeCondition(business_process, line_list)
+
+    taxed = self.createProductTaxed()
+    discounted = self.createProductDiscounted()
+    order = self.createOrder(trade_condition, (
+      dict(price=1, quantity=2, id='taxed',
+                                resource_value=taxed),
+      dict(price=3, quantity=4, id='discounted',
+                                resource_value=discounted),
+      ))
+    discount_price = (3*4) * 0.32
+    tax_price = (1*2) * 0.2
+    total_tax_price = tax_price * 2 * 0.12
+    self[order.getPath()] = dict(
+      service_tax={None: tax_price, 'taxed': tax_price},
+      total_dicount_2={None: discount_price, 'discounted': discount_price},
+      service_tax_2={None: tax_price, 'taxed': tax_price},
+      tax_3={None: total_tax_price, 'taxed': total_tax_price},
+      total_discount={None: (total_tax_price+discount_price) * 0.8,
+                      'taxed': total_tax_price * 0.8,
+                      'discounted': discount_price * 0.8})
 
-    order.setSpecialiseValue(trade_condition_1)
-    trade_condition_1.setSpecialiseValueList((trade_condition_2,
-      trade_condition_3))
-    trade_condition_2.setSpecialiseValue(trade_condition_4)
-    trade_condition_4.setSpecialiseValue(business_process)
+    transaction.commit()
+    self.tic()
+
+    self.checkModelLineOnDelivery(order)
 
-    self.assertEqual(trade_condition_1.findEffectiveSpecialiseValueList(order),
-      [trade_condition_1, trade_condition_2, trade_condition_3,
-       trade_condition_4])
-    self.assertEqual(trade_condition_1.findEffectiveSpecialiseValueList(order,
-      portal_type_list = ['Business Process']), [business_process])
+    for movement in order, order['taxed'], order['discounted']:
+      self.checkComposition(movement, [trade_condition], {
+        self.trade_model_path_portal_type: 10,
+        self.business_link_portal_type: 5,
+        "Trade Model Line": 5})
+
+    self.checkAggregatedAmountList(order)
 
   def test_tradeModelLineWithFixedPrice(self):
     """
@@ -1435,597 +724,39 @@ class TestTradeModelLine(TestTradeModelLineMixin):
       to say "discount 10 euros" or "pay more 10 euros" instead of saying "10%
       discount from total"
     """
-    trade_condition = self.createTradeCondition()
-    tax = self.createResource('Service', title='Tax', use='tax')
-    # create a model line with 100 euros
-    A = self.createTradeModelLine(trade_condition, reference='A',
-                                  resource_value=tax, quantity=100, price=1)
-    # add a discount of 10 euros
-    B = self.createTradeModelLine(trade_condition, reference='B',
-                                  resource_value=tax, quantity=10, price=-1)
-    order = self.createOrder()
-    order.setSpecialiseValue(trade_condition)
-    amount_list = order.getGeneratedAmountList()
+    tax = self.createServiceTax()
+    trade_condition = self.createTradeCondition((), (
+      # create a model line with 100 euros
+      dict(reference='A', resource_value=tax, quantity=100, price=1),
+      # add a discount of 10 euros
+      dict(reference='B', resource_value=tax, quantity=10, price=-1)))
+    order = self.createOrder(trade_condition)
+    amount_list = order.getAggregatedAmountList()
     self.assertEqual([-10, 100], sorted(x.getTotalPrice() for x in amount_list))
 
-  def test_getAggregatedAmountList(self, quiet=quiet):
-    """
-      Test for case, when discount contributes to tax, and order has mix of contributing lines
-    """
-    sequence_list = SequenceList()
-    sequence_string = self.AGGREGATED_AMOUNT_LIST_COMMON_SEQUENCE_STRING
-
-    sequence_list.addSequenceString(sequence_string)
-    sequence_list.play(self, quiet=quiet)
-
-  ORDER_SPECIALISE_AGGREGATED_AMOUNT_COMMON_SEQUENCE_STRING = \
-      COMMON_DOCUMENTS_CREATION_SEQUENCE_STRING + """
-              CreateBusinessProcess
-              CreateBusinessLink
-              ModifyBusinessLinkTaxing
-              CreateBusinessLink
-              ModifyBusinessLinkDiscounting
-              CreateTradeModelPath
-              CreateTradeCondition
-              SpecialiseTradeConditionWithBusinessProcess
-              CreateTradeModelLine
-              ModifyTradeModelLineTax
-              Tic
-              CreateOrder
-              OrderCreateTradeModelLine
-              ModifyTradeModelLineDiscount
-              SpecialiseOrderTradeCondition
-              FillOrder
-              Tic
-              CreateOrderLine
-              ModifyOrderLineTaxed
-              CreateOrderLine
-              ModifyOrderLineDiscounted
-              CreateOrderLine
-              ModifyOrderLineDiscountedTaxed
-              Tic
-    """ + AGGREGATED_AMOUNT_LIST_CHECK_SEQUENCE_STRING
-
-  def test_getAggregatedAmountListOrderSpecialise(self, quiet=quiet):
-    """
-      Test for case, when discount contributes to tax, and order has mix of contributing lines and order itself defines Trade Model Line
-    """
-    sequence_list = SequenceList()
-    sequence_string = self\
-        .ORDER_SPECIALISE_AGGREGATED_AMOUNT_COMMON_SEQUENCE_STRING
-
-    sequence_list.addSequenceString(sequence_string)
-    sequence_list.play(self, quiet=quiet)
-
-  AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING = """
-              CheckOrderLineTaxedSimulation
-              CheckOrderLineDiscountedSimulation
-              CheckOrderLineDiscountedTaxedSimulation
-  """
-  TRADE_MODEL_RULE_SIMULATION_SEQUENCE_STRING = \
-      AGGREGATED_AMOUNT_LIST_COMMON_SEQUENCE_STRING + """
-              Tic
-              PlanOrder
-              Tic
-  """ + AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING
-
-  def test_TradeModelRuleSimulationExpand(self, quiet=quiet):
-    """Tests tree of simulations from Trade Model Rule"""
-    sequence_list = SequenceList()
-    sequence_string = self.TRADE_MODEL_RULE_SIMULATION_SEQUENCE_STRING
-    sequence_list.addSequenceString(sequence_string)
-    sequence_list.play(self, quiet=quiet)
-
-  def test_TradeModelRuleSimulationReexpand(self, quiet=quiet):
-    """Tests tree of simulations from Trade Model Rule with reexpanding"""
-    sequence_list = SequenceList()
-    sequence_string = self.TRADE_MODEL_RULE_SIMULATION_SEQUENCE_STRING + """
-              ModifyAgainOrderLineTaxed
-              ModifyAgainOrderLineDiscounted
-              ModifyAgainOrderLineDiscountedTaxed
-              Tic
-    """ + self.AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING
-    sequence_list.addSequenceString(sequence_string)
-    sequence_list.play(self, quiet=quiet)
-
-  def test_TradeModelRuleSimulationReexpandResourceChange(self, quiet=quiet):
-    """Tests tree of simulations from Trade Model Rule with reexpanding when resource changes on model"""
-    sequence_list = SequenceList()
-    sequence_string = self.TRADE_MODEL_RULE_SIMULATION_SEQUENCE_STRING + """
-              CreateServiceTax
-              CreateServiceDiscount
-              OrderCreateTradeModelLine
-              ModifyTradeModelLineDiscount
-              OrderCreateTradeModelLine
-              ModifyTradeModelLineTax
-              Tic
-    """ + self.AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING
-    sequence_list.addSequenceString(sequence_string)
-    sequence_list.play(self, quiet=quiet)
-
-  TRADE_MODEL_RULE_SIMULATION_ORDER_SPECIALISED_SEQUENCE_STRING = \
-      ORDER_SPECIALISE_AGGREGATED_AMOUNT_COMMON_SEQUENCE_STRING + """
-              Tic
-              PlanOrder
-              Tic
-  """ + AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING
-
-  def test_TradeModelRuleSimulationExpandOrderSpecialise(self, quiet=quiet):
-    sequence_list = SequenceList()
-    sequence_string = self \
-        .TRADE_MODEL_RULE_SIMULATION_ORDER_SPECIALISED_SEQUENCE_STRING
-    sequence_list.addSequenceString(sequence_string)
-    sequence_list.play(self, quiet=quiet)
-
-  def test_TradeModelRuleSimulationReexpandOrderSpecialise(self, quiet=quiet):
-    sequence_list = SequenceList()
-    sequence_string = self \
-        .TRADE_MODEL_RULE_SIMULATION_ORDER_SPECIALISED_SEQUENCE_STRING+ """
-              ModifyAgainOrderLineTaxed
-              ModifyAgainOrderLineDiscounted
-              ModifyAgainOrderLineDiscountedTaxed
-              Tic
-    """ + self.AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING
-    sequence_list.addSequenceString(sequence_string)
-    sequence_list.play(self, quiet=quiet)
-
-  def test_TradeModelRuleSimulationWithoutBPM(self, quiet=quiet):
-    """Tests tree of simulations from Trade Model Rule when there is no BPM"""
-    sequence_list = SequenceList()
-    sequence_string = self.COMMON_DOCUMENTS_CREATION_SEQUENCE_STRING + """
-              CreateTradeCondition
-              CreateTradeModelLine
-              ModifyTradeModelLineTax
-              Tic
-              CreateOrder
-              SpecialiseOrderTradeCondition
-              FillOrder
-              Tic
-              CreateOrderLine
-              ModifyOrderLineTaxed
-              Tic
-              PlanOrder
-              Tic
-              CheckOrderTaxNoSimulation
-    """
-    sequence_list.addSequenceString(sequence_string)
-    sequence_list.play(self, quiet=quiet)
-
-  def test_TradeModelRuleSimulationWithoutTradeCondition(self, quiet=quiet):
-    """Tests tree of simulations from Trade Model Rule when there is no Trade Condition"""
-    sequence_list = SequenceList()
-    sequence_string = self.COMMON_DOCUMENTS_CREATION_SEQUENCE_STRING + """
-              CreateOrder
-              FillOrder
-              Tic
-              CreateOrderLine
-              ModifyOrderLineTaxed
-              Tic
-              PlanOrder
-              Tic
-              CheckOrderTaxNoSimulation
-    """
-    sequence_list.addSequenceString(sequence_string)
-    sequence_list.play(self, quiet=quiet)
-
-  def test_TradeModelRuleSimulationBuildInvoice(self, quiet=quiet):
-    sequence_list = SequenceList()
-    sequence_string = self.TRADE_MODEL_RULE_SIMULATION_SEQUENCE_STRING
-    sequence_string += """
-              ConfirmOrder
-              Tic
-    """ + self.AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              GetPackingList
-              PackPackingList
-              Tic
-    """ + self.AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              StartPackingList
-              StopPackingList
-              DeliverPackingList
-              Tic
-    """ + self.AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              GetInvoice
-              CheckInvoiceCausalityStateSolved
-              CheckInvoiceNormalMovements
-    """
-    sequence_list.addSequenceString(sequence_string)
-    sequence_list.play(self, quiet=quiet)
-
-  def test_TradeModelRuleSimulationBuildInvoiceOrderSpecialise(self, quiet=quiet):
-    sequence_list = SequenceList()
-    sequence_string = self\
-        .TRADE_MODEL_RULE_SIMULATION_ORDER_SPECIALISED_SEQUENCE_STRING
-    sequence_string += """
-              ConfirmOrder
-              Tic
-    """ + self.AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              GetPackingList
-              PackPackingList
-              Tic
-    """ + self.AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              StartPackingList
-              StopPackingList
-              DeliverPackingList
-              Tic
-    """ + self.AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              GetInvoice
-              CheckInvoiceCausalityStateSolved
-              CheckInvoiceNormalMovements
-    """
-    sequence_list.addSequenceString(sequence_string)
-    sequence_list.play(self, quiet=quiet)
-
-  def test_TradeModelRuleSimulationBuildInvoiceNewTradeCondition(self, quiet=quiet):
-    """Check that after changing trade condition invoice is diverged"""
-    sequence_list = SequenceList()
-    sequence_string = self.TRADE_MODEL_RULE_SIMULATION_SEQUENCE_STRING
-    sequence_string += """
-              ConfirmOrder
-              Tic
-    """ + self.AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              GetPackingList
-              PackPackingList
-              Tic
-    """ + self.AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              StartPackingList
-              StopPackingList
-              DeliverPackingList
-              Tic
-    """ + self.AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              GetInvoice
-              CheckInvoiceCausalityStateSolved
-              CheckInvoiceNormalMovements
-
-              SetTradeConditionOld
-
-              CreateTradeCondition
-              SpecialiseTradeConditionWithBusinessProcess
-              CreateTradeModelLine
-              ModifyTradeModelLineNewTax
-              CreateTradeModelLine
-              ModifyTradeModelLineNewDiscount
-              Tic
-
-              SpecialiseInvoiceTradeCondition
-              Tic
-              CheckInvoiceCausalityStateDiverged
-    """
-    sequence_list.addSequenceString(sequence_string)
-    sequence_list.play(self, quiet=quiet)
-
-  def test_TradeModelRuleSimulationBuildInvoiceNewInvoiceLineSupport(self, quiet=quiet):
-    """Check how is supported addition of invoice line to invoice build from order"""
-    sequence_list = SequenceList()
-    sequence_string = self.TRADE_MODEL_RULE_SIMULATION_SEQUENCE_STRING
-    sequence_string += """
-              ConfirmOrder
-              Tic
-    """ + self.AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              GetPackingList
-              PackPackingList
-              Tic
-    """ + self.AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              StartPackingList
-              StopPackingList
-              DeliverPackingList
-              Tic
-    """ + self.AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              GetInvoice
-              CheckInvoiceCausalityStateSolved
-              CheckInvoiceNormalMovements
-
-              CreateInvoiceLine
-              ModifyInvoiceLineDiscounted
-              CreateInvoiceLine
-              ModifyInvoiceLineDiscountedTaxed
-              CreateInvoiceLine
-              ModifyInvoiceLineTaxed
-
-              Tic
-
-              CheckInvoiceCausalityStateSolved
-
-              StartInvoice
-              Tic
-              CheckInvoiceCausalityStateSolved
-              CheckInvoiceNormalMovements
-              CheckInvoiceAccountingMovements
-              StopInvoice
-              DeliverInvoice
-              Tic
-    """
-    sequence_list.addSequenceString(sequence_string)
-    sequence_list.play(self, quiet=quiet)
-
-  def test_TradeModelRuleSimulationBuildInvoiceInvoiceLineModifyDivergencyAndSolving(self, quiet=quiet):
-    """Check that after changing invoice line invoice is properly diverged and it is possible to solve"""
-    sequence_list = SequenceList()
-    sequence_string = self.TRADE_MODEL_RULE_SIMULATION_SEQUENCE_STRING
-    sequence_string += """
-              ConfirmOrder
-              Tic
-    """ + self.AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              GetPackingList
-              PackPackingList
-              Tic
-    """ + self.AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              StartPackingList
-              StopPackingList
-              DeliverPackingList
-              Tic
-    """ + self.AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              GetInvoice
-              CheckInvoiceCausalityStateSolved
-              CheckInvoiceNormalMovements
-
-              GetInvoiceLineDiscounted
-              GetInvoiceLineDiscountedTaxed
-              GetInvoiceLineTaxed
-
-              ModifyQuantityInvoiceLineDiscounted
-              ModifyQuantityInvoiceLineDiscountedTaxed
-              ModifyQuantityInvoiceLineTaxed
-              Tic
-              CheckInvoiceCausalityStateDiverged
-              AcceptDecisionQuantityInvoice
-              Tic
-              CheckInvoiceCausalityStateSolved
-              CheckInvoiceNormalMovements
-    """
-    sequence_list.addSequenceString(sequence_string)
-    sequence_list.play(self, quiet=quiet)
-
-  def test_TradeModelRuleSimulationBuildInvoiceBuildInvoiceTransactionLines(self, quiet=quiet):
-    """Check that having properly configured invoice transaction rule it invoice transaction lines are nicely generated and have proper amounts"""
-    sequence_list = SequenceList()
-    sequence_string = self.TRADE_MODEL_RULE_SIMULATION_SEQUENCE_STRING
-    sequence_string += """
-              ConfirmOrder
-              Tic
-    """ + self.AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              GetPackingList
-              PackPackingList
-              Tic
-    """ + self.AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              StartPackingList
-              StopPackingList
-              DeliverPackingList
-              Tic
-    """ + self.AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              GetInvoice
-              CheckInvoiceCausalityStateSolved
-              CheckInvoiceNormalMovements
-
-              StartInvoice
-              Tic
-              CheckInvoiceCausalityStateSolved
-              CheckInvoiceNormalMovements
-              CheckInvoiceAccountingMovements
-              StopInvoice
-              DeliverInvoice
-              Tic
-    """
-    sequence_list.addSequenceString(sequence_string)
-    sequence_list.play(self, quiet=quiet)
-
-  PACKING_LIST_SPLIT_INVOICE_BUILD_SEQUENCE_STRING = \
-      TRADE_MODEL_RULE_SIMULATION_SEQUENCE_STRING + """
-              ConfirmOrder
-              Tic
-    """ + AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              GetPackingList
-              DecreasePackingListLineListQuantity
-              Tic
-    """ + AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              CheckPackingListDiverged
-              SplitAndDeferPackingList
-              Tic
-    """ + AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              GetNewPackingList
-              PackPackingList
-              Tic
-    """ + AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              StartPackingList
-              StopPackingList
-              DeliverPackingList
-              Tic
-    """ + AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              GetInvoice
-              CheckInvoiceCausalityStateSolved
-              CheckInvoiceNormalMovements
-
-              SetNewPackingListAsPackingList
-              PackPackingList
-              Tic
-              StartPackingList
-    """ + AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              StopPackingList
-              DeliverPackingList
-              Tic
-    """ + AGGREGATED_AMOUNT_SIMULATION_CHECK_SEQUENCE_STRING + """
-              GetInvoice
-              CheckInvoiceCausalityStateSolved
-              CheckInvoiceNormalMovements
-    """
-
-  def test_TradeModelRuleSimulationPackingListSplitBuildInvoiceBuildDifferentRatio(self, quiet=quiet):
-    """Check building invoice after splitting packing list using different ratio"""
-    self.modified_packing_list_line_quantity_ratio = 0.4
-    sequence_list = SequenceList()
-    sequence_list.addSequenceString(
-        self.PACKING_LIST_SPLIT_INVOICE_BUILD_SEQUENCE_STRING)
-    sequence_list.play(self, quiet=quiet)
-
-  def test_TradeModelRuleSimulationPackingListSplitBuildInvoiceBuild(self, quiet=quiet):
-    """Check building invoice after splitting packing list"""
-    sequence_list = SequenceList()
-    sequence_list.addSequenceString(
-        self.PACKING_LIST_SPLIT_INVOICE_BUILD_SEQUENCE_STRING)
-    sequence_list.play(self, quiet=quiet)
-
-  def test_getAggregatedAmountListWithComplexModelLinesCreateInEasyOrder(self, quiet=quiet):
-    """
-    Test the return of getAggregatedAmountList in the case of many model lines
-    depending each others. In this test, lines are created in the order of the
-    dependencies (it means that if a line A depend of a line B, line B is
-    created before A). This is the most easy case.
-
-    Dependency tree :
-    ModelLineTaxContributingToTotalTax : A
-    ModelLineDiscountContributingToTotalDiscount : B
-    ModelLineTaxContributingToTotalTax2 : C
-    ModelLineTotalTax : D
-    ModelLineTotalDiscount : E
-
-                              D       E
-                               \     /
-                                \   /
-                                 \ /
-                                  C      B
-                                   \    /
-                                    \  /
-                                     \/
-                                      A
-    Model line creation order : E, D, C, B, A
-    """
-    sequence_list = SequenceList()
-    sequence_string = self.COMMON_DOCUMENTS_CREATION_SEQUENCE_STRING + """
-              CreateBusinessProcess
-              CreateBusinessLink
-              ModifyBusinessLinkTaxing
-              CreateBusinessLink
-              ModifyBusinessLinkDiscounting
-              CreateTradeModelPath
-              CreateTradeCondition
-              SpecialiseTradeConditionWithBusinessProcess
-              CreateTradeModelLine
-              ModifyTradeModelLineTotalDiscount
-              CreateTradeModelLine
-              ModifyTradeModelLineTotalTax
-              CreateTradeModelLine
-              ModifyTradeModelLineTaxContributingToTotalTax2
-              CreateTradeModelLine
-              ModifyTradeModelLineDiscountContributingToTotalDiscount
-              CreateTradeModelLine
-              ModifyTradeModelLineTaxContributingToTotalTax
-              Tic
-              CreateOrder
-              SpecialiseOrderTradeCondition
-              FillOrder
-              Tic
-              CreateOrderLine
-              ModifyOrderLineTaxed
-              CreateOrderLine
-              ModifyOrderLineDiscounted
-              Tic
-              CheckAggregatedAmountListWithComplexBaseContributionBaseApplication
-    """
-    sequence_list.addSequenceString(sequence_string)
-    sequence_list.play(self, quiet=quiet)
-
-  def test_getAggregatedAmountListWithComplexModelLinesCreateInRandomOrder(self, quiet=quiet):
-    """
-    Test the return of getAggregatedAmountList in the case of many model lines
-    depending each others. In this test, lines are created in a random order,
-    not in the dependencies order (it means that if a line A depend of a 
-    line B, line A can be created before line B). getAggregatedAmountList
-    should be able to handle this case and redo calculation until all
-    dependencies are satisfied
-
-    Dependency tree :
-    ModelLineTaxContributingToTotalTax : A
-    ModelLineDiscountContributingToTotalDiscount : B
-    ModelLineTaxContributingToTotalTax2 : C
-    ModelLineTotalTax : D
-    ModelLineTotalDiscount : E
-
-                              D       E
-                               \     /
-                                \   /
-                                 \ /
-                                  C      B
-                                   \    /
-                                    \  /
-                                     \/
-                                      A
-    Model line creation order : A, C, D, B, E
-    """
-    sequence_list = SequenceList()
-    sequence_string = self.COMMON_DOCUMENTS_CREATION_SEQUENCE_STRING + """
-              CreateBusinessProcess
-              CreateBusinessLink
-              ModifyBusinessLinkTaxing
-              CreateBusinessLink
-              ModifyBusinessLinkDiscounting
-              CreateTradeModelPath
-              CreateTradeCondition
-              SpecialiseTradeConditionWithBusinessProcess
-              CreateTradeModelLine
-              ModifyTradeModelLineTaxContributingToTotalTax
-              CreateTradeModelLine
-              ModifyTradeModelLineTaxContributingToTotalTax2
-              CreateTradeModelLine
-              ModifyTradeModelLineTotalTax
-              CreateTradeModelLine
-              ModifyTradeModelLineDiscountContributingToTotalDiscount
-              CreateTradeModelLine
-              ModifyTradeModelLineTotalDiscount
-              Tic
-              CreateOrder
-              SpecialiseOrderTradeCondition
-              FillOrder
-              Tic
-              CreateOrderLine
-              ModifyOrderLineTaxed
-              CreateOrderLine
-              ModifyOrderLineDiscounted
-              Tic
-              CheckAggregatedAmountListWithComplexBaseContributionBaseApplication
-    """
-    sequence_list.addSequenceString(sequence_string)
-    sequence_list.play(self, quiet=quiet)
-
   def test_BuildTradeModelLineAndAccountingFromOrder(self):
     business_process = self.createBusinessProcess()
-    business_link = self.createBusinessLink(business_process,
-                              trade_phase='default/tax')
-
-    product = self.createResource('Product',
-                              title='Product',
-                              use='normal',
-                              base_contribution='base_amount/tax')
-    tax = self.createResource('Service',
-                              title='Tax',
-                              use='tax')
-    currency = self.createResource('Currency', title='EUR')
-    trade_condition = self.createTradeCondition()
-    trade_condition.setSpecialiseValue(business_process)
-    trade_model_line = self.createTradeModelLine(
-                              trade_condition,
-                              reference='VAT',
-                              price=.15,
-                              resource_value=tax,
-                              trade_phase='default/tax',
-                              base_application='base_amount/tax',)
-    source = self.portal.organisation_module.newContent(
-                              portal_type='Organisation')
-    destination = self.portal.organisation_module.newContent(
-                              portal_type='Organisation')
-
-    order = self.createOrder()
-    order.edit(source_value=source,
-               destination_value=destination,
-               source_section_value=source,
-               destination_section_value=destination,
-               specialise_value=trade_condition,
-               price_currency_value=currency,
-               start_date=self.order_date,
-               stop_date=self.order_date,)
-    order.newContent(
-                portal_type=self.order_line_portal_type,
-                resource_value=product,
-                quantity=10,
-                price=100)
+
+    product = self.createProductTaxed()
+    tax = self.createServiceTax()
+    trade_condition = self.createTradeCondition(
+      business_process, (
+      dict(reference='VAT',
+           price=.15,
+           resource_value=tax,
+           trade_phase='default/tax',
+           base_application='base_amount/tax'),
+      ))
+    source = self.createNode()
+    destination = self.createNode()
+
+    order = self.createOrder(trade_condition, (
+      dict(price=100, quantity=10, resource_value=product),
+      ),
+      source_value=source,
+      destination_value=destination,
+      source_section_value=source,
+      destination_section_value=destination)
 
     order.plan()
     order.confirm()
@@ -2084,33 +815,22 @@ class TestTradeModelLine(TestTradeModelLineMixin):
                       self.expense_account)
     self.assertEquals(1000, income_movement.getSourceCredit())
 
-
   def test_BuildTradeModelLineAndAccountingFromInvoice(self):
     business_process = self.createBusinessProcess()
-    business_link = self.createBusinessLink(business_process,
-                              trade_phase='default/tax')
-
-    product = self.createResource('Product',
-                              title='Product',
-                              use='normal',
-                              base_contribution='base_amount/tax')
-    tax = self.createResource('Service',
-                              title='Tax',
-                              use='tax')
+
+    product = self.createProductTaxed()
+    tax = self.createServiceTax()
     currency = self.createResource('Currency', title='EUR')
-    trade_condition = self.createTradeCondition()
-    trade_condition.setSpecialiseValue(business_process)
-    trade_model_line = self.createTradeModelLine(
-                              trade_condition,
-                              reference='VAT',
-                              price=.15,
-                              resource_value=tax,
-                              trade_phase='default/tax',
-                              base_application='base_amount/tax',)
-    source = self.portal.organisation_module.newContent(
-                              portal_type='Organisation')
-    destination = self.portal.organisation_module.newContent(
-                              portal_type='Organisation')
+    trade_condition = self.createTradeCondition(
+      business_process, (
+      dict(reference='VAT',
+           price=.15,
+           resource_value=tax,
+           trade_phase='default/tax',
+           base_application='base_amount/tax'),
+      ))
+    source = self.createNode()
+    destination = self.createNode()
 
     invoice = self.portal.accounting_module.newContent(
                portal_type=self.invoice_portal_type,
@@ -2177,8 +897,8 @@ class TestTradeModelLine(TestTradeModelLineMixin):
       and trade model line can works with appropriate context(delivery or
       movement) only.
     """
-    tax = self.createResource('Service', title='Tax', use='tax')
-    trade_condition = self.createTradeCondition()
+    tax = self.createServiceTax()
+    trade_condition = self.createTradeCondition(self.createBusinessProcess())
     # create a model line and set target level to `delivery`.
     # XXX When it is possible to accumulate contributed quantities between
     #     input amounts, the trade condition should be configured as follows:
@@ -2199,8 +919,7 @@ class TestTradeModelLine(TestTradeModelLineMixin):
     # create an order.
     resource_A = self.createResource('Product', title='A')
     resource_B = self.createResource('Product', title='B')
-    order = self.createOrder()
-    order.setSpecialiseValue(trade_condition)
+    order = self.createOrder(trade_condition)
     base_contribution_list = 'base_amount/tax', 'base_amount/extra_fee'
     order.setBaseContributionList(base_contribution_list)
     kw = {'portal_type': self.order_line_portal_type,
@@ -2274,7 +993,7 @@ if base_application == 'base_amount/extra_fee':
     """
       Test if trade model line works with rounding.
     """
-    trade_condition = self.createTradeCondition()
+    trade_condition = self.createTradeCondition(self.createBusinessProcess())
     # create a model line and set target level to `delivery`
     tax = self.createTradeModelLine(trade_condition,
                                     reference='TAX',
@@ -2294,8 +1013,7 @@ if base_application == 'base_amount/extra_fee':
     # create an order
     resource_A = self.createResource('Product', title='A')
     resource_B = self.createResource('Product', title='B')
-    order = self.createOrder()
-    order.setSpecialiseValue(trade_condition)
+    order = self.createOrder(trade_condition)
     order_line_1 = order.newContent(portal_type=self.order_line_portal_type,
                                     price=3333, quantity=1,
                                     resource_value=resource_A,
@@ -2309,13 +1027,13 @@ if base_application == 'base_amount/extra_fee':
     self.tic()
 
     # check the result without rounding
-    amount_list = trade_condition.getAggregatedAmountList(order, rounding=False)
+    amount_list = order.getAggregatedAmountList(rounding=False)
     self.assertEqual(1, len(amount_list))
     self.assertEqual(set([order_line_1, order_line_2]),
                      set(amount_list[0].getCausalityValueList()))
     self.assertEqual((3333+171)*0.05, amount_list[0].getTotalPrice())
     # check the result with rounding
-    amount_list = trade_condition.getAggregatedAmountList(order, rounding=True)
+    amount_list = order.getAggregatedAmountList(rounding=True)
     self.assertEqual(1, len(amount_list))
     self.assertEqual(set([order_line_1, order_line_2]),
                      set(amount_list[0].getCausalityValueList()))
@@ -2332,31 +1050,27 @@ if base_application == 'base_amount/extra_fee':
       return result
 
     # check the result without rounding
-    amount_list = trade_condition.getAggregatedAmountList(order, rounding=False)
+    amount_list = order.getAggregatedAmountList(rounding=False)
     self.assertEqual(2, len(amount_list))
     self.assertEqual(3333*0.05+171*0.05, getTotalAmount(amount_list))
     # check the result with rounding
-    amount_list = trade_condition.getAggregatedAmountList(order, rounding=True)
+    amount_list = order.getAggregatedAmountList(rounding=True)
     self.assertEqual(2, len(amount_list))
     self.assertEqual(174, getTotalAmount(amount_list))
 
     # check getAggregatedAmountList result of each movement
     # order line 1
-    amount_list = trade_condition.getAggregatedAmountList(order_line_1,
-                                                          rounding=False)
+    amount_list = order_line_1.getAggregatedAmountList(rounding=False)
     self.assertEqual(1, len(amount_list))
     self.assertEqual(3333*0.05, amount_list[0].getTotalPrice())
-    amount_list = trade_condition.getAggregatedAmountList(order_line_1,
-                                                          rounding=True)
+    amount_list = order_line_1.getAggregatedAmountList(rounding=True)
     self.assertEqual(1, len(amount_list))
     self.assertEqual(166, amount_list[0].getTotalPrice())
     # order line 2
-    amount_list = trade_condition.getAggregatedAmountList(order_line_2,
-                                                          rounding=False)
+    amount_list = order_line_2.getAggregatedAmountList(rounding=False)
     self.assertEqual(1, len(amount_list))
     self.assertEqual(171*0.05, amount_list[0].getTotalPrice())
-    amount_list = trade_condition.getAggregatedAmountList(order_line_2,
-                                                          rounding=True)
+    amount_list = order_line_2.getAggregatedAmountList(rounding=True)
     self.assertEqual(1, len(amount_list))
     self.assertEqual(8, amount_list[0].getTotalPrice())
 
@@ -2372,14 +1086,14 @@ if base_application == 'base_amount/extra_fee':
     self.tic()
 
     # check the result without rounding
-    amount_list = trade_condition.getAggregatedAmountList(order, rounding=False)
+    amount_list = order.getAggregatedAmountList(rounding=False)
     self.assertEqual(2, len(amount_list))
     self.assertEqual(3.3333*3333*0.05+171*0.05, getTotalAmount(amount_list))
     # check the result with rounding
     # both quantity and total price will be rounded so that the expression
     # should be "round_up(round_up(3.3333 * 3333) * 0.05) + round_up(round_up
     # (1* 171) * 0.05)"
-    amount_list = trade_condition.getAggregatedAmountList(order, rounding=True)
+    amount_list = order.getAggregatedAmountList(rounding=True)
     self.assertEqual(2, len(amount_list))
     self.assertEqual(565, getTotalAmount(amount_list))
 
@@ -2395,7 +1109,7 @@ if base_application == 'base_amount/extra_fee':
     transaction.commit()
     self.tic()
 
-    amount_list = trade_condition.getAggregatedAmountList(order, rounding=True)
+    amount_list = order.getAggregatedAmountList(rounding=True)
     # The expression should be "round_up(round_up(round_down(3.3333) * 3333)
     # * 0.05) + round_up(round_up(round_down(1) * 171) * 0.05)"
     self.assertEqual(2, len(amount_list))
@@ -2419,11 +1133,11 @@ if base_application == 'base_amount/extra_fee':
     self.tic()
 
     # check the result without rounding
-    amount_list = trade_condition.getAggregatedAmountList(order, rounding=False)
+    amount_list = order.getAggregatedAmountList(rounding=False)
     self.assertEqual(2, len(amount_list))
     self.assertEqual(3.3333*3333*0.05+171.1234*0.05, getTotalAmount(amount_list))
     # check the result with rounding
-    amount_list = trade_condition.getAggregatedAmountList(order, rounding=True)
+    amount_list = order.getAggregatedAmountList(rounding=True)
     # The expression should be "round_down(3.3333) * round_up(3333) * 0.05 +
     # round_down(1) * round_up(171.1234) * 0.05"
     self.assertEqual(2, len(amount_list))
@@ -2434,40 +1148,37 @@ if base_application == 'base_amount/extra_fee':
     Make sure that a movement which does not have any base_contribution values
     does not match to any trade model lines.
     """
-    trade_condition = self.createTradeCondition()
-
-    # create a model line
-    tax = self.createTradeModelLine(trade_condition,
-                                    reference='TAX',
-                                    base_application_list=['base_amount/tax'],
-                                    base_contribution_list=['base_amount/total_tax'])
-    tax.edit(price=0.05)
+    trade_condition = self.createTradeCondition(
+      (), (
+      dict(price=0.05,
+           reference='TAX',
+           base_application_list=['base_amount/tax'],
+           base_contribution_list=['base_amount/total_tax']),
+      ))
 
     # create an order
     resource_A = self.createResource('Product', title='A')
-    order = self.createOrder()
-    order.setSpecialiseValue(trade_condition)
-    # create a movement which should be aggregated
-    order_line_1 = order.newContent(portal_type=self.order_line_portal_type,
-                                    price=100, quantity=1,
-                                    resource_value=resource_A,
-                                    base_contribution_list=['base_amount/tax'])
-    # create a movement which base contribution is empty and shoud not be
-    # aggregated
-    order_line_2 = order.newContent(portal_type=self.order_line_portal_type,
-                                    price=50, quantity=1,
-                                    resource_value=resource_A,
-                                    base_contribution_list=[])
+    order = self.createOrder(
+      trade_condition, (
+      # create a movement which should be aggregated
+      dict(id='1',
+           price=100, quantity=1,
+           resource_value=resource_A,
+           base_contribution_list=['base_amount/tax']),
+      # create a movement which base contribution is empty and shoud not be
+      # aggregated
+      dict(id='2',
+           price=31, quantity=1,
+           resource_value=resource_A,
+           base_contribution_list=[]),
+      ))
 
     transaction.commit()
     self.tic()
 
     # check the result
-    amount_list = trade_condition.getAggregatedAmountList(order)
-    self.assertEqual(1, len(amount_list))
-    self.assertEqual(set([order_line_1]),
-                     set(amount_list[0].getCausalityValueList()))
-    self.assertEqual(100*0.05, amount_list[0].getTotalPrice())
+    amount, = order.getAggregatedAmountList()
+    self.assertEqual(100*0.05, amount.getTotalPrice())
 
 
 class TestTradeModelLineSale(TestTradeModelLine):
@@ -2479,6 +1190,7 @@ class TestTradeModelLineSale(TestTradeModelLine):
   packing_list_portal_type = 'Sale Packing List'
   trade_condition_portal_type = 'Sale Trade Condition'
 
+
 def test_suite():
   suite = unittest.TestSuite()
   suite.addTest(unittest.makeSuite(TestTradeModelLineSale))
diff --git a/product/ERP5Legacy/Document/BusinessPath.py b/product/ERP5Legacy/Document/BusinessPath.py
index 35e46a6a4b..f981d6791d 100644
--- a/product/ERP5Legacy/Document/BusinessPath.py
+++ b/product/ERP5Legacy/Document/BusinessPath.py
@@ -33,11 +33,11 @@ from AccessControl import ClassSecurityInfo
 
 from Products.ERP5Type import Permissions, PropertySheet, interfaces
 from Products.ERP5.Document.Path import Path
-from Products.ERP5.Document.Predicate import Predicate
+from Products.ERP5.Document.Amount import Amount
 
 import zope.interface
 
-class BusinessPath(Path, Predicate):
+class BusinessPath(Path, Amount):
   """
     The BusinessPath class embeds all information related to
     lead times and parties involved at a given phase of a business
diff --git a/product/ERP5Legacy/tests/testLegacyTradeModelLine.py b/product/ERP5Legacy/tests/testLegacyTradeModelLine.py
new file mode 100644
index 0000000000..6ef5b18a10
--- /dev/null
+++ b/product/ERP5Legacy/tests/testLegacyTradeModelLine.py
@@ -0,0 +1,134 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
+#          Julien Muchembled <jm@nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility 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
+# guarantees 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.
+#
+##############################################################################
+
+import sys
+from Products.ERP5Legacy.tests import testLegacyBPMCore
+sys.modules['Products.ERP5.tests.testBPMCore'] = testLegacyBPMCore
+from Products.ERP5.tests.testTradeModelLine import *
+
+###
+##  TestTradeModelLine
+##
+
+TestTradeModelLine.trade_model_path_portal_type = None
+TestTradeModelLine.business_link_portal_type = None
+
+def createBusinessProcess(self, *args, **kw):
+  business_process = super(TestTradeModelLine, self) \
+    .createBusinessProcess(*args, **kw)
+  taxed = self.createBusinessState(business_process, reference='taxed')
+  invoiced = self.createBusinessState(business_process, reference='invoiced')
+  self.createBusinessPath(business_process, trade_phase='default/discount',
+    predecessor_value=invoiced, successor_value=taxed)
+  self.createBusinessPath(business_process, trade_phase='default/tax',
+    predecessor_value=invoiced, successor_value=taxed)
+  return business_process
+TestTradeModelLine.createBusinessProcess = createBusinessProcess
+
+def checkWithoutBPM(self, order):
+  transaction.commit() # clear transactional cache
+  order.getSpecialiseValue()._setSpecialise(None)
+  self.checkAggregatedAmountList(order)
+  applied_rule_id = order.getCausalityRelatedId(portal_type='Applied Rule')
+  order.expand(applied_rule_id=applied_rule_id)
+  for line in order.getMovementList():
+    simulation_movement_list, = self.getTradeModelSimulationMovementList(line)
+    self.assertFalse(simulation_movement_list)
+  transaction.abort()
+TestTradeModelLine.checkWithoutBPM = checkWithoutBPM
+
+def checkModelLineOnDelivery(self, delivery):
+  transaction.commit() # clear transactional cache
+  delivery.newContent(portal_type='Trade Model Line',
+                      price=0.5,
+                      base_application='base_amount/discount',
+                      base_contribution='base_amount/total_discount',
+                      trade_phase='default/discount',
+                      resource_value=self['service/discount'],
+                      reference='total_dicount_2',
+                      int_index=10)
+  discount_price = (3*4) * 0.5
+  tax_price = (1*2) * 0.2
+  total_tax_price = tax_price * 2 * 0.12
+  self.getAggregatedAmountDict(delivery,
+      service_tax=dict(total_price=tax_price),
+      total_dicount_2=dict(total_price=discount_price),
+      service_tax_2=dict(total_price=tax_price),
+      tax_3=dict(total_price=total_tax_price),
+      total_discount=dict(total_price=(total_tax_price+discount_price) * 0.8))
+  transaction.abort()
+TestTradeModelLine.checkModelLineOnDelivery = checkModelLineOnDelivery
+
+def checkComposition(self, movement, specialise_value_list, type_count_dict):
+  composed = movement.asComposedDocument()
+  self.assertTrue(movement in composed._effective_model_list)
+  self.assertSameSet(composed.getSpecialiseValueList(),
+                     specialise_value_list)
+  count = 0
+  for portal_type, n in type_count_dict.iteritems():
+    if portal_type:
+      count += n
+      self.assertEqual(n, len(composed.objectValues(portal_type=portal_type)))
+  self.assertTrue(count, len(composed.objectValues()))
+TestTradeModelLine.checkComposition = checkComposition
+
+def checkTradeModelRuleSimulationExpand(self, delivery):
+  expected_result_dict = self[delivery.getPath()]
+  price_currency = self['price_currency']
+
+  # There is no 'specialise' on the packing list, deleting entire branches
+  # of the simulation tree. See also SimulationMovement.asComposedDocument
+  no_expand = 'packing_list' in self and 'invoice' not in self
+
+  for line in delivery.getMovementList():
+    simulation_movement_list, = \
+      self.getTradeModelSimulationMovementList(line)
+    if no_expand:
+      self.assertFalse(simulation_movement_list)
+      continue
+    result_dict = dict((sm.getResourceValue().getUse(), sm)
+                       for sm in simulation_movement_list)
+    self.assertEqual(len(simulation_movement_list),
+                     len(result_dict))
+    for use in 'discount', 'tax':
+      total_price = expected_result_dict[use].get(line.getId())
+      if total_price:
+        sm = result_dict.pop(use)
+        self.assertEqual(sm.getTotalPrice(), total_price)
+        self.assertEqual(1, len(sm.getCausalityValueList()))
+        self.assertEqual(1, len(sm.getCausalityValueList(
+          portal_type='Business Path')))
+        self.assertEqual(0, len(sm.getCausalityValueList(
+          portal_type='Trade Model Line')))
+        self.assertEqual(sm.getBaseApplicationList(),
+                          ['base_amount/' + use])
+        self.assertEqual(sm.getBaseContributionList(),
+                          dict(discount=['base_amount/tax'], tax=[])[use])
+    self.assertEqual({}, result_dict)
+TestTradeModelLine.checkTradeModelRuleSimulationExpand = \
+  checkTradeModelRuleSimulationExpand
diff --git a/product/ERP5Type/Utils.py b/product/ERP5Type/Utils.py
index 91ada44ab1..cbda429b55 100644
--- a/product/ERP5Type/Utils.py
+++ b/product/ERP5Type/Utils.py
@@ -889,7 +889,7 @@ def writeLocalDocument(class_id, text, create=1, instance_home=None):
     f.close()
   # load the file, so that an error is raised if file is invalid
   module = imp.load_source(class_id, path)
-  getattr(module, class_id)
+  getattr(module, class_id, 'patch')
 
 def setDefaultClassProperties(property_holder):
   """Initialize default properties for ERP5Type Documents.
@@ -948,10 +948,6 @@ def importLocalDocument(class_id, document_path = None):
   f = open(path)
   try:
     document_module = imp.load_source(module_path, path, f)
-    document_class = getattr(document_module, class_id)
-    document_constructor = DocumentConstructor(document_class)
-    document_constructor_name = "add%s" % class_id
-    document_constructor.__name__ = document_constructor_name
   except Exception:
     f.close()
     if document_module is not None:
@@ -959,6 +955,19 @@ def importLocalDocument(class_id, document_path = None):
     raise
   else:
     f.close()
+  # Tolerate that Document doesn't define any class, which can be useful if we
+  # only want to monkey patch.
+  # XXX A new 'Patch' folder should be introduced instead. Each module would
+  #     define 2 methods: 'patch' and 'unpatch' (for proper upgrading).
+  try:
+    document_class = getattr(document_module, class_id)
+  except AttributeError:
+    document_module.patch
+    return
+  else:
+    document_constructor = DocumentConstructor(document_class)
+    document_constructor_name = "add%s" % class_id
+    document_constructor.__name__ = document_constructor_name
     setattr(Products.ERP5Type.Document, class_id, document_module)
     setattr(Products.ERP5Type.Document, document_constructor_name,
                                       document_constructor)
-- 
2.30.9