Most Powerful Open Source ERP

Technical Note on Predicate

showing how to define a predicate to provide a list of documents on web section or it's default page.
  • Last Update:2016-04-22
  • Version:001
  • Language:en

Introduction

This document will describe how to well define a predicate, and which tools are used in order to find a predicate for a particular context.

Table of Contents

What's a Mapped Value ?

A mapped value allows to define some values (properties or categories) corresponding to some conditions.

For example, you may need to define the price (the value) corresponding to a resource and a range of quantities (conditions).

The idea is to create in many places into ERP5 mapped values. Let's say you have a document, if you are looking for a value (for example a price) for this document, then a tool will look at all mapped values stored in ERP5, and it will give you all mapped values (most of the time you will get only one) where the conditions corresponds to your document, so you can know which value you can take.

The document on which you are looking for a value is usually called the "context".

If you want to define a mapped value, there are two different sets of properties to update. The first set of properties defines the Value, the second set of properties defines the conditions. There are two classes in ERP5 corresponding to the two sets, the first one is MappedValue, the second one which defines conditions is Predicate.

On each predicate, there is a method called test defined like this:

    def test(self, context,**kw):

This method returns True if the context corresponds to this predicate, or it returns False otherwise.

For example, you have a mapped value A defined like this :

    value:
      base_price : 23
    conditions:
      5 < quantity < 23
      resource = product/openbrick

Then let's say you have an order with a line (this is your context), the resource of this line is an openbrick and the quantity is 10. What is the base_price for this context ? You can look at all mapped values in your ERP5 site, then run the test method on each of them, if the result is true, you can see if the mapped values define a price. You will see later that the portal_domains tool will do all that for you.

Define the Mapped Value :

Here you will define values. There are two things to do. The first step is to define which property or category you want to specify, then you have to store values.

In order to tell which properties you want to define, you have to use the method setMappedValuePropertyList, like this :

    mapped_value.setMappedValuePropertyList(['base_price'])

In order to tell which categories you want to define, you have to use the method setMappedValueBaseCategoryList, like this:

    mapped_value.setMappedValueBaseCategoryList(['destination'])

Finally, you can store values and categories, like it is already done everywhere in ERP5, with default setters :

    mapped_value.setBasePrice(34.5)
    mapped_value.setProperty('base_price',34.5)
    mapped_value.setDefaultDestination('person/111')

Once values are stored, you may want to define conditions, we usually say that we will define the Predicate.

Define Predicates :

First, you will learn how to define conditions based on properties and then conditions based on categories.

About properties, you may want to define different types of conditions :

    - equality
    - more than
    - less than
    - a range

You first need to define what are the properties used in order to create your condition. This method is called setCriterionPropertyList and you can use it like this:

    mapped_value.setCriterionPropertyList(['quantity'])

Then, there is a method called setCriterion, defined like this :

    setCriterion(self, property, identity=None, min=None, max=None, **kw)

The name of arguments are quite obvious, it is easy to use, these are some examples:


quantity==4: mapped_value.setCriterion('quantity',identity=4)
quantity>=4: mapped_value.setCriterion('quantity',min=4)
quantity<5: mapped_value.setCriterion('quantity',max=5)
 2 < quantity < 5: mapped_value.setcriterion('quantity',min=2,max=5)

Let's define conditions on categories now. You first need to set which base categories are used for conditions. There are two possibilities for each base category. You may want that your context is a member of many categories with the same base category, or you may want that your context is a member of at least one category from a list of categories.

For the first case, you want that your context is a member of many categories for a particular base category, you will need to define the multimembership_criterion_base_category_list property and membership_criterion_category_list like this:

    mapped_value.setMultimemberhsipCriterionBaseCategoryList(['skill'])
    mapped_value.setMembershipCriterionCategoryList(['skill/a','skill/b'])

In this case, the mapped_value corresponds if the context is both member of skill/a and skill/b.

For the second case, you want that your context is a member of at least one category from a list of categories, you will need to define the membership_criterion_base_category_list property like this:

    mapped_value.setMembershipCriterionBaseCategoryList(['region'])
    mapped_value.setMembershipCriterionCategoryList(['region/europe','region/africa'])

In this second case, the mapped_value corresponds if the context is a member of region/europe or region/africa.

AND and OR logic summary

By defining membership_criterion_base_category_list you are using OR logic on chosen categories. By defining multimembership_criterion_base_category_list categories you are using AND logic.

The asPredicate Method :

There is also another way to specify some properties used for the test. The previous way of doing is quite annoying for resource, source and destination. Why should it be defined as a category on a document, and as a membership_criterion_category_list on a predicate ?

This is the same thing for the start and stop dates. So a method called asPredicate exists on predicates (and very soon on many other documents), and it will create predicate properties thanks to local properties.

If you want to define a period, you can use the newly created range parameter of properties. Indeed you can see the propertySheet of Task, and you will see that the start_date has a range value to True. This means that we can use ranges for the start_date, like this:

    setStartDateRangeMin()
    setStartDateRangeMax()
    getStartDateRangeMin()
    getStartDateRangeMax()

So if you create a predicate with a start_date_range_min property and also a start_date_range_max property, with the asPredicate method it is like to define a range criterion. Then the test method of this predicate will check that the start_date of the context is greater than the start_date_range_min and lesser than the start_date_range_max.

If you define a resource on your predicate, with the asPredicate method, this is like to define correctly membership_category_list and multimembership_criterion_category_list.

You may want to configure the list of local properties which works like a criterion property and the list of base categories which works like a membership criterion base category with 2 portal defaults values:

portal_criterion_base_category_list, default to (source,destination,resource,group)
portal_criterion_property_list, default to (start_date,stop_date)

Search Predicates

If you want to find a predicate for a particular context, you can use the portal_domains tools. There is a method call searchPredicateList defined like this:

      def searchPredicateList(self,context,test=1,sort_method=None,
                                            ignored_category_list=None,**kw)

This method will give you all predicates corresponding to your context. If a sort_method is provided, you can define in which order the list will be sorted. This is very useful if you want to sort by date or by whatever you want.

You can give many parameters, for example a portal_type. Indeed, searchPredicateList will call the portal_catalog.searchResults methods, so searchPredicateList will be able to understand any parameter that searchResults can handle.

Generate a mapped Value

The domain tool provides another method called generateMappedValue. You can use it with the same list of parameters than searchPredicateList:

    def generateMappedValue(self,context,test=1,predicate_list=None,**kw)

You can also provide a sort method and anything that portal_catalog.searchResults will be able to understand.

This method will create a new mapped value for a particular context.

If no predicate_list is provided, this method will call searchPredicateList in order to find the list of predicates corresponding to your context, then it will look at each of them for all mapped value properties and categories defined. So if you have a predicate which defines a price, and another one a discount, the new mapped value generated will have both properties.

If you have several predicates which define the same properties for a particular context, generateMappedValue will take the value of the first predicate. That's why it is sometimes very important to specify how to sort the list.

Example.

Let's say that you have several supply lines defined for a resource, each of them defines a base price for several ranges of quantities, and each of them is available only in some periods.

So on each supply line you define a start_date_range_min and a star_date_range_max, and a cell is defined for each range of quantities.

Then, let's say that you have a context: a movement of the resource for a particular quantity and a particular date.

If you want to get the right base price, for your context, the only thing you need to do is:

my_price = portal_domains.generateMappedValue(context).getProperty(base_price)

Related Articles