After many attemps and reading a lot here, I was finally able to set the domain of a field dynamically using a function.
I will post my findings here in case it would help anyone.
1) Domain is typically set in XML view like this :
domain="[('id', 'in', ['1','2','3','4','5'])]"
Here, I provide five ID's manually for the example.
2) Another method to set the same domain is to write a function in your model .py
Example to modify domain depending on "yourfield" :
@api.onchange("yourfield")
def _onchange_all_partner_ids(self):
res = {}
res['domain'] = {'partner_id': [('id', '=', ['1','2','3','4','5']
)], }
return res
3) Another method to provide ID's dynamicaly with a function, we can use à "virtual non-stored and computed" many2many field and use it in the view:
In .py file :
#first function retrieves a list of ID's :
@api.multi
def _get_all_partner_ids(self):
partner_id_list = []
# write some code to retrieve partners in xxxx
partner_id_list.append( xxxxx.id )
return partner_id_list
#second function is a compute for field all_partner_ids :
@api.multi
@api.depends("partner_contact_id")
def _load_all_partner_ids(self):
self.all_partner_ids = [(6,0, self._get_all_partner_ids())]
#last : define all_partner_ids many2many field
all_partner_ids = fields.Many2many('res.partner',string="All partners",compute="_load_all_partner_ids")
in .XML
In your form view, you have to add you many2many field like this :
<field name="all_partner_ids" invisible="True"/>
And you finally set domain like this :
domain="[('id', 'in', all_partner_ids and all_partner_ids[0] and all_partner_ids[0][2] or False)]"
To give a more specific example :
I wanted to set a domain on field "partner_id" based on a list of parner ID that I would compute in a function.
That list had to be retrieved recursively based on another field : partner_contact_id.
I had to do two things :
A) I began to set domain with "on_change("partner_contact_id)" compute domain, like this :
@api.multi
def _get_all_partner_ids(self):
if not self.partner_contact_id.id:
partner_id_list = []
else:
partner_id_list = []
partner_id_obj = self.env['res.partner'].search([('parent_id', '=', self.partner_contact_id.id)])
for data in partner_id_obj:
partner_id_list.append(data.id)
for ids in partner_id_list:
childs = self.env['res.partner'].search([('parent_id', '=', ids)])
for child in childs:
if child.id <> self.partner_contact_id.id and child.id not in partner_id_list:
partner_id_list.append(child.id)
return partner_id_list
@api.onchange("partner_contact_id")
def _onchange_all_partner_ids(self):
if not self._get_all_partner_ids():
res = {}
res['domain'] = {'partner_id': [],
'partner_invoice_id': [],
'partner_shipping_id': []
}
return res
else:
res = {}
res['domain'] = {'partner_id': [('id', '=', self._get_all_partner_ids())],
'partner_invoice_id': [('id', '=', self._get_all_partner_ids())],
'partner_shipping_id': [('id', '=', self._get_all_partner_ids())]
}
return res
But when I loaded an old sale order, or when I created a new one, " @api.onchange("partner_contact_id") " was not triggered and my domain was not set.
B) To solve this, I decided to compute a many2many field with my ID list :
in .py :
@api.multi
@api.depends("partner_contact_id")
def _load_all_partner_ids(self):
self.all_partner_ids = [(6,0, self._get_all_partner_ids())]
all_partner_ids = fields.Many2many('res.partner',string="All partners",compute="_load_all_partner_ids")
in xml :
<field name="all_partner_ids" invisible="True"/>
<field name="partner_id" position="attributes">
<attribute name="domain">[('id', 'in', all_partner_ids and all_partner_ids[0] and all_partner_ids[0][2] or False)]</attribute>
</field>
Hi men, i was looking for a solution like that for some days right now.
REALLY THANK FOR THE GREAT EXPLATION YOU GIVE!
Thanks for your feedback ! You're welcome !
idea: https://learnopenerp.blogspot.com/2021/03/domain-filter-one2many-child-fields-on-the-basis-of-parent-fields.html
https://learnopenerp.blogspot.com/2018/12/add-domain-on-many2many-field-in-odoo.html