Most Powerful Open Source ERP

How To Use Acquisition

How To showing how to use programmable acquisition to access and use properties of parent objects.
  • Last Update:2016-02-11
  • Version:001
  • Language:en

Every coder is more or less familiar with a concept of "inheritance": if object A has a certain property or method, and object B "is-a-kind-of" A, the object B has the same property, unless it overwrites it. This is simple.

Table of Contents


Zope users should know the concept of "acquisition": if object B is contained in A, then if you ask B about a certain property which it does not have, it "acquires" this property from A. Simple at the first glance, can be used in mindbogglingly complicated ways.

ERP5 extends the concept by allowing the developer to define which property is acquired from which object and in what way. This is called "programmable acquisition". It allows for easy implementation of statements like:

A person works in the same industry as the company he is working for


A child has the same nationality as his parent, unless we explicitly say he has a different one

The property can be acquired from any object which is in any way related to the object; the developer can decide:

  • what relation is used to find the object from which we aquire (see how to configure Relation String Field for some information about relations)
  • what portal type we want to acquire from
  • what accessor is to be used on the "source" object

and define some more precise settings, which are described below.

The acquisition can be defined in a Property Sheet, to be used only by objects which use it, or site-wide by configuring a base category (e.g. region).

In many cases, in practically boils down to setting a default value for a property; yes, it can be done by putting a simple instruction in TALES tab on a field in Formulator - but by using programmable acquisition we work on the data model, not on GUI, thus maintaining a clean separation between Model and View.

Simple Example

This is a simple, real-life example, from a running CRM implementation. The properties being recorded are:

  • when did we have the first contact with an Organisation
  • when did we first meet a Person
  • where do we know them from

The first two information are recorded as a property "first_contact_date" in a Property Sheet, which is defined as follows:

{   'id'                         : 'first_contact_date'
    , 'description'              : 'Date of first contact.'
    , 'type'                     : 'date'
    , 'acquisition_base_category': ( 'subordination', )
    , 'acquisition_portal_type'  : ( 'Organisation', 'Career')
    , 'acquisition_accessor_id'  : 'getFirstContactDate'
    , 'acquisition_copy_value'   : 0
    , 'acquisition_mask_value'   : 1
    , 'acquisition_sync_value'   : 0
    , 'mode'                     : 'w'

What this says is:

The date we first met a Person is the same as the date we first met the company 
he is working for, unless we met him some other time.

This is useful, because if we meet many people from the same company at the same time, we have to enter the date only once - for the Organisation, and all the Persons will acquire it. If we meet somebody from the same company later on, we can set another date for this Person only, and it will "mask" the acquired value (because we set acquisition_mask_value to 1).

The acquisition uses 'subordination' base category to find the Organisation; it is a bit more complicated because it has to go two steps, through a Career object, so we have to specify two portal types as acquisition_portal_type.

The information about where do we know them from is recorded as membership in an "origin" category, which has the same settings as the first_contact_date property, but is configured through portal_categories tool, and has acquisition_copy_value set to 1, because we want to use it for listbox' domain tree.

Acquisition parameters

  • acquisition_base_category:
    Where we are looking for the "source" object. Usually a relation name - one of base categories. Can also be 'object', then we acquire from self.
  • acquisition_object_id:
    Instead of looking for an object related by a given category, we can acquire from a contained object of a given id.
  • acquisition_portal_type:
    This limits the search for a property source to objects of certain portal types.
  • fallback_base_category:
    What category to search if the above fails (XXX not sure).
  • acquisition_accessor_id:
    What property we want to get from "source" (which accessor is to be run on it). This is used only in Property Sheets, not in base category configuration, because accessor for category membership is derived from category name.
  • alt_accessor_id:
    A list of alternative accessor id; if you make it a tuple containing one item - the one you gave as acquisition_accessor_id - it will work. (XXX real use for it?)
  • acquisition_copy_value:
    Do we want to copy the property value to the local object, or do we rather acquire it every time it is needed. In most cases it does not matter (maybe it has some impact on performance?), with one exception: if the property being acquired is a category membership, and you want to build a listbox domain tree using this property, then you need to have a local "copy" of the property, so set it to 1. (XXX probably needed for searching too)
  • acquisition_mask_value:
    Does locally set value overwrite the acquired one - required if you want it editable. If you set value locally, it will overwrite the acquired value; when you delete it or set to an empty value, you'll get the acquired value again.
  • acquisition_sync_value:
    not used
  • acquisition_append_value:
    not used
  • category_type:
    not used
  • mode:
    Not used (though must be present in property sheet).

A special case - "content" data type

Instead of 'string', 'int', 'float' or 'date', you can set the type of property you are defining to 'content'. This is for special cases, when you want to create a content object to store the property you are setting, and then acquire the property back from the new object.

This is best explained by looking at a Person object and how it stores, for instance, street address. When you create a Person, it has no address. If you set a street address, it is not really set on the Person object - instead there emerges a new object with id 'default_address' which has the street address you've just set. The Person object displays the new street address because it acquires the address from default_address object. Edit the street address in either of the two places - the other place will show the new value. Change the id from 'default_address' to anything else - the Person no longer shows the street address.

The key to achieve this is specify the following:

  • type: Make it 'content'.
  • : The id of content object (like default_address).
  • portal_type: Must be given, and only one, so that ERP5 knows what to create.
  • acquired_property_idA tuple of properties which will be 'synchronized' between the current object and the content object.

The system will generate special accessors, built from storage_id and acquired_property_id; for example, Person object has methods getDefaultAddressStreetAddress and setDefaultAddressStreetAddress.

Related Articles