Carritos vacíos al enviar productos al mismo en Prestashop. Posible solución.

Hace unos días, tras una actualización en un Prestashop 1.6 de un cliente, empezaron a ocurrir «cosas raras».

En el Front End, de manera aleatoria (y muy frecuente en ciertos momentos), los clientes estaban enviando productos al carrito, y al llegar al mismo estaba vacío.
En el Back End, algunos plugins como el de TPV, al intentar enviar productos para cobrar, también daban un resultado vacío.

Todo esto sin errores significativos en los logs del servidor, ni en modo debug. Sólo salía este error en los logs de Prestashop (backend) :
Frontcontroller::init – Cart cannot be loaded or an order has already been placed using this cart

Posible solución.

Y digo posible porque en estas cosas las causas pueden ser múltiples, y lo que vale para uno no siempre vale para otro.

Nosotros descubrimos la causa en este post.
Al comprobar las tablas ps_cart y ps_order, viemos que el id del carrito que estaba asignando ps_cart, como posible siguiente carrito, era MUCHO menor que los que estaban en ps_order.
Y los pedidos fallaban cuando coincidían con un ps_cart que ya había tenido un pedido (en ps_order). A veces no fallaba porque no todos los carritos se convierten en pedidos.

La solución entonces es hacer que el último número de ps_cart (a partir del cual crea el siguiente) sea mayor que el último de los carritos de los pedidos. Así no pueden coincidir.

Prestashop Error Invalid address # at line 444 in line classes/Address.php

Este es otro de esos fallos de Prestashop que lleva desde las versiones 1.4 o 1.5 y no se ha arreglado. Incomprensible porque no parece muy difícil de arreglar…aunque no debe ser prioritario.

El error surge cuando vas en el backend a la sección Pedidos->Carritos de la compra y, de repente, da un pantallazo como el que adjunto (el #XXX puede variar).

Solución.

Básicamente lo que está diciendo el problema es que uno de los carritos tiene la dirección que aparece en el número #XXX (en la foto la dirección número #1347) con algún error. Por qué esto hace que deje de funcionar ese apartado es algo incomprensible…..sobre todo porque, como podéis ver aquí y aquí, es algo que lleva tiempo.

En los enlaces anteriores hay varias soluciones. No os molestéis en ejecutar el módulo PS_Cleaner, el limpiador de Prestashop. No hace nada (y si os equivocáis de botón es muy peligroso.

Lo primero que tenéis que hacer es una copia de la base de datos en la sección Avanzado>Base de datos. Porque vais a tener que modificarla.
Después tenéis dos opciones:

  • Lo que nosotros hicimos fue ir a la base de datos con un phpMyAdmin o similar. Después vas a la tabla ps_cart y ordenas los resultados por
    id_address_delivery  o haces un search id_address_delivery = 1347 (sustituye el número por el que te de a tí). Lo curioso es que el número ese #XXX (en nuestro ejemplo 1347), no es el id del carrito, sino el de dirección. Lo siguiente que hicimos fue borrar esa línea. No es importante, es un carrito abandonado y encima está mal. Cuando lo hayas borrado verás que ya puedes entrar en la sección Carritos de la compra del backend.
  • Los artículos sugieren el código MYSQL siguiente. NO LO HEMOS PROBADO. Lo dejo como opción si la primera no funciona. Además, en nuestro caso no era una dirección nula así que esto no lo arreglaría.
SELECT * FROM ps_cart AS c
LEFT JOIN ps_address AS a
ON c.id_address_delivery = a.id_address
WHERE a.id_address IS NULL

Error «los rangos se superponen» al editar transportistas de Prestashop.

Prestashop tiene cosas muy buenas, y cosas que realmente frustran porque se podrían haber arreglado hace años. Esta es una de ellas, que lleva ocurriendo desde hace muchos años (sólo tienes que mirar en Google) sin ponerle solución. Y la solución sería MUY sencilla (una simple comprobación al guardar un transportista).

Si editas un transportista, y le pones un intervalo de precios o de peso que se superponga con otro, te deja guardarlo. Pero la siguiente vez que vayas a editarlo te da un error «los rangos se superponen» y no puedes hacer nada con ese transportista. ¿De verdad cuesta tanto realizar una comprobación automática antes de guardarlo?

Hoy os decimos cómo solucionarlo.

Sólución.

La solución que sugiere todo el mundo en los foros es borrar y volver a recrear el transportista. Esto, evidentemente, elimina el conflicto…pero si has creado muchos intervalos, y le has dedicado tiempo…es una lata.
Si sabes editar la base de datos hay una solución más simple.

Tienes que abrir un phpMyAdmin o similar para editar tu base de datos, e ir a la tabla ps_carrier para identificar el id_carrier del transportista que da error. También está en la url del transportista al ir a editarlo desde el backend.

Con ese dato ve a las tablas ps_range_price o ps_range_weight dependiendo de si tus intervalos son por precios o por peso. Ordénalas por id_carrier y ahí verás todos los intervalos. Uno de ellos se superpone con el siguiente o con el anterior. Edítalo y corrígelo.
El truco es que el final de un intervalo puede coincidir con el principio de otro, pero no debe superponerse. Por ejemplo en esta tabla el final del intervalo de la segunda línea se solapa con el principio del intervalo de la primera línea.
Después prueba a editar el transportista y ya debería funcionar.

Hacer que las páginas de producto empiecen arriba en Prestashop

Hace unos días un cliente os comentó que los usuarios se quejaban porque al pinchar en la paginación de Prestashop, la nueva página se cargaba en la parte inferior y no al comienzo. Eso les obligaba a hacer scroll hasta arriba.

Os dejamos la solución.

Tenéis dos maneras de solucionarlo.

Botón Volver Arriba.

En móviles Prestashop pone un botón de volver arriba. En escritorio lo esconde, pero podéis activarlo por CSS haciendo esto:

.hidden-md-up.text-xs-right.up {display:block!important;}

Esto debía ser suficiente … pero no lo era para los usuarios de nuestro cliente. Así que tuvimos que desarrollar una segunda opción.

Hacer que cada página se cargue automáticamente al principio.

Lo que vamos a incluir es un JQuery para que cuando pulse en la paginación, la siguiente carga comience al principio. Tras probar varios, el código lo encontramos aquí.

Es el siguiente:

 $( '#products' ).on( 'click', '.js-search-link', function( event ) {
    var target = $( "#header" );
    if ( target.length ) {
      event.preventDefault();
      $( 'html, body' ).animate( {
        scrollTop: target.offset().top+300
      }, 1000 );
    }
  });

Se puede poner en varios sitios, pero nosotros lo pusimos al final del fichero  /themes/core.js

Como veis, lo que hace es que cuando pinches en la clase .js-search-link hace un scrollTop hasta el principio.

Funciona muy bien en modo desktop y tablet, en móviles después del la cabecera vienen los menús, así que tienes que bajar un poco más. Lo ideal sería que subiera a un anchor. Si lo modificamos lo colgaremos aquí.

Activar o desactivar módulos para dispositivos móviles en Prestashop

En ocasiones queremos que un módulo no aparezca en dispositivos móviles. O activar uno que por defecto no aparece como el Ps_banner (módulo Banner) de Prestashop. La opción existe…pero está algo escondida. Hoy os enseñamos a hacerlo.

Cómo activar o desactivar módulos para dispositivos móviles en Prestashop.

Para hacerlo tenéis que ir al backend a módulos, y buscar el que queréis activar o desactivar para dispositivos móviles.

A la derecha del botón tenéis una flecha para desplegar opciones y, si pincháis ahí, veréis «Enable mobile» si está desactivado, o «Disable mobile» si ahora está activado para móviles.

Pinchad ahí para activar/desactivar ese módulo para esos dispositivos móviles. Lo podéis ver en este vídeo.

Una gran opción que está algo escondida.

 

Mejorar la calidad de las imágenes que se suben o se importan en Prestashop

Hace unos días hemos hecho una página web para un cliente en Prestashop. Este cliente, tenía la peculiaridad (respecto a clientes anteriores) que disponía de unas imágenes con buena calidad. Al subirlas a Prestashop, tanto en la subida por producto, como en la importación masiva, la pérdida de calidad era más que evidente. Las fotos de Prestashop no tenían una calidad aceptable teniendo en cuenta las fotos de origen. Y en esa página, las imágenes eran clave.

La página la hemos creado en 1.7, pero lo probamos en 1.6 y el resultado era el mismo. Calidad muy por debajo de lo aceptable cuando las imágenes originales son de calidad (para imágenes «usuales» si que es funcional).
Obviamente modificamos todas las opciones de calidad en el backend de Prestashop ( Diseño > Ajustes de imágenes ), sin mejora importante del resultado.

Nos somos a los primeros a los que les pasó (ver aquí y aquí). Es un tema recurrente y sin resolver («sorpresa» por parte de Prestashop).

Al final descubrimos por qué y cómo resolverlo. Os lo contamos en este artículo.

Cómo mejorar la calidad de las imágenes que se suben o se importan en Prestashop.

Lo primero es la causa que ocasiona esta pérdida de calidad. Prestashop comprime todas las imágenes al subirlas. Y crea varias imágenes de diferentes tamaños (que se definen en Diseño > Ajustes de imágenes ). Esto es lógico y bueno. El problema es que usa un método de compresión, GD2, que antes se usaba mucho por estar integrado en Php (o en la mayoría de las instalaciones). Sin embargo no es la librería que mejores resultados da, tiene pocas opciones y, además, creo que Php 7 ya no la incluye por defecto.

Después de varias pruebas infructuosas con GD, optamos por cambiar a Imagemagick siguiendo las indicaciones de un post al que enlazamos en los párrafos anteriores.

Siguiendo los resultados arreglaba algo la subida producto a producto, pero no la masiva. Y aún así, no conseguíamos los mejores resultados así que modificamos el proceso para lo que queríamos. Esto es lo que hicimos..

Para las subidas desde el backend.

Tenemos que:

  • Hacer un override (copiar en la carpeta override/classes) de la clase ImageManager.php porque queremos modificar la función resize para que use ImageMagick.
    Nota:  Para esto tu servidor tiene que tener ImageMagick o deberías poder instalarlo. Si no no funciona.
  • En ese fichero   override/classes/ImageManager.php ponemos lo siguiente:

    <?php

    class ImageManager extends ImageManagerCore
    {

    /**
    * Resize, cut and optimize image
    *
    * @param string $src_file Image object from $_FILE
    * @param string $dst_file Destination filename
    * @param int $dst_width Desired width (optional)
    * @param int $dst_height Desired height (optional)
    * @param string $file_type
    * @param bool $force_type
    * @param int $error
    * @param int $tgt_width
    * @param int $tgt_height
    * @param int $quality
    * @param int $src_width
    * @param int $src_height
    * @return bool Operation result
    */
    public static function resize2($src_file, $dst_file, $dst_width = null, $dst_height = null, $file_type = ‘jpg’,
    $force_type = false, &$error = 0, &$tgt_width = null, &$tgt_height = null, $quality = 5,
    &$src_width = null, &$src_height = null)
    {
    if (PHP_VERSION_ID < 50300) {
    clearstatcache();
    } else {
    clearstatcache(true, $src_file);
    }

    if (!file_exists($src_file) || !filesize($src_file)) {
    return !($error = self::ERROR_FILE_NOT_EXIST);
    }

    list($src_width, $src_height, $type) = getimagesize($src_file);

    // If PS_IMAGE_QUALITY is activated, the generated image will be a PNG with .jpg as a file extension.
    // This allow for higher quality and for transparency. JPG source files will also benefit from a higher quality
    // because JPG reencoding by GD, even with max quality setting, degrades the image.
    if (Configuration::get(‘PS_IMAGE_QUALITY’) == ‘png_all’
    || (Configuration::get(‘PS_IMAGE_QUALITY’) == ‘png’ && $type == IMAGETYPE_PNG) && !$force_type) {
    $file_type = ‘png’;
    }

    if (!$src_width) {
    return !($error = self::ERROR_FILE_WIDTH);
    }
    if (!$dst_width) {
    $dst_width = $src_width;
    }
    if (!$dst_height) {
    $dst_height = $src_height;
    }

    $width_diff = $dst_width / $src_width;
    $height_diff = $dst_height / $src_height;

    $ps_image_generation_method = Configuration::get(‘PS_IMAGE_GENERATION_METHOD’);
    if ($width_diff > 1 && $height_diff > 1) {
    $next_width = $src_width;
    $next_height = $src_height;
    } else {
    if ($ps_image_generation_method == 2 || (!$ps_image_generation_method && $width_diff > $height_diff)) {
    $next_height = $dst_height;
    $next_width = round(($src_width * $next_height) / $src_height);
    $dst_width = (int)(!$ps_image_generation_method ? $dst_width : $next_width);
    } else {
    $next_width = $dst_width;
    $next_height = round($src_height * $dst_width / $src_width);
    $dst_height = (int)(!$ps_image_generation_method ? $dst_height : $next_height);
    }
    }

    if (!ImageManager::checkImageMemoryLimit($src_file)) {
    return !($error = self::ERROR_MEMORY_LIMIT);
    }

    //Set Imagick Object values
    /* $src_image = new Imagick();
    $src_image->readImage($src_file);
    $src_image->setImageCompression(Imagick::COMPRESSION_JPEG);
    $src_image->setInterlaceScheme(Imagick::INTERLACE_PLANE);
    $src_image->setImageCompressionQuality(89);
    $src_image->sharpenimage(2, 0.5, 134217727);
    $src_image->gaussianBlurImage(0.03,0.03);
    $src_image->stripImage();
    $src_image->thumbnailImage($dst_width, $dst_height, Imagick::FILTER_SINC, 1);*/
    $src_image = new Imagick();
    $src_image->readImage($src_file);
    $src_image->setImageCompression(Imagick::COMPRESSION_JPEG);
    /*$src_image->setImageFormat(‘png’);*/
    /*$src_image->setInterlaceScheme(Imagick::INTERLACE_PLANE);*/
    $src_image->setImageCompressionQuality(89);
    /*$src_image->sharpenimage(1.5, 0.5, Imagick::CHANNEL_ALL);*/
    /* $src_image->gaussianBlurImage(0.03,0.03);*/
    $src_image->stripImage();
    $src_image->thumbnailImage($dst_width, $dst_height, Imagick::FILTER_LANCZOS, 1);
    $src_image->brightnessContrastImage(1,0,Imagick::CHANNEL_ALL);
    $src_image->gammaImage(0.9,Imagick::CHANNEL_ALL);
    $src_image->modulateImage(100,120,100);
    //Output the final Image using Imagick
    return $src_image->writeImage($dst_file);

    }

    }

Como veis hemos creado la función resize2 que usará ImageMagick. Dejando el nombre resize arregla el backend. Pero no las masivas. Eso si, si sólo hacemos esto con otro nombre no usa resize2 al subir productos. Tenemos que indicarle que lo haga. Para eso.

  • Hacer un override en override/controllers/admin/AdminProductsController.php  . Ahí hay 5 sitios en el que llama a resize. Cambia los 5 a la función anterior (pon un 2 y ya está si has usado nuestro método.

Para las importaciones masivas desde ficheros CSV o Excel en el backend.

Sólo modificando el ImageManager no arreglaba las importaciones masivas. Estas van «por otro lado». Usaban en resize pero además el GD de nuevo. Para eso tenemos que hacer lo siguiente.

  • Ahora copiamos el fichero controlador AdminImportController.php en override/controllers/admin/AdminImportController.php
  • Lo editamos y veréis que hay dos sitios que usan la biblioteca resize. Para el resultado que queríamos el primero hay que dejarlo usando la original (con resize) y el segundo el resize2. Así:

    ImageManager::resize($tmpfile, $path.’.jpg’, null, null, ‘jpg’, false, $error, $tgt_width, $tgt_height, 5, $src_width, $src_height);
    $images_types = ImageType::getImagesTypes($entity, true);

    if ($regenerate) {
    $previous_path = null;
    $path_infos = array();
    $path_infos[] = array($tgt_width, $tgt_height, $path.’.jpg’);
    foreach ($images_types as $image_type) {
    $tmpfile = self::get_best_path($image_type[‘width’], $image_type[‘height’], $path_infos);

    if (ImageManager::resize2(
    $tmpfile,
    $path.’-‘.stripslashes($image_type[‘name’]).’.jpg’,
    $image_type[‘width’],
    $image_type[‘height’],
    ‘jpg’,
    false,
    $error,
    $tgt_width,
    $tgt_height,
    5,
    $src_width,
    $src_height

Con esto nuestra imágenes han mejorado notablemente. Puedes modificar o añadir los filtros usados (deberías) en ImageMagick. Esta página nos vino muy bien para entenderlos y hacer pruebas. Aquí las librerías de PHP.

También os recomiendo que subáis el tamaño de las imágenes a uno de mucha resolución en las de producto. Luego podéis bajarlo por CSS. Pero la diferencia es notable si usáis fotos de, digamos 1500px o de 900px.

Cómo insertar un vídeo en un producto o módulo de Prestashop

Nos lo ha pedido algún cliente, somos muy vagos y creamos un vídeo para no explicarlo cada vez.

Cómo insertar un vídeo en un producto de Prestashop.

Sirve lo mismo para un módulo de las barras laterales o del contenido. Sólo tienen que tener editores html
El vídeo se puede insertar en el resumen como en la descripción.

Tienes dos métodos.

  1. Insertar la URL. Casi todos los editores tienen un botón para insertar vídeo. Sólo tienes que copiar la URL (dirección del navegador) de tu vídeo y pegarlo en la ventana que salga al pinchar en ese botón. En Prestashop además te deja cambiar el tamaño y la imagen que presenta el vídeo (si no pones una usa la de Youtube o Vimeo).
    El mayor problema es que, una vez hecho, si quieres modificar, tienes que editar código o borrar el vídeo y volver a ponerlo.
    Y que el resultado no suele ser responsive (cuidado con el tamaño que pongas).
  2. Incrustar vídeo (embed). El segundo método es más laborioso pero lo usamos cuando no funciona el anterior (sólo es compatible con ciertos servicios de vídeo) o cuando queremos personalizar más lo que insertamos (por ejemplo hacer Youtube responsive). Para ello vamos a pinchar en el botón de Compartir en Youtube (no todos los vídeos lo tienen, tiene que aprobarlo el creador) y elegir la opción Insertar.
    Ahí veras un código que tienes que copiar  y pegar en Prestashop>Insertar /Editar Video>Incrustar.
    En ese iframe podemos personalizar width y height y poner CSS y otros cambios.

Campos obligatorios en el registro de Prestashop: DNI, teléfono, código postal

Esto debería ser fácil, deberías poder entrar en la configuración de Prestashop y marcarlo. Pero no… Estar está ahí, pero parece que lo han escondido a conciencia. Os decimos cómo seleccionar los campos que queréis que sean obligatorios en el registro de Prestashop.

Campos obligatorios en el registro de Prestashop DNI, teléfono código postal.

Método 1.

Para seleccionar que campos, dentro de los que deja Prestashop, son obligatorios en el registro de Prestashop tienes que ir al backend a Clientes > Direcciones.

Ahí aparece todas las listas de direcciones de clientes registrados y, al final (casi para pasar desapercibido, tienes un campo recogido con un +, llamado Establecer los campos requeridos para esta sección.

Pincha en el más y verás una serie de campos que puedes seleccionar.

Siempre es mejor hacerlo de esta manera porque no retocas ni el sistema ni la plantilla.

Nota: esto debería funcionar en teoría. A nosotros en la 1.7.42 se nos marca la casilla (teléfono) pero al refrescar se desmarca. No hay manera. Usamos el modo 2.

Método 2.

Si esto no te vale, o necesitas algún otro campo puedes hacerlo de esta manera.

Primero tienes que ver si es un campo de cliente o de dirección. Por ejemplo teléfono es de dirección. Los campos de dirección están en Address.php y el de cliente en Customer.php, ambos en el directorio clases. Te ponemos el ejemplo con el de cliente (es lo mismo en Address).

    • Copia el fichero classes/Customer.php en la carpeta override/classes/Customer.php
    • Edita el fichero.
    • Donde pone
      /**
          * @see ObjectModel::$definition
          */
         public static $definition = array(
             'table' => 'customer',
             'primary' => 'id_customer',

Vienen todos los campos. Sólo tienes que poner ‘required’ => true al final del campo que quieras que sea obligatorio. Por ejemplo si quieres la empresa obligatorio cambias:

'company' =>array('type' => self::TYPE_STRING, 'validate' =>'isGenericName'),

por

'company' =>array('type' => self::TYPE_STRING, 'validate' => 'isGenericName','required' => true),

Algún campo puede requerir más modificaciones porque impliquen algún otro campo más (por ejemplo el cumpleaños incluye año, mes y día).

Una vez hecho este cambio, da un error 500 si intentas dejar vacío el campo. En Address.php hay que modificar el fichero AddressFormat.php (copialo antes a override/classes) y meter el valor en el array required. Por ejemplo aquí meto al final el phone:

/** @var array Default required form fields list */
public static $requireFormFieldsList = array(
‘firstname’,
‘lastname’,
‘address1’,
‘city’,
‘Country:name’,
‘phone’,
);

En Customer.php hay que buscar la clase que valida y hacer lo mismo.

Prestashop: backend va lento o no puedes acceder. Solución

Estos días muchos de nuestros clientes con Prestashop han tenido el mismo problema. O lentitud (enorme) en el backend, o directamente no podían entrar en él (rueda girando indefinidamente tras pinchar en acceder). Obviamente todos pensamos en los hosting y hemos perdido mucho tiempo intentando depurar el problema.

Pero la causa era otra. Parece ser que Prestashop usa servidores externos cuando ciertos módulos o addons están activos. Estos módulos estaban colapsados y esto provocaba la lentitud o la imposibilidad de acceso. Os decimos cómo solucionarlo.

Solución a backend lento o a no poder acceder por los problemas de addons externos.

La solución varía dependiendo de la versión de Prestashop.

  1. En Pretashop 1.6: hay que desactivar el módulo gamification. Pero no vale hacerlo desde el backend (y si no puedes acceder peor jejeje). Hay que cambiar el nombre de la carpeta  /modules/gamification 

    Con esto verás como ya puedes acceder a tu backend y va muy rápido.

  2. En Prestashop 1.7:  no hay módulo, hay que modificar unos ficheros. Podéis ver la solución propuesta por Prestashop aquí pero consiste en:

    – Editar el fichero classes/Tools.php y cambiar la línea :
    protected static $is_addons_up = true
    a
    protected static $is_addons_up = false;

    – Editar el fichero src/Adapter/Addons/AddonsDataProvider.php  y cambiar la línea:
    protected static $is_addons_up = true;
    a
    protected static $is_addons_up = false;

Con esto debería estar arreglado.

Nota: hay quien recomienda borrar el fichero config/xml/default_country_modules_list.xml. Si no te funciona prueba con eso, aunque nosotros no lo hemos necesitado.

Prestashop: mostrar las variables y valores que llegan a un tpl en Smarty con debug

Cuando tienes que modificar ficheros tpl en Prestashop, muy a menudo necesitas saber qué variables le llegan desde Smarty (no las de la plantilla). Porque existen unas variables globales y otras que llegan a cada plantilla (diferentes a cada una). Si no pierdes mucho tiempo «probando».

Cómo mostrar las variables y valores que llegan a un tpl en Smarty.

Esto para nosotros ha sido «un descubrimiento».  Sólo tienes que poner {debug} en la plantilla tpl que quieras «evaluar».

Después de poner ese código, recarga la página. El código va a producir un pop-up, así que comprueba que el navegador no te lo está bloqueando.

Cuando lo activas, saldrá un pop up como el de la imagen con las variables buscadas y sus valores.
Con eso sólo tienes que programar lo que necesites.

Esto es muy útil para programar cambios, pero también para hacer «debug», es decir para ver por qué falla alguna cosa.

Las de la plantilla puedes obtenerlas con getTemplateVars.

Por ejemplo con:

$all_tpl_vars = $smarty->getTemplateVars();
var_dump($all_tpl_vars);