Commit 85dca7cc authored by Julien Muchembled's avatar Julien Muchembled

trade: preliminary work by JPS to use new amount generator

git-svn-id: https://svn.erp5.org/repos/public/erp5/sandbox/amount_generator@34652 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent d0f89f1a
# -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved. # Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
...@@ -123,7 +124,7 @@ class InvoiceRuleMovementGenerator(MovementGeneratorMixin): ...@@ -123,7 +124,7 @@ class InvoiceRuleMovementGenerator(MovementGeneratorMixin):
existing_movement_list = context.objectValues() existing_movement_list = context.objectValues()
for movement in delivery.getMovementList( for movement in delivery.getMovementList(
portal_type=(delivery.getPortalInvoiceMovementTypeList() + \ portal_type=(delivery.getPortalInvoiceMovementTypeList() + \
delivery.getPortalTaxMovementTypeList())): delivery.getPortalTaxMovementTypeList())): # This is bad XXX-JPS - use use
simulation_movement = self._getDeliveryRelatedSimulationMovement(movement) simulation_movement = self._getDeliveryRelatedSimulationMovement(movement)
if simulation_movement is None or \ if simulation_movement is None or \
simulation_movement in existing_movement_list: simulation_movement in existing_movement_list:
......
...@@ -399,10 +399,6 @@ class Amount(Base, Variated): ...@@ -399,10 +399,6 @@ class Amount(Base, Variated):
duration = None duration = None
return duration return duration
def getPrice(self):
pass
security.declareProtected(Permissions.AccessContentsInformation, 'getTotalPrice') security.declareProtected(Permissions.AccessContentsInformation, 'getTotalPrice')
def getTotalPrice(self, **kw): def getTotalPrice(self, **kw):
""" """
...@@ -411,6 +407,8 @@ class Amount(Base, Variated): ...@@ -411,6 +407,8 @@ class Amount(Base, Variated):
Price is defined on Price is defined on
""" """
price = self.getPrice()
if not price:
price = self.getResourcePrice() price = self.getResourcePrice()
quantity = self.getNetConvertedQuantity() quantity = self.getNetConvertedQuantity()
if isinstance(price, (int, float)) and isinstance(quantity, (int, float)): if isinstance(price, (int, float)) and isinstance(quantity, (int, float)):
......
...@@ -77,8 +77,10 @@ class BusinessPath(Path, Predicate): ...@@ -77,8 +77,10 @@ class BusinessPath(Path, Predicate):
, PropertySheet.CategoryCore , PropertySheet.CategoryCore
, PropertySheet.DublinCore , PropertySheet.DublinCore
, PropertySheet.Folder , PropertySheet.Folder
, PropertySheet.Reference
, PropertySheet.Comment , PropertySheet.Comment
, PropertySheet.Arrow , PropertySheet.Arrow
, PropertySheet.Amount
, PropertySheet.Chain , PropertySheet.Chain
, PropertySheet.SortIndex , PropertySheet.SortIndex
, PropertySheet.BusinessPath , PropertySheet.BusinessPath
......
# -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved. # Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
...@@ -26,30 +27,17 @@ ...@@ -26,30 +27,17 @@
# #
############################################################################## ##############################################################################
import zope.interface
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5.Document.Predicate import Predicate from Products.ERP5.Document.Predicate import Predicate
from Products.ERP5.Document.Amount import Amount
class MappedValue(Predicate, Amount):
"""
A MappedValue allows to associate a value to a domain
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 - Amount should be remove from here
TRANSFORMATION_FIX = True
_MARKER = []
Interesting Idea: properties and categories of the mapped value class MappedValue(Predicate):
(not of the predicate) could be handled through additional matrix """
dimensions rather than through ad-hoc definition. A MappedValue allows to associate a value to a predicate
""" """
meta_type = 'ERP5 Mapped Value' meta_type = 'ERP5 Mapped Value'
portal_type = 'Mapped Value' portal_type = 'Mapped Value'
...@@ -66,6 +54,83 @@ class MappedValue(Predicate, Amount): ...@@ -66,6 +54,83 @@ class MappedValue(Predicate, Amount):
, PropertySheet.Predicate , PropertySheet.Predicate
, PropertySheet.MappedValue , PropertySheet.MappedValue
) )
# Declarative interfaces
zope.interface.implements(interfaces.IMappedValue,
)
security.declareProtected(Permissions.AccessContentsInformation, 'getMappedValueBaseCategoryList')
def getMappedValueBaseCategoryList(self, d=_MARKER):
if TRANSFORMATION_FIX:
# Fix Mapped Value Objects which forgot to define their Mapped Base Categories
if not self._baseGetMappedValueBaseCategoryList():
if self.getParentValue().getParentValue().getPortalType() == 'Transformation':
base_category_dict = {}
for category in self.getCategoryList():
# XXX-JPS additional test required to prevent taking too much ?
base_category_dict[category.split('/')[0]] = None
self._setMappedValueBaseCategoryList(base_category_dict.keys())
if d is _MARKER:
return self._baseGetMappedValueBaseCategoryList(d=d)
return self._baseGetMappedValueBaseCategoryList()
security.declareProtected( Permissions.AccessContentsInformation, 'getProperty' )
def getProperty(self, key, d=_MARKER, **kw):
"""
Use local property instead of calling (acquired) accessor
whenever key is provided by the mapped value.
TODO:
- handle list properties (key ends with _list)
- add unit tests
"""
if key in self.getMappedValuePropertyList():
result = getattr(self, key, _MARKER)
if result is not _MARKER:
return result
if d is _MARKER:
return Predicate.getProperty(self, key, **kw) # XXX-JPS I would prefer to use always getProperty
# Is there any reason to overload ?
return Predicate.getProperty(self, key, d=d, **kw)
def getPropertyList(self, key, d=None):
"""
Use local property instead of calling (acquired) accessor
whenever key is provided by the mapped value.
TODO:
- add unit tests
"""
if key in self.getMappedValuePropertyList():
result = getattr(self, key, _MARKER)
if result is not _MARKER:
return result
if d is None:
return Predicate.getPropertyList(self, key)
return Predicate.getPropertyList(self, key, d=d)
def _setProperty(self, key, value, type=None, **kw):
"""
Use local property instead of calling (acquired) accessor
whenever key is provided by the mapped value.
TODO:
- handle type
- add unit tests
"""
if key in self.getMappedValuePropertyList():
return setattr(self, key, value)
return Predicate._setProperty(self, key, value, type=type, **kw)
# Check is this method should also be overriden
#def _setPropValue(self, key, value, **kw):
def hasProperty(self, key):
"""
Use local property instead of calling (acquired) accessor
whenever key is provided by the mapped value.
"""
if key in self.getMappedValuePropertyList():
return getattr(self, key, _MARKER) is not _MARKER
return Predicate.hasProperty(self, key)
def _edit(self, **kw): def _edit(self, **kw):
# We must first prepare the mapped value before we do the edit # We must first prepare the mapped value before we do the edit
......
...@@ -31,23 +31,21 @@ ...@@ -31,23 +31,21 @@
############################################################################## ##############################################################################
from collections import deque from collections import deque
import zope.interface
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet, interfaces from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5.mixin.composition import _getEffectiveModel from Products.ERP5.mixin.composition import _getEffectiveModel
from Products.ERP5.Document.Transformation import Transformation from Products.ERP5.Document.Transformation import Transformation
from Products.ERP5.Document.Path import Path
from Products.ERP5.AggregatedAmountList import AggregatedAmountList from Products.ERP5.AggregatedAmountList import AggregatedAmountList
from Products.ZSQLCatalog.SQLCatalog import Query, ComplexQuery from Products.ZSQLCatalog.SQLCatalog import Query, ComplexQuery
from Products.ERP5.Document.MappedValue import MappedValue
import zope.interface from Products.ERP5.mixin.amount_generator import AmountGeneratorMixin
from Products.ERP5.mixin.variated import VariatedMixin
# XXX TODO : getTradeModelLineComposedList and findSpecialiseValueList should class TradeCondition(MappedValue, AmountGeneratorMixin, VariatedMixin):
# probably move to Transformation (better names should be used)
# XXX TODO: review naming of new methods
# XXX WARNING: current API naming may change although model should be stable.
class TradeCondition(Path, Transformation):
""" """
Trade Conditions are used to store the conditions (payment, logistic,...) Trade Conditions are used to store the conditions (payment, logistic,...)
which should be applied (and used in the orders) when two companies make which should be applied (and used in the orders) when two companies make
...@@ -80,45 +78,35 @@ class TradeCondition(Path, Transformation): ...@@ -80,45 +78,35 @@ class TradeCondition(Path, Transformation):
interfaces.IMovementGenerator, interfaces.IMovementGenerator,
interfaces.IMovementCollectionUpdater,) interfaces.IMovementCollectionUpdater,)
security.declareProtected(Permissions.AccessContentsInformation,
'updateAggregatedAmountList')
def updateAggregatedAmountList(self, context, movement_list=None, rounding=None, **kw):
existing_movement_list = context.getMovementList()
aggregated_amount_list = self.getAggregatedAmountList(context=context,
movement_list=movement_list, **kw)
modified_reference_list = []
# check if the existing movements are in aggregated movements
movement_to_delete_list = []
for movement in existing_movement_list:
keep_movement = False
# check if the movement is a generated one or entered by the user.
# If it has been entered by user, keep it.
if not movement.getBaseApplicationList():
continue
for amount in aggregated_amount_list:
# if movement is generated and if not exist, append to delete list
update_kw = {}
for p in self.edited_property_list:
update_kw[p] = amount.getProperty(p)
if movement.getProperty('reference') == update_kw['reference'] and\ # Mapped Value implementation
movement.getVariationCategoryList() == \ # Transformation itself provides no properties or categories
amount.getVariationCategoryList(): def getMappedValuePropertyList(self):
movement.edit(**update_kw) return ()
modified_reference_list.append(update_kw['reference'])
keep_movement = True
if not keep_movement: def getMappedValueBaseCategoryList(self):
movement_to_delete_list.append(movement) return ()
movement_to_add_list = AggregatedAmountList( # Amount Generator Mixin
[amount for amount in aggregated_amount_list if def _getGlobalPropertyDict(self, context, amount_list=None, rounding=False):
amount.getReference() not in modified_reference_list]) """
No global properties needed
"""
return {
'delivery_count' : 1, # Use a better category here if possible - XXX - System preference
}
return {'movement_to_delete_list' : movement_to_delete_list, def _getAmountPropertyDict(self, amount, amount_list=None, rounding=False):
'movement_to_add_list': movement_to_add_list} """
Produced amount quantity is needed to initialize transformation
"""
result = {
'quantity' : amount.getQuantity(), # Use a better category here if possible - XXX - System preference
# and possibly make it extensible
}
for category in amount.getBaseContributionList():
result[category] = amount.getTotalPrice()
return result
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'findEffectiveSpecialiseValueList') 'findEffectiveSpecialiseValueList')
...@@ -213,41 +201,12 @@ class TradeCondition(Path, Transformation): ...@@ -213,41 +201,12 @@ class TradeCondition(Path, Transformation):
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'getAggregatedAmountList') 'getAggregatedAmountList')
def getAggregatedAmountList(self, context, movement_list=None, def getAggregatedAmountList(self, context, amount_list=None,
force_create_line=False, **kw): force_create_line=False, **kw):
if movement_list is None: """
movement_list = [] XXX-JPS - TODO
result = AggregatedAmountList() """
return self.getGeneratedAmountList(context, amount_list=amount_list, **kw)
trade_model_line_composed_list = \
self.getTradeModelLineComposedList(context)
# trade_model_line_composed_list is sorted in good way to have
# simple algorithm
for model_line in trade_model_line_composed_list:
result.extend(model_line.getAggregatedAmountList(context,
movement_list=movement_list,
current_aggregated_amount_list=result,
**kw))
movement_list = result
# remove amounts that should not be created, or with "incorrect" references.
# XXX what are incorrect references ???
# getTradeModelLineComposedList should have removed duplicate reference
# in the model graph
# TODO: review this part
aggregated_amount_list = AggregatedAmountList()
for movement in movement_list:
movement_reference = movement.getReference()
if movement_reference is None:
raise ValueError('Reference on Trade Model Line is None. '
'Reference must be set.')
for model_line in trade_model_line_composed_list:
if model_line.getReference() == movement_reference and\
(force_create_line or model_line.isCreateLine()):
aggregated_amount_list.append(movement)
return aggregated_amount_list
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'getEffectiveModel') 'getEffectiveModel')
......
...@@ -33,14 +33,14 @@ from Products.CMFCore.utils import getToolByName ...@@ -33,14 +33,14 @@ from Products.CMFCore.utils import getToolByName
from Products.ERP5Type import Permissions, PropertySheet, interfaces from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Type.XMLMatrix import XMLMatrix from Products.ERP5Type.XMLMatrix import XMLMatrix
from Products.ERP5.Document.Amount import Amount from Products.ERP5.Document.Amount import Amount
from Products.ERP5.Document.Predicate import Predicate from Products.ERP5.Document.MappedValue import MappedValue
from Products.ERP5.AggregatedAmountList import AggregatedAmountList from Products.ERP5.AggregatedAmountList import AggregatedAmountList
from Products.ERP5.Document.TradeCondition import TradeCondition from Products.ERP5.Document.TradeCondition import TradeCondition
from Products.ERP5.PropertySheet.TradeModelLine import (TARGET_LEVEL_MOVEMENT, from Products.ERP5.PropertySheet.TradeModelLine import (TARGET_LEVEL_MOVEMENT,
TARGET_LEVEL_DELIVERY) TARGET_LEVEL_DELIVERY)
import zope.interface import zope.interface
class TradeModelLine(Predicate, XMLMatrix, Amount): class TradeModelLine(MappedValue, XMLMatrix, Amount):
"""Trade Model Line is a way to represent trade transformation for movements""" """Trade Model Line is a way to represent trade transformation for movements"""
meta_type = 'ERP5 Trade Model Line' meta_type = 'ERP5 Trade Model Line'
portal_type = 'Trade Model Line' portal_type = 'Trade Model Line'
...@@ -64,11 +64,35 @@ class TradeModelLine(Predicate, XMLMatrix, Amount): ...@@ -64,11 +64,35 @@ class TradeModelLine(Predicate, XMLMatrix, Amount):
, PropertySheet.TradeModelLine , PropertySheet.TradeModelLine
, PropertySheet.Reference , PropertySheet.Reference
, PropertySheet.Predicate , PropertySheet.Predicate
, PropertySheet.MappedValue
) )
### Mapped Value Definition
# Provide default mapped value properties and categories if
# not defined
def getMappedValuePropertyList(self):
"""
"""
result = self._baseGetMappedValuePropertyList()
if result:
return result
if self._baseGetQuantity(): # If quantity is defined, then tax works as transformed resource
return ('quantity', 'price', )
# Else tax provides only a ratio on amount
return ('price', 'efficiency')
def getMappedValueBaseCategoryList(self):
result = self._baseGetMappedValueBaseCategoryList()
if result:
return result
return ('base_contribution', 'trade_phase', )
#
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'getPrice') 'getPrice')
def getPrice(self): def getPrice(self):
"""
"""
return self._baseGetPrice() return self._baseGetPrice()
def updateAggregatedAmountList(self, context, **kw): def updateAggregatedAmountList(self, context, **kw):
......
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