lindev.fr

Aller au contenu | Aller au menu | Aller à la recherche

dimanche, décembre 6 2015

Réference commande dans prestashop [1.6.x]

Prestashop et ses bizarreries

presta.png Je travaille quotidiennement sur Prestashop, car à mon grand regret, il n’existe pas aujourd'hui, beaucoup d'alternatives qui proposent un produit :

  • libre
  • fonctionnel et évolutif
  • supporté par une communauté active
  • avec une documentation complète ( pour l'utilisateur et le développeur )
  • développé en python sous Django par exemple .... humm ce dernier point sent le troll ;)

Bref dans ma grande frustration personnel, je dois tout de même admettre que Pretashop offre une solution complète performante et fonctionnelle pour l'utilisateur final, mais dés qu'il s'agit de mettre les mains dans le code, la documentation et plus que maigre !! Et il faut investiguer, poser des questions qui bien souvent dés que ces dernières sont un peu techniques, restent sans réponse sur le forum officiel :( !!
Nous ( développeurs ) sommes doc seuls devant le code et devons faire preuve de patience pour apprivoiser le code sans documentation approfondie. Ce manque d'information est selon moi voulu pour pousser le système économique qui tourne autour de Prestashop ( modules payants pour ajouter des fonctionnalités qui parfois devraient être disponibles par défaut dans la solution ).

D'autant que certains choix fait par Prestashop semblent parfois peu efficaces, voir illogiques.
Prenons le cas de la référence commande, qui est sous la forme d'une chaine de caractères aléatoire et unique .
Pour une boutique qui n'a que quelques commandes / jour cela ne pose aucun problème (quoique), mais pour un flux plus important, cette référence qui sera utilisée par vous et vos clients n'est absolument pas pratique.

Prenons un exemple. Si je vous donne la référence : QRFRUMBMF vous êtes bien avancé, ce n'est pas mnémotechnique et ne vous apprend rien sur la commande.

Alors que ( par exemple ) une référence comme : 20151206-225 qui est la concaténation de :

  • 2015 : L'année de la commande
  • 12 : le mois de la commande
  • 06 : le jour de la commande
  • 225 : id unique de la commande

Aurait était un choix beaucoup plus pertinent pour tout le monde ( client et marchand ).

Posez la questions sur le forum et vous aurez avec un peu de chance, une âme charitable qui va vous guider pour effectuer ce changement. Dans le cas contraire, votre demande tombera dans les abîmes avec les autres messages du genre restés sans réponse .

Aller au boulot, voyons comment faire ce changement dans le code.

La surcharge

Je ne vais pas détailler comment surcharger les différents éléments de Prestashop mais utiliser cette technique pour deux fichiers.

Comme expliqué pus haut, prestashop est bien fini et permet de tout "surcharger" pour vous permettre de modeler/modifier les fonctionnalités de base de l'outil à volonté.

Nous allons donc surcharger dans un premier temps les fichiers suivants :

  1. classes/order/OrderHistory.php
  2. classes/PaymentModule.php

OrderHistory.php

Cette classe gère l'historique des commandes et de ce fait, gère également l’expédition des emails liés à ces états. Mails dans lesquels est rappelé la référence de la commande.

Nous allons donc créer le fichier de surcharge override/classes/order/OrderHistory.php , copier la méthode d'origine addWithemail et enfin le modeler à notre sauce.

ce qui donne :

<?php
class OrderHistory extends OrderHistoryCore{
    
    /**
     * @param bool $autodate Optional
     * @param array $template_vars Optional
     * @param Context $context Optional
     * @return bool
     */

	public function addWithemail($autodate = true, $template_vars = false, Context $context = null){
            if (!$context)
                $context = Context::getContext();

            $order = new Order( (int)$this->id_order );
            $date = new DateTime($order->date_add);

            $data = array(
                '{id_order}' => $date->format('Ymd').'-'.$this->id_order
            );

            if ($template_vars){
                $data = array_merge($data, $template_vars);
            }

            return parent::addWithemail($autodate, $data, $context);
    }
}

Ne vous reste plus qu'à remplacer dans vos mails la balise {order_name} par {id_order}

PaymentModule.php

Cette classe est utilisée à la confirmation de commande et s'occupe également de l'expédition du mail de confirmation de commande ( pourquoi avoir séparé ce mails des autres ? ). Bref, nous allons donc également surcharger cette classe et plus précisément la méthode validateOrder.

Nous allons donc créer le fichier de surcharge override/classes/PaymentModule.php , copier la méthode d'origine validateOrder() et enfin le modeler à notre sauce.

class PaymentModule extends PaymentModuleCore{
    
        /**
     * Validate an order in database
     * Function called from a payment module
     *
     * @param int $id_cart
     * @param int $id_order_state
     * @param float   $amount_paid    Amount really paid by customer (in the default currency)
     * @param string  $payment_method Payment method (eg. 'Credit card')
     * @param null    $message        Message to attach to order
     * @param array   $extra_vars
     * @param null    $currency_special
     * @param bool    $dont_touch_amount
     * @param bool    $secure_key
     * @param Shop    $shop
     *
     * @return bool
     * @throws PrestaShopException
     */
    public function validateOrder($id_cart, $id_order_state, $amount_paid, $payment_method = 'Unknown',
        $message = null, $extra_vars = array(), $currency_special = null, $dont_touch_amount = false,
        $secure_key = false, Shop $shop = null)
    {

        ...
        ...
        ...
        //contenu de la méthode d'origine ( copier - coller de la methode validateOrder dans le fichier classes/PaymentModule.php )
        ...
        ...
        ...
        // Avant la liste des paramétres, o va formater la date de la commande
        $dateOrder = new DateTime($order->date_add);
        ....
        //Vers la ligne 622 dans la liste des paramètres balancés au template du mail, ajouter la ligne 
        ...
        '{id_order}' => $dateOrder->format('Ymd').'-'.$order->id
        ...
        ...
        ...
    }
}

Voilà , ne vous reste plus qu'à mettre à jour le mail orderConf pour remplacer {order_name} par {idorder}

vider le cache

Afin que les surcharges soient bien prises en compte, vous devez supprimer le fichier de cache suivant :

cache/class_index.php

Conclusion

Voilà comment changer une partie de prestashop sans toucher au code d'origine.
Si ce que vous avez fait ne fonctionne pas ou casse quelque chose, il vous suffit alors de supprimer ces fichiers ( dans le répertoire Override ) et remettre les balises dans les mails.
Pas d'inquiétude donc lancez-vous.

samedi, juin 27 2015

Prestashop - Surcharger le code d'un module

Prestashop permet dans sa version actuelle (1.6) de surcharger à peu prés .. tout !
Ce qui est vraiment pratique pour modeler votre site e-commerce comme vous l'entendez sans toucher au cœur du code. Ce qui vous permettra ( dans une certaine mesure ) de pouvoir mettre à jour prestashop sans devoir réécrire vos fonctionnalités spécifiques ou votre thème.

Mais il y a les modules

Une autre des force de cet outil e-commerce, ce son ses modules, très nombreux qui permettent d'ajouter des fonctionnalités de façon .. "plug and play", activer désactiver etc .. super !!

Seulement dans la documentation, impossible de trouver le moyen de surcharger un module existant.( je parle de la surcharge de la fonctionnalité apportée par ce module, les templates associés sont surchargeable de façon classique )
jusqu'à maintenant, si vous souhaitiez changer le fonctionnement d'un module, vous aviez le choix entre :

  1. Toucher directement au code du module ( prochaine mise à jour .. et hop ... vous devez recommencer )
  2. Copier le module pour le dupliquer à votre sauce ( Assez lourd )

La signature à utiliser

Et bien figurez vous que c'est possible .. si si ..

Note : uniquement à partir de la v1.6.0.11 ( merci à ChDUP pour cette précision )
Pour surcharger une classe, vous utilisez l'écriture suivante :

class [className sans Core] extends [className]Core

dans le répertoire override/ ou modules/monModule/override/

Et bien pour surcharger le code d'un module, il vous faut utiliser l'écriture suivante

class [classNameModule]Override extends ClassNameModule

Il suffit donc d'utiliser la chaine "Override" dans le nom de votre classe qui va surcharger le module souhaité .
Par contre, il faut placer votre code dans le répertoire

override/modules/moduleName/

L'exemple qui va bien

Prenons le cas du module BlockPaymentLogo disponible par défaut, qui permet d'afficher les logis de paiement sur une colonne de votre home.
Moi je souhaite les afficher .. dans le pied de page "Footer", ce module n'a pas prévu ce cas, est n'est donc pas enregistré au bon Hook ( displayFooter ) . Bref impossible de le faire en natif .

Surchargeons

Nous allons commencer par créer le répertoire du même nom que celui d'origine

override/modules/blockpaymentlogo/

Puis créer le code de surcharge que voici.

if (!defined('_PS_VERSION_'))
    exit;

class BlockPaymentLogoOverride extends BlockPaymentLogo
{


    public function install(){

        if( parent::install() ){
            return $this->registerHook('displayFooter');
        }else{
            return false;
        }

    }

    public function hookDisplayFooter($params)
    {
        return $this->hookLeftColumn($params);
    }

}

Petites explications,

  • on commence par déclarer notre classe comme il se doit ( en ajoutant Override, et en héritant de la classe du module d'origine )
  • Puis j'ajoute dans le constructeur l'association du module dans le hook displayFooter
  • Et enfin, je définis le code spécifique à ce hook ( ici le code sera le même que sur le hook leftcolumn, donc je ne réécris pas le code j'appelle celui du hook leftcolumn )

Ne reste plus qu'à réinitialiser le module .. et voilà les logos qui apparaissent dans mon footer .

Restez informé

inscrivez vous à la newsletter pour recevoir les nouveaux billets par mail. ( formulaire en haut à droite )

samedi, mai 16 2015

Envoi de mail à la création de compte via le backend de prestashop

logo.png Un petit truc tout bête bien pratique, à tel point que je ne comprends pas pourquoi ce n'est pas en place dans les fonctionnalités de base de prestashop.

Le besoin

Prestashop 1.6

Lorsqu'un employé crée un compte utilisateur depuis le backoffice de prestashop, aucun mail n'est envoyé au client final ( le mail de bienvenue contenant ses identifiants pour se connecter ), de plus l'employé doit entrer lui-même un mot de passe, ce qui n'est pas des plus pratique, surtout lorsque l'employé est en manque d'imagination, on peut se retrouver alors dans une problématique de sécurité non négligeable.

La solution

Nous allons simplement permettre à l'employé de laisser le champs "passwd" vide, il sera alors automatiquement généré de façon aléatoire.
Enfin, nous allons ajouter un bouton "On/Off" dans le formulaire de création de compte qui va permettre à l'employé de décider si oui ou non les identifiants seront envoyés au client.

Le code

Bien commençons par créer la classe de surcharge

override/controllers/admin/AdminCustomersController.php

Nous allons donc surcharger les méthodes suivantes :

  1. processAdd()
  2. renderForm()

Et créer une fonction

  1. sendConfirmationMail()

Voici donc le code complet de notre fichier AdminCustomersController.php

<?php
/*
*  05-2015
*
*  @author Christophe De Saint Leger 
*  @Description Surcharge Formulaire création compte depuis le BackEnd
*/
class AdminCustomersController extends AdminCustomersControllerCore
{


        public function processAdd()
        {
            if (Tools::getValue('submitFormAjax'))
                $this->redirect_after = false;
            // Check that the new email is not already in use
            $customer_email = strval(Tools::getValue('email'));
            $customer = new Customer();
            if (Validate::isEmail($customer_email))
                $customer->getByEmail($customer_email);
            if ($customer->id)
            {
                $this->errors[] = Tools::displayError('An account already exists for this email address:').' '.$customer_email;
                $this->display = 'edit';
                return $customer;
            }
            elseif (trim(Tools::getValue('passwd')) == '')
            {
                $_POST['passwd'] = Tools::passwdGen();
            }
            if ($customer = parent::processAdd())
            {
                $this->context->smarty->assign('new_customer', $customer);
                if( Tools::getValue('sendWelcomeEmail') ){
                    $this->sendConfirmationMail($customer);
                }
                return $customer;
            }
            return false;
        }




        public function renderForm()
        {

            if (!($obj = $this->loadObject(true)))
                return;
            
            $genders = Gender::getGenders();
            $list_genders = array();
            foreach ($genders as $key => $gender)
            {
                $list_genders[$key]['id'] = 'gender_'.$gender->id;
                $list_genders[$key]['value'] = $gender->id;
                $list_genders[$key]['label'] = $gender->name;
            }

            $years = Tools::dateYears();
            $months = Tools::dateMonths();
            $days = Tools::dateDays();

            $groups = Group::getGroups($this->default_form_language, true);
            $this->fields_form = array(
                'legend' => array(
                    'title' => $this->l('Customer'),
                    'icon' => 'icon-user'
                ),
                'input' => array(
                    array(
                        'type' => 'radio',
                        'label' => $this->l('Social title'),
                        'name' => 'id_gender',
                        'required' => false,
                        'class' => 't',
                        'values' => $list_genders
                    ),
                    array(
                        'type' => 'text',
                        'label' => $this->l('First name'),
                        'name' => 'firstname',
                        'required' => true,
                        'col' => '4',
                        'hint' => $this->l('Invalid characters:').' 0-9!&lt;&gt;,;?=+()@#"°{}_$%:'
                    ),
                    array(
                        'type' => 'text',
                        'label' => $this->l('Last name'),
                        'name' => 'lastname',
                        'required' => true,
                        'col' => '4',
                        'hint' => $this->l('Invalid characters:').' 0-9!&lt;&gt;,;?=+()@#"°{}_$%:'
                    ),
                    array(
                        'type' => 'text',
                        'prefix' => '<i class="icon-envelope-o"></i>',
                        'label' => $this->l('Email address'),
                        'name' => 'email',
                        'col' => '4',
                        'required' => true,
                        'autocomplete' => false
                    ),
                    array(
                        'type' => 'password',
                        'label' => $this->l('Password'),
                        'name' => 'passwd',
                        'required' => ($obj->id ? false : true),
                        'col' => '4',
                        'hint' => ($obj->id ? $this->l('Leave this field blank if there\'s no change.') :
                            sprintf($this->l('Password should be at least %s characters long. or void for automatic generation'), Validate::PASSWORD_LENGTH))
                    ), 
                    array(
                        'type' => 'switch',
                        'label' => $this->l('Send Welcome Email'),
                        'name' => 'sendWelcomeEmail',
                        'required' => false,
                        'class' => 't',
                        'is_bool' => true,
                        'values' => array(
                            array(
                                'id' => 'sendWelcomeEmail_on',
                                'value' => 1,
                                'label' => $this->l('Enabled')
                            ),
                            array(
                                'id' => 'sendWelcomeEmail_off',
                                'value' => 0,
                                'label' => $this->l('Disabled')
                            )
                        ),
                        'hint' => $this->l('Send the credentials to the client')
                    ),
                    array(
                        'type' => 'birthday',
                        'label' => $this->l('Birthday'),
                        'name' => 'birthday',
                        'options' => array(
                            'days' => $days,
                            'months' => $months,
                            'years' => $years
                        )
                    ),
                    array(
                        'type' => 'switch',
                        'label' => $this->l('Enabled'),
                        'name' => 'active',
                        'required' => false,
                        'class' => 't',
                        'is_bool' => true,
                        'values' => array(
                            array(
                                'id' => 'active_on',
                                'value' => 1,
                                'label' => $this->l('Enabled')
                            ),
                            array(
                                'id' => 'active_off',
                                'value' => 0,
                                'label' => $this->l('Disabled')
                            )
                        ),
                        'hint' => $this->l('Enable or disable customer login.')
                    ),
                    array(
                        'type' => 'switch',
                        'label' => $this->l('Newsletter'),
                        'name' => 'newsletter',
                        'required' => false,
                        'class' => 't',
                        'is_bool' => true,
                        'values' => array(
                            array(
                                'id' => 'newsletter_on',
                                'value' => 1,
                                'label' => $this->l('Enabled')
                            ),
                            array(
                                'id' => 'newsletter_off',
                                'value' => 0,
                                'label' => $this->l('Disabled')
                            )
                        ),
                        'disabled' =>  (bool)!Configuration::get('PS_CUSTOMER_NWSL'),
                        'hint' => $this->l('This customer will receive your newsletter via email.')
                    ),
                    array(
                        'type' => 'switch',
                        'label' => $this->l('Opt-in'),
                        'name' => 'optin',
                        'required' => false,
                        'class' => 't',
                        'is_bool' => true,
                        'values' => array(
                            array(
                                'id' => 'optin_on',
                                'value' => 1,
                                'label' => $this->l('Enabled')
                            ),
                            array(
                                'id' => 'optin_off',
                                'value' => 0,
                                'label' => $this->l('Disabled')
                            )
                        ),
                        'disabled' =>  (bool)!Configuration::get('PS_CUSTOMER_OPTIN'),
                        'hint' => $this->l('This customer will receive your ads via email.')
                    ),
                )
            );
            
            // if we add a customer via fancybox (ajax), it's a customer and he doesn't need to be added to the visitor and guest groups
            if (Tools::isSubmit('addcustomer') && Tools::isSubmit('submitFormAjax'))
            {
                $visitor_group = Configuration::get('PS_UNIDENTIFIED_GROUP');
                $guest_group = Configuration::get('PS_GUEST_GROUP');
                foreach ($groups as $key => $g)
                    if (in_array($g['id_group'], array($visitor_group, $guest_group)))
                        unset($groups[$key]);
            }

            $this->fields_form['input'] = array_merge(
                $this->fields_form['input'],
                array(
                    array(
                        'type' => 'group',
                        'label' => $this->l('Group access'),
                        'name' => 'groupBox',
                        'values' => $groups,
                        'required' => true,
                        'col' => '6',
                        'hint' => $this->l('Select all the groups that you would like to apply to this customer.')
                    ),
                    array(
                        'type' => 'select',
                        'label' => $this->l('Default customer group'),
                        'name' => 'id_default_group',
                        'options' => array(
                            'query' => $groups,
                            'id' => 'id_group',
                            'name' => 'name'
                        ),
                        'col' => '4',
                        'hint' => array(
                            $this->l('This group will be the user\'s default group.'),
                            $this->l('Only the discount for the selected group will be applied to this customer.')
                        )
                    )
                )
            );

            // if customer is a guest customer, password hasn't to be there
            if ($obj->id && ($obj->is_guest && $obj->id_default_group == Configuration::get('PS_GUEST_GROUP')))
            {
                foreach ($this->fields_form['input'] as $k => $field)
                    if ($field['type'] == 'password')
                        array_splice($this->fields_form['input'], $k, 1);
            }

            if (Configuration::get('PS_B2B_ENABLE'))
            {
                $risks = Risk::getRisks();

                $list_risks = array();
                foreach ($risks as $key => $risk)
                {
                    $list_risks[$key]['id_risk'] = (int)$risk->id;
                    $list_risks[$key]['name'] = $risk->name;
                }

                $this->fields_form['input'][] = array(
                    'type' => 'text',
                    'label' => $this->l('Company'),
                    'name' => 'company'
                );
                $this->fields_form['input'][] = array(
                    'type' => 'text',
                    'label' => $this->l('SIRET'),
                    'name' => 'siret'
                );
                $this->fields_form['input'][] = array(
                    'type' => 'text',
                    'label' => $this->l('APE'),
                    'name' => 'ape'
                );
                $this->fields_form['input'][] = array(
                    'type' => 'text',
                    'label' => $this->l('Website'),
                    'name' => 'website'
                );
                $this->fields_form['input'][] = array(
                    'type' => 'text',
                    'label' => $this->l('Allowed outstanding amount'),
                    'name' => 'outstanding_allow_amount',
                    'hint' => $this->l('Valid characters:').' 0-9',
                    'suffix' => $this->context->currency->sign
                );
                $this->fields_form['input'][] = array(
                    'type' => 'text',
                    'label' => $this->l('Maximum number of payment days'),
                    'name' => 'max_payment_days',
                    'hint' => $this->l('Valid characters:').' 0-9'
                );
                $this->fields_form['input'][] = array(
                    'type' => 'select',
                    'label' => $this->l('Risk rating'),
                    'name' => 'id_risk',
                    'required' => false,
                    'class' => 't',
                    'options' => array(
                        'query' => $list_risks,
                        'id' => 'id_risk',
                        'name' => 'name'
                    ),
                );
            }

            $this->fields_form['submit'] = array(
                'title' => $this->l('Save'),
            );

            $birthday = explode('-', $this->getFieldValue($obj, 'birthday'));

            $this->fields_value = array(
                'years' => $this->getFieldValue($obj, 'birthday') ? $birthday[0] : 0,
                'months' => $this->getFieldValue($obj, 'birthday') ? $birthday[1] : 0,
                'days' => $this->getFieldValue($obj, 'birthday') ? $birthday[2] : 0,
            );

            // Added values of object Group
            if (!Validate::isUnsignedId($obj->id))
                $customer_groups = array();
            else
                $customer_groups = $obj->getGroups();
            $customer_groups_ids = array();
            if (is_array($customer_groups))
                foreach ($customer_groups as $customer_group)
                    $customer_groups_ids[] = $customer_group;

            // if empty $carrier_groups_ids : object creation : we set the default groups
            if (empty($customer_groups_ids))
            {
                $preselected = array(Configuration::get('PS_UNIDENTIFIED_GROUP'), Configuration::get('PS_GUEST_GROUP'), Configuration::get('PS_CUSTOMER_GROUP'));
                $customer_groups_ids = array_merge($customer_groups_ids, $preselected);
            }

            foreach ($groups as $group)
                $this->fields_value['groupBox_'.$group['id_group']] =
                    Tools::getValue('groupBox_'.$group['id_group'], in_array($group['id_group'], $customer_groups_ids));

            return AdminController::renderForm();
        }



        /**
         * sendConfirmationMail
         * @param Customer $customer
         * @return bool
         */
        protected function sendConfirmationMail(Customer $customer)
        {
            if (!Configuration::get('PS_CUSTOMER_CREATION_EMAIL'))
                return true;

            return Mail::Send(
                $this->context->language->id,
                'account',
                Mail::l('Welcome!'),
                array(
                    '{firstname}' => $customer->firstname,
                    '{lastname}' => $customer->lastname,
                    '{email}' => $customer->email,
                    '{passwd}' => Tools::getValue('passwd')),
                $customer->email,
                $customer->firstname.' '.$customer->lastname
            );
        }



}

Attention à ne pas oublier

Pour toute nouvelle surcharge, il vous faut supprimer le fichier de cache suivant

cache/class_index.php

Résultat

Et voilà le résultat au niveau du formulaire

Capture_d_e_cran_2015-05-16_a__14.27.06.png

Bonne journée,

Ch.

Restez informé

inscrivez vous à la newsletter pour recevoir les nouveaux billets par mail. ( formulaire en haut à droite )