Browse Source

[ADD] Possibility to display a value base on a field (sum/avg..);

[REF] refactor openerp file description;
pull/111/head
Sylvain LE GAL 10 years ago
parent
commit
85d3152c71
  1. 65
      web_dashboard_tile/__openerp__.py
  2. 41
      web_dashboard_tile/static/src/css/tile.css
  3. BIN
      web_dashboard_tile/static/src/img/avg.png
  4. BIN
      web_dashboard_tile/static/src/img/max.png
  5. BIN
      web_dashboard_tile/static/src/img/min.png
  6. BIN
      web_dashboard_tile/static/src/img/screenshot_action_click.png
  7. BIN
      web_dashboard_tile/static/src/img/screenshot_dashboard.png
  8. BIN
      web_dashboard_tile/static/src/img/sum.png
  9. 88
      web_dashboard_tile/tile.py
  10. 18
      web_dashboard_tile/tile.xml

65
web_dashboard_tile/__openerp__.py

@ -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": """
Add Tiles to Dashboard
======================
Features:
---------
module to give you a dashboard where you can configure tile from any view module to give you a dashboard where you can configure tile from any view
and add them as short cut. and add them as short cut.
Tile can be:
* affected to a user;
* be global for all users (In that case, some tiles will be hidden if
* Tile can be:
* displayed only for a user;
* global for all users (In that case, some tiles will be hidden if
the current user doesn't have access to the given model); the current user doesn't have access to the given model);
* The tile displays items count of a given model restricted to a given domain;
* 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;
Screenshot:
-----------
* Dashboad sample, displaying Sale Orders to invoice:
.. image:: web_dashboard_tile/static/src/img/screenshot_dashboard.png
* Tree view displayed when user click on the tile:
.. image:: web_dashboard_tile/static/src/img/screenshot_action_click.png
Kown issues/limits: Kown issues/limits:
* change color picks wrong color
* can not edit tile from dashboard
* context are ignored
* date filter can not be relative
* combine domain of menue and filter so can not restore origin filter
-------------------
* 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: possible future improvments:
* support context_today
* add icons
* support client side action (like inbox)
* support select int/float column with min/max/avg/sum to display
----------------------------
* support context_today;
* add icons;
* support client side action (like inbox);
""", """,
"summary": "Add tile to dashboard",
'data': ['tile.xml',
'data': [
'tile.xml',
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'security/rules.xml'],
'css': ['static/src/css/tile.css'],
'security/rules.xml',
],
'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'],
} }

41
web_dashboard_tile/static/src/css/tile.css

@ -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;
font-weight: bold;
position: absolute;
left: 9px;
bottom: 9px;
.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{ .openerp .oe_kanban_view .oe_dashbaord_tile .tile_label{
padding: 9px;
padding: 5px;
font-size: 15px; font-size: 15px;
} }
.openerp .oe_kanban_view .oe_dashbaord_tile .tile_count_without_computed_value{
font-size: 52px;
font-weight: bold;
position: absolute;
left: 5px;
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;
}

BIN
web_dashboard_tile/static/src/img/avg.png

After

Width: 24  |  Height: 24  |  Size: 287 B

BIN
web_dashboard_tile/static/src/img/max.png

After

Width: 24  |  Height: 24  |  Size: 264 B

BIN
web_dashboard_tile/static/src/img/min.png

After

Width: 24  |  Height: 24  |  Size: 283 B

BIN
web_dashboard_tile/static/src/img/screenshot_action_click.png

After

Width: 754  |  Height: 168  |  Size: 24 KiB

BIN
web_dashboard_tile/static/src/img/screenshot_dashboard.png

After

Width: 638  |  Height: 165  |  Size: 36 KiB

BIN
web_dashboard_tile/static/src/img/sum.png

After

Width: 24  |  Height: 24  |  Size: 305 B

88
web_dashboard_tile/tile.py

@ -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(
cr, uid, eval(r.domain), context),
}
else:
res[r.id] = {'active': False, 'count': 0}
'count': count,
})
# Compute datas for field_id depending of field_function
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)

18
web_dashboard_tile/tile.xml

@ -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 != ''">
<div class="tile_count_with_computed_value">
<span><field name="count"/></span> <span><field name="count"/></span>
</div> </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…
Cancel
Save