It seems onchange method only works on current visible fields. If I use it to fill other fields that are in other pages of the view, it does not do anything. For example I have view with many pages. Mainly all information needs to be filled in first page, so most fields are filled correctly. But there is one field I need to fill in other page, when I choose partner_id in another page. For example in view like this:
...
<page string="page1">
<field name=partner_id on_change="onchange_partner(partner_id)"
<field name="field1"/>
<field name="field2"/>
</page>
<page string="page2">
<field name="field3"/>
</page>
...
field1 and field2 will be filled. But how to fill field3 or is it impossible, because system do not save it into database using onchange method?
My onchange method looks something like this:
def onchange_partner(self, cr, uid, ids, partner_id, context=None):
res = {}
if partner_id:
obj = self.pool.get('res.partner').browse(cr, uid, partner_id)
res['field1'] = obj.field1
res['field2'] = obj.field2
res['field3'] = obj.field3 # this value isn't being filled
return {'value': res}
So how could I fill field3?
Its not like that. It will definitely change the value. you might not be getting value in field3 because the record you are fetching will not have value of field3. and answer to your question is IT WORKS. Try this,
def onchange_partner(self, cr, uid, ids, partner_id, context=None):
res = {}
if partner_id:
obj = self.pool.get('res.partner').browse(cr, uid, partner_id)
res['field1'] = obj.field1
res['field2'] = obj.field2
res['field3'] = 'Hello' # this field type must be char or if not then give
#value accordingly, its just to prove you that values are filled on onchange.
return {'value': res}
Hope this will help you.
Related
I would like to retrieve all the values of the records of a model in a Many2one field. I don't know if it's possible with a compute.
My class of I want to recover the value:
class ResPartner_school(models.Model):
_name = 'ecole.partner.school'
_order = 'id desc'
school_name = fields.Many2one(comodel_name="ecole.establishment.webservice",
string="Etablissement Scolaire",
default=1)
school_level = fields.Many2one(comodel_name="ecole.establishment.webservice",
string="Niveau Scolaire",
compute="_get_level")
My other class:
class SchoolEstablishmentWebServices(models.Model):
_name = 'ecole.establishment.webservice'
_rec_name = "establishment_name"
establishment_name = fields.Many2one(comodel_name="horanet.school.establishment", string="Etablissement Scolaire")
id_establishment = fields.Char(string='idEtablissement')
grade_name = fields.Many2one(comodel_name="horanet.school.grade", string="Niveau Scolaire")
id_class = fields.Char(string='idClasse')
My function in my class ResPartner_school:
#api.multi
def _get_level(self):
school_level = self.school_name.grade_name
return school_level
How to retrieve all values from the grade_name field of the class SchoolEstablishmentWebServices?
One solution to show "all" data (depends on how much of data there is) is overriding the name_get() of a model with two variants.
Variant context-based name_get()
First override the name_get() of model ecole.establishment.webservice
class SchoolEstablishmentWebServices(models.Model):
_name = 'ecole.establishment.webservice'
#api.multi
def name_get(self):
res = []
for webservice in self:
if 'full_name' in self.env.context:
res.append((webservice.id, webservice.get_full_name()))
else:
res.append((webservice.id, webservice.establishment_name))
return res
def get_full_name(self):
# example logic
self.ensure_one()
full_format = "{establishment}, {grade}"
return full_format.format(
establishment=self.establishment_name, grade=self.grade_name)
Then you need to put the value full_name into the context. You could add it to the field itself, which won't work that good. Better you put that value into the context of the action of the menu, which is used to show your 'ecole.partner.school' entries.
<record id="my.list.action.for.school" model="ir.actions.act_window">
<field name="name">my.action</field>
<!-- and so on -->
<field name="context">{'full_name': 1}</field>
</record>
Variant non context-based -> it's the same as variant 1 without using the context at all. It will be system-wide at not only where you want it to be. It's the easier variant.
I have these fields on my class:
class bsi_production_order(models.Model):
_name = 'bsi.production.order'
_inherit = ['mail.thread','text.paper','product.template']
product_id = fields.Many2one('product.template', string="Product")
qty_available = fields.Float(string="Qty Available", related="product_id.qty_available")
Originally, on stock module You got this function:
class product_template(osv.osv):
_name = 'product.template'
_inherit = 'product.template'
def action_open_quants(self, cr, uid, ids, context=None):
products = self._get_products(cr, uid, ids, context=context)
result = self._get_act_window_dict(cr, uid, 'stock.product_open_quants', context=context)
result['domain'] = "[('product_id','in',[" + ','.join(map(str, products)) + "])]"
result['context'] = "{'search_default_locationgroup': 1, 'search_default_internal_loc': 1}"
return result
Since I've inherited product.template on my custom module, I want to show this very same function on my view, so I just declared like this:
<field name="product_id"/>
<field name="qty_available"/>
<button class="oe_stat_button"
name="action_open_quants"
icon="fa-building-o"
type="object">
Originally (on stock module), it is declared like this:
<button class="oe_stat_button"
name="action_open_quants"
icon="fa-building-o"
type="object" attrs="{'invisible':[('type', '=', 'service')]}" groups="stock.group_locations">
<div><field name="qty_available_text"/></div>
</button>
Right now, it is partially working, since I can visualize the quants associated with the product I choose from Many2one and related fields, but it is not related to the product I dinamically choose on my view.
So, is there a way o get it work exactly as it is on stock module?
I hope I've explained myself.
You could modify the _get_products function in your module to return the products you want to show on quants view. One way I would do is to use context to pass the product_id to _get_products function
On your view:
<field name="product_id" context="{'product_tmpl_id': product_id}"/>
And in your _get_products function:
def _get_products(self, cr, uid, ids, context=None):
products = []
context = context or {}
product_tmpl_id = context.get('product_tmpl_id', False)
if product_tmpl_id:
prodtmpl = self.pool.get('product.template').browse(cr, uid, product_tmpl_id, context=None)
if prodtmpl:
products += [x.id for x in prodtmpl.product_variant_ids]
else:
products = #... call super here
return products
i've one doubt:
I created a field called niu to increase its value for each product type stockable.
niu = fields.Char(string="NIU", compute="_niu_validation", defalut=" ", readonly=True)
With the attribute compute=_ niu_validation I call the method of the same name. In this, I want to validate that the product type is stockable type.
#api.depends('product_id.product_tmpl_id.type')
def _niu_validation(self):
if 'product_id.product_tmpl_id.type' == 'product':
niu = lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'sale.order.line')
return super(SaleOrderLine,self)
On the other hand I created the render sequence for ' niu ' field in sale.order.line model.
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<!-- Sequence for sale.order.line -->
<record id="seq_sale_order_line" model="ir.sequence">
<field name="name">NIU Sequence</field>
<field name="code">sale.order.line</field>
<field name="prefix">00</field>
<field name="padding">3</field>
</record>
</data>
</openerp>
And in the view , I want for each product type stockable, the field 'niu' increase its value.
Image: http://en.zimagez.com/zimage/viewsequenceniu.php
Please I need help because I 've been a long time on this and I can't do it on my own. I hope your help , advice , recommendations. Thank you very much to all.
In your code you are assigning a function(lambda) to niu:
niu = lambda ...
and you return super(), return is not needed.
To assign a new value to niu field use:
niu = value
To increment its value you can use:
sequence = self.env['ir.sequence'].next_by_code('sale.order.line')
for rec in self:
rec.niu = sequence
Use odoo official documentation (very useful), go to this LINK and search for Computed fields.
EDIT:
Check if niu is already set (add and not rec.niu to the condition):
#api.depends('product_id.product_tmpl_id.type')
def _niu_validation(self):
ir_sequence = self.env['ir.sequence']
for rec in self:
if rec.product_id.product_tmpl_id.type == 'product' and not rec.niu:
rec.niu = ir_sequence.next_by_code('sale.order.line')
You can create auto incremented field in odoo by two ways. The first on the creation of record and the other on clicking of button.
# on button click event
#api.one
def submit_application(self):
if self.application_no == '/':
sequence_id = self.env['ir.sequence'].search([('code', '=', 'your.sequence.code')])
sequence_pool = self.env['ir.sequence']
application_no = sequence_pool.sudo().get_id(sequence_id.id)
self.write({'application_no': application_no})
Thanks WoLy you're amazing , you could solve my problem. But i've other question. What happens is that I actually generates the sequence for stockables type products . But when I press the button (save) from the header , the sequence is changed. I do not understand why this happens.
This is my method:
niu = fields.Char(string="NIU", compute="_niu_validation", default=" ", readonly=True)
#api.depends('product_id.product_tmpl_id.type')
def _niu_validation(self):
for rec in self:
if rec.product_id.product_tmpl_id.type == 'product':
rec.niu = self.env['ir.sequence'].next_by_code('sale.order.line')
LOOK THIS IMAGES PLEASE.
Sequence products before pressing the (save) button.
http://en.zimagez.com/zimage/4s4s4s.php
Sequence products after pressing the (save) button.
http://en.zimagez.com/zimage/8658946.php
If you see the images you can see that the sequence is changed. And I want the sequence is maintained.
I hope you can help me and I really appreciate it . Thank you.
I'm trying to show a res.partner field, which is called phone into the treeview of a sale.order.
But it is not showing anything, just the name of the field without data. This is my code on sale.order
phone : fields.char('Telefono del Cliente'),
Onchange function for this field:
def onchange_phone(self, cr, uid, ids, phone, context=None):
res = {}
if phone:
obj = self.pool.get('res.partner')
browse(cr, uid, phone)
res['phone'] = obj.phone
return {'value' : res}
On res.partner the field is also called phone which is obviously the client's phone, i need to show it on the sale.order treeview, this is the code on my sale_view.xml:
<field name="phone" on_change="onchange_phone(phone)"/>
Any ideas?
Thanks in advance.
As a suggestion, If you want phone number of partner, than you should not create on_change of phone field. You can get phone number in 2 ways.
First way and best way, In sale.order, onchange_partner_id() method is their, you need to override that method and update vals with phone number of partner.
And Second way and long way, You may override create() method and write() method of sale.order.
create() method trick:
in create() method, you can take partner id from the context. For example vals.get('partner_id')
write() method trick:
in write() method, you have id of created record so you need to simply browse that record and write phone number of partner.
As Odedra suggested, you should do this like so (this is taken from sale.py file):
def onchange_partner_id(self, cr, uid, ids, part, context=None):
if not part:
return {'value': {'partner_invoice_id': False, 'partner_shipping_id': False, 'payment_term': False, 'fiscal_position': False}}
part = self.pool.get('res.partner').browse(cr, uid, part, context=context)
addr = self.pool.get('res.partner').address_get(cr, uid, [part.id], ['delivery', 'invoice', 'contact'])
pricelist = part.property_product_pricelist and part.property_product_pricelist.id or False
payment_term = part.property_payment_term and part.property_payment_term.id or False
fiscal_position = part.property_account_position and part.property_account_position.id or False
dedicated_salesman = part.user_id and part.user_id.id or uid
phone = part.phone or False
val = {
'partner_invoice_id': addr['invoice'],
'partner_shipping_id': addr['delivery'],
'payment_term': payment_term,
'fiscal_position': fiscal_position,
'user_id': dedicated_salesman,
'phone': phone,
}
if pricelist:
val['pricelist_id'] = pricelist
return {'value': val}
Note that you should not do that on base module, but instead create your own module and inherit it to sale.order model. What is more, onchange will not work on tree view (like you suggested), but you can easily show it on the tree - you have to first put it on your form with the onchange provided, then modify tree view to show phone number.
I wonder if there is some way to show in one field, a concatenation of 3 other fields, already in the same form.
Like for example:
'field_1' : fields.integer('Campo 1'),
field_2' : fields.integer('Campo 2'),
field_3' : fields.integer('Campo 3'),
Then show the inputs of these 3 fields concatenated in one single field:
field_concatenated : fields.related(?)('field_1', 'field_2', 'field_3', 'Name of the field'),
I put an '?' sign cause i actually don't know how to achieve this, maybe using a related type one? By the way the 3 fields are on the same class-form.
The resulting field could be readonly, and show up after the form has been saved.
I hope i've explained myself.
Thanks in advance
EDIT
The fields can be of the type integer and char
2nd EDIT
Actual Example:
'empresa' : fields.integer('Empresa'),
'provee' : fields.integer('Proveedor'),
'soli_cant' : fields.integer('Cantidad de Solicitudes'),
'dest' : fields.char('Destino'),
'anho' : fields.integer('Año'),
So, after these fields are filled manually, the resulting field have to show me a concatenation of these 4 fields, in a format like empresa-proveesoli_cant-dest-anho
Being provee and soli_cant one after the another, (without the '-') if it can't be possible then that show me the concatenation without separator
Maybe it isn't necessarily declared on the python code, maybe there is some shortcut in the xml view?
Something like <field name="empresa" "provee" "soli_cant" "dest" "anho" /> idk...
3rd EDIT
The actual code i'm using right now (Thanks to Ethan Furman):
The columns:
'empresa' : fields.integer('Empresa'),
'provee' : fields.integer('Proveedor'),
'soli_cant' : fields.integer('Cantidad de Solicitudes'),
'dest' : fields.char('Destino'),
'anho' : fields.integer('Año'),
The function with it's column:
def _combinalos(self, cr, uid, ids, field_name, args, context=None):
values = {}
for id in ids:
rec = self.browse(cr, uid, [id], context=context)[0]
values[id] = {}
values[id][field_name] = '%s %s %s %s %s' %(rec.empresa, rec.provee, rec.soli_cant, rec.dest, rec.anho)
return values
columns = {
'nombre' : fields.function(_combinalos, string='Referencia de Pedido', type='char', arg=('empresa','provee','soli_cant', 'dest', 'anho'), method=True),
All this on the same class of course.
Then i call it from my xml view like this:
<h1>
<label string="Request for Quotation " attrs="{'invisible': [('state','not in',('draft','sent'))]}"/>
<label string="Purchase Order " attrs="{'invisible': [('state','in',('draft','sent'))]}"/>
<field name="nombre" class="oe_inline" readonly="1" />
</h1>
The label string is to filter if this is a Request for Quotation or a Purchase Order
After all this, i do fill the 5 fields of integer and char type, but still don't get these fields 'concatenated' in one string, or title, just a label saying [object Object], it could be a label issue? String name of function in the column maybe?
Make a new functional field and combine the three other fields there:
def _combine(self, cr, uid, ids, field_name, args, context=None):
values = {}
for id in ids:
rec = self.browse(cr, uid, [id], context=context)[0]
values[id] = {}
values[id] = '%s %s %s' % (rec.field1, rec.field2, rec.field3)
return values
_columns = {
...
fields.function(_combine, string='three fields in one!', type='char',
arg=('field1','field2','field3'), method=True),
Note: untested code
The _combine method should be part of the class with the other columns, and the fields.function should also be in that class' _columns.