diff --git a/product/ERP5/Document/BudgetLine.py b/product/ERP5/Document/BudgetLine.py index eeaa11a95320daa380c3bd21c14e280ac728b0d2..a9e9e3943a8383551595b53d1f6b43a3fc19d748 100644 --- a/product/ERP5/Document/BudgetLine.py +++ b/product/ERP5/Document/BudgetLine.py @@ -116,14 +116,15 @@ class BudgetLine(Predicate, XMLMatrix, Variated): query_dict.setdefault('ignore_group_by', True) sign = self.BudgetLine_getConsumptionSign() + cell_key_cache = dict() budget_dict = dict() for brain in self.getPortalObject().portal_simulation\ .getCurrentInventoryList(**query_dict): + cell_key = budget_model._getCellKeyFromInventoryListBrain(brain, self, + cell_key_cache=cell_key_cache) # XXX total_quantity or total_price ?? - previous_value = budget_dict.get( - budget_model._getCellKeyFromInventoryListBrain(brain, self), 0) - budget_dict[budget_model._getCellKeyFromInventoryListBrain(brain, self)] = \ - previous_value + brain.total_price * sign + previous_value = budget_dict.get(cell_key, 0) + budget_dict[cell_key] = previous_value + brain.total_price * sign return budget_dict diff --git a/product/ERP5/Document/BudgetModel.py b/product/ERP5/Document/BudgetModel.py index 9d3e69da630c9351814d87edfe9115c16cda54c2..edb5e69c98a45fdfefef1e9e9098e22dd851f631 100644 --- a/product/ERP5/Document/BudgetModel.py +++ b/product/ERP5/Document/BudgetModel.py @@ -99,10 +99,16 @@ class BudgetModel(Predicate): 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 + variation_query_dict = budget_variation.getInventoryListQueryDict( + budget_line) + # Merge group_by and select_list arguments. + # 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']) + if 'select_list' in query_dict \ + and 'select_list' in variation_query_dict: + variation_query_dict['select_list'].extend( + query_dict['select_list']) query_dict.update(variation_query_dict) @@ -114,7 +120,8 @@ class BudgetModel(Predicate): query_dict.setdefault('at_date', start_date_range_max.latestTime()) return query_dict - def _getCellKeyFromInventoryListBrain(self, brain, budget_line): + def _getCellKeyFromInventoryListBrain(self, brain, budget_line, + cell_key_cache=None): """Compute the cell key from an inventory brain, the cell key can be used to retrieve the budget cell in the corresponding budget line. """ @@ -122,8 +129,8 @@ class BudgetModel(Predicate): for budget_variation in sorted(self.contentValues( portal_type=self.getPortalBudgetVariationTypeList()), key=lambda x:x.getIntIndex()): - key = budget_variation._getCellKeyFromInventoryListBrain(brain, - budget_line) + key = budget_variation._getCellKeyFromInventoryListBrain( + brain, budget_line, cell_key_cache=cell_key_cache) if key: cell_key += (key,) return cell_key diff --git a/product/ERP5/Document/BudgetVariation.py b/product/ERP5/Document/BudgetVariation.py index 34e22e307bd56fcaedad695b1b952293f202d91d..99d9a87d49d3ff3f478873a579c8bc66b98adfd8 100644 --- a/product/ERP5/Document/BudgetVariation.py +++ b/product/ERP5/Document/BudgetVariation.py @@ -96,10 +96,12 @@ class BudgetVariation(Predicate): """ return {} - def _getCellKeyFromInventoryListBrain(self, brain, budget_line): + def _getCellKeyFromInventoryListBrain(self, brain, budget_line, + cell_key_cache=None): """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 + A dictionnary can be passed as "cell_key_cache" to cache catalog lookups """ if not self.isMemberOf('budget_variation/budget_cell'): return None @@ -110,55 +112,30 @@ class BudgetVariation(Predicate): 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()) + + getObject = self.getPortalObject().portal_catalog.getObject + def getUrlFromUidNoCache(uid): + relative_url = getObject(uid).getRelativeUrl() + if relative_url.startswith('%s/' % base_category): + return relative_url + return '%s/%s' % (base_category, relative_url) + + if cell_key_cache is not None: + def getUrlFromUidWithCache(uid): + try: + return cell_key_cache[uid] + except KeyError: + relative_url = getUrlFromUidNoCache(uid) + cell_key_cache[uid] = relative_url + return relative_url + getUrlFromUid = getUrlFromUidWithCache 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 + getUrlFromUid = getUrlFromUidNoCache - # 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)) + if axis == 'movement': + return getUrlFromUid(getattr(brain, 'default_%s_uid' % base_category)) + elif axis == 'movement_strict_membership': + return getUrlFromUid(getattr(brain, + 'default_strict_%s_uid' % base_category)) + return getUrlFromUid(getattr(brain, '%s_uid' % axis)) diff --git a/product/ERP5/Document/CategoryBudgetVariation.py b/product/ERP5/Document/CategoryBudgetVariation.py index 7555cbfa3cf3a8e171fa03ceeea3903de831b5eb..6d2b13f9bfb6766368bac621a9ed1ee5a82a5cd1 100644 --- a/product/ERP5/Document/CategoryBudgetVariation.py +++ b/product/ERP5/Document/CategoryBudgetVariation.py @@ -95,6 +95,8 @@ class CategoryBudgetVariation(BudgetVariation): # Different possible inventory axis here if axis == 'movement': return {'default_%s_uid' % base_category: category_uid} + if axis == 'movement_strict_membership': + return {'default_strict_%s_uid' % base_category: category_uid} if axis in ('node', 'section', 'payment', 'function', 'project', 'mirror_section', 'mirror_node' ): return {'%s_uid' % axis: category_uid} @@ -117,8 +119,13 @@ class CategoryBudgetVariation(BudgetVariation): query_dict = dict() if axis == 'movement': + axis = 'default_%s_uid' % base_category + query_dict['group_by'] = [axis] + query_dict['select_list'] = [axis] + elif axis == 'movement_strict_membership': axis = 'default_strict_%s_uid' % base_category query_dict['group_by'] = [axis] + query_dict['select_list'] = [axis] else: query_dict['group_by_%s' % axis] = True if axis in ('node', 'section', 'payment', 'function', 'project', @@ -126,7 +133,7 @@ class CategoryBudgetVariation(BudgetVariation): axis = '%s_uid' % axis for category in context.getVariationCategoryList( - base_category_list=(base_category,)): + base_category_list=(base_category,)): if axis.endswith('_uid'): category = self.getPortalObject().portal_categories\ .getCategoryUid(category) diff --git a/product/ERP5/Document/NodeBudgetVariation.py b/product/ERP5/Document/NodeBudgetVariation.py index 0b8b3e92880d61e86274b1026249b8540ab8f7af..be12ea83108e820d3914994f224b0c9391b20700 100644 --- a/product/ERP5/Document/NodeBudgetVariation.py +++ b/product/ERP5/Document/NodeBudgetVariation.py @@ -147,8 +147,11 @@ class NodeBudgetVariation(BudgetVariation): if criterion_base_category == base_category: if axis == 'movement': axis = 'default_%s' % base_category - # TODO: This is not correct if axis is a category (such as - # section_category) + if axis == 'movement_strict_membership': + axis = 'default_strict_%s' % base_category + # TODO: This is not correct if axis is a category such as + # section_category, because getInventoryList for now does not support + # parameters such as section_category_uid axis = '%s_uid' % axis if node_url == budget_line.getRelativeUrl(): # This is the "All Other" virtual node @@ -181,7 +184,12 @@ class NodeBudgetVariation(BudgetVariation): query_dict = dict() if axis == 'movement': axis = 'default_%s_uid' % base_category + query_dict['select_list'] = [axis] + if axis == 'movement_strict_membership': + axis = 'default_strict_%s_uid' % base_category + query_dict['select_list'] = [axis] 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 @@ -191,17 +199,19 @@ class NodeBudgetVariation(BudgetVariation): return query_dict for node_url in context.getVariationCategoryList( - base_category_list=(base_category,)): + 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. + def _getCellKeyFromInventoryListBrain(self, brain, budget_line, + cell_key_cache=None): + """Compute key from inventory brain, with support for "all others" virtual + node. """ key = BudgetVariation._getCellKeyFromInventoryListBrain( - self, brain, budget_line) + 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)]: diff --git a/product/ERP5/tests/testBudget.py b/product/ERP5/tests/testBudget.py index a5e8a4c658a724a0f2c27f584908dbf0e01d7619..3f83ae79d941d9221737337b38a8c9e1bdc9011f 100644 --- a/product/ERP5/tests/testBudget.py +++ b/product/ERP5/tests/testBudget.py @@ -634,11 +634,14 @@ class TestBudget(ERP5TypeTestCase): 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(), + default_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=['default_product_line_uid'], + # select list is passed, because getInventoryList does not add + # group by related keys to select + select_list=['default_product_line_uid'], group_by_node=True, group_by_section_category=True, ), @@ -668,28 +671,123 @@ class TestBudget(ERP5TypeTestCase): 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 + # summary lines are automatically added + ('source/account_module/fixed_assets', 'product_line/1'): -100.0, + ('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 + ('source/account_module/fixed_assets', 'product_line/1'): -100.0, + ('source/account_module/goods_purchase', 'product_line/1'): 100.0 }, budget_line.getEngagedBudgetDict()) self.assertEquals( {('source/account_module/fixed_assets', 'product_line/1/1.2'): 100.0, ('source/account_module/goods_purchase', 'product_line/1/1.1'): -98.0, - # summary line is automatically added (TODO) -## ('source/account_module/goods_purchase', 'product_line/1'): 98.0 - ('source/account_module/goods_purchase', 'product_line/1'): 2.0 + ('source/account_module/fixed_assets', 'product_line/1'): 100.0, + ('source/account_module/goods_purchase', 'product_line/1'): -98, }, budget_line.getAvailableBudgetDict()) + def test_consumption_category_variation_summary(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='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 = 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',) + + # set the range, this will adjust the matrix + budget_line.edit( + variation_category_list=( + 'source/account_module/goods_purchase', + 'group/demo_group', + 'group/demo_group/sub1', + )) + + 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="500", + field_matrixbox_membership_criterion_category_list_cell_1_0_0=[ + 'group/demo_group/sub1', + 'source/account_module/goods_purchase', ], + )) + budget_line.Base_edit(form_id=form.getId()) + + self.assertEquals(1, len(budget_line.contentValues())) + + 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(),], + section_category=['group/demo_group', + 'group/demo_group/sub1'], + 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( + {('group/demo_group/sub1', 'source/account_module/goods_purchase'): 100.0, + ('group/demo_group', 'source/account_module/goods_purchase'): 100.0,}, + budget_line.getConsumedBudgetDict()) + + self.assertEquals( + {('group/demo_group/sub1', 'source/account_module/goods_purchase'): 100.0, + ('group/demo_group', 'source/account_module/goods_purchase'): 100.0,}, + budget_line.getEngagedBudgetDict()) + def test_budget_consumption_report(self): budget_model = self.portal.budget_model_module.newContent( @@ -837,9 +935,316 @@ class TestBudget(ERP5TypeTestCase): self.fail(''.join(err_list)) - # Other TODOs: + def test_update_summary_cell_simple(self): + # test the action to create or update quantity on summary cells + 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='movement', + variation_base_category='product_line',) + 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='section_category', + variation_base_category='group',) + + 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') + + # set the range, this will adjust the matrix + budget_line.edit( + variation_category_list=( + 'group/demo_group', + 'group/demo_group/sub1', + 'group/demo_group/sub2', + 'source/account_module/goods_purchase', + 'source/account_module/fixed_assets', + 'product_line/1', + 'product_line/1/1.1', + 'product_line/1/1.2', )) - # section_category & summary + 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'), + + # group/demo_group + field_matrixbox_quantity_cell_0_0_0="", + field_matrixbox_membership_criterion_category_list_cell_0_0_0=[], + field_matrixbox_quantity_cell_1_0_0="", + field_matrixbox_membership_criterion_category_list_cell_1_0_0=[], + field_matrixbox_quantity_cell_2_0_0="", + field_matrixbox_membership_criterion_category_list_cell_2_0_0=[], + field_matrixbox_quantity_cell_0_1_0="", + field_matrixbox_membership_criterion_category_list_cell_0_1_0=[], + field_matrixbox_quantity_cell_1_1_0="", + field_matrixbox_membership_criterion_category_list_cell_1_1_0=[], + # This is a summary cell, but we set a manual value. + field_matrixbox_quantity_cell_2_1_0="100", + field_matrixbox_membership_criterion_category_list_cell_2_1_0=[ + 'product_line/1/1.2', + 'source/account_module/fixed_assets', + 'group/demo_group', + ], + + # group/demo_group/sub1 + field_matrixbox_quantity_cell_0_0_1="", + field_matrixbox_membership_criterion_category_list_cell_0_0_1=[], + field_matrixbox_quantity_cell_1_0_1="1", + field_matrixbox_membership_criterion_category_list_cell_1_0_1=[ + 'product_line/1/1.1', + 'source/account_module/goods_purchase', + 'group/demo_group/sub1', + ], + field_matrixbox_quantity_cell_2_0_1="2", + field_matrixbox_membership_criterion_category_list_cell_2_0_1=[ + 'product_line/1/1.2', + 'source/account_module/goods_purchase', + 'group/demo_group/sub1', + ], + field_matrixbox_quantity_cell_0_1_1="", + field_matrixbox_membership_criterion_category_list_cell_0_1_1=[], + field_matrixbox_quantity_cell_1_1_1="3", + field_matrixbox_membership_criterion_category_list_cell_1_1_1=[ + 'product_line/1/1.1', + 'source/account_module/fixed_assets', + 'group/demo_group/sub1', + ], + field_matrixbox_quantity_cell_2_1_1="4", + field_matrixbox_membership_criterion_category_list_cell_2_1_1=[ + 'product_line/1/1.2', + 'source/account_module/fixed_assets', + 'group/demo_group/sub1', + ], + + # group/demo_group/sub2 + field_matrixbox_quantity_cell_0_0_2="", + field_matrixbox_membership_criterion_category_list_cell_0_0_2=[], + # we only have 1 cell here + field_matrixbox_quantity_cell_1_0_2="5", + field_matrixbox_membership_criterion_category_list_cell_1_0_2=[ + 'product_line/1/1.1', + 'source/account_module/goods_purchase', + 'group/demo_group/sub2', + ], + field_matrixbox_quantity_cell_2_0_2="", + field_matrixbox_membership_criterion_category_list_cell_2_0_2=[], + # we have no cells here + field_matrixbox_quantity_cell_0_1_2="", + field_matrixbox_membership_criterion_category_list_cell_0_1_2=[], + field_matrixbox_quantity_cell_1_1_2="", + field_matrixbox_membership_criterion_category_list_cell_1_1_2=[], + field_matrixbox_quantity_cell_2_1_2="", + field_matrixbox_membership_criterion_category_list_cell_2_1_2=[], + )) + + budget_line.Base_edit(form_id=form.getId()) + + self.assertEquals(6, len(budget_line.contentValues())) + + budget_line.BudgetLine_setQuantityOnSummaryCellList() + + # summary cells have been created: + self.assertEquals(14, len(budget_line.contentValues())) + + # those cells are aggregating + self.assertEquals(1+2, budget_line.getCell( + 'product_line/1', + 'source/account_module/goods_purchase', + 'group/demo_group/sub1',).getQuantity()) + self.assertEquals(4+3, budget_line.getCell( + 'product_line/1', + 'source/account_module/fixed_assets', + 'group/demo_group/sub1',).getQuantity()) + self.assertEquals(1+5, budget_line.getCell( + 'product_line/1/1.1', + 'source/account_module/goods_purchase', + 'group/demo_group',).getQuantity()) + self.assertEquals(1+2+5, budget_line.getCell( + 'product_line/1', + 'source/account_module/goods_purchase', + 'group/demo_group',).getQuantity()) + + # the cell that we have modified is erased + self.assertEquals(4, budget_line.getCell( + 'product_line/1/1.2', + 'source/account_module/fixed_assets', + 'group/demo_group',).getQuantity()) + + # test all cells for complete coverage + self.assertEquals(6, budget_line.getCell( + 'product_line/1/1.1', + 'source/account_module/goods_purchase', + 'group/demo_group',).getQuantity()) + self.assertEquals(2, budget_line.getCell( + 'product_line/1/1.2', + 'source/account_module/goods_purchase', + 'group/demo_group',).getQuantity()) + self.assertEquals(3+4, budget_line.getCell( + 'product_line/1', + 'source/account_module/fixed_assets', + 'group/demo_group',).getQuantity()) + self.assertEquals(3, budget_line.getCell( + 'product_line/1/1.1', + 'source/account_module/fixed_assets', + 'group/demo_group',).getQuantity()) + self.assertEquals(4, budget_line.getCell( + 'product_line/1/1.2', + 'source/account_module/fixed_assets', + 'group/demo_group',).getQuantity()) + self.assertEquals(5, budget_line.getCell( + 'product_line/1', + 'source/account_module/goods_purchase', + 'group/demo_group/sub2',).getQuantity()) + + # change a cell quantity and call again + budget_cell = budget_line.getCell( + 'product_line/1/1.2', + 'source/account_module/goods_purchase', + 'group/demo_group/sub1') + self.assertNotEquals(None, budget_cell) + self.assertEquals(2, budget_cell.getQuantity()) + budget_cell.setQuantity(6) + + budget_line.BudgetLine_setQuantityOnSummaryCellList() + self.assertEquals(14, len(budget_line.contentValues())) + + self.assertEquals(1+6, budget_line.getCell( + 'product_line/1', + 'source/account_module/goods_purchase', + 'group/demo_group/sub1',).getQuantity()) + self.assertEquals(4+3, budget_line.getCell( + 'product_line/1', + 'source/account_module/fixed_assets', + 'group/demo_group/sub1',).getQuantity()) + self.assertEquals(1+5, budget_line.getCell( + 'product_line/1/1.1', + 'source/account_module/goods_purchase', + 'group/demo_group',).getQuantity()) + self.assertEquals(1+6+5, budget_line.getCell( + 'product_line/1', + 'source/account_module/goods_purchase', + 'group/demo_group',).getQuantity()) + + + def test_update_summary_cell_non_strict_and_second_summary(self): + # test the action to create or update quantity on summary cells, variation + # which are strict are not updated, and multiple level summary does not + # aggregate again intermediate summaries + 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='movement_strict_membership', + variation_base_category='product_line',) + 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_line = budget.newContent(portal_type='Budget Line') + + # set the range, this will adjust the matrix + budget_line.edit( + variation_category_list=( + 'account_type/asset', + 'account_type/asset/cash', + 'account_type/asset/cash/bank', + 'source/account_module/goods_purchase', + 'product_line/1', + 'product_line/1/1.1', )) + + 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="", + field_matrixbox_membership_criterion_category_list_cell_1_0_0=[], + field_matrixbox_quantity_cell_0_0_1="", + field_matrixbox_membership_criterion_category_list_cell_0_0_1=[], + field_matrixbox_quantity_cell_1_0_1="", + field_matrixbox_membership_criterion_category_list_cell_1_0_1=[], + field_matrixbox_quantity_cell_0_0_2="", + field_matrixbox_membership_criterion_category_list_cell_0_0_2=[], + field_matrixbox_quantity_cell_1_0_2="1", + field_matrixbox_membership_criterion_category_list_cell_1_0_2=[ + 'product_line/1/1.1', + 'source/account_module/goods_purchase', + 'account_type/asset/cash/bank', + ], + )) + budget_line.Base_edit(form_id=form.getId()) + + self.assertEquals(1, len(budget_line.contentValues())) + + budget_line.BudgetLine_setQuantityOnSummaryCellList() + self.assertEquals(3, len(budget_line.contentValues())) + + budget_cell = budget_line.getCell( + 'product_line/1/1.1', + 'source/account_module/goods_purchase', + 'account_type/asset/cash') + self.assertNotEquals(None, budget_cell) + self.assertEquals(1, budget_cell.getQuantity()) + + budget_cell = budget_line.getCell( + 'product_line/1/1.1', + 'source/account_module/goods_purchase', + 'account_type/asset',) + self.assertNotEquals(None, budget_cell) + self.assertEquals(1, budget_cell.getQuantity()) + + + # Other TODOs: # budget level variation and budget cell level variation for same inventory # axis