diff --git a/addons/product/models/product_pricelist.py b/addons/product/models/product_pricelist.py index 4970d0287b9..7f651473480 100644 --- a/addons/product/models/product_pricelist.py +++ b/addons/product/models/product_pricelist.py @@ -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'] diff --git a/addons/product/models/res_partner.py b/addons/product/models/res_partner.py index 47ddba8fa5e..79cc3140d0c 100644 --- a/addons/product/models/res_partner.py +++ b/addons/product/models/res_partner.py @@ -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: diff --git a/addons/website_sale/models/product_pricelist.py b/addons/website_sale/models/product_pricelist.py index baafe0952a1..fd57c28fde8 100644 --- a/addons/website_sale/models/product_pricelist.py +++ b/addons/website_sale/models/product_pricelist.py @@ -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. diff --git a/addons/website_sale/models/website.py b/addons/website_sale/models/website.py index 9c90d1e49f2..748da9d7e74 100644 --- a/addons/website_sale/models/website.py +++ b/addons/website_sale/models/website.py @@ -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