[IMP] product,website_sale: pricelist API cleanup

* query pricelist filters/domains directly for the correct company 
instead of relying on context/session/... content
* pass the record instead of the id to avoid useless browse calls
* also clearly highlight sudoed partner record

Part-of: odoo/odoo#82374
This commit is contained in:
Victor Feyens
2022-02-21 08:41:29 +00:00
parent 06f417012c
commit a73fb16da0
4 changed files with 64 additions and 56 deletions
+2 -2
View File
@@ -264,7 +264,7 @@ class Pricelist(models.Model):
# res.partner.property_product_pricelist field computation
@api.model
def _get_partner_pricelist_multi(self, partner_ids, company_id=None):
def _get_partner_pricelist_multi(self, partner_ids):
""" Retrieve the applicable pricelist for given partners in a given company.
It will return the first found pricelist in this order:
@@ -282,7 +282,7 @@ class Pricelist(models.Model):
# `partner_ids` might be ID from inactive users. We should use active_test
# as we will do a search() later (real case for website public user).
Partner = self.env['res.partner'].with_context(active_test=False)
company_id = company_id or self.env.company.id
company_id = self.env.company.id
Property = self.env['ir.property'].with_company(company_id)
Pricelist = self.env['product.pricelist']
+3 -4
View File
@@ -20,10 +20,9 @@ class ResPartner(models.Model):
@api.depends('country_id')
@api.depends_context('company')
def _compute_product_pricelist(self):
company = self.env.company.id
res = self.env['product.pricelist']._get_partner_pricelist_multi(self.ids, company_id=company)
for p in self:
p.property_product_pricelist = res.get(p.id)
res = self.env['product.pricelist']._get_partner_pricelist_multi(self.ids)
for partner in self:
partner.property_product_pricelist = res.get(partner.id)
def _inverse_product_pricelist(self):
for partner in self:
+11 -22
View File
@@ -58,17 +58,17 @@ class ProductPricelist(models.Model):
return res
def _get_partner_pricelist_multi_search_domain_hook(self, company_id):
domain = super(ProductPricelist, self)._get_partner_pricelist_multi_search_domain_hook(company_id)
domain = super()._get_partner_pricelist_multi_search_domain_hook(company_id)
website = ir_http.get_request_website()
if website:
domain += self._get_website_pricelists_domain(website.id)
domain += self._get_website_pricelists_domain(website)
return domain
def _get_partner_pricelist_multi_filter_hook(self):
res = super(ProductPricelist, self)._get_partner_pricelist_multi_filter_hook()
res = super()._get_partner_pricelist_multi_filter_hook()
website = ir_http.get_request_website()
if website:
res = res.filtered(lambda pl: pl._is_available_on_website(website.id))
res = res.filtered(lambda pl: pl._is_available_on_website(website))
return res
def _check_website_pricelist(self):
@@ -76,7 +76,7 @@ class ProductPricelist(models.Model):
if not website.pricelist_ids:
raise UserError(_("With this action, '%s' website would not have any pricelist available.") % (website.name))
def _is_available_on_website(self, website_id):
def _is_available_on_website(self, website):
""" To be able to be used on a website, a pricelist should either:
- Have its `website_id` set to current website (specific pricelist).
- Have no `website_id` set and should be `selectable` (generic pricelist)
@@ -89,33 +89,22 @@ class ProductPricelist(models.Model):
Change in this method should be reflected in `_get_website_pricelists_domain`.
"""
self.ensure_one()
if self.company_id and self.company_id != self.env["website"].browse(website_id).company_id:
if self.company_id and self.company_id != website.company_id:
return False
return self.website_id.id == website_id or (not self.website_id and (self.selectable or self.sudo().code))
return self.website_id.id == website.id or (not self.website_id and (self.selectable or self.sudo().code))
def _get_website_pricelists_domain(self, website_id):
def _get_website_pricelists_domain(self, website):
''' Check above `_is_available_on_website` for explanation.
Change in this method should be reflected in `_is_available_on_website`.
'''
company_id = self.env["website"].browse(website_id).company_id.id
return [
'&', ('company_id', 'in', [False, company_id]),
'|', ('website_id', '=', website_id),
('active', '=', True),
('company_id', 'in', [False, website.company_id.id]),
'|', ('website_id', '=', website.id),
'&', ('website_id', '=', False),
'|', ('selectable', '=', True), ('code', '!=', False),
]
def _get_partner_pricelist_multi(self, partner_ids, company_id=None):
''' If `property_product_pricelist` is read from website, we should use
the website's company and not the user's one.
Passing a `company_id` to super will avoid using the current user's
company.
'''
website = ir_http.get_request_website()
if not company_id and website:
company_id = website.company_id.id
return super(ProductPricelist, self)._get_partner_pricelist_multi(partner_ids, company_id)
@api.constrains('company_id', 'website_id')
def _check_websites_in_company(self):
'''Prevent misconfiguration multi-website/multi-companies.
+48 -28
View File
@@ -78,15 +78,16 @@ class Website(models.Model):
@api.depends('all_pricelist_ids')
def _compute_pricelist_ids(self):
Pricelist = self.env['product.pricelist']
for website in self:
website.pricelist_ids = Pricelist.search(
Pricelist._get_website_pricelists_domain(website.id)
website = website.with_company(website.company_id)
ProductPricelist = website.env['product.pricelist'] # with correct company in env
website.pricelist_ids = ProductPricelist.sudo().search(
ProductPricelist._get_website_pricelists_domain(website)
)
def _compute_pricelist_id(self):
for website in self:
website.pricelist_id = website.with_context(website_id=website.id).get_current_pricelist()
website.pricelist_id = website.get_current_pricelist()
# This method is cached, must not return records! See also #8795
@tools.ormcache('self.env.uid', 'country_code', 'show_visible', 'website_pl', 'current_pl', 'all_pl', 'partner_pl', 'order_pl')
@@ -125,7 +126,7 @@ class Website(models.Model):
if country_code:
for cgroup in self.env['res.country.group'].search([('country_ids.code', '=', country_code)]):
pricelists |= cgroup.pricelist_ids.filtered(
lambda pl: pl._is_available_on_website(self.id) and _check_show_visible(pl)
lambda pl: pl._is_available_on_website(self) and _check_show_visible(pl)
)
# no GeoIP or no pricelist for this country
@@ -136,7 +137,8 @@ class Website(models.Model):
is_public = self.user_id.id == self.env.user.id
if not is_public:
# keep partner_pl only if website compliant
partner_pl = pricelists.browse(partner_pl).filtered(lambda pl: pl._is_available_on_website(self.id) and _check_show_visible(pl))
partner_pl = pricelists.browse(partner_pl).filtered(
lambda pl: pl._is_available_on_website(self) and _check_show_visible(pl))
if country_code:
# keep partner_pl only if GeoIP compliant in case of GeoIP enabled
partner_pl = partner_pl.filtered(
@@ -161,16 +163,27 @@ class Website(models.Model):
# In the weird case we are coming from the backend (https://github.com/odoo/odoo/issues/20245)
website = len(self) == 1 and self or self.search([], limit=1)
isocountry = req and req.session.geoip and req.session.geoip.get('country_code') or False
partner = self.env.user.partner_id
last_order_pl = partner.last_website_so_id.pricelist_id
partner_pl = partner.property_product_pricelist
pricelists = website._get_pl_partner_order(isocountry, show_visible,
website.user_id.sudo().partner_id.property_product_pricelist.id,
req and req.session.get('website_sale_current_pl') or None,
website.pricelist_ids,
partner_pl=partner_pl and partner_pl.id or None,
order_pl=last_order_pl and last_order_pl.id or None)
return self.env['product.pricelist'].browse(pricelists)
website = website.with_company(website.company_id)
partner_sudo = website.env.user.partner_id
last_order_pricelist = partner_sudo.last_website_so_id.pricelist_id
partner_pricelist = partner_sudo.property_product_pricelist
website_pricelist = website.sudo().partner_id.property_product_pricelist
website_pricelists = website.sudo().pricelist_ids
current_pricelist_id = req and req.session.get('website_sale_current_pl') or None
pricelist_ids = website._get_pl_partner_order(
isocountry,
show_visible,
website_pricelist.id,
current_pricelist_id,
website_pricelists, # TODO give as ids for better cache ?
partner_pl=partner_pricelist.id,
order_pl=last_order_pricelist.id)
return self.env['product.pricelist'].browse(pricelist_ids)
def get_pricelist_available(self, show_visible=False):
return self._get_pricelist_available(request, show_visible)
@@ -187,6 +200,8 @@ class Website(models.Model):
"""
:returns: The current pricelist record
"""
self = self.with_company(self.company_id)
# The list of available pricelists for this user.
# If the user is signed in, and has a pricelist set different than the public user pricelist
# then this pricelist will always be considered as available
@@ -271,11 +286,16 @@ class Website(models.Model):
:returns: browse record for the current sales order
"""
self.ensure_one()
partner = self.env.user.partner_id
self = self.with_company(self.company_id)
partner_sudo = self.env.user.partner_id
SaleOrder = self.env['sale.order'].sudo()
sale_order_id = request.session.get('sale_order_id')
check_fpos = False
if not sale_order_id and not self.env.user._is_public():
last_order = partner.last_website_so_id
last_order = partner_sudo.last_website_so_id
if last_order:
available_pricelists = self.get_pricelist_available()
# Do not reload the cart of this user last visit if the cart uses a pricelist no longer available.
@@ -283,7 +303,7 @@ class Website(models.Model):
check_fpos = True
# Test validity of the sale_order_id
sale_order = self.env['sale.order'].with_company(request.website.company_id.id).sudo().browse(sale_order_id).exists() if sale_order_id else None
sale_order = SaleOrder.browse(sale_order_id).exists() if sale_order_id else None
# Do not reload the cart of this user last visit if the Fiscal Position has changed.
if check_fpos and sale_order:
@@ -314,11 +334,11 @@ class Website(models.Model):
if not sale_order:
# TODO cache partner_id session
pricelist = self.env['product.pricelist'].browse(pricelist_id).sudo()
so_data = self._prepare_sale_order_values(partner, pricelist)
sale_order = self.env['sale.order'].with_company(request.website.company_id.id).with_user(SUPERUSER_ID).create(so_data)
so_data = self._prepare_sale_order_values(partner_sudo, pricelist)
sale_order = SaleOrder.with_user(SUPERUSER_ID).create(so_data)
# set fiscal position
if request.website.partner_id.id == partner.id: # For public user, fiscal position based on geolocation
if request.website.partner_id.id == partner_sudo.id: # For public user, fiscal position based on geolocation
country_code = request.session['geoip'].get('country_code')
if country_code:
country_id = request.env['res.country'].search([('code', '=', country_code)], limit=1).id
@@ -331,10 +351,10 @@ class Website(models.Model):
request.session['sale_order_id'] = sale_order.id
# check for change of pricelist with a coupon
pricelist_id = pricelist_id or partner.property_product_pricelist.id
pricelist_id = pricelist_id or partner_sudo.property_product_pricelist.id
# check for change of partner_id ie after signup
if sale_order.partner_id.id != partner.id and request.website.partner_id.id != partner.id:
if sale_order.partner_id.id != partner_sudo.id and request.website.partner_id.id != partner_sudo.id:
flag_pricelist = False
if pricelist_id != sale_order.pricelist_id.id:
flag_pricelist = True
@@ -342,9 +362,9 @@ class Website(models.Model):
# change the partner, and trigger the onchange
sale_order.write({
'partner_id': partner.id,
'partner_invoice_id': partner.id,
'payment_term_id': self.sale_get_payment_term(partner),
'partner_id': partner_sudo.id,
'partner_invoice_id': partner_sudo.id,
'payment_term_id': self.sale_get_payment_term(partner_sudo),
})
# check the pricelist : update it if the pricelist is not the 'forced' one
@@ -375,7 +395,7 @@ class Website(models.Model):
update_pricelist = True
elif code is not None and sale_order.pricelist_id.code and code != sale_order.pricelist_id.code:
# code is not None when user removes code and click on "Apply"
pricelist_id = partner.property_product_pricelist.id
pricelist_id = partner_sudo.property_product_pricelist.id
update_pricelist = True
# update the pricelist