diff --git a/setup/web_dashboard_tile/odoo/addons/web_dashboard_tile b/setup/web_dashboard_tile/odoo/addons/web_dashboard_tile
new file mode 120000
index 000000000..f721548b7
--- /dev/null
+++ b/setup/web_dashboard_tile/odoo/addons/web_dashboard_tile
@@ -0,0 +1 @@
+../../../../web_dashboard_tile
\ No newline at end of file
diff --git a/setup/web_dashboard_tile/setup.py b/setup/web_dashboard_tile/setup.py
new file mode 100644
index 000000000..28c57bb64
--- /dev/null
+++ b/setup/web_dashboard_tile/setup.py
@@ -0,0 +1,6 @@
+import setuptools
+
+setuptools.setup(
+ setup_requires=['setuptools-odoo'],
+ odoo_addon=True,
+)
diff --git a/web_dashboard_tile/controllers/main.py b/web_dashboard_tile/controllers/main.py
index 1f29fe29a..d561a67b9 100644
--- a/web_dashboard_tile/controllers/main.py
+++ b/web_dashboard_tile/controllers/main.py
@@ -2,14 +2,13 @@
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-from odoo.http import Controller, route, request
+from odoo.http import Controller, request, route
class WebDashboardTile(Controller):
-
- @route('/web_dashboard_tile/create_tile', type='json', auth='user')
+ @route("/web_dashboard_tile/create_tile", type="json", auth="user")
def create_tile(self, model_name, *args, **kwargs):
- IrModel = request.env['ir.model']
- model = IrModel.search([('model', '=', model_name)])
- kwargs.update({'model_id': model.id})
- return request.env['tile.tile'].create(kwargs)
+ IrModel = request.env["ir.model"]
+ model = IrModel.search([("model", "=", model_name)])
+ kwargs.update({"model_id": model.id})
+ return request.env["tile.tile"].create(kwargs)
diff --git a/web_dashboard_tile/demo/tile_category.xml b/web_dashboard_tile/demo/tile_category.xml
index c8be7c677..9a1da03d4 100644
--- a/web_dashboard_tile/demo/tile_category.xml
+++ b/web_dashboard_tile/demo/tile_category.xml
@@ -1,16 +1,16 @@
-
+
Modules
1
-
+
Currencies
2
-
+
diff --git a/web_dashboard_tile/demo/tile_tile.xml b/web_dashboard_tile/demo/tile_tile.xml
index aca1a2d34..cd1550cb1 100644
--- a/web_dashboard_tile/demo/tile_tile.xml
+++ b/web_dashboard_tile/demo/tile_tile.xml
@@ -1,28 +1,32 @@
-
+
Installed Modules
-
-
-
- [['state', 'in', ['installed', 'to upgrade', 'to remove']]]
+
+
+
+ [['state', 'in', ['installed', 'to upgrade', 'to remove']]]
Installed OCA Modules
-
-
-
- [['state', 'in', ['installed', 'to upgrade', 'to remove']], ['author', 'ilike', 'Odoo Community Association (OCA)']]
+
+
+
+ [['state', 'in', ['installed', 'to upgrade', 'to remove']], ['author', 'ilike', 'Odoo Community Association (OCA)']]
Currencies (Max Rate)
-
-
+
+
max
-
+
[]
diff --git a/web_dashboard_tile/migrations/12.0.1.0.0/post-migration.py b/web_dashboard_tile/migrations/12.0.1.0.0/post-migration.py
index 118657c6d..16ecb2788 100644
--- a/web_dashboard_tile/migrations/12.0.1.0.0/post-migration.py
+++ b/web_dashboard_tile/migrations/12.0.1.0.0/post-migration.py
@@ -13,17 +13,15 @@ def migrate(cr, version):
# categories was optional in previous versions
# affecting all tiles without categories
- tiles_without_category = env["tile.tile"].search(
- [('category_id', '=', False)])
+ tiles_without_category = env["tile.tile"].search([("category_id", "=", False)])
if tiles_without_category:
- default_category = env["tile.category"].create({
- "name": "Default Category",
- })
- tiles_without_category.write({
- 'category_id': default_category.id
- })
+ default_category = env["tile.category"].create(
+ {
+ "name": "Default Category",
+ }
+ )
+ tiles_without_category.write({"category_id": default_category.id})
# Enable all categories, to generate actions and menus
- categories = env['tile.category'].with_context(
- active_test=False).search([])
- categories.write({'active': True})
+ categories = env["tile.category"].with_context(active_test=False).search([])
+ categories.write({"active": True})
diff --git a/web_dashboard_tile/models/tile_category.py b/web_dashboard_tile/models/tile_category.py
index f43c96369..6a48073f0 100644
--- a/web_dashboard_tile/models/tile_category.py
+++ b/web_dashboard_tile/models/tile_category.py
@@ -17,23 +17,24 @@ class TileCategory(models.Model):
active = fields.Boolean(default=True)
action_id = fields.Many2one(
- string='Odoo Action', comodel_name='ir.actions.act_window',
- readonly=True)
+ string="Odoo Action", comodel_name="ir.actions.act_window", readonly=True
+ )
menu_id = fields.Many2one(
- string='Odoo Menu', comodel_name='ir.ui.menu', readonly=True)
+ string="Odoo Menu", comodel_name="ir.ui.menu", readonly=True
+ )
tile_ids = fields.One2many(
- string='Tiles', comodel_name='tile.tile',
- inverse_name='category_id')
+ string="Tiles", comodel_name="tile.tile", inverse_name="category_id"
+ )
tile_qty = fields.Integer(
- string='Tiles Quantity',
- compute='_compute_tile_qty',
+ string="Tiles Quantity",
+ compute="_compute_tile_qty",
store=True,
)
- @api.depends('tile_ids')
+ @api.depends("tile_ids")
def _compute_tile_qty(self):
for category in self:
category.tile_qty = len(category.tile_ids)
@@ -41,34 +42,36 @@ class TileCategory(models.Model):
def _prepare_action(self):
self.ensure_one()
return {
- 'name': self.name,
- 'res_model': 'tile.tile',
- 'type': 'ir.actions.act_window',
- 'view_mode': 'kanban',
- 'domain': """[
+ "name": self.name,
+ "res_model": "tile.tile",
+ "type": "ir.actions.act_window",
+ "view_mode": "kanban",
+ "domain": """[
('hidden', '=', False),
'|', ('user_id', '=', False), ('user_id', '=', uid),
('category_id', '=', {self.id})
- ]""".format(self=self),
+ ]""".format(
+ self=self
+ ),
}
def _prepare_menu(self):
self.ensure_one()
return {
- 'name': self.name,
- 'parent_id': self.env.ref(
- 'web_dashboard_tile.menu_dashboard_tile').id,
- 'action': 'ir.actions.act_window,%d' % self.action_id.id,
- 'sequence': self.sequence,
+ "name": self.name,
+ "parent_id": self.env.ref("web_dashboard_tile.menu_dashboard_tile").id,
+ "action": "ir.actions.act_window,%d" % self.action_id.id,
+ "sequence": self.sequence,
}
def _create_ui(self):
- IrUiMenu = self.env['ir.ui.menu']
- IrActionsActWindows = self.env['ir.actions.act_window']
+ IrUiMenu = self.env["ir.ui.menu"]
+ IrActionsActWindows = self.env["ir.actions.act_window"]
for category in self:
if not category.action_id:
category.action_id = IrActionsActWindows.create(
- category._prepare_action())
+ category._prepare_action()
+ )
if not category.menu_id:
category.menu_id = IrUiMenu.create(category._prepare_menu())
@@ -88,16 +91,16 @@ class TileCategory(models.Model):
def write(self, vals):
res = super().write(vals)
- if 'active' in vals.keys():
- if vals.get('active'):
+ if "active" in vals.keys():
+ if vals.get("active"):
self._create_ui()
else:
self._delete_ui()
- if 'sequence' in vals.keys():
- self.mapped('menu_id').write({'sequence': vals['sequence']})
- if 'name' in vals.keys():
- self.mapped('menu_id').write({'name': vals['name']})
- self.mapped('action_id').write({'name': vals['name']})
+ if "sequence" in vals.keys():
+ self.mapped("menu_id").write({"sequence": vals["sequence"]})
+ if "name" in vals.keys():
+ self.mapped("menu_id").write({"name": vals["name"]})
+ self.mapped("action_id").write({"name": vals["name"]})
return res
def unlink(self):
diff --git a/web_dashboard_tile/models/tile_tile.py b/web_dashboard_tile/models/tile_tile.py
index 3eb408c58..e9f9a8d10 100644
--- a/web_dashboard_tile/models/tile_tile.py
+++ b/web_dashboard_tile/models/tile_tile.py
@@ -5,15 +5,15 @@
import datetime
import time
-from statistics import median
-from dateutil.relativedelta import relativedelta
from collections import OrderedDict
+from statistics import median
+
+from dateutil.relativedelta import relativedelta
from odoo import api, fields, models
+from odoo.exceptions import ValidationError, except_orm
from odoo.tools.safe_eval import safe_eval as eval
from odoo.tools.translate import _
-from odoo.exceptions import ValidationError, except_orm
-
FIELD_FUNCTIONS = OrderedDict(
[
@@ -81,8 +81,11 @@ class TileTile(models.Model):
sequence = fields.Integer(default=0, required=True)
category_id = fields.Many2one(
- string="Category", comodel_name="tile.category", required=True,
- ondelete="CASCADE")
+ string="Category",
+ comodel_name="tile.category",
+ required=True,
+ ondelete="CASCADE",
+ )
user_id = fields.Many2one(string="User", comodel_name="res.users")
@@ -98,9 +101,7 @@ class TileTile(models.Model):
"(that is, when User field is left empty)",
)
- model_id = fields.Many2one(
- comodel_name="ir.model", string="Model", required=True
- )
+ model_id = fields.Many2one(comodel_name="ir.model", string="Model", required=True)
model_name = fields.Char(string="Model name", related="model_id.model")
@@ -108,26 +109,30 @@ class TileTile(models.Model):
action_id = fields.Many2one(
comodel_name="ir.actions.act_window",
- string="Action", help="Let empty to use the default action related to"
- " the selected model.",
- domain="[('res_model', '=', model_name)]")
+ string="Action",
+ help="Let empty to use the default action related to" " the selected model.",
+ domain="[('res_model', '=', model_name)]",
+ )
active = fields.Boolean(
compute="_compute_active", search="_search_active", readonly=True
)
hide_if_null = fields.Boolean(
- string="Hide if null", help="If checked, the item will be hidden"
- " if the primary value is null.")
+ string="Hide if null",
+ help="If checked, the item will be hidden" " if the primary value is null.",
+ )
hidden = fields.Boolean(
- string="Hidden", compute="_compute_data",
- search="_search_hidden")
+ string="Hidden", compute="_compute_data", search="_search_hidden"
+ )
# Primary Value
primary_function = fields.Selection(
- string="Primary Function", required=True,
- selection=FIELD_FUNCTION_SELECTION, default="count",
+ string="Primary Function",
+ required=True,
+ selection=FIELD_FUNCTION_SELECTION,
+ default="count",
)
primary_field_id = fields.Many2one(
@@ -143,19 +148,20 @@ class TileTile(models.Model):
"ie: '{:,} Kgs' will output '1,000 Kgs' if value is 1000.",
)
- primary_value = fields.Float(
- string="Primary Value", compute="_compute_data")
+ primary_value = fields.Float(string="Primary Value", compute="_compute_data")
primary_formated_value = fields.Char(
- string="Primary Formated Value", compute="_compute_data")
+ string="Primary Formated Value", compute="_compute_data"
+ )
primary_helper = fields.Char(
- string="Primary Helper", compute="_compute_helper",
- store=True)
+ string="Primary Helper", compute="_compute_helper", store=True
+ )
# Secondary Value
secondary_function = fields.Selection(
- string="Secondary Function", selection=FIELD_FUNCTION_SELECTION,
+ string="Secondary Function",
+ selection=FIELD_FUNCTION_SELECTION,
)
secondary_field_id = fields.Many2one(
@@ -171,16 +177,14 @@ class TileTile(models.Model):
"ie: '{:,} Kgs' will output '1,000 Kgs' if value is 1000.",
)
- secondary_value = fields.Float(
- string="Secondary Value", compute="_compute_data")
+ secondary_value = fields.Float(string="Secondary Value", compute="_compute_data")
secondary_formated_value = fields.Char(
string="Secondary Formated Value", compute="_compute_data"
)
secondary_helper = fields.Char(
- string="Secondary Helper", compute="_compute_helper",
- store=True
+ string="Secondary Helper", compute="_compute_helper", store=True
)
error = fields.Char(string="Error Details", compute="_compute_data")
@@ -198,19 +202,14 @@ class TileTile(models.Model):
count = model.search_count(eval(domain, eval_context))
except Exception as e:
tile.primary_value = 0.0
- tile.primary_formated_value =\
- tile.secondary_formated_value = _("Error")
+ tile.primary_formated_value = tile.secondary_formated_value = _("Error")
tile.error = str(e)
return
fields = [
- f.name
- for f in [tile.primary_field_id, tile.secondary_field_id]
- if f
+ f.name for f in [tile.primary_field_id, tile.secondary_field_id] if f
]
read_vals = (
- fields
- and model.search_read(eval(domain, eval_context), fields)
- or []
+ fields and model.search_read(eval(domain, eval_context), fields) or []
)
for f in ["primary_", "secondary_"]:
f_function = f + "function"
@@ -231,8 +230,9 @@ class TileTile(models.Model):
value = func(vals or [0.0])
try:
tile[f_value] = value
- tile[f_formated_value] = (
- tile[f_format] or "{:,}").format(value)
+ tile[f_formated_value] = (tile[f_format] or "{:,}").format(
+ value
+ )
if tile.hide_if_null and not value:
tile.hidden = True
except ValueError as e:
@@ -274,8 +274,9 @@ class TileTile(models.Model):
def _search_hidden(self, operator, operand):
items = self.search([])
hidden_tile_ids = [x.id for x in items if x.hidden]
- if (operator == "=" and operand is False) or\
- (operator == "!=" and operand is True):
+ if (operator == "=" and operand is False) or (
+ operator == "!=" and operand is True
+ ):
domain = [("id", "not in", hidden_tile_ids)]
else:
domain = [("id", "in", hidden_tile_ids)]
@@ -346,12 +347,14 @@ class TileTile(models.Model):
"target": "current",
"domain": self.domain,
}
- action.update({
- "name": self.name,
- "display_name": self.name,
- "context": dict(self.env.context, group_by=False),
- "domain": self.domain,
- })
+ action.update(
+ {
+ "name": self.name,
+ "display_name": self.name,
+ "context": dict(self.env.context, group_by=False),
+ "domain": self.domain,
+ }
+ )
return action
@api.model
@@ -359,9 +362,7 @@ class TileTile(models.Model):
if "model_id" in vals and not vals["model_id"].isdigit():
# need to replace model_name with its id
vals["model_id"] = (
- self.env["ir.model"]
- .search([("model", "=", vals["model_id"])])
- .id
+ self.env["ir.model"].search([("model", "=", vals["model_id"])]).id
)
self.create(vals)
diff --git a/web_dashboard_tile/readme/CONFIGURE.rst b/web_dashboard_tile/readme/CONFIGURE.rst
index 858db81f8..96b88356e 100644
--- a/web_dashboard_tile/readme/CONFIGURE.rst
+++ b/web_dashboard_tile/readme/CONFIGURE.rst
@@ -30,4 +30,3 @@ You can optionally define a secondary value, for that purpose :
* You can define a specific format. (``.format()`` python syntax)
.. image:: ../static/description/tile_tile_form_secondary_value.png
-
diff --git a/web_dashboard_tile/readme/ROADMAP.rst b/web_dashboard_tile/readme/ROADMAP.rst
index b6bc023e7..6801a4c4c 100644
--- a/web_dashboard_tile/readme/ROADMAP.rst
+++ b/web_dashboard_tile/readme/ROADMAP.rst
@@ -12,4 +12,3 @@
* Restore original Domain + Filter when an action is set.
* Posibility to hide the tile based on a field expression.
* Posibility to set the background color based on a field expression.
-
diff --git a/web_dashboard_tile/security/ir_rule.xml b/web_dashboard_tile/security/ir_rule.xml
index f30705e35..15f43a31f 100644
--- a/web_dashboard_tile/security/ir_rule.xml
+++ b/web_dashboard_tile/security/ir_rule.xml
@@ -1,10 +1,10 @@
-
+
tile.owner
-
+
[
'|',
diff --git a/web_dashboard_tile/static/src/css/web_dashboard_tile.css b/web_dashboard_tile/static/src/css/web_dashboard_tile.css
index e9a3d962a..7e9d1ceec 100644
--- a/web_dashboard_tile/static/src/css/web_dashboard_tile.css
+++ b/web_dashboard_tile/static/src/css/web_dashboard_tile.css
@@ -5,11 +5,11 @@
/* Fix bug where draggin a tile results in the element losing its style */
.o_kanban_view .oe_dashboard_tile {
- padding: 0px !important;
+ padding: 0px !important;
}
.o_kanban_view .oe_dashboard_tile .tile_background {
- padding: 8px;
- height: 100%;
+ padding: 8px;
+ height: 100%;
}
.o_kanban_view .oe_dashboard_tile .tile_label,
@@ -24,7 +24,7 @@
font-size: 15px;
}
-.o_kanban_view .oe_dashboard_tile .tile_primary_value{
+.o_kanban_view .oe_dashboard_tile .tile_primary_value {
font-size: 54px;
position: absolute;
left: 5px;
@@ -32,7 +32,7 @@
bottom: 5px;
}
-.o_kanban_view .oe_dashboard_tile .tile_secondary_value{
+.o_kanban_view .oe_dashboard_tile .tile_secondary_value {
display: none;
font-size: 18px;
font-style: italic;
@@ -42,12 +42,12 @@
bottom: 5px;
}
-.o_kanban_view .oe_dashboard_tile .with_secondary .tile_primary_value{
+.o_kanban_view .oe_dashboard_tile .with_secondary .tile_primary_value {
font-size: 38px;
bottom: 30px;
}
-.o_kanban_view .oe_dashboard_tile .with_secondary .tile_secondary_value{
+.o_kanban_view .oe_dashboard_tile .with_secondary .tile_secondary_value {
display: block;
}
diff --git a/web_dashboard_tile/views/menu.xml b/web_dashboard_tile/views/menu.xml
index a2dd84a61..3c7004c20 100644
--- a/web_dashboard_tile/views/menu.xml
+++ b/web_dashboard_tile/views/menu.xml
@@ -1,14 +1,18 @@
-
+
-
+ sequence="0"
+ />
-
+ sequence="100"
+ />
diff --git a/web_dashboard_tile/views/templates.xml b/web_dashboard_tile/views/templates.xml
index 69acf7d7c..e143f532c 100644
--- a/web_dashboard_tile/views/templates.xml
+++ b/web_dashboard_tile/views/templates.xml
@@ -1,9 +1,12 @@
-
+
-
+
diff --git a/web_dashboard_tile/views/tile_category.xml b/web_dashboard_tile/views/tile_category.xml
index b8c8eab58..f88da43d6 100644
--- a/web_dashboard_tile/views/tile_category.xml
+++ b/web_dashboard_tile/views/tile_category.xml
@@ -1,4 +1,4 @@
-
+
@@ -15,21 +15,35 @@
@@ -40,10 +54,10 @@
tile.category
-
-
-
-
+
+
+
+
@@ -56,8 +70,11 @@
{'active_test': False}
-
+ action="action_tile_category"
+ sequence="50"
+ />
diff --git a/web_dashboard_tile/views/tile_tile.xml b/web_dashboard_tile/views/tile_tile.xml
index 2d2ecb247..5ffdb1c84 100644
--- a/web_dashboard_tile/views/tile_tile.xml
+++ b/web_dashboard_tile/views/tile_tile.xml
@@ -1,13 +1,13 @@
-
+
tile.tile
-
-
-
+
+
+
@@ -16,17 +16,17 @@
tile.tile
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -37,64 +37,79 @@