How to add search other field in many2one? - python

HI I have a customer field and the default search is by name, and I want to add a search by barcode as well to the customer field
I have tried adding a barcode(partner_id.barcode) on the domain as below, but it still doesn't work (model = sale.order)
#api.model
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
if self._context.get('sale_show_partner_name'):
if operator == 'ilike' and not (name or '').strip():
domain = []
elif operator in ('ilike', 'like', '=', '=like', '=ilike'):
domain = expression.AND([
args or [],
['|', '|', ('name', operator, name), ('partner_id.name', operator, name), ('partner_id.barcode', operator, name)]
])
return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
return super(SaleOrder, self)._name_search(name, args=args, operator=operator, limit=limit, name_get_uid=name_get_uid)
I have also tried in the (res.partner) model as below. it can search customer by barcode, but cannot search customer by name :
#api.model
def name_search(self, name, args=None, operator='ilike', limit=100):
if not self.env.context.get('display_barcode', True):
return super(ResPartnerInherit, self).name_search(name, args, operator, limit)
else:
args = args or []
recs = self.browse()
if not recs:
recs = self.search([('barcode', operator, name)] + args, limit=limit)
return recs.name_get()
What should I do if I want to find a customer by name and barcode?
If anyone knows, please let me know
Best Regards

The barcode field in res.partner is a property field and stored in ir.property model which name is Company Propeties in Odoo and you can access it with developer mode from Settings -> Technical -> Company Propeties.
The _name_search method for res.partner enable you to search in any Many2one partner relation field in any model by one of these fields display_name, email, reference and vat and you can override it to add barcode as below:
from odoo import api, models
from odoo.osv.expression import get_unaccent_wrapper
import re
class ResPartner(models.Model):
_inherit = 'res.partner'
#api.model
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
self = self.with_user(name_get_uid) if name_get_uid else self
# as the implementation is in SQL, we force the recompute of fields if necessary
self.recompute(['display_name'])
self.flush()
print(args)
if args is None:
args = []
order_by_rank = self.env.context.get('res_partner_search_mode')
if (name or order_by_rank) and operator in ('=', 'ilike', '=ilike', 'like', '=like'):
self.check_access_rights('read')
where_query = self._where_calc(args)
self._apply_ir_rules(where_query, 'read')
from_clause, where_clause, where_clause_params = where_query.get_sql()
from_str = from_clause if from_clause else 'res_partner'
where_str = where_clause and (" WHERE %s AND " % where_clause) or ' WHERE '
print(where_clause_params)
# search on the name of the contacts and of its company
search_name = name
if operator in ('ilike', 'like'):
search_name = '%%%s%%' % name
if operator in ('=ilike', '=like'):
operator = operator[1:]
unaccent = get_unaccent_wrapper(self.env.cr)
fields = self._get_name_search_order_by_fields()
query = """SELECT res_partner.id
FROM {from_str}
LEFT JOIN ir_property trust_property ON (
trust_property.res_id = 'res.partner,'|| {from_str}."id"
AND trust_property.name = 'barcode')
{where} ({email} {operator} {percent}
OR {display_name} {operator} {percent}
OR {reference} {operator} {percent}
OR {barcode} {operator} {percent}
OR {vat} {operator} {percent})
-- don't panic, trust postgres bitmap
ORDER BY {fields} {display_name} {operator} {percent} desc,
{display_name}
""".format(from_str=from_str,
fields=fields,
where=where_str,
operator=operator,
email=unaccent('res_partner.email'),
display_name=unaccent('res_partner.display_name'),
reference=unaccent('res_partner.ref'),
barcode=unaccent('trust_property.value_text'),
percent=unaccent('%s'),
vat=unaccent('res_partner.vat'), )
where_clause_params += [search_name] * 4 # for email / display_name, reference
where_clause_params += [re.sub('[^a-zA-Z0-9\-\.]+', '', search_name) or None] # for vat
where_clause_params += [search_name] # for order by
if limit:
query += ' limit %s'
where_clause_params.append(limit)
print(query)
print(where_clause_params)
self.env.cr.execute(query, where_clause_params)
return [row[0] for row in self.env.cr.fetchall()]
return super(ResPartner, self)._name_search(name, args, operator=operator, limit=limit, name_get_uid=name_get_uid)

Related

Get records from odoo relational table

I have two models in my odoo project. Employee and equipment, as shown bellow.
Equipment/model.py
from openerp import models, fields, api
import datetime
class equipment(models.Model):
_name = 'equipment.equipment'
name = fields.Char(string='Name', )
date_of_purchase = fields.Date(string='Date Of Purchase', default=fields.Date.today(), )
write_off_days = fields.Integer(string="Days To Write-off", required=True, )
write_off_date = fields.Date(string="Write-off Date", compute="_get_write_off_date", )
price = fields.Float(string="Price '$'", required=True, )
description = fields.Char(string="Description", required=False, )
employee_id = fields.Char(string="Owner", compute="_get_owner", )
#api.one
#api.depends('employee_id')
def _get_owner(self):
//result = self.env.['res.equipment_table'].
//get id from relation database <-------------
#api.one
#api.depends('write_off_days', 'date_of_purchase')
def _get_write_off_date(self):
date = datetime.datetime.strptime(self.date_of_purchase, "%Y-%m-%d")
self.write_off_date = date + datetime.timedelta(days=self.write_off_days)
employee/model.py
from openerp import models, fields, api
class employee(models.Model):
_name = 'employee.employee'
name = fields.Char(string='First Name')
last_name = fields.Char(string='Last Name')
birth_date = fields.Date(string='Birth Date', default=fields.Date.today(), )
equipment_ids = fields.Many2many(string="Equipment", comodel_name="equipment.equipment", relation="equipment_table", )
total_equipment_price = fields.Float(string="Total Equipment Price '$'", compute="_get_total_equipment_price", )
#api.one
#api.depends('equipment_ids')
def _get_total_equipment_price(self):
total = 0
for equipment in self.equipment_ids:
total += equipment.price
self.total_equipment_price = total
I have many2many field, which holds all equipment that employee owns. I need to update the equipment owner every time the field is changed. The reason for this is.. when a user adds new equipment to the employee, there should be shown only unowned equipment. That is why I need to check and update the owner.
I already made a domain to check if equipment already has an owner, which will be shown below. Just need to update that employee_id field somehow.
<notebook>
<page string="Equipment">
<group>
<field name="equipment_ids" domain="[('employee_id', '=', False)]"/>
</group>
</page>
</notebook>
Change the type of employee_id to Many2one:
employee_id = fields.Many2one('employee.employee', string="Owner")
Override create and write methods to set employee_id each time new equipment is added to the list:
class Employee(models.Model):
_name = 'employee.employee'
#api.model
def create(self, values):
res = super(Employee, self).create(values)
if res.equipment_ids:
res.equipment_ids.write({'employee_id': res.id})
return res
#api.multi
def write(self, values):
res = super(Employee, self).write(values)
for r in self:
equipment_ids = r.equipment_ids.filtered(lambda s: not s.employee_id)
if equipment_ids:
equipment_ids.write({'employee_id': r.id})
return res
Because an equipment can set to one owner, I suggest you to change the type of equipment_ids to One2many to be able to set a list of equipments by employee and the employee_id field value will be set automatically for you when you add equipment to the list.
If you change the value of employee_id the equipment will be automatically visible in the list of equipments of the new employee.
equipment_ids = fields.One2many("equipment.equipment", 'employee_id', string="Equipments")
Reset owner
#api.multi
def write(self, values):
removed_ids = set()
old_ids = {r.id: r.equipment_ids.ids for r in self}
res = super(Employee, self).write(values)
for r in self:
equipment_ids = r.equipment_ids.filtered(lambda s: not s.employee_id)
if equipment_ids:
equipment_ids.write({'employee_id': r.id})
removed_ids |= set(old_ids[r.id]) - set(r.equipment_ids.ids)
self.equipment_ids.browse(removed_ids).write({'employee_id': False})
return res

How to get the value of a dynamic many2many field in create() function

I want to get the value from of a dynamically populated many2many field in create() function, but i get this outcome [[6, False, [98]]], 98 is actually the expected outcome though
this is my code below
class CustomTransRequest(models.Model):
_name = 'custom.trans.request'
_description = 'Transfer Request'
branch_from_id = fields.Many2one('custom.branch', string="From", required=True)
branch_to_id = fields.Many2one('custom.branch', string="To", required=True)
line_id = fields.Many2one('custom.branch.line', string="Products", required=True)
product_id = fields.Many2many('custom.product', required=False, )
qty = fields.Integer(string="Qty", required=True)
#api.onchange('line_id')
def onchange_line(self):
if self.line_id:
for rec in self:
selected_products = rec.env['custom.branch.line'].search(
[('id', '=', rec.line_id.id)]).mapped('product_id')
self.product_id = [(6, 0, selected_products.ids)]
#api.model
def create(self, vals):
print("Create Function ")
print("SELECT id FROM custom_branch_line WHERE (branch_id = %s) AND (product_id = %s)" % (
vals.get('branch_to_id'), vals['product_id']))
result = super(CustomTransRequest, self).create(vals)
return result
This how Odoo Handles X2many fields, by convention they are called command (or list of command) most of the time in the create method, the command that will be passed for your m2m fields will be:
# only one command 6 which tell Odoo replace all record with the selected ids
[(6, 0, [list_of_selected_record_ids)]
So in order to retrieve them just do: vals['product_id'][0][2]
No I don't know if you are just trying to show select query or you want to use, if you are just printing it:
# replace vals['product_id'] with
'({})'.format(','.join(vals['product_id'][0][2]))
If you will execute it, use query params:
self.cr.execute("SELECT id FROM custom_branch_line WHERE (branch_id = %s) AND (product_id = %s)", (vals['branch_to_id'], vals['product_id'][0][2]))
for more information about X2many command check this:
One2many and Many2many use a special "commands"
Note: I assumed that this field will not be empty if not you need to check if the fields are not empty first.

How to search product by field in different table

I want to use name_search in product to get product by search the lot name which in stock.production.lot
relation stock_quant_ids,product_id,lot_id
class ProductProduct(models.Model):
_inherit = 'product.product'
#api.model
def name_search(self, name='', args=None, operator='ilike', limit=100):
args = args or []
print('arg ',args)
recs = self.search([('???', operator, name)] args, limit=limit) #stock_quant_ids.lot_id.name
print('recs ', recs)
if not recs.ids:
return super(ProductProduct, self).name_search(name=name, args=args,
operator=operator,
limit=limit)
return recs.name_get()
Try this:
#api.model
def name_search(self, name='', args=None, operator='ilike', limit=100):
""" search for product using the Lot number """
args = args or []
recs = None
# only perform search by when the name is passed
if name:
# don't use limit here
recs = self.env['stock.production.lot'].search([('name', operator, name)])
if recs:
# handle extra filter that was passed by the domain attribute in the XML
args = expression.AND([args, [('id', 'in', recs.mapped('product_id').ids)]])
return self.search(args, limit=limit).name_get()
# no Lot was founded may be the user meant to search by product name
return super(ProductProduct, self).name_search(name=name,
args=args,
operator=operator,
limit=limit)

Openerp: Iterate Many2Many field

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.

odoo - display name of many2one field combination of 2 fields

In my module i have the following many2one field:
'xx_insurance_type': fields.many2one('xx.insurance.type', string='Insurance')
where xx.insurance.type is the following:
class InsuranceType(osv.Model):
_name='xx.insurance.type'
_columns = {
'name' : fields.char(size=128, string = 'Name'),
'sale_ids': fields.one2many('sale.order', 'xx_insurance_type', string = 'Sale orders'),
'insurance_percentage' : fields.float('Insurance cost in %')
}
I know the many2one field takes the name field as its display name but I would like to have it use the combination of name and insurance_percentage in the form of name + " - " + insurance_percentage + "%"
I read it is best to overwrite the get_name method so I tried the following:
def get_name(self,cr, uid, ids, context=None):
if context is None:
context = {}
if isinstance(ids, (int, long)):
ids = [ids]
res = []
for record in self.browse(cr, uid, ids, context=context):
name = record.name
percentage = record.insurance_percentage
res.append(record.id, name + " - " + percentage + "%")
return res
and placed this inside the ĂŚnsuranceType` class.
Since nothing happened:
Do i have to place it inside the main class containing the field? If so, is there an other way to do this since that will probably also change the display ways of the other many2one fields?
If you don't want to alter the display name of the rest of the many2one related to the model xx.insurance.type, you can add a context in the XML view to the many2one whose display name you want to modify:
<field name="xx_insurance_type" context="{'special_display_name': True}"/>
And then, in your name_get function:
def name_get(self, cr, uid, ids, context=None):
if context is None:
context = {}
if isinstance(ids, (int, long)):
ids = [ids]
res = []
if context.get('special_display_name', False):
for record in self.browse(cr, uid, ids, context=context):
name = record.name
percentage = record.insurance_percentage
res.append(record.id, name + " - " + percentage + "%")
else:
# Do a for and set here the standard display name, for example if the standard display name were name, you should do the next for
for record in self.browse(cr, uid, ids, context=context):
res.append(record.id, record.name)
return res
#api.depends('name', 'insurance_percentage')
def name_get(self):
res = []
for record in self:
name = record.name
if record.insurance_percentage:
name = '[' + record.insurance_percentage+ ']' + name
res.append((record.id, name))
return res

Categories

Resources