From 5bfec5695270b40741020b2dbef0301e853f28f6 Mon Sep 17 00:00:00 2001
From: Julien Muchembled <jm@nexedi.com>
Date: Mon, 18 Oct 2010 14:12:01 +0000
Subject: [PATCH] Bugfixes to Transformation + new testLegacyTransformation

git-svn-id: https://svn.erp5.org/repos/public/erp5/sandbox/amount_generator@39282 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5/Document/Amount.py               |   3 +-
 product/ERP5/Document/Resource.py             |  15 +--
 product/ERP5/Document/SupplyLink.py           |   5 +-
 product/ERP5/Document/Transformation.py       | 115 +++++++-----------
 product/ERP5/Document/TransformedResource.py  |   4 +-
 product/ERP5/mixin/amount_generator.py        |   6 +-
 product/ERP5/mixin/variated.py                |   8 +-
 product/ERP5/tests/testProductionOrder.py     |  13 +-
 product/ERP5/tests/testTransformation.py      |  14 +--
 .../ERP5Legacy/Document/TransformationRule.py |   3 +-
 .../tests/testLegacyProductionOrder.py        |   3 +-
 .../tests/testLegacyTransformation.py         |  36 ++++++
 12 files changed, 105 insertions(+), 120 deletions(-)
 create mode 100644 product/ERP5Legacy/tests/testLegacyTransformation.py

diff --git a/product/ERP5/Document/Amount.py b/product/ERP5/Document/Amount.py
index 14d1607889..a922bca1a5 100644
--- a/product/ERP5/Document/Amount.py
+++ b/product/ERP5/Document/Amount.py
@@ -63,8 +63,7 @@ class Amount(Base, VariatedMixin):
   # Declarative interfaces
   zope.interface.implements(interfaces.IAmount)
 
-  property_sheets = ( PropertySheet.Base
-                    , PropertySheet.SimpleItem
+  property_sheets = ( PropertySheet.SimpleItem
                     , PropertySheet.Amount
                     , PropertySheet.Price
                     , PropertySheet.Reference
diff --git a/product/ERP5/Document/Resource.py b/product/ERP5/Document/Resource.py
index 8d8222b07a..82e56e6416 100644
--- a/product/ERP5/Document/Resource.py
+++ b/product/ERP5/Document/Resource.py
@@ -27,14 +27,14 @@
 #
 ##############################################################################
 
-import zope.interface
 from math import log
 from warnings import warn
 
 from AccessControl import ClassSecurityInfo
 
-from Products.ERP5Type import Permissions, PropertySheet, interfaces
+from Products.ERP5Type import Permissions, PropertySheet
 from Products.ERP5Type.XMLMatrix import XMLMatrix
+from Products.ERP5Type.XMLObject import XMLObject
 from Products.ERP5Type.Base import Base
 
 from Products.ERP5Type.Utils import cartesianProduct
@@ -44,7 +44,7 @@ from Products.CMFCore.utils import getToolByName
 
 from zLOG import LOG, WARNING
 
-class Resource(XMLMatrix, VariatedMixin):
+class Resource(XMLObject, XMLMatrix, VariatedMixin):
     """
       A Resource
     """
@@ -57,20 +57,13 @@ class Resource(XMLMatrix, VariatedMixin):
     security = ClassSecurityInfo()
     security.declareObjectProtected(Permissions.AccessContentsInformation)
 
-    # Declarative interfaces
-    zope.interface.implements( interfaces.IVariated, )
-
     # Declarative properties
-    property_sheets = ( PropertySheet.Base
-                      , PropertySheet.XMLObject
-                      , PropertySheet.CategoryCore
-                      , PropertySheet.DublinCore
+    property_sheets = ( PropertySheet.DublinCore
                       , PropertySheet.Price
                       , PropertySheet.Resource
                       , PropertySheet.Reference
                       , PropertySheet.Comment
                       , PropertySheet.FlowCapacity
-                      , PropertySheet.VariationRange
                       , PropertySheet.DefaultSupply
                       , PropertySheet.Aggregated
                       )
diff --git a/product/ERP5/Document/SupplyLink.py b/product/ERP5/Document/SupplyLink.py
index 425b76142a..1e79e37e0f 100644
--- a/product/ERP5/Document/SupplyLink.py
+++ b/product/ERP5/Document/SupplyLink.py
@@ -147,10 +147,9 @@ class SupplyLink(Path, XMLObject):
           rule = applied_rule.getSpecialiseValue()
           transformation = rule.getTransformation(movement)
           # Call getAggregatedAmountList
-          tmp_context = movement.getParentValue().getParentValue().asContext()
-          tmp_context.asComposedDocument = lambda *args: transformation
+          input_amount = applied_rule.getParentValue()
           resource_list = [x.getResourceValue()
-            for x in tmp_context.getAggregatedAmountList()
+            for x in transformation.getAggregatedAmountList((input_amount,))
             if x.getCausalityValue().getIndustrialPhase() in ind_phase_url_list]
           current_resource = movement.getResourceValue()
           if current_resource not in resource_list:
diff --git a/product/ERP5/Document/Transformation.py b/product/ERP5/Document/Transformation.py
index a9ed756c9b..623f954bc8 100644
--- a/product/ERP5/Document/Transformation.py
+++ b/product/ERP5/Document/Transformation.py
@@ -31,14 +31,12 @@
 #
 ##############################################################################
 from zLOG import LOG, WARNING
-
-import zope.interface
-
 from warnings import warn
 from AccessControl import ClassSecurityInfo
 
 from Products.CMFCategory.Renderer import Renderer
-from Products.ERP5Type import Permissions, PropertySheet, interfaces
+from Products.ERP5Type import Permissions, PropertySheet
+from Products.ERP5.Document.Amount import Amount
 from Products.ERP5.Document.MappedValue import MappedValue
 
 from Products.ERP5.mixin.amount_generator import AmountGeneratorMixin
@@ -46,7 +44,9 @@ from Products.ERP5.mixin.variated import VariatedMixin
 from Products.ERP5.mixin.composition import CompositionMixin
 from Products.ERP5Type.XMLObject import XMLObject
 
-class Transformation(MappedValue, AmountGeneratorMixin, VariatedMixin):
+# XXX Give priority to VariatedMixin (over Amount) due to conflicting
+#     implementations of getVariationBaseCategoryList
+class Transformation(MappedValue, VariatedMixin, Amount, AmountGeneratorMixin):
     """
       Build of material - contains a list of transformed resources
 
@@ -65,59 +65,47 @@ class Transformation(MappedValue, AmountGeneratorMixin, VariatedMixin):
     security.declareObjectProtected(Permissions.AccessContentsInformation)
 
     # Declarative properties
-    property_sheets = ( PropertySheet.Base
-                      , PropertySheet.XMLObject
-                      , PropertySheet.CategoryCore
-                      , PropertySheet.DublinCore
-                      , PropertySheet.VariationRange
-                      , PropertySheet.Predicate
-                      , PropertySheet.Comment
-                      , PropertySheet.Reference
+    property_sheets = ( PropertySheet.Comment
                       , PropertySheet.Version
                       #, PropertySheet.Resource
                       , PropertySheet.TransformedResource
-                      , PropertySheet.Path
                       , PropertySheet.Transformation
                       , PropertySheet.Order
                       , PropertySheet.Task
                       )
 
-    # Declarative interfaces
-    zope.interface.implements(interfaces.IVariated, 
-                              interfaces.IVariationRange,
-                              interfaces.IAmountGenerator
-                              )
+    def getAggregatedAmountList(self, *args, **kw):
+      """
+      """
+      getAggregatedAmountList = \
+        super(Transformation, self).getAggregatedAmountList
+      # Detect old use of getAggregatedAmountList
+      if 'context' in kw:
+        context = kw.pop('context')
+      else:
+        if not args or isinstance(args[0], (list, tuple)):
+          return getAggregatedAmountList(*args, **kw)
+        context, args = args[0], args[1:]
+      warn("The API of getAggregatedAmountList has changed:"
+           " it must be called on the context instead of passing"
+           " the context as first parameter", DeprecationWarning)
+      # XXX add a 'transformation_amount_generator' group type
+      kw['amount_generator_type_list'] = ('Transformation',
+                                          'Transformed Resource',
+                                          'Assorted Resource')
+      if context is not None:
+        context = (context,)
+      return getAggregatedAmountList(context, *args, **kw)
+
+    def getQuantity(self, default=None):
+      # Used for amount generation
+      # (Transformation is defined for 1 unit of target resource)
+      return 1
 
     # Predicate Value implementation
     #   asPredicate takes into account the resource
     # XXX-JPS not Impl.
 
-    def getCellAggregateKey(self, amount_generator_cell):
-      """Define a key in order to aggregate amounts at cell level
-
-        Transformed Resource (Transformation)
-          key must be None because:
-            - quantity and variation are defined in different cells so that the
-              user does not need to enter values depending on all axes
-            - amount_generator_cell.test should filter only 1 variant
-          current key = (acquired resource, acquired variation)
-
-        Assorted Resource (Transformation)
-          key = (assorted resource, assorted resource variation)
-          usually resource and quantity provided together
-
-        Payroll
-          key = (payroll resource, payroll resource variation)
-
-        Tax
-          key = (tax resource, tax resource variation)
-      """
-      if len(amount_generator_cell.contentValues()):
-        key = (amount_generator_cell.getRelativeUrl(),)
-      else:
-        key = (amount_generator_cell.getParentValue().getRelativeUrl(),)
-      return key
-
     # Mapped Value implementation
     #  Transformation itself provides no properties or categories
     def getMappedValuePropertyList(self):
@@ -126,34 +114,6 @@ class Transformation(MappedValue, AmountGeneratorMixin, VariatedMixin):
     def getMappedValueBaseCategoryList(self):
       return ()
 
-    # Amount Generator Mixin
-    def _getGlobalPropertyDict(self, context, amount_list=None, rounding=False):
-      """
-      No global properties needed
-      """
-      return {
-      }
-
-    def _getAmountPropertyDict(self, amount, amount_list=None, rounding=False):
-      """
-      Produced amount quantity is needed to initialize transformation
-      """
-      # XXX 1 is for compatibility
-      return {
-        'produced_quantity' : amount.getQuantity(1),
-      }
-
-    def getBaseApplication(self):
-      """
-      
-      """
-      # It is OK to try to acquire
-      if getattr(self, '_baseGetBaseApplication', None) is not None:
-        result = self._baseGetBaseApplication()
-        if result:
-          return result
-      return 'produced_quantity'
-
     # IVariationRange and IVariated Implementation
     security.declareProtected(Permissions.AccessContentsInformation,
                               'updateVariationCategoryList')
@@ -229,6 +189,15 @@ class Transformation(MappedValue, AmountGeneratorMixin, VariatedMixin):
                          base_category_list, base=1, display_none_category=0)
         return result
 
+    security.declareProtected(Permissions.AccessContentsInformation,
+                              'setVariationBaseCategoryList')
+    def setVariationBaseCategoryList(self, value):
+      """
+        Define the possible base categories and reindex object
+      """
+      self._setVariationBaseCategoryList(value)
+      self.reindexObject()
+
     security.declareProtected(Permissions.AccessContentsInformation,
                               'getVariationCategoryItemList')
     def getVariationCategoryItemList(self, base_category_list=(), base=1,
diff --git a/product/ERP5/Document/TransformedResource.py b/product/ERP5/Document/TransformedResource.py
index 21fb470db0..fd2e740e52 100644
--- a/product/ERP5/Document/TransformedResource.py
+++ b/product/ERP5/Document/TransformedResource.py
@@ -71,6 +71,8 @@ class TransformedResource(AmountGeneratorLine):
       if not result:
         if not self.hasCellContent(base_id='variation'):
           result = self.getVariationRangeBaseCategoryList() # The current resource variation
+        if 'trade_phase' not in result:
+          result.append('trade_phase')
       return result
 
     def getCellAggregateKey(self):
@@ -81,7 +83,7 @@ class TransformedResource(AmountGeneratorLine):
     def getBaseAmountQuantity(cls, delivery_amount, base_application, rounding):
       value = delivery_amount.getGeneratedAmountQuantity(base_application)
       if base_application == 'produced_quantity':
-        value += delivery_amount.getQuantity()
+        value += delivery_amount.getConvertedQuantity()
       return value
 
     def getBaseApplication(self):
diff --git a/product/ERP5/mixin/amount_generator.py b/product/ERP5/mixin/amount_generator.py
index b7249e866f..1ec604e678 100644
--- a/product/ERP5/mixin/amount_generator.py
+++ b/product/ERP5/mixin/amount_generator.py
@@ -174,8 +174,10 @@ class AmountGeneratorMixin:
             dict(rounding=rounding))
     # If amount_list is None, then try to collect amount_list from
     # the current context
+    default_target = None
     if amount_list is None:
       if self.providesIMovementCollection():
+        default_target = 'isMovement'
         base_amount_list = BaseAmountDict(*args).__of__(self) \
           .recurseMovementList(self.getMovementList())
       elif self.providesIAmount():
@@ -204,8 +206,8 @@ class AmountGeneratorMixin:
         return
       elif (self.getPortalType() not in amount_generator_line_type_list):
         return
-      if not getattr(delivery_amount, self.isTargetDelivery() and
-                                      'isDelivery' or 'isMovement')():
+      target_method = self.isTargetDelivery() and 'isDelivery' or default_target
+      if target_method and not getattr(delivery_amount, target_method)():
         return
       # Try to collect cells and aggregate their mapped properties
       # using resource + variation as aggregation key or base_application
diff --git a/product/ERP5/mixin/variated.py b/product/ERP5/mixin/variated.py
index bd3844c7d4..f494bb3d1b 100644
--- a/product/ERP5/mixin/variated.py
+++ b/product/ERP5/mixin/variated.py
@@ -30,7 +30,7 @@
 from warnings import warn
 from AccessControl import ClassSecurityInfo
 from Products.CMFCategory.Renderer import Renderer
-from Products.ERP5Type import interfaces, Permissions
+from Products.ERP5Type import interfaces, Permissions, PropertySheet
 import zope.interface
 
 
@@ -54,7 +54,11 @@ class VariatedMixin:
   security = ClassSecurityInfo()
 
   # Declarative interfaces
-  zope.interface.implements(interfaces.IVariated)
+  zope.interface.implements(interfaces.IVariated,
+                            interfaces.IVariationRange)
+
+  isRADContent = 1 # for 'property_sheets'
+  property_sheets = (PropertySheet.VariationRange, )
 
   security.declareProtected(Permissions.AccessContentsInformation, 
                             'getVariationBaseCategoryList')
diff --git a/product/ERP5/tests/testProductionOrder.py b/product/ERP5/tests/testProductionOrder.py
index af085db9d9..5c02e7afcf 100644
--- a/product/ERP5/tests/testProductionOrder.py
+++ b/product/ERP5/tests/testProductionOrder.py
@@ -395,25 +395,16 @@ class TestProductionOrderMixin(TestOrderMixin):
     """
       Fills categories of variation
     """
-
     transformation = sequence.get('transformation')
+    transformation.setVariationBaseCategoryList(self.variation_category_list)
 
-    transformation.edit(
-        variation_base_category_list = self.variation_category_list
-    )
-    
   def stepFillTransformationWithResource(self, sequence=None, sequence_list=None,
                                **kw):
 
     transformation = sequence.get('transformation')
-
     resource = sequence.get('resource')
-
     self.assertNotEquals(None, resource)
-
-    transformation.edit(
-      resource_value = resource
-    )
+    transformation.setResourceValue(resource)
 
   def stepSetOrderLineQuantity(self, sequence=None, sequence_list=None,
                                **kw):
diff --git a/product/ERP5/tests/testTransformation.py b/product/ERP5/tests/testTransformation.py
index a8a1bd1be1..fa1b6ad317 100644
--- a/product/ERP5/tests/testTransformation.py
+++ b/product/ERP5/tests/testTransformation.py
@@ -26,8 +26,8 @@
 #
 ##############################################################################
 
-from testProductionOrder import TestProductionOrderMixin
-from testInventoryAPI import BaseTestUnitConversion
+from Products.ERP5.tests.testProductionOrder import TestProductionOrderMixin
+from Products.ERP5.tests.testInventoryAPI import BaseTestUnitConversion
 import transaction
 
 class TestTransformationMixin(TestProductionOrderMixin):
@@ -146,15 +146,7 @@ class TestTransformation(TestTransformationMixin, BaseTestUnitConversion):
         resource_value=component,
         quantity=1)
     transformed_resource.setTested(True)
-    from Products.ERP5Type.Document import newTempAmount
-    amount = newTempAmount(transformation, "foobar")
-    amount.edit(
-        quantity = 1.0,
-        resource = component.getRelativeUrl(),
-    )
-    aggregated_amount_list = transformation.getAggregatedAmountList(amount)
-    self.assertEquals(len(aggregated_amount_list), 1)
-    aggregated_amount = aggregated_amount_list[0]
+    aggregated_amount, = transformation.getAggregatedAmountList()
     # Make sure that the isTested method is working properly on the
     # temp object
     self.assertTrue(aggregated_amount.isTested())
diff --git a/product/ERP5Legacy/Document/TransformationRule.py b/product/ERP5Legacy/Document/TransformationRule.py
index 1397f2638f..35ec49279c 100644
--- a/product/ERP5Legacy/Document/TransformationRule.py
+++ b/product/ERP5Legacy/Document/TransformationRule.py
@@ -219,7 +219,6 @@ class TransformationRule(TransformationSourcingRuleMixin, Rule):
       transformation = self.getTransformation(applied_rule)
       # Generate the fake context
       tmp_context = parent_movement.asContext(categories=category_list)
-      tmp_context.asComposedDocument = lambda *args: transformation
       # Calculate the industrial phase list
       previous_ind_phase_list = supply_chain.\
           getPreviousPackingListIndustrialPhaseList(current_supply_link)
@@ -228,7 +227,7 @@ class TransformationRule(TransformationSourcingRuleMixin, Rule):
       # Call getAggregatedAmountList
       # XXX expand failed if transformation is not defined.
       # Do we need to catch the exception ?
-      amount_list = tmp_context.getAggregatedAmountList()
+      amount_list = transformation.getAggregatedAmountList((tmp_context,))
       # Add entries in the consumed_movement_dict
       consumed_movement_dict = {}
       for amount in amount_list:
diff --git a/product/ERP5Legacy/tests/testLegacyProductionOrder.py b/product/ERP5Legacy/tests/testLegacyProductionOrder.py
index 1067da5066..25875c44e2 100644
--- a/product/ERP5Legacy/tests/testLegacyProductionOrder.py
+++ b/product/ERP5Legacy/tests/testLegacyProductionOrder.py
@@ -98,9 +98,8 @@ class TestProductionOrderMixin(TestOrderMixin):
       some categories for testing them
     """
     TestOrderMixin.createCategories(self)
-    operation_category_list = ['operation1', 'operation2']
     if len(self.category_tool.operation.contentValues()) == 0:
-      for category_id in operation_category_list:
+      for category_id in self.operation_category_list:
         o = self.category_tool.operation.newContent(
                                                portal_type='Category',
                                                id=category_id)
diff --git a/product/ERP5Legacy/tests/testLegacyTransformation.py b/product/ERP5Legacy/tests/testLegacyTransformation.py
new file mode 100644
index 0000000000..4ae5c830f2
--- /dev/null
+++ b/product/ERP5Legacy/tests/testLegacyTransformation.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# 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
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+##############################################################################
+
+import sys
+from Products.ERP5Legacy.tests import testLegacyProductionOrder
+sys.modules['Products.ERP5.tests.testProductionOrder'] = \
+  testLegacyProductionOrder
+from Products.ERP5.tests.testTransformation import *
+
+from Products.ERP5Legacy.tests import Legacy_getBusinessTemplateList
+Legacy_getBusinessTemplateList(TestTransformation)
-- 
2.30.9