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.

291 lines
9.5 KiB

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