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.
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.
Installation
- Download the
dfoffers-vX.Y.Z.zipfile from your DataFirefly customer area - In the PrestaShop back office, go to Modules → Module Manager
- Click the Upload a module button at the top
- Drag-drop the ZIP file or click to select it
- Installation is automatic: tables are created, hooks are registered, and a new Catalog → Bundled offers tab appears in the menu
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).
- It fetches all active offers for the current store
- 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)
- It evaluates whether the offer conditions are met
- If so, it adds the missing reward products to the cart via
Cart::updateQty - It creates or updates a cart rule (
CartRule) with a fixed tax-incl discount equal to the value of the offered units - It records in the
ps_dfoffers_cart_autotable the units it added, to distinguish them from units the customer added themselves
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).
displayCartExtraProductActions hook, present in all standard PrestaShop 8 themes that follow the native structure of cart-detailed-product-line.tpl.Detailed cart footer
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).
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 pagedisplayShoppingCartFooter: detailed footer on the cart pagedisplayCartExtraProductActions: gift badge on each cart lineactionCartSave: evaluation and auto-add engineactionFrontControllerSetMediaandactionAdminControllerSetMedia: CSS and JS injectionactionObjectProductDeleteAfter: 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 languageps_dfoffers_trigger: trigger products of each offerps_dfoffers_reward: reward products of each offerps_dfoffers_shop: offer / store association in multi-storeps_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 pagecart-offer.tpl: summary block in the cart footercart-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.
Troubleshooting
Reward products are not added to the cart
- Clear the PrestaShop cache in Advanced Parameters → Performance
- Verify that the
actionCartSavehook contains the module in Design → Positions - Verify that the reward product is available (not out of stock if back-orders are forbidden, not disabled, assigned to the current store)
- 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.