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 :

Checkout Nouvelle étape
Affichage de la nouvelle étape

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 &eacute;tapes du tunnel de commande -->
{extends file='checkout/_partials/steps/checkout-step.tpl'}
 
<!-- Le contenu doit doit &ecirc;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&eacute;mentaire sur votre service</h2>
        <!-- Le formulaire doit envoyer les donn&eacute;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&eacute;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

hh_customcheckout