[FIX] website_sale: avoid adding website on customer when not needed

Before this commit, when an address would be edited or created from checkout,
we would always add a website_id on it.
(values_postprocess -> _checkout_form_save)

That would be incorrect if the website has not 'specific user account' enabled.

It would lead to an error as the user would not be able to connect to another
website. Indeed, the billing address is stored on the partner directly. Since
the res.user website_id is a related to the partner one, the user would then be
restricted to that website.

Fixes https://github.com/odoo/odoo/issues/34308

closes odoo/odoo#34396

Signed-off-by: Jérémy Kersten (jke) <jke@openerp.com>
This commit is contained in:
Robot Odoo
2019-07-01 14:26:35 +02:00
committed by GitHub
6 changed files with 112 additions and 54 deletions
+8 -45
View File
@@ -2,10 +2,9 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import re
import werkzeug
import odoo
from odoo import tools
from odoo.addons.website.tools import MockRequest
from odoo.modules.module import get_module_resource
from odoo.tests.common import TransactionCase
@@ -66,42 +65,6 @@ class TestQweb(TransactionCase):
"user_id": demo.id,
}).encode('utf8'))
class MockObject(object):
_log_call = []
def __init__(self, *args, **kwargs):
self.__dict__ = kwargs
def __call__(self, *args, **kwargs):
self._log_call.append((args, kwargs))
return self
def __getitem__(self, index):
return self
def werkzeugRaiseNotFound(*args, **kwargs):
raise werkzeug.exceptions.NotFound()
class MockRequest(object):
""" Class with context manager mocking odoo.http.request for tests """
def __init__(self, env, website=None, context=None, multilang=True, routing=True):
app = MockObject(routing={
'type': 'http',
'website': True,
'multilang': multilang,
})
app.get_db_router = app.bind = app.match = app
if not routing:
app.match = werkzeugRaiseNotFound
self.request = MockObject(
env=env, context=context or {}, db=None, debug=False,
website=website, httprequest=MockObject(
path='/hello/',
app=app
)
)
odoo.http._request_stack.push(self.request)
def __enter__(self):
return self.request
def __exit__(self, exc_type, exc_value, traceback):
odoo.http._request_stack.pop()
class TestQwebProcessAtt(TransactionCase):
def setUp(self):
@@ -128,7 +91,7 @@ class TestQwebProcessAtt(TransactionCase):
self._test_att('/a', {'href': '/a'})
def test_process_att_no_website(self):
with MockRequest(self.env) as request:
with MockRequest(self.env):
# no website so URL rewriting
self._test_att('/', {'href': '/'})
self._test_att('/en_US/', {'href': '/en_US/'})
@@ -137,7 +100,7 @@ class TestQwebProcessAtt(TransactionCase):
self._test_att('/a', {'href': '/a'})
def test_process_att_monolang_route(self):
with MockRequest(self.env, website=self.website, multilang=False) as request:
with MockRequest(self.env, website=self.website, multilang=False):
# lang not changed in URL but CDN enabled
self._test_att('/a', {'href': 'http://test.cdn/a'})
self._test_att('/en_US/a', {'href': 'http://test.cdn/en_US/a'})
@@ -145,19 +108,19 @@ class TestQwebProcessAtt(TransactionCase):
self._test_att('/en_US/b', {'href': '/en_US/b'})
def test_process_att_no_request_lang(self):
with MockRequest(self.env, self.website) as request:
with MockRequest(self.env, website=self.website):
self._test_att('/', {'href': '/'})
self._test_att('/en_US/', {'href': '/'})
self._test_att('/fr_FR/', {'href': '/fr_FR/'})
def test_process_att_with_request_lang(self):
with MockRequest(self.env, self.website, context={'lang': 'fr_FR'}) as request:
with MockRequest(self.env, website=self.website, context={'lang': 'fr_FR'}):
self._test_att('/', {'href': '/fr_FR/'})
self._test_att('/en_US/', {'href': '/'})
self._test_att('/fr_FR/', {'href': '/fr_FR/'})
def test_process_att_matching_cdn_and_lang(self):
with MockRequest(self.env, self.website) as request:
with MockRequest(self.env, website=self.website):
# lang prefix is added before CDN
self._test_att('/a', {'href': 'http://test.cdn/a'})
self._test_att('/en_US/a', {'href': 'http://test.cdn/a'})
@@ -167,13 +130,13 @@ class TestQwebProcessAtt(TransactionCase):
self._test_att('/fr_FR/b', {'href': '/fr_FR/b'})
def test_process_att_no_route(self):
with MockRequest(self.env, self.website, context={'lang': 'fr_FR'}, routing=False) as request:
with MockRequest(self.env, website=self.website, context={'lang': 'fr_FR'}, routing=False):
# default on multilang=True if route is not /{module}/static/
self._test_att('/web/static/hi', {'href': '/web/static/hi'})
self._test_att('/my-page', {'href': '/fr_FR/my-page'})
def test_process_att_url_crap(self):
with MockRequest(self.env, self.website) as request:
with MockRequest(self.env, website=self.website) as request:
# #{fragment} is stripped from URL when testing route
self._test_att('/x#y?z', {'href': '/x#y?z'})
self.assertEqual(
+60
View File
@@ -0,0 +1,60 @@
# -*- encoding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import odoo
import werkzeug
from odoo.tools import DotDict
class MockObject(object):
_log_call = []
def __init__(self, *args, **kwargs):
self.__dict__ = kwargs
def __call__(self, *args, **kwargs):
self._log_call.append((args, kwargs))
return self
def __getitem__(self, index):
return self
def werkzeugRaiseNotFound(*args, **kwargs):
raise werkzeug.exceptions.NotFound()
class MockRequest(object):
""" Class with context manager mocking odoo.http.request for tests """
def __init__(self, env, **kw):
app = MockObject(routing={
'type': 'http',
'website': True,
'multilang': kw.get('multilang', True),
})
app.get_db_router = app.bind = app.match = app
if not kw.get('routing', True):
app.match = werkzeugRaiseNotFound
self.request = DotDict({
'context': kw.get('context', {}),
'db': None,
'debug': False,
'env': env,
'httprequest': {
'path': '/hello/',
'app': app,
},
'redirect': werkzeug.utils.redirect,
'session': {
'sale_order_id': kw.get('sale_order_id'),
},
'website': kw.get('website'),
})
odoo.http._request_stack.push(self.request)
def __enter__(self):
return self.request
def __exit__(self, exc_type, exc_value, traceback):
odoo.http._request_stack.pop()
+3 -1
View File
@@ -605,7 +605,9 @@ class WebsiteSale(ProductConfiguratorController):
new_values['customer'] = True
new_values['team_id'] = request.website.salesteam_id and request.website.salesteam_id.id
new_values['user_id'] = request.website.salesperson_id and request.website.salesperson_id.id
new_values['website_id'] = request.website.id
if request.website.specific_user_account:
new_values['website_id'] = request.website.id
if order.partner_id.id == request.website.user_id.sudo().partner_id.id:
# If the partner is public, we assign the company of the website
+29 -1
View File
@@ -1,7 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import odoo.tests
from odoo.addons.website_sale.controllers.main import WebsiteSale
from odoo.addons.website.tools import MockRequest
@odoo.tests.tagged('post_install', '-at_install')
class TestUi(odoo.tests.HttpCase):
@@ -14,4 +18,28 @@ class TestUi(odoo.tests.HttpCase):
def test_03_demo_checkout(self):
self.phantom_js("/", "odoo.__DEBUG__.services['web_tour.tour'].run('shop_buy_product')", "odoo.__DEBUG__.services['web_tour.tour'].tours.shop_buy_product.ready", login="demo")
# TO DO - add public test with new address when convert to web.tour format.
@odoo.tests.tagged('post_install', '-at_install')
class TestWebsiteSaleCheckoutAddress(odoo.tests.TransactionCase):
def test_01_edit_billing_address(self):
website = self.env['website'].browse(1)
p = self.env.user.partner_id
so = self.env['sale.order'].create({
'partner_id': p.id,
'website_id': website.id,
'order_line': [(0, 0, {
'product_id': self.env['product.product'].create({'name': 'Product A', 'list_price': 100}).id,
'name': 'Product A',
})]
})
country_id = self.env['res.country'].search([], limit=1).id
WebsiteSaleController = WebsiteSale()
with MockRequest(self.env, website=website, sale_order_id=so.id):
WebsiteSaleController.address(name='website_sale test user', email='email@email.email', street='ooo', city='ooo', country_id=country_id, submitted=1, partner_id=p.id)
self.assertFalse(p.website_id, "Partner should not have a website set on him after editing billing.")
website.specific_user_account = True
with MockRequest(self.env, website=website, sale_order_id=so.id):
WebsiteSaleController.address(name='website_sale test user', email='email@email.email', street='ooo', city='ooo', country_id=country_id, submitted=1, partner_id=p.id)
self.assertEqual(p.website_id, website, "Partner should have a website set on him after editing billing.")
@@ -5,6 +5,7 @@ try:
except ImportError:
from mock import patch
from odoo.tests.common import HttpCase, TransactionCase
from odoo.tools import DotDict
''' /!\/!\
Calling `get_pricelist_available` after setting `property_product_pricelist` on
@@ -154,13 +155,6 @@ def simulate_frontend_context(self, website_id=1):
self.addCleanup(patcher.stop)
class DotDict(dict):
"""dot.notation access to dictionary attributes"""
def __getattr__(*args):
val = dict.get(*args)
return DotDict(val) if type(val) is dict else val
class TestWebsitePriceListAvailable(TransactionCase):
# This is enough to avoid a mock (request.session/website do not exist during test)
def get_pricelist_available(self, show_visible=False, website_id=1, country_code=None, website_sale_current_pl=None):
+11
View File
@@ -1242,3 +1242,14 @@ def wrap_module(module, attr_list):
raise AttributeError(attrib)
# module and attr_list are in the closure
return WrappedModule()
class DotDict(dict):
"""Helper for dot.notation access to dictionary attributes
E.g.
foo = DotDict({'bar': False})
return foo.bar
"""
def __getattr__(self, attrib):
val = self.get(attrib)
return DotDict(val) if type(val) is dict else val