Here's my class:
employee_ids = fields.Many2many('hr.employee', string="Empls")
status = fields.Selection([
('draft', 'Draft'),
('done', 'Done'),
])
then in fields_view_get method i want to iterate through employee_ids and make list of each employee.
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
if context is None:
context = {}
res = super(help_desk, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar,submenu=False)
#here i want to iterate throught employee_ids and make list of each employee
for f in res['fields']:
if f == 'status':
res['fields'][f]['selection'] = #put list here
return res
how can i do it? thanks
I am supposing help_desk model have 3 fields Selection,Many2one and Many2many:
status = fields.Selection([
('draft', 'Draft'),
('done', 'Done'),
])
partner_id = fields.Many2one(comodel_name='res.partner', string='Partner')
employee_ids = fields.Many2many('hr.employee', string="Empls")
Now if you want to apply some logic so go through the below mention lines.
Well method like fields_get ,fields_view_get help us in improving UI experience by applying the filter/domain on fly.
so the code is here:
#api.model
def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
res = super(help_desk, self).fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu)
PartnerObj= self.env['res.partner']
domain = [('phone','!=',False)]# put your domain or just place blank list
partners = PartnerObj.search(domain)
if partners
for field in res['fields']:
# if field == 'partner_id':
# res['fields'][field]['domain'] = [('id', 'in', partners.ids)]
elif field == 'employee_ids':
res['fields'][field]['domain'] = [('id', 'in', partners.ids)]
elif field=='status':
# Appending the partners in status doesn't making any sense but as per your words "put list here"
res['fields'][field]['selection'] = partners and [(partner.id, partner.name) for partner in partners] or [('', '')]
return res
here i have put the domain on Many2one and Many2many and add some dynamic list based on domain inside the Selection field.
you can also refer account/models/chart_template.py.
Related
I have this model and method in odoo
class stock_transfer_details_items(models.TransientModel):
_inherit = 'stock.transfer_details_items'
lot_id = fields.Many2one('stock.production.lot', 'Lot/Serial Number')
q_auth = fields.Boolean(related='lot_id.q_auth', string="Quality Auth.")
needs_auth = fields.Boolean(related='product_id.needs_quality_auth')
#api.onchange("needs_auth")
def _onchange_NQA_domain(self):
domain = {'domain': {'lot_id': [('product_id', '=', rec.product_id.id)]}}
if rec.needs_auth:
domain = {'domain': {'lot_id': [('q_auth', '!=', False), ('product_id', '=', rec.product_id.id)]}}
return domain
All this code is working ok. My problem is that I need to apply this domain when the wizard opens. #api.depends means that the value changes depending on the value of needs_auth but it actually don't work if I don't interact with the checkbox of needs_auth. I want to apply the domain when the view is open and not interact with the needs_auth field, because the user will not be allow to interact.
The solution for your problem is to use fields_view_get.
It was already mentioned before here : https://stackoverflow.com/a/54825093/9796551
#api.model
def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
res = super(stock_transfer_details_items,self).fields_view_get(view_id=view_id,
view_type=view_type,
toolbar=toolbar,
submenu=submenu)
if view_type == 'form':
domain = "["
# Your condition...
domain += "]"
res['fields']['lot_id']['domain'] = domain
return res
I am trying to dynamically change the values of a many2many field products_ids based on multiple onchange functions of other fields (e.g. brand_id and origin_id).
So far everything is working great and it does show the expected values, but once i hit the save button the values of the many2many field disappear
class CustomModifyPrice(models.Model):
brand_id = fields.Many2many(comodel_name="custom.brand", string="Brand", required=False, )
origin_id = fields.Many2many(comodel_name="custom.country", string="Origin", required=False, )
product_ids = fields.Many2many(comodel_name="custom.product", string="Products", readonly=True, )
search_terms = {}
product_ids_list = []
#api.onchange('brand_id')
def onchange_change_brand(self):
for rec in self:
product_brands = []
for prod_brand in rec.brand_id:
product_brands.append(prod_brand.id)
rec.search_terms["product_brands"] = product_brands
rec.get_products()
#api.onchange('origin_id')
def onchange_change_origin(self):
for rec in self:
product_origins = []
for prod_origin in rec.origin_id:
product_origins.append(prod_origin.id)
rec.search_terms["product_origins"] = product_origins
rec.get_products()
def get_products(self):
domain = []
self.product_ids_list = []
if 'product_brands' in self.search_terms:
product_brands = self.search_terms['product_brands']
if product_brands:
tuple1 = ('brand_id', 'in', product_brands)
domain.append(tuple1)
if 'product_origins' in self.search_terms:
product_origins = self.search_terms['product_origins']
if product_origins:
tuple1 = ('country_id', 'in', product_origins)
domain.append(tuple1)
if domain:
products = self.env['custom.product'].search(domain)
if products.ids:
for prod in products:
self.product_ids_list.append(prod.id)
self.product_ids = [(6, False, self.product_ids_list)]
Make sure force_save="1" is placed as an attribute in your field (xml file)
I added a module "Base Candidat" in this module base.candidat I want to open the applications of a specific candidate (that have the same email as the candidate ) I added this function to base_candidat.py
class base_candidat(osv.osv):
_columns = {
'candidat_name': fields.char('Candidat', size=128, required=True),
'blacklist': fields.boolean('Blacklist'),
'email_candidat': fields.char('Email', size=32),
'mobile': fields.char('Mobile', size=32),
'priority': fields.selection(AVAILABLE_PRIORITIES, 'Appreciation'),
'user_id': fields.many2one('res.users', 'Responsable'),
'specialite': fields.many2one('base.candidat.specialite', 'Spécialité'),
}
def action_get_applications(self, cr, uid, ids, context=None):
modelDataClass = self.pool.get('ir.model.data')
hrApplicantClass = self.pool.get('hr.applicant')
model, action_id = modelDataClass.get_object_reference(cr, uid, 'hr_recruitment', 'action_hr_job_applications')
action = self.pool.get(model).read(cr, uid, action_id, context=context)
email_ids = self.browse(cr, uid, ids[0], context).email_candidat
candidature_ids = hrApplicantClass.search(cr, uid, [('email_from', '=', email_ids)], context=context)
action['context'] = {'default_res_model': self._name, 'default_res_id': ids[0]}
action['domain'] = str([('candidature_ids', 'in', email_ids)])
return action
I have no result . Any suggestions??
If I know well, 'action' is an unallowed variable name. If you use it, the program will be not consistent. (Same situation with 'active')
In my opinion, this domain expression is not correct in this line:
action['domain'] = str([('candidature_ids', 'in', email_ids)])
Because this mean you model has candidature_ids field, but the model has not, so this condition can't satisfied, so you can't see any record, when you use it.Another problem with your conception, email_ids is string (not number, so the variable name is deceptive)and candidature_ids is a list of numbers.
In my opinion, you want to write a function field. This is an example code, it may not the optimum performance, but I think it will help you understanding the essential of function field:
def _getApplications(self, cr, uid, ids):
hrApplicantClass = self.pool.get('hr.applicant')
# get all of candidate
candidateDatas = self.read(cr, uid, ids, ['email_candidat'])
# collect all of emails
emails = []
for candidate in candidateDatas:
if candidate['email_candidat'] not in emails:
emails.append(candidate['email_candidat'])
# get all of relevant applicant
applicantIds = hrApplicantClass.search(cr, uid, [('email_from', 'in', emails)])
applicantDatas = hrApplicantClass.read(cr, uid, applicantIds, ['email_from'])
# pair base_candidat id with application id
retVal = {}
for candidate in candidateDatas:
retVal[candidate['id']] = []
if not candidate['email_candidat']:
continue
else:
for applicant in applicantDatas:
if applicant['email_from'] == candidate['email_candidat']:
retVal[candidate['id']].append(applicant['id'])
return retVal
# the field definition
'application_ids': fields.function(_getApplications, method=True, type='one2many', 'Applications')
Below code is asset inherited class . Here i will add 'place' field with 'Karn/Bang/Kengeri' and 'karn/bang/malleshwaram' for 'Karn/Bang/Kengeri' will add 'asset_catg_id' with A and B. then for 'karn/bang/malleshwaram' with Y and Z.
Now at calander inherited class . if i select 'place' with 'Karn/Bang/Kengeri' then next field 'asset_catg_id' i have to get only A and B drop down list. if again 'karn/bang/malleshwaram' then i have to get only Y,Z options . and previous selected 'asset_catg_id' values should get deleted . i have tried with domain filter option got keyvalue error
class asset_asset(osv.osv):
_inherit = "asset.asset"
#_name = "asset_asset"
_rec_name= "folio_num"
_columns = {
'name': fields.char('Asset Name', size=64),
'place': fields.many2one('asset.parentlocation', 'Location'),
'asset_catg_id' : fields.many2one('asset.catg', 'Asset Catg Selection', select=True, required=True),}
class asset_catg(osv.Model):
_name="asset.catg"
_rec_name='name1'
_description="Define Asset Catgs"
_columns={ 'name1':fields.char('Asset Catg Names',size=64,required=True),}
asset_catg()
class asset_parentlocation(osv.osv):
_name="asset.parentlocation"
_rec_name="location_name"
_columns = {
'location_name' : fields.char('Asset Location', required=True),
'parent_location' : fields.many2one('asset.parentlocation','Parent Location'),
'nameee':fields.many2one('ir.attachment','Attachments'),}
def name_get(self, cr, uid, ids, context=None):
if context is None:
context = {}
if not ids:
return []
reads = self.read(cr, uid, ids, ['location_name','parent_location'], context=context)
res = []
for record in reads:
name = record['location_name']
if record['parent_location']:
name = record['parent_location'][1]+' / '+name
res.append((record['id'], name))
return res
**Following code is calendar inherited class**
class calendar_event(osv.osv):
_inherit = "calendar.event"
_rec_name = 'number'
_columns = {
'number' : fields.char('Meeting ID',readonly=1),
#'place' : fields.many2one('stock.location','Substation Location',),
'place' : fields.many2one('asset.parentlocation','Substation Location',),
#'location' : fields.selection(STATUS_SELECTION,'Location', default='Board A'),
'asset_catg_id' : fields.many2one('asset.catg','Asset Catg Selection', domain="[('asset_catg_id', '=',place)]"),}
First your domain is wrong in a principle. Domain is what is "inside" a field, in other words in its model (for example field name or id in asset.catg model). So you should fix that one first.
If domain depends on another field, you can use onchange method to return domain (used placeholder place_id). Like this:
#api.onchange('place')
def onchange_place(self):
res = {}
if self.place:
res['domain'] = {'asset_catg_id': [('place_id', '=', self.place.id)]}
return res
P.S. This is example with new v8 api, but same principle applies to old api (you then don't nee to use decorator, also add cr, uid, ids on method and call it through your view. All of this not needed for new api). As it looks like you are still developing on old api.
Update
For old api:
def onchange_place(self,cr, uid, ids, place, context=None):
res = {}
if self.place: #on old api it will return id, instead of record
res['domain'] = {'asset_catg_id': [('place_id', '=', self.place)]}
return res
And then in your view (don't know what kind of view you are using):
<field name="place" on_change="onchange_place(place)"/>
Still you need to define some field in asset.catg so it would be used to match place field. For example:
'place_id': fields.many2one('asset.parentlocation', 'Place')
And then when you define asset category, you set which Place it should belong to. Then when you choose place calendar.event, onchange method will set domain on asset_catg_id field correctly.
In product.template model, I have a one2many field(attribute_line_ids) making reference the following class:
class product_attribute_line(osv.osv):
_name = "product.attribute.line"
_rec_name = 'attribute_id'
_columns = {
'product_tmpl_id': fields.many2one('product.template', 'Product Template', required=True, ondelete='cascade'),
'attribute_id': fields.many2one('product.attribute', 'Attribute', required=True, ondelete='restrict'),
'value_ids': fields.many2many('product.attribute.value', id1='line_id', id2='val_id', string='Product Attribute Value'),
}
On the other hand, in product.template created a many2one field making reference to a new model(comun.denominador):
'cm_id' : fields.many2one('comun.denominador','Comun denominador', select=True, ondelete='cascade')
So that when I select 'cm_id' it brings records to product.template from comun.denominador by means of on_change function.
This is my onchange_function:
def on_change_cm_id(self,cr, uid, ids,cm_id,context=None):
context=context or {}
attributes_product_template = []
value = {}
if ids:
old_note_ids = self.pool.get('product.attribute.line').search(cr, uid,[('product_tmpl_id','in',ids)])
self.pool.get('product.attribute.line').unlink(cr, uid, old_note_ids)
attribute_cm_ids = []
attribute_cm_ids = self.pool.get('attribute.comun.denominador.line').search(cr, uid, [('comun_denominador_id', '=', cm_id)])
for attribute_id in self.pool.get('attribute.comun.denominador.line').read(cr, uid, attribute_cm_ids, ['attribute_comun_denominador_id', 'value_comun_denominador_ids']):
attributes_product_template.append((0,0,{'value_ids':attribute_id['value_comun_denominador_ids'][0],'attribute_id':attribute_id['attribute_comun_denominador_id'][0]}))
value.update(attribute_line_ids=attributes_product_template)
return {'value':value}
These models store information in comun.denominador:
class product_attribute_line(osv.osv):
_name = "attribute.comun.denominador.line"
_rec_name = 'attribute_comun_denominador_id'
_columns = {
'comun_denominador_id': fields.many2one('comun.denominador', 'Comun denominador', required=True, ondelete='cascade'),
'attribute_comun_denominador_id': fields.many2one('product.attribute', 'Attribute', required=True, ondelete='restrict', readonly=True),
'value_comun_denominador_ids': fields.many2many('product.attribute.value', id1='line_id', id2='val_id', string='Product Attribute Value'),
}
class comun_denominador(osv.osv):
_name='comun.denominador'
_rec_name='comun_denominador'
_columns = {
'comun_denominador': fields.char('Común denominador', size=10),
'code': fields.char('Código clasificación', size=10),
'attribute_line_comun_denominador_ids' : fields.one2many('attribute.comun.denominador.line', 'comun_denominador_id', 'Atributos del comun denominador')
}
As you can see in my onchange_function all I want to do is to populate fields
'attribute_id'
and
'value_ids'
from 'product.attribute.line' model with the values 'attribute_comun_denominador_id' and 'value_comun_denominador_ids' belonging to 'attribute.comun.denominador.line' model.
Onchange function works great with fields many2one ('attribute_comun_denominador_id'->'attribute_id'), but in the case of many2many fields ('value_comun_denominador_ids'->'value_ids'), I get this error:
File "/home/odoo/openerp/fields.py", line 1568, in convert_to_cache
raise ValueError("Wrong value for %s: %s" % (self, value))
ValueError: Wrong value for product.attribute.line.value_ids: 1
Does anyone know how to do migration correctly between many2many fields?
def on_change_cm_id(self,cr, uid, ids,cm_id,context=None):
context=context or {}
attributes_product_template = []
value = {}
if ids:
old_note_ids = self.pool.get('product.attribute.line').search(cr, uid,[('product_tmpl_id','in',ids)])
self.pool.get('product.attribute.line').unlink(cr, uid, old_note_ids)
attribute_cm_ids = []
attribute_cm_ids = self.pool.get('attribute.comun.denominador.line').search(cr, uid, [('comun_denominador_id', '=', cm_id)])
for attribute_id in self.pool.get('attribute.comun.denominador.line').read(cr, uid, attribute_cm_ids, ['attribute_comun_denominador_id', 'value_comun_denominador_ids']):
attributes_product_template.append((0,0,{'value_ids':[(6,0,[attribute_id['value_comun_denominador_ids'][0]])],'attribute_id':attribute_id['attribute_comun_denominador_id'][0]}))
value.update(attribute_line_ids=attributes_product_template)
return {'value':value}