PropertySheets
  
  ERP systems involve many different attribute names which
  can be used in different contexts. For example, a resource
  may have a default global price. A path may define a default price
  for specific source.
  
  ERP5 uses PropertySheets in order to provide an explicit data model
  which is used independently from interfaces. The role of interfaces
  is to define common explicit behaviour which can be implemented through
  by different classes. The role of property sheets is
  to define common **attribute sets** which can be stored on instances
  of different documents.
  
  PropertySheets also hold explicit information about of the ERP data structure.
  For example, some documents may not hold themselves pricing information
  but rather acquire it through the order or the sourcing contract they depend from.
  
  ERP5 implements this feature through **explicit acquisition**. The value
  of attributes can be obtained by looking up the value of that attribute
  on another related document or by calling a method on another related document.
  
  Explicit acquisition requires to define relations between documents. The general
  model adopted by ERP5 is a **categorization** model which is equivalent to
  a DACG model (Direct Acyclic Colored Graph). Thanks to Zope acquisition, this model
  is theoretically equivalent to a relationnal model.
  
  We will explain bellow how those three concepts (attribute sets, categorization and
  explicit acquisition) relate and how to define new Property Sheets in ERP5.
  
  Attribute Sets
  
    ERP5 uses Attribute  Sets based on an approach which was originally developped
    by Max M. for its Zope Easy Product (http://www.zope.org/Members/maxm/HowTo/easyProduct).
    This approach consists in defining a set of attributes in the following way::
  
      class Organisation:
        """
          Organisation properties and categories
        """
  
        _properties = (
          { 'id'          : 'corporate_name',
            'description' : 'The official name of this organisation',
            'type'        : 'string',
            'mode'        : 'w' },
          { 'id'          : 'social_capital',
            'description' : 'The social capital of this organisation',
            'type'        : 'int',
            'mode'        : 'w' },
          { 'id'          : 'social_capital_currency',
            'description' : "The currency in which the social capital is"
                            "expressed",
            'type'        : 'string',
            'mode'        : 'w' },
        )
  
    The class *Organisation* is simply a place holder for an class attribute
    called *_properties* which holds a list of dictionnaries, each of which
    describes the *id*, the *purpose* and the *type* of an attribute. In the
    above example, the attribute *corporate_name* is of type *string*, is
    in *write mode* which means it can be read and modified and is described
    as *The official name of this organisation*.
  
    Attribute sets allow to group related attributes in a common *package*
    and reuse them to define ERP5 classes. For example, the *Organisation* class
    in ERP5 is defined as::
  
      class Organisation(Entity, MetaNode, XMLObject):
          """
            An Organisation object holds the information about
            an organisation (ex. a division in a company, a company,
            a service in a public administration).
  
            Organisation objects can contain Coordinate objects
            (ex. Telephone, Url) as well a documents of various types.
  
            Organisation objects can be synchronized accross multiple
            sites.
  
            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'
          isPortalContent = 1
          isRADContent = 1
  
          # Declarative security
          security = ClassSecurityInfo()
          security.declareObjectProtected(ERP5Permissions.AccessContentsInformation)
  
          # Declarative properties
          property_sheets = ( PropertySheet.Base
                            , PropertySheet.XMLObject
                            , PropertySheet.CategoryCore
                            , PropertySheet.DublinCore
                            , PropertySheet.Organisation
                            )
  
          # Factory Type Information
          factory_type_information = \
            {    'id'             : portal_type
               , 'meta_type'      : meta_type
               , 'description'    : """\
      An Organisation object holds the information about
      an organisation (ex. a division in a company, a company,
      a service in a public administration)."""
               , 'icon'           : 'organisation_icon.gif'
               , 'product'        : 'ERP5'
               , 'factory'        : 'addOrganisation'
               , 'immediate_view' : 'organisation_edit'
               , 'actions'        :
              ( { 'id'            : 'view'
                , 'name'          : 'View'
                , 'category'      : 'object_view'
                , 'action'        : 'organisation_edit'
                , 'permissions'   : (
                    ERP5Permissions.View, )
                }
              , { 'id'            : 'print'
                , 'name'          : 'Print'
                , 'category'      : 'object_print'
                , 'action'        : 'organisation_print'
                , 'permissions'   : (
                    ERP5Permissions.View, )
                }
              , { 'id'            : 'metadata'
                , 'name'          : 'Metadata'
                , 'category'      : 'object_edit'
                , 'action'        : 'metadata_edit'
                , 'permissions'   : (
                    ERP5Permissions.View, )
                }
              , { 'id'            : 'translate'
                , 'name'          : 'Translate'
                , 'category'      : 'object_action'
                , 'action'        : 'translation_template_view'
                , 'permissions'   : (
                    ERP5Permissions.TranslateContent, )
                }
              )
            }
  
      InitializeClass(Organisation)
  
    The reader should take notice of two ERP5 specific features in the
    class definition:
  
    - the attribute *isRADContent* is set to 1, which is used by ERP5
      to determine that a given class definition should be considered
      as an **ERP5 RAD Class** or not. The meaning of *RAD* is *Rapid
      Application Development*. Classes which set the
      the attribute *isRADContent* to 1 will automatically benefit from
      ERP5 RAD features (ex. automatic generation of accessors, automatic
      generation of default values, etc.)
  
    - the attribute *property_sheets* contains a list of PropertySheets. This
      list defines the default attributes for all instances of the *Organisation*
      class
  
    During the initialization of an **ERP5 RAD Class**, the following
    class attributes and methods will be automatically generated:
  
    - a class attribute holding a **default value**. In
      the example of the *Organisation* class, a class attribute
      named *corporate_name* is created and set to
      the default string ('').
  
    - a default **getter** accessor method if no such accessor
      is already defined by the class. In
      the example of the *Organisation* class, a method
      named *getCorporateName* is created, which returns
      the value of the *corporate_name* instance attribute.
  
    - a default **setter** accessor method if no such accessor
      is already defined by the class. In
      the example of the *Organisation* class, a method
      named *setCorporateName* is created, which changes
      the value of the *corporate_name* document attribute
      and reindexes the document. Another method
      named *_setCorporateName* is created to change
      the value of the *corporate_name* document attribute
      without reindexing it.
  
    Besides *getters* and *setters*, all ERP5 documents
    which are implemented by an **ERP5 RAD Class** can
    be modified with the **edit** method. For example::
  
      document.edit(corporate_name='ACME')
  
    is equivalent to::
  
      document.setCorporateName('ACME')
  
    The use of *edit* allows to reindex documents only once.
    For example::
  
      document.edit(corporate_name='ACME',social_capital=30000)
  
    is equivalent to::
  
      document._setCorporateName('ACME')
      document.setSocialCapital(30000)
  
    Default attribute values and types are defined in the Utils.py
    file at the root of the ERP5 Product::
  
      defaults = {
          'float'              : 0.0,
          'int'                : 0,
          'long'               : 0L,
          'date'               : DateTime(),
          'string'             : '',
          'text'               : '',
          'boolean'            : 0,
          'lines'              : [],
          'tokens'             : [],
          'selection'          : [],
          'multiple selection' : [],
      }
  
    **The use of getters and setters in ERP5 is not only recommended
    but is considered as COMPULSORY** since they provide a functional
    approach to content access and allow a more consistent implementation
    of complex interactions between documents. **NEVER ACCESS DIRECTLY
    A DOCUMENT ATTRIBUTE IN ERP5 OUTSIDE A PRIVATE METHOD.
    ONLY USE GETTERS AND SETTERS**
    
    *Implementation*: functions createDefaultAccessors and
  
  Categories
  
  
    First of all, a certain number
    of base categories is defined. Such categories can are identified by a string such
    as: region, client, skill, function, etc. Each category can hold subcategories,
    sub-subcategories, etc. For example, regions allow to categorize geography in EPR5::
  
      region/americas
      region/americas/central
      region/americas/north
      region/americas/south
      region/americas/south/brazil
      region/europe
      region/europe/central
      region/europe/central/poland
      region/europe/east
      region/europe/north
      region/europe/west
      region/europe/west/france
      region/europe/west/netherlands
  
    (we only included the countries where people contribute to ERP5 currently - if you
    contribute to ERP5 and your country is not list, let us know)
  
    Each ERP5 document holds a *categories* attribute which contains a tuple
    of category paths storever as a tuple of lines. Categories are considered
    as a *slowly evolving* in an ERP5 system.
  
    Documents which are implemented as **ERP5 RAD Class** can also benefit
    from the automatic creation of accessors in the area of categories. For example,
    the *Organisation* property sheet includes references to some default
    base categories of ERP5::
  
      class Organisation:
        """
          Organisation properties and categories
        """
  
        _properties = (
          { 'id'          : 'corporate_name',
            'description' : 'The official name of this organisation',
            'type'        : 'string',
            'mode'        : 'w' },
          { 'id'          : 'social_capital',
            'description' : 'The social capital of this organisation',
            'type'        : 'int',
            'mode'        : 'w' },
          { 'id'          : 'social_capital_currency',
            'description' : "The currency in which the social capital is"
                            "expressed",
            'type'        : 'string',
            'mode'        : 'w' },
        )
  
        _categories = ( 'role', 'group', 'activity', 'skill', 'market_segment',
                          'social_form', 'function' )
  
  
    The *_categories* attributes holds a tuple of strings which defines
    which ids of base categories should be implemented by documents of
    type Organisation. Based on this definition, the following getter and
    setter methods are defined::
  
      getRole            getDefaultRole           setRole           _setRole
      getGroup           getDefaultGroup          setGroup          _setGroup
      getActivity        getDefaultActivity       setActivity       _setActivity
      getSkill           getDefaultSkill          setSkill          _setSkill
      getMarketSegment   getDefaultMarketSegment  setMarketSegment  _setMarketSegment
      getSocialForm      getDefaultSocialForm     setSocialForm     _setSocialForm
      getFunction        getDefaultFunction       setFunction       _setFunction
  
  
    Each of these getters and setters sets or returns a list of partial path values except
    for the *Default* category getter which returns the first value in of the base
    category membership of a document.
  
    Ordered Categories
  
      Categories are ordered.
  
  
    *Implementation*: all this is implemented by createCategoryAccessors
    in Utils.py
  
  
  Categories and relations
  
    Categories allow to implement document categorization but also relations.
    In ERP5, categories are managed by a portal tool names **portal_categories**.
    Because ERP5 is based on Zope and because provides implicit acquisition,
    if an EPR5 document has for example the following path::
  
      /portal_root/organisation/3
  
    Then, this object can also be accessed as::
  
      /portal_root/portal_categories/client/organisation/3
  
    This allows to define virtual categories in a base category
    without having to create many subcategories. For example,
    the category::
  
      client/organisation/3
  
    only exists through acquisition. Including in the list of category paths
    of a document a paths such as::
  
      client/organisation/3
  
    is equivalent to creating an **arc** of coulor *client* between
    that document and the document
  
      /portal_root/organisation/3
  
    Since relaltional models are equivalent to graphs, we can see
    here that the ERP5 categorization model provides the same expressive
    power as a relational model. Base categories are the equivalent
    to a relation identifier. Arcs are created between a document
    and a virtual subcategory acquired through implicit acquisition.
  
    **All relations are therefore treated in ERP5 as categories**
  
  
  Explicit Relational Acquisition
  
  
    One of the purpose of property sheets is to create
    a complete documentation of all attributes which can be used
    in a complexe ERP5 system, and to make sure that no name conflict
    happens. We want the *getTitle* or the *getPrice* accessor to mean the same thing
    wherever it is used from.
    
    In some cases, some attribute values can be *acquired* by one
    document from another document. In the Zope environment,
    acquisition allows to get the value of an attribute of one
    document from the value of its parents. However, in ERP5,
    we have multiple hierarchies. We may want to get the telephone
    of a person based on the office he/she is working (which is a region
    based tree) while we want to get the name of his product line 
    through the division he/she belongs to (this is a product line tree).
  
    Explicit Relational Acquisition provides a simple way to implement
    this feature. Let us look at the example bellow::
  
  
      class SalesOpportunity:
        """
          Sales Opportunity properties and categories
        """
  
        _properties = (
          { 'id'          : 'client_organisation',
            'description' : 'The organisation involved',
            'type'        : 'string',
            'acquisition' : {
                              'base_category' : ('client',),
                              'portal_type'   : ('Organisation',),
                              'copy_value'    : 0,
                              'accessor_id'   : 'getTitle',
                              'depends'       : None
                              },
            'mode'        : 'w' },
          { 'id'          : 'client_person',
            'description' : "The contact person involved",
            'type'        : 'string',
            'acquisition' : {
                              'base_category' : ('client',),
                              'portal_type'   : ('Person',),
                              'copy_value'    : 0,
                              'accessor_id'   : 'getTitle',
                              'depends'       : None
                              },
            'mode'        : 'w' },
        )
  
        _categories = ('role', 'group', 'activity', 'function',
                 'social_form', 'skill', 'market_segment')
  
  
    The name of the client_person is acquired by look at documents
    of type *Person* in the *client* tree (or relation) and applying
    the getTitle method. The attribute can be calculated each time
    (copy_value is 0) or copied once for all (copy_value is 0).
  
    Accessors:
  
      _getReference
      _getReferencePath
      _getReferenceList
  
  
  FAQ
  
    Do I need to create an attribute for a category ?
  
    It depends : if some information can be in a different application,
    you need to holde the information somewhere, ....
  
  Possible scenario
  
    copy_value
    mask_value
    sync_value
  
    
  Future
    
    Atribute lookup should be put outside property sheet
    into a tool ?
