OCA reporting engine fork for dev and update.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

192 lines
6.4 KiB

  1. # Copyright 2020 Creu Blanca
  2. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
  3. from odoo import api, fields, models, _
  4. from odoo.exceptions import ValidationError
  5. class KpiDashboard(models.Model):
  6. _name = "kpi.dashboard"
  7. _description = "Dashboard"
  8. name = fields.Char(required=True)
  9. active = fields.Boolean(default=True,)
  10. item_ids = fields.One2many(
  11. "kpi.dashboard.item", inverse_name="dashboard_id", copy=True,
  12. )
  13. number_of_columns = fields.Integer(default=5, required=True)
  14. width = fields.Integer(compute="_compute_width")
  15. margin_y = fields.Integer(default=10, required=True)
  16. margin_x = fields.Integer(default=10, required=True)
  17. widget_dimension_x = fields.Integer(default=250, required=True)
  18. widget_dimension_y = fields.Integer(default=250, required=True)
  19. background_color = fields.Char(required=True, default="#f9f9f9")
  20. group_ids = fields.Many2many("res.groups",)
  21. menu_id = fields.Many2one("ir.ui.menu", copy=False)
  22. def write(self, vals):
  23. res = super().write(vals)
  24. if "group_ids" in vals:
  25. for rec in self:
  26. if rec.menu_id:
  27. rec.menu_id.write(
  28. {"groups_id": [(6, 0, rec.group_ids.ids)]}
  29. )
  30. return res
  31. @api.depends("widget_dimension_x", "margin_x", "number_of_columns")
  32. def _compute_width(self):
  33. for rec in self:
  34. rec.width = (
  35. rec.margin_x * (rec.number_of_columns + 1)
  36. + rec.widget_dimension_x * rec.number_of_columns
  37. )
  38. def read_dashboard(self):
  39. self.ensure_one()
  40. result = {
  41. "name": self.name,
  42. "width": self.width,
  43. "item_ids": self.item_ids.read_dashboard(),
  44. "max_cols": self.number_of_columns,
  45. "margin_x": self.margin_x,
  46. "margin_y": self.margin_y,
  47. "widget_dimension_x": self.widget_dimension_x,
  48. "widget_dimension_y": self.widget_dimension_y,
  49. "background_color": self.background_color,
  50. }
  51. if self.menu_id:
  52. result["action_id"] = self.menu_id.action.id
  53. return result
  54. def _generate_menu_vals(self, menu, action):
  55. return {
  56. "parent_id": menu.id or False,
  57. "name": self.name,
  58. "action": "%s,%s" % (action._name, action.id),
  59. "groups_id": [(6, 0, self.group_ids.ids)],
  60. }
  61. def _generate_action_vals(self, menu):
  62. return {
  63. "name": self.name,
  64. "res_model": self._name,
  65. "view_mode": "dashboard",
  66. "res_id": self.id,
  67. }
  68. def _generate_menu(self, menu):
  69. action = self.env["ir.actions.act_window"].create(
  70. self._generate_action_vals(menu)
  71. )
  72. self.menu_id = self.env["ir.ui.menu"].create(
  73. self._generate_menu_vals(menu, action)
  74. )
  75. class KpiDashboardItem(models.Model):
  76. _name = "kpi.dashboard.item"
  77. _description = "Dashboard Items"
  78. _order = "row asc, column asc"
  79. name = fields.Char(required=True)
  80. kpi_id = fields.Many2one("kpi.kpi")
  81. dashboard_id = fields.Many2one(
  82. "kpi.dashboard", required=True, ondelete="cascade"
  83. )
  84. column = fields.Integer(required=True, default=1)
  85. row = fields.Integer(required=True, default=1)
  86. end_row = fields.Integer(store=True, compute='_compute_end_row')
  87. end_column = fields.Integer(store=True, compute='_compute_end_column')
  88. size_x = fields.Integer(required=True, default=1)
  89. size_y = fields.Integer(required=True, default=1)
  90. color = fields.Char()
  91. font_color = fields.Char()
  92. @api.depends('row', 'size_y')
  93. def _compute_end_row(self):
  94. for r in self:
  95. r.end_row = r.row + r.size_y - 1
  96. @api.depends('column', 'size_x')
  97. def _compute_end_column(self):
  98. for r in self:
  99. r.end_column = r.column + r.size_x - 1
  100. @api.constrains('size_y')
  101. def _check_size_y(self):
  102. for rec in self:
  103. if rec.size_y > 10:
  104. raise ValidationError(_(
  105. 'Size Y of the widget cannot be bigger than 10'))
  106. def _check_size_domain(self):
  107. return [
  108. ('dashboard_id', '=', self.dashboard_id.id),
  109. ('id', '!=', self.id),
  110. ('row', '<=', self.end_row),
  111. ('end_row', '>=', self.row),
  112. ('column', '<=', self.end_column),
  113. ('end_column', '>=', self.column),
  114. ]
  115. @api.constrains('end_row', 'end_column', 'row', 'column')
  116. def _check_size(self):
  117. for r in self:
  118. if self.search(r._check_size_domain(), limit=1):
  119. raise ValidationError(_(
  120. 'Widgets cannot be crossed by other widgets'
  121. ))
  122. if r.end_column > r.dashboard_id.number_of_columns:
  123. raise ValidationError(_(
  124. 'Widget %s is bigger than expected'
  125. ) % r.display_name)
  126. @api.onchange("kpi_id")
  127. def _onchange_kpi(self):
  128. for rec in self:
  129. if not rec.name and rec.kpi_id:
  130. rec.name = rec.kpi_id.name
  131. def _read_dashboard(self):
  132. vals = {
  133. "id": self.id,
  134. "name": self.name,
  135. "col": self.column,
  136. "row": self.row,
  137. "sizex": self.size_x,
  138. "sizey": self.size_y,
  139. "color": self.color,
  140. "font_color": self.font_color or "000000",
  141. }
  142. if self.kpi_id:
  143. vals.update(
  144. {
  145. "widget": self.kpi_id.widget,
  146. "kpi_id": self.kpi_id.id,
  147. "suffix": self.kpi_id.suffix or "",
  148. "prefix": self.kpi_id.prefix or "",
  149. }
  150. )
  151. if self.kpi_id.compute_on_fly:
  152. vals.update({
  153. "value": self.kpi_id._compute_value(),
  154. "value_last_update": fields.Datetime.now(),
  155. })
  156. else:
  157. vals.update({
  158. "value": self.kpi_id.value,
  159. "value_last_update": self.kpi_id.value_last_update,
  160. })
  161. if self.kpi_id.action_ids:
  162. vals["actions"] = self.kpi_id.action_ids.read_dashboard()
  163. else:
  164. vals["widget"] = "base_text"
  165. return vals
  166. def read_dashboard(self):
  167. result = []
  168. for kpi in self:
  169. result.append(kpi._read_dashboard())
  170. return result