123 lines
No EOL
5.6 KiB
Markdown
123 lines
No EOL
5.6 KiB
Markdown
# 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
|
|
|
|
```bash
|
|
# 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. |