What do I return for function field value? - odoo

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.

Related

Increment integer fileds Odoo

I have added this fields under account.invoice in order to get an autoincrement number but it doesn't work.
Help me please to figure out my error
Example Code
class invoice(osv.osv):
_inherit = 'account.invoice'
def _get_increment(self, cr, uid, ids, fields, arg, context=None):
if context is None: context = {}
res = {}
if type == 'out_invoice':
ids = self.search(cr,uid,[('id','!=',False),('type','in',('out_invoice','out_refund'))])
if ids:
last_id = ids and max(ids)
print 'last_id',last_id
for invoice in self.browse(cr, uid, last_id, context):
print 'invoice', invoice
if invoice.name1:
res[invoice.id] = invoice.name1
else :
res[invoice.id] = invoice.name1 + 1
return res
_columns={
'name1':fields.function(_get_increment, type='integer', string='Name1'),
}
First of all. Your function never returns a value since type is never set.
Which means the if condition is never triggered.
At second. I'd suggest that you'd use the new Odoo API.
function fields are replaced by the compute attribute on fields and the declaration no longer takes place in the _columns dictionary.
New API
instead of importing from openerp.osv you should import the following:
from openerp import fields, models, api
The code would look like this:
from openerp import fields, models, api
class invoice(models.Model):
_inherit = 'account.invoice'
name1 = fields.Integer('Name1', compute='_get_increment')
#api.one
def _get_increment(self):
self.name1 = 1 + 1 #This value should be the value that you've calculated
the only thing you need to do in the method _get_increment is set self.name1.
In the new API self is a record. So self.id would get you the id of the record and so on.

How do I access a model's attribute from inside of the model's function

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.

OpenERP domain : char compare

Here's is what i've tried to do:
<field name="of_num" domain="[('etat','=','Terminé')]"/>
where 'of_num' is a many2one field and 'etat' is a function field of char type.
But it seems not working.I still get all records in my dropdown list.
I have also tried with some other text with no unicode chars but still the same.
I tried also to use 'ilike' operator and tried to put domain in python code with the field definition but with no chance.
EDITED
I've figured out the source of my problem :
the field 'etat' is computed but not stored since I'am using 'store=false'.
it's working with store=True.
Still, I don't wan't to store it because my value needs to be computed every time a view is loaded.
Could anyone please help me to do that without having to store my value ? thank you
The only solution I've found to get around my problem is to use a Boolean field that is stored and updated every time my function is computed (of the function field 'etat').
Use fnct_search. For functional fields there is an argument called 'fnct_search' which returns a search domain condition.
For example
_columns = {
'a':fields.float('A'),
'b':fields.float('B'),
'total_fn': fields.function(_total, fnct_search=_total_search, string='Total'),
}
def _total(self, cr, uid, ids, name, arg, context=None):
res = {}
for obj in self.browse(cr, uid, ids, context):
res[obj.id] = obj.a + obj.b
return res
def _total_search(self, cursor, user, obj, name, args, domain=None, context=None):
ids = set()
for cond in args:
amount = cond[2]
cr.execute("select id from your_table having sum(a,b) %s %%s" % (cond[1]),(amount,))
res_ids = set(id[0] for id in cr.fetchall())
ids = ids and (ids & res_ids) or res_ids
if ids:
return [('id', 'in', tuple(ids))]
return [('id', '=', '0')]
Here _total returns the value to display for the field total_fn, fnct_search returns the list of tuple need for searching. So whenever we are giving the argument [('total_fn','=',1500)]

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.

openerp fields.function not affecting value

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