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.

239 lines
8.0 KiB

  1. # Copyright 2015-2019 Onestein (<https://www.onestein.eu>)
  2. # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
  3. from collections import defaultdict
  4. from odoo import api, models, registry
  5. NO_BI_MODELS = [
  6. 'fetchmail.server'
  7. ]
  8. NO_BI_TTYPES = [
  9. 'many2many',
  10. 'one2many',
  11. 'html',
  12. 'binary',
  13. 'reference'
  14. ]
  15. def dict_for_field(field):
  16. return {
  17. 'id': field.id,
  18. 'name': field.name,
  19. 'description': field.field_description,
  20. 'type': field.ttype,
  21. 'relation': field.relation,
  22. 'custom': False,
  23. 'model_id': field.model_id.id,
  24. 'model': field.model_id.model,
  25. 'model_name': field.model_id.name
  26. }
  27. def dict_for_model(model):
  28. return {
  29. 'id': model.id,
  30. 'name': model.name,
  31. 'model': model.model
  32. }
  33. class IrModel(models.Model):
  34. _inherit = 'ir.model'
  35. @api.model
  36. def _filter_bi_models(self, model):
  37. def _check_name(model_model):
  38. if model_model in NO_BI_MODELS:
  39. return 1
  40. return 0
  41. def _check_startswith(model_model):
  42. if model_model.startswith('workflow') or \
  43. model_model.startswith('ir.') or \
  44. model_model.startswith('base_'):
  45. return 1
  46. return 0
  47. def _check_contains(model_model):
  48. if 'mail' in model_model or \
  49. 'report' in model_model or \
  50. 'edi.' in model_model:
  51. return 1
  52. return 0
  53. def _check_unknown(model_name):
  54. if model_name == 'Unknown' or '.' in model_name:
  55. return 1
  56. return 0
  57. model_model = model['model']
  58. model_name = model['name']
  59. count_check = 0
  60. count_check += _check_name(model_model)
  61. count_check += _check_startswith(model_model)
  62. count_check += _check_contains(model_model)
  63. count_check += _check_unknown(model_name)
  64. if not count_check:
  65. return self.env['ir.model.access'].check(
  66. model['model'], 'read', False)
  67. return False
  68. def get_model_list(self, model_table_map):
  69. if not model_table_map:
  70. return []
  71. domain = [('model_id', 'in', list(model_table_map.keys())),
  72. ('store', '=', True),
  73. ('ttype', '=', 'many2one')]
  74. fields = self.env['ir.model.fields'].sudo().search(domain)
  75. model_list = []
  76. for field in fields:
  77. for table_alias in model_table_map[field.model_id.id]:
  78. model_list.append(dict(
  79. dict_for_field(field),
  80. table_alias=table_alias,
  81. join_node=-1,
  82. ))
  83. return model_list
  84. def get_relation_list(self, model_table_map):
  85. if not model_table_map:
  86. return []
  87. model_names = {}
  88. for model in self.sudo().browse(model_table_map.keys()):
  89. model_names.update({model.model: model.id})
  90. domain = [('relation', 'in', list(model_names.keys())),
  91. ('store', '=', True),
  92. ('ttype', '=', 'many2one')]
  93. fields = self.env['ir.model.fields'].sudo().search(domain)
  94. relation_list = []
  95. for field in fields:
  96. model_id = model_names[field.relation]
  97. for join_node in model_table_map[model_id]:
  98. relation_list.append(dict(
  99. dict_for_field(field),
  100. join_node=join_node,
  101. table_alias=-1
  102. ))
  103. return relation_list
  104. @api.model
  105. def _get_related_models_domain(self, model_table_map):
  106. domain = [('transient', '=', False)]
  107. if model_table_map:
  108. model_list = self.get_model_list(model_table_map)
  109. relation_list = self.get_relation_list(model_table_map)
  110. model_ids = [f['model_id'] for f in relation_list + model_list]
  111. model_ids += list(model_table_map.keys())
  112. relations = [f['relation'] for f in model_list]
  113. domain += [
  114. '|', ('id', 'in', model_ids), ('model', 'in', relations)]
  115. return domain
  116. @api.model
  117. def get_related_models(self, model_table_map):
  118. """ Return list of model dicts for all models that can be
  119. joined with the already selected models.
  120. """
  121. domain = self._get_related_models_domain(model_table_map)
  122. return self.sudo().search(domain, order='name asc')
  123. @api.model
  124. def get_models(self, table_model_map=None):
  125. """ Return list of model dicts for all available models.
  126. """
  127. self = self.with_context(lang=self.env.user.lang)
  128. model_table_map = defaultdict(list)
  129. for k, v in (table_model_map or {}).items():
  130. model_table_map[v].append(k)
  131. models = self.get_related_models(model_table_map)
  132. # filter out abstract models (they do not have DB tables)
  133. non_abstract_models = self.env.registry.models.keys()
  134. models = models.filtered(lambda m: m.model in non_abstract_models)
  135. return list(map(dict_for_model, models))
  136. @api.model
  137. def get_join_nodes(self, field_data, new_field):
  138. """ Return list of field dicts of join nodes
  139. Return all possible join nodes to add new_field to the query
  140. containing model_ids.
  141. """
  142. def remove_duplicate_nodes(join_nodes):
  143. seen = set()
  144. nodes_list = []
  145. for node in join_nodes:
  146. node_tuple = tuple(node.items())
  147. if node_tuple not in seen:
  148. seen.add(node_tuple)
  149. nodes_list.append(node)
  150. return nodes_list
  151. self = self.with_context(lang=self.env.user.lang)
  152. keys = []
  153. model_table_map = defaultdict(list)
  154. for field in field_data:
  155. model_table_map[field['model_id']].append(field['table_alias'])
  156. if field.get('join_node', -1) != -1:
  157. keys.append((field['table_alias'], field['id']))
  158. # nodes in current model
  159. existing_aliases = model_table_map[new_field['model_id']]
  160. join_nodes = [{'table_alias': alias} for alias in existing_aliases]
  161. # nodes in past selected models
  162. for field in self.get_model_list(model_table_map):
  163. if new_field['model'] == field['relation']:
  164. if (field['table_alias'], field['id']) not in keys:
  165. join_nodes.append(field)
  166. # nodes in new model
  167. for field in self.get_relation_list(model_table_map):
  168. if new_field['model_id'] == field['model_id']:
  169. if (field['table_alias'], field['id']) not in keys:
  170. join_nodes.append(field)
  171. return remove_duplicate_nodes(join_nodes)
  172. @api.model
  173. def get_fields(self, model_id):
  174. self = self.with_context(lang=self.env.user.lang)
  175. fields = self.env['ir.model.fields'].sudo().search([
  176. ('model_id', '=', model_id),
  177. ('store', '=', True),
  178. ('name', 'not in', models.MAGIC_COLUMNS),
  179. ('ttype', 'not in', NO_BI_TTYPES)
  180. ], order='field_description desc')
  181. fields_dict = list(map(dict_for_field, fields))
  182. return fields_dict
  183. @api.model
  184. def create(self, vals):
  185. if self.env.context and self.env.context.get('bve'):
  186. vals['state'] = 'base'
  187. res = super().create(vals)
  188. # this sql update is necessary since a write method here would
  189. # be not working (an orm constraint is restricting the modification
  190. # of the state field while updating ir.model)
  191. q = "UPDATE ir_model SET state = 'manual' WHERE id = %s"
  192. self.env.cr.execute(q, (res.id, ))
  193. # # update registry
  194. if self.env.context.get('bve'):
  195. # setup models; this reloads custom models in registry
  196. self.pool.setup_models(self._cr)
  197. # signal that registry has changed
  198. registry(self.env.cr.dbname).signal_changes()
  199. return res