[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