Jump to content
Martin Fabre

[Presque résolu 1.7.x.x]Ajout d'un custom field back office

Recommended Posts

Bonjour, 

 

je suis actuellement à la recherche d'un moyen d'ajouter un custom field ( afin de pouvoir entrer les numéros correspondants pour la douane ) dans mon back office sur la fiche d'un produit.

 

J'ai trouvé nombreux tutoriels 1.5/1.6 mais malheureusement ne s'appliquent pas à la très chère 1.7

ex: impossible de trouver informations.tpl dans admin/templates/defaut/.... car le dossier product n'existe pas.

 

 

 

 

Cdt

Edited by Martin Fabre (see edit history)

Share this post


Link to post
Share on other sites

Salut,
après quelques recherches dans le code j'ai un truc qui marche pour toi mais par contre je n'ai pas encore compris le système d'override du 1.7 donc si qqn a des infos...
 
1) on ajoute un champ "custom_field" de type TEXT dans la table ps_product_lang
2) dans la classe Product.php on declare la variable custom_field // chemin = /classes/Product.php

    public $custom_field;

3) dans la classe Product.php on modifie $definition au niveau des lang // chemin = /classes/Product.php

..
  /* Lang fields */
            'meta_description' =>            array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255),
            'meta_keywords' =>                array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255),
            'meta_title' =>                array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 128),
            'link_rewrite' =>    array(
                'type' => self::TYPE_STRING,
                'lang' => true,
                'validate' => 'isLinkRewrite',
                'required' => false,
                'size' => 128,
                'ws_modifier' => array(
                    'http_method' => WebserviceRequest::HTTP_POST,
                    'modifier' => 'modifierWsLinkRewrite'
                )
            ),
            'name' =>                        array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isCatalogName', 'required' => false, 'size' => 128),
            'custom_field' =>                array('type' => self::TYPE_HTML, 'lang' => true, 'validate' => 'isCleanHtml'),
            'description' =>                array('type' => self::TYPE_HTML, 'lang' => true, 'validate' => 'isCleanHtml'),
            'description_short' =>            array('type' => self::TYPE_HTML, 'lang' => true, 'validate' => 'isCleanHtml'),

...

4) on edite le fichier ProductInformation.php dans /src/PrestaShopBundle/Form/Admin/Product
vers la ligne 158 (function buildForm) on ajoute:
 

->add('custom_field', 'PrestaShopBundle\Form\Admin\Type\TranslateType', array(
            'type' => 'Symfony\Component\Form\Extension\Core\Type\TextType',
            'options' => [
                'constraints' => array(
                    new Assert\Regex(array(
                        'pattern' => '/[<>;=#{}]/',
                        'match'   => false,
                    )),
                    new Assert\NotBlank(),
                    new Assert\Length(array('min' => 3, 'max' => 128))
                ), 'attr' => ['placeholder' => $this->translator->trans('Enter your custom field', [], 'Admin.Catalog.Help'), 'class' => 'edit js-edit']
            ],
            'locales' => $this->locales,
            'hideTabs' => true,
            'label' => $this->translator->trans('Custom field', [], 'Admin.Global')
        ))

5) on edite le fichier AdminModelAdapter.php dans /src/PrestaShopBundle/Model/Product
vers la ligne 412 (function getFormData) on ajoute
 

'custom_field' => $this->product->custom_field,

après le nom du produit

'name' => $this->product->name,

6) on edite le fichierform.html.twig dans /src/PrestaShopBundle/Resources/views/Admin/Product
vers la ligne 215 on ajoute
 

<div class="m-b-1 m-t-1">
 <h2>Custom Field</h2>
 [spam-filter] form_widget(form.step1.custom_field) [spam-filter]
</div>

ceci fait apparaitre un nouveau champs dans la fiche produit du BO

 

7) pour injecter la nouvelle variable dans le thème on édite product.tpl // chemin = /themes/classic/templates/catalog

{$product.custom_field nofilter}

 
Voila chez moi ca marche en 1.7.1 maintenant ce serait bien de pouvoir overrider tout cela car si tu fais une mise a jour de Prestashop tout se barre...
 
cdt

Edited by Alexandre Carette (see edit history)
  • Like 1

Share this post


Link to post
Share on other sites

Par contre, il est censé s'afficher ou ? Parce que après avoir suivi ta manip, je ne le trouve toujours pas :(

 

P-e l'erreur vient de la (?) voilà ma requete sql pour créer une nouveau champ, ca créer bien mais rien dans le bo..

ALTER TABLE ps_product_lang
ADD custom_field TEXT
Edited by Martin Fabre (see edit history)

Share this post


Link to post
Share on other sites

Merci pour ces indications fort utiles. Le champ produit additionnel apparait bien dans la page produit du back office et il fonctionne parfaitement, j'aimerai avoir ce champ egalement dans la liste des produits.

J'ai edité /src/PrestaShopBundle/Resources/views/Admin/Product/list.html

et j'obtiens bien le champ dans la liste des produits pour autant il reste vide, alors qu'il est sauvegardé et affiché correctement dans la page du produit. Reste t'il autre chose a editer pour peupler le champ dans la liste, peut etre au niveau de /themes/classic/templates/catalog   ? je n'ai rien trouvé.

Merci.

Share this post


Link to post
Share on other sites

Salut,

 

1) dans la classe Category.php tu as la fonction getProducts();

 

vers la ligne 919

$sql = 'SELECT p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) AS quantity'.(Combination::isFeatureActive() ? ', IFNULL(product_attribute_shop.id_product_attribute, 0) AS id_product_attribute,
					product_attribute_shop.minimal_quantity AS product_attribute_minimal_quantity' : '').', pl.`description`, pl.`description_short`, pl.`available_now`,
					pl.`available_later`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`,pl.`custom_field`, image_shop.`id_image` id_image,
					il.`legend` as legend, m.`name` AS manufacturer_name, cl.`name` AS category_default,
					DATEDIFF(product_shop.`date_add`, DATE_SUB("'.date('Y-m-d').' 00:00:00",
					INTERVAL '.(int) $nbDaysNewProduct.' DAY)) > 0 AS new, product_shop.price AS orderprice
				FROM `'._DB_PREFIX_.'category_product` cp
				LEFT JOIN `'._DB_PREFIX_.'product` p
					ON p.`id_product` = cp.`id_product`
				'.Shop::addSqlAssociation('product', 'p').
                (Combination::isFeatureActive() ? ' LEFT JOIN `'._DB_PREFIX_.'product_attribute_shop` product_attribute_shop
				ON (p.`id_product` = product_attribute_shop.`id_product` AND product_attribute_shop.`default_on` = 1 AND product_attribute_shop.id_shop='.(int) $context->shop->id.')':'').'
				'.Product::sqlStock('p', 0).'
				LEFT JOIN `'._DB_PREFIX_.'category_lang` cl
					ON (product_shop.`id_category_default` = cl.`id_category`
					AND cl.`id_lang` = '.(int) $idLang.Shop::addSqlRestrictionOnLang('cl').')
				LEFT JOIN `'._DB_PREFIX_.'product_lang` pl
					ON (p.`id_product` = pl.`id_product`
					AND pl.`id_lang` = '.(int) $idLang.Shop::addSqlRestrictionOnLang('pl').')
				LEFT JOIN `'._DB_PREFIX_.'image_shop` image_shop
					ON (image_shop.`id_product` = p.`id_product` AND image_shop.cover=1 AND image_shop.id_shop='.(int)$context->shop->id.')
				LEFT JOIN `'._DB_PREFIX_.'image_lang` il
					ON (image_shop.`id_image` = il.`id_image`
					AND il.`id_lang` = '.(int) $idLang.')
				LEFT JOIN `'._DB_PREFIX_.'manufacturer` m
					ON m.`id_manufacturer` = p.`id_manufacturer`
				WHERE product_shop.`id_shop` = '.(int) $context->shop->id.'
					AND cp.`id_category` = '.(int) $this->id
                    .($active ? ' AND product_shop.`active` = 1' : '')
                    .($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '')
                    .($idSupplier ? ' AND p.id_supplier = '.(int)$idSupplier : '');

        if ($random === true) {
            $sql .= ' ORDER BY RAND() LIMIT '.(int) $randomNumberProducts;
        } else {
            $sql .= ' ORDER BY '.(!empty($orderByPrefix) ? $orderByPrefix.'.' : '').'`'.bqSQL($orderyBy).'` '.pSQL($orderWay).'
			LIMIT '.(((int) $p - 1) * (int) $n).','.(int) $n;
        }

après pl.`name` j'ai ajouté pl.`custom_field`

 

2) dans /themes/classic/templates/catalog/_partials/miniatures

 

vers ligne 39

       {block name='product_name'}
          <h1 class="h3 product-title" itemprop="name"><a href="{$product.url}">{$product.name|truncate:30:'...'}</a></h1>
          {$product.custom_field|truncate:30:'...'}
        {/block}

j'ai mis le custom field en dessous du name très grossièrement

 

cdt

Share this post


Link to post
Share on other sites

Wow, merci beaucoup, je n'aurais jamais été chercher dans category.php et _partials/miniature, j'espere que tous ces fichiers src pourront un jour etre overridés facilement.

J'ai un autre probleme de champ custom sur la liste des commandes, j'ai deux champs editables afin d'ajouter le numero de suivi et modifier l'addresse a la volée directement depuis la liste des commandes.  Les modifications ont été faites dans une override de AdmiOrderController.php et dans les fichiers tpl.

Les champs sont bien affichés, je peux leur assigner le contenu d'une variable, j'arrive a recuperer les données pour chaque commande (addresse formatée) mais uniquement avec $this->_select et je n'arrive pas assigner le resultat (addresse formatée ici) a une variable (ensuite injectée par smarty).

J'arrive uniquement a afficher le resultat dans $this->fields_list (ici ca fonctionne dans le champ non editable Address tester), ce dont j'ai besoin c'est d'assigner ce resultat a $formatAddress

Quelle est la bonne maniere de proceder?

	$this->_select .= ', 
		a.id_order as id_trackingField,
		address.`address1` as id_addressField,
		CONCAT(
		address.`firstname`," ", address.`lastname`, "\n", 
		address.`address1`, \' \', 
		address.`address2`, \' \', 
		address.`postcode`," ", address.`city`, \' \', 
		country_lang.`name`,\' \'
		) as sAddressFull
		';
		

	$formatAddress = "test\nformated\nAddress";
		
	$this->context->smarty->assign('formatAddress', $formatAddress);
		
	$this->fields_list['sAddressFull'] = array(
		'title' => $this->l('Address tester'), 
		'value' => $formatAddress
	);
		
        
        $this->fields_list = array_merge($this->fields_list, array(
            'id_trackingField' => array(
                'title' => $this->trans('Tracking', array(), 'Admin.Global'),
                'align' => 'text-left',
                'callback' => 'printTrackingField',
                'orderby' => false,
                'search' => false,
                'remove_onclick' => true
            ),
            'id_addressField' => array(
                'title' => $this->trans('Address', array(), 'Admin.Global'),
                'align' => 'text-left',
                'callback' => 'printAddressField',
                'orderby' => false,
                'search' => false,
                'remove_onclick' => true
            ),          
        ));
Edited by oowwaa (see edit history)

Share this post


Link to post
Share on other sites

J'ai essayé quelques autres champs de la clause select mais meme erreur

        //$formatAddress = $this->address.sAddressFull;
        //$formatAddress = $this->sAddressFull;
        $formatAddress = $this->id_addressField;
        var_dump($formatAddress);
        die();

Share this post


Link to post
Share on other sites

Oui j''en ai peur,  je copie l'override complete :

<?php


class AdminOrdersController extends AdminOrdersControllerCore
{
    public $addressTest;
    public $order;
  
	public function printTrackingField(){
	    
		return $this->createTemplate('_print_tracking_field.tpl')->fetch();
	}
	
	public function printAddressField(){
	    
		return $this->createTemplate('_print_address_field.tpl')->fetch();
	}
	
	public function printManageButtons(){
	    
		return $this->createTemplate('_print_manage_buttons.tpl')->fetch();
	}
	
    
	public function __construct(){
	    
		
		parent::__construct();

		
		$this->addJS('prestashop/prestashop/override/js/DYMO.Label.Framework.2.0.2.js');
        $this->addJS('prestashop/prestashop/override/js/dymo.js');
  
  
  
   
        $this->_select = '
		a.id_currency,
		a.id_order AS id_pdf,
		CONCAT(LEFT(c.`firstname`, 1), \'. \', c.`lastname`) AS `customer`,
		osl.`name` AS `osname`,
		os.`color`,
		IF((SELECT so.id_order FROM `'._DB_PREFIX_.'orders` so WHERE so.id_customer = a.id_customer AND so.id_order < a.id_order LIMIT 1) > 0, 0, 1) as new,
		country_lang.name as cname,
		IF(a.valid, 1, 0) badge_success';



        $this->_join = '
        LEFT JOIN `'._DB_PREFIX_.'order_carrier` oc ON a.`id_order` = oc.`id_order`
		LEFT JOIN `'._DB_PREFIX_.'customer` c ON (c.`id_customer` = a.`id_customer`)
		INNER JOIN `'._DB_PREFIX_.'address` address ON address.id_address = a.id_address_delivery
		INNER JOIN `'._DB_PREFIX_.'country` country ON address.id_country = country.id_country
		INNER JOIN `'._DB_PREFIX_.'country_lang` country_lang ON (country.`id_country` = country_lang.`id_country` AND country_lang.`id_lang` = '.(int)$this->context->language->id.')
		LEFT JOIN `'._DB_PREFIX_.'order_state` os ON (os.`id_order_state` = a.`current_state`)
		LEFT JOIN `'._DB_PREFIX_.'order_state_lang` osl ON (os.`id_order_state` = osl.`id_order_state` AND osl.`id_lang` = '.(int)$this->context->language->id.')';
        $this->_orderBy = 'id_order';
        $this->_orderWay = 'DESC';
        $this->_use_found_rows = true;

        $statuses = OrderState::getOrderStates((int)$this->context->language->id);
        foreach ($statuses as $status) {
            $this->statuses_array[$status['id_order_state']] = $status['name'];
        }



        $this->fields_list = array(
                'id_order' => array(
                'title' => $this->l('ID'),
                'align' => 'text-center',
                'class' => 'fixed-width-xs'
            ),
                'reference' => array(
                'title' => $this->l('Reference')
            ),
                    'tracking_number' => array(
                     'title' => $this->l('tracking number'),
                             'havingFilter' => true,
            ),
            'new' => array(
                'title' => $this->trans('New client', array(), 'Admin.Orderscustomers.Feature'),
                'align' => 'text-center',
                'type' => 'bool',
                'tmpTableFilter' => true,
                'orderby' => false,
            ),
            'customer' => array(
                'title' => $this->trans('Customer', array(), 'Admin.Global'),
                'havingFilter' => true,
            ),
        );
  

  
  
        
		$this->_select .= ', 
		a.id_order as id_trackingField,
		a.id_order as id_manageBtns,
		address.`address1` as id_addressField,
		CONCAT(
		address.`firstname`," ", address.`lastname`, "\n", 
		address.`address1`, \' \', 
		address.`address2`, \' \', 
		address.`postcode`," ", address.`city`, \' \', 
		country_lang.`name`,\' \'
		) as sAddressFull
		';
		
        //$formatAddress = $this->address.sAddressFull;
        //$formatAddress = $this->sAddressFull;
        $formatAddress = $this->id_addressField;
        var_dump($formatAddress);
        die();
		//$formatAddress = "test\nformated\nAddress";
		
		
		$this->fields_list['sAddressFull'] = array(
			'title' => $this->l('Address tester'), 
			'value' => $formatAddress
		);
		

        $this->context->smarty->assign('formatAddress', $formatAddress);		

        
        $this->fields_list = array_merge($this->fields_list, array(
            'id_trackingField' => array(
                'title' => $this->trans('Tracking', array(), 'Admin.Global'),
                'align' => 'text-left',
                'callback' => 'printTrackingField',
                'orderby' => false,
                'search' => false,
                'remove_onclick' => true
            ),
            'id_addressField' => array(
                'title' => $this->trans('Address', array(), 'Admin.Global'),
                'align' => 'text-left',
                'callback' => 'printAddressField',
                'orderby' => false,
                'search' => false,
                'remove_onclick' => true
            ),
            'id_manageBtns' => array(
                'title' => $this->trans('Manage', array(), 'Admin.Global'),
                'align' => 'text-left',
                'callback' => 'printManageButtons',
                'orderby' => false,
                'search' => false,
                'remove_onclick' => true
            )           
        ));
        
	}
}

Share this post


Link to post
Share on other sites

c pas plutot cette function a overrider ?

  public function renderForm()
    {
        if (Context::getContext()->shop->getContext() != Shop::CONTEXT_SHOP && Shop::isFeatureActive()) {
            $this->errors[] = $this->trans('You have to select a shop before creating new orders.', array(), 'Admin.Orderscustomers.Notification');
        }

        $id_cart = (int)Tools::getValue('id_cart');
        $cart = new Cart((int)$id_cart);
        if ($id_cart && !Validate::isLoadedObject($cart)) {
            $this->errors[] = $this->trans('This cart does not exists', array(), 'Admin.Orderscustomers.Notification');
        }
        if ($id_cart && Validate::isLoadedObject($cart) && !$cart->id_customer) {
            $this->errors[] = $this->trans('The cart must have a customer', array(), 'Admin.Orderscustomers.Notification');
        }
        if (count($this->errors)) {
            return false;
        }

        parent::renderForm();
        unset($this->toolbar_btn['save']);
        $this->addJqueryPlugin(array('autocomplete', 'fancybox', 'typewatch', 'highlight'));

        $defaults_order_state = array('cheque' => (int)Configuration::get('PS_OS_CHEQUE'),
                                                'bankwire' => (int)Configuration::get('PS_OS_BANKWIRE'),
                                                'cashondelivery' => Configuration::get('PS_OS_COD_VALIDATION') ? (int)Configuration::get('PS_OS_COD_VALIDATION') : (int)Configuration::get('PS_OS_PREPARATION'),
                                                'other' => (int)Configuration::get('PS_OS_PAYMENT'));
        $payment_modules = array();
        foreach (PaymentModule::getInstalledPaymentModules() as $p_module) {
            $payment_modules[] = Module::getInstanceById((int)$p_module['id_module']);
        }

        $this->context->smarty->assign(array(
            'recyclable_pack' => (int)Configuration::get('PS_RECYCLABLE_PACK'),
            'gift_wrapping' => (int)Configuration::get('PS_GIFT_WRAPPING'),
            'cart' => $cart,
            'currencies' => Currency::getCurrenciesByIdShop(Context::getContext()->shop->id),
            'langs' => Language::getLanguages(true, Context::getContext()->shop->id),
            'payment_modules' => $payment_modules,
            'order_states' => OrderState::getOrderStates((int)Context::getContext()->language->id),
            'defaults_order_state' => $defaults_order_state,
            'show_toolbar' => $this->show_toolbar,
            'toolbar_btn' => $this->toolbar_btn,
            'toolbar_scroll' => $this->toolbar_scroll,
            'PS_CATALOG_MODE' => Configuration::get('PS_CATALOG_MODE'),
            'title' => array($this->trans('Orders', array(), 'Admin.Orderscustomers.Feature'), $this->trans('Create order', array(), 'Admin.Orderscustomers.Feature'))

        ));
        $this->content .= $this->createTemplate('form.tpl')->fetch();
    }

Share this post


Link to post
Share on other sites

Non j'ai fait l'essais, cette function est appelée lors que l'utilisation du bouton "ajouter une commande" (addorder), mais elle n'est jamais appelée lors de la construction de la liste des commandes.

 

Edit: la seule autre fonction appelée au moment du chargement de la liste des comandes dans ce controlleur est renderList()

	public function renderList()
    {
        if (Tools::isSubmit('submitBulkupdateOrderStatus'.$this->table)) {
            if (Tools::getIsset('cancel')) {
                Tools::redirectAdmin(self::$currentIndex.'&token='.$this->token);
            }

            $this->tpl_list_vars['updateOrderStatus_mode'] = true;
            $this->tpl_list_vars['order_statuses'] = $this->statuses_array;
            $this->tpl_list_vars['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
            $this->tpl_list_vars['POST'] = $_POST;
        }
        
        var_dump($this->address.sAddressFull);
        die();
        
        return parent::renderList();
    }

le var_dump done string(12) "sAddressFull",  la variable n'est pas reconnue et interpretée comme string.

Une autre fonction doit etre reponsable du remplissage de tous les champs pour chque ligne, peut etre parent:renderList() 

Edited by oowwaa (see edit history)

Share this post


Link to post
Share on other sites

J'ai enfin trouvé comment transferer les données au champ de texte editable, le callback peut prendre un argument, qui correspond au resultat de la clause select transmis a la liste des champs, il fallait le deviner...

 

le callback

public function printAddressField($formatAddress){
        return '<span class="btn-group-action">
	        <textarea name="addressTextArea" id="addressTextArea" rows="5" cols="40" >'.$formatAddress.' </textarea>
                </span>';
}

l'override de contruct()

public function __construct(){
	    	
	parent::__construct();

	$this->_select .= ', 
	a.id_order as id_trackingField,
	a.id_order as id_manageBtns,
	address.`address1` as id_addressField,
	CONCAT(
	address.`firstname`," ", address.`lastname`, "\n", 
	address.`address1`, \' \', 
	address.`address2`, "\n", 
	address.`postcode`," ", address.`city`, "\n", 
	country_lang.`name`,\' \'
	) as id_addressFull
	';
		

        $this->fields_list = array_merge($this->fields_list, array(
            'id_addressFull' => array(
                'title' => $this->trans('Address', array(), 'Admin.Global'),
                'align' => 'text-left',
                'callback' => 'printAddressField',
                'orderby' => false,
                'search' => false,
                'remove_onclick' => true
            ),        
      ));        
}

Maintenant il ne me reste plus qu'a trouver comment recuperer le contenu de la commande avec une clause select pour un autre champ.

 

N.B Assigner la variable a smarty etait une tentative pour peupler (indirectement) le champ de texte editable

Edited by oowwaa (see edit history)

Share this post


Link to post
Share on other sites

du coup, proprement cela donne

public function printAddressField($formatAddress){
     $this->context->smarty->assign('formatAddress', $formatAddress);  
     return $this->createTemplate('_print_address_field.tpl')->fetch();
}

Share this post


Link to post
Share on other sites

Bonjour ,

J'aimerai ajouter sur la fiche produit au back office une option permettant que si active affiche en front une information

 

Je m'explique :

 

Je vend un produit a l'unite ou en lot de 5 , si lors de la creation de produit j'active l'option LOT  en front la fiche produit aura :  10 euro le lot de 5

et dans le cas contraire : 10 euros l'unite par exemple

 

Cest un peut le meme principe que l'ajout d'un custom field dans le back office mais je n'arrive pas a l'adapter .

 

Merci par avance pour votre aide.

Share this post


Link to post
Share on other sites
On 29/01/2018 at 12:15 PM, amerigeau said:

Hello !

 

Je pense que ça en aidera plus d'un, j'ai rédigé un tuto simple et rapide pour ajouter un champ aux produits dans PrestaShop 1.7 avec un module gratuit/

Par ici : https://www.arnaud-merigeau.fr/prestashop-1-7-ajouter-des-champs-texte-aux-produits/

 

:)


Merci je vois que tu as bien édité ta source ;)
 

Edited by hhennes
Delete comment (see edit history)
  • Like 1

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...

Important Information

Cookies ensure the smooth running of our services. Using these, you accept the use of cookies. Learn More