addons-cm/website_membership_signup_required/README.md

5.6 KiB

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/update y /shop/cart/update_json solo para product.template.membership = True y 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 / lastname en el formulario de signup (gracias a la dependencia OCA partner_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.po y ca.po como esqueleto.

Dependencias

  • website_sale
  • auth_signup
  • membership
  • partner_firstname (OCA, partner-contact/).

Configuración

  1. Settings → Website → Ajustes e-commerce → "Customer Accounts" debe estar en Optional (guest checkout) o lo equivalente a b2b en el website activo (esto es website.account_on_checkout = 'disabled').
  2. Settings → "Membership signup required" checkbox: activado por defecto.
  3. 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_scope es 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_active y membership_signup_product_id de 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.