En un eCommerce es un valor añadido las imágenes que acompañan al producto, al carecer de trato personal con el cliente en muchas tiendas, y según el tipo de producto a vender, la calidad de la imagen es primordial. A través del foro de Magento podemos encontrar este hilo de conversación del cual podemos extraer como modificando un par de líneas en el adaptador que se ocupa del tratamiento de la librería GD localizado en lib/Varien/Image/Adapter/Gd2.php. es posible cambiar el porcentaje de compresión. Para ello procedemos a sustituir en el fichero en cuestión la línea:

<?php

call_user_func($this->_getCallback(‘output’), $this->_imageHandler, $fileName);

?>

por el siguiente código:

<?php

if (IMAGETYPE_JPEG === $this->_fileType)
   call_user_func($this->_getCallback(‘output’), $this->_imageHandler, $fileName,90);
else
   call_user_func($this->_getCallback(‘output’), $this->_imageHandler, $fileName);

?>

De esta forma aumentaremos la calidad al definir un porcentaje de compresión del 90%, ni que decir tiene que podemos definir el porcentaje que más se ajuste a nuestras necesidades.

Este cambio solo será aplicable a ficheros de tipo JPEG.

Vía | Magento Forum

Novalis, a través de su blog Exanto nos muestra un sencillo script SQL para la inserción masiva del IVA para países de Europa. Es conveniente revisar los valores para que se ajusten a nuestras necesidades ya en Alemania el IVA es un 19%.

Echando un vistazo a las entradas del foro en Magento.es, leí este tema. Me pareció interesante la idea.

Hace unos años, en una empresa donde trabajaba, por el 2002 con las primeras versiones de .Net, desarrollamos una aplicación B2B. El objetivo era conseguir una comunicación más agil y directa con sus distribuidores y proveedores. Gestionar pedidos, facturación y envíos. Hasta aquí Magento, cumpliría el cometido. Pero un acceso totalmente restringido, ya que conocemos de antemano a nuestro cliente o distribuidor. Y creando un microclima para cada uno, en cuanto a condiciones de venta, idiomas, etc….

Navegando por Magento Connect podemos ver un par de extensiones que podrian conseguir inicialmente este efecto.  La primera sería Customer Activation, esta extensión obliga a una activación por parte del administrador una vez que el usuario se ha dado de alta, y no puede realizar ningún actividad hasta ese momento. La segunda extensión, del mismo autor, es Login only catalog la extensión limita el acceso de la tienda a usuarios registrados.

Por supuesto quedarían muchos detalles a tener en cuenta para conseguir un B2B completo, pero sería un buen comienzo. Refinando la configuración de Magento a nivel de (WebSite/WebSite Store/Store View) o de grupo de clientes.

Explotar la flexibilidad de Magento y la buena estructuración de su base, nos puede permitir que esta aplicación cubra necesidades para las que inicialmente no ha sido diseñada.

Uno de los trabajos más laboriosos en nuestra tienda, pero interesante a nivel de maketing,  será relacionar productos entre si, y explotar las opciones de venta sugerida que implementa Magento de serie (Productos relacionados, Ventas sugeridas y Ventas cruzadas). Supongo que todos conocemos el funcionamiento y diferencias de cada uno. Pues bien si nuestra tienda consta de una cantidad pequeña de productos es fácil de manejar y relacionar cada uno de ellos con el resto según nos interese, pero si manejamos cientos o miles de productos la cosa cambia y el proceso se hará algo pesado. Ir a la ficha de cada producto  y discernir del resto que es complemento, accesorio o misma colección (entiendo que esto sería producto relacionado), de la misma familia o uso (Ventas sugeridas), y mismo sector, por ejemplo edad (Venta cruzada cuando estamos en el checkout). Esto es una idea no quiero decir que sea la mejor opción para relacionar. No tenemos ningun tipo de limitación o prohibición para ello, solo no relacionar a un producto consigo mismo. Después de todo este rollo que a mas de uno le sobrará, continuo con la idea original de este post que era comentar una nueva extensión para Magento que nos facilitará este trabajo, Mass Product Relater. Dicha extensión añade nuevas funcionalidades en el desplegable de acciones en la página de gestión de productos, aunque debemos “ayudar” a la instalación para que el proceso concluya con éxito. Tal como explica el autor aquí, tras la instalación dispondremos de unos ficheros que sustituirán, por completo o añadiendo el código nosotros dependiendo del metodo elegido para ello. El seguro añadir a los ficheros ProductController.php y Grid.php, el código sugerido o el rápido sobreescribir los ficheros completamente. He elegido la primera opción (segura), llamadme miedoso, por si acaso. Según el autor esta testeado con la versión 1.0.x, lo he probado 1.1.6 y aparentemente funciona, aunque en Magento Connect, esta extensión está marcada como estable, su autor sigue mejorandola, ya que la ultima funcionalidad Relate To… parece no estár operativa. Recordad, mejor probar en un entorno de test y no en el servidor de producción, por lo que pueda pasar.

Via | Magento Connect

Algunas veces las cosas más pequeñas y sencillas como un espacio nos pueden fastidiar unas horas de trabajo. Sé que es culpa mía por no probar hasta la saciedad, pero juro que en la versión 1.1.4 funcionaba. Pero la cara que se le queda a uno cuando muestra la útil opción de exportar a CSV/EXCEL de los Informes (Reports) de Magento, a un tercero y aparece este mensaje, no tiene precio:

Cannot send headers; headers already sent in /xxx/app/code/core/Mage/Reports/Model/Totals.php, line 2
[...]

Pues bien, tal y como se comenta en esta entrada del foro oficial de Magento, simplemente debemos dirigirnos al fichero totals.php ubicado en la carpeta: app/code/core/Mage/Reports/Model y eliminaremos el espacio de la primera linea justo antes de <?php y listo, vuelve a funcionar.

Via | MagentoCommerce

Durante el periodo de prueba o test, es lógico que “ensuciemos” nuestra base de datos con pedidos y facturas de prueba, para verificar que el funcionamiento de toda la aplicación es el deseado.

Pero una vez hayamos instalado la tienda en el servidor de producción es recomendable dejar nuestra base datos limpia de polvo y paja. La sección de administración de Magento no permite limpiar completamente la base de datos discriminando un poco, de modo que podamos conservar productos y configuración pero eliminar pedidos, facturas, envíos y clientes que hemos inventado en dicho proceso de verificación.

Sería interesante disponer de ello en futuras versiones, aunque creo que es algo tildado de un perfil más técnico y no tanto orientado al usuario final que debe gestionar la tienda. Nuevamente en el blog Exanto perteneciente a un miembro muy activo  de la comunidad Magento,  novalis,  podemos leer un interesante método que nos facilitará todo este proceso.

Ha recopilado un script SQL, originariamente publicado en esta entrada en el foro oficial.

El código es el siguiente:

SET FOREIGN_KEY_CHECKS=0;

TRUNCATE `sales_order`;
TRUNCATE `sales_order_datetime`;
TRUNCATE `sales_order_decimal`;
TRUNCATE `sales_order_entity`;
TRUNCATE `sales_order_entity_datetime`;
TRUNCATE `sales_order_entity_decimal`;
TRUNCATE `sales_order_entity_int`;
TRUNCATE `sales_order_entity_text`;
TRUNCATE `sales_order_entity_varchar`;
TRUNCATE `sales_order_int`;
TRUNCATE `sales_order_text`;
TRUNCATE `sales_order_varchar`;
TRUNCATE `sales_flat_quote`;
TRUNCATE `sales_flat_quote_address`;
TRUNCATE `sales_flat_quote_address_item`;
TRUNCATE `sales_flat_quote_item`;
TRUNCATE `sales_flat_quote_item_option`;
TRUNCATE `sales_flat_order_item`;
TRUNCATE `sendfriend_log`;
TRUNCATE `tag`;
TRUNCATE `tag_relation`;
TRUNCATE `tag_summary`;
TRUNCATE `wishlist`;
TRUNCATE `log_quote`;
TRUNCATE `report_event`;

ALTER TABLE `sales_order` AUTO_INCREMENT=1;
ALTER TABLE `sales_order_datetime` AUTO_INCREMENT=1;
ALTER TABLE `sales_order_decimal` AUTO_INCREMENT=1;
ALTER TABLE `sales_order_entity` AUTO_INCREMENT=1;
ALTER TABLE `sales_order_entity_datetime` AUTO_INCREMENT=1;
ALTER TABLE `sales_order_entity_decimal` AUTO_INCREMENT=1;
ALTER TABLE `sales_order_entity_int` AUTO_INCREMENT=1;
ALTER TABLE `sales_order_entity_text` AUTO_INCREMENT=1;
ALTER TABLE `sales_order_entity_varchar` AUTO_INCREMENT=1;
ALTER TABLE `sales_order_int` AUTO_INCREMENT=1;
ALTER TABLE `sales_order_text` AUTO_INCREMENT=1;
ALTER TABLE `sales_order_varchar` AUTO_INCREMENT=1;
ALTER TABLE `sales_flat_quote` AUTO_INCREMENT=1;
ALTER TABLE `sales_flat_quote_address` AUTO_INCREMENT=1;
ALTER TABLE `sales_flat_quote_address_item` AUTO_INCREMENT=1;
ALTER TABLE `sales_flat_quote_item` AUTO_INCREMENT=1;
ALTER TABLE `sales_flat_quote_item_option` AUTO_INCREMENT=1;
ALTER TABLE `sales_flat_order_item` AUTO_INCREMENT=1;
ALTER TABLE `sendfriend_log` AUTO_INCREMENT=1;
ALTER TABLE `tag` AUTO_INCREMENT=1;
ALTER TABLE `tag_relation` AUTO_INCREMENT=1;
ALTER TABLE `tag_summary` AUTO_INCREMENT=1;
ALTER TABLE `wishlist` AUTO_INCREMENT=1;
ALTER TABLE `log_quote` AUTO_INCREMENT=1;
ALTER TABLE `report_event` AUTO_INCREMENT=1;

– Reset all ID counters
TRUNCATE `eav_entity_store`;
ALTER TABLE  `eav_entity_store` AUTO_INCREMENT=1;

SET FOREIGN_KEY_CHECKS=1;

Esta primera parte elimina el contenido de las tablas relacionadas con pedido, facturas, lista de favoritos(wishlist) y, esto es importante, Tags (etiquetas),  perderemos las etiquetas asociadas a los productos, lo cual es lógico ya que está asociado al cliente. El script utiliza la sentencia TRUNCATE, su objetivo es el mismo que DELETE, pero destruye y vuelve a crear la tabla, en caso de tablas que contengan muchos registros es más rápido.

Segunda parte, inicializar clientes:

SET FOREIGN_KEY_CHECKS=0;

– reset customers
TRUNCATE `customer_address_entity`;
TRUNCATE `customer_address_entity_datetime`;
TRUNCATE `customer_address_entity_decimal`;
TRUNCATE `customer_address_entity_int`;
TRUNCATE `customer_address_entity_text`;
TRUNCATE `customer_address_entity_varchar`;
TRUNCATE `customer_entity`;
TRUNCATE `customer_entity_datetime`;
TRUNCATE `customer_entity_decimal`;
TRUNCATE `customer_entity_int`;
TRUNCATE `customer_entity_text`;
TRUNCATE `customer_entity_varchar`;
TRUNCATE `log_customer`;
TRUNCATE `log_visitor`;
TRUNCATE `log_visitor_info`;

ALTER TABLE `customer_address_entity` AUTO_INCREMENT=1;
ALTER TABLE `customer_address_entity_datetime` AUTO_INCREMENT=1;
ALTER TABLE `customer_address_entity_decimal` AUTO_INCREMENT=1;
ALTER TABLE `customer_address_entity_int` AUTO_INCREMENT=1;
ALTER TABLE `customer_address_entity_text` AUTO_INCREMENT=1;
ALTER TABLE `customer_address_entity_varchar` AUTO_INCREMENT=1;
ALTER TABLE `customer_entity` AUTO_INCREMENT=1;
ALTER TABLE `customer_entity_datetime` AUTO_INCREMENT=1;
ALTER TABLE `customer_entity_decimal` AUTO_INCREMENT=1;
ALTER TABLE `customer_entity_int` AUTO_INCREMENT=1;
ALTER TABLE `customer_entity_text` AUTO_INCREMENT=1;
ALTER TABLE `customer_entity_varchar` AUTO_INCREMENT=1;
ALTER TABLE `log_customer` AUTO_INCREMENT=1;
ALTER TABLE `log_visitor` AUTO_INCREMENT=1;
ALTER TABLE `log_visitor_info` AUTO_INCREMENT=1;

SET FOREIGN_KEY_CHECKS=1;

Cómo se comenta en el post original. Este proceso inicializará los contadores al estado que tienen inmediatamente después de una instalación limpia, 100000001. Para evitar esto y cambiar el formato de numeración, si nos interesa, realizaremos la modificación que deseemos en la tabla eav_entity_store. Esta es la estructura de dicha tabla:

Estructura eav_entity_store

Estructura eav_entity_store

Cómo vemos la columna increment_prefix dicta si queremos definir prefijo y que incremento. Entity_type_id es el tipo de entidad relacionada: 11 - Order(Pedido), 1 - Customer(Cliente), 16 - Invoice(Factura), 19 - Shipment(Envío). Si se requiere el incremento a partir de un número o código en concreto; modificaremos el valor del campo increment_last_id mediante una instrucción SQL como la del siguiente ejemplo:

UPDATE eav_entity_store SET increment_last_id = ‘100000069′ WHERE entity_store_id = 1;

El tema del formato de codificación es más profundo e interesante ya que Magento está preparado para varios formatos, no solo codificación numérica si no también alfanumérica, muy practico para facturas. Sobre esto último se hace alusión  en esta entrada del foro oficial.

Resumiendo un poco. Podemos localizar en la siguiente carpeta de nuestra instalación de Magento, app/code/core/Mage/Eav/Model/Entity/Increment/, el código PHP (Alphanum.php, Numeric.php) que se ocupan de ello. Modificariamos, en dichos ficheros el patrón de cálculo.

Cómo podéis comprobar los cimientos de Magento son firmes, y bien de una forma u otra nos permite configurar nuestra tienda casi en su totalidad.

Via | Exanto

Magento, FastCGI y el error 500 Septiembre 1st, 2008

En un par de lineas del post anterior comentaba que intento que el entorno de pruebas previo a la instalación en el cliente fuera lo más parecido posible, si no igual, a la configuración que este va a disponer en producción. Pues bien esta semana he vivido uno de esos momentos, en el cual he echado de menos no poder cumplir a raja tabla este paso. Imaginamos por un momento el escenario, una tienda con Magento 1.0 en un hosting compartido. La configuración del servidor cumple todos los requisitos de la aplicación, pero en dos procesos claves del manejo de la tienda: el paso de un producto al carrito e inicio del proceso de compra (checkout), puede llegar a tardar (según la carga del servidor en ese momento, recuerdo que es compartido) hasta 6 segundos, además de estar un poco cortos de espacio. Resultado, el cliente no es feliz. El proveedor nos oferta Apache + FastCGI, eso sí en un hosting “menos saturado”, esto penaliza algo en velocidad respecto a mod_php (PHP embebido en Apache) pero si hay un cuelgue FastCGI no necesita reinicio de Apache es posible monitorizar el consumo de recursos, supongo que eso facilita el evitar abusos y controlar consumos. Tras evaluar la migración a un hosting con la posibilidad de disponer más tiempo fijo de procesador asignado, un VPS o en última opción servidor dedicado con el aumento correspondiente de la factura. El cliente sigue sin dar señales de estar contento. Último cartucho migrar a la versión 1.1.x, eso sí sobre FastCGI. Es necesario algunos cambios en la plantilla además es interesante para la futura  integración de la tienda con su ERP gracias a Magento Core API, pero eso es otra história ;) . Finalmente se decide realizar la migración, en fín no perdemos nada. El proceso se completa satisfactoriamente. Se prueba su funcionamiento, se testea el acceso al API todo correcto. El cliente comenta de mostrar más infomación en la página personalizada que captura el error 404, banners promocionales del producto, etc…. Buena idea, nos facilita las imagenes adecuadas y se modifica el html. TEST!!! Todo funciona. Pues a “subirlo”.

Una vez el FTP ha finalizado. Entramos en el administrador, la primera en la frente el gráfico de ventas no funciona. La extensión para el procesamiento de imagenes (php_gd) está cargado. Bueno es un mal menor. Seguimos navegando probando opciones, la mejora de velocidad se aprecia y mucho. Introducimos una dirección web inexistente para provocar el error 404 y ver la nueva página. Resultado: …

Not Found

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator, hostmaster@server.tld and inform them of the time the error occurred, and anything you might have done that may have caused the error.

More information about this error may be available in the server error log.

Additionally, a 500 Internal Server Error error was encountered while trying to use an ErrorDocument to handle the request.
Apache/2.0.63 (FreeBSD) mod_fastcgi/2.4.6 PHP/5.2.5 with Suhosin-Patch mod_ssl/2.0.63 OpenSSL/0.9.7e-p1 Server at xxxxxx.com Port 80

Importante el texto en negrita. Algo ha provocado error interno del servidor. ¿Pero qué?

Algo inquietos probar la siguiente “novedad”:

Fatal error: Uncaught SoapFault exception: [WSDL] SOAP-ERROR: Parsing WSDL: Couldn’t load from http:…

Nos centramos inicialmente en el error de SOAP, ya que equivocadamente, creemos que el primero es un problema de configuración en el hosting y le remitimos la incidencia al proveedor. La extensión SOAP de php está cargada. Todo bien. Probamos en el entorno de test, ningún problema. En ese momento el proveedor nos contesta. Nos comunica que no es problema suyo, debe existir un error en nuestro fichero .htaccess y nos enlaza el tutorial en la página de apache. Un momento pensemos…, que hemos hecho esta vez que es diferente. En qué difiere el hosting de Mgnt 1.0, el entorno de desarrollo y/o pruebas y donde está actualmente Mgnt 1.1.3,……….La diferencia además del sistema operativo, Debian en los dos primeros casos y FreeBSD en el último, es FastCGI. Efectivamente el origen de todos mis males, incluido que el sistema sea incapaz de mostrar el gráfico de ventas en la pantalla de administración. Este comportamiento de Magento se reportó en su día como error. Aunque el origen es php + fastCGI. Este “bug” provoca la duplicidad de estados en la cabecera.

Resultado esperado:

Content-type: text/html; charset=UTF-8
Status: 301
Location: http://www.example.com

Resultado obtenido:

Status: 302
Content-type: text/html; charset=UTF-8
Status: 301
Location: http://www.example.com

Según los comentarios en el inicio del fichero app/code/core/Mage/Core/Controller/Response/Http.php, el equipo de Magento contempló y modificó el código para superar este fallo. Pero parece ser que se sigue dando en el caso de página no encontrada, error 404, y SOAP. Un miembro de la comunidad, novalis, nos facilita el código para solucionar el problema además de remitirnos a su blog donde amplia la explicación, en alemán. Cómo novalis comenta la función a modificar es sendHeaders del fichero antes comentado, Http.php.

Vamos a dar un vistazo al código que nos propone:

Código original aquí:

<?php



public function sendHeaders()
{
    if (!$this->canSendHeaders()) {
        Mage::log(‘HEADERS ALREADY SENT: ‘.mageDebugBacktrace(true, true, true));
        return $this;
    }

    if (substr(php_sapi_name(), 0, 3) == ‘cgi’) {
        $statusSent  = FALSE;
        $contentSent = FALSE;
        foreach ($this->_headersRaw as $i=>$header) {
            if (stripos($header, ’status:’)===0 || stripos($header, ‘http/1.1′)===0) {
                if ($statusSent) {
                    unset($this->_headersRaw[$i]);
                } else {
                    $statusSent = true;
                }
            }
            if (stripos($header, ‘content-type’)===0) {
                if ($contentSent) {
                    unset($this->_headersRaw[$i]);
                } else {
                    $contentSent = true;
                }
            }
        }
        foreach ($this->_headers as $i=>$header) {
            if (strcasecmp($header[‘name’], ’status’)===0 || strcasecmp($header[‘name’], ‘Http/1.1′)===0) {
                if ($statusSent) {
                    unset($this->_headers[$i]);
                } else {
                    $statusSent = true;
                }
            }
            if (strcasecmp($header[‘name’], ‘content-type’)===0) {
                if ($contentSent) {
                    unset($this->_headers[$i]);
                } else {
                    $contentSent = true;
                }
            }
        }
    }
    parent::sendHeaders();
}

?>

Podemos ver, en este código que novalis publica en su  blog, que la función analiza con más “cariño” la cadena resultante para extraer los valores de la cabecera (status, content-type, Http/1.1.)  de forma adecuada y no provocar el error.

Módificado este fichero todo vuelve a la normalidad, aparentemente, se muestra el gráfico de ventas y pedidos en el admin, captura del error 404, accesibilidad SOAP. ¡¡¡GENIAL!!!

¿És o no és genial la comunidad Magento?

“Feliz vuelta al cole”. :)

No soy un experto en administración de sistemas.  Simplemente me defiendo. Hace años, en mi etapa como Analista-Programador,  en empresas de servicios  informáticos y consultoría, esos temas de servidores, redes, etc…, me aburrían. Me consideraba programador, únicamente me interesaba saber la ip y/o nombre del servidor de base de datos contra el que desarrollar. Lo demás era tarea de otros compañeros con mucho más oficio y experiencia. Pero debo reconocer que desde que desarrollo para la Web, no solo de Magento vive el hombre, esa “distancia de seguridad” se ha visto comprometida :). Por necesidad he tenido que pelearme con Windows Server y con Linux (mi debilidad es Debian). Y me gusta…

Esta última semana he preparado un entorno de prueba lo más parecido posible al del cliente final. El servidor dedicado que finalmente acogerá la aplicación utiliza CentOS  5.2. ¡Pues a instalar se ha dicho!

Por cierto en este enlace podeis leer un mini-tutorial de como instalar Magento en CentOS. Rapido y sencillo.

Este proceso puede ser algo tedioso. Conseguir la máquina adecuada, descargar la distribución Linux a instalar y el tiempo para ello.

¿Por que no virtualizar?

“Googleando” un poco a la busca y captura de imagenes de CentOS ya listas encontré VMPLANET .Es un sitio que recopila y publica imagenes para VMWare Player de las distribuciones Linux más conocidos. Versiones finales, Betas o incluso Alpha. Para su uso como Desktop (KDE,GNOME, …) o instalaciones mínimas para su uso como Server.

VMWare Player solo es compatible con Windows.

Una alternativa para crear y reproducir imagenes virtuales en Linux es QEMU. Disponeis de documentación y ejemplos en el sitio del proyecto y en algunos blogs, por ejemplo aquí.

Aunque desde hace unos meses la mayoría del tiempo que dedico a desarrollar en  php y testear  implementaciones Magento lo hago en una máquina Apple, no hace mucho tuve que configurar un entorno de desarrollo en Windows XP + WAMP (Windows,Apache,MySQL,PHP). En el Apple utilizo MAMP, que sería su homólogo para esta plataforma, con este último nunca tuve problemas, únicamente en su día fue necesario modificar en el fichero .htaccess de Magento el valor php_value memory_limit a 32M, ya que por defecto era 8M, y las instalaciones nunca finalizaban. Volviendo a WAMP, para crear un entorno adecuado para Magento, debemos instalar PEAR. Es requisito indispensable para que Magento Connect funcione. WAMP lo incluye, aunque inicialmente no está configurado para su uso. Buscando información para completar dicho proceso encontré esta entrada: Instalando PEAR en windows en phpleo, el autor ha realizado un pequeño tutorial paso a paso, fácil y conciso.

Sólo tener en cuenta que dicho tutorial es de hace un año, el path de php en la última versión WAMP (WampServer 2.0c [05/05/2008]) es: c:\directorioInstalación\bin\php\php5.2.6, en el podemos encontrar el fichero go-pear.bat

Artículo original: Instalando PEAR en Windows.

Esta pequeña entrada sé que no aportará ningún conocimiento extra a la comunidad. Su utilidad es más como nota mental para un servidor. Siempre que debo actualizar a una nueva versión de Magento (llevamos tres en un mes), no recuerdo la key a introduccir en el campo install de Magento Connect. Pues bien, tras seleccionar en la pestaña Settings el estado Stable. Introducimos la valor de la key:

magento-core/Mage_All_Latest

pulsamos install, voilà la mágia de Magento Connect hace el resto.