In the model account_move, I have a field called "invoice_origin" that have the ID of a sale order, but I need the entire sale.order. Both models are not related. How can I search and return the entire sale.order, in the move_account file?
I've tried something like this but it doesn't work
client_order_ref = fields.Char('get_sale_order().client_order_ref')
# Get a sale order with the invoice_origin field
def get_sale_order(self):
sale_order = self.env['sale.order'].search([
('name', '=', self.invoice_origin)
])
return sale_order
ERROR psycopg2.errors.UndefinedColumn: column
account_move.client_order_ref does not exist LINE 1:
...ier_move_reference" as "supplier_move_reference", "account_m...
You passed 'get_sale_order().client_order_ref' as first parameter to Char, Odoo will use it as a label for client_order_ref field and the get_sale_order function will not be called.
To compute the value of a field, use the compute parameter
Example:
client_order_ref = fields.Char(compute='get_sale_order')
#api.depends('invoice_origin')
def get_sale_order(self):
for move in self:
sale_order = self.env['sale.order'].search([
('name', '=', move.invoice_origin)
])
move.client_order_ref = sale_order.client_order_ref
Note that move lines are related to sale lines with sale_line_ids field
Related
Hay I am new to Odoo Customizing and Python and wanted to know how I can iterate through a field and take the values out of the field and put them in a new one.
The field I want to iterate through contains multiple email adresses. I want to iterate through these email adress fields, collect the email adresses and store them together in a new field.
For that I need a function.
The field I want to iterate through:
My One2many field contains multiple mail adresses which I want to iterate through and collect.
field_contacts_customer_info = fields.One2many(
'contacts.customer.information', 'another_id', string='Contacts for customer information')
The field I want to store the collected email adresses in:
selected_email = fields.Char(compute='compute_email')
This is my class:
I want to collect all the email adresses from the mail_contacts field.
_name = 'contacts.customer.information'
_rec_name = 'name_contacts'
name_contacts = fields.Many2one(
'res.partner', string="Person", domain = [('is_company', '=', False)])
mail_contacts = fields.Char(
related = 'name_contacts.email' ,string="Email")
another_id = fields.Many2one('res.partner', string="AnotherID")
My Try: This function collects only the last set record of the field_contacts_customer_info field and puts this record in the selected_email field of every company.So it does not work right. It should collect all the mails of the field_contacts_customer_info field for every company seperated and then put them in the selected_mail field of the belonging company.
#api.onchange('field_contacts_customer_info.mail_contacts')
def compute_email(self):
list_email = []
for record in self:
if record.is_company:
for element in record.field_contacts_customer_info:
if element.name_contacts:
list_email.append(element.mail_contacts)
for email in list_email:
self.selected_email = email
Thanks.
You need to iterate over self which is a record set and loop over field_contacts_customer_info field to get mail_contacts field values.
#api.depends('field_contacts_customer_info.mail_contacts')
def get_email(self):
for record in self:
record.selected_email = ','.join(info.mail_contacts for info in record.field_contacts_customer_info if info.mail_contacts)
Then set the compute attribute to get_email:
selected_email = fields.Char(string="Mail4Info", compute='get_email')
You can check the ORM documentation on how to use the computed fields.
Edit (compute method):
You are setting the value of selected_email to each element of list_email, after the compute_email is executed the value of selected_email will always be the last value of list_email.
The last for loop is executed each time we loop over record.field_contacts_customer_info, it should be at the same level as the second loop.
The list_email is declared before we loop over records (it is not reset in the loop), after the first record, each record will use the email values of previous records.
When record.is_company is evaluated to False, the compute method will not assign a field value, you should see the following error:
ValueError: Compute method failed to assign {record description}.selected_email
It happens because the compute method must assign a field value
Example:
#api.depends('field_contacts_customer_info.mail_contacts')
def compute_email(self):
for record in self:
list_email = []
if record.is_company:
for element in record.field_contacts_customer_info:
if element.name_contacts:
list_email.append(element.mail_contacts)
emails = ""
for email in list_email:
emails += email + " "
record.selected_email = emails
else:
record.selected_email = ""
You can change the list_email type to a string and avoid looping again to get the field value:
Example:
#api.depends('field_contacts_customer_info.mail_contacts')
def compute_email(self):
for record in self:
list_email = ""
if record.is_company:
for element in record.field_contacts_customer_info:
if element.name_contacts:
list_email += element.mail_contacts
record.selected_email = list_email
I am setting a module with models:
Department(department)
Registered Business (reg.dept.business)
Department Business Records (dept.business.records)
Current working model is dept.business.records.
I want to display business list against current department using business_types field via calling compute method.
I am unable to filter records based on Domain applying on search() in method get_dept_business_ids(). All business list are showing currently, if I apply domain there is not result.
dept_id = fields.Many2one('department', 'Department')
business_types = fields.Many2many('reg.dept.business', string='Business List', compute='get_firm_business_ids')
#api.multi
def get_dept_business_ids(self):
for record in self:
list_val = []
env_rec = self.env['reg.dept.business'].search([('reg_dept_id', '=', dept_id.id)])
for x in env_rec:
list_val.append(x.id)
record.update({
'business_types': [(6, False, list_val)]
})
I am working with Odoo 10.
I have a one2many field with two columns in the hr.employee model. If the field "Bonus" (many2one field) is assigned to a particular date, it should not be saved or repeated once again on the same date.
How to achieve this?
Take a look at this below code, this is one possible solution, not the best.
from odoo import models, fields, api
from odoo.exceptions import ValidationError
class HrEmployee(models.Model):
_inherit = 'hr.employee'
prod_details_ids = fields.One2many(
string=u'Product details',
comodel_name='prod.details',
inverse_name='employee_id',
)
class ProdDetails(models.Model):
_name = 'prod.details'
employee_id = fields.Many2one(
string=u'Employee',
comodel_name='hr.employee',
)
date = fields.Date(
string=u'Date',
default=fields.Date.context_today,
)
bonus_id = fields.Many2one(
string=u'Bonus',
comodel_name='res.partner', # just an example
)
And then you need to add the constrains:
Solution 1
_sql_constraints = [
('bonus_unique', 'unique(employee_id, date, bonus_id)',
_('Date + Bonus cannot be repeated in one employee!')),
]
Solution 2
#api.one
#api.constrains('date', 'bonus_id')
def _check_unique_date(self):
# you have more freedom here if you want to check more things
rest = self.employee_id.prod_details_ids - self
for record in rest:
if record.date == self.date and record.bonus_id.id == self.bonus_id.id:
raise ValidationError("Date + Bonus already exists and violates unique field constraint")
Note: If you have date already in your database make sure that the constrains can be added with this data, because if not the constraint cannot be added to the database. This happens with the _sql_constraints at least
Use constrains to stop creating another record with the same name, so duplication of records doesnot occur.
you can use constraints and the search_count() method to check if there is a record. like below
#api.constraints('date')
def validate_date(self):
result = self.search_count([your_domain])
if result:
raise ValidationError(_('Your Text'))
I want to display all the taxes that are applied in a particular invoice and their amounts[in the tree view of a model account.invoice]
This is the output:
The column Tax Lines shows the ids of the taxes that are present in table account.invoice.tax (whereas I want to show their names and corresponding amount)
The model account.invoice has a field called tax_line_ids[Tax Lines] that contains the record of all the taxes on a invoice which is stored in a separate table account.invoice.tax, which in its own tree view looks like this:
I want to extract the tax name and its corresponding amount for it to reflect in account.invoice's tree view
Here's my python code which does'nt seem to work:
#api.one
def taxz(self):
tax_pool = self.pool.get("account.tax")
found_taxes = tax_pool.read(cr, uid, [tax_id,], ["tax_line_ids"], context)
found_tax = found_taxes[0] if found_taxes else None
tax_line_ids = found_tax["tax_line_ids"]
_logger.critical("context type: " + type(context))
_logger.critical("context content: " + str(context))
_logger.critical(tax_line_ids)
xml code for the view:
<field name="tax_line_ids" widget="many2many_tags" />
Try this:
class Invoice(models.Model):
_inherit = 'account.invoice'
tax_line_ids = fields.Many2many('account.invoice.tax',
'invoice_taxes',
'invoice_id',
'taxt_id',
'List of taxes',
compute='get_tax_list', store=True)
#api.depends('tax_line', 'tax_line.amount')
def get_tax_list(self):
for rec in self:
if rec.taxe_line:
rec.tax_line_ids = [(6,0,rec.tax_line.ids)]
else:
rec.tax_line_ids = [(5,0,0)]
But doing this will just show the list of taxes without amount in your tree view
if you want to show the amount you need to override the name_get method in account.invoice.tax
but this will affect all the x2many fields.
class AcountInvoiceTax(models.Model):
_inherit = 'account.invoice.tax'
#api.multi
def name_get(self):
res = []
for rec in self:
res.append((rec.id, rec.name +': '+ str(rec.amount)))
return res
If you don't want this then you need to change the type to Char and recompute the field
or create another model to save taxes and define name_get for that model.
As you can see this worked for me now if you still getting the keyErro you must be doing some thing wrong check your code for indentation inherit value... :
You can do add a char field just to use it in display in treeview :
class Invoice(models.Model):
_inherit = 'account.invoice'
tax_line_tree_view = fields.Char(compute='get_tax_list')
#api.multi
def get_tax_list(self):
tax_disp = ""
for rec in self:
if rec.taxe_line:
for tax in taxe_line:
tax_disp = tax_disp + "["+tax.name+"], "
rec.tax_line_tree_view = tax_disp[:-2] # [:-2] just to remove the last ', ' characters.
else:
rec.tax_line_tree_view = tax_disp
In your XML file where tree_view is located, add this field:
<field name="tax_line_tree_view " />
instead of tax_line_ids.
I hope that answer your question.
I have to tables wit similar fields and I want to copy objects from one table to another.
Problem that object could be absent in second table, so I have to use get_or_create() method:
#these are new products, they all are instances of NewProduct model, which is similar
#to Product model
new_products_list = [<NewProduct: EEEF0AP>, <NewProduct: XR3D-F>,<Product: XXID-F>]
#loop over them and check if they are already in database
for product in new_products_list:
product, created = Products.objects.get_or_create(article=product.article)
if created:
#here is no problem because new object saved
pass
else:
# here I need to code that will update existing Product instance
# with values from NewProduct instance fields
The case is that I don't want to list all fields for update manually, like this,, because I have about 30 of them:
update_old_product = Product(name=new_product.name,article= new_product.article)
Please advise more elegant way than above
You can loop over the field names and update them in the the other Product instance:
for new_product in new_products_list:
# use different variable names, otherwise you won't be able to access
# the item from new_product_list here
product, created = Products.objects.get_or_create(article=new_product.article)
if not created:
for field in new_product._meta.get_all_field_names():
setattr(product, field, getattr(new_product, field))
product.save()
You could try something like this.
def copy_fields(frm, to):
id = to.id
for field in frm.__class__._meta.fields:
setattr(to, field.verbose_name, field._get_val_from_obj(frm))
to.id = id
This is similar to Ashwini Chaudhary, although I think it will take care of that error that you mentioned in the comments.
new_products_list= (
# obj1, obj2, obj3 would be from [<NewProduct: EEEF0AP>, <NewProduct: XR3D-F>,<Product: XXID-F>] in your question
# NewProduct would just be the model that you import
# NewProduct._meta.fields would be all the fields
(obj1, NewProduct, NewProduct._meta.fields,),
(obj2, NewProduct, NewProduct._meta.fields,),
(obj3, NewProduct, NewProduct._meta.fields,),
)
for instance, model, fields in new_products_list:
new_fields = {}
obj, created = model.objects.get_or_create(pk=instance.article) # this is pretty much just here to ensure that it is created for filter later
for field in fields:
if field != model._meta.pk: # do not want to update the pk
new_fields[field.name] = request.POST[field.name]
model.objects.filter(pk=question_id).update(**new_fields) # you won't have to worry about updating multiple models in the db because there can only be one instance with this pk
I know this was over a month ago, but I figured I would share my solution even if you have already figured it out