Mensajes de envío gratis a partir de un monto mínimo

En este tutorial, vemos cómo mejorar la visibilidad del envío gratis cuando es a partir de un monto mínimo de la siguiente manera:

  • Mostrando un cartel de "Envío gratis" cuando el precio del producto (o la variante) supera el monto mínimo requerido para tener envío gratis
  • A través de un mensaje en el detalle del producto que muestra a partir de cuanto hay envío gratis. 
  • Mostrando un mensaje que incentiva a agregar un producto al carrito para obtener envío gratis.
  • Con una barra de progreso en el carrito que muestra el monto restante para llegar a tener envío gratis.


Estos mensajes se mostrarán siempre que el envío gratis a partir de un mínimo no tenga restricciones de región o categorías.

HTML

1. Lo primero que vamos a hacer es sumar el siguiente código en el archivo layout.tpl que va a servir para que el JS privado de Tiendanube funcione con los cálculos que permiten que los mensajes cambien al superar el monto mínimo por envío gratis.

{% if cart.free_shipping.cart_has_free_shipping or cart.free_shipping.min_price_free_shipping.min_price %}

    {# Minimum used for free shipping progress messages. Located on header so it can be accesed everywhere with shipping calculator active or inactive #}

    <span class="js-ship-free-min hidden" data-pricemin="{{ cart.free_shipping.min_price_free_shipping.min_price_raw }}"></span>
    <span class="js-cart-subtotal hidden" data-priceraw="{{ cart.subtotal }}"></span>
    <span class="js-cart-discount hidden" data-priceraw="{{ cart.promotional_discount_amount }}"></span>
{% endif %}

2. Vamos a crear el siguiente archivo shipping-free-rest.tpl dentro de la carpeta snipplets/shipping que va a servir tanto para la barra de progreso en el carrito de compras como para el mensaje que incentiva el agregado de un producto al carrito para tener envío gratis.

{% if product_detail %}
    
    {% if not product.free_shipping %}
        {# Wording to notice that adding one more product free shipping is achieved #}

        <div class="js-shipping-add-product-label full-width-container mb-4 text-center text-md-left h6" style="display: none;">
            <span class='js-fs-add-this-product'>{{ "¡Agregá este producto y " | translate }}</span>
            <span class='js-fs-add-one-more' style='display: none;'>{{ "¡Agregá uno más y " | translate }}</span>
            <strong class='text-accent'>{{ "tenés envío gratis!" | translate }}</strong>
        </div>
    {% endif %}
{% else %}
    <div class="js-fulfillment-info js-allows-non-shippable" {% if not cart.has_shippable_products %}style="display: none"{% endif %}>

        {# Free shipping progress bar #}
        <div class="js-ship-free-rest">
            <div class="js-bar-progress bar-progress">
                <div class="js-bar-progress-active bar-progress-active transition-soft"></div>
            </div>
            <div class="js-ship-free-rest-message ship-free-rest-message">
                <div class="ship-free-rest-text bar-progress-success h5 text-accent transition-soft">
                    {{ "¡Genial! Tenés envío gratis" | translate }}
                </div>
                <div class="ship-free-rest-text bar-progress-amount h6 transition-soft">
                    {{ "¡Estás a <strong class='js-ship-free-dif h5'></strong> de tener <strong class='text-accent'>envío gratis</strong>!" | translate }}
                </div>
                <div class="ship-free-rest-text bar-progress-condition transition-soft">
                    <strong class="text-accent">{{ "Envío gratis" | translate }}</strong> {{ "superando los" | translate }} <span>{{ cart.free_shipping.min_price_free_shipping.min_price }}</span>
                </div>
            </div>
        </div>
    </div>
{% endif %}

3Ahora vamos a cambiar el archivo labels.tpl que contiene las etiquetas promocionales para poder mostrar el mensaje de "Envío gratis" no solo cuando el producto lo tiene de forma intrínseca, sino también cuando el precio del producto (o la variante) es superior al monto mínimo. El cambio que necesitamos sumar es el siguiente:

{% set has_product_available = product.available and product.display_price %}
{% set store_has_free_shipping = not product.is_non_shippable and (product.free_shipping or (has_product_available and (cart.free_shipping.cart_has_free_shipping or cart.free_shipping.min_price_free_shipping.min_price))) %}
{% set product_price_above_free_shipping_minimum = cart.free_shipping.min_price_free_shipping and (product.price >= cart.free_shipping.min_price_free_shipping.min_price_raw) %}

Estas condiciones permiten mostrar el cartel dentro del grupo de carteles con el siguiente código (revisar la parte {% if store_has_free_shipping %})

<div class="{% if product.video_url and product %}js-labels-group{% endif %} labels" data-store="product-item-labels">
  {% if show_labels %}
    <div class="js-stock-label label label-default" {% if product.has_stock %}style="display:none;"{% endif %}>{{ "Sin stock" | translate }}</div>
    {% if product.compare_at_price or product.promotional_offer %}
      <div class="{% if not product.promotional_offer and product %}js-offer-label{% endif %} label label-accent" {% if (not product.compare_at_price and not product.promotional_offer) or not product.display_price %}style="display:none;"{% endif %} data-store="product-item-{% if product.compare_at_price %}offer{% else %}promotion{% endif %}-label">
        {% if product.promotional_offer.script.is_percentage_off %}
          {{ product.promotional_offer.parameters.percent * 100 }}% OFF
        {% elseif product.promotional_offer.script.is_discount_for_quantity %}
          <div>{{ product.promotional_offer.selected_threshold.discount_decimal_percentage * 100 }}% OFF</div>
          <div class="label-small p-right-quarter p-left-quarter">{{ "Comprando {1} o más" | translate(product.promotional_offer.selected_threshold.quantity) }}</div>
        {% elseif product.promotional_offer %}
          {% if store.country == 'BR' %}
            {{ "Leve {1} Pague {2}" | translate(product.promotional_offer.script.quantity_to_take, product.promotional_offer.script.quantity_to_pay) }}
          {% else %}
            {{ product.promotional_offer.script.type }} 
          {% endif %}
        {% else %}
          <span class="js-offer-percentage">{{ price_discount_percentage |round }}</span>% OFF
        {% endif %}
      </div>
    {% endif %}
    {% if store_has_free_shipping %}
      <div class="{% if not product.free_shipping %}js-free-shipping-minimum-label {% endif %}label label-accent" {% if not (product.free_shipping or product_price_above_free_shipping_minimum) %}style="display: none;"{% endif %}>{{ "Envío gratis" | translate }}</div>
    {% endif %}
  {% endif %}
</div>
<span class="hidden" data-store="stock-product-{{ product.id }}-{% if product.has_stock %}{% if product.stock %}{{ product.stock }}{% else %}infinite{% endif %}{% else %}0{% endif %}"></span>

Por último en el archivo item.tpl, que representa a los productos en el listado , vamos a buscar el elemento que contiene el precio del producto con la clase "js-price-display" y sumarle el siguiente atributo:

data-product-price="{{ product.price }}"

4. Ahora vamos a cambiar el formulario de producto para poder mostrar los mensajes.

Primero incluimos el archivo shipping-free-rest.tpl  dentro del archivo product-form.tpl debajo del botón de compra para el mensaje que incentiva el agregado de un producto para lograr el envío gratis, que también cambia a  otro mensaje cuando el envío gratis ya fue alcanzado.

{# Free shipping visibility message #}

{% set free_shipping_minimum_label_changes_visibility = has_free_shipping and cart.free_shipping.min_price_free_shipping.min_price_raw > 0 %}

{% set include_product_free_shipping_min_wording = cart.free_shipping.min_price_free_shipping.min_price_raw > 0 %}

{% if not product.is_non_shippable and show_product_quantity and has_free_shipping and not has_product_free_shipping %}

    {# Free shipping add to cart message #}

    {% if include_product_free_shipping_min_wording %}


        {% include "snipplets/shipping/shipping-free-rest.tpl" with {'product_detail': true} %}

    {% endif %}

    {# Free shipping achieved message #}

    <div class="{% if free_shipping_minimum_label_changes_visibility %}js-free-shipping-message{% endif %} text-accent font-weight-bold mb-4 text-center text-md-left h6" {% if not cart.free_shipping.cart_has_free_shipping %}style="display: none;"{% endif %}>
        {{ "¡Genial! Tenés envío gratis" | translate }}
    </div>
{% endif %}

Como ya hicimos en el item de producto, vamos a hacer lo mismo buscando el elemento que contiene el precio del producto con la clase "js-price-display" y sumarle el siguiente atributo:

data-product-price="{{ product.price }}"

Seguimos con sumar el siguiente código arriba de donde comienza el formulario de producto. Este mensaje va a exponer que en la tienda hay envío gratis a partir de un cierto mínimo.

{# Product availability #}

{% set show_product_quantity = product.available and product.display_price %}

{# Free shipping minimum message #}

{% set has_free_shipping = cart.free_shipping.cart_has_free_shipping or cart.free_shipping.min_price_free_shipping.min_price %}
{% set has_product_free_shipping = product.free_shipping %}

{% if not product.is_non_shippable and show_product_quantity and (has_free_shipping or has_product_free_shipping) %}
    <div class="free-shipping-message mb-4 pb-2">
        <span class="d-inline-block">
            {% include "snipplets/svg/truck.tpl" with {svg_custom_class: "icon-inline icon-w-18 icon-lg svg-icon-accent mr-2"} %}
        </span>
        <span class="d-inline-block">
            <strong class="text-accent">{{ "Envío gratis" | translate }} </strong>
            <span {% if has_product_free_shipping %}style="display: none;"{% else %}class="js-shipping-minimum-label"{% endif %}>
                {{ "superando los" | translate }} <span>{{ cart.free_shipping.min_price_free_shipping.min_price }}</span>
            </span>
        </span>
    </div>
{% endif %}

 5. Luego agregamos el archivo shipping-free-rest.tpl en el carrito, en este caso en su formato de popup dentro del archivo cart-panel.tpl debajo de la lista de productos. Si tu diseño tiene un carrito en formato de página, vas a necesitar agregarlo en el archivo cart.tpl

{# Check if store has free shipping without regions or categories #}

{% set has_free_shipping = cart.free_shipping.cart_has_free_shipping or cart.free_shipping.min_price_free_shipping.min_price %}

{% if has_free_shipping and cart.free_shipping.min_price_free_shipping.min_price_raw > 0 %}
  
  {# includes free shipping progress bar: only if store has free shipping with a minimum #}
  
  {% include "snipplets/shipping/shipping-free-rest.tpl" %}
{% endif %}

6. En caso de tener el label del calculador de envíos "dinámico", es decir que si este cambiaba de texto en base a si el envío gratis es alcanzado o no. Necesitamos limpiar esta parte de código dejando simplemente un texto fijo, ya que ahora todos los mensajes de envío gratis se encontrarán por fuera del calculador. Para esto necesitamos remover todas las condiciones de visibilidad de twig y las clases de tipo "js-" dentro del archivo shipping-calculator.tpl en la carpeta shipping. Dejando algo como esto:

<span>{{ 'Medios de envío' | translate }}</span>

7. Por último para la parte de HTML, necesitamos agregar una carpeta SVG dentro de la carpeta snipplets. Acá vamos sumar el SVG para el icono de envíos con el nombre truck.tpl

<svg class="{{ svg_custom_class }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path d="M624 368h-16V251.9c0-19-7.7-37.5-21.1-50.9L503 117.1C489.6 103.7 471 96 452.1 96H416V56c0-30.9-25.1-56-56-56H56C25.1 0 0 25.1 0 56v304c0 30.9 25.1 56 56 56h8c0 53 43 96 96 96s96-43 96-96h128c0 53 43 96 96 96s96-43 96-96h48c8.8 0 16-7.2 16-16v-16c0-8.8-7.2-16-16-16zm-464 96c-26.5 0-48-21.5-48-48s21.5-48 48-48 48 21.5 48 48-21.5 48-48 48zm208-96H242.7c-16.6-28.6-47.2-48-82.7-48s-66.1 19.4-82.7 48H56c-4.4 0-8-3.6-8-8V56c0-4.4 3.6-8 8-8h304c4.4 0 8 3.6 8 8v312zm48-224h36.1c6.3 0 12.5 2.6 17 7l73 73H416v-80zm64 320c-26.5 0-48-21.5-48-48s21.5-48 48-48 48 21.5 48 48-21.5 48-48 48zm80-100.9c-17.2-25.9-46.6-43.1-80-43.1-24.7 0-47 9.6-64 24.9V272h144v91.1z"/></svg>

CSS

Requisito:

Tener agregados en tu diseño las clases helpers. Podés seguir este este pequeño tutorial para hacerlo (simplemente es copiar y pegar algunas clases, no toma más de 1 minuto).

1.Agregamos el siguiente SASS de colores en style-colors.scss.tpl (o la hoja de tu diseño que tenga los colores y tipografías de la tienda). Recordá que las variables de colores y tipografías pueden variar respecto a tu diseño:

.bar-progress {
  background: rgba($accent-color, 0.1);
  &-active {
    background: $accent-color;
  }
}

2. Agregar los estilos dentro del archivo static/style-async.tpl 

Si en tu diseño usas una hoja de estilos para CSS asíncrono, vamos a necesitar el siguiente código dentro de la misma, pero si no es el caso entonces podés unificar todo en el mismo CSS.

/* // Progress bar */

.bar-progress {
  position: relative;
  height: 7px;
  .bar-progress-active {
    width: 0%;
    height: 7px;
  }
}

.ship-free-rest-message {
  position: relative;
  height: 45px;
  .ship-free-rest-text {
    position: absolute;
    top: -5px;
    width: 100%;
    text-align: center;  
    line-height: 36px;
    opacity: 0;
  }
  &.success .bar-progress-success,
  &.amount .bar-progress-amount,
  &.condition .bar-progress-condition {
    top: 0;
    opacity: 1;
  }
}

JS

⚠️ A partir del día 30 de enero de 2023, la librería jQuery será removida del código de nuestras tiendas, por lo tanto la función "$" no podrá ser utilizada.

1. El JavaScript necesitamos agregarlo en el archivo store.js.tpl (o donde tengas tus funciones de JS).  

Primero buscamos la function changeVariant y dentro de estay al final de la misma sumamos lo siguiente que permite actualizar los mensajes de envío gratis ante el cambio de variantes en el detalle del producto

{% if cart.free_shipping.min_price_free_shipping.min_price %}
    {# Updates free shipping bar #}

    LS.freeShippingProgress(true, parent);

{% endif %}

Luego buscamos la siguiente línea

parent.find('.js-price-display').attr("content", variant.price_number).data('productPrice', variant_price_raw);

Y la reemplazamos por esta

parent.find('.js-price-display').attr("content", variant.price_number).data('productPrice', variant.price_number_raw);

Y por último sumamos este código donde tengamos el JS relacionado al carrito para actualizar la barra de progreso al cargar la página

{# /* // Free shipping bar */ #}

{% if cart.free_shipping.min_price_free_shipping.min_price %}

    {# Updates free progress on page load #}

    LS.freeShippingProgress(true);

{% endif %}

Traducciones

Para terminar agregamos los textos para las traducciones en el archivo config/translations.txt

es "Medios de envío"
pt "Meios de envio"
en "Shipping Methods"
es_mx "Opciones de envío"

es "Envío gratis"
pt "Frete grátis"
en "Free shipping"
es_mx "Envío gratis"

es "superando los"
pt "a partir de"
en "buying more than"
es_mx "superando los"

es "¡Genial! Tenés envío gratis"
pt "Sucesso! Você tem frete grátis"
en "You have free shipping"
es_mx "¡Genial! Tienes envío gratis"

es "tenés envío gratis!"
pt "tenha frete grátis!"
en "you have free shipping!"
es_mx "tienes envío gratis!"

es "¡Agregá este producto y "
pt "Adicione este produto e "
en "Add this product and "
es_mx "¡Agrega este producto y "

es "¡Agregá uno más y "
pt "Adicione mais um e "
en "Add one more and "
es_mx "¡Agrega uno más y "

es "¡Estás a <strong class='js-ship-free-dif h5'></strong> de tener <strong class='text-accent'>envío gratis</strong>!"
pt "<strong class='text-accent'>Ganhe frete grátis</strong> com mais <strong class='js-ship-free-dif h5'></strong>"
en "You need <strong class='js-ship-free-dif h5'></strong> to have <strong class='text-accent'>free shipping</strong>"
es_mx "¡Estás a <strong class='js-ship-free-dif h5'></strong> de tener <strong class='text-accent'>envío gratis</strong>!"

Listo, ya tenés en tu diseño la funcionalidad aplicada ¡Excelente!

Configuración

Para poder activar el envío gratis a partir de un mínimo en tu administrador tenes que ir a la sección de Marketing > Envío gratis y crear una "Opción de envío gratis".