[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:
@@ -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(
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user