# Smart Offers — Documentación completa

> Guía completa del módulo Smart Offers para PrestaShop 8 y 9: los cuatro tipos de ofertas agrupadas, creación paso a paso, productos con variantes, añadido automático al carrito, migración de PS8 a PS9, idiomas y arquitectura técnica.

- Página: <https://www.datafirefly.com/es/documentation/dfoffers/>
- Idioma: es
- Actualizado el: 2026-09-10
- Otros idiomas: [fr](https://www.datafirefly.com/documentation/dfoffers/index.md), [en](https://www.datafirefly.com/en/documentation/dfoffers/index.md), [de](https://www.datafirefly.com/de/documentation/dfoffers/index.md), [it](https://www.datafirefly.com/it/documentation/dfoffers/index.md), [pl](https://www.datafirefly.com/pl/documentation/dfoffers/index.md), [pt](https://www.datafirefly.com/pt/documentation/dfoffers/index.md), [nl](https://www.datafirefly.com/nl/documentation/dfoffers/index.md)
- Índice: <https://www.datafirefly.com/es/documentation/llms.txt>

Smart Offers es un módulo compatible con PrestaShop 8 y 9 que permite crear ofertas 1+1, packs al por mayor, packs multi-producto y ofertas de elección, con añadido automático de los productos regalo al carrito y presentación cuidada en la ficha de producto.

## Visión general

Smart Offers cubre los cuatro formatos de ofertas agrupadas más utilizados en e-commerce en un solo módulo, sin configuración compleja. El motor evalúa el carrito en cada modificación, añade automáticamente los productos regalo en cuanto se cumplen las condiciones y crea una regla de carrito que hace esas unidades gratuitas. La experiencia del cliente es inmediata y legible.

**En resumen:** El comerciante crea una oferta en menos de un minuto a través de una interfaz visual, el cliente ve aparecer el regalo automáticamente en su carrito con un indicador claro, y el tracking interno permite una revocación limpia si el cliente quita un activador durante la sesión.

## Compatibilidad con PrestaShop 8 y 9

Desde la versión 2.0.0, un solo archivo ZIP cubre PrestaShop 8.0 a 9.x. No hay que elegir una rama concreta al descargar: el módulo detecta la versión de la tienda en tiempo de ejecución y adapta sus llamadas a las API que cambiaron entre ambas generaciones.

| Elemento | PrestaShop 8 | PrestaShop 9 |
| --- | --- | --- |
| Versión de PHP requerida | 7.4 a 8.3 | 8.1 a 8.3 |
| Esquema de base de datos | Idéntico, seis tablas ps_dfoffers_* |  |
| Hooks utilizados | Idénticos |  |
| Configuración de las ofertas | Idéntica, migración transparente |  |

Las diferencias de API están agrupadas en una sola clase interna, `DfOfferCompat`. Si sobrescribe código del módulo en un proyecto a medida, es el único archivo que hay que consultar para entender los saltos de versión.

## Instalación

1. Descargue el archivo `dfoffers-vX.Y.Z.zip` desde su área de cliente DataFirefly
2. En el back office de PrestaShop, vaya a **Módulos → Administrador de módulos**
3. Haga clic en el botón **Subir un módulo** en la parte superior
4. Arrastre y suelte el archivo ZIP o haga clic para seleccionarlo
5. La instalación es automática: las tablas se crean, los hooks se registran y aparece una nueva pestaña **Catálogo → Ofertas agrupadas** en el menú

No se requiere ninguna dependencia externa. El módulo utiliza las clases nativas de PrestaShop (Cart, CartRule, Product) y no añade nada a su composer.json.

## Los cuatro tipos de ofertas

### 1+1 sobre el mismo producto

El formato viral del _buy-one-get-one_: el cliente compra una unidad de un producto y recibe otra unidad del mismo producto de regalo. Configura:

- Un solo producto (utilizado como activador y como recompensa)
- La cantidad a comprar para activar la oferta (generalmente 1)
- La cantidad de regalo (generalmente 1)

Ejemplo típico: "Por 1 par de calcetines comprado, el segundo es gratis." Cuando el cliente añade el par a su carrito, el motor añade automáticamente un segundo y aplica un descuento igual al precio unitario.

### Compra X, recibe Y de regalo (productos diferentes)

Formato de bundle: varios productos activadores distintos deben estar presentes en el carrito para que se active la oferta, y entonces se regalan uno o varios productos diferentes. Configura:

- La lista de productos activadores con sus cantidades respectivas
- La lista de productos regalo con sus cantidades respectivas

Ejemplo típico: "Compra una crema de día y un sérum juntos, recibe una muestra de mascarilla de regalo." El motor verifica que todos los activadores estén presentes antes de activar la oferta.

### Elección de variantes

Formato de composición libre: defines un conjunto de productos o variantes candidatas entre las cuales el cliente compone su lote. El motor identifica automáticamente las unidades más baratas del carrito como las unidades de regalo, lo que corresponde a la interpretación comercial estándar del _buy-N-get-M_.

- La lista de productos o variantes candidatas
- El número de unidades a comprar en este conjunto
- El número de unidades de regalo (las más baratas)

Ejemplo típico: "3 camisetas compradas de nuestra selección, la más barata es gratis." El cliente compone su lote, el motor no toca su carrito sino que aplica un descuento sobre las unidades más baratas.

### Pack al por mayor

Formato B2B y liquidación de stock: por cada lote de X unidades compradas de un producto, el cliente recibe Y unidades gratuitas de otro producto. Configura:

- El producto activador con la cantidad de palier (por ejemplo 10)
- El producto regalo con la cantidad ofrecida (por ejemplo 20)

Ejemplo típico: "Por 10 botellas de vino compradas, 2 copas de regalo." Práctico para proveedores que quieren impulsar un producto complementario o liquidar stock durmiente vinculándolo a un producto que se vende bien.

## Crear su primera oferta

Desde el back office, vaya a **Catálogo → Ofertas agrupadas** y haga clic en **Nueva oferta**.

### Paso 1: elegir el tipo

Cuatro tarjetas visuales le presentan los tipos disponibles con una breve descripción. Haga clic en la que corresponda a su operación comercial. El formulario se adapta automáticamente y solo muestra los campos relevantes para ese tipo.

### Paso 2: nombrar y etiquetar la oferta

Complete:

- **Nombre de la oferta** (obligatorio): lo que verá el cliente en el banner. Un campo por idioma activo de la tienda.
- **Texto del badge** (opcional, máx 64 caracteres): mensaje corto que se muestra en la pill en la parte superior del banner (por ejemplo, _1+1 GRATIS_, _OFERTA ESPECIAL_, _BLACK FRIDAY_).
- **Color del badge**: seis presets DataFirefly disponibles más un selector de color libre. El color se utiliza tanto para el banner de la ficha de producto COMO para la etiqueta de regalo del lado del carrito.

### Paso 3: añadir productos activadores

Haga clic en **Añadir un producto activador**. Se abre una modal de búsqueda con un campo que consulta su catálogo en directo (búsqueda con debounce de 250 ms tras la última pulsación). Escriba un nombre, una referencia o un EAN; los resultados aparecen inmediatamente.

Haga clic en un producto para añadirlo. Si el producto tiene variantes, aparecen como botones bajo el resultado, haga clic en la que le interesa para añadirla directamente. Indique la cantidad requerida en el campo a la derecha de la fila.

**Producto con variantes:** el botón _Producto principal (sin variante)_ añade el producto con un comodín. La oferta se aplica entonces a todas sus variantes, y la cantidad comprada se cuenta sumando todas ellas. Elija una variante concreta solo si la oferta debe limitarse a esa.

### Paso 4: añadir productos de regalo

Mismo procedimiento para los productos de regalo. Esta sección está oculta para el tipo _Elección de variantes_, ya que las variantes sirven como candidatos Y como recompensas.

### Paso 5: reglas específicas

- **Acumulable**: si se activa, la oferta se aplica varias veces por cada lote activador. Sin acumulación, la oferta se aplica una sola vez sin importar el número de unidades. Desactivado por defecto para proteger sus márgenes.
- Para el tipo _Elección de variantes_, aparecen dos campos adicionales: cuántas unidades debe comprar el cliente y cuántas son de regalo.

### Paso 6: activación

- **Fechas de validez**: deje vacío para una oferta permanente. Indique la fecha de inicio o de fin para automatizar la activación. Una fecha ilegible se rechaza, y la fecha de fin debe ser posterior a la de inicio.
- **Prioridad**: si varias ofertas pueden aplicarse simultáneamente, la de prioridad más baja se evalúa primero.
- **Estado**: interruptor on/off, activado por defecto. Útil para desactivar temporalmente una oferta sin eliminarla.

### Paso 7: tiendas (si multi-tienda)

Marque las tiendas en las que debe estar disponible la oferta. No marcar ninguna equivale a activar la oferta en todas las tiendas.

## Modificar o eliminar una oferta

Desde **Catálogo → Ofertas agrupadas**, cada fila de la lista tiene un botón **Modificar** y, en su menú desplegable, **Eliminar**. El interruptor de la columna Activada permite también suspender una oferta sin eliminarla. Para actuar sobre varias ofertas a la vez, márquelas y use las acciones agrupadas al pie de la lista.

La eliminación es definitiva y limpia todo lo que depende de la oferta: activadores, recompensas, asociaciones de tiendas y las reglas de carrito que el motor había generado para los carritos en curso. Un cliente que tenía el regalo en su carrito lo verá desaparecer en su próxima acción.

## Cómo funciona el motor de añadido automático

El motor se engancha al hook PrestaShop `actionCartSave` y se ejecuta en cada modificación del carrito (añadido, eliminación, cambio de cantidad, fusión al iniciar sesión).

1. Recupera todas las ofertas activas para la tienda actual
2. Para cada oferta, calcula la **cantidad pagada** de cada producto activador (cantidad total del carrito menos lo que el motor ya ha añadido automáticamente en una evaluación anterior)
3. Evalúa si se cumplen las condiciones de la oferta
4. Si es así, añade los productos de regalo faltantes al carrito a través de `Cart::updateQty`
5. Crea o actualiza una regla de carrito (`CartRule`) con un descuento fijo IVA incluido igual al valor de las unidades de regalo
6. Registra en la tabla `ps_dfoffers_cart_auto` las unidades que ha añadido, para distinguirlas de las unidades que el cliente añadió él mismo

El motor protege contra los bucles infinitos: `Cart::updateQty` vuelve a disparar el hook `actionCartSave`, pero una protección estática en el módulo impide la recursión.

### Revocación limpia

Si el cliente quita un producto activador o reduce su cantidad por debajo del umbral, el motor reevalúa la oferta en el siguiente `actionCartSave`. Si la condición ya no se cumple, retira las unidades que había añadido automáticamente (sin tocar las unidades que el cliente añadió él mismo gracias al tracking) y elimina la regla de carrito asociada.

## Ajustes del módulo

Desde **Módulos → Administrador de módulos → Smart Offers → Configurar**, dos ajustes globales:

- **Posición del banner en la ficha de producto**. Cinco ubicaciones, cada una correspondiente a un hook del tema Classic: El módulo está registrado en los cinco hooks y solo el elegido renderiza el banner. Si su tema no llama al hook seleccionado, el banner no aparecerá: elija otro.
   - Bajo el bloque Añadir al carrito (predeterminado): `displayProductAdditionalInfo`
   - Bajo el precio: `displayProductPriceBlock`, tipo `after_price`
   - Bajo las imágenes del producto: `displayAfterProductThumbs`
   - En el bloque de confianza, bajo los iconos de pago: `displayReassurance`
   - Ancho completo, bajo la ficha: `displayFooterProduct`
- **Banner compacto**. Fuerza en todas partes el diseño denso (márgenes reducidos, miniaturas de 92 px, grupos uno junto a otro). Sin esta opción, el banner ya adopta ese diseño por sí mismo cuando su columna mide menos de 520 px, y apila los grupos por debajo de 300 px. La detección se basa en el ancho de la columna, no en el de la ventana: una ficha de producto estrecha en un sitio ancho se trata como un móvil.

Los navegadores anteriores a 2023 no gestionan las consultas de contenedor; recurren a una detección por ancho de ventana (768 px y 400 px).

## Visualización en la ficha de producto

En cada ficha de producto activadora, un **banner con degradado** se muestra en la ubicación elegida en los ajustes (por defecto bajo el botón Añadir al carrito). Contiene:

- Un badge pill blanco con icono de regalo, conteniendo el texto del badge
- El título de la oferta
- Un mensaje dinámico que depende del tipo de oferta ("Compra 1 y recibe 1 más gratis", "Por cada lote de 10, recibe 20 más gratis", etc.)
- Una cuadrícula con las miniaturas clicables de los productos involucrados, separados en dos grupos _Compra_ / _Recibe gratis_ con un separador SVG circular entre ellos

El color del banner reutiliza el del badge configurado en la oferta. La presentación es responsive: en móvil, los dos grupos se apilan verticalmente y el separador rota para apuntar hacia abajo.

## Visualización en el carrito

Dos indicadores distintos ayudan al cliente a identificar los productos de regalo en su carrito.

### Etiqueta de regalo en cada línea

En cada línea del carrito que contiene unidades añadidas automáticamente por una oferta, aparece una pequeña etiqueta coloreada `🎁 ×N gratis` en la columna de información del producto, bajo el precio y las variantes. El color reutiliza el del badge de la oferta, y la etiqueta indica cuántas unidades de esta línea son gratuitas (útil cuando una parte de la cantidad está pagada y la otra ofrecida, por ejemplo en un 1+1 mismo producto).

Desde la 2.1.2, esta etiqueta se renderiza mediante el hook `displayProductPriceBlock` (tipo `unit_price`), que el tema Classic llama en la columna de información de cada línea del carrito. Para los temas que no llaman a ese hook, el módulo recurre a `displayCartExtraProductActions` en la columna de acciones. Un registro por petición garantiza que una línea solo se decora una vez, incluso si el tema expone ambos hooks.

### Pie de carrito detallado

En la parte inferior de la cuadrícula de productos, un bloque verde resume las ofertas activadas en el carrito. Para cada oferta, el bloque muestra:

- El nombre de la oferta y su texto de badge (en pill coloreada)
- La lista de productos ofrecidos por esta oferta, en forma de chips visuales con miniatura redonda, nombre (con su variante, por ejemplo "Cojín oso pardo (Color: Blanco)") y cantidad
- Cada chip es clicable y enlaza a la ficha del producto de regalo

El cliente puede así verificar de un vistazo lo que ha obtenido gratuitamente y gracias a qué operación comercial.

## Casos particulares y comportamientos

### Por qué el 1+1 sobre el mismo producto se trata específicamente

Cuando el producto activador también es el producto de regalo, muchos módulos de ofertas agrupadas del mercado cometen el error de identificar la unidad pagada del cliente como si ya fuera la unidad ofrecida, y aplican el descuento sobre esa unidad. Al final, el cliente paga cero por una unidad en lugar de pagar por una y recibir una segunda gratis.

Smart Offers maneja este caso con una lógica precisa: la cantidad objetivo en el carrito vale _cantidad pagada por el cliente_ + _cantidad de regalo_. Cuando el cliente añade una unidad, el motor añade una segunda para que el carrito contenga dos unidades, y el descuento se aplica solo sobre la segunda unidad. El cliente paga por tanto el precio de una unidad para tener dos en su carrito.

### Ofertas sobre productos con variantes

Una oferta puede apuntar a una variante concreta o al producto en su conjunto. En el back office, añadir el producto mediante _Producto principal (sin variante)_ guarda un comodín: el motor lo lee exactamente como el banner, es decir "cualquier variante".

- **Activador con comodín**: la cantidad comprada es la suma de todas las variantes del producto presentes en el carrito. Dos cojines blancos y uno negro cuentan como tres unidades.
- **Recompensa con comodín**: el motor debe elegir una variante concreta antes de añadir al carrito. Primero toma la que el cliente ya tiene en su carrito para ese producto (un 1+1 sobre un cojín blanco añade un cojín blanco). Si el producto aún no está en el carrito, toma la variante por defecto definida en el catálogo. Un producto sin variantes se añade tal cual.
- **Elección de variantes con comodín**: cada variante presente en el carrito se convierte en una línea candidata por derecho propio, lo que permite a la ordenación por precio designar las más baratas.

Antes de la 2.0.1, una oferta guardada con comodín nunca se activaba cuando el cliente añadía una variante: el banner se mostraba pero nada ocurría en el carrito. Si observa ese síntoma, actualice el módulo.

### Acumulación de lotes (opción stackable)

Sin acumulación, la oferta se aplica una sola vez sin importar el número de lotes activadores presentes en el carrito. Si el cliente compra 5 unidades de un producto con una oferta 1+1 y stackable desactivado, recibirá 1 unidad de regalo (no 5).

Con la acumulación activada, el motor multiplica el número de lotes de recompensa por el número entero de lotes activadores presentes. Para la misma oferta 1+1 con stackable activado y 5 unidades en el carrito, el cliente recibirá 5 unidades de regalo (carrito final: 10 unidades, 5 pagadas).

La opción stackable está desactivada por defecto. Actívela con precaución: puede comerse significativamente sus márgenes en operaciones de alto volumen.

### Stock e indisponibilidad

El añadido de los productos de regalo al carrito pasa por `Cart::updateQty`, que respeta las reglas de stock nativas de PrestaShop. Si un producto de regalo está sin stock y la tienda no permite el pedido sin stock, el añadido falla silenciosamente y el descuento no se aplica. La condición queda lista para activarse en cuanto haya stock disponible.

### Múltiples ofertas simultáneas en un mismo carrito

Cada oferta genera su propia regla de carrito con `partial_use` activado. Esto permite apilar varias ofertas concurrentes en un mismo carrito sin conflicto, y sigue siendo compatible con los códigos de descuento clásicos que sus clientes pueden introducir.

## Idiomas del módulo

Desde la 2.1.2, el idioma fuente del módulo es el inglés y se entregan siete traducciones en la carpeta `translations/`: francés, alemán, italiano, español, neerlandés, portugués y polaco. Cubren el banner de la ficha de producto, el carrito, el nombre de la regla de carrito que ve el cliente, el formulario de creación de oferta y los mensajes del selector de productos.

Cada cadena sigue siendo modificable desde **Internacional → Traducciones** eligiendo el módulo _dfoffers_. Una tienda en un idioma no suministrado muestra el inglés y puede traducirse en el mismo lugar.

No confundir con el nombre, el badge y la descripción de cada oferta, que usted mismo introduce en cada idioma activo de la tienda al crear la oferta.

## Arquitectura técnica

### Hooks utilizados

- `displayProductAdditionalInfo`, `displayProductPriceBlock` (tipo `after_price`), `displayAfterProductThumbs`, `displayReassurance`, `displayFooterProduct`: banner en la ficha de producto, uno solo activo según los ajustes
- `displayShoppingCartFooter`: pie detallado en la página del carrito
- `displayProductPriceBlock`: etiqueta de regalo en la columna de información de cada línea del carrito (tipo `unit_price`, solo página del carrito)
- `displayCartExtraProductActions`: etiqueta de regalo de respaldo, en la columna de acciones
- `actionCartSave`: motor de evaluación y añadido automático
- `actionFrontControllerSetMedia` y `actionAdminControllerSetMedia`: inyección de CSS y JS
- `actionObjectProductDeleteAfter`: limpieza automática de ofertas que hacen referencia a un producto eliminado

Solo `actionCartSave` y `actionFrontControllerSetMedia` se consideran indispensables para la instalación. Los hooks de visualización que un tema no implemente se registran en el log sin hacer fallar la instalación.

### Tablas añadidas

- `ps_dfoffers_offer`: configuración de cada oferta (tipo, fechas, prioridad, acumulación)
- `ps_dfoffers_offer_lang`: nombre, badge y descripción traducidos por idioma
- `ps_dfoffers_trigger`: productos activadores de cada oferta
- `ps_dfoffers_reward`: productos de regalo de cada oferta
- `ps_dfoffers_shop`: asociación oferta / tienda en multi-tienda
- `ps_dfoffers_cart_auto`: tracking de las unidades añadidas automáticamente por carrito y por oferta, con el identificador de la regla de carrito generada

Todas las tablas utilizan el prefijo configurado en su instalación PrestaShop (`ps_` por defecto). El esquema es idéntico en PrestaShop 8 y 9, lo que hace la migración transparente.

### La clase DfOfferCompat

Todas las diferencias de API entre PrestaShop 8 y 9 se concentran en `classes/DfOfferCompat.php`. El resto del módulo nunca comprueba la versión de PrestaShop directamente. Los puntos absorbidos por esta clase:

- **Lectura de variantes**: PrestaShop 9 retiró el argumento de idioma de `Product::getAttributeCombinations()`, donde el primer parámetro es ahora el indicador booleano de agrupación.
- **URL AJAX del backoffice**: en PrestaShop 9, el par `ajax` y `action` debe transitar por el cuarto argumento de `getAdminLink()`, porque el token se calcula antes de la fusión de los parámetros.
- **Respuesta JSON**: el método de envío lleva deliberadamente un nombre distinto de `ajaxRender()`, cuya firma heredada no debe sobrescribirse.
- **Pestaña admin**: PrestaShop 9 introdujo columnas de traducción adicionales, rellenadas bajo una comprobación para no crear una propiedad dinámica en PrestaShop 8.

### Sobrescribir plantillas en su tema

El CSS del módulo está aislado bajo el prefijo `.dfoffers-` para evitar conflictos con su hoja de estilo. Si desea modificar la presentación, copie las plantillas desde `/modules/dfoffers/views/templates/hook/` hacia `/themes/su-tema/modules/dfoffers/views/templates/hook/` y personalícelas. Tres plantillas están disponibles:

- `product-banner.tpl`: banner en la ficha de producto
- `cart-offer.tpl`: bloque resumen en el pie del carrito
- `cart-line-gift.tpl`: etiqueta de regalo inline en las líneas del carrito

## Actualización del módulo

Para actualizar a una nueva versión, simplemente suba el nuevo ZIP desde el Administrador de módulos. PrestaShop detecta el cambio de versión en `config.xml` y ejecuta automáticamente los scripts de actualización presentes en `/upgrade/upgrade-X.Y.Z.php`, que se encargan por ejemplo de registrar nuevos hooks añadidos entre versiones.

No se necesita desinstalación/reinstalación entre versiones, y sus ofertas existentes se conservan intactas.

### Migrar una tienda de PrestaShop 8 a PrestaShop 9

Al ser idéntico el esquema de base de datos, sus ofertas, sus traducciones y sus asociaciones de tiendas pasan la migración sin transformación. El procedimiento recomendado:

1. Pase el módulo a 2.x **antes** de migrar la tienda, mientras todavía funciona sobre PrestaShop 8. Las versiones 2.x funcionan en ambas generaciones, así reduce el número de variables si algo sale mal.
2. Migre la tienda a PrestaShop 9 siguiendo el procedimiento oficial de PrestaShop.
3. Vaya a **Diseño → Posiciones** y compruebe que los hooks del módulo siguen conectados. Una migración puede perder alguno.
4. Si faltan hooks, vuelva a subir el ZIP: el script de actualización registra de nuevo cada hook ausente y limpia las filas de seguimiento cuyo carrito ya no existe.

PrestaShop 9 requiere PHP 8.1 como mínimo. Compruebe la versión de PHP de su alojamiento antes de lanzar la migración: es la causa de fallo más frecuente, muy por delante de los módulos.

## Solución de problemas

### Los productos de regalo no se añaden al carrito

1. Vacíe la caché de PrestaShop en **Parámetros avanzados → Rendimiento**
2. Verifique que el hook `actionCartSave` contiene el módulo en **Diseño → Posiciones**
3. Verifique que el producto de regalo está disponible (no agotado si los pedidos sin stock están prohibidos, no desactivado, asignado a la tienda actual)
4. Si el producto activador tiene variantes y el banner sí se muestra en la ficha, compruebe que el módulo está en 2.0.1 o superior: las versiones anteriores no leían el comodín "todas las variantes" del lado del motor
5. Consulte **Parámetros avanzados → Logs** buscando `dfoffers`: el motor traza su ejecución en cada modificación del carrito

### El descuento no se aplica a pesar del añadido del producto

Verifique en los logs la línea `checkValidity` que sigue a la creación de la regla de carrito. PrestaShop indica con precisión por qué se rechaza una regla (agotado, restricción de cliente, divisa diferente, etc.).

### La etiqueta de regalo no aparece en las líneas del carrito

El módulo busca primero el hook `displayProductPriceBlock` en `cart-detailed-product-line.tpl`, luego `displayCartExtraProductActions`. Los temas Classic de PrestaShop 8 y 9 y la mayoría de temas comerciales contienen al menos uno de los dos. Si su tema personalizado no implementa ninguno, añada una de estas líneas en su archivo `cart-detailed-product-line.tpl`, preferiblemente la primera en la columna de información del producto:

```
{hook h='displayProductPriceBlock' product=$product type="unit_price"}
{hook h='displayCartExtraProductActions' product=$product}
```

### La etiqueta está truncada o solo muestra el icono

Síntoma de las versiones 2.1.0 y 2.1.1 en el tema Classic, donde la etiqueta se renderizaba en la columna de acciones, demasiado estrecha para texto. Corregido en 2.1.2 trasladándola a la columna de información del producto. Actualice el módulo.

### Error 500 al guardar una oferta

Antes de la 2.2.0, un nombre o texto de badge que contenía `=`, `;`, `#`, `{` o `}` era rechazado por los validadores de PrestaShop y el guardado terminaba en una página en blanco. Desde la 2.2.0 esos caracteres se aceptan, y cualquier valor realmente no válido se señala en el formulario en lugar de provocar un error. Si sigue encontrando un 500, consulte **Parámetros avanzados → Logs**: la causa queda registrada bajo `dfoffers save failed`.

### La búsqueda de productos del backoffice no devuelve nada tras una migración a PrestaShop 9

Vacíe la caché de PrestaShop y recargue la página de creación de oferta. La URL del endpoint de búsqueda se construye en el servidor al renderizar el formulario; una página cacheada antes de la migración puede seguir llevando una URL antigua. Si el problema persiste, abra la consola del navegador: una respuesta 404 en la petición de búsqueda indica que la pestaña del módulo no se recreó correctamente, y reinstalar el módulo lo corrige sin perder las ofertas.

### La instalación parece correcta pero no se puede crear ninguna oferta

Antes de la 2.0.0, un fallo en la creación de tablas durante la instalación era silencioso y el módulo aparecía como instalado. Desde la 2.0.0, esta situación hace fallar la instalación con un mensaje explícito. Si se encuentra este caso en una versión antigua, compruebe los permisos del usuario MySQL sobre la creación de tablas, luego desinstale y reinstale el módulo.

## Preguntas frecuentes

### ¿El módulo es compatible con PrestaShop 9?

Sí, desde la versión 2.0.0. El mismo archivo ZIP se instala tanto en PrestaShop 8.0 como en PrestaShop 9.x. Las diferencias de API las absorbe la clase interna `DfOfferCompat`, por lo que no hay que elegir una rama concreta al descargar. Las versiones 1.x seguían limitadas a PrestaShop 8.0 a 8.99.

### ¿Cuál es el impacto en el rendimiento?

El motor ejecuta una consulta SQL por oferta activa en la tienda, luego evalúa las condiciones en memoria. En un catálogo con una decena de ofertas activas, la evaluación completa tarda en promedio menos de cincuenta milisegundos. Esta cifra es idéntica en PrestaShop 8 y 9.

### ¿Puedo utilizar el módulo con un tema headless?

El motor de añadido automático es independiente del tema y funciona para cualquier frontal que pase por `Cart::updateQty` o la API REST de PrestaShop. El banner de ficha de producto y la etiqueta de regalo del carrito son hooks Smarty nativos que necesitan un tema clásico para mostrarse. Para un front headless, puede exponer los datos a través de una API custom que consulte directamente `ps_dfoffers_offer` y `ps_dfoffers_cart_auto`.

### ¿El módulo gestiona múltiples divisas?

Sí. La regla de carrito generada para cada oferta utiliza la divisa del carrito actual. Si el cliente cambia de divisa, la regla se regenera con el valor correcto en el siguiente `actionCartSave`.

### ¿Qué ocurre si hago clic en Restablecer en el administrador de módulos?

El módulo se desinstala y se reinstala en la misma petición, lo que elimina y vuelve a crear las tablas: todas sus ofertas se pierden. Desde la 2.0.0 esta operación recrea correctamente las tablas, mientras que las versiones anteriores dejaban la tienda sin tablas en absoluto. En ambos casos, haga una copia de seguridad antes de restablecer.
