Commit 5fd031ec authored by Jérome Perrin's avatar Jérome Perrin

Simplify budget model variations, only 2 simple class are used:

 - NodeBudgetVariation that explicitly lists the possible values one by one,
   either by selecting them of specifying a script to return this list of
   values
 - CategoryBudgetVariation that uses a base category
for each one, you specify what will be the "axis" used for getInventory (node,
mirror_node, node_category etc)

"SectionCategoryBudgetVariation" is therefore useless, it's a
CategoryBudgetVariation where the axis is "section_category"

Budget Cell, Budget Line and now Budget itself can be variated
 


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@25611 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 6992dd3f
......@@ -31,12 +31,13 @@ from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
from Products.ERP5.Document.Predicate import Predicate
from Products.ERP5.Variated import Variated
from Products.ERP5.Document.Delivery import Delivery
from Products.ERP5.Document.Inventory import Inventory
from zLOG import LOG
class Budget(Predicate):
class Budget(Predicate, Variated):
"""
Budget means a kind of budget stock.
"""
......@@ -52,6 +53,7 @@ class Budget(Predicate):
, PropertySheet.Arrow
, PropertySheet.Budget
, PropertySheet.Path
, PropertySheet.VariationRange
)
# CMF Type Definition
......
......@@ -65,7 +65,7 @@ class BudgetModel(Predicate):
for budget_variation in sorted(self.contentValues(
portal_type=self.getPortalBudgetVariationTypeList(),),
key=lambda x:x.getIntIndex()):
if not budget_variation.isMemberOf('budget_variation/cell'):
if not budget_variation.isMemberOf('budget_variation/budget_cell'):
continue
variation_cell_range = budget_variation.getCellRangeForBudgetLine(
budget_line, matrixbox=matrixbox)
......@@ -107,6 +107,8 @@ class BudgetModel(Predicate):
for budget_variation in sorted(self.contentValues(
portal_type=self.getPortalBudgetVariationTypeList()),
key=lambda x:x.getIntIndex()):
if budget_variation.isMemberOf('budget_variation/budget'):
continue
variation_range = \
budget_variation.getBudgetLineVariationRangeCategoryList(budget_line)
if variation_range and variation_range not in\
......@@ -114,9 +116,31 @@ class BudgetModel(Predicate):
variation_range_category_list.extend(variation_range)
return variation_range_category_list
def getBudgetVariationRangeCategoryList(self, budget):
variation_range_category_list = []
for budget_variation in sorted(self.contentValues(
portal_type=self.getPortalBudgetVariationTypeList()),
key=lambda x:x.getIntIndex()):
if not budget_variation.isMemberOf('budget_variation/budget'):
continue
variation_range = \
budget_variation.getBudgetVariationRangeCategoryList(budget)
if variation_range and variation_range not in\
variation_range_category_list:
variation_range_category_list.extend(variation_range)
return variation_range_category_list
def initializeBudgetLine(self, budget_line):
for budget_variation in sorted(self.contentValues(
portal_type=self.getPortalBudgetVariationTypeList()),
key=lambda x:x.getIntIndex()):
budget_variation.initializeBudgetLine(budget_line)
if not budget_variation.isMemberOf('budget_variation/budget'):
budget_variation.initializeBudgetLine(budget_line)
def initializeBudget(self, budget):
for budget_variation in sorted(self.contentValues(
portal_type=self.getPortalBudgetVariationTypeList()),
key=lambda x:x.getIntIndex()):
if budget_variation.isMemberOf('budget_variation/budget'):
budget_variation.initializeBudget(budget)
......@@ -37,6 +37,7 @@ from zLOG import LOG
class BudgetVariation(Predicate):
"""Base class for budget variations.
"""
# Default Properties
......@@ -65,7 +66,15 @@ class BudgetVariation(Predicate):
"""
def initializeBudgetLine(self, budget_line):
"""Initialize a budget line
"""Initialize a budget line.
Called when a new budget line is created.
"""
def initializeBudget(self, budget):
"""Initialize a budget.
Called when a budget is associated to a budget model.
"""
def getBudgetLineVariationRangeCategoryList(self, budget_line):
......@@ -73,8 +82,13 @@ class BudgetVariation(Predicate):
"""
return []
def getBudgetVariationRangeCategoryList(self, budget):
"""Returns the variation range categories for this budget
"""
return []
def getCellRangeForBudgetLine(self, budget_line, matrixbox=0):
"""Return the cell range to use for the budget.
"""Return the cell range to use for the budget line
"""
return []
......
......@@ -27,12 +27,13 @@
from AccessControl import ClassSecurityInfo
from AccessControl.ZopeGuards import guarded_getattr
from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
from Products.ERP5.Document.BudgetVariation import BudgetVariation
class SectionCategoryBudgetVariation(BudgetVariation):
"""A budget variation for section category.
class CategoryBudgetVariation(BudgetVariation):
""" A budget variation based on a category
"""
# Default Properties
property_sheets = ( PropertySheet.Base
......@@ -41,11 +42,12 @@ class SectionCategoryBudgetVariation(BudgetVariation):
, PropertySheet.SortIndex
, PropertySheet.Path
, PropertySheet.Predicate
, PropertySheet.BudgetVariation
)
# CMF Type Definition
meta_type = 'ERP5 Section Category Budget Variation'
portal_type = 'Section Category Budget Variation'
meta_type = 'ERP5 Category Budget Variation'
portal_type = 'Category Budget Variation'
add_permission = Permissions.AddPortalContent
isPortalContent = 1
isRADContent = 1
......@@ -63,65 +65,100 @@ class SectionCategoryBudgetVariation(BudgetVariation):
def getCellRangeForBudgetLine(self, budget_line, matrixbox=0):
"""The cell range added by this variation
"""
portal = self.getPortalObject()
resolveCategory = portal.portal_categories.resolveCategory
cell_range_dict = dict()
for variation_base_category in \
self.getProperty('variation_base_category_list'):
for category in budget_line.getVariationCategoryList():
if category.startswith(variation_base_category):
if matrixbox:
cell_range_dict.setdefault(
variation_base_category, []).append(
(category,
resolveCategory(category).getTranslatedLogicalPath(),))
else:
cell_range_dict.setdefault(
variation_base_category, []).append(category)
return cell_range_dict.values()
item_list = self.getBudgetLineVariationRangeCategoryList(budget_line)
variation_category_list = budget_line.getVariationCategoryList()
if matrixbox:
return [[(i[1], i[0]) for i in item_list if i[1] in variation_category_list]]
return [[i[1] for i in item_list if i[1] in variation_category_list]]
def getInventoryQueryDict(self, budget_cell):
""" Query dict to pass to simulation query
"""
section_category_list = []
for variation_base_category in \
self.getProperty('variation_base_category_list'):
for category in budget_cell.getMembershipCriterionCategoryList():
if category.startswith(variation_base_category):
section_category_list.append(category)
# FIXME: this should be a AND, but passing this to inventory API:
# section_category=dict(query=section_category_list,
# operator='AND')
# just generates an impossible query
return dict(section_category=section_category_list)
if not self.getInventoryAxis():
return dict()
base_category = self.getProperty('variation_base_category')
if not base_category:
return dict()
# XXX pass base_category= ...
for criterion_category in budget_cell.getMembershipCriterionCategoryList():
if '/' not in criterion_category: # safe ...
continue
criterion_base_category, category_url = criterion_category.split('/', 1)
# Different possible inventory axis here
axis = self.getInventoryAxis()
if axis == 'movement':
return {'default_%s_uid' % base_category:
self.getPortalObject().portal_categories.getCategoryUid(criterion_category)}
if criterion_base_category == base_category:
return {axis: criterion_category}
return dict()
def getBudgetVariationRangeCategoryList(self, context):
"""Returns the Variation Range Category List that can be applied to this
budget.
"""
base_category = self.getProperty('variation_base_category')
if not base_category:
return []
portal = self.getPortalObject()
item_list_method = portal.portal_preferences.getPreference(
'preferred_category_child_item_list_method_id',
'getCategoryChildCompactLogicalPathItemList')
return getattr(portal.portal_categories._getOb(base_category),
item_list_method)(
base=1,
local_sort_id=('int_index',
'translated_title'),
checked_permission='View')
def getBudgetLineVariationRangeCategoryList(self, budget_line):
"""Returns the Variation Range Category List that can be applied to this
budget line.
"""
base_category = self.getProperty('variation_base_category')
if not base_category:
return []
portal = self.getPortalObject()
variation_range_category_list = []
category_tool = portal.portal_categories
item_list_method_id = portal.portal_preferences\
.getPreferredCategoryChildItemListMethodId()
for variation_base_category in \
self.getProperty('variation_base_category_list'):
if variation_base_category in category_tool.objectIds():
base_category = category_tool._getOb(variation_base_category)
variation_range_category_list.extend(
getattr(base_category, item_list_method_id)(base=1))
return variation_range_category_list
item_list_method = portal.portal_preferences.getPreference(
'preferred_category_child_item_list_method_id',
'getCategoryChildCompactLogicalPathItemList')
# If this category is defined on budget level, only show subcategories.
budget = budget_line.getParentValue()
if base_category in budget.getVariationBaseCategoryList():
for budget_variation_category in budget.getVariationCategoryList():
if budget_variation_category.split('/')[0] == base_category:
base_category = budget_variation_category
break
return getattr(portal.portal_categories.unrestrictedTraverse(base_category),
item_list_method)(
base=1,
local_sort_id=('int_index',
'translated_title'),
checked_permission='View')
def initializeBudgetLine(self, budget_line):
"""Initialize a budget line
"""
budget_line_variation_category_list =\
list(budget_line.getVariationBaseCategoryList() or [])
for variation_base_category in \
self.getProperty('variation_base_category_list'):
if variation_base_category not in budget_line_variation_category_list:
budget_line_variation_category_list.append(variation_base_category)
budget_line.setVariationBaseCategoryList(
base_category = self.getProperty('variation_base_category')
if base_category:
budget_line_variation_category_list.append(base_category)
budget_line.setVariationBaseCategoryList(
budget_line_variation_category_list)
def initializeBudget(self, budget):
"""Initialize a budget.
"""
# same as budget line
return self.initializeBudgetLine(budget)
......@@ -34,6 +34,9 @@ from Products.ERP5.Document.BudgetVariation import BudgetVariation
class NodeBudgetVariation(BudgetVariation):
""" A budget variation for node
A script will return the list of possible nodes, or they will be configured
explicitly on the budget variation.
"""
# Default Properties
property_sheets = ( PropertySheet.Base
......@@ -42,6 +45,7 @@ class NodeBudgetVariation(BudgetVariation):
, PropertySheet.SortIndex
, PropertySheet.Path
, PropertySheet.Predicate
, PropertySheet.BudgetVariation
)
# CMF Type Definition
......@@ -67,7 +71,8 @@ class NodeBudgetVariation(BudgetVariation):
node_select_method_id = self.getProperty('node_select_method_id')
if node_select_method_id:
return guarded_getattr(context, node_select_method_id)()
return ()
# no script defined, used the explicitly selected values
return self.getAggregateValueList()
def _getNodeTitle(self, node):
"""Returns the title of a node
......@@ -94,18 +99,27 @@ class NodeBudgetVariation(BudgetVariation):
def getInventoryQueryDict(self, budget_cell):
""" Query dict to pass to simulation query
"""
if not self.getInventoryAxis():
return dict()
base_category = self.getProperty('variation_base_category')
if not base_category:
return dict()
# TODO: pass base_category_list instead of stupidly iterating !
for criterion_category in budget_cell.getMembershipCriterionCategoryList():
if '/' not in criterion_category: # safe ...
continue
criterion_base_category, node_url = criterion_category.split('/', 1)
if criterion_base_category == base_category:
return dict(
node_uid=self.getPortalObject().unrestrictedTraverse(node_url).getUid())
axis = self.getInventoryAxis()
if axis == 'movement':
axis = 'default_%s' % base_category
axis = '%s_uid' % axis
return {axis:
self.getPortalObject().unrestrictedTraverse(node_url).getUid()}
return dict()
def getBudgetLineVariationRangeCategoryList(self, budget_line):
"""Returns the Variation Range Category List that can be applied to this
budget line.
......@@ -117,6 +131,17 @@ class NodeBudgetVariation(BudgetVariation):
return [(self._getNodeTitle(node), '%s%s' % (prefix, node.getRelativeUrl()))
for node in self._getNodeList(budget_line)]
def getBudgetVariationRangeCategoryList(self, budget):
"""Returns the Variation Range Category Listhat can be applied to this
budget.
"""
base_category = self.getProperty('variation_base_category')
prefix = ''
if base_category:
prefix = '%s/' % base_category
return [(self._getNodeTitle(node), '%s%s' % (prefix, node.getRelativeUrl()))
for node in self._getNodeList(budget)]
def initializeBudgetLine(self, budget_line):
"""Initialize a budget line
"""
......@@ -128,3 +153,10 @@ class NodeBudgetVariation(BudgetVariation):
budget_line.setVariationBaseCategoryList(
budget_line_variation_category_list)
def initializeBudget(self, budget):
"""Initialize a budget.
"""
# same as budget line
return self.initializeBudgetLine(budget)
##############################################################################
#
# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
# Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
......@@ -30,4 +30,4 @@ class BudgetVariation:
Budget variation properties
"""
_categories = ( 'budget_variation', )
_categories = ( 'inventory_axis', 'budget_variation')
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