This How To describes the approach to proxify form fields to keep customisation
to a minimum by reusing existing field libraries. From Guidelines on Module Creation.
What Is A Field Library
Field library is an ERP5 form which will not be accessed from users but used as
a container of proxy fields used as template field by other fields contained in
other real forms and reports. In other words, it is a collection of template
fields of proxy fields commonly used in a business template.
Why Differentiate Field Library And Proxy Field?
Using proxy field is very useful for providing consistent UI and reducing code
duplications.
For UI
For example, comment field is one of the very common fields in whole ERP5 system
and it should be always the same size everywhere. Then we use proxy field, we can
define the height and width only once to the template field in the core field
library and the remaining is that all other comment fields just inherit the core
template field. This way, we can make consistent UI easily. If we did not use
proxy field, we had to set the same height and width for all comment fields one
by one, this is very hard to maintain.
About field library, this is a kind of buffer between the core field library in
erp5_core and the real fields to be displayed to users. We saw an example of
comment field above, it was a common field globally. But sometimes we want to
change an appearance of generic fields only in some business template. In such
case, we cannot change the core template fields, because they are used globally.
So we can change a field library of a target business template instead. Then we
can customize some fields only in a business template and this does not affect
UIs defined by other business templates.
For code reuse/abstraction
Sometimes we can find similar patterns in ERP5. For example, when we use a
RelationStringField, we often name it like "my_source_title" by following our
naming convention. And from such names, we can find a pattern. The name suggests
this field uses source category and searches document by title. And we can make
a template field for RelationStringField and put a tales expression to parse
field id and find category name and index property. Once we have such a template
field which is an abstraction of a pattern we don't have to enter a category
name and an index property name each time when adding RelationStringField,
but just using a proxy field and inherit the template field is enough. We can
omit entering various parameters and it is also useful for maintenance, future
changes. Above is a very wide range example which can apply whole ERP5, but we
can find many other small patterns in a specific context managed by single business
template like Accounting, DMS. Therefore finding a pattern and making a template
field is very practical use case of proxy field and field library.
Define once and applying to many
Sometimes we have to enter the same information in multiple places. For example,
a company has the same reference naming rule for Service and Product. In such case,
we have to apply the same input validation functionality to Product_view.my_reference
and Service_view.my_reference. Then we can say that both my_reference in Product_view
and Service_view are the same in reality. In such case, we should make a template field
in field library and reuse the field to inherit from proxy fields in both Product_view
nd Service_view. And in this way even if the input validation rule was changed, we
only have to update the template field and no need to touch proxy fields.
How To Use The Proxify Tool To Migrate Existing Business Templates To Proxy Fields
This section describes the procedure that has been used to convert erp5_base
business template to use proxy fields. The goal is to have configuration bits
shared by multiple fields defined in only one place.
Think How To Group Fields In Field Libraries
Look at existing document types and find out what information is shared by those
documents. For example, Organisations, Person both share the concept of geographical
address, which is the fields city, postal code or region. Those fields are also
used by Address portal type itself.
Create Conceptual Field Libraries
In our address example, you should create a field library named Address_viewFieldLibrary,
containing fields like my_city or my_region. Fields in the field library will be
proxyfields to level 0 fields, from Base_viewFieldLibrary, which defines the
default look of the fields.
For this you should use fields from existing business forms as a starting point,
for example, you can copy and paste Address_view/my_city as Address_viewFieldLibrary/my_city
and then use proxify tool on Address_viewFieldLibrary to make my_city a proxyfield of
the default string field from Base_viewFieldLibrary.
Fields in those "conceptual" field libraries must usually define title and description.
Use Field Libraries
Replace all fields in your business forms by fields from conceptual field library
you just created. For this you should also use the proxify tool. Fields in those
forms (like Organisation_view) should delegate almost all their values, especially
title and description which must be defined in only one place. The proxify tool
doesn't delegate values that are differents from the original field than the
target relation field.
For rare cases where the field is not shared by any other forms, you can make a
proxyfield directly to the first level field from Base_viewFieldLibrary.
Check The Result
The important rule is that your proxyfields must delegate everything possible.
Everytime a value is NOT delagated, it's an exception, and it should have an
explanation. You can use this code to display all non delegated values:
print '<html>'
skin_folder = context.getPortalObject().portal_skins.erp5_base # <- XXX configure this
for field_id, field in skin_folder.ZopeFind(skin_folder, search_sub=1, obj_metatypes=('ProxyField',)):
form = field.aq_parent
if form.getId().endswith('FieldLibrary'):
continue
field_path = '%s/%s' % (form.getId(), field.getId())
print '<a href="%s/%s/manage_main">%s</a><br/>' % (skin_folder.absolute_url(), field_path, field_path)
tf = field.getRecursiveTemplateField()
for v in tf.values:
if not field.is_delegated(v):
print " %s<br/>" % v
print
return printed