| .. | ||
| controllers | ||
| i18n | ||
| models | ||
| static | ||
| tests | ||
| views | ||
| __init__.py | ||
| __manifest__.py | ||
| README.md | ||
website_membership_signup_required
Módulo in-house (Criptomart) para el cliente EMES que replica el flujo de alta de asociados de YourMembership (YM) dentro de Odoo 18.
Qué hace
Intercepta el "Add to Cart" del e-commerce solo para productos de
membresía (product.template.membership = True). Si el visitante es
anónimo (usuario público) y el ajuste membership_signup_required del
website está activo (por defecto ON), el request no se completa y se redirige
al formulario estándar de /web/signup (de auth_signup), con un
redirect de vuelta a la ficha del producto y un marcador efímero
membership_signup=1 que re-habilita el signup de modo condicional aunque el
website esté en modo b2b (guest checkout).
Tras crear la cuenta, el usuario queda autenticado y vuelve a la ficha del producto, donde ya puede añadirlo al carrito y continuar al checkout.
Para el resto del catálogo (productos no miembros) el flujo b2b nativo (guest checkout) se mantiene intacto, sin interferencia.
Por qué es necesario un módulo propio
website_sale fuerza por defecto website.auth_signup_uninvited = 'b2c'
(signup/registro dentro del checkout). Pasar el website a b2b — lo que
permite guest checkout para el catálogo general — deshabilita el route
/web/signup para el público general. Para re-abrirlo solo durante el
flujo de membresía, este módulo extiende con un override conservador de
res.users._get_signup_invitation_scope() que devuelve 'b2c' únicamente
cuando el request porta el marcador efímero (membership_signup=1 en la query
o membership_signup_active en sesión). El marcador se setea al interceptar
el cart_update y se limpia tras signup/login exitosos.
Características / requisitos funcionales
- RF-01 Interceptación de
/shop/cart/updatey/shop/cart/update_jsonsolo paraproduct.template.membership = Truey usuario público. - RF-02 Redirección a
/web/signup?membership_signup=1&redirect=/shop/<slug>. - RF-03 Re-habilitación condicional del signup en b2b vía override de
_get_signup_invitation_scope. - RF-04 Flujo completo: signup → vuelta a la ficha del producto → add to cart → carrito.
- RF-05 Productos no miembros: sin interceptación, flujo b2b normal.
- RF-06 Usuarios ya autenticados no se interceptan.
- RF-07 Configurable por website desde Ajustes → E-commerce → "Membership signup required" (default ON).
- RF-08 No afecta al backend ni a métodos de pago: la interceptación
vive solo en los controllers
/shop/cart/update*. - RF-09 Separación
firstname/lastnameen el formulario de signup (gracias a la dependencia OCApartner_firstname). - RF-10 El enlace "Already have an account?" del formulario de signup
preserva el
redirect, por lo que un usuario con cuenta existente puede loguearse y volver a la ficha del producto. - RF-11 i18n:
module.pot+es.po(español dominante) +gl.poyca.pocomo esqueleto.
Dependencias
website_saleauth_signupmembershippartner_firstname(OCA,partner-contact/).
Configuración
- Settings → Website → Ajustes e-commerce → "Customer Accounts" debe estar
en Optional (guest checkout) o lo equivalente a
b2ben el website activo (esto eswebsite.account_on_checkout = 'disabled'). - Settings → "Membership signup required" checkbox: activado por defecto.
- Para productos con
membership = True, el flujo aplica; para el resto, no.
Cómo correr los tests
# En el entorno EMES (desde OCB):
./odoo-bin -c emes.conf -i website_membership_signup_required \
--test-tags website_membership_signup_required --stop-after-init
Los tests unitarios (TransactionCase) cubren RF-01..RF-08 con lógica; el tour HttpCase end-to-end cubre RF-04 (flujo completo).
Notas técnicas
- El override de
_get_signup_invitation_scopees el punto más sensible de seguridad: solo devuelve'b2c'cuando el request porta el marcador efímero. No re-abre el signup libre en b2b para el catálogo general. - La decisión de interceptar está centralizada en
website._membership_signup_required_for(product_id, is_public)(capa modelo) para poder testearla sin HTTP y reutilizarla en futuras extensiones. - El controller
_membership_signup_is_required(product_id)delega en ese helper. - Tras signup / login exitoso, se limpian los flags
membership_signup_activeymembership_signup_product_idde la sesión.
Licencia
AGPL-3.
Autor
Criptomart (https://git.criptomart.net/criptomart/addons-cm).
Limitaciones / notas de seguridad
El marcador membership_signup=1 que habilita el route /web/signup en modo
b2b es un query param público: cualquier visitante que conozca ese param
puede acceder al formulario de signup directamente, sin pasar por el carrito
de membresía. Esta decisión está aceptada por diseño: el signup crea
cuentas de tipo portal (no internal), y la postura b2b del catálogo general
se mantiene — el route solo se reabre de forma condicional vía el override de
_get_signup_invitation_scope cuando el request porta el marcador efímero (ver
models/res_users.py y controllers/website_sale.py); nunca se reabre el
signup libre para el resto del tráfico.
Para reforzar la seguridad se podría sustituir el query param estático por un
token HMAC firmado (vinculado a la sesión y al producto objetivo), de modo
que solo el flujo de cart_update pueda generar un redirect válido y el
acceso directo a /web/signup fuera de ese flujo quede restringido. Ver
requirements.md §7 R-01.