From a0a8115b6b09c0e45355ed7e126fa8e6fc14124e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Fri, 23 Jul 2010 15:34:16 +0000
Subject: [PATCH] When we group by an axis or an axis category in
 getInventoryList, expose the uid of this axis on the brain. This means that
 if we do group_by_section_category=True, the brains will have an attribute
 section_category_uid that will be the uid of the section category for this
 brain. Also add support for select_dict and select_list to have the same
 feature when grouping on a related key. Fix typo from r37215,
 group_by_project_category was not working

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@37257 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5/Tool/SimulationTool.py           | 54 ++++++++++++++++---
 .../erp5_core/Resource_zGetInventoryList.xml  | 12 ++++-
 product/ERP5/bootstrap/erp5_core/bt/revision  |  2 +-
 product/ERP5/tests/testInventoryAPI.py        | 54 +++++++++++++++++++
 4 files changed, 113 insertions(+), 9 deletions(-)

diff --git a/product/ERP5/Tool/SimulationTool.py b/product/ERP5/Tool/SimulationTool.py
index f086dcc982..1b3c15a55b 100644
--- a/product/ERP5/Tool/SimulationTool.py
+++ b/product/ERP5/Tool/SimulationTool.py
@@ -423,13 +423,12 @@ class SimulationTool(BaseTool):
         ctool = getToolByName(self, 'portal_catalog')
         sql_kw = sql_kw.copy()
         new_kw = new_kw.copy()
-        # Some columns cannot be found automatically, prepend table name to
-        # avoid ambiguities.
 
         # Group-by expression  (eg. group_by=['node_uid'])
         group_by = new_kw.pop('group_by', [])
 
         # group by from stock table (eg. group_by_node=True)
+        # prepend table name to avoid ambiguities.
         column_group_by = new_kw.pop('column_group_by', [])
         if column_group_by:
           group_by.extend(['%s.%s' % (table, x) for x in column_group_by])
@@ -450,6 +449,14 @@ class SimulationTool(BaseTool):
         if group_by:
           new_kw['group_by'] = group_by
 
+        # select expression
+        select_dict = new_kw.get('select_dict', dict())
+        related_key_select_expression_list = new_kw.pop(
+                'related_key_select_expression_list', [])
+        if related_key_select_expression_list:
+          select_dict[x] = '%s_%s' % (table, x)
+        new_kw['select_dict'] = select_dict
+
         # Column values
         column_value_dict = new_kw.pop('column_value_dict', {})
         for key, value in column_value_dict.iteritems():
@@ -745,6 +752,18 @@ class SimulationTool(BaseTool):
                       ['catalog.uid=%s' % uid for uid in uid_list])
 
       # build the group by expression
+      # if we group by a criterion, we also add this criterion to the select
+      # expression, unless it is already selected in Resource_zGetInventoryList
+      # the caller can also pass select_dict or select_list. select_expression,
+      # which is deprecated in ZSQLCatalog is not supported here.
+      select_dict = kw.get('select_dict', {})
+      # we support select_list, if passed
+      select_list = kw.get('select_list', [])
+      for select_key in kw.get('select_list', []):
+        select_dict[select_key] = None
+      new_kw['select_dict'] = select_dict
+      related_key_select_expression_list = []
+
       column_group_by_expression_list = []
       related_key_group_by_expression_list = []
       if group_by_node:
@@ -777,42 +796,65 @@ class SimulationTool(BaseTool):
 
       if group_by_section_category:
         related_key_group_by_expression_list.append('section_category_uid')
+        related_key_select_expression_list.append('stock_section_category_uid')
       if group_by_section_category_strict_membership:
         related_key_group_by_expression_list.append(
             'section_category_strict_membership_uid')
+        related_key_select_expression_list.append(
+            'section_category_strict_membership_uid')
       if group_by_mirror_section_category:
         related_key_group_by_expression_list.append('mirror_section_category_uid')
+        related_key_select_expression_list.append('mirror_section_category_uid')
       if group_by_mirror_section_category_strict_membership:
         related_key_group_by_expression_list.append(
             'mirror_section_category_strict_membership_uid')
+        related_key_select_expression_list.append(
+            'mirror_section_category_strict_membership_uid')
       if group_by_node_category:
         related_key_group_by_expression_list.append('node_category_uid')
+        related_key_select_expression_list.append('node_category_uid')
       if group_by_node_category_strict_membership:
         related_key_group_by_expression_list.append(
             'node_category_strict_membership_uid')
+        related_key_select_expression_list.append(
+            'node_category_strict_membership_uid')
       if group_by_mirror_node_category:
         related_key_group_by_expression_list.append('mirror_node_category_uid')
       if group_by_mirror_node_category_strict_membership:
         related_key_group_by_expression_list.append(
             'mirror_node_category_strict_membership_uid')
+        related_key_select_expression_list.append(
+            'mirror_node_category_strict_membership_uid')
       if group_by_payment_category:
         related_key_group_by_expression_list.append('payment_category_uid')
+        related_key_select_expression_list.append('payment_category_uid')
       if group_by_payment_category_strict_membership:
         related_key_group_by_expression_list.append(
             'payment_category_strict_membership_uid')
+        related_key_select_expression_list.append(
+            'payment_category_strict_membership_uid')
       if group_by_function_category:
         related_key_group_by_expression_list.append('function_category_uid')
+        related_key_select_expression_list.append('function_category_uid')
       if group_by_function_category_strict_membership:
         related_key_group_by_expression_list.append(
             'function_category_strict_membership_uid')
-      if group_by_function_category:
-        related_key_group_by_expression_list.append('function_category_uid')
-      if group_by_function_category_strict_membership:
-        related_key_group_by_expression_list.append(
+        related_key_select_expression_list.append(
             'function_category_strict_membership_uid')
+      if group_by_project_category:
+        related_key_group_by_expression_list.append('project_category_uid')
+        related_key_select_expression_list.append('project_category_uid')
+      if group_by_project_category_strict_membership:
+        related_key_group_by_expression_list.append(
+            'project_category_strict_membership_uid')
+        related_key_select_expression_list.append(
+            'project_category_strict_membership_uid')
 
       if related_key_group_by_expression_list:
         new_kw['related_key_group_by'] = related_key_group_by_expression_list
+      if related_key_select_expression_list:
+        new_kw['related_key_select_expression_list'] =\
+                related_key_select_expression_list
 
       return sql_kw, new_kw
 
diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetInventoryList.xml b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetInventoryList.xml
index a9d5b943ce..68bc29cc6c 100644
--- a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetInventoryList.xml
+++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetInventoryList.xml
@@ -130,6 +130,12 @@
                                   <dictionary/>
                                 </value>
                             </item>
+                            <item>
+                                <key> <string>select_expression</string> </key>
+                                <value>
+                                  <dictionary/>
+                                </value>
+                            </item>
                             <item>
                                 <key> <string>selection_domain</string> </key>
                                 <value>
@@ -195,6 +201,7 @@
                             <string>order_by_expression</string>
                             <string>group_by_expression</string>
                             <string>selection_domain</string>
+                            <string>select_expression</string>
                             <string>selection_report</string>
                             <string>ignore_variation</string>
                             <string>standardize</string>
@@ -233,6 +240,7 @@ where_expression\r\n
 order_by_expression\r\n
 group_by_expression\r\n
 selection_domain\r\n
+select_expression\r\n
 selection_report\r\n
 ignore_variation\r\n
 standardize\r\n
@@ -354,7 +362,7 @@ SELECT\n
   COUNT(DISTINCT <dtml-var stock_table_id>.uid) AS stock_uid,\n
   MAX(<dtml-var stock_table_id>.date) AS date\n
 </dtml-if>\n
-\n
+<dtml-if select_expression>, <dtml-var select_expression></dtml-if>\n
 \n
 FROM\n
   catalog, <dtml-var stock_table_id>\n
@@ -526,7 +534,7 @@ SELECT\n
   COUNT(DISTINCT <dtml-var stock_table_id>.uid) AS stock_uid,\n
   MAX(<dtml-var stock_table_id>.date) AS date\n
 </dtml-if>\n
-\n
+<dtml-if select_expression>, <dtml-var select_expression></dtml-if>\n
 \n
 FROM\n
   catalog, <dtml-var stock_table_id>\n
diff --git a/product/ERP5/bootstrap/erp5_core/bt/revision b/product/ERP5/bootstrap/erp5_core/bt/revision
index 65c6790520..e61847c7fa 100644
--- a/product/ERP5/bootstrap/erp5_core/bt/revision
+++ b/product/ERP5/bootstrap/erp5_core/bt/revision
@@ -1 +1 @@
-1636
\ No newline at end of file
+1637
\ No newline at end of file
diff --git a/product/ERP5/tests/testInventoryAPI.py b/product/ERP5/tests/testInventoryAPI.py
index ba88dc8578..12cdb0b5f6 100644
--- a/product/ERP5/tests/testInventoryAPI.py
+++ b/product/ERP5/tests/testInventoryAPI.py
@@ -798,6 +798,28 @@ class TestInventoryList(InventoryAPITestCase):
                                       group_by_section_category=1)
     self.assertEquals(1, len(inventory_list))
     self.assertEquals(3+2, inventory_list[0].inventory)
+    # section category is exported in the brain
+    self.assertTrue(hasattr(inventory_list[0], 'section_category_uid'))
+    self.assertEquals(self.portal.portal_categories.group.level1.getUid(),
+                      inventory_list[0].section_category_uid)
+
+  def test_GroupBySectionCategoryStrict(self):
+    getInventoryList = self.getSimulationTool().getInventoryList
+    self.section.setGroup('level1')
+    self.other_section.setGroup('level1')
+    m1 = self._makeMovement(quantity=2)
+    m2 = self._makeMovement(destination_section_value=self.other_section, quantity=3)
+
+    inventory_list = getInventoryList(node_uid=self.node.getUid(),
+                                      section_category='group/level1',
+                                      group_by_section_category_strict_membership=1)
+    self.assertEquals(1, len(inventory_list))
+    self.assertEquals(3+2, inventory_list[0].inventory)
+    # section category is exported in the brain
+    self.assertTrue(hasattr(inventory_list[0],
+      'section_category_strict_membership_uid'))
+    self.assertEquals(self.portal.portal_categories.group.level1.getUid(),
+                      inventory_list[0].section_category_strict_membership_uid)
 
   def test_GroupByFunction(self):
     getInventoryList = self.getSimulationTool().getInventoryList
@@ -896,6 +918,21 @@ class TestInventoryList(InventoryAPITestCase):
             if r.getObject().getUse() == 'use1'][0].inventory, 5)
     self.assertEquals([r for r in inventory_list
         if r.getObject().getUse() == 'use2'][0].inventory, 11)
+    
+    # in such case, it's interesting to pass a select expression, to be have on
+    # brain the information of which category is used
+    inventory_list = getInventoryList(node_uid=(self.node.getUid(),
+                                                self.other_node.getUid()),
+                                      group_by=('strict_use_uid', ),
+                                      select_list=['strict_use_uid'])
+    self.assertEquals(2, len(inventory_list))
+    self.assertTrue(hasattr(inventory_list[0], 'strict_use_uid'))
+    use = self.portal.portal_categories.use
+    self.assertEquals([r for r in inventory_list
+      if r.strict_use_uid == use.use1.getUid()][0].inventory, 5)
+    self.assertEquals([r for r in inventory_list
+      if r.strict_use_uid == use.use2.getUid()][0].inventory, 11)
+
     # group_by can also be passed as string
     inventory_list = getInventoryList(node_uid=(self.node.getUid(),
                                                 self.other_node.getUid()),
@@ -906,6 +943,23 @@ class TestInventoryList(InventoryAPITestCase):
     self.assertEquals([r for r in inventory_list
         if r.getObject().getUse() == 'use2'][0].inventory, 11)
 
+    # if we group by "use_uid" instead of "strict_use_uid", then we'll have
+    # summary lines.
+    inventory_list = getInventoryList(node_uid=(self.node.getUid(),
+                                                self.other_node.getUid()),
+                                      group_by=('use_uid', ),
+                                      select_list=['use_uid'])
+    self.assertEquals(3, len(inventory_list))
+    self.assertTrue(hasattr(inventory_list[0], 'use_uid'))
+    use = self.portal.portal_categories.use
+    self.assertEquals([r for r in inventory_list
+      if r.use_uid == use.use1.getUid()][0].inventory, 5)
+    self.assertEquals([r for r in inventory_list
+      if r.use_uid == use.use2.getUid()][0].inventory, 11)
+    # the summary line
+    self.assertEquals([r for r in inventory_list
+      if r.use_uid == use.getUid()][0].inventory, 11+5)
+
     # the name of a column can also be used, from stock or other tables
     inventory_list = getInventoryList(node_uid=(self.node.getUid(),
                                                 self.other_node.getUid()),
-- 
2.30.9