I'm new to Odoo and I'm stuck at an easy point.
I already added some widgets to the backend in my custom module. Now I want to add a widget to my website frontend and I don't get it work.
I have the following snippets:
frontend_views.xml
<openerp>
<data>
<!-- Templates -->
<template id="assets_frontend" name="test_module_asset_frontend" inherit_id="website.theme">
<xpath expr="." position="inside">
<!-- Custom JS and CSS -->
<link rel="stylesheet" href="/test_module/static/src/css/frontend.css" />
<script type="text/javascript" src="/test_module/static/src/js/frontend.js" />
</xpath>
</template>
</data>
</openerp>
And the JavaScript code for the widget:
static/src/js/frontend.js
openerp.test_module = function(instance, local) {
local.TestWidget = instance.Widget.extend({
start: function() {
console.log('Widget loaded!');
this._super();
},
});
instance.web.client_actions.add('example.action', 'instance.test_module.TestWidget');
}
How could I call the widget in my template?
I tried the following things:
frontend_views.xml
<record model="ir.actions.client" id="action_client_example">
<field name="name">Example Client Action</field>
<field name="tag">example.action</field>
</record>
<template id="details">
<t t-call="website.layout">
<t t-set="title">Details</t>
<div class="oe_structure">
<div class="container">
<button id="test" name="action_client_example" sequence="0" type="object">Run Widget</button>
</div>
</div>
</t>
</template>
But I don't get the Widget running. I'm a little confused, maybe I don't understand the whole thing how to integrate a widget because in the backend i just put in the following line to add the widget
<widget type="test_module.MyWidget" />
But how to do that in in frontend?
Please check the following, this can be found in the source code of odoo.
openerp.base = function(instance) {
instance.base.apps_remote = null;
instance.base.apps_client = null;
var _t = instance.web._t;
instance.base.Apps = instance.web.Widget.extend({
template: 'EmptyComponent',
remote_action_id: 'loempia.action_embed',
failback_action_id: 'base.open_module_tree',
init: function(parent, action) {
this._super(parent, action);
var options = action.params || {};
if (options.apps_user) {
sessionStorage.setItem('apps.login', options.apps_user);
}
if (options.apps_access_token) {
sessionStorage.setItem('apps.access_token', options.apps_access_token);
}
this.params = options; // NOTE read by embedded client action
},
get_client: function() {
// return the client via a deferred, resolved or rejected depending if the remote host is available or not.
var check_client_available = function(client) {
var d = $.Deferred();
var i = new Image();
i.onerror = function() {
d.reject(client);
};
i.onload = function() {
client.session.session_bind(client.origin).then(function() {
// check if client can authenticate
client.authenticate().then(
function() { /* done */
d.resolve(client);
}, function() { /* fail */
if (client.login === 'anonymous') {
d.reject(client);
} else {
sessionStorage.removeItem('apps.login');
sessionStorage.removeItem('apps.access_token');
client.bind_credentials(client.dbname, 'anonymous', 'anonymous');
client.authenticate().then(
function() { /* done */
d.resolve(client);
}, function() { /* fail */
d.reject(client);
});
}
});
});
};
var ts = new Date().getTime();
i.src = _.str.sprintf('%s/web/static/src/img/sep-a.gif?%s', client.origin, ts);
return d.promise();
};
if (instance.base.apps_client) {
return check_client_available(instance.base.apps_client);
} else {
var Mod = new instance.web.Model('ir.module.module');
return Mod.call('get_apps_server').then(function(u) {
var link = $(_.str.sprintf('', u))[0];
var host = _.str.sprintf('%s//%s', link.protocol, link.host);
var dbname = link.pathname;
if (dbname[0] === '/') {
dbname = dbname.substr(1);
}
var login = (sessionStorage ? sessionStorage.getItem('apps.login') : null) || 'anonymous';
var passwd = (sessionStorage ? sessionStorage.getItem('apps.access_token') : null) || 'anonymous';
if (_.isNull(instance.base.apps_remote)) {
instance.base.apps_remote = new openerp.init();
}
var client = new instance.base.apps_remote.web.EmbeddedClient(null, host, dbname, login, passwd);
instance.base.apps_client = client;
return check_client_available(client);
});
}
},
destroy: function() {
if (instance.base.apps_client) {
instance.base.apps_client.destroy();
}
return this._super();
},
start: function() {
var self = this;
// desactivated for now because apps does not work anyway due to changes in the framework
/*return self.get_client().
done(function(client) {
client.replace(self.$el).
done(function() {
client.$el.removeClass('openerp');
client.do_action(self.remote_action_id, {hide_breadcrumb: true});
});
}).
fail(function(client) {*/
self.do_warn(_t('Mwmy Apps will be available soon'), _t('Showing locally available modules'), true);
self.rpc('/web/action/load', {action_id: self.failback_action_id}).done(function(action) {
self.do_action(action);
instance.webclient.menu.open_action(action.id);
});
//});
},
});
instance.base.AppsUpdates = instance.base.Apps.extend({
remote_action_id: 'loempia.action_embed_updates'
});
instance.web.client_actions.add("apps", "instance.base.Apps");
instance.web.client_actions.add("apps.updates", "instance.base.AppsUpdates");
};
and in the xml
<!-- Apps modules -->
<record model="ir.actions.client" id="modules_act_cl">
<field name="name">Apps</field>
<field name="tag">apps</field>
</record>
<menuitem id="module_mi" parent="base.menu_management" sequence="10" action="modules_act_cl"/>
<record model="ir.actions.client" id="modules_updates_act_cl">
<field name="name">Updates</field>
<field name="tag">apps.updates</field>
<field name="params">{}</field>
</record>
<menuitem id="menu_module_updates" parent="base.menu_management" sequence="20" action="modules_updates_act_cl"/>
hope this help you.
If u want to learn from the staring from the scratch then you should follow the below link from the slide share
Click To Refere the Slide Share Link For Odoo Dynamic Widget
I hope this should helpful for you ..! :)
Related
I added another button to print receipt.So, one that prints the default receipt when clicked and another that prints a different template. Here is my code which print the same receipt , my question is how to modify for the second button another template similar to the default with some modifications.
Any help please? Thanks.
odoo.define('pos_print.pos_report', function (require) { "use
strict";
var screens = require('point_of_sale.screens'); console.log("screens
: " + screens)
var gui = require('point_of_sale.gui'); var core =
require('web.core'); var _t = core._t;
screens.ReceiptScreenWidget.include({
renderElement: function() {
var self = this;
this._super();
this.$('.next').click(function(){
if (!self._locked) {
self.click_next();
}
});
this.$('.back').click(function(){
if (!self._locked) {
self.click_back();
}
});
this.$('.button.order-print').click(function(){
if (!self._locked) {
self.print();
}
});
},
}); });
<templates id="point_of_sale.template" xml:space="preserve">
<t t-extend="ReceiptScreenWidget">
<t t-jquery=".button.print" t-operation="after">
<div class="button order-print">
<i class='fa fa-print'></i>
Print POS Order
</div>
</t> </t> </templates>
you need to create new print method in ReceiptScreenWidget:
renderElement: function() {
var self = this;
this._super();
this.$('.next').click(function(){
if (!self._locked) {
self.click_next();
}
});
this.$('.back').click(function(){
if (!self._locked) {
self.click_back();
}
});
this.$('.button.print').click(function(){
if (!self._locked) {
self.print();
}
});
this.$('.button.order-print').click(function(){
// To print new format
if (!self._locked) {
self.myPrint();
}
});
},
myPrint: function () {
// MyNewOrderReceipt is your new receipt template
// YOUR_PARAMS is parameters required in your template
var receipt = QWeb.render('MyNewOrderReceipt', YOUR_PARAMS);
this.pos.proxy.printer.print_receipt(receipt);
this.pos.get_order()._printed = true;
this.lock_screen(false);
},
note: this is assuming you are aiming to print the new template directly to printer without preview, if you want to change the receipt screen you need to override ActionButtonWidget.
I have a model called student. I also have form view, tree view for the student model. What I want to do is call my custom javascript file only when the form view of the student model is loaded. Is it possible? How to achieve this? Thanks.
What I tried is .....
openerp.student= function (instance) {
instance.web.FormView.include({
load_form: function(data) {
var self = this;
if (data.model === "student") {
altert('HELLO');
console.log('BLAH BLAH');
}
return this._super(data);
},
});
};
You can override the load_form method of FormView.
openerp.module_name= function (instance) {
instance.web.FormView.include({
load_form: function(data) {
var self = this;
if (data.model === "student") {
// Your custom code
}
return this._super(data);
},
});
};
To add the above code check this link inherit-or-override-js
It is possible to add a new view mode by extending FormFiew as Odoo did with account_move_line_quickadd.
openerp.your_module_name = function (instance) {
var _t = instance.web._t,
_lt = instance.web._lt;
var QWeb = instance.web.qweb;
instance.web.your_module_name = instance.web.your_module_name || {};
instance.web.views.add('student_form', 'instance.web.StudentFormView');
instance.web.StudentFormView = instance.web.FormView.extend({
load_form: function(data) {
var self = this;
// Add your custom code here
return this._super(data);
},
});
};
You just need to add the new mode to window action.
<record id="student_action" model="ir.actions.act_window">
<field name="name">student.action</field>
<field name="res_model">student</field>
<field name="view_mode">student_form,tree</field>
...
I would like to create a module which controls edit button on certain conditions. I tried the following code in js but I got no effect. So I would like to know how to extend a function in js.
formView.include({
init:function(){
var edits = new Model('sale.order');
edits.query(['validity_date']);
console.log(validity_date)
},
on_button_edit: function(){
this._super();
You can write something like this in the js file. I wrote some examples to help you.
openerp.custom_edit_button = function (instance) {
var _t = instance.web._t;
instance.web.FormView.include({
init: function() {
console.log('JS loaded')
this._super.apply(this, arguments);
},
to_edit_mode: function(){
// examples of useful methods
var field_values = this.get_fields_values();
var ids = this.get_selected_ids();
var id = field_values['id'];
var date = field_values['date'];
var model = this.model;
console.log(field_values)
console.log(ids)
console.log(id)
console.log(model)
console.log(date)
console.log(Date.today())
date_to_compare = new Date(date);
console.log(date_to_compare)
if(date_to_compare < Date.today()){
error = this.error;
var QWeb = instance.web.qweb;
var dialog = new instance.web.Dialog(this, {
title: _t("Set new expiry date"),
width: '30%',
size: 'medium',
/*dialogClass: 'oe_act_window',*/
buttons: [
{ text: _t("OK"), click: function() { self.set_new_expiry_date(); }},
{ text: _t("Close"), click: function() { dialog.close(); return; }
},
],
}, QWeb.render('custom_edit_button.expiry_date_form', {error: error})).open();
}
this._super();
}
});
}
So, if the expiry date is in the past, the form is going to appear to change it. You must define the method set_new_expiry_date as well. And on the other hand you must add this template, or something similar to show the form. Add the file in the qweb section of your __openerp__.py
<templates xml:space="preserve">
<div t-name="custom_edit_button.expiry_date_form" >
<div class="form-group">
<label for="date" class="control-label">New expiry date:</label>
<input name="date" class="form-control"/>
</div>
</div>
</templates>
Notice that the name of my module is custom_edit_button in the example
I have succesfully extended the "PosTicket" template from Point_of_Sale module and included couple of fields in that existing template. I have also written the function similar to that in screen.js (render_reciept) to get the values of the newly added fields. The problem I'm facing is, I am not able to call that function. How to execute that function?
This is the template that was extended:
<?xml version="1.0" encoding="UTF-8"?>
<template>
<t t-extend="PosTicket">
<t t-jquery=".receipt-orderlines"
t-operation="before">
customer name:<t t-esc="customer_name"/>
<br />
customer street:<t t-esc="street"/>
<br />
customer city:<t t-esc="city"/>
<br />
</t>
</t>
</template>
And this is the js file :
odoo.define('custom_module.print_cust_details_pos_bill', function (require) {
"use strict";
var core = require('web.core');
var screens = require('point_of_sale.screens');
var gui = require('point_of_sale.gui');
var QWeb = core.qweb;
var BillScreenWidget = screens.ReceiptScreenWidget.extend({
template: 'BillScreenWidget',
show: function(){
this._super();
var self = this;
this.render_receipt();
},
render_receipt: function(){
console.log("Render Reciept funtion called");
this._super();
//RKD-Start
var customer = this.pos.get_order().get_client();
var street = '';
var city ='';
var customer_name='';
if (customer != undefined)
{
customer_name = customer.name;
street = customer.street;
city=customer.city;
}
this.$('.pos-receipt-container').html(QWeb.render('PosTicket',{
widget:this,
order: order,
receipt: order.export_for_printing(),
orderlines: order.get_orderlines(),
paymentlines: order.get_paymentlines(),
customer_name:customer_name,
customer_street:street,
city:city,
}));
//RKD-End
}
});
gui.define_screen({name:'receipt', widget: custom_module.BillScreenWidget});
});
have you tried to call it into start? That's called whenever the widget is injected in the DOM and looks like the place to do that.
Check "using a widget" in official docs.
I am building a JS framework to simulate AngularJS models, only for educational purposes.
The thing is: I assign a callback to run when the models(inputs) are updated, but after they run by the first time, they "disappear".
Before trying to do this using jQuery, I was trying with querySelectorAll and got stuck on the same problem.
Here it is: http://jsfiddle.net/YmY2w/
HTML
<div class="container" jd-app="test">
<input type="text" jd-model="name" value="foo" /><br />
<input type="text" jd-model="email" value="foo#foo" />
<hr />
<p>Name: {{ name }}</p>
<p>Email: {{ email }}</p>
</div>
JAVASCRIPT
(function($, window, document, undefined) {
'use strict';
// registering models
(function() {
var $app = $('[jd-app]');
var $models = $('[jd-model]', $app);
$models.each(function(index) {
UpdateModel.apply(this);
});
function UpdateModel() {
var model = { name: $(this).attr('jd-model'), value: this.value }
var re = new RegExp('{{\\s+('+ model.name +')\\s+}}', 'gm');
$('*', $app).each(function(index) {
var $this = $(this);
$this.text( $this.text().replace(re, model.value) );
});
$(this).on('keyup', UpdateModel);
}
})();
})(jQuery, window, document);
What am I doing wrong? Is there a better way to accomplish this?
The problem you're having is that when your script first sees {{ name }} it converts it to the value of the model "name". But then in your html, you have the text "foo" instead of {{ name }} (the thing you're trying to search/replace), so you aren't able to update the displayed text.
What you have to do is keep track of the individual DOM nodes (as attached to particular models) which contain the text that initially has the "{{ }}" in them with the proper model name.
I came up with a kind of ugly prototype here:
http://jsfiddle.net/YmY2w/11/
I'm not sure how flexible it is in terms of identifying the proper templated locations, but it serves as a demo for the kind of implementation I'm talking about. Also - not very efficient I would guess. (But then neither is angularjs really...)
(function($, window, document, undefined) {
'use strict';
// From http://stackoverflow.com/questions/298750/how-do-i-select-text-nodes-with-jquery
function getTextNodesIn(node, includeWhitespaceNodes) {
var textNodes = [], nonWhitespaceMatcher = /\S/;
function getTextNodes(node) {
if (node.nodeType == 3) {
if (includeWhitespaceNodes || nonWhitespaceMatcher.test(node.nodeValue)) {
textNodes.push(node);
}
} else {
for (var i = 0, len = node.childNodes.length; i < len; ++i) {
getTextNodes(node.childNodes[i]);
}
}
}
getTextNodes(node);
return textNodes;
}
// registering models
(function() {
var $app = $('[jd-app]');
var $models = $('[jd-model]', $app);
var models = [];
$models.each(function(index) {
registerModel.apply(this);
});
function registerModel() {
var model = { name: $(this).attr('jd-model'), value: this.value, domElements: [] };
var re = new RegExp('{{\\s+('+ model.name +')\\s+}}', 'gm');
$app.contents().each(function(index) {
if ($(this).text().match(re)) {
var textNodes = getTextNodesIn(this, false);
console.log(textNodes);
$.each(textNodes, function(index, elem) {
if (elem.nodeValue.match(re)) {
var text = elem.nodeValue;
var myArray = re.exec(text);
var match = myArray[0];
var firstIndex = text.indexOf(match);
var newElem = elem.splitText(firstIndex);
newElem.splitText(match.length);
model.domElements.push(newElem);
}
});
}
});
models.push(model);
$(this).on('keyup', updateModel);
$(this).trigger('keyup');
}
function updateModel() {
var $input = $(this);
$.each(models, function(index, model) {
if (model.name === $input.attr('jd-model')) {
var newVal = $input.val();
$.each(model.domElements, function(index, elem) {
elem.nodeValue = newVal;
});
}
});
}
})();
})(jQuery, window, document);