# Theme M22 Tech Consulting Tema personalizado para Odoo 18 con estilo **"Futurismo Cálido"** - una combinación de diseño dark mode moderno con acentos vibrantes en naranja y magenta. ## 📋 Información General | Campo | Valor | |-------|-------| | **Nombre técnico** | `theme_m22tc` | | **Versión** | 1.0.3 | | **Categoría** | Theme/Corporate | | **Licencia** | LGPL-3 | | **Compatibilidad** | Odoo 18.0 | | **Dependencias** | `website`, `auth_signup` | ## 🎨 Design System ### Concepto: Split Theme + Glassmorphism El tema implementa un **"Split Theme"** (Tema Dividido) con **Glassmorphism** aplicado consistentemente: - **Login/Auth**: 100% Dark + Glassmorphism intenso - **Sidebar**: Dark + Glassmorphism sutil - **Contenido Principal**: Light + Glassmorphism elegante ### Paleta de Colores | Variable | Color | Hex | Uso | |----------|-------|-----|-----| | `o-color-1` | 🟠 Naranja M22 | `#FF7C00` | Acento primario, CTAs, links | | `o-color-2` | 🩷 Magenta | `#E0407B` | Acento secundario, gradientes | | `o-color-3` | 🌑 Midnight Blue | `#0F111A` | Fondo sidebar (dark mode) | | `o-color-4` | ⚪ Soft Gray | `#F8F8F8` | Fondo contenido principal (light mode) | | `o-color-5` | ⬜ Blanco | `#FFFFFF` | Textos sobre fondo oscuro, reportes | ### Glassmorphism Efecto glassmorphism aplicado en diferentes intensidades: ```scss // Dark Glassmorphism (Sidebar) background: rgba(#0F111A, 0.95); backdrop-filter: blur(20px); border: 1px solid rgba(255, 255, 255, 0.08); // Light Glassmorphism (Contenido) background: rgba(#FFFFFF, 0.75); backdrop-filter: blur(12px); border: 1px solid rgba(0, 0, 0, 0.08); ``` ### Tipografía - **Fuente principal**: Inter (sans-serif) - **Fuente alternativa**: Montserrat (sans-serif) - **Pesos disponibles**: 300, 400, 500, 600, 700, 800 ### Gradientes Principales ```css /* Gradiente primario (botones, logos) */ background: linear-gradient(90deg, #FF6B00, #E1467C); /* Gradiente para fondos */ background-image: radial-gradient(ellipse 80% 50% at 10% 10%, rgba(255, 107, 0, 0.15), transparent), radial-gradient(ellipse 80% 50% at 90% 90%, rgba(225, 70, 124, 0.15), transparent); ``` ## 📁 Estructura del Proyecto ``` theme_m22tc/ ├── __init__.py ├── __manifest__.py ├── README.md │ ├── data/ │ ├── generate_primary_template.xml # Template generator para snippets │ ├── ir_asset.xml # Registro de assets (SCSS, JS) │ └── menu_data.xml # Datos de menú │ ├── controllers/ │ ├── __init__.py │ └── helpdesk_portal.py # Extensiones del portal de helpdesk (dashboard, aprobación) │ ├── models/ │ ├── __init__.py │ ├── theme_m22tc.py # Modelo principal del tema │ └── website_menu.py # Extensiones al menú del website │ ├── static/ │ └── src/ │ ├── js/ │ │ ├── m22_sidebar.js # Widget sidebar interactivo │ │ └── m22_bottom_sheet.js # Widget bottom sheet móvil (iOS style) │ │ │ └── scss/ │ ├── primary_variables.scss # Variables de diseño (colores, fuentes) │ ├── bootstrap_overridden.scss # Overrides de Bootstrap │ ├── m22tc_styles.scss # Estilos personalizados │ └── snippets/ │ └── s_popup_m22.scss # Estilos del popup M22 │ └── views/ ├── customizations.xml # Personalizaciones generales (Tailwind, login enforcement) ├── frontend_layout.xml # Layout para usuarios autenticados + sidebar M22 ├── login_custom.xml # Login, Signup, Reset Password ├── portal_sidebar.xml # Cleanup de vistas legacy (sin overrides) ├── snippets.xml # Snippets personalizados (Bento Grid) ├── snippets_popup.xml # Popup M22 personalizado ├── website_menu_view.xml # Vistas de menú ├── helpdesk_dashboard.xml # Template del dashboard de tickets └── helpdesk_portal_approval.xml # Template de botones de aprobación/rechazo ``` ## 🛠 Tecnologías Utilizadas ### Backend (Odoo) - **QWeb Templates**: Motor de plantillas de Odoo para renderizado HTML - **ir.asset**: Sistema de assets de Odoo 18 para registro de SCSS/JS - **Template Inheritance**: Herencia de vistas con `inherit_id` y `xpath` ### Frontend - **SCSS/Sass**: Preprocesador CSS para estilos modulares - **Tailwind CSS** (CDN): Framework utility-first para páginas de autenticación y bottom sheet móvil - **Bootstrap 5**: Framework CSS base de Odoo - **publicWidget**: Framework JS de Odoo para widgets frontend interactivos ### Fuentes Externas - **Google Fonts**: Inter, Montserrat ## 📦 Assets y Bundles Los assets se registran en `data/ir_asset.xml` usando `theme.ir.asset` (no `ir.asset`) para que solo se apliquen cuando el tema está activo: | Asset | Bundle | Descripción | |-------|--------|-------------| | `primary_variables.scss` | `web._assets_primary_variables` | Variables de diseño (colores claros para reportes) | | `bootstrap_overridden.scss` | `web._assets_frontend_helpers` | Overrides Bootstrap (colores base claros) | | `m22tc_styles.scss` | `web.assets_frontend` | Estilos personalizados (Split Theme + Glassmorphism) | | `m22_sidebar.js` | `web.assets_frontend` | Widget del sidebar interactivo | | `m22_bottom_sheet.js` | `web.assets_frontend` | Widget del bottom sheet móvil (iOS style) | ### Estilos Generalizados El tema incluye estilos generalizados para elementos nativos de Odoo: #### Badges de Bootstrap Soporte completo para todos los badges de Bootstrap con buen contraste: - `.badge-primary`, `.text-bg-primary` - Naranja M22 - `.badge-secondary`, `.text-bg-secondary` - Gris - `.badge-success`, `.text-bg-success` - Verde - `.badge-warning`, `.text-bg-warning` - Amarillo - `.badge-danger`, `.text-bg-danger` - Rojo - `.badge-info`, `.text-bg-info` - Cyan - `.badge-light`, `.text-bg-light`, `.bg-200` - Gris claro con texto oscuro (crítico para helpdesk) #### Navegación en Sidebar Nativo Estilos para elementos de navegación en sidebars nativos de Odoo: - `.nav.flex-column .nav-link` - Links de navegación vertical - `[role="complementary"] .nav-link` - Sidebars complementarios - `.navspy`, `.bs-sidenav` - Navegación scroll-spy - `#ticket-nav`, `#ticket-links` - Sidebar específico de helpdesk #### Botones en Sidebar Nativo - `.btn-light` - Texto oscuro visible sobre fondo claro - `.btn-primary` - Gradiente M22 con texto blanco - `.btn-outline-primary` - Borde naranja, fondo transparente ## 🔐 Sistema de Autenticación Personalizado El tema incluye páginas de autenticación completamente personalizadas con estilo glassmorphism: ### Templates (`views/login_custom.xml`) | Template | Hereda de | Descripción | |----------|-----------|-------------| | `m22_tailwind_config` | `web.layout` | Configuración Tailwind + estilos globales | | `m22_login_layout_override` | `website.login_layout` | Layout contenedor | | `m22_login_form_override` | `web.login` | Formulario de login | | `m22_signup_form_override` | `auth_signup.signup` | Formulario de registro | | `m22_reset_password_override` | `auth_signup.reset_password` | Formulario reset password | ### Características - ✅ Logo dinámico del sitio web (`request.website.image_url`) - ✅ Link de registro condicional (`signup_enabled`) - ✅ Link de reset password condicional (`reset_password_enabled`) - ✅ Efecto glassmorphism con backdrop blur - ✅ Botones con gradiente naranja-magenta - ✅ Inputs con estilo dark mode ### URLs - `/web/login` - Iniciar sesión - `/web/signup` - Crear cuenta - `/web/reset_password` - Restablecer contraseña ## 🎯 Sidebar de Navegación El tema incluye un sidebar personalizado para usuarios autenticados con **inicialización temprana** para prevenir flash visual: ### Características - Ancho fijo de 260px en desktop (72px colapsado) - Colapsable con persistencia en localStorage - **Inicialización temprana**: Script inline en `` previene flash visual al cargar - Responsive: drawer en mobile - Animaciones suaves con cubic-bezier - **Prevención de layout shift**: Iconos fijos, textos con `position: absolute` ### Sistema de Inicialización El sidebar usa un sistema de inicialización temprana para evitar efectos visuales desagradables: 1. **Script inline en ``** (`views/frontend_layout.xml`): - Lee `localStorage` antes del render - Aplica clase `m22-sidebar-collapsed-init` al `` si está colapsado 2. **CSS de inicialización** (`m22tc_styles.scss`): - `html.m22-sidebar-collapsed-init`: Aplica estado colapsado sin transiciones - Iconos fijos con `position: relative` y `flex-shrink: 0` - Textos ocultos con `position: absolute` y `left: -9999px` (sin layout shift) - Icono de flecha reemplazado via `::after` para evitar rotación visible 3. **JavaScript sincronizado** (`m22_sidebar.js`): - Reconoce estado previo aplicado por CSS - Sincroniza estado interno sin cambios visuales - Habilita transiciones después de inicializar ### Clases CSS Principales ```css .m22-sidebar /* Contenedor del sidebar */ .m22-sidebar-collapsed /* Estado colapsado */ .m22-sidebar-mobile /* Versión mobile */ .o_has_m22_sidebar /* Clase en body cuando hay sidebar */ .o_main_with_sidebar /* Main content con offset */ html.m22-sidebar-collapsed-init /* Estado inicial colapsado (sin transiciones) */ html.m22-sidebar-initialized /* Sidebar inicializado (transiciones habilitadas) */ ``` ## 📱 Navegación Móvil El tema incluye una **navegación inferior móvil** (bottom navigation) con un **bottom sheet estilo iOS** para mostrar opciones adicionales: ### Características de la Bottom Navigation - **Barra fija inferior**: Navegación siempre visible en la parte inferior en dispositivos móviles - **Items principales**: Muestra los primeros 3 items del menú del sidebar (Inicio, Cotizaciones, Pedidos) - **Item "Cuenta"**: Siempre visible, enlace directo a `/my/account` - **Botón "Más"**: Siempre al final, abre el bottom sheet con opciones adicionales - **Diseño responsive**: Solo visible en pantallas pequeñas (`d-lg-none`) ### Bottom Sheet Estilo iOS Modal que se desliza desde la parte inferior mostrando opciones adicionales del menú: #### Características - ✅ **Animaciones suaves**: Transiciones con cubic-bezier estilo iOS - ✅ **Backdrop blur**: Fondo oscuro difuminado (glassmorphism) - ✅ **Handle indicator**: Barrita superior que indica que se puede arrastrar - ✅ **Drag to dismiss**: Arrastrar hacia abajo desde el handle o header para cerrar - ✅ **Múltiples formas de cierre**: - Botón "Cerrar" en la parte inferior - Tap en el backdrop (fondo oscuro) - Tecla Escape - Drag hacia abajo - ✅ **Scroll interno**: El contenido es scrolleable si hay muchos items - ✅ **Integración con tema**: Estilos glassmorphism consistentes con el diseño #### Contenido del Bottom Sheet Muestra los items adicionales del menú (desde el 4to en adelante): - Facturas - Proyectos - Tareas - Tickets #### Implementación Técnica **Archivos relacionados**: - `views/frontend_layout.xml`: Estructura HTML del bottom nav y bottom sheet - `static/src/js/m22_bottom_sheet.js`: Lógica JavaScript con widgets de Odoo - `static/src/scss/m22tc_styles.scss`: Estilos de integración con el tema **Widgets JavaScript**: - `M22BottomSheet`: Widget principal que gestiona el bottom sheet - `M22BottomSheetTrigger`: Widget del botón "Más" que abre el sheet **Orden de elementos en bottom nav**: 1. Items principales (primeros 3 del menú) 2. Cuenta (siempre visible) 3. Más (siempre al final) ### Clases CSS Principales ```css .m22-bottom-nav /* Barra de navegación inferior móvil */ .bottom-nav-item /* Item individual de la barra */ #m22_bottom_sheet /* Contenedor del bottom sheet */ .m22-bottom-sheet-container /* Contenedor principal con backdrop */ .m22-bottom-sheet-backdrop /* Fondo oscuro difuminado */ .m22-bottom-sheet-content /* Contenido del sheet (scrolleable) */ .m22-sheet-item /* Item individual dentro del sheet */ ``` ## 🔧 Compatibilidad y Ajustes Técnicos ### Filtros del Portal con Tailwind CSS **Problema**: Los filtros del portal de Odoo (`portal_searchbar`) usan clases de Bootstrap (`.collapse`, `.navbar-collapse`) que dependen del JavaScript de Bootstrap para expandirse/colapsarse. Cuando Tailwind reemplaza Bootstrap, estos elementos permanecen colapsados por defecto. **Solución**: El tema incluye CSS con alta especificidad en `m22tc_styles.scss` que fuerza la visualización de los filtros en desktop: ```scss .o_portal_wrap nav.o_portal_navbar { @media (min-width: 992px) { .collapse, .navbar-collapse, #o_portal_navbar_content { display: flex !important; visibility: visible !important; height: auto !important; } } } ``` **Resultado**: Los controles "Ordenar por", "Filtrar por", "Agrupar por" y la búsqueda son siempre visibles en pantallas de escritorio (≥992px), manteniendo la funcionalidad completa del portal de Odoo. ### Arquitectura No-Invasiva El tema sigue una **filosofía de no-interferencia** con los templates nativos de Odoo: - ✅ **NO reemplaza** el contenedor principal del portal - ✅ **NO modifica** la estructura de vistas de Odoo - ✅ **Inyecta** la sidebar M22 como componente adicional - ✅ **Ajusta** el layout exclusivamente con CSS - ✅ **Preserva** toda la funcionalidad nativa (filtros, paginación, breadcrumbs) El archivo `portal_sidebar.xml` solo contiene una función de cleanup de vistas legacy. Todos los ajustes visuales se manejan en `m22tc_styles.scss`. ## 🧩 Snippets Disponibles ### Bento Grid (`s_m22_bento_grid`) Grid asimétrico estilo "Bento Box" para mostrar servicios/features: - Layout responsive con CSS Grid - Tarjetas con efecto hover - Iconos con gradiente - Compatible con el editor de Odoo ### M22 Popup (`s_popup` variante M22) Popup personalizado con estilos glassmorphism del tema M22: - **Dark Glassmorphism**: Fondo oscuro con blur y transparencia - **Gradiente M22**: Botón cerrar con gradiente naranja-magenta característico - **Botones estilizados**: Primarios con gradiente, secundarios con glassmorphism - **Forms personalizados**: Inputs con fondo translúcido y focus naranja - **Responsive**: Adaptado para móvil y desktop - **Opciones configurables**: - Posición: Top, Middle, Bottom - Tamaño: Small, Medium, Large, XL, Full - Display: Delay, Exit, Click - Backdrop con blur **Cómo usar:** 1. En el Website Builder, arrastra el snippet "Popup" a tu página 2. El popup usará automáticamente los estilos M22 (variante `data-vcss="m22"`) 3. Personaliza posición, tamaño y comportamiento desde el panel de opciones 4. Edita el contenido del popup haciendo clic dentro de él ## ⚙️ Configuración del Website Editor El tema configura automáticamente: - **Botones**: Border radius 0.5rem, efecto ripple - **Header**: Template "boxed" - **Footer**: Template "centered" ## 🚀 Instalación y Actualización ### Instalar el tema ```bash # Desde el directorio workspace/ source ../venv/bin/activate python src/odoo/odoo-bin -c ../odoo.conf -d m22_techconsulting_dev -i theme_m22tc --stop-after-init ``` ### Actualizar el tema ```bash # Desde el directorio workspace/ source ../venv/bin/activate python src/odoo/odoo-bin -c ../odoo.conf -d m22_techconsulting_dev -u theme_m22tc --stop-after-init ./odoo_dev.sh restart ``` ### Reiniciar Odoo ```bash ./odoo_dev.sh restart ``` ## 🔧 Desarrollo ### Modificar estilos 1. Editar archivos en `static/src/scss/` 2. Actualizar el tema: `python src/odoo/odoo-bin -c ../odoo.conf -u theme_m22tc --stop-after-init` 3. Reiniciar: `./odoo_dev.sh restart` ### Modificar templates 1. Editar archivos en `views/` 2. Actualizar el tema 3. Limpiar caché si es necesario: ```python # En Odoo shell env.registry.clear_cache() env['ir.qweb'].clear_caches() ``` ### Agregar nuevos assets 1. Crear archivo en `static/src/scss/` o `static/src/js/` 2. Registrar en `data/ir_asset.xml` 3. Actualizar el tema ## 📝 Notas Importantes 1. **Tailwind CSS**: La plataforma usa Tailwind CSS para reemplazar Bootstrap en toda la aplicación. El tema se carga vía CDN en `customizations.xml` y es compatible con los componentes nativos de Odoo mediante CSS con alta especificidad que asegura la funcionalidad de elementos que originalmente dependían de Bootstrap (como filtros colapsables). 2. **Assets con `theme.ir.asset`**: Todos los assets usan `model="theme.ir.asset"` en lugar de `ir.asset` para que solo se apliquen cuando el tema está activo. Odoo copia estos assets a `ir.asset` con `website_id` cuando se activa el tema. 3. **Colores base claros**: Las variables `$body-bg` y `$body-color` en `bootstrap_overridden.scss` y `primary_variables.scss` usan valores claros (blanco/gris oscuro) para compatibilidad con reportes HTML (facturas, órdenes) que requieren fondo blanco. 4. **Especificidad CSS**: Los estilos de inicialización usan `#wrapwrap.o_has_m22_sidebar` para igualar la especificidad de los estilos normales y evitar conflictos. 5. **Prioridades de templates**: Los overrides usan `priority="100"` o superior para asegurar que se apliquen después de otros módulos. 6. **Variables condicionales**: Las páginas de auth respetan las configuraciones de Odoo: - `signup_enabled`: Habilitado en Ajustes > Usuarios > Acceso de cliente - `reset_password_enabled`: Habilitado en Ajustes > Usuarios > Restablecer contraseña 7. **Logo del sitio**: El logo se obtiene de la configuración del Website (`request.website.image_url`), no de la compañía. 8. **Prevención de layout shift**: Los textos del sidebar usan `position: absolute` y `left: -9999px` para sacarlos del flujo sin causar movimiento de iconos durante la carga. 9. **Bottom Sheet móvil**: El bottom sheet usa Tailwind CSS vía CDN y está integrado con el sistema de estilos del tema. El botón "Más" siempre aparece al final de la navegación inferior, después del item "Cuenta". 10. **Navegación móvil**: La bottom navigation solo muestra los primeros 3 items principales del menú más "Cuenta" y "Más". Los items adicionales se muestran en el bottom sheet al tocar "Más". 11. **Compatibilidad Tailwind + Bootstrap**: El tema está diseñado para funcionar en plataformas donde Tailwind reemplaza Bootstrap. Los filtros del portal de Odoo (que originalmente usan clases `.collapse` de Bootstrap) se fuerzan a mostrarse en desktop mediante CSS con alta especificidad en `m22tc_styles.scss`. Esto asegura que los controles "Ordenar por", "Filtrar por", "Agrupar por" y la búsqueda siempre sean visibles en pantallas ≥992px. 12. **Arquitectura no-invasiva**: El tema NO reemplaza templates del portal de Odoo. La sidebar M22 es un componente adicional que se inyecta vía `frontend_layout.xml`. Los ajustes de layout se realizan exclusivamente con CSS, preservando toda la funcionalidad nativa de Odoo (filtros, breadcrumbs, paginación, etc.). ## 🎫 Funcionalidades de Helpdesk El tema incluye extensiones personalizadas para el módulo de Helpdesk que mejoran la experiencia del cliente en el portal: ### Dashboard de Tickets (`/my/tickets-dashboard`) Dashboard personalizado que muestra información clave para el cliente: #### Métricas Principales 1. **Tiempo Usado/Disponible** - Calcula horas usadas vs horas disponibles del partner - Basado en líneas de pedido prepagadas (`sale.order.line`) - Compatible con `sale_timesheet` si está instalado - Muestra porcentaje de uso 2. **Resumen de Tickets** - Total de tickets abiertos - Total de tickets cerrados - Tickets por prioridad (Urgente, Alta, Media, Baja) - Tickets por etapa 3. **Cumplimiento SLA** - Porcentaje de tickets cumpliendo SLA - Tickets en riesgo (próximos a vencer) - Tickets con SLA fallido 4. **Tickets Esperando Respuesta** - Tickets que requieren acción del cliente - Lógica de cálculo: - **Prioridad 1**: Tickets en etapas excluidas de SLA (`exclude_stage_ids`) - **Prioridad 2**: Tickets donde el último mensaje es del equipo de helpdesk - Solo incluye tickets asignados (excluye tickets nuevos sin asignar) - Ordenados por tiempo de espera (más antiguos primero) 5. **Tickets Recientes** - Últimos 10 tickets creados - Enlace directo a cada ticket #### Diseño Visual - Cards con efecto glassmorphism consistente con el tema - Barras de progreso para métricas porcentuales - Badges de colores para estados y prioridades - Diseño responsive y mobile-friendly - Integración con la paleta de colores del tema ### Lista de Tickets (`/my/tickets`) #### Agrupamiento por Defecto - Por defecto, los tickets se agrupan por **Etapa** (`stage_id`) - El usuario puede cambiar el agrupamiento usando los controles nativos de Odoo - No interfiere con la funcionalidad nativa del portal ### Aprobación/Rechazo de Tickets Sistema de aprobación para tickets en etapas de espera del cliente: #### Funcionalidad - **Botones de Aprobación**: Aparecen en tickets que están en etapas excluidas de SLA - **Aprobar Solución**: Mueve el ticket a la siguiente etapa (mayor secuencia) - **Rechazar / Necesito más ayuda**: Mueve el ticket a la etapa anterior no-excluida (menor secuencia) - **Mensajes automáticos**: Cada acción publica un mensaje en el chatter del ticket #### Lógica de Etapas Excluidas - Las etapas excluidas se definen en los SLAs del equipo (`helpdesk.sla.exclude_stage_ids`) - Cuando un ticket está en una etapa excluida, el SLA se pausa - El cliente puede aprobar o rechazar desde el portal - Al aprobar, el ticket avanza y el SLA se reanuda #### Template - Banner de aprobación visible en la página de seguimiento del ticket (`/my/ticket/`) - Estilos glassmorphism consistentes con el tema - Botones con gradientes y efectos hover - Alertas de confirmación después de cada acción ### Mejoras Visuales en Portal #### Buscador y Dropdowns - Fondo claro con texto oscuro (consistente con glassmorphism) - Acentos naranja en elementos seleccionados y focus - Posicionamiento corregido de elementos - Compatible con el diseño "Futurismo Cálido" #### Estilos Específicos - `.o_portal_navbar`: Buscador y controles de filtrado - `.dropdown-menu`: Menús desplegables con estilos personalizados - `.form-control`: Inputs con estilo glassmorphism ### Implementación Técnica **Archivos relacionados**: - `controllers/helpdesk_portal.py`: Lógica del dashboard y aprobación - `views/helpdesk_dashboard.xml`: Template del dashboard - `views/helpdesk_portal_approval.xml`: Template de botones de aprobación - `static/src/scss/m22tc_styles.scss`: Estilos del dashboard y aprobación **Dependencias**: - `helpdesk`: Módulo base de helpdesk de Odoo Enterprise - `helpdesk_extras`: Módulo custom con funcionalidades extendidas (colaboradores, SLAs) **Rutas**: - `/my/tickets-dashboard`: Dashboard personalizado - `/my/tickets`: Lista de tickets (nativa, con agrupamiento por defecto) - `/my/ticket//`: Página de seguimiento (con banner de aprobación) - `/my/ticket/approve//`: Endpoint para aprobar ticket - `/my/ticket/reject//`: Endpoint para rechazar ticket **Notas importantes**: - El dashboard muestra métricas agregadas de todos los equipos donde el usuario es colaborador - Las reglas de seguridad (`ir.rule`) de `helpdesk_extras` filtran automáticamente los tickets según el rol del usuario en cada equipo - Los tickets en etapa "Nuevo" sin asignar NO aparecen en "Esperando Respuesta" (están esperando respuesta del equipo, no del cliente) ## 📄 Licencia LGPL-3 - Ver archivo LICENSE para más detalles. --- **Desarrollado por**: M22 Tech Consulting **Versión de Odoo**: 18.0