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.

184 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. ###################################################################################
  22. import functools
  23. import json
  24. import operator
  25. from odoo import api, fields, models
  26. from odoo.osv import expression
  27. class Hierarchy(models.AbstractModel):
  28. _name = "muk_utils.mixins.hierarchy"
  29. _description = "Hierarchy Mixin"
  30. _parent_store = True
  31. _parent_path_sudo = False
  32. _parent_path_store = False
  33. _name_path_context = "show_path"
  34. # ----------------------------------------------------------
  35. # Database
  36. # ----------------------------------------------------------
  37. parent_path = fields.Char(string="Parent Path", index=True)
  38. @api.model
  39. def _add_magic_fields(self):
  40. super(Hierarchy, self)._add_magic_fields()
  41. def add(name, field):
  42. if name not in self._fields:
  43. self._add_field(name, field)
  44. path_names_search = None
  45. if not self._parent_path_store:
  46. path_names_search = "_search_parent_path_names"
  47. add(
  48. "parent_path_names",
  49. fields.Char(
  50. _module=self._module,
  51. compute="_compute_parent_paths",
  52. compute_sudo=self._parent_path_sudo,
  53. store=self._parent_path_store,
  54. search=path_names_search,
  55. string="Path Names",
  56. readonly=True,
  57. automatic=True,
  58. ),
  59. )
  60. add(
  61. "parent_path_json",
  62. fields.Text(
  63. _module=self._module,
  64. compute="_compute_parent_paths",
  65. compute_sudo=self._parent_path_sudo,
  66. store=self._parent_path_store,
  67. string="Path Json",
  68. readonly=True,
  69. automatic=True,
  70. ),
  71. )
  72. # ----------------------------------------------------------
  73. # Helper
  74. # ----------------------------------------------------------
  75. @api.model
  76. def _get_depends_parent_paths(self):
  77. depends = ["parent_path"]
  78. if self._rec_name:
  79. depends += [self._rec_name]
  80. elif "name" in self._fields:
  81. depends += ["name"]
  82. elif "x_name" in self._fields:
  83. depends += ["x_name"]
  84. return depends
  85. # ----------------------------------------------------------
  86. # Search
  87. # ----------------------------------------------------------
  88. @api.model
  89. def _search_parent_path_names(self, operator, operand):
  90. domain = []
  91. for value in operand.split("/"):
  92. args = [(self._rec_name_fallback(), operator, value)]
  93. domain = expression.OR([args, domain]) if domain else args
  94. return domain if domain else [(self._rec_name_fallback(), operator, "")]
  95. # ----------------------------------------------------------
  96. # Read, View
  97. # ----------------------------------------------------------
  98. @api.depends(lambda self: self._get_depends_parent_paths())
  99. def _compute_parent_paths(self):
  100. records = self.filtered("parent_path")
  101. records_without_parent_path = self - records
  102. paths = [list(map(int, rec.parent_path.split("/")[:-1])) for rec in records]
  103. ids = paths and set(functools.reduce(operator.concat, paths)) or []
  104. model_without_path = self.with_context(**{self._name_path_context: False})
  105. filtered_records = model_without_path.browse(ids)._filter_access("read")
  106. data = dict(filtered_records.name_get())
  107. for record in records:
  108. path_names = [""]
  109. path_json = []
  110. for id in reversed(list(map(int, record.parent_path.split("/")[:-1]))):
  111. if id not in data:
  112. break
  113. path_names.append(data[id])
  114. path_json.append({"model": record._name, "name": data[id], "id": id})
  115. path_names.reverse()
  116. path_json.reverse()
  117. record.update(
  118. {
  119. "parent_path_names": "/".join(path_names),
  120. "parent_path_json": json.dumps(path_json),
  121. }
  122. )
  123. records_without_parent_path.update(
  124. {"parent_path_names": False, "parent_path_json": False}
  125. )
  126. @api.model
  127. def _name_search(
  128. self, name="", args=None, operator="ilike", limit=100, name_get_uid=None
  129. ):
  130. domain = list(args or [])
  131. if not (name == "" and operator == "ilike"):
  132. if "/" in name:
  133. domain += [("parent_path_names", operator, name)]
  134. else:
  135. domain += [(self._rec_name, operator, name)]
  136. records = self.browse(
  137. self._search(domain, limit=limit, access_rights_uid=name_get_uid)
  138. )
  139. return models.lazy_name_get(records.with_user(name_get_uid or self.env.uid))
  140. def name_get(self):
  141. if self.env.context.get(self._name_path_context):
  142. res = []
  143. for record in self:
  144. names = record.parent_path_names
  145. if not names:
  146. res.append(super(Hierarchy, record).name_get()[0])
  147. elif not len(names) > 50:
  148. res.append((record.id, names))
  149. else:
  150. res.append((record.id, ".." + names[-48:]))
  151. return res
  152. return super(Hierarchy, self).name_get()
  153. # ----------------------------------------------------------
  154. # Create, Update, Delete
  155. # ----------------------------------------------------------
  156. def write(self, vals):
  157. res = super(Hierarchy, self).write(vals)
  158. if self._parent_path_store and self._rec_name_fallback() in vals:
  159. domain = [("id", "child_of", self.ids)]
  160. records = self.sudo().search(domain)
  161. records.modified(["parent_path"])
  162. return res