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.

211 lines
9.3 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 logging
  23. from odoo import api, models
  24. from odoo.addons.muk_utils.tools import utils
  25. from odoo.osv import expression
  26. _logger = logging.getLogger(__name__)
  27. class Base(models.AbstractModel):
  28. _inherit = "base"
  29. # ----------------------------------------------------------
  30. # Helper Methods
  31. # ----------------------------------------------------------
  32. @api.model
  33. def _check_parent_field(self):
  34. if self._parent_name not in self._fields:
  35. raise TypeError(
  36. "The parent ({}) field does not exist.".format(self._parent_name)
  37. )
  38. @api.model
  39. def _build_search_childs_domain(self, parent_id, domain=[]):
  40. self._check_parent_field()
  41. parent_domain = [[self._parent_name, "=", parent_id]]
  42. return expression.AND([parent_domain, domain]) if domain else parent_domain
  43. @api.model
  44. def _check_context_bin_size(self, field):
  45. return any(
  46. key in self.env.context for key in ["bin_size", "bin_size_{}".format(field)]
  47. )
  48. # ----------------------------------------------------------
  49. # Security
  50. # ----------------------------------------------------------
  51. def _filter_access(self, operation, in_memory=True):
  52. if self.check_access_rights(operation, False):
  53. if in_memory:
  54. return self._filter_access_rules_python(operation)
  55. else:
  56. return self._filter_access_rules(operation)
  57. return self.env[self._name]
  58. def _filter_access_ids(self, operation, in_memory=True):
  59. return self._filter_access(operation, in_memory=in_memory).ids
  60. def check_access(self, operation, raise_exception=False):
  61. """ Verifies that the operation given by ``operation`` is allowed for
  62. the current user according to the access level.
  63. :param operation: one of ``read``, ``create``, ``write``, ``unlink``
  64. :raise AccessError: * if current level of access do not permit this operation.
  65. :return: True if the operation is allowed
  66. """
  67. try:
  68. access_right = self.check_access_rights(operation, raise_exception)
  69. access_rule = self.check_access_rule(operation) is None
  70. return access_right and access_rule
  71. except AccessError:
  72. if raise_exception:
  73. raise
  74. return False
  75. # ----------------------------------------------------------
  76. # Hierarchy Methods
  77. # ----------------------------------------------------------
  78. @api.model
  79. def search_parents(self, domain=[], offset=0, limit=None, order=None, count=False):
  80. """ This method finds the top level elements of the hierarchy for a given search query.
  81. :param domain: a search domain <reference/orm/domains> (default: empty list)
  82. :param order: a string to define the sort order of the query (default: none)
  83. :returns: the top level elements for the given search query
  84. """
  85. res = self._search_parents(
  86. domain=domain, offset=offset, limit=limit, order=order, count=count
  87. )
  88. return res if count else self.browse(res)
  89. @api.model
  90. def search_read_parents(
  91. self, domain=[], fields=None, offset=0, limit=None, order=None
  92. ):
  93. """ This method finds the top level elements of the hierarchy for a given search query.
  94. :param domain: a search domain <reference/orm/domains> (default: empty list)
  95. :param fields: a list of fields to read (default: all fields of the model)
  96. :param order: a string to define the sort order of the query (default: none)
  97. :returns: the top level elements for the given search query
  98. """
  99. records = self.search_parents(
  100. domain=domain, offset=offset, limit=limit, order=order
  101. )
  102. if not records:
  103. return []
  104. if fields and fields == ["id"]:
  105. return [{"id": record.id} for record in records]
  106. result = records.read(fields)
  107. if len(result) <= 1:
  108. return result
  109. index = {vals["id"]: vals for vals in result}
  110. return [index[record.id] for record in records if record.id in index]
  111. @api.model
  112. def _search_parents(self, domain=[], offset=0, limit=None, order=None, count=False):
  113. self._check_parent_field()
  114. self.check_access_rights("read")
  115. if expression.is_false(self, domain):
  116. return 0 if count else []
  117. self._flush_search(domain, fields=[self._parent_name], order=order)
  118. query = self._where_calc(domain)
  119. self._apply_ir_rules(query, "read")
  120. from_clause, where_clause, where_clause_arguments = query.get_sql()
  121. parent_where = where_clause and (" WHERE {}".format(where_clause)) or ""
  122. parent_query = (
  123. 'SELECT "{}".id FROM '.format(self._table) + from_clause + parent_where
  124. )
  125. no_parent_clause = '"{table}"."{field}" IS NULL'.format(
  126. table=self._table, field=self._parent_name
  127. )
  128. no_access_clause = '"{table}"."{field}" NOT IN ({query})'.format(
  129. table=self._table, field=self._parent_name, query=parent_query
  130. )
  131. parent_clause = "({} OR {})".format(no_parent_clause, no_access_clause)
  132. order_by = self._generate_order_by(order, query)
  133. from_clause, where_clause, where_clause_params = query.get_sql()
  134. where_str = (
  135. where_clause
  136. and (" WHERE {} AND {}".format(where_clause, parent_clause))
  137. or (" WHERE {}".format(parent_clause))
  138. )
  139. if count:
  140. query_str = "SELECT count(1) FROM " + from_clause + where_str
  141. self.env.cr.execute(query_str, where_clause_params + where_clause_arguments)
  142. return self.env.cr.fetchone()[0]
  143. limit_str = limit and " limit %d" % limit or ""
  144. offset_str = offset and " offset %d" % offset or ""
  145. query_str = (
  146. 'SELECT "{}".id FROM '.format(self._table)
  147. + from_clause
  148. + where_str
  149. + order_by
  150. + limit_str
  151. + offset_str
  152. )
  153. self.env.cr.execute(query_str, where_clause_params + where_clause_arguments)
  154. return utils.uniquify_list([x[0] for x in self.env.cr.fetchall()])
  155. @api.model
  156. def search_childs(
  157. self, parent_id, domain=[], offset=0, limit=None, order=None, count=False
  158. ):
  159. """ This method finds the direct child elements of the parent record for a given search query.
  160. :param parent_id: the integer representing the ID of the parent record
  161. :param domain: a search domain <reference/orm/domains> (default: empty list)
  162. :param offset: the number of results to ignore (default: none)
  163. :param limit: maximum number of records to return (default: all)
  164. :param order: a string to define the sort order of the query (default: none)
  165. :param count: counts and returns the number of matching records (default: False)
  166. :returns: the top level elements for the given search query
  167. """
  168. domain = self._build_search_childs_domain(parent_id, domain=domain)
  169. return self.search(domain, offset=offset, limit=limit, order=order, count=count)
  170. @api.model
  171. def search_read_childs(
  172. self, parent_id, domain=[], fields=None, offset=0, limit=None, order=None
  173. ):
  174. """ This method finds the direct child elements of the parent record for a given search query.
  175. :param parent_id: the integer representing the ID of the parent record
  176. :param domain: a search domain <reference/orm/domains> (default: empty list)
  177. :param fields: a list of fields to read (default: all fields of the model)
  178. :param offset: the number of results to ignore (default: none)
  179. :param limit: maximum number of records to return (default: all)
  180. :param order: a string to define the sort order of the query (default: none)
  181. :returns: the top level elements for the given search query
  182. """
  183. domain = self._build_search_childs_domain(parent_id, domain=domain)
  184. return self.search_read(
  185. domain=domain, fields=fields, offset=offset, limit=limit, order=order
  186. )