Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IMP] base: Speed-up address_get method #165010

Open
wants to merge 2 commits into
base: 15.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
64 changes: 51 additions & 13 deletions odoo/addons/base/models/res_partner.py
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,51 @@ def _email_send(self, email_from, subject, body, on_error=None):
warnings.warn("Partner._email_send has not done anything but raise errors since 15.0", stacklevel=2, category=DeprecationWarning)
return True

def _address_get_type_id_children(self, adr_pref, visited):
"""Scan all the descendants address filtered by type

It is using yield (self.type, self.id) in order to process this soft data
and avoid running an extra query if it is not needed
"""
self.ensure_one()

yield self.type, self.id

self.check_access_rights("read")
self.flush(["active", "display_name", "is_company", "name", "parent_id", "partner_share", "type"])
query = """WITH RECURSIVE descendants AS (
SELECT *, LPAD(id::TEXT, 10, '0') AS path
FROM res_partner AS parent
WHERE id = %%(partner_id)s

UNION ALL

SELECT res_partner.*, d.path || '>' || LPAD(res_partner.id::TEXT, 10, '0') AS path
FROM res_partner
JOIN descendants d
ON res_partner.parent_id = d.id
WHERE (res_partner.is_company IS FALSE OR res_partner.is_company IS NULL)
AND res_partner.id NOT IN %%(visited)s
AND %(query_rule)s
)
SELECT DISTINCT ON (type)
type, id
FROM descendants
WHERE type IN %%(adr_pref)s
ORDER BY type, path, %(order)s
"""
query_obj = self._where_calc([])
self._apply_ir_rules(query_obj)
where_clause, where_clause_params = query_obj.get_sql()[1:]
where_clause = where_clause or "TRUE"
query_rule = self.env.cr.mogrify(where_clause, where_clause_params).decode("UTF-8")
self.env.cr.execute(
query % {"order": self._order or "id", "query_rule": query_rule},
{"partner_id": self.id, "adr_pref": tuple(adr_pref), "visited": tuple(visited or [0])},
)
partner_types_ids = self.env.cr.fetchall()
yield from partner_types_ids

def address_get(self, adr_pref=None):
""" Find contacts/addresses of the right type(s) by doing a depth-first-search
through descendants within company boundaries (stop at entities flagged ``is_company``)
Expand All @@ -970,19 +1015,12 @@ def address_get(self, adr_pref=None):
for partner in self:
current_partner = partner
while current_partner:
to_scan = [current_partner]
# Scan descendants, DFS
while to_scan:
record = to_scan.pop(0)
visited.add(record)
if record.type in adr_pref and not result.get(record.type):
result[record.type] = record.id
if len(result) == len(adr_pref):
return result
to_scan = [c for c in record.child_ids
if c not in visited
if not c.is_company] + to_scan

for partner_type, partner_id in current_partner._address_get_type_id_children(adr_pref-set(result), visited):
if partner_type in adr_pref and not result.get(partner_type):
result[partner_type] = partner_id
if len(result) == len(adr_pref):
return result
visited.add(current_partner.id)
# Continue scanning at ancestor if current_partner is not a commercial entity
if current_partner.is_company or not current_partner.parent_id:
break
Expand Down
1 change: 1 addition & 0 deletions odoo/addons/base/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,4 @@
from . import test_pdf
from . import test_config_parameter
from . import test_ir_module_category
from . import test_base_address_get_demo