[Erp5-report] r39010 jerome - in /erp5/trunk/products/ERP5: Document/ tests/

nobody at svn.erp5.org nobody at svn.erp5.org
Fri Oct 8 18:09:49 CEST 2010


Author: jerome
Date: Fri Oct  8 18:09:48 2010
New Revision: 39010

URL: http://svn.erp5.org?rev=39010&view=rev
Log:
make it possible to budget for "unset" value. Due to technical limitations,
this is not working for section, node and movement axis.

Modified:
    erp5/trunk/products/ERP5/Document/NodeBudgetVariation.py
    erp5/trunk/products/ERP5/tests/testBudget.py

Modified: erp5/trunk/products/ERP5/Document/NodeBudgetVariation.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/Document/NodeBudgetVariation.py?rev=39010&r1=39009&r2=39010&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/Document/NodeBudgetVariation.py [utf8] (original)
+++ erp5/trunk/products/ERP5/Document/NodeBudgetVariation.py [utf8] Fri Oct  8 18:09:48 2010
@@ -34,28 +34,6 @@ from Products.ZSQLCatalog.SQLCatalog imp
 from Products.ERP5Type.Message import translateString
 
 
-class VirtualNode(object):
-  """A Virtual Node for all Other Nodes.
-
-  This virtual document can be used in budget variations.
-  """
-  __allow_access_to_unprotected_subobjects__ = 1
-  def __init__(self, relative_url):
-    """The Virtual Node will use the relative URL of the budget line for
-    memberships.
-    """
-    self.relative_url = relative_url
-
-  def getTitle(self):
-    return str(translateString('All Others'))
-
-  def getRelativeUrl(self):
-    return self.relative_url
-
-  def getUid(self):
-    return -1L
-
-
 class NodeBudgetVariation(BudgetVariation):
   """ A budget variation for node
 
@@ -94,11 +72,15 @@ class NodeBudgetVariation(BudgetVariatio
     node_select_method_id = self.getProperty('node_select_method_id')
     if node_select_method_id:
       return guarded_getattr(context, node_select_method_id)()
+
     # no script defined, used the explicitly selected values
+    node_list = self.getAggregateValueList()
+    portal_categories = self.getPortalObject().portal_categories
+    if self.getProperty('include_virtual_none_node'):
+      node_list.append(portal_categories.budget_special_node.none)
     if self.getProperty('include_virtual_other_node'):
-      return self.getAggregateValueList() + [
-                    VirtualNode(context.getRelativeUrl()), ]
-    return self.getAggregateValueList()
+      node_list.append(portal_categories.budget_special_node.all_other)
+    return node_list
 
   def _getNodeTitle(self, node):
     """Returns the title of a node
@@ -155,18 +137,31 @@ class NodeBudgetVariation(BudgetVariatio
         continue
       criterion_base_category, node_url = criterion_category.split('/', 1)
       if criterion_base_category == base_category:
-        if node_url == budget_line.getRelativeUrl():
+        if node_url == 'budget_special_node/none':
+          # This is the "Nothing" virtual node
+          query_dict.setdefault(axis, []).append(Query(**{axis: None}))
+        if node_url == 'budget_special_node/all_other':
           # This is the "All Other" virtual node
           other_uid_list = []
+          none_node_selected = False
           for node in self._getNodeList(budget_line):
             if '%s/%s' % (base_category, node.getRelativeUrl()) in\
                                     budget_line.getVariationCategoryList():
-              other_uid_list.append(node.getUid())
-          query_dict.setdefault(axis, []).append(
-                      ComplexQuery(
-                          NegatedQuery(Query(**{axis: other_uid_list})),
-                          Query(**{axis: None}),
-                          operator="OR"))
+              if node.getRelativeUrl() == 'budget_special_node/none':
+                none_node_selected = True
+              else:
+                other_uid_list.append(node.getUid())
+          if none_node_selected:
+            # in this case we don't want to include NULL in All others
+            query_dict.setdefault(axis, []).append(
+                            NegatedQuery(Query(**{axis: other_uid_list})))
+          else:
+            query_dict.setdefault(axis, []).append(
+                        ComplexQuery(
+                            NegatedQuery(Query(**{axis: other_uid_list})),
+                            Query(**{axis: None}),
+                            operator="OR"))
+
         query_dict.setdefault(axis, []).append(
                 portal_categories.getCategoryValue(node_url,
                   base_category=criterion_base_category).getUid())
@@ -204,29 +199,38 @@ class NodeBudgetVariation(BudgetVariatio
     # if we have a virtual "all others" node, we don't set a criterion here.
     if self.getProperty('include_virtual_other_node'):
       return query_dict
+    
     found = False
     for node_url in context.getVariationCategoryList(
                           base_category_list=(base_category,)):
-      query_dict.setdefault(axis, []).append(
+      if node_url != '%s/budget_special_node/none' % base_category:
+        query_dict.setdefault(axis, []).append(
                 portal_categories.getCategoryValue(node_url,
                       base_category=base_category).getUid())
       found = True
     if found:
+      if self.getProperty('include_virtual_none_node'):
+        query_dict[axis] = ComplexQuery(
+              Query(**{axis: None}),
+              Query(**{axis: query_dict[axis]}),
+              operator="OR")
       return query_dict
     return dict()
   
   def _getCellKeyFromInventoryListBrain(self, brain, budget_line,
                                          cell_key_cache=None):
-    """Compute key from inventory brain, with support for "all others" virtual
-    node.
+    """Compute key from inventory brain, with support for virtual nodes.
     """
+    cell_key_cache[None] = '%s/budget_special_node/none'\
+                                 % self.getProperty('variation_base_category')
+    
     key = BudgetVariation._getCellKeyFromInventoryListBrain(
                    self, brain, budget_line, cell_key_cache=cell_key_cache)
     if self.getProperty('include_virtual_other_node'):
       if key not in [x[1] for x in
           self.getBudgetVariationRangeCategoryList(budget_line)]:
-        key = '%s/%s' % ( self.getProperty('variation_base_category'),
-                          budget_line.getRelativeUrl() )
+        key = '%s/budget_special_node/all_other' % (
+            self.getProperty('variation_base_category'),)
     return key
 
   def getBudgetLineVariationRangeCategoryList(self, budget_line):

Modified: erp5/trunk/products/ERP5/tests/testBudget.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/tests/testBudget.py?rev=39010&r1=39009&r2=39010&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/tests/testBudget.py [utf8] (original)
+++ erp5/trunk/products/ERP5/tests/testBudget.py [utf8] Fri Oct  8 18:09:48 2010
@@ -31,6 +31,7 @@ import transaction
 from DateTime import DateTime
 
 from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
+from Products.ZSQLCatalog.SQLCatalog import ComplexQuery
 from AccessControl import getSecurityManager
 
 class TestBudget(ERP5TypeTestCase):
@@ -470,7 +471,7 @@ class TestBudget(ERP5TypeTestCase):
     budget_line.edit(
         variation_category_list=(
           'source/account_module/goods_purchase',
-          'source/%s' % budget_line.getRelativeUrl(), # this is 'all others'
+          'source/budget_special_node/all_other', # this is 'all others'
           'account_type/expense',
           'account_type/asset', ))
 
@@ -489,7 +490,7 @@ class TestBudget(ERP5TypeTestCase):
              field_matrixbox_membership_criterion_category_list_cell_0_0_0=[],
              field_matrixbox_quantity_cell_1_0_0="2",
              field_matrixbox_membership_criterion_category_list_cell_1_0_0=[
-               'source/%s' % budget_line.getRelativeUrl(),
+               'source/budget_special_node/all_other',
                'account_type/asset'],
              field_matrixbox_quantity_cell_0_1_0="1",
              field_matrixbox_membership_criterion_category_list_cell_0_1_0=[
@@ -534,20 +535,237 @@ class TestBudget(ERP5TypeTestCase):
     self.tic()
 
     self.assertEquals(
-      {('source/%s' % budget_line.getRelativeUrl(), 'account_type/asset'): -100.0,
+      {('source/budget_special_node/all_other', 'account_type/asset'): -100.0,
        ('source/account_module/goods_purchase', 'account_type/expense'): 100.0},
         budget_line.getConsumedBudgetDict())
 
     self.assertEquals(
-      {('source/%s' % budget_line.getRelativeUrl(), 'account_type/asset'): -100.0,
+      {('source/budget_special_node/all_other', 'account_type/asset'): -100.0,
        ('source/account_module/goods_purchase', 'account_type/expense'): 100.0},
         budget_line.getEngagedBudgetDict())
 
     self.assertEquals(
-      {('source/%s' % budget_line.getRelativeUrl(), 'account_type/asset'): 102.0,
+      {('source/budget_special_node/all_other', 'account_type/asset'): 102.0,
        ('source/account_module/goods_purchase', 'account_type/expense'): -99.0},
         budget_line.getAvailableBudgetDict())
-      
+   
+  def test_none_virtual_node(self):
+    # tests consumptions, by using "none" virtual node on a node budget
+    # variation
+    budget_model = self.portal.budget_model_module.newContent(
+                            portal_type='Budget Model')
+    budget_model.newContent(
+                    portal_type='Node Budget Variation',
+                    int_index=1,
+                    budget_variation='budget_cell',
+                    # this does not work for movement, node and section
+                    # categories ...
+                    inventory_axis='project',
+                    variation_base_category='source_project',
+                    aggregate_value_list=(
+                      self.portal.organisation_module.my_organisation,),
+                    include_virtual_none_node=True)
+    budget_model.newContent(
+                    portal_type='Category Budget Variation',
+                    int_index=2,
+                    budget_variation='budget_line',
+                    inventory_axis='node_category_strict_membership',
+                    variation_base_category='account_type',)
+
+    budget = self.portal.budget_module.newContent(
+                    portal_type='Budget',
+                    start_date_range_min=DateTime(2000, 1, 1),
+                    start_date_range_max=DateTime(2000, 12, 31),
+                    specialise_value=budget_model)
+
+    budget_line = budget.newContent(portal_type='Budget Line')
+
+    budget_line.edit(
+        variation_category_list=(
+          'source_project/organisation_module/my_organisation',
+          'source_project/budget_special_node/none', # this is 'none'
+          'account_type/expense',))
+
+    form = budget_line.BudgetLine_view
+    self.portal.REQUEST.other.update(
+        dict(AUTHENTICATED_USER=getSecurityManager().getUser(),
+
+             field_membership_criterion_base_category_list=
+        form.membership_criterion_base_category_list.get_value('default'),
+             field_mapped_value_property_list=
+        form.mapped_value_property_list.get_value('default'),
+
+             field_matrixbox_quantity_cell_0_0_0="100",
+             field_matrixbox_membership_criterion_category_list_cell_0_0_0=[
+               'source_project/organisation_module/my_organisation',],
+             field_matrixbox_quantity_cell_1_0_0="200",
+             field_matrixbox_membership_criterion_category_list_cell_1_0_0=[
+               'source_project/budget_special_node/none',],
+        ))
+    budget_line.Base_edit(form_id=form.getId())
+
+    self.assertEquals(2, len(budget_line.contentValues()))
+
+    class ReferenceQuery:
+      """Helper class to compare queries
+      """
+      def __eq__(me, query):
+        self.assertTrue(isinstance(query, ComplexQuery))
+        self.assertEquals(query.logical_operator, 'or')
+        self.assertEquals(2, len(query.query_list))
+        self.assertEquals(query.query_list[0].kw, {'project_uid': None})
+        self.assertEquals(query.query_list[1].kw,
+          {'project_uid':
+            [self.portal.organisation_module.my_organisation.getUid()]})
+        return True
+
+    self.assertEquals(
+        dict(from_date=DateTime(2000, 1, 1),
+             at_date=DateTime(2000, 12, 31).latestTime(),
+             node_category_strict_membership=['account_type/expense',],
+             project_uid=ReferenceQuery(),
+             group_by_node_category_strict_membership=True,
+             group_by_project=True,
+             ),
+        budget_model.getInventoryListQueryDict(budget_line))
+
+    atransaction = self.portal.accounting_module.newContent(
+                  portal_type='Accounting Transaction',
+                  source_section_value=self.portal.organisation_module.my_organisation,
+                  resource_value=self.portal.currency_module.euro,
+                  start_date=DateTime(2000, 1, 2))
+    atransaction.newContent(
+                  portal_type='Accounting Transaction Line',
+                  source_value=self.portal.account_module.goods_purchase,
+                  source_project_value=self.portal.organisation_module.my_organisation,
+                  source_debit=200)
+    atransaction.newContent(
+                  portal_type='Accounting Transaction Line',
+                  source_value=self.portal.account_module.goods_purchase,
+                  source_credit=300)
+    atransaction.stop()
+    
+    transaction.commit()
+    self.tic()
+
+    self.assertEquals(
+      {('source_project/organisation_module/my_organisation',): 200.0,
+       ('source_project/budget_special_node/none',): -300.0
+       }, budget_line.getConsumedBudgetDict())
+
+    self.assertEquals(
+      {('source_project/organisation_module/my_organisation',): 200.0,
+       ('source_project/budget_special_node/none',): -300.0
+       }, budget_line.getEngagedBudgetDict())
+
+
+  def test_none_and_all_others_virtual_nodes_together(self):
+    # tests consumptions, by using "none" and "all other" virtual nodes
+    # together on a node budget variation
+    budget_model = self.portal.budget_model_module.newContent(
+                            portal_type='Budget Model')
+    budget_model.newContent(
+                    portal_type='Node Budget Variation',
+                    int_index=1,
+                    budget_variation='budget_cell',
+                    inventory_axis='project',
+                    variation_base_category='source_project',
+                    aggregate_value_list=(
+                      self.portal.organisation_module.my_organisation,),
+                    include_virtual_other_node=True,
+                    include_virtual_none_node=True)
+    budget_model.newContent(
+                    portal_type='Category Budget Variation',
+                    int_index=2,
+                    budget_variation='budget_line',
+                    inventory_axis='node_category_strict_membership',
+                    variation_base_category='account_type',)
+
+    budget = self.portal.budget_module.newContent(
+                    portal_type='Budget',
+                    start_date_range_min=DateTime(2000, 1, 1),
+                    start_date_range_max=DateTime(2000, 12, 31),
+                    specialise_value=budget_model)
+
+    budget_line = budget.newContent(portal_type='Budget Line')
+
+    budget_line.edit(
+        variation_category_list=(
+          'source_project/organisation_module/my_organisation',
+          'source_project/budget_special_node/none', # this is 'none'
+          'source_project/budget_special_node/all_other', # this is 'all_other'
+          'account_type/expense',))
+
+    form = budget_line.BudgetLine_view
+    self.portal.REQUEST.other.update(
+        dict(AUTHENTICATED_USER=getSecurityManager().getUser(),
+
+             field_membership_criterion_base_category_list=
+        form.membership_criterion_base_category_list.get_value('default'),
+             field_mapped_value_property_list=
+        form.mapped_value_property_list.get_value('default'),
+
+             field_matrixbox_quantity_cell_0_0_0="100",
+             field_matrixbox_membership_criterion_category_list_cell_0_0_0=[
+               'source_project/organisation_module/my_organisation',],
+             field_matrixbox_quantity_cell_1_0_0="200",
+             field_matrixbox_membership_criterion_category_list_cell_1_0_0=[
+               'source_project/budget_special_node/none',],
+             field_matrixbox_quantity_cell_2_0_0="300",
+             field_matrixbox_membership_criterion_category_list_cell_2_0_0=[
+               'source_project/budget_special_node/all_other',],
+        ))
+    budget_line.Base_edit(form_id=form.getId())
+
+    self.assertEquals(3, len(budget_line.contentValues()))
+
+    self.assertEquals(
+        dict(from_date=DateTime(2000, 1, 1),
+             at_date=DateTime(2000, 12, 31).latestTime(),
+             node_category_strict_membership=['account_type/expense',],
+             group_by_node_category_strict_membership=True,
+             group_by_project=True,
+             ),
+        budget_model.getInventoryListQueryDict(budget_line))
+
+    atransaction = self.portal.accounting_module.newContent(
+                  portal_type='Accounting Transaction',
+                  source_section_value=self.portal.organisation_module.my_organisation,
+                  resource_value=self.portal.currency_module.euro,
+                  start_date=DateTime(2000, 1, 2))
+    atransaction.newContent(
+                  portal_type='Accounting Transaction Line',
+                  source_value=self.portal.account_module.goods_purchase,
+                  source_project_value=self.portal.organisation_module.my_organisation,
+                  source_debit=200)
+    atransaction.newContent(
+                  portal_type='Accounting Transaction Line',
+                  source_value=self.portal.account_module.goods_purchase,
+                  # this will count for all other
+                  source_project_value=self.portal.organisation_module.client_1,
+                  source_credit=80)
+    atransaction.newContent(
+                  portal_type='Accounting Transaction Line',
+                  source_value=self.portal.account_module.goods_purchase,
+                  # this will count for none
+                  source_credit=120)
+    atransaction.stop()
+    
+    transaction.commit()
+    self.tic()
+
+    self.assertEquals(
+      {('source_project/organisation_module/my_organisation',): 200.0,
+       ('source_project/budget_special_node/all_other',): -80.0,
+       ('source_project/budget_special_node/none',): -120.0
+       }, budget_line.getConsumedBudgetDict())
+
+    self.assertEquals(
+      {('source_project/organisation_module/my_organisation',): 200.0,
+       ('source_project/budget_special_node/all_other',): -80.0,
+       ('source_project/budget_special_node/none',): -120.0
+       }, budget_line.getEngagedBudgetDict())
+
 
   def test_consumption_movement_category(self):
     # test for budget consumption using movement category




More information about the Erp5-report mailing list