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.
......@@ -123,7 +124,7 @@ class InvoiceRuleMovementGenerator(MovementGeneratorMixin):
existing_movement_list = context.objectValues()
for movement in delivery.getMovementList(
portal_type=(delivery.getPortalInvoiceMovementTypeList() + \
delivery.getPortalTaxMovementTypeList())):
delivery.getPortalTaxMovementTypeList())): # This is bad XXX-JPS - use use
simulation_movement = self._getDeliveryRelatedSimulationMovement(movement)
if simulation_movement is None or \
simulation_movement in existing_movement_list:
......
......@@ -399,10 +399,6 @@ class Amount(Base, Variated):
duration = None
return duration
def getPrice(self):
pass
security.declareProtected(Permissions.AccessContentsInformation, 'getTotalPrice')
def getTotalPrice(self, **kw):
"""
......@@ -411,7 +407,9 @@ class Amount(Base, Variated):
Price is defined on
"""
price = self.getResourcePrice()
price = self.getPrice()
if not price:
price = self.getResourcePrice()
quantity = self.getNetConvertedQuantity()
if isinstance(price, (int, float)) and isinstance(quantity, (int, float)):
return quantity * price
......
......@@ -77,8 +77,10 @@ class BusinessPath(Path, Predicate):
, PropertySheet.CategoryCore
, PropertySheet.DublinCore
, PropertySheet.Folder
, PropertySheet.Reference
, PropertySheet.Comment
, PropertySheet.Arrow
, PropertySheet.Amount
, PropertySheet.Chain
, PropertySheet.SortIndex
, PropertySheet.BusinessPath
......
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
......@@ -26,30 +27,17 @@
#
##############################################################################
import zope.interface
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5Type import Permissions, PropertySheet, interfaces
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.
TRANSFORMATION_FIX = True
_MARKER = []
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.
class MappedValue(Predicate):
"""
A MappedValue allows to associate a value to a predicate
"""
meta_type = 'ERP5 Mapped Value'
portal_type = 'Mapped Value'
......@@ -60,12 +48,89 @@ class MappedValue(Predicate, Amount):
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative properties
property_sheets = ( PropertySheet.Base
property_sheets = ( PropertySheet.Base
, PropertySheet.SimpleItem
, PropertySheet.CategoryCore
, PropertySheet.Predicate
, 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):
# We must first prepare the mapped value before we do the edit
......
......@@ -31,23 +31,21 @@
##############################################################################
from collections import deque
import zope.interface
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5.mixin.composition import _getEffectiveModel
from Products.ERP5.Document.Transformation import Transformation
from Products.ERP5.Document.Path import Path
from Products.ERP5.AggregatedAmountList import AggregatedAmountList
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
# 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):
class TradeCondition(MappedValue, AmountGeneratorMixin, VariatedMixin):
"""
Trade Conditions are used to store the conditions (payment, logistic,...)
which should be applied (and used in the orders) when two companies make
......@@ -80,45 +78,35 @@ class TradeCondition(Path, Transformation):
interfaces.IMovementGenerator,
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
# Mapped Value implementation
# Transformation itself provides no properties or categories
def getMappedValuePropertyList(self):
return ()
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)
def getMappedValueBaseCategoryList(self):
return ()
if movement.getProperty('reference') == update_kw['reference'] and\
movement.getVariationCategoryList() == \
amount.getVariationCategoryList():
movement.edit(**update_kw)
modified_reference_list.append(update_kw['reference'])
keep_movement = True
if not keep_movement:
movement_to_delete_list.append(movement)
movement_to_add_list = AggregatedAmountList(
[amount for amount in aggregated_amount_list if
amount.getReference() not in modified_reference_list])
# Amount Generator Mixin
def _getGlobalPropertyDict(self, context, amount_list=None, rounding=False):
"""
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,
'movement_to_add_list': movement_to_add_list}
def _getAmountPropertyDict(self, amount, amount_list=None, rounding=False):
"""
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,
'findEffectiveSpecialiseValueList')
......@@ -213,41 +201,12 @@ class TradeCondition(Path, Transformation):
security.declareProtected(Permissions.AccessContentsInformation,
'getAggregatedAmountList')
def getAggregatedAmountList(self, context, movement_list=None,
def getAggregatedAmountList(self, context, amount_list=None,
force_create_line=False, **kw):
if movement_list is None:
movement_list = []
result = AggregatedAmountList()
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
"""
XXX-JPS - TODO
"""
return self.getGeneratedAmountList(context, amount_list=amount_list, **kw)
security.declareProtected(Permissions.AccessContentsInformation,
'getEffectiveModel')
......
......@@ -33,14 +33,14 @@ 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
from Products.ERP5.Document.Predicate import Predicate
from Products.ERP5.Document.MappedValue import MappedValue
from Products.ERP5.AggregatedAmountList import AggregatedAmountList
from Products.ERP5.Document.TradeCondition import TradeCondition
from Products.ERP5.PropertySheet.TradeModelLine import (TARGET_LEVEL_MOVEMENT,
TARGET_LEVEL_DELIVERY)
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"""
meta_type = 'ERP5 Trade Model Line'
portal_type = 'Trade Model Line'
......@@ -64,11 +64,35 @@ class TradeModelLine(Predicate, XMLMatrix, Amount):
, PropertySheet.TradeModelLine
, PropertySheet.Reference
, 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,
'getPrice')
def getPrice(self):
"""
"""
return self._baseGetPrice()
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