Commit dccd6b8e authored by Julien Muchembled's avatar Julien Muchembled

Make composition work for movements and use transactional cache

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@34252 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 6b08e40c
...@@ -36,12 +36,12 @@ from Products.ERP5Type.Base import Base ...@@ -36,12 +36,12 @@ from Products.ERP5Type.Base import Base
#from Products.ERP5.Core import MetaNode, MetaResource #from Products.ERP5.Core import MetaNode, MetaResource
from Products.ERP5Type.XMLObject import XMLObject from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5.mixin.composition import CompositionMixin
from Products.ERP5.Document.Amount import Amount from Products.ERP5.Document.Amount import Amount
from zLOG import LOG, WARNING from zLOG import LOG, WARNING
class Movement(XMLObject, Amount): class Movement(XMLObject, Amount, CompositionMixin):
""" """
The Movement class allows to implement ERP5 universal accounting model. The Movement class allows to implement ERP5 universal accounting model.
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
############################################################################## ##############################################################################
# #
# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved. # Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
# Julien Muchembled <jm@nexedi.com>
# #
# WARNING: This program as such is intended to be used by professional # WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential # programmers who take the whole responsibility of assessing all potential
...@@ -29,9 +30,12 @@ ...@@ -29,9 +30,12 @@
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Acquisition import aq_base from Acquisition import aq_base
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from Products.ERP5Type.Cache import transactional_cached
from Products.ERP5.Document.Predicate import Predicate from Products.ERP5.Document.Predicate import Predicate
from Products.ZSQLCatalog.SQLCatalog import Query, ComplexQuery from Products.ZSQLCatalog.SQLCatalog import Query, ComplexQuery
@transactional_cached()
def _getEffectiveModel(self, start_date=None, stop_date=None): def _getEffectiveModel(self, start_date=None, stop_date=None):
"""Return the most appropriate model using effective_date, expiration_date """Return the most appropriate model using effective_date, expiration_date
and version number. and version number.
...@@ -69,6 +73,72 @@ def _getEffectiveModel(self, start_date=None, stop_date=None): ...@@ -69,6 +73,72 @@ def _getEffectiveModel(self, start_date=None, stop_date=None):
sort_on=(('version', 'descending'),)) sort_on=(('version', 'descending'),))
return model_list[0].getObject() return model_list[0].getObject()
@transactional_cached()
def _findPredicateList(*container_list):
predicate_list = []
reference_dict = {}
line_count = 0
for container in container_list:
for ob in container.contentValues():
if isinstance(ob, Predicate):
# reference is used to hide lines on farther containers
reference = ob.getProperty('reference')
if reference:
reference_set = reference_dict.setdefault(ob.getPortalType(), set())
if reference in reference_set:
continue
reference_set.add(reference)
id = str(line_count)
line_count += 1
predicate_list.append(aq_base(ob.asContext(id=id)))
return predicate_list
class _asComposedDocument(object):
"""Return a temporary object which is the composition of all effective models
The returned value is a temporary copy of the given object. The list of all
effective models (specialise values) is stored in a private attribute.
Collecting predicates (from effective models) is done lazily. Predicates can
be accessed through standard Folder API (ex: contentValues).
"""
def __new__(cls, orig_self):
if '_effective_model_list' in orig_self.__dict__:
return orig_self # if asComposedDocument is called on a composed
# document after any access to its subobjects
self = orig_self.asContext()
self._initBTrees()
base_class = self.__class__
# this allows to intercept first access to '_folder_handler'
self.__class__ = type(base_class.__name__, (cls, base_class),
{'__module__': base_class.__module__})
self._effective_model_list = orig_self._findEffectiveSpecialiseValueList()
return self
def __init__(self, orig_self):
# __new__ does not call __init__ because returned object
# is wrapped in an acquisition context.
assert False
def asComposedDocument(self):
return self # if asComposedDocument is called on a composed
# document before any access to its subobjects
@property
def _folder_handler(self):
# restore the original class
# because we don't need to hook _folder_handler anymore
self.__class__ = self.__class__.__bases__[1]
# we filter out objects without any subobject to make the cache of
# '_findPredicateList' useful. Otherwise, the key would be always different
# (starting with 'orig_self').
for ob in _findPredicateList(*filter(None, self._effective_model_list)):
self._setOb(ob.id, ob)
return self._folder_handler
class CompositionMixin: class CompositionMixin:
""" """
""" """
...@@ -79,26 +149,10 @@ class CompositionMixin: ...@@ -79,26 +149,10 @@ class CompositionMixin:
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'asComposedDocument') 'asComposedDocument')
def asComposedDocument(self): asComposedDocument = transactional_cached()(_asComposedDocument)
container_list = self._findEffectiveSpecialiseValueList()
self = self.asContext() # XXX add accessors to get properties from '_effective_model_list' ?
self._initBTrees() # (cf PaySheetModel)
reference_dict = {}
line_count = 0
for container in container_list:
for ob in container.contentValues():
if isinstance(ob, Predicate):
# reference is used to hide lines on farther containers
reference = ob.getProperty('reference')
if reference:
reference_set = reference_dict.setdefault(ob.getPortalType(), set())
if reference in reference_set:
continue
reference_set.add(reference)
id = str(line_count)
line_count += 1
self._setOb(id, aq_base(ob.asContext(id=id)))
return self
def _findEffectiveSpecialiseValueList(self): def _findEffectiveSpecialiseValueList(self):
"""Return a list of effective specialised objects that is the """Return a list of effective specialised objects that is the
...@@ -118,9 +172,18 @@ class CompositionMixin: ...@@ -118,9 +172,18 @@ class CompositionMixin:
while model_index < len(model_list): while model_index < len(model_list):
model = model_list[model_index] model = model_list[model_index]
model_index += 1 model_index += 1
for model in map(getEffectiveModel, model.getSpecialiseValueList()): # we don't use getSpecialiseValueList to avoid acquisition on the parent
for model in map(getEffectiveModel, model.getValueList('specialise')):
if model not in model_set: if model not in model_set:
model_set.add(model) model_set.add(model)
if 1: #model.test(self): if 1: #model.test(self): # XXX
model_list.append(model) model_list.append(model)
try:
parent_asComposedDocument = self.getParentValue().asComposedDocument
except AttributeError:
pass
else:
model_list += [model
for model in parent_asComposedDocument()._effective_model_list
if model not in model_set]
return model_list return model_list
...@@ -2632,6 +2632,7 @@ return current_movement ...@@ -2632,6 +2632,7 @@ return current_movement
# change tax trade model line to `movement` level # change tax trade model line to `movement` level
tax.edit(target_level=TARGET_LEVEL_MOVEMENT) tax.edit(target_level=TARGET_LEVEL_MOVEMENT)
transaction.commit() # flush transactional cache
def getTotalAmount(amount_list): def getTotalAmount(amount_list):
result = 0 result = 0
......
...@@ -2811,7 +2811,7 @@ class Base( CopyContainer, ...@@ -2811,7 +2811,7 @@ class Base( CopyContainer,
if k != 'SESSION': if k != 'SESSION':
setattr(context, k, REQUEST[k]) setattr(context, k, REQUEST[k])
# Define local properties # Define local properties
if kw is not None: context.__dict__.update(kw) context.__dict__.update(kw)
# Make it a temp content # Make it a temp content
temp_object = TempBase(self.getId()) temp_object = TempBase(self.getId())
for k in ('isIndexable', 'reindexObject', 'recursiveReindexObject', 'activate', 'setUid', ): for k in ('isIndexable', 'reindexObject', 'recursiveReindexObject', 'activate', 'setUid', ):
......
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