How to navigate back in view again in Odoo? - odoo

I have 2 screens about that I have passed the context and get the context in a method. just like this
stock_picking_obj=self.env['stock.picking']
context = dict(self._context) or {}
context.update({'is_first_picking_open':True})
stock_picking_obj.with_context(context)
When I receive context in the method.
#api.multi
def button_validate(self):
context=dict(self._context)
if context.get('is_first_picking_open'):
return {'type': 'ir.actions.client', 'tag': 'history_back'}
so basically I want to once a process has been that after coming back with the old screen.

That is not how you update the context in recent versions of Odoo.
First of all the context exists on the Environment and you can fetch it like this:
self.env.context and it will always be a dictionary so there is no reason for dict() or {}
Now, the with_context function returns the recordset you call it upon with the context changed. Example:
stock_pickings = stock_pickings.with_context(context)
and then you can
stock_pickings.button_validate() and you will be able to fetch the context by using self.env.context

Related

Question on How # and # String Trigger Works in Odoo Chatter?

Hope you guys doin well,
I'm curious about how can a certain string like # and # can trigger a popup of a user and channels in Odoo Chatter & Discuss. This chatter has mail.thread Models related to it but i can't seem to understand how is it possible to create my own custom trigger in chatter?
Is it belong to certain views and not in models?
Any help will be appreciated!
Those are suggestion delimiters for the models used to update the suggestion list.
const suggestionDelimiters = ['#', ':', '#', '/'];
For example # is used to fetch partners (mail.partner model) matching a given search term. The _updateSuggestionList function will search the suggestions, sort them then will show (update) the popup list
To add a new custom trigger, you need add the delimiter to the suggestionDelimiters list and alter the _computeSuggestionModelName function to return the related model, the model must be defined like the mail.partner model.
If you want to patch an existing model, check the following example (taken from the hr module):
/** #odoo-module **/
import {
registerInstancePatchModel,
registerFieldPatchModel,
} from '#mail/model/model_core';
import { attr, one2one } from '#mail/model/model_field';
// Patch model
registerInstancePatchModel('mail.partner', 'hr/static/src/models/partner/partner.js', {
async checkIsEmployee() {
await this.async(() => this.messaging.models['hr.employee'].performRpcSearchRead({
context: { active_test: false },
domain: [['user_partner_id', '=', this.id]],
fields: ['user_id', 'user_partner_id'],
}));
this.update({ hasCheckedEmployee: true });
},
});
// Patch fields
registerFieldPatchModel('mail.partner', 'hr/static/src/models/partner/partner.js', {
hasCheckedEmployee: attr({
default: false,
}),
});
There is another way, and that is using the / or "command" popup. You can add your own item in the popup list and trigger any actions when a user selects your item from the popup.
Inherit from mail.channel
class MailChannel(models.Model):
_inherit = 'mail.channel'
Add a new method that starts with _define_command_XXXXX. This registers the item in the popup.
def _define_command_sample(self):
return {'help': _('Here is some sample help')}
Add a new method that starts with _execute_command_XXXXX. This method is executed when the user uses your command.
def _execute_command_sample(self, **kwargs):
# Send a "temporary" message to the user
self._send_transient_message(self.env.user.partner_id, f"Hello! You just successfully used the /sample command. You typed {kwargs['body']}.")
Reference: Github where Odoo defines the /lead command
PS: The above is for Odoo V12.0 - V14.0. Odoo V15.0 works a bit different, but you can check this and this for how to do it.

Odoo 10 - Form view opened in edit mode

I'm developing on Odoo 10.
I created a dynamic form view which search and display a product from its barcode, but I've got a problem.
Since the view has no initial record to display it is opened in edit mode, and that's ok, because I want to type the 'barcode' field.
But, after the product is displayed, when I exit from that view the 'can_be_discarded' function is fired, opening the confirm dialog.
Have I to create a new view type inheriting from FormView or is there a way to workaround this problem?
The view is a classic form view, with nothing special.
Here's the server code instead.
class ProductFromBarcode(models.TransientModel):
_name = 'levelprime_product_general_status.product_from_barcode'
_inherits = { 'product.product': 'product_id' }
product_id = fields.Many2one(
comodel_name='product.product',
store=False)
product_barcode = fields.Integer(help='Insert the barcode to search '
'the correspondent product',
store=False)
#api.onchange('product_barcode')
def on_barcode_changed(self):
if self.product_barcode != 0:
self.product_id = self.get_product_from_barcode(self.product_barcode)
#api.model
def get_product_from_barcode(self, barcode):
r = self.env['product.product'].search([('barcode', '=', barcode)])
if r:
return r
Ok, I think I'm in the right way, there are some rendering problem to solve, but the main behavior is what I was searching for.
I created a new type of view which inherit from the 'FormView' one and override the 'can_be_discarded' method to not execute controls about the data changes.
JS
odoo.define('levelprime_product_general_status.readonly_formview', function(require) {
'use strict'
var core = require('web.core')
var FormView = require('web.FormView')
var ReadOnly_FormView = FormView.extend({
init: function() {
this._super.apply(this, arguments)
},
start: function() {
this._super.apply(this, arguments)
},
can_be_discarded: function() {
return $.Deferred().resolve()
}
})
core.view_registry.add('readonly_form', ReadOnly_FormView)
return ReadOnly_FormView
})
PY
class ViewExtension(models.Model):
_inherit = 'ir.ui.view'
type = fields.Selection(selection_add=[
('readonly_form', 'ReadOnly Form Version')])
Then you can simply use the tag in the xml.
You have used _inherits = { 'product.product': 'product_id' } in your wizard.
When using _inherits you will do a kind of polymorphic model in the database way.
For example product.product inherits product.template or res.users inherits res.partner.
This mean we create a model that gets the know how of a Model but adds aditional data/columns in a new database table. So when you create a user, all partner data is stored in res_partner table (and a partner is created) and all user related info is stored in res_users table.
In your code when wizard (levelprime_product_general_status.product_from_barcode) record will create then all product.product/product.template fields are required,due to that reason you are getting this type of error.
You can check difference between _inherit & _inherits from following link.
https://www.odoo.com/forum/how-to/developers-13/the-different-openerp-model-inheritance-mechanisms-what-s-the-difference-between-them-and-when-should-they-be-used-46
You need to follow following code.
class ProductFromBarcode(models.TransientModel):
_name = 'levelprime_product_general_status.product_from_barcode'
product_id= fields.Many2one(comodel_name='product.product',string="Product")
product_barcode = fields.Char(help='Insert the barcode to search '
'the correspondent product',
"Barcode")
#api.onchange('product_barcode')
def on_barcode_changed(self):
if self.product_barcode:
self.product_id = self.get_product_from_barcode(self.product_barcode)
#api.model
def get_product_from_barcode(self,barcode):
r = self.env['product.product'].search([('barcode', '=', barcode)])
if r:
return r
else:
return False
In above code just create wizard & add two fields.
This may help you.
from what you are saying you are using inherits to show the information of the product selected by the barcode remove it and use related field:
add the fields that you are showing on the form view to you model
name = fields.Char(related='product_id.name', readonly=True)
Now you can use name on your view it's like compute field or proxy.
you can make them readonly if you want to display them.

CasperJS/PhantomJS how to keep old page open?

I have form with some dynamic input that need to be filled. The problem is that to fill the form I need to visit another page to get data that depends on input on the previous page. So after I got a data and then backed to form, the form is already changed so I need to keep that form open while I getting a data for it. So question is how to make it?
You could probably create two casper instances. Where your first instance will have first page, and when required create another instance and get the data from it. Store data in casper's global scope.
Make waitFor until your variable value set # first instance.
Maybe with casper.back() and casper.forward() :
https://github.com/n1k0/casperjs/blob/master/tests/suites/casper/history.js
I suppose your previous form will be saved in the previous step.I hope so, try it.
Edit :
getData(newPage) was an example of your own casper method, you could try something like that :
var password ="wtoc95a";
casper.getData = function (newPage, previousPageInputData) {
//visit another page
casper.open(newPage)
//or casper.open(newPage+previousPageInputData) if the new page opened use the previous input
.then(function() {
//here you do what you want using "input" argument of the previous page
//just return the data you need to fill the form
});
return dataNeeded;
};
And in your form :
this.fill('form[id="signup_frm"]', {
nick_name: "pseudo",
e_mail: "mail",
password: "password",
password2: password,
input : casper.getData(arg1,arg2)
http://docs.casperjs.org/en/1.1-beta2/extending.html

How to use store.filter / store.find with Ember-Data to implement infinite scrolling?

This was originally posted on discuss.emberjs.com. See:
http://discuss.emberjs.com/t/what-is-the-proper-use-of-store-filter-store-find-for-infinite-scrolling/3798/2
but that site seems to get worse and worse as far as quality of content these days so I'm hoping StackOverflow can rescue me.
Intent: Build a page in ember with ember-data implementing infinite scrolling.
Background Knowledge: Based on the emberjs.com api docs on ember-data, specifically the store.filter and store.find methods ( see: http://emberjs.com/api/data/classes/DS.Store.html#method_filter ) I should be able to set the model hook of a route to the promise of a store filter operation. The response of the promise should be a filtered record array which is a an array of items from the store filtered by a filter function which is suppose to be constantly updated whenever new items are pushed into the store. By combining this with the store.find method which will push items into the store, the filteredRecordArray should automatically update with the new items thus updating the model and resulting in new items showing on the page.
For instance, assume we have a Questions Route, Controller and a model of type Question.
App.QuestionsRoute = Ember.Route.extend({
model: function (urlParams) {
return this.get('store').filter('question', function (q) {
return true;
});
}
});
Then we have a controller with some method that will call store.find, this could be triggered by some event/action whether it be detecting scroll events or the user explicitly clicking to load more, regardless this method would be called to load more questions.
Example:
App.QuestionsController = Ember.ArrayController.extend({
...
loadMore: function (offset) {
return this.get('store').find('question', { skip: currentOffset});
}
...
});
And the template to render the items:
...
{{#each question in controller}}
{{question.title}}
{{/each}}
...
Notice, that with this method we do NOT have to add a function to the store.find promise which explicitly calls this.get('model').pushObjects(questions); In fact, trying to do that once you have already returned a filter record array to the model does not work. Either we manage the content of the model manually, or we let ember-data do the work and I would very much like to let Ember-data do the work.
This is is a very clean API; however, it does not seem to work they way I've written it. Based on the documentation I cannot see anything wrong.
Using the Ember-Inspector tool from chrome I can see that the new questions from the second find call are loaded into the store under the 'question' type but the page does not refresh until I change routes and come back. It seems like the is simply a problem with observers, which made me think that this would be a bug in Ember-Data, but I didn't want to jump to conclusions like that until I asked to see if I'm using Ember-Data as intended.
If someone doesn't know exactly what is wrong but knows how to use store.push/pushMany to recreate this scenario in a jsbin that would also help too. I'm just not familiar with how to use the lower level methods on the store.
Help is much appreciated.
I just made this pattern work for myself, but in the "traditional" way, i.e. without using store.filter().
I managed the "loadMore" part in the router itself :
actions: {
loadMore: function () {
var model = this.controller.get('model'), route = this;
if (!this.get('loading')) {
this.set('loading', true);
this.store.find('question', {offset: model.get('length')}).then(function (records) {
model.addObjects(records);
route.set('loading', false);
});
}
}
}
Since you already tried the traditional way (from what I see in your post on discuss), it seems that the key part is to use addObjects() instead of pushObjects() as you did.
For the records, here is the relevant part of my view to trigger the loadMore action:
didInsertElement: function() {
var controller = this.get('controller');
$(window).on('scroll', function() {
if ($(window).scrollTop() > $(document).height() - ($(window).height()*2)) {
controller.send('loadMore');
}
});
},
willDestroyElement: function() {
$(window).off('scroll');
}
I am now looking to move the loading property to the controller so that I get a nice loader for the user.

Backbone.js Collection.models not working

I have a Rails project with Backbone.js and HAML as client side templating language.
in file app/assets/views/meeting.coffee:
class window.MeetingIndex extends Backbone.View
template: JST['meeting/index']
render: ->
#collection.fetch()
#$el.html(#template(collection: #collection))
this
in file app/assets/javascripts/templates/meeting/index.hamlc
- console.log(#collection.length) # prints 0 in console
- console.log(#collection.models) # prints [] in console
- console.log(#collection.at(0)) # prints undefined in console
- window.x = #collection
if I go to the browser console I get:
x.length # returns 2
x.models # returns [Meeting, Meeting]
x.at(0) # returns Meeting object
If I can access #collection variable in .hamlc file, because I am assigning it to window.x. Why can't I access the #collection items from the .hamlc file?
I need something like
- for model in #collection.models
%p= model.get('landlord_id')
%p= model.get('tenant_id')
%p= model.get('at')
to work
The Collection#fetch method is asynchronous (i.e. it is an AJAX call behind the curtains) so #collection.fetch() hasn't gotten anything back from the server when you try to use it in your view. However:
The options hash takes success and error callbacks which will be passed (collection, response) as arguments. When the model data returns from the server, the collection will reset.
So you can use the callbacks:
render: ->
#collection.fetch(
success: (collection, response) =>
#$el.html(#template(collection: #collection))
#
Or you can bind the view's render to the collection's reset event:
class window.MeetingIndex extends Backbone.View
template: JST['meeting/index']
initialize: ->
#collection.on('reset', #render)
#collection.fetch()
render: =>
#$el.html(#template(collection: #collection))
#
Then the fetch call in the view's initialize would indirectly trigger the proper render call when it gets something back from the server. This approach works best if your template knows what to do with an empty collection, perhaps it could detect an empty collection and display a "loading" message or just say "nothing's here yet".