# ⚠️ Addons OCA Originales y OCB (Odoo)

No modificar el directorio de fuentes de OCB (`ocb/`) ni los siguientes addons OCA originales:

- `product_main_seller`
- `account_invoice_triple_discount`
- `product_get_price_helper`
- `product_price_category`
- `purchase_triple_discount`
- `base_bank_from_iban`
- `l10n_es_partner`

Estos módulos y el core de Odoo (OCB) solo están para referencia y herencia de nuestros addons custom. Cualquier cambio debe hacerse en los addons propios, nunca en los OCA originales ni en el core OCB.

# AI Agent Skills & Prompt Guidance

Para máxima productividad y calidad, los agentes AI deben seguir estas pautas y consultar los archivos de skills detallados:

## 1. Skills por Lenguaje/Stack

- **Python/Odoo ORM**: `.github/skills/python.md`
- **XML/Odoo Views**: `.github/skills/xml.md`
- **HTML/CSS/QWeb**: `.github/skills/html_css.md`
- **JavaScript/Odoo Web**: `.github/skills/javascript.md`

## 2. Ejecución de tests

Siempre usa:

```bash
docker-compose run odoo odoo -d odoo --test-enable --stop-after-init -u addon_name
```

> NOTA: Usa `docker-compose run` (no `exec`) para evitar efectos de caché.

## 3. Patrones y convenciones clave

- Sigue OCA guidelines y skills.
- No pongas lógica en QWeb, solo acceso a datos preparados en el controller.
- Usa `main_seller_id` en vez de `default_supplier_id` en product_main_seller.
- Para bulk updates, prefiere `search().write()` sobre loops.

## 4. Errores comunes a evitar

- No uses `_()` en definiciones de campos, solo en métodos ejecutables.
- No pongas lógica de negocio en JavaScript, solo en Python.
- No uses estilos inline en QWeb salvo casos justificados.

## 5. Documentación y referencias

- Consulta este archivo para patrones globales.
- Consulta los archivos de skills para detalles por lenguaje.
- Para troubleshooting de templates, revisa `docs/QWEB_BEST_PRACTICES.md` y `docs/FINAL_SOLUTION_SUMMARY.md`.
- **Para documentación de addons**: Sigue los estándares OCA descritos en `docs/OCA_DOCUMENTATION.md`
    - Estructura de directorios readme/ con fragmentos .rst
    - Créditos: Criptomart (autor) + Elika Bilbo (financiador)
    - Logo: CriptoMart en `static/description/icon.png` (ver `docs/LOGO_INSTRUCTIONS.md`)

# GitHub Copilot Instructions - Kidekoop Addons Custom

## Project Overview

Este repositorio contiene addons personalizados y modificados de Odoo 18.0. El proyecto combina:

- **OCB (Odoo Community Backports)**: Base de Odoo 18.0 community en `/ocb`
- **Addons OCA**: Módulos de la comunidad OCA de los que heredan nuestros addons, modificados para adaptarse a nuestras necesidades
- **Addons Custom**: Módulos desarrollados por este proyecto

## Architecture & Stack

- **Odoo Version**: 18.0 (OCB)
- **Python Version**: 3.10+
- **Framework**: Odoo ORM
- **Deployment**: Docker Compose
- **DB**: PostgreSQL
- **Languages**: Python, XML, JavaScript, QWeb

## Code Standards

### Python Style

- Seguir **OCA guidelines** estrictamente
- Usar **black** para formateo (configurado en `pyproject.toml`)
- **isort** para ordenar imports (perfil black)
- **flake8** para linting
- **pylint** con pylint-odoo para verificaciones específicas de Odoo
- Pre-commit hooks activos (ver `.pre-commit-config.yaml`)

### Odoo Conventions

- **Model names**: snake_case con punto (`product.price.category`)
- **Class names**: PascalCase (`ProductPriceCategory`)
- **File names**: snake_case (`product_price_category.py`)
- **XML IDs**: módulo.nombre_descriptivo (`product_price_category.view_form`)
- **Manifest**: Siempre `__manifest__.py`, nunca `__openerp__.py`

### XML/View Standards

- **Indent**: 4 espacios (no tabs)
- **XPath**: Usar `position` explícito (`before`, `after`, `inside`, `replace`, `attributes`)
- **Groups**: Referenciar grupos con módulo.xml_id (`sales_team.group_sale_manager`)
- **Sequence**: Usar `sequence` attribute para ordenar campos en vistas

### Translation System

**IMPORTANTE**: El sistema de traducciones está funcionando correctamente. Seguir estas reglas:

1. **Estructura de carpeta i18n/**:

    ```
    addon_name/
    ├── i18n/
    │   ├── es.po          # Español (obligatorio)
    │   ├── eu.po          # Euskera (obligatorio)
    │   └── addon_name.pot # Template (generado)
    ```

2. **NO usar `_()` en definiciones de campos a nivel de módulo**:

    ```python
    # ❌ INCORRECTO - causa warnings
    from odoo import _
    name = fields.Char(string=_("Name"))

    # ✅ CORRECTO - traducción se maneja por .po files
    name = fields.Char(string="Name")
    ```

3. **Usar `_()` solo en métodos y código ejecutable**:

    ```python
    def action_confirm(self):
        message = _("Confirmed successfully")
        return {'warning': {'message': message}}
    ```

4. **Generar/actualizar traducciones**:

    ```bash
    # Exportar términos a traducir
    Pedir al usuario generar a través de UI, no sabemos el método correcto para exportar SÓLO las cadenas del addon sin incluir todo el sistema.
    ```

Usar sólo polib y apend cadenas en los archivos .po, msmerge corrompe los archivos.

```

5. **Formato de archivos .po**:
- Encoding: UTF-8
- Content-Type: text/plain; charset=UTF-8
- Language codes: `es`, `eu`, `ca`, `gl`, `pt`, `fr`, `it`

## Project Structure

```

addons-cm/
├── .github/ # GitHub configs
│ └── copilot-instructions.md # Este archivo
├── ocb/ # Odoo Community Backports (18.0)
│ └── addons/ # Addons core de Odoo
├── oca_dependencies.txt # Dependencias OCA
├── requirements.txt # Dependencias Python
├── docker-compose.yml # Configuración Docker
├── Makefile # Comandos útiles
│
├── # === ADDONS OCA NO MODIFICADOS ===
├── account_invoice_triple_discount/ # Triple descuento en facturas (OCA)
├── purchase_triple_discount/ # Triple descuento en compras (OCA)
├── product_get_price_helper/ # Helper de precios (OCA)
├── product_main_seller/ # Proveedor principal (OCA)
├── product_price_category/ # Categorías de precio (OCA)
├── base_bank_from_iban/ # Banco desde IBAN (OCA)
├── l10n_es_partner/ # Adaptación partners/bancos ES (OCA)
│
├── # === ADDONS CUSTOM ===
├── # --- Precios y productos ---
├── product_sale_price_from_pricelist/ # Auto-cálculo precio venta (+ descuentos supplierinfo)
├── product_pricelist_total_margin/ # Margen total aditivo (markup / comercial) con límites
├── product_price_category_supplier/ # Extensión categorías precio por proveedor
├── product_origin_char/ # Campo origen texto libre por producto
├── account_invoice_triple_discount_readonly/ # Fix bug acumulación triple descuento
├── # --- Ventas y web ---
├── website_sale_aplicoop/ # Sistema eskaera (compras grupo)
├── website_sale_disable_cart/ # Tienda solo catálogo: desactiva el carrito estándar
├── portal_event_registration/ # Portal: ver registros de eventos + adjuntos
├── # --- Membresías ---
├── membership_monthly_invoicing/ # Factura mensual de membresía por socio (cron)
├── membership_expiry_reminder/ # Email recordatorio de renovación próxima
├── # --- Logística y contabilidad ---
├── stock_picking_batch_custom/ # Batch picking: columnas extra + vista operario (Basket Assembly)
├── account_banking_mandate_batch/ # Crear/validar mandatos SEPA en bloque desde contactos
└── l10n_es_edi_tbai_reagyp_recibidas/ # Fix TicketBAI REAGYP facturas recibidas (19 → 02)

````

## Addon References

**Para arquitectura, detalles de implementación y uso específico de cada addon, consultar su `README.rst` (documentación oficial) o `README_DEV.md` (detalles técnicos).**

### Addons OCA No Modificados

- [account_invoice_triple_discount](../account_invoice_triple_discount/README.rst)
- [purchase_triple_discount](../purchase_triple_discount/README.rst)
- [product_get_price_helper](../product_get_price_helper/README.rst)
- [product_main_seller](../product_main_seller/README.rst)
- [product_price_category](../product_price_category/README.rst)
- [base_bank_from_iban](../base_bank_from_iban/README.rst)
- [l10n_es_partner](../l10n_es_partner/README.rst)

### Addons Custom Propios

**Precios y productos**

- [product_sale_price_from_pricelist](../product_sale_price_from_pricelist/README.rst) - Auto-precio basado en compra (+ descuentos de supplierinfo)
- [product_pricelist_total_margin](../product_pricelist_total_margin/README.rst) - Margen total aditivo (markup / comercial) con límites globales
- [product_price_category_supplier](../product_price_category_supplier/README.rst) - Gestión categorías por proveedor
- [product_origin_char](../product_origin_char/README.rst) - Campo origen de texto libre por producto
- [account_invoice_triple_discount_readonly](../account_invoice_triple_discount_readonly/README.rst) - Fix bug acumulación descuentos

**Ventas y web**

- [website_sale_aplicoop](../website_sale_aplicoop/README.rst) - Sistema eskaera completo
- [website_sale_disable_cart](../website_sale_disable_cart/README.rst) - Tienda solo catálogo: oculta el carrito estándar y redirige `/shop/cart*`
- [portal_event_registration](../portal_event_registration/README.rst) - Portal: registros de eventos + adjuntos al chatter

**Membresías**

- [membership_monthly_invoicing](../membership_monthly_invoicing/README.rst) - Factura mensual de membresía por socio activo (cron)
- [membership_expiry_reminder](../membership_expiry_reminder/README.rst) - Email automático de recordatorio de renovación

**Logística y contabilidad**

- [stock_picking_batch_custom](../stock_picking_batch_custom/README.rst) - Columnas extra y vista operario para batch picking
- [account_banking_mandate_batch](../account_banking_mandate_batch/README.rst) - Creación masiva de mandatos SEPA desde contactos
- [l10n_es_edi_tbai_reagyp_recibidas](../l10n_es_edi_tbai_reagyp_recibidas/README.rst) - Fix TicketBAI REAGYP en facturas recibidas (clave régimen 19 → 02)

**Nota**: Todos los addons custom siguen la estructura OCA con readme/ fragmentos. Ver [docs/OCA_DOCUMENTATION.md](../docs/OCA_DOCUMENTATION.md) para detalles.

## Development Workflow

### Local Development

```bash
# Iniciar entorno (puertos: 8070=web, 8073=longpolling)
docker-compose up -d

# Actualizar addon
docker-compose exec odoo odoo -d odoo -u addon_name --stop-after-init

# Ver logs
docker-compose logs -f odoo

# Ejecutar tests
docker-compose run odoo odoo -d odoo --test-enable --stop-after-init -u addon_name

# NOTA: Usa `docker-compose run` (no `exec`) para lanzar un contenedor limpio y evitar efectos de caché. Cambia `addon_name` por el módulo a testear.

# Acceder a shell de Odoo
docker-compose exec odoo bash

# Acceder a PostgreSQL
docker-compose exec db psql -U odoo -d odoo
````

### Quality Checks

```bash
# Ejecutar todos los checks (usa .pre-commit-config.yaml)
pre-commit run --all-files

# O usar Makefile (ver `make help` para todos los comandos)
make lint          # Solo linting (pre-commit)
make format        # Formatear código (black + isort)
make check-format  # Verificar formateo sin modificar
make flake8        # Ejecutar flake8
make pylint        # Ejecutar pylint (todos)
make pylint-required  # Solo verificaciones mandatorias
make clean         # Limpiar archivos temporales
```

### Tools Configuration

- **black**: Line length 88, target Python 3.10+ (ver `pyproject.toml`)
- **isort**: Profile black, sections: STDLIB > THIRDPARTY > ODOO > ODOO_ADDONS > FIRSTPARTY > LOCALFOLDER
- **flake8**: Ver `.flake8` para reglas específicas
- **pylint**: Configurado para Odoo con `pylint-odoo` plugin

### Testing

- Tests en `tests/` de cada addon
- Naming: `test_*.py`
- Herencia: `odoo.tests.common.TransactionCase`
- Ejecutar: `--test-enable` flag

## Critical Architecture Patterns

### Product Variants Architecture

**IMPORTANTE**: Los campos de lógica de negocio SIEMPRE van en `product.product` (variantes), no en `product.template`:

```python
# ✅ CORRECTO - Lógica en product.product
class ProductProduct(models.Model):
    _inherit = 'product.product'

    last_purchase_price_updated = fields.Boolean(default=False)
    list_price_theoritical = fields.Float(default=0.0)

    def _compute_theoritical_price(self):
        for product in self:
            # Cálculo real por variante
            pass

# ✅ CORRECTO - Template solo tiene campos related
class ProductTemplate(models.Model):
    _inherit = 'product.template'

    last_purchase_price_updated = fields.Boolean(
        related='product_variant_ids.last_purchase_price_updated',
        readonly=False
    )
```

**Por qué**: Evita problemas con pricelists y reportes que operan a nivel de variante. Ver `product_sale_price_from_pricelist` como ejemplo.

### QWeb Template Best Practices

**CRÍTICO**: QWeb tiene limitaciones estrictas con lógica compleja. **Siempre mover lógica al controller**:

```python
# ❌ MAL - QWeb no puede parsear esto
# <t t-set="price" t-value="price_info.get('price') or product.list_price or 0"/>

# ✅ CORRECTO - Preparar datos en controller
class WebsiteController:
    def _prepare_product_display_info(self, product, price_info):
        """Pre-procesar todos los valores para QWeb."""
        price = price_info.get(product.id, {}).get('price') or product.list_price or 0.0
        return {
            'display_price': float(price),
            'safe_uom_category': product.uom_id.category_id.name or '',
        }

# En template: acceso simple, sin lógica
# <span t-esc="product_display['display_price']"/>
```

Ver [docs/QWEB_BEST_PRACTICES.md](../docs/QWEB_BEST_PRACTICES.md) para más detalles.

## Common Patterns

### Extending Models

```python
from odoo import models, fields, api

class ResPartner(models.Model):
    _inherit = 'res.partner'

    custom_field = fields.Char(string="Custom Field")

    @api.depends('field1', 'field2')
    def _compute_custom(self):
        for record in self:
            record.custom_computed = record.field1 + record.field2
```

### Creating Wizards (Transient Models)

```python
class WizardModel(models.TransientModel):
    _name = 'wizard.model.name'
    _description = "Wizard Description"

    def action_confirm(self):
        # Business logic
        return {'type': 'ir.actions.act_window_close'}
```

### Bulk Updates

```python
# Prefer SQL-level updates for performance
self.env['product.template'].search([
    ('default_supplier_id', '=', partner_id)
]).write({'price_category_id': category_id})
```

### Notifications

```python
return {
    'type': 'ir.actions.client',
    'tag': 'display_notification',
    'params': {
        'title': _('Success'),
        'message': _('Operation completed'),
        'type': 'success',  # or 'warning', 'danger', 'info'
        'sticky': False,
    }
}
```

### Logging Pattern

```python
import logging

_logger = logging.getLogger(__name__)

# En métodos de cálculo de precios, usar logging detallado:
_logger.info(
    "[PRICE DEBUG] Product %s [%s]: base_price=%.2f, tax_amount=%.2f",
    product.default_code or product.name,
    product.id,
    base_price,
    tax_amount,
)
```

### Price Calculation Pattern

```python
# Usar product_get_price_helper para cálculos consistentes
partial_price = product._get_price(qty=1, pricelist=pricelist)
base_price = partial_price.get('value', 0.0) or 0.0

# Siempre validar taxes
if not product.taxes_id:
    raise UserError(_("No taxes defined for product %s") % product.name)
```

## Dependencies Management

### OCA Dependencies (`oca_dependencies.txt`)

```
account-invoicing
product-attribute
purchase-workflow
sale-workflow
```

### Python Dependencies (`requirements.txt`)

- Versiones específicas para evitar breaking changes
- Incluir herramientas de desarrollo (linters, etc)

## Security & Access Rights

### Grupos Comunes

- `base.group_user` - Usuario interno
- `base.group_system` - Administrador
- `sales_team.group_sale_manager` - Manager de ventas
- `sales_team.group_sale_salesman` - Vendedor
- `purchase.group_purchase_manager` - Manager de compras

### Security Files

```xml
<!-- security/ir.model.access.csv -->
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_model_user,model.name.user,model_model_name,base.group_user,1,1,1,0
```

## Common Issues & Solutions

### Translation Warnings

**Problem**: `WARNING: _() called at import time at module...`
**Solution**: Remove `_()` from field definitions, use only in methods

### XPath Not Found

**Problem**: `Element ... not found in parent view`
**Solution**: Check view inheritance chain, verify XML IDs are correct

### Discount Reset Issue

**Problem**: Multiple discounts being accumulated in discount1
**Solution**: Use `account_invoice_triple_discount_readonly` addon

### Price Calculation

**Problem**: Prices not updating from pricelist
**Solution**:

1. Use `product_sale_price_from_pricelist` with proper configuration
2. Set pricelist in Settings > Sales > Automatic Price Configuration
3. Ensure `last_purchase_price_compute_type` is NOT set to `manual_update`
4. Verify product has taxes configured (required for price calculation)

### Product Variant Issues

**Problem**: Computed fields not working in pricelists/reports
**Solution**: Move business logic from `product.template` to `product.product` and use `related` fields in template

### Manifest Dependencies

**Problem**: Module not loading, dependency errors
**Solution**: Check both `__manifest__.py` depends AND `oca_dependencies.txt` for OCA repos

### QWeb Template Errors

**Problem**: `TypeError: 'NoneType' object is not callable` in templates
**Solution**:

1. Move complex logic from template to controller
2. Use simple attribute access in templates (no conditionals)
3. Pre-process all display values in Python
4. See [docs/QWEB_BEST_PRACTICES.md](../docs/QWEB_BEST_PRACTICES.md) for patterns

**Example Pattern**:

```python
# Controller: prepare clean data
def _prepare_display_info(self, product):
    return {
        'price': product.price or 0.0,
        'uom': product.uom_id.name or '',
    }

# Template: use simple access
<span t-esc="display_info['price']"/>
```

## Testing Guidelines

### Unit Tests

- Test business logic in isolation
- Mock external dependencies
- Use `TransactionCase` for DB tests

### Integration Tests

- Test workflow end-to-end
- Verify computed fields
- Check access rights

### UI Tests (Tours)

```javascript
odoo.define("module.tour", function (require) {
    "use strict";
    var tour = require("web_tour.tour");
    tour.register(
        "tour_name",
        {
            test: true,
            url: "/web",
        },
        [
            // Tour steps
        ],
    );
});
```

## Debugging Tips

### Enable Developer Mode

```
Settings > Activate Developer Mode
```

### Check Logs

```bash
docker-compose logs -f odoo | grep ERROR
docker-compose logs -f odoo | grep addon_name
```

### Python Debugger

```python
import pdb; pdb.set_trace()  # Set breakpoint
```

### Performance Profiling

```bash
--log-level=debug_sql  # Show SQL queries
```

## Documentation Standards

### OCA README Structure

Cada addon custom sigue la **estructura OCA** con fragmentos en `readme/`:

- `readme/DESCRIPTION.rst` - Descripción y características
- `readme/INSTALL.rst` - Instalación y dependencias
- `readme/CONFIGURE.rst` - Configuración post-instalación
- `readme/USAGE.rst` - Guía de uso
- `readme/CONTRIBUTORS.rst` - Contribuidores
- `readme/CREDITS.rst` - Créditos (Criptomart + Elika Bilbo)
- `README.rst` - README oficial generado/manual
- `README_DEV.md` - Notas técnicas para desarrolladores

**Ver**: [docs/OCA_DOCUMENTATION.md](../docs/OCA_DOCUMENTATION.md) para estructura completa.

### **manifest**.py Structure

Todos los addons custom deben seguir esta estructura:

```python
# Copyright YEAR - Today AUTHOR
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{  # noqa: B018
    "name": "Addon Name",
    "version": "18.0.X.Y.Z",  # X=major, Y=minor, Z=patch
    "category": "category_name",
    "summary": "Short description",
    "author": "Odoo Community Association (OCA), Criptomart",
    "maintainers": ["Criptomart"],
    "website": "https://github.com/OCA/repo",
    "license": "AGPL-3",
    "depends": [
        "base",
        # Lista ordenada alfabéticamente
    ],
    "data": [
        "security/ir.model.access.csv",
        "views/actions.xml",
        "views/menu.xml",
        "views/model_views.xml",
    ],
}
```

### Code Comments

- Docstrings en clases y métodos públicos
- Comentarios inline para lógica compleja
- TODOs con contexto completo
- Logging detallado en operaciones de precios/descuentos

## Version Control

### Commit Messages

```
[TAG] module: Brief description

Longer explanation if needed
```

Tags: `[ADD]`, `[FIX]`, `[IMP]`, `[REF]`, `[REM]`, `[I18N]`, `[DOC]`

### Branch Strategy

- `main` - Production ready
- `dev` - Development
- `feature/*` - New features
- `fix/*` - Bug fixes

## Performance Considerations

- Use `@api.depends` correctamente para computed fields
- Prefer `search()` + `write()` sobre loops con `write()`
- Use `create()` con lista de vals para bulk creates
- Indexes en campos frecuentemente buscados
- Avoid N+1 queries con `prefetch`

## Key Business Features

### Eskaera System (website_sale_aplicoop)

Sistema completo de compras colaborativas para cooperativas de consumo:

- **Group Orders**: Pedidos grupales con estados (draft → confirmed → collected → completed)
- **Separate Carts**: Carrito independiente por miembro y por grupo
- **Cutoff Dates**: Validación de fechas límite para pedidos
- **Pickup Management**: Gestión de días de recogida
- **Lazy Loading**: Carga configurable de productos (v18.0.1.3.0+)
- **Multi-language**: ES, EU, CA, GL, PT, FR, IT
- **Member Tracking**: Gestión de miembros activos/inactivos por grupo

**Flujo típico**:

1. Administrador crea grupo order con fechas (collection, cutoff, pickup)
2. Miembros añaden productos a su carrito individual
3. Sistema valida cutoff date antes de confirmar
4. Notificaciones automáticas al cambiar estados
5. Tracking de fulfillment por miembro

**Configuración Lazy Loading** (v18.0.1.3.0+):

```
Settings > Website > Shop Performance
  [✓] Enable Lazy Loading
  [20] Products Per Page
```

**Mejoras Recientes**:

- v18.0.1.3.1: Fixes críticos de cálculo de fechas
- v18.0.1.3.0: Lazy loading, mejora de rendimiento de 10-20s → 500-800ms
- Refactor de template rendering: Mover lógica QWeb al controller

Ver [website_sale_aplicoop/README.rst](../website_sale_aplicoop/README.rst) y [docs/LAZY_LOADING.md](../docs/LAZY_LOADING.md) para detalles.

### Triple Discount System

Todos los documentos de compra/venta soportan 3 descuentos consecutivos:

```python
# Ejemplo: Precio = 600.00
# Desc. 1 = 50% → 300.00
# Desc. 2 = 50% → 150.00
# Desc. 3 = 50% → 75.00
```

**IMPORTANTE**: Usar `account_invoice_triple_discount_readonly` para evitar bug de acumulación de descuentos.

### Automatic Pricing System

`product_sale_price_from_pricelist` calcula automáticamente precio de venta basado en:

- Último precio de compra (`last_purchase_price_received`)
- Tipo de cálculo de descuentos (`last_purchase_price_compute_type`)
- Pricelist configurado en Settings
- Impuestos del producto

**Configuración crítica**:

```python
# En Settings > Sales > Automatic Price Configuration
product_pricelist_automatic = [ID_pricelist]

# En producto
last_purchase_price_compute_type != "manual_update"  # Para auto-cálculo
```

## Resources

- **OCA Guidelines**: https://github.com/OCA/odoo-community.org/blob/master/website/Contribution/CONTRIBUTING.rst
- **Odoo Documentation**: https://www.odoo.com/documentation/18.0/
- **OCB Repository**: https://github.com/OCA/OCB
- **OCA Repositories**: https://github.com/OCA/

---

**Last Updated**: 2026-06-04
**Odoo Version**: 18.0
**Python Version**: 3.10+

## Recent Changes Summary

- **2026-06-04**: Nuevos addons custom documentados: `membership_monthly_invoicing`,
  `membership_expiry_reminder`, `account_banking_mandate_batch`, `portal_event_registration`,
  `product_pricelist_total_margin`, `product_origin_char`, `l10n_es_edi_tbai_reagyp_recibidas`.
  `product_origin` (OCA) sustituido por `product_origin_char` (custom).
- **2026-06-04**: `product_sale_price_from_pricelist` aplica descuentos de proveedor desde `supplierinfo`.
- **2026-06**: `stock_picking_batch_custom` — vista operario a pantalla completa (Basket Assembly),
  ordenación de operaciones por categoría/producto/partner.
- **2026-02-18**: Refactor `product_main_seller` - Remover alias innecesario `default_supplier_id`
- **2026-02-16**: v18.0.1.3.1 fixes críticos de cálculo de fechas en Eskaera
- **2026-02-12**: v18.0.1.3.0 Lazy loading y fixes de template rendering QWeb
- **2026-02-02**: UI improvements y date calculation fixes
