- 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>
196 lines
7.5 KiB
Markdown
196 lines
7.5 KiB
Markdown
# 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 (0–6) | 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)
|