Skip to content

Gift Rules & Free Products

Flow ID: CF-14 | Module(s): eshop, Promotion domain | Complexity: High | Last Updated: 2026-05-19

Business Overview

Gift rules automatically grant free products when cart meets conditions. 13 rule types cover: specific products, vendor products, cart total, product combinations, and the special "cheapest product free" (Rule 13).

Key business behaviors:

  • Evaluated on every cart render via AdvCartResource::getGifts()
  • Gift quantity: floor(validatorResult / gift_per_count), capped by remaining stock
  • Rule 13 special: cheapest qualifying product becomes free (no gift selection)
  • Customer can choose from eligible gifts (gift_user_choice_count limit)

API Reference

REST Endpoints

MethodPathAuthDescription
GET/rest/promotion/giftBackendList gift rules
POST/rest/promotion/giftBackendCreate gift rule
GET/rest/promotion/gift-requirementBackendList requirements
GET/rest/promotion/gift-choiceBackendList gift choices

13 Rule Types

RuleValidatorTrigger
1ruleProductsValidatorSpecific products in cart
2ruleVendorsValidatorVendor products in cart
3ruleTotalCartValidatorCart total in amount range
4ruleProductsTotalCartValidatorProducts + cart total
5ruleProductsTotalMinAmountValidatorProducts' total value in range
6ruleVendorsTotalCartValidatorVendors + cart total
7ruleVendorsTotalMinAmountValidatorVendors' total value in range
8ruleVendorsTotalMinPriceValidatorIndividual vendor product prices
9ruleProductsTotalMinPriceValidatorIndividual product prices
10ruleProductsCombinationValidatorALL required products present (minimum qty)
11ruleProductsValidatorOneGiftProducts, caps at 1 gift
12ruleVendorsValidatorReturnOneGiftVendors, caps at 1 gift
13ruleProductsCheapestFreeValidatorCheapest requirement product = free

Rule 13 Special Handling

Doesn't use gift_choices table. Instead finds cheapest requirement product via getCheapestRequirementProductInCart(). In order pricing, paidQty = quantity - rule13GiftCount.

Save-side invariant (4.101.0): dom_gift_ID is stripped on save and historical orphan rows were purged — see AD-09 Gift Rules Admin for the full save-pipeline detail and cleanup migration.

Admin path convergence (4.101.0): Both admin order paths in Adv_orders_admin.php now delegate to filterApplicableGiftRules() — see AD-09 Gift Rules Admin for details.

REST checkout (src/Domains/Checkout/PlaceOrderService.php) does not yet apply gift rules — any order placed via /rest/checkout/place-order skips Rule 13 and all other gift rule evaluations. Tracked as #85.


Known Issues & Security Gaps

  1. Race on gifts.remaining counterAdv_gifts_model::updateCounter() (ecommercen/eshop/models/Adv_gifts_model.php:1192-1199) decrements the remaining counter with a non-transactional read-then-write. Two concurrent checkouts can both observe remaining=1, both pass the eligibility check, and both decrement — driving the counter negative. Self-corrects on the next evaluation (negative remaining fails the >0 filter) but the N-th concurrent buyer still receives the gift. Tracked as #203.

  2. [RESOLVED 4.101.0, commit bd187f2db] Admin order validation dropped Rule 13 gifts — see AD-09 Known Issues for the full resolution detail.

  3. [SAVE-SIDE RESOLVED 4.101.0, commit 879423e21] Rule 13 admin form persisted dom_gift_ID to gift_choices (never read at runtime) — see AD-09 Known Issues for the fix and cleanup migration detail.

  4. [RENDER-SIDE OPEN] Admin form still renders the gift-products picker when Rule 13 is selected — see AD-09 Known Issues for the open tracking detail.

  5. [OPEN — client override risk] Any client repo that overrides Adv_orders_admin::validateProductGiftSelections() and retains the old inline array_filter closure will continue to drop Rule 13 gifts from admin order validation. Check application/modules/eshop/controllers/Adv_orders_admin.php in client repos for this pattern.


Client Extension Points

  • Gift model override: Custom validator logic, new rule types
  • Gift rules model: Rule definitions, field visibility per type
  • Admin: postDataRuleCleanupHelper() strips inapplicable fields per rule

Data Model

TablePurpose
gifts / gifts_muiGift rule master + translations
gift_requirementsTrigger conditions (option_type 1=product, 2=vendor)
gift_choicesProducts offered as gifts

For full column-level schema details, see AD-09 Gift Rules Admin.

Wiki Guide: Detailed gift module documentation — see Gifts Module.