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