Welcome to BACON:CONSULT‎ > ‎Services‎ > ‎odoo‎ > ‎

OpenERP Developments

OpenERP Module Development 

This section touches on some aspects of OpenERP module development, with some code snippets I've found quite useful. The codes is based on OpenERP version 6.1-1.


Filling many2one selection field with values based on associate fields
An often discussed challenge is how to create a drop-down menu that contains records,  based on a selection from another field. I've desribed two approaches in this section which I used for a client project. 

Method A is simpler solution than Method B. The examples are based on display of a Bill of Materials (BOM) and to display all child components of a selected BOM. 

The object used is MRP.BOM which has the following details:

Object Fields:
The following fields were used in the object:

    id:the id of the master bom record
    bom_id: the BOM id (not the same as the product_id)
    bom_line:the id of all child components
    name: name of the product
    product_id: Parent Record  

openerp object - mrp.bom


When creating new functions in OpenERP, I normally test the logic directly in python using IDLE . For me this is a quick technique for debugging. I setup an XML-RFC connection to the OpenERP database on the server. In most cases if you can get the logic working in IDLE then it should work OK in OpenERP...but in some case this is not always the case.  


Python Code - XML-RPC

import xmlrpclib


sock = xmlrpclib.ServerProxy('http://<your ip address of Openerp server>:8069/xmlrpc/object')
uid = 1
pwd = 'admin'  (normally the default user is 1 with password admin)
dbname ='<the name of the OpenERP database>'

# find all products with a BOM i.e. where bom field != blank
args = [('bom_lines', '!=', '')] #query clause
ids = sock.execute(dbname, uid, pwd, 'mrp.bom', 'search', args)
ids=sorted(ids)

# Print these records ids

print 'The following BOM Records have a BOM ID of = ',ids 

# Get the details of the child for each master bom record
# Field Product ID returns the product ID and name of the product in tht product.product table
# Which is useful to retrieve all details of the parent and child BOM too.
bom_fields = ('name', 'product_id','bom_lines' )



for i in range(len(ids)):
   
    bom_id1 = sock.execute(dbname, uid, pwd, ('mrp.bom'), 'read', ids[i], bom_fields)

    
    print '----------------------------------'
    print 'Main BOM=', ids[i], bom_id1, bom_id1['bom_lines']

    # get childs boms and show these too    
    for child1 in bom_id1['bom_lines']:
        bom_child1 = sock.execute(dbname, uid, pwd, ('mrp.bom'), 'read', child1, bom_fields)

        # Extract the parent BOM ID of this child to see if this also has a BOM    
        anewlist = bom_child1 ['product_id']
        #print 'Child=',bom_child1, 
        print '*******Child1 =',child1, ',',bom_child1 ['name'], ',', anewlist [0] , ',',   bom_child1 ['bom_lines']

        
        #print '..........',anewlist [0] # extract the id of the parent bom
        #bom_id2 = sock.execute(dbname, uid, pwd, ('mrp.bom'), 'read', anewlist [0], bom_fields)

        
            
        # get any BOMs of childs boms and show these too  (this part needs finishing)



A list of all BOMS and child components will be printed out as follows:  

Text Box

Main BOM= 46 {'product_id': [30, '[SHE100] Shelf of 100cm'], 'id': 46, 'name': 'Default BOM for Shelf of 100cm', 'bom_lines': [47, 48, 49, 50]} [47, 48, 49, 50]
*******Child1 = 47 , Side Panel , 42 , []
*******Child1 = 48 , Assembly Section , 45 , []
*******Child1 = 49 , Rear panel SHE100 , 40 , []
*******Child1 = 50 , RCK100 , 43 , []
----------------------------------
Main BOM= 51 {'product_id': [43, '[RCK100] Rack 100cm'], 'id': 51, 'name': 'RCK100', 'bom_lines': [52, 53]} [52, 53]
*******Child1 = 52 , SPAN100 , 32 , []
*******Child1 = 53 , METC000 , 47 , []
----------------------------------
Main BOM= 54 {'product_id': [32, '[SPAN100] Shelf Panel'], 'id': 54, 'name': 'SPAN100', 'bom_lines': [55]} [55]
*******Child1 = 55 , WOOD010 , 35 , []
----------------------------------
Main BOM= 56 {'product_id': [45, '[PROFIL] Assembly Section'], 'id': 56, 'name': 'Assembly Section', 'bom_lines': [57]} [57]
*******Child1 = 57 , Wood Lintel 0.25m , 46 , []
----------------------------------
Main BOM= 58 {'product_id': [40, '[RPAN100] Rear Panel SHE100'], 'id': 58, 'name': 'Rear panel SHE100', 'bom_lines': [59]} [59]
*******Child1 = 59 , WOOD002 0.25m , 33 , []
----------------------------------
Main BOM= 60 {'product_id': [31, '[SHE200] Shelf of 200cm'], 'id': 60, 'name': 'Default BOM for Shelf of 200cm', 'bom_lines': [61, 62, 63, 64, 65]} [61, 62, 63, 64, 65]
*******Child1 = 61 , Rear panel SHE200 , 41 , []
*******Child1 = 62 , Assembly Section , 45 , []
*******Child1 = 63 , Side Panel , 42 , []
*******Child1 = 64 , Shelf 200 , 44 , []
*******Child1 = 65 , Metal Cleats , 47 , []
----------------------------------
Main BOM= 66 {'product_id': [49, '[SHE100KIT] KIT Shelf of 100cm'], 'id': 66, 'name': 'Default BOM for KIT Shelf of 100cm', 'bom_lines': [67, 68]} [67, 68]
*******Child1 = 67 , Assembly Section , 45 , []
*******Child1 = 68 , Side Panel , 42 , []
----------------------------------
Main BOM= 69 {'product_id': [53, 'Orange Juice'], 'id': 69, 'name': 'Orange Juice', 'bom_lines': [70, 71, 72]} [70, 71, 72]
*******Child1 = 70 , Orange , 54 , []
*******Child1 = 71 , Sugar , 55 , []
*******Child1 = 72 , Water , 56 , []
----------------------------------
Main BOM= 73 {'product_id': [57, 'Shirt'], 'id': 73, 'name': 'Shirt', 'bom_lines': [74, 75]} [74, 75]
*******Child1 = 74 , Cloth , 58 , []
*******Child1 = 75 , Shirt Buttons , 59 , []

Method A - Defining a domain in a many2one field


This is useful if you want to restrict certain selections into a drop down list and then further restrict the selection based on the first selection.

Notice there is an icon on the right side of the fields for search, create, etc. 

Screen Shots

  • field  'bom_select_list' (BOM Master)

This list shows all products that have has a bom defined



  • field bom_select_list4 (BOM Childs)

This list only shows the bom components related the previous selection





.py file

The following fields were defined into an inherited view.

_columns = {
'bom_select_list' : fields.many2one("mrp.bom", 'BOM Master', domain="[('bom_lines','!=','')]"),

If you understand SQL,  then this is equivalent to:select * from mrp.bom where bom_lines <> "" 


This creates a new many2one drop down list which is populated as for the previous field, but the filter is based on the record select in the previous field:  ' bom_select_list:

 'bom_select_list2' : fields.many2one("mrp.bom",'Bom Child', domain="[('bom_id','=',bom_select_list)]"),

 This is equivalent to following SQL statement select * from mrp.bom where bom_id = bom_select_list


XML file


The field for 1st level selection:

<field name="bom_select_list"/>

The field for 2nd level selection:

<field name="bom_select_list2"/>

Comments


This is a quick method of achieving drop down fields. No further functions or methods need to be defined


Method B - Using a selection field


Screen Shots

This shows two fields. The first field 'Master Bom' is a many2one field with pre-filitered records of all products with a BOM defined. The changing of the field triggers and on-change event that then finds all component products related to the selected BOM master. The results is a direct dump from the dictionary. Some additional text formating would produce a tidy list.

Note: that the pull-down field has a different icon,  compared to method A above. With this method it's not possible to create or search for additional records as all records are shown in the drop-down menu. If the list is long, then it could affect performance.

Note that in the BOM child field the appropriate child boms are automatically displayed. 





.py file


Functions:


def _get_bom_selection(self, cr, uid, context=None):
obj = self.pool.get('mrp.bom')
args = [('bom_lines', '!=', '')] #set filter 
ids = obj.search(cr, uid, args)  #filters all BOMs with childs
bom_fields = ('name', 'id', 'product_id','bom_lines' )
res = obj.read(cr, uid, ids, bom_fields, context) #filters all BOMs with childs
res = [( r['id'],r['name'], r['bom_lines'],r['product_id'] ) for r in res]
return res

    def onchange_get_childbom_selection(self, cr, uid, ids, bom_select_list1_example_b, context=None):    
#get the table
a_child_list =[] #clear the bom child list
result = []
child_list2 = []
obj = self.pool.get('mrp.bom')
bom_select_list1_example_b = str(bom_select_list1_example_b)
print '>>> BOM Master: %s' %bom_select_list1_example_b
print '>>> obj: %s' % obj
#finds that one BOMs in the complete BOM table
bom_fields = ('name', 'bom_lines' )                                         #determine all field that will returned in the results
bom_id1 = obj.read(cr, uid,[bom_select_list1_example_b],bom_fields, context)
print '>>> bom_id1:',bom_id1[0]['bom_lines']  
a_value4 = []                                                                        #read the record and extract the bom_lines field
child_list = bom_id1[0]['bom_lines']
for child1 in child_list:
bom_child1 = obj.read(cr, uid, child1, ['name'], context)
a_value1 = str(bom_child1['id']) + ' '
a_value2 = str(bom_child1['name'])  
a_value3 = a_value1 + a_value2 + '\n'
a_value4 = str(a_value4) + str(a_value3)
print '.........', a_value3              #Debug Message
res = {'value': {'bom_results_list_example_b': a_value4}} 
return res

*the red values are the fields from/to  which the values are retrieved / returned  from / to
      

The . py fields     

The 1st field is of type 'selection' which can be used to trigger a function. The function _get_bom_selection  fills the selection list with records before  making the result available in the XML view. 

When a record in the XML view is changed the on_change event is triggered  and this function called  onchange_get_childbom_selection   from within the XML view. This on_change trigger is done on the XLM record (see below). Note how the value in field  'bom_select_list1_exampleb' is passed to the function, merely by using the field name. 


 _columns = {
                    'bom_select_list1_example_b' : fields.selection(_get_bom_selection,'Master BOM:'),
                    'bom_results_list_example_b' : fields.text('Bom Child'),

                    }
       

XML file

<field name="bom_select_list1_example2" on_change="onchange_get_childbom_selection(bom_select_list1_example2)"/>

This a selection field that is pre-loaded with records (see field declaration above) When a selection is made the   onchange_get_childbom_selection  method is triggered. The function searches mrp.bom for child bom records associated with the other field. The results are merely written to the text field

<field name="bom_select_list2_example2"/>




Subpages (2): OpenERP Processes xwPython
Comments