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.

172 lines
6.7 KiB

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