Issue with editing Odoo 11 One2many computed field - odoo

When I edit a record from this field (code below), it doesn't save for some reason. It's a computed field, linking to res.partner records. If I edit it and click save, it doesn't save at all (no changes in the database and/or if I hard refresh the page). Does someone see something here that I'm missing? If I can't edit it via what I'm expecting, is there another way to do this? The reason I do a computed field and not a domain on child_ids is because child_ids field with a domain doesn't seem to work properly with this domain.
Model
contact_ids = fields.One2many(comodel_name='res.partner', compute="_get_contact_ids", readonly=False)
#api.multi
#api.depends('child_ids')
def _get_contact_ids(self):
for company in self:
if company.child_ids:
company.contact_ids = company.child_ids.search([('is_location', '=', False), ('parent_id', '=', company.id), ('type', '=', 'contact')])
View
<field name="contact_ids" string="Contacts">
<tree create="true" delete="false" edit="true" default_order="create_date">
<field name="name"/>
<field name="phone"/>
<field name="email"/>
</tree>
</field>
Update
Added this per ideas, but it didn't work. Keep in mind, this is on a model that inherits res.partner.
activity_contact_id = fields.Many2one('res.partner', string="Contact")
contact_ids = fields.One2many(
comodel_name='res.partner',
inverse_name='activity_contact_id',
compute="_get_contact_ids",
readonly=False,
stored=True
)

Computed fields in Odoo are not stored by default, you need to set store=True in order to save the fields to database.
contact_ids = fields.One2many(comodel_name='res.partner', compute="_get_contact_ids", stored=True, readonly=False)

To store a one2many value in database you need the inverse_name on the other model.
I mean that you need to create a many2one field to save the id of the current record
in the co_model. (o2m needs m2o you cannot store the values without m2o !! remember this role)
don't use one2many field use many2many field it is better.
contact_ids = fields.Many2many(comodel_name='res.partner',
relation="your_model_res_partner_rel", # always mention the name of the relation good practice
column1 = "you_mode_id",
column2 = "partner_id",
compute="_get_contact_ids",
store=True) # make your field stored no need for readonly it's by default
#api.depends('child_ids')
def _get_contact_ids(self):
""" always explain what the method do here good practice for team work"""
for company in self:
if company.child_ids:
# break you line when it's to long to be readable
ids = company.child_ids.search([('is_location', '=', False),
('parent_id', '=', company.id),
('type', '=', 'contact')]).ids
company.contact_ids = [(6, False, ids)] # replace all records by the new ids

Related

Odoo can I display a field of a many2one related field without duplicating it in the model using the related flag

Say, in odoo I have a Model A with a many2one field to model B.
Model B has a field "city".
Now I want to create a form for model A in which I want the "city" field of model B.
I can do this by adding the city field of Model B to model A as well and giving it the related flag.
b_city = fields.Char(related='b_id.city', depends=['b_id'])
But I dont like this because than I have to add this field to my model. I would prefer if can do this without creating this field. Is this possible?
-----------Edit---------------
Something that I'm looking for is like this:
<page string="Offers">
<field name="offer_ids">
<tree>
<field name="price"/>
<field name="partner_id"/>
<field name="validity"/>
<field name="date_deadline"/>
<button name="accept" type="object" icon="fa-check"/>
<button name="reject" string="X" type="object" icon="fa-xmark"/>
<field name="status"/>
</tree>
</field>
</page>
This a page in a form where the corresponding model has a one2many field which is being displayed in a tree view. Now, I want to the other way around. I want to display in a form some fields of another model to which the there is a many2one field.
Is it possible to do this in the the xml view?
There is no way to do that in Odoo unless using related field.
Check Odoo ORM documentation
USING PYTHON CODE :
Example 1:
invoice_partner_icon = fields.Char(compute='_compute_invoice_partner_display_info', store=False, compute_sudo=True)
#api.depends('partner_id', 'invoice_source_email')
def _compute_invoice_partner_display_info(self):
for move in self:
vendor_display_name = move.partner_id.display_name
if not vendor_display_name:
if move.invoice_source_email:
vendor_display_name = _('From: ') + move.invoice_source_email
move.invoice_partner_icon = '#'
else:
vendor_display_name = _('Created by: %s') % (move.sudo().create_uid.name or self.env.user.name)
move.invoice_partner_icon = '#'
else:
move.invoice_partner_icon = False
move.invoice_partner_display_name = vendor_display_name
OR USING UI :
As admin, You can create a new field in Model B which is related to model A without storing it in the database : in App Settings, switch to debug mode by adding "?debug=1" in your url, reload the page, then go to the new Technical Menu-tab > Fields.
Then, create your new related field using the panel "Advanced Properties" Related Field: ...
and then uncheck [ ] Stored

conditions for a field to appear in odoo 14

I'd like field A to appear only if field B has a given value
field B doesn't belong to the same model, it belongs to a model connected through a many2one relationship
How do I write the contents of the attrs attribute for field A in the form ?
Until now I used
<field name="A"...
attrs="{'invisible': [('B', '=', 'some_value')
But until now both A and B belonged to the same model
Field used in attrs must be present in view
You can't use dotted field names because Odoo will raise an error if the field name contain . when validating the view definition:
Invalid composed field %(definition)s in %(use)s (attrs.invisible ...)
There is an exception for list sub-views (One2many/Many2many display in a form view) where you can use parent.field_name
ok, I reply to myself
First, in the model:
field_B = field.fieldtype(related='many2one_field.field_b')
Then, in the form view:
<field name="field_B" invisible="1"/>
and then
<field name="field_A" ...
attrs="{'invisible': [('field_B', '=', 'some_value')]}">

How to conditionally hide rows in One2Many field tree in a form view? [duplicate]

In my module I want to filter one2many records based on current date.
This is my xml code
<field name="record_ids" domain="[('end_date', '>', cur_date)]">
<tree string="records_tree">
<field name="record_id"/>
<field name="record"/>
<field name="start_date"/>
<field name="end_date"/>
</tree>
</field>
cur_date is a functional field I added to get current date.
My problem is records are not filtered in the view. Also it doesn't show any error message
you are define the domain in the XML file .
so this domain it's not work .
please define in the .py file .
For Example :
'record_ids':fields.one2many('model_name','model_id','Record',domain=[('end_date', '>=', 'cur_date')])
here the cur_date you need to define one function field which show the current date.
So Please check this may be it's help full to you :).
I also faced this problem, and the solution is put domain in .py file, in .xml domain is not working properly.
import_transaction_log_ids = fields.One2many(comodel_name = 'transaction.log','sale_order_id', string = 'Import Transaction Log',domain=[('operation_type','=','import')])
in example operation_type field is in transaction.log model.
Write domain in end_date field, like this:
<field name="record_ids" >
<tree string="records_tree">
<field name="record_id"/>
<field name="record"/>
<field name="start_date"/>
<field name="end_date" domain="[('end_date', '>', cur_date)]"/>
</tree>
</field>
i think it will help you..
you can pass only those field in domain those are store in Database.
So in that case cur_date is not store in Database.
Then also you need to pass into domain so you need to store cur_date field from py.
first of all, one2many fields are not for selection purpose. We can create the new records or update the existing records in one2many field. so we cannot apply domain to a one2many field.
eg: sale_order_line field in sale.order
moreover one2many fields, functional_fields [**if store=True not specified ] wont store in the table.
Many2one or Many2Many are used for selecting the records [ as well as creating new ones ], so here we can apply domain and we can restrict the user to select some type of records
eg: Many2one- product_id field in sale.order.line
many2many - user_ids field in res.users
So, in order to get your task, try many2many and apply domain, then the records will be filtered
domain contains 'field name' 'expression' 'value'.
instead of value you given a field
<field name="record_ids" domain="[('field', 'expression', value)]">
Add it in python:
Eg:
xn_cutting_ids = fields.One2many('mrp.bom.line', 'bom_id', 'Cutting Lines', domain=lambda self:[('xn_stage','=','cut')])
Use domain = lambda else there is a chance of error while using string values in domain.
Here xn_stage is in mrp.bom.line model.
On Odoo V11
Define a function that returns a domain in the one2many field definition.
class GroupContract(models.Model):
_name = 'group.contract'
#api.multi
def _domain_move_ids(self):
"""Odoo default domain for many2one field is [('contract_id', '=', self.id)].
This function adds new criteria according to your needs"""
res = []
if len(self) == 1: # do not compute in tree view
ids = self.env['stock.move'].search([
('state', '=', 'done'),
('date', '>=', self.start_date),
('date', '<=', self.end_date)
]).ids # choose your own criteria
res = [('id', 'in', ids)]
return res
start_date = fields.Date(string="Start date", required=True)
end_date = fields.Date(string="End date", required=True)
move_ids = fields.One2many(comodel_name='stock.move', inverse_name='contract_id', string="Moves",
domain=lambda self: self._domain_move_ids())

How to allow only create option in many2one field

How can I hide all the items from the many2one field. I want to allow the user only to create a new payment term.
We have this code in account module in account_invoice.py:
class AccountInvoice(models.Model):
_name = "account.invoice"
payment_term_id = fields.Many2one('account.payment.term',
string='Payment Terms',
oldname='payment_term',
readonly=True,
states={'draft': [('readonly', False)]})
In account_invoice_view.xml we have:
<field name="payment_term_id"
options="{'no_create': True}"
attrs="{'invisible': [('payment_term_id','=',False)]}"/>
I tried this code {'no_open':True} but it didn't work.
If you want to hide all the elements on the list try adding a domain that is always False:
<field name="id" />
<field name="payment_term_id"
domain="[('id', '=', -1)]"
attrs="{'invisible': [('payment_term_id','=',False)]}"/>
With options="{'no_create': True}" you get the opposite of what you want if I understood well
I think you going to find this solution good for you:
what is suggest is add a field to your model this field is many2many field
contains the list of payment_term_id that are created in the current record.
# this field is for technical purpose
create_payment_terms_ids = fields.Many2many(co_model='account.payment.term',
relation='created_payment_rel',
column1= 'invoice_id',
column2= 'paymen_id',
string='Created payment terms')
After this use onchange method to keep track of created payments and add the new
Payment terms to this field
#api.onchange('payment_term_id')
def onchange_payment_terms(self):
""" keep track of created payment from the current
invoice and show only them in drop down list."""
if self.payment_term_id:
# keep list of old ids here
payment_ids = self.create_payment_terms_ids and self.create_payment_terms_ids.ids or []
# check if list payment is empty this means that this is the first created payment
# second check if the selected payment is a new payment that is created
# if one of the two is true add the selected value to the list of payment
if not self.create_payment_terms_ids or self.payment_term_id.id not in payment_ids:
payment_ids.append(self.payment_term_id.id)
self.create_payment_terms_ids = [(4, self.payment_term_id.id)]
# if list is not empty we change the domain to show only payment that exist in the list
# else we use a special domain to show empty set.
domain = payment_ids and [('payment_term_id', 'in', payment_ids)] or [('id', '=', -1)]
return {'domain': {'payment_term_id': domain}}
In your view add the new field with visible = "1" you don't the user to see it's value.
you need to put it in the view because you need it's value in onchange event if you don't put it your many2many field will always be empty.
<field name="create_payment_terms_ids" invisible="1"/>
and remove options="{'no_create':False}" this will remove the create and edit option in the drop down and you don't want that.
Note: when you are in development remove invisible="1" to see if the many2many field contains the list of payment that you have
created.

Domain for one2many not working Odoo

In my module I want to filter one2many records based on current date.
This is my xml code
<field name="record_ids" domain="[('end_date', '>', cur_date)]">
<tree string="records_tree">
<field name="record_id"/>
<field name="record"/>
<field name="start_date"/>
<field name="end_date"/>
</tree>
</field>
cur_date is a functional field I added to get current date.
My problem is records are not filtered in the view. Also it doesn't show any error message
you are define the domain in the XML file .
so this domain it's not work .
please define in the .py file .
For Example :
'record_ids':fields.one2many('model_name','model_id','Record',domain=[('end_date', '>=', 'cur_date')])
here the cur_date you need to define one function field which show the current date.
So Please check this may be it's help full to you :).
I also faced this problem, and the solution is put domain in .py file, in .xml domain is not working properly.
import_transaction_log_ids = fields.One2many(comodel_name = 'transaction.log','sale_order_id', string = 'Import Transaction Log',domain=[('operation_type','=','import')])
in example operation_type field is in transaction.log model.
Write domain in end_date field, like this:
<field name="record_ids" >
<tree string="records_tree">
<field name="record_id"/>
<field name="record"/>
<field name="start_date"/>
<field name="end_date" domain="[('end_date', '>', cur_date)]"/>
</tree>
</field>
i think it will help you..
you can pass only those field in domain those are store in Database.
So in that case cur_date is not store in Database.
Then also you need to pass into domain so you need to store cur_date field from py.
first of all, one2many fields are not for selection purpose. We can create the new records or update the existing records in one2many field. so we cannot apply domain to a one2many field.
eg: sale_order_line field in sale.order
moreover one2many fields, functional_fields [**if store=True not specified ] wont store in the table.
Many2one or Many2Many are used for selecting the records [ as well as creating new ones ], so here we can apply domain and we can restrict the user to select some type of records
eg: Many2one- product_id field in sale.order.line
many2many - user_ids field in res.users
So, in order to get your task, try many2many and apply domain, then the records will be filtered
domain contains 'field name' 'expression' 'value'.
instead of value you given a field
<field name="record_ids" domain="[('field', 'expression', value)]">
Add it in python:
Eg:
xn_cutting_ids = fields.One2many('mrp.bom.line', 'bom_id', 'Cutting Lines', domain=lambda self:[('xn_stage','=','cut')])
Use domain = lambda else there is a chance of error while using string values in domain.
Here xn_stage is in mrp.bom.line model.
On Odoo V11
Define a function that returns a domain in the one2many field definition.
class GroupContract(models.Model):
_name = 'group.contract'
#api.multi
def _domain_move_ids(self):
"""Odoo default domain for many2one field is [('contract_id', '=', self.id)].
This function adds new criteria according to your needs"""
res = []
if len(self) == 1: # do not compute in tree view
ids = self.env['stock.move'].search([
('state', '=', 'done'),
('date', '>=', self.start_date),
('date', '<=', self.end_date)
]).ids # choose your own criteria
res = [('id', 'in', ids)]
return res
start_date = fields.Date(string="Start date", required=True)
end_date = fields.Date(string="End date", required=True)
move_ids = fields.One2many(comodel_name='stock.move', inverse_name='contract_id', string="Moves",
domain=lambda self: self._domain_move_ids())