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
No related merge requests found
......@@ -36,12 +36,12 @@ from Products.ERP5Type.Base import Base
#from Products.ERP5.Core import MetaNode, MetaResource
from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5.mixin.composition import CompositionMixin
from Products.ERP5.Document.Amount import Amount
from zLOG import LOG, WARNING
class Movement(XMLObject, Amount):
class Movement(XMLObject, Amount, CompositionMixin):
"""
The Movement class allows to implement ERP5 universal accounting model.
......
......@@ -2,6 +2,7 @@
##############################################################################
#
# 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
# programmers who take the whole responsibility of assessing all potential
......@@ -29,9 +30,12 @@
from AccessControl import ClassSecurityInfo
from Acquisition import aq_base
from Products.ERP5Type import Permissions
from Products.ERP5Type.Cache import transactional_cached
from Products.ERP5.Document.Predicate import Predicate
from Products.ZSQLCatalog.SQLCatalog import Query, ComplexQuery
@transactional_cached()
def _getEffectiveModel(self, start_date=None, stop_date=None):
"""Return the most appropriate model using effective_date, expiration_date
and version number.
......@@ -69,6 +73,72 @@ def _getEffectiveModel(self, start_date=None, stop_date=None):
sort_on=(('version', 'descending'),))
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:
"""
"""
......@@ -79,26 +149,10 @@ class CompositionMixin:
security.declareProtected(Permissions.AccessContentsInformation,
'asComposedDocument')
def asComposedDocument(self):
container_list = self._findEffectiveSpecialiseValueList()
self = self.asContext()
self._initBTrees()
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
asComposedDocument = transactional_cached()(_asComposedDocument)
# XXX add accessors to get properties from '_effective_model_list' ?
# (cf PaySheetModel)
def _findEffectiveSpecialiseValueList(self):
"""Return a list of effective specialised objects that is the
......@@ -118,9 +172,18 @@ class CompositionMixin:
while model_index < len(model_list):
model = model_list[model_index]
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:
model_set.add(model)
if 1: #model.test(self):
if 1: #model.test(self): # XXX
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
......@@ -2632,6 +2632,7 @@ return current_movement
# change tax trade model line to `movement` level
tax.edit(target_level=TARGET_LEVEL_MOVEMENT)
transaction.commit() # flush transactional cache
def getTotalAmount(amount_list):
result = 0
......
......@@ -2811,7 +2811,7 @@ class Base( CopyContainer,
if k != 'SESSION':
setattr(context, k, REQUEST[k])
# Define local properties
if kw is not None: context.__dict__.update(kw)
context.__dict__.update(kw)
# Make it a temp content
temp_object = TempBase(self.getId())
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