Compare commits
No commits in common. "b14a6f84ed0ffe67e46aa4f266e2718c61d4374d" and "91cfb9e13735f8bf2856699dabb2712e9bdfda64" have entirely different histories.
b14a6f84ed
...
91cfb9e137
8 changed files with 386 additions and 398 deletions
87
README.md
87
README.md
|
|
@ -1,81 +1,10 @@
|
||||||
# Odoo Addons — Criptomart
|
# Oddo Addons Criptomart
|
||||||
|
|
||||||
Colección de addons Odoo 18.0 para Criptomart / Elika Bilbo. Incluye addons OCA incluidos como dependencias y addons custom desarrollados internamente.
|
## Addons
|
||||||
|
|
||||||
## Addons OCA (dependencias incluidas)
|
| Addon | Propósito | Estado |
|
||||||
|
|-------|-----------|--------|
|
||||||
Addons del ecosistema [OCA](https://github.com/OCA) incorporados al repositorio. No se modifican salvo hotfixes puntuales.
|
| [account_invoice_triple_discount_readonly](account_invoice_triple_discount_readonly/) | Fix para bug de descuentos acumulados | Alpha |
|
||||||
|
| [product_price_category_supplier](product_price_category_supplier/) | Gestión de categorías por proveedor | Alpha |
|
||||||
| Addon | Versión | Descripción |
|
| [product_sale_price_from_pricelist](product_sale_price_from_pricelist/) | Auto-cálculo precio venta desde compra | Alpha |
|
||||||
| ----- | ------- | ----------- |
|
| [website_sale_aplicoop](website_sale_aplicoop/) | Sistema completo de pedidos para grupos de consumo | Alpha |
|
||||||
| [account_invoice_triple_discount](account_invoice_triple_discount/) | 18.0.1.0.0 | Triple descuento en líneas de factura |
|
|
||||||
| [product_get_price_helper](product_get_price_helper/) | 18.0.1.1.0 | Helper para obtener precios de producto respetando tarifas |
|
|
||||||
| [product_main_seller](product_main_seller/) | 18.0.1.0.0 | Campo "proveedor principal" en producto |
|
|
||||||
| [product_origin_char](product_origin_char/) | 18.0.2.0.0 | Campo de texto libre para origen del producto |
|
|
||||||
| [product_price_category](product_price_category/) | 18.0.1.0.0 | Categoría de precio en producto + aplicación masiva vía tarifas |
|
|
||||||
| [purchase_triple_discount](purchase_triple_discount/) | 18.0.1.0.0 | Triple descuento en líneas de pedido de compra |
|
|
||||||
|
|
||||||
## Addons Custom
|
|
||||||
|
|
||||||
Addons desarrollados por Criptomart para necesidades específicas del proyecto.
|
|
||||||
|
|
||||||
| Addon | Versión | Descripción |
|
|
||||||
| ----- | ------- | ----------- |
|
|
||||||
| [account_invoice_triple_discount_readonly](account_invoice_triple_discount_readonly/) | 18.0.1.0.0 | Corrige bug de acumulación de descuentos y pone descuento total en solo lectura |
|
|
||||||
| [membership_expiry_reminder](membership_expiry_reminder/) | 18.0.1.0.0 | Recordatorio por email de membresías próximas a vencer |
|
|
||||||
| [portal_event_registration](portal_event_registration/) | 18.0.1.0.0 | Vista portal de inscripciones a eventos con adjuntos al chatter |
|
|
||||||
| [product_price_category_supplier](product_price_category_supplier/) | 18.0.1.0.0 | Categoría de precio por defecto en proveedor + actualización masiva de productos |
|
|
||||||
| [product_pricelist_total_margin](product_pricelist_total_margin/) | 18.0.1.2.0 | Margen aditivo (no compuesto) en tarifas encadenadas, con límites globales |
|
|
||||||
| [product_sale_price_from_pricelist](product_sale_price_from_pricelist/) | 18.0.2.7.0 | Calcula precio de venta desde último precio de compra vía tarifa configurable |
|
|
||||||
| [stock_picking_batch_custom](stock_picking_batch_custom/) | 18.0.1.0.0 | Columnas extra en operaciones detalladas de lotes: partner, categoría, recogido |
|
|
||||||
| [website_sale_aplicoop](website_sale_aplicoop/) | 18.0.1.9.0 | Sistema de pedidos colaborativos para grupos de consumo (reemplazo de Aplicoop) |
|
|
||||||
|
|
||||||
## Dependencias entre addons custom
|
|
||||||
|
|
||||||
```text
|
|
||||||
website_sale_aplicoop
|
|
||||||
└── product_sale_price_from_pricelist
|
|
||||||
└── product_pricelist_total_margin
|
|
||||||
└── product_price_category
|
|
||||||
└── product_main_seller
|
|
||||||
└── product_price_category_supplier
|
|
||||||
└── product_price_category
|
|
||||||
|
|
||||||
account_invoice_triple_discount_readonly
|
|
||||||
└── account_invoice_triple_discount
|
|
||||||
└── purchase_triple_discount
|
|
||||||
```
|
|
||||||
|
|
||||||
## Desarrollo
|
|
||||||
|
|
||||||
### Instalación / actualización de un addon
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker-compose exec -T odoo odoo -d odoo -u <nombre_addon> --stop-after-init
|
|
||||||
```
|
|
||||||
|
|
||||||
### Tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker-compose exec -T odoo odoo -d odoo --test-enable --stop-after-init -u <nombre_addon>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Linters
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Python
|
|
||||||
black . && isort . && flake8 . && pylint --load-plugins=pylint_odoo <addon>/
|
|
||||||
|
|
||||||
# JS
|
|
||||||
npx eslint <addon>/static/src/
|
|
||||||
```
|
|
||||||
|
|
||||||
Ver [docs/LINTERS_README.md](docs/LINTERS_README.md) para configuración completa.
|
|
||||||
|
|
||||||
### Traducciones
|
|
||||||
|
|
||||||
Ver [docs/TRANSLATIONS.md](docs/TRANSLATIONS.md).
|
|
||||||
|
|
||||||
## Documentación técnica
|
|
||||||
|
|
||||||
Ver carpeta [docs/](docs/) para documentación transversal (instalación, lazy loading, QWeb, etc.).
|
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,57 @@
|
||||||
# Documentación Técnica
|
# Documentación Técnica
|
||||||
|
|
||||||
Documentación transversal del proyecto (no específica de un addon individual).
|
Esta carpeta contiene documentación técnica y de referencia del proyecto.
|
||||||
|
|
||||||
## Configuración y desarrollo
|
## Contenido
|
||||||
|
|
||||||
- [LINTERS_README.md](LINTERS_README.md) — Configuración de herramientas de calidad: black, isort, flake8, pylint, eslint
|
### <20> Cambios Recientes
|
||||||
- [TRANSLATIONS.md](TRANSLATIONS.md) — Guía completa del sistema de traducciones (i18n/l10n)
|
|
||||||
- [QWEB_BEST_PRACTICES.md](QWEB_BEST_PRACTICES.md) — Buenas prácticas para templates QWeb (evitar errores comunes)
|
|
||||||
- [OCA_DOCUMENTATION.md](OCA_DOCUMENTATION.md) — Notas sobre integración con el ecosistema OCA
|
|
||||||
|
|
||||||
## Instalación
|
- **[RECENT_CHANGES.md](RECENT_CHANGES.md)** - 🆕 Resumen de todos los cambios recientes (Feb 2026)
|
||||||
|
|
||||||
- [INSTALACION_COMPLETA.md](INSTALACION_COMPLETA.md) — Proceso completo de instalación con Docker
|
### <20>🚀 Performance & Features (Nuevas)
|
||||||
- [RESUMEN_INSTALACION.md](RESUMEN_INSTALACION.md) — Resumen ejecutivo de instalación
|
|
||||||
|
|
||||||
## website_sale_aplicoop
|
- **[LAZY_LOADING_QUICK_START.md](LAZY_LOADING_QUICK_START.md)** - ⚡ Guía rápida (5 min) si solo necesitas lo esencial
|
||||||
|
- **[LAZY_LOADING_DOCS_INDEX.md](LAZY_LOADING_DOCS_INDEX.md)** - Índice centralizado de documentación de lazy loading (v18.0.1.3.0)
|
||||||
|
- **[LAZY_LOADING.md](LAZY_LOADING.md)** - Documentación técnica completa de lazy loading en website_sale_aplicoop
|
||||||
|
- **[UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md](UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md)** - Guía de actualización e instalación de lazy loading
|
||||||
|
|
||||||
- [LAZY_LOADING.md](LAZY_LOADING.md) — Documentación técnica completa de paginación de productos
|
### Configuración y Desarrollo
|
||||||
- [UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md](UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md) — Instrucciones de upgrade para la versión con lazy loading
|
|
||||||
- [CORRECCION_PRECIOS_IVA.md](CORRECCION_PRECIOS_IVA.md) — Correcciones de precios con IVA
|
|
||||||
- [TEST_MANUAL.md](TEST_MANUAL.md) — Guía de pruebas manuales
|
|
||||||
|
|
||||||
## Documentación principal
|
- **[LINTERS_README.md](LINTERS_README.md)** - Guía de herramientas de calidad de código (black, isort, flake8, pylint)
|
||||||
|
- **[TRANSLATIONS.md](TRANSLATIONS.md)** - Guía completa del sistema de traducciones (IMPORTANTE)
|
||||||
|
- **[INSTALACION_COMPLETA.md](INSTALACION_COMPLETA.md)** - Documentación detallada del proceso de instalación
|
||||||
|
- **[RESUMEN_INSTALACION.md](RESUMEN_INSTALACION.md)** - Resumen ejecutivo de la instalación
|
||||||
|
|
||||||
- [README.md](../README.md) — Tabla de todos los addons con versiones y descripción
|
### Resolución de Problemas
|
||||||
- [website_sale_aplicoop/README_DEV.md](../website_sale_aplicoop/README_DEV.md) — Documentación técnica del addon principal
|
|
||||||
- [website_sale_aplicoop/CHANGELOG.md](../website_sale_aplicoop/CHANGELOG.md) — Historial de cambios completo
|
- **[FINAL_SOLUTION_SUMMARY.md](FINAL_SOLUTION_SUMMARY.md)** - Solución definitiva para errores de templates QWeb en website_sale_aplicoop
|
||||||
|
- **[FIX_TEMPLATE_ERROR_SUMMARY.md](FIX_TEMPLATE_ERROR_SUMMARY.md)** - Resumen de correcciones de templates
|
||||||
|
- **[QWEB_BEST_PRACTICES.md](QWEB_BEST_PRACTICES.md)** - Mejores prácticas para templates QWeb (CRÍTICO)
|
||||||
|
- **[TEMPLATE_FIX_INDEX.md](TEMPLATE_FIX_INDEX.md)** - Índice de documentación de fixes de templates
|
||||||
|
- **[CORRECCION_PRECIOS_IVA.md](CORRECCION_PRECIOS_IVA.md)** - Correcciones relacionadas con precios e IVA
|
||||||
|
- **[TEST_MANUAL.md](TEST_MANUAL.md)** - Guía de tests manuales
|
||||||
|
|
||||||
|
## Documentación Principal
|
||||||
|
|
||||||
|
Para información general del proyecto y cómo empezar, ver:
|
||||||
|
|
||||||
|
- **[README.md](../README.md)** - Documentación principal del repositorio
|
||||||
|
- **[.github/copilot-instructions.md](../.github/copilot-instructions.md)** - Instrucciones para GitHub Copilot
|
||||||
|
|
||||||
|
## Documentación por Addon
|
||||||
|
|
||||||
|
Cada addon tiene su propio README:
|
||||||
|
|
||||||
|
### Addons Custom
|
||||||
|
- [account_invoice_triple_discount_readonly](../account_invoice_triple_discount_readonly/README.md)
|
||||||
|
- [product_price_category_supplier](../product_price_category_supplier/README.md)
|
||||||
|
- [product_sale_price_from_pricelist](../product_sale_price_from_pricelist/README.md)
|
||||||
|
- [website_sale_aplicoop](../website_sale_aplicoop/README.md)
|
||||||
|
|
||||||
|
### Addons OCA Modificados
|
||||||
|
- [account_invoice_triple_discount](../account_invoice_triple_discount/README.rst)
|
||||||
|
- [purchase_triple_discount](../purchase_triple_discount/README.rst)
|
||||||
|
- [product_origin](../product_origin/README.md)
|
||||||
|
- [product_get_price_helper](../product_get_price_helper/README.md)
|
||||||
|
- [product_main_seller](../product_main_seller/README.md)
|
||||||
|
- [product_price_category](../product_price_category/README.rst)
|
||||||
|
|
|
||||||
|
|
@ -1,70 +1,5 @@
|
||||||
# Changelog - Website Sale Aplicoop
|
# Changelog - Website Sale Aplicoop
|
||||||
|
|
||||||
## [18.0.1.9.0] - 2026-05-20
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- **Category sequence**: New `sequence` field on `product.category` for controlling product display order on the web shop. Migration adds the column with default 10.
|
|
||||||
- **Stock availability check on history orders**: When loading a historical order, products are validated against current stock (`virtual_available`). Out-of-stock items are flagged before adding to cart.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- **Cart disabled / redirected to /eskaera**: Standard `website_sale` cart is fully hidden — header cart icon removed, add-to-cart buttons removed from standard shop, `/cart` and `/shop` routes redirect to `/eskaera`. Customers interact exclusively through the group order flow.
|
|
||||||
- **Product card UI overhaul**: Improved responsive layout, placeholder image for products without photo, better accessibility markup, and professional styling for mobile.
|
|
||||||
- **Stock check uses `virtual_available`**: Replaces `qty_available` so forecasted quantity (including incoming moves) is considered.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Lint fixes: exception chaining (`raise ... from exc`), unused imports/vars, `disable attribute-string-redundant` annotations.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## [18.0.1.8.0] - 2026-04-08
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- **Clear cart button**: New button in the sidebar to empty the current group order cart in one click (`eskaera_clear_cart` route).
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- **Portal access restricted to consumer group**: Portal users can only see group orders belonging to their consumer group. Security rule `rule_group_order_company_read` updated with `user.share` guard.
|
|
||||||
- **Controller filtering by consumer group**: `eskaera_list` and `eskaera_shop` routes filter orders by the logged-in user's consumer group, not just by company.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Stale cart invalidation: carts linked to closed/cancelled group orders are cleared on next access.
|
|
||||||
- Home delivery draft flow: fixed state transition for home delivery orders.
|
|
||||||
- Auto-confirm `sale.order` on cutoff day: fixed off-by-one (`>` vs `>=`) in cutoff guard so orders on the cutoff day are correctly confirmed.
|
|
||||||
- Pickup dates frozen after cron confirm: cron no longer overwrites pickup dates on already-confirmed orders.
|
|
||||||
- Cron hardened against missing/invalid group order configurations.
|
|
||||||
- Cart layout: moved cart sidebar before product grid.
|
|
||||||
- i18n: added missing ES/EU translations for checkout, cart labels, Save Draft button, and weekday strings. Removed legacy `week_draft` translation keys.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## [18.0.1.7.0] - 2026-03-06
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- **Automatic batch picking creation**: After the cutoff cron confirms sale orders, picking batches are automatically created grouped by consumer group and pickup date.
|
|
||||||
- **Consumer group propagation**: `consumer_group_id` is now propagated directly from the `group.order` to linked `sale.order` records, eliminating manual assignment.
|
|
||||||
- **Out-of-stock blocking**: Add-to-cart button is hard-disabled for out-of-stock products. `allow_out_of_stock_order` flag on product is respected.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- **Cron auto-confirm sale orders**: Daily cron (`_cron_confirm_group_orders`) now confirms linked sale orders on the cutoff date instead of requiring manual action.
|
|
||||||
- **Order card meta extracted**: Order card metadata (dates, state badge) moved to a reusable `_eskaera_order_card_meta` template for cleaner rendering.
|
|
||||||
- Delivery toggle (reparto/envío a domicilio) wired directly from the cart sidebar.
|
|
||||||
- Pricing alignment: theoretical price and displayed price reconciled for orders with home delivery.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed broken `aplicoopShop` → `groupOrderShop` JS class reference.
|
|
||||||
- Ensured add-to-cart event listeners are re-attached after infinite scroll loads new products.
|
|
||||||
- i18n: translated weekday names (ES/EU).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## [18.0.1.6.0] - 2026-02-22
|
## [18.0.1.6.0] - 2026-02-22
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
||||||
|
|
@ -1,196 +1,336 @@
|
||||||
# Website Sale - Aplicoop
|
# Website Sale - Aplicoop
|
||||||
|
|
||||||
**Versión:** 18.0.1.9.0 | **Licencia:** AGPL-3 | **Autor:** Criptomart SL
|
**Author:** Criptomart
|
||||||
|
**License:** AGPL-3
|
||||||
|
**Maintainer:** Criptomart SL
|
||||||
|
|
||||||
Sistema de pedidos colaborativos para grupos de consumo. Reemplaza el legacy Aplicoop con una solución moderna integrada en Odoo 18.
|
## Summary
|
||||||
|
|
||||||
## Resumen de funcionalidades
|
Modern replacement for legacy Aplicoop - Cooperative group ordering system with separate carts and multi-language support.
|
||||||
|
|
||||||
- Gestión completa de órdenes de grupo (draft → confirmed → collected → invoiced → completed)
|
## Description
|
||||||
- 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
|
Website Sale Aplicoop provides a complete group ordering system designed for cooperative consumption groups. It replaces the legacy Aplicoop system with a modern, scalable solution where customers organize collaborative orders, manage group memberships, and handle separate shopping carts. Perfect for food cooperatives, buying groups, and collective purchasing organizations.
|
||||||
|
|
||||||
### `group.order`
|
## Features
|
||||||
|
|
||||||
Orden de grupo central. Contiene toda la lógica de ciclo de vida.
|
- ✅ Group order management with full lifecycle (draft → confirmed → completed)
|
||||||
|
- ✅ Separate shopping carts per order group
|
||||||
|
- ✅ Group membership tracking with active/inactive states
|
||||||
|
- ✅ Order collection and cutoff dates with validation
|
||||||
|
- ✅ Pickup day configuration and fulfillment tracking
|
||||||
|
- ✅ Multi-language support (ES, PT, GL, CA, EU, FR, IT)
|
||||||
|
- ✅ Partner location management for group coordination
|
||||||
|
- ✅ Product ecosystem integration (ribbons, pricing, margins)
|
||||||
|
- ✅ Order state transitions with email notifications
|
||||||
|
- ✅ Delivery tracking and group order fulfillment
|
||||||
|
- ✅ Financial tracking per group member
|
||||||
|
- ✅ Automatic translation of UI elements
|
||||||
|
- ✅ **Lazy Loading**: Configurable product pagination for fast page loads
|
||||||
|
|
||||||
**Campos principales:**
|
## Installation
|
||||||
|
|
||||||
| Campo | Tipo | Descripción |
|
1. Place addon in Odoo addons folder: `/addons/website_sale_aplicoop`
|
||||||
| ----- | ---- | ----------- |
|
2. Activate developer mode
|
||||||
| `name` | Char | Identificador de la orden |
|
3. Go to **Apps** → **Update Apps List**
|
||||||
| `consumer_group_id` | Many2one → `res.partner` | Grupo de consumo |
|
4. Search for "Website Sale - Aplicoop"
|
||||||
| `state` | Selection | draft / confirmed / collected / invoiced / completed / cancelled |
|
5. Click **Install**
|
||||||
| `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:**
|
### Requirements
|
||||||
|
|
||||||
- `_get_products_for_group_order()` — catálogo efectivo aplicando whitelist + blacklist
|
- Odoo 18.0+
|
||||||
- `_get_products_paginated(page, per_page)` — paginación para lazy loading
|
- Website module
|
||||||
- `_cron_confirm_group_orders()` — auto-confirma órdenes pasado el cutoff y crea lotes de picking
|
- Sale module
|
||||||
- `_cron_update_dates()` — recalcula fechas de entrega/recogida diariamente
|
- Product module
|
||||||
|
- Account module
|
||||||
|
|
||||||
### `group.order.slot` *(en desarrollo)*
|
### Dependencies
|
||||||
|
|
||||||
Franjas horarias de recogida para una orden de grupo.
|
```
|
||||||
|
- base
|
||||||
|
- web
|
||||||
|
- website
|
||||||
|
- sale
|
||||||
|
- product
|
||||||
|
- account
|
||||||
|
```
|
||||||
|
|
||||||
| Campo | Tipo | Descripción |
|
## Usage
|
||||||
| ----- | ---- | ----------- |
|
|
||||||
| `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
|
### Administrator Setup
|
||||||
|
|
||||||
|
#### 1. Create a Group Order
|
||||||
|
|
||||||
|
1. Go to **Website Sale** → **Group Orders** (or **Coops** → **Órdenes de Grupo**)
|
||||||
|
2. Click **Create**
|
||||||
|
3. Fill in:
|
||||||
|
- **Name**: e.g., "Weekly Cooperative Order #5"
|
||||||
|
- **Group**: Select the cooperative group
|
||||||
|
- **Collection Date**: When orders will be collected
|
||||||
|
- **Cutoff Date**: Last moment to add items
|
||||||
|
- **Pickup Date**: When group members collect their orders
|
||||||
|
4. Save
|
||||||
|
|
||||||
|
#### 2. Configure Pickup Dates
|
||||||
|
|
||||||
|
1. Go to **Settings** → **Website** → **Shop Settings**
|
||||||
|
2. Configure **Pickup Days**: Define which days are available
|
||||||
|
3. Set **Group Settings**: Default locations, delivery partners
|
||||||
|
|
||||||
|
#### 3. Add Group Members
|
||||||
|
|
||||||
|
1. Open a Group Order
|
||||||
|
2. In the **Members** tab, click **Add**
|
||||||
|
3. Select partner(s)
|
||||||
|
4. Set active/inactive status
|
||||||
|
5. Save
|
||||||
|
|
||||||
|
### Customer Experience
|
||||||
|
|
||||||
|
#### For Group Members on Website
|
||||||
|
|
||||||
|
1. **Browse Products**: Members see products with eco-ribbons, pricing, margin info
|
||||||
|
2. **Add to Cart**: Select items (cart is separate per group order)
|
||||||
|
3. **Review Cart**: See order summary before cutoff date
|
||||||
|
4. **Submit Order**: Confirm before cutoff time
|
||||||
|
5. **Receive Notification**: Get email with pickup details
|
||||||
|
6. **Pickup**: Collect order on designated pickup date
|
||||||
|
|
||||||
|
#### Order Workflow
|
||||||
|
|
||||||
|
```
|
||||||
|
Draft → Confirmed → Collected → Invoiced → Completed
|
||||||
|
↓
|
||||||
|
Cancelled (if member opts out)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Basic Configuration
|
||||||
|
|
||||||
|
**Required:**
|
||||||
|
1. Create group orders with collection/cutoff/pickup dates
|
||||||
|
2. Assign group members to orders
|
||||||
|
3. Set available pickup dates
|
||||||
|
|
||||||
|
**Optional:**
|
||||||
|
1. Configure custom email templates
|
||||||
|
2. Set up product-specific group restrictions
|
||||||
|
3. Customize group order report
|
||||||
|
|
||||||
|
### Multi-Language Setup
|
||||||
|
|
||||||
|
The addon automatically translates:
|
||||||
|
- Interface elements
|
||||||
|
- Form labels
|
||||||
|
- Report headers
|
||||||
|
- Email notifications
|
||||||
|
|
||||||
|
**Supported Languages:** ES, PT, GL, CA, EU, FR, IT
|
||||||
|
|
||||||
|
**Translations are managed in:**
|
||||||
|
- `i18n/[language].po` files
|
||||||
|
- Auto-extracted from templates
|
||||||
|
- See `docs/TRANSLATION_CONVENTIONS.md` for translation patterns
|
||||||
|
|
||||||
|
### Website Customization
|
||||||
|
|
||||||
|
Edit templates in: `views/website_templates.xml`
|
||||||
|
|
||||||
|
Key customizable sections:
|
||||||
|
- `eskaera_page`: Main group order display
|
||||||
|
- `eskaera_details`: Order details view
|
||||||
|
- `member_cart`: Individual member cart interface
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
### Core Models
|
||||||
|
|
||||||
|
**`group.order`** (Main group order)
|
||||||
|
- `name` (Char): Order identifier
|
||||||
|
- `group_id` (Many2one): Link to group
|
||||||
|
- `state` (Selection): draft/confirmed/collected/invoiced/completed/cancelled
|
||||||
|
- `collection_date` (Date): When group collects
|
||||||
|
- `cutoff_date` (Datetime): Last moment to order
|
||||||
|
- `pickup_date` (Date): Member pickup day
|
||||||
|
- `line_ids` (One2many): Order lines
|
||||||
|
- `member_ids` (One2many): Group members
|
||||||
|
- `active` (Boolean): Soft delete
|
||||||
|
|
||||||
|
**`group.order.line`** (Order items per member)
|
||||||
|
- `order_id` (Many2one): Parent group order
|
||||||
|
- `member_id` (Many2one): Which group member
|
||||||
|
- `product_id` (Many2one): Ordered product
|
||||||
|
- `quantity` (Float): Amount ordered
|
||||||
|
- `unit_price` (Float): Price per unit
|
||||||
|
- `subtotal` (Float): Computed (qty × price)
|
||||||
|
|
||||||
|
**`group.partner`** (Group member tracking)
|
||||||
|
- `partner_id` (Many2one): Odoo partner
|
||||||
|
- `group_id` (Many2one): Which group
|
||||||
|
- `active` (Boolean): Active member status
|
||||||
|
- `role` (Selection): admin/member
|
||||||
|
|
||||||
|
### Extended Models
|
||||||
|
|
||||||
**`product.template`**
|
**`product.template`**
|
||||||
|
- `group_order_allowed` (Boolean): Can be in group orders
|
||||||
- `main_seller_id` — proveedor principal (de `product_main_seller`)
|
- `eco_ribbon_id` (Many2one): Environmental ribbon
|
||||||
- `sequence` en `product.category` — orden de visualización en la tienda web
|
- `margin_type_id` (Many2one): Pricing margin
|
||||||
|
|
||||||
**`sale.order`**
|
**`sale.order`**
|
||||||
|
- `group_order_id` (Many2one): Parent group order (if applicable)
|
||||||
|
|
||||||
- `group_order_id` — enlace a la orden de grupo
|
### Views & Templates
|
||||||
- `consumer_group_id` — propagado desde `group_order_id`
|
|
||||||
- `pickup_slot_label` — etiqueta legible de la franja de recogida
|
|
||||||
|
|
||||||
**`stock.picking`**
|
**Backend Views:**
|
||||||
|
- `group.order` list/form views
|
||||||
|
- `group.order.line` inline form
|
||||||
|
- `group.partner` configuration view
|
||||||
|
|
||||||
- Extensión para agrupación por grupo de consumo en lotes de picking
|
**Frontend Templates:**
|
||||||
|
- `eskaera_page`: Main group order display
|
||||||
|
- `eskaera_details`: Order details/summary
|
||||||
|
- `member_cart`: Individual cart interface
|
||||||
|
- `group_members`: Member list view
|
||||||
|
|
||||||
## Controladores
|
## Integration Points
|
||||||
|
|
||||||
Todos los endpoints viven bajo `/eskaera/`:
|
This addon integrates with:
|
||||||
|
|
||||||
| Ruta | Descripción |
|
- **Product Modules**
|
||||||
| ---- | ----------- |
|
- `product_eco_ribbon` - Eco-friendly product indicators
|
||||||
| `GET /eskaera` | Lista de órdenes de grupo activas del usuario |
|
- `product_margin_type` - Dynamic product pricing
|
||||||
| `GET /eskaera/<order_id>` | Tienda de la orden (con lazy loading) |
|
- `product_pricing_margins` - Cost management
|
||||||
| `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`.
|
- **Website/E-commerce**
|
||||||
|
- `elika_bilbo_website_theme` - Custom website theme
|
||||||
|
- `website_sale` - Core shop functionality
|
||||||
|
- `website_legal_es` - Legal compliance (Spanish)
|
||||||
|
|
||||||
**Seguridad portal:** los usuarios solo ven órdenes de su `consumer_group_id`. La regla `rule_group_order_company_read` incluye guardia `user.share`.
|
- **Sales/Accounting**
|
||||||
|
- `sale` - Sales order generation
|
||||||
|
- `account` - Invoicing
|
||||||
|
|
||||||
## Templates QWeb
|
## Testing
|
||||||
|
|
||||||
| Template | Descripción |
|
Run tests with:
|
||||||
| -------- | ----------- |
|
|
||||||
| `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
|
```bash
|
||||||
docker-compose exec -T odoo odoo -d odoo -u website_sale_aplicoop --stop-after-init
|
cd /home/snt/Documentos/lab/odoo/kidekoop/odoo-addons
|
||||||
|
python -m pytest website_sale_aplicoop/tests/ -v
|
||||||
```
|
```
|
||||||
|
|
||||||
Para migraciones con cambios de esquema (e.g., nuevas columnas):
|
**Test Coverage:**
|
||||||
|
- ✅ Group order creation/deletion
|
||||||
|
- ✅ Member management
|
||||||
|
- ✅ Order line addition/removal
|
||||||
|
- ✅ State transitions
|
||||||
|
- ✅ Cutoff date validation
|
||||||
|
- ✅ Pickup date assignment
|
||||||
|
- ✅ Translation extraction (7 languages)
|
||||||
|
- ✅ Website template rendering
|
||||||
|
|
||||||
```bash
|
## Known Limitations
|
||||||
docker-compose exec -T odoo odoo -d odoo --update website_sale_aplicoop --stop-after-init
|
|
||||||
```
|
|
||||||
|
|
||||||
## Tests
|
- Group orders are company-specific
|
||||||
|
- Cannot change pickup date after order is confirmed
|
||||||
|
- Members cannot modify orders after cutoff
|
||||||
|
- Automatic invoicing must be triggered manually
|
||||||
|
|
||||||
```bash
|
## Changelog
|
||||||
docker-compose exec -T odoo odoo -d odoo --test-enable --stop-after-init -u website_sale_aplicoop
|
|
||||||
```
|
|
||||||
|
|
||||||
Los tests están en `tests/`:
|
### 18.0.1.3.1 (2026-02-18)
|
||||||
|
- **Date Calculation Fixes (Critical)**:
|
||||||
|
- Fixed `_compute_cutoff_date` logic: Changed `days_ahead <= 0` to `days_ahead < 0` to allow cutoff_date to be the same day as today
|
||||||
|
- Enabled `store=True` for `delivery_date` field to persist calculated values and enable database filtering
|
||||||
|
- Added constraint `_check_cutoff_before_pickup` to validate that pickup_day >= cutoff_day in weekly orders
|
||||||
|
- Added `@api.onchange` methods for immediate UI feedback when changing cutoff_day or pickup_day
|
||||||
|
- **Automatic Date Updates**:
|
||||||
|
- Created daily cron job `_cron_update_dates` to automatically recalculate dates for active orders
|
||||||
|
- Ensures computed dates stay current as time passes
|
||||||
|
- **UI Improvements**:
|
||||||
|
- Added "Calculated Dates" section in form view showing readonly cutoff_date, pickup_date, and delivery_date
|
||||||
|
- Improved visibility of automatically calculated dates for administrators
|
||||||
|
- **Testing**:
|
||||||
|
- Added 6 regression tests with `@tagged('post_install', 'date_calculations')`:
|
||||||
|
- `test_cutoff_same_day_as_today_bug_fix`: Validates cutoff can be today
|
||||||
|
- `test_delivery_date_stored_correctly`: Ensures delivery_date persistence
|
||||||
|
- `test_constraint_cutoff_before_pickup_invalid`: Tests invalid configurations are rejected
|
||||||
|
- `test_constraint_cutoff_before_pickup_valid`: Tests valid configurations work
|
||||||
|
- `test_all_weekday_combinations_consistency`: Tests all 49 date combinations
|
||||||
|
- `test_cron_update_dates_executes`: Validates cron job execution
|
||||||
|
- **Documentation**:
|
||||||
|
- Documented that this is a more robust fix than v18.0.1.2.0, addressing edge cases in date calculations
|
||||||
|
|
||||||
- `test_group_order.py` — ciclo de vida básico, fechas, cron
|
### 18.0.1.3.0 (2026-02-16)
|
||||||
- `test_product_discovery.py` — whitelist/blacklist (productos, proveedores, categorías)
|
- **Performance**: Lazy loading of products for faster page loads
|
||||||
- `test_confirm_eskaera.py` — integración: confirmación de carrito y generación de sale.order
|
- Configurable product pagination (default: 20 per page)
|
||||||
|
- New Settings: Enable Lazy Loading, Products Per Page
|
||||||
|
- Page 1: 500-800ms load time (vs 10-20s before)
|
||||||
|
- Subsequent pages: 200-400ms via AJAX
|
||||||
|
- New endpoint: `GET /eskaera/<order_id>/load-page?page=N`
|
||||||
|
- **Templates**: Split product rendering into reusable template
|
||||||
|
- New: `eskaera_shop_products` template
|
||||||
|
- Backend: `_get_products_paginated()` in group_order model
|
||||||
|
- **JavaScript**: Load More button with event handling
|
||||||
|
- `_attachLoadMoreListener()` for AJAX pagination
|
||||||
|
- Spinner during load (button disabled + "Loading..." text)
|
||||||
|
- Re-attach event listeners for new products
|
||||||
|
- Auto-hide button when no more products
|
||||||
|
- Documentation: Added `docs/LAZY_LOADING.md` with full technical details
|
||||||
|
|
||||||
## Traducciones
|
### 18.0.1.2.0 (2026-02-02)
|
||||||
|
- UI Improvements:
|
||||||
|
- Increased cart text size (2x) for better readability
|
||||||
|
- Increased cart icon sizes (1.2rem) with proper button proportions
|
||||||
|
- Enlarged "Save as Draft" button in checkout (2x text and icon)
|
||||||
|
- Date Calculation Fixes:
|
||||||
|
- Fixed pickup_date calculation (was adding extra week incorrectly)
|
||||||
|
- Simplified pickup_date computation logic
|
||||||
|
- Display Enhancements:
|
||||||
|
- Added delivery_date display to all order pages
|
||||||
|
- Improved date field visibility on order cards and product pages
|
||||||
|
|
||||||
```bash
|
### 18.0.1.0.0 (2024-12-20)
|
||||||
# Exportar .pot
|
- Initial release
|
||||||
docker-compose exec -T odoo odoo -d odoo \
|
- Core group order functionality
|
||||||
-i website_sale_aplicoop \
|
- Multi-language translation support
|
||||||
--i18n-export=/tmp/website_sale_aplicoop.pot \
|
- Complete member management
|
||||||
--stop-after-init
|
- Order state machine implementation
|
||||||
|
|
||||||
# Actualizar .po existentes
|
### 18.0.1.1.0 (2025-01-10)
|
||||||
cd website_sale_aplicoop/i18n
|
- Fixed translation extraction for "Pickup day" and "Cutoff day"
|
||||||
for lang in es eu; do
|
- Improved QWeb template for better performance
|
||||||
msgmerge -U ${lang}.po ../website_sale_aplicoop.pot
|
- Added comprehensive documentation
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
Ver [docs/TRANSLATIONS.md](../docs/TRANSLATIONS.md) para convenciones.
|
## Support
|
||||||
|
|
||||||
## Repositorio
|
For issues, feature requests, or contributions:
|
||||||
|
- **Repository**: https://git.criptomart.net/KideKoop/kidekoop/odoo-addons
|
||||||
|
- **Main Documentation**: `/docs/` folder (transversal docs)
|
||||||
|
- **Addon Documentation**: This README + `/docs/ODOO18_TRANSLATIONS_LEARNINGS.md`
|
||||||
|
- **Maintainer**: Criptomart SL
|
||||||
|
|
||||||
- Repo: <https://git.criptomart.net/criptomart/addons-cm>
|
## Documentation References
|
||||||
- Changelog: [CHANGELOG.md](CHANGELOG.md)
|
|
||||||
|
- **Translation Patterns**: See `docs/TRANSLATION_CONVENTIONS.md`
|
||||||
|
- **Translation Examples**: See `docs/TRANSLATION_EXAMPLES.md`
|
||||||
|
- **Odoo 18 Translation Guide**: See `docs/ODOO18_TRANSLATIONS_LEARNINGS.md`
|
||||||
|
- **Project Architecture**: See `docs/ARCHITECTURE.md`
|
||||||
|
|
||||||
|
## Related Modules
|
||||||
|
|
||||||
|
- `product_eco_ribbon` - Product environmental classification
|
||||||
|
- `product_margin_type` - Dynamic product pricing
|
||||||
|
- `product_pricing_margins` - Complete pricing system
|
||||||
|
- `elika_bilbo_website_theme` - Custom website theme
|
||||||
|
- `website_legal_es` - Legal compliance
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Version:** 18.0.1.3.1
|
||||||
|
**Odoo:** 18.0+
|
||||||
|
**License:** AGPL-3
|
||||||
|
**Maintainer:** Criptomart
|
||||||
|
**Repository:** https://git.criptomart.net/criptomart/addons-cm
|
||||||
|
|
|
||||||
|
|
@ -23,15 +23,7 @@ class GroupOrderSlot(models.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
weekday = fields.Selection(
|
weekday = fields.Selection(
|
||||||
[
|
[(str(i), str(i)) for i in range(7)],
|
||||||
("0", "Monday"),
|
|
||||||
("1", "Tuesday"),
|
|
||||||
("2", "Wednesday"),
|
|
||||||
("3", "Thursday"),
|
|
||||||
("4", "Friday"),
|
|
||||||
("5", "Saturday"),
|
|
||||||
("6", "Sunday"),
|
|
||||||
],
|
|
||||||
string="Weekday",
|
string="Weekday",
|
||||||
required=True,
|
required=True,
|
||||||
help="Day of week for this slot (0=Monday)",
|
help="Day of week for this slot (0=Monday)",
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,3 @@ access_group_order_user,group.order user,model_group_order,website_sale_aplicoop
|
||||||
access_group_order_manager,group.order manager,model_group_order,website_sale_aplicoop.group_group_order_manager,1,1,1,1
|
access_group_order_manager,group.order manager,model_group_order,website_sale_aplicoop.group_group_order_manager,1,1,1,1
|
||||||
access_group_order_portal,group.order portal,model_group_order,base.group_portal,1,0,0,0
|
access_group_order_portal,group.order portal,model_group_order,base.group_portal,1,0,0,0
|
||||||
access_product_supplierinfo_portal,product.supplierinfo portal,product.model_product_supplierinfo,base.group_portal,1,0,0,0
|
access_product_supplierinfo_portal,product.supplierinfo portal,product.model_product_supplierinfo,base.group_portal,1,0,0,0
|
||||||
access_group_order_slot_base,group.order.slot base,model_group_order_slot,,1,1,1,0
|
|
||||||
access_group_order_slot_manager,group.order.slot manager,model_group_order_slot,website_sale_aplicoop.group_group_order_manager,1,1,1,1
|
|
||||||
|
|
|
||||||
|
|
|
@ -36,14 +36,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-img-cover {
|
.product-img-cover {
|
||||||
height: 120px;
|
max-height: 120px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border-radius: 8px 8px 0 0;
|
border-radius: 8px 8px 0 0;
|
||||||
box-shadow: 0 2px 8px rgba(40, 39, 39, 0.09);
|
box-shadow: 0 2px 8px rgba(40, 39, 39, 0.09);
|
||||||
background: #f3f3f3;
|
background: #f3f3f3;
|
||||||
display: block;
|
display: block;
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-card .card-body {
|
.product-card .card-body {
|
||||||
|
|
@ -51,7 +50,7 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
padding: 0.4rem 0.55rem 0.5rem;
|
padding: 0.6rem 0.7rem 0.7rem 0.7rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: linear-gradient(135deg, rgba(0, 123, 255, 0.07) 0%, rgba(0, 123, 255, 0.04) 100%);
|
background: linear-gradient(135deg, rgba(0, 123, 255, 0.07) 0%, rgba(0, 123, 255, 0.04) 100%);
|
||||||
transition: background 0.3s;
|
transition: background 0.3s;
|
||||||
|
|
@ -68,13 +67,13 @@
|
||||||
|
|
||||||
.product-card .card-title {
|
.product-card .card-title {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
margin: 0 0 0.1rem 0;
|
margin: 0 0 0.15rem 0;
|
||||||
min-height: auto;
|
min-height: auto;
|
||||||
display: block;
|
display: block;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
font-size: 1rem !important;
|
font-size: 1.08rem !important;
|
||||||
line-height: 1.15;
|
line-height: 1.1;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #1a202c;
|
color: #1a202c;
|
||||||
|
|
@ -82,9 +81,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-card .card-text {
|
.product-card .card-text {
|
||||||
margin-bottom: 0.08rem;
|
margin-bottom: 0.12rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 0.95rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-card .card-text strong {
|
.product-card .card-text strong {
|
||||||
|
|
@ -98,19 +97,19 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #4a5568;
|
color: #4a5568;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
margin-bottom: 0.08rem;
|
margin-bottom: 0.12rem;
|
||||||
font-size: 0.85rem !important;
|
font-size: 0.92rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-tags {
|
.product-tags {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 0.12rem;
|
gap: 0.18rem;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 1rem !important;
|
font-size: 1.1rem !important;
|
||||||
margin: 0 0 0.08rem 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,15 +129,15 @@
|
||||||
|
|
||||||
.card-body p.card-text {
|
.card-body p.card-text {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 0.35rem;
|
margin-bottom: 0.6rem;
|
||||||
min-height: 1.5rem;
|
min-height: 1.7rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background-color: var(--primary-color, #007bff);
|
background-color: var(--primary-color, #007bff);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border-radius: 0.18rem;
|
border-radius: 0.18rem;
|
||||||
font-size: 0.98rem;
|
font-size: 1.05rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-body p.card-text strong {
|
.card-body p.card-text strong {
|
||||||
|
|
@ -161,73 +160,53 @@
|
||||||
.product-img-placeholder {
|
.product-img-placeholder {
|
||||||
height: 120px;
|
height: 120px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
object-fit: cover;
|
||||||
border-radius: 8px 8px 0 0;
|
border-radius: 8px 8px 0 0;
|
||||||
background: #f3f3f3;
|
background: #f3f3f3
|
||||||
display: flex;
|
url('data:image/svg+xml;utf8,<svg width="120" height="120" xmlns="http://www.w3.org/2000/svg"><rect width="100%25" height="100%25" fill="%23f3f3f3"/><text x="50%25" y="50%25" dominant-baseline="middle" text-anchor="middle" fill="%23cccccc" font-size="16">Sin imagen</text></svg>')
|
||||||
align-items: center;
|
no-repeat center center;
|
||||||
justify-content: center;
|
display: block;
|
||||||
flex-shrink: 0;
|
|
||||||
color: #ccc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Responsive: tablet (≤768px) — compresión moderada */
|
/* Responsive: mejorar altura y espaciado en móvil */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 600px) {
|
||||||
.product-card .product-image,
|
|
||||||
.product-img-cover,
|
|
||||||
.product-img-fixed,
|
|
||||||
.product-img-placeholder {
|
|
||||||
height: 90px;
|
|
||||||
}
|
|
||||||
.product-card .card-body {
|
|
||||||
padding: 0.35rem 0.45rem 0.4rem;
|
|
||||||
}
|
|
||||||
.card-body p.card-text {
|
|
||||||
margin-bottom: 0.25rem;
|
|
||||||
min-height: 1.4rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive: móvil (≤480px) — compresión máxima */
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
.product-card {
|
.product-card {
|
||||||
padding: 0.15rem;
|
padding: 0.25rem;
|
||||||
border-radius: 6px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
.product-card .product-image,
|
.product-card .product-image,
|
||||||
.product-img-cover,
|
.product-img-cover,
|
||||||
.product-img-fixed,
|
.product-img-fixed,
|
||||||
.product-img-placeholder {
|
.product-img-placeholder {
|
||||||
height: 60px;
|
height: 70px;
|
||||||
border-radius: 5px 5px 0 0;
|
max-height: 70px;
|
||||||
|
min-height: 70px;
|
||||||
|
border-radius: 6px 6px 0 0;
|
||||||
}
|
}
|
||||||
.product-card .card-body {
|
.product-card .card-body {
|
||||||
padding: 0.25rem 0.3rem 0.3rem;
|
padding: 0.4rem 0.4rem 0.5rem 0.4rem;
|
||||||
}
|
}
|
||||||
.product-card .card-title {
|
.product-card .card-title {
|
||||||
font-size: 0.82rem !important;
|
font-size: 0.98rem !important;
|
||||||
line-height: 1.15;
|
margin-bottom: 0.08rem;
|
||||||
margin-bottom: 0.05rem;
|
|
||||||
}
|
}
|
||||||
.product-card .card-text {
|
.product-card .card-text {
|
||||||
font-size: 0.8rem;
|
font-size: 0.92rem;
|
||||||
}
|
}
|
||||||
.badge-km {
|
.badge-km {
|
||||||
font-size: 0.55rem !important;
|
font-size: 0.58rem !important;
|
||||||
padding: 0.1rem 0.18rem !important;
|
padding: 0.13rem 0.22rem !important;
|
||||||
}
|
}
|
||||||
.product-tags {
|
.product-tags {
|
||||||
font-size: 0.82rem !important;
|
font-size: 0.95rem !important;
|
||||||
gap: 0.08rem;
|
|
||||||
margin-bottom: 0.05rem;
|
|
||||||
}
|
}
|
||||||
.card-body p.card-text {
|
.card-body p.card-text {
|
||||||
min-height: 1rem;
|
min-height: 1.1rem;
|
||||||
font-size: 0.85rem;
|
font-size: 0.95rem;
|
||||||
margin-bottom: 0.2rem;
|
margin-bottom: 0.3rem;
|
||||||
}
|
}
|
||||||
.product-card .product-supplier {
|
.product-card .product-supplier {
|
||||||
font-size: 0.72rem !important;
|
font-size: 0.82rem !important;
|
||||||
margin-bottom: 0.05rem;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,20 +92,6 @@
|
||||||
<field name="excluded_product_ids" widget="many2many_tags" help="Products explicitly excluded from this order (blacklist has absolute priority over inclusions)"/>
|
<field name="excluded_product_ids" widget="many2many_tags" help="Products explicitly excluded from this order (blacklist has absolute priority over inclusions)"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<notebook>
|
|
||||||
<page string="Pickup Slots" name="pickup_slots">
|
|
||||||
<field name="pickup_slot_ids">
|
|
||||||
<list editable="bottom">
|
|
||||||
<field name="sequence" widget="handle"/>
|
|
||||||
<field name="weekday"/>
|
|
||||||
<field name="label" placeholder="e.g. Morning slot"/>
|
|
||||||
<field name="start_hour" widget="float_time"/>
|
|
||||||
<field name="end_hour" widget="float_time"/>
|
|
||||||
<field name="active"/>
|
|
||||||
</list>
|
|
||||||
</field>
|
|
||||||
</page>
|
|
||||||
</notebook>
|
|
||||||
</sheet>
|
</sheet>
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue