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.
Related
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.
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
I'm currently building a module like a project module which shows Kanban view for model 'test.project' and when I click Kanban view, It shows tree views for model 'test.task' related to 'test.project'.
So I made a code below
class Test_project(osv.osv):
_name = "test.project"
_columns = {
'name': fields.char('Name'),
'task_ids': fields.one2many('test.task', 'project_id', string='TASK')
}
class Test_Task(osv.osv):
_name = 'test.task'
_columns = {
'project_id': fields.many2one('test.project', string='Project ID', required=1),
}
When I saved a data for 'test.project', It is saved correctly, But When I saved a data for 'test.task', It shows an error which is
IntegrityError: null value in column "project_id" violates not-null constraint
Cannot get the id.
You have set required=1 on the definition of the project_id field. That means that every time you create and save a record you will have to give a value to that fields otherwise you will not be able to save it.
I am using KeystoneJS. I have a relationship field in my Post model that references another model called Device. I am using many: true to allow users to select multiple items from the Device model.
Is there a way to have them all initially selected when creating a Post?
Right now my relationship field looks like this:
availableDevices: {
type: Types.Relationship,
ref: 'Device',
many: true
}
In your model definition, you can add pre-processing steps to populate fields that you'd like to have a default value in the relationship. I haven't done this with a many relationship, but I have done it with a relationship like this:
Report.schema.pre('save', function (next) {
this.wasNew = this.isNew;
if (!this.employee) {
this.employee = this.createdBy;
}
if (!this.timeEnteredTheHomeStr) {
if(!this.timeEnteredTheHome) {
this.timeEnteredTheHome = Date.now
}
this.timeEnteredTheHomeStr = String(this.timeEnteredTheHome);
}
next();
});
When someone creates a new report object, the employee field is automatically set to the user object referenced in the createdBy field.
I have an area named customer. it contains a controller named customer with following action
public ActionResult BrowseByCategory(int id=0)
{
//some code here............
return View();
}
I need to create a link on for above action on the _layout.cshtml view on the root.
I have written following markup. but its not working
#Html.ActionLink(c.CategoryName, "BrowseByCategory", "Customer", new { area = "customer" },new { id = c.CategoryCode })
Please suggest the change.
There is an overload of ActionLink method that can be used in your case like so:
#Html.ActionLink(c.CategoryName, "BrowseByCategory", new { area = "customer", controller = "Customer", id = c.CategoryCode }, new { #* There should be htmlParameters *# })
You can't reference a model in an action in something as global as _Layout.cshtml, or at least you shouldn't. Technically, you could define a model for _Layout.cshtml, but then every... single.. view that uses this layout could only work with that same model. If you passed a different class type, you'd get runtime errors from your layout.
In your particular situation here, it seems that the link necessary in the layout is conditional on what category is currently being viewed. The best way to handle this, then, would be using sections.
In your layout, you can define a section like:
#RenderSection("CustomLink", required: false)
Just put that wherever you want the link to appear. Making it not required means you won't get runtime errors if some other view doesn't need this custom link.
Then, in your view:
#section CustomLink
{
#Html.ActionLink(c.CategoryName, "BrowseByCategory", "Customer", new { area = "customer" },new { id = c.CategoryCode })
}
Now, this will appear where you want in your layout, but you can define it at the view level.