How to return a domain from Odoo server action - odoo

I am trying to create a server action via the Odoo UI that will alter the domain of another field in the view. This seems to be a pretty common use-case when dealing with the Odoo source code as you can see in the following documentation:
https://www.odoo.com/documentation/10.0/reference/orm.html#odoo.api.onchange
In those docs, they indicate that if I were in the source code of the model, I can define an onchange method and return a domain, for example, the behavior I'm trying to accomplish in the sale.order.line model would be:
#api.onchange('product_id')
def _onchange_product(self):
return {
'domain': {'route_id': [('id', 'in', x_all_route_ids.ids)]}
}
In other words, when the product of an sales order line changes, update the available options in the route_id field.
Is there any way to accomplish this same thing via a server action created through the UI? I am having trouble figuring out how to return a domain from the Python code.
The notes in the code section say:
# Available variables:
# - time, datetime, dateutil, timezone: Python libraries
# - env: Odoo Environement
# - model: Model of the record on which the action is triggered
# - record: Record on which the action is triggered if there is one, otherwise None
# - records: Records on which the action is triggered if there is one, otherwise None
# - log : log(message), function to log debug information in logging table
# - Warning: Warning Exception to use with raise
# To return an action, assign: action = {...}
I don't see how I can use this to return a domain. Does anybody have any idea?
I have tried setting the python code field to simply:
domain = {'route_id': [('id', 'in', record.x_all_route_ids)]}
But that doesn't work. The route_id list is unchanged.

I got some insight from Odoo technical support and it turns out this is possible. Anything assigned to the action variable will be treated as the return value for the server action.
So you can simply do:
action = {
'domain': {
'route_id': [('id', 'in', record.x_all_route_ids.ids)]
}
}

It is not possible. Only these special on-change methods return values will be evaluated correctly.

Related

how to set domain with python in odoo

Is it possible to set domain on the python side without putting it in view?
I tried to set the domain in the customer field (sale order) based on branch in the Customer menu/template
I have tried this but why it doesn't work ?
#api.onchange('partner_id')
def _onchange_cust_domain(self):
for rec in self:
if self.branch_id.id:
cust_list = rec.partner_id.branch_id.id
return {'domain' : {'partner_id' : [('branch_id', '=', cust_list)]}}
To set the partner_id domain when the branch_id field value changes, use the following onchange function:
Example:
#api.onchange('branch_id')
def onchange_branch_id(self):
if self.branch_id:
return {'domain': {'partner_id': ['|', ('branch_id', '=', False), ('branch_id', '=', self.branch_id.id)]}}
Odoo sets by default the following domain from the python code:
['|', ('company_id', '=', False), ('company_id', '=', company_id)]
I think your problem is that you are making a domain for a field partner_id, but in the onchange method of the same field.
It makes no sense to me. So if you put a Customer with branch X, then you can only search customers of branch X for that sale_order (if that domain do the job).
I dont know what are you trying to do exactly, but i think than changing a domain in the same field you are changing the value it is no possible.
Your code would be correct if that domain applies to another field.
This is a working exaple. Notice that onchange field method and field to apply domain are diferent:
#api.onchange('operating_unit_id')
def _applicants_onchange(self):
if self.operating_unit_id:
return {'domain': {'applicant_id': [('default_operating_unit_id', '=', self.operating_unit_id.id)]}}
return {}
Returning a domain using onchange method has been deprecated in Odoo 14.
You can use the new way used by Odoo:
Returning a domain using onchange method has been deprecated in Odoo 14.
You can use this new way.

How to store logged user in Odoo14

I am trying to store the currently logged user into a many2one field using compute method. It's working fine if i define the Mnay2one field without the store="True" parameter. Actually, i need to save it.
Here is the code:
def get_logged_user(self):
for rec in self:
print('inside get_logged_user---------------',rec.env.user.name)
rec.logged_user_id = rec.env.user.id
logged_user_id = fields.Many2one('res.users',string="Logged user",store=True,compute="get_logged_user")
EDIT:
If you only need to control visibility of a field/button inside QWeb view you could archive this without dedicated field. You could use context.get('uid') to get current user like this:
<button ... invisible="context.get('uid') == assigned_user_id">
But if you need to store logged-in user inside a field you could use default instead of compute.
Something like this:
logged_user_id = fields.Many2one('res.users', string="Logged user", default=lambda self: self.env.user)
Note usage of lambda function.
If you really need to use compute field with store=True you need to specify when to compute it. By using api.depends decorator you can trigger it when your_field is changed.
#api.depends('your_field')
def get_logged_user(self):
But I would ask a question why do you need to store logged-in user inside a field? If you could provide more context maybe we could suggest different solution.

How to pass an external id to a domain filter

I have a Many2One field accepting a product in my model, but I want to limit this field to a specific product template, using the said template external id (The XML id).
I've try this without success:
#This piece of code doesn't work
the_product = fields.Many2one('product.product',
domain = [('product_tmpl_id','=', "ref('the_package.the_external_id')")])
How can I do this?
The solution to this problem is to use a function that returns the filters parameters. That way, we have access to the self variable in the body of the function, and therefore, we can use it to search specific external ids.
#api.model
def _get_domain(self):
# We have access to self.env in this context.
ids = self.env.ref('the_package.the_external_id').ids
return [('product_tmpl_id','=', ids)]
the_field = fields.Many2one('product.product',
required=False,
domain = _get_picking_product_domain)
The above solution does does not work.
Error :
ProgrammingError: can't adapt type 'model_name'
But try this:
You don't have to insert domain in your field :
#api.onchange('field_a')
def fiel_change(self):
dom['field_a'] = [(*)] #* type your domain
return {'domain':dom}

Reach through several relationships to get model from ember-data

I am setting the contents of one menu based on the selected value of another one. The first menu is a set of companies. A Company has an OrderProfile; an OrderProfile has a Warehouse. So based on a known Company, I need to reach through the OrderProfile and get a Warehouse.
Here's what happens in the console when I have the Company in a company variable:
company.get('orderProfile.warehouse.name') # Ember-data gets the OrderProfile but not the Warehouse
// => null
var op = company.get('orderProfile')
// => undefined # The OrderProfile assigned to the variable
var warehouse = op.get('warehouse')
// undefined # Now the Warehouse is assigned to the variable
warehouse.get('name')
// => "Warehouse 1" # This was null when we asked for company.orderProfile.warehouse.name
So I know I need to unravel this in several steps, waiting for each to complete. Is there an Ember idiom for doing this?
N.B. this is using:
DEBUG: Ember : 1.5.0-beta.1+canary.13995621 index.js:3496
DEBUG: Ember Data : 1.0.0-beta.7+canary index.js:3496
Here's how we worked this out. As I suspected, the key was handling the promises correctly, but we also had to be sure we had async: true set on the relevant relationships.
warehouse = company.get("orderProfile.warehouse.name") # Doesn't work
# Instead, we need to treat orderProfile as a promise, and ask for the warehouse
# once it resolves:
company.get("orderProfile").then(
(orderProfile) ->
return orderProfile.get("warehouse") if orderProfile
).then(
(warehouse) =>
# Do something with the warehouse
)
As I understand it, the relationships - specifically orderProfile: DS.belongsTo 'orderProfile', async: true in the Company model - need async: true to remind Ember-Data that it might not already have the referenced models, and it needs to fetch them. I was on the right track figuring out the promise handling, but the async: true part was the real key.

How to test an application when each call changes state of the system?

Testing is simple when you have methods that you can call 100 times and they yield the same results. But how do you test something like an Api, where you have something like this:
int CreateUser(username,password); //returns the id of the user, -1 if error
int SubmitOrder(username,password,productName,quantity);//returns the id of the order -1 if error
int CancelOrder(username,password,orderId); //returns -1 if error
How would you test that this Api works? How do you create test data? I can not write unit tests fot it, I can not use the same test data, since I can not create a user two times(UserName is unique). When I submit an order I always get different orderIds as responses.
You need to find some way to "reset" system to well-known initial state, i.e. state with no users nor orders.
Also you need to find some way to observe state in the system. This way could be actually destructive, i.e. it could modify or even damage state of the system, but though. In your case, method CreateUser could be used as such observer to check whether user already exists, because it is known to return -1 in such situation.
Here is how one of your test cases could look like:
reset (); // Each test case should start with reset
assertNotEquals (-1, CreateUser ("foo", "bar")); // This should work fine
assertEquals (-1, CreateUser ("foo", "zoo")); // Make sure user "foo" does exist
assertNotEquals (-1, SubmitOrder ("foo", "bar", "apple", 1)); // Make sure user can pass authentication
assertEquals (-1, SubmitOrder ("foo", "zoo", "apple", 1)); // Make sure password is actually checked
The above test case checks that CreateUser actually creates user with given name and passwords, and does not allow creating two users with the same name.
Here is another test case:
reset ();
CreateUser ("foo", "bar");
orderID = SubmitOrder ("foo", "bar", "apple", 1); // Submit order
assertNotEquals (-1, CancelOrder (orderID)); // Make sure order was really created
assertEquals (-1, CancelOrder (orderID)); // Make sure order was cancelled
And so on. Of cause it would be better to find more straightforward ways to observe system state, e.g. by querying database directly.
I assume you are talking about some OOP language. I think you can solve your problem by Unit Testing and Dependency Injection.
Core concept:
first you do test of your DataBase class
when testing API you inject a FakeDataBase class into it (that shares same Interface)
So in testing environment API writes to some other fake database or simply prints queries to a file and you just check if content of this file is what you expected.
Great video:
http://www.youtube.com/watch?feature=player_embedded&v=wEhu57pih5w#!
http://en.wikipedia.org/wiki/Dependency_injection
http://en.wikipedia.org/wiki/Unit_testing