In Openerp, we have object_A with one one2many field belonging to object_B. Object_B has a float field. In object_A we have a one2many_list widget for the corresponding object_B so naturally we'd have multiple rows for each new record.
I know it's trivial but I'm having a hard time writing a function in object_A to sum up the total value of Object_B float column. What i have so far is something like that:
def get_result(self, cr, uid, ids):
total = {}
for obj in self.browse(cr, uid, ids):
result=0.0
total[result]+= obj.o2m_field.float_field
return total
The code provided by #DReispt should work, and if you approve an answer, please approve his, and not mine.
The important thing to understand is that a function field in OpenERP returns a dictionary with the object ids for the key and the value of the field for the given object as the associated value.
In your orginal code:
result = 0.0
total[result] += anything
would result in a KeyError since the dictionary is empty originally (total = {} at the beginning of your code).
A shorter version of DReispt code would be
def get_result(self, cr, uid, ids, context=None):
total = {}
for obj in self.browse(cr, uid, ids, context=context):
total[obj.id] = sum(o2m.float_field for o2m in obj.o2m_field)
return total
This version takes advantage of Python's generator expression which can be passed to the sum() built in function. It is also slightly faster, because you avoid accessing the total dictionary multiple times for each object.
You need to loop the o2m:
def get_result(self, cr, uid, ids, context=None):
total = {}
for obj in self.browse(cr, uid, ids, context=context):
total[obj.id] = 0
for o2m in obj.o2m_field:
total[obj.id] += o2m.float_field
return total
#example: {56: 12.34, 57: 56.78}
Just for kicks, doing it the functional way:
def get_result(self, cr, uid, ids, context=None):
return {obj.id: sum(o2m.float_field for o2m in obj.o2m_field)
for obj in self.browse(cr, uid, ids, context=context)}
Related
I want to override a original field function in Odoo.
According to the answer here: Odoo: How to override original function
i just have to define exactly the same method as in the original model. So here is my code:
class paiement_client_difference_montant(models.Model):
_inherit="account.voucher"
#writeoff_amount=fields.Float(compute='_get_writeoff_amount')
def _get_writeoff_amount(self, cr, uid, ids, name, args, context=None):
print '_get_writeoff_amount _inherit'
if not ids: return {}
currency_obj = self.pool.get('res.currency')
res = {}
for voucher in self.browse(cr, uid, ids, context=context):
debit = credit = 0.0
sign = voucher.type == 'payment' and -1 or 1
for l in voucher.line_dr_ids:
debit += l.amount
for l in voucher.line_cr_ids:
credit += l.amount
currency = voucher.currency_id or voucher.company_id.currency_id
res[voucher.id] = currency_obj.round(cr, uid, currency, voucher.amount - sign * (credit - debit))
return res
But that code is never reached.
Any help please. Thank you.
You have to override that field again in your class, to execute this newly created method.
Pythionans
Why would these 2 functions return a different value when using a temp return result?
I have never seen this behaviour before and need to understand why.
Any clues or ideas?
def _store_get_values(self, cr, uid, ids, fields, context=None):
return super(pai, self)._store_get_values(cr, uid, ids, fields, context)
Returns
[(10, 'pai', [13484L], ['line_next_approver_id', 'next_approver_id', 'line_id', 'partner_id'])]
And
def _store_get_values(self, cr, uid, ids, fields, context=None):
result = super(pai, self)._store_get_values(cr, uid, ids, fields, context)
return result
Returns
[(10, 'pai', [13485L], ['line_id', 'next_approver_id', 'partner_id', 'line_next_approver_id'])]
I have this in the openerp/odoo framework. But I believe this has nothing to do with the framework logic but with Python ways
From sourcecodebrowser.com:
02486 def _store_get_values(self, cr, uid, ids, fields, context):
02487 result = {}
02488 fncts = self.pool._store_function.get(self._name, [])
02489 for fnct in range(len(fncts)):
02490 result.setdefault(fncts[fnct][0], {})
02491 ids2 = fncts[fnct][2](self,cr, uid, ids, context)
02492 for id in filter(None, ids2):
02493 result[fncts[fnct][0]].setdefault(id, [])
02494 result[fncts[fnct][0]][id].append(fnct)
02495 result2 = []
02496 for object in result:
02497 k2 = {}
02498 for id,fnct in result[object].items():
02499 k2.setdefault(tuple(fnct), [])
02500 k2[tuple(fnct)].append(id)
02501 for fnct,id in k2.items():
02502 result2.append((fncts[fnct[0]][4],object,id,map(lambda x: fncts[x][1], fnct)))
02503 result2.sort()
02504 return result2
This code seems the result of a code golf challenge, however... result2.append(...) is the line that is producing your result. Let's take a closer look at it:
result2.append((
fncts[fnct[0]][4],
object,
id,
map(lambda x: fncts[x][1], fnct)
))
The result of map() is the list that is giving you problems. The order of items returned by map() depends on the order of items in fnct.
What is fnct? It comes from k2. k2 was generated from result[object]. result is generated from ids2. ids2 is the result of a call to fncts[fnct][2]. fncts is taken from self.pool._store_function.
So, look at the source of your _store_function and you'll find the answer.
I need to do two activities in my activity method.
one is need to return value for total_workers field.but need to return ID also because its necessary requirement in OpenERP when we override that method.
then have to return 2 things.please advice me how to implement this in my form.
with one return value this is worked for both things.but need to return both.
def create(self, cr, uid, values, context=None):
name = 'CheckRoll No = ' + str(values['checkroll_no']) + ' & Gang No = ' + str(values['gang_no'])
values.update({'name': name})
total_tea_workers = 0
offer_id = super(bpl_work_offer, self).create(cr, uid, values, context=context)
tea_worker_obj = self.browse(cr, uid, offer_id, context=context) or []
tea_worker_ids = self.pool.get('bpl.selected.tea.workers.line').search(cr, uid, [('tea_line_worker_id', '=', tea_worker_obj.id)])
for tea_worker in self.pool.get('bpl.selected.tea.workers.line').browse(cr, uid, tea_worker_ids):
if tea_worker.is_selected:
total_tea_workers += 1
return {'value': {'total_workers': total_tea_workers}}
return offer_id
EDITED
I sort it out in this way.hope this will help for others
:-)
i wrote a function for my field
def _compute_workers(self, cr, uid, ids, fieldname, arg, context=None):
total_workers = total_tea_workers = total_rubber_workers = total_sundry_workers = total_other_workers = 0
res = dict.fromkeys(ids, False)
for this in self.browse(cr, uid, ids, context=context):
tea_worker_ids = self.pool.get('bpl.selected.tea.workers.line').search(cr, uid, [('tea_line_worker_id', '=', this.id)])
for tea_worker in self.pool.get('bpl.selected.tea.workers.line').browse(cr, uid, tea_worker_ids):
if tea_worker.is_selected:
total_tea_workers += 1
rubber_worker_ids = self.pool.get('bpl.selected.rubber.workers.line').search(cr, uid, [('rubber_line_worker_id', '=', this.id)])
for rubber_worker in self.pool.get('bpl.selected.rubber.workers.line').browse(cr, uid, rubber_worker_ids):
if rubber_worker.is_selected:
total_rubber_workers += 1
sundry_worker_ids = self.pool.get('bpl.selected.sundry.workers.line').search(cr, uid, [('sundry_line_worker_id', '=', this.id)])
for sundry_worker in self.pool.get('bpl.selected.sundry.workers.line').browse(cr, uid, sundry_worker_ids):
if sundry_worker.is_selected:
total_sundry_workers += 1
other_worker_ids = self.pool.get('bpl.selected.other.workers.line').search(cr, uid, [('other_line_worker_id', '=', this.id)])
for other_worker in self.pool.get('bpl.selected.other.workers.line').browse(cr, uid, other_worker_ids):
if other_worker.is_selected:
total_other_workers += 1
total_workers = total_tea_workers + total_rubber_workers + total_sundry_workers + total_other_workers
res[this.id] = total_workers
return res
i changed my integer field to functional field
'total_workers': fields.function(_compute_workers, type="integer", string="Total Workers"),
You should never return anything else in create() except the ID of the record that was created. Usually this is done by returning the result of the call to the parent via super(myclass, self).create(...) indeed.
It's not clear what you are trying to achieve with your total_workers code. If total_workers is supposed to be a computed field, you don't have to override create at all: just declare this column as a fields.function and put the code to compute it in the corresponding function.
For example:
def _compute_workers(self, cr, uid, ids, fieldname, arg, context=None):
result = dict.fromkeys(ids, False) # default result for each id
for this in self.browse(cr, uid, ids, context=context):
# let's assume you have a one2many called worker_ids
result[this.id] = len(this.worker_ids)
return result
_columns = {
'total_workers': fields.function(_compute_workers, type="integer",
string="Total Workers")
}
At this point it would be a good idea to have a good look at the OpenERP technical memento and the OpenERP framework documentation, to get an overview of the OpenERP API and how it is supposed to be used :-)
Once you have a good understanding of the structure of models, the various field types, the CRUD methods and the inheritance patterns, you should be able to quickly browse the source code of the official modules and find examples of anything you want to accomplish. This should make your life much more easier!
I think that you're doing the counting of the workers in a very inefficient manner.
You're doing your SQL filter to obtain each of the workers matching an ID, and then testing for a single flag within them.
I think it would be much more efficient to just add another criteria to your select statement.
total_tea_workers = self.pool.get('bpl.selected.tea.workers.line').search(cr, uid, [('tea_line_worker_id', '=', id), ('is_selected', '=', True)]), count=True)
Edited code in a correct way. Now it's working.
Here is my code:
def _random_letter(self, cr, uid, ids, name, args, context):
num=[]
for i in range(5):
res = random.choice([1, 2, 3, 5, 9])
num.append(res)
print (num)
return num
def _mytest_query(self, cr, uid, ids, name, args, context):
cr.execute("""update checkroll_plucker set worker_name =concat('worker','%s') where id='1'""", self._random_letter(cr, uid, ids, name, args, context))
cr.commit()
Also, I am still confused about which parameters to pass to functions. I go through a developer book, but haven't got clear idea. I will mention those below please correct me if they correct.
Example:
self, cr, uid, ids, name, args, context
self = call to current class of that method called
cr = current row in the table
uid = user id for check if he authorized or not for run this function
name = column name we need to insert
context = language/time details (but what is the use of it.more times it equals to None)
put
self._random_letter in (,)
Repair Bug
cr.execute("""update checkroll_plucker set worker_name =concat('worker','%s') where id='1'""", (self._random_letter,)(cr, uid, ids, name, args, context))
To pass parameters in tuples , That is to use (arg,)
I want to calculate the value of a function field using its previous value ( = value of the record with previous id)
'testrest' : fields.function(get_reste, method=True, string='Restant',type='integer'),
def get_reste(self, cr, uid, ids, field_name, arg, context):
x = {}
for record in self.browse(cr, uid, ids ,context):
if record.statut != 'entree':
x[record.id]= a + record.entree_nbr # a should be the same field for the previous record
How can I do that? thank you
First point here about OE 6.1+ and fields.function() : it does not take a method parameter anymore [ Server rev 3495 revid odo#openerp.com-20110701232328-flgxulxva70vnyxr and addons rev 4844].So please do not use the "method" parameter anymore!
Now you want to calculate the value based on previous value so what you can do is you can use store=True param here that will store your previous value in data now in your calculation for your record you can read previous value and calculate new value and return it.
'testrest' : fields.function(get_reste, store=True, string='Restant',type='integer'),
def get_reste(self, cr, uid, ids, field_name, arg, context):
x = {}
for record in self.browse(cr, uid, ids ,context):
if record.statut != 'entree':
x[record.id]= record.testrest + record.entree_nbr
return x
Here benefit of string will be you can use this value any where out side OE or fro some external reporting tool and you can even expoer this field.
Hope this will help.
Some More Code :
'price': fields.function(_price_get, method=True, string="Price", store=True),
def get_reste(self, cr, uid, ids, field_name, arg, context):
x = {}
a = 0.0
for record in self.browse(cr, uid, ids ,context):
if record.statut != 'entree':
x[record.id]= a + record.entree_nbr
a =record.testrest
return x
If you need you can sort the list of ids by ids.sort()