[ADD] website_sale_disable_cart: extract cart restriction from website_sale_aplicoop
The "shop as a read-only catalog" behaviour lived inside website_sale_aplicoop, so every site that wanted the eskaera flow also lost the standard cart. It now ships as its own installable addon, with the redirect target configurable instead of hardcoded to /eskaera. - website_sale_disable_cart: hides the cart UI (12 header styles + the product card quick-add) and overrides the standard cart endpoints. Redirect URL is configurable in Website settings (default /shop); only site-internal paths are accepted, so a misconfigured value cannot turn the shop into an open redirect nor loop back into a disabled route. - Fixes carried over from the original code: /shop/cart/quantity is the Odoo 18 path (it was /shop/cart_quantity, which never matched), the boxed, sidebar and sales two/three/four headers were not covered (the cart link stayed visible on them), and the routes now override the standard methods instead of registering duplicate ones. - website_sale_aplicoop 18.0.1.12.0: drops the view file and the four redirect routes; installing it no longer touches the standard shop. Upgrade order matters: update website_sale_aplicoop first, then install website_sale_disable_cart in a second Odoo run — both use the same XPaths and obsolete records are only cleaned up at the end of a run. Tests: 8/8 in website_sale_disable_cart, aplicoop unaffected (its 2 failures predate this change). Verified live on a DB clone: /shop/cart returns 303 to the configured URL and no cart markup remains on /shop. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
73660ee226
commit
f2194c5367
28 changed files with 733 additions and 91 deletions
102
website_sale_disable_cart/controllers/website_sale.py
Normal file
102
website_sale_disable_cart/controllers/website_sale.py
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# Copyright 2026 Criptomart
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
|
||||
import logging
|
||||
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
|
||||
from odoo.addons.website_sale.controllers.main import WebsiteSale
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_REDIRECT_URL = "/shop"
|
||||
REDIRECT_URL_PARAM = "website_sale_disable_cart.redirect_url"
|
||||
|
||||
|
||||
class WebsiteSaleDisableCart(WebsiteSale):
|
||||
"""Neutralize the standard ``website_sale`` cart endpoints.
|
||||
|
||||
The shop keeps working as a plain catalog (listing, search and product
|
||||
pages are untouched); only the cart itself becomes unreachable. HTTP
|
||||
routes redirect to the configured URL, JSON routes answer with an inert
|
||||
payload so any leftover frontend call is a no-op instead of an error.
|
||||
"""
|
||||
|
||||
def _get_cart_redirect_url(self):
|
||||
"""Return the internal path the disabled cart routes redirect to."""
|
||||
url = (
|
||||
request.env["ir.config_parameter"]
|
||||
.sudo()
|
||||
.get_param(REDIRECT_URL_PARAM, DEFAULT_REDIRECT_URL)
|
||||
)
|
||||
url = (url or "").strip()
|
||||
# Only site-internal paths are accepted: an absolute or protocol
|
||||
# relative URL would turn the shop into an open redirect, and a path
|
||||
# back under /shop/cart would loop through the disabled routes.
|
||||
if (
|
||||
not url.startswith("/")
|
||||
or url.startswith("//")
|
||||
or url.startswith("/shop/cart")
|
||||
):
|
||||
_logger.warning(
|
||||
"[DISABLE_CART] Invalid redirect URL %r, falling back to %s",
|
||||
url,
|
||||
DEFAULT_REDIRECT_URL,
|
||||
)
|
||||
return DEFAULT_REDIRECT_URL
|
||||
return url
|
||||
|
||||
def _redirect_disabled_cart(self, route):
|
||||
"""Redirect a disabled cart route to the configured URL."""
|
||||
url = self._get_cart_redirect_url()
|
||||
_logger.info("[DISABLE_CART] %s → %s", route, url)
|
||||
return request.redirect(url)
|
||||
|
||||
@http.route()
|
||||
def cart(self, access_token=None, revive="", **post):
|
||||
"""Cart page is disabled: send the visitor to the configured URL."""
|
||||
return self._redirect_disabled_cart("/shop/cart")
|
||||
|
||||
@http.route()
|
||||
def cart_update(
|
||||
self,
|
||||
product_id=None,
|
||||
add_qty=1,
|
||||
set_qty=0,
|
||||
product_custom_attribute_values=None,
|
||||
no_variant_attribute_value_ids=None,
|
||||
**kwargs,
|
||||
):
|
||||
"""Adding to cart is disabled: nothing is written, just redirect."""
|
||||
return self._redirect_disabled_cart("/shop/cart/update")
|
||||
|
||||
@http.route()
|
||||
def cart_update_json(
|
||||
self,
|
||||
product_id=None,
|
||||
line_id=None,
|
||||
add_qty=None,
|
||||
set_qty=None,
|
||||
display=True,
|
||||
product_custom_attribute_values=None,
|
||||
no_variant_attribute_value_ids=None,
|
||||
**kwargs,
|
||||
):
|
||||
"""Adding to cart is disabled.
|
||||
|
||||
An empty dict is the response ``website_sale`` already returns when the
|
||||
order cannot be updated, so callers handle it without breaking.
|
||||
"""
|
||||
_logger.info("[DISABLE_CART] /shop/cart/update_json ignored")
|
||||
return {}
|
||||
|
||||
@http.route()
|
||||
def cart_quantity(self):
|
||||
"""The cart is always empty while this module is installed."""
|
||||
return 0
|
||||
|
||||
@http.route()
|
||||
def clear_cart(self):
|
||||
"""Nothing to clear: the cart is never fed through the standard shop."""
|
||||
return None
|
||||
Loading…
Add table
Add a link
Reference in a new issue