[12.0][IMP] kpi_dashboard: Add demo data and testing
parent
55d7ab2c3a
commit
76fe2d1195
|
@ -45,6 +45,18 @@ Configure KPIs
|
|||
#. Meter: result must contain `value`, `min` and `max`
|
||||
#. Graph: result must contain a list on `graphs` containing `values`, `title` and `key`
|
||||
|
||||
#. In order to compute the KPI you can use a predefined function from a model or
|
||||
use the code to directly compute it.
|
||||
|
||||
Using KPI with code
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Define the code directly on the code field. You can use:
|
||||
|
||||
* `self` and `model` as the kpi element
|
||||
* The script should create a variable called `result` as a dictionary that
|
||||
will be stored as the value
|
||||
|
||||
|
||||
Configure dashboards
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -20,4 +20,5 @@
|
|||
"views/kpi_kpi.xml",
|
||||
"views/kpi_dashboard.xml",
|
||||
],
|
||||
"demo": ["demo/demo_dashboard.xml"],
|
||||
}
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="demo_dashboard" model="kpi.dashboard">
|
||||
<field name="name">Dashboard</field>
|
||||
<field name="number_of_columns">4</field>
|
||||
<field name="widget_dimension_y">50</field>
|
||||
<field name="widget_dimension_x">250</field>
|
||||
<field name="background_color">#020202</field>
|
||||
</record>
|
||||
|
||||
<record id="widget_number_01" model="kpi.kpi">
|
||||
<field name="name">Number 01</field>
|
||||
<field name="prefix">$</field>
|
||||
<field name="computation_method">code</field>
|
||||
<field name="widget">number</field>
|
||||
<field name="code">
|
||||
result = {"value": 10000,"previous": 12000}
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="widget_number_02" model="kpi.kpi">
|
||||
<field name="name">Number 02</field>
|
||||
<field name="suffix">€</field>
|
||||
<field name="computation_method">code</field>
|
||||
<field name="widget">number</field>
|
||||
<field name="code">
|
||||
result = {"value": 12000,"previous": 10000}
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<function model="kpi.kpi" name="compute"
|
||||
eval="[[ref('widget_number_01'), ref('widget_number_02')]]"/>
|
||||
|
||||
<record id="widget_meter_01" model="kpi.kpi">
|
||||
<field name="name">Meter 01</field>
|
||||
<field name="suffix">€</field>
|
||||
<field name="computation_method">code</field>
|
||||
<field name="widget">meter</field>
|
||||
<field name="code">
|
||||
result = {"min": 0, "max": 100, "value": 90}
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="widget_meter_02" model="kpi.kpi">
|
||||
<field name="name">Meter 02</field>
|
||||
<field name="prefix">$</field>
|
||||
<field name="computation_method">code</field>
|
||||
<field name="widget">meter</field>
|
||||
<field name="code">
|
||||
result = {"min": 0, "max": 100, "value": 40}
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<function model="kpi.kpi" name="compute"
|
||||
eval="[[ref('widget_meter_01'), ref('widget_meter_02')]]"/>
|
||||
|
||||
<record id="widget_graph" model="kpi.kpi">
|
||||
<field name="name">Graph</field>
|
||||
<field name="computation_method">code</field>
|
||||
<field name="widget">graph</field>
|
||||
<field name="code">
|
||||
result = {"graphs": [
|
||||
{
|
||||
"values": [
|
||||
{"x": i, "y": i * 1000}
|
||||
for i in range(1, 12)
|
||||
],
|
||||
"title": "Current Year",
|
||||
"key": "current",
|
||||
"area": True,
|
||||
"color": "ffffff",
|
||||
},
|
||||
{
|
||||
"values": [
|
||||
{"x": i, "y": 1000 * (12-i)}
|
||||
for i in range(1, 12)
|
||||
],
|
||||
"title": "Previous Year",
|
||||
"key": "previous",
|
||||
"area": True,
|
||||
"color": "000000",
|
||||
},
|
||||
]}
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<function model="kpi.kpi" name="compute"
|
||||
eval="[[ref('widget_graph')]]"/>
|
||||
|
||||
|
||||
<record id="dashboard_widget_text" model="kpi.dashboard.item">
|
||||
<field name="name">Dashboard title</field>
|
||||
<field name="dashboard_id" ref="demo_dashboard"/>
|
||||
<field name="column">1</field>
|
||||
<field name="row">1</field>
|
||||
<field name="size_x">4</field>
|
||||
<field name="color">#707070</field>
|
||||
<field name="font_color">#000000</field>
|
||||
</record>
|
||||
|
||||
<record id="dashboard_widget_number_01" model="kpi.dashboard.item">
|
||||
<field name="name">Number 01</field>
|
||||
<field name="dashboard_id" ref="demo_dashboard"/>
|
||||
<field name="kpi_id" ref="widget_number_01"/>
|
||||
<field name="column">1</field>
|
||||
<field name="row">2</field>
|
||||
<field name="size_y">4</field>
|
||||
<field name="color">#47bbb3</field>
|
||||
<field name="font_color">#ffffff</field>
|
||||
</record>
|
||||
|
||||
<record id="dashboard_widget_number_02" model="kpi.dashboard.item">
|
||||
<field name="name">Number 02</field>
|
||||
<field name="dashboard_id" ref="demo_dashboard"/>
|
||||
<field name="kpi_id" ref="widget_number_02"/>
|
||||
<field name="column">1</field>
|
||||
<field name="row">6</field>
|
||||
<field name="size_y">4</field>
|
||||
<field name="color">#ec663c</field>
|
||||
<field name="font_color">#ffffff</field>
|
||||
</record>
|
||||
|
||||
<record id="dashboard_widget_meter_01" model="kpi.dashboard.item">
|
||||
<field name="name">Meter 01</field>
|
||||
<field name="dashboard_id" ref="demo_dashboard"/>
|
||||
<field name="kpi_id" ref="widget_meter_01"/>
|
||||
<field name="column">2</field>
|
||||
<field name="row">2</field>
|
||||
<field name="size_y">4</field>
|
||||
<field name="color">#9c4274</field>
|
||||
<field name="font_color">#ffffff</field>
|
||||
</record>
|
||||
|
||||
<record id="dashboard_widget_meter_02" model="kpi.dashboard.item">
|
||||
<field name="name">Meter 02</field>
|
||||
<field name="dashboard_id" ref="demo_dashboard"/>
|
||||
<field name="kpi_id" ref="widget_meter_02"/>
|
||||
<field name="column">2</field>
|
||||
<field name="row">6</field>
|
||||
<field name="size_y">4</field>
|
||||
<field name="color">#12b0c5</field>
|
||||
<field name="font_color">#ffffff</field>
|
||||
</record>
|
||||
|
||||
<record id="dashboard_widget_graph" model="kpi.dashboard.item">
|
||||
<field name="name">Graph</field>
|
||||
<field name="dashboard_id" ref="demo_dashboard"/>
|
||||
<field name="kpi_id" ref="widget_graph"/>
|
||||
<field name="column">3</field>
|
||||
<field name="row">2</field>
|
||||
<field name="size_x">2</field>
|
||||
<field name="size_y">8</field>
|
||||
<field name="color">#ff9618</field>
|
||||
<field name="font_color">#ffffff</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
|
@ -11,6 +11,17 @@ Configure KPIs
|
|||
#. In order to compute the KPI you can use a predefined function from a model or
|
||||
use the code to directly compute it.
|
||||
|
||||
Using KPI with code
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Define the code directly on the code field. You can use `self` and `model` as the kpi element
|
||||
The script should create a variable called `result` as a dictionary that
|
||||
will be stored as the value.
|
||||
For example, we can use::
|
||||
|
||||
result = {}
|
||||
result['value'] = len(model.search([('id', '=', %s)]))
|
||||
result['previous'] = len(model.search([('id', '!=', %s)]))
|
||||
|
||||
Configure dashboards
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="generator" content="Docutils 0.15.1: http://docutils.sourceforge.net/" />
|
||||
<meta name="generator" content="Docutils 0.14: http://docutils.sourceforge.net/" />
|
||||
<title>Kpi Dashboard</title>
|
||||
<style type="text/css">
|
||||
|
||||
|
@ -374,14 +374,15 @@ ul.auto-toc {
|
|||
<ul class="simple">
|
||||
<li><a class="reference internal" href="#configuration" id="id1">Configuration</a><ul>
|
||||
<li><a class="reference internal" href="#configure-kpis" id="id2">Configure KPIs</a></li>
|
||||
<li><a class="reference internal" href="#configure-dashboards" id="id3">Configure dashboards</a></li>
|
||||
<li><a class="reference internal" href="#using-kpi-with-code" id="id3">Using KPI with code</a></li>
|
||||
<li><a class="reference internal" href="#configure-dashboards" id="id4">Configure dashboards</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#bug-tracker" id="id4">Bug Tracker</a></li>
|
||||
<li><a class="reference internal" href="#credits" id="id5">Credits</a><ul>
|
||||
<li><a class="reference internal" href="#authors" id="id6">Authors</a></li>
|
||||
<li><a class="reference internal" href="#contributors" id="id7">Contributors</a></li>
|
||||
<li><a class="reference internal" href="#maintainers" id="id8">Maintainers</a></li>
|
||||
<li><a class="reference internal" href="#bug-tracker" id="id5">Bug Tracker</a></li>
|
||||
<li><a class="reference internal" href="#credits" id="id6">Credits</a><ul>
|
||||
<li><a class="reference internal" href="#authors" id="id7">Authors</a></li>
|
||||
<li><a class="reference internal" href="#contributors" id="id8">Contributors</a></li>
|
||||
<li><a class="reference internal" href="#maintainers" id="id9">Maintainers</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -398,10 +399,21 @@ ul.auto-toc {
|
|||
<li>Graph: result must contain a list on <cite>graphs</cite> containing <cite>values</cite>, <cite>title</cite> and <cite>key</cite></li>
|
||||
</ol>
|
||||
</li>
|
||||
<li>In order to compute the KPI you can use a predefined function from a model or
|
||||
use the code to directly compute it.</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div class="section" id="using-kpi-with-code">
|
||||
<h2><a class="toc-backref" href="#id3">Using KPI with code</a></h2>
|
||||
<p>Define the code directly on the code field. You can use:</p>
|
||||
<ul class="simple">
|
||||
<li><cite>self</cite> and <cite>model</cite> as the kpi element</li>
|
||||
<li>The script should create a variable called <cite>result</cite> as a dictionary that
|
||||
will be stored as the value</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="configure-dashboards">
|
||||
<h2><a class="toc-backref" href="#id3">Configure dashboards</a></h2>
|
||||
<h2><a class="toc-backref" href="#id4">Configure dashboards</a></h2>
|
||||
<ol class="arabic simple">
|
||||
<li>Access <cite>Dashboards > Configuration > KPI Dashboards > Configure Dashboards</cite></li>
|
||||
<li>Create a new dashboard and specify all the standard parameters on <cite>Widget configuration</cite></li>
|
||||
|
@ -412,7 +424,7 @@ ul.auto-toc {
|
|||
</div>
|
||||
</div>
|
||||
<div class="section" id="bug-tracker">
|
||||
<h1><a class="toc-backref" href="#id4">Bug Tracker</a></h1>
|
||||
<h1><a class="toc-backref" href="#id5">Bug Tracker</a></h1>
|
||||
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/reporting-engine/issues">GitHub Issues</a>.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us smashing it by providing a detailed and welcomed
|
||||
|
@ -420,21 +432,21 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
|
|||
<p>Do not contact contributors directly about support or help with technical issues.</p>
|
||||
</div>
|
||||
<div class="section" id="credits">
|
||||
<h1><a class="toc-backref" href="#id5">Credits</a></h1>
|
||||
<h1><a class="toc-backref" href="#id6">Credits</a></h1>
|
||||
<div class="section" id="authors">
|
||||
<h2><a class="toc-backref" href="#id6">Authors</a></h2>
|
||||
<h2><a class="toc-backref" href="#id7">Authors</a></h2>
|
||||
<ul class="simple">
|
||||
<li>Creu Blanca</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="contributors">
|
||||
<h2><a class="toc-backref" href="#id7">Contributors</a></h2>
|
||||
<h2><a class="toc-backref" href="#id8">Contributors</a></h2>
|
||||
<ul class="simple">
|
||||
<li>Enric Tobella <<a class="reference external" href="mailto:etobella@creublanca.es">etobella@creublanca.es</a>></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="maintainers">
|
||||
<h2><a class="toc-backref" href="#id8">Maintainers</a></h2>
|
||||
<h2><a class="toc-backref" href="#id9">Maintainers</a></h2>
|
||||
<p>This module is maintained by the OCA.</p>
|
||||
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
|
||||
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
from . import test_formula
|
||||
from . import test_kpi_dashboard
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
# Copyright 2020 Creu Blanca
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tests.common import Form
|
||||
from mock import patch
|
||||
|
||||
|
||||
class TestKpiDashboard(TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestKpiDashboard, self).setUp()
|
||||
self.kpi_01 = self.env['kpi.kpi'].create({
|
||||
'name': 'KPI 01',
|
||||
'computation_method': 'function',
|
||||
'widget': 'number',
|
||||
'function': 'test_demo_number'
|
||||
})
|
||||
self.kpi_02 = self.env['kpi.kpi'].create({
|
||||
'name': 'KPI 02',
|
||||
'computation_method': 'function',
|
||||
'widget': 'number',
|
||||
'function': 'test_demo_number'
|
||||
})
|
||||
self.dashboard = self.env['kpi.dashboard'].create({
|
||||
'name': 'Dashboard',
|
||||
'number_of_columns': 4,
|
||||
'widget_dimension_x': 250,
|
||||
'widget_dimension_y': 250,
|
||||
})
|
||||
self.env['kpi.dashboard.item'].create({
|
||||
'dashboard_id': self.dashboard.id,
|
||||
'kpi_id': self.kpi_01.id,
|
||||
'name': self.kpi_01.name,
|
||||
'row': 1,
|
||||
'column': 1,
|
||||
})
|
||||
self.env['kpi.dashboard.item'].create({
|
||||
'dashboard_id': self.dashboard.id,
|
||||
'name': self.kpi_02.name,
|
||||
'kpi_id': self.kpi_02.id,
|
||||
'row': 1,
|
||||
'column': 2,
|
||||
})
|
||||
self.env['kpi.dashboard.item'].create({
|
||||
'dashboard_id': self.dashboard.id,
|
||||
'name': 'TITLE',
|
||||
'row': 2,
|
||||
'column': 1,
|
||||
})
|
||||
|
||||
def test_constrains_01(self):
|
||||
with self.assertRaises(ValidationError):
|
||||
self.kpi_01.dashboard_item_ids.write({'size_x': 2})
|
||||
|
||||
def test_constrains_02(self):
|
||||
with self.assertRaises(ValidationError):
|
||||
self.kpi_02.dashboard_item_ids.write({'size_x': 4})
|
||||
|
||||
def test_constrains_03(self):
|
||||
with self.assertRaises(ValidationError):
|
||||
self.kpi_01.dashboard_item_ids.write({'size_y': 11})
|
||||
|
||||
def test_menu(self):
|
||||
self.assertFalse(self.dashboard.menu_id)
|
||||
wzd = self.env['kpi.dashboard.menu'].create({
|
||||
'dashboard_id': self.dashboard.id,
|
||||
'menu_id': self.env['ir.ui.menu'].search([], limit=1).id,
|
||||
})
|
||||
wzd.generate_menu()
|
||||
self.assertTrue(self.dashboard.menu_id)
|
||||
self.assertFalse(self.dashboard.menu_id.groups_id)
|
||||
self.dashboard.write({
|
||||
'group_ids': [
|
||||
(6, 0, self.env['res.groups'].search([], limit=1).ids)]
|
||||
})
|
||||
self.assertTrue(self.dashboard.menu_id.groups_id)
|
||||
|
||||
def test_onchange(self):
|
||||
with Form(self.env['kpi.dashboard']) as dashboard:
|
||||
dashboard.name = 'New Dashboard'
|
||||
with dashboard.item_ids.new() as item:
|
||||
item.kpi_id = self.kpi_01
|
||||
self.assertTrue(item.name)
|
||||
|
||||
def test_read_dashboard(self):
|
||||
data = self.dashboard.read_dashboard()
|
||||
title_found = False
|
||||
actions = 0
|
||||
for item in data['item_ids']:
|
||||
if not item.get('kpi_id'):
|
||||
title_found = True
|
||||
if item.get('actions', False):
|
||||
actions += len(item['actions'])
|
||||
self.assertTrue(title_found)
|
||||
self.assertEqual(0, actions)
|
||||
act01 = self.env['ir.actions.act_window'].search(
|
||||
[], limit=1)
|
||||
self.env['kpi.kpi.action'].create({
|
||||
'kpi_id': self.kpi_01.id,
|
||||
'action': '%s,%s' % (act01._name, act01.id)
|
||||
})
|
||||
act02 = self.env['ir.actions.act_url'].search(
|
||||
[], limit=1)
|
||||
self.env['kpi.kpi.action'].create({
|
||||
'kpi_id': self.kpi_01.id,
|
||||
'action': '%s,%s' % (act02._name, act02.id)
|
||||
})
|
||||
data = self.dashboard.read_dashboard()
|
||||
title_found = False
|
||||
actions = 0
|
||||
for item in data['item_ids']:
|
||||
if not item.get('kpi_id'):
|
||||
title_found = True
|
||||
if item.get('actions', False):
|
||||
actions += len(item['actions'])
|
||||
self.assertTrue(title_found)
|
||||
self.assertEqual(2, actions)
|
||||
self.assertFalse(data.get("action_id", False))
|
||||
wzd = self.env['kpi.dashboard.menu'].create({
|
||||
'dashboard_id': self.dashboard.id,
|
||||
'menu_id': self.env['ir.ui.menu'].search([], limit=1).id,
|
||||
})
|
||||
wzd.generate_menu()
|
||||
data = self.dashboard.read_dashboard()
|
||||
self.assertTrue(data.get("action_id", False))
|
||||
|
||||
def test_compute(self):
|
||||
self.assertFalse(self.kpi_01.value_last_update)
|
||||
with patch(
|
||||
"odoo.addons.kpi_dashboard.models.kpi_kpi."
|
||||
"KpiKpi.test_demo_number", create=True
|
||||
) as f:
|
||||
f.return_value = {"value": 0}
|
||||
self.kpi_01.compute()
|
||||
self.assertTrue(self.kpi_01.value_last_update)
|
||||
|
||||
def test_compute_model(self):
|
||||
self.assertFalse(self.kpi_01.value_last_update)
|
||||
self.kpi_01.model_id = self.env.ref('base.model_res_partner')
|
||||
with patch(
|
||||
"odoo.addons.base.models.res_partner.Partner.test_demo_number",
|
||||
create=True
|
||||
) as f:
|
||||
f.return_value = {"value": 0}
|
||||
self.kpi_01.compute()
|
||||
self.assertTrue(self.kpi_01.value_last_update)
|
||||
|
||||
def test_generate_cron(self):
|
||||
self.assertFalse(self.kpi_01.cron_id)
|
||||
self.kpi_01.generate_cron()
|
||||
self.assertTrue(self.kpi_01.cron_id)
|
||||
self.assertFalse(self.kpi_01.value_last_update)
|
||||
with patch(
|
||||
"odoo.addons.kpi_dashboard.models.kpi_kpi."
|
||||
"KpiKpi.test_demo_number", create=True
|
||||
) as f:
|
||||
f.return_value = {"value": 0}
|
||||
self.kpi_01.cron_id.method_direct_trigger()
|
||||
self.assertTrue(self.kpi_01.value_last_update)
|
Loading…
Reference in New Issue