I'm trying to get the ID of the currency in a Journal Item but I get a False boolean value for some reason.
Here's the relevant code:
class JournalItem(models.Model):
_name = "account.move.line"
_inherit = "account.move.line"
def _get_current_rate(self):
...
print(self.currency_id.id)
#currency_id is a Many2one field of comodel res.currency.
rate = fields.Float(string="Rate", digits=(12, 6),
default=_get_current_rate)
Which outputs:
False
I toyed around with the #api.multi and #api.one decorators but noticed no changes. Feels like I'm missing something crucial.
Update 1:
self is account.move.line() when checked
This should be like account.move.line(1, 2, 3, 4, ...) maybe it's something to do with the default attribute, will update as I figure it out.
Update 2:
The main thing here is the lack of a currency, it persists even when given a valid Journal Entry record number
From the 'account' module:
#api.model
def _get_currency(self):
currency = False
context = self._context or {}
if context.get('default_journal_id', False):
currency = self.env['account.journal'].browse(context['default_journal_id']).currency_id
return currency
This is unlikely to be the issue since currency_id returns a res.currency object, it's only when I further look into it that I get False return values.
Update 3:
It appears that currencies not explicitly set during transaction creation are set to False, this is likely the source of my troubles. Fixed by testing for it and replacing by environment's default.
Methods for default values always have an empty recordset. You should fill the value in one of the following ways:
compute the value and store it (little hint: call the currency field rate with the journal entries date in context --> currency.with_context(date=<journal_entry_date>).rate --> because this field is computed and context date is taken into the computation)
override create/write and try to figure out the rate
Related
I have a many2one field as A and one2many field as A_details that A_details has filter base on A,
A = fields.Many2one(comodel_name="headertable")
A_details = fields.One2many(comodel_name="detailtable")
and in the xml I pass the A value with context in A_detail to filter it
<field name="A_detail" context="{'parent_id': A,}"/>
now I want to delete A_detail's records when user changes the A value, so I use onchange decorator on A field like this:
#api.onchange('A')
def _delete_selected_records(self):
for rec in self.A_details:
self.A_details = [(3, rec.id, 0)]
this function workes correctly to create mode but the problem occurs when I open the record from tree view and while A field is getting value from model Onchanged decorator call the _delete_selected_records function and it delete all the A_detail's records, That's why I want to check in this function that if user change the A, delete the A_detail's records else if system sets value to field do nothing... how can I handle this???
You should add a check if A is deleted or not meaning if A is False then do X:
#api.onchange('A')
def _delete_selected_records(self):
for rec in self:
if rec.A is False:
rec.A_details = [(3, rec.id, 0)] # You can you .unlink() btw
else:
continue
This way even if the function is triggered by let's say a user accidentally putting cursor on the field in the view or something you won't risk deleting anything unless the correct condition is met.
In sales order, there is a field discount %. Is it possible to ensure the users only input value lower than x and it will not accept any value higher than x.
You can achieve this with creating a function using #api.onchange('discount') decorator on python code to ensure discount value is not higher than x, if higher then set x to discount field and also possible to show warning popup from that function.
But if python code change is not prefered, you can also achieve this with Automated action where you create a new rule on sale.order.line, set trigger On form modification, action Execute python code and add the following code
if record.discount > 10:
record.discount = 10
Where 10 is the value of x. This will ensure discount is always less than x.
Op1: If you want to ensure that behaviour you can add an sql constraint to the database, this will work fine if your X value never change, something like this:
_sql_constraints = [
('discount_less_than_X', 'CHECK(discount < X)', 'The discount value should be less than X'),
]
This constraint will trigger in all cases(create, write, import, SQL insert).
Replace X with the value desired.
Op2: Use decorator api.constrains, get X value from somewherelse and apply the restriccion, something like this:
#api.constrains('discount')
def _check_discount(self):
# Create a key:value parameter in `ir.config_parameter` table before.
disscount_max_value = self.env['ir.config_parameter'].sudo().get_param('disscount_max_value')
for rec in self:
if rec.disscount > int(disscount_max_value):
raise ValidationError('The discount value should be less than {}'.format(disscount_max_value))
I hope this answer can be helful for you.
I am trying to get the old value of a field in onchange method.
Here is what I tried.
#api.onchange('assigned_to')
# #api.depends('assigned_to')
def onchange_assigned_to(self):
print('onchange_assigned_to')
history = self._origin.read(["assigned_to"])
if history:
id = history[0]["assigned_to"][0]
last_assigned = self.env['res.users'].browse([id])
self.last_assign_id = last_assigned
The above code is working and I getting the old value only if I change the field value through GUI.
I am also changing the field value via button action., that time this function is not working.
How can I achieve this?
And I also tried on compute function with #api.depends.
That time I got an
'AttributeError: 'crm.lead' object has no attribute '_origin''
You can implement this in write method, and will always work, you can keep the onchange
if you want to give a feedback to the user:
#api.multi
def write(vals):
""" keep assignment history. """
if 'assigned_to' in vals: # keep assignment history
# no need to keep it in vals here
vals.pop('last_assign_id', None)
for rec in self:
rec.last_assign_id = rec.assigned_to
return super(ClassName, self).write(vals)
Hi i am trying to delete a key value from the context for res.partner form view.
I opening the partner form view using controller function and trying to set phone number as default and its working fine. But when i try to create a new customer by clicking on the create button the phone number again auto-filled. In order to avoid this behaviour, in default_get function, i copied the context into another variable, removed the key value from the context using del context['cc_mobile']. And reassigned to self.env.context. But when i try to create a new customer, the deleted key value comes in the context again.
Controller.py
#http.route('/open_customer/<string:val>', type="http",method=['POST','GET'],website=False, auth="public")
def open_case_window(self,**kw):
mobile_no = kw.get('val')
action = request.env.ref('base.action_partner_form').sudo()
mobile_flag = 0
partner = 'res.partner'
partner_model = request.env[partner]
regex = re.match( '^(?:\01|02|03|04|06|07|09)\d*$', mobile_no)
if regex:
mobile_flag = 0
partner_id = partner_model.search([('phone', '=', mobile_no)]).id
else:
mobile_flag = 1
partner_id = partner_model.search([('mobile','=',mobile_no)]).id
if partner_id:
return werkzeug.utils.redirect('/web#id='+str(partner_id)+'&view_type=form&model='+partner)
else:
context = dict(action._context)
if mobile_flag == 0:
context.update({'cc_phone': mobile_no})
else:
context.update({'cc_mobile': mobile_no})
context.pop('lang')
url = werkzeug.utils.redirect('/web?debug=#view_type=form&model='+str(partner)+'&action=%s'%(action.id))
return url
ResPartner.py
#api.model
def default_get(self, fields):
context = self.env.context.copy()
print'default_get context',context
res = super(Partner, self).default_get(fields)
if 'cc_mobile' in context:
res.update({'mobile':context.get('cc_mobile')})
if 'cc_phone' in context:
res.update({'phone':context.get('cc_phone')})
if context.get('cc_mobile'):
del context['cc_mobile']
if context.get('cc_phone'):
del context['cc_phone']
self.env.context = context
print'self.env.context after',self.env.context
action = self.env.ref('base.action_partner_form').sudo()
action.env.context = self.env.context
return res
You cannot remove a key of action context from python side, because it's in the client side. when ever you call the server like search in many2one field, create a record in fly you will see this context comeback again every time (The way Odoo work).
What you need is something that will be used for one time, I think you need some kind of persistence for example:
dummy model that contains user_id, model_name, value, active fields so in the controller you create a record for default value for that specific user.
get that value by overriding default_get by searching with user_id and model_name field and hide that value or delete it.
this way when yo hit create button or create contact in fly when you search for the value it will be gone so it will not be used a second time.
This a simple Idea and easy to implement, you need to handle some cases to prevent user from saving two default value if some interruption happens should not be hard.
Edit
After second thought to prevent any error when you create a record just pass it's ID in the context with a special key, then use That Id to retrieve it, use it then delete it. easier, safer and no need for search.
I have added one onchange method, in that onchange method I have used sudo() while accessing many2one field.
But with sudo(), I am not able to get record's values with sudo.
So how can I get values of onchange record (<odoo.models.NewId object at 0x7fba62f7b3d8>) with sudo().
Here is sample code :
#api.onchange('product_id')
def onchange_product_id(self):
for record in self:
print(record.product_id)
print(record.sudo().product_id)
Actual result :
product.product(13,)
product.product()
Expected result :
product.product(13,)
product.product(13,)
That's because the recordset doesn't exist outside the current transaction. So your current user can see the contents but other users can't.
The code looks good to me, in fact, if you see path_to_v12/addons/hr_expense/models/hr_expense.py lines 563-567, you'll see a similar code:
#api.onchange('employee_id')
def _onchange_employee_id(self):
self.address_id = self.employee_id.sudo().address_home_id
self.department_id = self.employee_id.department_id
self.user_id = self.employee_id.expense_manager_id or
self.employee_id.parent_id.user_id