[Erp5-report] r43576 jm - in /erp5/trunk/products/ERP5: Document/ mixin/ tests/
nobody at svn.erp5.org
nobody at svn.erp5.org
Tue Feb 22 19:41:53 CET 2011
Author: jm
Date: Tue Feb 22 19:41:53 2011
New Revision: 43576
URL: http://svn.erp5.org?rev=43576&view=rev
Log:
amount_generator: add support for variation
Modified:
erp5/trunk/products/ERP5/Document/AmountGeneratorLine.py
erp5/trunk/products/ERP5/mixin/amount_generator.py
erp5/trunk/products/ERP5/tests/testBPMCore.py
erp5/trunk/products/ERP5/tests/testTradeModelLine.py
Modified: erp5/trunk/products/ERP5/Document/AmountGeneratorLine.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/Document/AmountGeneratorLine.py?rev=43576&r1=43575&r2=43576&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/Document/AmountGeneratorLine.py [utf8] (original)
+++ erp5/trunk/products/ERP5/Document/AmountGeneratorLine.py [utf8] Tue Feb 22 19:41:53 2011
@@ -56,17 +56,25 @@ class AmountGeneratorLine(MappedValue, X
'getCellAggregateKey')
def getCellAggregateKey(self):
"""Define a key in order to aggregate amounts at cell level"""
- return (self.getResource(),
- self.getVariationText()) # Variation UID, Hash ?
+ resource = self.getResource()
+ if resource:
+ return (resource, self.getVariationText()) # Variation UID, Hash ?
+ # For a pure intermediate line, we need another way to prevent merging:
+ # do not merge if base_application or base_contribution is variated.
+ return frozenset(self.getBaseApplicationList() +
+ self.getBaseContributionList())
security.declareProtected(Permissions.AccessContentsInformation,
'getBaseAmountQuantity')
@classmethod
- def getBaseAmountQuantity(cls, delivery_amount, base_application, rounding):
+ def getBaseAmountQuantity(cls, delivery_amount, base_application,
+ variation_category_list=(), **kw):
"""Default method to compute quantity for the given base_application"""
- value = delivery_amount.getGeneratedAmountQuantity(base_application)
+ value = delivery_amount.getGeneratedAmountQuantity(
+ base_application, variation_category_list)
delivery_amount = delivery_amount.getObject()
if base_application in delivery_amount.getBaseContributionList():
+ assert not variation_category_list
value += cls._getBaseAmountQuantity(delivery_amount)
return value
Modified: erp5/trunk/products/ERP5/mixin/amount_generator.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/mixin/amount_generator.py?rev=43576&r1=43575&r2=43576&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/mixin/amount_generator.py [utf8] (original)
+++ erp5/trunk/products/ERP5/mixin/amount_generator.py [utf8] Tue Feb 22 19:41:53 2011
@@ -70,24 +70,29 @@ class BaseAmountDict(Implicit):
yield amount
yield self
- def contribute(self, base_amount, value):
- if base_amount in self._frozen:
+ def contribute(self, base_amount, variation_category_list, value):
+ variated_base_amount = base_amount, variation_category_list
+ if variated_base_amount in self._frozen:
+ if variation_category_list:
+ base_amount = (base_amount,) + variation_category_list
raise ValueError("Can not contribute to %r because this base_amount is"
" already applied. Order of Amount Generator Lines is"
- " wrong." % base_amount)
- self._dict[base_amount] = self._getQuantity(base_amount) + value
+ " wrong." % (base_amount,))
+ self._dict[variated_base_amount] = \
+ self._getQuantity(variated_base_amount) + value
- def _getQuantity(self, base_amount):
+ def _getQuantity(self, variated_base_amount):
"""Get intermediate computed quantity for given base_application"""
try:
- return self._dict[base_amount]
+ return self._dict[variated_base_amount]
except KeyError:
value = 0
amount_generator_line = self._amount_generator_line
for base_amount_dict in self._amount_list:
base_amount_dict._amount_generator_line = amount_generator_line
- value += base_amount_dict.getGeneratedAmountQuantity(base_amount)
- self._dict[base_amount] = value
+ value += base_amount_dict.getGeneratedAmountQuantity(
+ *variated_base_amount)
+ self._dict[variated_base_amount] = value
return value
getBaseAmountList__roles__ = None # public
@@ -100,7 +105,7 @@ class BaseAmountDict(Implicit):
return list(self._amount_list)
getGeneratedAmountQuantity__roles__ = None # public
- def getGeneratedAmountQuantity(self, base_amount):
+ def getGeneratedAmountQuantity(self, base_amount, variation_category_list=()):
"""Get final computed quantity for given base_amount
Note: During a call to getQuantity, this method may be called again by
@@ -108,9 +113,10 @@ class BaseAmountDict(Implicit):
In this case, the returned value is the last intermediate value just
before finalization.
"""
- if base_amount in self._frozen:
- return self._getQuantity(base_amount)
- self._frozen.add(base_amount)
+ variated_base_amount = base_amount, variation_category_list
+ if variated_base_amount in self._frozen:
+ return self._getQuantity(variated_base_amount)
+ self._frozen.add(variated_base_amount)
try:
method = self._cache[base_amount]
except KeyError:
@@ -121,8 +127,13 @@ class BaseAmountDict(Implicit):
if method is None:
method = self._amount_generator_line.getBaseAmountQuantity
self._cache[base_amount] = method
- value = method(self, base_amount, **self._method_kw)
- self._dict[base_amount] = value
+ if variation_category_list:
+ kw = dict(self._method_kw,
+ variation_category_list=variation_category_list)
+ else:
+ kw = self._method_kw
+ value = method(self, base_amount, **kw)
+ self._dict[variated_base_amount] = value
return value
@@ -218,6 +229,8 @@ class AmountGeneratorMixin:
portal_type=amount_generator_cell_type_list)
cell_aggregate = {} # aggregates final line information
+ base_application_list = self.getBaseApplicationList()
+ base_contribution_list = self.getBaseContributionList()
for cell in amount_generator_cell_list:
if not cell.test(delivery_amount):
if cell is self:
@@ -228,8 +241,8 @@ class AmountGeneratorMixin:
property_dict = cell_aggregate[key]
except KeyError:
cell_aggregate[key] = property_dict = {
- 'base_application_set': set(),
- 'base_contribution_set': set(),
+ 'base_application_set': set(base_application_list),
+ 'base_contribution_set': set(base_contribution_list),
'category_list': [],
'causality_value_list': [],
'efficiency': self.getEfficiency(),
@@ -250,11 +263,11 @@ class AmountGeneratorMixin:
cell.getMappedValueBaseCategoryList(), base=1)
property_dict['category_list'] += category_list
property_dict['resource'] = cell.getResource()
- # For final amounts, base_application and id MUST be defined
- property_dict['base_application_set'].update(
+ if cell is not self:
+ # cells inherit base_application and base_contribution from line
+ property_dict['base_application_set'].update(
cell.getBaseApplicationList())
- # For intermediate calculations, base_contribution_list MUST be defined
- property_dict['base_contribution_set'].update(
+ property_dict['base_contribution_set'].update(
cell.getBaseContributionList())
property_dict['causality_value_list'].append(cell)
@@ -271,6 +284,12 @@ class AmountGeneratorMixin:
if causality_value is self and len(cell_aggregate) > 1:
continue
base_application_set = property_dict['base_application_set']
+ # allow a single base_application to be variated
+ variation_category_list = tuple(sorted([x for x in base_application_set
+ if x[:12] != 'base_amount/']))
+ if variation_category_list:
+ base_application_set.difference_update(variation_category_list)
+ assert len(base_application_set) == 1
# property_dict may include
# resource - VAT service or a Component in MRP
# (if unset, the amount will only be used for reporting)
@@ -286,8 +305,9 @@ class AmountGeneratorMixin:
# for future simulation of efficiencies.
# If no quantity is provided, we consider that the value is 1.0
# (XXX is it OK ?) XXX-JPS Need careful review with taxes
- quantity = float(sum(map(base_amount.getGeneratedAmountQuantity,
- base_application_set)))
+ quantity = float(sum(base_amount.getGeneratedAmountQuantity(
+ base_application, variation_category_list)
+ for base_application in base_application_set))
for key in 'quantity', 'price', 'efficiency':
if property_dict.get(key, 0) in (None, ''):
del property_dict[key]
@@ -324,8 +344,16 @@ class AmountGeneratorMixin:
quantity /= property_dict.get('efficiency', 1)
except ZeroDivisionError:
quantity *= float('inf')
- for base_contribution in property_dict['base_contribution_set']:
- base_amount.contribute(base_contribution, quantity)
+ base_contribution_set = property_dict['base_contribution_set']
+ # allow a single base_contribution to be variated
+ variation_category_list = tuple(sorted([x for x in base_contribution_set
+ if x[:12] != 'base_amount/']))
+ if variation_category_list:
+ base_contribution_set.difference_update(variation_category_list)
+ assert len(base_contribution_set) == 1
+ for base_contribution in base_contribution_set:
+ base_amount.contribute(base_contribution, variation_category_list,
+ quantity)
is_mapped_value = isinstance(self, MappedValue)
Modified: erp5/trunk/products/ERP5/tests/testBPMCore.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/tests/testBPMCore.py?rev=43576&r1=43575&r2=43576&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/tests/testBPMCore.py [utf8] (original)
+++ erp5/trunk/products/ERP5/tests/testBPMCore.py [utf8] Tue Feb 22 19:41:53 2011
@@ -56,8 +56,8 @@ class TestBPMMixin(ERP5TypeTestCase):
def createCategoriesInCategory(self, category, category_id_list):
for category_id in category_id_list:
if not category.hasObject(category_id):
- category.newContent(portal_type='Category', id = category_id,
- title = category_id)
+ category.newContent(category_id,
+ title=category_id.replace('_', ' ').title())
@reindex
def createCategories(self):
@@ -73,6 +73,10 @@ class TestBPMMixin(ERP5TypeTestCase):
self.createCategoriesInCategory(category_tool.trade_state,
['ordered', 'invoiced', 'delivered', 'taxed',
'state_a', 'state_b', 'state_c', 'state_d', 'state_e'])
+ self.createCategoriesInCategory(category_tool, ('tax_range', 'tax_share'))
+ self.createCategoriesInCategory(category_tool.tax_range,
+ ('0_200', '200_inf'))
+ self.createCategoriesInCategory(category_tool.tax_share, 'AB')
@reindex
def createBusinessProcess(self, **kw):
Modified: erp5/trunk/products/ERP5/tests/testTradeModelLine.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/tests/testTradeModelLine.py?rev=43576&r1=43575&r2=43576&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/tests/testTradeModelLine.py [utf8] (original)
+++ erp5/trunk/products/ERP5/tests/testTradeModelLine.py [utf8] Tue Feb 22 19:41:53 2011
@@ -37,7 +37,7 @@ from Products.ERP5.tests.testBPMCore imp
from Products.ERP5Type.Base import Base
from Products.ERP5Type.Utils import simple_decorator
from DateTime import DateTime
-from Products.ERP5Type.tests.utils import createZODBPythonScript
+from Products.ERP5Type.tests.utils import createZODBPythonScript, updateCellList
def save_result_as(name):
@@ -774,6 +774,66 @@ class TestTradeModelLine(TestTradeModelL
self.checkAggregatedAmountList(order)
+ def test_03_VariatedModelLine(self):
+ base_amount = self.setBaseAmountQuantityMethod('tax', """\
+def getBaseAmountQuantity(delivery_amount, base_application,
+ variation_category_list=(), **kw):
+ if variation_category_list:
+ quantity = delivery_amount.getGeneratedAmountQuantity(base_application)
+ tax_range, = variation_category_list
+ if tax_range == 'tax_range/0_200':
+ return min(quantity, 200)
+ else:
+ assert tax_range == 'tax_range/200_inf'
+ return max(0, quantity - 200)
+ return context.getBaseAmountQuantity(delivery_amount, base_application, **kw)
+return getBaseAmountQuantity""")
+ business_process = self.createBusinessProcess()
+ trade_condition = self.createTradeCondition(business_process, (
+ dict(price=0.3,
+ base_application=base_amount,
+ reference='tax1',
+ int_index=10),
+ dict(base_application=base_amount,
+ base_contribution='base_amount/total_tax',
+ reference='tax2',
+ int_index=20),
+ dict(base_application='base_amount/total_tax',
+ base_contribution='base_amount/total',
+ reference='tax3',
+ int_index=30),
+ ))
+ def createCells(line, matrix, base_application=(), base_contribution=()):
+ range_list = [set() for x in iter(matrix).next()]
+ for index in matrix:
+ for x, y in zip(range_list, index):
+ x.add(y)
+ line.setCellRange(*range_list)
+ for index, price in matrix.iteritems():
+ line.newCell(mapped_value_property='price', price=price,
+ base_application_list=[index[i] for i in base_application],
+ base_contribution_list=[index[i] for i in base_contribution],
+ *index)
+ createCells(self['trade_model_line/tax2'], {
+ ('tax_range/0_200', 'tax_share/A'): .1,
+ ('tax_range/0_200', 'tax_share/B'): .2,
+ ('tax_range/200_inf', 'tax_share/A'): .3,
+ ('tax_range/200_inf', 'tax_share/B'): .4,
+ }, base_application=(0,), base_contribution=(1,))
+ createCells(self['trade_model_line/tax3'], {
+ ('tax_share/A',): .5,
+ ('tax_share/B',): .6,
+ }, base_application=(0,))
+ from Products.ERP5Type.Document import newTempAmount
+ for x in ((100, 30, 10, 20, 5, 12),
+ (500, 150, 20, 90, 40, 120, 55, 96)):
+ amount = newTempAmount(self.portal, '_',
+ quantity=x[0], price=1,
+ base_contribution=base_amount)
+ amount_list = trade_condition.getGeneratedAmountList((amount,))
+ self.assertEqual(sorted(x[1:]),
+ sorted(y.getTotalPrice() for y in amount_list))
+
def test_tradeModelLineWithFixedPrice(self):
"""
Check it's possible to have fixed quantity on lines. Sometimes we want
More information about the Erp5-report
mailing list