Odoo8 - Need a example of ir.actions.server executing multiple actions one after the other - odoo-8

In a wizzard view I have a button returning pdf report in a 'ir.action.act_url'. It works fine. Problem is that after the pdf appeared I would like the wizzard window to be closed automatically. To do this I can return close_window dict.
Separately this two 'return' work fine.
I would like to execute two actions, one after another. I found that this is possible using ir.action.server with multi attribute.
Unfortunately I couldn't find even one example.
close_window = {'type': 'ir.actions.act_window_close'}
final_report = {
'type': 'ir.actions.act_url',
'url': '/web/binary/saveas?model=ir.attachment&field=datas&
filename_field=name&id=' + str(file.id),
'target': 'self',
}
return final_report

Check the ir_actions tests that ship with Odoo, they might be able to help you in the right direction.
base/tests/test_ir_actions.py
def test_60_multi(self):
cr, uid = self.cr, self.uid
# Data: 2 server actions that will be nested
act1_id = self.ir_actions_server.create(cr, uid, {
'name': 'Subaction1',
'sequence': 1,
'model_id': self.res_partner_model_id,
'state': 'code',
'code': 'action = {"type": "ir.actions.act_window"}',
})
act2_id = self.ir_actions_server.create(cr, uid, {
'name': 'Subaction2',
'sequence': 2,
'model_id': self.res_partner_model_id,
'state': 'object_create',
'use_create': 'copy_current',
})
act3_id = self.ir_actions_server.create(cr, uid, {
'name': 'Subaction3',
'sequence': 3,
'model_id': self.res_partner_model_id,
'state': 'code',
'code': 'action = {"type": "ir.actions.act_url"}',
})
self.ir_actions_server.write(cr, uid, [self.act_id], {
'state': 'multi',
'child_ids': [(6, 0, [act1_id, act2_id, act3_id])],
})
# Do: run the action
res = self.ir_actions_server.run(cr, uid, [self.act_id], context=self.context)
# Test: new partner created
pids = self.res_partner.search(cr, uid, [('name', 'ilike', 'TestingPartner (copy)')]) # currently res_partner overrides default['name'] whatever its value
self.assertEqual(len(pids), 1, 'ir_actions_server: TODO')
# Test: action returned
self.assertEqual(res.get('type'), 'ir.actions.act_url')
# Test loops
with self.assertRaises(except_orm):
self.ir_actions_server.write(cr, uid, [self.act_id], {
'child_ids': [(6, 0, [self.act_id])]
})
Have not done it myself, but it looks like you have to specify that it is a multi action, and specify the child_ids actions to be executed.
Also keep in mind that according to the docs (https://www.odoo.com/documentation/8.0/reference/actions.html):
multi
Executes multiple actions one after the other. Actions to execute are
defined via the child_ids m2m. If sub-actions themselves return
actions, the last one will be returned to the client as the multi's
own next action

According to #dgeorgiev I wrote further code. I managed to create server actions and individually this actions are able to return pdf file or close the window. But I couldn't combine these two return's. See below:
# first server action returning pdf
ir_actions_server = self.env['ir.actions.server']
act1_id = ir_actions_server.create({
'type': 'ir.actions.server',
'name': 'divided_package_labels',
'sequence': 1,
'model_id': self.id,
'state': 'code',
'code': 'action = {"type": "ir.actions.act_url", "url": "/web/binary/saveas?model=ir.attachment&field=datas&filename_field=name&id= %s", "target": "new"}' % str(file.id),
})
# second server action closing window
act2_id = ir_actions_server.create({
'type': 'ir.actions.server',
'name': 'Close_sale.package.wizard',
'sequence': 2,
'model_id': self.id,
'state': 'code',
'code': 'action = {"type": "ir.actions.act_window_close"}'
})
# server action for combining two previously described
act_id = ir_actions_server.create({
'type': 'ir.actions.server',
'name': 'TestAction',
'condition': 'True',
'model_id': self.id,
'state': 'multi',
# 'child_ids': [(6, 0, [act1_id.id])] # return pdf
# 'child_ids': [(6, 0, [act2_id.id])] # close window
'child_ids': [(6, 0, [act1_id.id, act2_id.id])] # close window, no pdf file
})
print act_id, act1_id, act2_id
print "act_id.child_ids", act_id.child_ids
# shows that the relations are properly made
# run
return act_id.run()
Probably there is a small mistake, but I couldn't find it.

Related

What is a good practice to map my Elasticsearch index?

In order to model different types of metrics and audit logs when a search results should include both of them.
What is more encouraged in terms of fast search performance and indexing efficiency from the 3 options below:
Mapping different subfields for each object
Using a generic object that has predefined fields and a dynamic extra metadata field
Using a different index for each object
Examples for each option:
Option 1 - Mapping different subfields under the same index unified_index_00001
{
'type': 'audit_log',
'audit_log_object': {
'name': 'object_name',
'action': 'edit',
'before': 'field_value',
'after': 'field_value_update'
}
},
{
'type': 'network',
'network': {
'name': 'network_name',
'event': 'access',
'ip': 'xxx.xxx.xxx.xxx'
}
},
{...} ...
Option 2 - Mapping a generic object under the same index unified_index_00001
{
'type': 'audit_log',
'name': 'object_name',
'action': 'edit'
'meta': {
'before': 'field_value',
'after': 'field_value_update'
}
},
{
'type': 'network',
'name': 'network_name',
'action': 'access',
'meta': {
'ip': 'xxx.xxx.xxx.xxx'
}
},
{...} ...
Option 3 - Using a different index for each object
audit_log_index_00001
{
'name': 'object_name',
'action': 'edit',
'before': 'field_value',
'after': 'field_value_update'
},
{...}
...
metric_index_00001
{
'name': 'network_name',
'event': 'event_type',
'ip': 'xxx.xxx.xxx.xxx'
},
{...} ...
Note: There is only need for Term indexing (no need of text searches)
In Elasticsearch, you usually want to start with the queries and then work backwards from there.
If you always query only events of one type, separate indices make sense. If there are mixed queries - you would be happier with a joint index, and usually with extracted common fields ("option 2") otherwise those queries won't really work.
Also, take into account Elasticsearch limitations:
fields per index (1000 by default)
shards and indices per cluster (thousands but still)
etc

Spring Data: Update an element in an objects array of a nested MongoDB document

I'm building Spring/MongoDB backend app for an online store and I have the next structure of MongoDB document:
'client_data': [
'_id': 'sdas2DSA',
'client_name': 'aurelis',
'stores': [
{
'store_id': 1,
'products': [
{
'product_id': 1,
'name': 'T-shirt',
'price': 120
},
{
'product_id': 2,
'name': 'Pants',
'price': 120
},
],
}
]
]
strong text
I need to implement logic of updating a product entity of the given client's store by product_id, but I'm stucked at the stage of querying.
Suppose the client with the 'client_name'='aurelis' is sent a post request with parameter 'store_id'=1 with the given body:
{
'product_id': 1,
'name': 'T-shirt',
'price': 90
},
How can I built an query that updates the product object of the specific client's store by product_id?
I tried to implement the functionality by myself:
Query query = new Query(new Criteria().andOperator(
Criteria.where("client_name").is(clientName),
Criteria.where("stores.store_id").is(storeId),Criteria.where("stores.products").elemMatch(Criteria.where("products.product_id").is(product_id))
));
Update update = new Update().set("stores.products", product)
mongoTemplate = new MongoTemplate().findAndModify(query, update, Product.class);
but it doesn't work.
I'd be grateful for your answers:)

Chaining rxjs 6 observables with nested array

I have to do 3 dependent request to an API.
The first retreive an array of user id's.
The second have to iterate over the user id's array and for each retreive an array of project id's related to the user.
The third have to iterate over the project id's array and retreive data related to projects.
I want this kind of result:
[{'username': 'richard', projects: [{"id": 1, "name": "test"}]}, ...]
But i'm totally stuck with mergeMap, forkJoin etc..
The thing i have tried:
getUserTeamMembers(): Observable<any> {
return this.http.get(`${environment.serverUrl}/user/profil`).pipe(
mergeMap((teamUsers: any) =>
this.http.get(
`api/user/${teamUsers.data._id}/team`
)
),
mergeMap((teamUsers: any) =>
forkJoin(
...teamUsers.data.map((user: any) =>
this.http.get(`api/user/${user.id}`)
)
)
),
map((res: any) =>
res
.map((user: any) => {
if (user.data) {
return {
firstname: user.data.local.firstname,
lastname: user.data.local.lastname,
email: user.data.local.email,
projects: user.data.local.projects,
language: user.data.local.lang
};
}
})
.filter((user: any) => user !== undefined)
),
tap(t => console.log(t)),
catchError(err => throwError(err))
);}
I try so many things, but i have no clue where to populate my array of projects which is currently only contains id's, not data related:
[{'username': 'richard', projects: ['id1', 'id2']}, ...]
have made a similar example hope it clear the idea.
of course did't use http instead made it local using of
and stored the final result in an array called results
we have a source to get the list of all users, another source that given the user id it will return the list of this user's projects' ids projectList, and finally a source that give one project id will returns it's details(projcectsDetails)
let users = of([ 1, 2, 3 ])
let projectsList = (userId) => of([ userId, userId*2 ])
let projectsDetails = (projectId) => of({ id: projectId, details: `details about ${projectId}` })
let results = [];
users
.pipe(
tap(users => {
users.forEach(userId => results.push({ userId, projects: [] }));
return users
}),
switchMap(users => forkJoin(users.map(userId => projectsList(userId)))),
switchMap(projectsArray => forkJoin(
projectsArray.map(
oneProjectArray =>
forkJoin(oneProjectArray.map(projectId => projectsDetails(projectId)))
)
)
),
tap(projectDetailsArray => {
projectDetailsArray.forEach((projectsArray, index) =>{
results[index]['projects'] = [ ...projectsArray ]
})
})
)
.subscribe(() => console.warn('final',results))
explanation
1- initalize the result array from the given users
[
{ userId: X, projcts: [] },
{ userId: Y, projcts: [] },
....
]
2- for each give users we want the projects ids he/she has
so the first switchMap will return (in order)
[
[ 1, 2, 3, ...] project ids for the first user,
[ 8, 12, 63, ...] project ids for the second user,
...
]
3- in the second switchMap we iterate over the previous array
(which is an array that each item in it is an array of projects ids)
so we needed two forkJoin the outer will iterate over each array of projects ids(the big outer
array previously mentioned)
the inner one will iterate over each single project id and resolve it's observable value(which is the
project details)
4- finally in the tap process we have an array of array like the 2nd step but this time each
array has the projects details and not the projects ids
[
{ userId:1, projects: [ { id:1, details: 'details about 1' }, { id:2, details: 'details about 2' }]},
{ userId:2, projects: [ { id:2, details: 'details about 2' }, { id:4, details: 'details about 4' }]},
{ userId:3, projects: [ { id:3, details: 'details about 3' }, { id:6, details: 'details about 6' }]}
]

Return view with activated filter in Odoo

I'm trying to return a tree view in Odoo filtered by a parameter. Some one knows how to code the variable my_context of the view?
I need to apply the filter with the field: father_competence_id
The filter is defined as:
<filter name="groupby_fathercompetence"
context="{'group_by' : 'father_competence_id'}"
string="Father competence" />
academic_record_lines = self.env['education.record']
for line in self:
academic_record_lines = academic_record_lines + line.env['education.record'].search([('n_line_id', '=', line.id)])
return {
'name': _('Academic records for {} [{}]').format(
description, self.planification_id.teacher_id.name),
'view_type': 'form',
'view_mode': 'tree,form',
'res_model': 'education.record',
'type': 'ir.actions.act_window',
'context': **my_context**,
'domain': [('id', 'in', academic_record_lines.ids)],
}
Please use the below code
my_context = dict(self._context or {})
my_context.update({'':})
Using my_context.update you can add filters through context.
return {
'name': _('Academic records for {} [{}]').format(
description, self.planification_id.teacher_id.name),
'view_type': 'form',
'view_mode': 'tree,form',
'res_model': 'education.record',
'type': 'ir.actions.act_window',
'context': my_context,
'domain': [('id', 'in', academic_record_lines.ids)],
}
If you know the filter name, which is normally set by attribute name on the search <filter> entries, you can automatically activate them by an action by providing the filter name with search_default_ in front of it and as value a truthy value like True or 1.
academic_record_lines = self.env['education.record'].search(
[('n_line_id', 'in', self.ids)])
context = dict(self.env.context or {})
context['search_default_groupby_fathercompetence'] = True
return {
# self.pla... only working with singleton!!!
'name': _('Academic records for {} [{}]').format(
description, self.planification_id.teacher_id.name),
'view_type': 'form',
'view_mode': 'tree,form',
'res_model': 'education.record',
'type': 'ir.actions.act_window',
'context': context,
'domain': [('id', 'in', academic_record_lines.ids)],
}

Why do I get this error?: Expected singleton: spray.action(1, 2)

I have a button in a form that when clicked is supposed to update the current model mrl with data from the spray.action model. Then I do further processing but it raises the error
ValueError("Expected singleton: %s" % self) ValueError: Expected singleton: spray.action(1, 2)
#api.multi
def mrlCreateSprayRecords(self):
spray_ids = []
vals = []
spray_obj = self.env['spray.action'].search([])
print("spray_obj \n\n\n\t %s ", spray_obj)
for obj in spray_obj:
print("Spray Action Objects \n\n %s \n\t ", obj)
vals = {
'ref': obj.ref,
'farm': obj.farm.farm,
'block': obj.block.block,
'valves': obj.valves.valve,
}
print("Spray Action Data Browse , \n\n\t %s ", vals)
res = super(Mrl, self).create(vals)
res.update(vals)
print("object in mrlCreateSprayRecords \n\n\t %s", res)
return {
'name': 'Update Mrl Operations',
'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'form',
'res_model': 'mrl',
'views': [(spray_obj.id, 'form')],
'view_id': spray_obj.id,
# 'target': 'new',
'res_id': self.id,
'context': self.env.context,
}
I think you are getting the error in the row 'view_id': spray_obj.id, you must write the view id there. The spray_obj recordset has many record, so you cannot use it like that (spray_obj.id). You can also remove the view_id parameter in order to user the default view.
#api.multi
def mrlCreateSprayRecords(self):
self.ensure_one()
spray_obj = self.env['spray.action'].search([]) # recordset of all records of the model????
for obj in spray_obj:
vals = {
'ref': obj.ref,
'farm': obj.farm.farm,
'block': obj.block.block,
'valves': obj.valves.valve,
}
self.create(vals)
view_id = self.env.ref('module.xml_view_id').id
return {
'name': 'Update Mrl Operations',
'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'form',
'res_model': 'mrl',
'view_id': view_id,
'res_id': self.id,
'context': self.env.context,
}
I have added self.ensure_one() because res_id has to be only one id as well.
I have remove the res.update(vals) line because it does not make any sense to me haha
More things
Instead of print you should use the logger:
import logging
_logger = logging.getLogger(__name__)
_logger.info('Hello')
Instead the line res = super(Mrl, self).create(vals) I think you should use this one
res = self.create(vals) # if you are in the mrl model
In short you get this kind of error when you acces a field direclty of a recordSet that contains more than one record.
You performed a search on the spray.action that search returnd 2 record (1, 2)
So when you do spray_obj.id odoo will be confused what id he should return 1 or 2. And here odoo throw this error.
So dont access a field a search result, or a x2many fiels they may have more than one record inside.
#ChesuCR has improved your code and corrected it

Resources