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.

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