PS PrestaShop Intermediate

Smart Offers — Complete documentation

Everything you need to configure and operate the Smart Offers module: the four bundled offer types, the auto-add engine, and the v1.1.0 customer display.

Updated Module version 1.1.0

Smart Offers is a PrestaShop 8 module that lets you create 1+1 deals, bulk packs, multi-product bundles, and variant-choice offers — with auto-added reward products and a polished product page presentation.

Overview

Smart Offers covers the four most-used bundled offer formats in e-commerce in a single module, without complex configuration. The engine evaluates the cart on every change, automatically adds reward products as soon as conditions are met, and creates a cart rule that makes those units free — the customer experience is immediate and readable.

In short: The merchant creates an offer in under a minute via a visual interface, the customer sees the gift appear automatically in their cart with a clear indicator, and internal tracking allows clean revocation if the customer removes a trigger during the session.

Installation

  1. Download the dfoffers-vX.Y.Z.zip file from your DataFirefly customer area
  2. In the PrestaShop back office, go to Modules → Module Manager
  3. Click the Upload a module button at the top
  4. Drag-drop the ZIP file or click to select it
  5. Installation is automatic: tables are created, hooks are registered, and a new Catalog → Bundled offers tab appears in the menu
No external dependency is required. The module uses native PrestaShop classes (Cart, CartRule, Product) and adds nothing to your composer.json.

The four offer types

1+1 on the same product

The viral buy-one-get-one format: the customer buys one unit of a product and receives another unit of the same product for free. You configure:

  • A single product (used as both trigger and reward)
  • The quantity to buy to trigger the offer (usually 1)
  • The free quantity (usually 1)

Typical example: “Buy 1 pair of socks, the second is free.” When the customer adds the pair to their cart, the engine automatically adds a second one and applies a discount equal to the unit price.

Buy X, get Y for free (different products)

Bundle format: several distinct trigger products must be in the cart for the offer to activate, and one or more different products are then offered for free. You configure:

  • The list of trigger products with their respective quantities
  • The list of reward products with their respective quantities

Typical example: “Buy a day cream and a serum together, get a mask sample for free.” The engine checks all triggers are present before activating the offer.

Variant choice

Free composition format: you define a set of candidate products or variants from which the customer composes their lot. The engine automatically identifies the cheapest cart items as the free units, which matches the standard commercial interpretation of buy-N-get-M.

  • The list of candidate products or variants
  • The number of units to buy from this set
  • The number of free units (the cheapest ones)

Typical example: “Buy 3 t-shirts from our selection, the cheapest is free.” The customer composes their lot, the engine doesn’t touch their cart but applies a discount on the cheapest units.

Bulk pack

B2B and stock clearance format: for every X units bought of a product, the customer receives Y free units of another product. You configure:

  • The trigger product with the tier quantity (e.g., 10)
  • The reward product with the offered quantity (e.g., 20)

Typical example: “Buy 10 bottles of wine, get 2 glasses free.” Practical for suppliers who want to push a complementary product or clear dormant stock by attaching it to a best-seller.

Creating your first offer

From the back office, go to Catalog → Bundled offers then click New offer.

Step 1: pick the type

Four visual cards present the available types with a short description. Click the one matching your commercial operation. The form adapts automatically and only shows fields relevant to that type.

Step 2: name and badge the offer

Fill in:

  • Offer name (required): what the customer sees on the banner. Available in five languages.
  • Badge text (optional, max 64 chars): short message shown in the pill at the top of the banner (e.g., BOGO, SPECIAL OFFER, BLACK FRIDAY).
  • Badge color: six DataFirefly presets available plus a free color picker. The color is used for both the product page banner AND the cart gift badge.

Step 3: add trigger products

Click Add a trigger product. A search modal opens with a field that queries your catalog live (debounced 250 ms after the last keystroke). Type a name, a reference, or an EAN; results appear immediately.

Click on a product to add it. If the product has variants, they appear as buttons under the result — click the one you want to add it directly. Set the required quantity in the field to the right of the row.

Step 4: add reward products

Same procedure for reward products. This section is hidden for the Variant choice type, since the variants serve as both candidates and rewards.

Step 5: specific rules

  • Stackable: if enabled, the offer applies multiple times for each trigger lot. Without stacking, the offer applies only once regardless of the number of units. Disabled by default to protect your margins.
  • For the Variant choice type, two extra fields appear: how many units the customer must buy, and how many are offered for free.

Step 6: activation

  • Validity dates: leave empty for a permanent offer. Fill in start or end date to automate activation.
  • Priority: if multiple offers can apply simultaneously, the one with the lowest priority is evaluated first.
  • Status: on/off toggle, enabled by default. Useful to temporarily disable an offer without deleting it.

Step 7: stores (if multi-store)

Check the stores on which the offer should be available. Leaving all unchecked means the offer applies to all stores.

How the auto-add engine works

The engine hooks into PrestaShop’s actionCartSave and runs on every cart change (add, remove, quantity change, merge on login).

  1. It fetches all active offers for the current store
  2. For each offer, it computes the paid quantity of each trigger product (total cart quantity minus what the engine has already auto-added in a previous evaluation)
  3. It evaluates whether the offer conditions are met
  4. If so, it adds the missing reward products to the cart via Cart::updateQty
  5. It creates or updates a cart rule (CartRule) with a fixed tax-incl discount equal to the value of the offered units
  6. It records in the ps_dfoffers_cart_auto table the units it added, to distinguish them from units the customer added themselves
The engine protects against infinite loops: Cart::updateQty re-fires the actionCartSave hook, but a static guard in the module prevents recursion.

Clean revocation

If the customer removes a trigger product or reduces its quantity below the threshold, the engine re-evaluates the offer on the next actionCartSave. If the condition is no longer met, it removes the units it had auto-added (without touching the units the customer added themselves, thanks to tracking) and deletes the associated cart rule.

Product page display

On every trigger product page, a gradient banner is displayed via the displayProductAdditionalInfo hook. It contains:

  • A white pill badge with a gift icon, containing the badge text
  • The offer title
  • A dynamic message depending on the offer type (“Add 1 product, get 1 free”, “For 10 units bought, 20 units free”, etc.)
  • A grid with clickable thumbnails of the involved products, separated into two groups Buy / Get free with a circular SVG separator between them

The banner color uses the badge color configured for the offer. The rendering is responsive: on mobile, the two groups stack vertically and the separator rotates to point downward.

Cart display

From version 1.1.0, two distinct indicators help the customer identify gift products in their cart.

Gift badge on each line

On every cart line that contains units auto-added by an offer, a small colored badge 🎁 ×N free appears next to the line actions. The color matches the offer badge color, and the badge shows how many units of this line are free (useful when part of the quantity is paid and the other is offered, e.g., on a 1+1 same product).

This badge is rendered via the displayCartExtraProductActions hook, present in all standard PrestaShop 8 themes that follow the native structure of cart-detailed-product-line.tpl.

At the bottom of the product grid, a green block summarizes the offers active in the cart. For each offer, the block shows:

  • The offer name and its badge text (in a colored pill)
  • The list of products offered by this offer, as visual chips with a round thumbnail, name, and quantity
  • Each chip is clickable and links to the gift product page

The customer can quickly verify what they got for free and through which commercial operation.

Edge cases and behaviors

Why 1+1 on the same product is handled specifically

When the trigger product is also the reward product, many bundled offer modules on the market make the mistake of identifying the customer’s paid unit as already being the offered unit, and apply the discount on that unit — in the end, the customer pays zero for one unit instead of paying for one and receiving a second one free.

Smart Offers handles this case with precise logic: the target quantity in the cart equals quantity paid by the customer + reward quantity. When the customer adds one unit, the engine adds a second one so the cart contains two units, and the discount applies only on the second unit. The customer therefore pays the price of one unit to have two in their cart.

Stacking lots (stackable option)

Without stacking, the offer applies only once regardless of the number of trigger lots in the cart. If the customer buys 5 units of a product with a 1+1 offer and stackable disabled, they receive 1 free unit (not 5).

With stacking enabled, the engine multiplies the number of reward lots by the integer number of trigger lots present. For the same 1+1 offer with stackable enabled and 5 units in the cart, the customer receives 5 free units (final cart: 10 units, 5 paid).

The stackable option is disabled by default. Enable it carefully: it can significantly eat into your margins on high-volume operations.

Stock and unavailability

Adding reward products to the cart goes through Cart::updateQty, which respects PrestaShop’s native stock rules. If a reward product is out of stock and the store doesn’t allow ordering out-of-stock products, the add silently fails and the discount is not applied — the condition stays ready to fire as soon as the product is back in stock.

Multiple simultaneous offers on the same cart

Each offer generates its own cart rule with partial_use enabled. This allows stacking multiple concurrent offers on the same cart without conflict, and remains compatible with classic coupon codes your customers can enter.

Technical architecture

Hooks used

  • displayProductAdditionalInfo: banner on the product page
  • displayShoppingCartFooter: detailed footer on the cart page
  • displayCartExtraProductActions: gift badge on each cart line
  • actionCartSave: evaluation and auto-add engine
  • actionFrontControllerSetMedia and actionAdminControllerSetMedia: CSS and JS injection
  • actionObjectProductDeleteAfter: automatic cleanup of offers referencing a deleted product

Tables added

  • ps_dfoffers_offer: each offer’s configuration (type, dates, priority, stacking)
  • ps_dfoffers_offer_lang: translated name, badge, and description per language
  • ps_dfoffers_trigger: trigger products of each offer
  • ps_dfoffers_reward: reward products of each offer
  • ps_dfoffers_shop: offer / store association in multi-store
  • ps_dfoffers_cart_auto: tracking of auto-added units per cart and per offer, with the ID of the generated cart rule

All tables use the prefix configured in your PrestaShop installation (ps_ by default).

Overriding templates in your theme

The module’s CSS is namespaced under the .dfoffers- prefix to avoid conflicts with your stylesheet. If you want to modify the rendering, copy the templates from /modules/dfoffers/views/templates/hook/ to /themes/your-theme/modules/dfoffers/views/templates/hook/ and customize them. Three templates are available:

  • product-banner.tpl: banner on the product page
  • cart-offer.tpl: summary block in the cart footer
  • cart-line-gift.tpl: inline gift badge on cart lines

Updating the module

To update to a new version, simply upload the new ZIP from the Module Manager. PrestaShop detects the version change in config.xml and automatically runs the upgrade scripts present in /upgrade/upgrade-X.Y.Z.php, which handle for example registering new hooks added between versions.

No uninstall/reinstall is needed between versions, and your existing offers are preserved intact.

Troubleshooting

Reward products are not added to the cart

  1. Clear the PrestaShop cache in Advanced Parameters → Performance
  2. Verify that the actionCartSave hook contains the module in Design → Positions
  3. Verify that the reward product is available (not out of stock if back-orders are forbidden, not disabled, assigned to the current store)
  4. Check Advanced Parameters → Logs searching for dfoffers: the engine traces its execution on every cart change

The discount is not applied even though the product was added

Check the logs for the checkValidity line that follows the cart rule creation. PrestaShop tells you exactly why a rule is rejected (out of stock, customer restriction, different currency, etc.).

The gift badge does not appear on cart lines

Verify that your theme implements the displayCartExtraProductActions hook in cart-detailed-product-line.tpl. The Classic theme and most commercial themes contain it. If you use a custom theme that doesn’t implement it, add the following line in your cart-detailed-product-line.tpl file at the desired position:

{hook h='displayCartExtraProductActions' product=$product}

Frequently asked questions

Is the module compatible with PrestaShop 9?

No, the 1.x version targets PrestaShop 8.0 to 8.99 only. A 2.x version compatible with PrestaShop 9 is planned separately due to significant changes to the controller API in PS 9.

What is the performance impact?

The engine runs one SQL query per active offer on the store, then evaluates conditions in memory. On a catalog with around ten active offers, full evaluation takes on average less than fifty milliseconds.

Can I use the module with a headless theme?

The auto-add engine is theme-independent and works for any front-end that goes through Cart::updateQty or the PrestaShop REST API. The product page banner and the cart gift badge are native Smarty hooks that require a classic theme to display. For a headless front, you can expose the data via a custom API that queries ps_dfoffers_offer and ps_dfoffers_cart_auto directly.

Does the module handle multiple currencies?

Yes. The cart rule generated for each offer uses the current cart’s currency. If the customer changes currency, the rule is regenerated at the correct value on the next actionCartSave.

Was this page helpful?

Still stuck? Contact support