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.

194 lines
6.4 KiB

  1. # -*- coding: utf-8 -*-
  2. # Copyright (C) 2017 - Today: GRAP (http://www.grap.coop)
  3. # @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
  4. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  5. import re
  6. from openerp import api, fields, models
  7. class BiSQLViewField(models.Model):
  8. _name = 'bi.sql.view.field'
  9. _order = 'sequence'
  10. _TTYPE_SELECTION = [
  11. ('boolean', 'boolean'),
  12. ('char', 'char'),
  13. ('date', 'date'),
  14. ('datetime', 'datetime'),
  15. ('float', 'float'),
  16. ('integer', 'integer'),
  17. ('many2one', 'many2one'),
  18. ('selection', 'selection'),
  19. ]
  20. _GRAPH_TYPE_SELECTION = [
  21. ('col', 'Column'),
  22. ('row', 'Row'),
  23. ('measure', 'Measure'),
  24. ]
  25. # Mapping to guess Odoo field type, from SQL column type
  26. _SQL_MAPPING = {
  27. 'boolean': 'boolean',
  28. 'bigint': 'integer',
  29. 'integer': 'integer',
  30. 'double precision': 'float',
  31. 'numeric': 'float',
  32. 'text': 'char',
  33. 'character varying': 'char',
  34. 'date': 'datetime',
  35. 'timestamp without time zone': 'datetime',
  36. }
  37. name = fields.Char(string='Name', required=True, readonly=True)
  38. sql_type = fields.Char(
  39. string='SQL Type', required=True, readonly=True,
  40. help="SQL Type in the database")
  41. sequence = fields.Integer(string='sequence', required=True, readonly=True)
  42. bi_sql_view_id = fields.Many2one(
  43. string='SQL View', comodel_name='bi.sql.view', ondelete='cascade')
  44. is_index = fields.Boolean(
  45. string='Is Index', help="Check this box if you want to create"
  46. " an index on that field. This is recommended for searchable and"
  47. " groupable fields, to reduce duration")
  48. is_group_by = fields.Boolean(
  49. string='Is Group by', help="Check this box if you want to create"
  50. " a 'group by' option in the search view")
  51. index_name = fields.Char(
  52. string='Index Name', compute='_compute_index_name')
  53. graph_type = fields.Selection(
  54. string='Graph Type', selection=_GRAPH_TYPE_SELECTION)
  55. field_description = fields.Char(
  56. string='Field Description', help="This will be used as the name"
  57. " of the Odoo field, displayed for users")
  58. ttype = fields.Selection(
  59. string='Field Type', selection=_TTYPE_SELECTION, help="Type of the"
  60. " Odoo field that will be created. Let empty if you don't want to"
  61. " create a new field. If empty, this field will not be displayed"
  62. " neither available for search or group by function")
  63. selection = fields.Text(
  64. string='Selection Options', default='[]',
  65. help="For 'Selection' Odoo field.\n"
  66. " List of options, specified as a Python expression defining a list of"
  67. " (key, label) pairs. For example:"
  68. " [('blue','Blue'), ('yellow','Yellow')]")
  69. many2one_model_id = fields.Many2one(
  70. comodel_name='ir.model', string='Model',
  71. help="For 'Many2one' Odoo field.\n"
  72. " Co Model of the field.")
  73. # Compute Section
  74. @api.multi
  75. def _compute_index_name(self):
  76. for sql_field in self:
  77. sql_field.index_name = '%s_%s' % (
  78. sql_field.bi_sql_view_id.view_name, sql_field.name)
  79. # Overload Section
  80. @api.multi
  81. def create(self, vals):
  82. field_without_prefix = vals['name'][2:]
  83. # guess field description
  84. field_description = re.sub(
  85. r'\w+', lambda m: m.group(0).capitalize(),
  86. field_without_prefix.replace('_id', '').replace('_', ' '))
  87. # Guess ttype
  88. # Don't execute as simple .get() in the dict to manage
  89. # correctly the type 'character varying(x)'
  90. ttype = False
  91. for k, v in self._SQL_MAPPING.iteritems():
  92. if k in vals['sql_type']:
  93. ttype = v
  94. # Guess many2one_model_id
  95. many2one_model_id = False
  96. if vals['sql_type'] == 'integer' and(
  97. vals['name'][-3:] == '_id'):
  98. ttype = 'many2one'
  99. model_name = self._model_mapping().get(field_without_prefix, '')
  100. many2one_model_id = self.env['ir.model'].search(
  101. [('model', '=', model_name)]).id
  102. vals.update({
  103. 'ttype': ttype,
  104. 'field_description': field_description,
  105. 'many2one_model_id': many2one_model_id,
  106. })
  107. return super(BiSQLViewField, self).create(vals)
  108. # Custom Section
  109. @api.model
  110. def _model_mapping(self):
  111. """Return dict of key value, to try to guess the model based on a
  112. field name. Sample :
  113. {'account_id': 'account.account'; 'product_id': 'product.product'}
  114. """
  115. relation_fields = self.env['ir.model.fields'].search([
  116. ('ttype', '=', 'many2one')])
  117. res = {}
  118. keys_to_pop = []
  119. for field in relation_fields:
  120. if field.name in res and res.get(field.name) != field.relation:
  121. # The field name is not predictive
  122. keys_to_pop.append(field.name)
  123. else:
  124. res.update({field.name: field.relation})
  125. for key in list(set(keys_to_pop)):
  126. res.pop(key)
  127. return res
  128. @api.multi
  129. def _prepare_model_field(self):
  130. self.ensure_one()
  131. return {
  132. 'name': self.name,
  133. 'field_description': self.field_description,
  134. 'model_id': self.bi_sql_view_id.model_id.id,
  135. 'ttype': self.ttype,
  136. 'selection': self.ttype == 'selection' and self.selection or False,
  137. 'relation': self.ttype == 'many2one' and
  138. self.many2one_model_id.model or False,
  139. }
  140. @api.multi
  141. def _prepare_graph_field(self):
  142. self.ensure_one()
  143. res = ''
  144. if self.graph_type and self.field_description:
  145. res = """<field name="{}" type="{}" />""".format(
  146. self.name, self.graph_type)
  147. return res
  148. @api.multi
  149. def _prepare_search_field(self):
  150. self.ensure_one()
  151. res = ''
  152. if self.field_description:
  153. res = """<field name="{}"/>""".format(self.name)
  154. return res
  155. @api.multi
  156. def _prepare_search_filter_field(self):
  157. self.ensure_one()
  158. res = ''
  159. if self.field_description and self.is_group_by:
  160. res =\
  161. """<filter string="%s" context="{'group_by':'%s'}"/>""" % (
  162. self.field_description, self.name)
  163. return res