openerp fields.function not affecting value - odoo

I have a function field that doesn't give any errors but then also doesn't set any values even when it should.
code:
class stock_move_custom(osv.osv):
_name = 'stock.move'
_inherit = 'stock.move'
def _penalty(self, cr, uid, ids, field_name, arg, context=None):
if not ids: return {}
res = {}
for id in ids:
res[id] = 20.0
for line in self.browse(cr, uid, ids, context=context):
if line.picking_id:
if line.picking_id.purchase_id:
if line.picking_id.purchase_id.penalty_id:
res[line.id] = 10.0
return res
_columns = {
'value_forpenalty': fields.float('Value', digits=(16,2)),
'actual_penalty': fields.function(_penalty, type='float', digits=(16,2), string='Penalty', store=True, select=True)
}
stock_move_custom()
Note: penalty_id is a many2one added to a custom child of purchase.order.
Q1. All I get is a 0 for penalty. Neither 20 nor 10. What could be wrong? If it is because of ids being blank, how do I get around that?
Q2. How do I pass the value_forpenalty to the the function as I will eventually need it to calculate the penalty? So when a user enters value for the value_forpenalty field in the form (and press a calculate button, or ideally without that), the value should get passed to the function for calculation.
Thanks

I your code work as you accepted, no problem in your code.
Can you restart your server and check again.
for line in self.browse(cr, uid, ids, context=context):
if line.picking_id and line.picking_id.purchase_id and line.picking_id.purchase_id.penalty_id:
res[line.id] = 10.0
And passing your argument value_forpenalty, you can get this value in you function from line
like
res[line.id] = line.value_forpenalty
Hope this helpful to you

Related

Assign default value to a Many2one field for specific user group in odoo

I am assigning a default value to analytic distribution field in account.invoice.line by below code
def _get_default_account(self, cr, uid, context=None):
res = self.pool.get('account.analytic.plan.instance').search(cr, uid, [('code','=','LAL')], context=context)
return res and res[0] or False
_defaults = {
'analytics_id': _get_default_account,
}
but now i want to set default value for specific group of user. I want to set one value for a group and other value for other group. Means I want to set two different default values for different users.
Someone please give me some idea about it. I'll be very thankful...
Easiest way to approach that is using has_group method. You should do it like:
if self.env['res.users'].has_group('base.group1'):
res = self.pool.get('account.analytic.plan.instance').search(cr, uid, [('code','=','LAL')], context=context)
return res and res[0] or False
elif self.env['res.users'].has_group('base.group2'):
res = self.pool.get('account.analytic.plan.instance').search(cr, uid, [('code','=','SAS')], context=context)
return res and res[0] or False
And so on and so forth.

OpenERP 7 : How to trigger a auto version increase function after click save

how could i trigger a function after i click save in OpenERP 7?
In my custom module, i wish to auto increase the "version_number" parameter where every time the user click save it will trigger a function to execute the logic of "ver=ver+1" and write back to the "version_number" fields. How could i do that?
I tried use "def write()" but not sure exactly how it could be done. Appreciate your kind help. Thanks!
You need to override write method.
def write(self, cr, uid, ids, vals, context=None):
res = super(your_model, self).write(cr, uid, ids, vals, context)
self._increment_version(cr, uid, ids)
return res
def _increment_version(self, cr, uid, ids):
for record in self.browse(cr, uid, ids):
cr.execute('update table_name set version_number=%s where id=%s' % (record.version_number + 1, record.id))
Here is the write method work for me
def write(self, cr, uid, ids, vals, context=None):
res = super(your_model, self).write(cr, uid, ids, vals, context)
self._increment_version(cr, uid, ids)
return res
def _increment_version(self, cr, uid, ids):
res = {}
reads = self.read(cr, uid, ids, ['version'], context=context)
ver_num = reads.get('version')
ver_id = reads.get('id')
if ver_num:
cr.execute('update table_name set version_number=%s where id=%s' % (ver_num + 1, ver_id))
return res
you are right you should use write function as.
_columns={...,
'ver':fields.integer(),
...}
def write(self, cr, uid, ids, vals, context=None)
vals['ver']= trigger_func()
return self.super(your_class_name).write(cr, uid, vals, context)
EDIT
It looks like my answer was not clear enough sorry if I didn't check the comments at the time. But because the problem and solutions are the same in newer versions of odoo/openerp, I am adding more description here.
The trigger_func must not write any value to the db (no self.write or self.some_field=some_value). It should simply return the new value for version.
def trigger_func(self):
return self.ver + 1
for newer version of Odoo (V8+), use the same format for trigger function and:
#api.multi
def write(self,vals):
vals['ver'] = self.trigger_func()
return super(YourClassName, self).write(vals)
Please note that this will set the same version for all records, You should use self.ensure_one() at the beginning of the trigger_func if you plan to change versions record by record. If you want a solution that works for multi writes (which I do recommend change your code to avoid it) you can use this slower code:
#api.multi
def write(self,vals):
if len(self)>1:
for rec in self:
rec.write(vals)
return self
else:
vals['ver'] = self.trigger_func()
return super(YourClassName, self).write(vals)
Another solution if you want to add version number to all models, would be patching the Abstract class and adding an auto increase magic field but it looks very overkill for this purpose.
Anyway, I think the direct use of the cursor should be the last resort.

openerp function field not working - OpenERP v7

I am having issues with getting a function field to display a string returned by a function in a custom module I am using to extend the stock.picking.out form. Ideally I would like to have a specific string from a field stored in the database displayed but that's an issue for another time because I can't even get a generic string to display..
Here is my field definition within my custom class:
_columns = {
'my_field_name': fields.function(_my_func, type='char', string='description', store=True, method=True, readonly=True),
}
Here is my definition for '_my_func':
def _my_func(self, cr, uid, ids, field_name, arg, context=None):
str="some string to be displayed"
return str
Here is the XML for the field:
<field name="my_field_name" string="Here is my string:" class="oe_inline"/>
I have searched the OpenERP dev book as well as their forums and these forums, and believe I have followed all of the proper syntax for this field so any help is greatly appreciated.
You need to improve your function/method like,
def _my_func(self, cr, uid, ids, field_name, arg, context=None):
res = {}
for rec in self.browse(cr, uid, ids, context=context):
res[rec.id] = 'Some string'
return res
while writing functional field(up to v7) make sure you return a dictionary like
{17: "some string"}
if you have multiple field it can be like
{17: {'field_one': 'value 1', 'field_two': 'value 1'}}
EDIT:
Also In _columns please remove readonly=True like
_columns = {
'my_field_name': fields.function(_my_func, type='char', string='description', store=True, method=True),
}
Hope this helps.
Just you must have to improve your function login
you can do some things like this.
def _my_func(self, cr, uid, ids, field_name, arg, context=None)
v={}
if field_name:
v['my_field_name'] = " some string to be displayed "
return {'value': v}
Then finally your function logic working like a charm.
I hope this should helpful for you :)
Try following,
def _my_func(self, cr, uid, ids, field_name, arg, context=None):
res = {}
for obj in self.browse(cr, uid, ids, context=context):
###Set your string here
str="some string to be displayed"
res[obj.id]['my_field_name'] = str
return res
If you are a little confused with OpenERP v7, you always can try with Odoo(OpenERP v8) It's much more easiest to work.
from openerp import models, fields,api
class my_class(models.Model):
_name="my.class"
my_field_name=fields.Text(string="description", compute=_my_func, store=True, readonly=True)
def _my_func(self):
self.my_field_name = "some string to be displayed"
I hope this can help you!
try this,
def _my_func(self, cr, uid, ids, field_name, arg, context=None):
result = {}
for record in self.browse(cr, uid, ids):
str="some string to be displayed"
result[record.id] = str
return result
and if you use store = True on a functional-field then it will be accessed for the first time and it will store the result in database, and from next time it wont be called.
To access functional field every time for store = True , refer sale.py [amount_total] field. We have to write some addtional code
You can give like this also :
without using browse method..
def _my_func(self, cr, uid, ids, field_name, arg, context=None):
result = {}
str="some string to be displayed"
result[ids[0]] = str
return result
Make sure, in model field type should be char/text.

What do I return for function field value?

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.

Openerpv7: Numbeing in OrderLine

I'm using OpenERP v7 and requesting this below output in Purchase order line window
PO0001 PO0002 PO0003
Line Line Line
10 10 10
20 20 20
30 30
My code:
<field name="order_line" context="{'order_id':active_id}">
_columns={
'sequence':fields.integer('Sequence', store=True),
}
_defaults = {
lambda obj, cr, uid, context: obj._default_sequence(cr, uid, context)
}
def _default_sequence(self, cr, uid, context=None):
if context is None:
context = {}
max_sequence = 0
if context.get('order_id'):
order_line = self.search(cr, uid, [('order_id', '=', context['order_id'])])
if order_line:
max_sequence = max([line['sequence'] for line in self.read(cr, uid, order_line, ['sequence'])])
return max_sequence + 10
But im getting this kind of output . Any advice would be greatful.
PO0001 PO0002 PO0003
Line Line Line
10 10 10
10 10 10
10 10
Anyone could help me to achive the required output.
I think you are doing wrong. A record will be created only after you click on any button action(button other than adding the lines) for the first time for the record. Then only you will be able to make the search. I hope default will get the context even if we don't specify context. Still not getting the context, then try by following the answer given by Andrei Boyanov.Also please understand that field names like active, state, sequence etc have some super powers in openerp. For example if a table have a field "sequence", then the table's tree view will have a drag and drop functionality and if we do the drag and drop functionality, then a new sequence for each record in the table will be automatically calculated and saved.
So my opinion is not to use the sequence field. Use any other field name. Then write a on_change method to your one2many field and pass the one2many field name as the attribute to the on_change method. Then in the on_change method check what all values are coming when you add a new line. If the new line's value is in the format (0,0,dictionary_with_fields_as_keys_and_its_values_as_values).Then to this dictionary add the sequence_number with the value.For example:
<field name="order_line" on_change="onchange_line(order_line)">
def onchange_line(self, cr, uid, ids, lines,context=None):
result = {}
result['value'] = {}
#do the proper checking
count_dict = {}
count = 10
had_seq = 0
for index,line in enumerate(lines):
if line[0] == 0:
count_dict[index] = count
count +=10
else:
had_seq +=1
#seqnece_no is the new sequence field defined
for k in count_dict:
if had_seq:
lines[k][2]['sequence_no'] = had_seq*10 + count_dict[k]
else:
lines[k][2]['sequence_no'] = count_dict[k]
result['value'].update({'order_line':lines})
return result
Please try it.
How do you execute _default_sequence(...) ? I suppose you intend to invoke it trough the _defaults dictionary. In this case correct it as follow:
_defaults = {
'sequence': lambda obj, cr, uid, context: obj._default_sequence(cr, uid, context)
}
In your code you omitted the key sequence. In fact what you defined is not a dictionary but a set with one element - the lambda function.
Better, you can define it just like this:
_defaults = {
'sequence': _default_sequence
}
change your Python file by
def _default_sequence(self, cr, uid, context=None):
if not context: context = {}
max_sequence = 0
if context.get('order_id'):
order_line = self.search(cr, uid, [('order_id', '=', context['order_id'])])
if order_line:
max_sequence = max([line['sequence'] for line in self.read(cr, uid, order_line, ['sequence'])])
return max_sequence + 10
_columns={
'sequence':fields.integer('Sequence'),
}
_defaults = {
'sequence': _default_sequence
}
No need to put store=True in the integer field, it will always store value in the database.