odoo 8 make button invisible from compute result - odoo

This is my .py
#api.depends('product_id')
def get_sales_divisi(self):
for line in self:
if line.product_id.divisi_update:
line.sales_divisi_id = line.product_id.divisi_update.id
elif line.product_id.divisi_ids:
line.sales_divisi_id = line.product_id.divisi_ids.ids[0]
this is my .xml
<button name="action_approve" string="Approve Checker" type="object" attrs="{'invisible':['|',('sales_divisi_id', '=', '2'),('sales_divisi_id', 'in', ('VET'))]}" groups="ts_addons_tbk.group_tbk_checker" />
I'm tying to make the button invisible when the sales_divisi_id is Vet but it doesn't work. Any advice please? Thank you

Assuming sales_divisi_id is a relation, in operator will query by id or name field.
Furthermore, in order to use fields in queries inside your views, you need to define them first.
For example:
<field name="sales_divisi_id" invisible="1" />
<button name="action_approve" attrs="{'invisible':[('sales_divisi_id', 'in', [1,2])]}" groups="ts_addons_tbk.group_tbk_checker" />
action_approve button will show only if sales_divisi_id is related to a record having id 1 or 2.

Related

Odoo : limit editable rows on editable tree

Suppose I have an editable tree
<tree editable="top">
<field name="date">
<field name="value">
</tree>
Now suppose I want to let the user edit the values for 3 most recent dates, but the others should remain readonly.
How would I do that ?
Well, you could add a boolen field to the model. which will be a computed field. based on that field you could apply the read-only attrs as following:
class TheModel(models.Model):
_name = 'The.Model'
old_dated = fields.Boolean(compute='_old_dated_rec')
date = fields.Date()
value = fields.Integer()
#api.model
def _old_dated_rec(self):
"""define the condition of old dated records which could be as"""
recent_rec = self.search([], order='date desc', limit=3)
old_rec = self.search([('id', 'not in', recent_rec._ids)])
old_rec.write({'old_dated': True})
Then you could apply a scheduler to run everyday calling such method
<field name="old_dated" invisible="1" />
<field name="date" attrs="{'readonly':[('the_boolen_field','=',True)]}"/>
in such way, the compute method will update the boolean field.

In search view filter by related one2many field

I have inherited account.account model and added a move_ids One2many field related to account.move.line model:
class ViewSumasYSaldos(models.Model):
_inherit = "account.account"
moves_ids = fields.One2many('account.move.line', 'account_id', string="Account moves")
So now I need, in a search view, filter by date field from moves_ids. Is there any way to do this?
I need something like this:
<search>
<field string="Year" filter_domain="[('move_ids.date', '=', self]" />
</search>
I had found the solution and answer myself,
<field string="Year" name="year" filter_domain="[('moves_ids.date', '>=', str(self) + '-01-01'), ('moves_ids.date', '<=', str(self) + '-12-31')]" />
where 'self' is the year.

Not getting records which created in One2many field on onchange in transient model

I am trying to create records in one2many field in one of my transient model on onchange of boolean field.
Eg.
Models
class test_model(models.TransientModel):
_name ="test.model"
is_okay = fields.Boolean("Okay?")
lines = fields.One2many("opposite.model","test_id",string="Lines")
#api.onchange('is_okay')
def onchnage_is_okay(self):
ids = []
for l in range(5):
record = self.env['opposite.model'].create({'name':str(l),'test_id':self.id})
ids.append(record.id)
self.lines = [(6,0,ids)]
class opposite_model(models.TransientModel):
_name ="opposite.model"
name = fields.Char("Name")
test_id = fields.Many2one("test.model",string="Test Model")
View
<record id="view_form" model="ir.ui.view">
<field name="name">view.form</field>
<field name="model">test.model</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Test Model">
<field name="is_okay" />
<field name="lines" />
<footer>
<button name ="click_okay" string="Okay" type="object"/>
</footer>
</form>
</field>
</record>
Now, problem is that when in check or uncheck the is_okay field it fills the records in the One2many field.
That is working fine.
But in my above view i have button which calls the method click_okay().
Eg.
#api.one
def click_okay(self):
print self.lines
So, print statement gives me blank recordset. But, i can see the 5 records in the view when i am changing is_okay field.
I am not getting any idea how to get those lines in method?
Any response would be appreciated?
It should work. That's wired behavior.
You may try with following alternative way using self.update()
#api.onchange('is_okay')
def onchnage_is_okay(self):
ids = []
for l in range(5):
record = self.env['opposite.model'].create({'name':str(l),'test_id':self.id})
ids.append(record.id)
self.update({
'lines' : [(6,0,ids)]
)}
No matter what odoo keep doing the same thing:
the problem is that odoo always passes this records to create method with command 1 but in odoo we cannot use this command in create method and this
why you lose this records in the call of method.
(1, id, values)
updates an existing record of id id with the values in values. Can not be used in create().
i don't know why you are creating this record in onchange event because this not recomanded if the user hit close instead of ok the record all ready created in database and every time he check the button will recreate this record again and again.
if you don't need to create this records in the onchange event what you should do is:
#api.onchange('is_okay')
def onchnage_is_okay(self):
ids = []
for l in range(5):
record = self.env['opposite.model'].new({'name': str(l)})
ids.append(record.id)
self.lines = ids
one thing here onchange will return the dictionnary to the form, the tree of the one2field must have all field that are passed in that dictionary in this case the tree must have field name if oppisite.model have for example another field like test_field if we pass {'name': value, 'test_field': value_2} if the tree have only name field value of test_field will be lost in create method.
but if you need like you are doing, you should work arround odoo and change the command to 4 in create a method:
#api.model
def create(self, vals):
"""
"""
lines = vals.get('lines', False)
if lines:
for line in lines:
line[0] = 4
line[2] = False
vals.update({'lines': lines})
return super(test_model, self).create(vals)

Display multiple one2many field with different domain dynamically?

I have a model(modelA) with one2many field related to another model(modelB) and one of the fields in modelB is a category field, which is a many2one field. The requirement is to have a one2many field displayed for each category. So if there are 2 categories named 'category1' and 'category2', the form view of modelA should have 2 one2many fields, one which displays records of having category1 and another for category2(which could possibly done using domain).
For eg modelA and modelB has the following structure.
class classA(models.Model):
_name = 'modelA'
modelA_one2manyfield = fields.One2many('modelB', 'modelB_many2onefield')
class classB(models.Model):
_name = 'modelB'
name = fields.Char()
category = fields.Many2one('modelC')
modelB_many2onefield = fields.Many2one('modelA')
How would i go about implementing a form view for modelA so that for each category(which can be added by the user, hence there can be of any number of categories) there is a seperate one2many field.
What you are asking take a lot of time to give a very good answer one of the way that i think you need to try is override the fields_view_get because this is the method that retreive the view and here you can change the arch field to add a costum field take a look at this tutorial :
Tutorial for dynamic view
but i think you will have a problem, because even when you put the domain on the one2many field in XML, odoo will not filter
the record when the loading happen on the view :
<!-- here all record are shown but the expected behavior is the one2many should be empty -->
<field name="one2many_field_name" readonly="1" nolabel="1" domain="[('id', '=', False)]">
but when i add this field to the python declaration
# here no record will be shown on the view and that's what was expected
one2many_field_name = fields.One2many(..., domain=[('id', '=', False)])
so the question adding one2many field to arch via fields_view_get is easy but the problem is filtring data !!
It's technically not possible. Because you can't have 2 times the same field in the same view.
But you can create a specific widget to showing what you want. How you can see in the timesheet view (My Current timesheet menu).
This is a little tutorial to created a widget.
https://www.odoo.com/documentation/10.0/howtos/web.html#widgets-basics
This not an answer but you can say a tutorial example of dynamic view :
modul structur:
->dynamic_view
--> __ini__.py
--> models.py
--> views.xml
--> __manifest__.py
__manifest__.py :
# -*- coding: utf-8 -*-
{
'name' : 'Dynamic view',
'version' : '1.0',
'summary': 'Tutorial for Dynamic view',
'sequence': 30,
'description': """
This Module is for showing that you can update the code of the view
when it's called and even create new field without having to use python
code at all
""",
'category': 'StackOverFlow',
'depends' : ['base_setup',],
'data': [
'views.xml'
],
'installable': True,
'application': True,
'auto_install': False,
}
__init__.py :
# -*- coding: utf-8 -*-
from . import models
models.py :
# -*- coding: utf-8 -*-
from odoo import models, fields, api
class Person(models.Model):
_name = "training.person"
name = fields.Char("Full name")
class Car(models.Model):
_name = "training.car"
name = fields.Char("Car name")
mark_id = fields.Many2one(comodel_name="training.mark", string="Mark")
owner_id = fields.Many2one(comodel_name="training.person", string="Owner")
person_view_id = "dynamic_view.dgapr_form_person"
# here default arch value body in the view contains only
# name field but as we create new mark we add others field
person_view_arch = """
<group>
<field name="name"/>
</group>
"""
class Mark(models.Model):
_name = "training.mark"
name = fields.Char("Mark")
#api.model
def create(self, values):
"""
when we create a category we add one2many field to person view
TODO: when we unlink a category we need to remove the one2many
name of field is : x_mark_{id of deleted record}
"""
rec_id = super(Mark, self).create(values)
o2m_field = {
# fields created using orm method must start with x_
"name": "x_mark_%s"% rec_id.id,
"field_description": "Mark %s" % rec_id.name,
"ttype": "one2many",
"relation": "training.car",
"relation_field": "owner_id",
"stored": True,
"domain": "[('mark_id','=', %s)]"%rec_id.id,
"model_id": self.env.ref("dynamic_view.model_training_person").id,
}
# add on2many field to ir.model.fields
self.env["ir.model.fields"].create(o2m_field)
self.update_arch()
return rec_id
def update_arch(self):
"""
when ever we create or delete a mark record
we need to update the the view to add new one2many field
if we want to hide the one2many field in view that don't have
any record we should create compute field to use attrs features
"""
view_id = self.env.ref(person_view_id)
o2m_fields_ids = self.env['ir.model.fields'].search(
[
('model_id', '=', self.env.ref("dynamic_view.model_training_person").id),
('ttype', 'like', 'one2many'),
('relation_field', 'like', 'owner_id')
])
o2many_arch = ""
for o2m_id in o2m_fields_ids:
o2many_arch = o2many_arch + """
<group col="1" string="%s">
<field name="%s" noloable="1" />
</group>
""" % (o2m_id.field_description, o2m_id.name,)
arch_begin = """
<form>
<sheet>
"""
arch_close = """
</sheet>
</form>
"""
arch_body = person_view_arch + o2many_arch
new_arch = arch_begin + arch_body + arch_close
# update the arch of the view in database
view_id.arch = new_arch
views.xml:
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<data>
<record id="dgapr_form_car" model="ir.ui.view">
<field name="name">car.form</field>
<field name="model">training.car</field>
<field name="arch" type="xml">
<form >
<sheet>
<group>
<field name="name"/>
<field name="mark_id"/>
<field name="owner_id"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="dgapr_action_car" model="ir.actions.act_window">
<field name="name">Cars</field>
<field name="res_model">training.car</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="menu_root_training" name="Training"/>
<menuitem id="menu_ch_car" name="Cars" parent="menu_root_training" action="dgapr_action_car"/>
<record id="dgapr_form_person" model="ir.ui.view">
<field name="name">dgapr.form.person</field>
<field name="model">training.person</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="name"/>
</group>
</sheet>
</form>
</field>
</record>
</data>
</odoo>
i found out that you can create field using ORM method even compute field. i think creating a widget is better but good to know that wen can create costum fields .
Hope this helps you
Note i didn't create a menu for person record but you can see the view by clicking on the owner_id in the car form if the new one2many field not shown just refresh the page.

Populate select option on change odoo 9

How populate select option on change other field. For example:
Default value for select option is store in database tbl_car (Audi, Opel, Mercedes, VW, Bmw). In other table tbl_car_user I store car_name and user_name ('Peter','Audi'). Now I want after change user_id (Select user Peter) in car select option get all car not include Audi (User Peter already use Audi).
Maybe like this:
for car in self.env['tbl.car'].search([]):
for car_user in self.env['car.user'].search([('user_id','=','self.user_id.id]):
if (car.name = car_user.name):
print("DUPLICATE")
else:
print("ADD TO SELECT OPTION")
Any simple solution?
my first answar is correct now i will give a solution if you don't want to change the selection:
Create a wizard to affect a car to user :
class AffectCar(model.TransientModel):
_name = 'affect.user.car.wizard'
use_id = fields.Many2one(..) # you know how you do it
car_name = fields.Selection(selection='_get_car_selection', 'Car name')
def _get_car_selection(self):
"""
generate a selection for field car_name according to
the default user_id passed to this form
"""
# get all car name that this user don't have
# generate the selection [('car_name','car_name')..]
return computed_selection
def create_user_car(self):
""" save a new tbbl_car_user record """
# this method is called from the form of the wizard
# save the user_id and the car_name in tbl_car_user
now add a button to the user form and call a method to open the wizard form with user_id by default is the
same user
#api.multi()
def add_car(self):
"""
open the wizard to add a car
to this user
"""
return {
'type': 'ir.actions.act_window',
'view_mode': 'form',
'view_type': 'form',
'res_model':'affect.user.car.wizard',
'target': 'new',
'context': {
# pass the id the the user to the wizard
'default_use_id': self.id,
},
}
one thing to prevent the user of you application from changing the user_id when the popup is shown
make the user is in the form view of the wizard invisble="1"
<record id="add_car_wizard" model="ir.ui.view">
<field name="name">tax.adjustments.wizard.form</field>
<field name="model">tax.adjustments.wizard</field>
<field name="arch" type="xml">
<form>
<group>
<field name="user_id" invisible="1"/>
<field name="car_name"/>
</group>
<footer>
<button name="create_user_car" string="Add car" type="object" class="oe_highlight"/>
or
<button string="Cancel" special="cancel" />
</footer>
</form>
</field>
</record>
This kind of problem don't use Selection, even when you find this, if you edit the record next time the selection will not know the value that it contain because odoo will fill the selection by all value except the value that it have. you will see uknown value on the selection field.
but if you want to do this don't use selection use many2one change the selection of car name to a Model (table in database) and use domain for you many2one field.
you cannot do this logic by selection this logic can be don with selection only for wizard.
field_selection = fields.Selection(selection='generate_selection')
def generate_selection(self):
# compute selection
return computed_selection
but this works when the view is load the first time now the value of the selection cannot be edited or changed with onchange event.