How to get old value of a field in a fucntion - Odoo12 - odoo

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)

Related

How to make antispam function discord.py?

I need antispam function on my discord server. Please help me. I tried this:
import datetime
import time
time_window_milliseconds = 5000
max_msg_per_window = 5
author_msg_times = {}
#client.event
async def on_ready():
print('logged in as {0.user}'.format(client))
await client.change_presence(activity=discord.Activity(type=discord.ActivityType.playing,name="stack overflow"))
#client.event
async def on_message(message):
global author_msg_counts
ctx = await client.get_context(message)
author_id = ctx.author.id
# Get current epoch time in milliseconds
curr_time = datetime.datetime.now().timestamp() * 1000
# Make empty list for author id, if it does not exist
if not author_msg_times.get(author_id, False):
author_msg_times[author_id] = []
# Append the time of this message to the users list of message times
author_msg_times[author_id].append(curr_time)
# Find the beginning of our time window.
expr_time = curr_time - time_window_milliseconds
# Find message times which occurred before the start of our window
expired_msgs = [
msg_time for msg_time in author_msg_times[author_id]
if msg_time < expr_time
]
# Remove all the expired messages times from our list
for msg_time in expired_msgs:
author_msg_times[author_id].remove(msg_time)
# ^ note: we probably need to use a mutex here. Multiple threads
# might be trying to update this at the same time. Not sure though.
if len(author_msg_times[author_id]) > max_msg_per_window:
await ctx.send("Stop Spamming")
ping()
client.run(os.getenv('token'))
And it doesn't seem to work when I type the same message over and over again. Can you guys please help me? I need the good antispam function which will work inside on_message
I think the best thing you can do is to make an event on_member_join, which will be called every time user joins. Then in this event, you can make a list instead of variables that will save user id, and their current currency.
users_currency = ["user's id", "5$", "another user's id", "7$"] and so on. Next, I would recommend saving it to a text file.
Example code
global users_currency
users_currrency = []
#client.event
global users_currency
async def on_member_join(member): #on_member_join event
user = str(member.id) #gets user's id and changes it to string
users_currency.append(user) #adds user's id to your list
users_currency.append("0") #sets the currency to 0
Now if someone will join their id will appear in list and change their currency to 0.
How can you use assigned values in list
If you keep the code close to example higher then on users_currrency[0], users_currrency[2], [...]. You will get users' ids and on users_currrency[1], users_currrency[3], etc. You will get their currency. Then you can use on_message event or #client.command to make command that will look for user's id in list and change next value - their currency.
Saving it to a text file
You have to save it in a text file (Writing a list to a file with Python) and then make a function that will run at the start of the bot and read everything from the file and assign it inside your list.
Example code:
with open("users_currency.txt") as f:
rd=f.read()
changed_to_a_list=rd.split()
users_currency = changed_to_a_list

How to update/ delete a key from a context in Odoo 10?

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.

How to get values in onchange with sudo()

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

Odoo 10 getting the id of the selected value of many2one

I have this many2one field that I want to get the ID of the selected value
service = fields.Many2one('mainservices.mtvehlog', string='Select Service(s)')
so onchange i want to know the id and do something after knowing (I want to make other fields invisible)
What if the many2one is multi selection? How do you get the ID's that have been selected?
#api.depends('service')
def _compute_hide(self):
source_obj=self.pool.get('mainservices.mtvehlog').browse(self.service)
if source_obj == '1':
self.services_selected = source_obj
self.hide = True
else:
self.services_selected = source_obj
self.hide = False
kindly help
In compute methods using depends decorator always loop throuth self first.
for rec in self:
# and this is how you get the id value
#you don't have to browse you have direct acces to properties of the selected value
if rec.service.id == somthing :
# and you will not get except singlton error
you question is not clear what you want to do with the id value exactly.

Expected singleton odoo 9

After enter 2 and more new row in tree view and click on save get error
raise ValueError("Expected singleton: %s" % self)
ValueError: Expected singleton: my.model(2116, 2117)
My source code:
#api.depends('start', 'finish','stop')
def total_fun(self):
time1 = datetime.strptime(self.start, "%Y-%m-%d %H:%M:%S")
time2 = datetime.strptime(self.finish, "%Y-%m-%d %H:%M:%S")
self.total = round(((time2 - time1).seconds / float(60*60) - self.stop))
Error message says -> expected singleton this means: you are using recordset instead of record.
To fix this use
for rec in self:
in the begining of function, and then use rec instead of self
As you can see in the error message Expected singleton: my.model(2116, 2117)
By default in odoo the self is always a recordSet (means it can contain more then one record.) so when you do self.getSomeField here odoo will be confused wich record you want to get the value from.
if you don't tell odoo that make sure that the self will always contain one record when you acces an attribute if the recordSet contains more than one record this error is raised.
Now how to tell odoo make sure there is always one record is by adding #api.one decorator to the method. but is not recommended because odoo in your case there is two record so he will loop and call the method for each record and pass a recordSet that have only that record. imagine that you execute a search or any communication with database.
so don't use #api.one only if you are sure of what you are doing because you can make 10000 method call and interact with database.
like this example using #api.one:
# every call to this method you will execute a search.
self.env['some.model'].search([('m2o_id' , '=', self.id)]
you can do this before the loop:
# one query for all record with one call to the method
result = self.env['some.model'].search([('m2o_id' , 'in', self.ids)]
for rec in self:
# use result here
# or here ..