🛒 CommandesAvancé PS 1.7 PS 8.x

Ajouter un champ date de livraison dans les commandes PrestaShop

Guide complet pour ajouter et modifier une date de livraison par produit dans le détail des commandes PrestaShop via AJAX. Code, SQL et bonnes pratiques.

En bref : Guide complet pour ajouter un champ date de livraison éditable par produit dans les commandes PrestaShop, avec table SQL dédiée, traitement AJAX sécurisé et approche module compatible 1.7 et 8.x.

Publié le 21 mars 2026 6 min de lecture Alexandre Carette

Pourquoi ajouter une date de livraison par produit dans PrestaShop

Par défaut, PrestaShop gère un transporteur et une date d'expédition globale par commande, mais aucune date de livraison individuelle par ligne de produit. C'est un besoin fréquent pour les marchands qui vendent des produits avec des délais de livraison différents (meubles sur-mesure, produits frais, précommandes…).

L'objectif de ce guide est d'ajouter un champ date de livraison éditable directement dans la vue détail d'une commande en back-office, avec mise à jour en temps réel via AJAX — sans rechargement de page.

Architecture de la solution

La mise en place repose sur trois briques :

  1. **Une table SQL dédiée** pour stocker la date de livraison par ligne de commande
  2. **Un traitement AJAX côté contrôleur** pour recevoir et enregistrer la modification
  3. **Un champ date dans le template** de la vue commande avec le JavaScript associé
  4. Bonne pratique PrestaShop 8.x : Encapsulez toute cette logique dans un module plutôt que de modifier le cœur. Utilisez les hooks displayAdminOrderTabContent et actionValidateOrder pour rester compatible avec les mises à jour.

    Étape 1 : Créer la table SQL

    Commencez par créer une table qui lie chaque ligne de commande (id_order_detail) à une date de livraison :

    
    CREATE TABLE IF NOT EXISTS `ps_order_date_liv` (
      `id_order_date_liv` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
      `id_order_detail` INT(11) UNSIGNED NOT NULL,
      `date` DATE DEFAULT NULL,
      `date_add` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
      `date_upd` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      PRIMARY KEY (`id_order_date_liv`),
      KEY `idx_order_detail` (`id_order_detail`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
    

    Placez cette requête dans la méthode install() de votre module pour qu'elle s'exécute automatiquement.

    Étape 2 : Initialiser la date à la validation de commande

    Utilisez le hook actionValidateOrder pour insérer automatiquement une ligne par produit commandé. Cela vous permet de pré-remplir la date à partir d'un champ personnalisé du produit si besoin :

    
    public function hookActionValidateOrder($params)
    {
        $order = $params['order'];
        $orderDetails = $order->getOrderDetailList();
    
        foreach ($orderDetails as $detail) {
            // Optionnel : récupérer une date prévisionnelle depuis le produit
            $product = new Product((int) $detail['product_id']);
            $dateLiv = Validate::isDate($product->available_date) 
                ? $product->available_date 
                : null;
    
            Db::getInstance()->insert('order_date_liv', [
                'id_order_detail' => (int) $detail['id_order_detail'],
                'date' => $dateLiv ? pSQL($dateLiv) : null,
            ]);
        }
    }
    

    Étape 3 : Afficher le champ dans la vue commande

    PrestaShop 1.7

    Sur PrestaShop 1.7, le template de la vue commande se trouve dans admin-dev/themes/default/template/controllers/orders/helpers/view/. Vous pouvez overrider le template pour y ajouter une colonne dans le tableau des produits :

    
    <td class="dateLiv">
        <input type="date" 
               class="form-control input-date-liv" 
               value="{$detail.date_liv|default:''}" 
               data-id-order-detail="{$detail.id_order_detail}" />
    </td>
    

    PrestaShop 8.x (Symfony)

    Sur PrestaShop 8.x, la page commande est entièrement portée sous Symfony. Privilégiez le hook displayAdminOrderTabContent pour injecter votre interface sans toucher au cœur :

    
    public function hookDisplayAdminOrderTabContent($params)
    {
        $orderId = $params['id_order'];
        $order = new Order((int) $orderId);
        $orderDetails = $order->getOrderDetailList();
    
        $datesLiv = [];
        foreach ($orderDetails as $detail) {
            $row = Db::getInstance()->getRow(
                'SELECT `date` FROM `' . _DB_PREFIX_ . 'order_date_liv` 
                 WHERE `id_order_detail` = ' . (int) $detail['id_order_detail']
            );
            $datesLiv[$detail['id_order_detail']] = $row ? $row['date'] : '';
        }
    
        $this->context->smarty->assign([
            'orderDetails' => $orderDetails,
            'datesLiv' => $datesLiv,
        ]);
    
        return $this->display(__FILE__, 'views/templates/admin/date_livraison.tpl');
    }
    

    Étape 4 : Le JavaScript AJAX

    Le JavaScript écoute les changements sur les champs date et envoie la nouvelle valeur au contrôleur en AJAX :

    
    $(document).on('change', '.input-date-liv', function () {
        var $input = $(this);
        var idOrderDetail = $input.data('id-order-detail');
        var dateLiv = $input.val();
        var token = window.prestashop && window.prestashop.modules 
            ? window.prestashop.modules.currentToken 
            : token; // PS 1.7 fallback
    
        if (!idOrderDetail || !dateLiv) {
            return;
        }
    
        $.ajax({
            url: currentIndex,
            type: 'POST',
            dataType: 'json',
            data: {
                ajax: 1,
                action: 'updateDateLivProd',
                token: token,
                id_order_detail: idOrderDetail,
                date_liv: dateLiv
            },
            success: function (response) {
                if (response.success) {
                    showSuccessMessage('Date de livraison mise à jour');
                } else {
                    showErrorMessage('Erreur lors de la mise à jour');
                }
            },
            error: function () {
                showErrorMessage('Erreur de communication avec le serveur');
            }
        });
    });
    

    Point d'attention : Assurez-vous que votre input se trouve bien dans un élément lui-même dans un . Si la structure HTML ne correspond pas au sélecteur jQuery, vous obtiendrez undefined en retour — c'est l'erreur la plus fréquente dans ce type d'implémentation.

    Étape 5 : Le traitement côté contrôleur

    Version sécurisée (à utiliser en production)

    
    public function ajaxProcessUpdateDateLivProd()
    {
        $idOrderDetail = (int) Tools::getValue('id_order_detail');
        $dateLiv = Tools::getValue('date_liv');
    
        // Validation des entrées
        if (!$idOrderDetail || !Validate::isDate($dateLiv)) {
            die(json_encode([
                'success' => false,
                'message' => 'Paramètres invalides',
            ]));
        }
    
        // Vérifier que la ligne de commande existe
        $orderDetail = new OrderDetail($idOrderDetail);
        if (!Validate::isLoadedObject($orderDetail)) {
            die(json_encode([
                'success' => false,
                'message' => 'Ligne de commande introuvable',
            ]));
        }
    
        $result = Db::getInstance()->update(
            'order_date_liv',
            ['date' => pSQL($dateLiv)],
            'id_order_detail = ' . $idOrderDetail
        );
    
        die(json_encode([
            'success' => (bool) $result,
            'message' => $result ? 'Date mise à jour' : 'Échec de la mise à jour',
        ]));
    }
    

    Ce qui ne va pas dans une approche naïve

    Une requête SQL construite par concaténation directe expose votre boutique à des injections SQL :

    
    // ❌ DANGEREUX — Ne jamais faire ça
    $sql = 'UPDATE `' . _DB_PREFIX_ . 'order_date_liv` SET Date = ' . $date_liv . ' WHERE id_order_detail = ' . $id_order_detail;
    

    Utilisez toujours les méthodes Db::getInstance()->update() ou au minimum pSQL() et le cast (int) pour protéger vos requêtes.

    Étape 6 : Approche module complet (recommandée)

    Voici la structure recommandée pour un module propre :

    
    my_deliverydate/
    ├── my_deliverydate.php          # Classe principale + hooks
    ├── sql/
    │   ├── install.sql              # CREATE TABLE
    │   └── uninstall.sql            # DROP TABLE
    ├── views/
    │   ├── templates/
    │   │   └── admin/
    │   │       └── date_livraison.tpl
    │   └── js/
    │       └── admin-order.js       # AJAX
    └── controllers/
        └── admin/
            └── AdminDeliveryDateController.php  # PS 8.x
    

    En PrestaShop 8.x, si vous souhaitez ajouter un contrôleur AJAX propre plutôt que surcharger AdminOrdersController, déclarez-le comme service Symfony dans votre module :

    
    # my_deliverydate/config/routes.yml
    my_deliverydate_update_date:
        path: /my-deliverydate/update
        methods: [POST]
        defaults:
            _controller: 'MyModule\Controller\Admin\DeliveryDateController::updateAction'
    

    Déboguer l'AJAX : méthodologie pas à pas

    Si votre appel AJAX ne fonctionne pas, procédez par élimination :

    1. **Testez la valeur JavaScript** — Remplacez temporairement la valeur dynamique par une chaîne fixe (`var DateLiv = "ok"`) et vérifiez que le contrôleur la reçoit
    2. **Vérifiez la console réseau** — Ouvrez l'onglet Network (XHR) de votre navigateur et inspectez la requête AJAX : URL, paramètres, réponse
    3. **Vérifiez le cache** — PrestaShop met en cache les fichiers JS. Faites `Ctrl+F5` ou videz le cache dans Paramètres avancés > Performances
    4. **Ajoutez un var_dump temporaire** — Dans votre méthode PHP, faites `var_dump(Tools::getValue('date_liv')); die();` pour confirmer la réception du paramètre
    5. **Vérifiez le sélecteur jQuery** — La cause n°1 d'un `undefined` est un sélecteur CSS qui ne correspond pas à la structure HTML réelle
    6. Récapitulatif des points critiques

      PointDétail Sécurité SQLToujours utiliser `pSQL()` et `(int)` — jamais de concaténation directe ValidationVérifier `Validate::isDate()` avant toute insertion Cache JSVider le cache navigateur ET le cache PrestaShop après modification Structure HTMLLes sélecteurs jQuery doivent correspondre exactement aux classes CSS Réponse JSONToujours retourner du JSON structuré avec un champ `success` Module vs overridePrivilégier un module avec hooks pour la maintenabilité
#commandes #ajax #back-office #order-detail #personnalisation #adminorderscontroller

Questions fréquentes

Tout ce que vous devez savoir sur ce sujet.

Un projet PrestaShop ?

Discutons-en directement.

★★★★★

193 projets livrés

Gratuit & sans engagement — réponse sous 24h

Alexandre Carette

Alexandre Carette

Expert PrestaShop & Architecture E-commerce

Développeur PrestaShop depuis 2014, 193 projets livrés. Je conçois des architectures headless Nuxt + PrestaShop et des outils d'automatisation IA pour les e-commerçants.