addons-cm/website_sale_aplicoop/README_DEV.md
GitHub Copilot a4410b9b9e [DOC] actualizar y sincronizar documentación de todos los addons
- README.md: reescrito con tabla completa de los 14 addons (6 OCA + 8 custom),
  versiones actuales, árbol de dependencias y comandos de desarrollo
- docs/README.md: simplificado a índice limpio, eliminadas referencias rotas
- website_sale_aplicoop/CHANGELOG.md: añadidas versiones 1.7.0, 1.8.0 y 1.9.0
  con los cambios agrupados por temática desde el último registro (1.6.0)
- website_sale_aplicoop/README_DEV.md: reescrito para reflejar v1.9.0 —
  modelos actuales (group.order.slot), controladores /eskaera, catálogo
  whitelist/blacklist, lazy loading, crons y árbol de dependencias

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 16:27:17 +02:00

196 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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; tienda estándar de `website_sale` deshabilitada
- 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 `/cart` y `/shop` de `website_sale` están redirigidas a `/eskaera`.
**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
## Control del catálogo
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
```text
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
```bash
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):
```bash
docker-compose exec -T odoo odoo -d odoo --update website_sale_aplicoop --stop-after-init
```
## Tests
```bash
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
```bash
# 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](../docs/TRANSLATIONS.md) para convenciones.
## Repositorio
- Repo: <https://git.criptomart.net/criptomart/addons-cm>
- Changelog: [CHANGELOG.md](CHANGELOG.md)