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.

165 lines
5.6 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', compute="_compute_min_value")
  46. min_fixed_value = fields.Float('Minimum')
  47. min_code = fields.Text('Minimum Computation Code')
  48. min_error = fields.Char('Error', compute="_compute_min_value")
  49. min_dbsource_id = fields.Many2one(
  50. 'base.external.dbsource',
  51. 'External DB Source',
  52. )
  53. max_type = fields.Selection((
  54. ('static', 'Fixed value'),
  55. ('python', 'Python Code'),
  56. ('local', 'SQL - Local DB'),
  57. ('external', 'SQL - External DB'),
  58. ), 'Max Type', required=True)
  59. max_value = fields.Float(string='Maximum', compute="_compute_max_value")
  60. max_fixed_value = fields.Float('Maximum')
  61. max_code = fields.Text('Maximum Computation Code')
  62. max_error = fields.Char('Error', compute="_compute_max_value")
  63. max_dbsource_id = fields.Many2one(
  64. 'base.external.dbsource',
  65. 'External DB Source',
  66. )
  67. color = fields.Char(
  68. string="Color",
  69. help="Choose your color"
  70. )
  71. threshold_ids = fields.Many2many(
  72. 'kpi.threshold',
  73. 'kpi_threshold_range_rel',
  74. 'range_id',
  75. 'threshold_id',
  76. 'Thresholds',
  77. )
  78. company_id = fields.Many2one(
  79. 'res.company', 'Company',
  80. default=lambda self: self.env.user.company_id.id)
  81. @api.multi
  82. def _compute_min_value(self):
  83. for obj in self:
  84. value = None
  85. error = None
  86. try:
  87. if obj.min_type == 'local' and is_sql_or_ddl_statement(
  88. obj.min_code):
  89. self.env.cr.execute(obj.min_code)
  90. dic = self.env.cr.dictfetchall()
  91. if is_one_value(dic):
  92. value = dic[0]['value']
  93. elif (obj.min_type == 'external' and obj.min_dbsource_id.id and
  94. is_sql_or_ddl_statement(obj.min_code)):
  95. dbsrc_obj = obj.min_dbsource_id
  96. res = dbsrc_obj.execute(obj.min_code)
  97. if is_one_value(res):
  98. value = res[0]['value']
  99. elif obj.min_type == 'python':
  100. value = safe_eval(obj.min_code)
  101. else:
  102. value = obj.min_fixed_value
  103. except Exception as e:
  104. value = None
  105. error = str(e)
  106. obj.min_value = value
  107. obj.min_error = error
  108. @api.multi
  109. def _compute_max_value(self):
  110. for obj in self:
  111. value = None
  112. error = None
  113. try:
  114. if obj.max_type == 'local' and is_sql_or_ddl_statement(
  115. obj.max_code):
  116. self.env.cr.execute(obj.max_code)
  117. dic = self.env.cr.dictfetchall()
  118. if is_one_value(dic):
  119. value = dic[0]['value']
  120. elif (obj.max_type == 'external' and obj.max_dbsource_id.id and
  121. is_sql_or_ddl_statement(obj.max_code)):
  122. dbsrc_obj = obj.max_dbsource_id
  123. res = dbsrc_obj.execute(obj.max_code)
  124. if is_one_value(res):
  125. value = res[0]['value']
  126. elif obj.max_type == 'python':
  127. value = safe_eval(obj.max_code)
  128. else:
  129. value = obj.max_fixed_value
  130. except Exception as e:
  131. value = None
  132. error = str(e)
  133. obj.max_value = value
  134. obj.max_error = error
  135. @api.multi
  136. def _compute_is_valid_range(self):
  137. for obj in self:
  138. if obj.min_error or obj.max_error:
  139. obj.valid = False
  140. obj.invalid_message = (
  141. "Either minimum or maximum value has "
  142. "computation errors. Please fix them.")
  143. elif obj.max_value < obj.min_value:
  144. obj.valid = False
  145. obj.invalid_message = (
  146. "Minimum value is greater than the maximum "
  147. "value! Please adjust them.")
  148. else:
  149. obj.valid = True
  150. obj.invalid_message = ""