[ADD] website_sale_aplicoop: serve Eskaera per website
A co-op can run the plain shop on one website and the group orders on another, but routes are registered process-wide: every /eskaera page answered on every website of the database, and Odoo had already copied the Eskaera menu to all of them. `website.eskaera_enabled` decides which websites serve it, on by default so installing changes nothing. It reaches the settings screen through `website_id`, so it follows the website selector there. Where it is off the routes raise NotFound and the menu is hidden. The menu is hidden rather than deleted, by extending `_compute_visible`. That keeps the record and any manual rename or reordering, so switching the feature back on restores it as it was. Note that a JSON route reports the 404 inside the JSON-RPC payload and still answers HTTP 200; that is the transport, not a hole in the guard, and a test pins it so the next reader does not take it for one. The three remaining settings (lazy loading, products per page, low stock threshold) are still `config_parameter`, so they stay global to the database. Two websites share their values. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
817ff31d39
commit
5a2f5d120f
9 changed files with 313 additions and 0 deletions
|
|
@ -24,3 +24,4 @@ from . import test_date_edge_cases # noqa: F401
|
|||
from . import test_portal_routes # noqa: F401
|
||||
from . import test_price_with_taxes_included # noqa: F401
|
||||
from . import test_pricing_delegation # noqa: F401
|
||||
from . import test_website_enabled # noqa: F401
|
||||
|
|
|
|||
|
|
@ -63,6 +63,8 @@ class TestGroupOrderStatusEndpoint(TransactionCase):
|
|||
data=json.dumps(payload).encode("utf-8"),
|
||||
),
|
||||
make_response=_make_response,
|
||||
# The Eskaera routes check that this website serves Eskaera.
|
||||
website=SimpleNamespace(eskaera_enabled=True),
|
||||
)
|
||||
|
||||
def test_check_group_order_status_open(self):
|
||||
|
|
|
|||
|
|
@ -69,6 +69,8 @@ def _build_request_mock(env, payload=None, website=None):
|
|||
show_line_subtotals_tax_selection="tax_excluded",
|
||||
fiscal_position_id=False,
|
||||
company_id=False,
|
||||
# The Eskaera routes check that this website serves Eskaera.
|
||||
eskaera_enabled=True,
|
||||
)
|
||||
request_mock = SimpleNamespace(
|
||||
env=env,
|
||||
|
|
|
|||
208
website_sale_aplicoop/tests/test_website_enabled.py
Normal file
208
website_sale_aplicoop/tests/test_website_enabled.py
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
# Copyright 2025 Criptomart
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
|
||||
"""Eskaera is served per website, not database-wide.
|
||||
|
||||
A co-op can run the plain shop on one website and the group orders on another.
|
||||
`website.eskaera_enabled` decides which is which: where it is off the /eskaera
|
||||
routes answer 404 and the Eskaera menu is hidden.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
|
||||
from odoo.tests import tagged
|
||||
from odoo.tests.common import HttpCase
|
||||
from odoo.tests.common import TransactionCase
|
||||
|
||||
|
||||
class EskaeraWebsiteCommon:
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.website = self.env.ref("website.default_website")
|
||||
|
||||
def _eskaera_menus(self, website):
|
||||
return self.env["website.menu"].search(
|
||||
[("url", "=like", "/eskaera%"), ("website_id", "=", website.id)]
|
||||
)
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
class TestEskaeraEnabledField(EskaeraWebsiteCommon, TransactionCase):
|
||||
"""The switch itself, and what it does to the menu."""
|
||||
|
||||
def test_enabled_by_default(self):
|
||||
"""Installing the addon leaves every website serving Eskaera."""
|
||||
self.assertTrue(self.website.eskaera_enabled)
|
||||
self.assertTrue(
|
||||
all(website.eskaera_enabled for website in self.env["website"].search([]))
|
||||
)
|
||||
|
||||
def test_new_website_serves_eskaera(self):
|
||||
"""A website created later also starts with Eskaera on."""
|
||||
new_website = self.env["website"].create({"name": "Brand New Site"})
|
||||
|
||||
self.assertTrue(new_website.eskaera_enabled)
|
||||
|
||||
def test_menu_is_visible_while_enabled(self):
|
||||
"""The Eskaera menu shows on a website that serves it."""
|
||||
menus = self._eskaera_menus(self.website)
|
||||
|
||||
self.assertTrue(menus, "the website should have an Eskaera menu")
|
||||
self.assertTrue(all(menu.is_visible for menu in menus))
|
||||
|
||||
def test_menu_is_hidden_when_disabled(self):
|
||||
"""Switching Eskaera off hides its menu on that website only."""
|
||||
other_website = self.env["website"].create({"name": "Shop Only Site"})
|
||||
self.website.eskaera_enabled = False
|
||||
|
||||
self.env["website.menu"].invalidate_model(["is_visible"])
|
||||
|
||||
self.assertFalse(any(m.is_visible for m in self._eskaera_menus(self.website)))
|
||||
# The other website is untouched.
|
||||
other_menus = self._eskaera_menus(other_website)
|
||||
if other_menus:
|
||||
self.assertTrue(all(menu.is_visible for menu in other_menus))
|
||||
|
||||
def test_menu_is_kept_not_deleted(self):
|
||||
"""Turning the feature back on restores the menu as it was."""
|
||||
menus = self._eskaera_menus(self.website)
|
||||
menu_ids = menus.ids
|
||||
|
||||
self.website.eskaera_enabled = False
|
||||
self.website.eskaera_enabled = True
|
||||
self.env["website.menu"].invalidate_model(["is_visible"])
|
||||
|
||||
restored = self._eskaera_menus(self.website)
|
||||
self.assertEqual(restored.ids, menu_ids)
|
||||
self.assertTrue(all(menu.is_visible for menu in restored))
|
||||
|
||||
def test_other_menus_are_left_alone(self):
|
||||
"""The override must not touch menus that are not Eskaera's."""
|
||||
self.website.eskaera_enabled = False
|
||||
self.env["website.menu"].invalidate_model(["is_visible"])
|
||||
|
||||
home = self.env["website.menu"].search(
|
||||
[("url", "=", "/"), ("website_id", "=", self.website.id)], limit=1
|
||||
)
|
||||
|
||||
if home:
|
||||
self.assertTrue(home.is_visible)
|
||||
|
||||
def test_setting_is_exposed_per_website(self):
|
||||
"""The switch reaches the settings screen through website_id."""
|
||||
settings = self.env["res.config.settings"].create(
|
||||
{"website_id": self.website.id}
|
||||
)
|
||||
|
||||
self.assertTrue(settings.eskaera_enabled)
|
||||
|
||||
settings.eskaera_enabled = False
|
||||
settings.execute()
|
||||
|
||||
self.assertFalse(self.website.eskaera_enabled)
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
class TestEskaeraRoutesPerWebsite(EskaeraWebsiteCommon, HttpCase):
|
||||
"""The /eskaera routes answer only where the feature is on."""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.group = self.env["res.partner"].create(
|
||||
{
|
||||
"name": "Enabled Test Group",
|
||||
"is_company": True,
|
||||
"is_group": True,
|
||||
}
|
||||
)
|
||||
self.member_partner = self.env["res.partner"].create(
|
||||
{
|
||||
"name": "Enabled Test Member",
|
||||
"group_ids": [(6, 0, [self.group.id])],
|
||||
}
|
||||
)
|
||||
self.login = "enabled.member@test.com"
|
||||
self.env["res.users"].create(
|
||||
{
|
||||
"name": "Enabled Test Member",
|
||||
"login": self.login,
|
||||
"password": self.login,
|
||||
"partner_id": self.member_partner.id,
|
||||
"groups_id": [(4, self.env.ref("base.group_portal").id)],
|
||||
}
|
||||
)
|
||||
|
||||
start_date = datetime.now().date()
|
||||
self.group_order = self.env["group.order"].create(
|
||||
{
|
||||
"name": "Enabled Test Order",
|
||||
"group_ids": [(6, 0, [self.group.id])],
|
||||
"type": "regular",
|
||||
"start_date": start_date,
|
||||
"end_date": start_date + timedelta(days=7),
|
||||
"period": "weekly",
|
||||
"pickup_day": "3",
|
||||
"cutoff_day": "0",
|
||||
}
|
||||
)
|
||||
self.group_order.action_open()
|
||||
|
||||
def _routes(self):
|
||||
return [
|
||||
"/eskaera",
|
||||
f"/eskaera/{self.group_order.slug}",
|
||||
f"/eskaera/{self.group_order.slug}/checkout",
|
||||
]
|
||||
|
||||
def test_routes_answer_when_enabled(self):
|
||||
"""With the feature on, the pages render."""
|
||||
self.authenticate(self.login, self.login)
|
||||
|
||||
for route in self._routes():
|
||||
response = self.url_open(route, allow_redirects=True)
|
||||
self.assertEqual(response.status_code, 200, msg=f"{route} should be served")
|
||||
|
||||
def test_routes_are_404_when_disabled(self):
|
||||
"""With the feature off, the pages are gone from that website."""
|
||||
self.website.eskaera_enabled = False
|
||||
self.authenticate(self.login, self.login)
|
||||
|
||||
for route in self._routes():
|
||||
response = self.url_open(route, allow_redirects=True)
|
||||
self.assertEqual(
|
||||
response.status_code, 404, msg=f"{route} should not be served"
|
||||
)
|
||||
|
||||
def _call_labels(self):
|
||||
return self.url_open(
|
||||
"/eskaera/labels",
|
||||
data=b'{"jsonrpc": "2.0", "method": "call", "params": {}}',
|
||||
headers={"Content-Type": "application/json"},
|
||||
).json()
|
||||
|
||||
def test_json_endpoint_answers_when_enabled(self):
|
||||
"""With the feature on, the labels endpoint returns its dictionary."""
|
||||
self.authenticate(self.login, self.login)
|
||||
|
||||
payload = self._call_labels()
|
||||
|
||||
self.assertIn("result", payload)
|
||||
self.assertIn("total", payload["result"])
|
||||
|
||||
def test_json_endpoint_is_gated_when_disabled(self):
|
||||
"""The JSON endpoints are gated too, not just the pages.
|
||||
|
||||
A JSON route reports the 404 inside the payload and still answers
|
||||
HTTP 200: that is how Odoo's JSON-RPC transport surfaces exceptions.
|
||||
"""
|
||||
self.website.eskaera_enabled = False
|
||||
self.authenticate(self.login, self.login)
|
||||
|
||||
payload = self._call_labels()
|
||||
|
||||
self.assertNotIn("result", payload)
|
||||
self.assertEqual(payload["error"]["code"], 404)
|
||||
self.assertEqual(
|
||||
payload["error"]["data"]["name"], "werkzeug.exceptions.NotFound"
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue