addons-cm/website_sale_aplicoop/README_DEV.md
GitHub Copilot f2194c5367 [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>
2026-08-06 11:34:59 +02:00

7.7 KiB
Raw Blame History

Website Sale - Aplicoop

Versión: 18.0.1.9.0 | Licencia: AGPL-3 | Autor: Criptomart SL

Sistema de pedidos colaborativos para grupos de consumo. Reemplaza el legacy Aplicoop con una solución moderna integrada en Odoo 18.

Resumen de funcionalidades

  • Gestión completa de órdenes de grupo (draft → confirmed → collected → invoiced → completed)
  • Carrito separado por grupo de consumo (la tienda estándar de website_sale se puede deshabilitar aparte con website_sale_disable_cart)
  • Control de catálogo por listas de inclusión (proveedores, categorías, productos) y exclusión (blacklists)
  • Cron diario de auto-confirmación + creación automática de lotes de picking
  • Paginación de productos (lazy loading) para cargas rápidas
  • Soporte multilingüe: ES, EU, PT, GL, CA, FR, IT
  • Restricción de acceso portal por grupo de consumo

Modelos

group.order

Orden de grupo central. Contiene toda la lógica de ciclo de vida.

Campos principales:

Campo Tipo Descripción
name Char Identificador de la orden
consumer_group_id Many2one → res.partner Grupo de consumo
state Selection draft / confirmed / collected / invoiced / completed / cancelled
cutoff_date Datetime Hasta cuándo se pueden añadir productos
pickup_date Date Día de recogida para los socios
delivery_date Date Fecha de entrega (almacenada, calculada por cron)
product_ids Many2many Productos incluidos directamente
category_ids Many2many Categorías incluidas (y sus subcategorías)
supplier_ids Many2many Proveedores incluidos (por main_seller_id)
excluded_product_ids Many2many Blacklist de productos
excluded_supplier_ids Many2many Blacklist de proveedores
excluded_category_ids Many2many Blacklist de categorías (recursiva)
allow_home_delivery Boolean Permite envío a domicilio además de recogida

Métodos clave:

  • _get_products_for_group_order() — catálogo efectivo aplicando whitelist + blacklist
  • _get_products_paginated(page, per_page) — paginación para lazy loading
  • _cron_confirm_group_orders() — auto-confirma órdenes pasado el cutoff y crea lotes de picking
  • _cron_update_dates() — recalcula fechas de entrega/recogida diariamente

group.order.slot (en desarrollo)

Franjas horarias de recogida para una orden de grupo.

Campo Tipo Descripción
group_order_id Many2one Orden de grupo
weekday Selection (06) Día de la semana (0 = lunes)
start_hour / end_hour Float Horario en formato decimal (9.5 = 09:30)
sequence Integer Orden de visualización

Extensiones de modelos core

product.template

  • main_seller_id — proveedor principal (de product_main_seller)
  • sequence en product.category — orden de visualización en la tienda web

sale.order

  • group_order_id — enlace a la orden de grupo
  • consumer_group_id — propagado desde group_order_id
  • pickup_slot_label — etiqueta legible de la franja de recogida

stock.picking

  • Extensión para agrupación por grupo de consumo en lotes de picking

Controladores

Todos los endpoints viven bajo /eskaera/:

Ruta Descripción
GET /eskaera Lista de órdenes de grupo activas del usuario
GET /eskaera/<order_id> Tienda de la orden (con lazy loading)
GET /eskaera/<order_id>/load-page?page=N Carga AJAX de página de productos
POST /eskaera/<order_id>/add Añadir producto al carrito
POST /eskaera/<order_id>/confirm Confirmar carrito (sale.order en draft)
POST /eskaera/clear-cart Limpiar carrito actual

Las rutas estándar de website_sale (/shop y su carrito) no se tocan desde este módulo. Para desactivar el carrito estándar y redirigirlo a /eskaera, instala website_sale_disable_cart y configura la URL de redirección.

Seguridad portal: los usuarios solo ven órdenes de su consumer_group_id. La regla rule_group_order_company_read incluye guardia user.share.

Templates QWeb

Template Descripción
eskaera_list Lista de órdenes activas
eskaera_shop Página de la tienda (incluye eskaera_shop_products)
eskaera_shop_products Grid de productos (reutilizable por lazy loading + initial render)
eskaera_cart Sidebar del carrito
eskaera_checkout Confirmación del pedido
load_from_history Cargar productos de una orden histórica

JavaScript (website_sale.js)

Clase GroupOrderShop (extiende publicWidget.Widget):

  • _attachEventListeners() — inicializa todos los listeners
  • _attachLoadMoreListener() — botón "Cargar más" para lazy loading (AJAX)
  • _onAddToCart() — añadir al carrito con feedback visual
  • _onClearCart() — limpiar carrito
  • _onDeliveryToggle() — toggle envío a domicilio / recogida

La lógica en _get_products_for_group_order() aplica en este orden:

  1. Whitelist: unión de product_ids + productos de category_ids (recursivo) + productos de supplier_ids (por main_seller_id)
  2. Blacklist: se restan excluded_product_ids, productos con main_seller_id en excluded_supplier_ids, y productos en excluded_category_ids (recursivo)
  3. La blacklist tiene prioridad absoluta sobre cualquier fuente de inclusión

Lazy loading

Configurable desde Ajustes > Website > Shop:

  • website_sale_aplicoop.lazy_loading_enabled (default: True)
  • website_sale_aplicoop.products_per_page (default: 20)

La página inicial renderiza la primera página server-side. El botón "Cargar más" hace peticiones AJAX al endpoint /load-page que devuelve HTML parcial con el template eskaera_shop_products.

Cron jobs

Cron Frecuencia Acción
_cron_confirm_group_orders Diario (medianoche) Confirma sale.orders pasado el cutoff; crea lotes de picking agrupados por consumer_group + pickup_date
_cron_update_dates Diario Recalcula delivery_date y pickup_date en órdenes activas

Dependencias

website_sale_aplicoop
  ├── website_sale (Odoo core)
  ├── sale (Odoo core)
  ├── product_main_seller (OCA)
  ├── product_sale_price_from_pricelist (custom)
  │     └── product_pricelist_total_margin (custom)
  │           └── product_price_category (OCA)
  └── stock_picking_batch_custom (custom)

Instalación y actualización

docker-compose exec -T odoo odoo -d odoo -u website_sale_aplicoop --stop-after-init

Para migraciones con cambios de esquema (e.g., nuevas columnas):

docker-compose exec -T odoo odoo -d odoo --update website_sale_aplicoop --stop-after-init

Tests

docker-compose exec -T odoo odoo -d odoo --test-enable --stop-after-init -u website_sale_aplicoop

Los tests están en tests/:

  • test_group_order.py — ciclo de vida básico, fechas, cron
  • test_product_discovery.py — whitelist/blacklist (productos, proveedores, categorías)
  • test_confirm_eskaera.py — integración: confirmación de carrito y generación de sale.order

Traducciones

# Exportar .pot
docker-compose exec -T odoo odoo -d odoo \
    -i website_sale_aplicoop \
    --i18n-export=/tmp/website_sale_aplicoop.pot \
    --stop-after-init

# Actualizar .po existentes
cd website_sale_aplicoop/i18n
for lang in es eu; do
    msgmerge -U ${lang}.po ../website_sale_aplicoop.pot
done

Ver docs/TRANSLATIONS.md para convenciones.

Repositorio