[Erp5-report] r34252 jm - in /erp5/trunk/products: ERP5/Document/ ERP5/mixin/ ERP5/tests/ E...

nobody at svn.erp5.org nobody at svn.erp5.org
Thu Apr 1 19:58:52 CEST 2010


Author: jm
Date: Thu Apr  1 19:58:52 2010
New Revision: 34252

URL: http://svn.erp5.org?rev=34252&view=rev
Log:
Make composition work for movements and use transactional cache

Modified:
    erp5/trunk/products/ERP5/Document/Movement.py
    erp5/trunk/products/ERP5/mixin/composition.py
    erp5/trunk/products/ERP5/tests/testTradeModelLine.py
    erp5/trunk/products/ERP5Type/Base.py

Modified: erp5/trunk/products/ERP5/Document/Movement.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/Document/Movement.py?rev=34252&r1=34251&r2=34252&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/Document/Movement.py [utf8] (original)
+++ erp5/trunk/products/ERP5/Document/Movement.py [utf8] Thu Apr  1 19:58:52 2010
@@ -36,12 +36,12 @@
 #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.
 

Modified: erp5/trunk/products/ERP5/mixin/composition.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/mixin/composition.py?rev=34252&r1=34251&r2=34252&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/mixin/composition.py [utf8] (original)
+++ erp5/trunk/products/ERP5/mixin/composition.py [utf8] Thu Apr  1 19:58:52 2010
@@ -2,6 +2,7 @@
 ##############################################################################
 #
 # Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
+#                    Julien Muchembled <jm at 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
 
+
+ at 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 @@
       sort_on=(('version', 'descending'),))
   return model_list[0].getObject()
 
+
+ at 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 @@
 
   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 @@
     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

Modified: erp5/trunk/products/ERP5/tests/testTradeModelLine.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/tests/testTradeModelLine.py?rev=34252&r1=34251&r2=34252&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/tests/testTradeModelLine.py [utf8] (original)
+++ erp5/trunk/products/ERP5/tests/testTradeModelLine.py [utf8] Thu Apr  1 19:58:52 2010
@@ -2632,6 +2632,7 @@
 
     # 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

Modified: erp5/trunk/products/ERP5Type/Base.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Base.py?rev=34252&r1=34251&r2=34252&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/Base.py [utf8] (original)
+++ erp5/trunk/products/ERP5Type/Base.py [utf8] Thu Apr  1 19:58:52 2010
@@ -2811,7 +2811,7 @@
           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', ):




More information about the Erp5-report mailing list