From 49a9b13ec2b7f27195fd87c69f1c4b96f2059d27 Mon Sep 17 00:00:00 2001
From: Yusei Tahara <yusei@nexedi.com>
Date: Wed, 2 Dec 2009 07:39:41 +0000
Subject: [PATCH] Separate complex trade model line test to another test case
 class and add 4 more tests.

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@30971 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5/tests/testTradeModelLine.py | 634 +++++++++++++++++++----
 1 file changed, 522 insertions(+), 112 deletions(-)

diff --git a/product/ERP5/tests/testTradeModelLine.py b/product/ERP5/tests/testTradeModelLine.py
index 3ad37d0a0c..b028f3616f 100644
--- a/product/ERP5/tests/testTradeModelLine.py
+++ b/product/ERP5/tests/testTradeModelLine.py
@@ -32,6 +32,7 @@ import transaction
 
 from Products.ERP5.tests.testBPMCore import TestBPMMixin
 from Products.ERP5Type.tests.Sequence import SequenceList
+from Products.ERP5Type.tests.utils import createZODBPythonScript
 from DateTime import DateTime
 from Products.CMFCore.utils import getToolByName
 from Products.ERP5.PropertySheet.TradeModelLine import (TARGET_LEVEL_MOVEMENT,
@@ -2657,83 +2658,143 @@ class TestTradeModelLine(TestTradeModelLineMixin):
     self.assertEqual(2, len(amount_list))
     self.assertEqual(508.51000000000005, getTotalAmount(amount_list))
 
-  def test_complexTradeModel(self):
-    """Make sure that trade model line is capable of complex use cases in the
-    real world.
-    """
+
+class TestComplexTradeModelLineUseCase(TestTradeModelLineMixin):
+  """This test provides several complex use cases which are seen in the normal
+  shop and make sure that trade model line is capable of real business scene.
+  """
+
+  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 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
+
+  def getAmount(self, order, reference, return_object=False):
+    trade_condition = order.getSpecialiseValue()
+    for movement in trade_condition.getAggregatedAmountList(order):
+      if movement.getReference() == reference:
+        if return_object == True:
+          return movement
+        else:
+          return movement.getTotalPrice()
+
+  def appendBaseContributionCategory(self, document, new_category):
+    base_contribution_value_list = document.getBaseContributionValueList()
+    document.setBaseContributionValueList(
+      base_contribution_value_list+[new_category])
+
+  def beforeTearDown(self):
+    # abort any transaction
+    transaction.abort()
+    # put non finished activities into ignored state
+    activity_connection = self.portal.cmf_activity_sql_connection
+    for table in 'message', 'message_queue':
+      activity_connection.manage_test(
+          'delete from %s where processing_node=-2' % table)
+
+    def removeAll(*args):
+      for container in args:
+        container.manage_delObjects(ids=list(container.objectIds()))
+    removeAll(self.portal.sale_order_module,
+              self.portal.purchase_order_module,
+              self.portal.sale_trade_condition_module,
+              self.portal.purchase_trade_condition_module,
+              self.portal.person_module,
+              self.portal.organisation_module,
+              self.portal.service_module,
+              self.portal.product_module,
+              self.portal.currency_module,
+              self.portal.portal_categories.product_line,
+              self.portal.portal_categories.base_amount,
+              self.portal.portal_categories.trade_phase,
+              self.portal.portal_categories.use,
+              self.portal.portal_categories.quantity_unit,
+              )
+
+    self.stepTic()
+
+  def afterSetUp(self):
     portal = self.portal
 
+    # inherited method
+    self.createCategories()
+
+    self.stepTic()
+
     # add currency
     jpy = portal.currency_module.newContent(title='Yen', reference='JPY', base_unit_quantity='1')
 
-    transaction.commit()
-    self.tic()
+    self.stepTic()
 
     # add organisations
     my_company = portal.organisation_module.newContent(title='My Company')
     client_1 = portal.organisation_module.newContent(title='Client 1')
 
-    transaction.commit()
-    self.tic()
+    self.stepTic()
 
     # add base amount subcategories
     base_amount = portal.portal_categories.base_amount
-    total_price_of_ordered_items = base_amount.newContent(id='total_price_of_ordered_items')
-    discount_amount_of_non_vat_taxable = base_amount.newContent(id='discount_amount_of_non_vat_taxable')
-    discount_amount_of_vat_taxable = base_amount.newContent(id='discount_amount_of_vat_taxable')
-    vat_taxable = base_amount.newContent(id='vat_taxable')
-    total_price_without_vat = base_amount.newContent(id='total_price_without_vat')
-    total_price_of_vat_taxable = base_amount.newContent(id='total_price_of_vat_taxable')
-    discount_amount = base_amount.newContent(id='discount_amount')
-    vat_amount = base_amount.newContent(id='vat_amount')
-    total_price_with_vat = base_amount.newContent(id='total_price_with_vat')
-    poster_present_1dvd = base_amount.newContent(id='poster_present_1dvd')
-    poster_present_3cd = base_amount.newContent(id='poster_present_3cd')
-    special_discount_3cd = base_amount.newContent(id='special_discount_3cd')
+    self.total_price_of_ordered_items = base_amount.newContent(id='total_price_of_ordered_items')
+    self.discount_amount_of_non_vat_taxable = base_amount.newContent(id='discount_amount_of_non_vat_taxable')
+    self.discount_amount_of_vat_taxable = base_amount.newContent(id='discount_amount_of_vat_taxable')
+    self.vat_taxable = base_amount.newContent(id='vat_taxable')
+    self.total_price_without_vat = base_amount.newContent(id='total_price_without_vat')
+    self.total_price_of_vat_taxable = base_amount.newContent(id='total_price_of_vat_taxable')
+    self.discount_amount = base_amount.newContent(id='discount_amount')
+    self.vat_amount = base_amount.newContent(id='vat_amount')
+    self.total_price_with_vat = base_amount.newContent(id='total_price_with_vat')
+    self.poster_present_1dvd = base_amount.newContent(id='poster_present_1dvd')
+    self.poster_present_3cd = base_amount.newContent(id='poster_present_3cd')
+    self.special_discount_3cd = base_amount.newContent(id='special_discount_3cd')
     # add product line subcategories
     product_line = portal.portal_categories.product_line
     audio = product_line.newContent(id='audio')
     audio_cd = audio.newContent(id='cd')
-    video = product_line.newContent(id='dvd')
+    video = product_line.newContent(id='video')
     video_dvd = video.newContent(id='dvd')
     other_product = product_line.newContent(id='other')
     # add a quantity unit subcategory
-    unit = portal.portal_categories.quantity_unit.newContent(id='unit')
+    self.unit = portal.portal_categories.quantity_unit.newContent(id='unit')
 
-    transaction.commit()
-    self.tic()
+    self.stepTic()
 
     # create services
-    service_vat = portal.service_module.newContent(title='VAT')
-    service_discount = portal.service_module.newContent(title='VAT')
+    self.service_vat = portal.service_module.newContent(title='VAT')
+    self.service_discount = portal.service_module.newContent(title='VAT')
 
-    transaction.commit()
-    self.tic()
+    self.stepTic()
 
     # create products
     def addProductDocument(title, product_line_value):
       return portal.product_module.newContent(
         title=title,
         product_line_value=product_line_value,
-        quantity_unit_value=unit,
-        base_contribution_value_list=[vat_taxable,
-                                      total_price_of_ordered_items])
-    
-    music_album_1 = addProductDocument('Music Album 1', audio_cd)
-    movie_dvd_1 = addProductDocument('Movie DVD 1', video_dvd)
-    music_album_2 = addProductDocument('Movie Album 2', audio_cd)
-    candy = addProductDocument('Candy', other_product)
-    poster = addProductDocument('Poster', other_product)
-    music_album_3 = addProductDocument('Movie Album 3', audio_cd)
-    movie_dvd_2 = addProductDocument('Movie DVD 2', video_dvd)
-    music_album_4 = addProductDocument('Movie Album 4', audio_cd)
+        quantity_unit_value=self.unit,
+        base_contribution_value_list=[self.vat_taxable,
+                                      self.total_price_of_ordered_items])
 
-    transaction.commit()
-    self.tic()
+    self.music_album_1 = addProductDocument('Music Album 1', audio_cd)
+    self.movie_dvd_1 = addProductDocument('Movie DVD 1', video_dvd)
+    self.music_album_2 = addProductDocument('Movie Album 2', audio_cd)
+    self.candy = addProductDocument('Candy', other_product)
+    self.poster = addProductDocument('Poster', other_product)
+    self.music_album_3 = addProductDocument('Movie Album 3', audio_cd)
+    self.movie_dvd_2 = addProductDocument('Movie DVD 2', video_dvd)
+    self.music_album_4 = addProductDocument('Movie Album 4', audio_cd)
+
+    self.stepTic()
 
     # create a trade condition and add several common trade model lines in it.
-    trade_condition = self.createTradeCondition()
-    trade_condition.edit(
+    self.trade_condition = self.createTradeCondition()
+    self.trade_condition.edit(
       source_section_value=my_company,
       source_value=my_company,
       source_decision_value=my_company,
@@ -2741,7 +2802,7 @@ class TestTradeModelLine(TestTradeModelLineMixin):
       destination_value=client_1,
       destination_decision_value=client_1,
       price_currency_value=jpy)
-    trade_condition.newContent(
+    self.trade_condition.newContent(
       portal_type='Trade Model Line',
       title='Total Price Without VAT',
       reference='TOTAL_PRICE_WITHOUT_VAT',
@@ -2751,11 +2812,11 @@ class TestTradeModelLine(TestTradeModelLineMixin):
       target_level=TARGET_LEVEL_DELIVERY,
       create_line=True,
       trade_phase=None,
-      base_application_value_list=[discount_amount_of_non_vat_taxable,
-                                   discount_amount_of_vat_taxable,
-                                   total_price_of_ordered_items],
-      base_contribution_value_list=[total_price_without_vat])
-    trade_condition.newContent(
+      base_application_value_list=[self.discount_amount_of_non_vat_taxable,
+                                   self.discount_amount_of_vat_taxable,
+                                   self.total_price_of_ordered_items],
+      base_contribution_value_list=[self.total_price_without_vat])
+    self.trade_condition.newContent(
       portal_type='Trade Model Line',
       title='Total Price Of VAT Taxable',
       reference='TOTAL_PRICE_OF_VAT_TAXABLE',
@@ -2765,38 +2826,38 @@ class TestTradeModelLine(TestTradeModelLineMixin):
       target_level=TARGET_LEVEL_DELIVERY,
       create_line=True,
       trade_phase=None,
-      base_application_value_list=[discount_amount_of_vat_taxable,
-                                   vat_taxable],
-      base_contribution_value_list=[total_price_of_vat_taxable])
-    trade_condition.newContent(
+      base_application_value_list=[self.discount_amount_of_vat_taxable,
+                                   self.vat_taxable],
+      base_contribution_value_list=[self.total_price_of_vat_taxable])
+    self.trade_condition.newContent(
       portal_type='Trade Model Line',
       title='Discount Amount',
       reference='DISCOUNT_AMOUNT',
-      resource_value=service_discount,
+      resource_value=self.service_discount,
       price=1,
       quantity=None,
       efficiency=1,
       target_level=TARGET_LEVEL_DELIVERY,
       create_line=True,
       trade_phase_value=portal.portal_categories.trade_phase.default.invoicing,
-      base_application_value_list=[discount_amount_of_vat_taxable,
-                                   discount_amount_of_non_vat_taxable],
-      base_contribution_value_list=[discount_amount])
-    trade_condition.newContent(
+      base_application_value_list=[self.discount_amount_of_vat_taxable,
+                                   self.discount_amount_of_non_vat_taxable],
+      base_contribution_value_list=[self.discount_amount])
+    self.trade_condition.newContent(
       portal_type='Trade Model Line',
       title='VAT Amount',
       reference='VAT_AMOUNT',
-      resource_value=service_vat,
+      resource_value=self.service_vat,
       price=0.05,
       quantity=None,
       efficiency=1,
       target_level=TARGET_LEVEL_DELIVERY,
       create_line=True,
       trade_phase_value=portal.portal_categories.trade_phase.default.invoicing,
-      base_application_value_list=[discount_amount_of_vat_taxable,
-                                   vat_taxable],
-      base_contribution_value_list=[vat_amount])
-    trade_condition.newContent(
+      base_application_value_list=[self.discount_amount_of_vat_taxable,
+                                   self.vat_taxable],
+      base_contribution_value_list=[self.vat_amount])
+    self.trade_condition.newContent(
       portal_type='Trade Model Line',
       title='Total Price With VAT',
       reference='TOTAL_PRICE_WITH_VAT',
@@ -2806,34 +2867,40 @@ class TestTradeModelLine(TestTradeModelLineMixin):
       target_level=TARGET_LEVEL_DELIVERY,
       create_line=True,
       trade_phase=None,
-      base_application_value_list=[vat_amount, total_price_without_vat],
-      base_contribution_value_list=[total_price_with_vat])
+      base_application_value_list=[self.vat_amount,
+                                   self.total_price_without_vat],
+      base_contribution_value_list=[self.total_price_with_vat])
 
-    transaction.commit()
-    self.tic()
+    self.stepTic()
+
+  def test_usecase1(self):
+    """
+    Use case 1 : Buy 3 CDs or more, get 10% off them.
 
-    #
-    # Use case 1 : Buy 3 CDs or more, get 10 percent discount off them.
-    #
-    from Products.ERP5Type.tests.utils import createZODBPythonScript
+    1 CD   5000 yen
+    1 CD   3000 yen
+    1 Candy 100 yen
+    1 CD   2400 yen
+    discount (5000+3000+2400) * 0.1 = 1040 yen
+    """
     createZODBPythonScript(
-      portal.portal_skins.custom,
+      self.portal.portal_skins.custom,
       'TradeModelLine_calculate3CD10PercentDiscount',
       'current_aggregated_amount_list, current_movement, aggregated_movement_list',
-      '''\
+      """\
 total_quantity = sum([movement.getQuantity()
                       for movement in aggregated_movement_list])
 if total_quantity >= 3:
   return current_movement
 else:
   return None
-''')
+""")
     order = self.createOrder()
-    order.edit(specialise_value=trade_condition)
-    order.Order_applyTradeCondition(trade_condition)
+    order.edit(specialise_value=self.trade_condition)
+    order.Order_applyTradeCondition(order.getSpecialiseValue())
     order.newContent(portal_type='Trade Model Line',
                      reference='3CD_AND_10PERCENT_DISCOUNT_OFF_THEM',
-                     resource_value=service_discount,
+                     resource_value=self.service_discount,
                      price=-0.1,
                      quantity=None,
                      efficiency=1,
@@ -2841,59 +2908,388 @@ else:
                      calculation_script_id='TradeModelLine_calculate3CD10PercentDiscount',
                      create_line=True,
                      trade_phase=None,
-                     base_application_value_list=[special_discount_3cd],
-                     base_contribution_value_list=[discount_amount_of_vat_taxable])
+                     base_application_value_list=[self.special_discount_3cd],
+                     base_contribution_value_list=[self.discount_amount_of_vat_taxable])
 
-    def appendBaseContributionCategory(document, new_category):
-      base_contribution_value_list = document.getBaseContributionValueList()
-      document.setBaseContributionValueList(
-        base_contribution_value_list+[new_category])
-    
     order_line_1 = order.newContent(portal_type=self.order_line_portal_type,
-                                    resource_value=music_album_1,
+                                    resource_value=self.music_album_1,
                                     quantity=1,
                                     price=5000)
-    appendBaseContributionCategory(order_line_1, special_discount_3cd)
+    self.appendBaseContributionCategory(order_line_1, self.special_discount_3cd)
     order_line_2 = order.newContent(portal_type=self.order_line_portal_type,
-                                    resource_value=music_album_2,
+                                    resource_value=self.music_album_2,
                                     quantity=1,
                                     price=3000)
-    appendBaseContributionCategory(order_line_2, special_discount_3cd)
+    self.appendBaseContributionCategory(order_line_2, self.special_discount_3cd)
     order_line_3 = order.newContent(portal_type=self.order_line_portal_type,
-                                    resource_value=candy,
+                                    resource_value=self.candy,
                                     quantity=1,
                                     price=100)
 
-    transaction.commit()
-    self.tic()
-
-    def getAggregatedAmountResult(order, reference):
-      trade_condition = order.getSpecialiseValue()
-      for movement in trade_condition.getAggregatedAmountList(order):
-        if movement.getReference()==reference:
-          return movement.getTotalPrice()
+    self.stepTic()
 
     # check the current amount
-    self.assertEqual(getAggregatedAmountResult(order, 'TOTAL_PRICE_WITHOUT_VAT'),
-                     8100)
-    self.assertEqual(getAggregatedAmountResult(order, 'VAT_AMOUNT'), 405)
-    self.assertEqual(getAggregatedAmountResult(order, 'TOTAL_PRICE_WITH_VAT'),
-                     8505)
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITHOUT_VAT'), 8100)
+    self.assertEqual(self.getAmount(order, 'VAT_AMOUNT'), 405)
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITH_VAT'), 8505)
     # add one more cd, then total is 3. the special discount will be applied.
     order_line_4 = order.newContent(portal_type=self.order_line_portal_type,
-                                    resource_value=music_album_3,
+                                    resource_value=self.music_album_3,
                                     quantity=1,
                                     price=2400)
-    appendBaseContributionCategory(order_line_4, special_discount_3cd)
+    self.appendBaseContributionCategory(order_line_4, self.special_discount_3cd)
+
+    self.stepTic()
+
     # check again
-    self.assertEqual(getAggregatedAmountResult(order, '3CD_AND_10PERCENT_DISCOUNT_OFF_THEM'),
+    self.assertEqual(self.getAmount(order, '3CD_AND_10PERCENT_DISCOUNT_OFF_THEM'),
                      -1040)
-    self.assertEqual(getAggregatedAmountResult(order, 'TOTAL_PRICE_WITHOUT_VAT'),
-                     9460)
-    self.assertEqual(getAggregatedAmountResult(order, 'VAT_AMOUNT'), 473)
-    self.assertEqual(getAggregatedAmountResult(order, 'TOTAL_PRICE_WITH_VAT'),
-                     9933)
-    ### TODO: More use cases are needed.
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITHOUT_VAT'), 9460)
+    self.assertEqual(self.getAmount(order, 'VAT_AMOUNT'), 473)
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITH_VAT'), 9933)
+
+  def test_usecase2(self):
+    """
+    Use case 2 : Buy 3 CDs or more, get 500 yen off.
+
+    1 CD  5000 yen
+    1 CD  3000 yen
+    1 DVD 3000 yen
+    1 CD  2400 yen
+    discount 500 yen
+    """
+    createZODBPythonScript(
+      self.portal.portal_skins.custom,
+      'TradeModelLine_calculate3CD500YenDiscount',
+      'current_aggregated_amount_list, current_movement, aggregated_movement_list',
+      """\
+total_quantity = sum([movement.getQuantity() for movement in aggregated_movement_list])
+if total_quantity >= 3:
+  current_movement.setQuantity(-500)
+  return current_movement
+else:
+  return None
+""")
+    order = self.createOrder()
+    order.edit(specialise_value=self.trade_condition)
+    order.Order_applyTradeCondition(order.getSpecialiseValue())
+    order.newContent(portal_type='Trade Model Line',
+                     reference='3CD_AND_500YEN_OFF',
+                     resource_value=self.service_discount,
+                     price=1,
+                     quantity=None,
+                     efficiency=1,
+                     target_level=TARGET_LEVEL_DELIVERY,
+                     calculation_script_id='TradeModelLine_calculate3CD500YenDiscount',
+                     create_line=True,
+                     trade_phase=None,
+                     base_application_value_list=[self.special_discount_3cd],
+                     base_contribution_value_list=[self.discount_amount_of_vat_taxable])
+
+    order_line_1 = order.newContent(portal_type=self.order_line_portal_type,
+                                    resource_value=self.music_album_1,
+                                    quantity=1,
+                                    price=5000)
+    self.appendBaseContributionCategory(order_line_1, self.special_discount_3cd)
+    order_line_2 = order.newContent(portal_type=self.order_line_portal_type,
+                                    resource_value=self.music_album_2,
+                                    quantity=1,
+                                    price=3000)
+    self.appendBaseContributionCategory(order_line_2, self.special_discount_3cd)
+    order_line_3 = order.newContent(portal_type=self.order_line_portal_type,
+                                    resource_value=self.movie_dvd_1,
+                                    quantity=1,
+                                    price=3000)
+
+    self.stepTic()
+
+    # check the current amount
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITHOUT_VAT'), 11000)
+    self.assertEqual(self.getAmount(order, 'VAT_AMOUNT'), 550)
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITH_VAT'), 11550)
+    # add one more cd, then total is 3. the special discount will be applied.
+    order_line_4 = order.newContent(portal_type=self.order_line_portal_type,
+                                    resource_value=self.music_album_3,
+                                    quantity=1,
+                                    price=2400)
+    self.appendBaseContributionCategory(order_line_4, self.special_discount_3cd)
+    # check again
+    self.assertEqual(self.getAmount(order, '3CD_AND_500YEN_OFF'), -500)
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITHOUT_VAT'), 12900)
+    self.assertEqual(self.getAmount(order, 'VAT_AMOUNT'), 645)
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITH_VAT'), 13545)
+
+  def test_usecase3(self):
+    """
+    Use case 3 : Buy 3 CDs or more, get 10% off total.
+
+    1 CD  5000 yen
+    1 DVD 3000 yen
+    1 CD  3000 yen
+    1 CD  2400 yen
+    discount (5000+3000+3000+2400) * 0.1 = 1340 yen
+    """
+    createZODBPythonScript(
+      self.portal.portal_skins.custom,
+      'TradeModelLine_calculate3CD10PercentDiscountFromTotal',
+      'current_aggregated_amount_list, current_movement, aggregated_movement_list',
+      '''\
+special_discount_3cd = context.portal_categories.base_amount.special_discount_3cd
+total_quantity = sum([movement.getQuantity() for movement in current_aggregated_amount_list
+                      if special_discount_3cd in movement.getBaseContributionValueList()])
+if total_quantity >= 3:
+  return current_movement
+else:
+  return None
+''')
+    order = self.createOrder()
+    order.edit(specialise_value=self.trade_condition)
+    order.Order_applyTradeCondition(order.getSpecialiseValue())
+    order.newContent(portal_type='Trade Model Line',
+                     reference='3CD_10PERCENT_OFF_FROM_TOTAL',
+                     resource_value=self.service_discount,
+                     price=-0.1,
+                     quantity=None,
+                     efficiency=1,
+                     target_level=TARGET_LEVEL_DELIVERY,
+                     calculation_script_id='TradeModelLine_calculate3CD10PercentDiscountFromTotal',
+                     create_line=True,
+                     trade_phase=None,
+                     base_application_value_list=[self.total_price_of_ordered_items],
+                     base_contribution_value_list=[self.discount_amount_of_vat_taxable])
+
+    order_line_1 = order.newContent(portal_type=self.order_line_portal_type,
+                                    resource_value=self.music_album_1,
+                                    quantity=1,
+                                    price=5000)
+    self.appendBaseContributionCategory(order_line_1, self.special_discount_3cd)
+    order_line_2 = order.newContent(portal_type=self.order_line_portal_type,
+                                    resource_value=self.movie_dvd_1,
+                                    quantity=1,
+                                    price=3000)
+    order_line_3 = order.newContent(portal_type=self.order_line_portal_type,
+                                    resource_value=self.music_album_2,
+                                    quantity=1,
+                                    price=3000)
+    self.appendBaseContributionCategory(order_line_3, self.special_discount_3cd)
+
+    self.stepTic()
+
+    # check the current amount
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITHOUT_VAT'), 11000)
+    self.assertEqual(self.getAmount(order, 'VAT_AMOUNT'), 550)
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITH_VAT'), 11550)
+    # add one more cd, then total is 3. the special discount will be applied.
+    order_line_4 = order.newContent(portal_type=self.order_line_portal_type,
+                                    resource_value=self.music_album_3,
+                                    quantity=1,
+                                    price=2400)
+    self.appendBaseContributionCategory(order_line_4, self.special_discount_3cd)
+    # check again
+    self.assertEqual(self.getAmount(order, '3CD_10PERCENT_OFF_FROM_TOTAL'),
+                     -1340)
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITHOUT_VAT'), 12060)
+    self.assertEqual(self.getAmount(order, 'VAT_AMOUNT'), 603)
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITH_VAT'), 12663)
+
+  def test_usecase4(self):
+    """
+    Use case 4 : Buy 3 CDs or 1 DVD, get 1 poster free.
+
+    2 CD     6000 yen
+    1 DVD    3000 yen
+    1 Poster    0 yen
+    """
+    createZODBPythonScript(
+      self.portal.portal_skins.custom,
+      'TradeModelLine_calculate3CDOr1DVDForPoster',
+      'current_aggregated_amount_list, current_movement, aggregated_movement_list',
+      '''\
+poster_present_3cd = context.portal_categories.base_amount.poster_present_3cd
+poster_present_1dvd = context.portal_categories.base_amount.poster_present_1dvd
+
+total_quantity_3cd = sum([movement.getQuantity() for movement in aggregated_movement_list
+                          if poster_present_3cd in movement.getBaseContributionValueList()])
+total_quantity_1dvd = sum([movement.getQuantity() for movement in aggregated_movement_list
+                           if poster_present_1dvd in movement.getBaseContributionValueList()])
+if (total_quantity_3cd >= 3 or total_quantity_1dvd >= 1):
+  current_movement.setQuantity(1)
+  return current_movement
+else:
+  return None
+''')
+    order = self.createOrder()
+    order.edit(specialise_value=self.trade_condition)
+    order.Order_applyTradeCondition(order.getSpecialiseValue())
+    order.newContent(portal_type='Trade Model Line',
+                     reference='3CD_OR_1DVD_GET_1_POSTER_FREE',
+                     resource_value=self.poster,
+                     price=0,
+                     quantity=None,
+                     efficiency=1,
+                     target_level=TARGET_LEVEL_DELIVERY,
+                     calculation_script_id='TradeModelLine_calculate3CDOr1DVDForPoster',
+                     create_line=True,
+                     trade_phase=None,
+                     base_application_value_list=[self.poster_present_1dvd,
+                                                  self.poster_present_3cd])
+
+    order_line_1 = order.newContent(portal_type=self.order_line_portal_type,
+                                    resource_value=self.music_album_4,
+                                    quantity=2,
+                                    price=3000)
+    self.appendBaseContributionCategory(order_line_1, self.poster_present_3cd)
+
+    self.stepTic()
+
+    # check the current amount
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITHOUT_VAT'), 6000)
+    self.assertEqual(self.getAmount(order, 'VAT_AMOUNT'), 300)
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITH_VAT'), 6300)
+    self.assertEqual(self.getAmount(order, '3CD_OR_1DVD_GET_1_POSTER_FREE'),
+                     None)
+    # add 1 dvd, then 1 poster will be given.
+    order_line_2 = order.newContent(portal_type=self.order_line_portal_type,
+                                    resource_value=self.movie_dvd_1,
+                                    quantity=1,
+                                    price=3000)
+    self.appendBaseContributionCategory(order_line_2, self.poster_present_1dvd)
+
+    self.stepTic()
+    
+    # check again
+    one_free_poster_amount = self.getAmount(order,
+                                            '3CD_OR_1DVD_GET_1_POSTER_FREE',
+                                            return_object=True)
+    self.assertEqual(one_free_poster_amount.getTotalPrice(), 0)
+    self.assertEqual(one_free_poster_amount.getQuantity(), 1)
+    self.assertEqual(one_free_poster_amount.getPrice(), 0)
+    self.assertEqual(one_free_poster_amount.getResourceValue(), self.poster)
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITHOUT_VAT'), 9000)
+    self.assertEqual(self.getAmount(order, 'VAT_AMOUNT'), 450)
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITH_VAT'), 9450)
+
+    # even if we buy 3 CDs and 1 DVD, only one poster will be given.
+    order_line_3 = order.newContent(portal_type=self.order_line_portal_type,
+                                    resource_value=self.music_album_3,
+                                    quantity=1,
+                                    price=2400)
+    self.appendBaseContributionCategory(order_line_3, self.poster_present_3cd)
+
+    self.stepTic()
+
+    # check again
+    one_free_poster_amount = self.getAmount(order,
+                                            '3CD_OR_1DVD_GET_1_POSTER_FREE',
+                                            return_object=True)
+    self.assertEqual(one_free_poster_amount.getTotalPrice(), 0)
+    self.assertEqual(one_free_poster_amount.getQuantity(), 1)
+    self.assertEqual(one_free_poster_amount.getPrice(), 0)
+    self.assertEqual(one_free_poster_amount.getResourceValue(), self.poster)
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITHOUT_VAT'), 11400)
+    self.assertEqual(self.getAmount(order, 'VAT_AMOUNT'), 570)
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITH_VAT'), 11970)
+
+  def test_usecase5(self):
+    """
+    Use case 5 : Buy 3 CDs or more, 1 highest priced DVD in ordered 15% off.
+
+    1 DVD    3000 yen
+    1 DVD    1000 yen
+    2 CD    10000 yen
+    1 CD     3000 yen
+    discount 3000 * 0.15 = 450 yen
+    """
+    createZODBPythonScript(
+      self.portal.portal_skins.custom,
+      'TradeModelLine_calculate3CD15PercentDiscountOf1HighestPricedDVD',
+      'current_aggregated_amount_list, current_movement, aggregated_movement_list',
+      '''\
+total_quantity = sum([movement.getQuantity() for movement in aggregated_movement_list])
+if total_quantity >= 3:
+  price_dvd_list = []
+  product_line_dvd = context.portal_categories.product_line.video.dvd
+  for movement in current_aggregated_amount_list:
+    resource = movement.getResourceValue()
+    if resource.getProductLineValue() == product_line_dvd:
+      price_dvd_list.append((movement.getPrice(), movement))
+  if price_dvd_list:
+    price_dvd_list.sort()
+    highest_priced_dvd_movement = price_dvd_list[-1][1]
+    total_price = highest_priced_dvd_movement.getTotalPrice()
+
+    from Products.ERP5Type.Document import newTempSimulationMovement
+    causality_value_list = list(aggregated_movement_list) + [highest_priced_dvd_movement]
+    temporary_movement = newTempSimulationMovement(current_movement.getParentValue(), current_movement.getId())
+    temporary_movement.edit(title=current_movement.getProperty('title'),
+                            description=current_movement.getProperty('description'),
+                            resource=current_movement.getProperty('resource'),
+                            reference=current_movement.getProperty('reference'),
+                            int_index=current_movement.getProperty('int_index'),
+                            base_application_list=current_movement.getProperty('base_application_list'),
+                            base_contribution_list=current_movement.getProperty('base_contribution_list'),
+                            start_date=highest_priced_dvd_movement.getStartDate(),
+                            stop_date=highest_priced_dvd_movement.getStopDate(),
+                            create_line=current_movement.getProperty('is_create_line'),
+                            trade_phase_list=current_movement.getTradePhaseList(),
+                            causality_list=[movement.getRelativeUrl() for movement in causality_value_list])
+    temporary_movement.setPrice(current_movement.getProperty('price'))
+    temporary_movement.setQuantity(highest_priced_dvd_movement.getPrice())
+    return temporary_movement
+''')
+    order = self.createOrder()
+    order.edit(specialise_value=self.trade_condition)
+    order.Order_applyTradeCondition(order.getSpecialiseValue())
+    order.newContent(portal_type='Trade Model Line',
+                     reference='3CD_AND_1HIGHEST_PRICED_DVD_15PERCENT_OFF',
+                     resource_value=self.service_discount,
+                     price=-0.15,
+                     quantity=None,
+                     efficiency=1,
+                     target_level=TARGET_LEVEL_DELIVERY,
+                     calculation_script_id='TradeModelLine_calculate3CD15PercentDiscountOf1HighestPricedDVD',
+                     create_line=True,
+                     trade_phase=None,
+                     base_application_value_list=[self.special_discount_3cd],
+                     base_contribution_value_list=[self.discount_amount_of_vat_taxable])
+
+    order_line_1 = order.newContent(portal_type=self.order_line_portal_type,
+                                    resource_value=self.movie_dvd_1,
+                                    quantity=1,
+                                    price=3000)
+    order_line_2 = order.newContent(portal_type=self.order_line_portal_type,
+                                    resource_value=self.movie_dvd_2,
+                                    quantity=1,
+                                    price=1000)
+    order_line_3 = order.newContent(portal_type=self.order_line_portal_type,
+                                    resource_value=self.music_album_1,
+                                    quantity=1,
+                                    price=5000)
+    self.appendBaseContributionCategory(order_line_3, self.special_discount_3cd)
+    order_line_4 = order.newContent(portal_type=self.order_line_portal_type,
+                                    resource_value=self.music_album_2,
+                                    quantity=1,
+                                    price=3000)
+    self.appendBaseContributionCategory(order_line_4, self.special_discount_3cd)
+
+    self.stepTic()
+
+    # check the current amount
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITHOUT_VAT'), 12000)
+    self.assertEqual(self.getAmount(order, 'VAT_AMOUNT'), 600)
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITH_VAT'),
+                     12600)
+    # add one more cd, then total is 3. the special discount will be applied.
+    order_line_3.setQuantity(2)
+
+    self.stepTic()
+
+    # check again
+    self.assertEqual(self.getAmount(order, '3CD_AND_1HIGHEST_PRICED_DVD_15PERCENT_OFF'),
+                     -450)
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITHOUT_VAT'), 16550)
+    self.assertEqual(self.getAmount(order, 'VAT_AMOUNT'), 827.5)
+    self.assertEqual(self.getAmount(order, 'TOTAL_PRICE_WITH_VAT'), 17377.5)
 
 
 class TestTradeModelLineSale(TestTradeModelLine):
@@ -2916,8 +3312,22 @@ class TestTradeModelLinePurchase(TestTradeModelLine):
   trade_condition_portal_type = 'Purchase Trade Condition'
 
 
+class TestComplexTradeModelLineUseCaseSale(TestComplexTradeModelLineUseCase):
+  order_portal_type = 'Sale Order'
+  order_line_portal_type = 'Sale Order Line'
+  trade_condition_portal_type = 'Sale Trade Condition'
+
+
+class TestComplexTradeModelLineUseCasePurchase(TestComplexTradeModelLineUseCase):
+  order_portal_type = 'Purchase Order'
+  order_line_portal_type = 'Purchase Order Line'
+  trade_condition_portal_type = 'Purchase Trade Condition'
+
+
 def test_suite():
   suite = unittest.TestSuite()
   suite.addTest(unittest.makeSuite(TestTradeModelLineSale))
   suite.addTest(unittest.makeSuite(TestTradeModelLinePurchase))
+  suite.addTest(unittest.makeSuite(TestComplexTradeModelLineUseCaseSale))
+  suite.addTest(unittest.makeSuite(TestComplexTradeModelLineUseCasePurchase))
   return suite
-- 
2.30.9