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.

295 lines
9.6 KiB

  1. # -*- coding: utf-8 -*-
  2. # Copyright 2015-2017 Onestein (<http://www.onestein.eu>)
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  4. from odoo import api, models
  5. from odoo.modules.registry import RegistryManager
  6. NO_BI_MODELS = [
  7. 'temp.range',
  8. 'account.statement.operation.template',
  9. 'fetchmail.server'
  10. ]
  11. NO_BI_FIELDS = [
  12. 'id',
  13. 'create_uid',
  14. 'create_date',
  15. 'write_uid',
  16. 'write_date'
  17. ]
  18. NO_BI_TTYPES = [
  19. 'many2many',
  20. 'one2many',
  21. 'html',
  22. 'binary',
  23. 'reference'
  24. ]
  25. def dict_for_field(field):
  26. return {
  27. 'id': field.id,
  28. 'name': field.name,
  29. 'description': field.field_description,
  30. 'type': field.ttype,
  31. 'relation': field.relation,
  32. 'custom': False,
  33. 'model_id': field.model_id.id,
  34. 'model': field.model_id.model,
  35. 'model_name': field.model_id.name
  36. }
  37. def dict_for_model(model):
  38. return {
  39. 'id': model.id,
  40. 'name': model.name,
  41. 'model': model.model
  42. }
  43. class IrModel(models.Model):
  44. _inherit = 'ir.model'
  45. @api.model
  46. def _filter_bi_models(self, model):
  47. def _check_name(model_model):
  48. if model_model in NO_BI_MODELS:
  49. return 1
  50. return 0
  51. def _check_startswith(model_model):
  52. if model_model.startswith('workflow') or \
  53. model_model.startswith('ir.') or \
  54. model_model.startswith('base_'):
  55. return 1
  56. return 0
  57. def _check_contains(model_model):
  58. if 'mail' in model_model or \
  59. '_' in model_model or \
  60. 'report' in model_model or \
  61. 'edi.' in model_model:
  62. return 1
  63. return 0
  64. def _check_unknow(model_name):
  65. if model_name == 'Unknow' or '.' in model_name:
  66. return 1
  67. return 0
  68. model_model = model['model']
  69. model_name = model['name']
  70. count_check = 0
  71. count_check += _check_name(model_model)
  72. count_check += _check_startswith(model_model)
  73. count_check += _check_contains(model_model)
  74. count_check += _check_unknow(model_name)
  75. if not count_check:
  76. return self.env['ir.model.access'].check(
  77. model['model'], 'read', False)
  78. return False
  79. @api.model
  80. def sort_filter_models(self, models_list):
  81. res = sorted(
  82. filter(self._filter_bi_models, models_list),
  83. key=lambda x: x['name'])
  84. return res
  85. @api.model
  86. def _search_fields(self, domain):
  87. Fields = self.env['ir.model.fields']
  88. fields = Fields.sudo().search(domain)
  89. return fields
  90. @api.model
  91. def get_related_fields(self, model_ids):
  92. """ Return list of field dicts for all fields that can be
  93. joined with models in model_ids
  94. """
  95. def get_model_list(model_ids):
  96. model_list = []
  97. domain = [('model_id', 'in', model_ids.values()),
  98. ('store', '=', True),
  99. ('ttype', 'in', ['many2one'])]
  100. filtered_fields = self._search_fields(domain)
  101. for model in model_ids.items():
  102. for field in filtered_fields:
  103. if model[1] == field.model_id.id:
  104. model_list.append(
  105. dict(dict_for_field(field),
  106. join_node=-1,
  107. table_alias=model[0])
  108. )
  109. return model_list
  110. def get_relation_list(model_ids, model_names):
  111. relation_list = []
  112. domain = [('relation', 'in', model_names.values()),
  113. ('store', '=', True),
  114. ('ttype', 'in', ['many2one'])]
  115. filtered_fields = self._search_fields(domain)
  116. for model in model_ids.items():
  117. for field in filtered_fields:
  118. if model_names[model[1]] == field['relation']:
  119. relation_list.append(
  120. dict(dict_for_field(field),
  121. join_node=model[0],
  122. table_alias=-1)
  123. )
  124. return relation_list
  125. models = self.sudo().browse(model_ids.values())
  126. model_names = {}
  127. for model in models:
  128. model_names.update({model.id: model.model})
  129. model_list = get_model_list(model_ids)
  130. relation_list = get_relation_list(model_ids, model_names)
  131. return relation_list + model_list
  132. @api.model
  133. def get_related_models(self, model_ids):
  134. """ Return list of model dicts for all models that can be
  135. joined with the already selected models.
  136. """
  137. def _get_field(fields, orig, target):
  138. field_list = []
  139. for f in fields:
  140. if f[orig] == -1:
  141. field_list.append(f[target])
  142. return field_list
  143. def _get_list_id(model_ids, fields):
  144. list_model = model_ids.values()
  145. list_model += _get_field(fields, 'table_alias', 'model_id')
  146. return list_model
  147. def _get_list_relation(fields):
  148. list_model = _get_field(fields, 'join_node', 'relation')
  149. return list_model
  150. models_list = []
  151. related_fields = self.get_related_fields(model_ids)
  152. list_id = _get_list_id(model_ids, related_fields)
  153. list_model = _get_list_relation(related_fields)
  154. domain = ['|',
  155. ('id', 'in', list_id),
  156. ('model', 'in', list_model)]
  157. for model in self.sudo().search(domain):
  158. models_list.append(dict_for_model(model))
  159. return self.sort_filter_models(models_list)
  160. @api.model
  161. def get_models(self):
  162. """ Return list of model dicts for all available models.
  163. """
  164. models_list = []
  165. for model in self.search([('transient', '=', False)]):
  166. models_list.append(dict_for_model(model))
  167. return self.sort_filter_models(models_list)
  168. @api.model
  169. def get_join_nodes(self, field_data, new_field):
  170. """ Return list of field dicts of join nodes
  171. Return all possible join nodes to add new_field to the query
  172. containing model_ids.
  173. """
  174. def _get_model_ids(field_data):
  175. model_ids = dict([(field['table_alias'],
  176. field['model_id']) for field in field_data])
  177. return model_ids
  178. def _get_join_nodes_dict(model_ids, new_field):
  179. join_nodes = []
  180. for alias, model_id in model_ids.items():
  181. if model_id == new_field['model_id']:
  182. join_nodes.append({'table_alias': alias})
  183. for field in self.get_related_fields(model_ids):
  184. c = [field['join_node'] == -1, field['table_alias'] == -1]
  185. a = (new_field['model'] == field['relation'])
  186. b = (new_field['model_id'] == field['model_id'])
  187. if (a and c[0]) or (b and c[1]):
  188. join_nodes.append(field)
  189. return join_nodes
  190. def remove_duplicate_nodes(join_nodes):
  191. seen = set()
  192. nodes_list = []
  193. for node in join_nodes:
  194. node_tuple = tuple(node.items())
  195. if node_tuple not in seen:
  196. seen.add(node_tuple)
  197. nodes_list.append(node)
  198. return nodes_list
  199. model_ids = _get_model_ids(field_data)
  200. keys = [(field['table_alias'], field['id'])
  201. for field in field_data if field.get('join_node', -1) != -1]
  202. join_nodes = _get_join_nodes_dict(model_ids, new_field)
  203. join_nodes = remove_duplicate_nodes(join_nodes)
  204. return filter(
  205. lambda x: 'id' not in x or
  206. (x['table_alias'], x['id']) not in keys, join_nodes)
  207. @api.model
  208. def get_fields(self, model_id):
  209. domain = [
  210. ('model_id', '=', model_id),
  211. ('store', '=', True),
  212. ('name', 'not in', NO_BI_FIELDS),
  213. ('ttype', 'not in', NO_BI_TTYPES)
  214. ]
  215. fields_dict = []
  216. filtered_fields = self._search_fields(domain)
  217. for field in filtered_fields:
  218. fields_dict.append(
  219. {'id': field.id,
  220. 'model_id': model_id,
  221. 'name': field.name,
  222. 'description': field.field_description,
  223. 'type': field.ttype,
  224. 'custom': False,
  225. 'model': field.model_id.model,
  226. 'model_name': field.model_id.name
  227. }
  228. )
  229. sorted_fields = sorted(
  230. fields_dict,
  231. key=lambda x: x['description'],
  232. reverse=True
  233. )
  234. return sorted_fields
  235. @api.model
  236. def create(self, vals):
  237. if self._context and self._context.get('bve'):
  238. vals['state'] = 'base'
  239. res = super(IrModel, self).create(vals)
  240. # this sql update is necessary since a write method here would
  241. # be not working (an orm constraint is restricting the modification
  242. # of the state field while updating ir.model)
  243. q = ("""UPDATE ir_model SET state = 'manual'
  244. WHERE id = """ + str(res.id))
  245. self.env.cr.execute(q)
  246. # # update registry
  247. if self._context.get('bve'):
  248. # setup models; this reloads custom models in registry
  249. self.pool.setup_models(self._cr, partial=(not self.pool.ready))
  250. # signal that registry has changed
  251. RegistryManager.signal_registry_change(self.env.cr.dbname)
  252. return res