I am building a module (Odoo 8) , my target is create offers in sale order, this offer can set a fix price for a determinate product or set a gift to zero cost.
I am adding my custom model offer_line, in new tab inside sale order.
Is defined like this:
class OfferSaleOrderLine(models.Model):
_name = 'offer.sale.order.line'
sale_order_ref = fields.Many2one('sale.order',ondelete='set null', string="Sale Order", index=True)
offer_ref = fields.Many2one('offer',ondelete='set null', string="Oferta", index=True)
is_active = fields.Boolean(default=True,string='Activo')
accumulations = fields.Float(digits=(6, 2), help="Acumulaciones")
class SaleOrder(models.Model):
_inherit = 'sale.order'
offers_lines = fields.One2many('offer.sale.order.line','sale_order_ref', string="Lineas de Ofertas")
I have a new api onchange method inside sale order:
#api.onchange('offers_lines')
def _onchange_offers_lines(self):
I check is offer need apply, and i add to offers_line new lines from this onchange function, like this:
self.offers_lines += self.env['offer.sale.order.line'].new({'is_active': True, 'offer_ref': offer, 'accumulations' : is_offer})
This is working perfect, lines is created, added to tab in form and onchange methods is trigger.
But the problem is next, if i try the same with sale order line, no working:
val = {
'name': gift_line.free_product.name,
'order_id': self.id,
'product_id': gift_line.free_product.id,
'product_uom_qty': gift_line.qty,
'product_uom': self.order_line[0].product_uom.id,
'price_unit': 0.0,
'state': 'draft',
}
self.order_line += self.env['sale.order.line'].new(val)
In log, this lines is created, i can see the newid id is created when i foreach self.order_line
****ORDER LINES : ID : ; Product: product.product(5152,) ; Qty: 6.0 ; Price: 0.0 ;****
but the item is no created in sale order line tab, i dont know why, my custom lines(One2many) is created, but, the sale_order_lines, with same code and one2many field too, is not created. I have the same problem if i try set the price_unit to this sale_order_lines. Log says changes is added, but is not updated in form. In next onchange trigger, the changes is dissapear.
Thanks to all!
#api.onchange('Put Your Onchange Field Here')
def _onchange_offers_lines(self):
vals = {
'name': gift_line.free_product.name,
'order_id': self.id,
'product_id': gift_line.free_product.id,
'product_uom_qty': gift_line.qty,
'product_uom': self.order_line[0].product_uom.id,
'price_unit': 0.0,
'state': 'draft'
}
self.order_line = [(0, 0, vals)]
Hope it will help you.
Odoo doesn't natively support onchange on *2many fields, anymore.
You can see that in openerp.models here https://github.com/odoo/odoo/blob/9.0/openerp/models.py#L6050
And furthermore a discussion on that topic here: https://github.com/odoo/odoo/issues/2693
I'm not sure to have properly understand your problem, but I see two things about it.
First you need to check that the field you want to set onchange is not already set in the base module that you are extending. If so, you had to disable the old-style onchange in the view by setting the attribute to 1 in the field (keep in mind that by disabling the api-v7 onchange on the field will not call the old onchange function you will probably want to call it in your new onchange function).
The second problem is that you can't add an item to a one2many field, you probably can to a many2one instead. You also can't use var += value to add an item to a relation field, you must use the special tupple (as described here).
Related
I am trying to modify an One2many field through an onchange method. What I need is to add some records but preserve some of the existing ones before the onchange computation.
To be more accurate, I have a field named tax_line_ids. This field stores taxes. Taxes have a field named manual, a Boolean.
So each time the field my_field changes, I need to add new taxes to the One2many field tax_line_ids, but I need to preserve the old ones which have manual set to True.
Attempt A
#api.onchange('my_field')
def onchange_my_field(self):
new_tax_lines = []
tax_line_ids = self.tax_line_ids.filtered(
lambda r: r.manual).ids
# get_taxes_values returns a list of dictionaries with values
tax_grouped = self.get_taxes_values()
for tax in tax_grouped.values():
new_tax = self.env['account.invoice.tax'].create(tax)
tax_line_ids.append(new_tax.id)
self.tax_line_ids = [(6, 0, tax_line_ids)]
Problem
The onchange method works fine, but when I click on Save button, the tax_line_ids records introduced by my onchange method disappear (tax_line_ids is not a readonly field).
Attempt B
#api.onchange('my_field')
def onchange_my_field(self):
new_tax_lines = []
manual_tax_lines = self.tax_line_ids.filtered(
lambda r: r.manual)
for manual_tax_line in manual_tax_lines:
new_tax_lines.append(manual_tax_line.sudo().read()[0])
tax_grouped = self.get_taxes_values()
for tax in tax_grouped.values():
new_tax_lines.append((0, 0, tax))
self.tax_line_ids = new_tax_lines
Problem
The onchange method works fine the first time, even if I click on Save button (records does not dissapear), but if I modify my_field a second time before saving, I got a security access error (I am working with the admin):
(Document type: account.invoice.tax, OperaciĆ³n: read)
This is the reason why I added sudo() before the read(), just in case, but the error still remains.
Conclusion
So, in my onchange method, how can I add new records to the One2many field preserving some of the existing ones?
#api.onchange('my_field')
def onchange_my_field(self):
tax_line_ids = self.tax_line_ids.filtered(
lambda r: r.manual).ids
# get_taxes_values returns a list of dictionaries with values
tax_grouped = self.get_taxes_values()
new_tax_datas = []
for tax in tax_grouped.values():
new_tax_datas.append(tax)
self.tax_line_ids = [
(4, tl_id) for tl_id in tax_line_ids,
(0, 0, td) for td in new_tax_datas]
OR
#api.onchange('my_field')
def onchange_my_field(self):
new_tax_lines = []
tax_lines = self.tax_line_ids.filtered(
lambda r: r.manual)
# get_taxes_values returns a list of dictionaries with values
tax_grouped = self.get_taxes_values()
for tax in tax_grouped.values():
new_tax = self.env['account.invoice.tax'].create(tax)
tax_lines += new_tax
self.tax_line_ids = [(6, 0, tax_lines.ids)]
try to do like this
#api.onchange('my_field')
def onchange_my_field(self):
account_invoice_tax = self.env['account.invoice.tax']
for invoice in self:
invoice._cr.execute("DELETE FROM account_invoice_tax WHERE invoice_id=%s AND manual is False", (invoice.id,))
if invoice._cr.rowcount:
invoice.invalidate_cache()
tax_grouped = invoice.get_taxes_values()
# Create new tax lines
for tax in tax_grouped.values():
account_invoice_tax.create(tax)
I just tried existing method.
forvas
Try to do this,
return {'value': {'your_one2many_field' : list_of_ids}}
Thanks
In hr.holidays model for employee_id field onchange function is there but I removed that onchange function from 'employee_id' field.The main aim of that function is Auto filling of 'department_id' field of same model when the change of 'employee_id' field.
problem:
My requirement is the below code is existing in odoo v7 but i need in odoo v8.
I tried in different ways but I didn't get any result so please help me.
def onchange_employee(self, cr, uid, ids, employee_id):
result = {'value': {'department_id': False}}
if employee_id:
employee = self.pool.get('hr.employee').browse(cr, uid, employee_id)
result['value'] = {'department_id': employee.department_id.id}
return result
My odoo V8 code:
I am getting object of 'hr.employee' but I am unable to fill that object in 'department_id' field because of it is many2one field.Below is my code.
#api.onchange('employee_id')
#api.constrains('employee_id')
def joining_date(self):
if self.employee_id:
self.department_id =''
depart_obj = self.env['hr.employee'].search([('name', '=' , self.employee_id.name)])
if depart_obj:
for departments in depart_obj:
depart_new_obj = self.env['hr.employee'].browse([departments.id])
for tax in depart_new_obj.department_id:
self.department_id = [tax.id]
Why are you searching and browsing object if you have already object of self.employee_id
just set
self.department_id = self.employee_id.department_id.id
At finally I got the answer removing of [ ] .""self.department_id = tax.id""
anyone know how to reference a field value in a model when it's described with the 'property' decorator?
I have an 'order' model with a property decorator which totals a number of values in fields related to my 'order' class via a foreign key.
#property
def total_price(self):
"""
The total value of all items in the order, minus any discounts.
"""
total = sum([item.total_price
for item in self.order_orderitem_set.all()])
discounts = sum([item.discount
for item in self.order_orderitem_set.all()])
return total - discountsenter code here
When I reference this type it's quite simple. I do:
myOrders = Orders.objects.all()
for key in myOrders:
print "My total is: ", key.total_price
However if I use the source attribute as: Orders.objects.all and try and reference this value 'total_orders' Chartit provides me with an error that it can't find this field value.
My chartit datapool looks like:
orderdata = \
DataPool(
series=
[{'options': {
'source': Order.objects.all()},
'terms': [
'order_date',
'total_price']}
])
#Step 2: Create the Chart object
cht = Chart(
datasource = orderdata,
series_options =
[{'options':{
'type': 'line',
'stacking': False},
'terms':{
'order_date': [
'total_price']
}}],
chart_options =
{'title': {
'text': 'Total Orders Over Time'},
'xAxis': {
'title': {
'text': 'Order Date'}}})
I get the error:
Field u'total_price' does not exist. Valid lookups are promo_code, enduser_address, etc....
It looks like to me it is not able to reference my 'property' within the model. Is this just a limitation of the framework?
Does anyone know of a neat way of getting round this - it seems my options are:
1) Create my own json object and iterate around my 'orders' and create my own data list. Then pass this to highcharts directly; or
2) Create another table say 'OrderSaleHistory' and populate for each month via a management function that django will update periodically or from manual action. This new table will then be passed to chartit.
Or does anyone have better ideas? :-)
This is my first 'post' so quite a newbie in posting but not reading!!!!
Kind regards, Nicholas.
I added 2 fields in 'sale.order.line' object. Let's say 'field1' and 'field2', those are readonly field. The value of the 2 fields will appear whenever the product is change in order line.
When I select a product, it shows the value of the two fields but when save it, the value will back 0, not stored.
Here's my code:
class sale_order_line(models.Model):
_inherit = 'sale.order.line'
field1 = fields.Float('One')
field2 = fields.Float('Two')
#api.multi
def product_id_change(self, pricelist, product, qty=0,
uom=False, qty_uos=0, uos=False, name='', partner_id=False,
lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False):
res = super(sale_order_line, self).product_id_change(pricelist, product, qty,
uom, qty_uos, uos, name, partner_id,
lang, update_tax, date_order, packaging, fiscal_position, flag)
if product:
one = self.search([('product_id', '=', product), ('partner_id', '=', partner_id)])
two = self.search([('product_id', '=', product), ('partner_id', '!=', partner_id)])
if customer:
field1 = one[-1]
res['value']['field1'] = field1
if other:
field2 = two[-1].
res['value']['field2'] = field2
return res
In Odoo framework we are now allowing to set readonly fields value, as well as readonly fields will not be getting in vals in create and write methods.
So if you set those readonly fields value in onchange methods then also it will not persist it's value because by nature it's readonly, it will not gives you any errors.
Purpose : The aims behind to define readonly attributes is to behave same through the all states of the record on UI and user can not change it's value and mainly define for display purpose.That is why readonly fields are not accessible for edit in onchange method.
Solution:
You need to override CREATE or WRITE method in following manner.
#api.model
def create(self, vals):
######
# WRITE YOUR LOGIC HERE AND BRING THOSE VALUE INTO VARIABLE AND THEN UPDATE IT IN VALS
VARIABLE_1 = SEARCH YOUR DATA
VARIABLE_2 = SEARCH YOUR DATA
vals.update({field1' : VARIABLE_1, 'field_2' : VARIABLE_2})
return super(sale_order_line, self).create(vals)
Update vals (record dictionary) by setting those readonly fields in to dictionary before calling super method or update those fields after calling super method.
There is an alternative solution for your problem. In Odoo Apps one module available from that system will store readonly value in the database.
Read Only ByPass
It is because readonly mode works only for display. In this case value from fields will not send to server-side.
You can override method create of sale.order.line. It should be something like this:
class sale_order_line(models.Model):
_inherit = 'sale.order.line'
#api.model
def create(self, vals):
# just example
vals[u'field1'] = 2.03
vals[u'field2'] = 3.05
return super(sale_order_line, self).create(vals)
Hope this helps you.
What I've done:
I have a module with
myfield = fields.Many2one('res.partner', string="Graduate", domain=[('is_graduated', '=', True)])
Then I have another class with
_inherit = 'res.partner'
is_graduated = fields.Boolean("Graduated before?", default=False)
graduations = fields.Many2many('my_module.courses', string="Graduation courses")
What I get:
The myfield works good, but the graduations field is empty. If you edit user 1 profile you can add entries to graduation field using Add item, but I need it to be filled automaticaly.
What I expect:
I expect that every record where myfield is set to lets say user 1, will be visible in field graduations when you open user 1 profile. When I create record and set myfield value to lets say user 1, that record must to be visible in user 1 profile in the field graduations. How to achieve that?
user_rel_ids = fields.Many2many(comodel_name='course',
relation='user_course_rel',
column1='user_id',
column2='course_id')
Or
user_rel_id = fields.Many2many('course')
For Filling Data (for add new relation)
user_rel_id = [(4,course_id)]
According to http://odoo4u.blogspot.com/2014/10/orm-methods.html, It says:
A full list of options is in the documentation for the class.
This same thing will apply for one2many
For a many2many and one2many field, a list of tuples is
expected. Here is the list of the tuple that is accepted, with the
corresponding semantics:
(0, 0, { values }) link to a new record that needs to be
created with the given values dictionary
(1, ID, { values }) update the linked record with id = ID (write
values on it)
(2, ID) remove and delete the linked record with id = ID (calls
unlink on ID, that will delete the object completely, and the link to
it as well)
(3, ID) cut the link to the linked record with id = ID (delete the
relationship between the two objects but does not delete the target
object itself)
(4, ID) link to existing record with id = ID (adds a
relationship)
(5) unlink all (like using (3, ID) for all linked records)
(6, 0, [IDs]) replace the list of linked IDs (like using (5)
then (4,ID) for each ID in the list of IDs)
You need to use an onchange method for myfield, then inside it you need to fill the graduations field, something like this:
#api.onchange('myfield'):
def _onchange_myfield(self):
#fill graduations field here...
...
_inherit = 'crm.phonecall'
alarm_ids = fields.Many2many('calendar.alarm',string="Reminders")
set the alarm_ids of calendar.event model in create method of crm phonecall...
alarm_ids = [(6,0,self.alarm_ids.ids)]
_inherit = 'calendar.event'
alarm_ids = fields.Many2many('calendar.alarm',string="Reminders")
You can achieve like these.
For example:
#api.one
#api.depends(
#here you may define your depend field name
)
def _set_graduations(self):
#here comes your logic which will collect ids
#and than return it with self.field_name like
self.graduations = [list_of_ids]
graduations = fields.Many2many('my_module.courses', string='Payments',
compute='_set_graduations')
If you don't want to use #api.depends than you may use #api.multi. For reference you may check out account module with account_invoice.py file.