From a16eabfbd6ec30bad74a43895e6d5c8f61cf186f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Nowak?= <luke@nexedi.com>
Date: Tue, 9 Jun 2009 12:50:56 +0000
Subject: [PATCH]  - implement sorting which allows to do easy traversal  -
 test sorting order

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@27470 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5/Document/TradeCondition.py | 33 ++++++++++++---
 product/ERP5/tests/testBPMCore.py       | 56 ++++++++++++++++++++++++-
 2 files changed, 83 insertions(+), 6 deletions(-)

diff --git a/product/ERP5/Document/TradeCondition.py b/product/ERP5/Document/TradeCondition.py
index b7835ecd0f..d596bb1137 100644
--- a/product/ERP5/Document/TradeCondition.py
+++ b/product/ERP5/Document/TradeCondition.py
@@ -175,12 +175,35 @@ class TradeCondition(Path, Transformation):
             portal_type=portal_type_list):
           reference = trade_model_line.getReference()
           if reference not in reference_list or reference is None:
-            trade_model_line_composed_list.append(trade_model_line)
             reference_list.append(reference)
-
-      return sorted(trade_model_line_composed_list,
-          cmp=lambda x,y: set(x.getBaseContributionList()).
-          intersection(set(y.getBaseApplicationList())) and -1 or 1)
+            base_contribution_list = trade_model_line \
+              .getBaseContributionList()
+            if len(base_contribution_list) == 0:
+              # when movement will not generate anything which contributes
+              # it is safe to be last on list
+              trade_model_line_composed_list.append(trade_model_line)
+            else:
+              # if movements contributes to anything it have to be placed
+              # just before to what it contributes
+              index = 0
+              inserted = False
+              for old_trade_model_line in trade_model_line_composed_list:
+                for base_application in old_trade_model_line \
+                  .getBaseApplicationList():
+                  if base_application in base_contribution_list:
+                    trade_model_line_composed_list.insert(index,
+                        trade_model_line)
+                    inserted = True
+                    break
+                if inserted:
+                  break
+                index += 1
+              if not inserted:
+                # last resort - nothing was found, it is safe to put movement
+                # in beginning of list
+                trade_model_line_composed_list.insert(0, trade_model_line)
+
+      return trade_model_line_composed_list
 
     security.declareProtected(Permissions.AccessContentsInformation,
                               'getAggregatedAmountList')
diff --git a/product/ERP5/tests/testBPMCore.py b/product/ERP5/tests/testBPMCore.py
index e6e05ddb6a..faf4cd5b58 100644
--- a/product/ERP5/tests/testBPMCore.py
+++ b/product/ERP5/tests/testBPMCore.py
@@ -90,7 +90,7 @@ class TestBPMMixin(ERP5TypeTestCase):
   def createCategories(self):
     category_tool = getToolByName(self.portal, 'portal_categories')
     self.createCategoriesInCategory(category_tool.base_amount, ['discount',
-      'tax', 'total_tax', 'total_discount'])
+      'tax', 'total_tax', 'total_discount', 'total'])
     self.createCategoriesInCategory(category_tool.use,
         self.normal_resource_use_category_list + \
             self.invoicing_resource_use_category_list)
@@ -1697,6 +1697,60 @@ class TestBPMTestCases(TestBPMMixin):
       trade_condition_1.getTradeModelLineComposedList()
     )
 
+  def test_getTradeModelLineComposedList(self):
+    """Test that list of contribution/application relations is sorted to do easy traversal
+
+    Let assume such graph of contribution/application dependency:
+
+    D -----> B
+          /   \
+    E ---/     > A
+              /
+    F -----> C
+          /
+    G ---/
+
+    It shall return list which is sorted like:
+      * (DE) B (FG) C A
+        or
+      * (FG) C (DE) B A
+    where everything in parenthesis can be not sorted
+    """
+    trade_condition = self.createTradeCondition()
+
+    A = self.createTradeModelLine(trade_condition, reference='A',
+        base_application_list=['base_amount/total'])
+
+    B = self.createTradeModelLine(trade_condition, reference='B',
+        base_contribution_list=['base_amount/total'],
+        base_application_list=['base_amount/total_tax'])
+
+    C = self.createTradeModelLine(trade_condition, reference='C',
+        base_contribution_list=['base_amount/total'],
+        base_application_list=['base_amount/total_discount'])
+
+    D = self.createTradeModelLine(trade_condition, reference='D',
+        base_contribution_list=['base_amount/total_tax'],
+        base_application_list=['base_amount/tax'])
+
+    E = self.createTradeModelLine(trade_condition, reference='E',
+        base_contribution_list=['base_amount/total_tax'],
+        base_application_list=['base_amount/tax'])
+
+    F = self.createTradeModelLine(trade_condition, reference='F',
+        base_contribution_list=['base_amount/total_discount'],
+        base_application_list=['base_amount/discount'])
+
+    G = self.createTradeModelLine(trade_condition, reference='G',
+        base_contribution_list=['base_amount/total_discount'],
+        base_application_list=['base_amount/discount'])
+
+    trade_model_line_list = trade_condition.getTradeModelLineComposedList()
+
+    # XXX: This is only one good possible sorting
+    self.assertEquals(sorted([q.getRelativeUrl() for q in trade_model_line_list]),
+        sorted([q.getRelativeUrl() for q in [D, E, B, F, G, C, A]]))
+
   def test_getAggregatedAmountList(self):
     """
       Test for case, when discount contributes to tax, and order has mix of contributing lines
-- 
2.30.9