[Erp5-report] r33187 jp - /erp5/trunk/products/ERP5/mixin/
nobody at svn.erp5.org
nobody at svn.erp5.org
Mon Mar 1 16:01:12 CET 2010
Author: jp
Date: Mon Mar 1 16:01:10 2010
New Revision: 33187
URL: http://svn.erp5.org?rev=33187&view=rev
Log:
First working version for transformations (using new transformation code which is not yet committed).
Added:
erp5/trunk/products/ERP5/mixin/variated.py
erp5/trunk/products/ERP5/mixin/variation.py
Modified:
erp5/trunk/products/ERP5/mixin/amount_generator.py
Modified: erp5/trunk/products/ERP5/mixin/amount_generator.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/mixin/amount_generator.py?rev=33187&r1=33186&r2=33187&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/mixin/amount_generator.py [utf8] (original)
+++ erp5/trunk/products/ERP5/mixin/amount_generator.py [utf8] Mon Mar 1 16:01:10 2010
@@ -27,6 +27,7 @@
##############################################################################
import zope.interface
+from zLOG import LOG
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, interfaces
from Products.ERP5.Document.Amount import Amount
@@ -34,8 +35,15 @@
class AmountGeneratorMixin:
"""
This class provides a generic implementation of IAmountGenerator.
-
- NOTE: this is an early prototype of implementation
+ It is used by Transformation, Trade Model, Paysheet, etc. It is
+ designed to support about any transformation process based
+ on IMappedValue interface. The key idea is that the Amount Generator
+ Lines and Cell provide either directly or through acquisition the
+ methods 'getMappedValuePropertyList' and 'getMappedValueBaseCategoryList'
+ to gather the properties and categories to copy from the model
+ to the generated amounts.
+
+ NOTE: this is an first prototype of implementation
"""
# Declarative security
@@ -47,27 +55,48 @@
def _getGlobalPropertyDict(self, context, amount_list=None, rounding=False):
"""
- This method can be overridden to define global
- properties involved in trade model line calculation
+ This method must be overridden to define global
+ properties involved in trade model line or transformation calculation
+
+ TODO:
+ default implementation could use type based method
"""
raise NotImplementedError
+ # Example of return value
return {
- 'delivery': 1,
- 'employee': 100,
+ 'delivery': 1, # Sets the base_amount 'delivery' to 1
+ # so that it is possible to create models based
+ # on the number of deliveries (instead of quantity)
+ 'employee': 100, # Sets the base_amount 'employee' to 100
+ # so that it is possible to create models based
+ # on the number of employee (instead of quantity)
}
def _getAmountPropertyDict(self, amount, amount_list=None, rounding=False):
"""
- This method can be overridden to define local
- properties involved in trade model line calculation
+ This method must be overridden to define per amount local
+ properties involved in trade model line or transformation calculation
+
+ TODO:
+ default implementation could use type based method
"""
raise NotImplementedError
+ # Example of return value
return dict(
price=amount.getPrice(),
+ # Sets the base_amount 'price' to the price
+ # This base_amount often uses another name though
quantity=amount.getQuantity(),
+ # Sets the base_amount 'quantity' to the quantity
+ # This base_amount often uses another name though
unit=(amount.getQuantityUnit() == 'unit') * amount.getQuantity(),
+ # Sets the base_amount 'unit' to the number of units
+ # so that it is possible to create models based
+ # on the number of units
ton=(amount.getQuantityUnit() == 'ton') * amount.getQuantity(),
- # more base applications could be set here
+ # Sets the base_amount 'ton' to the weight in tons
+ # so that it is possible to create models based
+ # on the weight in tons
)
security.declareProtected(Permissions.AccessContentsInformation, 'getGeneratedAmountList')
@@ -82,6 +111,10 @@
- is rounding really well supported (ie. before and after aggregation)
very likely not - proxying before or after must be decided
"""
+ # It is the only place we can import this
+ from Products.ERP5Type.Document import newTempAmount
+ portal = self.getPortalObject()
+
# Initialize base_amount global properties (which can be modified
# during the calculation process)
base_amount = self._getGlobalPropertyDict(context, amount_list=amount_list, rounding=rounding)
@@ -104,13 +137,13 @@
# Each amount in amount_list creates a new amount to take into account
# We thus need to start with a loop on amount_list
- for amount in amount_list:
+ for delivery_amount in amount_list:
# Initialize base_amount with per amount properties
- amount_propert_dict = self._getAmountPropertyDict(amount, amount_list=amount_list, rounding=rounding)
+ amount_propert_dict = self._getAmountPropertyDict(delivery_amount, amount_list=amount_list, rounding=rounding)
base_amount.update(amount_propert_dict)
# Initialize base_amount with total_price for each amount applications
- for application in amount.getBaseApplicationList(): # Acquired from Resource
+ for application in delivery_amount.getBaseApplicationList(): # Acquired from Resource
base_amount[application] = amount.getTotalPrice()
# Browse recursively the trade model and accumulate
@@ -119,7 +152,7 @@
amount_generator_line_list = amount_generator_line.contentValues(portal_type=self.getPortalAmountGeneratorLineTypeList())
# Recursively feed base_amount
if len(amount_generator_line_list):
- amount_generator_line_list = amount_generator_line_list.sort(key=lambda x: x.getIntIndex())
+ amount_generator_line_list.sort(key=lambda x: x.getIntIndex())
for amount_generator_line in amount_generator_line_list:
accumulateAmountList(amount_generator_line)
return
@@ -133,21 +166,27 @@
resource_amount_aggregate = {} # aggregates final line information
value_amount_aggregate = {} # aggregates intermediate line information
for amount_generator_cell in amount_generator_cell_list:
- if amount_generator_cell.test(amount): # XXX-JPS getTargetLevel not supported
+ if amount_generator_cell.test(delivery_amount): # XXX-JPS getTargetLevel not supported
# Case 1: the cell defines a final amount of resource
- if amount_generator_cell.getResource():
+ if amount_generator_cell.getResource() and\
+ getattr(amount_generator_cell, 'getBaseApplication', None) is not None:
# We must aggregate per resource, variation
key = (amount_generator_cell.getResource(), amount_generator_cell.getVariationText()) # Variation UID, Hash ?
resource_amount_aggregate.setdefault(key, {})
# Then collect the mapped properties (resource, quantity, net_converted_quantity, base_contribution_list, base_application, etc.)
for property_key in amount_generator_cell.getMappedValuePropertyList():
- # Handling of property lists ? XXX?
+ # XXX-JPS Make sure handling of list properties can be handled
resource_amount_aggregate[key][property_key] = amount_generator_cell.getProperty(property_key)
resource_amount_aggregate[key]['category_list'] = amount_generator_cell.getCategoryMembershipList(
amount_generator_cell.getMappedValueBaseCategoryList())
+ resource_amount_aggregate[key]['resource'] = amount_generator_cell.getResource()
+ # For final amounts, base_application and id MUST be defined
+ resource_amount_aggregate[key]['base_application'] = amount_generator_cell.getBaseApplication() # Required
+ resource_amount_aggregate[key]['id'] = amount_generator_cell.getRelativeUrl().replace('/', '_')
# Case 2: the cell defines a temporary calculation line
- else:
+ elif getattr(amount_generator_cell, 'getBaseContributionList', None) is not None:
# We must aggregate per base_application
+ # Therefore, base_application MUST be defined
key = amount_generator_cell.getBaseApplication()
value_amount_aggregate.setdefault(key, {})
# Then collect the mapped properties
@@ -155,6 +194,8 @@
value_amount_aggregate[key][property_key] = amount_generator_cell.getProperty(property_key)
value_amount_aggregate[key]['category_list'] = amount_generator_cell.getCategoryMembershipList(
amount_generator_cell.getMappedValueBaseCategoryList())
+ # For intermediate calculations, base_contribution_list MUST be defined
+ value_amount_aggregate[key]['base_contribution_list'] = amount_generator_cell.getBaseContributionList() # Required
if resource_amount_aggregate:
for key, property_dict in resource_amount_aggregate.items():
resource, variation_text = key
@@ -164,6 +205,7 @@
else:
category_list = None
base_application = property_dict['base_application']
+ del property_dict['base_application']
# property_dict should include
# resource - VAT service or a Component in MRP
# quantity - quantity in component in MRP, (what else XXX)
@@ -177,12 +219,16 @@
# values converted to the default management unit
# If not quantity is provided, we consider that the value is 1.0 (XXX is it OK ?)
property_dict['quantity'] = base_amount[amount_generator_line.getBaseApplication()] * \
- (property_dict.get('net_converted_quantity', property_dict.get('quantity')), 1.0)
+ (property_dict.get('net_converted_quantity', property_dict.get('quantity', 1.0)))
+ # This sounds wrong if cell has getBaseApplication()
+
# We should not keep net_converted_quantity
if property_dict.get('net_converted_quantity', None) is not None:
del property_dict['net_converted_quantity']
# Create an Amount object
- amount = Amount() # XXX-JPS we could use a movement for safety
+ id = property_dict['id']
+ del property_dict['id']
+ amount = newTempAmount(portal, id) # XXX-JPS Could we use a movement for safety ?
if category_list: amount._setCategoryList(category_list)
amount._edit(**property_dict)
if rounding:
@@ -221,7 +267,11 @@
Implementation of a generic transformation algorith which is
applicable to payroll, tax generation and BOMs. Return the
list of amounts with aggregation.
+
+ TODO:
+ - make working sample code
"""
raise NotImplementedError
+ # Example of return code
result = self.getGeneratedAmountList(context, movement_list=movement_list, rounding=rounding)
return SomeMovementGroup(result)
Added: erp5/trunk/products/ERP5/mixin/variated.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/mixin/variated.py?rev=33187&view=auto
==============================================================================
--- erp5/trunk/products/ERP5/mixin/variated.py (added)
+++ erp5/trunk/products/ERP5/mixin/variated.py [utf8] Mon Mar 1 16:01:10 2010
@@ -1,0 +1,394 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
+# Jean-Paul Smets-Solanes <jp at nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+##############################################################################
+
+from AccessControl import ClassSecurityInfo
+from Products.ERP5Type.Globals import InitializeClass
+from Products.CMFCore.utils import getToolByName
+
+from Products.ERP5Type import Context, interfaces, Permissions
+from Products.ERP5Type.Base import Base
+from Products.CMFCategory.Renderer import Renderer
+
+from warnings import warn
+from zope.interface import implements
+
+class VariatedMixin(Base):
+ """
+ Variated is a mix-in class for all classes which implement
+ the Variated Interface.
+
+ A Variable object is an object which can variate
+ according to multiple dimensions. Variable objects include:
+
+ - a Resource instance
+
+ - an Amount instance (a Movement, a DeliveryLine, etc.)
+
+ - an Item
+
+ - a TransformedResource instance
+ """
+
+ # Declarative security
+ security = ClassSecurityInfo()
+
+ # Declarative interfaces
+ implements(interfaces.IVariated)
+
+ security.declareProtected(Permissions.AccessContentsInformation,
+ 'getVariationBaseCategoryList')
+ def getVariationBaseCategoryList(self, omit_optional_variation=0,
+ omit_option_base_category=None, omit_individual_variation=0):
+ """
+ Return the list of variation base category.
+ If omit_optional_variation==1, do not include base category
+ considered as option (ex: industrial_phase).
+ """
+ #XXX backwards compatibility
+ if omit_option_base_category is not None:
+ warn("Please use omit_optional_variation instead of"\
+ " omit_option_base_category.", DeprecationWarning)
+ omit_optional_variation = omit_option_base_category
+
+ vbcl = self._baseGetVariationBaseCategoryList()
+ if omit_optional_variation == 1:
+ # XXX First implementation
+ # option base category list is a portal method, until the creation
+ # of a good API.
+ option_base_category_list = self.getPortalOptionBaseCategoryList()
+ vbcl = [x for x in vbcl if x not in option_base_category_list]
+ else:
+ vbcl.extend(self.getOptionalVariationBaseCategoryList())
+
+ if omit_individual_variation == 0:
+ vbcl.extend(self.getIndividualVariationBaseCategoryList())
+
+ return vbcl
+
+ security.declareProtected(Permissions.AccessContentsInformation,
+ '_getVariationCategoryList')
+ def _getVariationCategoryList(self, base_category_list = ()):
+ if base_category_list is ():
+ base_category_list = self.getVariationBaseCategoryList()
+# base_category_list = self.getVariationRangeBaseCategoryList()
+ return self.getAcquiredCategoryMembershipList(base_category_list, base=1)
+
+ security.declareProtected(Permissions.AccessContentsInformation,
+ 'getVariationCategoryList')
+ def getVariationCategoryList(self, base_category_list=(),
+ omit_optional_variation=0, omit_option_base_category=None):
+ """
+ Returns the list of possible variations
+ """
+ #XXX backwards compatibility
+ if omit_option_base_category is not None:
+ warn("Please use omit_optional_variation instead of"\
+ " omit_option_base_category.", DeprecationWarning)
+ omit_optional_variation = omit_option_base_category
+
+ return self._getVariationCategoryList(
+ base_category_list=base_category_list)
+
+ security.declareProtected(Permissions.AccessContentsInformation,
+ 'getVariationCategoryItemList')
+ def getVariationCategoryItemList(self, base_category_list=(), base=1,
+ display_id='logical_path', display_base_category=1,
+ current_category=None, omit_optional_variation=0,
+ omit_option_base_category=None, **kw):
+ """
+ Returns the list of possible variations
+ """
+ #XXX backwards compatibility
+ if omit_option_base_category is not None:
+ warn("Please use omit_optional_variation instead of"\
+ " omit_option_base_category.", DeprecationWarning)
+ omit_optional_variation = omit_option_base_category
+
+ variation_category_item_list = []
+ if current_category is not None:
+ variation_category_item_list.append((current_category,current_category))
+
+ if base_category_list is ():
+ base_category_list = self.getVariationBaseCategoryList()
+ if omit_optional_variation == 1:
+ base_category_list = [x for x in base_category_list if x not in
+ self.getPortalOptionBaseCategoryList()]
+ # Prepare 2 rendering
+ portal_categories = self.portal_categories
+ for base_category in base_category_list:
+ variation_category_list = self._getVariationCategoryList(
+ base_category_list=[base_category])
+
+ category_list = []
+ object_list = []
+ for variation_category_path in variation_category_list:
+ try:
+ variation_category = portal_categories.resolveCategory(
+ variation_category_path)
+ var_cat_portal_type = variation_category.getPortalType()
+ except AttributeError:
+ variation_category_item_list.append((variation_category_path,
+ variation_category_path))
+ else:
+ if var_cat_portal_type != 'Category':
+ object_list.append(variation_category)
+ else:
+ category_list.append(variation_category)
+ # Render categories
+ variation_category_item_list.extend(Renderer(
+ display_base_category=display_base_category,
+ display_none_category=0, base=base,
+ current_category=current_category,
+ display_id=display_id, **kw).\
+ render(category_list))
+ # Render the others
+ variation_category_item_list.extend(Renderer(
+ base_category=base_category,
+ display_base_category=display_base_category,
+ display_none_category=0, base=base,
+ current_category=current_category,
+ display_id='title', **kw).\
+ render(object_list))
+ return variation_category_item_list
+
+ # XXX Is it used ?
+# def getVariationCategoryTitleOrIdItemList(self, base_category_list=(),
+# base=1, **kw):
+# """
+# Returns a list of tuples by parsing recursively all categories in a
+# given list of base categories. Uses getTitleOrId as method
+# """
+# return self.getVariationCategoryItemList(
+# display_id='title_or_id',
+# base_category_list=base_category_list, base=base, **kw)
+
+ security.declareProtected(Permissions.ModifyPortalContent,
+ '_setVariationCategoryList')
+ def _setVariationCategoryList(self, node_list, base_category_list=()):
+ if base_category_list is ():
+ base_category_list = self.getVariationBaseCategoryList()
+ self._setCategoryMembership(base_category_list,node_list,base=1)
+
+ security.declareProtected(Permissions.ModifyPortalContent,
+ 'setVariationCategoryList')
+ def setVariationCategoryList(self, node_list, base_category_list=()):
+ self._setVariationCategoryList(node_list,
+ base_category_list=base_category_list)
+ self.reindexObject()
+
+ # Range
+ security.declareProtected(Permissions.AccessContentsInformation,
+ 'getVariationRangeBaseCategoryList')
+ def getVariationRangeBaseCategoryList(self):
+ """
+ Returns possible variation base_category ids.
+ """
+ # Get a portal method which defines a list of
+ # variation base category
+ return self.getPortalVariationBaseCategoryList()
+
+ security.declareProtected(Permissions.AccessContentsInformation,
+ 'getVariationRangeBaseCategoryItemList')
+ def getVariationRangeBaseCategoryItemList(self, base=1,
+ display_id='getTitle',
+ current_category=None):
+ """
+ Returns possible variations of the resource
+ as a list of tuples (id, title). This is mostly
+ useful in ERP5Form instances to generate selection
+ menus.
+ """
+ return self.portal_categories.getItemList(
+ self.getVariationBaseCategoryList())
+
+ security.declareProtected(Permissions.AccessContentsInformation,
+ 'getVariationBaseCategoryItemList')
+ def getVariationBaseCategoryItemList(self, display_id='title_or_id',
+ omit_optional_variation=0, omit_option_base_category=None,
+ omit_individual_variation=0):
+ """
+ Returns base category of the resource
+ as a list of tuples (title, id). This is mostly
+ useful in ERP5Form instances to generate selection
+ menus.
+ """
+ #XXX backwards compatibility
+ if omit_option_base_category is not None:
+ warn("Please use omit_optional_variation instead of"\
+ " omit_option_base_category.", DeprecationWarning)
+ omit_optional_variation = omit_option_base_category
+
+ variation_base_category_list = self.getVariationBaseCategoryList(
+ omit_optional_variation=omit_optional_variation,
+ omit_individual_variation=omit_individual_variation)
+ result = []
+ for base_category in variation_base_category_list:
+ bc = self.portal_categories.resolveCategory(base_category)
+ result.extend(Renderer(display_base_category=0,
+ display_none_category=0, base=1,
+ display_id=display_id).render([bc]))
+ return result
+
+ # Methods for matrix UI widgets
+ # XXX FIXME Those method are depreciated.
+ # We now use _asCellRange scripts.
+ security.declareProtected(Permissions.AccessContentsInformation,
+ 'getLineVariationRangeCategoryItemList')
+ def getLineVariationRangeCategoryItemList(self):
+ """
+ Returns possible variations in line
+ """
+ try:
+ resource = self.getDefaultResourceValue()
+ except AttributeError:
+ resource = None
+ if resource is not None:
+ clist = resource.getVariationRangeCategoryItemList(
+ base_category_list=self.getVariationBaseCategoryLine(),
+ root=0)
+ else:
+ clist = [(None,None)]
+ return clist
+
+ security.declareProtected(Permissions.AccessContentsInformation,
+ 'getColumnVariationRangeCategoryItemList')
+ def getColumnVariationRangeCategoryItemList(self):
+ """
+ Returns possible variations in column
+ """
+ try:
+ resource = self.getDefaultResourceValue()
+ except AttributeError:
+ resource = None
+ if resource is not None:
+ clist = resource.getVariationRangeCategoryItemList(base_category_list =
+ self.getVariationBaseCategoryColumn(), root=0)
+ else:
+ clist = [(None,None)]
+ return clist
+
+ security.declareProtected(Permissions.AccessContentsInformation,
+ 'getTabVariationRangeCategoryItemList')
+ def getTabVariationRangeCategoryItemList(self):
+ """
+ Returns possible variations in tab
+ """
+ try:
+ resource = self.getDefaultResourceValue()
+ except AttributeError:
+ resource = None
+ if resource is not None:
+ clist = resource.getVariationRangeCategoryItemList(base_category_list =
+ self.getVariationBaseCategoryTabList(), root=0)
+ else:
+ clist = [(None,None)]
+ return clist
+
+ # Help
+ security.declareProtected(Permissions.AccessContentsInformation,
+ 'getMatrixVariationRangeBaseCategoryList')
+ def getMatrixVariationRangeBaseCategoryList(self):
+ """
+ Return base categories used in the matrix
+ """
+ line_bc= self.getVariationBaseCategoryLine()
+ column_bc = self.getVariationBaseCategoryColumn()
+ # We need to copy values first
+ tab_bc = list(self.getVariationBaseCategoryTabList())
+ result = tab_bc
+ if line_bc is not None and line_bc is not '':
+ result += [line_bc]
+ if column_bc is not None and column_bc is not '':
+ result += [column_bc]
+ return result
+
+ security.declareProtected(Permissions.AccessContentsInformation,
+ 'getVariationRangeCategoryItemList')
+ def getVariationRangeCategoryItemList(self, base_category_list=(), base=1,
+ root=1,
+ display_method_id='getCategoryChildLogicalPathItemList',
+ display_base_category=1,
+ current_category=None, **kw):
+ """
+ Returns possible variations
+ => [(display, value)]
+ """
+ result = []
+ if base_category_list is ():
+ base_category_list = self.getVariationBaseCategoryList()
+ elif type(base_category_list) is type('a'):
+ base_category_list = (base_category_list, )
+
+ traverse = getToolByName(self, 'portal_categories').unrestrictedTraverse
+ # Render categories
+ for base_category in base_category_list:
+ result += getattr(traverse(base_category), display_method_id)(
+ base=base,
+ display_base_category=display_base_category,
+ display_none_category=0, **kw)
+ # Return result
+ return result
+
+ security.declareProtected(Permissions.AccessContentsInformation,
+ 'getVariationRangeCategoryList')
+ def getVariationRangeCategoryList(self, base_category_list=(), base=1,
+ root=1, current_category=None,
+ omit_individual_variation=0):
+ """
+ Returns the range of acceptable categories
+ """
+ vrcil = self.getVariationRangeCategoryItemList(
+ base_category_list=base_category_list,
+ base=base, root=root,
+ current_category=current_category,
+ omit_individual_variation=omit_individual_variation)
+ # display is on left
+ return [x[1] for x in vrcil]
+
+ # Context related methods
+ security.declarePublic('newVariationValue')
+ def newVariationValue(self, context=None, REQUEST=None, **kw):
+ # PERFORMANCE ISSUE
+ from Products.ERP5.VariationValue import newVariationValue
+ if context is None:
+ return newVariationValue(REQUEST=REQUEST, **kw)
+ else:
+ return newVariationValue(context=context, REQUEST=REQUEST, **kw)
+
+ # Provide a string representation of variations
+ security.declarePublic('getVariationText')
+ def getVariationText(self):
+ """
+ Provide a string representation of variation
+ """
+ category_list = list(self.getVariationCategoryList())
+ category_list.sort()
+ return '\n'.join(category_list)
+
+InitializeClass(VariatedMixin)
Added: erp5/trunk/products/ERP5/mixin/variation.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/mixin/variation.py?rev=33187&view=auto
==============================================================================
--- erp5/trunk/products/ERP5/mixin/variation.py (added)
+++ erp5/trunk/products/ERP5/mixin/variation.py [utf8] Mon Mar 1 16:01:10 2010
@@ -1,0 +1,53 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+##############################################################################
+
+import zope.interface
+from AccessControl import ClassSecurityInfo
+from Products.ERP5Type import Permissions, interfaces
+from Products.ERP5.Document.Amount import Amount
+
+class VariationMixin:
+ """
+
+ Although MappedValue are supposed to be independent of any
+ design choice, we have to implement them as subclasses of
+ Amount in order to make sure they provide a complete
+ variation interface. In particular, we want to be able
+ to call getVariationValue / setVariationValue on a
+ MappedValue.
+ (XXX - NO MORE)
+
+ XXX - Amount should be remove from here
+
+
+ Interesting Idea: properties and categories of the mapped value
+ (not of the predicate) could be handled through additional matrix
+ dimensions rather than through ad-hoc definition.
+ """
+
+
More information about the Erp5-report
mailing list