Transformation.py 13.5 KB
Newer Older
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1 2 3 4
##############################################################################
#
# Copyright (c) 2002 Coramy SAS and Contributors. All Rights Reserved.
#                    Thierry_Faucher <Thierry_Faucher@coramy.com>
5
# Copyright (c) 2004, 2005 Nexedi SARL and Contributors. All Rights Reserved.
6
#                    Romain Courteaud <romain@nexedi.com>
Jean-Paul Smets's avatar
Jean-Paul Smets committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability 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
# garantees 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################

31
from Globals import InitializeClass
Jean-Paul Smets's avatar
Jean-Paul Smets committed
32 33
from AccessControl import ClassSecurityInfo

34
from Products.ERP5Type import Permissions, PropertySheet, Interface
Jean-Paul Smets's avatar
Jean-Paul Smets committed
35 36 37 38
from Products.ERP5Type.XMLObject import XMLObject

from Products.ERP5.Variated import Variated

39
from Products.ERP5.Document.Predicate import Predicate
Jean-Paul Smets's avatar
Jean-Paul Smets committed
40

41 42
from Products.PythonScripts.Utility import allow_class

43
from Products.CMFCategory.Renderer import Renderer
44

Jean-Paul Smets's avatar
Jean-Paul Smets committed
45

46
class Transformation(XMLObject, Predicate, Variated):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
47 48 49 50 51 52
    """
      Build of material - contains a list of transformed resources

      Use of default_resource... (to define the variation range,
      to ...)

53 54
      XXX Transformation works only for a miximum of 3 variation base category...
      Matrixbox must be rewrite for a clean implementation of n base category
Jean-Paul Smets's avatar
Jean-Paul Smets committed
55 56

    """
57
    isMovement = 1 # XXX very stupid, but for doing a test on catalog
Jean-Paul Smets's avatar
Jean-Paul Smets committed
58 59 60 61 62 63

    meta_type = 'ERP5 Transformation'
    portal_type = 'Transformation'

    # Declarative security
    security = ClassSecurityInfo()
64
    security.declareObjectProtected(Permissions.AccessContentsInformation)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
65 66 67 68 69 70 71

    # Declarative properties
    property_sheets = ( PropertySheet.Base
                      , PropertySheet.XMLObject
                      , PropertySheet.CategoryCore
                      , PropertySheet.DublinCore
                      , PropertySheet.VariationRange
72
                      , PropertySheet.Predicate
73 74 75
                      #, PropertySheet.Resource
                      , PropertySheet.TransformedResource
                      , PropertySheet.Path
Jean-Paul Smets's avatar
Jean-Paul Smets committed
76 77 78 79 80 81
                      , PropertySheet.Transformation
                      )

    # Declarative interfaces
    __implements__ = ( Interface.Variated, )

82

83 84
    security.declareProtected(Permissions.AccessContentsInformation, 
                              'updateVariationCategoryList')
85 86 87 88 89
    def updateVariationCategoryList(self):
      """
        Check if variation category list of the resource changed and update transformation
        and transformation line
      """
90
      self.setVariationBaseCategoryList(self.getVariationBaseCategoryList())
91 92 93 94
      transformation_line_list = self.contentValues()
      for transformation_line in transformation_line_list:
        transformation_line.updateVariationCategoryList()

95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getVariationRangeBaseCategoryList')
    def getVariationRangeBaseCategoryList(self):
      """
        Returns possible variation base_category ids of the
        default resource which can be used a variation axis
        in the transformation.
      """
      resource = self.getResourceValue()
      if resource is not None:
        result = resource.getVariationBaseCategoryList()
      else:
        # XXX result = self.getBaseCategoryIds()
        # Why calling this method ?
        # Get a global variable which define a list of variation base category
        result = self.getPortalVariationBaseCategoryList()
      return result

113 114
    security.declareProtected(Permissions.AccessContentsInformation, 
                              'getVariationRangeBaseCategoryItemList')
115
    def getVariationRangeBaseCategoryItemList(self,display_id='title_or_id',**kw):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
116
        """
117
          Returns possible variations of the transformation
Jean-Paul Smets's avatar
Jean-Paul Smets committed
118 119 120 121
          as a list of tuples (id, title). This is mostly
          useful in ERP5Form instances to generate selection
          menus.
        """
122 123 124
        return self.portal_categories.getItemList( 
                              self.getVariationRangeBaseCategoryList(),
                              display_id=display_id, **kw)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
125

126 127
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getVariationRangeCategoryItemList')
128
    def getVariationRangeCategoryItemList(self, base_category_list=(),
129
                                          omit_individual_variation=0,
130
                                          display_base_category=1, **kw):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
131 132 133 134 135 136 137
        """
          Returns possible variation category values for the
          transformation according to the default resource.
          Possible category values is provided as a list of
          tuples (id, title). This is mostly
          useful in ERP5Form instances to generate selection
          menus.
138 139
          User may want to define generic transformation without
          any resource define.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
140 141 142
        """
        if base_category_list is ():
          base_category_list = self.getVariationBaseCategoryList()
143 144 145

        resource = self.getResourceValue()
        if resource != None:
146
          result = resource.getVariationCategoryItemList(
147
                        base_category_list=base_category_list,
148
                        omit_individual_variation=omit_individual_variation,
149
                        display_base_category=display_base_category,**kw)
150
        else:
151 152 153 154
          # No resource is define on transformation. 
          # We want to display content of base categories
          result = self.portal_categories.getCategoryChildTitleItemList(
                         base_category_list, base=1, display_none_category=0)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
155 156
        return result

157 158
    security.declareProtected(Permissions.AccessContentsInformation, 
                              '_setVariationBaseCategoryList')
159
    def _setVariationBaseCategoryList(self, value):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
160
      """
161
        Define the possible base categories
Jean-Paul Smets's avatar
Jean-Paul Smets committed
162
      """
163 164 165 166 167 168 169 170 171 172
#      XXX TransformedResource works only for a maximum of 3 variation base category...
#      Matrixbox must be rewrite for a clean implementation of n base category
      if len(value) <= 3:
        self._baseSetVariationBaseCategoryList(value)
      else:
        raise MoreThan3VariationBaseCategory

      # create relations between resource variation and transformation
      self._setVariationCategoryList( self.getVariationRangeCategoryList() )

173 174
    security.declareProtected(Permissions.AccessContentsInformation, 
                              'setVariationBaseCategoryList')
175 176 177 178 179 180 181
    def setVariationBaseCategoryList(self, value):
      """
        Define the possible base categories and reindex object
      """
      self._setVariationBaseCategoryList(value)
      self.reindexObject()

182 183
    security.declareProtected(Permissions.AccessContentsInformation, 
                              'getVariationCategoryItemList')
184 185
    def getVariationCategoryItemList(self, base_category_list=(), base=1, 
                                     display_id='title', 
186 187
                                     current_category=None,
                                     **kw):
188 189 190 191 192 193 194 195 196
      """
        Returns the list of possible variations
        XXX Copied and modified from Variated
        Result is left display.
      """
      variation_category_item_list = []
      if base_category_list == ():
        base_category_list = self.getVariationBaseCategoryList()

197 198 199 200 201 202 203 204 205 206 207 208
      for base_category in base_category_list:
        variation_category_list = self.getVariationCategoryList(
                                            base_category_list=[base_category])

        resource_list = [self.portal_categories.resolveCategory(x) for x in\
                         variation_category_list]
        category_list = [x for x in resource_list \
                         if x.getPortalType() == 'Category']
        variation_category_item_list.extend(Renderer(
                               is_right_display=0,
                               display_none_category=0, base=base,
                               current_category=current_category,
209
                               display_id='logical_path',**kw).\
210 211 212 213 214 215 216 217
                                                 render(category_list))
        object_list = [x for x in resource_list \
                         if x.getPortalType() != 'Category']
        variation_category_item_list.extend(Renderer(
                               is_right_display=0,
                               base_category=base_category, 
                               display_none_category=0, base=base,
                               current_category=current_category,
218
                               display_id=display_id,**kw).\
219
                                                 render(object_list))
220 221
      return variation_category_item_list

222 223 224
    security.declareProtected(Permissions.AccessContentsInformation, 
                              'getAggregatedAmountList')
    def getAggregatedAmountList(self, context=None, REQUEST=None,
225
                                ind_phase_url_list=None, 
226 227
                                rejected_resource_uid_list=None, 
                                context_quantity=0,**kw):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
228
      """
229 230 231
        getAggregatedAmountList returns a AggregatedAmountList which
        can be used either to do some calculation (ex. price, BOM)
        or to display a detailed view of a transformation.
232 233 234

        context_quantity : if set to one, multiply all quantities
                           with the quantity of the context
Jean-Paul Smets's avatar
Jean-Paul Smets committed
235
      """
236
      context = self.asContext(context=context, REQUEST=REQUEST, **kw)
237 238 239
      # First we need to get the list of transformations which this 
      # transformation depends on
      # At this moment, we only consider 1 dependency
240
      template_transformation_list = self.getSpecialiseValueList()
241
      result = AggregatedAmountList()
242 243 244 245 246 247 248 249
      # Browse all involved transformations and create one line per 
      # line of transformation
      # Currently, we do not consider abstractions, we just add 
      # whatever we find in all transformations
      transformation_line_list = []
      for transformation in ([self]+template_transformation_list):
        transformation_line_list.extend(transformation.objectValues())
      # Get only lines related to a precise industrial_phase
250 251 252 253 254 255 256 257
      if ind_phase_url_list is not None:
        new_transf_line_list = []
        for line in transformation_line_list:
          ind_ph = line.getIndustrialPhaseValue()
          if ind_ph is not None:
            if ind_ph.getRelativeUrl() in ind_phase_url_list:
              new_transf_line_list.append(line)
        transformation_line_list = new_transf_line_list
258 259 260 261 262 263
      # Filter lines with resource we do not want to see
      if rejected_resource_uid_list is not None:
        transformation_line_list = filter(
                        lambda x: x.getResourceUid() not in\
                                                   rejected_resource_uid_list,
                        transformation_line_list)
264 265 266 267
      for transformation_line in transformation_line_list:
        # Browse each transformed or assorted resource of the current 
        # transformation
        result.extend(transformation_line.getAggregatedAmountList(context))
268 269
      if context_quantity:
        result.multiplyQuantity(context=context)
270 271
      return result

272 273
# XXX subclassing directly list would be better, 
# but does not work yet (error with class and security)
274 275
from UserList import UserList
class AggregatedAmountList(UserList):
276 277 278 279 280 281
  """
    Temporary object needed to aggregate Amount value
    And to calculate some report or total value
  """
  meta_type = "AggregatedAmountList"
  security = ClassSecurityInfo()
282
#  security.declareObjectPublic()
283

284 285
  security.declarePublic('getTotalPrice')
  def getTotalPrice(self):
286 287 288
    """
      Return total bas price of the transformation
    """
289 290
    result = sum(filter(lambda y: y is not None,
                        map(lambda x: x.getTotalPrice(), self)))
291 292 293 294 295 296 297
    return result

  security.declarePublic('getTotalDuration')
  def getTotalDuration(self):
    """
      Return total duration of the transformation
    """
298 299
    result = sum(filter(lambda y: y is not None, 
                        map(lambda x: x.getDuration(), self)))
300
    return result
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315

  def multiplyQuantity(self,context=None):
    """
      Take into account the quantity of the 
      context. Change the quantity of each element.
    """
    quantity = None
    if context is not None:
      if context.getQuantity() is not None:
        quantity = context.getQuantity()
    if quantity is not None:
      for x in self:
        previous_quantity = x.getQuantity()
        if previous_quantity is not None:
          x.edit(quantity=context.getQuantity()*previous_quantity)
316 317 318
  
InitializeClass(AggregatedAmountList)
allow_class(AggregatedAmountList)