X

Sylius: Asociando gastos adicionales al Método de Pago

En un artículo pasado, vimos cómo podemos implementar una calculadora asociada a los gastos de envío en Sylius. En este artículo vamos a ver cómo podemos asociar un gasto adicional al pedido en función del método de pago seleccionado. El caso de uso surge por la necesidad de añadir un método de pago “a contra reembolso”, el cual que requiere añadir un gasto adicional para el cliente.

Para conseguir nuestro objetivo, nos vamos a basar en el uso de OrderProcessors. Los OrderProcessor son responsables de manipular los pedidos para aplicar diferentes ajustes predefinidos u otras modificaciones basadas en el estado del pedido. Con esta estrategia, podemos por ejemplo, aplicar descuentos por volumen o añadir impuestos adicionales. En nuestro caso, vamos a configurar un coste asociado al Método de Pago dado de alta que se sumará al pedido como un coste adicional.

Modificando el modelo PaymentMethod de Sylius

Para ello, lo primero que debemos de hacer es modificar el modelo PaymentMethod para añadirle un campo adicional. Para ello extendemos del modelo base que nos proporciona el framework Sylius\Component\Core\Model\PaymentMethod y añadimos nuestro campo price.

<?php

namespace AppBundle\Entity;

use Sylius\Component\Core\Model\PaymentMethod as BasePaymentMethod;

/**
 * Class Product
 * @package AppBundle\Entity
 */
class PaymentMethod extends BasePaymentMethod
{
    /**
     * @var int
     */
    protected $price;

    /**
     * @return int
     */
    public function getPrice(): ?int
    {
        return $this->price;
    }

    /**
     * @param int $price
     */
    public function setPrice(?int $price): void
    {
        $this->price = $price;
    }
}

Una vez incluido nuestro campo lo añadimos a los metadatos de doctrine:

AppBundle\Entity\PaymentMethod:
    type: entity
    table: sylius_payment_method
    fields:
        price:
            type: integer
            nullable: true

Para profundizar en la customización de los modelos, os recomiendo este apartado de la documentación. Una vez tenemos el modelo añadido, necesitamos añadir la opción en el panel de administración para añadir el coste asociado al método de pago. Para ello, vamos a extender el formulario utilizando un AbstractTypeExtension de Symfony tratando de extender el formulario SyliusPaymentMethodType. A este formulario añadiremos el campo adicional price.

<?php

declare(strict_types=1);

namespace AppBundle\Form\Extension;

use Sylius\Bundle\MoneyBundle\Form\Type\MoneyType;
use Sylius\Bundle\PaymentBundle\Form\Type\PaymentMethodType as SyliusPaymentMethodType;
use Sylius\Bundle\ResourceBundle\Form\EventSubscriber\AddCodeFormSubscriber;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Type;

/**
 * Class PaymentMethodTypeExtension
 * @package AppBundle\Form\Extension
 */
class PaymentMethodTypeExtension extends AbstractTypeExtension
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->addEventSubscriber(new AddCodeFormSubscriber())
            ->add(
                'price',
                MoneyType::class,
                [
                    'required'=> false,
                    'label' => 'Precio',
                    'constraints' => [
                        new Type(['type' => 'integer', 'groups' => ['sylius']]),
                    ],
                ]
            );
    }

    /**
     * {@inheritdoc}
     */
    public function getExtendedType()
    {
        return SyliusPaymentMethodType::class;
    }
}

Una vez implementada la clase, debemos de darla de alta en nuestro fichero services.yml

 app.form.extension.type.payment_method:
        class: AppBundle\Form\Extension\PaymentMethodTypeExtension
        tags:
            - { name: form.type_extension, extended_type: Sylius\Bundle\PaymentBundle\Form\Type\PaymentMethodType }


Para modificar la vista del panel de administración debemos de adaptar el la vista modificando el twig app/Resources/SyliusAdminBundle/views/PaymentMethod/_form.html.twig añadiendo al final del documento la renderización de nuestro nuevo campo:

<div class="ui segment">
    <h4 class="ui dividing header">Coste Adicional</h4>
    {{ form_row(form.price) }}
</div>

Utilizando OrderProcessor

Como hemos comentado al principio, vamos a ayudarnos del concepto OrderProcessor para añadir nuestro gasto en el pedido. Para ello, vamos a añadir al pedido un elemento Adjustment en el caso de que el método de pago elegido durante la creación del pedido tenga un coste asociado.

namespace AppBundle\Order\Processor;

use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Order\Model\AdjustmentInterface as BaseAdjustmentInterface;
use Sylius\Component\Order\Model\OrderInterface as BaseOrderInterface;
use Sylius\Component\Order\Processor\OrderProcessorInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;

/**
 * Class PaymentChargesProcessor
 * @package AppBundle\Order\Processor
 */
final class PaymentChargesProcessor implements OrderProcessorInterface
{
    /**
     * @var FactoryInterface
     */
    private $adjustmentFactory;

    /**
     * @param FactoryInterface $adjustmentFactory
     */
    public function __construct(
        FactoryInterface $adjustmentFactory
    ) {
        $this->adjustmentFactory = $adjustmentFactory;
    }

    /**
     * @param BaseOrderInterface $order
     */
    public function process(BaseOrderInterface $order): void
    {
        assert($order instanceof OrderInterface);
        $order->removeAdjustments();
        foreach ($order->getPayments() as $payment) {
            if (!$price = $payment->getMethod()->getPrice()) {
                continue;
            }

            $adjustment = $this->adjustmentFactory->createNew();
            assert($adjustment instanceof BaseAdjustmentInterface);
            $adjustment->setType('adjustment');
            $adjustment->setAmount($price);
            $adjustment->setLabel($payment->getMethod() !== null ? $payment->getMethod()->getName() : null);
            $adjustment->setOriginCode($payment->getMethod() !== null ? $payment->getMethod()->getCode() : null);
            $adjustment->setNeutral(false);
            $order->addAdjustment($adjustment);
        }
    }
}

 

El concepto Adjustment en Sylius nos permite hacer ajustes en el total de nuestro pedido. Los ajustes habituales se pueden dividir en tres grupos: ajustes de promoción, ajustes de envío y ajustes de impuestos. Hay que tener en cuenta que los ajustes pueden ser positivos (cargos) o negativos (descuentos).

Finalmente, para que el OrderProcessor empiece a funcionar, debemos de darlo de alta con el tag sylius.order_processor

    order.processor.payment_charges:
        class: AppBundle\Order\Processor\PaymentChargesProcessor
        arguments:
            - '@sylius.factory.adjustment'
        tags:
            - name: sylius.order_processor
              priority: 0

Los OrderProcessor se añaden a un servicio de Sylius CompositeOrderProcessor que incluye todos los processors del sistema priorizados. Este servicio es capaz de ejecutarlos en el orden adecuado. Por defecto, la ejecución de estos OrderProcessors se ejecutan en casos particulares, por ejemplo cuando se añaden o eliminan elementos en el carrito de compra.

Como nuestra necesidad es que este cálculo se aplique cuando se asocia un método de pago a un pedido, se ha desarrollado un Subscriber asociado al evento sylius.order.post_payment, el cual es ejecutado justo después de asociar el método de pago, de esta manera recalculamos los ajustes necesarios:

<?php

namespace AppBundle\Subscriber;

use Sylius\Component\Order\Processor\CompositeOrderProcessor;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Class PostPaymentSubscriber
 * @package AppBundle\Subsriber
 */
class PostPaymentSubscriber implements EventSubscriberInterface
{
    /**
     * @var CompositeOrderProcessor
     */
    private $processor;

    public function __construct(CompositeOrderProcessor $processor)
    {
        $this->processor = $processor;
    }

    /**
     * @return array
     */
    public static function getSubscribedEvents()
    {
        return [
            'sylius.order.post_payment'=> 'onPostPayment',
        ];
    }

    /**
     * @param Event $event
     */
    public function onPostPayment(Event $event)
    {
        $subject = $event->getSubject();

        $this->processor->process($subject);
    }
}

Conclusión

Como resumen de lo visto a lo largo del articulo, hemos modificado el modelo PaymentMethod del framework y hemos modificado el formulario del panel de administración para que el usuario configure el coste asociado al método de pago.

Una vez realizado estos pasos, hemos añadido un OrderProcessor que se encargue de añadir el coste asociado al PaymentMethod, para que se ejecute cuando haya algún cambio en el método de pago del pedido, usamos el servicio CompositeOrderProcessor dentro de nuestro Subscriber al eventosylius.order.post_payment.

Espero que este artículo pueda resultar de utilidad y os animo a comentar alternativas a este método y vuestra experiencia manipulando el coste de un pedido en Sylius.

Categorías: Desarrollo Webe-commerce
Tags: ecommercesylius
alonsus91 :Joven ingeniero en Informática especializado en Ingeniería Web apasionado por las nuevas tecnologías y las últimas novedades en informática e Internet con ansias de conocer a fondo el mercado laboral y aportarle todos mis conocimientos.
Disqus Comments Loading...

Utilizamos cookies para asegurar que damos la mejor experiencia al usuario en nuestro sitio web. Si continúa utilizando este sitio asumiremos que está de acuerdo.