this is my create function:
#api.model
def create(self, vals):
record = super(Shift, self).create(vals)
flag = False
if record.Date_range:
do smth
return super(Shift, self).create(vals)
but when I try to create a record by clicking on save button, it creates two records
In your method, you have called super method twice that's lead to create two record.
Replace code from
return super(Shift, self).create(vals)
to
return record
Related
I'm inherit purchase.order.line and try change value in field. For product_qty I can change value but for price_unit I can't change value.
My custom .py file:
class PurchaseOrderLine(models.Model):
_inherit = 'purchase.order.line'
#api.onchange('product_id')
def my_fucn(self):
for rec in self:
rec.product_qty = 10 #WORKING
rec.price_unit = 1 #NOT WORKING
Maybe is problem because in original purcahase.py odoo file also have #api.onchange('product_id').
Any solution?
You can't predict which onchange method will be triggered first or last, but the original onchange method for product_id changes in purchase.order.line is setting the price_unit field, but not the product_qty field.
So it seems your method is called before the other one, because price_unit is overwritten. You can check that by debugging both methods.
What to do now? I would prefer the extension of the original method:
#api.onchange('product_id')
def the_original_method(self):
res = super(PurchaseOrderLine, self).the_original_method()
# your logic here
return res
In your case a product_qty change will trigger another onchange event. Always have in mind, that field changes can trigger onchange events and field recomputations.
Try to extend both methods:
#api.onchange('product_id')
def onchange_product_id(self):
res = super(PurchaseOrderLine, self).onchange_product_id()
# your logic here
for rec in self:
rec.product_qty = 10 # will trigger _onchange_quantity() on return
return res
#api.onchange('product_qty', 'product_uom')
def _onchange_quantity(self):
res = super(PurchaseOrderLine, self)._onchange_quantity()
# your logic here
for rec in self:
rec.price_unit = 1.0
return res
class SunOrder(models.Model):
_name = 'sun.order'
manufacture_id = fields.Many2one(
'product.product',
#api.model
def create(self, vals):
Sequence = self.env['ir.sequence']
vals['name'] = Sequence.next_by_code('sun.order')
return super(SunOrder, self).create(vals)
here is simple create method that i use when creating data in my module.
the goal is to create quotation with same CREATE method with same name and samemanufacture_id.I mean when i creat sun.order i need that the same time quotation would be created. So maybe some 1 can give me example or general idea how it can be done. because i have no clue.
class pos_quotation(models.Model):
_name = "pos.quotation"
name = fields.Char('Name')
manufacture_id = fields.Many2one(
'product.product',
You can rewrite your create method as follows:
#api.model
def create(self, vals):
Sequence = self.env['ir.sequence']
vals['name'] = Sequence.next_by_code('sun.order')
#set your pos_quotation dictionary
vals_quot = {'manufacture_id': vals['manufacture_id'],
#... other fields for pos.quotation model
}
self.env['pos.quotation'].create(vals_quot)
return super(SunOrder, self).create(vals)
I hope this help you.
In Odoo 8, is there a preferred method for standardizing field values on create or write? Several methods come to mind, but this functionality seems like it belongs in the API. Essentially, I am wanting to create a field that specifies a standardize function, somewhat like a compute field that only specifies an inverse function. Does this already exist somewhere in the API?
Method 0: Create a field that specifies a standardize function.
The only flaw that I can see with this method is that the API does not exist.
import openerp
class Model(openerp.models.Model):
_name = 'addon.model'
field = openerp.fields.Text(
required=True,
standardize='_standardize_field',
)
#openerp.api.one
def _standardize_field(self):
self.field = self.field.upper()
Method 1: Override the create and write methods to insert a call to standardize the field.
This works, but seems rather verbose for what could be done with a single function, above. Note that the constraint is required if required=True and the standardization might yield an empty field.
import openerp
class Model(openerp.models.Model):
_name = 'addon.model'
field = openerp.fields.Text(
required=True,
)
#openerp.api.one
#openerp.api.constrains('field')
def _constrains_field(self):
if len(self.field) == 0:
raise openerp.exceptions.ValidationError('Field must be valid.')
def _standardize(self, args):
if 'field' in args:
# Return standardized field or empty string.
args['field'] = args['field'].upper()
#openerp.api.model
def create(self, args):
self._standardize(args)
return super(Model, self).create(args)
#openerp.api.multi
def write(self, args):
self._standardize(args)
super(Model, self).write(args)
return True
Method 2: Use a computed field and a bit of magic.
This works but feels a bit contrived. In addition, this method requires that the standardization function is deterministic, or this may create an infinite loop. Note that the standardization function may be called twice, which could be a concern if standardization is an expensive operation.
import openerp
class Model(openerp.models.Model):
_name = 'addon.model'
field = openerp.fields.Text(
compute=lambda x: x,
inverse='_inverse_field',
required=True,
store=True,
)
#openerp.api.one
#openerp.api.constrains('field')
def _constrains_field(self):
if self._standardize_field() is None:
raise openerp.exceptions.ValidationError('Field must be valid.')
def _inverse_field(self):
field = self._standardize_field()
# If the field is updated during standardization, this function will
# run a second time, so use this check to prevent an infinite loop.
if self.field != field:
self.field = field
def _standardize_field(self):
# Return the standardized field.
return self.field.upper()
Method 3: Use a regular field and a computed field, with only the computed field being exposed in the view.
The readonly flag and the constraints help to protect the underlying field, but I am not certain that this method would maintain data integrity, and the method as a whole feels contrived.
import openerp
class Model(openerp.models.Model):
_name = 'addon.model'
field = openerp.fields.Text(
readonly=True,
required=True,
)
field_for_view = openerp.fields.Text(
compute='_compute_field_for_view',
inverse='_inverse_field_for_view',
required=True,
)
#openerp.api.one
#openerp.api.depends('field')
def _compute_field_for_view(self):
self.field_for_view = self.field
#openerp.api.one
#openerp.api.constrains('field', 'field_for_view')
def _constrains_field(self):
if self._standardize_field() is None:
raise openerp.exceptions.ValidationError('Field must be valid.')
def _inverse_field(self):
self.field = self._standardize_field()
def _standardize_field(self):
# Return the standardized field.
return self.field_for_view.upper()
Maybe the 'default' attribute is an implementation of your approach #1?
Here's the example taken from the Odoo8 documentation at https://www.odoo.com/documentation/8.0/reference/orm.html#creating-models
a_field = fields.Char(default=compute_default_value)
def compute_default_value(self):
return self.get_value()
Another option is to override the write() method in your subclass to add your call like so:
def write(self, vals):
for record in self:
# do the cleanup here for each record, storing the result in
# vals again
# call the super:
res = super(extendedProject, self).write(vals)
return res
vals is a dictionary with the modified values to store; self is a recordset with all records to store the values to. Note that the transaction in Odoo may still be rolled back after returning from your call to write.
I have a function field, but I don't know what should the function return.
Here's my code:
the function:
def _property_expense_preset_expenses(self, cr, uid, ids, expenses, arg, context):
spus = self.browse(cr, uid, ids)
_spu = False
for spu in spus:
_spu = spu
if(_spu):
expenses_acc = {}
property_expense_presets = _spu.property_expense_presets
for property_expense_preset in property_expense_presets:
expenses = property_expense_preset.expense_preset.expenses
for expense in expenses:
expenses_acc[expense.id] = expense
return expenses_acc
else:
return {}
The field definition:
'expenses' : fields.function(
_property_expense_preset_expenses,
type='one2many',
obj="property.expense",
method=True,
string='Expenses'
),
The code above doesn't work, it raises an error : KeyError: 788
Like all function fields, it must return a dictionary with an entry and value for every ID you get passed in ids, although your value can be False, None, []
In your case your functional field is declared as a one2many type which means your functional field must return a dictionary with an entry per id and the value, a list of integers that represent the ids of the related table, in your case, property.expense.
A very common pattern is:
def _property_expense_preset_expenses(self, cr, uid, ids, field, arg, context = None):
res = {}
for spu in self.browse(cr, uid, ids, context = context):
res[spu.id] = []
for preset in spu.property_expense_presets:
res[spu.id].extend([x.id for x in preset.expense_preset.expenses])
return res
Assuming ids contains 1,2,3 you will get a result of
{1: [...], 2: [...], 3: []}
Where each list contains the integer ids of the expenses or an empty list if there are none.
As a general comment, I note your code doesn't default the context argument to None or pass the context as a named argument to the browse method - it is important to do both.
What is Function Field? What return this functional field function?
In OpenERP functional field is a field that return the calculated/logical value that value store into table. That value you can not get directly. that's why we want to use function field and return some value.
When you inserting data into object model functional field call every time defined function and that function logic code whatever you made and return that functional field value.
Example
class me.branch(osv.osv):
_name = "me.branch"
_order = 'name'
def _get_branch_name(self, cr, uid, ids, field_name, arg, context=None):
r = {}
for branch in self.browse(cr, uid, ids, context=context):
r[branch.id] = branch.name.split('/')[-1]
return r
_columns = {
'name': fields.char('Name', required=True),
'branch_name': fields.function(_get_branch_name, type='char', string='Branch', readonly=1, store=True),
}
Above example code branch name (eg. saas/demo/rarone ) already exist in another table.
But I want to get that branch name only last slash (/) after string (rarone) and store into this table only.
I want to access an attribute of a model from within a function. Take a look at the line if(len(self.order_line) > 0):
How do I do this correctly? Because the code above doesn't work.
The purpose of this function field is to read and modify another attribute of the same model which is the order_line. So it acts as a bridge to simplify the ui, the user only need to specify a property unit to represent the order_line. So I need to access the said order_line from within the function.
And I also want to SET the order_line value based on the property_unit_rel value BEFORE the creation of sale.order. How do I do that within the _property_unit_inv function?
Overall code:
from osv import osv,fields
class custom_sale_order(osv.osv):
_name = "sale.order"
_inherit = 'sale.order'
def _property_unit_read(self, cr, uid, ids, property_unit_rel, arg, context):
if(len(self.order_line) > 0):
pass
else:
return None
def _property_unit_inv(self, cr, uid, ids, property_unit_rel, arg, context):
pass
#this will simplify the need of defining a sale_order_line
_columns = {
'property_unit_rel' : fields.function(
_property_unit_read,
fnct_inv = _property_unit_inv,
type='many2one',
obj="property.unit",
method=True,
string='Property'
),
}
_defaults = {
}
_sql_constraints = [
]
def init(self, cr):
pass
custom_sale_order()
Most methods you call in OpenERP have parameters self, cr, uid, ids, ....
self is pool (see object pool pattern), cr is database cursor, uid is user id and ids is id or list of ids of objects you call method for. If you want to get number of order lines you must get order object at first. You can do it with a=self.browse(cr, uid, ids, context=context) to get object (or objects) specified by ids.
If ids is int or long you'll get browse_record but if it's list you'll get iterable browse_record_list (list of browse records). To get lines of some order you can call a.order_line (or a[0].order_line if ids was a list).
So if you can get an attribute of object you have to call it for browse_record.