odoo calculate 2 fields with compute - odoo

I have this 2 fields which is I want to calculate using compute
_columns = {
'feb_target':fields.float('Februari Target'),
'apr_target':fields.float('April Target'),
'ytd':fields.float('YTD', compute='_computeytd'),
}
#api.depends('feb_target', 'apr_target')
def _computeytd(self):
for record in self:
record.ytd = record.feb_target + record.apr_target
But the result is 0.
What should I do? Thank you

Wow that took me back to my beginnings.
Old API fields defined in _columns should use fields.function for computed fields. But i prefer to use the "new" API when possible. And your code looks like fully done by yourself, so it is possible.
from odoo import api, fields, models
class YourClass(models.Model):
feb_target = fields.Float('Februari Target')
apr_target = fields.Float('April Target')
ytd = fields.Float('YTD', compute='_compute_ytd')
#api.multi
#api.depends('feb_target', 'apr_target')
def _compute_ytd(self):
for record in self:
record.ytd = record.feb_target + record.apr_target

Related

Django REST Framework serializer for a list of dictionaries

I'm new to Django REST Framework and searching for a method to serialize a list of dictionaries.
Target data is a time-series. Each dictionary contains hourly value and each list contains hourly values of a day.
class Hourly:
hour = None
value = None
def __init__(self, hour, value):
self.hour = hour
self.value = value
class Daily:
date = None
vlist = None
def __init__(self, date, vlist):
self.date = date
self.vlist = vlist
It is easy to create a serializer for the hourly class by following the Django REST Framework API guide.
class HourlySerializer(serializers.Serializer):
hour = serializers.IntegerField(required=True, min_value=0, max_value=23)
value = serializers.FloatField(required=True)
def create(self, validated_data):
return Hourly(**validated_data)
def update(self, instance, validated_data):
instance.hour = validated_data.get('hour', instance.hour)
instance.value = validated_data.get('value', instance.value)
return instance
My objective is simple: creating a serializer for the daily data.
(1) The serializer must restrict the length of data as 24. (2) It should have an ability to validate each hourly data.
#1. Using ListField
In case of restricting the data length, ListField has arguments min_length and max_length #.
I think this kind of code may be possible #, however, the API guide does not say anything about defining the child of a ListField as a serializer.
class DailySerializer(serializers.Serializer):
date = serializers.DateField(required=True)
vlist = serializers.ListField(child=HourlySerializer(), min_length=24, max_length=24)
#2. Using ListSerializer
The API guide says one can nest serializers just like they are fields. If the nested value is a list of items, many=True can be used to initiate ListSerializer. However, there is no argument about restricting the data length.
class DailySerializer(serializers.Serializer):
date = serializers.DateField(required=True)
vlist = HourlySerializer(many=True)
Here are my questions:
Is the method using ListField is possible with no problem? Or, can I pass min_length and max_length arguments with many=True?
The API guide does not say anything about defining create() and update() for ListField or ListSerializer. There are only cases for a nested serializer without many option # and a ListSerializer without non-list field #. Is there any example about this?
Answer to my own question.
class DailySerializer(serializers.Serializer):
date = serializers.DateField(required=True)
vlist = serializers.ListField(child=HourlySerializer(), min_length=24, max_length=24)
def create(self, validated_data):
return Daily(**validated_data)
def update(self, instance, validated_data):
instance.date = validated_data.get('date', instance.date)
instance.vlist = validated_data.get('vlist', instance.vlist)
return instance
Above ListField based DailySerializer is tested with the below input.
[
{'date': '2000-01-01', 'vlist': [{'hour':'00', 'value': '012'}, {'hour':'01', 'value': '123'}, ...],
{'date': '2000-01-02', 'vlist': [{'hour':'00', 'value': '012'}, {'hour':'01', 'value': '123'}, ...],
...,
]
To deal with the additional multi-day nest, serializer in the view is defined with many=True.
serializer = DailySerializer(data=data, many=True)
Test results are below:
Using the ListField method does not make any problem during validation.
Changing some hourly value as an arbitrary string is detected as an non-integer value.
Daily data with its length not equal to 24 is also detected.
Using naive create() and update() methods with ListField can be problematic.
obj_list = serializer.validated_data returns a List of OrderedDict objects.
obj_list = serializer.save() returns a List of Daily objects.However, obj_list[0].vlist is a List of OrderedDict objects, not a List of Hourly objects.

Odoo: Override Integer field in onchange not working whereas Float is changed - WHY?

I have extended the product.template with following fields:
length = fields.Integer("Length", default=0)
length_float = fields.Float("Float", default=0.0)
Additionally I have extended the product.packaging model with this fields and an onchange method:
product_id_ref = fields.Many2one('product.product', string="Product Reference")
length = fields.Integer('Length')
length_float = fields.Float('Length_Float')
#api.onchange("product_id_ref")
def _onchange_product(self):
if self.product_id_ref:
self.length_float = self.product_id_ref.length_float
self.length = self.product_id_ref.length
Interestingly the FLOAT field length_float is changed as expected and shown in the view. The INTEGER field is not changed.
Something more suprising is that if I change the order of the onchange method
#api.onchange("product_id_ref")
def _onchange_product(self):
if self.product_id_ref:
self.length = self.product_id_ref.length
self.length_float = self.product_id_ref.length_float
NO value is changed!
How could that be?? Does anyone have an idea?
Thanks!
It came out that the problem was the 'length' field itself. This name leads to problems on the javascript side. Renaming leads to the desired result.

Odoo 10 selection fields value

How can i get selection fields value in odoo 10?
def compute_default_value(self):
return self.get_value("field")
I tried this,
def compute_default_value(self):
return dict(self._fields['field'].selection).get(self.type)
Also tried this,but it is not working.
Please help me, i could not find the solution.
Thank you.
You can do this in a following manner:
self._fields['your_field']._desription_selection(self.env)
This will return the selection list of pairs (value, label).
If you just need possible values, you can use get_values method.
self._fields['your_field'].get_values(self.env)
But it's not a common way. Most of the time people define selections differently and then use those definitions. For example, I commonly use classes for those.
class BaseSelectionType(object):
""" Base abstract class """
values = None
#classmethod
def get_selection(cls):
return [(x, cls.values[x]) for x in sorted(cls.values)]
#classmethod
def get_value(cls, _id):
return cls.values.get(_id, False)
class StateType(BaseSelectionType):
""" Your selection """
NEW = 1
IN_PROGRESS = 2
FINISHED = 3
values = {
NEW: 'New',
IN_PROGRESS: 'In Progress',
FINISHED: 'Finished'
}
You can use this class wherever you want, just import it.
state = fields.Selection(StateType.get_selection(), 'State')
And it's really handy to use those in the code. For example, if you want to do something on a specific state:
if self.state == StateType.NEW:
# do your code ...
I don't get the question fully, but let me try to answer. Why not just define the selection as method and use it for both situations:
from datetime import datetime
from odoo import models, fields
class MyModel(models.Model):
_name = 'my.model'
def month_selection(self):
return [(1, 'Month1'), (2, 'Month2')]
def compute_default_value(self):
selection = self.month_selection()
# do whatever you want here
month = fields.Selection(
selection=month_selection, string='Month',
default=datetime.now().month, required=True)

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 to standardize field values on create or write in Odoo 8?

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.