Dashboards are a way of centralizing business data and providing a quick overview of the important information. This is used to plot the performance of the organization as a whole. Different visualization methods are utilized for this, including charts, diagrams, and other forms or representations.
In Odoo, dashboards play a big part in building a visually appealing platform for efficient data management. It assists with numerous matrices for the business intelligence and interactively connects all areas. Let's now examine how to establish a dashboard in Odoo16.
The dashboard is rendered via client actions, which are typically triggered by menu items. Client actions, as their name suggests, are a form of action that are almost entirely implemented in unique client code. Basically, this is started by menu items that are defined in XML, with associated actions being mapped to a widget.
The client action that renders the dashboard is defined here;
<!--Client action for dashboard?
<record id="action_dashboard_purchase" model="ir.actions.client">
<field name="name">Purchase</field>
<field name="tag">purchase_dashboard</field>
</record>
<menuitem id="menu_purchase_dashboard" name="Dashboard" action="action_dashboard_purchase" parent="purchase.menu_purchase_root" sequence="-1"/>
Here the menu item will trigger the client action ‘action_dashboard_purchase’ and then the “tag” ‘purchase_dashboard’ will connect the js file to the action. The fields of client actions are;
id: id for the client action
name: name to be shown when the widget is triggered
tag: tag that connect the client action to the js file
After declaring the client action next we need to create a js file to be triggered for the client action.
By adding the js file we are actually trying to connect the xml file using the tag mentioned in the client action.
For that, define a js file in the path static/src/js/file_name.js.
odoo.define("dashboard_dashboard.DashboardDashboard", function (require) {
"use strict";
var AbstractAction = require('web.AbstractAction');
var core = require('web.core');
var DashBoard = AbstractAction.extend({
contentTemplate: 'DashboardDashboard',
});
core.action_registry.add('dashboard_dashboard_tag', DashBoard);
return DashBoard;
});
The part of the screen that lies below the menu bar is controlled by client actions that is usually a widget. The client actions are declared as;
- introducing a new widget:
As you see in the code here we load the dependency abstraction action; and then creating a new widget
var AbstractAction = require('web.AbstractAction');
var ClientAction = AbstractAction.extend({
ContentTemplate: ‘DashboardDashboard’,
...
});
- registering it in the action registry
var core = require('web.core');
core.action_registry.add(dashboard_dashboard_tag, Dashboard);
After defining this client action, next needs to create a template in the path static/src/xml/file_name.xml.
<t t-name="DashboardDashboard">
<div class="oh_dashboards">
<div class="container-fluid my-5 o_hr_dashboard"/>
</div>
</t>
After defining all these files don’t forget to add them in the manifest;
'assets': {
'web.assets_backend': [
'dashboard_dashboard/static/src/js/dashboard.js',
'dashboard_dashboard/static/src/xml/dashboard.xml',
],
},
After declaring all of these a blank screen will be displayed in the view.
Next let’s see how to add tiles to this dashboard.
In order to define the tiles first we need to declare the code inside an XML file with your own design for the tiles.
<t t-name="MainSection">
<section class="dashboard_main" id="main_section">
<div class="col-sm-12 mb-4">
<div class="row">
<div class="col-12 col-sm-12 col-md-8">
<h2 class="section-header">Dashboard</h2>
</div>
<!-- Tiles -->
<div class="col-sm-12 mb-3">
<div class="row">
<div class="col-md-3 col-sm-5 all_products oh-data">
<div class="oh-card" style="width: 288px;">
<div class="oh-card-body">
<div class="stat-widget-one">
<div class="stat-icon" style="background:#ff4d94">
<i class="fa fa-star"/>
</div>
<div class="stat-content">
<div class="stat-head">Products</div>
<div class="stat_count">
<span id="templates">
<div id="product_templates"/>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3 col-sm-5 product_variants oh-data">
<div class="oh-card" style="width: 288px;">
<div class="oh-card-body">
<div class="stat-widget-one">
<div class="stat-icon" style="background:#99e6ff">
<i class="fa fa-star"/>
</div>
<div class="stat-content">
<div class="stat-head">Product Variants</div>
<div class="stat_count">
<span id="count">
<div id="variants_count"/>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3 col-sm-5 storable_products oh-data">
<div class="oh-card" style="width: 288px;">
<div class="oh-card-body">
<div class="stat-widget-one">
<div class="stat-icon" style="background:#aaff80">
<i class="fa fa-star"/>
</div>
<div class="stat-content">
<div class="stat-head">Storable Products</div>
<div class="stat_count">
<span id="storable">
<div id="products_storable"/>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3 col-sm-5 consu_products oh-data">
<div class="oh-card" style="width: 288px;">
<div class="oh-card-body">
<div class="stat-widget-one">
<div class="stat-icon" style="background:#bf80ff">
<i class="fa fa-star"/>
</div>
<div class="stat-content">
<div class="stat-head">Consumable Products</div>
<div class="stat_count">
<span id="consumable">
<div id="product_consumable"/>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</t>
In this template we show the tiles that include the information about products.
After declaring the xml code we need to render this template as well
odoo.define("dashboard_dashboard.DashboardDashboard", function (require) {
"use strict";
var AbstractAction = require('web.AbstractAction');
var core = require('web.core');
var QWeb = core.qweb;
var web_client = require('web.web_client');
var session = require('web.session');
var ajax = require('web.ajax');
var _t = core._t;
var rpc = require('web.rpc');
var self = this;
var DashBoard = AbstractAction.extend({
contentTemplate: 'DashboardDashboard',
init: function(parent, context) {
this._super(parent, context);
this.dashboard_templates = ['MainSection'];
},
start: function() {
var self = this;
this.set("title", 'Dashboard');
return this._super().then(function() {
self.render_dashboards();
});
},
willStart: function(){
var self = this;
return this._super()
},
render_dashboards: function() {
var self = this;
this.fetch_data()
var templates = []
var templates = ['MainSection'];
_.each(templates, function(template) {
self.$('.o_hr_dashboard').append(QWeb.render(template, {widget: self}))
});
},
fetch_data: function() {
var self = this
// fetch data to the tiles
var def1 = this._rpc({
model: 'product.template',
method: "get_data",
})
.then(function (result) {
$('#product_templates').append('<span>' + result.product_templates + '</span>');
$('#variants_count').append('<span>' + result.product_variants + '</span>');
$('#products_storable').append('<span>' + result.storable + '</span>');
$('#product_consumable').append('<span>' + result.consumable + '</span>');
});
},
});
core.action_registry.add('dashboard_dashboard_tag', DashBoard);
return DashBoard;
});
Here the data is fetched from the backend using rpc query. As you can see this the function get_data() is used to get the data from the model ‘product.template’
It’s python code will be like this;
class ProductTemplate(models.Model):
_inherit = 'product.template'
@api.model
def get_data(self):
"""Returns data to the tiles of dashboard"""
product_templates = self.env['product.template'].search([])
product_variants = self.env['product.product'].search([])
storable = self.env['product.template'].search([('detailed_type', '=', 'product')])
consumable = self.env['product.template'].search([('detailed_type', '=', 'consu')])
return {
'product_templates': len(product_templates),
'product_variants': len(product_variants),
'storable': len(storable),
'consumable': len(consumable),
}
Here the function is declared along with the decorator @api.model this helps to load when it’s triggered. Once you have completed this then the view will be showing the tiles that we have created.
Here the function is declared along with the decorator @api.model this helps to load when it’s triggered. Once you have completed this then the view will be showing the tiles that we have created.
Here you can use customized design and styles as you wish. This is a basic dashboard but here we can create different graphical representations and other enhancements according to the needs.
This way one can create the dashboard easily.