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 ?