Contexte
La branche 9.1.x de PrestaShop introduit un nouveau système de remises (Discounts), protégé derrière un feature flag. Ce changement structurel se reflète directement dans classes/CartRule.php, qui sert de fondation au nouveau système tout en maintenant la rétro-compatibilité avec le système historique de cart rules.
Sources : DevDocs 9.1.x, Blog officiel, Core Monthly Janvier 2026, branche
developde GitHub.
1. Nouveaux imports et dépendances
9.0.x
La classe CartRuleCore dans la 9.0.x utilise les imports classiques PrestaShop sans dépendance vers le nouveau système de remises.
9.1.x (develop)
De nouveaux use statements apparaissent en tête de fichier :
<span class="kn">use</span> <span class="nc">PrestaShop\PrestaShop\Adapter\ContainerFinder</span><span class="p">;</span>
<span class="kn">use</span> <span class="nc">PrestaShop\PrestaShop\Adapter\Discount\Application\DiscountApplicationService</span><span class="p">;</span>
<span class="kn">use</span> <span class="nc">PrestaShop\PrestaShop\Core\Domain\CartRule\CartRuleSettings</span><span class="p">;</span>
<span class="kn">use</span> <span class="nc">PrestaShop\PrestaShop\Core\Domain\Discount\DiscountSettings</span><span class="p">;</span>
<span class="kn">use</span> <span class="nc">PrestaShop\PrestaShop\Core\Domain\Discount\ValueObject\DiscountType</span><span class="p">;</span>
<span class="kn">use</span> <span class="nc">PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagSettings</span><span class="p">;</span>
<span class="kn">use</span> <span class="nc">PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagStateCheckerInterface</span><span class="p">;</span>
Impact : La classe CartRule s’intègre désormais avec :
DiscountType: un Value Object qui distingue les niveaux d’application (ORDER_LEVEL,PRODUCT_LEVEL)DiscountApplicationService: le service qui orchestre l’application des remises dans le nouveau systèmeFeatureFlagSettings/FeatureFlagStateCheckerInterface: pour vérifier dynamiquement si le nouveau système Discount est activé
2. Intégration du Feature Flag Discount
Nouvelle méthode : isDiscountFeatureFlagEnabled()
Un ajout clé dans la 9.1.x est la méthode privée qui vérifie l’état du feature flag :
<span class="k">private</span> <span class="k">function</span> <span class="n">isDiscountFeatureFlagEnabled</span><span class="p">():</span> <span class="kt">bool</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nv">$this</span><span class="o">-></span><span class="nf">getFeatureFlagManager</span><span class="p">()</span> <span class="o">!==</span> <span class="kc">null</span>
<span class="o">&&</span> <span class="nv">$this</span><span class="o">-></span><span class="nf">getFeatureFlagManager</span><span class="p">()</span><span class="o">-></span><span class="nf">isEnabled</span><span class="p">(</span><span class="nc">FeatureFlagSettings</span><span class="o">::</span><span class="no">FEATURE_FLAG_DISCOUNT</span><span class="p">);</span>
<span class="p">}</span>
Rôle : Cette méthode agit comme un aiguillage dans l’ensemble du fichier. Quand le flag est désactivé (par défaut), le comportement reste identique à la 9.0.x. Quand il est activé, les nouvelles logiques de calcul prennent le relais.
Nouvelle méthode : getFeatureFlagManager()
Méthode protégée qui instancie le FeatureFlagStateCheckerInterface depuis le container Symfony :
<span class="k">protected</span> <span class="k">function</span> <span class="n">getFeatureFlagManager</span><span class="p">():</span> <span class="kt">?FeatureFlagStateCheckerInterface</span>
3. Nouveau concept : getType() — Typage des remises
Ajout dans 9.1.x
La méthode getType() est introduite pour classifier une cart rule selon le nouveau modèle de remises à 4 types :
Type Description Catalog (PRODUCT_LEVEL) Remise appliquée à un produit ou segment de produits Cart (ORDER_LEVEL) Remise sur le montant total du panier (hors frais de port) Free Shipping Livraison gratuite Free Gift Produit offert ajouté à la commande Cette méthode retourne un DiscountType Value Object basé sur la configuration interne de la cart rule (champs reduction_product, free_shipping, gift_product, etc.).
Impact développeur : Les modules qui interrogent le type de remise peuvent désormais utiliser $cartRule->getType() au lieu de vérifier manuellement les champs individuels.
4. Modifications dans getContextualValue()
La méthode getContextualValue() — le cœur du calcul des remises — contient les changements les plus significatifs.
4.1 Réduction en pourcentage au niveau commande (ORDER_LEVEL)
9.0.x : La réduction en pourcentage est appliquée uniquement sur le total produits.
9.1.x : Quand le feature flag est activé et que le type est ORDER_LEVEL :
<span class="k">if</span> <span class="p">(</span><span class="nv">$this</span><span class="o">-></span><span class="nb">getType</span><span class="p">()</span> <span class="o">===</span> <span class="nc">DiscountType</span><span class="o">::</span><span class="no">ORDER_LEVEL</span>
<span class="o">&&</span> <span class="nv">$this</span><span class="o">-></span><span class="n">reduction_percent</span> <span class="o">></span> <span class="mf">0.00</span>
<span class="o">&&</span> <span class="nv">$this</span><span class="o">-></span><span class="n">reduction_product</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$order_products_total</span> <span class="o">=</span> <span class="nv">$context</span><span class="o">-></span><span class="n">cart</span><span class="o">-></span><span class="nf">getOrderTotal</span><span class="p">(</span><span class="nv">$use_tax</span><span class="p">,</span> <span class="nc">Cart</span><span class="o">::</span><span class="no">ONLY_PRODUCTS</span><span class="p">,</span> <span class="nv">$package_products</span><span class="p">);</span>
<span class="nv">$order_shipping_total</span> <span class="o">=</span> <span class="nv">$context</span><span class="o">-></span><span class="n">cart</span><span class="o">-></span><span class="nf">getOrderTotal</span><span class="p">(</span><span class="nv">$use_tax</span><span class="p">,</span> <span class="nc">Cart</span><span class="o">::</span><span class="no">ONLY_SHIPPING</span><span class="p">,</span> <span class="nv">$package_products</span><span class="p">);</span>
<span class="nv">$order_total</span> <span class="o">=</span> <span class="nv">$order_products_total</span> <span class="o">+</span> <span class="nv">$order_shipping_total</span><span class="p">;</span>
<span class="nv">$reduction_value</span> <span class="o">+=</span> <span class="nv">$order_total</span> <span class="o">*</span> <span class="nv">$this</span><span class="o">-></span><span class="n">reduction_percent</span> <span class="o">/</span> <span class="mi">100</span><span class="p">;</span>
<span class="p">}</span>
Changement clé : Le pourcentage s’applique maintenant sur produits + frais de port au lieu de produits seuls.
4.2 Réduction en montant fixe — plafonné différemment
9.0.x : Le montant de réduction est plafonné au total produits.
9.1.x : Pour ORDER_LEVEL avec le feature flag activé, le plafond inclut les frais de port :
<span class="k">if</span> <span class="p">(</span><span class="nv">$this</span><span class="o">-></span><span class="nf">isDiscountFeatureFlagEnabled</span><span class="p">()</span> <span class="o">&&</span> <span class="nv">$this</span><span class="o">-></span><span class="nb">getType</span><span class="p">()</span> <span class="o">===</span> <span class="nc">DiscountType</span><span class="o">::</span><span class="no">ORDER_LEVEL</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$max_reduction_amount</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-></span><span class="n">reduction_tax</span>
<span class="o">?</span> <span class="nv">$cart_amount_ti</span> <span class="o">+</span> <span class="nv">$context</span><span class="o">-></span><span class="n">cart</span><span class="o">-></span><span class="nf">getOrderTotal</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span> <span class="nc">Cart</span><span class="o">::</span><span class="no">ONLY_SHIPPING</span><span class="p">,</span> <span class="nv">$package_products</span><span class="p">)</span>
<span class="o">:</span> <span class="nv">$cart_amount_te</span> <span class="o">+</span> <span class="nv">$context</span><span class="o">-></span><span class="n">cart</span><span class="o">-></span><span class="nf">getOrderTotal</span><span class="p">(</span><span class="kc">false</span><span class="p">,</span> <span class="nc">Cart</span><span class="o">::</span><span class="no">ONLY_SHIPPING</span><span class="p">,</span> <span class="nv">$package_products</span><span class="p">);</span>
<span class="nv">$reduction_amount</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="nv">$reduction_amount</span><span class="p">,</span> <span class="nv">$max_reduction_amount</span><span class="p">);</span>
<span class="p">}</span>
4.3 Calcul du montant courant du panier
Quand une réduction en montant fixe est appliquée (reduction_amount > 0), le calcul du $current_cart_amount inclut désormais les frais de port pour les remises ORDER_LEVEL :
<span class="k">if</span> <span class="p">(</span><span class="nv">$this</span><span class="o">-></span><span class="nf">isDiscountFeatureFlagEnabled</span><span class="p">()</span> <span class="o">&&</span> <span class="nv">$this</span><span class="o">-></span><span class="nb">getType</span><span class="p">()</span> <span class="o">===</span> <span class="nc">DiscountType</span><span class="o">::</span><span class="no">ORDER_LEVEL</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$current_cart_amount</span> <span class="o">+=</span> <span class="nv">$this</span><span class="o">-></span><span class="n">reduction_tax</span>
<span class="o">?</span> <span class="nv">$context</span><span class="o">-></span><span class="n">cart</span><span class="o">-></span><span class="nf">getOrderTotal</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span> <span class="nc">Cart</span><span class="o">::</span><span class="no">ONLY_SHIPPING</span><span class="p">,</span> <span class="nv">$package_products</span><span class="p">)</span>
<span class="o">:</span> <span class="nv">$context</span><span class="o">-></span><span class="n">cart</span><span class="o">-></span><span class="nf">getOrderTotal</span><span class="p">(</span><span class="kc">false</span><span class="p">,</span> <span class="nc">Cart</span><span class="o">::</span><span class="no">ONLY_SHIPPING</span><span class="p">,</span> <span class="nv">$package_products</span><span class="p">);</span>
<span class="p">}</span>
5. Correction de bug : incohérence de variable (PR #40424)
Le PR #40424 par @pjouglet corrige une incohérence dans l’utilisation des variables à travers le fichier.
Avant : Certaines variables étaient utilisées de manière inconsistante (par ex. mélange entre $cart_rule->name et htmlspecialchars($cart_rule->name)).
Après : Normalisation de l’utilisation des variables dans tout le fichier, notamment dans les messages d’erreur de compatibilité entre vouchers :
<span class="c1">// 9.1.x - avec htmlspecialchars pour la sécurité XSS</span>
<span class="k">return</span> <span class="p">(</span><span class="o">!</span><span class="nv">$display_error</span><span class="p">)</span> <span class="o">?</span> <span class="kc">false</span> <span class="o">:</span> <span class="nv">$this</span><span class="o">-></span><span class="nf">trans</span><span class="p">(</span>
<span class="s1">'This voucher is not combinable with an other voucher already in your cart: %s'</span><span class="p">,</span>
<span class="p">[</span><span class="nb">htmlspecialchars</span><span class="p">(</span><span class="nv">$cart_rule</span><span class="o">-></span><span class="n">name</span><span class="p">)],</span>
<span class="s1">'Shop.Notifications.Error'</span>
<span class="p">);</span>
6. Nouveau système de compatibilité entre remises
9.0.x
Le système de compatibilité repose sur la table ps_cart_rule_combination et le champ cart_rule_restriction. Chaque combinaison interdite est stockée comme un couple dans la table, ce qui provoque une explosion exponentielle des données avec de nombreuses règles.
9.1.x
Le nouveau système introduit une compatibilité par type de remise :
- Les remises ne sont plus ordonnées par code promo vs. automatique
- Ordre d’application fixe :
- Catalog (produit) → Cart (panier) → Free Shipping → Free Gift
- Au sein d’un même type : tri par priorité (plus basse = appliquée en premier), puis par date de création
- Réévaluation dynamique à chaque modification du panier
7. Impact sur CartRuleCalculator.php
Bien que ce fichier soit distinct de CartRule.php, les changements sont intrinsèquement liés. Le CartRuleCalculator (dans src/Core/Cart/) intègre les mêmes vérifications de feature flag :
<span class="k">if</span> <span class="p">(</span><span class="nv">$cartRule</span><span class="o">-></span><span class="nb">getType</span><span class="p">()</span> <span class="o">===</span> <span class="nc">DiscountType</span><span class="o">::</span><span class="no">ORDER_LEVEL</span>
<span class="o">&&</span> <span class="p">(</span><span class="n">float</span><span class="p">)</span> <span class="nv">$cartRule</span><span class="o">-></span><span class="n">reduction_percent</span> <span class="o">></span> <span class="mi">0</span>
<span class="o">&&</span> <span class="nv">$cartRule</span><span class="o">-></span><span class="n">reduction_product</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nv">$this</span><span class="o">-></span><span class="nf">isDiscountFeatureFlagEnabled</span><span class="p">())</span> <span class="p">{</span>
<span class="c1">// Nouveau calcul : produits + frais de port</span>
<span class="nv">$initialShippingFees</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-></span><span class="n">calculator</span><span class="o">-></span><span class="nf">getFees</span><span class="p">()</span><span class="o">-></span><span class="nf">getInitialShippingFees</span><span class="p">();</span>
<span class="nv">$productsTotal</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-></span><span class="n">calculator</span><span class="o">-></span><span class="nf">getRowTotal</span><span class="p">();</span>
<span class="nv">$orderTotal</span> <span class="o">=</span> <span class="nv">$productsTotal</span><span class="o">-></span><span class="nf">add</span><span class="p">(</span><span class="nv">$initialShippingFees</span><span class="p">);</span>
<span class="c1">// ...</span>
<span class="p">}</span>
<span class="p">}</span>
8. Changements de base de données associés
La 9.1.x introduit des modifications de schéma pour supporter le nouveau système de remises (visible dans install-dev/upgrade/sql/9.1.0.sql). Ces changements sont liés au feature flag et n’altèrent pas les tables existantes tant que le flag reste désactivé.
9. Tableau récapitulatif des changements
Aspect 9.0.x 9.1.x Imports Pas de dépendance Discount DiscountType, DiscountSettings, FeatureFlagSettings Feature Flag Absent isDiscountFeatureFlagEnabled() présent Typage des remises Implicite (champs individuels) Explicite via getType() → DiscountType % sur ORDER_LEVEL Produits seuls Produits + frais de port Plafond montant fixe Total produits Total produits + frais de port (ORDER_LEVEL) Sécurité XSS messages $cart_rule->name htmlspecialchars($cart_rule->name) Compatibilité remises Table cart_rule_combination Par type avec ordre d’application fixe Rétro-compatibilité — ✅ Flag désactivé = comportement 9.0.x ---
10. Recommandations pour les développeurs de modules
-
Ne pas surcharger
CartRule.phpen override si vous comptez supporter la 9.1.x — le fichier est en pleine évolution. -
Vérifier le feature flag dans vos modules si vous interagissez avec les remises : ``` $featureFlagManager = $this->get(FeatureFlagStateCheckerInterface::class); if ($featureFlagManager->isEnabled(FeatureFlagSettings::FEATURE_FLAG_DISCOUNT)) { // Logique nouveau système }
-
Tester les deux modes (flag activé et désactivé) car les marchands peuvent basculer entre les deux.
-
Utiliser
getType()au lieu de vérifier manuellementreduction_product,free_shipping,gift_productetc. -
Attention au calcul des frais de port : si votre module calcule des totaux de réduction, le comportement change selon le type de remise en 9.1.x.
🔑#### Points Clés à Retenir — CartRule.php en PrestaShop 9.1.x
Ce que tout développeur de module PrestaShop doit retenir de l’évolution CartRule 9.0.x → 9.1.x :
- Le feature flag protège la rétro-compatibilité. Le nouveau système Discount est désactivé par défaut — la 9.1.x se comporte exactement comme la 9.0.x tant que le flag reste désactivé. Aucune urgence de migration forcée.
- Le calcul ORDER_LEVEL change radicalement. Avec le flag activé, les réductions en pourcentage et en montant fixe s’appliquent désormais sur produits + frais de port, pas sur les produits seuls. Vos tests de calcul de panier doivent couvrir les deux modes.
- Utilisez
getType()au lieu des champs individuels. La nouvelle méthode encapsule proprement le type de remise (Catalog, Cart, Free Shipping, Free Gift) — plus besoin de vérifierreduction_product,free_shipping,gift_productmanuellement. - N’override pas CartRule.php. Ce fichier est en pleine évolution. Préférez les hooks et les services Symfony pour interagir avec le système de remises.
- Testez les deux états du flag dans votre CI : flag désactivé (comportement 9.0.x) et flag activé (nouveau système). C’est le seul moyen de garantir la compatibilité sur les boutiques de vos clients.
Sources et références
- PrestaShop DevDocs — Changes in 9.1.x
- Blog — Improved Discounts System in PrestaShop 9.1
- Blog — PrestaShop 9.1 Beta
- Core Monthly Janvier 2026
- GitHub — CartRule.php (develop/9.1.x)
- GitHub — CartRule.php (9.0.x)
- PR #40424 — Fixed inconsistency use of variable in CartRule.php
Vous pourriez aussi aimer
Articles sélectionnés pour approfondir le sujet
[
Avancé
PrestaShop
Vibe Coding en e-commerce : pourquoi 80% des modules generés par IA ne passeront jamais en production
Le Vibe Coding révolutionne le développement, mais appliqué à PrestaShop, c'est un champ de mines...
15 minutes Lire l'article →
](/articles/2026/02/24/vibe-coding-ecommerce-modules-ia-production/) [
Avancé
Intelligence Artificielle
Gouvernance IA dans PrestaShop : le cadre stratégique indispensable en 2026
En 2026, intégrer l'IA dans PrestaShop ne signifie pas abandonner le contrôle. Découvrez le cadre...
18 minutes Lire l'article →
](/articles/2026/03/17/gouvernance-ia-prestashop-cadre-strategique-2026/) [
Intermédiaire
Intelligence Artificielle
Claude + MCP Tools Plus vs ChatGPT + MCP Tools Plus : Quel assistant IA pour piloter votre boutique PrestaShop en 2026 ?
Claude ou ChatGPT pour gérer votre boutique PrestaShop ? Test comparatif réel avec MCP Tools Plus...
15 minutes Lire l'article →
](/articles/2026/02/19/prestashop-claude-chatgpt-mcp-tools-plus/)