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.

167 lines
5.7 KiB

  1. # Copyright 2012 - Now Savoir-faire Linux <https://www.savoirfairelinux.com/>
  2. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
  3. from odoo import fields, models, api
  4. from odoo.tools.safe_eval import safe_eval
  5. import re
  6. def is_one_value(result):
  7. # check if sql query returns only one value
  8. if type(result) is dict and 'value' in result.dictfetchone():
  9. return True
  10. elif type(result) is list and 'value' in result[0]:
  11. return True
  12. else:
  13. return False
  14. RE_SELECT_QUERY = re.compile('.*(' + '|'.join((
  15. 'INSERT',
  16. 'UPDATE',
  17. 'DELETE',
  18. 'CREATE',
  19. 'ALTER',
  20. 'DROP',
  21. 'GRANT',
  22. 'REVOKE',
  23. 'INDEX',
  24. )) + ')')
  25. def is_sql_or_ddl_statement(query):
  26. """Check if sql query is a SELECT statement"""
  27. return not RE_SELECT_QUERY.match(query.upper())
  28. class KPIThresholdRange(models.Model):
  29. """
  30. KPI Threshold Range
  31. """
  32. _name = "kpi.threshold.range"
  33. _description = "KPI Threshold Range"
  34. name = fields.Char('Name', size=50, required=True)
  35. valid = fields.Boolean(string='Valid', required=True,
  36. compute="_compute_is_valid_range", default=True)
  37. invalid_message = fields.Char(string='Message', size=100,
  38. compute="_compute_is_valid_range")
  39. min_type = fields.Selection((
  40. ('static', 'Fixed value'),
  41. ('python', 'Python Code'),
  42. ('local', 'SQL - Local DB'),
  43. ('external', 'SQL - Externa DB'),
  44. ), 'Min Type', required=True)
  45. min_value = fields.Float(string='Minimum Value',
  46. compute="_compute_min_value")
  47. min_fixed_value = fields.Float('Minimum Fixed Value')
  48. min_code = fields.Text('Minimum Computation Code')
  49. min_error = fields.Char('Minimum Error', compute="_compute_min_value")
  50. min_dbsource_id = fields.Many2one(
  51. 'base.external.dbsource',
  52. 'External DB Source Minimum',
  53. )
  54. max_type = fields.Selection((
  55. ('static', 'Fixed value'),
  56. ('python', 'Python Code'),
  57. ('local', 'SQL - Local DB'),
  58. ('external', 'SQL - External DB'),
  59. ), 'Max Type', required=True)
  60. max_value = fields.Float(string='Maximum Value',
  61. compute="_compute_max_value")
  62. max_fixed_value = fields.Float('Maximum Fixed Value')
  63. max_code = fields.Text('Maximum Computation Code')
  64. max_error = fields.Char('Maximum Error', compute="_compute_max_value")
  65. max_dbsource_id = fields.Many2one(
  66. 'base.external.dbsource',
  67. 'External DB Source Maximum',
  68. )
  69. color = fields.Char(
  70. string="Color",
  71. help="Choose your color"
  72. )
  73. threshold_ids = fields.Many2many(
  74. 'kpi.threshold',
  75. 'kpi_threshold_range_rel',
  76. 'range_id',
  77. 'threshold_id',
  78. 'Thresholds',
  79. )
  80. company_id = fields.Many2one(
  81. 'res.company', 'Company',
  82. default=lambda self: self.env.user.company_id.id)
  83. @api.multi
  84. def _compute_min_value(self):
  85. for obj in self:
  86. value = None
  87. error = None
  88. try:
  89. if obj.min_type == 'local' and is_sql_or_ddl_statement(
  90. obj.min_code):
  91. self.env.cr.execute(obj.min_code)
  92. dic = self.env.cr.dictfetchall()
  93. if is_one_value(dic):
  94. value = dic[0]['value']
  95. elif (obj.min_type == 'external' and obj.min_dbsource_id.id and
  96. is_sql_or_ddl_statement(obj.min_code)):
  97. dbsrc_obj = obj.min_dbsource_id
  98. res = dbsrc_obj.execute(obj.min_code)
  99. if is_one_value(res):
  100. value = res[0]['value']
  101. elif obj.min_type == 'python':
  102. value = safe_eval(obj.min_code)
  103. else:
  104. value = obj.min_fixed_value
  105. except Exception as e:
  106. value = None
  107. error = str(e)
  108. obj.min_value = value
  109. obj.min_error = error
  110. @api.multi
  111. def _compute_max_value(self):
  112. for obj in self:
  113. value = None
  114. error = None
  115. try:
  116. if obj.max_type == 'local' and is_sql_or_ddl_statement(
  117. obj.max_code):
  118. self.env.cr.execute(obj.max_code)
  119. dic = self.env.cr.dictfetchall()
  120. if is_one_value(dic):
  121. value = dic[0]['value']
  122. elif (obj.max_type == 'external' and obj.max_dbsource_id.id and
  123. is_sql_or_ddl_statement(obj.max_code)):
  124. dbsrc_obj = obj.max_dbsource_id
  125. res = dbsrc_obj.execute(obj.max_code)
  126. if is_one_value(res):
  127. value = res[0]['value']
  128. elif obj.max_type == 'python':
  129. value = safe_eval(obj.max_code)
  130. else:
  131. value = obj.max_fixed_value
  132. except Exception as e:
  133. value = None
  134. error = str(e)
  135. obj.max_value = value
  136. obj.max_error = error
  137. @api.multi
  138. def _compute_is_valid_range(self):
  139. for obj in self:
  140. if obj.min_error or obj.max_error:
  141. obj.valid = False
  142. obj.invalid_message = (
  143. "Either minimum or maximum value has "
  144. "computation errors. Please fix them.")
  145. elif obj.max_value < obj.min_value:
  146. obj.valid = False
  147. obj.invalid_message = (
  148. "Minimum value is greater than the maximum "
  149. "value! Please adjust them.")
  150. else:
  151. obj.valid = True
  152. obj.invalid_message = ""