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.

236 lines
8.0 KiB

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