From 1ff851b10841175f8cc772ef42a2b8d82dabd74c Mon Sep 17 00:00:00 2001
From: Arnaud Fontaine <arnaud.fontaine@nexedi.com>
Date: Tue, 24 Dec 2019 17:40:33 +0900
Subject: [PATCH] ZODB Components: Preparation of erp5_base migration from FS:
 Fix pylint bad-indentation warnings.

---
 product/ERP5/Document/AgentPrivilege.py |  34 +-
 product/ERP5/Document/Assignment.py     |  34 +-
 product/ERP5/Document/BankAccount.py    |  52 +--
 product/ERP5/Document/Career.py         |  40 +--
 product/ERP5/Document/Coordinate.py     | 350 ++++++++++----------
 product/ERP5/Document/Image.py          |  10 +-
 product/ERP5/Document/Organisation.py   |  38 +--
 product/ERP5/Document/Person.py         | 336 ++++++++++----------
 product/ERP5/Document/RoleDefinition.py |  56 ++--
 product/ERP5/Document/SupplyCell.py     |  80 ++---
 product/ERP5/Document/SupplyLine.py     | 404 ++++++++++++------------
 product/ERP5/mixin/builder.py           |   6 +-
 12 files changed, 719 insertions(+), 721 deletions(-)

diff --git a/product/ERP5/Document/AgentPrivilege.py b/product/ERP5/Document/AgentPrivilege.py
index 9cee24d19c..486aa922a6 100644
--- a/product/ERP5/Document/AgentPrivilege.py
+++ b/product/ERP5/Document/AgentPrivilege.py
@@ -32,22 +32,22 @@ from Products.ERP5Type.XMLObject import XMLObject
 
 
 class AgentPrivilege(XMLObject):
-    """
-    An Agent Privilege allow the Bank Account owner to give permissions to an Agent for a given period of time, for a maximum amount of money.
-    """
-    # CMF Type Definition
-    meta_type = 'ERP5 Agent Privilege'
-    portal_type = 'Agent Privilege'
+  """
+  An Agent Privilege allow the Bank Account owner to give permissions to an Agent for a given period of time, for a maximum amount of money.
+  """
+  # CMF Type Definition
+  meta_type = 'ERP5 Agent Privilege'
+  portal_type = 'Agent Privilege'
 
-    # Declarative security
-    security = ClassSecurityInfo()
-    security.declareObjectProtected(Permissions.AccessContentsInformation)
+  # Declarative security
+  security = ClassSecurityInfo()
+  security.declareObjectProtected(Permissions.AccessContentsInformation)
 
-    # Default Properties
-    property_sheets = ( PropertySheet.Base
-                      , PropertySheet.XMLObject
-                      , PropertySheet.CategoryCore
-                      , PropertySheet.DublinCore
-                      , PropertySheet.Task
-                      , PropertySheet.AgentPrivilege
-                      )
+  # Default Properties
+  property_sheets = ( PropertySheet.Base
+                    , PropertySheet.XMLObject
+                    , PropertySheet.CategoryCore
+                    , PropertySheet.DublinCore
+                    , PropertySheet.Task
+                    , PropertySheet.AgentPrivilege
+                    )
diff --git a/product/ERP5/Document/Assignment.py b/product/ERP5/Document/Assignment.py
index 76f8ad09da..fd8925eec3 100644
--- a/product/ERP5/Document/Assignment.py
+++ b/product/ERP5/Document/Assignment.py
@@ -33,22 +33,22 @@ from Products.ERP5.Document.Path import Path
 
 
 class Assignment(Path):
-    # CMF Type Definition
-    meta_type = 'ERP5 Assignment'
-    portal_type = 'Assignment'
-    add_permission = Permissions.AddPortalContent
+  # CMF Type Definition
+  meta_type = 'ERP5 Assignment'
+  portal_type = 'Assignment'
+  add_permission = Permissions.AddPortalContent
 
-    # Declarative security
-    security = ClassSecurityInfo()
-    security.declareObjectProtected(Permissions.AccessContentsInformation)
+  # Declarative security
+  security = ClassSecurityInfo()
+  security.declareObjectProtected(Permissions.AccessContentsInformation)
 
-    # Default Properties
-    property_sheets = ( PropertySheet.Base
-                      , PropertySheet.XMLObject
-                      , PropertySheet.CategoryCore
-                      , PropertySheet.DublinCore
-                      , PropertySheet.Task
-                      , PropertySheet.Arrow
-                      , PropertySheet.Path
-                      , PropertySheet.Assignment
-                      )
+  # Default Properties
+  property_sheets = ( PropertySheet.Base
+                    , PropertySheet.XMLObject
+                    , PropertySheet.CategoryCore
+                    , PropertySheet.DublinCore
+                    , PropertySheet.Task
+                    , PropertySheet.Arrow
+                    , PropertySheet.Path
+                    , PropertySheet.Assignment
+                    )
diff --git a/product/ERP5/Document/BankAccount.py b/product/ERP5/Document/BankAccount.py
index f6f844388f..0e3dadf976 100644
--- a/product/ERP5/Document/BankAccount.py
+++ b/product/ERP5/Document/BankAccount.py
@@ -34,43 +34,43 @@ from Products.ERP5.Document.Node import Node
 from Products.ERP5.Document.Coordinate import Coordinate
 
 class BankAccount(Node, Coordinate):
-    """
+  """
       A bank account number holds a collection of numbers and codes
         (ex. SWIFT, RIB, etc.) which may be used to identify a bank account.
 
       A Bank Account is owned by a Person or an Organisation. A Bank Account
         contain Agents with Agent Privileges used by the owner to delegate the
         management of the bank account to trusted third-party Persons.
-    """
+  """
 
-    meta_type = 'ERP5 Bank Account'
-    portal_type = 'Bank Account'
-    add_permission = Permissions.AddPortalContent
+  meta_type = 'ERP5 Bank Account'
+  portal_type = 'Bank Account'
+  add_permission = Permissions.AddPortalContent
 
-    # Declarative security
-    security = ClassSecurityInfo()
-    security.declareObjectProtected(Permissions.AccessContentsInformation)
+  # Declarative security
+  security = ClassSecurityInfo()
+  security.declareObjectProtected(Permissions.AccessContentsInformation)
 
-    # Declarative properties
-    property_sheets = ( PropertySheet.CategoryCore
-                      , PropertySheet.Task
-                      , PropertySheet.Resource
-                      , PropertySheet.Reference
-                      , PropertySheet.BankAccount
-                      )
+  # Declarative properties
+  property_sheets = ( PropertySheet.CategoryCore
+                    , PropertySheet.Task
+                    , PropertySheet.Resource
+                    , PropertySheet.Reference
+                    , PropertySheet.BankAccount
+                    )
 
 
-    security.declareProtected(Permissions.AccessContentsInformation, 'getReference')
-    def getReference(self, *args, **kw):
-      """reference depends on the site configuration.
-      """
-      value = self._baseGetReference(*args, **kw)
-      if value in (None, ''):
-        # Try to get a skin from type name
-        method = self._getTypeBasedMethod('getReference')
-        if method is not None:
-          return method(*args, **kw)
-      return value
+  security.declareProtected(Permissions.AccessContentsInformation, 'getReference')
+  def getReference(self, *args, **kw):
+    """reference depends on the site configuration.
+    """
+    value = self._baseGetReference(*args, **kw)
+    if value in (None, ''):
+      # Try to get a skin from type name
+      method = self._getTypeBasedMethod('getReference')
+      if method is not None:
+        return method(*args, **kw)
+    return value
 
 # XXX The following "helper methods" have been commented out, and kept in the
 # code as an example.
diff --git a/product/ERP5/Document/Career.py b/product/ERP5/Document/Career.py
index f839f00b18..a7033012bf 100644
--- a/product/ERP5/Document/Career.py
+++ b/product/ERP5/Document/Career.py
@@ -33,27 +33,27 @@ from Products.ERP5Type import Permissions, PropertySheet
 from Products.ERP5.Document.Path import Path
 
 class Career(Path):
-    """
+  """
       Contains information about abilities, salary, grade, role... of a
       Person at a certain career step.
-    """
-    # CMF Type Definition
-    meta_type = 'ERP5 Career'
-    portal_type = 'Career'
-    add_permission = Permissions.AddPortalContent
+  """
+  # CMF Type Definition
+  meta_type = 'ERP5 Career'
+  portal_type = 'Career'
+  add_permission = Permissions.AddPortalContent
 
-    # Declarative security
-    security = ClassSecurityInfo()
-    security.declareObjectProtected(Permissions.AccessContentsInformation)
+  # Declarative security
+  security = ClassSecurityInfo()
+  security.declareObjectProtected(Permissions.AccessContentsInformation)
 
-    # Default Properties
-    property_sheets = ( PropertySheet.Base
-                      , PropertySheet.XMLObject
-                      , PropertySheet.CategoryCore
-                      , PropertySheet.DublinCore
-                      , PropertySheet.Task
-                      , PropertySheet.Arrow
-                      , PropertySheet.Path
-                      , PropertySheet.Reference
-                      , PropertySheet.Assignment
-                      )
+  # Default Properties
+  property_sheets = ( PropertySheet.Base
+                    , PropertySheet.XMLObject
+                    , PropertySheet.CategoryCore
+                    , PropertySheet.DublinCore
+                    , PropertySheet.Task
+                    , PropertySheet.Arrow
+                    , PropertySheet.Path
+                    , PropertySheet.Reference
+                    , PropertySheet.Assignment
+                    )
diff --git a/product/ERP5/Document/Coordinate.py b/product/ERP5/Document/Coordinate.py
index 8b1dce0ebb..f60aac09d4 100644
--- a/product/ERP5/Document/Coordinate.py
+++ b/product/ERP5/Document/Coordinate.py
@@ -38,7 +38,7 @@ import re
 _marker = object()
 
 class Coordinate(Base):
-    """
+  """
         Coordinates is a mix-in class which is used to store elementary
         coordinates of an Entity (ex. Person, Organisation)
 
@@ -75,179 +75,177 @@ class Coordinate(Base):
         In order to be able to list all coordinates of an Entity,
         a list of Coordinate metatypes has to be defined and
         stored somewhere. (TODO)
-        """
-
-    meta_type = 'ERP5 Coordinate'
-    portal_type = 'Coordinate'
-    add_permission = Permissions.AddPortalContent
-
-    # Declarative interface
-    zope.interface.implements(interfaces.ICoordinate, )
-
-    # Declarative security (replaces __ac_permissions__)
-    security = ClassSecurityInfo()
-    security.declareObjectProtected(Permissions.AccessContentsInformation)
-
-    # Declarative properties
-    property_sheets = ( PropertySheet.Base
-                      , PropertySheet.SimpleItem
-                      , PropertySheet.Coordinate
-                      )
-
-    ### helper methods
-    security.declareProtected( Permissions.AccessContentsInformation,
-                               'getRegularExpressionFindAll')
-    def getRegularExpressionFindAll(self, regular_expression, string):
-      """
-      allows call of re.findall in a python script used for Coordinate
-      """
-      return re.findall(regular_expression, string)
-
-    security.declareProtected( Permissions.AccessContentsInformation,
-                               'getRegularExpressionGroups')
-    def getRegularExpressionGroups(self, regular_expression, string):
-      """
-      allows call of re.search.groups in a python script used for Coordinate
-      """
-      match = re.search(regular_expression, string)
-      if match is None:
-        return ()
-      return re.search(regular_expression, string).groups()
-
-    ### Mix-in methods
-    security.declareProtected( Permissions.AccessContentsInformation,
-                               'asText' )
-    def asText(self):
-      """
-          returns the coordinate as a text string
-      """
-      script = self._getTypeBasedMethod('asText')
-      if script is not None:
-        return script()
-
-    security.declareProtected( Permissions.AccessContentsInformation,
-                               'getText')
-    def getText(self):
-      """
-      calls asText
-      """
-      return self.asText()
-
-    security.declareProtected( Permissions.AccessContentsInformation,
-                               'hasText')
-    def hasText(self):
-      """
-      calls asText
-      """
-      return bool(self.asText())
-
-    security.declareProtected(Permissions.AccessContentsInformation, 'getCoordinateText')
-    def getCoordinateText(self, default=_marker):
-      """Fallback on splitted values (old API)
-      """
-      if not self.hasCoordinateText() and self.isDetailed():
-        # Display old values (parsed ones)
-        # empty value is None, not ''
-        value = self.asText()
-        if value:
-          return value
-        if default is _marker:
-          return None
-        else:
-          return default
+  """
+
+  meta_type = 'ERP5 Coordinate'
+  portal_type = 'Coordinate'
+  add_permission = Permissions.AddPortalContent
+
+  # Declarative interface
+  zope.interface.implements(interfaces.ICoordinate, )
+
+  # Declarative security (replaces __ac_permissions__)
+  security = ClassSecurityInfo()
+  security.declareObjectProtected(Permissions.AccessContentsInformation)
+
+  # Declarative properties
+  property_sheets = ( PropertySheet.Base
+                    , PropertySheet.SimpleItem
+                    , PropertySheet.Coordinate
+                    )
+
+  ### helper methods
+  security.declareProtected( Permissions.AccessContentsInformation,
+                             'getRegularExpressionFindAll')
+  def getRegularExpressionFindAll(self, regular_expression, string):
+    """
+    allows call of re.findall in a python script used for Coordinate
+    """
+    return re.findall(regular_expression, string)
+
+  security.declareProtected( Permissions.AccessContentsInformation,
+                             'getRegularExpressionGroups')
+  def getRegularExpressionGroups(self, regular_expression, string):
+    """
+    allows call of re.search.groups in a python script used for Coordinate
+    """
+    match = re.search(regular_expression, string)
+    if match is None:
+      return ()
+    return re.search(regular_expression, string).groups()
+
+  ### Mix-in methods
+  security.declareProtected( Permissions.AccessContentsInformation,
+                             'asText' )
+  def asText(self):
+    """
+    returns the coordinate as a text string
+    """
+    script = self._getTypeBasedMethod('asText')
+    if script is not None:
+      return script()
+
+  security.declareProtected( Permissions.AccessContentsInformation,
+                             'getText')
+  def getText(self):
+    """
+    calls asText
+    """
+    return self.asText()
+
+  security.declareProtected( Permissions.AccessContentsInformation,
+                             'hasText')
+  def hasText(self):
+    """
+    calls asText
+    """
+    return bool(self.asText())
+
+  security.declareProtected(Permissions.AccessContentsInformation, 'getCoordinateText')
+  def getCoordinateText(self, default=_marker):
+    """Fallback on splitted values (old API)
+    """
+    if not self.hasCoordinateText() and self.isDetailed():
+      # Display old values (parsed ones)
+      # empty value is None, not ''
+      value = self.asText()
+      if value:
+        return value
       if default is _marker:
-        return self._baseGetCoordinateText()
-      return self._baseGetCoordinateText(default)
-
-    security.declareProtected( Permissions.ModifyPortalContent, 'fromText' )
-    @deprecated
-    def fromText(self, coordinate_text):
-      """
-            modifies the coordinate according to the input text
-            must be implemented by subclasses
-      """
-      script = self._getTypeBasedMethod('fromText')
-      if script is not None:
-        return script(text=coordinate_text)
-
-    security.declareProtected(Permissions.ModifyPortalContent, '_setText')
-    def _setText(self, value):
-      """
-      calls fromText
-      """
-      return self.fromText(value)
-
-    security.declareProtected( Permissions.AccessContentsInformation,
-                               'standardTextFormat')
-    def standardTextFormat(self):
-      """
-      Returns the standard text formats for telephone numbers
-      """
-      pass
-
-    security.declareProtected(Permissions.AccessContentsInformation, 'isDetailed')
-    def isDetailed(self):
-      return False
-
-    security.declarePrivate( '_writeFromPUT' )
-    def _writeFromPUT( self, body ):
-      headers = {}
-      headers, body = parseHeadersBody(body, headers)
-      lines = body.split( '\n' )
-      self.edit( lines[0] )
-      headers['Format'] = self.COORDINATE_FORMAT
-      new_subject = keywordsplitter(headers)
-      headers['Subject'] = new_subject or self.Subject()
-      haveheader = headers.has_key
-      for key, value in self.getMetadataHeaders():
-        if key != 'Format' and not haveheader(key):
-          headers[key] = value
-
-      self._editMetadata(title=headers['Title'],
-                        subject=headers['Subject'],
-                        description=headers['Description'],
-                        contributors=headers['Contributors'],
-                        effective_date=headers['Effective_date'],
-                        expiration_date=headers['Expiration_date'],
-                        format=headers['Format'],
-                        language=headers['Language'],
-                        rights=headers['Rights'],
-                        )
-
-    ## FTP handlers
-    security.declareProtected( Permissions.ModifyPortalContent, 'PUT')
-    def PUT(self, REQUEST, RESPONSE):
-      """
-          Handle HTTP / WebDAV / FTP PUT requests.
-      """
-      self.dav__init(REQUEST, RESPONSE)
-      self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
-      if REQUEST.environ['REQUEST_METHOD'] != 'PUT':
-        raise Forbidden, 'REQUEST_METHOD should be PUT.'
-      body = REQUEST.get('BODY', '')
-      try:
-        self._writeFromPUT( body )
-        RESPONSE.setStatus(204)
-        return RESPONSE
-      except ResourceLockedError:
-        get_transaction().abort()
-        RESPONSE.setStatus(423)
-        return RESPONSE
-
-    security.declareProtected( Permissions.View, 'manage_FTPget' )
-    def manage_FTPget(self):
-      """
-          Get the coordinate as text for WebDAV src / FTP download.
-      """
-      hdrlist = self.getMetadataHeaders()
-      hdrtext = formatRFC822Headers( hdrlist )
-      bodytext = '%s\n\n%s' % ( hdrtext, self.asText() )
-
-      return bodytext
-
-    security.declareProtected( Permissions.View, 'get_size' )
-    def get_size( self ):
-      """
-          Used for FTP and apparently the ZMI now too
-      """
-      return len(self.manage_FTPget())
+        return None
+      else:
+        return default
+    if default is _marker:
+      return self._baseGetCoordinateText()
+    return self._baseGetCoordinateText(default)
+
+  security.declareProtected( Permissions.ModifyPortalContent, 'fromText' )
+  @deprecated
+  def fromText(self, coordinate_text):
+    """
+    modifies the coordinate according to the input text
+    must be implemented by subclasses
+    """
+    script = self._getTypeBasedMethod('fromText')
+    if script is not None:
+      return script(text=coordinate_text)
+
+  security.declareProtected(Permissions.ModifyPortalContent, '_setText')
+  def _setText(self, value):
+    """
+    calls fromText
+    """
+    return self.fromText(value)
+
+  security.declareProtected( Permissions.AccessContentsInformation,
+                             'standardTextFormat')
+  def standardTextFormat(self):
+    """
+    Returns the standard text formats for telephone numbers
+    """
+    pass
+
+  security.declareProtected(Permissions.AccessContentsInformation, 'isDetailed')
+  def isDetailed(self):
+    return False
+
+  security.declarePrivate( '_writeFromPUT' )
+  def _writeFromPUT( self, body ):
+    headers, body = parseHeadersBody(body, headers)
+    lines = body.split( '\n' )
+    self.edit( lines[0] )
+    headers['Format'] = self.COORDINATE_FORMAT
+    new_subject = keywordsplitter(headers)
+    headers['Subject'] = new_subject or self.Subject()
+    haveheader = headers.has_key
+    for key, value in self.getMetadataHeaders():
+      if key != 'Format' and not haveheader(key):
+        headers[key] = value
+    self._editMetadata(title=headers['Title'],
+                       subject=headers['Subject'],
+                       description=headers['Description'],
+                       contributors=headers['Contributors'],
+                       effective_date=headers['Effective_date'],
+                       expiration_date=headers['Expiration_date'],
+                       format=headers['Format'],
+                       language=headers['Language'],
+                       rights=headers['Rights'],
+                       )
+
+  ## FTP handlers
+  security.declareProtected( Permissions.ModifyPortalContent, 'PUT')
+  def PUT(self, REQUEST, RESPONSE):
+    """
+    Handle HTTP / WebDAV / FTP PUT requests.
+    """
+    self.dav__init(REQUEST, RESPONSE)
+    self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
+    if REQUEST.environ['REQUEST_METHOD'] != 'PUT':
+      raise Forbidden, 'REQUEST_METHOD should be PUT.'
+    body = REQUEST.get('BODY', '')
+    try:
+      self._writeFromPUT( body )
+      RESPONSE.setStatus(204)
+      return RESPONSE
+    except ResourceLockedError:
+      get_transaction().abort()
+      RESPONSE.setStatus(423)
+      return RESPONSE
+
+  security.declareProtected( Permissions.View, 'manage_FTPget' )
+  def manage_FTPget(self):
+    """
+    Get the coordinate as text for WebDAV src / FTP download.
+    """
+    hdrlist = self.getMetadataHeaders()
+    hdrtext = formatRFC822Headers( hdrlist )
+    bodytext = '%s\n\n%s' % ( hdrtext, self.asText() )
+
+    return bodytext
+
+  security.declareProtected( Permissions.View, 'get_size' )
+  def get_size( self ):
+    """
+    Used for FTP and apparently the ZMI now too
+    """
+    return len(self.manage_FTPget())
diff --git a/product/ERP5/Document/Image.py b/product/ERP5/Document/Image.py
index b17073a735..19b30869ba 100644
--- a/product/ERP5/Document/Image.py
+++ b/product/ERP5/Document/Image.py
@@ -204,7 +204,7 @@ class Image(TextConvertableMixin, File, OFSImage):
     """Return list of HTML <a> tags for displays."""
     links = []
     for display in self.displayIds(exclude):
-        links.append('<a href="%s?display=%s">%s</a>' % (self.REQUEST['URL'], display, display))
+      links.append('<a href="%s?display=%s">%s</a>' % (self.REQUEST['URL'], display, display))
     return links
 
   security.declareProtected(Permissions.AccessContentsInformation, 'displayMap')
@@ -367,11 +367,11 @@ class Image(TextConvertableMixin, File, OFSImage):
                                cwd='/',
                                close_fds=True)
     try:
-        # XXX: The only portable way is to pass what stdin.write can accept,
-        #      which is a string for PIPE.
-        image, err = process.communicate(data)
+      # XXX: The only portable way is to pass what stdin.write can accept,
+      #      which is a string for PIPE.
+      image, err = process.communicate(data)
     finally:
-        del process
+      del process
     if image:
       return StringIO(image)
     raise ConversionError('Image conversion failed (%s).' % err)
diff --git a/product/ERP5/Document/Organisation.py b/product/ERP5/Document/Organisation.py
index 4b9b7783aa..db876acb4b 100644
--- a/product/ERP5/Document/Organisation.py
+++ b/product/ERP5/Document/Organisation.py
@@ -33,7 +33,7 @@ from Products.ERP5Type import Permissions, PropertySheet, interfaces
 from Products.ERP5.Document.Node import Node
 
 class Organisation(Node):
-    """
+  """
       An Organisation object holds the information about
       an organisation (ex. a division in a company, a company,
       a service in a public administration).
@@ -46,27 +46,27 @@ class Organisation(Node):
 
       Organisation objects inherit from the MetaNode base class
       (one of the 5 base classes in the ERP5 universal business model)
-    """
+  """
 
-    meta_type = 'ERP5 Organisation'
-    portal_type = 'Organisation'
-    add_permission = Permissions.AddPortalContent
+  meta_type = 'ERP5 Organisation'
+  portal_type = 'Organisation'
+  add_permission = Permissions.AddPortalContent
 
-    zope.interface.implements(interfaces.INode)
+  zope.interface.implements(interfaces.INode)
 
-    # Declarative security
-    security = ClassSecurityInfo()
-    security.declareObjectProtected(Permissions.AccessContentsInformation)
+  # Declarative security
+  security = ClassSecurityInfo()
+  security.declareObjectProtected(Permissions.AccessContentsInformation)
 
-    # Declarative properties
-    property_sheets = ( PropertySheet.Base
-                      , PropertySheet.XMLObject
-                      , PropertySheet.CategoryCore
-                      , PropertySheet.DublinCore
-                      , PropertySheet.Organisation
-                      , PropertySheet.Mapping
-                      , PropertySheet.Task
-                      , PropertySheet.Reference
-                      )
+  # Declarative properties
+  property_sheets = ( PropertySheet.Base
+                    , PropertySheet.XMLObject
+                    , PropertySheet.CategoryCore
+                    , PropertySheet.DublinCore
+                    , PropertySheet.Organisation
+                    , PropertySheet.Mapping
+                    , PropertySheet.Task
+                    , PropertySheet.Reference
+                    )
 
 
diff --git a/product/ERP5/Document/Person.py b/product/ERP5/Document/Person.py
index 5faf138739..eb21bbb2ef 100644
--- a/product/ERP5/Document/Person.py
+++ b/product/ERP5/Document/Person.py
@@ -50,7 +50,7 @@ class UserExistsError(ValidationFailed):
     super(UserExistsError, self).__init__('user id %s already exists' % (user_id, ))
 
 class Person(Node, LoginAccountProviderMixin, EncryptedPasswordMixin, ERP5UserMixin):
-    """
+  """
       An Person object holds the information about
       an person (ex. you, me, someone in the company,
       someone outside of the company, a member of the portal,
@@ -64,194 +64,194 @@ class Person(Node, LoginAccountProviderMixin, EncryptedPasswordMixin, ERP5UserMi
 
       Person objects inherit from the Node base class
       (one of the 5 base classes in the ERP5 universal business model)
-    """
+  """
 
-    meta_type = 'ERP5 Person'
-    portal_type = 'Person'
-    add_permission = Permissions.AddPortalContent
+  meta_type = 'ERP5 Person'
+  portal_type = 'Person'
+  add_permission = Permissions.AddPortalContent
 
-    zope.interface.implements(interfaces.INode)
+  zope.interface.implements(interfaces.INode)
 
-    # Declarative security
-    security = ClassSecurityInfo()
-    security.declareObjectProtected(Permissions.AccessContentsInformation)
+  # Declarative security
+  security = ClassSecurityInfo()
+  security.declareObjectProtected(Permissions.AccessContentsInformation)
 
-    # Declarative properties
-    property_sheets = ( PropertySheet.Base
-                      , PropertySheet.XMLObject
-                      , PropertySheet.CategoryCore
-                      , PropertySheet.DublinCore
-                      , PropertySheet.Reference
-                      , PropertySheet.Person
-                      , PropertySheet.Login
-                      , PropertySheet.Mapping
-                      , PropertySheet.Task
-                      )
+  # Declarative properties
+  property_sheets = ( PropertySheet.Base
+                    , PropertySheet.XMLObject
+                    , PropertySheet.CategoryCore
+                    , PropertySheet.DublinCore
+                    , PropertySheet.Reference
+                    , PropertySheet.Person
+                    , PropertySheet.Login
+                    , PropertySheet.Mapping
+                    , PropertySheet.Task
+                    )
 
-    security.declareProtected(Permissions.AccessContentsInformation,
-                              'getTitle')
-    def getTitle(self, **kw):
-      """
-        Returns the title if it exists or a combination of
-        first name, middle name and last name
-      """
-      title = ' '.join([x for x in (self.getFirstName(),
-                                    self.getMiddleName(),
-                                    self.getLastName()) if x])
-      if title:
-        return title
-      return super(Person, self).getTitle(**kw)
+  security.declareProtected(Permissions.AccessContentsInformation,
+                            'getTitle')
+  def getTitle(self, **kw):
+    """
+    Returns the title if it exists or a combination of
+    first name, middle name and last name
+    """
+    title = ' '.join([x for x in (self.getFirstName(),
+                                  self.getMiddleName(),
+                                  self.getLastName()) if x])
+    if title:
+      return title
+    return super(Person, self).getTitle(**kw)
 
-    security.declareProtected(Permissions.AccessContentsInformation,
-                              'getTranslatedTitle')
-    def getTranslatedTitle(self, **kw):
-      """
-        Returns the title if it exists or a combination of
-        first name, middle name and last name
-      """
-      title = ' '.join([x for x in (self.getTranslatedFirstName(**kw),
-                                    self.getTranslatedMiddleName(**kw),
-                                    self.getTranslatedLastName(**kw)) if x])
-      if title:
-        return title
-      return super(Person, self).getTranslatedTitle(**kw)
+  security.declareProtected(Permissions.AccessContentsInformation,
+                            'getTranslatedTitle')
+  def getTranslatedTitle(self, **kw):
+    """
+    Returns the title if it exists or a combination of
+    first name, middle name and last name
+    """
+    title = ' '.join([x for x in (self.getTranslatedFirstName(**kw),
+                                  self.getTranslatedMiddleName(**kw),
+                                  self.getTranslatedLastName(**kw)) if x])
+    if title:
+      return title
+    return super(Person, self).getTranslatedTitle(**kw)
 
-    security.declareProtected(Permissions.AccessContentsInformation,
-                              'title_or_id')
-    def title_or_id(self):
-      return self.getTitleOrId()
+  security.declareProtected(Permissions.AccessContentsInformation,
+                            'title_or_id')
+  def title_or_id(self):
+    return self.getTitleOrId()
 
-    security.declareProtected(Permissions.AccessContentsInformation,
-                              'hasTitle')
-    def hasTitle(self):
-      return self.hasFirstName() or \
-          self.hasLastName() or \
-          self.hasMiddleName() or \
-          self._baseHasTitle()
+  security.declareProtected(Permissions.AccessContentsInformation,
+                            'hasTitle')
+  def hasTitle(self):
+    return self.hasFirstName() or \
+        self.hasLastName() or \
+        self.hasMiddleName() or \
+        self._baseHasTitle()
 
-    def __checkUserIdAvailability(self, pas_plugin_class, user_id=None, login=None):
-      # Encode reference to hex to prevent uppercase/lowercase conflict in
-      # activity table (when calling countMessageWithTag)
-      if user_id:
-        tag = 'set_userid_' + user_id.encode('hex')
+  def __checkUserIdAvailability(self, pas_plugin_class, user_id=None, login=None):
+    # Encode reference to hex to prevent uppercase/lowercase conflict in
+    # activity table (when calling countMessageWithTag)
+    if user_id:
+      tag = 'set_userid_' + user_id.encode('hex')
+    else:
+      tag = 'set_login_' + login.encode('hex')
+    # Check that there no existing user
+    acl_users = getattr(self, 'acl_users', None)
+    if PluggableAuthService is not None and isinstance(acl_users,
+          PluggableAuthService.PluggableAuthService.PluggableAuthService):
+      plugin_name_set = {
+        plugin_name for plugin_name, plugin_value in acl_users.plugins.listPlugins(
+          PluggableAuthService.interfaces.plugins.IUserEnumerationPlugin,
+        ) if isinstance(plugin_value, pas_plugin_class)
+      }
+      if plugin_name_set:
+        if any(
+          user['pluginid'] in plugin_name_set
+          for user in acl_users.searchUsers(
+            id=user_id,
+            login=login,
+            exact_match=True,
+          )
+        ):
+          raise UserExistsError(user_id)
       else:
-        tag = 'set_login_' + login.encode('hex')
-      # Check that there no existing user
-      acl_users = getattr(self, 'acl_users', None)
-      if PluggableAuthService is not None and isinstance(acl_users,
-            PluggableAuthService.PluggableAuthService.PluggableAuthService):
-        plugin_name_set = {
-          plugin_name for plugin_name, plugin_value in acl_users.plugins.listPlugins(
-            PluggableAuthService.interfaces.plugins.IUserEnumerationPlugin,
-          ) if isinstance(plugin_value, pas_plugin_class)
-        }
-        if plugin_name_set:
-          if any(
-            user['pluginid'] in plugin_name_set
-            for user in acl_users.searchUsers(
-              id=user_id,
-              login=login,
-              exact_match=True,
-            )
-          ):
-            raise UserExistsError(user_id)
-        else:
-          # PAS is used, without expected enumeration plugin: property has no
-          # effect on user enumeration, skip checks.
-          # XXX: what if desired plugin becomes active later ?
-          return
-      # Check that there is no reindexation related to reference indexation
-      if self.getPortalObject().portal_activities.countMessageWithTag(tag):
-        raise UserExistsError(user_id)
+        # PAS is used, without expected enumeration plugin: property has no
+        # effect on user enumeration, skip checks.
+        # XXX: what if desired plugin becomes active later ?
+        return
+    # Check that there is no reindexation related to reference indexation
+    if self.getPortalObject().portal_activities.countMessageWithTag(tag):
+      raise UserExistsError(user_id)
 
-      # Prevent concurrent transaction to set the same reference on 2
-      # different persons
-      # XXX: person_module is rather large because of all permission
-      # declarations, it would be better to find a smaller document to use
-      # here.
-      self.getParentValue().serialize()
-      # Prevent to set the same reference on 2 different persons during the
-      # same transaction
-      transactional_variable = getTransactionalVariable()
-      if tag in transactional_variable:
-        raise UserExistsError(user_id)
-      else:
-        transactional_variable[tag] = None
-      self.reindexObject(activate_kw={'tag': tag})
+    # Prevent concurrent transaction to set the same reference on 2
+    # different persons
+    # XXX: person_module is rather large because of all permission
+    # declarations, it would be better to find a smaller document to use
+    # here.
+    self.getParentValue().serialize()
+    # Prevent to set the same reference on 2 different persons during the
+    # same transaction
+    transactional_variable = getTransactionalVariable()
+    if tag in transactional_variable:
+      raise UserExistsError(user_id)
+    else:
+      transactional_variable[tag] = None
+    self.reindexObject(activate_kw={'tag': tag})
 
-    def _setReference(self, value):
-      """
-        Set the user id. This method is defined explicitly, because
-        we want to prevent duplicated user ids, but only when
-        PAS _AND_ ERP5UserManager are used
-      """
-      if value != self.getReference():
-        if value:
-          self.__checkUserIdAvailability(
-            pas_plugin_class=ERP5UserManager,
-            login=value,
-          )
-        self._baseSetReference(value)
-        # invalid the cache for ERP5Security
-        self.getPortalObject().portal_caches.clearCache(cache_factory_list=('erp5_content_short', ))
+  def _setReference(self, value):
+    """
+    Set the user id. This method is defined explicitly, because
+    we want to prevent duplicated user ids, but only when
+    PAS _AND_ ERP5UserManager are used
+    """
+    if value != self.getReference():
+      if value:
+        self.__checkUserIdAvailability(
+          pas_plugin_class=ERP5UserManager,
+          login=value,
+        )
+      self._baseSetReference(value)
+      # invalid the cache for ERP5Security
+      self.getPortalObject().portal_caches.clearCache(cache_factory_list=('erp5_content_short', ))
 
-    def _setUserId(self, value):
-      """
-        Set the user id. This method is defined explicitly, because:
+  def _setUserId(self, value):
+    """
+    Set the user id. This method is defined explicitly, because:
 
-        - we want to apply a different permission
+      - we want to apply a different permission
 
-        - we want to prevent duplicated user ids, but only when
-          PAS _AND_ ERP5LoginUserManager are used
-      """
-      if value != self.getUserId():
-        if value:
-          self.__checkUserIdAvailability(
-            pas_plugin_class=ERP5LoginUserManager,
-            user_id=value,
-          )
-        self._baseSetUserId(value)
+      - we want to prevent duplicated user ids, but only when
+        PAS _AND_ ERP5LoginUserManager are used
+    """
+    if value != self.getUserId():
+      if value:
+        self.__checkUserIdAvailability(
+          pas_plugin_class=ERP5LoginUserManager,
+          user_id=value,
+        )
+      self._baseSetUserId(value)
 
-    # Time management
-    security.declareProtected(Permissions.AccessContentsInformation,
-                              'getAvailableTime')
-    def getAvailableTime(self, *args, **kw):
-      """
-      Calculate available time for a person
+  # Time management
+  security.declareProtected(Permissions.AccessContentsInformation,
+                            'getAvailableTime')
+  def getAvailableTime(self, *args, **kw):
+    """
+    Calculate available time for a person
 
-      See SimulationTool.getAvailableTime
-      """
-      kw['node'] = [self.getUid()]
+    See SimulationTool.getAvailableTime
+    """
+    kw['node'] = [self.getUid()]
 
-      portal_simulation = self.getPortalObject().portal_simulation
-      return portal_simulation.getAvailableTime(*args, **kw)
+    portal_simulation = self.getPortalObject().portal_simulation
+    return portal_simulation.getAvailableTime(*args, **kw)
 
-    security.declareProtected(Permissions.AccessContentsInformation,
-                              'getAvailableTimeSequence')
-    def getAvailableTimeSequence(self, *args, **kw):
-      """
-      Calculate available time for a person in a sequence
+  security.declareProtected(Permissions.AccessContentsInformation,
+                            'getAvailableTimeSequence')
+  def getAvailableTimeSequence(self, *args, **kw):
+    """
+    Calculate available time for a person in a sequence
 
-      See SimulationTool.getAvailableTimeSequence
-      """
-      kw['node'] = [self.getUid()]
+    See SimulationTool.getAvailableTimeSequence
+    """
+    kw['node'] = [self.getUid()]
 
-      portal_simulation = self.getPortalObject().portal_simulation
-      return portal_simulation.getAvailableTimeSequence(*args, **kw)
+    portal_simulation = self.getPortalObject().portal_simulation
+    return portal_simulation.getAvailableTimeSequence(*args, **kw)
 
-    # Notifiation API
-    security.declareProtected(Permissions.AccessContentsInformation,
-                              'notifyMessage')
-    def notifyMessage(self, message):
-      """
-      This method can only be called with proxy roles.
+  # Notifiation API
+  security.declareProtected(Permissions.AccessContentsInformation,
+                            'notifyMessage')
+  def notifyMessage(self, message):
+    """
+    This method can only be called with proxy roles.
 
-      A per user preference allows for deciding how to be notified.
-      - by email
-      - by SMS (if meaningful)
-      - daily
-      - weekly
-      - instantly
+    A per user preference allows for deciding how to be notified.
+    - by email
+    - by SMS (if meaningful)
+    - daily
+    - weekly
+    - instantly
 
-      notification is handled as an activity
-      """
+    notification is handled as an activity
+    """
diff --git a/product/ERP5/Document/RoleDefinition.py b/product/ERP5/Document/RoleDefinition.py
index 3cb735096c..837720856b 100644
--- a/product/ERP5/Document/RoleDefinition.py
+++ b/product/ERP5/Document/RoleDefinition.py
@@ -33,36 +33,36 @@ from Products.ERP5Type.ERP5Type \
   import ERP5TYPE_SECURITY_GROUP_ID_GENERATION_SCRIPT
 
 class RoleDefinition(XMLObject):
-    # CMF Type Definition
-    meta_type = 'ERP5 Role Definition'
-    portal_type = 'Role Definition'
-    add_permission = Permissions.ChangeLocalRoles
+  # CMF Type Definition
+  meta_type = 'ERP5 Role Definition'
+  portal_type = 'Role Definition'
+  add_permission = Permissions.ChangeLocalRoles
 
-    # Declarative security
-    security = ClassSecurityInfo()
-    security.declareObjectProtected(Permissions.AccessContentsInformation)
+  # Declarative security
+  security = ClassSecurityInfo()
+  security.declareObjectProtected(Permissions.AccessContentsInformation)
 
-    zope.interface.implements(interfaces.ILocalRoleGenerator)
+  zope.interface.implements(interfaces.ILocalRoleGenerator)
 
-    # Default Properties
-    property_sheets = ( PropertySheet.Base
-                      , PropertySheet.XMLObject
-                      , PropertySheet.CategoryCore
-                      , PropertySheet.DublinCore
-                      , PropertySheet.RoleDefinition
-                      )
+  # Default Properties
+  property_sheets = ( PropertySheet.Base
+                    , PropertySheet.XMLObject
+                    , PropertySheet.CategoryCore
+                    , PropertySheet.DublinCore
+                    , PropertySheet.RoleDefinition
+                    )
 
-    def _setRoleName(self, value):
-      if value and value not in \
-         zip(*self.RoleDefinition_getRoleNameItemList())[1]:
-        raise Unauthorized("You are not allowed to give %s role" % value)
-      self._baseSetRoleName(value)
+  def _setRoleName(self, value):
+    if value and value not in \
+       zip(*self.RoleDefinition_getRoleNameItemList())[1]:
+      raise Unauthorized("You are not allowed to give %s role" % value)
+    self._baseSetRoleName(value)
 
-    security.declarePrivate("getLocalRolesFor")
-    def getLocalRolesFor(self, ob, user_name=None):
-      group_id_generator = getattr(ob,
-        ERP5TYPE_SECURITY_GROUP_ID_GENERATION_SCRIPT)
-      role_list = self.getRoleName(),
-      return {group_id: role_list
-        for group_id in group_id_generator(category_order=('agent',),
-                                           agent=self.getAgentList())}
+  security.declarePrivate("getLocalRolesFor")
+  def getLocalRolesFor(self, ob, user_name=None):
+    group_id_generator = getattr(ob,
+      ERP5TYPE_SECURITY_GROUP_ID_GENERATION_SCRIPT)
+    role_list = self.getRoleName(),
+    return {group_id: role_list
+      for group_id in group_id_generator(category_order=('agent',),
+                                         agent=self.getAgentList())}
diff --git a/product/ERP5/Document/SupplyCell.py b/product/ERP5/Document/SupplyCell.py
index 67dfabf69a..e5ce572f71 100644
--- a/product/ERP5/Document/SupplyCell.py
+++ b/product/ERP5/Document/SupplyCell.py
@@ -32,49 +32,49 @@ from Products.ERP5Type import Permissions, PropertySheet
 from Products.ERP5.Document.Path import Path
 
 class SupplyCell(Path):
-    """A Supply Cell is used for different variations in a supply line.
-    """
+  """A Supply Cell is used for different variations in a supply line.
+  """
 
-    meta_type = 'ERP5 Supply Cell'
-    portal_type = 'Supply Cell'
-    add_permission = Permissions.AddPortalContent
+  meta_type = 'ERP5 Supply Cell'
+  portal_type = 'Supply Cell'
+  add_permission = Permissions.AddPortalContent
 
-    # Declarative security
-    security = ClassSecurityInfo()
-    security.declareObjectProtected(Permissions.AccessContentsInformation)
+  # Declarative security
+  security = ClassSecurityInfo()
+  security.declareObjectProtected(Permissions.AccessContentsInformation)
 
-    # Declarative properties
-    property_sheets = ( PropertySheet.Base
-                      , PropertySheet.CategoryCore
-                      , PropertySheet.Amount
-                      , PropertySheet.Task
-                      , PropertySheet.Movement
-                      , PropertySheet.Price
-                      , PropertySheet.SupplyLine
-                      , PropertySheet.Discount
-                      , PropertySheet.Path
-                      , PropertySheet.FlowCapacity
-                      , PropertySheet.Predicate
-                      , PropertySheet.MappedValue
-                      , PropertySheet.Reference
-                      )
+  # Declarative properties
+  property_sheets = ( PropertySheet.Base
+                    , PropertySheet.CategoryCore
+                    , PropertySheet.Amount
+                    , PropertySheet.Task
+                    , PropertySheet.Movement
+                    , PropertySheet.Price
+                    , PropertySheet.SupplyLine
+                    , PropertySheet.Discount
+                    , PropertySheet.Path
+                    , PropertySheet.FlowCapacity
+                    , PropertySheet.Predicate
+                    , PropertySheet.MappedValue
+                    , PropertySheet.Reference
+                    )
 
-    security.declareProtected( Permissions.AccessContentsInformation,
-                               'hasCellContent' )
-    def hasCellContent(self, base_id='movement'):
-      """A cell cannot have cell content itself.
-      """
-      return 0
+  security.declareProtected( Permissions.AccessContentsInformation,
+                             'hasCellContent' )
+  def hasCellContent(self, base_id='movement'):
+    """A cell cannot have cell content itself.
+    """
+    return 0
 
-    # Override getQuantityUnitXXX to negate same methods defined in
-    # Amount class. Because cell must acquire quantity unit from line
-    # not from resource.
-    security.declareProtected( Permissions.AccessContentsInformation,
-                               'getQuantityUnitValue')
-    def getQuantityUnitValue(self):
-      return self.getParentValue().getQuantityUnitValue()
+  # Override getQuantityUnitXXX to negate same methods defined in
+  # Amount class. Because cell must acquire quantity unit from line
+  # not from resource.
+  security.declareProtected( Permissions.AccessContentsInformation,
+                             'getQuantityUnitValue')
+  def getQuantityUnitValue(self):
+    return self.getParentValue().getQuantityUnitValue()
 
-    security.declareProtected( Permissions.AccessContentsInformation,
-                               'getQuantityUnit')
-    def getQuantityUnit(self, checked_permission=None):
-      return self.getParentValue().getQuantityUnit(checked_permission=checked_permission)
+  security.declareProtected( Permissions.AccessContentsInformation,
+                             'getQuantityUnit')
+  def getQuantityUnit(self, checked_permission=None):
+    return self.getParentValue().getQuantityUnit(checked_permission=checked_permission)
diff --git a/product/ERP5/Document/SupplyLine.py b/product/ERP5/Document/SupplyLine.py
index 49d1f7f545..fb03b50f4a 100644
--- a/product/ERP5/Document/SupplyLine.py
+++ b/product/ERP5/Document/SupplyLine.py
@@ -37,208 +37,208 @@ from Products.ERP5Type.Utils import convertToUpperCase
 
 
 class SupplyLine(Path, Amount, XMLMatrix):
-    """A Supply Line is a path to define price
-    """
-
-    meta_type = 'ERP5 Supply Line'
-    portal_type = 'Supply Line'
-    add_permission = Permissions.AddPortalContent
-    isPredicate = ConstantGetter('isPredicate', value=True)
-
-    # Declarative security
-    security = ClassSecurityInfo()
-    security.declareObjectProtected(Permissions.AccessContentsInformation)
-
-    # Declarative properties
-    property_sheets = ( PropertySheet.Base
-                      , PropertySheet.XMLObject
-                      , PropertySheet.CategoryCore
-                      , PropertySheet.Amount
-                      , PropertySheet.Task
-                      , PropertySheet.Arrow
-                      , PropertySheet.Movement
-                      , PropertySheet.Price
-                      , PropertySheet.SupplyLine
-                      , PropertySheet.VariationRange
-                      , PropertySheet.Path
-                      , PropertySheet.FlowCapacity
-                      , PropertySheet.Predicate
-                      , PropertySheet.Comment
-                      , PropertySheet.Reference
-                      )
-
-    #############################################
-    # Pricing methods
-    #############################################
-    security.declareProtected(Permissions.AccessContentsInformation,
-                              'getPrice')
-    def getPrice(self):
-      # FIXME: this have to be done in an interaction wf
-      if getattr(self, 'price', None) is None:
-        self.price = 0.0
-      # Return the price
-      return self.price
-
-    security.declareProtected(Permissions.AccessContentsInformation,
-                              'getTotalPrice')
-    def getTotalPrice(self):
-      """
-        Returns the totals price for this line
-      """
-      quantity = self.getQuantity() or 0.0
-      price = self.getPrice() or 0.0
-      return quantity * price
-
-    def _getPrice(self, context):
-       return 0.0
-
-    def _getTotalPrice(self, context):
-      return 0.0
-
-    security.declareProtected(Permissions.AccessContentsInformation,
-                              'isAccountable')
-    def isAccountable(self):
-      """Supply Line are not accounted.
-      """
-      return 0
-
-    #############################################
-    # Predicate method
-    #############################################
-    asPredicate = Path.asPredicate
-
-    #############################################
-    # XMLMatrix methods
-    # XXX to be removed if possible
-    #############################################
-    security.declareProtected(Permissions.AccessContentsInformation,
-                              'hasCellContent')
-    def hasCellContent(self, base_id='path'):
-      """
-          This method can be overriden
-      """
-      return XMLMatrix.hasCellContent(self, base_id=base_id)
-
-    security.declareProtected(Permissions.AccessContentsInformation,
-                              'getCellValueList' )
-    def getCellValueList(self, base_id='path'):
-      """
-          This method can be overriden
-      """
-      return XMLMatrix.getCellValueList(self, base_id=base_id)
-
-    security.declareProtected(Permissions.AccessContentsInformation, 'getCell')
-    def getCell(self, *kw , **kwd):
-      """
-          This method can be overriden
-      """
-      kwd.setdefault('base_id', 'path')
-      return XMLMatrix.getCell(self, *kw, **kwd)
-
-    security.declareProtected(Permissions.ModifyPortalContent, 'newCell')
-    def newCell(self, *kw, **kwd):
-      """
-          This method creates a new cell
-      """
-      kwd.setdefault('base_id', 'path')
-      return XMLMatrix.newCell(self, *kw, **kwd)
-
-    ############################################################
-    # Quantity predicate API
-    ############################################################
-    security.declareProtected(Permissions.AccessContentsInformation,
-                              'getQuantityPredicateIdList')
-    def getQuantityPredicateIdList(self, price_parameter):
-      """
-        Return predicate id related to a price parameter.
-      """
-      predicate_id_start_with = "quantity_range_"
-      if price_parameter not in ("base_price", "slice_base_price"):
-        predicate_id_start_with = "%s_%s" % \
-            (price_parameter, predicate_id_start_with)
-      # XXX Hardcoded portal type name
-      predicate_list = self.objectValues(portal_type='Predicate',
-              sort_on=('int_index', 'id'))
-      predicate_list.sort(key=lambda p: p.getIntIndex() or p.getId())
-      predicate_id_list = [x.getId() for x in predicate_list]
-      result = [x for x in predicate_id_list \
+  """A Supply Line is a path to define price
+  """
+
+  meta_type = 'ERP5 Supply Line'
+  portal_type = 'Supply Line'
+  add_permission = Permissions.AddPortalContent
+  isPredicate = ConstantGetter('isPredicate', value=True)
+
+  # Declarative security
+  security = ClassSecurityInfo()
+  security.declareObjectProtected(Permissions.AccessContentsInformation)
+
+  # Declarative properties
+  property_sheets = ( PropertySheet.Base
+                    , PropertySheet.XMLObject
+                    , PropertySheet.CategoryCore
+                    , PropertySheet.Amount
+                    , PropertySheet.Task
+                    , PropertySheet.Arrow
+                    , PropertySheet.Movement
+                    , PropertySheet.Price
+                    , PropertySheet.SupplyLine
+                    , PropertySheet.VariationRange
+                    , PropertySheet.Path
+                    , PropertySheet.FlowCapacity
+                    , PropertySheet.Predicate
+                    , PropertySheet.Comment
+                    , PropertySheet.Reference
+                    )
+
+  #############################################
+  # Pricing methods
+  #############################################
+  security.declareProtected(Permissions.AccessContentsInformation,
+                            'getPrice')
+  def getPrice(self):
+    # FIXME: this have to be done in an interaction wf
+    if getattr(self, 'price', None) is None:
+      self.price = 0.0
+    # Return the price
+    return self.price
+
+  security.declareProtected(Permissions.AccessContentsInformation,
+                            'getTotalPrice')
+  def getTotalPrice(self):
+    """
+      Returns the totals price for this line
+    """
+    quantity = self.getQuantity() or 0.0
+    price = self.getPrice() or 0.0
+    return quantity * price
+
+  def _getPrice(self, context):
+    return 0.0
+
+  def _getTotalPrice(self, context):
+    return 0.0
+
+  security.declareProtected(Permissions.AccessContentsInformation,
+                            'isAccountable')
+  def isAccountable(self):
+    """Supply Line are not accounted.
+    """
+    return 0
+
+  #############################################
+  # Predicate method
+  #############################################
+  asPredicate = Path.asPredicate
+
+  #############################################
+  # XMLMatrix methods
+  # XXX to be removed if possible
+  #############################################
+  security.declareProtected(Permissions.AccessContentsInformation,
+                            'hasCellContent')
+  def hasCellContent(self, base_id='path'):
+    """
+        This method can be overriden
+    """
+    return XMLMatrix.hasCellContent(self, base_id=base_id)
+
+  security.declareProtected(Permissions.AccessContentsInformation,
+                            'getCellValueList' )
+  def getCellValueList(self, base_id='path'):
+    """
+        This method can be overriden
+    """
+    return XMLMatrix.getCellValueList(self, base_id=base_id)
+
+  security.declareProtected(Permissions.AccessContentsInformation, 'getCell')
+  def getCell(self, *kw , **kwd):
+    """
+        This method can be overriden
+    """
+    kwd.setdefault('base_id', 'path')
+    return XMLMatrix.getCell(self, *kw, **kwd)
+
+  security.declareProtected(Permissions.ModifyPortalContent, 'newCell')
+  def newCell(self, *kw, **kwd):
+    """
+        This method creates a new cell
+    """
+    kwd.setdefault('base_id', 'path')
+    return XMLMatrix.newCell(self, *kw, **kwd)
+
+  ############################################################
+  # Quantity predicate API
+  ############################################################
+  security.declareProtected(Permissions.AccessContentsInformation,
+                            'getQuantityPredicateIdList')
+  def getQuantityPredicateIdList(self, price_parameter):
+    """
+      Return predicate id related to a price parameter.
+    """
+    predicate_id_start_with = "quantity_range_"
+    if price_parameter not in ("base_price", "slice_base_price"):
+      predicate_id_start_with = "%s_%s" % \
+          (price_parameter, predicate_id_start_with)
+    # XXX Hardcoded portal type name
+    predicate_list = self.objectValues(portal_type='Predicate',
+            sort_on=('int_index', 'id'))
+    predicate_list.sort(key=lambda p: p.getIntIndex() or p.getId())
+    predicate_id_list = [x.getId() for x in predicate_list]
+    result = [x for x in predicate_id_list \
               if x.startswith(predicate_id_start_with)]
-      return result
-
-    security.declareProtected(Permissions.AccessContentsInformation,
-                              'getQuantityPredicateValueList')
-    def getQuantityPredicateValueList(self, price_parameter):
-      """
-        Return predicate related to a price parameter.
-      """
-      result = [self[x] for x in \
-              self.getQuantityPredicateIdList(price_parameter)]
-      return result
-
-    security.declareProtected(Permissions.AccessContentsInformation,
-                              'getQuantityStepList')
-    def getQuantityStepList(self, *args, **kw):
-      """
-        Return predicate step related to a price_parameter
-      """
-      # We need to keep compatibility with generated accessor
-      price_parameter = kw.get('price_parameter', "base_price")
-      if price_parameter in ("base_price", "slice_base_price"):
-        method_name = "_baseGetQuantityStepList"
-      else:
-        method_name = 'get%sList' % \
-                     convertToUpperCase("%s_quantity_step" % price_parameter)
-      return getattr(self, method_name)() or []
-
-    security.declareProtected(Permissions.ModifyPortalContent,
-                              'updateQuantityPredicate')
-    def updateQuantityPredicate(self, price_parameter):
-      """
-        Update the quantity predicate for this price parameter
-      """
-      quantity_step_list = self.getQuantityStepList(price_parameter=price_parameter)
-      unused_predicate_id_set = self.getQuantityPredicateIdList(price_parameter)
-      if quantity_step_list:
-        quantity_step_list.sort()
-        quantity_step_list = [None] + quantity_step_list + [None]
-        getTitle = getattr(
-          self,
-          'SupplyLine_getTitle',
-          lambda min, max: (
-            (
-              '' if min is None else '%s <= ' % (min, )
-            ) + 'quantity' + (
-              '' if max is None else ' < %s' % (max, )
-            )
-          ),
-        )
-        predicate_id_start_with = "quantity_range"
-        if price_parameter not in ("base_price", "slice_base_price"):
-          predicate_id_start_with = "%s_%s" % (
-            price_parameter,
-            predicate_id_start_with,
+    return result
+
+  security.declareProtected(Permissions.AccessContentsInformation,
+                            'getQuantityPredicateValueList')
+  def getQuantityPredicateValueList(self, price_parameter):
+    """
+      Return predicate related to a price parameter.
+    """
+    result = [self[x] for x in \
+            self.getQuantityPredicateIdList(price_parameter)]
+    return result
+
+  security.declareProtected(Permissions.AccessContentsInformation,
+                            'getQuantityStepList')
+  def getQuantityStepList(self, *args, **kw):
+    """
+      Return predicate step related to a price_parameter
+    """
+    # We need to keep compatibility with generated accessor
+    price_parameter = kw.get('price_parameter', "base_price")
+    if price_parameter in ("base_price", "slice_base_price"):
+      method_name = "_baseGetQuantityStepList"
+    else:
+      method_name = 'get%sList' % \
+                   convertToUpperCase("%s_quantity_step" % price_parameter)
+    return getattr(self, method_name)() or []
+
+  security.declareProtected(Permissions.ModifyPortalContent,
+                            'updateQuantityPredicate')
+  def updateQuantityPredicate(self, price_parameter):
+    """
+      Update the quantity predicate for this price parameter
+    """
+    quantity_step_list = self.getQuantityStepList(price_parameter=price_parameter)
+    unused_predicate_id_set = self.getQuantityPredicateIdList(price_parameter)
+    if quantity_step_list:
+      quantity_step_list.sort()
+      quantity_step_list = [None] + quantity_step_list + [None]
+      getTitle = getattr(
+        self,
+        'SupplyLine_getTitle',
+        lambda min, max: (
+          (
+            '' if min is None else '%s <= ' % (min, )
+          ) + 'quantity' + (
+            '' if max is None else ' < %s' % (max, )
           )
-        for i in range(len(quantity_step_list) - 1):
-          min_quantity = quantity_step_list[i]
-          max_quantity = quantity_step_list[i + 1]
-          predicate_id = '%s_%s' % (predicate_id_start_with, i)
-          try:
-            predicate_value = self[predicate_id]
-          except KeyError:
-            # XXX Hardcoded portal type name
-            predicate_value = self.newContent(
-              id='%s_%s' % (predicate_id_start_with, i),
-              portal_type='Predicate',
-              int_index=i + 1,
-            )
-          else:
-            unused_predicate_id_set.remove(predicate_id)
-          predicate_value.setTitle(getTitle(min=min_quantity, max=max_quantity))
-          predicate_value.setCriterionPropertyList(('quantity', ))
-          predicate_value.setCriterion(
-            'quantity',
-            min=min_quantity,
-            max=(None if price_parameter == 'slice_base_price' else max_quantity),
+        ),
+      )
+      predicate_id_start_with = "quantity_range"
+      if price_parameter not in ("base_price", "slice_base_price"):
+        predicate_id_start_with = "%s_%s" % (
+          price_parameter,
+          predicate_id_start_with,
+        )
+      for i in range(len(quantity_step_list) - 1):
+        min_quantity = quantity_step_list[i]
+        max_quantity = quantity_step_list[i + 1]
+        predicate_id = '%s_%s' % (predicate_id_start_with, i)
+        try:
+          predicate_value = self[predicate_id]
+        except KeyError:
+          # XXX Hardcoded portal type name
+          predicate_value = self.newContent(
+            id='%s_%s' % (predicate_id_start_with, i),
+            portal_type='Predicate',
+            int_index=i + 1,
           )
-      if unused_predicate_id_set:
-        self.deleteContent(unused_predicate_id_set)
+        else:
+          unused_predicate_id_set.remove(predicate_id)
+        predicate_value.setTitle(getTitle(min=min_quantity, max=max_quantity))
+        predicate_value.setCriterionPropertyList(('quantity', ))
+        predicate_value.setCriterion(
+          'quantity',
+          min=min_quantity,
+          max=(None if price_parameter == 'slice_base_price' else max_quantity),
+        )
+    if unused_predicate_id_set:
+      self.deleteContent(unused_predicate_id_set)
diff --git a/product/ERP5/mixin/builder.py b/product/ERP5/mixin/builder.py
index 7667120ffe..a1df524fca 100644
--- a/product/ERP5/mixin/builder.py
+++ b/product/ERP5/mixin/builder.py
@@ -791,9 +791,9 @@ class BuilderMixin(XMLObject, Amount, Predicate):
       category_index_dict[i.getId()] = i.getIntIndex()
 
     def sort_movement_group(a, b):
-        return cmp(category_index_dict.get(a.getCollectOrderGroup()),
-                   category_index_dict.get(b.getCollectOrderGroup())) or \
-               cmp(a.getIntIndex(), b.getIntIndex())
+      return cmp(category_index_dict.get(a.getCollectOrderGroup()),
+                 category_index_dict.get(b.getCollectOrderGroup())) or \
+             cmp(a.getIntIndex(), b.getIntIndex())
     if portal_type is None:
       portal_type = self.getPortalMovementGroupTypeList()
     movement_group_list = [x for x in self.contentValues(filter={'portal_type': portal_type}) \
-- 
2.30.9