Commit d17c20bf by Arturo Jasso Origel

cambios en el apartado de graficas y se agrega modulo de graficas dinamicas

parent 519e2f06
......@@ -37,7 +37,6 @@
'views/odoo/helpdesk_warehouse.xml',
'views/odoo/helpdesk_module.xml',
'views/odoo/helpdesk_reason.xml',
'views/odoo/helpdesk_ticket_graph.xml',
'views/odoo/res_users.xml',
],
'assets': {
......
......@@ -35,7 +35,7 @@ class HelpdeskTicket(models.Model):
is_support = fields.Boolean('Es soporte',compute='_compute_ticket_type')
is_erp = fields.Boolean('Es ERP',compute='_compute_ticket_type')
is_emergency = fields.Boolean('Es 911',compute='_compute_ticket_type')
user_branch_id = fields.Many2one('warehouse.helpdesk',related="partner_id.user_ids.warehouse_id")
user_branch_id = fields.Many2one('warehouse.helpdesk',related="partner_id.user_ids.warehouse_id",store=True)
......@@ -336,7 +336,7 @@ class HelpdeskTicket(models.Model):
if "stage_id" in vals:
if self.description == '<p><br></p>' or self.description==False:
raise ValidationError (_("Para cambiar de etapa, es necesario agregar una descripción"))
if "description" in vals:
if "description" in vals and vals['description'] != '<p><br></p>':
min_description = int(self.env['ir.config_parameter'].sudo().get_param('helpdesk_morsa.description_min_lenght'))
if len(vals['description'])-11 < min_description:
raise ValidationError (_("Favor de agregar al menos "+str(min_description)+" caracteres a la descripción"))
......
......@@ -10,4 +10,4 @@ class ResUsers(models.Model):
comodel_name='warehouse.helpdesk',
relation='warehouse_helpdesk_res_users_rel'
)
name = fields.Char('Name',store=True)
......@@ -14,12 +14,6 @@
background-color: #386aeb;
}
.fa-ticket{
content:url('/helpdesk_morsa/static/src/img/ticket-create.png') !important;
height: 37px;
margin-right: 5px;
}
.bg-light{
box-shadow: 5px 5px 10px rgba(0,0,0,0.3);
}
......@@ -28,11 +22,16 @@
border-radius: 30px !important;
background-color: #e4e4e5 !important;
color: #212529;
margin-bottom: 16px;
}
.nav-button{
color:#6C757D !important;
font-size: 14px !important;
border-radius: 30px !important;
padding: 0.5rem 1rem;
}
.nav-mr{
margin-right:10px
}
.card-body-custom{
......@@ -43,9 +42,6 @@
box-shadow: 5px 5px 10px rgba(0,0,0,0.3);
}
.o_center_object{
display: flex;
align-items: center;
......
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="helpdesk_ticket_graph_tipes" model="ir.ui.view">
<field name="name">helpdesk_ticket.graph.tipes</field>
<field name="model">helpdesk.ticket</field>
<field name="arch" type="xml">
<graph string="Ticket">
<field name="problem_solved_on_time" type="row"/>
<field name="ticket_type" type="measure"/>
</graph>
</field>
</record>
<record id="helpdesk_ticket_graph_act_window" model="ir.actions.act_window">
<field name="name">Tickets</field>
<field name="res_model">helpdesk.ticket</field>
<field name="view_mode">graph</field>
<field name="domain">[('closed_stage_boolean','=',True)]</field>
<field name="view_id" ref="helpdesk_ticket_graph_tipes"/>
</record>
<menuitem name="Resolvidos en tiempo" sequence="2" id="helpdesk_graph" parent="sh_all_in_one_helpdesk.helpdesk_reporting_menu" action="helpdesk_ticket_graph_act_window"/>
</data>
</odoo>
\ No newline at end of file
......@@ -68,12 +68,12 @@
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<li class="nav-item nav-mr active">
<t t-if="breadcrumbs_searchbar" t-call="portal.portal_breadcrumbs"/>
<span t-else="" class="navbar-brand mb-0 h1 mr-auto" t-esc="title or 'No title'"/>
</li>
<li class="nav-item dropdown">
<li class="nav-item nav-mr dropdown">
<a class="nav-link dropdown-toggle" href="#" id="sort_by" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Ordenar por:
</a>
......@@ -87,7 +87,7 @@
</div>
</li>
<li class="nav-item dropdown">
<li class="nav-item nav-mr dropdown">
<a class="nav-link dropdown-toggle" href="#" id="filter_by" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Filtrar por:
</a>
......@@ -101,7 +101,7 @@
</div>
</li>
<li class="nav-item dropdown">
<li class="nav-item nav-mr dropdown">
<a class="nav-link dropdown-toggle" href="#" id="group_by" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Agrupar por:
</a>
......@@ -116,14 +116,13 @@
</li>
<li class="nav-item">
<a id="new_request"
class="btn border btn-sm nav-button"
class="btn btn-primary btn-sm nav-button"
name="new_request"
title="Create Support Request"
aria-label="Create Support Request"
role="button">
<span class="o_center_object">
<i class="fa fa-ticket "/>
Crear Ticket</span>
<i class="fa fa-plus "/>
Crear Ticket
</a>
</li>
......@@ -132,13 +131,13 @@
<div class="input-group input-group-sm w-100">
<span class="input-group-append">
<button class="btn btn-primary o_wait_lazy_js" type="submit">
<button class="btn btn-primary o_wait_lazy_js rounded-left" type="submit">
<span class="fa fa-search"/>
</button>
</span>
<input type="text" class="form-control form-control-sm" placeholder="Search" t-att-value='search' name="search"/>
<div class="input-group-prepend">
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown"/>
<button type="button" class="btn btn-primary dropdown-toggle rounded-right" data-toggle="dropdown"/>
<div class="dropdown-menu" role="menu">
<t t-foreach='searchbar_inputs' t-as='input'>
<a t-att-href="'#' + input_value['input']"
......
Odoo Dynamic Dashboard
======================
Dynamically Arrange the dashboard to get the information that are relevant to your business, department, or a specific process or need.
Installation
============
- www.odoo.com/documentation/15.0/setup/install.html
- Install our custom addon
License
=======
GNU AFFERO GENERAL PUBLIC LICENSE, Version 3 (AGPLv3)
(http://www.gnu.org/licenses/agpl.html)
Bug Tracker
===========
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported.
Credits
=======
* Cybrosys Techno Solutions <https://www.cybrosys.com>
Author
------
Developer: Irfan @ Cybrosys, Afras @Cybrosys
Maintainer
----------
This module is maintained by Cybrosys Technologies.
For support and more information, please visit https://www.cybrosys.com.
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2021-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
from . import models
from . import controllers
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2021-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
{
'name': "Odoo Dynamic Dashboard",
'version': '15.0.1.0.2',
'summary': """Create Configurable Dashboards Easily""",
'description': """Create Configurable Dashboard Dynamically to get the information that are relevant to your business, department, or a specific process or need, Dynamic Dashboard, Dashboard, Dashboard Odoo""",
'author': 'Cybrosys Techno Solutions',
'company': 'Cybrosys Techno Solutions',
'maintainer': 'Cybrosys Techno Solutions',
'depends': ['base', 'web'],
'data': [
'views/dashboard_view.xml',
'views/dynamic_block_view.xml',
'views/dashboard_menu_view.xml',
'security/ir.model.access.csv',
],
'assets': {
'web.assets_backend': [
'odoo_dynamic_dashboard/static/src/js/dynamic_dashboard.js',
'odoo_dynamic_dashboard/static/src/scss/style.scss',
'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.bundle.js',
'https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700',
],
'web.assets_qweb': [
'odoo_dynamic_dashboard/static/src/xml/dynamic_dashboard_template.xml',
],
},
'images': ['static/description/banner.png'],
'license': "AGPL-3",
'installable': True,
'application': True,
}
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2021-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
from odoo import http, _
from odoo.http import request
from odoo.exceptions import UserError
class DynamicDashboard(http.Controller):
@http.route('/create/tile', type='json', auth='user')
def tile_creation(self, **kw):
"""While clicking ADD Block"""
type = kw.get('type')
action_id = kw.get('action_id')
tile_id = request.env['dashboard.block'].sudo().create({
'name': 'New Block',
'type': type,
'tile_color': '#1f6abb',
'text_color': '#FFFFFF',
'fa_icon': 'fa fa-money',
'edit_mode': True,
'client_action': int(action_id),
})
return {'id': tile_id.id, 'name': tile_id.name, 'type': type, 'icon': 'fa fa-money',
'color': 'background-color: #1f6abb;',
'text_color': 'color: #FFFFFF',
'icon_color': 'color: #1f6abb'}
@http.route('/tile/details', type='json', auth='user')
def tile_details(self, **kw):
tile_id = request.env['dashboard.block'].sudo().search([('id', '=', kw.get('id'))])
if tile_id:
# model_name = dict(request.env['dashboard.block'].fields_get(allfields=['model_id'])['model_id']['selection'])[tile_id.model_id]
return {'model': tile_id.model_id.model, 'filter': tile_id.filter, 'model_name': tile_id.model_id.name}
return False
## Module <odoo_dynamic_dashboard>
#### 01.10.2022
#### Version 15.0.1.0.0
##### Initial Commit for odoo_dynamic_dashboard
#### 28.04.2022
#### Version 15.0.1.0.1
##### Style Updated
#### 06.10.2022
#### Version 15.0.1.0.2
##### Code modified
from . import dashboard_block
from . import domain_to_sql
from . import dashboard_menu
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2021-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
from odoo import models, fields, api
from odoo.osv import expression
from ast import literal_eval
class DashboardBlock(models.Model):
_name = "dashboard.block"
_description = "Dashboard Blocks"
_rec_name = "name"
def get_default_action(self):
action_id = self.env.ref('odoo_dynamic_dashboard.action_dynamic_dashboard')
if action_id:
return action_id.id
else:
return False
name = fields.Char(string="Name", help='Name of the block')
field_id = fields.Many2one('ir.model.fields', 'Measured Field',domain="[('store', '=', True), ('model_id', '=', model_id), ('ttype', 'in', ['float','integer','monetary'])]")
fa_icon = fields.Char(string="Icon")
graph_size = fields.Selection(
selection=[("col-lg-4", "Small"), ("col-lg-6", "Medium"), ("col-lg-12", "Large")],
string="Graph Size",default='col-lg-4')
operation = fields.Selection(
selection=[("sum", "Sum"), ("avg", "Average"), ("count", "Count")],
string="Operation", help='Tile Operation that needs to bring values for tile')
graph_type = fields.Selection(
selection=[("bar", "Bar"), ("radar", "Radar"), ("pie", "Pie"), ("line", "Line"), ("doughnut", "Doughnut")],
string="Chart Type", help='Type of Chart')
measured_field = fields.Many2one("ir.model.fields", "Measured Field")
client_action = fields.Many2one('ir.actions.client', default = get_default_action)
type = fields.Selection(
selection=[("graph", "Chart"), ("tile", "Tile")], string="Type", help='Type of Block ie, Chart or Tile')
x_axis = fields.Char(string="X-Axis")
y_axis = fields.Char(string="Y-Axis")
group_by = fields.Many2one("ir.model.fields", store=True, string="Group by(Y-Axis)", help='Field value for Y-Axis')
tile_color = fields.Char(string="Tile Color", help='Primary Color of Tile')
text_color = fields.Char(string="Text Color", help='Text Color of Tile')
fa_color = fields.Char(string="Icon Color", help='Icon Color of Tile')
filter = fields.Char(string="Filter")
model_id = fields.Many2one('ir.model', 'Model')
model_name = fields.Char(related='model_id.model', readonly=True)
filter_by = fields.Many2one("ir.model.fields", string=" Filter By")
filter_values = fields.Char(string="Filter Values")
sequence = fields.Integer(string="Sequence")
edit_mode = fields.Boolean(default=False, invisible=True)
def get_dashboard_vals(self, action_id):
"""Dashboard block values"""
block_id = []
dashboard_block = self.env['dashboard.block'].sudo().search([('client_action', '=', int(action_id))])
for rec in dashboard_block:
color = rec.tile_color if rec.tile_color else '#1f6abb;'
icon_color = rec.tile_color if rec.tile_color else '#1f6abb;'
text_color = rec.text_color if rec.text_color else '#FFFFFF;'
vals = {
'id': rec.id,
'name': rec.name,
'type': rec.type,
'graph_type': rec.graph_type,
'icon': rec.fa_icon,
'cols': rec.graph_size,
'color': 'background-color: %s;' % color,
'text_color': 'color: %s;' % text_color,
'icon_color': 'color: %s;' % icon_color,
}
domain = []
if rec.filter:
domain = expression.AND([literal_eval(rec.filter)])
if rec.model_name:
if rec.type == 'graph':
query = self.env[rec.model_name].get_query(domain, rec.operation, rec.measured_field,
group_by=rec.group_by)
self._cr.execute(query)
records = self._cr.dictfetchall()
x_axis = []
for record in records:
x_axis.append(record.get(rec.group_by.name))
y_axis = []
for record in records:
y_axis.append(record.get('value'))
vals.update({'x_axis': x_axis, 'y_axis': y_axis})
else:
query = self.env[rec.model_name].get_query(domain, rec.operation, rec.measured_field)
self._cr.execute(query)
records = self._cr.dictfetchall()
magnitude = 0
total = records[0].get('value')
while abs(total) >= 1000:
magnitude += 1
total /= 1000.0
# add more suffixes if you need them
val = '%.2f%s' % (total, ['', 'K', 'M', 'G', 'T', 'P'][magnitude])
# if rec.measured_field.ttype == 'monetary':
# amount = str(
# value) + currency_id.symbol if currency_id.position == 'after' else currency_id.symbol + str(
# value)
records[0]['value'] = val
vals.update(records[0])
block_id.append(vals)
return block_id
class DashboardBlockLine(models.Model):
_name = "dashboard.block.line"
sequence = fields.Integer(string="Sequence")
block_size = fields.Integer(string="Block size")
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2021-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
from odoo import models, fields, api
from odoo.osv import expression
class DashboardMenu(models.Model):
_name = "dashboard.menu"
_description = "Dashboard Menu"
_rec_name = "name"
name = fields.Char(string="Name")
menu_id = fields.Many2one('ir.ui.menu', string="Menu")
group_ids = fields.Many2many('res.groups', string='Groups',
related='menu_id.groups_id',
help="User need to be at least in one of these groups to see the menu")
client_action = fields.Many2one('ir.actions.client')
@api.model
def create(self, vals):
"""This code is to create menu"""
values = {
'name': vals['name'],
'tag': 'dynamic_dashboard',
}
action_id = self.env['ir.actions.client'].create(values)
vals['client_action'] = action_id.id
menu_id = self.env['ir.ui.menu'].create({
'name': vals['name'],
'parent_id': vals['menu_id'],
'action': 'ir.actions.client,%d' % (action_id.id,)
})
return super(DashboardMenu, self).create(vals)
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2021-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
from odoo.osv import expression
from odoo import models
from odoo.release import version_info
def get_query(self, args, operation, field, group_by=False, apply_ir_rules=False):
"""Dashboard block Query Creation"""
query = self._where_calc(args)
if apply_ir_rules:
self._apply_ir_rules(query, 'read')
if operation and field:
data = 'COALESCE(%s("%s".%s),0) AS value' % (operation.upper(), self._table, field.name)
join = ''
group_by_str = ''
if group_by:
if group_by.ttype == 'many2one':
relation_model = group_by.relation.replace('.', '_')
join = ' INNER JOIN %s on "%s".id = "%s".%s' % (
relation_model, relation_model, self._table, group_by.name)
rec_name = self.env[group_by.relation]._rec_name_fallback()
data = data + ',"%s".%s AS %s' % (relation_model, rec_name, group_by.name)
group_by_str = ' Group by "%s".%s' % (relation_model, rec_name)
else:
data = data + ',"%s".%s' % (self._table, group_by.name)
group_by_str = ' Group by "%s".%s' % (self._table, str(group_by.name))
else:
data = '"%s".id' % (self._table)
from_clause, where_clause, where_clause_params = query.get_sql()
where_str = where_clause and (" WHERE %s" % where_clause) or ''
if 'company_id' in self._fields:
if len(self.env.companies.ids) > 1:
operator = 'in'
company = str(tuple(self.env.companies.ids))
else:
operator = '='
company = self.env.companies.ids[0]
if where_str == '':
add = ' where'
else:
add = ' and'
multicompany_condition = '%s "%s".company_id %s %s' % (add, self._table, operator, company)
else:
multicompany_condition = ''
query_str = 'SELECT %s FROM ' % data + from_clause + join + where_str + multicompany_condition + group_by_str
where_clause_params = map(lambda x: "'" + str(x) + "'", where_clause_params)
return query_str % tuple(where_clause_params)
models.BaseModel.get_query = get_query
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_dashboard_block,access.dashboard.block,model_dashboard_block,base.group_user,1,1,1,1
access_dashboard_menu,access.dashboard.menu,model_dashboard_menu,base.group_user,1,1,1,1
access_dashboard_block_line,access.dashboard.block.line,model_dashboard_block_line,base.group_user,1,1,1,1
\ No newline at end of file
odoo.define('odoo_dynamic_dashboard.Dashboard', function (require) {
"use strict";
var AbstractAction = require('web.AbstractAction');
var ajax = require('web.ajax');
var core = require('web.core');
var rpc = require('web.rpc');
var session = require('web.session');
var web_client = require('web.web_client');
var _t = core._t;
var QWeb = core.qweb;
var DynamicDashboard = AbstractAction.extend({
template: 'dynamic_dashboard',
events: {
'click .add_block': '_onClick_add_block',
'click .add_grapgh': '_onClick_add_grapgh',
'click .block_setting': '_onClick_block_setting',
'click .tile': '_onClick_tile',
},
init: function(parent, context) {
this.action_id = context['id'];
this._super(parent, context);
this.block_ids = []
},
start: function() {
var self = this;
this.set("title", 'Dashboard');
return this._super().then(function() {
self.render_dashboards();
});
},
willStart: function() {
var self = this;
return $.when(ajax.loadLibs(this), this._super()).then(function() {
return self.fetch_data();
});
},
fetch_data: function() {
var self = this;
var def1 = this._rpc({
model: 'dashboard.block',
method: 'get_dashboard_vals',
args: [[],this.action_id]
}).then(function(result) {
self.block_ids = result;
});
return $.when(def1);
},
get_colors : function(x_axis) {
var color = []
for (var j = 0; j < x_axis.length; j++) {
var r = Math.floor(Math.random() * 255);
var g = Math.floor(Math.random() * 255);
var b = Math.floor(Math.random() * 255);
color.push("rgb(" + r + "," + g + "," + b + ")");
}
return color
},
get_values_bar : function(block){
var labels = block['x_axis']
var data = {
labels: labels,
datasets: [{
label: "",
data: block['y_axis'],
backgroundColor: this.get_colors(block['x_axis']),
borderColor: 'rgba(200, 200, 200, 0.75)',
borderWidth: 1
}]
};
var options = {
scales: {
y: {
beginAtZero: true
}
}
},
bar_data = [data,options]
return bar_data;
},
get_values_pie : function(block){
var data = {
labels: block['x_axis'],
datasets: [{
label: '',
data: block['y_axis'],
backgroundColor: this.get_colors(block['x_axis']),
hoverOffset: 4
}]
};
var options = { },
pie_data = [data,options]
return pie_data;
},
get_values_line : function(block){
var labels = block['x_axis']
var data = {
labels: labels,
datasets: [{
label: '',
data: block['y_axis'],
fill: false,
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}]
};
var options = { },
line_data = [data,options]
return line_data;
},
get_values_doughnut : function(block){
var data = {
labels: block['x_axis'],
datasets: [{
label: '',
data: block['y_axis'],
backgroundColor: this.get_colors(block['x_axis']),
hoverOffset: 4
}]
};
var options = { },
doughnut_data = [data,options]
return doughnut_data;
},
get_values_radar : function(block){
var data = {
labels: block['x_axis'],
datasets: [{
label: '',
data: block['y_axis'],
fill: true,
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgb(255, 99, 132)',
pointBackgroundColor: 'rgb(255, 99, 132)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgb(255, 99, 132)'
}]
};
var options = {
elements: {
line: {
borderWidth: 3
}
}
},
radar_data = [data,options]
return radar_data;
},
render_dashboards: function() {
var self = this;
_.each(this.block_ids, function(block) {
if (block['type'] == 'tile') {
console.log('INSIDE IF');
self.$('.o_dynamic_dashboard').append(QWeb.render('DynamicDashboardTile', {widget: block}));
}
else{
console.log('INSIDE ELSE');
self.$('.o_dynamic_chart').append(QWeb.render('DynamicDashboardChart', {widget: block}));
var element = $('[data-id=' + block['id'] + ']')
console.log('INSIDE ELSE 2');
if (!('x_axis' in block)){
return false
}
var ctx =self.$('.chart_graphs').last()
var type = block['graph_type']
var chart_type = 'self.get_values_' + `${type}(block)`
var data = eval(chart_type)
console.log('chart_type: ', chart_type);
//create Chart class object
var chart = new Chart(ctx, {
type: type,
data: data[0],
options: data[1]
});
}
});
},
_onClick_block_setting : function(event){
event.stopPropagation();
var self = this;
var id = $(event.currentTarget).closest('.block').attr('data-id');
this.do_action({
type: 'ir.actions.act_window',
res_model: 'dashboard.block',
view_mode: 'form',
res_id: parseInt(id),
views: [[false,'form']],
context: {'form_view_initial_mode': 'edit'},
});
},
_onClick_add_block : function(e){
var self = this;
var type = $(e.currentTarget).attr('data-type');
ajax.jsonRpc('/create/tile', 'call', {
'type' : type,
'action_id' : self.action_id
}).then(function (result) {
if(result['type'] == 'tile'){
self.$('.o_dynamic_dashboard').append(QWeb.render('DynamicDashboardTile', {widget: result}));
}
else{
self.$('.o_dynamic_chart').append(QWeb.render('DynamicDashboardChart', {widget: result}));
var element = $('[data-id=' + result['id'] + ']')
var ctx =self.$('.chart_graphs').last()
var options = {
type: 'bar',
data: {
labels: [],
datasets: [
{
data: [],
borderWidth: 1
},
]
},
}
var chart = new Chart(ctx, {
type: "bar",
data: options
});
}
});
},
_onClick_tile : function(e){
e.stopPropagation();
var self = this;
var id = $(e.currentTarget).attr('data-id');
ajax.jsonRpc('/tile/details', 'call', {
'id': id
}).then(function (result) {
self.do_action({
name : result['model_name'],
type: 'ir.actions.act_window',
res_model:result['model'] ,
view_mode: 'tree,form',
views: [[false, 'list'], [false, 'form']],
domain: result['filter']
});
});
},
});
core.action_registry.add('dynamic_dashboard', DynamicDashboard);
return DynamicDashboard;
});
\ No newline at end of file
.card{
border: none;
border-radius: 0px;
box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
-webkit-box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
-moz-box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
}
.card-header{
background-color: transparent;
border: none;
}
:root {
/* Colors */
--green: #00C689;
--blue: #3DA5F4;
--red: #F1536E;
--yellow: #FDA006;
/*Fonts*/
--primary-font: 'Roboto', sans-serif;
}
html .o_web_client > .o_action_manager {
overflow:auto;
}
.bg-green {
background-color: var(--green);
}
.bg-blue {
background-color: var(--blue);
}
.bg-red {
background-color: var(--red);
}
.bg-yellow {
background-color: var(--yellow);
}
.text-color-yellow {
color: var(--yellow);
}
.text-color-green {
color: var(--green);
}
.text-color-blue {
color: var(--blue);
}
.text-color-red {
color: var(--red);
}
.text-color-yellow {
color: var(--yellow);
}
.tile-container {
padding: 3.2rem 1.5rem;
border-radius: 2rem;
}
.tile-container__icon-container {
border-radius: 50%;
width: 4.75rem;
height: 4.75rem;
font-size: 28px;
}
.status-container__title {
font-family: var(--primary-font);
font-weight: 500;
font-size: 1.5rem;
line-height: 1.5rem;
}
.status-container__figures {
font-family: var(--primary-font);
}
.status-container__figures>h3 {
font-weight: 700;
font-size: 1.5rem;
line-height: 1.813rem;
}
.tile-container__setting-icon {
top: 0.638rem;
right: 1rem;
}
// Main Navbar Dropdown menu location override
.o_menu_systray > .o_user_menu > .o-dropdown--menu{
left: auto !important;
right: 0px !important;
}
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-name="dynamic_dashboard">
<div class="container">
<button class="btn btn-primary add_block" data-type="tile" type="button">Add Block
</button>
<button class="btn btn-primary add_block" data-type="graph" type="button">Add Graph
</button>
<div class=" o_dynamic_dashboard row">
</div>
<div style="padding-top: 100px" class="o_dynamic_chart row">
</div>
</div>
</t>
<t t-name="DynamicDashboardTile">
<div class="col-sm-12 col-md-12 col-lg-3 tile block" t-att-data-id="widget.id">
<div t-att-style="widget.color+widget.text_color"
class="tile-container d-flex justify-content-around align-items-center position-relative w-100 h-auto my-3">
<a t-att-style="widget.text_color"
class="block_setting position-absolute tile-container__setting-icon"><i
class="fa fa-cog"></i></a>
<div t-att-style="widget.icon_color"
class="tile-container__icon-container bg-white d-flex justify-content-center align-items-center">
<i t-att-class="widget.icon"></i>
</div>
<div class="tile-container__status-container">
<h2 class="status-container__title"><t t-esc="widget.name"/></h2>
<div class="status-container__figures d-flex flex-wrap align-items-baseline">
<h3 class="mb-0 mb-md-1 mb-lg-0 mr-1"><t t-esc="widget.value"/></h3>
</div>
</div>
</div>
</div>
</t>
<t t-name="DynamicDashboardChart">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Dashboard</title>
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/css/bootstrap.min.css"
integrity="sha512-P5MgMn1jBN01asBgU0z60Qk4QxiXo86+wlFahKrsQf37c9cro517WzVSPPV1tDKzhku2iJ2FVgL67wG03SGnNA=="
crossorigin="anonymous"/>
</head>
<div style="padding-bottom:30px" t-att-class="widget.cols +' col-4 block'" t-att-data-id="widget.id">
<div class="card">
<div class="card-header">
<div class="row">
<div class="col">
<h3><t t-esc="widget.name"/></h3>
</div>
<div class="col">
<div style="float:right;"><i title="Configuration" class="fa fa-cog block_setting fa-2x"/></div>
</div>
</div>
</div>
<div class="card-body mt-3" id="in_ex_body_hide">
<div class="row">
<div class="col-md-12 chart_canvas">
<div id="chart_canvas">
<canvas class="chart_graphs" width="300" height="200"> </canvas>
</div>
</div>
</div>
</div>
</div>
</div>
</t>
</templates>
\ No newline at end of file
<odoo>
<record id="dashboard_menu_form_view" model="ir.ui.view">
<field name="name">dashboard.menu.form.view</field>
<field name="model">dashboard.menu</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<group>
<field name="name"/>
<field name="menu_id"/>
<field name="group_ids" widget="many2many_tags" invisible="1"/>
<field name="client_action" invisible="1"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record id="view_dashboard_menu_tree" model="ir.ui.view">
<field name="name">dashboard.menu.tree.view</field>
<field name="model">dashboard.menu</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="menu_id"/>
</tree>
</field>
</record>
<record id="action_dashboard_menu" model="ir.actions.act_window">
<field name="name">Dashboard Menu</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">dashboard.menu</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem name="Configuration" id="menu_dynamic_dashboard_configuration" parent="odoo_dynamic_dashboard.menu_dashboard"
sequence="3"/>
<menuitem name="Dashboards" id="menu_dynamic_dashboard_menu" parent="odoo_dynamic_dashboard.menu_dynamic_dashboard_configuration"
sequence="3" action="action_dashboard_menu"/>
</odoo>
\ No newline at end of file
<odoo>
<data>
<record id="action_dynamic_dashboard" model="ir.actions.client">
<field name="name">Dashboard</field>
<field name="tag">dynamic_dashboard</field>
</record>
<menuitem name="Dashboard" id="menu_dashboard" sequence="1"/>
<menuitem name="Dashboards" id="menu_dynamic_dashboard" parent="odoo_dynamic_dashboard.menu_dashboard"
sequence="1" action="action_dynamic_dashboard"/>
</data>
</odoo>
\ No newline at end of file
<odoo>
<record id="dashboard_block_form_view" model="ir.ui.view">
<field name="name">dashboard.block.form.view</field>
<field name="model">dashboard.block</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<group>
<div>
<field name="name" style="font-size: 30px;" placeholder="Block Name" required="1"/>
</div>
</group>
</group>
<group>
<group>
<field name="model_id" attrs="{'required':[('edit_mode','=', True)]}"/>
<field name="client_action" invisible="1"/>
<field name="model_name" invisible="1"/>
<field name="edit_mode" invisible="1"/>
<field name="operation" attrs="{'required':[('edit_mode','=', True)]}"/>
<field name="measured_field" domain="[('model_id','=',model_id), ('ttype','in',['float','integer','monetary']), ('store', '=', True)]" attrs="{'required':[('edit_mode','=', True)]}"/>
<field name="filter" widget="domain" options="{'model': 'model_name'}"/>
</group>
</group>
<group string="Block Information">
<group>
<field name="sequence" invisible="1"/>
<field name="type" required="1"/>
<field name="graph_type" attrs="{'invisible': [('type','not in', 'graph')], 'required':[('type', '=', 'graph')]}"/>
<field name="graph_size" attrs="{'invisible': [('type','not in', 'graph')]}"/>
<field name="fa_icon" attrs="{'invisible': [('type','not in', 'tile')]}"/>
<field name="group_by" attrs="{'invisible': [('type','not in', 'graph')], 'required':[('edit_mode','=', True),('type','=','graph')]}"
domain="[('model_id','=',model_id), ('ttype','!=','one2many'), ('store', '=', True)]"/>
<field name="tile_color" attrs="{'invisible': [('type','not in', 'tile')]}"
widget="color"/>
<field name="text_color" attrs="{'invisible': [('type','not in', 'tile')]}"
widget="color"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record id="view_dashboard_block_tree" model="ir.ui.view">
<field name="name">dashboard.block.tree.view</field>
<field name="model">dashboard.block</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="model_id"/>
<field name="type"/>
</tree>
</field>
</record>
<record id="action_dashboard_block" model="ir.actions.act_window">
<field name="name">Dashboard Block</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">dashboard.block</field>
<field name="view_mode">tree,form</field>
<field name="context">{'default_edit_mode' : True}</field>
</record>
<menuitem name="Blocks" id="menu_dynamic_dashboard_blocks" parent="odoo_dynamic_dashboard.menu_dashboard"
sequence="1" action="action_dashboard_block"/>
</odoo>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment