[Erp5-report] r30865 - in /erp5/trunk/products/ERP5: ./ Document/ PropertySheet/ Tool/ inte...
nobody at svn.erp5.org
nobody at svn.erp5.org
Thu Nov 26 03:28:05 CET 2009
Author: yusei
Date: Thu Nov 26 03:28:04 2009
New Revision: 30865
URL: http://svn.erp5.org?rev=30865&view=rev
Log:
Add rounding system(tool and model). Insert rounding into TradeModelLine.getAggregatedAmountList.
Added:
erp5/trunk/products/ERP5/Document/RoundingModel.py
erp5/trunk/products/ERP5/Tool/RoundingTool.py
erp5/trunk/products/ERP5/interfaces/roundable.py
Modified:
erp5/trunk/products/ERP5/Document/TradeModelLine.py
erp5/trunk/products/ERP5/PropertySheet/RoundingModel.py
erp5/trunk/products/ERP5/__init__.py
erp5/trunk/products/ERP5/interfaces/rounding_tool.py
Added: erp5/trunk/products/ERP5/Document/RoundingModel.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/Document/RoundingModel.py?rev=30865&view=auto
==============================================================================
--- erp5/trunk/products/ERP5/Document/RoundingModel.py (added)
+++ erp5/trunk/products/ERP5/Document/RoundingModel.py [utf8] Thu Nov 26 03:28:04 2009
@@ -1,0 +1,141 @@
+##############################################################################
+#
+# Copyright (c) 2009 Nexedi KK 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.
+#
+##############################################################################
+from AccessControl import ClassSecurityInfo
+from Products.ERP5Type import PropertySheet, Permissions
+from Products.ERP5.Document.Predicate import Predicate
+from Products.ERP5Type.Utils import UpperCase
+
+
+class RoundingModel(Predicate):
+ """
+ A Rounding Model class which defines rounding rule.
+ """
+ meta_type = 'ERP5 Rounding Model'
+ portal_type = 'Rounding Model'
+ add_permission = Permissions.AddPortalContent
+
+ security = ClassSecurityInfo()
+
+ property_sheets = (PropertySheet.Base,
+ PropertySheet.SimpleItem,
+ PropertySheet.XMLObject,
+ PropertySheet.CategoryCore,
+ PropertySheet.DublinCore,
+ PropertySheet.Predicate,
+ PropertySheet.SortIndex,
+ PropertySheet.RoundingModel,
+ )
+
+ security.declareProtected(Permissions.AccessContentsInformation, 'roundValue')
+ def roundValue(self, value):
+ """
+ Return rounded value.
+ """
+ if self.getRoundingMethodId() is not None:
+ rounding_method = getattr(self, 'RoundingModel_%s' % self.getRoundingMethodId(), None)
+ if rounding_method is None:
+ raise ValueError, 'Rounding method (%s) was not found.'
+ else:
+ from decimal import Decimal
+ from Products.ERP5.Tool.RoundingTool import ROUNDING_OPTION_DICT
+ decimal_rounding_option = self.getDecimalRoundingOption()
+ if (decimal_rounding_option is None or
+ decimal_rounding_option not in ROUNDING_OPTION_DICT):
+ raise ValueError, 'Decimal rounding option must be selected.'
+ def rounding_method(value, decimal_exponent):
+ return float(Decimal(str(value)).quantize(Decimal(decimal_exponent),
+ rounding=decimal_rounding_option))
+ return rounding_method(value, self.getDecimalExponent())
+
+ security.declareProtected(Permissions.AccessContentsInformation, 'getRoundingProxy')
+ def getRoundingProxy(self, document):
+ """
+ Return a rounding proxy object which getter methods returns rounded
+ value by following the rounding model definition.
+ """
+ rounding_model = self
+ rounded_property_getter_method_name_list = []
+
+ if isinstance(document, RoundingProxy):
+ temp_document = document._getOriginalDocument()
+ original_document = document
+ else:
+ from Products.ERP5Type import Document
+ if document.__class__.__name__ == 'TempDocument':
+ class_ = document.__class__.__bases__[0]
+ else:
+ class_ = document.__class__
+ constructor = getattr(Document, 'newTemp%s' % class_.__name__)
+ temp_document = constructor(document.getParentValue(), 'id')
+ temp_document.__dict__.update(document.__dict__)
+ original_document = temp_document
+
+ for property_id in rounding_model.getRoundedPropertyIdList():
+ getter_name = 'get%s' % UpperCase(property_id)
+ getter = getattr(temp_document,
+ getter_name, None)
+ setter_name = 'set%s' % UpperCase(property_id)
+ setter = getattr(temp_document,
+ setter_name, None)
+
+ if getter is not None and setter is not None:
+ # round the property value itself
+ setter(self.roundValue(getter()))
+ else:
+ # cannot round the property value so that the return value of getter
+ # will be rounded
+ rounded_property_getter_method_name_list.append(getter_name)
+
+ class _RoundingProxy(RoundingProxy):
+
+ def _getOriginalDocument(self):
+ if isinstance(original_document, RoundingProxy):
+ return original_document._editOriginalDocument()
+ else:
+ return original_document
+
+ def __getattr__(self, name):
+ attribute = getattr(original_document, name)
+ if getattr(attribute, 'DUMMY_ROUNDING_METHOD_MARK', None) is DUMMY_ROUNDING_METHOD_MARK:
+ return attribute
+ if name in rounded_property_getter_method_name_list:
+ def dummyMethod(*args, **kw):
+ return rounding_model.roundValue(attribute(*args, **kw))
+ dummyMethod.DUMMY_ROUNDING_METHOD_MARK = DUMMY_ROUNDING_METHOD_MARK
+ return dummyMethod
+ else:
+ return attribute
+ return _RoundingProxy()
+
+DUMMY_ROUNDING_METHOD_MARK = object()
+
+class RoundingProxy(object):
+ """Super class of _RoundingProxy class defined above. Use this class for
+ isinstance method to check if object is a real instance or a rounding proxy
+ instance.
+ """
Modified: erp5/trunk/products/ERP5/Document/TradeModelLine.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/Document/TradeModelLine.py?rev=30865&r1=30864&r2=30865&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/Document/TradeModelLine.py [utf8] (original)
+++ erp5/trunk/products/ERP5/Document/TradeModelLine.py [utf8] Thu Nov 26 03:28:04 2009
@@ -29,6 +29,7 @@
##############################################################################
from AccessControl import ClassSecurityInfo
+from Products.CMFCore.utils import getToolByName
from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Type.XMLMatrix import XMLMatrix
from Products.ERP5.Document.Amount import Amount
@@ -167,6 +168,14 @@
current_aggregated_amount_list=None,
base_id='movement', rounding=False, **kw):
from Products.ERP5Type.Document import newTempSimulationMovement
+
+ # Define rounding stuff
+ portal_roundings = getToolByName(self, 'portal_roundings', None)
+
+ # ROUNDING
+ if rounding:
+ movement_list = [portal_roundings.getRoundingProxy(movement, context=self)
+ for movement in movement_list]
aggregated_amount_list = AggregatedAmountList()
base_application_list = self.getBaseApplicationList()
@@ -247,6 +256,7 @@
update = 0
base_category_list = self.getVariationBaseCategoryList()
+
# get cells categories cartesian product
cell_key_list = self.getCellKeyList(base_id='movement')
if len(cell_key_list) > 0:
@@ -260,6 +270,17 @@
cell_coordinates))
tmp_movement = newTempSimulationMovement(self.getPortalObject(),
self_id)
+
+ # ROUNDING
+ if rounding:
+ # Once tmp_movement is replaced with the proxy, then the proxy
+ # object returns rounded value.
+ # For example, if rounding model is defined as
+ # rounded_property_id='total_price', then proxied
+ # tmp_movement.getTotalPrice() returns rounded result.
+ # If rounded_property_id='quantity', then
+ # tmp_movement.getQuantity() will be rounded.
+ tmp_movement = portal_roundings.getRoundingProxy(tmp_movement, context=self)
tmp_movement.edit(
variation_base_category_list = cell.getVariationBaseCategoryList(),
variation_category_list = cell.getVariationCategoryList(),
@@ -275,6 +296,12 @@
price = self.getPrice(),
**common_params
)
+
+ # ROUNDING
+ if rounding:
+ # Replace temporary movement with rounding proxy so that target
+ # property value will be rounded.
+ tmp_movement = portal_roundings.getRoundingProxy(tmp_movement, context=self)
tmp_movement_list.append(tmp_movement)
modified = 0
for tmp_movement in tmp_movement_list:
Modified: erp5/trunk/products/ERP5/PropertySheet/RoundingModel.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/PropertySheet/RoundingModel.py?rev=30865&r1=30864&r2=30865&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/PropertySheet/RoundingModel.py [utf8] (original)
+++ erp5/trunk/products/ERP5/PropertySheet/RoundingModel.py [utf8] Thu Nov 26 03:28:04 2009
@@ -42,7 +42,7 @@
},
{ 'id' : 'rounded_property_id',
'description' : 'The property name which value is rounded. Note that some property is virtual, like total_price.',
- 'type' : 'string',
+ 'type' : 'tokens',
'mode' : 'w',
'default' : None,
},
Added: erp5/trunk/products/ERP5/Tool/RoundingTool.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/Tool/RoundingTool.py?rev=30865&view=auto
==============================================================================
--- erp5/trunk/products/ERP5/Tool/RoundingTool.py (added)
+++ erp5/trunk/products/ERP5/Tool/RoundingTool.py [utf8] Thu Nov 26 03:28:04 2009
@@ -1,0 +1,115 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+#
+# Copyright (c) 2009 Nexedi KK and Contributors. All Rights Reserved.
+# Yusei TAHARA <yusei 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.
+#
+##############################################################################
+import zope.interface
+from AccessControl import ClassSecurityInfo
+from Products.ERP5Type.Tool.BaseTool import BaseTool
+from Products.ERP5.interfaces.rounding_tool import IRoundingTool
+from decimal import (ROUND_DOWN, ROUND_UP, ROUND_CEILING, ROUND_FLOOR,
+ ROUND_HALF_DOWN, ROUND_HALF_EVEN, ROUND_HALF_UP)
+
+ROUNDING_OPTION_DICT = {'ROUND_DOWN':ROUND_DOWN,
+ 'ROUND_UP':ROUND_UP,
+ 'ROUND_CEILING':ROUND_CEILING,
+ 'ROUND_FLOOR':ROUND_FLOOR,
+ 'ROUND_HALF_DOWN':ROUND_HALF_DOWN,
+ 'ROUND_HALF_EVEN':ROUND_HALF_EVEN,
+ 'ROUND_HALF_UP':ROUND_HALF_UP}
+
+class RoundingTool(BaseTool):
+ """Rounding Tool"""
+ id = 'portal_roundings'
+ title = 'Rounding Tool'
+ meta_type = 'ERP5 Rounding Tool'
+ portal_type = 'Rounding Tool'
+
+ zope.interface.implements(IRoundingTool)
+
+ security = ClassSecurityInfo()
+
+ security.declarePublic('findRoundingModel')
+ def findRoundingModelValueList(self, document, property_id=None, context=None):
+ """
+ Return a list of matched rounding models for `document` which is ordered
+ by increasing distance from `context`.
+ """
+ portal = self.getPortalObject()
+ parent_uid_list = [portal.portal_roundings.getUid()]
+ kw = {}
+
+ if context is not None:
+ current_document = context
+ while True:
+ if (current_document is None or current_document is portal or
+ not current_document.getUid() or
+ current_document.getUid() in parent_uid_list):
+ break
+ else:
+ parent_uid_list.append(current_document.getUid())
+ current_document = current_document.aq_parent
+
+ def sortMethod(document_a, document_b):
+ def score(document):
+ context_path = context.getPhysicalPath()
+ result = len(context_path)
+ for a, b in zip(context_path,
+ document.getPhysicalPath()):
+ if a==b:
+ result -= 1
+ else:
+ break
+ return result
+ return cmp(score(document_a), score(document_b))
+ kw['sort_method'] = sortMethod
+
+ result = portal.portal_domains.searchPredicateList(
+ context=document,
+ parent_uid=parent_uid_list,
+ portal_type='Rounding Model',
+ validation_state='validated',
+ **kw)
+ return result
+
+ security.declarePublic('getRoundingProxy')
+ def getRoundingProxy(self, document, context=None):
+ """
+ Return a rounding proxy object which getter methods returns rounded
+ value by following matched rounding model definition.
+ """
+ target_object = document
+ for rounding_model in self.findRoundingModelValueList(document, context=context):
+ target_object = rounding_model.getRoundingProxy(target_object)
+ return target_object
+
+ security.declarePublic('getDecimalRoundingOptionList')
+ def getDecimalRoundingOptionItemList(self):
+ """
+ Return the possible decimal rounding option item list which is provided
+ by python standard decimal module.
+ """
+ return ROUNDING_OPTION_DICT.items()
Modified: erp5/trunk/products/ERP5/__init__.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/__init__.py?rev=30865&r1=30864&r2=30865&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/__init__.py [utf8] (original)
+++ erp5/trunk/products/ERP5/__init__.py [utf8] Thu Nov 26 03:28:04 2009
@@ -49,7 +49,7 @@
TestTool, DomainTool, AlarmTool, OrderTool, DeliveryTool,\
TrashTool, ContributionTool, NotificationTool, PasswordTool,\
GadgetTool, ContributionRegistryTool, IntrospectionTool,\
- AcknowledgementTool, SolverTool, ConversionTool
+ AcknowledgementTool, SolverTool, ConversionTool, RoundingTool
import ERP5Site
object_classes = ( ERP5Site.ERP5Site,
)
@@ -73,6 +73,7 @@
AcknowledgementTool.AcknowledgementTool,
SolverTool.SolverTool,
ConversionTool.ConversionTool,
+ RoundingTool.RoundingTool,
)
content_classes = ()
content_constructors = ()
@@ -107,4 +108,4 @@
# backward compatibility names
XML = None
-UI = None
+UI = None
Added: erp5/trunk/products/ERP5/interfaces/roundable.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/interfaces/roundable.py?rev=30865&view=auto
==============================================================================
--- erp5/trunk/products/ERP5/interfaces/roundable.py (added)
+++ erp5/trunk/products/ERP5/interfaces/roundable.py [utf8] Thu Nov 26 03:28:04 2009
@@ -1,0 +1,40 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2009 Nexedi KK and Contributors. All Rights Reserved.
+# Yusei Tahara <yusei at nexedi.com>
+#
+# 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 advised 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 zope.interface import Interface
+
+class IRoundable(Interface):
+ """
+ Roundable interface
+ """
+
+ def asRoundingProxy(context=None):
+ """
+ Return a proxy object with getter methods which returns rounded value.
+ """
Modified: erp5/trunk/products/ERP5/interfaces/rounding_tool.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/interfaces/rounding_tool.py?rev=30865&r1=30864&r2=30865&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/interfaces/rounding_tool.py [utf8] (original)
+++ erp5/trunk/products/ERP5/interfaces/rounding_tool.py [utf8] Thu Nov 26 03:28:04 2009
@@ -34,9 +34,9 @@
Rounding tool interface
"""
- def findRoundingModel(document, property_id, context=None):
+ def findRoundingModelValueList(document, property_id=None, context=None):
"""
- Find matched rounding model for context and property id.
+ Find matched rounding models for context and property id.
Parameters:
@@ -44,18 +44,25 @@
This is the object which contains value to be rounded.
property_id
+ XXX I'm not quite sure if this is really necessary or not...
This indicates which property value is rounded.
context
- This indicates where lookup starts from. If this is None, then rounding tool itself
- is used.
+ This indicates where lookup starts from. If this is None, then rounding
+ tool itself is used.
Example:
- temporary_movement is generated by getAggregatedAmountList from a set of movements
- and represents total price with tax. trade_model_line contains a rounding model to
- be used here to round the total price with tax.
+ temporary_movement is generated by getAggregatedAmountList from a set of
+ movements and represents total price with tax. trade_model_line contains
+ a rounding model to be used here to round the total price with tax.
portal_roundings.findRoundingModel(temporary_movement, 'total_price',
context=trade_model_line)
"""
+
+ def getRoundingProxy(document, context=None):
+ """
+ Find matched rounding models from context and return proxy object for
+ `document`. The proxy object returns rounded value through getters.
+ """
More information about the Erp5-report
mailing list