mirror of https://github.com/OCA/web.git
[ADD] Possibility to display a value base on a field (sum/avg..);
[REF] refactor openerp file description;pull/111/head
parent
c83923b31a
commit
85d3152c71
|
@ -22,6 +22,7 @@
|
||||||
##############################################################################
|
##############################################################################
|
||||||
{
|
{
|
||||||
"name": "Dashboard Tile",
|
"name": "Dashboard Tile",
|
||||||
|
"summary": "Add Tiles to Dashboard",
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"depends": [
|
"depends": [
|
||||||
'web',
|
'web',
|
||||||
|
@ -33,41 +34,59 @@
|
||||||
"category": "",
|
"category": "",
|
||||||
'license': 'AGPL-3',
|
'license': 'AGPL-3',
|
||||||
"description": """
|
"description": """
|
||||||
module to give you a dashboard where you can configure tile from any view
|
Add Tiles to Dashboard
|
||||||
and add them as short cut.
|
======================
|
||||||
Tile can be:
|
Features:
|
||||||
* affected to a user;
|
---------
|
||||||
* be global for all users (In that case, some tiles will be hidden if
|
module to give you a dashboard where you can configure tile from any view
|
||||||
the current user doesn't have access to the given model);
|
and add them as short cut.
|
||||||
|
|
||||||
Kown issues/limits:
|
* Tile can be:
|
||||||
* change color picks wrong color
|
* displayed only for a user;
|
||||||
* can not edit tile from dashboard
|
* global for all users (In that case, some tiles will be hidden if
|
||||||
* context are ignored
|
the current user doesn't have access to the given model);
|
||||||
* date filter can not be relative
|
* The tile displays items count of a given model restricted to a given domain;
|
||||||
* combine domain of menue and filter so can not restore origin filter
|
* Optionnaly, the tile can display the result of a function of a field;
|
||||||
|
* Function is one of sum/avg/min/max;
|
||||||
|
* Field must be integer or float;
|
||||||
|
|
||||||
possible future improvments:
|
Screenshot:
|
||||||
* support context_today
|
-----------
|
||||||
* add icons
|
* Dashboad sample, displaying Sale Orders to invoice:
|
||||||
* support client side action (like inbox)
|
.. image:: web_dashboard_tile/static/src/img/screenshot_dashboard.png
|
||||||
* support select int/float column with min/max/avg/sum to display
|
* Tree view displayed when user click on the tile:
|
||||||
|
.. image:: web_dashboard_tile/static/src/img/screenshot_action_click.png
|
||||||
|
|
||||||
|
|
||||||
|
Kown issues/limits:
|
||||||
|
-------------------
|
||||||
|
* can not edit tile from dashboard (color, sequence, function, ...);
|
||||||
|
* context are ignored;
|
||||||
|
* date filter can not be relative;
|
||||||
|
* combine domain of menue and filter so can not restore origin filter;
|
||||||
|
|
||||||
|
possible future improvments:
|
||||||
|
----------------------------
|
||||||
|
* support context_today;
|
||||||
|
* add icons;
|
||||||
|
* support client side action (like inbox);
|
||||||
""",
|
""",
|
||||||
"summary": "Add tile to dashboard",
|
'data': [
|
||||||
'data': ['tile.xml',
|
'tile.xml',
|
||||||
'security/ir.model.access.csv',
|
'security/ir.model.access.csv',
|
||||||
'security/rules.xml'],
|
'security/rules.xml',
|
||||||
'css': ['static/src/css/tile.css'],
|
],
|
||||||
|
'css': [
|
||||||
|
'static/src/css/tile.css',
|
||||||
|
],
|
||||||
'demo': [
|
'demo': [
|
||||||
'demo/res_groups.yml',
|
'demo/res_groups.yml',
|
||||||
'demo/tile_tile.yml',
|
'demo/tile_tile.yml',
|
||||||
],
|
],
|
||||||
'test': [
|
'js': [
|
||||||
|
'static/src/js/custom_js.js',
|
||||||
|
],
|
||||||
|
'qweb': [
|
||||||
|
'static/src/xml/custom_xml.xml',
|
||||||
],
|
],
|
||||||
'installable': True,
|
|
||||||
'auto_install': False,
|
|
||||||
'js': ['static/src/js/custom_js.js'],
|
|
||||||
'qweb': ['static/src/xml/custom_xml.xml'],
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,41 @@
|
||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
.openerp .oe_kanban_view .oe_dashbaord_tile .tile_count{
|
|
||||||
font-size: 48px;
|
.openerp .oe_kanban_view .oe_dashbaord_tile .tile_label,
|
||||||
|
.openerp .oe_kanban_view .oe_dashbaord_tile .tile_count_without_computed_value,
|
||||||
|
.openerp .oe_kanban_view .oe_dashbaord_tile .tile_count_with_computed_value,
|
||||||
|
.openerp .oe_kanban_view .oe_dashbaord_tile .tile_computed_value {
|
||||||
|
width: 140px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openerp .oe_kanban_view .oe_dashbaord_tile .tile_label{
|
||||||
|
padding: 5px;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openerp .oe_kanban_view .oe_dashbaord_tile .tile_count_without_computed_value{
|
||||||
|
font-size: 52px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 9px;
|
left: 5px;
|
||||||
bottom: 9px;
|
bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openerp .oe_kanban_view .oe_dashbaord_tile .tile_count_with_computed_value{
|
||||||
|
font-size: 38px;
|
||||||
|
font-weight: bold;
|
||||||
|
position: absolute;
|
||||||
|
left: 5px;
|
||||||
|
bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openerp .oe_kanban_view .oe_dashbaord_tile .tile_computed_value{
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
bottom: 5px;
|
||||||
|
font-style: italic;
|
||||||
}
|
}
|
||||||
.openerp .oe_kanban_view .oe_dashbaord_tile .tile_label{
|
|
||||||
padding: 9px;
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
Binary file not shown.
After Width: | Height: | Size: 287 B |
Binary file not shown.
After Width: | Height: | Size: 264 B |
Binary file not shown.
After Width: | Height: | Size: 283 B |
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
Binary file not shown.
After Width: | Height: | Size: 305 B |
|
@ -24,7 +24,7 @@
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
from openerp.osv import orm, fields
|
from openerp.osv import orm, fields
|
||||||
import random
|
from openerp.tools.translate import _
|
||||||
|
|
||||||
|
|
||||||
class tile(orm.Model):
|
class tile(orm.Model):
|
||||||
|
@ -36,16 +36,46 @@ class tile(orm.Model):
|
||||||
res = {}
|
res = {}
|
||||||
records = self.browse(cr, uid, ids, context=context)
|
records = self.browse(cr, uid, ids, context=context)
|
||||||
for r in records:
|
for r in records:
|
||||||
|
res[r.id] = {
|
||||||
|
'active': False,
|
||||||
|
'count': 0,
|
||||||
|
'computed_value': 0,
|
||||||
|
'helper': '',
|
||||||
|
}
|
||||||
if ima_obj.check(
|
if ima_obj.check(
|
||||||
cr, uid, r.model_id.model, 'read', False, context):
|
cr, uid, r.model_id.model, 'read', False, context):
|
||||||
|
# Compute count item
|
||||||
model = self.pool.get(r.model_id.model)
|
model = self.pool.get(r.model_id.model)
|
||||||
res[r.id] = {
|
count = model.search_count(
|
||||||
|
cr, uid, eval(r.domain), context=context)
|
||||||
|
res[r.id].update({
|
||||||
'active': True,
|
'active': True,
|
||||||
'count': model.search_count(
|
'count': count,
|
||||||
cr, uid, eval(r.domain), context),
|
})
|
||||||
}
|
|
||||||
else:
|
# Compute datas for field_id depending of field_function
|
||||||
res[r.id] = {'active': False, 'count': 0}
|
if r.field_function and r.field_id and count != 0:
|
||||||
|
ids = model.search(
|
||||||
|
cr, uid, eval(r.domain), context=context)
|
||||||
|
vals = [x[r.field_id.name] for x in model.read(
|
||||||
|
cr, uid, ids, [r.field_id.name], context=context)]
|
||||||
|
desc = r.field_id.field_description
|
||||||
|
if r.field_function == 'min':
|
||||||
|
value = min(vals)
|
||||||
|
helper = _("'Minimum value of %s'" % desc)
|
||||||
|
elif r.field_function == 'max':
|
||||||
|
value = max(vals)
|
||||||
|
helper = _("'Maximum value of %s'" % desc)
|
||||||
|
elif r.field_function == 'sum':
|
||||||
|
value = sum(vals)
|
||||||
|
helper = _("'Total value of %s'" % desc)
|
||||||
|
elif r.field_function == 'avg':
|
||||||
|
value = sum(vals) / len(vals)
|
||||||
|
helper = _("'Average value of %s'" % desc)
|
||||||
|
res[r.id].update({
|
||||||
|
'computed_value': value,
|
||||||
|
'helper': helper,
|
||||||
|
})
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def _search_active(self, cr, uid, obj, name, arg, context=None):
|
def _search_active(self, cr, uid, obj, name, arg, context=None):
|
||||||
|
@ -64,13 +94,28 @@ class tile(orm.Model):
|
||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
'name': fields.char('Tile Name'),
|
'name': fields.char('Tile Name'),
|
||||||
'model_id': fields.many2one('ir.model', 'Model'),
|
'model_id': fields.many2one('ir.model', 'Model', required=True),
|
||||||
'user_id': fields.many2one('res.users', 'User'),
|
'user_id': fields.many2one('res.users', 'User'),
|
||||||
'domain': fields.text('Domain'),
|
'domain': fields.text('Domain'),
|
||||||
'action_id': fields.many2one('ir.actions.act_window', 'Action'),
|
'action_id': fields.many2one('ir.actions.act_window', 'Action'),
|
||||||
'count': fields.function(
|
'count': fields.function(
|
||||||
_get_tile_info, type='int', string='Count',
|
_get_tile_info, type='int', string='Count',
|
||||||
multi='tile_info', readonly=True),
|
multi='tile_info', readonly=True),
|
||||||
|
'computed_value': fields.function(
|
||||||
|
_get_tile_info, type='float', string='Computed Value',
|
||||||
|
multi='tile_info', readonly=True),
|
||||||
|
'helper': fields.function(
|
||||||
|
_get_tile_info, type='char', string='Helper',
|
||||||
|
multi='tile_info', readonly=True),
|
||||||
|
'field_function': fields.selection([
|
||||||
|
('min', 'Minimum'),
|
||||||
|
('max', 'Maximum'),
|
||||||
|
('sum', 'Sum'),
|
||||||
|
('avg', 'Average')], 'Function'),
|
||||||
|
'field_id': fields.many2one(
|
||||||
|
'ir.model.fields', 'Field',
|
||||||
|
domain="[('model_id', '=', model_id),"
|
||||||
|
" ('ttype', 'in', ['float', 'int'])]"),
|
||||||
'active': fields.function(
|
'active': fields.function(
|
||||||
_get_tile_info, type='boolean', string='Active',
|
_get_tile_info, type='boolean', string='Active',
|
||||||
multi='tile_info', readonly=True, fnct_search=_search_active),
|
multi='tile_info', readonly=True, fnct_search=_search_active),
|
||||||
|
@ -80,6 +125,31 @@ class tile(orm.Model):
|
||||||
'Sequence', required=True),
|
'Sequence', required=True),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Constraint Section
|
||||||
|
def _check_model_id_field_id(self, cr, uid, ids, context=None):
|
||||||
|
for t in self.browse(cr, uid, ids, context=context):
|
||||||
|
if t.field_id and t.field_id.model_id.id != t.model_id.id:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _check_field_id_field_function(self, cr, uid, ids, context=None):
|
||||||
|
for t in self.browse(cr, uid, ids, context=context):
|
||||||
|
if t.field_id and not t.field_function or\
|
||||||
|
t.field_function and not t.field_id:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
_constraints = [
|
||||||
|
(
|
||||||
|
_check_model_id_field_id,
|
||||||
|
"Error ! Please select a field of the select model.",
|
||||||
|
['model_id', 'field_id']),
|
||||||
|
(
|
||||||
|
_check_field_id_field_function,
|
||||||
|
"Error ! Please set both fields: 'Field' and 'Function'.",
|
||||||
|
['field_id', 'field_function']),
|
||||||
|
]
|
||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
'domain': '[]',
|
'domain': '[]',
|
||||||
'color': '#0E6C7E',
|
'color': '#0E6C7E',
|
||||||
|
@ -123,6 +193,4 @@ class tile(orm.Model):
|
||||||
[('model', '=',
|
[('model', '=',
|
||||||
vals['model_id'])])
|
vals['model_id'])])
|
||||||
vals['model_id'] = model_ids[0]
|
vals['model_id'] = model_ids[0]
|
||||||
if 'color' not in vals:
|
|
||||||
vals['color'] = random.randint(1, 10)
|
|
||||||
return self.create(cr, uid, vals, context)
|
return self.create(cr, uid, vals, context)
|
||||||
|
|
|
@ -27,6 +27,8 @@
|
||||||
<field name="model_id"/>
|
<field name="model_id"/>
|
||||||
<field name="user_id"/>
|
<field name="user_id"/>
|
||||||
<field name="action_id"/>
|
<field name="action_id"/>
|
||||||
|
<field name="field_function"/>
|
||||||
|
<field name="field_id"/>
|
||||||
<field name="color" widget="color"/>
|
<field name="color" widget="color"/>
|
||||||
<field name="font_color" widget="color"/>
|
<field name="font_color" widget="color"/>
|
||||||
</group>
|
</group>
|
||||||
|
@ -47,6 +49,9 @@
|
||||||
<field name="count"/>
|
<field name="count"/>
|
||||||
<field name="color"/>
|
<field name="color"/>
|
||||||
<field name="font_color"/>
|
<field name="font_color"/>
|
||||||
|
<field name="field_id" />
|
||||||
|
<field name="field_function" />
|
||||||
|
<field name="helper" />
|
||||||
<templates>
|
<templates>
|
||||||
<t t-name="kanban-box">
|
<t t-name="kanban-box">
|
||||||
<div t-attf-class="oe_dashbaord_tile oe_kanban_global_click" t-attf-style="background-color:#{record.color.raw_value}" >
|
<div t-attf-class="oe_dashbaord_tile oe_kanban_global_click" t-attf-style="background-color:#{record.color.raw_value}" >
|
||||||
|
@ -67,9 +72,20 @@
|
||||||
<div style="padding-left: 0.5em; height: 115px;">
|
<div style="padding-left: 0.5em; height: 115px;">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="tile_count">
|
<t t-if="record.field_id.raw_value != '' and record.field_function.raw_value != ''">
|
||||||
<span><field name="count"/></span>
|
<div class="tile_count_with_computed_value">
|
||||||
</div>
|
<span><field name="count"/></span>
|
||||||
|
</div>
|
||||||
|
<div class="tile_computed_value" t-att-title="record.helper.raw_value">
|
||||||
|
<img t-att-src="_s + '/web_dashboard_tile/static/src/img/' + record.field_function.raw_value + '.png'"/>
|
||||||
|
<span><field name="computed_value"/></span>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
<t t-if="!(record.field_id.raw_value != '' and record.field_function.raw_value != '')">
|
||||||
|
<div class="tile_count_without_computed_value">
|
||||||
|
<span><field name="count"/></span>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="oe_clear"></div>
|
<div class="oe_clear"></div>
|
||||||
|
|
Loading…
Reference in New Issue