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.

183 lines
6.8 KiB

  1. ###################################################################################
  2. #
  3. # Copyright (c) 2017-2019 MuK IT GmbH.
  4. #
  5. # This file is part of MuK Utils
  6. # (see https://mukit.at).
  7. #
  8. # This program is free software: you can redistribute it and/or modify
  9. # it under the terms of the GNU Lesser General Public License as published by
  10. # the Free Software Foundation, either version 3 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU Lesser General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU Lesser General Public License
  19. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. ###################################################################################
  21. import functools
  22. import json
  23. import operator
  24. from odoo import api, fields, models
  25. from odoo.osv import expression
  26. class Hierarchy(models.AbstractModel):
  27. _name = "muk_utils.mixins.hierarchy"
  28. _description = "Hierarchy Mixin"
  29. _parent_store = True
  30. _parent_path_sudo = False
  31. _parent_path_store = False
  32. _name_path_context = "show_path"
  33. # ----------------------------------------------------------
  34. # Database
  35. # ----------------------------------------------------------
  36. parent_path = fields.Char(string="Parent Path", index=True)
  37. @api.model
  38. def _add_magic_fields(self):
  39. super(Hierarchy, self)._add_magic_fields()
  40. def add(name, field):
  41. if name not in self._fields:
  42. self._add_field(name, field)
  43. path_names_search = None
  44. if not self._parent_path_store:
  45. path_names_search = "_search_parent_path_names"
  46. add(
  47. "parent_path_names",
  48. fields.Char(
  49. _module=self._module,
  50. compute="_compute_parent_paths",
  51. compute_sudo=self._parent_path_sudo,
  52. store=self._parent_path_store,
  53. search=path_names_search,
  54. string="Path Names",
  55. readonly=True,
  56. automatic=True,
  57. ),
  58. )
  59. add(
  60. "parent_path_json",
  61. fields.Text(
  62. _module=self._module,
  63. compute="_compute_parent_paths",
  64. compute_sudo=self._parent_path_sudo,
  65. store=self._parent_path_store,
  66. string="Path Json",
  67. readonly=True,
  68. automatic=True,
  69. ),
  70. )
  71. # ----------------------------------------------------------
  72. # Helper
  73. # ----------------------------------------------------------
  74. @api.model
  75. def _get_depends_parent_paths(self):
  76. depends = ["parent_path"]
  77. if self._rec_name:
  78. depends += [self._rec_name]
  79. elif "name" in self._fields:
  80. depends += ["name"]
  81. elif "x_name" in self._fields:
  82. depends += ["x_name"]
  83. return depends
  84. # ----------------------------------------------------------
  85. # Search
  86. # ----------------------------------------------------------
  87. @api.model
  88. def _search_parent_path_names(self, operator, operand):
  89. domain = []
  90. for value in operand.split("/"):
  91. args = [(self._rec_name_fallback(), operator, value)]
  92. domain = expression.OR([args, domain]) if domain else args
  93. return domain if domain else [(self._rec_name_fallback(), operator, "")]
  94. # ----------------------------------------------------------
  95. # Read, View
  96. # ----------------------------------------------------------
  97. @api.depends(lambda self: self._get_depends_parent_paths())
  98. def _compute_parent_paths(self):
  99. records = self.filtered("parent_path")
  100. records_without_parent_path = self - records
  101. paths = [list(map(int, rec.parent_path.split("/")[:-1])) for rec in records]
  102. ids = paths and set(functools.reduce(operator.concat, paths)) or []
  103. model_without_path = self.with_context(**{self._name_path_context: False})
  104. filtered_records = model_without_path.browse(ids)._filter_access("read")
  105. data = dict(filtered_records.name_get())
  106. for record in records:
  107. path_names = [""]
  108. path_json = []
  109. for id in reversed(list(map(int, record.parent_path.split("/")[:-1]))):
  110. if id not in data:
  111. break
  112. path_names.append(data[id])
  113. path_json.append({"model": record._name, "name": data[id], "id": id})
  114. path_names.reverse()
  115. path_json.reverse()
  116. record.update(
  117. {
  118. "parent_path_names": "/".join(path_names),
  119. "parent_path_json": json.dumps(path_json),
  120. }
  121. )
  122. records_without_parent_path.update(
  123. {"parent_path_names": False, "parent_path_json": False}
  124. )
  125. @api.model
  126. def _name_search(
  127. self, name="", args=None, operator="ilike", limit=100, name_get_uid=None
  128. ):
  129. domain = list(args or [])
  130. if not (name == "" and operator == "ilike"):
  131. if "/" in name:
  132. domain += [("parent_path_names", operator, name)]
  133. else:
  134. domain += [(self._rec_name, operator, name)]
  135. records = self.browse(
  136. self._search(domain, limit=limit, access_rights_uid=name_get_uid)
  137. )
  138. return models.lazy_name_get(records.with_user(name_get_uid or self.env.uid))
  139. def name_get(self):
  140. if self.env.context.get(self._name_path_context):
  141. res = []
  142. for record in self:
  143. names = record.parent_path_names
  144. if not names:
  145. res.append(super(Hierarchy, record).name_get()[0])
  146. elif not len(names) > 50:
  147. res.append((record.id, names))
  148. else:
  149. res.append((record.id, ".." + names[-48:]))
  150. return res
  151. return super(Hierarchy, self).name_get()
  152. # ----------------------------------------------------------
  153. # Create, Update, Delete
  154. # ----------------------------------------------------------
  155. def write(self, vals):
  156. res = super(Hierarchy, self).write(vals)
  157. if self._parent_path_store and self._rec_name_fallback() in vals:
  158. domain = [("id", "child_of", self.ids)]
  159. records = self.sudo().search(domain)
  160. records.modified(["parent_path"])
  161. return res