Cómo añadir un total personalizado al checkout de Magento 2

, ,
Cómo añadir un total personalizado al checkout de Magento 2

En ocasiones necesitamos añadir una comisión, o un descuento extra a un pedido, en este artículo te explicamos cómo añadir un total personalizado al checkout de Magento 2. Ejemplos de ellos sería una comisión por elegir una forma de pago como PayPal, o un añadir un descuento personalizado que venga desde un ERP.

Magento tiene una serie de ‘totales’ que se encargan de mostrar el subtotal, el IVA, descuento, envío, etc de un pedido. Por lo tanto solo tendremos que añadir un total nuevo a esta lista para que Magento nos lo muestre automáticamente. Para ello, modificaremos unos pocos archivos de Magento y obtendremos el resultado deseado.

Antes de nada, crearemos un módulo si no tenemos ya uno preparado. Para ello podemos seguir esta guía.

Ahora tenemos que informar a Magento de que hay un nuevo total que debe tener en cuenta. Esto lo hacemos a través del archivo etc/sales.xml de nuestro módulo:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Sales:etc/sales.xsd">
    <section name="quote">
        <group name="totals">
            <item name="testdiscount" instance="Gsoft\Cps\Model\Total\Quote\Custom" sort_order="420"/>
        </group>
    </section>
</config>

Como podemos ver, estamos diciendo a magento que el modelo quote tiene un total llamado ‘testdiscount’, y la clase que lo va a gestionar es Gsoft\Cps\Model\Total\Quote\Custom.php

El contenido de este archivo sería el siguiente:

<?php

namespace Gsoft\Cps\Model\Total\Quote;
/**
 * Class Custom
 * @package Gsoft\Cps\Model\Total\Quote
 */
class Custom extends \Magento\Quote\Model\Quote\Address\Total\AbstractTotal
{
    public function __construct(
        \Magento\Framework\Event\ManagerInterface $eventManager,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Magento\SalesRule\Model\Validator $validator,
        \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency
    )
    {
        $this->setCode('testdiscount');
        $this->eventManager = $eventManager;
        $this->calculator = $validator;$this->storeManager = $storeManager;
        $this->priceCurrency = $priceCurrency;
    }

    public function collect(
        \Magento\Quote\Model\Quote $quote,
        \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment,
        \Magento\Quote\Model\Quote\Address\Total $total
    )
    {
        parent::collect($quote, $shippingAssignment, $total);
        $address = $shippingAssignment->getShipping()->getAddress();
        $label = 'Descuento personalizado';

        $TotalAmount = 0;
        foreach($quote->getAllItems() as $item){
            if($item->getData("test_discount")>0) {
                $TotalAmount += $item->getPrice()*$item->getQty() * $item->getData("test_discount") / 100;
            }
        }

        $discountAmount = "-" . $TotalAmount;
        $appliedCartDiscount = 0;

        if ($total->getDiscountDescription()) {
            $appliedCartDiscount = $total->getDiscountAmount();
            $discountAmount = $total->getDiscountAmount() + $discountAmount;
            $label = $total->getDiscountDescription() . ', ' . $label;
        }

        $total->setDiscountDescription($label);
        $total->setDiscountAmount($discountAmount);
        $total->setBaseDiscountAmount($discountAmount);
        $total->setSubtotalWithDiscount($total->getSubtotal() + $discountAmount);
        $total->setBaseSubtotalWithDiscount($total->getBaseSubtotal() + $discountAmount);

        if (isset($appliedCartDiscount)) {
            $total->addTotalAmount($this->getCode(), $discountAmount - $appliedCartDiscount);
            $total->addBaseTotalAmount($this->getCode(), $discountAmount - $appliedCartDiscount);
        } else {
            $total->addTotalAmount($this->getCode(), $discountAmount);
            $total->addBaseTotalAmount($this->getCode(), $discountAmount);
        }
        return $this;
    }

    public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Quote\Address\Total $total)
    {
        $result = null;
        $amount = $total->getDiscountAmount();

        if ($amount != 0) {
            $description = $total->getDiscountDescription();
            $result = [
                'code' => $this->getCode(),
                'title' => strlen($description) ? __('Discount (%1)', $description) : __('Discount'),
                'value' => $amount
            ];
        }
        return $result;
    }

}

En este modelo podemos ver la función collect(), que es la que se encarga de obtener el importe del total. En este ejemplo hemos definido que el total lo obtiene mirando todos los ítems añadidos al carrito y consultando si tiene un valor en el campo ‘test_discount’, y si lo tiene, calcula el descuento. Este campo podría ser un atributo del producto que contuviera un % de descuento a aplicar al producto. Para añadir atributos de producto personalizados al carrito podemos seguir esta guía.

La función fetch() se encarga de insertar en la lista de totales este total, con su nombre.

Mostrar al usuario el total personalizado

Con estos 2 archivos ya tenemos el total operativo. Para continuar con el proceso de cómo añadir un total personalizado al checkout de Magento faltaría mostrarlo al usuario en el carrito y en el proceso de compra. Esto lo conseguimos añadiendo los archivos xml a nuestro módulo:

view/frontend/layout/checkout_cart_index.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="cart.summary">
            <block class="Gsoft\Cps\Block\Promotions"
                   name="cps_promotions" template="Gsoft_Cps::cart_promotions.phtml" before="-">
            </block>
        </referenceBlock>
        <referenceBlock name="checkout.cart.totals">
            <arguments>
                <argument name="jsLayout" xsi:type="array">
                    <item name="components" xsi:type="array">
                        <item name="block-totals" xsi:type="array">
                            <item name="children" xsi:type="array">
                                <item name="before_grandtotal" xsi:type="array">
                                    <item name="children" xsi:type="array">
                                        <item name="testdiscount" xsi:type="array">
                                            <item name="config" xsi:type="array">
                                                <item name="title" xsi:type="string" translate="true">My Discount
                                                </item>
                                            </item>
                                        </item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>

    </body>
</page>

El anterior xml añade el total a la vista del carrito.

view/frontend/layout/checkout_index_index.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="checkout.root">
            <arguments>
                <argument name="jsLayout" xsi:type="array">
                    <item name="components" xsi:type="array">
                        <item name="checkout" xsi:type="array">
                            <item name="children" xsi:type="array">
                                <item name="sidebar" xsi:type="array">
                                    <item name="children" xsi:type="array">
                                        <item name="summary" xsi:type="array">
                                            <item name="children" xsi:type="array">
                                                <item name="totals" xsi:type="array">
                                                    <item name="children" xsi:type="array">
                                                        <item name="testdiscount" xsi:type="array">
                                                            <item name="config" xsi:type="array">
                                                                <item name="title" xsi:type="string" translate="true">My Discount
                                                                </item>
                                                            </item>
                                                        </item>
                                                    </item>
                                                </item>
                                            </item>
                                        </item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

Este xml se encarga de añadir el total al proceso de compra.

Como se puede ver es muy sencillo añadir nuevos totales a un pedido. Y si necesitas ayuda, no dudes en contactar con nosotros.