Commit cd62e742 authored by Jérome Perrin's avatar Jérome Perrin

- Initial implementation of a new way of calulating budget consumptions:

  instead of doing one getInventory by budget cell, we do one getInventoryList
  by budget line.
- Budget variation now uses the variation defined at the proper level, ie if
  this is a line level variation, it uses membership criterion from the line,
  if this is budget level, from the budget. if this is a cell level, from the
  cell. This means that we no longer have to copy all level categories on the
  cell. The UI will have to be updated.
- Test those new features and add some missing tests



git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@37220 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 6eee2db8
...@@ -36,31 +36,75 @@ from Products.ERP5.Variated import Variated ...@@ -36,31 +36,75 @@ from Products.ERP5.Variated import Variated
class BudgetLine(Predicate, XMLMatrix, Variated): class BudgetLine(Predicate, XMLMatrix, Variated):
""" A Line of budget, variated in budget cells.
"""
# Default Properties
property_sheets = ( PropertySheet.Base
, PropertySheet.XMLObject
, PropertySheet.SimpleItem
, PropertySheet.CategoryCore
, PropertySheet.Folder
, PropertySheet.Predicate
, PropertySheet.SortIndex
, PropertySheet.Task
, PropertySheet.Arrow
, PropertySheet.Budget
, PropertySheet.Amount
, PropertySheet.VariationRange
)
# CMF Type Definition
meta_type='ERP5 Budget Line'
portal_type='Budget Line'
add_permission = Permissions.AddPortalContent
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
security.declareProtected(Permissions.AccessContentsInformation,
'getConsumedBudgetDict')
def getConsumedBudgetDict(self, **kw):
"""Returns all the consumptions in a dict where the keys are the cells, and
the value is the consumed budget.
""" """
BudgetLine a line of budget... return self._getBudgetDict(**kw)
security.declareProtected(Permissions.AccessContentsInformation,
'getEngagedBudgetDict')
def getEngagedBudgetDict(self, **kw):
"""Returns all the engagements in a dict where the keys are the cells, and
the value is the engaged budget.
"""
kw.setdefault('explanation_simulation_state',
self.getPortalReservedInventoryStateList() +
self.getPortalCurrentInventoryStateList() +
self.getPortalTransitInventoryStateList())
return self._getBudgetDict(**kw)
def _getBudgetDict(self, **kw):
"""Use getCurrentInventoryList to compute all budget cell consumptions at
once, and returns them in a dict.
""" """
budget = self.getParentValue()
budget_model = budget.getSpecialiseValue(portal_type='Budget Model')
if budget_model is None:
return dict()
query_dict = budget_model.getInventoryListQueryDict(self)
query_dict.update(kw)
query_dict.setdefault('ignore_group_by', True)
# Default Properties sign = self.BudgetLine_getConsumptionSign()
property_sheets = ( PropertySheet.Base budget_dict = dict()
, PropertySheet.XMLObject for brain in self.getPortalObject().portal_simulation\
, PropertySheet.SimpleItem .getCurrentInventoryList(**query_dict):
, PropertySheet.CategoryCore # XXX total_quantity or total_price ??
, PropertySheet.Folder previous_value = budget_dict.get(
, PropertySheet.Predicate budget_model._getCellKeyFromInventoryListBrain(brain, self), 0)
, PropertySheet.SortIndex budget_dict[budget_model._getCellKeyFromInventoryListBrain(brain, self)] = \
, PropertySheet.Task previous_value + brain.total_price * sign
, PropertySheet.Arrow
, PropertySheet.Budget
, PropertySheet.Amount
, PropertySheet.VariationRange
, PropertySheet.Assignment
)
# CMF Type Definition return budget_dict
meta_type='ERP5 Budget Line'
portal_type='Budget Line'
add_permission = Permissions.AddPortalContent
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
...@@ -75,7 +75,7 @@ class BudgetModel(Predicate): ...@@ -75,7 +75,7 @@ class BudgetModel(Predicate):
return cell_range return cell_range
def getInventoryQueryDict(self, budget_cell): def getInventoryQueryDict(self, budget_cell):
"""Returns the query dict to pass to simulation query """Returns the query dict to pass to simulation query for a budget cell
""" """
query_dict = dict() query_dict = dict()
for budget_variation in sorted(self.contentValues( for budget_variation in sorted(self.contentValues(
...@@ -83,14 +83,57 @@ class BudgetModel(Predicate): ...@@ -83,14 +83,57 @@ class BudgetModel(Predicate):
key=lambda x:x.getIntIndex()): key=lambda x:x.getIntIndex()):
query_dict.update( query_dict.update(
budget_variation.getInventoryQueryDict(budget_cell)) budget_variation.getInventoryQueryDict(budget_cell))
# include dates from the budget
budget = budget_cell.getParentValue().getParentValue()
query_dict.setdefault('from_date', budget.getStartDateRangeMin())
start_date_range_max = budget.getStartDateRangeMax()
if start_date_range_max:
query_dict.setdefault('at_date', start_date_range_max.latestTime())
return query_dict return query_dict
def getInventoryListQueryDict(self, budget_line):
"""Returns the query dict to pass to simulation query for a budget line
"""
query_dict = dict()
for budget_variation in sorted(self.contentValues(
portal_type=self.getPortalBudgetVariationTypeList()),
key=lambda x:x.getIntIndex()):
variation_query_dict = budget_variation.getInventoryListQueryDict(budget_line)
# Merge group_by argument. All other arguments should not conflict
if 'group_by' in query_dict and 'group_by' in variation_query_dict:
variation_query_dict['group_by'].extend(query_dict['group_by'])
query_dict.update(variation_query_dict)
# include dates from the budget
budget = budget_line.getParentValue()
query_dict.setdefault('from_date', budget.getStartDateRangeMin())
start_date_range_max = budget.getStartDateRangeMax()
if start_date_range_max:
query_dict.setdefault('at_date', start_date_range_max.latestTime())
return query_dict
def _getCellKeyFromInventoryListBrain(self, brain, budget_line):
"""Compute the cell key from an inventory brain, the cell key can be used
to retrieve the budget cell in the corresponding budget line.
"""
cell_key = ()
for budget_variation in sorted(self.contentValues(
portal_type=self.getPortalBudgetVariationTypeList()),
key=lambda x:x.getIntIndex()):
key = budget_variation._getCellKeyFromInventoryListBrain(brain,
budget_line)
if key:
cell_key += (key,)
return cell_key
def asBudgetPredicate(self): def asBudgetPredicate(self):
" " " "
# XXX predicate for line / cell ? # XXX predicate for line / cell ?
def getBudgetConsumptionMethod(self, budget_cell): def getBudgetConsumptionMethod(self, budget_cell):
# XXX this API might disapear
# XXX return the method, or compute directly ? # XXX return the method, or compute directly ?
budget_consumption_method = None budget_consumption_method = None
for budget_variation in sorted(self.contentValues( for budget_variation in sorted(self.contentValues(
......
...@@ -91,3 +91,74 @@ class BudgetVariation(Predicate): ...@@ -91,3 +91,74 @@ class BudgetVariation(Predicate):
""" """
return {} return {}
def getInventoryListQueryDict(self, budget_line):
"""Returns the query dict to pass to simulation query for a budget line
"""
return {}
def _getCellKeyFromInventoryListBrain(self, brain, budget_line):
"""Compute the cell key from an inventory brain.
The cell key can be used to retrieve the budget cell in the corresponding
budget line using budget_line.getCell
"""
if not self.isMemberOf('budget_variation/budget_cell'):
return None
axis = self.getInventoryAxis()
if not axis:
return None
base_category = self.getProperty('variation_base_category')
if not base_category:
return None
movement = brain.getObject()
# axis 'movement' is simply a category membership on movements
if axis == 'movement':
return movement.getDefaultAcquiredCategoryMembership(base_category,
base=True)
# is it a source brain or destination brain ?
is_source_brain = True
if (brain.node_uid != brain.mirror_node_uid):
is_source_brain = (brain.node_uid == movement.getSourceUid())
elif (brain.section_uid != brain.mirror_section_uid):
is_source_brain = (brain.section_uid == movement.getSourceSectionUid())
elif brain.total_quantity:
is_source_brain = (brain.total_quantity == movement.getQuantity())
else:
raise NotImplementedError('Could not guess brain side')
if axis.endswith('_category') or\
axis.endswith('_category_strict_membership'):
# if the axis is category, we get the node and then returns the category
# from that node
if axis.endswith('_category'):
axis = axis[:-len('_category')]
if axis.endswith('_category_strict_membership'):
axis = axis[:-len('_category_strict_membership')]
if is_source_brain:
if axis == 'node':
node = movement.getSourceValue()
else:
node = movement.getProperty('source_%s_value' % axis)
else:
if axis == 'node':
node = movement.getDestinationValue()
else:
node = movement.getProperty('destination_%s_value' % axis)
if node is not None:
return node.getDefaultAcquiredCategoryMembership(base_category,
base=True)
return None
# otherwise we just return the node
if is_source_brain:
if axis == 'node':
return '%s/%s' % (base_category, movement.getSource())
return '%s/%s' % (base_category,
movement.getProperty('source_%s' % axis))
if axis == 'node':
return '%s/%s' % (base_category, movement.getDestination())
return '%s/%s' % (base_category,
movement.getProperty('destination_%s' % axis))
...@@ -78,7 +78,14 @@ class CategoryBudgetVariation(BudgetVariation): ...@@ -78,7 +78,14 @@ class CategoryBudgetVariation(BudgetVariation):
base_category = self.getProperty('variation_base_category') base_category = self.getProperty('variation_base_category')
if not base_category: if not base_category:
return dict() return dict()
for criterion_category in budget_cell.getMembershipCriterionCategoryList():
context = budget_cell
if self.isMemberOf('budget_variation/budget'):
context = budget_cell.getParentValue().getParentValue()
elif self.isMemberOf('budget_variation/budget_line'):
context = budget_cell.getParentValue()
for criterion_category in context.getMembershipCriterionCategoryList():
if '/' not in criterion_category: # safe ... if '/' not in criterion_category: # safe ...
continue continue
criterion_base_category, category_url = criterion_category.split('/', 1) criterion_base_category, category_url = criterion_category.split('/', 1)
...@@ -94,6 +101,39 @@ class CategoryBudgetVariation(BudgetVariation): ...@@ -94,6 +101,39 @@ class CategoryBudgetVariation(BudgetVariation):
return {axis: criterion_category} return {axis: criterion_category}
return dict() return dict()
def getInventoryListQueryDict(self, budget_line):
"""Returns the query dict to pass to simulation query for a budget line
"""
axis = self.getInventoryAxis()
if not axis:
return dict()
base_category = self.getProperty('variation_base_category')
if not base_category:
return dict()
context = budget_line
if self.isMemberOf('budget_variation/budget'):
context = budget_line.getParentValue()
query_dict = dict()
if axis == 'movement':
axis = 'default_strict_%s_uid' % base_category
query_dict['group_by'] = [axis]
else:
query_dict['group_by_%s' % axis] = True
if axis in ('node', 'section', 'payment', 'function', 'project',
'mirror_section', 'mirror_node' ):
axis = '%s_uid' % axis
for category in context.getVariationCategoryList(
base_category_list=(base_category,)):
if axis.endswith('_uid'):
category = self.getPortalObject().portal_categories\
.getCategoryUid(category)
query_dict.setdefault(axis, []).append(category)
return query_dict
def getBudgetVariationRangeCategoryList(self, context): def getBudgetVariationRangeCategoryList(self, context):
"""Returns the Variation Range Category List that can be applied to this """Returns the Variation Range Category List that can be applied to this
budget. budget.
......
...@@ -132,15 +132,23 @@ class NodeBudgetVariation(BudgetVariation): ...@@ -132,15 +132,23 @@ class NodeBudgetVariation(BudgetVariation):
if not base_category: if not base_category:
return dict() return dict()
budget_line = budget_cell.getParentValue() budget_line = budget_cell.getParentValue()
portal = self.getPortalObject()
portal_categories = portal.portal_categories context = budget_cell
for criterion_category in budget_cell.getMembershipCriterionCategoryList(): if self.isMemberOf('budget_variation/budget'):
context = budget_line.getParentValue()
elif self.isMemberOf('budget_variation/budget_line'):
context = budget_line
portal_categories = self.getPortalObject().portal_categories
for criterion_category in context.getMembershipCriterionCategoryList():
if '/' not in criterion_category: # safe ... if '/' not in criterion_category: # safe ...
continue continue
criterion_base_category, node_url = criterion_category.split('/', 1) criterion_base_category, node_url = criterion_category.split('/', 1)
if criterion_base_category == base_category: if criterion_base_category == base_category:
if axis == 'movement': if axis == 'movement':
axis = 'default_%s' % base_category axis = 'default_%s' % base_category
# TODO: This is not correct if axis is a category (such as
# section_category)
axis = '%s_uid' % axis axis = '%s_uid' % axis
if node_url == budget_line.getRelativeUrl(): if node_url == budget_line.getRelativeUrl():
# This is the "All Other" virtual node # This is the "All Other" virtual node
...@@ -155,6 +163,51 @@ class NodeBudgetVariation(BudgetVariation): ...@@ -155,6 +163,51 @@ class NodeBudgetVariation(BudgetVariation):
return dict() return dict()
def getInventoryListQueryDict(self, budget_line):
"""Returns the query dict to pass to simulation query for a budget line
"""
axis = self.getInventoryAxis()
if not axis:
return dict()
base_category = self.getProperty('variation_base_category')
if not base_category:
return dict()
context = budget_line
if self.isMemberOf('budget_variation/budget'):
context = budget_line.getParentValue()
portal_categories = self.getPortalObject().portal_categories
query_dict = dict()
if axis == 'movement':
axis = 'default_%s_uid' % base_category
query_dict['group_by_%s' % axis] = True
# TODO: This is not correct if axis is a category (such as
# section_category)
axis = '%s_uid' % axis
# 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
for node_url in context.getVariationCategoryList(
base_category_list=(base_category,)):
query_dict.setdefault(axis, []).append(
portal_categories.getCategoryValue(node_url,
base_category=base_category).getUid())
return query_dict
def _getCellKeyFromInventoryListBrain(self, brain, budget_line):
"""Compute key from inventory brain, with support for "all others" virtual node.
"""
key = BudgetVariation._getCellKeyFromInventoryListBrain(
self, brain, budget_line)
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() )
return key
def getBudgetLineVariationRangeCategoryList(self, budget_line): def getBudgetLineVariationRangeCategoryList(self, budget_line):
"""Returns the Variation Range Category List that can be applied to this """Returns the Variation Range Category List that can be applied to this
......
...@@ -28,9 +28,31 @@ ...@@ -28,9 +28,31 @@
import unittest import unittest
import transaction import transaction
from DateTime import DateTime
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from AccessControl import getSecurityManager
class TestBudget(ERP5TypeTestCase): class TestBudget(ERP5TypeTestCase):
def afterSetUp(self):
self.validateRules()
product_line = self.portal.portal_categories.product_line
if '1' not in product_line.objectIds():
category = product_line.newContent(portal_type='Category', id='1')
category.newContent(portal_type='Category', id='1.1')
category.newContent(portal_type='Category', id='1.2')
if '2' not in product_line.objectIds():
category = product_line.newContent(portal_type='Category', id='2')
category.newContent(portal_type='Category', id='2.1')
category.newContent(portal_type='Category', id='2.2')
def beforeTearDown(self):
transaction.abort()
self.portal.accounting_module.manage_delObjects(
list(self.portal.accounting_module.objectIds()))
transaction.commit()
self.tic()
def getBusinessTemplateList(self): def getBusinessTemplateList(self):
"""Return the list of required business templates. """Return the list of required business templates.
...@@ -93,21 +115,513 @@ class TestBudget(ERP5TypeTestCase): ...@@ -93,21 +115,513 @@ class TestBudget(ERP5TypeTestCase):
self.assertEquals(budget_line.getMembershipCriterionCategoryList(), []) self.assertEquals(budget_line.getMembershipCriterionCategoryList(), [])
self.assertEquals( self.assertEquals(
budget_line.getMembershipCriterionBaseCategoryList(), []) budget_line.getMembershipCriterionBaseCategoryList(), [])
# TODO: create cells and test variation on cell # simuate a request and call Base_edit, which does all the work of creating
# cell and setting cell properties.
# Other TODOs form = budget_line.BudgetLine_view
# test simple category variation in getInventory self.portal.REQUEST.other.update(
dict(AUTHENTICATED_USER=getSecurityManager().getUser(),
# test that using a category variation on budget level sets membership
# criterion on budget 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="5",
field_matrixbox_membership_criterion_category_list_cell_0_0_0=[
'source/account_module/goods_purchase'],
))
budget_line.Base_edit(form_id=form.getId())
self.assertEquals(1, len(budget_line.contentValues()))
budget_cell = budget_line.getCell('source/account_module/goods_purchase')
self.assertNotEquals(None, budget_cell)
self.assertEquals(['source/account_module/goods_purchase'],
budget_cell.getMembershipCriterionCategoryList())
self.assertEquals(5, budget_cell.getQuantity())
# there is no budget consumption
self.assertEquals(0, budget_cell.getConsumedBudget())
self.assertEquals(0, budget_cell.getEngagedBudget())
self.assertEquals(5, budget_cell.getAvailableBudget())
# there is no budget transfer
self.assertEquals(5, budget_cell.getCurrentBalance())
def test_category_budget_cell_variation(self):
budget_model = self.portal.budget_model_module.newContent(
portal_type='Budget Model')
budget_model.newContent(
portal_type='Category Budget Variation',
int_index=1,
budget_variation='budget_cell',
inventory_axis='node_category',
variation_base_category='account_type',)
budget = self.portal.budget_module.newContent(
portal_type='Budget',
specialise_value=budget_model)
budget_line = budget.newContent(portal_type='Budget Line')
self.assertEquals(['account_type'],
budget_line.getVariationBaseCategoryList())
variation_range_category_list = \
budget_line.BudgetLine_getVariationRangeCategoryList()
self.assertTrue(['', ''] in variation_range_category_list)
self.assertTrue(['Expense', 'account_type/expense'] in variation_range_category_list)
def test_category_budget_line_variation(self):
# test that using a variation on budget line level sets membership
# criterion on budget line
budget_model = self.portal.budget_model_module.newContent(
portal_type='Budget Model')
budget_model.newContent(
portal_type='Category Budget Variation',
int_index=1,
budget_variation='budget_line',
inventory_axis='section_category',
variation_base_category='group',)
budget = self.portal.budget_module.newContent(
portal_type='Budget',
specialise_value=budget_model)
budget_line = budget.newContent(portal_type='Budget Line')
self.assertEquals(['group'],
budget_line.getVariationBaseCategoryList())
variation_range_category_list = \
budget_line.BudgetLine_getVariationRangeCategoryList()
self.assertTrue(['', ''] in variation_range_category_list)
self.assertTrue(['Demo Group', 'group/demo_group'] in variation_range_category_list)
budget_line.edit(variation_category_list=['group/demo_group'])
self.assertEquals(['group'],
budget_line.getMembershipCriterionBaseCategoryList())
self.assertEquals(['group/demo_group'],
budget_line.getMembershipCriterionCategoryList())
def test_category_budget_variation(self):
budget_model = self.portal.budget_model_module.newContent(
portal_type='Budget Model')
budget_model.newContent(
portal_type='Category Budget Variation',
int_index=1,
budget_variation='budget',
inventory_axis='section_category',
variation_base_category='group',)
budget = self.portal.budget_module.newContent(
portal_type='Budget',
specialise_value=budget_model)
self.assertEquals(['group'],
budget.getVariationBaseCategoryList())
variation_range_category_list = \
budget.Budget_getVariationRangeCategoryList()
self.assertTrue(['', ''] in variation_range_category_list)
self.assertTrue(['Demo Group', 'group/demo_group'] in variation_range_category_list)
# setting this variation on the budget also sets membership
budget.edit(variation_category_list=['group/demo_group'])
self.assertEquals('demo_group', budget.getGroup())
self.assertEquals('Demo Group', budget.getGroupTitle())
def test_simple_consumption(self):
budget_model = self.portal.budget_model_module.newContent(
portal_type='Budget Model')
budget_model.newContent(
portal_type='Category Budget Variation',
int_index=1,
budget_variation='budget',
inventory_axis='section_category',
variation_base_category='group',)
budget_model.newContent(
portal_type='Node Budget Variation',
int_index=2,
budget_variation='budget_cell',
inventory_axis='node',
variation_base_category='source',
aggregate_value_list=(
self.portal.account_module.goods_purchase,
self.portal.account_module.fixed_assets,
))
budget_model.newContent(
portal_type='Category Budget Variation',
int_index=3,
budget_variation='budget_cell',
inventory_axis='node_category',
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.edit(variation_category_list=['group/demo_group'])
budget_line = budget.newContent(portal_type='Budget Line')
# set the range, this will adjust the matrix
budget_line.edit(
variation_category_list=(
'source/account_module/goods_purchase',
'source/account_module/fixed_assets',
'account_type/expense',
'account_type/asset', ))
# simuate a request and call Base_edit, which does all the work of creating
# cell and setting cell properties.
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="",
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/account_module/fixed_assets',
'account_type/asset'],
field_matrixbox_quantity_cell_0_1_0="1",
field_matrixbox_membership_criterion_category_list_cell_0_1_0=[
'source/account_module/goods_purchase',
'account_type/expense'],
field_matrixbox_quantity_cell_1_1_0="",
field_matrixbox_membership_criterion_category_list_cell_1_1_0=[],
))
budget_line.Base_edit(form_id=form.getId())
self.assertEquals(2, len(budget_line.contentValues()))
budget_cell = budget_line.getCell('source/account_module/goods_purchase',
'account_type/expense')
self.assertNotEquals(None, budget_cell)
self.assertEquals(
dict(from_date=DateTime(2000, 1, 1),
at_date=DateTime(2000, 12, 31).latestTime(),
node_category='account_type/expense',
node_uid=self.portal.account_module.goods_purchase.getUid(),
section_category='group/demo_group',),
budget_model.getInventoryQueryDict(budget_cell))
budget_cell = budget_line.getCell('source/account_module/fixed_assets',
'account_type/asset')
self.assertNotEquals(None, budget_cell)
self.assertEquals(
dict(from_date=DateTime(2000, 1, 1),
at_date=DateTime(2000, 12, 31).latestTime(),
node_category='account_type/asset',
node_uid=self.portal.account_module.fixed_assets.getUid(),
section_category='group/demo_group',),
budget_model.getInventoryQueryDict(budget_cell))
self.assertEquals(
dict(from_date=DateTime(2000, 1, 1),
at_date=DateTime(2000, 12, 31).latestTime(),
node_category=['account_type/expense', 'account_type/asset'],
node_uid=[self.portal.account_module.goods_purchase.getUid(),
self.portal.account_module.fixed_assets.getUid()],
section_category=['group/demo_group'],
group_by_node_category=True,
group_by_node=True,
group_by_section_category=True,
),
budget_model.getInventoryListQueryDict(budget_line))
atransaction = self.portal.accounting_module.newContent(
portal_type='Accounting Transaction',
resource_value=self.portal.currency_module.euro,
source_section_value=self.portal.organisation_module.my_organisation,
start_date=DateTime(2000, 1, 2))
atransaction.newContent(
portal_type='Accounting Transaction Line',
source_value=self.portal.account_module.goods_purchase,
source_debit=100)
atransaction.newContent(
portal_type='Accounting Transaction Line',
source_value=self.portal.account_module.fixed_assets,
source_credit=100)
atransaction.stop()
transaction.commit()
self.tic()
self.assertEquals(
{('source/account_module/fixed_assets', 'account_type/asset'): -100.0,
('source/account_module/goods_purchase', 'account_type/expense'): 100.0},
budget_line.getConsumedBudgetDict())
self.assertEquals(
{('source/account_module/fixed_assets', 'account_type/asset'): -100.0,
('source/account_module/goods_purchase', 'account_type/expense'): 100.0},
budget_line.getEngagedBudgetDict())
def test_all_other_and_strict_consumption(self):
# tests consumptions, by using "all other" virtual node on a node budget
# variation, and strict membership on category budget variation
budget_model = self.portal.budget_model_module.newContent(
portal_type='Budget Model')
budget_model.newContent(
portal_type='Category Budget Variation',
int_index=1,
budget_variation='budget',
inventory_axis='section_category_strict_membership',
variation_base_category='group',)
budget_model.newContent(
portal_type='Node Budget Variation',
int_index=2,
budget_variation='budget_cell',
inventory_axis='node',
variation_base_category='source',
aggregate_value_list=(
self.portal.account_module.goods_purchase,),
include_virtual_other_node=True)
budget_model.newContent(
portal_type='Category Budget Variation',
int_index=3,
budget_variation='budget_cell',
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.edit(variation_category_list=['group/demo_group/sub1'])
budget_line = budget.newContent(portal_type='Budget Line')
# set the range, this will adjust the matrix
budget_line.edit(
variation_category_list=(
'source/account_module/goods_purchase',
'source/%s' % budget_line.getRelativeUrl(), # this is 'all others'
'account_type/expense',
'account_type/asset', ))
# simuate a request and call Base_edit, which does all the work of creating
# cell and setting cell properties.
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="",
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(),
'account_type/asset'],
field_matrixbox_quantity_cell_0_1_0="1",
field_matrixbox_membership_criterion_category_list_cell_0_1_0=[
'source/account_module/goods_purchase',
'account_type/expense'],
field_matrixbox_quantity_cell_1_1_0="",
field_matrixbox_membership_criterion_category_list_cell_1_1_0=[],
))
budget_line.Base_edit(form_id=form.getId())
self.assertEquals(2, 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',
'account_type/asset'],
section_category_strict_membership=['group/demo_group/sub1'],
group_by_node_category_strict_membership=True,
group_by_node=True,
group_by_section_category_strict_membership=True,
),
budget_model.getInventoryListQueryDict(budget_line))
atransaction = self.portal.accounting_module.newContent(
portal_type='Accounting Transaction',
resource_value=self.portal.currency_module.euro,
source_section_value=self.portal.organisation_module.my_organisation,
start_date=DateTime(2000, 1, 2))
atransaction.newContent(
portal_type='Accounting Transaction Line',
source_value=self.portal.account_module.goods_purchase,
source_debit=100)
atransaction.newContent(
portal_type='Accounting Transaction Line',
source_value=self.portal.account_module.fixed_assets,
source_credit=100)
atransaction.stop()
transaction.commit()
self.tic()
self.assertEquals(
{('source/%s' % budget_line.getRelativeUrl(), '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/account_module/goods_purchase', 'account_type/expense'): 100.0},
budget_line.getEngagedBudgetDict())
def test_consumption_movement_category(self):
# test for budget consumption using movement category
budget_model = self.portal.budget_model_module.newContent(
portal_type='Budget Model')
budget_model.newContent(
portal_type='Category Budget Variation',
int_index=1,
budget_variation='budget',
inventory_axis='section_category',
variation_base_category='group',)
budget_model.newContent(
portal_type='Node Budget Variation',
int_index=2,
budget_variation='budget_cell',
inventory_axis='node',
variation_base_category='source',
aggregate_value_list=(
self.portal.account_module.goods_purchase,
self.portal.account_module.fixed_assets,
))
budget_model.newContent(
portal_type='Category Budget Variation',
int_index=3,
budget_variation='budget_cell',
inventory_axis='movement',
variation_base_category='product_line',)
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.edit(variation_category_list=['group/demo_group'])
budget_line = budget.newContent(portal_type='Budget Line')
# set the range, this will adjust the matrix
budget_line.edit(
variation_category_list=(
'source/account_module/goods_purchase',
'source/account_module/fixed_assets',
'product_line/1',
'product_line/1/1.1',
'product_line/1/1.2', ))
# simuate a request and call Base_edit, which does all the work of creating
# cell and setting cell properties.
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'),
# this cell will be a summary cell
field_matrixbox_quantity_cell_0_0_0="2",
field_matrixbox_membership_criterion_category_list_cell_0_0_0=[
'source/account_module/goods_purchase',
'product_line/1'],
field_matrixbox_quantity_cell_1_0_0="",
field_matrixbox_membership_criterion_category_list_cell_1_0_0=[],
field_matrixbox_quantity_cell_0_1_0="2",
field_matrixbox_membership_criterion_category_list_cell_0_1_0=[
'source/account_module/goods_purchase',
'product_line/1/1.1'],
field_matrixbox_quantity_cell_1_1_0="",
field_matrixbox_membership_criterion_category_list_cell_1_1_0=[],
field_matrixbox_quantity_cell_0_2_0="",
field_matrixbox_membership_criterion_category_list_cell_0_2_0=[],
field_matrixbox_quantity_cell_1_2_0="",
field_matrixbox_membership_criterion_category_list_cell_1_2_0=[],
))
budget_line.Base_edit(form_id=form.getId())
self.assertEquals(2, len(budget_line.contentValues()))
product_line_1 = self.portal.portal_categories.product_line['1']
product_line_1_11 = product_line_1['1.1']
product_line_1_12 = product_line_1['1.2']
self.assertEquals(
dict(from_date=DateTime(2000, 1, 1),
at_date=DateTime(2000, 12, 31).latestTime(),
node_uid=[self.portal.account_module.goods_purchase.getUid(),
self.portal.account_module.fixed_assets.getUid(),],
default_strict_product_line_uid=[product_line_1.getUid(),
product_line_1_11.getUid(),
product_line_1_12.getUid(),],
section_category=['group/demo_group'],
group_by=['default_strict_product_line_uid'],
group_by_node=True,
group_by_section_category=True,
),
budget_model.getInventoryListQueryDict(budget_line))
atransaction = self.portal.accounting_module.newContent(
portal_type='Accounting Transaction',
resource_value=self.portal.currency_module.euro,
source_section_value=self.portal.organisation_module.my_organisation,
start_date=DateTime(2000, 1, 2))
atransaction.newContent(
portal_type='Accounting Transaction Line',
source_value=self.portal.account_module.goods_purchase,
product_line_value=product_line_1_11,
source_debit=100)
atransaction.newContent(
portal_type='Accounting Transaction Line',
source_value=self.portal.account_module.fixed_assets,
product_line_value=product_line_1_12,
source_credit=100)
atransaction.stop()
transaction.commit()
self.tic()
self.assertEquals(
{('source/account_module/fixed_assets', 'product_line/1/1.2'): -100.0,
('source/account_module/goods_purchase', 'product_line/1/1.1'): 100.0,
# summary line is automatically added (TODO)
## ('source/account_module/goods_purchase', 'product_line/1'): 100.0
},
budget_line.getConsumedBudgetDict())
self.assertEquals(
{('source/account_module/fixed_assets', 'product_line/1/1.2'): -100.0,
('source/account_module/goods_purchase', 'product_line/1/1.1'): 100.0,
# summary line is automatically added (TODO)
## ('source/account_module/goods_purchase', 'product_line/1'): 100.0
},
budget_line.getEngagedBudgetDict())
# Other TODOs:
# section_category & summary
# test that using a variation on budget line level sets membership # resource/price currency on budget ?
# criterion on budget line (and budget cell or not ?)
# test virtual all others when cloning an existing budget
# test that using a category variation on budget level is used in inventory # predicates
# calculation
def test_suite(): def test_suite():
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment