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.

142 lines
5.4 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. #----------------------------------------------------------
  32. # Database
  33. #----------------------------------------------------------
  34. parent_path = fields.Char(
  35. string="Parent Path",
  36. 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('parent_path_names', fields.Char(
  47. _module=self._module,
  48. compute='_compute_parent_paths',
  49. compute_sudo=self._parent_path_sudo,
  50. store=self._parent_path_store,
  51. search=path_names_search,
  52. string="Path Names",
  53. readonly=True,
  54. automatic=True))
  55. add('parent_path_json', fields.Text(
  56. _module=self._module,
  57. compute='_compute_parent_paths',
  58. compute_sudo=self._parent_path_sudo,
  59. store=self._parent_path_store,
  60. string="Path Json",
  61. readonly=True,
  62. automatic=True))
  63. #----------------------------------------------------------
  64. # Helper
  65. #----------------------------------------------------------
  66. def _get_depends_parent_paths(self):
  67. depends = ['parent_path']
  68. if self._rec_name:
  69. depends += [self._rec_name]
  70. elif 'name' in self._fields:
  71. depends += ['name']
  72. elif 'x_name' in self._fields:
  73. depends += ['x_name']
  74. return depends
  75. #----------------------------------------------------------
  76. # Search
  77. #----------------------------------------------------------
  78. @api.model
  79. def _search_parent_path_names(self, operator, operand):
  80. domain = []
  81. for value in operand.split('/'):
  82. args = [(self._rec_name_fallback(), operator, value)]
  83. domain = expression.OR([args, domain]) if domain else args
  84. return domain if domain else [(self._rec_name_fallback(), operator, "")]
  85. #----------------------------------------------------------
  86. # Read, View
  87. #----------------------------------------------------------
  88. @api.depends(lambda self: self._get_depends_parent_paths())
  89. def _compute_parent_paths(self):
  90. records = self.filtered(lambda record: record.parent_path)
  91. paths = [list(map(int, rec.parent_path.split('/')[:-1])) for rec in records]
  92. ids = paths and set(functools.reduce(operator.concat, paths)) or []
  93. data = dict(self.browse(ids)._filter_access('read').name_get())
  94. for record in records:
  95. path_names = [""]
  96. path_json = []
  97. for id in reversed(list(map(int, record.parent_path.split('/')[:-1]))):
  98. if id not in data:
  99. break
  100. path_names.append(data[id])
  101. path_json.append({
  102. 'model': record._name,
  103. 'name': data[id],
  104. 'id': id,
  105. })
  106. path_names.reverse()
  107. path_json.reverse()
  108. record.update({
  109. 'parent_path_names': '/'.join(path_names),
  110. 'parent_path_json': json.dumps(path_json),
  111. })
  112. #----------------------------------------------------------
  113. # Create, Update, Delete
  114. #----------------------------------------------------------
  115. @api.multi
  116. def write(self, vals):
  117. if self._parent_path_store and self._rec_name_fallback() in vals:
  118. with self.env.norecompute():
  119. res = super(Hierarchy, self).write(vals)
  120. domain = [('id', 'child_of', self.ids)]
  121. records = self.sudo().search(domain)
  122. records.modified(['parent_path'])
  123. if self.env.recompute and self.env.context.get('recompute', True):
  124. records.recompute()
  125. return res
  126. return super(Hierarchy, self).write(vals)