Dans sa version 1.7 de Prestashop a complètement refondu le fonctionnement du tunnel de commande.
Le fonctionnement est plus propre que dans la version précédente et avec cette nouvelle architecture il devient relativement simple d’ajouter une nouvelle étape.
Nous allons voir comment réaliser cela via la création d’un module.
L’idée étant d’ajouter une étape « Test de nouvelle étape » comme sur la capture ci-dessous :

Cet étape contiendra uniquement 2 informations que nous souhaitons pouvoir réutiliser dans le panier

Fonctionnement technique
Les différentes étapes du tunnel de commandes sont gérées dans la méthode bootstrap du controller OrderController , le code est relativement simple à comprendre.
Un instance de la classe CheckoutProcess est créé.
Puis l’ensemble des steps ( étapes ) sont ensuite ajoutée au checkout via la méthode addStep(CheckoutStepInterface $step) qui attends donc une classe implémentant l’interface CheckoutStepInterface
Voici le code de la fonction du controller natif ( version 1.7.6 de Prestashop )
protected function bootstrap() { $translator = $this->getTranslator(); $session = $this->getCheckoutSession(); /** * Création du process */ $this->checkoutProcess = new CheckoutProcess( $this->context, $session ); /** * Ajout des différentes étapes */ $this->checkoutProcess ->addStep(new CheckoutPersonalInformationStep( $this->context, $translator, $this->makeLoginForm(), $this->makeCustomerForm() )) ->addStep(new CheckoutAddressesStep( $this->context, $translator, $this->makeAddressForm() )); if (!$this->context->cart->isVirtualCart()) { $checkoutDeliveryStep = new CheckoutDeliveryStep( $this->context, $translator ); $checkoutDeliveryStep ->setRecyclablePackAllowed((bool) Configuration::get('PS_RECYCLABLE_PACK')) ->setGiftAllowed((bool) Configuration::get('PS_GIFT_WRAPPING')) ->setIncludeTaxes( !Product::getTaxCalculationMethod((int) $this->context->cart->id_customer) && (int) Configuration::get('PS_TAX') ) ->setDisplayTaxesLabel((Configuration::get('PS_TAX') && !Configuration::get('AEUC_LABEL_TAX_INC_EXC'))) ->setGiftCost( $this->context->cart->getGiftWrappingPrice( $checkoutDeliveryStep->getIncludeTaxes() ) ); $this->checkoutProcess->addStep($checkoutDeliveryStep); } $this->checkoutProcess ->addStep(new CheckoutPaymentStep( $this->context, $translator, new PaymentOptionsFinder(), new ConditionsToApproveFinder( $this->context, $translator ) )); } |
Comme vous pouvez le remarque il n’existe pas de hook, ni de solution non invasive pour rajouter une nouvelle étape personnalisée.
Il sera donc nécessaire de surcharger cette méthode pour pouvoir ajouter notre nouvelle étape.
Ajout de la nouvelle étape
C’est donc parti pour ajouter notre nouvelle étape via le module hh_customcheckout
Voici la structure des fichiers que nous allons créer :

Le contenu du fichier hh_customcheckout.php n’a pas d’importance car il sert uniquement à déclarer le fichier afin que la surcharge du controller OrderController soit prise en compte.
Pour commencer il faut créer la classe de gestion CustomCheckoutStep.php de notre étape de checkout , voici son contenu :
<?php use Symfony\Component\Translation\TranslatorInterface; class CustomCheckoutStep extends AbstractCheckoutStep { /** @var Hh_CustomCheckout */ protected $module; /** @var string $serviceName */ protected $serviceName; /** @var string $responsableName */ protected $responsableName; public function __construct( Context $context, TranslatorInterface $translator, Hh_CustomCheckout $module ) { parent::__construct($context, $translator); $this->module = $module; $this->setTitle('Test de nouvelle étape'); } /** * Récupération des données à persister * * @return array step */ public function getDataToPersist() { return array( 'service_name' => $this->serviceName, 'responsable_name' => $this->responsableName, ); } /** * Restoration des données persistées de la step * * @param array $data * @return $this|AbstractCheckoutStep */ public function restorePersistedData(array $data) { if (array_key_exists('service_name', $data)) { $this->serviceName = $data['service_name']; } if (array_key_exists('responsable_name', $data)) { $this->responsableName = $data['responsable_name']; } return $this; } /** * Traitement de la requête ( ie = Variables Posts du checkout ) * @param array $requestParameters * @return $this */ public function handleRequest(array $requestParameters = array()) { //Si les informations sont postées assignation des valeurs if (isset($requestParameters['submitCustomStep'])) { $this->serviceName = $requestParameters['service_name']; $this->responsableName = $requestParameters['responsable_name']; //Passage à l'étape suivante $this->setComplete(true); //Code 1.7.6 if (version_compare(_PS_VERSION_, '1.7.6') > 0) { $this->setNextStepAsCurrent(); } else { $this->setCurrent(false); } } return $this; } /** * Affichage de la step * * @param array $extraParams * @return string */ public function render(array $extraParams = []) { //Assignation des informations d'affichage $defaultParams = array( //Informations nécessaires 'identifier' => 'test', 'position' => 3, //La position n'est qu'indicative ... 'title' => $this->getTitle(), 'step_is_complete' => (int)$this->isComplete(), 'step_is_reachable' => (int)$this->isReachable(), 'step_is_current' => (int)$this->isCurrent(), //Variables custom 'serviceName' => $this->serviceName, 'responsableName' => $this->responsableName, ); $this->context->smarty->assign($defaultParams); return $this->module->display( _PS_MODULE_DIR_ . $this->module->name, 'views/templates/front/customCheckoutStep.tpl' ); } } |
Nous pouvons ensuite définir le template front de cet étape dans le fichier customCheckoutStep.tpl dont voici le contenu
<!-- Le template doit ếtendre le template parent des étapes du tunnel de commande --> {extends file='checkout/_partials/steps/checkout-step.tpl'} <!-- Le contenu doit doit être dans le block step content --> {block name='step_content'} <div class="custom-checkout-step"> <h2>Afin de confirmer votre commande merci de renseigner ces informations complémentaire sur votre service</h2> <!-- Le formulaire doit envoyer les données sur la page de commande en post --> <form method="POST" action="{$urls.pages.order}" data-refresh-url="{url entity='order' params=['ajax' => 1, 'action' => 'customStep']}" > <!-- Les Champs spécifiques de la step avec assignation de la variable si elle existe --> <section class="form-fields"> <div class="form-group row"> <label class="col-md-3 form-control-label required">Nom du service</label> <div class="col-md-6"> <input type="text" name="service_name" {if isset($serviceName)}value="{$serviceName}"{/if}/> </div> </div> <div class="form-group row"> <label class="col-md-3 form-control-label required">Nom du responsable</label> <div class="col-md-6"> <input type="text" name="responsable_name" {if isset($responsableName)}value="{$responsableName}"{/if}/> </div> </div </section> <footer class="form-footer clearfix"> <input type="submit" name="submitCustomStep" value="Envoyer" class="btn btn-primary continue float-xs-right"/> </footer> </form> </div> {/block} |
Notre nouvelle étape est maintenant définie et son affichage également, il ne reste plus qu’a l’ajouter dans le checkout à la position souhaitée, ceci sera fait via l’override du fichier OrderController.php dont voici le contenu
<?php include_once _PS_MODULE_DIR_ . 'hh_customcheckout/hh_customcheckout.php'; include_once _PS_MODULE_DIR_ . 'hh_customcheckout/classes/CustomCheckoutStep.php'; class OrderController extends OrderControllerCore { protected function bootstrap() { $translator = $this->getTranslator(); $session = $this->getCheckoutSession(); $this->checkoutProcess = new CheckoutProcess( $this->context, $session ); $this->checkoutProcess ->addStep(new CheckoutPersonalInformationStep( $this->context, $translator, $this->makeLoginForm(), $this->makeCustomerForm() )) ->addStep(new CheckoutAddressesStep( $this->context, $translator, $this->makeAddressForm() )); //Les steps sont affichée dans l'ordre d'ajout et il n'y a pas de possibilité de modifier l'ordre //Du coup il faut l'ajouter dans la position souhaitée $customCheckout = Module::getInstanceByName('hh_customcheckout'); $this->checkoutProcess ->addStep(new CustomCheckoutStep( $this->context, $this->getTranslator(), $customCheckout ) ); if (!$this->context->cart->isVirtualCart()) { $checkoutDeliveryStep = new CheckoutDeliveryStep( $this->context, $translator ); $checkoutDeliveryStep ->setRecyclablePackAllowed((bool) Configuration::get('PS_RECYCLABLE_PACK')) ->setGiftAllowed((bool) Configuration::get('PS_GIFT_WRAPPING')) ->setIncludeTaxes( !Product::getTaxCalculationMethod((int) $this->context->cart->id_customer) && (int) Configuration::get('PS_TAX') ) ->setDisplayTaxesLabel((Configuration::get('PS_TAX') && !Configuration::get('AEUC_LABEL_TAX_INC_EXC'))) ->setGiftCost( $this->context->cart->getGiftWrappingPrice( $checkoutDeliveryStep->getIncludeTaxes() ) ); $this->checkoutProcess->addStep($checkoutDeliveryStep); } $this->checkoutProcess ->addStep(new CheckoutPaymentStep( $this->context, $translator, new PaymentOptionsFinder(), new ConditionsToApproveFinder( $this->context, $translator ) )); } } |
Une fois le module installé, notre nouvelle étape sera bien disponible dans le tunnel de commande 😀
Pour ceux qui le souhaitent vous pouvez télécharger directement le fichier du module , pour l’instant je ne l’ai testé sur les versions 1.7.5 et supérieur
février 4th, 2020
Bonjour Hervé,
J’ai installé votre module pour ajouter une étape au processus de commande sur prestashop 1.7.
Aucun souci sur l’installation, l’étape s’affiche correctement.
Seulement les deux champs n’apparaissent ni en back office, ni dans le mail de confirmation de commande. Ai-je raté quelque chose ?
Merci d’avance de votre retour.
Fred,
février 4th, 2020
Bonjour Fred,
Tout à fait ce module est un POC pour ajouter une step dans le tunnel de commande.
Ce n’est pas un module à utiliser directement en production des développements additionnels sont nécessaires.
Pour la récupération des informations vous pouvez les trouver dans la colonne checkout_session_data du panier lié à la commande.
Cordialement,
Hervé
février 4th, 2020
Rebonjour,
Merci pour cette réponse rapide.
C’est bien ce qu’il me semblait.
Je reçois bien les infos dans la bdd, pas de souci.
En revanche, je ne suis pas assez aguerri pour récupérer ces infos et les inclure dans le mail order_conf. Dommage. C’était presque top !
avril 29th, 2020
Bonjour Hervé, j’ai suivi votre tutoriel pour ajouter une étape au checkout. Tout est fonctionnel, le template s’affiche correctement mais les datas ne sont pas enregistré dans la colonne « checkout_session_data » de la table « cart ».
Avez-vous aussi ce problème ?
Version 1.7.6
mai 5th, 2020
Bonjour Aymeric,
Je n’ai pas constaté de problèmes de ce type.
J’ai eut des retours récemment sur l’utilisation de ce tutoriel pour faire un module qui fonctionne très bien sur cette version.
Avez-vous bien suivi l’ensemble des étapes ?
Cordialement,
Hervé
novembre 11th, 2020
Bonjour,
Merci pour ce tuto, j’ai une question annexe, est-t-il possible de changer l’url pour chaque étape ou d’ajouter des ancres ? ceci est pour des raisons GA
novembre 13th, 2020
Bonjour Nizar,
Les étapes sont gérées nativement pas le module google analytics de prestashop il me semble.
Il faudrait voir comment c’est configuré, je ne saurais pas vous dire sur ce point.
Cordialement,
hervé
janvier 7th, 2021
Ca fonctionne super comme toujours, petite question sur le checkout, est-ce qu’il y a moyen de rediriger le client vers une étape spécifique du tunnel après une action ? J’ajoute un produit au panier depuis le tunnel et je souhaite revenir vers une étape précise juste après. Mais cela ne fonctionne pas. Merci 🙂
janvier 7th, 2021
Bonjour,
Difficile de vous répondre avec exactitude car tout dépends :
en quelle position est votre étape ?
et si les informations nécessaires aux étapes qui la précède sont définies ou pas.
janvier 13th, 2021
Bonjour Hervé,
Avec vous un tuto similaire pour la version 1.6.1x ?
Merci d’avance
janvier 13th, 2021
Bonjour,
Non malheureusement.
L’ajout d’étapes sur les version 1.6 est moins pratique car il n’existe pas cette notion de steps.
Il va falloir surcharger le controller OrderCOntroller et rajouter vos étapes dedans ( ou OrderOpcController en fonction du tunnel utilisé )
Cordialement,
Hervé
janvier 16th, 2021
Merci Hervé, je vais regarder à cela.
J’utilise le tunnel avec les blocs un en dessous des autres.