add website_membership_signup_required: force to create user before add to cart a membership product
This commit is contained in:
parent
80f24bcc46
commit
c4d32c6add
22 changed files with 1202 additions and 0 deletions
123
website_membership_signup_required/README.md
Normal file
123
website_membership_signup_required/README.md
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
# 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue